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