问题:我可以对代码执行哪种模式以使其更容易转换为另一种编程语言?[关闭]
我正着手做一个副项目,目标是将代码从一种编程语言转换为另一种编程语言。我开始使用的语言是PHP和Python(Python到PHP应该更容易入手),但理想情况下,我可以(相对)轻松地添加其他语言。该计划是:
这是针对Web开发的。原始代码和目标代码将位于框架的顶部(我也将不得不编写这些框架)。这些框架将包含MVC设计模式并遵循严格的编码约定。这应该使翻译更加容易。
我还在研究IOC和依赖项注入,因为它们可能使翻译过程更容易且更不易出错。
我将使用Python的解析器模块,该模块可让我摆弄抽象语法树。显然,我可以用PHP获得的最接近的是token_get_all(),这是一个开始。
从那时起,我可以构建AST,符号表和控制流程。
然后,我相信我可以开始输出代码了。我不需要完美的翻译。我仍然需要查看生成的代码并解决问题。理想情况下,翻译人员应标记有问题的翻译。
在您问“这到底是什么意思?”之前 答案是……这将是一次有趣的学习经历。如果您对如何减少这种麻烦有任何见解,请告诉我。
编辑:
我更想知道我可以对代码强制执行哪种类型的模式,而不是如何进行翻译,从而使代码的翻译(即:IoC,SOA?)更容易。
回答 0
自1995年以来,在强大的计算机科学家团队的支持下,我一直在构建工具(DMS Software Reengineering Toolkit)来进行通用程序处理(语言翻译为特例)。DMS提供通用解析,AST构建,符号表,控制和数据流分析,转换规则的应用,带有注释的源文本的再生等,所有这些都通过计算机语言的显式定义进行参数化。
机器,你需要做到这一点的量也为广大的(特别是如果你希望能够在一个通用的方式来做到这一点对于多国语言),然后你需要用不可靠的定义语言可靠分析器(PHP是这个完美的例子)。
您考虑构建或尝试进行语言到语言的翻译并没有错,但是我认为您会发现,对于真正的语言而言,这是一项比您期望的大得多的任务。我们仅在DMS上投入了大约100个人年,在每种“可靠”的语言定义(包括我们为PHP痛苦地构建的一种语言)上又花了6到12个月的时间,对于讨厌的语言(例如C ++)则投入了更多。这将是“一次学习经历”;这一直在我们身上。(您可能会发现上述网站上的“技术论文”部分有趣,可快速开始学习)。
人们经常尝试从某种他们熟悉的技术入手来构建某种通用的机器。(Python AST是一个很好的例子)。好消息是,这项工作已经完成。坏消息是,机械中内置了无数种假设,直到您尝试将其用于其他用途之前,您几乎不会发现其中的大部分假设。到那时,您发现机器已连接起来可以执行其最初的工作,并且会真的,真的会抵制您使它做其他事情的尝试。(我怀疑尝试让Python AST建模PHP会很有趣)。
我最初开始构建DMS的原因是建立的基础很少内置这样的假设。它使我们有些头痛。到目前为止,还没有黑洞。(在过去的15年中,我工作中最难的部分是试图防止这种假设蔓延)。
很多人也犯了一个错误的假设,即如果他们可以解析(并且可能获得AST),那么他们就可以做复杂的事情了。困难的教训之一是,您需要符号表和流程分析才能进行良好的程序分析或转换。AST是必要的,但还不够。这就是Aho&Ullman的编译器书不止于第二章的原因。(OP拥有此权利,因为他计划在AST之外构建其他机器)。有关此主题的更多信息,请参见解析后的生命。
关于“我不需要完美的翻译”的评论很麻烦。弱翻译的工作是转换80%的“简单”代码,而剩下20%的代码要手工完成。如果要转换的应用程序很小,并且只打算转换一次,那么20%就可以了。如果要转换许多应用程序(甚至是随时间变化很小的同一应用程序),那不是很好。如果您尝试转换100K SLOC,则20%是20,000原始代码行,这些代码很难翻译,理解和修改,而您还无法理解另外80,000行已翻译程序。这需要大量的努力。在百万行级别,这实际上是不可能的。更难,他们通常会长时间拖延,付出高昂的代价并经常彻底失败,这很痛苦。
要翻译大型系统,您需要拍摄的是90%的高转换率,或者您可能无法完成翻译活动的手动部分。
另一个关键考虑因素是要翻译的代码大小。即使使用良好的工具,也要花费大量的精力来构建能正常运行的强大翻译器。尽管构建翻译器而不是简单地进行手动转换似乎很酷,而且很酷,但是对于较小的代码库(例如,根据我们的经验,最多10万个SLOC),从经济角度讲,这样做并不合理。没有人喜欢这个答案,但是,如果您真的只需要翻译10K SLOC代码,则最好是硬着头皮做一下。是的,那很痛苦。
我认为我们的工具非常出色(但后来我颇有偏见)。建立一个好的翻译仍然非常困难。我们大约需要1.5到2个人工年,我们知道如何使用我们的工具。不同之处在于,有了如此多的设备,我们成功的次数多于失败的次数。
回答 1
我的答案将解决解析Python以便将其翻译为另一种语言的特定任务,而不是Ira在其答案中很好解决的更高层次的方面。
简而言之:不要使用解析器模块,这是一种更简单的方法。
ast
自Python 2.6起提供的模块更加适合您的需求,因为它为您提供了现成的AST可以使用。我已经写了一本关于文章最后一年,但在短,使用parse
的方法ast
将Python源代码解析为AST。该parser
模块将为您提供一个解析树,而不是AST。小心区别。
现在,由于Python的AST非常详细,因此对于AST来说,前端工作并不困难。我想您可以很快为功能的某些部分准备一个简单的原型。但是,获得完整的解决方案将花费更多时间,这主要是因为语言的语义不同。语言的一个简单子集(功能,基本类型等)可以轻松翻译,但是一旦进入更复杂的层次,您将需要笨拙的机制来模仿一种语言的核心。例如,考虑一下Python的生成器和列表理解,这在PHP中是不存在的(据我所知,当涉及到PHP时,这是很差的)。
为了给您最后的提示,请考虑2to3
由Python开发人员创建的将Python 2代码转换为Python 3代码的工具。从前端来看,它具有将Python转换成某种东西所需的大多数元素。但是,由于Python 2和3的内核相似,因此那里不需要仿真机制。
回答 2
编写翻译不是没有可能,尤其是考虑到乔尔的实习生是在夏天完成的。
如果您想讲一种语言,这很容易。如果您想做更多的事情,那会有些困难,但不要太多。最难的部分是,尽管任何图灵完备的语言都可以完成另一种图灵完备的语言所能做的事情,但是内置数据类型却可以显着改变一种语言所要做的事情。
例如:
word = 'This is not a word'
print word[::-2]
需要很多复制的C ++代码(好的,您可以使用一些循环结构来做得很短,但是仍然可以)。
我想那是一个问题。
您是否曾经根据语言语法编写过分词器/解析器?如果没有,您可能想学习如何做,因为这是该项目的主要部分。我要做的是提供基本的Turing完整语法-与Python 字节码相当相似 。然后创建一个采用语言语法的词法分析器/解析器(也许使用BNF),并基于该语法将语言编译为中间语言。然后,您需要做的是相反的操作-根据语法将您的语言创建为目标语言的解析器。
我看到的最明显的问题是,一开始您可能会创建极其低效的代码,尤其是在Python等功能更强大的语言中。
但是,如果以这种方式进行操作,那么您可能会一直想出优化输出的方法。总结一下:
- 阅读提供的语法
- 将程序编译成中间(也包括图灵完整)语法
- 将中间程序编译成最终语言(基于提供的语法)
- …?
- 利润!(?)
*功能强大,我的意思是这需要4行:
myinput = raw_input("Enter something: ")
print myinput.replace('a', 'A')
print sum(ord(c) for c in myinput)
print myinput[::-1]
向我展示另一种可以在4行中完成类似工作的语言,并且我将向您展示一种与Python一样强大的语言。
回答 3
有几个答案告诉您不要打扰。好吧,那有什么帮助?你想学习吗?你可以学习。这是编译。碰巧您的目标语言不是机器代码,而是另一种高级语言。这一直都在做。
有一种相对简单的入门方法。首先,进入http://sourceforge.net/projects/lime-php/(如果您要使用PHP)或类似的代码,并查看示例代码。接下来,您可以使用一系列正则表达式编写词法分析器,并将令牌提供给生成的解析器。您的语义动作既可以直接使用另一种语言输出代码,也可以构建一些数据结构(例如对象,人),您可以对其进行按摩和遍历以生成输出代码。
您对PHP和Python很幸运,因为在很多方面,它们是彼此相同的语言,但是语法不同。困难的部分是克服语法形式和数据结构之间的语义差异。例如,Python具有列表和字典,而PHP仅具有assoc数组。
“学习者”方法是为语言的受限子集(例如仅打印语句,简单的数学和变量赋值)构建可以正常运行的内容,然后逐步消除限制。这基本上就是该领域的“大人物”所做的。
哦,由于您在Python中没有静态类型,因此最好编写并依赖PHP函数,例如“ python_add”,该函数根据Python的执行方式添加数字,字符串或对象。
显然,如果您允许它会变得更大。
回答 4
对于使用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营销,实际上也不是(不要犹豫告诉我,网络礼仪是否看起来不正确):
使用原型继承在Plain Old 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是一个库,您只需要在html中包含.py文件即可执行,开发人员无需进行任何编译阶段。
尝试过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完全用Javascript编写,例如Skulpt,不需要编译和大量的绒毛…而是用JavaScript编写。
自从在该项目的过程中编写了第一行代码以来,我就了解PyPy,甚至包括PyPy的JavaScript后端。是的,如果找到它,您可以直接从PyPy用JavaScript生成Python解释器。人们说,那是一场灾难。我没有读到为什么。但是我认为原因是它们用于实现解释器的中间语言RPython是为转换为C(也许是asm)而定制的Python子集。艾拉·巴克斯特(Ira Baxter)说,在构建某些东西时,您总是会做一些假设,并且可能会对其进行微调,使其在PyPy:Python-> C转换的情况下达到最佳效果。这些假设在其他情况下可能不相关,更糟糕的是,它们可以推断出开销,否则,说直接翻译很可能总是会更好。
用Python编写解释器听起来是一个(非常)好主意。但是出于性能原因,我对编译器更感兴趣,实际上将Python编译为JavaScript比解释它更容易。
我以将可以轻松转换为JavaScript的Python子集组合在一起的想法开始了PythonJS。起初,由于过去的经验,我什至没有去实施OO系统。我实现的翻译成JavaScript的Python子集是:
- 在定义和调用中具有全参数语义的函数。这是我最引以为傲的部分。
- while / if / 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贡献者)的发现非常好。
- 全局关键字
- 关闭
- Lambdas
- 清单理解
- 通过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中找到 //bitbucket.org/amirouche/pythonium/src/33898da731ee2d768ced392f1c369afd746c25d7/pythonium/veloce/veloce.py?at master 793 SLOC +大约100 SLOC与其他翻译器共享的代码。
可以在Veloce模式下翻译pystones.py的改编版本。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兼容代码也是一项巨大的努力。
- getattributre:实际的getattribute解析算法有点棘手,它仍然不支持数据描述符
- 基于元类的类:我知道在哪里插入代码,但仍然…
- 最后一点不是最重要的:some_callable(…)始终转换为“ call(some_callable)”。AFAIK转换程序根本不使用推理,因此,每次调用时,都需要检查调用该对象的方式,以及调用该对象的方式。
这部分在https://bitbucket.org/amirouche/pythonium/src/33898da731ee2d768ced392f1c369afd746c25d7/pythonium/compatible/runtime.py?at=master中进行了分解它是用Python编写的,与Python Veloce兼容。
实际的兼容翻译器https://bitbucket.org/amirouche/pythonium/src/33898da731ee2d768ced392f1c369afd746c25d7/pythonium/compatible/compatible.py?at=master不会直接生成JavaScript代码,最重要的是不会进行ast-> ast转换。我尝试过ast-> ast事情,即使ast.NodeTransformer比cst都好,但也无法使用ast。> NodeTransformer,更重要的是,我不需要做ast-> ast。
就我而言,至少对python ast做python ast可能会提高性能,因为我有时会在生成与块相关的代码之前检查块的内容,例如:
- var / global:要能够var某些东西,我必须知道我需要什么,而不是var。无需生成跟踪在给定块中创建哪个变量并将其插入到生成的功能块顶部的块,而是在进入该块之前实际访问子节点以生成相关代码之前,我只是寻找启示性的变量分配。
- 到目前为止,生成器在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/compatible/builtins/?at=master
可以理解从pythonium兼容生成的JS代码的阅读,但是源映射将有很大帮助。
根据这种经验,我可以给您的宝贵建议是老屁:
- 无论是在文献上还是在现有项目中,都对该主题进行了广泛的审查,这些项目是封闭的或免费的。当我回顾现有的不同项目时,我应该给它更多的时间和动力。
- 问问题!如果我事先知道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 ++解析器。细节是邪恶的吗?我知道。仅仅说不可能是没有帮助的,它比令人沮丧的“没有帮助”还要糟糕,而且有些人会劝阻其他人。我通过听说了这个问题 /programming/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转换可视化为Framework-> Framework,但您可能仍要牢记这些内容,以改进生成的代码。例如:Prolog是非常特定的语法,但是您仍然可以通过在Python中描述相同的图形来像计算一样进行Prolog …如果我要实现从Prolog到Python的转换器,我不会在Python中实现统一,而是在C库中实现带有“ Python语法”,这对于Python编写者来说非常容易理解。最后,语法只是我们赋予其含义的“绘画”(这就是我开始使用scheme的原因)。语言的细节是邪恶的,我不是在谈论语法。语言中使用的概念 getattribute钩子(您可以没有它),但是所需的VM功能(如尾递归优化)可能很难处理。您不必担心初始程序是否不使用尾部递归,即使目标语言中没有尾部递归,也可以使用greenlets / event循环来模拟它。
对于目标语言和源语言,请查找:
- 大而具体的想法
- 微小且共同的想法
由此将出现:
- 容易翻译的东西
- 难以翻译的事物
您也许还可以知道将翻译成快速和慢速代码的内容。
还有stdlib或任何库的问题,但没有明确的答案,这取决于您的目标。
成语代码或可读的生成代码也有解决方案…
因为可以提供慢速和/或关键路径的C实现,所以针对PHP之类的平台比针对浏览器要容易得多。
鉴于您的第一个项目是将Python转换为PHP,至少对于我所知道的PHP3子集,自定义veloce.py是最好的选择。如果您可以为PHP实现veloce.py,则可能可以运行兼容模式…同样,如果您可以将PHP转换为可以用php_veloce.py生成的PHP子集,则意味着您可以将PHP转换为veloce.py可以使用的Python子集,这意味着您可以将PHP转换为Javascript。只是说…
您还可以查看这些库:
另外,您可能对此博客文章(和评论)感兴趣:https : //www.rfk.id.au/blog/entry/pypy-js-poc-jit/
- 伊拉克·百特(Ira Baxter)的这次Google技术讲座很有趣https://www.youtube.com/watch?v=C-_dw9iEzhA