1use std::rc::Rc;
7
8use crate::diagnostics::{BuildDiagnostics, Spanned};
9use crate::expression_tree::{
10 BuiltinFunction, BuiltinMacroFunction, Callable, EasingCurve, Expression, Unit,
11};
12use crate::langtype::{ElementType, Enumeration, EnumerationValue, Type};
13use crate::namedreference::NamedReference;
14use crate::object_tree::{ElementRc, PropertyVisibility};
15use crate::parser::NodeOrToken;
16use crate::typeregister::TypeRegister;
17use smol_str::{SmolStr, ToSmolStr};
18use std::cell::RefCell;
19
20mod named_colors;
21
22pub use named_colors::named_colors;
23
24pub struct LookupCtx<'a> {
26 pub property_name: Option<&'a str>,
28
29 pub property_type: Type,
32
33 pub component_scope: &'a [ElementRc],
35
36 pub diag: &'a mut BuildDiagnostics,
38
39 pub arguments: Vec<SmolStr>,
41
42 pub type_register: &'a TypeRegister,
44
45 pub type_loader: Option<&'a crate::typeloader::TypeLoader>,
48
49 pub current_token: Option<NodeOrToken>,
51
52 pub local_variables: Vec<Vec<(SmolStr, Type)>>,
54}
55
56impl<'a> LookupCtx<'a> {
57 pub fn empty_context(type_register: &'a TypeRegister, diag: &'a mut BuildDiagnostics) -> Self {
59 Self {
60 property_name: Default::default(),
61 property_type: Default::default(),
62 component_scope: Default::default(),
63 diag,
64 arguments: Default::default(),
65 type_register,
66 type_loader: None,
67 current_token: None,
68 local_variables: Default::default(),
69 }
70 }
71
72 pub fn return_type(&self) -> &Type {
73 match &self.property_type {
74 Type::Callback(f) | Type::Function(f) => &f.return_type,
75 _ => &self.property_type,
76 }
77 }
78
79 pub fn is_legacy_component(&self) -> bool {
80 self.component_scope.first().is_some_and(|e| e.borrow().is_legacy_syntax)
81 }
82
83 pub fn is_local_element(&self, elem: &ElementRc) -> bool {
85 Option::zip(
86 elem.borrow().enclosing_component.upgrade(),
87 self.component_scope.first().and_then(|x| x.borrow().enclosing_component.upgrade()),
88 )
89 .map_or(true, |(x, y)| Rc::ptr_eq(&x, &y))
90 }
91}
92
93#[derive(Debug)]
94pub enum LookupResult {
95 Expression {
96 expression: Expression,
97 deprecated: Option<String>,
99 },
100 Enumeration(Rc<Enumeration>),
101 Namespace(BuiltinNamespace),
102 Callable(LookupResultCallable),
103}
104
105#[derive(Debug)]
106pub enum LookupResultCallable {
107 Callable(Callable),
108 Macro(BuiltinMacroFunction),
109 MemberFunction {
111 base: Expression,
113 base_node: Option<NodeOrToken>,
114 member: Box<LookupResultCallable>,
115 },
116}
117
118#[derive(Debug, derive_more::Display)]
119pub enum BuiltinNamespace {
120 Colors,
121 Easing,
122 Math,
123 Key,
124 SlintInternal,
125}
126
127impl From<Expression> for LookupResult {
128 fn from(expression: Expression) -> Self {
129 Self::Expression { expression, deprecated: None }
130 }
131}
132impl From<Callable> for LookupResult {
133 fn from(callable: Callable) -> Self {
134 Self::Callable(LookupResultCallable::Callable(callable))
135 }
136}
137impl From<BuiltinMacroFunction> for LookupResult {
138 fn from(macro_function: BuiltinMacroFunction) -> Self {
139 Self::Callable(LookupResultCallable::Macro(macro_function))
140 }
141}
142impl From<BuiltinFunction> for LookupResult {
143 fn from(function: BuiltinFunction) -> Self {
144 Self::Callable(LookupResultCallable::Callable(Callable::Builtin(function)))
145 }
146}
147
148impl LookupResult {
149 pub fn deprecated(&self) -> Option<&str> {
150 match self {
151 Self::Expression { deprecated: Some(x), .. } => Some(x.as_str()),
152 _ => None,
153 }
154 }
155}
156
157pub trait LookupObject {
159 fn for_each_entry<R>(
162 &self,
163 ctx: &LookupCtx,
164 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
165 ) -> Option<R>;
166
167 fn lookup(&self, ctx: &LookupCtx, name: &SmolStr) -> Option<LookupResult> {
170 self.for_each_entry(ctx, &mut |prop, expr| (prop == name).then_some(expr))
171 }
172}
173
174impl<T1: LookupObject, T2: LookupObject> LookupObject for (T1, T2) {
175 fn for_each_entry<R>(
176 &self,
177 ctx: &LookupCtx,
178 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
179 ) -> Option<R> {
180 self.0.for_each_entry(ctx, f).or_else(|| self.1.for_each_entry(ctx, f))
181 }
182
183 fn lookup(&self, ctx: &LookupCtx, name: &SmolStr) -> Option<LookupResult> {
184 self.0.lookup(ctx, name).or_else(|| self.1.lookup(ctx, name))
185 }
186}
187
188impl LookupObject for LookupResult {
189 fn for_each_entry<R>(
190 &self,
191 ctx: &LookupCtx,
192 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
193 ) -> Option<R> {
194 match self {
195 LookupResult::Expression { expression, .. } => expression.for_each_entry(ctx, f),
196 LookupResult::Enumeration(e) => e.for_each_entry(ctx, f),
197 LookupResult::Namespace(BuiltinNamespace::Colors) => {
198 (ColorSpecific, ColorFunctions).for_each_entry(ctx, f)
199 }
200 LookupResult::Namespace(BuiltinNamespace::Easing) => {
201 EasingSpecific.for_each_entry(ctx, f)
202 }
203 LookupResult::Namespace(BuiltinNamespace::Math) => MathFunctions.for_each_entry(ctx, f),
204 LookupResult::Namespace(BuiltinNamespace::Key) => KeysLookup.for_each_entry(ctx, f),
205 LookupResult::Namespace(BuiltinNamespace::SlintInternal) => {
206 SlintInternal.for_each_entry(ctx, f)
207 }
208 LookupResult::Callable(..) => None,
209 }
210 }
211
212 fn lookup(&self, ctx: &LookupCtx, name: &SmolStr) -> Option<LookupResult> {
213 match self {
214 LookupResult::Expression { expression, .. } => expression.lookup(ctx, name),
215 LookupResult::Enumeration(e) => e.lookup(ctx, name),
216 LookupResult::Namespace(BuiltinNamespace::Colors) => {
217 (ColorSpecific, ColorFunctions).lookup(ctx, name)
218 }
219 LookupResult::Namespace(BuiltinNamespace::Easing) => EasingSpecific.lookup(ctx, name),
220 LookupResult::Namespace(BuiltinNamespace::Math) => MathFunctions.lookup(ctx, name),
221 LookupResult::Namespace(BuiltinNamespace::Key) => KeysLookup.lookup(ctx, name),
222 LookupResult::Namespace(BuiltinNamespace::SlintInternal) => {
223 SlintInternal.lookup(ctx, name)
224 }
225 LookupResult::Callable(..) => None,
226 }
227 }
228}
229
230struct LocalVariableLookup;
231impl LookupObject for LocalVariableLookup {
232 fn for_each_entry<R>(
233 &self,
234 ctx: &LookupCtx,
235 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
236 ) -> Option<R> {
237 for scope in ctx.local_variables.iter() {
238 for (name, ty) in scope {
239 if let Some(r) = f(
240 &name.strip_prefix("local_").unwrap_or(name).into(),
242 Expression::ReadLocalVariable { name: name.clone(), ty: ty.clone() }.into(),
243 ) {
244 return Some(r);
245 }
246 }
247 }
248 None
249 }
250}
251
252struct ArgumentsLookup;
253impl LookupObject for ArgumentsLookup {
254 fn for_each_entry<R>(
255 &self,
256 ctx: &LookupCtx,
257 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
258 ) -> Option<R> {
259 let args = match &ctx.property_type {
260 Type::Callback(f) | Type::Function(f) => &f.args,
261 _ => return None,
262 };
263 for (index, (name, ty)) in ctx.arguments.iter().zip(args.iter()).enumerate() {
264 if let Some(r) =
265 f(name, Expression::FunctionParameterReference { index, ty: ty.clone() }.into())
266 {
267 return Some(r);
268 }
269 }
270 None
271 }
272}
273
274struct SpecialIdLookup;
275impl LookupObject for SpecialIdLookup {
276 fn for_each_entry<R>(
277 &self,
278 ctx: &LookupCtx,
279 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
280 ) -> Option<R> {
281 let last = ctx.component_scope.last();
282 let mut f = |n, e: Expression| f(&SmolStr::new_static(n), e.into());
283 None.or_else(|| f("self", Expression::ElementReference(Rc::downgrade(last?))))
284 .or_else(|| {
285 let len = ctx.component_scope.len();
286 if len >= 2 {
287 f(
288 "parent",
289 Expression::ElementReference(Rc::downgrade(&ctx.component_scope[len - 2]))
290 .into(),
291 )
292 } else {
293 None
294 }
295 })
296 .or_else(|| f("true", Expression::BoolLiteral(true)))
297 .or_else(|| f("false", Expression::BoolLiteral(false)))
298 }
300}
301
302struct IdLookup;
303impl LookupObject for IdLookup {
304 fn for_each_entry<R>(
305 &self,
306 ctx: &LookupCtx,
307 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
308 ) -> Option<R> {
309 fn visit<R>(
310 root: &ElementRc,
311 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
312 ) -> Option<R> {
313 if !root.borrow().id.is_empty() {
314 if let Some(r) =
315 f(&root.borrow().id, Expression::ElementReference(Rc::downgrade(root)).into())
316 {
317 return Some(r);
318 }
319 }
320 for x in &root.borrow().children {
321 if x.borrow().repeated.is_some() {
322 continue;
323 }
324 if let Some(r) = visit(x, f) {
325 return Some(r);
326 }
327 }
328 None
329 }
330 for e in ctx.component_scope.iter().rev() {
331 if e.borrow().repeated.is_some() {
332 if let Some(r) = visit(e, f) {
333 return Some(r);
334 }
335 }
336 }
337 if let Some(root) = ctx.component_scope.first() {
338 if let Some(r) = visit(root, f) {
339 return Some(r);
340 }
341 }
342 None
343 }
344 }
346
347pub struct InScopeLookup;
349impl InScopeLookup {
350 fn visit_scope<R>(
351 ctx: &LookupCtx,
352 mut visit_entry: impl FnMut(&SmolStr, LookupResult) -> Option<R>,
353 mut visit_legacy_scope: impl FnMut(&ElementRc) -> Option<R>,
354 mut visit_scope: impl FnMut(&ElementRc) -> Option<R>,
355 ) -> Option<R> {
356 let is_legacy = ctx.is_legacy_component();
357 for (idx, elem) in ctx.component_scope.iter().rev().enumerate() {
358 if let Some(repeated) = &elem.borrow().repeated {
359 if !repeated.index_id.is_empty() {
360 if let Some(r) = visit_entry(
361 &repeated.index_id,
362 Expression::RepeaterIndexReference { element: Rc::downgrade(elem) }.into(),
363 ) {
364 return Some(r);
365 }
366 }
367 if !repeated.model_data_id.is_empty() {
368 if let Some(r) = visit_entry(
369 &repeated.model_data_id,
370 Expression::RepeaterModelReference { element: Rc::downgrade(elem) }.into(),
371 ) {
372 return Some(r);
373 }
374 }
375 }
376
377 if is_legacy {
378 if elem.borrow().repeated.is_some()
379 || idx == 0
380 || idx == ctx.component_scope.len() - 1
381 {
382 if let Some(r) = visit_legacy_scope(elem) {
383 return Some(r);
384 }
385 }
386 } else if let Some(r) = visit_scope(elem) {
387 return Some(r);
388 }
389 }
390 None
391 }
392}
393impl LookupObject for InScopeLookup {
394 fn for_each_entry<R>(
395 &self,
396 ctx: &LookupCtx,
397 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
398 ) -> Option<R> {
399 let f = RefCell::new(f);
400 Self::visit_scope(
401 ctx,
402 |str, r| f.borrow_mut()(str, r),
403 |elem| elem.for_each_entry(ctx, *f.borrow_mut()),
404 |elem| {
405 for (name, prop) in &elem.borrow().property_declarations {
406 let e = expression_from_reference(
407 NamedReference::new(elem, name.clone()),
408 &prop.property_type,
409 None,
410 );
411 if let Some(r) = f.borrow_mut()(name, e) {
412 return Some(r);
413 }
414 }
415 None
416 },
417 )
418 }
419
420 fn lookup(&self, ctx: &LookupCtx, name: &SmolStr) -> Option<LookupResult> {
421 if name.is_empty() {
422 return None;
423 }
424 Self::visit_scope(
425 ctx,
426 |str, r| (str == name).then_some(r),
427 |elem| elem.lookup(ctx, name),
428 |elem| {
429 elem.borrow().property_declarations.get(name).map(|prop| {
430 expression_from_reference(
431 NamedReference::new(elem, name.clone()),
432 &prop.property_type,
433 None,
434 )
435 })
436 },
437 )
438 }
439}
440
441impl LookupObject for ElementRc {
442 fn for_each_entry<R>(
443 &self,
444 ctx: &LookupCtx,
445 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
446 ) -> Option<R> {
447 for (name, prop) in &self.borrow().property_declarations {
448 let r = expression_from_reference(
449 NamedReference::new(self, name.clone()),
450 &prop.property_type,
451 check_extra_deprecated(self, ctx, name),
452 );
453 if let Some(r) = f(name, r) {
454 return Some(r);
455 }
456 }
457 let list = self.borrow().base_type.property_list();
458 for (name, ty) in list {
459 let e = expression_from_reference(NamedReference::new(self, name.clone()), &ty, None);
460 if let Some(r) = f(&name, e) {
461 return Some(r);
462 }
463 }
464 if !(matches!(self.borrow().base_type, ElementType::Global)) {
465 for (name, ty, _) in crate::typeregister::reserved_properties() {
466 let name = SmolStr::new_static(name);
467 let e =
468 expression_from_reference(NamedReference::new(self, name.clone()), &ty, None);
469 if let Some(r) = f(&name, e) {
470 return Some(r);
471 }
472 }
473 }
474 None
475 }
476
477 fn lookup(&self, ctx: &LookupCtx, name: &SmolStr) -> Option<LookupResult> {
478 let lookup_result = self.borrow().lookup_property(name);
479 if lookup_result.property_type != Type::Invalid
480 && (lookup_result.is_local_to_component
481 || lookup_result.property_visibility != PropertyVisibility::Private)
482 {
483 let deprecated = (lookup_result.resolved_name != name.as_str())
484 .then(|| lookup_result.resolved_name.to_string())
485 .or_else(|| check_extra_deprecated(self, ctx, name));
486 Some(expression_from_reference(
487 NamedReference::new(self, lookup_result.resolved_name.to_smolstr()),
488 &lookup_result.property_type,
489 deprecated,
490 ))
491 } else {
492 None
493 }
494 }
495}
496
497pub fn check_extra_deprecated(
498 elem: &ElementRc,
499 ctx: &LookupCtx<'_>,
500 name: &SmolStr,
501) -> Option<String> {
502 if crate::typeregister::DEPRECATED_ROTATION_ORIGIN_PROPERTIES.iter().any(|(p, _)| p == name) {
503 return Some(format!("transform-origin.{}", &name[name.len() - 1..]));
504 }
505 let borrow = elem.borrow();
506 (!ctx.type_register.expose_internal_types
507 && matches!(
508 borrow.enclosing_component.upgrade().unwrap().id.as_str(),
509 "StyleMetrics" | "NativeStyleMetrics"
510 )
511 && borrow
512 .debug
513 .first()
514 .and_then(|x| x.node.source_file())
515 .map_or(true, |x| x.path().starts_with("builtin:"))
516 && !name.starts_with("layout-"))
517 .then(|| format!("Palette.{name}"))
518}
519
520fn expression_from_reference(
521 n: NamedReference,
522 ty: &Type,
523 deprecated: Option<String>,
524) -> LookupResult {
525 match ty {
526 Type::Callback { .. } => Callable::Callback(n).into(),
527 Type::InferredCallback => Callable::Callback(n).into(),
528 Type::Function { .. } => Callable::Function(n).into(),
529 _ => LookupResult::Expression { expression: Expression::PropertyReference(n), deprecated },
530 }
531}
532
533struct LookupType;
535impl LookupObject for LookupType {
536 fn for_each_entry<R>(
537 &self,
538 ctx: &LookupCtx,
539 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
540 ) -> Option<R> {
541 for (name, ty) in ctx.type_register.all_types() {
542 if let Some(r) = Self::from_type(ty).and_then(|e| f(&name, e)) {
543 return Some(r);
544 }
545 }
546 for (name, ty) in ctx.type_register.all_elements() {
547 if let Some(r) = Self::from_element(ty, ctx, &name).and_then(|e| f(&name, e)) {
548 return Some(r);
549 }
550 }
551 None
552 }
553
554 fn lookup(&self, ctx: &LookupCtx, name: &SmolStr) -> Option<LookupResult> {
555 Self::from_type(ctx.type_register.lookup(name))
556 .or_else(|| Self::from_element(ctx.type_register.lookup_element(name).ok()?, ctx, name))
557 }
558}
559impl LookupType {
560 fn from_type(ty: Type) -> Option<LookupResult> {
561 match ty {
562 Type::Enumeration(e) => Some(LookupResult::Enumeration(e)),
563 _ => None,
564 }
565 }
566
567 fn from_element(el: ElementType, ctx: &LookupCtx, name: &str) -> Option<LookupResult> {
568 match el {
569 ElementType::Component(c) if c.is_global() => {
570 if c.root_element
572 .borrow()
573 .builtin_type()
574 .is_some_and(|x| x.is_internal && x.name == name)
575 && !ctx.type_register.expose_internal_types
576 {
577 None
578 } else {
579 Some(Expression::ElementReference(Rc::downgrade(&c.root_element)).into())
580 }
581 }
582 _ => None,
583 }
584 }
585}
586
587pub struct ReturnTypeSpecificLookup;
589impl LookupObject for ReturnTypeSpecificLookup {
590 fn for_each_entry<R>(
591 &self,
592 ctx: &LookupCtx,
593 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
594 ) -> Option<R> {
595 match ctx.return_type() {
596 Type::Color => ColorSpecific.for_each_entry(ctx, f),
597 Type::Brush => ColorSpecific.for_each_entry(ctx, f),
598 Type::Easing => EasingSpecific.for_each_entry(ctx, f),
599 Type::Enumeration(enumeration) => enumeration.clone().for_each_entry(ctx, f),
600 _ => None,
601 }
602 }
603
604 fn lookup(&self, ctx: &LookupCtx, name: &SmolStr) -> Option<LookupResult> {
605 match ctx.return_type() {
606 Type::Color => ColorSpecific.lookup(ctx, name),
607 Type::Brush => ColorSpecific.lookup(ctx, name),
608 Type::Easing => EasingSpecific.lookup(ctx, name),
609 Type::Enumeration(enumeration) => enumeration.clone().lookup(ctx, name),
610 _ => None,
611 }
612 }
613}
614
615struct ColorSpecific;
616impl LookupObject for ColorSpecific {
617 fn for_each_entry<R>(
618 &self,
619 _ctx: &LookupCtx,
620 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
621 ) -> Option<R> {
622 for (name, c) in named_colors::named_colors().iter() {
623 if let Some(r) = f(&SmolStr::new_static(name), Self::as_result(*c)) {
624 return Some(r);
625 }
626 }
627 None
628 }
629 fn lookup(&self, _ctx: &LookupCtx, name: &SmolStr) -> Option<LookupResult> {
630 named_colors::named_colors().get(name.as_str()).map(|c| Self::as_result(*c))
631 }
632}
633impl ColorSpecific {
634 fn as_result(value: u32) -> LookupResult {
635 Expression::Cast {
636 from: Box::new(Expression::NumberLiteral(value as f64, Unit::None)),
637 to: Type::Color,
638 }
639 .into()
640 }
641}
642
643struct KeysLookup;
644
645macro_rules! special_keys_lookup {
646 ($($char:literal # $name:ident # $($qt:ident)|* # $($winit:ident $(($_pos:ident))?)|* # $($_xkb:ident)|*;)*) => {
647 impl LookupObject for KeysLookup {
648 fn for_each_entry<R>(
649 &self,
650 _ctx: &LookupCtx,
651 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
652 ) -> Option<R> {
653 None
654 $(.or_else(|| {
655 let mut tmp = [0; 4];
656 f(&SmolStr::new_static(stringify!($name)), Expression::StringLiteral(SmolStr::new_inline($char.encode_utf8(&mut tmp))).into())
657 }))*
658 }
659 }
660 };
661}
662
663i_slint_common::for_each_special_keys!(special_keys_lookup);
664
665struct EasingSpecific;
666impl LookupObject for EasingSpecific {
667 fn for_each_entry<R>(
668 &self,
669 _ctx: &LookupCtx,
670 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
671 ) -> Option<R> {
672 use EasingCurve::CubicBezier;
673 let mut curve = |n, e| f(&SmolStr::new_static(n), Expression::EasingCurve(e).into());
674 let r = None
675 .or_else(|| curve("linear", EasingCurve::Linear))
676 .or_else(|| curve("ease-in-quad", CubicBezier(0.11, 0.0, 0.5, 0.0)))
677 .or_else(|| curve("ease-out-quad", CubicBezier(0.5, 1.0, 0.89, 1.0)))
678 .or_else(|| curve("ease-in-out-quad", CubicBezier(0.45, 0.0, 0.55, 1.0)))
679 .or_else(|| curve("ease", CubicBezier(0.25, 0.1, 0.25, 1.0)))
680 .or_else(|| curve("ease-in", CubicBezier(0.42, 0.0, 1.0, 1.0)))
681 .or_else(|| curve("ease-in-out", CubicBezier(0.42, 0.0, 0.58, 1.0)))
682 .or_else(|| curve("ease-out", CubicBezier(0.0, 0.0, 0.58, 1.0)))
683 .or_else(|| curve("ease-in-quart", CubicBezier(0.5, 0.0, 0.75, 0.0)))
684 .or_else(|| curve("ease-out-quart", CubicBezier(0.25, 1.0, 0.5, 1.0)))
685 .or_else(|| curve("ease-in-out-quart", CubicBezier(0.76, 0.0, 0.24, 1.0)))
686 .or_else(|| curve("ease-in-quint", CubicBezier(0.64, 0.0, 0.78, 0.0)))
687 .or_else(|| curve("ease-out-quint", CubicBezier(0.22, 1.0, 0.36, 1.0)))
688 .or_else(|| curve("ease-in-out-quint", CubicBezier(0.83, 0.0, 0.17, 1.0)))
689 .or_else(|| curve("ease-in-expo", CubicBezier(0.7, 0.0, 0.84, 0.0)))
690 .or_else(|| curve("ease-out-expo", CubicBezier(0.16, 1.0, 0.3, 1.0)))
691 .or_else(|| curve("ease-in-out-expo", CubicBezier(0.87, 0.0, 0.13, 1.0)))
692 .or_else(|| curve("ease-in-back", CubicBezier(0.36, 0.0, 0.66, -0.56)))
693 .or_else(|| curve("ease-out-back", CubicBezier(0.34, 1.56, 0.64, 1.0)))
694 .or_else(|| curve("ease-in-out-back", CubicBezier(0.68, -0.6, 0.32, 1.6)))
695 .or_else(|| curve("ease-in-sine", CubicBezier(0.12, 0.0, 0.39, 0.0)))
696 .or_else(|| curve("ease-out-sine", CubicBezier(0.61, 1.0, 0.88, 1.0)))
697 .or_else(|| curve("ease-in-out-sine", CubicBezier(0.37, 0.0, 0.63, 1.0)))
698 .or_else(|| curve("ease-in-circ", CubicBezier(0.55, 0.0, 1.0, 0.45)))
699 .or_else(|| curve("ease-out-circ", CubicBezier(0.0, 0.55, 0.45, 1.0)))
700 .or_else(|| curve("ease-in-out-circ", CubicBezier(0.85, 0.0, 0.15, 1.0)))
701 .or_else(|| curve("ease-in-elastic", EasingCurve::EaseInElastic))
702 .or_else(|| curve("ease-out-elastic", EasingCurve::EaseOutElastic))
703 .or_else(|| curve("ease-in-out-elastic", EasingCurve::EaseInOutElastic))
704 .or_else(|| curve("ease-in-bounce", EasingCurve::EaseInBounce))
705 .or_else(|| curve("ease-out-bounce", EasingCurve::EaseOutBounce))
706 .or_else(|| curve("ease-in-out-bounce", EasingCurve::EaseInOutBounce));
707 r.or_else(|| {
708 f(&SmolStr::new_static("cubic-bezier"), BuiltinMacroFunction::CubicBezier.into())
709 })
710 }
711}
712
713impl LookupObject for Rc<Enumeration> {
714 fn for_each_entry<R>(
715 &self,
716 _ctx: &LookupCtx,
717 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
718 ) -> Option<R> {
719 for (value, name) in self.values.iter().enumerate() {
720 if let Some(r) = f(
721 name,
722 Expression::EnumerationValue(EnumerationValue { value, enumeration: self.clone() })
723 .into(),
724 ) {
725 return Some(r);
726 }
727 }
728 None
729 }
730}
731
732struct MathFunctions;
733impl LookupObject for MathFunctions {
734 fn for_each_entry<R>(
735 &self,
736 _ctx: &LookupCtx,
737 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
738 ) -> Option<R> {
739 let mut f = |n, e| f(&SmolStr::new_static(n), e);
740 let b = |b| LookupResult::from(Callable::Builtin(b));
741 None.or_else(|| f("mod", BuiltinMacroFunction::Mod.into()))
742 .or_else(|| f("round", b(BuiltinFunction::Round)))
743 .or_else(|| f("ceil", b(BuiltinFunction::Ceil)))
744 .or_else(|| f("floor", b(BuiltinFunction::Floor)))
745 .or_else(|| f("clamp", BuiltinMacroFunction::Clamp.into()))
746 .or_else(|| f("abs", BuiltinMacroFunction::Abs.into()))
747 .or_else(|| f("sqrt", b(BuiltinFunction::Sqrt)))
748 .or_else(|| f("max", BuiltinMacroFunction::Max.into()))
749 .or_else(|| f("min", BuiltinMacroFunction::Min.into()))
750 .or_else(|| f("sin", b(BuiltinFunction::Sin)))
751 .or_else(|| f("cos", b(BuiltinFunction::Cos)))
752 .or_else(|| f("tan", b(BuiltinFunction::Tan)))
753 .or_else(|| f("asin", b(BuiltinFunction::ASin)))
754 .or_else(|| f("acos", b(BuiltinFunction::ACos)))
755 .or_else(|| f("atan", b(BuiltinFunction::ATan)))
756 .or_else(|| f("atan2", b(BuiltinFunction::ATan2)))
757 .or_else(|| f("log", b(BuiltinFunction::Log)))
758 .or_else(|| f("ln", b(BuiltinFunction::Ln)))
759 .or_else(|| f("pow", b(BuiltinFunction::Pow)))
760 .or_else(|| f("exp", b(BuiltinFunction::Exp)))
761 .or_else(|| f("sign", BuiltinMacroFunction::Sign.into()))
762 }
763}
764
765struct SlintInternal;
766impl LookupObject for SlintInternal {
767 fn for_each_entry<R>(
768 &self,
769 ctx: &LookupCtx,
770 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
771 ) -> Option<R> {
772 let sl = || ctx.current_token.as_ref().map(|t| t.to_source_location());
773 let mut f = |n, e: LookupResult| f(&SmolStr::new_static(n), e);
774 let b = |b| LookupResult::from(Callable::Builtin(b));
775 None.or_else(|| {
776 let style = ctx.type_loader.and_then(|tl| tl.compiler_config.style.as_ref());
777 f(
778 "color-scheme",
779 if style.is_some_and(|s| s.ends_with("-light")) {
780 let e = crate::typeregister::BUILTIN.with(|e| e.enums.ColorScheme.clone());
781 Expression::EnumerationValue(e.try_value_from_string("light").unwrap())
782 } else if style.is_some_and(|s| s.ends_with("-dark")) {
783 let e = crate::typeregister::BUILTIN.with(|e| e.enums.ColorScheme.clone());
784 Expression::EnumerationValue(e.try_value_from_string("dark").unwrap())
785 } else {
786 Expression::FunctionCall {
787 function: BuiltinFunction::ColorScheme.into(),
788 arguments: vec![],
789 source_location: sl(),
790 }
791 }
792 .into(),
793 )
794 })
795 .or_else(|| {
796 f(
797 "use-24-hour-format",
798 Expression::FunctionCall {
799 function: BuiltinFunction::Use24HourFormat.into(),
800 arguments: vec![],
801 source_location: sl(),
802 }
803 .into(),
804 )
805 })
806 .or_else(|| f("month-day-count", b(BuiltinFunction::MonthDayCount)))
807 .or_else(|| f("month-offset", b(BuiltinFunction::MonthOffset)))
808 .or_else(|| f("format-date", b(BuiltinFunction::FormatDate)))
809 .or_else(|| f("date-now", b(BuiltinFunction::DateNow)))
810 .or_else(|| f("valid-date", b(BuiltinFunction::ValidDate)))
811 .or_else(|| f("parse-date", b(BuiltinFunction::ParseDate)))
812 }
813}
814
815struct ColorFunctions;
816impl LookupObject for ColorFunctions {
817 fn for_each_entry<R>(
818 &self,
819 _ctx: &LookupCtx,
820 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
821 ) -> Option<R> {
822 let mut f = |n, m| f(&SmolStr::new_static(n), LookupResult::from(m));
823 None.or_else(|| f("rgb", BuiltinMacroFunction::Rgb))
824 .or_else(|| f("rgba", BuiltinMacroFunction::Rgb))
825 .or_else(|| f("hsv", BuiltinMacroFunction::Hsv))
826 }
827}
828
829struct BuiltinFunctionLookup;
830impl LookupObject for BuiltinFunctionLookup {
831 fn for_each_entry<R>(
832 &self,
833 ctx: &LookupCtx,
834 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
835 ) -> Option<R> {
836 (MathFunctions, ColorFunctions)
837 .for_each_entry(ctx, f)
838 .or_else(|| f(&SmolStr::new_static("debug"), BuiltinMacroFunction::Debug.into()))
839 .or_else(|| {
840 f(&SmolStr::new_static("animation-tick"), BuiltinFunction::AnimationTick.into())
841 })
842 }
843}
844
845struct BuiltinNamespaceLookup;
846impl LookupObject for BuiltinNamespaceLookup {
847 fn for_each_entry<R>(
848 &self,
849 ctx: &LookupCtx,
850 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
851 ) -> Option<R> {
852 let mut f = |s, res| f(&SmolStr::new_static(s), res);
853 None.or_else(|| f("Colors", LookupResult::Namespace(BuiltinNamespace::Colors)))
854 .or_else(|| f("Easing", LookupResult::Namespace(BuiltinNamespace::Easing)))
855 .or_else(|| f("Math", LookupResult::Namespace(BuiltinNamespace::Math)))
856 .or_else(|| f("Key", LookupResult::Namespace(BuiltinNamespace::Key)))
857 .or_else(|| {
858 if ctx.type_register.expose_internal_types {
859 f("SlintInternal", LookupResult::Namespace(BuiltinNamespace::SlintInternal))
860 } else {
861 None
862 }
863 })
864 }
865}
866
867pub fn global_lookup() -> impl LookupObject {
868 (
869 LocalVariableLookup,
870 (
871 ArgumentsLookup,
872 (
873 SpecialIdLookup,
874 (
875 IdLookup,
876 (
877 InScopeLookup,
878 (
879 LookupType,
880 (
881 BuiltinNamespaceLookup,
882 (ReturnTypeSpecificLookup, BuiltinFunctionLookup),
883 ),
884 ),
885 ),
886 ),
887 ),
888 ),
889 )
890}
891
892impl LookupObject for Expression {
893 fn for_each_entry<R>(
894 &self,
895 ctx: &LookupCtx,
896 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
897 ) -> Option<R> {
898 match self {
899 Expression::ElementReference(e) => e.upgrade().unwrap().for_each_entry(ctx, f),
900 _ => match self.ty() {
901 Type::Struct(s) => {
902 for name in s.fields.keys() {
903 if let Some(r) = f(
904 name,
905 Expression::StructFieldAccess {
906 base: Box::new(self.clone()),
907 name: name.clone(),
908 }
909 .into(),
910 ) {
911 return Some(r);
912 }
913 }
914 None
915 }
916 Type::String => StringExpression(self).for_each_entry(ctx, f),
917 Type::Brush | Type::Color => ColorExpression(self).for_each_entry(ctx, f),
918 Type::Image => ImageExpression(self).for_each_entry(ctx, f),
919 Type::Array(_) => ArrayExpression(self).for_each_entry(ctx, f),
920 Type::Float32 | Type::Int32 | Type::Percent => {
921 NumberExpression(self).for_each_entry(ctx, f)
922 }
923 ty if ty.as_unit_product().is_some() => {
924 NumberWithUnitExpression(self).for_each_entry(ctx, f)
925 }
926 _ => None,
927 },
928 }
929 }
930
931 fn lookup(&self, ctx: &LookupCtx, name: &SmolStr) -> Option<LookupResult> {
932 match self {
933 Expression::ElementReference(e) => e.upgrade().unwrap().lookup(ctx, name),
934 _ => match self.ty() {
935 Type::Struct(s) => s.fields.contains_key(name).then(|| {
936 LookupResult::from(Expression::StructFieldAccess {
937 base: Box::new(self.clone()),
938 name: name.clone(),
939 })
940 }),
941 Type::String => StringExpression(self).lookup(ctx, name),
942 Type::Brush | Type::Color => ColorExpression(self).lookup(ctx, name),
943 Type::Image => ImageExpression(self).lookup(ctx, name),
944 Type::Array(_) => ArrayExpression(self).lookup(ctx, name),
945 Type::Float32 | Type::Int32 | Type::Percent => {
946 NumberExpression(self).lookup(ctx, name)
947 }
948 ty if ty.as_unit_product().is_some() => {
949 NumberWithUnitExpression(self).lookup(ctx, name)
950 }
951 _ => None,
952 },
953 }
954 }
955}
956
957struct StringExpression<'a>(&'a Expression);
958impl LookupObject for StringExpression<'_> {
959 fn for_each_entry<R>(
960 &self,
961 ctx: &LookupCtx,
962 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
963 ) -> Option<R> {
964 let member_function = |f: BuiltinFunction| {
965 LookupResult::Callable(LookupResultCallable::MemberFunction {
966 base: self.0.clone(),
967 base_node: ctx.current_token.clone(), member: LookupResultCallable::Callable(Callable::Builtin(f)).into(),
969 })
970 };
971 let function_call = |f: BuiltinFunction| {
972 LookupResult::from(Expression::FunctionCall {
973 function: Callable::Builtin(f),
974 source_location: ctx.current_token.as_ref().map(|t| t.to_source_location()),
975 arguments: vec![self.0.clone()],
976 })
977 };
978
979 let mut f = |s, res| f(&SmolStr::new_static(s), res);
980 None.or_else(|| f("is-float", member_function(BuiltinFunction::StringIsFloat)))
981 .or_else(|| f("to-float", member_function(BuiltinFunction::StringToFloat)))
982 .or_else(|| f("is-empty", function_call(BuiltinFunction::StringIsEmpty)))
983 .or_else(|| f("character-count", function_call(BuiltinFunction::StringCharacterCount)))
984 .or_else(|| f("to-lowercase", member_function(BuiltinFunction::StringToLowercase)))
985 .or_else(|| f("to-uppercase", member_function(BuiltinFunction::StringToUppercase)))
986 }
987}
988struct ColorExpression<'a>(&'a Expression);
989impl LookupObject for ColorExpression<'_> {
990 fn for_each_entry<R>(
991 &self,
992 ctx: &LookupCtx,
993 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
994 ) -> Option<R> {
995 let member_function = |f: BuiltinFunction| {
996 let base = if f == BuiltinFunction::ColorHsvaStruct && self.0.ty() == Type::Brush {
997 Expression::Cast { from: Box::new(self.0.clone()), to: Type::Color }
998 } else {
999 self.0.clone()
1000 };
1001 LookupResult::Callable(LookupResultCallable::MemberFunction {
1002 base,
1003 base_node: ctx.current_token.clone(), member: Box::new(LookupResultCallable::Callable(Callable::Builtin(f))),
1005 })
1006 };
1007 let field_access = |f: &'static str| {
1008 let base = if self.0.ty() == Type::Brush {
1009 Expression::Cast { from: Box::new(self.0.clone()), to: Type::Color }
1010 } else {
1011 self.0.clone()
1012 };
1013 LookupResult::from(Expression::StructFieldAccess {
1014 base: Box::new(Expression::FunctionCall {
1015 function: BuiltinFunction::ColorRgbaStruct.into(),
1016 source_location: ctx.current_token.as_ref().map(|t| t.to_source_location()),
1017 arguments: vec![base],
1018 }),
1019 name: SmolStr::new_static(f),
1020 })
1021 };
1022
1023 let mut f = |s, res| f(&SmolStr::new_static(s), res);
1024 None.or_else(|| f("red", field_access("red")))
1025 .or_else(|| f("green", field_access("green")))
1026 .or_else(|| f("blue", field_access("blue")))
1027 .or_else(|| f("alpha", field_access("alpha")))
1028 .or_else(|| f("to-hsv", member_function(BuiltinFunction::ColorHsvaStruct)))
1029 .or_else(|| f("brighter", member_function(BuiltinFunction::ColorBrighter)))
1030 .or_else(|| f("darker", member_function(BuiltinFunction::ColorDarker)))
1031 .or_else(|| f("transparentize", member_function(BuiltinFunction::ColorTransparentize)))
1032 .or_else(|| f("with-alpha", member_function(BuiltinFunction::ColorWithAlpha)))
1033 .or_else(|| f("mix", member_function(BuiltinFunction::ColorMix)))
1034 }
1035}
1036
1037struct ImageExpression<'a>(&'a Expression);
1038impl LookupObject for ImageExpression<'_> {
1039 fn for_each_entry<R>(
1040 &self,
1041 ctx: &LookupCtx,
1042 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
1043 ) -> Option<R> {
1044 let field_access = |f: &str| {
1045 LookupResult::from(Expression::StructFieldAccess {
1046 base: Box::new(Expression::FunctionCall {
1047 function: BuiltinFunction::ImageSize.into(),
1048 source_location: ctx.current_token.as_ref().map(|t| t.to_source_location()),
1049 arguments: vec![self.0.clone()],
1050 }),
1051 name: f.into(),
1052 })
1053 };
1054 let mut f = |s, res| f(&SmolStr::new_static(s), res);
1055 None.or_else(|| f("width", field_access("width")))
1056 .or_else(|| f("height", field_access("height")))
1057 }
1058}
1059
1060struct ArrayExpression<'a>(&'a Expression);
1061impl LookupObject for ArrayExpression<'_> {
1062 fn for_each_entry<R>(
1063 &self,
1064 ctx: &LookupCtx,
1065 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
1066 ) -> Option<R> {
1067 let member_function = |f: BuiltinFunction| {
1068 LookupResult::from(Expression::FunctionCall {
1069 function: Callable::Builtin(f),
1070 source_location: ctx.current_token.as_ref().map(|t| t.to_source_location()),
1071 arguments: vec![self.0.clone()],
1072 })
1073 };
1074 None.or_else(|| {
1075 f(&SmolStr::new_static("length"), member_function(BuiltinFunction::ArrayLength))
1076 })
1077 }
1078}
1079
1080struct NumberExpression<'a>(&'a Expression);
1082impl LookupObject for NumberExpression<'_> {
1083 fn for_each_entry<R>(
1084 &self,
1085 ctx: &LookupCtx,
1086 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
1087 ) -> Option<R> {
1088 let member = |f: LookupResultCallable| {
1089 LookupResult::Callable(LookupResultCallable::MemberFunction {
1090 base: self.0.clone(),
1091 base_node: ctx.current_token.clone(), member: f.into(),
1093 })
1094 };
1095 let member_function = |f| member(LookupResultCallable::Callable(Callable::Builtin(f)));
1096 let member_macro = |f| member(LookupResultCallable::Macro(f));
1097
1098 let mut f2 = |s, res| f(&SmolStr::new_static(s), res);
1099 None.or_else(|| f2("round", member_function(BuiltinFunction::Round)))
1100 .or_else(|| f2("ceil", member_function(BuiltinFunction::Ceil)))
1101 .or_else(|| f2("floor", member_function(BuiltinFunction::Floor)))
1102 .or_else(|| f2("sqrt", member_function(BuiltinFunction::Sqrt)))
1103 .or_else(|| f2("asin", member_function(BuiltinFunction::ASin)))
1104 .or_else(|| f2("acos", member_function(BuiltinFunction::ACos)))
1105 .or_else(|| f2("atan", member_function(BuiltinFunction::ATan)))
1106 .or_else(|| f2("log", member_function(BuiltinFunction::Log)))
1107 .or_else(|| f2("ln", member_function(BuiltinFunction::Ln)))
1108 .or_else(|| f2("pow", member_function(BuiltinFunction::Pow)))
1109 .or_else(|| f2("exp", member_function(BuiltinFunction::Exp)))
1110 .or_else(|| f2("sign", member_macro(BuiltinMacroFunction::Sign)))
1111 .or_else(|| f2("to-fixed", member_function(BuiltinFunction::ToFixed)))
1112 .or_else(|| f2("to-precision", member_function(BuiltinFunction::ToPrecision)))
1113 .or_else(|| NumberWithUnitExpression(self.0).for_each_entry(ctx, f))
1114 }
1115}
1116
1117struct NumberWithUnitExpression<'a>(&'a Expression);
1119impl LookupObject for NumberWithUnitExpression<'_> {
1120 fn for_each_entry<R>(
1121 &self,
1122 ctx: &LookupCtx,
1123 f: &mut impl FnMut(&SmolStr, LookupResult) -> Option<R>,
1124 ) -> Option<R> {
1125 let member_macro = |f: BuiltinMacroFunction| {
1126 LookupResult::Callable(LookupResultCallable::MemberFunction {
1127 base: self.0.clone(),
1128 base_node: ctx.current_token.clone(), member: Box::new(LookupResultCallable::Macro(f)),
1130 })
1131 };
1132
1133 let mut f = |s, res| f(&SmolStr::new_static(s), res);
1134 None.or_else(|| f("mod", member_macro(BuiltinMacroFunction::Mod)))
1135 .or_else(|| f("clamp", member_macro(BuiltinMacroFunction::Clamp)))
1136 .or_else(|| f("abs", member_macro(BuiltinMacroFunction::Abs)))
1137 .or_else(|| f("max", member_macro(BuiltinMacroFunction::Max)))
1138 .or_else(|| f("min", member_macro(BuiltinMacroFunction::Min)))
1139 .or_else(|| {
1140 if self.0.ty() != Type::Angle {
1141 return None;
1142 }
1143 let member_function = |f: BuiltinFunction| {
1144 LookupResult::Callable(LookupResultCallable::MemberFunction {
1145 base: self.0.clone(),
1146 base_node: ctx.current_token.clone(), member: Box::new(LookupResultCallable::Callable(Callable::Builtin(f))),
1148 })
1149 };
1150 None.or_else(|| f("sin", member_function(BuiltinFunction::Sin)))
1151 .or_else(|| f("cos", member_function(BuiltinFunction::Cos)))
1152 .or_else(|| f("tan", member_function(BuiltinFunction::Tan)))
1153 })
1154 }
1155}