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                };
164                d.push(leaf(format!("from {:?}", decl.from.value)));
165                kids.push(node(head, d));
166            }
167            format!("extern {}", target.target.name)
168        }
169        ItemKind::Config(entry) => {
170            flag(entry.public, "pub", &mut kids);
171            kids.push(leaf(entry.name.name.clone()));
172            kids.push(ty(&entry.ty));
173            kids.push(expr(&entry.value));
174            "const".to_string()
175        }
176        ItemKind::Struct(decl) => {
177            flag(decl.public, "pub", &mut kids);
178            kids.push(leaf(decl.name.name.clone()));
179            generics(&decl.generics, &mut kids);
180            kids.extend(decl.fields.iter().map(field));
181            "struct".to_string()
182        }
183        ItemKind::Enum(decl) => {
184            flag(decl.public, "pub", &mut kids);
185            kids.push(leaf(decl.name.name.clone()));
186            generics(&decl.generics, &mut kids);
187            for v in &decl.variants {
188                let mut vk = Vec::new();
189                docs(&v.docs, &mut vk);
190                vk.push(leaf(v.name.name.clone()));
191                match &v.payload {
192                    VariantPayload::None => {}
193                    VariantPayload::Tuple(types) => {
194                        vk.push(node("tuple", types.iter().map(ty).collect()))
195                    }
196                    VariantPayload::Struct(fields) => {
197                        vk.push(node("fields", fields.iter().map(field).collect()))
198                    }
199                }
200                kids.push(node("variant", vk));
201            }
202            "enum".to_string()
203        }
204        ItemKind::Alias(decl) => {
205            flag(decl.public, "pub", &mut kids);
206            kids.push(leaf(decl.name.name.clone()));
207            generics(&decl.generics, &mut kids);
208            kids.push(ty(&decl.ty));
209            "alias".to_string()
210        }
211        ItemKind::IntrinsicType(decl) => {
212            flag(decl.public, "pub", &mut kids);
213            kids.push(leaf(decl.name.name.clone()));
214            generics(&decl.generics, &mut kids);
215            "intrinsic type".to_string()
216        }
217        ItemKind::Interface(decl) => {
218            flag(decl.public, "pub", &mut kids);
219            kids.push(leaf(decl.name.name.clone()));
220            generics(&decl.generics, &mut kids);
221            for m in &decl.methods {
222                let mut mk = Vec::new();
223                docs(&m.docs, &mut mk);
224                sig(&m.sig, &mut mk);
225                kids.push(node("sig", mk));
226            }
227            "interface".to_string()
228        }
229        ItemKind::Impl(block) => {
230            kids.push(leaf(block.type_name.name.clone()));
231            generics(&block.generics, &mut kids);
232            if let Some(interface) = &block.interface {
233                kids.push(node("for", vec![ty(interface)]));
234            }
235            for m in &block.methods {
236                let mut mk = Vec::new();
237                docs(&m.docs, &mut mk);
238                fn_decl(&m.decl, &mut mk);
239                kids.push(node("fn", mk));
240            }
241            "impl".to_string()
242        }
243        ItemKind::Fn(decl) => {
244            fn_decl(decl, &mut kids);
245            "fn".to_string()
246        }
247        ItemKind::Test(test) => {
248            kids.push(leaf(format!("{:?}", test.name.value)));
249            kids.push(block(&test.body));
250            "test".to_string()
251        }
252    };
253    node(head, kids)
254}
255
256fn field(f: &Field) -> Tree {
257    let mut kids = Vec::new();
258    docs(&f.docs, &mut kids);
259    flag(f.public, "pub", &mut kids);
260    kids.push(leaf(f.name.name.clone()));
261    kids.push(ty(&f.ty));
262    node("field", kids)
263}
264
265fn fn_decl(decl: &FnDecl, kids: &mut Vec<Tree>) {
266    flag(decl.public, "pub", kids);
267    flag(decl.intrinsic, "intrinsic", kids);
268    sig(&decl.sig, kids);
269    if let Some(body) = &decl.body {
270        kids.push(block(body));
271    }
272}
273
274fn mode(m: Mode) -> &'static str {
275    match m {
276        Mode::Read => "",
277        Mode::Mut => "mut ",
278        Mode::Own => "own ",
279    }
280}
281
282fn sig(s: &FnSig, kids: &mut Vec<Tree>) {
283    kids.push(leaf(s.name.name.clone()));
284    generics(&s.generics, kids);
285    let params = s
286        .params
287        .iter()
288        .map(|p| match &p.kind {
289            ParamKind::SelfParam => leaf(format!("{}self", mode(p.mode))),
290            ParamKind::Named { name, ty: t } => {
291                node(format!("{}{}", mode(p.mode), name.name), vec![ty(t)])
292            }
293        })
294        .collect();
295    kids.push(group("params", "()", params));
296    if let Some(ret) = &s.ret {
297        kids.push(node("->", vec![ty(ret)]));
298    }
299}
300
301fn ty(t: &Ty) -> Tree {
302    match &t.kind {
303        TyKind::Path { path: p, args } => {
304            if args.is_empty() {
305                leaf(path(p))
306            } else {
307                node(path(p), args.iter().map(ty).collect())
308            }
309        }
310        TyKind::Fn { params, ret } => {
311            let mut kids: Vec<Tree> = params
312                .iter()
313                .map(|(m, t)| {
314                    if *m == Mode::Read {
315                        ty(t)
316                    } else {
317                        node(mode(*m).trim(), vec![ty(t)])
318                    }
319                })
320                .collect();
321            if let Some(ret) = ret {
322                kids.push(node("->", vec![ty(ret)]));
323            }
324            node("fn", kids)
325        }
326        TyKind::SelfType => leaf("Self"),
327        TyKind::Error => leaf("<error>"),
328    }
329}
330
331fn block(b: &Block) -> Tree {
332    group("block", "{}", b.stmts.iter().map(stmt).collect())
333}
334
335fn stmt(s: &Stmt) -> Tree {
336    match &s.kind {
337        StmtKind::Let {
338            mutable,
339            name,
340            ty: t,
341            value,
342        } => {
343            let mut kids = vec![leaf(name.as_ref().map_or("_", |n| n.name.as_str()))];
344            if let Some(t) = t {
345                kids.push(ty(t));
346            }
347            kids.push(expr(value));
348            node(if *mutable { "var" } else { "let" }, kids)
349        }
350        StmtKind::Assign { target, op, value } => {
351            node(assign_symbol(*op), vec![expr(target), expr(value)])
352        }
353        StmtKind::While { cond, body } => node("while", vec![expr(cond), block(body)]),
354        StmtKind::For { var, iter, body } => {
355            let iter = match iter {
356                ForIter::Collection { mode: m, expr: e } => {
357                    if *m == Mode::Read {
358                        expr(e)
359                    } else {
360                        node(mode(*m).trim(), vec![expr(e)])
361                    }
362                }
363                ForIter::Range {
364                    start,
365                    end,
366                    inclusive,
367                } => node(
368                    if *inclusive { "..=" } else { ".." },
369                    vec![expr(start), expr(end)],
370                ),
371            };
372            node("for", vec![leaf(var.name.clone()), iter, block(body)])
373        }
374        StmtKind::Expr(e) => expr(e),
375    }
376}
377
378fn assign_symbol(op: AssignOp) -> &'static str {
379    match op {
380        AssignOp::Assign => "=",
381        AssignOp::Add => "+=",
382        AssignOp::Sub => "-=",
383        AssignOp::Mul => "*=",
384        AssignOp::Div => "/=",
385        AssignOp::Rem => "%=",
386        AssignOp::BitAnd => "&=",
387        AssignOp::BitOr => "|=",
388        AssignOp::BitXor => "^=",
389        AssignOp::Shl => "<<=",
390        AssignOp::Shr => ">>=",
391    }
392}
393
394fn expr(e: &Expr) -> Tree {
395    match &e.kind {
396        ExprKind::Int(v) => leaf(v.to_string()),
397        ExprKind::Float(v) => leaf(format!("{v:?}")),
398        ExprKind::Str(v) => leaf(format!("{v:?}")),
399        ExprKind::Bool(v) => leaf(v.to_string()),
400        ExprKind::Unit => leaf("()"),
401        ExprKind::Name(n) => leaf(n.name.clone()),
402        ExprKind::SelfValue => leaf("self"),
403        ExprKind::SelfType => leaf("Self"),
404        ExprKind::Paren(inner) => node("paren", vec![expr(inner)]),
405        ExprKind::List(items) => group("list", "[]", items.iter().map(expr).collect()),
406        ExprKind::Map(entries) => group(
407            "map",
408            "[:]",
409            entries
410                .iter()
411                .map(|(k, v)| node(":", vec![expr(k), expr(v)]))
412                .collect(),
413        ),
414        ExprKind::StructLit {
415            path: p,
416            type_args,
417            fields,
418        } => {
419            let mut kids = vec![leaf(path(p))];
420            if !type_args.is_empty() {
421                kids.push(node(".<>", type_args.iter().map(ty).collect()));
422            }
423            for f in fields {
424                kids.push(match &f.value {
425                    Some(v) => node(format!("{}:", f.name.name), vec![expr(v)]),
426                    None => leaf(f.name.name.clone()),
427                });
428            }
429            node("struct", kids)
430        }
431        ExprKind::Field { base, name } => node(".", vec![expr(base), leaf(name.name.clone())]),
432        ExprKind::TypeArgs { base, args } => {
433            let mut kids = vec![expr(base)];
434            kids.extend(args.iter().map(ty));
435            node(".<>", kids)
436        }
437        ExprKind::Call { callee, args } => {
438            let mut kids = vec![expr(callee)];
439            for a in args {
440                kids.push(if a.mode == Mode::Read {
441                    expr(&a.value)
442                } else {
443                    node(mode(a.mode).trim(), vec![expr(&a.value)])
444                });
445            }
446            node("call", kids)
447        }
448        ExprKind::Index { base, index } => node("index", vec![expr(base), expr(index)]),
449        ExprKind::Slice {
450            base,
451            start,
452            end,
453            inclusive,
454        } => {
455            let bound = |b: &Option<Box<Expr>>| b.as_ref().map_or(leaf("_"), |e| expr(e));
456            node(
457                if *inclusive { "slice..=" } else { "slice.." },
458                vec![expr(base), bound(start), bound(end)],
459            )
460        }
461        ExprKind::Try(inner) => node("?", vec![expr(inner)]),
462        ExprKind::Unary { op, operand } => {
463            let symbol = match op {
464                UnaryOp::Neg => "neg",
465                UnaryOp::Not => "!",
466                UnaryOp::BitNot => "~",
467            };
468            node(symbol, vec![expr(operand)])
469        }
470        ExprKind::Binary { op, lhs, rhs } => node(op.symbol(), vec![expr(lhs), expr(rhs)]),
471        ExprKind::Closure { params, ret, body } => {
472            let params = params
473                .iter()
474                .map(|p| match &p.ty {
475                    Some(t) => node(p.name.name.clone(), vec![ty(t)]),
476                    None => leaf(p.name.name.clone()),
477                })
478                .collect();
479            let mut kids = vec![group("params", "||", params)];
480            if let Some(ret) = ret {
481                kids.push(node("->", vec![ty(ret)]));
482            }
483            kids.push(expr(body));
484            node("closure", kids)
485        }
486        ExprKind::Block(b) => block(b),
487        ExprKind::If {
488            cond,
489            then,
490            otherwise,
491        } => {
492            let mut kids = vec![expr(cond), block(then)];
493            if let Some(other) = otherwise {
494                kids.push(node("else", vec![expr(other)]));
495            }
496            node("if", kids)
497        }
498        ExprKind::Match {
499            mode: m,
500            scrutinee,
501            arms,
502        } => {
503            let mut kids = vec![if *m == Mode::Read {
504                expr(scrutinee)
505            } else {
506                node(mode(*m).trim(), vec![expr(scrutinee)])
507            }];
508            for arm in arms {
509                let mut ak = vec![pat(&arm.pattern)];
510                if let Some(guard) = &arm.guard {
511                    ak.push(node("if", vec![expr(guard)]));
512                }
513                ak.push(expr(&arm.body));
514                kids.push(node("=>", ak));
515            }
516            node("match", kids)
517        }
518        ExprKind::Return(value) => match value {
519            Some(v) => node("return", vec![expr(v)]),
520            None => leaf("return"),
521        },
522        ExprKind::Break => leaf("break"),
523        ExprKind::Continue => leaf("continue"),
524        ExprKind::Error => leaf("<error>"),
525    }
526}
527
528fn pat(p: &Pat) -> Tree {
529    match &p.kind {
530        PatKind::Wild => leaf("_"),
531        PatKind::Name(n) => leaf(n.name.clone()),
532        PatKind::Int { negative, value } => {
533            leaf(format!("{}{}", if *negative { "-" } else { "" }, value))
534        }
535        PatKind::Str(s) => leaf(format!("{s:?}")),
536        PatKind::Bool(b) => leaf(b.to_string()),
537        PatKind::Path(p) => leaf(path(p)),
538        PatKind::Tuple { path: p, items } => node(path(p), items.iter().map(pat).collect()),
539        PatKind::Struct {
540            path: p,
541            fields,
542            rest,
543        } => {
544            let mut kids: Vec<Tree> = fields
545                .iter()
546                .map(|f| match &f.pat {
547                    Some(sub) => node(format!("{}:", f.name.name), vec![pat(sub)]),
548                    None => leaf(f.name.name.clone()),
549                })
550                .collect();
551            if *rest {
552                kids.push(leaf(".."));
553            }
554            node(format!("{} {{}}", path(p)), kids)
555        }
556        PatKind::Or(alts) => node("|", alts.iter().map(pat).collect()),
557        PatKind::Error => leaf("<error>"),
558    }
559}