LL(1) 文法是否需要通过有条件或无条件跳转的翻译来扩充?
Do LL(1) grammars need to be augmented via translation either on conditional or not conditional jumps?
考虑下面的语法
S -> for id:= E to E by E do S end
S -> other
E -> num
假设上面的文法是LL(1)
也是SLR(1)
.
在自下而上解析器中作为Pr.Alfred.Vho在编译器原理和技术与工具一书中声明我们这样做在 一次性翻译 中使用额外的产生式规则 (M -> e) 扩充语法,例如翻译条件跳转和非条件跳转
S -> for id:= E M to E by E do S M end M
S -> other
E -> num
M -> e {Semantic Action()}
如您所见,我们用额外的产生式规则 (M) 扩充了语法,因此我们知道 trueList
和 falseList
跳转。
所以这是我的主要关注点和问题,我们是否需要扩充像 LL(1) 这样的自上而下语法,以便翻译像 SLR(1) 这样的条件跳转而不是条件跳转?
正如您在问题中指出的那样,该教科书的作者专门谈论一次性翻译。添加标记非终结符是 LR 文法中常用的技术,用于在右侧处理的中间执行操作;您引用(或写)的 for
语句就是这种技术的一个例子。 (但是请注意,右侧的最后一个 M
是不必要的;它可能只是 for
语句本身的缩减操作的一部分。)
这根本不是自底向上解析的要求。不如说是一次翻译的需求。在编写 Dragon book 时,计算机有限的计算能力迫使编译器编写者采取策略来减少编译器通过的次数,但现在这种技术是不必要的,而且通常是不受欢迎的。在解析期间构建抽象语法树 (AST) 并进行所有语义分析(包括代码生成)更简单、更快和更通用,因为 post-解析遍历 AST。
如果您构建 AST,则不再需要为了将动作潜入正确的顺序而玩弄解析算法。相反,您可以按照对您正在进行的特定分析而言方便的任何顺序(或多个顺序)遍历树。
对于自上而下的解析,基本上可以做出相同的评论;如果你想将动作排序到解析中,你需要找到一种方法将动作插入到产生式中。至少,如果您使用基于 table 的 LL 解析器生成器,则需要这样做。然而,LL 文法用于构建递归下降解析器是很常见的。由于递归下降解析器是用图灵完备编程语言编写的完全通用的计算机程序,因此您不需要任何特殊的东西来在程序的某个点插入动作。您只需将操作放在解析器中的适当位置即可。
乍一看这似乎是一种简化,但它会迅速将您的程序变成一团乱麻,其中不同的动作都在一点一点地同时计算,这使得所有计算都难以遵循。经验告诉我们,有时痛苦地告诉我们,最好将程序组织成单独的松耦合任务,每个任务都在自己的控制流中执行。因此,您很可能最终也将自上而下的解析器也变成了语法树生成器,而不是不断地发现自己说:“是的,我想实现该功能,但我的编译器的结构使它变得困难。”
考虑下面的语法
S -> for id:= E to E by E do S end
S -> other
E -> num
假设上面的文法是LL(1)
也是SLR(1)
.
在自下而上解析器中作为Pr.Alfred.Vho在编译器原理和技术与工具一书中声明我们这样做在 一次性翻译 中使用额外的产生式规则 (M -> e) 扩充语法,例如翻译条件跳转和非条件跳转
S -> for id:= E M to E by E do S M end M
S -> other
E -> num
M -> e {Semantic Action()}
如您所见,我们用额外的产生式规则 (M) 扩充了语法,因此我们知道 trueList
和 falseList
跳转。
所以这是我的主要关注点和问题,我们是否需要扩充像 LL(1) 这样的自上而下语法,以便翻译像 SLR(1) 这样的条件跳转而不是条件跳转?
正如您在问题中指出的那样,该教科书的作者专门谈论一次性翻译。添加标记非终结符是 LR 文法中常用的技术,用于在右侧处理的中间执行操作;您引用(或写)的 for
语句就是这种技术的一个例子。 (但是请注意,右侧的最后一个 M
是不必要的;它可能只是 for
语句本身的缩减操作的一部分。)
这根本不是自底向上解析的要求。不如说是一次翻译的需求。在编写 Dragon book 时,计算机有限的计算能力迫使编译器编写者采取策略来减少编译器通过的次数,但现在这种技术是不必要的,而且通常是不受欢迎的。在解析期间构建抽象语法树 (AST) 并进行所有语义分析(包括代码生成)更简单、更快和更通用,因为 post-解析遍历 AST。
如果您构建 AST,则不再需要为了将动作潜入正确的顺序而玩弄解析算法。相反,您可以按照对您正在进行的特定分析而言方便的任何顺序(或多个顺序)遍历树。
对于自上而下的解析,基本上可以做出相同的评论;如果你想将动作排序到解析中,你需要找到一种方法将动作插入到产生式中。至少,如果您使用基于 table 的 LL 解析器生成器,则需要这样做。然而,LL 文法用于构建递归下降解析器是很常见的。由于递归下降解析器是用图灵完备编程语言编写的完全通用的计算机程序,因此您不需要任何特殊的东西来在程序的某个点插入动作。您只需将操作放在解析器中的适当位置即可。
乍一看这似乎是一种简化,但它会迅速将您的程序变成一团乱麻,其中不同的动作都在一点一点地同时计算,这使得所有计算都难以遵循。经验告诉我们,有时痛苦地告诉我们,最好将程序组织成单独的松耦合任务,每个任务都在自己的控制流中执行。因此,您很可能最终也将自上而下的解析器也变成了语法树生成器,而不是不断地发现自己说:“是的,我想实现该功能,但我的编译器的结构使它变得困难。”