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