0

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!

josh
  • 1,544
  • 2
  • 16
  • 27

2 Answers2

0

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;
}
TimWolla
  • 31,849
  • 8
  • 63
  • 96
Mark Hills
  • 1,028
  • 5
  • 4
  • Hmm...thanks @MarkHills. Is this just standard practice or is there actually no way to accomplish just a match on the `Exp` type? The reason I ask is I'm trying to compile these various things and the work that's going to be done in each case will, hopefully, be identical. If I could just match on `Exp` I think I would be in really good shape. – josh Feb 06 '15 at 14:28
  • Standard in the sense that you are actually using the constructors themselves, instead of just their arities, to drive the computation. – Mark Hills Feb 06 '15 at 17:21
0

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;
}
josh
  • 1,544
  • 2
  • 16
  • 27
  • If you don't need to use the constructor this will work fine. You should take a look at the `Node` library, which has a number of functions for working with constructed terms in very generic ways, like getting back the name of the constructor that was used as a string or getting back a value list representing the arguments. This is essentially what the pattern match you give above is doing: matching all unary nodes where the first argument is an `Exp`, or all binary nodes where both arguments are type `Exp`. – Mark Hills Feb 06 '15 at 17:27