1use super::{
5 GlobalIdx, GridLayoutRepeatedElement, LayoutRepeatedElement, LocalMemberIndex,
6 LocalMemberReference, MemberReference, RepeatedElementIdx, SubComponentIdx,
7 SubComponentInstanceIdx,
8};
9use crate::expression_tree::{BuiltinFunction, MinMaxOp, OperatorClass};
10use crate::langtype::{Keys, Type};
11use crate::layout::Orientation;
12use itertools::Either;
13use smol_str::SmolStr;
14use std::collections::BTreeMap;
15use std::sync::Arc;
16
17#[derive(Debug, Clone)]
18pub enum ArrayOutput {
19 Slice,
20 Model,
21 Vector,
22}
23
24pub use crate::expression_tree::MouseCursorInner;
25
26#[derive(Debug, Clone)]
29#[allow(clippy::large_enum_variant)]
30pub enum FlexboxMeasureCell {
31 Static { v_info: Expression },
34 Repeated(LayoutRepeatedElement),
37 Fixed,
41}
42
43#[derive(Debug, Clone)]
46#[allow(clippy::large_enum_variant)]
47pub enum BoxMeasureCell {
48 Static { info: Expression },
52 Repeated(LayoutRepeatedElement),
56}
57
58#[derive(Debug, Clone)]
59pub enum Expression {
60 StringLiteral(SmolStr),
62 NumberLiteral(f64),
64 BoolLiteral(bool),
66
67 KeysLiteral(Keys),
69
70 PropertyReference(MemberReference),
72
73 FunctionParameterReference {
75 index: usize,
76 },
78
79 StoreLocalVariable {
81 name: SmolStr,
82 value: Box<Expression>,
83 },
84
85 ReadLocalVariable {
88 name: SmolStr,
89 ty: Type,
90 },
91
92 StructFieldAccess {
94 base: Box<Expression>,
96 name: SmolStr,
97 },
98
99 ArrayIndex {
101 array: Box<Expression>,
103 index: Box<Expression>,
104 },
105
106 Cast {
108 from: Box<Expression>,
109 to: Type,
110 },
111
112 CodeBlock(Vec<Expression>),
114
115 BuiltinFunctionCall {
117 function: BuiltinFunction,
118 arguments: Vec<Expression>,
119 source_location: Option<crate::diagnostics::SourceLocation>,
121 },
122 CallBackCall {
123 callback: MemberReference,
124 arguments: Vec<Expression>,
125 },
126 FunctionCall {
127 function: MemberReference,
128 arguments: Vec<Expression>,
129 },
130 ItemMemberFunctionCall {
131 function: MemberReference,
132 },
133
134 ExtraBuiltinFunctionCall {
137 return_ty: Type,
138 function: String,
139 arguments: Vec<Expression>,
140 },
141
142 PropertyAssignment {
144 property: MemberReference,
145 value: Box<Expression>,
146 },
147 ModelDataAssignment {
149 level: usize,
151 value: Box<Expression>,
152 },
153 ArrayIndexAssignment {
155 array: Box<Expression>,
156 index: Box<Expression>,
157 value: Box<Expression>,
158 },
159 SliceIndexAssignment {
162 slice_name: SmolStr,
164 index: usize,
165 value: Box<Expression>,
166 },
167
168 BinaryExpression {
169 lhs: Box<Expression>,
170 rhs: Box<Expression>,
171 op: char,
173 },
174
175 UnaryOp {
176 sub: Box<Expression>,
177 op: char,
179 },
180
181 ImageReference {
182 resource_ref: crate::expression_tree::ImageReference,
183 nine_slice: Option<[u16; 4]>,
184 },
185
186 Condition {
187 condition: Box<Expression>,
188 true_expr: Box<Expression>,
189 false_expr: Box<Expression>,
190 },
191
192 Array {
193 element_ty: Type,
194 values: Vec<Expression>,
195 output: ArrayOutput,
197 },
198 Struct {
199 ty: Arc<crate::langtype::Struct>,
200 values: BTreeMap<SmolStr, Expression>,
201 },
202
203 EasingCurve(crate::expression_tree::EasingCurve),
204
205 MouseCursor(MouseCursorInner<Expression>),
206
207 LinearGradient {
208 angle: Box<Expression>,
209 stops: Vec<(Expression, Expression)>,
211 },
212
213 RadialGradient {
214 center: Option<(Box<Expression>, Box<Expression>)>,
217 radius: Option<Box<Expression>>,
220 stops: Vec<(Expression, Expression)>,
222 },
223
224 ConicGradient {
225 from_angle: Box<Expression>,
227 center: Option<(Box<Expression>, Box<Expression>)>,
230 stops: Vec<(Expression, Expression)>,
232 },
233
234 EnumerationValue(crate::langtype::EnumerationValue),
235
236 LayoutCacheAccess {
239 layout_cache_prop: MemberReference,
240 index: usize,
241 repeater_index: Option<Box<Expression>>,
242 entries_per_item: usize,
243 },
244 GridRepeaterCacheAccess {
247 layout_cache_prop: MemberReference,
248 index: usize,
249 repeater_index: Box<Expression>,
250 stride: Box<Expression>,
251 child_offset: usize,
252 inner_repeater_index: Option<Box<Expression>>,
253 entries_per_item: usize,
254 },
255 WithLayoutItemInfo {
258 cells_variable: String,
260 repeater_indices_var_name: Option<SmolStr>,
262 repeater_steps_var_name: Option<SmolStr>,
264 elements: Vec<Either<Expression, LayoutRepeatedElement>>,
266 orientation: Orientation,
267 repeated_cross_size: Option<Box<Expression>>,
275 sub_expression: Box<Expression>,
276 },
277 WithFlexboxLayoutItemInfo {
280 cells_h_variable: String,
282 cells_v_variable: String,
284 flex_props_variable: Option<String>,
291 repeater_indices_var_name: Option<SmolStr>,
293 elements: Vec<Either<(Expression, Expression, Expression), LayoutRepeatedElement>>,
296 repeated_cross_width: Option<Box<Expression>>,
301 sub_expression: Box<Expression>,
302 },
303 SolveFlexboxLayoutWithMeasure {
314 data: Box<Expression>,
317 repeater_indices: Box<Expression>,
318 measure_cells: Vec<FlexboxMeasureCell>,
319 },
320 BoxLayoutInfoOrthoWithMeasure {
326 solve_data: Box<Expression>,
329 padding_ortho: Box<Expression>,
331 measure_cells: Vec<BoxMeasureCell>,
332 },
333 FlexboxLayoutInfoCrossAxisWithMeasure {
339 arguments: Vec<Expression>,
342 measure_cells: Vec<FlexboxMeasureCell>,
343 },
344 WithGridInputData {
347 cells_variable: String,
349 repeater_indices_var_name: SmolStr,
351 repeater_steps_var_name: SmolStr,
353 elements: Vec<Either<Expression, GridLayoutRepeatedElement>>,
355 sub_expression: Box<Expression>,
356 },
357
358 MinMax {
359 ty: Type,
360 op: MinMaxOp,
361 lhs: Box<Expression>,
362 rhs: Box<Expression>,
363 },
364
365 EmptyComponentFactory,
366
367 EmptyDataTransfer,
368
369 TranslationReference {
371 format_args: Box<Expression>,
373 string_index: usize,
374 plural: Option<Box<Expression>>,
376 },
377
378 Closure {
379 arg_name: SmolStr,
380 expression: Box<Expression>,
381 },
382
383 DebugHook {
386 expression: Box<Expression>,
387 id: SmolStr,
388 },
389}
390
391pub fn binary_expression_ty(op: char, lhs_ty: impl FnOnce() -> Type) -> Type {
395 if crate::expression_tree::operator_class(op) != OperatorClass::ArithmeticOp {
396 Type::Bool
397 } else {
398 lhs_ty()
399 }
400}
401
402impl Expression {
403 pub fn default_value_for_type(ty: &Type) -> Option<Self> {
404 Some(match ty {
405 Type::Invalid
406 | Type::Callback { .. }
407 | Type::Function { .. }
408 | Type::Void
409 | Type::InferredProperty
410 | Type::InferredCallback
411 | Type::ElementReference
412 | Type::LayoutCache
413 | Type::ArrayOfU16
414 | Type::Closure => return None,
415 Type::Float32
416 | Type::Duration
417 | Type::Int32
418 | Type::Angle
419 | Type::PhysicalLength
420 | Type::LogicalLength
421 | Type::Rem
422 | Type::UnitProduct(_) => Expression::NumberLiteral(0.),
423 Type::Percent => Expression::NumberLiteral(1.),
424 Type::String => Expression::StringLiteral(SmolStr::default()),
425 Type::Color => {
426 Expression::Cast { from: Box::new(Expression::NumberLiteral(0.)), to: ty.clone() }
427 }
428 Type::Image => Expression::ImageReference {
429 resource_ref: crate::expression_tree::ImageReference::None,
430 nine_slice: None,
431 },
432 Type::Bool => Expression::BoolLiteral(false),
433 Type::Model => return None,
434 Type::PathData => return None,
435 Type::Array(element_ty) => Expression::Array {
436 element_ty: (**element_ty).clone(),
437 values: Vec::new(),
438 output: ArrayOutput::Model,
439 },
440 Type::Struct(s) => Expression::Struct {
441 ty: s.clone(),
442 values: s
443 .fields
444 .iter()
445 .map(|(k, v)| {
446 let value = match s.field_defaults.get(k) {
447 Some(default_value) => {
448 super::lower_expression::lower_constant_expression(default_value)
449 }
450 None => Expression::default_value_for_type(v)?,
451 };
452 Some((k.clone(), value))
453 })
454 .collect::<Option<_>>()?,
455 },
456 Type::Easing => Expression::EasingCurve(crate::expression_tree::EasingCurve::default()),
457 Type::MouseCursor => {
458 let e = crate::typeregister::BUILTIN.enums.BuiltInMouseCursor.clone();
459 Expression::MouseCursor(MouseCursorInner::BuiltIn(Box::new(
460 Expression::EnumerationValue(e.default_value()),
461 )))
462 }
463 Type::Brush => Expression::Cast {
464 from: Box::new(Expression::default_value_for_type(&Type::Color)?),
465 to: Type::Brush,
466 },
467 Type::Enumeration(enumeration) => {
468 Expression::EnumerationValue(enumeration.clone().default_value())
469 }
470 Type::Keys => Expression::KeysLiteral(Keys::default()),
471 Type::DataTransfer => Expression::EmptyDataTransfer,
472 Type::ComponentFactory => Expression::EmptyComponentFactory,
473 Type::StyledText => Expression::BuiltinFunctionCall {
474 source_location: None,
475 function: BuiltinFunction::StringToStyledText,
476 arguments: vec![Expression::StringLiteral(SmolStr::default())],
477 },
478 })
479 }
480
481 pub fn ty(&self, ctx: &dyn TypeResolutionContext) -> Type {
482 match self {
483 Self::StringLiteral(_) => Type::String,
484 Self::NumberLiteral(_) => Type::Float32,
485 Self::BoolLiteral(_) => Type::Bool,
486 Self::PropertyReference(prop) => ctx.property_ty(prop).clone(),
487 Self::FunctionParameterReference { index } => ctx.arg_type(*index).clone(),
488 Self::StoreLocalVariable { .. } => Type::Void,
489 Self::ReadLocalVariable { ty, .. } => ty.clone(),
490 Self::StructFieldAccess { base, name } => match base.ty(ctx) {
491 Type::Struct(s) => s.fields[name].clone(),
492 _ => unreachable!(),
493 },
494 Self::ArrayIndex { array, .. } => match array.ty(ctx) {
495 Type::Array(ty) => (*ty).clone(),
496 _ => unreachable!(),
497 },
498 Self::Cast { to, .. } => to.clone(),
499 Self::CodeBlock(sub) => sub.last().map_or(Type::Void, |e| e.ty(ctx)),
500 Self::BuiltinFunctionCall { function, .. } => function.ty().return_type.clone(),
501 Self::CallBackCall { callback, .. } => match ctx.property_ty(callback) {
502 Type::Callback(callback) => callback.return_type.clone(),
503 _ => Type::Invalid,
504 },
505 Self::FunctionCall { function, .. } => ctx.property_ty(function).clone(),
506 Self::ItemMemberFunctionCall { function } => match ctx.property_ty(function) {
507 Type::Function(function) => function.return_type.clone(),
508 _ => Type::Invalid,
509 },
510 Self::ExtraBuiltinFunctionCall { return_ty, .. } => return_ty.clone(),
511 Self::PropertyAssignment { .. } => Type::Void,
512 Self::ModelDataAssignment { .. } => Type::Void,
513 Self::ArrayIndexAssignment { .. } => Type::Void,
514 Self::SliceIndexAssignment { .. } => Type::Void,
515 Self::BinaryExpression { lhs, rhs: _, op } => binary_expression_ty(*op, || lhs.ty(ctx)),
516 Self::UnaryOp { sub, .. } => sub.ty(ctx),
517 Self::ImageReference { .. } => Type::Image,
518 Self::Condition { false_expr, .. } => false_expr.ty(ctx),
519 Self::Array { element_ty, .. } => Type::Array(element_ty.clone().into()),
520 Self::Struct { ty, .. } => ty.clone().into(),
521 Self::EasingCurve(_) => Type::Easing,
522 Self::MouseCursor(_) => Type::MouseCursor,
523 Self::LinearGradient { .. } => Type::Brush,
524 Self::RadialGradient { .. } => Type::Brush,
525 Self::ConicGradient { .. } => Type::Brush,
526 Self::EnumerationValue(e) => Type::Enumeration(e.enumeration.clone()),
527 Self::KeysLiteral(_) => Type::Keys,
528 Self::LayoutCacheAccess { .. } => Type::LogicalLength,
529 Self::GridRepeaterCacheAccess { .. } => Type::LogicalLength,
530 Self::WithLayoutItemInfo { sub_expression, .. } => sub_expression.ty(ctx),
531 Self::WithFlexboxLayoutItemInfo { sub_expression, .. } => sub_expression.ty(ctx),
532 Self::SolveFlexboxLayoutWithMeasure { .. } => Type::LayoutCache,
533 Self::BoxLayoutInfoOrthoWithMeasure { .. } => {
534 crate::typeregister::layout_info_type().into()
535 }
536 Self::FlexboxLayoutInfoCrossAxisWithMeasure { .. } => {
537 crate::typeregister::layout_info_type().into()
538 }
539 Self::WithGridInputData { sub_expression, .. } => sub_expression.ty(ctx),
540 Self::MinMax { ty, .. } => ty.clone(),
541 Self::EmptyComponentFactory => Type::ComponentFactory,
542 Self::EmptyDataTransfer => Type::DataTransfer,
543 Self::TranslationReference { .. } => Type::String,
544 Self::Closure { .. } => Type::Closure,
545 Self::DebugHook { expression, .. } => expression.ty(ctx),
546 }
547 }
548}
549
550macro_rules! visit_impl {
551 ($self:ident, $visitor:ident, $as_ref:ident, $iter:ident, $values:ident) => {
552 match $self {
553 Expression::StringLiteral(_) => {}
554 Expression::NumberLiteral(_) => {}
555 Expression::BoolLiteral(_) => {}
556 Expression::PropertyReference(_) => {}
557 Expression::FunctionParameterReference { .. } => {}
558 Expression::StoreLocalVariable { value, .. } => $visitor(value),
559 Expression::ReadLocalVariable { .. } => {}
560 Expression::StructFieldAccess { base, .. } => $visitor(base),
561 Expression::ArrayIndex { array, index } => {
562 $visitor(array);
563 $visitor(index);
564 }
565 Expression::Cast { from, .. } => $visitor(from),
566 Expression::CodeBlock(b) => b.$iter().for_each($visitor),
567 Expression::BuiltinFunctionCall { arguments, .. }
568 | Expression::CallBackCall { arguments, .. }
569 | Expression::FunctionCall { arguments, .. } => arguments.$iter().for_each($visitor),
570 Expression::ItemMemberFunctionCall { function: _ } => {}
571 Expression::ExtraBuiltinFunctionCall { arguments, .. } => {
572 arguments.$iter().for_each($visitor)
573 }
574 Expression::PropertyAssignment { value, .. } => $visitor(value),
575 Expression::ModelDataAssignment { value, .. } => $visitor(value),
576 Expression::ArrayIndexAssignment { array, index, value } => {
577 $visitor(array);
578 $visitor(index);
579 $visitor(value);
580 }
581 Expression::SliceIndexAssignment { value, .. } => {
582 $visitor(value);
583 }
584 Expression::BinaryExpression { lhs, rhs, .. } => {
585 $visitor(lhs);
586 $visitor(rhs);
587 }
588 Expression::UnaryOp { sub, .. } => {
589 $visitor(sub);
590 }
591 Expression::ImageReference { .. } => {}
592 Expression::Condition { condition, true_expr, false_expr } => {
593 $visitor(condition);
594 $visitor(true_expr);
595 $visitor(false_expr);
596 }
597 Expression::Array { values, .. } => values.$iter().for_each($visitor),
598 Expression::Struct { values, .. } => values.$values().for_each($visitor),
599 Expression::EasingCurve(_) => {}
600 Expression::MouseCursor(cursor) => match cursor {
601 MouseCursorInner::CustomMouseCursor { image, hotspot_x, hotspot_y } => {
602 $visitor(image);
603 $visitor(hotspot_x);
604 $visitor(hotspot_y);
605 }
606 MouseCursorInner::BuiltIn(e) => {
607 $visitor(e);
608 }
609 },
610 Expression::LinearGradient { angle, stops } => {
611 $visitor(angle);
612 for (a, b) in stops {
613 $visitor(a);
614 $visitor(b);
615 }
616 }
617 Expression::RadialGradient { center, radius, stops } => {
618 if let Some((cx, cy)) = center {
619 $visitor(cx);
620 $visitor(cy);
621 }
622 if let Some(r) = radius {
623 $visitor(r);
624 }
625 for (a, b) in stops {
626 $visitor(a);
627 $visitor(b);
628 }
629 }
630 Expression::ConicGradient { from_angle, center, stops } => {
631 $visitor(from_angle);
632 if let Some((cx, cy)) = center {
633 $visitor(cx);
634 $visitor(cy);
635 }
636 for (a, b) in stops {
637 $visitor(a);
638 $visitor(b);
639 }
640 }
641 Expression::EnumerationValue(_) => {}
642 Expression::KeysLiteral(_) => {}
643 Expression::LayoutCacheAccess { repeater_index, .. } => {
644 if let Some(repeater_index) = repeater_index {
645 $visitor(repeater_index);
646 }
647 }
648 Expression::GridRepeaterCacheAccess {
649 repeater_index,
650 stride,
651 inner_repeater_index,
652 ..
653 } => {
654 $visitor(repeater_index);
655 $visitor(stride);
656 if let Some(inner_repeater_index) = inner_repeater_index {
657 $visitor(inner_repeater_index);
658 }
659 }
660 Expression::WithLayoutItemInfo {
661 elements,
662 repeated_cross_size,
663 sub_expression,
664 ..
665 } => {
666 $visitor(sub_expression);
667 if let Some(s) = repeated_cross_size {
668 $visitor(s);
669 }
670 elements.$iter().for_each(|x| match x.$as_ref() {
671 Either::Left(e) => $visitor(e),
672 Either::Right(r) => {
673 if let Some(w) = r.cross_width.$as_ref() {
674 $visitor(w);
675 }
676 }
677 });
678 }
679 Expression::WithFlexboxLayoutItemInfo {
680 elements,
681 repeated_cross_width,
682 sub_expression,
683 ..
684 } => {
685 $visitor(sub_expression);
686 if let Some(w) = repeated_cross_width {
687 $visitor(w);
688 }
689 elements.$iter().for_each(|x| match x.$as_ref() {
690 Either::Left((h, v, f)) => {
691 $visitor(h);
692 $visitor(v);
693 $visitor(f);
697 }
698 Either::Right(r) => {
699 if let Some(w) = r.cross_width.$as_ref() {
700 $visitor(w);
701 }
702 }
703 });
704 }
705 Expression::SolveFlexboxLayoutWithMeasure { data, repeater_indices, measure_cells } => {
706 $visitor(data);
707 $visitor(repeater_indices);
708 measure_cells.$iter().for_each(|x| match x {
709 FlexboxMeasureCell::Static { v_info } => $visitor(v_info),
710 FlexboxMeasureCell::Repeated(r) => {
711 if let Some(w) = r.cross_width.$as_ref() {
712 $visitor(w);
713 }
714 }
715 FlexboxMeasureCell::Fixed => {}
716 });
717 }
718 Expression::BoxLayoutInfoOrthoWithMeasure {
719 solve_data,
720 padding_ortho,
721 measure_cells,
722 } => {
723 $visitor(solve_data);
724 $visitor(padding_ortho);
725 measure_cells.$iter().for_each(|x| match x {
726 BoxMeasureCell::Static { info } => $visitor(info),
727 BoxMeasureCell::Repeated(r) => {
728 if let Some(w) = r.cross_width.$as_ref() {
729 $visitor(w);
730 }
731 }
732 });
733 }
734 Expression::FlexboxLayoutInfoCrossAxisWithMeasure { arguments, measure_cells } => {
735 arguments.$iter().for_each(&mut $visitor);
736 measure_cells.$iter().for_each(|x| {
737 if let FlexboxMeasureCell::Static { v_info } = x {
738 $visitor(v_info);
739 }
740 });
741 }
742 Expression::WithGridInputData { elements, sub_expression, .. } => {
743 $visitor(sub_expression);
744 elements.$iter().filter_map(|x| x.$as_ref().left()).for_each($visitor);
745 }
746 Expression::MinMax { ty: _, op: _, lhs, rhs } => {
747 $visitor(lhs);
748 $visitor(rhs);
749 }
750 Expression::EmptyComponentFactory => {}
751 Expression::EmptyDataTransfer => {}
752 Expression::TranslationReference { format_args, plural, string_index: _ } => {
753 $visitor(format_args);
754 if let Some(plural) = plural {
755 $visitor(plural);
756 }
757 }
758 Expression::Closure { expression, .. } => {
759 $visitor(expression);
760 }
761 Expression::DebugHook { expression, id: _ } => $visitor(expression),
762 }
763 };
764}
765
766impl Expression {
767 pub fn visit(&self, mut visitor: impl FnMut(&Self)) {
769 visit_impl!(self, visitor, as_ref, iter, values)
770 }
771
772 pub fn visit_mut(&mut self, mut visitor: impl FnMut(&mut Self)) {
774 visit_impl!(self, visitor, as_mut, iter_mut, values_mut)
775 }
776
777 pub fn visit_recursive(&self, visitor: &mut dyn FnMut(&Self)) {
779 visitor(self);
780 self.visit(|e| e.visit_recursive(visitor));
781 }
782
783 pub fn visit_recursive_mut(&mut self, visitor: &mut dyn FnMut(&mut Self)) {
785 visitor(self);
786 self.visit_mut(|e| e.visit_recursive_mut(visitor));
787 }
788
789 pub fn visit_property_references(
790 &self,
791 ctx: &EvaluationContext,
792 visitor: &mut dyn FnMut(&MemberReference, &EvaluationContext),
793 ) {
794 self.visit_recursive(&mut |expr| {
795 let p = match expr {
796 Expression::PropertyReference(p) => p,
797 Expression::CallBackCall { callback, .. } => callback,
798 Expression::FunctionCall { function, .. } => function,
802 Expression::PropertyAssignment { property, .. } => {
803 if let Some((a, map)) = &ctx.property_info(property).animation {
804 let ctx2 = map.map_context(ctx);
805 a.visit_property_references(&ctx2, visitor);
806 }
807 property
808 }
809 Expression::ModelDataAssignment { .. } => return,
811 Expression::LayoutCacheAccess { layout_cache_prop, .. } => layout_cache_prop,
812 Expression::GridRepeaterCacheAccess { layout_cache_prop, .. } => layout_cache_prop,
813 _ => return,
814 };
815 visitor(p, ctx)
816 });
817 }
818}
819
820pub trait TypeResolutionContext {
821 fn property_ty(&self, _: &MemberReference) -> &Type;
825
826 fn arg_type(&self, _index: usize) -> &Type {
828 unimplemented!()
829 }
830}
831
832#[derive(Clone, Copy)]
834pub struct ParentScope<'a> {
835 pub sub_component: SubComponentIdx,
837 pub repeater_index: Option<RepeatedElementIdx>,
839 pub parent: Option<&'a ParentScope<'a>>,
841}
842
843impl<'a> ParentScope<'a> {
844 pub fn new<T>(
845 ctx: &'a EvaluationContext<'a, T>,
846 repeater_index: Option<RepeatedElementIdx>,
847 ) -> Self {
848 let EvaluationScope::SubComponent(sub_component, parent) = ctx.current_scope else {
849 unreachable!()
850 };
851 Self { sub_component, repeater_index, parent }
852 }
853}
854
855#[derive(Clone, Copy)]
856pub enum EvaluationScope<'a> {
857 SubComponent(SubComponentIdx, Option<&'a ParentScope<'a>>),
859 Global(GlobalIdx),
861 Const,
864}
865
866#[derive(Clone)]
867pub struct EvaluationContext<'a, T = ()> {
868 pub compilation_unit: &'a super::CompilationUnit,
869 pub current_scope: EvaluationScope<'a>,
870 pub generator_state: T,
871
872 pub argument_types: &'a [Type],
874}
875
876impl<'a, T> EvaluationContext<'a, T> {
877 pub fn new_sub_component(
878 compilation_unit: &'a super::CompilationUnit,
879 sub_component: SubComponentIdx,
880 generator_state: T,
881 parent: Option<&'a ParentScope<'a>>,
882 ) -> Self {
883 Self {
884 compilation_unit,
885 current_scope: EvaluationScope::SubComponent(sub_component, parent),
886 generator_state,
887 argument_types: &[],
888 }
889 }
890
891 pub fn new_global(
892 compilation_unit: &'a super::CompilationUnit,
893 global: GlobalIdx,
894 generator_state: T,
895 ) -> Self {
896 Self {
897 compilation_unit,
898 current_scope: EvaluationScope::Global(global),
899 generator_state,
900 argument_types: &[],
901 }
902 }
903
904 pub fn new_const(compilation_unit: &'a super::CompilationUnit, generator_state: T) -> Self {
908 Self {
909 compilation_unit,
910 current_scope: EvaluationScope::Const,
911 generator_state,
912 argument_types: &[],
913 }
914 }
915
916 pub(crate) fn property_info<'b>(&'b self, prop: &MemberReference) -> PropertyInfoResult<'b> {
917 fn match_in_sub_component<'b>(
918 cu: &'b super::CompilationUnit,
919 sc: &'b super::SubComponent,
920 prop: &LocalMemberReference,
921 map: ContextMap,
922 ) -> PropertyInfoResult<'b> {
923 let use_count_and_ty = || {
924 let mut sc = sc;
925 for i in &prop.sub_component_path {
926 sc = &cu.sub_components[sc.sub_components[*i].ty];
927 }
928 match &prop.reference {
929 LocalMemberIndex::Property(property_index) => {
930 sc.properties.get(*property_index).map(|x| (&x.use_count, &x.ty))
931 }
932 LocalMemberIndex::Callback(callback_index) => {
933 sc.callbacks.get(*callback_index).map(|x| (&x.use_count, &x.ty))
934 }
935 _ => None,
936 }
937 };
938
939 let animation = sc.animations.get(prop).map(|a| (a, map.clone()));
940 let analysis = sc.prop_analysis.get(&prop.clone().into());
941 if let Some(a) = &analysis
942 && let Some(init) = a.property_init
943 {
944 let u = use_count_and_ty();
945 return PropertyInfoResult {
946 analysis: Some(&a.analysis),
947 binding: Some((&sc.property_init[init].1, map)),
948 animation,
949 ty: u.map_or(Type::Invalid, |x| x.1.clone()),
950 use_count: u.map(|x| x.0),
951 };
952 }
953 let mut r = if let &[idx, ref rest @ ..] = prop.sub_component_path.as_slice() {
954 let prop2 = LocalMemberReference {
955 sub_component_path: rest.to_vec(),
956 reference: prop.reference.clone(),
957 };
958 match_in_sub_component(
959 cu,
960 &cu.sub_components[sc.sub_components[idx].ty],
961 &prop2,
962 map.deeper_in_sub_component(idx),
963 )
964 } else {
965 let u = use_count_and_ty();
966 PropertyInfoResult {
967 ty: u.map_or(Type::Invalid, |x| x.1.clone()),
968 use_count: u.map(|x| x.0),
969 ..Default::default()
970 }
971 };
972
973 if animation.is_some() {
974 r.animation = animation
975 };
976 if let Some(a) = analysis {
977 r.analysis = Some(&a.analysis);
978 }
979 r
980 }
981
982 fn in_global<'a>(
983 g: &'a super::GlobalComponent,
984 r: &'_ LocalMemberIndex,
985 map: ContextMap,
986 ) -> PropertyInfoResult<'a> {
987 let binding = g.init_values.get(r).map(|b| (b, map));
988 match r {
989 LocalMemberIndex::Property(index) => {
990 let property_decl = &g.properties[*index];
991 PropertyInfoResult {
992 analysis: Some(&g.prop_analysis[*index]),
993 binding,
994 animation: None,
995 ty: property_decl.ty.clone(),
996 use_count: Some(&property_decl.use_count),
997 }
998 }
999 LocalMemberIndex::Callback(index) => {
1000 let callback_decl = &g.callbacks[*index];
1001 PropertyInfoResult {
1002 analysis: None,
1003 binding,
1004 animation: None,
1005 ty: callback_decl.ty.clone(),
1006 use_count: Some(&callback_decl.use_count),
1007 }
1008 }
1009 _ => PropertyInfoResult::default(),
1010 }
1011 }
1012
1013 match prop {
1014 MemberReference::Relative { parent_level, local_reference } => {
1015 match self.current_scope {
1016 EvaluationScope::Global(g) => {
1017 let g = &self.compilation_unit.globals[g];
1018 in_global(g, &local_reference.reference, ContextMap::Identity)
1019 }
1020 EvaluationScope::SubComponent(mut sc, mut parent) => {
1021 for _ in 0..*parent_level {
1022 let Some(p) = parent else {
1026 return PropertyInfoResult::default();
1027 };
1028 sc = p.sub_component;
1029 parent = p.parent;
1030 }
1031 match_in_sub_component(
1032 self.compilation_unit,
1033 &self.compilation_unit.sub_components[sc],
1034 local_reference,
1035 ContextMap::from_parent_level(*parent_level),
1036 )
1037 }
1038 EvaluationScope::Const => {
1039 panic!("property reference in a constant expression")
1040 }
1041 }
1042 }
1043 MemberReference::Global { global_index, member } => {
1044 let g = &self.compilation_unit.globals[*global_index];
1045 in_global(g, member, ContextMap::InGlobal(*global_index))
1046 }
1047 }
1048 }
1049
1050 pub(crate) fn function_info<'b>(
1053 &'b self,
1054 reference: &MemberReference,
1055 ) -> Option<(&'b super::Function, ContextMap)> {
1056 let cu = self.compilation_unit;
1057 match reference {
1058 MemberReference::Relative { parent_level, local_reference } => {
1059 let LocalMemberIndex::Function(idx) = local_reference.reference else {
1061 return None;
1062 };
1063 let mut scope = self.current_scope;
1064 for _ in 0..*parent_level {
1065 let EvaluationScope::SubComponent(_, Some(p)) = scope else { return None };
1066 scope = EvaluationScope::SubComponent(p.sub_component, p.parent);
1067 }
1068 let EvaluationScope::SubComponent(mut sc, _) = scope else { return None };
1069 for i in &local_reference.sub_component_path {
1070 sc = cu.sub_components[sc].sub_components[*i].ty;
1071 }
1072 Some((
1073 cu.sub_components[sc].functions.get(idx)?,
1074 ContextMap::from_parent_level(*parent_level)
1075 .deeper_by_path(&local_reference.sub_component_path),
1076 ))
1077 }
1078 MemberReference::Global { global_index, member } => {
1079 let LocalMemberIndex::Function(idx) = member else { return None };
1080 Some((
1081 cu.globals[*global_index].functions.get(*idx)?,
1082 ContextMap::InGlobal(*global_index),
1083 ))
1084 }
1085 }
1086 }
1087
1088 pub fn with_reference_scope<R>(
1095 &self,
1096 parent_level: usize,
1097 sub_component_path: &[SubComponentInstanceIdx],
1098 f: impl FnOnce(ParentScope<'_>) -> R,
1099 ) -> R {
1100 fn descend<R>(
1101 cu: &super::CompilationUnit,
1102 sc: SubComponentIdx,
1103 parent: Option<&ParentScope<'_>>,
1104 path: &[SubComponentInstanceIdx],
1105 f: impl FnOnce(ParentScope<'_>) -> R,
1106 ) -> R {
1107 if let [first, rest @ ..] = path {
1108 let ps = ParentScope { sub_component: sc, repeater_index: None, parent };
1109 let child = cu.sub_components[sc].sub_components[*first].ty;
1110 descend(cu, child, Some(&ps), rest, f)
1111 } else {
1112 f(ParentScope { sub_component: sc, repeater_index: None, parent })
1113 }
1114 }
1115 let EvaluationScope::SubComponent(mut sc, mut parent) = self.current_scope else {
1116 panic!("not in a sub-component scope")
1117 };
1118 for _ in 0..parent_level {
1119 let p = parent.expect("invalid parent reference");
1120 sc = p.sub_component;
1121 parent = p.parent;
1122 }
1123 descend(self.compilation_unit, sc, parent, sub_component_path, f)
1124 }
1125
1126 pub fn current_sub_component(&self) -> Option<&super::SubComponent> {
1127 let EvaluationScope::SubComponent(i, _) = self.current_scope else { return None };
1128 self.compilation_unit.sub_components.get(i)
1129 }
1130
1131 pub fn current_global(&self) -> Option<&super::GlobalComponent> {
1132 let EvaluationScope::Global(i) = self.current_scope else { return None };
1133 self.compilation_unit.globals.get(i)
1134 }
1135
1136 pub fn parent_sub_component_idx(&self, parent: usize) -> Option<SubComponentIdx> {
1137 let EvaluationScope::SubComponent(mut sc, mut par) = self.current_scope else {
1138 return None;
1139 };
1140 for _ in 0..parent {
1141 let p = par?;
1142 sc = p.sub_component;
1143 par = p.parent;
1144 }
1145 Some(sc)
1146 }
1147
1148 pub fn relative_property_ty(
1149 &self,
1150 local_reference: &LocalMemberReference,
1151 parent_level: usize,
1152 ) -> &Type {
1153 if let Some(g) = self.current_global() {
1154 return match &local_reference.reference {
1155 LocalMemberIndex::Property(property_idx) => &g.properties[*property_idx].ty,
1156 LocalMemberIndex::Function(function_idx) => &g.functions[*function_idx].ret_ty,
1157 LocalMemberIndex::Callback(callback_idx) => &g.callbacks[*callback_idx].ty,
1158 LocalMemberIndex::Native { .. } | LocalMemberIndex::Timer(_) => unreachable!(),
1159 };
1160 }
1161
1162 let mut sc = &self.compilation_unit.sub_components
1163 [self.parent_sub_component_idx(parent_level).unwrap()];
1164 for i in &local_reference.sub_component_path {
1165 sc = &self.compilation_unit.sub_components[sc.sub_components[*i].ty];
1166 }
1167 match &local_reference.reference {
1168 LocalMemberIndex::Property(property_index) => &sc.properties[*property_index].ty,
1169 LocalMemberIndex::Function(function_index) => &sc.functions[*function_index].ret_ty,
1170 LocalMemberIndex::Callback(callback_index) => &sc.callbacks[*callback_index].ty,
1171 LocalMemberIndex::Timer(_) => unreachable!("a timer reference has no type"),
1172 LocalMemberIndex::Native { item_index, prop_name, .. } => {
1173 if prop_name == "elements" {
1174 return &Type::PathData;
1176 }
1177 let item = &sc.items[*item_index];
1178 item.ty.lookup_property(prop_name).unwrap_or_else(|| {
1179 panic!("Failed to lookup property {prop_name} for {}", item.name)
1180 })
1181 }
1182 }
1183 }
1184}
1185
1186impl<T> TypeResolutionContext for EvaluationContext<'_, T> {
1187 fn property_ty(&self, prop: &MemberReference) -> &Type {
1188 match prop {
1189 MemberReference::Relative { parent_level, local_reference } => {
1190 self.relative_property_ty(local_reference, *parent_level)
1191 }
1192 MemberReference::Global { global_index, member } => {
1193 let g = &self.compilation_unit.globals[*global_index];
1194 match member {
1195 LocalMemberIndex::Property(property_idx) => &g.properties[*property_idx].ty,
1196 LocalMemberIndex::Function(function_idx) => &g.functions[*function_idx].ret_ty,
1197 LocalMemberIndex::Callback(callback_idx) => &g.callbacks[*callback_idx].ty,
1198 LocalMemberIndex::Native { .. } | LocalMemberIndex::Timer(_) => unreachable!(),
1199 }
1200 }
1201 }
1202 }
1203
1204 fn arg_type(&self, index: usize) -> &Type {
1205 &self.argument_types[index]
1206 }
1207}
1208
1209#[derive(Default, Debug)]
1210pub(crate) struct PropertyInfoResult<'a> {
1211 pub analysis: Option<&'a crate::object_tree::PropertyAnalysis>,
1212 pub binding: Option<(&'a super::BindingExpression, ContextMap)>,
1213 pub animation: Option<(&'a Expression, ContextMap)>,
1214 pub ty: Type,
1215 pub use_count: Option<&'a std::cell::Cell<usize>>,
1216}
1217
1218#[derive(Debug, Clone)]
1222pub(crate) enum ContextMap {
1223 Identity,
1224 InSubElement { path: Vec<SubComponentInstanceIdx>, parent: usize },
1225 InGlobal(GlobalIdx),
1226}
1227
1228impl ContextMap {
1229 fn from_parent_level(parent_level: usize) -> Self {
1230 if parent_level == 0 {
1231 ContextMap::Identity
1232 } else {
1233 ContextMap::InSubElement { parent: parent_level, path: Vec::new() }
1234 }
1235 }
1236
1237 fn deeper_in_sub_component(self, sub: SubComponentInstanceIdx) -> Self {
1238 match self {
1239 ContextMap::Identity => ContextMap::InSubElement { parent: 0, path: vec![sub] },
1240 ContextMap::InSubElement { mut path, parent } => {
1241 path.push(sub);
1242 ContextMap::InSubElement { path, parent }
1243 }
1244 ContextMap::InGlobal(_) => panic!(),
1245 }
1246 }
1247
1248 fn deeper_by_path(self, path: &[SubComponentInstanceIdx]) -> Self {
1249 path.iter().fold(self, |m, sub| m.deeper_in_sub_component(*sub))
1250 }
1251
1252 pub fn map_property_reference(&self, p: &MemberReference) -> MemberReference {
1253 match self {
1254 ContextMap::Identity => p.clone(),
1255 ContextMap::InSubElement { path, parent } => match p {
1256 MemberReference::Relative { parent_level, local_reference } => {
1257 MemberReference::Relative {
1258 parent_level: *parent_level + *parent,
1259 local_reference: LocalMemberReference {
1260 sub_component_path: path
1261 .iter()
1262 .chain(local_reference.sub_component_path.iter())
1263 .copied()
1264 .collect(),
1265 reference: local_reference.reference.clone(),
1266 },
1267 }
1268 }
1269 MemberReference::Global { .. } => p.clone(),
1270 },
1271 ContextMap::InGlobal(global_index) => match p {
1272 MemberReference::Relative { parent_level, local_reference } => {
1273 assert!(local_reference.sub_component_path.is_empty());
1274 assert_eq!(*parent_level, 0);
1275 MemberReference::Global {
1276 global_index: *global_index,
1277 member: local_reference.reference.clone(),
1278 }
1279 }
1280 g @ MemberReference::Global { .. } => g.clone(),
1281 },
1282 }
1283 }
1284
1285 pub fn map_expression(&self, e: &mut Expression) {
1286 match e {
1287 Expression::PropertyReference(p)
1288 | Expression::CallBackCall { callback: p, .. }
1289 | Expression::FunctionCall { function: p, .. }
1290 | Expression::ItemMemberFunctionCall { function: p, .. }
1291 | Expression::PropertyAssignment { property: p, .. }
1292 | Expression::LayoutCacheAccess { layout_cache_prop: p, .. }
1293 | Expression::GridRepeaterCacheAccess { layout_cache_prop: p, .. } => {
1294 *p = self.map_property_reference(p);
1295 }
1296 _ => (),
1297 }
1298 e.visit_mut(|e| self.map_expression(e))
1299 }
1300
1301 pub fn map_context<'a>(&self, ctx: &EvaluationContext<'a>) -> EvaluationContext<'a> {
1302 match self {
1303 ContextMap::Identity => ctx.clone(),
1304 ContextMap::InSubElement { path, parent } => {
1305 let mut sc = ctx.parent_sub_component_idx(*parent).unwrap();
1306 for i in path {
1307 sc = ctx.compilation_unit.sub_components[sc].sub_components[*i].ty;
1308 }
1309 EvaluationContext::new_sub_component(ctx.compilation_unit, sc, (), None)
1310 }
1311 ContextMap::InGlobal(g) => EvaluationContext::new_global(ctx.compilation_unit, *g, ()),
1312 }
1313 }
1314}