1use crate::diagnostics::{BuildDiagnostics, SourceLocation, Spanned};
5use crate::langtype::{BuiltinElement, EnumerationValue, Function, Struct, Type};
6use crate::layout::Orientation;
7use crate::lookup::LookupCtx;
8use crate::object_tree::*;
9use crate::parser::{NodeOrToken, SyntaxNode};
10use crate::typeregister;
11use core::cell::RefCell;
12use smol_str::{format_smolstr, SmolStr};
13use std::cell::Cell;
14use std::collections::HashMap;
15use std::rc::{Rc, Weak};
16
17pub use crate::namedreference::NamedReference;
19pub use crate::passes::resolving;
20
21#[derive(Debug, Clone, PartialEq, Eq)]
22pub enum BuiltinFunction {
24 GetWindowScaleFactor,
25 GetWindowDefaultFontSize,
26 AnimationTick,
27 Debug,
28 Mod,
29 Round,
30 Ceil,
31 Floor,
32 Abs,
33 Sqrt,
34 Cos,
35 Sin,
36 Tan,
37 ACos,
38 ASin,
39 ATan,
40 ATan2,
41 Log,
42 Pow,
43 ToFixed,
44 ToPrecision,
45 SetFocusItem,
46 ClearFocusItem,
47 ShowPopupWindow,
48 ClosePopupWindow,
49 ShowPopupMenu,
56 SetSelectionOffsets,
57 ItemFontMetrics,
58 StringToFloat,
60 StringIsFloat,
62 StringIsEmpty,
64 StringCharacterCount,
66 StringToLowercase,
67 StringToUppercase,
68 ColorRgbaStruct,
69 ColorHsvaStruct,
70 ColorBrighter,
71 ColorDarker,
72 ColorTransparentize,
73 ColorMix,
74 ColorWithAlpha,
75 ImageSize,
76 ArrayLength,
77 Rgb,
78 Hsv,
79 ColorScheme,
80 SupportsNativeMenuBar,
81 SetupNativeMenuBar,
88 Use24HourFormat,
89 MonthDayCount,
90 MonthOffset,
91 FormatDate,
92 DateNow,
93 ValidDate,
94 ParseDate,
95 TextInputFocused,
96 SetTextInputFocused,
97 ImplicitLayoutInfo(Orientation),
98 ItemAbsolutePosition,
99 RegisterCustomFontByPath,
100 RegisterCustomFontByMemory,
101 RegisterBitmapFont,
102 Translate,
103 UpdateTimers,
104}
105
106#[derive(Debug, Clone)]
107pub enum BuiltinMacroFunction {
113 Min,
115 Max,
117 Clamp,
119 Mod,
121 Abs,
123 CubicBezier,
124 Rgb,
127 Hsv,
128 Debug,
130}
131
132macro_rules! declare_builtin_function_types {
133 ($( $Name:ident $(($Pattern:tt))? : ($( $Arg:expr ),*) -> $ReturnType:expr $(,)? )*) => {
134 #[allow(non_snake_case)]
135 pub struct BuiltinFunctionTypes {
136 $(pub $Name : Rc<Function>),*
137 }
138 impl BuiltinFunctionTypes {
139 pub fn new() -> Self {
140 Self {
141 $($Name : Rc::new(Function{
142 args: vec![$($Arg),*],
143 return_type: $ReturnType,
144 arg_names: vec![],
145 })),*
146 }
147 }
148
149 pub fn ty(&self, function: &BuiltinFunction) -> Rc<Function> {
150 match function {
151 $(BuiltinFunction::$Name $(($Pattern))? => self.$Name.clone()),*
152 }
153 }
154 }
155 };
156}
157
158declare_builtin_function_types!(
159 GetWindowScaleFactor: () -> Type::UnitProduct(vec![(Unit::Phx, 1), (Unit::Px, -1)]),
160 GetWindowDefaultFontSize: () -> Type::LogicalLength,
161 AnimationTick: () -> Type::Duration,
162 Debug: (Type::String) -> Type::Void,
163 Mod: (Type::Int32, Type::Int32) -> Type::Int32,
164 Round: (Type::Float32) -> Type::Int32,
165 Ceil: (Type::Float32) -> Type::Int32,
166 Floor: (Type::Float32) -> Type::Int32,
167 Sqrt: (Type::Float32) -> Type::Float32,
168 Abs: (Type::Float32) -> Type::Float32,
169 Cos: (Type::Angle) -> Type::Float32,
170 Sin: (Type::Angle) -> Type::Float32,
171 Tan: (Type::Angle) -> Type::Float32,
172 ACos: (Type::Float32) -> Type::Angle,
173 ASin: (Type::Float32) -> Type::Angle,
174 ATan: (Type::Float32) -> Type::Angle,
175 ATan2: (Type::Float32, Type::Float32) -> Type::Angle,
176 Log: (Type::Float32, Type::Float32) -> Type::Float32,
177 Pow: (Type::Float32, Type::Float32) -> Type::Float32,
178 ToFixed: (Type::Float32, Type::Int32) -> Type::String,
179 ToPrecision: (Type::Float32, Type::Int32) -> Type::String,
180 SetFocusItem: (Type::ElementReference) -> Type::Void,
181 ClearFocusItem: (Type::ElementReference) -> Type::Void,
182 ShowPopupWindow: (Type::ElementReference) -> Type::Void,
183 ClosePopupWindow: (Type::ElementReference) -> Type::Void,
184 ShowPopupMenu: (Type::ElementReference, Type::Model, typeregister::logical_point_type()) -> Type::Void,
185 SetSelectionOffsets: (Type::ElementReference, Type::Int32, Type::Int32) -> Type::Void,
186 ItemFontMetrics: (Type::ElementReference) -> typeregister::font_metrics_type(),
187 StringToFloat: (Type::String) -> Type::Float32,
188 StringIsFloat: (Type::String) -> Type::Bool,
189 StringIsEmpty: (Type::String) -> Type::Bool,
190 StringCharacterCount: (Type::String) -> Type::Int32,
191 StringToLowercase: (Type::String) -> Type::String,
192 StringToUppercase: (Type::String) -> Type::String,
193 ImplicitLayoutInfo(..): (Type::ElementReference) -> Type::Struct(typeregister::layout_info_type()),
194 ColorRgbaStruct: (Type::Color) -> Type::Struct(Rc::new(Struct {
195 fields: IntoIterator::into_iter([
196 (SmolStr::new_static("red"), Type::Int32),
197 (SmolStr::new_static("green"), Type::Int32),
198 (SmolStr::new_static("blue"), Type::Int32),
199 (SmolStr::new_static("alpha"), Type::Int32),
200 ])
201 .collect(),
202 name: Some("Color".into()),
203 node: None,
204 rust_attributes: None,
205 })),
206 ColorHsvaStruct: (Type::Color) -> Type::Struct(Rc::new(Struct {
207 fields: IntoIterator::into_iter([
208 (SmolStr::new_static("hue"), Type::Float32),
209 (SmolStr::new_static("saturation"), Type::Float32),
210 (SmolStr::new_static("value"), Type::Float32),
211 (SmolStr::new_static("alpha"), Type::Float32),
212 ])
213 .collect(),
214 name: Some("Color".into()),
215 node: None,
216 rust_attributes: None,
217 })),
218 ColorBrighter: (Type::Brush, Type::Float32) -> Type::Brush,
219 ColorDarker: (Type::Brush, Type::Float32) -> Type::Brush,
220 ColorTransparentize: (Type::Brush, Type::Float32) -> Type::Brush,
221 ColorWithAlpha: (Type::Brush, Type::Float32) -> Type::Brush,
222 ColorMix: (Type::Color, Type::Color, Type::Float32) -> Type::Color,
223 ImageSize: (Type::Image) -> Type::Struct(Rc::new(Struct {
224 fields: IntoIterator::into_iter([
225 (SmolStr::new_static("width"), Type::Int32),
226 (SmolStr::new_static("height"), Type::Int32),
227 ])
228 .collect(),
229 name: Some("Size".into()),
230 node: None,
231 rust_attributes: None,
232 })),
233 ArrayLength: (Type::Model) -> Type::Int32,
234 Rgb: (Type::Int32, Type::Int32, Type::Int32, Type::Float32) -> Type::Color,
235 Hsv: (Type::Float32, Type::Float32, Type::Float32, Type::Float32) -> Type::Color,
236 ColorScheme: () -> Type::Enumeration(
237 typeregister::BUILTIN.with(|e| e.enums.ColorScheme.clone()),
238 ),
239 SupportsNativeMenuBar: () -> Type::Bool,
240 SetupNativeMenuBar: (Type::Model, typeregister::noarg_callback_type(), typeregister::noarg_callback_type()) -> Type::Void,
242 MonthDayCount: (Type::Int32, Type::Int32) -> Type::Int32,
243 MonthOffset: (Type::Int32, Type::Int32) -> Type::Int32,
244 FormatDate: (Type::String, Type::Int32, Type::Int32, Type::Int32) -> Type::String,
245 TextInputFocused: () -> Type::Bool,
246 DateNow: () -> Type::Array(Rc::new(Type::Int32)),
247 ValidDate: (Type::String, Type::String) -> Type::Bool,
248 ParseDate: (Type::String, Type::String) -> Type::Array(Rc::new(Type::Int32)),
249 SetTextInputFocused: (Type::Bool) -> Type::Void,
250 ItemAbsolutePosition: (Type::ElementReference) -> typeregister::logical_point_type(),
251 RegisterCustomFontByPath: (Type::String) -> Type::Void,
252 RegisterCustomFontByMemory: (Type::Int32) -> Type::Void,
253 RegisterBitmapFont: (Type::Int32) -> Type::Void,
254 Translate: (Type::String, Type::String, Type::String, Type::Array(Type::String.into())) -> Type::String,
256 Use24HourFormat: () -> Type::Bool,
257 UpdateTimers: () -> Type::Void,
258);
259
260impl BuiltinFunction {
261 pub fn ty(&self) -> Rc<Function> {
262 thread_local! {
263 static TYPES: BuiltinFunctionTypes = BuiltinFunctionTypes::new();
264 }
265 TYPES.with(|types| types.ty(self))
266 }
267
268 fn is_const(&self) -> bool {
270 match self {
271 BuiltinFunction::GetWindowScaleFactor => false,
272 BuiltinFunction::GetWindowDefaultFontSize => false,
273 BuiltinFunction::AnimationTick => false,
274 BuiltinFunction::ColorScheme => false,
275 BuiltinFunction::SupportsNativeMenuBar => false,
276 BuiltinFunction::SetupNativeMenuBar => false,
277 BuiltinFunction::MonthDayCount => false,
278 BuiltinFunction::MonthOffset => false,
279 BuiltinFunction::FormatDate => false,
280 BuiltinFunction::DateNow => false,
281 BuiltinFunction::ValidDate => false,
282 BuiltinFunction::ParseDate => false,
283 BuiltinFunction::Debug => true,
285 BuiltinFunction::Mod
286 | BuiltinFunction::Round
287 | BuiltinFunction::Ceil
288 | BuiltinFunction::Floor
289 | BuiltinFunction::Abs
290 | BuiltinFunction::Sqrt
291 | BuiltinFunction::Cos
292 | BuiltinFunction::Sin
293 | BuiltinFunction::Tan
294 | BuiltinFunction::ACos
295 | BuiltinFunction::ASin
296 | BuiltinFunction::Log
297 | BuiltinFunction::Pow
298 | BuiltinFunction::ATan
299 | BuiltinFunction::ATan2
300 | BuiltinFunction::ToFixed
301 | BuiltinFunction::ToPrecision => true,
302 BuiltinFunction::SetFocusItem | BuiltinFunction::ClearFocusItem => false,
303 BuiltinFunction::ShowPopupWindow
304 | BuiltinFunction::ClosePopupWindow
305 | BuiltinFunction::ShowPopupMenu => false,
306 BuiltinFunction::SetSelectionOffsets => false,
307 BuiltinFunction::ItemFontMetrics => false, BuiltinFunction::StringToFloat
309 | BuiltinFunction::StringIsFloat
310 | BuiltinFunction::StringIsEmpty
311 | BuiltinFunction::StringCharacterCount
312 | BuiltinFunction::StringToLowercase
313 | BuiltinFunction::StringToUppercase => true,
314 BuiltinFunction::ColorRgbaStruct
315 | BuiltinFunction::ColorHsvaStruct
316 | BuiltinFunction::ColorBrighter
317 | BuiltinFunction::ColorDarker
318 | BuiltinFunction::ColorTransparentize
319 | BuiltinFunction::ColorMix
320 | BuiltinFunction::ColorWithAlpha => true,
321 #[cfg(not(target_arch = "wasm32"))]
326 BuiltinFunction::ImageSize => true,
327 #[cfg(target_arch = "wasm32")]
328 BuiltinFunction::ImageSize => false,
329 BuiltinFunction::ArrayLength => true,
330 BuiltinFunction::Rgb => true,
331 BuiltinFunction::Hsv => true,
332 BuiltinFunction::SetTextInputFocused => false,
333 BuiltinFunction::TextInputFocused => false,
334 BuiltinFunction::ImplicitLayoutInfo(_) => false,
335 BuiltinFunction::ItemAbsolutePosition => true,
336 BuiltinFunction::RegisterCustomFontByPath
337 | BuiltinFunction::RegisterCustomFontByMemory
338 | BuiltinFunction::RegisterBitmapFont => false,
339 BuiltinFunction::Translate => false,
340 BuiltinFunction::Use24HourFormat => false,
341 BuiltinFunction::UpdateTimers => false,
342 }
343 }
344
345 pub fn is_pure(&self) -> bool {
347 match self {
348 BuiltinFunction::GetWindowScaleFactor => true,
349 BuiltinFunction::GetWindowDefaultFontSize => true,
350 BuiltinFunction::AnimationTick => true,
351 BuiltinFunction::ColorScheme => true,
352 BuiltinFunction::SupportsNativeMenuBar => true,
353 BuiltinFunction::SetupNativeMenuBar => false,
354 BuiltinFunction::MonthDayCount => true,
355 BuiltinFunction::MonthOffset => true,
356 BuiltinFunction::FormatDate => true,
357 BuiltinFunction::DateNow => true,
358 BuiltinFunction::ValidDate => true,
359 BuiltinFunction::ParseDate => true,
360 BuiltinFunction::Debug => true,
362 BuiltinFunction::Mod
363 | BuiltinFunction::Round
364 | BuiltinFunction::Ceil
365 | BuiltinFunction::Floor
366 | BuiltinFunction::Abs
367 | BuiltinFunction::Sqrt
368 | BuiltinFunction::Cos
369 | BuiltinFunction::Sin
370 | BuiltinFunction::Tan
371 | BuiltinFunction::ACos
372 | BuiltinFunction::ASin
373 | BuiltinFunction::Log
374 | BuiltinFunction::Pow
375 | BuiltinFunction::ATan
376 | BuiltinFunction::ATan2
377 | BuiltinFunction::ToFixed
378 | BuiltinFunction::ToPrecision => true,
379 BuiltinFunction::SetFocusItem | BuiltinFunction::ClearFocusItem => false,
380 BuiltinFunction::ShowPopupWindow
381 | BuiltinFunction::ClosePopupWindow
382 | BuiltinFunction::ShowPopupMenu => false,
383 BuiltinFunction::SetSelectionOffsets => false,
384 BuiltinFunction::ItemFontMetrics => true,
385 BuiltinFunction::StringToFloat
386 | BuiltinFunction::StringIsFloat
387 | BuiltinFunction::StringIsEmpty
388 | BuiltinFunction::StringCharacterCount
389 | BuiltinFunction::StringToLowercase
390 | BuiltinFunction::StringToUppercase => true,
391 BuiltinFunction::ColorRgbaStruct
392 | BuiltinFunction::ColorHsvaStruct
393 | BuiltinFunction::ColorBrighter
394 | BuiltinFunction::ColorDarker
395 | BuiltinFunction::ColorTransparentize
396 | BuiltinFunction::ColorMix
397 | BuiltinFunction::ColorWithAlpha => true,
398 BuiltinFunction::ImageSize => true,
399 BuiltinFunction::ArrayLength => true,
400 BuiltinFunction::Rgb => true,
401 BuiltinFunction::Hsv => true,
402 BuiltinFunction::ImplicitLayoutInfo(_) => true,
403 BuiltinFunction::ItemAbsolutePosition => true,
404 BuiltinFunction::SetTextInputFocused => false,
405 BuiltinFunction::TextInputFocused => true,
406 BuiltinFunction::RegisterCustomFontByPath
407 | BuiltinFunction::RegisterCustomFontByMemory
408 | BuiltinFunction::RegisterBitmapFont => false,
409 BuiltinFunction::Translate => true,
410 BuiltinFunction::Use24HourFormat => true,
411 BuiltinFunction::UpdateTimers => false,
412 }
413 }
414}
415
416#[derive(Debug, Clone)]
418pub enum Callable {
419 Callback(NamedReference),
420 Function(NamedReference),
421 Builtin(BuiltinFunction),
422}
423impl Callable {
424 pub fn ty(&self) -> Type {
425 match self {
426 Callable::Callback(nr) => nr.ty(),
427 Callable::Function(nr) => nr.ty(),
428 Callable::Builtin(b) => Type::Function(b.ty()),
429 }
430 }
431}
432impl From<BuiltinFunction> for Callable {
433 fn from(function: BuiltinFunction) -> Self {
434 Self::Builtin(function)
435 }
436}
437
438#[derive(Debug, Clone, Eq, PartialEq)]
439pub enum OperatorClass {
440 ComparisonOp,
441 LogicalOp,
442 ArithmeticOp,
443}
444
445pub fn operator_class(op: char) -> OperatorClass {
447 match op {
448 '=' | '!' | '<' | '>' | '≤' | '≥' => OperatorClass::ComparisonOp,
449 '&' | '|' => OperatorClass::LogicalOp,
450 '+' | '-' | '/' | '*' => OperatorClass::ArithmeticOp,
451 _ => panic!("Invalid operator {op:?}"),
452 }
453}
454
455macro_rules! declare_units {
456 ($( $(#[$m:meta])* $ident:ident = $string:literal -> $ty:ident $(* $factor:expr)? ,)*) => {
457 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, strum::EnumIter)]
459 pub enum Unit {
460 $($(#[$m])* $ident,)*
461 }
462
463 impl std::fmt::Display for Unit {
464 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
465 match self {
466 $(Self::$ident => write!(f, $string), )*
467 }
468 }
469 }
470
471 impl std::str::FromStr for Unit {
472 type Err = ();
473 fn from_str(s: &str) -> Result<Self, Self::Err> {
474 match s {
475 $($string => Ok(Self::$ident), )*
476 _ => Err(())
477 }
478 }
479 }
480
481 impl Unit {
482 pub fn ty(self) -> Type {
483 match self {
484 $(Self::$ident => Type::$ty, )*
485 }
486 }
487
488 pub fn normalize(self, x: f64) -> f64 {
489 match self {
490 $(Self::$ident => x $(* $factor as f64)?, )*
491 }
492 }
493
494 }
495 };
496}
497
498declare_units! {
499 None = "" -> Float32,
501 Percent = "%" -> Percent,
503
504 Phx = "phx" -> PhysicalLength,
508 Px = "px" -> LogicalLength,
510 Cm = "cm" -> LogicalLength * 37.8,
512 Mm = "mm" -> LogicalLength * 3.78,
514 In = "in" -> LogicalLength * 96,
516 Pt = "pt" -> LogicalLength * 96./72.,
518 Rem = "rem" -> Rem,
520
521 S = "s" -> Duration * 1000,
525 Ms = "ms" -> Duration,
527
528 Deg = "deg" -> Angle,
532 Grad = "grad" -> Angle * 360./180.,
534 Turn = "turn" -> Angle * 360.,
536 Rad = "rad" -> Angle * 360./std::f32::consts::TAU,
538}
539
540impl Default for Unit {
541 fn default() -> Self {
542 Self::None
543 }
544}
545
546#[derive(Debug, Clone, Copy)]
547pub enum MinMaxOp {
548 Min,
549 Max,
550}
551
552#[derive(Debug, Clone, Default)]
554pub enum Expression {
555 #[default]
557 Invalid,
558 Uncompiled(SyntaxNode),
560
561 StringLiteral(SmolStr),
563 NumberLiteral(f64, Unit),
565 BoolLiteral(bool),
567
568 PropertyReference(NamedReference),
570
571 ElementReference(Weak<RefCell<Element>>),
574
575 RepeaterIndexReference {
580 element: Weak<RefCell<Element>>,
581 },
582
583 RepeaterModelReference {
588 element: Weak<RefCell<Element>>,
589 },
590
591 FunctionParameterReference {
593 index: usize,
594 ty: Type,
595 },
596
597 StoreLocalVariable {
599 name: SmolStr,
600 value: Box<Expression>,
601 },
602
603 ReadLocalVariable {
606 name: SmolStr,
607 ty: Type,
608 },
609
610 StructFieldAccess {
612 base: Box<Expression>,
614 name: SmolStr,
615 },
616
617 ArrayIndex {
619 array: Box<Expression>,
621 index: Box<Expression>,
622 },
623
624 Cast {
626 from: Box<Expression>,
627 to: Type,
628 },
629
630 CodeBlock(Vec<Expression>),
632
633 FunctionCall {
635 function: Callable,
636 arguments: Vec<Expression>,
637 source_location: Option<SourceLocation>,
638 },
639
640 SelfAssignment {
642 lhs: Box<Expression>,
643 rhs: Box<Expression>,
644 op: char,
646 node: Option<NodeOrToken>,
647 },
648
649 BinaryExpression {
650 lhs: Box<Expression>,
651 rhs: Box<Expression>,
652 op: char,
654 },
655
656 UnaryOp {
657 sub: Box<Expression>,
658 op: char,
660 },
661
662 ImageReference {
663 resource_ref: ImageReference,
664 source_location: Option<SourceLocation>,
665 nine_slice: Option<[u16; 4]>,
666 },
667
668 Condition {
669 condition: Box<Expression>,
670 true_expr: Box<Expression>,
671 false_expr: Box<Expression>,
672 },
673
674 Array {
675 element_ty: Type,
676 values: Vec<Expression>,
677 },
678 Struct {
679 ty: Rc<Struct>,
680 values: HashMap<SmolStr, Expression>,
681 },
682
683 PathData(Path),
684
685 EasingCurve(EasingCurve),
686
687 LinearGradient {
688 angle: Box<Expression>,
689 stops: Vec<(Expression, Expression)>,
691 },
692
693 RadialGradient {
694 stops: Vec<(Expression, Expression)>,
696 },
697
698 EnumerationValue(EnumerationValue),
699
700 ReturnStatement(Option<Box<Expression>>),
701
702 LayoutCacheAccess {
703 layout_cache_prop: NamedReference,
704 index: usize,
705 repeater_index: Option<Box<Expression>>,
708 },
709 ComputeLayoutInfo(crate::layout::Layout, crate::layout::Orientation),
712 SolveLayout(crate::layout::Layout, crate::layout::Orientation),
713
714 MinMax {
715 ty: Type,
716 op: MinMaxOp,
717 lhs: Box<Expression>,
718 rhs: Box<Expression>,
719 },
720
721 DebugHook {
722 expression: Box<Expression>,
723 id: SmolStr,
724 },
725
726 EmptyComponentFactory,
727}
728
729impl Expression {
730 pub fn ty(&self) -> Type {
732 match self {
733 Expression::Invalid => Type::Invalid,
734 Expression::Uncompiled(_) => Type::Invalid,
735 Expression::StringLiteral(_) => Type::String,
736 Expression::NumberLiteral(_, unit) => unit.ty(),
737 Expression::BoolLiteral(_) => Type::Bool,
738 Expression::PropertyReference(nr) => nr.ty(),
739 Expression::ElementReference(_) => Type::ElementReference,
740 Expression::RepeaterIndexReference { .. } => Type::Int32,
741 Expression::RepeaterModelReference { element } => element
742 .upgrade()
743 .unwrap()
744 .borrow()
745 .repeated
746 .as_ref()
747 .map_or(Type::Invalid, |e| model_inner_type(&e.model)),
748 Expression::FunctionParameterReference { ty, .. } => ty.clone(),
749 Expression::StructFieldAccess { base, name } => match base.ty() {
750 Type::Struct(s) => s.fields.get(name.as_str()).unwrap_or(&Type::Invalid).clone(),
751 _ => Type::Invalid,
752 },
753 Expression::ArrayIndex { array, .. } => match array.ty() {
754 Type::Array(ty) => (*ty).clone(),
755 _ => Type::Invalid,
756 },
757 Expression::Cast { to, .. } => to.clone(),
758 Expression::CodeBlock(sub) => sub.last().map_or(Type::Void, |e| e.ty()),
759 Expression::FunctionCall { function, .. } => match function.ty() {
760 Type::Function(f) | Type::Callback(f) => f.return_type.clone(),
761 _ => Type::Invalid,
762 },
763 Expression::SelfAssignment { .. } => Type::Void,
764 Expression::ImageReference { .. } => Type::Image,
765 Expression::Condition { condition: _, true_expr, false_expr } => {
766 let true_type = true_expr.ty();
767 let false_type = false_expr.ty();
768 if true_type == false_type {
769 true_type
770 } else if true_type == Type::Invalid {
771 false_type
772 } else if false_type == Type::Invalid {
773 true_type
774 } else {
775 Type::Void
776 }
777 }
778 Expression::BinaryExpression { op, lhs, rhs } => {
779 if operator_class(*op) != OperatorClass::ArithmeticOp {
780 Type::Bool
781 } else if *op == '+' || *op == '-' {
782 let (rhs_ty, lhs_ty) = (rhs.ty(), lhs.ty());
783 if rhs_ty == lhs_ty {
784 rhs_ty
785 } else {
786 Type::Invalid
787 }
788 } else {
789 debug_assert!(*op == '*' || *op == '/');
790 let unit_vec = |ty| {
791 if let Type::UnitProduct(v) = ty {
792 v
793 } else if let Some(u) = ty.default_unit() {
794 vec![(u, 1)]
795 } else {
796 vec![]
797 }
798 };
799 let mut l_units = unit_vec(lhs.ty());
800 let mut r_units = unit_vec(rhs.ty());
801 if *op == '/' {
802 for (_, power) in &mut r_units {
803 *power = -*power;
804 }
805 }
806 for (unit, power) in r_units {
807 if let Some((_, p)) = l_units.iter_mut().find(|(u, _)| *u == unit) {
808 *p += power;
809 } else {
810 l_units.push((unit, power));
811 }
812 }
813
814 l_units.retain(|(_, p)| *p != 0);
816 l_units.sort_unstable_by(|(u1, p1), (u2, p2)| match p2.cmp(p1) {
817 std::cmp::Ordering::Equal => u1.cmp(u2),
818 x => x,
819 });
820
821 if l_units.is_empty() {
822 Type::Float32
823 } else if l_units.len() == 1 && l_units[0].1 == 1 {
824 l_units[0].0.ty()
825 } else {
826 Type::UnitProduct(l_units)
827 }
828 }
829 }
830 Expression::UnaryOp { sub, .. } => sub.ty(),
831 Expression::Array { element_ty, .. } => Type::Array(Rc::new(element_ty.clone())),
832 Expression::Struct { ty, .. } => ty.clone().into(),
833 Expression::PathData { .. } => Type::PathData,
834 Expression::StoreLocalVariable { .. } => Type::Void,
835 Expression::ReadLocalVariable { ty, .. } => ty.clone(),
836 Expression::EasingCurve(_) => Type::Easing,
837 Expression::LinearGradient { .. } => Type::Brush,
838 Expression::RadialGradient { .. } => Type::Brush,
839 Expression::EnumerationValue(value) => Type::Enumeration(value.enumeration.clone()),
840 Expression::ReturnStatement(_) => Type::Invalid,
842 Expression::LayoutCacheAccess { .. } => Type::LogicalLength,
843 Expression::ComputeLayoutInfo(..) => typeregister::layout_info_type().into(),
844 Expression::SolveLayout(..) => Type::LayoutCache,
845 Expression::MinMax { ty, .. } => ty.clone(),
846 Expression::EmptyComponentFactory => Type::ComponentFactory,
847 Expression::DebugHook { expression, .. } => expression.ty(),
848 }
849 }
850
851 pub fn visit(&self, mut visitor: impl FnMut(&Self)) {
853 match self {
854 Expression::Invalid => {}
855 Expression::Uncompiled(_) => {}
856 Expression::StringLiteral(_) => {}
857 Expression::NumberLiteral(_, _) => {}
858 Expression::BoolLiteral(_) => {}
859 Expression::PropertyReference { .. } => {}
860 Expression::FunctionParameterReference { .. } => {}
861 Expression::ElementReference(_) => {}
862 Expression::StructFieldAccess { base, .. } => visitor(base),
863 Expression::ArrayIndex { array, index } => {
864 visitor(array);
865 visitor(index);
866 }
867 Expression::RepeaterIndexReference { .. } => {}
868 Expression::RepeaterModelReference { .. } => {}
869 Expression::Cast { from, .. } => visitor(from),
870 Expression::CodeBlock(sub) => {
871 sub.iter().for_each(visitor);
872 }
873 Expression::FunctionCall { function: _, arguments, source_location: _ } => {
874 arguments.iter().for_each(visitor);
875 }
876 Expression::SelfAssignment { lhs, rhs, .. } => {
877 visitor(lhs);
878 visitor(rhs);
879 }
880 Expression::ImageReference { .. } => {}
881 Expression::Condition { condition, true_expr, false_expr } => {
882 visitor(condition);
883 visitor(true_expr);
884 visitor(false_expr);
885 }
886 Expression::BinaryExpression { lhs, rhs, .. } => {
887 visitor(lhs);
888 visitor(rhs);
889 }
890 Expression::UnaryOp { sub, .. } => visitor(sub),
891 Expression::Array { values, .. } => {
892 for x in values {
893 visitor(x);
894 }
895 }
896 Expression::Struct { values, .. } => {
897 for x in values.values() {
898 visitor(x);
899 }
900 }
901 Expression::PathData(data) => match data {
902 Path::Elements(elements) => {
903 for element in elements {
904 element.bindings.values().for_each(|binding| visitor(&binding.borrow()))
905 }
906 }
907 Path::Events(events, coordinates) => {
908 events.iter().chain(coordinates.iter()).for_each(visitor);
909 }
910 Path::Commands(commands) => visitor(commands),
911 },
912 Expression::StoreLocalVariable { value, .. } => visitor(value),
913 Expression::ReadLocalVariable { .. } => {}
914 Expression::EasingCurve(_) => {}
915 Expression::LinearGradient { angle, stops } => {
916 visitor(angle);
917 for (c, s) in stops {
918 visitor(c);
919 visitor(s);
920 }
921 }
922 Expression::RadialGradient { stops } => {
923 for (c, s) in stops {
924 visitor(c);
925 visitor(s);
926 }
927 }
928 Expression::EnumerationValue(_) => {}
929 Expression::ReturnStatement(expr) => {
930 expr.as_deref().map(visitor);
931 }
932 Expression::LayoutCacheAccess { repeater_index, .. } => {
933 repeater_index.as_deref().map(visitor);
934 }
935 Expression::ComputeLayoutInfo(..) => {}
936 Expression::SolveLayout(..) => {}
937 Expression::MinMax { lhs, rhs, .. } => {
938 visitor(lhs);
939 visitor(rhs);
940 }
941 Expression::EmptyComponentFactory => {}
942 Expression::DebugHook { expression, .. } => visitor(expression),
943 }
944 }
945
946 pub fn visit_mut(&mut self, mut visitor: impl FnMut(&mut Self)) {
947 match self {
948 Expression::Invalid => {}
949 Expression::Uncompiled(_) => {}
950 Expression::StringLiteral(_) => {}
951 Expression::NumberLiteral(_, _) => {}
952 Expression::BoolLiteral(_) => {}
953 Expression::PropertyReference { .. } => {}
954 Expression::FunctionParameterReference { .. } => {}
955 Expression::ElementReference(_) => {}
956 Expression::StructFieldAccess { base, .. } => visitor(base),
957 Expression::ArrayIndex { array, index } => {
958 visitor(array);
959 visitor(index);
960 }
961 Expression::RepeaterIndexReference { .. } => {}
962 Expression::RepeaterModelReference { .. } => {}
963 Expression::Cast { from, .. } => visitor(from),
964 Expression::CodeBlock(sub) => {
965 sub.iter_mut().for_each(visitor);
966 }
967 Expression::FunctionCall { function: _, arguments, source_location: _ } => {
968 arguments.iter_mut().for_each(visitor);
969 }
970 Expression::SelfAssignment { lhs, rhs, .. } => {
971 visitor(lhs);
972 visitor(rhs);
973 }
974 Expression::ImageReference { .. } => {}
975 Expression::Condition { condition, true_expr, false_expr } => {
976 visitor(condition);
977 visitor(true_expr);
978 visitor(false_expr);
979 }
980 Expression::BinaryExpression { lhs, rhs, .. } => {
981 visitor(lhs);
982 visitor(rhs);
983 }
984 Expression::UnaryOp { sub, .. } => visitor(sub),
985 Expression::Array { values, .. } => {
986 for x in values {
987 visitor(x);
988 }
989 }
990 Expression::Struct { values, .. } => {
991 for x in values.values_mut() {
992 visitor(x);
993 }
994 }
995 Expression::PathData(data) => match data {
996 Path::Elements(elements) => {
997 for element in elements {
998 element
999 .bindings
1000 .values_mut()
1001 .for_each(|binding| visitor(&mut binding.borrow_mut()))
1002 }
1003 }
1004 Path::Events(events, coordinates) => {
1005 events.iter_mut().chain(coordinates.iter_mut()).for_each(visitor);
1006 }
1007 Path::Commands(commands) => visitor(commands),
1008 },
1009 Expression::StoreLocalVariable { value, .. } => visitor(value),
1010 Expression::ReadLocalVariable { .. } => {}
1011 Expression::EasingCurve(_) => {}
1012 Expression::LinearGradient { angle, stops } => {
1013 visitor(angle);
1014 for (c, s) in stops {
1015 visitor(c);
1016 visitor(s);
1017 }
1018 }
1019 Expression::RadialGradient { stops } => {
1020 for (c, s) in stops {
1021 visitor(c);
1022 visitor(s);
1023 }
1024 }
1025 Expression::EnumerationValue(_) => {}
1026 Expression::ReturnStatement(expr) => {
1027 expr.as_deref_mut().map(visitor);
1028 }
1029 Expression::LayoutCacheAccess { repeater_index, .. } => {
1030 repeater_index.as_deref_mut().map(visitor);
1031 }
1032 Expression::ComputeLayoutInfo(..) => {}
1033 Expression::SolveLayout(..) => {}
1034 Expression::MinMax { lhs, rhs, .. } => {
1035 visitor(lhs);
1036 visitor(rhs);
1037 }
1038 Expression::EmptyComponentFactory => {}
1039 Expression::DebugHook { expression, .. } => visitor(expression),
1040 }
1041 }
1042
1043 pub fn visit_recursive(&self, visitor: &mut dyn FnMut(&Self)) {
1045 visitor(self);
1046 self.visit(|e| e.visit_recursive(visitor));
1047 }
1048
1049 pub fn visit_recursive_mut(&mut self, visitor: &mut dyn FnMut(&mut Self)) {
1051 visitor(self);
1052 self.visit_mut(|e| e.visit_recursive_mut(visitor));
1053 }
1054
1055 pub fn is_constant(&self) -> bool {
1056 match self {
1057 Expression::Invalid => true,
1058 Expression::Uncompiled(_) => false,
1059 Expression::StringLiteral(_) => true,
1060 Expression::NumberLiteral(_, _) => true,
1061 Expression::BoolLiteral(_) => true,
1062 Expression::PropertyReference(nr) => nr.is_constant(),
1063 Expression::ElementReference(_) => false,
1064 Expression::RepeaterIndexReference { .. } => false,
1065 Expression::RepeaterModelReference { .. } => false,
1066 Expression::FunctionParameterReference { .. } => true,
1068 Expression::StructFieldAccess { base, .. } => base.is_constant(),
1069 Expression::ArrayIndex { array, index } => array.is_constant() && index.is_constant(),
1070 Expression::Cast { from, .. } => from.is_constant(),
1071 Expression::CodeBlock(sub) => sub.iter().all(|s| s.is_constant()),
1074 Expression::FunctionCall { function, arguments, .. } => {
1075 let is_const = match function {
1076 Callable::Builtin(b) => b.is_const(),
1077 Callable::Function(nr) => nr.is_constant(),
1078 Callable::Callback(..) => false,
1079 };
1080 is_const && arguments.iter().all(|a| a.is_constant())
1081 }
1082 Expression::SelfAssignment { .. } => false,
1083 Expression::ImageReference { .. } => true,
1084 Expression::Condition { condition, false_expr, true_expr } => {
1085 condition.is_constant() && false_expr.is_constant() && true_expr.is_constant()
1086 }
1087 Expression::BinaryExpression { lhs, rhs, .. } => lhs.is_constant() && rhs.is_constant(),
1088 Expression::UnaryOp { sub, .. } => sub.is_constant(),
1089 Expression::Array { .. } => false,
1093 Expression::Struct { values, .. } => values.iter().all(|(_, v)| v.is_constant()),
1094 Expression::PathData(data) => match data {
1095 Path::Elements(elements) => elements
1096 .iter()
1097 .all(|element| element.bindings.values().all(|v| v.borrow().is_constant())),
1098 Path::Events(_, _) => true,
1099 Path::Commands(_) => false,
1100 },
1101 Expression::StoreLocalVariable { value, .. } => value.is_constant(),
1102 Expression::ReadLocalVariable { .. } => true,
1104 Expression::EasingCurve(_) => true,
1105 Expression::LinearGradient { angle, stops } => {
1106 angle.is_constant() && stops.iter().all(|(c, s)| c.is_constant() && s.is_constant())
1107 }
1108 Expression::RadialGradient { stops } => {
1109 stops.iter().all(|(c, s)| c.is_constant() && s.is_constant())
1110 }
1111 Expression::EnumerationValue(_) => true,
1112 Expression::ReturnStatement(expr) => {
1113 expr.as_ref().map_or(true, |expr| expr.is_constant())
1114 }
1115 Expression::LayoutCacheAccess { .. } => false,
1117 Expression::ComputeLayoutInfo(..) => false,
1118 Expression::SolveLayout(..) => false,
1119 Expression::MinMax { lhs, rhs, .. } => lhs.is_constant() && rhs.is_constant(),
1120 Expression::EmptyComponentFactory => true,
1121 Expression::DebugHook { .. } => false,
1122 }
1123 }
1124
1125 #[must_use]
1127 pub fn maybe_convert_to(
1128 self,
1129 target_type: Type,
1130 node: &dyn Spanned,
1131 diag: &mut BuildDiagnostics,
1132 ) -> Expression {
1133 let ty = self.ty();
1134 if ty == target_type
1135 || target_type == Type::Void
1136 || target_type == Type::Invalid
1137 || ty == Type::Invalid
1138 {
1139 self
1140 } else if ty.can_convert(&target_type) {
1141 let from = match (ty, &target_type) {
1142 (Type::Brush, Type::Color) => match self {
1143 Expression::LinearGradient { .. } | Expression::RadialGradient { .. } => {
1144 let message = format!("Narrowing conversion from {0} to {1}. This can lead to unexpected behavior because the {0} is a gradient", Type::Brush, Type::Color);
1145 diag.push_warning(message, node);
1146 self
1147 }
1148 _ => self,
1149 },
1150 (Type::Percent, Type::Float32) => Expression::BinaryExpression {
1151 lhs: Box::new(self),
1152 rhs: Box::new(Expression::NumberLiteral(0.01, Unit::None)),
1153 op: '*',
1154 },
1155 (ref from_ty @ Type::Struct(ref left), Type::Struct(right))
1156 if left.fields != right.fields =>
1157 {
1158 if let Expression::Struct { mut values, .. } = self {
1159 let mut new_values = HashMap::new();
1160 for (key, ty) in &right.fields {
1161 let (key, expression) = values.remove_entry(key).map_or_else(
1162 || (key.clone(), Expression::default_value_for_type(ty)),
1163 |(k, e)| (k, e.maybe_convert_to(ty.clone(), node, diag)),
1164 );
1165 new_values.insert(key, expression);
1166 }
1167 return Expression::Struct { values: new_values, ty: right.clone() };
1168 }
1169 static COUNT: std::sync::atomic::AtomicUsize =
1170 std::sync::atomic::AtomicUsize::new(0);
1171 let var_name = format_smolstr!(
1172 "tmpobj_conv_{}",
1173 COUNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
1174 );
1175 let mut new_values = HashMap::new();
1176 for (key, ty) in &right.fields {
1177 let expression = if left.fields.contains_key(key) {
1178 Expression::StructFieldAccess {
1179 base: Box::new(Expression::ReadLocalVariable {
1180 name: var_name.clone(),
1181 ty: from_ty.clone(),
1182 }),
1183 name: key.clone(),
1184 }
1185 .maybe_convert_to(ty.clone(), node, diag)
1186 } else {
1187 Expression::default_value_for_type(ty)
1188 };
1189 new_values.insert(key.clone(), expression);
1190 }
1191 return Expression::CodeBlock(vec![
1192 Expression::StoreLocalVariable { name: var_name, value: Box::new(self) },
1193 Expression::Struct { values: new_values, ty: right.clone() },
1194 ]);
1195 }
1196 (left, right) => match (left.as_unit_product(), right.as_unit_product()) {
1197 (Some(left), Some(right)) => {
1198 if let Some(conversion_powers) =
1199 crate::langtype::unit_product_length_conversion(&left, &right)
1200 {
1201 let apply_power =
1202 |mut result, power: i8, builtin_fn: BuiltinFunction| {
1203 let op = if power < 0 { '*' } else { '/' };
1204 for _ in 0..power.abs() {
1205 result = Expression::BinaryExpression {
1206 lhs: Box::new(result),
1207 rhs: Box::new(Expression::FunctionCall {
1208 function: Callable::Builtin(builtin_fn.clone()),
1209 arguments: vec![],
1210 source_location: Some(node.to_source_location()),
1211 }),
1212 op,
1213 }
1214 }
1215 result
1216 };
1217
1218 let mut result = self;
1219
1220 if conversion_powers.rem_to_px_power != 0 {
1221 result = apply_power(
1222 result,
1223 conversion_powers.rem_to_px_power,
1224 BuiltinFunction::GetWindowDefaultFontSize,
1225 )
1226 }
1227 if conversion_powers.px_to_phx_power != 0 {
1228 result = apply_power(
1229 result,
1230 conversion_powers.px_to_phx_power,
1231 BuiltinFunction::GetWindowScaleFactor,
1232 )
1233 }
1234
1235 result
1236 } else {
1237 self
1238 }
1239 }
1240 _ => self,
1241 },
1242 };
1243 Expression::Cast { from: Box::new(from), to: target_type }
1244 } else if matches!(
1245 (&ty, &target_type, &self),
1246 (Type::Array(_), Type::Array(_), Expression::Array { .. })
1247 ) {
1248 match (self, target_type) {
1250 (Expression::Array { values, .. }, Type::Array(target_type)) => Expression::Array {
1251 values: values
1252 .into_iter()
1253 .map(|e| e.maybe_convert_to((*target_type).clone(), node, diag))
1254 .take_while(|e| !matches!(e, Expression::Invalid))
1255 .collect(),
1256 element_ty: (*target_type).clone(),
1257 },
1258 _ => unreachable!(),
1259 }
1260 } else if let (Type::Struct(struct_type), Expression::Struct { values, .. }) =
1261 (&target_type, &self)
1262 {
1263 let mut fields = struct_type.fields.clone();
1265 let mut new_values = HashMap::new();
1266 for (f, v) in values {
1267 if let Some(t) = fields.remove(f) {
1268 new_values.insert(f.clone(), v.clone().maybe_convert_to(t, node, diag));
1269 } else {
1270 diag.push_error(format!("Cannot convert {ty} to {target_type}"), node);
1271 return self;
1272 }
1273 }
1274 for (f, t) in fields {
1275 new_values.insert(f, Expression::default_value_for_type(&t));
1276 }
1277 Expression::Struct { ty: struct_type.clone(), values: new_values }
1278 } else {
1279 let mut message = format!("Cannot convert {ty} to {target_type}");
1280 if let Some(from_unit) = ty.default_unit() {
1282 if matches!(&target_type, Type::Int32 | Type::Float32 | Type::String) {
1283 message =
1284 format!("{message}. Divide by 1{from_unit} to convert to a plain number");
1285 }
1286 } else if let Some(to_unit) = target_type.default_unit() {
1287 if matches!(ty, Type::Int32 | Type::Float32) {
1288 if let Expression::NumberLiteral(value, Unit::None) = self {
1289 if value == 0. {
1290 return Expression::NumberLiteral(0., to_unit);
1292 }
1293 }
1294 message = format!(
1295 "{message}. Use an unit, or multiply by 1{to_unit} to convert explicitly"
1296 );
1297 }
1298 }
1299 diag.push_error(message, node);
1300 self
1301 }
1302 }
1303
1304 pub fn default_value_for_type(ty: &Type) -> Expression {
1306 match ty {
1307 Type::Invalid
1308 | Type::Callback { .. }
1309 | Type::Function { .. }
1310 | Type::InferredProperty
1311 | Type::InferredCallback
1312 | Type::ElementReference
1313 | Type::LayoutCache => Expression::Invalid,
1314 Type::Void => Expression::CodeBlock(vec![]),
1315 Type::Float32 => Expression::NumberLiteral(0., Unit::None),
1316 Type::String => Expression::StringLiteral(SmolStr::default()),
1317 Type::Int32 | Type::Color | Type::UnitProduct(_) => Expression::Cast {
1318 from: Box::new(Expression::NumberLiteral(0., Unit::None)),
1319 to: ty.clone(),
1320 },
1321 Type::Duration => Expression::NumberLiteral(0., Unit::Ms),
1322 Type::Angle => Expression::NumberLiteral(0., Unit::Deg),
1323 Type::PhysicalLength => Expression::NumberLiteral(0., Unit::Phx),
1324 Type::LogicalLength => Expression::NumberLiteral(0., Unit::Px),
1325 Type::Rem => Expression::NumberLiteral(0., Unit::Rem),
1326 Type::Percent => Expression::NumberLiteral(100., Unit::Percent),
1327 Type::Image => Expression::ImageReference {
1328 resource_ref: ImageReference::None,
1329 source_location: None,
1330 nine_slice: None,
1331 },
1332 Type::Bool => Expression::BoolLiteral(false),
1333 Type::Model => Expression::Invalid,
1334 Type::PathData => Expression::PathData(Path::Elements(vec![])),
1335 Type::Array(element_ty) => {
1336 Expression::Array { element_ty: (**element_ty).clone(), values: vec![] }
1337 }
1338 Type::Struct(s) => Expression::Struct {
1339 ty: s.clone(),
1340 values: s
1341 .fields
1342 .iter()
1343 .map(|(k, v)| (k.clone(), Expression::default_value_for_type(v)))
1344 .collect(),
1345 },
1346 Type::Easing => Expression::EasingCurve(EasingCurve::default()),
1347 Type::Brush => Expression::Cast {
1348 from: Box::new(Expression::default_value_for_type(&Type::Color)),
1349 to: Type::Brush,
1350 },
1351 Type::Enumeration(enumeration) => {
1352 Expression::EnumerationValue(enumeration.clone().default_value())
1353 }
1354 Type::ComponentFactory => Expression::EmptyComponentFactory,
1355 }
1356 }
1357
1358 pub fn try_set_rw(
1362 &mut self,
1363 ctx: &mut LookupCtx,
1364 what: &'static str,
1365 node: &dyn Spanned,
1366 ) -> bool {
1367 match self {
1368 Expression::PropertyReference(nr) => {
1369 nr.mark_as_set();
1370 let mut lookup = nr.element().borrow().lookup_property(nr.name());
1371 lookup.is_local_to_component &= ctx.is_local_element(&nr.element());
1372 if lookup.property_visibility == PropertyVisibility::Constexpr {
1373 ctx.diag.push_error(
1374 "The property must be known at compile time and cannot be changed at runtime"
1375 .into(),
1376 node,
1377 );
1378 false
1379 } else if lookup.is_valid_for_assignment() {
1380 if !nr
1381 .element()
1382 .borrow()
1383 .property_analysis
1384 .borrow()
1385 .get(nr.name())
1386 .is_some_and(|d| d.is_linked_to_read_only)
1387 {
1388 true
1389 } else if ctx.is_legacy_component() {
1390 ctx.diag.push_warning("Modifying a property that is linked to a read-only property is deprecated".into(), node);
1391 true
1392 } else {
1393 ctx.diag.push_error(
1394 "Cannot modify a property that is linked to a read-only property"
1395 .into(),
1396 node,
1397 );
1398 false
1399 }
1400 } else if ctx.is_legacy_component()
1401 && lookup.property_visibility == PropertyVisibility::Output
1402 {
1403 ctx.diag
1404 .push_warning(format!("{what} on an output property is deprecated"), node);
1405 true
1406 } else {
1407 ctx.diag.push_error(
1408 format!("{what} on a {} property", lookup.property_visibility),
1409 node,
1410 );
1411 false
1412 }
1413 }
1414 Expression::StructFieldAccess { base, .. } => base.try_set_rw(ctx, what, node),
1415 Expression::RepeaterModelReference { .. } => true,
1416 Expression::ArrayIndex { array, .. } => array.try_set_rw(ctx, what, node),
1417 _ => {
1418 ctx.diag.push_error(format!("{what} needs to be done on a property"), node);
1419 false
1420 }
1421 }
1422 }
1423
1424 pub fn ignore_debug_hooks(&self) -> &Expression {
1426 match self {
1427 Expression::DebugHook { expression, .. } => expression.as_ref(),
1428 _ => self,
1429 }
1430 }
1431}
1432
1433fn model_inner_type(model: &Expression) -> Type {
1434 match model {
1435 Expression::Cast { from, to: Type::Model } => model_inner_type(from),
1436 Expression::CodeBlock(cb) => cb.last().map_or(Type::Invalid, model_inner_type),
1437 _ => match model.ty() {
1438 Type::Float32 | Type::Int32 => Type::Int32,
1439 Type::Array(elem) => (*elem).clone(),
1440 _ => Type::Invalid,
1441 },
1442 }
1443}
1444
1445#[derive(Debug, Clone, derive_more::Deref, derive_more::DerefMut)]
1447pub struct BindingExpression {
1448 #[deref]
1449 #[deref_mut]
1450 pub expression: Expression,
1451 pub span: Option<SourceLocation>,
1453 pub priority: i32,
1458
1459 pub animation: Option<PropertyAnimation>,
1460
1461 pub analysis: Option<BindingAnalysis>,
1463
1464 pub two_way_bindings: Vec<NamedReference>,
1466}
1467
1468impl std::convert::From<Expression> for BindingExpression {
1469 fn from(expression: Expression) -> Self {
1470 Self {
1471 expression,
1472 span: None,
1473 priority: 0,
1474 animation: Default::default(),
1475 analysis: Default::default(),
1476 two_way_bindings: Default::default(),
1477 }
1478 }
1479}
1480
1481impl BindingExpression {
1482 pub fn new_uncompiled(node: SyntaxNode) -> Self {
1483 Self {
1484 expression: Expression::Uncompiled(node.clone()),
1485 span: Some(node.to_source_location()),
1486 priority: 1,
1487 animation: Default::default(),
1488 analysis: Default::default(),
1489 two_way_bindings: Default::default(),
1490 }
1491 }
1492 pub fn new_with_span(expression: Expression, span: SourceLocation) -> Self {
1493 Self {
1494 expression,
1495 span: Some(span),
1496 priority: 0,
1497 animation: Default::default(),
1498 analysis: Default::default(),
1499 two_way_bindings: Default::default(),
1500 }
1501 }
1502
1503 pub fn new_two_way(other: NamedReference) -> Self {
1505 Self {
1506 expression: Expression::Invalid,
1507 span: None,
1508 priority: 0,
1509 animation: Default::default(),
1510 analysis: Default::default(),
1511 two_way_bindings: vec![other],
1512 }
1513 }
1514
1515 pub fn merge_with(&mut self, other: &Self) -> bool {
1523 if self.animation.is_none() {
1524 self.animation.clone_from(&other.animation);
1525 }
1526 let has_binding = self.has_binding();
1527 self.two_way_bindings.extend_from_slice(&other.two_way_bindings);
1528 if !has_binding {
1529 self.priority = other.priority;
1530 self.expression = other.expression.clone();
1531 true
1532 } else {
1533 false
1534 }
1535 }
1536
1537 pub fn has_binding(&self) -> bool {
1539 !matches!(self.expression, Expression::Invalid) || !self.two_way_bindings.is_empty()
1540 }
1541}
1542
1543impl Spanned for BindingExpression {
1544 fn span(&self) -> crate::diagnostics::Span {
1545 self.span.as_ref().map(|x| x.span()).unwrap_or_default()
1546 }
1547 fn source_file(&self) -> Option<&crate::diagnostics::SourceFile> {
1548 self.span.as_ref().and_then(|x| x.source_file())
1549 }
1550}
1551
1552#[derive(Default, Debug, Clone)]
1553pub struct BindingAnalysis {
1554 pub is_in_binding_loop: Cell<bool>,
1556
1557 pub is_const: bool,
1559
1560 pub no_external_dependencies: bool,
1563}
1564
1565#[derive(Debug, Clone)]
1566pub enum Path {
1567 Elements(Vec<PathElement>),
1568 Events(Vec<Expression>, Vec<Expression>),
1569 Commands(Box<Expression>), }
1571
1572#[derive(Debug, Clone)]
1573pub struct PathElement {
1574 pub element_type: Rc<BuiltinElement>,
1575 pub bindings: BindingsMap,
1576}
1577
1578#[derive(Clone, Debug, Default)]
1579pub enum EasingCurve {
1580 #[default]
1581 Linear,
1582 CubicBezier(f32, f32, f32, f32),
1583 EaseInElastic,
1584 EaseOutElastic,
1585 EaseInOutElastic,
1586 EaseInBounce,
1587 EaseOutBounce,
1588 EaseInOutBounce,
1589 }
1592
1593#[derive(Clone, Debug)]
1596pub enum ImageReference {
1597 None,
1598 AbsolutePath(SmolStr),
1599 EmbeddedData { resource_id: usize, extension: String },
1600 EmbeddedTexture { resource_id: usize },
1601}
1602
1603pub fn pretty_print(f: &mut dyn std::fmt::Write, expression: &Expression) -> std::fmt::Result {
1605 match expression {
1606 Expression::Invalid => write!(f, "<invalid>"),
1607 Expression::Uncompiled(u) => write!(f, "{u:?}"),
1608 Expression::StringLiteral(s) => write!(f, "{s:?}"),
1609 Expression::NumberLiteral(vl, unit) => write!(f, "{vl}{unit}"),
1610 Expression::BoolLiteral(b) => write!(f, "{b:?}"),
1611 Expression::PropertyReference(a) => write!(f, "{a:?}"),
1612 Expression::ElementReference(a) => write!(f, "{a:?}"),
1613 Expression::RepeaterIndexReference { element } => {
1614 crate::namedreference::pretty_print_element_ref(f, element)
1615 }
1616 Expression::RepeaterModelReference { element } => {
1617 crate::namedreference::pretty_print_element_ref(f, element)?;
1618 write!(f, ".@model")
1619 }
1620 Expression::FunctionParameterReference { index, ty: _ } => write!(f, "_arg_{index}"),
1621 Expression::StoreLocalVariable { name, value } => {
1622 write!(f, "{name} = ")?;
1623 pretty_print(f, value)
1624 }
1625 Expression::ReadLocalVariable { name, ty: _ } => write!(f, "{name}"),
1626 Expression::StructFieldAccess { base, name } => {
1627 pretty_print(f, base)?;
1628 write!(f, ".{name}")
1629 }
1630 Expression::ArrayIndex { array, index } => {
1631 pretty_print(f, array)?;
1632 write!(f, "[")?;
1633 pretty_print(f, index)?;
1634 write!(f, "]")
1635 }
1636 Expression::Cast { from, to } => {
1637 write!(f, "(")?;
1638 pretty_print(f, from)?;
1639 write!(f, "/* as {to} */)")
1640 }
1641 Expression::CodeBlock(c) => {
1642 write!(f, "{{ ")?;
1643 for e in c {
1644 pretty_print(f, e)?;
1645 write!(f, "; ")?;
1646 }
1647 write!(f, "}}")
1648 }
1649 Expression::FunctionCall { function, arguments, source_location: _ } => {
1650 match function {
1651 Callable::Builtin(b) => write!(f, "{b:?}")?,
1652 Callable::Callback(nr) | Callable::Function(nr) => write!(f, "{nr:?}")?,
1653 }
1654 write!(f, "(")?;
1655 for e in arguments {
1656 pretty_print(f, e)?;
1657 write!(f, ", ")?;
1658 }
1659 write!(f, ")")
1660 }
1661 Expression::SelfAssignment { lhs, rhs, op, .. } => {
1662 pretty_print(f, lhs)?;
1663 write!(f, " {}= ", if *op == '=' { ' ' } else { *op })?;
1664 pretty_print(f, rhs)
1665 }
1666 Expression::BinaryExpression { lhs, rhs, op } => {
1667 write!(f, "(")?;
1668 pretty_print(f, lhs)?;
1669 match *op {
1670 '=' | '!' => write!(f, " {op}= ")?,
1671 _ => write!(f, " {op} ")?,
1672 };
1673 pretty_print(f, rhs)?;
1674 write!(f, ")")
1675 }
1676 Expression::UnaryOp { sub, op } => {
1677 write!(f, "{op}")?;
1678 pretty_print(f, sub)
1679 }
1680 Expression::ImageReference { resource_ref, .. } => write!(f, "{resource_ref:?}"),
1681 Expression::Condition { condition, true_expr, false_expr } => {
1682 write!(f, "if (")?;
1683 pretty_print(f, condition)?;
1684 write!(f, ") {{ ")?;
1685 pretty_print(f, true_expr)?;
1686 write!(f, " }} else {{ ")?;
1687 pretty_print(f, false_expr)?;
1688 write!(f, " }}")
1689 }
1690 Expression::Array { element_ty: _, values } => {
1691 write!(f, "[")?;
1692 for e in values {
1693 pretty_print(f, e)?;
1694 write!(f, ", ")?;
1695 }
1696 write!(f, "]")
1697 }
1698 Expression::Struct { ty: _, values } => {
1699 write!(f, "{{ ")?;
1700 for (name, e) in values {
1701 write!(f, "{name}: ")?;
1702 pretty_print(f, e)?;
1703 write!(f, ", ")?;
1704 }
1705 write!(f, " }}")
1706 }
1707 Expression::PathData(data) => write!(f, "{data:?}"),
1708 Expression::EasingCurve(e) => write!(f, "{e:?}"),
1709 Expression::LinearGradient { angle, stops } => {
1710 write!(f, "@linear-gradient(")?;
1711 pretty_print(f, angle)?;
1712 for (c, s) in stops {
1713 write!(f, ", ")?;
1714 pretty_print(f, c)?;
1715 write!(f, " ")?;
1716 pretty_print(f, s)?;
1717 }
1718 write!(f, ")")
1719 }
1720 Expression::RadialGradient { stops } => {
1721 write!(f, "@radial-gradient(circle")?;
1722 for (c, s) in stops {
1723 write!(f, ", ")?;
1724 pretty_print(f, c)?;
1725 write!(f, " ")?;
1726 pretty_print(f, s)?;
1727 }
1728 write!(f, ")")
1729 }
1730 Expression::EnumerationValue(e) => match e.enumeration.values.get(e.value) {
1731 Some(val) => write!(f, "{}.{}", e.enumeration.name, val),
1732 None => write!(f, "{}.{}", e.enumeration.name, e.value),
1733 },
1734 Expression::ReturnStatement(e) => {
1735 write!(f, "return ")?;
1736 e.as_ref().map(|e| pretty_print(f, e)).unwrap_or(Ok(()))
1737 }
1738 Expression::LayoutCacheAccess { layout_cache_prop, index, repeater_index } => {
1739 write!(
1740 f,
1741 "{:?}[{}{}]",
1742 layout_cache_prop,
1743 index,
1744 if repeater_index.is_some() { " + $index" } else { "" }
1745 )
1746 }
1747 Expression::ComputeLayoutInfo(..) => write!(f, "layout_info(..)"),
1748 Expression::SolveLayout(..) => write!(f, "solve_layout(..)"),
1749 Expression::MinMax { ty: _, op, lhs, rhs } => {
1750 match op {
1751 MinMaxOp::Min => write!(f, "min(")?,
1752 MinMaxOp::Max => write!(f, "max(")?,
1753 }
1754 pretty_print(f, lhs)?;
1755 write!(f, ", ")?;
1756 pretty_print(f, rhs)?;
1757 write!(f, ")")
1758 }
1759 Expression::EmptyComponentFactory => write!(f, "<empty-component-factory>"),
1760 Expression::DebugHook { expression, id } => {
1761 write!(f, "debug-hook(")?;
1762 pretty_print(f, expression)?;
1763 write!(f, "\"{id}\")")
1764 }
1765 }
1766}