1use crate::diagnostics::{BuildDiagnostics, SourceLocation, Spanned};
5use crate::langtype::{
6 BuiltinElement, BuiltinStruct, EnumerationValue, Function, Keys, PropertyLookupMode, Struct,
7 Type,
8};
9use crate::layout::Orientation;
10use crate::lookup::LookupCtx;
11use crate::object_tree::*;
12use crate::parser::{NodeOrToken, SyntaxNode};
13use crate::symbol_counters::SymbolCounters;
14use crate::typeregister;
15use core::cell::RefCell;
16use smol_str::{SmolStr, format_smolstr};
17use std::cell::Cell;
18use std::collections::BTreeMap;
19use std::rc::{Rc, Weak};
20use std::sync::Arc;
21
22pub use crate::namedreference::NamedReference;
24pub use crate::passes::resolving;
25
26#[derive(Debug, Clone, PartialEq, Eq, strum::EnumString)]
27pub enum BuiltinFunction {
30 GetWindowScaleFactor,
31 GetWindowDefaultFontSize,
32 AnimationTick,
33 Debug,
34 Mod,
35 Round,
36 Ceil,
37 Floor,
38 Abs,
39 Sqrt,
40 Cos,
41 Sin,
42 Tan,
43 ACos,
44 ASin,
45 ATan,
46 ATan2,
47 Log,
48 Ln,
49 Pow,
50 Exp,
51 ToFixed,
52 ToPrecision,
53 ToStringUnlocalized,
54 SetFocusItem,
55 ClearFocusItem,
56 ShowPopupWindow,
57 ClosePopupWindow,
58 ShowPopupMenu,
65 ShowPopupMenuInternal,
69 SetSelectionOffsets,
70 ItemFontMetrics,
71 StringToFloat,
73 StringIsFloat,
75 StringIsEmpty,
77 StringCharacterCount,
79 StringToLowercase,
80 StringToUppercase,
81 StringStartsWith,
82 StringEndsWith,
83 StringReplaceAll,
84 KeysToString,
85 ColorRgbaStruct,
86 ColorHsvaStruct,
87 ColorOklchStruct,
88 ColorBrighter,
89 ColorDarker,
90 ColorTransparentize,
91 ColorMix,
92 ColorWithAlpha,
93 ImageSize,
94 ArrayLength,
95 ArrayPush,
96 ArrayRemove,
97 ArrayInsert,
98 ArrayAny,
99 ArrayAll,
100 ArrayFindIndex,
101 Rgb,
102 Hsv,
103 Oklch,
104 ColorScheme,
105 AccentColor,
106 SupportsNativeMenuBar,
107 SetupMenuBar,
114 SetupSystemTrayIcon,
119 Use24HourFormat,
120 MonthDayCount,
121 MonthOffset,
122 FormatDate,
123 DateNow,
124 ValidDate,
125 ParseDate,
126 TextInputFocused,
127 SetTextInputFocused,
128 #[strum(disabled)]
129 ImplicitLayoutInfo(Orientation),
130 ItemAbsolutePosition,
131 RegisterCustomFontByPath,
132 RegisterCustomFontByMemory,
133 RegisterBitmapFont,
134 Translate,
135 UpdateTimers,
136 DetectOperatingSystem,
137 StartTimer,
138 StopTimer,
139 RestartTimer,
140 OpenUrl,
141 MacosBringAllWindowsToFront,
142 ParseMarkdown,
143 StringToStyledText,
144 ColorToStyledText,
148 DecimalSeparator,
149 DefaultWindowTitle,
152 PathPointAt,
153 PathAngleAt,
154}
155
156#[derive(Debug, Clone)]
157pub enum BuiltinMacroFunction {
163 Min,
165 Max,
167 Clamp,
169 Mod,
171 Abs,
173 Sign,
175 CubicBezier,
176 Rgb,
179 Hsv,
180 Oklch,
181 Debug,
183 ArrayPush,
184 ArrayRemove,
185 ArrayInsert,
186 ArrayIndexOf,
188 CustomMouseCursor,
189 Spring,
190}
191
192macro_rules! declare_builtin_function_types {
193 ($( $Name:ident $(($Pattern:tt))? : ($( $Arg:expr ),*) -> $ReturnType:expr $(,)? )*) => {
194 #[allow(non_snake_case)]
195 pub struct BuiltinFunctionTypes {
196 $(pub $Name : Arc<Function>),*
197 }
198 impl BuiltinFunctionTypes {
199 pub fn new() -> Self {
200 Self {
201 $($Name : Arc::new(Function{
202 args: vec![$($Arg),*],
203 return_type: $ReturnType,
204 arg_names: Vec::new(),
205 })),*
206 }
207 }
208
209 pub fn ty(&self, function: &BuiltinFunction) -> Arc<Function> {
210 match function {
211 $(BuiltinFunction::$Name $(($Pattern))? => self.$Name.clone()),*
212 }
213 }
214 }
215 };
216}
217
218declare_builtin_function_types!(
219 GetWindowScaleFactor: () -> Type::UnitProduct(vec![(Unit::Phx, 1), (Unit::Px, -1)]),
220 GetWindowDefaultFontSize: () -> Type::LogicalLength,
221 AnimationTick: () -> Type::Duration,
222 Debug: (Type::String) -> Type::Void,
223 Mod: (Type::Int32, Type::Int32) -> Type::Int32,
224 Round: (Type::Float32) -> Type::Int32,
225 Ceil: (Type::Float32) -> Type::Int32,
226 Floor: (Type::Float32) -> Type::Int32,
227 Sqrt: (Type::Float32) -> Type::Float32,
228 Abs: (Type::Float32) -> Type::Float32,
229 Cos: (Type::Angle) -> Type::Float32,
230 Sin: (Type::Angle) -> Type::Float32,
231 Tan: (Type::Angle) -> Type::Float32,
232 ACos: (Type::Float32) -> Type::Angle,
233 ASin: (Type::Float32) -> Type::Angle,
234 ATan: (Type::Float32) -> Type::Angle,
235 ATan2: (Type::Float32, Type::Float32) -> Type::Angle,
236 DecimalSeparator: () -> Type::String,
237 DefaultWindowTitle: () -> Type::String,
238 Log: (Type::Float32, Type::Float32) -> Type::Float32,
239 Ln: (Type::Float32) -> Type::Float32,
240 Pow: (Type::Float32, Type::Float32) -> Type::Float32,
241 Exp: (Type::Float32) -> Type::Float32,
242 ToFixed: (Type::Float32, Type::Int32) -> Type::String,
243 ToPrecision: (Type::Float32, Type::Int32) -> Type::String,
244 ToStringUnlocalized: (Type::Float32) -> Type::String,
245 SetFocusItem: (Type::ElementReference) -> Type::Void,
246 ClearFocusItem: (Type::ElementReference) -> Type::Void,
247 ShowPopupWindow: (Type::ElementReference) -> Type::Void,
248 ClosePopupWindow: (Type::ElementReference) -> Type::Void,
249 ShowPopupMenu: (Type::ElementReference, Type::ElementReference, typeregister::logical_point_type().into()) -> Type::Void,
250 ShowPopupMenuInternal: (Type::ElementReference, Type::Model, typeregister::logical_point_type().into()) -> Type::Void,
251 SetSelectionOffsets: (Type::ElementReference, Type::Int32, Type::Int32) -> Type::Void,
252 ItemFontMetrics: (Type::ElementReference) -> typeregister::font_metrics_type(),
253 StringToFloat: (Type::String) -> Type::Float32,
254 StringIsFloat: (Type::String) -> Type::Bool,
255 StringIsEmpty: (Type::String) -> Type::Bool,
256 StringCharacterCount: (Type::String) -> Type::Int32,
257 StringToLowercase: (Type::String) -> Type::String,
258 StringToUppercase: (Type::String) -> Type::String,
259 StringStartsWith: (Type::String, Type::String) -> Type::Bool,
260 StringEndsWith: (Type::String, Type::String) -> Type::Bool,
261 StringReplaceAll: (Type::String, Type::String, Type::String) -> Type::String,
262 KeysToString: (Type::Keys) -> Type::String,
263 ImplicitLayoutInfo(..): (Type::ElementReference, Type::Float32) -> typeregister::layout_info_type().into(),
264 ColorRgbaStruct: (Type::Color) -> Type::Struct(Arc::new(Struct::new(IntoIterator::into_iter([
265 (SmolStr::new_static("red"), Type::Int32),
266 (SmolStr::new_static("green"), Type::Int32),
267 (SmolStr::new_static("blue"), Type::Int32),
268 (SmolStr::new_static("alpha"), Type::Int32),
269 ])
270 .collect(), BuiltinStruct::Color))),
271 ColorHsvaStruct: (Type::Color) -> Type::Struct(Arc::new(Struct::new(IntoIterator::into_iter([
272 (SmolStr::new_static("hue"), Type::Float32),
273 (SmolStr::new_static("saturation"), Type::Float32),
274 (SmolStr::new_static("value"), Type::Float32),
275 (SmolStr::new_static("alpha"), Type::Float32),
276 ])
277 .collect(), BuiltinStruct::Color))),
278 ColorOklchStruct: (Type::Color) -> Type::Struct(Arc::new(Struct::new(IntoIterator::into_iter([
279 (SmolStr::new_static("lightness"), Type::Float32),
280 (SmolStr::new_static("chroma"), Type::Float32),
281 (SmolStr::new_static("hue"), Type::Float32),
282 (SmolStr::new_static("alpha"), Type::Float32),
283 ])
284 .collect(), BuiltinStruct::Color))),
285 ColorBrighter: (Type::Brush, Type::Float32) -> Type::Brush,
286 ColorDarker: (Type::Brush, Type::Float32) -> Type::Brush,
287 ColorTransparentize: (Type::Brush, Type::Float32) -> Type::Brush,
288 ColorWithAlpha: (Type::Brush, Type::Float32) -> Type::Brush,
289 ColorMix: (Type::Color, Type::Color, Type::Float32) -> Type::Color,
290 ImageSize: (Type::Image) -> Type::Struct(Arc::new(Struct::new(IntoIterator::into_iter([
291 (SmolStr::new_static("width"), Type::Int32),
292 (SmolStr::new_static("height"), Type::Int32),
293 ])
294 .collect(), crate::langtype::BuiltinStruct::Size))),
295 ArrayLength: (Type::Model) -> Type::Int32,
296 ArrayPush: (Type::Model, Type::InferredProperty) -> Type::Void,
298 ArrayRemove: (Type::Model, Type::Int32) -> Type::Void,
299 ArrayInsert: (Type::Model, Type::Int32, Type::InferredProperty) -> Type::Void,
300 ArrayAny: (Type::Model, Type::Closure) -> Type::Bool,
301 ArrayAll: (Type::Model, Type::Closure) -> Type::Bool,
302 ArrayFindIndex: (Type::Model, Type::Closure) -> Type::Int32,
303 Rgb: (Type::Int32, Type::Int32, Type::Int32, Type::Float32) -> Type::Color,
304 Hsv: (Type::Float32, Type::Float32, Type::Float32, Type::Float32) -> Type::Color,
305 Oklch: (Type::Float32, Type::Float32, Type::Float32, Type::Float32) -> Type::Color,
306 ColorScheme: () -> Type::Enumeration(
307 typeregister::BUILTIN.enums.ColorScheme.clone(),
308 ),
309 AccentColor: () -> Type::Color,
310 SupportsNativeMenuBar: () -> Type::Bool,
311 SetupMenuBar: (Type::Model, typeregister::noarg_callback_type(), typeregister::noarg_callback_type()) -> Type::Void,
313 SetupSystemTrayIcon: (Type::ElementReference, Type::ElementReference) -> Type::Void,
315 MonthDayCount: (Type::Int32, Type::Int32) -> Type::Int32,
316 MonthOffset: (Type::Int32, Type::Int32) -> Type::Int32,
317 FormatDate: (Type::String, Type::Int32, Type::Int32, Type::Int32) -> Type::String,
318 TextInputFocused: () -> Type::Bool,
319 DateNow: () -> Type::Array(Arc::new(Type::Int32)),
320 ValidDate: (Type::String, Type::String) -> Type::Bool,
321 ParseDate: (Type::String, Type::String) -> Type::Array(Arc::new(Type::Int32)),
322 SetTextInputFocused: (Type::Bool) -> Type::Void,
323 ItemAbsolutePosition: (Type::ElementReference) -> typeregister::logical_point_type().into(),
324 RegisterCustomFontByPath: (Type::String) -> Type::Void,
325 RegisterCustomFontByMemory: (Type::Int32) -> Type::Void,
326 RegisterBitmapFont: (Type::Int32) -> Type::Void,
327 Translate: (Type::String, Type::String, Type::String, Type::Array(Type::String.into())) -> Type::String,
329 Use24HourFormat: () -> Type::Bool,
330 UpdateTimers: () -> Type::Void,
331 DetectOperatingSystem: () -> Type::Enumeration(
332 typeregister::BUILTIN.enums.OperatingSystemType.clone(),
333 ),
334 StartTimer: (Type::ElementReference) -> Type::Void,
335 StopTimer: (Type::ElementReference) -> Type::Void,
336 RestartTimer: (Type::ElementReference) -> Type::Void,
337 ParseMarkdown: (Type::String, Type::Array(Type::StyledText.into())) -> Type::StyledText,
338 StringToStyledText: (Type::String) -> Type::StyledText,
339 ColorToStyledText: (Type::Color) -> Type::StyledText
340 OpenUrl: (Type::String) -> Type::Bool,
341 MacosBringAllWindowsToFront: () -> Type::Void,
342 PathPointAt: (Type::ElementReference, Type::Float32) -> typeregister::logical_point_type().into(),
343 PathAngleAt: (Type::ElementReference, Type::Float32) -> Type::Angle,
344);
345
346impl Default for BuiltinFunctionTypes {
347 fn default() -> Self {
348 Self::new()
349 }
350}
351
352impl BuiltinFunction {
353 pub fn ty(&self) -> Arc<Function> {
354 static TYPES: std::sync::LazyLock<BuiltinFunctionTypes> =
355 std::sync::LazyLock::new(BuiltinFunctionTypes::new);
356 TYPES.ty(self)
357 }
358
359 fn is_const(&self, global_analysis: Option<&crate::passes::GlobalAnalysis>) -> bool {
361 match self {
362 BuiltinFunction::GetWindowScaleFactor => {
363 global_analysis.is_some_and(|x| x.const_scale_factor.is_some())
364 }
365 BuiltinFunction::GetWindowDefaultFontSize => {
366 global_analysis.is_some_and(|x| x.default_font_size.is_const())
367 }
368 BuiltinFunction::AnimationTick => false,
369 BuiltinFunction::ColorScheme => false,
370 BuiltinFunction::AccentColor => false,
371 BuiltinFunction::SupportsNativeMenuBar => false,
372 BuiltinFunction::SetupMenuBar => false,
373 BuiltinFunction::SetupSystemTrayIcon => false,
374 BuiltinFunction::MonthDayCount => false,
375 BuiltinFunction::MonthOffset => false,
376 BuiltinFunction::FormatDate => false,
377 BuiltinFunction::DateNow => false,
378 BuiltinFunction::ValidDate => false,
379 BuiltinFunction::ParseDate => false,
380 BuiltinFunction::DecimalSeparator => false,
381 BuiltinFunction::DefaultWindowTitle => false,
382 BuiltinFunction::Debug => true,
384 BuiltinFunction::Mod
385 | BuiltinFunction::Round
386 | BuiltinFunction::Ceil
387 | BuiltinFunction::Floor
388 | BuiltinFunction::Abs
389 | BuiltinFunction::Sqrt
390 | BuiltinFunction::Cos
391 | BuiltinFunction::Sin
392 | BuiltinFunction::Tan
393 | BuiltinFunction::ACos
394 | BuiltinFunction::ASin
395 | BuiltinFunction::Log
396 | BuiltinFunction::Ln
397 | BuiltinFunction::Pow
398 | BuiltinFunction::Exp
399 | BuiltinFunction::ATan
400 | BuiltinFunction::ATan2
401 | BuiltinFunction::ToStringUnlocalized => true,
402 BuiltinFunction::ToFixed
406 | BuiltinFunction::ToPrecision
407 | BuiltinFunction::StringToFloat
408 | BuiltinFunction::StringIsFloat => false,
409 BuiltinFunction::SetFocusItem | BuiltinFunction::ClearFocusItem => false,
410 BuiltinFunction::ShowPopupWindow
411 | BuiltinFunction::ClosePopupWindow
412 | BuiltinFunction::ShowPopupMenu
413 | BuiltinFunction::ShowPopupMenuInternal => false,
414 BuiltinFunction::SetSelectionOffsets => false,
415 BuiltinFunction::ItemFontMetrics => false, BuiltinFunction::StringIsEmpty
417 | BuiltinFunction::StringCharacterCount
418 | BuiltinFunction::StringToLowercase
419 | BuiltinFunction::StringToUppercase
420 | BuiltinFunction::StringStartsWith
421 | BuiltinFunction::StringEndsWith
422 | BuiltinFunction::StringReplaceAll
423 | BuiltinFunction::KeysToString => true,
424 BuiltinFunction::ColorRgbaStruct
425 | BuiltinFunction::ColorHsvaStruct
426 | BuiltinFunction::ColorOklchStruct
427 | BuiltinFunction::ColorBrighter
428 | BuiltinFunction::ColorDarker
429 | BuiltinFunction::ColorTransparentize
430 | BuiltinFunction::ColorMix
431 | BuiltinFunction::ColorWithAlpha => true,
432 BuiltinFunction::ImageSize => global_analysis.is_some_and(|x| x.const_image_sizes),
436 BuiltinFunction::ArrayLength => true,
437 BuiltinFunction::ArrayPush
438 | BuiltinFunction::ArrayRemove
439 | BuiltinFunction::ArrayInsert => false,
440 BuiltinFunction::Rgb => true,
441 BuiltinFunction::Hsv => true,
442 BuiltinFunction::Oklch => true,
443 BuiltinFunction::SetTextInputFocused => false,
444 BuiltinFunction::TextInputFocused => false,
445 BuiltinFunction::ImplicitLayoutInfo(_) => false,
446 BuiltinFunction::ItemAbsolutePosition => true,
447 BuiltinFunction::RegisterCustomFontByPath
448 | BuiltinFunction::RegisterCustomFontByMemory
449 | BuiltinFunction::RegisterBitmapFont => false,
450 BuiltinFunction::Translate => false,
451 BuiltinFunction::Use24HourFormat => false,
452 BuiltinFunction::UpdateTimers => false,
453 BuiltinFunction::DetectOperatingSystem => true,
454 BuiltinFunction::StartTimer => false,
455 BuiltinFunction::StopTimer => false,
456 BuiltinFunction::RestartTimer => false,
457 BuiltinFunction::ParseMarkdown => false,
458 BuiltinFunction::StringToStyledText => true,
459 BuiltinFunction::ColorToStyledText => true,
460 BuiltinFunction::OpenUrl => false,
461 BuiltinFunction::MacosBringAllWindowsToFront => false,
462 BuiltinFunction::PathPointAt => true,
463 BuiltinFunction::PathAngleAt => true,
464 BuiltinFunction::ArrayAny
465 | BuiltinFunction::ArrayAll
466 | BuiltinFunction::ArrayFindIndex => true,
467 }
468 }
469
470 pub fn is_pure(&self) -> bool {
472 match self {
473 BuiltinFunction::GetWindowScaleFactor => true,
474 BuiltinFunction::GetWindowDefaultFontSize => true,
475 BuiltinFunction::AnimationTick => true,
476 BuiltinFunction::ColorScheme => true,
477 BuiltinFunction::AccentColor => true,
478 BuiltinFunction::SupportsNativeMenuBar => true,
479 BuiltinFunction::SetupMenuBar => false,
480 BuiltinFunction::SetupSystemTrayIcon => false,
481 BuiltinFunction::MonthDayCount => true,
482 BuiltinFunction::MonthOffset => true,
483 BuiltinFunction::FormatDate => true,
484 BuiltinFunction::DateNow => true,
485 BuiltinFunction::ValidDate => true,
486 BuiltinFunction::ParseDate => true,
487 BuiltinFunction::DecimalSeparator => true,
488 BuiltinFunction::DefaultWindowTitle => true,
489 BuiltinFunction::Debug => true,
491 BuiltinFunction::Mod
492 | BuiltinFunction::Round
493 | BuiltinFunction::Ceil
494 | BuiltinFunction::Floor
495 | BuiltinFunction::Abs
496 | BuiltinFunction::Sqrt
497 | BuiltinFunction::Cos
498 | BuiltinFunction::Sin
499 | BuiltinFunction::Tan
500 | BuiltinFunction::ACos
501 | BuiltinFunction::ASin
502 | BuiltinFunction::Log
503 | BuiltinFunction::Ln
504 | BuiltinFunction::Pow
505 | BuiltinFunction::Exp
506 | BuiltinFunction::ATan
507 | BuiltinFunction::ATan2
508 | BuiltinFunction::ToFixed
509 | BuiltinFunction::ToPrecision
510 | BuiltinFunction::ToStringUnlocalized => true,
511 BuiltinFunction::SetFocusItem | BuiltinFunction::ClearFocusItem => false,
512 BuiltinFunction::ShowPopupWindow
513 | BuiltinFunction::ClosePopupWindow
514 | BuiltinFunction::ShowPopupMenu
515 | BuiltinFunction::ShowPopupMenuInternal => false,
516 BuiltinFunction::SetSelectionOffsets => false,
517 BuiltinFunction::ItemFontMetrics => true,
518 BuiltinFunction::StringToFloat
519 | BuiltinFunction::StringIsFloat
520 | BuiltinFunction::StringIsEmpty
521 | BuiltinFunction::StringCharacterCount
522 | BuiltinFunction::StringToLowercase
523 | BuiltinFunction::StringToUppercase
524 | BuiltinFunction::StringStartsWith
525 | BuiltinFunction::StringEndsWith
526 | BuiltinFunction::StringReplaceAll
527 | BuiltinFunction::KeysToString => true,
528 BuiltinFunction::ColorRgbaStruct
529 | BuiltinFunction::ColorHsvaStruct
530 | BuiltinFunction::ColorOklchStruct
531 | BuiltinFunction::ColorBrighter
532 | BuiltinFunction::ColorDarker
533 | BuiltinFunction::ColorTransparentize
534 | BuiltinFunction::ColorMix
535 | BuiltinFunction::ColorWithAlpha => true,
536 BuiltinFunction::ImageSize => true,
537 BuiltinFunction::ArrayLength => true,
538 BuiltinFunction::ArrayPush
539 | BuiltinFunction::ArrayRemove
540 | BuiltinFunction::ArrayInsert => false,
541 BuiltinFunction::Rgb => true,
542 BuiltinFunction::Hsv => true,
543 BuiltinFunction::Oklch => true,
544 BuiltinFunction::ImplicitLayoutInfo(_) => true,
545 BuiltinFunction::ItemAbsolutePosition => true,
546 BuiltinFunction::SetTextInputFocused => false,
547 BuiltinFunction::TextInputFocused => true,
548 BuiltinFunction::RegisterCustomFontByPath
549 | BuiltinFunction::RegisterCustomFontByMemory
550 | BuiltinFunction::RegisterBitmapFont => false,
551 BuiltinFunction::Translate => true,
552 BuiltinFunction::Use24HourFormat => true,
553 BuiltinFunction::UpdateTimers => false,
554 BuiltinFunction::DetectOperatingSystem => true,
555 BuiltinFunction::StartTimer => false,
556 BuiltinFunction::StopTimer => false,
557 BuiltinFunction::RestartTimer => false,
558 BuiltinFunction::ParseMarkdown => true,
559 BuiltinFunction::StringToStyledText => true,
560 BuiltinFunction::ColorToStyledText => true,
561 BuiltinFunction::OpenUrl => false,
562 BuiltinFunction::MacosBringAllWindowsToFront => false,
563 BuiltinFunction::PathPointAt => true,
564 BuiltinFunction::PathAngleAt => true,
565 BuiltinFunction::ArrayAny
566 | BuiltinFunction::ArrayAll
567 | BuiltinFunction::ArrayFindIndex => true,
568 }
569 }
570}
571
572#[derive(Debug, Clone)]
574pub enum Callable {
575 Callback(NamedReference),
576 Function(NamedReference),
577 Builtin(BuiltinFunction),
578}
579impl Callable {
580 pub fn ty(&self) -> Type {
581 match self {
582 Callable::Callback(nr) => nr.ty(),
583 Callable::Function(nr) => nr.ty(),
584 Callable::Builtin(b) => Type::Function(b.ty()),
585 }
586 }
587}
588impl From<BuiltinFunction> for Callable {
589 fn from(function: BuiltinFunction) -> Self {
590 Self::Builtin(function)
591 }
592}
593
594#[derive(Debug, Clone, Eq, PartialEq)]
595pub enum OperatorClass {
596 ComparisonOp,
597 LogicalOp,
598 ArithmeticOp,
599}
600
601pub fn operator_class(op: char) -> OperatorClass {
603 match op {
604 '=' | '!' | '<' | '>' | '≤' | '≥' => OperatorClass::ComparisonOp,
605 '&' | '|' => OperatorClass::LogicalOp,
606 '+' | '-' | '/' | '*' => OperatorClass::ArithmeticOp,
607 _ => panic!("Invalid operator {op:?}"),
608 }
609}
610
611macro_rules! declare_units {
612 (@normalize $value:ident, $ident:ident) => { ($value, Unit::$ident) };
614 (@normalize $value:ident, $ident:ident, $canon:ident, $factor:expr) => {
616 ($value * ($factor as f64), Unit::$canon)
617 };
618 ($( $(#[$m:meta])* $ident:ident = $string:literal $(-> $canon:ident * $factor:expr)? ,)*) => {
619 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, strum::EnumIter)]
625 pub enum WrittenUnit {
626 $($(#[$m])* $ident,)*
627 }
628
629 impl std::fmt::Display for WrittenUnit {
630 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
631 match self {
632 $(Self::$ident => write!(f, $string), )*
633 }
634 }
635 }
636
637 impl std::str::FromStr for WrittenUnit {
638 type Err = ();
639 fn from_str(s: &str) -> Result<Self, Self::Err> {
640 match s {
641 $($string => Ok(Self::$ident), )*
642 _ => Err(())
643 }
644 }
645 }
646
647 impl WrittenUnit {
648 pub fn normalize(self, value: f64) -> (f64, Unit) {
652 match self {
653 $(Self::$ident => declare_units!(@normalize value, $ident $(, $canon, $factor)?), )*
654 }
655 }
656 }
657 };
658}
659
660declare_units! {
661 None = "",
663 Percent = "%",
665
666 Phx = "phx",
670 Px = "px",
672 Cm = "cm" -> Px * 37.8,
674 Mm = "mm" -> Px * 3.78,
676 In = "in" -> Px * 96,
678 Pt = "pt" -> Px * 96./72.,
680 Rem = "rem",
682
683 S = "s" -> Ms * 1000,
687 Ms = "ms",
689
690 Deg = "deg",
694 Grad = "grad" -> Deg * 360./180.,
696 Turn = "turn" -> Deg * 360.,
698 Rad = "rad" -> Deg * 360./std::f32::consts::TAU,
700}
701
702#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
707pub enum Unit {
708 #[default]
710 None,
711 Percent,
713 Phx,
715 Px,
717 Rem,
719 Ms,
721 Deg,
723}
724
725impl Unit {
726 pub fn ty(self) -> Type {
727 match self {
728 Unit::None => Type::Float32,
729 Unit::Percent => Type::Percent,
730 Unit::Px => Type::LogicalLength,
731 Unit::Phx => Type::PhysicalLength,
732 Unit::Rem => Type::Rem,
733 Unit::Ms => Type::Duration,
734 Unit::Deg => Type::Angle,
735 }
736 }
737}
738
739impl std::fmt::Display for Unit {
740 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
741 let s = match self {
742 Unit::None => "",
743 Unit::Percent => "%",
744 Unit::Px => "px",
745 Unit::Phx => "phx",
746 Unit::Rem => "rem",
747 Unit::Ms => "ms",
748 Unit::Deg => "deg",
749 };
750 write!(f, "{s}")
751 }
752}
753
754#[derive(Debug, Clone, Copy)]
755pub enum MinMaxOp {
756 Min,
757 Max,
758}
759
760#[derive(Debug, Clone)]
762pub enum ConditionLocation {
763 Question(SourceLocation),
765 StateSelection { name: SourceLocation, when: SourceLocation },
767 StateChange(SourceLocation),
769}
770
771#[derive(Debug, Clone, Default)]
773pub enum Expression {
774 #[default]
776 Invalid,
777 Uncompiled(SyntaxNode),
779
780 StringLiteral(SmolStr),
782 NumberLiteral(f64, Unit),
784 BoolLiteral(bool),
786
787 PropertyReference(NamedReference),
789
790 ElementReference(Weak<RefCell<Element>>),
793
794 RepeaterIndexReference {
799 element: Weak<RefCell<Element>>,
800 },
801
802 RepeaterModelReference {
807 element: Weak<RefCell<Element>>,
808 },
809
810 FunctionParameterReference {
812 index: usize,
813 ty: Type,
814 },
815
816 StoreLocalVariable {
818 name: SmolStr,
819 value: Box<Expression>,
820 },
821
822 ReadLocalVariable {
825 name: SmolStr,
826 ty: Type,
827 },
828
829 StructFieldAccess {
831 base: Box<Expression>,
833 name: SmolStr,
834 },
835
836 ArrayIndex {
838 array: Box<Expression>,
840 index: Box<Expression>,
841 },
842
843 Cast {
845 from: Box<Expression>,
846 to: Type,
847 },
848
849 CodeBlock(Vec<Expression>),
851
852 FunctionCall {
854 function: Callable,
855 arguments: Vec<Expression>,
856 source_location: Option<SourceLocation>,
857 },
858
859 SelfAssignment {
861 lhs: Box<Expression>,
862 rhs: Box<Expression>,
863 op: char,
865 node: Option<NodeOrToken>,
866 },
867
868 BinaryExpression {
869 lhs: Box<Expression>,
870 rhs: Box<Expression>,
871 op: char,
873 source_location: Option<SourceLocation>,
875 },
876
877 UnaryOp {
878 sub: Box<Expression>,
879 op: char,
881 },
882
883 ImageReference {
884 resource_ref: ImageReference,
885 source_location: Option<SourceLocation>,
886 nine_slice: Option<[u16; 4]>,
887 },
888
889 Condition {
890 condition: Box<Expression>,
891 true_expr: Box<Expression>,
892 false_expr: Box<Expression>,
893 source_location: Option<ConditionLocation>,
895 },
896
897 Array {
898 element_ty: Type,
899 values: Vec<Expression>,
900 },
901 Struct {
902 ty: Arc<Struct>,
903 values: BTreeMap<SmolStr, Expression>,
904 },
905
906 PathData(Path),
907
908 EasingCurve(EasingCurve),
909
910 EmptyDataTransfer,
911
912 MouseCursor(MouseCursorInner),
913
914 LinearGradient {
915 angle: Box<Expression>,
916 stops: Vec<(Expression, Expression)>,
918 },
919
920 RadialGradient {
921 center: Option<(Box<Expression>, Box<Expression>)>,
924 radius: Option<Box<Expression>>,
927 stops: Vec<(Expression, Expression)>,
929 },
930
931 ConicGradient {
932 from_angle: Box<Expression>,
934 center: Option<(Box<Expression>, Box<Expression>)>,
937 stops: Vec<(Expression, Expression)>,
939 },
940
941 EnumerationValue(EnumerationValue),
942
943 Keys(Keys),
944
945 ReturnStatement(Option<Box<Expression>>),
946
947 LayoutCacheAccess {
949 layout_cache_prop: NamedReference,
951 index: usize,
953 repeater_index: Option<Box<Expression>>,
957 entries_per_item: usize,
961 },
962
963 GridRepeaterCacheAccess {
965 layout_cache_prop: NamedReference,
967 index: usize,
969 repeater_index: Box<Expression>,
971 stride: Box<Expression>,
975 child_offset: usize,
977 inner_repeater_index: Option<Box<Expression>>,
979 entries_per_item: usize,
981 },
982
983 OrganizeGridLayout(crate::layout::GridLayout),
985
986 ComputeBoxLayoutInfo {
989 layout: crate::layout::BoxLayout,
990 orientation: crate::layout::Orientation,
991 cross_axis_size: Option<Box<Expression>>,
993 },
994 ComputeGridLayoutInfo {
995 layout_organized_data_prop: NamedReference,
996 layout: crate::layout::GridLayout,
997 orientation: crate::layout::Orientation,
998 cross_axis_size: Option<Box<Expression>>,
1000 },
1001 SolveBoxLayout(crate::layout::BoxLayout, crate::layout::Orientation),
1003 SolveGridLayout {
1004 layout_organized_data_prop: NamedReference,
1005 layout: crate::layout::GridLayout,
1006 orientation: crate::layout::Orientation,
1007 },
1008 SolveFlexboxLayout(crate::layout::FlexboxLayout),
1010 ComputeFlexboxLayoutInfo {
1012 layout: crate::layout::FlexboxLayout,
1013 orientation: crate::layout::Orientation,
1014 cross_axis_size: Option<Box<Expression>>,
1017 },
1018
1019 MinMax {
1020 ty: Type,
1021 op: MinMaxOp,
1022 lhs: Box<Expression>,
1023 rhs: Box<Expression>,
1024 },
1025
1026 DebugHook {
1027 expression: Box<Expression>,
1028 id: SmolStr,
1029 synthetic: bool,
1032 },
1033
1034 EmptyComponentFactory,
1035
1036 Closure {
1037 arg_name: SmolStr,
1038 expression: Box<Expression>,
1039 },
1040}
1041
1042impl Expression {
1043 pub fn ty(&self) -> Type {
1045 match self {
1046 Expression::Invalid => Type::Invalid,
1047 Expression::Uncompiled(_) => Type::Invalid,
1048 Expression::StringLiteral(_) => Type::String,
1049 Expression::NumberLiteral(_, unit) => unit.ty(),
1050 Expression::BoolLiteral(_) => Type::Bool,
1051 Expression::PropertyReference(nr) => nr.ty(),
1052 Expression::ElementReference(_) => Type::ElementReference,
1053 Expression::RepeaterIndexReference { .. } => Type::Int32,
1054 Expression::RepeaterModelReference { element } => element
1055 .upgrade()
1056 .unwrap()
1057 .borrow()
1058 .repeated
1059 .as_ref()
1060 .map_or(Type::Invalid, |e| model_inner_type(&e.model)),
1061 Expression::FunctionParameterReference { ty, .. } => ty.clone(),
1062 Expression::StructFieldAccess { base, name } => match base.ty() {
1063 Type::Struct(s) => s.fields.get(name.as_str()).unwrap_or(&Type::Invalid).clone(),
1064 _ => Type::Invalid,
1065 },
1066 Expression::ArrayIndex { array, .. } => match array.ty() {
1067 Type::Array(ty) => (*ty).clone(),
1068 _ => Type::Invalid,
1069 },
1070 Expression::Cast { to, .. } => to.clone(),
1071 Expression::CodeBlock(sub) => sub.last().map_or(Type::Void, |e| e.ty()),
1072 Expression::FunctionCall { function, .. } => match function.ty() {
1073 Type::Function(f) | Type::Callback(f) => f.return_type.clone(),
1074 _ => Type::Invalid,
1075 },
1076 Expression::SelfAssignment { .. } => Type::Void,
1077 Expression::ImageReference { .. } => Type::Image,
1078 Expression::Condition { condition: _, true_expr, false_expr, .. } => {
1079 let true_type = true_expr.ty();
1080 let false_type = false_expr.ty();
1081 if true_type == false_type {
1082 true_type
1083 } else if true_type == Type::Invalid {
1084 false_type
1085 } else if false_type == Type::Invalid {
1086 true_type
1087 } else {
1088 Type::Void
1089 }
1090 }
1091 Expression::BinaryExpression { op, lhs, rhs, .. } => {
1092 if operator_class(*op) != OperatorClass::ArithmeticOp {
1093 Type::Bool
1094 } else if *op == '+' || *op == '-' {
1095 let (rhs_ty, lhs_ty) = (rhs.ty(), lhs.ty());
1096 if rhs_ty == lhs_ty { rhs_ty } else { Type::Invalid }
1097 } else {
1098 debug_assert!(*op == '*' || *op == '/');
1099 let unit_vec = |ty| {
1100 if let Type::UnitProduct(v) = ty {
1101 v
1102 } else if let Some(u) = ty.default_unit() {
1103 vec![(u, 1)]
1104 } else {
1105 Vec::new()
1106 }
1107 };
1108 let mut l_units = unit_vec(lhs.ty());
1109 let mut r_units = unit_vec(rhs.ty());
1110 if *op == '/' {
1111 for (_, power) in &mut r_units {
1112 *power = -*power;
1113 }
1114 }
1115 for (unit, power) in r_units {
1116 if let Some((_, p)) = l_units.iter_mut().find(|(u, _)| *u == unit) {
1117 *p += power;
1118 } else {
1119 l_units.push((unit, power));
1120 }
1121 }
1122
1123 l_units.retain(|(_, p)| *p != 0);
1125 l_units.sort_unstable_by(|(u1, p1), (u2, p2)| match p2.cmp(p1) {
1126 std::cmp::Ordering::Equal => u1.cmp(u2),
1127 x => x,
1128 });
1129
1130 if l_units.is_empty() {
1131 Type::Float32
1132 } else if l_units.len() == 1 && l_units[0].1 == 1 {
1133 l_units[0].0.ty()
1134 } else {
1135 Type::UnitProduct(l_units)
1136 }
1137 }
1138 }
1139 Expression::UnaryOp { sub, .. } => sub.ty(),
1140 Expression::Array { element_ty, .. } => Type::Array(Arc::new(element_ty.clone())),
1141 Expression::Struct { ty, .. } => ty.clone().into(),
1142 Expression::PathData { .. } => Type::PathData,
1143 Expression::EmptyDataTransfer => Type::DataTransfer,
1144 Expression::StoreLocalVariable { .. } => Type::Void,
1145 Expression::ReadLocalVariable { ty, .. } => ty.clone(),
1146 Expression::EasingCurve(_) => Type::Easing,
1147 Expression::MouseCursor(_) => Type::MouseCursor,
1148 Expression::LinearGradient { .. } => Type::Brush,
1149 Expression::RadialGradient { .. } => Type::Brush,
1150 Expression::ConicGradient { .. } => Type::Brush,
1151 Expression::EnumerationValue(value) => Type::Enumeration(value.enumeration.clone()),
1152 Expression::Keys(_) => Type::Keys,
1153 Expression::ReturnStatement(_) => Type::Invalid,
1155 Expression::LayoutCacheAccess { .. } => Type::LogicalLength,
1156 Expression::GridRepeaterCacheAccess { .. } => Type::LogicalLength,
1157 Expression::OrganizeGridLayout(..) => Type::ArrayOfU16,
1158 Expression::ComputeBoxLayoutInfo { .. } => typeregister::layout_info_type().into(),
1159 Expression::ComputeGridLayoutInfo { .. } => typeregister::layout_info_type().into(),
1160 Expression::SolveBoxLayout(..) => Type::LayoutCache,
1161 Expression::SolveGridLayout { .. } => Type::LayoutCache,
1162 Expression::SolveFlexboxLayout(..) => Type::LayoutCache,
1163 Expression::ComputeFlexboxLayoutInfo { .. } => typeregister::layout_info_type().into(),
1164 Expression::MinMax { ty, .. } => ty.clone(),
1165 Expression::EmptyComponentFactory => Type::ComponentFactory,
1166 Expression::DebugHook { expression, .. } => expression.ty(),
1167 Expression::Closure { .. } => Type::Closure,
1168 }
1169 }
1170
1171 pub fn visit(&self, mut visitor: impl FnMut(&Self)) {
1173 self.visit_dyn(&mut visitor)
1174 }
1175
1176 fn visit_dyn(&self, visitor: &mut dyn FnMut(&Self)) {
1177 match self {
1178 Expression::Invalid => {}
1179 Expression::Uncompiled(_) => {}
1180 Expression::StringLiteral(_) => {}
1181 Expression::NumberLiteral(_, _) => {}
1182 Expression::BoolLiteral(_) => {}
1183 Expression::PropertyReference { .. } => {}
1184 Expression::FunctionParameterReference { .. } => {}
1185 Expression::ElementReference(_) => {}
1186 Expression::StructFieldAccess { base, .. } => visitor(base),
1187 Expression::ArrayIndex { array, index } => {
1188 visitor(array);
1189 visitor(index);
1190 }
1191 Expression::RepeaterIndexReference { .. } => {}
1192 Expression::RepeaterModelReference { .. } => {}
1193 Expression::Cast { from, .. } => visitor(from),
1194 Expression::CodeBlock(sub) => {
1195 sub.iter().for_each(visitor);
1196 }
1197 Expression::FunctionCall { function: _, arguments, source_location: _ } => {
1198 arguments.iter().for_each(visitor);
1199 }
1200 Expression::SelfAssignment { lhs, rhs, .. } => {
1201 visitor(lhs);
1202 visitor(rhs);
1203 }
1204 Expression::ImageReference { .. } => {}
1205 Expression::Condition { condition, true_expr, false_expr, .. } => {
1206 visitor(condition);
1207 visitor(true_expr);
1208 visitor(false_expr);
1209 }
1210 Expression::BinaryExpression { lhs, rhs, .. } => {
1211 visitor(lhs);
1212 visitor(rhs);
1213 }
1214 Expression::UnaryOp { sub, .. } => visitor(sub),
1215 Expression::Array { values, .. } => {
1216 for x in values {
1217 visitor(x);
1218 }
1219 }
1220 Expression::Struct { values, .. } => {
1221 for x in values.values() {
1222 visitor(x);
1223 }
1224 }
1225 Expression::PathData(data) => match data {
1226 Path::Elements(elements) => {
1227 for element in elements {
1228 element.bindings.values().for_each(|binding| visitor(&binding.borrow()))
1229 }
1230 }
1231 Path::Events(events, coordinates) => {
1232 events.iter().chain(coordinates.iter()).for_each(visitor);
1233 }
1234 Path::Commands(commands) => visitor(commands),
1235 },
1236 Expression::EmptyDataTransfer => {}
1237 Expression::StoreLocalVariable { value, .. } => visitor(value),
1238 Expression::ReadLocalVariable { .. } => {}
1239 Expression::EasingCurve(_) => {}
1240 Expression::MouseCursor(cursor) => match cursor {
1241 MouseCursorInner::CustomMouseCursor { image, hotspot_x, hotspot_y } => {
1242 visitor(image);
1243 visitor(hotspot_x);
1244 visitor(hotspot_y);
1245 }
1246 MouseCursorInner::BuiltIn(e) => visitor(e),
1247 },
1248 Expression::LinearGradient { angle, stops } => {
1249 visitor(angle);
1250 for (c, s) in stops {
1251 visitor(c);
1252 visitor(s);
1253 }
1254 }
1255 Expression::RadialGradient { center, radius, stops } => {
1256 if let Some((cx, cy)) = center {
1257 visitor(cx);
1258 visitor(cy);
1259 }
1260 if let Some(r) = radius {
1261 visitor(r);
1262 }
1263 for (c, s) in stops {
1264 visitor(c);
1265 visitor(s);
1266 }
1267 }
1268 Expression::ConicGradient { from_angle, center, stops } => {
1269 visitor(from_angle);
1270 if let Some((cx, cy)) = center {
1271 visitor(cx);
1272 visitor(cy);
1273 }
1274 for (c, s) in stops {
1275 visitor(c);
1276 visitor(s);
1277 }
1278 }
1279 Expression::EnumerationValue(_) => {}
1280 Expression::Keys(_) => {}
1281 Expression::ReturnStatement(expr) => {
1282 expr.as_deref().map(visitor);
1283 }
1284 Expression::LayoutCacheAccess { repeater_index, .. } => {
1285 repeater_index.as_deref().map(visitor);
1286 }
1287 Expression::GridRepeaterCacheAccess {
1288 repeater_index,
1289 stride,
1290 inner_repeater_index,
1291 ..
1292 } => {
1293 visitor(repeater_index);
1294 visitor(stride);
1295 inner_repeater_index.as_deref().map(visitor);
1296 }
1297 Expression::OrganizeGridLayout(..) => {}
1298 Expression::ComputeBoxLayoutInfo { cross_axis_size, .. }
1299 | Expression::ComputeGridLayoutInfo { cross_axis_size, .. }
1300 | Expression::ComputeFlexboxLayoutInfo { cross_axis_size, .. } => {
1301 if let Some(cas) = cross_axis_size {
1302 visitor(cas);
1303 }
1304 }
1305 Expression::SolveBoxLayout(..) => {}
1306 Expression::SolveGridLayout { .. } => {}
1307 Expression::SolveFlexboxLayout(..) => {}
1308 Expression::MinMax { lhs, rhs, .. } => {
1309 visitor(lhs);
1310 visitor(rhs);
1311 }
1312 Expression::EmptyComponentFactory => {}
1313 Expression::DebugHook { expression, .. } => visitor(expression),
1314 Expression::Closure { expression, .. } => visitor(expression),
1315 }
1316 }
1317
1318 pub fn visit_mut(&mut self, mut visitor: impl FnMut(&mut Self)) {
1319 self.visit_mut_dyn(&mut visitor)
1320 }
1321
1322 fn visit_mut_dyn(&mut self, visitor: &mut dyn FnMut(&mut Self)) {
1323 match self {
1324 Expression::Invalid => {}
1325 Expression::Uncompiled(_) => {}
1326 Expression::StringLiteral(_) => {}
1327 Expression::NumberLiteral(_, _) => {}
1328 Expression::BoolLiteral(_) => {}
1329 Expression::PropertyReference { .. } => {}
1330 Expression::FunctionParameterReference { .. } => {}
1331 Expression::ElementReference(_) => {}
1332 Expression::StructFieldAccess { base, .. } => visitor(base),
1333 Expression::ArrayIndex { array, index } => {
1334 visitor(array);
1335 visitor(index);
1336 }
1337 Expression::RepeaterIndexReference { .. } => {}
1338 Expression::RepeaterModelReference { .. } => {}
1339 Expression::Cast { from, .. } => visitor(from),
1340 Expression::CodeBlock(sub) => {
1341 sub.iter_mut().for_each(visitor);
1342 }
1343 Expression::FunctionCall { function: _, arguments, source_location: _ } => {
1344 arguments.iter_mut().for_each(visitor);
1345 }
1346 Expression::SelfAssignment { lhs, rhs, .. } => {
1347 visitor(lhs);
1348 visitor(rhs);
1349 }
1350 Expression::ImageReference { .. } => {}
1351 Expression::Condition { condition, true_expr, false_expr, .. } => {
1352 visitor(condition);
1353 visitor(true_expr);
1354 visitor(false_expr);
1355 }
1356 Expression::BinaryExpression { lhs, rhs, .. } => {
1357 visitor(lhs);
1358 visitor(rhs);
1359 }
1360 Expression::UnaryOp { sub, .. } => visitor(sub),
1361 Expression::Array { values, .. } => {
1362 for x in values {
1363 visitor(x);
1364 }
1365 }
1366 Expression::Struct { values, .. } => {
1367 for x in values.values_mut() {
1368 visitor(x);
1369 }
1370 }
1371 Expression::PathData(data) => match data {
1372 Path::Elements(elements) => {
1373 for element in elements {
1374 element
1375 .bindings
1376 .values_mut()
1377 .for_each(|binding| visitor(&mut binding.borrow_mut()))
1378 }
1379 }
1380 Path::Events(events, coordinates) => {
1381 events.iter_mut().chain(coordinates.iter_mut()).for_each(visitor);
1382 }
1383 Path::Commands(commands) => visitor(commands),
1384 },
1385 Expression::EmptyDataTransfer => {}
1386 Expression::StoreLocalVariable { value, .. } => visitor(value),
1387 Expression::ReadLocalVariable { .. } => {}
1388 Expression::EasingCurve(_) => {}
1389 Expression::MouseCursor(cursor) => match cursor {
1390 MouseCursorInner::CustomMouseCursor { image, hotspot_x, hotspot_y } => {
1391 visitor(image);
1392 visitor(hotspot_x);
1393 visitor(hotspot_y);
1394 }
1395 MouseCursorInner::BuiltIn(e) => visitor(e),
1396 },
1397 Expression::LinearGradient { angle, stops } => {
1398 visitor(angle);
1399 for (c, s) in stops {
1400 visitor(c);
1401 visitor(s);
1402 }
1403 }
1404 Expression::RadialGradient { center, radius, stops } => {
1405 if let Some((cx, cy)) = center {
1406 visitor(cx);
1407 visitor(cy);
1408 }
1409 if let Some(r) = radius {
1410 visitor(r);
1411 }
1412 for (c, s) in stops {
1413 visitor(c);
1414 visitor(s);
1415 }
1416 }
1417 Expression::ConicGradient { from_angle, center, stops } => {
1418 visitor(from_angle);
1419 if let Some((cx, cy)) = center {
1420 visitor(cx);
1421 visitor(cy);
1422 }
1423 for (c, s) in stops {
1424 visitor(c);
1425 visitor(s);
1426 }
1427 }
1428 Expression::EnumerationValue(_) => {}
1429 Expression::Keys(_) => {}
1430 Expression::ReturnStatement(expr) => {
1431 expr.as_deref_mut().map(visitor);
1432 }
1433 Expression::LayoutCacheAccess { repeater_index, .. } => {
1434 repeater_index.as_deref_mut().map(visitor);
1435 }
1436 Expression::GridRepeaterCacheAccess {
1437 repeater_index,
1438 stride,
1439 inner_repeater_index,
1440 ..
1441 } => {
1442 visitor(repeater_index);
1443 visitor(stride);
1444 inner_repeater_index.as_deref_mut().map(visitor);
1445 }
1446 Expression::OrganizeGridLayout(..) => {}
1447 Expression::ComputeBoxLayoutInfo { cross_axis_size, .. }
1448 | Expression::ComputeGridLayoutInfo { cross_axis_size, .. }
1449 | Expression::ComputeFlexboxLayoutInfo { cross_axis_size, .. } => {
1450 if let Some(cas) = cross_axis_size {
1451 visitor(cas);
1452 }
1453 }
1454 Expression::SolveBoxLayout(..) => {}
1455 Expression::SolveGridLayout { .. } => {}
1456 Expression::SolveFlexboxLayout(..) => {}
1457 Expression::MinMax { lhs, rhs, .. } => {
1458 visitor(lhs);
1459 visitor(rhs);
1460 }
1461 Expression::EmptyComponentFactory => {}
1462 Expression::DebugHook { expression, .. } => visitor(expression),
1463 Expression::Closure { expression, .. } => visitor(expression),
1464 }
1465 }
1466
1467 pub fn visit_recursive(&self, visitor: &mut dyn FnMut(&Self)) {
1469 visitor(self);
1470 self.visit(|e| e.visit_recursive(visitor));
1471 }
1472
1473 pub fn visit_recursive_mut(&mut self, visitor: &mut dyn FnMut(&mut Self)) {
1475 visitor(self);
1476 self.visit_mut(|e| e.visit_recursive_mut(visitor));
1477 }
1478
1479 pub fn is_constant(&self, ga: Option<&crate::passes::GlobalAnalysis>) -> bool {
1480 match self {
1481 Expression::Invalid => true,
1482 Expression::Uncompiled(_) => false,
1483 Expression::StringLiteral(_) => true,
1484 Expression::NumberLiteral(_, _) => true,
1485 Expression::BoolLiteral(_) => true,
1486 Expression::PropertyReference(nr) => nr.is_constant(),
1487 Expression::ElementReference(_) => false,
1488 Expression::RepeaterIndexReference { .. } => false,
1489 Expression::RepeaterModelReference { .. } => false,
1490 Expression::FunctionParameterReference { .. } => true,
1492 Expression::StructFieldAccess { base, .. } => base.is_constant(ga),
1493 Expression::ArrayIndex { array, index } => {
1494 array.is_constant(ga) && index.is_constant(ga)
1495 }
1496 Expression::Cast { from, to } => {
1497 if *to == Type::String
1502 && from.ty() == Type::Float32
1503 && !matches!(&**from, Expression::NumberLiteral(n, Unit::None)
1504 if locale_independent_number_to_string(*n).is_some())
1505 {
1506 return false;
1507 }
1508 from.is_constant(ga)
1509 }
1510 Expression::CodeBlock(sub) => sub.iter().all(|s| s.is_constant(ga)),
1513 Expression::FunctionCall { function, arguments, .. } => {
1514 let is_const = match function {
1515 Callable::Builtin(b) => b.is_const(ga),
1516 Callable::Function(nr) => nr.is_constant(),
1517 Callable::Callback(..) => false,
1518 };
1519 is_const && arguments.iter().all(|a| a.is_constant(ga))
1520 }
1521 Expression::SelfAssignment { .. } => false,
1522 Expression::ImageReference { .. } => true,
1523 Expression::Condition { condition, false_expr, true_expr, .. } => {
1524 condition.is_constant(ga) && false_expr.is_constant(ga) && true_expr.is_constant(ga)
1525 }
1526 Expression::BinaryExpression { lhs, rhs, .. } => {
1527 lhs.is_constant(ga) && rhs.is_constant(ga)
1528 }
1529 Expression::UnaryOp { sub, .. } => sub.is_constant(ga),
1530 Expression::Array { .. } => false,
1534 Expression::Struct { values, .. } => values.iter().all(|(_, v)| v.is_constant(ga)),
1535 Expression::PathData(data) => match data {
1536 Path::Elements(elements) => elements
1537 .iter()
1538 .all(|element| element.bindings.values().all(|v| v.borrow().is_constant(ga))),
1539 Path::Events(_, _) => true,
1540 Path::Commands(_) => false,
1541 },
1542 Expression::EmptyDataTransfer => true,
1543 Expression::StoreLocalVariable { value, .. } => value.is_constant(ga),
1544 Expression::ReadLocalVariable { .. } => true,
1546 Expression::EasingCurve(_) => true,
1547 Expression::MouseCursor(cursor) => match cursor {
1548 MouseCursorInner::BuiltIn(cursor) => cursor.is_constant(ga),
1549 MouseCursorInner::CustomMouseCursor { image, hotspot_x, hotspot_y } => {
1550 image.is_constant(ga) && hotspot_x.is_constant(ga) && hotspot_y.is_constant(ga)
1551 }
1552 },
1553 Expression::LinearGradient { angle, stops } => {
1554 angle.is_constant(ga)
1555 && stops.iter().all(|(c, s)| c.is_constant(ga) && s.is_constant(ga))
1556 }
1557 Expression::RadialGradient { center, radius, stops } => {
1558 center.as_ref().is_none_or(|(cx, cy)| cx.is_constant(ga) && cy.is_constant(ga))
1559 && radius.as_ref().is_none_or(|r| r.is_constant(ga))
1560 && stops.iter().all(|(c, s)| c.is_constant(ga) && s.is_constant(ga))
1561 }
1562 Expression::ConicGradient { from_angle, center, stops } => {
1563 from_angle.is_constant(ga)
1564 && center
1565 .as_ref()
1566 .is_none_or(|(cx, cy)| cx.is_constant(ga) && cy.is_constant(ga))
1567 && stops.iter().all(|(c, s)| c.is_constant(ga) && s.is_constant(ga))
1568 }
1569 Expression::EnumerationValue(_) => true,
1570 Expression::Keys(_) => true,
1571 Expression::ReturnStatement(expr) => {
1572 expr.as_ref().is_none_or(|expr| expr.is_constant(ga))
1573 }
1574 Expression::LayoutCacheAccess { .. } => false,
1576 Expression::GridRepeaterCacheAccess { .. } => false,
1577 Expression::OrganizeGridLayout { .. } => false,
1578 Expression::ComputeBoxLayoutInfo { .. } => false,
1579 Expression::ComputeGridLayoutInfo { .. } => false,
1580 Expression::SolveBoxLayout(..) => false,
1581 Expression::SolveGridLayout { .. } => false,
1582 Expression::SolveFlexboxLayout(..) => false,
1583 Expression::ComputeFlexboxLayoutInfo { .. } => false,
1584 Expression::MinMax { lhs, rhs, .. } => lhs.is_constant(ga) && rhs.is_constant(ga),
1585 Expression::EmptyComponentFactory => true,
1586 Expression::DebugHook { .. } => false,
1587 Expression::Closure { expression, .. } => expression.is_constant(ga),
1588 }
1589 }
1590
1591 #[must_use]
1593 pub fn maybe_convert_to(
1594 self,
1595 target_type: Type,
1596 node: &dyn Spanned,
1597 diag: &mut BuildDiagnostics,
1598 symbol_counters: &SymbolCounters,
1599 ) -> Expression {
1600 let ty = self.ty();
1601
1602 if let Expression::Condition { .. } = self
1603 && ty == Type::Void
1604 {
1605 self
1608 } else if ty == target_type
1609 || target_type == Type::Void
1610 || target_type == Type::Invalid
1611 || ty == Type::Invalid
1612 {
1613 self
1614 } else if ty.can_convert(&target_type) {
1615 let from = match (ty, &target_type) {
1616 (Type::Brush, Type::Color) => match self {
1617 Expression::LinearGradient { .. }
1618 | Expression::RadialGradient { .. }
1619 | Expression::ConicGradient { .. } => {
1620 let message = format!(
1621 "Narrowing conversion from {0} to {1}. This can lead to unexpected behavior because the {0} is a gradient",
1622 Type::Brush,
1623 Type::Color
1624 );
1625 diag.push_warning(message, node);
1626 self
1627 }
1628 _ => self,
1629 },
1630 (Type::Percent, Type::Float32) => Expression::BinaryExpression {
1631 lhs: Box::new(self),
1632 rhs: Box::new(Expression::NumberLiteral(0.01, Unit::None)),
1633 op: '*',
1634 source_location: None,
1635 },
1636 (ref from_ty @ Type::Struct(ref left), Type::Struct(right))
1637 if left.fields != right.fields =>
1638 {
1639 #[cfg(feature = "slint-sc")]
1643 if diag.slint_sc {
1644 for f in left.fields.keys() {
1645 if !right.fields.contains_key(f) {
1646 diag.slint_sc_error(
1647 &format!("Providing the extra struct member '{f}' is"),
1648 node,
1649 );
1650 }
1651 }
1652 for f in right.fields.keys() {
1653 if !left.fields.contains_key(f) {
1654 diag.slint_sc_error(
1655 &format!("Omitting the struct field '{f}' is"),
1656 node,
1657 );
1658 }
1659 }
1660 }
1661 if !diag.is_slint_sc() {
1662 let extra = left
1663 .fields
1664 .keys()
1665 .filter(|f| !right.fields.contains_key(*f))
1666 .map(|f| format!("'{f}'"))
1667 .collect::<Vec<_>>();
1668 if let Some((last, rest)) = extra.split_last() {
1669 let (noun, list) = match rest {
1670 [] => ("field", last.clone()),
1671 _ => ("fields", format!("{} and {last}", rest.join(", "))),
1672 };
1673 diag.push_warning(
1674 format!(
1675 "Conversion to {target_type} ignores the extra {noun} {list}"
1676 ),
1677 node,
1678 );
1679 }
1680 }
1681 if let Expression::Struct { mut values, .. } = self {
1682 let mut new_values = BTreeMap::new();
1683 for (key, ty) in &right.fields {
1684 let (key, expression) = values.remove_entry(key).map_or_else(
1685 || (key.clone(), right.default_value_for_field(key)),
1686 |(k, e)| {
1687 (k, e.maybe_convert_to(ty.clone(), node, diag, symbol_counters))
1688 },
1689 );
1690 new_values.insert(key, expression);
1691 }
1692 return Expression::Struct { values: new_values, ty: right.clone() };
1693 }
1694 let var_name = symbol_counters.generate_name("tmpobj_conv_");
1695 let mut new_values = BTreeMap::new();
1696 for (key, ty) in &right.fields {
1697 let expression = if left.fields.contains_key(key) {
1698 Expression::StructFieldAccess {
1699 base: Box::new(Expression::ReadLocalVariable {
1700 name: var_name.clone(),
1701 ty: from_ty.clone(),
1702 }),
1703 name: key.clone(),
1704 }
1705 .maybe_convert_to(
1706 ty.clone(),
1707 node,
1708 diag,
1709 symbol_counters,
1710 )
1711 } else {
1712 right.default_value_for_field(key)
1713 };
1714 new_values.insert(key.clone(), expression);
1715 }
1716 return Expression::CodeBlock(vec![
1717 Expression::StoreLocalVariable { name: var_name, value: Box::new(self) },
1718 Expression::Struct { values: new_values, ty: right.clone() },
1719 ]);
1720 }
1721 (left, right) => match (left.as_unit_product(), right.as_unit_product()) {
1722 (Some(left), Some(right)) => {
1723 if let Some(conversion_powers) =
1724 crate::langtype::unit_product_length_conversion(&left, &right)
1725 {
1726 let apply_power =
1727 |mut result, power: i8, builtin_fn: BuiltinFunction| {
1728 let op = if power < 0 { '*' } else { '/' };
1729 for _ in 0..power.abs() {
1730 result = Expression::BinaryExpression {
1731 source_location: None,
1732 lhs: Box::new(result),
1733 rhs: Box::new(Expression::FunctionCall {
1734 function: Callable::Builtin(builtin_fn.clone()),
1735 arguments: Vec::new(),
1736 source_location: Some(node.to_source_location()),
1737 }),
1738 op,
1739 }
1740 }
1741 result
1742 };
1743
1744 let mut result = self;
1745
1746 if conversion_powers.rem_to_px_power != 0 {
1747 result = apply_power(
1748 result,
1749 conversion_powers.rem_to_px_power,
1750 BuiltinFunction::GetWindowDefaultFontSize,
1751 )
1752 }
1753 if conversion_powers.px_to_phx_power != 0 {
1754 result = apply_power(
1755 result,
1756 conversion_powers.px_to_phx_power,
1757 BuiltinFunction::GetWindowScaleFactor,
1758 )
1759 }
1760
1761 result
1762 } else {
1763 self
1764 }
1765 }
1766 _ => self,
1767 },
1768 };
1769 Expression::Cast { from: Box::new(from), to: target_type }
1770 } else if matches!(
1771 (&ty, &target_type, &self),
1772 (Type::Array(_), Type::Array(_), Expression::Array { .. })
1773 ) {
1774 match (self, target_type) {
1776 (Expression::Array { values, .. }, Type::Array(target_type)) => Expression::Array {
1777 values: values
1778 .into_iter()
1779 .map(|e| {
1780 e.maybe_convert_to((*target_type).clone(), node, diag, symbol_counters)
1781 })
1782 .take_while(|e| !matches!(e, Expression::Invalid))
1783 .collect(),
1784 element_ty: (*target_type).clone(),
1785 },
1786 _ => unreachable!(),
1787 }
1788 } else if let (Type::Struct(target_struct_type), Expression::Struct { values, .. }) =
1789 (&target_type, &self)
1790 {
1791 let mut target_fields = target_struct_type.fields.clone();
1793 let mut new_values = BTreeMap::new();
1794 for (f, v) in values {
1795 if let Some(t) = target_fields.remove(f) {
1796 new_values.insert(
1797 f.clone(),
1798 v.clone().maybe_convert_to(t, node, diag, symbol_counters),
1799 );
1800 } else {
1801 let available_fields_message = if target_struct_type.name.slint_name().is_some()
1802 {
1803 let available_fields = target_struct_type
1804 .fields
1805 .keys()
1806 .map(SmolStr::as_str)
1807 .collect::<Vec<_>>()
1808 .join("', '");
1809 format!(". Available fields: '{available_fields}'")
1810 } else {
1811 String::new()
1812 };
1813 diag.push_error(
1814 format!("Cannot convert {ty} to {target_type}: Field '{f}' not found{available_fields_message}"),
1815 node,
1816 );
1817 return self;
1818 }
1819 }
1820 for f in target_fields.into_keys() {
1821 let default_value = target_struct_type.default_value_for_field(&f);
1822 new_values.insert(f, default_value);
1823 }
1824 Expression::Struct { ty: target_struct_type.clone(), values: new_values }
1825 } else if let Expression::Condition { condition, true_expr, false_expr, source_location } =
1826 self
1827 {
1828 let true_expr_converted = true_expr.clone().maybe_convert_to(
1833 target_type.clone(),
1834 node,
1835 diag,
1836 symbol_counters,
1837 );
1838 if true_expr_converted.ty() != target_type.clone() {
1839 Expression::Condition { condition, true_expr, false_expr, source_location }
1841 } else {
1842 Expression::Condition {
1843 condition,
1844 source_location,
1845 true_expr: Box::new(true_expr_converted),
1846 false_expr: Box::new(false_expr.maybe_convert_to(
1847 target_type,
1848 node,
1849 diag,
1850 symbol_counters,
1851 )),
1852 }
1853 }
1854 } else {
1855 let mut message = format!("Cannot convert {ty} to {target_type}");
1856 if let Some(from_unit) = ty.default_unit() {
1858 if matches!(&target_type, Type::Int32 | Type::Float32 | Type::String) {
1859 message =
1860 format!("{message}. Divide by 1{from_unit} to convert to a plain number");
1861 }
1862 } else if matches!(target_type, Type::StyledText) && ty.can_convert(&Type::String) {
1863 message = format!(
1864 "{message}. Wrap the expression in `@markdown(\"\\{{...}}\")` to convert it explicitly"
1865 );
1866 } else if let Some(to_unit) = target_type.default_unit()
1867 && matches!(ty, Type::Int32 | Type::Float32)
1868 {
1869 if let Expression::NumberLiteral(value, Unit::None) = self
1870 && value == 0.
1871 {
1872 return Expression::NumberLiteral(0., to_unit);
1874 }
1875 message = format!(
1876 "{message}. Use an unit, or multiply by 1{to_unit} to convert explicitly"
1877 );
1878 }
1879 diag.push_error(message, node);
1880 self
1881 }
1882 }
1883
1884 pub fn default_value_for_type(ty: &Type) -> Expression {
1886 match ty {
1887 Type::Invalid
1888 | Type::Callback { .. }
1889 | Type::Function { .. }
1890 | Type::InferredProperty
1891 | Type::InferredCallback
1892 | Type::ElementReference
1893 | Type::LayoutCache
1894 | Type::ArrayOfU16 => Expression::Invalid,
1895 Type::Void => Expression::CodeBlock(Vec::new()),
1896 Type::DataTransfer => Expression::EmptyDataTransfer,
1897 Type::Float32 => Expression::NumberLiteral(0., Unit::None),
1898 Type::String => Expression::StringLiteral(SmolStr::default()),
1899 Type::Int32 | Type::Color | Type::UnitProduct(_) => Expression::Cast {
1900 from: Box::new(Expression::NumberLiteral(0., Unit::None)),
1901 to: ty.clone(),
1902 },
1903 Type::Duration => Expression::NumberLiteral(0., Unit::Ms),
1904 Type::Angle => Expression::NumberLiteral(0., Unit::Deg),
1905 Type::PhysicalLength => Expression::NumberLiteral(0., Unit::Phx),
1906 Type::LogicalLength => Expression::NumberLiteral(0., Unit::Px),
1907 Type::Rem => Expression::NumberLiteral(0., Unit::Rem),
1908 Type::Percent => Expression::NumberLiteral(100., Unit::Percent),
1909 Type::Image => Expression::ImageReference {
1910 resource_ref: ImageReference::None,
1911 source_location: None,
1912 nine_slice: None,
1913 },
1914 Type::Bool => Expression::BoolLiteral(false),
1915 Type::Model => Expression::Invalid,
1916 Type::PathData => Expression::PathData(Path::Elements(Vec::new())),
1917 Type::Array(element_ty) => {
1918 Expression::Array { element_ty: (**element_ty).clone(), values: Vec::new() }
1919 }
1920 Type::Struct(s) => Expression::Struct {
1921 ty: s.clone(),
1922 values: s
1923 .fields
1924 .keys()
1925 .map(|k| (k.clone(), s.default_value_for_field(k)))
1926 .collect(),
1927 },
1928 Type::Easing => Expression::EasingCurve(EasingCurve::default()),
1929 Type::MouseCursor => {
1930 let e = crate::typeregister::BUILTIN.enums.BuiltInMouseCursor.clone();
1931 Expression::MouseCursor(MouseCursorInner::BuiltIn(Box::new(
1932 Expression::EnumerationValue(e.default_value()),
1933 )))
1934 }
1935 Type::Brush => Expression::Cast {
1936 from: Box::new(Expression::default_value_for_type(&Type::Color)),
1937 to: Type::Brush,
1938 },
1939 Type::Enumeration(enumeration) => {
1940 Expression::EnumerationValue(enumeration.clone().default_value())
1941 }
1942 Type::Keys => Expression::Keys(Keys::default()),
1943 Type::ComponentFactory => Expression::EmptyComponentFactory,
1944 Type::StyledText => Expression::FunctionCall {
1945 function: Callable::Builtin(BuiltinFunction::StringToStyledText),
1946 arguments: vec![Self::default_value_for_type(&Type::String)],
1947 source_location: None,
1948 },
1949 Type::Closure => Expression::Invalid,
1950 }
1951 }
1952
1953 pub fn try_set_rw(
1957 &mut self,
1958 ctx: &mut LookupCtx,
1959 what: &'static str,
1960 node: &dyn Spanned,
1961 ) -> bool {
1962 match self {
1963 Expression::PropertyReference(nr) => {
1964 nr.mark_as_set();
1965 let mut lookup = nr
1966 .element()
1967 .borrow()
1968 .lookup_property(nr.name(), PropertyLookupMode::InternalName);
1969 lookup.is_local_to_component &= ctx.is_local_element(&nr.element());
1970 if lookup.property_visibility == PropertyVisibility::Constexpr {
1971 ctx.diag.push_error(
1972 "The property must be known at compile time and cannot be changed at runtime"
1973 .into(),
1974 node,
1975 );
1976 false
1977 } else if lookup.is_valid_for_assignment() {
1978 if !nr
1979 .element()
1980 .borrow()
1981 .property_analysis
1982 .borrow()
1983 .get(nr.name())
1984 .is_some_and(|d| d.is_linked_to_read_only)
1985 {
1986 true
1987 } else if ctx.is_legacy_component() {
1988 ctx.diag.push_warning("Modifying a property that is linked to a read-only property is deprecated".into(), node);
1989 true
1990 } else {
1991 ctx.diag.push_error(
1992 "Cannot modify a property that is linked to a read-only property"
1993 .into(),
1994 node,
1995 );
1996 false
1997 }
1998 } else if ctx.is_legacy_component()
1999 && lookup.property_visibility == PropertyVisibility::Output
2000 {
2001 ctx.diag.push_warning(
2002 format!(
2003 "{what} on an '{}' property is deprecated",
2004 PropertyVisibility::Output
2005 ),
2006 node,
2007 );
2008 true
2009 } else {
2010 ctx.diag.push_error(
2011 format!("{what} on an '{}' property", lookup.property_visibility),
2012 node,
2013 );
2014 false
2015 }
2016 }
2017 Expression::StructFieldAccess { base, .. } => base.try_set_rw(ctx, what, node),
2018 Expression::RepeaterModelReference { .. } => true,
2019 Expression::ArrayIndex { array, .. } => array.try_set_rw(ctx, what, node),
2020 _ => {
2021 ctx.diag.push_error(format!("{what} needs to be done on a property"), node);
2022 false
2023 }
2024 }
2025 }
2026
2027 pub fn ignore_debug_hooks(&self) -> &Expression {
2029 match self {
2030 Expression::DebugHook { expression, .. } => expression.as_ref(),
2031 _ => self,
2032 }
2033 }
2034
2035 pub fn ignore_debug_hooks_mut(&mut self) -> &mut Expression {
2036 match self {
2037 Expression::DebugHook { expression, .. } => expression.as_mut(),
2038 _ => self,
2039 }
2040 }
2041
2042 pub fn is_synthetic_debug_hook(&self) -> bool {
2045 matches!(self, Expression::DebugHook { synthetic: true, .. })
2046 }
2047}
2048
2049fn model_inner_type(model: &Expression) -> Type {
2050 match model {
2051 Expression::Cast { from, to: Type::Model } => model_inner_type(from),
2052 Expression::CodeBlock(cb) => cb.last().map_or(Type::Invalid, model_inner_type),
2053 _ => match model.ty() {
2054 Type::Float32 | Type::Int32 => Type::Int32,
2055 Type::Array(elem) => (*elem).clone(),
2056 _ => Type::Invalid,
2057 },
2058 }
2059}
2060
2061pub fn locale_independent_number_to_string(n: f64) -> Option<SmolStr> {
2064 let string = format_smolstr!("{}", i_slint_common::FormattedNumber(n));
2065 (!string.contains('.')).then_some(string)
2066}
2067
2068#[derive(Clone, Debug)]
2070pub enum TwoWayBinding {
2071 Property {
2072 property: NamedReference,
2074 field_access: Vec<SmolStr>,
2077 },
2078 ModelData {
2079 repeated_element: ElementWeak,
2081 field_access: Vec<SmolStr>,
2083 },
2084}
2085impl TwoWayBinding {
2086 pub fn ty(&self) -> Type {
2087 let (mut ty, field_access) = match self {
2088 Self::Property { property, field_access } => (property.ty(), field_access),
2089 Self::ModelData { repeated_element, field_access } => {
2090 let ty =
2091 Expression::RepeaterModelReference { element: repeated_element.clone() }.ty();
2092 (ty, field_access)
2093 }
2094 };
2095 for x in field_access {
2096 ty = match ty {
2097 Type::InferredProperty | Type::InferredCallback => return ty,
2098 Type::Struct(s) => s.fields.get(x).cloned().unwrap_or_default(),
2099 _ => return Type::Invalid,
2100 };
2101 }
2102 ty
2103 }
2104
2105 pub fn is_constant(&self) -> bool {
2106 match self {
2107 Self::Property { property, .. } => property.is_constant(),
2108 Self::ModelData { .. } => false,
2109 }
2110 }
2111
2112 pub fn property(&self) -> Option<&NamedReference> {
2113 match self {
2114 Self::Property { property, .. } => Some(property),
2115 Self::ModelData { .. } => None,
2116 }
2117 }
2118}
2119
2120impl From<NamedReference> for TwoWayBinding {
2121 fn from(nr: NamedReference) -> Self {
2122 Self::Property { property: nr, field_access: Vec::new() }
2123 }
2124}
2125
2126#[derive(Debug, Clone, derive_more::Deref, derive_more::DerefMut)]
2128pub struct BindingExpression {
2129 #[deref]
2130 #[deref_mut]
2131 pub expression: Expression,
2132 pub span: Option<SourceLocation>,
2134 pub priority: i32,
2139
2140 pub animation: Option<PropertyAnimation>,
2141
2142 pub analysis: Option<BindingAnalysis>,
2144
2145 pub two_way_bindings: Vec<TwoWayBinding>,
2147}
2148
2149impl std::convert::From<Expression> for BindingExpression {
2150 fn from(expression: Expression) -> Self {
2151 Self {
2152 expression,
2153 span: None,
2154 priority: 0,
2155 animation: Default::default(),
2156 analysis: Default::default(),
2157 two_way_bindings: Default::default(),
2158 }
2159 }
2160}
2161
2162impl BindingExpression {
2163 pub fn new_uncompiled(node: SyntaxNode) -> Self {
2164 Self {
2165 expression: Expression::Uncompiled(node.clone()),
2166 span: Some(node.to_source_location()),
2167 priority: 1,
2168 animation: Default::default(),
2169 analysis: Default::default(),
2170 two_way_bindings: Default::default(),
2171 }
2172 }
2173 pub fn new_with_span(expression: Expression, span: SourceLocation) -> Self {
2174 Self {
2175 expression,
2176 span: Some(span),
2177 priority: 0,
2178 animation: Default::default(),
2179 analysis: Default::default(),
2180 two_way_bindings: Default::default(),
2181 }
2182 }
2183
2184 pub fn new_two_way(other: TwoWayBinding) -> Self {
2186 Self {
2187 expression: Expression::Invalid,
2188 span: None,
2189 priority: 0,
2190 animation: Default::default(),
2191 analysis: Default::default(),
2192 two_way_bindings: vec![other],
2193 }
2194 }
2195
2196 pub fn merge_with(&mut self, other: &Self) -> bool {
2205 if self.animation.is_none() {
2206 self.animation.clone_from(&other.animation);
2207 }
2208 let has_binding = self.has_binding();
2209 self.two_way_bindings.extend_from_slice(&other.two_way_bindings);
2210 if has_binding {
2211 return false;
2212 }
2213 if let Expression::DebugHook { expression, synthetic, .. } = &mut self.expression {
2217 debug_assert!(*synthetic, "has_binding() returned false for a non-synthetic hook");
2218 if !matches!(other.expression, Expression::Invalid)
2219 && !other.expression.is_synthetic_debug_hook()
2220 {
2221 **expression = other.expression.clone();
2222 *synthetic = false;
2223 self.priority = other.priority;
2224 return true;
2225 }
2226 if self.two_way_bindings.is_empty() {
2227 return false;
2229 }
2230 self.expression = Expression::Invalid;
2234 self.priority = other.priority;
2235 return true;
2236 }
2237 self.priority = other.priority;
2238 self.expression = other.expression.clone();
2239 true
2240 }
2241
2242 pub fn has_binding(&self) -> bool {
2247 (!matches!(self.expression, Expression::Invalid)
2248 && !self.expression.is_synthetic_debug_hook())
2249 || !self.two_way_bindings.is_empty()
2250 }
2251
2252 pub fn value_expression(&self) -> &Expression {
2255 self.expression.ignore_debug_hooks()
2256 }
2257
2258 pub fn set_value_expression(&mut self, expr: Expression) {
2264 match &mut self.expression {
2265 Expression::DebugHook { expression, synthetic, .. } if *synthetic => {
2266 **expression = expr;
2267 *synthetic = false;
2268 }
2269 expression => *expression = expr,
2270 }
2271 }
2272}
2273
2274impl Spanned for BindingExpression {
2275 fn span(&self) -> crate::diagnostics::Span {
2276 self.span.as_ref().map(|x| x.span()).unwrap_or_default()
2277 }
2278 fn source_file(&self) -> Option<&crate::diagnostics::SourceFile> {
2279 self.span.as_ref().and_then(|x| x.source_file())
2280 }
2281}
2282
2283#[derive(Default, Debug, Clone)]
2284pub struct BindingAnalysis {
2285 pub is_in_binding_loop: Cell<bool>,
2287
2288 pub is_const: bool,
2290
2291 pub no_external_dependencies: bool,
2294}
2295
2296#[derive(Debug, Clone)]
2297pub enum Path {
2298 Elements(Vec<PathElement>),
2299 Events(Vec<Expression>, Vec<Expression>),
2300 Commands(Box<Expression>), }
2302
2303#[derive(Debug, Clone)]
2304pub struct PathElement {
2305 pub element_type: Rc<BuiltinElement>,
2306 pub bindings: BindingsMap,
2307}
2308
2309#[derive(Clone, Debug, Default)]
2310pub enum EasingCurve {
2311 #[default]
2312 Linear,
2313 CubicBezier(f32, f32, f32, f32),
2314 EaseInElastic,
2315 EaseOutElastic,
2316 EaseInOutElastic,
2317 EaseInBounce,
2318 EaseOutBounce,
2319 EaseInOutBounce,
2320 Spring(f32),
2321 }
2324
2325#[derive(Clone, Debug)]
2328pub enum MouseCursorInner<E = Expression> {
2329 BuiltIn(Box<E>),
2330 CustomMouseCursor { image: Box<E>, hotspot_x: Box<E>, hotspot_y: Box<E> },
2331}
2332
2333impl<E: Default> Default for MouseCursorInner<E> {
2334 fn default() -> Self {
2335 Self::BuiltIn(Box::default())
2336 }
2337}
2338
2339#[derive(Clone, Debug)]
2343pub enum ImageReference {
2344 None,
2345 Path(SmolStr),
2347 Url(url::Url),
2349 DataUri(SmolStr),
2351 EmbeddedData {
2352 resource_id: crate::embedded_resources::EmbeddedResourcesIdx,
2353 extension: String,
2354 },
2355 EmbeddedTexture {
2356 resource_id: crate::embedded_resources::EmbeddedResourcesIdx,
2357 },
2358}
2359
2360impl ImageReference {
2361 pub fn from_resolved(reference: SmolStr) -> Self {
2364 if reference.starts_with("data:") {
2365 return Self::DataUri(reference);
2366 }
2367 match url::Url::parse(&reference) {
2370 Ok(url) if url.scheme().len() > 1 => Self::Url(url),
2371 _ => Self::Path(reference),
2372 }
2373 }
2374
2375 pub fn from_mapped_url(url: url::Url) -> Self {
2379 if url.scheme() == "data" { Self::DataUri(url.as_str().into()) } else { Self::Url(url) }
2380 }
2381
2382 pub fn source(&self) -> Option<&str> {
2386 match self {
2387 Self::Path(source) | Self::DataUri(source) => Some(source),
2388 Self::Url(url) => Some(url.as_str()),
2389 Self::None | Self::EmbeddedData { .. } | Self::EmbeddedTexture { .. } => None,
2390 }
2391 }
2392}
2393
2394pub fn pretty_print(f: &mut dyn std::fmt::Write, expression: &Expression) -> std::fmt::Result {
2396 match expression {
2397 Expression::Invalid => write!(f, "<invalid>"),
2398 Expression::Uncompiled(u) => write!(f, "{u:?}"),
2399 Expression::StringLiteral(s) => write!(f, "{s:?}"),
2400 Expression::NumberLiteral(vl, unit) => write!(f, "{vl}{unit}"),
2401 Expression::BoolLiteral(b) => write!(f, "{b:?}"),
2402 Expression::PropertyReference(a) => write!(f, "{a:?}"),
2403 Expression::ElementReference(a) => write!(f, "{a:?}"),
2404 Expression::RepeaterIndexReference { element } => {
2405 crate::namedreference::pretty_print_element_ref(f, element)
2406 }
2407 Expression::RepeaterModelReference { element } => {
2408 crate::namedreference::pretty_print_element_ref(f, element)?;
2409 write!(f, ".@model")
2410 }
2411 Expression::FunctionParameterReference { index, ty: _ } => write!(f, "_arg_{index}"),
2412 Expression::StoreLocalVariable { name, value } => {
2413 write!(f, "{name} = ")?;
2414 pretty_print(f, value)
2415 }
2416 Expression::ReadLocalVariable { name, ty: _ } => write!(f, "{name}"),
2417 Expression::StructFieldAccess { base, name } => {
2418 pretty_print(f, base)?;
2419 write!(f, ".{name}")
2420 }
2421 Expression::ArrayIndex { array, index } => {
2422 pretty_print(f, array)?;
2423 write!(f, "[")?;
2424 pretty_print(f, index)?;
2425 write!(f, "]")
2426 }
2427 Expression::Cast { from, to } => {
2428 write!(f, "(")?;
2429 pretty_print(f, from)?;
2430 write!(f, "/* as {to} */)")
2431 }
2432 Expression::CodeBlock(c) => {
2433 write!(f, "{{ ")?;
2434 for e in c {
2435 pretty_print(f, e)?;
2436 write!(f, "; ")?;
2437 }
2438 write!(f, "}}")
2439 }
2440 Expression::FunctionCall { function, arguments, source_location: _ } => {
2441 match function {
2442 Callable::Builtin(b) => write!(f, "{b:?}")?,
2443 Callable::Callback(nr) | Callable::Function(nr) => write!(f, "{nr:?}")?,
2444 }
2445 write!(f, "(")?;
2446 for e in arguments {
2447 pretty_print(f, e)?;
2448 write!(f, ", ")?;
2449 }
2450 write!(f, ")")
2451 }
2452 Expression::SelfAssignment { lhs, rhs, op, .. } => {
2453 pretty_print(f, lhs)?;
2454 write!(f, " {}= ", if *op == '=' { ' ' } else { *op })?;
2455 pretty_print(f, rhs)
2456 }
2457 Expression::BinaryExpression { lhs, rhs, op, .. } => {
2458 write!(f, "(")?;
2459 pretty_print(f, lhs)?;
2460 match *op {
2461 '=' | '!' => write!(f, " {op}= ")?,
2462 _ => write!(f, " {op} ")?,
2463 };
2464 pretty_print(f, rhs)?;
2465 write!(f, ")")
2466 }
2467 Expression::UnaryOp { sub, op } => {
2468 write!(f, "{op}")?;
2469 pretty_print(f, sub)
2470 }
2471 Expression::ImageReference { resource_ref, .. } => write!(f, "{resource_ref:?}"),
2472 Expression::Condition { condition, true_expr, false_expr, .. } => {
2473 write!(f, "if (")?;
2474 pretty_print(f, condition)?;
2475 write!(f, ") {{ ")?;
2476 pretty_print(f, true_expr)?;
2477 write!(f, " }} else {{ ")?;
2478 pretty_print(f, false_expr)?;
2479 write!(f, " }}")
2480 }
2481 Expression::Array { element_ty: _, values } => {
2482 write!(f, "[")?;
2483 for e in values {
2484 pretty_print(f, e)?;
2485 write!(f, ", ")?;
2486 }
2487 write!(f, "]")
2488 }
2489 Expression::Struct { ty: _, values } => {
2490 write!(f, "{{ ")?;
2491 for (name, e) in values {
2492 write!(f, "{name}: ")?;
2493 pretty_print(f, e)?;
2494 write!(f, ", ")?;
2495 }
2496 write!(f, " }}")
2497 }
2498 Expression::PathData(data) => write!(f, "{data:?}"),
2499 Expression::EmptyDataTransfer => write!(f, "{{ }}"),
2500 Expression::EasingCurve(e) => write!(f, "{e:?}"),
2501 Expression::MouseCursor(m) => write!(f, "{m:?}"),
2502 Expression::LinearGradient { angle, stops } => {
2503 write!(f, "@linear-gradient(")?;
2504 pretty_print(f, angle)?;
2505 for (c, s) in stops {
2506 write!(f, ", ")?;
2507 pretty_print(f, c)?;
2508 write!(f, " ")?;
2509 pretty_print(f, s)?;
2510 }
2511 write!(f, ")")
2512 }
2513 Expression::RadialGradient { center, radius, stops } => {
2514 write!(f, "@radial-gradient(circle")?;
2515 if let Some(r) = radius {
2516 write!(f, " ")?;
2517 pretty_print(f, r)?;
2518 }
2519 if let Some((cx, cy)) = center {
2520 write!(f, " at ")?;
2521 pretty_print(f, cx)?;
2522 write!(f, " ")?;
2523 pretty_print(f, cy)?;
2524 }
2525 for (c, s) in stops {
2526 write!(f, ", ")?;
2527 pretty_print(f, c)?;
2528 write!(f, " ")?;
2529 pretty_print(f, s)?;
2530 }
2531 write!(f, ")")
2532 }
2533 Expression::ConicGradient { from_angle, center, stops } => {
2534 write!(f, "@conic-gradient(from ")?;
2535 pretty_print(f, from_angle)?;
2536 if let Some((cx, cy)) = center {
2537 write!(f, " at ")?;
2538 pretty_print(f, cx)?;
2539 write!(f, " ")?;
2540 pretty_print(f, cy)?;
2541 }
2542 for (c, s) in stops {
2543 write!(f, ", ")?;
2544 pretty_print(f, c)?;
2545 write!(f, " ")?;
2546 pretty_print(f, s)?;
2547 }
2548 write!(f, ")")
2549 }
2550 Expression::EnumerationValue(e) => match e.enumeration.values.get(e.value) {
2551 Some(val) => write!(f, "{}.{}", e.enumeration.name, val),
2552 None => write!(f, "{}.{}", e.enumeration.name, e.value),
2553 },
2554 Expression::Keys(keys) => {
2555 write!(f, "@keys({keys})")
2556 }
2557 Expression::ReturnStatement(e) => {
2558 write!(f, "return ")?;
2559 e.as_ref().map(|e| pretty_print(f, e)).unwrap_or(Ok(()))
2560 }
2561 Expression::LayoutCacheAccess {
2562 layout_cache_prop,
2563 index,
2564 repeater_index,
2565 entries_per_item,
2566 } => {
2567 if repeater_index.is_some() {
2568 write!(
2569 f,
2570 "{:?}[{:?}[{}] + $repeater_index * {}]",
2571 layout_cache_prop, layout_cache_prop, index, entries_per_item
2572 )
2573 } else {
2574 write!(f, "{:?}[{}]", layout_cache_prop, index)
2575 }
2576 }
2577 Expression::GridRepeaterCacheAccess {
2578 layout_cache_prop,
2579 index,
2580 repeater_index: _,
2581 stride: _,
2582 child_offset,
2583 inner_repeater_index,
2584 entries_per_item,
2585 } => {
2586 if inner_repeater_index.is_some() {
2587 write!(
2588 f,
2589 "{0:?}[{0:?}[{1}] + $repeater_index * $stride + {2} + $inner_repeater_index * {3}]",
2590 layout_cache_prop, index, child_offset, entries_per_item
2591 )
2592 } else {
2593 write!(
2594 f,
2595 "{0:?}[{0:?}[{1}] + $repeater_index * $stride + {2}]",
2596 layout_cache_prop, index, child_offset
2597 )
2598 }
2599 }
2600 Expression::OrganizeGridLayout(..) => write!(f, "organize_grid_layout(..)"),
2601 Expression::ComputeBoxLayoutInfo { .. } => write!(f, "layout_info(..)"),
2602 Expression::ComputeGridLayoutInfo { .. } => write!(f, "grid_layout_info(..)"),
2603 Expression::SolveBoxLayout(..) => write!(f, "solve_box_layout(..)"),
2604 Expression::SolveGridLayout { .. } => write!(f, "solve_grid_layout(..)"),
2605 Expression::SolveFlexboxLayout(..) => write!(f, "solve_flexbox_layout(..)"),
2606 Expression::ComputeFlexboxLayoutInfo { .. } => write!(f, "flexbox_layout_info(..)"),
2607 Expression::MinMax { ty: _, op, lhs, rhs } => {
2608 match op {
2609 MinMaxOp::Min => write!(f, "min(")?,
2610 MinMaxOp::Max => write!(f, "max(")?,
2611 }
2612 pretty_print(f, lhs)?;
2613 write!(f, ", ")?;
2614 pretty_print(f, rhs)?;
2615 write!(f, ")")
2616 }
2617 Expression::EmptyComponentFactory => write!(f, "<empty-component-factory>"),
2618 Expression::DebugHook { expression, id, synthetic } => {
2619 write!(f, "debug-hook(")?;
2620 pretty_print(f, expression)?;
2621 if *synthetic {
2622 write!(f, " SYNTHETIC")?;
2623 }
2624 write!(f, "\"{id}\")")
2625 }
2626 Expression::Closure { arg_name, expression } => {
2627 let display_name = arg_name.strip_prefix("local_").unwrap_or(arg_name);
2628 write!(f, "({display_name}) => ")?;
2629 pretty_print(f, expression)
2630 }
2631 }
2632}