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