Довольно печатное выражение с максимально возможными скобками?
Мой вопрос: Каков самый чистый способ красивого вывода выражения без лишних круглых скобок?
У меня есть следующее представление лямбда-выражений:
Term ::= Fun(String x, Term t)
| App(Term t1, Term t2)
| Var(String x)
По соглашению App
остается ассоциативным, то есть a b c
интерпретируется как (a b) c
, а тела функций растягиваются как можно дальше вправо, т.е. λ x. x y
интерпретируется как λ x. (x y)
.
У меня есть синтаксический анализатор, который делает хорошую работу, но теперь Я хочу красивый принтер. Здесь у меня есть (псевдо scala):
term match {
case Fun(v, t) => "(λ %s.%s)".format(v, prettyPrint(t))
case App(s, t) => "(%s %s)".format(prettyPrint(s), prettyPrint(t))
case Var(v) => v
}
Вышеприведенный принтер всегда помещает (
)
вокруг выражений (кроме атомных переменных). Таким образом, для Fun(x, App(Fun(y, x), y))
он производит
(λ x.((λ y.x) y))
Я хотел бы иметь
λ x.(λ y.x) y
Ответы
Ответ 1
Разве вам не нужно проверять типы аргументов приложения?
Я не уверен, как записать это в scala..
term match {
case Fun(v: String, t: Term) => "λ %s.%s".format(v, prettyPrint(t))
case App(s: Fun, t: App) => "(%s) (%s)".format(prettyPrint(s), prettyPrint(t))
case App(s: Term, t: App) => "%s (%s)".format(prettyPrint(s), prettyPrint(t))
case App(s: Fun, t: Term) => "(%s) %s".format(prettyPrint(s), prettyPrint(t))
case App(s: Term, t: Term) => "%s %s".format(prettyPrint(s), prettyPrint(t))
case Var(v: String) => v
}
Ответ 2
Здесь я буду использовать простую грамматику для инфиксных выражений с ассоциативностью и приоритетом, определяемыми следующей грамматикой, операторы которой перечислены в порядке возрастания приоритета
E -> E + T | E - T | T left associative
T -> T * F | T / F | F left associative
F -> G ^ F | G right associative
G -> - G | ( E ) | NUM
Для абстрактного синтаксического дерева (AST) мы преобразуем AST в строку с только необходимой скобкой, как описано ниже в псевдокоде. Мы рассматриваем относительный приоритет и ассоциативность, когда мы рекурсивно спускаемся к дереву, чтобы определить, когда необходимы скобки. Обратите внимание, что все решения об обертке скобок вокруг выражения должны быть сделаны в родительском node.
toParenString(AST) {
if (AST.type == NUM) // simple atomic type (no operator)
return toString(AST)
else if (AST.TYPE == UNARY_MINUS) // prefix unary operator
if (AST.arg.type != NUM AND
precedence(AST.op) > precedence(AST.arg.op))
return "-(" + toParenString(AST.arg) + ")"
else
return "-" + toParenString(AST.arg)
else { // binary operation
var useLeftParen =
AST.leftarg.type != NUM AND
(precedence(AST.op) > precedence(AST.leftarg.op) OR
(precedence(AST.op) == precedence(AST.leftarg.op) AND
isRightAssociative(AST.op)))
var useRightParen =
AST.rightarg.type != NUM AND
(precedence(AST.op) > precedence(AST.rightarg.op) OR
(precedence(AST.op) == precedence(AST.rightarg.op) AND
isLeftAssociative(AST.op)))
var leftString;
if (useLeftParen) {
leftString = "(" + toParenString(AST.leftarg) + ")"
else
leftString = toParenString(AST.leftarg)
var rightString;
if (useRightParen) {
rightString = "(" + toParenString(AST.rightarg) + ")"
else
rightString = toParenString(AST.rightarg)
return leftString + AST.op + rightString;
}
}