众所周知,自上而下的解析范例不能处理左递归。必须重构语法以摆脱左递归或必须使用其他一些范例。我一直在研究一个解析器组合库,因为我用一种允许全局副作用的语言来做这件事让我感到震惊,我可以使用一些全局存储来跟踪哪些规则被触发,哪些规则没有。这种在某些条件下保护的方案让我可以处理非常简单的左递归情况,代价是组合器上的一些额外注释。这是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
的情况。如果我们遇到循环状态并且在我们取得进展后立即取消防守,则规则将失败。
我想知道的是,这个想法是否已被探索和分析。如果这是一个死胡同,我宁愿不要走下这个兔子洞并尝试解决问题。
答案 0 :(得分:2)
看看GLL算法 (例如https://github.com/djspiewak/gll-combinators)。 他们可以有效地处理模糊和左递归语法。
他们不直接调用子解析器的解析器函数,而是保留(解析器,位置)tupels(称为Trampoline)的'todo'列表。 这样就避免了无限循环(递归到自身)(没有添加两次tupel)。