说我有一个recursive descent parser,它定义了一堆嵌套规则。
Expr ← Sum
Sum ← Product (('+' / '-') Product)*
Product ← Value (('*' / '/') Value)*
Value ← [0-9]+ / '(' Expr ')'
说对了●在此过程中第二个Value
上:
Expr ← Sum
Sum ← Product (('+' / '-') Product)*
Product ← Value (('*' / '/') ●)*
Value ← [0-9]+ / '(' Expr ')'
这意味着我位于嵌套级别的某个地方,可以这样说:
Expr
Sum
|Product
+
Product
|Product
-
Product
|Value
*
Value
|Value
*
●
使用递归下降进行解析时,它是递归的,因此当Value
返回时,我们返回到“序列” *
解析节点,然后返回到Product
节点,该节点返回到产品序列节点等。因此,很容易构建解析树。
但是,假设您要使用iterative stack进行此操作。问题是,如何跟踪嵌套信息,以便最终可以在代码中说:
function handleValue(state, string) {
// ...
}
function handleValueSequence(state, string) {
if (state.startedValueSequenceEarlier) {
wrapItUp(new ValueSequence(state.values))
}
}
function handleProduct(state, string) {
// ...
}
function handleProductSequence(state, string) {
if (state.startedProductSequenceEarlier) {
wrapItUp(new ProductSequence(state.products))
}
}
棘手的部分是,它可以任意嵌套,因此您可能需要:
Product
Value
Product
Value
Product
...
因此,如果您的函数handleProductSequence
除了该函数的参数之外没有其他上下文,则无法确定应该如何“ wrapItUp”并最终创建该ProductSequence
对象。在我添加的state
对象中,我试图考虑添加state.stack
属性或其他内容的方法,但是我不确定其中会有什么。任何帮助将不胜感激。
答案 0 :(得分:1)
您的堆栈必须在控制流中包含“您所在的位置”。在递归下降解析器中,这实际上与解析中的位置相同,因此您可以以这种方式编写通用的LL解析器。就个人而言,我可能将产品表示为带有令牌列表和处理函数的对象。 (加上对*
之类的EBNF运算符的扩展。)然后,状态将是生产,生产中的职位以及已经匹配的值的列表。
但是,当已经存在LR解析器生成器时,很难看到这样做的充分理由,本质上就是使用这种表示形式,并且它们可以处理更多的语法。