Skip to main content

leo_parser_rowan/parser/
statements.rs

1// Copyright (C) 2019-2026 Provable Inc.
2// This file is part of the Leo library.
3
4// The Leo library is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// The Leo library is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
16
17//! Statement parsing for the Leo language.
18//!
19//! This module implements parsing for all Leo statement forms:
20//! - Let and const bindings
21//! - Assignments (including compound assignments)
22//! - Control flow (if, for)
23//! - Return statements
24//! - Assert statements
25//! - Expression statements
26//! - Blocks
27
28use super::{CompletedMarker, EXPR_RECOVERY, Parser, STMT_RECOVERY, TYPE_RECOVERY, expressions::ExprOpts};
29use crate::syntax_kind::{SyntaxKind, SyntaxKind::*};
30
31impl Parser<'_, '_> {
32    /// Recovery tokens for pattern parsing.
33    const PATTERN_RECOVERY: &'static [SyntaxKind] = &[COMMA, R_PAREN, COLON, EQ];
34
35    /// Parse a statement.
36    pub fn parse_stmt(&mut self) -> Option<CompletedMarker> {
37        self.skip_trivia();
38
39        match self.current() {
40            KW_LET => self.parse_let_stmt(),
41            KW_CONST => self.parse_const_stmt(),
42            KW_RETURN => self.parse_return_stmt(),
43            KW_IF => self.parse_if_stmt(),
44            KW_FOR => self.parse_for_stmt(),
45            KW_ASSERT => self.parse_assert_stmt(),
46            KW_ASSERT_EQ => self.parse_assert_eq_stmt(),
47            KW_ASSERT_NEQ => self.parse_assert_neq_stmt(),
48            L_BRACE => self.parse_block(),
49            _ => self.parse_expr_or_assign_stmt(),
50        }
51    }
52
53    /// Parse a let statement: `let x: Type = expr;` or `let (a, b) = expr;`
54    fn parse_let_stmt(&mut self) -> Option<CompletedMarker> {
55        let m = self.start();
56        self.bump_any(); // let
57
58        // Parse pattern (identifier or tuple destructuring)
59        self.parse_pattern();
60
61        // Optional type annotation
62        if self.eat(COLON) && self.parse_type().is_none() {
63            self.error_recover("expected type", TYPE_RECOVERY);
64        }
65
66        // Initializer
67        self.expect(EQ);
68        if self.parse_expr().is_none() {
69            self.error_recover("expected expression", EXPR_RECOVERY);
70        }
71
72        self.expect(SEMICOLON);
73        Some(m.complete(self, LET_STMT))
74    }
75
76    /// Parse a const statement: `const X: Type = expr;`
77    fn parse_const_stmt(&mut self) -> Option<CompletedMarker> {
78        let m = self.start();
79        self.bump_any(); // const
80
81        // Name
82        self.skip_trivia();
83        if self.at(IDENT) {
84            self.bump_any();
85        } else {
86            self.error("expected identifier".to_string());
87        }
88
89        // Type annotation (required for const)
90        self.expect(COLON);
91        if self.parse_type().is_none() {
92            self.error_recover("expected type", TYPE_RECOVERY);
93        }
94
95        // Initializer
96        self.expect(EQ);
97        if self.parse_expr().is_none() {
98            self.error_recover("expected expression", EXPR_RECOVERY);
99        }
100
101        self.expect(SEMICOLON);
102        Some(m.complete(self, CONST_STMT))
103    }
104
105    /// Parse a return statement: `return expr;` or `return;`
106    fn parse_return_stmt(&mut self) -> Option<CompletedMarker> {
107        let m = self.start();
108        self.bump_any(); // return
109
110        // Optional expression - only attempt if we're not at semicolon/EOF
111        if !self.at(SEMICOLON) && !self.at_eof() && self.parse_expr().is_none() {
112            // Tried to parse expression but failed - recover
113            self.error_recover("expected expression or ';'", EXPR_RECOVERY);
114        }
115
116        self.expect(SEMICOLON);
117        Some(m.complete(self, RETURN_STMT))
118    }
119
120    /// Parse an if statement: `if cond { } else if cond { } else { }`
121    fn parse_if_stmt(&mut self) -> Option<CompletedMarker> {
122        let m = self.start();
123        self.bump_any(); // if
124
125        // Parse condition (no struct literals to avoid ambiguity)
126        if self.parse_expr_with_opts(ExprOpts::no_struct()).is_none() {
127            self.error_recover("expected condition", EXPR_RECOVERY);
128        }
129
130        // Then block - recover if missing
131        if self.parse_block().is_none() && !self.at_eof() {
132            self.error_recover("expected block", STMT_RECOVERY);
133        }
134
135        // Optional else clause
136        if self.eat(KW_ELSE) {
137            if self.at(KW_IF) {
138                // else if
139                self.parse_if_stmt();
140            } else {
141                // else block - recover if missing
142                if self.parse_block().is_none() && !self.at_eof() {
143                    self.error_recover("expected block after 'else'", STMT_RECOVERY);
144                }
145            }
146        }
147
148        Some(m.complete(self, IF_STMT))
149    }
150
151    /// Parse a for statement: `for i: Type in lo..hi { }`
152    fn parse_for_stmt(&mut self) -> Option<CompletedMarker> {
153        let m = self.start();
154        self.bump_any(); // for
155
156        // Loop variable
157        self.skip_trivia();
158        if self.at(IDENT) {
159            self.bump_any();
160        } else {
161            self.error("expected loop variable".to_string());
162        }
163
164        // Optional type annotation
165        if self.eat(COLON) && self.parse_type().is_none() {
166            self.error_recover("expected type", TYPE_RECOVERY);
167        }
168
169        // in keyword
170        self.expect(KW_IN);
171
172        // Range: lo..hi
173        // Disallow struct literals so `IDENT {` parses as range + block body.
174        if self.parse_expr_with_opts(ExprOpts::no_struct()).is_none() {
175            self.error_recover("expected range start", EXPR_RECOVERY);
176        }
177        if self.eat(DOT_DOT) && self.parse_expr_with_opts(ExprOpts::no_struct()).is_none() {
178            self.error_recover("expected range end", EXPR_RECOVERY);
179        }
180
181        // Body - recover if missing
182        if self.parse_block().is_none() && !self.at_eof() {
183            self.error_recover("expected block", STMT_RECOVERY);
184        }
185
186        Some(m.complete(self, FOR_STMT))
187    }
188
189    /// Parse an assert statement: `assert(cond);`
190    fn parse_assert_stmt(&mut self) -> Option<CompletedMarker> {
191        let m = self.start();
192        self.bump_any(); // assert
193
194        self.expect(L_PAREN);
195        if self.parse_expr().is_none() {
196            self.error_recover("expected expression", EXPR_RECOVERY);
197        }
198        self.expect(R_PAREN);
199
200        self.expect(SEMICOLON);
201        Some(m.complete(self, ASSERT_STMT))
202    }
203
204    /// Parse an assert_eq statement: `assert_eq(a, b);`
205    fn parse_assert_eq_stmt(&mut self) -> Option<CompletedMarker> {
206        let m = self.start();
207        self.bump_any(); // assert_eq
208
209        self.expect(L_PAREN);
210        if self.parse_expr().is_none() {
211            self.error_recover("expected first expression", EXPR_RECOVERY);
212        }
213        self.expect(COMMA);
214        if self.parse_expr().is_none() {
215            self.error_recover("expected second expression", EXPR_RECOVERY);
216        }
217        self.expect(R_PAREN);
218
219        self.expect(SEMICOLON);
220        Some(m.complete(self, ASSERT_EQ_STMT))
221    }
222
223    /// Parse an assert_neq statement: `assert_neq(a, b);`
224    fn parse_assert_neq_stmt(&mut self) -> Option<CompletedMarker> {
225        let m = self.start();
226        self.bump_any(); // assert_neq
227
228        self.expect(L_PAREN);
229        if self.parse_expr().is_none() {
230            self.error_recover("expected first expression", EXPR_RECOVERY);
231        }
232        self.expect(COMMA);
233        if self.parse_expr().is_none() {
234            self.error_recover("expected second expression", EXPR_RECOVERY);
235        }
236        self.expect(R_PAREN);
237
238        self.expect(SEMICOLON);
239        Some(m.complete(self, ASSERT_NEQ_STMT))
240    }
241
242    /// Parse a block: `{ stmts... }`
243    pub fn parse_block(&mut self) -> Option<CompletedMarker> {
244        let m = self.start();
245
246        if !self.eat(L_BRACE) {
247            self.error("expected {".to_string());
248            m.abandon(self);
249            return None;
250        }
251
252        // Parse statements until }
253        while !self.at(R_BRACE) && !self.at_eof() {
254            if self.parse_stmt().is_none() {
255                // Error recovery: skip to next statement boundary
256                self.error_recover("expected statement", STMT_RECOVERY);
257            }
258        }
259
260        self.expect(R_BRACE);
261        Some(m.complete(self, BLOCK))
262    }
263
264    /// Parse an expression statement or assignment.
265    fn parse_expr_or_assign_stmt(&mut self) -> Option<CompletedMarker> {
266        let m = self.start();
267
268        let expr = self.parse_expr();
269        if expr.is_none() {
270            m.abandon(self);
271            return None;
272        }
273
274        // Check for assignment operators
275        if let Some(assign_kind) = self.current_assign_op() {
276            self.bump_any(); // operator
277            if self.parse_expr().is_none() {
278                self.error_recover("expected expression after assignment operator", EXPR_RECOVERY);
279            }
280            self.expect(SEMICOLON);
281            return Some(m.complete(self, assign_kind));
282        }
283
284        // Expression statement
285        self.expect(SEMICOLON);
286        Some(m.complete(self, EXPR_STMT))
287    }
288
289    /// Get the statement kind for the current assignment operator, if any.
290    fn current_assign_op(&self) -> Option<SyntaxKind> {
291        match self.current() {
292            EQ => Some(ASSIGN_STMT),
293            PLUS_EQ => Some(ASSIGN_STMT),
294            MINUS_EQ => Some(ASSIGN_STMT),
295            STAR_EQ => Some(ASSIGN_STMT),
296            SLASH_EQ => Some(ASSIGN_STMT),
297            PERCENT_EQ => Some(ASSIGN_STMT),
298            STAR2_EQ => Some(ASSIGN_STMT),
299            AMP_EQ => Some(ASSIGN_STMT),
300            PIPE_EQ => Some(ASSIGN_STMT),
301            CARET_EQ => Some(ASSIGN_STMT),
302            SHL_EQ => Some(ASSIGN_STMT),
303            SHR_EQ => Some(ASSIGN_STMT),
304            AMP2_EQ => Some(ASSIGN_STMT),
305            PIPE2_EQ => Some(ASSIGN_STMT),
306            _ => None,
307        }
308    }
309
310    /// Parse a pattern for let bindings.
311    fn parse_pattern(&mut self) {
312        self.skip_trivia();
313
314        match self.current() {
315            // Tuple pattern: (a, b, c)
316            L_PAREN => {
317                let m = self.start();
318                self.bump_any(); // (
319
320                if !self.at(R_PAREN) {
321                    self.parse_pattern();
322                    while self.eat(COMMA) {
323                        if self.at(R_PAREN) {
324                            break;
325                        }
326                        self.parse_pattern();
327                    }
328                }
329
330                self.expect(R_PAREN);
331                m.complete(self, TUPLE_PATTERN);
332            }
333            // Simple identifier pattern
334            IDENT => {
335                let m = self.start();
336                self.bump_any();
337                m.complete(self, IDENT_PATTERN);
338            }
339            // Wildcard pattern
340            UNDERSCORE => {
341                let m = self.start();
342                self.bump_any();
343                m.complete(self, WILDCARD_PATTERN);
344            }
345            _ => {
346                self.error_recover("expected pattern", Self::PATTERN_RECOVERY);
347            }
348        }
349    }
350}
351
352#[cfg(test)]
353mod tests {
354    use super::*;
355    use crate::{lexer::lex, parser::Parse};
356    use expect_test::{Expect, expect};
357
358    fn check_stmt(input: &str, expect: Expect) {
359        let (tokens, _) = lex(input);
360        let mut parser = Parser::new(input, &tokens);
361        let root = parser.start();
362        parser.parse_stmt();
363        parser.skip_trivia();
364        root.complete(&mut parser, ROOT);
365        let parse: Parse = parser.finish();
366        let output = format!("{:#?}", parse.syntax());
367        expect.assert_eq(&output);
368    }
369
370    // =========================================================================
371    // Let Statements
372    // =========================================================================
373
374    #[test]
375    fn parse_stmt_let_simple() {
376        check_stmt("let x = 1;", expect![[r#"
377                ROOT@0..10
378                  LET_STMT@0..10
379                    KW_LET@0..3 "let"
380                    WHITESPACE@3..4 " "
381                    IDENT_PATTERN@4..5
382                      IDENT@4..5 "x"
383                    WHITESPACE@5..6 " "
384                    EQ@6..7 "="
385                    WHITESPACE@7..8 " "
386                    LITERAL@8..9
387                      INTEGER@8..9 "1"
388                    SEMICOLON@9..10 ";"
389            "#]]);
390    }
391
392    #[test]
393    fn parse_stmt_let_typed() {
394        check_stmt("let x: u32 = 42;", expect![[r#"
395                ROOT@0..16
396                  LET_STMT@0..16
397                    KW_LET@0..3 "let"
398                    WHITESPACE@3..4 " "
399                    IDENT_PATTERN@4..5
400                      IDENT@4..5 "x"
401                    COLON@5..6 ":"
402                    WHITESPACE@6..7 " "
403                    TYPE_PATH@7..10
404                      KW_U32@7..10 "u32"
405                    WHITESPACE@10..11 " "
406                    EQ@11..12 "="
407                    WHITESPACE@12..13 " "
408                    LITERAL@13..15
409                      INTEGER@13..15 "42"
410                    SEMICOLON@15..16 ";"
411            "#]]);
412    }
413
414    #[test]
415    fn parse_stmt_let_tuple_destructure() {
416        check_stmt("let (a, b) = tuple;", expect![[r#"
417                ROOT@0..19
418                  LET_STMT@0..19
419                    KW_LET@0..3 "let"
420                    WHITESPACE@3..4 " "
421                    TUPLE_PATTERN@4..10
422                      L_PAREN@4..5 "("
423                      IDENT_PATTERN@5..6
424                        IDENT@5..6 "a"
425                      COMMA@6..7 ","
426                      WHITESPACE@7..8 " "
427                      IDENT_PATTERN@8..9
428                        IDENT@8..9 "b"
429                      R_PAREN@9..10 ")"
430                    WHITESPACE@10..11 " "
431                    EQ@11..12 "="
432                    WHITESPACE@12..13 " "
433                    PATH_EXPR@13..18
434                      IDENT@13..18 "tuple"
435                    SEMICOLON@18..19 ";"
436            "#]]);
437    }
438
439    // =========================================================================
440    // Const Statements
441    // =========================================================================
442
443    #[test]
444    fn parse_stmt_const() {
445        check_stmt("const MAX: u32 = 100;", expect![[r#"
446                ROOT@0..21
447                  CONST_STMT@0..21
448                    KW_CONST@0..5 "const"
449                    WHITESPACE@5..6 " "
450                    IDENT@6..9 "MAX"
451                    COLON@9..10 ":"
452                    WHITESPACE@10..11 " "
453                    TYPE_PATH@11..14
454                      KW_U32@11..14 "u32"
455                    WHITESPACE@14..15 " "
456                    EQ@15..16 "="
457                    WHITESPACE@16..17 " "
458                    LITERAL@17..20
459                      INTEGER@17..20 "100"
460                    SEMICOLON@20..21 ";"
461            "#]]);
462    }
463
464    // =========================================================================
465    // Return Statements
466    // =========================================================================
467
468    #[test]
469    fn parse_stmt_return_value() {
470        check_stmt("return 42;", expect![[r#"
471                ROOT@0..10
472                  RETURN_STMT@0..10
473                    KW_RETURN@0..6 "return"
474                    WHITESPACE@6..7 " "
475                    LITERAL@7..9
476                      INTEGER@7..9 "42"
477                    SEMICOLON@9..10 ";"
478            "#]]);
479    }
480
481    #[test]
482    fn parse_stmt_return_empty() {
483        check_stmt("return;", expect![[r#"
484                ROOT@0..7
485                  RETURN_STMT@0..7
486                    KW_RETURN@0..6 "return"
487                    SEMICOLON@6..7 ";"
488            "#]]);
489    }
490
491    // =========================================================================
492    // Assignment Statements
493    // =========================================================================
494
495    #[test]
496    fn parse_stmt_assign() {
497        check_stmt("x = 1;", expect![[r#"
498                ROOT@0..6
499                  ASSIGN_STMT@0..6
500                    PATH_EXPR@0..2
501                      IDENT@0..1 "x"
502                      WHITESPACE@1..2 " "
503                    EQ@2..3 "="
504                    WHITESPACE@3..4 " "
505                    LITERAL@4..5
506                      INTEGER@4..5 "1"
507                    SEMICOLON@5..6 ";"
508            "#]]);
509    }
510
511    #[test]
512    fn parse_stmt_assign_add() {
513        check_stmt("x += 1;", expect![[r#"
514                ROOT@0..7
515                  ASSIGN_STMT@0..7
516                    PATH_EXPR@0..2
517                      IDENT@0..1 "x"
518                      WHITESPACE@1..2 " "
519                    PLUS_EQ@2..4 "+="
520                    WHITESPACE@4..5 " "
521                    LITERAL@5..6
522                      INTEGER@5..6 "1"
523                    SEMICOLON@6..7 ";"
524            "#]]);
525    }
526
527    // =========================================================================
528    // If Statements
529    // =========================================================================
530
531    #[test]
532    fn parse_stmt_if_simple() {
533        check_stmt("if cond { }", expect![[r#"
534                ROOT@0..11
535                  IF_STMT@0..11
536                    KW_IF@0..2 "if"
537                    WHITESPACE@2..3 " "
538                    PATH_EXPR@3..8
539                      IDENT@3..7 "cond"
540                      WHITESPACE@7..8 " "
541                    BLOCK@8..11
542                      L_BRACE@8..9 "{"
543                      WHITESPACE@9..10 " "
544                      R_BRACE@10..11 "}"
545            "#]]);
546    }
547
548    #[test]
549    fn parse_stmt_if_else() {
550        check_stmt("if a { } else { }", expect![[r#"
551                ROOT@0..17
552                  IF_STMT@0..17
553                    KW_IF@0..2 "if"
554                    WHITESPACE@2..3 " "
555                    PATH_EXPR@3..5
556                      IDENT@3..4 "a"
557                      WHITESPACE@4..5 " "
558                    BLOCK@5..8
559                      L_BRACE@5..6 "{"
560                      WHITESPACE@6..7 " "
561                      R_BRACE@7..8 "}"
562                    WHITESPACE@8..9 " "
563                    KW_ELSE@9..13 "else"
564                    BLOCK@13..17
565                      WHITESPACE@13..14 " "
566                      L_BRACE@14..15 "{"
567                      WHITESPACE@15..16 " "
568                      R_BRACE@16..17 "}"
569            "#]]);
570    }
571
572    // =========================================================================
573    // For Statements
574    // =========================================================================
575
576    #[test]
577    fn parse_stmt_for() {
578        check_stmt("for i in 0..10 { }", expect![[r#"
579                ROOT@0..18
580                  FOR_STMT@0..18
581                    KW_FOR@0..3 "for"
582                    WHITESPACE@3..4 " "
583                    IDENT@4..5 "i"
584                    WHITESPACE@5..6 " "
585                    KW_IN@6..8 "in"
586                    WHITESPACE@8..9 " "
587                    LITERAL@9..10
588                      INTEGER@9..10 "0"
589                    DOT_DOT@10..12 ".."
590                    LITERAL@12..14
591                      INTEGER@12..14 "10"
592                    BLOCK@14..18
593                      WHITESPACE@14..15 " "
594                      L_BRACE@15..16 "{"
595                      WHITESPACE@16..17 " "
596                      R_BRACE@17..18 "}"
597            "#]]);
598    }
599
600    // =========================================================================
601    // Assert Statements
602    // =========================================================================
603
604    #[test]
605    fn parse_stmt_assert() {
606        check_stmt("assert(x);", expect![[r#"
607                ROOT@0..10
608                  ASSERT_STMT@0..10
609                    KW_ASSERT@0..6 "assert"
610                    L_PAREN@6..7 "("
611                    PATH_EXPR@7..8
612                      IDENT@7..8 "x"
613                    R_PAREN@8..9 ")"
614                    SEMICOLON@9..10 ";"
615            "#]]);
616    }
617
618    #[test]
619    fn parse_stmt_assert_eq() {
620        check_stmt("assert_eq(a, b);", expect![[r#"
621                ROOT@0..16
622                  ASSERT_EQ_STMT@0..16
623                    KW_ASSERT_EQ@0..9 "assert_eq"
624                    L_PAREN@9..10 "("
625                    PATH_EXPR@10..11
626                      IDENT@10..11 "a"
627                    COMMA@11..12 ","
628                    WHITESPACE@12..13 " "
629                    PATH_EXPR@13..14
630                      IDENT@13..14 "b"
631                    R_PAREN@14..15 ")"
632                    SEMICOLON@15..16 ";"
633            "#]]);
634    }
635
636    // =========================================================================
637    // Block and Expression Statements
638    // =========================================================================
639
640    #[test]
641    fn parse_stmt_block() {
642        check_stmt("{ let x = 1; }", expect![[r#"
643                ROOT@0..14
644                  BLOCK@0..14
645                    L_BRACE@0..1 "{"
646                    WHITESPACE@1..2 " "
647                    LET_STMT@2..12
648                      KW_LET@2..5 "let"
649                      WHITESPACE@5..6 " "
650                      IDENT_PATTERN@6..7
651                        IDENT@6..7 "x"
652                      WHITESPACE@7..8 " "
653                      EQ@8..9 "="
654                      WHITESPACE@9..10 " "
655                      LITERAL@10..11
656                        INTEGER@10..11 "1"
657                      SEMICOLON@11..12 ";"
658                    WHITESPACE@12..13 " "
659                    R_BRACE@13..14 "}"
660            "#]]);
661    }
662
663    #[test]
664    fn parse_stmt_expr() {
665        check_stmt("foo();", expect![[r#"
666                ROOT@0..6
667                  EXPR_STMT@0..6
668                    CALL_EXPR@0..5
669                      PATH_EXPR@0..3
670                        IDENT@0..3 "foo"
671                      L_PAREN@3..4 "("
672                      R_PAREN@4..5 ")"
673                    SEMICOLON@5..6 ";"
674            "#]]);
675    }
676}