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