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::r#type::parse_type;
10use super::statements::parse_statement;
11
12#[cfg_attr(test, parser_test)]
13/// ```test,Element
14/// Item { }
15/// Item { property: value; SubElement { } }
16/// Item { if true: Rectangle {} }
17/// ```
18pub fn parse_element(p: &mut impl Parser) -> bool {
19 let mut p: Node<'_, impl Parser> = p.start_node(kind:SyntaxKind::Element);
20 if !parse_qualified_name(&mut *p) {
21 return if p.test(kind:SyntaxKind::LBrace) {
22 // recover
23 parse_element_content(&mut *p);
24 p.expect(kind:SyntaxKind::RBrace)
25 } else {
26 false
27 };
28 }
29
30 if !p.expect(kind:SyntaxKind::LBrace) {
31 return false;
32 }
33
34 parse_element_content(&mut *p);
35
36 p.expect(kind:SyntaxKind::RBrace)
37}
38
39#[cfg_attr(test, parser_test)]
40/// ```test
41/// property1: value; property2: value;
42/// sub := Sub { }
43/// for xx in model: Sub {}
44/// if condition : Sub {}
45/// clicked => {}
46/// callback foobar;
47/// property<int> width;
48/// animate someProp { }
49/// animate * { }
50/// @children
51/// double_binding <=> element.property;
52/// public pure function foo() {}
53/// changed foo => {}
54/// ```
55pub fn parse_element_content(p: &mut impl Parser) {
56 let mut had_parse_error = false;
57 loop {
58 match p.nth(0).kind() {
59 SyntaxKind::RBrace => return,
60 SyntaxKind::Eof => return,
61 SyntaxKind::Identifier => match p.nth(1).kind() {
62 SyntaxKind::Colon => parse_property_binding(&mut *p),
63 SyntaxKind::ColonEqual | SyntaxKind::LBrace => {
64 had_parse_error |= !parse_sub_element(&mut *p)
65 }
66 SyntaxKind::FatArrow | SyntaxKind::LParent if p.peek().as_str() != "if" => {
67 parse_callback_connection(&mut *p)
68 }
69 SyntaxKind::DoubleArrow => parse_two_way_binding(&mut *p),
70 SyntaxKind::Identifier if p.peek().as_str() == "for" => {
71 parse_repeated_element(&mut *p);
72 }
73 SyntaxKind::Identifier
74 if p.peek().as_str() == "callback"
75 || (p.peek().as_str() == "pure" && p.nth(1).as_str() == "callback") =>
76 {
77 parse_callback_declaration(&mut *p);
78 }
79 SyntaxKind::Identifier
80 if p.peek().as_str() == "function"
81 || (matches!(p.peek().as_str(), "public" | "pure" | "protected")
82 && p.nth(1).as_str() == "function")
83 || (matches!(p.nth(1).as_str(), "public" | "pure" | "protected")
84 && p.nth(2).as_str() == "function") =>
85 {
86 parse_function(&mut *p);
87 }
88 SyntaxKind::Identifier | SyntaxKind::Star if p.peek().as_str() == "animate" => {
89 parse_property_animation(&mut *p);
90 }
91 SyntaxKind::Identifier if p.peek().as_str() == "changed" => {
92 parse_changed_callback(&mut *p);
93 }
94 SyntaxKind::LAngle | SyntaxKind::Identifier if p.peek().as_str() == "property" => {
95 parse_property_declaration(&mut *p);
96 }
97 SyntaxKind::Identifier
98 if p.nth(1).as_str() == "property"
99 && matches!(
100 p.peek().as_str(),
101 "in" | "out" | "in_out" | "in-out" | "private"
102 ) =>
103 {
104 parse_property_declaration(&mut *p);
105 }
106 _ if p.peek().as_str() == "if" => {
107 parse_if_element(&mut *p);
108 }
109 SyntaxKind::LBracket if p.peek().as_str() == "states" => {
110 parse_states(&mut *p);
111 }
112 SyntaxKind::LBracket if p.peek().as_str() == "transitions" => {
113 parse_transitions(&mut *p);
114 }
115 _ => {
116 if p.peek().as_str() == "changed" {
117 // Try to recover some errors
118 parse_changed_callback(&mut *p);
119 } else {
120 p.consume();
121 if !had_parse_error {
122 p.error("Parse error");
123 had_parse_error = true;
124 }
125 }
126 }
127 },
128 SyntaxKind::At => {
129 let checkpoint = p.checkpoint();
130 p.consume();
131 if p.peek().as_str() == "children" {
132 let mut p =
133 p.start_node_at(checkpoint.clone(), SyntaxKind::ChildrenPlaceholder);
134 p.consume()
135 } else {
136 p.test(SyntaxKind::Identifier);
137 p.error("Parse error: Expected @children")
138 }
139 }
140 _ => {
141 if !had_parse_error {
142 p.error("Parse error");
143 had_parse_error = true;
144 }
145 p.consume();
146 }
147 }
148 }
149}
150
151#[cfg_attr(test, parser_test)]
152/// ```test,SubElement
153/// Bar {}
154/// foo := Bar {}
155/// Bar { x : y ; }
156/// ```
157/// Must consume at least one token
158fn parse_sub_element(p: &mut impl Parser) -> bool {
159 let mut p: Node<'_, impl Parser> = p.start_node(kind:SyntaxKind::SubElement);
160 if p.nth(1).kind() == SyntaxKind::ColonEqual {
161 p.expect(kind:SyntaxKind::Identifier);
162 p.expect(kind:SyntaxKind::ColonEqual);
163 }
164 parse_element(&mut *p)
165}
166
167#[cfg_attr(test, parser_test)]
168/// ```test,RepeatedElement
169/// for xx in mm: Elem { }
170/// for [idx] in mm: Elem { }
171/// for xx [idx] in foo.bar: Elem { }
172/// for _ in (xxx()): blah := Elem { Elem{} }
173/// ```
174/// Must consume at least one token
175fn parse_repeated_element(p: &mut impl Parser) {
176 debug_assert_eq!(p.peek().as_str(), "for");
177 let mut p = p.start_node(SyntaxKind::RepeatedElement);
178 p.expect(SyntaxKind::Identifier); // "for"
179 if p.nth(0).kind() == SyntaxKind::Identifier {
180 let mut p = p.start_node(SyntaxKind::DeclaredIdentifier);
181 p.expect(SyntaxKind::Identifier);
182 }
183 if p.nth(0).kind() == SyntaxKind::LBracket {
184 let mut p = p.start_node(SyntaxKind::RepeatedIndex);
185 p.expect(SyntaxKind::LBracket);
186 p.expect(SyntaxKind::Identifier);
187 p.expect(SyntaxKind::RBracket);
188 }
189 if p.peek().as_str() != "in" {
190 p.error("Invalid 'for' syntax: there should be a 'in' token");
191 drop(p.start_node(SyntaxKind::Expression));
192 drop(p.start_node(SyntaxKind::SubElement).start_node(SyntaxKind::Element));
193 return;
194 }
195 p.consume(); // "in"
196 parse_expression(&mut *p);
197 p.expect(SyntaxKind::Colon);
198 parse_sub_element(&mut *p);
199}
200
201#[cfg_attr(test, parser_test)]
202/// ```test,ConditionalElement
203/// if (condition) : Elem { }
204/// if (foo ? bar : xx) : Elem { foo:bar; Elem {}}
205/// if (true) : foo := Elem {}
206/// if true && true : Elem {}
207/// ```
208/// Must consume at least one token
209fn parse_if_element(p: &mut impl Parser) {
210 debug_assert_eq!(p.peek().as_str(), "if");
211 let mut p: Node<'_, impl Parser> = p.start_node(kind:SyntaxKind::ConditionalElement);
212 p.expect(kind:SyntaxKind::Identifier); // "if"
213 parse_expression(&mut *p);
214 if !p.expect(kind:SyntaxKind::Colon) {
215 drop(p.start_node(SyntaxKind::SubElement).start_node(kind:SyntaxKind::Element));
216 return;
217 }
218 parse_sub_element(&mut *p);
219}
220
221#[cfg_attr(test, parser_test)]
222/// ```test,Binding
223/// foo: bar;
224/// foo: {}
225/// ```
226fn parse_property_binding(p: &mut impl Parser) {
227 let mut p: Node<'_, impl Parser> = p.start_node(kind:SyntaxKind::Binding);
228 p.consume();
229 p.expect(kind:SyntaxKind::Colon);
230 parse_binding_expression(&mut *p);
231}
232
233#[cfg_attr(test, parser_test)]
234/// ```test,BindingExpression
235/// { }
236/// expression ;
237/// {expression }
238/// {object: 42};
239/// ```
240fn parse_binding_expression(p: &mut impl Parser) -> bool {
241 let mut p: Node<'_, impl Parser> = p.start_node(kind:SyntaxKind::BindingExpression);
242 if p.nth(0).kind() == SyntaxKind::LBrace && p.nth(2).kind() != SyntaxKind::Colon {
243 parse_code_block(&mut *p);
244 p.test(kind:SyntaxKind::Semicolon);
245 true
246 } else if parse_expression(&mut *p) {
247 p.expect(kind:SyntaxKind::Semicolon)
248 } else {
249 p.test(kind:SyntaxKind::Semicolon);
250 false
251 }
252}
253
254#[cfg_attr(test, parser_test)]
255/// ```test,CodeBlock
256/// { }
257/// { expression }
258/// { expression ; expression }
259/// { expression ; expression ; }
260/// { ;;;; }
261/// ```
262pub fn parse_code_block(p: &mut impl Parser) {
263 let mut p: Node<'_, impl Parser> = p.start_node(kind:SyntaxKind::CodeBlock);
264 p.expect(kind:SyntaxKind::LBrace); // Or assert?
265
266 while p.nth(0).kind() != SyntaxKind::RBrace {
267 if !parse_statement(&mut *p) {
268 break;
269 }
270 }
271 p.expect(kind:SyntaxKind::RBrace);
272}
273
274#[cfg_attr(test, parser_test)]
275/// ```test,CallbackConnection
276/// clicked => {}
277/// clicked() => { foo; }
278/// mouse_move(x, y) => {}
279/// mouse_move(x, y, ) => { bar; goo; }
280/// ```
281fn parse_callback_connection(p: &mut impl Parser) {
282 let mut p: Node<'_, impl Parser> = p.start_node(kind:SyntaxKind::CallbackConnection);
283 p.consume(); // the identifier
284 if p.test(kind:SyntaxKind::LParent) {
285 while p.peek().kind() != SyntaxKind::RParent {
286 {
287 let mut p: Node<'_, impl Parser> = p.start_node(kind:SyntaxKind::DeclaredIdentifier);
288 p.expect(kind:SyntaxKind::Identifier);
289 }
290 if !p.test(kind:SyntaxKind::Comma) {
291 break;
292 }
293 }
294 p.expect(kind:SyntaxKind::RParent);
295 }
296 p.expect(kind:SyntaxKind::FatArrow);
297 parse_code_block(&mut *p);
298}
299
300#[cfg_attr(test, parser_test)]
301/// ```test,TwoWayBinding
302/// foo <=> bar;
303/// foo <=> bar.xxx;
304/// ```
305fn parse_two_way_binding(p: &mut impl Parser) {
306 let mut p: Node<'_, impl Parser> = p.start_node(kind:SyntaxKind::TwoWayBinding);
307 p.consume(); // the identifier
308 p.expect(kind:SyntaxKind::DoubleArrow);
309 parse_expression(&mut *p);
310 p.expect(kind:SyntaxKind::Semicolon);
311}
312
313#[cfg_attr(test, parser_test)]
314/// ```test,CallbackDeclaration
315/// callback foobar;
316/// callback my_callback();
317/// callback foo(int, string);
318/// callback foo(foo: int, string, xx: { a: string });
319/// pure callback one_arg({ a: string, b: string});
320/// callback end_coma(a, b, c,);
321/// callback with_return(a, b) -> int;
322/// callback with_return2({a: string}) -> { a: string };
323/// callback foobar <=> elem.foobar;
324/// ```
325/// Must consume at least one token
326fn parse_callback_declaration(p: &mut impl Parser) {
327 let mut p = p.start_node(SyntaxKind::CallbackDeclaration);
328 if p.peek().as_str() == "pure" {
329 p.consume();
330 }
331 debug_assert_eq!(p.peek().as_str(), "callback");
332 p.expect(SyntaxKind::Identifier); // "callback"
333 {
334 let mut p = p.start_node(SyntaxKind::DeclaredIdentifier);
335 p.expect(SyntaxKind::Identifier);
336 }
337 if p.test(SyntaxKind::LParent) {
338 while p.peek().kind() != SyntaxKind::RParent {
339 {
340 let mut p = p.start_node(SyntaxKind::CallbackDeclarationParameter);
341 if p.peek().kind() == SyntaxKind::Identifier && p.nth(1).kind() == SyntaxKind::Colon
342 {
343 {
344 let mut p = p.start_node(SyntaxKind::DeclaredIdentifier);
345 p.expect(SyntaxKind::Identifier);
346 }
347 p.expect(SyntaxKind::Colon);
348 }
349 parse_type(&mut *p);
350 }
351 if !p.test(SyntaxKind::Comma) {
352 break;
353 }
354 }
355 p.expect(SyntaxKind::RParent);
356 if p.test(SyntaxKind::Arrow) {
357 let mut p = p.start_node(SyntaxKind::ReturnType);
358 parse_type(&mut *p);
359 }
360
361 if p.peek().kind() == SyntaxKind::DoubleArrow {
362 p.error("When declaring a callback alias, one must omit parentheses. e.g. 'callback foo <=> other.bar;'");
363 }
364 } else if p.test(SyntaxKind::Arrow) {
365 // Force callback with return value to also have parentheses, we could remove this
366 // restriction in the future
367 p.error("Callback with return value must be declared with parentheses e.g. 'callback foo() -> int;'");
368 parse_type(&mut *p);
369 }
370
371 if p.peek().kind() == SyntaxKind::DoubleArrow {
372 let mut p = p.start_node(SyntaxKind::TwoWayBinding);
373 p.expect(SyntaxKind::DoubleArrow);
374 parse_expression(&mut *p);
375 }
376
377 p.expect(SyntaxKind::Semicolon);
378}
379
380#[cfg_attr(test, parser_test)]
381/// ```test,PropertyDeclaration
382/// in property <int> xxx;
383/// property<int> foobar;
384/// property<string> text: "Something";
385/// property<string> text <=> two.way;
386/// property alias <=> two.way;
387/// ```
388fn parse_property_declaration(p: &mut impl Parser) {
389 let checkpoint = p.checkpoint();
390 while matches!(p.peek().as_str(), "in" | "out" | "in-out" | "in_out" | "private") {
391 p.consume();
392 }
393 if p.peek().as_str() != "property" {
394 p.error("Expected 'property' keyword");
395 return;
396 }
397 let mut p = p.start_node_at(checkpoint, SyntaxKind::PropertyDeclaration);
398 p.consume(); // property
399
400 if p.test(SyntaxKind::LAngle) {
401 parse_type(&mut *p);
402 p.expect(SyntaxKind::RAngle);
403 } else if p.nth(0).kind() == SyntaxKind::Identifier
404 && p.nth(1).kind() != SyntaxKind::DoubleArrow
405 {
406 p.error("Missing type. The syntax to declare a property is `property <type> name;`. Only two way bindings can omit the type");
407 }
408
409 {
410 let mut p = p.start_node(SyntaxKind::DeclaredIdentifier);
411 p.expect(SyntaxKind::Identifier);
412 }
413
414 match p.nth(0).kind() {
415 SyntaxKind::Colon => {
416 p.consume();
417 parse_binding_expression(&mut *p);
418 }
419 SyntaxKind::DoubleArrow => {
420 let mut p = p.start_node(SyntaxKind::TwoWayBinding);
421 p.consume();
422 parse_expression(&mut *p);
423 p.expect(SyntaxKind::Semicolon);
424 }
425 _ => {
426 p.expect(SyntaxKind::Semicolon);
427 }
428 }
429}
430
431#[cfg_attr(test, parser_test)]
432/// ```test,PropertyAnimation
433/// animate x { duration: 1000; }
434/// animate x, foo.y { }
435/// animate * { }
436/// ```
437fn parse_property_animation(p: &mut impl Parser) {
438 debug_assert_eq!(p.peek().as_str(), "animate");
439 let mut p = p.start_node(SyntaxKind::PropertyAnimation);
440 p.expect(SyntaxKind::Identifier); // animate
441 if p.nth(0).kind() == SyntaxKind::Star {
442 p.consume();
443 } else {
444 parse_qualified_name(&mut *p);
445 while p.nth(0).kind() == SyntaxKind::Comma {
446 p.consume();
447 parse_qualified_name(&mut *p);
448 }
449 };
450 p.expect(SyntaxKind::LBrace);
451
452 loop {
453 match p.nth(0).kind() {
454 SyntaxKind::RBrace => {
455 p.consume();
456 return;
457 }
458 SyntaxKind::Eof => return,
459 SyntaxKind::Identifier => match p.nth(1).kind() {
460 SyntaxKind::Colon => parse_property_binding(&mut *p),
461 _ => {
462 p.consume();
463 p.error("Only bindings are allowed in animations");
464 }
465 },
466 _ => {
467 p.consume();
468 p.error("Only bindings are allowed in animations");
469 }
470 }
471 }
472}
473
474#[cfg_attr(test, parser_test)]
475/// ```test,PropertyChangedCallback
476/// changed the-property => { x = y; }
477/// ```
478fn parse_changed_callback(p: &mut impl Parser) {
479 debug_assert_eq!(p.peek().as_str(), "changed");
480 let mut p: Node<'_, impl Parser> = p.start_node(kind:SyntaxKind::PropertyChangedCallback);
481 p.expect(kind:SyntaxKind::Identifier); // changed
482 {
483 let mut p: Node<'_, impl Parser> = p.start_node(kind:SyntaxKind::DeclaredIdentifier);
484 p.expect(kind:SyntaxKind::Identifier);
485 }
486 p.expect(kind:SyntaxKind::FatArrow);
487 parse_code_block(&mut *p);
488}
489
490#[cfg_attr(test, parser_test)]
491/// ```test,States
492/// states []
493/// states [ foo when bar : { x:y; } another_state : { x:z; }]
494/// ```
495fn parse_states(p: &mut impl Parser) {
496 debug_assert_eq!(p.peek().as_str(), "states");
497 let mut p: Node<'_, impl Parser> = p.start_node(kind:SyntaxKind::States);
498 p.expect(kind:SyntaxKind::Identifier); // "states"
499 p.expect(kind:SyntaxKind::LBracket);
500 while parse_state(&mut *p) {}
501 p.expect(kind:SyntaxKind::RBracket);
502}
503
504#[cfg_attr(test, parser_test)]
505/// ```test,State
506/// foo : { x: 1px + 2px; aaa.y: {1px + 2px} }
507/// foo when bar == 1: { color: blue; foo.color: red; }
508/// a when b: { color: blue; in { animate color { duration: 120s; } } }
509/// a when b: { out { animate foo.bar { } } foo.bar: 42; }
510/// ```
511fn parse_state(p: &mut impl Parser) -> bool {
512 if p.nth(0).kind() != SyntaxKind::Identifier {
513 return false;
514 }
515 let mut p = p.start_node(SyntaxKind::State);
516 {
517 let mut p = p.start_node(SyntaxKind::DeclaredIdentifier);
518 p.expect(SyntaxKind::Identifier);
519 }
520 if p.peek().as_str() == "when" {
521 p.consume();
522 parse_expression(&mut *p);
523 }
524 p.expect(SyntaxKind::Colon);
525 if !p.expect(SyntaxKind::LBrace) {
526 return false;
527 }
528
529 loop {
530 match p.nth(0).kind() {
531 SyntaxKind::RBrace => {
532 p.consume();
533 return true;
534 }
535 SyntaxKind::Eof => return false,
536 _ => {
537 if p.nth(1).kind() == SyntaxKind::LBrace
538 && matches!(p.peek().as_str(), "in" | "out")
539 {
540 let mut p = p.start_node(SyntaxKind::Transition);
541 p.consume(); // "in" or "out"
542 p.expect(SyntaxKind::LBrace);
543 if !parse_transition_inner(&mut *p) {
544 return false;
545 }
546 continue;
547 };
548 let checkpoint = p.checkpoint();
549 if !parse_qualified_name(&mut *p)
550 || !p.expect(SyntaxKind::Colon)
551 || !parse_binding_expression(&mut *p)
552 {
553 p.test(SyntaxKind::RBrace);
554 return false;
555 }
556 let _ = p.start_node_at(checkpoint, SyntaxKind::StatePropertyChange);
557 }
558 }
559 }
560}
561
562#[cfg_attr(test, parser_test)]
563/// ```test,Transitions
564/// transitions []
565/// transitions [in checked: {animate x { duration: 88ms; }} out checked: {animate x { duration: 88ms; }}]
566/// ```
567fn parse_transitions(p: &mut impl Parser) {
568 debug_assert_eq!(p.peek().as_str(), "transitions");
569 let mut p: Node<'_, impl Parser> = p.start_node(kind:SyntaxKind::Transitions);
570 p.expect(kind:SyntaxKind::Identifier); // "transitions"
571 p.expect(kind:SyntaxKind::LBracket);
572 while p.nth(0).kind() != SyntaxKind::RBracket && parse_transition(&mut *p) {}
573 p.expect(kind:SyntaxKind::RBracket);
574}
575
576#[cfg_attr(test, parser_test)]
577/// ```test,Transition
578/// in pressed : {}
579/// in pressed: { animate x { duration: 88ms; } }
580/// out pressed: { animate x { duration: 88ms; } }
581/// ```
582fn parse_transition(p: &mut impl Parser) -> bool {
583 if !matches!(p.peek().as_str(), "in" | "out") {
584 p.error("Expected 'in' or 'out' to declare a transition");
585 return false;
586 }
587 let mut p: Node<'_, impl Parser> = p.start_node(kind:SyntaxKind::Transition);
588 p.consume(); // "in" or "out"
589 {
590 let mut p: Node<'_, impl Parser> = p.start_node(kind:SyntaxKind::DeclaredIdentifier);
591 p.expect(kind:SyntaxKind::Identifier);
592 }
593 p.expect(kind:SyntaxKind::Colon);
594 if !p.expect(kind:SyntaxKind::LBrace) {
595 return false;
596 }
597 parse_transition_inner(&mut *p)
598}
599
600#[cfg_attr(test, parser_test)]
601/// ```test
602/// }
603/// animate x { duration: 88ms; } animate foo.bar { } }
604/// ```
605fn parse_transition_inner(p: &mut impl Parser) -> bool {
606 loop {
607 match p.nth(0).kind() {
608 SyntaxKind::RBrace => {
609 p.consume();
610 return true;
611 }
612 SyntaxKind::Eof => return false,
613 SyntaxKind::Identifier if p.peek().as_str() == "animate" => {
614 parse_property_animation(&mut *p);
615 }
616 _ => {
617 p.consume();
618 p.error("Expected 'animate'");
619 }
620 }
621 }
622}
623
624#[cfg_attr(test, parser_test)]
625/// ```test,Function
626/// function foo() {}
627/// function bar(xx : int) { yy = xx; }
628/// function bar(xx : int,) -> int { return 42; }
629/// public function aa(x: int, b: {a: int}, c: int) {}
630/// protected pure function fff() {}
631/// ```
632fn parse_function(p: &mut impl Parser) {
633 let mut p = p.start_node(SyntaxKind::Function);
634 if matches!(p.peek().as_str(), "public" | "protected") {
635 p.consume();
636 if p.peek().as_str() == "pure" {
637 p.consume()
638 }
639 } else if p.peek().as_str() == "pure" {
640 p.consume();
641 if matches!(p.peek().as_str(), "public" | "protected") {
642 p.consume()
643 }
644 }
645 if p.peek().as_str() != "function" {
646 p.error("Unexpected identifier");
647 p.consume();
648 while p.peek().kind == SyntaxKind::Identifier && p.peek().as_str() != "function" {
649 p.consume();
650 }
651 }
652 debug_assert_eq!(p.peek().as_str(), "function");
653 p.expect(SyntaxKind::Identifier); // "function"
654 {
655 let mut p = p.start_node(SyntaxKind::DeclaredIdentifier);
656 p.expect(SyntaxKind::Identifier);
657 }
658 if p.expect(SyntaxKind::LParent) {
659 while p.peek().kind() != SyntaxKind::RParent {
660 let mut p_arg = p.start_node(SyntaxKind::ArgumentDeclaration);
661 {
662 let mut p = p_arg.start_node(SyntaxKind::DeclaredIdentifier);
663 p.expect(SyntaxKind::Identifier);
664 }
665 p_arg.expect(SyntaxKind::Colon);
666 parse_type(&mut *p_arg);
667 drop(p_arg);
668 if !p.test(SyntaxKind::Comma) {
669 break;
670 }
671 }
672 p.expect(SyntaxKind::RParent);
673 if p.test(SyntaxKind::Arrow) {
674 let mut p = p.start_node(SyntaxKind::ReturnType);
675 parse_type(&mut *p);
676 }
677 }
678 parse_code_block(&mut *p);
679}
680