我一直在研究编译器。词法分析器似乎非常直接:取一个“句子”并将其分解为单词(或标记)。为了确保正确的语法,需要一个解析器。解析器通常使用令牌并构建一个树,从而生成根节点(将单词分为句子,段落,页面等等。)
从this question开始,解析器似乎会构建一个AST。 AST只包含执行代码所需的内容,所以括号之类的内容是不必要的,因为运算符优先级内置在AST中。 AST可能都是编译器需要的。
但是将代码从一种语言转换为另一种语言呢?采用一种伪造的语言(语法)或现有的语法并将其转换为另一种语言,其中运算符优先级规则可能会有所不同?运算符优先级是否也“内置”到CST?
举个例子,假设我编写了一种语言,并希望将其翻译成PHP代码。大多数语言的三元运算符具有从右到左的关联性。 PHP错误地使用从左到右的关联性(see more about this here)。我希望“我的语言”从右到左使用,但生成的PHP代码必须应用括号才能在PHP中获得正确的结果(使用link to Wikipedia,结果需要是“train”而不是“horse” )。
那么对于语言翻译,CST会更好吗?运营商优先级通常是否构建在CST中?介于两者之间吗?有没有例子比较两个树和一个简单的代数方程?任何说明三元运算符的例子?
(“转码”是“编程语言翻译”的正确用语吗?谷歌搜索会带来转换媒体。)
我想弄清楚的是:何时使用另一个比较合适?
答案 0 :(得分:7)
用于模拟源语言所有语义细节的AST就是您所需要的。根据定义,如果它确实对语义进行了正确的建模,并且你的语言包含一个三元运算符,那么它也会正确地模拟应用运算符的特定顺序(例如,先验模数覆盖的结果,如括号)。 / p>
所以你的问题不在AST中。它使用优先级不同的类似(三元)运算符生成另一种语言。
这是代码生成中一个古老的问题:目标的运算符与源的运算符不完全匹配,因此输出不能是一对一的。在您的情况下,您应该能够通过生成带有括号的PHP三元运算符来解决问题,以控制顺序以实现原始语义,因此这不是一个大问题。
通常,生成实现所需结果的代码序列可能非常复杂,并且有很多方法可以实现。这就是编译器书籍厚而不是薄的原因。你似乎已经隐含地决定“获取AST,走AST,吐代码”;这几乎是一个动态代码生成器。如果您不关心生成的代码是否特别好,并且目标语言非常接近源语言,那么这种方法就足够了。
如果代码生成问题更复杂,通常会发生的是AST用于生成计算的数据流模型,包括生成结果的运算符,并消耗先前运算符的结果,基于获取变量值和常量的“运算符”。然后遍历数据流表示以生成代码;这样做的好处是,您可以在数据流表示中选择一个运算符,找到目标语言中匹配的代码序列,生成该序列,然后担心如何收集操作数。更好的方案将数据流子图(表示等效的复合目标语言结构)与生成的数据流图进行匹配;这可以产生明显更好的代码。通常,可以在生成原始代码之后应用特定于目标语言的优化,以生成更好的代码。在这两种情况下,您都必须担心管理运营商的结果;它们可以直接送到下一个目标语言操作符,还是必须进入某种临时存储(对于机器代码,这可以是另一个寄存器或内存位置)。做这一切并不容易;再次,这就是编译器书籍不薄的原因。
这个想法的变体是源到源程序转换。这将源代码中的构造“直接”映射到目标代码中的构造,尽管这通常是在后台通过操作AST来完成的,因为未解析的编程语言文本很难匹配。我们的DMS Software Reengineering Toolkit就是这种系统的一个例子。使用这样的工具,您可以在源语言中编写模式(隐式地与解析树匹配),以及目标语言中的相应模式(隐式生成目标语言AST)。您可以编写复杂的源或目标构造,从而提供上面匹配的数据流图的大部分效果。后代优化包含更多重写规则,可将目标代码转换为目标代码。
结论:除非你的翻译真的微不足道,否则有一个AST是不够的。 您可以在此SO答案中详细了解您的需求:https://stackoverflow.com/a/3460977/120163
警告:大声的意见如下。
Re“Transcoder”:我更喜欢术语“编译”,“翻译”或“源到源”编译器。近40年来,我一直在构建程序分析和操作工具。在我遇到这个问题之前,我从未听过“转码器”这个术语:Experience migrating legacy Cobol/PL1 to Java和描述恕我直言的回复是一个名为NACA的真正糟糕的代码转换方案。从那以后我听说这个词正在获得牵引力;我不明白为什么当我们有完全适当的术语时,我们必须发明另一个术语。通常这是某人发明高级祭司的标志; “让我们发明一个闪亮的新术语,这样人们就不会真正理解我们正在做的事情”。我很高兴将这个词留给真正糟糕的翻译。