i_slint_compiler/llr/
pretty_print.rs

1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
3
4use 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}