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                self.advance();
681                let name = self.consume_identifier("error variable")?;
682                let ty = self.try_parse_type_annotation()?;
683                self.consume(&TokenKind::RParen, ")")?;
684                (Some(name), ty)
685            } else {
686                (None, None)
687            };
688            self.consume(&TokenKind::LBrace, "{")?;
689            let cb = self.parse_block()?;
690            self.consume(&TokenKind::RBrace, "}")?;
691            (ev, et, cb)
692        } else {
693            (None, None, Vec::new())
694        };
695
696        self.skip_newlines();
697
698        // Parse optional finally block
699        let finally_body = if self.check(&TokenKind::Finally) {
700            self.advance();
701            self.consume(&TokenKind::LBrace, "{")?;
702            let fb = self.parse_block()?;
703            self.consume(&TokenKind::RBrace, "}")?;
704            Some(fb)
705        } else {
706            None
707        };
708
709        // If no catch or finally, this is a try-expression (returns Result)
710        if !has_catch && finally_body.is_none() {
711            return Ok(spanned(
712                Node::TryExpr { body },
713                Span::merge(start, self.prev_span()),
714            ));
715        }
716
717        Ok(spanned(
718            Node::TryCatch {
719                body,
720                error_var,
721                error_type,
722                catch_body,
723                finally_body,
724            },
725            Span::merge(start, self.prev_span()),
726        ))
727    }
728
729    fn parse_select(&mut self) -> Result<SNode, ParserError> {
730        let start = self.current_span();
731        self.consume(&TokenKind::Select, "select")?;
732        self.consume(&TokenKind::LBrace, "{")?;
733        self.skip_newlines();
734
735        let mut cases = Vec::new();
736        let mut timeout = None;
737        let mut default_body = None;
738
739        while !self.is_at_end() && !self.check(&TokenKind::RBrace) {
740            self.skip_newlines();
741            // Check for "timeout" (contextual keyword)
742            if let Some(tok) = self.current() {
743                if let TokenKind::Identifier(ref id) = tok.kind {
744                    if id == "timeout" {
745                        self.advance();
746                        let duration = self.parse_expression()?;
747                        self.consume(&TokenKind::LBrace, "{")?;
748                        let body = self.parse_block()?;
749                        self.consume(&TokenKind::RBrace, "}")?;
750                        timeout = Some((Box::new(duration), body));
751                        self.skip_newlines();
752                        continue;
753                    }
754                    if id == "default" {
755                        self.advance();
756                        self.consume(&TokenKind::LBrace, "{")?;
757                        let body = self.parse_block()?;
758                        self.consume(&TokenKind::RBrace, "}")?;
759                        default_body = Some(body);
760                        self.skip_newlines();
761                        continue;
762                    }
763                }
764            }
765            // Regular case: variable from channel { body }
766            let variable = self.consume_identifier("select case variable")?;
767            self.consume(&TokenKind::From, "from")?;
768            let channel = self.parse_expression()?;
769            self.consume(&TokenKind::LBrace, "{")?;
770            let body = self.parse_block()?;
771            self.consume(&TokenKind::RBrace, "}")?;
772            cases.push(SelectCase {
773                variable,
774                channel: Box::new(channel),
775                body,
776            });
777            self.skip_newlines();
778        }
779
780        self.consume(&TokenKind::RBrace, "}")?;
781
782        if cases.is_empty() && timeout.is_none() && default_body.is_none() {
783            return Err(self.error("at least one select case"));
784        }
785        if timeout.is_some() && default_body.is_some() {
786            return Err(self.error("select cannot have both timeout and default"));
787        }
788
789        Ok(spanned(
790            Node::SelectExpr {
791                cases,
792                timeout,
793                default_body,
794            },
795            Span::merge(start, self.prev_span()),
796        ))
797    }
798
799    fn parse_fn_decl_with_pub(&mut self, is_pub: bool) -> Result<SNode, ParserError> {
800        let start = self.current_span();
801        self.consume(&TokenKind::Fn, "fn")?;
802        let name = self.consume_identifier("function name")?;
803
804        // Optional generic type parameters: fn name<T, U>(...)
805        let type_params = if self.check(&TokenKind::Lt) {
806            self.advance(); // skip <
807            self.parse_type_param_list()?
808        } else {
809            Vec::new()
810        };
811
812        self.consume(&TokenKind::LParen, "(")?;
813        let params = self.parse_typed_param_list()?;
814        self.consume(&TokenKind::RParen, ")")?;
815        // Optional return type: -> type
816        let return_type = if self.check(&TokenKind::Arrow) {
817            self.advance();
818            Some(self.parse_type_expr()?)
819        } else {
820            None
821        };
822
823        // Optional where clause: where T: bound, U: bound
824        let where_clauses = self.parse_where_clauses()?;
825
826        self.consume(&TokenKind::LBrace, "{")?;
827        let body = self.parse_block()?;
828        self.consume(&TokenKind::RBrace, "}")?;
829        Ok(spanned(
830            Node::FnDecl {
831                name,
832                type_params,
833                params,
834                return_type,
835                where_clauses,
836                body,
837                is_pub,
838            },
839            Span::merge(start, self.prev_span()),
840        ))
841    }
842
843    fn parse_type_decl(&mut self) -> Result<SNode, ParserError> {
844        let start = self.current_span();
845        self.consume(&TokenKind::TypeKw, "type")?;
846        let name = self.consume_identifier("type name")?;
847        self.consume(&TokenKind::Assign, "=")?;
848        let type_expr = self.parse_type_expr()?;
849        Ok(spanned(
850            Node::TypeDecl { name, type_expr },
851            Span::merge(start, self.prev_span()),
852        ))
853    }
854
855    fn parse_enum_decl(&mut self) -> Result<SNode, ParserError> {
856        let start = self.current_span();
857        self.consume(&TokenKind::Enum, "enum")?;
858        let name = self.consume_identifier("enum name")?;
859        self.consume(&TokenKind::LBrace, "{")?;
860        self.skip_newlines();
861
862        let mut variants = Vec::new();
863        while !self.is_at_end() && !self.check(&TokenKind::RBrace) {
864            let variant_name = self.consume_identifier("variant name")?;
865            let fields = if self.check(&TokenKind::LParen) {
866                self.advance();
867                let params = self.parse_typed_param_list()?;
868                self.consume(&TokenKind::RParen, ")")?;
869                params
870            } else {
871                Vec::new()
872            };
873            variants.push(EnumVariant {
874                name: variant_name,
875                fields,
876            });
877            self.skip_newlines();
878            if self.check(&TokenKind::Comma) {
879                self.advance();
880                self.skip_newlines();
881            }
882        }
883
884        self.consume(&TokenKind::RBrace, "}")?;
885        Ok(spanned(
886            Node::EnumDecl { name, variants },
887            Span::merge(start, self.prev_span()),
888        ))
889    }
890
891    fn parse_struct_decl(&mut self) -> Result<SNode, ParserError> {
892        let start = self.current_span();
893        self.consume(&TokenKind::Struct, "struct")?;
894        let name = self.consume_identifier("struct name")?;
895        self.consume(&TokenKind::LBrace, "{")?;
896        self.skip_newlines();
897
898        let mut fields = Vec::new();
899        while !self.is_at_end() && !self.check(&TokenKind::RBrace) {
900            let field_name = self.consume_identifier("field name")?;
901            let optional = if self.check(&TokenKind::Question) {
902                self.advance();
903                true
904            } else {
905                false
906            };
907            let type_expr = self.try_parse_type_annotation()?;
908            fields.push(StructField {
909                name: field_name,
910                type_expr,
911                optional,
912            });
913            self.skip_newlines();
914            if self.check(&TokenKind::Comma) {
915                self.advance();
916                self.skip_newlines();
917            }
918        }
919
920        self.consume(&TokenKind::RBrace, "}")?;
921        Ok(spanned(
922            Node::StructDecl { name, fields },
923            Span::merge(start, self.prev_span()),
924        ))
925    }
926
927    fn parse_interface_decl(&mut self) -> Result<SNode, ParserError> {
928        let start = self.current_span();
929        self.consume(&TokenKind::Interface, "interface")?;
930        let name = self.consume_identifier("interface name")?;
931        self.consume(&TokenKind::LBrace, "{")?;
932        self.skip_newlines();
933
934        let mut methods = Vec::new();
935        while !self.is_at_end() && !self.check(&TokenKind::RBrace) {
936            self.consume(&TokenKind::Fn, "fn")?;
937            let method_name = self.consume_identifier("method name")?;
938            self.consume(&TokenKind::LParen, "(")?;
939            let params = self.parse_typed_param_list()?;
940            self.consume(&TokenKind::RParen, ")")?;
941            // Optional return type: -> type
942            let return_type = if self.check(&TokenKind::Arrow) {
943                self.advance();
944                Some(self.parse_type_expr()?)
945            } else {
946                None
947            };
948            methods.push(InterfaceMethod {
949                name: method_name,
950                params,
951                return_type,
952            });
953            self.skip_newlines();
954        }
955
956        self.consume(&TokenKind::RBrace, "}")?;
957        Ok(spanned(
958            Node::InterfaceDecl { name, methods },
959            Span::merge(start, self.prev_span()),
960        ))
961    }
962
963    fn parse_impl_block(&mut self) -> Result<SNode, ParserError> {
964        let start = self.current_span();
965        self.consume(&TokenKind::Impl, "impl")?;
966        let type_name = self.consume_identifier("type name")?;
967        self.consume(&TokenKind::LBrace, "{")?;
968        self.skip_newlines();
969
970        let mut methods = Vec::new();
971        while !self.is_at_end() && !self.check(&TokenKind::RBrace) {
972            let is_pub = self.check(&TokenKind::Pub);
973            if is_pub {
974                self.advance();
975            }
976            let method = self.parse_fn_decl_with_pub(is_pub)?;
977            methods.push(method);
978            self.skip_newlines();
979        }
980
981        self.consume(&TokenKind::RBrace, "}")?;
982        Ok(spanned(
983            Node::ImplBlock { type_name, methods },
984            Span::merge(start, self.prev_span()),
985        ))
986    }
987
988    fn parse_guard(&mut self) -> Result<SNode, ParserError> {
989        let start = self.current_span();
990        self.consume(&TokenKind::Guard, "guard")?;
991        let condition = self.parse_expression()?;
992        // Consume "else" — we reuse the Else keyword
993        self.consume(&TokenKind::Else, "else")?;
994        self.consume(&TokenKind::LBrace, "{")?;
995        let else_body = self.parse_block()?;
996        self.consume(&TokenKind::RBrace, "}")?;
997        Ok(spanned(
998            Node::GuardStmt {
999                condition: Box::new(condition),
1000                else_body,
1001            },
1002            Span::merge(start, self.prev_span()),
1003        ))
1004    }
1005
1006    fn parse_deadline(&mut self) -> Result<SNode, ParserError> {
1007        let start = self.current_span();
1008        self.consume(&TokenKind::Deadline, "deadline")?;
1009        let duration = self.parse_primary()?;
1010        self.consume(&TokenKind::LBrace, "{")?;
1011        let body = self.parse_block()?;
1012        self.consume(&TokenKind::RBrace, "}")?;
1013        Ok(spanned(
1014            Node::DeadlineBlock {
1015                duration: Box::new(duration),
1016                body,
1017            },
1018            Span::merge(start, self.prev_span()),
1019        ))
1020    }
1021
1022    fn parse_yield(&mut self) -> Result<SNode, ParserError> {
1023        let start = self.current_span();
1024        self.consume(&TokenKind::Yield, "yield")?;
1025        if self.is_at_end() || self.check(&TokenKind::Newline) || self.check(&TokenKind::RBrace) {
1026            return Ok(spanned(
1027                Node::YieldExpr { value: None },
1028                Span::merge(start, self.prev_span()),
1029            ));
1030        }
1031        let value = self.parse_expression()?;
1032        Ok(spanned(
1033            Node::YieldExpr {
1034                value: Some(Box::new(value)),
1035            },
1036            Span::merge(start, self.prev_span()),
1037        ))
1038    }
1039
1040    fn parse_mutex(&mut self) -> Result<SNode, ParserError> {
1041        let start = self.current_span();
1042        self.consume(&TokenKind::Mutex, "mutex")?;
1043        self.consume(&TokenKind::LBrace, "{")?;
1044        let body = self.parse_block()?;
1045        self.consume(&TokenKind::RBrace, "}")?;
1046        Ok(spanned(
1047            Node::MutexBlock { body },
1048            Span::merge(start, self.prev_span()),
1049        ))
1050    }
1051
1052    fn parse_ask_expr(&mut self) -> Result<SNode, ParserError> {
1053        let start = self.current_span();
1054        self.consume(&TokenKind::Ask, "ask")?;
1055        self.consume(&TokenKind::LBrace, "{")?;
1056        // Parse as dict entries
1057        let mut entries = Vec::new();
1058        self.skip_newlines();
1059        while !self.is_at_end() && !self.check(&TokenKind::RBrace) {
1060            let key_span = self.current_span();
1061            let name = self.consume_identifier("ask field")?;
1062            let key = spanned(Node::StringLiteral(name), key_span);
1063            self.consume(&TokenKind::Colon, ":")?;
1064            let value = self.parse_expression()?;
1065            entries.push(DictEntry { key, value });
1066            self.skip_newlines();
1067            if self.check(&TokenKind::Comma) {
1068                self.advance();
1069                self.skip_newlines();
1070            }
1071        }
1072        self.consume(&TokenKind::RBrace, "}")?;
1073        Ok(spanned(
1074            Node::AskExpr { fields: entries },
1075            Span::merge(start, self.prev_span()),
1076        ))
1077    }
1078
1079    // --- Expressions (precedence climbing) ---
1080
1081    fn parse_expression_statement(&mut self) -> Result<SNode, ParserError> {
1082        let start = self.current_span();
1083        let expr = self.parse_expression()?;
1084
1085        // Check for assignment or compound assignment on valid targets:
1086        // identifier, property access (obj.field), subscript access (obj[key])
1087        let is_assignable = matches!(
1088            expr.node,
1089            Node::Identifier(_) | Node::PropertyAccess { .. } | Node::SubscriptAccess { .. }
1090        );
1091        if is_assignable {
1092            if self.check(&TokenKind::Assign) {
1093                self.advance();
1094                let value = self.parse_expression()?;
1095                return Ok(spanned(
1096                    Node::Assignment {
1097                        target: Box::new(expr),
1098                        value: Box::new(value),
1099                        op: None,
1100                    },
1101                    Span::merge(start, self.prev_span()),
1102                ));
1103            }
1104            let compound_op = if self.check(&TokenKind::PlusAssign) {
1105                Some("+")
1106            } else if self.check(&TokenKind::MinusAssign) {
1107                Some("-")
1108            } else if self.check(&TokenKind::StarAssign) {
1109                Some("*")
1110            } else if self.check(&TokenKind::SlashAssign) {
1111                Some("/")
1112            } else if self.check(&TokenKind::PercentAssign) {
1113                Some("%")
1114            } else {
1115                None
1116            };
1117            if let Some(op) = compound_op {
1118                self.advance();
1119                let value = self.parse_expression()?;
1120                return Ok(spanned(
1121                    Node::Assignment {
1122                        target: Box::new(expr),
1123                        value: Box::new(value),
1124                        op: Some(op.into()),
1125                    },
1126                    Span::merge(start, self.prev_span()),
1127                ));
1128            }
1129        }
1130
1131        Ok(expr)
1132    }
1133
1134    fn parse_expression(&mut self) -> Result<SNode, ParserError> {
1135        self.skip_newlines();
1136        self.parse_pipe()
1137    }
1138
1139    fn parse_pipe(&mut self) -> Result<SNode, ParserError> {
1140        let mut left = self.parse_range()?;
1141        while self.check_skip_newlines(&TokenKind::Pipe) {
1142            let start = left.span;
1143            self.advance();
1144            let right = self.parse_range()?;
1145            left = spanned(
1146                Node::BinaryOp {
1147                    op: "|>".into(),
1148                    left: Box::new(left),
1149                    right: Box::new(right),
1150                },
1151                Span::merge(start, self.prev_span()),
1152            );
1153        }
1154        Ok(left)
1155    }
1156
1157    fn parse_range(&mut self) -> Result<SNode, ParserError> {
1158        let left = self.parse_ternary()?;
1159        if self.check(&TokenKind::Thru) {
1160            let start = left.span;
1161            self.advance();
1162            let right = self.parse_ternary()?;
1163            return Ok(spanned(
1164                Node::RangeExpr {
1165                    start: Box::new(left),
1166                    end: Box::new(right),
1167                    inclusive: true,
1168                },
1169                Span::merge(start, self.prev_span()),
1170            ));
1171        }
1172        if self.check(&TokenKind::Upto) {
1173            let start = left.span;
1174            self.advance();
1175            let right = self.parse_ternary()?;
1176            return Ok(spanned(
1177                Node::RangeExpr {
1178                    start: Box::new(left),
1179                    end: Box::new(right),
1180                    inclusive: false,
1181                },
1182                Span::merge(start, self.prev_span()),
1183            ));
1184        }
1185        Ok(left)
1186    }
1187
1188    fn parse_ternary(&mut self) -> Result<SNode, ParserError> {
1189        let condition = self.parse_nil_coalescing()?;
1190        if !self.check(&TokenKind::Question) {
1191            return Ok(condition);
1192        }
1193        let start = condition.span;
1194        self.advance(); // skip ?
1195        let true_val = self.parse_nil_coalescing()?;
1196        self.consume(&TokenKind::Colon, ":")?;
1197        let false_val = self.parse_nil_coalescing()?;
1198        Ok(spanned(
1199            Node::Ternary {
1200                condition: Box::new(condition),
1201                true_expr: Box::new(true_val),
1202                false_expr: Box::new(false_val),
1203            },
1204            Span::merge(start, self.prev_span()),
1205        ))
1206    }
1207
1208    fn parse_nil_coalescing(&mut self) -> Result<SNode, ParserError> {
1209        let mut left = self.parse_logical_or()?;
1210        while self.check(&TokenKind::NilCoal) {
1211            let start = left.span;
1212            self.advance();
1213            let right = self.parse_logical_or()?;
1214            left = spanned(
1215                Node::BinaryOp {
1216                    op: "??".into(),
1217                    left: Box::new(left),
1218                    right: Box::new(right),
1219                },
1220                Span::merge(start, self.prev_span()),
1221            );
1222        }
1223        Ok(left)
1224    }
1225
1226    fn parse_logical_or(&mut self) -> Result<SNode, ParserError> {
1227        let mut left = self.parse_logical_and()?;
1228        while self.check_skip_newlines(&TokenKind::Or) {
1229            let start = left.span;
1230            self.advance();
1231            let right = self.parse_logical_and()?;
1232            left = spanned(
1233                Node::BinaryOp {
1234                    op: "||".into(),
1235                    left: Box::new(left),
1236                    right: Box::new(right),
1237                },
1238                Span::merge(start, self.prev_span()),
1239            );
1240        }
1241        Ok(left)
1242    }
1243
1244    fn parse_logical_and(&mut self) -> Result<SNode, ParserError> {
1245        let mut left = self.parse_equality()?;
1246        while self.check_skip_newlines(&TokenKind::And) {
1247            let start = left.span;
1248            self.advance();
1249            let right = self.parse_equality()?;
1250            left = spanned(
1251                Node::BinaryOp {
1252                    op: "&&".into(),
1253                    left: Box::new(left),
1254                    right: Box::new(right),
1255                },
1256                Span::merge(start, self.prev_span()),
1257            );
1258        }
1259        Ok(left)
1260    }
1261
1262    fn parse_equality(&mut self) -> Result<SNode, ParserError> {
1263        let mut left = self.parse_comparison()?;
1264        while self.check(&TokenKind::Eq) || self.check(&TokenKind::Neq) {
1265            let start = left.span;
1266            let op = if self.check(&TokenKind::Eq) {
1267                "=="
1268            } else {
1269                "!="
1270            };
1271            self.advance();
1272            let right = self.parse_comparison()?;
1273            left = spanned(
1274                Node::BinaryOp {
1275                    op: op.into(),
1276                    left: Box::new(left),
1277                    right: Box::new(right),
1278                },
1279                Span::merge(start, self.prev_span()),
1280            );
1281        }
1282        Ok(left)
1283    }
1284
1285    fn parse_comparison(&mut self) -> Result<SNode, ParserError> {
1286        let mut left = self.parse_additive()?;
1287        while self.check(&TokenKind::Lt)
1288            || self.check(&TokenKind::Gt)
1289            || self.check(&TokenKind::Lte)
1290            || self.check(&TokenKind::Gte)
1291        {
1292            let start = left.span;
1293            let op = match self.current().map(|t| &t.kind) {
1294                Some(TokenKind::Lt) => "<",
1295                Some(TokenKind::Gt) => ">",
1296                Some(TokenKind::Lte) => "<=",
1297                Some(TokenKind::Gte) => ">=",
1298                _ => "<",
1299            };
1300            self.advance();
1301            let right = self.parse_additive()?;
1302            left = spanned(
1303                Node::BinaryOp {
1304                    op: op.into(),
1305                    left: Box::new(left),
1306                    right: Box::new(right),
1307                },
1308                Span::merge(start, self.prev_span()),
1309            );
1310        }
1311        Ok(left)
1312    }
1313
1314    fn parse_additive(&mut self) -> Result<SNode, ParserError> {
1315        let mut left = self.parse_multiplicative()?;
1316        while self.check_skip_newlines(&TokenKind::Plus) || self.check(&TokenKind::Minus) {
1317            let start = left.span;
1318            let op = if self.check(&TokenKind::Plus) {
1319                "+"
1320            } else {
1321                "-"
1322            };
1323            self.advance();
1324            let right = self.parse_multiplicative()?;
1325            left = spanned(
1326                Node::BinaryOp {
1327                    op: op.into(),
1328                    left: Box::new(left),
1329                    right: Box::new(right),
1330                },
1331                Span::merge(start, self.prev_span()),
1332            );
1333        }
1334        Ok(left)
1335    }
1336
1337    fn parse_multiplicative(&mut self) -> Result<SNode, ParserError> {
1338        let mut left = self.parse_unary()?;
1339        while self.check_skip_newlines(&TokenKind::Star)
1340            || self.check_skip_newlines(&TokenKind::Slash)
1341            || self.check_skip_newlines(&TokenKind::Percent)
1342        {
1343            let start = left.span;
1344            let op = if self.check(&TokenKind::Star) {
1345                "*"
1346            } else if self.check(&TokenKind::Slash) {
1347                "/"
1348            } else {
1349                "%"
1350            };
1351            self.advance();
1352            let right = self.parse_unary()?;
1353            left = spanned(
1354                Node::BinaryOp {
1355                    op: op.into(),
1356                    left: Box::new(left),
1357                    right: Box::new(right),
1358                },
1359                Span::merge(start, self.prev_span()),
1360            );
1361        }
1362        Ok(left)
1363    }
1364
1365    fn parse_unary(&mut self) -> Result<SNode, ParserError> {
1366        if self.check(&TokenKind::Not) {
1367            let start = self.current_span();
1368            self.advance();
1369            let operand = self.parse_unary()?;
1370            return Ok(spanned(
1371                Node::UnaryOp {
1372                    op: "!".into(),
1373                    operand: Box::new(operand),
1374                },
1375                Span::merge(start, self.prev_span()),
1376            ));
1377        }
1378        if self.check(&TokenKind::Minus) {
1379            let start = self.current_span();
1380            self.advance();
1381            let operand = self.parse_unary()?;
1382            return Ok(spanned(
1383                Node::UnaryOp {
1384                    op: "-".into(),
1385                    operand: Box::new(operand),
1386                },
1387                Span::merge(start, self.prev_span()),
1388            ));
1389        }
1390        self.parse_postfix()
1391    }
1392
1393    fn parse_postfix(&mut self) -> Result<SNode, ParserError> {
1394        let mut expr = self.parse_primary()?;
1395
1396        loop {
1397            if self.check_skip_newlines(&TokenKind::Dot)
1398                || self.check_skip_newlines(&TokenKind::QuestionDot)
1399            {
1400                let optional = self.check(&TokenKind::QuestionDot);
1401                let start = expr.span;
1402                self.advance();
1403                let member = self.consume_identifier_or_keyword("member name")?;
1404                if self.check(&TokenKind::LParen) {
1405                    self.advance();
1406                    let args = self.parse_arg_list()?;
1407                    self.consume(&TokenKind::RParen, ")")?;
1408                    if optional {
1409                        expr = spanned(
1410                            Node::OptionalMethodCall {
1411                                object: Box::new(expr),
1412                                method: member,
1413                                args,
1414                            },
1415                            Span::merge(start, self.prev_span()),
1416                        );
1417                    } else {
1418                        expr = spanned(
1419                            Node::MethodCall {
1420                                object: Box::new(expr),
1421                                method: member,
1422                                args,
1423                            },
1424                            Span::merge(start, self.prev_span()),
1425                        );
1426                    }
1427                } else if optional {
1428                    expr = spanned(
1429                        Node::OptionalPropertyAccess {
1430                            object: Box::new(expr),
1431                            property: member,
1432                        },
1433                        Span::merge(start, self.prev_span()),
1434                    );
1435                } else {
1436                    expr = spanned(
1437                        Node::PropertyAccess {
1438                            object: Box::new(expr),
1439                            property: member,
1440                        },
1441                        Span::merge(start, self.prev_span()),
1442                    );
1443                }
1444            } else if self.check(&TokenKind::LBracket) {
1445                let start = expr.span;
1446                self.advance();
1447
1448                // Check for slice vs subscript:
1449                // [:end] — slice with no start
1450                // [start:end] or [start:] — slice with start
1451                // [index] — normal subscript
1452                if self.check(&TokenKind::Colon) {
1453                    // [:end] or [:]
1454                    self.advance(); // consume ':'
1455                    let end_expr = if self.check(&TokenKind::RBracket) {
1456                        None
1457                    } else {
1458                        Some(Box::new(self.parse_expression()?))
1459                    };
1460                    self.consume(&TokenKind::RBracket, "]")?;
1461                    expr = spanned(
1462                        Node::SliceAccess {
1463                            object: Box::new(expr),
1464                            start: None,
1465                            end: end_expr,
1466                        },
1467                        Span::merge(start, self.prev_span()),
1468                    );
1469                } else {
1470                    let index = self.parse_expression()?;
1471                    if self.check(&TokenKind::Colon) {
1472                        // [start:end] or [start:]
1473                        self.advance(); // consume ':'
1474                        let end_expr = if self.check(&TokenKind::RBracket) {
1475                            None
1476                        } else {
1477                            Some(Box::new(self.parse_expression()?))
1478                        };
1479                        self.consume(&TokenKind::RBracket, "]")?;
1480                        expr = spanned(
1481                            Node::SliceAccess {
1482                                object: Box::new(expr),
1483                                start: Some(Box::new(index)),
1484                                end: end_expr,
1485                            },
1486                            Span::merge(start, self.prev_span()),
1487                        );
1488                    } else {
1489                        self.consume(&TokenKind::RBracket, "]")?;
1490                        expr = spanned(
1491                            Node::SubscriptAccess {
1492                                object: Box::new(expr),
1493                                index: Box::new(index),
1494                            },
1495                            Span::merge(start, self.prev_span()),
1496                        );
1497                    }
1498                }
1499            } else if self.check(&TokenKind::LParen) && matches!(expr.node, Node::Identifier(_)) {
1500                let start = expr.span;
1501                self.advance();
1502                let args = self.parse_arg_list()?;
1503                self.consume(&TokenKind::RParen, ")")?;
1504                if let Node::Identifier(name) = expr.node {
1505                    expr = spanned(
1506                        Node::FunctionCall { name, args },
1507                        Span::merge(start, self.prev_span()),
1508                    );
1509                }
1510            } else if self.check(&TokenKind::Question) {
1511                // Distinguish postfix try operator (expr?) from ternary (expr ? a : b).
1512                // If the token after ? could start a ternary branch, leave it for parse_ternary.
1513                let next_pos = self.pos + 1;
1514                let is_ternary = self.tokens.get(next_pos).is_some_and(|t| {
1515                    matches!(
1516                        t.kind,
1517                        TokenKind::Identifier(_)
1518                            | TokenKind::IntLiteral(_)
1519                            | TokenKind::FloatLiteral(_)
1520                            | TokenKind::StringLiteral(_)
1521                            | TokenKind::InterpolatedString(_)
1522                            | TokenKind::True
1523                            | TokenKind::False
1524                            | TokenKind::Nil
1525                            | TokenKind::LParen
1526                            | TokenKind::LBracket
1527                            | TokenKind::LBrace
1528                            | TokenKind::Not
1529                            | TokenKind::Minus
1530                            | TokenKind::Fn
1531                    )
1532                });
1533                if is_ternary {
1534                    break;
1535                }
1536                let start = expr.span;
1537                self.advance(); // consume ?
1538                expr = spanned(
1539                    Node::TryOperator {
1540                        operand: Box::new(expr),
1541                    },
1542                    Span::merge(start, self.prev_span()),
1543                );
1544            } else {
1545                break;
1546            }
1547        }
1548
1549        Ok(expr)
1550    }
1551
1552    fn parse_primary(&mut self) -> Result<SNode, ParserError> {
1553        let tok = self.current().ok_or_else(|| ParserError::UnexpectedEof {
1554            expected: "expression".into(),
1555        })?;
1556        let start = self.current_span();
1557
1558        match &tok.kind {
1559            TokenKind::StringLiteral(s) => {
1560                let s = s.clone();
1561                self.advance();
1562                Ok(spanned(
1563                    Node::StringLiteral(s),
1564                    Span::merge(start, self.prev_span()),
1565                ))
1566            }
1567            TokenKind::InterpolatedString(segments) => {
1568                let segments = segments.clone();
1569                self.advance();
1570                Ok(spanned(
1571                    Node::InterpolatedString(segments),
1572                    Span::merge(start, self.prev_span()),
1573                ))
1574            }
1575            TokenKind::IntLiteral(n) => {
1576                let n = *n;
1577                self.advance();
1578                Ok(spanned(
1579                    Node::IntLiteral(n),
1580                    Span::merge(start, self.prev_span()),
1581                ))
1582            }
1583            TokenKind::FloatLiteral(n) => {
1584                let n = *n;
1585                self.advance();
1586                Ok(spanned(
1587                    Node::FloatLiteral(n),
1588                    Span::merge(start, self.prev_span()),
1589                ))
1590            }
1591            TokenKind::True => {
1592                self.advance();
1593                Ok(spanned(
1594                    Node::BoolLiteral(true),
1595                    Span::merge(start, self.prev_span()),
1596                ))
1597            }
1598            TokenKind::False => {
1599                self.advance();
1600                Ok(spanned(
1601                    Node::BoolLiteral(false),
1602                    Span::merge(start, self.prev_span()),
1603                ))
1604            }
1605            TokenKind::Nil => {
1606                self.advance();
1607                Ok(spanned(
1608                    Node::NilLiteral,
1609                    Span::merge(start, self.prev_span()),
1610                ))
1611            }
1612            TokenKind::Identifier(name) => {
1613                let name = name.clone();
1614                self.advance();
1615                Ok(spanned(
1616                    Node::Identifier(name),
1617                    Span::merge(start, self.prev_span()),
1618                ))
1619            }
1620            TokenKind::LParen => {
1621                self.advance();
1622                let expr = self.parse_expression()?;
1623                self.consume(&TokenKind::RParen, ")")?;
1624                Ok(expr)
1625            }
1626            TokenKind::LBracket => self.parse_list_literal(),
1627            TokenKind::LBrace => self.parse_dict_or_closure(),
1628            TokenKind::Parallel => self.parse_parallel(),
1629            TokenKind::ParallelMap => self.parse_parallel_map(),
1630            TokenKind::Retry => self.parse_retry(),
1631            TokenKind::If => self.parse_if_else(),
1632            TokenKind::Spawn => self.parse_spawn_expr(),
1633            TokenKind::DurationLiteral(ms) => {
1634                let ms = *ms;
1635                self.advance();
1636                Ok(spanned(
1637                    Node::DurationLiteral(ms),
1638                    Span::merge(start, self.prev_span()),
1639                ))
1640            }
1641            TokenKind::Ask => self.parse_ask_expr(),
1642            TokenKind::Deadline => self.parse_deadline(),
1643            TokenKind::Try => self.parse_try_catch(),
1644            _ => Err(self.error("expression")),
1645        }
1646    }
1647
1648    fn parse_spawn_expr(&mut self) -> Result<SNode, ParserError> {
1649        let start = self.current_span();
1650        self.consume(&TokenKind::Spawn, "spawn")?;
1651        self.consume(&TokenKind::LBrace, "{")?;
1652        let body = self.parse_block()?;
1653        self.consume(&TokenKind::RBrace, "}")?;
1654        Ok(spanned(
1655            Node::SpawnExpr { body },
1656            Span::merge(start, self.prev_span()),
1657        ))
1658    }
1659
1660    fn parse_list_literal(&mut self) -> Result<SNode, ParserError> {
1661        let start = self.current_span();
1662        self.consume(&TokenKind::LBracket, "[")?;
1663        let mut elements = Vec::new();
1664        self.skip_newlines();
1665
1666        while !self.is_at_end() && !self.check(&TokenKind::RBracket) {
1667            // Check for spread: ...expr
1668            if self.check(&TokenKind::Dot) {
1669                let saved_pos = self.pos;
1670                self.advance(); // first .
1671                if self.check(&TokenKind::Dot) {
1672                    self.advance(); // second .
1673                    self.consume(&TokenKind::Dot, ".")?; // third .
1674                    let spread_start = self.tokens[saved_pos].span;
1675                    let expr = self.parse_expression()?;
1676                    elements.push(spanned(
1677                        Node::Spread(Box::new(expr)),
1678                        Span::merge(spread_start, self.prev_span()),
1679                    ));
1680                } else {
1681                    // Not a spread, restore and parse as expression
1682                    self.pos = saved_pos;
1683                    elements.push(self.parse_expression()?);
1684                }
1685            } else {
1686                elements.push(self.parse_expression()?);
1687            }
1688            self.skip_newlines();
1689            if self.check(&TokenKind::Comma) {
1690                self.advance();
1691                self.skip_newlines();
1692            }
1693        }
1694
1695        self.consume(&TokenKind::RBracket, "]")?;
1696        Ok(spanned(
1697            Node::ListLiteral(elements),
1698            Span::merge(start, self.prev_span()),
1699        ))
1700    }
1701
1702    fn parse_dict_or_closure(&mut self) -> Result<SNode, ParserError> {
1703        let start = self.current_span();
1704        self.consume(&TokenKind::LBrace, "{")?;
1705        self.skip_newlines();
1706
1707        // Empty dict
1708        if self.check(&TokenKind::RBrace) {
1709            self.advance();
1710            return Ok(spanned(
1711                Node::DictLiteral(Vec::new()),
1712                Span::merge(start, self.prev_span()),
1713            ));
1714        }
1715
1716        // Lookahead: scan for -> before } to disambiguate closure from dict.
1717        let saved = self.pos;
1718        if self.is_closure_lookahead() {
1719            self.pos = saved;
1720            return self.parse_closure_body(start);
1721        }
1722        self.pos = saved;
1723        self.parse_dict_literal(start)
1724    }
1725
1726    /// Scan forward to determine if this is a closure (has -> before matching }).
1727    /// Does not consume tokens (caller saves/restores pos).
1728    fn is_closure_lookahead(&mut self) -> bool {
1729        let mut depth = 0;
1730        while !self.is_at_end() {
1731            if let Some(tok) = self.current() {
1732                match &tok.kind {
1733                    TokenKind::Arrow if depth == 0 => return true,
1734                    TokenKind::LBrace | TokenKind::LParen | TokenKind::LBracket => depth += 1,
1735                    TokenKind::RBrace if depth == 0 => return false,
1736                    TokenKind::RBrace => depth -= 1,
1737                    TokenKind::RParen | TokenKind::RBracket => {
1738                        if depth > 0 {
1739                            depth -= 1;
1740                        }
1741                    }
1742                    _ => {}
1743                }
1744                self.advance();
1745            } else {
1746                return false;
1747            }
1748        }
1749        false
1750    }
1751
1752    /// Parse closure params and body (after opening { has been consumed).
1753    fn parse_closure_body(&mut self, start: Span) -> Result<SNode, ParserError> {
1754        let params = self.parse_typed_param_list_until_arrow()?;
1755        self.consume(&TokenKind::Arrow, "->")?;
1756        let body = self.parse_block()?;
1757        self.consume(&TokenKind::RBrace, "}")?;
1758        Ok(spanned(
1759            Node::Closure { params, body },
1760            Span::merge(start, self.prev_span()),
1761        ))
1762    }
1763
1764    /// Parse typed params until we see ->. Handles: `x`, `x: int`, `x, y`, `x: int, y: string`.
1765    fn parse_typed_param_list_until_arrow(&mut self) -> Result<Vec<TypedParam>, ParserError> {
1766        self.parse_typed_params_until(|tok| tok == &TokenKind::Arrow)
1767    }
1768
1769    fn parse_dict_literal(&mut self, start: Span) -> Result<SNode, ParserError> {
1770        let mut entries = Vec::new();
1771        self.skip_newlines();
1772
1773        while !self.is_at_end() && !self.check(&TokenKind::RBrace) {
1774            // Check for spread: ...expr
1775            if self.check(&TokenKind::Dot) {
1776                let saved_pos = self.pos;
1777                self.advance(); // first .
1778                if self.check(&TokenKind::Dot) {
1779                    self.advance(); // second .
1780                    if self.check(&TokenKind::Dot) {
1781                        self.advance(); // third .
1782                        let spread_start = self.tokens[saved_pos].span;
1783                        let expr = self.parse_expression()?;
1784                        entries.push(DictEntry {
1785                            key: spanned(Node::NilLiteral, spread_start),
1786                            value: spanned(
1787                                Node::Spread(Box::new(expr)),
1788                                Span::merge(spread_start, self.prev_span()),
1789                            ),
1790                        });
1791                        self.skip_newlines();
1792                        if self.check(&TokenKind::Comma) {
1793                            self.advance();
1794                            self.skip_newlines();
1795                        }
1796                        continue;
1797                    }
1798                    // Not three dots — restore
1799                    self.pos = saved_pos;
1800                } else {
1801                    self.pos = saved_pos;
1802                }
1803            }
1804            let key = if self.check(&TokenKind::LBracket) {
1805                // Computed key: [expression]
1806                self.advance();
1807                let k = self.parse_expression()?;
1808                self.consume(&TokenKind::RBracket, "]")?;
1809                k
1810            } else if matches!(
1811                self.current().map(|t| &t.kind),
1812                Some(TokenKind::StringLiteral(_))
1813            ) {
1814                // Quoted string key: {"key": value}
1815                let key_span = self.current_span();
1816                let name =
1817                    if let Some(TokenKind::StringLiteral(s)) = self.current().map(|t| &t.kind) {
1818                        s.clone()
1819                    } else {
1820                        unreachable!()
1821                    };
1822                self.advance();
1823                spanned(Node::StringLiteral(name), key_span)
1824            } else {
1825                // Static key: identifier or keyword -> string literal
1826                let key_span = self.current_span();
1827                let name = self.consume_identifier_or_keyword("dict key")?;
1828                spanned(Node::StringLiteral(name), key_span)
1829            };
1830            self.consume(&TokenKind::Colon, ":")?;
1831            let value = self.parse_expression()?;
1832            entries.push(DictEntry { key, value });
1833            self.skip_newlines();
1834            if self.check(&TokenKind::Comma) {
1835                self.advance();
1836                self.skip_newlines();
1837            }
1838        }
1839
1840        self.consume(&TokenKind::RBrace, "}")?;
1841        Ok(spanned(
1842            Node::DictLiteral(entries),
1843            Span::merge(start, self.prev_span()),
1844        ))
1845    }
1846
1847    // --- Helpers ---
1848
1849    /// Parse untyped parameter list (for pipelines, overrides).
1850    fn parse_param_list(&mut self) -> Result<Vec<String>, ParserError> {
1851        let mut params = Vec::new();
1852        self.skip_newlines();
1853
1854        while !self.is_at_end() && !self.check(&TokenKind::RParen) {
1855            params.push(self.consume_identifier("parameter name")?);
1856            if self.check(&TokenKind::Comma) {
1857                self.advance();
1858                self.skip_newlines();
1859            }
1860        }
1861        Ok(params)
1862    }
1863
1864    /// Parse typed parameter list (for fn declarations).
1865    fn parse_typed_param_list(&mut self) -> Result<Vec<TypedParam>, ParserError> {
1866        self.parse_typed_params_until(|tok| tok == &TokenKind::RParen)
1867    }
1868
1869    /// Shared implementation: parse typed params with optional defaults until
1870    /// a terminator token is reached.
1871    fn parse_typed_params_until(
1872        &mut self,
1873        is_terminator: impl Fn(&TokenKind) -> bool,
1874    ) -> Result<Vec<TypedParam>, ParserError> {
1875        let mut params = Vec::new();
1876        let mut seen_default = false;
1877        self.skip_newlines();
1878
1879        while !self.is_at_end() {
1880            if let Some(tok) = self.current() {
1881                if is_terminator(&tok.kind) {
1882                    break;
1883                }
1884            } else {
1885                break;
1886            }
1887            let name = self.consume_identifier("parameter name")?;
1888            let type_expr = self.try_parse_type_annotation()?;
1889            let default_value = if self.check(&TokenKind::Assign) {
1890                self.advance();
1891                seen_default = true;
1892                Some(Box::new(self.parse_expression()?))
1893            } else {
1894                if seen_default {
1895                    return Err(self.error(
1896                        "Required parameter cannot follow a parameter with a default value",
1897                    ));
1898                }
1899                None
1900            };
1901            params.push(TypedParam {
1902                name,
1903                type_expr,
1904                default_value,
1905            });
1906            if self.check(&TokenKind::Comma) {
1907                self.advance();
1908                self.skip_newlines();
1909            }
1910        }
1911        Ok(params)
1912    }
1913
1914    /// Parse a comma-separated list of type parameter names until `>`.
1915    fn parse_type_param_list(&mut self) -> Result<Vec<TypeParam>, ParserError> {
1916        let mut params = Vec::new();
1917        self.skip_newlines();
1918        while !self.is_at_end() && !self.check(&TokenKind::Gt) {
1919            let name = self.consume_identifier("type parameter name")?;
1920            params.push(TypeParam { name });
1921            if self.check(&TokenKind::Comma) {
1922                self.advance();
1923                self.skip_newlines();
1924            }
1925        }
1926        self.consume(&TokenKind::Gt, ">")?;
1927        Ok(params)
1928    }
1929
1930    /// Parse an optional `where T: bound, U: bound` clause.
1931    /// Looks for an identifier "where" before `{`.
1932    fn parse_where_clauses(&mut self) -> Result<Vec<WhereClause>, ParserError> {
1933        // Check if the next identifier is "where"
1934        if let Some(tok) = self.current() {
1935            if let TokenKind::Identifier(ref id) = tok.kind {
1936                if id == "where" {
1937                    self.advance(); // skip "where"
1938                    let mut clauses = Vec::new();
1939                    loop {
1940                        self.skip_newlines();
1941                        // Stop if we hit `{` or EOF
1942                        if self.check(&TokenKind::LBrace) || self.is_at_end() {
1943                            break;
1944                        }
1945                        let type_name = self.consume_identifier("type parameter name")?;
1946                        self.consume(&TokenKind::Colon, ":")?;
1947                        let bound = self.consume_identifier("type bound")?;
1948                        clauses.push(WhereClause { type_name, bound });
1949                        if self.check(&TokenKind::Comma) {
1950                            self.advance();
1951                        } else {
1952                            break;
1953                        }
1954                    }
1955                    return Ok(clauses);
1956                }
1957            }
1958        }
1959        Ok(Vec::new())
1960    }
1961
1962    /// Try to parse an optional type annotation (`: type`).
1963    /// Returns None if no colon follows.
1964    fn try_parse_type_annotation(&mut self) -> Result<Option<TypeExpr>, ParserError> {
1965        if !self.check(&TokenKind::Colon) {
1966            return Ok(None);
1967        }
1968        self.advance(); // skip :
1969        Ok(Some(self.parse_type_expr()?))
1970    }
1971
1972    /// Parse a type expression: `int`, `string | nil`, `{name: string, age?: int}`.
1973    fn parse_type_expr(&mut self) -> Result<TypeExpr, ParserError> {
1974        self.skip_newlines();
1975        let first = self.parse_type_primary()?;
1976
1977        // Check for union: type | type | ...
1978        if self.check(&TokenKind::Bar) {
1979            let mut types = vec![first];
1980            while self.check(&TokenKind::Bar) {
1981                self.advance(); // skip |
1982                types.push(self.parse_type_primary()?);
1983            }
1984            return Ok(TypeExpr::Union(types));
1985        }
1986
1987        Ok(first)
1988    }
1989
1990    /// Parse a primary type: named type or shape type.
1991    /// Accepts identifiers and certain keywords (nil, bool, etc.) as type names.
1992    fn parse_type_primary(&mut self) -> Result<TypeExpr, ParserError> {
1993        self.skip_newlines();
1994        if self.check(&TokenKind::LBrace) {
1995            return self.parse_shape_type();
1996        }
1997        // Accept keyword type names: nil, true, false map to their type names
1998        if let Some(tok) = self.current() {
1999            let type_name = match &tok.kind {
2000                TokenKind::Nil => {
2001                    self.advance();
2002                    return Ok(TypeExpr::Named("nil".to_string()));
2003                }
2004                TokenKind::True | TokenKind::False => {
2005                    self.advance();
2006                    return Ok(TypeExpr::Named("bool".to_string()));
2007                }
2008                _ => None,
2009            };
2010            if let Some(name) = type_name {
2011                return Ok(TypeExpr::Named(name));
2012            }
2013        }
2014        // Function type: fn(T, U) -> R
2015        if self.check(&TokenKind::Fn) {
2016            self.advance(); // skip `fn`
2017            self.consume(&TokenKind::LParen, "(")?;
2018            let mut params = Vec::new();
2019            self.skip_newlines();
2020            while !self.is_at_end() && !self.check(&TokenKind::RParen) {
2021                params.push(self.parse_type_expr()?);
2022                self.skip_newlines();
2023                if self.check(&TokenKind::Comma) {
2024                    self.advance();
2025                    self.skip_newlines();
2026                }
2027            }
2028            self.consume(&TokenKind::RParen, ")")?;
2029            self.consume(&TokenKind::Arrow, "->")?;
2030            let return_type = self.parse_type_expr()?;
2031            return Ok(TypeExpr::FnType {
2032                params,
2033                return_type: Box::new(return_type),
2034            });
2035        }
2036        let name = self.consume_identifier("type name")?;
2037        // Check for generic type parameters: list<int>, dict<string, int>
2038        if self.check(&TokenKind::Lt) {
2039            self.advance(); // skip <
2040            let first_param = self.parse_type_expr()?;
2041            if name == "list" {
2042                self.consume(&TokenKind::Gt, ">")?;
2043                return Ok(TypeExpr::List(Box::new(first_param)));
2044            } else if name == "dict" {
2045                self.consume(&TokenKind::Comma, ",")?;
2046                let second_param = self.parse_type_expr()?;
2047                self.consume(&TokenKind::Gt, ">")?;
2048                return Ok(TypeExpr::DictType(
2049                    Box::new(first_param),
2050                    Box::new(second_param),
2051                ));
2052            }
2053            // Unknown generic — just consume > and treat as Named
2054            self.consume(&TokenKind::Gt, ">")?;
2055        }
2056        Ok(TypeExpr::Named(name))
2057    }
2058
2059    /// Parse a shape type: `{ name: string, age: int, active?: bool }`.
2060    fn parse_shape_type(&mut self) -> Result<TypeExpr, ParserError> {
2061        self.consume(&TokenKind::LBrace, "{")?;
2062        let mut fields = Vec::new();
2063        self.skip_newlines();
2064
2065        while !self.is_at_end() && !self.check(&TokenKind::RBrace) {
2066            let name = self.consume_identifier("field name")?;
2067            let optional = if self.check(&TokenKind::Question) {
2068                self.advance();
2069                true
2070            } else {
2071                false
2072            };
2073            self.consume(&TokenKind::Colon, ":")?;
2074            let type_expr = self.parse_type_expr()?;
2075            fields.push(ShapeField {
2076                name,
2077                type_expr,
2078                optional,
2079            });
2080            self.skip_newlines();
2081            if self.check(&TokenKind::Comma) {
2082                self.advance();
2083                self.skip_newlines();
2084            }
2085        }
2086
2087        self.consume(&TokenKind::RBrace, "}")?;
2088        Ok(TypeExpr::Shape(fields))
2089    }
2090
2091    fn parse_arg_list(&mut self) -> Result<Vec<SNode>, ParserError> {
2092        let mut args = Vec::new();
2093        self.skip_newlines();
2094
2095        while !self.is_at_end() && !self.check(&TokenKind::RParen) {
2096            // Check for spread: ...expr
2097            if self.check(&TokenKind::Dot) {
2098                let saved_pos = self.pos;
2099                self.advance(); // first .
2100                if self.check(&TokenKind::Dot) {
2101                    self.advance(); // second .
2102                    self.consume(&TokenKind::Dot, ".")?; // third .
2103                    let spread_start = self.tokens[saved_pos].span;
2104                    let expr = self.parse_expression()?;
2105                    args.push(spanned(
2106                        Node::Spread(Box::new(expr)),
2107                        Span::merge(spread_start, self.prev_span()),
2108                    ));
2109                } else {
2110                    // Not a spread, restore and parse as expression
2111                    self.pos = saved_pos;
2112                    args.push(self.parse_expression()?);
2113                }
2114            } else {
2115                args.push(self.parse_expression()?);
2116            }
2117            self.skip_newlines();
2118            if self.check(&TokenKind::Comma) {
2119                self.advance();
2120                self.skip_newlines();
2121            }
2122        }
2123        Ok(args)
2124    }
2125
2126    fn is_at_end(&self) -> bool {
2127        self.pos >= self.tokens.len()
2128            || matches!(self.tokens.get(self.pos), Some(t) if t.kind == TokenKind::Eof)
2129    }
2130
2131    fn current(&self) -> Option<&Token> {
2132        self.tokens.get(self.pos)
2133    }
2134
2135    fn peek_kind(&self) -> Option<&TokenKind> {
2136        self.tokens.get(self.pos + 1).map(|t| &t.kind)
2137    }
2138
2139    fn check(&self, kind: &TokenKind) -> bool {
2140        self.current()
2141            .map(|t| std::mem::discriminant(&t.kind) == std::mem::discriminant(kind))
2142            .unwrap_or(false)
2143    }
2144
2145    /// Check for a token kind, skipping past any newlines first.
2146    /// Used for binary operators like `||` and `&&` that can span lines.
2147    fn check_skip_newlines(&mut self, kind: &TokenKind) -> bool {
2148        let saved = self.pos;
2149        self.skip_newlines();
2150        if self.check(kind) {
2151            true
2152        } else {
2153            self.pos = saved;
2154            false
2155        }
2156    }
2157
2158    fn advance(&mut self) {
2159        if self.pos < self.tokens.len() {
2160            self.pos += 1;
2161        }
2162    }
2163
2164    fn consume(&mut self, kind: &TokenKind, expected: &str) -> Result<Token, ParserError> {
2165        self.skip_newlines();
2166        let tok = self.current().ok_or_else(|| self.make_error(expected))?;
2167        if std::mem::discriminant(&tok.kind) != std::mem::discriminant(kind) {
2168            return Err(self.make_error(expected));
2169        }
2170        let tok = tok.clone();
2171        self.advance();
2172        Ok(tok)
2173    }
2174
2175    fn consume_identifier(&mut self, expected: &str) -> Result<String, ParserError> {
2176        self.skip_newlines();
2177        let tok = self.current().ok_or_else(|| self.make_error(expected))?;
2178        if let TokenKind::Identifier(name) = &tok.kind {
2179            let name = name.clone();
2180            self.advance();
2181            Ok(name)
2182        } else {
2183            Err(self.make_error(expected))
2184        }
2185    }
2186
2187    /// Like `consume_identifier`, but also accepts keywords as identifiers.
2188    /// Used for property access (e.g., `obj.type`) and dict keys where
2189    /// keywords are valid member names.
2190    fn consume_identifier_or_keyword(&mut self, expected: &str) -> Result<String, ParserError> {
2191        self.skip_newlines();
2192        let tok = self.current().ok_or_else(|| self.make_error(expected))?;
2193        if let TokenKind::Identifier(name) = &tok.kind {
2194            let name = name.clone();
2195            self.advance();
2196            return Ok(name);
2197        }
2198        // Accept any keyword token as an identifier
2199        let name = match &tok.kind {
2200            TokenKind::Pipeline => "pipeline",
2201            TokenKind::Extends => "extends",
2202            TokenKind::Override => "override",
2203            TokenKind::Let => "let",
2204            TokenKind::Var => "var",
2205            TokenKind::If => "if",
2206            TokenKind::Else => "else",
2207            TokenKind::For => "for",
2208            TokenKind::In => "in",
2209            TokenKind::Match => "match",
2210            TokenKind::Retry => "retry",
2211            TokenKind::Parallel => "parallel",
2212            TokenKind::ParallelMap => "parallel_map",
2213            TokenKind::Return => "return",
2214            TokenKind::Import => "import",
2215            TokenKind::True => "true",
2216            TokenKind::False => "false",
2217            TokenKind::Nil => "nil",
2218            TokenKind::Try => "try",
2219            TokenKind::Catch => "catch",
2220            TokenKind::Throw => "throw",
2221            TokenKind::Fn => "fn",
2222            TokenKind::Spawn => "spawn",
2223            TokenKind::While => "while",
2224            TokenKind::TypeKw => "type",
2225            TokenKind::Enum => "enum",
2226            TokenKind::Struct => "struct",
2227            TokenKind::Interface => "interface",
2228            TokenKind::Pub => "pub",
2229            TokenKind::From => "from",
2230            TokenKind::Thru => "thru",
2231            TokenKind::Upto => "upto",
2232            TokenKind::Guard => "guard",
2233            TokenKind::Ask => "ask",
2234            TokenKind::Deadline => "deadline",
2235            TokenKind::Yield => "yield",
2236            TokenKind::Mutex => "mutex",
2237            TokenKind::Break => "break",
2238            TokenKind::Continue => "continue",
2239            TokenKind::Impl => "impl",
2240            _ => return Err(self.make_error(expected)),
2241        };
2242        let name = name.to_string();
2243        self.advance();
2244        Ok(name)
2245    }
2246
2247    fn skip_newlines(&mut self) {
2248        while self.pos < self.tokens.len() && self.tokens[self.pos].kind == TokenKind::Newline {
2249            self.pos += 1;
2250        }
2251    }
2252
2253    fn make_error(&self, expected: &str) -> ParserError {
2254        if let Some(tok) = self.tokens.get(self.pos) {
2255            if tok.kind == TokenKind::Eof {
2256                return ParserError::UnexpectedEof {
2257                    expected: expected.into(),
2258                };
2259            }
2260            ParserError::Unexpected {
2261                got: tok.kind.to_string(),
2262                expected: expected.into(),
2263                span: tok.span,
2264            }
2265        } else {
2266            ParserError::UnexpectedEof {
2267                expected: expected.into(),
2268            }
2269        }
2270    }
2271
2272    fn error(&self, expected: &str) -> ParserError {
2273        self.make_error(expected)
2274    }
2275}