Skip to main content

harn_parser/
parser.rs

1use crate::ast::*;
2use harn_lexer::{Span, Token, TokenKind};
3use std::fmt;
4
5/// Parser errors.
6#[derive(Debug, Clone, PartialEq)]
7pub enum ParserError {
8    Unexpected {
9        got: String,
10        expected: String,
11        span: Span,
12    },
13    UnexpectedEof {
14        expected: String,
15    },
16}
17
18impl fmt::Display for ParserError {
19    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20        match self {
21            ParserError::Unexpected {
22                got,
23                expected,
24                span,
25            } => write!(
26                f,
27                "Expected {expected}, got {got} at {}:{}",
28                span.line, span.column
29            ),
30            ParserError::UnexpectedEof { expected } => {
31                write!(f, "Unexpected end of file, expected {expected}")
32            }
33        }
34    }
35}
36
37impl std::error::Error for ParserError {}
38
39/// Recursive descent parser for Harn.
40pub struct Parser {
41    tokens: Vec<Token>,
42    pos: usize,
43    errors: Vec<ParserError>,
44}
45
46impl Parser {
47    pub fn new(tokens: Vec<Token>) -> Self {
48        Self {
49            tokens,
50            pos: 0,
51            errors: Vec::new(),
52        }
53    }
54
55    fn current_span(&self) -> Span {
56        self.tokens
57            .get(self.pos)
58            .map(|t| t.span)
59            .unwrap_or(Span::dummy())
60    }
61
62    fn current_kind(&self) -> Option<&TokenKind> {
63        self.tokens.get(self.pos).map(|t| &t.kind)
64    }
65
66    fn prev_span(&self) -> Span {
67        if self.pos > 0 {
68            self.tokens[self.pos - 1].span
69        } else {
70            Span::dummy()
71        }
72    }
73
74    /// Parse a complete .harn file. Reports multiple errors via recovery.
75    pub fn parse(&mut self) -> Result<Vec<SNode>, ParserError> {
76        let mut nodes = Vec::new();
77        self.skip_newlines();
78
79        while !self.is_at_end() {
80            // Skip any stray closing braces at top level (after recovery)
81            if self.check(&TokenKind::RBrace) {
82                self.advance();
83                self.skip_newlines();
84                continue;
85            }
86
87            let result = if self.check(&TokenKind::Import) {
88                self.parse_import()
89            } else if self.check(&TokenKind::Pipeline) {
90                self.parse_pipeline()
91            } else {
92                self.parse_statement()
93            };
94
95            match result {
96                Ok(node) => nodes.push(node),
97                Err(err) => {
98                    self.errors.push(err);
99                    self.synchronize();
100                }
101            }
102            self.skip_newlines();
103        }
104
105        if let Some(first) = self.errors.first() {
106            return Err(first.clone());
107        }
108        Ok(nodes)
109    }
110
111    /// Return all accumulated parser errors (after `parse()` returns).
112    pub fn all_errors(&self) -> &[ParserError] {
113        &self.errors
114    }
115
116    /// Check if the current token is one that starts a statement.
117    fn is_statement_start(&self) -> bool {
118        matches!(
119            self.current_kind(),
120            Some(
121                TokenKind::Let
122                    | TokenKind::Var
123                    | TokenKind::If
124                    | TokenKind::For
125                    | TokenKind::While
126                    | TokenKind::Match
127                    | TokenKind::Retry
128                    | TokenKind::Return
129                    | TokenKind::Throw
130                    | TokenKind::Fn
131                    | TokenKind::Pub
132                    | TokenKind::Try
133                    | TokenKind::Select
134                    | TokenKind::Pipeline
135                    | TokenKind::Import
136                    | TokenKind::Parallel
137                    | TokenKind::ParallelMap
138                    | TokenKind::ParallelSettle
139                    | TokenKind::Enum
140                    | TokenKind::Struct
141                    | TokenKind::Interface
142                    | TokenKind::Guard
143                    | TokenKind::Require
144                    | TokenKind::Deadline
145                    | TokenKind::Yield
146                    | TokenKind::Mutex
147            )
148        )
149    }
150
151    /// Advance past tokens until we reach a likely statement boundary.
152    fn synchronize(&mut self) {
153        while !self.is_at_end() {
154            if self.check(&TokenKind::Newline) {
155                self.advance();
156                if self.is_at_end() || self.is_statement_start() {
157                    return;
158                }
159                continue;
160            }
161            if self.check(&TokenKind::RBrace) {
162                return;
163            }
164            self.advance();
165        }
166    }
167
168    /// Parse a single expression (for string interpolation).
169    pub fn parse_single_expression(&mut self) -> Result<SNode, ParserError> {
170        self.skip_newlines();
171        self.parse_expression()
172    }
173
174    // --- Declarations ---
175
176    fn parse_pipeline(&mut self) -> Result<SNode, ParserError> {
177        let start = self.current_span();
178        self.consume(&TokenKind::Pipeline, "pipeline")?;
179        let name = self.consume_identifier("pipeline name")?;
180
181        self.consume(&TokenKind::LParen, "(")?;
182        let params = self.parse_param_list()?;
183        self.consume(&TokenKind::RParen, ")")?;
184
185        let extends = if self.check(&TokenKind::Extends) {
186            self.advance();
187            Some(self.consume_identifier("parent pipeline name")?)
188        } else {
189            None
190        };
191
192        self.consume(&TokenKind::LBrace, "{")?;
193        let body = self.parse_block()?;
194        self.consume(&TokenKind::RBrace, "}")?;
195
196        Ok(spanned(
197            Node::Pipeline {
198                name,
199                params,
200                body,
201                extends,
202            },
203            Span::merge(start, self.prev_span()),
204        ))
205    }
206
207    fn parse_import(&mut self) -> Result<SNode, ParserError> {
208        let start = self.current_span();
209        self.consume(&TokenKind::Import, "import")?;
210
211        // Check for selective import: import { foo, bar } from "module"
212        if self.check(&TokenKind::LBrace) {
213            self.advance(); // skip {
214            self.skip_newlines();
215            let mut names = Vec::new();
216            while !self.is_at_end() && !self.check(&TokenKind::RBrace) {
217                let name = self.consume_identifier("import name")?;
218                names.push(name);
219                self.skip_newlines();
220                if self.check(&TokenKind::Comma) {
221                    self.advance();
222                    self.skip_newlines();
223                }
224            }
225            self.consume(&TokenKind::RBrace, "}")?;
226            self.consume(&TokenKind::From, "from")?;
227            if let Some(tok) = self.current() {
228                if let TokenKind::StringLiteral(path) = &tok.kind {
229                    let path = path.clone();
230                    self.advance();
231                    return Ok(spanned(
232                        Node::SelectiveImport { names, path },
233                        Span::merge(start, self.prev_span()),
234                    ));
235                }
236            }
237            return Err(self.error("import path string"));
238        }
239
240        if let Some(tok) = self.current() {
241            if let TokenKind::StringLiteral(path) = &tok.kind {
242                let path = path.clone();
243                self.advance();
244                return Ok(spanned(
245                    Node::ImportDecl { path },
246                    Span::merge(start, self.prev_span()),
247                ));
248            }
249        }
250        Err(self.error("import path string"))
251    }
252
253    // --- Statements ---
254
255    fn parse_block(&mut self) -> Result<Vec<SNode>, ParserError> {
256        let mut stmts = Vec::new();
257        self.skip_newlines();
258
259        while !self.is_at_end() && !self.check(&TokenKind::RBrace) {
260            stmts.push(self.parse_statement()?);
261            self.skip_newlines();
262        }
263        Ok(stmts)
264    }
265
266    fn parse_statement(&mut self) -> Result<SNode, ParserError> {
267        self.skip_newlines();
268
269        let tok = self.current().ok_or_else(|| ParserError::UnexpectedEof {
270            expected: "statement".into(),
271        })?;
272
273        match &tok.kind {
274            TokenKind::Let => self.parse_let_binding(),
275            TokenKind::Var => self.parse_var_binding(),
276            TokenKind::If => self.parse_if_else(),
277            TokenKind::For => self.parse_for_in(),
278            TokenKind::Match => self.parse_match(),
279            TokenKind::Retry => self.parse_retry(),
280            TokenKind::While => self.parse_while_loop(),
281            TokenKind::Parallel => self.parse_parallel(),
282            TokenKind::ParallelMap => self.parse_parallel_map(),
283            TokenKind::ParallelSettle => self.parse_parallel_settle(),
284            TokenKind::Return => self.parse_return(),
285            TokenKind::Throw => self.parse_throw(),
286            TokenKind::Override => self.parse_override(),
287            TokenKind::Try => self.parse_try_catch(),
288            TokenKind::Select => self.parse_select(),
289            TokenKind::Fn => self.parse_fn_decl_with_pub(false),
290            TokenKind::Pub => {
291                self.advance(); // consume 'pub'
292                let tok = self.current().ok_or_else(|| ParserError::UnexpectedEof {
293                    expected: "fn, struct, enum, or pipeline after pub".into(),
294                })?;
295                match &tok.kind {
296                    TokenKind::Fn => self.parse_fn_decl_with_pub(true),
297                    _ => Err(self.error("fn, struct, enum, or pipeline after pub")),
298                }
299            }
300            TokenKind::TypeKw => self.parse_type_decl(),
301            TokenKind::Enum => self.parse_enum_decl(),
302            TokenKind::Struct => self.parse_struct_decl(),
303            TokenKind::Interface => self.parse_interface_decl(),
304            TokenKind::Impl => self.parse_impl_block(),
305            TokenKind::Guard => self.parse_guard(),
306            TokenKind::Require => self.parse_require(),
307            TokenKind::Deadline => self.parse_deadline(),
308            TokenKind::Yield => self.parse_yield(),
309            TokenKind::Mutex => self.parse_mutex(),
310            TokenKind::Break => {
311                let span = self.current_span();
312                self.advance();
313                Ok(spanned(Node::BreakStmt, span))
314            }
315            TokenKind::Continue => {
316                let span = self.current_span();
317                self.advance();
318                Ok(spanned(Node::ContinueStmt, span))
319            }
320            _ => self.parse_expression_statement(),
321        }
322    }
323
324    fn parse_let_binding(&mut self) -> Result<SNode, ParserError> {
325        let start = self.current_span();
326        self.consume(&TokenKind::Let, "let")?;
327        let pattern = self.parse_binding_pattern()?;
328        let type_ann = if matches!(pattern, BindingPattern::Identifier(_)) {
329            self.try_parse_type_annotation()?
330        } else {
331            None
332        };
333        self.consume(&TokenKind::Assign, "=")?;
334        let value = self.parse_expression()?;
335        Ok(spanned(
336            Node::LetBinding {
337                pattern,
338                type_ann,
339                value: Box::new(value),
340            },
341            Span::merge(start, self.prev_span()),
342        ))
343    }
344
345    fn parse_var_binding(&mut self) -> Result<SNode, ParserError> {
346        let start = self.current_span();
347        self.consume(&TokenKind::Var, "var")?;
348        let pattern = self.parse_binding_pattern()?;
349        let type_ann = if matches!(pattern, BindingPattern::Identifier(_)) {
350            self.try_parse_type_annotation()?
351        } else {
352            None
353        };
354        self.consume(&TokenKind::Assign, "=")?;
355        let value = self.parse_expression()?;
356        Ok(spanned(
357            Node::VarBinding {
358                pattern,
359                type_ann,
360                value: Box::new(value),
361            },
362            Span::merge(start, self.prev_span()),
363        ))
364    }
365
366    fn parse_if_else(&mut self) -> Result<SNode, ParserError> {
367        let start = self.current_span();
368        self.consume(&TokenKind::If, "if")?;
369        let condition = self.parse_expression()?;
370        self.consume(&TokenKind::LBrace, "{")?;
371        let then_body = self.parse_block()?;
372        self.consume(&TokenKind::RBrace, "}")?;
373        self.skip_newlines();
374
375        let else_body = if self.check(&TokenKind::Else) {
376            self.advance();
377            if self.check(&TokenKind::If) {
378                Some(vec![self.parse_if_else()?])
379            } else {
380                self.consume(&TokenKind::LBrace, "{")?;
381                let body = self.parse_block()?;
382                self.consume(&TokenKind::RBrace, "}")?;
383                Some(body)
384            }
385        } else {
386            None
387        };
388
389        Ok(spanned(
390            Node::IfElse {
391                condition: Box::new(condition),
392                then_body,
393                else_body,
394            },
395            Span::merge(start, self.prev_span()),
396        ))
397    }
398
399    fn parse_for_in(&mut self) -> Result<SNode, ParserError> {
400        let start = self.current_span();
401        self.consume(&TokenKind::For, "for")?;
402        let pattern = self.parse_binding_pattern()?;
403        self.consume(&TokenKind::In, "in")?;
404        let iterable = self.parse_expression()?;
405        self.consume(&TokenKind::LBrace, "{")?;
406        let body = self.parse_block()?;
407        self.consume(&TokenKind::RBrace, "}")?;
408        Ok(spanned(
409            Node::ForIn {
410                pattern,
411                iterable: Box::new(iterable),
412                body,
413            },
414            Span::merge(start, self.prev_span()),
415        ))
416    }
417
418    /// Parse a binding pattern for let/var/for-in:
419    ///   identifier | { fields } | [ elements ]
420    fn parse_binding_pattern(&mut self) -> Result<BindingPattern, ParserError> {
421        self.skip_newlines();
422        if self.check(&TokenKind::LBrace) {
423            // Dict destructuring: { key, key: alias, ...rest }
424            self.advance(); // consume {
425            let mut fields = Vec::new();
426            while !self.is_at_end() && !self.check(&TokenKind::RBrace) {
427                // Check for rest pattern: ...ident
428                if self.check(&TokenKind::Dot) {
429                    // Consume three dots
430                    self.advance(); // .
431                    self.consume(&TokenKind::Dot, ".")?;
432                    self.consume(&TokenKind::Dot, ".")?;
433                    let name = self.consume_identifier("rest variable name")?;
434                    fields.push(DictPatternField {
435                        key: name,
436                        alias: None,
437                        is_rest: true,
438                    });
439                    // Rest must be last
440                    break;
441                }
442                let key = self.consume_identifier("field name")?;
443                let alias = if self.check(&TokenKind::Colon) {
444                    self.advance(); // consume :
445                    Some(self.consume_identifier("alias name")?)
446                } else {
447                    None
448                };
449                fields.push(DictPatternField {
450                    key,
451                    alias,
452                    is_rest: false,
453                });
454                if self.check(&TokenKind::Comma) {
455                    self.advance();
456                }
457            }
458            self.consume(&TokenKind::RBrace, "}")?;
459            Ok(BindingPattern::Dict(fields))
460        } else if self.check(&TokenKind::LBracket) {
461            // List destructuring: [ name, name, ...rest ]
462            self.advance(); // consume [
463            let mut elements = Vec::new();
464            while !self.is_at_end() && !self.check(&TokenKind::RBracket) {
465                // Check for rest pattern: ...ident
466                if self.check(&TokenKind::Dot) {
467                    self.advance(); // .
468                    self.consume(&TokenKind::Dot, ".")?;
469                    self.consume(&TokenKind::Dot, ".")?;
470                    let name = self.consume_identifier("rest variable name")?;
471                    elements.push(ListPatternElement {
472                        name,
473                        is_rest: true,
474                    });
475                    // Rest must be last
476                    break;
477                }
478                let name = self.consume_identifier("element name")?;
479                elements.push(ListPatternElement {
480                    name,
481                    is_rest: false,
482                });
483                if self.check(&TokenKind::Comma) {
484                    self.advance();
485                }
486            }
487            self.consume(&TokenKind::RBracket, "]")?;
488            Ok(BindingPattern::List(elements))
489        } else {
490            // Simple identifier
491            let name = self.consume_identifier("variable name or destructuring pattern")?;
492            Ok(BindingPattern::Identifier(name))
493        }
494    }
495
496    fn parse_match(&mut self) -> Result<SNode, ParserError> {
497        let start = self.current_span();
498        self.consume(&TokenKind::Match, "match")?;
499        let value = self.parse_expression()?;
500        self.consume(&TokenKind::LBrace, "{")?;
501        self.skip_newlines();
502
503        let mut arms = Vec::new();
504        while !self.is_at_end() && !self.check(&TokenKind::RBrace) {
505            let pattern = self.parse_expression()?;
506            self.consume(&TokenKind::Arrow, "->")?;
507            self.consume(&TokenKind::LBrace, "{")?;
508            let body = self.parse_block()?;
509            self.consume(&TokenKind::RBrace, "}")?;
510            arms.push(MatchArm { pattern, body });
511            self.skip_newlines();
512        }
513
514        self.consume(&TokenKind::RBrace, "}")?;
515        Ok(spanned(
516            Node::MatchExpr {
517                value: Box::new(value),
518                arms,
519            },
520            Span::merge(start, self.prev_span()),
521        ))
522    }
523
524    fn parse_while_loop(&mut self) -> Result<SNode, ParserError> {
525        let start = self.current_span();
526        self.consume(&TokenKind::While, "while")?;
527        let condition = if self.check(&TokenKind::LParen) {
528            self.advance();
529            let c = self.parse_expression()?;
530            self.consume(&TokenKind::RParen, ")")?;
531            c
532        } else {
533            self.parse_expression()?
534        };
535        self.consume(&TokenKind::LBrace, "{")?;
536        let body = self.parse_block()?;
537        self.consume(&TokenKind::RBrace, "}")?;
538        Ok(spanned(
539            Node::WhileLoop {
540                condition: Box::new(condition),
541                body,
542            },
543            Span::merge(start, self.prev_span()),
544        ))
545    }
546
547    fn parse_retry(&mut self) -> Result<SNode, ParserError> {
548        let start = self.current_span();
549        self.consume(&TokenKind::Retry, "retry")?;
550        let count = if self.check(&TokenKind::LParen) {
551            self.advance();
552            let c = self.parse_expression()?;
553            self.consume(&TokenKind::RParen, ")")?;
554            c
555        } else {
556            self.parse_primary()?
557        };
558        self.consume(&TokenKind::LBrace, "{")?;
559        let body = self.parse_block()?;
560        self.consume(&TokenKind::RBrace, "}")?;
561        Ok(spanned(
562            Node::Retry {
563                count: Box::new(count),
564                body,
565            },
566            Span::merge(start, self.prev_span()),
567        ))
568    }
569
570    fn parse_parallel(&mut self) -> Result<SNode, ParserError> {
571        let start = self.current_span();
572        self.consume(&TokenKind::Parallel, "parallel")?;
573        self.consume(&TokenKind::LParen, "(")?;
574        let count = self.parse_expression()?;
575        self.consume(&TokenKind::RParen, ")")?;
576        self.consume(&TokenKind::LBrace, "{")?;
577
578        // Optional closure parameter: { i ->
579        let mut variable = None;
580        self.skip_newlines();
581        if let Some(tok) = self.current() {
582            if let TokenKind::Identifier(name) = &tok.kind {
583                if self.peek_kind() == Some(&TokenKind::Arrow) {
584                    let name = name.clone();
585                    self.advance(); // skip identifier
586                    self.advance(); // skip ->
587                    variable = Some(name);
588                }
589            }
590        }
591
592        let body = self.parse_block()?;
593        self.consume(&TokenKind::RBrace, "}")?;
594        Ok(spanned(
595            Node::Parallel {
596                count: Box::new(count),
597                variable,
598                body,
599            },
600            Span::merge(start, self.prev_span()),
601        ))
602    }
603
604    fn parse_parallel_map(&mut self) -> Result<SNode, ParserError> {
605        let start = self.current_span();
606        self.consume(&TokenKind::ParallelMap, "parallel_map")?;
607        self.consume(&TokenKind::LParen, "(")?;
608        let list = self.parse_expression()?;
609        self.consume(&TokenKind::RParen, ")")?;
610        self.consume(&TokenKind::LBrace, "{")?;
611
612        self.skip_newlines();
613        let variable = self.consume_identifier("map variable")?;
614        self.consume(&TokenKind::Arrow, "->")?;
615
616        let body = self.parse_block()?;
617        self.consume(&TokenKind::RBrace, "}")?;
618        Ok(spanned(
619            Node::ParallelMap {
620                list: Box::new(list),
621                variable,
622                body,
623            },
624            Span::merge(start, self.prev_span()),
625        ))
626    }
627
628    fn parse_parallel_settle(&mut self) -> Result<SNode, ParserError> {
629        let start = self.current_span();
630        self.consume(&TokenKind::ParallelSettle, "parallel_settle")?;
631        self.consume(&TokenKind::LParen, "(")?;
632        let list = self.parse_expression()?;
633        self.consume(&TokenKind::RParen, ")")?;
634        self.consume(&TokenKind::LBrace, "{")?;
635
636        self.skip_newlines();
637        let variable = self.consume_identifier("settle variable")?;
638        self.consume(&TokenKind::Arrow, "->")?;
639
640        let body = self.parse_block()?;
641        self.consume(&TokenKind::RBrace, "}")?;
642        Ok(spanned(
643            Node::ParallelSettle {
644                list: Box::new(list),
645                variable,
646                body,
647            },
648            Span::merge(start, self.prev_span()),
649        ))
650    }
651
652    fn parse_return(&mut self) -> Result<SNode, ParserError> {
653        let start = self.current_span();
654        self.consume(&TokenKind::Return, "return")?;
655        if self.is_at_end() || self.check(&TokenKind::Newline) || self.check(&TokenKind::RBrace) {
656            return Ok(spanned(
657                Node::ReturnStmt { value: None },
658                Span::merge(start, self.prev_span()),
659            ));
660        }
661        let value = self.parse_expression()?;
662        Ok(spanned(
663            Node::ReturnStmt {
664                value: Some(Box::new(value)),
665            },
666            Span::merge(start, self.prev_span()),
667        ))
668    }
669
670    fn parse_throw(&mut self) -> Result<SNode, ParserError> {
671        let start = self.current_span();
672        self.consume(&TokenKind::Throw, "throw")?;
673        let value = self.parse_expression()?;
674        Ok(spanned(
675            Node::ThrowStmt {
676                value: Box::new(value),
677            },
678            Span::merge(start, self.prev_span()),
679        ))
680    }
681
682    fn parse_override(&mut self) -> Result<SNode, ParserError> {
683        let start = self.current_span();
684        self.consume(&TokenKind::Override, "override")?;
685        let name = self.consume_identifier("override name")?;
686        self.consume(&TokenKind::LParen, "(")?;
687        let params = self.parse_param_list()?;
688        self.consume(&TokenKind::RParen, ")")?;
689        self.consume(&TokenKind::LBrace, "{")?;
690        let body = self.parse_block()?;
691        self.consume(&TokenKind::RBrace, "}")?;
692        Ok(spanned(
693            Node::OverrideDecl { name, params, body },
694            Span::merge(start, self.prev_span()),
695        ))
696    }
697
698    fn parse_try_catch(&mut self) -> Result<SNode, ParserError> {
699        let start = self.current_span();
700        self.consume(&TokenKind::Try, "try")?;
701        self.consume(&TokenKind::LBrace, "{")?;
702        let body = self.parse_block()?;
703        self.consume(&TokenKind::RBrace, "}")?;
704        self.skip_newlines();
705
706        // Parse optional catch block
707        let has_catch = self.check(&TokenKind::Catch);
708        let (error_var, error_type, catch_body) = if has_catch {
709            self.advance();
710            let (ev, et) = if self.check(&TokenKind::LParen) {
711                // catch (e) { ... } or catch (e: Type) { ... }
712                self.advance();
713                let name = self.consume_identifier("error variable")?;
714                let ty = self.try_parse_type_annotation()?;
715                self.consume(&TokenKind::RParen, ")")?;
716                (Some(name), ty)
717            } else if matches!(
718                self.current().map(|t| &t.kind),
719                Some(TokenKind::Identifier(_))
720            ) {
721                // catch e { ... } (no parens)
722                let name = self.consume_identifier("error variable")?;
723                (Some(name), None)
724            } else {
725                (None, None)
726            };
727            self.consume(&TokenKind::LBrace, "{")?;
728            let cb = self.parse_block()?;
729            self.consume(&TokenKind::RBrace, "}")?;
730            (ev, et, cb)
731        } else {
732            (None, None, Vec::new())
733        };
734
735        self.skip_newlines();
736
737        // Parse optional finally block
738        let finally_body = if self.check(&TokenKind::Finally) {
739            self.advance();
740            self.consume(&TokenKind::LBrace, "{")?;
741            let fb = self.parse_block()?;
742            self.consume(&TokenKind::RBrace, "}")?;
743            Some(fb)
744        } else {
745            None
746        };
747
748        // If no catch or finally, this is a try-expression (returns Result)
749        if !has_catch && finally_body.is_none() {
750            return Ok(spanned(
751                Node::TryExpr { body },
752                Span::merge(start, self.prev_span()),
753            ));
754        }
755
756        Ok(spanned(
757            Node::TryCatch {
758                body,
759                error_var,
760                error_type,
761                catch_body,
762                finally_body,
763            },
764            Span::merge(start, self.prev_span()),
765        ))
766    }
767
768    fn parse_select(&mut self) -> Result<SNode, ParserError> {
769        let start = self.current_span();
770        self.consume(&TokenKind::Select, "select")?;
771        self.consume(&TokenKind::LBrace, "{")?;
772        self.skip_newlines();
773
774        let mut cases = Vec::new();
775        let mut timeout = None;
776        let mut default_body = None;
777
778        while !self.is_at_end() && !self.check(&TokenKind::RBrace) {
779            self.skip_newlines();
780            // Check for "timeout" (contextual keyword)
781            if let Some(tok) = self.current() {
782                if let TokenKind::Identifier(ref id) = tok.kind {
783                    if id == "timeout" {
784                        self.advance();
785                        let duration = self.parse_expression()?;
786                        self.consume(&TokenKind::LBrace, "{")?;
787                        let body = self.parse_block()?;
788                        self.consume(&TokenKind::RBrace, "}")?;
789                        timeout = Some((Box::new(duration), body));
790                        self.skip_newlines();
791                        continue;
792                    }
793                    if id == "default" {
794                        self.advance();
795                        self.consume(&TokenKind::LBrace, "{")?;
796                        let body = self.parse_block()?;
797                        self.consume(&TokenKind::RBrace, "}")?;
798                        default_body = Some(body);
799                        self.skip_newlines();
800                        continue;
801                    }
802                }
803            }
804            // Regular case: variable from channel { body }
805            let variable = self.consume_identifier("select case variable")?;
806            self.consume(&TokenKind::From, "from")?;
807            let channel = self.parse_expression()?;
808            self.consume(&TokenKind::LBrace, "{")?;
809            let body = self.parse_block()?;
810            self.consume(&TokenKind::RBrace, "}")?;
811            cases.push(SelectCase {
812                variable,
813                channel: Box::new(channel),
814                body,
815            });
816            self.skip_newlines();
817        }
818
819        self.consume(&TokenKind::RBrace, "}")?;
820
821        if cases.is_empty() && timeout.is_none() && default_body.is_none() {
822            return Err(self.error("at least one select case"));
823        }
824        if timeout.is_some() && default_body.is_some() {
825            return Err(self.error("select cannot have both timeout and default"));
826        }
827
828        Ok(spanned(
829            Node::SelectExpr {
830                cases,
831                timeout,
832                default_body,
833            },
834            Span::merge(start, self.prev_span()),
835        ))
836    }
837
838    fn parse_fn_decl_with_pub(&mut self, is_pub: bool) -> Result<SNode, ParserError> {
839        let start = self.current_span();
840        self.consume(&TokenKind::Fn, "fn")?;
841        let name = self.consume_identifier("function name")?;
842
843        // Optional generic type parameters: fn name<T, U>(...)
844        let type_params = if self.check(&TokenKind::Lt) {
845            self.advance(); // skip <
846            self.parse_type_param_list()?
847        } else {
848            Vec::new()
849        };
850
851        self.consume(&TokenKind::LParen, "(")?;
852        let params = self.parse_typed_param_list()?;
853        self.consume(&TokenKind::RParen, ")")?;
854        // Optional return type: -> type
855        let return_type = if self.check(&TokenKind::Arrow) {
856            self.advance();
857            Some(self.parse_type_expr()?)
858        } else {
859            None
860        };
861
862        // Optional where clause: where T: bound, U: bound
863        let where_clauses = self.parse_where_clauses()?;
864
865        self.consume(&TokenKind::LBrace, "{")?;
866        let body = self.parse_block()?;
867        self.consume(&TokenKind::RBrace, "}")?;
868        Ok(spanned(
869            Node::FnDecl {
870                name,
871                type_params,
872                params,
873                return_type,
874                where_clauses,
875                body,
876                is_pub,
877            },
878            Span::merge(start, self.prev_span()),
879        ))
880    }
881
882    fn parse_type_decl(&mut self) -> Result<SNode, ParserError> {
883        let start = self.current_span();
884        self.consume(&TokenKind::TypeKw, "type")?;
885        let name = self.consume_identifier("type name")?;
886        self.consume(&TokenKind::Assign, "=")?;
887        let type_expr = self.parse_type_expr()?;
888        Ok(spanned(
889            Node::TypeDecl { name, type_expr },
890            Span::merge(start, self.prev_span()),
891        ))
892    }
893
894    fn parse_enum_decl(&mut self) -> Result<SNode, ParserError> {
895        let start = self.current_span();
896        self.consume(&TokenKind::Enum, "enum")?;
897        let name = self.consume_identifier("enum name")?;
898        self.consume(&TokenKind::LBrace, "{")?;
899        self.skip_newlines();
900
901        let mut variants = Vec::new();
902        while !self.is_at_end() && !self.check(&TokenKind::RBrace) {
903            let variant_name = self.consume_identifier("variant name")?;
904            let fields = if self.check(&TokenKind::LParen) {
905                self.advance();
906                let params = self.parse_typed_param_list()?;
907                self.consume(&TokenKind::RParen, ")")?;
908                params
909            } else {
910                Vec::new()
911            };
912            variants.push(EnumVariant {
913                name: variant_name,
914                fields,
915            });
916            self.skip_newlines();
917            if self.check(&TokenKind::Comma) {
918                self.advance();
919                self.skip_newlines();
920            }
921        }
922
923        self.consume(&TokenKind::RBrace, "}")?;
924        Ok(spanned(
925            Node::EnumDecl { name, variants },
926            Span::merge(start, self.prev_span()),
927        ))
928    }
929
930    fn parse_struct_decl(&mut self) -> Result<SNode, ParserError> {
931        let start = self.current_span();
932        self.consume(&TokenKind::Struct, "struct")?;
933        let name = self.consume_identifier("struct name")?;
934        self.consume(&TokenKind::LBrace, "{")?;
935        self.skip_newlines();
936
937        let mut fields = Vec::new();
938        while !self.is_at_end() && !self.check(&TokenKind::RBrace) {
939            let field_name = self.consume_identifier("field name")?;
940            let optional = if self.check(&TokenKind::Question) {
941                self.advance();
942                true
943            } else {
944                false
945            };
946            let type_expr = self.try_parse_type_annotation()?;
947            fields.push(StructField {
948                name: field_name,
949                type_expr,
950                optional,
951            });
952            self.skip_newlines();
953            if self.check(&TokenKind::Comma) {
954                self.advance();
955                self.skip_newlines();
956            }
957        }
958
959        self.consume(&TokenKind::RBrace, "}")?;
960        Ok(spanned(
961            Node::StructDecl { name, fields },
962            Span::merge(start, self.prev_span()),
963        ))
964    }
965
966    fn parse_interface_decl(&mut self) -> Result<SNode, ParserError> {
967        let start = self.current_span();
968        self.consume(&TokenKind::Interface, "interface")?;
969        let name = self.consume_identifier("interface name")?;
970        self.consume(&TokenKind::LBrace, "{")?;
971        self.skip_newlines();
972
973        let mut methods = Vec::new();
974        while !self.is_at_end() && !self.check(&TokenKind::RBrace) {
975            self.consume(&TokenKind::Fn, "fn")?;
976            let method_name = self.consume_identifier("method name")?;
977            self.consume(&TokenKind::LParen, "(")?;
978            let params = self.parse_typed_param_list()?;
979            self.consume(&TokenKind::RParen, ")")?;
980            // Optional return type: -> type
981            let return_type = if self.check(&TokenKind::Arrow) {
982                self.advance();
983                Some(self.parse_type_expr()?)
984            } else {
985                None
986            };
987            methods.push(InterfaceMethod {
988                name: method_name,
989                params,
990                return_type,
991            });
992            self.skip_newlines();
993        }
994
995        self.consume(&TokenKind::RBrace, "}")?;
996        Ok(spanned(
997            Node::InterfaceDecl { name, methods },
998            Span::merge(start, self.prev_span()),
999        ))
1000    }
1001
1002    fn parse_impl_block(&mut self) -> Result<SNode, ParserError> {
1003        let start = self.current_span();
1004        self.consume(&TokenKind::Impl, "impl")?;
1005        let type_name = self.consume_identifier("type name")?;
1006        self.consume(&TokenKind::LBrace, "{")?;
1007        self.skip_newlines();
1008
1009        let mut methods = Vec::new();
1010        while !self.is_at_end() && !self.check(&TokenKind::RBrace) {
1011            let is_pub = self.check(&TokenKind::Pub);
1012            if is_pub {
1013                self.advance();
1014            }
1015            let method = self.parse_fn_decl_with_pub(is_pub)?;
1016            methods.push(method);
1017            self.skip_newlines();
1018        }
1019
1020        self.consume(&TokenKind::RBrace, "}")?;
1021        Ok(spanned(
1022            Node::ImplBlock { type_name, methods },
1023            Span::merge(start, self.prev_span()),
1024        ))
1025    }
1026
1027    fn parse_guard(&mut self) -> Result<SNode, ParserError> {
1028        let start = self.current_span();
1029        self.consume(&TokenKind::Guard, "guard")?;
1030        let condition = self.parse_expression()?;
1031        // Consume "else" — we reuse the Else keyword
1032        self.consume(&TokenKind::Else, "else")?;
1033        self.consume(&TokenKind::LBrace, "{")?;
1034        let else_body = self.parse_block()?;
1035        self.consume(&TokenKind::RBrace, "}")?;
1036        Ok(spanned(
1037            Node::GuardStmt {
1038                condition: Box::new(condition),
1039                else_body,
1040            },
1041            Span::merge(start, self.prev_span()),
1042        ))
1043    }
1044
1045    fn parse_require(&mut self) -> Result<SNode, ParserError> {
1046        let start = self.current_span();
1047        self.consume(&TokenKind::Require, "require")?;
1048        let condition = self.parse_expression()?;
1049        let message = if self.check(&TokenKind::Comma) {
1050            self.advance();
1051            Some(Box::new(self.parse_expression()?))
1052        } else {
1053            None
1054        };
1055        Ok(spanned(
1056            Node::RequireStmt {
1057                condition: Box::new(condition),
1058                message,
1059            },
1060            Span::merge(start, self.prev_span()),
1061        ))
1062    }
1063
1064    fn parse_deadline(&mut self) -> Result<SNode, ParserError> {
1065        let start = self.current_span();
1066        self.consume(&TokenKind::Deadline, "deadline")?;
1067        let duration = self.parse_primary()?;
1068        self.consume(&TokenKind::LBrace, "{")?;
1069        let body = self.parse_block()?;
1070        self.consume(&TokenKind::RBrace, "}")?;
1071        Ok(spanned(
1072            Node::DeadlineBlock {
1073                duration: Box::new(duration),
1074                body,
1075            },
1076            Span::merge(start, self.prev_span()),
1077        ))
1078    }
1079
1080    fn parse_yield(&mut self) -> Result<SNode, ParserError> {
1081        let start = self.current_span();
1082        self.consume(&TokenKind::Yield, "yield")?;
1083        if self.is_at_end() || self.check(&TokenKind::Newline) || self.check(&TokenKind::RBrace) {
1084            return Ok(spanned(
1085                Node::YieldExpr { value: None },
1086                Span::merge(start, self.prev_span()),
1087            ));
1088        }
1089        let value = self.parse_expression()?;
1090        Ok(spanned(
1091            Node::YieldExpr {
1092                value: Some(Box::new(value)),
1093            },
1094            Span::merge(start, self.prev_span()),
1095        ))
1096    }
1097
1098    fn parse_mutex(&mut self) -> Result<SNode, ParserError> {
1099        let start = self.current_span();
1100        self.consume(&TokenKind::Mutex, "mutex")?;
1101        self.consume(&TokenKind::LBrace, "{")?;
1102        let body = self.parse_block()?;
1103        self.consume(&TokenKind::RBrace, "}")?;
1104        Ok(spanned(
1105            Node::MutexBlock { body },
1106            Span::merge(start, self.prev_span()),
1107        ))
1108    }
1109
1110    fn parse_ask_expr(&mut self) -> Result<SNode, ParserError> {
1111        let start = self.current_span();
1112        self.consume(&TokenKind::Ask, "ask")?;
1113        self.consume(&TokenKind::LBrace, "{")?;
1114        // Parse as dict entries
1115        let mut entries = Vec::new();
1116        self.skip_newlines();
1117        while !self.is_at_end() && !self.check(&TokenKind::RBrace) {
1118            let key_span = self.current_span();
1119            let name = self.consume_identifier("ask field")?;
1120            let key = spanned(Node::StringLiteral(name), key_span);
1121            self.consume(&TokenKind::Colon, ":")?;
1122            let value = self.parse_expression()?;
1123            entries.push(DictEntry { key, value });
1124            self.skip_newlines();
1125            if self.check(&TokenKind::Comma) {
1126                self.advance();
1127                self.skip_newlines();
1128            }
1129        }
1130        self.consume(&TokenKind::RBrace, "}")?;
1131        Ok(spanned(
1132            Node::AskExpr { fields: entries },
1133            Span::merge(start, self.prev_span()),
1134        ))
1135    }
1136
1137    // --- Expressions (precedence climbing) ---
1138
1139    fn parse_expression_statement(&mut self) -> Result<SNode, ParserError> {
1140        let start = self.current_span();
1141        let expr = self.parse_expression()?;
1142
1143        // Check for assignment or compound assignment on valid targets:
1144        // identifier, property access (obj.field), subscript access (obj[key])
1145        let is_assignable = matches!(
1146            expr.node,
1147            Node::Identifier(_) | Node::PropertyAccess { .. } | Node::SubscriptAccess { .. }
1148        );
1149        if is_assignable {
1150            if self.check(&TokenKind::Assign) {
1151                self.advance();
1152                let value = self.parse_expression()?;
1153                return Ok(spanned(
1154                    Node::Assignment {
1155                        target: Box::new(expr),
1156                        value: Box::new(value),
1157                        op: None,
1158                    },
1159                    Span::merge(start, self.prev_span()),
1160                ));
1161            }
1162            let compound_op = if self.check(&TokenKind::PlusAssign) {
1163                Some("+")
1164            } else if self.check(&TokenKind::MinusAssign) {
1165                Some("-")
1166            } else if self.check(&TokenKind::StarAssign) {
1167                Some("*")
1168            } else if self.check(&TokenKind::SlashAssign) {
1169                Some("/")
1170            } else if self.check(&TokenKind::PercentAssign) {
1171                Some("%")
1172            } else {
1173                None
1174            };
1175            if let Some(op) = compound_op {
1176                self.advance();
1177                let value = self.parse_expression()?;
1178                return Ok(spanned(
1179                    Node::Assignment {
1180                        target: Box::new(expr),
1181                        value: Box::new(value),
1182                        op: Some(op.into()),
1183                    },
1184                    Span::merge(start, self.prev_span()),
1185                ));
1186            }
1187        }
1188
1189        Ok(expr)
1190    }
1191
1192    fn parse_expression(&mut self) -> Result<SNode, ParserError> {
1193        self.skip_newlines();
1194        self.parse_pipe()
1195    }
1196
1197    fn parse_pipe(&mut self) -> Result<SNode, ParserError> {
1198        let mut left = self.parse_range()?;
1199        while self.check_skip_newlines(&TokenKind::Pipe) {
1200            let start = left.span;
1201            self.advance();
1202            let right = self.parse_range()?;
1203            left = spanned(
1204                Node::BinaryOp {
1205                    op: "|>".into(),
1206                    left: Box::new(left),
1207                    right: Box::new(right),
1208                },
1209                Span::merge(start, self.prev_span()),
1210            );
1211        }
1212        Ok(left)
1213    }
1214
1215    fn parse_range(&mut self) -> Result<SNode, ParserError> {
1216        let left = self.parse_ternary()?;
1217        if self.check(&TokenKind::Thru) {
1218            let start = left.span;
1219            self.advance();
1220            let right = self.parse_ternary()?;
1221            return Ok(spanned(
1222                Node::RangeExpr {
1223                    start: Box::new(left),
1224                    end: Box::new(right),
1225                    inclusive: true,
1226                },
1227                Span::merge(start, self.prev_span()),
1228            ));
1229        }
1230        if self.check(&TokenKind::Upto) {
1231            let start = left.span;
1232            self.advance();
1233            let right = self.parse_ternary()?;
1234            return Ok(spanned(
1235                Node::RangeExpr {
1236                    start: Box::new(left),
1237                    end: Box::new(right),
1238                    inclusive: false,
1239                },
1240                Span::merge(start, self.prev_span()),
1241            ));
1242        }
1243        Ok(left)
1244    }
1245
1246    fn parse_ternary(&mut self) -> Result<SNode, ParserError> {
1247        let condition = self.parse_nil_coalescing()?;
1248        if !self.check(&TokenKind::Question) {
1249            return Ok(condition);
1250        }
1251        let start = condition.span;
1252        self.advance(); // skip ?
1253        let true_val = self.parse_nil_coalescing()?;
1254        self.consume(&TokenKind::Colon, ":")?;
1255        let false_val = self.parse_nil_coalescing()?;
1256        Ok(spanned(
1257            Node::Ternary {
1258                condition: Box::new(condition),
1259                true_expr: Box::new(true_val),
1260                false_expr: Box::new(false_val),
1261            },
1262            Span::merge(start, self.prev_span()),
1263        ))
1264    }
1265
1266    fn parse_nil_coalescing(&mut self) -> Result<SNode, ParserError> {
1267        let mut left = self.parse_logical_or()?;
1268        while self.check(&TokenKind::NilCoal) {
1269            let start = left.span;
1270            self.advance();
1271            let right = self.parse_logical_or()?;
1272            left = spanned(
1273                Node::BinaryOp {
1274                    op: "??".into(),
1275                    left: Box::new(left),
1276                    right: Box::new(right),
1277                },
1278                Span::merge(start, self.prev_span()),
1279            );
1280        }
1281        Ok(left)
1282    }
1283
1284    fn parse_logical_or(&mut self) -> Result<SNode, ParserError> {
1285        let mut left = self.parse_logical_and()?;
1286        while self.check_skip_newlines(&TokenKind::Or) {
1287            let start = left.span;
1288            self.advance();
1289            let right = self.parse_logical_and()?;
1290            left = spanned(
1291                Node::BinaryOp {
1292                    op: "||".into(),
1293                    left: Box::new(left),
1294                    right: Box::new(right),
1295                },
1296                Span::merge(start, self.prev_span()),
1297            );
1298        }
1299        Ok(left)
1300    }
1301
1302    fn parse_logical_and(&mut self) -> Result<SNode, ParserError> {
1303        let mut left = self.parse_equality()?;
1304        while self.check_skip_newlines(&TokenKind::And) {
1305            let start = left.span;
1306            self.advance();
1307            let right = self.parse_equality()?;
1308            left = spanned(
1309                Node::BinaryOp {
1310                    op: "&&".into(),
1311                    left: Box::new(left),
1312                    right: Box::new(right),
1313                },
1314                Span::merge(start, self.prev_span()),
1315            );
1316        }
1317        Ok(left)
1318    }
1319
1320    fn parse_equality(&mut self) -> Result<SNode, ParserError> {
1321        let mut left = self.parse_comparison()?;
1322        while self.check(&TokenKind::Eq) || self.check(&TokenKind::Neq) {
1323            let start = left.span;
1324            let op = if self.check(&TokenKind::Eq) {
1325                "=="
1326            } else {
1327                "!="
1328            };
1329            self.advance();
1330            let right = self.parse_comparison()?;
1331            left = spanned(
1332                Node::BinaryOp {
1333                    op: op.into(),
1334                    left: Box::new(left),
1335                    right: Box::new(right),
1336                },
1337                Span::merge(start, self.prev_span()),
1338            );
1339        }
1340        Ok(left)
1341    }
1342
1343    fn parse_comparison(&mut self) -> Result<SNode, ParserError> {
1344        let mut left = self.parse_additive()?;
1345        loop {
1346            if self.check(&TokenKind::Lt)
1347                || self.check(&TokenKind::Gt)
1348                || self.check(&TokenKind::Lte)
1349                || self.check(&TokenKind::Gte)
1350            {
1351                let start = left.span;
1352                let op = match self.current().map(|t| &t.kind) {
1353                    Some(TokenKind::Lt) => "<",
1354                    Some(TokenKind::Gt) => ">",
1355                    Some(TokenKind::Lte) => "<=",
1356                    Some(TokenKind::Gte) => ">=",
1357                    _ => "<",
1358                };
1359                self.advance();
1360                let right = self.parse_additive()?;
1361                left = spanned(
1362                    Node::BinaryOp {
1363                        op: op.into(),
1364                        left: Box::new(left),
1365                        right: Box::new(right),
1366                    },
1367                    Span::merge(start, self.prev_span()),
1368                );
1369            } else if self.check(&TokenKind::In) {
1370                let start = left.span;
1371                self.advance();
1372                let right = self.parse_additive()?;
1373                left = spanned(
1374                    Node::BinaryOp {
1375                        op: "in".into(),
1376                        left: Box::new(left),
1377                        right: Box::new(right),
1378                    },
1379                    Span::merge(start, self.prev_span()),
1380                );
1381            } else if self.check_identifier("not") {
1382                // Look ahead for "not in"
1383                let saved = self.pos;
1384                self.advance(); // consume "not"
1385                if self.check(&TokenKind::In) {
1386                    let start = left.span;
1387                    self.advance(); // consume "in"
1388                    let right = self.parse_additive()?;
1389                    left = spanned(
1390                        Node::BinaryOp {
1391                            op: "not_in".into(),
1392                            left: Box::new(left),
1393                            right: Box::new(right),
1394                        },
1395                        Span::merge(start, self.prev_span()),
1396                    );
1397                } else {
1398                    self.pos = saved;
1399                    break;
1400                }
1401            } else {
1402                break;
1403            }
1404        }
1405        Ok(left)
1406    }
1407
1408    fn parse_additive(&mut self) -> Result<SNode, ParserError> {
1409        let mut left = self.parse_multiplicative()?;
1410        while self.check_skip_newlines(&TokenKind::Plus) || self.check(&TokenKind::Minus) {
1411            let start = left.span;
1412            let op = if self.check(&TokenKind::Plus) {
1413                "+"
1414            } else {
1415                "-"
1416            };
1417            self.advance();
1418            let right = self.parse_multiplicative()?;
1419            left = spanned(
1420                Node::BinaryOp {
1421                    op: op.into(),
1422                    left: Box::new(left),
1423                    right: Box::new(right),
1424                },
1425                Span::merge(start, self.prev_span()),
1426            );
1427        }
1428        Ok(left)
1429    }
1430
1431    fn parse_multiplicative(&mut self) -> Result<SNode, ParserError> {
1432        let mut left = self.parse_unary()?;
1433        while self.check_skip_newlines(&TokenKind::Star)
1434            || self.check_skip_newlines(&TokenKind::Slash)
1435            || self.check_skip_newlines(&TokenKind::Percent)
1436        {
1437            let start = left.span;
1438            let op = if self.check(&TokenKind::Star) {
1439                "*"
1440            } else if self.check(&TokenKind::Slash) {
1441                "/"
1442            } else {
1443                "%"
1444            };
1445            self.advance();
1446            let right = self.parse_unary()?;
1447            left = spanned(
1448                Node::BinaryOp {
1449                    op: op.into(),
1450                    left: Box::new(left),
1451                    right: Box::new(right),
1452                },
1453                Span::merge(start, self.prev_span()),
1454            );
1455        }
1456        Ok(left)
1457    }
1458
1459    fn parse_unary(&mut self) -> Result<SNode, ParserError> {
1460        if self.check(&TokenKind::Not) {
1461            let start = self.current_span();
1462            self.advance();
1463            let operand = self.parse_unary()?;
1464            return Ok(spanned(
1465                Node::UnaryOp {
1466                    op: "!".into(),
1467                    operand: Box::new(operand),
1468                },
1469                Span::merge(start, self.prev_span()),
1470            ));
1471        }
1472        if self.check(&TokenKind::Minus) {
1473            let start = self.current_span();
1474            self.advance();
1475            let operand = self.parse_unary()?;
1476            return Ok(spanned(
1477                Node::UnaryOp {
1478                    op: "-".into(),
1479                    operand: Box::new(operand),
1480                },
1481                Span::merge(start, self.prev_span()),
1482            ));
1483        }
1484        self.parse_postfix()
1485    }
1486
1487    fn parse_postfix(&mut self) -> Result<SNode, ParserError> {
1488        let mut expr = self.parse_primary()?;
1489
1490        loop {
1491            if self.check_skip_newlines(&TokenKind::Dot)
1492                || self.check_skip_newlines(&TokenKind::QuestionDot)
1493            {
1494                let optional = self.check(&TokenKind::QuestionDot);
1495                let start = expr.span;
1496                self.advance();
1497                let member = self.consume_identifier_or_keyword("member name")?;
1498                if self.check(&TokenKind::LParen) {
1499                    self.advance();
1500                    let args = self.parse_arg_list()?;
1501                    self.consume(&TokenKind::RParen, ")")?;
1502                    if optional {
1503                        expr = spanned(
1504                            Node::OptionalMethodCall {
1505                                object: Box::new(expr),
1506                                method: member,
1507                                args,
1508                            },
1509                            Span::merge(start, self.prev_span()),
1510                        );
1511                    } else {
1512                        expr = spanned(
1513                            Node::MethodCall {
1514                                object: Box::new(expr),
1515                                method: member,
1516                                args,
1517                            },
1518                            Span::merge(start, self.prev_span()),
1519                        );
1520                    }
1521                } else if optional {
1522                    expr = spanned(
1523                        Node::OptionalPropertyAccess {
1524                            object: Box::new(expr),
1525                            property: member,
1526                        },
1527                        Span::merge(start, self.prev_span()),
1528                    );
1529                } else {
1530                    expr = spanned(
1531                        Node::PropertyAccess {
1532                            object: Box::new(expr),
1533                            property: member,
1534                        },
1535                        Span::merge(start, self.prev_span()),
1536                    );
1537                }
1538            } else if self.check(&TokenKind::LBracket) {
1539                let start = expr.span;
1540                self.advance();
1541
1542                // Check for slice vs subscript:
1543                // [:end] — slice with no start
1544                // [start:end] or [start:] — slice with start
1545                // [index] — normal subscript
1546                if self.check(&TokenKind::Colon) {
1547                    // [:end] or [:]
1548                    self.advance(); // consume ':'
1549                    let end_expr = if self.check(&TokenKind::RBracket) {
1550                        None
1551                    } else {
1552                        Some(Box::new(self.parse_expression()?))
1553                    };
1554                    self.consume(&TokenKind::RBracket, "]")?;
1555                    expr = spanned(
1556                        Node::SliceAccess {
1557                            object: Box::new(expr),
1558                            start: None,
1559                            end: end_expr,
1560                        },
1561                        Span::merge(start, self.prev_span()),
1562                    );
1563                } else {
1564                    let index = self.parse_expression()?;
1565                    if self.check(&TokenKind::Colon) {
1566                        // [start:end] or [start:]
1567                        self.advance(); // consume ':'
1568                        let end_expr = if self.check(&TokenKind::RBracket) {
1569                            None
1570                        } else {
1571                            Some(Box::new(self.parse_expression()?))
1572                        };
1573                        self.consume(&TokenKind::RBracket, "]")?;
1574                        expr = spanned(
1575                            Node::SliceAccess {
1576                                object: Box::new(expr),
1577                                start: Some(Box::new(index)),
1578                                end: end_expr,
1579                            },
1580                            Span::merge(start, self.prev_span()),
1581                        );
1582                    } else {
1583                        self.consume(&TokenKind::RBracket, "]")?;
1584                        expr = spanned(
1585                            Node::SubscriptAccess {
1586                                object: Box::new(expr),
1587                                index: Box::new(index),
1588                            },
1589                            Span::merge(start, self.prev_span()),
1590                        );
1591                    }
1592                }
1593            } else if self.check(&TokenKind::LParen) && matches!(expr.node, Node::Identifier(_)) {
1594                let start = expr.span;
1595                self.advance();
1596                let args = self.parse_arg_list()?;
1597                self.consume(&TokenKind::RParen, ")")?;
1598                if let Node::Identifier(name) = expr.node {
1599                    expr = spanned(
1600                        Node::FunctionCall { name, args },
1601                        Span::merge(start, self.prev_span()),
1602                    );
1603                }
1604            } else if self.check(&TokenKind::Question) {
1605                // Distinguish postfix try operator (expr?) from ternary (expr ? a : b).
1606                // If the token after ? could start a ternary branch, leave it for parse_ternary.
1607                let next_pos = self.pos + 1;
1608                let is_ternary = self.tokens.get(next_pos).is_some_and(|t| {
1609                    matches!(
1610                        t.kind,
1611                        TokenKind::Identifier(_)
1612                            | TokenKind::IntLiteral(_)
1613                            | TokenKind::FloatLiteral(_)
1614                            | TokenKind::StringLiteral(_)
1615                            | TokenKind::InterpolatedString(_)
1616                            | TokenKind::True
1617                            | TokenKind::False
1618                            | TokenKind::Nil
1619                            | TokenKind::LParen
1620                            | TokenKind::LBracket
1621                            | TokenKind::LBrace
1622                            | TokenKind::Not
1623                            | TokenKind::Minus
1624                            | TokenKind::Fn
1625                    )
1626                });
1627                if is_ternary {
1628                    break;
1629                }
1630                let start = expr.span;
1631                self.advance(); // consume ?
1632                expr = spanned(
1633                    Node::TryOperator {
1634                        operand: Box::new(expr),
1635                    },
1636                    Span::merge(start, self.prev_span()),
1637                );
1638            } else {
1639                break;
1640            }
1641        }
1642
1643        Ok(expr)
1644    }
1645
1646    fn parse_primary(&mut self) -> Result<SNode, ParserError> {
1647        let tok = self.current().ok_or_else(|| ParserError::UnexpectedEof {
1648            expected: "expression".into(),
1649        })?;
1650        let start = self.current_span();
1651
1652        match &tok.kind {
1653            TokenKind::StringLiteral(s) => {
1654                let s = s.clone();
1655                self.advance();
1656                Ok(spanned(
1657                    Node::StringLiteral(s),
1658                    Span::merge(start, self.prev_span()),
1659                ))
1660            }
1661            TokenKind::InterpolatedString(segments) => {
1662                let segments = segments.clone();
1663                self.advance();
1664                Ok(spanned(
1665                    Node::InterpolatedString(segments),
1666                    Span::merge(start, self.prev_span()),
1667                ))
1668            }
1669            TokenKind::IntLiteral(n) => {
1670                let n = *n;
1671                self.advance();
1672                Ok(spanned(
1673                    Node::IntLiteral(n),
1674                    Span::merge(start, self.prev_span()),
1675                ))
1676            }
1677            TokenKind::FloatLiteral(n) => {
1678                let n = *n;
1679                self.advance();
1680                Ok(spanned(
1681                    Node::FloatLiteral(n),
1682                    Span::merge(start, self.prev_span()),
1683                ))
1684            }
1685            TokenKind::True => {
1686                self.advance();
1687                Ok(spanned(
1688                    Node::BoolLiteral(true),
1689                    Span::merge(start, self.prev_span()),
1690                ))
1691            }
1692            TokenKind::False => {
1693                self.advance();
1694                Ok(spanned(
1695                    Node::BoolLiteral(false),
1696                    Span::merge(start, self.prev_span()),
1697                ))
1698            }
1699            TokenKind::Nil => {
1700                self.advance();
1701                Ok(spanned(
1702                    Node::NilLiteral,
1703                    Span::merge(start, self.prev_span()),
1704                ))
1705            }
1706            TokenKind::Identifier(name) => {
1707                let name = name.clone();
1708                self.advance();
1709                Ok(spanned(
1710                    Node::Identifier(name),
1711                    Span::merge(start, self.prev_span()),
1712                ))
1713            }
1714            TokenKind::LParen => {
1715                self.advance();
1716                let expr = self.parse_expression()?;
1717                self.consume(&TokenKind::RParen, ")")?;
1718                Ok(expr)
1719            }
1720            TokenKind::LBracket => self.parse_list_literal(),
1721            TokenKind::LBrace => self.parse_dict_or_closure(),
1722            TokenKind::Parallel => self.parse_parallel(),
1723            TokenKind::ParallelMap => self.parse_parallel_map(),
1724            TokenKind::ParallelSettle => self.parse_parallel_settle(),
1725            TokenKind::Retry => self.parse_retry(),
1726            TokenKind::If => self.parse_if_else(),
1727            TokenKind::Spawn => self.parse_spawn_expr(),
1728            TokenKind::DurationLiteral(ms) => {
1729                let ms = *ms;
1730                self.advance();
1731                Ok(spanned(
1732                    Node::DurationLiteral(ms),
1733                    Span::merge(start, self.prev_span()),
1734                ))
1735            }
1736            TokenKind::Ask => self.parse_ask_expr(),
1737            TokenKind::Deadline => self.parse_deadline(),
1738            TokenKind::Try => self.parse_try_catch(),
1739            TokenKind::Fn => self.parse_fn_expr(),
1740            _ => Err(self.error("expression")),
1741        }
1742    }
1743
1744    /// Parse an anonymous function expression: `fn(params) { body }`
1745    /// Produces a Closure node with `fn_syntax: true` so the formatter
1746    /// can round-trip the original syntax.
1747    fn parse_fn_expr(&mut self) -> Result<SNode, ParserError> {
1748        let start = self.current_span();
1749        self.consume(&TokenKind::Fn, "fn")?;
1750        self.consume(&TokenKind::LParen, "(")?;
1751        let params = self.parse_typed_param_list()?;
1752        self.consume(&TokenKind::RParen, ")")?;
1753        self.consume(&TokenKind::LBrace, "{")?;
1754        let body = self.parse_block()?;
1755        self.consume(&TokenKind::RBrace, "}")?;
1756        Ok(spanned(
1757            Node::Closure {
1758                params,
1759                body,
1760                fn_syntax: true,
1761            },
1762            Span::merge(start, self.prev_span()),
1763        ))
1764    }
1765
1766    fn parse_spawn_expr(&mut self) -> Result<SNode, ParserError> {
1767        let start = self.current_span();
1768        self.consume(&TokenKind::Spawn, "spawn")?;
1769        self.consume(&TokenKind::LBrace, "{")?;
1770        let body = self.parse_block()?;
1771        self.consume(&TokenKind::RBrace, "}")?;
1772        Ok(spanned(
1773            Node::SpawnExpr { body },
1774            Span::merge(start, self.prev_span()),
1775        ))
1776    }
1777
1778    fn parse_list_literal(&mut self) -> Result<SNode, ParserError> {
1779        let start = self.current_span();
1780        self.consume(&TokenKind::LBracket, "[")?;
1781        let mut elements = Vec::new();
1782        self.skip_newlines();
1783
1784        while !self.is_at_end() && !self.check(&TokenKind::RBracket) {
1785            // Check for spread: ...expr
1786            if self.check(&TokenKind::Dot) {
1787                let saved_pos = self.pos;
1788                self.advance(); // first .
1789                if self.check(&TokenKind::Dot) {
1790                    self.advance(); // second .
1791                    self.consume(&TokenKind::Dot, ".")?; // third .
1792                    let spread_start = self.tokens[saved_pos].span;
1793                    let expr = self.parse_expression()?;
1794                    elements.push(spanned(
1795                        Node::Spread(Box::new(expr)),
1796                        Span::merge(spread_start, self.prev_span()),
1797                    ));
1798                } else {
1799                    // Not a spread, restore and parse as expression
1800                    self.pos = saved_pos;
1801                    elements.push(self.parse_expression()?);
1802                }
1803            } else {
1804                elements.push(self.parse_expression()?);
1805            }
1806            self.skip_newlines();
1807            if self.check(&TokenKind::Comma) {
1808                self.advance();
1809                self.skip_newlines();
1810            }
1811        }
1812
1813        self.consume(&TokenKind::RBracket, "]")?;
1814        Ok(spanned(
1815            Node::ListLiteral(elements),
1816            Span::merge(start, self.prev_span()),
1817        ))
1818    }
1819
1820    fn parse_dict_or_closure(&mut self) -> Result<SNode, ParserError> {
1821        let start = self.current_span();
1822        self.consume(&TokenKind::LBrace, "{")?;
1823        self.skip_newlines();
1824
1825        // Empty dict
1826        if self.check(&TokenKind::RBrace) {
1827            self.advance();
1828            return Ok(spanned(
1829                Node::DictLiteral(Vec::new()),
1830                Span::merge(start, self.prev_span()),
1831            ));
1832        }
1833
1834        // Lookahead: scan for -> before } to disambiguate closure from dict.
1835        let saved = self.pos;
1836        if self.is_closure_lookahead() {
1837            self.pos = saved;
1838            return self.parse_closure_body(start);
1839        }
1840        self.pos = saved;
1841        self.parse_dict_literal(start)
1842    }
1843
1844    /// Scan forward to determine if this is a closure (has -> before matching }).
1845    /// Does not consume tokens (caller saves/restores pos).
1846    fn is_closure_lookahead(&mut self) -> bool {
1847        let mut depth = 0;
1848        while !self.is_at_end() {
1849            if let Some(tok) = self.current() {
1850                match &tok.kind {
1851                    TokenKind::Arrow if depth == 0 => return true,
1852                    TokenKind::LBrace | TokenKind::LParen | TokenKind::LBracket => depth += 1,
1853                    TokenKind::RBrace if depth == 0 => return false,
1854                    TokenKind::RBrace => depth -= 1,
1855                    TokenKind::RParen | TokenKind::RBracket => {
1856                        if depth > 0 {
1857                            depth -= 1;
1858                        }
1859                    }
1860                    _ => {}
1861                }
1862                self.advance();
1863            } else {
1864                return false;
1865            }
1866        }
1867        false
1868    }
1869
1870    /// Parse closure params and body (after opening { has been consumed).
1871    fn parse_closure_body(&mut self, start: Span) -> Result<SNode, ParserError> {
1872        let params = self.parse_typed_param_list_until_arrow()?;
1873        self.consume(&TokenKind::Arrow, "->")?;
1874        let body = self.parse_block()?;
1875        self.consume(&TokenKind::RBrace, "}")?;
1876        Ok(spanned(
1877            Node::Closure {
1878                params,
1879                body,
1880                fn_syntax: false,
1881            },
1882            Span::merge(start, self.prev_span()),
1883        ))
1884    }
1885
1886    /// Parse typed params until we see ->. Handles: `x`, `x: int`, `x, y`, `x: int, y: string`.
1887    fn parse_typed_param_list_until_arrow(&mut self) -> Result<Vec<TypedParam>, ParserError> {
1888        self.parse_typed_params_until(|tok| tok == &TokenKind::Arrow)
1889    }
1890
1891    fn parse_dict_literal(&mut self, start: Span) -> Result<SNode, ParserError> {
1892        let mut entries = Vec::new();
1893        self.skip_newlines();
1894
1895        while !self.is_at_end() && !self.check(&TokenKind::RBrace) {
1896            // Check for spread: ...expr
1897            if self.check(&TokenKind::Dot) {
1898                let saved_pos = self.pos;
1899                self.advance(); // first .
1900                if self.check(&TokenKind::Dot) {
1901                    self.advance(); // second .
1902                    if self.check(&TokenKind::Dot) {
1903                        self.advance(); // third .
1904                        let spread_start = self.tokens[saved_pos].span;
1905                        let expr = self.parse_expression()?;
1906                        entries.push(DictEntry {
1907                            key: spanned(Node::NilLiteral, spread_start),
1908                            value: spanned(
1909                                Node::Spread(Box::new(expr)),
1910                                Span::merge(spread_start, self.prev_span()),
1911                            ),
1912                        });
1913                        self.skip_newlines();
1914                        if self.check(&TokenKind::Comma) {
1915                            self.advance();
1916                            self.skip_newlines();
1917                        }
1918                        continue;
1919                    }
1920                    // Not three dots — restore
1921                    self.pos = saved_pos;
1922                } else {
1923                    self.pos = saved_pos;
1924                }
1925            }
1926            let key = if self.check(&TokenKind::LBracket) {
1927                // Computed key: [expression]
1928                self.advance();
1929                let k = self.parse_expression()?;
1930                self.consume(&TokenKind::RBracket, "]")?;
1931                k
1932            } else if matches!(
1933                self.current().map(|t| &t.kind),
1934                Some(TokenKind::StringLiteral(_))
1935            ) {
1936                // Quoted string key: {"key": value}
1937                let key_span = self.current_span();
1938                let name =
1939                    if let Some(TokenKind::StringLiteral(s)) = self.current().map(|t| &t.kind) {
1940                        s.clone()
1941                    } else {
1942                        unreachable!()
1943                    };
1944                self.advance();
1945                spanned(Node::StringLiteral(name), key_span)
1946            } else {
1947                // Static key: identifier or keyword -> string literal
1948                let key_span = self.current_span();
1949                let name = self.consume_identifier_or_keyword("dict key")?;
1950                spanned(Node::StringLiteral(name), key_span)
1951            };
1952            self.consume(&TokenKind::Colon, ":")?;
1953            let value = self.parse_expression()?;
1954            entries.push(DictEntry { key, value });
1955            self.skip_newlines();
1956            if self.check(&TokenKind::Comma) {
1957                self.advance();
1958                self.skip_newlines();
1959            }
1960        }
1961
1962        self.consume(&TokenKind::RBrace, "}")?;
1963        Ok(spanned(
1964            Node::DictLiteral(entries),
1965            Span::merge(start, self.prev_span()),
1966        ))
1967    }
1968
1969    // --- Helpers ---
1970
1971    /// Parse untyped parameter list (for pipelines, overrides).
1972    fn parse_param_list(&mut self) -> Result<Vec<String>, ParserError> {
1973        let mut params = Vec::new();
1974        self.skip_newlines();
1975
1976        while !self.is_at_end() && !self.check(&TokenKind::RParen) {
1977            params.push(self.consume_identifier("parameter name")?);
1978            if self.check(&TokenKind::Comma) {
1979                self.advance();
1980                self.skip_newlines();
1981            }
1982        }
1983        Ok(params)
1984    }
1985
1986    /// Parse typed parameter list (for fn declarations).
1987    fn parse_typed_param_list(&mut self) -> Result<Vec<TypedParam>, ParserError> {
1988        self.parse_typed_params_until(|tok| tok == &TokenKind::RParen)
1989    }
1990
1991    /// Shared implementation: parse typed params with optional defaults until
1992    /// a terminator token is reached.
1993    fn parse_typed_params_until(
1994        &mut self,
1995        is_terminator: impl Fn(&TokenKind) -> bool,
1996    ) -> Result<Vec<TypedParam>, ParserError> {
1997        let mut params = Vec::new();
1998        let mut seen_default = false;
1999        self.skip_newlines();
2000
2001        while !self.is_at_end() {
2002            if let Some(tok) = self.current() {
2003                if is_terminator(&tok.kind) {
2004                    break;
2005                }
2006            } else {
2007                break;
2008            }
2009            let name = self.consume_identifier("parameter name")?;
2010            let type_expr = self.try_parse_type_annotation()?;
2011            let default_value = if self.check(&TokenKind::Assign) {
2012                self.advance();
2013                seen_default = true;
2014                Some(Box::new(self.parse_expression()?))
2015            } else {
2016                if seen_default {
2017                    return Err(self.error(
2018                        "Required parameter cannot follow a parameter with a default value",
2019                    ));
2020                }
2021                None
2022            };
2023            params.push(TypedParam {
2024                name,
2025                type_expr,
2026                default_value,
2027            });
2028            if self.check(&TokenKind::Comma) {
2029                self.advance();
2030                self.skip_newlines();
2031            }
2032        }
2033        Ok(params)
2034    }
2035
2036    /// Parse a comma-separated list of type parameter names until `>`.
2037    fn parse_type_param_list(&mut self) -> Result<Vec<TypeParam>, ParserError> {
2038        let mut params = Vec::new();
2039        self.skip_newlines();
2040        while !self.is_at_end() && !self.check(&TokenKind::Gt) {
2041            let name = self.consume_identifier("type parameter name")?;
2042            params.push(TypeParam { name });
2043            if self.check(&TokenKind::Comma) {
2044                self.advance();
2045                self.skip_newlines();
2046            }
2047        }
2048        self.consume(&TokenKind::Gt, ">")?;
2049        Ok(params)
2050    }
2051
2052    /// Parse an optional `where T: bound, U: bound` clause.
2053    /// Looks for an identifier "where" before `{`.
2054    fn parse_where_clauses(&mut self) -> Result<Vec<WhereClause>, ParserError> {
2055        // Check if the next identifier is "where"
2056        if let Some(tok) = self.current() {
2057            if let TokenKind::Identifier(ref id) = tok.kind {
2058                if id == "where" {
2059                    self.advance(); // skip "where"
2060                    let mut clauses = Vec::new();
2061                    loop {
2062                        self.skip_newlines();
2063                        // Stop if we hit `{` or EOF
2064                        if self.check(&TokenKind::LBrace) || self.is_at_end() {
2065                            break;
2066                        }
2067                        let type_name = self.consume_identifier("type parameter name")?;
2068                        self.consume(&TokenKind::Colon, ":")?;
2069                        let bound = self.consume_identifier("type bound")?;
2070                        clauses.push(WhereClause { type_name, bound });
2071                        if self.check(&TokenKind::Comma) {
2072                            self.advance();
2073                        } else {
2074                            break;
2075                        }
2076                    }
2077                    return Ok(clauses);
2078                }
2079            }
2080        }
2081        Ok(Vec::new())
2082    }
2083
2084    /// Try to parse an optional type annotation (`: type`).
2085    /// Returns None if no colon follows.
2086    fn try_parse_type_annotation(&mut self) -> Result<Option<TypeExpr>, ParserError> {
2087        if !self.check(&TokenKind::Colon) {
2088            return Ok(None);
2089        }
2090        self.advance(); // skip :
2091        Ok(Some(self.parse_type_expr()?))
2092    }
2093
2094    /// Parse a type expression: `int`, `string | nil`, `{name: string, age?: int}`.
2095    fn parse_type_expr(&mut self) -> Result<TypeExpr, ParserError> {
2096        self.skip_newlines();
2097        let first = self.parse_type_primary()?;
2098
2099        // Check for union: type | type | ...
2100        if self.check(&TokenKind::Bar) {
2101            let mut types = vec![first];
2102            while self.check(&TokenKind::Bar) {
2103                self.advance(); // skip |
2104                types.push(self.parse_type_primary()?);
2105            }
2106            return Ok(TypeExpr::Union(types));
2107        }
2108
2109        Ok(first)
2110    }
2111
2112    /// Parse a primary type: named type or shape type.
2113    /// Accepts identifiers and certain keywords (nil, bool, etc.) as type names.
2114    fn parse_type_primary(&mut self) -> Result<TypeExpr, ParserError> {
2115        self.skip_newlines();
2116        if self.check(&TokenKind::LBrace) {
2117            return self.parse_shape_type();
2118        }
2119        // Accept keyword type names: nil, true, false map to their type names
2120        if let Some(tok) = self.current() {
2121            let type_name = match &tok.kind {
2122                TokenKind::Nil => {
2123                    self.advance();
2124                    return Ok(TypeExpr::Named("nil".to_string()));
2125                }
2126                TokenKind::True | TokenKind::False => {
2127                    self.advance();
2128                    return Ok(TypeExpr::Named("bool".to_string()));
2129                }
2130                _ => None,
2131            };
2132            if let Some(name) = type_name {
2133                return Ok(TypeExpr::Named(name));
2134            }
2135        }
2136        // Function type: fn(T, U) -> R
2137        if self.check(&TokenKind::Fn) {
2138            self.advance(); // skip `fn`
2139            self.consume(&TokenKind::LParen, "(")?;
2140            let mut params = Vec::new();
2141            self.skip_newlines();
2142            while !self.is_at_end() && !self.check(&TokenKind::RParen) {
2143                params.push(self.parse_type_expr()?);
2144                self.skip_newlines();
2145                if self.check(&TokenKind::Comma) {
2146                    self.advance();
2147                    self.skip_newlines();
2148                }
2149            }
2150            self.consume(&TokenKind::RParen, ")")?;
2151            self.consume(&TokenKind::Arrow, "->")?;
2152            let return_type = self.parse_type_expr()?;
2153            return Ok(TypeExpr::FnType {
2154                params,
2155                return_type: Box::new(return_type),
2156            });
2157        }
2158        let name = self.consume_identifier("type name")?;
2159        // Check for generic type parameters: list<int>, dict<string, int>
2160        if self.check(&TokenKind::Lt) {
2161            self.advance(); // skip <
2162            let first_param = self.parse_type_expr()?;
2163            if name == "list" {
2164                self.consume(&TokenKind::Gt, ">")?;
2165                return Ok(TypeExpr::List(Box::new(first_param)));
2166            } else if name == "dict" {
2167                self.consume(&TokenKind::Comma, ",")?;
2168                let second_param = self.parse_type_expr()?;
2169                self.consume(&TokenKind::Gt, ">")?;
2170                return Ok(TypeExpr::DictType(
2171                    Box::new(first_param),
2172                    Box::new(second_param),
2173                ));
2174            }
2175            // Unknown generic — just consume > and treat as Named
2176            self.consume(&TokenKind::Gt, ">")?;
2177        }
2178        Ok(TypeExpr::Named(name))
2179    }
2180
2181    /// Parse a shape type: `{ name: string, age: int, active?: bool }`.
2182    fn parse_shape_type(&mut self) -> Result<TypeExpr, ParserError> {
2183        self.consume(&TokenKind::LBrace, "{")?;
2184        let mut fields = Vec::new();
2185        self.skip_newlines();
2186
2187        while !self.is_at_end() && !self.check(&TokenKind::RBrace) {
2188            let name = self.consume_identifier("field name")?;
2189            let optional = if self.check(&TokenKind::Question) {
2190                self.advance();
2191                true
2192            } else {
2193                false
2194            };
2195            self.consume(&TokenKind::Colon, ":")?;
2196            let type_expr = self.parse_type_expr()?;
2197            fields.push(ShapeField {
2198                name,
2199                type_expr,
2200                optional,
2201            });
2202            self.skip_newlines();
2203            if self.check(&TokenKind::Comma) {
2204                self.advance();
2205                self.skip_newlines();
2206            }
2207        }
2208
2209        self.consume(&TokenKind::RBrace, "}")?;
2210        Ok(TypeExpr::Shape(fields))
2211    }
2212
2213    fn parse_arg_list(&mut self) -> Result<Vec<SNode>, ParserError> {
2214        let mut args = Vec::new();
2215        self.skip_newlines();
2216
2217        while !self.is_at_end() && !self.check(&TokenKind::RParen) {
2218            // Check for spread: ...expr
2219            if self.check(&TokenKind::Dot) {
2220                let saved_pos = self.pos;
2221                self.advance(); // first .
2222                if self.check(&TokenKind::Dot) {
2223                    self.advance(); // second .
2224                    self.consume(&TokenKind::Dot, ".")?; // third .
2225                    let spread_start = self.tokens[saved_pos].span;
2226                    let expr = self.parse_expression()?;
2227                    args.push(spanned(
2228                        Node::Spread(Box::new(expr)),
2229                        Span::merge(spread_start, self.prev_span()),
2230                    ));
2231                } else {
2232                    // Not a spread, restore and parse as expression
2233                    self.pos = saved_pos;
2234                    args.push(self.parse_expression()?);
2235                }
2236            } else {
2237                args.push(self.parse_expression()?);
2238            }
2239            self.skip_newlines();
2240            if self.check(&TokenKind::Comma) {
2241                self.advance();
2242                self.skip_newlines();
2243            }
2244        }
2245        Ok(args)
2246    }
2247
2248    fn is_at_end(&self) -> bool {
2249        self.pos >= self.tokens.len()
2250            || matches!(self.tokens.get(self.pos), Some(t) if t.kind == TokenKind::Eof)
2251    }
2252
2253    fn current(&self) -> Option<&Token> {
2254        self.tokens.get(self.pos)
2255    }
2256
2257    fn peek_kind(&self) -> Option<&TokenKind> {
2258        self.tokens.get(self.pos + 1).map(|t| &t.kind)
2259    }
2260
2261    fn check(&self, kind: &TokenKind) -> bool {
2262        self.current()
2263            .map(|t| std::mem::discriminant(&t.kind) == std::mem::discriminant(kind))
2264            .unwrap_or(false)
2265    }
2266
2267    /// Check for a token kind, skipping past any newlines first.
2268    /// Used for binary operators like `||` and `&&` that can span lines.
2269    fn check_skip_newlines(&mut self, kind: &TokenKind) -> bool {
2270        let saved = self.pos;
2271        self.skip_newlines();
2272        if self.check(kind) {
2273            true
2274        } else {
2275            self.pos = saved;
2276            false
2277        }
2278    }
2279
2280    /// Check if current token is an identifier with the given name (without consuming it).
2281    fn check_identifier(&self, name: &str) -> bool {
2282        matches!(self.current().map(|t| &t.kind), Some(TokenKind::Identifier(s)) if s == name)
2283    }
2284
2285    fn advance(&mut self) {
2286        if self.pos < self.tokens.len() {
2287            self.pos += 1;
2288        }
2289    }
2290
2291    fn consume(&mut self, kind: &TokenKind, expected: &str) -> Result<Token, ParserError> {
2292        self.skip_newlines();
2293        let tok = self.current().ok_or_else(|| self.make_error(expected))?;
2294        if std::mem::discriminant(&tok.kind) != std::mem::discriminant(kind) {
2295            return Err(self.make_error(expected));
2296        }
2297        let tok = tok.clone();
2298        self.advance();
2299        Ok(tok)
2300    }
2301
2302    fn consume_identifier(&mut self, expected: &str) -> Result<String, ParserError> {
2303        self.skip_newlines();
2304        let tok = self.current().ok_or_else(|| self.make_error(expected))?;
2305        if let TokenKind::Identifier(name) = &tok.kind {
2306            let name = name.clone();
2307            self.advance();
2308            Ok(name)
2309        } else {
2310            Err(self.make_error(expected))
2311        }
2312    }
2313
2314    /// Like `consume_identifier`, but also accepts keywords as identifiers.
2315    /// Used for property access (e.g., `obj.type`) and dict keys where
2316    /// keywords are valid member names.
2317    fn consume_identifier_or_keyword(&mut self, expected: &str) -> Result<String, ParserError> {
2318        self.skip_newlines();
2319        let tok = self.current().ok_or_else(|| self.make_error(expected))?;
2320        if let TokenKind::Identifier(name) = &tok.kind {
2321            let name = name.clone();
2322            self.advance();
2323            return Ok(name);
2324        }
2325        // Accept any keyword token as an identifier
2326        let name = match &tok.kind {
2327            TokenKind::Pipeline => "pipeline",
2328            TokenKind::Extends => "extends",
2329            TokenKind::Override => "override",
2330            TokenKind::Let => "let",
2331            TokenKind::Var => "var",
2332            TokenKind::If => "if",
2333            TokenKind::Else => "else",
2334            TokenKind::For => "for",
2335            TokenKind::In => "in",
2336            TokenKind::Match => "match",
2337            TokenKind::Retry => "retry",
2338            TokenKind::Parallel => "parallel",
2339            TokenKind::ParallelMap => "parallel_map",
2340            TokenKind::ParallelSettle => "parallel_settle",
2341            TokenKind::Return => "return",
2342            TokenKind::Import => "import",
2343            TokenKind::True => "true",
2344            TokenKind::False => "false",
2345            TokenKind::Nil => "nil",
2346            TokenKind::Try => "try",
2347            TokenKind::Catch => "catch",
2348            TokenKind::Throw => "throw",
2349            TokenKind::Fn => "fn",
2350            TokenKind::Spawn => "spawn",
2351            TokenKind::While => "while",
2352            TokenKind::TypeKw => "type",
2353            TokenKind::Enum => "enum",
2354            TokenKind::Struct => "struct",
2355            TokenKind::Interface => "interface",
2356            TokenKind::Pub => "pub",
2357            TokenKind::From => "from",
2358            TokenKind::Thru => "thru",
2359            TokenKind::Upto => "upto",
2360            TokenKind::Guard => "guard",
2361            TokenKind::Ask => "ask",
2362            TokenKind::Deadline => "deadline",
2363            TokenKind::Yield => "yield",
2364            TokenKind::Mutex => "mutex",
2365            TokenKind::Break => "break",
2366            TokenKind::Continue => "continue",
2367            TokenKind::Impl => "impl",
2368            _ => return Err(self.make_error(expected)),
2369        };
2370        let name = name.to_string();
2371        self.advance();
2372        Ok(name)
2373    }
2374
2375    fn skip_newlines(&mut self) {
2376        while self.pos < self.tokens.len() && self.tokens[self.pos].kind == TokenKind::Newline {
2377            self.pos += 1;
2378        }
2379    }
2380
2381    fn make_error(&self, expected: &str) -> ParserError {
2382        if let Some(tok) = self.tokens.get(self.pos) {
2383            if tok.kind == TokenKind::Eof {
2384                return ParserError::UnexpectedEof {
2385                    expected: expected.into(),
2386                };
2387            }
2388            ParserError::Unexpected {
2389                got: tok.kind.to_string(),
2390                expected: expected.into(),
2391                span: tok.span,
2392            }
2393        } else {
2394            ParserError::UnexpectedEof {
2395                expected: expected.into(),
2396            }
2397        }
2398    }
2399
2400    fn error(&self, expected: &str) -> ParserError {
2401        self.make_error(expected)
2402    }
2403}