我有一个使用CharsAsTokens伪lexer的无扫描语法分析器,它为ANTLR4到4.6版本生成了一个可用的Java parser类。但是,当更新到ANTLR 4.7.2到4.9.3-SNAPSHOT时,该工具会从同一语法文件生成数十个编译错误的代码,如下所述。
我的问题很简单:是不再支持无扫描语法分析器,还是在4.7及更高版本中必须以不同的方式指定基于字符的终端?
更新:
不幸的是,我无法在这里发布我的完整语法,因为它源于FOUO安全标记指南,美国政府(我是国防部/IC承包商(会禁止访问该指南。
然而,Ter在《最终ANTLR 4参考》第5.6节中提到的CSQL.g4无扫描解析器语法示例完全可以再现不兼容的升级问题。
和我的语法一样,CSQL示例使用CharsAsTokens.java作为其标记器,并使用CharVocab.tokens作为其标记词汇表。
请注意,每个令牌名称都是由其ASCII字符文字等效项指定的,如:
'*'=42
'+'=43
语法分析器直接在其规则中引用引用的令牌名称,如:
star: '*' ws? ;
plus: '+' ws? ;
这里的问题是,使用ANTLR4版本4.2到4.6从这些语法生成了可编译的解析器类,而ANTLRv4.7.2及更高版本生成的Java代码有很多错误。
以下是ANTLRv4.6:生成的可用CSQLJava类定义中的一个片段
public static class ArgsContext extends ParserRuleContext {
public List<ArgContext> arg() {
return getRuleContexts(ArgContext.class);
}
public ArgContext arg(int i) {
return getRuleContext(ArgContext.class,i);
}
public ArgsContext(ParserRuleContext parent, int invokingState) {
super(parent, invokingState);
}
@Override public int getRuleIndex() { return RULE_args; }
@Override
public void enterRule(ParseTreeListener listener) {
if ( listener instanceof CSQLListener ) ((CSQLListener)listener).enterArgs(this);
}
@Override
public void exitRule(ParseTreeListener listener) {
if ( listener instanceof CSQLListener ) ((CSQLListener)listener).exitArgs(this);
}
}
以下是ANTLR v4.7.2:生成的相应但现已损坏的代码
public static class ArgsContext extends ParserRuleContext {
public List<ArgContext> arg() {
return getRuleContexts(ArgContext.class);
}
public ArgContext arg(int i) {
return getRuleContext(ArgContext.class,i);
}
public List<TerminalNode> ','() { return getTokens(CSQL.','); } // line 446
public TerminalNode ','(int i) { // line 447
return getToken(CSQL.',', i); // line 448
} // line 449
public ArgsContext(ParserRuleContext parent, int invokingState) {
super(parent, invokingState);
}
@Override public int getRuleIndex() { return RULE_args; }
@Override
public void enterRule(ParseTreeListener listener) {
if ( listener instanceof CSQLListener ) ((CSQLListener)listener).enterArgs(this);
}
@Override
public void exitRule(ParseTreeListener listener) {
if ( listener instanceof CSQLListener ) ((CSQLListener)listener).exitArgs(this);
}
}
上面的编号行仅由更新的ANTLR工具生成(没有添加注释(,编译后会产生:
Syntax error on token "','", Identifier expected CSQL.java /CSQL/generated-sources line 446 Java Problem
Syntax error on token "','", delete this token CSQL.java /CSQL/generated-sources line 447 Java Problem
CSQL cannot be resolved to a variable CSQL.java /CSQL/generated-sources line 448 Java Problem
Syntax error on token ".", , expected CSQL.java /CSQL/generated-sources line 448 Java Problem
那么,为什么ANTLR v4.7+中存在向后不兼容的更改,以及我应该如何最好地解决它呢?
尝试定义GrammarLexer.g4文件,而不是GrammarLexer.tokens文件。(您仍然可以像创建GrammarLexer.tokens文件一样使用options: { tokenVocab = GrammarLexer; }
(它可以简单到:
T1 : ' ';
T2 : 'n';
T3 : 'r';
T4 : 'a';
T5 : 'b';
这将为您创建令牌名称。Antlr将允许您在解析器语法规则中包含'a'
、'n'
等,但会将它们与lexer语法中的lexer规则名称进行匹配,并使用该名称(例如:当您的规则中有'a'
时为T4
,当您有'n'
时为T2
(,这样它将编译干净。只要CharsAsTokens
生成相同的令牌值,就不必使用lexer。(不过,仔细想想,这个杠杆可能相当于你正在使用的CharsAsTokens
代币化器,并可以保证代币编号匹配。(
这似乎仍然可以实现您的目标,即令牌只是一个字符流,并处理解析器规则中的所有内容。(这并不比生成*.tokens文件更麻烦。两者都需要是所有有效字符的详尽列表。(