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            if !decl.supers.is_empty() {
226                kids.push(node(":", decl.supers.iter().map(ty).collect()));
227            }
228            for m in &decl.methods {
229                let mut mk = Vec::new();
230                docs(&m.docs, &mut mk);
231                flag(m.decl.overrides.is_some(), "override", &mut mk);
232                sig(&m.decl.sig, &mut mk);
233                match &m.decl.body {
234                    Some(body) => {
235                        mk.push(block(body));
236                        kids.push(node("default", mk));
237                    }
238                    None => kids.push(node("sig", mk)),
239                }
240            }
241            "interface".to_string()
242        }
243        ItemKind::Impl(block) => {
244            kids.push(leaf(block.type_name.name.clone()));
245            generics(&block.generics, &mut kids);
246            if let Some(interface) = &block.interface {
247                kids.push(node("for", vec![ty(interface)]));
248            }
249            for m in &block.methods {
250                let mut mk = Vec::new();
251                docs(&m.docs, &mut mk);
252                fn_decl(&m.decl, &mut mk);
253                kids.push(node("fn", mk));
254            }
255            "impl".to_string()
256        }
257        ItemKind::Fn(decl) => {
258            fn_decl(decl, &mut kids);
259            "fn".to_string()
260        }
261        ItemKind::Test(test) => {
262            kids.push(leaf(format!("{:?}", test.name.value)));
263            kids.push(block(&test.body));
264            "test".to_string()
265        }
266    };
267    node(head, kids)
268}
269
270fn field(f: &Field) -> Tree {
271    let mut kids = Vec::new();
272    docs(&f.docs, &mut kids);
273    flag(f.public, "pub", &mut kids);
274    kids.push(leaf(f.name.name.clone()));
275    kids.push(ty(&f.ty));
276    node("field", kids)
277}
278
279fn fn_decl(decl: &FnDecl, kids: &mut Vec<Tree>) {
280    flag(decl.public, "pub", kids);
281    flag(decl.intrinsic, "intrinsic", kids);
282    flag(decl.overrides.is_some(), "override", kids);
283    sig(&decl.sig, kids);
284    if let Some(body) = &decl.body {
285        kids.push(block(body));
286    }
287}
288
289fn mode(m: Mode) -> &'static str {
290    match m {
291        Mode::Read => "",
292        Mode::Mut => "mut ",
293        Mode::Own => "own ",
294    }
295}
296
297fn sig(s: &FnSig, kids: &mut Vec<Tree>) {
298    kids.push(leaf(s.name.name.clone()));
299    generics(&s.generics, kids);
300    let params = s
301        .params
302        .iter()
303        .map(|p| match &p.kind {
304            ParamKind::SelfParam => leaf(format!("{}self", mode(p.mode))),
305            ParamKind::Named { name, ty: t } => {
306                node(format!("{}{}", mode(p.mode), name.name), vec![ty(t)])
307            }
308        })
309        .collect();
310    kids.push(group("params", "()", params));
311    if let Some(ret) = &s.ret {
312        kids.push(node("->", vec![ty(ret)]));
313    }
314}
315
316fn ty(t: &Ty) -> Tree {
317    match &t.kind {
318        TyKind::Path { path: p, args } => {
319            if args.is_empty() {
320                leaf(path(p))
321            } else {
322                node(path(p), args.iter().map(ty).collect())
323            }
324        }
325        TyKind::Fn { params, ret } => {
326            let mut kids: Vec<Tree> = params
327                .iter()
328                .map(|(m, t)| {
329                    if *m == Mode::Read {
330                        ty(t)
331                    } else {
332                        node(mode(*m).trim(), vec![ty(t)])
333                    }
334                })
335                .collect();
336            if let Some(ret) = ret {
337                kids.push(node("->", vec![ty(ret)]));
338            }
339            node("fn", kids)
340        }
341        TyKind::SelfType => leaf("Self"),
342        TyKind::Error => leaf("<error>"),
343    }
344}
345
346fn block(b: &Block) -> Tree {
347    group("block", "{}", b.stmts.iter().map(stmt).collect())
348}
349
350fn stmt(s: &Stmt) -> Tree {
351    match &s.kind {
352        StmtKind::Let {
353            mutable,
354            name,
355            ty: t,
356            value,
357        } => {
358            let mut kids = vec![leaf(name.as_ref().map_or("_", |n| n.name.as_str()))];
359            if let Some(t) = t {
360                kids.push(ty(t));
361            }
362            kids.push(expr(value));
363            node(if *mutable { "var" } else { "let" }, kids)
364        }
365        StmtKind::LetPattern {
366            mutable,
367            pattern,
368            value,
369        } => node(
370            if *mutable { "var" } else { "let" },
371            vec![pat(pattern), expr(value)],
372        ),
373        StmtKind::Assign { target, op, value } => {
374            node(assign_symbol(*op), vec![expr(target), expr(value)])
375        }
376        StmtKind::While { label, cond, body } => {
377            labelled(label, node("while", vec![expr(cond), block(body)]))
378        }
379        StmtKind::WhileLet { label, items, body } => {
380            let mut kids = cond_items(items);
381            kids.push(block(body));
382            labelled(label, node("while let", kids))
383        }
384        StmtKind::For {
385            label,
386            var,
387            iter,
388            body,
389        } => {
390            let iter = match iter {
391                ForIter::Collection { mode: m, expr: e } => {
392                    if *m == Mode::Read {
393                        expr(e)
394                    } else {
395                        node(mode(*m).trim(), vec![expr(e)])
396                    }
397                }
398                ForIter::Range {
399                    start,
400                    end,
401                    inclusive,
402                } => node(
403                    if *inclusive { "..=" } else { ".." },
404                    vec![expr(start), expr(end)],
405                ),
406            };
407            labelled(
408                label,
409                node("for", vec![leaf(var.name.clone()), iter, block(body)]),
410            )
411        }
412        StmtKind::Expr(e) => expr(e),
413    }
414}
415
416/// A loop with its label, if it has one.
417fn labelled(label: &Option<Ident>, t: Tree) -> Tree {
418    match label {
419        Some(l) => node(format!("{}:", l.name), vec![t]),
420        None => t,
421    }
422}
423
424/// The items of the condition of an `if let` or a `while let`. One `let` alone is its pattern and
425/// value, as before chains; in a chain each `let` is a node of its own.
426fn cond_items(items: &[CondItem]) -> Vec<Tree> {
427    if let [
428        CondItem::Let {
429            pattern,
430            mode,
431            value,
432        },
433    ] = items
434    {
435        return vec![pat(pattern), moded(*mode, expr(value))];
436    }
437    items
438        .iter()
439        .map(|item| match item {
440            CondItem::Let {
441                pattern,
442                mode,
443                value,
444            } => node("let", vec![pat(pattern), moded(*mode, expr(value))]),
445            CondItem::Bool(e) => expr(e),
446        })
447        .collect()
448}
449
450/// A value with its mode marker, if it has one.
451fn moded(m: Mode, t: Tree) -> Tree {
452    if m == Mode::Read {
453        t
454    } else {
455        node(mode(m).trim(), vec![t])
456    }
457}
458
459fn assign_symbol(op: AssignOp) -> &'static str {
460    match op {
461        AssignOp::Assign => "=",
462        AssignOp::Add => "+=",
463        AssignOp::Sub => "-=",
464        AssignOp::Mul => "*=",
465        AssignOp::Div => "/=",
466        AssignOp::Rem => "%=",
467        AssignOp::BitAnd => "&=",
468        AssignOp::BitOr => "|=",
469        AssignOp::BitXor => "^=",
470        AssignOp::Shl => "<<=",
471        AssignOp::Shr => ">>=",
472    }
473}
474
475fn expr(e: &Expr) -> Tree {
476    match &e.kind {
477        ExprKind::Int(v) => leaf(v.to_string()),
478        ExprKind::Float(v) => leaf(format!("{v:?}")),
479        ExprKind::Str(v) => leaf(format!("{v:?}")),
480        ExprKind::FStr(parts) => node(
481            "f",
482            parts
483                .iter()
484                .map(|part| match part {
485                    FStrPart::Text(t) => leaf(format!("{t:?}")),
486                    FStrPart::Expr(e, None) => expr(e),
487                    FStrPart::Expr(e, Some(spec)) => {
488                        node(format!("format {:?}", spec.text()), vec![expr(e)])
489                    }
490                })
491                .collect(),
492        ),
493        ExprKind::Bool(v) => leaf(v.to_string()),
494        ExprKind::Unit => leaf("()"),
495        ExprKind::Name(n) => leaf(n.name.clone()),
496        ExprKind::SelfValue => leaf("self"),
497        ExprKind::SelfType => leaf("Self"),
498        ExprKind::Paren(inner) => node("paren", vec![expr(inner)]),
499        ExprKind::List(items) => group("list", "[]", items.iter().map(expr).collect()),
500        ExprKind::Map(entries) => group(
501            "map",
502            "[:]",
503            entries
504                .iter()
505                .map(|(k, v)| node(":", vec![expr(k), expr(v)]))
506                .collect(),
507        ),
508        ExprKind::StructLit {
509            path: p,
510            type_args,
511            fields,
512            base,
513        } => {
514            let mut kids = vec![leaf(path(p))];
515            if !type_args.is_empty() {
516                kids.push(node(".<>", type_args.iter().map(ty).collect()));
517            }
518            for f in fields {
519                kids.push(match &f.value {
520                    Some(v) => node(format!("{}:", f.name.name), vec![expr(v)]),
521                    None => leaf(f.name.name.clone()),
522                });
523            }
524            if let Some(base) = base {
525                kids.push(node("..", vec![expr(base)]));
526            }
527            node("struct", kids)
528        }
529        ExprKind::Field { base, name } => node(".", vec![expr(base), leaf(name.name.clone())]),
530        ExprKind::TypeArgs { base, args } => {
531            let mut kids = vec![expr(base)];
532            kids.extend(args.iter().map(ty));
533            node(".<>", kids)
534        }
535        ExprKind::Call { callee, args } => {
536            let mut kids = vec![expr(callee)];
537            for a in args {
538                kids.push(if a.mode == Mode::Read {
539                    expr(&a.value)
540                } else {
541                    node(mode(a.mode).trim(), vec![expr(&a.value)])
542                });
543            }
544            node("call", kids)
545        }
546        ExprKind::Index { base, index } => node("index", vec![expr(base), expr(index)]),
547        ExprKind::Slice {
548            base,
549            start,
550            end,
551            inclusive,
552        } => {
553            let bound = |b: &Option<Box<Expr>>| b.as_ref().map_or(leaf("_"), |e| expr(e));
554            node(
555                if *inclusive { "slice..=" } else { "slice.." },
556                vec![expr(base), bound(start), bound(end)],
557            )
558        }
559        ExprKind::Try(inner) => node("?", vec![expr(inner)]),
560        ExprKind::Unary { op, operand } => {
561            let symbol = match op {
562                UnaryOp::Neg => "neg",
563                UnaryOp::Not => "!",
564                UnaryOp::BitNot => "~",
565            };
566            node(symbol, vec![expr(operand)])
567        }
568        ExprKind::Binary { op, lhs, rhs } => node(op.symbol(), vec![expr(lhs), expr(rhs)]),
569        ExprKind::Closure { params, ret, body } => {
570            let params = params
571                .iter()
572                .map(|p| match &p.ty {
573                    Some(t) => node(p.name.name.clone(), vec![ty(t)]),
574                    None => leaf(p.name.name.clone()),
575                })
576                .collect();
577            let mut kids = vec![group("params", "||", params)];
578            if let Some(ret) = ret {
579                kids.push(node("->", vec![ty(ret)]));
580            }
581            kids.push(expr(body));
582            node("closure", kids)
583        }
584        ExprKind::Block(b) => block(b),
585        ExprKind::If {
586            cond,
587            then,
588            otherwise,
589        } => {
590            let mut kids = vec![expr(cond), block(then)];
591            if let Some(other) = otherwise {
592                kids.push(node("else", vec![expr(other)]));
593            }
594            node("if", kids)
595        }
596        ExprKind::IfLet {
597            items,
598            then,
599            otherwise,
600        } => {
601            let mut kids = cond_items(items);
602            kids.push(block(then));
603            if let Some(other) = otherwise {
604                kids.push(node("else", vec![expr(other)]));
605            }
606            node("if let", kids)
607        }
608        ExprKind::Loop { label, body } => labelled(label, node("loop", vec![block(body)])),
609        ExprKind::Match {
610            mode: m,
611            scrutinee,
612            arms,
613        } => {
614            let mut kids = vec![if *m == Mode::Read {
615                expr(scrutinee)
616            } else {
617                node(mode(*m).trim(), vec![expr(scrutinee)])
618            }];
619            for arm in arms {
620                let mut ak = vec![pat(&arm.pattern)];
621                if let Some(guard) = &arm.guard {
622                    ak.push(node("if", vec![expr(guard)]));
623                }
624                ak.push(expr(&arm.body));
625                kids.push(node("=>", ak));
626            }
627            node("match", kids)
628        }
629        ExprKind::Return(value) => match value {
630            Some(v) => node("return", vec![expr(v)]),
631            None => leaf("return"),
632        },
633        ExprKind::Break { label, value } => {
634            let mut kids = Vec::new();
635            if let Some(l) = label {
636                kids.push(leaf(format!("{}:", l.name)));
637            }
638            if let Some(v) = value {
639                kids.push(expr(v));
640            }
641            if kids.is_empty() {
642                leaf("break")
643            } else {
644                node("break", kids)
645            }
646        }
647        ExprKind::Continue { label } => match label {
648            Some(l) => node("continue", vec![leaf(format!("{}:", l.name))]),
649            None => leaf("continue"),
650        },
651        ExprKind::Error => leaf("<error>"),
652    }
653}
654
655fn pat(p: &Pat) -> Tree {
656    match &p.kind {
657        PatKind::Wild => leaf("_"),
658        PatKind::Name(n) => leaf(n.name.clone()),
659        PatKind::Int { negative, value } => {
660            leaf(format!("{}{}", if *negative { "-" } else { "" }, value))
661        }
662        PatKind::Str(s) => leaf(format!("{s:?}")),
663        PatKind::Bool(b) => leaf(b.to_string()),
664        PatKind::Path(p) => leaf(path(p)),
665        PatKind::Tuple { path: p, items } => node(path(p), items.iter().map(pat).collect()),
666        PatKind::Struct {
667            path: p,
668            fields,
669            rest,
670        } => {
671            let mut kids: Vec<Tree> = fields
672                .iter()
673                .map(|f| match &f.pat {
674                    Some(sub) => node(format!("{}:", f.name.name), vec![pat(sub)]),
675                    None => leaf(f.name.name.clone()),
676                })
677                .collect();
678            if *rest {
679                kids.push(leaf(".."));
680            }
681            node(format!("{} {{}}", path(p)), kids)
682        }
683        PatKind::Or(alts) => node("|", alts.iter().map(pat).collect()),
684        PatKind::Error => leaf("<error>"),
685    }
686}