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 a dynamic interface access expression.
359    ///
360    /// Supports three forms:
361    /// - `expr@(expr)::func(args)` — dynamic function call
362    /// - `expr@(expr)::storage_name.op(args)` — dynamic mapping/vector access
363    /// - `expr@(expr)::storage_name` — dynamic singleton storage read
364    fn parse_dynamic_op_expr(&mut self, lhs: CompletedMarker) -> Option<CompletedMarker> {
365        let m = lhs.precede(self);
366
367        self.bump_any(); // @
368        self.expect(L_PAREN);
369        self.parse_expr(); // target expression
370        // Optional network argument
371        if self.eat(COMMA) {
372            self.parse_expr(); // network expression
373        }
374        self.expect(R_PAREN);
375        self.expect(COLON_COLON);
376        self.expect(IDENT); // function name or storage name
377
378        // Bare read form: no `.op(...)` and no `(...)` arguments.
379        if !self.at(DOT) && !self.at(L_PAREN) {
380            return Some(m.complete(self, DYNAMIC_OP_EXPR));
381        }
382
383        // Check for storage access form: `::storage_name.op(args)`
384        if self.at(DOT) {
385            self.bump_any(); // .
386            if self.at(IDENT) {
387                self.bump_any(); // operation name (get, contains, get_or_use)
388            }
389        }
390        // parse call arguments
391        self.expect(L_PAREN);
392        if !self.at(R_PAREN) {
393            self.parse_expr();
394            while self.eat(COMMA) {
395                if self.at(R_PAREN) {
396                    break;
397                }
398                self.parse_expr();
399            }
400        }
401        self.expect(R_PAREN);
402        Some(m.complete(self, DYNAMIC_OP_EXPR))
403    }
404
405    /// Parse call expression: `expr(args)`.
406    fn parse_call_expr(&mut self, lhs: CompletedMarker) -> Option<CompletedMarker> {
407        let m = lhs.precede(self);
408        self.bump_any(); // (
409
410        // Parse arguments
411        if !self.at(R_PAREN) {
412            if self.parse_expr().is_none() && !self.at(R_PAREN) && !self.at(COMMA) {
413                // Skip invalid tokens until we find a recovery point
414                self.error_recover("expected argument expression", EXPR_RECOVERY);
415            }
416            while self.eat(COMMA) {
417                if self.at(R_PAREN) {
418                    break;
419                }
420                if self.parse_expr().is_none() && !self.at(R_PAREN) && !self.at(COMMA) {
421                    self.error_recover("expected argument expression", EXPR_RECOVERY);
422                }
423            }
424        }
425
426        self.expect(R_PAREN);
427
428        Some(m.complete(self, CALL_EXPR))
429    }
430
431    // =========================================================================
432    // Primary Expressions
433    // =========================================================================
434
435    /// Parse a primary expression (atoms and grouped expressions).
436    fn parse_primary_expr(&mut self, opts: ExprOpts) -> Option<CompletedMarker> {
437        self.skip_trivia();
438
439        match self.current() {
440            // Literals
441            INTEGER => self.parse_integer_literal(),
442            STRING => self.parse_string_literal(),
443            ADDRESS_LIT => self.parse_address_literal(),
444            IDENT_LIT => self.parse_identifier_literal(),
445            KW_TRUE | KW_FALSE => self.parse_bool_literal(),
446            KW_NONE => self.parse_none_literal(),
447
448            // Parenthesized or tuple expression
449            L_PAREN => self.parse_paren_or_tuple_expr(),
450
451            // Array expression
452            L_BRACKET => self.parse_array_expr(),
453
454            // Identifier, path, or struct literal
455            IDENT | KW_FINAL_UPPER => self.parse_ident_expr(opts),
456
457            // `self` access
458            KW_SELF => self.parse_self_expr(),
459
460            // `Self` is reserved for future use
461            KW_SELF_UPPER => self.parse_self_upper_expr(),
462
463            // Block expressions (block, network)
464            KW_BLOCK => self.parse_block_access(),
465            KW_NETWORK => self.parse_network_access(),
466
467            // Async block expression: `final { ... }`
468            KW_FINAL => self.parse_final_block_expr(),
469
470            _ => {
471                self.error_unexpected(self.current(), &[
472                    "an identifier",
473                    "a program id",
474                    "an address literal",
475                    "an integer literal",
476                    "a static string",
477                    "'!'",
478                    "'-'",
479                    "'('",
480                    "'['",
481                    "'true'",
482                    "'false'",
483                    "'final'",
484                    "'block'",
485                    "'network'",
486                    "'self'",
487                ]);
488                None
489            }
490        }
491    }
492
493    /// Parse an integer literal.
494    ///
495    /// Classifies by suffix: `42field` → `LITERAL_FIELD`, `42group` → `LITERAL_GROUP`,
496    /// `42scalar` → `LITERAL_SCALAR`, otherwise `LITERAL_INT`.
497    fn parse_integer_literal(&mut self) -> Option<CompletedMarker> {
498        let m = self.start();
499        let text = self.current_text();
500        let kind = if text.ends_with("field") {
501            LITERAL_FIELD
502        } else if text.ends_with("group") {
503            LITERAL_GROUP
504        } else if text.ends_with("scalar") {
505            LITERAL_SCALAR
506        } else {
507            LITERAL_INT
508        };
509        self.bump_any();
510        Some(m.complete(self, kind))
511    }
512
513    /// Parse a string literal.
514    fn parse_string_literal(&mut self) -> Option<CompletedMarker> {
515        let m = self.start();
516        self.bump_any();
517        Some(m.complete(self, LITERAL_STRING))
518    }
519
520    /// Parse an identifier literal: `'foo'`.
521    fn parse_identifier_literal(&mut self) -> Option<CompletedMarker> {
522        let m = self.start();
523        self.bump_any();
524        Some(m.complete(self, LITERAL_IDENT))
525    }
526
527    /// Parse an address literal.
528    fn parse_address_literal(&mut self) -> Option<CompletedMarker> {
529        let m = self.start();
530        self.bump_any();
531        Some(m.complete(self, LITERAL_ADDRESS))
532    }
533
534    /// Parse a boolean literal (true/false).
535    fn parse_bool_literal(&mut self) -> Option<CompletedMarker> {
536        let m = self.start();
537        self.bump_any();
538        Some(m.complete(self, LITERAL_BOOL))
539    }
540
541    /// Parse the `none` literal.
542    fn parse_none_literal(&mut self) -> Option<CompletedMarker> {
543        let m = self.start();
544        self.bump_any();
545        Some(m.complete(self, LITERAL_NONE))
546    }
547
548    /// Parse a parenthesized expression or tuple.
549    fn parse_paren_or_tuple_expr(&mut self) -> Option<CompletedMarker> {
550        let m = self.start();
551        self.bump_any(); // (
552
553        // Empty tuple: ()
554        if self.eat(R_PAREN) {
555            return Some(m.complete(self, TUPLE_EXPR));
556        }
557
558        // Parse first expression
559        if self.parse_expr().is_none() && !self.at(R_PAREN) && !self.at(COMMA) {
560            self.error_recover("expected expression", EXPR_RECOVERY);
561        }
562
563        // Check if this is a tuple
564        if self.eat(COMMA) {
565            // It's a tuple - parse remaining elements
566            if !self.at(R_PAREN) {
567                if self.parse_expr().is_none() && !self.at(R_PAREN) && !self.at(COMMA) {
568                    self.error_recover("expected tuple element", EXPR_RECOVERY);
569                }
570                while self.eat(COMMA) {
571                    if self.at(R_PAREN) {
572                        break;
573                    }
574                    if self.parse_expr().is_none() && !self.at(R_PAREN) && !self.at(COMMA) {
575                        self.error_recover("expected tuple element", EXPR_RECOVERY);
576                    }
577                }
578            }
579            self.expect(R_PAREN);
580            return Some(m.complete(self, TUPLE_EXPR));
581        }
582
583        // Single expression - parenthesized
584        self.expect(R_PAREN);
585        Some(m.complete(self, PAREN_EXPR))
586    }
587
588    /// Parse an array expression: `[a, b, c]` or `[x; n]`.
589    fn parse_array_expr(&mut self) -> Option<CompletedMarker> {
590        let m = self.start();
591        self.bump_any(); // [
592
593        // Empty array
594        if self.eat(R_BRACKET) {
595            return Some(m.complete(self, ARRAY_EXPR));
596        }
597
598        // Parse first element
599        if self.parse_expr().is_none() && !self.at(R_BRACKET) && !self.at(COMMA) && !self.at(SEMICOLON) {
600            self.error_recover("expected array element", EXPR_RECOVERY);
601        }
602
603        // Check for repeat syntax: [x; n]
604        if self.eat(SEMICOLON) {
605            if self.parse_expr().is_none() && !self.at(R_BRACKET) {
606                self.error("expected repeat count");
607            }
608            self.expect(R_BRACKET);
609            return Some(m.complete(self, REPEAT_EXPR));
610        }
611
612        // List syntax: [a, b, c]
613        while self.eat(COMMA) {
614            if self.at(R_BRACKET) {
615                break;
616            }
617            if self.parse_expr().is_none() && !self.at(R_BRACKET) && !self.at(COMMA) {
618                self.error_recover("expected array element", EXPR_RECOVERY);
619            }
620        }
621
622        self.expect(R_BRACKET);
623        Some(m.complete(self, ARRAY_EXPR))
624    }
625
626    /// Parse an identifier expression, path, or struct literal.
627    fn parse_ident_expr(&mut self, opts: ExprOpts) -> Option<CompletedMarker> {
628        let m = self.start();
629        self.bump_any(); // first identifier
630
631        // Check for locator: name.aleo::path
632        if self.at(DOT) && self.nth(1) == KW_ALEO {
633            self.bump_any(); // .
634            self.bump_any(); // aleo
635
636            let is_locator = if self.eat(COLON_COLON) {
637                // Locator path: name.aleo::TypeKind or name.aleo::module::TypeKind
638                if self.at(IDENT) {
639                    self.bump_any();
640                    // Consume additional path segments: name.aleo::module::submodule::item
641                    while self.at(COLON_COLON) && self.nth(1) == IDENT {
642                        self.bump_any(); // ::
643                        self.bump_any(); // IDENT
644                    }
645                }
646                true
647            } else {
648                false
649            };
650
651            // Optional const generic args after locator: child.aleo::foo::[3]
652            if self.at(COLON_COLON) && self.nth(1) == L_BRACKET {
653                self.bump_any(); // ::
654                self.parse_const_generic_args_bracket();
655            }
656
657            // Check for struct literal: `child.aleo::Foo::[N] { ... }`
658            if !opts.no_struct && self.at(L_BRACE) {
659                self.parse_struct_body();
660                let kind = if is_locator { STRUCT_LOCATOR_EXPR } else { STRUCT_EXPR };
661                return Some(m.complete(self, kind));
662            }
663
664            // Check for call
665            if self.at(L_PAREN) {
666                let kind = if is_locator { PATH_LOCATOR_EXPR } else { PROGRAM_REF_EXPR };
667                let cm = m.complete(self, kind);
668                return self.parse_call_expr(cm);
669            }
670
671            // Check for dynamic call: Interface @ ( target [, network] ) :: function ( args )
672            if self.at(AT) {
673                let cm = m.complete(self, TYPE_LOCATOR);
674                return self.parse_dynamic_op_expr(cm);
675            }
676
677            let kind = if is_locator { PATH_LOCATOR_EXPR } else { PROGRAM_REF_EXPR };
678            return Some(m.complete(self, kind));
679        }
680
681        // Check for path or const generics: Foo::Bar or Foo::[N]
682        while self.eat(COLON_COLON) {
683            if self.at(L_BRACKET) {
684                // Const generics with brackets: Foo::[N]
685                self.parse_const_generic_args_bracket();
686                break;
687            } else if self.at(LT) {
688                // This could be const generics or just less-than
689                // Try to parse as const generics
690                self.parse_const_generic_args_angle();
691                break;
692            } else if self.at(IDENT) {
693                self.bump_any();
694            } else {
695                self.error("expected identifier after ::");
696                break;
697            }
698        }
699
700        // Check for struct literal: `Foo { field: value }`
701        if !opts.no_struct && self.at(L_BRACE) {
702            self.parse_struct_body();
703            return Some(m.complete(self, STRUCT_EXPR));
704        }
705
706        // Check for function call
707        if self.at(L_PAREN) {
708            let cm = m.complete(self, PATH_EXPR);
709            return self.parse_call_expr(cm);
710        }
711
712        // Check for dynamic call: Interface @ ( target [, network] ) :: function ( args )
713        if self.at(AT) {
714            let cm = m.complete(self, TYPE_PATH);
715            return self.parse_dynamic_op_expr(cm);
716        }
717
718        Some(m.complete(self, PATH_EXPR))
719    }
720
721    /// Parse the brace-delimited body of a struct literal: `{ a: 1, b, ..base }`.
722    /// Assumes the current token is `{`.
723    fn parse_struct_body(&mut self) {
724        self.bump_any(); // {
725
726        if !self.at(R_BRACE) {
727            // A struct update base `..expr` may appear on its own: `Foo { ..base }`.
728            if self.at(DOT_DOT) {
729                self.parse_struct_base_update();
730            } else {
731                self.parse_struct_field();
732                while self.eat(COMMA) {
733                    if self.at(R_BRACE) {
734                        break;
735                    }
736                    // A trailing `..expr` ends the field list.
737                    if self.at(DOT_DOT) {
738                        self.parse_struct_base_update();
739                        break;
740                    }
741                    self.parse_struct_field();
742                }
743            }
744
745            // The base update must be last; report and consume anything after it once, so a trailing
746            // field doesn't cascade into misleading errors.
747            if self.at(COMMA) {
748                self.error("the base update `..` must be the last entry in a struct initializer");
749                while self.eat(COMMA) {
750                    if self.at(R_BRACE) {
751                        break;
752                    }
753                    if self.at(DOT_DOT) {
754                        self.parse_struct_base_update();
755                    } else {
756                        self.parse_struct_field();
757                    }
758                }
759            }
760        }
761
762        self.expect(R_BRACE);
763    }
764
765    /// Parse a struct update base: `..expr`.
766    fn parse_struct_base_update(&mut self) {
767        let m = self.start();
768        self.bump_any(); // ..
769        if self.at(R_BRACE) {
770            // `..` with no following base expression, e.g. `Foo { .. }`.
771            self.error("expected base expression after `..`");
772        } else {
773            // `parse_expr` reports its own error if the following tokens are not a valid expression.
774            self.parse_expr();
775        }
776        m.complete(self, STRUCT_BASE_UPDATE);
777    }
778
779    /// Parse a struct field: `name: value` or `name` (shorthand).
780    fn parse_struct_field(&mut self) {
781        let m = self.start();
782        self.skip_trivia();
783
784        if self.at(IDENT) {
785            self.bump_any(); // field name
786
787            if self.eat(COLON) {
788                // Field with value
789                if self.parse_expr().is_none() && !self.at(R_BRACE) && !self.at(COMMA) {
790                    self.error("expected field value");
791                }
792                m.complete(self, STRUCT_FIELD_INIT);
793            } else {
794                // Shorthand: `{ x }` means `{ x: x }`
795                m.complete(self, STRUCT_FIELD_SHORTHAND);
796            }
797        } else {
798            self.error("expected field name");
799            m.complete(self, STRUCT_FIELD_INIT);
800        }
801    }
802
803    /// Parse `self` expression.
804    fn parse_self_expr(&mut self) -> Option<CompletedMarker> {
805        let m = self.start();
806        self.bump_any(); // self
807
808        // `self` can only be followed by `.` for member access, not `::`
809        if self.at(COLON_COLON) {
810            self.error("expected '.' -- found '::'");
811        }
812
813        Some(m.complete(self, SELF_EXPR))
814    }
815
816    /// Parse `block.height` access.
817    fn parse_block_access(&mut self) -> Option<CompletedMarker> {
818        let m = self.start();
819        self.bump_any(); // block
820        Some(m.complete(self, BLOCK_KW_EXPR))
821    }
822
823    /// Parse a use of the reserved `Self` keyword.
824    ///
825    /// `Self` is reserved and always rejected by the AST layer, but users typically write it in
826    /// path position (`Self::foo::bar(...)`). Consume the trailing `::segment` chain and any
827    /// call parens so a single reserved-keyword error is emitted instead of a cascade of "expected
828    /// `;`, found `::`" recovery errors from the outer parser.
829    fn parse_self_upper_expr(&mut self) -> Option<CompletedMarker> {
830        let m = self.start();
831        self.bump_any(); // Self
832
833        while self.eat(COLON_COLON) {
834            if self.at(L_BRACKET) {
835                self.parse_const_generic_args_bracket();
836                break;
837            } else if self.at(LT) {
838                self.parse_const_generic_args_angle();
839                break;
840            } else if self.at(IDENT) {
841                self.bump_any();
842            } else {
843                break;
844            }
845        }
846        if self.at(L_PAREN) {
847            self.bump_any(); // (
848            if !self.at(R_PAREN) {
849                if self.parse_expr().is_none() && !self.at(R_PAREN) && !self.at(COMMA) {
850                    self.error_recover("expected argument expression", EXPR_RECOVERY);
851                }
852                while self.eat(COMMA) {
853                    if self.at(R_PAREN) {
854                        break;
855                    }
856                    if self.parse_expr().is_none() && !self.at(R_PAREN) && !self.at(COMMA) {
857                        self.error_recover("expected argument expression", EXPR_RECOVERY);
858                    }
859                }
860            }
861            self.expect(R_PAREN);
862        }
863
864        Some(m.complete(self, SELF_UPPER_EXPR))
865    }
866
867    /// Parse `network.id` access.
868    fn parse_network_access(&mut self) -> Option<CompletedMarker> {
869        let m = self.start();
870        self.bump_any(); // network
871        Some(m.complete(self, NETWORK_KW_EXPR))
872    }
873
874    /// Parse a final block expression: `final { stmts }`.
875    fn parse_final_block_expr(&mut self) -> Option<CompletedMarker> {
876        let m = self.start();
877        self.bump_any(); // final
878        self.skip_trivia();
879        if self.parse_block().is_none() {
880            self.error("expected block after 'final'");
881        }
882        Some(m.complete(self, FINAL_EXPR))
883    }
884}
885
886#[cfg(test)]
887mod tests {
888    use super::*;
889    use crate::{lexer::lex, parser::Parse};
890    use expect_test::{Expect, expect};
891
892    fn check_expr(input: &str, expect: Expect) {
893        let (tokens, _) = lex(input);
894        let mut parser = Parser::new(input, &tokens);
895        let root = parser.start();
896        parser.parse_expr();
897        parser.skip_trivia();
898        root.complete(&mut parser, ROOT);
899        let parse: Parse = parser.finish(vec![]);
900        let output = format!("{:#?}", parse.syntax());
901        expect.assert_eq(&output);
902    }
903
904    // =========================================================================
905    // Literals
906    // =========================================================================
907
908    #[test]
909    fn parse_expr_integer() {
910        check_expr("42", expect![[r#"
911            ROOT@0..2
912              LITERAL_INT@0..2
913                INTEGER@0..2 "42"
914        "#]]);
915    }
916
917    #[test]
918    fn parse_expr_bool_true() {
919        check_expr("true", expect![[r#"
920            ROOT@0..4
921              LITERAL_BOOL@0..4
922                KW_TRUE@0..4 "true"
923        "#]]);
924    }
925
926    #[test]
927    fn parse_expr_bool_false() {
928        check_expr("false", expect![[r#"
929            ROOT@0..5
930              LITERAL_BOOL@0..5
931                KW_FALSE@0..5 "false"
932        "#]]);
933    }
934
935    #[test]
936    fn parse_expr_none() {
937        check_expr("none", expect![[r#"
938            ROOT@0..4
939              LITERAL_NONE@0..4
940                KW_NONE@0..4 "none"
941        "#]]);
942    }
943
944    #[test]
945    fn parse_expr_identifier_literal() {
946        check_expr("'foo'", expect![[r#"
947            ROOT@0..5
948              LITERAL_IDENT@0..5
949                IDENT_LIT@0..5 "'foo'"
950        "#]]);
951    }
952
953    // =========================================================================
954    // Dynamic Call Expressions
955    // =========================================================================
956
957    #[test]
958    fn parse_expr_dynamic_call_basic() {
959        check_expr("Adder@(target)::sum(x, y)", expect![[r#"
960            ROOT@0..25
961              DYNAMIC_OP_EXPR@0..25
962                TYPE_PATH@0..5
963                  IDENT@0..5 "Adder"
964                AT@5..6 "@"
965                L_PAREN@6..7 "("
966                PATH_EXPR@7..13
967                  IDENT@7..13 "target"
968                R_PAREN@13..14 ")"
969                COLON_COLON@14..16 "::"
970                IDENT@16..19 "sum"
971                L_PAREN@19..20 "("
972                PATH_EXPR@20..21
973                  IDENT@20..21 "x"
974                COMMA@21..22 ","
975                WHITESPACE@22..23 " "
976                PATH_EXPR@23..24
977                  IDENT@23..24 "y"
978                R_PAREN@24..25 ")"
979        "#]]);
980    }
981
982    #[test]
983    fn parse_expr_dynamic_call_identifier_target() {
984        check_expr("Adder@('foo')::sum(x, y)", expect![[r#"
985            ROOT@0..24
986              DYNAMIC_OP_EXPR@0..24
987                TYPE_PATH@0..5
988                  IDENT@0..5 "Adder"
989                AT@5..6 "@"
990                L_PAREN@6..7 "("
991                LITERAL_IDENT@7..12
992                  IDENT_LIT@7..12 "'foo'"
993                R_PAREN@12..13 ")"
994                COLON_COLON@13..15 "::"
995                IDENT@15..18 "sum"
996                L_PAREN@18..19 "("
997                PATH_EXPR@19..20
998                  IDENT@19..20 "x"
999                COMMA@20..21 ","
1000                WHITESPACE@21..22 " "
1001                PATH_EXPR@22..23
1002                  IDENT@22..23 "y"
1003                R_PAREN@23..24 ")"
1004        "#]]);
1005    }
1006
1007    #[test]
1008    fn parse_expr_dynamic_call_with_network() {
1009        check_expr("Adder@('foo', 'aleo')::sum(x, y)", expect![[r#"
1010            ROOT@0..32
1011              DYNAMIC_OP_EXPR@0..32
1012                TYPE_PATH@0..5
1013                  IDENT@0..5 "Adder"
1014                AT@5..6 "@"
1015                L_PAREN@6..7 "("
1016                LITERAL_IDENT@7..12
1017                  IDENT_LIT@7..12 "'foo'"
1018                COMMA@12..13 ","
1019                WHITESPACE@13..14 " "
1020                LITERAL_IDENT@14..20
1021                  IDENT_LIT@14..20 "'aleo'"
1022                R_PAREN@20..21 ")"
1023                COLON_COLON@21..23 "::"
1024                IDENT@23..26 "sum"
1025                L_PAREN@26..27 "("
1026                PATH_EXPR@27..28
1027                  IDENT@27..28 "x"
1028                COMMA@28..29 ","
1029                WHITESPACE@29..30 " "
1030                PATH_EXPR@30..31
1031                  IDENT@30..31 "y"
1032                R_PAREN@31..32 ")"
1033        "#]]);
1034    }
1035
1036    #[test]
1037    fn parse_expr_dynamic_call_no_args() {
1038        check_expr("Adder@(target)::sum()", expect![[r#"
1039            ROOT@0..21
1040              DYNAMIC_OP_EXPR@0..21
1041                TYPE_PATH@0..5
1042                  IDENT@0..5 "Adder"
1043                AT@5..6 "@"
1044                L_PAREN@6..7 "("
1045                PATH_EXPR@7..13
1046                  IDENT@7..13 "target"
1047                R_PAREN@13..14 ")"
1048                COLON_COLON@14..16 "::"
1049                IDENT@16..19 "sum"
1050                L_PAREN@19..20 "("
1051                R_PAREN@20..21 ")"
1052        "#]]);
1053    }
1054
1055    #[test]
1056    fn parse_expr_dynamic_storage_access() {
1057        check_expr("Bank@(target)::balances.get(key)", expect![[r#"
1058            ROOT@0..32
1059              DYNAMIC_OP_EXPR@0..32
1060                TYPE_PATH@0..4
1061                  IDENT@0..4 "Bank"
1062                AT@4..5 "@"
1063                L_PAREN@5..6 "("
1064                PATH_EXPR@6..12
1065                  IDENT@6..12 "target"
1066                R_PAREN@12..13 ")"
1067                COLON_COLON@13..15 "::"
1068                IDENT@15..23 "balances"
1069                DOT@23..24 "."
1070                IDENT@24..27 "get"
1071                L_PAREN@27..28 "("
1072                PATH_EXPR@28..31
1073                  IDENT@28..31 "key"
1074                R_PAREN@31..32 ")"
1075        "#]]);
1076    }
1077
1078    #[test]
1079    fn parse_expr_dynamic_storage_access_missing_storage_name() {
1080        // Error-recovery path: `::.get(key)` has no storage-name IDENT before DOT.
1081        // The CST keeps the DOT and the trailing IDENT so downstream passes can detect the
1082        // malformed form and the rowan-to-AST conversion can fall back to `error_identifier`.
1083        check_expr("Bank@(target)::.get(key)", expect![[r#"
1084            ROOT@0..24
1085              DYNAMIC_OP_EXPR@0..24
1086                TYPE_PATH@0..4
1087                  IDENT@0..4 "Bank"
1088                AT@4..5 "@"
1089                L_PAREN@5..6 "("
1090                PATH_EXPR@6..12
1091                  IDENT@6..12 "target"
1092                R_PAREN@12..13 ")"
1093                COLON_COLON@13..15 "::"
1094                DOT@15..16 "."
1095                IDENT@16..19 "get"
1096                L_PAREN@19..20 "("
1097                PATH_EXPR@20..23
1098                  IDENT@20..23 "key"
1099                R_PAREN@23..24 ")"
1100        "#]]);
1101    }
1102
1103    #[test]
1104    fn parse_expr_dynamic_storage_access_missing_op() {
1105        // Error-recovery path: `::balances.(key)` has a DOT but no op IDENT.
1106        check_expr("Bank@(target)::balances.(key)", expect![[r#"
1107            ROOT@0..29
1108              DYNAMIC_OP_EXPR@0..29
1109                TYPE_PATH@0..4
1110                  IDENT@0..4 "Bank"
1111                AT@4..5 "@"
1112                L_PAREN@5..6 "("
1113                PATH_EXPR@6..12
1114                  IDENT@6..12 "target"
1115                R_PAREN@12..13 ")"
1116                COLON_COLON@13..15 "::"
1117                IDENT@15..23 "balances"
1118                DOT@23..24 "."
1119                L_PAREN@24..25 "("
1120                PATH_EXPR@25..28
1121                  IDENT@25..28 "key"
1122                R_PAREN@28..29 ")"
1123        "#]]);
1124    }
1125
1126    #[test]
1127    fn parse_expr_dynamic_read() {
1128        check_expr("Bank@(target)::total", expect![[r#"
1129            ROOT@0..20
1130              DYNAMIC_OP_EXPR@0..20
1131                TYPE_PATH@0..4
1132                  IDENT@0..4 "Bank"
1133                AT@4..5 "@"
1134                L_PAREN@5..6 "("
1135                PATH_EXPR@6..12
1136                  IDENT@6..12 "target"
1137                R_PAREN@12..13 ")"
1138                COLON_COLON@13..15 "::"
1139                IDENT@15..20 "total"
1140        "#]]);
1141    }
1142
1143    #[test]
1144    fn parse_expr_dynamic_read_with_network() {
1145        check_expr("Bank@('foo', 'aleo')::total", expect![[r#"
1146            ROOT@0..27
1147              DYNAMIC_OP_EXPR@0..27
1148                TYPE_PATH@0..4
1149                  IDENT@0..4 "Bank"
1150                AT@4..5 "@"
1151                L_PAREN@5..6 "("
1152                LITERAL_IDENT@6..11
1153                  IDENT_LIT@6..11 "'foo'"
1154                COMMA@11..12 ","
1155                WHITESPACE@12..13 " "
1156                LITERAL_IDENT@13..19
1157                  IDENT_LIT@13..19 "'aleo'"
1158                R_PAREN@19..20 ")"
1159                COLON_COLON@20..22 "::"
1160                IDENT@22..27 "total"
1161        "#]]);
1162    }
1163
1164    // =========================================================================
1165    // Identifiers and Paths
1166    // =========================================================================
1167
1168    #[test]
1169    fn parse_expr_ident() {
1170        check_expr("foo", expect![[r#"
1171                ROOT@0..3
1172                  PATH_EXPR@0..3
1173                    IDENT@0..3 "foo"
1174            "#]]);
1175    }
1176
1177    #[test]
1178    fn parse_expr_path() {
1179        check_expr("Foo::bar", expect![[r#"
1180                ROOT@0..8
1181                  PATH_EXPR@0..8
1182                    IDENT@0..3 "Foo"
1183                    COLON_COLON@3..5 "::"
1184                    IDENT@5..8 "bar"
1185            "#]]);
1186    }
1187
1188    #[test]
1189    fn parse_expr_self() {
1190        check_expr("self", expect![[r#"
1191            ROOT@0..4
1192              SELF_EXPR@0..4
1193                KW_SELF@0..4 "self"
1194        "#]]);
1195    }
1196
1197    #[test]
1198    fn parse_expr_self_colon_colon_is_error() {
1199        // `self::y` is invalid - self can only be followed by `.` not `::`
1200        let (tokens, _) = lex("self::y");
1201        let mut parser = Parser::new("self::y", &tokens);
1202        let root = parser.start();
1203        parser.parse_expr();
1204        parser.skip_trivia();
1205        root.complete(&mut parser, ROOT);
1206        let parse: Parse = parser.finish(vec![]);
1207        assert!(!parse.errors().is_empty(), "expected error for self::");
1208        assert!(
1209            parse.errors().iter().any(|e| e.message.contains("expected '.'")),
1210            "expected error message to mention expected '.', got: {:?}",
1211            parse.errors()
1212        );
1213    }
1214
1215    // =========================================================================
1216    // Arithmetic
1217    // =========================================================================
1218
1219    #[test]
1220    fn parse_expr_add() {
1221        check_expr("1 + 2", expect![[r#"
1222            ROOT@0..5
1223              BINARY_EXPR@0..5
1224                LITERAL_INT@0..1
1225                  INTEGER@0..1 "1"
1226                WHITESPACE@1..2 " "
1227                PLUS@2..3 "+"
1228                WHITESPACE@3..4 " "
1229                LITERAL_INT@4..5
1230                  INTEGER@4..5 "2"
1231        "#]]);
1232    }
1233
1234    #[test]
1235    fn parse_expr_mul() {
1236        check_expr("a * b", expect![[r#"
1237                ROOT@0..5
1238                  BINARY_EXPR@0..5
1239                    PATH_EXPR@0..2
1240                      IDENT@0..1 "a"
1241                      WHITESPACE@1..2 " "
1242                    STAR@2..3 "*"
1243                    WHITESPACE@3..4 " "
1244                    PATH_EXPR@4..5
1245                      IDENT@4..5 "b"
1246            "#]]);
1247    }
1248
1249    #[test]
1250    fn parse_expr_precedence() {
1251        // 1 + 2 * 3 should parse as 1 + (2 * 3)
1252        check_expr("1 + 2 * 3", expect![[r#"
1253            ROOT@0..9
1254              BINARY_EXPR@0..9
1255                LITERAL_INT@0..1
1256                  INTEGER@0..1 "1"
1257                WHITESPACE@1..2 " "
1258                PLUS@2..3 "+"
1259                WHITESPACE@3..4 " "
1260                BINARY_EXPR@4..9
1261                  LITERAL_INT@4..5
1262                    INTEGER@4..5 "2"
1263                  WHITESPACE@5..6 " "
1264                  STAR@6..7 "*"
1265                  WHITESPACE@7..8 " "
1266                  LITERAL_INT@8..9
1267                    INTEGER@8..9 "3"
1268        "#]]);
1269    }
1270
1271    #[test]
1272    fn parse_expr_power_right_assoc() {
1273        // a ** b ** c should parse as a ** (b ** c)
1274        check_expr("a ** b ** c", expect![[r#"
1275                ROOT@0..11
1276                  BINARY_EXPR@0..11
1277                    PATH_EXPR@0..2
1278                      IDENT@0..1 "a"
1279                      WHITESPACE@1..2 " "
1280                    STAR2@2..4 "**"
1281                    WHITESPACE@4..5 " "
1282                    BINARY_EXPR@5..11
1283                      PATH_EXPR@5..7
1284                        IDENT@5..6 "b"
1285                        WHITESPACE@6..7 " "
1286                      STAR2@7..9 "**"
1287                      WHITESPACE@9..10 " "
1288                      PATH_EXPR@10..11
1289                        IDENT@10..11 "c"
1290            "#]]);
1291    }
1292
1293    // =========================================================================
1294    // Unary Operators
1295    // =========================================================================
1296
1297    #[test]
1298    fn parse_expr_unary_neg() {
1299        check_expr("-x", expect![[r#"
1300                ROOT@0..2
1301                  UNARY_EXPR@0..2
1302                    MINUS@0..1 "-"
1303                    PATH_EXPR@1..2
1304                      IDENT@1..2 "x"
1305            "#]]);
1306    }
1307
1308    #[test]
1309    fn parse_expr_unary_not() {
1310        check_expr("!flag", expect![[r#"
1311                ROOT@0..5
1312                  UNARY_EXPR@0..5
1313                    BANG@0..1 "!"
1314                    PATH_EXPR@1..5
1315                      IDENT@1..5 "flag"
1316            "#]]);
1317    }
1318
1319    // =========================================================================
1320    // Comparison and Logical
1321    // =========================================================================
1322
1323    #[test]
1324    fn parse_expr_comparison() {
1325        check_expr("a < b", expect![[r#"
1326                ROOT@0..5
1327                  BINARY_EXPR@0..5
1328                    PATH_EXPR@0..2
1329                      IDENT@0..1 "a"
1330                      WHITESPACE@1..2 " "
1331                    LT@2..3 "<"
1332                    WHITESPACE@3..4 " "
1333                    PATH_EXPR@4..5
1334                      IDENT@4..5 "b"
1335            "#]]);
1336    }
1337
1338    #[test]
1339    fn parse_expr_logical_and() {
1340        check_expr("a && b", expect![[r#"
1341                ROOT@0..6
1342                  BINARY_EXPR@0..6
1343                    PATH_EXPR@0..2
1344                      IDENT@0..1 "a"
1345                      WHITESPACE@1..2 " "
1346                    AMP2@2..4 "&&"
1347                    WHITESPACE@4..5 " "
1348                    PATH_EXPR@5..6
1349                      IDENT@5..6 "b"
1350            "#]]);
1351    }
1352
1353    #[test]
1354    fn parse_expr_logical_or() {
1355        check_expr("a || b", expect![[r#"
1356                ROOT@0..6
1357                  BINARY_EXPR@0..6
1358                    PATH_EXPR@0..2
1359                      IDENT@0..1 "a"
1360                      WHITESPACE@1..2 " "
1361                    PIPE2@2..4 "||"
1362                    WHITESPACE@4..5 " "
1363                    PATH_EXPR@5..6
1364                      IDENT@5..6 "b"
1365            "#]]);
1366    }
1367
1368    // =========================================================================
1369    // Ternary
1370    // =========================================================================
1371
1372    #[test]
1373    fn parse_expr_ternary() {
1374        check_expr("a ? b : c", expect![[r#"
1375                ROOT@0..9
1376                  TERNARY_EXPR@0..9
1377                    PATH_EXPR@0..2
1378                      IDENT@0..1 "a"
1379                      WHITESPACE@1..2 " "
1380                    QUESTION@2..3 "?"
1381                    WHITESPACE@3..4 " "
1382                    PATH_EXPR@4..6
1383                      IDENT@4..5 "b"
1384                      WHITESPACE@5..6 " "
1385                    COLON@6..7 ":"
1386                    WHITESPACE@7..8 " "
1387                    PATH_EXPR@8..9
1388                      IDENT@8..9 "c"
1389            "#]]);
1390    }
1391
1392    // =========================================================================
1393    // Postfix: Member Access, Indexing, Calls
1394    // =========================================================================
1395
1396    #[test]
1397    fn parse_expr_member_access() {
1398        check_expr("foo.bar", expect![[r#"
1399                ROOT@0..7
1400                  FIELD_EXPR@0..7
1401                    PATH_EXPR@0..3
1402                      IDENT@0..3 "foo"
1403                    DOT@3..4 "."
1404                    IDENT@4..7 "bar"
1405            "#]]);
1406    }
1407
1408    #[test]
1409    fn parse_expr_tuple_access() {
1410        check_expr("tuple.0", expect![[r#"
1411                ROOT@0..7
1412                  TUPLE_ACCESS_EXPR@0..7
1413                    PATH_EXPR@0..5
1414                      IDENT@0..5 "tuple"
1415                    DOT@5..6 "."
1416                    INTEGER@6..7 "0"
1417            "#]]);
1418    }
1419
1420    #[test]
1421    fn parse_expr_index() {
1422        check_expr("arr[0]", expect![[r#"
1423            ROOT@0..6
1424              INDEX_EXPR@0..6
1425                PATH_EXPR@0..3
1426                  IDENT@0..3 "arr"
1427                L_BRACKET@3..4 "["
1428                LITERAL_INT@4..5
1429                  INTEGER@4..5 "0"
1430                R_BRACKET@5..6 "]"
1431        "#]]);
1432    }
1433
1434    #[test]
1435    fn parse_expr_call() {
1436        check_expr("foo(a, b)", expect![[r#"
1437                ROOT@0..9
1438                  CALL_EXPR@0..9
1439                    PATH_EXPR@0..3
1440                      IDENT@0..3 "foo"
1441                    L_PAREN@3..4 "("
1442                    PATH_EXPR@4..5
1443                      IDENT@4..5 "a"
1444                    COMMA@5..6 ","
1445                    WHITESPACE@6..7 " "
1446                    PATH_EXPR@7..8
1447                      IDENT@7..8 "b"
1448                    R_PAREN@8..9 ")"
1449            "#]]);
1450    }
1451
1452    #[test]
1453    fn parse_expr_method_call() {
1454        check_expr("x.foo()", expect![[r#"
1455                ROOT@0..7
1456                  METHOD_CALL_EXPR@0..7
1457                    PATH_EXPR@0..1
1458                      IDENT@0..1 "x"
1459                    DOT@1..2 "."
1460                    IDENT@2..5 "foo"
1461                    L_PAREN@5..6 "("
1462                    R_PAREN@6..7 ")"
1463            "#]]);
1464    }
1465
1466    // =========================================================================
1467    // Cast
1468    // =========================================================================
1469
1470    #[test]
1471    fn parse_expr_cast() {
1472        check_expr("x as u64", expect![[r#"
1473            ROOT@0..8
1474              CAST_EXPR@0..8
1475                PATH_EXPR@0..2
1476                  IDENT@0..1 "x"
1477                  WHITESPACE@1..2 " "
1478                KW_AS@2..4 "as"
1479                WHITESPACE@4..5 " "
1480                TYPE_PRIMITIVE@5..8
1481                  KW_U64@5..8 "u64"
1482        "#]]);
1483    }
1484
1485    // =========================================================================
1486    // Parentheses and Tuples
1487    // =========================================================================
1488
1489    #[test]
1490    fn parse_expr_paren() {
1491        check_expr("(a + b)", expect![[r#"
1492                ROOT@0..7
1493                  PAREN_EXPR@0..7
1494                    L_PAREN@0..1 "("
1495                    BINARY_EXPR@1..6
1496                      PATH_EXPR@1..3
1497                        IDENT@1..2 "a"
1498                        WHITESPACE@2..3 " "
1499                      PLUS@3..4 "+"
1500                      WHITESPACE@4..5 " "
1501                      PATH_EXPR@5..6
1502                        IDENT@5..6 "b"
1503                    R_PAREN@6..7 ")"
1504            "#]]);
1505    }
1506
1507    #[test]
1508    fn parse_expr_tuple() {
1509        check_expr("(a, b)", expect![[r#"
1510                ROOT@0..6
1511                  TUPLE_EXPR@0..6
1512                    L_PAREN@0..1 "("
1513                    PATH_EXPR@1..2
1514                      IDENT@1..2 "a"
1515                    COMMA@2..3 ","
1516                    WHITESPACE@3..4 " "
1517                    PATH_EXPR@4..5
1518                      IDENT@4..5 "b"
1519                    R_PAREN@5..6 ")"
1520            "#]]);
1521    }
1522
1523    #[test]
1524    fn parse_expr_unit() {
1525        check_expr("()", expect![[r#"
1526                ROOT@0..2
1527                  TUPLE_EXPR@0..2
1528                    L_PAREN@0..1 "("
1529                    R_PAREN@1..2 ")"
1530            "#]]);
1531    }
1532
1533    // =========================================================================
1534    // Arrays
1535    // =========================================================================
1536
1537    #[test]
1538    fn parse_expr_array() {
1539        check_expr("[1, 2, 3]", expect![[r#"
1540            ROOT@0..9
1541              ARRAY_EXPR@0..9
1542                L_BRACKET@0..1 "["
1543                LITERAL_INT@1..2
1544                  INTEGER@1..2 "1"
1545                COMMA@2..3 ","
1546                WHITESPACE@3..4 " "
1547                LITERAL_INT@4..5
1548                  INTEGER@4..5 "2"
1549                COMMA@5..6 ","
1550                WHITESPACE@6..7 " "
1551                LITERAL_INT@7..8
1552                  INTEGER@7..8 "3"
1553                R_BRACKET@8..9 "]"
1554        "#]]);
1555    }
1556
1557    #[test]
1558    fn parse_expr_array_repeat() {
1559        check_expr("[0; 10]", expect![[r#"
1560            ROOT@0..7
1561              REPEAT_EXPR@0..7
1562                L_BRACKET@0..1 "["
1563                LITERAL_INT@1..2
1564                  INTEGER@1..2 "0"
1565                SEMICOLON@2..3 ";"
1566                WHITESPACE@3..4 " "
1567                LITERAL_INT@4..6
1568                  INTEGER@4..6 "10"
1569                R_BRACKET@6..7 "]"
1570        "#]]);
1571    }
1572
1573    // =========================================================================
1574    // Struct Literals
1575    // =========================================================================
1576
1577    #[test]
1578    fn parse_expr_struct_init() {
1579        check_expr("Point { x: 1, y: 2 }", expect![[r#"
1580            ROOT@0..20
1581              STRUCT_EXPR@0..20
1582                IDENT@0..5 "Point"
1583                WHITESPACE@5..6 " "
1584                L_BRACE@6..7 "{"
1585                STRUCT_FIELD_INIT@7..12
1586                  WHITESPACE@7..8 " "
1587                  IDENT@8..9 "x"
1588                  COLON@9..10 ":"
1589                  WHITESPACE@10..11 " "
1590                  LITERAL_INT@11..12
1591                    INTEGER@11..12 "1"
1592                COMMA@12..13 ","
1593                STRUCT_FIELD_INIT@13..18
1594                  WHITESPACE@13..14 " "
1595                  IDENT@14..15 "y"
1596                  COLON@15..16 ":"
1597                  WHITESPACE@16..17 " "
1598                  LITERAL_INT@17..18
1599                    INTEGER@17..18 "2"
1600                WHITESPACE@18..19 " "
1601                R_BRACE@19..20 "}"
1602        "#]]);
1603    }
1604
1605    #[test]
1606    fn parse_expr_struct_shorthand() {
1607        check_expr("Point { x, y }", expect![[r#"
1608            ROOT@0..14
1609              STRUCT_EXPR@0..14
1610                IDENT@0..5 "Point"
1611                WHITESPACE@5..6 " "
1612                L_BRACE@6..7 "{"
1613                STRUCT_FIELD_SHORTHAND@7..9
1614                  WHITESPACE@7..8 " "
1615                  IDENT@8..9 "x"
1616                COMMA@9..10 ","
1617                STRUCT_FIELD_SHORTHAND@10..13
1618                  WHITESPACE@10..11 " "
1619                  IDENT@11..12 "y"
1620                  WHITESPACE@12..13 " "
1621                R_BRACE@13..14 "}"
1622        "#]]);
1623    }
1624
1625    #[test]
1626    fn parse_expr_struct_update_base() {
1627        check_expr("Point { x: 1, ..other }", expect![[r#"
1628            ROOT@0..23
1629              STRUCT_EXPR@0..23
1630                IDENT@0..5 "Point"
1631                WHITESPACE@5..6 " "
1632                L_BRACE@6..7 "{"
1633                STRUCT_FIELD_INIT@7..12
1634                  WHITESPACE@7..8 " "
1635                  IDENT@8..9 "x"
1636                  COLON@9..10 ":"
1637                  WHITESPACE@10..11 " "
1638                  LITERAL_INT@11..12
1639                    INTEGER@11..12 "1"
1640                COMMA@12..13 ","
1641                STRUCT_BASE_UPDATE@13..22
1642                  WHITESPACE@13..14 " "
1643                  DOT_DOT@14..16 ".."
1644                  PATH_EXPR@16..22
1645                    IDENT@16..21 "other"
1646                    WHITESPACE@21..22 " "
1647                R_BRACE@22..23 "}"
1648        "#]]);
1649    }
1650
1651    #[test]
1652    fn parse_expr_struct_update_base_only() {
1653        check_expr("Point { ..other }", expect![[r#"
1654            ROOT@0..17
1655              STRUCT_EXPR@0..17
1656                IDENT@0..5 "Point"
1657                WHITESPACE@5..6 " "
1658                L_BRACE@6..7 "{"
1659                STRUCT_BASE_UPDATE@7..16
1660                  WHITESPACE@7..8 " "
1661                  DOT_DOT@8..10 ".."
1662                  PATH_EXPR@10..16
1663                    IDENT@10..15 "other"
1664                    WHITESPACE@15..16 " "
1665                R_BRACE@16..17 "}"
1666        "#]]);
1667    }
1668
1669    // =========================================================================
1670    // Complex Expressions
1671    // =========================================================================
1672
1673    // =========================================================================
1674    // Const Generic Arguments (Use Sites) in Expressions
1675    // =========================================================================
1676
1677    fn check_expr_no_errors(input: &str) {
1678        let (tokens, _) = lex(input);
1679        let mut parser = Parser::new(input, &tokens);
1680        let root = parser.start();
1681        parser.parse_expr();
1682        parser.skip_trivia();
1683        root.complete(&mut parser, ROOT);
1684        let parse: Parse = parser.finish(vec![]);
1685        if !parse.errors().is_empty() {
1686            for err in parse.errors() {
1687                eprintln!("error at {:?}: {}", err.range, err.message);
1688            }
1689            eprintln!("tree:\n{:#?}", parse.syntax());
1690            panic!("expression parse had {} error(s)", parse.errors().len());
1691        }
1692    }
1693
1694    #[test]
1695    fn parse_expr_call_const_generic_simple() {
1696        // Function call with const generic integer arg: CONST_ARG_LIST is inside PATH_EXPR.
1697        check_expr("foo::[5]()", expect![[r#"
1698            ROOT@0..10
1699              CALL_EXPR@0..10
1700                PATH_EXPR@0..8
1701                  IDENT@0..3 "foo"
1702                  COLON_COLON@3..5 "::"
1703                  CONST_ARG_LIST@5..8
1704                    L_BRACKET@5..6 "["
1705                    LITERAL_INT@6..7
1706                      INTEGER@6..7 "5"
1707                    R_BRACKET@7..8 "]"
1708                L_PAREN@8..9 "("
1709                R_PAREN@9..10 ")"
1710        "#]]);
1711    }
1712
1713    #[test]
1714    fn parse_expr_call_const_generic_expr() {
1715        // Function call with expression const generic arg
1716        check_expr_no_errors("foo::[N + 1]()");
1717    }
1718
1719    #[test]
1720    fn parse_expr_call_const_generic_multi() {
1721        // Multi-arg const generic call
1722        check_expr_no_errors("bar::[M, K, N]()");
1723    }
1724
1725    #[test]
1726    fn parse_expr_struct_lit_const_generic() {
1727        // Struct literal with const generic arg: CONST_ARG_LIST is inside STRUCT_EXPR.
1728        check_expr("Foo::[8u32] { arr: x }", expect![[r#"
1729            ROOT@0..22
1730              STRUCT_EXPR@0..22
1731                IDENT@0..3 "Foo"
1732                COLON_COLON@3..5 "::"
1733                CONST_ARG_LIST@5..11
1734                  L_BRACKET@5..6 "["
1735                  LITERAL_INT@6..10
1736                    INTEGER@6..10 "8u32"
1737                  R_BRACKET@10..11 "]"
1738                WHITESPACE@11..12 " "
1739                L_BRACE@12..13 "{"
1740                STRUCT_FIELD_INIT@13..21
1741                  WHITESPACE@13..14 " "
1742                  IDENT@14..17 "arr"
1743                  COLON@17..18 ":"
1744                  WHITESPACE@18..19 " "
1745                  PATH_EXPR@19..21
1746                    IDENT@19..20 "x"
1747                    WHITESPACE@20..21 " "
1748                R_BRACE@21..22 "}"
1749        "#]]);
1750    }
1751
1752    #[test]
1753    fn parse_expr_locator_call_const_generic() {
1754        // Locator + const generic call
1755        check_expr_no_errors("child.aleo::foo::[3]()");
1756    }
1757
1758    #[test]
1759    fn parse_expr_assoc_fn_const_generic() {
1760        // Associated function with const generic: Path::method::[N]()
1761        check_expr_no_errors("Foo::bar::[N]()");
1762    }
1763
1764    // =========================================================================
1765    // Complex Expressions
1766    // =========================================================================
1767
1768    #[test]
1769    fn parse_expr_complex() {
1770        check_expr("a.b[c](d) + e", expect![[r#"
1771                ROOT@0..13
1772                  BINARY_EXPR@0..13
1773                    CALL_EXPR@0..9
1774                      INDEX_EXPR@0..6
1775                        FIELD_EXPR@0..3
1776                          PATH_EXPR@0..1
1777                            IDENT@0..1 "a"
1778                          DOT@1..2 "."
1779                          IDENT@2..3 "b"
1780                        L_BRACKET@3..4 "["
1781                        PATH_EXPR@4..5
1782                          IDENT@4..5 "c"
1783                        R_BRACKET@5..6 "]"
1784                      L_PAREN@6..7 "("
1785                      PATH_EXPR@7..8
1786                        IDENT@7..8 "d"
1787                      R_PAREN@8..9 ")"
1788                    WHITESPACE@9..10 " "
1789                    PLUS@10..11 "+"
1790                    WHITESPACE@11..12 " "
1791                    PATH_EXPR@12..13
1792                      IDENT@12..13 "e"
1793            "#]]);
1794    }
1795
1796    // =========================================================================
1797    // Non-Associative Operator Chaining (should produce errors)
1798    // =========================================================================
1799
1800    fn parse_expr_for_test(input: &str) -> Parse {
1801        let (tokens, _) = lex(input);
1802        let mut parser = Parser::new(input, &tokens);
1803        let root = parser.start();
1804        parser.parse_expr();
1805        parser.skip_trivia();
1806        root.complete(&mut parser, ROOT);
1807        parser.finish(vec![])
1808    }
1809
1810    #[test]
1811    fn parse_expr_chained_eq_is_error() {
1812        // Chained == is not allowed: 1 == 2 == 3
1813        let parse = parse_expr_for_test("1 == 2 == 3");
1814        assert!(!parse.errors().is_empty(), "expected error for chained ==, got none");
1815        assert!(
1816            parse.errors().iter().any(|e| e.message.contains("'&&'") || e.message.contains("expected")),
1817            "expected error message about valid operators, got: {:?}",
1818            parse.errors()
1819        );
1820    }
1821
1822    #[test]
1823    fn parse_expr_chained_neq_is_error() {
1824        // Chained != is not allowed: 1 != 2 != 3
1825        let parse = parse_expr_for_test("1 != 2 != 3");
1826        assert!(!parse.errors().is_empty(), "expected error for chained !=, got none");
1827    }
1828
1829    #[test]
1830    fn parse_expr_chained_lt_is_error() {
1831        // Chained < is not allowed: 1 < 2 < 3
1832        let parse = parse_expr_for_test("1 < 2 < 3");
1833        assert!(!parse.errors().is_empty(), "expected error for chained <, got none");
1834    }
1835
1836    #[test]
1837    fn parse_expr_chained_gt_is_error() {
1838        // Chained > is not allowed: 1 > 2 > 3
1839        let parse = parse_expr_for_test("1 > 2 > 3");
1840        assert!(!parse.errors().is_empty(), "expected error for chained >, got none");
1841    }
1842
1843    #[test]
1844    fn parse_expr_comparison_with_logical_is_ok() {
1845        // Comparison followed by logical is allowed: 1 == 2 && 3 == 4
1846        check_expr_no_errors("1 == 2 && 3 == 4");
1847        check_expr_no_errors("1 < 2 || 3 > 4");
1848    }
1849
1850    // =========================================================================
1851    // Associated function calls (type keyword :: function)
1852    // =========================================================================
1853
1854    #[test]
1855    fn parse_expr_group_associated_fn() {
1856        // The lexer produces a single IDENT token for "group::to_x_coordinate"
1857        // via the PathSpecial regex pattern.
1858        check_expr("group::to_x_coordinate(a)", expect![[r#"
1859                ROOT@0..25
1860                  CALL_EXPR@0..25
1861                    PATH_EXPR@0..22
1862                      IDENT@0..22 "group::to_x_coordinate"
1863                    L_PAREN@22..23 "("
1864                    PATH_EXPR@23..24
1865                      IDENT@23..24 "a"
1866                    R_PAREN@24..25 ")"
1867            "#]]);
1868    }
1869
1870    #[test]
1871    fn parse_expr_signature_associated_fn() {
1872        // The lexer produces a single IDENT token for "signature::verify"
1873        // via the PathSpecial regex pattern.
1874        check_expr("signature::verify(s, a, v)", expect![[r#"
1875                ROOT@0..26
1876                  CALL_EXPR@0..26
1877                    PATH_EXPR@0..17
1878                      IDENT@0..17 "signature::verify"
1879                    L_PAREN@17..18 "("
1880                    PATH_EXPR@18..19
1881                      IDENT@18..19 "s"
1882                    COMMA@19..20 ","
1883                    WHITESPACE@20..21 " "
1884                    PATH_EXPR@21..22
1885                      IDENT@21..22 "a"
1886                    COMMA@22..23 ","
1887                    WHITESPACE@23..24 " "
1888                    PATH_EXPR@24..25
1889                      IDENT@24..25 "v"
1890                    R_PAREN@25..26 ")"
1891            "#]]);
1892    }
1893
1894    // =========================================================================
1895    // Chained Comparison Errors (1a)
1896    // =========================================================================
1897
1898    #[test]
1899    fn parse_expr_chained_le_is_error() {
1900        let parse = parse_expr_for_test("1 <= 2 <= 3");
1901        assert!(!parse.errors().is_empty(), "expected error for chained <=, got none");
1902    }
1903
1904    #[test]
1905    fn parse_expr_chained_ge_is_error() {
1906        let parse = parse_expr_for_test("1 >= 2 >= 3");
1907        assert!(!parse.errors().is_empty(), "expected error for chained >=, got none");
1908    }
1909
1910    #[test]
1911    fn parse_expr_chained_mixed_cmp_is_error() {
1912        let parse = parse_expr_for_test("1 < 2 > 3");
1913        assert!(!parse.errors().is_empty(), "expected error for mixed chained comparisons, got none");
1914    }
1915
1916    // =========================================================================
1917    // Nested Ternary (1b)
1918    // =========================================================================
1919
1920    #[test]
1921    fn parse_expr_ternary_nested() {
1922        check_expr("a ? b ? c : d : e", expect![[r#"
1923            ROOT@0..17
1924              TERNARY_EXPR@0..17
1925                PATH_EXPR@0..2
1926                  IDENT@0..1 "a"
1927                  WHITESPACE@1..2 " "
1928                QUESTION@2..3 "?"
1929                WHITESPACE@3..4 " "
1930                TERNARY_EXPR@4..14
1931                  PATH_EXPR@4..6
1932                    IDENT@4..5 "b"
1933                    WHITESPACE@5..6 " "
1934                  QUESTION@6..7 "?"
1935                  WHITESPACE@7..8 " "
1936                  PATH_EXPR@8..10
1937                    IDENT@8..9 "c"
1938                    WHITESPACE@9..10 " "
1939                  COLON@10..11 ":"
1940                  WHITESPACE@11..12 " "
1941                  PATH_EXPR@12..14
1942                    IDENT@12..13 "d"
1943                    WHITESPACE@13..14 " "
1944                COLON@14..15 ":"
1945                WHITESPACE@15..16 " "
1946                PATH_EXPR@16..17
1947                  IDENT@16..17 "e"
1948        "#]]);
1949    }
1950
1951    // =========================================================================
1952    // Chained Casts (1c)
1953    // =========================================================================
1954
1955    #[test]
1956    fn parse_expr_cast_chained() {
1957        check_expr("x as u32 as u64", expect![[r#"
1958            ROOT@0..15
1959              CAST_EXPR@0..15
1960                CAST_EXPR@0..8
1961                  PATH_EXPR@0..2
1962                    IDENT@0..1 "x"
1963                    WHITESPACE@1..2 " "
1964                  KW_AS@2..4 "as"
1965                  WHITESPACE@4..5 " "
1966                  TYPE_PRIMITIVE@5..8
1967                    KW_U32@5..8 "u32"
1968                WHITESPACE@8..9 " "
1969                KW_AS@9..11 "as"
1970                WHITESPACE@11..12 " "
1971                TYPE_PRIMITIVE@12..15
1972                  KW_U64@12..15 "u64"
1973        "#]]);
1974    }
1975
1976    // =========================================================================
1977    // Collection Edge Cases (1d)
1978    // =========================================================================
1979
1980    #[test]
1981    fn parse_expr_array_trailing_comma() {
1982        check_expr("[1, 2, 3,]", expect![[r#"
1983            ROOT@0..10
1984              ARRAY_EXPR@0..10
1985                L_BRACKET@0..1 "["
1986                LITERAL_INT@1..2
1987                  INTEGER@1..2 "1"
1988                COMMA@2..3 ","
1989                WHITESPACE@3..4 " "
1990                LITERAL_INT@4..5
1991                  INTEGER@4..5 "2"
1992                COMMA@5..6 ","
1993                WHITESPACE@6..7 " "
1994                LITERAL_INT@7..8
1995                  INTEGER@7..8 "3"
1996                COMMA@8..9 ","
1997                R_BRACKET@9..10 "]"
1998        "#]]);
1999    }
2000
2001    #[test]
2002    fn parse_expr_array_empty() {
2003        check_expr("[]", expect![[r#"
2004            ROOT@0..2
2005              ARRAY_EXPR@0..2
2006                L_BRACKET@0..1 "["
2007                R_BRACKET@1..2 "]"
2008        "#]]);
2009    }
2010
2011    #[test]
2012    fn parse_expr_tuple_single() {
2013        check_expr("(a,)", expect![[r#"
2014            ROOT@0..4
2015              TUPLE_EXPR@0..4
2016                L_PAREN@0..1 "("
2017                PATH_EXPR@1..2
2018                  IDENT@1..2 "a"
2019                COMMA@2..3 ","
2020                R_PAREN@3..4 ")"
2021        "#]]);
2022    }
2023
2024    #[test]
2025    fn parse_expr_tuple_trailing_comma() {
2026        check_expr("(1, 2,)", expect![[r#"
2027            ROOT@0..7
2028              TUPLE_EXPR@0..7
2029                L_PAREN@0..1 "("
2030                LITERAL_INT@1..2
2031                  INTEGER@1..2 "1"
2032                COMMA@2..3 ","
2033                WHITESPACE@3..4 " "
2034                LITERAL_INT@4..5
2035                  INTEGER@4..5 "2"
2036                COMMA@5..6 ","
2037                R_PAREN@6..7 ")"
2038        "#]]);
2039    }
2040
2041    // =========================================================================
2042    // Struct Literal Edge Cases (1e)
2043    // =========================================================================
2044
2045    #[test]
2046    fn parse_expr_struct_empty() {
2047        check_expr("Point { }", expect![[r#"
2048            ROOT@0..9
2049              STRUCT_EXPR@0..9
2050                IDENT@0..5 "Point"
2051                WHITESPACE@5..6 " "
2052                L_BRACE@6..7 "{"
2053                WHITESPACE@7..8 " "
2054                R_BRACE@8..9 "}"
2055        "#]]);
2056    }
2057
2058    #[test]
2059    fn parse_expr_struct_trailing_comma() {
2060        check_expr("Point { x: 1, }", expect![[r#"
2061            ROOT@0..15
2062              STRUCT_EXPR@0..15
2063                IDENT@0..5 "Point"
2064                WHITESPACE@5..6 " "
2065                L_BRACE@6..7 "{"
2066                STRUCT_FIELD_INIT@7..12
2067                  WHITESPACE@7..8 " "
2068                  IDENT@8..9 "x"
2069                  COLON@9..10 ":"
2070                  WHITESPACE@10..11 " "
2071                  LITERAL_INT@11..12
2072                    INTEGER@11..12 "1"
2073                COMMA@12..13 ","
2074                WHITESPACE@13..14 " "
2075                R_BRACE@14..15 "}"
2076        "#]]);
2077    }
2078
2079    #[test]
2080    fn parse_expr_struct_mixed_fields() {
2081        check_expr("Point { x, y: 2 }", expect![[r#"
2082            ROOT@0..17
2083              STRUCT_EXPR@0..17
2084                IDENT@0..5 "Point"
2085                WHITESPACE@5..6 " "
2086                L_BRACE@6..7 "{"
2087                STRUCT_FIELD_SHORTHAND@7..9
2088                  WHITESPACE@7..8 " "
2089                  IDENT@8..9 "x"
2090                COMMA@9..10 ","
2091                STRUCT_FIELD_INIT@10..15
2092                  WHITESPACE@10..11 " "
2093                  IDENT@11..12 "y"
2094                  COLON@12..13 ":"
2095                  WHITESPACE@13..14 " "
2096                  LITERAL_INT@14..15
2097                    INTEGER@14..15 "2"
2098                WHITESPACE@15..16 " "
2099                R_BRACE@16..17 "}"
2100        "#]]);
2101    }
2102
2103    // =========================================================================
2104    // Additional Literals (1f)
2105    // =========================================================================
2106
2107    #[test]
2108    fn parse_expr_string() {
2109        check_expr("\"hello\"", expect![[r#"
2110            ROOT@0..7
2111              LITERAL_STRING@0..7
2112                STRING@0..7 "\"hello\""
2113        "#]]);
2114    }
2115
2116    #[test]
2117    fn parse_expr_address() {
2118        check_expr("aleo1qnr4dkkvkgfqph0vzc3y6z2eu975wnpz2925ntjccd5cfqxtyu8s7pyjh9", expect![[r#"
2119            ROOT@0..63
2120              LITERAL_ADDRESS@0..63
2121                ADDRESS_LIT@0..63 "aleo1qnr4dkkvkgfqph0v ..."
2122        "#]]);
2123    }
2124
2125    // =========================================================================
2126    // Deep Postfix Chains (1g)
2127    // =========================================================================
2128
2129    #[test]
2130    fn parse_expr_deep_postfix() {
2131        check_expr("a[0].b.c(x)[1]", expect![[r#"
2132            ROOT@0..14
2133              INDEX_EXPR@0..14
2134                METHOD_CALL_EXPR@0..11
2135                  FIELD_EXPR@0..6
2136                    INDEX_EXPR@0..4
2137                      PATH_EXPR@0..1
2138                        IDENT@0..1 "a"
2139                      L_BRACKET@1..2 "["
2140                      LITERAL_INT@2..3
2141                        INTEGER@2..3 "0"
2142                      R_BRACKET@3..4 "]"
2143                    DOT@4..5 "."
2144                    IDENT@5..6 "b"
2145                  DOT@6..7 "."
2146                  IDENT@7..8 "c"
2147                  L_PAREN@8..9 "("
2148                  PATH_EXPR@9..10
2149                    IDENT@9..10 "x"
2150                  R_PAREN@10..11 ")"
2151                L_BRACKET@11..12 "["
2152                LITERAL_INT@12..13
2153                  INTEGER@12..13 "1"
2154                R_BRACKET@13..14 "]"
2155        "#]]);
2156    }
2157
2158    // =========================================================================
2159    // Final Expression (1h)
2160    // =========================================================================
2161
2162    #[test]
2163    fn parse_expr_final() {
2164        check_expr("final { foo() }", expect![[r#"
2165            ROOT@0..15
2166              FINAL_EXPR@0..15
2167                KW_FINAL@0..5 "final"
2168                WHITESPACE@5..6 " "
2169                BLOCK@6..15
2170                  L_BRACE@6..7 "{"
2171                  WHITESPACE@7..8 " "
2172                  EXPR_STMT@8..14
2173                    CALL_EXPR@8..13
2174                      PATH_EXPR@8..11
2175                        IDENT@8..11 "foo"
2176                      L_PAREN@11..12 "("
2177                      R_PAREN@12..13 ")"
2178                    WHITESPACE@13..14 " "
2179                  ERROR@14..14
2180                  R_BRACE@14..15 "}"
2181        "#]]);
2182    }
2183
2184    // =========================================================================
2185    // Complex Precedence (1i)
2186    // =========================================================================
2187
2188    #[test]
2189    fn parse_expr_mixed_arithmetic() {
2190        // a + b * c / d - e  =>  (a + ((b * c) / d)) - e
2191        check_expr("a + b * c / d - e", expect![[r#"
2192            ROOT@0..17
2193              BINARY_EXPR@0..17
2194                BINARY_EXPR@0..14
2195                  PATH_EXPR@0..2
2196                    IDENT@0..1 "a"
2197                    WHITESPACE@1..2 " "
2198                  PLUS@2..3 "+"
2199                  WHITESPACE@3..4 " "
2200                  BINARY_EXPR@4..14
2201                    BINARY_EXPR@4..10
2202                      PATH_EXPR@4..6
2203                        IDENT@4..5 "b"
2204                        WHITESPACE@5..6 " "
2205                      STAR@6..7 "*"
2206                      WHITESPACE@7..8 " "
2207                      PATH_EXPR@8..10
2208                        IDENT@8..9 "c"
2209                        WHITESPACE@9..10 " "
2210                    SLASH@10..11 "/"
2211                    WHITESPACE@11..12 " "
2212                    PATH_EXPR@12..14
2213                      IDENT@12..13 "d"
2214                      WHITESPACE@13..14 " "
2215                MINUS@14..15 "-"
2216                WHITESPACE@15..16 " "
2217                PATH_EXPR@16..17
2218                  IDENT@16..17 "e"
2219        "#]]);
2220    }
2221
2222    #[test]
2223    fn parse_expr_bitwise_precedence() {
2224        // a | b & c ^ d  =>  a | ((b & c) ^ d)  ... actually:
2225        // & (BP 16,17) binds tighter than ^ (14,15) tighter than | (12,13)
2226        // so: a | ((b & c) ^ d)
2227        check_expr("a | b & c ^ d", expect![[r#"
2228            ROOT@0..13
2229              BINARY_EXPR@0..13
2230                PATH_EXPR@0..2
2231                  IDENT@0..1 "a"
2232                  WHITESPACE@1..2 " "
2233                PIPE@2..3 "|"
2234                WHITESPACE@3..4 " "
2235                BINARY_EXPR@4..13
2236                  BINARY_EXPR@4..10
2237                    PATH_EXPR@4..6
2238                      IDENT@4..5 "b"
2239                      WHITESPACE@5..6 " "
2240                    AMP@6..7 "&"
2241                    WHITESPACE@7..8 " "
2242                    PATH_EXPR@8..10
2243                      IDENT@8..9 "c"
2244                      WHITESPACE@9..10 " "
2245                  CARET@10..11 "^"
2246                  WHITESPACE@11..12 " "
2247                  PATH_EXPR@12..13
2248                    IDENT@12..13 "d"
2249        "#]]);
2250    }
2251
2252    #[test]
2253    fn parse_expr_shift_chain() {
2254        // << and >> are left-assoc at same precedence
2255        // x << 1 >> 2  =>  (x << 1) >> 2
2256        check_expr("x << 1 >> 2", expect![[r#"
2257            ROOT@0..11
2258              BINARY_EXPR@0..11
2259                BINARY_EXPR@0..6
2260                  PATH_EXPR@0..2
2261                    IDENT@0..1 "x"
2262                    WHITESPACE@1..2 " "
2263                  SHL@2..4 "<<"
2264                  WHITESPACE@4..5 " "
2265                  LITERAL_INT@5..6
2266                    INTEGER@5..6 "1"
2267                WHITESPACE@6..7 " "
2268                SHR@7..9 ">>"
2269                WHITESPACE@9..10 " "
2270                LITERAL_INT@10..11
2271                  INTEGER@10..11 "2"
2272        "#]]);
2273    }
2274}