1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
module Lox
  class AstPrinter
    def print(expr) = expr.accept(self)

    def visit_binary(expr)   = parenthesize(expr.op.lexeme, expr.left, expr.right)
    def visit_grouping(expr) = parenthesize("group", expr.expr)
    def visit_literal(expr)  = expr.value&.to_s || "nil"
    def visit_unary(expr)    = parenthesize(expr.op.lexeme, expr.right)

    def visit_print(expr)    = parenthesize("print", expr.expr)

    def visit_var(expr)
      exprs = [expr.initializer].reject(&:nil?)
      parenthesize("var #{expr.name.lexeme}", *exprs)
    end

    def visit_variable(expr)
      "(var #{expr.name.lexeme})"
    end

    def visit_assign(expr)
      parenthesize("assign #{expr.name.lexeme}", expr.value)
    end

    def visit_block(expr)
      parenthesize("block", *expr.stmts)
    end

    def visit_if(stmt)
      exprs = [stmt.cond, stmt.then]
      exprs << stmt.else if stmt.else
      parenthesize("if", *exprs)
    end

    def visit_expr(expr)
      expr.expr.accept(self)
    end

    def visit_call(call)
      parenthesize(call.callee.accept(self), *call.args)
    end

    def visit_return(stmt)
      exprs = stmt.value ? [stmt.value] : []
      parenthesize("return", *exprs)
    end

    def visit_while(stmt)
      parenthesize("while", stmt.cond, stmt.body)
    end

    def visit_class(stmt)
      exprs = [stmt.superclass, *stmt.methods].compact
      parenthesize("class #{stmt.name.lexeme}", *exprs)
    end

    def visit_function(stmt)
      parenthesize("function #{stmt.name.lexeme}", *stmt.body)
    end

    private

    def parenthesize(name, *exprs)
      "(#{[name, *exprs.map { _1.accept(self) }].join(" ")})"
    end
  end
end