1use crate::emit::Emitter;
4use sage_loader::ModuleTree;
5use sage_parser::{
6 AgentDecl, BinOp, Block, ConstDecl, EnumDecl, EventKind, Expr, FnDecl, InterpExpr, Literal,
7 MockValue, Program, RecordDecl, Stmt, StringPart, TestDecl, TypeExpr, UnaryOp,
8};
9
10#[derive(Debug, Clone)]
12pub enum RuntimeDep {
13 CratesIo { version: String },
15 Path { path: String },
17}
18
19impl Default for RuntimeDep {
20 fn default() -> Self {
21 Self::CratesIo {
23 version: env!("CARGO_PKG_VERSION").to_string(),
24 }
25 }
26}
27
28impl RuntimeDep {
29 fn to_cargo_dep(&self) -> String {
31 match self {
32 RuntimeDep::CratesIo { version } => {
33 format!("sage-runtime = \"{version}\"")
34 }
35 RuntimeDep::Path { path } => {
36 format!("sage-runtime = {{ path = \"{path}\" }}")
37 }
38 }
39 }
40}
41
42pub struct GeneratedProject {
44 pub main_rs: String,
46 pub cargo_toml: String,
48}
49
50pub fn generate(program: &Program, project_name: &str) -> GeneratedProject {
52 generate_with_config(program, project_name, RuntimeDep::default())
53}
54
55pub fn generate_with_config(
57 program: &Program,
58 project_name: &str,
59 runtime_dep: RuntimeDep,
60) -> GeneratedProject {
61 let mut gen = Generator::new(runtime_dep);
62 let main_rs = gen.generate_program(program);
63 let cargo_toml = gen.generate_cargo_toml(project_name);
64 GeneratedProject {
65 main_rs,
66 cargo_toml,
67 }
68}
69
70pub fn generate_module_tree(tree: &ModuleTree, project_name: &str) -> GeneratedProject {
75 generate_module_tree_with_config(tree, project_name, RuntimeDep::default())
76}
77
78pub fn generate_module_tree_with_config(
80 tree: &ModuleTree,
81 project_name: &str,
82 runtime_dep: RuntimeDep,
83) -> GeneratedProject {
84 let mut gen = Generator::new(runtime_dep);
85 let main_rs = gen.generate_module_tree(tree);
86 let cargo_toml = gen.generate_cargo_toml(project_name);
87 GeneratedProject {
88 main_rs,
89 cargo_toml,
90 }
91}
92
93pub struct GeneratedTestProject {
95 pub main_rs: String,
97 pub cargo_toml: String,
99}
100
101pub fn generate_test_program(program: &Program, test_name: &str) -> GeneratedTestProject {
103 generate_test_program_with_config(program, test_name, RuntimeDep::default())
104}
105
106pub fn generate_test_program_with_config(
108 program: &Program,
109 test_name: &str,
110 runtime_dep: RuntimeDep,
111) -> GeneratedTestProject {
112 let mut gen = Generator::new(runtime_dep);
113 let main_rs = gen.generate_test_binary(program);
114 let cargo_toml = gen.generate_test_cargo_toml(test_name);
115 GeneratedTestProject {
116 main_rs,
117 cargo_toml,
118 }
119}
120
121struct Generator {
122 emit: Emitter,
123 runtime_dep: RuntimeDep,
124}
125
126impl Generator {
127 fn new(runtime_dep: RuntimeDep) -> Self {
128 Self {
129 emit: Emitter::new(),
130 runtime_dep,
131 }
132 }
133
134 fn generate_program(&mut self, program: &Program) -> String {
135 self.emit
137 .writeln("//! Generated by Sage compiler. Do not edit.");
138 self.emit.blank_line();
139 self.emit.writeln("use sage_runtime::prelude::*;");
140 self.emit.blank_line();
141
142 for const_decl in &program.consts {
144 self.generate_const(const_decl);
145 self.emit.blank_line();
146 }
147
148 for enum_decl in &program.enums {
150 self.generate_enum(enum_decl);
151 self.emit.blank_line();
152 }
153
154 for record in &program.records {
156 self.generate_record(record);
157 self.emit.blank_line();
158 }
159
160 for func in &program.functions {
162 self.generate_function(func);
163 self.emit.blank_line();
164 }
165
166 for agent in &program.agents {
168 self.generate_agent(agent);
169 self.emit.blank_line();
170 }
171
172 if let Some(run_agent) = &program.run_agent {
174 if let Some(agent) = program.agents.iter().find(|a| a.name.name == run_agent.name) {
176 self.generate_main(agent);
177 }
178 }
179
180 std::mem::take(&mut self.emit).finish()
181 }
182
183 fn generate_module_tree(&mut self, tree: &ModuleTree) -> String {
184 self.emit
186 .writeln("//! Generated by Sage compiler. Do not edit.");
187 self.emit.blank_line();
188 self.emit.writeln("use sage_runtime::prelude::*;");
189 self.emit.blank_line();
190
191 for (path, module) in &tree.modules {
197 if path != &tree.root {
198 self.emit.write("// Module: ");
199 if path.is_empty() {
200 self.emit.writeln("(root)");
201 } else {
202 self.emit.writeln(&path.join("::"));
203 }
204
205 for const_decl in &module.program.consts {
206 self.generate_const(const_decl);
207 self.emit.blank_line();
208 }
209
210 for enum_decl in &module.program.enums {
211 self.generate_enum(enum_decl);
212 self.emit.blank_line();
213 }
214
215 for record in &module.program.records {
216 self.generate_record(record);
217 self.emit.blank_line();
218 }
219
220 for func in &module.program.functions {
221 self.generate_function(func);
222 self.emit.blank_line();
223 }
224
225 for agent in &module.program.agents {
226 self.generate_agent(agent);
227 self.emit.blank_line();
228 }
229 }
230 }
231
232 if let Some(root_module) = tree.modules.get(&tree.root) {
234 self.emit.writeln("// Root module");
235
236 for const_decl in &root_module.program.consts {
237 self.generate_const(const_decl);
238 self.emit.blank_line();
239 }
240
241 for enum_decl in &root_module.program.enums {
242 self.generate_enum(enum_decl);
243 self.emit.blank_line();
244 }
245
246 for record in &root_module.program.records {
247 self.generate_record(record);
248 self.emit.blank_line();
249 }
250
251 for func in &root_module.program.functions {
252 self.generate_function(func);
253 self.emit.blank_line();
254 }
255
256 for agent in &root_module.program.agents {
257 self.generate_agent(agent);
258 self.emit.blank_line();
259 }
260
261 if let Some(run_agent) = &root_module.program.run_agent {
263 if let Some(agent) = root_module
265 .program
266 .agents
267 .iter()
268 .find(|a| a.name.name == run_agent.name)
269 {
270 self.generate_main(agent);
271 }
272 }
273 }
274
275 std::mem::take(&mut self.emit).finish()
276 }
277
278 fn generate_cargo_toml(&self, name: &str) -> String {
279 let runtime_dep = self.runtime_dep.to_cargo_dep();
280 format!(
281 r#"[package]
282name = "{name}"
283version = "0.1.0"
284edition = "2021"
285
286[dependencies]
287{runtime_dep}
288tokio = {{ version = "1", features = ["full"] }}
289serde = {{ version = "1", features = ["derive"] }}
290serde_json = "1"
291
292# Standalone project, not part of parent workspace
293[workspace]
294"#
295 )
296 }
297
298 fn generate_test_binary(&mut self, program: &Program) -> String {
303 self.emit
305 .writeln("//! Generated test file by Sage compiler. Do not edit.");
306 self.emit.blank_line();
307 self.emit.writeln("use sage_runtime::prelude::*;");
308 self.emit.blank_line();
309
310 for const_decl in &program.consts {
312 self.generate_const(const_decl);
313 self.emit.blank_line();
314 }
315
316 for enum_decl in &program.enums {
318 self.generate_enum(enum_decl);
319 self.emit.blank_line();
320 }
321
322 for record in &program.records {
324 self.generate_record(record);
325 self.emit.blank_line();
326 }
327
328 for func in &program.functions {
330 self.generate_function(func);
331 self.emit.blank_line();
332 }
333
334 for agent in &program.agents {
336 self.generate_agent(agent);
337 self.emit.blank_line();
338 }
339
340 let (serial_tests, concurrent_tests): (Vec<_>, Vec<_>) =
342 program.tests.iter().partition(|t| t.is_serial);
343
344 for test in &concurrent_tests {
346 self.generate_test_function(test);
347 self.emit.blank_line();
348 }
349
350 for test in &serial_tests {
352 self.generate_test_function(test);
353 self.emit.blank_line();
354 }
355
356 self.emit.writeln("fn main() {}");
358
359 std::mem::take(&mut self.emit).finish()
360 }
361
362 fn generate_test_cargo_toml(&self, name: &str) -> String {
363 let runtime_dep = self.runtime_dep.to_cargo_dep();
364 format!(
365 r#"[package]
366name = "{name}"
367version = "0.1.0"
368edition = "2021"
369
370[dependencies]
371{runtime_dep}
372tokio = {{ version = "1", features = ["full"] }}
373serde = {{ version = "1", features = ["derive"] }}
374serde_json = "1"
375
376# Standalone project, not part of parent workspace
377[workspace]
378"#
379 )
380 }
381
382 fn generate_test_function(&mut self, test: &TestDecl) {
383 let mocks = self.collect_mock_infers(&test.body);
385
386 self.emit.writeln("#[tokio::test]");
388 let test_fn_name = self.sanitize_test_name(&test.name);
390 self.emit.write("async fn ");
391 self.emit.write(&test_fn_name);
392 self.emit.writeln("() {");
393 self.emit.indent();
394
395 if !mocks.is_empty() {
397 self.emit.writeln("let _mock_client = MockLlmClient::with_responses(vec![");
398 self.emit.indent();
399 for mock in &mocks {
400 match mock {
401 MockValue::Value(expr) => {
402 self.emit.write("MockResponse::value(");
403 self.generate_expr(expr);
404 self.emit.writeln("),");
405 }
406 MockValue::Fail(expr) => {
407 self.emit.write("MockResponse::fail(");
408 self.generate_expr(expr);
409 self.emit.writeln("),");
410 }
411 }
412 }
413 self.emit.dedent();
414 self.emit.writeln("]);");
415 self.emit.blank_line();
416 }
417
418 self.generate_test_block(&test.body);
420
421 self.emit.dedent();
422 self.emit.writeln("}");
423 }
424
425 fn collect_mock_infers(&self, block: &Block) -> Vec<MockValue> {
426 let mut mocks = Vec::new();
427 for stmt in &block.stmts {
428 if let Stmt::MockInfer { value, .. } = stmt {
429 mocks.push(value.clone());
430 }
431 }
432 mocks
433 }
434
435 fn generate_test_block(&mut self, block: &Block) {
436 for stmt in &block.stmts {
437 if matches!(stmt, Stmt::MockInfer { .. }) {
439 continue;
440 }
441 self.generate_test_stmt(stmt);
442 }
443 }
444
445 fn generate_test_stmt(&mut self, stmt: &Stmt) {
446 match stmt {
447 Stmt::Expr { expr, .. } => {
449 if let Expr::Call { name, args, .. } = expr {
450 if self.is_assertion_builtin(&name.name) {
451 self.generate_assertion(&name.name, args);
452 return;
453 }
454 }
455 self.generate_expr(expr);
457 self.emit.writeln(";");
458 }
459 _ => self.generate_stmt(stmt),
461 }
462 }
463
464 fn is_assertion_builtin(&self, name: &str) -> bool {
465 matches!(
466 name,
467 "assert"
468 | "assert_eq"
469 | "assert_neq"
470 | "assert_gt"
471 | "assert_lt"
472 | "assert_gte"
473 | "assert_lte"
474 | "assert_true"
475 | "assert_false"
476 | "assert_contains"
477 | "assert_not_contains"
478 | "assert_empty"
479 | "assert_not_empty"
480 | "assert_starts_with"
481 | "assert_ends_with"
482 | "assert_len"
483 | "assert_empty_list"
484 | "assert_not_empty_list"
485 | "assert_fails"
486 )
487 }
488
489 fn generate_assertion(&mut self, name: &str, args: &[Expr]) {
490 match name {
491 "assert" | "assert_true" => {
492 self.emit.write("assert!(");
493 if !args.is_empty() {
494 self.generate_expr(&args[0]);
495 }
496 self.emit.writeln(");");
497 }
498 "assert_false" => {
499 self.emit.write("assert!(!");
500 if !args.is_empty() {
501 self.generate_expr(&args[0]);
502 }
503 self.emit.writeln(");");
504 }
505 "assert_eq" => {
506 self.emit.write("assert_eq!(");
507 if args.len() >= 2 {
508 self.generate_expr(&args[0]);
509 self.emit.write(", ");
510 self.generate_expr(&args[1]);
511 }
512 self.emit.writeln(");");
513 }
514 "assert_neq" => {
515 self.emit.write("assert_ne!(");
516 if args.len() >= 2 {
517 self.generate_expr(&args[0]);
518 self.emit.write(", ");
519 self.generate_expr(&args[1]);
520 }
521 self.emit.writeln(");");
522 }
523 "assert_gt" => {
524 self.emit.write("assert!(");
525 if args.len() >= 2 {
526 self.generate_expr(&args[0]);
527 self.emit.write(" > ");
528 self.generate_expr(&args[1]);
529 }
530 self.emit.writeln(");");
531 }
532 "assert_lt" => {
533 self.emit.write("assert!(");
534 if args.len() >= 2 {
535 self.generate_expr(&args[0]);
536 self.emit.write(" < ");
537 self.generate_expr(&args[1]);
538 }
539 self.emit.writeln(");");
540 }
541 "assert_gte" => {
542 self.emit.write("assert!(");
543 if args.len() >= 2 {
544 self.generate_expr(&args[0]);
545 self.emit.write(" >= ");
546 self.generate_expr(&args[1]);
547 }
548 self.emit.writeln(");");
549 }
550 "assert_lte" => {
551 self.emit.write("assert!(");
552 if args.len() >= 2 {
553 self.generate_expr(&args[0]);
554 self.emit.write(" <= ");
555 self.generate_expr(&args[1]);
556 }
557 self.emit.writeln(");");
558 }
559 "assert_contains" => {
560 self.emit.write("assert!(");
561 if args.len() >= 2 {
562 self.generate_expr(&args[0]);
563 self.emit.write(".contains(&");
564 self.generate_expr(&args[1]);
565 self.emit.write(")");
566 }
567 self.emit.writeln(");");
568 }
569 "assert_not_contains" => {
570 self.emit.write("assert!(!");
571 if args.len() >= 2 {
572 self.generate_expr(&args[0]);
573 self.emit.write(".contains(&");
574 self.generate_expr(&args[1]);
575 self.emit.write(")");
576 }
577 self.emit.writeln(");");
578 }
579 "assert_empty" => {
580 self.emit.write("assert!(");
581 if !args.is_empty() {
582 self.generate_expr(&args[0]);
583 }
584 self.emit.writeln(".is_empty());");
585 }
586 "assert_not_empty" => {
587 self.emit.write("assert!(!");
588 if !args.is_empty() {
589 self.generate_expr(&args[0]);
590 }
591 self.emit.writeln(".is_empty());");
592 }
593 "assert_starts_with" => {
594 self.emit.write("assert!(");
595 if args.len() >= 2 {
596 self.generate_expr(&args[0]);
597 self.emit.write(".starts_with(&");
598 self.generate_expr(&args[1]);
599 self.emit.write(")");
600 }
601 self.emit.writeln(");");
602 }
603 "assert_ends_with" => {
604 self.emit.write("assert!(");
605 if args.len() >= 2 {
606 self.generate_expr(&args[0]);
607 self.emit.write(".ends_with(&");
608 self.generate_expr(&args[1]);
609 self.emit.write(")");
610 }
611 self.emit.writeln(");");
612 }
613 "assert_len" => {
614 self.emit.write("assert_eq!(");
615 if args.len() >= 2 {
616 self.generate_expr(&args[0]);
617 self.emit.write(".len() as i64, ");
618 self.generate_expr(&args[1]);
619 }
620 self.emit.writeln(");");
621 }
622 "assert_empty_list" => {
623 self.emit.write("assert!(");
624 if !args.is_empty() {
625 self.generate_expr(&args[0]);
626 }
627 self.emit.writeln(".is_empty());");
628 }
629 "assert_not_empty_list" => {
630 self.emit.write("assert!(!");
631 if !args.is_empty() {
632 self.generate_expr(&args[0]);
633 }
634 self.emit.writeln(".is_empty());");
635 }
636 "assert_fails" => {
637 self.emit.writeln("{");
639 self.emit.indent();
640 self.emit.write("let result = ");
641 if !args.is_empty() {
642 self.generate_expr(&args[0]);
643 }
644 self.emit.writeln(";");
645 self.emit.writeln("assert!(result.is_err(), \"Expected operation to fail but it succeeded\");");
646 self.emit.dedent();
647 self.emit.writeln("}");
648 }
649 _ => {
650 self.emit.write(name);
652 self.emit.write("(");
653 for (i, arg) in args.iter().enumerate() {
654 if i > 0 {
655 self.emit.write(", ");
656 }
657 self.generate_expr(arg);
658 }
659 self.emit.writeln(");");
660 }
661 }
662 }
663
664 fn sanitize_test_name(&self, name: &str) -> String {
665 name.chars()
667 .map(|c| {
668 if c.is_alphanumeric() {
669 c
670 } else {
671 '_'
672 }
673 })
674 .collect::<String>()
675 .to_lowercase()
676 }
677
678 fn generate_const(&mut self, const_decl: &ConstDecl) {
679 if const_decl.is_pub {
680 self.emit.write("pub ");
681 }
682 self.emit.write("const ");
683 self.emit.write(&const_decl.name.name);
684 self.emit.write(": ");
685 self.emit_type(&const_decl.ty);
686 self.emit.write(" = ");
687 self.generate_expr(&const_decl.value);
688 self.emit.writeln(";");
689 }
690
691 fn generate_enum(&mut self, enum_decl: &EnumDecl) {
692 if enum_decl.is_pub {
693 self.emit.write("pub ");
694 }
695 self.emit
696 .writeln("#[derive(Debug, Clone, Copy, PartialEq, Eq)]");
697 self.emit.write("enum ");
698 self.emit.write(&enum_decl.name.name);
699 self.emit.writeln(" {");
700 self.emit.indent();
701 for variant in &enum_decl.variants {
702 self.emit.write(&variant.name.name);
703 if let Some(payload_ty) = &variant.payload {
704 self.emit.write("(");
705 self.emit_type(payload_ty);
706 self.emit.write(")");
707 }
708 self.emit.writeln(",");
709 }
710 self.emit.dedent();
711 self.emit.writeln("}");
712 }
713
714 fn generate_record(&mut self, record: &RecordDecl) {
715 if record.is_pub {
716 self.emit.write("pub ");
717 }
718 self.emit.writeln("#[derive(Debug, Clone)]");
719 self.emit.write("struct ");
720 self.emit.write(&record.name.name);
721 self.emit.writeln(" {");
722 self.emit.indent();
723 for field in &record.fields {
724 self.emit.write(&field.name.name);
725 self.emit.write(": ");
726 self.emit_type(&field.ty);
727 self.emit.writeln(",");
728 }
729 self.emit.dedent();
730 self.emit.writeln("}");
731 }
732
733 fn generate_function(&mut self, func: &FnDecl) {
734 if func.is_pub {
736 self.emit.write("pub ");
737 }
738 self.emit.write("fn ");
739 self.emit.write(&func.name.name);
740 self.emit.write("(");
741
742 for (i, param) in func.params.iter().enumerate() {
743 if i > 0 {
744 self.emit.write(", ");
745 }
746 self.emit.write(¶m.name.name);
747 self.emit.write(": ");
748 self.emit_type(¶m.ty);
749 }
750
751 self.emit.write(") -> ");
752
753 if func.is_fallible {
755 self.emit.write("SageResult<");
756 self.emit_type(&func.return_ty);
757 self.emit.write(">");
758 } else {
759 self.emit_type(&func.return_ty);
760 }
761
762 self.emit.write(" ");
763 self.generate_block(&func.body);
764 }
765
766 fn generate_agent(&mut self, agent: &AgentDecl) {
767 let name = &agent.name.name;
768
769 let has_tools = !agent.tool_uses.is_empty();
771 let needs_struct_body = !agent.beliefs.is_empty() || has_tools;
772
773 if agent.is_pub {
775 self.emit.write("pub ");
776 }
777 self.emit.write("struct ");
778 self.emit.write(name);
779 if !needs_struct_body {
780 self.emit.writeln(";");
781 } else {
782 self.emit.writeln(" {");
783 self.emit.indent();
784
785 for tool_use in &agent.tool_uses {
787 self.emit.write(&tool_use.name.to_lowercase());
789 self.emit.write(": ");
790 self.emit.write(&tool_use.name);
791 self.emit.writeln("Client,");
792 }
793
794 for belief in &agent.beliefs {
796 self.emit.write(&belief.name.name);
797 self.emit.write(": ");
798 self.emit_type(&belief.ty);
799 self.emit.writeln(",");
800 }
801 self.emit.dedent();
802 self.emit.writeln("}");
803 }
804 self.emit.blank_line();
805
806 let output_type = self.infer_agent_output_type(agent);
808
809 self.emit.write("impl ");
811 self.emit.write(name);
812 self.emit.writeln(" {");
813 self.emit.indent();
814
815 for handler in &agent.handlers {
817 match &handler.event {
818 EventKind::Start => {
819 self.emit
820 .write("async fn on_start(&self, ctx: &mut AgentContext<");
821 self.emit.write(&output_type);
822 self.emit.write(">) -> SageResult<");
823 self.emit.write(&output_type);
824 self.emit.writeln("> {");
825 self.emit.indent();
826 self.generate_block_contents(&handler.body);
827 self.emit.dedent();
828 self.emit.writeln("}");
829 }
830
831 EventKind::Error { param_name } => {
833 self.emit.write("async fn on_error(&self, ");
834 self.emit.write(¶m_name.name);
835 self.emit.write(": SageError, ctx: &mut AgentContext<");
836 self.emit.write(&output_type);
837 self.emit.write(">) -> SageResult<");
838 self.emit.write(&output_type);
839 self.emit.writeln("> {");
840 self.emit.indent();
841 self.generate_block_contents(&handler.body);
842 self.emit.dedent();
843 self.emit.writeln("}");
844 }
845
846 EventKind::Stop => {
848 self.emit
849 .writeln("async fn on_stop(&self) {");
850 self.emit.indent();
851 self.generate_block_contents(&handler.body);
852 self.emit.dedent();
853 self.emit.writeln("}");
854 }
855
856 _ => {}
858 }
859 }
860
861 self.emit.dedent();
862 self.emit.writeln("}");
863 }
864
865 fn generate_main(&mut self, agent: &AgentDecl) {
866 let entry_agent = &agent.name.name;
867 let has_error_handler = agent
868 .handlers
869 .iter()
870 .any(|h| matches!(h.event, EventKind::Error { .. }));
871
872 let has_stop_handler = agent
873 .handlers
874 .iter()
875 .any(|h| matches!(h.event, EventKind::Stop));
876
877 let has_tools = !agent.tool_uses.is_empty();
879
880 self.emit.writeln("#[tokio::main]");
881 self.emit
882 .writeln("async fn main() -> Result<(), Box<dyn std::error::Error>> {");
883 self.emit.indent();
884
885 self.emit.writeln("sage_runtime::trace::init();");
887 self.emit.writeln("");
888
889 let agent_construct = if has_tools {
891 let mut s = format!("{entry_agent} {{ ");
892 for (i, tool_use) in agent.tool_uses.iter().enumerate() {
893 if i > 0 {
894 s.push_str(", ");
895 }
896 s.push_str(&tool_use.name.to_lowercase());
898 s.push_str(": ");
899 s.push_str(&tool_use.name);
900 s.push_str("Client::from_env()");
901 }
902 s.push_str(" }");
903 s
904 } else {
905 entry_agent.to_string()
906 };
907
908 self.emit
910 .writeln("let ctrl_c = async { tokio::signal::ctrl_c().await.ok() };");
911
912 self.emit.writeln("#[cfg(unix)]");
913 self.emit.writeln("let terminate = async {");
914 self.emit.indent();
915 self.emit.writeln(
916 "if let Ok(mut s) = tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate()) {",
917 );
918 self.emit.indent();
919 self.emit.writeln("s.recv().await;");
920 self.emit.dedent();
921 self.emit.writeln("} else {");
922 self.emit.indent();
923 self.emit.writeln("std::future::pending::<()>().await;");
924 self.emit.dedent();
925 self.emit.writeln("}");
926 self.emit.dedent();
927 self.emit.writeln("};");
928 self.emit.writeln("#[cfg(not(unix))]");
929 self.emit.writeln("let terminate = std::future::pending::<()>();");
930 self.emit.writeln("");
931
932 self.emit
933 .writeln("let handle = sage_runtime::spawn(|mut ctx| async move {");
934 self.emit.indent();
935 self.emit.write("let agent = ");
936 self.emit.write(&agent_construct);
937 self.emit.writeln(";");
938
939 if has_error_handler {
940 self.emit
942 .writeln("let result = match agent.on_start(&mut ctx).await {");
943 self.emit.indent();
944 self.emit.writeln("Ok(result) => Ok(result),");
945 self.emit.writeln("Err(e) => agent.on_error(e, &mut ctx).await,");
946 self.emit.dedent();
947 self.emit.writeln("};");
948 } else {
949 self.emit
951 .writeln("let result = agent.on_start(&mut ctx).await;");
952 }
953
954 if has_stop_handler {
955 self.emit.writeln("agent.on_stop().await;");
957 }
958
959 self.emit.writeln("result");
960 self.emit.dedent();
961 self.emit.writeln("});");
962
963 self.emit.writeln("");
965 self.emit.writeln("let result = tokio::select! {");
966 self.emit.indent();
967 self.emit.writeln("result = handle.result() => result?,");
968 self.emit.writeln("_ = ctrl_c => {");
969 self.emit.indent();
970 self.emit.writeln("eprintln!(\"\\nReceived interrupt signal, shutting down...\");");
971 self.emit.writeln("std::process::exit(0);");
972 self.emit.dedent();
973 self.emit.writeln("}");
974 self.emit.writeln("_ = terminate => {");
975 self.emit.indent();
976 self.emit.writeln("eprintln!(\"Received terminate signal, shutting down...\");");
977 self.emit.writeln("std::process::exit(0);");
978 self.emit.dedent();
979 self.emit.writeln("}");
980 self.emit.dedent();
981 self.emit.writeln("};");
982 self.emit.writeln("println!(\"{:?}\", result);");
983 self.emit.writeln("Ok(())");
984
985 self.emit.dedent();
986 self.emit.writeln("}");
987 }
988
989 fn generate_block(&mut self, block: &Block) {
990 self.emit.open_brace();
991 self.generate_block_contents(block);
992 self.emit.close_brace();
993 }
994
995 fn generate_block_inline(&mut self, block: &Block) {
996 self.emit.open_brace();
997 self.generate_block_contents(block);
998 self.emit.close_brace_inline();
999 }
1000
1001 fn generate_block_contents(&mut self, block: &Block) {
1002 for stmt in &block.stmts {
1003 self.generate_stmt(stmt);
1004 }
1005 }
1006
1007 fn generate_stmt(&mut self, stmt: &Stmt) {
1008 match stmt {
1009 Stmt::Let {
1010 name, ty, value, ..
1011 } => {
1012 self.emit.write("let ");
1013 if ty.is_some() {
1014 self.emit.write(&name.name);
1015 self.emit.write(": ");
1016 self.emit_type(ty.as_ref().unwrap());
1017 } else {
1018 self.emit.write(&name.name);
1019 }
1020 self.emit.write(" = ");
1021 self.generate_expr(value);
1022 self.emit.writeln(";");
1023 }
1024
1025 Stmt::Assign { name, value, .. } => {
1026 self.emit.write(&name.name);
1027 self.emit.write(" = ");
1028 self.generate_expr(value);
1029 self.emit.writeln(";");
1030 }
1031
1032 Stmt::Return { value, .. } => {
1033 self.emit.write("return ");
1034 if let Some(expr) = value {
1035 self.generate_expr(expr);
1036 }
1037 self.emit.writeln(";");
1038 }
1039
1040 Stmt::If {
1041 condition,
1042 then_block,
1043 else_block,
1044 ..
1045 } => {
1046 self.emit.write("if ");
1047 self.generate_expr(condition);
1048 self.emit.write(" ");
1049 if else_block.is_some() {
1050 self.generate_block_inline(then_block);
1051 self.emit.write(" else ");
1052 match else_block.as_ref().unwrap() {
1053 sage_parser::ElseBranch::Block(block) => {
1054 self.generate_block(block);
1055 }
1056 sage_parser::ElseBranch::ElseIf(stmt) => {
1057 self.generate_stmt(stmt);
1058 }
1059 }
1060 } else {
1061 self.generate_block(then_block);
1062 }
1063 }
1064
1065 Stmt::For {
1066 pattern,
1067 iter,
1068 body,
1069 ..
1070 } => {
1071 self.emit.write("for ");
1072 self.emit_pattern(pattern);
1073 self.emit.write(" in ");
1074 self.generate_expr(iter);
1075 self.emit.write(" ");
1076 self.generate_block(body);
1077 }
1078
1079 Stmt::While {
1080 condition, body, ..
1081 } => {
1082 self.emit.write("while ");
1083 self.generate_expr(condition);
1084 self.emit.write(" ");
1085 self.generate_block(body);
1086 }
1087
1088 Stmt::Loop { body, .. } => {
1089 self.emit.write("loop ");
1090 self.generate_block(body);
1091 }
1092
1093 Stmt::Break { .. } => {
1094 self.emit.writeln("break;");
1095 }
1096
1097 Stmt::Expr { expr, .. } => {
1098 if let Expr::Emit { value, .. } = expr {
1100 self.emit.write("return ctx.emit(");
1101 self.generate_expr(value);
1102 self.emit.writeln(");");
1103 } else {
1104 self.generate_expr(expr);
1105 self.emit.writeln(";");
1106 }
1107 }
1108
1109 Stmt::LetTuple { names, value, .. } => {
1110 self.emit.write("let (");
1111 for (i, name) in names.iter().enumerate() {
1112 if i > 0 {
1113 self.emit.write(", ");
1114 }
1115 self.emit.write(&name.name);
1116 }
1117 self.emit.write(") = ");
1118 self.generate_expr(value);
1119 self.emit.writeln(";");
1120 }
1121
1122 Stmt::MockInfer { value, .. } => {
1124 self.emit.write("// mock infer: ");
1127 match value {
1128 sage_parser::MockValue::Value(expr) => {
1129 self.generate_expr(expr);
1130 }
1131 sage_parser::MockValue::Fail(expr) => {
1132 self.emit.write("fail(");
1133 self.generate_expr(expr);
1134 self.emit.write(")");
1135 }
1136 }
1137 self.emit.writeln(";");
1138 }
1139 }
1140 }
1141
1142 fn generate_expr(&mut self, expr: &Expr) {
1143 match expr {
1144 Expr::Literal { value, .. } => {
1145 self.emit_literal(value);
1146 }
1147
1148 Expr::Var { name, .. } => {
1149 match name.name.as_str() {
1151 "PI" => self.emit.write("std::f64::consts::PI"),
1152 "E" => self.emit.write("std::f64::consts::E"),
1153 _ => self.emit.write(&name.name),
1154 }
1155 }
1156
1157 Expr::Binary {
1158 op, left, right, ..
1159 } => {
1160 if matches!(op, BinOp::Concat) {
1162 self.emit.write("format!(\"{}{}\", ");
1163 self.generate_expr(left);
1164 self.emit.write(", ");
1165 self.generate_expr(right);
1166 self.emit.write(")");
1167 } else {
1168 self.emit.write("(");
1169 self.generate_expr(left);
1170 self.emit.write(" ");
1171 self.emit_binop(op);
1172 self.emit.write(" ");
1173 self.generate_expr(right);
1174 self.emit.write(")");
1175 }
1176 }
1177
1178 Expr::Unary { op, operand, .. } => {
1179 self.emit_unaryop(op);
1180 self.generate_expr(operand);
1181 }
1182
1183 Expr::Call { name, args, .. } => {
1184 let fn_name = &name.name;
1185
1186 match fn_name.as_str() {
1188 "print" => {
1189 self.emit.write("println!(\"{}\", ");
1190 self.generate_expr(&args[0]);
1191 self.emit.write(")");
1192 }
1193 "str" => {
1194 self.generate_expr(&args[0]);
1195 self.emit.write(".to_string()");
1196 }
1197 "len" => {
1198 self.generate_expr(&args[0]);
1199 self.emit.write(".len() as i64");
1200 }
1201
1202 "split" => {
1204 self.generate_expr(&args[0]);
1205 self.emit.write(".split(&*");
1206 self.generate_expr(&args[1]);
1207 self.emit.write(").map(str::to_string).collect::<Vec<_>>()");
1208 }
1209 "trim" => {
1210 self.generate_expr(&args[0]);
1211 self.emit.write(".trim().to_string()");
1212 }
1213 "trim_start" => {
1214 self.generate_expr(&args[0]);
1215 self.emit.write(".trim_start().to_string()");
1216 }
1217 "trim_end" => {
1218 self.generate_expr(&args[0]);
1219 self.emit.write(".trim_end().to_string()");
1220 }
1221 "starts_with" => {
1222 self.generate_expr(&args[0]);
1223 self.emit.write(".starts_with(&*");
1224 self.generate_expr(&args[1]);
1225 self.emit.write(")");
1226 }
1227 "ends_with" => {
1228 self.generate_expr(&args[0]);
1229 self.emit.write(".ends_with(&*");
1230 self.generate_expr(&args[1]);
1231 self.emit.write(")");
1232 }
1233 "replace" => {
1234 self.generate_expr(&args[0]);
1235 self.emit.write(".replace(&*");
1236 self.generate_expr(&args[1]);
1237 self.emit.write(", &*");
1238 self.generate_expr(&args[2]);
1239 self.emit.write(")");
1240 }
1241 "replace_first" => {
1242 self.generate_expr(&args[0]);
1243 self.emit.write(".replacen(&*");
1244 self.generate_expr(&args[1]);
1245 self.emit.write(", &*");
1246 self.generate_expr(&args[2]);
1247 self.emit.write(", 1)");
1248 }
1249 "to_upper" => {
1250 self.generate_expr(&args[0]);
1251 self.emit.write(".to_uppercase()");
1252 }
1253 "to_lower" => {
1254 self.generate_expr(&args[0]);
1255 self.emit.write(".to_lowercase()");
1256 }
1257 "str_len" => {
1258 self.generate_expr(&args[0]);
1259 self.emit.write(".chars().count() as i64");
1260 }
1261 "str_slice" => {
1262 self.emit.write("sage_runtime::stdlib::str_slice(&");
1263 self.generate_expr(&args[0]);
1264 self.emit.write(", ");
1265 self.generate_expr(&args[1]);
1266 self.emit.write(", ");
1267 self.generate_expr(&args[2]);
1268 self.emit.write(")");
1269 }
1270 "str_index_of" => {
1271 self.emit.write("sage_runtime::stdlib::str_index_of(&");
1272 self.generate_expr(&args[0]);
1273 self.emit.write(", &");
1274 self.generate_expr(&args[1]);
1275 self.emit.write(")");
1276 }
1277 "str_repeat" => {
1278 self.generate_expr(&args[0]);
1279 self.emit.write(".repeat(");
1280 self.generate_expr(&args[1]);
1281 self.emit.write(" as usize)");
1282 }
1283 "str_pad_start" => {
1284 self.emit.write("sage_runtime::stdlib::str_pad_start(&");
1285 self.generate_expr(&args[0]);
1286 self.emit.write(", ");
1287 self.generate_expr(&args[1]);
1288 self.emit.write(", &");
1289 self.generate_expr(&args[2]);
1290 self.emit.write(")");
1291 }
1292 "str_pad_end" => {
1293 self.emit.write("sage_runtime::stdlib::str_pad_end(&");
1294 self.generate_expr(&args[0]);
1295 self.emit.write(", ");
1296 self.generate_expr(&args[1]);
1297 self.emit.write(", &");
1298 self.generate_expr(&args[2]);
1299 self.emit.write(")");
1300 }
1301
1302 "abs" => {
1304 self.generate_expr(&args[0]);
1305 self.emit.write(".abs()");
1306 }
1307 "abs_float" => {
1308 self.generate_expr(&args[0]);
1309 self.emit.write(".abs()");
1310 }
1311 "min" => {
1312 self.generate_expr(&args[0]);
1313 self.emit.write(".min(");
1314 self.generate_expr(&args[1]);
1315 self.emit.write(")");
1316 }
1317 "max" => {
1318 self.generate_expr(&args[0]);
1319 self.emit.write(".max(");
1320 self.generate_expr(&args[1]);
1321 self.emit.write(")");
1322 }
1323 "min_float" => {
1324 self.generate_expr(&args[0]);
1325 self.emit.write(".min(");
1326 self.generate_expr(&args[1]);
1327 self.emit.write(")");
1328 }
1329 "max_float" => {
1330 self.generate_expr(&args[0]);
1331 self.emit.write(".max(");
1332 self.generate_expr(&args[1]);
1333 self.emit.write(")");
1334 }
1335 "clamp" => {
1336 self.generate_expr(&args[0]);
1337 self.emit.write(".clamp(");
1338 self.generate_expr(&args[1]);
1339 self.emit.write(", ");
1340 self.generate_expr(&args[2]);
1341 self.emit.write(")");
1342 }
1343 "clamp_float" => {
1344 self.generate_expr(&args[0]);
1345 self.emit.write(".clamp(");
1346 self.generate_expr(&args[1]);
1347 self.emit.write(", ");
1348 self.generate_expr(&args[2]);
1349 self.emit.write(")");
1350 }
1351 "floor" => {
1352 self.generate_expr(&args[0]);
1353 self.emit.write(".floor() as i64");
1354 }
1355 "ceil" => {
1356 self.generate_expr(&args[0]);
1357 self.emit.write(".ceil() as i64");
1358 }
1359 "round" => {
1360 self.generate_expr(&args[0]);
1361 self.emit.write(".round() as i64");
1362 }
1363 "floor_float" => {
1364 self.generate_expr(&args[0]);
1365 self.emit.write(".floor()");
1366 }
1367 "ceil_float" => {
1368 self.generate_expr(&args[0]);
1369 self.emit.write(".ceil()");
1370 }
1371 "pow" => {
1372 self.emit.write("{ let __base = ");
1374 self.generate_expr(&args[0]);
1375 self.emit.write("; let __exp = ");
1376 self.generate_expr(&args[1]);
1377 self.emit.write("; if __exp < 0 { 0 } else { __base.pow(__exp as u32) } }");
1378 }
1379 "pow_float" => {
1380 self.generate_expr(&args[0]);
1381 self.emit.write(".powf(");
1382 self.generate_expr(&args[1]);
1383 self.emit.write(")");
1384 }
1385 "sqrt" => {
1386 self.generate_expr(&args[0]);
1387 self.emit.write(".sqrt()");
1388 }
1389 "int_to_float" => {
1390 self.generate_expr(&args[0]);
1391 self.emit.write(" as f64");
1392 }
1393 "float_to_int" => {
1394 self.generate_expr(&args[0]);
1395 self.emit.write(" as i64");
1396 }
1397
1398 "parse_int" => {
1400 self.generate_expr(&args[0]);
1401 self.emit.write(".trim().parse::<i64>().map_err(|e| e.to_string())");
1402 }
1403 "parse_float" => {
1404 self.generate_expr(&args[0]);
1405 self.emit.write(".trim().parse::<f64>().map_err(|e| e.to_string())");
1406 }
1407 "parse_bool" => {
1408 self.emit.write("sage_runtime::stdlib::parse_bool(&");
1409 self.generate_expr(&args[0]);
1410 self.emit.write(")");
1411 }
1412 "float_to_str" => {
1413 self.generate_expr(&args[0]);
1414 self.emit.write(".to_string()");
1415 }
1416 "bool_to_str" => {
1417 self.emit.write("if ");
1418 self.generate_expr(&args[0]);
1419 self.emit.write(" { \"true\".to_string() } else { \"false\".to_string() }");
1420 }
1421
1422 "map" => {
1424 self.generate_expr(&args[0]);
1425 self.emit.write(".into_iter().map(");
1426 self.generate_expr(&args[1]);
1427 self.emit.write(").collect::<Vec<_>>()");
1428 }
1429 "filter" => {
1430 self.generate_expr(&args[0]);
1431 self.emit.write(".into_iter().filter(|__x| (");
1432 self.generate_expr(&args[1]);
1433 self.emit.write(")((__x).clone())).collect::<Vec<_>>()");
1434 }
1435 "reduce" => {
1436 self.generate_expr(&args[0]);
1437 self.emit.write(".into_iter().fold(");
1438 self.generate_expr(&args[1]);
1439 self.emit.write(", ");
1440 self.generate_expr(&args[2]);
1441 self.emit.write(")");
1442 }
1443 "any" => {
1444 self.generate_expr(&args[0]);
1445 self.emit.write(".into_iter().any(|__x| (");
1446 self.generate_expr(&args[1]);
1447 self.emit.write(")((__x).clone()))");
1448 }
1449 "all" => {
1450 self.generate_expr(&args[0]);
1451 self.emit.write(".into_iter().all(|__x| (");
1452 self.generate_expr(&args[1]);
1453 self.emit.write(")((__x).clone()))");
1454 }
1455 "find" => {
1456 self.generate_expr(&args[0]);
1457 self.emit.write(".into_iter().find(|__x| (");
1458 self.generate_expr(&args[1]);
1459 self.emit.write(")((__x).clone()))");
1460 }
1461 "flat_map" => {
1462 self.generate_expr(&args[0]);
1463 self.emit.write(".into_iter().flat_map(");
1464 self.generate_expr(&args[1]);
1465 self.emit.write(").collect::<Vec<_>>()");
1466 }
1467 "zip" => {
1468 self.generate_expr(&args[0]);
1469 self.emit.write(".into_iter().zip(");
1470 self.generate_expr(&args[1]);
1471 self.emit.write(".into_iter()).collect::<Vec<_>>()");
1472 }
1473 "sort_by" => {
1474 self.emit.write("{ let mut __v = ");
1475 self.generate_expr(&args[0]);
1476 self.emit.write("; __v.sort_by(|__a, __b| { let __cmp = (");
1477 self.generate_expr(&args[1]);
1478 self.emit.write(")((__a).clone(), (__b).clone()); if __cmp < 0 { std::cmp::Ordering::Less } else if __cmp > 0 { std::cmp::Ordering::Greater } else { std::cmp::Ordering::Equal } }); __v }");
1479 }
1480 "enumerate" => {
1481 self.generate_expr(&args[0]);
1482 self.emit
1483 .write(".into_iter().enumerate().map(|(__i, __x)| (__i as i64, __x)).collect::<Vec<_>>()");
1484 }
1485 "take" => {
1486 self.generate_expr(&args[0]);
1487 self.emit.write(".into_iter().take(");
1488 self.generate_expr(&args[1]);
1489 self.emit.write(" as usize).collect::<Vec<_>>()");
1490 }
1491 "drop" => {
1492 self.generate_expr(&args[0]);
1493 self.emit.write(".into_iter().skip(");
1494 self.generate_expr(&args[1]);
1495 self.emit.write(" as usize).collect::<Vec<_>>()");
1496 }
1497 "flatten" => {
1498 self.generate_expr(&args[0]);
1499 self.emit.write(".into_iter().flatten().collect::<Vec<_>>()");
1500 }
1501 "reverse" => {
1502 self.emit.write("{ let mut __v = ");
1503 self.generate_expr(&args[0]);
1504 self.emit.write("; __v.reverse(); __v }");
1505 }
1506 "unique" => {
1507 self.emit.write("{ let mut __seen = std::collections::HashSet::new(); ");
1508 self.generate_expr(&args[0]);
1509 self.emit.write(".into_iter().filter(|__x| __seen.insert(format!(\"{:?}\", __x))).collect::<Vec<_>>() }");
1510 }
1511 "count_where" => {
1512 self.generate_expr(&args[0]);
1513 self.emit.write(".into_iter().filter(|__x| (");
1514 self.generate_expr(&args[1]);
1515 self.emit.write(")((__x).clone())).count() as i64");
1516 }
1517 "sum" => {
1518 self.generate_expr(&args[0]);
1519 self.emit.write(".iter().sum::<i64>()");
1520 }
1521 "sum_floats" => {
1522 self.generate_expr(&args[0]);
1523 self.emit.write(".iter().sum::<f64>()");
1524 }
1525
1526 _ => {
1527 self.emit.write(fn_name);
1528 self.emit.write("(");
1529 for (i, arg) in args.iter().enumerate() {
1530 if i > 0 {
1531 self.emit.write(", ");
1532 }
1533 self.generate_expr(arg);
1534 }
1535 self.emit.write(")");
1536 }
1537 }
1538 }
1539
1540 Expr::SelfField { field, .. } => {
1541 self.emit.write("self.");
1542 self.emit.write(&field.name);
1543 }
1544
1545 Expr::SelfMethodCall { method, args, .. } => {
1546 self.emit.write("self.");
1547 self.emit.write(&method.name);
1548 self.emit.write("(");
1549 for (i, arg) in args.iter().enumerate() {
1550 if i > 0 {
1551 self.emit.write(", ");
1552 }
1553 self.generate_expr(arg);
1554 }
1555 self.emit.write(")");
1556 }
1557
1558 Expr::List { elements, .. } => {
1559 self.emit.write("vec![");
1560 for (i, elem) in elements.iter().enumerate() {
1561 if i > 0 {
1562 self.emit.write(", ");
1563 }
1564 self.generate_expr(elem);
1565 }
1566 self.emit.write("]");
1567 }
1568
1569 Expr::Paren { inner, .. } => {
1570 self.emit.write("(");
1571 self.generate_expr(inner);
1572 self.emit.write(")");
1573 }
1574
1575 Expr::Infer { template, .. } => {
1576 self.emit.write("ctx.infer_string(&");
1577 self.emit_string_template(template);
1578 self.emit.write(").await?");
1579 }
1580
1581 Expr::Spawn { agent, fields, .. } => {
1582 self.emit.write("sage_runtime::spawn(|ctx| ");
1583 self.emit.write(&agent.name);
1584 if fields.is_empty() {
1585 self.emit.write(".on_start(ctx))");
1586 } else {
1587 self.emit.write(" { ");
1588 for (i, field) in fields.iter().enumerate() {
1589 if i > 0 {
1590 self.emit.write(", ");
1591 }
1592 self.emit.write(&field.name.name);
1593 self.emit.write(": ");
1594 self.generate_expr(&field.value);
1595 }
1596 self.emit.write(" }.on_start(ctx))");
1597 }
1598 }
1599
1600 Expr::Await {
1601 handle, timeout, ..
1602 } => {
1603 if let Some(timeout_expr) = timeout {
1604 self.emit.write("tokio::time::timeout(");
1606 self.emit.write("std::time::Duration::from_millis(");
1607 self.generate_expr(timeout_expr);
1608 self.emit.write(" as u64), ");
1609 self.generate_expr(handle);
1610 self.emit
1611 .write(".result()).await.map_err(|_| sage_runtime::SageError::agent(");
1612 self.emit.write("\"await timed out\"))??");
1613 } else {
1614 self.generate_expr(handle);
1616 self.emit.write(".result().await?");
1617 }
1618 }
1619
1620 Expr::Send {
1621 handle, message, ..
1622 } => {
1623 self.generate_expr(handle);
1624 self.emit.write(".send(sage_runtime::Message::new(");
1625 self.generate_expr(message);
1626 self.emit.write(")?).await?");
1627 }
1628
1629 Expr::Emit { value, .. } => {
1630 self.emit.write("ctx.emit(");
1631 self.generate_expr(value);
1632 self.emit.write(")");
1633 }
1634
1635 Expr::StringInterp { template, .. } => {
1636 self.emit_string_template(template);
1637 }
1638
1639 Expr::Match {
1640 scrutinee, arms, ..
1641 } => {
1642 self.emit.write("match ");
1643 self.generate_expr(scrutinee);
1644 self.emit.writeln(" {");
1645 self.emit.indent();
1646 for arm in arms {
1647 self.emit_pattern(&arm.pattern);
1648 self.emit.write(" => ");
1649 self.generate_expr(&arm.body);
1650 self.emit.writeln(",");
1651 }
1652 self.emit.dedent();
1653 self.emit.write("}");
1654 }
1655
1656 Expr::RecordConstruct { name, fields, .. } => {
1657 self.emit.write(&name.name);
1658 self.emit.write(" { ");
1659 for (i, field) in fields.iter().enumerate() {
1660 if i > 0 {
1661 self.emit.write(", ");
1662 }
1663 self.emit.write(&field.name.name);
1664 self.emit.write(": ");
1665 self.generate_expr(&field.value);
1666 }
1667 self.emit.write(" }");
1668 }
1669
1670 Expr::FieldAccess { object, field, .. } => {
1671 self.generate_expr(object);
1672 self.emit.write(".");
1673 self.emit.write(&field.name);
1674 }
1675
1676 Expr::Receive { .. } => {
1677 self.emit.write("ctx.receive().await?");
1678 }
1679
1680 Expr::Try { expr, .. } => {
1682 self.generate_expr(expr);
1684 self.emit.write("?");
1685 }
1686
1687 Expr::Catch {
1688 expr,
1689 error_bind,
1690 recovery,
1691 ..
1692 } => {
1693 self.emit.write("match ");
1695 self.generate_expr(expr);
1696 self.emit.writeln(" {");
1697 self.emit.indent();
1698
1699 self.emit.writeln("Ok(__val) => __val,");
1701
1702 if let Some(err_name) = error_bind {
1704 self.emit.write("Err(");
1705 self.emit.write(&err_name.name);
1706 self.emit.write(") => ");
1707 } else {
1708 self.emit.write("Err(_) => ");
1709 }
1710 self.generate_expr(recovery);
1711 self.emit.writeln(",");
1712
1713 self.emit.dedent();
1714 self.emit.write("}");
1715 }
1716
1717 Expr::Fail { error, .. } => {
1719 self.emit.write("return Err(sage_runtime::SageError::agent(");
1722 self.generate_expr(error);
1723 self.emit.write("))");
1724 }
1725
1726 Expr::Retry {
1728 count,
1729 delay,
1730 on_errors: _,
1731 body,
1732 ..
1733 } => {
1734 self.emit.writeln("'_retry: {");
1736 self.emit.indent();
1737
1738 self.emit.write("let _retry_max: i64 = ");
1739 self.generate_expr(count);
1740 self.emit.writeln(";");
1741
1742 if let Some(delay_expr) = delay {
1743 self.emit.write("let _retry_delay: u64 = ");
1744 self.generate_expr(delay_expr);
1745 self.emit.writeln(" as u64;");
1746 }
1747
1748 self.emit
1749 .writeln("let mut _last_error: Option<sage_runtime::SageError> = None;");
1750 self.emit.writeln("for _attempt in 0.._retry_max {");
1751 self.emit.indent();
1752
1753 self.emit.writeln("let _result = (async {");
1755 self.emit.indent();
1756 self.emit.write("Ok::<_, sage_runtime::SageError>(");
1757 self.generate_expr(body);
1758 self.emit.writeln(")");
1759 self.emit.dedent();
1760 self.emit.writeln("}).await;");
1761
1762 self.emit.writeln("match _result {");
1763 self.emit.indent();
1764 self.emit.writeln("Ok(v) => break '_retry v,");
1765 self.emit.writeln("Err(e) => {");
1766 self.emit.indent();
1767 self.emit.writeln("_last_error = Some(e);");
1768
1769 if delay.is_some() {
1771 self.emit.writeln("if _attempt < _retry_max - 1 {");
1772 self.emit.indent();
1773 self.emit.writeln(
1774 "tokio::time::sleep(std::time::Duration::from_millis(_retry_delay)).await;",
1775 );
1776 self.emit.dedent();
1777 self.emit.writeln("}");
1778 }
1779
1780 self.emit.dedent();
1781 self.emit.writeln("}");
1782 self.emit.dedent();
1783 self.emit.writeln("}");
1784
1785 self.emit.dedent();
1786 self.emit.writeln("}");
1787
1788 self.emit.writeln("return Err(_last_error.unwrap());");
1790
1791 self.emit.dedent();
1792 self.emit.write("}");
1793 }
1794
1795 Expr::Trace { message, .. } => {
1797 self.emit.write("sage_runtime::trace::user(&");
1798 self.generate_expr(message);
1799 self.emit.write(")");
1800 }
1801
1802 Expr::Closure { params, body, .. } => {
1804 self.emit.write("Box::new(move |");
1806 for (i, param) in params.iter().enumerate() {
1807 if i > 0 {
1808 self.emit.write(", ");
1809 }
1810 self.emit.write(¶m.name.name);
1811 if let Some(ty) = ¶m.ty {
1812 self.emit.write(": ");
1813 self.emit_type(ty);
1814 }
1815 }
1816 self.emit.write("| ");
1817 self.generate_expr(body);
1818 self.emit.write(")");
1819 }
1820
1821 Expr::Tuple { elements, .. } => {
1823 self.emit.write("(");
1824 for (i, elem) in elements.iter().enumerate() {
1825 if i > 0 {
1826 self.emit.write(", ");
1827 }
1828 self.generate_expr(elem);
1829 }
1830 self.emit.write(")");
1831 }
1832
1833 Expr::TupleIndex { tuple, index, .. } => {
1834 self.generate_expr(tuple);
1835 self.emit.write(&format!(".{index}"));
1836 }
1837
1838 Expr::Map { entries, .. } => {
1839 if entries.is_empty() {
1840 self.emit.write("std::collections::HashMap::new()");
1841 } else {
1842 self.emit.write("std::collections::HashMap::from([");
1843 for (i, entry) in entries.iter().enumerate() {
1844 if i > 0 {
1845 self.emit.write(", ");
1846 }
1847 self.emit.write("(");
1848 self.generate_expr(&entry.key);
1849 self.emit.write(", ");
1850 self.generate_expr(&entry.value);
1851 self.emit.write(")");
1852 }
1853 self.emit.write("])");
1854 }
1855 }
1856
1857 Expr::VariantConstruct {
1858 enum_name,
1859 variant,
1860 payload,
1861 ..
1862 } => {
1863 self.emit.write(&enum_name.name);
1864 self.emit.write("::");
1865 self.emit.write(&variant.name);
1866 if let Some(payload_expr) = payload {
1867 self.emit.write("(");
1868 self.generate_expr(payload_expr);
1869 self.emit.write(")");
1870 }
1871 }
1872
1873 Expr::ToolCall {
1875 tool,
1876 function,
1877 args,
1878 ..
1879 } => {
1880 self.emit.write("self.");
1883 self.emit.write(&tool.name.to_lowercase());
1884 self.emit.write(".");
1885 self.emit.write(&function.name);
1886 self.emit.write("(");
1887 for (i, arg) in args.iter().enumerate() {
1888 if i > 0 {
1889 self.emit.write(", ");
1890 }
1891 self.generate_expr(arg);
1892 }
1893 self.emit.write(").await");
1894 }
1895 }
1896 }
1897
1898 fn emit_pattern(&mut self, pattern: &sage_parser::Pattern) {
1899 use sage_parser::Pattern;
1900 match pattern {
1901 Pattern::Wildcard { .. } => {
1902 self.emit.write("_");
1903 }
1904 Pattern::Variant {
1905 enum_name,
1906 variant,
1907 payload,
1908 ..
1909 } => {
1910 if let Some(enum_name) = enum_name {
1911 self.emit.write(&enum_name.name);
1912 self.emit.write("::");
1913 }
1914 self.emit.write(&variant.name);
1915 if let Some(inner_pattern) = payload {
1916 self.emit.write("(");
1917 self.emit_pattern(inner_pattern);
1918 self.emit.write(")");
1919 }
1920 }
1921 Pattern::Literal { value, .. } => {
1922 self.emit_literal(value);
1923 }
1924 Pattern::Binding { name, .. } => {
1925 self.emit.write(&name.name);
1926 }
1927 Pattern::Tuple { elements, .. } => {
1928 self.emit.write("(");
1929 for (i, elem) in elements.iter().enumerate() {
1930 if i > 0 {
1931 self.emit.write(", ");
1932 }
1933 self.emit_pattern(elem);
1934 }
1935 self.emit.write(")");
1936 }
1937 }
1938 }
1939
1940 fn emit_literal(&mut self, lit: &Literal) {
1941 match lit {
1942 Literal::Int(n) => {
1943 self.emit.write(&format!("{n}_i64"));
1944 }
1945 Literal::Float(f) => {
1946 self.emit.write(&format!("{f}_f64"));
1947 }
1948 Literal::Bool(b) => {
1949 self.emit.write(if *b { "true" } else { "false" });
1950 }
1951 Literal::String(s) => {
1952 self.emit.write("\"");
1954 for c in s.chars() {
1955 match c {
1956 '"' => self.emit.write_raw("\\\""),
1957 '\\' => self.emit.write_raw("\\\\"),
1958 '\n' => self.emit.write_raw("\\n"),
1959 '\r' => self.emit.write_raw("\\r"),
1960 '\t' => self.emit.write_raw("\\t"),
1961 _ => self.emit.write_raw(&c.to_string()),
1962 }
1963 }
1964 self.emit.write("\".to_string()");
1965 }
1966 }
1967 }
1968
1969 fn emit_string_template(&mut self, template: &sage_parser::StringTemplate) {
1970 if !template.has_interpolations() {
1971 if let Some(StringPart::Literal(s)) = template.parts.first() {
1973 self.emit.write("\"");
1974 self.emit.write_raw(s);
1975 self.emit.write("\".to_string()");
1976 }
1977 return;
1978 }
1979
1980 self.emit.write("format!(\"");
1982 for part in &template.parts {
1983 match part {
1984 StringPart::Literal(s) => {
1985 let escaped = s.replace('{', "{{").replace('}', "}}");
1987 self.emit.write_raw(&escaped);
1988 }
1989 StringPart::Interpolation(_) => {
1990 self.emit.write_raw("{}");
1991 }
1992 }
1993 }
1994 self.emit.write("\"");
1995
1996 for part in &template.parts {
1998 if let StringPart::Interpolation(interp_expr) = part {
1999 self.emit.write(", ");
2000 self.emit_interp_expr(interp_expr);
2001 }
2002 }
2003 self.emit.write(")");
2004 }
2005
2006 fn emit_interp_expr(&mut self, expr: &InterpExpr) {
2008 match expr {
2009 InterpExpr::Ident(ident) => {
2010 self.emit.write(&ident.name);
2011 }
2012 InterpExpr::FieldAccess { base, field, .. } => {
2013 self.emit_interp_expr(base);
2014 self.emit.write(".");
2015 self.emit.write(&field.name);
2016 }
2017 InterpExpr::TupleIndex { base, index, .. } => {
2018 self.emit_interp_expr(base);
2019 self.emit.write(".");
2020 self.emit.write(&index.to_string());
2021 }
2022 }
2023 }
2024
2025 fn emit_type(&mut self, ty: &TypeExpr) {
2026 match ty {
2027 TypeExpr::Int => self.emit.write("i64"),
2028 TypeExpr::Float => self.emit.write("f64"),
2029 TypeExpr::Bool => self.emit.write("bool"),
2030 TypeExpr::String => self.emit.write("String"),
2031 TypeExpr::Unit => self.emit.write("()"),
2032 TypeExpr::List(inner) => {
2033 self.emit.write("Vec<");
2034 self.emit_type(inner);
2035 self.emit.write(">");
2036 }
2037 TypeExpr::Option(inner) => {
2038 self.emit.write("Option<");
2039 self.emit_type(inner);
2040 self.emit.write(">");
2041 }
2042 TypeExpr::Inferred(inner) => {
2043 self.emit_type(inner);
2045 }
2046 TypeExpr::Agent(agent_name) => {
2047 self.emit.write("AgentHandle<");
2050 self.emit.write(&agent_name.name);
2051 self.emit.write("Output>");
2052 }
2053 TypeExpr::Named(name) => {
2054 self.emit.write(&name.name);
2055 }
2056
2057 TypeExpr::Error => {
2059 self.emit.write("sage_runtime::SageError");
2060 }
2061
2062 TypeExpr::Fn(params, ret) => {
2064 self.emit.write("Box<dyn Fn(");
2065 for (i, param) in params.iter().enumerate() {
2066 if i > 0 {
2067 self.emit.write(", ");
2068 }
2069 self.emit_type(param);
2070 }
2071 self.emit.write(") -> ");
2072 self.emit_type(ret);
2073 self.emit.write(" + Send + 'static>");
2074 }
2075
2076 TypeExpr::Map(key, value) => {
2078 self.emit.write("std::collections::HashMap<");
2079 self.emit_type(key);
2080 self.emit.write(", ");
2081 self.emit_type(value);
2082 self.emit.write(">");
2083 }
2084 TypeExpr::Tuple(elems) => {
2085 self.emit.write("(");
2086 for (i, elem) in elems.iter().enumerate() {
2087 if i > 0 {
2088 self.emit.write(", ");
2089 }
2090 self.emit_type(elem);
2091 }
2092 self.emit.write(")");
2093 }
2094 TypeExpr::Result(ok, err) => {
2095 self.emit.write("Result<");
2096 self.emit_type(ok);
2097 self.emit.write(", ");
2098 self.emit_type(err);
2099 self.emit.write(">");
2100 }
2101 }
2102 }
2103
2104 fn emit_binop(&mut self, op: &BinOp) {
2105 let s = match op {
2106 BinOp::Add => "+",
2107 BinOp::Sub => "-",
2108 BinOp::Mul => "*",
2109 BinOp::Div => "/",
2110 BinOp::Rem => "%",
2111 BinOp::Eq => "==",
2112 BinOp::Ne => "!=",
2113 BinOp::Lt => "<",
2114 BinOp::Gt => ">",
2115 BinOp::Le => "<=",
2116 BinOp::Ge => ">=",
2117 BinOp::And => "&&",
2118 BinOp::Or => "||",
2119 BinOp::Concat => "++", };
2121 self.emit.write(s);
2122 }
2123
2124 fn emit_unaryop(&mut self, op: &UnaryOp) {
2125 let s = match op {
2126 UnaryOp::Neg => "-",
2127 UnaryOp::Not => "!",
2128 };
2129 self.emit.write(s);
2130 }
2131
2132 fn infer_agent_output_type(&self, agent: &AgentDecl) -> String {
2133 for handler in &agent.handlers {
2136 if let EventKind::Start = &handler.event {
2137 if let Some(ty) = self.find_emit_type(&handler.body) {
2138 return ty;
2139 }
2140 }
2141 }
2142 "i64".to_string()
2143 }
2144
2145 fn find_emit_type(&self, block: &Block) -> Option<String> {
2146 for stmt in &block.stmts {
2147 if let Stmt::Expr { expr, .. } = stmt {
2148 if let Expr::Emit { value, .. } = expr {
2149 return Some(self.infer_expr_type(value));
2150 }
2151 }
2152 if let Stmt::If {
2154 then_block,
2155 else_block,
2156 ..
2157 } = stmt
2158 {
2159 if let Some(ty) = self.find_emit_type(then_block) {
2160 return Some(ty);
2161 }
2162 if let Some(else_branch) = else_block {
2163 if let sage_parser::ElseBranch::Block(block) = else_branch {
2164 if let Some(ty) = self.find_emit_type(block) {
2165 return Some(ty);
2166 }
2167 }
2168 }
2169 }
2170 }
2171 None
2172 }
2173
2174 fn infer_expr_type(&self, expr: &Expr) -> String {
2175 match expr {
2176 Expr::Literal { value, .. } => match value {
2177 Literal::Int(_) => "i64".to_string(),
2178 Literal::Float(_) => "f64".to_string(),
2179 Literal::Bool(_) => "bool".to_string(),
2180 Literal::String(_) => "String".to_string(),
2181 },
2182 Expr::Var { .. } => "i64".to_string(), Expr::Binary { op, .. } => {
2184 if matches!(
2185 op,
2186 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Gt | BinOp::Le | BinOp::Ge
2187 ) {
2188 "bool".to_string()
2189 } else if matches!(op, BinOp::Concat) {
2190 "String".to_string()
2191 } else {
2192 "i64".to_string()
2193 }
2194 }
2195 Expr::Infer { .. } | Expr::StringInterp { .. } => "String".to_string(),
2196 Expr::Call { name, .. } if name.name == "str" => "String".to_string(),
2197 Expr::Call { name, .. } if name.name == "len" => "i64".to_string(),
2198 _ => "i64".to_string(),
2199 }
2200 }
2201}
2202
2203#[cfg(test)]
2204mod tests {
2205 use super::*;
2206 use sage_parser::{lex, parse};
2207 use std::sync::Arc;
2208
2209 fn generate_source(source: &str) -> String {
2210 let lex_result = lex(source).expect("lexing failed");
2211 let source_arc: Arc<str> = Arc::from(source);
2212 let (program, errors) = parse(lex_result.tokens(), source_arc);
2213 assert!(errors.is_empty(), "parse errors: {errors:?}");
2214 let program = program.expect("should parse");
2215 generate(&program, "test").main_rs
2216 }
2217
2218 #[test]
2219 fn generate_minimal_program() {
2220 let source = r#"
2221 agent Main {
2222 on start {
2223 emit(42);
2224 }
2225 }
2226 run Main;
2227 "#;
2228
2229 let output = generate_source(source);
2230 assert!(output.contains("struct Main;"));
2231 assert!(output.contains("async fn on_start"));
2232 assert!(output.contains("ctx.emit(42_i64)"));
2233 assert!(output.contains("#[tokio::main]"));
2234 }
2235
2236 #[test]
2237 fn generate_function() {
2238 let source = r#"
2239 fn add(a: Int, b: Int) -> Int {
2240 return a + b;
2241 }
2242 agent Main {
2243 on start {
2244 emit(add(1, 2));
2245 }
2246 }
2247 run Main;
2248 "#;
2249
2250 let output = generate_source(source);
2251 assert!(output.contains("fn add(a: i64, b: i64) -> i64"));
2252 assert!(output.contains("return (a + b);"));
2253 }
2254
2255 #[test]
2256 fn generate_agent_with_beliefs() {
2257 let source = r#"
2258 agent Worker {
2259 value: Int
2260
2261 on start {
2262 emit(self.value * 2);
2263 }
2264 }
2265 agent Main {
2266 on start {
2267 emit(0);
2268 }
2269 }
2270 run Main;
2271 "#;
2272
2273 let output = generate_source(source);
2274 assert!(output.contains("struct Worker {"));
2275 assert!(output.contains("value: i64,"));
2276 assert!(output.contains("self.value"));
2277 }
2278
2279 #[test]
2280 fn generate_string_interpolation() {
2281 let source = r#"
2282 agent Main {
2283 on start {
2284 let name = "World";
2285 let msg = "Hello, {name}!";
2286 print(msg);
2287 emit(0);
2288 }
2289 }
2290 run Main;
2291 "#;
2292
2293 let output = generate_source(source);
2294 assert!(output.contains("format!(\"Hello, {}!\", name)"));
2295 }
2296
2297 #[test]
2298 fn generate_control_flow() {
2299 let source = r#"
2300 agent Main {
2301 on start {
2302 let x = 10;
2303 if x > 5 {
2304 emit(1);
2305 } else {
2306 emit(0);
2307 }
2308 }
2309 }
2310 run Main;
2311 "#;
2312
2313 let output = generate_source(source);
2314 assert!(output.contains("if (x > 5_i64)"), "output:\n{output}");
2315 assert!(output.contains("else"), "output:\n{output}");
2317 }
2318
2319 #[test]
2320 fn generate_loops() {
2321 let source = r#"
2322 agent Main {
2323 on start {
2324 for x in [1, 2, 3] {
2325 print(str(x));
2326 }
2327 let n = 0;
2328 while n < 5 {
2329 n = n + 1;
2330 }
2331 emit(n);
2332 }
2333 }
2334 run Main;
2335 "#;
2336
2337 let output = generate_source(source);
2338 assert!(output.contains("for x in vec![1_i64, 2_i64, 3_i64]"));
2339 assert!(output.contains("while (n < 5_i64)"));
2340 }
2341
2342 #[test]
2343 fn generate_pub_function() {
2344 let source = r#"
2345 pub fn helper(x: Int) -> Int {
2346 return x * 2;
2347 }
2348 agent Main {
2349 on start {
2350 emit(helper(21));
2351 }
2352 }
2353 run Main;
2354 "#;
2355
2356 let output = generate_source(source);
2357 assert!(output.contains("pub fn helper(x: i64) -> i64"));
2358 }
2359
2360 #[test]
2361 fn generate_pub_agent() {
2362 let source = r#"
2363 pub agent Worker {
2364 on start {
2365 emit(42);
2366 }
2367 }
2368 agent Main {
2369 on start {
2370 emit(0);
2371 }
2372 }
2373 run Main;
2374 "#;
2375
2376 let output = generate_source(source);
2377 assert!(output.contains("pub struct Worker;"));
2378 }
2379
2380 #[test]
2381 fn generate_module_tree_simple() {
2382 use sage_loader::load_single_file;
2383 use std::fs;
2384 use tempfile::TempDir;
2385
2386 let dir = TempDir::new().unwrap();
2387 let file = dir.path().join("test.sg");
2388 fs::write(
2389 &file,
2390 r#"
2391agent Main {
2392 on start {
2393 emit(42);
2394 }
2395}
2396run Main;
2397"#,
2398 )
2399 .unwrap();
2400
2401 let tree = load_single_file(&file).unwrap();
2402 let project = generate_module_tree(&tree, "test");
2403
2404 assert!(project.main_rs.contains("struct Main;"));
2405 assert!(project.main_rs.contains("async fn on_start"));
2406 assert!(project.main_rs.contains("#[tokio::main]"));
2407 }
2408
2409 #[test]
2410 fn generate_record_declaration() {
2411 let source = r#"
2412 record Point {
2413 x: Int,
2414 y: Int,
2415 }
2416 agent Main {
2417 on start {
2418 let p = Point { x: 10, y: 20 };
2419 emit(p.x);
2420 }
2421 }
2422 run Main;
2423 "#;
2424
2425 let output = generate_source(source);
2426 assert!(output.contains("#[derive(Debug, Clone)]"));
2427 assert!(output.contains("struct Point {"));
2428 assert!(output.contains("x: i64,"));
2429 assert!(output.contains("y: i64,"));
2430 assert!(output.contains("Point { x: 10_i64, y: 20_i64 }"));
2431 assert!(output.contains("p.x"));
2432 }
2433
2434 #[test]
2435 fn generate_enum_declaration() {
2436 let source = r#"
2437 enum Status {
2438 Active,
2439 Inactive,
2440 Pending,
2441 }
2442 agent Main {
2443 on start {
2444 emit(0);
2445 }
2446 }
2447 run Main;
2448 "#;
2449
2450 let output = generate_source(source);
2451 assert!(output.contains("#[derive(Debug, Clone, Copy, PartialEq, Eq)]"));
2452 assert!(output.contains("enum Status {"));
2453 assert!(output.contains("Active,"));
2454 assert!(output.contains("Inactive,"));
2455 assert!(output.contains("Pending,"));
2456 }
2457
2458 #[test]
2459 fn generate_const_declaration() {
2460 let source = r#"
2461 const MAX_SIZE: Int = 100;
2462 const GREETING: String = "Hello";
2463 agent Main {
2464 on start {
2465 emit(MAX_SIZE);
2466 }
2467 }
2468 run Main;
2469 "#;
2470
2471 let output = generate_source(source);
2472 assert!(output.contains("const MAX_SIZE: i64 = 100_i64;"));
2473 assert!(output.contains("const GREETING: String = \"Hello\".to_string();"));
2474 }
2475
2476 #[test]
2477 fn generate_match_expression() {
2478 let source = r#"
2479 enum Status {
2480 Active,
2481 Inactive,
2482 }
2483 fn check_status(s: Status) -> Int {
2484 return match s {
2485 Active => 1,
2486 Inactive => 0,
2487 };
2488 }
2489 agent Main {
2490 on start {
2491 emit(0);
2492 }
2493 }
2494 run Main;
2495 "#;
2496
2497 let output = generate_source(source);
2498 assert!(output.contains("match s {"));
2499 assert!(output.contains("Active => 1_i64,"));
2500 assert!(output.contains("Inactive => 0_i64,"));
2501 }
2502
2503 #[test]
2508 fn generate_fallible_function() {
2509 let source = r#"
2510 fn get_data(url: String) -> String fails {
2511 return url;
2512 }
2513 agent Main {
2514 on start { emit(0); }
2515 }
2516 run Main;
2517 "#;
2518
2519 let output = generate_source(source);
2520 assert!(output.contains("fn get_data(url: String) -> SageResult<String>"));
2522 }
2523
2524 #[test]
2525 fn generate_try_expression() {
2526 let source = r#"
2527 fn fallible() -> Int fails { return 42; }
2528 fn caller() -> Int fails {
2529 let x = try fallible();
2530 return x;
2531 }
2532 agent Main {
2533 on start { emit(0); }
2534 }
2535 run Main;
2536 "#;
2537
2538 let output = generate_source(source);
2539 assert!(output.contains("fallible()?"));
2541 }
2542
2543 #[test]
2544 fn generate_catch_expression() {
2545 let source = r#"
2546 fn fallible() -> Int fails { return 42; }
2547 agent Main {
2548 on start {
2549 let x = fallible() catch { 0 };
2550 emit(x);
2551 }
2552 }
2553 run Main;
2554 "#;
2555
2556 let output = generate_source(source);
2557 assert!(output.contains("match fallible()"));
2559 assert!(output.contains("Ok(__val) => __val"));
2560 assert!(output.contains("Err(_) => 0_i64"));
2561 }
2562
2563 #[test]
2564 fn generate_catch_with_binding() {
2565 let source = r#"
2566 fn fallible() -> Int fails { return 42; }
2567 agent Main {
2568 on start {
2569 let x = fallible() catch(e) { 0 };
2570 emit(x);
2571 }
2572 }
2573 run Main;
2574 "#;
2575
2576 let output = generate_source(source);
2577 assert!(output.contains("Err(e) => 0_i64"));
2579 }
2580
2581 #[test]
2582 fn generate_on_error_handler() {
2583 let source = r#"
2584 agent Main {
2585 on start {
2586 emit(0);
2587 }
2588 on error(e) {
2589 emit(1);
2590 }
2591 }
2592 run Main;
2593 "#;
2594
2595 let output = generate_source(source);
2596 assert!(output.contains("async fn on_error(&self, e: SageError, ctx: &mut AgentContext"));
2598 assert!(output.contains(".on_error(e, &mut ctx)"));
2600 }
2601
2602 #[test]
2607 fn generate_agent_with_tool_use() {
2608 let source = r#"
2609 agent Fetcher {
2610 use Http
2611
2612 on start {
2613 let r = Http.get("https://example.com");
2614 emit(0);
2615 }
2616 }
2617 run Fetcher;
2618 "#;
2619
2620 let output = generate_source(source);
2621 assert!(output.contains("struct Fetcher {"));
2623 assert!(output.contains("http: HttpClient,"));
2624 assert!(output.contains("http: HttpClient::from_env()"));
2626 assert!(output.contains("self.http.get("));
2628 }
2629
2630 #[test]
2631 fn generate_tool_call_expression() {
2632 let source = r#"
2633 agent Fetcher {
2634 use Http
2635
2636 on start {
2637 let response = Http.get("https://httpbin.org/get");
2638 emit(0);
2639 }
2640 }
2641 run Fetcher;
2642 "#;
2643
2644 let output = generate_source(source);
2645 assert!(output.contains("self.http.get(\"https://httpbin.org/get\".to_string()).await"));
2647 }
2648}