Skip to main content

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, 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}