如何递归调用Java函数并获取派生类的版本(antlr 4)

如何递归调用Java函数并获取派生类的版本(antlr 4),java,arguments,antlr4,derived-class,graphwalker,Java,Arguments,Antlr4,Derived Class,Graphwalker,我继承了一个Antlr4解析器(Java),它使用listener方法,但有一些Walker需要特殊的大小写 背景资料: 当您在Antlr(v4)中编写解析器语法时,它会为您生成一些Java类,每个类对应一个解析器非终端。这些课程包括: PowerShellParser.ForEachObjectStatementContext用于定义ForEach语句的解析器规则。对于一个合理大小的语法,你可以得到几十个(通常接近100个类)。它们是Java类层次结构的一部分,如下所示: ParseTree

我继承了一个Antlr4解析器(Java),它使用listener方法,但有一些Walker需要特殊的大小写

背景资料:

当您在Antlr(v4)中编写解析器语法时,它会为您生成一些Java类,每个类对应一个解析器非终端。这些课程包括:
PowerShellParser.ForEachObjectStatementContext
用于定义
ForEach语句的解析器规则。对于一个合理大小的语法,你可以得到几十个(通常接近100个类)。它们是Java类层次结构的一部分,如下所示:

ParseTree
  |
  + - ErrorNode // used when there are errors in the program being compiled
  |
  + - TerminalNode // tokens, e.g. identifiers, '+', '=', whitespace, constants
  |
  * - RuleNode // non-terminals, subclassed further
        |
        + - ProgramContext  // for the Program rule/non-terminal
        |
        + - ForEachStatementContext // for the ForEach Statement rule
        |
        + - IfThenElseStatementContent // if (expr) then-clause else-clause?
        | 
        + - BlockStatementContext  // { statements* }
        |
        + - AssignmentStatementContext // var = expression
        |
        + - AddExpressionContext // expression + expression
        |
 
and literally many, many more.

public class Walker {

    public void walk(ParseTree t) {
        if (t instanceof ErrorNode) {
            listener.visitErrorNode((ErrorNode)t);
        } else if (t instanceof TerminalNode) {
            listener.visitTerminalNode((TerminalNode)t);
        } else if (t instanceof RuleNode) {
            RuleNode r = (RuleNode)t;
            ParserRuleContext ctx = 
                (ParserRuleContext)r.getRuleContext();
            listener.enterEveryRule(ctx);
            listener.enterRule(r);          // Here we call the listener
            int n = r.getChildCount();
            for (Integer i = 0; i < n; ++i) {
                ParseTree child = r.getChild(i);
                walk(child);                // Here we recursively call ourselves
                }  
             listener.exitRule(r);          // Here we call the listener again
        }
    }

antlr还生成一个“侦听器”模式,以便于为这些不同的代码位定义语义。此模式由上面的类树和两个函数组成,其中的默认实现可以更改。
walker
函数和
listener
函数。普通walker函数执行以下操作,调用侦听器的“enter(t)”函数,递归地遍历树中的子对象,并调用侦听器的exit(t)函数

原始antlr代码位于 大致如下:

ParseTree
  |
  + - ErrorNode // used when there are errors in the program being compiled
  |
  + - TerminalNode // tokens, e.g. identifiers, '+', '=', whitespace, constants
  |
  * - RuleNode // non-terminals, subclassed further
        |
        + - ProgramContext  // for the Program rule/non-terminal
        |
        + - ForEachStatementContext // for the ForEach Statement rule
        |
        + - IfThenElseStatementContent // if (expr) then-clause else-clause?
        | 
        + - BlockStatementContext  // { statements* }
        |
        + - AssignmentStatementContext // var = expression
        |
        + - AddExpressionContext // expression + expression
        |
 
and literally many, many more.

public class Walker {

    public void walk(ParseTree t) {
        if (t instanceof ErrorNode) {
            listener.visitErrorNode((ErrorNode)t);
        } else if (t instanceof TerminalNode) {
            listener.visitTerminalNode((TerminalNode)t);
        } else if (t instanceof RuleNode) {
            RuleNode r = (RuleNode)t;
            ParserRuleContext ctx = 
                (ParserRuleContext)r.getRuleContext();
            listener.enterEveryRule(ctx);
            listener.enterRule(r);          // Here we call the listener
            int n = r.getChildCount();
            for (Integer i = 0; i < n; ++i) {
                ParseTree child = r.getChild(i);
                walk(child);                // Here we recursively call ourselves
                }  
             listener.exitRule(r);          // Here we call the listener again
        }
    }

对于所有您关心的具有语义的类,依此类推。你可以走了 这些函数中的一些被禁用,默认情况下不执行任何操作

这对于“表达式”很好,因为如果将代码放在退出函数中 你得到了一个从下到上计算表达式的好方法,基本上是免费的

然而,这种遍历不适用于if语句或循环等,因为您不希望盲目遍历每个节点

因此,编写我继承的代码的人修改了walker代码,如下所示。它可以工作,但它有一层if语句,当我添加更多类型的语句时,我必须不断扩展。我希望对walker的递归调用能够像侦听器代码一样工作,调用与树中非终端类的“类型”匹配的版本,而不必在if语句中列出它们。在我的书中,检查物体的类型是一种“气味”。您只需编写具有正确签名的代码,它就会被调用。但是,递归调用并不像walker调用侦听器并传递对象那样发生这种情况。我想知道如何修复它


所以。因此,我最初的问题是:

然而,当walker递归地调用子类时,它不会选择特例版本,作者必须插入代码来检查类型并调用正确的类型。错误之处在于需要本规范中的ifs:

package walker;

import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.tree.*;

public class Walker {

