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