ANTLR4中是否仍然支持无扫描语法分析器



我有一个使用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文件更麻烦。两者都需要是所有有效字符的详尽列表。(

最新更新