1use std::cell::RefCell;
5use std::collections::{BTreeMap, HashMap};
6use std::rc::{Rc, Weak};
7
8use smol_str::{SmolStr, format_smolstr};
9
10use super::lower_layout_expression::{
11 compute_box_layout_info, compute_flexbox_layout_info, compute_grid_layout_info,
12 organize_grid_layout, solve_box_layout, solve_flexbox_layout, solve_grid_layout,
13};
14use super::lower_to_item_tree::{LoweredSubComponentMapping, LoweringState};
15use super::{Animation, LocalMemberReference, MemberReference, PropertyIdx};
16use crate::expression_tree::{BuiltinFunction, Callable, Expression as tree_Expression};
17use crate::langtype::{BuiltinPrivateStruct, Struct, StructName, Type};
18use crate::llr::ArrayOutput as llr_ArrayOutput;
19use crate::llr::Expression as llr_Expression;
20use crate::namedreference::NamedReference;
21use crate::object_tree::{Element, ElementRc, PropertyAnimation};
22use crate::typeregister::BUILTIN;
23
24pub struct ExpressionLoweringCtxInner<'a> {
25 pub component: &'a Rc<crate::object_tree::Component>,
26 pub mapping: &'a LoweredSubComponentMapping,
28 pub parent: Option<&'a ExpressionLoweringCtxInner<'a>>,
29}
30#[derive(derive_more::Deref)]
31pub struct ExpressionLoweringCtx<'a> {
32 pub state: &'a mut LoweringState,
33 #[deref]
34 pub inner: ExpressionLoweringCtxInner<'a>,
35}
36
37impl ExpressionLoweringCtx<'_> {
38 pub fn map_property_reference(&self, from: &NamedReference) -> MemberReference {
39 let element = from.element();
40 let enclosing = &element.borrow().enclosing_component.upgrade().unwrap();
41 let mut level = 0;
42 let mut map = &self.inner;
43 if !enclosing.is_global() {
44 while !Rc::ptr_eq(enclosing, map.component) {
45 map = map.parent.unwrap_or_else(|| {
46 panic!(
47 "Could not find component for property reference {from:?} in component {:?}. Started with enclosing={:?}",
48 self.component.id,
49 enclosing.id
50 )
51 });
52 level += 1;
53 }
54 }
55 let mut r = map.mapping.map_property_reference(from, self.state);
56 if let MemberReference::Relative { parent_level, .. } = &mut r {
57 *parent_level += level;
58 }
59 r
60 }
61}
62
63impl super::TypeResolutionContext for ExpressionLoweringCtx<'_> {
64 fn property_ty(&self, _: &MemberReference) -> &Type {
65 unimplemented!()
66 }
67}
68
69pub fn lower_expression(
70 expression: &tree_Expression,
71 ctx: &mut ExpressionLoweringCtx<'_>,
72) -> llr_Expression {
73 match expression {
74 tree_Expression::Invalid => {
75 panic!("internal error, encountered invalid expression at code generation time")
76 }
77 tree_Expression::Uncompiled(_) => panic!(),
78 tree_Expression::StringLiteral(s) => llr_Expression::StringLiteral(s.clone()),
79 tree_Expression::NumberLiteral(n, unit) => {
80 llr_Expression::NumberLiteral(unit.normalize(*n))
81 }
82 tree_Expression::BoolLiteral(b) => llr_Expression::BoolLiteral(*b),
83 tree_Expression::PropertyReference(nr) => {
84 llr_Expression::PropertyReference(ctx.map_property_reference(nr))
85 }
86 tree_Expression::ElementReference(e) => {
87 let elem = e.upgrade().unwrap();
88 let enclosing = elem.borrow().enclosing_component.upgrade().unwrap();
89 if Rc::ptr_eq(&elem, &enclosing.root_element)
91 && let Some(idx) = ctx
92 .component
93 .menu_item_tree
94 .borrow()
95 .iter()
96 .position(|c| Rc::ptr_eq(c, &enclosing))
97 {
98 return llr_Expression::NumberLiteral(idx as _);
99 }
100
101 llr_Expression::PropertyReference(
103 ctx.map_property_reference(&NamedReference::new(&elem, SmolStr::default())),
104 )
105 }
106 tree_Expression::RepeaterIndexReference { element } => llr_Expression::PropertyReference(
107 repeater_special_property(element, ctx.component, PropertyIdx::REPEATER_INDEX),
108 ),
109 tree_Expression::RepeaterModelReference { element } => llr_Expression::PropertyReference(
110 repeater_special_property(element, ctx.component, PropertyIdx::REPEATER_DATA),
111 ),
112 tree_Expression::FunctionParameterReference { index, .. } => {
113 llr_Expression::FunctionParameterReference { index: *index }
114 }
115 tree_Expression::StoreLocalVariable { name, value } => llr_Expression::StoreLocalVariable {
116 name: name.clone(),
117 value: Box::new(lower_expression(value, ctx)),
118 },
119 tree_Expression::ReadLocalVariable { name, ty } => {
120 llr_Expression::ReadLocalVariable { name: name.clone(), ty: ty.clone() }
121 }
122 tree_Expression::StructFieldAccess { base, name } => llr_Expression::StructFieldAccess {
123 base: Box::new(lower_expression(base, ctx)),
124 name: name.clone(),
125 },
126 tree_Expression::ArrayIndex { array, index } => llr_Expression::ArrayIndex {
127 array: Box::new(lower_expression(array, ctx)),
128 index: Box::new(lower_expression(index, ctx)),
129 },
130 tree_Expression::Cast { from, to } => {
131 llr_Expression::Cast { from: Box::new(lower_expression(from, ctx)), to: to.clone() }
132 }
133 tree_Expression::CodeBlock(expr) => {
134 llr_Expression::CodeBlock(expr.iter().map(|e| lower_expression(e, ctx)).collect::<_>())
135 }
136 tree_Expression::FunctionCall { function, arguments, .. } => match function {
137 Callable::Builtin(BuiltinFunction::RestartTimer) => lower_restart_timer(arguments),
138 Callable::Builtin(BuiltinFunction::ShowPopupWindow) => {
139 lower_show_popup_window(arguments, ctx)
140 }
141 Callable::Builtin(BuiltinFunction::ClosePopupWindow) => {
142 lower_close_popup_window(arguments, ctx)
143 }
144 Callable::Builtin(f) => {
145 let mut arguments =
146 arguments.iter().map(|e| lower_expression(e, ctx)).collect::<Vec<_>>();
147 #[allow(clippy::collapsible_if)]
149 if *f == BuiltinFunction::Translate {
150 if let llr_Expression::Array { output, .. } = &mut arguments[3] {
151 *output = llr_ArrayOutput::Slice;
152 }
153 #[cfg(feature = "bundle-translations")]
154 if let Some(translation_builder) = ctx.state.translation_builder.as_mut() {
155 return translation_builder.lower_translate_call(arguments);
156 }
157 }
158 if *f == BuiltinFunction::ParseMarkdown
159 && let Some(llr_Expression::Array { output, .. }) = &mut arguments.get_mut(1)
160 {
161 *output = llr_ArrayOutput::Slice;
162 }
163 llr_Expression::BuiltinFunctionCall { function: f.clone(), arguments }
164 }
165 Callable::Callback(nr) => {
166 let arguments = arguments.iter().map(|e| lower_expression(e, ctx)).collect::<_>();
167 llr_Expression::CallBackCall { callback: ctx.map_property_reference(nr), arguments }
168 }
169 Callable::Function(nr)
170 if nr
171 .element()
172 .borrow()
173 .native_class()
174 .is_some_and(|n| n.properties.contains_key(nr.name())) =>
175 {
176 llr_Expression::ItemMemberFunctionCall { function: ctx.map_property_reference(nr) }
177 }
178 Callable::Function(nr) => {
179 let arguments = arguments.iter().map(|e| lower_expression(e, ctx)).collect::<_>();
180 llr_Expression::FunctionCall { function: ctx.map_property_reference(nr), arguments }
181 }
182 },
183 tree_Expression::SelfAssignment { lhs, rhs, op, .. } => {
184 lower_assignment(lhs, rhs, *op, ctx)
185 }
186 tree_Expression::BinaryExpression { lhs, rhs, op } => llr_Expression::BinaryExpression {
187 lhs: Box::new(lower_expression(lhs, ctx)),
188 rhs: Box::new(lower_expression(rhs, ctx)),
189 op: *op,
190 },
191 tree_Expression::UnaryOp { sub, op } => {
192 llr_Expression::UnaryOp { sub: Box::new(lower_expression(sub, ctx)), op: *op }
193 }
194 tree_Expression::ImageReference { resource_ref, nine_slice, .. } => {
195 llr_Expression::ImageReference {
196 resource_ref: resource_ref.clone(),
197 nine_slice: *nine_slice,
198 }
199 }
200 tree_Expression::Condition { condition, true_expr, false_expr } => {
201 let (true_ty, false_ty) = (true_expr.ty(), false_expr.ty());
202 llr_Expression::Condition {
203 condition: Box::new(lower_expression(condition, ctx)),
204 true_expr: Box::new(lower_expression(true_expr, ctx)),
205 false_expr: if false_ty == Type::Invalid
206 || false_ty == Type::Void
207 || true_ty == false_ty
208 {
209 Box::new(lower_expression(false_expr, ctx))
210 } else {
211 Box::new(llr_Expression::Cast {
213 from: Box::new(lower_expression(false_expr, ctx)),
214 to: Type::Void,
215 })
216 },
217 }
218 }
219 tree_Expression::Array { element_ty, values } => llr_Expression::Array {
220 element_ty: element_ty.clone(),
221 values: values.iter().map(|e| lower_expression(e, ctx)).collect::<_>(),
222 output: llr_ArrayOutput::Model,
223 },
224 tree_Expression::Struct { ty, values } => llr_Expression::Struct {
225 ty: ty.clone(),
226 values: values
227 .iter()
228 .map(|(s, e)| (s.clone(), lower_expression(e, ctx)))
229 .collect::<_>(),
230 },
231 tree_Expression::PathData(data) => compile_path(data, ctx),
232 tree_Expression::EasingCurve(x) => llr_Expression::EasingCurve(x.clone()),
233 tree_Expression::LinearGradient { angle, stops } => llr_Expression::LinearGradient {
234 angle: Box::new(lower_expression(angle, ctx)),
235 stops: stops
236 .iter()
237 .map(|(a, b)| (lower_expression(a, ctx), lower_expression(b, ctx)))
238 .collect::<_>(),
239 },
240 tree_Expression::RadialGradient { stops } => llr_Expression::RadialGradient {
241 stops: stops
242 .iter()
243 .map(|(a, b)| (lower_expression(a, ctx), lower_expression(b, ctx)))
244 .collect::<_>(),
245 },
246 tree_Expression::ConicGradient { from_angle, stops } => llr_Expression::ConicGradient {
247 from_angle: Box::new(lower_expression(from_angle, ctx)),
248 stops: stops
249 .iter()
250 .map(|(a, b)| (lower_expression(a, ctx), lower_expression(b, ctx)))
251 .collect::<_>(),
252 },
253 tree_Expression::EnumerationValue(e) => llr_Expression::EnumerationValue(e.clone()),
254 tree_Expression::Keys(ks) => llr_Expression::KeysLiteral(ks.clone()),
255 tree_Expression::ReturnStatement(..) => {
256 panic!("The remove return pass should have removed all return")
257 }
258 tree_Expression::LayoutCacheAccess {
259 layout_cache_prop,
260 index,
261 repeater_index,
262 entries_per_item,
263 } => llr_Expression::LayoutCacheAccess {
264 layout_cache_prop: ctx.map_property_reference(layout_cache_prop),
265 index: *index,
266 repeater_index: repeater_index.as_ref().map(|e| lower_expression(e, ctx).into()),
267 entries_per_item: *entries_per_item,
268 },
269 tree_Expression::GridRepeaterCacheAccess {
270 layout_cache_prop,
271 index,
272 repeater_index,
273 stride,
274 child_offset,
275 inner_repeater_index,
276 entries_per_item,
277 } => llr_Expression::GridRepeaterCacheAccess {
278 layout_cache_prop: ctx.map_property_reference(layout_cache_prop),
279 index: *index,
280 repeater_index: lower_expression(repeater_index, ctx).into(),
281 stride: lower_expression(stride, ctx).into(),
282 child_offset: *child_offset,
283 inner_repeater_index: inner_repeater_index
284 .as_ref()
285 .map(|e| lower_expression(e, ctx).into()),
286 entries_per_item: *entries_per_item,
287 },
288 tree_Expression::OrganizeGridLayout(l) => organize_grid_layout(l, ctx),
289 tree_Expression::ComputeBoxLayoutInfo(l, o) => compute_box_layout_info(l, *o, ctx),
290 tree_Expression::ComputeGridLayoutInfo {
291 layout_organized_data_prop,
292 layout,
293 orientation,
294 } => compute_grid_layout_info(layout_organized_data_prop, layout, *orientation, ctx),
295 tree_Expression::SolveBoxLayout(l, o) => solve_box_layout(l, *o, ctx),
296 tree_Expression::SolveGridLayout { layout_organized_data_prop, layout, orientation } => {
297 solve_grid_layout(layout_organized_data_prop, layout, *orientation, ctx)
298 }
299 tree_Expression::SolveFlexboxLayout(l) => solve_flexbox_layout(l, ctx),
300 tree_Expression::ComputeFlexboxLayoutInfo(l, o) => compute_flexbox_layout_info(l, *o, ctx),
301 tree_Expression::MinMax { ty, op, lhs, rhs } => llr_Expression::MinMax {
302 ty: ty.clone(),
303 op: *op,
304 lhs: Box::new(lower_expression(lhs, ctx)),
305 rhs: Box::new(lower_expression(rhs, ctx)),
306 },
307 tree_Expression::EmptyComponentFactory => llr_Expression::EmptyComponentFactory,
308 tree_Expression::DebugHook { expression, .. } => lower_expression(expression, ctx),
309 }
310}
311
312fn lower_assignment(
313 lhs: &tree_Expression,
314 rhs: &tree_Expression,
315 op: char,
316 ctx: &mut ExpressionLoweringCtx,
317) -> llr_Expression {
318 match lhs {
319 tree_Expression::PropertyReference(nr) => {
320 let rhs = lower_expression(rhs, ctx);
321 let property = ctx.map_property_reference(nr);
322 let value = if op == '=' {
323 rhs
324 } else {
325 llr_Expression::BinaryExpression {
326 lhs: llr_Expression::PropertyReference(property.clone()).into(),
327 rhs: rhs.into(),
328 op,
329 }
330 }
331 .into();
332 llr_Expression::PropertyAssignment { property, value }
333 }
334 tree_Expression::StructFieldAccess { base, name } => {
335 let ty = base.ty();
336
337 static COUNT: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
338 let unique_name = format_smolstr!(
339 "struct_assignment{}",
340 COUNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
341 );
342 let s = tree_Expression::StoreLocalVariable {
343 name: unique_name.clone(),
344 value: base.clone(),
345 };
346 let lower_base =
347 tree_Expression::ReadLocalVariable { name: unique_name, ty: ty.clone() };
348 let mut values = HashMap::new();
349 let Type::Struct(ty) = ty else { unreachable!() };
350
351 for field in ty.fields.keys() {
352 let e = if field != name {
353 tree_Expression::StructFieldAccess {
354 base: lower_base.clone().into(),
355 name: field.clone(),
356 }
357 } else if op == '=' {
358 rhs.clone()
359 } else {
360 tree_Expression::BinaryExpression {
361 lhs: tree_Expression::StructFieldAccess {
362 base: lower_base.clone().into(),
363 name: field.clone(),
364 }
365 .into(),
366 rhs: Box::new(rhs.clone()),
367 op,
368 }
369 };
370 values.insert(field.clone(), e);
371 }
372
373 let new_value =
374 tree_Expression::CodeBlock(vec![s, tree_Expression::Struct { ty, values }]);
375 lower_assignment(base, &new_value, '=', ctx)
376 }
377 tree_Expression::RepeaterModelReference { element } => {
378 let rhs = lower_expression(rhs, ctx);
379 let prop =
380 repeater_special_property(element, ctx.component, PropertyIdx::REPEATER_DATA);
381
382 let level = match &prop {
383 MemberReference::Relative { parent_level, .. } => *parent_level,
384 _ => 0,
385 };
386
387 let value = Box::new(if op == '=' {
388 rhs
389 } else {
390 llr_Expression::BinaryExpression {
391 lhs: llr_Expression::PropertyReference(prop).into(),
392 rhs: rhs.into(),
393 op,
394 }
395 });
396
397 llr_Expression::ModelDataAssignment { level, value }
398 }
399 tree_Expression::ArrayIndex { array, index } => {
400 let rhs = lower_expression(rhs, ctx);
401 let array = Box::new(lower_expression(array, ctx));
402 let index = Box::new(lower_expression(index, ctx));
403 let value = Box::new(if op == '=' {
404 rhs
405 } else {
406 llr_Expression::BinaryExpression {
409 lhs: llr_Expression::ArrayIndex { array: array.clone(), index: index.clone() }
410 .into(),
411 rhs: rhs.into(),
412 op,
413 }
414 });
415
416 llr_Expression::ArrayIndexAssignment { array, index, value }
417 }
418 _ => panic!("not a rvalue"),
419 }
420}
421
422pub fn repeater_special_property(
423 element: &Weak<RefCell<Element>>,
424 component: &Rc<crate::object_tree::Component>,
425 property_index: PropertyIdx,
426) -> MemberReference {
427 let enclosing = element.upgrade().unwrap().borrow().enclosing_component.upgrade().unwrap();
428 let mut parent_level = 0;
429 let mut component = component.clone();
430 while !Rc::ptr_eq(&enclosing, &component) {
431 let parent_elem = component.parent_element().unwrap();
432 component = parent_elem.borrow().enclosing_component.upgrade().unwrap();
433 parent_level += 1;
434 }
435 MemberReference::Relative {
436 parent_level: parent_level - 1,
437 local_reference: LocalMemberReference {
438 sub_component_path: Vec::new(),
439 reference: property_index.into(),
440 },
441 }
442}
443
444fn lower_restart_timer(args: &[tree_Expression]) -> llr_Expression {
445 if let [tree_Expression::ElementReference(e)] = args {
446 let timer_element = e.upgrade().unwrap();
447 let timer_comp = timer_element.borrow().enclosing_component.upgrade().unwrap();
448
449 let timer_list = timer_comp.timers.borrow();
450 let timer_index = timer_list
451 .iter()
452 .position(|t| Rc::ptr_eq(&t.element.upgrade().unwrap(), &timer_element))
453 .unwrap();
454
455 llr_Expression::BuiltinFunctionCall {
456 function: BuiltinFunction::RestartTimer,
457 arguments: vec![llr_Expression::NumberLiteral(timer_index as _)],
458 }
459 } else {
460 panic!("invalid arguments to RestartTimer");
461 }
462}
463
464fn lower_show_popup_window(
465 args: &[tree_Expression],
466 ctx: &mut ExpressionLoweringCtx,
467) -> llr_Expression {
468 if let [tree_Expression::ElementReference(e)] = args {
469 let popup_window = e.upgrade().unwrap();
470 let pop_comp = popup_window.borrow().enclosing_component.upgrade().unwrap();
471 let parent_elem = pop_comp.parent_element().unwrap();
472 let parent_component = parent_elem.borrow().enclosing_component.upgrade().unwrap();
473 let popup_list = parent_component.popup_windows.borrow();
474 let (popup_index, popup) = popup_list
475 .iter()
476 .enumerate()
477 .find(|(_, p)| Rc::ptr_eq(&p.component, &pop_comp))
478 .unwrap();
479 let item_ref = lower_expression(
480 &tree_Expression::ElementReference(Rc::downgrade(&popup.parent_element)),
481 ctx,
482 );
483
484 llr_Expression::BuiltinFunctionCall {
485 function: BuiltinFunction::ShowPopupWindow,
486 arguments: vec![
487 llr_Expression::NumberLiteral(popup_index as _),
488 llr_Expression::EnumerationValue(popup.close_policy.clone()),
489 item_ref,
490 ],
491 }
492 } else {
493 panic!("invalid arguments to ShowPopupWindow");
494 }
495}
496
497fn lower_close_popup_window(
498 args: &[tree_Expression],
499 ctx: &mut ExpressionLoweringCtx,
500) -> llr_Expression {
501 if let [tree_Expression::ElementReference(e)] = args {
502 let popup_window = e.upgrade().unwrap();
503 let pop_comp = popup_window.borrow().enclosing_component.upgrade().unwrap();
504 let parent_elem = pop_comp.parent_element().unwrap();
505 let parent_component = parent_elem.borrow().enclosing_component.upgrade().unwrap();
506 let popup_list = parent_component.popup_windows.borrow();
507 let (popup_index, popup) = popup_list
508 .iter()
509 .enumerate()
510 .find(|(_, p)| Rc::ptr_eq(&p.component, &pop_comp))
511 .unwrap();
512 let item_ref = lower_expression(
513 &tree_Expression::ElementReference(Rc::downgrade(&popup.parent_element)),
514 ctx,
515 );
516
517 llr_Expression::BuiltinFunctionCall {
518 function: BuiltinFunction::ClosePopupWindow,
519 arguments: vec![llr_Expression::NumberLiteral(popup_index as _), item_ref],
520 }
521 } else {
522 panic!("invalid arguments to ShowPopupWindow");
523 }
524}
525
526pub fn lower_animation(a: &PropertyAnimation, ctx: &mut ExpressionLoweringCtx<'_>) -> Animation {
527 fn lower_animation_element(
528 a: &ElementRc,
529 ctx: &mut ExpressionLoweringCtx<'_>,
530 ) -> llr_Expression {
531 llr_Expression::Struct {
532 values: animation_fields()
533 .map(|(k, ty)| {
534 let e = a.borrow().bindings.get(&k).map_or_else(
535 || llr_Expression::default_value_for_type(&ty).unwrap(),
536 |v| lower_expression(&v.borrow().expression, ctx),
537 );
538 (k, e)
539 })
540 .collect::<_>(),
541 ty: animation_ty(),
542 }
543 }
544
545 fn animation_fields() -> impl Iterator<Item = (SmolStr, Type)> {
546 IntoIterator::into_iter([
547 (SmolStr::new_static("duration"), Type::Int32),
548 (SmolStr::new_static("iteration-count"), Type::Float32),
549 (
550 SmolStr::new_static("direction"),
551 Type::Enumeration(BUILTIN.with(|e| e.enums.AnimationDirection.clone())),
552 ),
553 (SmolStr::new_static("easing"), Type::Easing),
554 (SmolStr::new_static("delay"), Type::Int32),
555 ])
556 }
557
558 fn animation_ty() -> Rc<Struct> {
559 Rc::new(Struct {
560 fields: animation_fields().collect(),
561 name: BuiltinPrivateStruct::PropertyAnimation.into(),
562 })
563 }
564
565 match a {
566 PropertyAnimation::Static(a) => Animation::Static(lower_animation_element(a, ctx)),
567 PropertyAnimation::Transition { state_ref, animations } => {
568 let set_state = llr_Expression::StoreLocalVariable {
569 name: "state".into(),
570 value: Box::new(lower_expression(state_ref, ctx)),
571 };
572 let animation_ty = Type::Struct(animation_ty());
573 let mut get_anim = llr_Expression::default_value_for_type(&animation_ty).unwrap();
574 for tr in animations.iter().rev() {
575 let condition = lower_expression(
576 &tr.condition(tree_Expression::ReadLocalVariable {
577 name: "state".into(),
578 ty: state_ref.ty(),
579 }),
580 ctx,
581 );
582 get_anim = llr_Expression::Condition {
583 condition: Box::new(condition),
584 true_expr: Box::new(lower_animation_element(&tr.animation, ctx)),
585 false_expr: Box::new(get_anim),
586 }
587 }
588 let result = llr_Expression::Struct {
589 ty: Rc::new(Struct {
591 fields: IntoIterator::into_iter([
592 (SmolStr::new_static("0"), animation_ty),
593 (SmolStr::new_static("1"), Type::Invalid),
595 ])
596 .collect(),
597 name: StructName::None,
598 }),
599 values: IntoIterator::into_iter([
600 (SmolStr::new_static("0"), get_anim),
601 (
602 SmolStr::new_static("1"),
603 llr_Expression::StructFieldAccess {
604 base: llr_Expression::ReadLocalVariable {
605 name: "state".into(),
606 ty: state_ref.ty(),
607 }
608 .into(),
609 name: "change_time".into(),
610 },
611 ),
612 ])
613 .collect(),
614 };
615 Animation::Transition(llr_Expression::CodeBlock(vec![set_state, result]))
616 }
617 }
618}
619
620fn compile_path(
621 path: &crate::expression_tree::Path,
622 ctx: &mut ExpressionLoweringCtx,
623) -> llr_Expression {
624 fn llr_path_elements(elements: Vec<llr_Expression>) -> llr_Expression {
625 llr_Expression::Cast {
626 from: llr_Expression::Array {
627 element_ty: crate::typeregister::path_element_type(),
628 values: elements,
629 output: llr_ArrayOutput::Slice,
630 }
631 .into(),
632 to: Type::PathData,
633 }
634 }
635
636 match path {
637 crate::expression_tree::Path::Elements(elements) => {
638 let converted_elements = elements
639 .iter()
640 .map(|element| {
641 let element_type = Rc::new(Struct {
642 fields: element
643 .element_type
644 .properties
645 .iter()
646 .map(|(k, v)| (k.clone(), v.ty.clone()))
647 .collect(),
648 name: StructName::BuiltinPrivate(
649 element
650 .element_type
651 .native_class
652 .builtin_struct
653 .clone()
654 .expect("path elements should have a native_type"),
655 ),
656 });
657
658 llr_Expression::Struct {
659 ty: element_type,
660 values: element
661 .element_type
662 .properties
663 .iter()
664 .map(|(element_field_name, element_property)| {
665 (
666 element_field_name.clone(),
667 element.bindings.get(element_field_name).map_or_else(
668 || {
669 llr_Expression::default_value_for_type(
670 &element_property.ty,
671 )
672 .unwrap()
673 },
674 |expr| lower_expression(&expr.borrow().expression, ctx),
675 ),
676 )
677 })
678 .collect(),
679 }
680 })
681 .collect();
682 llr_path_elements(converted_elements)
683 }
684 crate::expression_tree::Path::Events(events, points) => {
685 if events.is_empty() || points.is_empty() {
686 return llr_path_elements(Vec::new());
687 }
688
689 let events: Vec<_> = events.iter().map(|event| lower_expression(event, ctx)).collect();
690
691 let event_type = events.first().unwrap().ty(ctx);
692
693 let points: Vec<_> = points.iter().map(|point| lower_expression(point, ctx)).collect();
694
695 let point_type = points.first().unwrap().ty(ctx);
696
697 llr_Expression::Cast {
698 from: llr_Expression::Struct {
699 ty: Rc::new(Struct {
700 fields: IntoIterator::into_iter([
701 (SmolStr::new_static("events"), Type::Array(event_type.clone().into())),
702 (SmolStr::new_static("points"), Type::Array(point_type.clone().into())),
703 ])
704 .collect(),
705 name: StructName::None,
706 }),
707 values: IntoIterator::into_iter([
708 (
709 SmolStr::new_static("events"),
710 llr_Expression::Array {
711 element_ty: event_type,
712 values: events,
713 output: llr_ArrayOutput::Slice,
714 },
715 ),
716 (
717 SmolStr::new_static("points"),
718 llr_Expression::Array {
719 element_ty: point_type,
720 values: points,
721 output: llr_ArrayOutput::Slice,
722 },
723 ),
724 ])
725 .collect(),
726 }
727 .into(),
728 to: Type::PathData,
729 }
730 }
731 crate::expression_tree::Path::Commands(commands) => llr_Expression::Cast {
732 from: lower_expression(commands, ctx).into(),
733 to: Type::PathData,
734 },
735 }
736}
737
738pub fn make_struct(
739 name: impl Into<StructName>,
740 it: impl IntoIterator<Item = (&'static str, Type, llr_Expression)>,
741) -> llr_Expression {
742 let mut fields = BTreeMap::<SmolStr, Type>::new();
743 let mut values = BTreeMap::<SmolStr, llr_Expression>::new();
744 for (name, ty, expr) in it {
745 fields.insert(SmolStr::new(name), ty);
746 values.insert(SmolStr::new(name), expr);
747 }
748
749 llr_Expression::Struct { ty: Rc::new(Struct { fields, name: name.into() }), values }
750}