【问题标题】:Generate output along with recursive descent parser与递归下降解析器一起生成输出
【发布时间】:2011-10-20 04:58:12
【问题描述】:

编写简单的解析器是微不足道的,多年来我已经实现了几个。在大学里,我们也不得不写一篇。但是我们不必使用这种方法生成有意义的输出; 我们从未学习过如何创建后端。

如果我有一个用于简化 Pascal 的递归下降解析器,并且我想将代码转换为 C++,我将如何做呢?我认为不需要生成抽象语法树等中间步骤。

那么我该如何输出已编译或翻译后的代码?我发现的唯一有用的例子是在 Jack Crenshaw 的教程中,但它更侧重于前端,与大多数其他代码一样资源。我的解析器代码和我的语法之间的关系非常明显。解析器方法和输出之间的关系如何?我的函数声明解析器方法可以与一个 EmitLn(C++ code here) 调用完全相关。但是那些不那么容易的解析器方法呢,比如表达式。表达式被分解为可能更多的调用,因此暗示需要一个 Emit() 函数,它允许我逐段分解表达式的输出代码。 有没有输出代码的样板代码,比如 Jack Crenshaw 的 Lets Build a Compiler 中的 EmitLn 函数?这也表明我需要维护一个基本的符号表,这是大多数示例中经常省略的另一件事。

我说的对吗?我还应该知道什么?任何提示/建议或资源?我的大问题是,编译器前端有很多教程,但是后端的一些解释怎么样?我可以解析语言,现在我想将其发展为能够将它们翻译和编译成其他语言。

【问题讨论】:

    标签: parsing compilation recursive-descent compiler-construction


    【解决方案1】:

    有一些简单的编译器使用“即时”代码生成器来吐出代码 当他们解析时,正如您似乎正在尝试做的那样。好消息是它们看起来 容易。

    问题在于编译从根本上说是关于从 代码的一部分到另一部分,以实现良好的代码生成。为了那个原因, 编译器人很久以前就决定将解析与代码生成分开。 解析器解析代码并构建另一个中间数据结构(通常是 表示程序的抽象语法树或三元组。

    通常,对中间结构进行非常深入的分析 确定如何生成好的代码,其次是合适的好 代码生成。做好这件事的技术很复杂, 但出于产生良好(和正确)代码的需要而受到很好的激励。

    尝试检查标准编译器资源。 Learning to write a compiler

    非常值得你 是时候详细阅读其中的一些参考资料了,而不是进行黑客攻击。 如果你坚持只看一本,《Aho and Ullman》就是经典之作。

    如果您想了解如何动态生成代码 可以正常工作,请查看 metacompilers.

    【讨论】:

    • 您并没有真正回答我的问题,也没有提供任何关于如何在解析过程中生成代码的具体想法,但您确实提供了很好的信息,而且元编译器的建议非常有趣,谢谢。
    • @Azh:对,我没有,因为我想强调另一点。只需在递归下降解析器代码的识别部分插入打印语句,就很容易“生成(坏)代码”。问题是,如何做到这一点,以及打印什么?元编译器(MetaII,引用)通过让您指定与语法交错打印的内容,将这一点发挥到了极致;两者都被编译成一个递归下降编译器,可以即时生成代码。但是,要生成好的代码,您需要 context,例如,程序其他部分的内存,而这正是失败的地方。
    【解决方案2】:

    简答匹配语法中的规则后,调用一个根据输入执行某些操作的函数。

    长答案:来自 Terence Parr 讨论语法导向解释器的语言实现模式:

    语法导向的解释器不会创建 AST 或将源代码转换为字节码...解释器直接根据语法来执行语句。

    这正是我在最初的问题中所追求的想法。他在书中继续描述了这项技术、它的工作原理、它的组成部分以及局限性。它适用于 DSL 和小型语言,而不是通用语言。

    他指出,IF 和循环等结构难以实现。虽然这很可能是真的,但我用我的语言Peay BASIC 实现这些功能没有问题。作为指令序列或简单语句的语言非常适合这种模式;他以文本处理语言为例。

    语法被扩展为由“匹配这个,调用那个”对组成。所以语法中的必要规则应该有一个与之相关的动作。赋值语句示例:

    assignment : ID '=' expr {interp.assign($ID, $expr.value);};
    expr returns [Object value] : ... ;
    

    【讨论】:

    • Parr 的描述显然是关于语法导向的解释器,但您专门询问了编译翻译 代码。作为一个显着的区别,一个语法导向的 compiler 可以很容易地发出一个“goto”语句以便稍后执行,而 Parr(正确地说)一个 interpreter 不能将这些处理为嗯...因为它不知道目标在哪里。我认为你误解了帕尔所说的话。我不知道你是如何实现你的 Basic 的,但如果你可以执行 goto,你就不会动态生成代码,而这正是语法导向翻译器通常所做的。
    • 我为什么还要质疑你?哦,是的,因为我每次都学到很多东西......我会留下我的答案,但再次接受你的答案。前几天读到这一章时,我有一种似曾相识的感觉,不禁认为它适用于这个问题。
    猜你喜欢
    • 2015-08-07
    • 1970-01-01
    • 1970-01-01
    • 2012-05-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多