Skip to main content

leo_parser_rowan/parser/
expressions.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//! Expression parsing for the Leo language.
18//!
19//! This module implements a Pratt parser (precedence climbing) for Leo expressions.
20//! It handles operator precedence, associativity, and all expression forms.
21
22use super::{CompletedMarker, EXPR_RECOVERY, Parser};
23use crate::syntax_kind::{SyntaxKind, SyntaxKind::*};
24
25// =============================================================================
26// Operator Precedence
27// =============================================================================
28
29/// Binding power for operators (higher = tighter binding).
30/// Returns (left_bp, right_bp) for the operator.
31/// Left-associative: left_bp < right_bp
32/// Right-associative: left_bp > right_bp
33/// Non-associative: left_bp == right_bp (with special handling)
34///
35/// LALRPOP levels go from Expr0 (atoms, tightest) to Expr15 (entry, loosest).
36/// Pratt BP: higher = tighter. So we use (16 - Level) * 2 as base BP.
37fn infix_binding_power(op: SyntaxKind) -> Option<(u8, u8)> {
38    let bp = match op {
39        // Ternary is handled specially at the lowest level (Level 15 -> BP 2)
40        // Level 14: || (lowest precedence among binary ops)
41        PIPE2 => (4, 5),
42        // Level 13: &&
43        AMP2 => (6, 7),
44        // Level 12: == != (non-associative - equal binding powers)
45        EQ2 | BANG_EQ => (8, 8),
46        // Level 11: < <= > >= (non-associative - equal binding powers)
47        LT | LT_EQ | GT | GT_EQ => (10, 10),
48        // Level 10: |
49        PIPE => (12, 13),
50        // Level 9: ^
51        CARET => (14, 15),
52        // Level 8: &
53        AMP => (16, 17),
54        // Level 7: << >>
55        SHL | SHR => (18, 19),
56        // Level 6: + -
57        PLUS | MINUS => (20, 21),
58        // Level 5: * / %
59        STAR | SLASH | PERCENT => (22, 23),
60        // Level 4: ** (right-associative: left_bp > right_bp)
61        STAR2 => (25, 24),
62        // Level 3: as (cast)
63        KW_AS => (26, 27),
64        _ => return None,
65    };
66    Some(bp)
67}
68
69/// Check if an operator is a comparison operator (non-associative).
70fn is_comparison_op(op: SyntaxKind) -> bool {
71    matches!(op, EQ2 | BANG_EQ | LT | LT_EQ | GT | GT_EQ)
72}
73
74/// Returns the operators valid after a comparison (next precedence level down).
75/// These are the lower-precedence operators that can follow a comparison.
76fn expected_after_comparison(bp: u8) -> &'static [&'static str] {
77    match bp {
78        8 => &["'&&'", "'||'", "'?'"],                  // After == != (BP 8)
79        10 => &["'&&'", "'||'", "'=='", "'!='", "'?'"], // After < > <= >= (BP 10)
80        _ => &["an operator"],
81    }
82}
83
84/// Prefix binding power for unary operators.
85fn prefix_binding_power(op: SyntaxKind) -> Option<u8> {
86    match op {
87        // Level 2: ! - (unary)
88        BANG | MINUS => Some(30),
89        _ => None,
90    }
91}
92
93/// Postfix binding power for postfix operators.
94fn postfix_binding_power(op: SyntaxKind) -> Option<u8> {
95    match op {
96        // Level 1: . [] () (postfix) - highest precedence
97        DOT | L_BRACKET | L_PAREN => Some(32),
98        _ => None,
99    }
100}
101
102// =============================================================================
103// Expression Options
104// =============================================================================
105
106/// Options for expression parsing to handle context-sensitive cases.
107#[derive(Default, Clone, Copy)]
108pub struct ExprOpts {
109    /// Disallow struct literals `Foo { ... }` in this context.
110    /// Used in conditional expressions to avoid ambiguity.
111    pub no_struct: bool,
112}
113
114impl ExprOpts {
115    /// Create options that disallow struct literals.
116    pub fn no_struct() -> Self {
117        Self { no_struct: true }
118    }
119}
120
121// =============================================================================
122// Expression Parsing
123// =============================================================================
124
125impl Parser<'_, '_> {
126    /// Tokens that may follow a complete expression (binary/postfix operators).
127    pub const EXPR_CONTINUATION: &'static [SyntaxKind] = &[
128        AMP2,
129        PIPE2,
130        AMP,
131        PIPE,
132        CARET,
133        EQ2,
134        BANG_EQ,
135        LT,
136        LT_EQ,
137        GT,
138        GT_EQ,
139        PLUS,
140        MINUS,
141        STAR,
142        SLASH,
143        STAR2,
144        PERCENT,
145        SHL,
146        SHR,
147        L_PAREN,
148        L_BRACKET,
149        L_BRACE,
150        DOT,
151        COLON_COLON,
152        QUESTION,
153        KW_AS,
154    ];
155
156    /// Parse an expression.
157    pub fn parse_expr(&mut self) -> Option<CompletedMarker> {
158        self.parse_expr_with_opts(ExprOpts::default())
159    }
160
161    /// Parse an expression with options.
162    pub fn parse_expr_with_opts(&mut self, opts: ExprOpts) -> Option<CompletedMarker> {
163        self.parse_expr_bp(0, opts)
164    }
165
166    /// Parse an expression with minimum binding power.
167    fn parse_expr_bp(&mut self, min_bp: u8, opts: ExprOpts) -> Option<CompletedMarker> {
168        // Parse prefix expression or primary
169        let mut lhs = self.parse_prefix_expr(opts)?;
170
171        loop {
172            // Try postfix operators (highest precedence)
173            if let Some(bp) = self.current_postfix_bp() {
174                if bp < min_bp {
175                    break;
176                }
177                lhs = self.parse_postfix_expr(lhs)?;
178                continue;
179            }
180
181            // Handle ternary operator specially (lowest precedence)
182            if self.at(QUESTION) && min_bp <= 2 {
183                lhs = self.parse_ternary_expr(lhs)?;
184                continue;
185            }
186
187            // Try infix operators
188            let op = self.current();
189            if let Some((l_bp, r_bp)) = infix_binding_power(op) {
190                if l_bp < min_bp {
191                    break;
192                }
193
194                // Check for non-associative operator chaining (e.g., 1 == 2 == 3)
195                // With equal binding powers, l_bp == r_bp for comparison operators.
196                // If min_bp equals l_bp and this is a comparison, it means we're
197                // trying to chain comparisons, which is not allowed.
198                if l_bp == r_bp && l_bp == min_bp && is_comparison_op(op) {
199                    let expected_tokens = expected_after_comparison(l_bp);
200                    self.error_unexpected(op, expected_tokens);
201                    break;
202                }
203
204                lhs = self.parse_infix_expr(lhs, op, r_bp, opts)?;
205                continue;
206            }
207
208            break;
209        }
210
211        Some(lhs)
212    }
213
214    /// Get the postfix binding power of the current token.
215    fn current_postfix_bp(&self) -> Option<u8> {
216        postfix_binding_power(self.current())
217    }
218
219    /// Parse a prefix expression (unary operators or primary).
220    fn parse_prefix_expr(&mut self, opts: ExprOpts) -> Option<CompletedMarker> {
221        self.skip_trivia();
222
223        // Check for prefix operators
224        if let Some(bp) = prefix_binding_power(self.current()) {
225            let m = self.start();
226            self.bump_any(); // operator
227
228            // Parse operand with prefix binding power
229            // If the operand fails, we still complete the unary expression
230            if self.parse_expr_bp(bp, opts).is_none() {
231                self.error("expected expression after unary operator");
232            }
233
234            return Some(m.complete(self, UNARY_EXPR));
235        }
236
237        // Parse primary expression
238        self.parse_primary_expr(opts)
239    }
240
241    /// Parse a postfix expression (member access, indexing, calls).
242    fn parse_postfix_expr(&mut self, lhs: CompletedMarker) -> Option<CompletedMarker> {
243        match self.current() {
244            DOT => self.parse_member_access(lhs),
245            L_BRACKET => self.parse_index_expr(lhs),
246            L_PAREN => self.parse_call_expr(lhs),
247            _ => Some(lhs),
248        }
249    }
250
251    /// Parse an infix (binary) expression.
252    fn parse_infix_expr(
253        &mut self,
254        lhs: CompletedMarker,
255        op: SyntaxKind,
256        r_bp: u8,
257        opts: ExprOpts,
258    ) -> Option<CompletedMarker> {
259        let m = lhs.precede(self);
260        self.bump_any(); // operator
261
262        // Handle cast specially - only primitive types are allowed after 'as'.
263        if op == KW_AS {
264            if self.parse_cast_type().is_none() {
265                let expected: Vec<&str> = Self::PRIMITIVE_TYPE_KINDS.iter().map(|k| k.user_friendly_name()).collect();
266                self.error_unexpected(self.current(), &expected);
267            }
268            return Some(m.complete(self, CAST_EXPR));
269        }
270
271        // Parse right-hand side
272        // If RHS fails, we still complete the binary expression with an error
273        if self.parse_expr_bp(r_bp, opts).is_none() {
274            self.error("expected expression after operator");
275        }
276
277        Some(m.complete(self, BINARY_EXPR))
278    }
279
280    /// Parse a ternary expression: `condition ? then : else`.
281    fn parse_ternary_expr(&mut self, condition: CompletedMarker) -> Option<CompletedMarker> {
282        let m = condition.precede(self);
283        self.bump_any(); // ?
284
285        // Parse then branch
286        if self.parse_expr().is_none() {
287            self.error("expected expression after '?'");
288        }
289
290        self.expect(COLON);
291
292        // Parse else branch (right-associative)
293        if self.parse_expr_bp(2, ExprOpts::default()).is_none() {
294            self.error("expected expression after ':'");
295        }
296
297        Some(m.complete(self, TERNARY_EXPR))
298    }
299
300    /// Parse member access: `expr.field`, `expr.0` (tuple index), or `expr.method(args)`.
301    fn parse_member_access(&mut self, lhs: CompletedMarker) -> Option<CompletedMarker> {
302        let m = lhs.precede(self);
303        self.bump_any(); // .
304
305        self.skip_trivia();
306
307        // Parse field name or tuple index.
308        // Keywords are valid as field names (e.g. `.field`, `.owner`).
309        if self.at(INTEGER) {
310            self.bump_any();
311            return Some(m.complete(self, TUPLE_ACCESS_EXPR));
312        }
313
314        if self.at(IDENT) || self.current().is_keyword() {
315            self.bump_any();
316        } else {
317            self.error("expected field name or tuple index");
318            return Some(m.complete(self, FIELD_EXPR));
319        }
320
321        // If followed by `(`, this is a method call — parse args inline.
322        if self.at(L_PAREN) {
323            self.bump_any(); // (
324            if !self.at(R_PAREN) {
325                if self.parse_expr().is_none() && !self.at(R_PAREN) && !self.at(COMMA) {
326                    self.error_recover("expected argument expression", EXPR_RECOVERY);
327                }
328                while self.eat(COMMA) {
329                    if self.at(R_PAREN) {
330                        break;
331                    }
332                    if self.parse_expr().is_none() && !self.at(R_PAREN) && !self.at(COMMA) {
333                        self.error_recover("expected argument expression", EXPR_RECOVERY);
334                    }
335                }
336            }
337            self.expect(R_PAREN);
338            return Some(m.complete(self, METHOD_CALL_EXPR));
339        }
340
341        Some(m.complete(self, FIELD_EXPR))
342    }
343
344    /// Parse index expression: `expr[index]`.
345    fn parse_index_expr(&mut self, lhs: CompletedMarker) -> Option<CompletedMarker> {
346        let m = lhs.precede(self);
347        self.bump_any(); // [
348
349        if self.parse_expr().is_none() {
350            self.error("expected index expression");
351        }
352
353        self.expect(R_BRACKET);
354
355        Some(m.complete(self, INDEX_EXPR))
356    }
357
358    /// Parse dynamic call expression: `expr@(expr)::func(args)`.
359    fn parse_dynamic_call_expr(&mut self, lhs: CompletedMarker) -> Option<CompletedMarker> {
360        let m = lhs.precede(self);
361
362        self.bump_any(); // @
363        self.expect(L_PAREN);
364        self.parse_expr(); // target expression
365        // Optional network argument
366        if self.eat(COMMA) {
367            self.parse_expr(); // network expression
368        }
369        self.expect(R_PAREN);
370        self.expect(COLON_COLON);
371        if self.at(IDENT) {
372            self.bump_any(); // function name
373        }
374        // parse call arguments
375        self.expect(L_PAREN);
376        if !self.at(R_PAREN) {
377            self.parse_expr();
378            while self.eat(COMMA) {
379                if self.at(R_PAREN) {
380                    break;
381                }
382                self.parse_expr();
383            }
384        }
385        self.expect(R_PAREN);
386        Some(m.complete(self, DYNAMIC_CALL_EXPR))
387    }
388
389    /// Parse call expression: `expr(args)`.
390    fn parse_call_expr(&mut self, lhs: CompletedMarker) -> Option<CompletedMarker> {
391        let m = lhs.precede(self);
392        self.bump_any(); // (
393
394        // Parse arguments
395        if !self.at(R_PAREN) {
396            if self.parse_expr().is_none() && !self.at(R_PAREN) && !self.at(COMMA) {
397                // Skip invalid tokens until we find a recovery point
398                self.error_recover("expected argument expression", EXPR_RECOVERY);
399            }
400            while self.eat(COMMA) {
401                if self.at(R_PAREN) {
402                    break;
403                }
404                if self.parse_expr().is_none() && !self.at(R_PAREN) && !self.at(COMMA) {
405                    self.error_recover("expected argument expression", EXPR_RECOVERY);
406                }
407            }
408        }
409
410        self.expect(R_PAREN);
411
412        Some(m.complete(self, CALL_EXPR))
413    }
414
415    // =========================================================================
416    // Primary Expressions
417    // =========================================================================
418
419    /// Parse a primary expression (atoms and grouped expressions).
420    fn parse_primary_expr(&mut self, opts: ExprOpts) -> Option<CompletedMarker> {
421        self.skip_trivia();
422
423        match self.current() {
424            // Literals
425            INTEGER => self.parse_integer_literal(),
426            STRING => self.parse_string_literal(),
427            ADDRESS_LIT => self.parse_address_literal(),
428            IDENT_LIT => self.parse_identifier_literal(),
429            KW_TRUE | KW_FALSE => self.parse_bool_literal(),
430            KW_NONE => self.parse_none_literal(),
431
432            // Parenthesized or tuple expression
433            L_PAREN => self.parse_paren_or_tuple_expr(),
434
435            // Array expression
436            L_BRACKET => self.parse_array_expr(),
437
438            // Identifier, path, or struct literal
439            IDENT | KW_FINAL_UPPER => self.parse_ident_expr(opts),
440
441            // Self access
442            KW_SELF => self.parse_self_expr(),
443
444            // Block expressions (block, network)
445            KW_BLOCK => self.parse_block_access(),
446            KW_NETWORK => self.parse_network_access(),
447
448            // Async block expression: `final { ... }`
449            KW_FINAL => self.parse_final_block_expr(),
450
451            _ => {
452                self.error_unexpected(self.current(), &[
453                    "an identifier",
454                    "a program id",
455                    "an address literal",
456                    "an integer literal",
457                    "a static string",
458                    "'!'",
459                    "'-'",
460                    "'('",
461                    "'['",
462                    "'true'",
463                    "'false'",
464                    "'final'",
465                    "'block'",
466                    "'network'",
467                    "'self'",
468                ]);
469                None
470            }
471        }
472    }
473
474    /// Parse an integer literal.
475    ///
476    /// Classifies by suffix: `42field` → `LITERAL_FIELD`, `42group` → `LITERAL_GROUP`,
477    /// `42scalar` → `LITERAL_SCALAR`, otherwise `LITERAL_INT`.
478    fn parse_integer_literal(&mut self) -> Option<CompletedMarker> {
479        let m = self.start();
480        let text = self.current_text();
481        let kind = if text.ends_with("field") {
482            LITERAL_FIELD
483        } else if text.ends_with("group") {
484            LITERAL_GROUP
485        } else if text.ends_with("scalar") {
486            LITERAL_SCALAR
487        } else {
488            LITERAL_INT
489        };
490        self.bump_any();
491        Some(m.complete(self, kind))
492    }
493
494    /// Parse a string literal.
495    fn parse_string_literal(&mut self) -> Option<CompletedMarker> {
496        let m = self.start();
497        self.bump_any();
498        Some(m.complete(self, LITERAL_STRING))
499    }
500
501    /// Parse an identifier literal: `'foo'`.
502    fn parse_identifier_literal(&mut self) -> Option<CompletedMarker> {
503        let m = self.start();
504        self.bump_any();
505        Some(m.complete(self, LITERAL_IDENT))
506    }
507
508    /// Parse an address literal.
509    fn parse_address_literal(&mut self) -> Option<CompletedMarker> {
510        let m = self.start();
511        self.bump_any();
512        Some(m.complete(self, LITERAL_ADDRESS))
513    }
514
515    /// Parse a boolean literal (true/false).
516    fn parse_bool_literal(&mut self) -> Option<CompletedMarker> {
517        let m = self.start();
518        self.bump_any();
519        Some(m.complete(self, LITERAL_BOOL))
520    }
521
522    /// Parse the `none` literal.
523    fn parse_none_literal(&mut self) -> Option<CompletedMarker> {
524        let m = self.start();
525        self.bump_any();
526        Some(m.complete(self, LITERAL_NONE))
527    }
528
529    /// Parse a parenthesized expression or tuple.
530    fn parse_paren_or_tuple_expr(&mut self) -> Option<CompletedMarker> {
531        let m = self.start();
532        self.bump_any(); // (
533
534        // Empty tuple: ()
535        if self.eat(R_PAREN) {
536            return Some(m.complete(self, TUPLE_EXPR));
537        }
538
539        // Parse first expression
540        if self.parse_expr().is_none() && !self.at(R_PAREN) && !self.at(COMMA) {
541            self.error_recover("expected expression", EXPR_RECOVERY);
542        }
543
544        // Check if this is a tuple
545        if self.eat(COMMA) {
546            // It's a tuple - parse remaining elements
547            if !self.at(R_PAREN) {
548                if self.parse_expr().is_none() && !self.at(R_PAREN) && !self.at(COMMA) {
549                    self.error_recover("expected tuple element", EXPR_RECOVERY);
550                }
551                while self.eat(COMMA) {
552                    if self.at(R_PAREN) {
553                        break;
554                    }
555                    if self.parse_expr().is_none() && !self.at(R_PAREN) && !self.at(COMMA) {
556                        self.error_recover("expected tuple element", EXPR_RECOVERY);
557                    }
558                }
559            }
560            self.expect(R_PAREN);
561            return Some(m.complete(self, TUPLE_EXPR));
562        }
563
564        // Single expression - parenthesized
565        self.expect(R_PAREN);
566        Some(m.complete(self, PAREN_EXPR))
567    }
568
569    /// Parse an array expression: `[a, b, c]` or `[x; n]`.
570    fn parse_array_expr(&mut self) -> Option<CompletedMarker> {
571        let m = self.start();
572        self.bump_any(); // [
573
574        // Empty array
575        if self.eat(R_BRACKET) {
576            return Some(m.complete(self, ARRAY_EXPR));
577        }
578
579        // Parse first element
580        if self.parse_expr().is_none() && !self.at(R_BRACKET) && !self.at(COMMA) && !self.at(SEMICOLON) {
581            self.error_recover("expected array element", EXPR_RECOVERY);
582        }
583
584        // Check for repeat syntax: [x; n]
585        if self.eat(SEMICOLON) {
586            if self.parse_expr().is_none() && !self.at(R_BRACKET) {
587                self.error("expected repeat count");
588            }
589            self.expect(R_BRACKET);
590            return Some(m.complete(self, REPEAT_EXPR));
591        }
592
593        // List syntax: [a, b, c]
594        while self.eat(COMMA) {
595            if self.at(R_BRACKET) {
596                break;
597            }
598            if self.parse_expr().is_none() && !self.at(R_BRACKET) && !self.at(COMMA) {
599                self.error_recover("expected array element", EXPR_RECOVERY);
600            }
601        }
602
603        self.expect(R_BRACKET);
604        Some(m.complete(self, ARRAY_EXPR))
605    }
606
607    /// Parse an identifier expression, path, or struct literal.
608    fn parse_ident_expr(&mut self, opts: ExprOpts) -> Option<CompletedMarker> {
609        let m = self.start();
610        self.bump_any(); // first identifier
611
612        // Check for locator: name.aleo::path
613        if self.at(DOT) && self.nth(1) == KW_ALEO {
614            self.bump_any(); // .
615            self.bump_any(); // aleo
616
617            let is_locator = if self.eat(COLON_COLON) {
618                // Locator path: name.aleo::Type or name.aleo::module::Type
619                if self.at(IDENT) {
620                    self.bump_any();
621                    // Consume additional path segments: name.aleo::module::submodule::item
622                    while self.at(COLON_COLON) && self.nth(1) == IDENT {
623                        self.bump_any(); // ::
624                        self.bump_any(); // IDENT
625                    }
626                }
627                true
628            } else {
629                false
630            };
631
632            // Optional const generic args after locator: child.aleo::foo::[3]
633            if self.at(COLON_COLON) && self.nth(1) == L_BRACKET {
634                self.bump_any(); // ::
635                self.parse_const_generic_args_bracket();
636            }
637
638            // Check for struct literal: `child.aleo::Foo::[N] { ... }`
639            if !opts.no_struct && self.at(L_BRACE) {
640                self.bump_any(); // {
641                if !self.at(R_BRACE) {
642                    self.parse_struct_field();
643                    while self.eat(COMMA) {
644                        if self.at(R_BRACE) {
645                            break;
646                        }
647                        self.parse_struct_field();
648                    }
649                }
650                self.expect(R_BRACE);
651                let kind = if is_locator { STRUCT_LOCATOR_EXPR } else { STRUCT_EXPR };
652                return Some(m.complete(self, kind));
653            }
654
655            // Check for call
656            if self.at(L_PAREN) {
657                let kind = if is_locator { PATH_LOCATOR_EXPR } else { PROGRAM_REF_EXPR };
658                let cm = m.complete(self, kind);
659                return self.parse_call_expr(cm);
660            }
661
662            // Check for dynamic call: Interface @ ( target [, network] ) :: function ( args )
663            if self.at(AT) {
664                let cm = m.complete(self, TYPE_LOCATOR);
665                return self.parse_dynamic_call_expr(cm);
666            }
667
668            let kind = if is_locator { PATH_LOCATOR_EXPR } else { PROGRAM_REF_EXPR };
669            return Some(m.complete(self, kind));
670        }
671
672        // Check for path or const generics: Foo::Bar or Foo::[N]
673        while self.eat(COLON_COLON) {
674            if self.at(L_BRACKET) {
675                // Const generics with brackets: Foo::[N]
676                self.parse_const_generic_args_bracket();
677                break;
678            } else if self.at(LT) {
679                // This could be const generics or just less-than
680                // Try to parse as const generics
681                self.parse_const_generic_args_angle();
682                break;
683            } else if self.at(IDENT) {
684                self.bump_any();
685            } else {
686                self.error("expected identifier after ::");
687                break;
688            }
689        }
690
691        // Check for struct literal: `Foo { field: value }`
692        if !opts.no_struct && self.at(L_BRACE) {
693            self.bump_any(); // {
694
695            // Parse fields
696            if !self.at(R_BRACE) {
697                self.parse_struct_field();
698                while self.eat(COMMA) {
699                    if self.at(R_BRACE) {
700                        break;
701                    }
702                    self.parse_struct_field();
703                }
704            }
705
706            self.expect(R_BRACE);
707            return Some(m.complete(self, STRUCT_EXPR));
708        }
709
710        // Check for function call
711        if self.at(L_PAREN) {
712            let cm = m.complete(self, PATH_EXPR);
713            return self.parse_call_expr(cm);
714        }
715
716        // Check for dynamic call: Interface @ ( target [, network] ) :: function ( args )
717        if self.at(AT) {
718            let cm = m.complete(self, TYPE_PATH);
719            return self.parse_dynamic_call_expr(cm);
720        }
721
722        Some(m.complete(self, PATH_EXPR))
723    }
724
725    /// Parse a struct field: `name: value` or `name` (shorthand).
726    fn parse_struct_field(&mut self) {
727        let m = self.start();
728        self.skip_trivia();
729
730        if self.at(IDENT) {
731            self.bump_any(); // field name
732
733            if self.eat(COLON) {
734                // Field with value
735                if self.parse_expr().is_none() && !self.at(R_BRACE) && !self.at(COMMA) {
736                    self.error("expected field value");
737                }
738                m.complete(self, STRUCT_FIELD_INIT);
739            } else {
740                // Shorthand: `{ x }` means `{ x: x }`
741                m.complete(self, STRUCT_FIELD_SHORTHAND);
742            }
743        } else {
744            self.error("expected field name");
745            m.complete(self, STRUCT_FIELD_INIT);
746        }
747    }
748
749    /// Parse `self` expression.
750    fn parse_self_expr(&mut self) -> Option<CompletedMarker> {
751        let m = self.start();
752        self.bump_any(); // self
753
754        // `self` can only be followed by `.` for member access, not `::`
755        if self.at(COLON_COLON) {
756            self.error("expected '.' -- found '::'");
757        }
758
759        Some(m.complete(self, SELF_EXPR))
760    }
761
762    /// Parse `block.height` access.
763    fn parse_block_access(&mut self) -> Option<CompletedMarker> {
764        let m = self.start();
765        self.bump_any(); // block
766        Some(m.complete(self, BLOCK_KW_EXPR))
767    }
768
769    /// Parse `network.id` access.
770    fn parse_network_access(&mut self) -> Option<CompletedMarker> {
771        let m = self.start();
772        self.bump_any(); // network
773        Some(m.complete(self, NETWORK_KW_EXPR))
774    }
775
776    /// Parse a final block expression: `final { stmts }`.
777    fn parse_final_block_expr(&mut self) -> Option<CompletedMarker> {
778        let m = self.start();
779        self.bump_any(); // final
780        self.skip_trivia();
781        if self.parse_block().is_none() {
782            self.error("expected block after 'final'");
783        }
784        Some(m.complete(self, FINAL_EXPR))
785    }
786}
787
788#[cfg(test)]
789mod tests {
790    use super::*;
791    use crate::{lexer::lex, parser::Parse};
792    use expect_test::{Expect, expect};
793
794    fn check_expr(input: &str, expect: Expect) {
795        let (tokens, _) = lex(input);
796        let mut parser = Parser::new(input, &tokens);
797        let root = parser.start();
798        parser.parse_expr();
799        parser.skip_trivia();
800        root.complete(&mut parser, ROOT);
801        let parse: Parse = parser.finish(vec![]);
802        let output = format!("{:#?}", parse.syntax());
803        expect.assert_eq(&output);
804    }
805
806    // =========================================================================
807    // Literals
808    // =========================================================================
809
810    #[test]
811    fn parse_expr_integer() {
812        check_expr("42", expect![[r#"
813            ROOT@0..2
814              LITERAL_INT@0..2
815                INTEGER@0..2 "42"
816        "#]]);
817    }
818
819    #[test]
820    fn parse_expr_bool_true() {
821        check_expr("true", expect![[r#"
822            ROOT@0..4
823              LITERAL_BOOL@0..4
824                KW_TRUE@0..4 "true"
825        "#]]);
826    }
827
828    #[test]
829    fn parse_expr_bool_false() {
830        check_expr("false", expect![[r#"
831            ROOT@0..5
832              LITERAL_BOOL@0..5
833                KW_FALSE@0..5 "false"
834        "#]]);
835    }
836
837    #[test]
838    fn parse_expr_none() {
839        check_expr("none", expect![[r#"
840            ROOT@0..4
841              LITERAL_NONE@0..4
842                KW_NONE@0..4 "none"
843        "#]]);
844    }
845
846    #[test]
847    fn parse_expr_identifier_literal() {
848        check_expr("'foo'", expect![[r#"
849            ROOT@0..5
850              LITERAL_IDENT@0..5
851                IDENT_LIT@0..5 "'foo'"
852        "#]]);
853    }
854
855    // =========================================================================
856    // Dynamic Call Expressions
857    // =========================================================================
858
859    #[test]
860    fn parse_expr_dynamic_call_basic() {
861        check_expr("Adder@(target)::sum(x, y)", expect![[r#"
862            ROOT@0..25
863              DYNAMIC_CALL_EXPR@0..25
864                TYPE_PATH@0..5
865                  IDENT@0..5 "Adder"
866                AT@5..6 "@"
867                L_PAREN@6..7 "("
868                PATH_EXPR@7..13
869                  IDENT@7..13 "target"
870                R_PAREN@13..14 ")"
871                COLON_COLON@14..16 "::"
872                IDENT@16..19 "sum"
873                L_PAREN@19..20 "("
874                PATH_EXPR@20..21
875                  IDENT@20..21 "x"
876                COMMA@21..22 ","
877                WHITESPACE@22..23 " "
878                PATH_EXPR@23..24
879                  IDENT@23..24 "y"
880                R_PAREN@24..25 ")"
881        "#]]);
882    }
883
884    #[test]
885    fn parse_expr_dynamic_call_identifier_target() {
886        check_expr("Adder@('foo')::sum(x, y)", expect![[r#"
887            ROOT@0..24
888              DYNAMIC_CALL_EXPR@0..24
889                TYPE_PATH@0..5
890                  IDENT@0..5 "Adder"
891                AT@5..6 "@"
892                L_PAREN@6..7 "("
893                LITERAL_IDENT@7..12
894                  IDENT_LIT@7..12 "'foo'"
895                R_PAREN@12..13 ")"
896                COLON_COLON@13..15 "::"
897                IDENT@15..18 "sum"
898                L_PAREN@18..19 "("
899                PATH_EXPR@19..20
900                  IDENT@19..20 "x"
901                COMMA@20..21 ","
902                WHITESPACE@21..22 " "
903                PATH_EXPR@22..23
904                  IDENT@22..23 "y"
905                R_PAREN@23..24 ")"
906        "#]]);
907    }
908
909    #[test]
910    fn parse_expr_dynamic_call_with_network() {
911        check_expr("Adder@('foo', 'aleo')::sum(x, y)", expect![[r#"
912            ROOT@0..32
913              DYNAMIC_CALL_EXPR@0..32
914                TYPE_PATH@0..5
915                  IDENT@0..5 "Adder"
916                AT@5..6 "@"
917                L_PAREN@6..7 "("
918                LITERAL_IDENT@7..12
919                  IDENT_LIT@7..12 "'foo'"
920                COMMA@12..13 ","
921                WHITESPACE@13..14 " "
922                LITERAL_IDENT@14..20
923                  IDENT_LIT@14..20 "'aleo'"
924                R_PAREN@20..21 ")"
925                COLON_COLON@21..23 "::"
926                IDENT@23..26 "sum"
927                L_PAREN@26..27 "("
928                PATH_EXPR@27..28
929                  IDENT@27..28 "x"
930                COMMA@28..29 ","
931                WHITESPACE@29..30 " "
932                PATH_EXPR@30..31
933                  IDENT@30..31 "y"
934                R_PAREN@31..32 ")"
935        "#]]);
936    }
937
938    #[test]
939    fn parse_expr_dynamic_call_no_args() {
940        check_expr("Adder@(target)::sum()", expect![[r#"
941            ROOT@0..21
942              DYNAMIC_CALL_EXPR@0..21
943                TYPE_PATH@0..5
944                  IDENT@0..5 "Adder"
945                AT@5..6 "@"
946                L_PAREN@6..7 "("
947                PATH_EXPR@7..13
948                  IDENT@7..13 "target"
949                R_PAREN@13..14 ")"
950                COLON_COLON@14..16 "::"
951                IDENT@16..19 "sum"
952                L_PAREN@19..20 "("
953                R_PAREN@20..21 ")"
954        "#]]);
955    }
956
957    // =========================================================================
958    // Identifiers and Paths
959    // =========================================================================
960
961    #[test]
962    fn parse_expr_ident() {
963        check_expr("foo", expect![[r#"
964                ROOT@0..3
965                  PATH_EXPR@0..3
966                    IDENT@0..3 "foo"
967            "#]]);
968    }
969
970    #[test]
971    fn parse_expr_path() {
972        check_expr("Foo::bar", expect![[r#"
973                ROOT@0..8
974                  PATH_EXPR@0..8
975                    IDENT@0..3 "Foo"
976                    COLON_COLON@3..5 "::"
977                    IDENT@5..8 "bar"
978            "#]]);
979    }
980
981    #[test]
982    fn parse_expr_self() {
983        check_expr("self", expect![[r#"
984            ROOT@0..4
985              SELF_EXPR@0..4
986                KW_SELF@0..4 "self"
987        "#]]);
988    }
989
990    #[test]
991    fn parse_expr_self_colon_colon_is_error() {
992        // `self::y` is invalid - self can only be followed by `.` not `::`
993        let (tokens, _) = lex("self::y");
994        let mut parser = Parser::new("self::y", &tokens);
995        let root = parser.start();
996        parser.parse_expr();
997        parser.skip_trivia();
998        root.complete(&mut parser, ROOT);
999        let parse: Parse = parser.finish(vec![]);
1000        assert!(!parse.errors().is_empty(), "expected error for self::");
1001        assert!(
1002            parse.errors().iter().any(|e| e.message.contains("expected '.'")),
1003            "expected error message to mention expected '.', got: {:?}",
1004            parse.errors()
1005        );
1006    }
1007
1008    // =========================================================================
1009    // Arithmetic
1010    // =========================================================================
1011
1012    #[test]
1013    fn parse_expr_add() {
1014        check_expr("1 + 2", expect![[r#"
1015            ROOT@0..5
1016              BINARY_EXPR@0..5
1017                LITERAL_INT@0..1
1018                  INTEGER@0..1 "1"
1019                WHITESPACE@1..2 " "
1020                PLUS@2..3 "+"
1021                WHITESPACE@3..4 " "
1022                LITERAL_INT@4..5
1023                  INTEGER@4..5 "2"
1024        "#]]);
1025    }
1026
1027    #[test]
1028    fn parse_expr_mul() {
1029        check_expr("a * b", expect![[r#"
1030                ROOT@0..5
1031                  BINARY_EXPR@0..5
1032                    PATH_EXPR@0..2
1033                      IDENT@0..1 "a"
1034                      WHITESPACE@1..2 " "
1035                    STAR@2..3 "*"
1036                    WHITESPACE@3..4 " "
1037                    PATH_EXPR@4..5
1038                      IDENT@4..5 "b"
1039            "#]]);
1040    }
1041
1042    #[test]
1043    fn parse_expr_precedence() {
1044        // 1 + 2 * 3 should parse as 1 + (2 * 3)
1045        check_expr("1 + 2 * 3", expect![[r#"
1046            ROOT@0..9
1047              BINARY_EXPR@0..9
1048                LITERAL_INT@0..1
1049                  INTEGER@0..1 "1"
1050                WHITESPACE@1..2 " "
1051                PLUS@2..3 "+"
1052                WHITESPACE@3..4 " "
1053                BINARY_EXPR@4..9
1054                  LITERAL_INT@4..5
1055                    INTEGER@4..5 "2"
1056                  WHITESPACE@5..6 " "
1057                  STAR@6..7 "*"
1058                  WHITESPACE@7..8 " "
1059                  LITERAL_INT@8..9
1060                    INTEGER@8..9 "3"
1061        "#]]);
1062    }
1063
1064    #[test]
1065    fn parse_expr_power_right_assoc() {
1066        // a ** b ** c should parse as a ** (b ** c)
1067        check_expr("a ** b ** c", expect![[r#"
1068                ROOT@0..11
1069                  BINARY_EXPR@0..11
1070                    PATH_EXPR@0..2
1071                      IDENT@0..1 "a"
1072                      WHITESPACE@1..2 " "
1073                    STAR2@2..4 "**"
1074                    WHITESPACE@4..5 " "
1075                    BINARY_EXPR@5..11
1076                      PATH_EXPR@5..7
1077                        IDENT@5..6 "b"
1078                        WHITESPACE@6..7 " "
1079                      STAR2@7..9 "**"
1080                      WHITESPACE@9..10 " "
1081                      PATH_EXPR@10..11
1082                        IDENT@10..11 "c"
1083            "#]]);
1084    }
1085
1086    // =========================================================================
1087    // Unary Operators
1088    // =========================================================================
1089
1090    #[test]
1091    fn parse_expr_unary_neg() {
1092        check_expr("-x", expect![[r#"
1093                ROOT@0..2
1094                  UNARY_EXPR@0..2
1095                    MINUS@0..1 "-"
1096                    PATH_EXPR@1..2
1097                      IDENT@1..2 "x"
1098            "#]]);
1099    }
1100
1101    #[test]
1102    fn parse_expr_unary_not() {
1103        check_expr("!flag", expect![[r#"
1104                ROOT@0..5
1105                  UNARY_EXPR@0..5
1106                    BANG@0..1 "!"
1107                    PATH_EXPR@1..5
1108                      IDENT@1..5 "flag"
1109            "#]]);
1110    }
1111
1112    // =========================================================================
1113    // Comparison and Logical
1114    // =========================================================================
1115
1116    #[test]
1117    fn parse_expr_comparison() {
1118        check_expr("a < b", expect![[r#"
1119                ROOT@0..5
1120                  BINARY_EXPR@0..5
1121                    PATH_EXPR@0..2
1122                      IDENT@0..1 "a"
1123                      WHITESPACE@1..2 " "
1124                    LT@2..3 "<"
1125                    WHITESPACE@3..4 " "
1126                    PATH_EXPR@4..5
1127                      IDENT@4..5 "b"
1128            "#]]);
1129    }
1130
1131    #[test]
1132    fn parse_expr_logical_and() {
1133        check_expr("a && b", expect![[r#"
1134                ROOT@0..6
1135                  BINARY_EXPR@0..6
1136                    PATH_EXPR@0..2
1137                      IDENT@0..1 "a"
1138                      WHITESPACE@1..2 " "
1139                    AMP2@2..4 "&&"
1140                    WHITESPACE@4..5 " "
1141                    PATH_EXPR@5..6
1142                      IDENT@5..6 "b"
1143            "#]]);
1144    }
1145
1146    #[test]
1147    fn parse_expr_logical_or() {
1148        check_expr("a || b", expect![[r#"
1149                ROOT@0..6
1150                  BINARY_EXPR@0..6
1151                    PATH_EXPR@0..2
1152                      IDENT@0..1 "a"
1153                      WHITESPACE@1..2 " "
1154                    PIPE2@2..4 "||"
1155                    WHITESPACE@4..5 " "
1156                    PATH_EXPR@5..6
1157                      IDENT@5..6 "b"
1158            "#]]);
1159    }
1160
1161    // =========================================================================
1162    // Ternary
1163    // =========================================================================
1164
1165    #[test]
1166    fn parse_expr_ternary() {
1167        check_expr("a ? b : c", expect![[r#"
1168                ROOT@0..9
1169                  TERNARY_EXPR@0..9
1170                    PATH_EXPR@0..2
1171                      IDENT@0..1 "a"
1172                      WHITESPACE@1..2 " "
1173                    QUESTION@2..3 "?"
1174                    WHITESPACE@3..4 " "
1175                    PATH_EXPR@4..6
1176                      IDENT@4..5 "b"
1177                      WHITESPACE@5..6 " "
1178                    COLON@6..7 ":"
1179                    WHITESPACE@7..8 " "
1180                    PATH_EXPR@8..9
1181                      IDENT@8..9 "c"
1182            "#]]);
1183    }
1184
1185    // =========================================================================
1186    // Postfix: Member Access, Indexing, Calls
1187    // =========================================================================
1188
1189    #[test]
1190    fn parse_expr_member_access() {
1191        check_expr("foo.bar", expect![[r#"
1192                ROOT@0..7
1193                  FIELD_EXPR@0..7
1194                    PATH_EXPR@0..3
1195                      IDENT@0..3 "foo"
1196                    DOT@3..4 "."
1197                    IDENT@4..7 "bar"
1198            "#]]);
1199    }
1200
1201    #[test]
1202    fn parse_expr_tuple_access() {
1203        check_expr("tuple.0", expect![[r#"
1204                ROOT@0..7
1205                  TUPLE_ACCESS_EXPR@0..7
1206                    PATH_EXPR@0..5
1207                      IDENT@0..5 "tuple"
1208                    DOT@5..6 "."
1209                    INTEGER@6..7 "0"
1210            "#]]);
1211    }
1212
1213    #[test]
1214    fn parse_expr_index() {
1215        check_expr("arr[0]", expect![[r#"
1216            ROOT@0..6
1217              INDEX_EXPR@0..6
1218                PATH_EXPR@0..3
1219                  IDENT@0..3 "arr"
1220                L_BRACKET@3..4 "["
1221                LITERAL_INT@4..5
1222                  INTEGER@4..5 "0"
1223                R_BRACKET@5..6 "]"
1224        "#]]);
1225    }
1226
1227    #[test]
1228    fn parse_expr_call() {
1229        check_expr("foo(a, b)", expect![[r#"
1230                ROOT@0..9
1231                  CALL_EXPR@0..9
1232                    PATH_EXPR@0..3
1233                      IDENT@0..3 "foo"
1234                    L_PAREN@3..4 "("
1235                    PATH_EXPR@4..5
1236                      IDENT@4..5 "a"
1237                    COMMA@5..6 ","
1238                    WHITESPACE@6..7 " "
1239                    PATH_EXPR@7..8
1240                      IDENT@7..8 "b"
1241                    R_PAREN@8..9 ")"
1242            "#]]);
1243    }
1244
1245    #[test]
1246    fn parse_expr_method_call() {
1247        check_expr("x.foo()", expect![[r#"
1248                ROOT@0..7
1249                  METHOD_CALL_EXPR@0..7
1250                    PATH_EXPR@0..1
1251                      IDENT@0..1 "x"
1252                    DOT@1..2 "."
1253                    IDENT@2..5 "foo"
1254                    L_PAREN@5..6 "("
1255                    R_PAREN@6..7 ")"
1256            "#]]);
1257    }
1258
1259    // =========================================================================
1260    // Cast
1261    // =========================================================================
1262
1263    #[test]
1264    fn parse_expr_cast() {
1265        check_expr("x as u64", expect![[r#"
1266            ROOT@0..8
1267              CAST_EXPR@0..8
1268                PATH_EXPR@0..2
1269                  IDENT@0..1 "x"
1270                  WHITESPACE@1..2 " "
1271                KW_AS@2..4 "as"
1272                WHITESPACE@4..5 " "
1273                TYPE_PRIMITIVE@5..8
1274                  KW_U64@5..8 "u64"
1275        "#]]);
1276    }
1277
1278    // =========================================================================
1279    // Parentheses and Tuples
1280    // =========================================================================
1281
1282    #[test]
1283    fn parse_expr_paren() {
1284        check_expr("(a + b)", expect![[r#"
1285                ROOT@0..7
1286                  PAREN_EXPR@0..7
1287                    L_PAREN@0..1 "("
1288                    BINARY_EXPR@1..6
1289                      PATH_EXPR@1..3
1290                        IDENT@1..2 "a"
1291                        WHITESPACE@2..3 " "
1292                      PLUS@3..4 "+"
1293                      WHITESPACE@4..5 " "
1294                      PATH_EXPR@5..6
1295                        IDENT@5..6 "b"
1296                    R_PAREN@6..7 ")"
1297            "#]]);
1298    }
1299
1300    #[test]
1301    fn parse_expr_tuple() {
1302        check_expr("(a, b)", expect![[r#"
1303                ROOT@0..6
1304                  TUPLE_EXPR@0..6
1305                    L_PAREN@0..1 "("
1306                    PATH_EXPR@1..2
1307                      IDENT@1..2 "a"
1308                    COMMA@2..3 ","
1309                    WHITESPACE@3..4 " "
1310                    PATH_EXPR@4..5
1311                      IDENT@4..5 "b"
1312                    R_PAREN@5..6 ")"
1313            "#]]);
1314    }
1315
1316    #[test]
1317    fn parse_expr_unit() {
1318        check_expr("()", expect![[r#"
1319                ROOT@0..2
1320                  TUPLE_EXPR@0..2
1321                    L_PAREN@0..1 "("
1322                    R_PAREN@1..2 ")"
1323            "#]]);
1324    }
1325
1326    // =========================================================================
1327    // Arrays
1328    // =========================================================================
1329
1330    #[test]
1331    fn parse_expr_array() {
1332        check_expr("[1, 2, 3]", expect![[r#"
1333            ROOT@0..9
1334              ARRAY_EXPR@0..9
1335                L_BRACKET@0..1 "["
1336                LITERAL_INT@1..2
1337                  INTEGER@1..2 "1"
1338                COMMA@2..3 ","
1339                WHITESPACE@3..4 " "
1340                LITERAL_INT@4..5
1341                  INTEGER@4..5 "2"
1342                COMMA@5..6 ","
1343                WHITESPACE@6..7 " "
1344                LITERAL_INT@7..8
1345                  INTEGER@7..8 "3"
1346                R_BRACKET@8..9 "]"
1347        "#]]);
1348    }
1349
1350    #[test]
1351    fn parse_expr_array_repeat() {
1352        check_expr("[0; 10]", expect![[r#"
1353            ROOT@0..7
1354              REPEAT_EXPR@0..7
1355                L_BRACKET@0..1 "["
1356                LITERAL_INT@1..2
1357                  INTEGER@1..2 "0"
1358                SEMICOLON@2..3 ";"
1359                WHITESPACE@3..4 " "
1360                LITERAL_INT@4..6
1361                  INTEGER@4..6 "10"
1362                R_BRACKET@6..7 "]"
1363        "#]]);
1364    }
1365
1366    // =========================================================================
1367    // Struct Literals
1368    // =========================================================================
1369
1370    #[test]
1371    fn parse_expr_struct_init() {
1372        check_expr("Point { x: 1, y: 2 }", expect![[r#"
1373            ROOT@0..20
1374              STRUCT_EXPR@0..20
1375                IDENT@0..5 "Point"
1376                WHITESPACE@5..6 " "
1377                L_BRACE@6..7 "{"
1378                STRUCT_FIELD_INIT@7..12
1379                  WHITESPACE@7..8 " "
1380                  IDENT@8..9 "x"
1381                  COLON@9..10 ":"
1382                  WHITESPACE@10..11 " "
1383                  LITERAL_INT@11..12
1384                    INTEGER@11..12 "1"
1385                COMMA@12..13 ","
1386                STRUCT_FIELD_INIT@13..18
1387                  WHITESPACE@13..14 " "
1388                  IDENT@14..15 "y"
1389                  COLON@15..16 ":"
1390                  WHITESPACE@16..17 " "
1391                  LITERAL_INT@17..18
1392                    INTEGER@17..18 "2"
1393                WHITESPACE@18..19 " "
1394                R_BRACE@19..20 "}"
1395        "#]]);
1396    }
1397
1398    #[test]
1399    fn parse_expr_struct_shorthand() {
1400        check_expr("Point { x, y }", expect![[r#"
1401            ROOT@0..14
1402              STRUCT_EXPR@0..14
1403                IDENT@0..5 "Point"
1404                WHITESPACE@5..6 " "
1405                L_BRACE@6..7 "{"
1406                STRUCT_FIELD_SHORTHAND@7..9
1407                  WHITESPACE@7..8 " "
1408                  IDENT@8..9 "x"
1409                COMMA@9..10 ","
1410                STRUCT_FIELD_SHORTHAND@10..13
1411                  WHITESPACE@10..11 " "
1412                  IDENT@11..12 "y"
1413                  WHITESPACE@12..13 " "
1414                R_BRACE@13..14 "}"
1415        "#]]);
1416    }
1417
1418    // =========================================================================
1419    // Complex Expressions
1420    // =========================================================================
1421
1422    // =========================================================================
1423    // Const Generic Arguments (Use Sites) in Expressions
1424    // =========================================================================
1425
1426    fn check_expr_no_errors(input: &str) {
1427        let (tokens, _) = lex(input);
1428        let mut parser = Parser::new(input, &tokens);
1429        let root = parser.start();
1430        parser.parse_expr();
1431        parser.skip_trivia();
1432        root.complete(&mut parser, ROOT);
1433        let parse: Parse = parser.finish(vec![]);
1434        if !parse.errors().is_empty() {
1435            for err in parse.errors() {
1436                eprintln!("error at {:?}: {}", err.range, err.message);
1437            }
1438            eprintln!("tree:\n{:#?}", parse.syntax());
1439            panic!("expression parse had {} error(s)", parse.errors().len());
1440        }
1441    }
1442
1443    #[test]
1444    fn parse_expr_call_const_generic_simple() {
1445        // Function call with const generic integer arg: CONST_ARG_LIST is inside PATH_EXPR.
1446        check_expr("foo::[5]()", expect![[r#"
1447            ROOT@0..10
1448              CALL_EXPR@0..10
1449                PATH_EXPR@0..8
1450                  IDENT@0..3 "foo"
1451                  COLON_COLON@3..5 "::"
1452                  CONST_ARG_LIST@5..8
1453                    L_BRACKET@5..6 "["
1454                    LITERAL_INT@6..7
1455                      INTEGER@6..7 "5"
1456                    R_BRACKET@7..8 "]"
1457                L_PAREN@8..9 "("
1458                R_PAREN@9..10 ")"
1459        "#]]);
1460    }
1461
1462    #[test]
1463    fn parse_expr_call_const_generic_expr() {
1464        // Function call with expression const generic arg
1465        check_expr_no_errors("foo::[N + 1]()");
1466    }
1467
1468    #[test]
1469    fn parse_expr_call_const_generic_multi() {
1470        // Multi-arg const generic call
1471        check_expr_no_errors("bar::[M, K, N]()");
1472    }
1473
1474    #[test]
1475    fn parse_expr_struct_lit_const_generic() {
1476        // Struct literal with const generic arg: CONST_ARG_LIST is inside STRUCT_EXPR.
1477        check_expr("Foo::[8u32] { arr: x }", expect![[r#"
1478            ROOT@0..22
1479              STRUCT_EXPR@0..22
1480                IDENT@0..3 "Foo"
1481                COLON_COLON@3..5 "::"
1482                CONST_ARG_LIST@5..11
1483                  L_BRACKET@5..6 "["
1484                  LITERAL_INT@6..10
1485                    INTEGER@6..10 "8u32"
1486                  R_BRACKET@10..11 "]"
1487                WHITESPACE@11..12 " "
1488                L_BRACE@12..13 "{"
1489                STRUCT_FIELD_INIT@13..21
1490                  WHITESPACE@13..14 " "
1491                  IDENT@14..17 "arr"
1492                  COLON@17..18 ":"
1493                  WHITESPACE@18..19 " "
1494                  PATH_EXPR@19..21
1495                    IDENT@19..20 "x"
1496                    WHITESPACE@20..21 " "
1497                R_BRACE@21..22 "}"
1498        "#]]);
1499    }
1500
1501    #[test]
1502    fn parse_expr_locator_call_const_generic() {
1503        // Locator + const generic call
1504        check_expr_no_errors("child.aleo::foo::[3]()");
1505    }
1506
1507    #[test]
1508    fn parse_expr_assoc_fn_const_generic() {
1509        // Associated function with const generic: Path::method::[N]()
1510        check_expr_no_errors("Foo::bar::[N]()");
1511    }
1512
1513    // =========================================================================
1514    // Complex Expressions
1515    // =========================================================================
1516
1517    #[test]
1518    fn parse_expr_complex() {
1519        check_expr("a.b[c](d) + e", expect![[r#"
1520                ROOT@0..13
1521                  BINARY_EXPR@0..13
1522                    CALL_EXPR@0..9
1523                      INDEX_EXPR@0..6
1524                        FIELD_EXPR@0..3
1525                          PATH_EXPR@0..1
1526                            IDENT@0..1 "a"
1527                          DOT@1..2 "."
1528                          IDENT@2..3 "b"
1529                        L_BRACKET@3..4 "["
1530                        PATH_EXPR@4..5
1531                          IDENT@4..5 "c"
1532                        R_BRACKET@5..6 "]"
1533                      L_PAREN@6..7 "("
1534                      PATH_EXPR@7..8
1535                        IDENT@7..8 "d"
1536                      R_PAREN@8..9 ")"
1537                    WHITESPACE@9..10 " "
1538                    PLUS@10..11 "+"
1539                    WHITESPACE@11..12 " "
1540                    PATH_EXPR@12..13
1541                      IDENT@12..13 "e"
1542            "#]]);
1543    }
1544
1545    // =========================================================================
1546    // Non-Associative Operator Chaining (should produce errors)
1547    // =========================================================================
1548
1549    fn parse_expr_for_test(input: &str) -> Parse {
1550        let (tokens, _) = lex(input);
1551        let mut parser = Parser::new(input, &tokens);
1552        let root = parser.start();
1553        parser.parse_expr();
1554        parser.skip_trivia();
1555        root.complete(&mut parser, ROOT);
1556        parser.finish(vec![])
1557    }
1558
1559    #[test]
1560    fn parse_expr_chained_eq_is_error() {
1561        // Chained == is not allowed: 1 == 2 == 3
1562        let parse = parse_expr_for_test("1 == 2 == 3");
1563        assert!(!parse.errors().is_empty(), "expected error for chained ==, got none");
1564        assert!(
1565            parse.errors().iter().any(|e| e.message.contains("'&&'") || e.message.contains("expected")),
1566            "expected error message about valid operators, got: {:?}",
1567            parse.errors()
1568        );
1569    }
1570
1571    #[test]
1572    fn parse_expr_chained_neq_is_error() {
1573        // Chained != is not allowed: 1 != 2 != 3
1574        let parse = parse_expr_for_test("1 != 2 != 3");
1575        assert!(!parse.errors().is_empty(), "expected error for chained !=, got none");
1576    }
1577
1578    #[test]
1579    fn parse_expr_chained_lt_is_error() {
1580        // Chained < is not allowed: 1 < 2 < 3
1581        let parse = parse_expr_for_test("1 < 2 < 3");
1582        assert!(!parse.errors().is_empty(), "expected error for chained <, got none");
1583    }
1584
1585    #[test]
1586    fn parse_expr_chained_gt_is_error() {
1587        // Chained > is not allowed: 1 > 2 > 3
1588        let parse = parse_expr_for_test("1 > 2 > 3");
1589        assert!(!parse.errors().is_empty(), "expected error for chained >, got none");
1590    }
1591
1592    #[test]
1593    fn parse_expr_comparison_with_logical_is_ok() {
1594        // Comparison followed by logical is allowed: 1 == 2 && 3 == 4
1595        check_expr_no_errors("1 == 2 && 3 == 4");
1596        check_expr_no_errors("1 < 2 || 3 > 4");
1597    }
1598
1599    // =========================================================================
1600    // Associated function calls (type keyword :: function)
1601    // =========================================================================
1602
1603    #[test]
1604    fn parse_expr_group_associated_fn() {
1605        // The lexer produces a single IDENT token for "group::to_x_coordinate"
1606        // via the PathSpecial regex pattern.
1607        check_expr("group::to_x_coordinate(a)", expect![[r#"
1608                ROOT@0..25
1609                  CALL_EXPR@0..25
1610                    PATH_EXPR@0..22
1611                      IDENT@0..22 "group::to_x_coordinate"
1612                    L_PAREN@22..23 "("
1613                    PATH_EXPR@23..24
1614                      IDENT@23..24 "a"
1615                    R_PAREN@24..25 ")"
1616            "#]]);
1617    }
1618
1619    #[test]
1620    fn parse_expr_signature_associated_fn() {
1621        // The lexer produces a single IDENT token for "signature::verify"
1622        // via the PathSpecial regex pattern.
1623        check_expr("signature::verify(s, a, v)", expect![[r#"
1624                ROOT@0..26
1625                  CALL_EXPR@0..26
1626                    PATH_EXPR@0..17
1627                      IDENT@0..17 "signature::verify"
1628                    L_PAREN@17..18 "("
1629                    PATH_EXPR@18..19
1630                      IDENT@18..19 "s"
1631                    COMMA@19..20 ","
1632                    WHITESPACE@20..21 " "
1633                    PATH_EXPR@21..22
1634                      IDENT@21..22 "a"
1635                    COMMA@22..23 ","
1636                    WHITESPACE@23..24 " "
1637                    PATH_EXPR@24..25
1638                      IDENT@24..25 "v"
1639                    R_PAREN@25..26 ")"
1640            "#]]);
1641    }
1642
1643    // =========================================================================
1644    // Chained Comparison Errors (1a)
1645    // =========================================================================
1646
1647    #[test]
1648    fn parse_expr_chained_le_is_error() {
1649        let parse = parse_expr_for_test("1 <= 2 <= 3");
1650        assert!(!parse.errors().is_empty(), "expected error for chained <=, got none");
1651    }
1652
1653    #[test]
1654    fn parse_expr_chained_ge_is_error() {
1655        let parse = parse_expr_for_test("1 >= 2 >= 3");
1656        assert!(!parse.errors().is_empty(), "expected error for chained >=, got none");
1657    }
1658
1659    #[test]
1660    fn parse_expr_chained_mixed_cmp_is_error() {
1661        let parse = parse_expr_for_test("1 < 2 > 3");
1662        assert!(!parse.errors().is_empty(), "expected error for mixed chained comparisons, got none");
1663    }
1664
1665    // =========================================================================
1666    // Nested Ternary (1b)
1667    // =========================================================================
1668
1669    #[test]
1670    fn parse_expr_ternary_nested() {
1671        check_expr("a ? b ? c : d : e", expect![[r#"
1672            ROOT@0..17
1673              TERNARY_EXPR@0..17
1674                PATH_EXPR@0..2
1675                  IDENT@0..1 "a"
1676                  WHITESPACE@1..2 " "
1677                QUESTION@2..3 "?"
1678                WHITESPACE@3..4 " "
1679                TERNARY_EXPR@4..14
1680                  PATH_EXPR@4..6
1681                    IDENT@4..5 "b"
1682                    WHITESPACE@5..6 " "
1683                  QUESTION@6..7 "?"
1684                  WHITESPACE@7..8 " "
1685                  PATH_EXPR@8..10
1686                    IDENT@8..9 "c"
1687                    WHITESPACE@9..10 " "
1688                  COLON@10..11 ":"
1689                  WHITESPACE@11..12 " "
1690                  PATH_EXPR@12..14
1691                    IDENT@12..13 "d"
1692                    WHITESPACE@13..14 " "
1693                COLON@14..15 ":"
1694                WHITESPACE@15..16 " "
1695                PATH_EXPR@16..17
1696                  IDENT@16..17 "e"
1697        "#]]);
1698    }
1699
1700    // =========================================================================
1701    // Chained Casts (1c)
1702    // =========================================================================
1703
1704    #[test]
1705    fn parse_expr_cast_chained() {
1706        check_expr("x as u32 as u64", expect![[r#"
1707            ROOT@0..15
1708              CAST_EXPR@0..15
1709                CAST_EXPR@0..8
1710                  PATH_EXPR@0..2
1711                    IDENT@0..1 "x"
1712                    WHITESPACE@1..2 " "
1713                  KW_AS@2..4 "as"
1714                  WHITESPACE@4..5 " "
1715                  TYPE_PRIMITIVE@5..8
1716                    KW_U32@5..8 "u32"
1717                WHITESPACE@8..9 " "
1718                KW_AS@9..11 "as"
1719                WHITESPACE@11..12 " "
1720                TYPE_PRIMITIVE@12..15
1721                  KW_U64@12..15 "u64"
1722        "#]]);
1723    }
1724
1725    // =========================================================================
1726    // Collection Edge Cases (1d)
1727    // =========================================================================
1728
1729    #[test]
1730    fn parse_expr_array_trailing_comma() {
1731        check_expr("[1, 2, 3,]", expect![[r#"
1732            ROOT@0..10
1733              ARRAY_EXPR@0..10
1734                L_BRACKET@0..1 "["
1735                LITERAL_INT@1..2
1736                  INTEGER@1..2 "1"
1737                COMMA@2..3 ","
1738                WHITESPACE@3..4 " "
1739                LITERAL_INT@4..5
1740                  INTEGER@4..5 "2"
1741                COMMA@5..6 ","
1742                WHITESPACE@6..7 " "
1743                LITERAL_INT@7..8
1744                  INTEGER@7..8 "3"
1745                COMMA@8..9 ","
1746                R_BRACKET@9..10 "]"
1747        "#]]);
1748    }
1749
1750    #[test]
1751    fn parse_expr_array_empty() {
1752        check_expr("[]", expect![[r#"
1753            ROOT@0..2
1754              ARRAY_EXPR@0..2
1755                L_BRACKET@0..1 "["
1756                R_BRACKET@1..2 "]"
1757        "#]]);
1758    }
1759
1760    #[test]
1761    fn parse_expr_tuple_single() {
1762        check_expr("(a,)", expect![[r#"
1763            ROOT@0..4
1764              TUPLE_EXPR@0..4
1765                L_PAREN@0..1 "("
1766                PATH_EXPR@1..2
1767                  IDENT@1..2 "a"
1768                COMMA@2..3 ","
1769                R_PAREN@3..4 ")"
1770        "#]]);
1771    }
1772
1773    #[test]
1774    fn parse_expr_tuple_trailing_comma() {
1775        check_expr("(1, 2,)", expect![[r#"
1776            ROOT@0..7
1777              TUPLE_EXPR@0..7
1778                L_PAREN@0..1 "("
1779                LITERAL_INT@1..2
1780                  INTEGER@1..2 "1"
1781                COMMA@2..3 ","
1782                WHITESPACE@3..4 " "
1783                LITERAL_INT@4..5
1784                  INTEGER@4..5 "2"
1785                COMMA@5..6 ","
1786                R_PAREN@6..7 ")"
1787        "#]]);
1788    }
1789
1790    // =========================================================================
1791    // Struct Literal Edge Cases (1e)
1792    // =========================================================================
1793
1794    #[test]
1795    fn parse_expr_struct_empty() {
1796        check_expr("Point { }", expect![[r#"
1797            ROOT@0..9
1798              STRUCT_EXPR@0..9
1799                IDENT@0..5 "Point"
1800                WHITESPACE@5..6 " "
1801                L_BRACE@6..7 "{"
1802                WHITESPACE@7..8 " "
1803                R_BRACE@8..9 "}"
1804        "#]]);
1805    }
1806
1807    #[test]
1808    fn parse_expr_struct_trailing_comma() {
1809        check_expr("Point { x: 1, }", expect![[r#"
1810            ROOT@0..15
1811              STRUCT_EXPR@0..15
1812                IDENT@0..5 "Point"
1813                WHITESPACE@5..6 " "
1814                L_BRACE@6..7 "{"
1815                STRUCT_FIELD_INIT@7..12
1816                  WHITESPACE@7..8 " "
1817                  IDENT@8..9 "x"
1818                  COLON@9..10 ":"
1819                  WHITESPACE@10..11 " "
1820                  LITERAL_INT@11..12
1821                    INTEGER@11..12 "1"
1822                COMMA@12..13 ","
1823                WHITESPACE@13..14 " "
1824                R_BRACE@14..15 "}"
1825        "#]]);
1826    }
1827
1828    #[test]
1829    fn parse_expr_struct_mixed_fields() {
1830        check_expr("Point { x, y: 2 }", expect![[r#"
1831            ROOT@0..17
1832              STRUCT_EXPR@0..17
1833                IDENT@0..5 "Point"
1834                WHITESPACE@5..6 " "
1835                L_BRACE@6..7 "{"
1836                STRUCT_FIELD_SHORTHAND@7..9
1837                  WHITESPACE@7..8 " "
1838                  IDENT@8..9 "x"
1839                COMMA@9..10 ","
1840                STRUCT_FIELD_INIT@10..15
1841                  WHITESPACE@10..11 " "
1842                  IDENT@11..12 "y"
1843                  COLON@12..13 ":"
1844                  WHITESPACE@13..14 " "
1845                  LITERAL_INT@14..15
1846                    INTEGER@14..15 "2"
1847                WHITESPACE@15..16 " "
1848                R_BRACE@16..17 "}"
1849        "#]]);
1850    }
1851
1852    // =========================================================================
1853    // Additional Literals (1f)
1854    // =========================================================================
1855
1856    #[test]
1857    fn parse_expr_string() {
1858        check_expr("\"hello\"", expect![[r#"
1859            ROOT@0..7
1860              LITERAL_STRING@0..7
1861                STRING@0..7 "\"hello\""
1862        "#]]);
1863    }
1864
1865    #[test]
1866    fn parse_expr_address() {
1867        check_expr("aleo1qnr4dkkvkgfqph0vzc3y6z2eu975wnpz2925ntjccd5cfqxtyu8s7pyjh9", expect![[r#"
1868            ROOT@0..63
1869              LITERAL_ADDRESS@0..63
1870                ADDRESS_LIT@0..63 "aleo1qnr4dkkvkgfqph0v ..."
1871        "#]]);
1872    }
1873
1874    // =========================================================================
1875    // Deep Postfix Chains (1g)
1876    // =========================================================================
1877
1878    #[test]
1879    fn parse_expr_deep_postfix() {
1880        check_expr("a[0].b.c(x)[1]", expect![[r#"
1881            ROOT@0..14
1882              INDEX_EXPR@0..14
1883                METHOD_CALL_EXPR@0..11
1884                  FIELD_EXPR@0..6
1885                    INDEX_EXPR@0..4
1886                      PATH_EXPR@0..1
1887                        IDENT@0..1 "a"
1888                      L_BRACKET@1..2 "["
1889                      LITERAL_INT@2..3
1890                        INTEGER@2..3 "0"
1891                      R_BRACKET@3..4 "]"
1892                    DOT@4..5 "."
1893                    IDENT@5..6 "b"
1894                  DOT@6..7 "."
1895                  IDENT@7..8 "c"
1896                  L_PAREN@8..9 "("
1897                  PATH_EXPR@9..10
1898                    IDENT@9..10 "x"
1899                  R_PAREN@10..11 ")"
1900                L_BRACKET@11..12 "["
1901                LITERAL_INT@12..13
1902                  INTEGER@12..13 "1"
1903                R_BRACKET@13..14 "]"
1904        "#]]);
1905    }
1906
1907    // =========================================================================
1908    // Final Expression (1h)
1909    // =========================================================================
1910
1911    #[test]
1912    fn parse_expr_final() {
1913        check_expr("final { foo() }", expect![[r#"
1914            ROOT@0..15
1915              FINAL_EXPR@0..15
1916                KW_FINAL@0..5 "final"
1917                WHITESPACE@5..6 " "
1918                BLOCK@6..15
1919                  L_BRACE@6..7 "{"
1920                  WHITESPACE@7..8 " "
1921                  EXPR_STMT@8..14
1922                    CALL_EXPR@8..13
1923                      PATH_EXPR@8..11
1924                        IDENT@8..11 "foo"
1925                      L_PAREN@11..12 "("
1926                      R_PAREN@12..13 ")"
1927                    WHITESPACE@13..14 " "
1928                  ERROR@14..14
1929                  R_BRACE@14..15 "}"
1930        "#]]);
1931    }
1932
1933    // =========================================================================
1934    // Complex Precedence (1i)
1935    // =========================================================================
1936
1937    #[test]
1938    fn parse_expr_mixed_arithmetic() {
1939        // a + b * c / d - e  =>  (a + ((b * c) / d)) - e
1940        check_expr("a + b * c / d - e", expect![[r#"
1941            ROOT@0..17
1942              BINARY_EXPR@0..17
1943                BINARY_EXPR@0..14
1944                  PATH_EXPR@0..2
1945                    IDENT@0..1 "a"
1946                    WHITESPACE@1..2 " "
1947                  PLUS@2..3 "+"
1948                  WHITESPACE@3..4 " "
1949                  BINARY_EXPR@4..14
1950                    BINARY_EXPR@4..10
1951                      PATH_EXPR@4..6
1952                        IDENT@4..5 "b"
1953                        WHITESPACE@5..6 " "
1954                      STAR@6..7 "*"
1955                      WHITESPACE@7..8 " "
1956                      PATH_EXPR@8..10
1957                        IDENT@8..9 "c"
1958                        WHITESPACE@9..10 " "
1959                    SLASH@10..11 "/"
1960                    WHITESPACE@11..12 " "
1961                    PATH_EXPR@12..14
1962                      IDENT@12..13 "d"
1963                      WHITESPACE@13..14 " "
1964                MINUS@14..15 "-"
1965                WHITESPACE@15..16 " "
1966                PATH_EXPR@16..17
1967                  IDENT@16..17 "e"
1968        "#]]);
1969    }
1970
1971    #[test]
1972    fn parse_expr_bitwise_precedence() {
1973        // a | b & c ^ d  =>  a | ((b & c) ^ d)  ... actually:
1974        // & (BP 16,17) binds tighter than ^ (14,15) tighter than | (12,13)
1975        // so: a | ((b & c) ^ d)
1976        check_expr("a | b & c ^ d", expect![[r#"
1977            ROOT@0..13
1978              BINARY_EXPR@0..13
1979                PATH_EXPR@0..2
1980                  IDENT@0..1 "a"
1981                  WHITESPACE@1..2 " "
1982                PIPE@2..3 "|"
1983                WHITESPACE@3..4 " "
1984                BINARY_EXPR@4..13
1985                  BINARY_EXPR@4..10
1986                    PATH_EXPR@4..6
1987                      IDENT@4..5 "b"
1988                      WHITESPACE@5..6 " "
1989                    AMP@6..7 "&"
1990                    WHITESPACE@7..8 " "
1991                    PATH_EXPR@8..10
1992                      IDENT@8..9 "c"
1993                      WHITESPACE@9..10 " "
1994                  CARET@10..11 "^"
1995                  WHITESPACE@11..12 " "
1996                  PATH_EXPR@12..13
1997                    IDENT@12..13 "d"
1998        "#]]);
1999    }
2000
2001    #[test]
2002    fn parse_expr_shift_chain() {
2003        // << and >> are left-assoc at same precedence
2004        // x << 1 >> 2  =>  (x << 1) >> 2
2005        check_expr("x << 1 >> 2", expect![[r#"
2006            ROOT@0..11
2007              BINARY_EXPR@0..11
2008                BINARY_EXPR@0..6
2009                  PATH_EXPR@0..2
2010                    IDENT@0..1 "x"
2011                    WHITESPACE@1..2 " "
2012                  SHL@2..4 "<<"
2013                  WHITESPACE@4..5 " "
2014                  LITERAL_INT@5..6
2015                    INTEGER@5..6 "1"
2016                WHITESPACE@6..7 " "
2017                SHR@7..9 ">>"
2018                WHITESPACE@9..10 " "
2019                LITERAL_INT@10..11
2020                  INTEGER@10..11 "2"
2021        "#]]);
2022    }
2023}