1use std::rc::Rc;
7
8use crate::diagnostics::{BuildDiagnostics, Spanned};
9use crate::expression_tree::{
10 BuiltinFunction, BuiltinMacroFunction, EasingCurve, Expression, Unit,
11};
12use crate::langtype::{Enumeration, EnumerationValue, Type};
13use crate::namedreference::NamedReference;
14use crate::object_tree::{find_parent_element, ElementRc};
15use crate::parser::NodeOrToken;
16use crate::typeregister::TypeRegister;
17
18pub struct LookupCtx<'a> {
20 pub property_name: Option<&'a str>,
22
23 pub property_type: Type,
26
27 pub component_scope: &'a [ElementRc],
29
30 pub diag: &'a mut BuildDiagnostics,
32
33 pub arguments: Vec<String>,
35
36 pub type_register: &'a TypeRegister,
38
39 pub type_loader: Option<&'a crate::typeloader::TypeLoader<'a>>,
42
43 pub current_token: Option<NodeOrToken>,
45}
46
47impl<'a> LookupCtx<'a> {
48 pub fn empty_context(type_register: &'a TypeRegister, diag: &'a mut BuildDiagnostics) -> Self {
50 Self {
51 property_name: Default::default(),
52 property_type: Default::default(),
53 component_scope: Default::default(),
54 diag,
55 arguments: Default::default(),
56 type_register,
57 type_loader: None,
58 current_token: None,
59 }
60 }
61
62 pub fn return_type(&self) -> &Type {
63 if let Type::Callback { return_type, .. } = &self.property_type {
64 return_type.as_ref().map_or(&Type::Void, |b| &(**b))
65 } else {
66 &self.property_type
67 }
68 }
69}
70
71pub enum LookupResult {
72 Expression {
73 expression: Expression,
74 deprecated: Option<String>,
76 },
77 Enumeration(Rc<Enumeration>),
78 Namespace(BuiltinNamespace),
79}
80
81pub enum BuiltinNamespace {
82 Colors,
83 Math,
84 Keys,
85}
86
87impl From<Expression> for LookupResult {
88 fn from(expression: Expression) -> Self {
89 Self::Expression { expression, deprecated: None }
90 }
91}
92
93impl LookupResult {
94 pub fn deprecated(&self) -> Option<&str> {
95 match self {
96 Self::Expression { deprecated: Some(x), .. } => Some(x.as_str()),
97 _ => None,
98 }
99 }
100}
101
102pub trait LookupObject {
104 fn for_each_entry<R>(
107 &self,
108 ctx: &LookupCtx,
109 f: &mut impl FnMut(&str, LookupResult) -> Option<R>,
110 ) -> Option<R>;
111
112 fn lookup(&self, ctx: &LookupCtx, name: &str) -> Option<LookupResult> {
115 self.for_each_entry(ctx, &mut |prop, expr| (prop == name).then(|| expr))
116 }
117}
118
119impl<T1: LookupObject, T2: LookupObject> LookupObject for (T1, T2) {
120 fn for_each_entry<R>(
121 &self,
122 ctx: &LookupCtx,
123 f: &mut impl FnMut(&str, LookupResult) -> Option<R>,
124 ) -> Option<R> {
125 self.0.for_each_entry(ctx, f).or_else(|| self.1.for_each_entry(ctx, f))
126 }
127
128 fn lookup(&self, ctx: &LookupCtx, name: &str) -> Option<LookupResult> {
129 self.0.lookup(ctx, name).or_else(|| self.1.lookup(ctx, name))
130 }
131}
132
133impl LookupObject for LookupResult {
134 fn for_each_entry<R>(
135 &self,
136 ctx: &LookupCtx,
137 f: &mut impl FnMut(&str, LookupResult) -> Option<R>,
138 ) -> Option<R> {
139 match self {
140 LookupResult::Expression { expression, .. } => expression.for_each_entry(ctx, f),
141 LookupResult::Enumeration(e) => e.for_each_entry(ctx, f),
142 LookupResult::Namespace(BuiltinNamespace::Colors) => {
143 (ColorSpecific, ColorFunctions).for_each_entry(ctx, f)
144 }
145 LookupResult::Namespace(BuiltinNamespace::Math) => MathFunctions.for_each_entry(ctx, f),
146 LookupResult::Namespace(BuiltinNamespace::Keys) => KeysLookup.for_each_entry(ctx, f),
147 }
148 }
149
150 fn lookup(&self, ctx: &LookupCtx, name: &str) -> Option<LookupResult> {
151 match self {
152 LookupResult::Expression { expression, .. } => expression.lookup(ctx, name),
153 LookupResult::Enumeration(e) => e.lookup(ctx, name),
154 LookupResult::Namespace(BuiltinNamespace::Colors) => {
155 (ColorSpecific, ColorFunctions).lookup(ctx, name)
156 }
157 LookupResult::Namespace(BuiltinNamespace::Math) => MathFunctions.lookup(ctx, name),
158 LookupResult::Namespace(BuiltinNamespace::Keys) => KeysLookup.lookup(ctx, name),
159 }
160 }
161}
162
163struct ArgumentsLookup;
164impl LookupObject for ArgumentsLookup {
165 fn for_each_entry<R>(
166 &self,
167 ctx: &LookupCtx,
168 f: &mut impl FnMut(&str, LookupResult) -> Option<R>,
169 ) -> Option<R> {
170 let args = match &ctx.property_type {
171 Type::Callback { args, .. } | Type::Function { args, .. } => args,
172 _ => return None,
173 };
174 for (index, (name, ty)) in ctx.arguments.iter().zip(args.iter()).enumerate() {
175 if let Some(r) =
176 f(name, Expression::FunctionParameterReference { index, ty: ty.clone() }.into())
177 {
178 return Some(r);
179 }
180 }
181 None
182 }
183}
184
185struct SpecialIdLookup;
186impl LookupObject for SpecialIdLookup {
187 fn for_each_entry<R>(
188 &self,
189 ctx: &LookupCtx,
190 f: &mut impl FnMut(&str, LookupResult) -> Option<R>,
191 ) -> Option<R> {
192 let last = ctx.component_scope.last();
193 None.or_else(|| f("self", Expression::ElementReference(Rc::downgrade(last?)).into()))
194 .or_else(|| {
195 f(
196 "parent",
197 Expression::ElementReference(Rc::downgrade(&find_parent_element(last?)?))
198 .into(),
199 )
200 })
201 .or_else(|| f("true", Expression::BoolLiteral(true).into()))
202 .or_else(|| f("false", Expression::BoolLiteral(false).into()))
203 }
205}
206
207struct IdLookup;
208impl LookupObject for IdLookup {
209 fn for_each_entry<R>(
210 &self,
211 ctx: &LookupCtx,
212 f: &mut impl FnMut(&str, LookupResult) -> Option<R>,
213 ) -> Option<R> {
214 fn visit<R>(
215 roots: &[ElementRc],
216 f: &mut impl FnMut(&str, LookupResult) -> Option<R>,
217 ) -> Option<R> {
218 for e in roots.iter().rev() {
219 if !e.borrow().id.is_empty() {
220 if let Some(r) =
221 f(&e.borrow().id, Expression::ElementReference(Rc::downgrade(e)).into())
222 {
223 return Some(r);
224 }
225 }
226 for x in &e.borrow().children {
227 if x.borrow().repeated.is_some() {
228 continue;
229 }
230 if let Some(r) = visit(&[x.clone()], f) {
231 return Some(r);
232 }
233 }
234 }
235 None
236 }
237 visit(ctx.component_scope, f)
238 }
239 }
241
242struct InScopeLookup;
243impl LookupObject for InScopeLookup {
244 fn for_each_entry<R>(
245 &self,
246 ctx: &LookupCtx,
247 f: &mut impl FnMut(&str, LookupResult) -> Option<R>,
248 ) -> Option<R> {
249 for elem in ctx.component_scope.iter().rev() {
250 if let Some(repeated) = &elem.borrow().repeated {
251 if !repeated.index_id.is_empty() {
252 if let Some(r) = f(
253 &repeated.index_id,
254 Expression::RepeaterIndexReference { element: Rc::downgrade(elem) }.into(),
255 ) {
256 return Some(r);
257 }
258 }
259 if !repeated.model_data_id.is_empty() {
260 if let Some(r) = f(
261 &repeated.model_data_id,
262 Expression::RepeaterIndexReference { element: Rc::downgrade(elem) }.into(),
263 ) {
264 return Some(r);
265 }
266 }
267 }
268
269 if let Some(r) = elem.for_each_entry(ctx, f) {
270 return Some(r);
271 }
272 }
273 None
274 }
275
276 fn lookup(&self, ctx: &LookupCtx, name: &str) -> Option<LookupResult> {
277 if name.is_empty() {
278 return None;
279 }
280 for elem in ctx.component_scope.iter().rev() {
281 if let Some(repeated) = &elem.borrow().repeated {
282 if repeated.index_id == name {
283 return Some(LookupResult::from(Expression::RepeaterIndexReference {
284 element: Rc::downgrade(elem),
285 }));
286 }
287 if repeated.model_data_id == name {
288 return Some(LookupResult::from(Expression::RepeaterModelReference {
289 element: Rc::downgrade(elem),
290 }));
291 }
292 }
293
294 if let Some(r) = elem.lookup(ctx, name) {
295 return Some(r);
296 }
297 }
298 None
299 }
300}
301
302impl LookupObject for ElementRc {
303 fn for_each_entry<R>(
304 &self,
305 _ctx: &LookupCtx,
306 f: &mut impl FnMut(&str, LookupResult) -> Option<R>,
307 ) -> Option<R> {
308 for (name, prop) in &self.borrow().property_declarations {
309 let e = expression_from_reference(NamedReference::new(self, name), &prop.property_type);
310 if let Some(r) = f(name, e.into()) {
311 return Some(r);
312 }
313 }
314 let list = self.borrow().base_type.property_list();
315 for (name, ty) in list {
316 let e = expression_from_reference(NamedReference::new(self, &name), &ty);
317 if let Some(r) = f(&name, e.into()) {
318 return Some(r);
319 }
320 }
321 for (name, ty) in crate::typeregister::reserved_properties() {
322 let e = expression_from_reference(NamedReference::new(self, name), &ty);
323 if let Some(r) = f(name, e.into()) {
324 return Some(r);
325 }
326 }
327 None
328 }
329
330 fn lookup(&self, _ctx: &LookupCtx, name: &str) -> Option<LookupResult> {
331 let crate::langtype::PropertyLookupResult { resolved_name, property_type } =
332 self.borrow().lookup_property(name);
333 (property_type != Type::Invalid).then(|| LookupResult::Expression {
334 expression: expression_from_reference(
335 NamedReference::new(self, &resolved_name),
336 &property_type,
337 ),
338 deprecated: (resolved_name != name).then(|| resolved_name.to_string()),
339 })
340 }
341}
342
343fn expression_from_reference(n: NamedReference, ty: &Type) -> Expression {
344 if matches!(ty, Type::Callback { .. }) {
345 Expression::CallbackReference(n)
346 } else {
347 Expression::PropertyReference(n)
348 }
349}
350
351struct LookupType;
354impl LookupObject for LookupType {
355 fn for_each_entry<R>(
356 &self,
357 ctx: &LookupCtx,
358 f: &mut impl FnMut(&str, LookupResult) -> Option<R>,
359 ) -> Option<R> {
360 for (name, ty) in ctx.type_register.all_types() {
361 if let Some(r) = Self::as_result(ty).and_then(|e| f(&name, e)) {
362 return Some(r);
363 }
364 }
365 None
366 }
367
368 fn lookup(&self, ctx: &LookupCtx, name: &str) -> Option<LookupResult> {
369 Self::as_result(ctx.type_register.lookup(name)).map(LookupResult::from)
370 }
371}
372impl LookupType {
373 fn as_result(ty: Type) -> Option<LookupResult> {
374 match ty {
375 Type::Component(c) if c.is_global() => {
376 Some(Expression::ElementReference(Rc::downgrade(&c.root_element)).into())
377 }
378 Type::Enumeration(e) => Some(LookupResult::Enumeration(e)),
379 _ => None,
380 }
381 }
382}
383
384struct ReturnTypeSpecificLookup;
385impl LookupObject for ReturnTypeSpecificLookup {
386 fn for_each_entry<R>(
387 &self,
388 ctx: &LookupCtx,
389 f: &mut impl FnMut(&str, LookupResult) -> Option<R>,
390 ) -> Option<R> {
391 match ctx.return_type() {
392 Type::Color => ColorSpecific.for_each_entry(ctx, f),
393 Type::Brush => ColorSpecific.for_each_entry(ctx, f),
394 Type::Easing => EasingSpecific.for_each_entry(ctx, f),
395 Type::Enumeration(enumeration) => enumeration.clone().for_each_entry(ctx, f),
396 _ => None,
397 }
398 }
399
400 fn lookup(&self, ctx: &LookupCtx, name: &str) -> Option<LookupResult> {
401 match ctx.return_type() {
402 Type::Color => ColorSpecific.lookup(ctx, name),
403 Type::Brush => ColorSpecific.lookup(ctx, name),
404 Type::Easing => EasingSpecific.lookup(ctx, name),
405 Type::Enumeration(enumeration) => enumeration.clone().lookup(ctx, name),
406 _ => None,
407 }
408 }
409}
410
411struct ColorSpecific;
412impl LookupObject for ColorSpecific {
413 fn for_each_entry<R>(
414 &self,
415 _ctx: &LookupCtx,
416 f: &mut impl FnMut(&str, LookupResult) -> Option<R>,
417 ) -> Option<R> {
418 for (name, c) in css_color_parser2::NAMED_COLORS.iter() {
419 if let Some(r) = f(name, Self::as_result(*c)) {
420 return Some(r);
421 }
422 }
423 None
424 }
425 fn lookup(&self, _ctx: &LookupCtx, name: &str) -> Option<LookupResult> {
426 css_color_parser2::NAMED_COLORS.get(name).map(|c| Self::as_result(*c))
427 }
428}
429impl ColorSpecific {
430 fn as_result(c: css_color_parser2::Color) -> LookupResult {
431 let value =
432 ((c.a as u32 * 255) << 24) | ((c.r as u32) << 16) | ((c.g as u32) << 8) | (c.b as u32);
433 Expression::Cast {
434 from: Box::new(Expression::NumberLiteral(value as f64, Unit::None)),
435 to: Type::Color,
436 }
437 .into()
438 }
439}
440
441struct KeysLookup;
442
443macro_rules! special_keys_lookup {
444 ($($char:literal # $name:ident # $($qt:ident)|* # $($winit:ident)|* ;)*) => {
445 impl LookupObject for KeysLookup {
446 fn for_each_entry<R>(
447 &self,
448 _ctx: &LookupCtx,
449 f: &mut impl FnMut(&str, LookupResult) -> Option<R>,
450 ) -> Option<R> {
451 None
452 $(.or_else(|| {
453 f(stringify!($name), Expression::StringLiteral($char.into()).into())
454 }))*
455 }
456 }
457 };
458}
459
460sixtyfps_common::for_each_special_keys!(special_keys_lookup);
461
462struct EasingSpecific;
463impl LookupObject for EasingSpecific {
464 fn for_each_entry<R>(
465 &self,
466 ctx: &LookupCtx,
467 f: &mut impl FnMut(&str, LookupResult) -> Option<R>,
468 ) -> Option<R> {
469 use EasingCurve::CubicBezier;
470 None.or_else(|| f("linear", Expression::EasingCurve(EasingCurve::Linear).into()))
471 .or_else(|| {
472 f("ease", Expression::EasingCurve(CubicBezier(0.25, 0.1, 0.25, 1.0)).into())
473 })
474 .or_else(|| {
475 f("ease-in", Expression::EasingCurve(CubicBezier(0.42, 0.0, 1.0, 1.0)).into())
476 })
477 .or_else(|| {
478 f("ease-in-out", Expression::EasingCurve(CubicBezier(0.42, 0.0, 0.58, 1.0)).into())
479 })
480 .or_else(|| {
481 f("ease-out", Expression::EasingCurve(CubicBezier(0.0, 0.0, 0.58, 1.0)).into())
482 })
483 .or_else(|| {
484 f(
485 "cubic-bezier",
486 Expression::BuiltinMacroReference(
487 BuiltinMacroFunction::CubicBezier,
488 ctx.current_token.clone(),
489 )
490 .into(),
491 )
492 })
493 }
494}
495
496impl LookupObject for Rc<Enumeration> {
497 fn for_each_entry<R>(
498 &self,
499 _ctx: &LookupCtx,
500 f: &mut impl FnMut(&str, LookupResult) -> Option<R>,
501 ) -> Option<R> {
502 for (value, name) in self.values.iter().enumerate() {
503 if let Some(r) = f(
504 name,
505 Expression::EnumerationValue(EnumerationValue { value, enumeration: self.clone() })
506 .into(),
507 ) {
508 return Some(r);
509 }
510 }
511 None
512 }
513}
514
515struct MathFunctions;
516impl LookupObject for MathFunctions {
517 fn for_each_entry<R>(
518 &self,
519 ctx: &LookupCtx,
520 f: &mut impl FnMut(&str, LookupResult) -> Option<R>,
521 ) -> Option<R> {
522 use Expression::{BuiltinFunctionReference, BuiltinMacroReference};
523 let t = &ctx.current_token;
524 let sl = || t.as_ref().map(|t| t.to_source_location());
525 let mut f = |n, e: Expression| f(n, e.into());
526 None.or_else(|| f("mod", BuiltinFunctionReference(BuiltinFunction::Mod, sl())))
527 .or_else(|| f("round", BuiltinFunctionReference(BuiltinFunction::Round, sl())))
528 .or_else(|| f("ceil", BuiltinFunctionReference(BuiltinFunction::Ceil, sl())))
529 .or_else(|| f("floor", BuiltinFunctionReference(BuiltinFunction::Floor, sl())))
530 .or_else(|| f("abs", BuiltinFunctionReference(BuiltinFunction::Abs, sl())))
531 .or_else(|| f("sqrt", BuiltinFunctionReference(BuiltinFunction::Sqrt, sl())))
532 .or_else(|| f("max", BuiltinMacroReference(BuiltinMacroFunction::Max, t.clone())))
533 .or_else(|| f("min", BuiltinMacroReference(BuiltinMacroFunction::Min, t.clone())))
534 .or_else(|| f("sin", BuiltinFunctionReference(BuiltinFunction::Sin, sl())))
535 .or_else(|| f("cos", BuiltinFunctionReference(BuiltinFunction::Cos, sl())))
536 .or_else(|| f("tan", BuiltinFunctionReference(BuiltinFunction::Tan, sl())))
537 .or_else(|| f("asin", BuiltinFunctionReference(BuiltinFunction::ASin, sl())))
538 .or_else(|| f("acos", BuiltinFunctionReference(BuiltinFunction::ACos, sl())))
539 .or_else(|| f("atan", BuiltinFunctionReference(BuiltinFunction::ATan, sl())))
540 .or_else(|| f("log", BuiltinFunctionReference(BuiltinFunction::Log, sl())))
541 .or_else(|| f("pow", BuiltinFunctionReference(BuiltinFunction::Pow, sl())))
542 }
543}
544
545struct ColorFunctions;
546impl LookupObject for ColorFunctions {
547 fn for_each_entry<R>(
548 &self,
549 ctx: &LookupCtx,
550 f: &mut impl FnMut(&str, LookupResult) -> Option<R>,
551 ) -> Option<R> {
552 use Expression::BuiltinMacroReference;
553 let t = &ctx.current_token;
554 let mut f = |n, e: Expression| f(n, e.into());
555 None.or_else(|| f("rgb", BuiltinMacroReference(BuiltinMacroFunction::Rgb, t.clone())))
556 .or_else(|| f("rgba", BuiltinMacroReference(BuiltinMacroFunction::Rgb, t.clone())))
557 }
558}
559
560struct BuiltinFunctionLookup;
561impl LookupObject for BuiltinFunctionLookup {
562 fn for_each_entry<R>(
563 &self,
564 ctx: &LookupCtx,
565 f: &mut impl FnMut(&str, LookupResult) -> Option<R>,
566 ) -> Option<R> {
567 (MathFunctions, ColorFunctions).for_each_entry(ctx, f).or_else(|| {
568 f(
569 "debug",
570 Expression::BuiltinMacroReference(
571 BuiltinMacroFunction::Debug,
572 ctx.current_token.clone(),
573 )
574 .into(),
575 )
576 })
577 }
578}
579
580struct BuiltinNamespaceLookup;
581impl LookupObject for BuiltinNamespaceLookup {
582 fn for_each_entry<R>(
583 &self,
584 _ctx: &LookupCtx,
585 f: &mut impl FnMut(&str, LookupResult) -> Option<R>,
586 ) -> Option<R> {
587 None.or_else(|| f("Colors", LookupResult::Namespace(BuiltinNamespace::Colors)))
588 .or_else(|| f("Math", LookupResult::Namespace(BuiltinNamespace::Math)))
589 .or_else(|| f("Keys", LookupResult::Namespace(BuiltinNamespace::Keys)))
590 }
591}
592
593pub fn global_lookup() -> impl LookupObject {
594 (
595 ArgumentsLookup,
596 (
597 SpecialIdLookup,
598 (
599 IdLookup,
600 (
601 InScopeLookup,
602 (
603 LookupType,
604 (BuiltinNamespaceLookup, (ReturnTypeSpecificLookup, BuiltinFunctionLookup)),
605 ),
606 ),
607 ),
608 ),
609 )
610}
611
612impl LookupObject for Expression {
613 fn for_each_entry<R>(
614 &self,
615 ctx: &LookupCtx,
616 f: &mut impl FnMut(&str, LookupResult) -> Option<R>,
617 ) -> Option<R> {
618 match self {
619 Expression::ElementReference(e) => e.upgrade().unwrap().for_each_entry(ctx, f),
620 _ => match self.ty() {
621 Type::Struct { fields, .. } => {
622 for name in fields.keys() {
623 if let Some(r) = f(
624 name,
625 Expression::StructFieldAccess {
626 base: Box::new(self.clone()),
627 name: name.clone(),
628 }
629 .into(),
630 ) {
631 return Some(r);
632 }
633 }
634 None
635 }
636 Type::Component(c) => c.root_element.for_each_entry(ctx, f),
637 Type::String => StringExpression(self).for_each_entry(ctx, f),
638 Type::Color => ColorExpression(self).for_each_entry(ctx, f),
639 Type::Image => ImageExpression(self).for_each_entry(ctx, f),
640 Type::Array(_) => ArrayExpression(self).for_each_entry(ctx, f),
641 _ => None,
642 },
643 }
644 }
645
646 fn lookup(&self, ctx: &LookupCtx, name: &str) -> Option<LookupResult> {
647 match self {
648 Expression::ElementReference(e) => e.upgrade().unwrap().lookup(ctx, name),
649 _ => match self.ty() {
650 Type::Struct { fields, .. } => fields.contains_key(name).then(|| {
651 LookupResult::from(Expression::StructFieldAccess {
652 base: Box::new(self.clone()),
653 name: name.to_string(),
654 })
655 }),
656 Type::Component(c) => c.root_element.lookup(ctx, name),
657 Type::String => StringExpression(self).lookup(ctx, name),
658 Type::Color => ColorExpression(self).lookup(ctx, name),
659 Type::Image => ImageExpression(self).lookup(ctx, name),
660 Type::Array(_) => ArrayExpression(self).lookup(ctx, name),
661 _ => None,
662 },
663 }
664 }
665}
666
667struct StringExpression<'a>(&'a Expression);
668impl<'a> LookupObject for StringExpression<'a> {
669 fn for_each_entry<R>(
670 &self,
671 ctx: &LookupCtx,
672 f: &mut impl FnMut(&str, LookupResult) -> Option<R>,
673 ) -> Option<R> {
674 let member_function = |f: BuiltinFunction| {
675 LookupResult::from(Expression::MemberFunction {
676 base: Box::new(self.0.clone()),
677 base_node: ctx.current_token.clone(), member: Box::new(Expression::BuiltinFunctionReference(
679 f,
680 ctx.current_token.as_ref().map(|t| t.to_source_location()),
681 )),
682 })
683 };
684 None.or_else(|| f("is-float", member_function(BuiltinFunction::StringIsFloat)))
685 .or_else(|| f("to-float", member_function(BuiltinFunction::StringToFloat)))
686 }
687}
688struct ColorExpression<'a>(&'a Expression);
689impl<'a> LookupObject for ColorExpression<'a> {
690 fn for_each_entry<R>(
691 &self,
692 ctx: &LookupCtx,
693 f: &mut impl FnMut(&str, LookupResult) -> Option<R>,
694 ) -> Option<R> {
695 let member_function = |f: BuiltinFunction| {
696 LookupResult::from(Expression::MemberFunction {
697 base: Box::new(self.0.clone()),
698 base_node: ctx.current_token.clone(), member: Box::new(Expression::BuiltinFunctionReference(
700 f,
701 ctx.current_token.as_ref().map(|t| t.to_source_location()),
702 )),
703 })
704 };
705 None.or_else(|| f("brighter", member_function(BuiltinFunction::ColorBrighter)))
706 .or_else(|| f("darker", member_function(BuiltinFunction::ColorDarker)))
707 }
708}
709
710struct ImageExpression<'a>(&'a Expression);
711impl<'a> LookupObject for ImageExpression<'a> {
712 fn for_each_entry<R>(
713 &self,
714 ctx: &LookupCtx,
715 f: &mut impl FnMut(&str, LookupResult) -> Option<R>,
716 ) -> Option<R> {
717 let field_access = |f: &str| {
718 LookupResult::from(Expression::StructFieldAccess {
719 base: Box::new(Expression::FunctionCall {
720 function: Box::new(Expression::BuiltinFunctionReference(
721 BuiltinFunction::ImageSize,
722 ctx.current_token.as_ref().map(|t| t.to_source_location()),
723 )),
724 source_location: ctx.current_token.as_ref().map(|t| t.to_source_location()),
725 arguments: vec![self.0.clone()],
726 }),
727 name: f.into(),
728 })
729 };
730 None.or_else(|| f("width", field_access("width")))
731 .or_else(|| f("height", field_access("height")))
732 }
733}
734
735struct ArrayExpression<'a>(&'a Expression);
736impl<'a> LookupObject for ArrayExpression<'a> {
737 fn for_each_entry<R>(
738 &self,
739 ctx: &LookupCtx,
740 f: &mut impl FnMut(&str, LookupResult) -> Option<R>,
741 ) -> Option<R> {
742 let member_function = |f: BuiltinFunction| {
743 LookupResult::from(Expression::FunctionCall {
744 function: Box::new(Expression::BuiltinFunctionReference(
745 f,
746 ctx.current_token.as_ref().map(|t| t.to_source_location()),
747 )),
748 source_location: ctx.current_token.as_ref().map(|t| t.to_source_location()),
749 arguments: vec![self.0.clone()],
750 })
751 };
752 None.or_else(|| f("length", member_function(BuiltinFunction::ArrayLength)))
753 }
754}