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