Skip to main content

tl_parser/
lib.rs

1// ThinkingLanguage — Recursive Descent Parser
2// Licensed under MIT OR Apache-2.0
3//
4// Phase 0: Parses the core language subset:
5//   - let bindings
6//   - fn declarations
7//   - if/else, while, for
8//   - expressions (arithmetic, comparison, logical, pipe)
9//   - function calls
10//   - case expressions
11
12use tl_ast::*;
13use tl_errors::{ParserError, Span, TlError};
14use tl_lexer::{SpannedToken, Token};
15
16/// Helper to create a Stmt with span from start position to previous token
17fn make_stmt(kind: StmtKind, start: usize, end: usize) -> Stmt {
18    Stmt {
19        kind,
20        span: Span::new(start, end),
21        doc_comment: None,
22    }
23}
24
25pub struct Parser {
26    tokens: Vec<SpannedToken>,
27    pos: usize,
28    depth: usize,
29}
30
31const MAX_PARSER_DEPTH: usize = 256;
32
33impl Parser {
34    pub fn new(tokens: Vec<SpannedToken>) -> Self {
35        Self {
36            tokens,
37            pos: 0,
38            depth: 0,
39        }
40    }
41
42    fn enter_depth(&mut self) -> Result<(), TlError> {
43        self.depth += 1;
44        if self.depth > MAX_PARSER_DEPTH {
45            Err(TlError::Parser(ParserError {
46                message: "Maximum parser nesting depth (256) exceeded".to_string(),
47                span: Span::new(0, 0),
48                hint: None,
49            }))
50        } else {
51            Ok(())
52        }
53    }
54
55    fn leave_depth(&mut self) {
56        self.depth -= 1;
57    }
58
59    /// Parse a complete program
60    pub fn parse_program(&mut self) -> Result<Program, TlError> {
61        // Collect module-level //! doc comments at the start
62        let module_doc = self.consume_inner_doc_comments();
63
64        let mut statements = Vec::new();
65        while !self.is_at_end() {
66            statements.push(self.parse_statement()?);
67        }
68        Ok(Program {
69            statements,
70            module_doc,
71        })
72    }
73
74    /// Consume consecutive `///` doc comment tokens and join them
75    fn consume_doc_comments(&mut self) -> Option<String> {
76        let mut lines = Vec::new();
77        while let Token::DocComment(text) = self.peek().clone() {
78            lines.push(text.trim().to_string());
79            self.advance();
80        }
81        if lines.is_empty() {
82            None
83        } else {
84            Some(lines.join("\n"))
85        }
86    }
87
88    /// Consume consecutive `//!` inner doc comment tokens and join them
89    fn consume_inner_doc_comments(&mut self) -> Option<String> {
90        let mut lines = Vec::new();
91        while let Token::InnerDocComment(text) = self.peek().clone() {
92            lines.push(text.trim().to_string());
93            self.advance();
94        }
95        if lines.is_empty() {
96            None
97        } else {
98            Some(lines.join("\n"))
99        }
100    }
101
102    // ── Helpers ──────────────────────────────────────────────
103
104    fn peek(&self) -> &Token {
105        if self.pos >= self.tokens.len() {
106            return &Token::None_;
107        }
108        &self.tokens[self.pos].token
109    }
110
111    fn peek_span(&self) -> Span {
112        if self.pos >= self.tokens.len() {
113            return Span::new(0, 0);
114        }
115        self.tokens[self.pos].span
116    }
117
118    fn previous_span(&self) -> Span {
119        if self.tokens.is_empty() {
120            return Span::new(0, 0);
121        }
122        if self.pos > 0 {
123            self.tokens[self.pos - 1].span
124        } else {
125            self.tokens[0].span
126        }
127    }
128
129    fn advance(&mut self) -> &SpannedToken {
130        if self.pos >= self.tokens.len() {
131            // Return last token (EOF sentinel) if past end
132            return self.tokens.last().expect("token list must not be empty");
133        }
134        let tok = &self.tokens[self.pos];
135        if !self.is_at_end() {
136            self.pos += 1;
137        }
138        tok
139    }
140
141    fn is_at_end(&self) -> bool {
142        if self.tokens.is_empty() {
143            return true;
144        }
145        // The tokenizer always appends a Token::None_ (EOF) sentinel as the last token.
146        // Check position rather than token value so the `none` keyword works correctly.
147        self.pos >= self.tokens.len() - 1
148    }
149
150    fn expect(&mut self, expected: &Token) -> Result<Span, TlError> {
151        if self.peek() == expected {
152            let span = self.peek_span();
153            self.advance();
154            Ok(span)
155        } else {
156            Err(TlError::Parser(ParserError {
157                message: format!(
158                    "Expected `{}`, found `{}`",
159                    token_name(expected),
160                    self.peek()
161                ),
162                span: self.peek_span(),
163                hint: None,
164            }))
165        }
166    }
167
168    fn expect_ident(&mut self) -> Result<String, TlError> {
169        match self.peek().clone() {
170            Token::Ident(name) => {
171                self.advance();
172                Ok(name)
173            }
174            Token::Self_ => {
175                self.advance();
176                Ok("self".to_string())
177            }
178            _ => Err(TlError::Parser(ParserError {
179                message: format!("Expected identifier, found `{}`", self.peek()),
180                span: self.peek_span(),
181                hint: None,
182            })),
183        }
184    }
185
186    fn check(&self, token: &Token) -> bool {
187        self.peek() == token
188    }
189
190    fn match_token(&mut self, token: &Token) -> bool {
191        if self.check(token) {
192            self.advance();
193            true
194        } else {
195            false
196        }
197    }
198
199    // ── Statement Parsing ────────────────────────────────────
200
201    fn parse_statement(&mut self) -> Result<Stmt, TlError> {
202        // Consume any doc comments before the statement
203        let doc = self.consume_doc_comments();
204        // Skip any inner doc comments that appear mid-file
205        while matches!(self.peek(), Token::InnerDocComment(_)) {
206            self.advance();
207        }
208        let mut stmt = self.parse_statement_inner()?;
209        if let Some(ref doc_text) = doc {
210            if stmt.doc_comment.is_none() {
211                stmt.doc_comment = doc.clone();
212            }
213            // Extract @version annotation from doc comment and apply to Schema
214            if let StmtKind::Schema {
215                ref mut version,
216                ref mut parent_version,
217                ..
218            } = stmt.kind
219            {
220                for line in doc_text.lines() {
221                    let trimmed = line.trim();
222                    if let Some(rest) = trimmed.strip_prefix("@version") {
223                        if let Ok(v) = rest.trim().parse::<i64>() {
224                            *version = Some(v);
225                        }
226                    } else if let Some(rest) = trimmed.strip_prefix("@evolves")
227                        && let Ok(v) = rest.trim().parse::<i64>()
228                    {
229                        *parent_version = Some(v);
230                    }
231                }
232            }
233        }
234        Ok(stmt)
235    }
236
237    fn parse_statement_inner(&mut self) -> Result<Stmt, TlError> {
238        match self.peek() {
239            Token::Let => self.parse_let(),
240            Token::Type => self.parse_type_alias(false),
241            Token::Fn => self.parse_fn_decl(),
242            Token::Async => {
243                let start = self.peek_span().start;
244                self.advance(); // consume 'async'
245                if self.check(&Token::Fn) {
246                    let mut stmt = self.parse_fn_decl()?;
247                    if let StmtKind::FnDecl {
248                        ref mut is_async, ..
249                    } = stmt.kind
250                    {
251                        *is_async = true;
252                    }
253                    stmt.span = Span::new(start, stmt.span.end);
254                    Ok(stmt)
255                } else {
256                    Err(TlError::Parser(tl_errors::ParserError {
257                        message: "Expected 'fn' after 'async'".to_string(),
258                        span: Span::new(start, self.peek_span().end),
259                        hint: None,
260                    }))
261                }
262            }
263            Token::If => self.parse_if(),
264            Token::While => self.parse_while(),
265            Token::For => self.parse_for(),
266            Token::Return => self.parse_return(),
267            Token::Schema => self.parse_schema(),
268            Token::Struct => self.parse_struct_decl(),
269            Token::Enum => self.parse_enum_decl(),
270            Token::Impl => self.parse_impl(),
271            Token::Trait => self.parse_trait_def(),
272            Token::Model => self.parse_train(),
273            Token::Pipeline => self.parse_pipeline(),
274            Token::Agent => self.parse_agent(),
275            Token::Stream => self.parse_stream_decl(),
276            Token::Source => self.parse_source_decl(),
277            Token::Sink => self.parse_sink_decl(),
278            Token::Try => self.parse_try_catch(),
279            Token::Throw => self.parse_throw(),
280            Token::Import => self.parse_import(),
281            Token::Use => self.parse_use(false),
282            Token::Pub => self.parse_pub(),
283            Token::Mod => self.parse_mod(false),
284            Token::Test => self.parse_test(),
285            Token::Migrate => self.parse_migrate(),
286            Token::Parallel => {
287                let start = self.peek_span().start;
288                self.advance(); // consume 'parallel'
289                self.expect(&Token::For)?;
290                let name = self.expect_ident()?;
291                self.expect(&Token::In)?;
292                let iter = self.parse_expression()?;
293                self.expect(&Token::LBrace)?;
294                let body = self.parse_block_body()?;
295                self.expect(&Token::RBrace)?;
296                let end = self.previous_span().end;
297                Ok(make_stmt(
298                    StmtKind::ParallelFor { name, iter, body },
299                    start,
300                    end,
301                ))
302            }
303            Token::Break => {
304                let start = self.peek_span().start;
305                self.advance();
306                let end = self.previous_span().end;
307                Ok(make_stmt(StmtKind::Break, start, end))
308            }
309            Token::Continue => {
310                let start = self.peek_span().start;
311                self.advance();
312                let end = self.previous_span().end;
313                Ok(make_stmt(StmtKind::Continue, start, end))
314            }
315            _ => {
316                let start = self.peek_span().start;
317                let expr = self.parse_expression()?;
318                let end = self.previous_span().end;
319                Ok(make_stmt(StmtKind::Expr(expr), start, end))
320            }
321        }
322    }
323
324    /// Parse `pub` visibility modifier
325    fn parse_pub(&mut self) -> Result<Stmt, TlError> {
326        let start = self.peek_span().start;
327        self.advance(); // consume 'pub'
328        match self.peek() {
329            Token::Fn => {
330                let mut stmt = self.parse_fn_decl()?;
331                if let StmtKind::FnDecl {
332                    ref mut is_public, ..
333                } = stmt.kind
334                {
335                    *is_public = true;
336                }
337                stmt.span = Span::new(start, stmt.span.end);
338                Ok(stmt)
339            }
340            Token::Struct => {
341                let mut stmt = self.parse_struct_decl()?;
342                if let StmtKind::StructDecl {
343                    ref mut is_public, ..
344                } = stmt.kind
345                {
346                    *is_public = true;
347                }
348                stmt.span = Span::new(start, stmt.span.end);
349                Ok(stmt)
350            }
351            Token::Enum => {
352                let mut stmt = self.parse_enum_decl()?;
353                if let StmtKind::EnumDecl {
354                    ref mut is_public, ..
355                } = stmt.kind
356                {
357                    *is_public = true;
358                }
359                stmt.span = Span::new(start, stmt.span.end);
360                Ok(stmt)
361            }
362            Token::Schema => {
363                let mut stmt = self.parse_schema()?;
364                if let StmtKind::Schema {
365                    ref mut is_public, ..
366                } = stmt.kind
367                {
368                    *is_public = true;
369                }
370                stmt.span = Span::new(start, stmt.span.end);
371                Ok(stmt)
372            }
373            Token::Let => {
374                let mut stmt = self.parse_let_with_pub(true)?;
375                stmt.span = Span::new(start, stmt.span.end);
376                Ok(stmt)
377            }
378            Token::Use => {
379                let mut stmt = self.parse_use(true)?;
380                stmt.span = Span::new(start, stmt.span.end);
381                Ok(stmt)
382            }
383            Token::Mod => {
384                let mut stmt = self.parse_mod(true)?;
385                stmt.span = Span::new(start, stmt.span.end);
386                Ok(stmt)
387            }
388            Token::Trait => {
389                let mut stmt = self.parse_trait_def()?;
390                if let StmtKind::TraitDef {
391                    ref mut is_public, ..
392                } = stmt.kind
393                {
394                    *is_public = true;
395                }
396                stmt.span = Span::new(start, stmt.span.end);
397                Ok(stmt)
398            }
399            Token::Type => {
400                let mut stmt = self.parse_type_alias(true)?;
401                stmt.span = Span::new(start, stmt.span.end);
402                Ok(stmt)
403            }
404            _ => Err(TlError::Parser(ParserError {
405                message: format!(
406                    "`pub` can only be applied to fn, struct, enum, schema, let, use, mod, trait, or type, found `{}`",
407                    self.peek()
408                ),
409                span: self.peek_span(),
410                hint: None,
411            })),
412        }
413    }
414
415    /// Parse `use` import statement with dot-path syntax
416    fn parse_use(&mut self, is_public: bool) -> Result<Stmt, TlError> {
417        let start = self.peek_span().start;
418        self.advance(); // consume 'use'
419
420        // Parse dot-separated path segments
421        let mut segments = Vec::new();
422        segments.push(self.expect_ident()?);
423
424        while self.match_token(&Token::Dot) {
425            match self.peek() {
426                Token::LBrace => {
427                    // Group import: use data.transforms.{a, b}
428                    self.advance(); // consume '{'
429                    let mut names = Vec::new();
430                    names.push(self.expect_ident()?);
431                    while self.match_token(&Token::Comma) {
432                        if self.check(&Token::RBrace) {
433                            break; // trailing comma
434                        }
435                        names.push(self.expect_ident()?);
436                    }
437                    self.expect(&Token::RBrace)?;
438                    let end = self.previous_span().end;
439                    return Ok(make_stmt(
440                        StmtKind::Use {
441                            item: UseItem::Group(segments, names),
442                            is_public,
443                        },
444                        start,
445                        end,
446                    ));
447                }
448                Token::Star => {
449                    // Wildcard import: use data.transforms.*
450                    self.advance(); // consume '*'
451                    let end = self.previous_span().end;
452                    return Ok(make_stmt(
453                        StmtKind::Use {
454                            item: UseItem::Wildcard(segments),
455                            is_public,
456                        },
457                        start,
458                        end,
459                    ));
460                }
461                Token::Ident(_) => {
462                    segments.push(self.expect_ident()?);
463                }
464                _ => {
465                    return Err(TlError::Parser(ParserError {
466                        message: format!(
467                            "Expected identifier, `{{`, or `*` after `.` in use path, found `{}`",
468                            self.peek()
469                        ),
470                        span: self.peek_span(),
471                        hint: None,
472                    }));
473                }
474            }
475        }
476
477        // Check for alias: use data.postgres as pg
478        if self.match_token(&Token::As) {
479            let alias = self.expect_ident()?;
480            let end = self.previous_span().end;
481            return Ok(make_stmt(
482                StmtKind::Use {
483                    item: UseItem::Aliased(segments, alias),
484                    is_public,
485                },
486                start,
487                end,
488            ));
489        }
490
491        // Single import
492        let end = self.previous_span().end;
493        Ok(make_stmt(
494            StmtKind::Use {
495                item: UseItem::Single(segments),
496                is_public,
497            },
498            start,
499            end,
500        ))
501    }
502
503    /// Parse `mod name` declaration
504    fn parse_mod(&mut self, is_public: bool) -> Result<Stmt, TlError> {
505        let start = self.peek_span().start;
506        self.advance(); // consume 'mod'
507        let name = self.expect_ident()?;
508        let end = self.previous_span().end;
509        Ok(make_stmt(StmtKind::ModDecl { name, is_public }, start, end))
510    }
511
512    /// Parse `schema Name { field: type, ... }`
513    /// Supports `@version N` in doc comment and field-level `///` doc comments with `= default`
514    fn parse_schema(&mut self) -> Result<Stmt, TlError> {
515        let start = self.peek_span().start;
516        self.advance(); // consume 'schema'
517        let name = self.expect_ident()?;
518        self.expect(&Token::LBrace)?;
519        let mut fields = Vec::new();
520        while !self.check(&Token::RBrace) && !self.is_at_end() {
521            // Consume field-level doc comments
522            let field_doc = self.consume_doc_comments();
523            let field_name = self.expect_ident()?;
524            self.expect(&Token::Colon)?;
525            let type_ann = self.parse_type()?;
526            // Optional default value: `= expr`
527            let default_value = if self.match_token(&Token::Assign) {
528                Some(self.parse_expression()?)
529            } else {
530                None
531            };
532            self.match_token(&Token::Comma); // optional trailing comma
533            // Parse security annotations from doc comment
534            let annotations = parse_field_annotations(field_doc.as_deref());
535            fields.push(SchemaField {
536                name: field_name,
537                type_ann,
538                doc_comment: field_doc,
539                default_value,
540                annotations,
541            });
542        }
543        self.expect(&Token::RBrace)?;
544        let end = self.previous_span().end;
545        Ok(make_stmt(
546            StmtKind::Schema {
547                name,
548                fields,
549                is_public: false,
550                version: None,
551                parent_version: None,
552            },
553            start,
554            end,
555        ))
556    }
557
558    /// Parse `migrate SchemaName from V1 to V2 { ops... }`
559    fn parse_migrate(&mut self) -> Result<Stmt, TlError> {
560        let start = self.peek_span().start;
561        self.advance(); // consume 'migrate'
562        let schema_name = self.expect_ident()?;
563
564        // expect 'from'
565        match self.peek() {
566            Token::Ident(s) if s == "from" => {
567                self.advance();
568            }
569            _ => {
570                return Err(TlError::Parser(ParserError {
571                    message: "Expected `from` after schema name in migrate statement".to_string(),
572                    span: self.peek_span(),
573                    hint: Some("migrate SchemaName from V1 to V2 { ... }".to_string()),
574                }));
575            }
576        }
577        let from_version = match self.peek() {
578            Token::Int(n) => {
579                let n = *n;
580                self.advance();
581                n
582            }
583            _ => {
584                return Err(TlError::Parser(ParserError {
585                    message: "Expected version number after `from`".to_string(),
586                    span: self.peek_span(),
587                    hint: None,
588                }));
589            }
590        };
591
592        // expect 'to'
593        match self.peek() {
594            Token::Ident(s) if s == "to" => {
595                self.advance();
596            }
597            _ => {
598                return Err(TlError::Parser(ParserError {
599                    message: "Expected `to` after from-version in migrate statement".to_string(),
600                    span: self.peek_span(),
601                    hint: Some("migrate SchemaName from V1 to V2 { ... }".to_string()),
602                }));
603            }
604        }
605        let to_version = match self.peek() {
606            Token::Int(n) => {
607                let n = *n;
608                self.advance();
609                n
610            }
611            _ => {
612                return Err(TlError::Parser(ParserError {
613                    message: "Expected version number after `to`".to_string(),
614                    span: self.peek_span(),
615                    hint: None,
616                }));
617            }
618        };
619
620        self.expect(&Token::LBrace)?;
621        let mut operations = Vec::new();
622        while !self.check(&Token::RBrace) && !self.is_at_end() {
623            operations.push(self.parse_migrate_op()?);
624        }
625        self.expect(&Token::RBrace)?;
626        let end = self.previous_span().end;
627        Ok(make_stmt(
628            StmtKind::Migrate {
629                schema_name,
630                from_version,
631                to_version,
632                operations,
633            },
634            start,
635            end,
636        ))
637    }
638
639    /// Parse a single migration operation like `add_column(name: type, default: expr)`
640    fn parse_migrate_op(&mut self) -> Result<MigrateOp, TlError> {
641        let op_name = self.expect_ident()?;
642        self.expect(&Token::LParen)?;
643        let result = match op_name.as_str() {
644            "add_column" => {
645                let name = self.expect_ident()?;
646                self.expect(&Token::Colon)?;
647                let type_ann = self.parse_type()?;
648                let default = if self.match_token(&Token::Comma) {
649                    // Check for `default:` named arg
650                    if matches!(self.peek(), Token::Ident(s) if s == "default") {
651                        self.advance(); // consume 'default'
652                        self.expect(&Token::Colon)?;
653                        Some(self.parse_expression()?)
654                    } else {
655                        None
656                    }
657                } else {
658                    None
659                };
660                MigrateOp::AddColumn { name, type_ann, default }
661            }
662            "drop_column" => {
663                let name = self.expect_ident()?;
664                MigrateOp::DropColumn { name }
665            }
666            "rename_column" => {
667                let from = self.expect_ident()?;
668                self.expect(&Token::Comma)?;
669                let to = self.expect_ident()?;
670                MigrateOp::RenameColumn { from, to }
671            }
672            "alter_type" => {
673                let column = self.expect_ident()?;
674                self.expect(&Token::Comma)?;
675                let new_type = self.parse_type()?;
676                MigrateOp::AlterType { column, new_type }
677            }
678            "add_constraint" => {
679                let column = self.expect_ident()?;
680                self.expect(&Token::Comma)?;
681                let constraint = self.expect_ident()?;
682                MigrateOp::AddConstraint { column, constraint }
683            }
684            "drop_constraint" => {
685                let column = self.expect_ident()?;
686                self.expect(&Token::Comma)?;
687                let constraint = self.expect_ident()?;
688                MigrateOp::DropConstraint { column, constraint }
689            }
690            _ => return Err(TlError::Parser(ParserError {
691                message: format!("Unknown migration operation: `{op_name}`"),
692                span: self.peek_span(),
693                hint: Some("Valid operations: add_column, drop_column, rename_column, alter_type, add_constraint, drop_constraint".to_string()),
694            })),
695        };
696        self.expect(&Token::RParen)?;
697        Ok(result)
698    }
699
700    /// Parse `model <name> = train <algorithm> { key: value, ... }`
701    fn parse_train(&mut self) -> Result<Stmt, TlError> {
702        let start = self.peek_span().start;
703        self.advance(); // consume 'model'
704        let name = self.expect_ident()?;
705        self.expect(&Token::Assign)?;
706        self.expect(&Token::Train)?;
707        let algorithm = self.expect_ident()?;
708        self.expect(&Token::LBrace)?;
709        let mut config = Vec::new();
710        while !self.check(&Token::RBrace) && !self.is_at_end() {
711            let key = self.expect_ident()?;
712            self.expect(&Token::Colon)?;
713            let value = self.parse_expression()?;
714            self.match_token(&Token::Comma); // optional trailing comma
715            config.push((key, value));
716        }
717        self.expect(&Token::RBrace)?;
718        let end = self.previous_span().end;
719        Ok(make_stmt(
720            StmtKind::Train {
721                name,
722                algorithm,
723                config,
724            },
725            start,
726            end,
727        ))
728    }
729
730    /// Parse `pipeline NAME { schedule: "...", timeout: "...", retries: N, extract { ... } transform { ... } load { ... } on_failure { ... } on_success { ... } }`
731    fn parse_pipeline(&mut self) -> Result<Stmt, TlError> {
732        let start = self.peek_span().start;
733        self.advance(); // consume 'pipeline'
734        let name = self.expect_ident()?;
735        self.expect(&Token::LBrace)?;
736
737        let mut extract = Vec::new();
738        let mut transform = Vec::new();
739        let mut load = Vec::new();
740        let mut schedule = None;
741        let mut timeout = None;
742        let mut retries = None;
743        let mut on_failure = None;
744        let mut on_success = None;
745
746        while !self.check(&Token::RBrace) && !self.is_at_end() {
747            match self.peek() {
748                Token::Extract => {
749                    self.advance();
750                    self.expect(&Token::LBrace)?;
751                    extract = self.parse_block_body()?;
752                    self.expect(&Token::RBrace)?;
753                }
754                Token::Transform => {
755                    self.advance();
756                    self.expect(&Token::LBrace)?;
757                    transform = self.parse_block_body()?;
758                    self.expect(&Token::RBrace)?;
759                }
760                Token::Load => {
761                    self.advance();
762                    self.expect(&Token::LBrace)?;
763                    load = self.parse_block_body()?;
764                    self.expect(&Token::RBrace)?;
765                }
766                Token::Ident(s) if s == "schedule" => {
767                    self.advance();
768                    self.expect(&Token::Colon)?;
769                    if let Token::String(s) = self.peek().clone() {
770                        self.advance();
771                        schedule = Some(s);
772                    } else {
773                        schedule = Some(self.parse_duration_literal()?);
774                    }
775                    self.match_token(&Token::Comma);
776                }
777                Token::Ident(s) if s == "timeout" => {
778                    self.advance();
779                    self.expect(&Token::Colon)?;
780                    if let Token::String(s) = self.peek().clone() {
781                        self.advance();
782                        timeout = Some(s);
783                    } else {
784                        timeout = Some(self.parse_duration_literal()?);
785                    }
786                    self.match_token(&Token::Comma);
787                }
788                Token::Ident(s) if s == "retries" => {
789                    self.advance();
790                    self.expect(&Token::Colon)?;
791                    if let Token::Int(n) = self.peek().clone() {
792                        self.advance();
793                        retries = Some(n);
794                    } else {
795                        return Err(TlError::Parser(ParserError {
796                            message: "Expected integer for retries".to_string(),
797                            span: self.peek_span(),
798                            hint: None,
799                        }));
800                    }
801                    self.match_token(&Token::Comma);
802                }
803                Token::Ident(s) if s == "on_failure" => {
804                    self.advance();
805                    self.expect(&Token::LBrace)?;
806                    on_failure = Some(self.parse_block_body()?);
807                    self.expect(&Token::RBrace)?;
808                }
809                Token::Ident(s) if s == "on_success" => {
810                    self.advance();
811                    self.expect(&Token::LBrace)?;
812                    on_success = Some(self.parse_block_body()?);
813                    self.expect(&Token::RBrace)?;
814                }
815                _ => {
816                    return Err(TlError::Parser(ParserError {
817                        message: format!(
818                            "Unexpected token in pipeline block: `{}`",
819                            self.peek()
820                        ),
821                        span: self.peek_span(),
822                        hint: Some("Expected extract, transform, load, schedule, timeout, retries, on_failure, or on_success".into()),
823                    }));
824                }
825            }
826        }
827        self.expect(&Token::RBrace)?;
828        let end = self.previous_span().end;
829        Ok(make_stmt(
830            StmtKind::Pipeline {
831                name,
832                extract,
833                transform,
834                load,
835                schedule,
836                timeout,
837                retries,
838                on_failure,
839                on_success,
840            },
841            start,
842            end,
843        ))
844    }
845
846    /// Convert a keyword token to a string name when used as a map key.
847    fn token_as_key_name(token: &Token) -> Option<String> {
848        match token {
849            Token::Type => Some("type".into()),
850            Token::Model => Some("model".into()),
851            Token::Source => Some("source".into()),
852            Token::Sink => Some("sink".into()),
853            Token::True => Some("true".into()),
854            Token::False => Some("false".into()),
855            Token::None_ => Some("none".into()),
856            Token::Match => Some("match".into()),
857            Token::If => Some("if".into()),
858            _ => None,
859        }
860    }
861
862    /// Parse a JSON-like map literal: `{ key: value, ... }` → `Expr::Map`
863    /// Used in agent tool definitions where `{ description: "...", parameters: {...} }` syntax is needed.
864    fn parse_map_literal(&mut self) -> Result<Expr, TlError> {
865        self.expect(&Token::LBrace)?;
866        let mut pairs = Vec::new();
867        while !self.check(&Token::RBrace) && !self.is_at_end() {
868            // Key can be an identifier, string, or keyword used as key
869            let key = match self.peek().clone() {
870                Token::Ident(s) => {
871                    self.advance();
872                    Expr::String(s)
873                }
874                Token::String(s) => {
875                    self.advance();
876                    Expr::String(s)
877                }
878                // Allow keywords as map keys (e.g., "type", "model", etc.)
879                ref t if Self::token_as_key_name(t).is_some() => {
880                    let name = Self::token_as_key_name(t).unwrap();
881                    self.advance();
882                    Expr::String(name)
883                }
884                _ => {
885                    return Err(TlError::Parser(ParserError {
886                        message: "Expected identifier or string key in map".to_string(),
887                        span: self.peek_span(),
888                        hint: None,
889                    }));
890                }
891            };
892            self.expect(&Token::Colon)?;
893            // Value: recurse for nested maps, otherwise parse expression
894            let value = if self.check(&Token::LBrace) {
895                self.parse_map_literal()?
896            } else if self.check(&Token::LBracket) {
897                // Allow JSON-like arrays too
898                self.parse_primary()?
899            } else {
900                self.parse_expression()?
901            };
902            pairs.push((key, value));
903            self.match_token(&Token::Comma);
904        }
905        self.expect(&Token::RBrace)?;
906        Ok(Expr::Map(pairs))
907    }
908
909    /// Parse `agent NAME { model: "...", system: "...", tools { ... }, max_turns: N }`
910    fn parse_agent(&mut self) -> Result<Stmt, TlError> {
911        let start = self.peek_span().start;
912        self.advance(); // consume 'agent'
913        let name = self.expect_ident()?;
914        self.expect(&Token::LBrace)?;
915
916        let mut model = None;
917        let mut system_prompt = None;
918        let mut tools: Vec<(String, Expr)> = Vec::new();
919        let mut max_turns = None;
920        let mut temperature = None;
921        let mut max_tokens = None;
922        let mut base_url = None;
923        let mut api_key = None;
924        let mut output_format = None;
925        let mut on_tool_call = None;
926        let mut on_complete = None;
927
928        while !self.check(&Token::RBrace) && !self.is_at_end() {
929            match self.peek().clone() {
930                Token::Model => {
931                    self.advance();
932                    self.expect(&Token::Colon)?;
933                    if let Token::String(s) = self.peek().clone() {
934                        self.advance();
935                        model = Some(s);
936                    } else {
937                        return Err(TlError::Parser(ParserError {
938                            message: "Expected string for model".to_string(),
939                            span: self.peek_span(),
940                            hint: None,
941                        }));
942                    }
943                    self.match_token(&Token::Comma);
944                }
945                Token::Ident(s) if s == "system" => {
946                    self.advance();
947                    self.expect(&Token::Colon)?;
948                    if let Token::String(s) = self.peek().clone() {
949                        self.advance();
950                        system_prompt = Some(s);
951                    } else {
952                        return Err(TlError::Parser(ParserError {
953                            message: "Expected string for system prompt".to_string(),
954                            span: self.peek_span(),
955                            hint: None,
956                        }));
957                    }
958                    self.match_token(&Token::Comma);
959                }
960                Token::Ident(s) if s == "tools" => {
961                    self.advance();
962                    self.expect(&Token::LBrace)?;
963                    // Parse tool definitions: fn_name: { description: "...", parameters: {...} }
964                    while !self.check(&Token::RBrace) && !self.is_at_end() {
965                        let tool_name = self.expect_ident()?;
966                        self.expect(&Token::Colon)?;
967                        let tool_def = self.parse_map_literal()?;
968                        tools.push((tool_name, tool_def));
969                        self.match_token(&Token::Comma);
970                    }
971                    self.expect(&Token::RBrace)?;
972                    self.match_token(&Token::Comma);
973                }
974                Token::Ident(s) if s == "max_turns" => {
975                    self.advance();
976                    self.expect(&Token::Colon)?;
977                    if let Token::Int(n) = self.peek().clone() {
978                        self.advance();
979                        max_turns = Some(n);
980                    } else {
981                        return Err(TlError::Parser(ParserError {
982                            message: "Expected integer for max_turns".to_string(),
983                            span: self.peek_span(),
984                            hint: None,
985                        }));
986                    }
987                    self.match_token(&Token::Comma);
988                }
989                Token::Ident(s) if s == "temperature" => {
990                    self.advance();
991                    self.expect(&Token::Colon)?;
992                    match self.peek().clone() {
993                        Token::Float(f) => {
994                            self.advance();
995                            temperature = Some(f);
996                        }
997                        Token::Int(n) => {
998                            self.advance();
999                            temperature = Some(n as f64);
1000                        }
1001                        _ => {
1002                            return Err(TlError::Parser(ParserError {
1003                                message: "Expected number for temperature".to_string(),
1004                                span: self.peek_span(),
1005                                hint: None,
1006                            }));
1007                        }
1008                    }
1009                    self.match_token(&Token::Comma);
1010                }
1011                Token::Ident(s) if s == "max_tokens" => {
1012                    self.advance();
1013                    self.expect(&Token::Colon)?;
1014                    if let Token::Int(n) = self.peek().clone() {
1015                        self.advance();
1016                        max_tokens = Some(n);
1017                    } else {
1018                        return Err(TlError::Parser(ParserError {
1019                            message: "Expected integer for max_tokens".to_string(),
1020                            span: self.peek_span(),
1021                            hint: None,
1022                        }));
1023                    }
1024                    self.match_token(&Token::Comma);
1025                }
1026                Token::Ident(s) if s == "base_url" => {
1027                    self.advance();
1028                    self.expect(&Token::Colon)?;
1029                    if let Token::String(s) = self.peek().clone() {
1030                        self.advance();
1031                        base_url = Some(s);
1032                    } else {
1033                        return Err(TlError::Parser(ParserError {
1034                            message: "Expected string for base_url".to_string(),
1035                            span: self.peek_span(),
1036                            hint: None,
1037                        }));
1038                    }
1039                    self.match_token(&Token::Comma);
1040                }
1041                Token::Ident(s) if s == "api_key" => {
1042                    self.advance();
1043                    self.expect(&Token::Colon)?;
1044                    if let Token::String(s) = self.peek().clone() {
1045                        self.advance();
1046                        api_key = Some(s);
1047                    } else {
1048                        return Err(TlError::Parser(ParserError {
1049                            message: "Expected string for api_key".to_string(),
1050                            span: self.peek_span(),
1051                            hint: None,
1052                        }));
1053                    }
1054                    self.match_token(&Token::Comma);
1055                }
1056                Token::Ident(s) if s == "output_format" => {
1057                    self.advance();
1058                    self.expect(&Token::Colon)?;
1059                    if let Token::String(s) = self.peek().clone() {
1060                        output_format = Some(s);
1061                        self.advance();
1062                    } else {
1063                        return Err(TlError::Parser(ParserError {
1064                            message: "Expected string for output_format".to_string(),
1065                            span: self.peek_span(),
1066                            hint: None,
1067                        }));
1068                    }
1069                    self.match_token(&Token::Comma);
1070                }
1071                Token::Ident(s) if s == "on_tool_call" => {
1072                    self.advance();
1073                    self.expect(&Token::LBrace)?;
1074                    on_tool_call = Some(self.parse_block_body()?);
1075                    self.expect(&Token::RBrace)?;
1076                }
1077                Token::Ident(s) if s == "on_complete" => {
1078                    self.advance();
1079                    self.expect(&Token::LBrace)?;
1080                    on_complete = Some(self.parse_block_body()?);
1081                    self.expect(&Token::RBrace)?;
1082                }
1083                _ => {
1084                    return Err(TlError::Parser(ParserError {
1085                        message: format!(
1086                            "Unexpected token in agent block: `{}`",
1087                            self.peek()
1088                        ),
1089                        span: self.peek_span(),
1090                        hint: Some("Expected model, system, tools, max_turns, temperature, max_tokens, base_url, api_key, on_tool_call, or on_complete".into()),
1091                    }));
1092                }
1093            }
1094        }
1095        self.expect(&Token::RBrace)?;
1096        let end = self.previous_span().end;
1097
1098        let model = model.ok_or_else(|| {
1099            TlError::Parser(ParserError {
1100                message: "Agent definition requires a 'model' field".to_string(),
1101                span: Span::new(start, end),
1102                hint: None,
1103            })
1104        })?;
1105
1106        Ok(make_stmt(
1107            StmtKind::Agent {
1108                name,
1109                model,
1110                system_prompt,
1111                tools,
1112                max_turns,
1113                temperature,
1114                max_tokens,
1115                base_url,
1116                api_key,
1117                output_format,
1118                on_tool_call,
1119                on_complete,
1120            },
1121            start,
1122            end,
1123        ))
1124    }
1125
1126    /// Parse `stream NAME { source: expr, window: spec, watermark: "duration", transform: { ... }, sink: expr }`
1127    fn parse_stream_decl(&mut self) -> Result<Stmt, TlError> {
1128        let start = self.peek_span().start;
1129        self.advance(); // consume 'stream'
1130        let name = self.expect_ident()?;
1131        self.expect(&Token::LBrace)?;
1132
1133        let mut source = None;
1134        let mut transform = Vec::new();
1135        let mut sink = None;
1136        let mut window = None;
1137        let mut watermark = None;
1138
1139        while !self.check(&Token::RBrace) && !self.is_at_end() {
1140            match self.peek() {
1141                Token::Source => {
1142                    self.advance();
1143                    self.expect(&Token::Colon)?;
1144                    source = Some(self.parse_expression()?);
1145                    self.match_token(&Token::Comma);
1146                }
1147                Token::Sink => {
1148                    self.advance();
1149                    self.expect(&Token::Colon)?;
1150                    sink = Some(self.parse_expression()?);
1151                    self.match_token(&Token::Comma);
1152                }
1153                Token::Transform => {
1154                    self.advance();
1155                    self.expect(&Token::Colon)?;
1156                    self.expect(&Token::LBrace)?;
1157                    transform = self.parse_block_body()?;
1158                    self.expect(&Token::RBrace)?;
1159                    self.match_token(&Token::Comma);
1160                }
1161                Token::Ident(s) if s == "window" => {
1162                    self.advance();
1163                    self.expect(&Token::Colon)?;
1164                    window = Some(self.parse_window_spec()?);
1165                    self.match_token(&Token::Comma);
1166                }
1167                Token::Ident(s) if s == "watermark" => {
1168                    self.advance();
1169                    self.expect(&Token::Colon)?;
1170                    if let Token::String(s) = self.peek().clone() {
1171                        self.advance();
1172                        watermark = Some(s);
1173                    } else {
1174                        watermark = Some(self.parse_duration_literal()?);
1175                    }
1176                    self.match_token(&Token::Comma);
1177                }
1178                _ => {
1179                    return Err(TlError::Parser(ParserError {
1180                        message: format!("Unexpected token in stream block: `{}`", self.peek()),
1181                        span: self.peek_span(),
1182                        hint: Some("Expected source, sink, transform, window, or watermark".into()),
1183                    }));
1184                }
1185            }
1186        }
1187        self.expect(&Token::RBrace)?;
1188
1189        let source = source.ok_or_else(|| {
1190            TlError::Parser(ParserError {
1191                message: "Stream declaration requires a source".to_string(),
1192                span: self.peek_span(),
1193                hint: None,
1194            })
1195        })?;
1196
1197        let end = self.previous_span().end;
1198        Ok(make_stmt(
1199            StmtKind::StreamDecl {
1200                name,
1201                source,
1202                transform,
1203                sink,
1204                window,
1205                watermark,
1206            },
1207            start,
1208            end,
1209        ))
1210    }
1211
1212    /// Parse `source NAME = connector TYPE { key: value, ... }`
1213    fn parse_source_decl(&mut self) -> Result<Stmt, TlError> {
1214        let start = self.peek_span().start;
1215        self.advance(); // consume 'source'
1216        let name = self.expect_ident()?;
1217        self.expect(&Token::Assign)?;
1218        self.expect(&Token::Connector)?;
1219        let connector_type = self.expect_ident()?;
1220        self.expect(&Token::LBrace)?;
1221        let mut config = Vec::new();
1222        while !self.check(&Token::RBrace) && !self.is_at_end() {
1223            let key = self.expect_ident()?;
1224            self.expect(&Token::Colon)?;
1225            let value = self.parse_expression()?;
1226            self.match_token(&Token::Comma);
1227            config.push((key, value));
1228        }
1229        self.expect(&Token::RBrace)?;
1230        let end = self.previous_span().end;
1231        Ok(make_stmt(
1232            StmtKind::SourceDecl {
1233                name,
1234                connector_type,
1235                config,
1236            },
1237            start,
1238            end,
1239        ))
1240    }
1241
1242    /// Parse `sink NAME = connector TYPE { key: value, ... }`
1243    fn parse_sink_decl(&mut self) -> Result<Stmt, TlError> {
1244        let start = self.peek_span().start;
1245        self.advance(); // consume 'sink'
1246        let name = self.expect_ident()?;
1247        self.expect(&Token::Assign)?;
1248        self.expect(&Token::Connector)?;
1249        let connector_type = self.expect_ident()?;
1250        self.expect(&Token::LBrace)?;
1251        let mut config = Vec::new();
1252        while !self.check(&Token::RBrace) && !self.is_at_end() {
1253            let key = self.expect_ident()?;
1254            self.expect(&Token::Colon)?;
1255            let value = self.parse_expression()?;
1256            self.match_token(&Token::Comma);
1257            config.push((key, value));
1258        }
1259        self.expect(&Token::RBrace)?;
1260        let end = self.previous_span().end;
1261        Ok(make_stmt(
1262            StmtKind::SinkDecl {
1263                name,
1264                connector_type,
1265                config,
1266            },
1267            start,
1268            end,
1269        ))
1270    }
1271
1272    /// Parse a window specification: `tumbling(DURATION)`, `sliding(DURATION, DURATION)`, `session(DURATION)`
1273    fn parse_window_spec(&mut self) -> Result<WindowSpec, TlError> {
1274        let kind = self.expect_ident()?;
1275        self.expect(&Token::LParen)?;
1276        match kind.as_str() {
1277            "tumbling" => {
1278                let dur = self.parse_duration_literal()?;
1279                self.expect(&Token::RParen)?;
1280                Ok(WindowSpec::Tumbling(dur))
1281            }
1282            "sliding" => {
1283                let window = self.parse_duration_literal()?;
1284                self.expect(&Token::Comma)?;
1285                let slide = self.parse_duration_literal()?;
1286                self.expect(&Token::RParen)?;
1287                Ok(WindowSpec::Sliding(window, slide))
1288            }
1289            "session" => {
1290                let gap = self.parse_duration_literal()?;
1291                self.expect(&Token::RParen)?;
1292                Ok(WindowSpec::Session(gap))
1293            }
1294            _ => Err(TlError::Parser(ParserError {
1295                message: format!("Unknown window type: `{kind}`"),
1296                span: self.peek_span(),
1297                hint: Some("Expected tumbling, sliding, or session".into()),
1298            })),
1299        }
1300    }
1301
1302    /// Parse a duration literal token (e.g., `5m`, `30s`, `100ms`) into a string like "5m"
1303    fn parse_duration_literal(&mut self) -> Result<String, TlError> {
1304        match self.peek().clone() {
1305            Token::DurationMs(n) => {
1306                self.advance();
1307                Ok(format!("{n}ms"))
1308            }
1309            Token::DurationS(n) => {
1310                self.advance();
1311                Ok(format!("{n}s"))
1312            }
1313            Token::DurationM(n) => {
1314                self.advance();
1315                Ok(format!("{n}m"))
1316            }
1317            Token::DurationH(n) => {
1318                self.advance();
1319                Ok(format!("{n}h"))
1320            }
1321            Token::DurationD(n) => {
1322                self.advance();
1323                Ok(format!("{n}d"))
1324            }
1325            Token::String(s) => {
1326                self.advance();
1327                Ok(s)
1328            }
1329            _ => Err(TlError::Parser(ParserError {
1330                message: format!("Expected duration literal, found `{}`", self.peek()),
1331                span: self.peek_span(),
1332                hint: Some("Expected a duration like 5m, 30s, 100ms, 1h, or 1d".into()),
1333            })),
1334        }
1335    }
1336
1337    fn parse_let(&mut self) -> Result<Stmt, TlError> {
1338        self.parse_let_with_pub(false)
1339    }
1340
1341    fn parse_let_with_pub(&mut self, is_public: bool) -> Result<Stmt, TlError> {
1342        let start = self.peek_span().start;
1343        self.advance(); // consume 'let'
1344        let mutable = self.match_token(&Token::Mut);
1345        // Check for destructuring patterns: let { x, y } = expr or let [a, b] = expr
1346        // Also Ident::Variant for enum destructure
1347        match self.peek() {
1348            Token::LBrace | Token::LBracket => {
1349                let pattern = self.parse_pattern()?;
1350                self.expect(&Token::Assign)?;
1351                let value = self.parse_expression()?;
1352                let end = self.previous_span().end;
1353                return Ok(make_stmt(
1354                    StmtKind::LetDestructure {
1355                        pattern,
1356                        mutable,
1357                        value,
1358                        is_public,
1359                    },
1360                    start,
1361                    end,
1362                ));
1363            }
1364            Token::Ident(_) => {
1365                // Lookahead: if Ident::ColonColon, it's enum destructure
1366                if self.pos + 1 < self.tokens.len()
1367                    && matches!(self.tokens[self.pos + 1].token, Token::ColonColon)
1368                {
1369                    let pattern = self.parse_pattern()?;
1370                    self.expect(&Token::Assign)?;
1371                    let value = self.parse_expression()?;
1372                    let end = self.previous_span().end;
1373                    return Ok(make_stmt(
1374                        StmtKind::LetDestructure {
1375                            pattern,
1376                            mutable,
1377                            value,
1378                            is_public,
1379                        },
1380                        start,
1381                        end,
1382                    ));
1383                }
1384                // Also check for Named struct: Ident { ... }
1385                if self.pos + 1 < self.tokens.len()
1386                    && matches!(self.tokens[self.pos + 1].token, Token::LBrace)
1387                    && self.pos + 2 < self.tokens.len()
1388                {
1389                    let third = &self.tokens[self.pos + 2].token;
1390                    if matches!(third, Token::Ident(_) | Token::RBrace) {
1391                        let pattern = self.parse_pattern()?;
1392                        self.expect(&Token::Assign)?;
1393                        let value = self.parse_expression()?;
1394                        let end = self.previous_span().end;
1395                        return Ok(make_stmt(
1396                            StmtKind::LetDestructure {
1397                                pattern,
1398                                mutable,
1399                                value,
1400                                is_public,
1401                            },
1402                            start,
1403                            end,
1404                        ));
1405                    }
1406                }
1407            }
1408            _ => {}
1409        }
1410        let name = self.expect_ident()?;
1411        let type_ann = if self.match_token(&Token::Colon) {
1412            Some(self.parse_type()?)
1413        } else {
1414            None
1415        };
1416        self.expect(&Token::Assign)?;
1417        let value = self.parse_expression()?;
1418        let end = self.previous_span().end;
1419        Ok(make_stmt(
1420            StmtKind::Let {
1421                name,
1422                mutable,
1423                type_ann,
1424                value,
1425                is_public,
1426            },
1427            start,
1428            end,
1429        ))
1430    }
1431
1432    fn parse_fn_decl(&mut self) -> Result<Stmt, TlError> {
1433        let start = self.peek_span().start;
1434        self.advance(); // consume 'fn'
1435        let name = self.expect_ident()?;
1436
1437        // Optional type parameters with inline bounds: <T: Comparable, U>
1438        let (type_params, mut bounds) = self.parse_optional_type_params_with_bounds()?;
1439
1440        self.expect(&Token::LParen)?;
1441        let params = self.parse_param_list()?;
1442        self.expect(&Token::RParen)?;
1443        let return_type = if self.match_token(&Token::Arrow) {
1444            Some(self.parse_type()?)
1445        } else {
1446            None
1447        };
1448
1449        // Optional where clause: `where T: Comparable + Hashable`
1450        if self.check(&Token::Where) {
1451            self.advance(); // consume 'where'
1452            let where_bounds = self.parse_where_clause()?;
1453            bounds.extend(where_bounds);
1454        }
1455
1456        self.expect(&Token::LBrace)?;
1457        let body = self.parse_block_body()?;
1458        self.expect(&Token::RBrace)?;
1459        let is_generator = body_contains_yield(&body);
1460        let end = self.previous_span().end;
1461        Ok(make_stmt(
1462            StmtKind::FnDecl {
1463                name,
1464                type_params,
1465                params,
1466                return_type,
1467                bounds,
1468                body,
1469                is_generator,
1470                is_public: false,
1471                is_async: false,
1472            },
1473            start,
1474            end,
1475        ))
1476    }
1477
1478    fn parse_if(&mut self) -> Result<Stmt, TlError> {
1479        let start = self.peek_span().start;
1480        self.advance(); // consume 'if'
1481        let condition = self.parse_expression()?;
1482        self.expect(&Token::LBrace)?;
1483        let then_body = self.parse_block_body()?;
1484        self.expect(&Token::RBrace)?;
1485
1486        let mut else_ifs = Vec::new();
1487        let mut else_body = None;
1488
1489        while self.match_token(&Token::Else) {
1490            if self.match_token(&Token::If) {
1491                let cond = self.parse_expression()?;
1492                self.expect(&Token::LBrace)?;
1493                let body = self.parse_block_body()?;
1494                self.expect(&Token::RBrace)?;
1495                else_ifs.push((cond, body));
1496            } else {
1497                self.expect(&Token::LBrace)?;
1498                else_body = Some(self.parse_block_body()?);
1499                self.expect(&Token::RBrace)?;
1500                break;
1501            }
1502        }
1503
1504        let end = self.previous_span().end;
1505        Ok(make_stmt(
1506            StmtKind::If {
1507                condition,
1508                then_body,
1509                else_ifs,
1510                else_body,
1511            },
1512            start,
1513            end,
1514        ))
1515    }
1516
1517    fn parse_while(&mut self) -> Result<Stmt, TlError> {
1518        let start = self.peek_span().start;
1519        self.advance(); // consume 'while'
1520        let condition = self.parse_expression()?;
1521        self.expect(&Token::LBrace)?;
1522        let body = self.parse_block_body()?;
1523        self.expect(&Token::RBrace)?;
1524        let end = self.previous_span().end;
1525        Ok(make_stmt(StmtKind::While { condition, body }, start, end))
1526    }
1527
1528    fn parse_for(&mut self) -> Result<Stmt, TlError> {
1529        let start = self.peek_span().start;
1530        self.advance(); // consume 'for'
1531        let name = self.expect_ident()?;
1532        self.expect(&Token::In)?;
1533        let iter = self.parse_expression()?;
1534        self.expect(&Token::LBrace)?;
1535        let body = self.parse_block_body()?;
1536        self.expect(&Token::RBrace)?;
1537        let end = self.previous_span().end;
1538        Ok(make_stmt(StmtKind::For { name, iter, body }, start, end))
1539    }
1540
1541    fn parse_return(&mut self) -> Result<Stmt, TlError> {
1542        let start = self.peek_span().start;
1543        self.advance(); // consume 'return'
1544        if self.check(&Token::RBrace) || self.is_at_end() {
1545            let end = self.previous_span().end;
1546            Ok(make_stmt(StmtKind::Return(None), start, end))
1547        } else {
1548            let expr = self.parse_expression()?;
1549            let end = self.previous_span().end;
1550            Ok(make_stmt(StmtKind::Return(Some(expr)), start, end))
1551        }
1552    }
1553
1554    fn parse_block_body(&mut self) -> Result<Vec<Stmt>, TlError> {
1555        let mut stmts = Vec::new();
1556        while !self.check(&Token::RBrace) && !self.is_at_end() {
1557            stmts.push(self.parse_statement()?);
1558        }
1559        Ok(stmts)
1560    }
1561
1562    // ── Expression Parsing (Pratt / precedence climbing) ─────
1563
1564    fn parse_expression(&mut self) -> Result<Expr, TlError> {
1565        self.enter_depth()?;
1566        let result = self.parse_expression_inner();
1567        self.leave_depth();
1568        result
1569    }
1570
1571    fn parse_expression_inner(&mut self) -> Result<Expr, TlError> {
1572        let expr = self.parse_pipe()?;
1573        // Assignment: target = value
1574        if self.match_token(&Token::Assign) {
1575            let value = self.parse_expression()?;
1576            return Ok(Expr::Assign {
1577                target: Box::new(expr),
1578                value: Box::new(value),
1579            });
1580        }
1581        Ok(expr)
1582    }
1583
1584    /// Pipe: expr |> expr |> expr
1585    fn parse_pipe(&mut self) -> Result<Expr, TlError> {
1586        let mut left = self.parse_null_coalesce()?;
1587        while self.match_token(&Token::Pipe) {
1588            let right = self.parse_null_coalesce()?;
1589            left = Expr::Pipe {
1590                left: Box::new(left),
1591                right: Box::new(right),
1592            };
1593        }
1594        Ok(left)
1595    }
1596
1597    /// Null coalesce: expr ?? expr
1598    fn parse_null_coalesce(&mut self) -> Result<Expr, TlError> {
1599        let mut left = self.parse_or()?;
1600        while self.match_token(&Token::NullCoalesce) {
1601            let right = self.parse_or()?;
1602            left = Expr::NullCoalesce {
1603                expr: Box::new(left),
1604                default: Box::new(right),
1605            };
1606        }
1607        Ok(left)
1608    }
1609
1610    /// Logical OR: expr or expr
1611    fn parse_or(&mut self) -> Result<Expr, TlError> {
1612        let mut left = self.parse_and()?;
1613        while self.match_token(&Token::Or) {
1614            let right = self.parse_and()?;
1615            left = Expr::BinOp {
1616                left: Box::new(left),
1617                op: BinOp::Or,
1618                right: Box::new(right),
1619            };
1620        }
1621        Ok(left)
1622    }
1623
1624    /// Logical AND: expr and expr
1625    fn parse_and(&mut self) -> Result<Expr, TlError> {
1626        let mut left = self.parse_comparison()?;
1627        while self.match_token(&Token::And) {
1628            let right = self.parse_comparison()?;
1629            left = Expr::BinOp {
1630                left: Box::new(left),
1631                op: BinOp::And,
1632                right: Box::new(right),
1633            };
1634        }
1635        Ok(left)
1636    }
1637
1638    /// Comparison: expr (== | != | < | > | <= | >=) expr
1639    fn parse_comparison(&mut self) -> Result<Expr, TlError> {
1640        let mut left = self.parse_addition()?;
1641        loop {
1642            let op = match self.peek() {
1643                Token::Eq => BinOp::Eq,
1644                Token::Neq => BinOp::Neq,
1645                Token::Lt => BinOp::Lt,
1646                Token::Gt => BinOp::Gt,
1647                Token::Lte => BinOp::Lte,
1648                Token::Gte => BinOp::Gte,
1649                _ => break,
1650            };
1651            self.advance();
1652            let right = self.parse_addition()?;
1653            left = Expr::BinOp {
1654                left: Box::new(left),
1655                op,
1656                right: Box::new(right),
1657            };
1658        }
1659        Ok(left)
1660    }
1661
1662    /// Addition/subtraction: expr (+ | -) expr
1663    fn parse_addition(&mut self) -> Result<Expr, TlError> {
1664        let mut left = self.parse_multiplication()?;
1665        loop {
1666            let op = match self.peek() {
1667                Token::Plus => BinOp::Add,
1668                Token::Minus => BinOp::Sub,
1669                _ => break,
1670            };
1671            self.advance();
1672            let right = self.parse_multiplication()?;
1673            left = Expr::BinOp {
1674                left: Box::new(left),
1675                op,
1676                right: Box::new(right),
1677            };
1678        }
1679        Ok(left)
1680    }
1681
1682    /// Multiplication/division/modulo: expr (* | / | %) expr
1683    fn parse_multiplication(&mut self) -> Result<Expr, TlError> {
1684        let mut left = self.parse_power()?;
1685        loop {
1686            let op = match self.peek() {
1687                Token::Star => BinOp::Mul,
1688                Token::Slash => BinOp::Div,
1689                Token::Percent => BinOp::Mod,
1690                _ => break,
1691            };
1692            self.advance();
1693            let right = self.parse_power()?;
1694            left = Expr::BinOp {
1695                left: Box::new(left),
1696                op,
1697                right: Box::new(right),
1698            };
1699        }
1700        Ok(left)
1701    }
1702
1703    /// Power: expr ** expr (right-associative)
1704    fn parse_power(&mut self) -> Result<Expr, TlError> {
1705        let left = self.parse_unary()?;
1706        if self.match_token(&Token::Power) {
1707            let right = self.parse_power()?; // right-associative
1708            Ok(Expr::BinOp {
1709                left: Box::new(left),
1710                op: BinOp::Pow,
1711                right: Box::new(right),
1712            })
1713        } else {
1714            Ok(left)
1715        }
1716    }
1717
1718    /// Unary: not expr | -expr | yield expr | await expr
1719    fn parse_unary(&mut self) -> Result<Expr, TlError> {
1720        if self.match_token(&Token::Yield) {
1721            // yield with no value if next token is a statement boundary or statement-starting keyword
1722            if self.is_at_end()
1723                || matches!(
1724                    self.peek(),
1725                    Token::RBrace
1726                        | Token::RParen
1727                        | Token::Comma
1728                        | Token::Semicolon
1729                        | Token::Let
1730                        | Token::Fn
1731                        | Token::If
1732                        | Token::While
1733                        | Token::For
1734                        | Token::Return
1735                        | Token::Yield
1736                        | Token::Struct
1737                        | Token::Enum
1738                        | Token::Impl
1739                        | Token::Trait
1740                        | Token::Import
1741                        | Token::Try
1742                        | Token::Throw
1743                )
1744            {
1745                return Ok(Expr::Yield(None));
1746            }
1747            let expr = self.parse_expression()?;
1748            return Ok(Expr::Yield(Some(Box::new(expr))));
1749        }
1750        if self.match_token(&Token::Await) {
1751            let expr = self.parse_unary()?;
1752            return Ok(Expr::Await(Box::new(expr)));
1753        }
1754        if self.match_token(&Token::Not) {
1755            let expr = self.parse_unary()?;
1756            return Ok(Expr::UnaryOp {
1757                op: UnaryOp::Not,
1758                expr: Box::new(expr),
1759            });
1760        }
1761        if self.match_token(&Token::Minus) {
1762            let expr = self.parse_unary()?;
1763            return Ok(Expr::UnaryOp {
1764                op: UnaryOp::Neg,
1765                expr: Box::new(expr),
1766            });
1767        }
1768        if self.match_token(&Token::Ampersand) {
1769            let expr = self.parse_unary()?;
1770            return Ok(Expr::UnaryOp {
1771                op: UnaryOp::Ref,
1772                expr: Box::new(expr),
1773            });
1774        }
1775        self.parse_postfix()
1776    }
1777
1778    /// Postfix: expr.field | expr(args) | expr[index] | Ident { field: val } | Ident::Variant
1779    fn parse_postfix(&mut self) -> Result<Expr, TlError> {
1780        let mut expr = self.parse_primary()?;
1781        loop {
1782            if self.match_token(&Token::Dot) {
1783                let field = self.expect_ident()?;
1784                expr = Expr::Member {
1785                    object: Box::new(expr),
1786                    field,
1787                };
1788            } else if self.check(&Token::ColonColon) {
1789                // Enum::Variant or Enum::Variant(args)
1790                if let Expr::Ident(enum_name) = &expr {
1791                    let enum_name = enum_name.clone();
1792                    self.advance(); // consume '::'
1793                    let variant = self.expect_ident()?;
1794                    let mut args = Vec::new();
1795                    if self.match_token(&Token::LParen) {
1796                        if !self.check(&Token::RParen) {
1797                            args.push(self.parse_expression()?);
1798                            while self.match_token(&Token::Comma) {
1799                                if self.check(&Token::RParen) {
1800                                    break;
1801                                }
1802                                args.push(self.parse_expression()?);
1803                            }
1804                        }
1805                        self.expect(&Token::RParen)?;
1806                    }
1807                    expr = Expr::EnumVariant {
1808                        enum_name,
1809                        variant,
1810                        args,
1811                    };
1812                } else {
1813                    break;
1814                }
1815            } else if self.check(&Token::LBrace) {
1816                // Struct init: if expr is an Ident and next tokens look like `{ ident :`
1817                if let Expr::Ident(name) = &expr {
1818                    // Lookahead: check if this is a struct init { field: val } vs a block
1819                    if self.is_struct_init_ahead() {
1820                        let name = name.clone();
1821                        self.advance(); // consume '{'
1822                        let mut fields = Vec::new();
1823                        while !self.check(&Token::RBrace) && !self.is_at_end() {
1824                            let field_name = self.expect_ident()?;
1825                            self.expect(&Token::Colon)?;
1826                            let value = self.parse_expression()?;
1827                            self.match_token(&Token::Comma);
1828                            fields.push((field_name, value));
1829                        }
1830                        self.expect(&Token::RBrace)?;
1831                        expr = Expr::StructInit { name, fields };
1832                    } else {
1833                        break;
1834                    }
1835                } else {
1836                    break;
1837                }
1838            } else if self.check(&Token::LParen) {
1839                self.advance();
1840                let args = self.parse_arg_list()?;
1841                self.expect(&Token::RParen)?;
1842                expr = Expr::Call {
1843                    function: Box::new(expr),
1844                    args,
1845                };
1846            } else if self.match_token(&Token::LBracket) {
1847                let index = self.parse_expression()?;
1848                self.expect(&Token::RBracket)?;
1849                expr = Expr::Index {
1850                    object: Box::new(expr),
1851                    index: Box::new(index),
1852                };
1853            } else if self.match_token(&Token::Question) {
1854                // Postfix ? operator: expr? → Try(expr)
1855                expr = Expr::Try(Box::new(expr));
1856            } else {
1857                break;
1858            }
1859        }
1860        Ok(expr)
1861    }
1862
1863    // Lookahead: is next `{ ident :` (struct init) vs `{ stmt` (block)?
1864    // ── Pattern Matching ─────────────────────────────────────
1865
1866    /// Parse a match arm: `pattern [if guard] => body`
1867    fn parse_match_arm(&mut self) -> Result<MatchArm, TlError> {
1868        let pattern = self.parse_pattern()?;
1869        let guard = if self.match_token(&Token::If) {
1870            Some(self.parse_expression()?)
1871        } else {
1872            None
1873        };
1874        self.expect(&Token::FatArrow)?;
1875        let body = self.parse_expression()?;
1876        Ok(MatchArm {
1877            pattern,
1878            guard,
1879            body,
1880        })
1881    }
1882
1883    /// Parse a case arm: `condition => body` or `_ => body` (default).
1884    /// Case arms use boolean expressions as conditions, not patterns.
1885    /// We represent them as MatchArm with Wildcard pattern + guard.
1886    fn parse_case_arm(&mut self) -> Result<MatchArm, TlError> {
1887        // Wildcard / default case
1888        if self.check(&Token::Underscore) {
1889            self.advance();
1890            self.expect(&Token::FatArrow)?;
1891            let body = self.parse_expression()?;
1892            return Ok(MatchArm {
1893                pattern: Pattern::Wildcard,
1894                guard: None,
1895                body,
1896            });
1897        }
1898        // Otherwise, parse expression as guard condition
1899        let condition = self.parse_expression()?;
1900        self.expect(&Token::FatArrow)?;
1901        let body = self.parse_expression()?;
1902        Ok(MatchArm {
1903            pattern: Pattern::Wildcard,
1904            guard: Some(condition),
1905            body,
1906        })
1907    }
1908
1909    /// Parse a pattern (for match arms and let-destructuring).
1910    fn parse_pattern(&mut self) -> Result<Pattern, TlError> {
1911        let pat = self.parse_single_pattern()?;
1912        // Check for OR pattern: pat1 | pat2 | pat3
1913        // Must be `|` without `>` (to distinguish from `|>` pipe)
1914        if self.check(&Token::Or) {
1915            let mut patterns = vec![pat];
1916            while self.check(&Token::Or) {
1917                self.advance(); // consume 'or'
1918                patterns.push(self.parse_single_pattern()?);
1919            }
1920            Ok(Pattern::Or(patterns))
1921        } else {
1922            Ok(pat)
1923        }
1924    }
1925
1926    /// Parse a single (non-OR) pattern.
1927    fn parse_single_pattern(&mut self) -> Result<Pattern, TlError> {
1928        let token = self.peek().clone();
1929        match token {
1930            // Wildcard: _
1931            Token::Underscore => {
1932                self.advance();
1933                Ok(Pattern::Wildcard)
1934            }
1935            // Literal patterns
1936            Token::Int(n) => {
1937                self.advance();
1938                Ok(Pattern::Literal(Expr::Int(n)))
1939            }
1940            Token::Float(n) => {
1941                self.advance();
1942                Ok(Pattern::Literal(Expr::Float(n)))
1943            }
1944            Token::String(s) => {
1945                self.advance();
1946                Ok(Pattern::Literal(Expr::String(s)))
1947            }
1948            Token::True => {
1949                self.advance();
1950                Ok(Pattern::Literal(Expr::Bool(true)))
1951            }
1952            Token::False => {
1953                self.advance();
1954                Ok(Pattern::Literal(Expr::Bool(false)))
1955            }
1956            Token::None_ => {
1957                self.advance();
1958                Ok(Pattern::Literal(Expr::None))
1959            }
1960            // Negative literal: -1, -3.14
1961            Token::Minus => {
1962                self.advance();
1963                match self.peek().clone() {
1964                    Token::Int(n) => {
1965                        self.advance();
1966                        Ok(Pattern::Literal(Expr::Int(-n)))
1967                    }
1968                    Token::Float(n) => {
1969                        self.advance();
1970                        Ok(Pattern::Literal(Expr::Float(-n)))
1971                    }
1972                    _ => Err(TlError::Parser(ParserError {
1973                        message: "Expected number after '-' in pattern".to_string(),
1974                        span: self.peek_span(),
1975                        hint: None,
1976                    })),
1977                }
1978            }
1979            // List pattern: [a, b, ...rest]
1980            Token::LBracket => {
1981                self.advance(); // consume '['
1982                let mut elements = Vec::new();
1983                let mut rest = None;
1984                while !self.check(&Token::RBracket) && !self.is_at_end() {
1985                    // Check for rest pattern: ...name
1986                    if self.check(&Token::DotDotDot) {
1987                        self.advance(); // consume '...'
1988                        let name = self.expect_ident()?;
1989                        rest = Some(name);
1990                        self.match_token(&Token::Comma); // optional trailing comma
1991                        break;
1992                    }
1993                    elements.push(self.parse_pattern()?);
1994                    if !self.match_token(&Token::Comma) {
1995                        break;
1996                    }
1997                }
1998                self.expect(&Token::RBracket)?;
1999                Ok(Pattern::List { elements, rest })
2000            }
2001            // Struct pattern: { x, y } or { x: pat, y }
2002            Token::LBrace => {
2003                self.advance(); // consume '{'
2004                let mut fields = Vec::new();
2005                while !self.check(&Token::RBrace) && !self.is_at_end() {
2006                    let name = self.expect_ident()?;
2007                    let sub_pat = if self.match_token(&Token::Colon) {
2008                        Some(self.parse_pattern()?)
2009                    } else {
2010                        None
2011                    };
2012                    fields.push(StructPatternField {
2013                        name,
2014                        pattern: sub_pat,
2015                    });
2016                    if !self.match_token(&Token::Comma) {
2017                        break;
2018                    }
2019                }
2020                self.expect(&Token::RBrace)?;
2021                Ok(Pattern::Struct { name: None, fields })
2022            }
2023            // Identifier-based patterns: binding, or Enum::Variant
2024            Token::Ident(name) => {
2025                // Check for Ident::Ident (enum variant pattern)
2026                if self.pos + 1 < self.tokens.len()
2027                    && matches!(self.tokens[self.pos + 1].token, Token::ColonColon)
2028                {
2029                    let type_name = name.clone();
2030                    self.advance(); // consume type name
2031                    self.advance(); // consume '::'
2032                    let variant = self.expect_ident()?;
2033                    // Optional args: Variant(pat1, pat2)
2034                    let mut args = Vec::new();
2035                    if self.match_token(&Token::LParen) {
2036                        while !self.check(&Token::RParen) && !self.is_at_end() {
2037                            args.push(self.parse_pattern()?);
2038                            if !self.match_token(&Token::Comma) {
2039                                break;
2040                            }
2041                        }
2042                        self.expect(&Token::RParen)?;
2043                    }
2044                    return Ok(Pattern::Enum {
2045                        type_name,
2046                        variant,
2047                        args,
2048                    });
2049                }
2050                // Check for Named struct pattern: Name { x, y }
2051                if self.pos + 1 < self.tokens.len()
2052                    && matches!(self.tokens[self.pos + 1].token, Token::LBrace)
2053                {
2054                    // Lookahead: check if this is ident { ident [,:|} ] — struct pattern
2055                    // vs ident { expr } — block (but blocks aren't patterns)
2056                    if self.pos + 2 < self.tokens.len() {
2057                        let third = &self.tokens[self.pos + 2].token;
2058                        if matches!(third, Token::Ident(_) | Token::RBrace) {
2059                            let struct_name = name.clone();
2060                            self.advance(); // consume name
2061                            self.advance(); // consume '{'
2062                            let mut fields = Vec::new();
2063                            while !self.check(&Token::RBrace) && !self.is_at_end() {
2064                                let fname = self.expect_ident()?;
2065                                let sub_pat = if self.match_token(&Token::Colon) {
2066                                    Some(self.parse_pattern()?)
2067                                } else {
2068                                    None
2069                                };
2070                                fields.push(StructPatternField {
2071                                    name: fname,
2072                                    pattern: sub_pat,
2073                                });
2074                                if !self.match_token(&Token::Comma) {
2075                                    break;
2076                                }
2077                            }
2078                            self.expect(&Token::RBrace)?;
2079                            return Ok(Pattern::Struct {
2080                                name: Some(struct_name),
2081                                fields,
2082                            });
2083                        }
2084                    }
2085                }
2086                // Simple binding
2087                self.advance();
2088                Ok(Pattern::Binding(name))
2089            }
2090            _ => Err(TlError::Parser(ParserError {
2091                message: format!("Expected pattern, found `{}`", self.peek()),
2092                span: self.peek_span(),
2093                hint: None,
2094            })),
2095        }
2096    }
2097
2098    fn is_struct_init_ahead(&self) -> bool {
2099        // Check: LBrace Ident Colon
2100        if self.pos + 2 < self.tokens.len() {
2101            matches!(self.tokens[self.pos].token, Token::LBrace)
2102                && matches!(&self.tokens[self.pos + 1].token, Token::Ident(_))
2103                && matches!(self.tokens[self.pos + 2].token, Token::Colon)
2104        } else {
2105            false
2106        }
2107    }
2108
2109    /// Primary: literals, identifiers, parenthesized expressions, etc.
2110    fn parse_primary(&mut self) -> Result<Expr, TlError> {
2111        let token = self.peek().clone();
2112        match token {
2113            Token::Int(n) => {
2114                self.advance();
2115                Ok(Expr::Int(n))
2116            }
2117            Token::Float(n) => {
2118                self.advance();
2119                Ok(Expr::Float(n))
2120            }
2121            Token::DecimalLiteral(s) => {
2122                let s = s.clone();
2123                self.advance();
2124                Ok(Expr::Decimal(s))
2125            }
2126            Token::String(s) => {
2127                let s = s.clone();
2128                self.advance();
2129                Ok(Expr::String(s))
2130            }
2131            Token::True => {
2132                self.advance();
2133                Ok(Expr::Bool(true))
2134            }
2135            Token::False => {
2136                self.advance();
2137                Ok(Expr::Bool(false))
2138            }
2139            Token::None_ => {
2140                self.advance();
2141                Ok(Expr::None)
2142            }
2143            Token::Ident(name) => {
2144                let name = name.clone();
2145                // Check for shorthand closure: x => expr
2146                if self.pos + 1 < self.tokens.len()
2147                    && self.tokens[self.pos + 1].token == Token::FatArrow
2148                {
2149                    self.advance(); // consume ident
2150                    self.advance(); // consume =>
2151                    let body = self.parse_expression()?;
2152                    return Ok(Expr::Closure {
2153                        params: vec![Param {
2154                            name,
2155                            type_ann: None,
2156                        }],
2157                        return_type: None,
2158                        body: ClosureBody::Expr(Box::new(body)),
2159                    });
2160                }
2161                self.advance();
2162                Ok(Expr::Ident(name))
2163            }
2164            Token::Self_ => {
2165                self.advance();
2166                Ok(Expr::Ident("self".to_string()))
2167            }
2168            Token::LParen => {
2169                if self.is_closure_ahead() {
2170                    self.parse_closure()
2171                } else {
2172                    self.advance();
2173                    let expr = self.parse_expression()?;
2174                    self.expect(&Token::RParen)?;
2175                    Ok(expr)
2176                }
2177            }
2178            Token::LBracket => {
2179                self.advance();
2180                let mut elements = Vec::new();
2181                if !self.check(&Token::RBracket) {
2182                    elements.push(self.parse_expression()?);
2183                    while self.match_token(&Token::Comma) {
2184                        if self.check(&Token::RBracket) {
2185                            break;
2186                        }
2187                        elements.push(self.parse_expression()?);
2188                    }
2189                }
2190                self.expect(&Token::RBracket)?;
2191                Ok(Expr::List(elements))
2192            }
2193            Token::Case => {
2194                self.advance();
2195                self.expect(&Token::LBrace)?;
2196                let mut arms = Vec::new();
2197                while !self.check(&Token::RBrace) && !self.is_at_end() {
2198                    let arm = self.parse_case_arm()?;
2199                    self.match_token(&Token::Comma); // optional trailing comma
2200                    arms.push(arm);
2201                }
2202                self.expect(&Token::RBrace)?;
2203                Ok(Expr::Case { arms })
2204            }
2205            Token::Match => {
2206                self.advance(); // consume 'match'
2207                let subject = self.parse_expression()?;
2208                self.expect(&Token::LBrace)?;
2209                let mut arms = Vec::new();
2210                while !self.check(&Token::RBrace) && !self.is_at_end() {
2211                    let arm = self.parse_match_arm()?;
2212                    self.match_token(&Token::Comma); // optional trailing comma
2213                    arms.push(arm);
2214                }
2215                self.expect(&Token::RBrace)?;
2216                Ok(Expr::Match {
2217                    subject: Box::new(subject),
2218                    arms,
2219                })
2220            }
2221            Token::With => {
2222                self.advance(); // consume 'with'
2223                self.expect(&Token::LBrace)?;
2224                let mut pairs = Vec::new();
2225                while !self.check(&Token::RBrace) && !self.is_at_end() {
2226                    let key_name = self.expect_ident()?;
2227                    self.expect(&Token::Assign)?;
2228                    let value = self.parse_expression()?;
2229                    self.match_token(&Token::Comma); // optional trailing comma
2230                    pairs.push((Expr::String(key_name), value));
2231                }
2232                self.expect(&Token::RBrace)?;
2233                Ok(Expr::Call {
2234                    function: Box::new(Expr::Ident("with".to_string())),
2235                    args: vec![Expr::Map(pairs)],
2236                })
2237            }
2238            Token::Emit => {
2239                // 'emit' is a keyword but used as a builtin function
2240                self.advance();
2241                Ok(Expr::Ident("emit".to_string()))
2242            }
2243            Token::Underscore => {
2244                self.advance();
2245                Ok(Expr::Ident("_".to_string()))
2246            }
2247            _ => Err(TlError::Parser(ParserError {
2248                message: format!("Unexpected token: `{}`", self.peek()),
2249                span: self.peek_span(),
2250                hint: Some("Expected an expression (literal, variable, or function call)".into()),
2251            })),
2252        }
2253    }
2254
2255    /// Look ahead from current `(` to determine if this is a closure `(params) => expr`
2256    /// or `(params) -> Type { ... }`.
2257    /// Scans forward to find the matching `)`, then checks if the next token is `=>` or `->`.
2258    fn is_closure_ahead(&self) -> bool {
2259        // Current token should be LParen
2260        let mut i = self.pos + 1; // skip the LParen
2261        let mut depth = 1;
2262        while i < self.tokens.len() {
2263            match &self.tokens[i].token {
2264                Token::LParen => depth += 1,
2265                Token::RParen => {
2266                    depth -= 1;
2267                    if depth == 0 {
2268                        // Check if next token after `)` is `=>` or `->`
2269                        return i + 1 < self.tokens.len()
2270                            && matches!(self.tokens[i + 1].token, Token::FatArrow | Token::Arrow);
2271                    }
2272                }
2273                _ if i >= self.tokens.len() - 1 => return false, // hit EOF sentinel
2274                _ => {}
2275            }
2276            i += 1;
2277        }
2278        false
2279    }
2280
2281    /// Parse a closure: `(params) => expr` or `(params) -> Type { stmts; expr }`
2282    fn parse_closure(&mut self) -> Result<Expr, TlError> {
2283        use tl_ast::ClosureBody;
2284        self.expect(&Token::LParen)?;
2285        let params = self.parse_param_list()?;
2286        self.expect(&Token::RParen)?;
2287
2288        if self.match_token(&Token::FatArrow) {
2289            // Expression-body closure: (x) => x * 2
2290            let body = self.parse_expression()?;
2291            Ok(Expr::Closure {
2292                params,
2293                return_type: None,
2294                body: ClosureBody::Expr(Box::new(body)),
2295            })
2296        } else if self.match_token(&Token::Arrow) {
2297            // Block-body closure: (x) -> int64 { let y = x * 2; y + 1 }
2298            let return_type = self.parse_type()?;
2299            self.expect(&Token::LBrace)?;
2300            let stmts = self.parse_block_body()?;
2301            // Check if the last statement is an expression statement — if so, treat as tail expr
2302            let (stmts, expr) = self.extract_tail_expr(stmts);
2303            self.expect(&Token::RBrace)?;
2304            Ok(Expr::Closure {
2305                params,
2306                return_type: Some(return_type),
2307                body: ClosureBody::Block { stmts, expr },
2308            })
2309        } else {
2310            Err(TlError::Parser(ParserError {
2311                message: "Expected `=>` or `->` after closure parameters".to_string(),
2312                span: self.peek_span(),
2313                hint: Some("Use `=>` for expression closures or `->` for block closures".into()),
2314            }))
2315        }
2316    }
2317
2318    /// Extract the last expression-statement from a block as a tail expression.
2319    fn extract_tail_expr(&self, mut stmts: Vec<Stmt>) -> (Vec<Stmt>, Option<Box<Expr>>) {
2320        if let Some(last) = stmts.last()
2321            && let StmtKind::Expr(_) = &last.kind
2322            && let StmtKind::Expr(e) = stmts.pop().unwrap().kind
2323        {
2324            return (stmts, Some(Box::new(e)));
2325        }
2326        (stmts, None)
2327    }
2328
2329    // ── Argument & Parameter Lists ───────────────────────────
2330
2331    fn parse_arg_list(&mut self) -> Result<Vec<Expr>, TlError> {
2332        let mut args = Vec::new();
2333        if self.check(&Token::RParen) {
2334            return Ok(args);
2335        }
2336        args.push(self.parse_arg()?);
2337        while self.match_token(&Token::Comma) {
2338            if self.check(&Token::RParen) {
2339                break;
2340            }
2341            args.push(self.parse_arg()?);
2342        }
2343        Ok(args)
2344    }
2345
2346    fn parse_arg(&mut self) -> Result<Expr, TlError> {
2347        // Check for named argument: `name: value`
2348        if let Token::Ident(name) = self.peek().clone() {
2349            let name = name.clone();
2350            if self.pos + 1 < self.tokens.len() && self.tokens[self.pos + 1].token == Token::Colon {
2351                self.advance(); // consume name
2352                self.advance(); // consume colon
2353                let value = self.parse_expression()?;
2354                return Ok(Expr::NamedArg {
2355                    name,
2356                    value: Box::new(value),
2357                });
2358            }
2359        }
2360        self.parse_expression()
2361    }
2362
2363    fn parse_param_list(&mut self) -> Result<Vec<Param>, TlError> {
2364        let mut params = Vec::new();
2365        if self.check(&Token::RParen) {
2366            return Ok(params);
2367        }
2368        params.push(self.parse_param()?);
2369        while self.match_token(&Token::Comma) {
2370            if self.check(&Token::RParen) {
2371                break;
2372            }
2373            params.push(self.parse_param()?);
2374        }
2375        Ok(params)
2376    }
2377
2378    fn parse_param(&mut self) -> Result<Param, TlError> {
2379        let name = if self.check(&Token::Self_) {
2380            self.advance();
2381            "self".to_string()
2382        } else {
2383            self.expect_ident()?
2384        };
2385        let type_ann = if self.match_token(&Token::Colon) {
2386            Some(self.parse_type()?)
2387        } else {
2388            None
2389        };
2390        Ok(Param { name, type_ann })
2391    }
2392
2393    // ── Type Parsing ─────────────────────────────────────────
2394
2395    fn parse_type(&mut self) -> Result<TypeExpr, TlError> {
2396        // Handle `fn(params) -> ret` function types
2397        let base = if self.check(&Token::Fn) {
2398            self.advance(); // consume 'fn'
2399            self.expect(&Token::LParen)?;
2400            let mut params = Vec::new();
2401            if !self.check(&Token::RParen) {
2402                params.push(self.parse_type()?);
2403                while self.match_token(&Token::Comma) {
2404                    if self.check(&Token::RParen) {
2405                        break;
2406                    }
2407                    params.push(self.parse_type()?);
2408                }
2409            }
2410            self.expect(&Token::RParen)?;
2411            let return_type = if self.match_token(&Token::Arrow) {
2412                self.parse_type()?
2413            } else {
2414                TypeExpr::Named("unit".to_string())
2415            };
2416            TypeExpr::Function {
2417                params,
2418                return_type: Box::new(return_type),
2419            }
2420        } else {
2421            let name = self.expect_ident()?;
2422            if self.match_token(&Token::Lt) {
2423                let mut args = Vec::new();
2424                args.push(self.parse_type()?);
2425                while self.match_token(&Token::Comma) {
2426                    args.push(self.parse_type()?);
2427                }
2428                self.expect(&Token::Gt)?;
2429                TypeExpr::Generic { name, args }
2430            } else {
2431                TypeExpr::Named(name)
2432            }
2433        };
2434        // Handle postfix `?` for optional types: T? -> Optional(T)
2435        if self.match_token(&Token::Question) {
2436            Ok(TypeExpr::Optional(Box::new(base)))
2437        } else {
2438            Ok(base)
2439        }
2440    }
2441
2442    // ── Phase 5: New parsing methods ─────────────────────────
2443
2444    /// Parse `struct Name { field: type, ... }`
2445    fn parse_struct_decl(&mut self) -> Result<Stmt, TlError> {
2446        let start = self.peek_span().start;
2447        self.advance(); // consume 'struct'
2448        let name = self.expect_ident()?;
2449        let type_params = self.parse_optional_type_params()?;
2450        self.expect(&Token::LBrace)?;
2451        let mut fields = Vec::new();
2452        while !self.check(&Token::RBrace) && !self.is_at_end() {
2453            let field_doc = self.consume_doc_comments();
2454            let field_name = self.expect_ident()?;
2455            self.expect(&Token::Colon)?;
2456            let type_ann = self.parse_type()?;
2457            self.match_token(&Token::Comma);
2458            let annotations = parse_field_annotations(field_doc.as_deref());
2459            fields.push(SchemaField {
2460                name: field_name,
2461                type_ann,
2462                doc_comment: field_doc,
2463                default_value: None,
2464                annotations,
2465            });
2466        }
2467        self.expect(&Token::RBrace)?;
2468        let end = self.previous_span().end;
2469        Ok(make_stmt(
2470            StmtKind::StructDecl {
2471                name,
2472                type_params,
2473                fields,
2474                is_public: false,
2475            },
2476            start,
2477            end,
2478        ))
2479    }
2480
2481    /// Parse `enum Name { Variant, Variant(Type, Type), ... }`
2482    fn parse_enum_decl(&mut self) -> Result<Stmt, TlError> {
2483        let start = self.peek_span().start;
2484        self.advance(); // consume 'enum'
2485        let name = self.expect_ident()?;
2486        let type_params = self.parse_optional_type_params()?;
2487        self.expect(&Token::LBrace)?;
2488        let mut variants = Vec::new();
2489        while !self.check(&Token::RBrace) && !self.is_at_end() {
2490            let variant_name = self.expect_ident()?;
2491            let mut fields = Vec::new();
2492            if self.match_token(&Token::LParen) {
2493                if !self.check(&Token::RParen) {
2494                    fields.push(self.parse_type()?);
2495                    while self.match_token(&Token::Comma) {
2496                        if self.check(&Token::RParen) {
2497                            break;
2498                        }
2499                        fields.push(self.parse_type()?);
2500                    }
2501                }
2502                self.expect(&Token::RParen)?;
2503            }
2504            self.match_token(&Token::Comma);
2505            variants.push(EnumVariant {
2506                name: variant_name,
2507                fields,
2508            });
2509        }
2510        self.expect(&Token::RBrace)?;
2511        let end = self.previous_span().end;
2512        Ok(make_stmt(
2513            StmtKind::EnumDecl {
2514                name,
2515                type_params,
2516                variants,
2517                is_public: false,
2518            },
2519            start,
2520            end,
2521        ))
2522    }
2523
2524    /// Parse `impl Type { ... }` or `impl<T> Type { ... }` or `impl Trait for Type { ... }`
2525    fn parse_impl(&mut self) -> Result<Stmt, TlError> {
2526        let start = self.peek_span().start;
2527        self.advance(); // consume 'impl'
2528
2529        // Optional type params: impl<T>
2530        let impl_type_params = self.parse_optional_type_params()?;
2531
2532        let first_name = self.expect_ident()?;
2533
2534        // Check if this is `impl Trait for Type { ... }`
2535        if self.check(&Token::For) {
2536            self.advance(); // consume 'for'
2537            let type_name = self.expect_ident()?;
2538            // Optional type params on the type: `for Type<T>`
2539            let _type_args = self.parse_optional_type_params()?;
2540
2541            self.expect(&Token::LBrace)?;
2542            let mut methods = Vec::new();
2543            while !self.check(&Token::RBrace) && !self.is_at_end() {
2544                let doc = self.consume_doc_comments();
2545                if self.check(&Token::Fn) {
2546                    let mut method = self.parse_fn_decl()?;
2547                    method.doc_comment = doc;
2548                    methods.push(method);
2549                } else {
2550                    return Err(TlError::Parser(ParserError {
2551                        message: "Expected `fn` in impl block".to_string(),
2552                        span: self.peek_span(),
2553                        hint: None,
2554                    }));
2555                }
2556            }
2557            self.expect(&Token::RBrace)?;
2558            let end = self.previous_span().end;
2559            return Ok(make_stmt(
2560                StmtKind::TraitImpl {
2561                    trait_name: first_name,
2562                    type_name,
2563                    type_params: impl_type_params,
2564                    methods,
2565                },
2566                start,
2567                end,
2568            ));
2569        }
2570
2571        // Regular impl block: `impl Type { ... }`
2572        self.expect(&Token::LBrace)?;
2573        let mut methods = Vec::new();
2574        while !self.check(&Token::RBrace) && !self.is_at_end() {
2575            let doc = self.consume_doc_comments();
2576            if self.check(&Token::Fn) {
2577                let mut method = self.parse_fn_decl()?;
2578                method.doc_comment = doc;
2579                methods.push(method);
2580            } else {
2581                return Err(TlError::Parser(ParserError {
2582                    message: "Expected `fn` in impl block".to_string(),
2583                    span: self.peek_span(),
2584                    hint: None,
2585                }));
2586            }
2587        }
2588        self.expect(&Token::RBrace)?;
2589        let end = self.previous_span().end;
2590        Ok(make_stmt(
2591            StmtKind::ImplBlock {
2592                type_name: first_name,
2593                type_params: impl_type_params,
2594                methods,
2595            },
2596            start,
2597            end,
2598        ))
2599    }
2600
2601    /// Parse `trait Name { fn method(self) -> type; ... }`
2602    /// Parse `type Name<T> = TypeExpr`
2603    fn parse_type_alias(&mut self, is_public: bool) -> Result<Stmt, TlError> {
2604        let start = self.peek_span().start;
2605        self.advance(); // consume 'type'
2606        let name = self.expect_ident()?;
2607        let type_params = self.parse_optional_type_params()?;
2608        self.expect(&Token::Assign)?;
2609        let value = self.parse_type()?;
2610        let end = self.previous_span().end;
2611        Ok(make_stmt(
2612            StmtKind::TypeAlias {
2613                name,
2614                type_params,
2615                value,
2616                is_public,
2617            },
2618            start,
2619            end,
2620        ))
2621    }
2622
2623    fn parse_trait_def(&mut self) -> Result<Stmt, TlError> {
2624        let start = self.peek_span().start;
2625        self.advance(); // consume 'trait'
2626        let name = self.expect_ident()?;
2627
2628        // Optional type parameters <T, U>
2629        let type_params = self.parse_optional_type_params()?;
2630
2631        self.expect(&Token::LBrace)?;
2632        let mut methods = Vec::new();
2633        while !self.check(&Token::RBrace) && !self.is_at_end() {
2634            // Skip doc comments before trait methods
2635            let _doc = self.consume_doc_comments();
2636            if self.check(&Token::Fn) {
2637                self.advance(); // consume 'fn'
2638                let method_name = self.expect_ident()?;
2639                self.expect(&Token::LParen)?;
2640                let params = self.parse_param_list()?;
2641                self.expect(&Token::RParen)?;
2642                let return_type = if self.match_token(&Token::Arrow) {
2643                    Some(self.parse_type()?)
2644                } else {
2645                    None
2646                };
2647                methods.push(TraitMethod {
2648                    name: method_name,
2649                    params,
2650                    return_type,
2651                });
2652            } else {
2653                return Err(TlError::Parser(ParserError {
2654                    message: "Expected `fn` in trait definition".to_string(),
2655                    span: self.peek_span(),
2656                    hint: None,
2657                }));
2658            }
2659        }
2660        self.expect(&Token::RBrace)?;
2661        let end = self.previous_span().end;
2662        Ok(make_stmt(
2663            StmtKind::TraitDef {
2664                name,
2665                type_params,
2666                methods,
2667                is_public: false,
2668            },
2669            start,
2670            end,
2671        ))
2672    }
2673
2674    /// Parse optional type parameters: `<T, U>` — returns empty vec if no `<`
2675    fn parse_optional_type_params(&mut self) -> Result<Vec<String>, TlError> {
2676        let (params, _bounds) = self.parse_optional_type_params_with_bounds()?;
2677        Ok(params)
2678    }
2679
2680    /// Parse optional type parameters with inline bounds: `<T: Comparable, U>`
2681    /// Returns (type_param_names, trait_bounds)
2682    fn parse_optional_type_params_with_bounds(
2683        &mut self,
2684    ) -> Result<(Vec<String>, Vec<TraitBound>), TlError> {
2685        if !self.check(&Token::Lt) {
2686            return Ok((vec![], vec![]));
2687        }
2688        self.advance(); // consume '<'
2689        let mut params = Vec::new();
2690        let mut bounds = Vec::new();
2691        loop {
2692            let name = self.expect_ident()?;
2693            // Check for inline bounds: `T: Comparable + Hashable`
2694            if self.match_token(&Token::Colon) {
2695                let mut traits = Vec::new();
2696                traits.push(self.expect_ident()?);
2697                while self.match_token(&Token::Plus) {
2698                    traits.push(self.expect_ident()?);
2699                }
2700                bounds.push(TraitBound {
2701                    type_param: name.clone(),
2702                    traits,
2703                });
2704            }
2705            params.push(name);
2706            if !self.match_token(&Token::Comma) {
2707                break;
2708            }
2709        }
2710        self.expect(&Token::Gt)?;
2711        Ok((params, bounds))
2712    }
2713
2714    /// Parse where clause: `T: Comparable + Hashable, U: Default`
2715    fn parse_where_clause(&mut self) -> Result<Vec<TraitBound>, TlError> {
2716        let mut bounds = Vec::new();
2717        loop {
2718            let type_param = self.expect_ident()?;
2719            self.expect(&Token::Colon)?;
2720            let mut traits = Vec::new();
2721            traits.push(self.expect_ident()?);
2722            while self.match_token(&Token::Plus) {
2723                traits.push(self.expect_ident()?);
2724            }
2725            bounds.push(TraitBound { type_param, traits });
2726            if !self.match_token(&Token::Comma) {
2727                break;
2728            }
2729            // Stop if next token is `{` (body start)
2730            if self.check(&Token::LBrace) {
2731                break;
2732            }
2733        }
2734        Ok(bounds)
2735    }
2736
2737    /// Parse `try { ... } catch var { ... }`
2738    fn parse_try_catch(&mut self) -> Result<Stmt, TlError> {
2739        let start = self.peek_span().start;
2740        self.advance(); // consume 'try'
2741        self.expect(&Token::LBrace)?;
2742        let mut try_body = Vec::new();
2743        while !self.check(&Token::RBrace) && !self.is_at_end() {
2744            try_body.push(self.parse_statement()?);
2745        }
2746        self.expect(&Token::RBrace)?;
2747        self.expect(&Token::Catch)?;
2748        let catch_var = self.expect_ident()?;
2749        self.expect(&Token::LBrace)?;
2750        let mut catch_body = Vec::new();
2751        while !self.check(&Token::RBrace) && !self.is_at_end() {
2752            catch_body.push(self.parse_statement()?);
2753        }
2754        self.expect(&Token::RBrace)?;
2755        // Parse optional finally block
2756        let finally_body = if self.check(&Token::Finally) {
2757            self.advance(); // consume 'finally'
2758            self.expect(&Token::LBrace)?;
2759            let mut body = Vec::new();
2760            while !self.check(&Token::RBrace) && !self.is_at_end() {
2761                body.push(self.parse_statement()?);
2762            }
2763            self.expect(&Token::RBrace)?;
2764            Some(body)
2765        } else {
2766            None
2767        };
2768        let end = self.previous_span().end;
2769        Ok(make_stmt(
2770            StmtKind::TryCatch {
2771                try_body,
2772                catch_var,
2773                catch_body,
2774                finally_body,
2775            },
2776            start,
2777            end,
2778        ))
2779    }
2780
2781    /// Parse `throw expr`
2782    fn parse_throw(&mut self) -> Result<Stmt, TlError> {
2783        let start = self.peek_span().start;
2784        self.advance(); // consume 'throw'
2785        let expr = self.parse_expression()?;
2786        let end = self.previous_span().end;
2787        Ok(make_stmt(StmtKind::Throw(expr), start, end))
2788    }
2789
2790    /// Parse `import "path.tl"` or `import "path.tl" as name`
2791    fn parse_import(&mut self) -> Result<Stmt, TlError> {
2792        let start = self.peek_span().start;
2793        self.advance(); // consume 'import'
2794        let path = match self.peek().clone() {
2795            Token::String(s) => {
2796                self.advance();
2797                s
2798            }
2799            _ => {
2800                return Err(TlError::Parser(ParserError {
2801                    message: "Expected string path after `import`".to_string(),
2802                    span: self.peek_span(),
2803                    hint: None,
2804                }));
2805            }
2806        };
2807        let alias = if self.match_token(&Token::As) {
2808            Some(self.expect_ident()?)
2809        } else {
2810            None
2811        };
2812        let end = self.previous_span().end;
2813        Ok(make_stmt(StmtKind::Import { path, alias }, start, end))
2814    }
2815
2816    /// Parse `test "name" { body }`
2817    fn parse_test(&mut self) -> Result<Stmt, TlError> {
2818        let start = self.peek_span().start;
2819        self.advance(); // consume 'test'
2820        let name = match self.peek().clone() {
2821            Token::String(s) => {
2822                self.advance();
2823                s
2824            }
2825            _ => {
2826                return Err(TlError::Parser(ParserError {
2827                    message: "Expected string after `test`".to_string(),
2828                    span: self.peek_span(),
2829                    hint: None,
2830                }));
2831            }
2832        };
2833        self.expect(&Token::LBrace)?;
2834        let mut body = Vec::new();
2835        while !self.check(&Token::RBrace) && !self.is_at_end() {
2836            body.push(self.parse_statement()?);
2837        }
2838        self.expect(&Token::RBrace)?;
2839        let end = self.previous_span().end;
2840        Ok(make_stmt(StmtKind::Test { name, body }, start, end))
2841    }
2842}
2843
2844/// Helper: human-readable token name for error messages
2845fn token_name(token: &Token) -> &'static str {
2846    match token {
2847        Token::LParen => "(",
2848        Token::RParen => ")",
2849        Token::LBrace => "{",
2850        Token::RBrace => "}",
2851        Token::LBracket => "[",
2852        Token::RBracket => "]",
2853        Token::Comma => ",",
2854        Token::Colon => ":",
2855        Token::Semicolon => ";",
2856        Token::Assign => "=",
2857        Token::Arrow => "->",
2858        Token::FatArrow => "=>",
2859        Token::Pipe => "|>",
2860        Token::Let => "let",
2861        Token::Fn => "fn",
2862        Token::If => "if",
2863        Token::Else => "else",
2864        Token::Return => "return",
2865        Token::In => "in",
2866        Token::Dot => ".",
2867        Token::Lt => "<",
2868        Token::Gt => ">",
2869        _ => "token",
2870    }
2871}
2872
2873/// Convenience: parse source text directly
2874pub fn parse(source: &str) -> Result<Program, TlError> {
2875    let tokens = tl_lexer::tokenize(source)?;
2876    let mut parser = Parser::new(tokens);
2877    parser.parse_program()
2878}
2879
2880/// Check if a function body contains any yield expressions (makes it a generator).
2881/// Parse security annotations from a doc comment string.
2882fn parse_field_annotations(doc: Option<&str>) -> Vec<tl_ast::Annotation> {
2883    let mut annotations = Vec::new();
2884    if let Some(doc) = doc {
2885        if doc.contains("@sensitive") {
2886            annotations.push(tl_ast::Annotation::Sensitive);
2887        }
2888        if doc.contains("@redact") {
2889            annotations.push(tl_ast::Annotation::Redact);
2890        }
2891        if doc.contains("@pii") {
2892            annotations.push(tl_ast::Annotation::Pii);
2893        }
2894    }
2895    annotations
2896}
2897
2898fn body_contains_yield(stmts: &[Stmt]) -> bool {
2899    for stmt in stmts {
2900        if stmt_contains_yield(stmt) {
2901            return true;
2902        }
2903    }
2904    false
2905}
2906
2907fn stmt_contains_yield(stmt: &Stmt) -> bool {
2908    match &stmt.kind {
2909        StmtKind::Expr(e) | StmtKind::Return(Some(e)) | StmtKind::Throw(e) => {
2910            expr_contains_yield(e)
2911        }
2912        StmtKind::Let { value, .. } => expr_contains_yield(value),
2913        StmtKind::If {
2914            condition,
2915            then_body,
2916            else_ifs,
2917            else_body,
2918        } => {
2919            expr_contains_yield(condition)
2920                || body_contains_yield(then_body)
2921                || else_ifs
2922                    .iter()
2923                    .any(|(c, b)| expr_contains_yield(c) || body_contains_yield(b))
2924                || else_body.as_ref().is_some_and(|b| body_contains_yield(b))
2925        }
2926        StmtKind::While { condition, body } => {
2927            expr_contains_yield(condition) || body_contains_yield(body)
2928        }
2929        StmtKind::For { iter, body, .. } => expr_contains_yield(iter) || body_contains_yield(body),
2930        StmtKind::TryCatch {
2931            try_body,
2932            catch_body,
2933            ..
2934        } => body_contains_yield(try_body) || body_contains_yield(catch_body),
2935        // Don't recurse into nested FnDecl — yield in nested fn is for that fn
2936        StmtKind::FnDecl { .. } => false,
2937        _ => false,
2938    }
2939}
2940
2941fn expr_contains_yield(expr: &Expr) -> bool {
2942    match expr {
2943        Expr::Yield(_) => true,
2944        Expr::BinOp { left, right, .. } => expr_contains_yield(left) || expr_contains_yield(right),
2945        Expr::UnaryOp { expr, .. } => expr_contains_yield(expr),
2946        Expr::Call { function, args } => {
2947            expr_contains_yield(function) || args.iter().any(expr_contains_yield)
2948        }
2949        Expr::Pipe { left, right } => expr_contains_yield(left) || expr_contains_yield(right),
2950        Expr::Member { object, .. } => expr_contains_yield(object),
2951        Expr::Index { object, index } => expr_contains_yield(object) || expr_contains_yield(index),
2952        Expr::List(items) => items.iter().any(expr_contains_yield),
2953        Expr::Map(pairs) => pairs
2954            .iter()
2955            .any(|(k, v)| expr_contains_yield(k) || expr_contains_yield(v)),
2956        Expr::Block { stmts, expr } => {
2957            body_contains_yield(stmts) || expr.as_ref().is_some_and(|e| expr_contains_yield(e))
2958        }
2959        Expr::Closure { .. } => false, // Don't recurse — yield in closure is not our yield
2960        Expr::Assign { target, value } => expr_contains_yield(target) || expr_contains_yield(value),
2961        Expr::NullCoalesce { expr, default } => {
2962            expr_contains_yield(expr) || expr_contains_yield(default)
2963        }
2964        Expr::Range { start, end } => expr_contains_yield(start) || expr_contains_yield(end),
2965        Expr::Await(e) => expr_contains_yield(e),
2966        Expr::NamedArg { value, .. } => expr_contains_yield(value),
2967        Expr::Case { arms } | Expr::Match { arms, .. } => arms.iter().any(|arm| {
2968            (arm.guard.as_ref().is_some_and(expr_contains_yield)) || expr_contains_yield(&arm.body)
2969        }),
2970        Expr::StructInit { fields, .. } => fields.iter().any(|(_, e)| expr_contains_yield(e)),
2971        Expr::EnumVariant { args, .. } => args.iter().any(expr_contains_yield),
2972        _ => false,
2973    }
2974}
2975
2976#[cfg(test)]
2977mod tests {
2978    use super::*;
2979
2980    #[test]
2981    fn test_parse_let() {
2982        let program = parse("let x = 42").unwrap();
2983        assert_eq!(program.statements.len(), 1);
2984        assert!(matches!(&program.statements[0].kind, StmtKind::Let { name, .. } if name == "x"));
2985    }
2986
2987    #[test]
2988    fn test_parse_fn() {
2989        let program = parse("fn add(a: int64, b: int64) -> int64 { a + b }").unwrap();
2990        assert_eq!(program.statements.len(), 1);
2991        assert!(
2992            matches!(&program.statements[0].kind, StmtKind::FnDecl { name, .. } if name == "add")
2993        );
2994    }
2995
2996    #[test]
2997    fn test_parse_pipe() {
2998        let program = parse("let result = x |> double()").unwrap();
2999        if let StmtKind::Let { value, .. } = &program.statements[0].kind {
3000            assert!(matches!(value, Expr::Pipe { .. }));
3001        } else {
3002            panic!("Expected let statement");
3003        }
3004    }
3005
3006    #[test]
3007    fn test_parse_if_else() {
3008        let program = parse("if x > 5 { x } else { 0 }").unwrap();
3009        assert!(matches!(program.statements[0].kind, StmtKind::If { .. }));
3010    }
3011
3012    #[test]
3013    fn test_parse_nested_arithmetic() {
3014        let program = parse("let x = 1 + 2 * 3").unwrap();
3015        // Should parse as 1 + (2 * 3) due to precedence
3016        if let StmtKind::Let { value, .. } = &program.statements[0].kind {
3017            assert!(matches!(value, Expr::BinOp { op: BinOp::Add, .. }));
3018        }
3019    }
3020
3021    #[test]
3022    fn test_parse_match() {
3023        let program = parse("match x { 1 => \"one\", 2 => \"two\", _ => \"other\" }").unwrap();
3024        assert_eq!(program.statements.len(), 1);
3025        if let StmtKind::Expr(Expr::Match { subject, arms }) = &program.statements[0].kind {
3026            assert!(matches!(subject.as_ref(), Expr::Ident(n) if n == "x"));
3027            assert_eq!(arms.len(), 3);
3028        } else {
3029            panic!("Expected match expression");
3030        }
3031    }
3032
3033    #[test]
3034    fn test_parse_closure() {
3035        let program = parse("let double = (x) => x * 2").unwrap();
3036        if let StmtKind::Let { value, .. } = &program.statements[0].kind {
3037            assert!(matches!(value, Expr::Closure { .. }));
3038        } else {
3039            panic!("Expected let with closure");
3040        }
3041    }
3042
3043    #[test]
3044    fn test_parse_function_call() {
3045        let program = parse("print(42)").unwrap();
3046        if let StmtKind::Expr(Expr::Call { function, args, .. }) = &program.statements[0].kind {
3047            assert!(matches!(function.as_ref(), Expr::Ident(n) if n == "print"));
3048            assert_eq!(args.len(), 1);
3049        } else {
3050            panic!("Expected function call");
3051        }
3052    }
3053
3054    #[test]
3055    fn test_parse_schema() {
3056        let program = parse("schema User { id: int64, name: string, age: float64 }").unwrap();
3057        if let StmtKind::Schema { name, fields, .. } = &program.statements[0].kind {
3058            assert_eq!(name, "User");
3059            assert_eq!(fields.len(), 3);
3060            assert_eq!(fields[0].name, "id");
3061            assert_eq!(fields[1].name, "name");
3062            assert_eq!(fields[2].name, "age");
3063        } else {
3064            panic!("Expected schema statement");
3065        }
3066    }
3067
3068    #[test]
3069    fn test_parse_pipeline_basic() {
3070        let program = parse(
3071            r#"pipeline etl {
3072            extract { let data = read_csv("input.csv") }
3073            transform { let cleaned = data }
3074            load { write_csv(cleaned, "output.csv") }
3075        }"#,
3076        )
3077        .unwrap();
3078        if let StmtKind::Pipeline {
3079            name,
3080            extract,
3081            transform,
3082            load,
3083            ..
3084        } = &program.statements[0].kind
3085        {
3086            assert_eq!(name, "etl");
3087            assert_eq!(extract.len(), 1);
3088            assert_eq!(transform.len(), 1);
3089            assert_eq!(load.len(), 1);
3090        } else {
3091            panic!("Expected pipeline statement");
3092        }
3093    }
3094
3095    #[test]
3096    fn test_parse_pipeline_with_options() {
3097        let program = parse(
3098            r#"pipeline daily_etl {
3099            schedule: "0 0 * * *",
3100            timeout: "30m",
3101            retries: 3,
3102            extract { let data = read_csv("input.csv") }
3103            transform { let cleaned = data }
3104            load { write_csv(cleaned, "output.csv") }
3105            on_failure { println("Pipeline failed!") }
3106            on_success { println("Pipeline succeeded!") }
3107        }"#,
3108        )
3109        .unwrap();
3110        if let StmtKind::Pipeline {
3111            name,
3112            schedule,
3113            timeout,
3114            retries,
3115            on_failure,
3116            on_success,
3117            ..
3118        } = &program.statements[0].kind
3119        {
3120            assert_eq!(name, "daily_etl");
3121            assert_eq!(schedule.as_deref(), Some("0 0 * * *"));
3122            assert_eq!(timeout.as_deref(), Some("30m"));
3123            assert_eq!(*retries, Some(3));
3124            assert!(on_failure.is_some());
3125            assert!(on_success.is_some());
3126        } else {
3127            panic!("Expected pipeline statement");
3128        }
3129    }
3130
3131    #[test]
3132    fn test_parse_stream_decl() {
3133        let program = parse(
3134            r#"stream events {
3135            source: src,
3136            window: tumbling(5m),
3137            transform: { let x = 1 },
3138            sink: out
3139        }"#,
3140        )
3141        .unwrap();
3142        if let StmtKind::StreamDecl {
3143            name,
3144            source,
3145            window,
3146            sink,
3147            ..
3148        } = &program.statements[0].kind
3149        {
3150            assert_eq!(name, "events");
3151            assert!(matches!(source, Expr::Ident(s) if s == "src"));
3152            assert!(matches!(window, Some(WindowSpec::Tumbling(d)) if d == "5m"));
3153            assert!(matches!(sink, Some(Expr::Ident(s)) if s == "out"));
3154        } else {
3155            panic!("Expected stream declaration");
3156        }
3157    }
3158
3159    #[test]
3160    fn test_parse_stream_sliding_window() {
3161        let program = parse(
3162            r#"stream metrics {
3163            source: input,
3164            window: sliding(10m, 1m),
3165            transform: { let x = 1 }
3166        }"#,
3167        )
3168        .unwrap();
3169        if let StmtKind::StreamDecl { window, .. } = &program.statements[0].kind {
3170            assert!(matches!(window, Some(WindowSpec::Sliding(w, s)) if w == "10m" && s == "1m"));
3171        } else {
3172            panic!("Expected stream declaration");
3173        }
3174    }
3175
3176    #[test]
3177    fn test_parse_stream_session_window() {
3178        let program = parse(
3179            r#"stream sessions {
3180            source: clicks,
3181            window: session(30m),
3182            transform: { let x = 1 }
3183        }"#,
3184        )
3185        .unwrap();
3186        if let StmtKind::StreamDecl { window, .. } = &program.statements[0].kind {
3187            assert!(matches!(window, Some(WindowSpec::Session(d)) if d == "30m"));
3188        } else {
3189            panic!("Expected stream declaration");
3190        }
3191    }
3192
3193    #[test]
3194    fn test_parse_source_decl() {
3195        let program = parse(
3196            r#"source kafka_in = connector kafka {
3197            topic: "events",
3198            group: "my_group"
3199        }"#,
3200        )
3201        .unwrap();
3202        if let StmtKind::SourceDecl {
3203            name,
3204            connector_type,
3205            config,
3206        } = &program.statements[0].kind
3207        {
3208            assert_eq!(name, "kafka_in");
3209            assert_eq!(connector_type, "kafka");
3210            assert_eq!(config.len(), 2);
3211            assert_eq!(config[0].0, "topic");
3212            assert_eq!(config[1].0, "group");
3213        } else {
3214            panic!("Expected source declaration");
3215        }
3216    }
3217
3218    #[test]
3219    fn test_parse_sink_decl() {
3220        let program = parse(
3221            r#"sink output = connector channel {
3222            buffer: 100
3223        }"#,
3224        )
3225        .unwrap();
3226        if let StmtKind::SinkDecl {
3227            name,
3228            connector_type,
3229            config,
3230        } = &program.statements[0].kind
3231        {
3232            assert_eq!(name, "output");
3233            assert_eq!(connector_type, "channel");
3234            assert_eq!(config.len(), 1);
3235            assert_eq!(config[0].0, "buffer");
3236        } else {
3237            panic!("Expected sink declaration");
3238        }
3239    }
3240
3241    #[test]
3242    fn test_parse_pipeline_with_duration_tokens() {
3243        let program = parse(
3244            r#"pipeline fast {
3245            timeout: 30s,
3246            extract { let x = 1 }
3247            transform { let y = x }
3248            load { println(y) }
3249        }"#,
3250        )
3251        .unwrap();
3252        if let StmtKind::Pipeline { timeout, .. } = &program.statements[0].kind {
3253            assert_eq!(timeout.as_deref(), Some("30s"));
3254        } else {
3255            panic!("Expected pipeline statement");
3256        }
3257    }
3258
3259    #[test]
3260    fn test_parse_stream_with_watermark() {
3261        let program = parse(
3262            r#"stream delayed {
3263            source: input,
3264            watermark: 10s,
3265            transform: { let x = 1 }
3266        }"#,
3267        )
3268        .unwrap();
3269        if let StmtKind::StreamDecl { watermark, .. } = &program.statements[0].kind {
3270            assert_eq!(watermark.as_deref(), Some("10s"));
3271        } else {
3272            panic!("Expected stream declaration");
3273        }
3274    }
3275
3276    #[test]
3277    fn test_parse_with_block() {
3278        let program = parse("with { doubled = age * 2, name = first }").unwrap();
3279        if let StmtKind::Expr(Expr::Call { function, args }) = &program.statements[0].kind {
3280            assert!(matches!(function.as_ref(), Expr::Ident(n) if n == "with"));
3281            assert_eq!(args.len(), 1);
3282            if let Expr::Map(pairs) = &args[0] {
3283                assert_eq!(pairs.len(), 2);
3284            } else {
3285                panic!("Expected Map arg");
3286            }
3287        } else {
3288            panic!("Expected with call expression");
3289        }
3290    }
3291
3292    #[test]
3293    fn test_parse_struct_decl() {
3294        let program = parse("struct Point { x: float64, y: float64 }").unwrap();
3295        if let StmtKind::StructDecl { name, fields, .. } = &program.statements[0].kind {
3296            assert_eq!(name, "Point");
3297            assert_eq!(fields.len(), 2);
3298            assert_eq!(fields[0].name, "x");
3299            assert!(matches!(&fields[0].type_ann, TypeExpr::Named(t) if t == "float64"));
3300            assert_eq!(fields[1].name, "y");
3301            assert!(matches!(&fields[1].type_ann, TypeExpr::Named(t) if t == "float64"));
3302        } else {
3303            panic!("Expected struct declaration");
3304        }
3305    }
3306
3307    #[test]
3308    fn test_parse_struct_init() {
3309        let program = parse("let p = Point { x: 1.0, y: 2.0 }").unwrap();
3310        if let StmtKind::Let { name, value, .. } = &program.statements[0].kind {
3311            assert_eq!(name, "p");
3312            if let Expr::StructInit {
3313                name: struct_name,
3314                fields,
3315            } = value
3316            {
3317                assert_eq!(struct_name, "Point");
3318                assert_eq!(fields.len(), 2);
3319                assert_eq!(fields[0].0, "x");
3320                assert!(matches!(&fields[0].1, Expr::Float(v) if *v == 1.0));
3321                assert_eq!(fields[1].0, "y");
3322                assert!(matches!(&fields[1].1, Expr::Float(v) if *v == 2.0));
3323            } else {
3324                panic!("Expected StructInit expression");
3325            }
3326        } else {
3327            panic!("Expected let statement");
3328        }
3329    }
3330
3331    #[test]
3332    fn test_parse_enum_decl() {
3333        let program = parse("enum Color { Red, Green, Blue }").unwrap();
3334        if let StmtKind::EnumDecl { name, variants, .. } = &program.statements[0].kind {
3335            assert_eq!(name, "Color");
3336            assert_eq!(variants.len(), 3);
3337            assert_eq!(variants[0].name, "Red");
3338            assert!(variants[0].fields.is_empty());
3339            assert_eq!(variants[1].name, "Green");
3340            assert!(variants[1].fields.is_empty());
3341            assert_eq!(variants[2].name, "Blue");
3342            assert!(variants[2].fields.is_empty());
3343        } else {
3344            panic!("Expected enum declaration");
3345        }
3346    }
3347
3348    #[test]
3349    fn test_parse_enum_variant() {
3350        // Simple variant: Color::Red
3351        let program = parse("Color::Red").unwrap();
3352        if let StmtKind::Expr(Expr::EnumVariant {
3353            enum_name,
3354            variant,
3355            args,
3356        }) = &program.statements[0].kind
3357        {
3358            assert_eq!(enum_name, "Color");
3359            assert_eq!(variant, "Red");
3360            assert!(args.is_empty());
3361        } else {
3362            panic!("Expected enum variant expression");
3363        }
3364
3365        // Variant with args: Color::Custom(1, 2, 3)
3366        let program = parse("Color::Custom(1, 2, 3)").unwrap();
3367        if let StmtKind::Expr(Expr::EnumVariant {
3368            enum_name,
3369            variant,
3370            args,
3371        }) = &program.statements[0].kind
3372        {
3373            assert_eq!(enum_name, "Color");
3374            assert_eq!(variant, "Custom");
3375            assert_eq!(args.len(), 3);
3376            assert!(matches!(&args[0], Expr::Int(1)));
3377            assert!(matches!(&args[1], Expr::Int(2)));
3378            assert!(matches!(&args[2], Expr::Int(3)));
3379        } else {
3380            panic!("Expected enum variant expression with args");
3381        }
3382    }
3383
3384    #[test]
3385    fn test_parse_impl_block() {
3386        let program = parse("impl Point { fn area(self) { self.x * self.y } }").unwrap();
3387        if let StmtKind::ImplBlock {
3388            type_name, methods, ..
3389        } = &program.statements[0].kind
3390        {
3391            assert_eq!(type_name, "Point");
3392            assert_eq!(methods.len(), 1);
3393            if let StmtKind::FnDecl {
3394                name, params, body, ..
3395            } = &methods[0].kind
3396            {
3397                assert_eq!(name, "area");
3398                assert_eq!(params.len(), 1);
3399                assert_eq!(params[0].name, "self");
3400                assert_eq!(body.len(), 1);
3401            } else {
3402                panic!("Expected fn declaration inside impl block");
3403            }
3404        } else {
3405            panic!("Expected impl block");
3406        }
3407    }
3408
3409    #[test]
3410    fn test_parse_try_catch() {
3411        let program = parse("try { 1 + 2 } catch e { println(e) }").unwrap();
3412        if let StmtKind::TryCatch {
3413            try_body,
3414            catch_var,
3415            catch_body,
3416            ..
3417        } = &program.statements[0].kind
3418        {
3419            assert_eq!(try_body.len(), 1);
3420            assert_eq!(catch_var, "e");
3421            assert_eq!(catch_body.len(), 1);
3422            // Verify try body contains the expression 1 + 2
3423            if let StmtKind::Expr(Expr::BinOp { op, .. }) = &try_body[0].kind {
3424                assert_eq!(*op, BinOp::Add);
3425            } else {
3426                panic!("Expected binary op in try body");
3427            }
3428            // Verify catch body contains println(e)
3429            if let StmtKind::Expr(Expr::Call { function, args }) = &catch_body[0].kind {
3430                assert!(matches!(function.as_ref(), Expr::Ident(n) if n == "println"));
3431                assert_eq!(args.len(), 1);
3432            } else {
3433                panic!("Expected function call in catch body");
3434            }
3435        } else {
3436            panic!("Expected try/catch statement");
3437        }
3438    }
3439
3440    #[test]
3441    fn test_parse_throw() {
3442        let program = parse(r#"throw "error""#).unwrap();
3443        if let StmtKind::Throw(expr) = &program.statements[0].kind {
3444            assert!(matches!(expr, Expr::String(s) if s == "error"));
3445        } else {
3446            panic!("Expected throw statement");
3447        }
3448    }
3449
3450    #[test]
3451    fn test_parse_import() {
3452        // Simple import
3453        let program = parse(r#"import "utils.tl""#).unwrap();
3454        if let StmtKind::Import { path, alias } = &program.statements[0].kind {
3455            assert_eq!(path, "utils.tl");
3456            assert!(alias.is_none());
3457        } else {
3458            panic!("Expected import statement");
3459        }
3460
3461        // Import with alias
3462        let program = parse(r#"import "math.tl" as math"#).unwrap();
3463        if let StmtKind::Import { path, alias } = &program.statements[0].kind {
3464            assert_eq!(path, "math.tl");
3465            assert_eq!(alias.as_deref(), Some("math"));
3466        } else {
3467            panic!("Expected import statement with alias");
3468        }
3469    }
3470
3471    #[test]
3472    fn test_parse_test() {
3473        let program = parse(r#"test "my test" { assert(true) }"#).unwrap();
3474        if let StmtKind::Test { name, body } = &program.statements[0].kind {
3475            assert_eq!(name, "my test");
3476            assert_eq!(body.len(), 1);
3477            if let StmtKind::Expr(Expr::Call { function, args }) = &body[0].kind {
3478                assert!(matches!(function.as_ref(), Expr::Ident(n) if n == "assert"));
3479                assert_eq!(args.len(), 1);
3480                assert!(matches!(&args[0], Expr::Bool(true)));
3481            } else {
3482                panic!("Expected function call in test body");
3483            }
3484        } else {
3485            panic!("Expected test statement");
3486        }
3487    }
3488
3489    #[test]
3490    fn test_parse_method_call() {
3491        let program = parse(r#""hello".split(" ")"#).unwrap();
3492        if let StmtKind::Expr(Expr::Call { function, args }) = &program.statements[0].kind {
3493            // The function should be a Member access: "hello".split
3494            if let Expr::Member { object, field } = function.as_ref() {
3495                assert!(matches!(object.as_ref(), Expr::String(s) if s == "hello"));
3496                assert_eq!(field, "split");
3497            } else {
3498                panic!("Expected member access as call function");
3499            }
3500            assert_eq!(args.len(), 1);
3501            assert!(matches!(&args[0], Expr::String(s) if s == " "));
3502        } else {
3503            panic!("Expected method call expression");
3504        }
3505    }
3506
3507    // Phase 7: Concurrency parser tests
3508
3509    #[test]
3510    fn test_parse_await_expr() {
3511        let program = parse("await x").unwrap();
3512        if let StmtKind::Expr(Expr::Await(inner)) = &program.statements[0].kind {
3513            assert!(matches!(inner.as_ref(), Expr::Ident(s) if s == "x"));
3514        } else {
3515            panic!("Expected Await expression, got {:?}", program.statements[0]);
3516        }
3517    }
3518
3519    #[test]
3520    fn test_parse_await_spawn() {
3521        let program = parse("await spawn(f)").unwrap();
3522        if let StmtKind::Expr(Expr::Await(inner)) = &program.statements[0].kind {
3523            assert!(matches!(inner.as_ref(), Expr::Call { .. }));
3524        } else {
3525            panic!("Expected Await(Call(...))");
3526        }
3527    }
3528
3529    #[test]
3530    fn test_parse_yield_expr() {
3531        let program = parse("yield 42").unwrap();
3532        if let StmtKind::Expr(Expr::Yield(Some(inner))) = &program.statements[0].kind {
3533            assert!(matches!(inner.as_ref(), Expr::Int(42)));
3534        } else {
3535            panic!("Expected Yield(Some(Int(42)))");
3536        }
3537    }
3538
3539    #[test]
3540    fn test_parse_bare_yield() {
3541        let program = parse("fn gen() { yield }").unwrap();
3542        if let StmtKind::FnDecl {
3543            body, is_generator, ..
3544        } = &program.statements[0].kind
3545        {
3546            assert!(*is_generator);
3547            if let StmtKind::Expr(Expr::Yield(None)) = &body[0].kind {
3548                // ok
3549            } else {
3550                panic!("Expected bare Yield(None), got {:?}", body[0]);
3551            }
3552        } else {
3553            panic!("Expected FnDecl");
3554        }
3555    }
3556
3557    #[test]
3558    fn test_parse_generator_fn() {
3559        let program = parse("fn gen() { yield 1\nyield 2 }").unwrap();
3560        if let StmtKind::FnDecl {
3561            name,
3562            is_generator,
3563            body,
3564            ..
3565        } = &program.statements[0].kind
3566        {
3567            assert_eq!(name, "gen");
3568            assert!(*is_generator);
3569            assert_eq!(body.len(), 2);
3570        } else {
3571            panic!("Expected FnDecl");
3572        }
3573    }
3574
3575    #[test]
3576    fn test_parse_non_generator_fn() {
3577        let program = parse("fn add(a, b) { return a }").unwrap();
3578        if let StmtKind::FnDecl { is_generator, .. } = &program.statements[0].kind {
3579            assert!(!*is_generator);
3580        } else {
3581            panic!("Expected FnDecl");
3582        }
3583    }
3584
3585    // ── Phase 11: Module System Parser Tests ──
3586
3587    #[test]
3588    fn test_parse_pub_fn() {
3589        let program = parse("pub fn foo() { 1 }").unwrap();
3590        if let StmtKind::FnDecl {
3591            name, is_public, ..
3592        } = &program.statements[0].kind
3593        {
3594            assert_eq!(name, "foo");
3595            assert!(*is_public);
3596        } else {
3597            panic!("Expected pub FnDecl");
3598        }
3599    }
3600
3601    #[test]
3602    fn test_parse_pub_struct() {
3603        let program = parse("pub struct Foo { x: int }").unwrap();
3604        if let StmtKind::StructDecl {
3605            name, is_public, ..
3606        } = &program.statements[0].kind
3607        {
3608            assert_eq!(name, "Foo");
3609            assert!(*is_public);
3610        } else {
3611            panic!("Expected pub StructDecl");
3612        }
3613    }
3614
3615    #[test]
3616    fn test_parse_use_single() {
3617        let program = parse("use data.transforms.clean").unwrap();
3618        if let StmtKind::Use { item, is_public } = &program.statements[0].kind {
3619            assert!(!*is_public);
3620            if let UseItem::Single(path) = item {
3621                assert_eq!(path, &["data", "transforms", "clean"]);
3622            } else {
3623                panic!("Expected UseItem::Single");
3624            }
3625        } else {
3626            panic!("Expected Use statement");
3627        }
3628    }
3629
3630    #[test]
3631    fn test_parse_use_group() {
3632        let program = parse("use data.transforms.{a, b}").unwrap();
3633        if let StmtKind::Use { item, .. } = &program.statements[0].kind {
3634            if let UseItem::Group(prefix, names) = item {
3635                assert_eq!(prefix, &["data", "transforms"]);
3636                assert_eq!(names, &["a", "b"]);
3637            } else {
3638                panic!("Expected UseItem::Group");
3639            }
3640        } else {
3641            panic!("Expected Use statement");
3642        }
3643    }
3644
3645    #[test]
3646    fn test_parse_use_wildcard() {
3647        let program = parse("use data.transforms.*").unwrap();
3648        if let StmtKind::Use { item, .. } = &program.statements[0].kind {
3649            if let UseItem::Wildcard(path) = item {
3650                assert_eq!(path, &["data", "transforms"]);
3651            } else {
3652                panic!("Expected UseItem::Wildcard");
3653            }
3654        } else {
3655            panic!("Expected Use statement");
3656        }
3657    }
3658
3659    #[test]
3660    fn test_parse_use_aliased() {
3661        let program = parse("use data.postgres as pg").unwrap();
3662        if let StmtKind::Use { item, .. } = &program.statements[0].kind {
3663            if let UseItem::Aliased(path, alias) = item {
3664                assert_eq!(path, &["data", "postgres"]);
3665                assert_eq!(alias, "pg");
3666            } else {
3667                panic!("Expected UseItem::Aliased");
3668            }
3669        } else {
3670            panic!("Expected Use statement");
3671        }
3672    }
3673
3674    #[test]
3675    fn test_parse_pub_use() {
3676        let program = parse("pub use data.clean").unwrap();
3677        if let StmtKind::Use { item, is_public } = &program.statements[0].kind {
3678            assert!(*is_public);
3679            assert!(matches!(item, UseItem::Single(p) if p == &["data", "clean"]));
3680        } else {
3681            panic!("Expected pub Use statement");
3682        }
3683    }
3684
3685    #[test]
3686    fn test_parse_pub_mod() {
3687        let program = parse("pub mod transforms").unwrap();
3688        if let StmtKind::ModDecl { name, is_public } = &program.statements[0].kind {
3689            assert_eq!(name, "transforms");
3690            assert!(*is_public);
3691        } else {
3692            panic!("Expected pub ModDecl");
3693        }
3694    }
3695
3696    #[test]
3697    fn test_parse_mod() {
3698        let program = parse("mod quality").unwrap();
3699        if let StmtKind::ModDecl { name, is_public } = &program.statements[0].kind {
3700            assert_eq!(name, "quality");
3701            assert!(!*is_public);
3702        } else {
3703            panic!("Expected ModDecl");
3704        }
3705    }
3706
3707    #[test]
3708    fn test_fn_default_not_public() {
3709        let program = parse("fn foo() { 1 }").unwrap();
3710        if let StmtKind::FnDecl { is_public, .. } = &program.statements[0].kind {
3711            assert!(!*is_public);
3712        } else {
3713            panic!("Expected FnDecl");
3714        }
3715    }
3716
3717    // ── Phase 12: Generics & Traits ──────────────────────────
3718
3719    #[test]
3720    fn test_generic_fn() {
3721        let program = parse("fn identity<T>(x: T) -> T { x }").unwrap();
3722        if let StmtKind::FnDecl {
3723            name,
3724            type_params,
3725            params,
3726            return_type,
3727            ..
3728        } = &program.statements[0].kind
3729        {
3730            assert_eq!(name, "identity");
3731            assert_eq!(type_params, &vec!["T".to_string()]);
3732            assert_eq!(params.len(), 1);
3733            assert!(return_type.is_some());
3734        } else {
3735            panic!("Expected FnDecl");
3736        }
3737    }
3738
3739    #[test]
3740    fn test_generic_struct() {
3741        let program = parse("struct Pair<A, B> { first: A, second: B }").unwrap();
3742        if let StmtKind::StructDecl {
3743            name,
3744            type_params,
3745            fields,
3746            ..
3747        } = &program.statements[0].kind
3748        {
3749            assert_eq!(name, "Pair");
3750            assert_eq!(type_params, &vec!["A".to_string(), "B".to_string()]);
3751            assert_eq!(fields.len(), 2);
3752        } else {
3753            panic!("Expected StructDecl");
3754        }
3755    }
3756
3757    #[test]
3758    fn test_generic_enum() {
3759        let program = parse("enum MyOption<T> { Some(T), Nothing }").unwrap();
3760        if let StmtKind::EnumDecl {
3761            name,
3762            type_params,
3763            variants,
3764            ..
3765        } = &program.statements[0].kind
3766        {
3767            assert_eq!(name, "MyOption");
3768            assert_eq!(type_params, &vec!["T".to_string()]);
3769            assert_eq!(variants.len(), 2);
3770            assert_eq!(variants[0].name, "Some");
3771            assert_eq!(variants[1].name, "Nothing");
3772        } else {
3773            panic!("Expected EnumDecl");
3774        }
3775    }
3776
3777    #[test]
3778    fn test_inline_trait_bound() {
3779        let program = parse("fn foo<T: Comparable>(x: T) { x }").unwrap();
3780        if let StmtKind::FnDecl {
3781            type_params,
3782            bounds,
3783            ..
3784        } = &program.statements[0].kind
3785        {
3786            assert_eq!(type_params, &vec!["T".to_string()]);
3787            assert_eq!(bounds.len(), 1);
3788            assert_eq!(bounds[0].type_param, "T");
3789            assert_eq!(bounds[0].traits, vec!["Comparable".to_string()]);
3790        } else {
3791            panic!("Expected FnDecl");
3792        }
3793    }
3794
3795    #[test]
3796    fn test_where_clause() {
3797        let program = parse("fn foo<T>(x: T) where T: Comparable + Hashable { x }").unwrap();
3798        if let StmtKind::FnDecl {
3799            type_params,
3800            bounds,
3801            ..
3802        } = &program.statements[0].kind
3803        {
3804            assert_eq!(type_params, &vec!["T".to_string()]);
3805            assert_eq!(bounds.len(), 1);
3806            assert_eq!(bounds[0].type_param, "T");
3807            assert_eq!(
3808                bounds[0].traits,
3809                vec!["Comparable".to_string(), "Hashable".to_string()]
3810            );
3811        } else {
3812            panic!("Expected FnDecl");
3813        }
3814    }
3815
3816    #[test]
3817    fn test_trait_def() {
3818        let program = parse("trait Display { fn show(self) -> string }").unwrap();
3819        if let StmtKind::TraitDef {
3820            name,
3821            type_params,
3822            methods,
3823            is_public,
3824        } = &program.statements[0].kind
3825        {
3826            assert_eq!(name, "Display");
3827            assert!(type_params.is_empty());
3828            assert_eq!(methods.len(), 1);
3829            assert_eq!(methods[0].name, "show");
3830            assert!(!*is_public);
3831        } else {
3832            panic!("Expected TraitDef");
3833        }
3834    }
3835
3836    #[test]
3837    fn test_trait_impl() {
3838        let program =
3839            parse("impl Display for Point { fn show(self) -> string { \"point\" } }").unwrap();
3840        if let StmtKind::TraitImpl {
3841            trait_name,
3842            type_name,
3843            methods,
3844            ..
3845        } = &program.statements[0].kind
3846        {
3847            assert_eq!(trait_name, "Display");
3848            assert_eq!(type_name, "Point");
3849            assert_eq!(methods.len(), 1);
3850        } else {
3851            panic!("Expected TraitImpl");
3852        }
3853    }
3854
3855    #[test]
3856    fn test_generic_trait_impl() {
3857        let program =
3858            parse("impl<T> Display for Box<T> { fn show(self) -> string { \"box\" } }").unwrap();
3859        if let StmtKind::TraitImpl {
3860            trait_name,
3861            type_name,
3862            type_params,
3863            methods,
3864        } = &program.statements[0].kind
3865        {
3866            assert_eq!(trait_name, "Display");
3867            assert_eq!(type_name, "Box");
3868            assert_eq!(type_params, &vec!["T".to_string()]);
3869            assert_eq!(methods.len(), 1);
3870        } else {
3871            panic!("Expected TraitImpl");
3872        }
3873    }
3874
3875    #[test]
3876    fn test_pub_trait() {
3877        let program = parse("pub trait Serializable { fn serialize(self) -> string }").unwrap();
3878        if let StmtKind::TraitDef {
3879            name, is_public, ..
3880        } = &program.statements[0].kind
3881        {
3882            assert_eq!(name, "Serializable");
3883            assert!(*is_public);
3884        } else {
3885            panic!("Expected TraitDef");
3886        }
3887    }
3888
3889    #[test]
3890    fn test_multiple_type_params() {
3891        let program = parse("fn zip<A, B>(a: list<A>, b: list<B>) { a }").unwrap();
3892        if let StmtKind::FnDecl { type_params, .. } = &program.statements[0].kind {
3893            assert_eq!(type_params, &vec!["A".to_string(), "B".to_string()]);
3894        } else {
3895            panic!("Expected FnDecl");
3896        }
3897    }
3898
3899    #[test]
3900    fn test_existing_code_no_type_params() {
3901        // Existing code should still parse with empty type_params
3902        let program = parse("fn add(a, b) { a + b }").unwrap();
3903        if let StmtKind::FnDecl {
3904            type_params,
3905            bounds,
3906            ..
3907        } = &program.statements[0].kind
3908        {
3909            assert!(type_params.is_empty());
3910            assert!(bounds.is_empty());
3911        } else {
3912            panic!("Expected FnDecl");
3913        }
3914    }
3915
3916    #[test]
3917    fn test_trait_with_multiple_methods() {
3918        let program =
3919            parse("trait Container { fn len(self) -> int fn is_empty(self) -> bool }").unwrap();
3920        if let StmtKind::TraitDef { name, methods, .. } = &program.statements[0].kind {
3921            assert_eq!(name, "Container");
3922            assert_eq!(methods.len(), 2);
3923            assert_eq!(methods[0].name, "len");
3924            assert_eq!(methods[1].name, "is_empty");
3925        } else {
3926            panic!("Expected TraitDef");
3927        }
3928    }
3929
3930    #[test]
3931    fn test_generic_impl_block() {
3932        let program = parse("impl<T> Box { fn get(self) -> T { self.val } }").unwrap();
3933        if let StmtKind::ImplBlock {
3934            type_name,
3935            type_params,
3936            ..
3937        } = &program.statements[0].kind
3938        {
3939            assert_eq!(type_name, "Box");
3940            assert_eq!(type_params, &vec!["T".to_string()]);
3941        } else {
3942            panic!("Expected ImplBlock");
3943        }
3944    }
3945
3946    // ── Phase 17: Pattern Matching ──
3947
3948    #[test]
3949    fn test_parse_match_wildcard() {
3950        let program = parse("match x { _ => 1 }").unwrap();
3951        if let StmtKind::Expr(Expr::Match { arms, .. }) = &program.statements[0].kind {
3952            assert!(matches!(arms[0].pattern, Pattern::Wildcard));
3953        } else {
3954            panic!("Expected match expression");
3955        }
3956    }
3957
3958    #[test]
3959    fn test_parse_match_literal() {
3960        let program = parse("match x { 1 => \"one\", 2 => \"two\", _ => \"other\" }").unwrap();
3961        if let StmtKind::Expr(Expr::Match { arms, .. }) = &program.statements[0].kind {
3962            assert_eq!(arms.len(), 3);
3963            assert!(matches!(arms[0].pattern, Pattern::Literal(Expr::Int(1))));
3964            assert!(matches!(arms[1].pattern, Pattern::Literal(Expr::Int(2))));
3965            assert!(matches!(arms[2].pattern, Pattern::Wildcard));
3966        } else {
3967            panic!("Expected match expression");
3968        }
3969    }
3970
3971    #[test]
3972    fn test_parse_match_binding() {
3973        let program = parse("match x { val => val + 1 }").unwrap();
3974        if let StmtKind::Expr(Expr::Match { arms, .. }) = &program.statements[0].kind {
3975            if let Pattern::Binding(name) = &arms[0].pattern {
3976                assert_eq!(name, "val");
3977            } else {
3978                panic!("Expected binding pattern");
3979            }
3980        } else {
3981            panic!("Expected match expression");
3982        }
3983    }
3984
3985    #[test]
3986    fn test_parse_match_enum_variant() {
3987        let program = parse("match x { Color::Red => 1, Color::Blue => 2 }").unwrap();
3988        if let StmtKind::Expr(Expr::Match { arms, .. }) = &program.statements[0].kind {
3989            if let Pattern::Enum {
3990                type_name,
3991                variant,
3992                args,
3993            } = &arms[0].pattern
3994            {
3995                assert_eq!(type_name, "Color");
3996                assert_eq!(variant, "Red");
3997                assert!(args.is_empty());
3998            } else {
3999                panic!("Expected enum pattern");
4000            }
4001        } else {
4002            panic!("Expected match expression");
4003        }
4004    }
4005
4006    #[test]
4007    fn test_parse_match_enum_with_args() {
4008        let program =
4009            parse("match x { Shape::Circle(r) => r, Shape::Rect(w, h) => w * h }").unwrap();
4010        if let StmtKind::Expr(Expr::Match { arms, .. }) = &program.statements[0].kind {
4011            if let Pattern::Enum { variant, args, .. } = &arms[0].pattern {
4012                assert_eq!(variant, "Circle");
4013                assert_eq!(args.len(), 1);
4014                assert!(matches!(args[0], Pattern::Binding(_)));
4015            } else {
4016                panic!("Expected enum pattern with args");
4017            }
4018        } else {
4019            panic!("Expected match expression");
4020        }
4021    }
4022
4023    #[test]
4024    fn test_parse_match_guard() {
4025        let program = parse("match x { n if n > 0 => \"pos\", _ => \"other\" }").unwrap();
4026        if let StmtKind::Expr(Expr::Match { arms, .. }) = &program.statements[0].kind {
4027            assert!(arms[0].guard.is_some());
4028            assert!(matches!(arms[0].pattern, Pattern::Binding(_)));
4029            assert!(arms[1].guard.is_none());
4030        } else {
4031            panic!("Expected match expression");
4032        }
4033    }
4034
4035    #[test]
4036    fn test_parse_match_or_pattern() {
4037        let program = parse("match x { 1 or 2 or 3 => \"small\", _ => \"big\" }").unwrap();
4038        if let StmtKind::Expr(Expr::Match { arms, .. }) = &program.statements[0].kind {
4039            if let Pattern::Or(pats) = &arms[0].pattern {
4040                assert_eq!(pats.len(), 3);
4041            } else {
4042                panic!("Expected OR pattern");
4043            }
4044        } else {
4045            panic!("Expected match expression");
4046        }
4047    }
4048
4049    #[test]
4050    fn test_parse_list_pattern() {
4051        let program = parse("match x { [a, b] => a + b, _ => 0 }").unwrap();
4052        if let StmtKind::Expr(Expr::Match { arms, .. }) = &program.statements[0].kind {
4053            if let Pattern::List { elements, rest } = &arms[0].pattern {
4054                assert_eq!(elements.len(), 2);
4055                assert!(rest.is_none());
4056            } else {
4057                panic!("Expected list pattern");
4058            }
4059        } else {
4060            panic!("Expected match expression");
4061        }
4062    }
4063
4064    #[test]
4065    fn test_parse_list_rest_pattern() {
4066        let program = parse("match x { [head, ...tail] => head, _ => 0 }").unwrap();
4067        if let StmtKind::Expr(Expr::Match { arms, .. }) = &program.statements[0].kind {
4068            if let Pattern::List { elements, rest } = &arms[0].pattern {
4069                assert_eq!(elements.len(), 1);
4070                assert_eq!(rest.as_deref(), Some("tail"));
4071            } else {
4072                panic!("Expected list pattern with rest");
4073            }
4074        } else {
4075            panic!("Expected match expression");
4076        }
4077    }
4078
4079    #[test]
4080    fn test_parse_struct_pattern() {
4081        let program = parse("match p { Point { x, y } => x + y, _ => 0 }").unwrap();
4082        if let StmtKind::Expr(Expr::Match { arms, .. }) = &program.statements[0].kind {
4083            if let Pattern::Struct { name, fields } = &arms[0].pattern {
4084                assert_eq!(name.as_deref(), Some("Point"));
4085                assert_eq!(fields.len(), 2);
4086                assert_eq!(fields[0].name, "x");
4087                assert_eq!(fields[1].name, "y");
4088            } else {
4089                panic!("Expected struct pattern");
4090            }
4091        } else {
4092            panic!("Expected match expression");
4093        }
4094    }
4095
4096    #[test]
4097    fn test_parse_negative_literal_pattern() {
4098        let program = parse("match x { -5 => \"neg five\", _ => \"other\" }").unwrap();
4099        if let StmtKind::Expr(Expr::Match { arms, .. }) = &program.statements[0].kind {
4100            if let Pattern::Literal(Expr::Int(-5)) = &arms[0].pattern {
4101                // ok
4102            } else {
4103                panic!(
4104                    "Expected negative literal pattern, got {:?}",
4105                    arms[0].pattern
4106                );
4107            }
4108        } else {
4109            panic!("Expected match expression");
4110        }
4111    }
4112
4113    #[test]
4114    fn test_parse_let_destructure_list() {
4115        let program = parse("let [a, b, c] = [1, 2, 3]").unwrap();
4116        if let StmtKind::LetDestructure { pattern, .. } = &program.statements[0].kind {
4117            if let Pattern::List { elements, rest } = pattern {
4118                assert_eq!(elements.len(), 3);
4119                assert!(rest.is_none());
4120            } else {
4121                panic!("Expected list pattern");
4122            }
4123        } else {
4124            panic!("Expected LetDestructure");
4125        }
4126    }
4127
4128    #[test]
4129    fn test_parse_let_destructure_struct() {
4130        let program = parse("let { x, y } = point").unwrap();
4131        if let StmtKind::LetDestructure { pattern, .. } = &program.statements[0].kind {
4132            if let Pattern::Struct { name, fields } = pattern {
4133                assert!(name.is_none());
4134                assert_eq!(fields.len(), 2);
4135            } else {
4136                panic!("Expected struct pattern");
4137            }
4138        } else {
4139            panic!("Expected LetDestructure");
4140        }
4141    }
4142
4143    #[test]
4144    fn test_parse_case_with_match_arm() {
4145        let program = parse("case { x > 10 => \"big\", _ => \"small\" }").unwrap();
4146        if let StmtKind::Expr(Expr::Case { arms }) = &program.statements[0].kind {
4147            assert_eq!(arms.len(), 2);
4148            // First arm: Wildcard + guard
4149            assert!(matches!(arms[0].pattern, Pattern::Wildcard));
4150            assert!(arms[0].guard.is_some());
4151            // Second arm: Wildcard, no guard (default)
4152            assert!(matches!(arms[1].pattern, Pattern::Wildcard));
4153            assert!(arms[1].guard.is_none());
4154        } else {
4155            panic!("Expected case expression");
4156        }
4157    }
4158
4159    #[test]
4160    fn test_parse_backward_compat_match() {
4161        // Existing match syntax should still work
4162        let program = parse("match x { 1 => \"one\", 2 => \"two\", _ => \"other\" }").unwrap();
4163        if let StmtKind::Expr(Expr::Match { arms, .. }) = &program.statements[0].kind {
4164            assert_eq!(arms.len(), 3);
4165        } else {
4166            panic!("Expected match expression");
4167        }
4168    }
4169
4170    // ── Phase 18: Closures & Lambdas Improvements ────────────────
4171
4172    #[test]
4173    fn test_parse_expr_closure_still_works() {
4174        let program = parse("let f = (x) => x * 2").unwrap();
4175        if let StmtKind::Let { value, .. } = &program.statements[0].kind {
4176            if let Expr::Closure {
4177                params,
4178                body,
4179                return_type,
4180            } = value
4181            {
4182                assert_eq!(params.len(), 1);
4183                assert_eq!(params[0].name, "x");
4184                assert!(return_type.is_none());
4185                assert!(matches!(body, ClosureBody::Expr(_)));
4186            } else {
4187                panic!("Expected closure");
4188            }
4189        } else {
4190            panic!("Expected let");
4191        }
4192    }
4193
4194    #[test]
4195    fn test_parse_block_body_closure() {
4196        let program = parse("let f = (x: int64) -> int64 { let y = x * 2\n y + 1 }").unwrap();
4197        if let StmtKind::Let { value, .. } = &program.statements[0].kind {
4198            if let Expr::Closure {
4199                params,
4200                body,
4201                return_type,
4202            } = value
4203            {
4204                assert_eq!(params.len(), 1);
4205                assert_eq!(params[0].name, "x");
4206                assert!(return_type.is_some());
4207                if let ClosureBody::Block { stmts, expr } = body {
4208                    assert_eq!(stmts.len(), 1); // let y = x * 2
4209                    assert!(expr.is_some()); // y + 1
4210                } else {
4211                    panic!("Expected block body");
4212                }
4213            } else {
4214                panic!("Expected closure");
4215            }
4216        } else {
4217            panic!("Expected let");
4218        }
4219    }
4220
4221    #[test]
4222    fn test_is_closure_ahead_arrow() {
4223        // (x) -> int64 { ... } should be detected as closure
4224        let program = parse("let f = (x) -> int64 { x + 1 }").unwrap();
4225        if let StmtKind::Let { value, .. } = &program.statements[0].kind {
4226            assert!(matches!(value, Expr::Closure { .. }));
4227        } else {
4228            panic!("Expected let with closure");
4229        }
4230    }
4231
4232    #[test]
4233    fn test_parse_block_body_closure_no_params() {
4234        let program = parse("let f = () -> int64 { 42 }").unwrap();
4235        if let StmtKind::Let { value, .. } = &program.statements[0].kind {
4236            if let Expr::Closure { params, body, .. } = value {
4237                assert_eq!(params.len(), 0);
4238                if let ClosureBody::Block { stmts, expr } = body {
4239                    assert!(stmts.is_empty());
4240                    assert!(expr.is_some());
4241                } else {
4242                    panic!("Expected block body");
4243                }
4244            } else {
4245                panic!("Expected closure");
4246            }
4247        } else {
4248            panic!("Expected let");
4249        }
4250    }
4251
4252    #[test]
4253    fn test_parse_type_alias_simple() {
4254        let program = parse("type Mapper = fn(int64) -> int64").unwrap();
4255        if let StmtKind::TypeAlias {
4256            name,
4257            type_params,
4258            value,
4259            is_public,
4260        } = &program.statements[0].kind
4261        {
4262            assert_eq!(name, "Mapper");
4263            assert!(type_params.is_empty());
4264            assert!(!is_public);
4265            assert!(matches!(value, TypeExpr::Function { .. }));
4266        } else {
4267            panic!("Expected TypeAlias");
4268        }
4269    }
4270
4271    #[test]
4272    fn test_parse_type_alias_generic() {
4273        let program = parse("type Pair<T> = list<T>").unwrap();
4274        if let StmtKind::TypeAlias {
4275            name, type_params, ..
4276        } = &program.statements[0].kind
4277        {
4278            assert_eq!(name, "Pair");
4279            assert_eq!(type_params, &["T"]);
4280        } else {
4281            panic!("Expected TypeAlias");
4282        }
4283    }
4284
4285    #[test]
4286    fn test_parse_pub_type_alias() {
4287        let program = parse("pub type Predicate = fn(string) -> bool").unwrap();
4288        if let StmtKind::TypeAlias {
4289            name, is_public, ..
4290        } = &program.statements[0].kind
4291        {
4292            assert_eq!(name, "Predicate");
4293            assert!(is_public);
4294        } else {
4295            panic!("Expected TypeAlias");
4296        }
4297    }
4298
4299    #[test]
4300    fn test_parse_shorthand_closure() {
4301        let program = parse("let f = x => x * 2").unwrap();
4302        if let StmtKind::Let { value, .. } = &program.statements[0].kind {
4303            if let Expr::Closure {
4304                params,
4305                body,
4306                return_type,
4307            } = value
4308            {
4309                assert_eq!(params.len(), 1);
4310                assert_eq!(params[0].name, "x");
4311                assert!(return_type.is_none());
4312                assert!(matches!(body, ClosureBody::Expr(_)));
4313            } else {
4314                panic!("Expected closure");
4315            }
4316        } else {
4317            panic!("Expected let");
4318        }
4319    }
4320
4321    #[test]
4322    fn test_parse_shorthand_closure_in_call() {
4323        // Shorthand closure as argument: map(list, x => x + 1)
4324        let program = parse("map(nums, x => x + 1)").unwrap();
4325        if let StmtKind::Expr(Expr::Call { args, .. }) = &program.statements[0].kind {
4326            assert_eq!(args.len(), 2);
4327            assert!(matches!(&args[1], Expr::Closure { .. }));
4328        } else {
4329            panic!("Expected call with closure arg");
4330        }
4331    }
4332
4333    // ── Phase 19: Doc comment parsing tests ─────────────────────
4334
4335    #[test]
4336    fn test_doc_comment_on_fn() {
4337        let program = parse("/// Adds two numbers\nfn add(a, b) { a + b }").unwrap();
4338        assert_eq!(
4339            program.statements[0].doc_comment.as_deref(),
4340            Some("Adds two numbers")
4341        );
4342    }
4343
4344    #[test]
4345    fn test_doc_comment_on_struct() {
4346        let program = parse("/// A 2D point\nstruct Point { x: int, y: int }").unwrap();
4347        assert_eq!(
4348            program.statements[0].doc_comment.as_deref(),
4349            Some("A 2D point")
4350        );
4351    }
4352
4353    #[test]
4354    fn test_doc_comment_on_enum() {
4355        let program = parse("/// Color values\nenum Color { Red, Green, Blue }").unwrap();
4356        assert_eq!(
4357            program.statements[0].doc_comment.as_deref(),
4358            Some("Color values")
4359        );
4360    }
4361
4362    #[test]
4363    fn test_doc_comment_on_trait() {
4364        let program =
4365            parse("/// Display trait\ntrait Display { fn show(self) -> string }").unwrap();
4366        assert_eq!(
4367            program.statements[0].doc_comment.as_deref(),
4368            Some("Display trait")
4369        );
4370    }
4371
4372    #[test]
4373    fn test_doc_comment_on_pub_fn() {
4374        let program = parse("/// Public function\npub fn greet() { print(\"hi\") }").unwrap();
4375        assert_eq!(
4376            program.statements[0].doc_comment.as_deref(),
4377            Some("Public function")
4378        );
4379    }
4380
4381    #[test]
4382    fn test_multiline_doc_comment() {
4383        let source = "/// First line\n/// Second line\n/// Third line\nfn foo() {}";
4384        let program = parse(source).unwrap();
4385        assert_eq!(
4386            program.statements[0].doc_comment.as_deref(),
4387            Some("First line\nSecond line\nThird line")
4388        );
4389    }
4390
4391    #[test]
4392    fn test_no_doc_comment() {
4393        let program = parse("fn foo() {}").unwrap();
4394        assert!(program.statements[0].doc_comment.is_none());
4395    }
4396
4397    #[test]
4398    fn test_inner_doc_comment_module() {
4399        let source = "//! This module does stuff\n//! More info\nfn foo() {}";
4400        let program = parse(source).unwrap();
4401        assert_eq!(
4402            program.module_doc.as_deref(),
4403            Some("This module does stuff\nMore info")
4404        );
4405    }
4406
4407    #[test]
4408    fn test_doc_comment_not_on_expr() {
4409        // Doc comments before expressions still get attached (parser is lenient)
4410        let source = "/// Some doc\n42";
4411        let program = parse(source).unwrap();
4412        // The doc attaches to the expression statement
4413        assert_eq!(
4414            program.statements[0].doc_comment.as_deref(),
4415            Some("Some doc")
4416        );
4417    }
4418
4419    #[test]
4420    fn test_doc_comment_on_let() {
4421        let program = parse("/// The answer\nlet x = 42").unwrap();
4422        assert_eq!(
4423            program.statements[0].doc_comment.as_deref(),
4424            Some("The answer")
4425        );
4426    }
4427
4428    #[test]
4429    fn test_doc_comment_on_schema() {
4430        let program = parse("/// User schema\nschema User { name: string, age: int }").unwrap();
4431        assert_eq!(
4432            program.statements[0].doc_comment.as_deref(),
4433            Some("User schema")
4434        );
4435    }
4436
4437    // ── Phase 21: Schema Evolution & Migration ──────────────────────
4438
4439    #[test]
4440    fn test_parse_versioned_schema() {
4441        let source = "/// User schema\n/// @version 1\nschema User { name: string }";
4442        let program = parse(source).unwrap();
4443        if let StmtKind::Schema { name, version, .. } = &program.statements[0].kind {
4444            assert_eq!(name, "User");
4445            assert_eq!(*version, Some(1));
4446        } else {
4447            panic!("Expected Schema statement");
4448        }
4449    }
4450
4451    #[test]
4452    fn test_parse_schema_field_doc_comments() {
4453        let source = "schema User {\n  /// User's name\n  /// @since 1\n  name: string\n}";
4454        let program = parse(source).unwrap();
4455        if let StmtKind::Schema { fields, .. } = &program.statements[0].kind {
4456            assert_eq!(fields[0].name, "name");
4457            assert!(fields[0].doc_comment.is_some());
4458            assert!(fields[0].doc_comment.as_ref().unwrap().contains("@since"));
4459        } else {
4460            panic!("Expected Schema statement");
4461        }
4462    }
4463
4464    #[test]
4465    fn test_parse_schema_field_default_value() {
4466        let source = "schema User { name: string = \"unknown\", age: int64 }";
4467        let program = parse(source).unwrap();
4468        if let StmtKind::Schema { fields, .. } = &program.statements[0].kind {
4469            assert_eq!(fields.len(), 2);
4470            assert!(fields[0].default_value.is_some());
4471            assert!(fields[1].default_value.is_none());
4472        } else {
4473            panic!("Expected Schema statement");
4474        }
4475    }
4476
4477    #[test]
4478    fn test_parse_migrate_add_column() {
4479        let source = "migrate User from 1 to 2 { add_column(email: string) }";
4480        let program = parse(source).unwrap();
4481        if let StmtKind::Migrate {
4482            schema_name,
4483            from_version,
4484            to_version,
4485            operations,
4486        } = &program.statements[0].kind
4487        {
4488            assert_eq!(schema_name, "User");
4489            assert_eq!(*from_version, 1);
4490            assert_eq!(*to_version, 2);
4491            assert_eq!(operations.len(), 1);
4492            assert!(matches!(&operations[0], MigrateOp::AddColumn { name, .. } if name == "email"));
4493        } else {
4494            panic!("Expected Migrate statement");
4495        }
4496    }
4497
4498    #[test]
4499    fn test_parse_migrate_drop_column() {
4500        let source = "migrate User from 2 to 3 { drop_column(legacy) }";
4501        let program = parse(source).unwrap();
4502        if let StmtKind::Migrate { operations, .. } = &program.statements[0].kind {
4503            assert!(matches!(&operations[0], MigrateOp::DropColumn { name } if name == "legacy"));
4504        } else {
4505            panic!("Expected Migrate statement");
4506        }
4507    }
4508
4509    #[test]
4510    fn test_parse_migrate_rename_column() {
4511        let source = "migrate User from 1 to 2 { rename_column(old_name, new_name) }";
4512        let program = parse(source).unwrap();
4513        if let StmtKind::Migrate { operations, .. } = &program.statements[0].kind {
4514            assert!(
4515                matches!(&operations[0], MigrateOp::RenameColumn { from, to } if from == "old_name" && to == "new_name")
4516            );
4517        } else {
4518            panic!("Expected Migrate statement");
4519        }
4520    }
4521
4522    #[test]
4523    fn test_parse_migrate_alter_type() {
4524        let source = "migrate User from 1 to 2 { alter_type(age, float64) }";
4525        let program = parse(source).unwrap();
4526        if let StmtKind::Migrate { operations, .. } = &program.statements[0].kind {
4527            assert!(
4528                matches!(&operations[0], MigrateOp::AlterType { column, .. } if column == "age")
4529            );
4530        } else {
4531            panic!("Expected Migrate statement");
4532        }
4533    }
4534
4535    #[test]
4536    fn test_parse_migrate_multiple_operations() {
4537        let source = "migrate User from 1 to 2 {\n  add_column(email: string)\n  drop_column(legacy)\n  rename_column(fname, first_name)\n}";
4538        let program = parse(source).unwrap();
4539        if let StmtKind::Migrate { operations, .. } = &program.statements[0].kind {
4540            assert_eq!(operations.len(), 3);
4541            assert!(matches!(&operations[0], MigrateOp::AddColumn { .. }));
4542            assert!(matches!(&operations[1], MigrateOp::DropColumn { .. }));
4543            assert!(matches!(&operations[2], MigrateOp::RenameColumn { .. }));
4544        } else {
4545            panic!("Expected Migrate statement");
4546        }
4547    }
4548
4549    #[test]
4550    fn test_parse_migrate_error_no_versions() {
4551        let result = parse("migrate User { add_column(x: int64) }");
4552        assert!(result.is_err());
4553    }
4554
4555    #[test]
4556    fn test_parse_migrate_error_invalid_op() {
4557        let result = parse("migrate User from 1 to 2 { invalid_op(x) }");
4558        assert!(result.is_err());
4559    }
4560
4561    #[test]
4562    fn test_parse_migrate_add_column_with_default() {
4563        let source = "migrate User from 1 to 2 { add_column(email: string, default: \"\") }";
4564        let program = parse(source).unwrap();
4565        if let StmtKind::Migrate { operations, .. } = &program.statements[0].kind {
4566            if let MigrateOp::AddColumn { name, default, .. } = &operations[0] {
4567                assert_eq!(name, "email");
4568                assert!(default.is_some());
4569            } else {
4570                panic!("Expected AddColumn");
4571            }
4572        } else {
4573            panic!("Expected Migrate statement");
4574        }
4575    }
4576
4577    #[test]
4578    fn test_parse_schema_version_in_doc() {
4579        let source = "/// @version 5\nschema Events { ts: int64 }";
4580        let program = parse(source).unwrap();
4581        if let StmtKind::Schema { version, .. } = &program.statements[0].kind {
4582            assert_eq!(*version, Some(5));
4583        } else {
4584            panic!("Expected Schema statement");
4585        }
4586    }
4587
4588    // ── Phase 22-24 Parser Tests ───────────────────────────────────
4589
4590    #[test]
4591    fn test_parse_decimal_literal() {
4592        let source = "let x = 3.14d";
4593        let program = parse(source).unwrap();
4594        if let StmtKind::Let { value, .. } = &program.statements[0].kind {
4595            match value {
4596                // The lexer strips the trailing 'd' and underscores
4597                Expr::Decimal(s) => assert_eq!(s, "3.14"),
4598                _ => panic!("Expected Expr::Decimal, got {value:?}"),
4599            }
4600        } else {
4601            panic!("Expected Let statement");
4602        }
4603    }
4604
4605    #[test]
4606    fn test_parse_decimal_underscore() {
4607        let source = "let x = 1_000.50d";
4608        let program = parse(source).unwrap();
4609        if let StmtKind::Let { value, .. } = &program.statements[0].kind {
4610            match value {
4611                // Underscores and trailing 'd' are stripped by lexer
4612                Expr::Decimal(s) => assert_eq!(s, "1000.50"),
4613                _ => panic!("Expected Expr::Decimal"),
4614            }
4615        } else {
4616            panic!("Expected Let statement");
4617        }
4618    }
4619
4620    #[test]
4621    fn test_parse_async_fn() {
4622        let source = "async fn fetch() { return 42 }";
4623        let program = parse(source).unwrap();
4624        if let StmtKind::FnDecl { name, is_async, .. } = &program.statements[0].kind {
4625            assert_eq!(name, "fetch");
4626            assert!(*is_async);
4627        } else {
4628            panic!("Expected FnDecl statement");
4629        }
4630    }
4631
4632    #[test]
4633    fn test_parse_async_fn_with_params() {
4634        let source = "async fn get(url, timeout) { return url }";
4635        let program = parse(source).unwrap();
4636        if let StmtKind::FnDecl {
4637            name,
4638            is_async,
4639            params,
4640            ..
4641        } = &program.statements[0].kind
4642        {
4643            assert_eq!(name, "get");
4644            assert!(*is_async);
4645            assert_eq!(params.len(), 2);
4646        } else {
4647            panic!("Expected FnDecl statement");
4648        }
4649    }
4650
4651    #[test]
4652    fn test_parse_sensitive_annotation() {
4653        let source = r#"
4654/// @sensitive
4655schema Secret {
4656    password: string
4657}
4658"#;
4659        let program = parse(source).unwrap();
4660        if let StmtKind::Schema { fields, .. } = &program.statements[0].kind {
4661            assert!(!fields.is_empty());
4662            // The annotation is on the schema-level doc, not on individual fields
4663        } else {
4664            panic!("Expected Schema statement");
4665        }
4666    }
4667
4668    // ── Phase 34: Agent Framework ──
4669
4670    #[test]
4671    fn test_parse_agent_basic() {
4672        let program = parse(
4673            r#"agent bot {
4674                model: "gpt-4o",
4675                system: "You are helpful.",
4676                tools {
4677                    search: {
4678                        description: "Search the web",
4679                        parameters: {}
4680                    }
4681                },
4682                max_turns: 10
4683            }"#,
4684        )
4685        .unwrap();
4686        if let StmtKind::Agent {
4687            name,
4688            model,
4689            system_prompt,
4690            tools,
4691            max_turns,
4692            ..
4693        } = &program.statements[0].kind
4694        {
4695            assert_eq!(name, "bot");
4696            assert_eq!(model, "gpt-4o");
4697            assert_eq!(system_prompt.as_deref(), Some("You are helpful."));
4698            assert_eq!(tools.len(), 1);
4699            assert_eq!(tools[0].0, "search");
4700            assert_eq!(max_turns, &Some(10));
4701        } else {
4702            panic!("Expected Agent statement");
4703        }
4704    }
4705
4706    #[test]
4707    fn test_parse_agent_minimal() {
4708        let program = parse(
4709            r#"agent minimal {
4710                model: "claude-sonnet-4-20250514"
4711            }"#,
4712        )
4713        .unwrap();
4714        if let StmtKind::Agent {
4715            name,
4716            model,
4717            system_prompt,
4718            tools,
4719            max_turns,
4720            ..
4721        } = &program.statements[0].kind
4722        {
4723            assert_eq!(name, "minimal");
4724            assert_eq!(model, "claude-sonnet-4-20250514");
4725            assert!(system_prompt.is_none());
4726            assert!(tools.is_empty());
4727            assert!(max_turns.is_none());
4728        } else {
4729            panic!("Expected Agent statement");
4730        }
4731    }
4732
4733    #[test]
4734    fn test_parse_agent_multiple_tools() {
4735        let program = parse(
4736            r#"agent assistant {
4737                model: "gpt-4o",
4738                tools {
4739                    search: { description: "Search", parameters: {} },
4740                    weather: { description: "Get weather", parameters: {} }
4741                }
4742            }"#,
4743        )
4744        .unwrap();
4745        if let StmtKind::Agent { tools, .. } = &program.statements[0].kind {
4746            assert_eq!(tools.len(), 2);
4747            assert_eq!(tools[0].0, "search");
4748            assert_eq!(tools[1].0, "weather");
4749        } else {
4750            panic!("Expected Agent statement");
4751        }
4752    }
4753
4754    #[test]
4755    fn test_parse_agent_with_base_url() {
4756        let program = parse(
4757            r#"agent local {
4758                model: "llama3",
4759                base_url: "http://localhost:11434/v1",
4760                max_turns: 3,
4761                temperature: 0.7
4762            }"#,
4763        )
4764        .unwrap();
4765        if let StmtKind::Agent {
4766            name,
4767            base_url,
4768            temperature,
4769            max_turns,
4770            ..
4771        } = &program.statements[0].kind
4772        {
4773            assert_eq!(name, "local");
4774            assert_eq!(base_url.as_deref(), Some("http://localhost:11434/v1"));
4775            assert_eq!(temperature, &Some(0.7));
4776            assert_eq!(max_turns, &Some(3));
4777        } else {
4778            panic!("Expected Agent statement");
4779        }
4780    }
4781
4782    #[test]
4783    fn test_parse_agent_lifecycle_hooks() {
4784        let program = parse(
4785            r#"agent bot {
4786                model: "gpt-4o",
4787                tools {
4788                    search: { description: "Search", parameters: {} }
4789                },
4790                on_tool_call {
4791                    println("Tool called: " + tool_name)
4792                }
4793                on_complete {
4794                    println("Done!")
4795                }
4796            }"#,
4797        )
4798        .unwrap();
4799        if let StmtKind::Agent {
4800            name,
4801            on_tool_call,
4802            on_complete,
4803            ..
4804        } = &program.statements[0].kind
4805        {
4806            assert_eq!(name, "bot");
4807            assert!(on_tool_call.is_some());
4808            assert_eq!(on_tool_call.as_ref().unwrap().len(), 1);
4809            assert!(on_complete.is_some());
4810            assert_eq!(on_complete.as_ref().unwrap().len(), 1);
4811        } else {
4812            panic!("Expected Agent statement");
4813        }
4814    }
4815}