1use std::cell::RefCell;
5use std::collections::BTreeMap;
6use std::rc::{Rc, Weak};
7use std::sync::Arc;
8
9use smol_str::SmolStr;
10
11use super::lower_layout_expression::{
12 compute_box_layout_info, compute_flexbox_layout_info, compute_grid_layout_info,
13 organize_grid_layout, solve_box_layout, solve_flexbox_layout, solve_grid_layout,
14};
15use super::lower_to_item_tree::{LoweredSubComponentMapping, LoweringState};
16use super::{Animation, LocalMemberReference, MemberReference, PropertyIdx};
17use crate::expression_tree::{
18 BuiltinFunction, Callable, Expression as tree_Expression, MouseCursorInner,
19};
20use crate::langtype::{BuiltinStruct, ConstantExpression, Struct, StructName, Type};
21use crate::llr::ArrayOutput as llr_ArrayOutput;
22use crate::llr::Expression as llr_Expression;
23use crate::namedreference::NamedReference;
24use crate::object_tree::{Component, Element, ElementRc, ElementWeak, PropertyAnimation};
25use crate::typeregister::BUILTIN;
26
27pub struct ExpressionLoweringCtxInner<'a> {
28 pub component: &'a Rc<crate::object_tree::Component>,
29 pub mapping: &'a LoweredSubComponentMapping,
31 pub parent: Option<&'a ExpressionLoweringCtxInner<'a>>,
32}
33#[derive(derive_more::Deref)]
34pub struct ExpressionLoweringCtx<'a> {
35 pub state: &'a mut LoweringState,
36 #[deref]
37 pub inner: ExpressionLoweringCtxInner<'a>,
38}
39
40impl ExpressionLoweringCtx<'_> {
41 fn find_component(
43 &self,
44 enclosing: &Rc<Component>,
45 ) -> (usize, &ExpressionLoweringCtxInner<'_>) {
46 let mut level = 0;
47 let mut map = &self.inner;
48 while !Rc::ptr_eq(enclosing, map.component) {
49 map = map.parent.unwrap_or_else(|| {
50 panic!(
51 "Could not find component {:?} from component {:?}",
52 enclosing.id, self.component.id
53 )
54 });
55 level += 1;
56 }
57 (level, map)
58 }
59
60 pub fn map_property_reference(&self, from: &NamedReference) -> MemberReference {
61 let element = from.element();
62 let enclosing = &element.borrow().enclosing_component.upgrade().unwrap();
63 let (level, map) =
64 if enclosing.is_global() { (0, &self.inner) } else { self.find_component(enclosing) };
65 let mut r = map.mapping.map_property_reference(from, self.state);
66 if let MemberReference::Relative { parent_level, .. } = &mut r {
67 *parent_level += level;
68 }
69 r
70 }
71}
72
73impl super::TypeResolutionContext for ExpressionLoweringCtx<'_> {
74 fn property_ty(&self, _: &MemberReference) -> &Type {
75 unimplemented!()
76 }
77}
78
79pub fn lower_constant_expression(expression: &ConstantExpression) -> llr_Expression {
83 match expression {
84 ConstantExpression::StringLiteral(s) => llr_Expression::StringLiteral(s.clone()),
85 ConstantExpression::NumberLiteral(n, _unit) => llr_Expression::NumberLiteral(*n),
86 ConstantExpression::BoolLiteral(b) => llr_Expression::BoolLiteral(*b),
87 ConstantExpression::EnumerationValue(e) => llr_Expression::EnumerationValue(e.clone()),
88 ConstantExpression::Cast { from, to } => {
89 llr_Expression::Cast { from: Box::new(lower_constant_expression(from)), to: to.clone() }
90 }
91 ConstantExpression::UnaryOp { sub, op } => {
92 llr_Expression::UnaryOp { sub: Box::new(lower_constant_expression(sub)), op: *op }
93 }
94 ConstantExpression::Struct { ty, values } => llr_Expression::Struct {
95 ty: ty.clone(),
96 values: values.iter().map(|(k, v)| (k.clone(), lower_constant_expression(v))).collect(),
97 },
98 ConstantExpression::Array { element_ty, values } => llr_Expression::Array {
99 element_ty: element_ty.clone(),
100 values: values.iter().map(lower_constant_expression).collect(),
101 output: llr_ArrayOutput::Model,
102 },
103 }
104}
105
106pub fn lower_expression(
111 expression: &tree_Expression,
112 ctx: &mut ExpressionLoweringCtx<'_>,
113) -> llr_Expression {
114 match expression {
115 tree_Expression::Invalid => {
116 panic!("internal error, encountered invalid expression at code generation time")
117 }
118 tree_Expression::Uncompiled(_) => panic!(),
119 tree_Expression::StringLiteral(s) => llr_Expression::StringLiteral(s.clone()),
120 tree_Expression::NumberLiteral(n, _unit) => llr_Expression::NumberLiteral(*n),
121 tree_Expression::BoolLiteral(b) => llr_Expression::BoolLiteral(*b),
122 tree_Expression::PropertyReference(nr) => {
123 llr_Expression::PropertyReference(ctx.map_property_reference(nr))
124 }
125 tree_Expression::ElementReference(_) => lower_element_reference(expression, ctx),
126 tree_Expression::RepeaterIndexReference { element } => llr_Expression::PropertyReference(
127 repeater_special_property(element, ctx.component, PropertyIdx::REPEATER_INDEX),
128 ),
129 tree_Expression::RepeaterModelReference { element } => llr_Expression::PropertyReference(
130 repeater_special_property(element, ctx.component, PropertyIdx::REPEATER_DATA),
131 ),
132 tree_Expression::FunctionParameterReference { index, .. } => {
133 llr_Expression::FunctionParameterReference { index: *index }
134 }
135 tree_Expression::StoreLocalVariable { name, value } => llr_Expression::StoreLocalVariable {
136 name: name.clone(),
137 value: Box::new(lower_expression(value, ctx)),
138 },
139 tree_Expression::ReadLocalVariable { name, ty } => {
140 llr_Expression::ReadLocalVariable { name: name.clone(), ty: ty.clone() }
141 }
142 tree_Expression::StructFieldAccess { base, name } => llr_Expression::StructFieldAccess {
143 base: Box::new(lower_expression(base, ctx)),
144 name: name.clone(),
145 },
146 tree_Expression::ArrayIndex { array, index } => llr_Expression::ArrayIndex {
147 array: Box::new(lower_expression(array, ctx)),
148 index: Box::new(lower_expression(index, ctx)),
149 },
150 tree_Expression::Cast { from, to } => {
151 llr_Expression::Cast { from: Box::new(lower_expression(from, ctx)), to: to.clone() }
152 }
153 tree_Expression::CodeBlock(expr) => {
154 llr_Expression::CodeBlock(expr.iter().map(|e| lower_expression(e, ctx)).collect::<_>())
155 }
156 tree_Expression::FunctionCall { .. } => lower_function_call(expression, ctx),
157 tree_Expression::SelfAssignment { lhs, rhs, op, .. } => {
158 lower_assignment(lhs, rhs, *op, ctx)
159 }
160 tree_Expression::BinaryExpression { .. } => lower_binary_expression(expression, ctx),
161 tree_Expression::UnaryOp { sub, op } => {
162 llr_Expression::UnaryOp { sub: Box::new(lower_expression(sub, ctx)), op: *op }
163 }
164 tree_Expression::ImageReference { resource_ref, nine_slice, .. } => {
165 llr_Expression::ImageReference {
166 resource_ref: resource_ref.clone(),
167 nine_slice: *nine_slice,
168 }
169 }
170 tree_Expression::Condition { .. } => lower_condition(expression, ctx),
171 tree_Expression::Array { element_ty, values } => llr_Expression::Array {
172 element_ty: if *element_ty == Type::Void { Type::Int32 } else { element_ty.clone() },
173 values: values.iter().map(|e| lower_expression(e, ctx)).collect::<_>(),
174 output: llr_ArrayOutput::Model,
175 },
176 tree_Expression::Struct { ty, values } => llr_Expression::Struct {
177 ty: ty.clone(),
178 values: values
179 .iter()
180 .map(|(s, e)| (s.clone(), lower_expression(e, ctx)))
181 .collect::<_>(),
182 },
183 tree_Expression::PathData(data) => compile_path(data, ctx),
184 tree_Expression::EasingCurve(x) => llr_Expression::EasingCurve(x.clone()),
185 tree_Expression::MouseCursor(_) => lower_mouse_cursor(expression, ctx),
186 tree_Expression::LinearGradient { .. } => lower_linear_gradient(expression, ctx),
187 tree_Expression::RadialGradient { .. } => lower_radial_gradient(expression, ctx),
188 tree_Expression::ConicGradient { .. } => lower_conic_gradient(expression, ctx),
189 tree_Expression::EnumerationValue(e) => llr_Expression::EnumerationValue(e.clone()),
190 tree_Expression::Keys(ks) => llr_Expression::KeysLiteral(ks.clone()),
191 tree_Expression::ReturnStatement(..) => {
192 panic!("The remove return pass should have removed all return")
193 }
194 tree_Expression::LayoutCacheAccess { .. } => lower_layout_cache_access(expression, ctx),
195 tree_Expression::GridRepeaterCacheAccess { .. } => {
196 lower_grid_repeater_cache_access(expression, ctx)
197 }
198 tree_Expression::OrganizeGridLayout(l) => organize_grid_layout(l, ctx),
199 tree_Expression::ComputeBoxLayoutInfo { layout, orientation, cross_axis_size } => {
200 compute_box_layout_info(layout, *orientation, ctx, cross_axis_size.as_deref())
201 }
202 tree_Expression::ComputeGridLayoutInfo {
203 layout_organized_data_prop,
204 layout,
205 orientation,
206 cross_axis_size,
207 } => compute_grid_layout_info(
208 layout_organized_data_prop,
209 layout,
210 *orientation,
211 ctx,
212 cross_axis_size.as_deref(),
213 ),
214 tree_Expression::SolveBoxLayout(l, o) => solve_box_layout(l, *o, ctx),
215 tree_Expression::SolveGridLayout { layout_organized_data_prop, layout, orientation } => {
216 solve_grid_layout(layout_organized_data_prop, layout, *orientation, ctx)
217 }
218 tree_Expression::SolveFlexboxLayout(l) => solve_flexbox_layout(l, ctx),
219 tree_Expression::ComputeFlexboxLayoutInfo { layout, orientation, cross_axis_size } => {
220 compute_flexbox_layout_info(layout, *orientation, ctx, cross_axis_size.as_deref())
221 }
222 tree_Expression::MinMax { ty, op, lhs, rhs } => llr_Expression::MinMax {
223 ty: ty.clone(),
224 op: *op,
225 lhs: Box::new(lower_expression(lhs, ctx)),
226 rhs: Box::new(lower_expression(rhs, ctx)),
227 },
228 tree_Expression::EmptyComponentFactory => llr_Expression::EmptyComponentFactory,
229 tree_Expression::EmptyDataTransfer => llr_Expression::EmptyDataTransfer,
230 tree_Expression::DebugHook { expression, id, .. } => llr_Expression::DebugHook {
231 expression: Box::new(lower_expression(expression, ctx)),
232 id: id.clone(),
233 },
234 tree_Expression::Closure { arg_name, expression } => llr_Expression::Closure {
235 arg_name: arg_name.clone(),
236 expression: Box::new(lower_expression(expression, ctx)),
237 },
238 }
239}
240
241#[inline(never)]
242fn lower_binary_expression(
243 expression: &tree_Expression,
244 ctx: &mut ExpressionLoweringCtx<'_>,
245) -> llr_Expression {
246 let mut spine = Vec::new();
250 let mut node = expression;
251 while let tree_Expression::BinaryExpression { lhs, rhs, op, .. } = node {
252 spine.push((rhs, *op));
253 node = lhs;
254 }
255 let mut result = lower_expression(node, ctx);
256 for (rhs, op) in spine.into_iter().rev() {
257 result = llr_Expression::BinaryExpression {
258 lhs: Box::new(result),
259 rhs: Box::new(lower_expression(rhs, ctx)),
260 op,
261 };
262 }
263 result
264}
265
266#[inline(never)]
267fn lower_element_reference(
268 expression: &tree_Expression,
269 ctx: &mut ExpressionLoweringCtx<'_>,
270) -> llr_Expression {
271 let tree_Expression::ElementReference(e) = expression else { unreachable!() };
272 let elem = e.upgrade().unwrap();
273 let enclosing = elem.borrow().enclosing_component.upgrade().unwrap();
274 if Rc::ptr_eq(&elem, &enclosing.root_element)
276 && let Some(idx) =
277 ctx.component.menu_item_tree.borrow().iter().position(|c| Rc::ptr_eq(c, &enclosing))
278 {
279 return llr_Expression::NumberLiteral(idx as _);
280 }
281
282 llr_Expression::PropertyReference(
284 ctx.map_property_reference(&NamedReference::new(&elem, SmolStr::default())),
285 )
286}
287
288#[inline(never)]
289fn lower_function_call(
290 expression: &tree_Expression,
291 ctx: &mut ExpressionLoweringCtx<'_>,
292) -> llr_Expression {
293 let tree_Expression::FunctionCall { function, arguments, source_location } = expression else {
294 unreachable!()
295 };
296 match function {
297 Callable::Builtin(BuiltinFunction::RestartTimer) => lower_restart_timer(arguments, ctx),
298 Callable::Builtin(BuiltinFunction::ShowPopupWindow) => {
299 lower_show_popup_window(arguments, ctx)
300 }
301 Callable::Builtin(BuiltinFunction::ClosePopupWindow) => {
302 lower_close_popup_window(arguments, ctx)
303 }
304 Callable::Builtin(f) => {
305 let mut arguments =
306 arguments.iter().map(|e| lower_expression(e, ctx)).collect::<Vec<_>>();
307 #[allow(clippy::collapsible_if)]
309 if *f == BuiltinFunction::Translate {
310 if let llr_Expression::Array { output, .. } = &mut arguments[3] {
311 *output = llr_ArrayOutput::Slice;
312 }
313 #[cfg(feature = "bundle-translations")]
314 if let Some(translation_builder) = ctx.state.translation_builder.as_mut() {
315 return translation_builder.lower_translate_call(arguments);
316 }
317 }
318 if *f == BuiltinFunction::ParseMarkdown
319 && let Some(llr_Expression::Array { output, .. }) = &mut arguments.get_mut(1)
320 {
321 *output = llr_ArrayOutput::Slice;
322 }
323 llr_Expression::BuiltinFunctionCall {
324 function: f.clone(),
325 arguments,
326 source_location: source_location.clone(),
327 }
328 }
329 Callable::Callback(nr) => {
330 let arguments = arguments.iter().map(|e| lower_expression(e, ctx)).collect::<_>();
331 llr_Expression::CallBackCall { callback: ctx.map_property_reference(nr), arguments }
332 }
333 Callable::Function(nr)
334 if nr
335 .element()
336 .borrow()
337 .native_class()
338 .is_some_and(|n| n.properties.contains_key(nr.name())) =>
339 {
340 llr_Expression::ItemMemberFunctionCall { function: ctx.map_property_reference(nr) }
341 }
342 Callable::Function(nr) => {
343 let arguments = arguments.iter().map(|e| lower_expression(e, ctx)).collect::<_>();
344 llr_Expression::FunctionCall { function: ctx.map_property_reference(nr), arguments }
345 }
346 }
347}
348
349#[inline(never)]
350fn lower_condition(
351 expression: &tree_Expression,
352 ctx: &mut ExpressionLoweringCtx<'_>,
353) -> llr_Expression {
354 let tree_Expression::Condition { condition, true_expr, false_expr, .. } = expression else {
355 unreachable!()
356 };
357 let (true_ty, false_ty) = (true_expr.ty(), false_expr.ty());
358 llr_Expression::Condition {
359 condition: Box::new(lower_expression(condition, ctx)),
360 true_expr: Box::new(lower_expression(true_expr, ctx)),
361 false_expr: if false_ty == Type::Invalid || false_ty == Type::Void || true_ty == false_ty {
362 Box::new(lower_expression(false_expr, ctx))
363 } else {
364 Box::new(llr_Expression::Cast {
366 from: Box::new(lower_expression(false_expr, ctx)),
367 to: Type::Void,
368 })
369 },
370 }
371}
372
373#[inline(never)]
374fn lower_mouse_cursor(
375 expression: &tree_Expression,
376 ctx: &mut ExpressionLoweringCtx<'_>,
377) -> llr_Expression {
378 let tree_Expression::MouseCursor(cursor) = expression else { unreachable!() };
379 llr_Expression::MouseCursor(match cursor {
380 MouseCursorInner::BuiltIn(expression) => {
381 crate::llr::MouseCursorInner::BuiltIn(Box::new(lower_expression(expression, ctx)))
382 }
383 MouseCursorInner::CustomMouseCursor { image, hotspot_x, hotspot_y } => {
384 crate::llr::MouseCursorInner::CustomMouseCursor {
385 image: Box::new(lower_expression(image, ctx)),
386 hotspot_x: Box::new(lower_expression(hotspot_x, ctx)),
387 hotspot_y: Box::new(lower_expression(hotspot_y, ctx)),
388 }
389 }
390 })
391}
392
393#[inline(never)]
394fn lower_linear_gradient(
395 expression: &tree_Expression,
396 ctx: &mut ExpressionLoweringCtx<'_>,
397) -> llr_Expression {
398 let tree_Expression::LinearGradient { angle, stops } = expression else { unreachable!() };
399 llr_Expression::LinearGradient {
400 angle: Box::new(lower_expression(angle, ctx)),
401 stops: stops
402 .iter()
403 .map(|(a, b)| (lower_expression(a, ctx), lower_expression(b, ctx)))
404 .collect::<_>(),
405 }
406}
407
408#[inline(never)]
409fn lower_radial_gradient(
410 expression: &tree_Expression,
411 ctx: &mut ExpressionLoweringCtx<'_>,
412) -> llr_Expression {
413 let tree_Expression::RadialGradient { center, radius, stops } = expression else {
414 unreachable!()
415 };
416 llr_Expression::RadialGradient {
417 center: center.as_ref().map(|(cx, cy)| {
418 (Box::new(lower_expression(cx, ctx)), Box::new(lower_expression(cy, ctx)))
419 }),
420 radius: radius.as_ref().map(|r| Box::new(lower_expression(r, ctx))),
421 stops: stops
422 .iter()
423 .map(|(a, b)| (lower_expression(a, ctx), lower_expression(b, ctx)))
424 .collect::<_>(),
425 }
426}
427
428#[inline(never)]
429fn lower_conic_gradient(
430 expression: &tree_Expression,
431 ctx: &mut ExpressionLoweringCtx<'_>,
432) -> llr_Expression {
433 let tree_Expression::ConicGradient { from_angle, center, stops } = expression else {
434 unreachable!()
435 };
436 llr_Expression::ConicGradient {
437 from_angle: Box::new(lower_expression(from_angle, ctx)),
438 center: center.as_ref().map(|(cx, cy)| {
439 (Box::new(lower_expression(cx, ctx)), Box::new(lower_expression(cy, ctx)))
440 }),
441 stops: stops
442 .iter()
443 .map(|(a, b)| (lower_expression(a, ctx), lower_expression(b, ctx)))
444 .collect::<_>(),
445 }
446}
447
448#[inline(never)]
449fn lower_layout_cache_access(
450 expression: &tree_Expression,
451 ctx: &mut ExpressionLoweringCtx<'_>,
452) -> llr_Expression {
453 let tree_Expression::LayoutCacheAccess {
454 layout_cache_prop,
455 index,
456 repeater_index,
457 entries_per_item,
458 } = expression
459 else {
460 unreachable!()
461 };
462 llr_Expression::LayoutCacheAccess {
463 layout_cache_prop: ctx.map_property_reference(layout_cache_prop),
464 index: *index,
465 repeater_index: repeater_index.as_ref().map(|e| lower_expression(e, ctx).into()),
466 entries_per_item: *entries_per_item,
467 }
468}
469
470#[inline(never)]
471fn lower_grid_repeater_cache_access(
472 expression: &tree_Expression,
473 ctx: &mut ExpressionLoweringCtx<'_>,
474) -> llr_Expression {
475 let tree_Expression::GridRepeaterCacheAccess {
476 layout_cache_prop,
477 index,
478 repeater_index,
479 stride,
480 child_offset,
481 inner_repeater_index,
482 entries_per_item,
483 } = expression
484 else {
485 unreachable!()
486 };
487 llr_Expression::GridRepeaterCacheAccess {
488 layout_cache_prop: ctx.map_property_reference(layout_cache_prop),
489 index: *index,
490 repeater_index: lower_expression(repeater_index, ctx).into(),
491 stride: lower_expression(stride, ctx).into(),
492 child_offset: *child_offset,
493 inner_repeater_index: inner_repeater_index
494 .as_ref()
495 .map(|e| lower_expression(e, ctx).into()),
496 entries_per_item: *entries_per_item,
497 }
498}
499
500fn lower_assignment(
501 lhs: &tree_Expression,
502 rhs: &tree_Expression,
503 op: char,
504 ctx: &mut ExpressionLoweringCtx,
505) -> llr_Expression {
506 match lhs {
507 tree_Expression::PropertyReference(nr) => {
508 let rhs = lower_expression(rhs, ctx);
509 let property = ctx.map_property_reference(nr);
510 let value = if op == '=' {
511 rhs
512 } else {
513 llr_Expression::BinaryExpression {
514 lhs: llr_Expression::PropertyReference(property.clone()).into(),
515 rhs: rhs.into(),
516 op,
517 }
518 }
519 .into();
520 llr_Expression::PropertyAssignment { property, value }
521 }
522 tree_Expression::StructFieldAccess { base, name } => {
523 let ty = base.ty();
524
525 let unique_name = ctx.state.unique_struct_assignment_name();
526 let s = tree_Expression::StoreLocalVariable {
527 name: unique_name.clone(),
528 value: base.clone(),
529 };
530 let lower_base =
531 tree_Expression::ReadLocalVariable { name: unique_name, ty: ty.clone() };
532 let mut values = BTreeMap::new();
533 let Type::Struct(ty) = ty else { unreachable!() };
534
535 for field in ty.fields.keys() {
536 let e = if field != name {
537 tree_Expression::StructFieldAccess {
538 base: lower_base.clone().into(),
539 name: field.clone(),
540 }
541 } else if op == '=' {
542 rhs.clone()
543 } else {
544 tree_Expression::BinaryExpression {
545 lhs: tree_Expression::StructFieldAccess {
546 base: lower_base.clone().into(),
547 name: field.clone(),
548 }
549 .into(),
550 rhs: Box::new(rhs.clone()),
551 op,
552 source_location: None,
553 }
554 };
555 values.insert(field.clone(), e);
556 }
557
558 let new_value =
559 tree_Expression::CodeBlock(vec![s, tree_Expression::Struct { ty, values }]);
560 lower_assignment(base, &new_value, '=', ctx)
561 }
562 tree_Expression::RepeaterModelReference { element } => {
563 let rhs = lower_expression(rhs, ctx);
564 let prop =
565 repeater_special_property(element, ctx.component, PropertyIdx::REPEATER_DATA);
566
567 let level = match &prop {
568 MemberReference::Relative { parent_level, .. } => *parent_level,
569 _ => 0,
570 };
571
572 let value = Box::new(if op == '=' {
573 rhs
574 } else {
575 llr_Expression::BinaryExpression {
576 lhs: llr_Expression::PropertyReference(prop).into(),
577 rhs: rhs.into(),
578 op,
579 }
580 });
581
582 llr_Expression::ModelDataAssignment { level, value }
583 }
584 tree_Expression::ArrayIndex { array, index } => {
585 let rhs = lower_expression(rhs, ctx);
586 let array = Box::new(lower_expression(array, ctx));
587 let index = Box::new(lower_expression(index, ctx));
588 let value = Box::new(if op == '=' {
589 rhs
590 } else {
591 llr_Expression::BinaryExpression {
594 lhs: llr_Expression::ArrayIndex { array: array.clone(), index: index.clone() }
595 .into(),
596 rhs: rhs.into(),
597 op,
598 }
599 });
600
601 llr_Expression::ArrayIndexAssignment { array, index, value }
602 }
603 _ => panic!("not a rvalue"),
604 }
605}
606
607pub fn repeater_special_property(
608 element: &Weak<RefCell<Element>>,
609 component: &Rc<crate::object_tree::Component>,
610 property_index: PropertyIdx,
611) -> MemberReference {
612 let enclosing = element.upgrade().unwrap().borrow().enclosing_component.upgrade().unwrap();
613 let mut parent_level = 0;
614 let mut component = component.clone();
615 while !Rc::ptr_eq(&enclosing, &component) {
616 let parent_elem = component.parent_element().unwrap();
617 component = parent_elem.borrow().enclosing_component.upgrade().unwrap();
618 parent_level += 1;
619 }
620 MemberReference::Relative {
621 parent_level: parent_level - 1,
622 local_reference: LocalMemberReference {
623 sub_component_path: Vec::new(),
624 reference: property_index.into(),
625 },
626 }
627}
628
629fn lower_restart_timer(args: &[tree_Expression], ctx: &ExpressionLoweringCtx) -> llr_Expression {
633 if let [tree_Expression::ElementReference(e)] = args {
634 let timer_element = e.upgrade().unwrap();
635 let timer_comp = timer_element.borrow().enclosing_component.upgrade().unwrap();
636 let (parent_level, _) = ctx.find_component(&timer_comp);
637
638 let timer_list = timer_comp.timers.borrow();
639 let timer_index = timer_list
640 .iter()
641 .position(|t| Rc::ptr_eq(&t.element.upgrade().unwrap(), &timer_element))
642 .unwrap();
643
644 llr_Expression::BuiltinFunctionCall {
645 function: BuiltinFunction::RestartTimer,
646 source_location: None,
647 arguments: vec![llr_Expression::PropertyReference(MemberReference::Relative {
648 parent_level,
649 local_reference: LocalMemberReference {
650 sub_component_path: Vec::new(),
651 reference: crate::llr::TimerIdx::from(timer_index).into(),
652 },
653 })],
654 }
655 } else {
656 panic!("invalid arguments to RestartTimer");
657 }
658}
659
660fn lower_popup_owner(
666 e: &ElementWeak,
667 ctx: &mut ExpressionLoweringCtx,
668) -> (Rc<Component>, llr_Expression, usize) {
669 let popup_window = e.upgrade().unwrap();
670 let pop_comp = popup_window.borrow().enclosing_component.upgrade().unwrap();
671 let parent_elem = pop_comp.parent_element().unwrap();
672 let parent_component = parent_elem.borrow().enclosing_component.upgrade().unwrap();
673 let owner_ref = lower_expression(
674 &tree_Expression::ElementReference(Rc::downgrade(&parent_component.root_element)),
675 ctx,
676 );
677 let popup_index = parent_component
678 .popup_windows
679 .borrow()
680 .iter()
681 .position(|p| Rc::ptr_eq(&p.component, &pop_comp))
682 .unwrap();
683 (parent_component, owner_ref, popup_index)
684}
685
686fn lower_show_popup_window(
687 args: &[tree_Expression],
688 ctx: &mut ExpressionLoweringCtx,
689) -> llr_Expression {
690 if let [tree_Expression::ElementReference(e)] = args {
691 let (parent_component, owner_ref, popup_index) = lower_popup_owner(e, ctx);
692 let popup_list = parent_component.popup_windows.borrow();
693 let popup = &popup_list[popup_index];
694 let item_ref = lower_expression(
695 &tree_Expression::ElementReference(Rc::downgrade(&popup.parent_element)),
696 ctx,
697 );
698
699 let mut arguments = vec![
700 llr_Expression::NumberLiteral(popup_index as _),
701 llr_Expression::EnumerationValue(popup.close_policy.clone()),
702 owner_ref,
703 item_ref,
704 ];
705 if let Some(is_open) = &popup.is_open {
709 arguments.push(llr_Expression::PropertyReference(ctx.map_property_reference(is_open)));
710 }
711 llr_Expression::BuiltinFunctionCall {
712 function: BuiltinFunction::ShowPopupWindow,
713 arguments,
714 source_location: None,
715 }
716 } else {
717 panic!("invalid arguments to ShowPopupWindow");
718 }
719}
720
721fn lower_close_popup_window(
722 args: &[tree_Expression],
723 ctx: &mut ExpressionLoweringCtx,
724) -> llr_Expression {
725 if let [tree_Expression::ElementReference(e)] = args {
726 let (_, owner_ref, popup_index) = lower_popup_owner(e, ctx);
727 llr_Expression::BuiltinFunctionCall {
728 function: BuiltinFunction::ClosePopupWindow,
729 arguments: vec![llr_Expression::NumberLiteral(popup_index as _), owner_ref],
730 source_location: None,
731 }
732 } else {
733 panic!("invalid arguments to ClosePopupWindow");
734 }
735}
736
737pub fn lower_animation(a: &PropertyAnimation, ctx: &mut ExpressionLoweringCtx<'_>) -> Animation {
738 fn lower_animation_element(
739 a: &ElementRc,
740 ctx: &mut ExpressionLoweringCtx<'_>,
741 ) -> llr_Expression {
742 llr_Expression::Struct {
743 values: animation_fields()
744 .map(|(k, ty)| {
745 let e = a.borrow().binding_cell_including_synthetic(&k).map_or_else(
746 || {
747 if k == "enabled" {
748 llr_Expression::BoolLiteral(true)
749 } else {
750 llr_Expression::default_value_for_type(&ty).unwrap()
751 }
752 },
753 |v| lower_expression(&v.borrow().expression, ctx),
754 );
755 (k, e)
756 })
757 .collect::<_>(),
758 ty: animation_ty(),
759 }
760 }
761
762 fn animation_fields() -> impl Iterator<Item = (SmolStr, Type)> {
763 IntoIterator::into_iter([
764 (SmolStr::new_static("duration"), Type::Int32),
765 (SmolStr::new_static("iteration-count"), Type::Float32),
766 (
767 SmolStr::new_static("direction"),
768 Type::Enumeration(BUILTIN.enums.AnimationDirection.clone()),
769 ),
770 (SmolStr::new_static("easing"), Type::Easing),
771 (SmolStr::new_static("delay"), Type::Int32),
772 (SmolStr::new_static("enabled"), Type::Bool),
773 ])
774 }
775
776 fn animation_ty() -> Arc<Struct> {
777 Arc::new(Struct::new(animation_fields().collect(), BuiltinStruct::PropertyAnimation))
778 }
779
780 match a {
781 PropertyAnimation::Static(a) => Animation::Static(lower_animation_element(a, ctx)),
782 PropertyAnimation::Transition { state_ref, animations } => {
783 let set_state = llr_Expression::StoreLocalVariable {
784 name: "state".into(),
785 value: Box::new(lower_expression(state_ref, ctx)),
786 };
787 let anim_struct_ty = animation_ty();
788 let animation_ty = Type::Struct(anim_struct_ty.clone());
789 let mut get_anim = llr_Expression::Struct {
790 ty: anim_struct_ty,
791 values: animation_fields()
792 .map(|(k, ty)| {
793 let e = if k == "enabled" {
794 llr_Expression::BoolLiteral(true)
795 } else {
796 llr_Expression::default_value_for_type(&ty).unwrap()
797 };
798 (k, e)
799 })
800 .collect(),
801 };
802 for tr in animations.iter().rev() {
803 let condition = lower_expression(
804 &tr.condition(tree_Expression::ReadLocalVariable {
805 name: "state".into(),
806 ty: state_ref.ty(),
807 }),
808 ctx,
809 );
810 get_anim = llr_Expression::Condition {
811 condition: Box::new(condition),
812 true_expr: Box::new(lower_animation_element(&tr.animation, ctx)),
813 false_expr: Box::new(get_anim),
814 }
815 }
816 let result = llr_Expression::Struct {
817 ty: Arc::new(Struct::new(
819 IntoIterator::into_iter([
820 (SmolStr::new_static("0"), animation_ty),
821 (SmolStr::new_static("1"), Type::Invalid),
823 ])
824 .collect(),
825 StructName::None,
826 )),
827 values: IntoIterator::into_iter([
828 (SmolStr::new_static("0"), get_anim),
829 (
830 SmolStr::new_static("1"),
831 llr_Expression::StructFieldAccess {
832 base: llr_Expression::ReadLocalVariable {
833 name: "state".into(),
834 ty: state_ref.ty(),
835 }
836 .into(),
837 name: "change_time".into(),
838 },
839 ),
840 ])
841 .collect(),
842 };
843 Animation::Transition(llr_Expression::CodeBlock(vec![set_state, result]))
844 }
845 }
846}
847
848fn compile_path(
849 path: &crate::expression_tree::Path,
850 ctx: &mut ExpressionLoweringCtx,
851) -> llr_Expression {
852 fn llr_path_elements(elements: Vec<llr_Expression>) -> llr_Expression {
853 llr_Expression::Cast {
854 from: llr_Expression::Array {
855 element_ty: crate::typeregister::path_element_type(),
856 values: elements,
857 output: llr_ArrayOutput::Slice,
858 }
859 .into(),
860 to: Type::PathData,
861 }
862 }
863
864 match path {
865 crate::expression_tree::Path::Elements(elements) => {
866 let converted_elements = elements
867 .iter()
868 .map(|element| {
869 let element_type = Arc::new(Struct::new(
870 element
871 .element_type
872 .properties
873 .iter()
874 .map(|(k, v)| (k.clone(), v.ty.clone()))
875 .collect(),
876 StructName::Builtin(
877 element
878 .element_type
879 .native_class
880 .builtin_struct
881 .clone()
882 .expect("path elements should have a native_type"),
883 ),
884 ));
885
886 llr_Expression::Struct {
887 ty: element_type,
888 values: element
889 .element_type
890 .properties
891 .iter()
892 .map(|(element_field_name, element_property)| {
893 (
894 element_field_name.clone(),
895 element.bindings.get(element_field_name).map_or_else(
896 || {
897 llr_Expression::default_value_for_type(
898 &element_property.ty,
899 )
900 .unwrap()
901 },
902 |expr| lower_expression(&expr.borrow().expression, ctx),
903 ),
904 )
905 })
906 .collect(),
907 }
908 })
909 .collect();
910 llr_path_elements(converted_elements)
911 }
912 crate::expression_tree::Path::Events(events, points) => {
913 if events.is_empty() || points.is_empty() {
914 return llr_path_elements(Vec::new());
915 }
916
917 let events: Vec<_> = events.iter().map(|event| lower_expression(event, ctx)).collect();
918
919 let event_type = events.first().unwrap().ty(ctx);
920
921 let points: Vec<_> = points.iter().map(|point| lower_expression(point, ctx)).collect();
922
923 let point_type = points.first().unwrap().ty(ctx);
924
925 llr_Expression::Cast {
926 from: llr_Expression::Struct {
927 ty: Arc::new(Struct::new(
928 IntoIterator::into_iter([
929 (SmolStr::new_static("events"), Type::Array(event_type.clone().into())),
930 (SmolStr::new_static("points"), Type::Array(point_type.clone().into())),
931 ])
932 .collect(),
933 StructName::None,
934 )),
935 values: IntoIterator::into_iter([
936 (
937 SmolStr::new_static("events"),
938 llr_Expression::Array {
939 element_ty: event_type,
940 values: events,
941 output: llr_ArrayOutput::Slice,
942 },
943 ),
944 (
945 SmolStr::new_static("points"),
946 llr_Expression::Array {
947 element_ty: point_type,
948 values: points,
949 output: llr_ArrayOutput::Slice,
950 },
951 ),
952 ])
953 .collect(),
954 }
955 .into(),
956 to: Type::PathData,
957 }
958 }
959 crate::expression_tree::Path::Commands(commands) => llr_Expression::Cast {
960 from: lower_expression(commands, ctx).into(),
961 to: Type::PathData,
962 },
963 }
964}
965
966pub fn make_struct(
967 name: impl Into<StructName>,
968 it: impl IntoIterator<Item = (&'static str, Type, llr_Expression)>,
969) -> llr_Expression {
970 let mut fields = BTreeMap::<SmolStr, Type>::new();
971 let mut values = BTreeMap::<SmolStr, llr_Expression>::new();
972 for (name, ty, expr) in it {
973 fields.insert(SmolStr::new(name), ty);
974 values.insert(SmolStr::new(name), expr);
975 }
976
977 llr_Expression::Struct { ty: Arc::new(Struct::new(fields, name)), values }
978}