我用的是云雀,一个极好的python parsing library。在
它提供了Earley和LALR(1)解析器,并通过custom ^{
小写定义是规则,大写定义是终端。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
,那么一切都会中断:
这里,directive
字符串与directive_definition
规则中的未命名文本匹配,而它应该与NAME.2
终端匹配。在
如何平衡/调整这一点,使LALR(1)解析器不存在歧义?在
《云雀》作者。在
之所以会出现这种误解,是因为“directive”可以是两个不同的标记:“directive”字符串或名称。默认情况下,Lark的LALR lexer总是选择更具体的一个,即字符串。在
那么我们如何让lexer知道
@directive
是一个名称,而不仅仅是两个常量字符串?在解决方案1-使用上下文词法器
在这种情况下(如果没有完整的语法,很难确定)使用上下文词法分析器,而不是标准的LALR(1)词法分析器。在
上下文词法分析器可以在某种程度上与解析器通信,以确定哪个终端在每一点上更有意义。这是云雀独有的算法,您可以这样使用它:
(这个lexer可以做标准lexer可以做的任何事情,甚至更多,因此在将来的版本中,它可能成为默认的lexer。)
解决方案2-为终端添加前缀
如果上下文词法器不能解决您的冲突,一个更“经典”的解决方案是定义一个指令标记,类似于:
^{pr2}$与您的指令规则不同,这不会给lexer留下歧义。指令和“指令”字符串(或名称终端)之间有明显的区别。在
如果所有其他方法都失败了,您可以始终使用Earley解析器,它将以性能为代价来处理您提供的任何语法,而不管有多少冲突。在
希望这有帮助!在
编辑:我只想指出上下文lexer是LALR的默认设置,因此可以调用:
相关问题 更多 >
编程相关推荐