sixtyfps_compilerlib/llr/
lower_expression.rs

1// Copyright © SixtyFPS GmbH <info@sixtyfps.io>
2// SPDX-License-Identifier: (GPL-3.0-only OR LicenseRef-SixtyFPS-commercial)
3
4use std::cell::RefCell;
5use std::collections::{BTreeMap, HashMap};
6use std::num::NonZeroUsize;
7use std::rc::{Rc, Weak};
8
9use itertools::Either;
10
11use super::lower_to_item_tree::{LoweredElement, LoweredSubComponentMapping, LoweringState};
12use super::{Animation, PropertyReference};
13use crate::expression_tree::{BuiltinFunction, Expression as tree_Expression};
14use crate::langtype::{EnumerationValue, Type};
15use crate::layout::Orientation;
16use crate::llr::Expression as llr_Expression;
17use crate::namedreference::NamedReference;
18use crate::object_tree::{Element, ElementRc, PropertyAnimation};
19
20pub struct ExpressionContext<'a> {
21    pub component: &'a Rc<crate::object_tree::Component>,
22    pub mapping: &'a LoweredSubComponentMapping,
23    pub state: &'a LoweringState,
24    pub parent: Option<&'a ExpressionContext<'a>>,
25}
26
27impl ExpressionContext<'_> {
28    pub fn map_property_reference(&self, from: &NamedReference) -> PropertyReference {
29        let element = from.element();
30        let enclosing = &element.borrow().enclosing_component.upgrade().unwrap();
31        if !enclosing.is_global() {
32            let mut map = self;
33            let mut level = 0;
34            while !Rc::ptr_eq(&enclosing, map.component) {
35                map = map.parent.unwrap();
36                level += 1;
37            }
38            if let Some(level) = NonZeroUsize::new(level) {
39                return PropertyReference::InParent {
40                    level,
41                    parent_reference: Box::new(
42                        map.mapping.map_property_reference(from, self.state),
43                    ),
44                };
45            }
46        }
47        self.mapping.map_property_reference(from, self.state)
48    }
49}
50
51impl super::TypeResolutionContext for ExpressionContext<'_> {
52    fn property_ty(&self, _: &PropertyReference) -> &Type {
53        todo!()
54    }
55}
56
57pub fn lower_expression(
58    expression: &tree_Expression,
59    ctx: &ExpressionContext<'_>,
60) -> llr_Expression {
61    match expression {
62        tree_Expression::Invalid => {
63            panic!("internal error, encountered invalid expression at code generation time")
64        }
65        tree_Expression::Uncompiled(_) => panic!(),
66        tree_Expression::StringLiteral(s) => llr_Expression::StringLiteral(s.clone()),
67        tree_Expression::NumberLiteral(n, unit) => {
68            llr_Expression::NumberLiteral(unit.normalize(*n))
69        }
70        tree_Expression::BoolLiteral(b) => llr_Expression::BoolLiteral(*b),
71        tree_Expression::CallbackReference(nr) => {
72            llr_Expression::PropertyReference(ctx.map_property_reference(nr))
73        }
74        tree_Expression::PropertyReference(nr) => {
75            llr_Expression::PropertyReference(ctx.map_property_reference(nr))
76        }
77        tree_Expression::BuiltinFunctionReference(_, _) => panic!(),
78        tree_Expression::MemberFunction { .. } => panic!(),
79        tree_Expression::BuiltinMacroReference(_, _) => panic!(),
80        tree_Expression::ElementReference(e) => {
81            // We map an element reference to a reference to the property "" inside that native item
82            llr_Expression::PropertyReference(
83                ctx.map_property_reference(&NamedReference::new(&e.upgrade().unwrap(), "")),
84            )
85        }
86        tree_Expression::RepeaterIndexReference { element } => {
87            repeater_special_property(element, ctx.component, 1)
88        }
89        tree_Expression::RepeaterModelReference { element } => {
90            repeater_special_property(element, ctx.component, 0)
91        }
92        tree_Expression::FunctionParameterReference { index, .. } => {
93            llr_Expression::FunctionParameterReference { index: *index }
94        }
95        tree_Expression::StoreLocalVariable { name, value } => llr_Expression::StoreLocalVariable {
96            name: name.clone(),
97            value: Box::new(lower_expression(value, ctx)),
98        },
99        tree_Expression::ReadLocalVariable { name, ty } => {
100            llr_Expression::ReadLocalVariable { name: name.clone(), ty: ty.clone() }
101        }
102        tree_Expression::StructFieldAccess { base, name } => llr_Expression::StructFieldAccess {
103            base: Box::new(lower_expression(base, ctx)),
104            name: name.clone(),
105        },
106        tree_Expression::ArrayIndex { array, index } => llr_Expression::ArrayIndex {
107            array: Box::new(lower_expression(array, ctx)),
108            index: Box::new(lower_expression(index, ctx)),
109        },
110        tree_Expression::Cast { from, to } => {
111            llr_Expression::Cast { from: Box::new(lower_expression(from, ctx)), to: to.clone() }
112        }
113        tree_Expression::CodeBlock(expr) => {
114            llr_Expression::CodeBlock(expr.iter().map(|e| lower_expression(e, ctx)).collect::<_>())
115        }
116        tree_Expression::FunctionCall { function, arguments, .. } => match &**function {
117            tree_Expression::BuiltinFunctionReference(BuiltinFunction::ShowPopupWindow, _) => {
118                lower_show_popup(arguments, ctx)
119            }
120            tree_Expression::BuiltinFunctionReference(f, _) => {
121                let arguments = arguments.iter().map(|e| lower_expression(e, ctx)).collect::<_>();
122                llr_Expression::BuiltinFunctionCall { function: *f, arguments }
123            }
124            tree_Expression::CallbackReference(nr) => {
125                let arguments = arguments.iter().map(|e| lower_expression(e, ctx)).collect::<_>();
126                llr_Expression::CallBackCall { callback: ctx.map_property_reference(nr), arguments }
127            }
128            _ => panic!("not calling a function"),
129        },
130        tree_Expression::SelfAssignment { lhs, rhs, op } => lower_assignment(lhs, rhs, *op, ctx),
131        tree_Expression::BinaryExpression { lhs, rhs, op } => llr_Expression::BinaryExpression {
132            lhs: Box::new(lower_expression(lhs, ctx)),
133            rhs: Box::new(lower_expression(rhs, ctx)),
134            op: *op,
135        },
136        tree_Expression::UnaryOp { sub, op } => {
137            llr_Expression::UnaryOp { sub: Box::new(lower_expression(sub, ctx)), op: *op }
138        }
139        tree_Expression::ImageReference { resource_ref, .. } => {
140            llr_Expression::ImageReference { resource_ref: resource_ref.clone() }
141        }
142        tree_Expression::Condition { condition, true_expr, false_expr } => {
143            llr_Expression::Condition {
144                condition: Box::new(lower_expression(condition, ctx)),
145                true_expr: Box::new(lower_expression(true_expr, ctx)),
146                false_expr: lower_expression(false_expr, ctx).into(),
147            }
148        }
149        tree_Expression::Array { element_ty, values } => llr_Expression::Array {
150            element_ty: element_ty.clone(),
151            values: values.iter().map(|e| lower_expression(e, ctx)).collect::<_>(),
152            as_model: true,
153        },
154        tree_Expression::Struct { ty, values } => llr_Expression::Struct {
155            ty: ty.clone(),
156            values: values
157                .iter()
158                .map(|(s, e)| (s.clone(), lower_expression(e, ctx)))
159                .collect::<_>(),
160        },
161        tree_Expression::PathData(data) => compile_path(data, ctx),
162        tree_Expression::EasingCurve(x) => llr_Expression::EasingCurve(x.clone()),
163        tree_Expression::LinearGradient { angle, stops } => llr_Expression::LinearGradient {
164            angle: Box::new(lower_expression(angle, ctx)),
165            stops: stops
166                .iter()
167                .map(|(a, b)| (lower_expression(a, ctx), lower_expression(b, ctx)))
168                .collect::<_>(),
169        },
170        tree_Expression::EnumerationValue(e) => llr_Expression::EnumerationValue(e.clone()),
171        tree_Expression::ReturnStatement(x) => {
172            llr_Expression::ReturnStatement(x.as_ref().map(|e| lower_expression(e, ctx).into()))
173        }
174        tree_Expression::LayoutCacheAccess { layout_cache_prop, index, repeater_index } => {
175            llr_Expression::LayoutCacheAccess {
176                layout_cache_prop: ctx.map_property_reference(layout_cache_prop),
177                index: *index,
178                repeater_index: repeater_index.as_ref().map(|e| lower_expression(e, ctx).into()),
179            }
180        }
181        tree_Expression::ComputeLayoutInfo(l, o) => compute_layout_info(l, *o, ctx),
182        tree_Expression::SolveLayout(l, o) => solve_layout(l, *o, ctx),
183    }
184}
185
186fn lower_assignment(
187    lhs: &tree_Expression,
188    rhs: &tree_Expression,
189    op: char,
190    ctx: &ExpressionContext,
191) -> llr_Expression {
192    match lhs {
193        tree_Expression::PropertyReference(nr) => {
194            let rhs = lower_expression(rhs, ctx);
195            let property = ctx.map_property_reference(nr);
196            let value = if op == '=' {
197                rhs
198            } else {
199                llr_Expression::BinaryExpression {
200                    lhs: llr_Expression::PropertyReference(property.clone()).into(),
201                    rhs: rhs.into(),
202                    op,
203                }
204            }
205            .into();
206            llr_Expression::PropertyAssignment { property, value }
207        }
208        tree_Expression::StructFieldAccess { base, name } => {
209            let ty = base.ty();
210
211            static COUNT: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
212            let unique_name = format!(
213                "struct_assignment{}",
214                COUNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
215            );
216            let s = tree_Expression::StoreLocalVariable {
217                name: unique_name.clone(),
218                value: base.clone(),
219            };
220            let lower_base =
221                tree_Expression::ReadLocalVariable { name: unique_name, ty: ty.clone() };
222            let mut values = HashMap::new();
223            match &ty {
224                Type::Struct { fields, .. } => {
225                    for (field, _) in fields {
226                        let e = if field != name {
227                            tree_Expression::StructFieldAccess {
228                                base: lower_base.clone().into(),
229                                name: field.clone(),
230                            }
231                        } else if op == '=' {
232                            rhs.clone()
233                        } else {
234                            tree_Expression::BinaryExpression {
235                                lhs: tree_Expression::StructFieldAccess {
236                                    base: lower_base.clone().into(),
237                                    name: field.clone(),
238                                }
239                                .into(),
240                                rhs: Box::new(rhs.clone()),
241                                op,
242                            }
243                        };
244                        values.insert(field.clone(), e);
245                    }
246                }
247                _ => unreachable!(),
248            }
249            let new_value =
250                tree_Expression::CodeBlock(vec![s, tree_Expression::Struct { ty, values }]);
251            lower_assignment(base, &new_value, '=', ctx)
252        }
253        tree_Expression::RepeaterModelReference { element } => {
254            let rhs = lower_expression(rhs, ctx);
255            let prop = repeater_special_property(element, ctx.component, 0);
256
257            let level = match &prop {
258                llr_Expression::PropertyReference(PropertyReference::InParent {
259                    level, ..
260                }) => (*level).into(),
261                _ => 0,
262            };
263
264            let value = Box::new(if op == '=' {
265                rhs
266            } else {
267                llr_Expression::BinaryExpression { lhs: prop.into(), rhs: rhs.into(), op }
268            });
269
270            llr_Expression::ModelDataAssignment { level, value }
271        }
272        tree_Expression::ArrayIndex { array, index } => {
273            let rhs = lower_expression(rhs, ctx);
274            let array = Box::new(lower_expression(array, ctx));
275            let index = Box::new(lower_expression(index, ctx));
276            let value = Box::new(if op == '=' {
277                rhs
278            } else {
279                // FIXME: this will compute the index and the array twice:
280                // Ideally we should store the index and the array in local variable
281                llr_Expression::BinaryExpression {
282                    lhs: llr_Expression::ArrayIndex { array: array.clone(), index: index.clone() }
283                        .into(),
284                    rhs: rhs.into(),
285                    op,
286                }
287            });
288
289            llr_Expression::ArrayIndexAssignment { array, index, value }
290        }
291        _ => panic!("not a rvalue"),
292    }
293}
294
295fn repeater_special_property(
296    element: &Weak<RefCell<Element>>,
297    component: &Rc<crate::object_tree::Component>,
298    property_index: usize,
299) -> llr_Expression {
300    let mut r = PropertyReference::Local { sub_component_path: vec![], property_index };
301    let enclosing = element.upgrade().unwrap().borrow().enclosing_component.upgrade().unwrap();
302    let mut level = 0;
303    let mut component = component.clone();
304    while !Rc::ptr_eq(&enclosing, &component) {
305        component = component
306            .parent_element
307            .upgrade()
308            .unwrap()
309            .borrow()
310            .enclosing_component
311            .upgrade()
312            .unwrap();
313        level += 1;
314    }
315    if let Some(level) = NonZeroUsize::new(level - 1) {
316        r = PropertyReference::InParent { level, parent_reference: Box::new(r) };
317    }
318    llr_Expression::PropertyReference(r)
319}
320
321fn lower_show_popup(args: &[tree_Expression], ctx: &ExpressionContext) -> llr_Expression {
322    if let [tree_Expression::ElementReference(e)] = args {
323        let popup_window = e.upgrade().unwrap();
324        let pop_comp = popup_window.borrow().enclosing_component.upgrade().unwrap();
325        let parent_component = pop_comp
326            .parent_element
327            .upgrade()
328            .unwrap()
329            .borrow()
330            .enclosing_component
331            .upgrade()
332            .unwrap();
333        let popup_list = parent_component.popup_windows.borrow();
334        let (popup_index, popup) = popup_list
335            .iter()
336            .enumerate()
337            .find(|(_, p)| Rc::ptr_eq(&p.component, &pop_comp))
338            .unwrap();
339        let x = llr_Expression::PropertyReference(ctx.map_property_reference(&popup.x));
340        let y = llr_Expression::PropertyReference(ctx.map_property_reference(&popup.y));
341        let item_ref = lower_expression(
342            &tree_Expression::ElementReference(Rc::downgrade(&popup.parent_element)),
343            ctx,
344        );
345        llr_Expression::BuiltinFunctionCall {
346            function: BuiltinFunction::ShowPopupWindow,
347            arguments: vec![llr_Expression::NumberLiteral(popup_index as _), x, y, item_ref],
348        }
349    } else {
350        panic!("invalid arguments to ShowPopupWindow");
351    }
352}
353
354pub fn lower_animation(a: &PropertyAnimation, ctx: &ExpressionContext<'_>) -> Animation {
355    fn lower_animation_element(a: &ElementRc, ctx: &ExpressionContext<'_>) -> llr_Expression {
356        llr_Expression::Struct {
357            values: animation_fields()
358                .map(|(k, ty)| {
359                    let e = a.borrow().bindings.get(&k).map_or_else(
360                        || llr_Expression::default_value_for_type(&ty).unwrap(),
361                        |v| lower_expression(&v.borrow().expression, ctx),
362                    );
363                    (k, e)
364                })
365                .collect::<_>(),
366            ty: animation_ty(),
367        }
368    }
369
370    fn animation_fields() -> impl Iterator<Item = (String, Type)> {
371        IntoIterator::into_iter([
372            ("duration".to_string(), Type::Int32),
373            ("loop-count".to_string(), Type::Int32),
374            ("easing".to_string(), Type::Easing),
375            ("delay".to_string(), Type::Int32),
376        ])
377    }
378
379    fn animation_ty() -> Type {
380        Type::Struct {
381            fields: animation_fields().collect(),
382            name: Some("PropertyAnimation".into()),
383            node: None,
384        }
385    }
386
387    match a {
388        PropertyAnimation::Static(a) => Animation::Static(lower_animation_element(a, ctx)),
389        PropertyAnimation::Transition { state_ref, animations } => {
390            let set_state = llr_Expression::StoreLocalVariable {
391                name: "state".into(),
392                value: Box::new(lower_expression(state_ref, ctx)),
393            };
394            let animation_ty = animation_ty();
395            let mut get_anim = llr_Expression::default_value_for_type(&animation_ty).unwrap();
396            for tr in animations.iter().rev() {
397                let condition = lower_expression(
398                    &tr.condition(tree_Expression::ReadLocalVariable {
399                        name: "state".into(),
400                        ty: state_ref.ty(),
401                    }),
402                    ctx,
403                );
404                get_anim = llr_Expression::Condition {
405                    condition: Box::new(condition),
406                    true_expr: Box::new(lower_animation_element(&tr.animation, ctx)),
407                    false_expr: Box::new(get_anim),
408                }
409            }
410            let result = llr_Expression::Struct {
411                // This is going to be a tuple
412                ty: Type::Struct {
413                    fields: IntoIterator::into_iter([
414                        ("0".to_string(), animation_ty),
415                        // The type is an instant, which does not exist in our type system
416                        ("1".to_string(), Type::Invalid),
417                    ])
418                    .collect(),
419                    name: None,
420                    node: None,
421                },
422                values: IntoIterator::into_iter([
423                    ("0".to_string(), get_anim),
424                    (
425                        "1".to_string(),
426                        llr_Expression::StructFieldAccess {
427                            base: llr_Expression::ReadLocalVariable {
428                                name: "state".into(),
429                                ty: state_ref.ty(),
430                            }
431                            .into(),
432                            name: "change_time".into(),
433                        },
434                    ),
435                ])
436                .collect(),
437            };
438            Animation::Transition(llr_Expression::CodeBlock(vec![set_state, result]))
439        }
440    }
441}
442
443fn compute_layout_info(
444    l: &crate::layout::Layout,
445    o: Orientation,
446    ctx: &ExpressionContext,
447) -> llr_Expression {
448    match l {
449        crate::layout::Layout::GridLayout(layout) => {
450            let (padding, spacing) = generate_layout_padding_and_spacing(&layout.geometry, o, ctx);
451            let cells = grid_layout_cell_data(layout, o, ctx);
452            llr_Expression::ExtraBuiltinFunctionCall {
453                function: "grid_layout_info".into(),
454                arguments: vec![cells, spacing, padding],
455                return_ty: crate::layout::layout_info_type(),
456            }
457        }
458        crate::layout::Layout::BoxLayout(layout) => {
459            let (padding, spacing) = generate_layout_padding_and_spacing(&layout.geometry, o, ctx);
460            let bld = box_layout_data(layout, o, ctx);
461            let sub_expression = if o == layout.orientation {
462                llr_Expression::ExtraBuiltinFunctionCall {
463                    function: "box_layout_info".into(),
464                    arguments: vec![bld.cells, spacing, padding, bld.alignment],
465                    return_ty: crate::layout::layout_info_type(),
466                }
467            } else {
468                llr_Expression::ExtraBuiltinFunctionCall {
469                    function: "box_layout_info_ortho".into(),
470                    arguments: vec![bld.cells, padding],
471                    return_ty: crate::layout::layout_info_type(),
472                }
473            };
474            match bld.compute_cells {
475                Some((cells_variable, elements)) => llr_Expression::BoxLayoutFunction {
476                    cells_variable,
477                    repeater_indices: None,
478                    elements,
479                    orientation: o,
480                    sub_expression: Box::new(sub_expression),
481                },
482                None => sub_expression,
483            }
484        }
485        crate::layout::Layout::PathLayout(_) => unimplemented!(),
486    }
487}
488
489fn solve_layout(
490    l: &crate::layout::Layout,
491    o: Orientation,
492    ctx: &ExpressionContext,
493) -> llr_Expression {
494    match l {
495        crate::layout::Layout::GridLayout(layout) => {
496            let (padding, spacing) = generate_layout_padding_and_spacing(&layout.geometry, o, ctx);
497            let cells = grid_layout_cell_data(layout, o, ctx);
498            let size = layout_geometry_size(&layout.geometry.rect, o, ctx);
499            if let (Some(button_roles), Orientation::Horizontal) = (&layout.dialog_button_roles, o)
500            {
501                let cells_ty = cells.ty(ctx);
502                let e = crate::typeregister::DIALOG_BUTTON_ROLE_ENUM.with(|e| e.clone());
503                let roles = button_roles
504                    .iter()
505                    .map(|r| {
506                        llr_Expression::EnumerationValue(EnumerationValue {
507                            value: e.values.iter().position(|x| x == r).unwrap() as _,
508                            enumeration: e.clone(),
509                        })
510                    })
511                    .collect();
512                llr_Expression::CodeBlock(vec![
513                    llr_Expression::ComputeDialogLayoutCells {
514                        cells_variable: "cells".into(),
515                        roles: llr_Expression::Array {
516                            element_ty: Type::Enumeration(e),
517                            values: roles,
518                            as_model: false,
519                        }
520                        .into(),
521                        unsorted_cells: Box::new(cells),
522                    },
523                    llr_Expression::ExtraBuiltinFunctionCall {
524                        function: "solve_grid_layout".into(),
525                        arguments: vec![make_struct(
526                            "GridLayoutData".into(),
527                            [
528                                ("size", Type::Float32, size),
529                                ("spacing", Type::Float32, spacing),
530                                ("padding", padding.ty(ctx), padding),
531                                (
532                                    "cells",
533                                    cells_ty.clone(),
534                                    llr_Expression::ReadLocalVariable {
535                                        name: "cells".into(),
536                                        ty: cells_ty,
537                                    },
538                                ),
539                            ],
540                        )],
541                        return_ty: Type::LayoutCache,
542                    },
543                ])
544            } else {
545                llr_Expression::ExtraBuiltinFunctionCall {
546                    function: "solve_grid_layout".into(),
547                    arguments: vec![make_struct(
548                        "GridLayoutData".into(),
549                        [
550                            ("size", Type::Float32, size),
551                            ("spacing", Type::Float32, spacing),
552                            ("padding", padding.ty(ctx), padding),
553                            ("cells", cells.ty(ctx), cells),
554                        ],
555                    )],
556                    return_ty: Type::LayoutCache,
557                }
558            }
559        }
560        crate::layout::Layout::BoxLayout(layout) => {
561            let (padding, spacing) = generate_layout_padding_and_spacing(&layout.geometry, o, ctx);
562            let bld = box_layout_data(layout, o, ctx);
563            let size = layout_geometry_size(&layout.geometry.rect, o, ctx);
564            let data = make_struct(
565                "BoxLayoutData".into(),
566                [
567                    ("size", Type::Float32, size),
568                    ("spacing", Type::Float32, spacing),
569                    ("padding", padding.ty(ctx), padding),
570                    (
571                        "alignment",
572                        crate::typeregister::LAYOUT_ALIGNMENT_ENUM
573                            .with(|e| Type::Enumeration(e.clone())),
574                        bld.alignment,
575                    ),
576                    ("cells", bld.cells.ty(ctx), bld.cells),
577                ],
578            );
579            match bld.compute_cells {
580                Some((cells_variable, elements)) => llr_Expression::BoxLayoutFunction {
581                    cells_variable,
582                    repeater_indices: Some("repeated_indices".into()),
583                    elements,
584                    orientation: o,
585                    sub_expression: Box::new(llr_Expression::ExtraBuiltinFunctionCall {
586                        function: "solve_box_layout".into(),
587                        arguments: vec![
588                            data,
589                            llr_Expression::ReadLocalVariable {
590                                name: "repeated_indices".into(),
591                                ty: Type::Array(Type::Int32.into()),
592                            },
593                        ],
594                        return_ty: Type::LayoutCache,
595                    }),
596                },
597                None => llr_Expression::ExtraBuiltinFunctionCall {
598                    function: "solve_box_layout".into(),
599                    arguments: vec![
600                        data,
601                        llr_Expression::Array {
602                            element_ty: Type::Int32,
603                            values: vec![],
604                            as_model: false,
605                        },
606                    ],
607                    return_ty: Type::LayoutCache,
608                },
609            }
610        }
611        crate::layout::Layout::PathLayout(layout) => {
612            let width = layout_geometry_size(&layout.rect, Orientation::Horizontal, ctx);
613            let height = layout_geometry_size(&layout.rect, Orientation::Vertical, ctx);
614            let elements = compile_path(&layout.path, ctx);
615            let offset = if let Some(expr) = &layout.offset_reference {
616                llr_Expression::PropertyReference(ctx.map_property_reference(expr))
617            } else {
618                llr_Expression::NumberLiteral(0.)
619            };
620
621            // FIXME! repeater
622            let repeater_indices =
623                llr_Expression::Array { element_ty: Type::Int32, values: vec![], as_model: false };
624            let count = layout.elements.len();
625            llr_Expression::ExtraBuiltinFunctionCall {
626                function: "solve_path_layout".into(),
627                arguments: vec![
628                    make_struct(
629                        "PathLayoutData".into(),
630                        [
631                            ("width", Type::Float32, width),
632                            ("height", Type::Float32, height),
633                            ("x", Type::Float32, llr_Expression::NumberLiteral(0.)),
634                            ("y", Type::Float32, llr_Expression::NumberLiteral(0.)),
635                            ("elements", elements.ty(ctx), elements),
636                            ("offset", Type::Int32, offset),
637                            ("item_count", Type::Int32, llr_Expression::NumberLiteral(count as _)),
638                        ],
639                    ),
640                    repeater_indices,
641                ],
642                return_ty: Type::LayoutCache,
643            }
644        }
645    }
646}
647
648struct BoxLayoutDataResult {
649    alignment: llr_Expression,
650    cells: llr_Expression,
651    /// When there are repeater involved, we need to do a BoxLayoutFunction with the
652    /// given cell variable and elements
653    compute_cells: Option<(String, Vec<Either<llr_Expression, usize>>)>,
654}
655
656fn box_layout_data(
657    layout: &crate::layout::BoxLayout,
658    orientation: Orientation,
659    ctx: &ExpressionContext,
660) -> BoxLayoutDataResult {
661    let alignment = if let Some(expr) = &layout.geometry.alignment {
662        llr_Expression::PropertyReference(ctx.map_property_reference(expr))
663    } else {
664        let e = crate::typeregister::LAYOUT_ALIGNMENT_ENUM.with(|e| e.clone());
665        llr_Expression::EnumerationValue(EnumerationValue {
666            value: e.default_value,
667            enumeration: e,
668        })
669    };
670
671    let repeater_count =
672        layout.elems.iter().filter(|i| i.element.borrow().repeated.is_some()).count();
673
674    let element_ty = Type::Struct {
675        fields: IntoIterator::into_iter([(
676            "constraint".to_string(),
677            crate::layout::layout_info_type(),
678        )])
679        .collect(),
680        name: Some("BoxLayoutCellData".into()),
681        node: None,
682    };
683
684    if repeater_count == 0 {
685        let cells = llr_Expression::Array {
686            values: layout
687                .elems
688                .iter()
689                .map(|li| {
690                    let layout_info =
691                        get_layout_info(&li.element, ctx, &li.constraints, orientation);
692                    make_struct(
693                        "BoxLayoutCellData".into(),
694                        [("constraint", crate::layout::layout_info_type(), layout_info)],
695                    )
696                })
697                .collect(),
698            element_ty,
699            as_model: false,
700        };
701        BoxLayoutDataResult { alignment, cells, compute_cells: None }
702    } else {
703        let mut elements = vec![];
704        for item in &layout.elems {
705            if item.element.borrow().repeated.is_some() {
706                let repeater_index =
707                    match ctx.mapping.element_mapping.get(&item.element.clone().into()).unwrap() {
708                        LoweredElement::Repeated { repeated_index } => *repeated_index,
709                        _ => panic!(),
710                    };
711                elements.push(Either::Right(repeater_index))
712            } else {
713                let layout_info =
714                    get_layout_info(&item.element, ctx, &item.constraints, orientation);
715                elements.push(Either::Left(make_struct(
716                    "BoxLayoutCellData".into(),
717                    [("constraint", crate::layout::layout_info_type(), layout_info)],
718                )));
719            }
720        }
721        let cells = llr_Expression::ReadLocalVariable {
722            name: "cells".into(),
723            ty: Type::Array(Box::new(crate::layout::layout_info_type())),
724        };
725        BoxLayoutDataResult { alignment, cells, compute_cells: Some(("cells".into(), elements)) }
726    }
727}
728
729fn grid_layout_cell_data(
730    layout: &crate::layout::GridLayout,
731    orientation: Orientation,
732    ctx: &ExpressionContext,
733) -> llr_Expression {
734    llr_Expression::Array {
735        element_ty: grid_layout_cell_data_ty(),
736        values: layout
737            .elems
738            .iter()
739            .map(|c| {
740                let (col_or_row, span) = c.col_or_row_and_span(orientation);
741                let layout_info =
742                    get_layout_info(&c.item.element, ctx, &c.item.constraints, orientation);
743
744                make_struct(
745                    "GridLayoutCellData".into(),
746                    [
747                        ("constraint", crate::layout::layout_info_type(), layout_info),
748                        ("col_or_row", Type::Int32, llr_Expression::NumberLiteral(col_or_row as _)),
749                        ("span", Type::Int32, llr_Expression::NumberLiteral(span as _)),
750                    ],
751                )
752            })
753            .collect(),
754        as_model: false,
755    }
756}
757
758pub(super) fn grid_layout_cell_data_ty() -> Type {
759    Type::Struct {
760        fields: IntoIterator::into_iter([
761            ("col_or_row".to_string(), Type::Int32),
762            ("span".to_string(), Type::Int32),
763            ("constraint".to_string(), crate::layout::layout_info_type()),
764        ])
765        .collect(),
766        name: Some("GridLayoutCellData".into()),
767        node: None,
768    }
769}
770
771fn generate_layout_padding_and_spacing(
772    layout_geometry: &crate::layout::LayoutGeometry,
773    orientation: Orientation,
774    ctx: &ExpressionContext,
775) -> (llr_Expression, llr_Expression) {
776    let padding_prop = |expr| {
777        if let Some(expr) = expr {
778            llr_Expression::PropertyReference(ctx.map_property_reference(expr))
779        } else {
780            llr_Expression::NumberLiteral(0.)
781        }
782    };
783    let spacing = padding_prop(layout_geometry.spacing.as_ref());
784    let (begin, end) = layout_geometry.padding.begin_end(orientation);
785
786    let padding = make_struct(
787        "Padding".into(),
788        [("begin", Type::Float32, padding_prop(begin)), ("end", Type::Float32, padding_prop(end))],
789    );
790
791    (padding, spacing)
792}
793
794fn layout_geometry_size(
795    rect: &crate::layout::LayoutRect,
796    orientation: Orientation,
797    ctx: &ExpressionContext,
798) -> llr_Expression {
799    match rect.size_reference(orientation) {
800        Some(nr) => llr_Expression::PropertyReference(ctx.map_property_reference(nr)),
801        None => llr_Expression::NumberLiteral(0.),
802    }
803}
804
805pub fn get_layout_info(
806    elem: &ElementRc,
807    ctx: &ExpressionContext,
808    constraints: &crate::layout::LayoutConstraints,
809    orientation: Orientation,
810) -> llr_Expression {
811    let layout_info = if let Some(layout_info_prop) = &elem.borrow().layout_info_prop(orientation) {
812        llr_Expression::PropertyReference(ctx.map_property_reference(layout_info_prop))
813    } else {
814        lower_expression(&crate::layout::implicit_layout_info_call(elem, orientation), ctx)
815    };
816
817    if constraints.has_explicit_restrictions() {
818        let store = llr_Expression::StoreLocalVariable {
819            name: "layout_info".into(),
820            value: layout_info.into(),
821        };
822        let ty = crate::layout::layout_info_type();
823        let fields = match &ty {
824            Type::Struct { fields, .. } => fields,
825            _ => panic!(),
826        };
827        let mut values = fields
828            .keys()
829            .map(|p| {
830                (
831                    p.clone(),
832                    llr_Expression::StructFieldAccess {
833                        base: llr_Expression::ReadLocalVariable {
834                            name: "layout_info".into(),
835                            ty: ty.clone(),
836                        }
837                        .into(),
838                        name: p.clone(),
839                    },
840                )
841            })
842            .collect::<HashMap<_, _>>();
843
844        for (nr, s) in constraints.for_each_restrictions(orientation) {
845            values.insert(
846                s.into(),
847                llr_Expression::PropertyReference(ctx.map_property_reference(nr)),
848            );
849        }
850        llr_Expression::CodeBlock([store, llr_Expression::Struct { ty, values }].into())
851    } else {
852        layout_info
853    }
854}
855
856fn compile_path(path: &crate::expression_tree::Path, ctx: &ExpressionContext) -> llr_Expression {
857    fn llr_path_elements(elements: Vec<llr_Expression>) -> llr_Expression {
858        llr_Expression::Cast {
859            from: llr_Expression::Array {
860                element_ty: Type::Struct {
861                    fields: Default::default(),
862                    name: Some("PathElement".to_owned()),
863                    node: None,
864                },
865                values: elements,
866                as_model: false,
867            }
868            .into(),
869            to: Type::PathData,
870        }
871    }
872
873    match path {
874        crate::expression_tree::Path::Elements(elements) => {
875            let converted_elements = elements
876                .iter()
877                .map(|element| {
878                    let element_type = Type::Struct {
879                        fields: element
880                            .element_type
881                            .properties
882                            .iter()
883                            .map(|(k, v)| (k.clone(), v.ty.clone()))
884                            .collect(),
885                        name: element.element_type.native_class.cpp_type.clone(),
886                        node: None,
887                    };
888
889                    llr_Expression::Struct {
890                        ty: element_type,
891                        values: element
892                            .element_type
893                            .properties
894                            .iter()
895                            .map(|(element_field_name, element_property)| {
896                                (
897                                    element_field_name.clone(),
898                                    element.bindings.get(element_field_name).map_or_else(
899                                        || {
900                                            llr_Expression::default_value_for_type(
901                                                &element_property.ty,
902                                            )
903                                            .unwrap()
904                                        },
905                                        |expr| lower_expression(&expr.borrow().expression, ctx),
906                                    ),
907                                )
908                            })
909                            .collect(),
910                    }
911                })
912                .collect();
913            llr_path_elements(converted_elements)
914        }
915        crate::expression_tree::Path::Events(events, points) => {
916            if events.is_empty() || points.is_empty() {
917                return llr_path_elements(vec![]);
918            }
919
920            let events: Vec<_> =
921                events.into_iter().map(|event| lower_expression(&event, &ctx)).collect();
922
923            let event_type = events.first().unwrap().ty(ctx).clone();
924
925            let points: Vec<_> =
926                points.into_iter().map(|point| lower_expression(&point, &ctx)).collect();
927
928            let point_type = points.first().unwrap().ty(ctx).clone();
929
930            llr_Expression::Cast {
931                from: llr_Expression::Struct {
932                    ty: Type::Struct {
933                        fields: IntoIterator::into_iter([
934                            ("events".to_owned(), Type::Array(event_type.clone().into())),
935                            ("points".to_owned(), Type::Array(point_type.clone().into())),
936                        ])
937                        .collect(),
938                        name: None,
939                        node: None,
940                    },
941                    values: IntoIterator::into_iter([
942                        (
943                            "events".to_owned(),
944                            llr_Expression::Array {
945                                element_ty: event_type,
946                                values: events,
947                                as_model: false,
948                            },
949                        ),
950                        (
951                            "points".to_owned(),
952                            llr_Expression::Array {
953                                element_ty: point_type.clone(),
954                                values: points,
955                                as_model: false,
956                            },
957                        ),
958                    ])
959                    .collect(),
960                }
961                .into(),
962                to: Type::PathData,
963            }
964        }
965    }
966}
967
968fn make_struct(
969    name: String,
970    it: impl IntoIterator<Item = (&'static str, Type, llr_Expression)>,
971) -> llr_Expression {
972    let mut fields = BTreeMap::<String, Type>::new();
973    let mut values = HashMap::<String, llr_Expression>::new();
974    for (name, ty, expr) in it {
975        fields.insert(name.to_string(), ty);
976        values.insert(name.to_string(), expr);
977    }
978
979    llr_Expression::Struct { ty: Type::Struct { fields, name: Some(name), node: None }, values }
980}