在ANTLR中,词法分析器与解析器是隔离的,这意味着它将把文本分割成typed根据词法分析器语法规则进行标记,解析器对此过程没有影响(不能说“给我一个INTEGER
now"例如)。它产生一个令牌流通过它自己。此外,解析器不关心标记文本,它只关心标记类型以匹配其规则。
当多个词法分析器规则可以匹配相同的输入文本时,这很容易成为问题。在这种情况下,将根据这些选择令牌类型优先规则:
- 首先,选择与longest输入子串
- 如果最长匹配的子字符串等于隐式定义的标记(例如
'='
),使用隐式规则作为token类型
- 如果多个词法分析器规则匹配相同的输入,请选择first一、基于定义顺序
为了有效地使用 ANTLR,记住这些规则非常重要。
在问题的示例中,解析器期望看到以下标记流来匹配keyValue
解析器规则:IDENTIFIER
'='
INTEGER
';'
where '='
and ';'
是隐式标记类型。
Since 42
可以匹配both INTEGER
and IDENTIFIER
, and IDENTIFIER
首先定义,解析器将收到以下输入:IDENTIFIER
'='
IDENTIFIER
';'
它将无法匹配到keyValue
规则。请记住,解析器cannot交流to词法分析器,它只能从中接收数据,因此它不能说“尝试匹配INTEGER
next".
建议尽量减少词法分析器规则重叠,以限制此效果的影响。在上面的例子中,我们有几种选择:
- 重新定义
IDENTIFIER
as [A-Za-z] [A-Za-z0-9]*
(要求以字母开头)。这完全避免了这个问题,但阻止了定义以数字开头的标识符名称,因此它改变了语法的意图。
- Reorder
INTEGER
and IDENTIFIER
。这解决了大多数情况下的问题,但阻止了定义完全数字的标识符,因此它也以微妙的、不那么明显的方式改变了语法的意图。
- 当词法分析器规则重叠时,使解析器接受两种标记类型:
首先,交换INTEGER
and IDENTIFIER
为了优先考虑INTEGER
。然后,定义解析器规则id: IDENTIFIER | INTEGER;
然后使用该规则而不是IDENTIFIER
在其他解析器规则中,这会改变keyValue
to 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;
给定以下输入:
aaa foo bar baz barz
将从词法分析器生成以下标记序列:
IDENTIFIER
'foo'
BAR
IDENTIFIER
IDENTIFIER
EOF
-
aaa
属于类型IDENTIFIER
只有IDENTIFIER
规则可以匹配这个标记,没有歧义。
-
foo
属于类型'foo'
解析器规则randomParserRule
引入了隐含的'foo'
令牌类型,优先于IDENTIFIER
rule.
-
bar
属于类型BAR
这段文字匹配BAR
规则,其定义为before the IDENTIFIER
规则,因此具有优先权。
-
baz
属于类型IDENTIFIER
这段文字匹配BAZ
规则,但它也符合IDENTIFIER
规则。后者是根据定义选择的before BAR
.
考虑到语法,BAZ
永远无法匹配,因为IDENTIFIER
规则已经涵盖了一切BAZ
可以匹配。
-
barz
属于类型IDENTIFIER
The BAR
规则可以匹配该字符串的前 3 个字符 (bar
),但是IDENTIFIER
规则将匹配 4 个字符。作为IDENTIFIER
匹配更长的子串,它被选择BAR
.
EOF
(文件结尾) 是隐式定义的标记类型,始终出现在输入的末尾。
根据经验,应定义具体规则before更通用的规则。如果规则只能匹配先前定义的规则已覆盖的输入,则它将never使用。
隐式定义的规则,例如'foo'
就好像它们已被定义一样before所有其他词法分析器规则。由于它们增加了复杂性,建议完全避免它们并声明显式词法分析器规则。这种方法的一个显着优点是,只需将标记列表放在一个位置,而不是将它们分散在语法中。