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