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))
404 .then_ignore(just(Token::RParen))
405 .map(|(param_name, param_ty)| EventKind::Message {
406 param_name,
407 param_ty,
408 });
409
410 start.or(stop).or(message)
411}
412
413#[allow(clippy::needless_pass_by_value)]
419fn fn_parser(source: Arc<str>) -> impl Parser<Token, TopLevel, Error = ParseError> {
420 let src = source.clone();
421 let src2 = source.clone();
422 let src3 = source.clone();
423
424 let param = ident_token_parser(src.clone())
425 .then_ignore(just(Token::Colon))
426 .then(type_parser(src.clone()))
427 .map_with_span(move |(name, ty), span: Range<usize>| Param {
428 name,
429 ty,
430 span: make_span(&src, span),
431 });
432
433 let params = param
434 .separated_by(just(Token::Comma))
435 .allow_trailing()
436 .delimited_by(just(Token::LParen), just(Token::RParen));
437
438 just(Token::KwPub)
439 .or_not()
440 .then_ignore(just(Token::KwFn))
441 .then(ident_token_parser(src2.clone()))
442 .then(params)
443 .then_ignore(just(Token::Arrow))
444 .then(type_parser(src2.clone()))
445 .then(block_parser(src2))
446 .map_with_span(
447 move |((((is_pub, name), params), return_ty), body), span: Range<usize>| {
448 TopLevel::Function(FnDecl {
449 is_pub: is_pub.is_some(),
450 name,
451 params,
452 return_ty,
453 body,
454 span: make_span(&src3, span),
455 })
456 },
457 )
458}
459
460#[allow(clippy::needless_pass_by_value)]
467fn block_parser(source: Arc<str>) -> BoxedParser<'static, Token, Block, ParseError> {
468 let src = source.clone();
469
470 recursive(move |block: Recursive<Token, Block, ParseError>| {
471 let src_inner = src.clone();
472 stmt_parser(src.clone(), block)
473 .repeated()
474 .delimited_by(just(Token::LBrace), just(Token::RBrace))
475 .recover_with(nested_delimiters(
476 Token::LBrace,
477 Token::RBrace,
478 [
479 (Token::LParen, Token::RParen),
480 (Token::LBracket, Token::RBracket),
481 ],
482 |_span: Range<usize>| vec![],
483 ))
484 .map_with_span(move |stmts, span: Range<usize>| Block {
485 stmts,
486 span: make_span(&src_inner, span),
487 })
488 })
489 .boxed()
490}
491
492#[allow(clippy::needless_pass_by_value)]
494fn stmt_parser(
495 source: Arc<str>,
496 block: impl Parser<Token, Block, Error = ParseError> + Clone + 'static,
497) -> impl Parser<Token, Stmt, Error = ParseError> + Clone {
498 let src = source.clone();
499 let src2 = source.clone();
500 let src3 = source.clone();
501 let src4 = source.clone();
502 let src5 = source.clone();
503 let src6 = source.clone();
504 let src7 = source.clone();
505
506 let let_stmt = just(Token::KwLet)
507 .ignore_then(ident_token_parser(src.clone()))
508 .then(
509 just(Token::Colon)
510 .ignore_then(type_parser(src.clone()))
511 .or_not(),
512 )
513 .then_ignore(just(Token::Eq))
514 .then(expr_parser(src.clone()))
515 .then_ignore(just(Token::Semicolon))
516 .map_with_span(move |((name, ty), value), span: Range<usize>| Stmt::Let {
517 name,
518 ty,
519 value,
520 span: make_span(&src, span),
521 });
522
523 let return_stmt = just(Token::KwReturn)
524 .ignore_then(expr_parser(src2.clone()).or_not())
525 .then_ignore(just(Token::Semicolon))
526 .map_with_span(move |value, span: Range<usize>| Stmt::Return {
527 value,
528 span: make_span(&src2, span),
529 });
530
531 let if_stmt = recursive(|if_stmt| {
532 let src_if = src3.clone();
533 let block_clone = block.clone();
534
535 just(Token::KwIf)
536 .ignore_then(expr_parser(src3.clone()))
537 .then(block_clone.clone())
538 .then(
539 just(Token::KwElse)
540 .ignore_then(
541 if_stmt
542 .map(|s| ElseBranch::ElseIf(Box::new(s)))
543 .or(block_clone.map(ElseBranch::Block)),
544 )
545 .or_not(),
546 )
547 .map_with_span(
548 move |((condition, then_block), else_block), span: Range<usize>| Stmt::If {
549 condition,
550 then_block,
551 else_block,
552 span: make_span(&src_if, span),
553 },
554 )
555 });
556
557 let for_stmt = just(Token::KwFor)
558 .ignore_then(ident_token_parser(src4.clone()))
559 .then_ignore(just(Token::KwIn))
560 .then(expr_parser(src4.clone()))
561 .then(block.clone())
562 .map_with_span(move |((var, iter), body), span: Range<usize>| Stmt::For {
563 var,
564 iter,
565 body,
566 span: make_span(&src4, span),
567 });
568
569 let while_stmt = just(Token::KwWhile)
570 .ignore_then(expr_parser(src7.clone()))
571 .then(block.clone())
572 .map_with_span(move |(condition, body), span: Range<usize>| Stmt::While {
573 condition,
574 body,
575 span: make_span(&src7, span),
576 });
577
578 let src8 = source.clone();
579 let loop_stmt = just(Token::KwLoop)
580 .ignore_then(block.clone())
581 .map_with_span(move |body, span: Range<usize>| Stmt::Loop {
582 body,
583 span: make_span(&src8, span),
584 });
585
586 let src9 = source.clone();
587 let break_stmt = just(Token::KwBreak)
588 .then_ignore(just(Token::Semicolon))
589 .map_with_span(move |_, span: Range<usize>| Stmt::Break {
590 span: make_span(&src9, span),
591 });
592
593 let assign_stmt = ident_token_parser(src5.clone())
594 .then_ignore(just(Token::Eq))
595 .then(expr_parser(src5.clone()))
596 .then_ignore(just(Token::Semicolon))
597 .map_with_span(move |(name, value), span: Range<usize>| Stmt::Assign {
598 name,
599 value,
600 span: make_span(&src5, span),
601 });
602
603 let expr_stmt = expr_parser(src6.clone())
604 .then_ignore(just(Token::Semicolon))
605 .map_with_span(move |expr, span: Range<usize>| Stmt::Expr {
606 expr,
607 span: make_span(&src6, span),
608 });
609
610 let_stmt
611 .or(return_stmt)
612 .or(if_stmt)
613 .or(for_stmt)
614 .or(while_stmt)
615 .or(loop_stmt)
616 .or(break_stmt)
617 .or(assign_stmt)
618 .or(expr_stmt)
619}
620
621#[allow(clippy::needless_pass_by_value, clippy::too_many_lines)]
628fn expr_parser(source: Arc<str>) -> BoxedParser<'static, Token, Expr, ParseError> {
629 recursive(move |expr: Recursive<Token, Expr, ParseError>| {
630 let src = source.clone();
631
632 let literal = literal_parser(src.clone());
633 let var = var_parser(src.clone());
634
635 let paren = expr
636 .clone()
637 .delimited_by(just(Token::LParen), just(Token::RParen))
638 .map_with_span({
639 let src = src.clone();
640 move |inner, span: Range<usize>| Expr::Paren {
641 inner: Box::new(inner),
642 span: make_span(&src, span),
643 }
644 });
645
646 let list = expr
647 .clone()
648 .separated_by(just(Token::Comma))
649 .allow_trailing()
650 .delimited_by(just(Token::LBracket), just(Token::RBracket))
651 .map_with_span({
652 let src = src.clone();
653 move |elements, span: Range<usize>| Expr::List {
654 elements,
655 span: make_span(&src, span),
656 }
657 });
658
659 let self_access = just(Token::KwSelf)
661 .ignore_then(just(Token::Dot))
662 .ignore_then(ident_token_parser(src.clone()))
663 .then(
664 expr.clone()
665 .separated_by(just(Token::Comma))
666 .allow_trailing()
667 .delimited_by(just(Token::LParen), just(Token::RParen))
668 .or_not(),
669 )
670 .map_with_span({
671 let src = src.clone();
672 move |(field, args), span: Range<usize>| match args {
673 Some(args) => Expr::SelfMethodCall {
674 method: field,
675 args,
676 span: make_span(&src, span),
677 },
678 None => Expr::SelfField {
679 field,
680 span: make_span(&src, span),
681 },
682 }
683 });
684
685 let infer_expr = just(Token::KwInfer)
687 .ignore_then(just(Token::LParen))
688 .ignore_then(string_template_parser(src.clone()))
689 .then(
690 just(Token::Arrow)
691 .ignore_then(type_parser(src.clone()))
692 .or_not(),
693 )
694 .then_ignore(just(Token::RParen))
695 .map_with_span({
696 let src = src.clone();
697 move |(template, result_ty), span: Range<usize>| Expr::Infer {
698 template,
699 result_ty,
700 span: make_span(&src, span),
701 }
702 });
703
704 let spawn_field_init = ident_token_parser(src.clone())
706 .then_ignore(just(Token::Colon))
707 .then(expr.clone())
708 .map_with_span({
709 let src = src.clone();
710 move |(name, value), span: Range<usize>| FieldInit {
711 name,
712 value,
713 span: make_span(&src, span),
714 }
715 });
716
717 let spawn_expr = just(Token::KwSpawn)
718 .ignore_then(ident_token_parser(src.clone()))
719 .then_ignore(just(Token::LBrace))
720 .then(
721 spawn_field_init
722 .separated_by(just(Token::Comma))
723 .allow_trailing(),
724 )
725 .then_ignore(just(Token::RBrace))
726 .map_with_span({
727 let src = src.clone();
728 move |(agent, fields), span: Range<usize>| Expr::Spawn {
729 agent,
730 fields,
731 span: make_span(&src, span),
732 }
733 });
734
735 let await_expr = just(Token::KwAwait)
737 .ignore_then(ident_token_parser(src.clone()).map_with_span({
738 let src = src.clone();
739 move |name, span: Range<usize>| Expr::Var {
740 name,
741 span: make_span(&src, span),
742 }
743 }))
744 .map_with_span({
745 let src = src.clone();
746 move |handle, span: Range<usize>| Expr::Await {
747 handle: Box::new(handle),
748 span: make_span(&src, span),
749 }
750 });
751
752 let send_expr = just(Token::KwSend)
754 .ignore_then(just(Token::LParen))
755 .ignore_then(expr.clone())
756 .then_ignore(just(Token::Comma))
757 .then(expr.clone())
758 .then_ignore(just(Token::RParen))
759 .map_with_span({
760 let src = src.clone();
761 move |(handle, message), span: Range<usize>| Expr::Send {
762 handle: Box::new(handle),
763 message: Box::new(message),
764 span: make_span(&src, span),
765 }
766 });
767
768 let emit_expr = just(Token::KwEmit)
770 .ignore_then(just(Token::LParen))
771 .ignore_then(expr.clone())
772 .then_ignore(just(Token::RParen))
773 .map_with_span({
774 let src = src.clone();
775 move |value, span: Range<usize>| Expr::Emit {
776 value: Box::new(value),
777 span: make_span(&src, span),
778 }
779 });
780
781 let call_expr = ident_token_parser(src.clone())
783 .then(
784 expr.clone()
785 .separated_by(just(Token::Comma))
786 .allow_trailing()
787 .delimited_by(just(Token::LParen), just(Token::RParen)),
788 )
789 .map_with_span({
790 let src = src.clone();
791 move |(name, args), span: Range<usize>| Expr::Call {
792 name,
793 args,
794 span: make_span(&src, span),
795 }
796 });
797
798 let pattern = pattern_parser(src.clone());
800
801 let match_arm = pattern
803 .then_ignore(just(Token::FatArrow))
804 .then(expr.clone())
805 .map_with_span({
806 let src = src.clone();
807 move |(pattern, body), span: Range<usize>| MatchArm {
808 pattern,
809 body,
810 span: make_span(&src, span),
811 }
812 });
813
814 let match_expr = just(Token::KwMatch)
815 .ignore_then(expr.clone())
816 .then(
817 match_arm
818 .separated_by(just(Token::Comma))
819 .allow_trailing()
820 .delimited_by(just(Token::LBrace), just(Token::RBrace)),
821 )
822 .map_with_span({
823 let src = src.clone();
824 move |(scrutinee, arms), span: Range<usize>| Expr::Match {
825 scrutinee: Box::new(scrutinee),
826 arms,
827 span: make_span(&src, span),
828 }
829 });
830
831 let receive_expr = just(Token::KwReceive)
833 .ignore_then(just(Token::LParen))
834 .ignore_then(just(Token::RParen))
835 .map_with_span({
836 let src = src.clone();
837 move |_, span: Range<usize>| Expr::Receive {
838 span: make_span(&src, span),
839 }
840 });
841
842 let record_field_init = ident_token_parser(src.clone())
846 .then_ignore(just(Token::Colon))
847 .then(expr.clone())
848 .map_with_span({
849 let src = src.clone();
850 move |(name, value), span: Range<usize>| FieldInit {
851 name,
852 value,
853 span: make_span(&src, span),
854 }
855 });
856
857 let record_construct = ident_token_parser(src.clone())
858 .then_ignore(just(Token::LBrace))
859 .then(
860 record_field_init
861 .separated_by(just(Token::Comma))
862 .allow_trailing(),
863 )
864 .then_ignore(just(Token::RBrace))
865 .map_with_span({
866 let src = src.clone();
867 move |(name, fields), span: Range<usize>| Expr::RecordConstruct {
868 name,
869 fields,
870 span: make_span(&src, span),
871 }
872 });
873
874 let atom = infer_expr
879 .or(spawn_expr)
880 .or(await_expr)
881 .or(send_expr)
882 .or(emit_expr)
883 .or(receive_expr)
884 .or(match_expr)
885 .or(self_access)
886 .or(record_construct)
887 .or(call_expr)
888 .or(list)
889 .or(paren)
890 .or(literal)
891 .or(var)
892 .boxed();
893
894 let postfix = atom
896 .then(
897 just(Token::Dot)
898 .ignore_then(ident_token_parser(src.clone()))
899 .repeated(),
900 )
901 .foldl({
902 let src = src.clone();
903 move |object, field| {
904 let span = make_span(&src, object.span().start..field.span.end);
905 Expr::FieldAccess {
906 object: Box::new(object),
907 field,
908 span,
909 }
910 }
911 })
912 .boxed();
913
914 let unary = just(Token::Minus)
916 .to(UnaryOp::Neg)
917 .or(just(Token::Bang).to(UnaryOp::Not))
918 .repeated()
919 .then(postfix)
920 .foldr(|op, operand| {
921 let span = operand.span().clone();
922 Expr::Unary {
923 op,
924 operand: Box::new(operand),
925 span,
926 }
927 })
928 .boxed();
929
930 let mul_div_op = just(Token::Star)
933 .to(BinOp::Mul)
934 .or(just(Token::Slash).to(BinOp::Div));
935
936 let mul_div = unary
937 .clone()
938 .then(mul_div_op.then(unary.clone()).repeated())
939 .foldl({
940 let src = src.clone();
941 move |left, (op, right)| {
942 let span = make_span(&src, left.span().start..right.span().end);
943 Expr::Binary {
944 op,
945 left: Box::new(left),
946 right: Box::new(right),
947 span,
948 }
949 }
950 })
951 .boxed();
952
953 let add_sub_op = just(Token::Plus)
955 .to(BinOp::Add)
956 .or(just(Token::Minus).to(BinOp::Sub));
957
958 let add_sub = mul_div
959 .clone()
960 .then(add_sub_op.then(mul_div).repeated())
961 .foldl({
962 let src = src.clone();
963 move |left, (op, right)| {
964 let span = make_span(&src, left.span().start..right.span().end);
965 Expr::Binary {
966 op,
967 left: Box::new(left),
968 right: Box::new(right),
969 span,
970 }
971 }
972 })
973 .boxed();
974
975 let concat_op = just(Token::PlusPlus).to(BinOp::Concat);
977
978 let concat = add_sub
979 .clone()
980 .then(concat_op.then(add_sub).repeated())
981 .foldl({
982 let src = src.clone();
983 move |left, (op, right)| {
984 let span = make_span(&src, left.span().start..right.span().end);
985 Expr::Binary {
986 op,
987 left: Box::new(left),
988 right: Box::new(right),
989 span,
990 }
991 }
992 })
993 .boxed();
994
995 let cmp_op = choice((
997 just(Token::Le).to(BinOp::Le),
998 just(Token::Ge).to(BinOp::Ge),
999 just(Token::Lt).to(BinOp::Lt),
1000 just(Token::Gt).to(BinOp::Gt),
1001 ));
1002
1003 let comparison = concat
1004 .clone()
1005 .then(cmp_op.then(concat).repeated())
1006 .foldl({
1007 let src = src.clone();
1008 move |left, (op, right)| {
1009 let span = make_span(&src, left.span().start..right.span().end);
1010 Expr::Binary {
1011 op,
1012 left: Box::new(left),
1013 right: Box::new(right),
1014 span,
1015 }
1016 }
1017 })
1018 .boxed();
1019
1020 let eq_op = just(Token::EqEq)
1022 .to(BinOp::Eq)
1023 .or(just(Token::Ne).to(BinOp::Ne));
1024
1025 let equality = comparison
1026 .clone()
1027 .then(eq_op.then(comparison).repeated())
1028 .foldl({
1029 let src = src.clone();
1030 move |left, (op, right)| {
1031 let span = make_span(&src, left.span().start..right.span().end);
1032 Expr::Binary {
1033 op,
1034 left: Box::new(left),
1035 right: Box::new(right),
1036 span,
1037 }
1038 }
1039 })
1040 .boxed();
1041
1042 let and_op = just(Token::And).to(BinOp::And);
1044
1045 let and = equality
1046 .clone()
1047 .then(and_op.then(equality).repeated())
1048 .foldl({
1049 let src = src.clone();
1050 move |left, (op, right)| {
1051 let span = make_span(&src, left.span().start..right.span().end);
1052 Expr::Binary {
1053 op,
1054 left: Box::new(left),
1055 right: Box::new(right),
1056 span,
1057 }
1058 }
1059 })
1060 .boxed();
1061
1062 let or_op = just(Token::Or).to(BinOp::Or);
1064
1065 and.clone().then(or_op.then(and).repeated()).foldl({
1066 let src = src.clone();
1067 move |left, (op, right)| {
1068 let span = make_span(&src, left.span().start..right.span().end);
1069 Expr::Binary {
1070 op,
1071 left: Box::new(left),
1072 right: Box::new(right),
1073 span,
1074 }
1075 }
1076 })
1077 })
1078 .boxed()
1079}
1080
1081fn make_span(source: &Arc<str>, range: Range<usize>) -> Span {
1087 Span::new(range.start, range.end, Arc::clone(source))
1088}
1089
1090fn ident_token_parser(source: Arc<str>) -> impl Parser<Token, Ident, Error = ParseError> + Clone {
1092 filter_map(move |span: Range<usize>, token| match token {
1093 Token::Ident => {
1094 let text = &source[span.start..span.end];
1095 Ok(Ident::new(text.to_string(), make_span(&source, span)))
1096 }
1097 _ => Err(Simple::expected_input_found(
1098 span,
1099 vec![Some(Token::Ident)],
1100 Some(token),
1101 )),
1102 })
1103}
1104
1105fn var_parser(source: Arc<str>) -> impl Parser<Token, Expr, Error = ParseError> + Clone {
1107 ident_token_parser(source.clone()).map_with_span(move |name, span: Range<usize>| Expr::Var {
1108 name,
1109 span: make_span(&source, span),
1110 })
1111}
1112
1113fn type_parser(source: Arc<str>) -> impl Parser<Token, TypeExpr, Error = ParseError> + Clone {
1115 recursive(move |ty| {
1116 let src = source.clone();
1117
1118 let primitive = choice((
1119 just(Token::TyInt).to(TypeExpr::Int),
1120 just(Token::TyFloat).to(TypeExpr::Float),
1121 just(Token::TyBool).to(TypeExpr::Bool),
1122 just(Token::TyString).to(TypeExpr::String),
1123 just(Token::TyUnit).to(TypeExpr::Unit),
1124 ));
1125
1126 let list_ty = just(Token::TyList)
1127 .ignore_then(just(Token::Lt))
1128 .ignore_then(ty.clone())
1129 .then_ignore(just(Token::Gt))
1130 .map(|inner| TypeExpr::List(Box::new(inner)));
1131
1132 let option_ty = just(Token::TyOption)
1133 .ignore_then(just(Token::Lt))
1134 .ignore_then(ty.clone())
1135 .then_ignore(just(Token::Gt))
1136 .map(|inner| TypeExpr::Option(Box::new(inner)));
1137
1138 let inferred_ty = just(Token::TyInferred)
1139 .ignore_then(just(Token::Lt))
1140 .ignore_then(ty.clone())
1141 .then_ignore(just(Token::Gt))
1142 .map(|inner| TypeExpr::Inferred(Box::new(inner)));
1143
1144 let agent_ty = just(Token::TyAgent)
1145 .ignore_then(just(Token::Lt))
1146 .ignore_then(ident_token_parser(src.clone()))
1147 .then_ignore(just(Token::Gt))
1148 .map(TypeExpr::Agent);
1149
1150 let named_ty = ident_token_parser(src).map(TypeExpr::Named);
1151
1152 primitive
1153 .or(list_ty)
1154 .or(option_ty)
1155 .or(inferred_ty)
1156 .or(agent_ty)
1157 .or(named_ty)
1158 })
1159}
1160
1161fn pattern_parser(source: Arc<str>) -> impl Parser<Token, Pattern, Error = ParseError> + Clone {
1163 let src = source.clone();
1164 let src2 = source.clone();
1165 let src3 = source.clone();
1166 let src4 = source.clone();
1167
1168 let wildcard = filter_map(move |span: Range<usize>, token| match &token {
1170 Token::Ident if src[span.start..span.end].eq("_") => Ok(()),
1171 _ => Err(Simple::expected_input_found(span, vec![], Some(token))),
1172 })
1173 .map_with_span(move |_, span: Range<usize>| Pattern::Wildcard {
1174 span: make_span(&src2, span),
1175 });
1176
1177 let lit_int = filter_map({
1179 let src = src3.clone();
1180 move |span: Range<usize>, token| match token {
1181 Token::IntLit => {
1182 let text = &src[span.start..span.end];
1183 text.parse::<i64>()
1184 .map(Literal::Int)
1185 .map_err(|_| Simple::custom(span, "invalid integer literal"))
1186 }
1187 _ => Err(Simple::expected_input_found(
1188 span,
1189 vec![Some(Token::IntLit)],
1190 Some(token),
1191 )),
1192 }
1193 })
1194 .map_with_span({
1195 let src = src3.clone();
1196 move |value, span: Range<usize>| Pattern::Literal {
1197 value,
1198 span: make_span(&src, span),
1199 }
1200 });
1201
1202 let lit_bool = just(Token::KwTrue)
1203 .to(Literal::Bool(true))
1204 .or(just(Token::KwFalse).to(Literal::Bool(false)))
1205 .map_with_span({
1206 let src = src3.clone();
1207 move |value, span: Range<usize>| Pattern::Literal {
1208 value,
1209 span: make_span(&src, span),
1210 }
1211 });
1212
1213 let qualified_variant = ident_token_parser(src4.clone())
1216 .then_ignore(just(Token::ColonColon))
1217 .then(ident_token_parser(src4.clone()))
1218 .map_with_span({
1219 let src = src4.clone();
1220 move |(enum_name, variant), span: Range<usize>| Pattern::Variant {
1221 enum_name: Some(enum_name),
1222 variant,
1223 span: make_span(&src, span),
1224 }
1225 });
1226
1227 let unqualified = ident_token_parser(src4.clone()).map_with_span({
1231 let src = src4.clone();
1232 move |name, span: Range<usize>| {
1233 if name.name.chars().next().is_some_and(|c| c.is_uppercase()) {
1236 Pattern::Variant {
1237 enum_name: None,
1238 variant: name,
1239 span: make_span(&src, span),
1240 }
1241 } else {
1242 Pattern::Binding {
1243 name,
1244 span: make_span(&src, span),
1245 }
1246 }
1247 }
1248 });
1249
1250 wildcard
1252 .or(qualified_variant)
1253 .or(lit_int)
1254 .or(lit_bool)
1255 .or(unqualified)
1256}
1257
1258fn literal_parser(source: Arc<str>) -> impl Parser<Token, Expr, Error = ParseError> + Clone {
1260 let src = source.clone();
1261 let src2 = source.clone();
1262 let src3 = source.clone();
1263 let src4 = source.clone();
1264 let src5 = source.clone();
1265
1266 let int_lit = filter_map(move |span: Range<usize>, token| match token {
1267 Token::IntLit => {
1268 let text = &src[span.start..span.end];
1269 text.parse::<i64>()
1270 .map(Literal::Int)
1271 .map_err(|_| Simple::custom(span, "invalid integer literal"))
1272 }
1273 _ => Err(Simple::expected_input_found(
1274 span,
1275 vec![Some(Token::IntLit)],
1276 Some(token),
1277 )),
1278 })
1279 .map_with_span(move |value, span: Range<usize>| Expr::Literal {
1280 value,
1281 span: make_span(&src2, span),
1282 });
1283
1284 let float_lit = filter_map(move |span: Range<usize>, token| match token {
1285 Token::FloatLit => {
1286 let text = &src3[span.start..span.end];
1287 text.parse::<f64>()
1288 .map(Literal::Float)
1289 .map_err(|_| Simple::custom(span, "invalid float literal"))
1290 }
1291 _ => Err(Simple::expected_input_found(
1292 span,
1293 vec![Some(Token::FloatLit)],
1294 Some(token),
1295 )),
1296 })
1297 .map_with_span(move |value, span: Range<usize>| Expr::Literal {
1298 value,
1299 span: make_span(&src4, span),
1300 });
1301
1302 let src6 = source.clone();
1303 let string_lit = filter_map(move |span: Range<usize>, token| match token {
1304 Token::StringLit => {
1305 let text = &src5[span.start..span.end];
1306 let inner = &text[1..text.len() - 1];
1307 let parts = parse_string_template(inner, &make_span(&src5, span.clone()));
1308 Ok(parts)
1309 }
1310 _ => Err(Simple::expected_input_found(
1311 span,
1312 vec![Some(Token::StringLit)],
1313 Some(token),
1314 )),
1315 })
1316 .map_with_span(move |parts, span: Range<usize>| {
1317 let span = make_span(&src6, span);
1318 if parts.len() == 1 {
1320 if let StringPart::Literal(s) = &parts[0] {
1321 return Expr::Literal {
1322 value: Literal::String(s.clone()),
1323 span,
1324 };
1325 }
1326 }
1327 Expr::StringInterp {
1329 template: StringTemplate {
1330 parts,
1331 span: span.clone(),
1332 },
1333 span,
1334 }
1335 });
1336
1337 let bool_lit = just(Token::KwTrue)
1338 .to(Literal::Bool(true))
1339 .or(just(Token::KwFalse).to(Literal::Bool(false)))
1340 .map_with_span(move |value, _span: Range<usize>| Expr::Literal {
1341 value,
1342 span: Span::dummy(), });
1344
1345 int_lit.or(float_lit).or(string_lit).or(bool_lit)
1346}
1347
1348fn string_template_parser(
1350 source: Arc<str>,
1351) -> impl Parser<Token, StringTemplate, Error = ParseError> + Clone {
1352 filter_map(move |span: Range<usize>, token| match token {
1353 Token::StringLit => {
1354 let text = &source[span.start..span.end];
1355 let inner = &text[1..text.len() - 1];
1356 let parts = parse_string_template(inner, &make_span(&source, span.clone()));
1357 Ok(StringTemplate {
1358 parts,
1359 span: make_span(&source, span),
1360 })
1361 }
1362 _ => Err(Simple::expected_input_found(
1363 span,
1364 vec![Some(Token::StringLit)],
1365 Some(token),
1366 )),
1367 })
1368}
1369
1370fn parse_string_template(s: &str, span: &Span) -> Vec<StringPart> {
1372 let mut parts = Vec::new();
1373 let mut current = String::new();
1374 let mut chars = s.chars().peekable();
1375
1376 while let Some(ch) = chars.next() {
1377 if ch == '{' {
1378 if !current.is_empty() {
1379 parts.push(StringPart::Literal(std::mem::take(&mut current)));
1380 }
1381
1382 let mut ident_name = String::new();
1383 while let Some(&c) = chars.peek() {
1384 if c == '}' {
1385 chars.next();
1386 break;
1387 }
1388 ident_name.push(c);
1389 chars.next();
1390 }
1391
1392 if !ident_name.is_empty() {
1393 parts.push(StringPart::Interpolation(Ident::new(
1394 ident_name,
1395 span.clone(),
1396 )));
1397 }
1398 } else if ch == '\\' {
1399 if let Some(escaped) = chars.next() {
1400 current.push(match escaped {
1401 'n' => '\n',
1402 't' => '\t',
1403 'r' => '\r',
1404 '\\' => '\\',
1405 '"' => '"',
1406 '{' => '{',
1407 '}' => '}',
1408 other => other,
1409 });
1410 }
1411 } else {
1412 current.push(ch);
1413 }
1414 }
1415
1416 if !current.is_empty() {
1417 parts.push(StringPart::Literal(current));
1418 }
1419
1420 if parts.is_empty() {
1421 parts.push(StringPart::Literal(String::new()));
1422 }
1423
1424 parts
1425}
1426
1427#[cfg(test)]
1432mod tests {
1433 use super::*;
1434 use sage_lexer::lex;
1435
1436 fn parse_str(source: &str) -> (Option<Program>, Vec<ParseError>) {
1437 let lex_result = lex(source).expect("lexing should succeed");
1438 let source_arc: Arc<str> = Arc::from(source);
1439 parse(lex_result.tokens(), source_arc)
1440 }
1441
1442 #[test]
1443 fn parse_minimal_program() {
1444 let source = r#"
1445 agent Main {
1446 on start {
1447 emit(42);
1448 }
1449 }
1450 run Main;
1451 "#;
1452
1453 let (prog, errors) = parse_str(source);
1454 assert!(errors.is_empty(), "errors: {errors:?}");
1455 let prog = prog.expect("should parse");
1456
1457 assert_eq!(prog.agents.len(), 1);
1458 assert_eq!(prog.agents[0].name.name, "Main");
1459 assert_eq!(prog.run_agent.as_ref().unwrap().name, "Main");
1460 }
1461
1462 #[test]
1463 fn parse_agent_with_beliefs() {
1464 let source = r#"
1465 agent Researcher {
1466 topic: String
1467 max_words: Int
1468
1469 on start {
1470 emit(self.topic);
1471 }
1472 }
1473 run Researcher;
1474 "#;
1475
1476 let (prog, errors) = parse_str(source);
1477 assert!(errors.is_empty(), "errors: {errors:?}");
1478 let prog = prog.expect("should parse");
1479
1480 assert_eq!(prog.agents[0].beliefs.len(), 2);
1481 assert_eq!(prog.agents[0].beliefs[0].name.name, "topic");
1482 assert_eq!(prog.agents[0].beliefs[1].name.name, "max_words");
1483 }
1484
1485 #[test]
1486 fn parse_multiple_handlers() {
1487 let source = r#"
1488 agent Worker {
1489 on start {
1490 print("started");
1491 }
1492
1493 on message(msg: String) {
1494 print(msg);
1495 }
1496
1497 on stop {
1498 print("stopped");
1499 }
1500 }
1501 run Worker;
1502 "#;
1503
1504 let (prog, errors) = parse_str(source);
1505 assert!(errors.is_empty(), "errors: {errors:?}");
1506 let prog = prog.expect("should parse");
1507
1508 assert_eq!(prog.agents[0].handlers.len(), 3);
1509 assert_eq!(prog.agents[0].handlers[0].event, EventKind::Start);
1510 assert!(matches!(
1511 prog.agents[0].handlers[1].event,
1512 EventKind::Message { .. }
1513 ));
1514 assert_eq!(prog.agents[0].handlers[2].event, EventKind::Stop);
1515 }
1516
1517 #[test]
1518 fn parse_function() {
1519 let source = r#"
1520 fn greet(name: String) -> String {
1521 return "Hello, " ++ name;
1522 }
1523
1524 agent Main {
1525 on start {
1526 emit(greet("World"));
1527 }
1528 }
1529 run Main;
1530 "#;
1531
1532 let (prog, errors) = parse_str(source);
1533 assert!(errors.is_empty(), "errors: {errors:?}");
1534 let prog = prog.expect("should parse");
1535
1536 assert_eq!(prog.functions.len(), 1);
1537 assert_eq!(prog.functions[0].name.name, "greet");
1538 assert_eq!(prog.functions[0].params.len(), 1);
1539 }
1540
1541 #[test]
1542 fn parse_let_statement() {
1543 let source = r#"
1544 agent Main {
1545 on start {
1546 let x: Int = 42;
1547 let y = "hello";
1548 emit(x);
1549 }
1550 }
1551 run Main;
1552 "#;
1553
1554 let (prog, errors) = parse_str(source);
1555 assert!(errors.is_empty(), "errors: {errors:?}");
1556 let prog = prog.expect("should parse");
1557
1558 let stmts = &prog.agents[0].handlers[0].body.stmts;
1559 assert!(matches!(stmts[0], Stmt::Let { .. }));
1560 assert!(matches!(stmts[1], Stmt::Let { .. }));
1561 }
1562
1563 #[test]
1564 fn parse_if_statement() {
1565 let source = r#"
1566 agent Main {
1567 on start {
1568 if true {
1569 emit(1);
1570 } else {
1571 emit(2);
1572 }
1573 }
1574 }
1575 run Main;
1576 "#;
1577
1578 let (prog, errors) = parse_str(source);
1579 assert!(errors.is_empty(), "errors: {errors:?}");
1580 let prog = prog.expect("should parse");
1581
1582 let stmts = &prog.agents[0].handlers[0].body.stmts;
1583 assert!(matches!(stmts[0], Stmt::If { .. }));
1584 }
1585
1586 #[test]
1587 fn parse_for_loop() {
1588 let source = r#"
1589 agent Main {
1590 on start {
1591 for x in [1, 2, 3] {
1592 print(x);
1593 }
1594 emit(0);
1595 }
1596 }
1597 run Main;
1598 "#;
1599
1600 let (prog, errors) = parse_str(source);
1601 assert!(errors.is_empty(), "errors: {errors:?}");
1602 let prog = prog.expect("should parse");
1603
1604 let stmts = &prog.agents[0].handlers[0].body.stmts;
1605 assert!(matches!(stmts[0], Stmt::For { .. }));
1606 }
1607
1608 #[test]
1609 fn parse_spawn_await() {
1610 let source = r#"
1611 agent Worker {
1612 name: String
1613
1614 on start {
1615 emit(self.name);
1616 }
1617 }
1618
1619 agent Main {
1620 on start {
1621 let w = spawn Worker { name: "test" };
1622 let result = await w;
1623 emit(result);
1624 }
1625 }
1626 run Main;
1627 "#;
1628
1629 let (prog, errors) = parse_str(source);
1630 assert!(errors.is_empty(), "errors: {errors:?}");
1631 prog.expect("should parse");
1632 }
1633
1634 #[test]
1635 fn parse_infer() {
1636 let source = r#"
1637 agent Main {
1638 on start {
1639 let result = infer("What is 2+2?");
1640 emit(result);
1641 }
1642 }
1643 run Main;
1644 "#;
1645
1646 let (prog, errors) = parse_str(source);
1647 assert!(errors.is_empty(), "errors: {errors:?}");
1648 prog.expect("should parse");
1649 }
1650
1651 #[test]
1652 fn parse_binary_precedence() {
1653 let source = r#"
1654 agent Main {
1655 on start {
1656 let x = 2 + 3 * 4;
1657 emit(x);
1658 }
1659 }
1660 run Main;
1661 "#;
1662
1663 let (prog, errors) = parse_str(source);
1664 assert!(errors.is_empty(), "errors: {errors:?}");
1665 let prog = prog.expect("should parse");
1666
1667 let stmts = &prog.agents[0].handlers[0].body.stmts;
1668 if let Stmt::Let { value, .. } = &stmts[0] {
1669 if let Expr::Binary { op, .. } = value {
1670 assert_eq!(*op, BinOp::Add);
1671 } else {
1672 panic!("expected binary expression");
1673 }
1674 }
1675 }
1676
1677 #[test]
1678 fn parse_string_interpolation() {
1679 let source = r#"
1680 agent Main {
1681 on start {
1682 let name = "World";
1683 let msg = infer("Greet {name}");
1684 emit(msg);
1685 }
1686 }
1687 run Main;
1688 "#;
1689
1690 let (prog, errors) = parse_str(source);
1691 assert!(errors.is_empty(), "errors: {errors:?}");
1692 let prog = prog.expect("should parse");
1693
1694 let stmts = &prog.agents[0].handlers[0].body.stmts;
1695 if let Stmt::Let { value, .. } = &stmts[1] {
1696 if let Expr::Infer { template, .. } = value {
1697 assert!(template.has_interpolations());
1698 } else {
1699 panic!("expected infer expression");
1700 }
1701 }
1702 }
1703
1704 #[test]
1709 fn recover_from_malformed_agent_continues_to_next() {
1710 let source = r#"
1712 agent Broken {
1713 x:
1714 }
1715
1716 agent Main {
1717 on start {
1718 emit(42);
1719 }
1720 }
1721 run Main;
1722 "#;
1723
1724 let (prog, errors) = parse_str(source);
1725 assert!(!errors.is_empty(), "should have parse errors");
1727 let prog = prog.expect("should produce partial AST");
1729 assert!(prog.agents.iter().any(|a| a.name.name == "Main"));
1730 }
1731
1732 #[test]
1733 fn recover_from_mismatched_braces_in_block() {
1734 let source = r#"
1735 agent Main {
1736 on start {
1737 let x = [1, 2, 3;
1738 emit(42);
1739 }
1740 }
1741 run Main;
1742 "#;
1743
1744 let (prog, errors) = parse_str(source);
1745 assert!(!errors.is_empty(), "should have parse errors");
1747 assert!(prog.is_some(), "should produce partial AST despite errors");
1748 }
1749
1750 #[test]
1751 fn parse_mod_declaration() {
1752 let source = r#"
1753 mod agents;
1754 pub mod utils;
1755
1756 agent Main {
1757 on start {
1758 emit(42);
1759 }
1760 }
1761 run Main;
1762 "#;
1763
1764 let (prog, errors) = parse_str(source);
1765 assert!(errors.is_empty(), "errors: {errors:?}");
1766 let prog = prog.expect("should parse");
1767
1768 assert_eq!(prog.mod_decls.len(), 2);
1769 assert!(!prog.mod_decls[0].is_pub);
1770 assert_eq!(prog.mod_decls[0].name.name, "agents");
1771 assert!(prog.mod_decls[1].is_pub);
1772 assert_eq!(prog.mod_decls[1].name.name, "utils");
1773 }
1774
1775 #[test]
1776 fn parse_use_simple() {
1777 let source = r#"
1778 use agents::Researcher;
1779
1780 agent Main {
1781 on start {
1782 emit(42);
1783 }
1784 }
1785 run Main;
1786 "#;
1787
1788 let (prog, errors) = parse_str(source);
1789 assert!(errors.is_empty(), "errors: {errors:?}");
1790 let prog = prog.expect("should parse");
1791
1792 assert_eq!(prog.use_decls.len(), 1);
1793 assert!(!prog.use_decls[0].is_pub);
1794 assert_eq!(prog.use_decls[0].path.len(), 2);
1795 assert_eq!(prog.use_decls[0].path[0].name, "agents");
1796 assert_eq!(prog.use_decls[0].path[1].name, "Researcher");
1797 assert!(matches!(prog.use_decls[0].kind, UseKind::Simple(None)));
1798 }
1799
1800 #[test]
1801 fn parse_use_with_alias() {
1802 let source = r#"
1803 use agents::Researcher as R;
1804
1805 agent Main {
1806 on start {
1807 emit(42);
1808 }
1809 }
1810 run Main;
1811 "#;
1812
1813 let (prog, errors) = parse_str(source);
1814 assert!(errors.is_empty(), "errors: {errors:?}");
1815 let prog = prog.expect("should parse");
1816
1817 assert_eq!(prog.use_decls.len(), 1);
1818 if let UseKind::Simple(Some(alias)) = &prog.use_decls[0].kind {
1819 assert_eq!(alias.name, "R");
1820 } else {
1821 panic!("expected Simple with alias");
1822 }
1823 }
1824
1825 #[test]
1826 fn parse_pub_agent() {
1827 let source = r#"
1828 pub agent Worker {
1829 on start {
1830 emit(42);
1831 }
1832 }
1833
1834 agent Main {
1835 on start {
1836 emit(0);
1837 }
1838 }
1839 run Main;
1840 "#;
1841
1842 let (prog, errors) = parse_str(source);
1843 assert!(errors.is_empty(), "errors: {errors:?}");
1844 let prog = prog.expect("should parse");
1845
1846 assert_eq!(prog.agents.len(), 2);
1847 assert!(prog.agents[0].is_pub);
1848 assert_eq!(prog.agents[0].name.name, "Worker");
1849 assert!(!prog.agents[1].is_pub);
1850 }
1851
1852 #[test]
1853 fn parse_pub_function() {
1854 let source = r#"
1855 pub fn helper(x: Int) -> Int {
1856 return x;
1857 }
1858
1859 agent Main {
1860 on start {
1861 emit(helper(42));
1862 }
1863 }
1864 run Main;
1865 "#;
1866
1867 let (prog, errors) = parse_str(source);
1868 assert!(errors.is_empty(), "errors: {errors:?}");
1869 let prog = prog.expect("should parse");
1870
1871 assert_eq!(prog.functions.len(), 1);
1872 assert!(prog.functions[0].is_pub);
1873 assert_eq!(prog.functions[0].name.name, "helper");
1874 }
1875
1876 #[test]
1877 fn parse_library_no_run() {
1878 let source = r#"
1880 pub agent Worker {
1881 on start {
1882 emit(42);
1883 }
1884 }
1885
1886 pub fn helper(x: Int) -> Int {
1887 return x;
1888 }
1889 "#;
1890
1891 let (prog, errors) = parse_str(source);
1892 assert!(errors.is_empty(), "errors: {errors:?}");
1893 let prog = prog.expect("should parse");
1894
1895 assert!(prog.run_agent.is_none());
1896 assert_eq!(prog.agents.len(), 1);
1897 assert_eq!(prog.functions.len(), 1);
1898 }
1899
1900 #[test]
1901 fn recover_multiple_errors_reported() {
1902 let source = r#"
1904 agent A {
1905 x:
1906 }
1907
1908 agent Main {
1909 on start {
1910 emit(42);
1911 }
1912 }
1913 run Main;
1914 "#;
1915
1916 let (prog, errors) = parse_str(source);
1917 if errors.is_empty() {
1921 let prog = prog.expect("should have AST with recovery");
1923 assert!(prog.agents.iter().any(|a| a.name.name == "Main"));
1924 }
1925 }
1927
1928 #[test]
1929 fn parse_record_declaration() {
1930 let source = r#"
1931 record Point {
1932 x: Int,
1933 y: Int,
1934 }
1935
1936 agent Main {
1937 on start {
1938 emit(0);
1939 }
1940 }
1941 run Main;
1942 "#;
1943
1944 let (prog, errors) = parse_str(source);
1945 assert!(errors.is_empty(), "errors: {errors:?}");
1946 let prog = prog.expect("should parse");
1947
1948 assert_eq!(prog.records.len(), 1);
1949 assert!(!prog.records[0].is_pub);
1950 assert_eq!(prog.records[0].name.name, "Point");
1951 assert_eq!(prog.records[0].fields.len(), 2);
1952 assert_eq!(prog.records[0].fields[0].name.name, "x");
1953 assert_eq!(prog.records[0].fields[1].name.name, "y");
1954 }
1955
1956 #[test]
1957 fn parse_pub_record() {
1958 let source = r#"
1959 pub record Config {
1960 host: String,
1961 port: Int,
1962 }
1963
1964 agent Main {
1965 on start { emit(0); }
1966 }
1967 run Main;
1968 "#;
1969
1970 let (prog, errors) = parse_str(source);
1971 assert!(errors.is_empty(), "errors: {errors:?}");
1972 let prog = prog.expect("should parse");
1973
1974 assert_eq!(prog.records.len(), 1);
1975 assert!(prog.records[0].is_pub);
1976 assert_eq!(prog.records[0].name.name, "Config");
1977 }
1978
1979 #[test]
1980 fn parse_enum_declaration() {
1981 let source = r#"
1982 enum Status {
1983 Active,
1984 Pending,
1985 Done,
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.enums.len(), 1);
2001 assert!(!prog.enums[0].is_pub);
2002 assert_eq!(prog.enums[0].name.name, "Status");
2003 assert_eq!(prog.enums[0].variants.len(), 3);
2004 assert_eq!(prog.enums[0].variants[0].name, "Active");
2005 assert_eq!(prog.enums[0].variants[1].name, "Pending");
2006 assert_eq!(prog.enums[0].variants[2].name, "Done");
2007 }
2008
2009 #[test]
2010 fn parse_pub_enum() {
2011 let source = r#"
2012 pub enum Priority { High, Medium, Low }
2013
2014 agent Main {
2015 on start { emit(0); }
2016 }
2017 run Main;
2018 "#;
2019
2020 let (prog, errors) = parse_str(source);
2021 assert!(errors.is_empty(), "errors: {errors:?}");
2022 let prog = prog.expect("should parse");
2023
2024 assert_eq!(prog.enums.len(), 1);
2025 assert!(prog.enums[0].is_pub);
2026 assert_eq!(prog.enums[0].name.name, "Priority");
2027 }
2028
2029 #[test]
2030 fn parse_const_declaration() {
2031 let source = r#"
2032 const MAX_RETRIES: Int = 3;
2033
2034 agent Main {
2035 on start {
2036 emit(0);
2037 }
2038 }
2039 run Main;
2040 "#;
2041
2042 let (prog, errors) = parse_str(source);
2043 assert!(errors.is_empty(), "errors: {errors:?}");
2044 let prog = prog.expect("should parse");
2045
2046 assert_eq!(prog.consts.len(), 1);
2047 assert!(!prog.consts[0].is_pub);
2048 assert_eq!(prog.consts[0].name.name, "MAX_RETRIES");
2049 assert!(matches!(prog.consts[0].ty, sage_types::TypeExpr::Int));
2050 }
2051
2052 #[test]
2053 fn parse_pub_const() {
2054 let source = r#"
2055 pub const API_URL: String = "https://api.example.com";
2056
2057 agent Main {
2058 on start { emit(0); }
2059 }
2060 run Main;
2061 "#;
2062
2063 let (prog, errors) = parse_str(source);
2064 assert!(errors.is_empty(), "errors: {errors:?}");
2065 let prog = prog.expect("should parse");
2066
2067 assert_eq!(prog.consts.len(), 1);
2068 assert!(prog.consts[0].is_pub);
2069 assert_eq!(prog.consts[0].name.name, "API_URL");
2070 }
2071
2072 #[test]
2073 fn parse_multiple_type_declarations() {
2074 let source = r#"
2075 record Point { x: Int, y: Int }
2076 enum Color { Red, Green, Blue }
2077 const ORIGIN_X: Int = 0;
2078
2079 agent Main {
2080 on start { emit(0); }
2081 }
2082 run Main;
2083 "#;
2084
2085 let (prog, errors) = parse_str(source);
2086 assert!(errors.is_empty(), "errors: {errors:?}");
2087 let prog = prog.expect("should parse");
2088
2089 assert_eq!(prog.records.len(), 1);
2090 assert_eq!(prog.enums.len(), 1);
2091 assert_eq!(prog.consts.len(), 1);
2092 }
2093
2094 #[test]
2095 fn parse_match_expression() {
2096 let source = r#"
2097 enum Status { Active, Pending, Done }
2098
2099 agent Main {
2100 on start {
2101 let s: Int = match Active {
2102 Active => 1,
2103 Pending => 2,
2104 Done => 3,
2105 };
2106 emit(s);
2107 }
2108 }
2109 run Main;
2110 "#;
2111
2112 let (prog, errors) = parse_str(source);
2113 assert!(errors.is_empty(), "errors: {errors:?}");
2114 let prog = prog.expect("should parse");
2115
2116 assert_eq!(prog.agents.len(), 1);
2118 let handler = &prog.agents[0].handlers[0];
2120 let stmt = &handler.body.stmts[0];
2121 if let Stmt::Let { value, .. } = stmt {
2122 assert!(matches!(value, Expr::Match { .. }));
2123 } else {
2124 panic!("expected let statement with match");
2125 }
2126 }
2127
2128 #[test]
2129 fn parse_match_with_wildcard() {
2130 let source = r#"
2131 agent Main {
2132 on start {
2133 let x = 5;
2134 let result = match x {
2135 1 => 10,
2136 2 => 20,
2137 _ => 0,
2138 };
2139 emit(result);
2140 }
2141 }
2142 run Main;
2143 "#;
2144
2145 let (prog, errors) = parse_str(source);
2146 assert!(errors.is_empty(), "errors: {errors:?}");
2147 let prog = prog.expect("should parse");
2148
2149 assert_eq!(prog.agents.len(), 1);
2150 }
2151
2152 #[test]
2153 fn parse_record_construction() {
2154 let source = r#"
2155 record Point { x: Int, y: Int }
2156
2157 agent Main {
2158 on start {
2159 let p = Point { x: 10, y: 20 };
2160 emit(0);
2161 }
2162 }
2163 run Main;
2164 "#;
2165
2166 let (prog, errors) = parse_str(source);
2167 assert!(errors.is_empty(), "errors: {errors:?}");
2168 let prog = prog.expect("should parse");
2169
2170 assert_eq!(prog.records.len(), 1);
2171 assert_eq!(prog.agents.len(), 1);
2172
2173 let handler = &prog.agents[0].handlers[0];
2175 let stmt = &handler.body.stmts[0];
2176 if let Stmt::Let { value, .. } = stmt {
2177 if let Expr::RecordConstruct { name, fields, .. } = value {
2178 assert_eq!(name.name, "Point");
2179 assert_eq!(fields.len(), 2);
2180 assert_eq!(fields[0].name.name, "x");
2181 assert_eq!(fields[1].name.name, "y");
2182 } else {
2183 panic!("expected RecordConstruct");
2184 }
2185 } else {
2186 panic!("expected let statement");
2187 }
2188 }
2189
2190 #[test]
2191 fn parse_match_with_qualified_variant() {
2192 let source = r#"
2193 enum Status { Active, Pending }
2194
2195 fn get_status() -> Int {
2196 return 1;
2197 }
2198
2199 agent Main {
2200 on start {
2201 let s = get_status();
2202 let result = match s {
2203 Status::Active => 1,
2204 Status::Pending => 0,
2205 };
2206 emit(result);
2207 }
2208 }
2209 run Main;
2210 "#;
2211
2212 let (prog, errors) = parse_str(source);
2213 assert!(errors.is_empty(), "errors: {errors:?}");
2214 let prog = prog.expect("should parse");
2215
2216 assert_eq!(prog.enums.len(), 1);
2217 assert_eq!(prog.agents.len(), 1);
2218 }
2219
2220 #[test]
2221 fn parse_field_access() {
2222 let source = r#"
2223 record Point { x: Int, y: Int }
2224
2225 agent Main {
2226 on start {
2227 let p = Point { x: 10, y: 20 };
2228 let x_val = p.x;
2229 let y_val = p.y;
2230 emit(x_val);
2231 }
2232 }
2233 run Main;
2234 "#;
2235
2236 let (prog, errors) = parse_str(source);
2237 assert!(errors.is_empty(), "errors: {errors:?}");
2238 let prog = prog.expect("should parse");
2239
2240 assert_eq!(prog.records.len(), 1);
2241 assert_eq!(prog.agents.len(), 1);
2242
2243 let handler = &prog.agents[0].handlers[0];
2245 let stmt = &handler.body.stmts[1]; if let Stmt::Let { value, .. } = stmt {
2247 if let Expr::FieldAccess { field, .. } = value {
2248 assert_eq!(field.name, "x");
2249 } else {
2250 panic!("expected FieldAccess");
2251 }
2252 } else {
2253 panic!("expected let statement");
2254 }
2255 }
2256
2257 #[test]
2258 fn parse_chained_field_access() {
2259 let source = r#"
2260 record Inner { val: Int }
2261 record Outer { inner: Inner }
2262
2263 agent Main {
2264 on start {
2265 let inner = Inner { val: 42 };
2266 let outer = Outer { inner: inner };
2267 let v = outer.inner.val;
2268 emit(v);
2269 }
2270 }
2271 run Main;
2272 "#;
2273
2274 let (prog, errors) = parse_str(source);
2275 assert!(errors.is_empty(), "errors: {errors:?}");
2276 let prog = prog.expect("should parse");
2277
2278 assert_eq!(prog.records.len(), 2);
2279 assert_eq!(prog.agents.len(), 1);
2280
2281 let handler = &prog.agents[0].handlers[0];
2283 let stmt = &handler.body.stmts[2]; if let Stmt::Let { value, .. } = stmt {
2285 if let Expr::FieldAccess {
2286 object, field: val, ..
2287 } = value
2288 {
2289 assert_eq!(val.name, "val");
2290 if let Expr::FieldAccess { field: inner, .. } = object.as_ref() {
2292 assert_eq!(inner.name, "inner");
2293 } else {
2294 panic!("expected nested FieldAccess");
2295 }
2296 } else {
2297 panic!("expected FieldAccess");
2298 }
2299 } else {
2300 panic!("expected let statement");
2301 }
2302 }
2303
2304 #[test]
2309 fn parse_loop_break() {
2310 let source = r#"
2311 agent Main {
2312 on start {
2313 let count = 0;
2314 loop {
2315 count = count + 1;
2316 if count > 5 {
2317 break;
2318 }
2319 }
2320 emit(count);
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.agents.len(), 1);
2331 let handler = &prog.agents[0].handlers[0];
2332 let loop_stmt = &handler.body.stmts[1];
2334 assert!(matches!(loop_stmt, Stmt::Loop { .. }));
2335 if let Stmt::Loop { body, .. } = loop_stmt {
2337 let if_stmt = &body.stmts[1];
2338 if let Stmt::If { then_block, .. } = if_stmt {
2339 assert!(matches!(then_block.stmts[0], Stmt::Break { .. }));
2340 } else {
2341 panic!("expected if statement");
2342 }
2343 }
2344 }
2345
2346 #[test]
2347 fn parse_agent_receives() {
2348 let source = r#"
2349 enum WorkerMsg {
2350 Task,
2351 Shutdown,
2352 }
2353
2354 agent Worker receives WorkerMsg {
2355 id: Int
2356
2357 on start {
2358 emit(0);
2359 }
2360 }
2361
2362 agent Main {
2363 on start {
2364 emit(0);
2365 }
2366 }
2367 run Main;
2368 "#;
2369
2370 let (prog, errors) = parse_str(source);
2371 assert!(errors.is_empty(), "errors: {errors:?}");
2372 let prog = prog.expect("should parse");
2373
2374 assert_eq!(prog.agents.len(), 2);
2375
2376 let worker = &prog.agents[0];
2378 assert_eq!(worker.name.name, "Worker");
2379 assert!(worker.receives.is_some());
2380 if let Some(TypeExpr::Named(name)) = &worker.receives {
2381 assert_eq!(name.name, "WorkerMsg");
2382 } else {
2383 panic!("expected named type for receives");
2384 }
2385
2386 let main = &prog.agents[1];
2388 assert_eq!(main.name.name, "Main");
2389 assert!(main.receives.is_none());
2390 }
2391
2392 #[test]
2393 fn parse_receive_expression() {
2394 let source = r#"
2395 enum Msg { Ping }
2396
2397 agent Worker receives Msg {
2398 on start {
2399 let msg = receive();
2400 emit(0);
2401 }
2402 }
2403
2404 agent Main {
2405 on start { emit(0); }
2406 }
2407 run Main;
2408 "#;
2409
2410 let (prog, errors) = parse_str(source);
2411 assert!(errors.is_empty(), "errors: {errors:?}");
2412 let prog = prog.expect("should parse");
2413
2414 let worker = prog
2416 .agents
2417 .iter()
2418 .find(|a| a.name.name == "Worker")
2419 .unwrap();
2420 let handler = &worker.handlers[0];
2421 let stmt = &handler.body.stmts[0];
2422
2423 if let Stmt::Let { value, .. } = stmt {
2424 assert!(matches!(value, Expr::Receive { .. }));
2425 } else {
2426 panic!("expected let with receive");
2427 }
2428 }
2429
2430 #[test]
2431 fn parse_message_passing_full() {
2432 let source = r#"
2433 enum WorkerMsg {
2434 Task,
2435 Shutdown,
2436 }
2437
2438 agent Worker receives WorkerMsg {
2439 id: Int
2440
2441 on start {
2442 let msg = receive();
2443 let result = match msg {
2444 Task => 1,
2445 Shutdown => 0,
2446 };
2447 emit(result);
2448 }
2449 }
2450
2451 agent Main {
2452 on start {
2453 let w = spawn Worker { id: 1 };
2454 send(w, Task);
2455 send(w, Shutdown);
2456 await w;
2457 emit(0);
2458 }
2459 }
2460 run Main;
2461 "#;
2462
2463 let (prog, errors) = parse_str(source);
2464 assert!(errors.is_empty(), "errors: {errors:?}");
2465 let prog = prog.expect("should parse");
2466
2467 assert_eq!(prog.enums.len(), 1);
2468 assert_eq!(prog.agents.len(), 2);
2469
2470 let worker = prog
2472 .agents
2473 .iter()
2474 .find(|a| a.name.name == "Worker")
2475 .unwrap();
2476 assert!(worker.receives.is_some());
2477 }
2478}