1use crate::ast::{
6 AgentDecl, BeliefDecl, BinOp, Block, ClosureParam, ConstDecl, ElseBranch, EnumDecl, EventKind,
7 Expr, FieldInit, FnDecl, HandlerDecl, InterpExpr, Literal, MapEntry, MatchArm, MockValue,
8 ModDecl, Param, Pattern, Program, RecordDecl, RecordField, Stmt, StringPart, StringTemplate,
9 TestDecl, ToolDecl, ToolFnDecl, UnaryOp, UseDecl, UseKind,
10};
11use chumsky::prelude::*;
12use chumsky::BoxedParser;
13use crate::{Spanned, Token};
14use crate::{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 .or(test_parser(source.clone()))
64 .recover_with(skip_then_retry_until([
65 Token::KwMod,
66 Token::KwUse,
67 Token::KwPub,
68 Token::KwRecord,
69 Token::KwEnum,
70 Token::KwConst,
71 Token::KwTool,
72 Token::KwAgent,
73 Token::KwFn,
74 Token::KwRun,
75 Token::KwTest,
76 ]));
77
78 let run_stmt = just(Token::KwRun)
79 .ignore_then(ident_token_parser(src.clone()))
80 .then_ignore(just(Token::Semicolon))
81 .or_not();
82
83 top_level.repeated().then(run_stmt).map_with_span(
84 move |(items, run_agent), span: Range<usize>| {
85 let mut mod_decls = Vec::new();
86 let mut use_decls = Vec::new();
87 let mut records = Vec::new();
88 let mut enums = Vec::new();
89 let mut consts = Vec::new();
90 let mut tools = Vec::new();
91 let mut agents = Vec::new();
92 let mut functions = Vec::new();
93 let mut tests = Vec::new();
94
95 for item in items {
96 match item {
97 TopLevel::Mod(m) => mod_decls.push(m),
98 TopLevel::Use(u) => use_decls.push(u),
99 TopLevel::Record(r) => records.push(r),
100 TopLevel::Enum(e) => enums.push(e),
101 TopLevel::Const(c) => consts.push(c),
102 TopLevel::Tool(t) => tools.push(t),
103 TopLevel::Agent(a) => agents.push(a),
104 TopLevel::Function(f) => functions.push(f),
105 TopLevel::Test(t) => tests.push(t),
106 }
107 }
108
109 Program {
110 mod_decls,
111 use_decls,
112 records,
113 enums,
114 consts,
115 tools,
116 agents,
117 functions,
118 tests,
119 run_agent,
120 span: make_span(&src2, span),
121 }
122 },
123 )
124}
125
126enum TopLevel {
128 Mod(ModDecl),
129 Use(UseDecl),
130 Record(RecordDecl),
131 Enum(EnumDecl),
132 Const(ConstDecl),
133 Tool(ToolDecl),
134 Agent(AgentDecl),
135 Function(FnDecl),
136 Test(TestDecl),
137}
138
139#[allow(clippy::needless_pass_by_value)]
145fn mod_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
146 let src = source.clone();
147
148 just(Token::KwPub)
149 .or_not()
150 .then_ignore(just(Token::KwMod))
151 .then(ident_token_parser(src.clone()))
152 .then_ignore(just(Token::Semicolon))
153 .map_with_span(move |(is_pub, name), span: Range<usize>| {
154 TopLevel::Mod(ModDecl {
155 is_pub: is_pub.is_some(),
156 name,
157 span: make_span(&src, span),
158 })
159 })
160}
161
162#[allow(clippy::needless_pass_by_value)]
164fn use_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
165 let src = source.clone();
166 let src2 = source.clone();
167 let src3 = source.clone();
168 let src4 = source.clone();
169
170 let simple_use = just(Token::KwPub)
172 .or_not()
173 .then_ignore(just(Token::KwUse))
174 .then(
175 ident_token_parser(src.clone())
176 .separated_by(just(Token::ColonColon))
177 .at_least(1),
178 )
179 .then(
180 just(Token::KwAs)
181 .ignore_then(ident_token_parser(src.clone()))
182 .or_not(),
183 )
184 .then_ignore(just(Token::Semicolon))
185 .map_with_span(move |((is_pub, path), alias), span: Range<usize>| {
186 TopLevel::Use(UseDecl {
187 is_pub: is_pub.is_some(),
188 path,
189 kind: UseKind::Simple(alias),
190 span: make_span(&src, span),
191 })
192 });
193
194 let group_item = ident_token_parser(src2.clone()).then(
196 just(Token::KwAs)
197 .ignore_then(ident_token_parser(src2.clone()))
198 .or_not(),
199 );
200
201 let group_use = just(Token::KwPub)
203 .or_not()
204 .then_ignore(just(Token::KwUse))
205 .then(
206 ident_token_parser(src3.clone())
207 .then_ignore(just(Token::ColonColon))
208 .repeated()
209 .at_least(1),
210 )
211 .then(
212 group_item
213 .separated_by(just(Token::Comma))
214 .allow_trailing()
215 .delimited_by(just(Token::LBrace), just(Token::RBrace)),
216 )
217 .then_ignore(just(Token::Semicolon))
218 .map_with_span(move |((is_pub, path), items), span: Range<usize>| {
219 TopLevel::Use(UseDecl {
220 is_pub: is_pub.is_some(),
221 path,
222 kind: UseKind::Group(items),
223 span: make_span(&src3, span),
224 })
225 });
226
227 let glob_use = just(Token::KwPub)
229 .or_not()
230 .then_ignore(just(Token::KwUse))
231 .then(
232 ident_token_parser(src4.clone())
233 .then_ignore(just(Token::ColonColon))
234 .repeated()
235 .at_least(1),
236 )
237 .then_ignore(just(Token::Star))
238 .then_ignore(just(Token::Semicolon))
239 .map_with_span(move |(is_pub, path), span: Range<usize>| {
240 TopLevel::Use(UseDecl {
241 is_pub: is_pub.is_some(),
242 path,
243 kind: UseKind::Glob,
244 span: make_span(&src4, span),
245 })
246 });
247
248 group_use.or(glob_use).or(simple_use)
250}
251
252#[allow(clippy::needless_pass_by_value)]
258fn record_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
259 let src = source.clone();
260 let src2 = source.clone();
261
262 let field = ident_token_parser(src.clone())
264 .then_ignore(just(Token::Colon))
265 .then(type_parser(src.clone()))
266 .map_with_span(move |(name, ty), span: Range<usize>| RecordField {
267 name,
268 ty,
269 span: make_span(&src, span),
270 });
271
272 just(Token::KwPub)
273 .or_not()
274 .then_ignore(just(Token::KwRecord))
275 .then(ident_token_parser(src2.clone()))
276 .then(
277 field
278 .separated_by(just(Token::Comma))
279 .allow_trailing()
280 .delimited_by(just(Token::LBrace), just(Token::RBrace)),
281 )
282 .map_with_span(move |((is_pub, name), fields), span: Range<usize>| {
283 TopLevel::Record(RecordDecl {
284 is_pub: is_pub.is_some(),
285 name,
286 fields,
287 span: make_span(&src2, span),
288 })
289 })
290}
291
292#[allow(clippy::needless_pass_by_value)]
294fn enum_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
295 let src = source.clone();
296 let src2 = source.clone();
297 let src3 = source.clone();
298
299 let variant = ident_token_parser(src.clone())
301 .then(
302 type_parser(src.clone())
303 .delimited_by(just(Token::LParen), just(Token::RParen))
304 .or_not(),
305 )
306 .map_with_span({
307 let src = src.clone();
308 move |(name, payload), span: Range<usize>| crate::ast::EnumVariant {
309 name,
310 payload,
311 span: make_span(&src, span),
312 }
313 });
314
315 just(Token::KwPub)
316 .or_not()
317 .then_ignore(just(Token::KwEnum))
318 .then(ident_token_parser(src3.clone()))
319 .then(
320 variant
321 .separated_by(just(Token::Comma))
322 .allow_trailing()
323 .delimited_by(just(Token::LBrace), just(Token::RBrace)),
324 )
325 .map_with_span(move |((is_pub, name), variants), span: Range<usize>| {
326 TopLevel::Enum(EnumDecl {
327 is_pub: is_pub.is_some(),
328 name,
329 variants,
330 span: make_span(&src2, span),
331 })
332 })
333}
334
335#[allow(clippy::needless_pass_by_value)]
337fn const_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
338 let src = source.clone();
339 let src2 = source.clone();
340
341 just(Token::KwPub)
342 .or_not()
343 .then_ignore(just(Token::KwConst))
344 .then(ident_token_parser(src.clone()))
345 .then_ignore(just(Token::Colon))
346 .then(type_parser(src.clone()))
347 .then_ignore(just(Token::Eq))
348 .then(expr_parser(src.clone()))
349 .then_ignore(just(Token::Semicolon))
350 .map_with_span(move |(((is_pub, name), ty), value), span: Range<usize>| {
351 TopLevel::Const(ConstDecl {
352 is_pub: is_pub.is_some(),
353 name,
354 ty,
355 value,
356 span: make_span(&src2, span),
357 })
358 })
359}
360
361#[allow(clippy::needless_pass_by_value)]
367fn tool_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
368 let src = source.clone();
369 let src2 = source.clone();
370 let src3 = source.clone();
371
372 let param = ident_token_parser(src.clone())
374 .then_ignore(just(Token::Colon))
375 .then(type_parser(src.clone()))
376 .map_with_span(move |(name, ty), span: Range<usize>| Param {
377 name,
378 ty,
379 span: make_span(&src, span),
380 });
381
382 let params = param
383 .separated_by(just(Token::Comma))
384 .allow_trailing()
385 .delimited_by(just(Token::LParen), just(Token::RParen));
386
387 let tool_fn = just(Token::KwFn)
389 .ignore_then(ident_token_parser(src2.clone()))
390 .then(params)
391 .then_ignore(just(Token::Arrow))
392 .then(type_parser(src2.clone()))
393 .map_with_span(move |((name, params), return_ty), span: Range<usize>| ToolFnDecl {
394 name,
395 params,
396 return_ty,
397 span: make_span(&src2, span),
398 });
399
400 just(Token::KwPub)
401 .or_not()
402 .then_ignore(just(Token::KwTool))
403 .then(ident_token_parser(src3.clone()))
404 .then(
405 tool_fn
406 .repeated()
407 .delimited_by(just(Token::LBrace), just(Token::RBrace)),
408 )
409 .map_with_span(move |((is_pub, name), functions), span: Range<usize>| {
410 TopLevel::Tool(ToolDecl {
411 is_pub: is_pub.is_some(),
412 name,
413 functions,
414 span: make_span(&src3, span),
415 })
416 })
417}
418
419#[allow(clippy::needless_pass_by_value)]
425fn test_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
426 let src = source.clone();
427 let src2 = source.clone();
428
429 let serial_annotation = just(Token::At)
432 .then(filter(|t: &Token| matches!(t, Token::Ident)))
433 .or_not()
434 .map(|opt| opt.is_some());
435
436 let test_name = filter_map(|span: Range<usize>, tok: Token| match tok {
438 Token::StringLit => Ok(()),
439 _ => Err(Simple::expected_input_found(span, [], Some(tok))),
440 })
441 .map_with_span(move |_, span: Range<usize>| {
442 let s = &src[span.clone()];
444 s.trim_matches('"').to_string()
445 });
446
447 let body = block_parser(src2.clone());
449
450 serial_annotation
451 .then_ignore(just(Token::KwTest))
452 .then(test_name)
453 .then(body)
454 .map_with_span(move |((is_serial, name), body), span: Range<usize>| {
455 TopLevel::Test(TestDecl {
456 name,
457 is_serial,
458 body,
459 span: make_span(&src2, span),
460 })
461 })
462}
463
464#[allow(clippy::needless_pass_by_value)]
470fn agent_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
471 let src = source.clone();
472 let src2 = source.clone();
473 let src3 = source.clone();
474 let src4 = source.clone();
475 let src5 = source.clone();
476
477 let tool_use = just(Token::KwUse)
479 .ignore_then(
480 ident_token_parser(src5.clone())
481 .separated_by(just(Token::Comma))
482 .at_least(1),
483 )
484 .or_not()
485 .map(|tools| tools.unwrap_or_default());
486
487 let belief = ident_token_parser(src.clone())
490 .then_ignore(just(Token::Colon))
491 .then(type_parser(src.clone()))
492 .map_with_span(move |(name, ty), span: Range<usize>| BeliefDecl {
493 name,
494 ty,
495 span: make_span(&src, span),
496 });
497
498 let handler = just(Token::KwOn)
499 .ignore_then(event_kind_parser(src2.clone()))
500 .then(block_parser(src2.clone()))
501 .map_with_span(move |(event, body), span: Range<usize>| HandlerDecl {
502 event,
503 body,
504 span: make_span(&src2, span),
505 });
506
507 let receives_clause = just(Token::KwReceives)
509 .ignore_then(type_parser(src3.clone()))
510 .or_not();
511
512 just(Token::KwPub)
513 .or_not()
514 .then_ignore(just(Token::KwAgent))
515 .then(ident_token_parser(src3.clone()))
516 .then(receives_clause)
517 .then_ignore(just(Token::LBrace))
518 .then(tool_use)
519 .then(belief.repeated())
520 .then(handler.repeated())
521 .then_ignore(just(Token::RBrace))
522 .map_with_span(
523 move |(((((is_pub, name), receives), tool_uses), beliefs), handlers),
524 span: Range<usize>| {
525 TopLevel::Agent(AgentDecl {
526 is_pub: is_pub.is_some(),
527 name,
528 receives,
529 tool_uses,
530 beliefs,
531 handlers,
532 span: make_span(&src4, span),
533 })
534 },
535 )
536}
537
538#[allow(clippy::needless_pass_by_value)]
540fn event_kind_parser(source: Arc<str>) -> impl Parser<Token, EventKind, Error = ParseError> {
541 let src = source.clone();
542
543 let start = just(Token::KwStart).to(EventKind::Start);
544 let stop = just(Token::KwStop).to(EventKind::Stop);
545
546 let message = just(Token::KwMessage)
547 .ignore_then(just(Token::LParen))
548 .ignore_then(ident_token_parser(src.clone()))
549 .then_ignore(just(Token::Colon))
550 .then(type_parser(src.clone()))
551 .then_ignore(just(Token::RParen))
552 .map(|(param_name, param_ty)| EventKind::Message {
553 param_name,
554 param_ty,
555 });
556
557 let error = just(Token::KwError)
559 .ignore_then(just(Token::LParen))
560 .ignore_then(ident_token_parser(src))
561 .then_ignore(just(Token::RParen))
562 .map(|param_name| EventKind::Error { param_name });
563
564 start.or(stop).or(message).or(error)
565}
566
567#[allow(clippy::needless_pass_by_value)]
573fn fn_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
574 let src = source.clone();
575 let src2 = source.clone();
576 let src3 = source.clone();
577
578 let param = ident_token_parser(src.clone())
579 .then_ignore(just(Token::Colon))
580 .then(type_parser(src.clone()))
581 .map_with_span(move |(name, ty), span: Range<usize>| Param {
582 name,
583 ty,
584 span: make_span(&src, span),
585 });
586
587 let params = param
588 .separated_by(just(Token::Comma))
589 .allow_trailing()
590 .delimited_by(just(Token::LParen), just(Token::RParen));
591
592 just(Token::KwPub)
593 .or_not()
594 .then_ignore(just(Token::KwFn))
595 .then(ident_token_parser(src2.clone()))
596 .then(params)
597 .then_ignore(just(Token::Arrow))
598 .then(type_parser(src2.clone()))
599 .then(just(Token::KwFails).or_not())
600 .then(block_parser(src2))
601 .map_with_span(
602 move |(((((is_pub, name), params), return_ty), is_fallible), body),
603 span: Range<usize>| {
604 TopLevel::Function(FnDecl {
605 is_pub: is_pub.is_some(),
606 name,
607 params,
608 return_ty,
609 is_fallible: is_fallible.is_some(),
610 body,
611 span: make_span(&src3, span),
612 })
613 },
614 )
615}
616
617#[allow(clippy::needless_pass_by_value)]
624fn block_parser(source: Arc<str>) -> BoxedParser<'static, Token, Block, ParseError> {
625 let src = source.clone();
626
627 recursive(move |block: Recursive<Token, Block, ParseError>| {
628 let src_inner = src.clone();
629 stmt_parser(src.clone(), block)
630 .repeated()
631 .delimited_by(just(Token::LBrace), just(Token::RBrace))
632 .recover_with(nested_delimiters(
633 Token::LBrace,
634 Token::RBrace,
635 [
636 (Token::LParen, Token::RParen),
637 (Token::LBracket, Token::RBracket),
638 ],
639 |_span: Range<usize>| vec![],
640 ))
641 .map_with_span(move |stmts, span: Range<usize>| Block {
642 stmts,
643 span: make_span(&src_inner, span),
644 })
645 })
646 .boxed()
647}
648
649#[allow(clippy::needless_pass_by_value)]
651fn stmt_parser(
652 source: Arc<str>,
653 block: impl Parser<Token, Block, Error = ParseError> + Clone + 'static,
654) -> impl Parser<Token, Stmt, Error = ParseError> + Clone {
655 let src = source.clone();
656 let src2 = source.clone();
657 let src3 = source.clone();
658 let src4 = source.clone();
659 let src5 = source.clone();
660 let src6 = source.clone();
661 let src7 = source.clone();
662
663 let src10 = source.clone();
665 let let_tuple_stmt = just(Token::KwLet)
666 .ignore_then(
667 ident_token_parser(src10.clone())
668 .separated_by(just(Token::Comma))
669 .at_least(2)
670 .allow_trailing()
671 .delimited_by(just(Token::LParen), just(Token::RParen)),
672 )
673 .then(
674 just(Token::Colon)
675 .ignore_then(type_parser(src10.clone()))
676 .or_not(),
677 )
678 .then_ignore(just(Token::Eq))
679 .then(expr_parser(src10.clone()))
680 .then_ignore(just(Token::Semicolon))
681 .map_with_span(move |((names, ty), value), span: Range<usize>| Stmt::LetTuple {
682 names,
683 ty,
684 value,
685 span: make_span(&src10, span),
686 });
687
688 let let_stmt = just(Token::KwLet)
689 .ignore_then(ident_token_parser(src.clone()))
690 .then(
691 just(Token::Colon)
692 .ignore_then(type_parser(src.clone()))
693 .or_not(),
694 )
695 .then_ignore(just(Token::Eq))
696 .then(expr_parser(src.clone()))
697 .then_ignore(just(Token::Semicolon))
698 .map_with_span(move |((name, ty), value), span: Range<usize>| Stmt::Let {
699 name,
700 ty,
701 value,
702 span: make_span(&src, span),
703 });
704
705 let return_stmt = just(Token::KwReturn)
706 .ignore_then(expr_parser(src2.clone()).or_not())
707 .then_ignore(just(Token::Semicolon))
708 .map_with_span(move |value, span: Range<usize>| Stmt::Return {
709 value,
710 span: make_span(&src2, span),
711 });
712
713 let if_stmt = recursive(|if_stmt| {
714 let src_if = src3.clone();
715 let block_clone = block.clone();
716
717 just(Token::KwIf)
718 .ignore_then(expr_parser(src3.clone()))
719 .then(block_clone.clone())
720 .then(
721 just(Token::KwElse)
722 .ignore_then(
723 if_stmt
724 .map(|s| ElseBranch::ElseIf(Box::new(s)))
725 .or(block_clone.map(ElseBranch::Block)),
726 )
727 .or_not(),
728 )
729 .map_with_span(
730 move |((condition, then_block), else_block), span: Range<usize>| Stmt::If {
731 condition,
732 then_block,
733 else_block,
734 span: make_span(&src_if, span),
735 },
736 )
737 });
738
739 let for_stmt = just(Token::KwFor)
740 .ignore_then(for_pattern_parser(src4.clone()))
741 .then_ignore(just(Token::KwIn))
742 .then(expr_parser(src4.clone()))
743 .then(block.clone())
744 .map_with_span(move |((pattern, iter), body), span: Range<usize>| Stmt::For {
745 pattern,
746 iter,
747 body,
748 span: make_span(&src4, span),
749 });
750
751 let while_stmt = just(Token::KwWhile)
752 .ignore_then(expr_parser(src7.clone()))
753 .then(block.clone())
754 .map_with_span(move |(condition, body), span: Range<usize>| Stmt::While {
755 condition,
756 body,
757 span: make_span(&src7, span),
758 });
759
760 let src8 = source.clone();
761 let loop_stmt = just(Token::KwLoop)
762 .ignore_then(block.clone())
763 .map_with_span(move |body, span: Range<usize>| Stmt::Loop {
764 body,
765 span: make_span(&src8, span),
766 });
767
768 let src9 = source.clone();
769 let break_stmt = just(Token::KwBreak)
770 .then_ignore(just(Token::Semicolon))
771 .map_with_span(move |_, span: Range<usize>| Stmt::Break {
772 span: make_span(&src9, span),
773 });
774
775 let src12 = source.clone();
777 let mock_infer_stmt = just(Token::KwMock)
778 .ignore_then(just(Token::KwInfer))
779 .ignore_then(just(Token::Arrow))
780 .ignore_then(expr_parser(src12.clone()).map(|expr| {
781 if let Expr::Fail { error, .. } = expr {
783 MockValue::Fail(*error)
784 } else {
785 MockValue::Value(expr)
786 }
787 }))
788 .then_ignore(just(Token::Semicolon))
789 .map_with_span(move |value, span: Range<usize>| Stmt::MockInfer {
790 value,
791 span: make_span(&src12, span),
792 });
793
794 let src13 = source.clone();
796 let src14 = source.clone();
797 let src15 = source.clone();
798 let mock_tool_stmt = just(Token::KwMock)
799 .ignore_then(just(Token::KwTool))
800 .ignore_then(ident_token_parser(src13.clone())) .then_ignore(just(Token::Dot))
802 .then(ident_token_parser(src14.clone())) .then_ignore(just(Token::Arrow))
804 .then(expr_parser(src15.clone()).map(|expr| {
805 if let Expr::Fail { error, .. } = expr {
807 MockValue::Fail(*error)
808 } else {
809 MockValue::Value(expr)
810 }
811 }))
812 .then_ignore(just(Token::Semicolon))
813 .map_with_span(move |((tool_name, fn_name), value), span: Range<usize>| Stmt::MockTool {
814 tool_name,
815 fn_name,
816 value,
817 span: make_span(&src15, span),
818 });
819
820 let assign_stmt = ident_token_parser(src5.clone())
821 .then_ignore(just(Token::Eq))
822 .then(expr_parser(src5.clone()))
823 .then_ignore(just(Token::Semicolon))
824 .map_with_span(move |(name, value), span: Range<usize>| Stmt::Assign {
825 name,
826 value,
827 span: make_span(&src5, span),
828 });
829
830 let expr_stmt = expr_parser(src6.clone())
831 .then_ignore(just(Token::Semicolon))
832 .map_with_span(move |expr, span: Range<usize>| Stmt::Expr {
833 expr,
834 span: make_span(&src6, span),
835 });
836
837 let_tuple_stmt
838 .or(let_stmt)
839 .or(return_stmt)
840 .or(if_stmt)
841 .or(for_stmt)
842 .or(while_stmt)
843 .or(loop_stmt)
844 .or(break_stmt)
845 .or(mock_infer_stmt)
846 .or(mock_tool_stmt)
847 .or(assign_stmt)
848 .or(expr_stmt)
849}
850
851#[allow(clippy::needless_pass_by_value, clippy::too_many_lines)]
858fn expr_parser(source: Arc<str>) -> BoxedParser<'static, Token, Expr, ParseError> {
859 recursive(move |expr: Recursive<Token, Expr, ParseError>| {
860 let src = source.clone();
861
862 let literal = literal_parser(src.clone());
863 let var = var_parser(src.clone());
864
865 let paren_or_tuple = just(Token::LParen)
868 .ignore_then(
869 expr.clone()
870 .separated_by(just(Token::Comma))
871 .allow_trailing(),
872 )
873 .then_ignore(just(Token::RParen))
874 .map_with_span({
875 let src = src.clone();
876 move |elements, span: Range<usize>| {
877 if elements.len() == 1 {
878 Expr::Paren {
880 inner: Box::new(elements.into_iter().next().unwrap()),
881 span: make_span(&src, span),
882 }
883 } else {
884 Expr::Tuple {
886 elements,
887 span: make_span(&src, span),
888 }
889 }
890 }
891 });
892
893 let list = expr
894 .clone()
895 .separated_by(just(Token::Comma))
896 .allow_trailing()
897 .delimited_by(just(Token::LBracket), just(Token::RBracket))
898 .map_with_span({
899 let src = src.clone();
900 move |elements, span: Range<usize>| Expr::List {
901 elements,
902 span: make_span(&src, span),
903 }
904 });
905
906 let self_access = just(Token::KwSelf)
908 .ignore_then(just(Token::Dot))
909 .ignore_then(ident_token_parser(src.clone()))
910 .then(
911 expr.clone()
912 .separated_by(just(Token::Comma))
913 .allow_trailing()
914 .delimited_by(just(Token::LParen), just(Token::RParen))
915 .or_not(),
916 )
917 .map_with_span({
918 let src = src.clone();
919 move |(field, args), span: Range<usize>| match args {
920 Some(args) => Expr::SelfMethodCall {
921 method: field,
922 args,
923 span: make_span(&src, span),
924 },
925 None => Expr::SelfField {
926 field,
927 span: make_span(&src, span),
928 },
929 }
930 });
931
932 let infer_expr = just(Token::KwInfer)
934 .ignore_then(just(Token::LParen))
935 .ignore_then(string_template_parser(src.clone()))
936 .then(
937 just(Token::Arrow)
938 .ignore_then(type_parser(src.clone()))
939 .or_not(),
940 )
941 .then_ignore(just(Token::RParen))
942 .map_with_span({
943 let src = src.clone();
944 move |(template, result_ty), span: Range<usize>| Expr::Infer {
945 template,
946 result_ty,
947 span: make_span(&src, span),
948 }
949 });
950
951 let spawn_field_init = ident_token_parser(src.clone())
953 .then_ignore(just(Token::Colon))
954 .then(expr.clone())
955 .map_with_span({
956 let src = src.clone();
957 move |(name, value), span: Range<usize>| FieldInit {
958 name,
959 value,
960 span: make_span(&src, span),
961 }
962 });
963
964 let spawn_expr = just(Token::KwSpawn)
965 .ignore_then(ident_token_parser(src.clone()))
966 .then_ignore(just(Token::LBrace))
967 .then(
968 spawn_field_init
969 .separated_by(just(Token::Comma))
970 .allow_trailing(),
971 )
972 .then_ignore(just(Token::RBrace))
973 .map_with_span({
974 let src = src.clone();
975 move |(agent, fields), span: Range<usize>| Expr::Spawn {
976 agent,
977 fields,
978 span: make_span(&src, span),
979 }
980 });
981
982 let timeout_clause = just(Token::KwTimeout)
985 .ignore_then(just(Token::LParen))
986 .ignore_then(expr.clone())
987 .then_ignore(just(Token::RParen));
988
989 let await_expr = just(Token::KwAwait)
990 .ignore_then(ident_token_parser(src.clone()).map_with_span({
991 let src = src.clone();
992 move |name, span: Range<usize>| Expr::Var {
993 name,
994 span: make_span(&src, span),
995 }
996 }))
997 .then(timeout_clause.or_not())
998 .map_with_span({
999 let src = src.clone();
1000 move |(handle, timeout), span: Range<usize>| Expr::Await {
1001 handle: Box::new(handle),
1002 timeout: timeout.map(Box::new),
1003 span: make_span(&src, span),
1004 }
1005 });
1006
1007 let send_expr = just(Token::KwSend)
1009 .ignore_then(just(Token::LParen))
1010 .ignore_then(expr.clone())
1011 .then_ignore(just(Token::Comma))
1012 .then(expr.clone())
1013 .then_ignore(just(Token::RParen))
1014 .map_with_span({
1015 let src = src.clone();
1016 move |(handle, message), span: Range<usize>| Expr::Send {
1017 handle: Box::new(handle),
1018 message: Box::new(message),
1019 span: make_span(&src, span),
1020 }
1021 });
1022
1023 let emit_expr = just(Token::KwEmit)
1025 .ignore_then(just(Token::LParen))
1026 .ignore_then(expr.clone())
1027 .then_ignore(just(Token::RParen))
1028 .map_with_span({
1029 let src = src.clone();
1030 move |value, span: Range<usize>| Expr::Emit {
1031 value: Box::new(value),
1032 span: make_span(&src, span),
1033 }
1034 });
1035
1036 let call_expr = ident_token_parser(src.clone())
1038 .then(
1039 expr.clone()
1040 .separated_by(just(Token::Comma))
1041 .allow_trailing()
1042 .delimited_by(just(Token::LParen), just(Token::RParen)),
1043 )
1044 .map_with_span({
1045 let src = src.clone();
1046 move |(name, args), span: Range<usize>| Expr::Call {
1047 name,
1048 args,
1049 span: make_span(&src, span),
1050 }
1051 });
1052
1053 let pattern = pattern_parser(src.clone());
1055
1056 let match_arm = pattern
1058 .then_ignore(just(Token::FatArrow))
1059 .then(expr.clone())
1060 .map_with_span({
1061 let src = src.clone();
1062 move |(pattern, body), span: Range<usize>| MatchArm {
1063 pattern,
1064 body,
1065 span: make_span(&src, span),
1066 }
1067 });
1068
1069 let match_expr = just(Token::KwMatch)
1070 .ignore_then(expr.clone())
1071 .then(
1072 match_arm
1073 .separated_by(just(Token::Comma))
1074 .allow_trailing()
1075 .delimited_by(just(Token::LBrace), just(Token::RBrace)),
1076 )
1077 .map_with_span({
1078 let src = src.clone();
1079 move |(scrutinee, arms), span: Range<usize>| Expr::Match {
1080 scrutinee: Box::new(scrutinee),
1081 arms,
1082 span: make_span(&src, span),
1083 }
1084 });
1085
1086 let receive_expr = just(Token::KwReceive)
1088 .ignore_then(just(Token::LParen))
1089 .ignore_then(just(Token::RParen))
1090 .map_with_span({
1091 let src = src.clone();
1092 move |_, span: Range<usize>| Expr::Receive {
1093 span: make_span(&src, span),
1094 }
1095 });
1096
1097 let trace_expr = just(Token::KwTrace)
1099 .ignore_then(just(Token::LParen))
1100 .ignore_then(expr.clone())
1101 .then_ignore(just(Token::RParen))
1102 .map_with_span({
1103 let src = src.clone();
1104 move |message, span: Range<usize>| Expr::Trace {
1105 message: Box::new(message),
1106 span: make_span(&src, span),
1107 }
1108 });
1109
1110 let record_field_init = ident_token_parser(src.clone())
1114 .then_ignore(just(Token::Colon))
1115 .then(expr.clone())
1116 .map_with_span({
1117 let src = src.clone();
1118 move |(name, value), span: Range<usize>| FieldInit {
1119 name,
1120 value,
1121 span: make_span(&src, span),
1122 }
1123 });
1124
1125 let record_construct = ident_token_parser(src.clone())
1126 .then_ignore(just(Token::LBrace))
1127 .then(
1128 record_field_init
1129 .separated_by(just(Token::Comma))
1130 .allow_trailing(),
1131 )
1132 .then_ignore(just(Token::RBrace))
1133 .map_with_span({
1134 let src = src.clone();
1135 move |(name, fields), span: Range<usize>| Expr::RecordConstruct {
1136 name,
1137 fields,
1138 span: make_span(&src, span),
1139 }
1140 });
1141
1142 let closure_param = ident_token_parser(src.clone())
1144 .then(just(Token::Colon).ignore_then(type_parser(src.clone())).or_not())
1145 .map_with_span({
1146 let src = src.clone();
1147 move |(name, ty), span: Range<usize>| ClosureParam {
1148 name,
1149 ty,
1150 span: make_span(&src, span),
1151 }
1152 });
1153
1154 let closure_empty = just(Token::Or)
1157 .ignore_then(expr.clone())
1158 .map_with_span({
1159 let src = src.clone();
1160 move |body, span: Range<usize>| Expr::Closure {
1161 params: vec![],
1162 body: Box::new(body),
1163 span: make_span(&src, span),
1164 }
1165 });
1166
1167 let closure_with_params = just(Token::Pipe)
1168 .ignore_then(
1169 closure_param
1170 .separated_by(just(Token::Comma))
1171 .allow_trailing(),
1172 )
1173 .then_ignore(just(Token::Pipe))
1174 .then(expr.clone())
1175 .map_with_span({
1176 let src = src.clone();
1177 move |(params, body), span: Range<usize>| Expr::Closure {
1178 params,
1179 body: Box::new(body),
1180 span: make_span(&src, span),
1181 }
1182 });
1183
1184 let closure = closure_with_params.or(closure_empty);
1185
1186 let map_entry = expr
1189 .clone()
1190 .then_ignore(just(Token::Colon))
1191 .then(expr.clone())
1192 .map_with_span({
1193 let src = src.clone();
1194 move |(key, value), span: Range<usize>| MapEntry {
1195 key,
1196 value,
1197 span: make_span(&src, span),
1198 }
1199 });
1200
1201 let map_literal = map_entry
1202 .separated_by(just(Token::Comma))
1203 .allow_trailing()
1204 .delimited_by(just(Token::LBrace), just(Token::RBrace))
1205 .map_with_span({
1206 let src = src.clone();
1207 move |entries, span: Range<usize>| Expr::Map {
1208 entries,
1209 span: make_span(&src, span),
1210 }
1211 });
1212
1213 let variant_construct = ident_token_parser(src.clone())
1215 .then_ignore(just(Token::ColonColon))
1216 .then(ident_token_parser(src.clone()))
1217 .then(
1218 expr.clone()
1219 .delimited_by(just(Token::LParen), just(Token::RParen))
1220 .or_not(),
1221 )
1222 .map_with_span({
1223 let src = src.clone();
1224 move |((enum_name, variant), payload), span: Range<usize>| Expr::VariantConstruct {
1225 enum_name,
1226 variant,
1227 payload: payload.map(Box::new),
1228 span: make_span(&src, span),
1229 }
1230 });
1231
1232 let atom = closure
1240 .or(infer_expr)
1241 .or(spawn_expr)
1242 .or(await_expr)
1243 .or(send_expr)
1244 .or(emit_expr)
1245 .or(receive_expr)
1246 .or(trace_expr)
1247 .or(match_expr)
1248 .or(self_access)
1249 .or(record_construct)
1250 .or(variant_construct)
1251 .or(call_expr)
1252 .or(map_literal)
1253 .or(list)
1254 .or(paren_or_tuple)
1255 .or(literal)
1256 .or(var)
1257 .boxed();
1258
1259 enum PostfixOp {
1262 Field(Ident),
1263 TupleIndex(usize, Range<usize>),
1264 MethodCall(Ident, Vec<Expr>, Range<usize>), }
1266
1267 let method_call = ident_token_parser(src.clone())
1269 .then(
1270 expr.clone()
1271 .separated_by(just(Token::Comma))
1272 .allow_trailing()
1273 .delimited_by(just(Token::LParen), just(Token::RParen)),
1274 )
1275 .map_with_span(|(name, args), span: Range<usize>| {
1276 PostfixOp::MethodCall(name, args, span)
1277 });
1278
1279 let postfix_op = just(Token::Dot).ignore_then(
1280 filter_map({
1282 let src = src.clone();
1283 move |span: Range<usize>, token| match token {
1284 Token::IntLit => {
1285 let text = &src[span.start..span.end];
1286 text.parse::<usize>()
1287 .map(|idx| PostfixOp::TupleIndex(idx, span.clone()))
1288 .map_err(|_| Simple::custom(span, "invalid tuple index"))
1289 }
1290 _ => Err(Simple::expected_input_found(
1291 span,
1292 vec![Some(Token::IntLit)],
1293 Some(token),
1294 )),
1295 }
1296 })
1297 .or(method_call)
1299 .or(ident_token_parser(src.clone()).map(PostfixOp::Field)),
1300 );
1301
1302 let postfix = atom
1303 .then(postfix_op.repeated())
1304 .foldl({
1305 let src = src.clone();
1306 move |object, op| match op {
1307 PostfixOp::Field(field) => {
1308 let span = make_span(&src, object.span().start..field.span.end);
1309 Expr::FieldAccess {
1310 object: Box::new(object),
1311 field,
1312 span,
1313 }
1314 }
1315 PostfixOp::TupleIndex(index, idx_span) => {
1316 let span = make_span(&src, object.span().start..idx_span.end);
1317 Expr::TupleIndex {
1318 tuple: Box::new(object),
1319 index,
1320 span,
1321 }
1322 }
1323 PostfixOp::MethodCall(method, args, call_span) => {
1324 if let Expr::Var { name: tool, .. } = &object {
1327 let span = make_span(&src, object.span().start..call_span.end);
1328 Expr::ToolCall {
1329 tool: tool.clone(),
1330 function: method,
1331 args,
1332 span,
1333 }
1334 } else {
1335 let span = make_span(&src, object.span().start..call_span.end);
1338 Expr::FieldAccess {
1339 object: Box::new(object),
1340 field: method,
1341 span,
1342 }
1343 }
1344 }
1345 }
1346 })
1347 .boxed();
1348
1349 let unary = just(Token::Minus)
1351 .to(UnaryOp::Neg)
1352 .or(just(Token::Bang).to(UnaryOp::Not))
1353 .repeated()
1354 .then(postfix.clone())
1355 .foldr(|op, operand| {
1356 let span = operand.span().clone();
1357 Expr::Unary {
1358 op,
1359 operand: Box::new(operand),
1360 span,
1361 }
1362 })
1363 .boxed();
1364
1365 let try_expr = just(Token::KwTry)
1368 .ignore_then(postfix.clone())
1369 .map_with_span({
1370 let src = src.clone();
1371 move |inner, span: Range<usize>| Expr::Try {
1372 expr: Box::new(inner),
1373 span: make_span(&src, span),
1374 }
1375 })
1376 .boxed();
1377
1378 let fail_expr = just(Token::KwFail)
1381 .ignore_then(postfix.clone())
1382 .map_with_span({
1383 let src = src.clone();
1384 move |error, span: Range<usize>| Expr::Fail {
1385 error: Box::new(error),
1386 span: make_span(&src, span),
1387 }
1388 })
1389 .boxed();
1390
1391 let retry_delay = just(Token::Comma)
1396 .ignore_then(just(Token::KwDelay))
1397 .ignore_then(just(Token::Colon))
1398 .ignore_then(postfix.clone());
1399
1400 let retry_on = just(Token::Comma)
1401 .ignore_then(just(Token::KwOn))
1402 .ignore_then(just(Token::Colon))
1403 .ignore_then(
1404 postfix
1405 .clone()
1406 .separated_by(just(Token::Comma))
1407 .allow_trailing()
1408 .delimited_by(just(Token::LBracket), just(Token::RBracket)),
1409 );
1410
1411 let retry_expr = just(Token::KwRetry)
1412 .ignore_then(just(Token::LParen))
1413 .ignore_then(postfix.clone())
1414 .then(retry_delay.or_not())
1415 .then(retry_on.or_not())
1416 .then_ignore(just(Token::RParen))
1417 .then(
1418 expr.clone()
1419 .delimited_by(just(Token::LBrace), just(Token::RBrace)),
1420 )
1421 .map_with_span({
1422 let src = src.clone();
1423 move |(((count, delay), on_errors), body), span: Range<usize>| Expr::Retry {
1424 count: Box::new(count),
1425 delay: delay.map(Box::new),
1426 on_errors,
1427 body: Box::new(body),
1428 span: make_span(&src, span),
1429 }
1430 })
1431 .boxed();
1432
1433 let unary = retry_expr.or(fail_expr).or(try_expr).or(unary).boxed();
1435
1436 let mul_div_op = just(Token::Star)
1439 .to(BinOp::Mul)
1440 .or(just(Token::Slash).to(BinOp::Div))
1441 .or(just(Token::Percent).to(BinOp::Rem));
1442
1443 let mul_div = unary
1444 .clone()
1445 .then(mul_div_op.then(unary.clone()).repeated())
1446 .foldl({
1447 let src = src.clone();
1448 move |left, (op, right)| {
1449 let span = make_span(&src, left.span().start..right.span().end);
1450 Expr::Binary {
1451 op,
1452 left: Box::new(left),
1453 right: Box::new(right),
1454 span,
1455 }
1456 }
1457 })
1458 .boxed();
1459
1460 let add_sub_op = just(Token::Plus)
1462 .to(BinOp::Add)
1463 .or(just(Token::Minus).to(BinOp::Sub));
1464
1465 let add_sub = mul_div
1466 .clone()
1467 .then(add_sub_op.then(mul_div).repeated())
1468 .foldl({
1469 let src = src.clone();
1470 move |left, (op, right)| {
1471 let span = make_span(&src, left.span().start..right.span().end);
1472 Expr::Binary {
1473 op,
1474 left: Box::new(left),
1475 right: Box::new(right),
1476 span,
1477 }
1478 }
1479 })
1480 .boxed();
1481
1482 let concat_op = just(Token::PlusPlus).to(BinOp::Concat);
1484
1485 let concat = add_sub
1486 .clone()
1487 .then(concat_op.then(add_sub).repeated())
1488 .foldl({
1489 let src = src.clone();
1490 move |left, (op, right)| {
1491 let span = make_span(&src, left.span().start..right.span().end);
1492 Expr::Binary {
1493 op,
1494 left: Box::new(left),
1495 right: Box::new(right),
1496 span,
1497 }
1498 }
1499 })
1500 .boxed();
1501
1502 let cmp_op = choice((
1504 just(Token::Le).to(BinOp::Le),
1505 just(Token::Ge).to(BinOp::Ge),
1506 just(Token::Lt).to(BinOp::Lt),
1507 just(Token::Gt).to(BinOp::Gt),
1508 ));
1509
1510 let comparison = concat
1511 .clone()
1512 .then(cmp_op.then(concat).repeated())
1513 .foldl({
1514 let src = src.clone();
1515 move |left, (op, right)| {
1516 let span = make_span(&src, left.span().start..right.span().end);
1517 Expr::Binary {
1518 op,
1519 left: Box::new(left),
1520 right: Box::new(right),
1521 span,
1522 }
1523 }
1524 })
1525 .boxed();
1526
1527 let eq_op = just(Token::EqEq)
1529 .to(BinOp::Eq)
1530 .or(just(Token::Ne).to(BinOp::Ne));
1531
1532 let equality = comparison
1533 .clone()
1534 .then(eq_op.then(comparison).repeated())
1535 .foldl({
1536 let src = src.clone();
1537 move |left, (op, right)| {
1538 let span = make_span(&src, left.span().start..right.span().end);
1539 Expr::Binary {
1540 op,
1541 left: Box::new(left),
1542 right: Box::new(right),
1543 span,
1544 }
1545 }
1546 })
1547 .boxed();
1548
1549 let and_op = just(Token::And).to(BinOp::And);
1551
1552 let and = equality
1553 .clone()
1554 .then(and_op.then(equality).repeated())
1555 .foldl({
1556 let src = src.clone();
1557 move |left, (op, right)| {
1558 let span = make_span(&src, left.span().start..right.span().end);
1559 Expr::Binary {
1560 op,
1561 left: Box::new(left),
1562 right: Box::new(right),
1563 span,
1564 }
1565 }
1566 })
1567 .boxed();
1568
1569 let or_op = just(Token::Or).to(BinOp::Or);
1571
1572 let or_expr = and.clone().then(or_op.then(and).repeated()).foldl({
1573 let src = src.clone();
1574 move |left, (op, right)| {
1575 let span = make_span(&src, left.span().start..right.span().end);
1576 Expr::Binary {
1577 op,
1578 left: Box::new(left),
1579 right: Box::new(right),
1580 span,
1581 }
1582 }
1583 });
1584
1585 let catch_recovery = just(Token::KwCatch)
1588 .ignore_then(
1589 ident_token_parser(src.clone())
1590 .delimited_by(just(Token::LParen), just(Token::RParen))
1591 .or_not(),
1592 )
1593 .then(
1594 expr.clone()
1595 .delimited_by(just(Token::LBrace), just(Token::RBrace)),
1596 );
1597
1598 or_expr.then(catch_recovery.or_not()).map_with_span({
1599 let src = src.clone();
1600 move |(inner, catch_opt), span: Range<usize>| match catch_opt {
1601 Some((error_bind, recovery)) => Expr::Catch {
1602 expr: Box::new(inner),
1603 error_bind,
1604 recovery: Box::new(recovery),
1605 span: make_span(&src, span),
1606 },
1607 None => inner,
1608 }
1609 })
1610 })
1611 .boxed()
1612}
1613
1614fn make_span(source: &Arc<str>, range: Range<usize>) -> Span {
1620 Span::new(range.start, range.end, Arc::clone(source))
1621}
1622
1623fn ident_token_parser(source: Arc<str>) -> impl Parser<Token, Ident, Error = ParseError> + Clone {
1625 filter_map(move |span: Range<usize>, token| match token {
1626 Token::Ident => {
1627 let text = &source[span.start..span.end];
1628 Ok(Ident::new(text.to_string(), make_span(&source, span)))
1629 }
1630 _ => Err(Simple::expected_input_found(
1631 span,
1632 vec![Some(Token::Ident)],
1633 Some(token),
1634 )),
1635 })
1636}
1637
1638fn var_parser(source: Arc<str>) -> impl Parser<Token, Expr, Error = ParseError> + Clone {
1640 ident_token_parser(source.clone()).map_with_span(move |name, span: Range<usize>| Expr::Var {
1641 name,
1642 span: make_span(&source, span),
1643 })
1644}
1645
1646fn type_parser(source: Arc<str>) -> impl Parser<Token, TypeExpr, Error = ParseError> + Clone {
1648 recursive(move |ty| {
1649 let src = source.clone();
1650
1651 let primitive = choice((
1652 just(Token::TyInt).to(TypeExpr::Int),
1653 just(Token::TyFloat).to(TypeExpr::Float),
1654 just(Token::TyBool).to(TypeExpr::Bool),
1655 just(Token::TyString).to(TypeExpr::String),
1656 just(Token::TyUnit).to(TypeExpr::Unit),
1657 ));
1658
1659 let list_ty = just(Token::TyList)
1660 .ignore_then(just(Token::Lt))
1661 .ignore_then(ty.clone())
1662 .then_ignore(just(Token::Gt))
1663 .map(|inner| TypeExpr::List(Box::new(inner)));
1664
1665 let option_ty = just(Token::TyOption)
1666 .ignore_then(just(Token::Lt))
1667 .ignore_then(ty.clone())
1668 .then_ignore(just(Token::Gt))
1669 .map(|inner| TypeExpr::Option(Box::new(inner)));
1670
1671 let inferred_ty = just(Token::TyInferred)
1672 .ignore_then(just(Token::Lt))
1673 .ignore_then(ty.clone())
1674 .then_ignore(just(Token::Gt))
1675 .map(|inner| TypeExpr::Inferred(Box::new(inner)));
1676
1677 let agent_ty = just(Token::TyAgent)
1678 .ignore_then(just(Token::Lt))
1679 .ignore_then(ident_token_parser(src.clone()))
1680 .then_ignore(just(Token::Gt))
1681 .map(TypeExpr::Agent);
1682
1683 let named_ty = ident_token_parser(src.clone()).map(TypeExpr::Named);
1684
1685 let fn_ty = just(Token::TyFn)
1687 .ignore_then(
1688 ty.clone()
1689 .separated_by(just(Token::Comma))
1690 .allow_trailing()
1691 .delimited_by(just(Token::LParen), just(Token::RParen)),
1692 )
1693 .then_ignore(just(Token::Arrow))
1694 .then(ty.clone())
1695 .map(|(params, ret)| TypeExpr::Fn(params, Box::new(ret)));
1696
1697 let map_ty = just(Token::TyMap)
1699 .ignore_then(just(Token::Lt))
1700 .ignore_then(ty.clone())
1701 .then_ignore(just(Token::Comma))
1702 .then(ty.clone())
1703 .then_ignore(just(Token::Gt))
1704 .map(|(k, v)| TypeExpr::Map(Box::new(k), Box::new(v)));
1705
1706 let result_ty = just(Token::TyResult)
1708 .ignore_then(just(Token::Lt))
1709 .ignore_then(ty.clone())
1710 .then_ignore(just(Token::Comma))
1711 .then(ty.clone())
1712 .then_ignore(just(Token::Gt))
1713 .map(|(ok, err)| TypeExpr::Result(Box::new(ok), Box::new(err)));
1714
1715 let tuple_ty = ty
1717 .clone()
1718 .separated_by(just(Token::Comma))
1719 .at_least(2)
1720 .allow_trailing()
1721 .delimited_by(just(Token::LParen), just(Token::RParen))
1722 .map(TypeExpr::Tuple);
1723
1724 primitive
1725 .or(list_ty)
1726 .or(option_ty)
1727 .or(inferred_ty)
1728 .or(agent_ty)
1729 .or(fn_ty)
1730 .or(map_ty)
1731 .or(result_ty)
1732 .or(tuple_ty)
1733 .or(named_ty)
1734 })
1735}
1736
1737fn for_pattern_parser(source: Arc<str>) -> impl Parser<Token, Pattern, Error = ParseError> + Clone {
1740 recursive(move |pattern| {
1741 let src = source.clone();
1742 let src2 = source.clone();
1743
1744 let binding = ident_token_parser(src.clone()).map_with_span({
1746 let src = src.clone();
1747 move |name, span: Range<usize>| Pattern::Binding {
1748 name,
1749 span: make_span(&src, span),
1750 }
1751 });
1752
1753 let tuple_pattern = pattern
1755 .clone()
1756 .separated_by(just(Token::Comma))
1757 .at_least(2)
1758 .allow_trailing()
1759 .delimited_by(just(Token::LParen), just(Token::RParen))
1760 .map_with_span({
1761 let src = src2.clone();
1762 move |elements, span: Range<usize>| Pattern::Tuple {
1763 elements,
1764 span: make_span(&src, span),
1765 }
1766 });
1767
1768 tuple_pattern.or(binding)
1769 })
1770}
1771
1772fn pattern_parser(source: Arc<str>) -> impl Parser<Token, Pattern, Error = ParseError> + Clone {
1774 recursive(move |pattern| {
1775 let src = source.clone();
1776 let src2 = source.clone();
1777 let src3 = source.clone();
1778 let src4 = source.clone();
1779 let src5 = source.clone();
1780
1781 let wildcard = filter_map({
1783 let src = src.clone();
1784 move |span: Range<usize>, token| match &token {
1785 Token::Ident if src[span.start..span.end].eq("_") => Ok(()),
1786 _ => Err(Simple::expected_input_found(span, vec![], Some(token))),
1787 }
1788 })
1789 .map_with_span(move |_, span: Range<usize>| Pattern::Wildcard {
1790 span: make_span(&src2, span),
1791 });
1792
1793 let lit_int = filter_map({
1795 let src = src3.clone();
1796 move |span: Range<usize>, token| match token {
1797 Token::IntLit => {
1798 let text = &src[span.start..span.end];
1799 text.parse::<i64>()
1800 .map(Literal::Int)
1801 .map_err(|_| Simple::custom(span, "invalid integer literal"))
1802 }
1803 _ => Err(Simple::expected_input_found(
1804 span,
1805 vec![Some(Token::IntLit)],
1806 Some(token),
1807 )),
1808 }
1809 })
1810 .map_with_span({
1811 let src = src3.clone();
1812 move |value, span: Range<usize>| Pattern::Literal {
1813 value,
1814 span: make_span(&src, span),
1815 }
1816 });
1817
1818 let lit_bool = just(Token::KwTrue)
1819 .to(Literal::Bool(true))
1820 .or(just(Token::KwFalse).to(Literal::Bool(false)))
1821 .map_with_span({
1822 let src = src3.clone();
1823 move |value, span: Range<usize>| Pattern::Literal {
1824 value,
1825 span: make_span(&src, span),
1826 }
1827 });
1828
1829 let tuple_pattern = pattern
1831 .clone()
1832 .separated_by(just(Token::Comma))
1833 .at_least(2)
1834 .allow_trailing()
1835 .delimited_by(just(Token::LParen), just(Token::RParen))
1836 .map_with_span({
1837 let src = src5.clone();
1838 move |elements, span: Range<usize>| Pattern::Tuple {
1839 elements,
1840 span: make_span(&src, span),
1841 }
1842 });
1843
1844 let qualified_variant_with_payload = ident_token_parser(src4.clone())
1847 .then_ignore(just(Token::ColonColon))
1848 .then(ident_token_parser(src4.clone()))
1849 .then(
1850 pattern
1851 .clone()
1852 .delimited_by(just(Token::LParen), just(Token::RParen))
1853 .or_not(),
1854 )
1855 .map_with_span({
1856 let src = src4.clone();
1857 move |((enum_name, variant), payload), span: Range<usize>| Pattern::Variant {
1858 enum_name: Some(enum_name),
1859 variant,
1860 payload: payload.map(Box::new),
1861 span: make_span(&src, span),
1862 }
1863 });
1864
1865 let unqualified_with_payload = ident_token_parser(src4.clone())
1867 .then(
1868 pattern
1869 .clone()
1870 .delimited_by(just(Token::LParen), just(Token::RParen))
1871 .or_not(),
1872 )
1873 .map_with_span({
1874 let src = src4.clone();
1875 move |(name, payload), span: Range<usize>| {
1876 if name.name.chars().next().is_some_and(|c| c.is_uppercase()) || payload.is_some() {
1879 Pattern::Variant {
1880 enum_name: None,
1881 variant: name,
1882 payload: payload.map(Box::new),
1883 span: make_span(&src, span),
1884 }
1885 } else {
1886 Pattern::Binding {
1887 name,
1888 span: make_span(&src, span),
1889 }
1890 }
1891 }
1892 });
1893
1894 wildcard
1896 .or(tuple_pattern)
1897 .or(qualified_variant_with_payload)
1898 .or(lit_int)
1899 .or(lit_bool)
1900 .or(unqualified_with_payload)
1901 })
1902}
1903
1904fn literal_parser(source: Arc<str>) -> impl Parser<Token, Expr, Error = ParseError> + Clone {
1906 let src = source.clone();
1907 let src2 = source.clone();
1908 let src3 = source.clone();
1909 let src4 = source.clone();
1910 let src5 = source.clone();
1911
1912 let int_lit = filter_map(move |span: Range<usize>, token| match token {
1913 Token::IntLit => {
1914 let text = &src[span.start..span.end];
1915 text.parse::<i64>()
1916 .map(Literal::Int)
1917 .map_err(|_| Simple::custom(span, "invalid integer literal"))
1918 }
1919 _ => Err(Simple::expected_input_found(
1920 span,
1921 vec![Some(Token::IntLit)],
1922 Some(token),
1923 )),
1924 })
1925 .map_with_span(move |value, span: Range<usize>| Expr::Literal {
1926 value,
1927 span: make_span(&src2, span),
1928 });
1929
1930 let float_lit = filter_map(move |span: Range<usize>, token| match token {
1931 Token::FloatLit => {
1932 let text = &src3[span.start..span.end];
1933 text.parse::<f64>()
1934 .map(Literal::Float)
1935 .map_err(|_| Simple::custom(span, "invalid float literal"))
1936 }
1937 _ => Err(Simple::expected_input_found(
1938 span,
1939 vec![Some(Token::FloatLit)],
1940 Some(token),
1941 )),
1942 })
1943 .map_with_span(move |value, span: Range<usize>| Expr::Literal {
1944 value,
1945 span: make_span(&src4, span),
1946 });
1947
1948 let src6 = source.clone();
1949 let string_lit = filter_map(move |span: Range<usize>, token| match token {
1950 Token::StringLit => {
1951 let text = &src5[span.start..span.end];
1952 let inner = &text[1..text.len() - 1];
1953 let parts = parse_string_template(inner, &make_span(&src5, span.clone()));
1954 Ok(parts)
1955 }
1956 _ => Err(Simple::expected_input_found(
1957 span,
1958 vec![Some(Token::StringLit)],
1959 Some(token),
1960 )),
1961 })
1962 .map_with_span(move |parts, span: Range<usize>| {
1963 let span = make_span(&src6, span);
1964 if parts.len() == 1 {
1966 if let StringPart::Literal(s) = &parts[0] {
1967 return Expr::Literal {
1968 value: Literal::String(s.clone()),
1969 span,
1970 };
1971 }
1972 }
1973 Expr::StringInterp {
1975 template: StringTemplate {
1976 parts,
1977 span: span.clone(),
1978 },
1979 span,
1980 }
1981 });
1982
1983 let bool_lit = just(Token::KwTrue)
1984 .to(Literal::Bool(true))
1985 .or(just(Token::KwFalse).to(Literal::Bool(false)))
1986 .map_with_span(move |value, _span: Range<usize>| Expr::Literal {
1987 value,
1988 span: Span::dummy(), });
1990
1991 int_lit.or(float_lit).or(string_lit).or(bool_lit)
1992}
1993
1994fn string_template_parser(
1996 source: Arc<str>,
1997) -> impl Parser<Token, StringTemplate, Error = ParseError> + Clone {
1998 filter_map(move |span: Range<usize>, token| match token {
1999 Token::StringLit => {
2000 let text = &source[span.start..span.end];
2001 let inner = &text[1..text.len() - 1];
2002 let parts = parse_string_template(inner, &make_span(&source, span.clone()));
2003 Ok(StringTemplate {
2004 parts,
2005 span: make_span(&source, span),
2006 })
2007 }
2008 _ => Err(Simple::expected_input_found(
2009 span,
2010 vec![Some(Token::StringLit)],
2011 Some(token),
2012 )),
2013 })
2014}
2015
2016fn parse_string_template(s: &str, span: &Span) -> Vec<StringPart> {
2019 let mut parts = Vec::new();
2020 let mut current = String::new();
2021 let mut chars = s.chars().peekable();
2022
2023 while let Some(ch) = chars.next() {
2024 if ch == '{' {
2025 if !current.is_empty() {
2026 parts.push(StringPart::Literal(std::mem::take(&mut current)));
2027 }
2028
2029 let mut expr_str = String::new();
2031 while let Some(&c) = chars.peek() {
2032 if c == '}' {
2033 chars.next();
2034 break;
2035 }
2036 expr_str.push(c);
2037 chars.next();
2038 }
2039
2040 if !expr_str.is_empty() {
2041 let interp_expr = parse_interp_expr(&expr_str, span);
2042 parts.push(StringPart::Interpolation(interp_expr));
2043 }
2044 } else if ch == '\\' {
2045 if let Some(escaped) = chars.next() {
2046 current.push(match escaped {
2047 'n' => '\n',
2048 't' => '\t',
2049 'r' => '\r',
2050 '\\' => '\\',
2051 '"' => '"',
2052 '{' => '{',
2053 '}' => '}',
2054 other => other,
2055 });
2056 }
2057 } else {
2058 current.push(ch);
2059 }
2060 }
2061
2062 if !current.is_empty() {
2063 parts.push(StringPart::Literal(current));
2064 }
2065
2066 if parts.is_empty() {
2067 parts.push(StringPart::Literal(String::new()));
2068 }
2069
2070 parts
2071}
2072
2073fn parse_interp_expr(s: &str, span: &Span) -> InterpExpr {
2075 let segments: Vec<&str> = s.split('.').collect();
2076
2077 let mut expr = InterpExpr::Ident(Ident::new(segments[0].to_string(), span.clone()));
2079
2080 for segment in &segments[1..] {
2082 if let Ok(index) = segment.parse::<usize>() {
2083 expr = InterpExpr::TupleIndex {
2085 base: Box::new(expr),
2086 index,
2087 span: span.clone(),
2088 };
2089 } else {
2090 expr = InterpExpr::FieldAccess {
2092 base: Box::new(expr),
2093 field: Ident::new(segment.to_string(), span.clone()),
2094 span: span.clone(),
2095 };
2096 }
2097 }
2098
2099 expr
2100}
2101
2102#[cfg(test)]
2107mod tests {
2108 use super::*;
2109 use crate::lex;
2110
2111 fn parse_str(source: &str) -> (Option<Program>, Vec<ParseError>) {
2112 let lex_result = lex(source).expect("lexing should succeed");
2113 let source_arc: Arc<str> = Arc::from(source);
2114 parse(lex_result.tokens(), source_arc)
2115 }
2116
2117 #[test]
2118 fn parse_minimal_program() {
2119 let source = r#"
2120 agent Main {
2121 on start {
2122 emit(42);
2123 }
2124 }
2125 run Main;
2126 "#;
2127
2128 let (prog, errors) = parse_str(source);
2129 assert!(errors.is_empty(), "errors: {errors:?}");
2130 let prog = prog.expect("should parse");
2131
2132 assert_eq!(prog.agents.len(), 1);
2133 assert_eq!(prog.agents[0].name.name, "Main");
2134 assert_eq!(prog.run_agent.as_ref().unwrap().name, "Main");
2135 }
2136
2137 #[test]
2138 fn parse_agent_with_beliefs() {
2139 let source = r#"
2140 agent Researcher {
2141 topic: String
2142 max_words: Int
2143
2144 on start {
2145 emit(self.topic);
2146 }
2147 }
2148 run Researcher;
2149 "#;
2150
2151 let (prog, errors) = parse_str(source);
2152 assert!(errors.is_empty(), "errors: {errors:?}");
2153 let prog = prog.expect("should parse");
2154
2155 assert_eq!(prog.agents[0].beliefs.len(), 2);
2156 assert_eq!(prog.agents[0].beliefs[0].name.name, "topic");
2157 assert_eq!(prog.agents[0].beliefs[1].name.name, "max_words");
2158 }
2159
2160 #[test]
2161 fn parse_multiple_handlers() {
2162 let source = r#"
2163 agent Worker {
2164 on start {
2165 print("started");
2166 }
2167
2168 on message(msg: String) {
2169 print(msg);
2170 }
2171
2172 on stop {
2173 print("stopped");
2174 }
2175 }
2176 run Worker;
2177 "#;
2178
2179 let (prog, errors) = parse_str(source);
2180 assert!(errors.is_empty(), "errors: {errors:?}");
2181 let prog = prog.expect("should parse");
2182
2183 assert_eq!(prog.agents[0].handlers.len(), 3);
2184 assert_eq!(prog.agents[0].handlers[0].event, EventKind::Start);
2185 assert!(matches!(
2186 prog.agents[0].handlers[1].event,
2187 EventKind::Message { .. }
2188 ));
2189 assert_eq!(prog.agents[0].handlers[2].event, EventKind::Stop);
2190 }
2191
2192 #[test]
2193 fn parse_function() {
2194 let source = r#"
2195 fn greet(name: String) -> String {
2196 return "Hello, " ++ name;
2197 }
2198
2199 agent Main {
2200 on start {
2201 emit(greet("World"));
2202 }
2203 }
2204 run Main;
2205 "#;
2206
2207 let (prog, errors) = parse_str(source);
2208 assert!(errors.is_empty(), "errors: {errors:?}");
2209 let prog = prog.expect("should parse");
2210
2211 assert_eq!(prog.functions.len(), 1);
2212 assert_eq!(prog.functions[0].name.name, "greet");
2213 assert_eq!(prog.functions[0].params.len(), 1);
2214 }
2215
2216 #[test]
2217 fn parse_let_statement() {
2218 let source = r#"
2219 agent Main {
2220 on start {
2221 let x: Int = 42;
2222 let y = "hello";
2223 emit(x);
2224 }
2225 }
2226 run Main;
2227 "#;
2228
2229 let (prog, errors) = parse_str(source);
2230 assert!(errors.is_empty(), "errors: {errors:?}");
2231 let prog = prog.expect("should parse");
2232
2233 let stmts = &prog.agents[0].handlers[0].body.stmts;
2234 assert!(matches!(stmts[0], Stmt::Let { .. }));
2235 assert!(matches!(stmts[1], Stmt::Let { .. }));
2236 }
2237
2238 #[test]
2239 fn parse_if_statement() {
2240 let source = r#"
2241 agent Main {
2242 on start {
2243 if true {
2244 emit(1);
2245 } else {
2246 emit(2);
2247 }
2248 }
2249 }
2250 run Main;
2251 "#;
2252
2253 let (prog, errors) = parse_str(source);
2254 assert!(errors.is_empty(), "errors: {errors:?}");
2255 let prog = prog.expect("should parse");
2256
2257 let stmts = &prog.agents[0].handlers[0].body.stmts;
2258 assert!(matches!(stmts[0], Stmt::If { .. }));
2259 }
2260
2261 #[test]
2262 fn parse_for_loop() {
2263 let source = r#"
2264 agent Main {
2265 on start {
2266 for x in [1, 2, 3] {
2267 print(x);
2268 }
2269 emit(0);
2270 }
2271 }
2272 run Main;
2273 "#;
2274
2275 let (prog, errors) = parse_str(source);
2276 assert!(errors.is_empty(), "errors: {errors:?}");
2277 let prog = prog.expect("should parse");
2278
2279 let stmts = &prog.agents[0].handlers[0].body.stmts;
2280 assert!(matches!(stmts[0], Stmt::For { .. }));
2281 }
2282
2283 #[test]
2284 fn parse_spawn_await() {
2285 let source = r#"
2286 agent Worker {
2287 name: String
2288
2289 on start {
2290 emit(self.name);
2291 }
2292 }
2293
2294 agent Main {
2295 on start {
2296 let w = spawn Worker { name: "test" };
2297 let result = await w;
2298 emit(result);
2299 }
2300 }
2301 run Main;
2302 "#;
2303
2304 let (prog, errors) = parse_str(source);
2305 assert!(errors.is_empty(), "errors: {errors:?}");
2306 prog.expect("should parse");
2307 }
2308
2309 #[test]
2310 fn parse_await_with_timeout() {
2311 let source = r#"
2312 agent Worker {
2313 on start {
2314 emit("done");
2315 }
2316 }
2317
2318 agent Main {
2319 on start {
2320 let w = spawn Worker {};
2321 let result = await w timeout(5000);
2322 emit(result);
2323 }
2324 }
2325 run Main;
2326 "#;
2327
2328 let (prog, errors) = parse_str(source);
2329 assert!(errors.is_empty(), "errors: {errors:?}");
2330 let prog = prog.expect("should parse");
2331
2332 let main = &prog.agents[1];
2334 let stmts = &main.handlers[0].body.stmts;
2335 if let Stmt::Let { value, .. } = &stmts[1] {
2338 if let Expr::Await { timeout, .. } = value {
2339 assert!(timeout.is_some(), "timeout should be present");
2340 } else {
2341 panic!("expected Await expression");
2342 }
2343 } else {
2344 panic!("expected Let statement with value");
2345 }
2346 }
2347
2348 #[test]
2349 fn parse_infer() {
2350 let source = r#"
2351 agent Main {
2352 on start {
2353 let result = infer("What is 2+2?");
2354 emit(result);
2355 }
2356 }
2357 run Main;
2358 "#;
2359
2360 let (prog, errors) = parse_str(source);
2361 assert!(errors.is_empty(), "errors: {errors:?}");
2362 prog.expect("should parse");
2363 }
2364
2365 #[test]
2366 fn parse_binary_precedence() {
2367 let source = r#"
2368 agent Main {
2369 on start {
2370 let x = 2 + 3 * 4;
2371 emit(x);
2372 }
2373 }
2374 run Main;
2375 "#;
2376
2377 let (prog, errors) = parse_str(source);
2378 assert!(errors.is_empty(), "errors: {errors:?}");
2379 let prog = prog.expect("should parse");
2380
2381 let stmts = &prog.agents[0].handlers[0].body.stmts;
2382 if let Stmt::Let { value, .. } = &stmts[0] {
2383 if let Expr::Binary { op, .. } = value {
2384 assert_eq!(*op, BinOp::Add);
2385 } else {
2386 panic!("expected binary expression");
2387 }
2388 }
2389 }
2390
2391 #[test]
2392 fn parse_string_interpolation() {
2393 let source = r#"
2394 agent Main {
2395 on start {
2396 let name = "World";
2397 let msg = infer("Greet {name}");
2398 emit(msg);
2399 }
2400 }
2401 run Main;
2402 "#;
2403
2404 let (prog, errors) = parse_str(source);
2405 assert!(errors.is_empty(), "errors: {errors:?}");
2406 let prog = prog.expect("should parse");
2407
2408 let stmts = &prog.agents[0].handlers[0].body.stmts;
2409 if let Stmt::Let { value, .. } = &stmts[1] {
2410 if let Expr::Infer { template, .. } = value {
2411 assert!(template.has_interpolations());
2412 } else {
2413 panic!("expected infer expression");
2414 }
2415 }
2416 }
2417
2418 #[test]
2423 fn recover_from_malformed_agent_continues_to_next() {
2424 let source = r#"
2426 agent Broken {
2427 x:
2428 }
2429
2430 agent Main {
2431 on start {
2432 emit(42);
2433 }
2434 }
2435 run Main;
2436 "#;
2437
2438 let (prog, errors) = parse_str(source);
2439 assert!(!errors.is_empty(), "should have parse errors");
2441 let prog = prog.expect("should produce partial AST");
2443 assert!(prog.agents.iter().any(|a| a.name.name == "Main"));
2444 }
2445
2446 #[test]
2447 fn recover_from_mismatched_braces_in_block() {
2448 let source = r#"
2449 agent Main {
2450 on start {
2451 let x = [1, 2, 3;
2452 emit(42);
2453 }
2454 }
2455 run Main;
2456 "#;
2457
2458 let (prog, errors) = parse_str(source);
2459 assert!(!errors.is_empty(), "should have parse errors");
2461 assert!(prog.is_some(), "should produce partial AST despite errors");
2462 }
2463
2464 #[test]
2465 fn parse_mod_declaration() {
2466 let source = r#"
2467 mod agents;
2468 pub mod utils;
2469
2470 agent Main {
2471 on start {
2472 emit(42);
2473 }
2474 }
2475 run Main;
2476 "#;
2477
2478 let (prog, errors) = parse_str(source);
2479 assert!(errors.is_empty(), "errors: {errors:?}");
2480 let prog = prog.expect("should parse");
2481
2482 assert_eq!(prog.mod_decls.len(), 2);
2483 assert!(!prog.mod_decls[0].is_pub);
2484 assert_eq!(prog.mod_decls[0].name.name, "agents");
2485 assert!(prog.mod_decls[1].is_pub);
2486 assert_eq!(prog.mod_decls[1].name.name, "utils");
2487 }
2488
2489 #[test]
2490 fn parse_use_simple() {
2491 let source = r#"
2492 use agents::Researcher;
2493
2494 agent Main {
2495 on start {
2496 emit(42);
2497 }
2498 }
2499 run Main;
2500 "#;
2501
2502 let (prog, errors) = parse_str(source);
2503 assert!(errors.is_empty(), "errors: {errors:?}");
2504 let prog = prog.expect("should parse");
2505
2506 assert_eq!(prog.use_decls.len(), 1);
2507 assert!(!prog.use_decls[0].is_pub);
2508 assert_eq!(prog.use_decls[0].path.len(), 2);
2509 assert_eq!(prog.use_decls[0].path[0].name, "agents");
2510 assert_eq!(prog.use_decls[0].path[1].name, "Researcher");
2511 assert!(matches!(prog.use_decls[0].kind, UseKind::Simple(None)));
2512 }
2513
2514 #[test]
2515 fn parse_use_with_alias() {
2516 let source = r#"
2517 use agents::Researcher as R;
2518
2519 agent Main {
2520 on start {
2521 emit(42);
2522 }
2523 }
2524 run Main;
2525 "#;
2526
2527 let (prog, errors) = parse_str(source);
2528 assert!(errors.is_empty(), "errors: {errors:?}");
2529 let prog = prog.expect("should parse");
2530
2531 assert_eq!(prog.use_decls.len(), 1);
2532 if let UseKind::Simple(Some(alias)) = &prog.use_decls[0].kind {
2533 assert_eq!(alias.name, "R");
2534 } else {
2535 panic!("expected Simple with alias");
2536 }
2537 }
2538
2539 #[test]
2540 fn parse_pub_agent() {
2541 let source = r#"
2542 pub agent Worker {
2543 on start {
2544 emit(42);
2545 }
2546 }
2547
2548 agent Main {
2549 on start {
2550 emit(0);
2551 }
2552 }
2553 run Main;
2554 "#;
2555
2556 let (prog, errors) = parse_str(source);
2557 assert!(errors.is_empty(), "errors: {errors:?}");
2558 let prog = prog.expect("should parse");
2559
2560 assert_eq!(prog.agents.len(), 2);
2561 assert!(prog.agents[0].is_pub);
2562 assert_eq!(prog.agents[0].name.name, "Worker");
2563 assert!(!prog.agents[1].is_pub);
2564 }
2565
2566 #[test]
2567 fn parse_pub_function() {
2568 let source = r#"
2569 pub fn helper(x: Int) -> Int {
2570 return x;
2571 }
2572
2573 agent Main {
2574 on start {
2575 emit(helper(42));
2576 }
2577 }
2578 run Main;
2579 "#;
2580
2581 let (prog, errors) = parse_str(source);
2582 assert!(errors.is_empty(), "errors: {errors:?}");
2583 let prog = prog.expect("should parse");
2584
2585 assert_eq!(prog.functions.len(), 1);
2586 assert!(prog.functions[0].is_pub);
2587 assert_eq!(prog.functions[0].name.name, "helper");
2588 }
2589
2590 #[test]
2591 fn parse_library_no_run() {
2592 let source = r#"
2594 pub agent Worker {
2595 on start {
2596 emit(42);
2597 }
2598 }
2599
2600 pub fn helper(x: Int) -> Int {
2601 return x;
2602 }
2603 "#;
2604
2605 let (prog, errors) = parse_str(source);
2606 assert!(errors.is_empty(), "errors: {errors:?}");
2607 let prog = prog.expect("should parse");
2608
2609 assert!(prog.run_agent.is_none());
2610 assert_eq!(prog.agents.len(), 1);
2611 assert_eq!(prog.functions.len(), 1);
2612 }
2613
2614 #[test]
2615 fn recover_multiple_errors_reported() {
2616 let source = r#"
2618 agent A {
2619 x:
2620 }
2621
2622 agent Main {
2623 on start {
2624 emit(42);
2625 }
2626 }
2627 run Main;
2628 "#;
2629
2630 let (prog, errors) = parse_str(source);
2631 if errors.is_empty() {
2635 let prog = prog.expect("should have AST with recovery");
2637 assert!(prog.agents.iter().any(|a| a.name.name == "Main"));
2638 }
2639 }
2641
2642 #[test]
2643 fn parse_record_declaration() {
2644 let source = r#"
2645 record Point {
2646 x: Int,
2647 y: Int,
2648 }
2649
2650 agent Main {
2651 on start {
2652 emit(0);
2653 }
2654 }
2655 run Main;
2656 "#;
2657
2658 let (prog, errors) = parse_str(source);
2659 assert!(errors.is_empty(), "errors: {errors:?}");
2660 let prog = prog.expect("should parse");
2661
2662 assert_eq!(prog.records.len(), 1);
2663 assert!(!prog.records[0].is_pub);
2664 assert_eq!(prog.records[0].name.name, "Point");
2665 assert_eq!(prog.records[0].fields.len(), 2);
2666 assert_eq!(prog.records[0].fields[0].name.name, "x");
2667 assert_eq!(prog.records[0].fields[1].name.name, "y");
2668 }
2669
2670 #[test]
2671 fn parse_pub_record() {
2672 let source = r#"
2673 pub record Config {
2674 host: String,
2675 port: Int,
2676 }
2677
2678 agent Main {
2679 on start { emit(0); }
2680 }
2681 run Main;
2682 "#;
2683
2684 let (prog, errors) = parse_str(source);
2685 assert!(errors.is_empty(), "errors: {errors:?}");
2686 let prog = prog.expect("should parse");
2687
2688 assert_eq!(prog.records.len(), 1);
2689 assert!(prog.records[0].is_pub);
2690 assert_eq!(prog.records[0].name.name, "Config");
2691 }
2692
2693 #[test]
2694 fn parse_enum_declaration() {
2695 let source = r#"
2696 enum Status {
2697 Active,
2698 Pending,
2699 Done,
2700 }
2701
2702 agent Main {
2703 on start {
2704 emit(0);
2705 }
2706 }
2707 run Main;
2708 "#;
2709
2710 let (prog, errors) = parse_str(source);
2711 assert!(errors.is_empty(), "errors: {errors:?}");
2712 let prog = prog.expect("should parse");
2713
2714 assert_eq!(prog.enums.len(), 1);
2715 assert!(!prog.enums[0].is_pub);
2716 assert_eq!(prog.enums[0].name.name, "Status");
2717 assert_eq!(prog.enums[0].variants.len(), 3);
2718 assert_eq!(prog.enums[0].variants[0].name.name, "Active");
2719 assert_eq!(prog.enums[0].variants[1].name.name, "Pending");
2720 assert_eq!(prog.enums[0].variants[2].name.name, "Done");
2721 }
2722
2723 #[test]
2724 fn parse_pub_enum() {
2725 let source = r#"
2726 pub enum Priority { High, Medium, Low }
2727
2728 agent Main {
2729 on start { emit(0); }
2730 }
2731 run Main;
2732 "#;
2733
2734 let (prog, errors) = parse_str(source);
2735 assert!(errors.is_empty(), "errors: {errors:?}");
2736 let prog = prog.expect("should parse");
2737
2738 assert_eq!(prog.enums.len(), 1);
2739 assert!(prog.enums[0].is_pub);
2740 assert_eq!(prog.enums[0].name.name, "Priority");
2741 }
2742
2743 #[test]
2744 fn parse_const_declaration() {
2745 let source = r#"
2746 const MAX_RETRIES: Int = 3;
2747
2748 agent Main {
2749 on start {
2750 emit(0);
2751 }
2752 }
2753 run Main;
2754 "#;
2755
2756 let (prog, errors) = parse_str(source);
2757 assert!(errors.is_empty(), "errors: {errors:?}");
2758 let prog = prog.expect("should parse");
2759
2760 assert_eq!(prog.consts.len(), 1);
2761 assert!(!prog.consts[0].is_pub);
2762 assert_eq!(prog.consts[0].name.name, "MAX_RETRIES");
2763 assert!(matches!(prog.consts[0].ty, TypeExpr::Int));
2764 }
2765
2766 #[test]
2767 fn parse_pub_const() {
2768 let source = r#"
2769 pub const API_URL: String = "https://api.example.com";
2770
2771 agent Main {
2772 on start { emit(0); }
2773 }
2774 run Main;
2775 "#;
2776
2777 let (prog, errors) = parse_str(source);
2778 assert!(errors.is_empty(), "errors: {errors:?}");
2779 let prog = prog.expect("should parse");
2780
2781 assert_eq!(prog.consts.len(), 1);
2782 assert!(prog.consts[0].is_pub);
2783 assert_eq!(prog.consts[0].name.name, "API_URL");
2784 }
2785
2786 #[test]
2787 fn parse_multiple_type_declarations() {
2788 let source = r#"
2789 record Point { x: Int, y: Int }
2790 enum Color { Red, Green, Blue }
2791 const ORIGIN_X: Int = 0;
2792
2793 agent Main {
2794 on start { emit(0); }
2795 }
2796 run Main;
2797 "#;
2798
2799 let (prog, errors) = parse_str(source);
2800 assert!(errors.is_empty(), "errors: {errors:?}");
2801 let prog = prog.expect("should parse");
2802
2803 assert_eq!(prog.records.len(), 1);
2804 assert_eq!(prog.enums.len(), 1);
2805 assert_eq!(prog.consts.len(), 1);
2806 }
2807
2808 #[test]
2809 fn parse_match_expression() {
2810 let source = r#"
2811 enum Status { Active, Pending, Done }
2812
2813 agent Main {
2814 on start {
2815 let s: Int = match Active {
2816 Active => 1,
2817 Pending => 2,
2818 Done => 3,
2819 };
2820 emit(s);
2821 }
2822 }
2823 run Main;
2824 "#;
2825
2826 let (prog, errors) = parse_str(source);
2827 assert!(errors.is_empty(), "errors: {errors:?}");
2828 let prog = prog.expect("should parse");
2829
2830 assert_eq!(prog.agents.len(), 1);
2832 let handler = &prog.agents[0].handlers[0];
2834 let stmt = &handler.body.stmts[0];
2835 if let Stmt::Let { value, .. } = stmt {
2836 assert!(matches!(value, Expr::Match { .. }));
2837 } else {
2838 panic!("expected let statement with match");
2839 }
2840 }
2841
2842 #[test]
2843 fn parse_match_with_wildcard() {
2844 let source = r#"
2845 agent Main {
2846 on start {
2847 let x = 5;
2848 let result = match x {
2849 1 => 10,
2850 2 => 20,
2851 _ => 0,
2852 };
2853 emit(result);
2854 }
2855 }
2856 run Main;
2857 "#;
2858
2859 let (prog, errors) = parse_str(source);
2860 assert!(errors.is_empty(), "errors: {errors:?}");
2861 let prog = prog.expect("should parse");
2862
2863 assert_eq!(prog.agents.len(), 1);
2864 }
2865
2866 #[test]
2867 fn parse_record_construction() {
2868 let source = r#"
2869 record Point { x: Int, y: Int }
2870
2871 agent Main {
2872 on start {
2873 let p = Point { x: 10, y: 20 };
2874 emit(0);
2875 }
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 assert_eq!(prog.records.len(), 1);
2885 assert_eq!(prog.agents.len(), 1);
2886
2887 let handler = &prog.agents[0].handlers[0];
2889 let stmt = &handler.body.stmts[0];
2890 if let Stmt::Let { value, .. } = stmt {
2891 if let Expr::RecordConstruct { name, fields, .. } = value {
2892 assert_eq!(name.name, "Point");
2893 assert_eq!(fields.len(), 2);
2894 assert_eq!(fields[0].name.name, "x");
2895 assert_eq!(fields[1].name.name, "y");
2896 } else {
2897 panic!("expected RecordConstruct");
2898 }
2899 } else {
2900 panic!("expected let statement");
2901 }
2902 }
2903
2904 #[test]
2905 fn parse_match_with_qualified_variant() {
2906 let source = r#"
2907 enum Status { Active, Pending }
2908
2909 fn get_status() -> Int {
2910 return 1;
2911 }
2912
2913 agent Main {
2914 on start {
2915 let s = get_status();
2916 let result = match s {
2917 Status::Active => 1,
2918 Status::Pending => 0,
2919 };
2920 emit(result);
2921 }
2922 }
2923 run Main;
2924 "#;
2925
2926 let (prog, errors) = parse_str(source);
2927 assert!(errors.is_empty(), "errors: {errors:?}");
2928 let prog = prog.expect("should parse");
2929
2930 assert_eq!(prog.enums.len(), 1);
2931 assert_eq!(prog.agents.len(), 1);
2932 }
2933
2934 #[test]
2935 fn parse_field_access() {
2936 let source = r#"
2937 record Point { x: Int, y: Int }
2938
2939 agent Main {
2940 on start {
2941 let p = Point { x: 10, y: 20 };
2942 let x_val = p.x;
2943 let y_val = p.y;
2944 emit(x_val);
2945 }
2946 }
2947 run Main;
2948 "#;
2949
2950 let (prog, errors) = parse_str(source);
2951 assert!(errors.is_empty(), "errors: {errors:?}");
2952 let prog = prog.expect("should parse");
2953
2954 assert_eq!(prog.records.len(), 1);
2955 assert_eq!(prog.agents.len(), 1);
2956
2957 let handler = &prog.agents[0].handlers[0];
2959 let stmt = &handler.body.stmts[1]; if let Stmt::Let { value, .. } = stmt {
2961 if let Expr::FieldAccess { field, .. } = value {
2962 assert_eq!(field.name, "x");
2963 } else {
2964 panic!("expected FieldAccess");
2965 }
2966 } else {
2967 panic!("expected let statement");
2968 }
2969 }
2970
2971 #[test]
2972 fn parse_chained_field_access() {
2973 let source = r#"
2974 record Inner { val: Int }
2975 record Outer { inner: Inner }
2976
2977 agent Main {
2978 on start {
2979 let inner = Inner { val: 42 };
2980 let outer = Outer { inner: inner };
2981 let v = outer.inner.val;
2982 emit(v);
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 assert_eq!(prog.records.len(), 2);
2993 assert_eq!(prog.agents.len(), 1);
2994
2995 let handler = &prog.agents[0].handlers[0];
2997 let stmt = &handler.body.stmts[2]; if let Stmt::Let { value, .. } = stmt {
2999 if let Expr::FieldAccess {
3000 object, field: val, ..
3001 } = value
3002 {
3003 assert_eq!(val.name, "val");
3004 if let Expr::FieldAccess { field: inner, .. } = object.as_ref() {
3006 assert_eq!(inner.name, "inner");
3007 } else {
3008 panic!("expected nested FieldAccess");
3009 }
3010 } else {
3011 panic!("expected FieldAccess");
3012 }
3013 } else {
3014 panic!("expected let statement");
3015 }
3016 }
3017
3018 #[test]
3023 fn parse_loop_break() {
3024 let source = r#"
3025 agent Main {
3026 on start {
3027 let count = 0;
3028 loop {
3029 count = count + 1;
3030 if count > 5 {
3031 break;
3032 }
3033 }
3034 emit(count);
3035 }
3036 }
3037 run Main;
3038 "#;
3039
3040 let (prog, errors) = parse_str(source);
3041 assert!(errors.is_empty(), "errors: {errors:?}");
3042 let prog = prog.expect("should parse");
3043
3044 assert_eq!(prog.agents.len(), 1);
3045 let handler = &prog.agents[0].handlers[0];
3046 let loop_stmt = &handler.body.stmts[1];
3048 assert!(matches!(loop_stmt, Stmt::Loop { .. }));
3049 if let Stmt::Loop { body, .. } = loop_stmt {
3051 let if_stmt = &body.stmts[1];
3052 if let Stmt::If { then_block, .. } = if_stmt {
3053 assert!(matches!(then_block.stmts[0], Stmt::Break { .. }));
3054 } else {
3055 panic!("expected if statement");
3056 }
3057 }
3058 }
3059
3060 #[test]
3061 fn parse_agent_receives() {
3062 let source = r#"
3063 enum WorkerMsg {
3064 Task,
3065 Shutdown,
3066 }
3067
3068 agent Worker receives WorkerMsg {
3069 id: Int
3070
3071 on start {
3072 emit(0);
3073 }
3074 }
3075
3076 agent Main {
3077 on start {
3078 emit(0);
3079 }
3080 }
3081 run Main;
3082 "#;
3083
3084 let (prog, errors) = parse_str(source);
3085 assert!(errors.is_empty(), "errors: {errors:?}");
3086 let prog = prog.expect("should parse");
3087
3088 assert_eq!(prog.agents.len(), 2);
3089
3090 let worker = &prog.agents[0];
3092 assert_eq!(worker.name.name, "Worker");
3093 assert!(worker.receives.is_some());
3094 if let Some(TypeExpr::Named(name)) = &worker.receives {
3095 assert_eq!(name.name, "WorkerMsg");
3096 } else {
3097 panic!("expected named type for receives");
3098 }
3099
3100 let main = &prog.agents[1];
3102 assert_eq!(main.name.name, "Main");
3103 assert!(main.receives.is_none());
3104 }
3105
3106 #[test]
3107 fn parse_receive_expression() {
3108 let source = r#"
3109 enum Msg { Ping }
3110
3111 agent Worker receives Msg {
3112 on start {
3113 let msg = receive();
3114 emit(0);
3115 }
3116 }
3117
3118 agent Main {
3119 on start { emit(0); }
3120 }
3121 run Main;
3122 "#;
3123
3124 let (prog, errors) = parse_str(source);
3125 assert!(errors.is_empty(), "errors: {errors:?}");
3126 let prog = prog.expect("should parse");
3127
3128 let worker = prog
3130 .agents
3131 .iter()
3132 .find(|a| a.name.name == "Worker")
3133 .unwrap();
3134 let handler = &worker.handlers[0];
3135 let stmt = &handler.body.stmts[0];
3136
3137 if let Stmt::Let { value, .. } = stmt {
3138 assert!(matches!(value, Expr::Receive { .. }));
3139 } else {
3140 panic!("expected let with receive");
3141 }
3142 }
3143
3144 #[test]
3145 fn parse_message_passing_full() {
3146 let source = r#"
3147 enum WorkerMsg {
3148 Task,
3149 Shutdown,
3150 }
3151
3152 agent Worker receives WorkerMsg {
3153 id: Int
3154
3155 on start {
3156 let msg = receive();
3157 let result = match msg {
3158 Task => 1,
3159 Shutdown => 0,
3160 };
3161 emit(result);
3162 }
3163 }
3164
3165 agent Main {
3166 on start {
3167 let w = spawn Worker { id: 1 };
3168 send(w, Task);
3169 send(w, Shutdown);
3170 await w;
3171 emit(0);
3172 }
3173 }
3174 run Main;
3175 "#;
3176
3177 let (prog, errors) = parse_str(source);
3178 assert!(errors.is_empty(), "errors: {errors:?}");
3179 let prog = prog.expect("should parse");
3180
3181 assert_eq!(prog.enums.len(), 1);
3182 assert_eq!(prog.agents.len(), 2);
3183
3184 let worker = prog
3186 .agents
3187 .iter()
3188 .find(|a| a.name.name == "Worker")
3189 .unwrap();
3190 assert!(worker.receives.is_some());
3191 }
3192
3193 #[test]
3198 fn parse_fallible_function() {
3199 let source = r#"
3200 fn get_data(url: String) -> String fails {
3201 return infer("Get data from {url}" -> String);
3202 }
3203
3204 agent Main {
3205 on start { emit(0); }
3206 }
3207 run Main;
3208 "#;
3209
3210 let (prog, errors) = parse_str(source);
3211 assert!(errors.is_empty(), "errors: {errors:?}");
3212 let prog = prog.expect("should parse");
3213
3214 assert_eq!(prog.functions.len(), 1);
3215 assert!(prog.functions[0].is_fallible);
3216 }
3217
3218 #[test]
3219 fn parse_try_expression() {
3220 let source = r#"
3221 fn fallible() -> Int fails { return 42; }
3222
3223 agent Main {
3224 on start {
3225 let x = try fallible();
3226 emit(x);
3227 }
3228 }
3229 run Main;
3230 "#;
3231
3232 let (prog, errors) = parse_str(source);
3233 assert!(errors.is_empty(), "errors: {errors:?}");
3234 let prog = prog.expect("should parse");
3235
3236 let handler = &prog.agents[0].handlers[0];
3238 if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
3239 assert!(matches!(value, Expr::Try { .. }));
3240 } else {
3241 panic!("expected Let statement");
3242 }
3243 }
3244
3245 #[test]
3246 fn parse_catch_expression() {
3247 let source = r#"
3248 fn fallible() -> Int fails { return 42; }
3249
3250 agent Main {
3251 on start {
3252 let x = fallible() catch { 0 };
3253 emit(x);
3254 }
3255 }
3256 run Main;
3257 "#;
3258
3259 let (prog, errors) = parse_str(source);
3260 assert!(errors.is_empty(), "errors: {errors:?}");
3261 let prog = prog.expect("should parse");
3262
3263 let handler = &prog.agents[0].handlers[0];
3265 if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
3266 if let Expr::Catch { error_bind, .. } = value {
3267 assert!(error_bind.is_none());
3268 } else {
3269 panic!("expected Catch expression");
3270 }
3271 } else {
3272 panic!("expected Let statement");
3273 }
3274 }
3275
3276 #[test]
3277 fn parse_catch_with_error_binding() {
3278 let source = r#"
3279 fn fallible() -> Int fails { return 42; }
3280
3281 agent Main {
3282 on start {
3283 let x = fallible() catch(e) { 0 };
3284 emit(x);
3285 }
3286 }
3287 run Main;
3288 "#;
3289
3290 let (prog, errors) = parse_str(source);
3291 assert!(errors.is_empty(), "errors: {errors:?}");
3292 let prog = prog.expect("should parse");
3293
3294 let handler = &prog.agents[0].handlers[0];
3296 if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
3297 if let Expr::Catch { error_bind, .. } = value {
3298 assert!(error_bind.is_some());
3299 assert_eq!(error_bind.as_ref().unwrap().name, "e");
3300 } else {
3301 panic!("expected Catch expression");
3302 }
3303 } else {
3304 panic!("expected Let statement");
3305 }
3306 }
3307
3308 #[test]
3309 fn parse_fail_expression() {
3310 let source = r#"
3311 agent Main {
3312 on start {
3313 fail "something went wrong";
3314 }
3315 on error(e) {
3316 emit(0);
3317 }
3318 }
3319 run Main;
3320 "#;
3321
3322 let (prog, errors) = parse_str(source);
3323 assert!(errors.is_empty(), "errors: {errors:?}");
3324 let prog = prog.expect("should parse");
3325
3326 let handler = &prog.agents[0].handlers[0];
3328 if let Stmt::Expr { expr, .. } = &handler.body.stmts[0] {
3329 if let Expr::Fail { error, .. } = expr {
3330 assert!(matches!(**error, Expr::Literal { .. }));
3331 } else {
3332 panic!("expected Fail expression, got {expr:?}");
3333 }
3334 } else {
3335 panic!("expected Expr statement");
3336 }
3337 }
3338
3339 #[test]
3340 fn parse_retry_expression() {
3341 let source = r#"
3342 agent Main {
3343 topic: String
3344
3345 on start {
3346 let result = retry(3) {
3347 try infer("Summarize: {self.topic}")
3348 } catch { "fallback" };
3349 emit(result);
3350 }
3351
3352 on error(e) {
3353 emit("");
3354 }
3355 }
3356 run Main;
3357 "#;
3358
3359 let (prog, errors) = parse_str(source);
3360 assert!(errors.is_empty(), "errors: {errors:?}");
3361 let prog = prog.expect("should parse");
3362
3363 let handler = &prog.agents[0].handlers[0];
3365 if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
3366 if let Expr::Catch { expr, .. } = value {
3368 if let Expr::Retry { count, delay, .. } = expr.as_ref() {
3369 assert!(matches!(**count, Expr::Literal { .. }));
3370 assert!(delay.is_none());
3371 } else {
3372 panic!("expected Retry expression");
3373 }
3374 } else {
3375 panic!("expected Catch expression");
3376 }
3377 } else {
3378 panic!("expected Let statement");
3379 }
3380 }
3381
3382 #[test]
3383 fn parse_retry_with_delay() {
3384 let source = r#"
3385 agent Main {
3386 on start {
3387 let result = retry(3, delay: 1000) {
3388 42
3389 } catch { 0 };
3390 emit(result);
3391 }
3392 }
3393 run Main;
3394 "#;
3395
3396 let (prog, errors) = parse_str(source);
3397 assert!(errors.is_empty(), "errors: {errors:?}");
3398 let prog = prog.expect("should parse");
3399
3400 let handler = &prog.agents[0].handlers[0];
3402 if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
3403 if let Expr::Catch { expr, .. } = value {
3404 if let Expr::Retry { delay, .. } = expr.as_ref() {
3405 assert!(delay.is_some());
3406 } else {
3407 panic!("expected Retry expression");
3408 }
3409 } else {
3410 panic!("expected Catch expression");
3411 }
3412 } else {
3413 panic!("expected Let statement");
3414 }
3415 }
3416
3417 #[test]
3418 fn parse_on_error_handler() {
3419 let source = r#"
3420 agent Main {
3421 on start {
3422 emit(0);
3423 }
3424
3425 on error(e) {
3426 emit(1);
3427 }
3428 }
3429 run Main;
3430 "#;
3431
3432 let (prog, errors) = parse_str(source);
3433 assert!(errors.is_empty(), "errors: {errors:?}");
3434 let prog = prog.expect("should parse");
3435
3436 assert_eq!(prog.agents.len(), 1);
3437 assert_eq!(prog.agents[0].handlers.len(), 2);
3438
3439 let error_handler = prog.agents[0]
3441 .handlers
3442 .iter()
3443 .find(|h| matches!(h.event, EventKind::Error { .. }));
3444 assert!(error_handler.is_some());
3445
3446 if let EventKind::Error { param_name } = &error_handler.unwrap().event {
3447 assert_eq!(param_name.name, "e");
3448 } else {
3449 panic!("expected Error event kind");
3450 }
3451 }
3452
3453 #[test]
3458 fn parse_fn_type() {
3459 let source = r#"
3460 fn apply(f: Fn(Int) -> Int, x: Int) -> Int {
3461 return f(x);
3462 }
3463
3464 agent Main {
3465 on start {
3466 emit(0);
3467 }
3468 }
3469 run Main;
3470 "#;
3471
3472 let (prog, errors) = parse_str(source);
3473 assert!(errors.is_empty(), "errors: {errors:?}");
3474 let prog = prog.expect("should parse");
3475
3476 assert_eq!(prog.functions.len(), 1);
3477 let func = &prog.functions[0];
3478 assert_eq!(func.name.name, "apply");
3479 assert_eq!(func.params.len(), 2);
3480
3481 if let TypeExpr::Fn(params, ret) = &func.params[0].ty {
3483 assert_eq!(params.len(), 1);
3484 assert!(matches!(params[0], TypeExpr::Int));
3485 assert!(matches!(ret.as_ref(), TypeExpr::Int));
3486 } else {
3487 panic!("expected Fn type for first param");
3488 }
3489 }
3490
3491 #[test]
3492 fn parse_closure_with_params() {
3493 let source = r#"
3494 agent Main {
3495 on start {
3496 let f = |x: Int| x + 1;
3497 emit(0);
3498 }
3499 }
3500 run Main;
3501 "#;
3502
3503 let (prog, errors) = parse_str(source);
3504 assert!(errors.is_empty(), "errors: {errors:?}");
3505 let prog = prog.expect("should parse");
3506
3507 let handler = &prog.agents[0].handlers[0];
3509 if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
3510 if let Expr::Closure { params, body, .. } = value {
3511 assert_eq!(params.len(), 1);
3512 assert_eq!(params[0].name.name, "x");
3513 assert!(matches!(¶ms[0].ty, Some(TypeExpr::Int)));
3514
3515 assert!(matches!(body.as_ref(), Expr::Binary { .. }));
3517 } else {
3518 panic!("expected closure expression");
3519 }
3520 } else {
3521 panic!("expected let statement");
3522 }
3523 }
3524
3525 #[test]
3526 fn parse_closure_empty_params() {
3527 let source = r#"
3528 agent Main {
3529 on start {
3530 let f = || 42;
3531 emit(0);
3532 }
3533 }
3534 run Main;
3535 "#;
3536
3537 let (prog, errors) = parse_str(source);
3538 assert!(errors.is_empty(), "errors: {errors:?}");
3539 let prog = prog.expect("should parse");
3540
3541 let handler = &prog.agents[0].handlers[0];
3543 if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
3544 if let Expr::Closure { params, body, .. } = value {
3545 assert!(params.is_empty());
3546
3547 assert!(matches!(body.as_ref(), Expr::Literal { .. }));
3549 } else {
3550 panic!("expected closure expression");
3551 }
3552 } else {
3553 panic!("expected let statement");
3554 }
3555 }
3556
3557 #[test]
3558 fn parse_closure_multiple_params() {
3559 let source = r#"
3560 agent Main {
3561 on start {
3562 let add = |x: Int, y: Int| x + y;
3563 emit(0);
3564 }
3565 }
3566 run Main;
3567 "#;
3568
3569 let (prog, errors) = parse_str(source);
3570 assert!(errors.is_empty(), "errors: {errors:?}");
3571 let prog = prog.expect("should parse");
3572
3573 let handler = &prog.agents[0].handlers[0];
3574 if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
3575 if let Expr::Closure { params, .. } = value {
3576 assert_eq!(params.len(), 2);
3577 assert_eq!(params[0].name.name, "x");
3578 assert_eq!(params[1].name.name, "y");
3579 } else {
3580 panic!("expected closure expression");
3581 }
3582 } else {
3583 panic!("expected let statement");
3584 }
3585 }
3586
3587 #[test]
3588 fn parse_fn_type_multiarg() {
3589 let source = r#"
3590 fn fold_left(f: Fn(Int, Int) -> Int, init: Int) -> Int {
3591 return init;
3592 }
3593
3594 agent Main {
3595 on start {
3596 emit(0);
3597 }
3598 }
3599 run Main;
3600 "#;
3601
3602 let (prog, errors) = parse_str(source);
3603 assert!(errors.is_empty(), "errors: {errors:?}");
3604 let prog = prog.expect("should parse");
3605
3606 if let TypeExpr::Fn(params, ret) = &prog.functions[0].params[0].ty {
3608 assert_eq!(params.len(), 2);
3609 assert!(matches!(params[0], TypeExpr::Int));
3610 assert!(matches!(params[1], TypeExpr::Int));
3611 assert!(matches!(ret.as_ref(), TypeExpr::Int));
3612 } else {
3613 panic!("expected Fn type");
3614 }
3615 }
3616
3617 #[test]
3618 fn parse_tuple_literal() {
3619 let source = r#"
3620 agent Main {
3621 on start {
3622 let t = (1, 2);
3623 emit(0);
3624 }
3625 }
3626 run Main;
3627 "#;
3628
3629 let (prog, errors) = parse_str(source);
3630 assert!(errors.is_empty(), "errors: {errors:?}");
3631 let prog = prog.expect("should parse");
3632
3633 let handler = &prog.agents[0].handlers[0];
3634 if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
3635 if let Expr::Tuple { elements, .. } = value {
3636 assert_eq!(elements.len(), 2);
3637 } else {
3638 panic!("expected tuple expression, got {:?}", value);
3639 }
3640 } else {
3641 panic!("expected let statement");
3642 }
3643 }
3644
3645 #[test]
3650 fn parse_tool_declaration() {
3651 let source = r#"
3652 tool Http {
3653 fn get(url: String) -> Result<String, String>
3654 fn post(url: String, body: String) -> Result<String, String>
3655 }
3656 agent Main {
3657 on start { emit(0); }
3658 }
3659 run Main;
3660 "#;
3661
3662 let (prog, errors) = parse_str(source);
3663 assert!(errors.is_empty(), "errors: {errors:?}");
3664 let prog = prog.expect("should parse");
3665
3666 assert_eq!(prog.tools.len(), 1);
3667 assert_eq!(prog.tools[0].name.name, "Http");
3668 assert_eq!(prog.tools[0].functions.len(), 2);
3669 assert_eq!(prog.tools[0].functions[0].name.name, "get");
3670 assert_eq!(prog.tools[0].functions[1].name.name, "post");
3671 }
3672
3673 #[test]
3674 fn parse_pub_tool_declaration() {
3675 let source = r#"
3676 pub tool Database {
3677 fn query(sql: String) -> Result<List<String>, String>
3678 }
3679 agent Main {
3680 on start { emit(0); }
3681 }
3682 run Main;
3683 "#;
3684
3685 let (prog, errors) = parse_str(source);
3686 assert!(errors.is_empty(), "errors: {errors:?}");
3687 let prog = prog.expect("should parse");
3688
3689 assert!(prog.tools[0].is_pub);
3690 assert_eq!(prog.tools[0].name.name, "Database");
3691 }
3692
3693 #[test]
3694 fn parse_agent_with_tool_use() {
3695 let source = r#"
3696 agent Fetcher {
3697 use Http
3698
3699 url: String
3700
3701 on start {
3702 emit(0);
3703 }
3704 }
3705 run Fetcher;
3706 "#;
3707
3708 let (prog, errors) = parse_str(source);
3709 assert!(errors.is_empty(), "errors: {errors:?}");
3710 let prog = prog.expect("should parse");
3711
3712 assert_eq!(prog.agents[0].tool_uses.len(), 1);
3713 assert_eq!(prog.agents[0].tool_uses[0].name, "Http");
3714 assert_eq!(prog.agents[0].beliefs.len(), 1);
3715 }
3716
3717 #[test]
3718 fn parse_agent_with_multiple_tool_uses() {
3719 let source = r#"
3720 agent Pipeline {
3721 use Http, Fs
3722
3723 on start {
3724 emit(0);
3725 }
3726 }
3727 run Pipeline;
3728 "#;
3729
3730 let (prog, errors) = parse_str(source);
3731 assert!(errors.is_empty(), "errors: {errors:?}");
3732 let prog = prog.expect("should parse");
3733
3734 assert_eq!(prog.agents[0].tool_uses.len(), 2);
3735 assert_eq!(prog.agents[0].tool_uses[0].name, "Http");
3736 assert_eq!(prog.agents[0].tool_uses[1].name, "Fs");
3737 }
3738
3739 #[test]
3740 fn parse_tool_call_expression() {
3741 let source = r#"
3742 agent Fetcher {
3743 use Http
3744
3745 on start {
3746 let response = Http.get("https://example.com");
3747 emit(0);
3748 }
3749 }
3750 run Fetcher;
3751 "#;
3752
3753 let (prog, errors) = parse_str(source);
3754 assert!(errors.is_empty(), "errors: {errors:?}");
3755 let prog = prog.expect("should parse");
3756
3757 let handler = &prog.agents[0].handlers[0];
3758 if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
3759 if let Expr::ToolCall {
3760 tool,
3761 function,
3762 args,
3763 ..
3764 } = value
3765 {
3766 assert_eq!(tool.name, "Http");
3767 assert_eq!(function.name, "get");
3768 assert_eq!(args.len(), 1);
3769 } else {
3770 panic!("expected ToolCall expression, got {:?}", value);
3771 }
3772 } else {
3773 panic!("expected let statement");
3774 }
3775 }
3776
3777 #[test]
3778 fn parse_tool_call_with_multiple_args() {
3779 let source = r#"
3780 agent Writer {
3781 use Fs
3782
3783 on start {
3784 let result = Fs.write("/tmp/test.txt", "hello world");
3785 emit(0);
3786 }
3787 }
3788 run Writer;
3789 "#;
3790
3791 let (prog, errors) = parse_str(source);
3792 assert!(errors.is_empty(), "errors: {errors:?}");
3793 let prog = prog.expect("should parse");
3794
3795 let handler = &prog.agents[0].handlers[0];
3796 if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
3797 if let Expr::ToolCall { args, .. } = value {
3798 assert_eq!(args.len(), 2);
3799 } else {
3800 panic!("expected ToolCall expression, got {:?}", value);
3801 }
3802 } else {
3803 panic!("expected let statement");
3804 }
3805 }
3806
3807 #[test]
3808 fn parse_string_interp_with_field_access() {
3809 let source = r#"
3810 record Person { name: String }
3811 agent Main {
3812 on start {
3813 let p = Person { name: "Alice" };
3814 print("Hello, {p.name}!");
3815 emit(0);
3816 }
3817 }
3818 run Main;
3819 "#;
3820
3821 let (prog, errors) = parse_str(source);
3822 assert!(errors.is_empty(), "errors: {errors:?}");
3823 let prog = prog.expect("should parse");
3824
3825 let handler = &prog.agents[0].handlers[0];
3827 if let Stmt::Expr { expr, .. } = &handler.body.stmts[1] {
3828 if let Expr::Call { args, .. } = expr {
3829 if let Expr::StringInterp { template, .. } = &args[0] {
3830 assert!(template.has_interpolations());
3831 let interps: Vec<_> = template.interpolations().collect();
3832 assert_eq!(interps.len(), 1);
3833 match interps[0] {
3835 InterpExpr::FieldAccess { base, field, .. } => {
3836 assert_eq!(base.base_ident().name, "p");
3837 assert_eq!(field.name, "name");
3838 }
3839 _ => panic!("expected FieldAccess, got {:?}", interps[0]),
3840 }
3841 } else {
3842 panic!("expected StringInterp");
3843 }
3844 } else {
3845 panic!("expected Call");
3846 }
3847 } else {
3848 panic!("expected Expr statement");
3849 }
3850 }
3851
3852 #[test]
3853 fn parse_string_interp_with_tuple_index() {
3854 let source = r#"
3855 agent Main {
3856 on start {
3857 let pair = (1, 2);
3858 print("First: {pair.0}");
3859 emit(0);
3860 }
3861 }
3862 run Main;
3863 "#;
3864
3865 let (prog, errors) = parse_str(source);
3866 assert!(errors.is_empty(), "errors: {errors:?}");
3867 let prog = prog.expect("should parse");
3868
3869 let handler = &prog.agents[0].handlers[0];
3870 if let Stmt::Expr { expr, .. } = &handler.body.stmts[1] {
3871 if let Expr::Call { args, .. } = expr {
3872 if let Expr::StringInterp { template, .. } = &args[0] {
3873 let interps: Vec<_> = template.interpolations().collect();
3874 assert_eq!(interps.len(), 1);
3875 match interps[0] {
3876 InterpExpr::TupleIndex { base, index, .. } => {
3877 assert_eq!(base.base_ident().name, "pair");
3878 assert_eq!(*index, 0);
3879 }
3880 _ => panic!("expected TupleIndex, got {:?}", interps[0]),
3881 }
3882 } else {
3883 panic!("expected StringInterp");
3884 }
3885 } else {
3886 panic!("expected Call");
3887 }
3888 } else {
3889 panic!("expected Expr statement");
3890 }
3891 }
3892
3893 #[test]
3894 fn parse_mock_tool_with_fail() {
3895 let source = r#"
3896 test "mock tool fail" {
3897 mock tool Http.get -> fail("network error");
3898 }
3899 "#;
3900
3901 let (prog, errors) = parse_str(source);
3902 assert!(errors.is_empty(), "errors: {errors:?}");
3903 let prog = prog.expect("should parse");
3904
3905 let test = &prog.tests[0];
3906 assert_eq!(test.body.stmts.len(), 1);
3907
3908 if let Stmt::MockTool {
3909 tool_name,
3910 fn_name,
3911 value,
3912 ..
3913 } = &test.body.stmts[0]
3914 {
3915 assert_eq!(tool_name.name, "Http");
3916 assert_eq!(fn_name.name, "get");
3917 assert!(
3918 matches!(value, MockValue::Fail(_)),
3919 "expected MockValue::Fail, got {:?}",
3920 value
3921 );
3922 } else {
3923 panic!("expected MockTool statement, got {:?}", test.body.stmts[0]);
3924 }
3925 }
3926
3927 #[test]
3928 fn parse_mock_tool_with_value() {
3929 let source = r#"
3930 test "mock tool value" {
3931 mock tool Http.get -> "response";
3932 }
3933 "#;
3934
3935 let (prog, errors) = parse_str(source);
3936 assert!(errors.is_empty(), "errors: {errors:?}");
3937 let prog = prog.expect("should parse");
3938
3939 let test = &prog.tests[0];
3940 if let Stmt::MockTool { value, .. } = &test.body.stmts[0] {
3941 assert!(
3942 matches!(value, MockValue::Value(_)),
3943 "expected MockValue::Value, got {:?}",
3944 value
3945 );
3946 } else {
3947 panic!("expected MockTool statement");
3948 }
3949 }
3950}