【问题标题】:Separate definitions of decimal number and word in ANTLR grammarANTLR语法中十进制数和单词的分离定义
【发布时间】:2021-06-25 10:31:40
【问题描述】:

我正在努力在 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”时,我得到了一个返回的单词“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)

【问题讨论】:

  • 一个额外的开始规则:S : expression ENDOFTEXT; 将在 "3f" 中的 "3" 之后期望和结束文本。

标签: java regex parsing antlr antlr4


【解决方案1】:

[对修改后的问题和 cmets 的响应进行了修改]

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 会将其遇到的错误的详细信息交给您,您可以根据需要将错误消息发送给您,但我认为您会发现很难达到您的目标是。您在错误处理程序中没有足够的上下文来知道之前发生了什么等等,即使您这样做了,这也会很快变得非常复杂。

如果您总是希望在 NUMs 和 WORDs 之间出现某种空白,您可以执行类似定义以下 Lexer 规则的操作:

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

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

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

警告:这有点不正统,并且可能会限制您在构建语法时可以允许的内容。也就是说,在某些人用于更好的错误消息和/或错误恢复的语法底部找到“包罗万象”的 Lexer 规则并不罕见。

【讨论】:

  • 嗨,马克。非常感谢您的帮助,但我认为我在第一篇文章中解释得不好。如果单词包含字母以外的符号,或者数字包含字母等,我希望解析器返回错误,而不是允许单词中的数字。目前,它只是省略了第一个之后的任何意外符号,例如d3i --> d 3f4f --> 3 value3 --> value 我想知道是否有某种方法可以让用户知道他们输入了错误的字符串(即 d3)。
  • 修改了我的答案以反映修改后的问题。
  • 非常感谢马克!这个答案是惊人的。我在开发的早期就删除了 EOF(这是我第一个使用任何类型的解析器生成器的项目),但不知道它的用途。我非常感谢你,你真的救了我在这里:)
  • 很高兴它有帮助。您可以点击答案左上角应出现的复选标记来接受答案。
猜你喜欢
  • 1970-01-01
  • 2011-08-26
  • 1970-01-01
  • 1970-01-01
  • 2011-12-23
  • 1970-01-01
  • 2018-09-20
  • 2011-01-30
  • 2021-08-15
相关资源
最近更新 更多