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