Rosetta

默认来说,ECMAScript是HTML的唯一标准脚本语言因此,当你使用的别的脚本语言时,你可能会期望大部分浏览器不会识别这个语言。 另外,对于某些语言来说(比如C),当它的环境并不像通常那样对资源有完全的权限时,该语言的某些语义也变得难以定义——例如考虑在HTML页面中C指针的意义! 虽然如此,通过结合现代浏览器不断提升的计算能力和ECMAScript推出的typed arrays,理论上我们可以在纯ECMAScript中建立完整的虚拟机。因此,理论上我们同样也可以用ECMAScript来实现一个小任务:解析其他编程语言(即创建编译器)。

在ECMAScript之外

这里并不是展示一个完整的ECMAScript编译器的地方。但是我们可以展示如何开始这项工作。下面的代码是一个扩展编译器的基础,但是默认没有加载任何编译器。它的工作原理是:不能被识别的MIME类型代码会被忽略——这使我们可以手动解析他们。

想要概览这里的代码,请git clone https://github.com/madmurphy/rosetta.js,或直接下载.zip文件.

rosetta.js:

"use strict";

/*\
|*|
|*|  :: rosetta.js ::
|*|
|*|  A possible, extensible collection of compilers to native ECMAScript.
|*|
|*|  November 12, 2014
|*|
|*|  https://developer.mozilla.org/en-US/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;
  };

})();

现在,想象你需要一个C (MIME类型:text/x-c)脚本的编译器。首先,你需要声明一个函数,这个函数会把传入的纯文本C转换为并输出纯文本的ECMAScript。让我们调用这个createECMASrc()函数并把它和C MIME类型关联:

rosetta_c.js:

一个C编译器
/* 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);

})();
注意: 从零开始创建一个编译器会是一个非常困难的任务。但是如果你成功的这么做了,欢迎为我们的Rosetta脚本公布你的编译器

现在,你仅仅需要在你的HTML页面中引用rosetta.js,就可以把你的C脚本和ECMAScript脚本一起执行了。

example.html:

HTML示例
<!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示例
#include <stdio.h>

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

如果觉得创建一个C语言的编译器看起来是一项非常庞大的任务,这里有一些ECMAScript的近亲可以很容易转译为ECMAScript。上面的例子展示了可用来转换这些语言(不管简单还是困难)的可行的解决方案。

MIME类型

这里是一些关联其他编程语言的类型types:

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

<script>元素的type属性对可用的MIME类型没有限制。

参见

文档标签和贡献者

 此页面的贡献者: fskuok
 最后编辑者: fskuok,