【问题标题】:Pattern matching AST nodes in RascalRascal 中的模式匹配 AST 节点
【发布时间】:2015-02-05 17:25:08
【问题描述】:

我有以下 AST 定义:

data Exp =
    app(Exp fun, Exp body)
    | var(str name)
    | nat(int nat)
    | func(list[str] formal, Exp body)
    | cond(Exp cond, Exp then, list[tuple[Exp,Exp]] elifs, Exp otherwise)
    | let(list[str] vars, list[Exp] exps, Exp body)
    | seq(Exp lhs, Exp rhs)
    | mul(Exp lhs, Exp rhs)
    | div(Exp lhs, Exp rhs)
    | md(Exp lhs, Exp rhs)
    | add(Exp lhs, Exp rhs)
    | sub(Exp lhs, Exp rhs)
    | eq(Exp lhs, Exp rhs)
    | gt(Exp lhs, Exp rhs)
    | lt(Exp lhs, Exp rhs)
    | geq(Exp lhs, Exp rhs)
    | leq(Exp lhs, Exp rhs)
    ;

我正在尝试在 switch 语句中匹配树的一个节点,以便我可以访问每个孩子。我尝试过的事情是:

private str synthesise_f(Core::AST::Exp exp) {
    switch (exp) {
        case \Exp(_, _): {
            println("EXP_,_!");
        }
    }
}

private str synthesise_f(Core::AST::Exp exp) {
    switch (exp) {
        case /Exp(_, _): {
            println("EXP_,_!");
        }
    }
}

private str synthesise_f(Core::AST::Exp exp) {
    switch (exp) {
        case "Exp"(_, _): {
            println("EXP_,_!");
        }
    }
}

和 私有 str 综合_f(Core::AST::Exp exp){ 案例 \adt(,): { println("EXP_!"); } }

最后一个确实有效......但没有让我访问节点的子节点。如果我打印出switch 语句中使用的exp,我会得到:

seq(var("x"),var("y"))

(已删除评论和位置)

我想知道如何匹配这些节点,然后访问它们的子节点。

谢谢!

【问题讨论】:

    标签: pattern-matching abstract-syntax-tree rascal


    【解决方案1】:

    嗯,我找到了解决办法。我所做的是创建一个基本案例节点(在本例中为\str())节点,然后匹配任何其他泛型类型。因此,这应该捕获基本情况,如果不是,则意味着它必须是其他类型的Exp,然后我可以处理。代码:

    private str synthesise_f(Core::AST::Exp exp) {
        switch (exp) {
            case \var(_): {
                doSomethingWithStr();
            }
            case &T _(Exp e0): {
                doSomethingWith1Exp();
            }
            case &T _(Exp e0, Exp e1): {
                doSomethingWith2Exps();
            }
        }
        return ret;
    }
    

    【讨论】:

    • 如果您不需要使用构造函数,这将正常工作。您应该查看Node 库,它有许多函数可以以非常通用的方式处理构造的术语,例如取回用作字符串的构造函数的名称或取回表示论据。这基本上就是您在上面给出的模式匹配正在做的事情:匹配第一个参数为Exp 的所有一元节点,或两个参数均为Exp 类型的所有二进制节点。
    【解决方案2】:

    执行此操作的标准方法是匹配实际的构造函数名称。例如,您可以编写如下 switch 语句:

    switch(exp) {
        case app(f,b) : {
            // code to handle case app(Exp fun, Exp body) goes here
        }
    
        case var(n) : {
            // code to handle case var(str name) goes here
        }
    
        // etc...
    }
    

    然后,每个定义的构造函数都会有一个案例。另一种选择是编写单独的函数来处理不同的情况,这在某些情况下可以更简洁(并且更具可扩展性,因为您可以添加更多函数):

    Result eval(app(Exp fun, Exp body), Env env) {
        // evaluate app
    }
    
    Result eval(var(str name), Env env) {
        // evaluate var
    }
    

    不同的情况是相互递归的。如果您有不想传递的共享状态,我建议您嵌套函数而不是创建全局变量:

    Result evalProgram(Exp p) {
        Env env = createEnv();
    
        Result eval(app(Exp fun, Exp body)) {
            // evaluate app
        }
    
        Result eval(var(str name)) {
            // evaluate var
        }
    
        // The other functions...
    
        Result res = eval(p);
        return res;
    }
    

    【讨论】:

    • 嗯...谢谢@MarkHills。这只是标准做法还是实际上没有办法仅在 Exp 类型上完成匹配?我问的原因是我正在尝试编译这些不同的东西,并且在每种情况下将要完成的工作希望是相同的。如果我能在Exp 上比赛,我想我的状态会非常好。
    • 标准,因为您实际上是在使用构造函数本身,而不仅仅是它们的参数来驱动计算。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-11-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-18
    • 2017-12-07
    相关资源
    最近更新 更多