本文使用的Antlr版本为4.7.2
demo来自第七章的第一节,属性文件的例子。我将原始文法修改后如下。
grammar Demo;
file : prop+;
prop : ID '=' STRING;
ID : [a-z]+;
STRING : [a-z0-9]+;
在控文法控制台中验证输入文本,产生错误。
输入:
abc=abc
错误提示:
line 1:4 missing STRING at 'abc'
line 1:7 mismatched input '' expecting '='
输入字符流abc=abc后,由于长期未使用相关工具及进行理论复习。根据文法文件中文法,错误的认为生成的单词流为id,=,string。并按此顺序匹配。以为是遇到ID,匹配到abc,= 匹配到字符=,最后遇到STRING去匹配STRING的词法定义。
翻书后,想了想,这个一定是数据单词识别错误问题。回想整个过程是 符号流->单词流->语法树。通过生成器生成代码后先打印看看单词流都是什么类型的。
public class AntlrTest {
public static void main(String[] args) throws Exception{
String input = "abc=abc";
CharStream charStream = CharStreams.fromString(input);
DemoLexer demoLexer = new DemoLexer(charStream);
System.out.println(demoLexer.getAllTokens());
}
}
输出如下
[[@-1,0:2='abc',<2>,1:0], [@-1,3:3='=',<1>,1:3], [@-1,4:6='abc',<2>,1:4]]
尖括号内容表示了单词的类型。根据词法分析器中的源码可以看到类型如下。
public static final int
T__0=1, ID=2, STRING=3;
因此说明等号后的abc被识别为ID。所以后续在根据文法进行语法分析的时候遇到STRING是无法找到的。所以在前文中的文法描述下STRING输入只能是数字才会避免这种问题。
练习第8章CSV的例子,简化文法后,如下。
grammar Demo;
csv : WORD;
WORD : [0-9a-z]+;
WS : [ \t\n]+ -> skip ;
测试代码
public class AntlrTest {
public static void main(String[] args) throws Exception{
String input = "1";
CharStream charStream = CharStreams.fromString(input);
DemoLexer demoLexer = new DemoLexer(charStream);
System.out.println(demoLexer.getAllTokens());
CommonTokenStream commonTokenStream = new CommonTokenStream(demoLexer);
DemoParser demoParser = new DemoParser(commonTokenStream);
demoParser.csv();
}
}
错误输出
line 1:1 missing WORD at ''
一开始产生这个问题觉得很奇怪。去掉代码demoLexer.getAllTokens()后恢复正常。随后查看getAllTokens的源码。getAllTokens是调用lexer的nextToken方法获取下一个词法符号
public List<? extends Token> getAllTokens() {
List<Token> tokens = new ArrayList<Token>();
Token t = nextToken();
while ( t.getType()!=Token.EOF ) {
tokens.add(t);
t = nextToken();
}
return tokens;
}
nextToken内部,当_hitEOF为真时会返回EOF。调用getAllTokens后,_hitEOF标记会被置为true,
if (_hitEOF) {
emitEOF();
return _token;
}
调用demoParser.csv();生成语法分树时,同样会从token流中获取单词,这时由于调用了getAllTokens,再次获取时会得到EOF。match方法调用getCurrentToken得到的是EOF。因此就产生了后续的missing WORD at ‘’ 异常。最后删除getAllTokens调用恢复正常。
public Token match(int ttype) throws RecognitionException {
Token t = getCurrentToken();
if ( t.getType()==ttype ) {
if ( ttype==Token.EOF ) {
matchedEOF = true;
}
_errHandler.reportMatch(this);
consume();
}
else {
t = _errHandler.recoverInline(this);
if ( _buildParseTrees && t.getTokenIndex()==-1 ) {
// we must have conjured up a new token during single token insertion
// if it's not the current symbol
_ctx.addErrorNode(createErrorNode(_ctx,t));
}
}
return t;
}
"main@1" prio=5 tid=0x1 nid=NA runnable
java.lang.Thread.State: RUNNABLE
at org.antlr.v4.runtime.Parser.match(Parser.java:198)
at antcode.DemoParser.csv(DemoParser.java:119)
at complie.AntlrTest.main(AntlrTest.java:20)
"Finalizer@759" daemon prio=8 tid=0x3 nid=NA waiting
java.lang.Thread.State: WAITING
at java.lang.Object.wait(Object.java:-1)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)
"Reference Handler@760" daemon prio=10 tid=0x2 nid=NA waiting
java.lang.Thread.State: WAITING
at java.lang.Object.wait(Object.java:-1)
at java.lang.Object.wait(Object.java:502)
at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
"Signal Dispatcher@758" daemon prio=9 tid=0x4 nid=NA runnable
java.lang.Thread.State: RUNNABLE
避免在获取parseTree之前调用lexer的getAllTokens()方法,方式出现遇到EOF获取不到TOKEN的问题