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