Skip to main content

i_slint_compiler/parser/
element.rs

1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
3
4//! The parser functions for elements and things inside them
5
6use super::document::parse_qualified_name;
7use super::expressions::parse_expression;
8use super::prelude::*;
9use super::statements::parse_statement;
10use super::r#type::parse_type;
11
12#[cfg_attr(test, parser_test)]
13/// ```test,Element
14/// Item { }
15/// Item { property: value; SubElement { } }
16/// Item { if true: Rectangle {} }
17/// Item { match foo { 1: Rectangle {} } }
18/// ```
19pub fn parse_element(p: &mut impl Parser) -> bool {
20    let mut p = p.start_node(SyntaxKind::Element);
21    if !parse_qualified_name(&mut *p) {
22        return if p.test(SyntaxKind::LBrace) {
23            // recover
24            parse_element_content(&mut *p);
25            p.expect(SyntaxKind::RBrace)
26        } else {
27            false
28        };
29    }
30
31    if !p.expect(SyntaxKind::LBrace) {
32        return false;
33    }
34
35    parse_element_content(&mut *p);
36
37    p.expect(SyntaxKind::RBrace)
38}
39
40#[cfg_attr(test, parser_test)]
41/// ```test
42/// property1: value; property2: value;
43/// sub := Sub { }
44/// for xx in model: Sub {}
45/// if condition : Sub {}
46/// clicked => {}
47/// callback foobar;
48/// property<int> width;
49/// animate someProp { }
50/// animate * { }
51/// @children
52/// @deprecated property alias <=> two.way;
53/// @shadowable @deprecated in-out property <int> yyy <=> two.way;
54/// @deprecated("Use 'foobar' instead") callback old_callback <=> foobar;
55/// @shadowable public function foo() {}
56/// double_binding <=> element.property;
57/// public pure function foo() {}
58/// changed foo => {}
59/// match (foo) { 1: Elem { } }
60/// match bar.property { 1: Elem { } }
61/// slot header;
62/// header {}
63/// header << HeaderComponent {}
64/// header << parentHeader;
65/// ```
66pub fn parse_element_content(p: &mut impl Parser) {
67    let mut had_parse_error = false;
68    loop {
69        match p.nth(0).kind() {
70            SyntaxKind::RBrace => return,
71            SyntaxKind::Eof => return,
72            SyntaxKind::Identifier => match p.nth(1).kind() {
73                SyntaxKind::Identifier | SyntaxKind::Semicolon if p.peek().as_str() == "slot" => {
74                    parse_slot_declaration(&mut *p)
75                }
76                SyntaxKind::Colon => parse_property_binding(&mut *p),
77                SyntaxKind::DoubleLess => {
78                    had_parse_error |= !parse_slot_assignment_or_forwarding(&mut *p)
79                }
80                SyntaxKind::ColonEqual | SyntaxKind::LBrace => {
81                    had_parse_error |= !parse_sub_element(&mut *p);
82                }
83                SyntaxKind::FatArrow | SyntaxKind::LParent
84                    if !["if", "match"].contains(&p.peek().as_str()) =>
85                {
86                    parse_callback_connection(&mut *p)
87                }
88                SyntaxKind::DoubleArrow => parse_two_way_binding(&mut *p),
89                SyntaxKind::Identifier if p.peek().as_str() == "for" => {
90                    parse_repeated_element(&mut *p);
91                }
92                SyntaxKind::Identifier
93                    if p.peek().as_str() == "callback"
94                        || (p.peek().as_str() == "pure" && p.nth(1).as_str() == "callback") =>
95                {
96                    parse_callback_declaration(&mut *p, None);
97                }
98                SyntaxKind::Identifier
99                    if p.peek().as_str() == "function"
100                        || (matches!(p.peek().as_str(), "public" | "pure" | "protected")
101                            && p.nth(1).as_str() == "function")
102                        || (matches!(p.nth(1).as_str(), "public" | "pure" | "protected")
103                            && p.nth(2).as_str() == "function") =>
104                {
105                    parse_function(&mut *p, None);
106                }
107                SyntaxKind::Identifier | SyntaxKind::Star if p.peek().as_str() == "animate" => {
108                    parse_property_animation(&mut *p);
109                }
110                SyntaxKind::Identifier if p.peek().as_str() == "changed" => {
111                    parse_changed_callback(&mut *p);
112                }
113                SyntaxKind::LAngle | SyntaxKind::Identifier if p.peek().as_str() == "property" => {
114                    parse_property_declaration(&mut *p, None);
115                }
116                SyntaxKind::Identifier
117                    if p.nth(1).as_str() == "property"
118                        && matches!(
119                            p.peek().as_str(),
120                            "in" | "out" | "in_out" | "in-out" | "private"
121                        ) =>
122                {
123                    parse_property_declaration(&mut *p, None);
124                }
125                _ if p.peek().as_str() == "if" => {
126                    parse_if_element(&mut *p);
127                }
128                SyntaxKind::Identifier | SyntaxKind::LParent if p.peek().as_str() == "match" => {
129                    let mut i = 2;
130                    loop {
131                        match p.nth(i).kind() {
132                            SyntaxKind::FatArrow => {
133                                parse_callback_connection(&mut *p);
134                                break;
135                            }
136                            SyntaxKind::LBrace => {
137                                parse_match_element(&mut *p);
138                                break;
139                            }
140                            SyntaxKind::Eof => {
141                                if !had_parse_error {
142                                    p.error("Error: Expected '{'");
143                                    had_parse_error = true;
144                                }
145                                break;
146                            }
147                            _ => i += 1,
148                        }
149                    }
150                }
151                SyntaxKind::LBracket if p.peek().as_str() == "states" => {
152                    parse_states(&mut *p);
153                }
154                SyntaxKind::LBracket if p.peek().as_str() == "transitions" => {
155                    parse_transitions(&mut *p);
156                }
157                SyntaxKind::Identifier if p.peek().as_str() == "implement" => {
158                    parse_implement_statement(&mut *p);
159                }
160                _ => {
161                    if p.peek().as_str() == "changed" {
162                        // Try to recover some errors
163                        parse_changed_callback(&mut *p);
164                    } else {
165                        p.consume();
166                        if !had_parse_error {
167                            p.error("Parse error");
168                            had_parse_error = true;
169                        }
170                    }
171                }
172            },
173            SyntaxKind::At => {
174                if matches!(p.nth(1).as_str(), "deprecated" | "shadowable") {
175                    let checkpoint = p.checkpoint();
176                    let attribute = parse_member_attributes(&mut *p);
177                    // skip the visibility/purity keywords to reach the member keyword
178                    let mut i = 0;
179                    while matches!(
180                        p.nth(i).as_str(),
181                        "in" | "out"
182                            | "in-out"
183                            | "in_out"
184                            | "private"
185                            | "public"
186                            | "protected"
187                            | "pure"
188                    ) {
189                        i += 1;
190                    }
191                    match p.nth(i).as_str() {
192                        "callback" => parse_callback_declaration(&mut *p, Some(checkpoint)),
193                        "function" => parse_function(&mut *p, Some(checkpoint)),
194                        "property" => parse_property_declaration(&mut *p, Some(checkpoint)),
195                        _ => {
196                            (0..i).for_each(|_| p.consume());
197                            if let Some(attribute) = attribute {
198                                p.error(format!(
199                                    "@{attribute} can only be applied to a member declaration"
200                                ));
201                            }
202                        }
203                    }
204                    continue;
205                }
206                let checkpoint = p.checkpoint();
207                p.consume();
208                if p.peek().as_str() == "children" {
209                    let mut p =
210                        p.start_node_at(checkpoint.clone(), SyntaxKind::ChildrenPlaceholder);
211                    p.consume()
212                } else {
213                    p.test(SyntaxKind::Identifier);
214                    p.error("Parse error: Expected @children")
215                }
216            }
217            _ => {
218                if !had_parse_error {
219                    p.error("Parse error");
220                    had_parse_error = true;
221                }
222                p.consume();
223            }
224        }
225    }
226}
227
228#[cfg_attr(test, parser_test)]
229/// ```test,SubElement
230/// Bar {}
231/// foo := Bar {}
232/// Bar { x : y ; }
233/// ```
234/// Must consume at least one token
235fn parse_sub_element(p: &mut impl Parser) -> bool {
236    let mut p = p.start_node(SyntaxKind::SubElement);
237    if p.nth(1).kind() == SyntaxKind::ColonEqual {
238        p.expect(SyntaxKind::Identifier);
239        p.expect(SyntaxKind::ColonEqual);
240    }
241    parse_element(&mut *p)
242}
243
244#[cfg_attr(test, parser_test)]
245/// ```test,SlotDeclaration
246/// slot header;
247/// ```
248fn parse_slot_declaration(p: &mut impl Parser) {
249    debug_assert_eq!(p.peek().as_str(), "slot");
250    let mut p = p.start_node(SyntaxKind::SlotDeclaration);
251    p.expect(SyntaxKind::Identifier); // "slot"
252    {
253        let mut p = p.start_node(SyntaxKind::DeclaredIdentifier);
254        p.expect(SyntaxKind::Identifier);
255    }
256    p.expect(SyntaxKind::Semicolon);
257}
258
259#[cfg_attr(test, parser_test)]
260/// ```test,SlotAssignment
261/// header << HeaderComponent {}
262/// ```
263fn parse_slot_assignment(p: &mut impl Parser) -> bool {
264    let mut p = p.start_node(SyntaxKind::SlotAssignment);
265    {
266        let mut p = p.start_node(SyntaxKind::DeclaredIdentifier);
267        p.expect(SyntaxKind::Identifier);
268    }
269    p.expect(SyntaxKind::DoubleLess);
270    if !parse_sub_element(&mut *p) {
271        p.error("Expected element after '<<'");
272        return false;
273    }
274    true
275}
276
277#[cfg_attr(test, parser_test)]
278/// ```test,SlotForwarding
279/// hostHeader << header;
280/// hostHeader << header + 1;
281/// ```
282fn parse_slot_forwarding(p: &mut impl Parser) -> bool {
283    let mut p = p.start_node(SyntaxKind::SlotForwarding);
284    {
285        let mut p = p.start_node(SyntaxKind::DeclaredIdentifier);
286        p.expect(SyntaxKind::Identifier);
287    }
288    p.expect(SyntaxKind::DoubleLess);
289    if !parse_expression(&mut *p) {
290        return false;
291    }
292    p.expect(SyntaxKind::Semicolon)
293}
294
295fn parse_slot_assignment_or_forwarding(p: &mut impl Parser) -> bool {
296    debug_assert_eq!(p.nth(1).kind(), SyntaxKind::DoubleLess);
297    let rhs_first = p.nth(2).kind();
298    let rhs_second = p.nth(3).kind();
299
300    let is_slot_assignment = rhs_first == SyntaxKind::Identifier
301        && matches!(rhs_second, SyntaxKind::LBrace | SyntaxKind::ColonEqual);
302
303    if is_slot_assignment { parse_slot_assignment(p) } else { parse_slot_forwarding(p) }
304}
305
306#[cfg_attr(test, parser_test)]
307/// ```test,RepeatedElement
308/// for xx in mm: Elem { }
309/// for [idx] in mm: Elem { }
310/// for xx [idx] in foo.bar: Elem { }
311/// for _ in (xxx()): blah := Elem { Elem{} }
312/// ```
313/// Must consume at least one token
314fn parse_repeated_element(p: &mut impl Parser) {
315    debug_assert_eq!(p.peek().as_str(), "for");
316    let mut p = p.start_node(SyntaxKind::RepeatedElement);
317    p.expect(SyntaxKind::Identifier); // "for"
318    if p.nth(0).kind() == SyntaxKind::Identifier {
319        let mut p = p.start_node(SyntaxKind::DeclaredIdentifier);
320        p.expect(SyntaxKind::Identifier);
321    }
322    if p.nth(0).kind() == SyntaxKind::LBracket {
323        let mut p = p.start_node(SyntaxKind::RepeatedIndex);
324        p.expect(SyntaxKind::LBracket);
325        p.expect(SyntaxKind::Identifier);
326        p.expect(SyntaxKind::RBracket);
327    }
328    if p.peek().as_str() != "in" {
329        p.error("Invalid 'for' syntax: there should be a 'in' token");
330        drop(p.start_node(SyntaxKind::Expression));
331        drop(p.start_node(SyntaxKind::SubElement).start_node(SyntaxKind::Element));
332        return;
333    }
334    p.consume(); // "in"
335    parse_expression(&mut *p);
336    p.expect(SyntaxKind::Colon);
337    parse_sub_element(&mut *p);
338}
339
340#[cfg_attr(test, parser_test)]
341/// ```test,ConditionalElement
342/// if (condition) : Elem { }
343/// if (foo ? bar : xx) : Elem { foo:bar; Elem {}}
344/// if (true) : foo := Elem {}
345/// if true && true : Elem {}
346/// ```
347/// Must consume at least one token
348fn parse_if_element(p: &mut impl Parser) {
349    debug_assert_eq!(p.peek().as_str(), "if");
350    let mut p = p.start_node(SyntaxKind::ConditionalElement);
351    p.expect(SyntaxKind::Identifier); // "if"
352    parse_expression(&mut *p);
353    if !p.expect(SyntaxKind::Colon) {
354        drop(p.start_node(SyntaxKind::SubElement).start_node(SyntaxKind::Element));
355        return;
356    }
357    parse_sub_element(&mut *p);
358}
359
360#[cfg_attr(test, parser_test)]
361/// ```test,MatchElement
362/// match (foo) { one_case: Elem { } }
363/// match foo { one_case: Elem { } another_case: Elem { } }
364/// match (foo) { one_case: Elem { } another_case: Elem { } *: Elem { } }
365/// ```
366fn parse_match_element(p: &mut impl Parser) {
367    debug_assert_eq!(p.peek().as_str(), "match");
368    let mut p = p.start_node(SyntaxKind::MatchElement);
369    p.expect(SyntaxKind::Identifier); // "match"
370    parse_expression(&mut *p);
371    if !p.test(SyntaxKind::LBrace) {
372        p.error("Expected '{' to start cases");
373    }
374    while ![SyntaxKind::RBrace, SyntaxKind::Star, SyntaxKind::Eof].contains(&p.peek().kind()) {
375        parse_match_case(&mut *p);
376    }
377    if p.peek().kind() == SyntaxKind::Star {
378        parse_wildcard_case(&mut *p);
379        let mut reported = false;
380        while p.peek().kind() == SyntaxKind::Star
381            || (![SyntaxKind::RBrace, SyntaxKind::Eof].contains(&p.peek().kind())
382                && p.nth(1).kind() == SyntaxKind::Colon)
383        {
384            if !reported {
385                p.error("Cases after the '*' case are never hit");
386                reported = true;
387            }
388            if p.peek().kind() == SyntaxKind::Star {
389                parse_wildcard_case(&mut *p);
390            } else {
391                parse_match_case(&mut *p);
392            }
393        }
394    }
395    p.expect(SyntaxKind::RBrace);
396}
397
398#[cfg_attr(test, parser_test)]
399/// ```test,MatchCase
400/// foo: Elem { }
401/// (foo): Elem { }
402/// foo: { }
403/// ```
404fn parse_match_case(p: &mut impl Parser) {
405    let mut p = p.start_node(SyntaxKind::MatchCase);
406    parse_expression(&mut *p);
407    parse_case_inner(&mut *p, "match case");
408}
409
410#[cfg_attr(test, parser_test)]
411/// ```test,WildcardMatchCase
412/// *: Elem { }
413/// ```
414fn parse_wildcard_case(p: &mut impl Parser) {
415    debug_assert_eq!(p.peek().kind(), SyntaxKind::Star);
416    let mut p = p.start_node(SyntaxKind::WildcardMatchCase);
417    p.expect(SyntaxKind::Star);
418    parse_case_inner(&mut *p, "'*'");
419}
420
421fn parse_case_inner(p: &mut impl Parser, after: &str) {
422    if !p.test(SyntaxKind::Colon) {
423        p.error(format!("Expected ':' after {after}"));
424        if p.peek().kind() != SyntaxKind::Identifier {
425            p.consume();
426        }
427    }
428    if p.peek().kind() == SyntaxKind::LBrace {
429        // pass case
430        p.expect(SyntaxKind::LBrace);
431        if p.peek().kind() == SyntaxKind::Identifier {
432            p.error("Remove '{ }' around case element");
433            parse_sub_element(&mut *p);
434        }
435        p.expect(SyntaxKind::RBrace);
436        return;
437    }
438    parse_sub_element(&mut *p);
439}
440
441#[cfg_attr(test, parser_test)]
442/// ```test,Binding
443/// foo: bar;
444/// foo: {}
445/// ```
446fn parse_property_binding(p: &mut impl Parser) {
447    let mut p = p.start_node(SyntaxKind::Binding);
448    p.consume();
449    p.expect(SyntaxKind::Colon);
450    parse_binding_expression(&mut *p);
451}
452
453#[cfg_attr(test, parser_test)]
454/// ```test,BindingExpression
455/// {  }
456/// expression ;
457/// {expression }
458/// {object: 42};
459/// ```
460fn parse_binding_expression(p: &mut impl Parser) -> bool {
461    let mut p = p.start_node(SyntaxKind::BindingExpression);
462    if p.nth(0).kind() == SyntaxKind::LBrace && p.nth(2).kind() != SyntaxKind::Colon {
463        parse_code_block(&mut *p);
464        p.test(SyntaxKind::Semicolon);
465        true
466    } else if parse_expression(&mut *p) {
467        p.expect(SyntaxKind::Semicolon)
468    } else {
469        p.test(SyntaxKind::Semicolon);
470        false
471    }
472}
473
474#[cfg_attr(test, parser_test)]
475/// ```test,CodeBlock
476/// {  }
477/// { expression }
478/// { expression ; expression }
479/// { expression ; expression ; }
480/// { ;;;; }
481/// ```
482pub fn parse_code_block(p: &mut impl Parser) {
483    let mut p = p.start_node(SyntaxKind::CodeBlock);
484    p.expect(SyntaxKind::LBrace); // Or assert?
485
486    while p.nth(0).kind() != SyntaxKind::RBrace {
487        if !parse_statement(&mut *p) {
488            break;
489        }
490    }
491    p.expect(SyntaxKind::RBrace);
492}
493
494#[cfg_attr(test, parser_test)]
495/// ```test,CallbackConnection
496/// clicked => {}
497/// clicked => bar ;
498/// clicked => { foo; } ;
499/// clicked() => { foo; }
500/// mouse_move(x, y) => {}
501/// mouse_move(x, y, ) => { bar; goo; }
502/// ```
503fn parse_callback_connection(p: &mut impl Parser) {
504    let mut p = p.start_node(SyntaxKind::CallbackConnection);
505    p.consume(); // the identifier
506    if p.test(SyntaxKind::LParent) {
507        while p.peek().kind() != SyntaxKind::RParent {
508            {
509                let mut p = p.start_node(SyntaxKind::DeclaredIdentifier);
510                p.expect(SyntaxKind::Identifier);
511            }
512            if !p.test(SyntaxKind::Comma) {
513                break;
514            }
515        }
516        p.expect(SyntaxKind::RParent);
517    }
518    p.expect(SyntaxKind::FatArrow);
519    if p.nth(0).kind() == SyntaxKind::LBrace && p.nth(2).kind() != SyntaxKind::Colon {
520        parse_code_block(&mut *p);
521        p.test(SyntaxKind::Semicolon);
522    } else if parse_expression(&mut *p) {
523        p.expect(SyntaxKind::Semicolon);
524    } else {
525        p.test(SyntaxKind::Semicolon);
526    }
527}
528
529#[cfg_attr(test, parser_test)]
530/// ```test,TwoWayBinding
531/// foo <=> bar;
532/// foo <=> bar.xxx;
533/// ```
534fn parse_two_way_binding(p: &mut impl Parser) {
535    let mut p = p.start_node(SyntaxKind::TwoWayBinding);
536    p.consume(); // the identifier
537    p.expect(SyntaxKind::DoubleArrow);
538    parse_expression(&mut *p);
539    p.expect(SyntaxKind::Semicolon);
540}
541
542#[cfg_attr(test, parser_test)]
543/// ```test,ImplementStatement
544/// implement Foo <=> self;
545/// implement Foo <=> root;
546/// implement Foo <=> parent;
547/// implement Foo <=> inner;
548/// implement Qualified.Foo <=> self;
549/// ```
550fn parse_implement_statement(p: &mut impl Parser) {
551    debug_assert_eq!(p.peek().as_str(), "implement");
552    let mut p = p.start_node(SyntaxKind::ImplementStatement);
553    p.expect(SyntaxKind::Identifier); // "implement"
554    parse_qualified_name(&mut *p);
555    p.expect(SyntaxKind::DoubleArrow);
556    {
557        let mut p = p.start_node(SyntaxKind::DeclaredIdentifier);
558        p.expect(SyntaxKind::Identifier);
559    }
560    p.expect(SyntaxKind::Semicolon);
561}
562
563#[cfg_attr(test, parser_test)]
564/// ```test,CallbackDeclaration
565/// callback foobar;
566/// callback my_callback();
567/// callback foo(int, string);
568/// callback foo(foo: int, string, xx: { a: string });
569/// pure callback one_arg({ a: string, b: string});
570/// callback end_coma(a, b, c,);
571/// callback with_return(a, b) -> int;
572/// callback with_return2({a: string}) -> { a: string };
573/// callback foobar <=> elem.foobar;
574/// ```
575/// Must consume at least one token
576fn parse_callback_declaration<P: Parser>(p: &mut P, checkpoint: Option<P::Checkpoint>) {
577    let checkpoint = checkpoint.unwrap_or_else(|| p.checkpoint());
578    let mut p = p.start_node_at(checkpoint, SyntaxKind::CallbackDeclaration);
579    if p.peek().as_str() == "pure" {
580        p.consume();
581    }
582    debug_assert_eq!(p.peek().as_str(), "callback");
583    p.expect(SyntaxKind::Identifier); // "callback"
584    {
585        let mut p = p.start_node(SyntaxKind::DeclaredIdentifier);
586        p.expect(SyntaxKind::Identifier);
587    }
588    if p.test(SyntaxKind::LParent) {
589        while p.peek().kind() != SyntaxKind::RParent {
590            {
591                let mut p = p.start_node(SyntaxKind::CallbackDeclarationParameter);
592                if p.peek().kind() == SyntaxKind::Identifier && p.nth(1).kind() == SyntaxKind::Colon
593                {
594                    {
595                        let mut p = p.start_node(SyntaxKind::DeclaredIdentifier);
596                        p.expect(SyntaxKind::Identifier);
597                    }
598                    p.expect(SyntaxKind::Colon);
599                }
600                parse_type(&mut *p);
601            }
602            if !p.test(SyntaxKind::Comma) {
603                break;
604            }
605        }
606        p.expect(SyntaxKind::RParent);
607        if p.test(SyntaxKind::Arrow) {
608            let mut p = p.start_node(SyntaxKind::ReturnType);
609            parse_type(&mut *p);
610        }
611
612        if p.peek().kind() == SyntaxKind::DoubleArrow {
613            p.error("When declaring a callback alias, one must omit parentheses. e.g. 'callback foo <=> other.bar;'");
614        }
615    } else if p.test(SyntaxKind::Arrow) {
616        // Force callback with return value to also have parentheses, we could remove this
617        // restriction in the future
618        p.error("Callback with return value must be declared with parentheses e.g. 'callback foo() -> int;'");
619        parse_type(&mut *p);
620    }
621
622    if p.peek().kind() == SyntaxKind::DoubleArrow {
623        let mut p = p.start_node(SyntaxKind::TwoWayBinding);
624        p.expect(SyntaxKind::DoubleArrow);
625        parse_expression(&mut *p);
626    }
627
628    p.expect(SyntaxKind::Semicolon);
629}
630
631#[cfg_attr(test, parser_test)]
632/// ```test
633/// @deprecated
634/// @deprecated("Some message")
635/// @shadowable
636/// ```
637fn parse_member_attributes(p: &mut impl Parser) -> Option<&'static str> {
638    let mut seen: Vec<&'static str> = Vec::new();
639    while p.nth(0).kind() == SyntaxKind::At
640        && matches!(p.nth(1).as_str(), "deprecated" | "shadowable")
641    {
642        let is_deprecated = p.nth(1).as_str() == "deprecated";
643        let (name, kind) = if is_deprecated {
644            ("deprecated", SyntaxKind::PropertyDeprecation)
645        } else {
646            ("shadowable", SyntaxKind::ShadowableAttribute)
647        };
648        let duplicated = seen.contains(&name);
649        seen.push(name);
650        let mut p = p.start_node(kind);
651        p.consume(); // "@"
652        p.consume(); // the attribute name
653        if duplicated {
654            p.error(format!("Duplicated @{name} attribute"));
655        }
656        if is_deprecated && p.test(SyntaxKind::LParent) {
657            let peek = p.peek();
658            if peek.kind() != SyntaxKind::StringLiteral
659                || !peek.as_str().starts_with('"')
660                || !peek.as_str().ends_with('"')
661            {
662                p.error("@deprecated message must be a plain string literal, without any '\\{}' expressions");
663                p.until(SyntaxKind::RParent);
664            } else {
665                p.expect(SyntaxKind::StringLiteral);
666                p.expect(SyntaxKind::RParent);
667            }
668        }
669    }
670    seen.first().copied()
671}
672
673#[cfg_attr(test, parser_test)]
674/// ```test,PropertyDeclaration
675/// in property <int> xxx;
676/// property<int> foobar;
677/// property<string> text: "Something";
678/// property<string> text <=> two.way;
679/// property alias <=> two.way;
680/// ```
681fn parse_property_declaration<P: Parser>(p: &mut P, checkpoint: Option<P::Checkpoint>) {
682    let checkpoint = checkpoint.unwrap_or_else(|| p.checkpoint());
683    while matches!(p.peek().as_str(), "in" | "out" | "in-out" | "in_out" | "private") {
684        p.consume();
685    }
686    if p.peek().as_str() != "property" {
687        p.error("Expected 'property' keyword");
688        return;
689    }
690    let mut p = p.start_node_at(checkpoint, SyntaxKind::PropertyDeclaration);
691    p.consume(); // property
692
693    if p.test(SyntaxKind::LAngle) {
694        parse_type(&mut *p);
695        p.expect(SyntaxKind::RAngle);
696    } else if p.nth(0).kind() == SyntaxKind::Identifier
697        && p.nth(1).kind() != SyntaxKind::DoubleArrow
698    {
699        p.error("Missing type. The syntax to declare a property is `property <type> name;`. Only two way bindings can omit the type");
700    }
701
702    {
703        let mut p = p.start_node(SyntaxKind::DeclaredIdentifier);
704        p.expect(SyntaxKind::Identifier);
705    }
706
707    match p.nth(0).kind() {
708        SyntaxKind::Colon => {
709            p.consume();
710            parse_binding_expression(&mut *p);
711        }
712        SyntaxKind::DoubleArrow => {
713            let mut p = p.start_node(SyntaxKind::TwoWayBinding);
714            p.consume();
715            parse_expression(&mut *p);
716            p.expect(SyntaxKind::Semicolon);
717        }
718        _ => {
719            p.expect(SyntaxKind::Semicolon);
720        }
721    }
722}
723
724#[cfg_attr(test, parser_test)]
725/// ```test,PropertyAnimation
726/// animate x { duration: 1000; }
727/// animate x, foo.y {  }
728/// animate * {  }
729/// ```
730fn parse_property_animation(p: &mut impl Parser) {
731    debug_assert_eq!(p.peek().as_str(), "animate");
732    let mut p = p.start_node(SyntaxKind::PropertyAnimation);
733    p.expect(SyntaxKind::Identifier); // animate
734    if p.nth(0).kind() == SyntaxKind::Star {
735        p.consume();
736    } else {
737        parse_qualified_name(&mut *p);
738        while p.nth(0).kind() == SyntaxKind::Comma {
739            p.consume();
740            parse_qualified_name(&mut *p);
741        }
742    };
743    p.expect(SyntaxKind::LBrace);
744
745    loop {
746        match p.nth(0).kind() {
747            SyntaxKind::RBrace => {
748                p.consume();
749                return;
750            }
751            SyntaxKind::Eof => return,
752            SyntaxKind::Identifier => match p.nth(1).kind() {
753                SyntaxKind::Colon => parse_property_binding(&mut *p),
754                _ => {
755                    p.consume();
756                    p.error("Only bindings are allowed in animations");
757                }
758            },
759            _ => {
760                p.consume();
761                p.error("Only bindings are allowed in animations");
762            }
763        }
764    }
765}
766
767#[cfg_attr(test, parser_test)]
768/// ```test,PropertyChangedCallback
769/// changed the-property => { x = y; }
770/// changed foo => debug(13);
771/// changed xyz => { foo() };
772/// ```
773fn parse_changed_callback(p: &mut impl Parser) {
774    debug_assert_eq!(p.peek().as_str(), "changed");
775    let mut p = p.start_node(SyntaxKind::PropertyChangedCallback);
776    p.expect(SyntaxKind::Identifier); // changed
777    {
778        let mut p = p.start_node(SyntaxKind::DeclaredIdentifier);
779        p.expect(SyntaxKind::Identifier);
780    }
781    p.expect(SyntaxKind::FatArrow);
782
783    if p.nth(0).kind() == SyntaxKind::LBrace && p.nth(2).kind() != SyntaxKind::Colon {
784        parse_code_block(&mut *p);
785        p.test(SyntaxKind::Semicolon);
786    } else if parse_expression(&mut *p) {
787        p.expect(SyntaxKind::Semicolon);
788    } else {
789        p.test(SyntaxKind::Semicolon);
790    }
791}
792
793#[cfg_attr(test, parser_test)]
794/// ```test,States
795/// states []
796/// states [ foo when bar : { x:y; } another_state : { x:z; }]
797/// ```
798fn parse_states(p: &mut impl Parser) {
799    debug_assert_eq!(p.peek().as_str(), "states");
800    let mut p = p.start_node(SyntaxKind::States);
801    p.expect(SyntaxKind::Identifier); // "states"
802    p.expect(SyntaxKind::LBracket);
803    while parse_state(&mut *p) {}
804    p.expect(SyntaxKind::RBracket);
805}
806
807#[cfg_attr(test, parser_test)]
808/// ```test,State
809/// foo : { x: 1px + 2px; aaa.y: {1px + 2px} }
810/// foo when bar == 1:  { color: blue; foo.color: red;   }
811/// a when b:  { color: blue; in { animate color { duration: 120s; } }   }
812/// a when b:  { out { animate foo.bar { } } foo.bar: 42;  }
813/// ```
814fn parse_state(p: &mut impl Parser) -> bool {
815    if p.nth(0).kind() != SyntaxKind::Identifier {
816        return false;
817    }
818    let mut p = p.start_node(SyntaxKind::State);
819    {
820        let mut p = p.start_node(SyntaxKind::DeclaredIdentifier);
821        p.expect(SyntaxKind::Identifier);
822    }
823    if p.peek().as_str() == "when" {
824        p.consume();
825        parse_expression(&mut *p);
826    }
827    p.expect(SyntaxKind::Colon);
828    if !p.expect(SyntaxKind::LBrace) {
829        return false;
830    }
831
832    loop {
833        match p.nth(0).kind() {
834            SyntaxKind::RBrace => {
835                p.consume();
836                return true;
837            }
838            SyntaxKind::Eof => return false,
839            _ => {
840                if p.nth(1).kind() == SyntaxKind::LBrace
841                    && matches!(p.peek().as_str(), "in" | "out" | "in-out" | "in_out")
842                {
843                    let mut p = p.start_node(SyntaxKind::Transition);
844                    p.consume(); // "in", "out" or "in-out"
845                    p.expect(SyntaxKind::LBrace);
846                    if !parse_transition_inner(&mut *p) {
847                        return false;
848                    }
849                    continue;
850                };
851                let checkpoint = p.checkpoint();
852                if !parse_qualified_name(&mut *p)
853                    || !p.expect(SyntaxKind::Colon)
854                    || !parse_binding_expression(&mut *p)
855                {
856                    p.test(SyntaxKind::RBrace);
857                    return false;
858                }
859                let _ = p.start_node_at(checkpoint, SyntaxKind::StatePropertyChange);
860            }
861        }
862    }
863}
864
865#[cfg_attr(test, parser_test)]
866/// ```test,Transitions
867/// transitions []
868/// transitions [in checked: {animate x { duration: 88ms; }} out checked: {animate x { duration: 88ms; }} in-out checked: {animate x { duration: 88ms; }}]
869/// ```
870fn parse_transitions(p: &mut impl Parser) {
871    debug_assert_eq!(p.peek().as_str(), "transitions");
872    let mut p = p.start_node(SyntaxKind::Transitions);
873    p.expect(SyntaxKind::Identifier); // "transitions"
874    p.expect(SyntaxKind::LBracket);
875    while p.nth(0).kind() != SyntaxKind::RBracket && parse_transition(&mut *p) {}
876    p.expect(SyntaxKind::RBracket);
877}
878
879#[cfg_attr(test, parser_test)]
880/// ```test,Transition
881/// in pressed : {}
882/// in pressed: { animate x { duration: 88ms; } }
883/// out pressed: { animate x { duration: 88ms; } }
884/// in-out pressed: { animate x { duration: 88ms; } }
885/// ```
886fn parse_transition(p: &mut impl Parser) -> bool {
887    if !matches!(p.peek().as_str(), "in" | "out" | "in-out" | "in_out") {
888        p.error("Expected 'in', 'out', or 'in-out' to declare a transition");
889        return false;
890    }
891    let mut p = p.start_node(SyntaxKind::Transition);
892    p.consume(); // "in", "out" or "in-out"
893    {
894        let mut p = p.start_node(SyntaxKind::DeclaredIdentifier);
895        p.expect(SyntaxKind::Identifier);
896    }
897    p.expect(SyntaxKind::Colon);
898    if !p.expect(SyntaxKind::LBrace) {
899        return false;
900    }
901    parse_transition_inner(&mut *p)
902}
903
904#[cfg_attr(test, parser_test)]
905/// ```test
906/// }
907/// animate x { duration: 88ms; }  animate foo.bar { } }
908/// ```
909fn parse_transition_inner(p: &mut impl Parser) -> bool {
910    loop {
911        match p.nth(0).kind() {
912            SyntaxKind::RBrace => {
913                p.consume();
914                return true;
915            }
916            SyntaxKind::Eof => return false,
917            SyntaxKind::Identifier if p.peek().as_str() == "animate" => {
918                parse_property_animation(&mut *p);
919            }
920            _ => {
921                p.consume();
922                p.error("Expected 'animate'");
923            }
924        }
925    }
926}
927
928#[cfg_attr(test, parser_test)]
929/// ```test,Function
930/// function foo() {}
931/// function bar(xx : int) { yy = xx; }
932/// function bar(xx : int,) -> int { return 42; }
933/// public function aa(x: int, b: {a: int}, c: int) {}
934/// protected pure function fff() {}
935/// function foo();
936/// function foo() -> int;
937/// ```
938fn parse_function<P: Parser>(p: &mut P, checkpoint: Option<P::Checkpoint>) {
939    let checkpoint = checkpoint.unwrap_or_else(|| p.checkpoint());
940    let mut p = p.start_node_at(checkpoint, SyntaxKind::Function);
941    if matches!(p.peek().as_str(), "public" | "protected") {
942        p.consume();
943        if p.peek().as_str() == "pure" {
944            p.consume()
945        }
946    } else if p.peek().as_str() == "pure" {
947        p.consume();
948        if matches!(p.peek().as_str(), "public" | "protected") {
949            p.consume()
950        }
951    }
952    if p.peek().as_str() != "function" {
953        p.error("Unexpected identifier");
954        p.consume();
955        while p.peek().kind == SyntaxKind::Identifier && p.peek().as_str() != "function" {
956            p.consume();
957        }
958    }
959    debug_assert_eq!(p.peek().as_str(), "function");
960    p.expect(SyntaxKind::Identifier); // "function"
961    {
962        let mut p = p.start_node(SyntaxKind::DeclaredIdentifier);
963        p.expect(SyntaxKind::Identifier);
964    }
965    if p.expect(SyntaxKind::LParent) {
966        while p.peek().kind() != SyntaxKind::RParent {
967            let mut p_arg = p.start_node(SyntaxKind::ArgumentDeclaration);
968            {
969                let mut p = p_arg.start_node(SyntaxKind::DeclaredIdentifier);
970                p.expect(SyntaxKind::Identifier);
971            }
972            p_arg.expect(SyntaxKind::Colon);
973            parse_type(&mut *p_arg);
974            drop(p_arg);
975            if !p.test(SyntaxKind::Comma) {
976                break;
977            }
978        }
979        p.expect(SyntaxKind::RParent);
980        if p.test(SyntaxKind::Arrow) {
981            let mut p = p.start_node(SyntaxKind::ReturnType);
982            parse_type(&mut *p);
983        }
984    }
985
986    if p.peek().kind() == SyntaxKind::LBrace {
987        parse_code_block(&mut *p);
988    } else if !p.test(SyntaxKind::Semicolon) {
989        p.error("Expected function body or semicolon");
990    }
991}