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            _ => Err(self.error("expression")),
1680        }
1681    }
1682
1683    fn parse_spawn_expr(&mut self) -> Result<SNode, ParserError> {
1684        let start = self.current_span();
1685        self.consume(&TokenKind::Spawn, "spawn")?;
1686        self.consume(&TokenKind::LBrace, "{")?;
1687        let body = self.parse_block()?;
1688        self.consume(&TokenKind::RBrace, "}")?;
1689        Ok(spanned(
1690            Node::SpawnExpr { body },
1691            Span::merge(start, self.prev_span()),
1692        ))
1693    }
1694
1695    fn parse_list_literal(&mut self) -> Result<SNode, ParserError> {
1696        let start = self.current_span();
1697        self.consume(&TokenKind::LBracket, "[")?;
1698        let mut elements = Vec::new();
1699        self.skip_newlines();
1700
1701        while !self.is_at_end() && !self.check(&TokenKind::RBracket) {
1702            // Check for spread: ...expr
1703            if self.check(&TokenKind::Dot) {
1704                let saved_pos = self.pos;
1705                self.advance(); // first .
1706                if self.check(&TokenKind::Dot) {
1707                    self.advance(); // second .
1708                    self.consume(&TokenKind::Dot, ".")?; // third .
1709                    let spread_start = self.tokens[saved_pos].span;
1710                    let expr = self.parse_expression()?;
1711                    elements.push(spanned(
1712                        Node::Spread(Box::new(expr)),
1713                        Span::merge(spread_start, self.prev_span()),
1714                    ));
1715                } else {
1716                    // Not a spread, restore and parse as expression
1717                    self.pos = saved_pos;
1718                    elements.push(self.parse_expression()?);
1719                }
1720            } else {
1721                elements.push(self.parse_expression()?);
1722            }
1723            self.skip_newlines();
1724            if self.check(&TokenKind::Comma) {
1725                self.advance();
1726                self.skip_newlines();
1727            }
1728        }
1729
1730        self.consume(&TokenKind::RBracket, "]")?;
1731        Ok(spanned(
1732            Node::ListLiteral(elements),
1733            Span::merge(start, self.prev_span()),
1734        ))
1735    }
1736
1737    fn parse_dict_or_closure(&mut self) -> Result<SNode, ParserError> {
1738        let start = self.current_span();
1739        self.consume(&TokenKind::LBrace, "{")?;
1740        self.skip_newlines();
1741
1742        // Empty dict
1743        if self.check(&TokenKind::RBrace) {
1744            self.advance();
1745            return Ok(spanned(
1746                Node::DictLiteral(Vec::new()),
1747                Span::merge(start, self.prev_span()),
1748            ));
1749        }
1750
1751        // Lookahead: scan for -> before } to disambiguate closure from dict.
1752        let saved = self.pos;
1753        if self.is_closure_lookahead() {
1754            self.pos = saved;
1755            return self.parse_closure_body(start);
1756        }
1757        self.pos = saved;
1758        self.parse_dict_literal(start)
1759    }
1760
1761    /// Scan forward to determine if this is a closure (has -> before matching }).
1762    /// Does not consume tokens (caller saves/restores pos).
1763    fn is_closure_lookahead(&mut self) -> bool {
1764        let mut depth = 0;
1765        while !self.is_at_end() {
1766            if let Some(tok) = self.current() {
1767                match &tok.kind {
1768                    TokenKind::Arrow if depth == 0 => return true,
1769                    TokenKind::LBrace | TokenKind::LParen | TokenKind::LBracket => depth += 1,
1770                    TokenKind::RBrace if depth == 0 => return false,
1771                    TokenKind::RBrace => depth -= 1,
1772                    TokenKind::RParen | TokenKind::RBracket => {
1773                        if depth > 0 {
1774                            depth -= 1;
1775                        }
1776                    }
1777                    _ => {}
1778                }
1779                self.advance();
1780            } else {
1781                return false;
1782            }
1783        }
1784        false
1785    }
1786
1787    /// Parse closure params and body (after opening { has been consumed).
1788    fn parse_closure_body(&mut self, start: Span) -> Result<SNode, ParserError> {
1789        let params = self.parse_typed_param_list_until_arrow()?;
1790        self.consume(&TokenKind::Arrow, "->")?;
1791        let body = self.parse_block()?;
1792        self.consume(&TokenKind::RBrace, "}")?;
1793        Ok(spanned(
1794            Node::Closure { params, body },
1795            Span::merge(start, self.prev_span()),
1796        ))
1797    }
1798
1799    /// Parse typed params until we see ->. Handles: `x`, `x: int`, `x, y`, `x: int, y: string`.
1800    fn parse_typed_param_list_until_arrow(&mut self) -> Result<Vec<TypedParam>, ParserError> {
1801        self.parse_typed_params_until(|tok| tok == &TokenKind::Arrow)
1802    }
1803
1804    fn parse_dict_literal(&mut self, start: Span) -> Result<SNode, ParserError> {
1805        let mut entries = Vec::new();
1806        self.skip_newlines();
1807
1808        while !self.is_at_end() && !self.check(&TokenKind::RBrace) {
1809            // Check for spread: ...expr
1810            if self.check(&TokenKind::Dot) {
1811                let saved_pos = self.pos;
1812                self.advance(); // first .
1813                if self.check(&TokenKind::Dot) {
1814                    self.advance(); // second .
1815                    if self.check(&TokenKind::Dot) {
1816                        self.advance(); // third .
1817                        let spread_start = self.tokens[saved_pos].span;
1818                        let expr = self.parse_expression()?;
1819                        entries.push(DictEntry {
1820                            key: spanned(Node::NilLiteral, spread_start),
1821                            value: spanned(
1822                                Node::Spread(Box::new(expr)),
1823                                Span::merge(spread_start, self.prev_span()),
1824                            ),
1825                        });
1826                        self.skip_newlines();
1827                        if self.check(&TokenKind::Comma) {
1828                            self.advance();
1829                            self.skip_newlines();
1830                        }
1831                        continue;
1832                    }
1833                    // Not three dots — restore
1834                    self.pos = saved_pos;
1835                } else {
1836                    self.pos = saved_pos;
1837                }
1838            }
1839            let key = if self.check(&TokenKind::LBracket) {
1840                // Computed key: [expression]
1841                self.advance();
1842                let k = self.parse_expression()?;
1843                self.consume(&TokenKind::RBracket, "]")?;
1844                k
1845            } else if matches!(
1846                self.current().map(|t| &t.kind),
1847                Some(TokenKind::StringLiteral(_))
1848            ) {
1849                // Quoted string key: {"key": value}
1850                let key_span = self.current_span();
1851                let name =
1852                    if let Some(TokenKind::StringLiteral(s)) = self.current().map(|t| &t.kind) {
1853                        s.clone()
1854                    } else {
1855                        unreachable!()
1856                    };
1857                self.advance();
1858                spanned(Node::StringLiteral(name), key_span)
1859            } else {
1860                // Static key: identifier or keyword -> string literal
1861                let key_span = self.current_span();
1862                let name = self.consume_identifier_or_keyword("dict key")?;
1863                spanned(Node::StringLiteral(name), key_span)
1864            };
1865            self.consume(&TokenKind::Colon, ":")?;
1866            let value = self.parse_expression()?;
1867            entries.push(DictEntry { key, value });
1868            self.skip_newlines();
1869            if self.check(&TokenKind::Comma) {
1870                self.advance();
1871                self.skip_newlines();
1872            }
1873        }
1874
1875        self.consume(&TokenKind::RBrace, "}")?;
1876        Ok(spanned(
1877            Node::DictLiteral(entries),
1878            Span::merge(start, self.prev_span()),
1879        ))
1880    }
1881
1882    // --- Helpers ---
1883
1884    /// Parse untyped parameter list (for pipelines, overrides).
1885    fn parse_param_list(&mut self) -> Result<Vec<String>, ParserError> {
1886        let mut params = Vec::new();
1887        self.skip_newlines();
1888
1889        while !self.is_at_end() && !self.check(&TokenKind::RParen) {
1890            params.push(self.consume_identifier("parameter name")?);
1891            if self.check(&TokenKind::Comma) {
1892                self.advance();
1893                self.skip_newlines();
1894            }
1895        }
1896        Ok(params)
1897    }
1898
1899    /// Parse typed parameter list (for fn declarations).
1900    fn parse_typed_param_list(&mut self) -> Result<Vec<TypedParam>, ParserError> {
1901        self.parse_typed_params_until(|tok| tok == &TokenKind::RParen)
1902    }
1903
1904    /// Shared implementation: parse typed params with optional defaults until
1905    /// a terminator token is reached.
1906    fn parse_typed_params_until(
1907        &mut self,
1908        is_terminator: impl Fn(&TokenKind) -> bool,
1909    ) -> Result<Vec<TypedParam>, ParserError> {
1910        let mut params = Vec::new();
1911        let mut seen_default = false;
1912        self.skip_newlines();
1913
1914        while !self.is_at_end() {
1915            if let Some(tok) = self.current() {
1916                if is_terminator(&tok.kind) {
1917                    break;
1918                }
1919            } else {
1920                break;
1921            }
1922            let name = self.consume_identifier("parameter name")?;
1923            let type_expr = self.try_parse_type_annotation()?;
1924            let default_value = if self.check(&TokenKind::Assign) {
1925                self.advance();
1926                seen_default = true;
1927                Some(Box::new(self.parse_expression()?))
1928            } else {
1929                if seen_default {
1930                    return Err(self.error(
1931                        "Required parameter cannot follow a parameter with a default value",
1932                    ));
1933                }
1934                None
1935            };
1936            params.push(TypedParam {
1937                name,
1938                type_expr,
1939                default_value,
1940            });
1941            if self.check(&TokenKind::Comma) {
1942                self.advance();
1943                self.skip_newlines();
1944            }
1945        }
1946        Ok(params)
1947    }
1948
1949    /// Parse a comma-separated list of type parameter names until `>`.
1950    fn parse_type_param_list(&mut self) -> Result<Vec<TypeParam>, ParserError> {
1951        let mut params = Vec::new();
1952        self.skip_newlines();
1953        while !self.is_at_end() && !self.check(&TokenKind::Gt) {
1954            let name = self.consume_identifier("type parameter name")?;
1955            params.push(TypeParam { name });
1956            if self.check(&TokenKind::Comma) {
1957                self.advance();
1958                self.skip_newlines();
1959            }
1960        }
1961        self.consume(&TokenKind::Gt, ">")?;
1962        Ok(params)
1963    }
1964
1965    /// Parse an optional `where T: bound, U: bound` clause.
1966    /// Looks for an identifier "where" before `{`.
1967    fn parse_where_clauses(&mut self) -> Result<Vec<WhereClause>, ParserError> {
1968        // Check if the next identifier is "where"
1969        if let Some(tok) = self.current() {
1970            if let TokenKind::Identifier(ref id) = tok.kind {
1971                if id == "where" {
1972                    self.advance(); // skip "where"
1973                    let mut clauses = Vec::new();
1974                    loop {
1975                        self.skip_newlines();
1976                        // Stop if we hit `{` or EOF
1977                        if self.check(&TokenKind::LBrace) || self.is_at_end() {
1978                            break;
1979                        }
1980                        let type_name = self.consume_identifier("type parameter name")?;
1981                        self.consume(&TokenKind::Colon, ":")?;
1982                        let bound = self.consume_identifier("type bound")?;
1983                        clauses.push(WhereClause { type_name, bound });
1984                        if self.check(&TokenKind::Comma) {
1985                            self.advance();
1986                        } else {
1987                            break;
1988                        }
1989                    }
1990                    return Ok(clauses);
1991                }
1992            }
1993        }
1994        Ok(Vec::new())
1995    }
1996
1997    /// Try to parse an optional type annotation (`: type`).
1998    /// Returns None if no colon follows.
1999    fn try_parse_type_annotation(&mut self) -> Result<Option<TypeExpr>, ParserError> {
2000        if !self.check(&TokenKind::Colon) {
2001            return Ok(None);
2002        }
2003        self.advance(); // skip :
2004        Ok(Some(self.parse_type_expr()?))
2005    }
2006
2007    /// Parse a type expression: `int`, `string | nil`, `{name: string, age?: int}`.
2008    fn parse_type_expr(&mut self) -> Result<TypeExpr, ParserError> {
2009        self.skip_newlines();
2010        let first = self.parse_type_primary()?;
2011
2012        // Check for union: type | type | ...
2013        if self.check(&TokenKind::Bar) {
2014            let mut types = vec![first];
2015            while self.check(&TokenKind::Bar) {
2016                self.advance(); // skip |
2017                types.push(self.parse_type_primary()?);
2018            }
2019            return Ok(TypeExpr::Union(types));
2020        }
2021
2022        Ok(first)
2023    }
2024
2025    /// Parse a primary type: named type or shape type.
2026    /// Accepts identifiers and certain keywords (nil, bool, etc.) as type names.
2027    fn parse_type_primary(&mut self) -> Result<TypeExpr, ParserError> {
2028        self.skip_newlines();
2029        if self.check(&TokenKind::LBrace) {
2030            return self.parse_shape_type();
2031        }
2032        // Accept keyword type names: nil, true, false map to their type names
2033        if let Some(tok) = self.current() {
2034            let type_name = match &tok.kind {
2035                TokenKind::Nil => {
2036                    self.advance();
2037                    return Ok(TypeExpr::Named("nil".to_string()));
2038                }
2039                TokenKind::True | TokenKind::False => {
2040                    self.advance();
2041                    return Ok(TypeExpr::Named("bool".to_string()));
2042                }
2043                _ => None,
2044            };
2045            if let Some(name) = type_name {
2046                return Ok(TypeExpr::Named(name));
2047            }
2048        }
2049        // Function type: fn(T, U) -> R
2050        if self.check(&TokenKind::Fn) {
2051            self.advance(); // skip `fn`
2052            self.consume(&TokenKind::LParen, "(")?;
2053            let mut params = Vec::new();
2054            self.skip_newlines();
2055            while !self.is_at_end() && !self.check(&TokenKind::RParen) {
2056                params.push(self.parse_type_expr()?);
2057                self.skip_newlines();
2058                if self.check(&TokenKind::Comma) {
2059                    self.advance();
2060                    self.skip_newlines();
2061                }
2062            }
2063            self.consume(&TokenKind::RParen, ")")?;
2064            self.consume(&TokenKind::Arrow, "->")?;
2065            let return_type = self.parse_type_expr()?;
2066            return Ok(TypeExpr::FnType {
2067                params,
2068                return_type: Box::new(return_type),
2069            });
2070        }
2071        let name = self.consume_identifier("type name")?;
2072        // Check for generic type parameters: list<int>, dict<string, int>
2073        if self.check(&TokenKind::Lt) {
2074            self.advance(); // skip <
2075            let first_param = self.parse_type_expr()?;
2076            if name == "list" {
2077                self.consume(&TokenKind::Gt, ">")?;
2078                return Ok(TypeExpr::List(Box::new(first_param)));
2079            } else if name == "dict" {
2080                self.consume(&TokenKind::Comma, ",")?;
2081                let second_param = self.parse_type_expr()?;
2082                self.consume(&TokenKind::Gt, ">")?;
2083                return Ok(TypeExpr::DictType(
2084                    Box::new(first_param),
2085                    Box::new(second_param),
2086                ));
2087            }
2088            // Unknown generic — just consume > and treat as Named
2089            self.consume(&TokenKind::Gt, ">")?;
2090        }
2091        Ok(TypeExpr::Named(name))
2092    }
2093
2094    /// Parse a shape type: `{ name: string, age: int, active?: bool }`.
2095    fn parse_shape_type(&mut self) -> Result<TypeExpr, ParserError> {
2096        self.consume(&TokenKind::LBrace, "{")?;
2097        let mut fields = Vec::new();
2098        self.skip_newlines();
2099
2100        while !self.is_at_end() && !self.check(&TokenKind::RBrace) {
2101            let name = self.consume_identifier("field name")?;
2102            let optional = if self.check(&TokenKind::Question) {
2103                self.advance();
2104                true
2105            } else {
2106                false
2107            };
2108            self.consume(&TokenKind::Colon, ":")?;
2109            let type_expr = self.parse_type_expr()?;
2110            fields.push(ShapeField {
2111                name,
2112                type_expr,
2113                optional,
2114            });
2115            self.skip_newlines();
2116            if self.check(&TokenKind::Comma) {
2117                self.advance();
2118                self.skip_newlines();
2119            }
2120        }
2121
2122        self.consume(&TokenKind::RBrace, "}")?;
2123        Ok(TypeExpr::Shape(fields))
2124    }
2125
2126    fn parse_arg_list(&mut self) -> Result<Vec<SNode>, ParserError> {
2127        let mut args = Vec::new();
2128        self.skip_newlines();
2129
2130        while !self.is_at_end() && !self.check(&TokenKind::RParen) {
2131            // Check for spread: ...expr
2132            if self.check(&TokenKind::Dot) {
2133                let saved_pos = self.pos;
2134                self.advance(); // first .
2135                if self.check(&TokenKind::Dot) {
2136                    self.advance(); // second .
2137                    self.consume(&TokenKind::Dot, ".")?; // third .
2138                    let spread_start = self.tokens[saved_pos].span;
2139                    let expr = self.parse_expression()?;
2140                    args.push(spanned(
2141                        Node::Spread(Box::new(expr)),
2142                        Span::merge(spread_start, self.prev_span()),
2143                    ));
2144                } else {
2145                    // Not a spread, restore and parse as expression
2146                    self.pos = saved_pos;
2147                    args.push(self.parse_expression()?);
2148                }
2149            } else {
2150                args.push(self.parse_expression()?);
2151            }
2152            self.skip_newlines();
2153            if self.check(&TokenKind::Comma) {
2154                self.advance();
2155                self.skip_newlines();
2156            }
2157        }
2158        Ok(args)
2159    }
2160
2161    fn is_at_end(&self) -> bool {
2162        self.pos >= self.tokens.len()
2163            || matches!(self.tokens.get(self.pos), Some(t) if t.kind == TokenKind::Eof)
2164    }
2165
2166    fn current(&self) -> Option<&Token> {
2167        self.tokens.get(self.pos)
2168    }
2169
2170    fn peek_kind(&self) -> Option<&TokenKind> {
2171        self.tokens.get(self.pos + 1).map(|t| &t.kind)
2172    }
2173
2174    fn check(&self, kind: &TokenKind) -> bool {
2175        self.current()
2176            .map(|t| std::mem::discriminant(&t.kind) == std::mem::discriminant(kind))
2177            .unwrap_or(false)
2178    }
2179
2180    /// Check for a token kind, skipping past any newlines first.
2181    /// Used for binary operators like `||` and `&&` that can span lines.
2182    fn check_skip_newlines(&mut self, kind: &TokenKind) -> bool {
2183        let saved = self.pos;
2184        self.skip_newlines();
2185        if self.check(kind) {
2186            true
2187        } else {
2188            self.pos = saved;
2189            false
2190        }
2191    }
2192
2193    fn advance(&mut self) {
2194        if self.pos < self.tokens.len() {
2195            self.pos += 1;
2196        }
2197    }
2198
2199    fn consume(&mut self, kind: &TokenKind, expected: &str) -> Result<Token, ParserError> {
2200        self.skip_newlines();
2201        let tok = self.current().ok_or_else(|| self.make_error(expected))?;
2202        if std::mem::discriminant(&tok.kind) != std::mem::discriminant(kind) {
2203            return Err(self.make_error(expected));
2204        }
2205        let tok = tok.clone();
2206        self.advance();
2207        Ok(tok)
2208    }
2209
2210    fn consume_identifier(&mut self, expected: &str) -> Result<String, ParserError> {
2211        self.skip_newlines();
2212        let tok = self.current().ok_or_else(|| self.make_error(expected))?;
2213        if let TokenKind::Identifier(name) = &tok.kind {
2214            let name = name.clone();
2215            self.advance();
2216            Ok(name)
2217        } else {
2218            Err(self.make_error(expected))
2219        }
2220    }
2221
2222    /// Like `consume_identifier`, but also accepts keywords as identifiers.
2223    /// Used for property access (e.g., `obj.type`) and dict keys where
2224    /// keywords are valid member names.
2225    fn consume_identifier_or_keyword(&mut self, expected: &str) -> Result<String, ParserError> {
2226        self.skip_newlines();
2227        let tok = self.current().ok_or_else(|| self.make_error(expected))?;
2228        if let TokenKind::Identifier(name) = &tok.kind {
2229            let name = name.clone();
2230            self.advance();
2231            return Ok(name);
2232        }
2233        // Accept any keyword token as an identifier
2234        let name = match &tok.kind {
2235            TokenKind::Pipeline => "pipeline",
2236            TokenKind::Extends => "extends",
2237            TokenKind::Override => "override",
2238            TokenKind::Let => "let",
2239            TokenKind::Var => "var",
2240            TokenKind::If => "if",
2241            TokenKind::Else => "else",
2242            TokenKind::For => "for",
2243            TokenKind::In => "in",
2244            TokenKind::Match => "match",
2245            TokenKind::Retry => "retry",
2246            TokenKind::Parallel => "parallel",
2247            TokenKind::ParallelMap => "parallel_map",
2248            TokenKind::ParallelSettle => "parallel_settle",
2249            TokenKind::Return => "return",
2250            TokenKind::Import => "import",
2251            TokenKind::True => "true",
2252            TokenKind::False => "false",
2253            TokenKind::Nil => "nil",
2254            TokenKind::Try => "try",
2255            TokenKind::Catch => "catch",
2256            TokenKind::Throw => "throw",
2257            TokenKind::Fn => "fn",
2258            TokenKind::Spawn => "spawn",
2259            TokenKind::While => "while",
2260            TokenKind::TypeKw => "type",
2261            TokenKind::Enum => "enum",
2262            TokenKind::Struct => "struct",
2263            TokenKind::Interface => "interface",
2264            TokenKind::Pub => "pub",
2265            TokenKind::From => "from",
2266            TokenKind::Thru => "thru",
2267            TokenKind::Upto => "upto",
2268            TokenKind::Guard => "guard",
2269            TokenKind::Ask => "ask",
2270            TokenKind::Deadline => "deadline",
2271            TokenKind::Yield => "yield",
2272            TokenKind::Mutex => "mutex",
2273            TokenKind::Break => "break",
2274            TokenKind::Continue => "continue",
2275            TokenKind::Impl => "impl",
2276            _ => return Err(self.make_error(expected)),
2277        };
2278        let name = name.to_string();
2279        self.advance();
2280        Ok(name)
2281    }
2282
2283    fn skip_newlines(&mut self) {
2284        while self.pos < self.tokens.len() && self.tokens[self.pos].kind == TokenKind::Newline {
2285            self.pos += 1;
2286        }
2287    }
2288
2289    fn make_error(&self, expected: &str) -> ParserError {
2290        if let Some(tok) = self.tokens.get(self.pos) {
2291            if tok.kind == TokenKind::Eof {
2292                return ParserError::UnexpectedEof {
2293                    expected: expected.into(),
2294                };
2295            }
2296            ParserError::Unexpected {
2297                got: tok.kind.to_string(),
2298                expected: expected.into(),
2299                span: tok.span,
2300            }
2301        } else {
2302            ParserError::UnexpectedEof {
2303                expected: expected.into(),
2304            }
2305        }
2306    }
2307
2308    fn error(&self, expected: &str) -> ParserError {
2309        self.make_error(expected)
2310    }
2311}