Pattern matching AST nodes in Rascal

不问归期 提交于 2019-12-12 03:26:14

问题


I have the following AST definition:

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)
    ;

and I am trying to match a node of the tree in a switch statement such that I have access to each child. The things I've tried are:

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

and

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

and

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

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

The last one does work...but doesn't give me access to the children of the node. If I print out the exp that is being used in the switch statement I get:

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

(Comments and locations removed)

I'm wondering how I can match these nodes and then have access to their children.

Thanks!


回答1:


Well, I have found a solution. What I've done is created a base case node (in this case a \str()) node, then I match on any other generic type. So, this should catch either the base case and if not that means it must be some other type of Exp that I can then process. Code:

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;
}



回答2:


The standard way to do this is to match on the actual constructor names. For instance, you could write a switch statement like:

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...
}

You would then have one case per defined constructor. Another option is to write separate functions to handle the different cases, which can be cleaner in some cases (and is more extensible, since you can just add more functions):

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

Result eval(var(str name), Env env) {
    // evaluate var
}

The different cases are mutually recursive. If you have shared state that you don't want to pass around, I would advise nesting the functions instead of creating global variables:

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;
}


来源:https://stackoverflow.com/questions/28350261/pattern-matching-ast-nodes-in-rascal

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!