我有一个简单的语法,最终将解析杨源。当我做时,似乎是一个任意的位置改变模块令牌的IntelliJ ANTLR 4插件可以/不能解析我的输入。
要分析的输入字符串:
module x { }
下面是正确的语法:
grammar Yang ;
yang: module_open module_close;
module_open : MODULE ID BRACKET_OPEN ;
module_close: BRACKET_CLOSE ;
MODULE: 'module' ;
ID: ([A-Za-z][A-Za-z0-9_-]*) ;
BRACKET_OPEN: '{' ;
BRACKET_CLOSE: '}' ;
WS: [ \t\r\n]+ -> skip ;
下面是失败的语法:
grammar Yang ;
yang: module_open module_close;
module_open : MODULE ID BRACKET_OPEN ;
module_close: BRACKET_CLOSE ;
ID: ([A-Za-z][A-Za-z0-9_-]*) ;
MODULE: 'module' ;
BRACKET_OPEN: '{' ;
BRACKET_CLOSE: '}' ;
WS: [ \t\r\n]+ -> skip ;
我所做的只是在ID标记之前/之后剪切粘贴MODULE标记定义,如果MODULE定义在ID定义之后,则剪切粘贴总是失败。
我错过了什么?我在文档中没有看到关于令牌顺序的讨论!
编辑:@BartKiers相关帖子... ANTLR4 lexer rules don't work as expected
2条答案
按热度按时间uinbv5nw1#
如果
module
在ID
之后,则此操作失败,因为文本“module”也是有效的“ID”。如果ID规则先出现,则它具有优先级。此时词法分析器规则的顺序很重要,**此时两个或多个词法分析器规则可以匹配同一输入。**在这种情况下,先出现的规则优先于后面的规则;它有优先权。这里您的优秀测试用例是这种行为在工作中的完美和示范性说明。
在ANTLR 4文档中,曾经有一篇很棒的文章,作者正是Sam Harwell,他完美地解释了这一点,但我再也找不到它了。
xdyibdwo2#
摘自《安特尔之书》(第5.5节):
匹配标识符
在语法伪码中,基本标识符是一个由大写字母和小写字母组成的非空序列。使用我们的新技能,我们知道使用符号
(...)+
来表示序列模式。因为序列的元素可以是大写字母或小写字母,所以我们还知道在子规则中有一个选择操作符。ID : ('a'..'z'|'A'..'Z')+ ; //
匹配1个或多个大写或小写字母这里唯一新的ANTLR符号是范围运算符:
'a'..'z'
表示从a到z的任何字符。这就是ASCII代码的字面范围,从97到122。要使用Unicode码点,我们需要使用'\uXXXX'
文字,其中XXXX
是Unicode字符码点值的十六进制值。作为字符集的缩写,ANTLR支持更熟悉的正则表达式集表示法。
ID : [a-zA-Z]+ ; //
匹配1个或多个大写或小写字母ID等规则有时会与语法中引用的其他词法规则或文字(如
'enum'
)冲突。规则ID还可以匹配
enum
和for
等关键字,这意味着有多个规则可以匹配同一个字符串。考虑ANTLR如何处理组合的词法分析器/解析器语法,如下面这样。ANTLR收集所有字符串文字和词法分析器规则,并将其从解析器规则中分离出来。成为词法规则,并且紧接在解析器规则之后但在显式词法规则之前。ANTLR词法分析器通过优先使用第一个指定的规则来解决词法规则之间的歧义。这意味着ID规则应该定义在所有关键字规则之后,就像它在这里相对于FOR一样。ANTLR将隐式生成的文本词法规则放在显式词法分析器规则之前,因此这些规则总是优先。在这种情况下,
'enum'
的优先级自动高于ID。因为ANTLR将词法规则重新排序为在解析器规则之后出现,所以KeywordTest的以下变体将导致相同的解析器和词法分析器: