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