lib/std/lang/ast/printer.rad 22.1 KiB raw
1
//! AST pretty printer using S-expression syntax.
2
3
use std::io;
4
use std::lang::sexpr;
5
use std::lang::alloc;
6
use std::lang::types;
7
8
/// Return the symbol for a binary operator.
9
fn binOpName(op: super::BinaryOp) -> *[u8] {
10
    match op {
11
        case super::BinaryOp::Add => return "+",
12
        case super::BinaryOp::Sub => return "-",
13
        case super::BinaryOp::Mul => return "*",
14
        case super::BinaryOp::Div => return "/",
15
        case super::BinaryOp::Mod => return "%",
16
        case super::BinaryOp::BitAnd => return "&",
17
        case super::BinaryOp::BitOr => return "|",
18
        case super::BinaryOp::BitXor => return "^",
19
        case super::BinaryOp::Shl => return "<<",
20
        case super::BinaryOp::Shr => return ">>",
21
        case super::BinaryOp::Eq => return "==",
22
        case super::BinaryOp::Ne => return "<>",
23
        case super::BinaryOp::Lt => return "<",
24
        case super::BinaryOp::Gt => return ">",
25
        case super::BinaryOp::Lte => return "<=",
26
        case super::BinaryOp::Gte => return ">=",
27
        case super::BinaryOp::And => return "and",
28
        case super::BinaryOp::Or => return "or",
29
        case super::BinaryOp::Xor => return "xor",
30
    }
31
}
32
33
/// Return the symbol for a unary operator.
34
fn unOpName(op: super::UnaryOp) -> *[u8] {
35
    match op {
36
        case super::UnaryOp::Not => return "not",
37
        case super::UnaryOp::Neg => return "-",
38
        case super::UnaryOp::BitNot => return "~",
39
    }
40
}
41
42
/// Return the name for a builtin.
43
fn builtinName(kind: super::Builtin) -> *[u8] {
44
    match kind {
45
        case super::Builtin::SizeOf => return "@sizeOf",
46
        case super::Builtin::AlignOf => return "@alignOf",
47
        case super::Builtin::SliceOf => return "@sliceOf",
48
    }
49
}
50
51
/// Return the name for an integer type.
52
fn intTypeName(width: u8, sign: super::Signedness) -> *[u8] {
53
    if let case super::Signedness::Signed = sign {
54
        match width {
55
            case 1 => return "i8",
56
            case 2 => return "i16",
57
            case 4 => return "i32",
58
            case 8 => return "i64",
59
            else => panic,
60
        }
61
    } else {
62
        match width {
63
            case 1 => return "u8",
64
            case 2 => return "u16",
65
            case 4 => return "u32",
66
            case 8 => return "u64",
67
            else => panic,
68
        }
69
    }
70
}
71
72
/// Return the S-expression head for a pointer class.
73
fn pointerClassHead(
74
    class: types::PointerClass,
75
    ownedHead: *[u8],
76
    refHead: *[u8],
77
    unsafeHead: *[u8],
78
) -> *[u8] {
79
    match class {
80
        case types::PointerClass::Owned => return ownedHead,
81
        case types::PointerClass::Ref => return refHead,
82
        case types::PointerClass::Unsafe => return unsafeHead,
83
    }
84
}
85
86
/// Convert a type signature to an S-expression.
87
fn typeSigToExpr(a: *mut alloc::Arena, sig: super::TypeSig) -> sexpr::Expr {
88
    match sig {
89
        case super::TypeSig::Void => return sexpr::sym("void"),
90
        case super::TypeSig::Opaque => return sexpr::sym("opaque"),
91
        case super::TypeSig::Bool => return sexpr::sym("bool"),
92
        case super::TypeSig::Integer { width, sign } => return sexpr::sym(intTypeName(width, sign)),
93
        case super::TypeSig::Array { itemType, length } =>
94
            return sexpr::list(a, "array", &[toExpr(a, itemType), toExpr(a, length)]),
95
        case super::TypeSig::Slice { class, itemType, mutable } => {
96
            let head = pointerClassHead(class, "slice", "slice-ref", "unsafe-slice");
97
            return sexpr::list(a, head, &[sexpr::sym("mut"), toExpr(a, itemType)]) if mutable
98
                else sexpr::list(a, head, &[toExpr(a, itemType)]);
99
        }
100
        case super::TypeSig::Pointer { class, valueType, mutable } => {
101
            let head = pointerClassHead(class, "ptr", "ref", "unsafe-ptr");
102
            return sexpr::list(a, head, &[sexpr::sym("mut"), toExpr(a, valueType)]) if mutable
103
                else sexpr::list(a, head, &[toExpr(a, valueType)]);
104
        }
105
        case super::TypeSig::Optional { valueType } =>
106
            return sexpr::list(a, "?", &[toExpr(a, valueType)]),
107
        case super::TypeSig::Nominal(name) => return toExpr(a, name),
108
        case super::TypeSig::Record { fields, .. } =>
109
            return sexpr::list(a, "record", nodeListToExprs(a, &fields[..])),
110
        case super::TypeSig::Fn(sig) => {
111
            let mut ret = sexpr::sym("void");
112
            if let rt = sig.returnType {
113
                set ret = toExpr(a, rt);
114
            }
115
            return sexpr::list(a, "fn", &[sexpr::list(a, "params", nodeListToExprs(a, &sig.params[..])), ret]);
116
        }
117
        case super::TypeSig::TraitObject { class, traitName, mutable } => {
118
            let head = pointerClassHead(class, "obj", "obj-ref", "unsafe-obj");
119
            return sexpr::list(a, head, &[sexpr::sym("mut"), toExpr(a, traitName)]) if mutable
120
                else sexpr::list(a, head, &[toExpr(a, traitName)]);
121
        }
122
    }
123
}
124
125
/// Convert a node slice to a slice of expressions.
126
fn nodeListToExprs(a: *mut alloc::Arena, nodes: *[*super::Node]) -> *[sexpr::Expr] {
127
    if nodes.len == 0 {
128
        return &[];
129
    }
130
    let buf = try! sexpr::allocExprs(a, nodes.len as u32);
131
    for node, i in nodes {
132
        set buf[i] = toExpr(a, node);
133
    }
134
    return buf;
135
}
136
137
/// Convert optional attributes to an attribute list expression.
138
fn attributesToExpr(a: *mut alloc::Arena, attrs: ?super::Attributes) -> sexpr::Expr {
139
    let mut exprs: *[sexpr::Expr] = &[];
140
    if let list = attrs {
141
        set exprs = nodeListToExprs(a, &list.list[..]);
142
    }
143
    return sexpr::list(a, "attrs", exprs);
144
}
145
146
/// Convert an optional node to an expression, or return placeholder.
147
fn toExprOpt(a: *mut alloc::Arena, opt: ?*super::Node) -> sexpr::Expr {
148
    if let n = opt {
149
        return toExpr(a, n);
150
    }
151
    return sexpr::sym("_");
152
}
153
154
/// Convert an optional node to an expression, or return `Null`.
155
fn toExprOrNull(a: *mut alloc::Arena, opt: ?*super::Node) -> sexpr::Expr {
156
    if let n = opt {
157
        return toExpr(a, n);
158
    }
159
    return sexpr::Expr::Null;
160
}
161
162
/// Convert an optional guard.
163
fn guardExpr(a: *mut alloc::Arena, guard: ?*super::Node) -> sexpr::Expr {
164
    if let g = guard {
165
        return sexpr::list(a, "guard", &[toExpr(a, g)]);
166
    }
167
    return sexpr::Expr::Null;
168
}
169
170
/// Convert a list of match prongs to expressions.
171
fn prongListToExprs(a: *mut alloc::Arena, nodes: *[*super::Node]) -> *[sexpr::Expr] {
172
    if nodes.len == 0 {
173
        return &[];
174
    }
175
    let buf = try! sexpr::allocExprs(a, nodes.len as u32);
176
    for prong, i in nodes {
177
        match prong.value {
178
            case super::NodeValue::MatchProng(p) => {
179
                set buf[i] = prongToExpr(a, p);
180
            }
181
            else => {
182
                set buf[i] = sexpr::sym("<invalid>");
183
            }
184
        }
185
    }
186
    return buf;
187
}
188
189
/// Convert a match prong to an S-expression.
190
fn prongToExpr(a: *mut alloc::Arena, p: super::MatchProng) -> sexpr::Expr {
191
    match p.arm {
192
        case super::ProngArm::Case(patterns) => {
193
            return sexpr::block(a, "case", &[
194
                sexpr::list(a, "patterns", nodeListToExprs(a, &patterns[..])),
195
                guardExpr(a, p.guard)
196
            ], &[toExpr(a, p.body)]);
197
        }
198
        case super::ProngArm::Else => {
199
            return sexpr::block(a, "else", &[guardExpr(a, p.guard)], &[toExpr(a, p.body)]);
200
        }
201
        case super::ProngArm::Binding(pat) => {
202
            if let g = p.guard {
203
                return sexpr::block(a, "let", &[toExpr(a, pat), guardExpr(a, p.guard)], &[toExpr(a, p.body)]);
204
            }
205
            return sexpr::block(a, "bind", &[toExpr(a, pat)], &[toExpr(a, p.body)]);
206
        }
207
    }
208
}
209
210
/// Convert a record field declaration to an S-expression.
211
fn fieldToExpr(
212
    a: *mut alloc::Arena,
213
    field: ?*super::Node,
214
    type: *super::Node,
215
    value: ?*super::Node
216
) -> sexpr::Expr {
217
    return sexpr::list(a, ":", &[toExprOpt(a, field), toExpr(a, type), toExprOrNull(a, value)]);
218
}
219
220
/// Convert a list of record fields to expressions.
221
fn fieldListToExprs(a: *mut alloc::Arena, nodes: *[*super::Node]) -> *[sexpr::Expr] {
222
    if nodes.len == 0 {
223
        return &[];
224
    }
225
    let buf = try! sexpr::allocExprs(a, nodes.len as u32);
226
    for node, i in nodes {
227
        match node.value {
228
            case super::NodeValue::RecordField { field, type, value } => {
229
                set buf[i] = fieldToExpr(a, field, type, value);
230
            }
231
            else => {
232
                set buf[i] = sexpr::sym("<invalid>");
233
            }
234
        }
235
    }
236
    return buf;
237
}
238
239
/// Convert a union variant to an S-expression.
240
fn variantToExpr(a: *mut alloc::Arena, name: *super::Node, type: ?*super::Node) -> sexpr::Expr {
241
    return sexpr::list(a, "variant", &[toExpr(a, name), toExprOrNull(a, type)]);
242
}
243
244
/// Convert a list of union variants to expressions.
245
fn variantListToExprs(a: *mut alloc::Arena, nodes: *[*super::Node]) -> *[sexpr::Expr] {
246
    if nodes.len == 0 {
247
        return &[];
248
    }
249
    let buf = try! sexpr::allocExprs(a, nodes.len as u32);
250
    for node, i in nodes {
251
        match node.value {
252
            case super::NodeValue::UnionDeclVariant(v) => {
253
                set buf[i] = variantToExpr(a, v.name, v.type);
254
            }
255
            else => {
256
                set buf[i] = sexpr::sym("<invalid>");
257
            }
258
        }
259
    }
260
    return buf;
261
}
262
263
/// Convert an AST node to an S-expression.
264
export fn toExpr(a: *mut alloc::Arena, node: *super::Node) -> sexpr::Expr {
265
    match node.value {
266
        case super::NodeValue::Placeholder => return sexpr::sym("_"),
267
        case super::NodeValue::Nil => return sexpr::sym("nil"),
268
        case super::NodeValue::Undef => return sexpr::sym("undefined"),
269
        case super::NodeValue::Bool(v) => {
270
            if v {
271
                return sexpr::sym("true");
272
            }
273
            return sexpr::sym("false");
274
        }
275
        case super::NodeValue::Char(c) => return sexpr::Expr::Char(c),
276
        case super::NodeValue::String(s) => return sexpr::Expr::Str(s),
277
        case super::NodeValue::Ident(name) => return sexpr::sym(name),
278
        case super::NodeValue::Number(lit) => return sexpr::sym(lit.text),
279
        case super::NodeValue::Super => return sexpr::sym("super"),
280
        case super::NodeValue::Break => return sexpr::list(a, "break", &[]),
281
        case super::NodeValue::Continue => return sexpr::list(a, "continue", &[]),
282
        case super::NodeValue::Range(r) =>
283
            return sexpr::list(a, "range", &[toExprOpt(a, r.start), toExprOpt(a, r.end)]),
284
        case super::NodeValue::BinOp(b) =>
285
            return sexpr::list(a, binOpName(b.op), &[toExpr(a, b.left), toExpr(a, b.right)]),
286
        case super::NodeValue::UnOp(u) =>
287
            return sexpr::list(a, unOpName(u.op), &[toExpr(a, u.value)]),
288
        case super::NodeValue::Call(c) => {
289
            let buf = try! sexpr::allocExprs(a, c.args.len as u32 + 1);
290
            set buf[0] = toExpr(a, c.callee);
291
            for arg, i in c.args { set buf[i + 1] = toExpr(a, arg); }
292
            return sexpr::Expr::List { head: "call", tail: buf, multiline: false };
293
        }
294
        case super::NodeValue::BuiltinCall { kind, args } =>
295
            return sexpr::list(a, builtinName(kind), nodeListToExprs(a, &args[..])),
296
        case super::NodeValue::Subscript { container, index } =>
297
            return sexpr::list(a, "[]", &[toExpr(a, container), toExpr(a, index)]),
298
        case super::NodeValue::FieldAccess(acc) =>
299
            return sexpr::list(a, ".", &[toExpr(a, acc.parent), toExpr(a, acc.child)]),
300
        case super::NodeValue::ScopeAccess(acc) =>
301
            return sexpr::list(a, "::", &[toExpr(a, acc.parent), toExpr(a, acc.child)]),
302
        case super::NodeValue::AddressOf(addr) =>
303
            return sexpr::list(a, "&mut", &[toExpr(a, addr.target)]) if addr.mutable
304
                else sexpr::list(a, "&", &[toExpr(a, addr.target)]),
305
        case super::NodeValue::Deref(target) =>
306
            return sexpr::list(a, "deref", &[toExpr(a, target)]),
307
        case super::NodeValue::As(cast) =>
308
            return sexpr::list(a, "as", &[toExpr(a, cast.value), toExpr(a, cast.type)]),
309
        case super::NodeValue::ArrayLit(elems) =>
310
            return sexpr::list(a, "array", nodeListToExprs(a, &elems[..])),
311
        case super::NodeValue::ArrayRepeatLit(rep) =>
312
            return sexpr::list(a, "array-repeat", &[toExpr(a, rep.item), toExpr(a, rep.count)]),
313
        case super::NodeValue::RecordLit(lit) => {
314
            let mut total: u32 = lit.fields.len as u32;
315
            if let _ = lit.typeName {
316
                set total += 1;
317
            }
318
            let buf = try! sexpr::allocExprs(a, total);
319
            let mut idx: u32 = 0;
320
            if let tn = lit.typeName {
321
                set buf[idx] = toExpr(a, tn); set idx = idx + 1;
322
            }
323
            for field, i in lit.fields {
324
                set buf[idx + i] = toExpr(a, field);
325
            }
326
            return sexpr::Expr::List { head: "record-lit", tail: buf, multiline: lit.fields.len > 2 };
327
        }
328
        case super::NodeValue::RecordLitField(f) =>
329
            return sexpr::list(a, "field", &[toExprOpt(a, f.label), toExpr(a, f.value)]),
330
        case super::NodeValue::TypeSig(sig) => return typeSigToExpr(a, sig),
331
        case super::NodeValue::FnParam(p) =>
332
            return sexpr::list(a, "param", &[toExpr(a, p.name), toExpr(a, p.type)]),
333
        case super::NodeValue::Attribute(attr) => {
334
            match attr {
335
                case super::Attribute::Export => return sexpr::sym("@export"),
336
                case super::Attribute::Default => return sexpr::sym("@default"),
337
                case super::Attribute::Extern => return sexpr::sym("@extern"),
338
                case super::Attribute::Test => return sexpr::sym("@test"),
339
                case super::Attribute::Intrinsic => return sexpr::sym("@intrinsic"),
340
                case super::Attribute::Unsafe => return sexpr::sym("@unsafe"),
341
            }
342
        }
343
        case super::NodeValue::Try(t) => {
344
            let mut head = "try";
345
            if t.shouldPanic { set head = "try!"; }
346
            if t.catches.len > 0 {
347
                let catches = nodeListToExprs(a, &t.catches[..]);
348
                return sexpr::list(a, head, &[toExpr(a, t.expr), sexpr::block(a, "catches", &[], catches)]);
349
            }
350
            return sexpr::list(a, head, &[toExpr(a, t.expr)]);
351
        }
352
        case super::NodeValue::CatchClause(clause) => {
353
            let mut head = "catch";
354
            let mut children: [sexpr::Expr; 3] = undefined;
355
            let mut len: u32 = 0;
356
            if let b = clause.binding {
357
                set children[len] = toExpr(a, b);
358
                set len += 1;
359
            }
360
            if let t = clause.typeNode {
361
                set children[len] = toExpr(a, t);
362
                set len += 1;
363
            }
364
            set children[len] = toExpr(a, clause.body);
365
            set len += 1;
366
            return sexpr::list(a, head, &children[..len]);
367
        }
368
        case super::NodeValue::Block(blk) => {
369
            let children = nodeListToExprs(a, &blk.statements[..]);
370
            return sexpr::block(a, "block", &[], children);
371
        }
372
        case super::NodeValue::Unsafe(body) =>
373
            return sexpr::list(a, "unsafe", &[toExpr(a, body)]),
374
        case super::NodeValue::Let(decl) => {
375
            let mut head = "let";
376
            if decl.mutable { set head = "let-mut"; }
377
            return sexpr::list(a, head, &[
378
                toExpr(a, decl.ident),
379
                toExprOrNull(a, decl.type),
380
                toExpr(a, decl.value)
381
            ]);
382
        }
383
        case super::NodeValue::ConstDecl(decl) =>
384
            return sexpr::list(a, "constant", &[toExpr(a, decl.ident), toExpr(a, decl.type), toExpr(a, decl.value)]),
385
        case super::NodeValue::StaticDecl(decl) =>
386
            return sexpr::list(a, "static", &[toExpr(a, decl.ident), toExpr(a, decl.type), toExpr(a, decl.value)]),
387
        case super::NodeValue::Assign(a_) =>
388
            return sexpr::list(a, "assign", &[toExpr(a, a_.left), toExpr(a, a_.right)]),
389
        case super::NodeValue::Return { value } =>
390
            return sexpr::list(a, "return", &[toExprOrNull(a, value)]),
391
        case super::NodeValue::Throw { expr } =>
392
            return sexpr::list(a, "throw", &[toExpr(a, expr)]),
393
        case super::NodeValue::Panic { message } =>
394
            return sexpr::list(a, "panic", &[toExprOrNull(a, message)]),
395
        case super::NodeValue::Assert { condition, message } =>
396
            return sexpr::list(a, "assert", &[toExpr(a, condition), toExprOrNull(a, message)]),
397
        case super::NodeValue::If(c) =>
398
            return sexpr::block(a, "if", &[toExpr(a, c.condition)],
399
                &[toExpr(a, c.thenBranch), toExprOrNull(a, c.elseBranch)]),
400
        case super::NodeValue::IfLet(c) => {
401
            let label = "if-let-mut" if c.pattern.mutable else "if-let";
402
            return sexpr::block(a, label, &[
403
                toExpr(a, c.pattern.pattern),
404
                toExpr(a, c.pattern.scrutinee),
405
                guardExpr(a, c.pattern.guard)
406
            ], &[
407
                toExpr(a, c.thenBranch),
408
                toExprOrNull(a, c.elseBranch)
409
            ]);
410
        }
411
        case super::NodeValue::LetElse(l) => {
412
            let label = "let-mut-else" if l.pattern.mutable else "let-else";
413
            return sexpr::block(a, label, &[
414
                toExpr(a, l.pattern.pattern),
415
                toExpr(a, l.pattern.scrutinee),
416
                guardExpr(a, l.pattern.guard)
417
            ], &[toExpr(a, l.elseBranch)]);
418
        }
419
        case super::NodeValue::While(w) =>
420
            return sexpr::block(a, "while", &[
421
                toExpr(a, w.condition)
422
            ], &[
423
                toExpr(a, w.body),
424
                toExprOrNull(a, w.elseBranch)
425
            ]),
426
        case super::NodeValue::WhileLet(w) => {
427
            let label = "while-let-mut" if w.pattern.mutable else "while-let";
428
            return sexpr::block(a, label, &[
429
                toExpr(a, w.pattern.pattern),
430
                toExpr(a, w.pattern.scrutinee),
431
                guardExpr(a, w.pattern.guard)
432
            ], &[
433
                toExpr(a, w.body),
434
                toExprOrNull(a, w.elseBranch)
435
            ]);
436
        }
437
        case super::NodeValue::For(f) =>
438
            return sexpr::block(a, "for", &[
439
                toExpr(a, f.binding),
440
                toExprOrNull(a, f.index),
441
                toExpr(a, f.iterable)
442
            ], &[toExpr(a, f.body), toExprOrNull(a, f.elseBranch)]),
443
        case super::NodeValue::Loop { body } =>
444
            return sexpr::block(a, "loop", &[], &[toExpr(a, body)]),
445
        case super::NodeValue::Match(m) => {
446
            let children = prongListToExprs(a, &m.prongs[..]);
447
            return sexpr::block(a, "match", &[toExpr(a, m.subject)], children);
448
        }
449
        case super::NodeValue::MatchProng(p) => {
450
            return prongToExpr(a, p);
451
        }
452
        case super::NodeValue::FnDecl(f) => {
453
            let params = sexpr::list(a, "params", nodeListToExprs(a, &f.sig.params[..]));
454
            let ret = toExprOrNull(a, f.sig.returnType);
455
            if let body = f.body {
456
                return sexpr::block(a, "fn", &[toExpr(a, f.name), params, ret], &[toExpr(a, body)]);
457
            }
458
            return sexpr::list(a, "fn", &[toExpr(a, f.name), params, ret]);
459
        }
460
        case super::NodeValue::Mod(m) => return sexpr::list(a, "mod", &[toExpr(a, m.name)]),
461
        case super::NodeValue::Use(u_) => return sexpr::list(a, "use", &[toExpr(a, u_.path)]),
462
        case super::NodeValue::RecordDecl(r) => {
463
            let children = fieldListToExprs(a, &r.fields[..]);
464
            return sexpr::block(a, "record", &[toExpr(a, r.name)], children);
465
        }
466
        case super::NodeValue::RecordField { field, type, value } => {
467
            return fieldToExpr(a, field, type, value);
468
        }
469
        case super::NodeValue::UnionDecl(u_) => {
470
            let children = variantListToExprs(a, &u_.variants[..]);
471
            return sexpr::block(a, "union", &[toExpr(a, u_.name)], children);
472
        }
473
        case super::NodeValue::UnionDeclVariant(v) => {
474
            return variantToExpr(a, v.name, v.type);
475
        }
476
        case super::NodeValue::ExprStmt(e) => return toExpr(a, e),
477
        case super::NodeValue::TraitDecl { name, supertraits, methods, .. } => {
478
            let children = nodeListToExprs(a, &methods[..]);
479
            let supers = sexpr::list(a, "supertraits", nodeListToExprs(a, &supertraits[..]));
480
            return sexpr::block(a, "trait", &[toExpr(a, name), supers], children);
481
        }
482
        case super::NodeValue::TraitMethodSig { name, receiver, sig, attrs } => {
483
            let params = sexpr::list(a, "params", nodeListToExprs(a, &sig.params[..]));
484
            let ret = toExprOrNull(a, sig.returnType);
485
            let attributes = attributesToExpr(a, attrs);
486
            return sexpr::list(
487
                a,
488
                "methodSig",
489
                &[attributes, toExpr(a, receiver), toExpr(a, name), params, ret],
490
            );
491
        }
492
        case super::NodeValue::InstanceDecl { traitName, targetType, methods } => {
493
            let children = nodeListToExprs(a, &methods[..]);
494
            return sexpr::block(a, "instance", &[toExpr(a, traitName), toExpr(a, targetType)], children);
495
        }
496
        case super::NodeValue::MethodDecl {
497
            name, receiverName, receiverType, sig, body, attrs,
498
        } => {
499
            let params = sexpr::list(a, "params", nodeListToExprs(a, &sig.params[..]));
500
            let ret = toExprOrNull(a, sig.returnType);
501
            let attributes = attributesToExpr(a, attrs);
502
            return sexpr::block(
503
                a,
504
                "method",
505
                &[
506
                    attributes,
507
                    toExpr(a, receiverType),
508
                    toExpr(a, receiverName),
509
                    toExpr(a, name),
510
                    params,
511
                    ret,
512
                ],
513
                &[toExpr(a, body)],
514
            );
515
        }
516
        else => return sexpr::sym("?"),
517
    }
518
}
519
520
/// Dump the tree rooted at `root`, using the provided arena for allocation.
521
export fn printTree(root: *super::Node, arena: *mut alloc::Arena) {
522
    match root.value {
523
        case super::NodeValue::Block(blk) => {
524
            for stmt, i in blk.statements {
525
                sexpr::print(toExpr(arena, stmt), 0);
526
                if i < blk.statements.len - 1 { io::print("\n\n"); }
527
            }
528
            io::print("\n");
529
        }
530
        else => {
531
            sexpr::print(toExpr(arena, root), 0);
532
            io::print("\n");
533
        }
534
    }
535
}