Skip to main content

draxl_ast/
lib.rs

1#![forbid(unsafe_code)]
2//! Typed IR for Draxl.
3//!
4//! This crate defines the shared syntax model for the entire workspace:
5//!
6//! - stable metadata on modeled nodes
7//! - typed items, statements, expressions, patterns, and types
8//! - deterministic JSON emission for test fixtures and tooling
9//!
10//! It deliberately does not own parsing, validation rules, formatting policy,
11//! lowering, or patch application.
12
13use std::fmt::Write;
14
15/// Stable identifier attached to an Draxl node.
16pub type NodeId = String;
17
18/// Opaque ordering key for ordered list slots.
19pub type Rank = String;
20
21/// Explicit name for the slot that owns a node.
22pub type SlotName = String;
23
24/// Byte range in the original source file.
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub struct Span {
27    pub start: usize,
28    pub end: usize,
29}
30
31/// Shared metadata carried by Draxl nodes.
32#[derive(Debug, Clone, PartialEq, Eq)]
33pub struct Meta {
34    pub id: NodeId,
35    pub rank: Option<Rank>,
36    pub anchor: Option<NodeId>,
37    pub slot: Option<SlotName>,
38    pub span: Option<Span>,
39}
40
41impl Meta {
42    /// Returns a copy with source spans removed.
43    pub fn without_span(&self) -> Self {
44        let mut meta = self.clone();
45        meta.span = None;
46        meta
47    }
48}
49
50/// Parsed Draxl file.
51#[derive(Debug, Clone, PartialEq, Eq)]
52pub struct File {
53    pub items: Vec<Item>,
54}
55
56impl File {
57    /// Returns a clone with all spans stripped for semantic comparisons.
58    pub fn without_spans(&self) -> Self {
59        let mut file = self.clone();
60        file.clear_spans();
61        file
62    }
63
64    /// Removes span data from the file in place.
65    pub fn clear_spans(&mut self) {
66        for item in &mut self.items {
67            item.clear_spans();
68        }
69    }
70
71    /// Emits deterministic JSON for the file.
72    pub fn to_json_pretty(&self) -> String {
73        let mut out = String::new();
74        write_file_json(self, &mut out, 0);
75        out.push('\n');
76        out
77    }
78}
79
80/// Top-level or module item.
81#[derive(Debug, Clone, PartialEq, Eq)]
82pub enum Item {
83    Mod(ItemMod),
84    Use(ItemUse),
85    Struct(ItemStruct),
86    Enum(ItemEnum),
87    Fn(ItemFn),
88    Doc(DocNode),
89    Comment(CommentNode),
90}
91
92impl Item {
93    /// Returns the metadata for the item.
94    pub fn meta(&self) -> &Meta {
95        match self {
96            Self::Mod(node) => &node.meta,
97            Self::Use(node) => &node.meta,
98            Self::Struct(node) => &node.meta,
99            Self::Enum(node) => &node.meta,
100            Self::Fn(node) => &node.meta,
101            Self::Doc(node) => &node.meta,
102            Self::Comment(node) => &node.meta,
103        }
104    }
105
106    /// Returns mutable metadata for the item.
107    pub fn meta_mut(&mut self) -> &mut Meta {
108        match self {
109            Self::Mod(node) => &mut node.meta,
110            Self::Use(node) => &mut node.meta,
111            Self::Struct(node) => &mut node.meta,
112            Self::Enum(node) => &mut node.meta,
113            Self::Fn(node) => &mut node.meta,
114            Self::Doc(node) => &mut node.meta,
115            Self::Comment(node) => &mut node.meta,
116        }
117    }
118
119    /// Removes span data from the item in place.
120    pub fn clear_spans(&mut self) {
121        self.meta_mut().span = None;
122        match self {
123            Self::Mod(node) => {
124                for item in &mut node.items {
125                    item.clear_spans();
126                }
127            }
128            Self::Use(_) => {}
129            Self::Struct(node) => {
130                for field in &mut node.fields {
131                    field.clear_spans();
132                }
133            }
134            Self::Enum(node) => {
135                for variant in &mut node.variants {
136                    variant.clear_spans();
137                }
138            }
139            Self::Fn(node) => {
140                for param in &mut node.params {
141                    param.clear_spans();
142                }
143                if let Some(ret_ty) = &mut node.ret_ty {
144                    ret_ty.clear_spans();
145                }
146                node.body.clear_spans();
147            }
148            Self::Doc(_) => {}
149            Self::Comment(_) => {}
150        }
151    }
152}
153
154/// Module item.
155#[derive(Debug, Clone, PartialEq, Eq)]
156pub struct ItemMod {
157    pub meta: Meta,
158    pub name: String,
159    pub items: Vec<Item>,
160}
161
162/// Use item.
163#[derive(Debug, Clone, PartialEq, Eq)]
164pub struct ItemUse {
165    /// Draxl metadata for the `use` item.
166    pub meta: Meta,
167    /// Structured `use` tree for the imported path set.
168    pub tree: UseTree,
169}
170
171/// Tree form for `use` items in the bootstrap subset.
172#[derive(Debug, Clone, PartialEq, Eq)]
173pub enum UseTree {
174    /// Single imported name.
175    Name(UseName),
176    /// Prefix-plus-child path segment.
177    Path(UsePathTree),
178    /// Braced `use` tree group.
179    Group(UseGroup),
180    /// Glob import.
181    Glob(UseGlob),
182}
183
184/// Single-name `use` tree segment.
185#[derive(Debug, Clone, PartialEq, Eq)]
186pub struct UseName {
187    /// Imported name.
188    pub name: String,
189}
190
191/// Prefix-plus-child `use` tree segment.
192#[derive(Debug, Clone, PartialEq, Eq)]
193pub struct UsePathTree {
194    /// Leading path segment.
195    pub prefix: String,
196    /// Remaining `use` tree after the prefix.
197    pub tree: Box<UseTree>,
198}
199
200/// Braced `use` tree group.
201#[derive(Debug, Clone, PartialEq, Eq)]
202pub struct UseGroup {
203    /// Children inside the braced group.
204    pub items: Vec<UseTree>,
205}
206
207/// Glob `use` tree leaf.
208#[derive(Debug, Clone, PartialEq, Eq)]
209pub struct UseGlob;
210
211/// Struct item.
212#[derive(Debug, Clone, PartialEq, Eq)]
213pub struct ItemStruct {
214    pub meta: Meta,
215    pub name: String,
216    pub fields: Vec<Field>,
217}
218
219/// Enum item.
220#[derive(Debug, Clone, PartialEq, Eq)]
221pub struct ItemEnum {
222    pub meta: Meta,
223    pub name: String,
224    pub variants: Vec<Variant>,
225}
226
227/// Function item.
228#[derive(Debug, Clone, PartialEq, Eq)]
229pub struct ItemFn {
230    pub meta: Meta,
231    pub name: String,
232    pub params: Vec<Param>,
233    pub ret_ty: Option<Type>,
234    pub body: Block,
235}
236
237/// Struct field.
238#[derive(Debug, Clone, PartialEq, Eq)]
239pub struct Field {
240    pub meta: Meta,
241    pub name: String,
242    pub ty: Type,
243}
244
245impl Field {
246    /// Removes span data from the field in place.
247    pub fn clear_spans(&mut self) {
248        self.meta.span = None;
249        self.ty.clear_spans();
250    }
251}
252
253/// Enum variant.
254#[derive(Debug, Clone, PartialEq, Eq)]
255pub struct Variant {
256    pub meta: Meta,
257    pub name: String,
258}
259
260impl Variant {
261    /// Removes span data from the variant in place.
262    pub fn clear_spans(&mut self) {
263        self.meta.span = None;
264    }
265}
266
267/// Function parameter.
268#[derive(Debug, Clone, PartialEq, Eq)]
269pub struct Param {
270    pub meta: Meta,
271    pub name: String,
272    pub ty: Type,
273}
274
275impl Param {
276    /// Removes span data from the parameter in place.
277    pub fn clear_spans(&mut self) {
278        self.meta.span = None;
279        self.ty.clear_spans();
280    }
281}
282
283/// Block expression body.
284#[derive(Debug, Clone, PartialEq, Eq)]
285pub struct Block {
286    pub meta: Option<Meta>,
287    pub stmts: Vec<Stmt>,
288}
289
290impl Block {
291    /// Removes span data from the block in place.
292    pub fn clear_spans(&mut self) {
293        if let Some(meta) = &mut self.meta {
294            meta.span = None;
295        }
296        for stmt in &mut self.stmts {
297            stmt.clear_spans();
298        }
299    }
300}
301
302/// Statement inside a block.
303#[derive(Debug, Clone, PartialEq, Eq)]
304pub enum Stmt {
305    Let(StmtLet),
306    Expr(StmtExpr),
307    Item(Item),
308    Doc(DocNode),
309    Comment(CommentNode),
310}
311
312impl Stmt {
313    /// Returns the metadata for ranked block children when present.
314    pub fn meta(&self) -> Option<&Meta> {
315        match self {
316            Self::Let(node) => Some(&node.meta),
317            Self::Expr(node) => Some(&node.meta),
318            Self::Item(node) => Some(node.meta()),
319            Self::Doc(node) => Some(&node.meta),
320            Self::Comment(node) => Some(&node.meta),
321        }
322    }
323
324    /// Removes span data from the statement in place.
325    pub fn clear_spans(&mut self) {
326        match self {
327            Self::Let(node) => node.clear_spans(),
328            Self::Expr(node) => node.clear_spans(),
329            Self::Item(node) => node.clear_spans(),
330            Self::Doc(node) => node.meta.span = None,
331            Self::Comment(node) => node.meta.span = None,
332        }
333    }
334}
335
336/// Let statement.
337#[derive(Debug, Clone, PartialEq, Eq)]
338pub struct StmtLet {
339    pub meta: Meta,
340    pub pat: Pattern,
341    pub value: Expr,
342}
343
344impl StmtLet {
345    /// Removes span data from the statement in place.
346    pub fn clear_spans(&mut self) {
347        self.meta.span = None;
348        self.pat.clear_spans();
349        self.value.clear_spans();
350    }
351}
352
353/// Expression statement.
354#[derive(Debug, Clone, PartialEq, Eq)]
355pub struct StmtExpr {
356    /// Draxl metadata for the statement node.
357    pub meta: Meta,
358    /// Expression carried by the statement.
359    pub expr: Expr,
360    /// Whether the statement ends with a semicolon.
361    pub has_semi: bool,
362}
363
364impl StmtExpr {
365    /// Removes span data from the statement in place.
366    pub fn clear_spans(&mut self) {
367        self.meta.span = None;
368        self.expr.clear_spans();
369    }
370}
371
372/// Expression in the bootstrap subset.
373#[derive(Debug, Clone, PartialEq, Eq)]
374pub enum Expr {
375    Path(ExprPath),
376    Lit(ExprLit),
377    Group(ExprGroup),
378    Binary(ExprBinary),
379    Unary(ExprUnary),
380    Call(ExprCall),
381    Match(ExprMatch),
382    Block(Block),
383}
384
385impl Expr {
386    /// Returns metadata when the expression carries explicit Draxl metadata.
387    pub fn meta(&self) -> Option<&Meta> {
388        match self {
389            Self::Path(node) => node.meta.as_ref(),
390            Self::Lit(node) => node.meta.as_ref(),
391            Self::Group(node) => node.meta.as_ref(),
392            Self::Binary(node) => node.meta.as_ref(),
393            Self::Unary(node) => node.meta.as_ref(),
394            Self::Call(node) => node.meta.as_ref(),
395            Self::Match(node) => node.meta.as_ref(),
396            Self::Block(node) => node.meta.as_ref(),
397        }
398    }
399
400    /// Returns mutable metadata when the expression carries explicit Draxl metadata.
401    pub fn meta_mut(&mut self) -> Option<&mut Meta> {
402        match self {
403            Self::Path(node) => node.meta.as_mut(),
404            Self::Lit(node) => node.meta.as_mut(),
405            Self::Group(node) => node.meta.as_mut(),
406            Self::Binary(node) => node.meta.as_mut(),
407            Self::Unary(node) => node.meta.as_mut(),
408            Self::Call(node) => node.meta.as_mut(),
409            Self::Match(node) => node.meta.as_mut(),
410            Self::Block(node) => node.meta.as_mut(),
411        }
412    }
413
414    /// Removes span data from the expression in place.
415    pub fn clear_spans(&mut self) {
416        if let Some(meta) = self.meta_mut() {
417            meta.span = None;
418        }
419        match self {
420            Self::Path(_) => {}
421            Self::Lit(_) => {}
422            Self::Group(node) => {
423                node.expr.clear_spans();
424            }
425            Self::Binary(node) => {
426                node.lhs.clear_spans();
427                node.rhs.clear_spans();
428            }
429            Self::Unary(node) => {
430                node.expr.clear_spans();
431            }
432            Self::Call(node) => {
433                node.callee.clear_spans();
434                for arg in &mut node.args {
435                    arg.clear_spans();
436                }
437            }
438            Self::Match(node) => {
439                node.scrutinee.clear_spans();
440                for arm in &mut node.arms {
441                    arm.clear_spans();
442                }
443            }
444            Self::Block(node) => node.clear_spans(),
445        }
446    }
447}
448
449/// Path expression.
450#[derive(Debug, Clone, PartialEq, Eq)]
451pub struct ExprPath {
452    pub meta: Option<Meta>,
453    pub path: Path,
454}
455
456/// Literal expression.
457#[derive(Debug, Clone, PartialEq, Eq)]
458pub struct ExprLit {
459    pub meta: Option<Meta>,
460    pub value: Literal,
461}
462
463/// Grouped expression that preserves source parentheses.
464#[derive(Debug, Clone, PartialEq, Eq)]
465pub struct ExprGroup {
466    /// Optional Draxl metadata attached to the grouped expression.
467    pub meta: Option<Meta>,
468    /// Inner expression wrapped by the source parentheses.
469    pub expr: Box<Expr>,
470}
471
472/// Binary expression.
473#[derive(Debug, Clone, PartialEq, Eq)]
474pub struct ExprBinary {
475    pub meta: Option<Meta>,
476    pub lhs: Box<Expr>,
477    pub op: BinaryOp,
478    pub rhs: Box<Expr>,
479}
480
481/// Unary expression.
482#[derive(Debug, Clone, PartialEq, Eq)]
483pub struct ExprUnary {
484    pub meta: Option<Meta>,
485    pub op: UnaryOp,
486    pub expr: Box<Expr>,
487}
488
489/// Call expression.
490#[derive(Debug, Clone, PartialEq, Eq)]
491pub struct ExprCall {
492    pub meta: Option<Meta>,
493    pub callee: Box<Expr>,
494    pub args: Vec<Expr>,
495}
496
497/// Match expression.
498#[derive(Debug, Clone, PartialEq, Eq)]
499pub struct ExprMatch {
500    pub meta: Option<Meta>,
501    pub scrutinee: Box<Expr>,
502    pub arms: Vec<MatchArm>,
503}
504
505/// Match arm.
506#[derive(Debug, Clone, PartialEq, Eq)]
507pub struct MatchArm {
508    pub meta: Meta,
509    pub pat: Pattern,
510    pub guard: Option<Expr>,
511    pub body: Expr,
512}
513
514impl MatchArm {
515    /// Removes span data from the match arm in place.
516    pub fn clear_spans(&mut self) {
517        self.meta.span = None;
518        self.pat.clear_spans();
519        if let Some(guard) = &mut self.guard {
520            guard.clear_spans();
521        }
522        self.body.clear_spans();
523    }
524}
525
526/// Pattern in the bootstrap subset.
527#[derive(Debug, Clone, PartialEq, Eq)]
528pub enum Pattern {
529    Ident(PatIdent),
530    Wild(PatWild),
531}
532
533impl Pattern {
534    /// Returns metadata when the pattern carries explicit Draxl metadata.
535    pub fn meta(&self) -> Option<&Meta> {
536        match self {
537            Self::Ident(node) => node.meta.as_ref(),
538            Self::Wild(node) => node.meta.as_ref(),
539        }
540    }
541
542    /// Removes span data from the pattern in place.
543    pub fn clear_spans(&mut self) {
544        match self {
545            Self::Ident(node) => {
546                if let Some(meta) = &mut node.meta {
547                    meta.span = None;
548                }
549            }
550            Self::Wild(node) => {
551                if let Some(meta) = &mut node.meta {
552                    meta.span = None;
553                }
554            }
555        }
556    }
557}
558
559/// Identifier pattern.
560#[derive(Debug, Clone, PartialEq, Eq)]
561pub struct PatIdent {
562    pub meta: Option<Meta>,
563    pub name: String,
564}
565
566/// Wildcard pattern.
567#[derive(Debug, Clone, PartialEq, Eq)]
568pub struct PatWild {
569    pub meta: Option<Meta>,
570}
571
572/// Type in the bootstrap subset.
573#[derive(Debug, Clone, PartialEq, Eq)]
574pub enum Type {
575    Path(TypePath),
576}
577
578impl Type {
579    /// Returns the metadata attached to the type.
580    pub fn meta(&self) -> &Meta {
581        match self {
582            Self::Path(node) => &node.meta,
583        }
584    }
585
586    /// Removes span data from the type in place.
587    pub fn clear_spans(&mut self) {
588        match self {
589            Self::Path(node) => node.meta.span = None,
590        }
591    }
592}
593
594/// Path type.
595#[derive(Debug, Clone, PartialEq, Eq)]
596pub struct TypePath {
597    pub meta: Meta,
598    pub path: Path,
599}
600
601/// Path value.
602#[derive(Debug, Clone, PartialEq, Eq)]
603pub struct Path {
604    pub segments: Vec<String>,
605}
606
607/// Literal value.
608#[derive(Debug, Clone, PartialEq, Eq)]
609pub enum Literal {
610    Int(i64),
611    Str(String),
612}
613
614/// Binary operator in the bootstrap subset.
615#[derive(Debug, Clone, Copy, PartialEq, Eq)]
616pub enum BinaryOp {
617    Add,
618    Sub,
619    Lt,
620}
621
622/// Unary operator in the bootstrap subset.
623#[derive(Debug, Clone, Copy, PartialEq, Eq)]
624pub enum UnaryOp {
625    Neg,
626}
627
628/// Doc comment node.
629#[derive(Debug, Clone, PartialEq, Eq)]
630pub struct DocNode {
631    pub meta: Meta,
632    pub text: String,
633}
634
635/// Line comment node.
636#[derive(Debug, Clone, PartialEq, Eq)]
637pub struct CommentNode {
638    pub meta: Meta,
639    pub text: String,
640}
641
642fn write_indent(out: &mut String, level: usize) {
643    for _ in 0..level {
644        out.push_str("  ");
645    }
646}
647
648fn write_json_string(value: &str, out: &mut String) {
649    out.push('"');
650    for ch in value.chars() {
651        match ch {
652            '"' => out.push_str("\\\""),
653            '\\' => out.push_str("\\\\"),
654            '\n' => out.push_str("\\n"),
655            '\r' => out.push_str("\\r"),
656            '\t' => out.push_str("\\t"),
657            _ => out.push(ch),
658        }
659    }
660    out.push('"');
661}
662
663fn write_json_meta(meta: &Meta, out: &mut String, level: usize) {
664    out.push_str("{\n");
665    write_indent(out, level + 1);
666    out.push_str("\"id\": ");
667    write_json_string(&meta.id, out);
668    out.push_str(",\n");
669    write_indent(out, level + 1);
670    out.push_str("\"rank\": ");
671    match &meta.rank {
672        Some(rank) => write_json_string(rank, out),
673        None => out.push_str("null"),
674    }
675    out.push_str(",\n");
676    write_indent(out, level + 1);
677    out.push_str("\"anchor\": ");
678    match &meta.anchor {
679        Some(anchor) => write_json_string(anchor, out),
680        None => out.push_str("null"),
681    }
682    out.push_str(",\n");
683    write_indent(out, level + 1);
684    out.push_str("\"slot\": ");
685    match &meta.slot {
686        Some(slot) => write_json_string(slot, out),
687        None => out.push_str("null"),
688    }
689    out.push_str(",\n");
690    write_indent(out, level + 1);
691    out.push_str("\"span\": ");
692    match meta.span {
693        Some(span) => {
694            out.push_str("{\n");
695            write_indent(out, level + 2);
696            let _ = write!(out, "\"start\": {},\n", span.start);
697            write_indent(out, level + 2);
698            let _ = write!(out, "\"end\": {}\n", span.end);
699            write_indent(out, level + 1);
700            out.push('}');
701        }
702        None => out.push_str("null"),
703    }
704    out.push('\n');
705    write_indent(out, level);
706    out.push('}');
707}
708
709fn write_json_path(path: &Path, out: &mut String, level: usize) {
710    out.push_str("{\n");
711    write_indent(out, level + 1);
712    out.push_str("\"segments\": [");
713    for (index, segment) in path.segments.iter().enumerate() {
714        if index > 0 {
715            out.push_str(", ");
716        }
717        write_json_string(segment, out);
718    }
719    out.push_str("]\n");
720    write_indent(out, level);
721    out.push('}');
722}
723
724fn write_json_use_tree(tree: &UseTree, out: &mut String, level: usize) {
725    match tree {
726        UseTree::Name(node) => {
727            out.push_str("{\n");
728            write_indent(out, level + 1);
729            out.push_str("\"kind\": \"Name\",\n");
730            write_indent(out, level + 1);
731            out.push_str("\"name\": ");
732            write_json_string(&node.name, out);
733            out.push('\n');
734            write_indent(out, level);
735            out.push('}');
736        }
737        UseTree::Path(node) => {
738            out.push_str("{\n");
739            write_indent(out, level + 1);
740            out.push_str("\"kind\": \"Path\",\n");
741            write_indent(out, level + 1);
742            out.push_str("\"prefix\": ");
743            write_json_string(&node.prefix, out);
744            out.push_str(",\n");
745            write_indent(out, level + 1);
746            out.push_str("\"tree\": ");
747            write_json_use_tree(&node.tree, out, level + 1);
748            out.push('\n');
749            write_indent(out, level);
750            out.push('}');
751        }
752        UseTree::Group(node) => {
753            out.push_str("{\n");
754            write_indent(out, level + 1);
755            out.push_str("\"kind\": \"Group\",\n");
756            write_indent(out, level + 1);
757            out.push_str("\"items\": [\n");
758            for (index, item) in node.items.iter().enumerate() {
759                if index > 0 {
760                    out.push_str(",\n");
761                }
762                write_indent(out, level + 2);
763                write_json_use_tree(item, out, level + 2);
764            }
765            out.push('\n');
766            write_indent(out, level + 1);
767            out.push_str("]\n");
768            write_indent(out, level);
769            out.push('}');
770        }
771        UseTree::Glob(_) => {
772            out.push_str("{\n");
773            write_indent(out, level + 1);
774            out.push_str("\"kind\": \"Glob\"\n");
775            write_indent(out, level);
776            out.push('}');
777        }
778    }
779}
780
781fn write_json_literal(literal: &Literal, out: &mut String, level: usize) {
782    out.push_str("{\n");
783    match literal {
784        Literal::Int(value) => {
785            write_indent(out, level + 1);
786            out.push_str("\"kind\": \"Int\",\n");
787            write_indent(out, level + 1);
788            let _ = write!(out, "\"value\": {}\n", value);
789        }
790        Literal::Str(value) => {
791            write_indent(out, level + 1);
792            out.push_str("\"kind\": \"Str\",\n");
793            write_indent(out, level + 1);
794            out.push_str("\"value\": ");
795            write_json_string(value, out);
796            out.push('\n');
797        }
798    }
799    write_indent(out, level);
800    out.push('}');
801}
802
803fn write_json_type(ty: &Type, out: &mut String, level: usize) {
804    match ty {
805        Type::Path(node) => {
806            out.push_str("{\n");
807            write_indent(out, level + 1);
808            out.push_str("\"kind\": \"Path\",\n");
809            write_indent(out, level + 1);
810            out.push_str("\"meta\": ");
811            write_json_meta(&node.meta, out, level + 1);
812            out.push_str(",\n");
813            write_indent(out, level + 1);
814            out.push_str("\"path\": ");
815            write_json_path(&node.path, out, level + 1);
816            out.push('\n');
817            write_indent(out, level);
818            out.push('}');
819        }
820    }
821}
822
823fn write_json_pattern(pattern: &Pattern, out: &mut String, level: usize) {
824    match pattern {
825        Pattern::Ident(node) => {
826            out.push_str("{\n");
827            write_indent(out, level + 1);
828            out.push_str("\"kind\": \"Ident\",\n");
829            write_indent(out, level + 1);
830            out.push_str("\"meta\": ");
831            match &node.meta {
832                Some(meta) => write_json_meta(meta, out, level + 1),
833                None => out.push_str("null"),
834            }
835            out.push_str(",\n");
836            write_indent(out, level + 1);
837            out.push_str("\"name\": ");
838            write_json_string(&node.name, out);
839            out.push('\n');
840            write_indent(out, level);
841            out.push('}');
842        }
843        Pattern::Wild(node) => {
844            out.push_str("{\n");
845            write_indent(out, level + 1);
846            out.push_str("\"kind\": \"Wild\",\n");
847            write_indent(out, level + 1);
848            out.push_str("\"meta\": ");
849            match &node.meta {
850                Some(meta) => write_json_meta(meta, out, level + 1),
851                None => out.push_str("null"),
852            }
853            out.push('\n');
854            write_indent(out, level);
855            out.push('}');
856        }
857    }
858}
859
860fn write_json_expr(expr: &Expr, out: &mut String, level: usize) {
861    match expr {
862        Expr::Path(node) => {
863            out.push_str("{\n");
864            write_indent(out, level + 1);
865            out.push_str("\"kind\": \"Path\",\n");
866            write_indent(out, level + 1);
867            out.push_str("\"meta\": ");
868            match &node.meta {
869                Some(meta) => write_json_meta(meta, out, level + 1),
870                None => out.push_str("null"),
871            }
872            out.push_str(",\n");
873            write_indent(out, level + 1);
874            out.push_str("\"path\": ");
875            write_json_path(&node.path, out, level + 1);
876            out.push('\n');
877            write_indent(out, level);
878            out.push('}');
879        }
880        Expr::Lit(node) => {
881            out.push_str("{\n");
882            write_indent(out, level + 1);
883            out.push_str("\"kind\": \"Lit\",\n");
884            write_indent(out, level + 1);
885            out.push_str("\"meta\": ");
886            match &node.meta {
887                Some(meta) => write_json_meta(meta, out, level + 1),
888                None => out.push_str("null"),
889            }
890            out.push_str(",\n");
891            write_indent(out, level + 1);
892            out.push_str("\"value\": ");
893            write_json_literal(&node.value, out, level + 1);
894            out.push('\n');
895            write_indent(out, level);
896            out.push('}');
897        }
898        Expr::Group(node) => {
899            out.push_str("{\n");
900            write_indent(out, level + 1);
901            out.push_str("\"kind\": \"Group\",\n");
902            write_indent(out, level + 1);
903            out.push_str("\"meta\": ");
904            match &node.meta {
905                Some(meta) => write_json_meta(meta, out, level + 1),
906                None => out.push_str("null"),
907            }
908            out.push_str(",\n");
909            write_indent(out, level + 1);
910            out.push_str("\"expr\": ");
911            write_json_expr(&node.expr, out, level + 1);
912            out.push('\n');
913            write_indent(out, level);
914            out.push('}');
915        }
916        Expr::Binary(node) => {
917            out.push_str("{\n");
918            write_indent(out, level + 1);
919            out.push_str("\"kind\": \"Binary\",\n");
920            write_indent(out, level + 1);
921            out.push_str("\"meta\": ");
922            match &node.meta {
923                Some(meta) => write_json_meta(meta, out, level + 1),
924                None => out.push_str("null"),
925            }
926            out.push_str(",\n");
927            write_indent(out, level + 1);
928            out.push_str("\"lhs\": ");
929            write_json_expr(&node.lhs, out, level + 1);
930            out.push_str(",\n");
931            write_indent(out, level + 1);
932            out.push_str("\"op\": ");
933            write_json_string(
934                match node.op {
935                    BinaryOp::Add => "Add",
936                    BinaryOp::Sub => "Sub",
937                    BinaryOp::Lt => "Lt",
938                },
939                out,
940            );
941            out.push_str(",\n");
942            write_indent(out, level + 1);
943            out.push_str("\"rhs\": ");
944            write_json_expr(&node.rhs, out, level + 1);
945            out.push('\n');
946            write_indent(out, level);
947            out.push('}');
948        }
949        Expr::Unary(node) => {
950            out.push_str("{\n");
951            write_indent(out, level + 1);
952            out.push_str("\"kind\": \"Unary\",\n");
953            write_indent(out, level + 1);
954            out.push_str("\"meta\": ");
955            match &node.meta {
956                Some(meta) => write_json_meta(meta, out, level + 1),
957                None => out.push_str("null"),
958            }
959            out.push_str(",\n");
960            write_indent(out, level + 1);
961            out.push_str("\"op\": ");
962            write_json_string("Neg", out);
963            out.push_str(",\n");
964            write_indent(out, level + 1);
965            out.push_str("\"expr\": ");
966            write_json_expr(&node.expr, out, level + 1);
967            out.push('\n');
968            write_indent(out, level);
969            out.push('}');
970        }
971        Expr::Call(node) => {
972            out.push_str("{\n");
973            write_indent(out, level + 1);
974            out.push_str("\"kind\": \"Call\",\n");
975            write_indent(out, level + 1);
976            out.push_str("\"meta\": ");
977            match &node.meta {
978                Some(meta) => write_json_meta(meta, out, level + 1),
979                None => out.push_str("null"),
980            }
981            out.push_str(",\n");
982            write_indent(out, level + 1);
983            out.push_str("\"callee\": ");
984            write_json_expr(&node.callee, out, level + 1);
985            out.push_str(",\n");
986            write_indent(out, level + 1);
987            out.push_str("\"args\": [\n");
988            for (index, arg) in node.args.iter().enumerate() {
989                if index > 0 {
990                    out.push_str(",\n");
991                }
992                write_indent(out, level + 2);
993                write_json_expr(arg, out, level + 2);
994            }
995            out.push('\n');
996            write_indent(out, level + 1);
997            out.push_str("]\n");
998            write_indent(out, level);
999            out.push('}');
1000        }
1001        Expr::Match(node) => {
1002            out.push_str("{\n");
1003            write_indent(out, level + 1);
1004            out.push_str("\"kind\": \"Match\",\n");
1005            write_indent(out, level + 1);
1006            out.push_str("\"meta\": ");
1007            match &node.meta {
1008                Some(meta) => write_json_meta(meta, out, level + 1),
1009                None => out.push_str("null"),
1010            }
1011            out.push_str(",\n");
1012            write_indent(out, level + 1);
1013            out.push_str("\"scrutinee\": ");
1014            write_json_expr(&node.scrutinee, out, level + 1);
1015            out.push_str(",\n");
1016            write_indent(out, level + 1);
1017            out.push_str("\"arms\": [\n");
1018            for (index, arm) in node.arms.iter().enumerate() {
1019                if index > 0 {
1020                    out.push_str(",\n");
1021                }
1022                write_indent(out, level + 2);
1023                write_json_match_arm(arm, out, level + 2);
1024            }
1025            out.push('\n');
1026            write_indent(out, level + 1);
1027            out.push_str("]\n");
1028            write_indent(out, level);
1029            out.push('}');
1030        }
1031        Expr::Block(node) => write_json_block(node, out, level),
1032    }
1033}
1034
1035fn write_json_match_arm(arm: &MatchArm, out: &mut String, level: usize) {
1036    out.push_str("{\n");
1037    write_indent(out, level + 1);
1038    out.push_str("\"meta\": ");
1039    write_json_meta(&arm.meta, out, level + 1);
1040    out.push_str(",\n");
1041    write_indent(out, level + 1);
1042    out.push_str("\"pat\": ");
1043    write_json_pattern(&arm.pat, out, level + 1);
1044    out.push_str(",\n");
1045    write_indent(out, level + 1);
1046    out.push_str("\"guard\": ");
1047    match &arm.guard {
1048        Some(guard) => write_json_expr(guard, out, level + 1),
1049        None => out.push_str("null"),
1050    }
1051    out.push_str(",\n");
1052    write_indent(out, level + 1);
1053    out.push_str("\"body\": ");
1054    write_json_expr(&arm.body, out, level + 1);
1055    out.push('\n');
1056    write_indent(out, level);
1057    out.push('}');
1058}
1059
1060fn write_json_block(block: &Block, out: &mut String, level: usize) {
1061    out.push_str("{\n");
1062    write_indent(out, level + 1);
1063    out.push_str("\"kind\": \"Block\",\n");
1064    write_indent(out, level + 1);
1065    out.push_str("\"meta\": ");
1066    match &block.meta {
1067        Some(meta) => write_json_meta(meta, out, level + 1),
1068        None => out.push_str("null"),
1069    }
1070    out.push_str(",\n");
1071    write_indent(out, level + 1);
1072    out.push_str("\"stmts\": [\n");
1073    for (index, stmt) in block.stmts.iter().enumerate() {
1074        if index > 0 {
1075            out.push_str(",\n");
1076        }
1077        write_indent(out, level + 2);
1078        write_json_stmt(stmt, out, level + 2);
1079    }
1080    out.push('\n');
1081    write_indent(out, level + 1);
1082    out.push_str("]\n");
1083    write_indent(out, level);
1084    out.push('}');
1085}
1086
1087fn write_json_stmt(stmt: &Stmt, out: &mut String, level: usize) {
1088    match stmt {
1089        Stmt::Let(node) => {
1090            out.push_str("{\n");
1091            write_indent(out, level + 1);
1092            out.push_str("\"kind\": \"Let\",\n");
1093            write_indent(out, level + 1);
1094            out.push_str("\"meta\": ");
1095            write_json_meta(&node.meta, out, level + 1);
1096            out.push_str(",\n");
1097            write_indent(out, level + 1);
1098            out.push_str("\"pat\": ");
1099            write_json_pattern(&node.pat, out, level + 1);
1100            out.push_str(",\n");
1101            write_indent(out, level + 1);
1102            out.push_str("\"value\": ");
1103            write_json_expr(&node.value, out, level + 1);
1104            out.push('\n');
1105            write_indent(out, level);
1106            out.push('}');
1107        }
1108        Stmt::Expr(node) => {
1109            out.push_str("{\n");
1110            write_indent(out, level + 1);
1111            out.push_str("\"kind\": \"Expr\",\n");
1112            write_indent(out, level + 1);
1113            out.push_str("\"meta\": ");
1114            write_json_meta(&node.meta, out, level + 1);
1115            out.push_str(",\n");
1116            write_indent(out, level + 1);
1117            let _ = write!(out, "\"has_semi\": {},\n", node.has_semi);
1118            write_indent(out, level + 1);
1119            out.push_str("\"expr\": ");
1120            write_json_expr(&node.expr, out, level + 1);
1121            out.push('\n');
1122            write_indent(out, level);
1123            out.push('}');
1124        }
1125        Stmt::Item(node) => {
1126            out.push_str("{\n");
1127            write_indent(out, level + 1);
1128            out.push_str("\"kind\": \"Item\",\n");
1129            write_indent(out, level + 1);
1130            out.push_str("\"item\": ");
1131            write_json_item(node, out, level + 1);
1132            out.push('\n');
1133            write_indent(out, level);
1134            out.push('}');
1135        }
1136        Stmt::Doc(node) => {
1137            out.push_str("{\n");
1138            write_indent(out, level + 1);
1139            out.push_str("\"kind\": \"Doc\",\n");
1140            write_indent(out, level + 1);
1141            out.push_str("\"meta\": ");
1142            write_json_meta(&node.meta, out, level + 1);
1143            out.push_str(",\n");
1144            write_indent(out, level + 1);
1145            out.push_str("\"text\": ");
1146            write_json_string(&node.text, out);
1147            out.push('\n');
1148            write_indent(out, level);
1149            out.push('}');
1150        }
1151        Stmt::Comment(node) => {
1152            out.push_str("{\n");
1153            write_indent(out, level + 1);
1154            out.push_str("\"kind\": \"Comment\",\n");
1155            write_indent(out, level + 1);
1156            out.push_str("\"meta\": ");
1157            write_json_meta(&node.meta, out, level + 1);
1158            out.push_str(",\n");
1159            write_indent(out, level + 1);
1160            out.push_str("\"text\": ");
1161            write_json_string(&node.text, out);
1162            out.push('\n');
1163            write_indent(out, level);
1164            out.push('}');
1165        }
1166    }
1167}
1168
1169fn write_json_item(item: &Item, out: &mut String, level: usize) {
1170    match item {
1171        Item::Mod(node) => {
1172            out.push_str("{\n");
1173            write_indent(out, level + 1);
1174            out.push_str("\"kind\": \"Mod\",\n");
1175            write_indent(out, level + 1);
1176            out.push_str("\"meta\": ");
1177            write_json_meta(&node.meta, out, level + 1);
1178            out.push_str(",\n");
1179            write_indent(out, level + 1);
1180            out.push_str("\"name\": ");
1181            write_json_string(&node.name, out);
1182            out.push_str(",\n");
1183            write_indent(out, level + 1);
1184            out.push_str("\"items\": [\n");
1185            for (index, child) in node.items.iter().enumerate() {
1186                if index > 0 {
1187                    out.push_str(",\n");
1188                }
1189                write_indent(out, level + 2);
1190                write_json_item(child, out, level + 2);
1191            }
1192            out.push('\n');
1193            write_indent(out, level + 1);
1194            out.push_str("]\n");
1195            write_indent(out, level);
1196            out.push('}');
1197        }
1198        Item::Use(node) => {
1199            out.push_str("{\n");
1200            write_indent(out, level + 1);
1201            out.push_str("\"kind\": \"Use\",\n");
1202            write_indent(out, level + 1);
1203            out.push_str("\"meta\": ");
1204            write_json_meta(&node.meta, out, level + 1);
1205            out.push_str(",\n");
1206            write_indent(out, level + 1);
1207            out.push_str("\"tree\": ");
1208            write_json_use_tree(&node.tree, out, level + 1);
1209            out.push('\n');
1210            write_indent(out, level);
1211            out.push('}');
1212        }
1213        Item::Struct(node) => {
1214            out.push_str("{\n");
1215            write_indent(out, level + 1);
1216            out.push_str("\"kind\": \"Struct\",\n");
1217            write_indent(out, level + 1);
1218            out.push_str("\"meta\": ");
1219            write_json_meta(&node.meta, out, level + 1);
1220            out.push_str(",\n");
1221            write_indent(out, level + 1);
1222            out.push_str("\"name\": ");
1223            write_json_string(&node.name, out);
1224            out.push_str(",\n");
1225            write_indent(out, level + 1);
1226            out.push_str("\"fields\": [\n");
1227            for (index, field) in node.fields.iter().enumerate() {
1228                if index > 0 {
1229                    out.push_str(",\n");
1230                }
1231                write_indent(out, level + 2);
1232                out.push_str("{\n");
1233                write_indent(out, level + 3);
1234                out.push_str("\"meta\": ");
1235                write_json_meta(&field.meta, out, level + 3);
1236                out.push_str(",\n");
1237                write_indent(out, level + 3);
1238                out.push_str("\"name\": ");
1239                write_json_string(&field.name, out);
1240                out.push_str(",\n");
1241                write_indent(out, level + 3);
1242                out.push_str("\"ty\": ");
1243                write_json_type(&field.ty, out, level + 3);
1244                out.push('\n');
1245                write_indent(out, level + 2);
1246                out.push('}');
1247            }
1248            out.push('\n');
1249            write_indent(out, level + 1);
1250            out.push_str("]\n");
1251            write_indent(out, level);
1252            out.push('}');
1253        }
1254        Item::Enum(node) => {
1255            out.push_str("{\n");
1256            write_indent(out, level + 1);
1257            out.push_str("\"kind\": \"Enum\",\n");
1258            write_indent(out, level + 1);
1259            out.push_str("\"meta\": ");
1260            write_json_meta(&node.meta, out, level + 1);
1261            out.push_str(",\n");
1262            write_indent(out, level + 1);
1263            out.push_str("\"name\": ");
1264            write_json_string(&node.name, out);
1265            out.push_str(",\n");
1266            write_indent(out, level + 1);
1267            out.push_str("\"variants\": [\n");
1268            for (index, variant) in node.variants.iter().enumerate() {
1269                if index > 0 {
1270                    out.push_str(",\n");
1271                }
1272                write_indent(out, level + 2);
1273                out.push_str("{\n");
1274                write_indent(out, level + 3);
1275                out.push_str("\"meta\": ");
1276                write_json_meta(&variant.meta, out, level + 3);
1277                out.push_str(",\n");
1278                write_indent(out, level + 3);
1279                out.push_str("\"name\": ");
1280                write_json_string(&variant.name, out);
1281                out.push('\n');
1282                write_indent(out, level + 2);
1283                out.push('}');
1284            }
1285            out.push('\n');
1286            write_indent(out, level + 1);
1287            out.push_str("]\n");
1288            write_indent(out, level);
1289            out.push('}');
1290        }
1291        Item::Fn(node) => {
1292            out.push_str("{\n");
1293            write_indent(out, level + 1);
1294            out.push_str("\"kind\": \"Fn\",\n");
1295            write_indent(out, level + 1);
1296            out.push_str("\"meta\": ");
1297            write_json_meta(&node.meta, out, level + 1);
1298            out.push_str(",\n");
1299            write_indent(out, level + 1);
1300            out.push_str("\"name\": ");
1301            write_json_string(&node.name, out);
1302            out.push_str(",\n");
1303            write_indent(out, level + 1);
1304            out.push_str("\"params\": [\n");
1305            for (index, param) in node.params.iter().enumerate() {
1306                if index > 0 {
1307                    out.push_str(",\n");
1308                }
1309                write_indent(out, level + 2);
1310                out.push_str("{\n");
1311                write_indent(out, level + 3);
1312                out.push_str("\"meta\": ");
1313                write_json_meta(&param.meta, out, level + 3);
1314                out.push_str(",\n");
1315                write_indent(out, level + 3);
1316                out.push_str("\"name\": ");
1317                write_json_string(&param.name, out);
1318                out.push_str(",\n");
1319                write_indent(out, level + 3);
1320                out.push_str("\"ty\": ");
1321                write_json_type(&param.ty, out, level + 3);
1322                out.push('\n');
1323                write_indent(out, level + 2);
1324                out.push('}');
1325            }
1326            out.push('\n');
1327            write_indent(out, level + 1);
1328            out.push_str("],\n");
1329            write_indent(out, level + 1);
1330            out.push_str("\"ret_ty\": ");
1331            match &node.ret_ty {
1332                Some(ret_ty) => write_json_type(ret_ty, out, level + 1),
1333                None => out.push_str("null"),
1334            }
1335            out.push_str(",\n");
1336            write_indent(out, level + 1);
1337            out.push_str("\"body\": ");
1338            write_json_block(&node.body, out, level + 1);
1339            out.push('\n');
1340            write_indent(out, level);
1341            out.push('}');
1342        }
1343        Item::Doc(node) => {
1344            out.push_str("{\n");
1345            write_indent(out, level + 1);
1346            out.push_str("\"kind\": \"Doc\",\n");
1347            write_indent(out, level + 1);
1348            out.push_str("\"meta\": ");
1349            write_json_meta(&node.meta, out, level + 1);
1350            out.push_str(",\n");
1351            write_indent(out, level + 1);
1352            out.push_str("\"text\": ");
1353            write_json_string(&node.text, out);
1354            out.push('\n');
1355            write_indent(out, level);
1356            out.push('}');
1357        }
1358        Item::Comment(node) => {
1359            out.push_str("{\n");
1360            write_indent(out, level + 1);
1361            out.push_str("\"kind\": \"Comment\",\n");
1362            write_indent(out, level + 1);
1363            out.push_str("\"meta\": ");
1364            write_json_meta(&node.meta, out, level + 1);
1365            out.push_str(",\n");
1366            write_indent(out, level + 1);
1367            out.push_str("\"text\": ");
1368            write_json_string(&node.text, out);
1369            out.push('\n');
1370            write_indent(out, level);
1371            out.push('}');
1372        }
1373    }
1374}
1375
1376fn write_file_json(file: &File, out: &mut String, level: usize) {
1377    out.push_str("{\n");
1378    write_indent(out, level + 1);
1379    out.push_str("\"items\": [\n");
1380    for (index, item) in file.items.iter().enumerate() {
1381        if index > 0 {
1382            out.push_str(",\n");
1383        }
1384        write_indent(out, level + 2);
1385        write_json_item(item, out, level + 2);
1386    }
1387    out.push('\n');
1388    write_indent(out, level + 1);
1389    out.push_str("]\n");
1390    write_indent(out, level);
1391    out.push('}');
1392}