ANTLR4 是否仍然支持无扫描器解析器语法?
Are scannerless parser grammars still supported in ANTLR4?
我有一个使用 CharsAsTokens 人造词法分析器的无扫描器解析器语法,它为 ANTLR4 版本到 4.6 生成可用的 Java 解析器 class。但是当通过 4.9.3-SNAPSHOT 更新到 ANTLR 4.7.2 时,该工具生成的代码会从同一个语法文件中产生数十个编译错误,详情如下。
我的问题很简单:是否不再支持无扫描器解析器语法,或者必须在 4.7 及更高版本中以不同方式指定其基于字符的终端?
更新:
不幸的是,我无法在此处 post 我的完整语法,因为它源自 FOUO 安全标记指南,U.S 限制对其的访问。政府(我是 DoD/IC 承包商)。
然而,不兼容的升级问题完全可以用 CSQL.g4 scannerless parser grammar example referred to by Ter in Section 5.6 of The Definitive ANTLR 4 Reference 重现。
与我的语法一样,CSQL 示例使用 CharsAsTokens.java for its tokenizer, and CharVocab.tokens 作为其标记词汇表。
请注意,每个令牌名称均由其对应的 ASCII 字符文字指定,如:
'\*'=42
'+'=43
并且解析器语法在其规则中直接引用引用的标记名称,如:
star: '*' ws? ;
plus: '+' ws? ;
这里的问题是,使用 ANTLR4 版本 4.2 到 4.6 从此类语法生成可编译的解析器 classes,而 ANTLR v4.7.2 及更高版本生成的 Java 代码有很多错误。
这是 ANTLR v4.6 生成的可用 CSQL Java class 定义的片段:
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 语法中的词法分析器规则名称匹配并使用该名称(例如:T4
当你的规则中有 'a'
时,T2
当你有 '\n'
) 时,它会编译干净。只要您的 CharsAsTokens
产生相同的标记值,您就不必使用词法分析器。 (不过,考虑一下,该杠杆可能相当于您正在使用的 CharsAsTokens
分词器,并且会保证分词数字匹配。)
看来这仍然可以实现您的目标,即标记只是一个字符流,并处理解析器规则中的所有内容。 (并且不会比生成 *.tokens 文件更麻烦。两者都需要是所有有效字符的详尽列表。)
我有一个使用 CharsAsTokens 人造词法分析器的无扫描器解析器语法,它为 ANTLR4 版本到 4.6 生成可用的 Java 解析器 class。但是当通过 4.9.3-SNAPSHOT 更新到 ANTLR 4.7.2 时,该工具生成的代码会从同一个语法文件中产生数十个编译错误,详情如下。
我的问题很简单:是否不再支持无扫描器解析器语法,或者必须在 4.7 及更高版本中以不同方式指定其基于字符的终端?
更新:
不幸的是,我无法在此处 post 我的完整语法,因为它源自 FOUO 安全标记指南,U.S 限制对其的访问。政府(我是 DoD/IC 承包商)。
然而,不兼容的升级问题完全可以用 CSQL.g4 scannerless parser grammar example referred to by Ter in Section 5.6 of The Definitive ANTLR 4 Reference 重现。
与我的语法一样,CSQL 示例使用 CharsAsTokens.java for its tokenizer, and CharVocab.tokens 作为其标记词汇表。
请注意,每个令牌名称均由其对应的 ASCII 字符文字指定,如:
'\*'=42
'+'=43
并且解析器语法在其规则中直接引用引用的标记名称,如:
star: '*' ws? ;
plus: '+' ws? ;
这里的问题是,使用 ANTLR4 版本 4.2 到 4.6 从此类语法生成可编译的解析器 classes,而 ANTLR v4.7.2 及更高版本生成的 Java 代码有很多错误。
这是 ANTLR v4.6 生成的可用 CSQL Java class 定义的片段:
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 语法中的词法分析器规则名称匹配并使用该名称(例如:T4
当你的规则中有 'a'
时,T2
当你有 '\n'
) 时,它会编译干净。只要您的 CharsAsTokens
产生相同的标记值,您就不必使用词法分析器。 (不过,考虑一下,该杠杆可能相当于您正在使用的 CharsAsTokens
分词器,并且会保证分词数字匹配。)
看来这仍然可以实现您的目标,即标记只是一个字符流,并处理解析器规则中的所有内容。 (并且不会比生成 *.tokens 文件更麻烦。两者都需要是所有有效字符的详尽列表。)