如何跟踪有关迭代(非递归)函数的嵌套信息

时间:2018-06-21 14:55:01

标签: algorithm parsing recursion data-structures stack

说我有一个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属性或其他内容的方法,但是我不确定其中会有什么。任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:1)

您的堆栈必须在控制流中包含“您所在的位置”。在递归下降解析器中,这实际上与解析中的位置相同,因此您可以以这种方式编写通用的LL解析器。就个人而言,我可能将产品表示为带有令牌列表和处理函数的对象。 (加上对*之类的EBNF运算符的扩展。)然后,状态将是生产,生产中的职位以及已经匹配的值的列表。

但是,当已经存在LR解析器生成器时,很难看到这样做的充分理由,本质上就是使用这种表示形式,并且它们可以处理更多的语法。