介绍
观察文档,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
虽然我可以理解更智能的解析器实现如何解决这些歧义,但我不明白这对于词法分析器会如何运作。
重现示例
为了确定,让我们尝试一下:
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,我注意到它不允许实数以
.
结尾,后面没有数字,因此从那里学习解决方案似乎不是一个选项。我看到了ANTLR4中的语义谓词?和从Antlr3升级到Antlr4的句法谓词。两者都讨论了解析器规则中的句法谓词。后者还提供了一个带有词法分析器规则的示例,但是向前查看与其后跟随的规则相同,这意味着规则可以被删除而不会产生不良影响。而在我上面的例子中,情况并非如此。
回答在词法分析器中检查先前/左侧标记提到了词法分析器的
emit
方法,并引用了如何在每个词法分析器规则中发出多个令牌? ANTLR 3 wiki 中的FAQ页面,所以我想那可能是一种方法。如果没有其他人比我更快地做到并且我能在我的示例中使其工作,我将把它变成一个答案。
ANTLR4负向预查在词法分析器中的应用的答案利用了_input.LA(int)
方法来检查前瞻。ANTLR 4 词法分析FAQ提到了_input.LA
,但没有详细说明。这对上面的示例应该也适用,但对于需要考虑多个字符前瞻的情况会更加困难。
YourParser.INT
/YourParser.RANGE
提供给词法分析器的_factory
。 - undefined