1use std::fmt::{Display, Result, Write};
5
6use itertools::Itertools;
7
8use crate::expression_tree::MinMaxOp;
9
10use super::{
11 CompilationUnit, EvaluationContext, Expression, LocalMemberIndex, LocalMemberReference,
12 MemberReference, ParentScope, SubComponentIdx,
13};
14
15pub fn pretty_print(root: &CompilationUnit, writer: &mut dyn Write) -> Result {
16 PrettyPrinter { writer, indentation: 0 }.print_root(root)
17}
18
19struct PrettyPrinter<'a> {
20 writer: &'a mut dyn Write,
21 indentation: usize,
22}
23
24impl PrettyPrinter<'_> {
25 fn print_root(&mut self, root: &CompilationUnit) -> Result {
26 for (idx, g) in root.globals.iter_enumerated() {
27 if !g.is_builtin {
28 self.print_global(root, idx, g)?;
29 }
30 }
31 for c in root.sub_components.keys() {
32 self.print_component(root, c, None)?
33 }
34
35 Ok(())
36 }
37
38 fn print_component(
39 &mut self,
40 root: &CompilationUnit,
41 sc_idx: SubComponentIdx,
42 parent: Option<&ParentScope<'_>>,
43 ) -> Result {
44 let ctx = EvaluationContext::new_sub_component(root, sc_idx, (), parent);
45 let sc = &root.sub_components[sc_idx];
46 writeln!(self.writer, "component {} {{", sc.name)?;
47 self.indentation += 1;
48 for p in &sc.properties {
49 self.indent()?;
50 writeln!(self.writer, "property <{}> {}; //use={}", p.ty, p.name, p.use_count.get())?;
51 }
52 for c in &sc.callbacks {
53 self.indent()?;
54 writeln!(
55 self.writer,
56 "callback {} ({}) -> {};",
57 c.name,
58 c.args.iter().map(ToString::to_string).join(", "),
59 c.ret_ty,
60 )?;
61 }
62 for f in &sc.functions {
63 self.indent()?;
64 writeln!(
65 self.writer,
66 "function {} ({}) -> {} {{ {} }}; ",
67 f.name,
68 f.args.iter().map(ToString::to_string).join(", "),
69 f.ret_ty,
70 DisplayExpression(&f.code, &ctx)
71 )?;
72 }
73 for (p1, p2, fields) in &sc.two_way_bindings {
74 self.indent()?;
75 writeln!(
76 self.writer,
77 "{} <=> {}{}{};",
78 DisplayPropertyRef(p1, &ctx),
79 DisplayPropertyRef(p2, &ctx),
80 if fields.is_empty() { "" } else { "." },
81 fields.join(".")
82 )?
83 }
84 for (p, init) in &sc.property_init {
85 self.indent()?;
86 writeln!(
87 self.writer,
88 "{}: {};{}",
89 DisplayPropertyRef(p, &ctx),
90 DisplayExpression(&init.expression.borrow(), &ctx),
91 if init.is_constant { " /*const*/" } else { "" }
92 )?
93 }
94 for (p, e) in &sc.change_callbacks {
95 self.indent()?;
96 writeln!(
97 self.writer,
98 "changed {} => {};",
99 DisplayPropertyRef(p, &ctx),
100 DisplayExpression(&e.borrow(), &ctx),
101 )?
102 }
103 for ssc in &sc.sub_components {
104 self.indent()?;
105 writeln!(self.writer, "{} := {} {{}};", ssc.name, root.sub_components[ssc.ty].name)?;
106 }
107 for (item, geom) in std::iter::zip(&sc.items, &sc.geometries) {
108 self.indent()?;
109 let geometry = geom.as_ref().map_or(String::new(), |geom| {
110 format!("geometry: {}", DisplayExpression(&geom.borrow(), &ctx))
111 });
112 writeln!(self.writer, "{} := {} {{ {geometry} }};", item.name, item.ty.class_name)?;
113 }
114 for (idx, r) in sc.repeated.iter_enumerated() {
115 self.indent()?;
116 write!(self.writer, "for in {} : ", DisplayExpression(&r.model.borrow(), &ctx))?;
117 self.print_component(root, r.sub_tree.root, Some(&ParentScope::new(&ctx, Some(idx))))?
118 }
119 for t in &sc.menu_item_trees {
120 self.indent()?;
121 self.print_component(root, t.root, Some(&ParentScope::new(&ctx, None)))?
122 }
123 for w in &sc.popup_windows {
124 self.indent()?;
125 self.print_component(root, w.item_tree.root, Some(&ParentScope::new(&ctx, None)))?
126 }
127 self.indentation -= 1;
128 self.indent()?;
129 writeln!(self.writer, "}}")
130 }
131
132 fn print_global(
133 &mut self,
134 root: &CompilationUnit,
135 idx: super::GlobalIdx,
136 global: &super::GlobalComponent,
137 ) -> Result {
138 let ctx = EvaluationContext::new_global(root, idx, ());
139 if global.exported {
140 write!(self.writer, "export ")?;
141 }
142 let aliases = global.aliases.join(",");
143 let aliases = if aliases.is_empty() { String::new() } else { format!(" /*{aliases}*/") };
144 writeln!(self.writer, "global {} {{{aliases}", global.name)?;
145 self.indentation += 1;
146 for (p, is_const) in std::iter::zip(&global.properties, &global.const_properties) {
147 self.indent()?;
148 writeln!(
149 self.writer,
150 "property <{}> {}; //use={}{}",
151 p.ty,
152 p.name,
153 p.use_count.get(),
154 if *is_const { " const" } else { "" }
155 )?;
156 }
157 for c in &global.callbacks {
158 self.indent()?;
159 writeln!(
160 self.writer,
161 "callback {} ({}) -> {};",
162 c.name,
163 c.args.iter().map(ToString::to_string).join(", "),
164 c.ret_ty,
165 )?;
166 }
167 for (p, init) in &global.init_values {
168 self.indent()?;
169 match p {
170 LocalMemberIndex::Property(p) => {
171 writeln!(
172 self.writer,
173 "{}: {}{};",
174 global.properties[*p].name,
175 DisplayExpression(&init.expression.borrow(), &ctx,),
176 if init.is_constant { "/*const*/" } else { "" }
177 )?;
178 }
179 LocalMemberIndex::Callback(c) => {
180 writeln!(
181 self.writer,
182 "{} => {};",
183 global.callbacks[*c].name,
184 DisplayExpression(&init.expression.borrow(), &ctx,),
185 )?;
186 }
187 _ => unreachable!(),
188 }
189 }
190
191 for (p, e) in &global.change_callbacks {
192 self.indent()?;
193 writeln!(
194 self.writer,
195 "changed {} => {};",
196 global.properties[*p].name,
197 DisplayExpression(&e.borrow(), &ctx),
198 )?
199 }
200 for f in &global.functions {
201 self.indent()?;
202 writeln!(
203 self.writer,
204 "function {} ({}) -> {} {{ {} }}; ",
205 f.name,
206 f.args.iter().map(ToString::to_string).join(", "),
207 f.ret_ty,
208 DisplayExpression(&f.code, &ctx)
209 )?;
210 }
211 self.indentation -= 1;
212 self.indent()?;
213 writeln!(self.writer, "}}")
214 }
215
216 fn indent(&mut self) -> Result {
217 for _ in 0..self.indentation {
218 self.writer.write_str(" ")?;
219 }
220 Ok(())
221 }
222}
223
224pub struct DisplayPropertyRef<'a, T>(pub &'a MemberReference, pub &'a EvaluationContext<'a, T>);
225impl<T> Display for DisplayPropertyRef<'_, T> {
226 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result {
227 let ctx = self.1;
228 match &self.0 {
229 MemberReference::Relative { parent_level, local_reference } => {
230 print_local_ref(f, ctx, local_reference, *parent_level)
231 }
232 MemberReference::Global { global_index, member } => {
233 let g = &ctx.compilation_unit.globals[*global_index];
234 match member {
235 LocalMemberIndex::Property(property_index) => {
236 write!(f, "{}.{}", g.name, g.properties[*property_index].name)
237 }
238 LocalMemberIndex::Function(function_index) => {
239 write!(f, "{}.{}", g.name, g.functions[*function_index].name)
240 }
241 _ => write!(f, "<invalid reference in global>"),
242 }
243 }
244 }
245 }
246}
247
248pub struct DisplayLocalRef<'a, T>(pub &'a LocalMemberReference, pub &'a EvaluationContext<'a, T>);
249impl<T> Display for DisplayLocalRef<'_, T> {
250 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result {
251 print_local_ref(f, self.1, self.0, 0)
252 }
253}
254
255fn print_local_ref<T>(
256 f: &mut std::fmt::Formatter<'_>,
257 ctx: &EvaluationContext<T>,
258 local_ref: &LocalMemberReference,
259 parent_level: usize,
260) -> Result {
261 if let Some(g) = ctx.current_global() {
262 match &local_ref.reference {
263 LocalMemberIndex::Property(property_index) => {
264 write!(f, "{}.{}", g.name, g.properties[*property_index].name)
265 }
266 LocalMemberIndex::Function(function_index) => {
267 write!(f, "{}.{}", g.name, g.functions[*function_index].name)
268 }
269 _ => write!(f, "<invalid reference in global>"),
270 }
271 } else {
272 let Some(s) = ctx.parent_sub_component_idx(parent_level) else {
273 return write!(f, "<invalid parent reference>");
274 };
275 let mut sc = &ctx.compilation_unit.sub_components[s];
276
277 for i in &local_ref.sub_component_path {
278 write!(f, "{}.", sc.sub_components[*i].name)?;
279 sc = &ctx.compilation_unit.sub_components[sc.sub_components[*i].ty];
280 }
281 match &local_ref.reference {
282 LocalMemberIndex::Property(property_index) => {
283 write!(f, "{}", sc.properties[*property_index].name)
284 }
285 LocalMemberIndex::Callback(callback_index) => {
286 write!(f, "{}", sc.callbacks[*callback_index].name)
287 }
288 LocalMemberIndex::Function(function_index) => {
289 write!(f, "{}", sc.functions[*function_index].name)
290 }
291 LocalMemberIndex::Native { item_index, prop_name } => {
292 let i = &sc.items[*item_index];
293 write!(f, "{}.{}", i.name, prop_name)
294 }
295 }
296 }
297}
298
299pub struct DisplayExpression<'a, T>(pub &'a Expression, pub &'a EvaluationContext<'a, T>);
300impl<'a, T> Display for DisplayExpression<'a, T> {
301 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result {
302 let ctx = self.1;
303 let e = |e: &'a Expression| DisplayExpression(e, ctx);
304 match self.0 {
305 Expression::StringLiteral(x) => write!(f, "{x:?}"),
306 Expression::NumberLiteral(x) => write!(f, "{x:?}"),
307 Expression::BoolLiteral(x) => write!(f, "{x:?}"),
308 Expression::PropertyReference(x) => write!(f, "{}", DisplayPropertyRef(x, ctx)),
309 Expression::FunctionParameterReference { index } => write!(f, "arg_{index}"),
310 Expression::StoreLocalVariable { name, value } => {
311 write!(f, "{} = {}", name, e(value))
312 }
313 Expression::ReadLocalVariable { name, .. } => write!(f, "{name}"),
314 Expression::StructFieldAccess { base, name } => write!(f, "{}.{}", e(base), name),
315 Expression::ArrayIndex { array, index } => write!(f, "{}[{}]", e(array), e(index)),
316 Expression::Cast { from, to } => write!(f, "{} /*as {:?}*/", e(from), to),
317 Expression::CodeBlock(v) => {
318 write!(f, "{{ {} }}", v.iter().map(e).join("; "))
319 }
320 Expression::BuiltinFunctionCall { function, arguments } => {
321 write!(f, "{:?}({})", function, arguments.iter().map(e).join(", "))
322 }
323 Expression::CallBackCall { callback, arguments } => {
324 write!(
325 f,
326 "{}({})",
327 DisplayPropertyRef(callback, ctx),
328 arguments.iter().map(e).join(", ")
329 )
330 }
331 Expression::FunctionCall { function, arguments } => {
332 write!(
333 f,
334 "{}({})",
335 DisplayPropertyRef(function, ctx),
336 arguments.iter().map(e).join(", ")
337 )
338 }
339 Expression::ItemMemberFunctionCall { function } => {
340 write!(f, "{}()", DisplayPropertyRef(function, ctx))
341 }
342 Expression::ExtraBuiltinFunctionCall { function, arguments, .. } => {
343 write!(f, "{}({})", function, arguments.iter().map(e).join(", "))
344 }
345 Expression::PropertyAssignment { property, value } => {
346 write!(f, "{} = {}", DisplayPropertyRef(property, ctx), e(value))
347 }
348 Expression::ModelDataAssignment { level, value } => {
349 write!(f, "data_{} = {}", level, e(value))
350 }
351 Expression::ArrayIndexAssignment { array, index, value } => {
352 write!(f, "{}[{}] = {}", e(array), e(index), e(value))
353 }
354 Expression::SliceIndexAssignment { slice_name, index, value } => {
355 write!(f, "{}[{}] = {}", slice_name, index, e(value))
356 }
357 Expression::BinaryExpression { lhs, rhs, op } => {
358 write!(f, "({} {} {})", e(lhs), op, e(rhs))
359 }
360 Expression::UnaryOp { sub, op } => write!(f, "{}{}", op, e(sub)),
361 Expression::ImageReference { resource_ref, nine_slice } => {
362 write!(f, "{resource_ref:?}")?;
363 if let Some(nine_slice) = &nine_slice {
364 write!(f, "nine-slice({nine_slice:?})")?;
365 }
366 Ok(())
367 }
368 Expression::Condition { condition, true_expr, false_expr } => {
369 write!(f, "({} ? {} : {})", e(condition), e(true_expr), e(false_expr))
370 }
371 Expression::Array { values, .. } => {
372 write!(f, "[{}]", values.iter().map(e).join(", "))
373 }
374 Expression::Struct { values, .. } => write!(
375 f,
376 "{{ {} }}",
377 values.iter().map(|(k, v)| format!("{}: {}", k, e(v))).join(", ")
378 ),
379 Expression::EasingCurve(x) => write!(f, "{x:?}"),
380 Expression::LinearGradient { angle, stops } => write!(
381 f,
382 "@linear-gradient({}, {})",
383 e(angle),
384 stops.iter().map(|(e1, e2)| format!("{} {}", e(e1), e(e2))).join(", ")
385 ),
386 Expression::RadialGradient { stops } => write!(
387 f,
388 "@radial-gradient(circle, {})",
389 stops.iter().map(|(e1, e2)| format!("{} {}", e(e1), e(e2))).join(", ")
390 ),
391 Expression::ConicGradient { from_angle, stops } => write!(
392 f,
393 "@conic-gradient(from {}, {})",
394 e(from_angle),
395 stops.iter().map(|(e1, e2)| format!("{} {}", e(e1), e(e2))).join(", ")
396 ),
397 Expression::EnumerationValue(x) => write!(f, "{x}"),
398 Expression::LayoutCacheAccess {
399 layout_cache_prop,
400 index,
401 repeater_index: None,
402 ..
403 } => {
404 write!(f, "{}[{}]", DisplayPropertyRef(layout_cache_prop, ctx), index)
405 }
406 Expression::LayoutCacheAccess {
407 layout_cache_prop,
408 index,
409 repeater_index: Some(ri),
410 entries_per_item,
411 } => {
412 write!(
413 f,
414 "{}[{} % {} * {}]",
415 DisplayPropertyRef(layout_cache_prop, ctx),
416 index,
417 e(ri),
418 entries_per_item
419 )
420 }
421 Expression::WithLayoutItemInfo { .. } => write!(f, "WithLayoutItemInfo(TODO)",),
422 Expression::WithGridInputData { .. } => write!(f, "WithGridInputData(TODO)",),
423 Expression::MinMax { ty: _, op, lhs, rhs } => match op {
424 MinMaxOp::Min => write!(f, "min({}, {})", e(lhs), e(rhs)),
425 MinMaxOp::Max => write!(f, "max({}, {})", e(lhs), e(rhs)),
426 },
427 Expression::EmptyComponentFactory => write!(f, "<empty-component-factory>",),
428 Expression::TranslationReference { format_args, string_index, plural } => {
429 match plural {
430 Some(plural) => write!(
431 f,
432 "@tr({:?} % {}, {})",
433 string_index,
434 DisplayExpression(plural, ctx),
435 DisplayExpression(format_args, ctx)
436 ),
437 None => write!(
438 f,
439 "@tr({:?}, {})",
440 string_index,
441 DisplayExpression(format_args, ctx)
442 ),
443 }
444 }
445 }
446 }
447}