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