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::{BuiltinStruct, 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 { center, radius, stops } => {
241 llr_Expression::RadialGradient {
242 center: center.as_ref().map(|(cx, cy)| {
243 (Box::new(lower_expression(cx, ctx)), Box::new(lower_expression(cy, ctx)))
244 }),
245 radius: radius.as_ref().map(|r| Box::new(lower_expression(r, ctx))),
246 stops: stops
247 .iter()
248 .map(|(a, b)| (lower_expression(a, ctx), lower_expression(b, ctx)))
249 .collect::<_>(),
250 }
251 }
252 tree_Expression::ConicGradient { from_angle, center, stops } => {
253 llr_Expression::ConicGradient {
254 from_angle: Box::new(lower_expression(from_angle, ctx)),
255 center: center.as_ref().map(|(cx, cy)| {
256 (Box::new(lower_expression(cx, ctx)), Box::new(lower_expression(cy, ctx)))
257 }),
258 stops: stops
259 .iter()
260 .map(|(a, b)| (lower_expression(a, ctx), lower_expression(b, ctx)))
261 .collect::<_>(),
262 }
263 }
264 tree_Expression::EnumerationValue(e) => llr_Expression::EnumerationValue(e.clone()),
265 tree_Expression::Keys(ks) => llr_Expression::KeysLiteral(ks.clone()),
266 tree_Expression::ReturnStatement(..) => {
267 panic!("The remove return pass should have removed all return")
268 }
269 tree_Expression::LayoutCacheAccess {
270 layout_cache_prop,
271 index,
272 repeater_index,
273 entries_per_item,
274 } => llr_Expression::LayoutCacheAccess {
275 layout_cache_prop: ctx.map_property_reference(layout_cache_prop),
276 index: *index,
277 repeater_index: repeater_index.as_ref().map(|e| lower_expression(e, ctx).into()),
278 entries_per_item: *entries_per_item,
279 },
280 tree_Expression::GridRepeaterCacheAccess {
281 layout_cache_prop,
282 index,
283 repeater_index,
284 stride,
285 child_offset,
286 inner_repeater_index,
287 entries_per_item,
288 } => llr_Expression::GridRepeaterCacheAccess {
289 layout_cache_prop: ctx.map_property_reference(layout_cache_prop),
290 index: *index,
291 repeater_index: lower_expression(repeater_index, ctx).into(),
292 stride: lower_expression(stride, ctx).into(),
293 child_offset: *child_offset,
294 inner_repeater_index: inner_repeater_index
295 .as_ref()
296 .map(|e| lower_expression(e, ctx).into()),
297 entries_per_item: *entries_per_item,
298 },
299 tree_Expression::OrganizeGridLayout(l) => organize_grid_layout(l, ctx),
300 tree_Expression::ComputeBoxLayoutInfo { layout, orientation, cross_axis_size } => {
301 compute_box_layout_info(layout, *orientation, ctx, cross_axis_size.as_deref())
302 }
303 tree_Expression::ComputeGridLayoutInfo {
304 layout_organized_data_prop,
305 layout,
306 orientation,
307 cross_axis_size,
308 } => compute_grid_layout_info(
309 layout_organized_data_prop,
310 layout,
311 *orientation,
312 ctx,
313 cross_axis_size.as_deref(),
314 ),
315 tree_Expression::SolveBoxLayout(l, o) => solve_box_layout(l, *o, ctx),
316 tree_Expression::SolveGridLayout { layout_organized_data_prop, layout, orientation } => {
317 solve_grid_layout(layout_organized_data_prop, layout, *orientation, ctx)
318 }
319 tree_Expression::SolveFlexboxLayout(l) => solve_flexbox_layout(l, ctx),
320 tree_Expression::ComputeFlexboxLayoutInfo { layout, orientation, cross_axis_size } => {
321 compute_flexbox_layout_info(layout, *orientation, ctx, cross_axis_size.as_deref())
322 }
323 tree_Expression::MinMax { ty, op, lhs, rhs } => llr_Expression::MinMax {
324 ty: ty.clone(),
325 op: *op,
326 lhs: Box::new(lower_expression(lhs, ctx)),
327 rhs: Box::new(lower_expression(rhs, ctx)),
328 },
329 tree_Expression::EmptyComponentFactory => llr_Expression::EmptyComponentFactory,
330 tree_Expression::EmptyDataTransfer => llr_Expression::EmptyDataTransfer,
331 tree_Expression::DebugHook { expression, .. } => lower_expression(expression, ctx),
332 }
333}
334
335fn lower_assignment(
336 lhs: &tree_Expression,
337 rhs: &tree_Expression,
338 op: char,
339 ctx: &mut ExpressionLoweringCtx,
340) -> llr_Expression {
341 match lhs {
342 tree_Expression::PropertyReference(nr) => {
343 let rhs = lower_expression(rhs, ctx);
344 let property = ctx.map_property_reference(nr);
345 let value = if op == '=' {
346 rhs
347 } else {
348 llr_Expression::BinaryExpression {
349 lhs: llr_Expression::PropertyReference(property.clone()).into(),
350 rhs: rhs.into(),
351 op,
352 }
353 }
354 .into();
355 llr_Expression::PropertyAssignment { property, value }
356 }
357 tree_Expression::StructFieldAccess { base, name } => {
358 let ty = base.ty();
359
360 static COUNT: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
361 let unique_name = format_smolstr!(
362 "struct_assignment{}",
363 COUNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
364 );
365 let s = tree_Expression::StoreLocalVariable {
366 name: unique_name.clone(),
367 value: base.clone(),
368 };
369 let lower_base =
370 tree_Expression::ReadLocalVariable { name: unique_name, ty: ty.clone() };
371 let mut values = HashMap::new();
372 let Type::Struct(ty) = ty else { unreachable!() };
373
374 for field in ty.fields.keys() {
375 let e = if field != name {
376 tree_Expression::StructFieldAccess {
377 base: lower_base.clone().into(),
378 name: field.clone(),
379 }
380 } else if op == '=' {
381 rhs.clone()
382 } else {
383 tree_Expression::BinaryExpression {
384 lhs: tree_Expression::StructFieldAccess {
385 base: lower_base.clone().into(),
386 name: field.clone(),
387 }
388 .into(),
389 rhs: Box::new(rhs.clone()),
390 op,
391 }
392 };
393 values.insert(field.clone(), e);
394 }
395
396 let new_value =
397 tree_Expression::CodeBlock(vec![s, tree_Expression::Struct { ty, values }]);
398 lower_assignment(base, &new_value, '=', ctx)
399 }
400 tree_Expression::RepeaterModelReference { element } => {
401 let rhs = lower_expression(rhs, ctx);
402 let prop =
403 repeater_special_property(element, ctx.component, PropertyIdx::REPEATER_DATA);
404
405 let level = match &prop {
406 MemberReference::Relative { parent_level, .. } => *parent_level,
407 _ => 0,
408 };
409
410 let value = Box::new(if op == '=' {
411 rhs
412 } else {
413 llr_Expression::BinaryExpression {
414 lhs: llr_Expression::PropertyReference(prop).into(),
415 rhs: rhs.into(),
416 op,
417 }
418 });
419
420 llr_Expression::ModelDataAssignment { level, value }
421 }
422 tree_Expression::ArrayIndex { array, index } => {
423 let rhs = lower_expression(rhs, ctx);
424 let array = Box::new(lower_expression(array, ctx));
425 let index = Box::new(lower_expression(index, ctx));
426 let value = Box::new(if op == '=' {
427 rhs
428 } else {
429 llr_Expression::BinaryExpression {
432 lhs: llr_Expression::ArrayIndex { array: array.clone(), index: index.clone() }
433 .into(),
434 rhs: rhs.into(),
435 op,
436 }
437 });
438
439 llr_Expression::ArrayIndexAssignment { array, index, value }
440 }
441 _ => panic!("not a rvalue"),
442 }
443}
444
445pub fn repeater_special_property(
446 element: &Weak<RefCell<Element>>,
447 component: &Rc<crate::object_tree::Component>,
448 property_index: PropertyIdx,
449) -> MemberReference {
450 let enclosing = element.upgrade().unwrap().borrow().enclosing_component.upgrade().unwrap();
451 let mut parent_level = 0;
452 let mut component = component.clone();
453 while !Rc::ptr_eq(&enclosing, &component) {
454 let parent_elem = component.parent_element().unwrap();
455 component = parent_elem.borrow().enclosing_component.upgrade().unwrap();
456 parent_level += 1;
457 }
458 MemberReference::Relative {
459 parent_level: parent_level - 1,
460 local_reference: LocalMemberReference {
461 sub_component_path: Vec::new(),
462 reference: property_index.into(),
463 },
464 }
465}
466
467fn lower_restart_timer(args: &[tree_Expression]) -> llr_Expression {
468 if let [tree_Expression::ElementReference(e)] = args {
469 let timer_element = e.upgrade().unwrap();
470 let timer_comp = timer_element.borrow().enclosing_component.upgrade().unwrap();
471
472 let timer_list = timer_comp.timers.borrow();
473 let timer_index = timer_list
474 .iter()
475 .position(|t| Rc::ptr_eq(&t.element.upgrade().unwrap(), &timer_element))
476 .unwrap();
477
478 llr_Expression::BuiltinFunctionCall {
479 function: BuiltinFunction::RestartTimer,
480 arguments: vec![llr_Expression::NumberLiteral(timer_index as _)],
481 }
482 } else {
483 panic!("invalid arguments to RestartTimer");
484 }
485}
486
487fn lower_show_popup_window(
488 args: &[tree_Expression],
489 ctx: &mut ExpressionLoweringCtx,
490) -> llr_Expression {
491 if let [tree_Expression::ElementReference(e)] = args {
492 let popup_window = e.upgrade().unwrap();
493 let pop_comp = popup_window.borrow().enclosing_component.upgrade().unwrap();
494 let parent_elem = pop_comp.parent_element().unwrap();
495 let parent_component = parent_elem.borrow().enclosing_component.upgrade().unwrap();
496 let popup_list = parent_component.popup_windows.borrow();
497 let (popup_index, popup) = popup_list
498 .iter()
499 .enumerate()
500 .find(|(_, p)| Rc::ptr_eq(&p.component, &pop_comp))
501 .unwrap();
502 let item_ref = lower_expression(
503 &tree_Expression::ElementReference(Rc::downgrade(&popup.parent_element)),
504 ctx,
505 );
506
507 let mut arguments = vec![
508 llr_Expression::NumberLiteral(popup_index as _),
509 llr_Expression::EnumerationValue(popup.close_policy.clone()),
510 item_ref,
511 ];
512 if let Some(is_open) = &popup.is_open {
516 arguments.push(llr_Expression::PropertyReference(ctx.map_property_reference(is_open)));
517 }
518 llr_Expression::BuiltinFunctionCall {
519 function: BuiltinFunction::ShowPopupWindow,
520 arguments,
521 }
522 } else {
523 panic!("invalid arguments to ShowPopupWindow");
524 }
525}
526
527fn lower_close_popup_window(
528 args: &[tree_Expression],
529 ctx: &mut ExpressionLoweringCtx,
530) -> llr_Expression {
531 if let [tree_Expression::ElementReference(e)] = args {
532 let popup_window = e.upgrade().unwrap();
533 let pop_comp = popup_window.borrow().enclosing_component.upgrade().unwrap();
534 let parent_elem = pop_comp.parent_element().unwrap();
535 let parent_component = parent_elem.borrow().enclosing_component.upgrade().unwrap();
536 let popup_list = parent_component.popup_windows.borrow();
537 let (popup_index, popup) = popup_list
538 .iter()
539 .enumerate()
540 .find(|(_, p)| Rc::ptr_eq(&p.component, &pop_comp))
541 .unwrap();
542 let item_ref = lower_expression(
543 &tree_Expression::ElementReference(Rc::downgrade(&popup.parent_element)),
544 ctx,
545 );
546
547 llr_Expression::BuiltinFunctionCall {
548 function: BuiltinFunction::ClosePopupWindow,
549 arguments: vec![llr_Expression::NumberLiteral(popup_index as _), item_ref],
550 }
551 } else {
552 panic!("invalid arguments to ShowPopupWindow");
553 }
554}
555
556pub fn lower_animation(a: &PropertyAnimation, ctx: &mut ExpressionLoweringCtx<'_>) -> Animation {
557 fn lower_animation_element(
558 a: &ElementRc,
559 ctx: &mut ExpressionLoweringCtx<'_>,
560 ) -> llr_Expression {
561 llr_Expression::Struct {
562 values: animation_fields()
563 .map(|(k, ty)| {
564 let e = a.borrow().bindings.get(&k).map_or_else(
565 || {
566 if k == "enabled" {
567 llr_Expression::BoolLiteral(true)
568 } else {
569 llr_Expression::default_value_for_type(&ty).unwrap()
570 }
571 },
572 |v| lower_expression(&v.borrow().expression, ctx),
573 );
574 (k, e)
575 })
576 .collect::<_>(),
577 ty: animation_ty(),
578 }
579 }
580
581 fn animation_fields() -> impl Iterator<Item = (SmolStr, Type)> {
582 IntoIterator::into_iter([
583 (SmolStr::new_static("duration"), Type::Int32),
584 (SmolStr::new_static("iteration-count"), Type::Float32),
585 (
586 SmolStr::new_static("direction"),
587 Type::Enumeration(BUILTIN.with(|e| e.enums.AnimationDirection.clone())),
588 ),
589 (SmolStr::new_static("easing"), Type::Easing),
590 (SmolStr::new_static("delay"), Type::Int32),
591 (SmolStr::new_static("enabled"), Type::Bool),
592 ])
593 }
594
595 fn animation_ty() -> Rc<Struct> {
596 Rc::new(Struct {
597 fields: animation_fields().collect(),
598 name: BuiltinStruct::PropertyAnimation.into(),
599 })
600 }
601
602 match a {
603 PropertyAnimation::Static(a) => Animation::Static(lower_animation_element(a, ctx)),
604 PropertyAnimation::Transition { state_ref, animations } => {
605 let set_state = llr_Expression::StoreLocalVariable {
606 name: "state".into(),
607 value: Box::new(lower_expression(state_ref, ctx)),
608 };
609 let anim_struct_ty = animation_ty();
610 let animation_ty = Type::Struct(anim_struct_ty.clone());
611 let mut get_anim = llr_Expression::Struct {
612 ty: anim_struct_ty,
613 values: animation_fields()
614 .map(|(k, ty)| {
615 let e = if k == "enabled" {
616 llr_Expression::BoolLiteral(true)
617 } else {
618 llr_Expression::default_value_for_type(&ty).unwrap()
619 };
620 (k, e)
621 })
622 .collect(),
623 };
624 for tr in animations.iter().rev() {
625 let condition = lower_expression(
626 &tr.condition(tree_Expression::ReadLocalVariable {
627 name: "state".into(),
628 ty: state_ref.ty(),
629 }),
630 ctx,
631 );
632 get_anim = llr_Expression::Condition {
633 condition: Box::new(condition),
634 true_expr: Box::new(lower_animation_element(&tr.animation, ctx)),
635 false_expr: Box::new(get_anim),
636 }
637 }
638 let result = llr_Expression::Struct {
639 ty: Rc::new(Struct {
641 fields: IntoIterator::into_iter([
642 (SmolStr::new_static("0"), animation_ty),
643 (SmolStr::new_static("1"), Type::Invalid),
645 ])
646 .collect(),
647 name: StructName::None,
648 }),
649 values: IntoIterator::into_iter([
650 (SmolStr::new_static("0"), get_anim),
651 (
652 SmolStr::new_static("1"),
653 llr_Expression::StructFieldAccess {
654 base: llr_Expression::ReadLocalVariable {
655 name: "state".into(),
656 ty: state_ref.ty(),
657 }
658 .into(),
659 name: "change_time".into(),
660 },
661 ),
662 ])
663 .collect(),
664 };
665 Animation::Transition(llr_Expression::CodeBlock(vec![set_state, result]))
666 }
667 }
668}
669
670fn compile_path(
671 path: &crate::expression_tree::Path,
672 ctx: &mut ExpressionLoweringCtx,
673) -> llr_Expression {
674 fn llr_path_elements(elements: Vec<llr_Expression>) -> llr_Expression {
675 llr_Expression::Cast {
676 from: llr_Expression::Array {
677 element_ty: crate::typeregister::path_element_type(),
678 values: elements,
679 output: llr_ArrayOutput::Slice,
680 }
681 .into(),
682 to: Type::PathData,
683 }
684 }
685
686 match path {
687 crate::expression_tree::Path::Elements(elements) => {
688 let converted_elements = elements
689 .iter()
690 .map(|element| {
691 let element_type = Rc::new(Struct {
692 fields: element
693 .element_type
694 .properties
695 .iter()
696 .map(|(k, v)| (k.clone(), v.ty.clone()))
697 .collect(),
698 name: StructName::Builtin(
699 element
700 .element_type
701 .native_class
702 .builtin_struct
703 .clone()
704 .expect("path elements should have a native_type"),
705 ),
706 });
707
708 llr_Expression::Struct {
709 ty: element_type,
710 values: element
711 .element_type
712 .properties
713 .iter()
714 .map(|(element_field_name, element_property)| {
715 (
716 element_field_name.clone(),
717 element.bindings.get(element_field_name).map_or_else(
718 || {
719 llr_Expression::default_value_for_type(
720 &element_property.ty,
721 )
722 .unwrap()
723 },
724 |expr| lower_expression(&expr.borrow().expression, ctx),
725 ),
726 )
727 })
728 .collect(),
729 }
730 })
731 .collect();
732 llr_path_elements(converted_elements)
733 }
734 crate::expression_tree::Path::Events(events, points) => {
735 if events.is_empty() || points.is_empty() {
736 return llr_path_elements(Vec::new());
737 }
738
739 let events: Vec<_> = events.iter().map(|event| lower_expression(event, ctx)).collect();
740
741 let event_type = events.first().unwrap().ty(ctx);
742
743 let points: Vec<_> = points.iter().map(|point| lower_expression(point, ctx)).collect();
744
745 let point_type = points.first().unwrap().ty(ctx);
746
747 llr_Expression::Cast {
748 from: llr_Expression::Struct {
749 ty: Rc::new(Struct {
750 fields: IntoIterator::into_iter([
751 (SmolStr::new_static("events"), Type::Array(event_type.clone().into())),
752 (SmolStr::new_static("points"), Type::Array(point_type.clone().into())),
753 ])
754 .collect(),
755 name: StructName::None,
756 }),
757 values: IntoIterator::into_iter([
758 (
759 SmolStr::new_static("events"),
760 llr_Expression::Array {
761 element_ty: event_type,
762 values: events,
763 output: llr_ArrayOutput::Slice,
764 },
765 ),
766 (
767 SmolStr::new_static("points"),
768 llr_Expression::Array {
769 element_ty: point_type,
770 values: points,
771 output: llr_ArrayOutput::Slice,
772 },
773 ),
774 ])
775 .collect(),
776 }
777 .into(),
778 to: Type::PathData,
779 }
780 }
781 crate::expression_tree::Path::Commands(commands) => llr_Expression::Cast {
782 from: lower_expression(commands, ctx).into(),
783 to: Type::PathData,
784 },
785 }
786}
787
788pub fn make_struct(
789 name: impl Into<StructName>,
790 it: impl IntoIterator<Item = (&'static str, Type, llr_Expression)>,
791) -> llr_Expression {
792 let mut fields = BTreeMap::<SmolStr, Type>::new();
793 let mut values = BTreeMap::<SmolStr, llr_Expression>::new();
794 for (name, ty, expr) in it {
795 fields.insert(SmolStr::new(name), ty);
796 values.insert(SmolStr::new(name), expr);
797 }
798
799 llr_Expression::Struct { ty: Rc::new(Struct { fields, name: name.into() }), values }
800}