Rosetta

By default, the only possible standardized scripting language for HTML is ECMAScript. Hence, if you are going to use another scripting language you might expect that most of the browsers will not recognize it. Also, regarding some languages (like C), it is even not well defined the meaning of part of their semantics in respect to a scope that is more restricted than the full access to the resources they usually deal with – imagine, for example, the meaning of a C pointer within a HTML page! Nevertheless, the increasing computational power of modern browsers together with the introduction of typed arrays in ECMAScript allow us, in theory, to build full virtual machines in pure ECMAScript. Therefore, it is also possible, in theory, to use ECMAScript for a smaller task: parsing exotic programming languages (i.e., creating compilers).

Beyond ECMAScript

This is not the place for showing a full compiler written in ECMAScript. We can show, however, a possible way to start from. The following code is nothing but the base for an extensible collection of compilers. But by default, no compilers are loaded. It relies on the fact that unrecognized MIME types will be simply ignored: this allows us to manually parse them.

For a fast overview on the code proposed here you can git clone https://github.com/madmurphy/rosetta.js, or, at your choice, directly download this .zip file.

rosetta.js:

Library
"use strict";

/*\
|*|
|*|  :: rosetta.js ::
|*|
|*|  A possible, extensible collection of compilers to native ECMAScript.
|*|
|*|  November 12, 2014
|*|
|*|  https://developer.mozilla.org/Add-ons/Code_snippets/Rosetta
|*|  https://developer.mozilla.org/User:fusionchess
|*|
|*|  This framework is released under the GNU Public License, version 3 or later.
|*|  http://www.gnu.org/licenses/gpl-3.0.html
|*|
|*|  Syntax:
|*|
|*|    rosetta.appendCompiler([ "text/x-csrc", "text/x-c" ], yourCompiler);
|*|
\*/

var rosetta = new (function () {

  function createScript (oScript, oXHR200) {

    var
      sMimeType = oScript.getAttribute("type").toLowerCase(),
      oBaton = document.createComment(" The previous code has been automatically translated from \"" + sMimeType + "\" to \"text/ecmascript\". ");

    if (!oDicts.hasOwnProperty(sMimeType)) {
      alert("rosetta.translateScript() \u2013 Unknown mime-type \"" + sMimeType + "\": script ignored.");
      return;
    }

    var oCompiled = document.createElement("script");
    oScript.parentNode.insertBefore(oBaton, oScript);
    oScript.parentNode.removeChild(oScript);

    for (var aAttrs = oScript.attributes, nAttr = 0; nAttr < aAttrs.length; nAttr++) {
      oCompiled.setAttribute(aAttrs[nAttr].name, aAttrs[nAttr].value);
    }

    oCompiled.type = "text\/ecmascript";
    if (oXHR200) { oCompiled.src = "data:text\/javascript," + encodeURIComponent(oDicts[sMimeType](oXHR200.responseText)); }
    oCompiled.text = oXHR200 ? "" : oDicts[sMimeType](oScript.text);
    oBaton.parentNode.insertBefore(oCompiled, oBaton);

  }

  function reqError (oError) {
    throw new URIError("The script " + oError.target.src + " is not accessible.");
  }

  function reqSuccess () {
    createScript(this.refScript, this);
  }

  function getSource (oScript) {
    var oReq = new XMLHttpRequest();
    oReq.onload = reqSuccess;
    oReq.onerror = reqError;
    oReq.refScript = oScript;
    oReq.open("GET", oScript.src, true);
    oReq.send(null);
  }

  function parseScript (oScript) {
    if (oScript.hasAttribute("type") && !rIgnoreMimes.test(oScript.getAttribute("type").toLowerCase())) {
      oScript.hasAttribute("src") ? getSource(oScript) : createScript(oScript);
    }
  }

  function parseDocument () {
    for (
      var
        aScripts = document.getElementsByTagName("script"),
        nIdx = 0;
      nIdx < aScripts.length;
      parseScript(aScripts[nIdx++])
    );
  }

  var
    oDicts = {},
    rIgnoreMimes = /^\s*(?:text\/javascript|text\/ecmascript)\s*$/;

  this.translateScript = parseScript;

  this.translateAll = parseDocument;

  this.appendCompiler = function (vMimeTypes, fCompiler) {

    if (arguments.length < 2) {
      throw new TypeError("rosetta.appendCompiler() \u2013 not enough arguments");
    }

    if (typeof fCompiler !== "function") {
      throw new TypeError("rosetta.appendCompiler() \u2013 second argument must be a function");
    }

    if (!Array.isArray(vMimeTypes)) {
      oDicts[(vMimeTypes).toString()] = fCompiler;
      return true;
    }

    for (var nIdx = 0; nIdx < vMimeTypes.length; nIdx++) {
      oDicts[(vMimeTypes[nIdx]).toString()] = fCompiler;
    }

    return true;
  };

})();

Now, imagine you need a compiler for scripts written in C (MIME type: text/x-c). First, you should declare a function which will translate an input plain-text written in C to an output plain-text written in ECMAScript. Let us call this function createECMASrc() and let us associate it with the C MIME types:

rosetta_c.js:

A C compiler
/* C Compiler for Rosetta */

(function () {

  if (!window.rosetta) { return; }

  /* This function takes as argument a plain text (in this case, a code written in C) and returns another plain text written in ECMAScript */
  function createECMASrc (sCSrc) {
    /* This is just an empty example... Enjoy in creating your C compiler! */
    return "alert(\"Here you have the C code to be compiled to ECMAScript:\\n\\n\" + " + JSON.stringify(sCSrc) + ");";
  }

  rosetta.appendCompiler([ "text/x-csrc", "text/x-c" ], createECMASrc);

})();
Note: Creating a compiler from scratch can be a very difficult task. However, if you are able, feel free to public it for our Rosetta script !

Now, all you need is to include rosetta.js and your compiler within your HTML page and you will be able to execute scripts written in C together with scripts written in ECMAScript:

example.html:

HTML Example
<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Rosetta C Example</title>
<script type="text/javascript" src="rosetta.js"></script>
<script type="text/javascript" src="rosetta_c.js"></script>
<script type="text/x-csrc">
#include <stdio.h>

int main () {
  printf("Hello world number 1!\n");
  return 0;
}
</script>
<script type="text/x-c" src="example.c"></script>
</head>

<body>

<p>Lorem ipsum</p>

<script type="text/javascript">
rosetta.translateAll();
</script>

</body>
</html>

example.c:

C Example
#include <stdio.h>

int main () {
  printf("Hello world number 2!\n");
  return 0;
}

If creating a compiler for the C programming language, as in the example above, can really look a huge task, there are many dialects of ECMAScript which could be easily translated to standard ECMAScript. The previous example shows a possible solution for all these cases, from the simplest to the hardest one.

MIME types

Here is a short list of the MIME types associated to some programming languages:

Language MIME type
Bash text/x-shellscript
Java text/x-java-source
C text/x-c, text/x-csrc
C++ text/x-c++, text/x-c++src
Python text/x-python

There are no limitations to the MIME types that can be used for the type attribute of the <script> element.

See also