ANTLR文法中十进制数和字的单独定义

Separate definitions of decimal number and word in ANTLR grammar

我正在努力在 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 returns 一个值为 3 的数字。有没有办法确保 'd3' 或任何类似的字符串 returns 来自语法的错误消息?

我查看了“~”符号,但它似乎是 'everything except',而不是 'only'。

总而言之,我正在寻找一种方法来确保只能将一系列字母解析为 Word,并且不包含其他符号。目前,语法似乎忽略了任何其他不允许的字符。

类似于输入“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”处获得 'mismatched input'。

同样的规则会在“22f.4”中的 'f' 字符处给出 'mismatched input'。


这应该可以解决您提出的具体问题,并且希望足以满足您的需求。以下讨论是对您的评论的一些解读,并且可能对错误消息的方式假设太多。

您的评论(有点)暗示您更愿意看到类似“您的单词中有一个数字”或“您的数字中有一个字母”的错误消息

它有助于理解 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 令牌的内容,将其放入令牌流并继续前进。 (类似于在单词中查找数字)

这是标准的 lexing/parsing 行为。

您可以实现自己的 ErrorListener,其中 ANTLR 会将遇到的错误的详细信息交给您,您可以根据自己的需要对错误消息进行表述,但我认为您会发现它看起来很棘手你的目标是。您在错误处理程序中没有足够的上下文来知道之前发生了什么,等等,即使您知道,这也会很快变得非常复杂。

IF 你总是希望在 NUMs 和 WORDs 之间出现某种空白,你可以做一些事情,比如定义以下 Lexer 规则:

BAD_ATOM: (INTEGER|VALID_CHAR|'.')+;

(将其放在语法的最后,以便首先匹配有效流)

然后当解析器规则因 BAD_ATOM 规则出错时,您可以检查它并提供更具体的错误消息。

警告:这有点不正统,可能会限制您在构建语法时允许的内容。也就是说,在语法底部找到“包罗万象”的 Lexer 规则并不少见,有些人使用它来获得更好的错误消息 and/or 错误恢复。