关于使用 ast.parse 而不是解析器(我以前不知道),我将第二次 @EliBendersky 的观点。我也热烈推荐你查看他的博客。我用 ast.parse 做 Python->JavaScript 翻译器(@ https://bitbucket.org/amirouche/pythonium)。通过对其他实现进行一些审查并自己尝试它们,我提出了 Pythonium 设计。我从我也开始的https://github.com/PythonJS/PythonJS分叉了 Pythonium ,它实际上是一个完整的重写。整体设计灵感来自 PyPy 和http://www.hpl.hp.com/techreports/Compaq-DEC/WRL-89-1.pdf论文。
我尝试的一切,从开始到最好的解决方案,即使它看起来像 Pythonium 营销,但实际上并非如此(如果网络礼仪似乎不正确,请随时告诉我):
使用原型继承在普通旧 JavaScript 中实现 Python 语义:AFAIK 使用 JS 原型对象系统实现 Python 多重继承是不可能的。后来我确实尝试使用其他技巧来做到这一点(参见getattribute)。据我所知,在 JavaScript 中没有 Python 多重继承的实现,存在的最好的是单继承 + mixins,我不确定它们是否处理菱形继承。有点类似于 Skulpt 但没有 google clojure。
我尝试使用 Google clojure,就像 Skulpt(编译器)一样,而不是实际阅读 Skulpt 代码#fail。无论如何,因为基于 JS 原型的对象系统仍然是不可能的。创建绑定非常非常困难,您需要编写 JavaScript 和大量样板代码(参见https://github.com/skulpt/skulpt/issues/50,我是幽灵)。那时还没有明确的方法将绑定集成到构建系统中。我认为 Skulpt 是一个库,您只需将 .py 文件包含在要执行的 html 中,开发人员无需进行编译阶段。
尝试过 pyjaco(编译器),但创建绑定(从 Python 代码调用 Javascript 代码)非常困难,每次都要创建太多样板代码。现在我认为 pyjaco 是更接近 Pythonium 的那个。pyjaco 是用 Python 编写的(ast.parse 也是),但很多都是用 JavaScript 编写的,并且它使用原型继承。
我从未真正成功地运行睡衣#fail,也从未尝试再次阅读代码#fail。但在我看来,睡衣是在做 API->API 翻译(或框架到框架),而不是 Python 到 JavaScript 的翻译。JavaScript 框架使用页面中已有的数据或来自服务器的数据。Python 代码只是“管道”。之后我发现睡衣其实是一个真正的python->js翻译器。
我仍然认为可以进行 API->API(或框架->框架)翻译,这基本上是我在 Pythonium 中所做的,但在较低级别。可能睡衣使用与Pythonium相同的算法......
然后我发现 brython 像 Skulpt 一样完全用 Javascript 编写,不需要编译和很多绒毛......但是用 JavaScript 编写的。
从这个项目过程中写的第一行开始,我就知道了 PyPy,甚至是 PyPy 的 JavaScript 后端。是的,如果你找到它,你可以直接从 PyPy 用 JavaScript 生成 Python 解释器。人们说,这是一场灾难。我没有读到为什么。但我认为原因是他们用来实现解释器的中间语言 RPython 是 Python 的一个子集,专门用于翻译成 C(可能还有 asm)。Ira Baxter 说,当你构建一些东西时,你总是会做出假设,并且可能你会对其进行微调,使其在 PyPy 的情况下做到最好:Python->C 翻译。这些假设在其他情况下可能不相关,更糟糕的是,它们可以推断出开销,否则直接翻译很可能总是更好。
用 Python 编写解释器听起来是一个(非常)好主意。但是出于性能原因,我对编译器更感兴趣,而且实际上将 Python 编译为 JavaScript 比解释它更容易。
我开始 PythonJS 的想法是把 Python 的一个子集放在一起,我可以很容易地把它翻译成 JavaScript。起初,由于过去的经验,我什至没有费心去实现 OO 系统。我实现转换为 JavaScript 的 Python 子集是:
- 在定义和调用中具有完整参数语义的函数。这是我最自豪的部分。
- 而/如果/elif/else
- Python 类型被转换为 JavaScript 类型(没有任何类型的 Python 类型)
- for 只能遍历 Javascript 数组(对于 in 数组)
- 对 JavaScript 的透明访问:如果您在 Python 代码中编写 Array,它将被转换为 javascript 中的 Array。这是在可用性方面超过其竞争对手的最大成就。
- 您可以将 Python 源代码中定义的函数传递给 javascript 函数。将考虑默认参数。
- 它添加了一个名为 new 的特殊函数,该函数被翻译为 JavaScript new 例如:new(Python)(1, 2, spam, "egg") 被翻译为 "new Python(1, 2, spam, "egg")。
- “var”由翻译器自动处理。(来自 Brett(PythonJS 贡献者)的非常好的发现。
- 全局关键字
- 关闭
- 拉姆达斯
- 列表推导
- 通过 requirejs 支持导入
- 通过classyjs的单类继承+ mixin
与 Python 的完整语义相比,这看起来很多,但实际上非常狭窄。它实际上是带有 Python 语法的 JavaScript。
生成的 JS 是完美的,即。没有开销,无法通过进一步编辑来提高性能。如果您可以改进生成的代码,您也可以从 Python 源文件中进行改进。此外,编译器不依赖于您可以在http://superherojs.com/编写的 .js 中找到的任何 JS 技巧,因此它非常具有可读性。
这部分 PythonJS 的直接后代是 Pythonium Veloce 模式。完整的实现可以在@https://bitbucket.org/amirouche/pythonium/src/33898da731ee2d768ced392f1c369afd746c25d7/pythonium/veloce/veloce.py?at = master 793 SLOC + 大约 100 SLOC 与其他翻译器共享代码中找到。
pystones.py 的改编版本可以在 Veloce 模式下翻译,参见。https://bitbucket.org/amirouche/pythonium/src/33898da731ee2d768ced392f1c369afd746c25d7/pystone/?at=master
在设置了基本的 Python->JavaScript 翻译后,我选择了另一条将完整的 Python 翻译成 JavaScript 的路径。除了目标语言之外,glib 执行基于类的面向对象代码的方式是 JS,因此您可以访问数组、类似映射的对象和许多其他技巧,所有这些部分都是用 Python 编写的。IIRC 没有用 Pythonium 翻译器编写的 javascript 代码。获得单一继承并不难,这里是使 Pythonium 完全兼容 Python 的困难部分:
spam.egg
在 Python 中总是被翻译成getattribute(spam, "egg")
我没有特别分析这个,但我认为它浪费了很多时间,我不确定我可以用 asm.js 或其他任何东西来改进它。
- 方法解析顺序:即使使用 Python 编写的算法,将其转换为 Python Veloce 兼容代码也是一项艰巨的工作。
- getattribute:实际的 getattribute 解析算法有点棘手,它仍然不支持数据描述符
- 基于元类类:我知道在哪里插入代码,但仍然......
- 最后一点很重要:some_callable(...) 总是转换为“call(some_callable)”。AFAIK 翻译器根本不使用推理,所以每次你打电话时,你都需要检查它是用哪种对象来调用它的,因为它们是按照它应该被调用的方式来调用的。
这部分包含在https://bitbucket.org/amirouche/pythonium/src/33898da731ee2d768ced3 92f1c369afd746c25d7/pythonium/compliant/runtime.py?at= master 它是用与 Python Veloce 兼容的 Python 编写的。
实际的兼容翻译器https://bitbucket.org/amirouche/pythonium/src/33898da731ee2d768ced3 92f1c369afd746c25d7/pythonium/compliant/compliant.py?at=master 不会直接生成 JavaScript 代码,最重要的是不会进行 ast->ast 转换. 我尝试了 ast->ast 的东西,即使使用 ast.NodeTransformer 比 cst 更好,ast 也不好使用,更重要的是我不需要做 ast->ast。
在我的情况下,至少对 python ast 执行 python ast 可能会提高性能,因为我有时会在生成与其关联的代码之前检查块的内容,例如:
- var/global:为了能够对某些东西进行 var,我必须知道我需要什么而不是 var。我没有生成一个块来跟踪在给定块中创建了哪些变量并将其插入到生成的功能块的顶部,我只是在实际访问子节点以生成相关代码之前进入块时寻找相关的变量分配。
- yield,到目前为止,生成器在 JS 中有一种特殊的语法,所以当我想编写“var my_generator = function”时,我需要知道哪个 Python 函数是生成器
因此,对于翻译的每个阶段,我并没有真正访问每个节点一次。
整个过程可以描述为:
Python source code -> Python ast -> Python source code compatible with Veloce mode -> Python ast -> JavaScript source code
Python 内置函数是用 Python 代码(!)编写的,IIRC 有一些与引导类型相关的限制,但您可以访问所有可以在兼容模式下转换 Pythonium 的东西。看看https://bitbucket.org/amirouche/pythonium/src/33898da731ee2d768ced392f1c369afd746c25d7/pythonium/compliant/builtins/? at=master
阅读从 pythonium 兼容生成的 JS 代码可以理解,但 source maps 将有很大帮助。
根据这次经验,我可以给你的宝贵建议是老屁:
- 广泛审查文献和现有项目中的主题,封闭源代码或免费。当我回顾不同的现有项目时,我应该给它更多的时间和动力。
- 问问题!如果我事先知道 PyPy 后端由于 C/Javascript 语义不匹配导致的开销而无用。我可能会在 6 个月前或 3 年前有 Pythonium 的想法。
- 知道自己想做什么,有目标。对于这个项目,我有不同的目标:练习一点 javascript,了解更多 Python 知识,并能够编写可以在浏览器中运行的 Python 代码(更多内容如下)。
- 失败是经验
- 一小步就是一步
- 从小处着手
- 远大的梦想
- 做演示
- 迭代
仅使用 Python Veloce 模式,我很高兴!但在此过程中,我发现我真正想要的是将我和其他人从 Javascript 中解放出来,但更重要的是能够以一种舒适的方式进行创作。这将我引向了 Scheme、DSL、模型以及最终的领域特定模型(参见http://dsmforum.org/)。
关于 Ira Baxter 的回应:
这些估计根本没有帮助。我花了大约 6 个月的空闲时间来学习 PythonJS 和 Pythonium。所以我可以期待更多的全职 6 个月。我想我们都知道企业环境中的 100 人年可能意味着什么,根本不意味着什么......
当有人说某事很难或更经常是不可能的时,我会回答“只需要时间来解决一个不可能的问题”,否则就说没有什么是不可能的,除非在这种情况下证明是不可能的数学证明......
如果它没有被证明是不可能的,那么它留下了想象的空间:
和
或者
这不仅仅是乐观的想法。当我开始使用 Python->Javascript 时,每个人都说这是不可能的。PyPy 不可能。元类太难了。等等...我认为,将 PyPy 引入 Scheme->C 论文(已有 25 年历史)的唯一革命是一些自动 JIT 生成(我认为基于 RPython 解释器编写的提示)。
大多数说某件事“困难”或“不可能”的人并没有提供原因。C++很难解析?我知道,它们仍然是(免费的)C++ 解析器。邪恶在细节中?我知道。一个人说不可能是没有帮助的,比“没有帮助”更令人沮丧,而且有些人的意思是要劝阻别人。我通过https://stackoverflow.com/questions/22621164/how-to-automatically-generate-a-parser-code-to-code-translator-from-a-corpus听说了这个问题。
什么对你来说是完美的?这就是您定义下一个目标并可能达到总体目标的方式。
我更感兴趣的是知道我可以对代码实施什么样的模式以使其更容易翻译(即:IoC、SOA?)代码,而不是如何进行翻译。
我没有看到不能至少以不完美的方式从一种语言翻译成另一种语言的模式。由于可以进行语言到语言的翻译,因此您最好首先针对这一点。因为,我认为根据http://en.wikipedia.org/wiki/Graph_isomorphism_problem,两种计算机语言之间的翻译是树或 DAG 同构。即使我们已经知道它们都是图灵完备的,所以...
框架-> 框架,我更好地将其可视化为 API-> API 翻译,您可能仍然需要牢记这一点,以改进生成的代码。例如:Prolog 作为非常具体的语法,但您仍然可以通过在 Python 中描述相同的图形来进行类似 Prolog 的计算...如果我要实现 Prolog 到 Python 的翻译器,我不会在 Python 中实现统一,而是在 C 库中实现加上对 Pythonist 来说非常易读的“Python 语法”。最后,语法只是我们赋予意义的“绘画”(这就是我开始方案的原因)。邪恶在于语言的细节,我不是在谈论语法。语言中使用的概念getattribute钩子(你可以没有它)但是像尾递归优化这样的虚拟机特性可能很难处理。您不在乎初始程序是否不使用尾递归,即使目标语言中没有尾递归,您也可以使用 greenlets/event loop 来模拟它。
对于目标语言和源语言,请查找:
由此会出现:
您可能还能够知道什么将被翻译成快速和慢速代码。
还有 stdlib 或任何库的问题,但没有明确的答案,这取决于您的目标。
惯用代码或可读生成代码也有解决方案......
定位像 PHP 这样的平台比定位浏览器容易得多,因为您可以提供慢速和/或关键路径的 C 实现。
鉴于您的第一个项目是将 Python 转换为 PHP,至少对于我所知道的 PHP3 子集而言,自定义 veloce.py 是您最好的选择。如果您可以为 PHP 实现 veloce.py,那么您可能可以运行兼容模式...此外,如果您可以将 PHP 转换为 PHP 的子集,您可以使用 php_veloce.py 生成,这意味着您可以将 PHP 转换为veloce.py 可以使用的 Python 子集,这意味着您可以将 PHP 转换为 Javascript。只是说...
您还可以查看这些库:
您也可能对这篇博文(和评论)感兴趣:https ://www.rfk.id.au/blog/entry/pypy-js-poc-jit/