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