如果您想为一种玩具语言构建一个主要递归下降解析器,其唯一的左递归或多或少是标准表达式语法,那么您应该考虑使用 Pratt 解析器 ( Java ) ( Javascript )。或者(等效地,事实证明),您可以使用运算符优先级解析器;该算法在 Dragon 书中得到了很好的描述,并且有很多使用调车场算法的可用示例(但请注意;许多热情地发现该算法并立即将其写在博客上的人与可靠的权威相去甚远。)
松散解析的注意事项:
如果要解析表达式语法,并且不关心运算符优先级(例如,如果您只需要对代码进行语法着色),则可以轻松地重新构建表达式语法以避免左递归。
起点是这个,*
用于 Kleene 星,?
用于可选和(
)
分组:
expression: term (INFIX term)*
term: PREFIX* operand postfix*
operand: ID
| CONSTANT
| '(' expression ')'
;
postfix: POSTFIX
| '[' expression ']'
| '(' ( expression (',' expression)* )? ')'
;
递归下降解析器可以轻松处理上面的*
and?
运算符,并且没有左递归,这样就足够了。
请注意,C 具有强制转换表达式的笨拙,除非您知道第一个带括号的表达式包含类型而不是表达式,否则它在语法上与函数调用无法区分。但是,出于松散解析的目的,可以使用上述语法将它们当作函数调用来解析。
C++ 使用尖括号作为运算符和模板参数更难处理。我见过的许多语法着色器完全忽略了模板参数大小写;让它们正确是一个挑战,但可能是值得的。
编辑评论:
如果您愿意,请忽略这一点,但我个人认为,学习使用自下而上的 LR 解析器,尤其是 GLR 解析器,比试图将一种语言硬塞进一个受限的解析算法,尤其是其中的细节语法的一部分被隐藏为代码路径。但是话虽如此,如果只是为了看看野牛解析器可以变得更加优雅和简单,同时做一个野牛和手动生成的解析器可能是有价值的。