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