问题
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