1use crate::diagnostics::{BuildDiagnostics, SourceLocation, Spanned};
5use crate::langtype::{
6 BuiltinElement, BuiltinPublicStruct, EnumerationValue, Function, Keys, Struct, Type,
7};
8use crate::layout::Orientation;
9use crate::lookup::LookupCtx;
10use crate::object_tree::*;
11use crate::parser::{NodeOrToken, SyntaxNode};
12use crate::typeregister;
13use core::cell::RefCell;
14use smol_str::{SmolStr, format_smolstr};
15use std::cell::Cell;
16use std::collections::HashMap;
17use std::rc::{Rc, Weak};
18
19pub use crate::namedreference::NamedReference;
21pub use crate::passes::resolving;
22
23#[derive(Debug, Clone, PartialEq, Eq)]
24pub enum BuiltinFunction {
26 GetWindowScaleFactor,
27 GetWindowDefaultFontSize,
28 AnimationTick,
29 Debug,
30 Mod,
31 Round,
32 Ceil,
33 Floor,
34 Abs,
35 Sqrt,
36 Cos,
37 Sin,
38 Tan,
39 ACos,
40 ASin,
41 ATan,
42 ATan2,
43 Log,
44 Ln,
45 Pow,
46 Exp,
47 ToFixed,
48 ToPrecision,
49 SetFocusItem,
50 ClearFocusItem,
51 ShowPopupWindow,
52 ClosePopupWindow,
53 ShowPopupMenu,
60 ShowPopupMenuInternal,
64 SetSelectionOffsets,
65 ItemFontMetrics,
66 StringToFloat,
68 StringIsFloat,
70 StringIsEmpty,
72 StringCharacterCount,
74 StringToLowercase,
75 StringToUppercase,
76 KeysToString,
77 ColorRgbaStruct,
78 ColorHsvaStruct,
79 ColorOklchStruct,
80 ColorBrighter,
81 ColorDarker,
82 ColorTransparentize,
83 ColorMix,
84 ColorWithAlpha,
85 ImageSize,
86 ArrayLength,
87 Rgb,
88 Hsv,
89 Oklch,
90 ColorScheme,
91 AccentColor,
92 SupportsNativeMenuBar,
93 SetupMenuBar,
100 Use24HourFormat,
101 MonthDayCount,
102 MonthOffset,
103 FormatDate,
104 DateNow,
105 ValidDate,
106 ParseDate,
107 TextInputFocused,
108 SetTextInputFocused,
109 ImplicitLayoutInfo(Orientation),
110 ItemAbsolutePosition,
111 RegisterCustomFontByPath,
112 RegisterCustomFontByMemory,
113 RegisterBitmapFont,
114 Translate,
115 UpdateTimers,
116 DetectOperatingSystem,
117 StartTimer,
118 StopTimer,
119 RestartTimer,
120 OpenUrl,
121 ParseMarkdown,
122 StringToStyledText,
123}
124
125#[derive(Debug, Clone)]
126pub enum BuiltinMacroFunction {
132 Min,
134 Max,
136 Clamp,
138 Mod,
140 Abs,
142 Sign,
144 CubicBezier,
145 Rgb,
148 Hsv,
149 Oklch,
150 Debug,
152}
153
154macro_rules! declare_builtin_function_types {
155 ($( $Name:ident $(($Pattern:tt))? : ($( $Arg:expr ),*) -> $ReturnType:expr $(,)? )*) => {
156 #[allow(non_snake_case)]
157 pub struct BuiltinFunctionTypes {
158 $(pub $Name : Rc<Function>),*
159 }
160 impl BuiltinFunctionTypes {
161 pub fn new() -> Self {
162 Self {
163 $($Name : Rc::new(Function{
164 args: vec![$($Arg),*],
165 return_type: $ReturnType,
166 arg_names: Vec::new(),
167 })),*
168 }
169 }
170
171 pub fn ty(&self, function: &BuiltinFunction) -> Rc<Function> {
172 match function {
173 $(BuiltinFunction::$Name $(($Pattern))? => self.$Name.clone()),*
174 }
175 }
176 }
177 };
178}
179
180declare_builtin_function_types!(
181 GetWindowScaleFactor: () -> Type::UnitProduct(vec![(Unit::Phx, 1), (Unit::Px, -1)]),
182 GetWindowDefaultFontSize: () -> Type::LogicalLength,
183 AnimationTick: () -> Type::Duration,
184 Debug: (Type::String) -> Type::Void,
185 Mod: (Type::Int32, Type::Int32) -> Type::Int32,
186 Round: (Type::Float32) -> Type::Int32,
187 Ceil: (Type::Float32) -> Type::Int32,
188 Floor: (Type::Float32) -> Type::Int32,
189 Sqrt: (Type::Float32) -> Type::Float32,
190 Abs: (Type::Float32) -> Type::Float32,
191 Cos: (Type::Angle) -> Type::Float32,
192 Sin: (Type::Angle) -> Type::Float32,
193 Tan: (Type::Angle) -> Type::Float32,
194 ACos: (Type::Float32) -> Type::Angle,
195 ASin: (Type::Float32) -> Type::Angle,
196 ATan: (Type::Float32) -> Type::Angle,
197 ATan2: (Type::Float32, Type::Float32) -> Type::Angle,
198 Log: (Type::Float32, Type::Float32) -> Type::Float32,
199 Ln: (Type::Float32) -> Type::Float32,
200 Pow: (Type::Float32, Type::Float32) -> Type::Float32,
201 Exp: (Type::Float32) -> Type::Float32,
202 ToFixed: (Type::Float32, Type::Int32) -> Type::String,
203 ToPrecision: (Type::Float32, Type::Int32) -> Type::String,
204 SetFocusItem: (Type::ElementReference) -> Type::Void,
205 ClearFocusItem: (Type::ElementReference) -> Type::Void,
206 ShowPopupWindow: (Type::ElementReference) -> Type::Void,
207 ClosePopupWindow: (Type::ElementReference) -> Type::Void,
208 ShowPopupMenu: (Type::ElementReference, Type::ElementReference, typeregister::logical_point_type().into()) -> Type::Void,
209 ShowPopupMenuInternal: (Type::ElementReference, Type::Model, typeregister::logical_point_type().into()) -> Type::Void,
210 SetSelectionOffsets: (Type::ElementReference, Type::Int32, Type::Int32) -> Type::Void,
211 ItemFontMetrics: (Type::ElementReference) -> typeregister::font_metrics_type(),
212 StringToFloat: (Type::String) -> Type::Float32,
213 StringIsFloat: (Type::String) -> Type::Bool,
214 StringIsEmpty: (Type::String) -> Type::Bool,
215 StringCharacterCount: (Type::String) -> Type::Int32,
216 StringToLowercase: (Type::String) -> Type::String,
217 StringToUppercase: (Type::String) -> Type::String,
218 KeysToString: (Type::Keys) -> Type::String,
219 ImplicitLayoutInfo(..): (Type::ElementReference, Type::Float32) -> typeregister::layout_info_type().into(),
220 ColorRgbaStruct: (Type::Color) -> Type::Struct(Rc::new(Struct {
221 fields: IntoIterator::into_iter([
222 (SmolStr::new_static("red"), Type::Int32),
223 (SmolStr::new_static("green"), Type::Int32),
224 (SmolStr::new_static("blue"), Type::Int32),
225 (SmolStr::new_static("alpha"), Type::Int32),
226 ])
227 .collect(),
228 name: BuiltinPublicStruct::Color.into(),
229 })),
230 ColorHsvaStruct: (Type::Color) -> Type::Struct(Rc::new(Struct {
231 fields: IntoIterator::into_iter([
232 (SmolStr::new_static("hue"), Type::Float32),
233 (SmolStr::new_static("saturation"), Type::Float32),
234 (SmolStr::new_static("value"), Type::Float32),
235 (SmolStr::new_static("alpha"), Type::Float32),
236 ])
237 .collect(),
238 name: BuiltinPublicStruct::Color.into(),
239 })),
240 ColorOklchStruct: (Type::Color) -> Type::Struct(Rc::new(Struct {
241 fields: IntoIterator::into_iter([
242 (SmolStr::new_static("lightness"), Type::Float32),
243 (SmolStr::new_static("chroma"), Type::Float32),
244 (SmolStr::new_static("hue"), Type::Float32),
245 (SmolStr::new_static("alpha"), Type::Float32),
246 ])
247 .collect(),
248 name: BuiltinPublicStruct::Color.into(),
249 })),
250 ColorBrighter: (Type::Brush, Type::Float32) -> Type::Brush,
251 ColorDarker: (Type::Brush, Type::Float32) -> Type::Brush,
252 ColorTransparentize: (Type::Brush, Type::Float32) -> Type::Brush,
253 ColorWithAlpha: (Type::Brush, Type::Float32) -> Type::Brush,
254 ColorMix: (Type::Color, Type::Color, Type::Float32) -> Type::Color,
255 ImageSize: (Type::Image) -> Type::Struct(Rc::new(Struct {
256 fields: IntoIterator::into_iter([
257 (SmolStr::new_static("width"), Type::Int32),
258 (SmolStr::new_static("height"), Type::Int32),
259 ])
260 .collect(),
261 name: crate::langtype::BuiltinPrivateStruct::Size.into(),
262 })),
263 ArrayLength: (Type::Model) -> Type::Int32,
264 Rgb: (Type::Int32, Type::Int32, Type::Int32, Type::Float32) -> Type::Color,
265 Hsv: (Type::Float32, Type::Float32, Type::Float32, Type::Float32) -> Type::Color,
266 Oklch: (Type::Float32, Type::Float32, Type::Float32, Type::Float32) -> Type::Color,
267 ColorScheme: () -> Type::Enumeration(
268 typeregister::BUILTIN.with(|e| e.enums.ColorScheme.clone()),
269 ),
270 AccentColor: () -> Type::Color,
271 SupportsNativeMenuBar: () -> Type::Bool,
272 SetupMenuBar: (Type::Model, typeregister::noarg_callback_type(), typeregister::noarg_callback_type()) -> Type::Void,
274 MonthDayCount: (Type::Int32, Type::Int32) -> Type::Int32,
275 MonthOffset: (Type::Int32, Type::Int32) -> Type::Int32,
276 FormatDate: (Type::String, Type::Int32, Type::Int32, Type::Int32) -> Type::String,
277 TextInputFocused: () -> Type::Bool,
278 DateNow: () -> Type::Array(Rc::new(Type::Int32)),
279 ValidDate: (Type::String, Type::String) -> Type::Bool,
280 ParseDate: (Type::String, Type::String) -> Type::Array(Rc::new(Type::Int32)),
281 SetTextInputFocused: (Type::Bool) -> Type::Void,
282 ItemAbsolutePosition: (Type::ElementReference) -> typeregister::logical_point_type().into(),
283 RegisterCustomFontByPath: (Type::String) -> Type::Void,
284 RegisterCustomFontByMemory: (Type::Int32) -> Type::Void,
285 RegisterBitmapFont: (Type::Int32) -> Type::Void,
286 Translate: (Type::String, Type::String, Type::String, Type::Array(Type::String.into())) -> Type::String,
288 Use24HourFormat: () -> Type::Bool,
289 UpdateTimers: () -> Type::Void,
290 DetectOperatingSystem: () -> Type::Enumeration(
291 typeregister::BUILTIN.with(|e| e.enums.OperatingSystemType.clone()),
292 ),
293 StartTimer: (Type::ElementReference) -> Type::Void,
294 StopTimer: (Type::ElementReference) -> Type::Void,
295 RestartTimer: (Type::ElementReference) -> Type::Void,
296 ParseMarkdown: (Type::String, Type::Array(Type::StyledText.into())) -> Type::StyledText,
297 StringToStyledText: (Type::String) -> Type::StyledText
298 OpenUrl: (Type::String) -> Type::Bool,
299);
300
301impl Default for BuiltinFunctionTypes {
302 fn default() -> Self {
303 Self::new()
304 }
305}
306
307impl BuiltinFunction {
308 pub fn ty(&self) -> Rc<Function> {
309 thread_local! {
310 static TYPES: BuiltinFunctionTypes = BuiltinFunctionTypes::new();
311 }
312 TYPES.with(|types| types.ty(self))
313 }
314
315 fn is_const(&self, global_analysis: Option<&crate::passes::GlobalAnalysis>) -> bool {
317 match self {
318 BuiltinFunction::GetWindowScaleFactor => {
319 global_analysis.is_some_and(|x| x.const_scale_factor.is_some())
320 }
321 BuiltinFunction::GetWindowDefaultFontSize => {
322 global_analysis.is_some_and(|x| x.default_font_size.is_const())
323 }
324 BuiltinFunction::AnimationTick => false,
325 BuiltinFunction::ColorScheme => false,
326 BuiltinFunction::AccentColor => false,
327 BuiltinFunction::SupportsNativeMenuBar => false,
328 BuiltinFunction::SetupMenuBar => false,
329 BuiltinFunction::MonthDayCount => false,
330 BuiltinFunction::MonthOffset => false,
331 BuiltinFunction::FormatDate => false,
332 BuiltinFunction::DateNow => false,
333 BuiltinFunction::ValidDate => false,
334 BuiltinFunction::ParseDate => false,
335 BuiltinFunction::Debug => true,
337 BuiltinFunction::Mod
338 | BuiltinFunction::Round
339 | BuiltinFunction::Ceil
340 | BuiltinFunction::Floor
341 | BuiltinFunction::Abs
342 | BuiltinFunction::Sqrt
343 | BuiltinFunction::Cos
344 | BuiltinFunction::Sin
345 | BuiltinFunction::Tan
346 | BuiltinFunction::ACos
347 | BuiltinFunction::ASin
348 | BuiltinFunction::Log
349 | BuiltinFunction::Ln
350 | BuiltinFunction::Pow
351 | BuiltinFunction::Exp
352 | BuiltinFunction::ATan
353 | BuiltinFunction::ATan2
354 | BuiltinFunction::ToFixed
355 | BuiltinFunction::ToPrecision => true,
356 BuiltinFunction::SetFocusItem | BuiltinFunction::ClearFocusItem => false,
357 BuiltinFunction::ShowPopupWindow
358 | BuiltinFunction::ClosePopupWindow
359 | BuiltinFunction::ShowPopupMenu
360 | BuiltinFunction::ShowPopupMenuInternal => false,
361 BuiltinFunction::SetSelectionOffsets => false,
362 BuiltinFunction::ItemFontMetrics => false, BuiltinFunction::StringToFloat
364 | BuiltinFunction::StringIsFloat
365 | BuiltinFunction::StringIsEmpty
366 | BuiltinFunction::StringCharacterCount
367 | BuiltinFunction::StringToLowercase
368 | BuiltinFunction::StringToUppercase
369 | BuiltinFunction::KeysToString => true,
370 BuiltinFunction::ColorRgbaStruct
371 | BuiltinFunction::ColorHsvaStruct
372 | BuiltinFunction::ColorOklchStruct
373 | BuiltinFunction::ColorBrighter
374 | BuiltinFunction::ColorDarker
375 | BuiltinFunction::ColorTransparentize
376 | BuiltinFunction::ColorMix
377 | BuiltinFunction::ColorWithAlpha => true,
378 #[cfg(not(target_arch = "wasm32"))]
383 BuiltinFunction::ImageSize => true,
384 #[cfg(target_arch = "wasm32")]
385 BuiltinFunction::ImageSize => false,
386 BuiltinFunction::ArrayLength => true,
387 BuiltinFunction::Rgb => true,
388 BuiltinFunction::Hsv => true,
389 BuiltinFunction::Oklch => true,
390 BuiltinFunction::SetTextInputFocused => false,
391 BuiltinFunction::TextInputFocused => false,
392 BuiltinFunction::ImplicitLayoutInfo(_) => false,
393 BuiltinFunction::ItemAbsolutePosition => true,
394 BuiltinFunction::RegisterCustomFontByPath
395 | BuiltinFunction::RegisterCustomFontByMemory
396 | BuiltinFunction::RegisterBitmapFont => false,
397 BuiltinFunction::Translate => false,
398 BuiltinFunction::Use24HourFormat => false,
399 BuiltinFunction::UpdateTimers => false,
400 BuiltinFunction::DetectOperatingSystem => true,
401 BuiltinFunction::StartTimer => false,
402 BuiltinFunction::StopTimer => false,
403 BuiltinFunction::RestartTimer => false,
404 BuiltinFunction::ParseMarkdown => false,
405 BuiltinFunction::StringToStyledText => true,
406 BuiltinFunction::OpenUrl => false,
407 }
408 }
409
410 pub fn is_pure(&self) -> bool {
412 match self {
413 BuiltinFunction::GetWindowScaleFactor => true,
414 BuiltinFunction::GetWindowDefaultFontSize => true,
415 BuiltinFunction::AnimationTick => true,
416 BuiltinFunction::ColorScheme => true,
417 BuiltinFunction::AccentColor => true,
418 BuiltinFunction::SupportsNativeMenuBar => true,
419 BuiltinFunction::SetupMenuBar => false,
420 BuiltinFunction::MonthDayCount => true,
421 BuiltinFunction::MonthOffset => true,
422 BuiltinFunction::FormatDate => true,
423 BuiltinFunction::DateNow => true,
424 BuiltinFunction::ValidDate => true,
425 BuiltinFunction::ParseDate => true,
426 BuiltinFunction::Debug => true,
428 BuiltinFunction::Mod
429 | BuiltinFunction::Round
430 | BuiltinFunction::Ceil
431 | BuiltinFunction::Floor
432 | BuiltinFunction::Abs
433 | BuiltinFunction::Sqrt
434 | BuiltinFunction::Cos
435 | BuiltinFunction::Sin
436 | BuiltinFunction::Tan
437 | BuiltinFunction::ACos
438 | BuiltinFunction::ASin
439 | BuiltinFunction::Log
440 | BuiltinFunction::Ln
441 | BuiltinFunction::Pow
442 | BuiltinFunction::Exp
443 | BuiltinFunction::ATan
444 | BuiltinFunction::ATan2
445 | BuiltinFunction::ToFixed
446 | BuiltinFunction::ToPrecision => true,
447 BuiltinFunction::SetFocusItem | BuiltinFunction::ClearFocusItem => false,
448 BuiltinFunction::ShowPopupWindow
449 | BuiltinFunction::ClosePopupWindow
450 | BuiltinFunction::ShowPopupMenu
451 | BuiltinFunction::ShowPopupMenuInternal => false,
452 BuiltinFunction::SetSelectionOffsets => false,
453 BuiltinFunction::ItemFontMetrics => true,
454 BuiltinFunction::StringToFloat
455 | BuiltinFunction::StringIsFloat
456 | BuiltinFunction::StringIsEmpty
457 | BuiltinFunction::StringCharacterCount
458 | BuiltinFunction::StringToLowercase
459 | BuiltinFunction::StringToUppercase
460 | BuiltinFunction::KeysToString => true,
461 BuiltinFunction::ColorRgbaStruct
462 | BuiltinFunction::ColorHsvaStruct
463 | BuiltinFunction::ColorOklchStruct
464 | BuiltinFunction::ColorBrighter
465 | BuiltinFunction::ColorDarker
466 | BuiltinFunction::ColorTransparentize
467 | BuiltinFunction::ColorMix
468 | BuiltinFunction::ColorWithAlpha => true,
469 BuiltinFunction::ImageSize => true,
470 BuiltinFunction::ArrayLength => true,
471 BuiltinFunction::Rgb => true,
472 BuiltinFunction::Hsv => true,
473 BuiltinFunction::Oklch => true,
474 BuiltinFunction::ImplicitLayoutInfo(_) => true,
475 BuiltinFunction::ItemAbsolutePosition => true,
476 BuiltinFunction::SetTextInputFocused => false,
477 BuiltinFunction::TextInputFocused => true,
478 BuiltinFunction::RegisterCustomFontByPath
479 | BuiltinFunction::RegisterCustomFontByMemory
480 | BuiltinFunction::RegisterBitmapFont => false,
481 BuiltinFunction::Translate => true,
482 BuiltinFunction::Use24HourFormat => true,
483 BuiltinFunction::UpdateTimers => false,
484 BuiltinFunction::DetectOperatingSystem => true,
485 BuiltinFunction::StartTimer => false,
486 BuiltinFunction::StopTimer => false,
487 BuiltinFunction::RestartTimer => false,
488 BuiltinFunction::ParseMarkdown => true,
489 BuiltinFunction::StringToStyledText => true,
490 BuiltinFunction::OpenUrl => false,
491 }
492 }
493}
494
495#[derive(Debug, Clone)]
497pub enum Callable {
498 Callback(NamedReference),
499 Function(NamedReference),
500 Builtin(BuiltinFunction),
501}
502impl Callable {
503 pub fn ty(&self) -> Type {
504 match self {
505 Callable::Callback(nr) => nr.ty(),
506 Callable::Function(nr) => nr.ty(),
507 Callable::Builtin(b) => Type::Function(b.ty()),
508 }
509 }
510}
511impl From<BuiltinFunction> for Callable {
512 fn from(function: BuiltinFunction) -> Self {
513 Self::Builtin(function)
514 }
515}
516
517#[derive(Debug, Clone, Eq, PartialEq)]
518pub enum OperatorClass {
519 ComparisonOp,
520 LogicalOp,
521 ArithmeticOp,
522}
523
524pub fn operator_class(op: char) -> OperatorClass {
526 match op {
527 '=' | '!' | '<' | '>' | '≤' | '≥' => OperatorClass::ComparisonOp,
528 '&' | '|' => OperatorClass::LogicalOp,
529 '+' | '-' | '/' | '*' => OperatorClass::ArithmeticOp,
530 _ => panic!("Invalid operator {op:?}"),
531 }
532}
533
534macro_rules! declare_units {
535 ($( $(#[$m:meta])* $ident:ident = $string:literal -> $ty:ident $(* $factor:expr)? ,)*) => {
536 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, strum::EnumIter)]
538 pub enum Unit {
539 $($(#[$m])* $ident,)*
540 }
541
542 impl std::fmt::Display for Unit {
543 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
544 match self {
545 $(Self::$ident => write!(f, $string), )*
546 }
547 }
548 }
549
550 impl std::str::FromStr for Unit {
551 type Err = ();
552 fn from_str(s: &str) -> Result<Self, Self::Err> {
553 match s {
554 $($string => Ok(Self::$ident), )*
555 _ => Err(())
556 }
557 }
558 }
559
560 impl Unit {
561 pub fn ty(self) -> Type {
562 match self {
563 $(Self::$ident => Type::$ty, )*
564 }
565 }
566
567 pub fn normalize(self, x: f64) -> f64 {
568 match self {
569 $(Self::$ident => x $(* $factor as f64)?, )*
570 }
571 }
572
573 }
574 };
575}
576
577declare_units! {
578 None = "" -> Float32,
580 Percent = "%" -> Percent,
582
583 Phx = "phx" -> PhysicalLength,
587 Px = "px" -> LogicalLength,
589 Cm = "cm" -> LogicalLength * 37.8,
591 Mm = "mm" -> LogicalLength * 3.78,
593 In = "in" -> LogicalLength * 96,
595 Pt = "pt" -> LogicalLength * 96./72.,
597 Rem = "rem" -> Rem,
599
600 S = "s" -> Duration * 1000,
604 Ms = "ms" -> Duration,
606
607 Deg = "deg" -> Angle,
611 Grad = "grad" -> Angle * 360./180.,
613 Turn = "turn" -> Angle * 360.,
615 Rad = "rad" -> Angle * 360./std::f32::consts::TAU,
617}
618
619#[allow(clippy::derivable_impls)] impl Default for Unit {
621 fn default() -> Self {
622 Self::None
623 }
624}
625
626#[derive(Debug, Clone, Copy)]
627pub enum MinMaxOp {
628 Min,
629 Max,
630}
631
632#[derive(Debug, Clone, Default)]
634pub enum Expression {
635 #[default]
637 Invalid,
638 Uncompiled(SyntaxNode),
640
641 StringLiteral(SmolStr),
643 NumberLiteral(f64, Unit),
645 BoolLiteral(bool),
647
648 PropertyReference(NamedReference),
650
651 ElementReference(Weak<RefCell<Element>>),
654
655 RepeaterIndexReference {
660 element: Weak<RefCell<Element>>,
661 },
662
663 RepeaterModelReference {
668 element: Weak<RefCell<Element>>,
669 },
670
671 FunctionParameterReference {
673 index: usize,
674 ty: Type,
675 },
676
677 StoreLocalVariable {
679 name: SmolStr,
680 value: Box<Expression>,
681 },
682
683 ReadLocalVariable {
686 name: SmolStr,
687 ty: Type,
688 },
689
690 StructFieldAccess {
692 base: Box<Expression>,
694 name: SmolStr,
695 },
696
697 ArrayIndex {
699 array: Box<Expression>,
701 index: Box<Expression>,
702 },
703
704 Cast {
706 from: Box<Expression>,
707 to: Type,
708 },
709
710 CodeBlock(Vec<Expression>),
712
713 FunctionCall {
715 function: Callable,
716 arguments: Vec<Expression>,
717 source_location: Option<SourceLocation>,
718 },
719
720 SelfAssignment {
722 lhs: Box<Expression>,
723 rhs: Box<Expression>,
724 op: char,
726 node: Option<NodeOrToken>,
727 },
728
729 BinaryExpression {
730 lhs: Box<Expression>,
731 rhs: Box<Expression>,
732 op: char,
734 },
735
736 UnaryOp {
737 sub: Box<Expression>,
738 op: char,
740 },
741
742 ImageReference {
743 resource_ref: ImageReference,
744 source_location: Option<SourceLocation>,
745 nine_slice: Option<[u16; 4]>,
746 },
747
748 Condition {
749 condition: Box<Expression>,
750 true_expr: Box<Expression>,
751 false_expr: Box<Expression>,
752 },
753
754 Array {
755 element_ty: Type,
756 values: Vec<Expression>,
757 },
758 Struct {
759 ty: Rc<Struct>,
760 values: HashMap<SmolStr, Expression>,
761 },
762
763 PathData(Path),
764
765 EasingCurve(EasingCurve),
766
767 LinearGradient {
768 angle: Box<Expression>,
769 stops: Vec<(Expression, Expression)>,
771 },
772
773 RadialGradient {
774 stops: Vec<(Expression, Expression)>,
776 },
777
778 ConicGradient {
779 from_angle: Box<Expression>,
781 stops: Vec<(Expression, Expression)>,
783 },
784
785 EnumerationValue(EnumerationValue),
786
787 Keys(Keys),
788
789 ReturnStatement(Option<Box<Expression>>),
790
791 LayoutCacheAccess {
793 layout_cache_prop: NamedReference,
795 index: usize,
797 repeater_index: Option<Box<Expression>>,
801 entries_per_item: usize,
805 },
806
807 GridRepeaterCacheAccess {
809 layout_cache_prop: NamedReference,
811 index: usize,
813 repeater_index: Box<Expression>,
815 stride: Box<Expression>,
819 child_offset: usize,
821 inner_repeater_index: Option<Box<Expression>>,
823 entries_per_item: usize,
825 },
826
827 OrganizeGridLayout(crate::layout::GridLayout),
829
830 ComputeBoxLayoutInfo(crate::layout::BoxLayout, crate::layout::Orientation),
833 ComputeGridLayoutInfo {
834 layout_organized_data_prop: NamedReference,
835 layout: crate::layout::GridLayout,
836 orientation: crate::layout::Orientation,
837 },
838 SolveBoxLayout(crate::layout::BoxLayout, crate::layout::Orientation),
840 SolveGridLayout {
841 layout_organized_data_prop: NamedReference,
842 layout: crate::layout::GridLayout,
843 orientation: crate::layout::Orientation,
844 },
845 SolveFlexboxLayout(crate::layout::FlexboxLayout),
847 ComputeFlexboxLayoutInfo(crate::layout::FlexboxLayout, crate::layout::Orientation),
849
850 MinMax {
851 ty: Type,
852 op: MinMaxOp,
853 lhs: Box<Expression>,
854 rhs: Box<Expression>,
855 },
856
857 DebugHook {
858 expression: Box<Expression>,
859 id: SmolStr,
860 },
861
862 EmptyComponentFactory,
863}
864
865impl Expression {
866 pub fn ty(&self) -> Type {
868 match self {
869 Expression::Invalid => Type::Invalid,
870 Expression::Uncompiled(_) => Type::Invalid,
871 Expression::StringLiteral(_) => Type::String,
872 Expression::NumberLiteral(_, unit) => unit.ty(),
873 Expression::BoolLiteral(_) => Type::Bool,
874 Expression::PropertyReference(nr) => nr.ty(),
875 Expression::ElementReference(_) => Type::ElementReference,
876 Expression::RepeaterIndexReference { .. } => Type::Int32,
877 Expression::RepeaterModelReference { element } => element
878 .upgrade()
879 .unwrap()
880 .borrow()
881 .repeated
882 .as_ref()
883 .map_or(Type::Invalid, |e| model_inner_type(&e.model)),
884 Expression::FunctionParameterReference { ty, .. } => ty.clone(),
885 Expression::StructFieldAccess { base, name } => match base.ty() {
886 Type::Struct(s) => s.fields.get(name.as_str()).unwrap_or(&Type::Invalid).clone(),
887 _ => Type::Invalid,
888 },
889 Expression::ArrayIndex { array, .. } => match array.ty() {
890 Type::Array(ty) => (*ty).clone(),
891 _ => Type::Invalid,
892 },
893 Expression::Cast { to, .. } => to.clone(),
894 Expression::CodeBlock(sub) => sub.last().map_or(Type::Void, |e| e.ty()),
895 Expression::FunctionCall { function, .. } => match function.ty() {
896 Type::Function(f) | Type::Callback(f) => f.return_type.clone(),
897 _ => Type::Invalid,
898 },
899 Expression::SelfAssignment { .. } => Type::Void,
900 Expression::ImageReference { .. } => Type::Image,
901 Expression::Condition { condition: _, true_expr, false_expr } => {
902 let true_type = true_expr.ty();
903 let false_type = false_expr.ty();
904 if true_type == false_type {
905 true_type
906 } else if true_type == Type::Invalid {
907 false_type
908 } else if false_type == Type::Invalid {
909 true_type
910 } else {
911 Type::Void
912 }
913 }
914 Expression::BinaryExpression { op, lhs, rhs } => {
915 if operator_class(*op) != OperatorClass::ArithmeticOp {
916 Type::Bool
917 } else if *op == '+' || *op == '-' {
918 let (rhs_ty, lhs_ty) = (rhs.ty(), lhs.ty());
919 if rhs_ty == lhs_ty { rhs_ty } else { Type::Invalid }
920 } else {
921 debug_assert!(*op == '*' || *op == '/');
922 let unit_vec = |ty| {
923 if let Type::UnitProduct(v) = ty {
924 v
925 } else if let Some(u) = ty.default_unit() {
926 vec![(u, 1)]
927 } else {
928 Vec::new()
929 }
930 };
931 let mut l_units = unit_vec(lhs.ty());
932 let mut r_units = unit_vec(rhs.ty());
933 if *op == '/' {
934 for (_, power) in &mut r_units {
935 *power = -*power;
936 }
937 }
938 for (unit, power) in r_units {
939 if let Some((_, p)) = l_units.iter_mut().find(|(u, _)| *u == unit) {
940 *p += power;
941 } else {
942 l_units.push((unit, power));
943 }
944 }
945
946 l_units.retain(|(_, p)| *p != 0);
948 l_units.sort_unstable_by(|(u1, p1), (u2, p2)| match p2.cmp(p1) {
949 std::cmp::Ordering::Equal => u1.cmp(u2),
950 x => x,
951 });
952
953 if l_units.is_empty() {
954 Type::Float32
955 } else if l_units.len() == 1 && l_units[0].1 == 1 {
956 l_units[0].0.ty()
957 } else {
958 Type::UnitProduct(l_units)
959 }
960 }
961 }
962 Expression::UnaryOp { sub, .. } => sub.ty(),
963 Expression::Array { element_ty, .. } => Type::Array(Rc::new(element_ty.clone())),
964 Expression::Struct { ty, .. } => ty.clone().into(),
965 Expression::PathData { .. } => Type::PathData,
966 Expression::StoreLocalVariable { .. } => Type::Void,
967 Expression::ReadLocalVariable { ty, .. } => ty.clone(),
968 Expression::EasingCurve(_) => Type::Easing,
969 Expression::LinearGradient { .. } => Type::Brush,
970 Expression::RadialGradient { .. } => Type::Brush,
971 Expression::ConicGradient { .. } => Type::Brush,
972 Expression::EnumerationValue(value) => Type::Enumeration(value.enumeration.clone()),
973 Expression::Keys(_) => Type::Keys,
974 Expression::ReturnStatement(_) => Type::Invalid,
976 Expression::LayoutCacheAccess { .. } => Type::LogicalLength,
977 Expression::GridRepeaterCacheAccess { .. } => Type::LogicalLength,
978 Expression::OrganizeGridLayout(..) => Type::ArrayOfU16,
979 Expression::ComputeBoxLayoutInfo(..) => typeregister::layout_info_type().into(),
980 Expression::ComputeGridLayoutInfo { .. } => typeregister::layout_info_type().into(),
981 Expression::SolveBoxLayout(..) => Type::LayoutCache,
982 Expression::SolveGridLayout { .. } => Type::LayoutCache,
983 Expression::SolveFlexboxLayout(..) => Type::LayoutCache,
984 Expression::ComputeFlexboxLayoutInfo(..) => typeregister::layout_info_type().into(),
985 Expression::MinMax { ty, .. } => ty.clone(),
986 Expression::EmptyComponentFactory => Type::ComponentFactory,
987 Expression::DebugHook { expression, .. } => expression.ty(),
988 }
989 }
990
991 pub fn visit(&self, mut visitor: impl FnMut(&Self)) {
993 match self {
994 Expression::Invalid => {}
995 Expression::Uncompiled(_) => {}
996 Expression::StringLiteral(_) => {}
997 Expression::NumberLiteral(_, _) => {}
998 Expression::BoolLiteral(_) => {}
999 Expression::PropertyReference { .. } => {}
1000 Expression::FunctionParameterReference { .. } => {}
1001 Expression::ElementReference(_) => {}
1002 Expression::StructFieldAccess { base, .. } => visitor(base),
1003 Expression::ArrayIndex { array, index } => {
1004 visitor(array);
1005 visitor(index);
1006 }
1007 Expression::RepeaterIndexReference { .. } => {}
1008 Expression::RepeaterModelReference { .. } => {}
1009 Expression::Cast { from, .. } => visitor(from),
1010 Expression::CodeBlock(sub) => {
1011 sub.iter().for_each(visitor);
1012 }
1013 Expression::FunctionCall { function: _, arguments, source_location: _ } => {
1014 arguments.iter().for_each(visitor);
1015 }
1016 Expression::SelfAssignment { lhs, rhs, .. } => {
1017 visitor(lhs);
1018 visitor(rhs);
1019 }
1020 Expression::ImageReference { .. } => {}
1021 Expression::Condition { condition, true_expr, false_expr } => {
1022 visitor(condition);
1023 visitor(true_expr);
1024 visitor(false_expr);
1025 }
1026 Expression::BinaryExpression { lhs, rhs, .. } => {
1027 visitor(lhs);
1028 visitor(rhs);
1029 }
1030 Expression::UnaryOp { sub, .. } => visitor(sub),
1031 Expression::Array { values, .. } => {
1032 for x in values {
1033 visitor(x);
1034 }
1035 }
1036 Expression::Struct { values, .. } => {
1037 for x in values.values() {
1038 visitor(x);
1039 }
1040 }
1041 Expression::PathData(data) => match data {
1042 Path::Elements(elements) => {
1043 for element in elements {
1044 element.bindings.values().for_each(|binding| visitor(&binding.borrow()))
1045 }
1046 }
1047 Path::Events(events, coordinates) => {
1048 events.iter().chain(coordinates.iter()).for_each(visitor);
1049 }
1050 Path::Commands(commands) => visitor(commands),
1051 },
1052 Expression::StoreLocalVariable { value, .. } => visitor(value),
1053 Expression::ReadLocalVariable { .. } => {}
1054 Expression::EasingCurve(_) => {}
1055 Expression::LinearGradient { angle, stops } => {
1056 visitor(angle);
1057 for (c, s) in stops {
1058 visitor(c);
1059 visitor(s);
1060 }
1061 }
1062 Expression::RadialGradient { stops } => {
1063 for (c, s) in stops {
1064 visitor(c);
1065 visitor(s);
1066 }
1067 }
1068 Expression::ConicGradient { from_angle, stops } => {
1069 visitor(from_angle);
1070 for (c, s) in stops {
1071 visitor(c);
1072 visitor(s);
1073 }
1074 }
1075 Expression::EnumerationValue(_) => {}
1076 Expression::Keys(_) => {}
1077 Expression::ReturnStatement(expr) => {
1078 expr.as_deref().map(visitor);
1079 }
1080 Expression::LayoutCacheAccess { repeater_index, .. } => {
1081 repeater_index.as_deref().map(visitor);
1082 }
1083 Expression::GridRepeaterCacheAccess {
1084 repeater_index,
1085 stride,
1086 inner_repeater_index,
1087 ..
1088 } => {
1089 visitor(repeater_index);
1090 visitor(stride);
1091 inner_repeater_index.as_deref().map(visitor);
1092 }
1093 Expression::OrganizeGridLayout(..) => {}
1094 Expression::ComputeBoxLayoutInfo(..) => {}
1095 Expression::ComputeGridLayoutInfo { .. } => {}
1096 Expression::SolveBoxLayout(..) => {}
1097 Expression::SolveGridLayout { .. } => {}
1098 Expression::SolveFlexboxLayout(..) => {}
1099 Expression::ComputeFlexboxLayoutInfo(..) => {}
1100 Expression::MinMax { lhs, rhs, .. } => {
1101 visitor(lhs);
1102 visitor(rhs);
1103 }
1104 Expression::EmptyComponentFactory => {}
1105 Expression::DebugHook { expression, .. } => visitor(expression),
1106 }
1107 }
1108
1109 pub fn visit_mut(&mut self, mut visitor: impl FnMut(&mut Self)) {
1110 match self {
1111 Expression::Invalid => {}
1112 Expression::Uncompiled(_) => {}
1113 Expression::StringLiteral(_) => {}
1114 Expression::NumberLiteral(_, _) => {}
1115 Expression::BoolLiteral(_) => {}
1116 Expression::PropertyReference { .. } => {}
1117 Expression::FunctionParameterReference { .. } => {}
1118 Expression::ElementReference(_) => {}
1119 Expression::StructFieldAccess { base, .. } => visitor(base),
1120 Expression::ArrayIndex { array, index } => {
1121 visitor(array);
1122 visitor(index);
1123 }
1124 Expression::RepeaterIndexReference { .. } => {}
1125 Expression::RepeaterModelReference { .. } => {}
1126 Expression::Cast { from, .. } => visitor(from),
1127 Expression::CodeBlock(sub) => {
1128 sub.iter_mut().for_each(visitor);
1129 }
1130 Expression::FunctionCall { function: _, arguments, source_location: _ } => {
1131 arguments.iter_mut().for_each(visitor);
1132 }
1133 Expression::SelfAssignment { lhs, rhs, .. } => {
1134 visitor(lhs);
1135 visitor(rhs);
1136 }
1137 Expression::ImageReference { .. } => {}
1138 Expression::Condition { condition, true_expr, false_expr } => {
1139 visitor(condition);
1140 visitor(true_expr);
1141 visitor(false_expr);
1142 }
1143 Expression::BinaryExpression { lhs, rhs, .. } => {
1144 visitor(lhs);
1145 visitor(rhs);
1146 }
1147 Expression::UnaryOp { sub, .. } => visitor(sub),
1148 Expression::Array { values, .. } => {
1149 for x in values {
1150 visitor(x);
1151 }
1152 }
1153 Expression::Struct { values, .. } => {
1154 for x in values.values_mut() {
1155 visitor(x);
1156 }
1157 }
1158 Expression::PathData(data) => match data {
1159 Path::Elements(elements) => {
1160 for element in elements {
1161 element
1162 .bindings
1163 .values_mut()
1164 .for_each(|binding| visitor(&mut binding.borrow_mut()))
1165 }
1166 }
1167 Path::Events(events, coordinates) => {
1168 events.iter_mut().chain(coordinates.iter_mut()).for_each(visitor);
1169 }
1170 Path::Commands(commands) => visitor(commands),
1171 },
1172 Expression::StoreLocalVariable { value, .. } => visitor(value),
1173 Expression::ReadLocalVariable { .. } => {}
1174 Expression::EasingCurve(_) => {}
1175 Expression::LinearGradient { angle, stops } => {
1176 visitor(angle);
1177 for (c, s) in stops {
1178 visitor(c);
1179 visitor(s);
1180 }
1181 }
1182 Expression::RadialGradient { stops } => {
1183 for (c, s) in stops {
1184 visitor(c);
1185 visitor(s);
1186 }
1187 }
1188 Expression::ConicGradient { from_angle, stops } => {
1189 visitor(from_angle);
1190 for (c, s) in stops {
1191 visitor(c);
1192 visitor(s);
1193 }
1194 }
1195 Expression::EnumerationValue(_) => {}
1196 Expression::Keys(_) => {}
1197 Expression::ReturnStatement(expr) => {
1198 expr.as_deref_mut().map(visitor);
1199 }
1200 Expression::LayoutCacheAccess { repeater_index, .. } => {
1201 repeater_index.as_deref_mut().map(visitor);
1202 }
1203 Expression::GridRepeaterCacheAccess {
1204 repeater_index,
1205 stride,
1206 inner_repeater_index,
1207 ..
1208 } => {
1209 visitor(repeater_index);
1210 visitor(stride);
1211 inner_repeater_index.as_deref_mut().map(visitor);
1212 }
1213 Expression::OrganizeGridLayout(..) => {}
1214 Expression::ComputeBoxLayoutInfo(..) => {}
1215 Expression::ComputeGridLayoutInfo { .. } => {}
1216 Expression::SolveBoxLayout(..) => {}
1217 Expression::SolveGridLayout { .. } => {}
1218 Expression::SolveFlexboxLayout(..) => {}
1219 Expression::ComputeFlexboxLayoutInfo(..) => {}
1220 Expression::MinMax { lhs, rhs, .. } => {
1221 visitor(lhs);
1222 visitor(rhs);
1223 }
1224 Expression::EmptyComponentFactory => {}
1225 Expression::DebugHook { expression, .. } => visitor(expression),
1226 }
1227 }
1228
1229 pub fn visit_recursive(&self, visitor: &mut dyn FnMut(&Self)) {
1231 visitor(self);
1232 self.visit(|e| e.visit_recursive(visitor));
1233 }
1234
1235 pub fn visit_recursive_mut(&mut self, visitor: &mut dyn FnMut(&mut Self)) {
1237 visitor(self);
1238 self.visit_mut(|e| e.visit_recursive_mut(visitor));
1239 }
1240
1241 pub fn is_constant(&self, ga: Option<&crate::passes::GlobalAnalysis>) -> bool {
1242 match self {
1243 Expression::Invalid => true,
1244 Expression::Uncompiled(_) => false,
1245 Expression::StringLiteral(_) => true,
1246 Expression::NumberLiteral(_, _) => true,
1247 Expression::BoolLiteral(_) => true,
1248 Expression::PropertyReference(nr) => nr.is_constant(),
1249 Expression::ElementReference(_) => false,
1250 Expression::RepeaterIndexReference { .. } => false,
1251 Expression::RepeaterModelReference { .. } => false,
1252 Expression::FunctionParameterReference { .. } => true,
1254 Expression::StructFieldAccess { base, .. } => base.is_constant(ga),
1255 Expression::ArrayIndex { array, index } => {
1256 array.is_constant(ga) && index.is_constant(ga)
1257 }
1258 Expression::Cast { from, .. } => from.is_constant(ga),
1259 Expression::CodeBlock(sub) => sub.iter().all(|s| s.is_constant(ga)),
1262 Expression::FunctionCall { function, arguments, .. } => {
1263 let is_const = match function {
1264 Callable::Builtin(b) => b.is_const(ga),
1265 Callable::Function(nr) => nr.is_constant(),
1266 Callable::Callback(..) => false,
1267 };
1268 is_const && arguments.iter().all(|a| a.is_constant(ga))
1269 }
1270 Expression::SelfAssignment { .. } => false,
1271 Expression::ImageReference { .. } => true,
1272 Expression::Condition { condition, false_expr, true_expr } => {
1273 condition.is_constant(ga) && false_expr.is_constant(ga) && true_expr.is_constant(ga)
1274 }
1275 Expression::BinaryExpression { lhs, rhs, .. } => {
1276 lhs.is_constant(ga) && rhs.is_constant(ga)
1277 }
1278 Expression::UnaryOp { sub, .. } => sub.is_constant(ga),
1279 Expression::Array { .. } => false,
1283 Expression::Struct { values, .. } => values.iter().all(|(_, v)| v.is_constant(ga)),
1284 Expression::PathData(data) => match data {
1285 Path::Elements(elements) => elements
1286 .iter()
1287 .all(|element| element.bindings.values().all(|v| v.borrow().is_constant(ga))),
1288 Path::Events(_, _) => true,
1289 Path::Commands(_) => false,
1290 },
1291 Expression::StoreLocalVariable { value, .. } => value.is_constant(ga),
1292 Expression::ReadLocalVariable { .. } => true,
1294 Expression::EasingCurve(_) => true,
1295 Expression::LinearGradient { angle, stops } => {
1296 angle.is_constant(ga)
1297 && stops.iter().all(|(c, s)| c.is_constant(ga) && s.is_constant(ga))
1298 }
1299 Expression::RadialGradient { stops } => {
1300 stops.iter().all(|(c, s)| c.is_constant(ga) && s.is_constant(ga))
1301 }
1302 Expression::ConicGradient { from_angle, stops } => {
1303 from_angle.is_constant(ga)
1304 && stops.iter().all(|(c, s)| c.is_constant(ga) && s.is_constant(ga))
1305 }
1306 Expression::EnumerationValue(_) => true,
1307 Expression::Keys(_) => true,
1308 Expression::ReturnStatement(expr) => {
1309 expr.as_ref().is_none_or(|expr| expr.is_constant(ga))
1310 }
1311 Expression::LayoutCacheAccess { .. } => false,
1313 Expression::GridRepeaterCacheAccess { .. } => false,
1314 Expression::OrganizeGridLayout { .. } => false,
1315 Expression::ComputeBoxLayoutInfo(..) => false,
1316 Expression::ComputeGridLayoutInfo { .. } => false,
1317 Expression::SolveBoxLayout(..) => false,
1318 Expression::SolveGridLayout { .. } => false,
1319 Expression::SolveFlexboxLayout(..) => false,
1320 Expression::ComputeFlexboxLayoutInfo(..) => false,
1321 Expression::MinMax { lhs, rhs, .. } => lhs.is_constant(ga) && rhs.is_constant(ga),
1322 Expression::EmptyComponentFactory => true,
1323 Expression::DebugHook { .. } => false,
1324 }
1325 }
1326
1327 #[must_use]
1329 pub fn maybe_convert_to(
1330 self,
1331 target_type: Type,
1332 node: &dyn Spanned,
1333 diag: &mut BuildDiagnostics,
1334 ) -> Expression {
1335 let ty = self.ty();
1336 if ty == target_type
1337 || target_type == Type::Void
1338 || target_type == Type::Invalid
1339 || ty == Type::Invalid
1340 {
1341 self
1342 } else if ty.can_convert(&target_type) {
1343 let from = match (ty, &target_type) {
1344 (Type::Brush, Type::Color) => match self {
1345 Expression::LinearGradient { .. }
1346 | Expression::RadialGradient { .. }
1347 | Expression::ConicGradient { .. } => {
1348 let message = format!(
1349 "Narrowing conversion from {0} to {1}. This can lead to unexpected behavior because the {0} is a gradient",
1350 Type::Brush,
1351 Type::Color
1352 );
1353 diag.push_warning(message, node);
1354 self
1355 }
1356 _ => self,
1357 },
1358 (Type::Percent, Type::Float32) => Expression::BinaryExpression {
1359 lhs: Box::new(self),
1360 rhs: Box::new(Expression::NumberLiteral(0.01, Unit::None)),
1361 op: '*',
1362 },
1363 (ref from_ty @ Type::Struct(ref left), Type::Struct(right))
1364 if left.fields != right.fields =>
1365 {
1366 if let Expression::Struct { mut values, .. } = self {
1367 let mut new_values = HashMap::new();
1368 for (key, ty) in &right.fields {
1369 let (key, expression) = values.remove_entry(key).map_or_else(
1370 || (key.clone(), Expression::default_value_for_type(ty)),
1371 |(k, e)| (k, e.maybe_convert_to(ty.clone(), node, diag)),
1372 );
1373 new_values.insert(key, expression);
1374 }
1375 return Expression::Struct { values: new_values, ty: right.clone() };
1376 }
1377 static COUNT: std::sync::atomic::AtomicUsize =
1378 std::sync::atomic::AtomicUsize::new(0);
1379 let var_name = format_smolstr!(
1380 "tmpobj_conv_{}",
1381 COUNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
1382 );
1383 let mut new_values = HashMap::new();
1384 for (key, ty) in &right.fields {
1385 let expression = if left.fields.contains_key(key) {
1386 Expression::StructFieldAccess {
1387 base: Box::new(Expression::ReadLocalVariable {
1388 name: var_name.clone(),
1389 ty: from_ty.clone(),
1390 }),
1391 name: key.clone(),
1392 }
1393 .maybe_convert_to(ty.clone(), node, diag)
1394 } else {
1395 Expression::default_value_for_type(ty)
1396 };
1397 new_values.insert(key.clone(), expression);
1398 }
1399 return Expression::CodeBlock(vec![
1400 Expression::StoreLocalVariable { name: var_name, value: Box::new(self) },
1401 Expression::Struct { values: new_values, ty: right.clone() },
1402 ]);
1403 }
1404 (left, right) => match (left.as_unit_product(), right.as_unit_product()) {
1405 (Some(left), Some(right)) => {
1406 if let Some(conversion_powers) =
1407 crate::langtype::unit_product_length_conversion(&left, &right)
1408 {
1409 let apply_power =
1410 |mut result, power: i8, builtin_fn: BuiltinFunction| {
1411 let op = if power < 0 { '*' } else { '/' };
1412 for _ in 0..power.abs() {
1413 result = Expression::BinaryExpression {
1414 lhs: Box::new(result),
1415 rhs: Box::new(Expression::FunctionCall {
1416 function: Callable::Builtin(builtin_fn.clone()),
1417 arguments: Vec::new(),
1418 source_location: Some(node.to_source_location()),
1419 }),
1420 op,
1421 }
1422 }
1423 result
1424 };
1425
1426 let mut result = self;
1427
1428 if conversion_powers.rem_to_px_power != 0 {
1429 result = apply_power(
1430 result,
1431 conversion_powers.rem_to_px_power,
1432 BuiltinFunction::GetWindowDefaultFontSize,
1433 )
1434 }
1435 if conversion_powers.px_to_phx_power != 0 {
1436 result = apply_power(
1437 result,
1438 conversion_powers.px_to_phx_power,
1439 BuiltinFunction::GetWindowScaleFactor,
1440 )
1441 }
1442
1443 result
1444 } else {
1445 self
1446 }
1447 }
1448 _ => self,
1449 },
1450 };
1451 Expression::Cast { from: Box::new(from), to: target_type }
1452 } else if matches!(
1453 (&ty, &target_type, &self),
1454 (Type::Array(_), Type::Array(_), Expression::Array { .. })
1455 ) {
1456 match (self, target_type) {
1458 (Expression::Array { values, .. }, Type::Array(target_type)) => Expression::Array {
1459 values: values
1460 .into_iter()
1461 .map(|e| e.maybe_convert_to((*target_type).clone(), node, diag))
1462 .take_while(|e| !matches!(e, Expression::Invalid))
1463 .collect(),
1464 element_ty: (*target_type).clone(),
1465 },
1466 _ => unreachable!(),
1467 }
1468 } else if let (Type::Struct(struct_type), Expression::Struct { values, .. }) =
1469 (&target_type, &self)
1470 {
1471 let mut fields = struct_type.fields.clone();
1473 let mut new_values = HashMap::new();
1474 for (f, v) in values {
1475 if let Some(t) = fields.remove(f) {
1476 new_values.insert(f.clone(), v.clone().maybe_convert_to(t, node, diag));
1477 } else {
1478 diag.push_error(format!("Cannot convert {ty} to {target_type}"), node);
1479 return self;
1480 }
1481 }
1482 for (f, t) in fields {
1483 new_values.insert(f, Expression::default_value_for_type(&t));
1484 }
1485 Expression::Struct { ty: struct_type.clone(), values: new_values }
1486 } else {
1487 let mut message = format!("Cannot convert {ty} to {target_type}");
1488 if let Some(from_unit) = ty.default_unit() {
1490 if matches!(&target_type, Type::Int32 | Type::Float32 | Type::String) {
1491 message =
1492 format!("{message}. Divide by 1{from_unit} to convert to a plain number");
1493 }
1494 } else if let Some(to_unit) = target_type.default_unit()
1495 && matches!(ty, Type::Int32 | Type::Float32)
1496 {
1497 if let Expression::NumberLiteral(value, Unit::None) = self
1498 && value == 0.
1499 {
1500 return Expression::NumberLiteral(0., to_unit);
1502 }
1503 message = format!(
1504 "{message}. Use an unit, or multiply by 1{to_unit} to convert explicitly"
1505 );
1506 }
1507 diag.push_error(message, node);
1508 self
1509 }
1510 }
1511
1512 pub fn default_value_for_type(ty: &Type) -> Expression {
1514 match ty {
1515 Type::Invalid
1516 | Type::Callback { .. }
1517 | Type::Function { .. }
1518 | Type::InferredProperty
1519 | Type::InferredCallback
1520 | Type::ElementReference
1521 | Type::LayoutCache
1522 | Type::ArrayOfU16 => Expression::Invalid,
1523 Type::Void => Expression::CodeBlock(Vec::new()),
1524 Type::Float32 => Expression::NumberLiteral(0., Unit::None),
1525 Type::String => Expression::StringLiteral(SmolStr::default()),
1526 Type::Int32 | Type::Color | Type::UnitProduct(_) => Expression::Cast {
1527 from: Box::new(Expression::NumberLiteral(0., Unit::None)),
1528 to: ty.clone(),
1529 },
1530 Type::Duration => Expression::NumberLiteral(0., Unit::Ms),
1531 Type::Angle => Expression::NumberLiteral(0., Unit::Deg),
1532 Type::PhysicalLength => Expression::NumberLiteral(0., Unit::Phx),
1533 Type::LogicalLength => Expression::NumberLiteral(0., Unit::Px),
1534 Type::Rem => Expression::NumberLiteral(0., Unit::Rem),
1535 Type::Percent => Expression::NumberLiteral(100., Unit::Percent),
1536 Type::Image => Expression::ImageReference {
1537 resource_ref: ImageReference::None,
1538 source_location: None,
1539 nine_slice: None,
1540 },
1541 Type::Bool => Expression::BoolLiteral(false),
1542 Type::Model => Expression::Invalid,
1543 Type::PathData => Expression::PathData(Path::Elements(Vec::new())),
1544 Type::Array(element_ty) => {
1545 Expression::Array { element_ty: (**element_ty).clone(), values: Vec::new() }
1546 }
1547 Type::Struct(s) => Expression::Struct {
1548 ty: s.clone(),
1549 values: s
1550 .fields
1551 .iter()
1552 .map(|(k, v)| (k.clone(), Expression::default_value_for_type(v)))
1553 .collect(),
1554 },
1555 Type::Easing => Expression::EasingCurve(EasingCurve::default()),
1556 Type::Brush => Expression::Cast {
1557 from: Box::new(Expression::default_value_for_type(&Type::Color)),
1558 to: Type::Brush,
1559 },
1560 Type::Enumeration(enumeration) => {
1561 Expression::EnumerationValue(enumeration.clone().default_value())
1562 }
1563 Type::Keys => Expression::Keys(Keys::default()),
1564 Type::ComponentFactory => Expression::EmptyComponentFactory,
1565 Type::StyledText => Expression::FunctionCall {
1566 function: Callable::Builtin(BuiltinFunction::StringToStyledText),
1567 arguments: vec![Self::default_value_for_type(&Type::String)],
1568 source_location: None,
1569 },
1570 }
1571 }
1572
1573 pub fn try_set_rw(
1577 &mut self,
1578 ctx: &mut LookupCtx,
1579 what: &'static str,
1580 node: &dyn Spanned,
1581 ) -> bool {
1582 match self {
1583 Expression::PropertyReference(nr) => {
1584 nr.mark_as_set();
1585 let mut lookup = nr.element().borrow().lookup_property(nr.name());
1586 lookup.is_local_to_component &= ctx.is_local_element(&nr.element());
1587 if lookup.property_visibility == PropertyVisibility::Constexpr {
1588 ctx.diag.push_error(
1589 "The property must be known at compile time and cannot be changed at runtime"
1590 .into(),
1591 node,
1592 );
1593 false
1594 } else if lookup.is_valid_for_assignment() {
1595 if !nr
1596 .element()
1597 .borrow()
1598 .property_analysis
1599 .borrow()
1600 .get(nr.name())
1601 .is_some_and(|d| d.is_linked_to_read_only)
1602 {
1603 true
1604 } else if ctx.is_legacy_component() {
1605 ctx.diag.push_warning("Modifying a property that is linked to a read-only property is deprecated".into(), node);
1606 true
1607 } else {
1608 ctx.diag.push_error(
1609 "Cannot modify a property that is linked to a read-only property"
1610 .into(),
1611 node,
1612 );
1613 false
1614 }
1615 } else if ctx.is_legacy_component()
1616 && lookup.property_visibility == PropertyVisibility::Output
1617 {
1618 ctx.diag
1619 .push_warning(format!("{what} on an output property is deprecated"), node);
1620 true
1621 } else {
1622 ctx.diag.push_error(
1623 format!("{what} on a {} property", lookup.property_visibility),
1624 node,
1625 );
1626 false
1627 }
1628 }
1629 Expression::StructFieldAccess { base, .. } => base.try_set_rw(ctx, what, node),
1630 Expression::RepeaterModelReference { .. } => true,
1631 Expression::ArrayIndex { array, .. } => array.try_set_rw(ctx, what, node),
1632 _ => {
1633 ctx.diag.push_error(format!("{what} needs to be done on a property"), node);
1634 false
1635 }
1636 }
1637 }
1638
1639 pub fn ignore_debug_hooks(&self) -> &Expression {
1641 match self {
1642 Expression::DebugHook { expression, .. } => expression.as_ref(),
1643 _ => self,
1644 }
1645 }
1646}
1647
1648fn model_inner_type(model: &Expression) -> Type {
1649 match model {
1650 Expression::Cast { from, to: Type::Model } => model_inner_type(from),
1651 Expression::CodeBlock(cb) => cb.last().map_or(Type::Invalid, model_inner_type),
1652 _ => match model.ty() {
1653 Type::Float32 | Type::Int32 => Type::Int32,
1654 Type::Array(elem) => (*elem).clone(),
1655 _ => Type::Invalid,
1656 },
1657 }
1658}
1659
1660#[derive(Clone, Debug)]
1662pub struct TwoWayBinding {
1663 pub property: NamedReference,
1665 pub field_access: Vec<SmolStr>,
1668}
1669impl TwoWayBinding {
1670 pub fn ty(&self) -> Type {
1671 let mut ty = self.property.ty();
1672 for x in &self.field_access {
1673 ty = match ty {
1674 Type::InferredProperty | Type::InferredCallback => return ty,
1675 Type::Struct(s) => s.fields.get(x).cloned().unwrap_or_default(),
1676 _ => return Type::Invalid,
1677 };
1678 }
1679 ty
1680 }
1681}
1682
1683impl From<NamedReference> for TwoWayBinding {
1684 fn from(nr: NamedReference) -> Self {
1685 Self { property: nr, field_access: Vec::new() }
1686 }
1687}
1688
1689#[derive(Debug, Clone, derive_more::Deref, derive_more::DerefMut)]
1691pub struct BindingExpression {
1692 #[deref]
1693 #[deref_mut]
1694 pub expression: Expression,
1695 pub span: Option<SourceLocation>,
1697 pub priority: i32,
1702
1703 pub animation: Option<PropertyAnimation>,
1704
1705 pub analysis: Option<BindingAnalysis>,
1707
1708 pub two_way_bindings: Vec<TwoWayBinding>,
1710}
1711
1712impl std::convert::From<Expression> for BindingExpression {
1713 fn from(expression: Expression) -> Self {
1714 Self {
1715 expression,
1716 span: None,
1717 priority: 0,
1718 animation: Default::default(),
1719 analysis: Default::default(),
1720 two_way_bindings: Default::default(),
1721 }
1722 }
1723}
1724
1725impl BindingExpression {
1726 pub fn new_uncompiled(node: SyntaxNode) -> Self {
1727 Self {
1728 expression: Expression::Uncompiled(node.clone()),
1729 span: Some(node.to_source_location()),
1730 priority: 1,
1731 animation: Default::default(),
1732 analysis: Default::default(),
1733 two_way_bindings: Default::default(),
1734 }
1735 }
1736 pub fn new_with_span(expression: Expression, span: SourceLocation) -> Self {
1737 Self {
1738 expression,
1739 span: Some(span),
1740 priority: 0,
1741 animation: Default::default(),
1742 analysis: Default::default(),
1743 two_way_bindings: Default::default(),
1744 }
1745 }
1746
1747 pub fn new_two_way(other: TwoWayBinding) -> Self {
1749 Self {
1750 expression: Expression::Invalid,
1751 span: None,
1752 priority: 0,
1753 animation: Default::default(),
1754 analysis: Default::default(),
1755 two_way_bindings: vec![other],
1756 }
1757 }
1758
1759 pub fn merge_with(&mut self, other: &Self) -> bool {
1767 if self.animation.is_none() {
1768 self.animation.clone_from(&other.animation);
1769 }
1770 let has_binding = self.has_binding();
1771 self.two_way_bindings.extend_from_slice(&other.two_way_bindings);
1772 if !has_binding {
1773 self.priority = other.priority;
1774 self.expression = other.expression.clone();
1775 true
1776 } else {
1777 false
1778 }
1779 }
1780
1781 pub fn has_binding(&self) -> bool {
1783 !matches!(self.expression, Expression::Invalid) || !self.two_way_bindings.is_empty()
1784 }
1785}
1786
1787impl Spanned for BindingExpression {
1788 fn span(&self) -> crate::diagnostics::Span {
1789 self.span.as_ref().map(|x| x.span()).unwrap_or_default()
1790 }
1791 fn source_file(&self) -> Option<&crate::diagnostics::SourceFile> {
1792 self.span.as_ref().and_then(|x| x.source_file())
1793 }
1794}
1795
1796#[derive(Default, Debug, Clone)]
1797pub struct BindingAnalysis {
1798 pub is_in_binding_loop: Cell<bool>,
1800
1801 pub is_const: bool,
1803
1804 pub no_external_dependencies: bool,
1807}
1808
1809#[derive(Debug, Clone)]
1810pub enum Path {
1811 Elements(Vec<PathElement>),
1812 Events(Vec<Expression>, Vec<Expression>),
1813 Commands(Box<Expression>), }
1815
1816#[derive(Debug, Clone)]
1817pub struct PathElement {
1818 pub element_type: Rc<BuiltinElement>,
1819 pub bindings: BindingsMap,
1820}
1821
1822#[derive(Clone, Debug, Default)]
1823pub enum EasingCurve {
1824 #[default]
1825 Linear,
1826 CubicBezier(f32, f32, f32, f32),
1827 EaseInElastic,
1828 EaseOutElastic,
1829 EaseInOutElastic,
1830 EaseInBounce,
1831 EaseOutBounce,
1832 EaseInOutBounce,
1833 }
1836
1837#[derive(Clone, Debug)]
1840pub enum ImageReference {
1841 None,
1842 AbsolutePath(SmolStr),
1843 EmbeddedData { resource_id: crate::embedded_resources::EmbeddedResourcesIdx, extension: String },
1844 EmbeddedTexture { resource_id: crate::embedded_resources::EmbeddedResourcesIdx },
1845}
1846
1847pub fn pretty_print(f: &mut dyn std::fmt::Write, expression: &Expression) -> std::fmt::Result {
1849 match expression {
1850 Expression::Invalid => write!(f, "<invalid>"),
1851 Expression::Uncompiled(u) => write!(f, "{u:?}"),
1852 Expression::StringLiteral(s) => write!(f, "{s:?}"),
1853 Expression::NumberLiteral(vl, unit) => write!(f, "{vl}{unit}"),
1854 Expression::BoolLiteral(b) => write!(f, "{b:?}"),
1855 Expression::PropertyReference(a) => write!(f, "{a:?}"),
1856 Expression::ElementReference(a) => write!(f, "{a:?}"),
1857 Expression::RepeaterIndexReference { element } => {
1858 crate::namedreference::pretty_print_element_ref(f, element)
1859 }
1860 Expression::RepeaterModelReference { element } => {
1861 crate::namedreference::pretty_print_element_ref(f, element)?;
1862 write!(f, ".@model")
1863 }
1864 Expression::FunctionParameterReference { index, ty: _ } => write!(f, "_arg_{index}"),
1865 Expression::StoreLocalVariable { name, value } => {
1866 write!(f, "{name} = ")?;
1867 pretty_print(f, value)
1868 }
1869 Expression::ReadLocalVariable { name, ty: _ } => write!(f, "{name}"),
1870 Expression::StructFieldAccess { base, name } => {
1871 pretty_print(f, base)?;
1872 write!(f, ".{name}")
1873 }
1874 Expression::ArrayIndex { array, index } => {
1875 pretty_print(f, array)?;
1876 write!(f, "[")?;
1877 pretty_print(f, index)?;
1878 write!(f, "]")
1879 }
1880 Expression::Cast { from, to } => {
1881 write!(f, "(")?;
1882 pretty_print(f, from)?;
1883 write!(f, "/* as {to} */)")
1884 }
1885 Expression::CodeBlock(c) => {
1886 write!(f, "{{ ")?;
1887 for e in c {
1888 pretty_print(f, e)?;
1889 write!(f, "; ")?;
1890 }
1891 write!(f, "}}")
1892 }
1893 Expression::FunctionCall { function, arguments, source_location: _ } => {
1894 match function {
1895 Callable::Builtin(b) => write!(f, "{b:?}")?,
1896 Callable::Callback(nr) | Callable::Function(nr) => write!(f, "{nr:?}")?,
1897 }
1898 write!(f, "(")?;
1899 for e in arguments {
1900 pretty_print(f, e)?;
1901 write!(f, ", ")?;
1902 }
1903 write!(f, ")")
1904 }
1905 Expression::SelfAssignment { lhs, rhs, op, .. } => {
1906 pretty_print(f, lhs)?;
1907 write!(f, " {}= ", if *op == '=' { ' ' } else { *op })?;
1908 pretty_print(f, rhs)
1909 }
1910 Expression::BinaryExpression { lhs, rhs, op } => {
1911 write!(f, "(")?;
1912 pretty_print(f, lhs)?;
1913 match *op {
1914 '=' | '!' => write!(f, " {op}= ")?,
1915 _ => write!(f, " {op} ")?,
1916 };
1917 pretty_print(f, rhs)?;
1918 write!(f, ")")
1919 }
1920 Expression::UnaryOp { sub, op } => {
1921 write!(f, "{op}")?;
1922 pretty_print(f, sub)
1923 }
1924 Expression::ImageReference { resource_ref, .. } => write!(f, "{resource_ref:?}"),
1925 Expression::Condition { condition, true_expr, false_expr } => {
1926 write!(f, "if (")?;
1927 pretty_print(f, condition)?;
1928 write!(f, ") {{ ")?;
1929 pretty_print(f, true_expr)?;
1930 write!(f, " }} else {{ ")?;
1931 pretty_print(f, false_expr)?;
1932 write!(f, " }}")
1933 }
1934 Expression::Array { element_ty: _, values } => {
1935 write!(f, "[")?;
1936 for e in values {
1937 pretty_print(f, e)?;
1938 write!(f, ", ")?;
1939 }
1940 write!(f, "]")
1941 }
1942 Expression::Struct { ty: _, values } => {
1943 write!(f, "{{ ")?;
1944 for (name, e) in values {
1945 write!(f, "{name}: ")?;
1946 pretty_print(f, e)?;
1947 write!(f, ", ")?;
1948 }
1949 write!(f, " }}")
1950 }
1951 Expression::PathData(data) => write!(f, "{data:?}"),
1952 Expression::EasingCurve(e) => write!(f, "{e:?}"),
1953 Expression::LinearGradient { angle, stops } => {
1954 write!(f, "@linear-gradient(")?;
1955 pretty_print(f, angle)?;
1956 for (c, s) in stops {
1957 write!(f, ", ")?;
1958 pretty_print(f, c)?;
1959 write!(f, " ")?;
1960 pretty_print(f, s)?;
1961 }
1962 write!(f, ")")
1963 }
1964 Expression::RadialGradient { stops } => {
1965 write!(f, "@radial-gradient(circle")?;
1966 for (c, s) in stops {
1967 write!(f, ", ")?;
1968 pretty_print(f, c)?;
1969 write!(f, " ")?;
1970 pretty_print(f, s)?;
1971 }
1972 write!(f, ")")
1973 }
1974 Expression::ConicGradient { from_angle, stops } => {
1975 write!(f, "@conic-gradient(from ")?;
1976 pretty_print(f, from_angle)?;
1977 for (c, s) in stops {
1978 write!(f, ", ")?;
1979 pretty_print(f, c)?;
1980 write!(f, " ")?;
1981 pretty_print(f, s)?;
1982 }
1983 write!(f, ")")
1984 }
1985 Expression::EnumerationValue(e) => match e.enumeration.values.get(e.value) {
1986 Some(val) => write!(f, "{}.{}", e.enumeration.name, val),
1987 None => write!(f, "{}.{}", e.enumeration.name, e.value),
1988 },
1989 Expression::Keys(keys) => {
1990 write!(f, "@keys({keys})")
1991 }
1992 Expression::ReturnStatement(e) => {
1993 write!(f, "return ")?;
1994 e.as_ref().map(|e| pretty_print(f, e)).unwrap_or(Ok(()))
1995 }
1996 Expression::LayoutCacheAccess {
1997 layout_cache_prop,
1998 index,
1999 repeater_index,
2000 entries_per_item,
2001 } => {
2002 if repeater_index.is_some() {
2003 write!(
2004 f,
2005 "{:?}[{:?}[{}] + $repeater_index * {}]",
2006 layout_cache_prop, layout_cache_prop, index, entries_per_item
2007 )
2008 } else {
2009 write!(f, "{:?}[{}]", layout_cache_prop, index)
2010 }
2011 }
2012 Expression::GridRepeaterCacheAccess {
2013 layout_cache_prop,
2014 index,
2015 repeater_index: _,
2016 stride: _,
2017 child_offset,
2018 inner_repeater_index,
2019 entries_per_item,
2020 } => {
2021 if inner_repeater_index.is_some() {
2022 write!(
2023 f,
2024 "{0:?}[{0:?}[{1}] + $repeater_index * $stride + {2} + $inner_repeater_index * {3}]",
2025 layout_cache_prop, index, child_offset, entries_per_item
2026 )
2027 } else {
2028 write!(
2029 f,
2030 "{0:?}[{0:?}[{1}] + $repeater_index * $stride + {2}]",
2031 layout_cache_prop, index, child_offset
2032 )
2033 }
2034 }
2035 Expression::OrganizeGridLayout(..) => write!(f, "organize_grid_layout(..)"),
2036 Expression::ComputeBoxLayoutInfo(..) => write!(f, "layout_info(..)"),
2037 Expression::ComputeGridLayoutInfo { .. } => write!(f, "grid_layout_info(..)"),
2038 Expression::SolveBoxLayout(..) => write!(f, "solve_box_layout(..)"),
2039 Expression::SolveGridLayout { .. } => write!(f, "solve_grid_layout(..)"),
2040 Expression::SolveFlexboxLayout(..) => write!(f, "solve_flexbox_layout(..)"),
2041 Expression::ComputeFlexboxLayoutInfo(..) => write!(f, "flexbox_layout_info(..)"),
2042 Expression::MinMax { ty: _, op, lhs, rhs } => {
2043 match op {
2044 MinMaxOp::Min => write!(f, "min(")?,
2045 MinMaxOp::Max => write!(f, "max(")?,
2046 }
2047 pretty_print(f, lhs)?;
2048 write!(f, ", ")?;
2049 pretty_print(f, rhs)?;
2050 write!(f, ")")
2051 }
2052 Expression::EmptyComponentFactory => write!(f, "<empty-component-factory>"),
2053 Expression::DebugHook { expression, id } => {
2054 write!(f, "debug-hook(")?;
2055 pretty_print(f, expression)?;
2056 write!(f, "\"{id}\")")
2057 }
2058 }
2059}