理解 ANTLR/EBNF 括号

Understanding ANTLR/EBNF parentheses

ANTLR Reference Guide关于括号的说法如下:

另一方面,我有以下语法:

fragment LOWERCASE  : [a-z] ;
fragment UPPERCASE  : [A-Z] ;
fragment DIGIT : [0-9] ;

WORD                : (LOWERCASE | UPPERCASE | DIGIT )+;

如果我 运行 字符串 "asd90",它将作为一个 WORD 进行匹配,如下所示:

>java org.antlr.v4.gui.TestRig Myg myg -tokens
asd90
[@0,0:4='asd90',<WORD>,1:0]
[@1,5:6='\r\n',<NEWLINE>,1:5]
[@2,7:6='<EOF>',<EOF>,2:0]

这让我感到困惑,因为我希望 "asd90" 不匹配,因为在我的语法中,LOWERCASE 和 DIGIT 是 2 个不同的子规则,所以我希望它不匹配,因为 "WORD"。

换句话说,这就像说 "intvoid" 可以作为 return 类型工作,而显然它不会。

您似乎忽略了循环运算符。词法分析器规则 WORD 可以匹配任何 LOWERCASEUPPERCASEDIGITsequence,这意味着匹配是完全有效的asd90 通过使用该循环的 5 次运行(匹配小写规则 3 次,数字规则匹配两次)。

书上的例子没有循环,所以只能匹配一次,不是一个类型就是void

更多细节:片段规则有点私人规则(事实上,在旧的 ANLTR 版本中有关键字 private,后来被重命名为 fragment)。但是,它们在解析器中不可用,也不会出现在词法分析器规则列表中。您的 WORD 规则像这样存储在 ATN 中:

这表明 3 个片段规则的调用方式与任何其他规则一样。

但是还有另一个区别(这就是 Pavel 提到的)。在歧义解析方面,片段词法分析器规则的处理方式略有不同。

通常情况下是这样的:匹配最长输入的规则获胜。如果两个规则匹配相同的输入,那么在语法中首先出现的规则将获胜。这适用于解析器和词法分析器规则。但是,不适用于片段规则。即使与另一个(非片段)规则匹配相同输入的片段规则首先出现在语法中,非片段规则仍然获胜。

你可以通过稍微改变一下语法来证明这一点:

grammar Example;

start: WORD;

WS : [ \t\r\n]+ -> skip;

fragment LOWERCASE  : [a-z]+ ;
fragment UPPERCASE  : [A-Z]+ ;
fragment DIGIT : [0-9]+ ;
WORD: (LOWERCASE | UPPERCASE | DIGIT )+;

这会为您提供这些标记:

通过输入 abc,您仍然会得到 WORD 作为匹配标记,即使 LOWERCASE 在语法中排在第一位。现在删除 fragment 关键字,您将看到 LOWERCASE 被匹配:

Parser error (1, 1): mismatched input 'abc' expecting WORD

Tokens:
[@0,0:2='abc',<2>,1:0]
[@1,4:3='<EOF>',<-1>,2:0]

Parse Tree:
start (
  <Error>"abc"
)

这会导致语法错误,因为需要 WORD。令牌列表变为:

但是,即使您删除了所有片段关键字,您仍然会为 abc90 这样的输入得到 WORD,因为该规则比它之前的任何单个词法分析器规则匹配得更多。