如何在python lark解析器中平衡规则和终端?

时间:2018-02-16 10:06:43

标签: python parsing ebnf lark-parser

我使用百灵鸟,一个很棒的python parsing library

它提供了一个Earley和LALR(1)解析器,并通过custom EBNF format定义。 (EBNF代表Extended Backus–Naur form)。

小写定义是规则,大写定义是终端。 Lark还为大写定义提供权重,以优先匹配。

我试图定义一个语法,但我仍然坚持一种似乎无法平衡的行为。

我有一些带有未命名文字的规则(双引号之间的字符串或字符):

directives: directive+
directive: "@" NAME arguments ?
directive_definition: description? "directive" "@" NAME arguments? "on" directive_locations
directive_locations: "SCALAR" | "OBJECT" | "ENUM"

arguments: "(" argument+ ")"
argument: NAME ":" value

union_type_definition: description? "union" NAME directives? union_member_types?

union_member_types: "=" NAME ("|" NAME)*

description: STRING | LONG_STRING    

STRING: /("(?!"").*?(?<!\\)(\\\\)*?"|'(?!'').*?(?<!\\)(\\\\)*?')/i
LONG_STRING: /(""".*?(?<!\\)(\\\\)*?"""|'''.*?(?<!\\)(\\\\)*?''')/is
NAME.2: /[_A-Za-z][_0-9A-Za-z]*/

适用于99%的用例。但是,如果用我的解析语言,我使用的是名为 directive的{​​{1}},那么一切都会中断:

directive

此处,union Foo @something(test: 42) = Bar | Baz # This works union Foo @directive(test: 42) = Bar | Baz # This fails 字符串与directive规则中的未命名文字匹配,且该字符串应与directive_definition终端匹配。

如何平衡/调整此值以使LALR(1)解析器不存在歧义?

1 个答案:

答案 0 :(得分:5)

Lark的作者。

这种误解的发生是因为&#34;指令&#34;可以是两个不同的令牌:&#34;指令&#34;字符串或NAME。默认情况下,Lark的LALR词法分析器总是选择更具体的词,即字符串。

那么我们怎样才能让词法分析器知道@directive是一个名字,而不仅仅是两个常量字符串?

解决方案1 ​​ - 使用上下文词汇表

在这种情况下可能有所帮助(如果没有完整的语法,很难确定),就是使用上下文词法分析器,而不是标准的LALR(1)词法分析器。

上下文词法分析器可以在某种程度上与解析器进行通信,以确定哪个终端在每个点上更有意义。这是Lark独有的算法,您可以像这样使用它:

parser = Lark(grammar, parser="lalr", lexer="contextual")

(这个词法分析器可以执行标准词法分析器可以执行的任何操作,因此在将来的版本中它可能会成为默认词法分析器。)

解决方案2 - 在终端前缀

如果上下文词法分析器没有解决你的碰撞,那么更多的经典&#34;解决这种情况的方法是定义一个指令令牌,如:

DIRECTIVE: "@" NAME

与您的指令规则不同,这对词法分析器没有任何歧义。指令和&#34;指令&#34;之间有明显的区别。字符串(或NAME终端)。

如果所有其他方法都失败了,你可以随时使用Earley解析器,它以性能为代价,适用于你提供的任何语法,无论可能有多少次碰撞。

希望这有帮助!

编辑:我想指出上下文词法分析器现在是LALR的默认值,所以它足以调用:

parser = Lark(grammar, parser="lalr")
相关问题