如果有人能够清醒地看到前瞻性关系背后的混淆,以及涉及greery / non-greedy匹配的令牌化,我会非常高兴。这是一个稍长的帖子,因为它跟随我的思考过程。
我正在尝试编写antlr3语法,允许我匹配输入,例如:
“identifierkeyword”
我在Antlr 3.4中想出了一个类似的语法:
KEYWORD: 'keyword' ;
IDENTIFIER
:
(options {greedy=false;}: (LOWCHAR|HIGHCHAR))+
;
/** lowercase letters */
fragment LOWCHAR
: 'a'..'z';
/** uppercase letters */
fragment HIGHCHAR
: 'A'..'Z';
parse: IDENTIFIER KEYWORD EOF;
然而它抱怨它永远不会以这种方式匹配IDENTIFIER,我真的不明白。 (以下替代方案永远不能匹配:1)
基本上我试图指定试图匹配(LOWCHAR | HIGHCHAR)非贪婪方式的词法分析器,因此它在KEYWORD前瞻停止。到目前为止我读到的关于ANTLR词法分析器的内容应该是词法规则的某种优先权。如果我首先在词法分析器语法中指定KEYWORD词法分析器规则,那么之后的任何词法分析器规则都不应该与消耗的字符匹配。
经过一番搜索,我明白这里的问题是它无法以正确的方式标记输入,因为例如输入:“identifierkeyword”“标识符”部分首先出现,所以它决定在那里开始匹配IDENTIFIER规则是没有匹配的KEYWORD令牌。
然后我尝试在ANTLR 4中编写相同的语法,以测试新的预装功能是否可以匹配我想要的,它看起来像这样:
KEYWORD: 'keyword' ;
/** lowercase letters */
fragment LOWCHAR
: 'a'..'z';
/** uppercase letters */
fragment HIGHCHAR
: 'A'..'Z';
IDENTIFIER
:
(LOWCHAR|HIGHCHAR)+?
;
parse: IDENTIFIER KEYWORD EOF;
输入:“identifierkeyword”它产生此错误: 第1:1行不匹配的输入'd'期待'关键字'
它将字符'i'(第一个字符)与IDENTIFIER标记匹配,然后解析器需要一个他不会这样做的KEYWORD标记。
对于词法分析器的非贪婪匹配是不是应该匹配,直到前瞻中有任何其他可能性?难道它不应该期待IDENTIFIER可以包含KEYWORD并以这种方式匹配吗?
我真的对此感到困惑,我看过Terence Parr介绍ANTLR4新功能的视频,他谈到了在实际匹配规则的同时监视所有“正确”解决方案的预先运行的线程。我认为它也适用于Lexer规则,其中标记输入“identifierkeyword”的可能正确解决方案是匹配IDENTIFIER:“identifier”和匹配KEYWORD:“keyword”
我觉得我脑子里有很多关于非贪婪/贪婪匹配的错误。有人可以解释一下它是如何运作的吗?
毕竟我在这里发现了一个类似的问题:ANTLR trying to match token within longer token并且做了与之相对应的语法:
parse
:
identifier 'keyword'
;
identifier
:
(HIGHCHAR | LOWCHAR)+
;
/** lowercase letters */
LOWCHAR
: 'a'..'z';
/** uppercase letters */
HIGHCHAR
: 'A'..'Z';
这就是我现在想要的,但是我不明白为什么我不能将标识符规则更改为Lexer规则而将LOWCHAR和HIGHCHAR更改为片段。 Lexer不知道“关键字”中的字母可以作为标识符匹配吗?或相反亦然?或者可能是规则只定义为内部有一个先行,而不是所有可能的匹配语法?
答案 0 :(得分:7)
在ANTLR 3和ANTLR 4中解决此问题的最简单方法是仅允许IDENTIFIER
匹配单个输入字符,然后创建解析器规则来处理这些字符的序列。
identifier : IDENTIFIER+;
IDENTIFIER : HIGHCHAR | LOWCHAR;
这会导致词法分析器将输入identifier
作为10个单独的字符跳过,然后将keyword
作为单个KEYWORD
标记读取。
您在ANTLR 4中使用非贪婪运算符+?
观察到的行为与此类似。此运算符表示“尽可能少地匹配(HIGHCHAR|LOWCHAR)
块,同时仍会创建IDENTIFIER
令牌”。显然,创建令牌的最少数量是一个,因此这实际上是一种非常低效的编写IDENTIFIER
以匹配单个字符的方式。 parse
规则无法处理此问题的原因是它只允许单个IDENTIFIER
令牌显示在KEYWORD
令牌之前。通过像我上面所示创建解析器规则identifier
,解析器将能够将IDENTIFIER
个令牌(每个都是单个字符)的序列视为单个标识符。
编辑:在ANTLR 3中收到消息“以下备选方案永远无法匹配......”的原因是静态分析已确定规则{{1}中的正结束永远不会匹配 more 而不是1个字符,因为完全 1个字符的规则总是成功。