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