[英]Hand-written top-down parser that preserves grammar associativity/precedence?
假设我有一个简单的语法,如:
X -> number
T -> X
T -> T + X
因此,例如3 + 4 + 5
将解析为:
+
/ \
+ 5
/ \
3 4
这具有+
“内置”语法的左右关联性。
它很简单LR(1),但是假设我想做一个手写的自上而下的解析。
我不能这样做,因为它是递归的,所以让我们留下它:
X -> number
T -> X T'
T' -> + X T'
T' -> e // empty
如果我现在为它编写一个解析器(伪代码):
parse_X:
if lookahead is number
return pop_lookahead
parse_T:
return (parse_X, parse_T')
parse_T':
if lookahead is +
pop_lookahead
return (parse_X, parse_T')
else
return ();
然后当我在3 + 4 + 5
的输入上调用parse_T
,我得到一条跟踪:
parse_T
(parse_X, parse_T')
(3, parse_T')
(3, (parse_X, parse_T'))
(3, (4, parse_T'))
(3, (4, (parse_X, parse_T')))
(3, (4, (5, ())))
看看解析是如何“向后”的。 从这样的解析中“天真地”构造的树看起来像:
+
/ \
3 +
/ \
4 5
哪个有错误的关联性。
任何人都可以清除这个吗? 一般来说,如何编写一个手写的自上而下的解析器来保留语法中内置的关联性?
一种策略是通过X
上的迭代替换T
上的递归(一般来说,通过迭代在下一个最高优先级运算符上替换运算符上的递归)。 在这种情况下,它有助于使用EBNF样式表示法
T -> X {+ X}
因为那时所需的迭代变得明显:
parse_T:
val = parse_X
while lookahead is +
pop_lookahead
val = PLUS(val, parse_X)
return val
其中PLUS()
表示您为评估加法表达式所做的任何操作,例如构造树节点。
如果将此应用于所有运算符,则最终会使用与EXPRESSION
相对应的一个函数,该函数仅在处理时进行递归
PRIMARY -> '(' EXPRESSION ')'
这种方法导致了一个相当快速的表达式解析器; 使用朴素递归下降来解析表达式的一个常见异议是,如果你有几个级别的运算符优先级,你很容易需要20个左右的嵌套函数调用来解析每个PRIMARY
,这可能会相当慢。 使用迭代方法,它通常每个PRIMARY
只需要一个函数调用。 如果你有一些右关联运算符(例如指数运算),你可以使用递归方法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.