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不认为42INTEGER
它应该匹配模式[0-9]+就好了。

如果我将INTEGERIDENTIFIER的定义顺序颠倒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可以匹配INTEGERIDENTIFIER ,并且IDENTIFIER被首先定义,parsing器将接收以下input: IDENTIFIER '=' IDENTIFIER ';' 它将无法匹配keyValue规则。 请记住,parsing器不能与词法分析器通信,只能从它接收数据,因此不能说“尝试匹配下一个INTEGER

build议尽量减less词法分析规则的重叠,以限制这种影响。 在上面的例子中,我们有几个选项:

  • IDENTIFIER重新定义为[A-Za-z] [A-Za-z0-9]* (要求以字母开头)。 这完全避免了这个问题,但是防止了以数字开始的标识符名称被定义,所以它改变了语法的意图。
  • INTEGERIDENTIFIER重新sorting。 这在大多数情况下解决了这个问题,但是阻止了完全数字标识符的定义,因此它也以一种微妙而不明显的方式改变了语法的意图。
  • 词法分析器规则重叠时,使parsing器接受两种标记types:
    首先,交换INTEGERIDENTIFIER为了优先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

  • aaaIDENTIFIERtypes

    只有IDENTIFIER规则可以匹配这个标记,没有歧义。

  • foo'foo'types'foo'

    parsing器规则randomParserRule引入了隐式的'foo'标记types,它比IDENTIFIER规则优先。

  • bar是types的BAR

    该文本与在IDENTIFIER规则之前定义的BAR规则匹配,因此具有优先权。

  • baz是一个types的IDENTIFIER

    该文本与BAZ规则相匹配,但也与IDENTIFIER规则相匹配。 后者是 BAR 之前定义的。

    鉴于语法, BAZ将永远无法匹配,因为IDENTIFIER规则已经涵盖了BAZ可以匹配的所有内容。

  • barzIDENTIFIERtypes的

    BAR规则可以匹配该string( bar )的前3个字符,但是IDENTIFIER规则将匹配4个字符。 由于IDENTIFIER匹配较长的子string,因此selectBAR

  • EOF文件结束 )是一种隐式定义的标记types,始终出现在input的末尾。

作为一个经验法则,应该更通用的规则之前定义特定的规则。 如果一个规则只能匹配一个已经被之前定义的规则覆盖的input,那么它将永远不会被使用。

隐式定义的规则(如'foo'就好像所有其他词法分析规则之前定义的一样 。 由于它们增加了复杂性,build议完全避免它们,而是声明明确的词法分析规则。 只要将一个令牌列表放在一个地方,而不是将它们分散在文法中,这种方法就是一个很有吸引力的优势。