1use std::fmt::Write;
25
26use crate::ast::{FnBody, FnDef, Stmt, TopLevel, TypeDef};
27use crate::checker::expr_to_str;
28use crate::ir::{AnalysisResult, BodyShape, FnAnalysis, ThinKind};
29
30pub fn dump_items(items: &[TopLevel], analysis: Option<&AnalysisResult>) -> String {
35 let mut out = String::new();
36 let mut first = true;
37 for item in items {
38 if !first {
39 out.push('\n');
40 }
41 first = false;
42 dump_top_level(item, &mut out, analysis);
43 }
44 out
45}
46
47fn dump_top_level(item: &TopLevel, out: &mut String, analysis: Option<&AnalysisResult>) {
48 match item {
49 TopLevel::Module(m) => {
50 writeln!(out, "module {}", m.name).ok();
51 if !m.depends.is_empty() {
52 writeln!(out, " depends [{}]", m.depends.join(", ")).ok();
53 }
54 if !m.exposes.is_empty() {
55 writeln!(out, " exposes [{}]", m.exposes.join(", ")).ok();
56 }
57 if let Some(effects) = &m.effects {
58 writeln!(out, " effects [{}]", effects.join(", ")).ok();
59 }
60 }
61 TopLevel::TypeDef(td) => dump_typedef(td, out),
62 TopLevel::FnDef(fd) => {
63 let fn_analysis = analysis.and_then(|a| a.fn_analyses.get(&fd.name));
64 dump_fndef(fd, out, fn_analysis);
65 }
66 TopLevel::Stmt(s) => dump_stmt(s, 0, out),
67 TopLevel::Verify(vb) => {
68 writeln!(out, "verify {} <{} case(s)>", vb.fn_name, vb.cases.len()).ok();
69 }
70 TopLevel::Decision(_) => {
71 writeln!(out, "decision <block>").ok();
72 }
73 }
74}
75
76fn dump_typedef(td: &TypeDef, out: &mut String) {
77 match td {
78 TypeDef::Product { name, fields, .. } => {
79 let parts: Vec<String> = fields
80 .iter()
81 .map(|(n, t)| format!("{}: {}", n, t))
82 .collect();
83 writeln!(out, "type {} = {{ {} }}", name, parts.join(", ")).ok();
84 }
85 TypeDef::Sum { name, variants, .. } => {
86 let parts: Vec<String> = variants
87 .iter()
88 .map(|v| {
89 if v.fields.is_empty() {
90 v.name.clone()
91 } else {
92 format!("{}({})", v.name, v.fields.join(", "))
93 }
94 })
95 .collect();
96 writeln!(out, "type {} = {}", name, parts.join(" | ")).ok();
97 }
98 }
99}
100
101fn dump_fndef(fd: &FnDef, out: &mut String, fn_analysis: Option<&FnAnalysis>) {
102 let params: Vec<String> = fd
103 .params
104 .iter()
105 .map(|(n, t)| format!("{}: {}", n, t))
106 .collect();
107 let effects = if fd.effects.is_empty() {
108 String::new()
109 } else {
110 let names: Vec<String> = fd.effects.iter().map(|e| e.node.clone()).collect();
111 format!(" ! [{}]", names.join(", "))
112 };
113
114 let tag_str = match fn_analysis {
115 None => String::new(),
116 Some(a) => {
117 let mut tags: Vec<String> = Vec::new();
118 if matches!(a.allocates, Some(false)) {
119 tags.push("no_alloc".to_string());
120 }
121 if let Some(n) = a.local_count {
122 tags.push(format!("locals={}", n));
123 }
124 if a.mutual_tco_member {
125 tags.push("mutual-tco".to_string());
126 } else if a.recursive {
127 if a.recursive_call_count >= 2 {
128 tags.push(format!("recursive×{}", a.recursive_call_count));
129 } else {
130 tags.push("recursive".to_string());
131 }
132 }
133 tags.push(body_shape_tag(a.body_shape, a.thin_kind));
134 format!(" [{}]", tags.join(", "))
135 }
136 };
137
138 writeln!(
139 out,
140 "fn {}({}) -> {}{}{}",
141 fd.name,
142 params.join(", "),
143 fd.return_type,
144 effects,
145 tag_str
146 )
147 .ok();
148
149 let FnBody::Block(stmts) = fd.body.as_ref();
150 for stmt in stmts {
151 dump_stmt(stmt, 1, out);
152 }
153}
154
155fn body_shape_tag(shape: BodyShape, thin_kind: Option<ThinKind>) -> String {
156 let kind_suffix = thin_kind.map(thin_kind_label).unwrap_or("");
157 let kind_part = if kind_suffix.is_empty() {
158 String::new()
159 } else {
160 format!(" ({})", kind_suffix)
161 };
162 match shape {
163 BodyShape::LeafExpr => format!("body=leaf-expr{}", kind_part),
164 BodyShape::SingleExpr => format!("body=single-expr{}", kind_part),
165 BodyShape::Block(n) => format!("body=block:{}{}", n, kind_part),
166 BodyShape::Unclassified(1) => "body=single-expr".to_string(),
167 BodyShape::Unclassified(n) => format!("body=block:{}", n),
168 }
169}
170
171fn thin_kind_label(kind: ThinKind) -> &'static str {
172 match kind {
173 ThinKind::Leaf => "leaf",
174 ThinKind::Direct => "direct",
175 ThinKind::Forward => "forward",
176 ThinKind::Dispatch => "dispatch",
177 ThinKind::Tail => "tail",
178 }
179}
180
181fn dump_stmt(stmt: &Stmt, indent: usize, out: &mut String) {
182 let pad = " ".repeat(indent);
183 match stmt {
184 Stmt::Binding(name, ty, expr) => {
185 let ty_part = ty
186 .as_deref()
187 .map(|t| format!(": {}", t))
188 .unwrap_or_default();
189 writeln!(out, "{}{}{} = {}", pad, name, ty_part, expr_to_str(expr)).ok();
190 }
191 Stmt::Expr(expr) => {
192 writeln!(out, "{}{}", pad, expr_to_str(expr)).ok();
193 }
194 }
195}