1use crate::ast::{
6 AgentDecl, BeliefDecl, BinOp, Block, ChildSpec, ClosureParam, ConstDecl, EffectHandlerDecl,
7 ElseBranch, EnumDecl, EventKind, Expr, FieldInit, FnDecl, HandlerAssignment, HandlerConfig,
8 HandlerDecl, Literal, MapEntry, MatchArm, MockValue, ModDecl, Param, Pattern, Program,
9 ProtocolDecl, ProtocolRole, ProtocolStep, RecordDecl, RecordField, RestartPolicy, Stmt,
10 StringPart, StringTemplate, SupervisionStrategy, SupervisorDecl, TestDecl, ToolDecl,
11 ToolFnDecl, UnaryOp, UseDecl, UseKind,
12};
13use crate::{Ident, Span, TypeExpr};
14use crate::{Spanned, Token};
15use chumsky::prelude::*;
16use chumsky::BoxedParser;
17use std::ops::Range;
18use std::sync::Arc;
19
20pub type ParseError = Simple<Token>;
22
23#[must_use]
29#[allow(clippy::needless_pass_by_value)] pub fn parse(tokens: &[Spanned], source: Arc<str>) -> (Option<Program>, Vec<ParseError>) {
31 let len = source.len();
32
33 let token_spans: Vec<(Token, Range<usize>)> = tokens
35 .iter()
36 .map(|s| (s.token.clone(), s.start..s.end))
37 .collect();
38
39 let stream = chumsky::Stream::from_iter(len..len, token_spans.into_iter());
40
41 let (ast, errors) = program_parser(Arc::clone(&source)).parse_recovery(stream);
42
43 (ast, errors)
44}
45
46#[allow(clippy::needless_pass_by_value)]
52fn program_parser(source: Arc<str>) -> impl Parser<Token, Program, Error = ParseError> {
53 let src = source.clone();
54 let src2 = source.clone();
55
56 let top_level = mod_parser(source.clone())
58 .or(use_parser(source.clone()))
59 .or(record_parser(source.clone()))
60 .or(enum_parser(source.clone()))
61 .or(const_parser(source.clone()))
62 .or(tool_parser(source.clone()))
63 .or(protocol_parser(source.clone()))
64 .or(effect_handler_parser(source.clone()))
65 .or(agent_parser(source.clone()))
66 .or(supervisor_parser(source.clone()))
67 .or(fn_parser(source.clone()))
68 .or(test_parser(source.clone()))
69 .recover_with(skip_then_retry_until([
70 Token::KwMod,
71 Token::KwUse,
72 Token::KwPub,
73 Token::KwRecord,
74 Token::KwEnum,
75 Token::KwConst,
76 Token::KwTool,
77 Token::KwProtocol,
78 Token::KwHandler,
79 Token::KwAgent,
80 Token::KwSupervisor,
81 Token::KwFn,
82 Token::KwRun,
83 Token::KwTest,
84 ]));
85
86 let run_stmt = just(Token::KwRun)
87 .ignore_then(ident_token_parser(src.clone()))
88 .then_ignore(just(Token::Semicolon))
89 .or_not();
90
91 top_level.repeated().then(run_stmt).map_with_span(
92 move |(items, run_agent), span: Range<usize>| {
93 let mut mod_decls = Vec::new();
94 let mut use_decls = Vec::new();
95 let mut records = Vec::new();
96 let mut enums = Vec::new();
97 let mut consts = Vec::new();
98 let mut tools = Vec::new();
99 let mut protocols = Vec::new();
100 let mut effect_handlers = Vec::new();
101 let mut agents = Vec::new();
102 let mut supervisors = Vec::new();
103 let mut functions = Vec::new();
104 let mut tests = Vec::new();
105
106 for item in items {
107 match item {
108 TopLevel::Mod(m) => mod_decls.push(m),
109 TopLevel::Use(u) => use_decls.push(u),
110 TopLevel::Record(r) => records.push(r),
111 TopLevel::Enum(e) => enums.push(e),
112 TopLevel::Const(c) => consts.push(c),
113 TopLevel::Tool(t) => tools.push(t),
114 TopLevel::Protocol(p) => protocols.push(p),
115 TopLevel::EffectHandler(h) => effect_handlers.push(h),
116 TopLevel::Agent(a) => agents.push(a),
117 TopLevel::Supervisor(s) => supervisors.push(s),
118 TopLevel::Function(f) => functions.push(f),
119 TopLevel::Test(t) => tests.push(t),
120 }
121 }
122
123 Program {
124 mod_decls,
125 use_decls,
126 records,
127 enums,
128 consts,
129 tools,
130 protocols,
131 effect_handlers,
132 agents,
133 supervisors,
134 functions,
135 tests,
136 run_agent,
137 span: make_span(&src2, span),
138 }
139 },
140 )
141}
142
143enum TopLevel {
145 Mod(ModDecl),
146 Use(UseDecl),
147 Record(RecordDecl),
148 Enum(EnumDecl),
149 Const(ConstDecl),
150 Tool(ToolDecl),
151 Protocol(ProtocolDecl),
152 EffectHandler(EffectHandlerDecl),
153 Agent(AgentDecl),
154 Supervisor(SupervisorDecl),
155 Function(FnDecl),
156 Test(TestDecl),
157}
158
159#[allow(clippy::needless_pass_by_value)]
165fn mod_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
166 let src = source.clone();
167
168 just(Token::KwPub)
169 .or_not()
170 .then_ignore(just(Token::KwMod))
171 .then(ident_token_parser(src.clone()))
172 .then_ignore(just(Token::Semicolon))
173 .map_with_span(move |(is_pub, name), span: Range<usize>| {
174 TopLevel::Mod(ModDecl {
175 is_pub: is_pub.is_some(),
176 name,
177 span: make_span(&src, span),
178 })
179 })
180}
181
182#[allow(clippy::needless_pass_by_value)]
184fn use_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
185 let src = source.clone();
186 let src2 = source.clone();
187 let src3 = source.clone();
188 let src4 = source.clone();
189
190 let simple_use = just(Token::KwPub)
192 .or_not()
193 .then_ignore(just(Token::KwUse))
194 .then(
195 ident_token_parser(src.clone())
196 .separated_by(just(Token::ColonColon))
197 .at_least(1),
198 )
199 .then(
200 just(Token::KwAs)
201 .ignore_then(ident_token_parser(src.clone()))
202 .or_not(),
203 )
204 .then_ignore(just(Token::Semicolon))
205 .map_with_span(move |((is_pub, path), alias), span: Range<usize>| {
206 TopLevel::Use(UseDecl {
207 is_pub: is_pub.is_some(),
208 path,
209 kind: UseKind::Simple(alias),
210 span: make_span(&src, span),
211 })
212 });
213
214 let group_item = ident_token_parser(src2.clone()).then(
216 just(Token::KwAs)
217 .ignore_then(ident_token_parser(src2.clone()))
218 .or_not(),
219 );
220
221 let group_use = just(Token::KwPub)
223 .or_not()
224 .then_ignore(just(Token::KwUse))
225 .then(
226 ident_token_parser(src3.clone())
227 .then_ignore(just(Token::ColonColon))
228 .repeated()
229 .at_least(1),
230 )
231 .then(
232 group_item
233 .separated_by(just(Token::Comma))
234 .allow_trailing()
235 .delimited_by(just(Token::LBrace), just(Token::RBrace)),
236 )
237 .then_ignore(just(Token::Semicolon))
238 .map_with_span(move |((is_pub, path), items), span: Range<usize>| {
239 TopLevel::Use(UseDecl {
240 is_pub: is_pub.is_some(),
241 path,
242 kind: UseKind::Group(items),
243 span: make_span(&src3, span),
244 })
245 });
246
247 let glob_use = just(Token::KwPub)
249 .or_not()
250 .then_ignore(just(Token::KwUse))
251 .then(
252 ident_token_parser(src4.clone())
253 .then_ignore(just(Token::ColonColon))
254 .repeated()
255 .at_least(1),
256 )
257 .then_ignore(just(Token::Star))
258 .then_ignore(just(Token::Semicolon))
259 .map_with_span(move |(is_pub, path), span: Range<usize>| {
260 TopLevel::Use(UseDecl {
261 is_pub: is_pub.is_some(),
262 path,
263 kind: UseKind::Glob,
264 span: make_span(&src4, span),
265 })
266 });
267
268 group_use.or(glob_use).or(simple_use)
270}
271
272#[allow(clippy::needless_pass_by_value)]
278fn type_params_parser(
281 source: Arc<str>,
282) -> impl Parser<Token, Vec<Ident>, Error = ParseError> + Clone {
283 ident_token_parser(source)
284 .separated_by(just(Token::Comma))
285 .allow_trailing()
286 .delimited_by(just(Token::Lt), just(Token::Gt))
287 .or_not()
288 .map(|params| params.unwrap_or_default())
289}
290
291fn record_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
292 let src = source.clone();
293 let src2 = source.clone();
294
295 let field = ident_token_parser(src.clone())
297 .then_ignore(just(Token::Colon))
298 .then(type_parser(src.clone()))
299 .map_with_span(move |(name, ty), span: Range<usize>| RecordField {
300 name,
301 ty,
302 span: make_span(&src, span),
303 });
304
305 just(Token::KwPub)
306 .or_not()
307 .then_ignore(just(Token::KwRecord))
308 .then(ident_token_parser(src2.clone()))
309 .then(type_params_parser(src2.clone()))
310 .then(
311 field
312 .separated_by(just(Token::Comma))
313 .allow_trailing()
314 .delimited_by(just(Token::LBrace), just(Token::RBrace)),
315 )
316 .map_with_span(
317 move |(((is_pub, name), type_params), fields), span: Range<usize>| {
318 TopLevel::Record(RecordDecl {
319 is_pub: is_pub.is_some(),
320 name,
321 type_params,
322 fields,
323 span: make_span(&src2, span),
324 })
325 },
326 )
327}
328
329#[allow(clippy::needless_pass_by_value)]
331fn enum_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
332 let src = source.clone();
333 let src2 = source.clone();
334 let src3 = source.clone();
335
336 let variant = ident_token_parser(src.clone())
338 .then(
339 type_parser(src.clone())
340 .delimited_by(just(Token::LParen), just(Token::RParen))
341 .or_not(),
342 )
343 .map_with_span({
344 let src = src.clone();
345 move |(name, payload), span: Range<usize>| crate::ast::EnumVariant {
346 name,
347 payload,
348 span: make_span(&src, span),
349 }
350 });
351
352 just(Token::KwPub)
353 .or_not()
354 .then_ignore(just(Token::KwEnum))
355 .then(ident_token_parser(src3.clone()))
356 .then(type_params_parser(src3.clone()))
357 .then(
358 variant
359 .separated_by(just(Token::Comma))
360 .allow_trailing()
361 .delimited_by(just(Token::LBrace), just(Token::RBrace)),
362 )
363 .map_with_span(
364 move |(((is_pub, name), type_params), variants), span: Range<usize>| {
365 TopLevel::Enum(EnumDecl {
366 is_pub: is_pub.is_some(),
367 name,
368 type_params,
369 variants,
370 span: make_span(&src2, span),
371 })
372 },
373 )
374}
375
376#[allow(clippy::needless_pass_by_value)]
378fn const_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
379 let src = source.clone();
380 let src2 = source.clone();
381
382 just(Token::KwPub)
383 .or_not()
384 .then_ignore(just(Token::KwConst))
385 .then(ident_token_parser(src.clone()))
386 .then_ignore(just(Token::Colon))
387 .then(type_parser(src.clone()))
388 .then_ignore(just(Token::Eq))
389 .then(expr_parser(src.clone()))
390 .then_ignore(just(Token::Semicolon))
391 .map_with_span(move |(((is_pub, name), ty), value), span: Range<usize>| {
392 TopLevel::Const(ConstDecl {
393 is_pub: is_pub.is_some(),
394 name,
395 ty,
396 value,
397 span: make_span(&src2, span),
398 })
399 })
400}
401
402#[allow(clippy::needless_pass_by_value)]
408fn tool_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
409 let src = source.clone();
410 let src2 = source.clone();
411 let src3 = source.clone();
412
413 let param = ident_token_parser(src.clone())
415 .then_ignore(just(Token::Colon))
416 .then(type_parser(src.clone()))
417 .map_with_span(move |(name, ty), span: Range<usize>| Param {
418 name,
419 ty,
420 span: make_span(&src, span),
421 });
422
423 let params = param
424 .separated_by(just(Token::Comma))
425 .allow_trailing()
426 .delimited_by(just(Token::LParen), just(Token::RParen));
427
428 let tool_fn = just(Token::KwFn)
430 .ignore_then(ident_token_parser(src2.clone()))
431 .then(params)
432 .then_ignore(just(Token::Arrow))
433 .then(type_parser(src2.clone()))
434 .map_with_span(
435 move |((name, params), return_ty), span: Range<usize>| ToolFnDecl {
436 name,
437 params,
438 return_ty,
439 span: make_span(&src2, span),
440 },
441 );
442
443 just(Token::KwPub)
444 .or_not()
445 .then_ignore(just(Token::KwTool))
446 .then(ident_token_parser(src3.clone()))
447 .then(
448 tool_fn
449 .repeated()
450 .delimited_by(just(Token::LBrace), just(Token::RBrace)),
451 )
452 .map_with_span(move |((is_pub, name), functions), span: Range<usize>| {
453 TopLevel::Tool(ToolDecl {
454 is_pub: is_pub.is_some(),
455 name,
456 functions,
457 span: make_span(&src3, span),
458 })
459 })
460}
461
462#[allow(clippy::needless_pass_by_value)]
476fn protocol_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
477 let src = source.clone();
478 let src2 = source.clone();
479 let src3 = source.clone();
480
481 let protocol_step = ident_token_parser(src.clone())
483 .then_ignore(just(Token::Arrow))
484 .then(ident_token_parser(src.clone()))
485 .then_ignore(just(Token::Colon))
486 .then(type_parser(src.clone()))
487 .map_with_span({
488 let src = src.clone();
489 move |((sender, receiver), message_type), span: Range<usize>| ProtocolStep {
490 sender,
491 receiver,
492 message_type,
493 span: make_span(&src, span),
494 }
495 });
496
497 just(Token::KwPub)
499 .or_not()
500 .then_ignore(just(Token::KwProtocol))
501 .then(ident_token_parser(src2.clone()))
502 .then(
503 protocol_step
504 .repeated()
505 .delimited_by(just(Token::LBrace), just(Token::RBrace)),
506 )
507 .map_with_span(move |((is_pub, name), steps), span: Range<usize>| {
508 TopLevel::Protocol(ProtocolDecl {
509 is_pub: is_pub.is_some(),
510 name,
511 steps,
512 span: make_span(&src3, span),
513 })
514 })
515}
516
517#[allow(clippy::needless_pass_by_value)]
532fn effect_handler_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
533 let src = source.clone();
534 let src2 = source.clone();
535
536 let config_entry = ident_token_parser(src.clone())
538 .then_ignore(just(Token::Colon))
539 .then(literal_value_parser(src.clone()))
540 .map_with_span({
541 let src = src.clone();
542 move |(key, value), span: Range<usize>| HandlerConfig {
543 key,
544 value,
545 span: make_span(&src, span),
546 }
547 });
548
549 just(Token::KwPub)
551 .or_not()
552 .then_ignore(just(Token::KwHandler))
553 .then(ident_token_parser(src2.clone()))
554 .then_ignore(just(Token::KwHandles))
555 .then(ident_token_parser(src2.clone()))
556 .then(
557 config_entry
558 .repeated()
559 .delimited_by(just(Token::LBrace), just(Token::RBrace)),
560 )
561 .map_with_span(move |(((is_pub, name), effect), config), span: Range<usize>| {
562 TopLevel::EffectHandler(EffectHandlerDecl {
563 is_pub: is_pub.is_some(),
564 name,
565 effect,
566 config,
567 span: make_span(&src2, span),
568 })
569 })
570}
571
572#[allow(clippy::needless_pass_by_value)]
574fn literal_value_parser(source: Arc<str>) -> impl Parser<Token, Literal, Error = ParseError> {
575 let src = source.clone();
576
577 filter_map(move |span: Range<usize>, tok| match tok {
578 Token::IntLit => {
579 let s = &src[span.clone()];
580 let n = s.parse::<i64>().map_err(|_| {
581 Simple::custom(span, format!("invalid integer literal `{s}`"))
582 })?;
583 Ok(Literal::Int(n))
584 }
585 Token::FloatLit => {
586 let s = &src[span.clone()];
587 let n = s.parse::<f64>().map_err(|_| {
588 Simple::custom(span, format!("invalid float literal `{s}`"))
589 })?;
590 Ok(Literal::Float(n))
591 }
592 Token::StringLit => {
593 let s = &src[span.clone()];
594 let content = &s[1..s.len() - 1];
596 let unescaped = unescape_string(content);
597 Ok(Literal::String(unescaped))
598 }
599 Token::KwTrue => Ok(Literal::Bool(true)),
600 Token::KwFalse => Ok(Literal::Bool(false)),
601 _ => Err(Simple::expected_input_found(span, [], Some(tok))),
602 })
603}
604
605fn unescape_string(s: &str) -> String {
607 let mut result = String::new();
608 let mut chars = s.chars().peekable();
609 while let Some(c) = chars.next() {
610 if c == '\\' {
611 match chars.next() {
612 Some('n') => result.push('\n'),
613 Some('t') => result.push('\t'),
614 Some('r') => result.push('\r'),
615 Some('\\') => result.push('\\'),
616 Some('"') => result.push('"'),
617 Some('\'') => result.push('\''),
618 Some(other) => {
619 result.push('\\');
620 result.push(other);
621 }
622 None => result.push('\\'),
623 }
624 } else {
625 result.push(c);
626 }
627 }
628 result
629}
630
631#[allow(clippy::needless_pass_by_value)]
637fn test_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
638 let src = source.clone();
639 let src2 = source.clone();
640
641 let serial_annotation = just(Token::At)
644 .then(filter(|t: &Token| matches!(t, Token::Ident)))
645 .or_not()
646 .map(|opt| opt.is_some());
647
648 let test_name = filter_map(|span: Range<usize>, tok: Token| match tok {
650 Token::StringLit => Ok(()),
651 _ => Err(Simple::expected_input_found(span, [], Some(tok))),
652 })
653 .map_with_span(move |_, span: Range<usize>| {
654 let s = &src[span.clone()];
656 s[1..s.len() - 1].to_string()
657 });
658
659 let body = block_parser(src2.clone());
661
662 serial_annotation
663 .then_ignore(just(Token::KwTest))
664 .then(test_name)
665 .then(body)
666 .map_with_span(move |((is_serial, name), body), span: Range<usize>| {
667 TopLevel::Test(TestDecl {
668 name,
669 is_serial,
670 body,
671 span: make_span(&src2, span),
672 })
673 })
674}
675
676#[allow(clippy::needless_pass_by_value)]
692fn supervisor_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
693 let src = source.clone();
694 let src2 = source.clone();
695 let src3 = source.clone();
696 let src4 = source.clone();
697
698 let strategy = just(Token::KwStrategy)
700 .ignore_then(just(Token::Colon))
701 .ignore_then(filter_map({
702 let src = src.clone();
703 move |span: Range<usize>, token| match token {
704 Token::Ident => {
705 let text = &src[span.start..span.end];
706 match text {
707 "OneForOne" => Ok(SupervisionStrategy::OneForOne),
708 "OneForAll" => Ok(SupervisionStrategy::OneForAll),
709 "RestForOne" => Ok(SupervisionStrategy::RestForOne),
710 _ => Err(Simple::custom(
711 span,
712 format!("unknown strategy `{text}`, expected OneForOne, OneForAll, or RestForOne"),
713 )),
714 }
715 }
716 _ => Err(Simple::expected_input_found(
717 span,
718 vec![Some(Token::Ident)],
719 Some(token),
720 )),
721 }
722 }));
723
724 let restart_policy = just(Token::KwRestart)
726 .ignore_then(just(Token::Colon))
727 .ignore_then(filter_map({
728 let src = src2.clone();
729 move |span: Range<usize>, token| match token {
730 Token::Ident => {
731 let text = &src[span.start..span.end];
732 match text {
733 "Permanent" => Ok(RestartPolicy::Permanent),
734 "Transient" => Ok(RestartPolicy::Transient),
735 "Temporary" => Ok(RestartPolicy::Temporary),
736 _ => Err(Simple::custom(
737 span,
738 format!("unknown restart policy `{text}`, expected Permanent, Transient, or Temporary"),
739 )),
740 }
741 }
742 _ => Err(Simple::expected_input_found(
743 span,
744 vec![Some(Token::Ident)],
745 Some(token),
746 )),
747 }
748 }));
749
750 let src_handler = src3.clone();
752 let handler_assignment = just(Token::KwHandler)
753 .ignore_then(ident_token_parser(src3.clone()))
754 .then_ignore(just(Token::Colon))
755 .then(ident_token_parser(src3.clone()))
756 .map_with_span(move |(effect, handler), span: Range<usize>| HandlerAssignment {
757 effect,
758 handler,
759 span: make_span(&src_handler, span),
760 });
761
762 let field_init = ident_token_parser(src3.clone())
764 .then_ignore(just(Token::Colon))
765 .then(expr_parser(src3.clone()))
766 .map_with_span({
767 let src = src3.clone();
768 move |(name, value), span: Range<usize>| FieldInit {
769 name,
770 value,
771 span: make_span(&src, span),
772 }
773 });
774
775 let child_spec = ident_token_parser(src3.clone())
779 .then_ignore(just(Token::LBrace))
780 .then(restart_policy.then_ignore(just(Token::Comma).or_not()).or_not())
781 .then(
782 handler_assignment
783 .then_ignore(just(Token::Comma).or_not())
784 .repeated(),
785 )
786 .then(
787 field_init
788 .then_ignore(just(Token::Comma).or_not())
789 .repeated(),
790 )
791 .then_ignore(just(Token::RBrace))
792 .map_with_span({
793 let src = src3.clone();
794 move |(((agent_name, restart), handler_assignments), beliefs), span: Range<usize>| {
795 ChildSpec {
796 agent_name,
797 restart: restart.unwrap_or_default(),
798 beliefs,
799 handler_assignments,
800 span: make_span(&src, span),
801 }
802 }
803 });
804
805 let children = just(Token::KwChildren)
807 .ignore_then(
808 child_spec
809 .repeated()
810 .at_least(1)
811 .delimited_by(just(Token::LBrace), just(Token::RBrace)),
812 );
813
814 just(Token::KwPub)
816 .or_not()
817 .then_ignore(just(Token::KwSupervisor))
818 .then(ident_token_parser(src4.clone()))
819 .then_ignore(just(Token::LBrace))
820 .then(strategy)
821 .then(children)
822 .then_ignore(just(Token::RBrace))
823 .map_with_span(
824 move |(((is_pub, name), strategy), children), span: Range<usize>| {
825 TopLevel::Supervisor(SupervisorDecl {
826 is_pub: is_pub.is_some(),
827 name,
828 strategy,
829 children,
830 span: make_span(&src4, span),
831 })
832 },
833 )
834}
835
836#[allow(clippy::needless_pass_by_value)]
842fn agent_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
843 let src2 = source.clone();
844 let src3 = source.clone();
845 let src4 = source.clone();
846 let src5 = source.clone();
847 let src6 = source.clone();
848
849 let tool_use = just(Token::KwUse)
851 .ignore_then(
852 ident_token_parser(src5.clone())
853 .separated_by(just(Token::Comma))
854 .at_least(1),
855 )
856 .or_not()
857 .map(|tools| tools.unwrap_or_default());
858
859 let src_annot = source.clone();
862 let persistent_annotation = just(Token::At)
863 .ignore_then(filter_map(move |span: Range<usize>, token| match token {
864 Token::Ident => {
865 let text = &src_annot[span.start..span.end];
866 if text == "persistent" {
867 Ok(true)
868 } else {
869 Err(Simple::custom(
870 span,
871 format!("unknown annotation @{}, expected @persistent", text),
872 ))
873 }
874 }
875 _ => Err(Simple::expected_input_found(
876 span,
877 vec![Some(Token::Ident)],
878 Some(token),
879 )),
880 }))
881 .or_not();
882
883 let src_belief = source.clone();
884 let belief = persistent_annotation
885 .then(ident_token_parser(src_belief.clone()))
886 .then_ignore(just(Token::Colon))
887 .then(type_parser(src_belief.clone()))
888 .map_with_span(move |((is_persistent, name), ty), span: Range<usize>| BeliefDecl {
889 is_persistent: is_persistent.unwrap_or(false),
890 name,
891 ty,
892 span: make_span(&src_belief, span),
893 });
894
895 let handler = just(Token::KwOn)
896 .ignore_then(event_kind_parser(src2.clone()))
897 .then(block_parser(src2.clone()))
898 .map_with_span(move |(event, body), span: Range<usize>| HandlerDecl {
899 event,
900 body,
901 span: make_span(&src2, span),
902 });
903
904 let receives_clause = just(Token::KwReceives)
906 .ignore_then(type_parser(src3.clone()))
907 .or_not();
908
909 let src_follows = src6.clone();
912 let single_follows = ident_token_parser(src6.clone())
913 .then_ignore(just(Token::KwAs))
914 .then(ident_token_parser(src6.clone()))
915 .map_with_span(move |(protocol, role), span: Range<usize>| ProtocolRole {
916 protocol,
917 role,
918 span: make_span(&src_follows, span),
919 });
920
921 let follows_clause = just(Token::KwFollows)
922 .ignore_then(single_follows.separated_by(just(Token::Comma)).at_least(1))
923 .or_not()
924 .map(|follows| follows.unwrap_or_default());
925
926 just(Token::KwPub)
927 .or_not()
928 .then_ignore(just(Token::KwAgent))
929 .then(ident_token_parser(src3.clone()))
930 .then(receives_clause)
931 .then(follows_clause)
932 .then_ignore(just(Token::LBrace))
933 .then(tool_use)
934 .then(belief.repeated())
935 .then(handler.repeated())
936 .then_ignore(just(Token::RBrace))
937 .map_with_span(
938 move |((((((is_pub, name), receives), follows), tool_uses), beliefs), handlers),
939 span: Range<usize>| {
940 TopLevel::Agent(AgentDecl {
941 is_pub: is_pub.is_some(),
942 name,
943 receives,
944 follows,
945 tool_uses,
946 beliefs,
947 handlers,
948 span: make_span(&src4, span),
949 })
950 },
951 )
952}
953
954#[allow(clippy::needless_pass_by_value)]
956fn event_kind_parser(source: Arc<str>) -> impl Parser<Token, EventKind, Error = ParseError> {
957 let src = source.clone();
958
959 let waking = just(Token::KwWaking).to(EventKind::Waking);
961 let start = just(Token::KwStart).to(EventKind::Start);
962 let pause = just(Token::KwPause).to(EventKind::Pause);
964 let resume = just(Token::KwResume).to(EventKind::Resume);
965 let stop = just(Token::KwStop).to(EventKind::Stop);
966 let resting = just(Token::KwResting).to(EventKind::Resting);
968
969 let message = just(Token::KwMessage)
970 .ignore_then(just(Token::LParen))
971 .ignore_then(ident_token_parser(src.clone()))
972 .then_ignore(just(Token::Colon))
973 .then(type_parser(src.clone()))
974 .then_ignore(just(Token::RParen))
975 .map(|(param_name, param_ty)| EventKind::Message {
976 param_name,
977 param_ty,
978 });
979
980 let error = just(Token::KwError)
982 .ignore_then(just(Token::LParen))
983 .ignore_then(ident_token_parser(src))
984 .then_ignore(just(Token::RParen))
985 .map(|param_name| EventKind::Error { param_name });
986
987 waking
988 .or(start)
989 .or(pause)
990 .or(resume)
991 .or(stop)
992 .or(resting)
993 .or(message)
994 .or(error)
995}
996
997#[allow(clippy::needless_pass_by_value)]
1003fn fn_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
1004 let src = source.clone();
1005 let src2 = source.clone();
1006 let src3 = source.clone();
1007
1008 let param = ident_token_parser(src.clone())
1009 .then_ignore(just(Token::Colon))
1010 .then(type_parser(src.clone()))
1011 .map_with_span(move |(name, ty), span: Range<usize>| Param {
1012 name,
1013 ty,
1014 span: make_span(&src, span),
1015 });
1016
1017 let params = param
1018 .separated_by(just(Token::Comma))
1019 .allow_trailing()
1020 .delimited_by(just(Token::LParen), just(Token::RParen));
1021
1022 just(Token::KwPub)
1023 .or_not()
1024 .then_ignore(just(Token::KwFn))
1025 .then(ident_token_parser(src2.clone()))
1026 .then(type_params_parser(src2.clone()))
1027 .then(params)
1028 .then_ignore(just(Token::Arrow))
1029 .then(type_parser(src2.clone()))
1030 .then(just(Token::KwFails).or_not())
1031 .then(block_parser(src2))
1032 .map_with_span(
1033 move |((((((is_pub, name), type_params), params), return_ty), is_fallible), body),
1034 span: Range<usize>| {
1035 TopLevel::Function(FnDecl {
1036 is_pub: is_pub.is_some(),
1037 name,
1038 type_params,
1039 params,
1040 return_ty,
1041 is_fallible: is_fallible.is_some(),
1042 body,
1043 span: make_span(&src3, span),
1044 })
1045 },
1046 )
1047}
1048
1049#[allow(clippy::needless_pass_by_value)]
1056fn block_parser(source: Arc<str>) -> BoxedParser<'static, Token, Block, ParseError> {
1057 let src = source.clone();
1058
1059 recursive(move |block: Recursive<Token, Block, ParseError>| {
1060 let src_inner = src.clone();
1061 stmt_parser(src.clone(), block)
1062 .repeated()
1063 .delimited_by(just(Token::LBrace), just(Token::RBrace))
1064 .recover_with(nested_delimiters(
1065 Token::LBrace,
1066 Token::RBrace,
1067 [
1068 (Token::LParen, Token::RParen),
1069 (Token::LBracket, Token::RBracket),
1070 ],
1071 |_span: Range<usize>| vec![],
1072 ))
1073 .map_with_span(move |stmts, span: Range<usize>| Block {
1074 stmts,
1075 span: make_span(&src_inner, span),
1076 })
1077 })
1078 .boxed()
1079}
1080
1081#[allow(clippy::needless_pass_by_value)]
1083fn stmt_parser(
1084 source: Arc<str>,
1085 block: impl Parser<Token, Block, Error = ParseError> + Clone + 'static,
1086) -> impl Parser<Token, Stmt, Error = ParseError> + Clone {
1087 let src = source.clone();
1088 let src2 = source.clone();
1089 let src3 = source.clone();
1090 let src4 = source.clone();
1091 let src5 = source.clone();
1092 let src6 = source.clone();
1093 let src7 = source.clone();
1094
1095 let src10 = source.clone();
1097 let let_tuple_stmt = just(Token::KwLet)
1098 .ignore_then(
1099 ident_token_parser(src10.clone())
1100 .separated_by(just(Token::Comma))
1101 .at_least(2)
1102 .allow_trailing()
1103 .delimited_by(just(Token::LParen), just(Token::RParen)),
1104 )
1105 .then(
1106 just(Token::Colon)
1107 .ignore_then(type_parser(src10.clone()))
1108 .or_not(),
1109 )
1110 .then_ignore(just(Token::Eq))
1111 .then(expr_parser(src10.clone()))
1112 .then_ignore(just(Token::Semicolon))
1113 .map_with_span(
1114 move |((names, ty), value), span: Range<usize>| Stmt::LetTuple {
1115 names,
1116 ty,
1117 value,
1118 span: make_span(&src10, span),
1119 },
1120 );
1121
1122 let let_stmt = just(Token::KwLet)
1123 .ignore_then(ident_token_parser(src.clone()))
1124 .then(
1125 just(Token::Colon)
1126 .ignore_then(type_parser(src.clone()))
1127 .or_not(),
1128 )
1129 .then_ignore(just(Token::Eq))
1130 .then(expr_parser(src.clone()))
1131 .then_ignore(just(Token::Semicolon))
1132 .map_with_span(move |((name, ty), value), span: Range<usize>| Stmt::Let {
1133 name,
1134 ty,
1135 value,
1136 span: make_span(&src, span),
1137 });
1138
1139 let return_stmt = just(Token::KwReturn)
1140 .ignore_then(expr_parser(src2.clone()).or_not())
1141 .then_ignore(just(Token::Semicolon))
1142 .map_with_span(move |value, span: Range<usize>| Stmt::Return {
1143 value,
1144 span: make_span(&src2, span),
1145 });
1146
1147 let if_stmt = recursive(|if_stmt| {
1148 let src_if = src3.clone();
1149 let block_clone = block.clone();
1150
1151 just(Token::KwIf)
1152 .ignore_then(expr_parser(src3.clone()))
1153 .then(block_clone.clone())
1154 .then(
1155 just(Token::KwElse)
1156 .ignore_then(
1157 if_stmt
1158 .map(|s| ElseBranch::ElseIf(Box::new(s)))
1159 .or(block_clone.map(ElseBranch::Block)),
1160 )
1161 .or_not(),
1162 )
1163 .map_with_span(
1164 move |((condition, then_block), else_block), span: Range<usize>| Stmt::If {
1165 condition,
1166 then_block,
1167 else_block,
1168 span: make_span(&src_if, span),
1169 },
1170 )
1171 });
1172
1173 let for_stmt = just(Token::KwFor)
1174 .ignore_then(for_pattern_parser(src4.clone()))
1175 .then_ignore(just(Token::KwIn))
1176 .then(expr_parser(src4.clone()))
1177 .then(block.clone())
1178 .map_with_span(
1179 move |((pattern, iter), body), span: Range<usize>| Stmt::For {
1180 pattern,
1181 iter,
1182 body,
1183 span: make_span(&src4, span),
1184 },
1185 );
1186
1187 let while_stmt = just(Token::KwWhile)
1188 .ignore_then(expr_parser(src7.clone()))
1189 .then(block.clone())
1190 .map_with_span(move |(condition, body), span: Range<usize>| Stmt::While {
1191 condition,
1192 body,
1193 span: make_span(&src7, span),
1194 });
1195
1196 let src8 = source.clone();
1197 let loop_stmt = just(Token::KwLoop)
1198 .ignore_then(block.clone())
1199 .map_with_span(move |body, span: Range<usize>| Stmt::Loop {
1200 body,
1201 span: make_span(&src8, span),
1202 });
1203
1204 let src9 = source.clone();
1205 let break_stmt = just(Token::KwBreak)
1206 .then_ignore(just(Token::Semicolon))
1207 .map_with_span(move |_, span: Range<usize>| Stmt::Break {
1208 span: make_span(&src9, span),
1209 });
1210
1211 let src12 = source.clone();
1214 let mock_divine_stmt = just(Token::KwMock)
1215 .ignore_then(just(Token::KwDivine).or(just(Token::KwInfer)))
1216 .ignore_then(just(Token::Arrow))
1217 .ignore_then(expr_parser(src12.clone()).map(|expr| {
1218 if let Expr::Fail { error, .. } = expr {
1220 MockValue::Fail(*error)
1221 } else {
1222 MockValue::Value(expr)
1223 }
1224 }))
1225 .then_ignore(just(Token::Semicolon))
1226 .map_with_span(move |value, span: Range<usize>| Stmt::MockDivine {
1227 value,
1228 span: make_span(&src12, span),
1229 });
1230
1231 let src13 = source.clone();
1233 let src14 = source.clone();
1234 let src15 = source.clone();
1235 let mock_tool_stmt = just(Token::KwMock)
1236 .ignore_then(just(Token::KwTool))
1237 .ignore_then(ident_token_parser(src13.clone())) .then_ignore(just(Token::Dot))
1239 .then(ident_token_parser(src14.clone())) .then_ignore(just(Token::Arrow))
1241 .then(expr_parser(src15.clone()).map(|expr| {
1242 if let Expr::Fail { error, .. } = expr {
1244 MockValue::Fail(*error)
1245 } else {
1246 MockValue::Value(expr)
1247 }
1248 }))
1249 .then_ignore(just(Token::Semicolon))
1250 .map_with_span(
1251 move |((tool_name, fn_name), value), span: Range<usize>| Stmt::MockTool {
1252 tool_name,
1253 fn_name,
1254 value,
1255 span: make_span(&src15, span),
1256 },
1257 );
1258
1259 let assign_stmt = ident_token_parser(src5.clone())
1260 .then_ignore(just(Token::Eq))
1261 .then(expr_parser(src5.clone()))
1262 .then_ignore(just(Token::Semicolon))
1263 .map_with_span(move |(name, value), span: Range<usize>| Stmt::Assign {
1264 name,
1265 value,
1266 span: make_span(&src5, span),
1267 });
1268
1269 let src16 = source.clone();
1271 let span_block_stmt = just(Token::KwSpan)
1272 .ignore_then(expr_parser(src16.clone()))
1273 .then(block.clone())
1274 .map_with_span(move |(name, body), span: Range<usize>| Stmt::SpanBlock {
1275 name,
1276 body,
1277 span: make_span(&src16, span),
1278 });
1279
1280 let src17 = source.clone();
1282 let checkpoint_stmt = just(Token::KwCheckpoint)
1283 .then_ignore(just(Token::LParen))
1284 .then_ignore(just(Token::RParen))
1285 .then_ignore(just(Token::Semicolon))
1286 .map_with_span(move |_, span: Range<usize>| Stmt::Checkpoint {
1287 span: make_span(&src17, span),
1288 });
1289
1290 let expr_stmt = expr_parser(src6.clone())
1291 .then_ignore(just(Token::Semicolon))
1292 .map_with_span(move |expr, span: Range<usize>| Stmt::Expr {
1293 expr,
1294 span: make_span(&src6, span),
1295 });
1296
1297 let_tuple_stmt
1298 .or(let_stmt)
1299 .or(return_stmt)
1300 .or(if_stmt)
1301 .or(for_stmt)
1302 .or(while_stmt)
1303 .or(loop_stmt)
1304 .or(break_stmt)
1305 .or(span_block_stmt)
1306 .or(checkpoint_stmt)
1307 .or(mock_divine_stmt)
1308 .or(mock_tool_stmt)
1309 .or(assign_stmt)
1310 .or(expr_stmt)
1311}
1312
1313#[allow(clippy::needless_pass_by_value, clippy::too_many_lines)]
1320fn expr_parser(source: Arc<str>) -> BoxedParser<'static, Token, Expr, ParseError> {
1321 recursive(move |expr: Recursive<Token, Expr, ParseError>| {
1322 let src = source.clone();
1323
1324 let literal = literal_parser(src.clone());
1325 let var = var_parser(src.clone());
1326
1327 let paren_or_tuple = just(Token::LParen)
1330 .ignore_then(
1331 expr.clone()
1332 .separated_by(just(Token::Comma))
1333 .allow_trailing(),
1334 )
1335 .then_ignore(just(Token::RParen))
1336 .map_with_span({
1337 let src = src.clone();
1338 move |elements, span: Range<usize>| {
1339 if elements.len() == 1 {
1340 Expr::Paren {
1342 inner: Box::new(elements.into_iter().next().unwrap()),
1343 span: make_span(&src, span),
1344 }
1345 } else {
1346 Expr::Tuple {
1348 elements,
1349 span: make_span(&src, span),
1350 }
1351 }
1352 }
1353 });
1354
1355 let list = expr
1356 .clone()
1357 .separated_by(just(Token::Comma))
1358 .allow_trailing()
1359 .delimited_by(just(Token::LBracket), just(Token::RBracket))
1360 .map_with_span({
1361 let src = src.clone();
1362 move |elements, span: Range<usize>| Expr::List {
1363 elements,
1364 span: make_span(&src, span),
1365 }
1366 });
1367
1368 let self_access = just(Token::KwSelf)
1370 .ignore_then(just(Token::Dot))
1371 .ignore_then(ident_token_parser(src.clone()))
1372 .then(
1373 expr.clone()
1374 .separated_by(just(Token::Comma))
1375 .allow_trailing()
1376 .delimited_by(just(Token::LParen), just(Token::RParen))
1377 .or_not(),
1378 )
1379 .map_with_span({
1380 let src = src.clone();
1381 move |(field, args), span: Range<usize>| match args {
1382 Some(args) => Expr::SelfMethodCall {
1383 method: field,
1384 args,
1385 span: make_span(&src, span),
1386 },
1387 None => Expr::SelfField {
1388 field,
1389 span: make_span(&src, span),
1390 },
1391 }
1392 });
1393
1394 let divine_expr = just(Token::KwDivine)
1397 .or(just(Token::KwInfer))
1398 .ignore_then(just(Token::LParen))
1399 .ignore_then(string_template_parser(src.clone()))
1400 .then(
1401 just(Token::Arrow)
1402 .ignore_then(type_parser(src.clone()))
1403 .or_not(),
1404 )
1405 .then_ignore(just(Token::RParen))
1406 .map_with_span({
1407 let src = src.clone();
1408 move |(template, result_ty), span: Range<usize>| Expr::Divine {
1409 template,
1410 result_ty,
1411 span: make_span(&src, span),
1412 }
1413 });
1414
1415 let summon_field_init = ident_token_parser(src.clone())
1417 .then_ignore(just(Token::Colon))
1418 .then(expr.clone())
1419 .map_with_span({
1420 let src = src.clone();
1421 move |(name, value), span: Range<usize>| FieldInit {
1422 name,
1423 value,
1424 span: make_span(&src, span),
1425 }
1426 });
1427
1428 let summon_expr = just(Token::KwSummon)
1429 .ignore_then(ident_token_parser(src.clone()))
1430 .then_ignore(just(Token::LBrace))
1431 .then(
1432 summon_field_init
1433 .separated_by(just(Token::Comma))
1434 .allow_trailing(),
1435 )
1436 .then_ignore(just(Token::RBrace))
1437 .map_with_span({
1438 let src = src.clone();
1439 move |(agent, fields), span: Range<usize>| Expr::Summon {
1440 agent,
1441 fields,
1442 span: make_span(&src, span),
1443 }
1444 });
1445
1446 let timeout_clause = just(Token::KwTimeout)
1449 .ignore_then(just(Token::LParen))
1450 .ignore_then(expr.clone())
1451 .then_ignore(just(Token::RParen));
1452
1453 let await_expr = just(Token::KwAwait)
1454 .ignore_then(ident_token_parser(src.clone()).map_with_span({
1455 let src = src.clone();
1456 move |name, span: Range<usize>| Expr::Var {
1457 name,
1458 span: make_span(&src, span),
1459 }
1460 }))
1461 .then(timeout_clause.or_not())
1462 .map_with_span({
1463 let src = src.clone();
1464 move |(handle, timeout), span: Range<usize>| Expr::Await {
1465 handle: Box::new(handle),
1466 timeout: timeout.map(Box::new),
1467 span: make_span(&src, span),
1468 }
1469 });
1470
1471 let send_expr = just(Token::KwSend)
1473 .ignore_then(just(Token::LParen))
1474 .ignore_then(expr.clone())
1475 .then_ignore(just(Token::Comma))
1476 .then(expr.clone())
1477 .then_ignore(just(Token::RParen))
1478 .map_with_span({
1479 let src = src.clone();
1480 move |(handle, message), span: Range<usize>| Expr::Send {
1481 handle: Box::new(handle),
1482 message: Box::new(message),
1483 span: make_span(&src, span),
1484 }
1485 });
1486
1487 let yield_expr = just(Token::KwYield)
1489 .ignore_then(just(Token::LParen))
1490 .ignore_then(expr.clone())
1491 .then_ignore(just(Token::RParen))
1492 .map_with_span({
1493 let src = src.clone();
1494 move |value, span: Range<usize>| Expr::Yield {
1495 value: Box::new(value),
1496 span: make_span(&src, span),
1497 }
1498 });
1499
1500 let reply_expr = just(Token::KwReply)
1502 .ignore_then(just(Token::LParen))
1503 .ignore_then(expr.clone())
1504 .then_ignore(just(Token::RParen))
1505 .map_with_span({
1506 let src = src.clone();
1507 move |message, span: Range<usize>| Expr::Reply {
1508 message: Box::new(message),
1509 span: make_span(&src, span),
1510 }
1511 });
1512
1513 let turbofish = just(Token::ColonColon)
1515 .ignore_then(
1516 type_parser(src.clone())
1517 .separated_by(just(Token::Comma))
1518 .allow_trailing()
1519 .delimited_by(just(Token::Lt), just(Token::Gt)),
1520 )
1521 .or_not()
1522 .map(|args| args.unwrap_or_default());
1523
1524 let call_expr = ident_token_parser(src.clone())
1526 .then(turbofish.clone())
1527 .then(
1528 expr.clone()
1529 .separated_by(just(Token::Comma))
1530 .allow_trailing()
1531 .delimited_by(just(Token::LParen), just(Token::RParen)),
1532 )
1533 .map_with_span({
1534 let src = src.clone();
1535 move |((name, type_args), args), span: Range<usize>| Expr::Call {
1536 name,
1537 type_args,
1538 args,
1539 span: make_span(&src, span),
1540 }
1541 });
1542
1543 let pattern = pattern_parser(src.clone());
1545
1546 let match_arm = pattern
1548 .then_ignore(just(Token::FatArrow))
1549 .then(expr.clone())
1550 .map_with_span({
1551 let src = src.clone();
1552 move |(pattern, body), span: Range<usize>| MatchArm {
1553 pattern,
1554 body,
1555 span: make_span(&src, span),
1556 }
1557 });
1558
1559 let match_expr = just(Token::KwMatch)
1560 .ignore_then(expr.clone())
1561 .then(
1562 match_arm
1563 .separated_by(just(Token::Comma))
1564 .allow_trailing()
1565 .delimited_by(just(Token::LBrace), just(Token::RBrace)),
1566 )
1567 .map_with_span({
1568 let src = src.clone();
1569 move |(scrutinee, arms), span: Range<usize>| Expr::Match {
1570 scrutinee: Box::new(scrutinee),
1571 arms,
1572 span: make_span(&src, span),
1573 }
1574 });
1575
1576 let receive_expr = just(Token::KwReceive)
1578 .ignore_then(just(Token::LParen))
1579 .ignore_then(just(Token::RParen))
1580 .map_with_span({
1581 let src = src.clone();
1582 move |_, span: Range<usize>| Expr::Receive {
1583 span: make_span(&src, span),
1584 }
1585 });
1586
1587 let trace_expr = just(Token::KwTrace)
1589 .ignore_then(just(Token::LParen))
1590 .ignore_then(expr.clone())
1591 .then_ignore(just(Token::RParen))
1592 .map_with_span({
1593 let src = src.clone();
1594 move |message, span: Range<usize>| Expr::Trace {
1595 message: Box::new(message),
1596 span: make_span(&src, span),
1597 }
1598 });
1599
1600 let record_field_init = ident_token_parser(src.clone())
1604 .then_ignore(just(Token::Colon))
1605 .then(expr.clone())
1606 .map_with_span({
1607 let src = src.clone();
1608 move |(name, value), span: Range<usize>| FieldInit {
1609 name,
1610 value,
1611 span: make_span(&src, span),
1612 }
1613 });
1614
1615 let record_turbofish = just(Token::ColonColon)
1617 .ignore_then(
1618 type_parser(src.clone())
1619 .separated_by(just(Token::Comma))
1620 .allow_trailing()
1621 .delimited_by(just(Token::Lt), just(Token::Gt)),
1622 )
1623 .or_not()
1624 .map(|args| args.unwrap_or_default());
1625
1626 let record_construct = ident_token_parser(src.clone())
1627 .then(record_turbofish)
1628 .then_ignore(just(Token::LBrace))
1629 .then(
1630 record_field_init
1631 .separated_by(just(Token::Comma))
1632 .allow_trailing(),
1633 )
1634 .then_ignore(just(Token::RBrace))
1635 .map_with_span({
1636 let src = src.clone();
1637 move |((name, type_args), fields), span: Range<usize>| Expr::RecordConstruct {
1638 name,
1639 type_args,
1640 fields,
1641 span: make_span(&src, span),
1642 }
1643 });
1644
1645 let closure_param = ident_token_parser(src.clone())
1647 .then(
1648 just(Token::Colon)
1649 .ignore_then(type_parser(src.clone()))
1650 .or_not(),
1651 )
1652 .map_with_span({
1653 let src = src.clone();
1654 move |(name, ty), span: Range<usize>| ClosureParam {
1655 name,
1656 ty,
1657 span: make_span(&src, span),
1658 }
1659 });
1660
1661 let closure_empty = just(Token::Or).ignore_then(expr.clone()).map_with_span({
1664 let src = src.clone();
1665 move |body, span: Range<usize>| Expr::Closure {
1666 params: vec![],
1667 body: Box::new(body),
1668 span: make_span(&src, span),
1669 }
1670 });
1671
1672 let closure_with_params = just(Token::Pipe)
1673 .ignore_then(
1674 closure_param
1675 .separated_by(just(Token::Comma))
1676 .allow_trailing(),
1677 )
1678 .then_ignore(just(Token::Pipe))
1679 .then(expr.clone())
1680 .map_with_span({
1681 let src = src.clone();
1682 move |(params, body), span: Range<usize>| Expr::Closure {
1683 params,
1684 body: Box::new(body),
1685 span: make_span(&src, span),
1686 }
1687 });
1688
1689 let closure = closure_with_params.or(closure_empty);
1690
1691 let map_entry = expr
1694 .clone()
1695 .then_ignore(just(Token::Colon))
1696 .then(expr.clone())
1697 .map_with_span({
1698 let src = src.clone();
1699 move |(key, value), span: Range<usize>| MapEntry {
1700 key,
1701 value,
1702 span: make_span(&src, span),
1703 }
1704 });
1705
1706 let map_literal = map_entry
1707 .separated_by(just(Token::Comma))
1708 .allow_trailing()
1709 .delimited_by(just(Token::LBrace), just(Token::RBrace))
1710 .map_with_span({
1711 let src = src.clone();
1712 move |entries, span: Range<usize>| Expr::Map {
1713 entries,
1714 span: make_span(&src, span),
1715 }
1716 });
1717
1718 let variant_turbofish = just(Token::ColonColon)
1721 .ignore_then(
1722 type_parser(src.clone())
1723 .separated_by(just(Token::Comma))
1724 .allow_trailing()
1725 .delimited_by(just(Token::Lt), just(Token::Gt)),
1726 )
1727 .or_not()
1728 .map(|args| args.unwrap_or_default());
1729
1730 let enum_name_parser = {
1733 let src = src.clone();
1734 ident_token_parser(src.clone()).or(just(Token::TyOption)
1735 .or(just(Token::TyResult))
1736 .map_with_span({
1737 let src = src.clone();
1738 move |token, span: Range<usize>| {
1739 let name = match token {
1740 Token::TyOption => "Option",
1741 Token::TyResult => "Result",
1742 _ => unreachable!(),
1743 };
1744 Ident {
1745 name: name.to_string(),
1746 span: make_span(&src, span),
1747 }
1748 }
1749 }))
1750 };
1751
1752 let variant_construct = enum_name_parser
1753 .then(variant_turbofish)
1754 .then_ignore(just(Token::ColonColon))
1755 .then(ident_token_parser(src.clone()))
1756 .then(
1757 expr.clone()
1758 .delimited_by(just(Token::LParen), just(Token::RParen))
1759 .or_not(),
1760 )
1761 .map_with_span({
1762 let src = src.clone();
1763 move |(((enum_name, type_args), variant), payload), span: Range<usize>| {
1764 Expr::VariantConstruct {
1765 enum_name,
1766 type_args,
1767 variant,
1768 payload: payload.map(Box::new),
1769 span: make_span(&src, span),
1770 }
1771 }
1772 });
1773
1774 let atom = closure
1782 .or(divine_expr)
1783 .or(summon_expr)
1784 .or(await_expr)
1785 .or(send_expr)
1786 .or(yield_expr)
1787 .or(reply_expr)
1788 .or(receive_expr)
1789 .or(trace_expr)
1790 .or(match_expr)
1791 .or(self_access)
1792 .or(record_construct)
1793 .or(variant_construct)
1794 .or(call_expr)
1795 .or(map_literal)
1796 .or(list)
1797 .or(paren_or_tuple)
1798 .or(literal)
1799 .or(var)
1800 .boxed();
1801
1802 enum PostfixOp {
1805 Field(Ident),
1806 TupleIndex(usize, Range<usize>),
1807 MethodCall(Ident, Vec<Expr>, Range<usize>), }
1809
1810 let method_call = ident_token_parser(src.clone())
1812 .then(
1813 expr.clone()
1814 .separated_by(just(Token::Comma))
1815 .allow_trailing()
1816 .delimited_by(just(Token::LParen), just(Token::RParen)),
1817 )
1818 .map_with_span(|(name, args), span: Range<usize>| {
1819 PostfixOp::MethodCall(name, args, span)
1820 });
1821
1822 let postfix_op = just(Token::Dot).ignore_then(
1823 filter_map({
1825 let src = src.clone();
1826 move |span: Range<usize>, token| match token {
1827 Token::IntLit => {
1828 let text = &src[span.start..span.end];
1829 text.parse::<usize>()
1830 .map(|idx| PostfixOp::TupleIndex(idx, span.clone()))
1831 .map_err(|_| Simple::custom(span, "invalid tuple index"))
1832 }
1833 _ => Err(Simple::expected_input_found(
1834 span,
1835 vec![Some(Token::IntLit)],
1836 Some(token),
1837 )),
1838 }
1839 })
1840 .or(method_call)
1842 .or(ident_token_parser(src.clone()).map(PostfixOp::Field)),
1843 );
1844
1845 let postfix = atom
1846 .then(postfix_op.repeated())
1847 .foldl({
1848 let src = src.clone();
1849 move |object, op| match op {
1850 PostfixOp::Field(field) => {
1851 let span = make_span(&src, object.span().start..field.span.end);
1852 Expr::FieldAccess {
1853 object: Box::new(object),
1854 field,
1855 span,
1856 }
1857 }
1858 PostfixOp::TupleIndex(index, idx_span) => {
1859 let span = make_span(&src, object.span().start..idx_span.end);
1860 Expr::TupleIndex {
1861 tuple: Box::new(object),
1862 index,
1863 span,
1864 }
1865 }
1866 PostfixOp::MethodCall(method, args, call_span) => {
1867 if let Expr::Var { name: tool, .. } = &object {
1870 let span = make_span(&src, object.span().start..call_span.end);
1871 Expr::ToolCall {
1872 tool: tool.clone(),
1873 function: method,
1874 args,
1875 span,
1876 }
1877 } else {
1878 let span = make_span(&src, object.span().start..call_span.end);
1881 let callee = Expr::FieldAccess {
1882 object: Box::new(object),
1883 field: method,
1884 span: span.clone(),
1885 };
1886 Expr::Apply {
1887 callee: Box::new(callee),
1888 args,
1889 span,
1890 }
1891 }
1892 }
1893 }
1894 })
1895 .boxed();
1896
1897 let unary = just(Token::Minus)
1899 .to(UnaryOp::Neg)
1900 .or(just(Token::Bang).to(UnaryOp::Not))
1901 .repeated()
1902 .then(postfix.clone())
1903 .foldr(|op, operand| {
1904 let span = operand.span().clone();
1905 Expr::Unary {
1906 op,
1907 operand: Box::new(operand),
1908 span,
1909 }
1910 })
1911 .boxed();
1912
1913 let try_expr = just(Token::KwTry)
1916 .ignore_then(postfix.clone())
1917 .map_with_span({
1918 let src = src.clone();
1919 move |inner, span: Range<usize>| Expr::Try {
1920 expr: Box::new(inner),
1921 span: make_span(&src, span),
1922 }
1923 })
1924 .boxed();
1925
1926 let fail_expr = just(Token::KwFail)
1929 .ignore_then(postfix.clone())
1930 .map_with_span({
1931 let src = src.clone();
1932 move |error, span: Range<usize>| Expr::Fail {
1933 error: Box::new(error),
1934 span: make_span(&src, span),
1935 }
1936 })
1937 .boxed();
1938
1939 let retry_delay = just(Token::Comma)
1944 .ignore_then(just(Token::KwDelay))
1945 .ignore_then(just(Token::Colon))
1946 .ignore_then(postfix.clone());
1947
1948 let retry_on = just(Token::Comma)
1949 .ignore_then(just(Token::KwOn))
1950 .ignore_then(just(Token::Colon))
1951 .ignore_then(
1952 postfix
1953 .clone()
1954 .separated_by(just(Token::Comma))
1955 .allow_trailing()
1956 .delimited_by(just(Token::LBracket), just(Token::RBracket)),
1957 );
1958
1959 let retry_expr = just(Token::KwRetry)
1960 .ignore_then(just(Token::LParen))
1961 .ignore_then(postfix.clone())
1962 .then(retry_delay.or_not())
1963 .then(retry_on.or_not())
1964 .then_ignore(just(Token::RParen))
1965 .then(
1966 expr.clone()
1967 .delimited_by(just(Token::LBrace), just(Token::RBrace)),
1968 )
1969 .map_with_span({
1970 let src = src.clone();
1971 move |(((count, delay), on_errors), body), span: Range<usize>| Expr::Retry {
1972 count: Box::new(count),
1973 delay: delay.map(Box::new),
1974 on_errors,
1975 body: Box::new(body),
1976 span: make_span(&src, span),
1977 }
1978 })
1979 .boxed();
1980
1981 let unary = retry_expr.or(fail_expr).or(try_expr).or(unary).boxed();
1983
1984 let mul_div_op = just(Token::Star)
1987 .to(BinOp::Mul)
1988 .or(just(Token::Slash).to(BinOp::Div))
1989 .or(just(Token::Percent).to(BinOp::Rem));
1990
1991 let mul_div = unary
1992 .clone()
1993 .then(mul_div_op.then(unary.clone()).repeated())
1994 .foldl({
1995 let src = src.clone();
1996 move |left, (op, right)| {
1997 let span = make_span(&src, left.span().start..right.span().end);
1998 Expr::Binary {
1999 op,
2000 left: Box::new(left),
2001 right: Box::new(right),
2002 span,
2003 }
2004 }
2005 })
2006 .boxed();
2007
2008 let add_sub_op = just(Token::Plus)
2010 .to(BinOp::Add)
2011 .or(just(Token::Minus).to(BinOp::Sub));
2012
2013 let add_sub = mul_div
2014 .clone()
2015 .then(add_sub_op.then(mul_div).repeated())
2016 .foldl({
2017 let src = src.clone();
2018 move |left, (op, right)| {
2019 let span = make_span(&src, left.span().start..right.span().end);
2020 Expr::Binary {
2021 op,
2022 left: Box::new(left),
2023 right: Box::new(right),
2024 span,
2025 }
2026 }
2027 })
2028 .boxed();
2029
2030 let concat_op = just(Token::PlusPlus).to(BinOp::Concat);
2032
2033 let concat = add_sub
2034 .clone()
2035 .then(concat_op.then(add_sub).repeated())
2036 .foldl({
2037 let src = src.clone();
2038 move |left, (op, right)| {
2039 let span = make_span(&src, left.span().start..right.span().end);
2040 Expr::Binary {
2041 op,
2042 left: Box::new(left),
2043 right: Box::new(right),
2044 span,
2045 }
2046 }
2047 })
2048 .boxed();
2049
2050 let cmp_op = choice((
2052 just(Token::Le).to(BinOp::Le),
2053 just(Token::Ge).to(BinOp::Ge),
2054 just(Token::Lt).to(BinOp::Lt),
2055 just(Token::Gt).to(BinOp::Gt),
2056 ));
2057
2058 let comparison = concat
2059 .clone()
2060 .then(cmp_op.then(concat).repeated())
2061 .foldl({
2062 let src = src.clone();
2063 move |left, (op, right)| {
2064 let span = make_span(&src, left.span().start..right.span().end);
2065 Expr::Binary {
2066 op,
2067 left: Box::new(left),
2068 right: Box::new(right),
2069 span,
2070 }
2071 }
2072 })
2073 .boxed();
2074
2075 let eq_op = just(Token::EqEq)
2077 .to(BinOp::Eq)
2078 .or(just(Token::Ne).to(BinOp::Ne));
2079
2080 let equality = comparison
2081 .clone()
2082 .then(eq_op.then(comparison).repeated())
2083 .foldl({
2084 let src = src.clone();
2085 move |left, (op, right)| {
2086 let span = make_span(&src, left.span().start..right.span().end);
2087 Expr::Binary {
2088 op,
2089 left: Box::new(left),
2090 right: Box::new(right),
2091 span,
2092 }
2093 }
2094 })
2095 .boxed();
2096
2097 let and_op = just(Token::And).to(BinOp::And);
2099
2100 let and = equality
2101 .clone()
2102 .then(and_op.then(equality).repeated())
2103 .foldl({
2104 let src = src.clone();
2105 move |left, (op, right)| {
2106 let span = make_span(&src, left.span().start..right.span().end);
2107 Expr::Binary {
2108 op,
2109 left: Box::new(left),
2110 right: Box::new(right),
2111 span,
2112 }
2113 }
2114 })
2115 .boxed();
2116
2117 let or_op = just(Token::Or).to(BinOp::Or);
2119
2120 let or_expr = and.clone().then(or_op.then(and).repeated()).foldl({
2121 let src = src.clone();
2122 move |left, (op, right)| {
2123 let span = make_span(&src, left.span().start..right.span().end);
2124 Expr::Binary {
2125 op,
2126 left: Box::new(left),
2127 right: Box::new(right),
2128 span,
2129 }
2130 }
2131 });
2132
2133 let catch_recovery = just(Token::KwCatch)
2136 .ignore_then(
2137 ident_token_parser(src.clone())
2138 .delimited_by(just(Token::LParen), just(Token::RParen))
2139 .or_not(),
2140 )
2141 .then(
2142 expr.clone()
2143 .delimited_by(just(Token::LBrace), just(Token::RBrace)),
2144 );
2145
2146 or_expr.then(catch_recovery.or_not()).map_with_span({
2147 let src = src.clone();
2148 move |(inner, catch_opt), span: Range<usize>| match catch_opt {
2149 Some((error_bind, recovery)) => Expr::Catch {
2150 expr: Box::new(inner),
2151 error_bind,
2152 recovery: Box::new(recovery),
2153 span: make_span(&src, span),
2154 },
2155 None => inner,
2156 }
2157 })
2158 })
2159 .boxed()
2160}
2161
2162fn make_span(source: &Arc<str>, range: Range<usize>) -> Span {
2168 Span::new(range.start, range.end, Arc::clone(source))
2169}
2170
2171fn ident_token_parser(source: Arc<str>) -> impl Parser<Token, Ident, Error = ParseError> + Clone {
2173 filter_map(move |span: Range<usize>, token| match token {
2174 Token::Ident => {
2175 let text = &source[span.start..span.end];
2176 Ok(Ident::new(text.to_string(), make_span(&source, span)))
2177 }
2178 _ => Err(Simple::expected_input_found(
2179 span,
2180 vec![Some(Token::Ident)],
2181 Some(token),
2182 )),
2183 })
2184}
2185
2186fn var_parser(source: Arc<str>) -> impl Parser<Token, Expr, Error = ParseError> + Clone {
2188 ident_token_parser(source.clone()).map_with_span(move |name, span: Range<usize>| Expr::Var {
2189 name,
2190 span: make_span(&source, span),
2191 })
2192}
2193
2194fn type_parser(source: Arc<str>) -> impl Parser<Token, TypeExpr, Error = ParseError> + Clone {
2196 recursive(move |ty| {
2197 let src = source.clone();
2198
2199 let primitive = choice((
2200 just(Token::TyInt).to(TypeExpr::Int),
2201 just(Token::TyFloat).to(TypeExpr::Float),
2202 just(Token::TyBool).to(TypeExpr::Bool),
2203 just(Token::TyString).to(TypeExpr::String),
2204 just(Token::TyUnit).to(TypeExpr::Unit),
2205 ));
2206
2207 let list_ty = just(Token::TyList)
2208 .ignore_then(just(Token::Lt))
2209 .ignore_then(ty.clone())
2210 .then_ignore(just(Token::Gt))
2211 .map(|inner| TypeExpr::List(Box::new(inner)));
2212
2213 let option_ty = just(Token::TyOption)
2214 .ignore_then(just(Token::Lt))
2215 .ignore_then(ty.clone())
2216 .then_ignore(just(Token::Gt))
2217 .map(|inner| TypeExpr::Option(Box::new(inner)));
2218
2219 let oracle_ty = just(Token::TyOracle)
2220 .ignore_then(just(Token::Lt))
2221 .ignore_then(ty.clone())
2222 .then_ignore(just(Token::Gt))
2223 .map(|inner| TypeExpr::Oracle(Box::new(inner)));
2224
2225 let agent_ty = just(Token::TyAgent)
2226 .ignore_then(just(Token::Lt))
2227 .ignore_then(ident_token_parser(src.clone()))
2228 .then_ignore(just(Token::Gt))
2229 .map(TypeExpr::Agent);
2230
2231 let named_ty = ident_token_parser(src.clone())
2233 .then(
2234 ty.clone()
2235 .separated_by(just(Token::Comma))
2236 .allow_trailing()
2237 .delimited_by(just(Token::Lt), just(Token::Gt))
2238 .or_not(),
2239 )
2240 .map(|(name, type_args)| TypeExpr::Named(name, type_args.unwrap_or_default()));
2241
2242 let fn_ty = just(Token::TyFn)
2244 .ignore_then(
2245 ty.clone()
2246 .separated_by(just(Token::Comma))
2247 .allow_trailing()
2248 .delimited_by(just(Token::LParen), just(Token::RParen)),
2249 )
2250 .then_ignore(just(Token::Arrow))
2251 .then(ty.clone())
2252 .map(|(params, ret)| TypeExpr::Fn(params, Box::new(ret)));
2253
2254 let map_ty = just(Token::TyMap)
2256 .ignore_then(just(Token::Lt))
2257 .ignore_then(ty.clone())
2258 .then_ignore(just(Token::Comma))
2259 .then(ty.clone())
2260 .then_ignore(just(Token::Gt))
2261 .map(|(k, v)| TypeExpr::Map(Box::new(k), Box::new(v)));
2262
2263 let result_ty = just(Token::TyResult)
2265 .ignore_then(just(Token::Lt))
2266 .ignore_then(ty.clone())
2267 .then_ignore(just(Token::Comma))
2268 .then(ty.clone())
2269 .then_ignore(just(Token::Gt))
2270 .map(|(ok, err)| TypeExpr::Result(Box::new(ok), Box::new(err)));
2271
2272 let tuple_ty = ty
2274 .clone()
2275 .separated_by(just(Token::Comma))
2276 .at_least(2)
2277 .allow_trailing()
2278 .delimited_by(just(Token::LParen), just(Token::RParen))
2279 .map(TypeExpr::Tuple);
2280
2281 primitive
2282 .or(list_ty)
2283 .or(option_ty)
2284 .or(oracle_ty)
2285 .or(agent_ty)
2286 .or(fn_ty)
2287 .or(map_ty)
2288 .or(result_ty)
2289 .or(tuple_ty)
2290 .or(named_ty)
2291 })
2292}
2293
2294fn for_pattern_parser(source: Arc<str>) -> impl Parser<Token, Pattern, Error = ParseError> + Clone {
2297 recursive(move |pattern| {
2298 let src = source.clone();
2299 let src2 = source.clone();
2300
2301 let binding = ident_token_parser(src.clone()).map_with_span({
2303 let src = src.clone();
2304 move |name, span: Range<usize>| Pattern::Binding {
2305 name,
2306 span: make_span(&src, span),
2307 }
2308 });
2309
2310 let tuple_pattern = pattern
2312 .clone()
2313 .separated_by(just(Token::Comma))
2314 .at_least(2)
2315 .allow_trailing()
2316 .delimited_by(just(Token::LParen), just(Token::RParen))
2317 .map_with_span({
2318 let src = src2.clone();
2319 move |elements, span: Range<usize>| Pattern::Tuple {
2320 elements,
2321 span: make_span(&src, span),
2322 }
2323 });
2324
2325 tuple_pattern.or(binding)
2326 })
2327}
2328
2329fn pattern_parser(source: Arc<str>) -> impl Parser<Token, Pattern, Error = ParseError> + Clone {
2331 recursive(move |pattern| {
2332 let src = source.clone();
2333 let src2 = source.clone();
2334 let src3 = source.clone();
2335 let src4 = source.clone();
2336 let src5 = source.clone();
2337
2338 let wildcard = filter_map({
2340 let src = src.clone();
2341 move |span: Range<usize>, token| match &token {
2342 Token::Ident if src[span.start..span.end].eq("_") => Ok(()),
2343 _ => Err(Simple::expected_input_found(span, vec![], Some(token))),
2344 }
2345 })
2346 .map_with_span(move |_, span: Range<usize>| Pattern::Wildcard {
2347 span: make_span(&src2, span),
2348 });
2349
2350 let lit_int = filter_map({
2352 let src = src3.clone();
2353 move |span: Range<usize>, token| match token {
2354 Token::IntLit => {
2355 let text = &src[span.start..span.end];
2356 text.parse::<i64>()
2357 .map(Literal::Int)
2358 .map_err(|_| Simple::custom(span, "invalid integer literal"))
2359 }
2360 _ => Err(Simple::expected_input_found(
2361 span,
2362 vec![Some(Token::IntLit)],
2363 Some(token),
2364 )),
2365 }
2366 })
2367 .map_with_span({
2368 let src = src3.clone();
2369 move |value, span: Range<usize>| Pattern::Literal {
2370 value,
2371 span: make_span(&src, span),
2372 }
2373 });
2374
2375 let lit_bool = just(Token::KwTrue)
2376 .to(Literal::Bool(true))
2377 .or(just(Token::KwFalse).to(Literal::Bool(false)))
2378 .map_with_span({
2379 let src = src3.clone();
2380 move |value, span: Range<usize>| Pattern::Literal {
2381 value,
2382 span: make_span(&src, span),
2383 }
2384 });
2385
2386 let tuple_pattern = pattern
2388 .clone()
2389 .separated_by(just(Token::Comma))
2390 .at_least(2)
2391 .allow_trailing()
2392 .delimited_by(just(Token::LParen), just(Token::RParen))
2393 .map_with_span({
2394 let src = src5.clone();
2395 move |elements, span: Range<usize>| Pattern::Tuple {
2396 elements,
2397 span: make_span(&src, span),
2398 }
2399 });
2400
2401 let pattern_enum_name_parser = {
2405 let src = src4.clone();
2406 ident_token_parser(src.clone()).or(just(Token::TyOption)
2407 .or(just(Token::TyResult))
2408 .map_with_span({
2409 let src = src.clone();
2410 move |token, span: Range<usize>| {
2411 let name = match token {
2412 Token::TyOption => "Option",
2413 Token::TyResult => "Result",
2414 _ => unreachable!(),
2415 };
2416 Ident {
2417 name: name.to_string(),
2418 span: make_span(&src, span),
2419 }
2420 }
2421 }))
2422 };
2423 let qualified_variant_with_payload = pattern_enum_name_parser
2424 .then_ignore(just(Token::ColonColon))
2425 .then(ident_token_parser(src4.clone()))
2426 .then(
2427 pattern
2428 .clone()
2429 .delimited_by(just(Token::LParen), just(Token::RParen))
2430 .or_not(),
2431 )
2432 .map_with_span({
2433 let src = src4.clone();
2434 move |((enum_name, variant), payload), span: Range<usize>| Pattern::Variant {
2435 enum_name: Some(enum_name),
2436 variant,
2437 payload: payload.map(Box::new),
2438 span: make_span(&src, span),
2439 }
2440 });
2441
2442 let unqualified_with_payload = ident_token_parser(src4.clone())
2444 .then(
2445 pattern
2446 .clone()
2447 .delimited_by(just(Token::LParen), just(Token::RParen))
2448 .or_not(),
2449 )
2450 .map_with_span({
2451 let src = src4.clone();
2452 move |(name, payload), span: Range<usize>| {
2453 if name.name.chars().next().is_some_and(|c| c.is_uppercase())
2456 || payload.is_some()
2457 {
2458 Pattern::Variant {
2459 enum_name: None,
2460 variant: name,
2461 payload: payload.map(Box::new),
2462 span: make_span(&src, span),
2463 }
2464 } else {
2465 Pattern::Binding {
2466 name,
2467 span: make_span(&src, span),
2468 }
2469 }
2470 }
2471 });
2472
2473 wildcard
2475 .or(tuple_pattern)
2476 .or(qualified_variant_with_payload)
2477 .or(lit_int)
2478 .or(lit_bool)
2479 .or(unqualified_with_payload)
2480 })
2481}
2482
2483fn literal_parser(source: Arc<str>) -> impl Parser<Token, Expr, Error = ParseError> + Clone {
2485 let src = source.clone();
2486 let src2 = source.clone();
2487 let src3 = source.clone();
2488 let src4 = source.clone();
2489 let src5 = source.clone();
2490
2491 let int_lit = filter_map(move |span: Range<usize>, token| match token {
2492 Token::IntLit => {
2493 let text = &src[span.start..span.end];
2494 text.parse::<i64>()
2495 .map(Literal::Int)
2496 .map_err(|_| Simple::custom(span, "invalid integer literal"))
2497 }
2498 _ => Err(Simple::expected_input_found(
2499 span,
2500 vec![Some(Token::IntLit)],
2501 Some(token),
2502 )),
2503 })
2504 .map_with_span(move |value, span: Range<usize>| Expr::Literal {
2505 value,
2506 span: make_span(&src2, span),
2507 });
2508
2509 let float_lit = filter_map(move |span: Range<usize>, token| match token {
2510 Token::FloatLit => {
2511 let text = &src3[span.start..span.end];
2512 text.parse::<f64>()
2513 .map(Literal::Float)
2514 .map_err(|_| Simple::custom(span, "invalid float literal"))
2515 }
2516 _ => Err(Simple::expected_input_found(
2517 span,
2518 vec![Some(Token::FloatLit)],
2519 Some(token),
2520 )),
2521 })
2522 .map_with_span(move |value, span: Range<usize>| Expr::Literal {
2523 value,
2524 span: make_span(&src4, span),
2525 });
2526
2527 let src6 = source.clone();
2528 let string_lit = filter_map(move |span: Range<usize>, token| match token {
2529 Token::StringLit => {
2530 let text = &src5[span.start..span.end];
2531 let inner = &text[1..text.len() - 1];
2532 let parts = parse_string_template(inner, &make_span(&src5, span.clone()));
2533 Ok(parts)
2534 }
2535 _ => Err(Simple::expected_input_found(
2536 span,
2537 vec![Some(Token::StringLit)],
2538 Some(token),
2539 )),
2540 })
2541 .map_with_span(move |parts, span: Range<usize>| {
2542 let span = make_span(&src6, span);
2543 if parts.len() == 1 {
2545 if let StringPart::Literal(s) = &parts[0] {
2546 return Expr::Literal {
2547 value: Literal::String(s.clone()),
2548 span,
2549 };
2550 }
2551 }
2552 Expr::StringInterp {
2554 template: StringTemplate {
2555 parts,
2556 span: span.clone(),
2557 },
2558 span,
2559 }
2560 });
2561
2562 let bool_lit = just(Token::KwTrue)
2563 .to(Literal::Bool(true))
2564 .or(just(Token::KwFalse).to(Literal::Bool(false)))
2565 .map_with_span(move |value, _span: Range<usize>| Expr::Literal {
2566 value,
2567 span: Span::dummy(), });
2569
2570 int_lit.or(float_lit).or(string_lit).or(bool_lit)
2571}
2572
2573fn string_template_parser(
2575 source: Arc<str>,
2576) -> impl Parser<Token, StringTemplate, Error = ParseError> + Clone {
2577 filter_map(move |span: Range<usize>, token| match token {
2578 Token::StringLit => {
2579 let text = &source[span.start..span.end];
2580 let inner = &text[1..text.len() - 1];
2581 let parts = parse_string_template(inner, &make_span(&source, span.clone()));
2582 Ok(StringTemplate {
2583 parts,
2584 span: make_span(&source, span),
2585 })
2586 }
2587 _ => Err(Simple::expected_input_found(
2588 span,
2589 vec![Some(Token::StringLit)],
2590 Some(token),
2591 )),
2592 })
2593}
2594
2595fn parse_string_template(s: &str, span: &Span) -> Vec<StringPart> {
2598 let mut parts = Vec::new();
2599 let mut current = String::new();
2600 let mut chars = s.chars().peekable();
2601
2602 while let Some(ch) = chars.next() {
2603 if ch == '{' {
2604 if !current.is_empty() {
2605 parts.push(StringPart::Literal(std::mem::take(&mut current)));
2606 }
2607
2608 let mut expr_str = String::new();
2610 let mut brace_depth = 1;
2611 let mut string_quote: Option<char> = None; let mut escape_next = false;
2613
2614 while let Some(&c) = chars.peek() {
2615 if escape_next {
2616 expr_str.push(c);
2617 chars.next();
2618 escape_next = false;
2619 continue;
2620 }
2621
2622 if c == '\\' && string_quote.is_some() {
2623 escape_next = true;
2624 expr_str.push(c);
2625 chars.next();
2626 continue;
2627 }
2628
2629 if c == '"' || c == '\'' {
2631 match string_quote {
2632 None => string_quote = Some(c), Some(q) if q == c => string_quote = None, Some(_) => {} }
2636 }
2637
2638 if string_quote.is_none() {
2639 if c == '{' {
2640 brace_depth += 1;
2641 } else if c == '}' {
2642 brace_depth -= 1;
2643 if brace_depth == 0 {
2644 chars.next();
2645 break;
2646 }
2647 }
2648 }
2649
2650 expr_str.push(c);
2651 chars.next();
2652 }
2653
2654 if !expr_str.is_empty() {
2655 let expr = parse_interp_expr(&expr_str, span);
2656 parts.push(StringPart::Interpolation(Box::new(expr)));
2657 }
2658 } else if ch == '\\' {
2659 if let Some(escaped) = chars.next() {
2660 current.push(match escaped {
2661 'n' => '\n',
2662 't' => '\t',
2663 'r' => '\r',
2664 '\\' => '\\',
2665 '"' => '"',
2666 '{' => '{',
2667 '}' => '}',
2668 other => other,
2669 });
2670 }
2671 } else {
2672 current.push(ch);
2673 }
2674 }
2675
2676 if !current.is_empty() {
2677 parts.push(StringPart::Literal(current));
2678 }
2679
2680 if parts.is_empty() {
2681 parts.push(StringPart::Literal(String::new()));
2682 }
2683
2684 parts
2685}
2686
2687fn parse_interp_expr(s: &str, span: &Span) -> Expr {
2690 let trimmed = s.trim();
2691 if trimmed.is_empty() {
2692 return Expr::Literal {
2694 value: Literal::String(String::new()),
2695 span: span.clone(),
2696 };
2697 }
2698
2699 let lex_result = crate::lex(trimmed);
2701 let (tokens, source) = match lex_result {
2702 Ok(result) => (result.tokens().to_vec(), trimmed.to_string()),
2703 Err(_) => {
2704 return Expr::Var {
2706 name: Ident::new(trimmed.to_string(), span.clone()),
2707 span: span.clone(),
2708 };
2709 }
2710 };
2711
2712 if tokens.is_empty() {
2713 return Expr::Var {
2714 name: Ident::new(trimmed.to_string(), span.clone()),
2715 span: span.clone(),
2716 };
2717 }
2718
2719 let mut parser = InterpExprParser::new(&tokens, &source, span.clone());
2721 parser.parse_expr()
2722}
2723
2724struct InterpExprParser<'a> {
2727 tokens: &'a [crate::Spanned],
2728 source: &'a str,
2729 pos: usize,
2730 span: Span,
2731}
2732
2733impl<'a> InterpExprParser<'a> {
2734 fn new(tokens: &'a [crate::Spanned], source: &'a str, span: Span) -> Self {
2735 Self {
2736 tokens,
2737 source,
2738 pos: 0,
2739 span,
2740 }
2741 }
2742
2743 fn current(&self) -> Option<&Token> {
2744 self.tokens.get(self.pos).map(|s| &s.token)
2745 }
2746
2747 fn current_text(&self) -> Option<&str> {
2748 self.tokens
2749 .get(self.pos)
2750 .map(|s| &self.source[s.start..s.end])
2751 }
2752
2753 fn advance(&mut self) {
2754 if self.pos < self.tokens.len() {
2755 self.pos += 1;
2756 }
2757 }
2758
2759 fn parse_expr(&mut self) -> Expr {
2760 self.parse_or()
2761 }
2762
2763 fn parse_or(&mut self) -> Expr {
2764 let mut left = self.parse_and();
2765 while matches!(self.current(), Some(Token::Or)) {
2766 self.advance();
2767 let right = self.parse_and();
2768 left = Expr::Binary {
2769 left: Box::new(left),
2770 op: BinOp::Or,
2771 right: Box::new(right),
2772 span: self.span.clone(),
2773 };
2774 }
2775 left
2776 }
2777
2778 fn parse_and(&mut self) -> Expr {
2779 let mut left = self.parse_comparison();
2780 while matches!(self.current(), Some(Token::And)) {
2781 self.advance();
2782 let right = self.parse_comparison();
2783 left = Expr::Binary {
2784 left: Box::new(left),
2785 op: BinOp::And,
2786 right: Box::new(right),
2787 span: self.span.clone(),
2788 };
2789 }
2790 left
2791 }
2792
2793 fn parse_comparison(&mut self) -> Expr {
2794 let mut left = self.parse_additive();
2795 loop {
2796 let op = match self.current() {
2797 Some(Token::EqEq) => BinOp::Eq,
2798 Some(Token::Ne) => BinOp::Ne,
2799 Some(Token::Lt) => BinOp::Lt,
2800 Some(Token::Le) => BinOp::Le,
2801 Some(Token::Gt) => BinOp::Gt,
2802 Some(Token::Ge) => BinOp::Ge,
2803 _ => break,
2804 };
2805 self.advance();
2806 let right = self.parse_additive();
2807 left = Expr::Binary {
2808 left: Box::new(left),
2809 op,
2810 right: Box::new(right),
2811 span: self.span.clone(),
2812 };
2813 }
2814 left
2815 }
2816
2817 fn parse_additive(&mut self) -> Expr {
2818 let mut left = self.parse_multiplicative();
2819 loop {
2820 let op = match self.current() {
2821 Some(Token::Plus) => BinOp::Add,
2822 Some(Token::Minus) => BinOp::Sub,
2823 Some(Token::PlusPlus) => BinOp::Concat,
2824 _ => break,
2825 };
2826 self.advance();
2827 let right = self.parse_multiplicative();
2828 left = Expr::Binary {
2829 left: Box::new(left),
2830 op,
2831 right: Box::new(right),
2832 span: self.span.clone(),
2833 };
2834 }
2835 left
2836 }
2837
2838 fn parse_multiplicative(&mut self) -> Expr {
2839 let mut left = self.parse_unary();
2840 loop {
2841 let op = match self.current() {
2842 Some(Token::Star) => BinOp::Mul,
2843 Some(Token::Slash) => BinOp::Div,
2844 Some(Token::Percent) => BinOp::Rem,
2845 _ => break,
2846 };
2847 self.advance();
2848 let right = self.parse_unary();
2849 left = Expr::Binary {
2850 left: Box::new(left),
2851 op,
2852 right: Box::new(right),
2853 span: self.span.clone(),
2854 };
2855 }
2856 left
2857 }
2858
2859 fn parse_unary(&mut self) -> Expr {
2860 match self.current() {
2861 Some(Token::Minus) => {
2862 self.advance();
2863 let operand = self.parse_unary();
2864 Expr::Unary {
2865 op: UnaryOp::Neg,
2866 operand: Box::new(operand),
2867 span: self.span.clone(),
2868 }
2869 }
2870 Some(Token::Bang) => {
2871 self.advance();
2872 let operand = self.parse_unary();
2873 Expr::Unary {
2874 op: UnaryOp::Not,
2875 operand: Box::new(operand),
2876 span: self.span.clone(),
2877 }
2878 }
2879 _ => self.parse_postfix(),
2880 }
2881 }
2882
2883 fn parse_postfix(&mut self) -> Expr {
2884 let mut expr = self.parse_primary();
2885
2886 loop {
2887 match self.current() {
2888 Some(Token::Dot) => {
2889 self.advance();
2890 match self.current() {
2891 Some(Token::IntLit) => {
2892 let text = self.current_text().unwrap_or("0");
2893 let index = text.parse::<usize>().unwrap_or(0);
2894 self.advance();
2895 expr = Expr::TupleIndex {
2896 tuple: Box::new(expr),
2897 index,
2898 span: self.span.clone(),
2899 };
2900 }
2901 Some(Token::Ident) => {
2902 let name = self.current_text().unwrap_or("").to_string();
2903 let field = Ident::new(name, self.span.clone());
2904 self.advance();
2905 expr = Expr::FieldAccess {
2906 object: Box::new(expr),
2907 field,
2908 span: self.span.clone(),
2909 };
2910 }
2911 _ => break,
2912 }
2913 }
2914 Some(Token::ColonColon) if matches!(expr, Expr::Var { .. }) => {
2915 if let Expr::Var { name, .. } = expr {
2917 self.advance(); let type_args = self.parse_turbofish();
2919 if matches!(self.current(), Some(Token::LParen)) {
2921 self.advance();
2922 let args = self.parse_args();
2923 expr = Expr::Call {
2924 name,
2925 type_args,
2926 args,
2927 span: self.span.clone(),
2928 };
2929 } else {
2930 expr = Expr::Call {
2933 name,
2934 type_args,
2935 args: vec![],
2936 span: self.span.clone(),
2937 };
2938 }
2939 }
2940 }
2941 Some(Token::LParen) if matches!(expr, Expr::Var { .. }) => {
2942 if let Expr::Var { name, .. } = expr {
2944 self.advance();
2945 let args = self.parse_args();
2946 expr = Expr::Call {
2947 name,
2948 type_args: vec![],
2949 args,
2950 span: self.span.clone(),
2951 };
2952 }
2953 }
2954 _ => break,
2955 }
2956 }
2957
2958 expr
2959 }
2960
2961 fn parse_primary(&mut self) -> Expr {
2962 match self.current() {
2963 Some(Token::IntLit) => {
2964 let text = self.current_text().unwrap_or("0");
2965 let n = text.parse::<i64>().unwrap_or(0);
2966 self.advance();
2967 Expr::Literal {
2968 value: Literal::Int(n),
2969 span: self.span.clone(),
2970 }
2971 }
2972 Some(Token::FloatLit) => {
2973 let text = self.current_text().unwrap_or("0.0");
2974 let f = text.parse::<f64>().unwrap_or(0.0);
2975 self.advance();
2976 Expr::Literal {
2977 value: Literal::Float(f),
2978 span: self.span.clone(),
2979 }
2980 }
2981 Some(Token::StringLit) => {
2982 let text = self.current_text().unwrap_or("\"\"");
2983 let s = if text.len() >= 2 {
2985 text[1..text.len() - 1].to_string()
2986 } else {
2987 String::new()
2988 };
2989 self.advance();
2990 Expr::Literal {
2991 value: Literal::String(s),
2992 span: self.span.clone(),
2993 }
2994 }
2995 Some(Token::KwTrue) => {
2996 self.advance();
2997 Expr::Literal {
2998 value: Literal::Bool(true),
2999 span: self.span.clone(),
3000 }
3001 }
3002 Some(Token::KwFalse) => {
3003 self.advance();
3004 Expr::Literal {
3005 value: Literal::Bool(false),
3006 span: self.span.clone(),
3007 }
3008 }
3009 Some(Token::Ident) => {
3010 let name = self.current_text().unwrap_or("").to_string();
3011 self.advance();
3012 Expr::Var {
3013 name: Ident::new(name, self.span.clone()),
3014 span: self.span.clone(),
3015 }
3016 }
3017 Some(Token::KwSelf) => {
3018 self.advance();
3019 if matches!(self.current(), Some(Token::Dot)) {
3021 self.advance();
3022 if let Some(Token::Ident) = self.current() {
3023 let field_name = self.current_text().unwrap_or("").to_string();
3024 let field = Ident::new(field_name, self.span.clone());
3025 self.advance();
3026 return Expr::SelfField {
3027 field,
3028 span: self.span.clone(),
3029 };
3030 }
3031 }
3032 Expr::Var {
3033 name: Ident::new("self".to_string(), self.span.clone()),
3034 span: self.span.clone(),
3035 }
3036 }
3037 Some(Token::LParen) => {
3038 self.advance();
3039 let inner = self.parse_expr();
3040 if matches!(self.current(), Some(Token::RParen)) {
3041 self.advance();
3042 }
3043 Expr::Paren {
3044 inner: Box::new(inner),
3045 span: self.span.clone(),
3046 }
3047 }
3048 Some(Token::LBracket) => {
3049 self.advance();
3051 let mut elements = Vec::new();
3052 while !matches!(self.current(), Some(Token::RBracket) | None) {
3053 elements.push(self.parse_expr());
3054 if matches!(self.current(), Some(Token::Comma)) {
3055 self.advance();
3056 } else {
3057 break;
3058 }
3059 }
3060 if matches!(self.current(), Some(Token::RBracket)) {
3061 self.advance();
3062 }
3063 Expr::List {
3064 elements,
3065 span: self.span.clone(),
3066 }
3067 }
3068 _ => {
3069 Expr::Literal {
3071 value: Literal::String(String::new()),
3072 span: self.span.clone(),
3073 }
3074 }
3075 }
3076 }
3077
3078 fn parse_args(&mut self) -> Vec<Expr> {
3079 let mut args = Vec::new();
3080 while !matches!(self.current(), Some(Token::RParen) | None) {
3081 args.push(self.parse_expr());
3082 if matches!(self.current(), Some(Token::Comma)) {
3083 self.advance();
3084 } else {
3085 break;
3086 }
3087 }
3088 if matches!(self.current(), Some(Token::RParen)) {
3089 self.advance();
3090 }
3091 args
3092 }
3093
3094 fn parse_turbofish(&mut self) -> Vec<TypeExpr> {
3097 let mut type_args = Vec::new();
3098
3099 if !matches!(self.current(), Some(Token::Lt)) {
3101 return type_args;
3102 }
3103 self.advance();
3104
3105 loop {
3107 if matches!(self.current(), Some(Token::Gt) | None) {
3108 break;
3109 }
3110
3111 if let Some(ty) = self.parse_type() {
3112 type_args.push(ty);
3113 }
3114
3115 if matches!(self.current(), Some(Token::Comma)) {
3116 self.advance();
3117 } else {
3118 break;
3119 }
3120 }
3121
3122 if matches!(self.current(), Some(Token::Gt)) {
3124 self.advance();
3125 }
3126
3127 type_args
3128 }
3129
3130 fn parse_type(&mut self) -> Option<TypeExpr> {
3132 let name = match self.current() {
3133 Some(Token::Ident) => self.current_text().unwrap_or("").to_string(),
3134 Some(Token::TyInt) => "Int".to_string(),
3135 Some(Token::TyFloat) => "Float".to_string(),
3136 Some(Token::TyBool) => "Bool".to_string(),
3137 Some(Token::TyString) => "String".to_string(),
3138 Some(Token::TyUnit) => "Unit".to_string(),
3139 _ => return None,
3140 };
3141 self.advance();
3142
3143 if matches!(self.current(), Some(Token::Lt)) {
3145 self.advance();
3146 let mut params = Vec::new();
3147
3148 loop {
3149 if matches!(self.current(), Some(Token::Gt) | None) {
3150 break;
3151 }
3152
3153 if let Some(param) = self.parse_type() {
3154 params.push(param);
3155 }
3156
3157 if matches!(self.current(), Some(Token::Comma)) {
3158 self.advance();
3159 } else {
3160 break;
3161 }
3162 }
3163
3164 if matches!(self.current(), Some(Token::Gt)) {
3165 self.advance();
3166 }
3167
3168 Some(match name.as_str() {
3170 "List" => {
3171 if let Some(elem) = params.into_iter().next() {
3172 TypeExpr::List(Box::new(elem))
3173 } else {
3174 TypeExpr::Named(Ident::new(name, self.span.clone()), vec![])
3175 }
3176 }
3177 "Map" => {
3178 let mut iter = params.into_iter();
3179 if let (Some(k), Some(v)) = (iter.next(), iter.next()) {
3180 TypeExpr::Map(Box::new(k), Box::new(v))
3181 } else {
3182 TypeExpr::Named(Ident::new(name, self.span.clone()), vec![])
3183 }
3184 }
3185 "Option" => {
3186 if let Some(inner) = params.into_iter().next() {
3187 TypeExpr::Option(Box::new(inner))
3188 } else {
3189 TypeExpr::Named(Ident::new(name, self.span.clone()), vec![])
3190 }
3191 }
3192 "Result" => {
3193 let mut iter = params.into_iter();
3194 if let (Some(ok), Some(err)) = (iter.next(), iter.next()) {
3195 TypeExpr::Result(Box::new(ok), Box::new(err))
3196 } else {
3197 TypeExpr::Named(Ident::new(name, self.span.clone()), vec![])
3198 }
3199 }
3200 _ => {
3201 TypeExpr::Named(Ident::new(name, self.span.clone()), params)
3203 }
3204 })
3205 } else {
3206 Some(match name.as_str() {
3208 "Int" => TypeExpr::Int,
3209 "Float" => TypeExpr::Float,
3210 "Bool" => TypeExpr::Bool,
3211 "String" => TypeExpr::String,
3212 "Unit" => TypeExpr::Unit,
3213 _ => TypeExpr::Named(Ident::new(name, self.span.clone()), vec![]),
3214 })
3215 }
3216 }
3217}
3218
3219#[cfg(test)]
3224mod tests {
3225 use super::*;
3226 use crate::lex;
3227
3228 fn parse_str(source: &str) -> (Option<Program>, Vec<ParseError>) {
3229 let lex_result = lex(source).expect("lexing should succeed");
3230 let source_arc: Arc<str> = Arc::from(source);
3231 parse(lex_result.tokens(), source_arc)
3232 }
3233
3234 #[test]
3235 fn parse_minimal_program() {
3236 let source = r#"
3237 agent Main {
3238 on start {
3239 yield(42);
3240 }
3241 }
3242 run Main;
3243 "#;
3244
3245 let (prog, errors) = parse_str(source);
3246 assert!(errors.is_empty(), "errors: {errors:?}");
3247 let prog = prog.expect("should parse");
3248
3249 assert_eq!(prog.agents.len(), 1);
3250 assert_eq!(prog.agents[0].name.name, "Main");
3251 assert_eq!(prog.run_agent.as_ref().unwrap().name, "Main");
3252 }
3253
3254 #[test]
3255 fn parse_agent_with_beliefs() {
3256 let source = r#"
3257 agent Researcher {
3258 topic: String
3259 max_words: Int
3260
3261 on start {
3262 yield(self.topic);
3263 }
3264 }
3265 run Researcher;
3266 "#;
3267
3268 let (prog, errors) = parse_str(source);
3269 assert!(errors.is_empty(), "errors: {errors:?}");
3270 let prog = prog.expect("should parse");
3271
3272 assert_eq!(prog.agents[0].beliefs.len(), 2);
3273 assert_eq!(prog.agents[0].beliefs[0].name.name, "topic");
3274 assert_eq!(prog.agents[0].beliefs[1].name.name, "max_words");
3275 }
3276
3277 #[test]
3278 fn parse_multiple_handlers() {
3279 let source = r#"
3280 agent Worker {
3281 on start {
3282 print("started");
3283 }
3284
3285 on message(msg: String) {
3286 print(msg);
3287 }
3288
3289 on stop {
3290 print("stopped");
3291 }
3292 }
3293 run Worker;
3294 "#;
3295
3296 let (prog, errors) = parse_str(source);
3297 assert!(errors.is_empty(), "errors: {errors:?}");
3298 let prog = prog.expect("should parse");
3299
3300 assert_eq!(prog.agents[0].handlers.len(), 3);
3301 assert_eq!(prog.agents[0].handlers[0].event, EventKind::Start);
3302 assert!(matches!(
3303 prog.agents[0].handlers[1].event,
3304 EventKind::Message { .. }
3305 ));
3306 assert_eq!(prog.agents[0].handlers[2].event, EventKind::Stop);
3307 }
3308
3309 #[test]
3310 fn parse_v2_lifecycle_hooks() {
3311 let source = r#"
3312 agent StatefulWorker {
3313 on waking {
3314 // Load persisted state
3315 trace("waking up");
3316 }
3317
3318 on start {
3319 trace("started");
3320 }
3321
3322 on pause {
3323 // Save state before pause
3324 trace("pausing");
3325 }
3326
3327 on resume {
3328 trace("resuming");
3329 }
3330
3331 on resting {
3332 // Cleanup before shutdown
3333 trace("resting");
3334 }
3335 }
3336 run StatefulWorker;
3337 "#;
3338
3339 let (prog, errors) = parse_str(source);
3340 assert!(errors.is_empty(), "errors: {errors:?}");
3341 let prog = prog.expect("should parse");
3342
3343 assert_eq!(prog.agents[0].handlers.len(), 5);
3344 assert_eq!(prog.agents[0].handlers[0].event, EventKind::Waking);
3345 assert_eq!(prog.agents[0].handlers[1].event, EventKind::Start);
3346 assert_eq!(prog.agents[0].handlers[2].event, EventKind::Pause);
3347 assert_eq!(prog.agents[0].handlers[3].event, EventKind::Resume);
3348 assert_eq!(prog.agents[0].handlers[4].event, EventKind::Resting);
3349 }
3350
3351 #[test]
3352 fn parse_persistent_beliefs() {
3353 let source = r#"
3354 agent DatabaseSteward {
3355 @persistent schema_version: Int
3356 @persistent migration_log: List<String>
3357 active_connections: Int
3358
3359 on start {
3360 yield(0);
3361 }
3362 }
3363 run DatabaseSteward;
3364 "#;
3365
3366 let (prog, errors) = parse_str(source);
3367 assert!(errors.is_empty(), "errors: {errors:?}");
3368 let prog = prog.expect("should parse");
3369
3370 assert_eq!(prog.agents[0].beliefs.len(), 3);
3371 assert!(prog.agents[0].beliefs[0].is_persistent);
3372 assert_eq!(prog.agents[0].beliefs[0].name.name, "schema_version");
3373 assert!(prog.agents[0].beliefs[1].is_persistent);
3374 assert_eq!(prog.agents[0].beliefs[1].name.name, "migration_log");
3375 assert!(!prog.agents[0].beliefs[2].is_persistent);
3376 assert_eq!(prog.agents[0].beliefs[2].name.name, "active_connections");
3377 }
3378
3379 #[test]
3380 fn parse_function() {
3381 let source = r#"
3382 fn greet(name: String) -> String {
3383 return "Hello, " ++ name;
3384 }
3385
3386 agent Main {
3387 on start {
3388 yield(greet("World"));
3389 }
3390 }
3391 run Main;
3392 "#;
3393
3394 let (prog, errors) = parse_str(source);
3395 assert!(errors.is_empty(), "errors: {errors:?}");
3396 let prog = prog.expect("should parse");
3397
3398 assert_eq!(prog.functions.len(), 1);
3399 assert_eq!(prog.functions[0].name.name, "greet");
3400 assert_eq!(prog.functions[0].params.len(), 1);
3401 }
3402
3403 #[test]
3404 fn parse_let_statement() {
3405 let source = r#"
3406 agent Main {
3407 on start {
3408 let x: Int = 42;
3409 let y = "hello";
3410 yield(x);
3411 }
3412 }
3413 run Main;
3414 "#;
3415
3416 let (prog, errors) = parse_str(source);
3417 assert!(errors.is_empty(), "errors: {errors:?}");
3418 let prog = prog.expect("should parse");
3419
3420 let stmts = &prog.agents[0].handlers[0].body.stmts;
3421 assert!(matches!(stmts[0], Stmt::Let { .. }));
3422 assert!(matches!(stmts[1], Stmt::Let { .. }));
3423 }
3424
3425 #[test]
3426 fn parse_if_statement() {
3427 let source = r#"
3428 agent Main {
3429 on start {
3430 if true {
3431 yield(1);
3432 } else {
3433 yield(2);
3434 }
3435 }
3436 }
3437 run Main;
3438 "#;
3439
3440 let (prog, errors) = parse_str(source);
3441 assert!(errors.is_empty(), "errors: {errors:?}");
3442 let prog = prog.expect("should parse");
3443
3444 let stmts = &prog.agents[0].handlers[0].body.stmts;
3445 assert!(matches!(stmts[0], Stmt::If { .. }));
3446 }
3447
3448 #[test]
3449 fn parse_for_loop() {
3450 let source = r#"
3451 agent Main {
3452 on start {
3453 for x in [1, 2, 3] {
3454 print(x);
3455 }
3456 yield(0);
3457 }
3458 }
3459 run Main;
3460 "#;
3461
3462 let (prog, errors) = parse_str(source);
3463 assert!(errors.is_empty(), "errors: {errors:?}");
3464 let prog = prog.expect("should parse");
3465
3466 let stmts = &prog.agents[0].handlers[0].body.stmts;
3467 assert!(matches!(stmts[0], Stmt::For { .. }));
3468 }
3469
3470 #[test]
3471 fn parse_spawn_await() {
3472 let source = r#"
3473 agent Worker {
3474 name: String
3475
3476 on start {
3477 yield(self.name);
3478 }
3479 }
3480
3481 agent Main {
3482 on start {
3483 let w = summon Worker { name: "test" };
3484 let result = await w;
3485 yield(result);
3486 }
3487 }
3488 run Main;
3489 "#;
3490
3491 let (prog, errors) = parse_str(source);
3492 assert!(errors.is_empty(), "errors: {errors:?}");
3493 prog.expect("should parse");
3494 }
3495
3496 #[test]
3497 fn parse_await_with_timeout() {
3498 let source = r#"
3499 agent Worker {
3500 on start {
3501 yield("done");
3502 }
3503 }
3504
3505 agent Main {
3506 on start {
3507 let w = summon Worker {};
3508 let result = await w timeout(5000);
3509 yield(result);
3510 }
3511 }
3512 run Main;
3513 "#;
3514
3515 let (prog, errors) = parse_str(source);
3516 assert!(errors.is_empty(), "errors: {errors:?}");
3517 let prog = prog.expect("should parse");
3518
3519 let main = &prog.agents[1];
3521 let stmts = &main.handlers[0].body.stmts;
3522 if let Stmt::Let { value, .. } = &stmts[1] {
3525 if let Expr::Await { timeout, .. } = value {
3526 assert!(timeout.is_some(), "timeout should be present");
3527 } else {
3528 panic!("expected Await expression");
3529 }
3530 } else {
3531 panic!("expected Let statement with value");
3532 }
3533 }
3534
3535 #[test]
3536 fn parse_divine() {
3537 let source = r#"
3538 agent Main {
3539 on start {
3540 let result = divine("What is 2+2?");
3541 yield(result);
3542 }
3543 }
3544 run Main;
3545 "#;
3546
3547 let (prog, errors) = parse_str(source);
3548 assert!(errors.is_empty(), "errors: {errors:?}");
3549 prog.expect("should parse");
3550 }
3551
3552 #[test]
3553 fn parse_binary_precedence() {
3554 let source = r#"
3555 agent Main {
3556 on start {
3557 let x = 2 + 3 * 4;
3558 yield(x);
3559 }
3560 }
3561 run Main;
3562 "#;
3563
3564 let (prog, errors) = parse_str(source);
3565 assert!(errors.is_empty(), "errors: {errors:?}");
3566 let prog = prog.expect("should parse");
3567
3568 let stmts = &prog.agents[0].handlers[0].body.stmts;
3569 if let Stmt::Let { value, .. } = &stmts[0] {
3570 if let Expr::Binary { op, .. } = value {
3571 assert_eq!(*op, BinOp::Add);
3572 } else {
3573 panic!("expected binary expression");
3574 }
3575 }
3576 }
3577
3578 #[test]
3579 fn parse_string_interpolation() {
3580 let source = r#"
3581 agent Main {
3582 on start {
3583 let name = "World";
3584 let msg = divine("Greet {name}");
3585 yield(msg);
3586 }
3587 }
3588 run Main;
3589 "#;
3590
3591 let (prog, errors) = parse_str(source);
3592 assert!(errors.is_empty(), "errors: {errors:?}");
3593 let prog = prog.expect("should parse");
3594
3595 let stmts = &prog.agents[0].handlers[0].body.stmts;
3596 if let Stmt::Let { value, .. } = &stmts[1] {
3597 if let Expr::Divine { template, .. } = value {
3598 assert!(template.has_interpolations());
3599 } else {
3600 panic!("expected infer expression");
3601 }
3602 }
3603 }
3604
3605 #[test]
3606 fn parse_single_quoted_string() {
3607 let source = r#"
3608 agent Main {
3609 on start {
3610 let x = 'hello';
3611 yield(0);
3612 }
3613 }
3614 run Main;
3615 "#;
3616
3617 let (prog, errors) = parse_str(source);
3618 assert!(errors.is_empty(), "errors: {errors:?}");
3619 let prog = prog.expect("should parse");
3620
3621 let stmts = &prog.agents[0].handlers[0].body.stmts;
3622 if let Stmt::Let { value, .. } = &stmts[0] {
3623 if let Expr::Literal {
3624 value: Literal::String(s),
3625 ..
3626 } = value
3627 {
3628 assert_eq!(s, "hello");
3629 } else {
3630 panic!("expected string literal, got {:?}", value);
3631 }
3632 } else {
3633 panic!("expected let statement");
3634 }
3635 }
3636
3637 #[test]
3638 fn parse_single_quoted_string_in_interpolation() {
3639 let source = r#"
3642 fn reverse(s: String) -> String {
3643 return s;
3644 }
3645 agent Main {
3646 on start {
3647 print("Result: {reverse('hello')}");
3648 print("Concat: {'abc' ++ 'def'}");
3649 yield(0);
3650 }
3651 }
3652 run Main;
3653 "#;
3654
3655 let (prog, errors) = parse_str(source);
3656 assert!(errors.is_empty(), "errors: {errors:?}");
3657 let prog = prog.expect("should parse");
3658
3659 let stmts = &prog.agents[0].handlers[0].body.stmts;
3661 if let Stmt::Expr {
3662 expr: Expr::Call { args, .. },
3663 ..
3664 } = &stmts[0]
3665 {
3666 if let Expr::StringInterp { template, .. } = &args[0] {
3667 assert!(template.has_interpolations());
3668 } else {
3669 panic!("expected string interpolation");
3670 }
3671 } else {
3672 panic!("expected print call");
3673 }
3674 }
3675
3676 #[test]
3681 fn recover_from_malformed_agent_continues_to_next() {
3682 let source = r#"
3684 agent Broken {
3685 x:
3686 }
3687
3688 agent Main {
3689 on start {
3690 yield(42);
3691 }
3692 }
3693 run Main;
3694 "#;
3695
3696 let (prog, errors) = parse_str(source);
3697 assert!(!errors.is_empty(), "should have parse errors");
3699 let prog = prog.expect("should produce partial AST");
3701 assert!(prog.agents.iter().any(|a| a.name.name == "Main"));
3702 }
3703
3704 #[test]
3705 fn recover_from_mismatched_braces_in_block() {
3706 let source = r#"
3707 agent Main {
3708 on start {
3709 let x = [1, 2, 3;
3710 yield(42);
3711 }
3712 }
3713 run Main;
3714 "#;
3715
3716 let (prog, errors) = parse_str(source);
3717 assert!(!errors.is_empty(), "should have parse errors");
3719 assert!(prog.is_some(), "should produce partial AST despite errors");
3720 }
3721
3722 #[test]
3723 fn parse_mod_declaration() {
3724 let source = r#"
3725 mod agents;
3726 pub mod utils;
3727
3728 agent Main {
3729 on start {
3730 yield(42);
3731 }
3732 }
3733 run Main;
3734 "#;
3735
3736 let (prog, errors) = parse_str(source);
3737 assert!(errors.is_empty(), "errors: {errors:?}");
3738 let prog = prog.expect("should parse");
3739
3740 assert_eq!(prog.mod_decls.len(), 2);
3741 assert!(!prog.mod_decls[0].is_pub);
3742 assert_eq!(prog.mod_decls[0].name.name, "agents");
3743 assert!(prog.mod_decls[1].is_pub);
3744 assert_eq!(prog.mod_decls[1].name.name, "utils");
3745 }
3746
3747 #[test]
3748 fn parse_use_simple() {
3749 let source = r#"
3750 use agents::Researcher;
3751
3752 agent Main {
3753 on start {
3754 yield(42);
3755 }
3756 }
3757 run Main;
3758 "#;
3759
3760 let (prog, errors) = parse_str(source);
3761 assert!(errors.is_empty(), "errors: {errors:?}");
3762 let prog = prog.expect("should parse");
3763
3764 assert_eq!(prog.use_decls.len(), 1);
3765 assert!(!prog.use_decls[0].is_pub);
3766 assert_eq!(prog.use_decls[0].path.len(), 2);
3767 assert_eq!(prog.use_decls[0].path[0].name, "agents");
3768 assert_eq!(prog.use_decls[0].path[1].name, "Researcher");
3769 assert!(matches!(prog.use_decls[0].kind, UseKind::Simple(None)));
3770 }
3771
3772 #[test]
3773 fn parse_use_with_alias() {
3774 let source = r#"
3775 use agents::Researcher as R;
3776
3777 agent Main {
3778 on start {
3779 yield(42);
3780 }
3781 }
3782 run Main;
3783 "#;
3784
3785 let (prog, errors) = parse_str(source);
3786 assert!(errors.is_empty(), "errors: {errors:?}");
3787 let prog = prog.expect("should parse");
3788
3789 assert_eq!(prog.use_decls.len(), 1);
3790 if let UseKind::Simple(Some(alias)) = &prog.use_decls[0].kind {
3791 assert_eq!(alias.name, "R");
3792 } else {
3793 panic!("expected Simple with alias");
3794 }
3795 }
3796
3797 #[test]
3798 fn parse_pub_agent() {
3799 let source = r#"
3800 pub agent Worker {
3801 on start {
3802 yield(42);
3803 }
3804 }
3805
3806 agent Main {
3807 on start {
3808 yield(0);
3809 }
3810 }
3811 run Main;
3812 "#;
3813
3814 let (prog, errors) = parse_str(source);
3815 assert!(errors.is_empty(), "errors: {errors:?}");
3816 let prog = prog.expect("should parse");
3817
3818 assert_eq!(prog.agents.len(), 2);
3819 assert!(prog.agents[0].is_pub);
3820 assert_eq!(prog.agents[0].name.name, "Worker");
3821 assert!(!prog.agents[1].is_pub);
3822 }
3823
3824 #[test]
3825 fn parse_pub_function() {
3826 let source = r#"
3827 pub fn helper(x: Int) -> Int {
3828 return x;
3829 }
3830
3831 agent Main {
3832 on start {
3833 yield(helper(42));
3834 }
3835 }
3836 run Main;
3837 "#;
3838
3839 let (prog, errors) = parse_str(source);
3840 assert!(errors.is_empty(), "errors: {errors:?}");
3841 let prog = prog.expect("should parse");
3842
3843 assert_eq!(prog.functions.len(), 1);
3844 assert!(prog.functions[0].is_pub);
3845 assert_eq!(prog.functions[0].name.name, "helper");
3846 }
3847
3848 #[test]
3849 fn parse_library_no_run() {
3850 let source = r#"
3852 pub agent Worker {
3853 on start {
3854 yield(42);
3855 }
3856 }
3857
3858 pub fn helper(x: Int) -> Int {
3859 return x;
3860 }
3861 "#;
3862
3863 let (prog, errors) = parse_str(source);
3864 assert!(errors.is_empty(), "errors: {errors:?}");
3865 let prog = prog.expect("should parse");
3866
3867 assert!(prog.run_agent.is_none());
3868 assert_eq!(prog.agents.len(), 1);
3869 assert_eq!(prog.functions.len(), 1);
3870 }
3871
3872 #[test]
3873 fn recover_multiple_errors_reported() {
3874 let source = r#"
3876 agent A {
3877 x:
3878 }
3879
3880 agent Main {
3881 on start {
3882 yield(42);
3883 }
3884 }
3885 run Main;
3886 "#;
3887
3888 let (prog, errors) = parse_str(source);
3889 if errors.is_empty() {
3893 let prog = prog.expect("should have AST with recovery");
3895 assert!(prog.agents.iter().any(|a| a.name.name == "Main"));
3896 }
3897 }
3899
3900 #[test]
3901 fn parse_record_declaration() {
3902 let source = r#"
3903 record Point {
3904 x: Int,
3905 y: Int,
3906 }
3907
3908 agent Main {
3909 on start {
3910 yield(0);
3911 }
3912 }
3913 run Main;
3914 "#;
3915
3916 let (prog, errors) = parse_str(source);
3917 assert!(errors.is_empty(), "errors: {errors:?}");
3918 let prog = prog.expect("should parse");
3919
3920 assert_eq!(prog.records.len(), 1);
3921 assert!(!prog.records[0].is_pub);
3922 assert_eq!(prog.records[0].name.name, "Point");
3923 assert_eq!(prog.records[0].fields.len(), 2);
3924 assert_eq!(prog.records[0].fields[0].name.name, "x");
3925 assert_eq!(prog.records[0].fields[1].name.name, "y");
3926 }
3927
3928 #[test]
3929 fn parse_pub_record() {
3930 let source = r#"
3931 pub record Config {
3932 host: String,
3933 port: Int,
3934 }
3935
3936 agent Main {
3937 on start { yield(0); }
3938 }
3939 run Main;
3940 "#;
3941
3942 let (prog, errors) = parse_str(source);
3943 assert!(errors.is_empty(), "errors: {errors:?}");
3944 let prog = prog.expect("should parse");
3945
3946 assert_eq!(prog.records.len(), 1);
3947 assert!(prog.records[0].is_pub);
3948 assert_eq!(prog.records[0].name.name, "Config");
3949 }
3950
3951 #[test]
3952 fn parse_enum_declaration() {
3953 let source = r#"
3954 enum Status {
3955 Active,
3956 Pending,
3957 Done,
3958 }
3959
3960 agent Main {
3961 on start {
3962 yield(0);
3963 }
3964 }
3965 run Main;
3966 "#;
3967
3968 let (prog, errors) = parse_str(source);
3969 assert!(errors.is_empty(), "errors: {errors:?}");
3970 let prog = prog.expect("should parse");
3971
3972 assert_eq!(prog.enums.len(), 1);
3973 assert!(!prog.enums[0].is_pub);
3974 assert_eq!(prog.enums[0].name.name, "Status");
3975 assert_eq!(prog.enums[0].variants.len(), 3);
3976 assert_eq!(prog.enums[0].variants[0].name.name, "Active");
3977 assert_eq!(prog.enums[0].variants[1].name.name, "Pending");
3978 assert_eq!(prog.enums[0].variants[2].name.name, "Done");
3979 }
3980
3981 #[test]
3982 fn parse_pub_enum() {
3983 let source = r#"
3984 pub enum Priority { High, Medium, Low }
3985
3986 agent Main {
3987 on start { yield(0); }
3988 }
3989 run Main;
3990 "#;
3991
3992 let (prog, errors) = parse_str(source);
3993 assert!(errors.is_empty(), "errors: {errors:?}");
3994 let prog = prog.expect("should parse");
3995
3996 assert_eq!(prog.enums.len(), 1);
3997 assert!(prog.enums[0].is_pub);
3998 assert_eq!(prog.enums[0].name.name, "Priority");
3999 }
4000
4001 #[test]
4002 fn parse_const_declaration() {
4003 let source = r#"
4004 const MAX_RETRIES: Int = 3;
4005
4006 agent Main {
4007 on start {
4008 yield(0);
4009 }
4010 }
4011 run Main;
4012 "#;
4013
4014 let (prog, errors) = parse_str(source);
4015 assert!(errors.is_empty(), "errors: {errors:?}");
4016 let prog = prog.expect("should parse");
4017
4018 assert_eq!(prog.consts.len(), 1);
4019 assert!(!prog.consts[0].is_pub);
4020 assert_eq!(prog.consts[0].name.name, "MAX_RETRIES");
4021 assert!(matches!(prog.consts[0].ty, TypeExpr::Int));
4022 }
4023
4024 #[test]
4025 fn parse_pub_const() {
4026 let source = r#"
4027 pub const API_URL: String = "https://api.example.com";
4028
4029 agent Main {
4030 on start { yield(0); }
4031 }
4032 run Main;
4033 "#;
4034
4035 let (prog, errors) = parse_str(source);
4036 assert!(errors.is_empty(), "errors: {errors:?}");
4037 let prog = prog.expect("should parse");
4038
4039 assert_eq!(prog.consts.len(), 1);
4040 assert!(prog.consts[0].is_pub);
4041 assert_eq!(prog.consts[0].name.name, "API_URL");
4042 }
4043
4044 #[test]
4045 fn parse_multiple_type_declarations() {
4046 let source = r#"
4047 record Point { x: Int, y: Int }
4048 enum Color { Red, Green, Blue }
4049 const ORIGIN_X: Int = 0;
4050
4051 agent Main {
4052 on start { yield(0); }
4053 }
4054 run Main;
4055 "#;
4056
4057 let (prog, errors) = parse_str(source);
4058 assert!(errors.is_empty(), "errors: {errors:?}");
4059 let prog = prog.expect("should parse");
4060
4061 assert_eq!(prog.records.len(), 1);
4062 assert_eq!(prog.enums.len(), 1);
4063 assert_eq!(prog.consts.len(), 1);
4064 }
4065
4066 #[test]
4067 fn parse_match_expression() {
4068 let source = r#"
4069 enum Status { Active, Pending, Done }
4070
4071 agent Main {
4072 on start {
4073 let s: Int = match Active {
4074 Active => 1,
4075 Pending => 2,
4076 Done => 3,
4077 };
4078 yield(s);
4079 }
4080 }
4081 run Main;
4082 "#;
4083
4084 let (prog, errors) = parse_str(source);
4085 assert!(errors.is_empty(), "errors: {errors:?}");
4086 let prog = prog.expect("should parse");
4087
4088 assert_eq!(prog.agents.len(), 1);
4090 let handler = &prog.agents[0].handlers[0];
4092 let stmt = &handler.body.stmts[0];
4093 if let Stmt::Let { value, .. } = stmt {
4094 assert!(matches!(value, Expr::Match { .. }));
4095 } else {
4096 panic!("expected let statement with match");
4097 }
4098 }
4099
4100 #[test]
4101 fn parse_match_with_wildcard() {
4102 let source = r#"
4103 agent Main {
4104 on start {
4105 let x = 5;
4106 let result = match x {
4107 1 => 10,
4108 2 => 20,
4109 _ => 0,
4110 };
4111 yield(result);
4112 }
4113 }
4114 run Main;
4115 "#;
4116
4117 let (prog, errors) = parse_str(source);
4118 assert!(errors.is_empty(), "errors: {errors:?}");
4119 let prog = prog.expect("should parse");
4120
4121 assert_eq!(prog.agents.len(), 1);
4122 }
4123
4124 #[test]
4125 fn parse_record_construction() {
4126 let source = r#"
4127 record Point { x: Int, y: Int }
4128
4129 agent Main {
4130 on start {
4131 let p = Point { x: 10, y: 20 };
4132 yield(0);
4133 }
4134 }
4135 run Main;
4136 "#;
4137
4138 let (prog, errors) = parse_str(source);
4139 assert!(errors.is_empty(), "errors: {errors:?}");
4140 let prog = prog.expect("should parse");
4141
4142 assert_eq!(prog.records.len(), 1);
4143 assert_eq!(prog.agents.len(), 1);
4144
4145 let handler = &prog.agents[0].handlers[0];
4147 let stmt = &handler.body.stmts[0];
4148 if let Stmt::Let { value, .. } = stmt {
4149 if let Expr::RecordConstruct { name, fields, .. } = value {
4150 assert_eq!(name.name, "Point");
4151 assert_eq!(fields.len(), 2);
4152 assert_eq!(fields[0].name.name, "x");
4153 assert_eq!(fields[1].name.name, "y");
4154 } else {
4155 panic!("expected RecordConstruct");
4156 }
4157 } else {
4158 panic!("expected let statement");
4159 }
4160 }
4161
4162 #[test]
4163 fn parse_match_with_qualified_variant() {
4164 let source = r#"
4165 enum Status { Active, Pending }
4166
4167 fn get_status() -> Int {
4168 return 1;
4169 }
4170
4171 agent Main {
4172 on start {
4173 let s = get_status();
4174 let result = match s {
4175 Status::Active => 1,
4176 Status::Pending => 0,
4177 };
4178 yield(result);
4179 }
4180 }
4181 run Main;
4182 "#;
4183
4184 let (prog, errors) = parse_str(source);
4185 assert!(errors.is_empty(), "errors: {errors:?}");
4186 let prog = prog.expect("should parse");
4187
4188 assert_eq!(prog.enums.len(), 1);
4189 assert_eq!(prog.agents.len(), 1);
4190 }
4191
4192 #[test]
4193 fn parse_field_access() {
4194 let source = r#"
4195 record Point { x: Int, y: Int }
4196
4197 agent Main {
4198 on start {
4199 let p = Point { x: 10, y: 20 };
4200 let x_val = p.x;
4201 let y_val = p.y;
4202 yield(x_val);
4203 }
4204 }
4205 run Main;
4206 "#;
4207
4208 let (prog, errors) = parse_str(source);
4209 assert!(errors.is_empty(), "errors: {errors:?}");
4210 let prog = prog.expect("should parse");
4211
4212 assert_eq!(prog.records.len(), 1);
4213 assert_eq!(prog.agents.len(), 1);
4214
4215 let handler = &prog.agents[0].handlers[0];
4217 let stmt = &handler.body.stmts[1]; if let Stmt::Let { value, .. } = stmt {
4219 if let Expr::FieldAccess { field, .. } = value {
4220 assert_eq!(field.name, "x");
4221 } else {
4222 panic!("expected FieldAccess");
4223 }
4224 } else {
4225 panic!("expected let statement");
4226 }
4227 }
4228
4229 #[test]
4230 fn parse_chained_field_access() {
4231 let source = r#"
4232 record Inner { val: Int }
4233 record Outer { inner: Inner }
4234
4235 agent Main {
4236 on start {
4237 let inner = Inner { val: 42 };
4238 let outer = Outer { inner: inner };
4239 let v = outer.inner.val;
4240 yield(v);
4241 }
4242 }
4243 run Main;
4244 "#;
4245
4246 let (prog, errors) = parse_str(source);
4247 assert!(errors.is_empty(), "errors: {errors:?}");
4248 let prog = prog.expect("should parse");
4249
4250 assert_eq!(prog.records.len(), 2);
4251 assert_eq!(prog.agents.len(), 1);
4252
4253 let handler = &prog.agents[0].handlers[0];
4255 let stmt = &handler.body.stmts[2]; if let Stmt::Let { value, .. } = stmt {
4257 if let Expr::FieldAccess {
4258 object, field: val, ..
4259 } = value
4260 {
4261 assert_eq!(val.name, "val");
4262 if let Expr::FieldAccess { field: inner, .. } = object.as_ref() {
4264 assert_eq!(inner.name, "inner");
4265 } else {
4266 panic!("expected nested FieldAccess");
4267 }
4268 } else {
4269 panic!("expected FieldAccess");
4270 }
4271 } else {
4272 panic!("expected let statement");
4273 }
4274 }
4275
4276 #[test]
4281 fn parse_loop_break() {
4282 let source = r#"
4283 agent Main {
4284 on start {
4285 let count = 0;
4286 loop {
4287 count = count + 1;
4288 if count > 5 {
4289 break;
4290 }
4291 }
4292 yield(count);
4293 }
4294 }
4295 run Main;
4296 "#;
4297
4298 let (prog, errors) = parse_str(source);
4299 assert!(errors.is_empty(), "errors: {errors:?}");
4300 let prog = prog.expect("should parse");
4301
4302 assert_eq!(prog.agents.len(), 1);
4303 let handler = &prog.agents[0].handlers[0];
4304 let loop_stmt = &handler.body.stmts[1];
4306 assert!(matches!(loop_stmt, Stmt::Loop { .. }));
4307 if let Stmt::Loop { body, .. } = loop_stmt {
4309 let if_stmt = &body.stmts[1];
4310 if let Stmt::If { then_block, .. } = if_stmt {
4311 assert!(matches!(then_block.stmts[0], Stmt::Break { .. }));
4312 } else {
4313 panic!("expected if statement");
4314 }
4315 }
4316 }
4317
4318 #[test]
4319 fn parse_agent_receives() {
4320 let source = r#"
4321 enum WorkerMsg {
4322 Task,
4323 Shutdown,
4324 }
4325
4326 agent Worker receives WorkerMsg {
4327 id: Int
4328
4329 on start {
4330 yield(0);
4331 }
4332 }
4333
4334 agent Main {
4335 on start {
4336 yield(0);
4337 }
4338 }
4339 run Main;
4340 "#;
4341
4342 let (prog, errors) = parse_str(source);
4343 assert!(errors.is_empty(), "errors: {errors:?}");
4344 let prog = prog.expect("should parse");
4345
4346 assert_eq!(prog.agents.len(), 2);
4347
4348 let worker = &prog.agents[0];
4350 assert_eq!(worker.name.name, "Worker");
4351 assert!(worker.receives.is_some());
4352 if let Some(TypeExpr::Named(name, _)) = &worker.receives {
4353 assert_eq!(name.name, "WorkerMsg");
4354 } else {
4355 panic!("expected named type for receives");
4356 }
4357
4358 let main = &prog.agents[1];
4360 assert_eq!(main.name.name, "Main");
4361 assert!(main.receives.is_none());
4362 }
4363
4364 #[test]
4365 fn parse_receive_expression() {
4366 let source = r#"
4367 enum Msg { Ping }
4368
4369 agent Worker receives Msg {
4370 on start {
4371 let msg = receive();
4372 yield(0);
4373 }
4374 }
4375
4376 agent Main {
4377 on start { yield(0); }
4378 }
4379 run Main;
4380 "#;
4381
4382 let (prog, errors) = parse_str(source);
4383 assert!(errors.is_empty(), "errors: {errors:?}");
4384 let prog = prog.expect("should parse");
4385
4386 let worker = prog
4388 .agents
4389 .iter()
4390 .find(|a| a.name.name == "Worker")
4391 .unwrap();
4392 let handler = &worker.handlers[0];
4393 let stmt = &handler.body.stmts[0];
4394
4395 if let Stmt::Let { value, .. } = stmt {
4396 assert!(matches!(value, Expr::Receive { .. }));
4397 } else {
4398 panic!("expected let with receive");
4399 }
4400 }
4401
4402 #[test]
4403 fn parse_message_passing_full() {
4404 let source = r#"
4405 enum WorkerMsg {
4406 Task,
4407 Shutdown,
4408 }
4409
4410 agent Worker receives WorkerMsg {
4411 id: Int
4412
4413 on start {
4414 let msg = receive();
4415 let result = match msg {
4416 Task => 1,
4417 Shutdown => 0,
4418 };
4419 yield(result);
4420 }
4421 }
4422
4423 agent Main {
4424 on start {
4425 let w = summon Worker { id: 1 };
4426 send(w, Task);
4427 send(w, Shutdown);
4428 await w;
4429 yield(0);
4430 }
4431 }
4432 run Main;
4433 "#;
4434
4435 let (prog, errors) = parse_str(source);
4436 assert!(errors.is_empty(), "errors: {errors:?}");
4437 let prog = prog.expect("should parse");
4438
4439 assert_eq!(prog.enums.len(), 1);
4440 assert_eq!(prog.agents.len(), 2);
4441
4442 let worker = prog
4444 .agents
4445 .iter()
4446 .find(|a| a.name.name == "Worker")
4447 .unwrap();
4448 assert!(worker.receives.is_some());
4449 }
4450
4451 #[test]
4456 fn parse_fallible_function() {
4457 let source = r#"
4458 fn get_data(url: String) -> String fails {
4459 return divine("Get data from {url}" -> String);
4460 }
4461
4462 agent Main {
4463 on start { yield(0); }
4464 }
4465 run Main;
4466 "#;
4467
4468 let (prog, errors) = parse_str(source);
4469 assert!(errors.is_empty(), "errors: {errors:?}");
4470 let prog = prog.expect("should parse");
4471
4472 assert_eq!(prog.functions.len(), 1);
4473 assert!(prog.functions[0].is_fallible);
4474 }
4475
4476 #[test]
4477 fn parse_try_expression() {
4478 let source = r#"
4479 fn fallible() -> Int fails { return 42; }
4480
4481 agent Main {
4482 on start {
4483 let x = try fallible();
4484 yield(x);
4485 }
4486 }
4487 run Main;
4488 "#;
4489
4490 let (prog, errors) = parse_str(source);
4491 assert!(errors.is_empty(), "errors: {errors:?}");
4492 let prog = prog.expect("should parse");
4493
4494 let handler = &prog.agents[0].handlers[0];
4496 if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
4497 assert!(matches!(value, Expr::Try { .. }));
4498 } else {
4499 panic!("expected Let statement");
4500 }
4501 }
4502
4503 #[test]
4504 fn parse_catch_expression() {
4505 let source = r#"
4506 fn fallible() -> Int fails { return 42; }
4507
4508 agent Main {
4509 on start {
4510 let x = fallible() catch { 0 };
4511 yield(x);
4512 }
4513 }
4514 run Main;
4515 "#;
4516
4517 let (prog, errors) = parse_str(source);
4518 assert!(errors.is_empty(), "errors: {errors:?}");
4519 let prog = prog.expect("should parse");
4520
4521 let handler = &prog.agents[0].handlers[0];
4523 if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
4524 if let Expr::Catch { error_bind, .. } = value {
4525 assert!(error_bind.is_none());
4526 } else {
4527 panic!("expected Catch expression");
4528 }
4529 } else {
4530 panic!("expected Let statement");
4531 }
4532 }
4533
4534 #[test]
4535 fn parse_catch_with_error_binding() {
4536 let source = r#"
4537 fn fallible() -> Int fails { return 42; }
4538
4539 agent Main {
4540 on start {
4541 let x = fallible() catch(e) { 0 };
4542 yield(x);
4543 }
4544 }
4545 run Main;
4546 "#;
4547
4548 let (prog, errors) = parse_str(source);
4549 assert!(errors.is_empty(), "errors: {errors:?}");
4550 let prog = prog.expect("should parse");
4551
4552 let handler = &prog.agents[0].handlers[0];
4554 if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
4555 if let Expr::Catch { error_bind, .. } = value {
4556 assert!(error_bind.is_some());
4557 assert_eq!(error_bind.as_ref().unwrap().name, "e");
4558 } else {
4559 panic!("expected Catch expression");
4560 }
4561 } else {
4562 panic!("expected Let statement");
4563 }
4564 }
4565
4566 #[test]
4567 fn parse_fail_expression() {
4568 let source = r#"
4569 agent Main {
4570 on start {
4571 fail "something went wrong";
4572 }
4573 on error(e) {
4574 yield(0);
4575 }
4576 }
4577 run Main;
4578 "#;
4579
4580 let (prog, errors) = parse_str(source);
4581 assert!(errors.is_empty(), "errors: {errors:?}");
4582 let prog = prog.expect("should parse");
4583
4584 let handler = &prog.agents[0].handlers[0];
4586 if let Stmt::Expr { expr, .. } = &handler.body.stmts[0] {
4587 if let Expr::Fail { error, .. } = expr {
4588 assert!(matches!(**error, Expr::Literal { .. }));
4589 } else {
4590 panic!("expected Fail expression, got {expr:?}");
4591 }
4592 } else {
4593 panic!("expected Expr statement");
4594 }
4595 }
4596
4597 #[test]
4598 fn parse_retry_expression() {
4599 let source = r#"
4600 agent Main {
4601 topic: String
4602
4603 on start {
4604 let result = retry(3) {
4605 try divine("Summarize: {self.topic}")
4606 } catch { "fallback" };
4607 yield(result);
4608 }
4609
4610 on error(e) {
4611 yield("");
4612 }
4613 }
4614 run Main;
4615 "#;
4616
4617 let (prog, errors) = parse_str(source);
4618 assert!(errors.is_empty(), "errors: {errors:?}");
4619 let prog = prog.expect("should parse");
4620
4621 let handler = &prog.agents[0].handlers[0];
4623 if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
4624 if let Expr::Catch { expr, .. } = value {
4626 if let Expr::Retry { count, delay, .. } = expr.as_ref() {
4627 assert!(matches!(**count, Expr::Literal { .. }));
4628 assert!(delay.is_none());
4629 } else {
4630 panic!("expected Retry expression");
4631 }
4632 } else {
4633 panic!("expected Catch expression");
4634 }
4635 } else {
4636 panic!("expected Let statement");
4637 }
4638 }
4639
4640 #[test]
4641 fn parse_retry_with_delay() {
4642 let source = r#"
4643 agent Main {
4644 on start {
4645 let result = retry(3, delay: 1000) {
4646 42
4647 } catch { 0 };
4648 yield(result);
4649 }
4650 }
4651 run Main;
4652 "#;
4653
4654 let (prog, errors) = parse_str(source);
4655 assert!(errors.is_empty(), "errors: {errors:?}");
4656 let prog = prog.expect("should parse");
4657
4658 let handler = &prog.agents[0].handlers[0];
4660 if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
4661 if let Expr::Catch { expr, .. } = value {
4662 if let Expr::Retry { delay, .. } = expr.as_ref() {
4663 assert!(delay.is_some());
4664 } else {
4665 panic!("expected Retry expression");
4666 }
4667 } else {
4668 panic!("expected Catch expression");
4669 }
4670 } else {
4671 panic!("expected Let statement");
4672 }
4673 }
4674
4675 #[test]
4676 fn parse_on_error_handler() {
4677 let source = r#"
4678 agent Main {
4679 on start {
4680 yield(0);
4681 }
4682
4683 on error(e) {
4684 yield(1);
4685 }
4686 }
4687 run Main;
4688 "#;
4689
4690 let (prog, errors) = parse_str(source);
4691 assert!(errors.is_empty(), "errors: {errors:?}");
4692 let prog = prog.expect("should parse");
4693
4694 assert_eq!(prog.agents.len(), 1);
4695 assert_eq!(prog.agents[0].handlers.len(), 2);
4696
4697 let error_handler = prog.agents[0]
4699 .handlers
4700 .iter()
4701 .find(|h| matches!(h.event, EventKind::Error { .. }));
4702 assert!(error_handler.is_some());
4703
4704 if let EventKind::Error { param_name } = &error_handler.unwrap().event {
4705 assert_eq!(param_name.name, "e");
4706 } else {
4707 panic!("expected Error event kind");
4708 }
4709 }
4710
4711 #[test]
4716 fn parse_fn_type() {
4717 let source = r#"
4718 fn apply(f: Fn(Int) -> Int, x: Int) -> Int {
4719 return f(x);
4720 }
4721
4722 agent Main {
4723 on start {
4724 yield(0);
4725 }
4726 }
4727 run Main;
4728 "#;
4729
4730 let (prog, errors) = parse_str(source);
4731 assert!(errors.is_empty(), "errors: {errors:?}");
4732 let prog = prog.expect("should parse");
4733
4734 assert_eq!(prog.functions.len(), 1);
4735 let func = &prog.functions[0];
4736 assert_eq!(func.name.name, "apply");
4737 assert_eq!(func.params.len(), 2);
4738
4739 if let TypeExpr::Fn(params, ret) = &func.params[0].ty {
4741 assert_eq!(params.len(), 1);
4742 assert!(matches!(params[0], TypeExpr::Int));
4743 assert!(matches!(ret.as_ref(), TypeExpr::Int));
4744 } else {
4745 panic!("expected Fn type for first param");
4746 }
4747 }
4748
4749 #[test]
4750 fn parse_closure_with_params() {
4751 let source = r#"
4752 agent Main {
4753 on start {
4754 let f = |x: Int| x + 1;
4755 yield(0);
4756 }
4757 }
4758 run Main;
4759 "#;
4760
4761 let (prog, errors) = parse_str(source);
4762 assert!(errors.is_empty(), "errors: {errors:?}");
4763 let prog = prog.expect("should parse");
4764
4765 let handler = &prog.agents[0].handlers[0];
4767 if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
4768 if let Expr::Closure { params, body, .. } = value {
4769 assert_eq!(params.len(), 1);
4770 assert_eq!(params[0].name.name, "x");
4771 assert!(matches!(¶ms[0].ty, Some(TypeExpr::Int)));
4772
4773 assert!(matches!(body.as_ref(), Expr::Binary { .. }));
4775 } else {
4776 panic!("expected closure expression");
4777 }
4778 } else {
4779 panic!("expected let statement");
4780 }
4781 }
4782
4783 #[test]
4784 fn parse_closure_empty_params() {
4785 let source = r#"
4786 agent Main {
4787 on start {
4788 let f = || 42;
4789 yield(0);
4790 }
4791 }
4792 run Main;
4793 "#;
4794
4795 let (prog, errors) = parse_str(source);
4796 assert!(errors.is_empty(), "errors: {errors:?}");
4797 let prog = prog.expect("should parse");
4798
4799 let handler = &prog.agents[0].handlers[0];
4801 if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
4802 if let Expr::Closure { params, body, .. } = value {
4803 assert!(params.is_empty());
4804
4805 assert!(matches!(body.as_ref(), Expr::Literal { .. }));
4807 } else {
4808 panic!("expected closure expression");
4809 }
4810 } else {
4811 panic!("expected let statement");
4812 }
4813 }
4814
4815 #[test]
4816 fn parse_closure_multiple_params() {
4817 let source = r#"
4818 agent Main {
4819 on start {
4820 let add = |x: Int, y: Int| x + y;
4821 yield(0);
4822 }
4823 }
4824 run Main;
4825 "#;
4826
4827 let (prog, errors) = parse_str(source);
4828 assert!(errors.is_empty(), "errors: {errors:?}");
4829 let prog = prog.expect("should parse");
4830
4831 let handler = &prog.agents[0].handlers[0];
4832 if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
4833 if let Expr::Closure { params, .. } = value {
4834 assert_eq!(params.len(), 2);
4835 assert_eq!(params[0].name.name, "x");
4836 assert_eq!(params[1].name.name, "y");
4837 } else {
4838 panic!("expected closure expression");
4839 }
4840 } else {
4841 panic!("expected let statement");
4842 }
4843 }
4844
4845 #[test]
4846 fn parse_fn_type_multiarg() {
4847 let source = r#"
4848 fn fold_left(f: Fn(Int, Int) -> Int, init: Int) -> Int {
4849 return init;
4850 }
4851
4852 agent Main {
4853 on start {
4854 yield(0);
4855 }
4856 }
4857 run Main;
4858 "#;
4859
4860 let (prog, errors) = parse_str(source);
4861 assert!(errors.is_empty(), "errors: {errors:?}");
4862 let prog = prog.expect("should parse");
4863
4864 if let TypeExpr::Fn(params, ret) = &prog.functions[0].params[0].ty {
4866 assert_eq!(params.len(), 2);
4867 assert!(matches!(params[0], TypeExpr::Int));
4868 assert!(matches!(params[1], TypeExpr::Int));
4869 assert!(matches!(ret.as_ref(), TypeExpr::Int));
4870 } else {
4871 panic!("expected Fn type");
4872 }
4873 }
4874
4875 #[test]
4876 fn parse_tuple_literal() {
4877 let source = r#"
4878 agent Main {
4879 on start {
4880 let t = (1, 2);
4881 yield(0);
4882 }
4883 }
4884 run Main;
4885 "#;
4886
4887 let (prog, errors) = parse_str(source);
4888 assert!(errors.is_empty(), "errors: {errors:?}");
4889 let prog = prog.expect("should parse");
4890
4891 let handler = &prog.agents[0].handlers[0];
4892 if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
4893 if let Expr::Tuple { elements, .. } = value {
4894 assert_eq!(elements.len(), 2);
4895 } else {
4896 panic!("expected tuple expression, got {:?}", value);
4897 }
4898 } else {
4899 panic!("expected let statement");
4900 }
4901 }
4902
4903 #[test]
4908 fn parse_tool_declaration() {
4909 let source = r#"
4910 tool Http {
4911 fn get(url: String) -> Result<String, String>
4912 fn post(url: String, body: String) -> Result<String, String>
4913 }
4914 agent Main {
4915 on start { yield(0); }
4916 }
4917 run Main;
4918 "#;
4919
4920 let (prog, errors) = parse_str(source);
4921 assert!(errors.is_empty(), "errors: {errors:?}");
4922 let prog = prog.expect("should parse");
4923
4924 assert_eq!(prog.tools.len(), 1);
4925 assert_eq!(prog.tools[0].name.name, "Http");
4926 assert_eq!(prog.tools[0].functions.len(), 2);
4927 assert_eq!(prog.tools[0].functions[0].name.name, "get");
4928 assert_eq!(prog.tools[0].functions[1].name.name, "post");
4929 }
4930
4931 #[test]
4932 fn parse_pub_tool_declaration() {
4933 let source = r#"
4934 pub tool Database {
4935 fn query(sql: String) -> Result<List<String>, String>
4936 }
4937 agent Main {
4938 on start { yield(0); }
4939 }
4940 run Main;
4941 "#;
4942
4943 let (prog, errors) = parse_str(source);
4944 assert!(errors.is_empty(), "errors: {errors:?}");
4945 let prog = prog.expect("should parse");
4946
4947 assert!(prog.tools[0].is_pub);
4948 assert_eq!(prog.tools[0].name.name, "Database");
4949 }
4950
4951 #[test]
4952 fn parse_agent_with_tool_use() {
4953 let source = r#"
4954 agent Fetcher {
4955 use Http
4956
4957 url: String
4958
4959 on start {
4960 yield(0);
4961 }
4962 }
4963 run Fetcher;
4964 "#;
4965
4966 let (prog, errors) = parse_str(source);
4967 assert!(errors.is_empty(), "errors: {errors:?}");
4968 let prog = prog.expect("should parse");
4969
4970 assert_eq!(prog.agents[0].tool_uses.len(), 1);
4971 assert_eq!(prog.agents[0].tool_uses[0].name, "Http");
4972 assert_eq!(prog.agents[0].beliefs.len(), 1);
4973 }
4974
4975 #[test]
4976 fn parse_agent_with_multiple_tool_uses() {
4977 let source = r#"
4978 agent Pipeline {
4979 use Http, Fs
4980
4981 on start {
4982 yield(0);
4983 }
4984 }
4985 run Pipeline;
4986 "#;
4987
4988 let (prog, errors) = parse_str(source);
4989 assert!(errors.is_empty(), "errors: {errors:?}");
4990 let prog = prog.expect("should parse");
4991
4992 assert_eq!(prog.agents[0].tool_uses.len(), 2);
4993 assert_eq!(prog.agents[0].tool_uses[0].name, "Http");
4994 assert_eq!(prog.agents[0].tool_uses[1].name, "Fs");
4995 }
4996
4997 #[test]
4998 fn parse_tool_call_expression() {
4999 let source = r#"
5000 agent Fetcher {
5001 use Http
5002
5003 on start {
5004 let response = Http.get("https://example.com");
5005 yield(0);
5006 }
5007 }
5008 run Fetcher;
5009 "#;
5010
5011 let (prog, errors) = parse_str(source);
5012 assert!(errors.is_empty(), "errors: {errors:?}");
5013 let prog = prog.expect("should parse");
5014
5015 let handler = &prog.agents[0].handlers[0];
5016 if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
5017 if let Expr::ToolCall {
5018 tool,
5019 function,
5020 args,
5021 ..
5022 } = value
5023 {
5024 assert_eq!(tool.name, "Http");
5025 assert_eq!(function.name, "get");
5026 assert_eq!(args.len(), 1);
5027 } else {
5028 panic!("expected ToolCall expression, got {:?}", value);
5029 }
5030 } else {
5031 panic!("expected let statement");
5032 }
5033 }
5034
5035 #[test]
5036 fn parse_tool_call_with_multiple_args() {
5037 let source = r#"
5038 agent Writer {
5039 use Fs
5040
5041 on start {
5042 let result = Fs.write("/tmp/test.txt", "hello world");
5043 yield(0);
5044 }
5045 }
5046 run Writer;
5047 "#;
5048
5049 let (prog, errors) = parse_str(source);
5050 assert!(errors.is_empty(), "errors: {errors:?}");
5051 let prog = prog.expect("should parse");
5052
5053 let handler = &prog.agents[0].handlers[0];
5054 if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
5055 if let Expr::ToolCall { args, .. } = value {
5056 assert_eq!(args.len(), 2);
5057 } else {
5058 panic!("expected ToolCall expression, got {:?}", value);
5059 }
5060 } else {
5061 panic!("expected let statement");
5062 }
5063 }
5064
5065 #[test]
5066 fn parse_string_interp_with_field_access() {
5067 let source = r#"
5068 record Person { name: String }
5069 agent Main {
5070 on start {
5071 let p = Person { name: "Alice" };
5072 print("Hello, {p.name}!");
5073 yield(0);
5074 }
5075 }
5076 run Main;
5077 "#;
5078
5079 let (prog, errors) = parse_str(source);
5080 assert!(errors.is_empty(), "errors: {errors:?}");
5081 let prog = prog.expect("should parse");
5082
5083 let handler = &prog.agents[0].handlers[0];
5085 if let Stmt::Expr { expr, .. } = &handler.body.stmts[1] {
5086 if let Expr::Call { args, .. } = expr {
5087 if let Expr::StringInterp { template, .. } = &args[0] {
5088 assert!(template.has_interpolations());
5089 let interps: Vec<_> = template.interpolations().collect();
5090 assert_eq!(interps.len(), 1);
5091 match interps[0] {
5093 Expr::FieldAccess { object, field, .. } => {
5094 if let Expr::Var { name, .. } = object.as_ref() {
5095 assert_eq!(name.name, "p");
5096 } else {
5097 panic!("expected Var as base");
5098 }
5099 assert_eq!(field.name, "name");
5100 }
5101 _ => panic!("expected FieldAccess, got {:?}", interps[0]),
5102 }
5103 } else {
5104 panic!("expected StringInterp");
5105 }
5106 } else {
5107 panic!("expected Call");
5108 }
5109 } else {
5110 panic!("expected Expr statement");
5111 }
5112 }
5113
5114 #[test]
5115 fn parse_string_interp_with_tuple_index() {
5116 let source = r#"
5117 agent Main {
5118 on start {
5119 let pair = (1, 2);
5120 print("First: {pair.0}");
5121 yield(0);
5122 }
5123 }
5124 run Main;
5125 "#;
5126
5127 let (prog, errors) = parse_str(source);
5128 assert!(errors.is_empty(), "errors: {errors:?}");
5129 let prog = prog.expect("should parse");
5130
5131 let handler = &prog.agents[0].handlers[0];
5132 if let Stmt::Expr { expr, .. } = &handler.body.stmts[1] {
5133 if let Expr::Call { args, .. } = expr {
5134 if let Expr::StringInterp { template, .. } = &args[0] {
5135 let interps: Vec<_> = template.interpolations().collect();
5136 assert_eq!(interps.len(), 1);
5137 match interps[0] {
5138 Expr::TupleIndex { tuple, index, .. } => {
5139 if let Expr::Var { name, .. } = tuple.as_ref() {
5140 assert_eq!(name.name, "pair");
5141 } else {
5142 panic!("expected Var as tuple base");
5143 }
5144 assert_eq!(*index, 0);
5145 }
5146 _ => panic!("expected TupleIndex, got {:?}", interps[0]),
5147 }
5148 } else {
5149 panic!("expected StringInterp");
5150 }
5151 } else {
5152 panic!("expected Call");
5153 }
5154 } else {
5155 panic!("expected Expr statement");
5156 }
5157 }
5158
5159 #[test]
5160 fn parse_mock_tool_with_fail() {
5161 let source = r#"
5162 test "mock tool fail" {
5163 mock tool Http.get -> fail("network error");
5164 }
5165 "#;
5166
5167 let (prog, errors) = parse_str(source);
5168 assert!(errors.is_empty(), "errors: {errors:?}");
5169 let prog = prog.expect("should parse");
5170
5171 let test = &prog.tests[0];
5172 assert_eq!(test.body.stmts.len(), 1);
5173
5174 if let Stmt::MockTool {
5175 tool_name,
5176 fn_name,
5177 value,
5178 ..
5179 } = &test.body.stmts[0]
5180 {
5181 assert_eq!(tool_name.name, "Http");
5182 assert_eq!(fn_name.name, "get");
5183 assert!(
5184 matches!(value, MockValue::Fail(_)),
5185 "expected MockValue::Fail, got {:?}",
5186 value
5187 );
5188 } else {
5189 panic!("expected MockTool statement, got {:?}", test.body.stmts[0]);
5190 }
5191 }
5192
5193 #[test]
5194 fn parse_mock_tool_with_value() {
5195 let source = r#"
5196 test "mock tool value" {
5197 mock tool Http.get -> "response";
5198 }
5199 "#;
5200
5201 let (prog, errors) = parse_str(source);
5202 assert!(errors.is_empty(), "errors: {errors:?}");
5203 let prog = prog.expect("should parse");
5204
5205 let test = &prog.tests[0];
5206 if let Stmt::MockTool { value, .. } = &test.body.stmts[0] {
5207 assert!(
5208 matches!(value, MockValue::Value(_)),
5209 "expected MockValue::Value, got {:?}",
5210 value
5211 );
5212 } else {
5213 panic!("expected MockTool statement");
5214 }
5215 }
5216
5217 #[test]
5222 fn parse_generic_function() {
5223 let source = r#"
5224 fn identity<T>(x: T) -> T {
5225 return x;
5226 }
5227
5228 agent Main {
5229 on start { yield(0); }
5230 }
5231 run Main;
5232 "#;
5233
5234 let (prog, errors) = parse_str(source);
5235 assert!(errors.is_empty(), "errors: {errors:?}");
5236 let prog = prog.expect("should parse");
5237
5238 assert_eq!(prog.functions.len(), 1);
5239 let func = &prog.functions[0];
5240 assert_eq!(func.name.name, "identity");
5241 assert_eq!(func.type_params.len(), 1);
5242 assert_eq!(func.type_params[0].name, "T");
5243 }
5244
5245 #[test]
5246 fn parse_generic_function_multiple_params() {
5247 let source = r#"
5248 fn map<T, U>(list: List<T>, f: Fn(T) -> U) -> List<U> {
5249 return [];
5250 }
5251
5252 agent Main {
5253 on start { yield(0); }
5254 }
5255 run Main;
5256 "#;
5257
5258 let (prog, errors) = parse_str(source);
5259 assert!(errors.is_empty(), "errors: {errors:?}");
5260 let prog = prog.expect("should parse");
5261
5262 let func = &prog.functions[0];
5263 assert_eq!(func.name.name, "map");
5264 assert_eq!(func.type_params.len(), 2);
5265 assert_eq!(func.type_params[0].name, "T");
5266 assert_eq!(func.type_params[1].name, "U");
5267 }
5268
5269 #[test]
5270 fn parse_generic_record() {
5271 let source = r#"
5272 record Pair<A, B> {
5273 first: A,
5274 second: B,
5275 }
5276
5277 agent Main {
5278 on start { yield(0); }
5279 }
5280 run Main;
5281 "#;
5282
5283 let (prog, errors) = parse_str(source);
5284 assert!(errors.is_empty(), "errors: {errors:?}");
5285 let prog = prog.expect("should parse");
5286
5287 assert_eq!(prog.records.len(), 1);
5288 let record = &prog.records[0];
5289 assert_eq!(record.name.name, "Pair");
5290 assert_eq!(record.type_params.len(), 2);
5291 assert_eq!(record.type_params[0].name, "A");
5292 assert_eq!(record.type_params[1].name, "B");
5293 }
5294
5295 #[test]
5296 fn parse_generic_enum() {
5297 let source = r#"
5298 enum Tree<T> {
5299 Leaf(T),
5300 Node(Tree<T>),
5301 }
5302
5303 agent Main {
5304 on start { yield(0); }
5305 }
5306 run Main;
5307 "#;
5308
5309 let (prog, errors) = parse_str(source);
5310 assert!(errors.is_empty(), "errors: {errors:?}");
5311 let prog = prog.expect("should parse");
5312
5313 assert_eq!(prog.enums.len(), 1);
5314 let enumm = &prog.enums[0];
5315 assert_eq!(enumm.name.name, "Tree");
5316 assert_eq!(enumm.type_params.len(), 1);
5317 assert_eq!(enumm.type_params[0].name, "T");
5318 }
5319
5320 #[test]
5321 fn parse_generic_type_argument() {
5322 let source = r#"
5323 record Wrapper<T> {
5324 value: T,
5325 }
5326
5327 fn make_wrapper<T>(value: T) -> Wrapper<T> {
5328 return Wrapper { value: value };
5329 }
5330
5331 agent Main {
5332 on start { yield(0); }
5333 }
5334 run Main;
5335 "#;
5336
5337 let (prog, errors) = parse_str(source);
5338 assert!(errors.is_empty(), "errors: {errors:?}");
5339 let prog = prog.expect("should parse");
5340
5341 let func = &prog.functions[0];
5342 if let TypeExpr::Named(name, type_args) = &func.return_ty {
5344 assert_eq!(name.name, "Wrapper");
5345 assert_eq!(type_args.len(), 1);
5346 if let TypeExpr::Named(inner_name, _) = &type_args[0] {
5347 assert_eq!(inner_name.name, "T");
5348 } else {
5349 panic!("expected Named type argument");
5350 }
5351 } else {
5352 panic!("expected Named return type");
5353 }
5354 }
5355
5356 #[test]
5357 fn parse_turbofish_function_call() {
5358 let source = r#"
5359 fn identity<T>(x: T) -> T {
5360 return x;
5361 }
5362
5363 agent Main {
5364 on start {
5365 let result = identity::<Int>(42);
5366 yield(0);
5367 }
5368 }
5369 run Main;
5370 "#;
5371
5372 let (prog, errors) = parse_str(source);
5373 assert!(errors.is_empty(), "errors: {errors:?}");
5374 let prog = prog.expect("should parse");
5375
5376 let handler = &prog.agents[0].handlers[0];
5377 if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
5378 if let Expr::Call {
5379 name, type_args, ..
5380 } = value
5381 {
5382 assert_eq!(name.name, "identity");
5383 assert_eq!(type_args.len(), 1);
5384 assert!(matches!(type_args[0], TypeExpr::Int));
5385 } else {
5386 panic!("expected Call expression");
5387 }
5388 } else {
5389 panic!("expected Let statement");
5390 }
5391 }
5392
5393 #[test]
5394 fn parse_turbofish_multiple_type_args() {
5395 let source = r#"
5396 fn make_pair<A, B>(a: A, b: B) -> (A, B) {
5397 return (a, b);
5398 }
5399
5400 agent Main {
5401 on start {
5402 let pair = make_pair::<Int, String>(42, "hello");
5403 yield(0);
5404 }
5405 }
5406 run Main;
5407 "#;
5408
5409 let (prog, errors) = parse_str(source);
5410 assert!(errors.is_empty(), "errors: {errors:?}");
5411 let prog = prog.expect("should parse");
5412
5413 let handler = &prog.agents[0].handlers[0];
5414 if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
5415 if let Expr::Call {
5416 name, type_args, ..
5417 } = value
5418 {
5419 assert_eq!(name.name, "make_pair");
5420 assert_eq!(type_args.len(), 2);
5421 assert!(matches!(type_args[0], TypeExpr::Int));
5422 assert!(matches!(type_args[1], TypeExpr::String));
5423 } else {
5424 panic!("expected Call expression");
5425 }
5426 } else {
5427 panic!("expected Let statement");
5428 }
5429 }
5430
5431 #[test]
5432 fn parse_turbofish_record_construction() {
5433 let source = r#"
5434 record Pair<A, B> {
5435 first: A,
5436 second: B,
5437 }
5438
5439 agent Main {
5440 on start {
5441 let p = Pair::<Int, String> { first: 42, second: "hi" };
5442 yield(0);
5443 }
5444 }
5445 run Main;
5446 "#;
5447
5448 let (prog, errors) = parse_str(source);
5449 assert!(errors.is_empty(), "errors: {errors:?}");
5450 let prog = prog.expect("should parse");
5451
5452 let handler = &prog.agents[0].handlers[0];
5453 if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
5454 if let Expr::RecordConstruct {
5455 name, type_args, ..
5456 } = value
5457 {
5458 assert_eq!(name.name, "Pair");
5459 assert_eq!(type_args.len(), 2);
5460 assert!(matches!(type_args[0], TypeExpr::Int));
5461 assert!(matches!(type_args[1], TypeExpr::String));
5462 } else {
5463 panic!("expected RecordConstruct expression");
5464 }
5465 } else {
5466 panic!("expected Let statement");
5467 }
5468 }
5469
5470 #[test]
5471 fn parse_turbofish_variant_construction() {
5472 let source = r#"
5473 enum Either<L, R> {
5474 Left(L),
5475 Right(R),
5476 }
5477
5478 agent Main {
5479 on start {
5480 let e = Either::<String, Int>::Left("hello");
5481 yield(0);
5482 }
5483 }
5484 run Main;
5485 "#;
5486
5487 let (prog, errors) = parse_str(source);
5488 assert!(errors.is_empty(), "errors: {errors:?}");
5489 let prog = prog.expect("should parse");
5490
5491 let handler = &prog.agents[0].handlers[0];
5492 if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
5493 if let Expr::VariantConstruct {
5494 enum_name,
5495 type_args,
5496 variant,
5497 ..
5498 } = value
5499 {
5500 assert_eq!(enum_name.name, "Either");
5501 assert_eq!(variant.name, "Left");
5502 assert_eq!(type_args.len(), 2);
5503 assert!(matches!(type_args[0], TypeExpr::String));
5504 assert!(matches!(type_args[1], TypeExpr::Int));
5505 } else {
5506 panic!("expected VariantConstruct expression");
5507 }
5508 } else {
5509 panic!("expected Let statement");
5510 }
5511 }
5512
5513 #[test]
5514 fn parse_generic_in_type_annotation() {
5515 let source = r#"
5516 record Page<T> {
5517 items: List<T>,
5518 count: Int,
5519 }
5520
5521 agent Main {
5522 on start {
5523 let page: Page<String> = Page { items: [], count: 0 };
5524 yield(0);
5525 }
5526 }
5527 run Main;
5528 "#;
5529
5530 let (prog, errors) = parse_str(source);
5531 assert!(errors.is_empty(), "errors: {errors:?}");
5532 let prog = prog.expect("should parse");
5533
5534 let handler = &prog.agents[0].handlers[0];
5535 if let Stmt::Let { ty: Some(ty), .. } = &handler.body.stmts[0] {
5536 if let TypeExpr::Named(name, type_args) = ty {
5537 assert_eq!(name.name, "Page");
5538 assert_eq!(type_args.len(), 1);
5539 assert!(matches!(type_args[0], TypeExpr::String));
5540 } else {
5541 panic!("expected Named type");
5542 }
5543 } else {
5544 panic!("expected Let statement with type annotation");
5545 }
5546 }
5547
5548 #[test]
5549 fn parse_nested_generic_types() {
5550 let source = r#"
5551 record Nested<T> {
5552 value: Option<List<T>>,
5553 }
5554
5555 agent Main {
5556 on start { yield(0); }
5557 }
5558 run Main;
5559 "#;
5560
5561 let (prog, errors) = parse_str(source);
5562 assert!(errors.is_empty(), "errors: {errors:?}");
5563 let prog = prog.expect("should parse");
5564
5565 let record = &prog.records[0];
5566 let field_ty = &record.fields[0].ty;
5567 if let TypeExpr::Option(inner) = field_ty {
5569 if let TypeExpr::List(elem) = inner.as_ref() {
5570 if let TypeExpr::Named(name, _) = elem.as_ref() {
5571 assert_eq!(name.name, "T");
5572 } else {
5573 panic!("expected Named type T");
5574 }
5575 } else {
5576 panic!("expected List type");
5577 }
5578 } else {
5579 panic!("expected Option type");
5580 }
5581 }
5582
5583 #[test]
5584 fn parse_span_block() {
5585 let source = r#"
5586 agent Main {
5587 on start {
5588 span "fetch_data" {
5589 let x = 1;
5590 let y = 2;
5591 }
5592 yield(42);
5593 }
5594 }
5595 run Main;
5596 "#;
5597
5598 let (prog, errors) = parse_str(source);
5599 assert!(errors.is_empty(), "errors: {errors:?}");
5600 let prog = prog.expect("should parse");
5601
5602 let handler = &prog.agents[0].handlers[0];
5603 if let Stmt::SpanBlock { name, body, .. } = &handler.body.stmts[0] {
5605 if let Expr::Literal {
5607 value: Literal::String(s),
5608 ..
5609 } = name
5610 {
5611 assert_eq!(s, "fetch_data");
5612 } else {
5613 panic!("expected string literal for span name");
5614 }
5615 assert_eq!(body.stmts.len(), 2);
5617 } else {
5618 panic!("expected SpanBlock statement");
5619 }
5620 }
5621
5622 #[test]
5623 fn parse_turbofish_in_interpolation() {
5624 let source = r#"
5625 fn identity<T>(x: T) -> T { return x; }
5626
5627 agent Main {
5628 on start {
5629 let msg = "result: {identity::<Int>(42)}";
5630 yield(0);
5631 }
5632 }
5633 run Main;
5634 "#;
5635
5636 let (prog, errors) = parse_str(source);
5637 assert!(errors.is_empty(), "errors: {errors:?}");
5638 let prog = prog.expect("should parse");
5639
5640 let handler = &prog.agents[0].handlers[0];
5641 if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
5642 if let Expr::StringInterp { template, .. } = value {
5643 assert_eq!(template.parts.len(), 2);
5645 if let StringPart::Interpolation(call_expr) = &template.parts[1] {
5646 if let Expr::Call { name, type_args, args, .. } = call_expr.as_ref() {
5647 assert_eq!(name.name, "identity");
5648 assert_eq!(type_args.len(), 1);
5649 assert!(matches!(type_args[0], TypeExpr::Int));
5650 assert_eq!(args.len(), 1);
5651 } else {
5652 panic!("expected Call expression in interpolation, got {:?}", call_expr);
5653 }
5654 } else {
5655 panic!("expected Interpolation part in template");
5656 }
5657 } else {
5658 panic!("expected StringInterp expression");
5659 }
5660 } else {
5661 panic!("expected Let statement");
5662 }
5663 }
5664
5665 #[test]
5670 fn parse_protocol_declaration() {
5671 let source = r#"
5672 protocol SchemaSync {
5673 DatabaseSteward -> APISteward: SchemaChanged
5674 APISteward -> DatabaseSteward: Acknowledged
5675 }
5676
5677 agent Main {
5678 on start {
5679 yield(0);
5680 }
5681 }
5682 run Main;
5683 "#;
5684
5685 let (prog, errors) = parse_str(source);
5686 assert!(errors.is_empty(), "errors: {errors:?}");
5687 let prog = prog.expect("should parse");
5688
5689 assert_eq!(prog.protocols.len(), 1);
5690 let proto = &prog.protocols[0];
5691 assert_eq!(proto.name.name, "SchemaSync");
5692 assert!(!proto.is_pub);
5693 assert_eq!(proto.steps.len(), 2);
5694
5695 assert_eq!(proto.steps[0].sender.name, "DatabaseSteward");
5696 assert_eq!(proto.steps[0].receiver.name, "APISteward");
5697 if let TypeExpr::Named(name, _) = &proto.steps[0].message_type {
5698 assert_eq!(name.name, "SchemaChanged");
5699 } else {
5700 panic!("expected Named type");
5701 }
5702
5703 assert_eq!(proto.steps[1].sender.name, "APISteward");
5704 assert_eq!(proto.steps[1].receiver.name, "DatabaseSteward");
5705 }
5706
5707 #[test]
5708 fn parse_public_protocol() {
5709 let source = r#"
5710 pub protocol PingPong {
5711 Pinger -> Ponger: Ping
5712 Ponger -> Pinger: Pong
5713 }
5714
5715 agent Main { on start { yield(0); } }
5716 run Main;
5717 "#;
5718
5719 let (prog, errors) = parse_str(source);
5720 assert!(errors.is_empty(), "errors: {errors:?}");
5721 let prog = prog.expect("should parse");
5722
5723 assert_eq!(prog.protocols.len(), 1);
5724 assert!(prog.protocols[0].is_pub);
5725 }
5726
5727 #[test]
5728 fn parse_agent_follows_protocol() {
5729 let source = r#"
5730 protocol SchemaSync {
5731 A -> B: Msg
5732 }
5733
5734 agent APISteward follows SchemaSync as APISteward {
5735 on start {
5736 yield(0);
5737 }
5738 }
5739 run APISteward;
5740 "#;
5741
5742 let (prog, errors) = parse_str(source);
5743 assert!(errors.is_empty(), "errors: {errors:?}");
5744 let prog = prog.expect("should parse");
5745
5746 assert_eq!(prog.agents[0].follows.len(), 1);
5747 assert_eq!(prog.agents[0].follows[0].protocol.name, "SchemaSync");
5748 assert_eq!(prog.agents[0].follows[0].role.name, "APISteward");
5749 }
5750
5751 #[test]
5752 fn parse_agent_follows_multiple_protocols() {
5753 let source = r#"
5754 agent MultiProtocolAgent follows Proto1 as RoleA, Proto2 as RoleB {
5755 on start {
5756 yield(0);
5757 }
5758 }
5759 run MultiProtocolAgent;
5760 "#;
5761
5762 let (prog, errors) = parse_str(source);
5763 assert!(errors.is_empty(), "errors: {errors:?}");
5764 let prog = prog.expect("should parse");
5765
5766 assert_eq!(prog.agents[0].follows.len(), 2);
5767 assert_eq!(prog.agents[0].follows[0].protocol.name, "Proto1");
5768 assert_eq!(prog.agents[0].follows[0].role.name, "RoleA");
5769 assert_eq!(prog.agents[0].follows[1].protocol.name, "Proto2");
5770 assert_eq!(prog.agents[0].follows[1].role.name, "RoleB");
5771 }
5772
5773 #[test]
5774 fn parse_agent_with_receives_and_follows() {
5775 let source = r#"
5776 agent Worker receives Request follows WorkProto as Worker {
5777 on message(msg: Request) {
5778 reply(Response {});
5779 }
5780 }
5781 run Worker;
5782 "#;
5783
5784 let (prog, errors) = parse_str(source);
5785 assert!(errors.is_empty(), "errors: {errors:?}");
5786 let prog = prog.expect("should parse");
5787
5788 assert!(prog.agents[0].receives.is_some());
5789 assert_eq!(prog.agents[0].follows.len(), 1);
5790 }
5791
5792 #[test]
5793 fn parse_reply_expression() {
5794 let source = r#"
5795 agent Responder {
5796 on message(msg: Request) {
5797 reply(Response { code: 200 });
5798 }
5799 }
5800 run Responder;
5801 "#;
5802
5803 let (prog, errors) = parse_str(source);
5804 assert!(errors.is_empty(), "errors: {errors:?}");
5805 let prog = prog.expect("should parse");
5806
5807 let handler = &prog.agents[0].handlers[0];
5808 if let Stmt::Expr { expr: Expr::Reply { message, .. }, .. } = &handler.body.stmts[0] {
5809 assert!(matches!(message.as_ref(), Expr::RecordConstruct { .. }));
5810 } else {
5811 panic!("expected Reply expression, got {:?}", handler.body.stmts[0]);
5812 }
5813 }
5814
5815 #[test]
5816 fn parse_effect_handler_declaration() {
5817 let source = r#"
5818 handler DefaultLLM handles Infer {
5819 model: "gpt-4o"
5820 temperature: 0.7
5821 max_tokens: 1024
5822 }
5823
5824 agent Main { on start { yield(0); } }
5825 run Main;
5826 "#;
5827
5828 let (prog, errors) = parse_str(source);
5829 assert!(errors.is_empty(), "errors: {errors:?}");
5830 let prog = prog.expect("should parse");
5831
5832 assert_eq!(prog.effect_handlers.len(), 1);
5833 let handler = &prog.effect_handlers[0];
5834 assert_eq!(handler.name.name, "DefaultLLM");
5835 assert_eq!(handler.effect.name, "Infer");
5836 assert!(!handler.is_pub);
5837 assert_eq!(handler.config.len(), 3);
5838
5839 assert_eq!(handler.config[0].key.name, "model");
5840 assert!(matches!(handler.config[0].value, Literal::String(_)));
5841
5842 assert_eq!(handler.config[1].key.name, "temperature");
5843 assert!(matches!(handler.config[1].value, Literal::Float(_)));
5844
5845 assert_eq!(handler.config[2].key.name, "max_tokens");
5846 assert!(matches!(handler.config[2].value, Literal::Int(1024)));
5847 }
5848
5849 #[test]
5850 fn parse_public_effect_handler() {
5851 let source = r#"
5852 pub handler FastLLM handles Infer {
5853 model: "gpt-4o-mini"
5854 }
5855
5856 agent Main { on start { yield(0); } }
5857 run Main;
5858 "#;
5859
5860 let (prog, errors) = parse_str(source);
5861 assert!(errors.is_empty(), "errors: {errors:?}");
5862 let prog = prog.expect("should parse");
5863
5864 assert!(prog.effect_handlers[0].is_pub);
5865 }
5866
5867 #[test]
5868 fn parse_supervisor_with_handler_assignment() {
5869 let source = r#"
5870 handler DefaultLLM handles Infer {
5871 model: "gpt-4o"
5872 }
5873
5874 agent Worker {
5875 on start {
5876 let thought = divine("Think!");
5877 yield(thought);
5878 }
5879 }
5880
5881 supervisor AppSupervisor {
5882 strategy: OneForOne
5883 children {
5884 Worker {
5885 restart: Permanent
5886 handler Infer: DefaultLLM
5887 }
5888 }
5889 }
5890
5891 run AppSupervisor;
5892 "#;
5893
5894 let (prog, errors) = parse_str(source);
5895 assert!(errors.is_empty(), "errors: {errors:?}");
5896 let prog = prog.expect("should parse");
5897
5898 let child = &prog.supervisors[0].children[0];
5899 assert_eq!(child.agent_name.name, "Worker");
5900 assert_eq!(child.handler_assignments.len(), 1);
5901 assert_eq!(child.handler_assignments[0].effect.name, "Infer");
5902 assert_eq!(child.handler_assignments[0].handler.name, "DefaultLLM");
5903 }
5904
5905 #[test]
5906 fn parse_supervisor_with_multiple_handler_assignments() {
5907 let source = r#"
5908 agent Worker { on start { yield(0); } }
5909
5910 supervisor MultiHandlerSupervisor {
5911 strategy: OneForOne
5912 children {
5913 Worker {
5914 restart: Permanent
5915 handler Infer: FastLLM
5916 handler Logger: FileLogger
5917 }
5918 }
5919 }
5920
5921 run MultiHandlerSupervisor;
5922 "#;
5923
5924 let (prog, errors) = parse_str(source);
5925 assert!(errors.is_empty(), "errors: {errors:?}");
5926 let prog = prog.expect("should parse");
5927
5928 let child = &prog.supervisors[0].children[0];
5929 assert_eq!(child.handler_assignments.len(), 2);
5930 assert_eq!(child.handler_assignments[0].effect.name, "Infer");
5931 assert_eq!(child.handler_assignments[0].handler.name, "FastLLM");
5932 assert_eq!(child.handler_assignments[1].effect.name, "Logger");
5933 assert_eq!(child.handler_assignments[1].handler.name, "FileLogger");
5934 }
5935}