如何在Java ANTLR4的visitTerminal重写方法中获取Python注释?
如何在ANTLR4 Python监听器中捕获注释内容
背景
根据相关方案自动生成了以下ANTLR4文件:
- Python3Lexer.java
- Python3ParserBase.java
- Python3ParserListener.java
- PythonDocstringModifierListener.java
- Python3Parser.java
随后编写了自定义监听器类:
public class SomePythonListener extends Python3ParserBaseListener { public SomePythonListener(Python3Parser parser, String someValue) { this.parser = parser; this.someValue = someValue; } @Override public void visitTerminal(TerminalNode node) { Token token = node.getSymbol(); System.out.println("token.getType()=" + token.getType()); System.out.println("getText:" + token.getText() + "XXXX\n\n"); } }
测试用的Python源码:
"""A file docstring. With a multiline starting docstring. That spans the first 3 lines.""" # Some Comment. # Another comment """Some string.""" def foo(): """Some docstring.""" print('hello world') def bar(): """Another docstring.""" print('hello world') def baz(): """Third docstring.""" print('hello universe')
运行后发现,文档字符串、换行符都被正常捕获,但# Some Comment.和# Another comment这两个注释完全没出现在输出里。
问题根源
自定义监听器的visitTerminal方法只能处理**默认通道(DEFAULT_TOKEN_CHANNEL)的Token,而ANTLR会把注释这类不需要参与语法解析的内容放到隐藏通道(HIDDEN)**中,这些Token不会被加入到语法树,所以默认方法访问不到。
解决方法
方法1:从Token流中提取隐藏通道的注释
在监听器中通过Token流手动获取隐藏通道的内容,推荐在enterEveryRule方法中处理(确保每个语法规则执行前都检查前置的注释):
public class SomePythonListener extends Python3ParserBaseListener { private final CommonTokenStream tokenStream; public SomePythonListener(Python3Parser parser, String someValue) { this.tokenStream = (CommonTokenStream) parser.getInputStream(); this.parser = parser; this.someValue = someValue; } @Override public void enterEveryRule(ParserRuleContext ctx) { // 获取当前规则起始Token左侧的所有隐藏Token List<Token> hiddenTokens = tokenStream.getHiddenTokensToLeft(ctx.getStart().getTokenIndex()); if (hiddenTokens != null) { for (Token token : hiddenTokens) { // 匹配Python3Lexer中定义的注释类型(需确认实际的注释Token类型值) if (token.getType() == Python3Lexer.COMMENT) { System.out.println("捕获到注释:" + token.getText()); } } } } // 保留原有的visitTerminal方法 @Override public void visitTerminal(TerminalNode node) { Token token = node.getSymbol(); System.out.println("token.getType()=" + token.getType()); System.out.println("getText:" + token.getText() + "XXXX\n\n"); } }
方法2:修改词法规则(不推荐)
如果不想处理隐藏通道,可以修改Python3Lexer.g4中的注释规则,将注释从HIDDEN通道移到默认通道:
原规则大概是这样:
COMMENT : '#' ~[\r\n]* -> channel(HIDDEN);
修改为:
COMMENT : '#' ~[\r\n]*;
修改后重新生成词法分析器,注释就会出现在默认通道,visitTerminal就能捕获到。但这种方法会改变语法树结构,可能影响后续解析逻辑,谨慎使用。
原理说明
ANTLR默认将注释、空白符这类辅助性内容放到HIDDEN通道,避免干扰语法解析逻辑。这些Token不会进入语法树,所以必须手动从Token流中提取才能访问到。
内容的提问来源于stack exchange,提问作者a.t.
相关产品推荐
相关产品推荐

