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