Skip to main content

harn_parser/
parser.rs

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