1use super::{Expr, InterpPart, Params, Script, Stmt};
2
3pub fn dump(script: &Script) -> String {
6 let mut out = String::new();
7 out.push_str("Script\n");
8 for stmt in &script.stmts {
9 dump_stmt(stmt, 1, &mut out);
10 }
11 out
12}
13
14fn indent(level: usize, out: &mut String) {
15 for _ in 0..level {
16 out.push_str(" ");
17 }
18}
19
20fn line(level: usize, text: &str, out: &mut String) {
21 indent(level, out);
22 out.push_str(text);
23 out.push('\n');
24}
25
26fn name_list(names: &[String], rest: Option<&str>) -> String {
30 let mut parts: Vec<String> = names.to_vec();
31 if let Some(rest) = rest {
32 parts.push(format!("many {rest}"));
33 }
34 parts.join(", ")
35}
36
37fn dump_block(label: &str, body: &[Stmt], level: usize, out: &mut String) {
38 line(level, label, out);
39 for stmt in body {
40 dump_stmt(stmt, level + 1, out);
41 }
42}
43
44fn dump_stmt(stmt: &Stmt, level: usize, out: &mut String) {
45 match stmt {
46 Stmt::Decl {
47 names, rest, expr, ..
48 } => {
49 line(
50 level,
51 &format!("Decl {}", name_list(names, rest.as_deref())),
52 out,
53 );
54 dump_expr(expr, level + 1, out);
55 }
56 Stmt::ConstDecl { name, expr, .. } => {
57 line(level, &format!("ConstDecl {name}"), out);
58 dump_expr(expr, level + 1, out);
59 }
60 Stmt::Import { module, .. } => line(level, &format!("Import {module}"), out),
61 Stmt::Assign {
62 targets,
63 rest,
64 expr,
65 flavored,
66 ..
67 } => {
68 let label = if *flavored { "Assign very" } else { "Assign" };
69 line(level, label, out);
70 for target in targets {
71 dump_block_expr("target", target, level + 1, out);
72 }
73 if let Some(rest) = rest {
74 dump_block_expr("many target", rest, level + 1, out);
75 }
76 dump_block_expr("value", expr, level + 1, out);
77 }
78 Stmt::Bark { expr, .. } => {
79 line(level, "Bark", out);
80 dump_expr(expr, level + 1, out);
81 }
82 Stmt::If {
83 branches,
84 else_body,
85 ..
86 } => {
87 line(level, "If", out);
88 for (cond, body) in branches {
89 line(level + 1, "branch", out);
90 dump_block_expr("cond", cond, level + 2, out);
91 dump_block("body", body, level + 2, out);
92 }
93 if let Some(body) = else_body {
94 dump_block("else", body, level + 1, out);
95 }
96 }
97 Stmt::For {
98 vars,
99 rest,
100 iter,
101 body,
102 ..
103 } => {
104 line(
105 level,
106 &format!("For {}", name_list(vars, rest.as_deref())),
107 out,
108 );
109 dump_block_expr("in", iter, level + 1, out);
110 dump_block("body", body, level + 1, out);
111 }
112 Stmt::While { cond, body, .. } => {
113 line(level, "While", out);
114 dump_block_expr("cond", cond, level + 1, out);
115 dump_block("body", body, level + 1, out);
116 }
117 Stmt::FuncDef {
118 name, params, body, ..
119 } => {
120 let params = if params.is_empty() {
121 String::new()
122 } else {
123 format!(" much {}", dump_params(params))
124 };
125 dump_block(&format!("FuncDef {name}{params}"), body, level, out);
126 }
127 Stmt::ObjDef {
128 name,
129 parent,
130 methods,
131 ..
132 } => {
133 let heading = match parent {
134 Some(parent) => format!("ObjDef {name} much {parent}"),
135 None => format!("ObjDef {name}"),
136 };
137 dump_block(&heading, methods, level, out);
138 }
139 Stmt::Try {
140 body,
141 err_name,
142 handler,
143 ..
144 } => {
145 line(level, "Try", out);
146 dump_block("body", body, level + 1, out);
147 dump_block(&format!("catch {err_name}"), handler, level + 1, out);
148 }
149 Stmt::Return { expr, .. } => match expr {
150 Some(expr) => {
151 line(level, "Return", out);
152 dump_expr(expr, level + 1, out);
153 }
154 None => line(level, "Return", out),
155 },
156 Stmt::Bonk { expr, .. } => {
157 line(level, "Bonk", out);
158 dump_expr(expr, level + 1, out);
159 }
160 Stmt::Amaze { cond, message, .. } => {
161 line(level, "Amaze", out);
162 dump_expr(cond, level + 1, out);
163 if let Some(message) = message {
164 dump_expr(message, level + 1, out);
165 }
166 }
167 Stmt::Bork { .. } => line(level, "Bork", out),
168 Stmt::Continue { .. } => line(level, "Continue", out),
169 Stmt::ExprStmt { expr } => {
170 line(level, "ExprStmt", out);
171 dump_expr(expr, level + 1, out);
172 }
173 }
174}
175
176fn dump_params(params: &Params) -> String {
179 let mut parts: Vec<String> = params
180 .params
181 .iter()
182 .map(|p| {
183 if p.default.is_some() {
184 format!("{} = …", p.name)
185 } else {
186 p.name.clone()
187 }
188 })
189 .collect();
190 if let Some(rest) = ¶ms.vararg {
191 parts.push(format!("many {rest}"));
192 }
193 parts.join(", ")
194}
195
196fn dump_block_expr(label: &str, expr: &Expr, level: usize, out: &mut String) {
198 line(level, label, out);
199 dump_expr(expr, level + 1, out);
200}
201
202fn dump_expr(expr: &Expr, level: usize, out: &mut String) {
203 match expr {
204 Expr::Int { value, .. } => line(level, &format!("Int {value}"), out),
205 Expr::Float { value, .. } => line(level, &format!("Float {value}"), out),
206 Expr::Str { value, .. } => line(level, &format!("Str {value:?}"), out),
207 Expr::Bool { value, .. } => line(level, &format!("Bool {value}"), out),
208 Expr::None { .. } => line(level, "None", out),
209 Expr::Ident { name, .. } => line(level, &format!("Ident {name}"), out),
210 Expr::List { items, .. } => {
211 line(level, "List", out);
212 for item in items {
213 dump_expr(item, level + 1, out);
214 }
215 }
216 Expr::Dict { entries, .. } => {
217 line(level, "Dict", out);
218 for (key, value) in entries {
219 line(level + 1, "entry", out);
220 dump_block_expr("key", key, level + 2, out);
221 dump_block_expr("value", value, level + 2, out);
222 }
223 }
224 Expr::Binary { op, lhs, rhs, .. } => {
225 line(level, &format!("Binary {}", op.symbol()), out);
226 dump_expr(lhs, level + 1, out);
227 dump_expr(rhs, level + 1, out);
228 }
229 Expr::Unary { op, operand, .. } => {
230 line(level, &format!("Unary {}", op.symbol()), out);
231 dump_expr(operand, level + 1, out);
232 }
233 Expr::Call {
234 callee,
235 args,
236 kwargs,
237 ..
238 } => {
239 line(level, "Call", out);
240 dump_block_expr("callee", callee, level + 1, out);
241 for arg in args {
242 dump_block_expr("arg", arg, level + 1, out);
243 }
244 for (name, value) in kwargs {
245 dump_block_expr(&format!("kwarg {name}"), value, level + 1, out);
246 }
247 }
248 Expr::Index { obj, index, .. } => {
249 line(level, "Index", out);
250 dump_block_expr("obj", obj, level + 1, out);
251 dump_block_expr("index", index, level + 1, out);
252 }
253 Expr::Slice {
254 obj,
255 start,
256 end,
257 step,
258 ..
259 } => {
260 line(level, "Slice", out);
261 dump_block_expr("obj", obj, level + 1, out);
262 for (label, part) in [("start", start), ("end", end), ("step", step)] {
263 match part {
264 Some(expr) => dump_block_expr(label, expr, level + 1, out),
265 None => line(level + 1, &format!("{label} none"), out),
266 }
267 }
268 }
269 Expr::Ternary {
270 cond,
271 then,
272 otherwise,
273 ..
274 } => {
275 line(level, "Ternary", out);
276 dump_block_expr("cond", cond, level + 1, out);
277 dump_block_expr("then", then, level + 1, out);
278 dump_block_expr("else", otherwise, level + 1, out);
279 }
280 Expr::Attr { obj, name, .. } => {
281 line(level, &format!("Attr {name}"), out);
282 dump_expr(obj, level + 1, out);
283 }
284 Expr::SuperCall { method, args, .. } => {
285 line(level, &format!("SuperCall {method}"), out);
286 for arg in args {
287 dump_block_expr("arg", arg, level + 1, out);
288 }
289 }
290 Expr::StrInterp { parts, .. } => {
291 line(level, "StrInterp", out);
292 for part in parts {
293 match part {
294 InterpPart::Lit(text) => line(level + 1, &format!("Lit {text:?}"), out),
295 InterpPart::Expr(expr) => dump_block_expr("hole", expr, level + 1, out),
296 }
297 }
298 }
299 }
300}