ANTLR:如何使用树语法替换子树中的特定节点?

ANTLR:如何使用树语法替换子树中的特定节点?,antlr,Antlr,我有一个ANTLR语法,它创建了一个AST,然后我编写了两个树语法,它们创建了树解析器,用于对AST进行两次传递,以便进行语义分析。(之后,我进行另一次传递,并使用StringTemplate生成输出代码) 到目前为止,一切都很好,但我正在尝试扩展该语言以支持函数中的参数多态性(而到目前为止,它只支持“简单”函数) 比如说,我想要这样的东西: T getMax<T> (T a, T b) { if (a > b) return a; return b; }

我有一个ANTLR语法,它创建了一个AST,然后我编写了两个树语法,它们创建了树解析器,用于对AST进行两次传递,以便进行语义分析。(之后,我进行另一次传递,并使用StringTemplate生成输出代码)

到目前为止,一切都很好,但我正在尝试扩展该语言以支持函数中的参数多态性(而到目前为止,它只支持“简单”函数)

比如说,我想要这样的东西:

T getMax<T>  (T a, T b) {
     if (a > b) return a;
   return b;
}
  • 在上面的示例中,用新创建的子树中的
    int
    替换所有出现的
    T
    类型的最佳方法是什么?我认为一个简单的重写规则是不够的,因为我还需要替换函数体中的所有类型。 我是否需要编写另一个树语法来处理这个函数声明子树并进行所有替换?手动操作容易吗

  • 抱歉,这太让人困惑了

    编辑

    假设您的输入是:

    T add<T> (T a, T b) { return a+b }
    add<int>(1, 2)
    add<string>('f', 'oo')
    
    删除并替换的子树如下所示:

    (METHOD_DECL T add (TYPEPARAMS T) (ARG_DECL T a) (ARG_DECL T b) (BLOCK (return (EXPR (+ (EXPR a) (EXPR b))))))
    

    另外,我想删除原始参数多态函数声明子树的原因是,我不知道在进行类型检查时如何忽略它。树解析器“自动”匹配所有二进制操作、返回语句、参数等,并执行类型检查。因此,如果我把它放在那里,它会报告错误,因为例如,T不是正确的类型,因此不能将它与+运算符一起使用。

    我个人认为,我会让解析器将方法与主代码块分开。根据输入:

    T添加(ta,tb){
    tc=a+b
    返回c
    }
    intsub(intx,inty){返回x-y}
    加(1、2)
    添加('f','oo')
    
    然后解析器将生成表示以下内容的树:

    添加(1,2)
    添加('f','oo')
    
    以及表示方法的单独树:

    intadd(inta,intb){intc=a+b返回c}
    stringadd(stringa,stringb){stringc=a+b返回c}
    intsub(intx,inty){返回x-y}
    
    在解析阶段,您只需跟踪两个实例变量中的所有参数化参数(pp)-调用和-methods:

    @parser::members{
    私有映射ppMethodCalls=newHashMap();
    私有映射ppMethods=newhashmap();
    //所有方法的单独AST
    公共CommonTree方法=新CommonTree(新CommonToken(方法,“方法”);
    // ...
    }
    
    解析示例代码后,
    ppMethodCalls
    将保持:

    {“add”=>{Token=“int”,Token=“string”}
    
    ppMethods
    将适用于:

    {“添加”=>Tree=^(方法T添加…)
    
    当解析器完全解析输入源时,将调用以下方法:

    public void replacePP(){
    //迭代所有pp方法调用
    对于(Map.Entry:ppMethodCalls.entrySet()){
    //获取正在调用的方法的名称
    字符串名称=entry.getKey();
    //迭代所有标记(在我的示例中为“int”和“string”)
    for(令牌tok:entry.getValue()){
    //获取原始pp方法实例
    CommonTree ppm=ppMethods.get(名称);
    //从原始pp方法创建深度副本(稍后将发布“copyTree(…)”)
    CommonTree cp=Main.copyTree(ppm);
    //从树中删除第一个令牌(在我的示例中为“T”)
    CommonTree pp=(CommonTree)cp.deleteChild(0);
    //用实际参数替换所有“T”标记(在我的示例中为“int”或“string”)
    更换(pp、tok、cp);
    //将重写的树添加到单独的“方法”树中
    方法:addChild(cp);
    }
    }
    }
    私有无效替换(CommonTree参数、令牌令牌、CommonTree树){
    if(tree==null)返回;
    if(tree.getType()==param.getType()&&tree.getText().equals(param.getText()){
    tree.getToken().setType(token.getType());
    tree.getToken().setText(token.getText());
    }
    if(tree.getChildCount()>0){
    for(对象子对象:tree.getChildren()){
    替换(参数、令牌、(CommonTree)子级);
    }
    }
    }
    
    这将为
    add(…)
    add(…)
    创建两个新树,并将它们添加到实例变量:
    methods:CommonTree

    一个小演示:

    档案:PP.g
    语法PP;
    选择权{
    输出=AST;
    ASTLabelType=CommonTree;
    }
    代币{
    根;
    方法;
    块
    分配
    精氨酸;
    ARGS;
    ELIST;
    方法;
    呼叫
    }
    @解析器::头{
    导入java.util.Map;
    导入java.util.HashMap;
    导入java.util.Set;
    导入java.util.HashSet;
    }
    @解析器::成员{
    私有映射ppMethodCalls=newHashMap();
    私有映射ppMethods=newhashmap();
    公共CommonTree方法=新CommonTree(新CommonToken(方法,“方法”);
    私有void ppCall(字符串名称、令牌pp){
    Set existing=ppMethodCalls.remove(名称);
    if(existing==null)existing=newhashset();
    新增(pp);
    ppMethodCalls.put(名称,现有);
    }
    公共文件{
    对于(Map.Entry:ppMethodCalls.entrySet()){
    字符串名称=entry.getKey();
    for(令牌tok:entry.getValue()){
    CommonTree ppm=ppMethods.get(名称);
    CommonTree cp=Main.copyTree(ppm);
    CommonTree pp=(CommonTree)cp.deleteChild(0);
    更换(pp、tok、cp);
    方法:addChild(cp);
    }
    }
    }
    私有无效替换(CommonTree参数、令牌令牌、CommonTree树){
    if(tree==null)返回;
    if(tree.getType()==param.getType()&&tree.getText().equals(param.getText()){
    tree.getToken().setType(token.getType());
    tree.getToken().setText(token.getText());
    }
    if(tree.getChildCount()>0){
    for(对象子对象:tree.getChildren()){
    替换(参数、令牌、(CommonTree)子级);
    
    (METHOD_DECL int add (ARG_DECL int a) (ARG_DECL int b) (BLOCK (return (EXPR (+ (EXPR a) (EXPR b)))))) 
    (METHOD_DECL string add (ARG_DECL string a) (ARG_DECL string b) (BLOCK (return (EXPR (+ (EXPR a) (EXPR b)))))) 
    (EXPR (CALL add (ELIST (EXPR 3) (EXPR 4)))) 
    (EXPR (CALL add (ELIST (EXPR "f") (EXPR "oo"))))
    
    (METHOD_DECL T add (TYPEPARAMS T) (ARG_DECL T a) (ARG_DECL T b) (BLOCK (return (EXPR (+ (EXPR a) (EXPR b))))))