1use super::document::parse_qualified_name;
5use super::prelude::*;
6
7#[cfg_attr(test, parser_test)]
8pub fn parse_expression(p: &mut impl Parser) -> bool {
36 p.peek(); parse_expression_helper(p, OperatorPrecedence::Default)
38}
39
40#[derive(Eq, PartialEq, Ord, PartialOrd)]
41#[repr(u8)]
42enum OperatorPrecedence {
43 Default,
45 Logical,
47 Equality,
49 Add,
51 Mul,
53 Unary,
54}
55
56fn parse_expression_helper(p: &mut impl Parser, precedence: OperatorPrecedence) -> bool {
57 let mut p = p.start_node(SyntaxKind::Expression);
58 let checkpoint = p.checkpoint();
59 let mut possible_range = false;
60 match p.nth(0).kind() {
61 SyntaxKind::Identifier => {
62 parse_qualified_name(&mut *p);
63 }
64 SyntaxKind::StringLiteral => {
65 if p.nth(0).as_str().ends_with('{') {
66 parse_template_string(&mut *p)
67 } else {
68 p.consume()
69 }
70 }
71 SyntaxKind::NumberLiteral => {
72 if p.nth(0).as_str().ends_with('.') {
73 possible_range = true;
74 }
75 p.consume()
76 }
77 SyntaxKind::ColorLiteral => p.consume(),
78 SyntaxKind::LParent => {
79 if p.nth(1).kind() == SyntaxKind::Identifier
80 && p.nth(2).kind() == SyntaxKind::RParent
81 && p.nth(3).kind() == SyntaxKind::FatArrow
82 {
83 parse_closure(&mut *p);
84 } else {
85 p.consume();
86 parse_expression(&mut *p);
87 p.expect(SyntaxKind::RParent);
88 }
89 }
90 SyntaxKind::LBracket => parse_array(&mut *p),
91 SyntaxKind::LBrace => parse_object_notation(&mut *p),
92 SyntaxKind::Plus | SyntaxKind::Minus | SyntaxKind::Bang => {
93 let mut p = p.start_node(SyntaxKind::UnaryOpExpression);
94 p.consume();
95 parse_expression_helper(&mut *p, OperatorPrecedence::Unary);
96 }
97 SyntaxKind::At => {
98 parse_at_keyword(&mut *p);
99 }
100 _ => {
101 p.error("invalid expression");
102 return false;
103 }
104 }
105
106 loop {
107 match p.nth(0).kind() {
108 SyntaxKind::Dot => {
109 {
110 let _ = p.start_node_at(checkpoint.clone(), SyntaxKind::Expression);
111 }
112 let mut p = p.start_node_at(checkpoint.clone(), SyntaxKind::MemberAccess);
113 p.consume(); if possible_range && p.peek().kind() == SyntaxKind::NumberLiteral {
115 let error = format!(
116 "Parse error. Range expressions are not supported in Slint. You can use an integer as a model to repeat something multiple time. Eg: `for i in {} : ...`",
117 p.peek().as_str()
118 );
119 p.error(error);
120 p.consume();
121 return false;
122 }
123 if !p.expect(SyntaxKind::Identifier) {
124 return false;
125 }
126 }
127 SyntaxKind::LParent => {
128 {
129 let _ = p.start_node_at(checkpoint.clone(), SyntaxKind::Expression);
130 }
131 let mut p = p.start_node_at(checkpoint.clone(), SyntaxKind::FunctionCallExpression);
132 parse_function_arguments(&mut *p);
133 }
134 SyntaxKind::LBracket => {
135 {
136 let _ = p.start_node_at(checkpoint.clone(), SyntaxKind::Expression);
137 }
138 let mut p = p.start_node_at(checkpoint.clone(), SyntaxKind::IndexExpression);
139 p.expect(SyntaxKind::LBracket);
140 parse_expression(&mut *p);
141 p.expect(SyntaxKind::RBracket);
142 }
143 _ => break,
144 }
145 possible_range = false;
146 }
147
148 if precedence >= OperatorPrecedence::Mul {
149 return true;
150 }
151
152 while matches!(p.nth(0).kind(), SyntaxKind::Star | SyntaxKind::Div) {
153 {
154 let _ = p.start_node_at(checkpoint.clone(), SyntaxKind::Expression);
155 }
156 let mut p = p.start_node_at(checkpoint.clone(), SyntaxKind::BinaryExpression);
157 p.consume();
158 parse_expression_helper(&mut *p, OperatorPrecedence::Mul);
159 }
160
161 if p.nth(0).kind() == SyntaxKind::Percent {
162 p.error("Unexpected '%'. For the unit, it should be attached to the number. If you're looking for the modulo operator, use the 'Math.mod(x, y)' function");
163 p.consume();
164 return false;
165 }
166
167 if precedence >= OperatorPrecedence::Add {
168 return true;
169 }
170
171 while matches!(p.nth(0).kind(), SyntaxKind::Plus | SyntaxKind::Minus) {
172 {
173 let _ = p.start_node_at(checkpoint.clone(), SyntaxKind::Expression);
174 }
175 let mut p = p.start_node_at(checkpoint.clone(), SyntaxKind::BinaryExpression);
176 p.consume();
177 parse_expression_helper(&mut *p, OperatorPrecedence::Add);
178 }
179
180 if precedence > OperatorPrecedence::Equality {
181 return true;
182 }
183
184 if matches!(
185 p.nth(0).kind(),
186 SyntaxKind::LessEqual
187 | SyntaxKind::GreaterEqual
188 | SyntaxKind::EqualEqual
189 | SyntaxKind::NotEqual
190 | SyntaxKind::LAngle
191 | SyntaxKind::RAngle
192 ) {
193 if precedence == OperatorPrecedence::Equality {
194 p.error("Use parentheses to disambiguate equality expression on the same level");
195 }
196
197 {
198 let _ = p.start_node_at(checkpoint.clone(), SyntaxKind::Expression);
199 }
200 let mut p = p.start_node_at(checkpoint.clone(), SyntaxKind::BinaryExpression);
201 p.consume();
202 parse_expression_helper(&mut *p, OperatorPrecedence::Equality);
203 }
204
205 if precedence >= OperatorPrecedence::Logical {
206 return true;
207 }
208
209 let mut prev_logical_op = None;
210 while matches!(p.nth(0).kind(), SyntaxKind::AndAnd | SyntaxKind::OrOr) {
211 if let Some(prev) = prev_logical_op {
212 if prev != p.nth(0).kind() {
213 p.error("Use parentheses to disambiguate between && and ||");
214 prev_logical_op = None;
215 }
216 } else {
217 prev_logical_op = Some(p.nth(0).kind());
218 }
219
220 {
221 let _ = p.start_node_at(checkpoint.clone(), SyntaxKind::Expression);
222 }
223 let mut p = p.start_node_at(checkpoint.clone(), SyntaxKind::BinaryExpression);
224 p.consume();
225 parse_expression_helper(&mut *p, OperatorPrecedence::Logical);
226 }
227
228 if p.nth(0).kind() == SyntaxKind::Question {
229 {
230 let _ = p.start_node_at(checkpoint.clone(), SyntaxKind::Expression);
231 }
232 let mut p = p.start_node_at(checkpoint, SyntaxKind::ConditionalExpression);
233 p.consume();
234 parse_expression(&mut *p);
235 p.expect(SyntaxKind::Colon);
236 parse_expression(&mut *p);
237 }
238 true
239}
240
241#[cfg_attr(test, parser_test)]
242fn parse_closure(p: &mut impl Parser) {
248 let mut p = p.start_node(SyntaxKind::Closure);
249
250 p.expect(SyntaxKind::LParent);
251
252 {
253 let mut p = p.start_node(SyntaxKind::DeclaredIdentifier);
254 p.expect(SyntaxKind::Identifier);
255 }
256
257 p.expect(SyntaxKind::RParent);
258
259 p.expect(SyntaxKind::FatArrow);
260
261 parse_expression(&mut *p);
262}
263
264#[cfg_attr(test, parser_test)]
265fn parse_at_keyword(p: &mut impl Parser) {
272 debug_assert_eq!(p.peek().kind(), SyntaxKind::At);
273 match p.nth(1).as_str() {
274 "image-url" | "image_url" => {
275 parse_image_url(p);
276 }
277 "linear-gradient" | "linear_gradient" => {
278 parse_gradient(p);
279 }
280 "radial-gradient" | "radial_gradient" => {
281 parse_gradient(p);
282 }
283 "conic-gradient" | "conic_gradient" => {
284 parse_gradient(p);
285 }
286 "tr" => {
287 parse_tr(p);
288 }
289 "markdown" => {
290 parse_markdown(p);
291 }
292 "keys" => {
293 parse_keys(p);
294 }
295 _ => {
296 p.consume();
297 p.test(SyntaxKind::Identifier); p.error("Expected 'image-url', 'tr', 'keys', 'markdown' 'conic-gradient', 'linear-gradient', or 'radial-gradient' after '@'");
299 }
300 }
301}
302
303#[cfg_attr(test, parser_test)]
304fn parse_array(p: &mut impl Parser) {
311 let mut p = p.start_node(SyntaxKind::Array);
312 p.expect(SyntaxKind::LBracket);
313
314 while p.nth(0).kind() != SyntaxKind::RBracket {
315 parse_expression(&mut *p);
316 if !p.test(SyntaxKind::Comma) {
317 break;
318 }
319 }
320 p.expect(SyntaxKind::RBracket);
321}
322
323#[cfg_attr(test, parser_test)]
324fn parse_object_notation(p: &mut impl Parser) {
331 let mut p = p.start_node(SyntaxKind::ObjectLiteral);
332 p.expect(SyntaxKind::LBrace);
333
334 while p.nth(0).kind() != SyntaxKind::RBrace {
335 let mut p = p.start_node(SyntaxKind::ObjectMember);
336 p.expect(SyntaxKind::Identifier);
337 p.expect(SyntaxKind::Colon);
338 parse_expression(&mut *p);
339 if !p.test(SyntaxKind::Comma) {
340 break;
341 }
342 }
343 p.expect(SyntaxKind::RBrace);
344}
345
346#[cfg_attr(test, parser_test)]
347fn parse_function_arguments(p: &mut impl Parser) {
354 p.expect(SyntaxKind::LParent);
355
356 while p.nth(0).kind() != SyntaxKind::RParent {
357 parse_expression(&mut *p);
358 if !p.test(SyntaxKind::Comma) {
359 break;
360 }
361 }
362 p.expect(SyntaxKind::RParent);
363}
364
365#[cfg_attr(test, parser_test)]
366fn parse_template_string(p: &mut impl Parser) {
371 let mut p = p.start_node(SyntaxKind::StringTemplate);
372 debug_assert!(p.nth(0).as_str().ends_with("\\{"));
373 p.expect(SyntaxKind::StringLiteral);
374 loop {
375 parse_expression(&mut *p);
376 let peek = p.peek();
377 if peek.kind != SyntaxKind::StringLiteral || !peek.as_str().starts_with('}') {
378 p.error("Error while parsing string template")
379 }
380 let cont = peek.as_str().ends_with('{');
381 p.consume();
382 if !cont {
383 break;
384 }
385 }
386}
387
388#[cfg_attr(test, parser_test)]
389fn parse_gradient(p: &mut impl Parser) {
402 let mut p = p.start_node(SyntaxKind::AtGradient);
403 p.expect(SyntaxKind::At);
404 debug_assert!(p.peek().as_str().ends_with("gradient"));
405 p.expect(SyntaxKind::Identifier); p.expect(SyntaxKind::LParent);
408
409 while !p.test(SyntaxKind::RParent) {
410 if !parse_expression(&mut *p) {
411 return;
412 }
413 p.test(SyntaxKind::Comma);
414 }
415}
416
417#[cfg_attr(test, parser_test)]
418fn parse_tr(p: &mut impl Parser) {
425 let mut p = p.start_node(SyntaxKind::AtTr);
426 p.expect(SyntaxKind::At);
427 debug_assert_eq!(p.peek().as_str(), "tr");
428 p.expect(SyntaxKind::Identifier); p.expect(SyntaxKind::LParent);
430
431 let checkpoint = p.checkpoint();
432
433 fn consume_literal(p: &mut impl Parser) -> bool {
434 let peek = p.peek();
435 if peek.kind() != SyntaxKind::StringLiteral
436 || !peek.as_str().starts_with('"')
437 || !peek.as_str().ends_with('"')
438 {
439 p.error("Expected plain string literal");
440 return false;
441 }
442 p.expect(SyntaxKind::StringLiteral)
443 }
444
445 if !consume_literal(&mut *p) {
446 return;
447 }
448
449 if p.test(SyntaxKind::FatArrow) {
450 drop(p.start_node_at(checkpoint, SyntaxKind::TrContext));
451 if !consume_literal(&mut *p) {
452 return;
453 }
454 }
455
456 if p.peek().kind() == SyntaxKind::Pipe {
457 let mut p = p.start_node(SyntaxKind::TrPlural);
458 p.consume();
459 if !consume_literal(&mut *p) || !p.expect(SyntaxKind::Percent) {
460 let _ = p.start_node(SyntaxKind::Expression);
461 return;
462 }
463 parse_expression(&mut *p);
464 }
465
466 while p.test(SyntaxKind::Comma) {
467 if !parse_expression(&mut *p) {
468 break;
469 }
470 }
471 p.expect(SyntaxKind::RParent);
472}
473
474fn parse_markdown(p: &mut impl Parser) {
480 let mut p = p.start_node(SyntaxKind::AtMarkdown);
481 p.expect(SyntaxKind::At);
482 debug_assert!(p.peek().as_str().ends_with("markdown"));
483 p.expect(SyntaxKind::Identifier); p.expect(SyntaxKind::LParent);
485
486 let mut has_content = false;
487 loop {
488 let peek = p.peek();
489 if peek.kind() != SyntaxKind::StringLiteral {
490 break;
491 }
492 if peek.as_str().ends_with('{') {
493 parse_template_string(&mut *p)
494 } else {
495 p.consume()
496 }
497 has_content = true;
498 }
499
500 if !has_content {
501 p.error("Expected string literal");
502 p.until(SyntaxKind::RParent);
503 return;
504 }
505
506 if !p.expect(SyntaxKind::RParent) {
507 p.until(SyntaxKind::RParent);
508 }
509}
510
511#[cfg_attr(test, parser_test)]
512fn parse_keys(p: &mut impl Parser) {
521 let mut p = p.start_node(SyntaxKind::AtKeys);
522 p.expect(SyntaxKind::At);
523 debug_assert_eq!(p.peek().as_str(), "keys");
524 p.expect(SyntaxKind::Identifier); p.expect(SyntaxKind::LParent);
526
527 let mut key_count = 0_u32;
529
530 let mut alt_count = 0_u32;
531 let mut control_count = 0_u32;
532 let mut shift_count = 0_u32;
533 let mut meta_count = 0_u32;
534 let mut ignore_shift_count = 0_u32;
535 let mut ignore_alt_count = 0_u32;
536
537 #[derive(Eq, PartialEq)]
538 enum State {
539 Start,
540 NeedPlus,
541 NeedKey,
542 }
543 let mut state = State::Start;
544
545 fn bail(p: &mut crate::parser::Node<'_, impl Parser>, message: &str) {
546 p.error(message);
547 p.until(SyntaxKind::RParent);
548 }
549
550 loop {
551 match p.peek().kind() {
552 SyntaxKind::RParent => {
553 assert!(key_count <= 1);
554 if state == State::NeedKey {
556 p.error("Expected another identifier or string literal");
557 } else if key_count == 0
558 && (alt_count + control_count + shift_count + meta_count) > 0
559 {
560 p.error("A keyboard shortcut must be empty or contain exactly one key (with modifiers)");
561 }
562 p.consume();
563 break;
564 }
565 SyntaxKind::Plus => {
566 if state == State::NeedPlus {
567 state = State::NeedKey;
568 p.consume();
569 } else {
570 bail(
571 &mut p,
572 "Unexpected '+' in keyboard shortcut (use Plus to refer to the key)",
573 );
574 break;
575 }
576 continue;
577 }
578 SyntaxKind::Identifier | SyntaxKind::StringLiteral => {
579 if state == State::NeedPlus {
580 bail(&mut p, "Expected '+' to separate parts of a keyboard shortcut");
581 break;
582 }
583
584 let token = p.peek();
585 let mut consume_count = 1;
586 if token.kind() == SyntaxKind::Identifier {
588 let text = token.as_str();
589
590 let mut try_consume_question = || -> bool {
591 let next_token = p.nth(1);
592 if next_token.kind() == SyntaxKind::Question {
593 consume_count += 1;
594 true
595 } else {
596 false
597 }
598 };
599
600 match text {
601 "Ctrl" => {
602 bail(&mut p, "Ctrl is not in the Key namespace (Use Control instead)");
603 break;
604 }
605 "Control" => control_count += 1,
606 "Meta" => meta_count += 1,
607 "Alt" => {
608 if try_consume_question() {
609 ignore_alt_count += 1;
610 } else {
611 alt_count += 1
612 }
613 }
614 "Shift" => {
615 if try_consume_question() {
616 ignore_shift_count += 1;
617 } else {
618 shift_count += 1;
619 }
620 }
621 "AltR" | "ShiftR" | "MetaR" | "ControlR" => {
622 bail(&mut p, "Right-side modifiers are not supported");
623 break;
624 }
625 "AltGr" => {
626 bail(&mut p, "AltGr cannot be used as a modifier");
627 break;
628 }
629 "Command" | "Cmd" => {
630 bail(
631 &mut p,
632 &format!(
635 "{text} is not a cross-platform modifier\n\
636 Use cross-platform modifier names instead:\n\
637 \x20 ⌘ command -> Control\n\
638 \x20 ⌥ option -> Alt\n\
639 \x20 ^ control -> Meta\n\
640 \x20 ⇧ shift -> Shift"
641 ),
642 );
643 break;
644 }
645 "Win" | "Windows" => {
646 bail(
647 &mut p,
648 &format!(
649 "{text} is not a cross-platform modifier (Use `Meta` instead)"
650 ),
651 );
652 break;
653 }
654 _ => key_count += 1,
655 }
656 } else {
657 key_count += 1;
658 }
659
660 state = State::NeedPlus;
661
662 if [
663 alt_count,
664 control_count,
665 meta_count,
666 shift_count,
667 ignore_shift_count,
668 ignore_alt_count,
669 ]
670 .into_iter()
671 .max()
672 .unwrap_or_default()
673 > 1
674 {
675 bail(&mut p, "Duplicated modifier in keyboard shortcut");
676 break;
677 }
678 if shift_count > 0 && ignore_shift_count > 0 {
679 bail(&mut p, "Cannot use both Shift and Shift? (remove one of them)");
680 break;
681 }
682 if alt_count > 0 && ignore_alt_count > 0 {
683 bail(&mut p, "Cannot use both Alt and Alt? (remove one of them)");
684 break;
685 }
686 if key_count > 1 {
687 bail(&mut p, "A keyboard shortcut can only contain one key (with modifiers)");
688 break;
689 }
690
691 for _ in 0..consume_count {
692 p.consume();
693 }
694 continue;
695 }
696 _ => {
697 let hint = if state == State::NeedKey {
698 format!("\n(Consider using \"{}\")", p.peek().as_str())
699 } else {
700 "".into()
701 };
702 bail(
703 &mut p,
704 &format!(
705 "Expected '+', a string literal, or an identifier in the Keys namespace{hint}"
706 ),
707 );
708 break;
709 }
710 }
711 }
712}
713
714#[cfg_attr(test, parser_test)]
715fn parse_image_url(p: &mut impl Parser) {
722 let mut p = p.start_node(SyntaxKind::AtImageUrl);
723 p.consume(); p.consume(); if !(p.expect(SyntaxKind::LParent)) {
726 return;
727 }
728 let peek = p.peek();
729 if peek.kind() != SyntaxKind::StringLiteral {
730 p.error("@image-url must contain a plain path as a string literal");
731 p.until(SyntaxKind::RParent);
732 return;
733 }
734 if !peek.as_str().starts_with('"') || !peek.as_str().ends_with('"') {
735 p.error("@image-url must contain a plain path as a string literal, without any '\\{}' expressions");
736 p.until(SyntaxKind::RParent);
737 return;
738 }
739 p.expect(SyntaxKind::StringLiteral);
740 if !p.test(SyntaxKind::Comma) {
741 if !p.test(SyntaxKind::RParent) {
742 p.error("Expected ')' or ','");
743 p.until(SyntaxKind::RParent);
744 }
745 return;
746 }
747 if p.test(SyntaxKind::RParent) {
748 return;
749 }
750 if p.peek().as_str() != "nine-slice" {
751 p.error("Expected 'nine-slice(...)' argument");
752 p.until(SyntaxKind::RParent);
753 return;
754 }
755 p.consume();
756 if !p.expect(SyntaxKind::LParent) {
757 p.until(SyntaxKind::RParent);
758 return;
759 }
760 let mut count = 0;
761 loop {
762 match p.peek().kind() {
763 SyntaxKind::RParent => {
764 if count != 1 && count != 2 && count != 4 {
765 p.error("Expected 1 or 2 or 4 numbers");
766 }
767 p.consume();
768 break;
769 }
770 SyntaxKind::NumberLiteral => {
771 count += 1;
772 p.consume();
773 }
774 SyntaxKind::Comma | SyntaxKind::Colon => {
775 p.error("Arguments of nine-slice need to be separated by spaces");
776 p.until(SyntaxKind::RParent);
777 break;
778 }
779 _ => {
780 p.error("Expected number literal or ')'");
781 p.until(SyntaxKind::RParent);
782 break;
783 }
784 }
785 }
786 if !p.expect(SyntaxKind::RParent) {
787 p.until(SyntaxKind::RParent);
788 }
789}