我可以在代码上强制执行哪种模式,以便于将其转换为另一种编程语言?
我正在着手做一个侧面项目,其目标是将代码从一种编程语言翻译到另一种编程语言。 我开始使用的语言是PHP和Python(从Python到PHP应该更容易一些),但理想情况下,我可以轻松地添加其他语言。 计划是:
-
这是面向web开发。 原始代码和目标代码将被放在框架之上(我也将写)。 这些框架将包含MVCdevise模式,并遵循严格的编码约定。 这应该使翻译更容易一些。
-
我也在研究IOC和dependency injection,因为它们可能会使翻译过程更容易,也更不容易出错。
-
我将利用Python的parsing器模块 ,它让我摆弄抽象语法树。 显然我能用PHP得到的最接近的是token_get_all() ,这是一个开始。
-
从那时起,我可以build立AST,符号表和控制stream程。
那我相信我可以开始输出代码了。 我不需要一个完美的翻译 。 我仍然需要查看生成的代码并修复问题。 理想情况下,翻译者应该标记有问题的翻译。
在你问“这到底是什么? 答案是…这将是一个有趣的学习经验。 如果您对如何使这个不那么艰巨的任何见解,请让我知道。
编辑:
我更感兴趣的是知道我可以在代码上执行什么样的模式,以便更易于翻译(即:IoC,SOA?)代码,而不是如何进行翻译。
自1995年以来,我一直在构build工具(DMS Software Reengineering Toolkit),以执行通用程序操作(语言翻译是一种特殊情况),得到了一个强大的计算机科学家团队的支持。 DMS提供通用parsing,AST构build,符号表,控制和数据stream分析,翻译规则的应用,带有注释的源文本的再生等,所有这些都通过计算机语言的明确定义来进行参数化。
你需要做的工作量非常大(特别是如果你想以一般的方式为多种语言做到这一点),然后你需要可靠的parsing器的定义不可靠的语言(PHP是这样的完美的例子)。
你想build立一个语言到语言的翻译者或尝试它,没有什么错,但我认为你会发现这是一个真正的语言比预期的更大的任务。 我们在DMS中投入了大约100个人工时间,而在每个“可靠”语言定义(包括我们为PHP构build的那个)中另外还有6-12个月,更多的是用于讨厌的语言,比如C ++。 这将是一个“学习经验的地狱”; 它已经为我们。 (您可能会发现上述网站上的技术论文部分有趣地启动了这个学习)。
人们常常试图用某种他们熟悉的技术开始build立某种广义的机器,这是一部分工作。 (Python的AST是很好的例子)。 好消息是,这部分工作已经完成。 坏消息是,机器内置了许多假设,其中大部分假设只有在你试图去做其他事情时才会发现。 在这一点上,你会发现机器是有线的做它原来做的事情,而且真的会真的抵制你试图做别的事情。 (我怀疑试图让Python ASTbuild模PHP将会很有趣)。
我最初开始build造DMS的原因是为了build立这样的基础,这些基础很less有这样的假设。有一些令我们头痛的问题。 到目前为止,没有黑洞。 (过去15年我工作中最困难的部分就是设法防止这种假设的蔓延)。
许多人也犯了一个错误,认为如果他们可以parsing(也许可以得到一个AST),他们正在做一些复杂的事情。 其中一个难点在于您需要符号表和stream程分析来进行良好的程序分析或转换。 AST是必要的,但并不足够。 这就是Aho&Ullman的编译器书籍在第2章没有停止的原因。(OP有这个权利,因为他计划在AST之外build立更多的机器)。 有关此主题的更多信息,请参阅parsing后的生活 。
关于“我不需要一个完美的翻译”的评论是麻烦的。 译者所做的是把80%的“简单”的代码转换成20%的代码。 如果您打算转换的应用程序非常小,并且您只打算将其转换一次,那么这20%是可以的。 如果你想转换很多应用程序(甚至是一个随着时间的推移而发生较小变化的应用程序),这是不好的。 如果您尝试转换100K SLOC,那么20%是20,000个原始代码行,难以翻译,理解和修改另外80,000行已翻译的程序的上下文中,您已经不明白。 这需要付出巨大的努力。 在百万行级别,这在实践中根本不可能。 (令人惊讶的是,有些人不信任自动化工具,坚持用手工翻译百万行系统,这更困难 ,他们通常会发现很长的时间延迟,高成本,经常彻底失败的痛苦。
你必须拍摄翻译大型系统是九十年代的高转换率,否则很可能你不能完成翻译活动的手册部分。
另一个关键考虑因素是要翻译的代码的大小。 即使使用良好的工具,也需要耗费大量精力build立一个工作强大的翻译工具。 虽然build立一个翻译器,而不是简单地进行手动转换似乎性感和酷,但对于小代码库(例如,在我们的经验中高达大约100K SLOC)来说,经济学根本就没有理由这样做。 没有人喜欢这个答案,但是如果你真的需要翻译10K SLOC的代码,那么你可能最好只是咬紧牙关去做。 是的,这是痛苦的。
我认为我们的工具是非常好的(但后来,我很偏颇)。 要build立一个好的翻译,还是很难的。 它需要我们大约1.5-2人年,我们知道如何使用我们的工具。 不同的是,有了这么多的机器,我们成功的次数要比失败的次数多得多。
我的答案将解决parsingPython的具体任务,以便将其翻译成另一种语言,而不是艾拉克在他的回答中提到的更高层次的方面。
总之: 不要使用parsing器模块,有一个更简单的方法。
自从Python 2.6以来, ast
模块更适合您的需求,因为它为您提供了一个现成的AST。 我在去年写了一篇文章 ,但总之,使用ast
的parse
方法将Python源代码parsing为AST。 parser
模块会给你一个parsing树,而不是AST。 谨防差异 。
现在,由于Python的AST非常详细,给AST一个前端工作并不难。 我想你可以有一个简单的原型function的一些部分准备相当快。 但是,获得完整的解决scheme将需要更多的时间,主要是因为语言的语义不同。 语言的简单子集(函数,基本types等)可以很容易地翻译,但是一旦进入更复杂的层次,就需要大量的机器来模仿另一种语言的核心。 例如,考虑一下Python中不存在的Python生成器和列表parsing(据我所知,在涉及PHP的时候,这当然是不好的)。
为了给你一个最后的提示,考虑由Python开发人员创build的2to3
工具来将Python 2代码翻译成Python 3代码。 前端方面,它有你需要将Python翻译成某些东西的大部分元素。 但是,由于Python 2和Python 3的核心是相似的,因此在那里不需要仿真机器。
写一个翻译是不是不可能的,特别是考虑到乔尔实习生在一个夏天做了。
如果你想做一种语言,这很容易。 如果你想做更多的事情,那会更困难一些,但不要太多。 最难的部分是,尽pipe任何一个完整的语言都可以完成另一个完整的语言,但是内置的数据types可以改变一个语言在现实中的performance。
例如:
word = 'This is not a word' print word[::-2]
需要大量的C ++代码来复制(好的,你可以用一些循环的结构来完成,但仍然可以)。
我猜,这有点不合时宜。
你有没有写过基于语言语法的标记器/parsing器? 如果没有的话,你可能会想要学习如何做,因为这是这个项目的主要部分。 我会做的是提出一个基本的图灵完整的语法 – 一些非常类似于Python 字节码 。 然后,你创build一个语言语法(可能使用BNF )的词法分析器,并根据语法将语言编译成中间语言。 那么你想要做的就是做相反的事情 – 根据语法从你的语言创build一个parsing器到目标语言。
我看到的最明显的问题是,起初你可能会创build非常低效的代码,特别是在像Python这样的更强大的语言中。
但是,如果你这样做,那么你可能会find方法来优化输出。 总结:
- 阅读提供的语法
- 将程序编译成中间语言(也包括图灵完成语法)
- 将中间程序编译成最终语言(基于提供的语法)
- …?
- 利润!(?)
*强大的我的意思是这需要4行:
myinput = raw_input("Enter something: ") print myinput.replace('a', 'A') print sum(ord(c) for c in myinput) print myinput[::-1]
给我看另一种语言,可以做4行这样的事情,我会告诉你一种和Python一样强大的语言。
有几个答案告诉你不要打扰。 那么,这有什么帮助? 你想学习? 你可以学习。 这是汇编。 恰恰相反,您的目标语言不是机器代码,而是另一种高级语言。 这是一直做的。
有一个相对简单的方法来开始。 首先,去http://sourceforge.net/projects/lime-php/ (如果你想在PHP中工作)或者这样的,并通过示例代码。 接下来,您可以使用一系列正则expression式来编写词法分析器,并将令牌传递给您生成的parsing器。 你的语义动作可以直接用另一种语言输出代码,或者build立一些你可以按摩和遍历的数据结构(思考对象,人)来生成输出代码。
对于PHP和Python来说,你很幸运,因为在很多方面它们是相同的语言,但是语法不同。 困难的部分是克服语法forms和数据结构之间的语义差异。 例如,Python有列表和字典,而PHP只有assoc数组。
“学习者”的方法是build立一些对于语言的一个受限子集(比如只有打印语句,简单的math和variables赋值)可行的东西,然后逐步消除限制。 这基本上是这个领域的“大人物”所做的。
呵呵,既然你在Python中没有静态types,那么编写和依赖像“python_add”这样的PHP函数是最好的,它根据Python的方式添加数字,string或对象。
显然,如果你愿意,这可以变得更大。
我将第二@EliBendersky关于使用ast.parse而不是parsing器(我以前不知道)的观点。 我也热烈地推荐你回顾他的博客。 我用ast.parse来做Python-> JavaScript翻译(@ https://bitbucket.org/amirouche/pythonium )。 我已经提出了Pythoniumdevise,通过对其他实现进行一些审查并单独尝试。 我从https://github.com/PythonJS/PythonJS分叉了我也开始的Pythonium,实际上是一个完整的重写。; 整体devise灵感来自PyPy和http://www.hpl.hp.com/techreports/Compaq-DEC/WRL-89-1.pdf 。
我试过的一切,从开始到最好的解决scheme,即使它看起来像python营销真的不是(不要犹豫,告诉我,如果有什么似乎不正确的networking礼仪):
-
在Plain Old JavaScript中使用原型inheritance实现Python语义:AFAIK使用JS原型对象系统实现Python多重inheritance是不可能的。 以后我也尝试过使用其他技巧(参考getattribute)。 据我所知在JavaScript中没有Python多重inheritance的实现,存在的最好是单一的inhertance + mixins,我不知道他们处理钻石inheritance。 有点类似于Skulpt,但没有google clojure。
-
我尝试了Google clojure,就像Skulpt(编译器),而不是实际阅读Skulpt代码#fail。 无论如何因为基于JS原型的对象系统还是不可能的。 创build绑定是非常困难的,你需要编写JavaScript和许多样板代码(参考https://github.com/skulpt/skulpt/issues/50 ,我是鬼)。 当时没有明确的方法将绑定整合到构build系统中。 我认为Skulpt是一个库,你只需要将你的.py文件包含在html中就可以执行,开发者不需要编译阶段。
-
尝试pyjaco(编译器),但创build绑定(从Python代码调用Javascript代码)是非常困难的,有太多的样板代码创build每一次。 现在我认为pyjaco是更接近python的那个。 pyjaco是用Python编写的(ast.parse),但很多是用JavaScript编写的,它使用原型inheritance。
我从来没有成功地运行睡衣#fail,从来没有尝试再次读取代码#fail。 但在我看来,睡衣正在做API-> API转换(或框架框架),而不是Python到JavaScript的转换。 JavaScript框架使用页面中已有的数据或来自服务器的数据。 Python代码只是“pipe道”。 之后,我发现睡衣实际上是一个真正的python-> js翻译。
不过我认为可以做API-> API(或框架 – >框架)翻译,这就是我在Pythonium中所做的基本工作,但是在较低层次上。 睡衣可能使用与Pythonium相同的algorithm…
然后我发现brython完全用Javascript编写,就像Skulpt一样,不需要编译,也不需要很多绒毛……但是用JavaScript编写。
由于在这个项目过程中写的最初的一行,我知道PyPy,甚至PyPy的JavaScript后端。 是的,如果你find它的话,可以直接用PyPy在JavaScript中生成一个Python解释器。 人们说,这是一场灾难。 我没有读到为什么。 但我认为原因是他们用来实现解释器RPython的中间语言是Python的一个子集,可以被翻译成C(也许是asm)。 Ira Baxter说,当你构build一些东西的时候,你总是会做出一些假设,而且你可能会把它调整到最好的状态,就像PyPy:Python-> C的翻译一样。 这些假设可能在另一种情况下是不相关的,甚至可能造成开销,否则直接翻译最有可能总是会更好。
用Python编写的解释器听起来很像一个好主意。 但是由于性能方面的原因,我对编译器更感兴趣,因此将Python编译成JavaScript比解释它更容易。
我开始创buildPythonJS,把Python的一个子集放在一起,我可以很容易地把它转换成JavaScript。 起初我根本就没有因为过去的经验而实施面向对象系统。 我获得的用于翻译为JavaScript的Python子集包括:
- 在定义和调用中都具有全参数语义的function。 这是我最引以为傲的部分。
- 而/ IF / ELIF / ELSE
- Pythontypes被转换为JavaScripttypes(没有任何types的Pythontypes)
- for只能遍历Javascript数组(对于一个数组)
- 对JavaScript的透明访问:如果您在Python代码中编写数组,它将被转换为JavaScript中的数组。 这是在其竞争对手的可用性方面最大的成就。
- 您可以将Python源代码中定义的函数传递给javascript函数。 默认参数将被考虑在内。
- 它添加了一个特殊的function,被称为新的,被翻译成JavaScript的新的例如:新(Python)(1,2,垃圾邮件,“鸡蛋”)被翻译成“新的Python(1,2,垃圾邮件,”鸡蛋“)。
- “var”由翻译器自动处理。 (来自Brett(PythonJS撰稿人)非常好的发现。
- 全局关键字
- closures
- lambdaexpression式
- 列表parsing
- 通过requirejs支持导入
- 单类inheritance+ mixin通过classyjs
与Python的全面语义相比,这看起来很多,但实际上很窄。 这真的是一个Python语法的JavaScript。
生成的JS是完美的即。 没有开销,通过进一步编辑它在性能方面是无法改进的。 如果你可以改进生成的代码,你也可以从Python源文件中完成。 另外,编译器不依赖任何JS技巧,你可以在http://superherojs.com/写的;.js中find它,所以它非常易读。
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翻译之后,我select了另外一种将完整Python翻译成JavaScript的途径。 除了目标语言之外,glib做基于对象的基于类的代码的方式是JS,所以你可以访问数组,类地图对象和许多其他技巧,所有这些部分都是用Python编写的。 IIRC在Pythonium翻译器中没有编写JavaScript代码。 获得单一inheritance并不困难,这是Pythonium完全兼容Python的难点:
- Python中的
spam.egg
总是被转换为getattribute(spam, "egg")
我没有特别指出这一点,但我认为,它在哪里放松了很多时间,我不确定我可以用asm.js改进它或其他任何东西。 - 方法parsing顺序:即使用Python编写的algorithm,将其翻译成Python Veloce兼容代码也是一个很大的努力。
- getattributre :实际的getattributeparsingalgorithm是棘手的,它仍然不支持数据描述符
- 基于metaclass类:我知道在哪里插入代码,但仍然…
- 最后一个bu不是最不重要的:some_callable(…)总是转化为“call(some_callable)”。 AFAIK翻译根本没有使用推论,所以每次你打电话时,你都需要检查它是以什么样的方式调用它的方式。
这部分是在https://bitbucket.org/amirouche/pythonium/src/33898da731ee2d768ced392f1c369afd746c25d7/pythonium/compliant/runtime.py?at=master中进行考虑的。它是用与Python Veloce兼容的Python编写的。
实际符合的翻译器https://bitbucket.org/amirouche/pythonium/src/33898da731ee2d768ced392f1c369afd746c25d7/pythonium/compliant/compliant.py?at=master不会直接生成JavaScript代码,最重要的是不会执行ast-> ast转换。 我尝试了ast-> ast的东西,即使比cst更好,即使使用ast.NodeTransformer也不好,更重要的是我不需要做ast-> ast。
python ast在我的情况下至less可能会是一个性能改进,因为我有时在生成与之关联的代码之前检查一个块的内容,例如:
- var / global:能够var的东西我必须知道我需要什么,而不是var。 除了生成一个块跟踪在给定块中创build哪个variables并将其插入到生成的function块的顶部之外,我只是在实际访问子节点以生成关联代码之前进入块时查找相关的variables赋值。
- 在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有一些与引导types相关的限制,但是您可以访问所有可以在兼容模式下翻译Pythonium的内容。 看看https://bitbucket.org/amirouche/pythonium/src/33898da731ee2d768ced392f1c369afd746c25d7/pythonium/compliant/builtins/?at=master
阅读从pythonium生成的JS代码兼容可以理解,但源地图将大大帮助。
根据这种经验,我可以给你的宝贵意见是:
- 在文献和现有项目中广泛地回顾这个问题。 当我回顾现有的不同项目时,我应该给它更多的时间和动力。
- 问问题! 如果我事先知道PyPy后端由于C / Javascript语义不匹配而导致的开销是无用的。 我也许会在6个月前,也许3年前有过Pythonium的想法。
- 知道你想做什么,有一个目标。 对于这个项目,我有不同的目标:实践一个JavaScript,学习更多的Python,并能够编写将在浏览器中运行的Python代码(更多和下面)。
- 失败就是经验
- 一小步就是一步
- 从小开始
- 远大的梦想
- 做演示
- 迭代
只有Python Veloce模式,我非常高兴! 但是一路上我发现我真正想要的是从Javascript解放我和其他人,但更重要的是能够以一种舒适的方式创造 。 这导致我的计划,DSL,模型,并最终领域具体模型(参见http://dsmforum.org/ )。
关于Ira Baxter的回应:
估计没有任何帮助。 我为PythonJS和Pythonium带来了大概6个月的空闲时间。 所以我可以期待更多从全职6个月。 我想大家都知道,在企业背景下,100人年的意思可能并不意味着什么…
当有人说某些事情很难或者更不可能的时候,我回答说:“只需要一段时间才能find一个不可能解决的问题”,否则就说没有什么是不可能的,除非在这种情况下certificate不可能有mathcertificate。
如果没有被certificate是不可能的,那么它留下想象的空间:
- findcertificate这是不可能的certificate
和
- 如果不可能,可能会有一个“劣等”的问题,可以有一个解决scheme。
要么
- 如果不是不可能,find一个解决scheme
这不仅仅是乐观的想法。 当我开始Python-> Javascript时,大家都说这是不可能的。 PyPy不可能。 元类太难了。 等等…我认为PyPy在Scheme-C文件(25岁)上的唯一革命是一些自动的JIT生成(基于RPython解释器编写的提示)。
大多数人认为事情“难”或“不可能”都不能提供原因。 C ++很难parsing? 我知道,他们仍然是(免费的)C ++parsing器。 邪恶是在细节? 我知道。 说单靠不可能是没有帮助的,甚至比“没有帮助”更令人沮丧,而有些人则是劝阻别人。 我听说过这个问题通过https://stackoverflow.com/questions/22621164/how-to-automatically-generate-a-parser-code-to-code-translator-from-a-corpus 。
什么对你来说是完美的 ? 这就是你如何定义下一个目标,也许达到了总体目标。
我更感兴趣的是知道我可以在代码上执行什么样的模式,以便更易于翻译(即:IoC,SOA?)代码,而不是如何进行翻译。
我看不出任何不能从一种语言翻译成另一种语言的模式,至less不是完美的方式。 由于语言到语言的翻译是可能的,所以你最好先瞄准这一点。 因为,我认为根据http://en.wikipedia.org/wiki/Graph_isomorphism_problem ,两种计算机语言之间的翻译是树或DAG同构。 即使我们已经知道他们都是完整的,所以…
框架 – >框架,我更好地形象化为API-> API翻译可能仍然是你可能牢记的一种方法来改善生成的代码。 例如:Prolog作为非常具体的语法,但仍然可以通过在Python中描述相同的graphics来进行Prolog计算…如果我要实现一个Prolog to Python翻译器,我不会在Python中实现统一,而是在C库中实现统一使用一个Python语言非常可读的“Python语法”。 最后,语法只是“绘画”,我们赋予其意义(这就是为什么我开始计划)。 邪恶是在语言的细节,我不是在谈论的语法。 在getattribute hook语言中使用的概念(你可以没有它),但是像tail-recursion优化这样的虚拟机function是很难处理的。 你不关心,如果最初的程序不使用尾recursion,即使没有目标语言的尾recursion,你可以使用greenlets /事件循环来模拟它。
对于目标语言和源语言,请查找:
- 大而具体的想法
- 微小和普遍的共同想法
从此将出现:
- 事情很容易翻译
- 事情很难翻译
你也可能会知道什么会被转换成快速和慢速的代码。
还有stdlib或任何库的问题,但没有明确的答案,这取决于你的目标。
语言代码或可读的生成代码也有解决scheme…
针对像PHP这样的平台比定位浏览器容易得多,因为您可以提供缓慢和/或关键path的C实现。
鉴于你第一个项目是将Python翻译成PHP,至less对于我所知道的PHP3子集来说,定制veloce.py是最好的select。 如果你可以为PHP实现veloce.py,那么你可能会运行兼容模式…另外,如果你可以将PHP翻译成PHP的子集,你可以用php_veloce.py生成,这意味着你可以把PHP翻译成Python的子集veloce.py可以消耗,这意味着你可以将PHP翻译成Javascript。 只是说…
你也可以看看这些库:
您也可以通过这篇博客(和评论)感兴趣: https : //www.rfk.id.au/blog/entry/pypy-js-poc-jit/
- 这个来自Ira Baxter的Google技术讲座很有趣https://www.youtube.com/watch?v=C-_dw9iEzhA
你可以看一下Vala编译器 ,它将Vala(一种类似于C#的语言)翻译成C语言。