我正在ANTLR4中定义一个语法,它分别包括单词和数字。
数字描述如下:
NUM
: INTEGER+ ('.' INTEGER+)?
;
fragment INTEGER
: ('0' .. '9')
;
并描述了单词:
WORD
: VALID_CHAR +
;
fragment VALID_CHAR
: ('a' .. 'z') | ('A' .. 'Z')
;
下面的简化语法描述了单词或字母之间的加法(需要像这样递归定义(:
expression
: left = expression '+' right = expression #addition
| value = WORD #word
| value = NUM #num
;
问题是,当我在解析器中输入"d3"时,我会得到一个返回的Word"d"实例。类似地,输入3f将返回数值为3的数字。有没有办法确保"d3"或任何类似的字符串从语法中返回错误消息?
我看过"~"符号,但它似乎是"除外的一切",而不是"唯一"。
总之,我正在寻找一种方法来确保只有一系列字母可以解析为一个单词,并且不包含其他符号。目前,语法似乎忽略了任何其他不允许使用的字符。
类似于输入"3+"时收到的消息:
simpleGrammar::compileUnit:1:2: mismatched input '<EOF>' expecting {WORD, NUM}
目前,出现以下情况:
d --> (d) (word) (correct)
22.3 --> (22.2) number (correct)
d3 --> d (word) (incorrect)
22f.4 --> 22 (number) (incorrect)
但理想情况下会发生以下情况:
d --> (d) (word) (correct)
22.3 --> (22.2) number (correct)
d3 --> (error)
22f.4 --> (error)
【修订以回应修订后的问题和意见】
ANTLR将尝试在输入流中匹配输入流中的内容,然后在达到可识别的最长输入后停止。这意味着,ANTLR对您的输入所能做的最好的事情就是识别一个单词('d'(,然后完全识别它,因为它可以将您输入的其余部分与您的任何规则(使用根expression
规则(相匹配
您可以添加一个规则来告诉ANTLR它需要消耗到整个输入中,并使用一个顶级规则,比如:
root: expression EOF;
使用此规则后,您将在"d3"中的"3"处获得"不匹配的输入"。
同样的规则会在"22f.4"中的"f"字符处给出"不匹配的输入"。
这应该能解决您提出的特定问题,并且有望满足您的需求。下面的讨论是对你的评论进行一点解读,可能会以错误消息的方式对你想要的内容进行过多假设。
你的评论(有点(暗示你更喜欢看到类似";你的单词中有一个数字";,或";你的号码里有一个字母";
它有助于理解ANTLR处理输入的管道。首先,它使用Lexer规则(以大写字母开头的规则(来处理您的输入流,以创建令牌流。
您的"d3"输入使用当前语法生成一个由2个标记组成的流;
WORD ('d')
NUM ('3')
这个令牌流就是您的解析器规则(即expression
(中要匹配的内容
流中的'22f.4'结果:
NUM ('22')
WORD ('f')
(I would expect an error here as there is no Lexer rule that matches a stream of characters beginning with a '.')
当ANTLR在匹配您的NUM
规则时看到数字(或'.'(以外的东西时,它会认为到目前为止匹配的是NUM
令牌的内容,将其放入令牌流中并继续前进。(类似于在单词中查找数字(
这是标准的词法分析/解析行为。
你可以实现你自己的ErrorListener,ANTLR会把它遇到的错误的细节交给你,你可以根据自己的意愿写错误消息,但我认为你会发现很难达到目标。你在错误处理程序中没有足够的上下文来知道之前发生了什么,等等,即使你做到了,这会很快变得非常复杂。
如果您总是希望在NUM
s和WORD
s之间出现某种空白,您可以执行类似于定义以下Lexer规则的操作:
BAD_ATOM: (INTEGER|VALID_CHAR|'.')+;
(将其放在语法的最后,以便有效流将首先匹配(
然后,当解析器规则与BAD_ATOM
规则发生错误时,您可以检查它并提供更具体的错误消息。
警告:这有点非正统,可能会在构建语法时对所允许的内容进行限制。也就是说;"一网打尽";语法底部的Lexer规则,有些人使用它来获得更好的错误消息和/或错误恢复。