在ANTLR中监听节点时,如何便捷查找多层子上下文?
如何在ANTLR监听器中便捷查找多层子节点
当然有便捷的实现方式,不用手动逐层遍历每一层节点!针对你在enterClassDeclaration中查找所有深层LocalVariableDeclarationContext的需求,这里有两种实用的方案:
方案1:自定义递归工具方法
写一个通用的静态工具方法,递归遍历当前节点的所有子树,筛选出符合目标类型的节点。这种方式轻量直接,适合快速实现需求:
工具类实现
import org.antlr.v4.runtime.ParserRuleContext; import java.util.ArrayList; import java.util.List; public class ParseTreeUtils { // 递归查找父节点下所有类型匹配的深层子节点 public static <T extends ParserRuleContext> List<T> findAllDescendants(ParserRuleContext parent, Class<T> targetClass) { List<T> matches = new ArrayList<>(); for (int i = 0; i < parent.getChildCount(); i++) { Object child = parent.getChild(i); if (child instanceof ParserRuleContext) { ParserRuleContext childCtx = (ParserRuleContext) child; // 匹配当前节点类型 if (targetClass.isInstance(childCtx)) { matches.add(targetClass.cast(childCtx)); } // 递归查找子节点的子节点 matches.addAll(findAllDescendants(childCtx, targetClass)); } } return matches; } }
在监听器中调用
public class MyListener extends JavaParserBaseListener { @Override public void enterClassDeclaration(JavaParser.ClassDeclarationContext ctx) { // 直接调用工具方法获取所有深层局部变量声明节点 List<JavaParser.LocalVariableDeclarationContext> localVarCtxs = ParseTreeUtils.findAllDescendants(ctx, JavaParser.LocalVariableDeclarationContext.class); // 示例:打印找到的变量名 for (JavaParser.LocalVariableDeclarationContext localVarCtx : localVarCtxs) { String varName = localVarCtx.variableDeclarators().variableDeclarator(0).variableDeclaratorId().getText(); System.out.println("找到变量:" + varName); } super.enterClassDeclaration(ctx); } }
方案2:专用节点收集器监听器
利用ANTLR的ParseTreeWalker,写一个专门收集目标节点的监听器,然后在原监听器中遍历当前节点的子树。这种方式更贴合ANTLR的设计模式,扩展性更强:
节点收集器实现
import org.antlr.v4.runtime.tree.ParseTreeWalker; import java.util.ArrayList; import java.util.List; public class LocalVariableCollector extends JavaParserBaseListener { private final List<JavaParser.LocalVariableDeclarationContext> localVarCtxs = new ArrayList<>(); // 每当遍历到LocalVariableDeclaration节点时,将其加入列表 @Override public void enterLocalVariableDeclaration(JavaParser.LocalVariableDeclarationContext ctx) { localVarCtxs.add(ctx); } public List<JavaParser.LocalVariableDeclarationContext> getLocalVarCtxs() { return localVarCtxs; } }
在原监听器中使用收集器
public class MyListener extends JavaParserBaseListener { @Override public void enterClassDeclaration(JavaParser.ClassDeclarationContext ctx) { LocalVariableCollector collector = new LocalVariableCollector(); // 遍历当前Class节点的整个子树,触发收集器的回调 ParseTreeWalker.DEFAULT.walk(collector, ctx); List<JavaParser.LocalVariableDeclarationContext> localVarCtxs = collector.getLocalVarCtxs(); // 处理找到的节点... super.enterClassDeclaration(ctx); } }
两种方案对比
- 工具方法:代码量少,直接高效,适合一次性的节点查找需求。
- 收集器监听器:遵循ANTLR监听器模式,便于扩展(比如后续需要收集其他类型节点,只需在收集器中添加对应的
enterXXX方法)。
这两种方式都能帮你跳过手动逐层遍历的繁琐,直接获取所有深层目标节点。
内容的提问来源于stack exchange,提问作者Xin
相关产品推荐
相关产品推荐

