【发布时间】:2016-03-01 13:20:26
【问题描述】:
简介
查看文档,ANTLR 2 曾经有一个叫做 predicated lexing 的东西,有这样的例子(受 Pascal 启发):
RANGE_OR_INT
: ( INT ".." ) => INT { $setType(INT); }
| ( INT '.' ) => REAL { $setType(REAL); }
| INT { $setType(INT); }
;
在我看来,这本质上是规则开头的积极前瞻断言:如果前瞻与 INT ".." 匹配,则将应用第一条规则(并匹配该输入的 INT 部分) 等等。
我还没有在 ANTLR 4 中找到类似的东西。 2 to 3 migration guide 似乎没有提到这一点,而 3 to 4 changes document 声明:
ANTLR 3 和 4 之间最大的区别是 ANTLR 4 接受你给它的任何语法,除非语法有间接左递归。这意味着我们不需要语法谓词或回溯,因此 ANTLR 4 不支持该语法;使用时会收到警告。
如果我基本上保持原样,这与我收到的错误消息一致:
(...)=> syntactic predicates are not supported in ANTLR 4
虽然我可以理解更智能的 解析器 实现如何解决这些歧义,但我看不出这对 lexers 是如何工作的。
重现示例
为了确定,让我们试试这个:
grammar Demo;
prog: atom (',' atom)* ;
atom: INT { System.out.println("INT: " + $INT.getText()); }
| REAL { System.out.println("REAL: " + $REAL.getText()); }
| a=INT RANGE b=INT { System.out.println("RANGE: " +
$a.getText() + " .. " + $b.getText()); }
;
WS : (' ' | '\t' | '\n' | '\r')+ -> skip ;
INT : ('0'..'9')+ ;
REAL: INT '.' INT? | '.' INT ;
RANGE: '..' ;
保存到Demo.g,然后编译运行:
$ wget -nc http://www.antlr.org/download/antlr-4.5.2-complete.jar
$ java -jar antlr-4.5.2-complete.jar Demo.g
$ javac -cp antlr-4.5.2-complete.jar Demo*.java
$ java -cp .:antlr-4.5.2-complete.jar org.antlr.v4.gui.TestRig \
Demo prog <<< '1,2.,3.4,5 ..6,7..8'
INT: 1
REAL: 2.
REAL: 3.4
RANGE: 5 .. 6
REAL: 7.
line 1:17 extraneous input '.8' expecting {<EOF>, ','}
看来我是对的:虽然删除句法前置词可能适合解析器,但词法分析器不会突然猜出正确的标记类型。
核心问题
那么如何将这个特定示例转换为 ANTLR 4? 有没有一种方法可以表达前瞻条件?或者也许有一种方法可以让像INT '..' 这样的单一规则发出两个不同的令牌?
参考和可能的解决方案
查看 ANTLR 4 Pascal grammar,我注意到它不允许实数以 . 结尾而没有数字,因此从那里学习解决方案似乎不是一个选择。
我见过Semantic predicates in ANTLR4? 和syntactic predicates - Upgrading from Antlr 3 to Antlr 4。两者都讨论了解析器规则中的句法谓词。后者也有一个带有词法分析器规则的示例,但前瞻与遵循它的规则相同,这意味着可以删除规则而不会产生不利影响。在我上面的示例中不是这种情况。
对check previous/left token in lexer 的回答提到了词法分析器的emit 方法,并附有引用ANTLR 3 wiki 中How can I emit more than a single token per lexer rule? 常见问题解答页面的评论,所以我想这是一种方法。如果没有人超过我并且我可以让它在我的示例中工作,我会将它变成一个答案。
ANTLR4 negative lookahead in lexer 的答案使用_input.LA(int) 方法来检查前瞻。 ANTLR 4 lexical analysis faq 提到了_input.LA,但没有详细说明。这也应该适用于上面的示例,但对于需要考虑多个前瞻字符的场景来说会很困难。
【问题讨论】:
标签: antlr antlr4 regex-lookarounds lexical-analysis