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