    public void walk(ParseTree t) {
        // why do I need this if statement, why isn't the right function called?
        if (t instanceof ErrorNode) {
            walk((ErrorNode)t);
        } else if (t instanceof TerminalNode) {
            walk((TerminalNode)t);
        } else if (t instanceof RuleNode) {
            walk((RuleNode)t);
        } else {
            notImplemented("walk ParseTree for " + t);
        }
    }

    public void walk(ErrorNode t) {
        listener.visitErrorNode(t);
    }

    public void walk(TerminalNode t) {
        listener.visitTerminal(t);
    }

    public void walk(RuleNode r) {
        // same issue, only worse
        if (r instanceof PowerShellParser.ForEachObjectStatementContext) {
            walk((PowerShellParser.ForEachObjectStatementContext)r);
            return;
        } else if (r instanceof PowerShellParser.ForEachStatementContext) {
            walk((PowerShellParser.ForEachStatementContext)r);
            return;
        } else if (r instanceof PowerShellParser.IfStatementContext) {
            walk((PowerShellParser.IfStatementContext)r);
            return;
        } else if (r instanceof PowerShellParser.DoWhileStatementContext) {
            walk((PowerShellParser.DoWhileStatementContext)r);
            return;
        } else if (r instanceof PowerShellParser.HardCase1Context) {
            walk((PowerShellParser.HardCase1Context)r);
            return;
        } else if (r instanceof PowerShellParser.HardCase2Context) {
            walk((PowerShellParser.HardCase2Context)r);
            return;
        } else if (r instanceof PowerShellParser.HiddenStringMethodExpressionContext) {
            walk((PowerShellParser.HiddenStringMethodExpressionContext)r);
            return;
        } else if (r instanceof PowerShellParser.InvokeCommandCommandContext) {
            walk((PowerShellParser.InvokeCommandCommandContext)r);
            return;
        } else if (r instanceof PowerShellParser.ExpressionListContext) {
            walk((PowerShellParser.ExpressionListContext)r);
            return;
        } else if (r instanceof PowerShellParser.JoinWithExpressionListContext) {
            walk((PowerShellParser.JoinWithExpressionListContext)r);
            return;
        }

        // this is all I want this function to do, provide a default traversal
        // when I don't have a specialized version
        ParserRuleContext ctx = (ParserRuleContext)r.getRuleContext();
        listener.enterEveryRule(ctx);
        enterRule(r);

        int n = r.getChildCount();

        for (Integer i = 0; i < n; ++i) {
            ParseTree child = r.getChild(i);  // I presume the issue is here
            walk(child);
        }

        exitRule(r);
        listener.exitEveryRule(ctx);
    }

    public void walk(PowerShellParser.ForEachObjectStatementContext r) {
         // I want recursive calls of this object to call this directly
         // and not go through the two ifs above.
         // E.g. this version walks its children multiple times
         // The if version only walks some of its children
         // The hard cases actually do something like "eval"
    }

    public void walk(PowerShellParser.ForEachStatementContext r) {
    }

    public void walk(PowerShellParser.IfStatementContext r) {
    }

    public void walk(PowerShellParser.DoWhileStatementContext r) {
    }

    public void walk(PowerShellParser.JoinWithExpressionListContext r) {
    }

    public void walk(PowerShellParser.QuotedProgramContext r) {
    }

    public void walk(PowerShellParser.HardCase1Context r) {
    }

    public void walk(PowerShellParser.HardCase2Context r) {
    }

}

packagewalker;
导入org.antlr.v4.runtime.ParserRuleContext;
导入org.antlr.v4.runtime.tree.*;
公共班级步行者{
公共空地步行道(ParsetTree){
//为什么我需要这个if语句,为什么没有调用正确的函数?
if(错误节点的t实例){
步行((错误节点)t);
}else if(终端节点的t实例){
步行((终点)t);
}else if(规则节点的t实例){
步行((规则节点)t);
}否则{
未实现(“为“+t”遍历解析树);
}
}
公共空间步行(错误节点t){
visitErrorNode(t);
}
公共空地步行道(终点节点t){
listener.visiterminal(t);
}
公共空间漫游(规则节点r){
//同样的问题,只是更糟
if(PowerShellParser.ForEachObjectStatementContext的r实例){
walk((PowerShellParser.ForEachObjectStatementContext)r);
返回;
}else if(PowerShellParser.ForEachStatementContext的r实例){
walk((PowerShellParser.ForEachStatementContext)r);
返回;
}else if(PowerShellParser.IfStatementContext的r实例){
walk((PowerShellParser.IfStatementContext)r);
返回;
}else if(PowerShellParser.DoWhileStatementContext的r实例){
walk((PowerShellParser.DoWhileStatementContext)r);
返回;
}else if(PowerShellParser.HardCase1Context的r实例){
walk((powershell解析器.HardCase1Context)r);
返回;
}else if(PowerShellParser.HardCase2Context的r实例){
walk((powershell解析器.HardCase2Context)r);
返回;
}else if(PowerShellParser.HiddenStringMethodExpressionContext的r实例){
walk((PowerShellParser.HiddenStringMethodExpressionContext)r);
返回;
}else if(r PowerShellParser.InvokeCommandCommandContext的实例){
walk((PowerShellParser.InvokeCommandCommandContext)r);
返回;
}else if(PowerShellParser.ExpressionListContext的r实例){
walk((PowerShellParser.ExpressionListContext)r);
返回;
}else if(PowerShellParser.JoinWithExpressionListContext的r实例){
walk((PowerShellParser.JoinWithExpressionListContext)r);
返回;
}
//这就是我想要这个函数做的,提供一个默认遍历
//当我没有一个专门的版本
ParserRuleContext ctx=(ParserRuleContext)r.getRuleContext();
listener.enterEveryRule(ctx);
enterRule(r);
int n=r.getChildCount();
for(整数i=0;i