Skip to main content

sage_codegen/
generator.rs

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