1use crate::ast::{
6 AgentDecl, BeliefDecl, BinOp, Block, ClosureParam, ConstDecl, ElseBranch, EnumDecl, EventKind,
7 Expr, FieldInit, FnDecl, HandlerDecl, Literal, MapEntry, MatchArm, ModDecl, Param, Pattern,
8 Program, RecordDecl, RecordField, Stmt, StringPart, StringTemplate, ToolDecl, ToolFnDecl,
9 UnaryOp, UseDecl, UseKind,
10};
11use chumsky::prelude::*;
12use chumsky::BoxedParser;
13use sage_lexer::{Spanned, Token};
14use sage_types::{Ident, Span, TypeExpr};
15use std::ops::Range;
16use std::sync::Arc;
17
18pub type ParseError = Simple<Token>;
20
21#[must_use]
27#[allow(clippy::needless_pass_by_value)] pub fn parse(tokens: &[Spanned], source: Arc<str>) -> (Option<Program>, Vec<ParseError>) {
29 let len = source.len();
30
31 let token_spans: Vec<(Token, Range<usize>)> = tokens
33 .iter()
34 .map(|s| (s.token.clone(), s.start..s.end))
35 .collect();
36
37 let stream = chumsky::Stream::from_iter(len..len, token_spans.into_iter());
38
39 let (ast, errors) = program_parser(Arc::clone(&source)).parse_recovery(stream);
40
41 (ast, errors)
42}
43
44#[allow(clippy::needless_pass_by_value)]
50fn program_parser(source: Arc<str>) -> impl Parser<Token, Program, Error = ParseError> {
51 let src = source.clone();
52 let src2 = source.clone();
53
54 let top_level = mod_parser(source.clone())
56 .or(use_parser(source.clone()))
57 .or(record_parser(source.clone()))
58 .or(enum_parser(source.clone()))
59 .or(const_parser(source.clone()))
60 .or(tool_parser(source.clone()))
61 .or(agent_parser(source.clone()))
62 .or(fn_parser(source.clone()))
63 .recover_with(skip_then_retry_until([
64 Token::KwMod,
65 Token::KwUse,
66 Token::KwPub,
67 Token::KwRecord,
68 Token::KwEnum,
69 Token::KwConst,
70 Token::KwTool,
71 Token::KwAgent,
72 Token::KwFn,
73 Token::KwRun,
74 ]));
75
76 let run_stmt = just(Token::KwRun)
77 .ignore_then(ident_token_parser(src.clone()))
78 .then_ignore(just(Token::Semicolon))
79 .or_not();
80
81 top_level.repeated().then(run_stmt).map_with_span(
82 move |(items, run_agent), span: Range<usize>| {
83 let mut mod_decls = Vec::new();
84 let mut use_decls = Vec::new();
85 let mut records = Vec::new();
86 let mut enums = Vec::new();
87 let mut consts = Vec::new();
88 let mut tools = Vec::new();
89 let mut agents = Vec::new();
90 let mut functions = Vec::new();
91
92 for item in items {
93 match item {
94 TopLevel::Mod(m) => mod_decls.push(m),
95 TopLevel::Use(u) => use_decls.push(u),
96 TopLevel::Record(r) => records.push(r),
97 TopLevel::Enum(e) => enums.push(e),
98 TopLevel::Const(c) => consts.push(c),
99 TopLevel::Tool(t) => tools.push(t),
100 TopLevel::Agent(a) => agents.push(a),
101 TopLevel::Function(f) => functions.push(f),
102 }
103 }
104
105 Program {
106 mod_decls,
107 use_decls,
108 records,
109 enums,
110 consts,
111 tools,
112 agents,
113 functions,
114 run_agent,
115 span: make_span(&src2, span),
116 }
117 },
118 )
119}
120
121enum TopLevel {
123 Mod(ModDecl),
124 Use(UseDecl),
125 Record(RecordDecl),
126 Enum(EnumDecl),
127 Const(ConstDecl),
128 Tool(ToolDecl),
129 Agent(AgentDecl),
130 Function(FnDecl),
131}
132
133#[allow(clippy::needless_pass_by_value)]
139fn mod_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
140 let src = source.clone();
141
142 just(Token::KwPub)
143 .or_not()
144 .then_ignore(just(Token::KwMod))
145 .then(ident_token_parser(src.clone()))
146 .then_ignore(just(Token::Semicolon))
147 .map_with_span(move |(is_pub, name), span: Range<usize>| {
148 TopLevel::Mod(ModDecl {
149 is_pub: is_pub.is_some(),
150 name,
151 span: make_span(&src, span),
152 })
153 })
154}
155
156#[allow(clippy::needless_pass_by_value)]
158fn use_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
159 let src = source.clone();
160 let src2 = source.clone();
161 let src3 = source.clone();
162 let src4 = source.clone();
163
164 let simple_use = just(Token::KwPub)
166 .or_not()
167 .then_ignore(just(Token::KwUse))
168 .then(
169 ident_token_parser(src.clone())
170 .separated_by(just(Token::ColonColon))
171 .at_least(1),
172 )
173 .then(
174 just(Token::KwAs)
175 .ignore_then(ident_token_parser(src.clone()))
176 .or_not(),
177 )
178 .then_ignore(just(Token::Semicolon))
179 .map_with_span(move |((is_pub, path), alias), span: Range<usize>| {
180 TopLevel::Use(UseDecl {
181 is_pub: is_pub.is_some(),
182 path,
183 kind: UseKind::Simple(alias),
184 span: make_span(&src, span),
185 })
186 });
187
188 let group_item = ident_token_parser(src2.clone()).then(
190 just(Token::KwAs)
191 .ignore_then(ident_token_parser(src2.clone()))
192 .or_not(),
193 );
194
195 let group_use = just(Token::KwPub)
197 .or_not()
198 .then_ignore(just(Token::KwUse))
199 .then(
200 ident_token_parser(src3.clone())
201 .then_ignore(just(Token::ColonColon))
202 .repeated()
203 .at_least(1),
204 )
205 .then(
206 group_item
207 .separated_by(just(Token::Comma))
208 .allow_trailing()
209 .delimited_by(just(Token::LBrace), just(Token::RBrace)),
210 )
211 .then_ignore(just(Token::Semicolon))
212 .map_with_span(move |((is_pub, path), items), span: Range<usize>| {
213 TopLevel::Use(UseDecl {
214 is_pub: is_pub.is_some(),
215 path,
216 kind: UseKind::Group(items),
217 span: make_span(&src3, span),
218 })
219 });
220
221 let glob_use = just(Token::KwPub)
223 .or_not()
224 .then_ignore(just(Token::KwUse))
225 .then(
226 ident_token_parser(src4.clone())
227 .then_ignore(just(Token::ColonColon))
228 .repeated()
229 .at_least(1),
230 )
231 .then_ignore(just(Token::Star))
232 .then_ignore(just(Token::Semicolon))
233 .map_with_span(move |(is_pub, path), span: Range<usize>| {
234 TopLevel::Use(UseDecl {
235 is_pub: is_pub.is_some(),
236 path,
237 kind: UseKind::Glob,
238 span: make_span(&src4, span),
239 })
240 });
241
242 group_use.or(glob_use).or(simple_use)
244}
245
246#[allow(clippy::needless_pass_by_value)]
252fn record_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
253 let src = source.clone();
254 let src2 = source.clone();
255
256 let field = ident_token_parser(src.clone())
258 .then_ignore(just(Token::Colon))
259 .then(type_parser(src.clone()))
260 .map_with_span(move |(name, ty), span: Range<usize>| RecordField {
261 name,
262 ty,
263 span: make_span(&src, span),
264 });
265
266 just(Token::KwPub)
267 .or_not()
268 .then_ignore(just(Token::KwRecord))
269 .then(ident_token_parser(src2.clone()))
270 .then(
271 field
272 .separated_by(just(Token::Comma))
273 .allow_trailing()
274 .delimited_by(just(Token::LBrace), just(Token::RBrace)),
275 )
276 .map_with_span(move |((is_pub, name), fields), span: Range<usize>| {
277 TopLevel::Record(RecordDecl {
278 is_pub: is_pub.is_some(),
279 name,
280 fields,
281 span: make_span(&src2, span),
282 })
283 })
284}
285
286#[allow(clippy::needless_pass_by_value)]
288fn enum_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
289 let src = source.clone();
290 let src2 = source.clone();
291 let src3 = source.clone();
292
293 let variant = ident_token_parser(src.clone())
295 .then(
296 type_parser(src.clone())
297 .delimited_by(just(Token::LParen), just(Token::RParen))
298 .or_not(),
299 )
300 .map_with_span({
301 let src = src.clone();
302 move |(name, payload), span: Range<usize>| crate::ast::EnumVariant {
303 name,
304 payload,
305 span: make_span(&src, span),
306 }
307 });
308
309 just(Token::KwPub)
310 .or_not()
311 .then_ignore(just(Token::KwEnum))
312 .then(ident_token_parser(src3.clone()))
313 .then(
314 variant
315 .separated_by(just(Token::Comma))
316 .allow_trailing()
317 .delimited_by(just(Token::LBrace), just(Token::RBrace)),
318 )
319 .map_with_span(move |((is_pub, name), variants), span: Range<usize>| {
320 TopLevel::Enum(EnumDecl {
321 is_pub: is_pub.is_some(),
322 name,
323 variants,
324 span: make_span(&src2, span),
325 })
326 })
327}
328
329#[allow(clippy::needless_pass_by_value)]
331fn const_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
332 let src = source.clone();
333 let src2 = source.clone();
334
335 just(Token::KwPub)
336 .or_not()
337 .then_ignore(just(Token::KwConst))
338 .then(ident_token_parser(src.clone()))
339 .then_ignore(just(Token::Colon))
340 .then(type_parser(src.clone()))
341 .then_ignore(just(Token::Eq))
342 .then(expr_parser(src.clone()))
343 .then_ignore(just(Token::Semicolon))
344 .map_with_span(move |(((is_pub, name), ty), value), span: Range<usize>| {
345 TopLevel::Const(ConstDecl {
346 is_pub: is_pub.is_some(),
347 name,
348 ty,
349 value,
350 span: make_span(&src2, span),
351 })
352 })
353}
354
355#[allow(clippy::needless_pass_by_value)]
361fn tool_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
362 let src = source.clone();
363 let src2 = source.clone();
364 let src3 = source.clone();
365
366 let param = ident_token_parser(src.clone())
368 .then_ignore(just(Token::Colon))
369 .then(type_parser(src.clone()))
370 .map_with_span(move |(name, ty), span: Range<usize>| Param {
371 name,
372 ty,
373 span: make_span(&src, span),
374 });
375
376 let params = param
377 .separated_by(just(Token::Comma))
378 .allow_trailing()
379 .delimited_by(just(Token::LParen), just(Token::RParen));
380
381 let tool_fn = just(Token::KwFn)
383 .ignore_then(ident_token_parser(src2.clone()))
384 .then(params)
385 .then_ignore(just(Token::Arrow))
386 .then(type_parser(src2.clone()))
387 .map_with_span(move |((name, params), return_ty), span: Range<usize>| ToolFnDecl {
388 name,
389 params,
390 return_ty,
391 span: make_span(&src2, span),
392 });
393
394 just(Token::KwPub)
395 .or_not()
396 .then_ignore(just(Token::KwTool))
397 .then(ident_token_parser(src3.clone()))
398 .then(
399 tool_fn
400 .repeated()
401 .delimited_by(just(Token::LBrace), just(Token::RBrace)),
402 )
403 .map_with_span(move |((is_pub, name), functions), span: Range<usize>| {
404 TopLevel::Tool(ToolDecl {
405 is_pub: is_pub.is_some(),
406 name,
407 functions,
408 span: make_span(&src3, span),
409 })
410 })
411}
412
413#[allow(clippy::needless_pass_by_value)]
419fn agent_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
420 let src = source.clone();
421 let src2 = source.clone();
422 let src3 = source.clone();
423 let src4 = source.clone();
424 let src5 = source.clone();
425
426 let tool_use = just(Token::KwUse)
428 .ignore_then(
429 ident_token_parser(src5.clone())
430 .separated_by(just(Token::Comma))
431 .at_least(1),
432 )
433 .or_not()
434 .map(|tools| tools.unwrap_or_default());
435
436 let belief = ident_token_parser(src.clone())
439 .then_ignore(just(Token::Colon))
440 .then(type_parser(src.clone()))
441 .map_with_span(move |(name, ty), span: Range<usize>| BeliefDecl {
442 name,
443 ty,
444 span: make_span(&src, span),
445 });
446
447 let handler = just(Token::KwOn)
448 .ignore_then(event_kind_parser(src2.clone()))
449 .then(block_parser(src2.clone()))
450 .map_with_span(move |(event, body), span: Range<usize>| HandlerDecl {
451 event,
452 body,
453 span: make_span(&src2, span),
454 });
455
456 let receives_clause = just(Token::KwReceives)
458 .ignore_then(type_parser(src3.clone()))
459 .or_not();
460
461 just(Token::KwPub)
462 .or_not()
463 .then_ignore(just(Token::KwAgent))
464 .then(ident_token_parser(src3.clone()))
465 .then(receives_clause)
466 .then_ignore(just(Token::LBrace))
467 .then(tool_use)
468 .then(belief.repeated())
469 .then(handler.repeated())
470 .then_ignore(just(Token::RBrace))
471 .map_with_span(
472 move |(((((is_pub, name), receives), tool_uses), beliefs), handlers),
473 span: Range<usize>| {
474 TopLevel::Agent(AgentDecl {
475 is_pub: is_pub.is_some(),
476 name,
477 receives,
478 tool_uses,
479 beliefs,
480 handlers,
481 span: make_span(&src4, span),
482 })
483 },
484 )
485}
486
487#[allow(clippy::needless_pass_by_value)]
489fn event_kind_parser(source: Arc<str>) -> impl Parser<Token, EventKind, Error = ParseError> {
490 let src = source.clone();
491
492 let start = just(Token::KwStart).to(EventKind::Start);
493 let stop = just(Token::KwStop).to(EventKind::Stop);
494
495 let message = just(Token::KwMessage)
496 .ignore_then(just(Token::LParen))
497 .ignore_then(ident_token_parser(src.clone()))
498 .then_ignore(just(Token::Colon))
499 .then(type_parser(src.clone()))
500 .then_ignore(just(Token::RParen))
501 .map(|(param_name, param_ty)| EventKind::Message {
502 param_name,
503 param_ty,
504 });
505
506 let error = just(Token::KwError)
508 .ignore_then(just(Token::LParen))
509 .ignore_then(ident_token_parser(src))
510 .then_ignore(just(Token::RParen))
511 .map(|param_name| EventKind::Error { param_name });
512
513 start.or(stop).or(message).or(error)
514}
515
516#[allow(clippy::needless_pass_by_value)]
522fn fn_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
523 let src = source.clone();
524 let src2 = source.clone();
525 let src3 = source.clone();
526
527 let param = ident_token_parser(src.clone())
528 .then_ignore(just(Token::Colon))
529 .then(type_parser(src.clone()))
530 .map_with_span(move |(name, ty), span: Range<usize>| Param {
531 name,
532 ty,
533 span: make_span(&src, span),
534 });
535
536 let params = param
537 .separated_by(just(Token::Comma))
538 .allow_trailing()
539 .delimited_by(just(Token::LParen), just(Token::RParen));
540
541 just(Token::KwPub)
542 .or_not()
543 .then_ignore(just(Token::KwFn))
544 .then(ident_token_parser(src2.clone()))
545 .then(params)
546 .then_ignore(just(Token::Arrow))
547 .then(type_parser(src2.clone()))
548 .then(just(Token::KwFails).or_not())
549 .then(block_parser(src2))
550 .map_with_span(
551 move |(((((is_pub, name), params), return_ty), is_fallible), body),
552 span: Range<usize>| {
553 TopLevel::Function(FnDecl {
554 is_pub: is_pub.is_some(),
555 name,
556 params,
557 return_ty,
558 is_fallible: is_fallible.is_some(),
559 body,
560 span: make_span(&src3, span),
561 })
562 },
563 )
564}
565
566#[allow(clippy::needless_pass_by_value)]
573fn block_parser(source: Arc<str>) -> BoxedParser<'static, Token, Block, ParseError> {
574 let src = source.clone();
575
576 recursive(move |block: Recursive<Token, Block, ParseError>| {
577 let src_inner = src.clone();
578 stmt_parser(src.clone(), block)
579 .repeated()
580 .delimited_by(just(Token::LBrace), just(Token::RBrace))
581 .recover_with(nested_delimiters(
582 Token::LBrace,
583 Token::RBrace,
584 [
585 (Token::LParen, Token::RParen),
586 (Token::LBracket, Token::RBracket),
587 ],
588 |_span: Range<usize>| vec![],
589 ))
590 .map_with_span(move |stmts, span: Range<usize>| Block {
591 stmts,
592 span: make_span(&src_inner, span),
593 })
594 })
595 .boxed()
596}
597
598#[allow(clippy::needless_pass_by_value)]
600fn stmt_parser(
601 source: Arc<str>,
602 block: impl Parser<Token, Block, Error = ParseError> + Clone + 'static,
603) -> impl Parser<Token, Stmt, Error = ParseError> + Clone {
604 let src = source.clone();
605 let src2 = source.clone();
606 let src3 = source.clone();
607 let src4 = source.clone();
608 let src5 = source.clone();
609 let src6 = source.clone();
610 let src7 = source.clone();
611
612 let src10 = source.clone();
614 let let_tuple_stmt = just(Token::KwLet)
615 .ignore_then(
616 ident_token_parser(src10.clone())
617 .separated_by(just(Token::Comma))
618 .at_least(2)
619 .allow_trailing()
620 .delimited_by(just(Token::LParen), just(Token::RParen)),
621 )
622 .then(
623 just(Token::Colon)
624 .ignore_then(type_parser(src10.clone()))
625 .or_not(),
626 )
627 .then_ignore(just(Token::Eq))
628 .then(expr_parser(src10.clone()))
629 .then_ignore(just(Token::Semicolon))
630 .map_with_span(move |((names, ty), value), span: Range<usize>| Stmt::LetTuple {
631 names,
632 ty,
633 value,
634 span: make_span(&src10, span),
635 });
636
637 let let_stmt = just(Token::KwLet)
638 .ignore_then(ident_token_parser(src.clone()))
639 .then(
640 just(Token::Colon)
641 .ignore_then(type_parser(src.clone()))
642 .or_not(),
643 )
644 .then_ignore(just(Token::Eq))
645 .then(expr_parser(src.clone()))
646 .then_ignore(just(Token::Semicolon))
647 .map_with_span(move |((name, ty), value), span: Range<usize>| Stmt::Let {
648 name,
649 ty,
650 value,
651 span: make_span(&src, span),
652 });
653
654 let return_stmt = just(Token::KwReturn)
655 .ignore_then(expr_parser(src2.clone()).or_not())
656 .then_ignore(just(Token::Semicolon))
657 .map_with_span(move |value, span: Range<usize>| Stmt::Return {
658 value,
659 span: make_span(&src2, span),
660 });
661
662 let if_stmt = recursive(|if_stmt| {
663 let src_if = src3.clone();
664 let block_clone = block.clone();
665
666 just(Token::KwIf)
667 .ignore_then(expr_parser(src3.clone()))
668 .then(block_clone.clone())
669 .then(
670 just(Token::KwElse)
671 .ignore_then(
672 if_stmt
673 .map(|s| ElseBranch::ElseIf(Box::new(s)))
674 .or(block_clone.map(ElseBranch::Block)),
675 )
676 .or_not(),
677 )
678 .map_with_span(
679 move |((condition, then_block), else_block), span: Range<usize>| Stmt::If {
680 condition,
681 then_block,
682 else_block,
683 span: make_span(&src_if, span),
684 },
685 )
686 });
687
688 let for_stmt = just(Token::KwFor)
689 .ignore_then(for_pattern_parser(src4.clone()))
690 .then_ignore(just(Token::KwIn))
691 .then(expr_parser(src4.clone()))
692 .then(block.clone())
693 .map_with_span(move |((pattern, iter), body), span: Range<usize>| Stmt::For {
694 pattern,
695 iter,
696 body,
697 span: make_span(&src4, span),
698 });
699
700 let while_stmt = just(Token::KwWhile)
701 .ignore_then(expr_parser(src7.clone()))
702 .then(block.clone())
703 .map_with_span(move |(condition, body), span: Range<usize>| Stmt::While {
704 condition,
705 body,
706 span: make_span(&src7, span),
707 });
708
709 let src8 = source.clone();
710 let loop_stmt = just(Token::KwLoop)
711 .ignore_then(block.clone())
712 .map_with_span(move |body, span: Range<usize>| Stmt::Loop {
713 body,
714 span: make_span(&src8, span),
715 });
716
717 let src9 = source.clone();
718 let break_stmt = just(Token::KwBreak)
719 .then_ignore(just(Token::Semicolon))
720 .map_with_span(move |_, span: Range<usize>| Stmt::Break {
721 span: make_span(&src9, span),
722 });
723
724 let assign_stmt = ident_token_parser(src5.clone())
725 .then_ignore(just(Token::Eq))
726 .then(expr_parser(src5.clone()))
727 .then_ignore(just(Token::Semicolon))
728 .map_with_span(move |(name, value), span: Range<usize>| Stmt::Assign {
729 name,
730 value,
731 span: make_span(&src5, span),
732 });
733
734 let expr_stmt = expr_parser(src6.clone())
735 .then_ignore(just(Token::Semicolon))
736 .map_with_span(move |expr, span: Range<usize>| Stmt::Expr {
737 expr,
738 span: make_span(&src6, span),
739 });
740
741 let_tuple_stmt
742 .or(let_stmt)
743 .or(return_stmt)
744 .or(if_stmt)
745 .or(for_stmt)
746 .or(while_stmt)
747 .or(loop_stmt)
748 .or(break_stmt)
749 .or(assign_stmt)
750 .or(expr_stmt)
751}
752
753#[allow(clippy::needless_pass_by_value, clippy::too_many_lines)]
760fn expr_parser(source: Arc<str>) -> BoxedParser<'static, Token, Expr, ParseError> {
761 recursive(move |expr: Recursive<Token, Expr, ParseError>| {
762 let src = source.clone();
763
764 let literal = literal_parser(src.clone());
765 let var = var_parser(src.clone());
766
767 let paren_or_tuple = just(Token::LParen)
770 .ignore_then(
771 expr.clone()
772 .separated_by(just(Token::Comma))
773 .allow_trailing(),
774 )
775 .then_ignore(just(Token::RParen))
776 .map_with_span({
777 let src = src.clone();
778 move |elements, span: Range<usize>| {
779 if elements.len() == 1 {
780 Expr::Paren {
782 inner: Box::new(elements.into_iter().next().unwrap()),
783 span: make_span(&src, span),
784 }
785 } else {
786 Expr::Tuple {
788 elements,
789 span: make_span(&src, span),
790 }
791 }
792 }
793 });
794
795 let list = expr
796 .clone()
797 .separated_by(just(Token::Comma))
798 .allow_trailing()
799 .delimited_by(just(Token::LBracket), just(Token::RBracket))
800 .map_with_span({
801 let src = src.clone();
802 move |elements, span: Range<usize>| Expr::List {
803 elements,
804 span: make_span(&src, span),
805 }
806 });
807
808 let self_access = just(Token::KwSelf)
810 .ignore_then(just(Token::Dot))
811 .ignore_then(ident_token_parser(src.clone()))
812 .then(
813 expr.clone()
814 .separated_by(just(Token::Comma))
815 .allow_trailing()
816 .delimited_by(just(Token::LParen), just(Token::RParen))
817 .or_not(),
818 )
819 .map_with_span({
820 let src = src.clone();
821 move |(field, args), span: Range<usize>| match args {
822 Some(args) => Expr::SelfMethodCall {
823 method: field,
824 args,
825 span: make_span(&src, span),
826 },
827 None => Expr::SelfField {
828 field,
829 span: make_span(&src, span),
830 },
831 }
832 });
833
834 let infer_expr = just(Token::KwInfer)
836 .ignore_then(just(Token::LParen))
837 .ignore_then(string_template_parser(src.clone()))
838 .then(
839 just(Token::Arrow)
840 .ignore_then(type_parser(src.clone()))
841 .or_not(),
842 )
843 .then_ignore(just(Token::RParen))
844 .map_with_span({
845 let src = src.clone();
846 move |(template, result_ty), span: Range<usize>| Expr::Infer {
847 template,
848 result_ty,
849 span: make_span(&src, span),
850 }
851 });
852
853 let spawn_field_init = ident_token_parser(src.clone())
855 .then_ignore(just(Token::Colon))
856 .then(expr.clone())
857 .map_with_span({
858 let src = src.clone();
859 move |(name, value), span: Range<usize>| FieldInit {
860 name,
861 value,
862 span: make_span(&src, span),
863 }
864 });
865
866 let spawn_expr = just(Token::KwSpawn)
867 .ignore_then(ident_token_parser(src.clone()))
868 .then_ignore(just(Token::LBrace))
869 .then(
870 spawn_field_init
871 .separated_by(just(Token::Comma))
872 .allow_trailing(),
873 )
874 .then_ignore(just(Token::RBrace))
875 .map_with_span({
876 let src = src.clone();
877 move |(agent, fields), span: Range<usize>| Expr::Spawn {
878 agent,
879 fields,
880 span: make_span(&src, span),
881 }
882 });
883
884 let await_expr = just(Token::KwAwait)
886 .ignore_then(ident_token_parser(src.clone()).map_with_span({
887 let src = src.clone();
888 move |name, span: Range<usize>| Expr::Var {
889 name,
890 span: make_span(&src, span),
891 }
892 }))
893 .map_with_span({
894 let src = src.clone();
895 move |handle, span: Range<usize>| Expr::Await {
896 handle: Box::new(handle),
897 span: make_span(&src, span),
898 }
899 });
900
901 let send_expr = just(Token::KwSend)
903 .ignore_then(just(Token::LParen))
904 .ignore_then(expr.clone())
905 .then_ignore(just(Token::Comma))
906 .then(expr.clone())
907 .then_ignore(just(Token::RParen))
908 .map_with_span({
909 let src = src.clone();
910 move |(handle, message), span: Range<usize>| Expr::Send {
911 handle: Box::new(handle),
912 message: Box::new(message),
913 span: make_span(&src, span),
914 }
915 });
916
917 let emit_expr = just(Token::KwEmit)
919 .ignore_then(just(Token::LParen))
920 .ignore_then(expr.clone())
921 .then_ignore(just(Token::RParen))
922 .map_with_span({
923 let src = src.clone();
924 move |value, span: Range<usize>| Expr::Emit {
925 value: Box::new(value),
926 span: make_span(&src, span),
927 }
928 });
929
930 let call_expr = ident_token_parser(src.clone())
932 .then(
933 expr.clone()
934 .separated_by(just(Token::Comma))
935 .allow_trailing()
936 .delimited_by(just(Token::LParen), just(Token::RParen)),
937 )
938 .map_with_span({
939 let src = src.clone();
940 move |(name, args), span: Range<usize>| Expr::Call {
941 name,
942 args,
943 span: make_span(&src, span),
944 }
945 });
946
947 let pattern = pattern_parser(src.clone());
949
950 let match_arm = pattern
952 .then_ignore(just(Token::FatArrow))
953 .then(expr.clone())
954 .map_with_span({
955 let src = src.clone();
956 move |(pattern, body), span: Range<usize>| MatchArm {
957 pattern,
958 body,
959 span: make_span(&src, span),
960 }
961 });
962
963 let match_expr = just(Token::KwMatch)
964 .ignore_then(expr.clone())
965 .then(
966 match_arm
967 .separated_by(just(Token::Comma))
968 .allow_trailing()
969 .delimited_by(just(Token::LBrace), just(Token::RBrace)),
970 )
971 .map_with_span({
972 let src = src.clone();
973 move |(scrutinee, arms), span: Range<usize>| Expr::Match {
974 scrutinee: Box::new(scrutinee),
975 arms,
976 span: make_span(&src, span),
977 }
978 });
979
980 let receive_expr = just(Token::KwReceive)
982 .ignore_then(just(Token::LParen))
983 .ignore_then(just(Token::RParen))
984 .map_with_span({
985 let src = src.clone();
986 move |_, span: Range<usize>| Expr::Receive {
987 span: make_span(&src, span),
988 }
989 });
990
991 let record_field_init = ident_token_parser(src.clone())
995 .then_ignore(just(Token::Colon))
996 .then(expr.clone())
997 .map_with_span({
998 let src = src.clone();
999 move |(name, value), span: Range<usize>| FieldInit {
1000 name,
1001 value,
1002 span: make_span(&src, span),
1003 }
1004 });
1005
1006 let record_construct = ident_token_parser(src.clone())
1007 .then_ignore(just(Token::LBrace))
1008 .then(
1009 record_field_init
1010 .separated_by(just(Token::Comma))
1011 .allow_trailing(),
1012 )
1013 .then_ignore(just(Token::RBrace))
1014 .map_with_span({
1015 let src = src.clone();
1016 move |(name, fields), span: Range<usize>| Expr::RecordConstruct {
1017 name,
1018 fields,
1019 span: make_span(&src, span),
1020 }
1021 });
1022
1023 let closure_param = ident_token_parser(src.clone())
1025 .then(just(Token::Colon).ignore_then(type_parser(src.clone())).or_not())
1026 .map_with_span({
1027 let src = src.clone();
1028 move |(name, ty), span: Range<usize>| ClosureParam {
1029 name,
1030 ty,
1031 span: make_span(&src, span),
1032 }
1033 });
1034
1035 let closure_empty = just(Token::Or)
1038 .ignore_then(expr.clone())
1039 .map_with_span({
1040 let src = src.clone();
1041 move |body, span: Range<usize>| Expr::Closure {
1042 params: vec![],
1043 body: Box::new(body),
1044 span: make_span(&src, span),
1045 }
1046 });
1047
1048 let closure_with_params = just(Token::Pipe)
1049 .ignore_then(
1050 closure_param
1051 .separated_by(just(Token::Comma))
1052 .allow_trailing(),
1053 )
1054 .then_ignore(just(Token::Pipe))
1055 .then(expr.clone())
1056 .map_with_span({
1057 let src = src.clone();
1058 move |(params, body), span: Range<usize>| Expr::Closure {
1059 params,
1060 body: Box::new(body),
1061 span: make_span(&src, span),
1062 }
1063 });
1064
1065 let closure = closure_with_params.or(closure_empty);
1066
1067 let map_entry = expr
1070 .clone()
1071 .then_ignore(just(Token::Colon))
1072 .then(expr.clone())
1073 .map_with_span({
1074 let src = src.clone();
1075 move |(key, value), span: Range<usize>| MapEntry {
1076 key,
1077 value,
1078 span: make_span(&src, span),
1079 }
1080 });
1081
1082 let map_literal = map_entry
1083 .separated_by(just(Token::Comma))
1084 .allow_trailing()
1085 .delimited_by(just(Token::LBrace), just(Token::RBrace))
1086 .map_with_span({
1087 let src = src.clone();
1088 move |entries, span: Range<usize>| Expr::Map {
1089 entries,
1090 span: make_span(&src, span),
1091 }
1092 });
1093
1094 let variant_construct = ident_token_parser(src.clone())
1096 .then_ignore(just(Token::ColonColon))
1097 .then(ident_token_parser(src.clone()))
1098 .then(
1099 expr.clone()
1100 .delimited_by(just(Token::LParen), just(Token::RParen))
1101 .or_not(),
1102 )
1103 .map_with_span({
1104 let src = src.clone();
1105 move |((enum_name, variant), payload), span: Range<usize>| Expr::VariantConstruct {
1106 enum_name,
1107 variant,
1108 payload: payload.map(Box::new),
1109 span: make_span(&src, span),
1110 }
1111 });
1112
1113 let atom = closure
1121 .or(infer_expr)
1122 .or(spawn_expr)
1123 .or(await_expr)
1124 .or(send_expr)
1125 .or(emit_expr)
1126 .or(receive_expr)
1127 .or(match_expr)
1128 .or(self_access)
1129 .or(record_construct)
1130 .or(variant_construct)
1131 .or(call_expr)
1132 .or(map_literal)
1133 .or(list)
1134 .or(paren_or_tuple)
1135 .or(literal)
1136 .or(var)
1137 .boxed();
1138
1139 enum PostfixOp {
1142 Field(Ident),
1143 TupleIndex(usize, Range<usize>),
1144 MethodCall(Ident, Vec<Expr>, Range<usize>), }
1146
1147 let method_call = ident_token_parser(src.clone())
1149 .then(
1150 expr.clone()
1151 .separated_by(just(Token::Comma))
1152 .allow_trailing()
1153 .delimited_by(just(Token::LParen), just(Token::RParen)),
1154 )
1155 .map_with_span(|(name, args), span: Range<usize>| {
1156 PostfixOp::MethodCall(name, args, span)
1157 });
1158
1159 let postfix_op = just(Token::Dot).ignore_then(
1160 filter_map({
1162 let src = src.clone();
1163 move |span: Range<usize>, token| match token {
1164 Token::IntLit => {
1165 let text = &src[span.start..span.end];
1166 text.parse::<usize>()
1167 .map(|idx| PostfixOp::TupleIndex(idx, span.clone()))
1168 .map_err(|_| Simple::custom(span, "invalid tuple index"))
1169 }
1170 _ => Err(Simple::expected_input_found(
1171 span,
1172 vec![Some(Token::IntLit)],
1173 Some(token),
1174 )),
1175 }
1176 })
1177 .or(method_call)
1179 .or(ident_token_parser(src.clone()).map(PostfixOp::Field)),
1180 );
1181
1182 let postfix = atom
1183 .then(postfix_op.repeated())
1184 .foldl({
1185 let src = src.clone();
1186 move |object, op| match op {
1187 PostfixOp::Field(field) => {
1188 let span = make_span(&src, object.span().start..field.span.end);
1189 Expr::FieldAccess {
1190 object: Box::new(object),
1191 field,
1192 span,
1193 }
1194 }
1195 PostfixOp::TupleIndex(index, idx_span) => {
1196 let span = make_span(&src, object.span().start..idx_span.end);
1197 Expr::TupleIndex {
1198 tuple: Box::new(object),
1199 index,
1200 span,
1201 }
1202 }
1203 PostfixOp::MethodCall(method, args, call_span) => {
1204 if let Expr::Var { name: tool, .. } = &object {
1207 let span = make_span(&src, object.span().start..call_span.end);
1208 Expr::ToolCall {
1209 tool: tool.clone(),
1210 function: method,
1211 args,
1212 span,
1213 }
1214 } else {
1215 let span = make_span(&src, object.span().start..call_span.end);
1218 Expr::FieldAccess {
1219 object: Box::new(object),
1220 field: method,
1221 span,
1222 }
1223 }
1224 }
1225 }
1226 })
1227 .boxed();
1228
1229 let unary = just(Token::Minus)
1231 .to(UnaryOp::Neg)
1232 .or(just(Token::Bang).to(UnaryOp::Not))
1233 .repeated()
1234 .then(postfix.clone())
1235 .foldr(|op, operand| {
1236 let span = operand.span().clone();
1237 Expr::Unary {
1238 op,
1239 operand: Box::new(operand),
1240 span,
1241 }
1242 })
1243 .boxed();
1244
1245 let try_expr = just(Token::KwTry)
1248 .ignore_then(postfix)
1249 .map_with_span({
1250 let src = src.clone();
1251 move |inner, span: Range<usize>| Expr::Try {
1252 expr: Box::new(inner),
1253 span: make_span(&src, span),
1254 }
1255 })
1256 .boxed();
1257
1258 let unary = try_expr.or(unary).boxed();
1260
1261 let mul_div_op = just(Token::Star)
1264 .to(BinOp::Mul)
1265 .or(just(Token::Slash).to(BinOp::Div));
1266
1267 let mul_div = unary
1268 .clone()
1269 .then(mul_div_op.then(unary.clone()).repeated())
1270 .foldl({
1271 let src = src.clone();
1272 move |left, (op, right)| {
1273 let span = make_span(&src, left.span().start..right.span().end);
1274 Expr::Binary {
1275 op,
1276 left: Box::new(left),
1277 right: Box::new(right),
1278 span,
1279 }
1280 }
1281 })
1282 .boxed();
1283
1284 let add_sub_op = just(Token::Plus)
1286 .to(BinOp::Add)
1287 .or(just(Token::Minus).to(BinOp::Sub));
1288
1289 let add_sub = mul_div
1290 .clone()
1291 .then(add_sub_op.then(mul_div).repeated())
1292 .foldl({
1293 let src = src.clone();
1294 move |left, (op, right)| {
1295 let span = make_span(&src, left.span().start..right.span().end);
1296 Expr::Binary {
1297 op,
1298 left: Box::new(left),
1299 right: Box::new(right),
1300 span,
1301 }
1302 }
1303 })
1304 .boxed();
1305
1306 let concat_op = just(Token::PlusPlus).to(BinOp::Concat);
1308
1309 let concat = add_sub
1310 .clone()
1311 .then(concat_op.then(add_sub).repeated())
1312 .foldl({
1313 let src = src.clone();
1314 move |left, (op, right)| {
1315 let span = make_span(&src, left.span().start..right.span().end);
1316 Expr::Binary {
1317 op,
1318 left: Box::new(left),
1319 right: Box::new(right),
1320 span,
1321 }
1322 }
1323 })
1324 .boxed();
1325
1326 let cmp_op = choice((
1328 just(Token::Le).to(BinOp::Le),
1329 just(Token::Ge).to(BinOp::Ge),
1330 just(Token::Lt).to(BinOp::Lt),
1331 just(Token::Gt).to(BinOp::Gt),
1332 ));
1333
1334 let comparison = concat
1335 .clone()
1336 .then(cmp_op.then(concat).repeated())
1337 .foldl({
1338 let src = src.clone();
1339 move |left, (op, right)| {
1340 let span = make_span(&src, left.span().start..right.span().end);
1341 Expr::Binary {
1342 op,
1343 left: Box::new(left),
1344 right: Box::new(right),
1345 span,
1346 }
1347 }
1348 })
1349 .boxed();
1350
1351 let eq_op = just(Token::EqEq)
1353 .to(BinOp::Eq)
1354 .or(just(Token::Ne).to(BinOp::Ne));
1355
1356 let equality = comparison
1357 .clone()
1358 .then(eq_op.then(comparison).repeated())
1359 .foldl({
1360 let src = src.clone();
1361 move |left, (op, right)| {
1362 let span = make_span(&src, left.span().start..right.span().end);
1363 Expr::Binary {
1364 op,
1365 left: Box::new(left),
1366 right: Box::new(right),
1367 span,
1368 }
1369 }
1370 })
1371 .boxed();
1372
1373 let and_op = just(Token::And).to(BinOp::And);
1375
1376 let and = equality
1377 .clone()
1378 .then(and_op.then(equality).repeated())
1379 .foldl({
1380 let src = src.clone();
1381 move |left, (op, right)| {
1382 let span = make_span(&src, left.span().start..right.span().end);
1383 Expr::Binary {
1384 op,
1385 left: Box::new(left),
1386 right: Box::new(right),
1387 span,
1388 }
1389 }
1390 })
1391 .boxed();
1392
1393 let or_op = just(Token::Or).to(BinOp::Or);
1395
1396 let or_expr = and.clone().then(or_op.then(and).repeated()).foldl({
1397 let src = src.clone();
1398 move |left, (op, right)| {
1399 let span = make_span(&src, left.span().start..right.span().end);
1400 Expr::Binary {
1401 op,
1402 left: Box::new(left),
1403 right: Box::new(right),
1404 span,
1405 }
1406 }
1407 });
1408
1409 let catch_recovery = just(Token::KwCatch)
1412 .ignore_then(
1413 ident_token_parser(src.clone())
1414 .delimited_by(just(Token::LParen), just(Token::RParen))
1415 .or_not(),
1416 )
1417 .then(
1418 expr.clone()
1419 .delimited_by(just(Token::LBrace), just(Token::RBrace)),
1420 );
1421
1422 or_expr.then(catch_recovery.or_not()).map_with_span({
1423 let src = src.clone();
1424 move |(inner, catch_opt), span: Range<usize>| match catch_opt {
1425 Some((error_bind, recovery)) => Expr::Catch {
1426 expr: Box::new(inner),
1427 error_bind,
1428 recovery: Box::new(recovery),
1429 span: make_span(&src, span),
1430 },
1431 None => inner,
1432 }
1433 })
1434 })
1435 .boxed()
1436}
1437
1438fn make_span(source: &Arc<str>, range: Range<usize>) -> Span {
1444 Span::new(range.start, range.end, Arc::clone(source))
1445}
1446
1447fn ident_token_parser(source: Arc<str>) -> impl Parser<Token, Ident, Error = ParseError> + Clone {
1449 filter_map(move |span: Range<usize>, token| match token {
1450 Token::Ident => {
1451 let text = &source[span.start..span.end];
1452 Ok(Ident::new(text.to_string(), make_span(&source, span)))
1453 }
1454 _ => Err(Simple::expected_input_found(
1455 span,
1456 vec![Some(Token::Ident)],
1457 Some(token),
1458 )),
1459 })
1460}
1461
1462fn var_parser(source: Arc<str>) -> impl Parser<Token, Expr, Error = ParseError> + Clone {
1464 ident_token_parser(source.clone()).map_with_span(move |name, span: Range<usize>| Expr::Var {
1465 name,
1466 span: make_span(&source, span),
1467 })
1468}
1469
1470fn type_parser(source: Arc<str>) -> impl Parser<Token, TypeExpr, Error = ParseError> + Clone {
1472 recursive(move |ty| {
1473 let src = source.clone();
1474
1475 let primitive = choice((
1476 just(Token::TyInt).to(TypeExpr::Int),
1477 just(Token::TyFloat).to(TypeExpr::Float),
1478 just(Token::TyBool).to(TypeExpr::Bool),
1479 just(Token::TyString).to(TypeExpr::String),
1480 just(Token::TyUnit).to(TypeExpr::Unit),
1481 ));
1482
1483 let list_ty = just(Token::TyList)
1484 .ignore_then(just(Token::Lt))
1485 .ignore_then(ty.clone())
1486 .then_ignore(just(Token::Gt))
1487 .map(|inner| TypeExpr::List(Box::new(inner)));
1488
1489 let option_ty = just(Token::TyOption)
1490 .ignore_then(just(Token::Lt))
1491 .ignore_then(ty.clone())
1492 .then_ignore(just(Token::Gt))
1493 .map(|inner| TypeExpr::Option(Box::new(inner)));
1494
1495 let inferred_ty = just(Token::TyInferred)
1496 .ignore_then(just(Token::Lt))
1497 .ignore_then(ty.clone())
1498 .then_ignore(just(Token::Gt))
1499 .map(|inner| TypeExpr::Inferred(Box::new(inner)));
1500
1501 let agent_ty = just(Token::TyAgent)
1502 .ignore_then(just(Token::Lt))
1503 .ignore_then(ident_token_parser(src.clone()))
1504 .then_ignore(just(Token::Gt))
1505 .map(TypeExpr::Agent);
1506
1507 let named_ty = ident_token_parser(src.clone()).map(TypeExpr::Named);
1508
1509 let fn_ty = just(Token::TyFn)
1511 .ignore_then(
1512 ty.clone()
1513 .separated_by(just(Token::Comma))
1514 .allow_trailing()
1515 .delimited_by(just(Token::LParen), just(Token::RParen)),
1516 )
1517 .then_ignore(just(Token::Arrow))
1518 .then(ty.clone())
1519 .map(|(params, ret)| TypeExpr::Fn(params, Box::new(ret)));
1520
1521 let map_ty = just(Token::TyMap)
1523 .ignore_then(just(Token::Lt))
1524 .ignore_then(ty.clone())
1525 .then_ignore(just(Token::Comma))
1526 .then(ty.clone())
1527 .then_ignore(just(Token::Gt))
1528 .map(|(k, v)| TypeExpr::Map(Box::new(k), Box::new(v)));
1529
1530 let result_ty = just(Token::TyResult)
1532 .ignore_then(just(Token::Lt))
1533 .ignore_then(ty.clone())
1534 .then_ignore(just(Token::Comma))
1535 .then(ty.clone())
1536 .then_ignore(just(Token::Gt))
1537 .map(|(ok, err)| TypeExpr::Result(Box::new(ok), Box::new(err)));
1538
1539 let tuple_ty = ty
1541 .clone()
1542 .separated_by(just(Token::Comma))
1543 .at_least(2)
1544 .allow_trailing()
1545 .delimited_by(just(Token::LParen), just(Token::RParen))
1546 .map(TypeExpr::Tuple);
1547
1548 primitive
1549 .or(list_ty)
1550 .or(option_ty)
1551 .or(inferred_ty)
1552 .or(agent_ty)
1553 .or(fn_ty)
1554 .or(map_ty)
1555 .or(result_ty)
1556 .or(tuple_ty)
1557 .or(named_ty)
1558 })
1559}
1560
1561fn for_pattern_parser(source: Arc<str>) -> impl Parser<Token, Pattern, Error = ParseError> + Clone {
1564 recursive(move |pattern| {
1565 let src = source.clone();
1566 let src2 = source.clone();
1567
1568 let binding = ident_token_parser(src.clone()).map_with_span({
1570 let src = src.clone();
1571 move |name, span: Range<usize>| Pattern::Binding {
1572 name,
1573 span: make_span(&src, span),
1574 }
1575 });
1576
1577 let tuple_pattern = pattern
1579 .clone()
1580 .separated_by(just(Token::Comma))
1581 .at_least(2)
1582 .allow_trailing()
1583 .delimited_by(just(Token::LParen), just(Token::RParen))
1584 .map_with_span({
1585 let src = src2.clone();
1586 move |elements, span: Range<usize>| Pattern::Tuple {
1587 elements,
1588 span: make_span(&src, span),
1589 }
1590 });
1591
1592 tuple_pattern.or(binding)
1593 })
1594}
1595
1596fn pattern_parser(source: Arc<str>) -> impl Parser<Token, Pattern, Error = ParseError> + Clone {
1598 recursive(move |pattern| {
1599 let src = source.clone();
1600 let src2 = source.clone();
1601 let src3 = source.clone();
1602 let src4 = source.clone();
1603 let src5 = source.clone();
1604
1605 let wildcard = filter_map({
1607 let src = src.clone();
1608 move |span: Range<usize>, token| match &token {
1609 Token::Ident if src[span.start..span.end].eq("_") => Ok(()),
1610 _ => Err(Simple::expected_input_found(span, vec![], Some(token))),
1611 }
1612 })
1613 .map_with_span(move |_, span: Range<usize>| Pattern::Wildcard {
1614 span: make_span(&src2, span),
1615 });
1616
1617 let lit_int = filter_map({
1619 let src = src3.clone();
1620 move |span: Range<usize>, token| match token {
1621 Token::IntLit => {
1622 let text = &src[span.start..span.end];
1623 text.parse::<i64>()
1624 .map(Literal::Int)
1625 .map_err(|_| Simple::custom(span, "invalid integer literal"))
1626 }
1627 _ => Err(Simple::expected_input_found(
1628 span,
1629 vec![Some(Token::IntLit)],
1630 Some(token),
1631 )),
1632 }
1633 })
1634 .map_with_span({
1635 let src = src3.clone();
1636 move |value, span: Range<usize>| Pattern::Literal {
1637 value,
1638 span: make_span(&src, span),
1639 }
1640 });
1641
1642 let lit_bool = just(Token::KwTrue)
1643 .to(Literal::Bool(true))
1644 .or(just(Token::KwFalse).to(Literal::Bool(false)))
1645 .map_with_span({
1646 let src = src3.clone();
1647 move |value, span: Range<usize>| Pattern::Literal {
1648 value,
1649 span: make_span(&src, span),
1650 }
1651 });
1652
1653 let tuple_pattern = pattern
1655 .clone()
1656 .separated_by(just(Token::Comma))
1657 .at_least(2)
1658 .allow_trailing()
1659 .delimited_by(just(Token::LParen), just(Token::RParen))
1660 .map_with_span({
1661 let src = src5.clone();
1662 move |elements, span: Range<usize>| Pattern::Tuple {
1663 elements,
1664 span: make_span(&src, span),
1665 }
1666 });
1667
1668 let qualified_variant_with_payload = ident_token_parser(src4.clone())
1671 .then_ignore(just(Token::ColonColon))
1672 .then(ident_token_parser(src4.clone()))
1673 .then(
1674 pattern
1675 .clone()
1676 .delimited_by(just(Token::LParen), just(Token::RParen))
1677 .or_not(),
1678 )
1679 .map_with_span({
1680 let src = src4.clone();
1681 move |((enum_name, variant), payload), span: Range<usize>| Pattern::Variant {
1682 enum_name: Some(enum_name),
1683 variant,
1684 payload: payload.map(Box::new),
1685 span: make_span(&src, span),
1686 }
1687 });
1688
1689 let unqualified_with_payload = ident_token_parser(src4.clone())
1691 .then(
1692 pattern
1693 .clone()
1694 .delimited_by(just(Token::LParen), just(Token::RParen))
1695 .or_not(),
1696 )
1697 .map_with_span({
1698 let src = src4.clone();
1699 move |(name, payload), span: Range<usize>| {
1700 if name.name.chars().next().is_some_and(|c| c.is_uppercase()) || payload.is_some() {
1703 Pattern::Variant {
1704 enum_name: None,
1705 variant: name,
1706 payload: payload.map(Box::new),
1707 span: make_span(&src, span),
1708 }
1709 } else {
1710 Pattern::Binding {
1711 name,
1712 span: make_span(&src, span),
1713 }
1714 }
1715 }
1716 });
1717
1718 wildcard
1720 .or(tuple_pattern)
1721 .or(qualified_variant_with_payload)
1722 .or(lit_int)
1723 .or(lit_bool)
1724 .or(unqualified_with_payload)
1725 })
1726}
1727
1728fn literal_parser(source: Arc<str>) -> impl Parser<Token, Expr, Error = ParseError> + Clone {
1730 let src = source.clone();
1731 let src2 = source.clone();
1732 let src3 = source.clone();
1733 let src4 = source.clone();
1734 let src5 = source.clone();
1735
1736 let int_lit = filter_map(move |span: Range<usize>, token| match token {
1737 Token::IntLit => {
1738 let text = &src[span.start..span.end];
1739 text.parse::<i64>()
1740 .map(Literal::Int)
1741 .map_err(|_| Simple::custom(span, "invalid integer literal"))
1742 }
1743 _ => Err(Simple::expected_input_found(
1744 span,
1745 vec![Some(Token::IntLit)],
1746 Some(token),
1747 )),
1748 })
1749 .map_with_span(move |value, span: Range<usize>| Expr::Literal {
1750 value,
1751 span: make_span(&src2, span),
1752 });
1753
1754 let float_lit = filter_map(move |span: Range<usize>, token| match token {
1755 Token::FloatLit => {
1756 let text = &src3[span.start..span.end];
1757 text.parse::<f64>()
1758 .map(Literal::Float)
1759 .map_err(|_| Simple::custom(span, "invalid float literal"))
1760 }
1761 _ => Err(Simple::expected_input_found(
1762 span,
1763 vec![Some(Token::FloatLit)],
1764 Some(token),
1765 )),
1766 })
1767 .map_with_span(move |value, span: Range<usize>| Expr::Literal {
1768 value,
1769 span: make_span(&src4, span),
1770 });
1771
1772 let src6 = source.clone();
1773 let string_lit = filter_map(move |span: Range<usize>, token| match token {
1774 Token::StringLit => {
1775 let text = &src5[span.start..span.end];
1776 let inner = &text[1..text.len() - 1];
1777 let parts = parse_string_template(inner, &make_span(&src5, span.clone()));
1778 Ok(parts)
1779 }
1780 _ => Err(Simple::expected_input_found(
1781 span,
1782 vec![Some(Token::StringLit)],
1783 Some(token),
1784 )),
1785 })
1786 .map_with_span(move |parts, span: Range<usize>| {
1787 let span = make_span(&src6, span);
1788 if parts.len() == 1 {
1790 if let StringPart::Literal(s) = &parts[0] {
1791 return Expr::Literal {
1792 value: Literal::String(s.clone()),
1793 span,
1794 };
1795 }
1796 }
1797 Expr::StringInterp {
1799 template: StringTemplate {
1800 parts,
1801 span: span.clone(),
1802 },
1803 span,
1804 }
1805 });
1806
1807 let bool_lit = just(Token::KwTrue)
1808 .to(Literal::Bool(true))
1809 .or(just(Token::KwFalse).to(Literal::Bool(false)))
1810 .map_with_span(move |value, _span: Range<usize>| Expr::Literal {
1811 value,
1812 span: Span::dummy(), });
1814
1815 int_lit.or(float_lit).or(string_lit).or(bool_lit)
1816}
1817
1818fn string_template_parser(
1820 source: Arc<str>,
1821) -> impl Parser<Token, StringTemplate, Error = ParseError> + Clone {
1822 filter_map(move |span: Range<usize>, token| match token {
1823 Token::StringLit => {
1824 let text = &source[span.start..span.end];
1825 let inner = &text[1..text.len() - 1];
1826 let parts = parse_string_template(inner, &make_span(&source, span.clone()));
1827 Ok(StringTemplate {
1828 parts,
1829 span: make_span(&source, span),
1830 })
1831 }
1832 _ => Err(Simple::expected_input_found(
1833 span,
1834 vec![Some(Token::StringLit)],
1835 Some(token),
1836 )),
1837 })
1838}
1839
1840fn parse_string_template(s: &str, span: &Span) -> Vec<StringPart> {
1842 let mut parts = Vec::new();
1843 let mut current = String::new();
1844 let mut chars = s.chars().peekable();
1845
1846 while let Some(ch) = chars.next() {
1847 if ch == '{' {
1848 if !current.is_empty() {
1849 parts.push(StringPart::Literal(std::mem::take(&mut current)));
1850 }
1851
1852 let mut ident_name = String::new();
1853 while let Some(&c) = chars.peek() {
1854 if c == '}' {
1855 chars.next();
1856 break;
1857 }
1858 ident_name.push(c);
1859 chars.next();
1860 }
1861
1862 if !ident_name.is_empty() {
1863 parts.push(StringPart::Interpolation(Ident::new(
1864 ident_name,
1865 span.clone(),
1866 )));
1867 }
1868 } else if ch == '\\' {
1869 if let Some(escaped) = chars.next() {
1870 current.push(match escaped {
1871 'n' => '\n',
1872 't' => '\t',
1873 'r' => '\r',
1874 '\\' => '\\',
1875 '"' => '"',
1876 '{' => '{',
1877 '}' => '}',
1878 other => other,
1879 });
1880 }
1881 } else {
1882 current.push(ch);
1883 }
1884 }
1885
1886 if !current.is_empty() {
1887 parts.push(StringPart::Literal(current));
1888 }
1889
1890 if parts.is_empty() {
1891 parts.push(StringPart::Literal(String::new()));
1892 }
1893
1894 parts
1895}
1896
1897#[cfg(test)]
1902mod tests {
1903 use super::*;
1904 use sage_lexer::lex;
1905
1906 fn parse_str(source: &str) -> (Option<Program>, Vec<ParseError>) {
1907 let lex_result = lex(source).expect("lexing should succeed");
1908 let source_arc: Arc<str> = Arc::from(source);
1909 parse(lex_result.tokens(), source_arc)
1910 }
1911
1912 #[test]
1913 fn parse_minimal_program() {
1914 let source = r#"
1915 agent Main {
1916 on start {
1917 emit(42);
1918 }
1919 }
1920 run Main;
1921 "#;
1922
1923 let (prog, errors) = parse_str(source);
1924 assert!(errors.is_empty(), "errors: {errors:?}");
1925 let prog = prog.expect("should parse");
1926
1927 assert_eq!(prog.agents.len(), 1);
1928 assert_eq!(prog.agents[0].name.name, "Main");
1929 assert_eq!(prog.run_agent.as_ref().unwrap().name, "Main");
1930 }
1931
1932 #[test]
1933 fn parse_agent_with_beliefs() {
1934 let source = r#"
1935 agent Researcher {
1936 topic: String
1937 max_words: Int
1938
1939 on start {
1940 emit(self.topic);
1941 }
1942 }
1943 run Researcher;
1944 "#;
1945
1946 let (prog, errors) = parse_str(source);
1947 assert!(errors.is_empty(), "errors: {errors:?}");
1948 let prog = prog.expect("should parse");
1949
1950 assert_eq!(prog.agents[0].beliefs.len(), 2);
1951 assert_eq!(prog.agents[0].beliefs[0].name.name, "topic");
1952 assert_eq!(prog.agents[0].beliefs[1].name.name, "max_words");
1953 }
1954
1955 #[test]
1956 fn parse_multiple_handlers() {
1957 let source = r#"
1958 agent Worker {
1959 on start {
1960 print("started");
1961 }
1962
1963 on message(msg: String) {
1964 print(msg);
1965 }
1966
1967 on stop {
1968 print("stopped");
1969 }
1970 }
1971 run Worker;
1972 "#;
1973
1974 let (prog, errors) = parse_str(source);
1975 assert!(errors.is_empty(), "errors: {errors:?}");
1976 let prog = prog.expect("should parse");
1977
1978 assert_eq!(prog.agents[0].handlers.len(), 3);
1979 assert_eq!(prog.agents[0].handlers[0].event, EventKind::Start);
1980 assert!(matches!(
1981 prog.agents[0].handlers[1].event,
1982 EventKind::Message { .. }
1983 ));
1984 assert_eq!(prog.agents[0].handlers[2].event, EventKind::Stop);
1985 }
1986
1987 #[test]
1988 fn parse_function() {
1989 let source = r#"
1990 fn greet(name: String) -> String {
1991 return "Hello, " ++ name;
1992 }
1993
1994 agent Main {
1995 on start {
1996 emit(greet("World"));
1997 }
1998 }
1999 run Main;
2000 "#;
2001
2002 let (prog, errors) = parse_str(source);
2003 assert!(errors.is_empty(), "errors: {errors:?}");
2004 let prog = prog.expect("should parse");
2005
2006 assert_eq!(prog.functions.len(), 1);
2007 assert_eq!(prog.functions[0].name.name, "greet");
2008 assert_eq!(prog.functions[0].params.len(), 1);
2009 }
2010
2011 #[test]
2012 fn parse_let_statement() {
2013 let source = r#"
2014 agent Main {
2015 on start {
2016 let x: Int = 42;
2017 let y = "hello";
2018 emit(x);
2019 }
2020 }
2021 run Main;
2022 "#;
2023
2024 let (prog, errors) = parse_str(source);
2025 assert!(errors.is_empty(), "errors: {errors:?}");
2026 let prog = prog.expect("should parse");
2027
2028 let stmts = &prog.agents[0].handlers[0].body.stmts;
2029 assert!(matches!(stmts[0], Stmt::Let { .. }));
2030 assert!(matches!(stmts[1], Stmt::Let { .. }));
2031 }
2032
2033 #[test]
2034 fn parse_if_statement() {
2035 let source = r#"
2036 agent Main {
2037 on start {
2038 if true {
2039 emit(1);
2040 } else {
2041 emit(2);
2042 }
2043 }
2044 }
2045 run Main;
2046 "#;
2047
2048 let (prog, errors) = parse_str(source);
2049 assert!(errors.is_empty(), "errors: {errors:?}");
2050 let prog = prog.expect("should parse");
2051
2052 let stmts = &prog.agents[0].handlers[0].body.stmts;
2053 assert!(matches!(stmts[0], Stmt::If { .. }));
2054 }
2055
2056 #[test]
2057 fn parse_for_loop() {
2058 let source = r#"
2059 agent Main {
2060 on start {
2061 for x in [1, 2, 3] {
2062 print(x);
2063 }
2064 emit(0);
2065 }
2066 }
2067 run Main;
2068 "#;
2069
2070 let (prog, errors) = parse_str(source);
2071 assert!(errors.is_empty(), "errors: {errors:?}");
2072 let prog = prog.expect("should parse");
2073
2074 let stmts = &prog.agents[0].handlers[0].body.stmts;
2075 assert!(matches!(stmts[0], Stmt::For { .. }));
2076 }
2077
2078 #[test]
2079 fn parse_spawn_await() {
2080 let source = r#"
2081 agent Worker {
2082 name: String
2083
2084 on start {
2085 emit(self.name);
2086 }
2087 }
2088
2089 agent Main {
2090 on start {
2091 let w = spawn Worker { name: "test" };
2092 let result = await w;
2093 emit(result);
2094 }
2095 }
2096 run Main;
2097 "#;
2098
2099 let (prog, errors) = parse_str(source);
2100 assert!(errors.is_empty(), "errors: {errors:?}");
2101 prog.expect("should parse");
2102 }
2103
2104 #[test]
2105 fn parse_infer() {
2106 let source = r#"
2107 agent Main {
2108 on start {
2109 let result = infer("What is 2+2?");
2110 emit(result);
2111 }
2112 }
2113 run Main;
2114 "#;
2115
2116 let (prog, errors) = parse_str(source);
2117 assert!(errors.is_empty(), "errors: {errors:?}");
2118 prog.expect("should parse");
2119 }
2120
2121 #[test]
2122 fn parse_binary_precedence() {
2123 let source = r#"
2124 agent Main {
2125 on start {
2126 let x = 2 + 3 * 4;
2127 emit(x);
2128 }
2129 }
2130 run Main;
2131 "#;
2132
2133 let (prog, errors) = parse_str(source);
2134 assert!(errors.is_empty(), "errors: {errors:?}");
2135 let prog = prog.expect("should parse");
2136
2137 let stmts = &prog.agents[0].handlers[0].body.stmts;
2138 if let Stmt::Let { value, .. } = &stmts[0] {
2139 if let Expr::Binary { op, .. } = value {
2140 assert_eq!(*op, BinOp::Add);
2141 } else {
2142 panic!("expected binary expression");
2143 }
2144 }
2145 }
2146
2147 #[test]
2148 fn parse_string_interpolation() {
2149 let source = r#"
2150 agent Main {
2151 on start {
2152 let name = "World";
2153 let msg = infer("Greet {name}");
2154 emit(msg);
2155 }
2156 }
2157 run Main;
2158 "#;
2159
2160 let (prog, errors) = parse_str(source);
2161 assert!(errors.is_empty(), "errors: {errors:?}");
2162 let prog = prog.expect("should parse");
2163
2164 let stmts = &prog.agents[0].handlers[0].body.stmts;
2165 if let Stmt::Let { value, .. } = &stmts[1] {
2166 if let Expr::Infer { template, .. } = value {
2167 assert!(template.has_interpolations());
2168 } else {
2169 panic!("expected infer expression");
2170 }
2171 }
2172 }
2173
2174 #[test]
2179 fn recover_from_malformed_agent_continues_to_next() {
2180 let source = r#"
2182 agent Broken {
2183 x:
2184 }
2185
2186 agent Main {
2187 on start {
2188 emit(42);
2189 }
2190 }
2191 run Main;
2192 "#;
2193
2194 let (prog, errors) = parse_str(source);
2195 assert!(!errors.is_empty(), "should have parse errors");
2197 let prog = prog.expect("should produce partial AST");
2199 assert!(prog.agents.iter().any(|a| a.name.name == "Main"));
2200 }
2201
2202 #[test]
2203 fn recover_from_mismatched_braces_in_block() {
2204 let source = r#"
2205 agent Main {
2206 on start {
2207 let x = [1, 2, 3;
2208 emit(42);
2209 }
2210 }
2211 run Main;
2212 "#;
2213
2214 let (prog, errors) = parse_str(source);
2215 assert!(!errors.is_empty(), "should have parse errors");
2217 assert!(prog.is_some(), "should produce partial AST despite errors");
2218 }
2219
2220 #[test]
2221 fn parse_mod_declaration() {
2222 let source = r#"
2223 mod agents;
2224 pub mod utils;
2225
2226 agent Main {
2227 on start {
2228 emit(42);
2229 }
2230 }
2231 run Main;
2232 "#;
2233
2234 let (prog, errors) = parse_str(source);
2235 assert!(errors.is_empty(), "errors: {errors:?}");
2236 let prog = prog.expect("should parse");
2237
2238 assert_eq!(prog.mod_decls.len(), 2);
2239 assert!(!prog.mod_decls[0].is_pub);
2240 assert_eq!(prog.mod_decls[0].name.name, "agents");
2241 assert!(prog.mod_decls[1].is_pub);
2242 assert_eq!(prog.mod_decls[1].name.name, "utils");
2243 }
2244
2245 #[test]
2246 fn parse_use_simple() {
2247 let source = r#"
2248 use agents::Researcher;
2249
2250 agent Main {
2251 on start {
2252 emit(42);
2253 }
2254 }
2255 run Main;
2256 "#;
2257
2258 let (prog, errors) = parse_str(source);
2259 assert!(errors.is_empty(), "errors: {errors:?}");
2260 let prog = prog.expect("should parse");
2261
2262 assert_eq!(prog.use_decls.len(), 1);
2263 assert!(!prog.use_decls[0].is_pub);
2264 assert_eq!(prog.use_decls[0].path.len(), 2);
2265 assert_eq!(prog.use_decls[0].path[0].name, "agents");
2266 assert_eq!(prog.use_decls[0].path[1].name, "Researcher");
2267 assert!(matches!(prog.use_decls[0].kind, UseKind::Simple(None)));
2268 }
2269
2270 #[test]
2271 fn parse_use_with_alias() {
2272 let source = r#"
2273 use agents::Researcher as R;
2274
2275 agent Main {
2276 on start {
2277 emit(42);
2278 }
2279 }
2280 run Main;
2281 "#;
2282
2283 let (prog, errors) = parse_str(source);
2284 assert!(errors.is_empty(), "errors: {errors:?}");
2285 let prog = prog.expect("should parse");
2286
2287 assert_eq!(prog.use_decls.len(), 1);
2288 if let UseKind::Simple(Some(alias)) = &prog.use_decls[0].kind {
2289 assert_eq!(alias.name, "R");
2290 } else {
2291 panic!("expected Simple with alias");
2292 }
2293 }
2294
2295 #[test]
2296 fn parse_pub_agent() {
2297 let source = r#"
2298 pub agent Worker {
2299 on start {
2300 emit(42);
2301 }
2302 }
2303
2304 agent Main {
2305 on start {
2306 emit(0);
2307 }
2308 }
2309 run Main;
2310 "#;
2311
2312 let (prog, errors) = parse_str(source);
2313 assert!(errors.is_empty(), "errors: {errors:?}");
2314 let prog = prog.expect("should parse");
2315
2316 assert_eq!(prog.agents.len(), 2);
2317 assert!(prog.agents[0].is_pub);
2318 assert_eq!(prog.agents[0].name.name, "Worker");
2319 assert!(!prog.agents[1].is_pub);
2320 }
2321
2322 #[test]
2323 fn parse_pub_function() {
2324 let source = r#"
2325 pub fn helper(x: Int) -> Int {
2326 return x;
2327 }
2328
2329 agent Main {
2330 on start {
2331 emit(helper(42));
2332 }
2333 }
2334 run Main;
2335 "#;
2336
2337 let (prog, errors) = parse_str(source);
2338 assert!(errors.is_empty(), "errors: {errors:?}");
2339 let prog = prog.expect("should parse");
2340
2341 assert_eq!(prog.functions.len(), 1);
2342 assert!(prog.functions[0].is_pub);
2343 assert_eq!(prog.functions[0].name.name, "helper");
2344 }
2345
2346 #[test]
2347 fn parse_library_no_run() {
2348 let source = r#"
2350 pub agent Worker {
2351 on start {
2352 emit(42);
2353 }
2354 }
2355
2356 pub fn helper(x: Int) -> Int {
2357 return x;
2358 }
2359 "#;
2360
2361 let (prog, errors) = parse_str(source);
2362 assert!(errors.is_empty(), "errors: {errors:?}");
2363 let prog = prog.expect("should parse");
2364
2365 assert!(prog.run_agent.is_none());
2366 assert_eq!(prog.agents.len(), 1);
2367 assert_eq!(prog.functions.len(), 1);
2368 }
2369
2370 #[test]
2371 fn recover_multiple_errors_reported() {
2372 let source = r#"
2374 agent A {
2375 x:
2376 }
2377
2378 agent Main {
2379 on start {
2380 emit(42);
2381 }
2382 }
2383 run Main;
2384 "#;
2385
2386 let (prog, errors) = parse_str(source);
2387 if errors.is_empty() {
2391 let prog = prog.expect("should have AST with recovery");
2393 assert!(prog.agents.iter().any(|a| a.name.name == "Main"));
2394 }
2395 }
2397
2398 #[test]
2399 fn parse_record_declaration() {
2400 let source = r#"
2401 record Point {
2402 x: Int,
2403 y: Int,
2404 }
2405
2406 agent Main {
2407 on start {
2408 emit(0);
2409 }
2410 }
2411 run Main;
2412 "#;
2413
2414 let (prog, errors) = parse_str(source);
2415 assert!(errors.is_empty(), "errors: {errors:?}");
2416 let prog = prog.expect("should parse");
2417
2418 assert_eq!(prog.records.len(), 1);
2419 assert!(!prog.records[0].is_pub);
2420 assert_eq!(prog.records[0].name.name, "Point");
2421 assert_eq!(prog.records[0].fields.len(), 2);
2422 assert_eq!(prog.records[0].fields[0].name.name, "x");
2423 assert_eq!(prog.records[0].fields[1].name.name, "y");
2424 }
2425
2426 #[test]
2427 fn parse_pub_record() {
2428 let source = r#"
2429 pub record Config {
2430 host: String,
2431 port: Int,
2432 }
2433
2434 agent Main {
2435 on start { emit(0); }
2436 }
2437 run Main;
2438 "#;
2439
2440 let (prog, errors) = parse_str(source);
2441 assert!(errors.is_empty(), "errors: {errors:?}");
2442 let prog = prog.expect("should parse");
2443
2444 assert_eq!(prog.records.len(), 1);
2445 assert!(prog.records[0].is_pub);
2446 assert_eq!(prog.records[0].name.name, "Config");
2447 }
2448
2449 #[test]
2450 fn parse_enum_declaration() {
2451 let source = r#"
2452 enum Status {
2453 Active,
2454 Pending,
2455 Done,
2456 }
2457
2458 agent Main {
2459 on start {
2460 emit(0);
2461 }
2462 }
2463 run Main;
2464 "#;
2465
2466 let (prog, errors) = parse_str(source);
2467 assert!(errors.is_empty(), "errors: {errors:?}");
2468 let prog = prog.expect("should parse");
2469
2470 assert_eq!(prog.enums.len(), 1);
2471 assert!(!prog.enums[0].is_pub);
2472 assert_eq!(prog.enums[0].name.name, "Status");
2473 assert_eq!(prog.enums[0].variants.len(), 3);
2474 assert_eq!(prog.enums[0].variants[0].name.name, "Active");
2475 assert_eq!(prog.enums[0].variants[1].name.name, "Pending");
2476 assert_eq!(prog.enums[0].variants[2].name.name, "Done");
2477 }
2478
2479 #[test]
2480 fn parse_pub_enum() {
2481 let source = r#"
2482 pub enum Priority { High, Medium, Low }
2483
2484 agent Main {
2485 on start { emit(0); }
2486 }
2487 run Main;
2488 "#;
2489
2490 let (prog, errors) = parse_str(source);
2491 assert!(errors.is_empty(), "errors: {errors:?}");
2492 let prog = prog.expect("should parse");
2493
2494 assert_eq!(prog.enums.len(), 1);
2495 assert!(prog.enums[0].is_pub);
2496 assert_eq!(prog.enums[0].name.name, "Priority");
2497 }
2498
2499 #[test]
2500 fn parse_const_declaration() {
2501 let source = r#"
2502 const MAX_RETRIES: Int = 3;
2503
2504 agent Main {
2505 on start {
2506 emit(0);
2507 }
2508 }
2509 run Main;
2510 "#;
2511
2512 let (prog, errors) = parse_str(source);
2513 assert!(errors.is_empty(), "errors: {errors:?}");
2514 let prog = prog.expect("should parse");
2515
2516 assert_eq!(prog.consts.len(), 1);
2517 assert!(!prog.consts[0].is_pub);
2518 assert_eq!(prog.consts[0].name.name, "MAX_RETRIES");
2519 assert!(matches!(prog.consts[0].ty, sage_types::TypeExpr::Int));
2520 }
2521
2522 #[test]
2523 fn parse_pub_const() {
2524 let source = r#"
2525 pub const API_URL: String = "https://api.example.com";
2526
2527 agent Main {
2528 on start { emit(0); }
2529 }
2530 run Main;
2531 "#;
2532
2533 let (prog, errors) = parse_str(source);
2534 assert!(errors.is_empty(), "errors: {errors:?}");
2535 let prog = prog.expect("should parse");
2536
2537 assert_eq!(prog.consts.len(), 1);
2538 assert!(prog.consts[0].is_pub);
2539 assert_eq!(prog.consts[0].name.name, "API_URL");
2540 }
2541
2542 #[test]
2543 fn parse_multiple_type_declarations() {
2544 let source = r#"
2545 record Point { x: Int, y: Int }
2546 enum Color { Red, Green, Blue }
2547 const ORIGIN_X: Int = 0;
2548
2549 agent Main {
2550 on start { emit(0); }
2551 }
2552 run Main;
2553 "#;
2554
2555 let (prog, errors) = parse_str(source);
2556 assert!(errors.is_empty(), "errors: {errors:?}");
2557 let prog = prog.expect("should parse");
2558
2559 assert_eq!(prog.records.len(), 1);
2560 assert_eq!(prog.enums.len(), 1);
2561 assert_eq!(prog.consts.len(), 1);
2562 }
2563
2564 #[test]
2565 fn parse_match_expression() {
2566 let source = r#"
2567 enum Status { Active, Pending, Done }
2568
2569 agent Main {
2570 on start {
2571 let s: Int = match Active {
2572 Active => 1,
2573 Pending => 2,
2574 Done => 3,
2575 };
2576 emit(s);
2577 }
2578 }
2579 run Main;
2580 "#;
2581
2582 let (prog, errors) = parse_str(source);
2583 assert!(errors.is_empty(), "errors: {errors:?}");
2584 let prog = prog.expect("should parse");
2585
2586 assert_eq!(prog.agents.len(), 1);
2588 let handler = &prog.agents[0].handlers[0];
2590 let stmt = &handler.body.stmts[0];
2591 if let Stmt::Let { value, .. } = stmt {
2592 assert!(matches!(value, Expr::Match { .. }));
2593 } else {
2594 panic!("expected let statement with match");
2595 }
2596 }
2597
2598 #[test]
2599 fn parse_match_with_wildcard() {
2600 let source = r#"
2601 agent Main {
2602 on start {
2603 let x = 5;
2604 let result = match x {
2605 1 => 10,
2606 2 => 20,
2607 _ => 0,
2608 };
2609 emit(result);
2610 }
2611 }
2612 run Main;
2613 "#;
2614
2615 let (prog, errors) = parse_str(source);
2616 assert!(errors.is_empty(), "errors: {errors:?}");
2617 let prog = prog.expect("should parse");
2618
2619 assert_eq!(prog.agents.len(), 1);
2620 }
2621
2622 #[test]
2623 fn parse_record_construction() {
2624 let source = r#"
2625 record Point { x: Int, y: Int }
2626
2627 agent Main {
2628 on start {
2629 let p = Point { x: 10, y: 20 };
2630 emit(0);
2631 }
2632 }
2633 run Main;
2634 "#;
2635
2636 let (prog, errors) = parse_str(source);
2637 assert!(errors.is_empty(), "errors: {errors:?}");
2638 let prog = prog.expect("should parse");
2639
2640 assert_eq!(prog.records.len(), 1);
2641 assert_eq!(prog.agents.len(), 1);
2642
2643 let handler = &prog.agents[0].handlers[0];
2645 let stmt = &handler.body.stmts[0];
2646 if let Stmt::Let { value, .. } = stmt {
2647 if let Expr::RecordConstruct { name, fields, .. } = value {
2648 assert_eq!(name.name, "Point");
2649 assert_eq!(fields.len(), 2);
2650 assert_eq!(fields[0].name.name, "x");
2651 assert_eq!(fields[1].name.name, "y");
2652 } else {
2653 panic!("expected RecordConstruct");
2654 }
2655 } else {
2656 panic!("expected let statement");
2657 }
2658 }
2659
2660 #[test]
2661 fn parse_match_with_qualified_variant() {
2662 let source = r#"
2663 enum Status { Active, Pending }
2664
2665 fn get_status() -> Int {
2666 return 1;
2667 }
2668
2669 agent Main {
2670 on start {
2671 let s = get_status();
2672 let result = match s {
2673 Status::Active => 1,
2674 Status::Pending => 0,
2675 };
2676 emit(result);
2677 }
2678 }
2679 run Main;
2680 "#;
2681
2682 let (prog, errors) = parse_str(source);
2683 assert!(errors.is_empty(), "errors: {errors:?}");
2684 let prog = prog.expect("should parse");
2685
2686 assert_eq!(prog.enums.len(), 1);
2687 assert_eq!(prog.agents.len(), 1);
2688 }
2689
2690 #[test]
2691 fn parse_field_access() {
2692 let source = r#"
2693 record Point { x: Int, y: Int }
2694
2695 agent Main {
2696 on start {
2697 let p = Point { x: 10, y: 20 };
2698 let x_val = p.x;
2699 let y_val = p.y;
2700 emit(x_val);
2701 }
2702 }
2703 run Main;
2704 "#;
2705
2706 let (prog, errors) = parse_str(source);
2707 assert!(errors.is_empty(), "errors: {errors:?}");
2708 let prog = prog.expect("should parse");
2709
2710 assert_eq!(prog.records.len(), 1);
2711 assert_eq!(prog.agents.len(), 1);
2712
2713 let handler = &prog.agents[0].handlers[0];
2715 let stmt = &handler.body.stmts[1]; if let Stmt::Let { value, .. } = stmt {
2717 if let Expr::FieldAccess { field, .. } = value {
2718 assert_eq!(field.name, "x");
2719 } else {
2720 panic!("expected FieldAccess");
2721 }
2722 } else {
2723 panic!("expected let statement");
2724 }
2725 }
2726
2727 #[test]
2728 fn parse_chained_field_access() {
2729 let source = r#"
2730 record Inner { val: Int }
2731 record Outer { inner: Inner }
2732
2733 agent Main {
2734 on start {
2735 let inner = Inner { val: 42 };
2736 let outer = Outer { inner: inner };
2737 let v = outer.inner.val;
2738 emit(v);
2739 }
2740 }
2741 run Main;
2742 "#;
2743
2744 let (prog, errors) = parse_str(source);
2745 assert!(errors.is_empty(), "errors: {errors:?}");
2746 let prog = prog.expect("should parse");
2747
2748 assert_eq!(prog.records.len(), 2);
2749 assert_eq!(prog.agents.len(), 1);
2750
2751 let handler = &prog.agents[0].handlers[0];
2753 let stmt = &handler.body.stmts[2]; if let Stmt::Let { value, .. } = stmt {
2755 if let Expr::FieldAccess {
2756 object, field: val, ..
2757 } = value
2758 {
2759 assert_eq!(val.name, "val");
2760 if let Expr::FieldAccess { field: inner, .. } = object.as_ref() {
2762 assert_eq!(inner.name, "inner");
2763 } else {
2764 panic!("expected nested FieldAccess");
2765 }
2766 } else {
2767 panic!("expected FieldAccess");
2768 }
2769 } else {
2770 panic!("expected let statement");
2771 }
2772 }
2773
2774 #[test]
2779 fn parse_loop_break() {
2780 let source = r#"
2781 agent Main {
2782 on start {
2783 let count = 0;
2784 loop {
2785 count = count + 1;
2786 if count > 5 {
2787 break;
2788 }
2789 }
2790 emit(count);
2791 }
2792 }
2793 run Main;
2794 "#;
2795
2796 let (prog, errors) = parse_str(source);
2797 assert!(errors.is_empty(), "errors: {errors:?}");
2798 let prog = prog.expect("should parse");
2799
2800 assert_eq!(prog.agents.len(), 1);
2801 let handler = &prog.agents[0].handlers[0];
2802 let loop_stmt = &handler.body.stmts[1];
2804 assert!(matches!(loop_stmt, Stmt::Loop { .. }));
2805 if let Stmt::Loop { body, .. } = loop_stmt {
2807 let if_stmt = &body.stmts[1];
2808 if let Stmt::If { then_block, .. } = if_stmt {
2809 assert!(matches!(then_block.stmts[0], Stmt::Break { .. }));
2810 } else {
2811 panic!("expected if statement");
2812 }
2813 }
2814 }
2815
2816 #[test]
2817 fn parse_agent_receives() {
2818 let source = r#"
2819 enum WorkerMsg {
2820 Task,
2821 Shutdown,
2822 }
2823
2824 agent Worker receives WorkerMsg {
2825 id: Int
2826
2827 on start {
2828 emit(0);
2829 }
2830 }
2831
2832 agent Main {
2833 on start {
2834 emit(0);
2835 }
2836 }
2837 run Main;
2838 "#;
2839
2840 let (prog, errors) = parse_str(source);
2841 assert!(errors.is_empty(), "errors: {errors:?}");
2842 let prog = prog.expect("should parse");
2843
2844 assert_eq!(prog.agents.len(), 2);
2845
2846 let worker = &prog.agents[0];
2848 assert_eq!(worker.name.name, "Worker");
2849 assert!(worker.receives.is_some());
2850 if let Some(TypeExpr::Named(name)) = &worker.receives {
2851 assert_eq!(name.name, "WorkerMsg");
2852 } else {
2853 panic!("expected named type for receives");
2854 }
2855
2856 let main = &prog.agents[1];
2858 assert_eq!(main.name.name, "Main");
2859 assert!(main.receives.is_none());
2860 }
2861
2862 #[test]
2863 fn parse_receive_expression() {
2864 let source = r#"
2865 enum Msg { Ping }
2866
2867 agent Worker receives Msg {
2868 on start {
2869 let msg = receive();
2870 emit(0);
2871 }
2872 }
2873
2874 agent Main {
2875 on start { emit(0); }
2876 }
2877 run Main;
2878 "#;
2879
2880 let (prog, errors) = parse_str(source);
2881 assert!(errors.is_empty(), "errors: {errors:?}");
2882 let prog = prog.expect("should parse");
2883
2884 let worker = prog
2886 .agents
2887 .iter()
2888 .find(|a| a.name.name == "Worker")
2889 .unwrap();
2890 let handler = &worker.handlers[0];
2891 let stmt = &handler.body.stmts[0];
2892
2893 if let Stmt::Let { value, .. } = stmt {
2894 assert!(matches!(value, Expr::Receive { .. }));
2895 } else {
2896 panic!("expected let with receive");
2897 }
2898 }
2899
2900 #[test]
2901 fn parse_message_passing_full() {
2902 let source = r#"
2903 enum WorkerMsg {
2904 Task,
2905 Shutdown,
2906 }
2907
2908 agent Worker receives WorkerMsg {
2909 id: Int
2910
2911 on start {
2912 let msg = receive();
2913 let result = match msg {
2914 Task => 1,
2915 Shutdown => 0,
2916 };
2917 emit(result);
2918 }
2919 }
2920
2921 agent Main {
2922 on start {
2923 let w = spawn Worker { id: 1 };
2924 send(w, Task);
2925 send(w, Shutdown);
2926 await w;
2927 emit(0);
2928 }
2929 }
2930 run Main;
2931 "#;
2932
2933 let (prog, errors) = parse_str(source);
2934 assert!(errors.is_empty(), "errors: {errors:?}");
2935 let prog = prog.expect("should parse");
2936
2937 assert_eq!(prog.enums.len(), 1);
2938 assert_eq!(prog.agents.len(), 2);
2939
2940 let worker = prog
2942 .agents
2943 .iter()
2944 .find(|a| a.name.name == "Worker")
2945 .unwrap();
2946 assert!(worker.receives.is_some());
2947 }
2948
2949 #[test]
2954 fn parse_fallible_function() {
2955 let source = r#"
2956 fn get_data(url: String) -> String fails {
2957 return infer("Get data from {url}" -> String);
2958 }
2959
2960 agent Main {
2961 on start { emit(0); }
2962 }
2963 run Main;
2964 "#;
2965
2966 let (prog, errors) = parse_str(source);
2967 assert!(errors.is_empty(), "errors: {errors:?}");
2968 let prog = prog.expect("should parse");
2969
2970 assert_eq!(prog.functions.len(), 1);
2971 assert!(prog.functions[0].is_fallible);
2972 }
2973
2974 #[test]
2975 fn parse_try_expression() {
2976 let source = r#"
2977 fn fallible() -> Int fails { return 42; }
2978
2979 agent Main {
2980 on start {
2981 let x = try fallible();
2982 emit(x);
2983 }
2984 }
2985 run Main;
2986 "#;
2987
2988 let (prog, errors) = parse_str(source);
2989 assert!(errors.is_empty(), "errors: {errors:?}");
2990 let prog = prog.expect("should parse");
2991
2992 let handler = &prog.agents[0].handlers[0];
2994 if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
2995 assert!(matches!(value, Expr::Try { .. }));
2996 } else {
2997 panic!("expected Let statement");
2998 }
2999 }
3000
3001 #[test]
3002 fn parse_catch_expression() {
3003 let source = r#"
3004 fn fallible() -> Int fails { return 42; }
3005
3006 agent Main {
3007 on start {
3008 let x = fallible() catch { 0 };
3009 emit(x);
3010 }
3011 }
3012 run Main;
3013 "#;
3014
3015 let (prog, errors) = parse_str(source);
3016 assert!(errors.is_empty(), "errors: {errors:?}");
3017 let prog = prog.expect("should parse");
3018
3019 let handler = &prog.agents[0].handlers[0];
3021 if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
3022 if let Expr::Catch { error_bind, .. } = value {
3023 assert!(error_bind.is_none());
3024 } else {
3025 panic!("expected Catch expression");
3026 }
3027 } else {
3028 panic!("expected Let statement");
3029 }
3030 }
3031
3032 #[test]
3033 fn parse_catch_with_error_binding() {
3034 let source = r#"
3035 fn fallible() -> Int fails { return 42; }
3036
3037 agent Main {
3038 on start {
3039 let x = fallible() catch(e) { 0 };
3040 emit(x);
3041 }
3042 }
3043 run Main;
3044 "#;
3045
3046 let (prog, errors) = parse_str(source);
3047 assert!(errors.is_empty(), "errors: {errors:?}");
3048 let prog = prog.expect("should parse");
3049
3050 let handler = &prog.agents[0].handlers[0];
3052 if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
3053 if let Expr::Catch { error_bind, .. } = value {
3054 assert!(error_bind.is_some());
3055 assert_eq!(error_bind.as_ref().unwrap().name, "e");
3056 } else {
3057 panic!("expected Catch expression");
3058 }
3059 } else {
3060 panic!("expected Let statement");
3061 }
3062 }
3063
3064 #[test]
3065 fn parse_on_error_handler() {
3066 let source = r#"
3067 agent Main {
3068 on start {
3069 emit(0);
3070 }
3071
3072 on error(e) {
3073 emit(1);
3074 }
3075 }
3076 run Main;
3077 "#;
3078
3079 let (prog, errors) = parse_str(source);
3080 assert!(errors.is_empty(), "errors: {errors:?}");
3081 let prog = prog.expect("should parse");
3082
3083 assert_eq!(prog.agents.len(), 1);
3084 assert_eq!(prog.agents[0].handlers.len(), 2);
3085
3086 let error_handler = prog.agents[0]
3088 .handlers
3089 .iter()
3090 .find(|h| matches!(h.event, EventKind::Error { .. }));
3091 assert!(error_handler.is_some());
3092
3093 if let EventKind::Error { param_name } = &error_handler.unwrap().event {
3094 assert_eq!(param_name.name, "e");
3095 } else {
3096 panic!("expected Error event kind");
3097 }
3098 }
3099
3100 #[test]
3105 fn parse_fn_type() {
3106 let source = r#"
3107 fn apply(f: Fn(Int) -> Int, x: Int) -> Int {
3108 return f(x);
3109 }
3110
3111 agent Main {
3112 on start {
3113 emit(0);
3114 }
3115 }
3116 run Main;
3117 "#;
3118
3119 let (prog, errors) = parse_str(source);
3120 assert!(errors.is_empty(), "errors: {errors:?}");
3121 let prog = prog.expect("should parse");
3122
3123 assert_eq!(prog.functions.len(), 1);
3124 let func = &prog.functions[0];
3125 assert_eq!(func.name.name, "apply");
3126 assert_eq!(func.params.len(), 2);
3127
3128 if let TypeExpr::Fn(params, ret) = &func.params[0].ty {
3130 assert_eq!(params.len(), 1);
3131 assert!(matches!(params[0], TypeExpr::Int));
3132 assert!(matches!(ret.as_ref(), TypeExpr::Int));
3133 } else {
3134 panic!("expected Fn type for first param");
3135 }
3136 }
3137
3138 #[test]
3139 fn parse_closure_with_params() {
3140 let source = r#"
3141 agent Main {
3142 on start {
3143 let f = |x: Int| x + 1;
3144 emit(0);
3145 }
3146 }
3147 run Main;
3148 "#;
3149
3150 let (prog, errors) = parse_str(source);
3151 assert!(errors.is_empty(), "errors: {errors:?}");
3152 let prog = prog.expect("should parse");
3153
3154 let handler = &prog.agents[0].handlers[0];
3156 if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
3157 if let Expr::Closure { params, body, .. } = value {
3158 assert_eq!(params.len(), 1);
3159 assert_eq!(params[0].name.name, "x");
3160 assert!(matches!(¶ms[0].ty, Some(TypeExpr::Int)));
3161
3162 assert!(matches!(body.as_ref(), Expr::Binary { .. }));
3164 } else {
3165 panic!("expected closure expression");
3166 }
3167 } else {
3168 panic!("expected let statement");
3169 }
3170 }
3171
3172 #[test]
3173 fn parse_closure_empty_params() {
3174 let source = r#"
3175 agent Main {
3176 on start {
3177 let f = || 42;
3178 emit(0);
3179 }
3180 }
3181 run Main;
3182 "#;
3183
3184 let (prog, errors) = parse_str(source);
3185 assert!(errors.is_empty(), "errors: {errors:?}");
3186 let prog = prog.expect("should parse");
3187
3188 let handler = &prog.agents[0].handlers[0];
3190 if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
3191 if let Expr::Closure { params, body, .. } = value {
3192 assert!(params.is_empty());
3193
3194 assert!(matches!(body.as_ref(), Expr::Literal { .. }));
3196 } else {
3197 panic!("expected closure expression");
3198 }
3199 } else {
3200 panic!("expected let statement");
3201 }
3202 }
3203
3204 #[test]
3205 fn parse_closure_multiple_params() {
3206 let source = r#"
3207 agent Main {
3208 on start {
3209 let add = |x: Int, y: Int| x + y;
3210 emit(0);
3211 }
3212 }
3213 run Main;
3214 "#;
3215
3216 let (prog, errors) = parse_str(source);
3217 assert!(errors.is_empty(), "errors: {errors:?}");
3218 let prog = prog.expect("should parse");
3219
3220 let handler = &prog.agents[0].handlers[0];
3221 if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
3222 if let Expr::Closure { params, .. } = value {
3223 assert_eq!(params.len(), 2);
3224 assert_eq!(params[0].name.name, "x");
3225 assert_eq!(params[1].name.name, "y");
3226 } else {
3227 panic!("expected closure expression");
3228 }
3229 } else {
3230 panic!("expected let statement");
3231 }
3232 }
3233
3234 #[test]
3235 fn parse_fn_type_multiarg() {
3236 let source = r#"
3237 fn fold_left(f: Fn(Int, Int) -> Int, init: Int) -> Int {
3238 return init;
3239 }
3240
3241 agent Main {
3242 on start {
3243 emit(0);
3244 }
3245 }
3246 run Main;
3247 "#;
3248
3249 let (prog, errors) = parse_str(source);
3250 assert!(errors.is_empty(), "errors: {errors:?}");
3251 let prog = prog.expect("should parse");
3252
3253 if let TypeExpr::Fn(params, ret) = &prog.functions[0].params[0].ty {
3255 assert_eq!(params.len(), 2);
3256 assert!(matches!(params[0], TypeExpr::Int));
3257 assert!(matches!(params[1], TypeExpr::Int));
3258 assert!(matches!(ret.as_ref(), TypeExpr::Int));
3259 } else {
3260 panic!("expected Fn type");
3261 }
3262 }
3263
3264 #[test]
3265 fn parse_tuple_literal() {
3266 let source = r#"
3267 agent Main {
3268 on start {
3269 let t = (1, 2);
3270 emit(0);
3271 }
3272 }
3273 run Main;
3274 "#;
3275
3276 let (prog, errors) = parse_str(source);
3277 assert!(errors.is_empty(), "errors: {errors:?}");
3278 let prog = prog.expect("should parse");
3279
3280 let handler = &prog.agents[0].handlers[0];
3281 if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
3282 if let Expr::Tuple { elements, .. } = value {
3283 assert_eq!(elements.len(), 2);
3284 } else {
3285 panic!("expected tuple expression, got {:?}", value);
3286 }
3287 } else {
3288 panic!("expected let statement");
3289 }
3290 }
3291
3292 #[test]
3297 fn parse_tool_declaration() {
3298 let source = r#"
3299 tool Http {
3300 fn get(url: String) -> Result<String, String>
3301 fn post(url: String, body: String) -> Result<String, String>
3302 }
3303 agent Main {
3304 on start { emit(0); }
3305 }
3306 run Main;
3307 "#;
3308
3309 let (prog, errors) = parse_str(source);
3310 assert!(errors.is_empty(), "errors: {errors:?}");
3311 let prog = prog.expect("should parse");
3312
3313 assert_eq!(prog.tools.len(), 1);
3314 assert_eq!(prog.tools[0].name.name, "Http");
3315 assert_eq!(prog.tools[0].functions.len(), 2);
3316 assert_eq!(prog.tools[0].functions[0].name.name, "get");
3317 assert_eq!(prog.tools[0].functions[1].name.name, "post");
3318 }
3319
3320 #[test]
3321 fn parse_pub_tool_declaration() {
3322 let source = r#"
3323 pub tool Database {
3324 fn query(sql: String) -> Result<List<String>, String>
3325 }
3326 agent Main {
3327 on start { emit(0); }
3328 }
3329 run Main;
3330 "#;
3331
3332 let (prog, errors) = parse_str(source);
3333 assert!(errors.is_empty(), "errors: {errors:?}");
3334 let prog = prog.expect("should parse");
3335
3336 assert!(prog.tools[0].is_pub);
3337 assert_eq!(prog.tools[0].name.name, "Database");
3338 }
3339
3340 #[test]
3341 fn parse_agent_with_tool_use() {
3342 let source = r#"
3343 agent Fetcher {
3344 use Http
3345
3346 url: String
3347
3348 on start {
3349 emit(0);
3350 }
3351 }
3352 run Fetcher;
3353 "#;
3354
3355 let (prog, errors) = parse_str(source);
3356 assert!(errors.is_empty(), "errors: {errors:?}");
3357 let prog = prog.expect("should parse");
3358
3359 assert_eq!(prog.agents[0].tool_uses.len(), 1);
3360 assert_eq!(prog.agents[0].tool_uses[0].name, "Http");
3361 assert_eq!(prog.agents[0].beliefs.len(), 1);
3362 }
3363
3364 #[test]
3365 fn parse_agent_with_multiple_tool_uses() {
3366 let source = r#"
3367 agent Pipeline {
3368 use Http, Fs
3369
3370 on start {
3371 emit(0);
3372 }
3373 }
3374 run Pipeline;
3375 "#;
3376
3377 let (prog, errors) = parse_str(source);
3378 assert!(errors.is_empty(), "errors: {errors:?}");
3379 let prog = prog.expect("should parse");
3380
3381 assert_eq!(prog.agents[0].tool_uses.len(), 2);
3382 assert_eq!(prog.agents[0].tool_uses[0].name, "Http");
3383 assert_eq!(prog.agents[0].tool_uses[1].name, "Fs");
3384 }
3385
3386 #[test]
3387 fn parse_tool_call_expression() {
3388 let source = r#"
3389 agent Fetcher {
3390 use Http
3391
3392 on start {
3393 let response = Http.get("https://example.com");
3394 emit(0);
3395 }
3396 }
3397 run Fetcher;
3398 "#;
3399
3400 let (prog, errors) = parse_str(source);
3401 assert!(errors.is_empty(), "errors: {errors:?}");
3402 let prog = prog.expect("should parse");
3403
3404 let handler = &prog.agents[0].handlers[0];
3405 if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
3406 if let Expr::ToolCall {
3407 tool,
3408 function,
3409 args,
3410 ..
3411 } = value
3412 {
3413 assert_eq!(tool.name, "Http");
3414 assert_eq!(function.name, "get");
3415 assert_eq!(args.len(), 1);
3416 } else {
3417 panic!("expected ToolCall expression, got {:?}", value);
3418 }
3419 } else {
3420 panic!("expected let statement");
3421 }
3422 }
3423
3424 #[test]
3425 fn parse_tool_call_with_multiple_args() {
3426 let source = r#"
3427 agent Writer {
3428 use Fs
3429
3430 on start {
3431 let result = Fs.write("/tmp/test.txt", "hello world");
3432 emit(0);
3433 }
3434 }
3435 run Writer;
3436 "#;
3437
3438 let (prog, errors) = parse_str(source);
3439 assert!(errors.is_empty(), "errors: {errors:?}");
3440 let prog = prog.expect("should parse");
3441
3442 let handler = &prog.agents[0].handlers[0];
3443 if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
3444 if let Expr::ToolCall { args, .. } = value {
3445 assert_eq!(args.len(), 2);
3446 } else {
3447 panic!("expected ToolCall expression, got {:?}", value);
3448 }
3449 } else {
3450 panic!("expected let statement");
3451 }
3452 }
3453}