1use crate::ast::{
6 AgentDecl, BeliefDecl, BinOp, Block, ConstDecl, ElseBranch, EnumDecl, EventKind, Expr,
7 FieldInit, FnDecl, HandlerDecl, Literal, MatchArm, ModDecl, Param, Pattern, Program,
8 RecordDecl, RecordField, Stmt, StringPart, StringTemplate, UnaryOp, UseDecl, UseKind,
9};
10use chumsky::prelude::*;
11use chumsky::BoxedParser;
12use sage_lexer::{Spanned, Token};
13use sage_types::{Ident, Span, TypeExpr};
14use std::ops::Range;
15use std::sync::Arc;
16
17pub type ParseError = Simple<Token>;
19
20#[must_use]
26#[allow(clippy::needless_pass_by_value)] pub fn parse(tokens: &[Spanned], source: Arc<str>) -> (Option<Program>, Vec<ParseError>) {
28 let len = source.len();
29
30 let token_spans: Vec<(Token, Range<usize>)> = tokens
32 .iter()
33 .map(|s| (s.token.clone(), s.start..s.end))
34 .collect();
35
36 let stream = chumsky::Stream::from_iter(len..len, token_spans.into_iter());
37
38 let (ast, errors) = program_parser(Arc::clone(&source)).parse_recovery(stream);
39
40 (ast, errors)
41}
42
43#[allow(clippy::needless_pass_by_value)]
49fn program_parser(source: Arc<str>) -> impl Parser<Token, Program, Error = ParseError> {
50 let src = source.clone();
51 let src2 = source.clone();
52
53 let top_level = mod_parser(source.clone())
55 .or(use_parser(source.clone()))
56 .or(record_parser(source.clone()))
57 .or(enum_parser(source.clone()))
58 .or(const_parser(source.clone()))
59 .or(agent_parser(source.clone()))
60 .or(fn_parser(source.clone()))
61 .recover_with(skip_then_retry_until([
62 Token::KwMod,
63 Token::KwUse,
64 Token::KwPub,
65 Token::KwRecord,
66 Token::KwEnum,
67 Token::KwConst,
68 Token::KwAgent,
69 Token::KwFn,
70 Token::KwRun,
71 ]));
72
73 let run_stmt = just(Token::KwRun)
74 .ignore_then(ident_token_parser(src.clone()))
75 .then_ignore(just(Token::Semicolon))
76 .or_not();
77
78 top_level.repeated().then(run_stmt).map_with_span(
79 move |(items, run_agent), span: Range<usize>| {
80 let mut mod_decls = Vec::new();
81 let mut use_decls = Vec::new();
82 let mut records = Vec::new();
83 let mut enums = Vec::new();
84 let mut consts = Vec::new();
85 let mut agents = Vec::new();
86 let mut functions = Vec::new();
87
88 for item in items {
89 match item {
90 TopLevel::Mod(m) => mod_decls.push(m),
91 TopLevel::Use(u) => use_decls.push(u),
92 TopLevel::Record(r) => records.push(r),
93 TopLevel::Enum(e) => enums.push(e),
94 TopLevel::Const(c) => consts.push(c),
95 TopLevel::Agent(a) => agents.push(a),
96 TopLevel::Function(f) => functions.push(f),
97 }
98 }
99
100 Program {
101 mod_decls,
102 use_decls,
103 records,
104 enums,
105 consts,
106 agents,
107 functions,
108 run_agent,
109 span: make_span(&src2, span),
110 }
111 },
112 )
113}
114
115enum TopLevel {
117 Mod(ModDecl),
118 Use(UseDecl),
119 Record(RecordDecl),
120 Enum(EnumDecl),
121 Const(ConstDecl),
122 Agent(AgentDecl),
123 Function(FnDecl),
124}
125
126#[allow(clippy::needless_pass_by_value)]
132fn mod_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
133 let src = source.clone();
134
135 just(Token::KwPub)
136 .or_not()
137 .then_ignore(just(Token::KwMod))
138 .then(ident_token_parser(src.clone()))
139 .then_ignore(just(Token::Semicolon))
140 .map_with_span(move |(is_pub, name), span: Range<usize>| {
141 TopLevel::Mod(ModDecl {
142 is_pub: is_pub.is_some(),
143 name,
144 span: make_span(&src, span),
145 })
146 })
147}
148
149#[allow(clippy::needless_pass_by_value)]
151fn use_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
152 let src = source.clone();
153 let src2 = source.clone();
154 let src3 = source.clone();
155 let src4 = source.clone();
156
157 let simple_use = just(Token::KwPub)
159 .or_not()
160 .then_ignore(just(Token::KwUse))
161 .then(
162 ident_token_parser(src.clone())
163 .separated_by(just(Token::ColonColon))
164 .at_least(1),
165 )
166 .then(
167 just(Token::KwAs)
168 .ignore_then(ident_token_parser(src.clone()))
169 .or_not(),
170 )
171 .then_ignore(just(Token::Semicolon))
172 .map_with_span(move |((is_pub, path), alias), span: Range<usize>| {
173 TopLevel::Use(UseDecl {
174 is_pub: is_pub.is_some(),
175 path,
176 kind: UseKind::Simple(alias),
177 span: make_span(&src, span),
178 })
179 });
180
181 let group_item = ident_token_parser(src2.clone()).then(
183 just(Token::KwAs)
184 .ignore_then(ident_token_parser(src2.clone()))
185 .or_not(),
186 );
187
188 let group_use = just(Token::KwPub)
190 .or_not()
191 .then_ignore(just(Token::KwUse))
192 .then(
193 ident_token_parser(src3.clone())
194 .then_ignore(just(Token::ColonColon))
195 .repeated()
196 .at_least(1),
197 )
198 .then(
199 group_item
200 .separated_by(just(Token::Comma))
201 .allow_trailing()
202 .delimited_by(just(Token::LBrace), just(Token::RBrace)),
203 )
204 .then_ignore(just(Token::Semicolon))
205 .map_with_span(move |((is_pub, path), items), span: Range<usize>| {
206 TopLevel::Use(UseDecl {
207 is_pub: is_pub.is_some(),
208 path,
209 kind: UseKind::Group(items),
210 span: make_span(&src3, span),
211 })
212 });
213
214 let glob_use = just(Token::KwPub)
216 .or_not()
217 .then_ignore(just(Token::KwUse))
218 .then(
219 ident_token_parser(src4.clone())
220 .then_ignore(just(Token::ColonColon))
221 .repeated()
222 .at_least(1),
223 )
224 .then_ignore(just(Token::Star))
225 .then_ignore(just(Token::Semicolon))
226 .map_with_span(move |(is_pub, path), span: Range<usize>| {
227 TopLevel::Use(UseDecl {
228 is_pub: is_pub.is_some(),
229 path,
230 kind: UseKind::Glob,
231 span: make_span(&src4, span),
232 })
233 });
234
235 group_use.or(glob_use).or(simple_use)
237}
238
239#[allow(clippy::needless_pass_by_value)]
245fn record_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
246 let src = source.clone();
247 let src2 = source.clone();
248
249 let field = ident_token_parser(src.clone())
251 .then_ignore(just(Token::Colon))
252 .then(type_parser(src.clone()))
253 .map_with_span(move |(name, ty), span: Range<usize>| RecordField {
254 name,
255 ty,
256 span: make_span(&src, span),
257 });
258
259 just(Token::KwPub)
260 .or_not()
261 .then_ignore(just(Token::KwRecord))
262 .then(ident_token_parser(src2.clone()))
263 .then(
264 field
265 .separated_by(just(Token::Comma))
266 .allow_trailing()
267 .delimited_by(just(Token::LBrace), just(Token::RBrace)),
268 )
269 .map_with_span(move |((is_pub, name), fields), span: Range<usize>| {
270 TopLevel::Record(RecordDecl {
271 is_pub: is_pub.is_some(),
272 name,
273 fields,
274 span: make_span(&src2, span),
275 })
276 })
277}
278
279#[allow(clippy::needless_pass_by_value)]
281fn enum_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
282 let src = source.clone();
283 let src2 = source.clone();
284
285 just(Token::KwPub)
286 .or_not()
287 .then_ignore(just(Token::KwEnum))
288 .then(ident_token_parser(src.clone()))
289 .then(
290 ident_token_parser(src.clone())
291 .separated_by(just(Token::Comma))
292 .allow_trailing()
293 .delimited_by(just(Token::LBrace), just(Token::RBrace)),
294 )
295 .map_with_span(move |((is_pub, name), variants), span: Range<usize>| {
296 TopLevel::Enum(EnumDecl {
297 is_pub: is_pub.is_some(),
298 name,
299 variants,
300 span: make_span(&src2, span),
301 })
302 })
303}
304
305#[allow(clippy::needless_pass_by_value)]
307fn const_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
308 let src = source.clone();
309 let src2 = source.clone();
310
311 just(Token::KwPub)
312 .or_not()
313 .then_ignore(just(Token::KwConst))
314 .then(ident_token_parser(src.clone()))
315 .then_ignore(just(Token::Colon))
316 .then(type_parser(src.clone()))
317 .then_ignore(just(Token::Eq))
318 .then(expr_parser(src.clone()))
319 .then_ignore(just(Token::Semicolon))
320 .map_with_span(move |(((is_pub, name), ty), value), span: Range<usize>| {
321 TopLevel::Const(ConstDecl {
322 is_pub: is_pub.is_some(),
323 name,
324 ty,
325 value,
326 span: make_span(&src2, span),
327 })
328 })
329}
330
331#[allow(clippy::needless_pass_by_value)]
337fn agent_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 let src4 = source.clone();
342
343 let belief = ident_token_parser(src.clone())
346 .then_ignore(just(Token::Colon))
347 .then(type_parser(src.clone()))
348 .map_with_span(move |(name, ty), span: Range<usize>| BeliefDecl {
349 name,
350 ty,
351 span: make_span(&src, span),
352 });
353
354 let handler = just(Token::KwOn)
355 .ignore_then(event_kind_parser(src2.clone()))
356 .then(block_parser(src2.clone()))
357 .map_with_span(move |(event, body), span: Range<usize>| HandlerDecl {
358 event,
359 body,
360 span: make_span(&src2, span),
361 });
362
363 let receives_clause = just(Token::KwReceives)
365 .ignore_then(type_parser(src3.clone()))
366 .or_not();
367
368 just(Token::KwPub)
369 .or_not()
370 .then_ignore(just(Token::KwAgent))
371 .then(ident_token_parser(src3.clone()))
372 .then(receives_clause)
373 .then_ignore(just(Token::LBrace))
374 .then(belief.repeated())
375 .then(handler.repeated())
376 .then_ignore(just(Token::RBrace))
377 .map_with_span(
378 move |((((is_pub, name), receives), beliefs), handlers), span: Range<usize>| {
379 TopLevel::Agent(AgentDecl {
380 is_pub: is_pub.is_some(),
381 name,
382 receives,
383 beliefs,
384 handlers,
385 span: make_span(&src4, span),
386 })
387 },
388 )
389}
390
391#[allow(clippy::needless_pass_by_value)]
393fn event_kind_parser(source: Arc<str>) -> impl Parser<Token, EventKind, Error = ParseError> {
394 let src = source.clone();
395
396 let start = just(Token::KwStart).to(EventKind::Start);
397 let stop = just(Token::KwStop).to(EventKind::Stop);
398
399 let message = just(Token::KwMessage)
400 .ignore_then(just(Token::LParen))
401 .ignore_then(ident_token_parser(src.clone()))
402 .then_ignore(just(Token::Colon))
403 .then(type_parser(src.clone()))
404 .then_ignore(just(Token::RParen))
405 .map(|(param_name, param_ty)| EventKind::Message {
406 param_name,
407 param_ty,
408 });
409
410 let error = just(Token::KwError)
412 .ignore_then(just(Token::LParen))
413 .ignore_then(ident_token_parser(src))
414 .then_ignore(just(Token::RParen))
415 .map(|param_name| EventKind::Error { param_name });
416
417 start.or(stop).or(message).or(error)
418}
419
420#[allow(clippy::needless_pass_by_value)]
426fn fn_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
427 let src = source.clone();
428 let src2 = source.clone();
429 let src3 = source.clone();
430
431 let param = ident_token_parser(src.clone())
432 .then_ignore(just(Token::Colon))
433 .then(type_parser(src.clone()))
434 .map_with_span(move |(name, ty), span: Range<usize>| Param {
435 name,
436 ty,
437 span: make_span(&src, span),
438 });
439
440 let params = param
441 .separated_by(just(Token::Comma))
442 .allow_trailing()
443 .delimited_by(just(Token::LParen), just(Token::RParen));
444
445 just(Token::KwPub)
446 .or_not()
447 .then_ignore(just(Token::KwFn))
448 .then(ident_token_parser(src2.clone()))
449 .then(params)
450 .then_ignore(just(Token::Arrow))
451 .then(type_parser(src2.clone()))
452 .then(just(Token::KwFails).or_not())
453 .then(block_parser(src2))
454 .map_with_span(
455 move |(((((is_pub, name), params), return_ty), is_fallible), body),
456 span: Range<usize>| {
457 TopLevel::Function(FnDecl {
458 is_pub: is_pub.is_some(),
459 name,
460 params,
461 return_ty,
462 is_fallible: is_fallible.is_some(),
463 body,
464 span: make_span(&src3, span),
465 })
466 },
467 )
468}
469
470#[allow(clippy::needless_pass_by_value)]
477fn block_parser(source: Arc<str>) -> BoxedParser<'static, Token, Block, ParseError> {
478 let src = source.clone();
479
480 recursive(move |block: Recursive<Token, Block, ParseError>| {
481 let src_inner = src.clone();
482 stmt_parser(src.clone(), block)
483 .repeated()
484 .delimited_by(just(Token::LBrace), just(Token::RBrace))
485 .recover_with(nested_delimiters(
486 Token::LBrace,
487 Token::RBrace,
488 [
489 (Token::LParen, Token::RParen),
490 (Token::LBracket, Token::RBracket),
491 ],
492 |_span: Range<usize>| vec![],
493 ))
494 .map_with_span(move |stmts, span: Range<usize>| Block {
495 stmts,
496 span: make_span(&src_inner, span),
497 })
498 })
499 .boxed()
500}
501
502#[allow(clippy::needless_pass_by_value)]
504fn stmt_parser(
505 source: Arc<str>,
506 block: impl Parser<Token, Block, Error = ParseError> + Clone + 'static,
507) -> impl Parser<Token, Stmt, Error = ParseError> + Clone {
508 let src = source.clone();
509 let src2 = source.clone();
510 let src3 = source.clone();
511 let src4 = source.clone();
512 let src5 = source.clone();
513 let src6 = source.clone();
514 let src7 = source.clone();
515
516 let let_stmt = just(Token::KwLet)
517 .ignore_then(ident_token_parser(src.clone()))
518 .then(
519 just(Token::Colon)
520 .ignore_then(type_parser(src.clone()))
521 .or_not(),
522 )
523 .then_ignore(just(Token::Eq))
524 .then(expr_parser(src.clone()))
525 .then_ignore(just(Token::Semicolon))
526 .map_with_span(move |((name, ty), value), span: Range<usize>| Stmt::Let {
527 name,
528 ty,
529 value,
530 span: make_span(&src, span),
531 });
532
533 let return_stmt = just(Token::KwReturn)
534 .ignore_then(expr_parser(src2.clone()).or_not())
535 .then_ignore(just(Token::Semicolon))
536 .map_with_span(move |value, span: Range<usize>| Stmt::Return {
537 value,
538 span: make_span(&src2, span),
539 });
540
541 let if_stmt = recursive(|if_stmt| {
542 let src_if = src3.clone();
543 let block_clone = block.clone();
544
545 just(Token::KwIf)
546 .ignore_then(expr_parser(src3.clone()))
547 .then(block_clone.clone())
548 .then(
549 just(Token::KwElse)
550 .ignore_then(
551 if_stmt
552 .map(|s| ElseBranch::ElseIf(Box::new(s)))
553 .or(block_clone.map(ElseBranch::Block)),
554 )
555 .or_not(),
556 )
557 .map_with_span(
558 move |((condition, then_block), else_block), span: Range<usize>| Stmt::If {
559 condition,
560 then_block,
561 else_block,
562 span: make_span(&src_if, span),
563 },
564 )
565 });
566
567 let for_stmt = just(Token::KwFor)
568 .ignore_then(ident_token_parser(src4.clone()))
569 .then_ignore(just(Token::KwIn))
570 .then(expr_parser(src4.clone()))
571 .then(block.clone())
572 .map_with_span(move |((var, iter), body), span: Range<usize>| Stmt::For {
573 var,
574 iter,
575 body,
576 span: make_span(&src4, span),
577 });
578
579 let while_stmt = just(Token::KwWhile)
580 .ignore_then(expr_parser(src7.clone()))
581 .then(block.clone())
582 .map_with_span(move |(condition, body), span: Range<usize>| Stmt::While {
583 condition,
584 body,
585 span: make_span(&src7, span),
586 });
587
588 let src8 = source.clone();
589 let loop_stmt = just(Token::KwLoop)
590 .ignore_then(block.clone())
591 .map_with_span(move |body, span: Range<usize>| Stmt::Loop {
592 body,
593 span: make_span(&src8, span),
594 });
595
596 let src9 = source.clone();
597 let break_stmt = just(Token::KwBreak)
598 .then_ignore(just(Token::Semicolon))
599 .map_with_span(move |_, span: Range<usize>| Stmt::Break {
600 span: make_span(&src9, span),
601 });
602
603 let assign_stmt = ident_token_parser(src5.clone())
604 .then_ignore(just(Token::Eq))
605 .then(expr_parser(src5.clone()))
606 .then_ignore(just(Token::Semicolon))
607 .map_with_span(move |(name, value), span: Range<usize>| Stmt::Assign {
608 name,
609 value,
610 span: make_span(&src5, span),
611 });
612
613 let expr_stmt = expr_parser(src6.clone())
614 .then_ignore(just(Token::Semicolon))
615 .map_with_span(move |expr, span: Range<usize>| Stmt::Expr {
616 expr,
617 span: make_span(&src6, span),
618 });
619
620 let_stmt
621 .or(return_stmt)
622 .or(if_stmt)
623 .or(for_stmt)
624 .or(while_stmt)
625 .or(loop_stmt)
626 .or(break_stmt)
627 .or(assign_stmt)
628 .or(expr_stmt)
629}
630
631#[allow(clippy::needless_pass_by_value, clippy::too_many_lines)]
638fn expr_parser(source: Arc<str>) -> BoxedParser<'static, Token, Expr, ParseError> {
639 recursive(move |expr: Recursive<Token, Expr, ParseError>| {
640 let src = source.clone();
641
642 let literal = literal_parser(src.clone());
643 let var = var_parser(src.clone());
644
645 let paren = expr
646 .clone()
647 .delimited_by(just(Token::LParen), just(Token::RParen))
648 .map_with_span({
649 let src = src.clone();
650 move |inner, span: Range<usize>| Expr::Paren {
651 inner: Box::new(inner),
652 span: make_span(&src, span),
653 }
654 });
655
656 let list = expr
657 .clone()
658 .separated_by(just(Token::Comma))
659 .allow_trailing()
660 .delimited_by(just(Token::LBracket), just(Token::RBracket))
661 .map_with_span({
662 let src = src.clone();
663 move |elements, span: Range<usize>| Expr::List {
664 elements,
665 span: make_span(&src, span),
666 }
667 });
668
669 let self_access = just(Token::KwSelf)
671 .ignore_then(just(Token::Dot))
672 .ignore_then(ident_token_parser(src.clone()))
673 .then(
674 expr.clone()
675 .separated_by(just(Token::Comma))
676 .allow_trailing()
677 .delimited_by(just(Token::LParen), just(Token::RParen))
678 .or_not(),
679 )
680 .map_with_span({
681 let src = src.clone();
682 move |(field, args), span: Range<usize>| match args {
683 Some(args) => Expr::SelfMethodCall {
684 method: field,
685 args,
686 span: make_span(&src, span),
687 },
688 None => Expr::SelfField {
689 field,
690 span: make_span(&src, span),
691 },
692 }
693 });
694
695 let infer_expr = just(Token::KwInfer)
697 .ignore_then(just(Token::LParen))
698 .ignore_then(string_template_parser(src.clone()))
699 .then(
700 just(Token::Arrow)
701 .ignore_then(type_parser(src.clone()))
702 .or_not(),
703 )
704 .then_ignore(just(Token::RParen))
705 .map_with_span({
706 let src = src.clone();
707 move |(template, result_ty), span: Range<usize>| Expr::Infer {
708 template,
709 result_ty,
710 span: make_span(&src, span),
711 }
712 });
713
714 let spawn_field_init = ident_token_parser(src.clone())
716 .then_ignore(just(Token::Colon))
717 .then(expr.clone())
718 .map_with_span({
719 let src = src.clone();
720 move |(name, value), span: Range<usize>| FieldInit {
721 name,
722 value,
723 span: make_span(&src, span),
724 }
725 });
726
727 let spawn_expr = just(Token::KwSpawn)
728 .ignore_then(ident_token_parser(src.clone()))
729 .then_ignore(just(Token::LBrace))
730 .then(
731 spawn_field_init
732 .separated_by(just(Token::Comma))
733 .allow_trailing(),
734 )
735 .then_ignore(just(Token::RBrace))
736 .map_with_span({
737 let src = src.clone();
738 move |(agent, fields), span: Range<usize>| Expr::Spawn {
739 agent,
740 fields,
741 span: make_span(&src, span),
742 }
743 });
744
745 let await_expr = just(Token::KwAwait)
747 .ignore_then(ident_token_parser(src.clone()).map_with_span({
748 let src = src.clone();
749 move |name, span: Range<usize>| Expr::Var {
750 name,
751 span: make_span(&src, span),
752 }
753 }))
754 .map_with_span({
755 let src = src.clone();
756 move |handle, span: Range<usize>| Expr::Await {
757 handle: Box::new(handle),
758 span: make_span(&src, span),
759 }
760 });
761
762 let send_expr = just(Token::KwSend)
764 .ignore_then(just(Token::LParen))
765 .ignore_then(expr.clone())
766 .then_ignore(just(Token::Comma))
767 .then(expr.clone())
768 .then_ignore(just(Token::RParen))
769 .map_with_span({
770 let src = src.clone();
771 move |(handle, message), span: Range<usize>| Expr::Send {
772 handle: Box::new(handle),
773 message: Box::new(message),
774 span: make_span(&src, span),
775 }
776 });
777
778 let emit_expr = just(Token::KwEmit)
780 .ignore_then(just(Token::LParen))
781 .ignore_then(expr.clone())
782 .then_ignore(just(Token::RParen))
783 .map_with_span({
784 let src = src.clone();
785 move |value, span: Range<usize>| Expr::Emit {
786 value: Box::new(value),
787 span: make_span(&src, span),
788 }
789 });
790
791 let call_expr = ident_token_parser(src.clone())
793 .then(
794 expr.clone()
795 .separated_by(just(Token::Comma))
796 .allow_trailing()
797 .delimited_by(just(Token::LParen), just(Token::RParen)),
798 )
799 .map_with_span({
800 let src = src.clone();
801 move |(name, args), span: Range<usize>| Expr::Call {
802 name,
803 args,
804 span: make_span(&src, span),
805 }
806 });
807
808 let pattern = pattern_parser(src.clone());
810
811 let match_arm = pattern
813 .then_ignore(just(Token::FatArrow))
814 .then(expr.clone())
815 .map_with_span({
816 let src = src.clone();
817 move |(pattern, body), span: Range<usize>| MatchArm {
818 pattern,
819 body,
820 span: make_span(&src, span),
821 }
822 });
823
824 let match_expr = just(Token::KwMatch)
825 .ignore_then(expr.clone())
826 .then(
827 match_arm
828 .separated_by(just(Token::Comma))
829 .allow_trailing()
830 .delimited_by(just(Token::LBrace), just(Token::RBrace)),
831 )
832 .map_with_span({
833 let src = src.clone();
834 move |(scrutinee, arms), span: Range<usize>| Expr::Match {
835 scrutinee: Box::new(scrutinee),
836 arms,
837 span: make_span(&src, span),
838 }
839 });
840
841 let receive_expr = just(Token::KwReceive)
843 .ignore_then(just(Token::LParen))
844 .ignore_then(just(Token::RParen))
845 .map_with_span({
846 let src = src.clone();
847 move |_, span: Range<usize>| Expr::Receive {
848 span: make_span(&src, span),
849 }
850 });
851
852 let record_field_init = ident_token_parser(src.clone())
856 .then_ignore(just(Token::Colon))
857 .then(expr.clone())
858 .map_with_span({
859 let src = src.clone();
860 move |(name, value), span: Range<usize>| FieldInit {
861 name,
862 value,
863 span: make_span(&src, span),
864 }
865 });
866
867 let record_construct = ident_token_parser(src.clone())
868 .then_ignore(just(Token::LBrace))
869 .then(
870 record_field_init
871 .separated_by(just(Token::Comma))
872 .allow_trailing(),
873 )
874 .then_ignore(just(Token::RBrace))
875 .map_with_span({
876 let src = src.clone();
877 move |(name, fields), span: Range<usize>| Expr::RecordConstruct {
878 name,
879 fields,
880 span: make_span(&src, span),
881 }
882 });
883
884 let atom = infer_expr
889 .or(spawn_expr)
890 .or(await_expr)
891 .or(send_expr)
892 .or(emit_expr)
893 .or(receive_expr)
894 .or(match_expr)
895 .or(self_access)
896 .or(record_construct)
897 .or(call_expr)
898 .or(list)
899 .or(paren)
900 .or(literal)
901 .or(var)
902 .boxed();
903
904 let postfix = atom
906 .then(
907 just(Token::Dot)
908 .ignore_then(ident_token_parser(src.clone()))
909 .repeated(),
910 )
911 .foldl({
912 let src = src.clone();
913 move |object, field| {
914 let span = make_span(&src, object.span().start..field.span.end);
915 Expr::FieldAccess {
916 object: Box::new(object),
917 field,
918 span,
919 }
920 }
921 })
922 .boxed();
923
924 let unary = just(Token::Minus)
926 .to(UnaryOp::Neg)
927 .or(just(Token::Bang).to(UnaryOp::Not))
928 .repeated()
929 .then(postfix.clone())
930 .foldr(|op, operand| {
931 let span = operand.span().clone();
932 Expr::Unary {
933 op,
934 operand: Box::new(operand),
935 span,
936 }
937 })
938 .boxed();
939
940 let try_expr = just(Token::KwTry)
943 .ignore_then(postfix)
944 .map_with_span({
945 let src = src.clone();
946 move |inner, span: Range<usize>| Expr::Try {
947 expr: Box::new(inner),
948 span: make_span(&src, span),
949 }
950 })
951 .boxed();
952
953 let unary = try_expr.or(unary).boxed();
955
956 let mul_div_op = just(Token::Star)
959 .to(BinOp::Mul)
960 .or(just(Token::Slash).to(BinOp::Div));
961
962 let mul_div = unary
963 .clone()
964 .then(mul_div_op.then(unary.clone()).repeated())
965 .foldl({
966 let src = src.clone();
967 move |left, (op, right)| {
968 let span = make_span(&src, left.span().start..right.span().end);
969 Expr::Binary {
970 op,
971 left: Box::new(left),
972 right: Box::new(right),
973 span,
974 }
975 }
976 })
977 .boxed();
978
979 let add_sub_op = just(Token::Plus)
981 .to(BinOp::Add)
982 .or(just(Token::Minus).to(BinOp::Sub));
983
984 let add_sub = mul_div
985 .clone()
986 .then(add_sub_op.then(mul_div).repeated())
987 .foldl({
988 let src = src.clone();
989 move |left, (op, right)| {
990 let span = make_span(&src, left.span().start..right.span().end);
991 Expr::Binary {
992 op,
993 left: Box::new(left),
994 right: Box::new(right),
995 span,
996 }
997 }
998 })
999 .boxed();
1000
1001 let concat_op = just(Token::PlusPlus).to(BinOp::Concat);
1003
1004 let concat = add_sub
1005 .clone()
1006 .then(concat_op.then(add_sub).repeated())
1007 .foldl({
1008 let src = src.clone();
1009 move |left, (op, right)| {
1010 let span = make_span(&src, left.span().start..right.span().end);
1011 Expr::Binary {
1012 op,
1013 left: Box::new(left),
1014 right: Box::new(right),
1015 span,
1016 }
1017 }
1018 })
1019 .boxed();
1020
1021 let cmp_op = choice((
1023 just(Token::Le).to(BinOp::Le),
1024 just(Token::Ge).to(BinOp::Ge),
1025 just(Token::Lt).to(BinOp::Lt),
1026 just(Token::Gt).to(BinOp::Gt),
1027 ));
1028
1029 let comparison = concat
1030 .clone()
1031 .then(cmp_op.then(concat).repeated())
1032 .foldl({
1033 let src = src.clone();
1034 move |left, (op, right)| {
1035 let span = make_span(&src, left.span().start..right.span().end);
1036 Expr::Binary {
1037 op,
1038 left: Box::new(left),
1039 right: Box::new(right),
1040 span,
1041 }
1042 }
1043 })
1044 .boxed();
1045
1046 let eq_op = just(Token::EqEq)
1048 .to(BinOp::Eq)
1049 .or(just(Token::Ne).to(BinOp::Ne));
1050
1051 let equality = comparison
1052 .clone()
1053 .then(eq_op.then(comparison).repeated())
1054 .foldl({
1055 let src = src.clone();
1056 move |left, (op, right)| {
1057 let span = make_span(&src, left.span().start..right.span().end);
1058 Expr::Binary {
1059 op,
1060 left: Box::new(left),
1061 right: Box::new(right),
1062 span,
1063 }
1064 }
1065 })
1066 .boxed();
1067
1068 let and_op = just(Token::And).to(BinOp::And);
1070
1071 let and = equality
1072 .clone()
1073 .then(and_op.then(equality).repeated())
1074 .foldl({
1075 let src = src.clone();
1076 move |left, (op, right)| {
1077 let span = make_span(&src, left.span().start..right.span().end);
1078 Expr::Binary {
1079 op,
1080 left: Box::new(left),
1081 right: Box::new(right),
1082 span,
1083 }
1084 }
1085 })
1086 .boxed();
1087
1088 let or_op = just(Token::Or).to(BinOp::Or);
1090
1091 let or_expr = and.clone().then(or_op.then(and).repeated()).foldl({
1092 let src = src.clone();
1093 move |left, (op, right)| {
1094 let span = make_span(&src, left.span().start..right.span().end);
1095 Expr::Binary {
1096 op,
1097 left: Box::new(left),
1098 right: Box::new(right),
1099 span,
1100 }
1101 }
1102 });
1103
1104 let catch_recovery = just(Token::KwCatch)
1107 .ignore_then(
1108 ident_token_parser(src.clone())
1109 .delimited_by(just(Token::LParen), just(Token::RParen))
1110 .or_not(),
1111 )
1112 .then(
1113 expr.clone()
1114 .delimited_by(just(Token::LBrace), just(Token::RBrace)),
1115 );
1116
1117 or_expr.then(catch_recovery.or_not()).map_with_span({
1118 let src = src.clone();
1119 move |(inner, catch_opt), span: Range<usize>| match catch_opt {
1120 Some((error_bind, recovery)) => Expr::Catch {
1121 expr: Box::new(inner),
1122 error_bind,
1123 recovery: Box::new(recovery),
1124 span: make_span(&src, span),
1125 },
1126 None => inner,
1127 }
1128 })
1129 })
1130 .boxed()
1131}
1132
1133fn make_span(source: &Arc<str>, range: Range<usize>) -> Span {
1139 Span::new(range.start, range.end, Arc::clone(source))
1140}
1141
1142fn ident_token_parser(source: Arc<str>) -> impl Parser<Token, Ident, Error = ParseError> + Clone {
1144 filter_map(move |span: Range<usize>, token| match token {
1145 Token::Ident => {
1146 let text = &source[span.start..span.end];
1147 Ok(Ident::new(text.to_string(), make_span(&source, span)))
1148 }
1149 _ => Err(Simple::expected_input_found(
1150 span,
1151 vec![Some(Token::Ident)],
1152 Some(token),
1153 )),
1154 })
1155}
1156
1157fn var_parser(source: Arc<str>) -> impl Parser<Token, Expr, Error = ParseError> + Clone {
1159 ident_token_parser(source.clone()).map_with_span(move |name, span: Range<usize>| Expr::Var {
1160 name,
1161 span: make_span(&source, span),
1162 })
1163}
1164
1165fn type_parser(source: Arc<str>) -> impl Parser<Token, TypeExpr, Error = ParseError> + Clone {
1167 recursive(move |ty| {
1168 let src = source.clone();
1169
1170 let primitive = choice((
1171 just(Token::TyInt).to(TypeExpr::Int),
1172 just(Token::TyFloat).to(TypeExpr::Float),
1173 just(Token::TyBool).to(TypeExpr::Bool),
1174 just(Token::TyString).to(TypeExpr::String),
1175 just(Token::TyUnit).to(TypeExpr::Unit),
1176 ));
1177
1178 let list_ty = just(Token::TyList)
1179 .ignore_then(just(Token::Lt))
1180 .ignore_then(ty.clone())
1181 .then_ignore(just(Token::Gt))
1182 .map(|inner| TypeExpr::List(Box::new(inner)));
1183
1184 let option_ty = just(Token::TyOption)
1185 .ignore_then(just(Token::Lt))
1186 .ignore_then(ty.clone())
1187 .then_ignore(just(Token::Gt))
1188 .map(|inner| TypeExpr::Option(Box::new(inner)));
1189
1190 let inferred_ty = just(Token::TyInferred)
1191 .ignore_then(just(Token::Lt))
1192 .ignore_then(ty.clone())
1193 .then_ignore(just(Token::Gt))
1194 .map(|inner| TypeExpr::Inferred(Box::new(inner)));
1195
1196 let agent_ty = just(Token::TyAgent)
1197 .ignore_then(just(Token::Lt))
1198 .ignore_then(ident_token_parser(src.clone()))
1199 .then_ignore(just(Token::Gt))
1200 .map(TypeExpr::Agent);
1201
1202 let named_ty = ident_token_parser(src).map(TypeExpr::Named);
1203
1204 primitive
1205 .or(list_ty)
1206 .or(option_ty)
1207 .or(inferred_ty)
1208 .or(agent_ty)
1209 .or(named_ty)
1210 })
1211}
1212
1213fn pattern_parser(source: Arc<str>) -> impl Parser<Token, Pattern, Error = ParseError> + Clone {
1215 let src = source.clone();
1216 let src2 = source.clone();
1217 let src3 = source.clone();
1218 let src4 = source.clone();
1219
1220 let wildcard = filter_map(move |span: Range<usize>, token| match &token {
1222 Token::Ident if src[span.start..span.end].eq("_") => Ok(()),
1223 _ => Err(Simple::expected_input_found(span, vec![], Some(token))),
1224 })
1225 .map_with_span(move |_, span: Range<usize>| Pattern::Wildcard {
1226 span: make_span(&src2, span),
1227 });
1228
1229 let lit_int = filter_map({
1231 let src = src3.clone();
1232 move |span: Range<usize>, token| match token {
1233 Token::IntLit => {
1234 let text = &src[span.start..span.end];
1235 text.parse::<i64>()
1236 .map(Literal::Int)
1237 .map_err(|_| Simple::custom(span, "invalid integer literal"))
1238 }
1239 _ => Err(Simple::expected_input_found(
1240 span,
1241 vec![Some(Token::IntLit)],
1242 Some(token),
1243 )),
1244 }
1245 })
1246 .map_with_span({
1247 let src = src3.clone();
1248 move |value, span: Range<usize>| Pattern::Literal {
1249 value,
1250 span: make_span(&src, span),
1251 }
1252 });
1253
1254 let lit_bool = just(Token::KwTrue)
1255 .to(Literal::Bool(true))
1256 .or(just(Token::KwFalse).to(Literal::Bool(false)))
1257 .map_with_span({
1258 let src = src3.clone();
1259 move |value, span: Range<usize>| Pattern::Literal {
1260 value,
1261 span: make_span(&src, span),
1262 }
1263 });
1264
1265 let qualified_variant = ident_token_parser(src4.clone())
1268 .then_ignore(just(Token::ColonColon))
1269 .then(ident_token_parser(src4.clone()))
1270 .map_with_span({
1271 let src = src4.clone();
1272 move |(enum_name, variant), span: Range<usize>| Pattern::Variant {
1273 enum_name: Some(enum_name),
1274 variant,
1275 span: make_span(&src, span),
1276 }
1277 });
1278
1279 let unqualified = ident_token_parser(src4.clone()).map_with_span({
1283 let src = src4.clone();
1284 move |name, span: Range<usize>| {
1285 if name.name.chars().next().is_some_and(|c| c.is_uppercase()) {
1288 Pattern::Variant {
1289 enum_name: None,
1290 variant: name,
1291 span: make_span(&src, span),
1292 }
1293 } else {
1294 Pattern::Binding {
1295 name,
1296 span: make_span(&src, span),
1297 }
1298 }
1299 }
1300 });
1301
1302 wildcard
1304 .or(qualified_variant)
1305 .or(lit_int)
1306 .or(lit_bool)
1307 .or(unqualified)
1308}
1309
1310fn literal_parser(source: Arc<str>) -> impl Parser<Token, Expr, Error = ParseError> + Clone {
1312 let src = source.clone();
1313 let src2 = source.clone();
1314 let src3 = source.clone();
1315 let src4 = source.clone();
1316 let src5 = source.clone();
1317
1318 let int_lit = filter_map(move |span: Range<usize>, token| match token {
1319 Token::IntLit => {
1320 let text = &src[span.start..span.end];
1321 text.parse::<i64>()
1322 .map(Literal::Int)
1323 .map_err(|_| Simple::custom(span, "invalid integer literal"))
1324 }
1325 _ => Err(Simple::expected_input_found(
1326 span,
1327 vec![Some(Token::IntLit)],
1328 Some(token),
1329 )),
1330 })
1331 .map_with_span(move |value, span: Range<usize>| Expr::Literal {
1332 value,
1333 span: make_span(&src2, span),
1334 });
1335
1336 let float_lit = filter_map(move |span: Range<usize>, token| match token {
1337 Token::FloatLit => {
1338 let text = &src3[span.start..span.end];
1339 text.parse::<f64>()
1340 .map(Literal::Float)
1341 .map_err(|_| Simple::custom(span, "invalid float literal"))
1342 }
1343 _ => Err(Simple::expected_input_found(
1344 span,
1345 vec![Some(Token::FloatLit)],
1346 Some(token),
1347 )),
1348 })
1349 .map_with_span(move |value, span: Range<usize>| Expr::Literal {
1350 value,
1351 span: make_span(&src4, span),
1352 });
1353
1354 let src6 = source.clone();
1355 let string_lit = filter_map(move |span: Range<usize>, token| match token {
1356 Token::StringLit => {
1357 let text = &src5[span.start..span.end];
1358 let inner = &text[1..text.len() - 1];
1359 let parts = parse_string_template(inner, &make_span(&src5, span.clone()));
1360 Ok(parts)
1361 }
1362 _ => Err(Simple::expected_input_found(
1363 span,
1364 vec![Some(Token::StringLit)],
1365 Some(token),
1366 )),
1367 })
1368 .map_with_span(move |parts, span: Range<usize>| {
1369 let span = make_span(&src6, span);
1370 if parts.len() == 1 {
1372 if let StringPart::Literal(s) = &parts[0] {
1373 return Expr::Literal {
1374 value: Literal::String(s.clone()),
1375 span,
1376 };
1377 }
1378 }
1379 Expr::StringInterp {
1381 template: StringTemplate {
1382 parts,
1383 span: span.clone(),
1384 },
1385 span,
1386 }
1387 });
1388
1389 let bool_lit = just(Token::KwTrue)
1390 .to(Literal::Bool(true))
1391 .or(just(Token::KwFalse).to(Literal::Bool(false)))
1392 .map_with_span(move |value, _span: Range<usize>| Expr::Literal {
1393 value,
1394 span: Span::dummy(), });
1396
1397 int_lit.or(float_lit).or(string_lit).or(bool_lit)
1398}
1399
1400fn string_template_parser(
1402 source: Arc<str>,
1403) -> impl Parser<Token, StringTemplate, Error = ParseError> + Clone {
1404 filter_map(move |span: Range<usize>, token| match token {
1405 Token::StringLit => {
1406 let text = &source[span.start..span.end];
1407 let inner = &text[1..text.len() - 1];
1408 let parts = parse_string_template(inner, &make_span(&source, span.clone()));
1409 Ok(StringTemplate {
1410 parts,
1411 span: make_span(&source, span),
1412 })
1413 }
1414 _ => Err(Simple::expected_input_found(
1415 span,
1416 vec![Some(Token::StringLit)],
1417 Some(token),
1418 )),
1419 })
1420}
1421
1422fn parse_string_template(s: &str, span: &Span) -> Vec<StringPart> {
1424 let mut parts = Vec::new();
1425 let mut current = String::new();
1426 let mut chars = s.chars().peekable();
1427
1428 while let Some(ch) = chars.next() {
1429 if ch == '{' {
1430 if !current.is_empty() {
1431 parts.push(StringPart::Literal(std::mem::take(&mut current)));
1432 }
1433
1434 let mut ident_name = String::new();
1435 while let Some(&c) = chars.peek() {
1436 if c == '}' {
1437 chars.next();
1438 break;
1439 }
1440 ident_name.push(c);
1441 chars.next();
1442 }
1443
1444 if !ident_name.is_empty() {
1445 parts.push(StringPart::Interpolation(Ident::new(
1446 ident_name,
1447 span.clone(),
1448 )));
1449 }
1450 } else if ch == '\\' {
1451 if let Some(escaped) = chars.next() {
1452 current.push(match escaped {
1453 'n' => '\n',
1454 't' => '\t',
1455 'r' => '\r',
1456 '\\' => '\\',
1457 '"' => '"',
1458 '{' => '{',
1459 '}' => '}',
1460 other => other,
1461 });
1462 }
1463 } else {
1464 current.push(ch);
1465 }
1466 }
1467
1468 if !current.is_empty() {
1469 parts.push(StringPart::Literal(current));
1470 }
1471
1472 if parts.is_empty() {
1473 parts.push(StringPart::Literal(String::new()));
1474 }
1475
1476 parts
1477}
1478
1479#[cfg(test)]
1484mod tests {
1485 use super::*;
1486 use sage_lexer::lex;
1487
1488 fn parse_str(source: &str) -> (Option<Program>, Vec<ParseError>) {
1489 let lex_result = lex(source).expect("lexing should succeed");
1490 let source_arc: Arc<str> = Arc::from(source);
1491 parse(lex_result.tokens(), source_arc)
1492 }
1493
1494 #[test]
1495 fn parse_minimal_program() {
1496 let source = r#"
1497 agent Main {
1498 on start {
1499 emit(42);
1500 }
1501 }
1502 run Main;
1503 "#;
1504
1505 let (prog, errors) = parse_str(source);
1506 assert!(errors.is_empty(), "errors: {errors:?}");
1507 let prog = prog.expect("should parse");
1508
1509 assert_eq!(prog.agents.len(), 1);
1510 assert_eq!(prog.agents[0].name.name, "Main");
1511 assert_eq!(prog.run_agent.as_ref().unwrap().name, "Main");
1512 }
1513
1514 #[test]
1515 fn parse_agent_with_beliefs() {
1516 let source = r#"
1517 agent Researcher {
1518 topic: String
1519 max_words: Int
1520
1521 on start {
1522 emit(self.topic);
1523 }
1524 }
1525 run Researcher;
1526 "#;
1527
1528 let (prog, errors) = parse_str(source);
1529 assert!(errors.is_empty(), "errors: {errors:?}");
1530 let prog = prog.expect("should parse");
1531
1532 assert_eq!(prog.agents[0].beliefs.len(), 2);
1533 assert_eq!(prog.agents[0].beliefs[0].name.name, "topic");
1534 assert_eq!(prog.agents[0].beliefs[1].name.name, "max_words");
1535 }
1536
1537 #[test]
1538 fn parse_multiple_handlers() {
1539 let source = r#"
1540 agent Worker {
1541 on start {
1542 print("started");
1543 }
1544
1545 on message(msg: String) {
1546 print(msg);
1547 }
1548
1549 on stop {
1550 print("stopped");
1551 }
1552 }
1553 run Worker;
1554 "#;
1555
1556 let (prog, errors) = parse_str(source);
1557 assert!(errors.is_empty(), "errors: {errors:?}");
1558 let prog = prog.expect("should parse");
1559
1560 assert_eq!(prog.agents[0].handlers.len(), 3);
1561 assert_eq!(prog.agents[0].handlers[0].event, EventKind::Start);
1562 assert!(matches!(
1563 prog.agents[0].handlers[1].event,
1564 EventKind::Message { .. }
1565 ));
1566 assert_eq!(prog.agents[0].handlers[2].event, EventKind::Stop);
1567 }
1568
1569 #[test]
1570 fn parse_function() {
1571 let source = r#"
1572 fn greet(name: String) -> String {
1573 return "Hello, " ++ name;
1574 }
1575
1576 agent Main {
1577 on start {
1578 emit(greet("World"));
1579 }
1580 }
1581 run Main;
1582 "#;
1583
1584 let (prog, errors) = parse_str(source);
1585 assert!(errors.is_empty(), "errors: {errors:?}");
1586 let prog = prog.expect("should parse");
1587
1588 assert_eq!(prog.functions.len(), 1);
1589 assert_eq!(prog.functions[0].name.name, "greet");
1590 assert_eq!(prog.functions[0].params.len(), 1);
1591 }
1592
1593 #[test]
1594 fn parse_let_statement() {
1595 let source = r#"
1596 agent Main {
1597 on start {
1598 let x: Int = 42;
1599 let y = "hello";
1600 emit(x);
1601 }
1602 }
1603 run Main;
1604 "#;
1605
1606 let (prog, errors) = parse_str(source);
1607 assert!(errors.is_empty(), "errors: {errors:?}");
1608 let prog = prog.expect("should parse");
1609
1610 let stmts = &prog.agents[0].handlers[0].body.stmts;
1611 assert!(matches!(stmts[0], Stmt::Let { .. }));
1612 assert!(matches!(stmts[1], Stmt::Let { .. }));
1613 }
1614
1615 #[test]
1616 fn parse_if_statement() {
1617 let source = r#"
1618 agent Main {
1619 on start {
1620 if true {
1621 emit(1);
1622 } else {
1623 emit(2);
1624 }
1625 }
1626 }
1627 run Main;
1628 "#;
1629
1630 let (prog, errors) = parse_str(source);
1631 assert!(errors.is_empty(), "errors: {errors:?}");
1632 let prog = prog.expect("should parse");
1633
1634 let stmts = &prog.agents[0].handlers[0].body.stmts;
1635 assert!(matches!(stmts[0], Stmt::If { .. }));
1636 }
1637
1638 #[test]
1639 fn parse_for_loop() {
1640 let source = r#"
1641 agent Main {
1642 on start {
1643 for x in [1, 2, 3] {
1644 print(x);
1645 }
1646 emit(0);
1647 }
1648 }
1649 run Main;
1650 "#;
1651
1652 let (prog, errors) = parse_str(source);
1653 assert!(errors.is_empty(), "errors: {errors:?}");
1654 let prog = prog.expect("should parse");
1655
1656 let stmts = &prog.agents[0].handlers[0].body.stmts;
1657 assert!(matches!(stmts[0], Stmt::For { .. }));
1658 }
1659
1660 #[test]
1661 fn parse_spawn_await() {
1662 let source = r#"
1663 agent Worker {
1664 name: String
1665
1666 on start {
1667 emit(self.name);
1668 }
1669 }
1670
1671 agent Main {
1672 on start {
1673 let w = spawn Worker { name: "test" };
1674 let result = await w;
1675 emit(result);
1676 }
1677 }
1678 run Main;
1679 "#;
1680
1681 let (prog, errors) = parse_str(source);
1682 assert!(errors.is_empty(), "errors: {errors:?}");
1683 prog.expect("should parse");
1684 }
1685
1686 #[test]
1687 fn parse_infer() {
1688 let source = r#"
1689 agent Main {
1690 on start {
1691 let result = infer("What is 2+2?");
1692 emit(result);
1693 }
1694 }
1695 run Main;
1696 "#;
1697
1698 let (prog, errors) = parse_str(source);
1699 assert!(errors.is_empty(), "errors: {errors:?}");
1700 prog.expect("should parse");
1701 }
1702
1703 #[test]
1704 fn parse_binary_precedence() {
1705 let source = r#"
1706 agent Main {
1707 on start {
1708 let x = 2 + 3 * 4;
1709 emit(x);
1710 }
1711 }
1712 run Main;
1713 "#;
1714
1715 let (prog, errors) = parse_str(source);
1716 assert!(errors.is_empty(), "errors: {errors:?}");
1717 let prog = prog.expect("should parse");
1718
1719 let stmts = &prog.agents[0].handlers[0].body.stmts;
1720 if let Stmt::Let { value, .. } = &stmts[0] {
1721 if let Expr::Binary { op, .. } = value {
1722 assert_eq!(*op, BinOp::Add);
1723 } else {
1724 panic!("expected binary expression");
1725 }
1726 }
1727 }
1728
1729 #[test]
1730 fn parse_string_interpolation() {
1731 let source = r#"
1732 agent Main {
1733 on start {
1734 let name = "World";
1735 let msg = infer("Greet {name}");
1736 emit(msg);
1737 }
1738 }
1739 run Main;
1740 "#;
1741
1742 let (prog, errors) = parse_str(source);
1743 assert!(errors.is_empty(), "errors: {errors:?}");
1744 let prog = prog.expect("should parse");
1745
1746 let stmts = &prog.agents[0].handlers[0].body.stmts;
1747 if let Stmt::Let { value, .. } = &stmts[1] {
1748 if let Expr::Infer { template, .. } = value {
1749 assert!(template.has_interpolations());
1750 } else {
1751 panic!("expected infer expression");
1752 }
1753 }
1754 }
1755
1756 #[test]
1761 fn recover_from_malformed_agent_continues_to_next() {
1762 let source = r#"
1764 agent Broken {
1765 x:
1766 }
1767
1768 agent Main {
1769 on start {
1770 emit(42);
1771 }
1772 }
1773 run Main;
1774 "#;
1775
1776 let (prog, errors) = parse_str(source);
1777 assert!(!errors.is_empty(), "should have parse errors");
1779 let prog = prog.expect("should produce partial AST");
1781 assert!(prog.agents.iter().any(|a| a.name.name == "Main"));
1782 }
1783
1784 #[test]
1785 fn recover_from_mismatched_braces_in_block() {
1786 let source = r#"
1787 agent Main {
1788 on start {
1789 let x = [1, 2, 3;
1790 emit(42);
1791 }
1792 }
1793 run Main;
1794 "#;
1795
1796 let (prog, errors) = parse_str(source);
1797 assert!(!errors.is_empty(), "should have parse errors");
1799 assert!(prog.is_some(), "should produce partial AST despite errors");
1800 }
1801
1802 #[test]
1803 fn parse_mod_declaration() {
1804 let source = r#"
1805 mod agents;
1806 pub mod utils;
1807
1808 agent Main {
1809 on start {
1810 emit(42);
1811 }
1812 }
1813 run Main;
1814 "#;
1815
1816 let (prog, errors) = parse_str(source);
1817 assert!(errors.is_empty(), "errors: {errors:?}");
1818 let prog = prog.expect("should parse");
1819
1820 assert_eq!(prog.mod_decls.len(), 2);
1821 assert!(!prog.mod_decls[0].is_pub);
1822 assert_eq!(prog.mod_decls[0].name.name, "agents");
1823 assert!(prog.mod_decls[1].is_pub);
1824 assert_eq!(prog.mod_decls[1].name.name, "utils");
1825 }
1826
1827 #[test]
1828 fn parse_use_simple() {
1829 let source = r#"
1830 use agents::Researcher;
1831
1832 agent Main {
1833 on start {
1834 emit(42);
1835 }
1836 }
1837 run Main;
1838 "#;
1839
1840 let (prog, errors) = parse_str(source);
1841 assert!(errors.is_empty(), "errors: {errors:?}");
1842 let prog = prog.expect("should parse");
1843
1844 assert_eq!(prog.use_decls.len(), 1);
1845 assert!(!prog.use_decls[0].is_pub);
1846 assert_eq!(prog.use_decls[0].path.len(), 2);
1847 assert_eq!(prog.use_decls[0].path[0].name, "agents");
1848 assert_eq!(prog.use_decls[0].path[1].name, "Researcher");
1849 assert!(matches!(prog.use_decls[0].kind, UseKind::Simple(None)));
1850 }
1851
1852 #[test]
1853 fn parse_use_with_alias() {
1854 let source = r#"
1855 use agents::Researcher as R;
1856
1857 agent Main {
1858 on start {
1859 emit(42);
1860 }
1861 }
1862 run Main;
1863 "#;
1864
1865 let (prog, errors) = parse_str(source);
1866 assert!(errors.is_empty(), "errors: {errors:?}");
1867 let prog = prog.expect("should parse");
1868
1869 assert_eq!(prog.use_decls.len(), 1);
1870 if let UseKind::Simple(Some(alias)) = &prog.use_decls[0].kind {
1871 assert_eq!(alias.name, "R");
1872 } else {
1873 panic!("expected Simple with alias");
1874 }
1875 }
1876
1877 #[test]
1878 fn parse_pub_agent() {
1879 let source = r#"
1880 pub agent Worker {
1881 on start {
1882 emit(42);
1883 }
1884 }
1885
1886 agent Main {
1887 on start {
1888 emit(0);
1889 }
1890 }
1891 run Main;
1892 "#;
1893
1894 let (prog, errors) = parse_str(source);
1895 assert!(errors.is_empty(), "errors: {errors:?}");
1896 let prog = prog.expect("should parse");
1897
1898 assert_eq!(prog.agents.len(), 2);
1899 assert!(prog.agents[0].is_pub);
1900 assert_eq!(prog.agents[0].name.name, "Worker");
1901 assert!(!prog.agents[1].is_pub);
1902 }
1903
1904 #[test]
1905 fn parse_pub_function() {
1906 let source = r#"
1907 pub fn helper(x: Int) -> Int {
1908 return x;
1909 }
1910
1911 agent Main {
1912 on start {
1913 emit(helper(42));
1914 }
1915 }
1916 run Main;
1917 "#;
1918
1919 let (prog, errors) = parse_str(source);
1920 assert!(errors.is_empty(), "errors: {errors:?}");
1921 let prog = prog.expect("should parse");
1922
1923 assert_eq!(prog.functions.len(), 1);
1924 assert!(prog.functions[0].is_pub);
1925 assert_eq!(prog.functions[0].name.name, "helper");
1926 }
1927
1928 #[test]
1929 fn parse_library_no_run() {
1930 let source = r#"
1932 pub agent Worker {
1933 on start {
1934 emit(42);
1935 }
1936 }
1937
1938 pub fn helper(x: Int) -> Int {
1939 return x;
1940 }
1941 "#;
1942
1943 let (prog, errors) = parse_str(source);
1944 assert!(errors.is_empty(), "errors: {errors:?}");
1945 let prog = prog.expect("should parse");
1946
1947 assert!(prog.run_agent.is_none());
1948 assert_eq!(prog.agents.len(), 1);
1949 assert_eq!(prog.functions.len(), 1);
1950 }
1951
1952 #[test]
1953 fn recover_multiple_errors_reported() {
1954 let source = r#"
1956 agent A {
1957 x:
1958 }
1959
1960 agent Main {
1961 on start {
1962 emit(42);
1963 }
1964 }
1965 run Main;
1966 "#;
1967
1968 let (prog, errors) = parse_str(source);
1969 if errors.is_empty() {
1973 let prog = prog.expect("should have AST with recovery");
1975 assert!(prog.agents.iter().any(|a| a.name.name == "Main"));
1976 }
1977 }
1979
1980 #[test]
1981 fn parse_record_declaration() {
1982 let source = r#"
1983 record Point {
1984 x: Int,
1985 y: Int,
1986 }
1987
1988 agent Main {
1989 on start {
1990 emit(0);
1991 }
1992 }
1993 run Main;
1994 "#;
1995
1996 let (prog, errors) = parse_str(source);
1997 assert!(errors.is_empty(), "errors: {errors:?}");
1998 let prog = prog.expect("should parse");
1999
2000 assert_eq!(prog.records.len(), 1);
2001 assert!(!prog.records[0].is_pub);
2002 assert_eq!(prog.records[0].name.name, "Point");
2003 assert_eq!(prog.records[0].fields.len(), 2);
2004 assert_eq!(prog.records[0].fields[0].name.name, "x");
2005 assert_eq!(prog.records[0].fields[1].name.name, "y");
2006 }
2007
2008 #[test]
2009 fn parse_pub_record() {
2010 let source = r#"
2011 pub record Config {
2012 host: String,
2013 port: Int,
2014 }
2015
2016 agent Main {
2017 on start { emit(0); }
2018 }
2019 run Main;
2020 "#;
2021
2022 let (prog, errors) = parse_str(source);
2023 assert!(errors.is_empty(), "errors: {errors:?}");
2024 let prog = prog.expect("should parse");
2025
2026 assert_eq!(prog.records.len(), 1);
2027 assert!(prog.records[0].is_pub);
2028 assert_eq!(prog.records[0].name.name, "Config");
2029 }
2030
2031 #[test]
2032 fn parse_enum_declaration() {
2033 let source = r#"
2034 enum Status {
2035 Active,
2036 Pending,
2037 Done,
2038 }
2039
2040 agent Main {
2041 on start {
2042 emit(0);
2043 }
2044 }
2045 run Main;
2046 "#;
2047
2048 let (prog, errors) = parse_str(source);
2049 assert!(errors.is_empty(), "errors: {errors:?}");
2050 let prog = prog.expect("should parse");
2051
2052 assert_eq!(prog.enums.len(), 1);
2053 assert!(!prog.enums[0].is_pub);
2054 assert_eq!(prog.enums[0].name.name, "Status");
2055 assert_eq!(prog.enums[0].variants.len(), 3);
2056 assert_eq!(prog.enums[0].variants[0].name, "Active");
2057 assert_eq!(prog.enums[0].variants[1].name, "Pending");
2058 assert_eq!(prog.enums[0].variants[2].name, "Done");
2059 }
2060
2061 #[test]
2062 fn parse_pub_enum() {
2063 let source = r#"
2064 pub enum Priority { High, Medium, Low }
2065
2066 agent Main {
2067 on start { emit(0); }
2068 }
2069 run Main;
2070 "#;
2071
2072 let (prog, errors) = parse_str(source);
2073 assert!(errors.is_empty(), "errors: {errors:?}");
2074 let prog = prog.expect("should parse");
2075
2076 assert_eq!(prog.enums.len(), 1);
2077 assert!(prog.enums[0].is_pub);
2078 assert_eq!(prog.enums[0].name.name, "Priority");
2079 }
2080
2081 #[test]
2082 fn parse_const_declaration() {
2083 let source = r#"
2084 const MAX_RETRIES: Int = 3;
2085
2086 agent Main {
2087 on start {
2088 emit(0);
2089 }
2090 }
2091 run Main;
2092 "#;
2093
2094 let (prog, errors) = parse_str(source);
2095 assert!(errors.is_empty(), "errors: {errors:?}");
2096 let prog = prog.expect("should parse");
2097
2098 assert_eq!(prog.consts.len(), 1);
2099 assert!(!prog.consts[0].is_pub);
2100 assert_eq!(prog.consts[0].name.name, "MAX_RETRIES");
2101 assert!(matches!(prog.consts[0].ty, sage_types::TypeExpr::Int));
2102 }
2103
2104 #[test]
2105 fn parse_pub_const() {
2106 let source = r#"
2107 pub const API_URL: String = "https://api.example.com";
2108
2109 agent Main {
2110 on start { emit(0); }
2111 }
2112 run Main;
2113 "#;
2114
2115 let (prog, errors) = parse_str(source);
2116 assert!(errors.is_empty(), "errors: {errors:?}");
2117 let prog = prog.expect("should parse");
2118
2119 assert_eq!(prog.consts.len(), 1);
2120 assert!(prog.consts[0].is_pub);
2121 assert_eq!(prog.consts[0].name.name, "API_URL");
2122 }
2123
2124 #[test]
2125 fn parse_multiple_type_declarations() {
2126 let source = r#"
2127 record Point { x: Int, y: Int }
2128 enum Color { Red, Green, Blue }
2129 const ORIGIN_X: Int = 0;
2130
2131 agent Main {
2132 on start { emit(0); }
2133 }
2134 run Main;
2135 "#;
2136
2137 let (prog, errors) = parse_str(source);
2138 assert!(errors.is_empty(), "errors: {errors:?}");
2139 let prog = prog.expect("should parse");
2140
2141 assert_eq!(prog.records.len(), 1);
2142 assert_eq!(prog.enums.len(), 1);
2143 assert_eq!(prog.consts.len(), 1);
2144 }
2145
2146 #[test]
2147 fn parse_match_expression() {
2148 let source = r#"
2149 enum Status { Active, Pending, Done }
2150
2151 agent Main {
2152 on start {
2153 let s: Int = match Active {
2154 Active => 1,
2155 Pending => 2,
2156 Done => 3,
2157 };
2158 emit(s);
2159 }
2160 }
2161 run Main;
2162 "#;
2163
2164 let (prog, errors) = parse_str(source);
2165 assert!(errors.is_empty(), "errors: {errors:?}");
2166 let prog = prog.expect("should parse");
2167
2168 assert_eq!(prog.agents.len(), 1);
2170 let handler = &prog.agents[0].handlers[0];
2172 let stmt = &handler.body.stmts[0];
2173 if let Stmt::Let { value, .. } = stmt {
2174 assert!(matches!(value, Expr::Match { .. }));
2175 } else {
2176 panic!("expected let statement with match");
2177 }
2178 }
2179
2180 #[test]
2181 fn parse_match_with_wildcard() {
2182 let source = r#"
2183 agent Main {
2184 on start {
2185 let x = 5;
2186 let result = match x {
2187 1 => 10,
2188 2 => 20,
2189 _ => 0,
2190 };
2191 emit(result);
2192 }
2193 }
2194 run Main;
2195 "#;
2196
2197 let (prog, errors) = parse_str(source);
2198 assert!(errors.is_empty(), "errors: {errors:?}");
2199 let prog = prog.expect("should parse");
2200
2201 assert_eq!(prog.agents.len(), 1);
2202 }
2203
2204 #[test]
2205 fn parse_record_construction() {
2206 let source = r#"
2207 record Point { x: Int, y: Int }
2208
2209 agent Main {
2210 on start {
2211 let p = Point { x: 10, y: 20 };
2212 emit(0);
2213 }
2214 }
2215 run Main;
2216 "#;
2217
2218 let (prog, errors) = parse_str(source);
2219 assert!(errors.is_empty(), "errors: {errors:?}");
2220 let prog = prog.expect("should parse");
2221
2222 assert_eq!(prog.records.len(), 1);
2223 assert_eq!(prog.agents.len(), 1);
2224
2225 let handler = &prog.agents[0].handlers[0];
2227 let stmt = &handler.body.stmts[0];
2228 if let Stmt::Let { value, .. } = stmt {
2229 if let Expr::RecordConstruct { name, fields, .. } = value {
2230 assert_eq!(name.name, "Point");
2231 assert_eq!(fields.len(), 2);
2232 assert_eq!(fields[0].name.name, "x");
2233 assert_eq!(fields[1].name.name, "y");
2234 } else {
2235 panic!("expected RecordConstruct");
2236 }
2237 } else {
2238 panic!("expected let statement");
2239 }
2240 }
2241
2242 #[test]
2243 fn parse_match_with_qualified_variant() {
2244 let source = r#"
2245 enum Status { Active, Pending }
2246
2247 fn get_status() -> Int {
2248 return 1;
2249 }
2250
2251 agent Main {
2252 on start {
2253 let s = get_status();
2254 let result = match s {
2255 Status::Active => 1,
2256 Status::Pending => 0,
2257 };
2258 emit(result);
2259 }
2260 }
2261 run Main;
2262 "#;
2263
2264 let (prog, errors) = parse_str(source);
2265 assert!(errors.is_empty(), "errors: {errors:?}");
2266 let prog = prog.expect("should parse");
2267
2268 assert_eq!(prog.enums.len(), 1);
2269 assert_eq!(prog.agents.len(), 1);
2270 }
2271
2272 #[test]
2273 fn parse_field_access() {
2274 let source = r#"
2275 record Point { x: Int, y: Int }
2276
2277 agent Main {
2278 on start {
2279 let p = Point { x: 10, y: 20 };
2280 let x_val = p.x;
2281 let y_val = p.y;
2282 emit(x_val);
2283 }
2284 }
2285 run Main;
2286 "#;
2287
2288 let (prog, errors) = parse_str(source);
2289 assert!(errors.is_empty(), "errors: {errors:?}");
2290 let prog = prog.expect("should parse");
2291
2292 assert_eq!(prog.records.len(), 1);
2293 assert_eq!(prog.agents.len(), 1);
2294
2295 let handler = &prog.agents[0].handlers[0];
2297 let stmt = &handler.body.stmts[1]; if let Stmt::Let { value, .. } = stmt {
2299 if let Expr::FieldAccess { field, .. } = value {
2300 assert_eq!(field.name, "x");
2301 } else {
2302 panic!("expected FieldAccess");
2303 }
2304 } else {
2305 panic!("expected let statement");
2306 }
2307 }
2308
2309 #[test]
2310 fn parse_chained_field_access() {
2311 let source = r#"
2312 record Inner { val: Int }
2313 record Outer { inner: Inner }
2314
2315 agent Main {
2316 on start {
2317 let inner = Inner { val: 42 };
2318 let outer = Outer { inner: inner };
2319 let v = outer.inner.val;
2320 emit(v);
2321 }
2322 }
2323 run Main;
2324 "#;
2325
2326 let (prog, errors) = parse_str(source);
2327 assert!(errors.is_empty(), "errors: {errors:?}");
2328 let prog = prog.expect("should parse");
2329
2330 assert_eq!(prog.records.len(), 2);
2331 assert_eq!(prog.agents.len(), 1);
2332
2333 let handler = &prog.agents[0].handlers[0];
2335 let stmt = &handler.body.stmts[2]; if let Stmt::Let { value, .. } = stmt {
2337 if let Expr::FieldAccess {
2338 object, field: val, ..
2339 } = value
2340 {
2341 assert_eq!(val.name, "val");
2342 if let Expr::FieldAccess { field: inner, .. } = object.as_ref() {
2344 assert_eq!(inner.name, "inner");
2345 } else {
2346 panic!("expected nested FieldAccess");
2347 }
2348 } else {
2349 panic!("expected FieldAccess");
2350 }
2351 } else {
2352 panic!("expected let statement");
2353 }
2354 }
2355
2356 #[test]
2361 fn parse_loop_break() {
2362 let source = r#"
2363 agent Main {
2364 on start {
2365 let count = 0;
2366 loop {
2367 count = count + 1;
2368 if count > 5 {
2369 break;
2370 }
2371 }
2372 emit(count);
2373 }
2374 }
2375 run Main;
2376 "#;
2377
2378 let (prog, errors) = parse_str(source);
2379 assert!(errors.is_empty(), "errors: {errors:?}");
2380 let prog = prog.expect("should parse");
2381
2382 assert_eq!(prog.agents.len(), 1);
2383 let handler = &prog.agents[0].handlers[0];
2384 let loop_stmt = &handler.body.stmts[1];
2386 assert!(matches!(loop_stmt, Stmt::Loop { .. }));
2387 if let Stmt::Loop { body, .. } = loop_stmt {
2389 let if_stmt = &body.stmts[1];
2390 if let Stmt::If { then_block, .. } = if_stmt {
2391 assert!(matches!(then_block.stmts[0], Stmt::Break { .. }));
2392 } else {
2393 panic!("expected if statement");
2394 }
2395 }
2396 }
2397
2398 #[test]
2399 fn parse_agent_receives() {
2400 let source = r#"
2401 enum WorkerMsg {
2402 Task,
2403 Shutdown,
2404 }
2405
2406 agent Worker receives WorkerMsg {
2407 id: Int
2408
2409 on start {
2410 emit(0);
2411 }
2412 }
2413
2414 agent Main {
2415 on start {
2416 emit(0);
2417 }
2418 }
2419 run Main;
2420 "#;
2421
2422 let (prog, errors) = parse_str(source);
2423 assert!(errors.is_empty(), "errors: {errors:?}");
2424 let prog = prog.expect("should parse");
2425
2426 assert_eq!(prog.agents.len(), 2);
2427
2428 let worker = &prog.agents[0];
2430 assert_eq!(worker.name.name, "Worker");
2431 assert!(worker.receives.is_some());
2432 if let Some(TypeExpr::Named(name)) = &worker.receives {
2433 assert_eq!(name.name, "WorkerMsg");
2434 } else {
2435 panic!("expected named type for receives");
2436 }
2437
2438 let main = &prog.agents[1];
2440 assert_eq!(main.name.name, "Main");
2441 assert!(main.receives.is_none());
2442 }
2443
2444 #[test]
2445 fn parse_receive_expression() {
2446 let source = r#"
2447 enum Msg { Ping }
2448
2449 agent Worker receives Msg {
2450 on start {
2451 let msg = receive();
2452 emit(0);
2453 }
2454 }
2455
2456 agent Main {
2457 on start { emit(0); }
2458 }
2459 run Main;
2460 "#;
2461
2462 let (prog, errors) = parse_str(source);
2463 assert!(errors.is_empty(), "errors: {errors:?}");
2464 let prog = prog.expect("should parse");
2465
2466 let worker = prog
2468 .agents
2469 .iter()
2470 .find(|a| a.name.name == "Worker")
2471 .unwrap();
2472 let handler = &worker.handlers[0];
2473 let stmt = &handler.body.stmts[0];
2474
2475 if let Stmt::Let { value, .. } = stmt {
2476 assert!(matches!(value, Expr::Receive { .. }));
2477 } else {
2478 panic!("expected let with receive");
2479 }
2480 }
2481
2482 #[test]
2483 fn parse_message_passing_full() {
2484 let source = r#"
2485 enum WorkerMsg {
2486 Task,
2487 Shutdown,
2488 }
2489
2490 agent Worker receives WorkerMsg {
2491 id: Int
2492
2493 on start {
2494 let msg = receive();
2495 let result = match msg {
2496 Task => 1,
2497 Shutdown => 0,
2498 };
2499 emit(result);
2500 }
2501 }
2502
2503 agent Main {
2504 on start {
2505 let w = spawn Worker { id: 1 };
2506 send(w, Task);
2507 send(w, Shutdown);
2508 await w;
2509 emit(0);
2510 }
2511 }
2512 run Main;
2513 "#;
2514
2515 let (prog, errors) = parse_str(source);
2516 assert!(errors.is_empty(), "errors: {errors:?}");
2517 let prog = prog.expect("should parse");
2518
2519 assert_eq!(prog.enums.len(), 1);
2520 assert_eq!(prog.agents.len(), 2);
2521
2522 let worker = prog
2524 .agents
2525 .iter()
2526 .find(|a| a.name.name == "Worker")
2527 .unwrap();
2528 assert!(worker.receives.is_some());
2529 }
2530
2531 #[test]
2536 fn parse_fallible_function() {
2537 let source = r#"
2538 fn get_data(url: String) -> String fails {
2539 return infer("Get data from {url}" -> String);
2540 }
2541
2542 agent Main {
2543 on start { emit(0); }
2544 }
2545 run Main;
2546 "#;
2547
2548 let (prog, errors) = parse_str(source);
2549 assert!(errors.is_empty(), "errors: {errors:?}");
2550 let prog = prog.expect("should parse");
2551
2552 assert_eq!(prog.functions.len(), 1);
2553 assert!(prog.functions[0].is_fallible);
2554 }
2555
2556 #[test]
2557 fn parse_try_expression() {
2558 let source = r#"
2559 fn fallible() -> Int fails { return 42; }
2560
2561 agent Main {
2562 on start {
2563 let x = try fallible();
2564 emit(x);
2565 }
2566 }
2567 run Main;
2568 "#;
2569
2570 let (prog, errors) = parse_str(source);
2571 assert!(errors.is_empty(), "errors: {errors:?}");
2572 let prog = prog.expect("should parse");
2573
2574 let handler = &prog.agents[0].handlers[0];
2576 if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
2577 assert!(matches!(value, Expr::Try { .. }));
2578 } else {
2579 panic!("expected Let statement");
2580 }
2581 }
2582
2583 #[test]
2584 fn parse_catch_expression() {
2585 let source = r#"
2586 fn fallible() -> Int fails { return 42; }
2587
2588 agent Main {
2589 on start {
2590 let x = fallible() catch { 0 };
2591 emit(x);
2592 }
2593 }
2594 run Main;
2595 "#;
2596
2597 let (prog, errors) = parse_str(source);
2598 assert!(errors.is_empty(), "errors: {errors:?}");
2599 let prog = prog.expect("should parse");
2600
2601 let handler = &prog.agents[0].handlers[0];
2603 if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
2604 if let Expr::Catch { error_bind, .. } = value {
2605 assert!(error_bind.is_none());
2606 } else {
2607 panic!("expected Catch expression");
2608 }
2609 } else {
2610 panic!("expected Let statement");
2611 }
2612 }
2613
2614 #[test]
2615 fn parse_catch_with_error_binding() {
2616 let source = r#"
2617 fn fallible() -> Int fails { return 42; }
2618
2619 agent Main {
2620 on start {
2621 let x = fallible() catch(e) { 0 };
2622 emit(x);
2623 }
2624 }
2625 run Main;
2626 "#;
2627
2628 let (prog, errors) = parse_str(source);
2629 assert!(errors.is_empty(), "errors: {errors:?}");
2630 let prog = prog.expect("should parse");
2631
2632 let handler = &prog.agents[0].handlers[0];
2634 if let Stmt::Let { value, .. } = &handler.body.stmts[0] {
2635 if let Expr::Catch { error_bind, .. } = value {
2636 assert!(error_bind.is_some());
2637 assert_eq!(error_bind.as_ref().unwrap().name, "e");
2638 } else {
2639 panic!("expected Catch expression");
2640 }
2641 } else {
2642 panic!("expected Let statement");
2643 }
2644 }
2645
2646 #[test]
2647 fn parse_on_error_handler() {
2648 let source = r#"
2649 agent Main {
2650 on start {
2651 emit(0);
2652 }
2653
2654 on error(e) {
2655 emit(1);
2656 }
2657 }
2658 run Main;
2659 "#;
2660
2661 let (prog, errors) = parse_str(source);
2662 assert!(errors.is_empty(), "errors: {errors:?}");
2663 let prog = prog.expect("should parse");
2664
2665 assert_eq!(prog.agents.len(), 1);
2666 assert_eq!(prog.agents[0].handlers.len(), 2);
2667
2668 let error_handler = prog.agents[0]
2670 .handlers
2671 .iter()
2672 .find(|h| matches!(h.event, EventKind::Error { .. }));
2673 assert!(error_handler.is_some());
2674
2675 if let EventKind::Error { param_name } = &error_handler.unwrap().event {
2676 assert_eq!(param_name.name, "e");
2677 } else {
2678 panic!("expected Error event kind");
2679 }
2680 }
2681}