Skip to main content

wolf_ast/
dump.rs

1//! A span-free text form of the tree, for snapshot tests and `wolf check --emit ast`.
2//!
3//! Each node becomes `(head child child)`. A node that does not fit on one line is printed with
4//! its head on a line and its children indented below it.
5
6use crate::*;
7
8/// Renders a file as an indented tree without spans.
9pub fn dump(file: &File) -> String {
10    let mut out = String::new();
11    render(&file_tree(file), 0, &mut out);
12    out
13}
14
15const WIDTH: usize = 100;
16
17struct Tree {
18    head: String,
19    kids: Vec<Tree>,
20}
21
22fn leaf(head: impl Into<String>) -> Tree {
23    Tree {
24        head: head.into(),
25        kids: Vec::new(),
26    }
27}
28
29fn node(head: impl Into<String>, kids: Vec<Tree>) -> Tree {
30    Tree {
31        head: head.into(),
32        kids,
33    }
34}
35
36/// A list-like node; when it is empty it prints as `empty` so it cannot be confused with a name.
37fn group(head: &str, empty: &str, kids: Vec<Tree>) -> Tree {
38    if kids.is_empty() {
39        leaf(empty)
40    } else {
41        node(head, kids)
42    }
43}
44
45fn flat(tree: &Tree, out: &mut String) {
46    if tree.kids.is_empty() {
47        out.push_str(&tree.head);
48        return;
49    }
50    out.push('(');
51    out.push_str(&tree.head);
52    for kid in &tree.kids {
53        out.push(' ');
54        flat(kid, out);
55    }
56    out.push(')');
57}
58
59fn render(tree: &Tree, indent: usize, out: &mut String) {
60    let mut line = String::new();
61    flat(tree, &mut line);
62    out.push_str(&" ".repeat(indent));
63    if tree.kids.is_empty() || indent + line.len() <= WIDTH {
64        out.push_str(&line);
65        out.push('\n');
66        return;
67    }
68    out.push_str(&tree.head);
69    out.push('\n');
70    for kid in &tree.kids {
71        render(kid, indent + 2, out);
72    }
73}
74
75fn path(p: &Path) -> String {
76    p.segments
77        .iter()
78        .map(|s| s.name.as_str())
79        .collect::<Vec<_>>()
80        .join(".")
81}
82
83fn docs(list: &[Doc], kids: &mut Vec<Tree>) {
84    for doc in list {
85        let kind = if doc.inner { "doc!" } else { "doc" };
86        kids.push(leaf(format!("{kind} {:?}", doc.text)));
87    }
88}
89
90fn flag(on: bool, name: &str, kids: &mut Vec<Tree>) {
91    if on {
92        kids.push(leaf(name));
93    }
94}
95
96fn file_tree(file: &File) -> Tree {
97    let mut kids = Vec::new();
98    docs(&file.docs, &mut kids);
99    match &file.module {
100        Some(p) => kids.push(leaf(format!("module {}", path(p)))),
101        None => kids.push(leaf("module <missing>")),
102    }
103    for section in &file.sections {
104        let items = section.items.iter().map(item).collect();
105        kids.push(node(format!("section {}", section.kind.name()), items));
106    }
107    node("file", kids)
108}
109
110fn generics(list: &[GenericParam], kids: &mut Vec<Tree>) {
111    if list.is_empty() {
112        return;
113    }
114    let params = list
115        .iter()
116        .map(|g| {
117            if g.bounds.is_empty() {
118                leaf(g.name.name.clone())
119            } else {
120                node(g.name.name.clone(), g.bounds.iter().map(ty).collect())
121            }
122        })
123        .collect();
124    kids.push(node("generics", params));
125}
126
127fn item(item: &Item) -> Tree {
128    let mut kids = Vec::new();
129    docs(&item.docs, &mut kids);
130    let head = match &item.kind {
131        ItemKind::Import(import) => {
132            flag(import.public, "pub", &mut kids);
133            kids.push(leaf(path(&import.path)));
134            if let Some(alias) = &import.alias {
135                kids.push(leaf(format!("as {}", alias.name)));
136            }
137            if let Some(names) = &import.names {
138                let group = names
139                    .iter()
140                    .map(|n| match &n.alias {
141                        Some(alias) => leaf(format!("{} as {}", n.name.name, alias.name)),
142                        None => leaf(n.name.name.clone()),
143                    })
144                    .collect();
145                kids.push(node("names", group));
146            }
147            "import".to_string()
148        }
149        ItemKind::ExternTarget(target) => {
150            for decl in &target.decls {
151                let mut d = Vec::new();
152                docs(&decl.docs, &mut d);
153                flag(decl.public, "pub", &mut d);
154                let head = match &decl.kind {
155                    ExternDeclKind::Fn(s) => {
156                        sig(s, &mut d);
157                        "fn"
158                    }
159                    ExternDeclKind::Type(name) => {
160                        d.push(leaf(name.name.clone()));
161                        "type"
162                    }
163                    ExternDeclKind::Struct(name) => {
164                        d.push(leaf(name.name.clone()));
165                        "struct"
166                    }
167                };
168                d.push(leaf(format!("from {:?}", decl.from.value)));
169                kids.push(node(head, d));
170            }
171            format!("extern {}", target.target.name)
172        }
173        ItemKind::Config(entry) => {
174            flag(entry.public, "pub", &mut kids);
175            kids.push(leaf(entry.name.name.clone()));
176            kids.push(ty(&entry.ty));
177            kids.push(expr(&entry.value));
178            "const".to_string()
179        }
180        ItemKind::Struct(decl) => {
181            flag(decl.public, "pub", &mut kids);
182            kids.push(leaf(decl.name.name.clone()));
183            generics(&decl.generics, &mut kids);
184            kids.extend(decl.fields.iter().map(field));
185            "struct".to_string()
186        }
187        ItemKind::Enum(decl) => {
188            flag(decl.public, "pub", &mut kids);
189            kids.push(leaf(decl.name.name.clone()));
190            generics(&decl.generics, &mut kids);
191            for v in &decl.variants {
192                let mut vk = Vec::new();
193                docs(&v.docs, &mut vk);
194                vk.push(leaf(v.name.name.clone()));
195                match &v.payload {
196                    VariantPayload::None => {}
197                    VariantPayload::Tuple(types) => {
198                        vk.push(node("tuple", types.iter().map(ty).collect()))
199                    }
200                    VariantPayload::Struct(fields) => {
201                        vk.push(node("fields", fields.iter().map(field).collect()))
202                    }
203                }
204                kids.push(node("variant", vk));
205            }
206            "enum".to_string()
207        }
208        ItemKind::Alias(decl) => {
209            flag(decl.public, "pub", &mut kids);
210            kids.push(leaf(decl.name.name.clone()));
211            generics(&decl.generics, &mut kids);
212            kids.push(ty(&decl.ty));
213            "alias".to_string()
214        }
215        ItemKind::IntrinsicType(decl) => {
216            flag(decl.public, "pub", &mut kids);
217            kids.push(leaf(decl.name.name.clone()));
218            generics(&decl.generics, &mut kids);
219            "intrinsic type".to_string()
220        }
221        ItemKind::Interface(decl) => {
222            flag(decl.public, "pub", &mut kids);
223            kids.push(leaf(decl.name.name.clone()));
224            generics(&decl.generics, &mut kids);
225            for m in &decl.methods {
226                let mut mk = Vec::new();
227                docs(&m.docs, &mut mk);
228                flag(m.decl.overrides.is_some(), "override", &mut mk);
229                sig(&m.decl.sig, &mut mk);
230                match &m.decl.body {
231                    Some(body) => {
232                        mk.push(block(body));
233                        kids.push(node("default", mk));
234                    }
235                    None => kids.push(node("sig", mk)),
236                }
237            }
238            "interface".to_string()
239        }
240        ItemKind::Impl(block) => {
241            kids.push(leaf(block.type_name.name.clone()));
242            generics(&block.generics, &mut kids);
243            if let Some(interface) = &block.interface {
244                kids.push(node("for", vec![ty(interface)]));
245            }
246            for m in &block.methods {
247                let mut mk = Vec::new();
248                docs(&m.docs, &mut mk);
249                fn_decl(&m.decl, &mut mk);
250                kids.push(node("fn", mk));
251            }
252            "impl".to_string()
253        }
254        ItemKind::Fn(decl) => {
255            fn_decl(decl, &mut kids);
256            "fn".to_string()
257        }
258        ItemKind::Test(test) => {
259            kids.push(leaf(format!("{:?}", test.name.value)));
260            kids.push(block(&test.body));
261            "test".to_string()
262        }
263    };
264    node(head, kids)
265}
266
267fn field(f: &Field) -> Tree {
268    let mut kids = Vec::new();
269    docs(&f.docs, &mut kids);
270    flag(f.public, "pub", &mut kids);
271    kids.push(leaf(f.name.name.clone()));
272    kids.push(ty(&f.ty));
273    node("field", kids)
274}
275
276fn fn_decl(decl: &FnDecl, kids: &mut Vec<Tree>) {
277    flag(decl.public, "pub", kids);
278    flag(decl.intrinsic, "intrinsic", kids);
279    flag(decl.overrides.is_some(), "override", kids);
280    sig(&decl.sig, kids);
281    if let Some(body) = &decl.body {
282        kids.push(block(body));
283    }
284}
285
286fn mode(m: Mode) -> &'static str {
287    match m {
288        Mode::Read => "",
289        Mode::Mut => "mut ",
290        Mode::Own => "own ",
291    }
292}
293
294fn sig(s: &FnSig, kids: &mut Vec<Tree>) {
295    kids.push(leaf(s.name.name.clone()));
296    generics(&s.generics, kids);
297    let params = s
298        .params
299        .iter()
300        .map(|p| match &p.kind {
301            ParamKind::SelfParam => leaf(format!("{}self", mode(p.mode))),
302            ParamKind::Named { name, ty: t } => {
303                node(format!("{}{}", mode(p.mode), name.name), vec![ty(t)])
304            }
305        })
306        .collect();
307    kids.push(group("params", "()", params));
308    if let Some(ret) = &s.ret {
309        kids.push(node("->", vec![ty(ret)]));
310    }
311}
312
313fn ty(t: &Ty) -> Tree {
314    match &t.kind {
315        TyKind::Path { path: p, args } => {
316            if args.is_empty() {
317                leaf(path(p))
318            } else {
319                node(path(p), args.iter().map(ty).collect())
320            }
321        }
322        TyKind::Fn { params, ret } => {
323            let mut kids: Vec<Tree> = params
324                .iter()
325                .map(|(m, t)| {
326                    if *m == Mode::Read {
327                        ty(t)
328                    } else {
329                        node(mode(*m).trim(), vec![ty(t)])
330                    }
331                })
332                .collect();
333            if let Some(ret) = ret {
334                kids.push(node("->", vec![ty(ret)]));
335            }
336            node("fn", kids)
337        }
338        TyKind::SelfType => leaf("Self"),
339        TyKind::Error => leaf("<error>"),
340    }
341}
342
343fn block(b: &Block) -> Tree {
344    group("block", "{}", b.stmts.iter().map(stmt).collect())
345}
346
347fn stmt(s: &Stmt) -> Tree {
348    match &s.kind {
349        StmtKind::Let {
350            mutable,
351            name,
352            ty: t,
353            value,
354        } => {
355            let mut kids = vec![leaf(name.as_ref().map_or("_", |n| n.name.as_str()))];
356            if let Some(t) = t {
357                kids.push(ty(t));
358            }
359            kids.push(expr(value));
360            node(if *mutable { "var" } else { "let" }, kids)
361        }
362        StmtKind::LetPattern {
363            mutable,
364            pattern,
365            value,
366        } => node(
367            if *mutable { "var" } else { "let" },
368            vec![pat(pattern), expr(value)],
369        ),
370        StmtKind::Assign { target, op, value } => {
371            node(assign_symbol(*op), vec![expr(target), expr(value)])
372        }
373        StmtKind::While { label, cond, body } => {
374            labelled(label, node("while", vec![expr(cond), block(body)]))
375        }
376        StmtKind::WhileLet {
377            label,
378            pattern,
379            mode: m,
380            value,
381            body,
382        } => labelled(
383            label,
384            node(
385                "while let",
386                vec![pat(pattern), moded(*m, expr(value)), block(body)],
387            ),
388        ),
389        StmtKind::For {
390            label,
391            var,
392            iter,
393            body,
394        } => {
395            let iter = match iter {
396                ForIter::Collection { mode: m, expr: e } => {
397                    if *m == Mode::Read {
398                        expr(e)
399                    } else {
400                        node(mode(*m).trim(), vec![expr(e)])
401                    }
402                }
403                ForIter::Range {
404                    start,
405                    end,
406                    inclusive,
407                } => node(
408                    if *inclusive { "..=" } else { ".." },
409                    vec![expr(start), expr(end)],
410                ),
411            };
412            labelled(
413                label,
414                node("for", vec![leaf(var.name.clone()), iter, block(body)]),
415            )
416        }
417        StmtKind::Expr(e) => expr(e),
418    }
419}
420
421/// A loop with its label, if it has one.
422fn labelled(label: &Option<Ident>, t: Tree) -> Tree {
423    match label {
424        Some(l) => node(format!("{}:", l.name), vec![t]),
425        None => t,
426    }
427}
428
429/// A value with its mode marker, if it has one.
430fn moded(m: Mode, t: Tree) -> Tree {
431    if m == Mode::Read {
432        t
433    } else {
434        node(mode(m).trim(), vec![t])
435    }
436}
437
438fn assign_symbol(op: AssignOp) -> &'static str {
439    match op {
440        AssignOp::Assign => "=",
441        AssignOp::Add => "+=",
442        AssignOp::Sub => "-=",
443        AssignOp::Mul => "*=",
444        AssignOp::Div => "/=",
445        AssignOp::Rem => "%=",
446        AssignOp::BitAnd => "&=",
447        AssignOp::BitOr => "|=",
448        AssignOp::BitXor => "^=",
449        AssignOp::Shl => "<<=",
450        AssignOp::Shr => ">>=",
451    }
452}
453
454fn expr(e: &Expr) -> Tree {
455    match &e.kind {
456        ExprKind::Int(v) => leaf(v.to_string()),
457        ExprKind::Float(v) => leaf(format!("{v:?}")),
458        ExprKind::Str(v) => leaf(format!("{v:?}")),
459        ExprKind::FStr(parts) => node(
460            "f",
461            parts
462                .iter()
463                .map(|part| match part {
464                    FStrPart::Text(t) => leaf(format!("{t:?}")),
465                    FStrPart::Expr(e, None) => expr(e),
466                    FStrPart::Expr(e, Some(spec)) => {
467                        node(format!("format {:?}", spec.text()), vec![expr(e)])
468                    }
469                })
470                .collect(),
471        ),
472        ExprKind::Bool(v) => leaf(v.to_string()),
473        ExprKind::Unit => leaf("()"),
474        ExprKind::Name(n) => leaf(n.name.clone()),
475        ExprKind::SelfValue => leaf("self"),
476        ExprKind::SelfType => leaf("Self"),
477        ExprKind::Paren(inner) => node("paren", vec![expr(inner)]),
478        ExprKind::List(items) => group("list", "[]", items.iter().map(expr).collect()),
479        ExprKind::Map(entries) => group(
480            "map",
481            "[:]",
482            entries
483                .iter()
484                .map(|(k, v)| node(":", vec![expr(k), expr(v)]))
485                .collect(),
486        ),
487        ExprKind::StructLit {
488            path: p,
489            type_args,
490            fields,
491            base,
492        } => {
493            let mut kids = vec![leaf(path(p))];
494            if !type_args.is_empty() {
495                kids.push(node(".<>", type_args.iter().map(ty).collect()));
496            }
497            for f in fields {
498                kids.push(match &f.value {
499                    Some(v) => node(format!("{}:", f.name.name), vec![expr(v)]),
500                    None => leaf(f.name.name.clone()),
501                });
502            }
503            if let Some(base) = base {
504                kids.push(node("..", vec![expr(base)]));
505            }
506            node("struct", kids)
507        }
508        ExprKind::Field { base, name } => node(".", vec![expr(base), leaf(name.name.clone())]),
509        ExprKind::TypeArgs { base, args } => {
510            let mut kids = vec![expr(base)];
511            kids.extend(args.iter().map(ty));
512            node(".<>", kids)
513        }
514        ExprKind::Call { callee, args } => {
515            let mut kids = vec![expr(callee)];
516            for a in args {
517                kids.push(if a.mode == Mode::Read {
518                    expr(&a.value)
519                } else {
520                    node(mode(a.mode).trim(), vec![expr(&a.value)])
521                });
522            }
523            node("call", kids)
524        }
525        ExprKind::Index { base, index } => node("index", vec![expr(base), expr(index)]),
526        ExprKind::Slice {
527            base,
528            start,
529            end,
530            inclusive,
531        } => {
532            let bound = |b: &Option<Box<Expr>>| b.as_ref().map_or(leaf("_"), |e| expr(e));
533            node(
534                if *inclusive { "slice..=" } else { "slice.." },
535                vec![expr(base), bound(start), bound(end)],
536            )
537        }
538        ExprKind::Try(inner) => node("?", vec![expr(inner)]),
539        ExprKind::Unary { op, operand } => {
540            let symbol = match op {
541                UnaryOp::Neg => "neg",
542                UnaryOp::Not => "!",
543                UnaryOp::BitNot => "~",
544            };
545            node(symbol, vec![expr(operand)])
546        }
547        ExprKind::Binary { op, lhs, rhs } => node(op.symbol(), vec![expr(lhs), expr(rhs)]),
548        ExprKind::Closure { params, ret, body } => {
549            let params = params
550                .iter()
551                .map(|p| match &p.ty {
552                    Some(t) => node(p.name.name.clone(), vec![ty(t)]),
553                    None => leaf(p.name.name.clone()),
554                })
555                .collect();
556            let mut kids = vec![group("params", "||", params)];
557            if let Some(ret) = ret {
558                kids.push(node("->", vec![ty(ret)]));
559            }
560            kids.push(expr(body));
561            node("closure", kids)
562        }
563        ExprKind::Block(b) => block(b),
564        ExprKind::If {
565            cond,
566            then,
567            otherwise,
568        } => {
569            let mut kids = vec![expr(cond), block(then)];
570            if let Some(other) = otherwise {
571                kids.push(node("else", vec![expr(other)]));
572            }
573            node("if", kids)
574        }
575        ExprKind::IfLet {
576            pattern,
577            mode: m,
578            value,
579            then,
580            otherwise,
581        } => {
582            let mut kids = vec![pat(pattern), moded(*m, expr(value)), block(then)];
583            if let Some(other) = otherwise {
584                kids.push(node("else", vec![expr(other)]));
585            }
586            node("if let", kids)
587        }
588        ExprKind::Loop { label, body } => labelled(label, node("loop", vec![block(body)])),
589        ExprKind::Match {
590            mode: m,
591            scrutinee,
592            arms,
593        } => {
594            let mut kids = vec![if *m == Mode::Read {
595                expr(scrutinee)
596            } else {
597                node(mode(*m).trim(), vec![expr(scrutinee)])
598            }];
599            for arm in arms {
600                let mut ak = vec![pat(&arm.pattern)];
601                if let Some(guard) = &arm.guard {
602                    ak.push(node("if", vec![expr(guard)]));
603                }
604                ak.push(expr(&arm.body));
605                kids.push(node("=>", ak));
606            }
607            node("match", kids)
608        }
609        ExprKind::Return(value) => match value {
610            Some(v) => node("return", vec![expr(v)]),
611            None => leaf("return"),
612        },
613        ExprKind::Break { label, value } => {
614            let mut kids = Vec::new();
615            if let Some(l) = label {
616                kids.push(leaf(format!("{}:", l.name)));
617            }
618            if let Some(v) = value {
619                kids.push(expr(v));
620            }
621            if kids.is_empty() {
622                leaf("break")
623            } else {
624                node("break", kids)
625            }
626        }
627        ExprKind::Continue { label } => match label {
628            Some(l) => node("continue", vec![leaf(format!("{}:", l.name))]),
629            None => leaf("continue"),
630        },
631        ExprKind::Error => leaf("<error>"),
632    }
633}
634
635fn pat(p: &Pat) -> Tree {
636    match &p.kind {
637        PatKind::Wild => leaf("_"),
638        PatKind::Name(n) => leaf(n.name.clone()),
639        PatKind::Int { negative, value } => {
640            leaf(format!("{}{}", if *negative { "-" } else { "" }, value))
641        }
642        PatKind::Str(s) => leaf(format!("{s:?}")),
643        PatKind::Bool(b) => leaf(b.to_string()),
644        PatKind::Path(p) => leaf(path(p)),
645        PatKind::Tuple { path: p, items } => node(path(p), items.iter().map(pat).collect()),
646        PatKind::Struct {
647            path: p,
648            fields,
649            rest,
650        } => {
651            let mut kids: Vec<Tree> = fields
652                .iter()
653                .map(|f| match &f.pat {
654                    Some(sub) => node(format!("{}:", f.name.name), vec![pat(sub)]),
655                    None => leaf(f.name.name.clone()),
656                })
657                .collect();
658            if *rest {
659                kids.push(leaf(".."));
660            }
661            node(format!("{} {{}}", path(p)), kids)
662        }
663        PatKind::Or(alts) => node("|", alts.iter().map(pat).collect()),
664        PatKind::Error => leaf("<error>"),
665    }
666}