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