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