У меня есть следующее определение 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_,_!");
        }
    }
}

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

Последний действительно работает ... но не дает мне доступа к дочерним элементам узла. Если я распечатаю exp, который используется в операторе switch, я получу:

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

(Комментарии и места удалены)

Мне интересно, как я могу сопоставить эти узлы, а затем получить доступ к их дочерним элементам.

Благодарность!

0
josh 5 Фев 2015 в 20:25

2 ответа

Лучший ответ

Что ж, я нашел решение. Я создал узел базового случая (в данном случае \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;
}
0
josh 6 Фев 2015 в 16:30

Стандартный способ сделать это - сопоставить фактические имена конструкторов. Например, вы можете написать такой оператор переключения:

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;
}
0
TimWolla 6 Фев 2015 в 16:59