ANTLR4解析顺序正确但Context为空,如何获取文本内容?
我尝试解析形如「Answer(x,y,z):-R(x,y),U(y,z).」的合取查询,使用Java(Maven)中的ANTLR4构建了如下语法规则:
grammar CQgrammar; // Parser rules query : head ':-' body; head : atom; body : atom (',' atom)* '.'; atom : ATOMNAME '(' term* (',' term)* ')'; term : CONST | VAR; // Lexer rules VAR: [a-z]; CONST: '"' [a-zA-Z0-9]+ '"'; TERMS: '(' | ')' | ',' | '.' | ':-'; ATOMNAME: [a-zA-Z0-9]+;
随后我扩展生成的Listener类,重写了enterQuery、enterHead、enterBody、enterAtom、enterTerm方法,当前仅在这些方法中加入简单打印语句,目标是构建合取查询类。主函数如下:
public static void main(String[] args) { String query = ""Answer(x,y,z):-R(x,y),U(y,z).""; org.parsing.CQgrammarLexer lexer = new org.parsing.CQgrammarLexer(CharStreams.fromString(query)); org.parsing.CQgrammarParser parser = new org.parsing.CQgrammarParser(new CommonTokenStream(lexer)); CQparser listener = new CQparser(); parser.addParseListener(listener); parser.query(); }
从打印结果可见,解析顺序正确且无错误,但在这些重写方法中调用ctx.getText()时返回空值,ctx的其他Getter方法(如enterAtom中的ctx.Atom())也为空。怀疑Lexer部分存在问题,但文档说明不够清晰,请问该如何解决?
1. 修复Lexer规则的优先级与定义错误
ANTLR4的Lexer规则按顺序匹配最长字符串,原TERMS规则存在核心问题:将多个符号打包为同一token类型,导致Parser无法正确识别语法中的对应符号,进而上下文对象无法关联有效内容。
替换原TERMS规则,为每个符号单独定义明确的token类型:
// 替换原TERMS规则 COLONMINUS : ':-'; LPAREN : '('; RPAREN : ')'; COMMA : ','; DOT : '.';
2. 修正Parser中atom规则的冗余定义
原atom规则里的term* (',' term)*存在语法歧义,会导致解析逻辑混乱,修改为更严谨的参数列表定义:
atom : ATOMNAME LPAREN (term (COMMA term)*)? RPAREN;
该规则既支持空参数列表,也支持多个逗号分隔的参数,避免重复定义引发的解析异常。
3. 修正主函数中的字符串转义问题
主函数中使用的"是HTML实体,应替换为Java原生的字符串转义符\",若查询本身不需要引号包裹,直接写原始字符串即可:
String query = "Answer(x,y,z):-R(x,y),U(y,z).";
否则Lexer会把开头的"误判为CONST的一部分,导致解析错位。
4. 正确获取Listener上下文内容
修改后的语法生成的Parser上下文会包含有效子节点,示例代码如下:
@Override public void enterAtom(CQgrammarParser.AtomContext ctx) { // 获取原子名称 System.out.println("原子名称: " + ctx.ATOMNAME().getText()); // 获取所有参数 List<CQgrammarParser.TermContext> terms = ctx.term(); for (CQgrammarParser.TermContext termCtx : terms) { System.out.println("参数: " + termCtx.getText()); } }
最终修正后的完整语法
grammar CQgrammar; // Parser rules query : head COLONMINUS body; head : atom; body : atom (COMMA atom)* DOT; atom : ATOMNAME LPAREN (term (COMMA term)*)? RPAREN; term : CONST | VAR; // Lexer rules VAR : [a-z]+; // 可选:支持多字符变量名,如xyz,仅需单字符可保留[a-z] CONST : '"' [a-zA-Z0-9]+ '"'; COLONMINUS : ':-'; LPAREN : '('; RPAREN : ')'; COMMA : ','; DOT : '.'; ATOMNAME : [a-zA-Z0-9]+;
内容的提问来源于stack exchange,提问作者nTn

