简单来说,任何人都可以解释“语法定向翻译”的含义吗?我开始从 Dragon Book 中读到这个主题,但无法理解。 Wiki article也没有帮助。
答案 0 :(得分:19)
简单来说,“语法定向翻译”意味着使用语法识别器(解析器)驱动整个编译(翻译)过程。
从概念上讲,编译程序(将其从源代码转换为机器代码)的过程从生成解析树的解析器开始,然后通过一系列树或图变换转换该解析树,每个变换都是在很大程度上是独立的,导致最终的简化树或图形被遍历以产生机器代码。
这个观点虽然理论上很好,但有一个缺点,即如果你试图直接实现它,就需要足够的内存来保存整个树或图的至少两个副本。回到写龙书的时候(当很多这个理论被淘汰出来的时候),计算机内存的测量单位是千字节,64K就是很多。所以编译大型程序可能会很棘手。
使用语法定向转换,您可以围绕解析器识别解析树的顺序组织所有图形转换。解析器不是生成一个完整的解析树,而是构建它的一小部分,然后将这些位提供给编译器的后续传递,最终生成一小段机器代码,然后继续解析过程以构建下一个解析树。由于在任何时候只存在少量的解析树(或后续图形),因此需要更少的内存。由于语法识别器是控制所有这一切的主序列器(决定事件发生的顺序),因此称为语法定向翻译。
由于这是一种减少内存使用的有效方法,人们甚至重新设计了语言以使其更容易实现 - 理想的情况是拥有一个“单一通道”编译器,实际上可以从解析到整个过程机器码生成一次通过。
如今,记忆并不是那么珍贵,因此将一切都强制进入一次传球的压力就会减小。相反,您通常只使用语法直接翻译作为前端,解析语法,进行类型检查和其他语义检查,以及一些简单的转换,这些转换都来自解析器并生成一些内部形式(三种地址代码,树或某种类型的dag) )然后具有独立的优化和后端传递(因此不是语法指导)。即使在这种情况下,您可能会声称这些后来的传递至少部分是语法指导的,因为编译器可能被组织为对大部分输入(例如整个函数或模块)进行操作,在继续执行之前推送所有传递。下一条输入。
像yacc这样的工具是围绕语法定向翻译的思想设计的 - 该工具生成一个语法识别器,可直接运行代码片段(工具用语中的“动作”),因为生成(解析树的片段)被识别,没有创建一个真正的“树”。这些操作可以直接调用编译器中逻辑上稍后传递的内容,然后返回继续解析。驱动所有这一切的命令式主循环是解析器的令牌读取状态机。
答案 1 :(得分:1)
在Dragon Book之前有语法指导的编译器编译器。 META II和TREE META是在20世纪60年代开发的。它们是元语言编译器(元编译器)。它们是自上而下的解析器,它采用一组解析规则,这些规则实际上是返回失败成功的函数。每个规则都是一个逻辑解析表达式。顶部驱动规则定义了源文件内容。例如,编程语言可能由一系列声明组成。因此,最重要的驾驶规则如下:
program = $declarations;
$意味着零或更多。 $ declarations指定正在编译的源由一些声明组成。然后,声明将定义声明的类型。您可能需要外部链接声明,数据声明,函数或过程声明。
declarations = linkage_decl | data_decl | code_decl;
声明的类型各自是一个单独的规则。语法将控制何时发生代码生成。元编译器根据语法规则控制语义处理。在规则中,它首先是自我。后来他们将源转换为树和列表结构,并将它们传递给产生输出的语义规则。例如,可以转换算术赋值语句并将其传递给语义规则。
asign = id '=' expr ';' :ASSIGN!2 arith_gen(*1);
expr = term $(('+':ADD | '-':SUB) term !2);
term = factor $(('*':MPY | '//' :REM | '/':DIV) factor!2);
factor = id | number | '(' expr ')';
据说这些语言很像。但事实并非如此。这些是分析自顶向下语法分析器。然后对arith_gen进行编码以识别嵌入树生成的树结构,':'和'!'运营商。使用的树符号是节点,后面跟着'['和']'括起来的树枝,用','分隔。上面的分析语法定义了表达式的数学层次结构。在加法或减法之前执行乘法,除法和余数。将算术赋值表达式转换为函数列表表达式。语法指导将输入转换为功能表示法。
x = a + (b - c) / d;
生成功能表示法树: ASSIGN [X,ADD并[a,DIV [SUB [B,C],D]]]
这些旧的元编译器与当前的术语并不完全匹配。
请参阅Wiki metacompiler META II和TREE META主题及其更多信息的讨论页面