我们正在编译器设计类中进行自顶向下解析。示例都是类似Java的语言。我决定尝试一种简单的函数式语言使它变得有趣,所以我选择了PCF(see e.g. here)。但我似乎无法将其纳入LL(1)语法。我认为问题是功能应用(即两个表达式的并置)。我没有得到明确的答案,如何确定它是否仅仅是我缺乏技能,或者这是一种没有LL(1)语法或LL(k)的语言。有人可以澄清我是否需要更聪明,或者是否只是不存在这样的语法?
基本上,我的PCF版本类似于下面的草图(大写是非终端," //"开始评论)。我说"类似"因为我并没有紧紧抓住这个并且我写了很多变种 - 只想要一个合理的PCF变体。
请注意,我已经尝试了左因子分解并考虑了中间非终端的优先级。
Exp -> Const // integer and boolean literals, normal ops e.g. +
| if Exp then Exp else Exp
| lambda identifier dot Exp //lambda (function) abstraction
| Exp Exp // function application
| fix Exp // fixpoint operator (recursion)
答案 0 :(得分:1)
问题是你的语法是左递归的,如LL(k)所描述的那样,自上而下的解析器效果不佳。
具体来说,尝试解析Exp
会导致尝试首先解析函数应用程序Exp Exp
的第一部分......对于自上而下的解析器,它会进行无限循环。 / p>
您的案例中可能的解决方案是以正确的递归方式实现任意长度的函数应用程序:
AppExp -> Exp | Exp AppExp
Exp -> Const | (AppExp) | ...
请注意,此构造消除了语法歧义。不幸的是,它为你的问题解决了错误的方向 - 并没有很好的方法来解决它;左关联版本:
AppExp -> Exp | AppExp Exp
将像原始一样左递归。
在自上而下的解析器范围内解决这个问题的方法是接受右关联语法,将AppExp
视为列表,并在解析后将其反转,以便您的抽象语法树具有您想要的关联性:
application expression: f a b c d
|
| LL(1) parse
v
right-associative ---> left-associative
@ list @
/ \ reversal / \
f @ @ d
/ \ / \
a @ @ c
/ \ / \
b @ @ b
/ \ / \
c . . a
| |
d f
像Parsec这样的组合器解析器库通常具有方便的预打包功能,可以为您完成此任务。
在语言理论术语中,这演示了语法接受的语言与基于该语法的解析器生成的解析之间的区别。