【问题标题】:Understanding ANTLR/EBNF parentheses了解 ANTLR/EBNF 括号
【发布时间】:2019-12-17 09:31:50
【问题描述】:

ANTLR Reference Guide 对括号的表述如下:

另一方面,我有以下语法:

fragment LOWERCASE  : [a-z] ;
fragment UPPERCASE  : [A-Z] ;
fragment DIGIT : [0-9] ;

WORD                : (LOWERCASE | UPPERCASE | DIGIT )+;

如果我运行字符串“asd90”,它将被匹配为一个字,如下所示:

>java org.antlr.v4.gui.TestRig Myg myg -tokens
asd90
[@0,0:4='asd90',<WORD>,1:0]
[@1,5:6='\r\n',<NEWLINE>,1:5]
[@2,7:6='<EOF>',<EOF>,2:0]

这让我感到困惑,因为我希望“asd90”不匹配,因为在我的语法中,LOWERCASE 和 DIGIT 是 2 个不同的子规则,所以我希望它不匹配为“WORD”。

换句话说,这就像说“intvoid”可以作为返回类型,而它显然不会。

【问题讨论】:

  • LOWERCASEDIGIT 不是 2 个不同的子规则,它们是一个特定规则的片段(更准确地说是令牌)WORD。简单来说,这听起来像:“WORD 可能包含小写字符或大写字符或数字”。这就是“asd90”的意义所在。至于intvoid - 它与int void 不同。

标签: antlr antlr4 parentheses bnf ebnf


【解决方案1】:

您似乎在考虑中错过了循环运算符。词法分析器规则WORD 可以匹配任何LOWERCASEUPPERCASEDIGIT序列,这意味着使用该循环的5 次运行匹配asd90 是完全有效的(匹配小写规则 3 次,数字规则匹配 2 次)。

书中的例子没有循环,所以只能匹配一次,要么是类型要么是void

更多细节:片段规则有点像私有规则(事实上在旧的 ANLTR 版本中有关键字private,后来被重命名为fragment)。但是,它们在解析器中不可用,并且不会出现在词法分析器规则列表中。您的 WORD 规则存储在 ATN 中,如下所示:

这表明 3 个片段规则的调用方式与其他任何规则一样。

但还有另一个区别(这就是 Pavel 提到的)。在解决歧义时,片段词法分析器规则的处理方式略有不同。

通常分辨率是这样的:匹配最长输入的规则获胜。如果两个规则匹配相同的输入,则语法中最先出现的规则获胜。这适用于解析器和词法分析器规则。但是,不适用于片段规则。即使与另一个(非片段)规则匹配相同输入的片段规则首先出现在语法中,非片段规则仍然获胜。

你可以通过稍微改变你的语法来证明这一点:

grammar Example;

start: WORD;

WS : [ \t\r\n]+ -> skip;

fragment LOWERCASE  : [a-z]+ ;
fragment UPPERCASE  : [A-Z]+ ;
fragment DIGIT : [0-9]+ ;
WORD: (LOWERCASE | UPPERCASE | DIGIT )+;

这会给你这些令牌:

使用输入abc,您仍将获得WORD 作为匹配标记,即使LOWERCASE 在语法中排在第一位。现在删除 fragment 关键字,您将看到 LOWERCASE 匹配:

Parser error (1, 1): mismatched input 'abc' expecting WORD

Tokens:
[@0,0:2='abc',<2>,1:0]
[@1,4:3='<EOF>',<-1>,2:0]

Parse Tree:
start (
  <Error>"abc"
)

这会导致语法错误,因为需要 WORD。令牌列表变为:

但是,即使您删除了所有片段关键字,您仍然会得到 WORD 输入,例如 abc90,因为该规则与之前的任何单个词法分析器规则匹配得更多。

【讨论】:

  • 循环运算符不是重点,即使我在本书示例中添加了一个循环运算符,它也可以匹配 'voidvoid' 但不能匹配 'intvoid' 在 WORD 的情况下我可以混合任何子片段。我认为答案在上面@Pavel Smirnov 的评论中。我比较了 2 个不同的东西,在书本案例中,它是解析器中的子规则,它被隔离评估,而在片段的情况下,它就像将片段内联到单个“规则”中。
  • 我猜你误解了“+”的工作原理。在我看来,您正在阅读它,就像“应用前面的解析,如果匹配,则一遍又一遍地应用它的 match”。这不是“+”的工作方式。它的作用是“应用前面的 parsing 并在匹配时一遍又一遍地 reaply parsing
  • 首先感谢进一步的解释。因此,如果我在本书示例中添加了一个“+”,那么“intvoid”将是一个有效的匹配项吗?
  • 是的,就是这样。它甚至可能是 int void 之类的东西,因为 returnType 规则是一个解析器规则,它还将接受由隐藏标记分隔的输入(如跳过的空格)。
猜你喜欢
  • 1970-01-01
  • 2021-03-14
  • 1970-01-01
  • 2021-02-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多