У меня есть следующее определение 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"))
(Комментарии и места удалены)
Мне интересно, как я могу сопоставить эти узлы, а затем получить доступ к их дочерним элементам.
Благодарность!
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;
}
Стандартный способ сделать это - сопоставить фактические имена конструкторов. Например, вы можете написать такой оператор переключения:
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;
}
Похожие вопросы
Связанные вопросы
Новые вопросы
pattern-matching
Используйте этот тег для вопросов о тестировании, имеет ли структура данных определенную форму или содержит определенные значения в определенных местах. Многие функциональные языки предоставляют конструкции сопоставления с образцом. Большинство вопросов в этом теге также должны иметь тег для языка, на котором вы программируете. НЕ ИСПОЛЬЗУЙТЕ ЭТУ ТЕГ ДЛЯ РЕГУЛЯРНЫХ ВОПРОСОВ ВЫРАЖЕНИЯ, ИСПОЛЬЗУЙТЕ [regex] INSTEAD; аналогично, для сопоставления с образцом (globbing) в POSIX-подобных оболочках используйте [glob].