Antlr4 ANTLR 4:识别和#x27;和';但不是';或';没有空间
我正在使用IntelliJ中的ANTLR 4插件,我有一个最奇怪的bug。我将从相关的解析器/词法分析器规则开始:Antlr4 ANTLR 4:识别和#x27;和';但不是';或';没有空间,antlr4,Antlr4,我正在使用IntelliJ中的ANTLR 4插件,我有一个最奇怪的bug。我将从相关的解析器/词法分析器规则开始: // Take care of whitespace. WS : [ \r\t\f\n]+ -> skip; OTHER: . -> skip; STRING : '"' [A-z ]+ '"' ; evaluate // starting rule. : textbox? // could be an empty textbox. ; textbox : (r
// Take care of whitespace.
WS : [ \r\t\f\n]+ -> skip;
OTHER: . -> skip;
STRING
: '"' [A-z ]+ '"'
;
evaluate // starting rule.
: textbox? // could be an empty textbox.
;
textbox
: (row '\n')*
;
row
: ability
| ability_list
ability
: activated_ability
| triggered_ability
| static_ability
triggered_ability
: trigger_words ',' STRING
;
trigger_words
: ('when'|'whenever'|'as') whenever_triggers|'at'
;
whenever_triggers
: triggerer (('or'|'and') triggerer)* // this line has the issue.
;
triggerer
: self
self: '~'
我将此文本传递给它:无论何时~或
,它在或
上失败,说行1:10输入不匹配”或“预期{'or','and}
。但是,如果每当触发规则的或
字符串时(使其成为或“|”和“
”),我都在中添加一个空格,那么它就可以正常工作
最奇怪的是,如果我在~and ~
时尝试,即使规则在和
字符串中没有空格,它也可以正常工作。如果我将和“|”或“
作为lexer规则,这一点不会改变。这很奇怪。我已经确认,在AntlrWorks2中运行“测试平台”时会发生此错误,因此它不仅仅是IntelliJ的东西
这是发生错误时解析树的图像:
好吧,你或多或少都是自己找到答案的,因此我的这个答案将重点解释问题发生的原因
首先,对于每个在这个问题上结结巴巴的人来说,问题是他定义了另一个隐式lexer规则,类似于”或“
”(注意空格)。将其更改为”或“
”解决了问题
但为什么会有问题呢?
为了理解这一点,您必须理解如果在一个解析器规则中编写'
,ANTLR会做什么:编译语法时,它将为每个声明生成一个新的lexer规则。这些lexer规则将在语法中定义的lexer规则之前创建。lexer本身将把给定的输入匹配到令牌中,为此,它一次按声明的顺序处理每个lexer规则。因此,它总是从隐式标记定义开始,然后转到最顶层的“真实”lexer规则。
问题是lexer在这个过程中并不太聪明,这意味着一旦它将一些输入与当前lexer规则匹配,它将创建一个相应的令牌并继续使用后续输入
因此,随后出现的一个lexer规则也将与输入匹配(但作为另一个标记,因为它是一个不同的lexer规则),将被跳过,这样相应的输入可能不会具有预期的标记类型,因为lexer规则本身已过度使用
在您的示例中,自覆盖规则是'或'
(令牌1)和'或'
(令牌2)。每个隐式lexer规则声明都会产生不同的lexer规则,当第一个规则匹配时,我假设它是在第二个规则之前声明的。
现在看看你的输入:无论何时~或~
词法分析器都会开始解释它,它遇到的第一条规则是”或“
(当然,在匹配开始后),它会匹配输入,因为或
之前确实有一个空格。因此,它将匹配它作为令牌1。
另一方面,解析器此时需要一个令牌2,这样它就会抱怨给定的输入(尽管它实际上是在抱怨错误的令牌类型)。只要~或~
将输入更改为,就会得到正确的解释
这就是为什么不应该在语法中使用隐式标记定义的原因(除非它非常小)。为每个输入创建一个新的lexer规则,并从最具体的规则开始。这意味着匹配特殊字符序列(例如关键字)的规则应该在诸如ID
或STRING
之类的常规lexer规则之前声明。为了防止lexer在无法识别的输入上抛出错误,匹配所有字符的规则必须最后声明,因为它们将覆盖它们之后的每个lexer规则。好的,您或多或少地自己找到了答案,因此我的这个答案将重点解释为什么问题会在第一次出现地点
首先,对于每个在这个问题上结结巴巴的人来说,问题是他定义了另一个隐式lexer规则,类似于”或“
”(注意空格)。将其更改为”或“
”解决了问题
但为什么会有问题呢?
为了理解这一点,您必须理解如果在一个解析器规则中编写'
,ANTLR会做什么:编译语法时,它将为每个声明生成一个新的lexer规则。这些lexer规则将在语法中定义的lexer规则之前创建。lexer本身将把给定的输入匹配到令牌中,为此,它一次按声明的顺序处理每个lexer规则。因此,它总是从隐式标记定义开始,然后转到最顶层的“真实”lexer规则。
问题是lexer在这个过程中并不太聪明,这意味着一旦它将一些输入与当前lexer规则匹配,它将创建一个相应的令牌并继续使用后续输入
因此,随后出现的一个lexer规则也将与输入匹配(但作为另一个标记,因为它是一个不同的lexer规则),将被跳过,这样相应的输入可能不会具有预期的标记类型,因为lexer规则本身已过度使用
在您的示例中,自覆盖规则是'或'
(令牌1)和'或'
(令牌2)。每个隐式lexer规则声明都会产生不同的lexer规则,当第一个规则匹配时,我假设它是在第二个规则之前声明的。
现在看看你的输入:无论何时~或~
词法分析器都会开始解释它,它遇到的第一条规则是”或“
(当然在匹配开始后),它会匹配输入,因为在输入之前确实有一个空格