ANTLR词法分析器如何消除其规则(或为什么我的分析器产生“不匹配的input”错误)?
注意:这是一个自我回答的问题 ,旨在提供有关ANTLR用户最常犯的错误之一的参考。
当我testing这个非常简单的语法时:
grammar KeyValues; keyValueList: keyValue*; keyValue: key=IDENTIFIER '=' value=INTEGER ';'; IDENTIFIER: [A-Za-z0-9]+; INTEGER: [0-9]+; WS: [ \t\r\n]+ -> skip; 通过以下input:
 foo = 42; 
我结束了以下运行时错误:
第1行:6个不匹配的input“42”,期望INTEGER
第1行:8个不匹配的input';' 期待'='
 为什么在这种情况下,ANTLR不认为42是INTEGER ? 
 它应该匹配模式[0-9]+就好了。 
 如果我将INTEGER和IDENTIFIER的定义顺序颠倒INTEGER ,似乎可行,但为什么顺序是重要的呢? 
 在ANTLR中,词法分析器与parsing器是分离的,这就意味着它会根据词法分析器的语法规则将文本分成types化的标记,parsing器对这个过程没有影响(不能说“现在就给我一个INTEGER ” )。 它自己产生一个令牌stream 。 此外,parsing器不关心标记文本,它只关心标记types以匹配其规则。 
当几个词法分析规则可以匹配相同的input文本时,这可能很容易成为一个问题。 在这种情况下,将根据这些优先规则select令牌types:
- 首先,select匹配最长input子string的词法分析规则
-  如果最长匹配的子string等于隐式定义的标记(如'='),则使用隐式规则作为标记types
- 如果几个词法规则匹配相同的input,请根据定义顺序select第一个
为了有效地使用ANTLR,这些规则非常重要。
 在这个问题的例子中,parsing器期望看到下面的标记stream匹配keyValueparsing器规则: IDENTIFIER '=' INTEGER ';'  where '='和';' 是隐式标记types。 
 由于42可以匹配INTEGER和IDENTIFIER ,并且IDENTIFIER被首先定义,parsing器将接收以下input: IDENTIFIER '=' IDENTIFIER ';' 它将无法匹配keyValue规则。 请记住,parsing器不能与词法分析器通信,只能从它接收数据,因此不能说“尝试匹配下一个INTEGER ” 。 
build议尽量减less词法分析规则的重叠,以限制这种影响。 在上面的例子中,我们有几个选项:
-  将IDENTIFIER重新定义为[A-Za-z] [A-Za-z0-9]*(要求以字母开头)。 这完全避免了这个问题,但是防止了以数字开始的标识符名称被定义,所以它改变了语法的意图。
-  对INTEGER和IDENTIFIER重新sorting。 这在大多数情况下解决了这个问题,但是阻止了完全数字标识符的定义,因此它也以一种微妙而不明显的方式改变了语法的意图。
-  词法分析器规则重叠时,使parsing器接受两种标记types: 
 首先,交换INTEGER和IDENTIFIER为了优先INTEGER。 然后,定义一个parsing器规则id: IDENTIFIER | INTEGER;id: IDENTIFIER | INTEGER;然后在其他parsing器规则中使用该规则而不是IDENTIFIER,这会将keyValue更改为key=id '=' value=INTEGER ';'。
下面是第二个词法分析器行为的例子:
以下组合语法:
 grammar LexerPriorityRulesExample; // Parser rules randomParserRule: 'foo'; // Implicitly declared token type // Lexer rules BAR: 'bar'; IDENTIFIER: [A-Za-z]+; BAZ: 'baz'; WS: [ \t\r\n]+ -> skip; 
鉴于以下input:
 aaa foo bar baz barz 
将从词法分析器产生以下令牌序列:
  IDENTIFIER 'foo' BAR IDENTIFIER IDENTIFIER EOF 
- 
aaa是IDENTIFIERtypes只有 IDENTIFIER规则可以匹配这个标记,没有歧义。
- 
foo是'foo'types'foo'parsing器规则 randomParserRule引入了隐式的'foo'标记types,它比IDENTIFIER规则优先。
- 
bar是types的BAR该文本与在 IDENTIFIER规则之前定义的BAR规则匹配,因此具有优先权。
- 
baz是一个types的IDENTIFIER该文本与 BAZ规则相匹配,但也与IDENTIFIER规则相匹配。 后者是在BAR之前定义的。鉴于语法, BAZ将永远无法匹配,因为IDENTIFIER规则已经涵盖了BAZ可以匹配的所有内容。
- 
barz是IDENTIFIERtypes的BAR规则可以匹配该string(bar)的前3个字符,但是IDENTIFIER规则将匹配4个字符。 由于IDENTIFIER匹配较长的子string,因此selectBAR。
- 
EOF( 文件结束 )是一种隐式定义的标记types,始终出现在input的末尾。
作为一个经验法则,应该在更通用的规则之前定义特定的规则。 如果一个规则只能匹配一个已经被之前定义的规则覆盖的input,那么它将永远不会被使用。
 隐式定义的规则(如'foo'就好像在所有其他词法分析规则之前定义的一样 。 由于它们增加了复杂性,build议完全避免它们,而是声明明确的词法分析规则。 只要将一个令牌列表放在一个地方,而不是将它们分散在文法中,这种方法就是一个很有吸引力的优势。