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