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

标签 python parsing ebnf lark-parser

我正在使用 lark,一个优秀的 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 ,它被称为 directive,一切都会崩溃:

union Foo @something(test: 42) = Bar | Baz   # This works
union Foo @directive(test: 42) = Bar | Baz   # This fails

此处,directive 字符串在 directive_definition 规则中应与 NAME.2 终端匹配时与未命名文字相匹配。

我怎样才能平衡/调整它,以便 LALR(1) 解析器不会出现歧义?

最佳答案

这里是 Lark 的作者。

发生这种误解是因为“指令”可以是两个不同的标记:“指令”字符串或名称。默认情况下,Lark 的 LALR 词法分析器总是选择更具体的一个,即字符串。

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

解决方案 1 - 使用上下文词法分析器

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

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

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

(这个词法分析器可以做标准词法分析器可以做的任何事情,甚至更多,所以在未来的版本中它可能会成为默认的词法分析器。)

解决方案 2 - 为终端添加前缀

如果上下文词法分析器不能解决您的冲突,针对这种情况的更“经典”解决方案是定义指令标记,例如:

DIRECTIVE: "@" NAME

与您的指令规则不同,这不会给词法分析器留下歧义。指令和“指令”字符串(或 NAME 终端)之间有明显的区别。

如果所有其他方法都失败了,您始终可以使用 Earley 解析器,它以性能为代价,可以处理您提供的任何语法,无论可能存在多少冲突。

希望这对您有所帮助!

编辑:我只想指出,上下文词法分析器现在是 LALR 的默认设置,因此调用就足够了:

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

关于python - 如何在 python lark 解析器中平衡规则和终端?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48824314/

相关文章:

linux - linux下解析文件

java - 这种 EBNF 方法是否正确?

python - 如何在Python中仅丢弃html标签并提取关联文本

c# - 正则表达式在斜线之间得到一个词

parsing - 如何处理 EBNF 语法中不同标记中的重叠字符组?

python - 字典和字符串

python - 在 matplotlib 中同时更改线宽和颜色

需要 Python 2.6 版,在注册表中找不到

python - 如何用 Python 制作漂亮的 OSD

python - 使用 BeautifulSoup 提取值列表