1use crate::diagnostics::{BuildDiagnostics, Spanned};
12use crate::expression_tree::*;
13use crate::langtype::{PropertyLookupResult, Type};
14use crate::lookup::{LookupCtx, LookupObject, LookupResult};
15use crate::object_tree::*;
16use crate::parser::{identifier_text, syntax_nodes, NodeOrToken, SyntaxKind, SyntaxNode};
17use crate::typeregister::TypeRegister;
18use std::collections::HashMap;
19use std::rc::Rc;
20
21#[derive(Clone)]
24struct ComponentScope(Vec<ElementRc>);
25
26fn resolve_expression(
27 expr: &mut Expression,
28 property_name: Option<&str>,
29 property_type: Type,
30 scope: &ComponentScope,
31 type_register: &TypeRegister,
32 type_loader: &crate::typeloader::TypeLoader,
33 two_ways: &mut Vec<(String, NamedReference)>,
34 diag: &mut BuildDiagnostics,
35) {
36 if let Expression::Uncompiled(node) = expr {
37 let mut lookup_ctx = LookupCtx {
38 property_name,
39 property_type,
40 component_scope: &scope.0,
41 diag,
42 arguments: vec![],
43 type_register,
44 type_loader: Some(type_loader),
45 current_token: None,
46 };
47
48 let new_expr = match node.kind() {
49 SyntaxKind::CallbackConnection => {
50 Expression::from_callback_connection(node.clone().into(), &mut lookup_ctx)
52 }
53 SyntaxKind::Expression => {
54 Expression::from_expression_node(node.clone().into(), &mut lookup_ctx)
56 .maybe_convert_to(lookup_ctx.property_type.clone(), node, diag)
57 }
58 SyntaxKind::BindingExpression => {
59 Expression::from_binding_expression_node(node.clone(), &mut lookup_ctx)
60 }
61 SyntaxKind::TwoWayBinding => {
62 if lookup_ctx.property_type == Type::Invalid {
63 assert!(diag.has_error());
65 return;
66 }
67 if let Some(nr) = resolve_two_way_binding(node.clone().into(), &mut lookup_ctx) {
68 two_ways.push((property_name.unwrap().into(), nr));
69 }
70 Expression::Invalid
71 }
72 _ => {
73 debug_assert!(diag.has_error());
74 Expression::Invalid
75 }
76 };
77 *expr = new_expr;
78 }
79}
80
81pub fn resolve_expressions(
82 doc: &Document,
83 type_loader: &crate::typeloader::TypeLoader,
84 diag: &mut BuildDiagnostics,
85) {
86 for component in doc.inner_components.iter() {
87 let scope = ComponentScope(vec![component.root_element.clone()]);
88
89 recurse_elem(&component.root_element, &scope, &mut |elem, scope| {
90 let mut new_scope = scope.clone();
91 let mut is_repeated = elem.borrow().repeated.is_some();
92 if is_repeated {
93 new_scope.0.push(elem.clone())
94 }
95 new_scope.0.push(elem.clone());
96 let mut two_ways = vec![];
97 visit_element_expressions(elem, |expr, property_name, property_type| {
98 if is_repeated {
99 debug_assert!(elem.borrow().repeated.as_ref().is_none()); let mut parent_scope = scope.clone();
102 if let Some(parent) = find_parent_element(elem) {
103 parent_scope.0.push(parent)
104 };
105 resolve_expression(
106 expr,
107 property_name,
108 property_type(),
109 &parent_scope,
110 &doc.local_registry,
111 type_loader,
112 &mut two_ways,
113 diag,
114 );
115 is_repeated = false;
116 } else {
117 resolve_expression(
118 expr,
119 property_name,
120 property_type(),
121 &new_scope,
122 &doc.local_registry,
123 type_loader,
124 &mut two_ways,
125 diag,
126 )
127 }
128 });
129 for (prop, nr) in two_ways {
130 elem.borrow().bindings.get(&prop).unwrap().borrow_mut().two_way_bindings.push(nr);
131 }
132 new_scope.0.pop();
133 new_scope
134 })
135 }
136}
137
138impl Expression {
139 pub fn from_binding_expression_node(node: SyntaxNode, ctx: &mut LookupCtx) -> Self {
140 debug_assert_eq!(node.kind(), SyntaxKind::BindingExpression);
141 let e = node
142 .child_node(SyntaxKind::Expression)
143 .map(|n| Self::from_expression_node(n.into(), ctx))
144 .or_else(|| {
145 node.child_node(SyntaxKind::CodeBlock)
146 .map(|c| Self::from_codeblock_node(c.into(), ctx))
147 })
148 .unwrap_or(Self::Invalid);
149 if ctx.property_type == Type::LogicalLength && e.ty() == Type::Percent {
150 const RELATIVE_TO_PARENT_PROPERTIES: [&str; 2] = ["width", "height"];
152 let property_name = ctx.property_name.unwrap_or_default();
153 if RELATIVE_TO_PARENT_PROPERTIES.contains(&property_name) {
154 return e;
155 } else {
156 ctx.diag.push_error(
157 format!(
158 "Automatic conversion from percentage to length is only possible for the properties {}",
159 RELATIVE_TO_PARENT_PROPERTIES.join(" and ")
160 ),
161 &node
162 );
163 return Expression::Invalid;
164 }
165 };
166 e.maybe_convert_to(ctx.property_type.clone(), &node, ctx.diag)
167 }
168
169 fn from_codeblock_node(node: syntax_nodes::CodeBlock, ctx: &mut LookupCtx) -> Expression {
170 debug_assert_eq!(node.kind(), SyntaxKind::CodeBlock);
171
172 let mut statements_or_exprs = node
173 .children()
174 .filter_map(|n| match n.kind() {
175 SyntaxKind::Expression => Some(Self::from_expression_node(n.into(), ctx)),
176 SyntaxKind::ReturnStatement => Some(Self::from_return_statement(n.into(), ctx)),
177 _ => None,
178 })
179 .collect::<Vec<_>>();
180
181 let exit_points_and_return_types = statements_or_exprs
182 .iter()
183 .enumerate()
184 .filter_map(|(index, statement_or_expr)| {
185 if index == statements_or_exprs.len()
186 || matches!(statement_or_expr, Expression::ReturnStatement(..))
187 {
188 Some((index, statement_or_expr.ty()))
189 } else {
190 None
191 }
192 })
193 .collect::<Vec<_>>();
194
195 let common_return_type = Self::common_target_type_for_type_list(
196 exit_points_and_return_types.iter().map(|(_, ty)| ty.clone()),
197 );
198
199 exit_points_and_return_types.into_iter().for_each(|(index, _)| {
200 let mut expr = std::mem::replace(&mut statements_or_exprs[index], Expression::Invalid);
201 expr = expr.maybe_convert_to(common_return_type.clone(), &node, ctx.diag);
202 statements_or_exprs[index] = expr;
203 });
204
205 Expression::CodeBlock(statements_or_exprs)
206 }
207
208 fn from_return_statement(
209 node: syntax_nodes::ReturnStatement,
210 ctx: &mut LookupCtx,
211 ) -> Expression {
212 let return_type = ctx.return_type().clone();
213 Expression::ReturnStatement(node.Expression().map(|n| {
214 Box::new(Self::from_expression_node(n, ctx).maybe_convert_to(
215 return_type,
216 &node,
217 ctx.diag,
218 ))
219 }))
220 }
221
222 fn from_callback_connection(
223 node: syntax_nodes::CallbackConnection,
224 ctx: &mut LookupCtx,
225 ) -> Expression {
226 ctx.arguments =
227 node.DeclaredIdentifier().map(|x| identifier_text(&x).unwrap_or_default()).collect();
228 Self::from_codeblock_node(node.CodeBlock(), ctx).maybe_convert_to(
229 ctx.return_type().clone(),
230 &node,
231 ctx.diag,
232 )
233 }
234
235 fn from_expression_node(node: syntax_nodes::Expression, ctx: &mut LookupCtx) -> Self {
236 node.Expression()
237 .map(|n| Self::from_expression_node(n, ctx))
238 .or_else(|| node.AtImageUrl().map(|n| Self::from_at_image_url_node(n, ctx)))
239 .or_else(|| node.AtLinearGradient().map(|n| Self::from_at_linear_gradient(n, ctx)))
240 .or_else(|| {
241 node.QualifiedName().map(|n| {
242 let exp = Self::from_qualified_name_node(n.clone(), ctx);
243 if matches!(exp.ty(), Type::Function { .. } | Type::Callback { .. }) {
244 ctx.diag.push_error(
245 format!(
246 "'{}' must be called. Did you forgot the '()'?",
247 QualifiedTypeName::from_node(n.clone())
248 ),
249 &n,
250 )
251 }
252 exp
253 })
254 })
255 .or_else(|| {
256 node.child_text(SyntaxKind::StringLiteral).map(|s| {
257 crate::literals::unescape_string(&s).map(Self::StringLiteral).unwrap_or_else(
258 || {
259 ctx.diag.push_error("Cannot parse string literal".into(), &node);
260 Self::Invalid
261 },
262 )
263 })
264 })
265 .or_else(|| {
266 node.child_text(SyntaxKind::NumberLiteral)
267 .map(crate::literals::parse_number_literal)
268 .transpose()
269 .unwrap_or_else(|e| {
270 ctx.diag.push_error(e, &node);
271 Some(Self::Invalid)
272 })
273 })
274 .or_else(|| {
275 node.child_text(SyntaxKind::ColorLiteral).map(|s| {
276 crate::literals::parse_color_literal(&s)
277 .map(|i| Expression::Cast {
278 from: Box::new(Expression::NumberLiteral(i as _, Unit::None)),
279 to: Type::Color,
280 })
281 .unwrap_or_else(|| {
282 ctx.diag.push_error("Invalid color literal".into(), &node);
283 Self::Invalid
284 })
285 })
286 })
287 .or_else(|| {
288 node.FunctionCallExpression().map(|n| Self::from_function_call_node(n, ctx))
289 })
290 .or_else(|| node.MemberAccess().map(|n| Self::from_member_access_node(n, ctx)))
291 .or_else(|| node.IndexExpression().map(|n| Self::from_index_expression_node(n, ctx)))
292 .or_else(|| node.SelfAssignment().map(|n| Self::from_self_assignment_node(n, ctx)))
293 .or_else(|| node.BinaryExpression().map(|n| Self::from_binary_expression_node(n, ctx)))
294 .or_else(|| {
295 node.UnaryOpExpression().map(|n| Self::from_unaryop_expression_node(n, ctx))
296 })
297 .or_else(|| {
298 node.ConditionalExpression().map(|n| Self::from_conditional_expression_node(n, ctx))
299 })
300 .or_else(|| node.ObjectLiteral().map(|n| Self::from_object_literal_node(n, ctx)))
301 .or_else(|| node.Array().map(|n| Self::from_array_node(n, ctx)))
302 .or_else(|| node.CodeBlock().map(|n| Self::from_codeblock_node(n, ctx)))
303 .or_else(|| node.StringTemplate().map(|n| Self::from_string_template_node(n, ctx)))
304 .unwrap_or(Self::Invalid)
305 }
306
307 fn from_at_image_url_node(node: syntax_nodes::AtImageUrl, ctx: &mut LookupCtx) -> Self {
308 let s = match node
309 .child_text(SyntaxKind::StringLiteral)
310 .and_then(|x| crate::literals::unescape_string(&x))
311 {
312 Some(s) => s,
313 None => {
314 ctx.diag.push_error("Cannot parse string literal".into(), &node);
315 return Self::Invalid;
316 }
317 };
318
319 if s.is_empty() {
320 return Expression::ImageReference {
321 resource_ref: ImageReference::None,
322 source_location: Some(node.to_source_location()),
323 };
324 }
325
326 let absolute_source_path = {
327 let path = std::path::Path::new(&s);
328 if path.is_absolute() || s.starts_with("http://") || s.starts_with("https://") {
329 s
330 } else {
331 ctx.type_loader
332 .map(|loader| {
333 loader
334 .resolve_import_path(Some(&(*node).clone().into()), &s)
335 .0
336 .to_string_lossy()
337 .to_string()
338 })
339 .unwrap_or(s)
340 }
341 };
342
343 Expression::ImageReference {
344 resource_ref: ImageReference::AbsolutePath(absolute_source_path),
345 source_location: Some(node.to_source_location()),
346 }
347 }
348
349 fn from_at_linear_gradient(node: syntax_nodes::AtLinearGradient, ctx: &mut LookupCtx) -> Self {
350 let mut subs = node
351 .children_with_tokens()
352 .filter(|n| matches!(n.kind(), SyntaxKind::Comma | SyntaxKind::Expression));
353 let angle_expr = match subs.next() {
354 Some(e) if e.kind() == SyntaxKind::Expression => {
355 syntax_nodes::Expression::from(e.into_node().unwrap())
356 }
357 _ => {
358 ctx.diag.push_error("Expected angle expression".into(), &node);
359 return Expression::Invalid;
360 }
361 };
362 if subs.next().map_or(false, |s| s.kind() != SyntaxKind::Comma) {
363 ctx.diag
364 .push_error("Angle expression must be an angle followed by a comma".into(), &node);
365 return Expression::Invalid;
366 }
367 let angle =
368 Box::new(Expression::from_expression_node(angle_expr.clone(), ctx).maybe_convert_to(
369 Type::Angle,
370 &angle_expr,
371 ctx.diag,
372 ));
373
374 let mut stops = vec![];
375 enum Stop {
376 Empty,
377 Color(Expression),
378 Finished,
379 }
380 let mut current_stop = Stop::Empty;
381 for n in subs {
382 if n.kind() == SyntaxKind::Comma {
383 match std::mem::replace(&mut current_stop, Stop::Empty) {
384 Stop::Empty => {
385 ctx.diag.push_error("Expected expression".into(), &n);
386 break;
387 }
388 Stop::Finished => {}
389 Stop::Color(col) => stops.push((
390 col,
391 if stops.is_empty() {
392 Expression::NumberLiteral(0., Unit::None)
393 } else {
394 Expression::Invalid
395 },
396 )),
397 }
398 } else {
399 let e = {
401 let old_property_type = std::mem::replace(&mut ctx.property_type, Type::Color);
402 let e =
403 Expression::from_expression_node(n.as_node().unwrap().clone().into(), ctx);
404 ctx.property_type = old_property_type;
405 e
406 };
407 match std::mem::replace(&mut current_stop, Stop::Finished) {
408 Stop::Empty => {
409 current_stop = Stop::Color(e.maybe_convert_to(Type::Color, &n, ctx.diag))
410 }
411 Stop::Finished => {
412 ctx.diag.push_error("Expected comma".into(), &n);
413 break;
414 }
415 Stop::Color(col) => {
416 stops.push((col, e.maybe_convert_to(Type::Float32, &n, ctx.diag)))
417 }
418 }
419 }
420 }
421 match current_stop {
422 Stop::Color(col) => stops.push((col, Expression::NumberLiteral(1., Unit::None))),
423 Stop::Empty => {
424 if let Some((_, e @ Expression::Invalid)) = stops.last_mut() {
425 *e = Expression::NumberLiteral(1., Unit::None)
426 }
427 }
428 Stop::Finished => (),
429 };
430
431 let mut start = 0;
433 while start < stops.len() {
434 start += match stops[start..].iter().position(|s| matches!(s.1, Expression::Invalid)) {
435 Some(p) => p,
436 None => break,
437 };
438 let (before, rest) = stops.split_at_mut(start);
439 let pos =
440 rest.iter().position(|s| !matches!(s.1, Expression::Invalid)).unwrap_or(rest.len());
441 if pos > 0 {
442 let (middle, after) = rest.split_at_mut(pos);
443 let begin = &before.last().expect("The first should never be invalid").1;
444 let end = &after.last().expect("The last should never be invalid").1;
445 for (i, (_, e)) in middle.iter_mut().enumerate() {
446 debug_assert!(matches!(e, Expression::Invalid));
447 *e = Expression::BinaryExpression {
449 lhs: Box::new(begin.clone()),
450 rhs: Box::new(Expression::BinaryExpression {
451 lhs: Box::new(Expression::BinaryExpression {
452 lhs: Box::new(Expression::NumberLiteral(i as f64 + 1., Unit::None)),
453 rhs: Box::new(Expression::BinaryExpression {
454 lhs: Box::new(end.clone()),
455 rhs: Box::new(begin.clone()),
456 op: '-',
457 }),
458 op: '*',
459 }),
460 rhs: Box::new(Expression::NumberLiteral(pos as f64 + 1., Unit::None)),
461 op: '/',
462 }),
463 op: '+',
464 };
465 }
466 }
467 start += pos + 1;
468 }
469
470 Expression::LinearGradient { angle, stops }
471 }
472
473 fn from_qualified_name_node(node: syntax_nodes::QualifiedName, ctx: &mut LookupCtx) -> Self {
475 let mut it = node
476 .children_with_tokens()
477 .filter(|n| n.kind() == SyntaxKind::Identifier)
478 .filter_map(|n| n.into_token());
479
480 let first = if let Some(first) = it.next() {
481 first
482 } else {
483 debug_assert!(ctx.diag.has_error());
485 return Self::Invalid;
486 };
487
488 ctx.current_token = Some(first.clone().into());
489 let first_str = crate::parser::normalize_identifier(first.text());
490 let global_lookup = crate::lookup::global_lookup();
491 let result = match global_lookup.lookup(ctx, &first_str) {
492 None => {
493 if let Some(minus_pos) = first.text().find('-') {
494 let first_str = &first.text()[0..minus_pos];
496 if global_lookup
497 .lookup(ctx, &crate::parser::normalize_identifier(first_str))
498 .is_some()
499 {
500 ctx.diag.push_error(format!("Unknown unqualified identifier '{}'. Use space before the '-' if you meant a subtraction", first.text()), &node);
501 return Expression::Invalid;
502 }
503 }
504
505 if it.next().is_some() {
506 ctx.diag.push_error(format!("Cannot access id '{}'", first.text()), &node);
507 } else {
508 ctx.diag.push_error(
509 format!("Unknown unqualified identifier '{}'", first.text()),
510 &node,
511 );
512 }
513 return Expression::Invalid;
514 }
515 Some(x) => x,
516 };
517
518 if let Some(depr) = result.deprecated() {
519 ctx.diag.push_property_deprecation_warning(&first_str, depr, &first);
520 }
521
522 match result {
523 LookupResult::Expression { expression: Expression::ElementReference(e), .. } => {
524 continue_lookup_within_element(&e.upgrade().unwrap(), &mut it, node, ctx)
525 }
526 LookupResult::Expression {
527 expression: r @ Expression::CallbackReference(..), ..
528 } => {
529 if let Some(x) = it.next() {
530 ctx.diag.push_error("Cannot access fields of callback".into(), &x)
531 }
532 r
533 }
534 LookupResult::Enumeration(enumeration) => {
535 if let Some(next_identifier) = it.next() {
536 match enumeration
537 .lookup(ctx, &crate::parser::normalize_identifier(next_identifier.text()))
538 {
539 Some(LookupResult::Expression { expression, .. }) => {
540 maybe_lookup_object(expression, it, ctx)
541 }
542 _ => {
543 ctx.diag.push_error(
544 format!(
545 "'{}' is not a member of the enum {}",
546 next_identifier.text(),
547 enumeration.name
548 ),
549 &next_identifier,
550 );
551 Expression::Invalid
552 }
553 }
554 } else {
555 ctx.diag.push_error("Cannot take reference to an enum".to_string(), &node);
556 Expression::Invalid
557 }
558 }
559 LookupResult::Expression { expression, .. } => maybe_lookup_object(expression, it, ctx),
560 LookupResult::Namespace(_) => {
561 if let Some(next_identifier) = it.next() {
562 match result
563 .lookup(ctx, &crate::parser::normalize_identifier(next_identifier.text()))
564 {
565 Some(LookupResult::Expression { expression, .. }) => {
566 maybe_lookup_object(expression, it, ctx)
567 }
568 _ => {
569 ctx.diag.push_error(
570 format!(
571 "'{}' is not a member of the namespace {}",
572 next_identifier.text(),
573 first_str
574 ),
575 &next_identifier,
576 );
577 Expression::Invalid
578 }
579 }
580 } else {
581 ctx.diag.push_error("Cannot take reference to a namespace".to_string(), &node);
582 Expression::Invalid
583 }
584 }
585 }
586 }
587
588 fn from_function_call_node(
589 node: syntax_nodes::FunctionCallExpression,
590 ctx: &mut LookupCtx,
591 ) -> Expression {
592 let mut arguments = Vec::new();
593
594 let mut sub_expr = node.Expression();
595
596 let function = sub_expr.next().map_or(Self::Invalid, |n| {
597 n.QualifiedName()
599 .or_else(|| {
600 n.Expression().and_then(|mut e| {
601 while let Some(e2) = e.Expression() {
602 e = e2;
603 }
604 e.QualifiedName().map(|q| {
605 ctx.diag.push_warning(
606 "Parentheses around callable are deprecated. Remove the parentheses"
607 .into(),
608 &n,
609 );
610 q
611 })
612 })
613 })
614 .map(|qn| Self::from_qualified_name_node(qn, ctx))
615 .unwrap_or_else(|| Self::from_expression_node(n, ctx))
616 });
617
618 let sub_expr = sub_expr.map(|n| {
619 (Self::from_expression_node(n.clone(), ctx), Some(NodeOrToken::from((*n).clone())))
620 });
621
622 let function = match function {
623 Expression::BuiltinMacroReference(mac, n) => {
624 arguments.extend(sub_expr);
625 return crate::builtin_macros::lower_macro(mac, n, arguments.into_iter(), ctx.diag);
626 }
627 Expression::MemberFunction { base, base_node, member } => {
628 arguments.push((*base, base_node));
629 member
630 }
631 _ => Box::new(function),
632 };
633 arguments.extend(sub_expr);
634
635 let arguments = match function.ty() {
636 Type::Function { args, .. } | Type::Callback { args, .. } => {
637 if arguments.len() != args.len() {
638 ctx.diag.push_error(
639 format!(
640 "The callback or function expects {} arguments, but {} are provided",
641 args.len(),
642 arguments.len()
643 ),
644 &node,
645 );
646 arguments.into_iter().map(|x| x.0).collect()
647 } else {
648 arguments
649 .into_iter()
650 .zip(args.iter())
651 .map(|((e, node), ty)| e.maybe_convert_to(ty.clone(), &node, ctx.diag))
652 .collect()
653 }
654 }
655 _ => {
656 ctx.diag.push_error("The expression is not a function".into(), &node);
657 arguments.into_iter().map(|x| x.0).collect()
658 }
659 };
660
661 Expression::FunctionCall {
662 function,
663 arguments,
664 source_location: Some(node.to_source_location()),
665 }
666 }
667
668 fn from_member_access_node(
669 node: syntax_nodes::MemberAccess,
670 ctx: &mut LookupCtx,
671 ) -> Expression {
672 let base = Self::from_expression_node(node.Expression(), ctx);
673 maybe_lookup_object(base, node.child_token(SyntaxKind::Identifier).into_iter(), ctx)
674 }
675
676 fn from_self_assignment_node(
677 node: syntax_nodes::SelfAssignment,
678 ctx: &mut LookupCtx,
679 ) -> Expression {
680 let (lhs_n, rhs_n) = node.Expression();
681 let mut lhs = Self::from_expression_node(lhs_n.clone(), ctx);
682 let op = None
683 .or_else(|| node.child_token(SyntaxKind::PlusEqual).and(Some('+')))
684 .or_else(|| node.child_token(SyntaxKind::MinusEqual).and(Some('-')))
685 .or_else(|| node.child_token(SyntaxKind::StarEqual).and(Some('*')))
686 .or_else(|| node.child_token(SyntaxKind::DivEqual).and(Some('/')))
687 .or_else(|| node.child_token(SyntaxKind::Equal).and(Some('=')))
688 .unwrap_or('_');
689 if !lhs.try_set_rw() && lhs.ty() != Type::Invalid {
690 ctx.diag.push_error(
691 format!(
692 "{} needs to be done on a property",
693 if op == '=' { "Assignment" } else { "Self assignment" }
694 ),
695 &node,
696 );
697 }
698 let ty = lhs.ty();
699 let expected_ty = match op {
700 '=' => ty,
701 '+' if ty == Type::String || ty.as_unit_product().is_some() => ty,
702 '-' if ty.as_unit_product().is_some() => ty,
703 '/' | '*' if ty.as_unit_product().is_some() => Type::Float32,
704 _ => {
705 if ty != Type::Invalid {
706 ctx.diag.push_error(
707 format!("the {}= operation cannot be done on a {}", op, ty),
708 &lhs_n,
709 );
710 }
711 Type::Invalid
712 }
713 };
714 let rhs = Self::from_expression_node(rhs_n.clone(), ctx);
715 Expression::SelfAssignment {
716 lhs: Box::new(lhs),
717 rhs: Box::new(rhs.maybe_convert_to(expected_ty, &rhs_n, ctx.diag)),
718 op,
719 }
720 }
721
722 fn from_binary_expression_node(
723 node: syntax_nodes::BinaryExpression,
724 ctx: &mut LookupCtx,
725 ) -> Expression {
726 let op = None
727 .or_else(|| node.child_token(SyntaxKind::Plus).and(Some('+')))
728 .or_else(|| node.child_token(SyntaxKind::Minus).and(Some('-')))
729 .or_else(|| node.child_token(SyntaxKind::Star).and(Some('*')))
730 .or_else(|| node.child_token(SyntaxKind::Div).and(Some('/')))
731 .or_else(|| node.child_token(SyntaxKind::LessEqual).and(Some('≤')))
732 .or_else(|| node.child_token(SyntaxKind::GreaterEqual).and(Some('≥')))
733 .or_else(|| node.child_token(SyntaxKind::LAngle).and(Some('<')))
734 .or_else(|| node.child_token(SyntaxKind::RAngle).and(Some('>')))
735 .or_else(|| node.child_token(SyntaxKind::EqualEqual).and(Some('=')))
736 .or_else(|| node.child_token(SyntaxKind::NotEqual).and(Some('!')))
737 .or_else(|| node.child_token(SyntaxKind::AndAnd).and(Some('&')))
738 .or_else(|| node.child_token(SyntaxKind::OrOr).and(Some('|')))
739 .unwrap_or('_');
740
741 let (lhs_n, rhs_n) = node.Expression();
742 let lhs = Self::from_expression_node(lhs_n.clone(), ctx);
743 let rhs = Self::from_expression_node(rhs_n.clone(), ctx);
744
745 let expected_ty = match operator_class(op) {
746 OperatorClass::ComparisonOp => {
747 Self::common_target_type_for_type_list([lhs.ty(), rhs.ty()].iter().cloned())
748 }
749 OperatorClass::LogicalOp => Type::Bool,
750 OperatorClass::ArithmeticOp => {
751 let (lhs_ty, rhs_ty) = (lhs.ty(), rhs.ty());
752 if op == '+' && (lhs_ty == Type::String || rhs_ty == Type::String) {
753 Type::String
754 } else if op == '+' || op == '-' {
755 if lhs_ty.default_unit().is_some() {
756 lhs_ty
757 } else if rhs_ty.default_unit().is_some() {
758 rhs_ty
759 } else if matches!(lhs_ty, Type::UnitProduct(_)) {
760 lhs_ty
761 } else if matches!(rhs_ty, Type::UnitProduct(_)) {
762 rhs_ty
763 } else {
764 Type::Float32
765 }
766 } else if op == '*' || op == '/' {
767 let has_unit = |ty: &Type| {
768 matches!(ty, Type::UnitProduct(_)) || ty.default_unit().is_some()
769 };
770 match (has_unit(&lhs_ty), has_unit(&rhs_ty)) {
771 (true, true) => {
772 return Expression::BinaryExpression {
773 lhs: Box::new(lhs),
774 rhs: Box::new(rhs),
775 op,
776 }
777 }
778 (true, false) => {
779 return Expression::BinaryExpression {
780 lhs: Box::new(lhs),
781 rhs: Box::new(rhs.maybe_convert_to(
782 Type::Float32,
783 &rhs_n,
784 ctx.diag,
785 )),
786 op,
787 }
788 }
789 (false, true) => {
790 return Expression::BinaryExpression {
791 lhs: Box::new(lhs.maybe_convert_to(
792 Type::Float32,
793 &lhs_n,
794 ctx.diag,
795 )),
796 rhs: Box::new(rhs),
797 op,
798 }
799 }
800 (false, false) => Type::Float32,
801 }
802 } else {
803 unreachable!()
804 }
805 }
806 };
807 Expression::BinaryExpression {
808 lhs: Box::new(lhs.maybe_convert_to(expected_ty.clone(), &lhs_n, ctx.diag)),
809 rhs: Box::new(rhs.maybe_convert_to(expected_ty, &rhs_n, ctx.diag)),
810 op,
811 }
812 }
813
814 fn from_unaryop_expression_node(
815 node: syntax_nodes::UnaryOpExpression,
816 ctx: &mut LookupCtx,
817 ) -> Expression {
818 let exp_n = node.Expression();
819 let exp = Self::from_expression_node(exp_n, ctx);
820
821 Expression::UnaryOp {
822 sub: Box::new(exp),
823 op: None
824 .or_else(|| node.child_token(SyntaxKind::Plus).and(Some('+')))
825 .or_else(|| node.child_token(SyntaxKind::Minus).and(Some('-')))
826 .or_else(|| node.child_token(SyntaxKind::Bang).and(Some('!')))
827 .unwrap_or('_'),
828 }
829 }
830
831 fn from_conditional_expression_node(
832 node: syntax_nodes::ConditionalExpression,
833 ctx: &mut LookupCtx,
834 ) -> Expression {
835 let (condition_n, true_expr_n, false_expr_n) = node.Expression();
836 let condition = Self::from_expression_node(condition_n.clone(), ctx).maybe_convert_to(
838 Type::Bool,
839 &condition_n,
840 ctx.diag,
841 );
842 let true_expr = Self::from_expression_node(true_expr_n.clone(), ctx);
843 let false_expr = Self::from_expression_node(false_expr_n.clone(), ctx);
844 let result_ty = Self::common_target_type_for_type_list(
845 [true_expr.ty(), false_expr.ty()].iter().cloned(),
846 );
847 let true_expr = true_expr.maybe_convert_to(result_ty.clone(), &true_expr_n, ctx.diag);
848 let false_expr = false_expr.maybe_convert_to(result_ty, &false_expr_n, ctx.diag);
849 Expression::Condition {
850 condition: Box::new(condition),
851 true_expr: Box::new(true_expr),
852 false_expr: Box::new(false_expr),
853 }
854 }
855
856 fn from_index_expression_node(
857 node: syntax_nodes::IndexExpression,
858 ctx: &mut LookupCtx,
859 ) -> Expression {
860 let (array_expr_n, index_expr_n) = node.Expression();
861 let array_expr = Self::from_expression_node(array_expr_n, ctx);
862 let index_expr = Self::from_expression_node(index_expr_n.clone(), ctx).maybe_convert_to(
863 Type::Int32,
864 &index_expr_n,
865 &mut ctx.diag,
866 );
867
868 let ty = array_expr.ty();
869 if !matches!(ty, Type::Array(_) | Type::Invalid) {
870 ctx.diag.push_error(format!("{} is not an indexable type", ty), &node);
871 }
872 Expression::ArrayIndex { array: Box::new(array_expr), index: Box::new(index_expr) }
873 }
874
875 fn from_object_literal_node(
876 node: syntax_nodes::ObjectLiteral,
877 ctx: &mut LookupCtx,
878 ) -> Expression {
879 let values: HashMap<String, Expression> = node
880 .ObjectMember()
881 .map(|n| {
882 (
883 identifier_text(&n).unwrap_or_default(),
884 Expression::from_expression_node(n.Expression(), ctx),
885 )
886 })
887 .collect();
888 let ty = Type::Struct {
889 fields: values.iter().map(|(k, v)| (k.clone(), v.ty())).collect(),
890 name: None,
891 node: None,
892 };
893 Expression::Struct { ty, values }
894 }
895
896 fn from_array_node(node: syntax_nodes::Array, ctx: &mut LookupCtx) -> Expression {
897 let mut values: Vec<Expression> =
898 node.Expression().map(|e| Expression::from_expression_node(e, ctx)).collect();
899
900 let element_ty =
902 Self::common_target_type_for_type_list(values.iter().map(|expr| expr.ty()));
903
904 for e in values.iter_mut() {
905 *e = core::mem::replace(e, Expression::Invalid).maybe_convert_to(
906 element_ty.clone(),
907 &node,
908 ctx.diag,
909 );
910 }
911
912 Expression::Array { element_ty, values }
913 }
914
915 fn from_string_template_node(
916 node: syntax_nodes::StringTemplate,
917 ctx: &mut LookupCtx,
918 ) -> Expression {
919 let mut exprs = node.Expression().map(|e| {
920 Expression::from_expression_node(e.clone(), ctx).maybe_convert_to(
921 Type::String,
922 &e,
923 ctx.diag,
924 )
925 });
926 let mut result = exprs.next().unwrap_or_default();
927 for x in exprs {
928 result = Expression::BinaryExpression {
929 lhs: Box::new(std::mem::take(&mut result)),
930 rhs: Box::new(x),
931 op: '+',
932 }
933 }
934 result
935 }
936
937 fn common_target_type_for_type_list(types: impl Iterator<Item = Type>) -> Type {
941 types.fold(Type::Invalid, |target_type, expr_ty| {
942 if target_type == expr_ty {
943 target_type
944 } else if target_type == Type::Invalid {
945 expr_ty
946 } else {
947 match (target_type, expr_ty) {
948 (
949 Type::Struct {
950 fields: mut result_fields,
951 name: result_name,
952 node: result_node,
953 },
954 Type::Struct { fields: elem_fields, name: elem_name, node: elem_node },
955 ) => {
956 for (elem_name, elem_ty) in elem_fields.into_iter() {
957 match result_fields.entry(elem_name) {
958 std::collections::btree_map::Entry::Vacant(free_entry) => {
959 free_entry.insert(elem_ty);
960 }
961 std::collections::btree_map::Entry::Occupied(
962 mut existing_field,
963 ) => {
964 *existing_field.get_mut() =
965 Self::common_target_type_for_type_list(
966 [existing_field.get().clone(), elem_ty].iter().cloned(),
967 );
968 }
969 }
970 }
971 Type::Struct {
972 name: result_name.or(elem_name),
973 fields: result_fields,
974 node: result_node.or(elem_node),
975 }
976 }
977 (target_type, expr_ty) => {
978 if expr_ty.can_convert(&target_type) {
979 target_type
980 } else if target_type.can_convert(&expr_ty)
981 || (expr_ty.default_unit().is_some()
982 && matches!(target_type, Type::Float32 | Type::Int32))
983 {
984 expr_ty
986 } else {
987 target_type
989 }
990 }
991 }
992 }
993 })
994 }
995}
996
997fn continue_lookup_within_element(
998 elem: &ElementRc,
999 it: &mut impl Iterator<Item = crate::parser::SyntaxToken>,
1000 node: syntax_nodes::QualifiedName,
1001 ctx: &mut LookupCtx,
1002) -> Expression {
1003 let second = if let Some(second) = it.next() {
1004 second
1005 } else if matches!(ctx.property_type, Type::ElementReference) {
1006 return Expression::ElementReference(Rc::downgrade(elem));
1007 } else {
1008 ctx.diag.push_error("Cannot take reference of an element".into(), &node);
1009 return Expression::Invalid;
1010 };
1011 let prop_name = crate::parser::normalize_identifier(second.text());
1012
1013 let PropertyLookupResult { resolved_name, property_type } =
1014 elem.borrow().lookup_property(&prop_name);
1015 if property_type.is_property_type() {
1016 if resolved_name != prop_name {
1017 ctx.diag.push_property_deprecation_warning(&prop_name, &resolved_name, &second);
1018 }
1019 let prop = Expression::PropertyReference(NamedReference::new(elem, &resolved_name));
1020 maybe_lookup_object(prop, it, ctx)
1021 } else if matches!(property_type, Type::Callback { .. }) {
1022 if let Some(x) = it.next() {
1023 ctx.diag.push_error("Cannot access fields of callback".into(), &x)
1024 }
1025 Expression::CallbackReference(NamedReference::new(elem, &resolved_name))
1026 } else if matches!(property_type, Type::Function { .. }) {
1027 let member = elem.borrow().base_type.lookup_member_function(&resolved_name);
1028 Expression::MemberFunction {
1029 base: Box::new(Expression::ElementReference(Rc::downgrade(elem))),
1030 base_node: Some(NodeOrToken::Node(node.into())),
1031 member: Box::new(member),
1032 }
1033 } else {
1034 let mut err = |extra: &str| {
1035 let what = match &elem.borrow().base_type {
1036 Type::Void => {
1037 let global = elem.borrow().enclosing_component.upgrade().unwrap();
1038 assert!(global.is_global());
1039 format!("'{}'", global.id)
1040 }
1041 Type::Component(c) => format!("Element '{}'", c.id),
1042 Type::Builtin(b) => format!("Element '{}'", b.name),
1043 _ => {
1044 assert!(ctx.diag.has_error());
1045 return;
1046 }
1047 };
1048 ctx.diag.push_error(
1049 format!("{} does not have a property '{}'{}", what, second.text(), extra),
1050 &second,
1051 );
1052 };
1053 if let Some(minus_pos) = second.text().find('-') {
1054 if elem
1056 .borrow()
1057 .lookup_property(&crate::parser::normalize_identifier(&second.text()[0..minus_pos]))
1058 .property_type
1059 != Type::Invalid
1060 {
1061 err(". Use space before the '-' if you meant a subtraction");
1062 return Expression::Invalid;
1063 }
1064 }
1065 err("");
1066 Expression::Invalid
1067 }
1068}
1069
1070fn maybe_lookup_object(
1071 mut base: Expression,
1072 it: impl Iterator<Item = crate::parser::SyntaxToken>,
1073 ctx: &mut LookupCtx,
1074) -> Expression {
1075 for next in it {
1076 let next_str = crate::parser::normalize_identifier(next.text());
1077 ctx.current_token = Some(next.clone().into());
1078 match base.lookup(ctx, &next_str) {
1079 Some(LookupResult::Expression { expression, .. }) => {
1080 base = expression;
1081 }
1082 _ => {
1083 if let Some(minus_pos) = next.text().find('-') {
1084 if base.lookup(ctx, &next.text()[0..minus_pos]).is_some() {
1085 ctx.diag.push_error(format!("Cannot access the field '{}'. Use space before the '-' if you meant a subtraction", next.text()), &next);
1086 return Expression::Invalid;
1087 }
1088 }
1089 let ty_descr = match base.ty() {
1090 Type::Struct { .. } => String::new(),
1091 ty => format!(" of {}", ty),
1092 };
1093 ctx.diag.push_error(
1094 format!("Cannot access the field '{}'{}", next.text(), ty_descr),
1095 &next,
1096 );
1097 return Expression::Invalid;
1098 }
1099 }
1100 }
1101 base
1102}
1103
1104pub fn resolve_two_way_binding(
1105 node: syntax_nodes::TwoWayBinding,
1106 ctx: &mut LookupCtx,
1107) -> Option<NamedReference> {
1108 let e = node
1109 .Expression()
1110 .QualifiedName()
1111 .map_or(Expression::Invalid, |n| Expression::from_qualified_name_node(n, ctx));
1112 let ty = e.ty();
1113 match e {
1114 Expression::PropertyReference(n) => {
1115 if ty != ctx.property_type && ctx.property_type != Type::InferredProperty {
1116 ctx.diag.push_error(
1117 "The property does not have the same type as the bound property".into(),
1118 &node,
1119 );
1120 }
1121 Some(n)
1122 }
1123 Expression::CallbackReference(n) => {
1124 if ctx.property_type != Type::InferredCallback && ty != ctx.property_type {
1125 ctx.diag.push_error("Cannot bind to a callback".into(), &node);
1126 None
1127 } else {
1128 Some(n)
1129 }
1130 }
1131 _ => {
1132 ctx.diag.push_error(
1133 "The expression in a two way binding must be a property reference".into(),
1134 &node,
1135 );
1136 None
1137 }
1138 }
1139}