解析器组合器和左递归

时间:2015-03-20 02:12:31

标签: parsing parser-combinators recursive-descent

众所周知,自上而下的解析范例不能处理左递归。必须重构语法以摆脱左递归或必须使用其他一些范例。我一直在研究一个解析器组合库,因为我用一种允许全局副作用的语言来做这件事让我感到震惊,我可以使用一些全局存储来跟踪哪些规则被触发,哪些规则没有。这种在某些条件下保护的方案让我可以处理非常简单的左递归情况,代价是组合器上的一些额外注释。这是TypeScript中的示例语法

var expression = Parser.delay(_ => TestGrammar.expression);
var in_plus = false;

class TestGrammar {

  static terminal = Parser.m(x => 'a' === x);

  static op = Parser.m(x => '+' === x);

  static plus = expression.on_success(_ => {
    in_plus = false; 
  }).then(TestGrammar.op).then(expression).on_enter(_ => {
    in_plus = true; 
  }).guard(_ => !in_plus);

  static expression = TestGrammar.plus.or(TestGrammar.terminal);

}

console.log(TestGrammar.expression.parse_input('a+a+a+a'));

这个想法非常简单。在我们可能陷入循环的情况下,规则会使用警卫进行修改,例如上例中plus的情况。如果我们遇到循环状态并且在我们取得进展后立即取消防守,则规则将失败。

我想知道的是,这个想法是否已被探索和分析。如果这是一个死胡同,我宁愿不要走下这个兔子洞并尝试解决问题。

1 个答案:

答案 0 :(得分:2)

看看GLL算法 (例如https://github.com/djspiewak/gll-combinators)。 他们可以有效地处理模糊和左递归语法。

他们不直接调用子解析器的解析器函数,而是保留(解析器,位置)tupels(称为Trampoline)的'todo'列表。 这样就避免了无限循环(递归到自身)(没有添加两次tupel)。