1use crate::emit::Emitter;
4use sage_loader::{ModuleTree, SupervisionConfig};
5use sage_parser::{
6 AgentDecl, BinOp, Block, ConstDecl, EffectHandlerDecl, EnumDecl, EventKind, Expr, ExternFnDecl,
7 FnDecl, Ident, Literal, MockValue, Program, ProtocolDecl, RecordDecl, RestartPolicy, Stmt,
8 StringPart, SupervisionStrategy, SupervisorDecl, TestDecl, TypeExpr, UnaryOp,
9};
10
11#[derive(Debug, Clone)]
13pub enum RuntimeDep {
14 CratesIo { version: String },
16 Path { path: String },
18}
19
20impl Default for RuntimeDep {
21 fn default() -> Self {
22 Self::CratesIo {
24 version: env!("CARGO_PKG_VERSION").to_string(),
25 }
26 }
27}
28
29impl RuntimeDep {
30 fn to_cargo_dep(&self, feature: Option<&str>) -> String {
32 match self {
33 RuntimeDep::CratesIo { version } => {
34 if let Some(feat) = feature {
35 format!("sage-runtime = {{ version = \"{version}\", features = [\"{feat}\"] }}")
36 } else {
37 format!("sage-runtime = \"{version}\"")
38 }
39 }
40 RuntimeDep::Path { path } => {
41 if let Some(feat) = feature {
42 format!("sage-runtime = {{ path = \"{path}\", features = [\"{feat}\"] }}")
43 } else {
44 format!("sage-runtime = {{ path = \"{path}\" }}")
45 }
46 }
47 }
48 }
49}
50
51#[derive(Debug, Clone, Default)]
53pub enum PersistenceBackend {
54 #[default]
56 Memory,
57 Sqlite { path: String },
59 Postgres { url: String },
61 File { path: String },
63}
64
65impl PersistenceBackend {
66 fn feature_flag(&self) -> Option<&'static str> {
68 match self {
69 PersistenceBackend::Memory => None,
70 PersistenceBackend::Sqlite { .. } => Some("persistence-sqlite"),
71 PersistenceBackend::Postgres { .. } => Some("persistence-postgres"),
72 PersistenceBackend::File { .. } => Some("persistence-file"),
73 }
74 }
75}
76
77#[derive(Debug, Clone, Default)]
79pub struct ObservabilityConfig {
80 pub backend: String,
82 pub otlp_endpoint: Option<String>,
84 pub service_name: String,
86}
87
88#[derive(Debug, Clone, Default, PartialEq, Eq)]
90pub enum CodegenTarget {
91 #[default]
93 Native,
94 Wasm,
96}
97
98#[derive(Debug, Clone, Default)]
100pub struct CodegenConfig {
101 pub runtime_dep: RuntimeDep,
103 pub persistence: PersistenceBackend,
105 pub supervision: SupervisionConfig,
107 pub observability: ObservabilityConfig,
109 pub target: CodegenTarget,
111}
112
113pub struct GeneratedProject {
115 pub main_rs: String,
117 pub cargo_toml: String,
119}
120
121pub fn generate(program: &Program, project_name: &str) -> GeneratedProject {
123 generate_with_config(program, project_name, RuntimeDep::default())
124}
125
126pub fn generate_with_config(
128 program: &Program,
129 project_name: &str,
130 runtime_dep: RuntimeDep,
131) -> GeneratedProject {
132 generate_with_full_config(
133 program,
134 project_name,
135 CodegenConfig {
136 runtime_dep,
137 persistence: PersistenceBackend::Memory,
138 supervision: SupervisionConfig::default(),
139 observability: ObservabilityConfig::default(),
140 ..Default::default()
141 },
142 )
143}
144
145pub fn generate_with_full_config(
147 program: &Program,
148 project_name: &str,
149 config: CodegenConfig,
150) -> GeneratedProject {
151 let mut gen = Generator::new(config);
152 let main_rs = gen.generate_program(program);
153 let needs_persistence = Generator::has_persistent_fields(program);
154 let cargo_toml = gen.generate_cargo_toml_with_persistence(project_name, needs_persistence);
155 GeneratedProject {
156 main_rs,
157 cargo_toml,
158 }
159}
160
161pub fn generate_module_tree(tree: &ModuleTree, project_name: &str) -> GeneratedProject {
166 generate_module_tree_with_config(tree, project_name, RuntimeDep::default())
167}
168
169pub fn generate_module_tree_with_config(
171 tree: &ModuleTree,
172 project_name: &str,
173 runtime_dep: RuntimeDep,
174) -> GeneratedProject {
175 generate_module_tree_with_full_config(
176 tree,
177 project_name,
178 CodegenConfig {
179 runtime_dep,
180 persistence: PersistenceBackend::Memory,
181 supervision: SupervisionConfig::default(),
182 observability: ObservabilityConfig::default(),
183 ..Default::default()
184 },
185 )
186}
187
188pub fn generate_module_tree_with_full_config(
190 tree: &ModuleTree,
191 project_name: &str,
192 config: CodegenConfig,
193) -> GeneratedProject {
194 let mut gen = Generator::new(config);
195 let main_rs = gen.generate_module_tree(tree);
196 let needs_persistence = Generator::has_persistent_fields_in_tree(tree);
197 let cargo_toml = gen.generate_cargo_toml_with_persistence(project_name, needs_persistence);
198 GeneratedProject {
199 main_rs,
200 cargo_toml,
201 }
202}
203
204pub struct GeneratedTestProject {
206 pub main_rs: String,
208 pub cargo_toml: String,
210}
211
212pub fn generate_test_program(program: &Program, test_name: &str) -> GeneratedTestProject {
214 generate_test_program_with_config(program, test_name, RuntimeDep::default())
215}
216
217pub fn generate_test_program_with_config(
219 program: &Program,
220 test_name: &str,
221 runtime_dep: RuntimeDep,
222) -> GeneratedTestProject {
223 let mut gen = Generator::with_runtime_dep(runtime_dep);
224 let main_rs = gen.generate_test_binary(program);
225 let cargo_toml = gen.generate_test_cargo_toml(test_name);
226 GeneratedTestProject {
227 main_rs,
228 cargo_toml,
229 }
230}
231
232#[derive(Clone)]
234struct AgentMessageHandlers {
235 handlers: Vec<(Ident, TypeExpr)>,
237}
238
239struct Generator {
240 emit: Emitter,
241 config: CodegenConfig,
242 reassigned_vars: std::collections::HashSet<String>,
244 agents_with_error_handlers: std::collections::HashSet<String>,
246 agents_with_message_handlers: std::collections::HashMap<String, AgentMessageHandlers>,
248 current_protocol_roles: std::collections::HashMap<String, String>,
250 current_agent_persistent_beliefs: Vec<String>,
252 agent_tool_uses: std::collections::HashMap<String, Vec<String>>,
254 extern_fn_names: std::collections::HashSet<String>,
256 extern_fn_fallible: std::collections::HashSet<String>,
258 string_consts: std::collections::HashSet<String>,
260}
261
262impl Generator {
263 fn new(config: CodegenConfig) -> Self {
264 Self {
265 emit: Emitter::new(),
266 config,
267 reassigned_vars: std::collections::HashSet::new(),
268 agents_with_error_handlers: std::collections::HashSet::new(),
269 agents_with_message_handlers: std::collections::HashMap::new(),
270 current_protocol_roles: std::collections::HashMap::new(),
271 current_agent_persistent_beliefs: Vec::new(),
272 agent_tool_uses: std::collections::HashMap::new(),
273 extern_fn_names: std::collections::HashSet::new(),
274 extern_fn_fallible: std::collections::HashSet::new(),
275 string_consts: std::collections::HashSet::new(),
276 }
277 }
278
279 fn with_runtime_dep(runtime_dep: RuntimeDep) -> Self {
281 Self::new(CodegenConfig {
282 runtime_dep,
283 persistence: PersistenceBackend::Memory,
284 supervision: SupervisionConfig::default(),
285 observability: ObservabilityConfig::default(),
286 ..Default::default()
287 })
288 }
289
290 fn collect_extern_fns(&mut self, extern_fns: &[ExternFnDecl]) {
292 for ext_fn in extern_fns {
293 self.extern_fn_names.insert(ext_fn.name.name.clone());
294 if ext_fn.is_fallible {
295 self.extern_fn_fallible.insert(ext_fn.name.name.clone());
296 }
297 }
298 }
299
300 fn emit_checkpoint_store_init(&mut self) {
302 match &self.config.persistence {
303 PersistenceBackend::Memory => {
304 self.emit
305 .writeln("sage_runtime::persistence::MemoryCheckpointStore::new()");
306 }
307 PersistenceBackend::Sqlite { path } => {
308 self.emit
309 .write("sage_runtime::persistence::SyncSqliteStore::open(\"");
310 self.emit.write(path);
311 self.emit
312 .writeln("\").expect(\"Failed to open checkpoint database\")");
313 }
314 PersistenceBackend::Postgres { url } => {
315 self.emit
316 .write("sage_runtime::persistence::SyncPostgresStore::connect(\"");
317 self.emit.write(url);
318 self.emit
319 .writeln("\").expect(\"Failed to connect to checkpoint database\")");
320 }
321 PersistenceBackend::File { path } => {
322 self.emit
323 .write("sage_runtime::persistence::SyncFileStore::open(\"");
324 self.emit.write(path);
325 self.emit
326 .writeln("\").expect(\"Failed to open checkpoint directory\")");
327 }
328 }
329 }
330
331 fn emit_tracing_init(&mut self) {
333 let obs = &self.config.observability;
334 if obs.backend.is_empty() || obs.backend == "ndjson" {
335 self.emit.writeln("sage_runtime::trace::init();");
337 } else {
338 self.emit.writeln(
340 "sage_runtime::trace::init_with_config(sage_runtime::trace::TracingConfig {",
341 );
342 self.emit.indent();
343 self.emit.write("backend: \"");
344 self.emit.write(&obs.backend);
345 self.emit.writeln("\".to_string(),");
346 if let Some(endpoint) = &obs.otlp_endpoint {
347 self.emit.write("otlp_endpoint: Some(\"");
348 self.emit.write(endpoint);
349 self.emit.writeln("\".to_string()),");
350 } else {
351 self.emit.writeln("otlp_endpoint: None,");
352 }
353 self.emit.write("service_name: \"");
354 self.emit.write(if obs.service_name.is_empty() {
355 "sage-agent"
356 } else {
357 &obs.service_name
358 });
359 self.emit.writeln("\".to_string(),");
360 self.emit.dedent();
361 self.emit.writeln("});");
362 }
363 }
364
365 fn collect_reassigned_vars(&mut self, block: &Block) {
367 for stmt in &block.stmts {
368 self.collect_reassigned_vars_stmt(stmt);
369 }
370 }
371
372 fn collect_reassigned_vars_stmt(&mut self, stmt: &Stmt) {
373 match stmt {
374 Stmt::Assign { name, .. } => {
375 self.reassigned_vars.insert(name.name.clone());
376 }
377 Stmt::If {
378 then_block,
379 else_block,
380 ..
381 } => {
382 self.collect_reassigned_vars(then_block);
383 if let Some(else_branch) = else_block {
384 match else_branch {
385 sage_parser::ElseBranch::Block(block) => {
386 self.collect_reassigned_vars(block)
387 }
388 sage_parser::ElseBranch::ElseIf(stmt) => {
389 self.collect_reassigned_vars_stmt(stmt)
390 }
391 }
392 }
393 }
394 Stmt::While { body, .. } | Stmt::Loop { body, .. } => {
395 self.collect_reassigned_vars(body);
396 }
397 Stmt::For { body, .. } => {
398 self.collect_reassigned_vars(body);
399 }
400 _ => {}
401 }
402 }
403
404 fn generate_program(&mut self, program: &Program) -> String {
405 self.collect_extern_fns(&program.extern_fns);
407
408 self.emit
410 .writeln("//! Generated by Sage compiler. Do not edit.");
411 self.emit.blank_line();
412 self.emit.writeln("use sage_runtime::prelude::*;");
413 if self.config.target == CodegenTarget::Wasm {
414 self.emit.writeln("use wasm_bindgen::prelude::*;");
415 }
416 self.emit.blank_line();
417
418 if !program.extern_fns.is_empty() {
420 self.emit.writeln("mod sage_extern;");
421 self.emit.blank_line();
422 }
423
424 for const_decl in &program.consts {
426 self.generate_const(const_decl);
427 self.emit.blank_line();
428 }
429
430 for enum_decl in &program.enums {
432 self.generate_enum(enum_decl);
433 self.emit.blank_line();
434 }
435
436 for record in &program.records {
438 self.generate_record(record);
439 self.emit.blank_line();
440 }
441
442 for func in &program.functions {
444 self.generate_function(func);
445 self.emit.blank_line();
446 }
447
448 for protocol in &program.protocols {
450 self.generate_protocol(protocol);
451 self.emit.blank_line();
452 }
453
454 for handler in &program.effect_handlers {
456 self.generate_effect_handler(handler);
457 self.emit.blank_line();
458 }
459
460 for agent in &program.agents {
462 self.collect_agent_metadata(agent);
463 }
464
465 for agent in &program.agents {
467 self.generate_agent(agent);
468 self.emit.blank_line();
469 }
470
471 for supervisor in &program.supervisors {
473 self.generate_supervisor(supervisor, program);
474 self.emit.blank_line();
475 }
476
477 if let Some(run_entry) = &program.run_agent {
479 if let Some(agent) = program
481 .agents
482 .iter()
483 .find(|a| a.name.name == run_entry.name)
484 {
485 self.generate_main(agent);
486 } else if let Some(supervisor) = program
487 .supervisors
488 .iter()
489 .find(|s| s.name.name == run_entry.name)
490 {
491 self.generate_supervisor_main(supervisor, program);
493 }
494 }
495
496 std::mem::take(&mut self.emit).finish()
497 }
498
499 fn generate_module_tree(&mut self, tree: &ModuleTree) -> String {
500 let mut has_extern_fns = false;
502 for module in tree.modules.values() {
503 for agent in &module.program.agents {
504 self.collect_agent_metadata(agent);
505 }
506 if !module.program.extern_fns.is_empty() {
507 has_extern_fns = true;
508 self.collect_extern_fns(&module.program.extern_fns);
509 }
510 }
511
512 self.emit
514 .writeln("//! Generated by Sage compiler. Do not edit.");
515 self.emit.blank_line();
516 self.emit.writeln("use sage_runtime::prelude::*;");
517 if self.config.target == CodegenTarget::Wasm {
518 self.emit.writeln("use wasm_bindgen::prelude::*;");
519 }
520 self.emit.blank_line();
521
522 if has_extern_fns {
524 self.emit.writeln("mod sage_extern;");
525 self.emit.blank_line();
526 }
527
528 for (path, module) in &tree.modules {
534 if path != &tree.root {
535 self.emit.write("// Module: ");
536 if path.is_empty() {
537 self.emit.writeln("(root)");
538 } else {
539 self.emit.writeln(&path.join("::"));
540 }
541
542 for const_decl in &module.program.consts {
543 self.generate_const(const_decl);
544 self.emit.blank_line();
545 }
546
547 for enum_decl in &module.program.enums {
548 self.generate_enum(enum_decl);
549 self.emit.blank_line();
550 }
551
552 for record in &module.program.records {
553 self.generate_record(record);
554 self.emit.blank_line();
555 }
556
557 for func in &module.program.functions {
558 self.generate_function(func);
559 self.emit.blank_line();
560 }
561
562 for protocol in &module.program.protocols {
564 self.generate_protocol(protocol);
565 self.emit.blank_line();
566 }
567
568 for handler in &module.program.effect_handlers {
570 self.generate_effect_handler(handler);
571 self.emit.blank_line();
572 }
573
574 for agent in &module.program.agents {
575 self.generate_agent(agent);
576 self.emit.blank_line();
577 }
578
579 for supervisor in &module.program.supervisors {
580 self.generate_supervisor(supervisor, &module.program);
581 self.emit.blank_line();
582 }
583 }
584 }
585
586 if let Some(root_module) = tree.modules.get(&tree.root) {
588 self.emit.writeln("// Root module");
589
590 for const_decl in &root_module.program.consts {
591 self.generate_const(const_decl);
592 self.emit.blank_line();
593 }
594
595 for enum_decl in &root_module.program.enums {
596 self.generate_enum(enum_decl);
597 self.emit.blank_line();
598 }
599
600 for record in &root_module.program.records {
601 self.generate_record(record);
602 self.emit.blank_line();
603 }
604
605 for func in &root_module.program.functions {
606 self.generate_function(func);
607 self.emit.blank_line();
608 }
609
610 for protocol in &root_module.program.protocols {
612 self.generate_protocol(protocol);
613 self.emit.blank_line();
614 }
615
616 for handler in &root_module.program.effect_handlers {
618 self.generate_effect_handler(handler);
619 self.emit.blank_line();
620 }
621
622 for agent in &root_module.program.agents {
623 self.generate_agent(agent);
624 self.emit.blank_line();
625 }
626
627 for supervisor in &root_module.program.supervisors {
628 self.generate_supervisor(supervisor, &root_module.program);
629 self.emit.blank_line();
630 }
631
632 if let Some(run_entry) = &root_module.program.run_agent {
634 if let Some(agent) = root_module
636 .program
637 .agents
638 .iter()
639 .find(|a| a.name.name == run_entry.name)
640 {
641 self.generate_main(agent);
642 } else if let Some(supervisor) = root_module
643 .program
644 .supervisors
645 .iter()
646 .find(|s| s.name.name == run_entry.name)
647 {
648 self.generate_supervisor_main(supervisor, &root_module.program);
650 }
651 }
652 }
653
654 std::mem::take(&mut self.emit).finish()
655 }
656
657 #[allow(dead_code)]
658 fn generate_cargo_toml(&self, name: &str) -> String {
659 self.generate_cargo_toml_impl(name, false)
660 }
661
662 fn generate_cargo_toml_with_persistence(&self, name: &str, needs_persistence: bool) -> String {
663 self.generate_cargo_toml_impl(name, needs_persistence)
664 }
665
666 fn generate_cargo_toml_impl(&self, name: &str, needs_persistence: bool) -> String {
667 if self.config.target == CodegenTarget::Wasm {
668 return self.generate_wasm_cargo_toml(name);
669 }
670
671 let feature_flag = if needs_persistence {
673 self.config.persistence.feature_flag()
674 } else {
675 None
676 };
677 let runtime_dep = self.config.runtime_dep.to_cargo_dep(feature_flag);
678
679 format!(
680 r#"[package]
681name = "{name}"
682version = "0.1.0"
683edition = "2021"
684
685[dependencies]
686{runtime_dep}
687tokio = {{ version = "1", features = ["full"] }}
688serde = {{ version = "1", features = ["derive"] }}
689serde_json = "1"
690
691# Standalone project, not part of parent workspace
692[workspace]
693"#
694 )
695 }
696
697 fn generate_wasm_cargo_toml(&self, name: &str) -> String {
698 let runtime_dep = self.config.runtime_dep.to_cargo_dep(None);
699
700 let web_dep = match &self.config.runtime_dep {
702 RuntimeDep::Path { path } => {
703 let web_path = path.replace("sage-runtime", "sage-runtime-web");
705 format!("sage-runtime-web = {{ path = \"{}\" }}", web_path)
706 }
707 RuntimeDep::CratesIo { version } => {
708 format!("sage-runtime-web = \"{version}\"")
709 }
710 };
711
712 format!(
713 r#"[package]
714name = "{name}"
715version = "0.1.0"
716edition = "2021"
717
718[lib]
719crate-type = ["cdylib", "rlib"]
720
721[dependencies]
722{runtime_dep}
723{web_dep}
724wasm-bindgen = "0.2"
725wasm-bindgen-futures = "0.4"
726serde = {{ version = "1", features = ["derive"] }}
727serde_json = "1"
728console_error_panic_hook = "0.1"
729
730# Standalone project, not part of parent workspace
731[workspace]
732
733[profile.wasm-release]
734inherits = "release"
735opt-level = "z"
736lto = true
737codegen-units = 1
738"#
739 )
740 }
741
742 fn has_persistent_fields(program: &Program) -> bool {
744 program
745 .agents
746 .iter()
747 .any(|agent| agent.beliefs.iter().any(|b| b.is_persistent))
748 }
749
750 fn has_persistent_fields_in_tree(tree: &ModuleTree) -> bool {
752 tree.modules
753 .values()
754 .any(|module| Self::has_persistent_fields(&module.program))
755 }
756
757 fn generate_test_binary(&mut self, program: &Program) -> String {
762 self.emit
764 .writeln("//! Generated test file by Sage compiler. Do not edit.");
765 self.emit.blank_line();
766 self.emit.writeln("#![allow(unused_imports, dead_code)]");
767 self.emit.blank_line();
768 self.emit.writeln("use sage_runtime::prelude::*;");
769 self.emit.blank_line();
770
771 for const_decl in &program.consts {
773 self.generate_const(const_decl);
774 self.emit.blank_line();
775 }
776
777 for enum_decl in &program.enums {
779 self.generate_enum(enum_decl);
780 self.emit.blank_line();
781 }
782
783 for record in &program.records {
785 self.generate_record(record);
786 self.emit.blank_line();
787 }
788
789 for func in &program.functions {
791 self.generate_function(func);
792 self.emit.blank_line();
793 }
794
795 for agent in &program.agents {
797 self.generate_agent(agent);
798 self.emit.blank_line();
799 }
800
801 let (serial_tests, concurrent_tests): (Vec<_>, Vec<_>) =
803 program.tests.iter().partition(|t| t.is_serial);
804
805 for test in &concurrent_tests {
807 self.generate_test_function(test);
808 self.emit.blank_line();
809 }
810
811 for test in &serial_tests {
813 self.generate_test_function(test);
814 self.emit.blank_line();
815 }
816
817 self.emit.writeln("fn main() {}");
819
820 std::mem::take(&mut self.emit).finish()
821 }
822
823 fn generate_test_cargo_toml(&self, name: &str) -> String {
824 let runtime_dep = self.config.runtime_dep.to_cargo_dep(None);
825 format!(
826 r#"[package]
827name = "{name}"
828version = "0.1.0"
829edition = "2021"
830
831[dependencies]
832{runtime_dep}
833tokio = {{ version = "1", features = ["full"] }}
834serde = {{ version = "1", features = ["derive"] }}
835serde_json = "1"
836
837# Standalone project, not part of parent workspace
838[workspace]
839"#
840 )
841 }
842
843 fn generate_test_function(&mut self, test: &TestDecl) {
844 let mock_infers = self.collect_mock_infers(&test.body);
846 let mock_tools = self.collect_mock_tools(&test.body);
847
848 self.emit.writeln("#[tokio::test]");
850 let test_fn_name = self.sanitize_test_name(&test.name);
852 self.emit.write("async fn ");
853 self.emit.write(&test_fn_name);
854 self.emit.writeln("() {");
855 self.emit.indent();
856
857 if !mock_infers.is_empty() {
859 self.emit
860 .writeln("let _mock_client = MockLlmClient::with_responses(vec![");
861 self.emit.indent();
862 for mock in &mock_infers {
863 match mock {
864 MockValue::Value(expr) => {
865 self.emit.write("MockResponse::value(");
866 self.generate_expr(expr);
867 self.emit.writeln("),");
868 }
869 MockValue::Fail(expr) => {
870 self.emit.write("MockResponse::fail(");
871 self.generate_expr(expr);
872 self.emit.writeln("),");
873 }
874 }
875 }
876 self.emit.dedent();
877 self.emit.writeln("]);");
878 self.emit.blank_line();
879 }
880
881 if !mock_tools.is_empty() {
883 self.emit
884 .writeln("let _mock_tools = MockToolRegistry::new();");
885 for (tool_name, fn_name, value) in &mock_tools {
886 self.emit.write("_mock_tools.register(\"");
887 self.emit.write(tool_name);
888 self.emit.write("\", \"");
889 self.emit.write(fn_name);
890 self.emit.write("\", ");
891 match value {
892 MockValue::Value(expr) => {
893 self.emit.write("MockResponse::value(");
894 self.generate_expr(expr);
895 self.emit.write(")");
896 }
897 MockValue::Fail(expr) => {
898 self.emit.write("MockResponse::fail(");
899 self.generate_expr(expr);
900 self.emit.write(")");
901 }
902 }
903 self.emit.writeln(");");
904 }
905 self.emit.blank_line();
906
907 self.emit.writeln("with_mock_tools(_mock_tools, async {");
909 self.emit.indent();
910 self.generate_test_block(&test.body);
911 self.emit.dedent();
912 self.emit.writeln("}).await;");
913 } else {
914 self.generate_test_block(&test.body);
916 }
917
918 self.emit.dedent();
919 self.emit.writeln("}");
920 }
921
922 fn collect_mock_infers(&self, block: &Block) -> Vec<MockValue> {
923 let mut mocks = Vec::new();
924 for stmt in &block.stmts {
925 if let Stmt::MockDivine { value, .. } = stmt {
926 mocks.push(value.clone());
927 }
928 }
929 mocks
930 }
931
932 fn collect_mock_tools(&self, block: &Block) -> Vec<(String, String, MockValue)> {
933 let mut mocks = Vec::new();
934 for stmt in &block.stmts {
935 if let Stmt::MockTool {
936 tool_name,
937 fn_name,
938 value,
939 ..
940 } = stmt
941 {
942 mocks.push((tool_name.name.clone(), fn_name.name.clone(), value.clone()));
943 }
944 }
945 mocks
946 }
947
948 fn generate_test_block(&mut self, block: &Block) {
949 for stmt in &block.stmts {
950 if matches!(stmt, Stmt::MockDivine { .. } | Stmt::MockTool { .. }) {
952 continue;
953 }
954 self.generate_test_stmt(stmt);
955 }
956 }
957
958 fn generate_test_stmt(&mut self, stmt: &Stmt) {
959 match stmt {
960 Stmt::Expr { expr, .. } => {
962 if let Expr::Call { name, args, .. } = expr {
963 if self.is_assertion_builtin(&name.name) {
964 self.generate_assertion(&name.name, args);
965 return;
966 }
967 }
968 self.generate_expr(expr);
970 self.emit.writeln(";");
971 }
972 _ => self.generate_stmt(stmt),
974 }
975 }
976
977 fn is_assertion_builtin(&self, name: &str) -> bool {
978 matches!(
979 name,
980 "assert"
981 | "assert_eq"
982 | "assert_neq"
983 | "assert_gt"
984 | "assert_lt"
985 | "assert_gte"
986 | "assert_lte"
987 | "assert_true"
988 | "assert_false"
989 | "assert_contains"
990 | "assert_not_contains"
991 | "assert_empty"
992 | "assert_not_empty"
993 | "assert_starts_with"
994 | "assert_ends_with"
995 | "assert_len"
996 | "assert_empty_list"
997 | "assert_not_empty_list"
998 | "assert_fails"
999 )
1000 }
1001
1002 fn generate_assertion(&mut self, name: &str, args: &[Expr]) {
1003 match name {
1004 "assert" | "assert_true" => {
1005 self.emit.write("assert!(");
1006 if !args.is_empty() {
1007 self.generate_expr(&args[0]);
1008 }
1009 self.emit.writeln(");");
1010 }
1011 "assert_false" => {
1012 self.emit.write("assert!(!");
1013 if !args.is_empty() {
1014 self.generate_expr(&args[0]);
1015 }
1016 self.emit.writeln(");");
1017 }
1018 "assert_eq" => {
1019 self.emit.write("assert_eq!(");
1020 if args.len() >= 2 {
1021 self.generate_expr(&args[0]);
1022 self.emit.write(", ");
1023 self.generate_expr(&args[1]);
1024 }
1025 self.emit.writeln(");");
1026 }
1027 "assert_neq" => {
1028 self.emit.write("assert_ne!(");
1029 if args.len() >= 2 {
1030 self.generate_expr(&args[0]);
1031 self.emit.write(", ");
1032 self.generate_expr(&args[1]);
1033 }
1034 self.emit.writeln(");");
1035 }
1036 "assert_gt" => {
1037 self.emit.write("assert!(");
1038 if args.len() >= 2 {
1039 self.generate_expr(&args[0]);
1040 self.emit.write(" > ");
1041 self.generate_expr(&args[1]);
1042 }
1043 self.emit.writeln(");");
1044 }
1045 "assert_lt" => {
1046 self.emit.write("assert!(");
1047 if args.len() >= 2 {
1048 self.generate_expr(&args[0]);
1049 self.emit.write(" < ");
1050 self.generate_expr(&args[1]);
1051 }
1052 self.emit.writeln(");");
1053 }
1054 "assert_gte" => {
1055 self.emit.write("assert!(");
1056 if args.len() >= 2 {
1057 self.generate_expr(&args[0]);
1058 self.emit.write(" >= ");
1059 self.generate_expr(&args[1]);
1060 }
1061 self.emit.writeln(");");
1062 }
1063 "assert_lte" => {
1064 self.emit.write("assert!(");
1065 if args.len() >= 2 {
1066 self.generate_expr(&args[0]);
1067 self.emit.write(" <= ");
1068 self.generate_expr(&args[1]);
1069 }
1070 self.emit.writeln(");");
1071 }
1072 "assert_contains" => {
1073 self.emit.write("assert!(");
1074 if args.len() >= 2 {
1075 self.generate_expr(&args[0]);
1076 self.emit.write(".contains(&");
1077 self.generate_expr(&args[1]);
1078 self.emit.write(")");
1079 }
1080 self.emit.writeln(");");
1081 }
1082 "assert_not_contains" => {
1083 self.emit.write("assert!(!");
1084 if args.len() >= 2 {
1085 self.generate_expr(&args[0]);
1086 self.emit.write(".contains(&");
1087 self.generate_expr(&args[1]);
1088 self.emit.write(")");
1089 }
1090 self.emit.writeln(");");
1091 }
1092 "assert_empty" => {
1093 self.emit.write("assert!(");
1094 if !args.is_empty() {
1095 self.generate_expr(&args[0]);
1096 }
1097 self.emit.writeln(".is_empty());");
1098 }
1099 "assert_not_empty" => {
1100 self.emit.write("assert!(!");
1101 if !args.is_empty() {
1102 self.generate_expr(&args[0]);
1103 }
1104 self.emit.writeln(".is_empty());");
1105 }
1106 "assert_starts_with" => {
1107 self.emit.write("assert!(");
1108 if args.len() >= 2 {
1109 self.generate_expr(&args[0]);
1110 self.emit.write(".starts_with(&");
1111 self.generate_expr(&args[1]);
1112 self.emit.write(")");
1113 }
1114 self.emit.writeln(");");
1115 }
1116 "assert_ends_with" => {
1117 self.emit.write("assert!(");
1118 if args.len() >= 2 {
1119 self.generate_expr(&args[0]);
1120 self.emit.write(".ends_with(&");
1121 self.generate_expr(&args[1]);
1122 self.emit.write(")");
1123 }
1124 self.emit.writeln(");");
1125 }
1126 "assert_len" => {
1127 self.emit.write("assert_eq!(");
1128 if args.len() >= 2 {
1129 self.generate_expr(&args[0]);
1130 self.emit.write(".len() as i64, ");
1131 self.generate_expr(&args[1]);
1132 }
1133 self.emit.writeln(");");
1134 }
1135 "assert_empty_list" => {
1136 self.emit.write("assert!(");
1137 if !args.is_empty() {
1138 self.generate_expr(&args[0]);
1139 }
1140 self.emit.writeln(".is_empty());");
1141 }
1142 "assert_not_empty_list" => {
1143 self.emit.write("assert!(!");
1144 if !args.is_empty() {
1145 self.generate_expr(&args[0]);
1146 }
1147 self.emit.writeln(".is_empty());");
1148 }
1149 "assert_fails" => {
1150 self.emit.writeln("{");
1152 self.emit.indent();
1153 self.emit.write("let result = ");
1154 if !args.is_empty() {
1155 self.generate_expr(&args[0]);
1156 }
1157 self.emit.writeln(";");
1158 self.emit.writeln(
1159 "assert!(result.is_err(), \"Expected operation to fail but it succeeded\");",
1160 );
1161 self.emit.dedent();
1162 self.emit.writeln("}");
1163 }
1164 _ => {
1165 self.emit.write(name);
1167 self.emit.write("(");
1168 for (i, arg) in args.iter().enumerate() {
1169 if i > 0 {
1170 self.emit.write(", ");
1171 }
1172 self.generate_expr(arg);
1173 }
1174 self.emit.writeln(");");
1175 }
1176 }
1177 }
1178
1179 fn sanitize_test_name(&self, name: &str) -> String {
1180 name.chars()
1182 .map(|c| if c.is_alphanumeric() { c } else { '_' })
1183 .collect::<String>()
1184 .to_lowercase()
1185 }
1186
1187 fn generate_const(&mut self, const_decl: &ConstDecl) {
1188 if const_decl.is_pub {
1189 self.emit.write("pub ");
1190 }
1191 self.emit.write("const ");
1192 self.emit.write(&const_decl.name.name);
1193 self.emit.write(": ");
1194 let is_string = matches!(const_decl.ty, TypeExpr::String);
1196 if is_string {
1197 self.string_consts.insert(const_decl.name.name.clone());
1198 self.emit.write("&'static str");
1199 } else {
1200 self.emit_type(&const_decl.ty);
1201 }
1202 self.emit.write(" = ");
1203 if is_string {
1205 if let Expr::Literal {
1206 value: Literal::String(s),
1207 ..
1208 } = &const_decl.value
1209 {
1210 self.emit.write("\"");
1211 self.emit.write(&Self::escape_string_for_rust(s));
1212 self.emit.write("\"");
1213 } else {
1214 self.generate_expr(&const_decl.value);
1215 }
1216 } else {
1217 self.generate_expr(&const_decl.value);
1218 }
1219 self.emit.writeln(";");
1220 }
1221
1222 fn generate_enum(&mut self, enum_decl: &EnumDecl) {
1223 if enum_decl.is_pub {
1224 self.emit.write("pub ");
1225 }
1226 if enum_decl.type_params.is_empty() {
1228 self.emit
1229 .writeln("#[derive(Debug, Clone, Copy, PartialEq, Eq)]");
1230 } else {
1231 self.emit.writeln("#[derive(Debug, Clone, PartialEq, Eq)]");
1232 }
1233 self.emit.write("enum ");
1234 self.emit.write(&enum_decl.name.name);
1235 if !enum_decl.type_params.is_empty() {
1237 self.emit.write("<");
1238 for (i, param) in enum_decl.type_params.iter().enumerate() {
1239 if i > 0 {
1240 self.emit.write(", ");
1241 }
1242 self.emit.write(¶m.name);
1243 }
1244 self.emit.write(">");
1245 }
1246 self.emit.writeln(" {");
1247 self.emit.indent();
1248 for variant in &enum_decl.variants {
1249 self.emit.write(&variant.name.name);
1250 if let Some(payload_ty) = &variant.payload {
1251 self.emit.write("(");
1252 self.emit_type(payload_ty);
1253 self.emit.write(")");
1254 }
1255 self.emit.writeln(",");
1256 }
1257 self.emit.dedent();
1258 self.emit.writeln("}");
1259 }
1260
1261 fn generate_record(&mut self, record: &RecordDecl) {
1262 if record.is_pub {
1263 self.emit.write("pub ");
1264 }
1265 self.emit
1267 .writeln("#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]");
1268 self.emit.write("struct ");
1269 self.emit.write(&record.name.name);
1270 if !record.type_params.is_empty() {
1272 self.emit.write("<");
1273 for (i, param) in record.type_params.iter().enumerate() {
1274 if i > 0 {
1275 self.emit.write(", ");
1276 }
1277 self.emit.write(¶m.name);
1278 }
1279 self.emit.write(">");
1280 }
1281 self.emit.writeln(" {");
1282 self.emit.indent();
1283 for field in &record.fields {
1284 self.emit.write(&field.name.name);
1285 self.emit.write(": ");
1286 self.emit_type(&field.ty);
1287 self.emit.writeln(",");
1288 }
1289 self.emit.dedent();
1290 self.emit.writeln("}");
1291 }
1292
1293 fn generate_function(&mut self, func: &FnDecl) {
1294 if func.is_pub {
1296 self.emit.write("pub ");
1297 }
1298 self.emit.write("fn ");
1299 self.emit.write(&func.name.name);
1300 if !func.type_params.is_empty() {
1302 self.emit.write("<");
1303 for (i, param) in func.type_params.iter().enumerate() {
1304 if i > 0 {
1305 self.emit.write(", ");
1306 }
1307 self.emit.write(¶m.name);
1308 }
1309 self.emit.write(">");
1310 }
1311 self.emit.write("(");
1312
1313 for (i, param) in func.params.iter().enumerate() {
1314 if i > 0 {
1315 self.emit.write(", ");
1316 }
1317 self.emit.write(¶m.name.name);
1318 self.emit.write(": ");
1319 self.emit_type(¶m.ty);
1320 }
1321
1322 self.emit.write(") -> ");
1323
1324 if func.is_fallible {
1326 self.emit.write("SageResult<");
1327 self.emit_type(&func.return_ty);
1328 self.emit.write(">");
1329 } else {
1330 self.emit_type(&func.return_ty);
1331 }
1332
1333 self.emit.write(" ");
1334 self.generate_block(&func.body);
1335 }
1336
1337 fn collect_agent_metadata(&mut self, agent: &AgentDecl) {
1340 let name = &agent.name.name;
1341
1342 let has_error_handler = agent
1344 .handlers
1345 .iter()
1346 .any(|h| matches!(h.event, EventKind::Error { .. }));
1347 if has_error_handler {
1348 self.agents_with_error_handlers.insert(name.clone());
1349 }
1350
1351 if !agent.tool_uses.is_empty() {
1353 let tool_names: Vec<String> = agent.tool_uses.iter().map(|t| t.name.clone()).collect();
1354 self.agent_tool_uses.insert(name.clone(), tool_names);
1355 }
1356
1357 let message_handlers: Vec<_> = agent
1359 .handlers
1360 .iter()
1361 .filter_map(|h| {
1362 if let EventKind::Message {
1363 param_name,
1364 param_ty,
1365 } = &h.event
1366 {
1367 Some((param_name.clone(), param_ty.clone()))
1368 } else {
1369 None
1370 }
1371 })
1372 .collect();
1373 if !message_handlers.is_empty() {
1374 self.agents_with_message_handlers.insert(
1375 name.clone(),
1376 AgentMessageHandlers {
1377 handlers: message_handlers,
1378 },
1379 );
1380 }
1381 }
1382
1383 fn generate_agent(&mut self, agent: &AgentDecl) {
1384 let name = &agent.name.name;
1385
1386 self.current_protocol_roles.clear();
1388 for pr in &agent.follows {
1389 self.current_protocol_roles
1390 .insert(pr.protocol.name.clone(), pr.role.name.clone());
1391 }
1392
1393 self.current_agent_persistent_beliefs = agent
1395 .beliefs
1396 .iter()
1397 .filter(|b| b.is_persistent)
1398 .map(|b| b.name.name.clone())
1399 .collect();
1400
1401 let has_tools = !agent.tool_uses.is_empty();
1403 let needs_struct_body = !agent.beliefs.is_empty() || has_tools;
1404
1405 if agent.is_pub {
1407 self.emit.write("pub ");
1408 }
1409 self.emit.write("struct ");
1410 self.emit.write(name);
1411 if !needs_struct_body {
1412 self.emit.writeln(";");
1413 } else {
1414 self.emit.writeln(" {");
1415 self.emit.indent();
1416
1417 for tool_use in &agent.tool_uses {
1419 self.emit.write(&tool_use.name.to_lowercase());
1421 self.emit.write(": ");
1422 self.emit.write(&tool_use.name);
1423 self.emit.writeln("Client,");
1424 }
1425
1426 let has_persistent = agent.beliefs.iter().any(|b| b.is_persistent);
1428
1429 if has_persistent {
1431 self.emit
1432 .writeln("_checkpoint: std::sync::Arc<dyn CheckpointStore>,");
1433 self.emit.writeln("_checkpoint_key: String,");
1434 }
1435
1436 for belief in &agent.beliefs {
1438 self.emit.write(&belief.name.name);
1439 self.emit.write(": ");
1440 if belief.is_persistent {
1441 self.emit.write("Persisted<");
1442 self.emit_type(&belief.ty);
1443 self.emit.write(">");
1444 } else {
1445 self.emit_type(&belief.ty);
1446 }
1447 self.emit.writeln(",");
1448 }
1449 self.emit.dedent();
1450 self.emit.writeln("}");
1451 }
1452 self.emit.blank_line();
1453
1454 let output_type = self.infer_agent_output_type(agent);
1456
1457 self.emit.write("impl ");
1459 self.emit.write(name);
1460 self.emit.writeln(" {");
1461 self.emit.indent();
1462
1463 for handler in &agent.handlers {
1465 match &handler.event {
1466 EventKind::Waking => {
1468 self.emit.writeln("async fn on_waking(&self) {");
1469 self.emit.indent();
1470 self.generate_block_contents(&handler.body);
1471 self.emit.dedent();
1472 self.emit.writeln("}");
1473 }
1474
1475 EventKind::Start => {
1476 self.emit
1477 .write("async fn on_start(&self, ctx: &mut AgentContext<");
1478 self.emit.write(&output_type);
1479 self.emit.write(">) -> SageResult<");
1480 self.emit.write(&output_type);
1481 self.emit.writeln("> {");
1482 self.emit.indent();
1483 self.generate_block_contents(&handler.body);
1484 self.emit.dedent();
1485 self.emit.writeln("}");
1486 }
1487
1488 EventKind::Error { param_name } => {
1490 self.emit.write("async fn on_error(&self, _");
1491 self.emit.write(¶m_name.name);
1492 self.emit.write(": SageError, ctx: &mut AgentContext<");
1493 self.emit.write(&output_type);
1494 self.emit.write(">) -> SageResult<");
1495 self.emit.write(&output_type);
1496 self.emit.writeln("> {");
1497 self.emit.indent();
1498 self.generate_block_contents(&handler.body);
1499 self.emit.write("Err(_");
1501 self.emit.write(¶m_name.name);
1502 self.emit.writeln(")");
1503 self.emit.dedent();
1504 self.emit.writeln("}");
1505 }
1506
1507 EventKind::Pause => {
1509 self.emit.writeln("async fn on_pause(&self) {");
1510 self.emit.indent();
1511 self.generate_block_contents(&handler.body);
1512 self.emit.dedent();
1513 self.emit.writeln("}");
1514 }
1515
1516 EventKind::Resume => {
1518 self.emit.writeln("async fn on_resume(&self) {");
1519 self.emit.indent();
1520 self.generate_block_contents(&handler.body);
1521 self.emit.dedent();
1522 self.emit.writeln("}");
1523 }
1524
1525 EventKind::Stop => {
1527 self.emit.writeln("async fn on_stop(&self) {");
1528 self.emit.indent();
1529 self.generate_block_contents(&handler.body);
1530 self.emit.dedent();
1531 self.emit.writeln("}");
1532 }
1533
1534 EventKind::Resting => {
1536 self.emit.writeln("async fn on_stop(&self) {");
1537 self.emit.indent();
1538 self.generate_block_contents(&handler.body);
1539 self.emit.dedent();
1540 self.emit.writeln("}");
1541 }
1542
1543 EventKind::Message {
1545 param_name,
1546 param_ty,
1547 } => {
1548 let type_name = self.type_expr_to_string(param_ty);
1550 let method_name = format!("on_message_{}", Self::to_snake_case(&type_name));
1551
1552 self.emit.write("async fn ");
1553 self.emit.write(&method_name);
1554 self.emit.write("(&self, ");
1555 self.emit.write(¶m_name.name);
1556 self.emit.write(": ");
1557 self.emit_type(param_ty);
1558 self.emit.write(", ctx: &mut AgentContext<");
1559 self.emit.write(&output_type);
1560 self.emit.write(">) -> SageResult<()> ");
1561 self.emit.writeln("{");
1562 self.emit.indent();
1563 self.generate_block_contents(&handler.body);
1564 self.emit.writeln("Ok(())");
1565 self.emit.dedent();
1566 self.emit.writeln("}");
1567 }
1568 }
1569 }
1570
1571 self.emit.dedent();
1572 self.emit.writeln("}");
1573 }
1574
1575 fn generate_main(&mut self, agent: &AgentDecl) {
1576 if self.config.target == CodegenTarget::Wasm {
1577 return self.generate_wasm_main(agent);
1578 }
1579
1580 let entry_agent = &agent.name.name;
1581
1582 self.current_protocol_roles.clear();
1584 for pr in &agent.follows {
1585 self.current_protocol_roles
1586 .insert(pr.protocol.name.clone(), pr.role.name.clone());
1587 }
1588
1589 self.current_agent_persistent_beliefs = agent
1591 .beliefs
1592 .iter()
1593 .filter(|b| b.is_persistent)
1594 .map(|b| b.name.name.clone())
1595 .collect();
1596
1597 let has_error_handler = agent
1598 .handlers
1599 .iter()
1600 .any(|h| matches!(h.event, EventKind::Error { .. }));
1601
1602 let has_stop_handler = agent
1603 .handlers
1604 .iter()
1605 .any(|h| matches!(h.event, EventKind::Stop | EventKind::Resting));
1606
1607 let has_tools = !agent.tool_uses.is_empty();
1609
1610 let has_persistent = agent.beliefs.iter().any(|b| b.is_persistent);
1612
1613 let has_waking = agent
1615 .handlers
1616 .iter()
1617 .any(|h| matches!(h.event, EventKind::Waking));
1618
1619 let message_handlers: Vec<_> = agent
1621 .handlers
1622 .iter()
1623 .filter_map(|h| {
1624 if let EventKind::Message {
1625 param_name,
1626 param_ty,
1627 } = &h.event
1628 {
1629 Some((param_name.clone(), param_ty.clone()))
1630 } else {
1631 None
1632 }
1633 })
1634 .collect();
1635 let has_message_handlers = !message_handlers.is_empty();
1636
1637 self.emit.writeln("#[tokio::main]");
1638 self.emit
1639 .writeln("async fn main() -> Result<(), Box<dyn std::error::Error>> {");
1640 self.emit.indent();
1641
1642 self.emit_tracing_init();
1644 self.emit.writeln("");
1645
1646 if has_persistent {
1648 self.emit
1649 .writeln("// Initialize persistence checkpoint store");
1650 self.emit.writeln(
1651 "let _checkpoint: std::sync::Arc<dyn CheckpointStore> = std::sync::Arc::new(",
1652 );
1653 self.emit.indent();
1654 self.emit_checkpoint_store_init();
1655 self.emit.dedent();
1656 self.emit.writeln(");");
1657 self.emit.write("let _checkpoint_key = \"");
1658 self.emit.write(entry_agent);
1659 self.emit.writeln("_entry\".to_string();");
1660 self.emit.writeln("");
1661 }
1662
1663 let needs_struct = has_tools || !agent.beliefs.is_empty();
1665 let agent_construct = if needs_struct {
1666 let mut s = format!("{entry_agent} {{ ");
1667 let mut fields = Vec::new();
1668
1669 if has_persistent {
1671 fields.push("_checkpoint: std::sync::Arc::clone(&_checkpoint)".to_string());
1672 fields.push("_checkpoint_key: _checkpoint_key.clone()".to_string());
1673 }
1674
1675 for tool_use in &agent.tool_uses {
1677 if tool_use.name == "Database" {
1678 fields.push(format!("{}: _db_client", tool_use.name.to_lowercase()));
1679 } else {
1680 fields.push(format!(
1681 "{}: {}Client::from_env()",
1682 tool_use.name.to_lowercase(),
1683 tool_use.name
1684 ));
1685 }
1686 }
1687
1688 for belief in &agent.beliefs {
1690 if belief.is_persistent {
1691 fields.push(format!(
1693 "{}: Persisted::new(std::sync::Arc::clone(&_checkpoint), &_checkpoint_key, \"{}\")",
1694 belief.name.name,
1695 belief.name.name
1696 ));
1697 } else {
1698 fields.push(format!("{}: Default::default()", belief.name.name));
1700 }
1701 }
1702
1703 s.push_str(&fields.join(", "));
1704 s.push_str(" }");
1705 s
1706 } else {
1707 entry_agent.to_string()
1708 };
1709
1710 self.emit
1712 .writeln("let ctrl_c = async { tokio::signal::ctrl_c().await.ok() };");
1713
1714 self.emit.writeln("#[cfg(unix)]");
1715 self.emit.writeln("let terminate = async {");
1716 self.emit.indent();
1717 self.emit.writeln(
1718 "if let Ok(mut s) = tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate()) {",
1719 );
1720 self.emit.indent();
1721 self.emit.writeln("s.recv().await;");
1722 self.emit.dedent();
1723 self.emit.writeln("} else {");
1724 self.emit.indent();
1725 self.emit.writeln("std::future::pending::<()>().await;");
1726 self.emit.dedent();
1727 self.emit.writeln("}");
1728 self.emit.dedent();
1729 self.emit.writeln("};");
1730 self.emit.writeln("#[cfg(not(unix))]");
1731 self.emit
1732 .writeln("let terminate = std::future::pending::<()>();");
1733 self.emit.writeln("");
1734
1735 self.emit
1736 .writeln("let handle = sage_runtime::spawn(move |mut ctx| async move {");
1737 self.emit.indent();
1738
1739 for tool_use in &agent.tool_uses {
1741 if tool_use.name == "Database" {
1742 self.emit
1744 .write("let _db_client = DatabaseClient::from_env().await");
1745 self.emit
1746 .writeln(".expect(\"Failed to connect to database\");");
1747 }
1748 }
1749
1750 self.emit.write("let agent = ");
1751 self.emit.write(&agent_construct);
1752 self.emit.writeln(";");
1753
1754 if has_waking {
1756 self.emit.writeln("");
1757 self.emit
1758 .writeln("// on_waking: runs after persistent state loaded, before on_start");
1759 self.emit.writeln("agent.on_waking().await;");
1760 }
1761
1762 if has_error_handler {
1763 self.emit
1765 .writeln("let result = match agent.on_start(&mut ctx).await {");
1766 self.emit.indent();
1767 self.emit.writeln("Ok(result) => Ok(result),");
1768 self.emit
1769 .writeln("Err(e) => agent.on_error(e, &mut ctx).await,");
1770 self.emit.dedent();
1771 self.emit.writeln("};");
1772 } else {
1773 self.emit
1775 .writeln("let result = agent.on_start(&mut ctx).await;");
1776 }
1777
1778 let protocol_roles: Vec<_> = agent
1780 .follows
1781 .iter()
1782 .map(|pr| (pr.protocol.name.clone(), pr.role.name.clone()))
1783 .collect();
1784 let has_protocols = !protocol_roles.is_empty();
1785
1786 if has_message_handlers {
1788 self.emit.writeln("");
1789 self.emit.writeln("// Message receive loop");
1790 self.emit.writeln("if result.is_ok() {");
1791 self.emit.indent();
1792 self.emit.writeln("loop {");
1793 self.emit.indent();
1794 self.emit.writeln("match ctx.receive_raw().await {");
1795 self.emit.indent();
1796 self.emit.writeln("Ok(msg) => {");
1797 self.emit.indent();
1798 self.emit.writeln("// Dispatch based on message type");
1799 self.emit.writeln("match msg.type_name.as_deref() {");
1800 self.emit.indent();
1801
1802 for (param_name, param_ty) in &message_handlers {
1804 let type_name = self.type_expr_to_string(param_ty);
1805 let method_name = format!("on_message_{}", Self::to_snake_case(&type_name));
1806
1807 self.emit.write("Some(\"");
1808 self.emit.write(&type_name);
1809 self.emit.writeln("\") => {");
1810 self.emit.indent();
1811
1812 if has_protocols {
1814 if let Some((_, role)) = protocol_roles.first() {
1818 self.emit.writeln("// Phase 3: Validate protocol state");
1819 self.emit.write("ctx.validate_protocol_receive(\"");
1820 self.emit.write(&type_name);
1821 self.emit.write("\", \"");
1822 self.emit.write(role);
1823 self.emit.writeln("\").await?;");
1824 }
1825 }
1826
1827 self.emit.write("let ");
1828 self.emit.write(¶m_name.name);
1829 self.emit.write(": ");
1830 self.emit_type(param_ty);
1831 self.emit.writeln(" = serde_json::from_value(msg.payload)");
1832 self.emit.indent();
1833 self.emit.writeln(".map_err(|e| SageError::Agent(format!(\"Failed to deserialize message: {e}\")))?;");
1834 self.emit.dedent();
1835 self.emit.write("agent.");
1836 self.emit.write(&method_name);
1837 self.emit.write("(");
1838 self.emit.write(¶m_name.name);
1839 self.emit.writeln(", &mut ctx).await?;");
1840 self.emit.dedent();
1841 self.emit.writeln("}");
1842 }
1843
1844 self.emit.writeln("_ => {");
1846 self.emit.indent();
1847 self.emit.writeln("// Unknown message type, skip");
1848 self.emit.dedent();
1849 self.emit.writeln("}");
1850
1851 self.emit.dedent();
1852 self.emit.writeln("}"); self.emit.dedent();
1854 self.emit.writeln("}"); self.emit.writeln("Err(_) => break, // Channel closed");
1856 self.emit.dedent();
1857 self.emit.writeln("}"); self.emit.dedent();
1859 self.emit.writeln("}"); self.emit.dedent();
1861 self.emit.writeln("}"); }
1863
1864 if has_stop_handler {
1865 self.emit.writeln("agent.on_stop().await;");
1867 }
1868
1869 self.emit.writeln("result");
1870 self.emit.dedent();
1871 self.emit.writeln("});");
1872
1873 self.emit.writeln("");
1875 self.emit.writeln("let _result = tokio::select! {");
1876 self.emit.indent();
1877 self.emit.writeln("result = handle.result() => result?,");
1878 self.emit.writeln("_ = ctrl_c => {");
1879 self.emit.indent();
1880 self.emit
1881 .writeln("eprintln!(\"\\nReceived interrupt signal, shutting down...\");");
1882 self.emit.writeln("std::process::exit(0);");
1883 self.emit.dedent();
1884 self.emit.writeln("}");
1885 self.emit.writeln("_ = terminate => {");
1886 self.emit.indent();
1887 self.emit
1888 .writeln("eprintln!(\"Received terminate signal, shutting down...\");");
1889 self.emit.writeln("std::process::exit(0);");
1890 self.emit.dedent();
1891 self.emit.writeln("}");
1892 self.emit.dedent();
1893 self.emit.writeln("};");
1894 self.emit.writeln("Ok(())");
1895
1896 self.emit.dedent();
1897 self.emit.writeln("}");
1898 }
1899
1900 fn generate_wasm_main(&mut self, agent: &AgentDecl) {
1905 let entry_agent = &agent.name.name;
1906
1907 self.current_protocol_roles.clear();
1909 for pr in &agent.follows {
1910 self.current_protocol_roles
1911 .insert(pr.protocol.name.clone(), pr.role.name.clone());
1912 }
1913
1914 self.current_agent_persistent_beliefs = agent
1916 .beliefs
1917 .iter()
1918 .filter(|b| b.is_persistent)
1919 .map(|b| b.name.name.clone())
1920 .collect();
1921
1922 let has_error_handler = agent
1923 .handlers
1924 .iter()
1925 .any(|h| matches!(h.event, EventKind::Error { .. }));
1926
1927 let has_stop_handler = agent
1928 .handlers
1929 .iter()
1930 .any(|h| matches!(h.event, EventKind::Stop | EventKind::Resting));
1931
1932 let has_tools = !agent.tool_uses.is_empty();
1933 let has_persistent = agent.beliefs.iter().any(|b| b.is_persistent);
1934
1935 let has_waking = agent
1936 .handlers
1937 .iter()
1938 .any(|h| matches!(h.event, EventKind::Waking));
1939
1940 let message_handlers: Vec<_> = agent
1942 .handlers
1943 .iter()
1944 .filter_map(|h| {
1945 if let EventKind::Message {
1946 param_name,
1947 param_ty,
1948 } = &h.event
1949 {
1950 Some((param_name.clone(), param_ty.clone()))
1951 } else {
1952 None
1953 }
1954 })
1955 .collect();
1956 let has_message_handlers = !message_handlers.is_empty();
1957
1958 self.emit.writeln("#[wasm_bindgen(start)]");
1960 self.emit.writeln("pub fn wasm_main() {");
1961 self.emit.indent();
1962
1963 self.emit.writeln("console_error_panic_hook::set_once();");
1965 self.emit.writeln("");
1966
1967 self.emit.writeln("sage_runtime::trace::init();");
1969 self.emit.writeln("");
1970
1971 if has_persistent {
1973 self.emit
1974 .writeln("// Initialize persistence checkpoint store (memory-only on WASM)");
1975 self.emit.writeln(
1976 "let _checkpoint: std::sync::Arc<dyn CheckpointStore> = std::sync::Arc::new(",
1977 );
1978 self.emit.indent();
1979 self.emit
1980 .writeln("sage_runtime::persistence::MemoryCheckpointStore::new()");
1981 self.emit.dedent();
1982 self.emit.writeln(");");
1983 self.emit.write("let _checkpoint_key = \"");
1984 self.emit.write(entry_agent);
1985 self.emit.writeln("_entry\".to_string();");
1986 self.emit.writeln("");
1987 }
1988
1989 let needs_struct = has_tools || !agent.beliefs.is_empty();
1991 let agent_construct = if needs_struct {
1992 let mut s = format!("{entry_agent} {{ ");
1993 let mut fields = Vec::new();
1994
1995 if has_persistent {
1996 fields.push("_checkpoint: std::sync::Arc::clone(&_checkpoint)".to_string());
1997 fields.push("_checkpoint_key: _checkpoint_key.clone()".to_string());
1998 }
1999
2000 for tool_use in &agent.tool_uses {
2001 if tool_use.name == "Database" {
2002 fields.push(format!("{}: _db_client", tool_use.name.to_lowercase()));
2004 } else {
2005 fields.push(format!(
2006 "{}: {}Client::from_env()",
2007 tool_use.name.to_lowercase(),
2008 tool_use.name
2009 ));
2010 }
2011 }
2012
2013 for belief in &agent.beliefs {
2014 if belief.is_persistent {
2015 fields.push(format!(
2016 "{}: Persisted::new(std::sync::Arc::clone(&_checkpoint), &_checkpoint_key, \"{}\")",
2017 belief.name.name,
2018 belief.name.name
2019 ));
2020 } else {
2021 fields.push(format!("{}: Default::default()", belief.name.name));
2022 }
2023 }
2024
2025 s.push_str(&fields.join(", "));
2026 s.push_str(" }");
2027 s
2028 } else {
2029 entry_agent.to_string()
2030 };
2031
2032 self.emit
2034 .writeln("let handle = sage_runtime::spawn(move |mut ctx| async move {");
2035 self.emit.indent();
2036
2037 for tool_use in &agent.tool_uses {
2039 if tool_use.name == "Database" {
2040 self.emit
2041 .write("let _db_client = DatabaseClient::from_env().await");
2042 self.emit
2043 .writeln(".expect(\"Failed to connect to database\");");
2044 }
2045 }
2046
2047 self.emit.write("let agent = ");
2048 self.emit.write(&agent_construct);
2049 self.emit.writeln(";");
2050
2051 if has_waking {
2052 self.emit.writeln("");
2053 self.emit
2054 .writeln("// on_waking: runs after persistent state loaded, before on_start");
2055 self.emit.writeln("agent.on_waking().await;");
2056 }
2057
2058 if has_error_handler {
2059 self.emit
2060 .writeln("let result = match agent.on_start(&mut ctx).await {");
2061 self.emit.indent();
2062 self.emit.writeln("Ok(result) => Ok(result),");
2063 self.emit
2064 .writeln("Err(e) => agent.on_error(e, &mut ctx).await,");
2065 self.emit.dedent();
2066 self.emit.writeln("};");
2067 } else {
2068 self.emit
2069 .writeln("let result = agent.on_start(&mut ctx).await;");
2070 }
2071
2072 let protocol_roles: Vec<_> = agent
2074 .follows
2075 .iter()
2076 .map(|pr| (pr.protocol.name.clone(), pr.role.name.clone()))
2077 .collect();
2078 let has_protocols = !protocol_roles.is_empty();
2079
2080 if has_message_handlers {
2082 self.emit.writeln("");
2083 self.emit.writeln("// Message receive loop");
2084 self.emit.writeln("if result.is_ok() {");
2085 self.emit.indent();
2086 self.emit.writeln("loop {");
2087 self.emit.indent();
2088 self.emit.writeln("match ctx.receive_raw().await {");
2089 self.emit.indent();
2090 self.emit.writeln("Ok(msg) => {");
2091 self.emit.indent();
2092 self.emit.writeln("match msg.type_name.as_deref() {");
2093 self.emit.indent();
2094
2095 for (param_name, param_ty) in &message_handlers {
2096 let type_name = self.type_expr_to_string(param_ty);
2097 let method_name = format!("on_message_{}", Self::to_snake_case(&type_name));
2098
2099 self.emit.write("Some(\"");
2100 self.emit.write(&type_name);
2101 self.emit.writeln("\") => {");
2102 self.emit.indent();
2103
2104 if has_protocols {
2105 if let Some((_, role)) = protocol_roles.first() {
2106 self.emit.writeln("// Validate protocol state");
2107 self.emit.write("ctx.validate_protocol_receive(\"");
2108 self.emit.write(&type_name);
2109 self.emit.write("\", \"");
2110 self.emit.write(role);
2111 self.emit.writeln("\").await?;");
2112 }
2113 }
2114
2115 self.emit.write("let ");
2116 self.emit.write(¶m_name.name);
2117 self.emit.write(": ");
2118 self.emit_type(param_ty);
2119 self.emit.writeln(" = serde_json::from_value(msg.payload)");
2120 self.emit.indent();
2121 self.emit.writeln(".map_err(|e| SageError::Agent(format!(\"Failed to deserialize message: {e}\")))?;");
2122 self.emit.dedent();
2123 self.emit.write("agent.");
2124 self.emit.write(&method_name);
2125 self.emit.write("(");
2126 self.emit.write(¶m_name.name);
2127 self.emit.writeln(", &mut ctx).await?;");
2128 self.emit.dedent();
2129 self.emit.writeln("}");
2130 }
2131
2132 self.emit.writeln("_ => {}");
2133 self.emit.dedent();
2134 self.emit.writeln("}"); self.emit.dedent();
2136 self.emit.writeln("}"); self.emit.writeln("Err(_) => break,");
2138 self.emit.dedent();
2139 self.emit.writeln("}"); self.emit.dedent();
2141 self.emit.writeln("}"); self.emit.dedent();
2143 self.emit.writeln("}"); }
2145
2146 if has_stop_handler {
2147 self.emit.writeln("agent.on_stop().await;");
2148 }
2149
2150 self.emit.writeln("result");
2151 self.emit.dedent();
2152 self.emit.writeln("});");
2153
2154 self.emit.writeln("");
2155
2156 self.emit.writeln("wasm_bindgen_futures::spawn_local(async move {");
2158 self.emit.indent();
2159 self.emit.writeln("match handle.result().await {");
2160 self.emit.indent();
2161 self.emit.writeln("Ok(_) => sage_runtime_web::console_log(\"Agent completed successfully\"),");
2162 self.emit.writeln("Err(e) => sage_runtime_web::console_error(&format!(\"Agent failed: {e}\")),");
2163 self.emit.dedent();
2164 self.emit.writeln("}");
2165 self.emit.dedent();
2166 self.emit.writeln("});");
2167
2168 self.emit.dedent();
2169 self.emit.writeln("}");
2170 }
2171
2172 fn generate_supervisor(&mut self, supervisor: &SupervisorDecl, _program: &Program) {
2177 let name = &supervisor.name.name;
2178
2179 self.emit.write("// Supervisor: ");
2181 self.emit.writeln(name);
2182
2183 if supervisor.is_pub {
2185 self.emit.write("pub ");
2186 }
2187 self.emit.write("struct ");
2188 self.emit.write(name);
2189 self.emit.writeln(";");
2190 }
2191
2192 fn generate_supervisor_main(&mut self, supervisor: &SupervisorDecl, program: &Program) {
2197 let name = &supervisor.name.name;
2198
2199 self.emit.writeln("#[tokio::main]");
2200 self.emit
2201 .writeln("async fn main() -> Result<(), Box<dyn std::error::Error>> {");
2202 self.emit.indent();
2203
2204 self.emit_tracing_init();
2206 self.emit.writeln("");
2207
2208 self.emit
2210 .write("let mut supervisor = Supervisor::new(Strategy::");
2211 match supervisor.strategy {
2212 SupervisionStrategy::OneForOne => self.emit.write("OneForOne"),
2213 SupervisionStrategy::OneForAll => self.emit.write("OneForAll"),
2214 SupervisionStrategy::RestForOne => self.emit.write("RestForOne"),
2215 }
2216 self.emit.writeln(&format!(
2217 ", RestartConfig {{ max_restarts: {}, within: std::time::Duration::from_secs({}) }});",
2218 self.config.supervision.max_restarts, self.config.supervision.within_seconds
2219 ));
2220 self.emit.writeln("");
2221
2222 for child in &supervisor.children {
2224 let child_agent_name = &child.agent_name.name;
2225
2226 let agent = program
2228 .agents
2229 .iter()
2230 .find(|a| a.name.name == *child_agent_name);
2231
2232 self.current_protocol_roles.clear();
2234 if let Some(agent) = agent {
2235 for pr in &agent.follows {
2236 self.current_protocol_roles
2237 .insert(pr.protocol.name.clone(), pr.role.name.clone());
2238 }
2239 }
2240
2241 self.current_agent_persistent_beliefs = if let Some(agent) = agent {
2243 agent
2244 .beliefs
2245 .iter()
2246 .filter(|b| b.is_persistent)
2247 .map(|b| b.name.name.clone())
2248 .collect()
2249 } else {
2250 Vec::new()
2251 };
2252
2253 let restart_policy = match child.restart {
2255 RestartPolicy::Permanent => "Permanent",
2256 RestartPolicy::Transient => "Transient",
2257 RestartPolicy::Temporary => "Temporary",
2258 };
2259
2260 self.emit.write("supervisor.add_child(\"");
2261 self.emit.write(child_agent_name);
2262 self.emit.write("\", RestartPolicy::");
2263 self.emit.write(restart_policy);
2264 self.emit.writeln(", || {");
2265 self.emit.indent();
2266
2267 self.emit.writeln("async {");
2269 self.emit.indent();
2270
2271 let (has_tools, has_beliefs, has_persistent) = if let Some(agent) = agent {
2273 let has_tools = !agent.tool_uses.is_empty();
2274 let has_beliefs = !agent.beliefs.is_empty();
2275 let has_persistent = agent.beliefs.iter().any(|b| b.is_persistent);
2276 (has_tools, has_beliefs, has_persistent)
2277 } else {
2278 (false, false, false)
2279 };
2280
2281 if has_persistent {
2283 self.emit.writeln(
2284 "let _checkpoint: std::sync::Arc<dyn CheckpointStore> = std::sync::Arc::new(",
2285 );
2286 self.emit.indent();
2287 self.emit_checkpoint_store_init();
2288 self.emit.dedent();
2289 self.emit.writeln(");");
2290 self.emit.write("let _checkpoint_key = \"");
2291 self.emit.write(child_agent_name);
2292 self.emit.writeln("\".to_string();");
2293 }
2294
2295 if let Some(agent) = agent {
2297 for tool_use in &agent.tool_uses {
2298 if tool_use.name == "Database" {
2299 self.emit
2300 .write("let _db_client = DatabaseClient::from_env().await");
2301 self.emit
2302 .writeln(".expect(\"Failed to connect to database\");");
2303 }
2304 }
2305 }
2306
2307 if has_tools || has_beliefs {
2309 self.emit.write("let agent = ");
2310 self.emit.write(child_agent_name);
2311 self.emit.writeln(" {");
2312 self.emit.indent();
2313
2314 if has_persistent {
2316 self.emit
2317 .writeln("_checkpoint: std::sync::Arc::clone(&_checkpoint),");
2318 self.emit
2319 .writeln("_checkpoint_key: _checkpoint_key.clone(),");
2320 }
2321
2322 if let Some(agent) = agent {
2324 for tool_use in &agent.tool_uses {
2325 if tool_use.name == "Database" {
2326 self.emit.write(&tool_use.name.to_lowercase());
2327 self.emit.writeln(": _db_client,");
2328 } else {
2329 self.emit.write(&tool_use.name.to_lowercase());
2330 self.emit.write(": ");
2331 self.emit.write(&tool_use.name);
2332 self.emit.writeln("Client::from_env(),");
2333 }
2334 }
2335 }
2336
2337 if let Some(agent) = agent {
2339 for belief in &agent.beliefs {
2340 let init_value = child
2342 .beliefs
2343 .iter()
2344 .find(|f| f.name.name == belief.name.name);
2345
2346 self.emit.write(&belief.name.name);
2347 self.emit.write(": ");
2348
2349 if belief.is_persistent {
2350 if let Some(init) = init_value {
2351 self.emit.write("Persisted::with_initial(std::sync::Arc::clone(&_checkpoint), &_checkpoint_key, \"");
2353 self.emit.write(&belief.name.name);
2354 self.emit.write("\", ");
2355 self.generate_expr(&init.value);
2356 self.emit.write(")");
2357 } else {
2358 self.emit.write("Persisted::new(std::sync::Arc::clone(&_checkpoint), &_checkpoint_key, \"");
2360 self.emit.write(&belief.name.name);
2361 self.emit.write("\")");
2362 }
2363 } else if let Some(init) = init_value {
2364 self.generate_expr(&init.value);
2366 } else {
2367 self.emit.write("Default::default()");
2369 }
2370 self.emit.writeln(",");
2371 }
2372 }
2373
2374 self.emit.dedent();
2375 self.emit.writeln("};");
2376 } else {
2377 self.emit.write("let agent = ");
2378 self.emit.write(child_agent_name);
2379 self.emit.writeln(";");
2380 }
2381
2382 let has_waking = agent
2384 .map(|a| {
2385 a.handlers
2386 .iter()
2387 .any(|h| matches!(h.event, EventKind::Waking))
2388 })
2389 .unwrap_or(false);
2390
2391 let has_error_handler = agent
2393 .map(|a| {
2394 a.handlers
2395 .iter()
2396 .any(|h| matches!(h.event, EventKind::Error { .. }))
2397 })
2398 .unwrap_or(false);
2399
2400 let has_stop_handler = agent
2402 .map(|a| {
2403 a.handlers
2404 .iter()
2405 .any(|h| matches!(h.event, EventKind::Stop | EventKind::Resting))
2406 })
2407 .unwrap_or(false);
2408
2409 let infer_handler = child
2411 .handler_assignments
2412 .iter()
2413 .find(|ha| ha.effect.name == "Infer")
2414 .map(|ha| ha.handler.name.clone());
2415
2416 if let Some(handler_name) = &infer_handler {
2418 self.emit
2420 .writeln("// Phase 3: Use effect handler configuration");
2421 self.emit
2422 .write("let llm_config = sage_runtime::LlmConfig::with_model(handler_");
2423 self.emit.write(&Self::to_snake_case(handler_name));
2424 self.emit.writeln("::CONFIG.model)");
2425 self.emit.indent();
2426 self.emit.write(".with_temperature(handler_");
2428 self.emit.write(&Self::to_snake_case(handler_name));
2429 self.emit.writeln("::CONFIG.temperature)");
2430 self.emit.write(".with_max_tokens(handler_");
2432 self.emit.write(&Self::to_snake_case(handler_name));
2433 self.emit.writeln("::CONFIG.max_tokens);");
2434 self.emit.dedent();
2435 self.emit.writeln(
2436 "let handle = sage_runtime::spawn_with_llm_config(move |mut ctx| async move {",
2437 );
2438 } else {
2439 self.emit
2440 .writeln("let handle = sage_runtime::spawn(move |mut ctx| async move {");
2441 }
2442 self.emit.indent();
2443
2444 if has_waking {
2446 self.emit.writeln("agent.on_waking().await;");
2447 }
2448
2449 if has_error_handler {
2451 self.emit
2452 .writeln("let result = match agent.on_start(&mut ctx).await {");
2453 self.emit.indent();
2454 self.emit.writeln("Ok(result) => Ok(result),");
2455 self.emit
2456 .writeln("Err(e) => agent.on_error(e, &mut ctx).await,");
2457 self.emit.dedent();
2458 self.emit.writeln("};");
2459 } else {
2460 self.emit
2461 .writeln("let result = agent.on_start(&mut ctx).await;");
2462 }
2463
2464 if has_stop_handler {
2466 self.emit.writeln("agent.on_stop().await;");
2467 }
2468
2469 self.emit.writeln("result");
2470 self.emit.dedent();
2471 self.emit.writeln("});");
2472
2473 self.emit
2475 .writeln("handle.result().await.map_err(|e| SageError::Agent(e.to_string()))?;");
2476 self.emit.writeln("Ok(())");
2477
2478 self.emit.dedent();
2479 self.emit.writeln("}");
2480 self.emit.dedent();
2481 self.emit.writeln("});");
2482 self.emit.writeln("");
2483 }
2484
2485 self.emit
2487 .writeln("let ctrl_c = async { tokio::signal::ctrl_c().await.ok() };");
2488 self.emit.writeln("");
2489 self.emit.writeln("#[cfg(unix)]");
2490 self.emit.writeln("let terminate = async {");
2491 self.emit.indent();
2492 self.emit.writeln("if let Ok(mut s) = tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate()) {");
2493 self.emit.indent();
2494 self.emit.writeln("s.recv().await;");
2495 self.emit.dedent();
2496 self.emit.writeln("} else {");
2497 self.emit.indent();
2498 self.emit.writeln("std::future::pending::<()>().await;");
2499 self.emit.dedent();
2500 self.emit.writeln("}");
2501 self.emit.dedent();
2502 self.emit.writeln("};");
2503 self.emit.writeln("#[cfg(not(unix))]");
2504 self.emit
2505 .writeln("let terminate = std::future::pending::<()>();");
2506 self.emit.writeln("");
2507
2508 let child_count = supervisor.children.len();
2510 self.emit.write("eprintln!(\"Starting supervisor '");
2511 self.emit.write(name);
2512 self.emit.write("' with ");
2513 self.emit.write(&child_count.to_string());
2514 self.emit.writeln(" children...\");");
2515 self.emit.writeln("");
2516 self.emit.writeln("let result = tokio::select! {");
2517 self.emit.indent();
2518 self.emit.writeln("result = supervisor.run() => result,");
2519 self.emit.writeln("_ = ctrl_c => {");
2520 self.emit.indent();
2521 self.emit
2522 .writeln("eprintln!(\"\\nReceived interrupt signal, shutting down supervisor...\");");
2523 self.emit.writeln("return Ok(());");
2524 self.emit.dedent();
2525 self.emit.writeln("}");
2526 self.emit.writeln("_ = terminate => {");
2527 self.emit.indent();
2528 self.emit
2529 .writeln("eprintln!(\"Received terminate signal, shutting down supervisor...\");");
2530 self.emit.writeln("return Ok(());");
2531 self.emit.dedent();
2532 self.emit.writeln("}");
2533 self.emit.dedent();
2534 self.emit.writeln("};");
2535 self.emit.writeln("");
2536 self.emit.writeln("if let Err(e) = result {");
2537 self.emit.indent();
2538 self.emit.writeln("eprintln!(\"Supervisor error: {}\", e);");
2539 self.emit.writeln("return Err(e.into());");
2540 self.emit.dedent();
2541 self.emit.writeln("}");
2542 self.emit.writeln("");
2543 self.emit.writeln("Ok(())");
2544
2545 self.emit.dedent();
2546 self.emit.writeln("}");
2547 }
2548
2549 fn generate_protocol(&mut self, protocol: &ProtocolDecl) {
2567 let name = &protocol.name.name;
2568 let mod_name = Self::to_snake_case(name);
2569
2570 self.emit.write("// Protocol: ");
2572 self.emit.writeln(name);
2573
2574 if protocol.is_pub {
2576 self.emit.write("pub ");
2577 }
2578 self.emit.write("mod protocol_");
2579 self.emit.write(&mod_name);
2580 self.emit.writeln(" {");
2581 self.emit.indent();
2582
2583 self.emit.writeln("use super::*;");
2584 self.emit.blank_line();
2585
2586 self.emit
2589 .writeln("#[derive(Debug, Clone, Copy, PartialEq, Eq)]");
2590 self.emit.writeln("pub enum State {");
2591 self.emit.indent();
2592 self.emit.writeln("Initial,");
2593 for (i, step) in protocol.steps.iter().enumerate() {
2594 self.emit.write("After");
2595 self.emit.write(&Self::capitalize(&step.sender.name));
2596 self.emit.write("Sends");
2597 self.generate_type_name_for_state(&step.message_type);
2598 self.emit.write("_");
2599 self.emit.write(&i.to_string());
2600 self.emit.writeln(",");
2601 }
2602 self.emit.writeln("Done,");
2603 self.emit.dedent();
2604 self.emit.writeln("}");
2605 self.emit.blank_line();
2606
2607 self.emit.writeln("impl Default for State {");
2609 self.emit.indent();
2610 self.emit.writeln("fn default() -> Self {");
2611 self.emit.indent();
2612 self.emit.writeln("Self::Initial");
2613 self.emit.dedent();
2614 self.emit.writeln("}");
2615 self.emit.dedent();
2616 self.emit.writeln("}");
2617 self.emit.blank_line();
2618
2619 self.emit.writeln("impl ProtocolStateMachine for State {");
2621 self.emit.indent();
2622
2623 self.emit.writeln("fn state_name(&self) -> &str {");
2625 self.emit.indent();
2626 self.emit.writeln("match self {");
2627 self.emit.indent();
2628 self.emit.writeln("State::Initial => \"Initial\",");
2629 for (i, step) in protocol.steps.iter().enumerate() {
2630 self.emit.write("State::After");
2631 self.emit.write(&Self::capitalize(&step.sender.name));
2632 self.emit.write("Sends");
2633 self.generate_type_name_for_state(&step.message_type);
2634 self.emit.write("_");
2635 self.emit.write(&i.to_string());
2636 self.emit.write(" => \"After");
2637 self.emit.write(&Self::capitalize(&step.sender.name));
2638 self.emit.write("Sends");
2639 self.generate_type_name_for_state(&step.message_type);
2640 self.emit.writeln("\",");
2641 }
2642 self.emit.writeln("State::Done => \"Done\",");
2643 self.emit.dedent();
2644 self.emit.writeln("}");
2645 self.emit.dedent();
2646 self.emit.writeln("}");
2647 self.emit.blank_line();
2648
2649 self.emit
2651 .writeln("fn can_send(&self, msg_type: &str, from_role: &str) -> bool {");
2652 self.emit.indent();
2653 self.emit.writeln("match (self, msg_type, from_role) {");
2654 self.emit.indent();
2655 for (i, step) in protocol.steps.iter().enumerate() {
2656 let state_name = if i == 0 {
2657 "State::Initial".to_string()
2658 } else {
2659 let prev = &protocol.steps[i - 1];
2660 format!(
2661 "State::After{}Sends{}_{}",
2662 Self::capitalize(&prev.sender.name),
2663 Self::type_name_for_state(&prev.message_type),
2664 i - 1
2665 )
2666 };
2667 self.emit.write("(");
2668 self.emit.write(&state_name);
2669 self.emit.write(", \"");
2670 self.generate_type_name_for_state(&step.message_type);
2671 self.emit.write("\", \"");
2672 self.emit.write(&step.sender.name);
2673 self.emit.writeln("\") => true,");
2674 }
2675 self.emit.writeln("_ => false,");
2676 self.emit.dedent();
2677 self.emit.writeln("}");
2678 self.emit.dedent();
2679 self.emit.writeln("}");
2680 self.emit.blank_line();
2681
2682 self.emit
2684 .writeln("fn can_receive(&self, msg_type: &str, to_role: &str) -> bool {");
2685 self.emit.indent();
2686 self.emit.writeln("match (self, msg_type, to_role) {");
2687 self.emit.indent();
2688 for (i, step) in protocol.steps.iter().enumerate() {
2689 let state_name = if i == 0 {
2690 "State::Initial".to_string()
2691 } else {
2692 let prev = &protocol.steps[i - 1];
2693 format!(
2694 "State::After{}Sends{}_{}",
2695 Self::capitalize(&prev.sender.name),
2696 Self::type_name_for_state(&prev.message_type),
2697 i - 1
2698 )
2699 };
2700 self.emit.write("(");
2701 self.emit.write(&state_name);
2702 self.emit.write(", \"");
2703 self.generate_type_name_for_state(&step.message_type);
2704 self.emit.write("\", \"");
2705 self.emit.write(&step.receiver.name);
2706 self.emit.writeln("\") => true,");
2707 }
2708 self.emit.writeln("_ => false,");
2709 self.emit.dedent();
2710 self.emit.writeln("}");
2711 self.emit.dedent();
2712 self.emit.writeln("}");
2713 self.emit.blank_line();
2714
2715 self.emit
2717 .writeln("fn transition(&mut self, msg_type: &str) -> Result<(), ProtocolViolation> {");
2718 self.emit.indent();
2719 self.emit.writeln("let next = match (&self, msg_type) {");
2720 self.emit.indent();
2721 for (i, step) in protocol.steps.iter().enumerate() {
2722 let state_name = if i == 0 {
2723 "State::Initial".to_string()
2724 } else {
2725 let prev = &protocol.steps[i - 1];
2726 format!(
2727 "State::After{}Sends{}_{}",
2728 Self::capitalize(&prev.sender.name),
2729 Self::type_name_for_state(&prev.message_type),
2730 i - 1
2731 )
2732 };
2733 let next_state = if i + 1 >= protocol.steps.len() {
2734 "State::Done".to_string()
2735 } else {
2736 format!(
2737 "State::After{}Sends{}_{}",
2738 Self::capitalize(&step.sender.name),
2739 Self::type_name_for_state(&step.message_type),
2740 i
2741 )
2742 };
2743 self.emit.write("(");
2744 self.emit.write(&state_name);
2745 self.emit.write(", \"");
2746 self.generate_type_name_for_state(&step.message_type);
2747 self.emit.write("\") => ");
2748 self.emit.write(&next_state);
2749 self.emit.writeln(",");
2750 }
2751 self.emit
2752 .writeln("_ => return Err(ProtocolViolation::UnexpectedMessage {");
2753 self.emit.indent();
2754 self.emit.write("protocol: \"");
2755 self.emit.write(name);
2756 self.emit.writeln("\".to_string(),");
2757 self.emit.writeln("expected: \"unknown\".to_string(),");
2758 self.emit.writeln("received: msg_type.to_string(),");
2759 self.emit.writeln("state: self.state_name().to_string(),");
2760 self.emit.dedent();
2761 self.emit.writeln("}),");
2762 self.emit.dedent();
2763 self.emit.writeln("};");
2764 self.emit.writeln("*self = next;");
2765 self.emit.writeln("Ok(())");
2766 self.emit.dedent();
2767 self.emit.writeln("}");
2768 self.emit.blank_line();
2769
2770 self.emit.writeln("fn is_terminal(&self) -> bool {");
2772 self.emit.indent();
2773 self.emit.writeln("matches!(self, State::Done)");
2774 self.emit.dedent();
2775 self.emit.writeln("}");
2776 self.emit.blank_line();
2777
2778 self.emit.writeln("fn protocol_name(&self) -> &str {");
2780 self.emit.indent();
2781 self.emit.write("\"");
2782 self.emit.write(name);
2783 self.emit.writeln("\"");
2784 self.emit.dedent();
2785 self.emit.writeln("}");
2786 self.emit.blank_line();
2787
2788 self.emit
2790 .writeln("fn clone_box(&self) -> Box<dyn ProtocolStateMachine> {");
2791 self.emit.indent();
2792 self.emit.writeln("Box::new(*self)");
2793 self.emit.dedent();
2794 self.emit.writeln("}");
2795
2796 self.emit.dedent();
2797 self.emit.writeln("}"); self.emit.dedent();
2801 self.emit.writeln("}"); }
2803
2804 fn to_snake_case(name: &str) -> String {
2806 let mut result = String::new();
2807 for (i, c) in name.chars().enumerate() {
2808 if c.is_uppercase() {
2809 if i > 0 {
2810 result.push('_');
2811 }
2812 result.push(c.to_ascii_lowercase());
2813 } else {
2814 result.push(c);
2815 }
2816 }
2817 result
2818 }
2819
2820 fn capitalize(name: &str) -> String {
2822 let mut chars = name.chars();
2823 match chars.next() {
2824 None => String::new(),
2825 Some(first) => first.to_uppercase().collect::<String>() + chars.as_str(),
2826 }
2827 }
2828
2829 fn generate_type_name_for_state(&mut self, ty: &TypeExpr) {
2831 match ty {
2832 TypeExpr::Int => self.emit.write("Int"),
2833 TypeExpr::Float => self.emit.write("Float"),
2834 TypeExpr::String => self.emit.write("String"),
2835 TypeExpr::Bool => self.emit.write("Bool"),
2836 TypeExpr::Unit => self.emit.write("Unit"),
2837 TypeExpr::Named(ident, _) => self.emit.write(&ident.name),
2838 TypeExpr::List(_) => self.emit.write("List"),
2839 TypeExpr::Map(_, _) => self.emit.write("Map"),
2840 TypeExpr::Option(_) => self.emit.write("Option"),
2841 TypeExpr::Result(_, _) => self.emit.write("Result"),
2842 TypeExpr::Oracle(_) => self.emit.write("Oracle"),
2843 TypeExpr::Agent(_) => self.emit.write("Agent"),
2844 TypeExpr::Tuple(_) => self.emit.write("Tuple"),
2845 TypeExpr::Fn(_, _) => self.emit.write("Fn"),
2846 TypeExpr::Error => self.emit.write("Error"),
2847 }
2848 }
2849
2850 fn type_name_for_state(ty: &TypeExpr) -> String {
2852 match ty {
2853 TypeExpr::Int => "Int".to_string(),
2854 TypeExpr::Float => "Float".to_string(),
2855 TypeExpr::String => "String".to_string(),
2856 TypeExpr::Bool => "Bool".to_string(),
2857 TypeExpr::Unit => "Unit".to_string(),
2858 TypeExpr::Named(ident, _) => ident.name.clone(),
2859 TypeExpr::List(_) => "List".to_string(),
2860 TypeExpr::Map(_, _) => "Map".to_string(),
2861 TypeExpr::Option(_) => "Option".to_string(),
2862 TypeExpr::Result(_, _) => "Result".to_string(),
2863 TypeExpr::Oracle(_) => "Oracle".to_string(),
2864 TypeExpr::Agent(_) => "Agent".to_string(),
2865 TypeExpr::Tuple(_) => "Tuple".to_string(),
2866 TypeExpr::Fn(_, _) => "Fn".to_string(),
2867 TypeExpr::Error => "Error".to_string(),
2868 }
2869 }
2870
2871 fn generate_effect_handler(&mut self, handler: &EffectHandlerDecl) {
2887 let name = &handler.name.name;
2888 let effect = &handler.effect.name;
2889
2890 self.emit.write("// Effect handler: ");
2892 self.emit.write(name);
2893 self.emit.write(" handles ");
2894 self.emit.writeln(effect);
2895
2896 if effect == "Infer" {
2898 if handler.is_pub {
2900 self.emit.write("pub ");
2901 }
2902 self.emit.write("mod handler_");
2903 self.emit.write(&Self::to_snake_case(name));
2904 self.emit.writeln(" {");
2905 self.emit.indent();
2906
2907 self.emit.writeln("#[derive(Debug, Clone)]");
2908 self.emit.writeln("pub struct Config {");
2909 self.emit.indent();
2910
2911 for config in &handler.config {
2913 self.emit.write("pub ");
2914 self.emit.write(&config.key.name);
2915 self.emit.write(": ");
2916 match &config.value {
2918 Literal::Int(_) => self.emit.write("i64"),
2919 Literal::Float(_) => self.emit.write("f64"),
2920 Literal::String(_) => self.emit.write("&'static str"),
2921 Literal::Bool(_) => self.emit.write("bool"),
2922 }
2923 self.emit.writeln(",");
2924 }
2925
2926 self.emit.dedent();
2927 self.emit.writeln("}");
2928 self.emit.blank_line();
2929
2930 self.emit.write("pub const CONFIG: Config = Config ");
2932 self.emit.writeln("{");
2933 self.emit.indent();
2934
2935 for config in &handler.config {
2936 self.emit.write(&config.key.name);
2937 self.emit.write(": ");
2938 match &config.value {
2939 Literal::Int(n) => self.emit.write(&n.to_string()),
2940 Literal::Float(f) => {
2941 let s = f.to_string();
2942 self.emit.write(&s);
2943 if !s.contains('.') {
2944 self.emit.write(".0");
2945 }
2946 }
2947 Literal::String(s) => {
2948 self.emit.write("\"");
2949 self.emit.write(s);
2950 self.emit.write("\"");
2951 }
2952 Literal::Bool(b) => self.emit.write(&b.to_string()),
2953 }
2954 self.emit.writeln(",");
2955 }
2956
2957 self.emit.dedent();
2958 self.emit.writeln("};");
2959
2960 self.emit.dedent();
2961 self.emit.writeln("}");
2962 } else {
2963 self.emit.write("// TODO: Handler for effect '");
2965 self.emit.write(effect);
2966 self.emit.writeln("' not yet implemented");
2967 }
2968 }
2969
2970 fn generate_block(&mut self, block: &Block) {
2971 self.emit.open_brace();
2972 self.generate_block_contents(block);
2973 self.emit.close_brace();
2974 }
2975
2976 fn generate_block_inline(&mut self, block: &Block) {
2977 self.emit.open_brace();
2978 self.generate_block_contents(block);
2979 self.emit.close_brace_inline();
2980 }
2981
2982 fn generate_block_contents(&mut self, block: &Block) {
2983 self.collect_reassigned_vars(block);
2985 for stmt in &block.stmts {
2986 self.generate_stmt(stmt);
2987 }
2988 }
2989
2990 fn generate_stmt(&mut self, stmt: &Stmt) {
2991 match stmt {
2992 Stmt::Let {
2993 name, ty, value, ..
2994 } => {
2995 if self.reassigned_vars.contains(&name.name) {
2997 self.emit.write("let mut ");
2998 } else {
2999 self.emit.write("let ");
3000 }
3001 if ty.is_some() {
3002 self.emit.write(&name.name);
3003 self.emit.write(": ");
3004 self.emit_type(ty.as_ref().unwrap());
3005 } else {
3006 self.emit.write(&name.name);
3007 }
3008 self.emit.write(" = ");
3009 self.generate_expr(value);
3010 self.emit.writeln(";");
3011 }
3012
3013 Stmt::Assign { name, value, .. } => {
3014 self.emit.write(&name.name);
3015 self.emit.write(" = ");
3016 self.generate_expr(value);
3017 self.emit.writeln(";");
3018 }
3019
3020 Stmt::Return { value, .. } => {
3021 self.emit.write("return ");
3022 if let Some(expr) = value {
3023 self.generate_expr(expr);
3024 }
3025 self.emit.writeln(";");
3026 }
3027
3028 Stmt::If {
3029 condition,
3030 then_block,
3031 else_block,
3032 ..
3033 } => {
3034 self.emit.write("if ");
3035 self.generate_expr(condition);
3036 self.emit.write(" ");
3037 if else_block.is_some() {
3038 self.generate_block_inline(then_block);
3039 self.emit.write(" else ");
3040 match else_block.as_ref().unwrap() {
3041 sage_parser::ElseBranch::Block(block) => {
3042 self.generate_block(block);
3043 }
3044 sage_parser::ElseBranch::ElseIf(stmt) => {
3045 self.generate_stmt(stmt);
3046 }
3047 }
3048 } else {
3049 self.generate_block(then_block);
3050 }
3051 }
3052
3053 Stmt::For {
3054 pattern,
3055 iter,
3056 body,
3057 ..
3058 } => {
3059 self.emit.write("for ");
3060 self.emit_pattern(pattern);
3061 self.emit.write(" in ");
3062 self.generate_expr(iter);
3063 self.emit.write(" ");
3064 self.generate_block(body);
3065 }
3066
3067 Stmt::While {
3068 condition, body, ..
3069 } => {
3070 self.emit.write("while ");
3071 self.generate_expr(condition);
3072 self.emit.write(" ");
3073 self.generate_block(body);
3074 }
3075
3076 Stmt::Loop { body, .. } => {
3077 self.emit.write("loop ");
3078 self.generate_block(body);
3079 }
3080
3081 Stmt::Break { .. } => {
3082 self.emit.writeln("break;");
3083 }
3084
3085 Stmt::SpanBlock { name, body, .. } => {
3086 self.emit.writeln("{");
3092 self.emit.indent();
3093 self.emit.write("let __span_name = ");
3094 self.generate_expr(name);
3095 self.emit.writeln(";");
3096 self.emit
3097 .writeln("let __span_start = std::time::Instant::now();");
3098 self.emit
3099 .writeln("sage_runtime::trace::span_start(&__span_name);");
3100 self.generate_block(body);
3102 self.emit.writeln(
3103 "sage_runtime::trace::span_end(&__span_name, __span_start.elapsed().as_millis() as u64);",
3104 );
3105 self.emit.dedent();
3106 self.emit.writeln("}");
3107 }
3108
3109 Stmt::Checkpoint { .. } => {
3110 if self.current_agent_persistent_beliefs.is_empty() {
3113 self.emit.writeln("// checkpoint() - no @persistent fields");
3115 } else {
3116 self.emit
3117 .writeln("// checkpoint() - save all @persistent beliefs");
3118 for field_name in &self.current_agent_persistent_beliefs.clone() {
3119 self.emit.write("self_");
3120 self.emit.write(field_name);
3121 self.emit.writeln(".checkpoint();");
3122 }
3123 }
3124 }
3125
3126 Stmt::Expr { expr, .. } => {
3127 if let Expr::Yield { value, .. } = expr {
3129 self.emit.write("return ctx.emit(");
3130 self.generate_expr(value);
3131 self.emit.writeln(");");
3132 } else if let Expr::Call { name, args, .. } = expr {
3133 if self.is_assertion_builtin(&name.name) {
3135 self.generate_assertion(&name.name, args);
3136 } else {
3137 self.generate_expr(expr);
3138 self.emit.writeln(";");
3139 }
3140 } else {
3141 self.generate_expr(expr);
3142 self.emit.writeln(";");
3143 }
3144 }
3145
3146 Stmt::LetTuple { names, value, .. } => {
3147 self.emit.write("let (");
3148 for (i, name) in names.iter().enumerate() {
3149 if i > 0 {
3150 self.emit.write(", ");
3151 }
3152 self.emit.write(&name.name);
3153 }
3154 self.emit.write(") = ");
3155 self.generate_expr(value);
3156 self.emit.writeln(";");
3157 }
3158
3159 Stmt::MockDivine { value, .. } => {
3161 self.emit.write("// mock divine: ");
3164 match value {
3165 sage_parser::MockValue::Value(expr) => {
3166 self.generate_expr(expr);
3167 }
3168 sage_parser::MockValue::Fail(expr) => {
3169 self.emit.write("fail(");
3170 self.generate_expr(expr);
3171 self.emit.write(")");
3172 }
3173 }
3174 self.emit.writeln(";");
3175 }
3176
3177 Stmt::MockTool {
3179 tool_name,
3180 fn_name,
3181 value,
3182 ..
3183 } => {
3184 self.emit.write("// mock tool ");
3187 self.emit.write(&tool_name.name);
3188 self.emit.write(".");
3189 self.emit.write(&fn_name.name);
3190 self.emit.write(": ");
3191 match value {
3192 sage_parser::MockValue::Value(expr) => {
3193 self.generate_expr(expr);
3194 }
3195 sage_parser::MockValue::Fail(expr) => {
3196 self.emit.write("fail(");
3197 self.generate_expr(expr);
3198 self.emit.write(")");
3199 }
3200 }
3201 self.emit.writeln(";");
3202 }
3203 }
3204 }
3205
3206 fn generate_expr(&mut self, expr: &Expr) {
3207 match expr {
3208 Expr::Literal { value, .. } => {
3209 self.emit_literal(value);
3210 }
3211
3212 Expr::Var { name, .. } => {
3213 match name.name.as_str() {
3215 "PI" => self.emit.write("std::f64::consts::PI"),
3216 "E" => self.emit.write("std::f64::consts::E"),
3217 "MS_PER_SECOND" => self.emit.write("1000_i64"),
3219 "MS_PER_MINUTE" => self.emit.write("60000_i64"),
3220 "MS_PER_HOUR" => self.emit.write("3600000_i64"),
3221 "MS_PER_DAY" => self.emit.write("86400000_i64"),
3222 _ => {
3223 self.emit.write(&name.name);
3224 if self.string_consts.contains(&name.name) {
3225 self.emit.write(".to_string()");
3226 }
3227 }
3228 }
3229 }
3230
3231 Expr::Binary {
3232 op, left, right, ..
3233 } => {
3234 if matches!(op, BinOp::Concat) {
3236 self.emit.write("format!(\"{}{}\", ");
3237 self.generate_expr(left);
3238 self.emit.write(", ");
3239 self.generate_expr(right);
3240 self.emit.write(")");
3241 } else {
3242 self.generate_expr(left);
3243 self.emit.write(" ");
3244 self.emit_binop(op);
3245 self.emit.write(" ");
3246 self.generate_expr(right);
3247 }
3248 }
3249
3250 Expr::Unary { op, operand, .. } => {
3251 self.emit_unaryop(op);
3252 self.generate_expr(operand);
3253 }
3254
3255 Expr::Call {
3256 name,
3257 type_args,
3258 args,
3259 ..
3260 } => {
3261 let fn_name = &name.name;
3262
3263 match fn_name.as_str() {
3265 "print" => {
3266 self.emit.write("println!(\"{}\", ");
3267 self.generate_expr(&args[0]);
3268 self.emit.write(")");
3269 }
3270 "str" => {
3271 self.generate_expr(&args[0]);
3272 self.emit.write(".to_string()");
3273 }
3274 "len" => {
3275 self.generate_expr(&args[0]);
3276 self.emit.write(".len() as i64");
3277 }
3278
3279 "split" => {
3281 self.generate_expr(&args[0]);
3282 self.emit.write(".split(&*");
3283 self.generate_expr(&args[1]);
3284 self.emit.write(").map(str::to_string).collect::<Vec<_>>()");
3285 }
3286 "lines" => {
3287 self.generate_expr(&args[0]);
3288 self.emit
3289 .write(".lines().map(str::to_string).collect::<Vec<_>>()");
3290 }
3291 "chars" => {
3292 self.generate_expr(&args[0]);
3293 self.emit
3294 .write(".chars().map(|c| c.to_string()).collect::<Vec<_>>()");
3295 }
3296 "join" => {
3297 self.generate_expr(&args[0]);
3298 self.emit.write(".join(&*");
3299 self.generate_expr(&args[1]);
3300 self.emit.write(")");
3301 }
3302 "trim" => {
3303 self.generate_expr(&args[0]);
3304 self.emit.write(".trim().to_string()");
3305 }
3306 "trim_start" => {
3307 self.generate_expr(&args[0]);
3308 self.emit.write(".trim_start().to_string()");
3309 }
3310 "trim_end" => {
3311 self.generate_expr(&args[0]);
3312 self.emit.write(".trim_end().to_string()");
3313 }
3314 "starts_with" => {
3315 self.generate_expr(&args[0]);
3316 self.emit.write(".starts_with(&*");
3317 self.generate_expr(&args[1]);
3318 self.emit.write(")");
3319 }
3320 "ends_with" => {
3321 self.generate_expr(&args[0]);
3322 self.emit.write(".ends_with(&*");
3323 self.generate_expr(&args[1]);
3324 self.emit.write(")");
3325 }
3326 "str_contains" => {
3327 self.generate_expr(&args[0]);
3328 self.emit.write(".contains(&*");
3329 self.generate_expr(&args[1]);
3330 self.emit.write(")");
3331 }
3332 "replace" => {
3333 self.generate_expr(&args[0]);
3334 self.emit.write(".replace(&*");
3335 self.generate_expr(&args[1]);
3336 self.emit.write(", &*");
3337 self.generate_expr(&args[2]);
3338 self.emit.write(")");
3339 }
3340 "replace_first" => {
3341 self.generate_expr(&args[0]);
3342 self.emit.write(".replacen(&*");
3343 self.generate_expr(&args[1]);
3344 self.emit.write(", &*");
3345 self.generate_expr(&args[2]);
3346 self.emit.write(", 1)");
3347 }
3348 "to_upper" => {
3349 self.generate_expr(&args[0]);
3350 self.emit.write(".to_uppercase()");
3351 }
3352 "to_lower" => {
3353 self.generate_expr(&args[0]);
3354 self.emit.write(".to_lowercase()");
3355 }
3356 "str_len" => {
3357 self.generate_expr(&args[0]);
3358 self.emit.write(".chars().count() as i64");
3359 }
3360 "str_slice" => {
3361 self.emit.write("sage_runtime::stdlib::str_slice(&");
3362 self.generate_expr(&args[0]);
3363 self.emit.write(", ");
3364 self.generate_expr(&args[1]);
3365 self.emit.write(", ");
3366 self.generate_expr(&args[2]);
3367 self.emit.write(")");
3368 }
3369 "str_index_of" => {
3370 self.emit.write("sage_runtime::stdlib::str_index_of(&");
3371 self.generate_expr(&args[0]);
3372 self.emit.write(", &");
3373 self.generate_expr(&args[1]);
3374 self.emit.write(")");
3375 }
3376 "str_repeat" => {
3377 self.generate_expr(&args[0]);
3378 self.emit.write(".repeat(");
3379 self.generate_expr(&args[1]);
3380 self.emit.write(" as usize)");
3381 }
3382 "str_pad_start" => {
3383 self.emit.write("sage_runtime::stdlib::str_pad_start(&");
3384 self.generate_expr(&args[0]);
3385 self.emit.write(", ");
3386 self.generate_expr(&args[1]);
3387 self.emit.write(", &");
3388 self.generate_expr(&args[2]);
3389 self.emit.write(")");
3390 }
3391 "str_pad_end" => {
3392 self.emit.write("sage_runtime::stdlib::str_pad_end(&");
3393 self.generate_expr(&args[0]);
3394 self.emit.write(", ");
3395 self.generate_expr(&args[1]);
3396 self.emit.write(", &");
3397 self.generate_expr(&args[2]);
3398 self.emit.write(")");
3399 }
3400
3401 "chr" => {
3402 self.emit.write("sage_runtime::stdlib::chr(");
3403 self.generate_expr(&args[0]);
3404 self.emit.write(")");
3405 }
3406
3407 "abs" => {
3409 self.emit.write("(");
3410 self.generate_expr(&args[0]);
3411 self.emit.write(").abs()");
3412 }
3413 "abs_float" => {
3414 self.emit.write("(");
3415 self.generate_expr(&args[0]);
3416 self.emit.write(").abs()");
3417 }
3418 "min" => {
3419 self.generate_expr(&args[0]);
3420 self.emit.write(".min(");
3421 self.generate_expr(&args[1]);
3422 self.emit.write(")");
3423 }
3424 "max" => {
3425 self.generate_expr(&args[0]);
3426 self.emit.write(".max(");
3427 self.generate_expr(&args[1]);
3428 self.emit.write(")");
3429 }
3430 "min_float" => {
3431 self.generate_expr(&args[0]);
3432 self.emit.write(".min(");
3433 self.generate_expr(&args[1]);
3434 self.emit.write(")");
3435 }
3436 "max_float" => {
3437 self.generate_expr(&args[0]);
3438 self.emit.write(".max(");
3439 self.generate_expr(&args[1]);
3440 self.emit.write(")");
3441 }
3442 "clamp" => {
3443 self.emit.write("(");
3444 self.generate_expr(&args[0]);
3445 self.emit.write(").clamp(");
3446 self.generate_expr(&args[1]);
3447 self.emit.write(", ");
3448 self.generate_expr(&args[2]);
3449 self.emit.write(")");
3450 }
3451 "clamp_float" => {
3452 self.emit.write("(");
3453 self.generate_expr(&args[0]);
3454 self.emit.write(").clamp(");
3455 self.generate_expr(&args[1]);
3456 self.emit.write(", ");
3457 self.generate_expr(&args[2]);
3458 self.emit.write(")");
3459 }
3460 "floor" => {
3461 self.generate_expr(&args[0]);
3462 self.emit.write(".floor() as i64");
3463 }
3464 "ceil" => {
3465 self.generate_expr(&args[0]);
3466 self.emit.write(".ceil() as i64");
3467 }
3468 "round" => {
3469 self.generate_expr(&args[0]);
3470 self.emit.write(".round() as i64");
3471 }
3472 "floor_float" => {
3473 self.generate_expr(&args[0]);
3474 self.emit.write(".floor()");
3475 }
3476 "ceil_float" => {
3477 self.generate_expr(&args[0]);
3478 self.emit.write(".ceil()");
3479 }
3480 "pow" => {
3481 self.emit.write("{ let __base = ");
3483 self.generate_expr(&args[0]);
3484 self.emit.write("; let __exp = ");
3485 self.generate_expr(&args[1]);
3486 self.emit
3487 .write("; if __exp < 0 { 0 } else { __base.pow(__exp as u32) } }");
3488 }
3489 "pow_float" => {
3490 self.generate_expr(&args[0]);
3491 self.emit.write(".powf(");
3492 self.generate_expr(&args[1]);
3493 self.emit.write(")");
3494 }
3495 "sqrt" => {
3496 self.generate_expr(&args[0]);
3497 self.emit.write(".sqrt()");
3498 }
3499 "int_to_float" => {
3500 self.generate_expr(&args[0]);
3501 self.emit.write(" as f64");
3502 }
3503 "float_to_int" => {
3504 self.generate_expr(&args[0]);
3505 self.emit.write(" as i64");
3506 }
3507
3508 "parse_int" => {
3510 self.generate_expr(&args[0]);
3511 self.emit
3512 .write(".trim().parse::<i64>().map_err(|e| e.to_string())");
3513 }
3514 "parse_float" => {
3515 self.generate_expr(&args[0]);
3516 self.emit
3517 .write(".trim().parse::<f64>().map_err(|e| e.to_string())");
3518 }
3519 "parse_bool" => {
3520 self.emit.write("sage_runtime::stdlib::parse_bool(&");
3521 self.generate_expr(&args[0]);
3522 self.emit.write(")");
3523 }
3524 "float_to_str" => {
3525 self.generate_expr(&args[0]);
3526 self.emit.write(".to_string()");
3527 }
3528 "bool_to_str" => {
3529 self.emit.write("if ");
3530 self.generate_expr(&args[0]);
3531 self.emit
3532 .write(" { \"true\".to_string() } else { \"false\".to_string() }");
3533 }
3534 "int_to_str" => {
3535 self.emit.write("(");
3536 self.generate_expr(&args[0]);
3537 self.emit.write(").to_string()");
3538 }
3539
3540 "map" => {
3542 self.generate_expr(&args[0]);
3543 self.emit.write(".into_iter().map(");
3544 self.generate_expr(&args[1]);
3545 self.emit.write(").collect::<Vec<_>>()");
3546 }
3547 "filter" => {
3548 self.generate_expr(&args[0]);
3549 self.emit.write(".into_iter().filter(|__x| (");
3550 self.generate_expr(&args[1]);
3551 self.emit.write(")((__x).clone())).collect::<Vec<_>>()");
3552 }
3553 "reduce" => {
3554 self.generate_expr(&args[0]);
3555 self.emit.write(".into_iter().fold(");
3556 self.generate_expr(&args[1]);
3557 self.emit.write(", ");
3558 self.generate_expr(&args[2]);
3559 self.emit.write(")");
3560 }
3561 "any" => {
3562 self.generate_expr(&args[0]);
3563 self.emit.write(".into_iter().any(|__x| (");
3564 self.generate_expr(&args[1]);
3565 self.emit.write(")((__x).clone()))");
3566 }
3567 "all" => {
3568 self.generate_expr(&args[0]);
3569 self.emit.write(".into_iter().all(|__x| (");
3570 self.generate_expr(&args[1]);
3571 self.emit.write(")((__x).clone()))");
3572 }
3573 "find" => {
3574 self.generate_expr(&args[0]);
3575 self.emit.write(".into_iter().find(|__x| (");
3576 self.generate_expr(&args[1]);
3577 self.emit.write(")((__x).clone()))");
3578 }
3579 "flat_map" => {
3580 self.generate_expr(&args[0]);
3581 self.emit.write(".into_iter().flat_map(");
3582 self.generate_expr(&args[1]);
3583 self.emit.write(").collect::<Vec<_>>()");
3584 }
3585 "zip" => {
3586 self.generate_expr(&args[0]);
3587 self.emit.write(".into_iter().zip(");
3588 self.generate_expr(&args[1]);
3589 self.emit.write(".into_iter()).collect::<Vec<_>>()");
3590 }
3591 "sort_by" => {
3592 self.emit.write("{ let mut __v = ");
3593 self.generate_expr(&args[0]);
3594 self.emit.write("; __v.sort_by(|__a, __b| { let __cmp = (");
3595 self.generate_expr(&args[1]);
3596 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 }");
3597 }
3598 "enumerate" => {
3599 self.generate_expr(&args[0]);
3600 self.emit
3601 .write(".into_iter().enumerate().map(|(__i, __x)| (__i as i64, __x)).collect::<Vec<_>>()");
3602 }
3603 "take" => {
3604 self.generate_expr(&args[0]);
3605 self.emit.write(".into_iter().take(");
3606 self.generate_expr(&args[1]);
3607 self.emit.write(" as usize).collect::<Vec<_>>()");
3608 }
3609 "drop" => {
3610 self.generate_expr(&args[0]);
3611 self.emit.write(".into_iter().skip(");
3612 self.generate_expr(&args[1]);
3613 self.emit.write(" as usize).collect::<Vec<_>>()");
3614 }
3615 "flatten" => {
3616 self.generate_expr(&args[0]);
3617 self.emit
3618 .write(".into_iter().flatten().collect::<Vec<_>>()");
3619 }
3620 "reverse" => {
3621 self.emit.write("{ let mut __v = ");
3622 self.generate_expr(&args[0]);
3623 self.emit.write("; __v.reverse(); __v }");
3624 }
3625 "unique" => {
3626 self.emit
3627 .write("{ let mut __seen = std::collections::HashSet::new(); ");
3628 self.generate_expr(&args[0]);
3629 self.emit.write(".into_iter().filter(|__x| __seen.insert(format!(\"{:?}\", __x))).collect::<Vec<_>>() }");
3630 }
3631 "count_where" => {
3632 self.generate_expr(&args[0]);
3633 self.emit.write(".into_iter().filter(|__x| (");
3634 self.generate_expr(&args[1]);
3635 self.emit.write(")((__x).clone())).count() as i64");
3636 }
3637 "sum" => {
3638 self.generate_expr(&args[0]);
3639 self.emit.write(".iter().sum::<i64>()");
3640 }
3641 "sum_floats" => {
3642 self.generate_expr(&args[0]);
3643 self.emit.write(".iter().sum::<f64>()");
3644 }
3645
3646 "range" => {
3650 self.emit.write("(");
3651 self.generate_expr(&args[0]);
3652 self.emit.write("..");
3653 self.generate_expr(&args[1]);
3654 self.emit.write(").collect::<Vec<_>>()");
3655 }
3656 "range_step" => {
3657 self.emit.write("(");
3658 self.generate_expr(&args[0]);
3659 self.emit.write("..");
3660 self.generate_expr(&args[1]);
3661 self.emit.write(").step_by(");
3662 self.generate_expr(&args[2]);
3663 self.emit.write(" as usize).collect::<Vec<_>>()");
3664 }
3665 "first" => {
3666 self.generate_expr(&args[0]);
3667 self.emit.write(".first().cloned()");
3668 }
3669 "last" => {
3670 self.generate_expr(&args[0]);
3671 self.emit.write(".last().cloned()");
3672 }
3673 "get" => {
3674 self.generate_expr(&args[0]);
3675 self.emit.write(".get(");
3676 self.generate_expr(&args[1]);
3677 self.emit.write(" as usize).cloned()");
3678 }
3679 "list_contains" => {
3680 self.generate_expr(&args[0]);
3681 self.emit.write(".contains(&");
3682 self.generate_expr(&args[1]);
3683 self.emit.write(")");
3684 }
3685 "sort" => {
3686 self.emit.write("{ let mut __v = ");
3687 self.generate_expr(&args[0]);
3688 self.emit.write("; __v.sort(); __v }");
3689 }
3690 "list_slice" => {
3691 self.emit.write("sage_runtime::stdlib::list_slice(");
3692 self.generate_expr(&args[0]);
3693 self.emit.write(", ");
3694 self.generate_expr(&args[1]);
3695 self.emit.write(", ");
3696 self.generate_expr(&args[2]);
3697 self.emit.write(")");
3698 }
3699 "chunk" => {
3700 self.generate_expr(&args[0]);
3701 self.emit.write(".chunks(");
3702 self.generate_expr(&args[1]);
3703 self.emit
3704 .write(" as usize).map(|c| c.to_vec()).collect::<Vec<_>>()");
3705 }
3706 "pop" => {
3707 self.emit.write("{ let mut __v = ");
3708 self.generate_expr(&args[0]);
3709 self.emit.write("; __v.pop() }");
3710 }
3711 "push" => {
3712 self.emit.write("{ let mut __v = ");
3713 self.generate_expr(&args[0]);
3714 self.emit.write("; __v.push(");
3715 self.generate_expr(&args[1]);
3716 self.emit.write("); __v }");
3717 }
3718 "concat" => {
3719 self.emit.write("{ let mut __v = ");
3720 self.generate_expr(&args[0]);
3721 self.emit.write("; __v.extend(");
3722 self.generate_expr(&args[1]);
3723 self.emit.write("); __v }");
3724 }
3725 "take_while" => {
3726 self.generate_expr(&args[0]);
3727 self.emit.write(".into_iter().take_while(|__x| (");
3728 self.generate_expr(&args[1]);
3729 self.emit.write(")((__x).clone())).collect::<Vec<_>>()");
3730 }
3731 "drop_while" => {
3732 self.generate_expr(&args[0]);
3733 self.emit.write(".into_iter().skip_while(|__x| (");
3734 self.generate_expr(&args[1]);
3735 self.emit.write(")((__x).clone())).collect::<Vec<_>>()");
3736 }
3737
3738 "is_some" => {
3742 self.generate_expr(&args[0]);
3743 self.emit.write(".is_some()");
3744 }
3745 "is_none" => {
3746 self.generate_expr(&args[0]);
3747 self.emit.write(".is_none()");
3748 }
3749 "unwrap" => {
3750 self.generate_expr(&args[0]);
3751 self.emit.write(".expect(\"unwrap called on None\")");
3752 }
3753 "unwrap_or" => {
3754 self.generate_expr(&args[0]);
3755 self.emit.write(".unwrap_or(");
3756 self.generate_expr(&args[1]);
3757 self.emit.write(")");
3758 }
3759 "unwrap_or_else" => {
3760 self.generate_expr(&args[0]);
3761 self.emit.write(".unwrap_or_else(");
3762 self.generate_expr(&args[1]);
3763 self.emit.write(")");
3764 }
3765 "map_option" => {
3766 self.generate_expr(&args[0]);
3767 self.emit.write(".map(");
3768 self.generate_expr(&args[1]);
3769 self.emit.write(")");
3770 }
3771 "or_option" => {
3772 self.generate_expr(&args[0]);
3773 self.emit.write(".or(");
3774 self.generate_expr(&args[1]);
3775 self.emit.write(")");
3776 }
3777
3778 "is_ok" => {
3782 self.generate_expr(&args[0]);
3783 self.emit.write(".is_ok()");
3784 }
3785 "is_err" => {
3786 self.generate_expr(&args[0]);
3787 self.emit.write(".is_err()");
3788 }
3789 "unwrap_result" => {
3790 self.generate_expr(&args[0]);
3791 self.emit.write(".expect(\"unwrap_result called on Err\")");
3792 }
3793 "unwrap_err" => {
3794 self.generate_expr(&args[0]);
3795 self.emit.write(".expect_err(\"unwrap_err called on Ok\")");
3796 }
3797 "unwrap_or_result" => {
3798 self.generate_expr(&args[0]);
3799 self.emit.write(".unwrap_or(");
3800 self.generate_expr(&args[1]);
3801 self.emit.write(")");
3802 }
3803 "map_result" => {
3804 self.generate_expr(&args[0]);
3805 self.emit.write(".map(");
3806 self.generate_expr(&args[1]);
3807 self.emit.write(")");
3808 }
3809 "map_err" => {
3810 self.generate_expr(&args[0]);
3811 self.emit.write(".map_err(");
3812 self.generate_expr(&args[1]);
3813 self.emit.write(")");
3814 }
3815 "ok" => {
3816 self.generate_expr(&args[0]);
3817 self.emit.write(".ok()");
3818 }
3819 "err_value" => {
3820 self.generate_expr(&args[0]);
3821 self.emit.write(".err()");
3822 }
3823
3824 "read_file" => {
3828 self.emit.write("sage_runtime::stdlib::read_file(&");
3829 self.generate_expr(&args[0]);
3830 self.emit
3831 .write(").map_err(sage_runtime::SageError::agent)?");
3832 }
3833 "write_file" => {
3834 self.emit.write("sage_runtime::stdlib::write_file(&");
3835 self.generate_expr(&args[0]);
3836 self.emit.write(", &");
3837 self.generate_expr(&args[1]);
3838 self.emit
3839 .write(").map_err(sage_runtime::SageError::agent)?");
3840 }
3841 "append_file" => {
3842 self.emit.write("sage_runtime::stdlib::append_file(&");
3843 self.generate_expr(&args[0]);
3844 self.emit.write(", &");
3845 self.generate_expr(&args[1]);
3846 self.emit
3847 .write(").map_err(sage_runtime::SageError::agent)?");
3848 }
3849 "file_exists" => {
3850 self.emit.write("sage_runtime::stdlib::file_exists(&");
3851 self.generate_expr(&args[0]);
3852 self.emit.write(")");
3853 }
3854 "delete_file" => {
3855 self.emit.write("sage_runtime::stdlib::delete_file(&");
3856 self.generate_expr(&args[0]);
3857 self.emit
3858 .write(").map_err(sage_runtime::SageError::agent)?");
3859 }
3860 "list_dir" => {
3861 self.emit.write("sage_runtime::stdlib::list_dir(&");
3862 self.generate_expr(&args[0]);
3863 self.emit
3864 .write(").map_err(sage_runtime::SageError::agent)?");
3865 }
3866 "make_dir" => {
3867 self.emit.write("sage_runtime::stdlib::make_dir(&");
3868 self.generate_expr(&args[0]);
3869 self.emit
3870 .write(").map_err(sage_runtime::SageError::agent)?");
3871 }
3872 "read_line" => {
3873 self.emit.write("sage_runtime::stdlib::read_line().map_err(sage_runtime::SageError::agent)");
3874 }
3875 "read_all" => {
3876 self.emit.write("sage_runtime::stdlib::read_all().map_err(sage_runtime::SageError::agent)");
3877 }
3878 "print_err" => {
3879 self.emit.write("eprintln!(\"{}\", ");
3880 self.generate_expr(&args[0]);
3881 self.emit.write(")");
3882 }
3883
3884 "now_ms" => {
3888 self.emit.write("sage_runtime::stdlib::now_ms()");
3889 }
3890 "now_s" => {
3891 self.emit.write("sage_runtime::stdlib::now_s()");
3892 }
3893 "sleep_ms" => {
3894 self.emit
3895 .write("tokio::time::sleep(std::time::Duration::from_millis(");
3896 self.generate_expr(&args[0]);
3897 self.emit.write(" as u64)).await");
3898 }
3899 "format_timestamp" => {
3900 self.emit.write("sage_runtime::stdlib::format_timestamp(");
3901 self.generate_expr(&args[0]);
3902 self.emit.write(", &");
3903 self.generate_expr(&args[1]);
3904 self.emit.write(")");
3905 }
3906 "parse_timestamp" => {
3907 self.emit.write("sage_runtime::stdlib::parse_timestamp(&");
3908 self.generate_expr(&args[0]);
3909 self.emit.write(", &");
3910 self.generate_expr(&args[1]);
3911 self.emit
3912 .write(").map_err(sage_runtime::SageError::agent)?");
3913 }
3914
3915 "json_parse" => {
3919 self.emit.write("sage_runtime::stdlib::json_parse(&");
3920 self.generate_expr(&args[0]);
3921 self.emit
3922 .write(").map_err(sage_runtime::SageError::agent)?");
3923 }
3924 "json_get" => {
3925 self.emit.write("sage_runtime::stdlib::json_get(&");
3926 self.generate_expr(&args[0]);
3927 self.emit.write(", &");
3928 self.generate_expr(&args[1]);
3929 self.emit.write(")");
3930 }
3931 "json_get_int" => {
3932 self.emit.write("sage_runtime::stdlib::json_get_int(&");
3933 self.generate_expr(&args[0]);
3934 self.emit.write(", &");
3935 self.generate_expr(&args[1]);
3936 self.emit.write(")");
3937 }
3938 "json_get_float" => {
3939 self.emit.write("sage_runtime::stdlib::json_get_float(&");
3940 self.generate_expr(&args[0]);
3941 self.emit.write(", &");
3942 self.generate_expr(&args[1]);
3943 self.emit.write(")");
3944 }
3945 "json_get_bool" => {
3946 self.emit.write("sage_runtime::stdlib::json_get_bool(&");
3947 self.generate_expr(&args[0]);
3948 self.emit.write(", &");
3949 self.generate_expr(&args[1]);
3950 self.emit.write(")");
3951 }
3952 "json_get_list" => {
3953 self.emit.write("sage_runtime::stdlib::json_get_list(&");
3954 self.generate_expr(&args[0]);
3955 self.emit.write(", &");
3956 self.generate_expr(&args[1]);
3957 self.emit.write(")");
3958 }
3959 "json_stringify" => {
3960 self.emit
3961 .write("sage_runtime::stdlib::json_stringify_string(&");
3962 self.generate_expr(&args[0]);
3963 self.emit.write(".to_string())");
3964 }
3965
3966 name if self.extern_fn_names.contains(name) => {
3967 self.emit.write("sage_extern::");
3969 self.emit.write(name);
3970 self.emit.write("(");
3971 for (i, arg) in args.iter().enumerate() {
3972 if i > 0 {
3973 self.emit.write(", ");
3974 }
3975 self.emit.write("(");
3977 self.generate_expr(arg);
3978 self.emit.write(").clone()");
3979 }
3980 self.emit.write(")");
3981 if self.extern_fn_fallible.contains(name) {
3983 self.emit.write(".map_err(sage_runtime::SageError::agent)?");
3984 }
3985 }
3986
3987 _ => {
3988 self.emit.write(fn_name);
3990 if !type_args.is_empty() {
3992 self.emit.write("::<");
3993 for (i, arg) in type_args.iter().enumerate() {
3994 if i > 0 {
3995 self.emit.write(", ");
3996 }
3997 self.emit_type(arg);
3998 }
3999 self.emit.write(">");
4000 }
4001 self.emit.write("(");
4002 for (i, arg) in args.iter().enumerate() {
4003 if i > 0 {
4004 self.emit.write(", ");
4005 }
4006 self.emit.write("(");
4009 self.generate_expr(arg);
4010 self.emit.write(").clone()");
4011 }
4012 self.emit.write(")");
4013 }
4014 }
4015 }
4016
4017 Expr::SelfField { field, .. } => {
4018 self.emit.write("self.");
4019 self.emit.write(&field.name);
4020 }
4021
4022 Expr::Apply { callee, args, .. } => {
4023 self.generate_expr(callee);
4025 self.emit.write("(");
4026 for (i, arg) in args.iter().enumerate() {
4027 if i > 0 {
4028 self.emit.write(", ");
4029 }
4030 self.generate_expr(arg);
4031 }
4032 self.emit.write(")");
4033 }
4034
4035 Expr::SelfMethodCall { method, args, .. } => {
4036 self.emit.write("self.");
4037 self.emit.write(&method.name);
4038 self.emit.write("(");
4039 for (i, arg) in args.iter().enumerate() {
4040 if i > 0 {
4041 self.emit.write(", ");
4042 }
4043 self.generate_expr(arg);
4044 }
4045 self.emit.write(")");
4046 }
4047
4048 Expr::List { elements, .. } => {
4049 self.emit.write("vec![");
4050 for (i, elem) in elements.iter().enumerate() {
4051 if i > 0 {
4052 self.emit.write(", ");
4053 }
4054 self.generate_expr(elem);
4055 }
4056 self.emit.write("]");
4057 }
4058
4059 Expr::Paren { inner, .. } => {
4060 self.emit.write("(");
4061 self.generate_expr(inner);
4062 self.emit.write(")");
4063 }
4064
4065 Expr::Divine { template, .. } => {
4066 self.emit.write("ctx.infer_string(&");
4068 self.emit_string_template(template);
4069 self.emit.write(").await");
4070 }
4071
4072 Expr::Summon { agent, fields, .. } => {
4073 let has_error_handler = self.agents_with_error_handlers.contains(&agent.name);
4074 let message_handlers = self.agents_with_message_handlers.get(&agent.name).cloned();
4075 let tool_uses = self
4076 .agent_tool_uses
4077 .get(&agent.name)
4078 .cloned()
4079 .unwrap_or_default();
4080
4081 self.emit.write("{ ");
4083 for (i, field) in fields.iter().enumerate() {
4085 self.emit.write(&format!("let __sf{} = (", i));
4086 self.generate_expr(&field.value);
4087 self.emit.write(").clone(); ");
4088 }
4089
4090 self.emit
4091 .write("sage_runtime::spawn(move |mut ctx| async move { ");
4092 self.emit.write("let agent = ");
4093 self.emit.write(&agent.name);
4094 if fields.is_empty() && tool_uses.is_empty() {
4096 self.emit.write("; ");
4097 } else {
4098 self.emit.write(" { ");
4099 for (i, field) in fields.iter().enumerate() {
4100 if i > 0 {
4101 self.emit.write(", ");
4102 }
4103 self.emit.write(&field.name.name);
4104 self.emit.write(&format!(": __sf{}", i));
4105 }
4106 for tool_name in &tool_uses {
4108 if !fields.is_empty()
4109 || tool_uses.iter().position(|t| t == tool_name) != Some(0)
4110 {
4111 self.emit.write(", ");
4112 }
4113 self.emit.write(&tool_name.to_lowercase());
4114 self.emit.write(": ");
4115 if tool_name == "Database" {
4116 self.emit.write("DatabaseClient::from_env().await.expect(\"Failed to connect to database\")");
4117 } else {
4118 self.emit.write(tool_name);
4119 self.emit.write("Client::from_env()");
4120 }
4121 }
4122 self.emit.write(" }; ");
4123 }
4124
4125 if has_error_handler {
4127 self.emit
4128 .write("let result = match agent.on_start(&mut ctx).await { ");
4129 self.emit.write("Ok(result) => Ok(result), ");
4130 self.emit
4131 .write("Err(e) => agent.on_error(e, &mut ctx).await }; ");
4132 } else {
4133 self.emit
4134 .write("let result = agent.on_start(&mut ctx).await; ");
4135 }
4136
4137 if let Some(handlers) = message_handlers {
4139 self.emit.write("if result.is_ok() { loop { ");
4140 self.emit.write("match ctx.receive_raw().await { ");
4141 self.emit.write("Ok(msg) => { ");
4142 self.emit.write("match msg.type_name.as_deref() { ");
4143
4144 for (param_name, param_ty) in &handlers.handlers {
4146 let type_name = self.type_expr_to_string(param_ty);
4147 let method_name = format!("on_message_{}", Self::to_snake_case(&type_name));
4148
4149 self.emit.write("Some(\"");
4150 self.emit.write(&type_name);
4151 self.emit.write("\") => { ");
4152 self.emit.write("if let Ok(");
4153 self.emit.write(¶m_name.name);
4154 self.emit
4155 .write(") = serde_json::from_value(msg.payload) { ");
4156 self.emit.write("let _ = agent.");
4157 self.emit.write(&method_name);
4158 self.emit.write("(");
4159 self.emit.write(¶m_name.name);
4160 self.emit.write(", &mut ctx).await; } } ");
4161 }
4162
4163 self.emit.write("_ => {} } } "); self.emit.write("Err(_) => break } } } "); }
4166
4167 self.emit.write("result }) }");
4168 }
4169
4170 Expr::Await {
4171 handle, timeout, ..
4172 } => {
4173 if let Some(timeout_expr) = timeout {
4175 self.emit.write("tokio::time::timeout(");
4177 self.emit.write("std::time::Duration::from_millis(");
4178 self.generate_expr(timeout_expr);
4179 self.emit.write(" as u64), ");
4180 self.generate_expr(handle);
4181 self.emit
4182 .write(".result()).await.map_err(|_| sage_runtime::SageError::agent(");
4183 self.emit.write("\"await timed out\"))?");
4184 } else {
4185 self.generate_expr(handle);
4187 self.emit.write(".result().await");
4188 }
4189 }
4190
4191 Expr::Send {
4192 handle, message, ..
4193 } => {
4194 self.generate_expr(handle);
4195
4196 if let Some(type_name) = Self::extract_message_type_name(message) {
4199 self.emit.write(".send_message(Message::new(");
4201 self.generate_expr(message);
4202 self.emit.write(")?.with_type_name(\"");
4203 self.emit.write(&type_name);
4204 self.emit.write("\")).await");
4205 } else {
4206 self.emit.write(".send(");
4208 self.generate_expr(message);
4209 self.emit.write(").await");
4210 }
4211 }
4212
4213 Expr::Yield { value, .. } => {
4214 self.emit.write("ctx.emit(");
4215 self.generate_expr(value);
4216 self.emit.write(")");
4217 }
4218
4219 Expr::StringInterp { template, .. } => {
4220 self.emit_string_template(template);
4221 }
4222
4223 Expr::Match {
4224 scrutinee, arms, ..
4225 } => {
4226 self.emit.write("match ");
4227 self.generate_expr(scrutinee);
4228 self.emit.writeln(" {");
4229 self.emit.indent();
4230 for arm in arms {
4231 self.emit_pattern(&arm.pattern);
4232 self.emit.write(" => ");
4233 self.generate_expr(&arm.body);
4234 self.emit.writeln(",");
4235 }
4236 self.emit.dedent();
4237 self.emit.write("}");
4238 }
4239
4240 Expr::RecordConstruct {
4241 name,
4242 type_args,
4243 fields,
4244 ..
4245 } => {
4246 self.emit.write(&name.name);
4247 if !type_args.is_empty() {
4249 self.emit.write("::<");
4250 for (i, arg) in type_args.iter().enumerate() {
4251 if i > 0 {
4252 self.emit.write(", ");
4253 }
4254 self.emit_type(arg);
4255 }
4256 self.emit.write(">");
4257 }
4258 self.emit.write(" { ");
4259 for (i, field) in fields.iter().enumerate() {
4260 if i > 0 {
4261 self.emit.write(", ");
4262 }
4263 self.emit.write(&field.name.name);
4264 self.emit.write(": ");
4265 self.generate_expr(&field.value);
4266 }
4267 self.emit.write(" }");
4268 }
4269
4270 Expr::FieldAccess { object, field, .. } => {
4271 self.generate_expr(object);
4272 self.emit.write(".");
4273 self.emit.write(&field.name);
4274 }
4275
4276 Expr::Receive { .. } => {
4277 self.emit.write("ctx.receive().await?");
4278 }
4279
4280 Expr::Try { expr, .. } => {
4282 self.generate_expr(expr);
4284 self.emit.write("?");
4285 }
4286
4287 Expr::Catch {
4288 expr,
4289 error_bind,
4290 recovery,
4291 ..
4292 } => {
4293 self.emit.write("match ");
4296 if let Expr::Try { expr: inner, .. } = expr.as_ref() {
4297 self.generate_expr(inner);
4298 } else {
4299 self.generate_expr(expr);
4300 }
4301 self.emit.writeln(" {");
4302 self.emit.indent();
4303
4304 self.emit.writeln("Ok(__val) => __val,");
4306
4307 if let Some(err_name) = error_bind {
4309 self.emit.write("Err(");
4310 self.emit.write(&err_name.name);
4311 self.emit.write(") => ");
4312 } else {
4313 self.emit.write("Err(_) => ");
4314 }
4315 self.generate_expr(recovery);
4316 self.emit.writeln(",");
4317
4318 self.emit.dedent();
4319 self.emit.write("}");
4320 }
4321
4322 Expr::Fail { error, .. } => {
4324 self.emit.write("return Err(sage_runtime::SageError::user(");
4326 self.generate_expr(error);
4327 self.emit.write("))");
4328 }
4329
4330 Expr::Retry {
4332 count,
4333 delay,
4334 on_errors: _,
4335 body,
4336 ..
4337 } => {
4338 self.emit.writeln("'_retry: {");
4340 self.emit.indent();
4341
4342 self.emit.write("let _retry_max: i64 = ");
4343 self.generate_expr(count);
4344 self.emit.writeln(";");
4345
4346 if let Some(delay_expr) = delay {
4347 self.emit.write("let _retry_delay: u64 = ");
4348 self.generate_expr(delay_expr);
4349 self.emit.writeln(" as u64;");
4350 }
4351
4352 self.emit
4353 .writeln("let mut _last_error: Option<sage_runtime::SageError> = None;");
4354 self.emit.writeln("for _attempt in 0.._retry_max {");
4355 self.emit.indent();
4356
4357 self.emit.writeln("let _result = (async {");
4359 self.emit.indent();
4360 self.emit.write("Ok::<_, sage_runtime::SageError>(");
4361 self.generate_expr(body);
4362 self.emit.writeln(")");
4363 self.emit.dedent();
4364 self.emit.writeln("}).await;");
4365
4366 self.emit.writeln("match _result {");
4367 self.emit.indent();
4368 self.emit.writeln("Ok(v) => break '_retry v,");
4369 self.emit.writeln("Err(e) => {");
4370 self.emit.indent();
4371 self.emit.writeln("_last_error = Some(e);");
4372
4373 if delay.is_some() {
4375 self.emit.writeln("if _attempt < _retry_max - 1 {");
4376 self.emit.indent();
4377 self.emit.writeln(
4378 "tokio::time::sleep(std::time::Duration::from_millis(_retry_delay)).await;",
4379 );
4380 self.emit.dedent();
4381 self.emit.writeln("}");
4382 }
4383
4384 self.emit.dedent();
4385 self.emit.writeln("}");
4386 self.emit.dedent();
4387 self.emit.writeln("}");
4388
4389 self.emit.dedent();
4390 self.emit.writeln("}");
4391
4392 self.emit.writeln("return Err(_last_error.unwrap());");
4394
4395 self.emit.dedent();
4396 self.emit.write("}");
4397 }
4398
4399 Expr::Trace { message, .. } => {
4401 self.emit.write("sage_runtime::trace::user(&");
4402 self.generate_expr(message);
4403 self.emit.write(")");
4404 }
4405
4406 Expr::Closure { params, body, .. } => {
4408 self.emit.write("Box::new(move |");
4410 for (i, param) in params.iter().enumerate() {
4411 if i > 0 {
4412 self.emit.write(", ");
4413 }
4414 self.emit.write(¶m.name.name);
4415 if let Some(ty) = ¶m.ty {
4416 self.emit.write(": ");
4417 self.emit_type(ty);
4418 }
4419 }
4420 self.emit.write("| ");
4421 self.generate_expr(body);
4422 self.emit.write(")");
4423 }
4424
4425 Expr::Tuple { elements, .. } => {
4427 self.emit.write("(");
4428 for (i, elem) in elements.iter().enumerate() {
4429 if i > 0 {
4430 self.emit.write(", ");
4431 }
4432 self.generate_expr(elem);
4433 }
4434 self.emit.write(")");
4435 }
4436
4437 Expr::TupleIndex { tuple, index, .. } => {
4438 self.generate_expr(tuple);
4439 self.emit.write(&format!(".{index}"));
4440 }
4441
4442 Expr::Map { entries, .. } => {
4443 if entries.is_empty() {
4444 self.emit.write("std::collections::HashMap::new()");
4445 } else {
4446 self.emit.write("std::collections::HashMap::from([");
4447 for (i, entry) in entries.iter().enumerate() {
4448 if i > 0 {
4449 self.emit.write(", ");
4450 }
4451 self.emit.write("(");
4452 self.generate_expr(&entry.key);
4453 self.emit.write(", ");
4454 self.generate_expr(&entry.value);
4455 self.emit.write(")");
4456 }
4457 self.emit.write("])");
4458 }
4459 }
4460
4461 Expr::VariantConstruct {
4462 enum_name,
4463 type_args,
4464 variant,
4465 payload,
4466 ..
4467 } => {
4468 self.emit.write(&enum_name.name);
4469 if !type_args.is_empty() {
4471 self.emit.write("::<");
4472 for (i, arg) in type_args.iter().enumerate() {
4473 if i > 0 {
4474 self.emit.write(", ");
4475 }
4476 self.emit_type(arg);
4477 }
4478 self.emit.write(">");
4479 }
4480 self.emit.write("::");
4481 self.emit.write(&variant.name);
4482 if let Some(payload_expr) = payload {
4483 self.emit.write("(");
4484 self.generate_expr(payload_expr);
4485 self.emit.write(")");
4486 }
4487 }
4488
4489 Expr::ToolCall {
4491 tool,
4492 function,
4493 args,
4494 ..
4495 } => {
4496 self.emit.write("self.");
4499 self.emit.write(&tool.name.to_lowercase());
4500 self.emit.write(".");
4501 self.emit.write(&function.name);
4502 self.emit.write("(");
4503 for (i, arg) in args.iter().enumerate() {
4504 if i > 0 {
4505 self.emit.write(", ");
4506 }
4507 self.emit.write("(");
4509 self.generate_expr(arg);
4510 self.emit.write(").clone()");
4511 }
4512 self.emit.write(").await");
4513 }
4514
4515 Expr::Reply { message, .. } => {
4517 let protocol_role = self
4519 .current_protocol_roles
4520 .iter()
4521 .next()
4522 .map(|(_, r)| r.clone());
4523
4524 if let Some(role) = protocol_role {
4525 let msg_type = self.infer_expr_type_name(message);
4528 self.emit.write("ctx.reply_with_protocol(");
4529 self.generate_expr(message);
4530 self.emit.write(", \"");
4531 self.emit.write(&msg_type);
4532 self.emit.write("\", \"");
4533 self.emit.write(&role);
4534 self.emit.write("\").await?");
4535 } else {
4536 self.emit.write("ctx.reply(");
4538 self.generate_expr(message);
4539 self.emit.write(").await?");
4540 }
4541 }
4542 }
4543 }
4544
4545 fn emit_pattern(&mut self, pattern: &sage_parser::Pattern) {
4546 use sage_parser::Pattern;
4547 match pattern {
4548 Pattern::Wildcard { .. } => {
4549 self.emit.write("_");
4550 }
4551 Pattern::Variant {
4552 enum_name,
4553 variant,
4554 payload,
4555 ..
4556 } => {
4557 if let Some(enum_name) = enum_name {
4558 self.emit.write(&enum_name.name);
4559 self.emit.write("::");
4560 }
4561 self.emit.write(&variant.name);
4562 if let Some(inner_pattern) = payload {
4563 self.emit.write("(");
4564 self.emit_pattern(inner_pattern);
4565 self.emit.write(")");
4566 }
4567 }
4568 Pattern::Literal { value, .. } => {
4569 self.emit_literal(value);
4570 }
4571 Pattern::Binding { name, .. } => {
4572 self.emit.write(&name.name);
4573 }
4574 Pattern::Tuple { elements, .. } => {
4575 self.emit.write("(");
4576 for (i, elem) in elements.iter().enumerate() {
4577 if i > 0 {
4578 self.emit.write(", ");
4579 }
4580 self.emit_pattern(elem);
4581 }
4582 self.emit.write(")");
4583 }
4584 }
4585 }
4586
4587 fn emit_literal(&mut self, lit: &Literal) {
4588 match lit {
4589 Literal::Int(n) => {
4590 self.emit.write(&format!("{n}_i64"));
4591 }
4592 Literal::Float(f) => {
4593 self.emit.write(&format!("{f}_f64"));
4594 }
4595 Literal::Bool(b) => {
4596 self.emit.write(if *b { "true" } else { "false" });
4597 }
4598 Literal::String(s) => {
4599 self.emit.write("\"");
4601 for c in s.chars() {
4602 match c {
4603 '"' => self.emit.write_raw("\\\""),
4604 '\\' => self.emit.write_raw("\\\\"),
4605 '\n' => self.emit.write_raw("\\n"),
4606 '\r' => self.emit.write_raw("\\r"),
4607 '\t' => self.emit.write_raw("\\t"),
4608 c if c.is_control() => {
4609 self.emit.write_raw(&format!("\\x{:02x}", c as u32));
4610 }
4611 _ => self.emit.write_raw(&c.to_string()),
4612 }
4613 }
4614 self.emit.write("\".to_string()");
4615 }
4616 }
4617 }
4618
4619 fn escape_string_for_rust(s: &str) -> String {
4620 let mut out = String::new();
4621 for c in s.chars() {
4622 match c {
4623 '"' => out.push_str("\\\""),
4624 '\\' => out.push_str("\\\\"),
4625 '\n' => out.push_str("\\n"),
4626 '\r' => out.push_str("\\r"),
4627 '\t' => out.push_str("\\t"),
4628 c if c.is_control() => out.push_str(&format!("\\x{:02x}", c as u32)),
4629 _ => out.push(c),
4630 }
4631 }
4632 out
4633 }
4634
4635 fn emit_string_template(&mut self, template: &sage_parser::StringTemplate) {
4636 if !template.has_interpolations() {
4637 if let Some(StringPart::Literal(s)) = template.parts.first() {
4639 self.emit.write("\"");
4640 self.emit.write_raw(&Self::escape_string_for_rust(s));
4641 self.emit.write("\".to_string()");
4642 }
4643 return;
4644 }
4645
4646 self.emit.write("format!(\"");
4648 for part in &template.parts {
4649 match part {
4650 StringPart::Literal(s) => {
4651 let escaped = Self::escape_string_for_rust(s)
4653 .replace('{', "{{")
4654 .replace('}', "}}");
4655 self.emit.write_raw(&escaped);
4656 }
4657 StringPart::Interpolation(_) => {
4658 self.emit.write_raw("{}");
4659 }
4660 }
4661 }
4662 self.emit.write("\"");
4663
4664 for part in &template.parts {
4666 if let StringPart::Interpolation(expr) = part {
4667 self.emit.write(", ");
4668 self.generate_expr(expr);
4669 }
4670 }
4671 self.emit.write(")");
4672 }
4673
4674 fn emit_type(&mut self, ty: &TypeExpr) {
4675 match ty {
4676 TypeExpr::Int => self.emit.write("i64"),
4677 TypeExpr::Float => self.emit.write("f64"),
4678 TypeExpr::Bool => self.emit.write("bool"),
4679 TypeExpr::String => self.emit.write("String"),
4680 TypeExpr::Unit => self.emit.write("()"),
4681 TypeExpr::List(inner) => {
4682 self.emit.write("Vec<");
4683 self.emit_type(inner);
4684 self.emit.write(">");
4685 }
4686 TypeExpr::Option(inner) => {
4687 self.emit.write("Option<");
4688 self.emit_type(inner);
4689 self.emit.write(">");
4690 }
4691 TypeExpr::Oracle(inner) => {
4692 self.emit_type(inner);
4694 }
4695 TypeExpr::Agent(agent_name) => {
4696 self.emit.write("AgentHandle<");
4699 self.emit.write(&agent_name.name);
4700 self.emit.write("Output>");
4701 }
4702 TypeExpr::Named(name, type_args) => {
4703 self.emit.write(&name.name);
4704 if !type_args.is_empty() {
4705 self.emit.write("<");
4706 for (i, arg) in type_args.iter().enumerate() {
4707 if i > 0 {
4708 self.emit.write(", ");
4709 }
4710 self.emit_type(arg);
4711 }
4712 self.emit.write(">");
4713 }
4714 }
4715
4716 TypeExpr::Error => {
4718 self.emit.write("sage_runtime::SageError");
4719 }
4720
4721 TypeExpr::Fn(params, ret) => {
4723 self.emit.write("Box<dyn Fn(");
4724 for (i, param) in params.iter().enumerate() {
4725 if i > 0 {
4726 self.emit.write(", ");
4727 }
4728 self.emit_type(param);
4729 }
4730 self.emit.write(") -> ");
4731 self.emit_type(ret);
4732 self.emit.write(" + Send + 'static>");
4733 }
4734
4735 TypeExpr::Map(key, value) => {
4737 self.emit.write("std::collections::HashMap<");
4738 self.emit_type(key);
4739 self.emit.write(", ");
4740 self.emit_type(value);
4741 self.emit.write(">");
4742 }
4743 TypeExpr::Tuple(elems) => {
4744 self.emit.write("(");
4745 for (i, elem) in elems.iter().enumerate() {
4746 if i > 0 {
4747 self.emit.write(", ");
4748 }
4749 self.emit_type(elem);
4750 }
4751 self.emit.write(")");
4752 }
4753 TypeExpr::Result(ok, err) => {
4754 self.emit.write("Result<");
4755 self.emit_type(ok);
4756 self.emit.write(", ");
4757 self.emit_type(err);
4758 self.emit.write(">");
4759 }
4760 }
4761 }
4762
4763 fn emit_binop(&mut self, op: &BinOp) {
4764 let s = match op {
4765 BinOp::Add => "+",
4766 BinOp::Sub => "-",
4767 BinOp::Mul => "*",
4768 BinOp::Div => "/",
4769 BinOp::Rem => "%",
4770 BinOp::Eq => "==",
4771 BinOp::Ne => "!=",
4772 BinOp::Lt => "<",
4773 BinOp::Gt => ">",
4774 BinOp::Le => "<=",
4775 BinOp::Ge => ">=",
4776 BinOp::And => "&&",
4777 BinOp::Or => "||",
4778 BinOp::Concat => "++", };
4780 self.emit.write(s);
4781 }
4782
4783 fn emit_unaryop(&mut self, op: &UnaryOp) {
4784 let s = match op {
4785 UnaryOp::Neg => "-",
4786 UnaryOp::Not => "!",
4787 };
4788 self.emit.write(s);
4789 }
4790
4791 fn infer_agent_output_type(&self, agent: &AgentDecl) -> String {
4792 for handler in &agent.handlers {
4795 if let EventKind::Start = &handler.event {
4796 if let Some(ty) = self.find_emit_type(&handler.body) {
4797 return ty;
4798 }
4799 }
4800 }
4801 "i64".to_string()
4802 }
4803
4804 fn find_emit_type(&self, block: &Block) -> Option<String> {
4805 let mut var_types: std::collections::HashMap<String, String> =
4807 std::collections::HashMap::new();
4808
4809 for stmt in &block.stmts {
4810 if let Stmt::Let { name, value, .. } = stmt {
4812 let ty = self.infer_expr_type_with_vars(value, &var_types);
4813 var_types.insert(name.name.clone(), ty);
4814 }
4815
4816 if let Stmt::Expr { expr, .. } = stmt {
4817 if let Expr::Yield { value, .. } = expr {
4818 return Some(self.infer_expr_type_with_vars(value, &var_types));
4819 }
4820 }
4821 if let Stmt::If {
4823 then_block,
4824 else_block,
4825 ..
4826 } = stmt
4827 {
4828 if let Some(ty) = self.find_emit_type(then_block) {
4829 return Some(ty);
4830 }
4831 if let Some(else_branch) = else_block {
4832 if let sage_parser::ElseBranch::Block(block) = else_branch {
4833 if let Some(ty) = self.find_emit_type(block) {
4834 return Some(ty);
4835 }
4836 }
4837 }
4838 }
4839 }
4840 None
4841 }
4842
4843 fn infer_expr_type_with_vars(
4844 &self,
4845 expr: &Expr,
4846 var_types: &std::collections::HashMap<String, String>,
4847 ) -> String {
4848 match expr {
4849 Expr::Var { name, .. } => {
4850 if let Some(ty) = var_types.get(&name.name) {
4852 return ty.clone();
4853 }
4854 "i64".to_string() }
4856 Expr::Try { expr, .. } => self.infer_expr_type_with_vars(expr, var_types),
4858 Expr::Catch { expr, .. } => self.infer_expr_type_with_vars(expr, var_types),
4860 _ => self.infer_expr_type(expr),
4862 }
4863 }
4864
4865 fn infer_expr_type(&self, expr: &Expr) -> String {
4866 match expr {
4867 Expr::Literal { value, .. } => match value {
4868 Literal::Int(_) => "i64".to_string(),
4869 Literal::Float(_) => "f64".to_string(),
4870 Literal::Bool(_) => "bool".to_string(),
4871 Literal::String(_) => "String".to_string(),
4872 },
4873 Expr::Var { .. } => "i64".to_string(), Expr::Binary { op, .. } => {
4875 if matches!(
4876 op,
4877 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Gt | BinOp::Le | BinOp::Ge
4878 ) {
4879 "bool".to_string()
4880 } else if matches!(op, BinOp::Concat) {
4881 "String".to_string()
4882 } else {
4883 "i64".to_string()
4884 }
4885 }
4886 Expr::Divine { .. } | Expr::StringInterp { .. } => "String".to_string(),
4887 Expr::Call { name, .. } if name.name == "str" => "String".to_string(),
4888 Expr::Call { name, .. } if name.name == "len" => "i64".to_string(),
4889 _ => "i64".to_string(),
4890 }
4891 }
4892
4893 fn extract_message_type_name(expr: &Expr) -> Option<String> {
4897 match expr {
4898 Expr::RecordConstruct { name, .. } => Some(name.name.clone()),
4900
4901 Expr::VariantConstruct { enum_name, .. } => Some(enum_name.name.clone()),
4903
4904 Expr::Paren { inner, .. } => Self::extract_message_type_name(inner),
4906
4907 _ => None,
4909 }
4910 }
4911
4912 fn infer_expr_type_name(&self, expr: &Expr) -> String {
4918 match expr {
4919 Expr::RecordConstruct { name, .. } => name.name.clone(),
4921 Expr::VariantConstruct { enum_name, .. } => enum_name.name.clone(),
4923 Expr::Var { name, .. } => name.name.clone(),
4925 _ => "Message".to_string(),
4927 }
4928 }
4929
4930 #[allow(clippy::only_used_in_recursion)]
4934 fn type_expr_to_string(&self, ty: &TypeExpr) -> String {
4935 match ty {
4936 TypeExpr::Int => "Int".to_string(),
4937 TypeExpr::Float => "Float".to_string(),
4938 TypeExpr::Bool => "Bool".to_string(),
4939 TypeExpr::String => "String".to_string(),
4940 TypeExpr::Unit => "Unit".to_string(),
4941 TypeExpr::Named(name, _) => name.name.clone(),
4942 TypeExpr::List(inner) => format!("List{}", self.type_expr_to_string(inner)),
4943 TypeExpr::Option(inner) => format!("Option{}", self.type_expr_to_string(inner)),
4944 TypeExpr::Oracle(inner) => self.type_expr_to_string(inner),
4945 TypeExpr::Agent(name) => name.name.clone(),
4946 TypeExpr::Error => "Error".to_string(),
4947 TypeExpr::Map(k, v) => {
4948 format!(
4949 "Map{}To{}",
4950 self.type_expr_to_string(k),
4951 self.type_expr_to_string(v)
4952 )
4953 }
4954 TypeExpr::Fn(_, _) => "Fn".to_string(),
4955 TypeExpr::Tuple(elems) => {
4956 let parts: Vec<_> = elems.iter().map(|e| self.type_expr_to_string(e)).collect();
4957 format!("Tuple{}", parts.join(""))
4958 }
4959 TypeExpr::Result(ok, err) => {
4960 format!(
4961 "Result{}Or{}",
4962 self.type_expr_to_string(ok),
4963 self.type_expr_to_string(err)
4964 )
4965 }
4966 }
4967 }
4968}
4969
4970#[cfg(test)]
4971mod tests {
4972 use super::*;
4973 use sage_parser::{lex, parse};
4974 use std::sync::Arc;
4975
4976 fn generate_source(source: &str) -> String {
4977 let lex_result = lex(source).expect("lexing failed");
4978 let source_arc: Arc<str> = Arc::from(source);
4979 let (program, errors) = parse(lex_result.tokens(), source_arc);
4980 assert!(errors.is_empty(), "parse errors: {errors:?}");
4981 let program = program.expect("should parse");
4982 generate(&program, "test").main_rs
4983 }
4984
4985 #[test]
4986 fn generate_minimal_program() {
4987 let source = r#"
4988 agent Main {
4989 on start {
4990 yield(42);
4991 }
4992 }
4993 run Main;
4994 "#;
4995
4996 let output = generate_source(source);
4997 assert!(output.contains("struct Main;"));
4998 assert!(output.contains("async fn on_start"));
4999 assert!(output.contains("ctx.emit(42_i64)"));
5000 assert!(output.contains("#[tokio::main]"));
5001 }
5002
5003 #[test]
5004 fn generate_function() {
5005 let source = r#"
5006 fn add(a: Int, b: Int) -> Int {
5007 return a + b;
5008 }
5009 agent Main {
5010 on start {
5011 yield(add(1, 2));
5012 }
5013 }
5014 run Main;
5015 "#;
5016
5017 let output = generate_source(source);
5018 assert!(output.contains("fn add(a: i64, b: i64) -> i64"));
5019 assert!(output.contains("return a + b;"));
5020 }
5021
5022 #[test]
5023 fn generate_agent_with_beliefs() {
5024 let source = r#"
5025 agent Worker {
5026 value: Int
5027
5028 on start {
5029 yield(self.value * 2);
5030 }
5031 }
5032 agent Main {
5033 on start {
5034 yield(0);
5035 }
5036 }
5037 run Main;
5038 "#;
5039
5040 let output = generate_source(source);
5041 assert!(output.contains("struct Worker {"));
5042 assert!(output.contains("value: i64,"));
5043 assert!(output.contains("self.value"));
5044 }
5045
5046 #[test]
5047 fn generate_persistent_beliefs() {
5048 let source = r#"
5049 agent Counter {
5050 @persistent count: Int
5051
5052 on waking {
5053 print("woke up");
5054 }
5055
5056 on start {
5057 yield(self.count.get());
5058 }
5059 }
5060 run Counter;
5061 "#;
5062
5063 let output = generate_source(source);
5064 assert!(output.contains("_checkpoint:"), "missing checkpoint field");
5066 assert!(
5067 output.contains("_checkpoint_key:"),
5068 "missing checkpoint key field"
5069 );
5070 assert!(
5071 output.contains("Persisted<i64>"),
5072 "count should be wrapped in Persisted"
5073 );
5074 assert!(
5076 output.contains("MemoryCheckpointStore"),
5077 "missing checkpoint store init"
5078 );
5079 assert!(
5080 output.contains("Persisted::new"),
5081 "missing Persisted::new in construction"
5082 );
5083 assert!(
5085 output.contains("async fn on_waking"),
5086 "missing on_waking handler"
5087 );
5088 assert!(
5089 output.contains("agent.on_waking().await"),
5090 "missing on_waking call"
5091 );
5092 }
5093
5094 #[test]
5095 fn generate_string_interpolation() {
5096 let source = r#"
5097 agent Main {
5098 on start {
5099 let name = "World";
5100 let msg = "Hello, {name}!";
5101 print(msg);
5102 yield(0);
5103 }
5104 }
5105 run Main;
5106 "#;
5107
5108 let output = generate_source(source);
5109 assert!(output.contains("format!(\"Hello, {}!\", name)"));
5110 }
5111
5112 #[test]
5113 fn generate_control_flow() {
5114 let source = r#"
5115 agent Main {
5116 on start {
5117 let x = 10;
5118 if x > 5 {
5119 yield(1);
5120 } else {
5121 yield(0);
5122 }
5123 }
5124 }
5125 run Main;
5126 "#;
5127
5128 let output = generate_source(source);
5129 assert!(output.contains("if x > 5_i64"), "output:\n{output}");
5130 assert!(output.contains("else"), "output:\n{output}");
5132 }
5133
5134 #[test]
5135 fn generate_loops() {
5136 let source = r#"
5137 agent Main {
5138 on start {
5139 for x in [1, 2, 3] {
5140 print(str(x));
5141 }
5142 let n = 0;
5143 while n < 5 {
5144 n = n + 1;
5145 }
5146 yield(n);
5147 }
5148 }
5149 run Main;
5150 "#;
5151
5152 let output = generate_source(source);
5153 assert!(output.contains("for x in vec![1_i64, 2_i64, 3_i64]"));
5154 assert!(output.contains("while n < 5_i64"));
5155 }
5156
5157 #[test]
5158 fn generate_pub_function() {
5159 let source = r#"
5160 pub fn helper(x: Int) -> Int {
5161 return x * 2;
5162 }
5163 agent Main {
5164 on start {
5165 yield(helper(21));
5166 }
5167 }
5168 run Main;
5169 "#;
5170
5171 let output = generate_source(source);
5172 assert!(output.contains("pub fn helper(x: i64) -> i64"));
5173 }
5174
5175 #[test]
5176 fn generate_pub_agent() {
5177 let source = r#"
5178 pub agent Worker {
5179 on start {
5180 yield(42);
5181 }
5182 }
5183 agent Main {
5184 on start {
5185 yield(0);
5186 }
5187 }
5188 run Main;
5189 "#;
5190
5191 let output = generate_source(source);
5192 assert!(output.contains("pub struct Worker;"));
5193 }
5194
5195 #[test]
5196 fn generate_module_tree_simple() {
5197 use sage_loader::load_single_file;
5198 use std::fs;
5199 use tempfile::TempDir;
5200
5201 let dir = TempDir::new().unwrap();
5202 let file = dir.path().join("test.sg");
5203 fs::write(
5204 &file,
5205 r#"
5206agent Main {
5207 on start {
5208 yield(42);
5209 }
5210}
5211run Main;
5212"#,
5213 )
5214 .unwrap();
5215
5216 let tree = load_single_file(&file).unwrap();
5217 let project = generate_module_tree(&tree, "test");
5218
5219 assert!(project.main_rs.contains("struct Main;"));
5220 assert!(project.main_rs.contains("async fn on_start"));
5221 assert!(project.main_rs.contains("#[tokio::main]"));
5222 }
5223
5224 #[test]
5225 fn generate_module_tree_with_supervisor() {
5226 use sage_loader::load_single_file;
5227 use std::fs;
5228 use tempfile::TempDir;
5229
5230 let dir = TempDir::new().unwrap();
5231 let file = dir.path().join("test.sg");
5232 fs::write(
5233 &file,
5234 r#"
5235agent Worker {
5236 on start {
5237 yield(42);
5238 }
5239}
5240supervisor AppSupervisor {
5241 strategy: OneForOne
5242 children {
5243 Worker { restart: Transient }
5244 }
5245}
5246run AppSupervisor;
5247"#,
5248 )
5249 .unwrap();
5250
5251 let tree = load_single_file(&file).unwrap();
5252 let project = generate_module_tree(&tree, "test");
5253
5254 assert!(
5256 project.main_rs.contains("struct AppSupervisor;"),
5257 "Missing supervisor struct. Output:\n{}",
5258 project.main_rs
5259 );
5260 assert!(
5262 project
5263 .main_rs
5264 .contains("Supervisor::new(Strategy::OneForOne"),
5265 "Missing supervisor main. Output:\n{}",
5266 project.main_rs
5267 );
5268 }
5269
5270 #[test]
5271 fn generate_supervisor_with_belief_inits() {
5272 let source = r#"
5275agent QueryMonitor {
5276 @persistent check_count: Int
5277 @persistent last_check: String
5278
5279 on start {
5280 yield(1);
5281 }
5282}
5283
5284supervisor DbGuardian {
5285 strategy: OneForOne
5286 children {
5287 QueryMonitor {
5288 restart: Permanent
5289 check_count: 0
5290 last_check: "never"
5291 }
5292 }
5293}
5294
5295run DbGuardian;
5296"#;
5297
5298 let output = generate_source(source);
5299
5300 assert!(
5302 output.contains("#[tokio::main]"),
5303 "Missing tokio::main. Output:\n{}",
5304 output
5305 );
5306 assert!(
5307 output.contains("Supervisor::new(Strategy::OneForOne"),
5308 "Missing supervisor creation. Output:\n{}",
5309 output
5310 );
5311 assert!(
5313 output.contains("check_count: Persisted::with_initial("),
5314 "Missing check_count with_initial. Output:\n{}",
5315 output
5316 );
5317 assert!(
5318 output.contains("last_check: Persisted::with_initial("),
5319 "Missing last_check with_initial. Output:\n{}",
5320 output
5321 );
5322 assert!(
5324 output.contains("0_i64"),
5325 "Missing check_count initial value. Output:\n{}",
5326 output
5327 );
5328 assert!(
5329 output.contains("\"never\""),
5330 "Missing last_check initial value. Output:\n{}",
5331 output
5332 );
5333 }
5334
5335 #[test]
5336 fn generate_record_declaration() {
5337 let source = r#"
5338 record Point {
5339 x: Int,
5340 y: Int,
5341 }
5342 agent Main {
5343 on start {
5344 let p = Point { x: 10, y: 20 };
5345 yield(p.x);
5346 }
5347 }
5348 run Main;
5349 "#;
5350
5351 let output = generate_source(source);
5352 assert!(output.contains("#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]"));
5353 assert!(output.contains("struct Point {"));
5354 assert!(output.contains("x: i64,"));
5355 assert!(output.contains("y: i64,"));
5356 assert!(output.contains("Point { x: 10_i64, y: 20_i64 }"));
5357 assert!(output.contains("p.x"));
5358 }
5359
5360 #[test]
5361 fn generate_enum_declaration() {
5362 let source = r#"
5363 enum Status {
5364 Active,
5365 Inactive,
5366 Pending,
5367 }
5368 agent Main {
5369 on start {
5370 yield(0);
5371 }
5372 }
5373 run Main;
5374 "#;
5375
5376 let output = generate_source(source);
5377 assert!(output.contains("#[derive(Debug, Clone, Copy, PartialEq, Eq)]"));
5378 assert!(output.contains("enum Status {"));
5379 assert!(output.contains("Active,"));
5380 assert!(output.contains("Inactive,"));
5381 assert!(output.contains("Pending,"));
5382 }
5383
5384 #[test]
5385 fn generate_const_declaration() {
5386 let source = r#"
5387 const MAX_SIZE: Int = 100;
5388 const GREETING: String = "Hello";
5389 agent Main {
5390 on start {
5391 yield(MAX_SIZE);
5392 }
5393 }
5394 run Main;
5395 "#;
5396
5397 let output = generate_source(source);
5398 assert!(output.contains("const MAX_SIZE: i64 = 100_i64;"));
5399 assert!(output.contains("const GREETING: &'static str = \"Hello\";"));
5401 }
5402
5403 #[test]
5404 fn generate_match_expression() {
5405 let source = r#"
5406 enum Status {
5407 Active,
5408 Inactive,
5409 }
5410 fn check_status(s: Status) -> Int {
5411 return match s {
5412 Active => 1,
5413 Inactive => 0,
5414 };
5415 }
5416 agent Main {
5417 on start {
5418 yield(0);
5419 }
5420 }
5421 run Main;
5422 "#;
5423
5424 let output = generate_source(source);
5425 assert!(output.contains("match s {"));
5426 assert!(output.contains("Active => 1_i64,"));
5427 assert!(output.contains("Inactive => 0_i64,"));
5428 }
5429
5430 #[test]
5435 fn generate_fallible_function() {
5436 let source = r#"
5437 fn get_data(url: String) -> String fails {
5438 return url;
5439 }
5440 agent Main {
5441 on start { yield(0); }
5442 }
5443 run Main;
5444 "#;
5445
5446 let output = generate_source(source);
5447 assert!(output.contains("fn get_data(url: String) -> SageResult<String>"));
5449 }
5450
5451 #[test]
5452 fn generate_try_expression() {
5453 let source = r#"
5454 fn fallible() -> Int fails { return 42; }
5455 fn caller() -> Int fails {
5456 let x = try fallible();
5457 return x;
5458 }
5459 agent Main {
5460 on start { yield(0); }
5461 }
5462 run Main;
5463 "#;
5464
5465 let output = generate_source(source);
5466 assert!(output.contains("fallible()?"));
5468 }
5469
5470 #[test]
5471 fn generate_catch_expression() {
5472 let source = r#"
5473 fn fallible() -> Int fails { return 42; }
5474 agent Main {
5475 on start {
5476 let x = fallible() catch { 0 };
5477 yield(x);
5478 }
5479 }
5480 run Main;
5481 "#;
5482
5483 let output = generate_source(source);
5484 assert!(output.contains("match fallible()"));
5486 assert!(output.contains("Ok(__val) => __val"));
5487 assert!(output.contains("Err(_) => 0_i64"));
5488 }
5489
5490 #[test]
5491 fn generate_catch_with_binding() {
5492 let source = r#"
5493 fn fallible() -> Int fails { return 42; }
5494 agent Main {
5495 on start {
5496 let x = fallible() catch(e) { 0 };
5497 yield(x);
5498 }
5499 }
5500 run Main;
5501 "#;
5502
5503 let output = generate_source(source);
5504 assert!(output.contains("Err(e) => 0_i64"));
5506 }
5507
5508 #[test]
5509 fn generate_on_error_handler() {
5510 let source = r#"
5511 agent Main {
5512 on start {
5513 yield(0);
5514 }
5515 on error(e) {
5516 yield(1);
5517 }
5518 }
5519 run Main;
5520 "#;
5521
5522 let output = generate_source(source);
5523 assert!(output.contains("async fn on_error(&self, _e: SageError, ctx: &mut AgentContext"));
5525 assert!(output.contains(".on_error(e, &mut ctx)"));
5527 }
5528
5529 #[test]
5534 fn generate_agent_with_tool_use() {
5535 let source = r#"
5536 agent Fetcher {
5537 use Http
5538
5539 on start {
5540 let r = Http.get("https://example.com");
5541 yield(0);
5542 }
5543 }
5544 run Fetcher;
5545 "#;
5546
5547 let output = generate_source(source);
5548 assert!(output.contains("struct Fetcher {"));
5550 assert!(output.contains("http: HttpClient,"));
5551 assert!(output.contains("http: HttpClient::from_env()"));
5553 assert!(output.contains("self.http.get("));
5555 }
5556
5557 #[test]
5558 fn generate_tool_call_expression() {
5559 let source = r#"
5560 agent Fetcher {
5561 use Http
5562
5563 on start {
5564 let response = Http.get("https://httpbin.org/get");
5565 yield(0);
5566 }
5567 }
5568 run Fetcher;
5569 "#;
5570
5571 let output = generate_source(source);
5572 assert!(output
5574 .contains("self.http.get((\"https://httpbin.org/get\".to_string()).clone()).await"));
5575 }
5576
5577 fn generate_test_source(source: &str) -> String {
5578 let lex_result = lex(source).expect("lexing failed");
5579 let source_arc: Arc<str> = Arc::from(source);
5580 let (program, errors) = parse(lex_result.tokens(), source_arc);
5581 assert!(errors.is_empty(), "parse errors: {errors:?}");
5582 let program = program.expect("should parse");
5583 super::generate_test_program(&program, "test").main_rs
5584 }
5585
5586 #[test]
5587 fn generate_mock_tool() {
5588 let source = r#"
5589 test "mocks http tool" {
5590 mock tool Http.get -> "mocked response";
5591 mock tool Http.post -> fail("network error");
5592 assert_eq(1, 1);
5593 }
5594 "#;
5595
5596 let output = generate_test_source(source);
5597 assert!(output.contains("let _mock_tools = MockToolRegistry::new();"));
5599 assert!(output.contains("_mock_tools.register(\"Http\", \"get\", MockResponse::value("));
5601 assert!(output.contains("_mock_tools.register(\"Http\", \"post\", MockResponse::fail("));
5602 }
5603
5604 #[test]
5605 fn generate_mock_infer_and_tool() {
5606 let source = r#"
5607 test "mocks both infer and tool" {
5608 mock divine -> "hello";
5609 mock tool Http.get -> "response";
5610 assert_true(true);
5611 }
5612 "#;
5613
5614 let output = generate_test_source(source);
5615 assert!(output.contains("MockLlmClient::with_responses"));
5617 assert!(output.contains("MockToolRegistry::new()"));
5618 }
5619
5620 #[test]
5621 fn generate_supervisor_declaration() {
5622 let source = r#"
5623 agent Worker {
5624 count: Int
5625
5626 on start {
5627 yield(self.count);
5628 }
5629 }
5630
5631 supervisor AppSupervisor {
5632 strategy: OneForOne
5633
5634 children {
5635 Worker { restart: Transient, count: 0 }
5636 }
5637 }
5638
5639 run AppSupervisor;
5640 "#;
5641
5642 let output = generate_source(source);
5643
5644 assert!(output.contains("// Supervisor: AppSupervisor"));
5646 assert!(output.contains("struct AppSupervisor;"));
5647
5648 assert!(output.contains("Supervisor::new(Strategy::OneForOne"));
5650 assert!(output.contains("RestartConfig { max_restarts: 5"));
5651 assert!(output.contains("Duration::from_secs(60)"));
5652 assert!(output.contains("supervisor.add_child(\"Worker\", RestartPolicy::Transient"));
5653 assert!(output.contains("supervisor.run()"));
5654 }
5655
5656 #[test]
5657 fn generate_supervisor_with_custom_config() {
5658 let source = r#"
5659 agent Worker {
5660 on start { yield(0); }
5661 }
5662
5663 supervisor AppSupervisor {
5664 strategy: OneForAll
5665
5666 children {
5667 Worker { restart: Permanent }
5668 }
5669 }
5670
5671 run AppSupervisor;
5672 "#;
5673
5674 let lex_result = lex(source).expect("lexing failed");
5675 let source_arc: Arc<str> = Arc::from(source);
5676 let (program, errors) = parse(lex_result.tokens(), source_arc);
5677 assert!(errors.is_empty());
5678 let program = program.expect("should parse");
5679
5680 let config = CodegenConfig {
5682 runtime_dep: RuntimeDep::default(),
5683 persistence: PersistenceBackend::Memory,
5684 supervision: SupervisionConfig {
5685 max_restarts: 10,
5686 within_seconds: 120,
5687 },
5688 observability: ObservabilityConfig::default(),
5689 ..Default::default()
5690 };
5691 let output = generate_with_full_config(&program, "test", config).main_rs;
5692
5693 assert!(output.contains("RestartConfig { max_restarts: 10"));
5695 assert!(output.contains("Duration::from_secs(120)"));
5696 assert!(output.contains("Strategy::OneForAll"));
5697 }
5698
5699 fn generate_with_backend(source: &str, backend: PersistenceBackend) -> String {
5704 let lex_result = lex(source).expect("lexing failed");
5705 let source_arc: Arc<str> = Arc::from(source);
5706 let (program, errors) = parse(lex_result.tokens(), source_arc);
5707 assert!(errors.is_empty(), "parse errors: {errors:?}");
5708 let program = program.expect("should parse");
5709
5710 let config = CodegenConfig {
5711 runtime_dep: RuntimeDep::default(),
5712 persistence: backend,
5713 supervision: SupervisionConfig::default(),
5714 observability: ObservabilityConfig::default(),
5715 ..Default::default()
5716 };
5717 generate_with_full_config(&program, "test", config).main_rs
5718 }
5719
5720 #[test]
5721 fn generate_persistence_memory_backend() {
5722 let source = r#"
5723 agent Counter {
5724 @persistent count: Int
5725 on start {
5726 yield(self.count.get());
5727 }
5728 }
5729 run Counter;
5730 "#;
5731
5732 let output = generate_with_backend(source, PersistenceBackend::Memory);
5733 assert!(
5734 output.contains("MemoryCheckpointStore::new()"),
5735 "memory backend should use MemoryCheckpointStore"
5736 );
5737 }
5738
5739 #[test]
5740 fn generate_persistence_sqlite_backend() {
5741 let source = r#"
5742 agent Counter {
5743 @persistent count: Int
5744 on start {
5745 yield(self.count.get());
5746 }
5747 }
5748 run Counter;
5749 "#;
5750
5751 let output = generate_with_backend(
5752 source,
5753 PersistenceBackend::Sqlite {
5754 path: ".sage/data.db".to_string(),
5755 },
5756 );
5757 assert!(
5758 output.contains("SyncSqliteStore::open(\".sage/data.db\")"),
5759 "sqlite backend should use SyncSqliteStore with correct path"
5760 );
5761 }
5762
5763 #[test]
5764 fn generate_persistence_postgres_backend() {
5765 let source = r#"
5766 agent Counter {
5767 @persistent count: Int
5768 on start {
5769 yield(self.count.get());
5770 }
5771 }
5772 run Counter;
5773 "#;
5774
5775 let output = generate_with_backend(
5776 source,
5777 PersistenceBackend::Postgres {
5778 url: "postgres://localhost/mydb".to_string(),
5779 },
5780 );
5781 assert!(
5782 output.contains("SyncPostgresStore::connect(\"postgres://localhost/mydb\")"),
5783 "postgres backend should use SyncPostgresStore with correct url"
5784 );
5785 }
5786
5787 #[test]
5788 fn generate_persistence_file_backend() {
5789 let source = r#"
5790 agent Counter {
5791 @persistent count: Int
5792 on start {
5793 yield(self.count.get());
5794 }
5795 }
5796 run Counter;
5797 "#;
5798
5799 let output = generate_with_backend(
5800 source,
5801 PersistenceBackend::File {
5802 path: "./state".to_string(),
5803 },
5804 );
5805 assert!(
5806 output.contains("SyncFileStore::open(\"./state\")"),
5807 "file backend should use SyncFileStore with correct path"
5808 );
5809 }
5810
5811 #[test]
5812 fn generate_cargo_toml_with_sqlite_feature() {
5813 let source = r#"
5814 agent Counter {
5815 @persistent count: Int
5816 on start { yield(0); }
5817 }
5818 run Counter;
5819 "#;
5820
5821 let lex_result = lex(source).expect("lexing failed");
5822 let source_arc: Arc<str> = Arc::from(source);
5823 let (program, errors) = parse(lex_result.tokens(), source_arc);
5824 assert!(errors.is_empty());
5825 let program = program.expect("should parse");
5826
5827 let config = CodegenConfig {
5828 runtime_dep: RuntimeDep::CratesIo {
5829 version: "1.0.0".to_string(),
5830 },
5831 persistence: PersistenceBackend::Sqlite {
5832 path: ".sage/data.db".to_string(),
5833 },
5834 supervision: SupervisionConfig::default(),
5835 observability: ObservabilityConfig::default(),
5836 ..Default::default()
5837 };
5838 let project = generate_with_full_config(&program, "test", config);
5839
5840 assert!(
5841 project.cargo_toml.contains("persistence-sqlite"),
5842 "Cargo.toml should include persistence-sqlite feature"
5843 );
5844 }
5845
5846 #[test]
5847 fn generate_cargo_toml_no_feature_for_memory() {
5848 let source = r#"
5849 agent Counter {
5850 @persistent count: Int
5851 on start { yield(0); }
5852 }
5853 run Counter;
5854 "#;
5855
5856 let lex_result = lex(source).expect("lexing failed");
5857 let source_arc: Arc<str> = Arc::from(source);
5858 let (program, errors) = parse(lex_result.tokens(), source_arc);
5859 assert!(errors.is_empty());
5860 let program = program.expect("should parse");
5861
5862 let config = CodegenConfig {
5863 runtime_dep: RuntimeDep::CratesIo {
5864 version: "1.0.0".to_string(),
5865 },
5866 persistence: PersistenceBackend::Memory,
5867 supervision: SupervisionConfig::default(),
5868 observability: ObservabilityConfig::default(),
5869 ..Default::default()
5870 };
5871 let project = generate_with_full_config(&program, "test", config);
5872
5873 assert!(
5874 !project.cargo_toml.contains("persistence-"),
5875 "Cargo.toml should NOT include any persistence feature for memory backend"
5876 );
5877 }
5878
5879 #[test]
5884 fn generate_protocol_state_machine() {
5885 let source = r#"
5886 protocol PingPong {
5887 Pinger -> Ponger: Ping
5888 Ponger -> Pinger: Pong
5889 }
5890
5891 record Ping {}
5892 record Pong {}
5893
5894 agent Main {
5895 on start { yield(0); }
5896 }
5897 run Main;
5898 "#;
5899
5900 let output = generate_source(source);
5901
5902 assert!(output.contains("mod protocol_ping_pong"));
5904
5905 assert!(output.contains("pub enum State"));
5907 assert!(output.contains("Initial"));
5908 assert!(output.contains("Done"));
5909
5910 assert!(output.contains("impl ProtocolStateMachine for State"));
5912 assert!(output.contains("fn state_name(&self)"));
5913 assert!(output.contains("fn can_send(&self, msg_type: &str, from_role: &str)"));
5914 assert!(output.contains("fn can_receive(&self, msg_type: &str, to_role: &str)"));
5915 assert!(output.contains("fn transition(&mut self, msg_type: &str)"));
5916 assert!(output.contains("fn is_terminal(&self)"));
5917 assert!(output.contains("fn protocol_name(&self)"));
5918 }
5919
5920 #[test]
5921 fn generate_effect_handler_config() {
5922 let source = r#"
5923 handler FastLlm handles Infer {
5924 model: "gpt-4o"
5925 temperature: 0.7
5926 max_tokens: 1024
5927 }
5928
5929 agent Main {
5930 on start { yield(0); }
5931 }
5932 run Main;
5933 "#;
5934
5935 let output = generate_source(source);
5936
5937 assert!(
5939 output.contains("mod handler_fast_llm"),
5940 "Should contain handler module: {}",
5941 output
5942 );
5943
5944 assert!(output.contains("pub struct Config"));
5946 assert!(output.contains("pub model: &'static str"));
5947 assert!(output.contains("pub temperature: f64"));
5948 assert!(output.contains("pub max_tokens: i64"));
5949
5950 assert!(output.contains("pub const CONFIG: Config"));
5952 assert!(output.contains("model: \"gpt-4o\""));
5953 assert!(output.contains("temperature: 0.7"));
5954 assert!(output.contains("max_tokens: 1024"));
5955 }
5956
5957 #[test]
5958 fn generate_reply_expression_parsing() {
5959 let source = r#"
5963 record Request {}
5964 record Response { code: Int }
5965
5966 agent Worker receives Request {
5967 on start { yield(0); }
5968 on message(msg: Request) {
5969 reply(Response { code: 200 });
5970 }
5971 }
5972 run Worker;
5973 "#;
5974
5975 let output = generate_source(source);
5977 assert!(output.contains("struct Worker"));
5978 assert!(output.contains("struct Request"));
5979 assert!(output.contains("struct Response"));
5980 }
5981
5982 #[test]
5987 fn generate_checkpoint_with_persistent_fields() {
5988 let source = r#"
5989 agent Counter {
5990 @persistent count: Int
5991 @persistent name: String
5992
5993 on start {
5994 checkpoint();
5995 yield(0);
5996 }
5997 }
5998 run Counter;
5999 "#;
6000
6001 let output = generate_source(source);
6002 assert!(
6004 output.contains("self_count.checkpoint()"),
6005 "Should generate checkpoint call for count field"
6006 );
6007 assert!(
6008 output.contains("self_name.checkpoint()"),
6009 "Should generate checkpoint call for name field"
6010 );
6011 }
6012
6013 #[test]
6014 fn generate_checkpoint_without_persistent_fields() {
6015 let source = r#"
6016 agent Worker {
6017 on start {
6018 checkpoint();
6019 yield(0);
6020 }
6021 }
6022 run Worker;
6023 "#;
6024
6025 let output = generate_source(source);
6026 assert!(
6028 output.contains("// checkpoint() - no @persistent fields"),
6029 "Should generate no-op comment when no persistent fields"
6030 );
6031 }
6032
6033 fn generate_wasm_source(source: &str) -> String {
6038 let lex_result = lex(source).expect("lexing failed");
6039 let source_arc: Arc<str> = Arc::from(source);
6040 let (program, errors) = parse(lex_result.tokens(), source_arc);
6041 assert!(errors.is_empty(), "parse errors: {errors:?}");
6042 let program = program.expect("should parse");
6043 let config = CodegenConfig {
6044 target: CodegenTarget::Wasm,
6045 ..Default::default()
6046 };
6047 generate_with_full_config(&program, "test", config).main_rs
6048 }
6049
6050 fn generate_wasm_cargo_toml(source: &str) -> String {
6051 let lex_result = lex(source).expect("lexing failed");
6052 let source_arc: Arc<str> = Arc::from(source);
6053 let (program, errors) = parse(lex_result.tokens(), source_arc);
6054 assert!(errors.is_empty(), "parse errors: {errors:?}");
6055 let program = program.expect("should parse");
6056 let config = CodegenConfig {
6057 target: CodegenTarget::Wasm,
6058 ..Default::default()
6059 };
6060 generate_with_full_config(&program, "test", config).cargo_toml
6061 }
6062
6063 #[test]
6064 fn generate_wasm_entry_point() {
6065 let source = r#"
6066 agent Main {
6067 on start {
6068 print("hello wasm");
6069 yield(42);
6070 }
6071 }
6072 run Main;
6073 "#;
6074
6075 let output = generate_wasm_source(source);
6076 assert!(
6077 output.contains("#[wasm_bindgen(start)]"),
6078 "Should have wasm_bindgen(start) entry point"
6079 );
6080 assert!(
6081 output.contains("console_error_panic_hook::set_once()"),
6082 "Should set panic hook"
6083 );
6084 assert!(
6085 output.contains("sage_runtime::trace::init()"),
6086 "Should init tracing"
6087 );
6088 assert!(
6089 output.contains("sage_runtime::spawn("),
6090 "Should spawn agent"
6091 );
6092 assert!(
6093 output.contains("wasm_bindgen_futures::spawn_local("),
6094 "Should spawn_local to await result"
6095 );
6096 assert!(
6097 !output.contains("tokio::main"),
6098 "Should NOT have tokio::main"
6099 );
6100 assert!(
6101 !output.contains("ctrl_c"),
6102 "Should NOT have signal handlers"
6103 );
6104 }
6105
6106 #[test]
6107 fn generate_wasm_imports() {
6108 let source = r#"
6109 agent Main {
6110 on start { yield(0); }
6111 }
6112 run Main;
6113 "#;
6114
6115 let output = generate_wasm_source(source);
6116 assert!(
6117 output.contains("use wasm_bindgen::prelude::*;"),
6118 "Should import wasm_bindgen prelude"
6119 );
6120 }
6121
6122 #[test]
6123 fn generate_wasm_cargo_toml_cdylib() {
6124 let source = r#"
6125 agent Main {
6126 on start { yield(0); }
6127 }
6128 run Main;
6129 "#;
6130
6131 let toml = generate_wasm_cargo_toml(source);
6132 assert!(
6133 toml.contains("cdylib"),
6134 "Should have cdylib crate type"
6135 );
6136 assert!(
6137 toml.contains("wasm-bindgen"),
6138 "Should depend on wasm-bindgen"
6139 );
6140 assert!(
6141 toml.contains("console_error_panic_hook"),
6142 "Should depend on console_error_panic_hook"
6143 );
6144 assert!(
6145 !toml.contains("tokio"),
6146 "Should NOT depend on tokio"
6147 );
6148 assert!(
6149 toml.contains("wasm-release"),
6150 "Should have wasm-release profile"
6151 );
6152 }
6153
6154 #[test]
6155 fn generate_wasm_with_tools() {
6156 let source = r#"
6157 agent Main {
6158 use Http
6159
6160 on start {
6161 let resp = Http.get("https://example.com");
6162 yield(0);
6163 }
6164 }
6165 run Main;
6166 "#;
6167
6168 let output = generate_wasm_source(source);
6169 assert!(
6170 output.contains("HttpClient::from_env()"),
6171 "Should construct HTTP tool"
6172 );
6173 assert!(
6174 output.contains("wasm_bindgen(start)"),
6175 "Should still have WASM entry point"
6176 );
6177 }
6178
6179 #[test]
6180 fn generate_wasm_with_persistent() {
6181 let source = r#"
6182 agent Main {
6183 @persistent count: Int
6184
6185 on start {
6186 yield(0);
6187 }
6188 }
6189 run Main;
6190 "#;
6191
6192 let output = generate_wasm_source(source);
6193 assert!(
6194 output.contains("MemoryCheckpointStore::new()"),
6195 "Should use memory checkpoint store on WASM: {output}"
6196 );
6197 assert!(
6198 output.contains("Persisted::new("),
6199 "Should wrap persistent field"
6200 );
6201 }
6202}