Skip to main content

leo_parser_rowan/parser/
expressions.rs

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