1use std::cell::RefCell;
5use std::collections::{BTreeMap, HashMap};
6use std::rc::{Rc, Weak};
7
8use itertools::Either;
9use smol_str::{SmolStr, format_smolstr};
10
11use super::lower_to_item_tree::{LoweredElement, LoweredSubComponentMapping, LoweringState};
12use super::{
13 Animation, GridLayoutRepeatedElement, LayoutRepeatedElement, LocalMemberReference,
14 MemberReference, PropertyIdx,
15};
16use crate::expression_tree::{BuiltinFunction, Callable, Expression as tree_Expression};
17use crate::langtype::{BuiltinPrivateStruct, EnumerationValue, Struct, StructName, Type};
18use crate::layout::{GridLayoutCell, Orientation, RowColExpr};
19use crate::llr::ArrayOutput as llr_ArrayOutput;
20use crate::llr::Expression as llr_Expression;
21use crate::namedreference::NamedReference;
22use crate::object_tree::{Element, ElementRc, PropertyAnimation};
23use crate::typeregister::BUILTIN;
24
25pub struct ExpressionLoweringCtxInner<'a> {
26 pub component: &'a Rc<crate::object_tree::Component>,
27 pub mapping: &'a LoweredSubComponentMapping,
29 pub parent: Option<&'a ExpressionLoweringCtxInner<'a>>,
30}
31#[derive(derive_more::Deref)]
32pub struct ExpressionLoweringCtx<'a> {
33 pub state: &'a mut LoweringState,
34 #[deref]
35 pub inner: ExpressionLoweringCtxInner<'a>,
36}
37
38impl ExpressionLoweringCtx<'_> {
39 pub fn map_property_reference(&self, from: &NamedReference) -> MemberReference {
40 let element = from.element();
41 let enclosing = &element.borrow().enclosing_component.upgrade().unwrap();
42 let mut level = 0;
43 let mut map = &self.inner;
44 if !enclosing.is_global() {
45 while !Rc::ptr_eq(enclosing, map.component) {
46 map = map.parent.unwrap_or_else(|| {
47 panic!(
48 "Could not find component for property reference {from:?} in component {:?}. Started with enclosing={:?}",
49 self.component.id,
50 enclosing.id
51 )
52 });
53 level += 1;
54 }
55 }
56 let mut r = map.mapping.map_property_reference(from, self.state);
57 if let MemberReference::Relative { parent_level, .. } = &mut r {
58 *parent_level += level;
59 }
60 r
61 }
62}
63
64impl super::TypeResolutionContext for ExpressionLoweringCtx<'_> {
65 fn property_ty(&self, _: &MemberReference) -> &Type {
66 unimplemented!()
67 }
68}
69
70pub fn lower_expression(
71 expression: &tree_Expression,
72 ctx: &mut ExpressionLoweringCtx<'_>,
73) -> llr_Expression {
74 match expression {
75 tree_Expression::Invalid => {
76 panic!("internal error, encountered invalid expression at code generation time")
77 }
78 tree_Expression::Uncompiled(_) => panic!(),
79 tree_Expression::StringLiteral(s) => llr_Expression::StringLiteral(s.clone()),
80 tree_Expression::NumberLiteral(n, unit) => {
81 llr_Expression::NumberLiteral(unit.normalize(*n))
82 }
83 tree_Expression::BoolLiteral(b) => llr_Expression::BoolLiteral(*b),
84 tree_Expression::PropertyReference(nr) => {
85 llr_Expression::PropertyReference(ctx.map_property_reference(nr))
86 }
87 tree_Expression::ElementReference(e) => {
88 let elem = e.upgrade().unwrap();
89 let enclosing = elem.borrow().enclosing_component.upgrade().unwrap();
90 if Rc::ptr_eq(&elem, &enclosing.root_element)
92 && let Some(idx) = ctx
93 .component
94 .menu_item_tree
95 .borrow()
96 .iter()
97 .position(|c| Rc::ptr_eq(c, &enclosing))
98 {
99 return llr_Expression::NumberLiteral(idx as _);
100 }
101
102 llr_Expression::PropertyReference(
104 ctx.map_property_reference(&NamedReference::new(&elem, SmolStr::default())),
105 )
106 }
107 tree_Expression::RepeaterIndexReference { element } => llr_Expression::PropertyReference(
108 repeater_special_property(element, ctx.component, PropertyIdx::REPEATER_INDEX),
109 ),
110 tree_Expression::RepeaterModelReference { element } => llr_Expression::PropertyReference(
111 repeater_special_property(element, ctx.component, PropertyIdx::REPEATER_DATA),
112 ),
113 tree_Expression::FunctionParameterReference { index, .. } => {
114 llr_Expression::FunctionParameterReference { index: *index }
115 }
116 tree_Expression::StoreLocalVariable { name, value } => llr_Expression::StoreLocalVariable {
117 name: name.clone(),
118 value: Box::new(lower_expression(value, ctx)),
119 },
120 tree_Expression::ReadLocalVariable { name, ty } => {
121 llr_Expression::ReadLocalVariable { name: name.clone(), ty: ty.clone() }
122 }
123 tree_Expression::StructFieldAccess { base, name } => llr_Expression::StructFieldAccess {
124 base: Box::new(lower_expression(base, ctx)),
125 name: name.clone(),
126 },
127 tree_Expression::ArrayIndex { array, index } => llr_Expression::ArrayIndex {
128 array: Box::new(lower_expression(array, ctx)),
129 index: Box::new(lower_expression(index, ctx)),
130 },
131 tree_Expression::Cast { from, to } => {
132 llr_Expression::Cast { from: Box::new(lower_expression(from, ctx)), to: to.clone() }
133 }
134 tree_Expression::CodeBlock(expr) => {
135 llr_Expression::CodeBlock(expr.iter().map(|e| lower_expression(e, ctx)).collect::<_>())
136 }
137 tree_Expression::FunctionCall { function, arguments, .. } => match function {
138 Callable::Builtin(BuiltinFunction::RestartTimer) => lower_restart_timer(arguments),
139 Callable::Builtin(BuiltinFunction::ShowPopupWindow) => {
140 lower_show_popup_window(arguments, ctx)
141 }
142 Callable::Builtin(BuiltinFunction::ClosePopupWindow) => {
143 lower_close_popup_window(arguments, ctx)
144 }
145 Callable::Builtin(f) => {
146 let mut arguments =
147 arguments.iter().map(|e| lower_expression(e, ctx)).collect::<Vec<_>>();
148 #[allow(clippy::collapsible_if)]
150 if *f == BuiltinFunction::Translate {
151 if let llr_Expression::Array { output, .. } = &mut arguments[3] {
152 *output = llr_ArrayOutput::Slice;
153 }
154 #[cfg(feature = "bundle-translations")]
155 if let Some(translation_builder) = ctx.state.translation_builder.as_mut() {
156 return translation_builder.lower_translate_call(arguments);
157 }
158 }
159 llr_Expression::BuiltinFunctionCall { function: f.clone(), arguments }
160 }
161 Callable::Callback(nr) => {
162 let arguments = arguments.iter().map(|e| lower_expression(e, ctx)).collect::<_>();
163 llr_Expression::CallBackCall { callback: ctx.map_property_reference(nr), arguments }
164 }
165 Callable::Function(nr)
166 if nr
167 .element()
168 .borrow()
169 .native_class()
170 .is_some_and(|n| n.properties.contains_key(nr.name())) =>
171 {
172 llr_Expression::ItemMemberFunctionCall { function: ctx.map_property_reference(nr) }
173 }
174 Callable::Function(nr) => {
175 let arguments = arguments.iter().map(|e| lower_expression(e, ctx)).collect::<_>();
176 llr_Expression::FunctionCall { function: ctx.map_property_reference(nr), arguments }
177 }
178 },
179 tree_Expression::SelfAssignment { lhs, rhs, op, .. } => {
180 lower_assignment(lhs, rhs, *op, ctx)
181 }
182 tree_Expression::BinaryExpression { lhs, rhs, op } => llr_Expression::BinaryExpression {
183 lhs: Box::new(lower_expression(lhs, ctx)),
184 rhs: Box::new(lower_expression(rhs, ctx)),
185 op: *op,
186 },
187 tree_Expression::UnaryOp { sub, op } => {
188 llr_Expression::UnaryOp { sub: Box::new(lower_expression(sub, ctx)), op: *op }
189 }
190 tree_Expression::ImageReference { resource_ref, nine_slice, .. } => {
191 llr_Expression::ImageReference {
192 resource_ref: resource_ref.clone(),
193 nine_slice: *nine_slice,
194 }
195 }
196 tree_Expression::Condition { condition, true_expr, false_expr } => {
197 let (true_ty, false_ty) = (true_expr.ty(), false_expr.ty());
198 llr_Expression::Condition {
199 condition: Box::new(lower_expression(condition, ctx)),
200 true_expr: Box::new(lower_expression(true_expr, ctx)),
201 false_expr: if false_ty == Type::Invalid
202 || false_ty == Type::Void
203 || true_ty == false_ty
204 {
205 Box::new(lower_expression(false_expr, ctx))
206 } else {
207 Box::new(llr_Expression::Cast {
209 from: Box::new(lower_expression(false_expr, ctx)),
210 to: Type::Void,
211 })
212 },
213 }
214 }
215 tree_Expression::Array { element_ty, values } => llr_Expression::Array {
216 element_ty: element_ty.clone(),
217 values: values.iter().map(|e| lower_expression(e, ctx)).collect::<_>(),
218 output: llr_ArrayOutput::Model,
219 },
220 tree_Expression::Struct { ty, values } => llr_Expression::Struct {
221 ty: ty.clone(),
222 values: values
223 .iter()
224 .map(|(s, e)| (s.clone(), lower_expression(e, ctx)))
225 .collect::<_>(),
226 },
227 tree_Expression::PathData(data) => compile_path(data, ctx),
228 tree_Expression::EasingCurve(x) => llr_Expression::EasingCurve(x.clone()),
229 tree_Expression::LinearGradient { angle, stops } => llr_Expression::LinearGradient {
230 angle: Box::new(lower_expression(angle, ctx)),
231 stops: stops
232 .iter()
233 .map(|(a, b)| (lower_expression(a, ctx), lower_expression(b, ctx)))
234 .collect::<_>(),
235 },
236 tree_Expression::RadialGradient { stops } => llr_Expression::RadialGradient {
237 stops: stops
238 .iter()
239 .map(|(a, b)| (lower_expression(a, ctx), lower_expression(b, ctx)))
240 .collect::<_>(),
241 },
242 tree_Expression::ConicGradient { from_angle, stops } => llr_Expression::ConicGradient {
243 from_angle: Box::new(lower_expression(from_angle, ctx)),
244 stops: stops
245 .iter()
246 .map(|(a, b)| (lower_expression(a, ctx), lower_expression(b, ctx)))
247 .collect::<_>(),
248 },
249 tree_Expression::EnumerationValue(e) => llr_Expression::EnumerationValue(e.clone()),
250 tree_Expression::ReturnStatement(..) => {
251 panic!("The remove return pass should have removed all return")
252 }
253 tree_Expression::LayoutCacheAccess {
254 layout_cache_prop,
255 index,
256 repeater_index,
257 entries_per_item,
258 } => llr_Expression::LayoutCacheAccess {
259 layout_cache_prop: ctx.map_property_reference(layout_cache_prop),
260 index: *index,
261 repeater_index: repeater_index.as_ref().map(|e| lower_expression(e, ctx).into()),
262 entries_per_item: *entries_per_item,
263 },
264 tree_Expression::OrganizeGridLayout(l) => organize_grid_layout(l, ctx),
265 tree_Expression::ComputeLayoutInfo(l, o) => compute_layout_info(l, *o, ctx),
266 tree_Expression::ComputeGridLayoutInfo {
267 layout_organized_data_prop,
268 layout,
269 orientation,
270 } => compute_grid_layout_info(layout_organized_data_prop, layout, *orientation, ctx),
271 tree_Expression::SolveLayout(l, o) => solve_layout(l, *o, ctx),
272 tree_Expression::SolveGridLayout { layout_organized_data_prop, layout, orientation } => {
273 solve_grid_layout(layout_organized_data_prop, layout, *orientation, ctx)
274 }
275 tree_Expression::MinMax { ty, op, lhs, rhs } => llr_Expression::MinMax {
276 ty: ty.clone(),
277 op: *op,
278 lhs: Box::new(lower_expression(lhs, ctx)),
279 rhs: Box::new(lower_expression(rhs, ctx)),
280 },
281 tree_Expression::EmptyComponentFactory => llr_Expression::EmptyComponentFactory,
282 tree_Expression::DebugHook { expression, .. } => lower_expression(expression, ctx),
283 }
284}
285
286fn lower_assignment(
287 lhs: &tree_Expression,
288 rhs: &tree_Expression,
289 op: char,
290 ctx: &mut ExpressionLoweringCtx,
291) -> llr_Expression {
292 match lhs {
293 tree_Expression::PropertyReference(nr) => {
294 let rhs = lower_expression(rhs, ctx);
295 let property = ctx.map_property_reference(nr);
296 let value = if op == '=' {
297 rhs
298 } else {
299 llr_Expression::BinaryExpression {
300 lhs: llr_Expression::PropertyReference(property.clone()).into(),
301 rhs: rhs.into(),
302 op,
303 }
304 }
305 .into();
306 llr_Expression::PropertyAssignment { property, value }
307 }
308 tree_Expression::StructFieldAccess { base, name } => {
309 let ty = base.ty();
310
311 static COUNT: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
312 let unique_name = format_smolstr!(
313 "struct_assignment{}",
314 COUNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
315 );
316 let s = tree_Expression::StoreLocalVariable {
317 name: unique_name.clone(),
318 value: base.clone(),
319 };
320 let lower_base =
321 tree_Expression::ReadLocalVariable { name: unique_name, ty: ty.clone() };
322 let mut values = HashMap::new();
323 let Type::Struct(ty) = ty else { unreachable!() };
324
325 for field in ty.fields.keys() {
326 let e = if field != name {
327 tree_Expression::StructFieldAccess {
328 base: lower_base.clone().into(),
329 name: field.clone(),
330 }
331 } else if op == '=' {
332 rhs.clone()
333 } else {
334 tree_Expression::BinaryExpression {
335 lhs: tree_Expression::StructFieldAccess {
336 base: lower_base.clone().into(),
337 name: field.clone(),
338 }
339 .into(),
340 rhs: Box::new(rhs.clone()),
341 op,
342 }
343 };
344 values.insert(field.clone(), e);
345 }
346
347 let new_value =
348 tree_Expression::CodeBlock(vec![s, tree_Expression::Struct { ty, values }]);
349 lower_assignment(base, &new_value, '=', ctx)
350 }
351 tree_Expression::RepeaterModelReference { element } => {
352 let rhs = lower_expression(rhs, ctx);
353 let prop =
354 repeater_special_property(element, ctx.component, PropertyIdx::REPEATER_DATA);
355
356 let level = match &prop {
357 MemberReference::Relative { parent_level, .. } => *parent_level,
358 _ => 0,
359 };
360
361 let value = Box::new(if op == '=' {
362 rhs
363 } else {
364 llr_Expression::BinaryExpression {
365 lhs: llr_Expression::PropertyReference(prop).into(),
366 rhs: rhs.into(),
367 op,
368 }
369 });
370
371 llr_Expression::ModelDataAssignment { level, value }
372 }
373 tree_Expression::ArrayIndex { array, index } => {
374 let rhs = lower_expression(rhs, ctx);
375 let array = Box::new(lower_expression(array, ctx));
376 let index = Box::new(lower_expression(index, ctx));
377 let value = Box::new(if op == '=' {
378 rhs
379 } else {
380 llr_Expression::BinaryExpression {
383 lhs: llr_Expression::ArrayIndex { array: array.clone(), index: index.clone() }
384 .into(),
385 rhs: rhs.into(),
386 op,
387 }
388 });
389
390 llr_Expression::ArrayIndexAssignment { array, index, value }
391 }
392 _ => panic!("not a rvalue"),
393 }
394}
395
396pub fn repeater_special_property(
397 element: &Weak<RefCell<Element>>,
398 component: &Rc<crate::object_tree::Component>,
399 property_index: PropertyIdx,
400) -> MemberReference {
401 let enclosing = element.upgrade().unwrap().borrow().enclosing_component.upgrade().unwrap();
402 let mut parent_level = 0;
403 let mut component = component.clone();
404 while !Rc::ptr_eq(&enclosing, &component) {
405 component = component
406 .parent_element
407 .upgrade()
408 .unwrap()
409 .borrow()
410 .enclosing_component
411 .upgrade()
412 .unwrap();
413 parent_level += 1;
414 }
415 MemberReference::Relative {
416 parent_level: parent_level - 1,
417 local_reference: LocalMemberReference {
418 sub_component_path: Vec::new(),
419 reference: property_index.into(),
420 },
421 }
422}
423
424fn lower_restart_timer(args: &[tree_Expression]) -> llr_Expression {
425 if let [tree_Expression::ElementReference(e)] = args {
426 let timer_element = e.upgrade().unwrap();
427 let timer_comp = timer_element.borrow().enclosing_component.upgrade().unwrap();
428
429 let timer_list = timer_comp.timers.borrow();
430 let timer_index = timer_list
431 .iter()
432 .position(|t| Rc::ptr_eq(&t.element.upgrade().unwrap(), &timer_element))
433 .unwrap();
434
435 llr_Expression::BuiltinFunctionCall {
436 function: BuiltinFunction::RestartTimer,
437 arguments: vec![llr_Expression::NumberLiteral(timer_index as _)],
438 }
439 } else {
440 panic!("invalid arguments to RestartTimer");
441 }
442}
443
444fn lower_show_popup_window(
445 args: &[tree_Expression],
446 ctx: &mut ExpressionLoweringCtx,
447) -> llr_Expression {
448 if let [tree_Expression::ElementReference(e)] = args {
449 let popup_window = e.upgrade().unwrap();
450 let pop_comp = popup_window.borrow().enclosing_component.upgrade().unwrap();
451 let parent_component = pop_comp
452 .parent_element
453 .upgrade()
454 .unwrap()
455 .borrow()
456 .enclosing_component
457 .upgrade()
458 .unwrap();
459 let popup_list = parent_component.popup_windows.borrow();
460 let (popup_index, popup) = popup_list
461 .iter()
462 .enumerate()
463 .find(|(_, p)| Rc::ptr_eq(&p.component, &pop_comp))
464 .unwrap();
465 let item_ref = lower_expression(
466 &tree_Expression::ElementReference(Rc::downgrade(&popup.parent_element)),
467 ctx,
468 );
469
470 llr_Expression::BuiltinFunctionCall {
471 function: BuiltinFunction::ShowPopupWindow,
472 arguments: vec![
473 llr_Expression::NumberLiteral(popup_index as _),
474 llr_Expression::EnumerationValue(popup.close_policy.clone()),
475 item_ref,
476 ],
477 }
478 } else {
479 panic!("invalid arguments to ShowPopupWindow");
480 }
481}
482
483fn lower_close_popup_window(
484 args: &[tree_Expression],
485 ctx: &mut ExpressionLoweringCtx,
486) -> llr_Expression {
487 if let [tree_Expression::ElementReference(e)] = args {
488 let popup_window = e.upgrade().unwrap();
489 let pop_comp = popup_window.borrow().enclosing_component.upgrade().unwrap();
490 let parent_component = pop_comp
491 .parent_element
492 .upgrade()
493 .unwrap()
494 .borrow()
495 .enclosing_component
496 .upgrade()
497 .unwrap();
498 let popup_list = parent_component.popup_windows.borrow();
499 let (popup_index, popup) = popup_list
500 .iter()
501 .enumerate()
502 .find(|(_, p)| Rc::ptr_eq(&p.component, &pop_comp))
503 .unwrap();
504 let item_ref = lower_expression(
505 &tree_Expression::ElementReference(Rc::downgrade(&popup.parent_element)),
506 ctx,
507 );
508
509 llr_Expression::BuiltinFunctionCall {
510 function: BuiltinFunction::ClosePopupWindow,
511 arguments: vec![llr_Expression::NumberLiteral(popup_index as _), item_ref],
512 }
513 } else {
514 panic!("invalid arguments to ShowPopupWindow");
515 }
516}
517
518pub fn lower_animation(a: &PropertyAnimation, ctx: &mut ExpressionLoweringCtx<'_>) -> Animation {
519 fn lower_animation_element(
520 a: &ElementRc,
521 ctx: &mut ExpressionLoweringCtx<'_>,
522 ) -> llr_Expression {
523 llr_Expression::Struct {
524 values: animation_fields()
525 .map(|(k, ty)| {
526 let e = a.borrow().bindings.get(&k).map_or_else(
527 || llr_Expression::default_value_for_type(&ty).unwrap(),
528 |v| lower_expression(&v.borrow().expression, ctx),
529 );
530 (k, e)
531 })
532 .collect::<_>(),
533 ty: animation_ty(),
534 }
535 }
536
537 fn animation_fields() -> impl Iterator<Item = (SmolStr, Type)> {
538 IntoIterator::into_iter([
539 (SmolStr::new_static("duration"), Type::Int32),
540 (SmolStr::new_static("iteration-count"), Type::Float32),
541 (
542 SmolStr::new_static("direction"),
543 Type::Enumeration(BUILTIN.with(|e| e.enums.AnimationDirection.clone())),
544 ),
545 (SmolStr::new_static("easing"), Type::Easing),
546 (SmolStr::new_static("delay"), Type::Int32),
547 ])
548 }
549
550 fn animation_ty() -> Rc<Struct> {
551 Rc::new(Struct {
552 fields: animation_fields().collect(),
553 name: BuiltinPrivateStruct::PropertyAnimation.into(),
554 })
555 }
556
557 match a {
558 PropertyAnimation::Static(a) => Animation::Static(lower_animation_element(a, ctx)),
559 PropertyAnimation::Transition { state_ref, animations } => {
560 let set_state = llr_Expression::StoreLocalVariable {
561 name: "state".into(),
562 value: Box::new(lower_expression(state_ref, ctx)),
563 };
564 let animation_ty = Type::Struct(animation_ty());
565 let mut get_anim = llr_Expression::default_value_for_type(&animation_ty).unwrap();
566 for tr in animations.iter().rev() {
567 let condition = lower_expression(
568 &tr.condition(tree_Expression::ReadLocalVariable {
569 name: "state".into(),
570 ty: state_ref.ty(),
571 }),
572 ctx,
573 );
574 get_anim = llr_Expression::Condition {
575 condition: Box::new(condition),
576 true_expr: Box::new(lower_animation_element(&tr.animation, ctx)),
577 false_expr: Box::new(get_anim),
578 }
579 }
580 let result = llr_Expression::Struct {
581 ty: Rc::new(Struct {
583 fields: IntoIterator::into_iter([
584 (SmolStr::new_static("0"), animation_ty),
585 (SmolStr::new_static("1"), Type::Invalid),
587 ])
588 .collect(),
589 name: StructName::None,
590 }),
591 values: IntoIterator::into_iter([
592 (SmolStr::new_static("0"), get_anim),
593 (
594 SmolStr::new_static("1"),
595 llr_Expression::StructFieldAccess {
596 base: llr_Expression::ReadLocalVariable {
597 name: "state".into(),
598 ty: state_ref.ty(),
599 }
600 .into(),
601 name: "change_time".into(),
602 },
603 ),
604 ])
605 .collect(),
606 };
607 Animation::Transition(llr_Expression::CodeBlock(vec![set_state, result]))
608 }
609 }
610}
611
612fn empty_int32_slice() -> llr_Expression {
613 llr_Expression::Array {
614 element_ty: Type::Int32,
615 values: Vec::new(),
616 output: llr_ArrayOutput::Slice,
617 }
618}
619
620fn compute_grid_layout_info(
621 layout_organized_data_prop: &NamedReference,
622 layout: &crate::layout::GridLayout,
623 o: Orientation,
624 ctx: &mut ExpressionLoweringCtx,
625) -> llr_Expression {
626 let (padding, spacing) = generate_layout_padding_and_spacing(&layout.geometry, o, ctx);
627 let organized_cells = ctx.map_property_reference(layout_organized_data_prop);
628 let constraints_result = grid_layout_cell_constraints(layout, o, ctx);
629 let orientation_literal = llr_Expression::EnumerationValue(EnumerationValue {
630 value: o as _,
631 enumeration: crate::typeregister::BUILTIN.with(|b| b.enums.Orientation.clone()),
632 });
633
634 let sub_expression = llr_Expression::ExtraBuiltinFunctionCall {
635 function: "grid_layout_info".into(),
636 arguments: vec![
637 llr_Expression::PropertyReference(organized_cells),
638 constraints_result.cells,
639 if constraints_result.compute_cells.is_none() {
640 empty_int32_slice()
641 } else {
642 llr_Expression::ReadLocalVariable {
643 name: "repeated_indices".into(),
644 ty: Type::Array(Type::Int32.into()),
645 }
646 },
647 if constraints_result.compute_cells.is_none() {
648 empty_int32_slice()
649 } else {
650 llr_Expression::ReadLocalVariable {
651 name: "repeater_steps".into(),
652 ty: Type::Array(Type::Int32.into()),
653 }
654 },
655 spacing,
656 padding,
657 orientation_literal,
658 ],
659 return_ty: crate::typeregister::layout_info_type().into(),
660 };
661 match constraints_result.compute_cells {
662 Some((cells_variable, elements)) => llr_Expression::WithLayoutItemInfo {
663 cells_variable,
664 repeater_indices_var_name: Some("repeated_indices".into()),
665 repeater_steps_var_name: Some("repeater_steps".into()),
666 elements,
667 orientation: o,
668 sub_expression: Box::new(sub_expression),
669 },
670 None => sub_expression,
671 }
672}
673
674fn compute_layout_info(
675 l: &crate::layout::Layout,
676 o: Orientation,
677 ctx: &mut ExpressionLoweringCtx,
678) -> llr_Expression {
679 match l {
680 crate::layout::Layout::GridLayout(_) => {
681 panic!("compute_layout_info called on GridLayout, use compute_grid_layout_info");
682 }
683 crate::layout::Layout::BoxLayout(layout) => {
684 let (padding, spacing) = generate_layout_padding_and_spacing(&layout.geometry, o, ctx);
685 let bld = box_layout_data(layout, o, ctx);
686 let sub_expression = if o == layout.orientation {
687 llr_Expression::ExtraBuiltinFunctionCall {
688 function: "box_layout_info".into(),
689 arguments: vec![bld.cells, spacing, padding, bld.alignment],
690 return_ty: crate::typeregister::layout_info_type().into(),
691 }
692 } else {
693 llr_Expression::ExtraBuiltinFunctionCall {
694 function: "box_layout_info_ortho".into(),
695 arguments: vec![bld.cells, padding],
696 return_ty: crate::typeregister::layout_info_type().into(),
697 }
698 };
699 match bld.compute_cells {
700 Some((cells_variable, elements)) => llr_Expression::WithLayoutItemInfo {
701 cells_variable,
702 repeater_indices_var_name: None,
703 repeater_steps_var_name: None,
704 elements,
705 orientation: o,
706 sub_expression: Box::new(sub_expression),
707 },
708 None => sub_expression,
709 }
710 }
711 }
712}
713
714fn organize_grid_layout(
715 layout: &crate::layout::GridLayout,
716 ctx: &mut ExpressionLoweringCtx,
717) -> llr_Expression {
718 let input_data = grid_layout_input_data(layout, ctx);
719
720 if let Some(button_roles) = &layout.dialog_button_roles {
721 let e = crate::typeregister::BUILTIN.with(|e| e.enums.DialogButtonRole.clone());
722 let roles = button_roles
723 .iter()
724 .map(|r| {
725 llr_Expression::EnumerationValue(EnumerationValue {
726 value: e.values.iter().position(|x| x == r).unwrap() as _,
727 enumeration: e.clone(),
728 })
729 })
730 .collect();
731 let roles_expr = llr_Expression::Array {
732 element_ty: Type::Enumeration(e),
733 values: roles,
734 output: llr_ArrayOutput::Slice,
735 };
736 llr_Expression::ExtraBuiltinFunctionCall {
737 function: "organize_dialog_button_layout".into(),
738 arguments: vec![input_data.cells, roles_expr],
739 return_ty: Type::Array(Type::Int32.into()),
740 }
741 } else {
742 let sub_expression = llr_Expression::ExtraBuiltinFunctionCall {
743 function: "organize_grid_layout".into(),
744 arguments: vec![
745 input_data.cells,
746 if input_data.compute_cells.is_none() {
747 empty_int32_slice()
748 } else {
749 llr_Expression::ReadLocalVariable {
750 name: SmolStr::new_static("repeated_indices"),
751 ty: Type::Array(Type::Int32.into()),
752 }
753 },
754 if input_data.compute_cells.is_none() {
755 empty_int32_slice()
756 } else {
757 llr_Expression::ReadLocalVariable {
758 name: SmolStr::new_static("repeater_steps"),
759 ty: Type::Array(Type::Int32.into()),
760 }
761 },
762 ],
763 return_ty: Type::Array(Type::Int32.into()),
764 };
765 if let Some((cells_variable, elements)) = input_data.compute_cells {
766 llr_Expression::WithGridInputData {
767 cells_variable,
768 repeater_indices_var_name: SmolStr::new_static("repeated_indices"),
769 repeater_steps_var_name: SmolStr::new_static("repeater_steps"),
770 elements,
771 sub_expression: Box::new(sub_expression),
772 }
773 } else {
774 sub_expression
775 }
776 }
777}
778
779fn solve_grid_layout(
780 layout_organized_data_prop: &NamedReference,
781 layout: &crate::layout::GridLayout,
782 o: Orientation,
783 ctx: &mut ExpressionLoweringCtx,
784) -> llr_Expression {
785 let (padding, spacing) = generate_layout_padding_and_spacing(&layout.geometry, o, ctx);
786 let cells = ctx.map_property_reference(layout_organized_data_prop);
787 let size = layout_geometry_size(&layout.geometry.rect, o, ctx);
788 let orientation_expr = llr_Expression::EnumerationValue(EnumerationValue {
789 value: o as _,
790 enumeration: crate::typeregister::BUILTIN.with(|b| b.enums.Orientation.clone()),
791 });
792 let data = make_struct(
793 BuiltinPrivateStruct::GridLayoutData,
794 [
795 ("size", Type::Float32, size),
796 ("spacing", Type::Float32, spacing),
797 ("padding", padding.ty(ctx), padding),
798 ("organized_data", Type::ArrayOfU16, llr_Expression::PropertyReference(cells)),
799 ],
800 );
801 let constraints_result = grid_layout_cell_constraints(layout, o, ctx);
802
803 match constraints_result.compute_cells {
804 Some((cells_variable, elements)) => llr_Expression::WithLayoutItemInfo {
805 cells_variable: cells_variable.clone(),
806 repeater_indices_var_name: Some("repeated_indices".into()),
807 repeater_steps_var_name: Some("repeater_steps".into()),
808 elements,
809 orientation: o,
810 sub_expression: Box::new(llr_Expression::ExtraBuiltinFunctionCall {
811 function: "solve_grid_layout".into(),
812 arguments: vec![
813 data,
814 llr_Expression::ReadLocalVariable {
815 name: cells_variable.into(),
816 ty: constraints_result.cells.ty(ctx),
817 },
818 orientation_expr,
819 llr_Expression::ReadLocalVariable {
820 name: "repeated_indices".into(),
821 ty: Type::Array(Type::Int32.into()),
822 },
823 llr_Expression::ReadLocalVariable {
824 name: "repeater_steps".into(),
825 ty: Type::Array(Type::Int32.into()),
826 },
827 ],
828 return_ty: Type::LayoutCache,
829 }),
830 },
831 None => llr_Expression::ExtraBuiltinFunctionCall {
832 function: "solve_grid_layout".into(),
833 arguments: vec![
834 data,
835 constraints_result.cells,
836 orientation_expr,
837 empty_int32_slice(),
838 empty_int32_slice(),
839 ],
840 return_ty: Type::LayoutCache,
841 },
842 }
843}
844
845fn solve_layout(
846 l: &crate::layout::Layout,
847 o: Orientation,
848 ctx: &mut ExpressionLoweringCtx,
849) -> llr_Expression {
850 match l {
851 crate::layout::Layout::BoxLayout(layout) => {
852 let (padding, spacing) = generate_layout_padding_and_spacing(&layout.geometry, o, ctx);
853 let bld = box_layout_data(layout, o, ctx);
854 let size = layout_geometry_size(&layout.geometry.rect, o, ctx);
855 let data = make_struct(
856 BuiltinPrivateStruct::BoxLayoutData,
857 [
858 ("size", Type::Float32, size),
859 ("spacing", Type::Float32, spacing),
860 ("padding", padding.ty(ctx), padding),
861 (
862 "alignment",
863 crate::typeregister::BUILTIN
864 .with(|e| Type::Enumeration(e.enums.LayoutAlignment.clone())),
865 bld.alignment,
866 ),
867 ("cells", bld.cells.ty(ctx), bld.cells),
868 ],
869 );
870 match bld.compute_cells {
871 Some((cells_variable, elements)) => llr_Expression::WithLayoutItemInfo {
872 cells_variable,
873 repeater_indices_var_name: Some("repeated_indices".into()),
874 repeater_steps_var_name: None,
875 elements,
876 orientation: o,
877 sub_expression: Box::new(llr_Expression::ExtraBuiltinFunctionCall {
878 function: "solve_box_layout".into(),
879 arguments: vec![
880 data,
881 llr_Expression::ReadLocalVariable {
882 name: "repeated_indices".into(),
883 ty: Type::Array(Type::Int32.into()),
884 },
885 ],
886 return_ty: Type::LayoutCache,
887 }),
888 },
889 None => llr_Expression::ExtraBuiltinFunctionCall {
890 function: "solve_box_layout".into(),
891 arguments: vec![data, empty_int32_slice()],
892 return_ty: Type::LayoutCache,
893 },
894 }
895 }
896 _ => panic!("solve_layout is only supported for BoxLayout"),
897 }
898}
899
900struct BoxLayoutDataResult {
901 alignment: llr_Expression,
902 cells: llr_Expression,
903 compute_cells: Option<(String, Vec<Either<llr_Expression, LayoutRepeatedElement>>)>,
906}
907
908fn make_layout_cell_data_struct(layout_info: llr_Expression) -> llr_Expression {
909 make_struct(
910 BuiltinPrivateStruct::LayoutItemInfo,
911 [("constraint", crate::typeregister::layout_info_type().into(), layout_info)],
912 )
913}
914
915fn box_layout_data(
916 layout: &crate::layout::BoxLayout,
917 orientation: Orientation,
918 ctx: &mut ExpressionLoweringCtx,
919) -> BoxLayoutDataResult {
920 let alignment = if let Some(expr) = &layout.geometry.alignment {
921 llr_Expression::PropertyReference(ctx.map_property_reference(expr))
922 } else {
923 let e = crate::typeregister::BUILTIN.with(|e| e.enums.LayoutAlignment.clone());
924 llr_Expression::EnumerationValue(EnumerationValue {
925 value: e.default_value,
926 enumeration: e,
927 })
928 };
929
930 let repeater_count =
931 layout.elems.iter().filter(|i| i.element.borrow().repeated.is_some()).count();
932
933 let element_ty = crate::typeregister::box_layout_cell_data_type();
934
935 if repeater_count == 0 {
936 let cells = llr_Expression::Array {
937 values: layout
938 .elems
939 .iter()
940 .map(|li| {
941 let layout_info =
942 get_layout_info(&li.element, ctx, &li.constraints, orientation);
943 make_layout_cell_data_struct(layout_info)
944 })
945 .collect(),
946 element_ty,
947 output: llr_ArrayOutput::Slice,
948 };
949 BoxLayoutDataResult { alignment, cells, compute_cells: None }
950 } else {
951 let mut elements = Vec::new();
952 for item in &layout.elems {
953 if item.element.borrow().repeated.is_some() {
954 let repeater_index =
955 match ctx.mapping.element_mapping.get(&item.element.clone().into()).unwrap() {
956 LoweredElement::Repeated { repeated_index } => *repeated_index,
957 _ => panic!(),
958 };
959 elements.push(Either::Right(LayoutRepeatedElement {
960 repeater_index,
961 repeated_children_count: None,
962 }))
963 } else {
964 let layout_info =
965 get_layout_info(&item.element, ctx, &item.constraints, orientation);
966 elements.push(Either::Left(make_layout_cell_data_struct(layout_info)));
967 }
968 }
969 let cells = llr_Expression::ReadLocalVariable {
970 name: "cells".into(),
971 ty: Type::Array(Rc::new(crate::typeregister::layout_info_type().into())),
972 };
973 BoxLayoutDataResult { alignment, cells, compute_cells: Some(("cells".into(), elements)) }
974 }
975}
976
977struct GridLayoutCellConstraintsResult {
978 cells: llr_Expression,
979 compute_cells: Option<(String, Vec<Either<llr_Expression, LayoutRepeatedElement>>)>,
982}
983
984fn grid_layout_cell_constraints(
985 layout: &crate::layout::GridLayout,
986 orientation: Orientation,
987 ctx: &mut ExpressionLoweringCtx,
988) -> GridLayoutCellConstraintsResult {
989 let repeater_count =
990 layout.elems.iter().filter(|i| i.item.element.borrow().repeated.is_some()).count();
991
992 let element_ty = crate::typeregister::box_layout_cell_data_type();
993
994 if repeater_count == 0 {
995 let cells = llr_Expression::Array {
996 element_ty,
997 values: layout
998 .elems
999 .iter()
1000 .map(|li| {
1001 let layout_info =
1002 get_layout_info(&li.item.element, ctx, &li.item.constraints, orientation);
1003 make_layout_cell_data_struct(layout_info)
1004 })
1005 .collect(),
1006 output: llr_ArrayOutput::Slice,
1007 };
1008 GridLayoutCellConstraintsResult { cells, compute_cells: None }
1009 } else {
1010 let mut elements = Vec::new();
1011 for item in &layout.elems {
1012 if item.item.element.borrow().repeated.is_some() {
1013 let repeater_index = match ctx
1014 .mapping
1015 .element_mapping
1016 .get(&item.item.element.clone().into())
1017 .unwrap()
1018 {
1019 LoweredElement::Repeated { repeated_index } => *repeated_index,
1020 _ => panic!(),
1021 };
1022 let cell = item.cell.borrow();
1023 let repeated_children_count = cell.child_items.as_ref().map(|c| c.len());
1024 elements.push(Either::Right(LayoutRepeatedElement {
1025 repeater_index,
1026 repeated_children_count,
1027 }));
1028 } else {
1029 let layout_info =
1030 get_layout_info(&item.item.element, ctx, &item.item.constraints, orientation);
1031 elements.push(Either::Left(make_layout_cell_data_struct(layout_info)));
1032 }
1033 }
1034 let cells = llr_Expression::ReadLocalVariable {
1035 name: "cells".into(),
1036 ty: Type::Array(Rc::new(crate::typeregister::layout_info_type().into())),
1037 };
1038 GridLayoutCellConstraintsResult { cells, compute_cells: Some(("cells".into(), elements)) }
1039 }
1040}
1041
1042struct GridLayoutInputDataResult {
1043 cells: llr_Expression,
1044 compute_cells: Option<(String, Vec<Either<llr_Expression, GridLayoutRepeatedElement>>)>,
1047}
1048
1049fn grid_layout_input_data(
1051 layout: &crate::layout::GridLayout,
1052 ctx: &mut ExpressionLoweringCtx,
1053) -> GridLayoutInputDataResult {
1054 let propref = |named_ref: &RowColExpr| match named_ref {
1055 RowColExpr::Literal(n) => llr_Expression::NumberLiteral((*n).into()),
1056 RowColExpr::Named(nr) => llr_Expression::PropertyReference(ctx.map_property_reference(nr)),
1057 RowColExpr::Auto => llr_Expression::NumberLiteral(i_slint_common::ROW_COL_AUTO as _),
1058 };
1059 let input_data_for_cell = |elem: &crate::layout::GridLayoutElement,
1060 new_row_expr: llr_Expression| {
1061 let row_expr = propref(&elem.cell.borrow().row_expr);
1062 let col_expr = propref(&elem.cell.borrow().col_expr);
1063 let rowspan_expr = propref(&elem.cell.borrow().rowspan_expr);
1064 let colspan_expr = propref(&elem.cell.borrow().colspan_expr);
1065
1066 make_struct(
1067 BuiltinPrivateStruct::GridLayoutInputData,
1068 [
1069 ("new_row", Type::Bool, new_row_expr),
1070 ("row", Type::Float32, row_expr),
1071 ("col", Type::Float32, col_expr),
1072 ("rowspan", Type::Float32, rowspan_expr),
1073 ("colspan", Type::Float32, colspan_expr),
1074 ],
1075 )
1076 };
1077 let repeater_count =
1078 layout.elems.iter().filter(|i| i.item.element.borrow().repeated.is_some()).count();
1079
1080 let element_ty = grid_layout_input_data_ty();
1081
1082 if repeater_count == 0 {
1083 let cells = llr_Expression::Array {
1084 element_ty,
1085 values: layout
1086 .elems
1087 .iter()
1088 .map(|elem| {
1089 input_data_for_cell(
1090 elem,
1091 llr_Expression::BoolLiteral(elem.cell.borrow().new_row),
1092 )
1093 })
1094 .collect(),
1095 output: llr_ArrayOutput::Slice,
1096 };
1097 GridLayoutInputDataResult { cells, compute_cells: None }
1098 } else {
1099 let mut elements = Vec::new();
1100 let mut after_repeater_in_same_row = false;
1101 for item in &layout.elems {
1102 let new_row = item.cell.borrow().new_row;
1103 if new_row {
1104 after_repeater_in_same_row = false;
1105 }
1106 if item.item.element.borrow().repeated.is_some() {
1107 let repeater_index = match ctx
1108 .mapping
1109 .element_mapping
1110 .get(&item.item.element.clone().into())
1111 .unwrap()
1112 {
1113 LoweredElement::Repeated { repeated_index } => *repeated_index,
1114 _ => panic!(),
1115 };
1116 let cell = item.cell.borrow();
1117 let repeated_children_count = cell.child_items.as_ref().map(|c| c.len());
1118 let repeated_element =
1119 GridLayoutRepeatedElement { new_row, repeater_index, repeated_children_count };
1120 elements.push(Either::Right(repeated_element));
1121 after_repeater_in_same_row = true;
1122 } else {
1123 let new_row_expr = if new_row || !after_repeater_in_same_row {
1124 llr_Expression::BoolLiteral(new_row)
1125 } else {
1126 llr_Expression::ReadLocalVariable {
1127 name: SmolStr::new_static("new_row"),
1128 ty: Type::Bool,
1129 }
1130 };
1131 elements.push(Either::Left(input_data_for_cell(item, new_row_expr)));
1132 }
1133 }
1134 let cells = llr_Expression::ReadLocalVariable {
1135 name: "cells".into(),
1136 ty: Type::Array(Rc::new(element_ty)),
1137 };
1138 GridLayoutInputDataResult { cells, compute_cells: Some(("cells".into(), elements)) }
1139 }
1140}
1141
1142pub(super) fn grid_layout_input_data_ty() -> Type {
1143 Type::Struct(Rc::new(Struct {
1144 fields: IntoIterator::into_iter([
1145 (SmolStr::new_static("new_row"), Type::Bool),
1146 (SmolStr::new_static("row"), Type::Int32),
1147 (SmolStr::new_static("col"), Type::Int32),
1148 (SmolStr::new_static("rowspan"), Type::Int32),
1149 (SmolStr::new_static("colspan"), Type::Int32),
1150 ])
1151 .collect(),
1152 name: BuiltinPrivateStruct::GridLayoutInputData.into(),
1153 }))
1154}
1155
1156fn generate_layout_padding_and_spacing(
1157 layout_geometry: &crate::layout::LayoutGeometry,
1158 orientation: Orientation,
1159 ctx: &ExpressionLoweringCtx,
1160) -> (llr_Expression, llr_Expression) {
1161 let padding_prop = |expr| {
1162 if let Some(expr) = expr {
1163 llr_Expression::PropertyReference(ctx.map_property_reference(expr))
1164 } else {
1165 llr_Expression::NumberLiteral(0.)
1166 }
1167 };
1168 let spacing = padding_prop(layout_geometry.spacing.orientation(orientation));
1169 let (begin, end) = layout_geometry.padding.begin_end(orientation);
1170
1171 let padding = make_struct(
1172 BuiltinPrivateStruct::Padding,
1173 [("begin", Type::Float32, padding_prop(begin)), ("end", Type::Float32, padding_prop(end))],
1174 );
1175
1176 (padding, spacing)
1177}
1178
1179fn layout_geometry_size(
1180 rect: &crate::layout::LayoutRect,
1181 orientation: Orientation,
1182 ctx: &ExpressionLoweringCtx,
1183) -> llr_Expression {
1184 match rect.size_reference(orientation) {
1185 Some(nr) => llr_Expression::PropertyReference(ctx.map_property_reference(nr)),
1186 None => llr_Expression::NumberLiteral(0.),
1187 }
1188}
1189
1190pub fn get_layout_info(
1191 elem: &ElementRc,
1192 ctx: &mut ExpressionLoweringCtx,
1193 constraints: &crate::layout::LayoutConstraints,
1194 orientation: Orientation,
1195) -> llr_Expression {
1196 let layout_info = if let Some(layout_info_prop) = &elem.borrow().layout_info_prop(orientation) {
1197 llr_Expression::PropertyReference(ctx.map_property_reference(layout_info_prop))
1198 } else {
1199 lower_expression(&crate::layout::implicit_layout_info_call(elem, orientation), ctx)
1200 };
1201
1202 if constraints.has_explicit_restrictions(orientation) {
1203 let store = llr_Expression::StoreLocalVariable {
1204 name: "layout_info".into(),
1205 value: layout_info.into(),
1206 };
1207 let ty = crate::typeregister::layout_info_type();
1208 let mut values = ty
1209 .fields
1210 .keys()
1211 .map(|p| {
1212 (
1213 p.clone(),
1214 llr_Expression::StructFieldAccess {
1215 base: llr_Expression::ReadLocalVariable {
1216 name: "layout_info".into(),
1217 ty: ty.clone().into(),
1218 }
1219 .into(),
1220 name: p.clone(),
1221 },
1222 )
1223 })
1224 .collect::<BTreeMap<_, _>>();
1225
1226 for (nr, s) in constraints.for_each_restrictions(orientation) {
1227 values.insert(
1228 s.into(),
1229 llr_Expression::PropertyReference(ctx.map_property_reference(&nr)),
1230 );
1231 }
1232 llr_Expression::CodeBlock([store, llr_Expression::Struct { ty, values }].into())
1233 } else {
1234 layout_info
1235 }
1236}
1237
1238pub fn get_grid_layout_input_for_repeated(
1240 ctx: &mut ExpressionLoweringCtx,
1241 grid_cell: &GridLayoutCell,
1242) -> llr_Expression {
1243 let mut assignments = Vec::new();
1244
1245 fn convert_row_col_expr(expr: &RowColExpr, ctx: &ExpressionLoweringCtx) -> llr_Expression {
1246 match expr {
1247 RowColExpr::Literal(n) => llr_Expression::NumberLiteral((*n).into()),
1248 RowColExpr::Named(nr) => {
1249 llr_Expression::PropertyReference(ctx.map_property_reference(nr))
1250 }
1251 RowColExpr::Auto => llr_Expression::NumberLiteral(i_slint_common::ROW_COL_AUTO as _),
1252 }
1253 }
1254
1255 let mut push_assignment =
1257 |i: usize, new_row_expr: &llr_Expression, grid_cell: &GridLayoutCell| {
1258 let row = convert_row_col_expr(&grid_cell.row_expr, &*ctx);
1259 let col = convert_row_col_expr(&grid_cell.col_expr, &*ctx);
1260 let rowspan = convert_row_col_expr(&grid_cell.rowspan_expr, &*ctx);
1261 let colspan = convert_row_col_expr(&grid_cell.colspan_expr, &*ctx);
1262 let value = make_struct(
1263 BuiltinPrivateStruct::GridLayoutInputData,
1264 [
1265 ("new_row", Type::Bool, new_row_expr.clone()),
1266 ("row", Type::Float32, row),
1267 ("col", Type::Float32, col),
1268 ("rowspan", Type::Float32, rowspan),
1269 ("colspan", Type::Float32, colspan),
1270 ],
1271 );
1272 assignments.push(llr_Expression::SliceIndexAssignment {
1273 slice_name: SmolStr::new_static("result"),
1274 index: i,
1275 value: value.into(),
1276 });
1277 };
1278
1279 if let Some(child_items) = grid_cell.child_items.as_ref() {
1280 let mut new_row_expr = llr_Expression::BoolLiteral(true);
1282 for (i, child_item) in child_items.iter().enumerate() {
1283 let child_element = child_item.element.borrow();
1284 let child_cell = child_element.grid_layout_cell.as_ref().unwrap().borrow();
1285 push_assignment(i, &new_row_expr, &child_cell);
1286 new_row_expr = llr_Expression::BoolLiteral(false);
1287 }
1288 } else {
1289 let new_row_expr = llr_Expression::ReadLocalVariable {
1295 name: SmolStr::new_static("new_row"),
1296 ty: Type::Bool,
1297 };
1298 push_assignment(0, &new_row_expr, grid_cell);
1299 }
1300
1301 llr_Expression::CodeBlock(assignments)
1302}
1303
1304fn compile_path(
1305 path: &crate::expression_tree::Path,
1306 ctx: &mut ExpressionLoweringCtx,
1307) -> llr_Expression {
1308 fn llr_path_elements(elements: Vec<llr_Expression>) -> llr_Expression {
1309 llr_Expression::Cast {
1310 from: llr_Expression::Array {
1311 element_ty: crate::typeregister::path_element_type(),
1312 values: elements,
1313 output: llr_ArrayOutput::Slice,
1314 }
1315 .into(),
1316 to: Type::PathData,
1317 }
1318 }
1319
1320 match path {
1321 crate::expression_tree::Path::Elements(elements) => {
1322 let converted_elements = elements
1323 .iter()
1324 .map(|element| {
1325 let element_type = Rc::new(Struct {
1326 fields: element
1327 .element_type
1328 .properties
1329 .iter()
1330 .map(|(k, v)| (k.clone(), v.ty.clone()))
1331 .collect(),
1332 name: StructName::BuiltinPrivate(
1333 element
1334 .element_type
1335 .native_class
1336 .builtin_struct
1337 .clone()
1338 .expect("path elements should have a native_type"),
1339 ),
1340 });
1341
1342 llr_Expression::Struct {
1343 ty: element_type,
1344 values: element
1345 .element_type
1346 .properties
1347 .iter()
1348 .map(|(element_field_name, element_property)| {
1349 (
1350 element_field_name.clone(),
1351 element.bindings.get(element_field_name).map_or_else(
1352 || {
1353 llr_Expression::default_value_for_type(
1354 &element_property.ty,
1355 )
1356 .unwrap()
1357 },
1358 |expr| lower_expression(&expr.borrow().expression, ctx),
1359 ),
1360 )
1361 })
1362 .collect(),
1363 }
1364 })
1365 .collect();
1366 llr_path_elements(converted_elements)
1367 }
1368 crate::expression_tree::Path::Events(events, points) => {
1369 if events.is_empty() || points.is_empty() {
1370 return llr_path_elements(Vec::new());
1371 }
1372
1373 let events: Vec<_> = events.iter().map(|event| lower_expression(event, ctx)).collect();
1374
1375 let event_type = events.first().unwrap().ty(ctx);
1376
1377 let points: Vec<_> = points.iter().map(|point| lower_expression(point, ctx)).collect();
1378
1379 let point_type = points.first().unwrap().ty(ctx);
1380
1381 llr_Expression::Cast {
1382 from: llr_Expression::Struct {
1383 ty: Rc::new(Struct {
1384 fields: IntoIterator::into_iter([
1385 (SmolStr::new_static("events"), Type::Array(event_type.clone().into())),
1386 (SmolStr::new_static("points"), Type::Array(point_type.clone().into())),
1387 ])
1388 .collect(),
1389 name: StructName::None,
1390 }),
1391 values: IntoIterator::into_iter([
1392 (
1393 SmolStr::new_static("events"),
1394 llr_Expression::Array {
1395 element_ty: event_type,
1396 values: events,
1397 output: llr_ArrayOutput::Slice,
1398 },
1399 ),
1400 (
1401 SmolStr::new_static("points"),
1402 llr_Expression::Array {
1403 element_ty: point_type,
1404 values: points,
1405 output: llr_ArrayOutput::Slice,
1406 },
1407 ),
1408 ])
1409 .collect(),
1410 }
1411 .into(),
1412 to: Type::PathData,
1413 }
1414 }
1415 crate::expression_tree::Path::Commands(commands) => llr_Expression::Cast {
1416 from: lower_expression(commands, ctx).into(),
1417 to: Type::PathData,
1418 },
1419 }
1420}
1421
1422pub fn make_struct(
1423 name: impl Into<StructName>,
1424 it: impl IntoIterator<Item = (&'static str, Type, llr_Expression)>,
1425) -> llr_Expression {
1426 let mut fields = BTreeMap::<SmolStr, Type>::new();
1427 let mut values = BTreeMap::<SmolStr, llr_Expression>::new();
1428 for (name, ty, expr) in it {
1429 fields.insert(SmolStr::new(name), ty);
1430 values.insert(SmolStr::new(name), expr);
1431 }
1432
1433 llr_Expression::Struct { ty: Rc::new(Struct { fields, name: name.into() }), values }
1434}