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