Skip to main content

logicaffeine_compile/
compile.rs

1//! LOGOS Compilation Pipeline
2//!
3//! This module provides the end-to-end compilation pipeline that transforms
4//! LOGOS source code into executable Rust programs.
5//!
6//! # Pipeline Overview
7//!
8//! ```text
9//! LOGOS Source (.md)
10//!       │
11//!       ▼
12//! ┌───────────────────┐
13//! │  1. Lexer         │ Tokenize source
14//! └─────────┬─────────┘
15//!           ▼
16//! ┌───────────────────┐
17//! │  2. Discovery     │ Type & policy definitions
18//! └─────────┬─────────┘
19//!           ▼
20//! ┌───────────────────┐
21//! │  3. Parser        │ Build AST
22//! └─────────┬─────────┘
23//!           ▼
24//! ┌───────────────────┐
25//! │  4. Analysis      │ Escape, ownership, verification
26//! └─────────┬─────────┘
27//!           ▼
28//! ┌───────────────────┐
29//! │  5. CodeGen       │ Emit Rust source
30//! └─────────┬─────────┘
31//!           ▼
32//!     Rust Source
33//! ```
34//!
35//! # Compilation Functions
36//!
37//! | Function | Analysis | Use Case |
38//! |----------|----------|----------|
39//! | [`compile_to_rust`] | Escape only | Basic compilation |
40//! | [`compile_to_rust_checked`] | Escape + Ownership | Use with `--check` flag |
41//! | `compile_to_rust_verified` | All + Z3 | Formal verification (requires `verification` feature) |
42//! | [`compile_project`] | Multi-file | Projects with imports |
43//! | [`compile_and_run`] | Full + Execute | Development workflow |
44//!
45//! # Examples
46//!
47//! ## Basic Compilation
48//!
49//! ```
50//! # use logicaffeine_compile::compile::compile_to_rust;
51//! # use logicaffeine_compile::ParseError;
52//! # fn main() -> Result<(), ParseError> {
53//! let source = "## Main\nLet x be 5.\nShow x.";
54//! let rust_code = compile_to_rust(source)?;
55//! // rust_code contains:
56//! // fn main() {
57//! //     let x = 5;
58//! //     println!("{}", x);
59//! // }
60//! # Ok(())
61//! # }
62//! ```
63//!
64//! ## With Ownership Checking
65//!
66//! ```
67//! # use logicaffeine_compile::compile::compile_to_rust_checked;
68//! let source = "## Main\nLet x be 5.\nGive x to y.\nShow x.";
69//! let result = compile_to_rust_checked(source);
70//! // Returns Err: "x has already been given away"
71//! ```
72
73use std::collections::{HashMap, HashSet};
74use std::fs;
75use std::io::Write;
76use std::path::Path;
77use std::process::Command;
78
79// Runtime crates paths (relative to workspace root)
80const CRATES_DATA_PATH: &str = "crates/logicaffeine_data";
81const CRATES_SYSTEM_PATH: &str = "crates/logicaffeine_system";
82
83use std::fmt::Write as FmtWrite;
84
85use crate::analysis::{DiscoveryPass, EscapeChecker, OwnershipChecker, PolicyRegistry};
86use crate::arena::Arena;
87use crate::arena_ctx::AstContext;
88use crate::ast::{Expr, MatchArm, Stmt, TypeExpr};
89use crate::ast::stmt::{BinaryOpKind, ClosureBody, Literal, Pattern, ReadSource, SelectBranch, StringPart};
90use crate::codegen::{codegen_program, generate_c_header, generate_python_bindings, generate_typescript_bindings};
91use crate::diagnostic::{parse_rustc_json, translate_diagnostics, LogosError};
92use crate::drs::WorldState;
93use crate::error::ParseError;
94use crate::intern::Interner;
95use crate::lexer::Lexer;
96use crate::parser::Parser;
97use crate::sourcemap::SourceMap;
98
99/// A declared external crate dependency from a `## Requires` block.
100#[derive(Debug, Clone)]
101pub struct CrateDependency {
102    pub name: String,
103    pub version: String,
104    pub features: Vec<String>,
105}
106
107/// Full compilation output including generated Rust code and extracted dependencies.
108#[derive(Debug)]
109pub struct CompileOutput {
110    pub rust_code: String,
111    pub dependencies: Vec<CrateDependency>,
112    /// Generated C header content (populated when C exports exist).
113    pub c_header: Option<String>,
114    /// Generated Python ctypes bindings (populated when C exports exist).
115    pub python_bindings: Option<String>,
116    /// Generated TypeScript type declarations (.d.ts content, populated when C exports exist).
117    pub typescript_types: Option<String>,
118    /// Generated TypeScript FFI bindings (.js content, populated when C exports exist).
119    pub typescript_bindings: Option<String>,
120}
121
122/// Interpret LOGOS source and return output as a string.
123///
124/// Runs the full pipeline (lex → discovery → parse → interpret) without
125/// generating Rust code. Useful for sub-second feedback during development.
126///
127/// # Arguments
128///
129/// * `source` - LOGOS source code as a string
130///
131/// # Returns
132///
133/// The collected output from `Show` statements, joined by newlines.
134///
135/// # Errors
136///
137/// Returns [`ParseError`] if parsing fails or the interpreter encounters
138/// a runtime error.
139pub fn interpret_program(source: &str) -> Result<String, ParseError> {
140    let result = crate::ui_bridge::interpret_for_ui_sync(source);
141    if let Some(err) = result.error {
142        Err(ParseError {
143            kind: crate::error::ParseErrorKind::Custom(err),
144            span: crate::token::Span::default(),
145        })
146    } else {
147        Ok(result.lines.join("\n"))
148    }
149}
150
151/// Compile LOGOS source to Rust source code.
152///
153/// This is the basic compilation function that runs lexing, parsing, and
154/// escape analysis before generating Rust code.
155///
156/// # Arguments
157///
158/// * `source` - LOGOS source code as a string
159///
160/// # Returns
161///
162/// Generated Rust source code on success.
163///
164/// # Errors
165///
166/// Returns [`ParseError`] if:
167/// - Lexical analysis fails (invalid tokens)
168/// - Parsing fails (syntax errors)
169/// - Escape analysis fails (zone-local values escaping)
170///
171/// # Example
172///
173/// ```
174/// # use logicaffeine_compile::compile::compile_to_rust;
175/// # use logicaffeine_compile::ParseError;
176/// # fn main() -> Result<(), ParseError> {
177/// let source = "## Main\nLet x be 5.\nShow x.";
178/// let rust_code = compile_to_rust(source)?;
179/// assert!(rust_code.contains("let x = 5;"));
180/// # Ok(())
181/// # }
182/// ```
183pub fn compile_to_rust(source: &str) -> Result<String, ParseError> {
184    compile_program_full(source).map(|o| o.rust_code)
185}
186
187/// Compile LOGOS source to C code (benchmark-only subset).
188///
189/// Produces a self-contained C file with embedded runtime that can be
190/// compiled with `gcc -O2 -o program output.c`.
191pub fn compile_to_c(source: &str) -> Result<String, ParseError> {
192    let mut interner = Interner::new();
193    let mut lexer = Lexer::new(source, &mut interner);
194    let tokens = lexer.tokenize();
195
196    let (type_registry, _policy_registry) = {
197        let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
198        let result = discovery.run_full();
199        (result.types, result.policies)
200    };
201    let codegen_registry = type_registry.clone();
202
203    let mut world_state = WorldState::new();
204    let expr_arena = Arena::new();
205    let term_arena = Arena::new();
206    let np_arena = Arena::new();
207    let sym_arena = Arena::new();
208    let role_arena = Arena::new();
209    let pp_arena = Arena::new();
210    let stmt_arena: Arena<Stmt> = Arena::new();
211    let imperative_expr_arena: Arena<Expr> = Arena::new();
212    let type_expr_arena: Arena<TypeExpr> = Arena::new();
213
214    let ast_ctx = AstContext::with_types(
215        &expr_arena, &term_arena, &np_arena, &sym_arena,
216        &role_arena, &pp_arena, &stmt_arena, &imperative_expr_arena,
217        &type_expr_arena,
218    );
219
220    let mut parser = Parser::new(tokens, &mut world_state, &mut interner, ast_ctx, type_registry);
221    let stmts = parser.parse_program()?;
222    let stmts = crate::optimize::optimize_program(stmts, &imperative_expr_arena, &stmt_arena, &mut interner);
223
224    Ok(crate::codegen_c::codegen_program_c(&stmts, &codegen_registry, &interner))
225}
226
227/// Compile LOGOS source and return full output including dependency metadata.
228///
229/// This is the primary compilation entry point that returns both the generated
230/// Rust code and any crate dependencies declared in `## Requires` blocks.
231pub fn compile_program_full(source: &str) -> Result<CompileOutput, ParseError> {
232    let mut interner = Interner::new();
233    let mut lexer = Lexer::new(source, &mut interner);
234    let tokens = lexer.tokenize();
235
236    // Pass 1: Discovery - scan for type definitions and policies
237    let (type_registry, policy_registry) = {
238        let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
239        let result = discovery.run_full();
240        (result.types, result.policies)
241    };
242    // Clone for codegen (parser takes ownership)
243    let codegen_registry = type_registry.clone();
244    let codegen_policies = policy_registry.clone();
245
246    let mut world_state = WorldState::new();
247    let expr_arena = Arena::new();
248    let term_arena = Arena::new();
249    let np_arena = Arena::new();
250    let sym_arena = Arena::new();
251    let role_arena = Arena::new();
252    let pp_arena = Arena::new();
253    let stmt_arena: Arena<Stmt> = Arena::new();
254    let imperative_expr_arena: Arena<Expr> = Arena::new();
255    let type_expr_arena: Arena<TypeExpr> = Arena::new();
256
257    let ast_ctx = AstContext::with_types(
258        &expr_arena,
259        &term_arena,
260        &np_arena,
261        &sym_arena,
262        &role_arena,
263        &pp_arena,
264        &stmt_arena,
265        &imperative_expr_arena,
266        &type_expr_arena,
267    );
268
269    // Pass 2: Parse with type context
270    let mut parser = Parser::new(tokens, &mut world_state, &mut interner, ast_ctx, type_registry);
271    // Note: Don't call process_block_headers() - parse_program handles blocks itself
272
273    let stmts = parser.parse_program()?;
274
275    // Pass 2.5: Optimization - constant folding and dead code elimination
276    let stmts = crate::optimize::optimize_program(stmts, &imperative_expr_arena, &stmt_arena, &mut interner);
277
278    // Extract dependencies before escape analysis
279    let mut dependencies = extract_dependencies(&stmts, &interner)?;
280
281    // FFI: Auto-inject wasm-bindgen dependency if any function is exported for WASM
282    let needs_wasm_bindgen = stmts.iter().any(|stmt| {
283        if let Stmt::FunctionDef { is_exported: true, export_target: Some(target), .. } = stmt {
284            interner.resolve(*target).eq_ignore_ascii_case("wasm")
285        } else {
286            false
287        }
288    });
289    if needs_wasm_bindgen && !dependencies.iter().any(|d| d.name == "wasm-bindgen") {
290        dependencies.push(CrateDependency {
291            name: "wasm-bindgen".to_string(),
292            version: "0.2".to_string(),
293            features: vec![],
294        });
295    }
296
297    // Pass 3: Escape analysis - check for zone escape violations
298    // This catches obvious cases like returning zone-local variables
299    let mut escape_checker = EscapeChecker::new(&interner);
300    escape_checker.check_program(&stmts).map_err(|e| {
301        // Convert EscapeError to ParseError for now
302        // The error message is already Socratic from EscapeChecker
303        ParseError {
304            kind: crate::error::ParseErrorKind::Custom(e.to_string()),
305            span: e.span,
306        }
307    })?;
308
309    // Note: Static verification is available when the `verification` feature is enabled,
310    // but must be explicitly invoked via compile_to_rust_verified().
311
312    let type_env = crate::analysis::check_program(&stmts, &interner, &codegen_registry)
313        .map_err(|e| ParseError {
314            kind: e.to_parse_error_kind(&interner),
315            span: crate::token::Span::default(),
316        })?;
317    let rust_code = codegen_program(&stmts, &codegen_registry, &codegen_policies, &interner, &type_env);
318
319    // Universal ABI: Generate C header + bindings if any C exports exist
320    let has_c = stmts.iter().any(|stmt| {
321        if let Stmt::FunctionDef { is_exported: true, export_target, .. } = stmt {
322            match export_target {
323                None => true,
324                Some(t) => interner.resolve(*t).eq_ignore_ascii_case("c"),
325            }
326        } else {
327            false
328        }
329    });
330
331    let c_header = if has_c {
332        Some(generate_c_header(&stmts, "module", &interner, &codegen_registry))
333    } else {
334        None
335    };
336
337    // Auto-inject serde_json dependency when C exports exist (needed for collection to_json and portable struct JSON accessors)
338    if has_c && !dependencies.iter().any(|d| d.name == "serde_json") {
339        dependencies.push(CrateDependency {
340            name: "serde_json".to_string(),
341            version: "1".to_string(),
342            features: vec![],
343        });
344    }
345
346    let python_bindings = if has_c {
347        Some(generate_python_bindings(&stmts, "module", &interner, &codegen_registry))
348    } else {
349        None
350    };
351
352    let (typescript_bindings, typescript_types) = if has_c {
353        let (js, dts) = generate_typescript_bindings(&stmts, "module", &interner, &codegen_registry);
354        (Some(js), Some(dts))
355    } else {
356        (None, None)
357    };
358
359    Ok(CompileOutput { rust_code, dependencies, c_header, python_bindings, typescript_types, typescript_bindings })
360}
361
362/// Extract crate dependencies from `Stmt::Require` nodes.
363///
364/// Deduplicates by crate name: same name + same version keeps one copy.
365/// Same name + different version returns a `ParseError`.
366/// Preserves declaration order (first occurrence wins).
367fn extract_dependencies(stmts: &[Stmt], interner: &Interner) -> Result<Vec<CrateDependency>, ParseError> {
368    use std::collections::HashMap;
369
370    let mut seen: HashMap<String, String> = HashMap::new(); // name → version
371    let mut deps: Vec<CrateDependency> = Vec::new();
372
373    for stmt in stmts {
374        if let Stmt::Require { crate_name, version, features, span } = stmt {
375            let name = interner.resolve(*crate_name).to_string();
376            let ver = interner.resolve(*version).to_string();
377
378            if let Some(existing_ver) = seen.get(&name) {
379                if *existing_ver != ver {
380                    return Err(ParseError {
381                        kind: crate::error::ParseErrorKind::Custom(format!(
382                            "Conflicting versions for crate \"{}\": \"{}\" and \"{}\".",
383                            name, existing_ver, ver
384                        )),
385                        span: *span,
386                    });
387                }
388                // Same name + same version: skip duplicate
389            } else {
390                seen.insert(name.clone(), ver.clone());
391                deps.push(CrateDependency {
392                    name,
393                    version: ver,
394                    features: features.iter().map(|f| interner.resolve(*f).to_string()).collect(),
395                });
396            }
397        }
398    }
399
400    Ok(deps)
401}
402
403/// Compile LOGOS source to Rust with ownership checking enabled.
404///
405/// This runs the lightweight ownership analysis pass that catches use-after-move
406/// errors with control flow awareness. The analysis is fast enough to run on
407/// every keystroke in an IDE.
408///
409/// # Arguments
410///
411/// * `source` - LOGOS source code as a string
412///
413/// # Returns
414///
415/// Generated Rust source code on success.
416///
417/// # Errors
418///
419/// Returns [`ParseError`] if:
420/// - Any error from [`compile_to_rust`] occurs
421/// - Ownership analysis detects use-after-move
422/// - Ownership analysis detects use-after-borrow violations
423///
424/// # Example
425///
426/// ```
427/// # use logicaffeine_compile::compile::compile_to_rust_checked;
428/// // This will fail ownership checking
429/// let source = "## Main\nLet x be 5.\nGive x to y.\nShow x.";
430/// let result = compile_to_rust_checked(source);
431/// assert!(result.is_err()); // "x has already been given away"
432/// ```
433///
434/// # Use Case
435///
436/// Use this function with the `--check` CLI flag for instant feedback on
437/// ownership errors before running the full Rust compilation.
438pub fn compile_to_rust_checked(source: &str) -> Result<String, ParseError> {
439    let mut interner = Interner::new();
440    let mut lexer = Lexer::new(source, &mut interner);
441    let tokens = lexer.tokenize();
442
443    // Pass 1: Discovery - scan for type definitions and policies
444    let (type_registry, policy_registry) = {
445        let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
446        let result = discovery.run_full();
447        (result.types, result.policies)
448    };
449    // Clone for codegen (parser takes ownership)
450    let codegen_registry = type_registry.clone();
451    let codegen_policies = policy_registry.clone();
452
453    let mut world_state = WorldState::new();
454    let expr_arena = Arena::new();
455    let term_arena = Arena::new();
456    let np_arena = Arena::new();
457    let sym_arena = Arena::new();
458    let role_arena = Arena::new();
459    let pp_arena = Arena::new();
460    let stmt_arena: Arena<Stmt> = Arena::new();
461    let imperative_expr_arena: Arena<Expr> = Arena::new();
462    let type_expr_arena: Arena<TypeExpr> = Arena::new();
463
464    let ast_ctx = AstContext::with_types(
465        &expr_arena,
466        &term_arena,
467        &np_arena,
468        &sym_arena,
469        &role_arena,
470        &pp_arena,
471        &stmt_arena,
472        &imperative_expr_arena,
473        &type_expr_arena,
474    );
475
476    // Pass 2: Parse with type context
477    let mut parser = Parser::new(tokens, &mut world_state, &mut interner, ast_ctx, type_registry);
478    let stmts = parser.parse_program()?;
479
480    // Pass 2.5: Optimization - constant folding, propagation, and dead code elimination
481    let stmts = crate::optimize::optimize_program(stmts, &imperative_expr_arena, &stmt_arena, &mut interner);
482
483    // Pass 3: Escape analysis
484    let mut escape_checker = EscapeChecker::new(&interner);
485    escape_checker.check_program(&stmts).map_err(|e| {
486        ParseError {
487            kind: crate::error::ParseErrorKind::Custom(e.to_string()),
488            span: e.span,
489        }
490    })?;
491
492    // Pass 4: Ownership analysis
493    // Catches use-after-move errors with control flow awareness
494    let mut ownership_checker = OwnershipChecker::new(&interner);
495    ownership_checker.check_program(&stmts).map_err(|e| {
496        ParseError {
497            kind: crate::error::ParseErrorKind::Custom(e.to_string()),
498            span: e.span,
499        }
500    })?;
501
502    let type_env = crate::analysis::check_program(&stmts, &interner, &codegen_registry)
503        .map_err(|e| ParseError {
504            kind: e.to_parse_error_kind(&interner),
505            span: crate::token::Span::default(),
506        })?;
507    let rust_code = codegen_program(&stmts, &codegen_registry, &codegen_policies, &interner, &type_env);
508
509    Ok(rust_code)
510}
511
512/// Compile LOGOS source to Rust with full Z3 static verification.
513///
514/// This runs the Z3-based verifier on Assert statements before code generation,
515/// proving that assertions hold for all possible inputs. This is the most
516/// thorough compilation mode, suitable for high-assurance code.
517///
518/// # Arguments
519///
520/// * `source` - LOGOS source code as a string
521///
522/// # Returns
523///
524/// Generated Rust source code on success.
525///
526/// # Errors
527///
528/// Returns [`ParseError`] if:
529/// - Any error from [`compile_to_rust`] occurs
530/// - Z3 cannot prove an Assert statement
531/// - Refinement type constraints cannot be satisfied
532/// - Termination cannot be proven for loops with `decreasing`
533///
534/// # Example
535///
536/// ```no_run
537/// # use logicaffeine_compile::compile::compile_to_rust_verified;
538/// # use logicaffeine_compile::ParseError;
539/// # fn main() -> Result<(), ParseError> {
540/// let source = r#"
541/// ## Main
542/// Let x: { it: Int | it > 0 } be 5.
543/// Assert that x > 0.
544/// "#;
545/// let rust_code = compile_to_rust_verified(source)?;
546/// # Ok(())
547/// # }
548/// ```
549///
550/// # Feature Flag
551///
552/// This function requires the `verification` feature to be enabled:
553///
554/// ```toml
555/// [dependencies]
556/// logicaffeine_compile = { version = "...", features = ["verification"] }
557/// ```
558#[cfg(feature = "verification")]
559pub fn compile_to_rust_verified(source: &str) -> Result<String, ParseError> {
560    use crate::verification::VerificationPass;
561
562    let mut interner = Interner::new();
563    let mut lexer = Lexer::new(source, &mut interner);
564    let tokens = lexer.tokenize();
565
566    // Pass 1: Discovery - scan for type definitions and policies
567    let (type_registry, policy_registry) = {
568        let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
569        let result = discovery.run_full();
570        (result.types, result.policies)
571    };
572    // Clone for codegen (parser takes ownership)
573    let codegen_registry = type_registry.clone();
574    let codegen_policies = policy_registry.clone();
575
576    let mut world_state = WorldState::new();
577    let expr_arena = Arena::new();
578    let term_arena = Arena::new();
579    let np_arena = Arena::new();
580    let sym_arena = Arena::new();
581    let role_arena = Arena::new();
582    let pp_arena = Arena::new();
583    let stmt_arena: Arena<Stmt> = Arena::new();
584    let imperative_expr_arena: Arena<Expr> = Arena::new();
585    let type_expr_arena: Arena<TypeExpr> = Arena::new();
586
587    let ast_ctx = AstContext::with_types(
588        &expr_arena,
589        &term_arena,
590        &np_arena,
591        &sym_arena,
592        &role_arena,
593        &pp_arena,
594        &stmt_arena,
595        &imperative_expr_arena,
596        &type_expr_arena,
597    );
598
599    // Pass 2: Parse with type context
600    let mut parser = Parser::new(tokens, &mut world_state, &mut interner, ast_ctx, type_registry);
601    let stmts = parser.parse_program()?;
602
603    // Pass 3: Escape analysis
604    let mut escape_checker = EscapeChecker::new(&interner);
605    escape_checker.check_program(&stmts).map_err(|e| {
606        ParseError {
607            kind: crate::error::ParseErrorKind::Custom(e.to_string()),
608            span: e.span,
609        }
610    })?;
611
612    // Pass 4: Static verification
613    let mut verifier = VerificationPass::new(&interner);
614    verifier.verify_program(&stmts).map_err(|e| {
615        ParseError {
616            kind: crate::error::ParseErrorKind::Custom(format!(
617                "Verification Failed:\n\n{}",
618                e
619            )),
620            span: crate::token::Span::default(),
621        }
622    })?;
623
624    let type_env = crate::analysis::check_program(&stmts, &interner, &codegen_registry)
625        .map_err(|e| ParseError {
626            kind: e.to_parse_error_kind(&interner),
627            span: crate::token::Span::default(),
628        })?;
629    let rust_code = codegen_program(&stmts, &codegen_registry, &codegen_policies, &interner, &type_env);
630
631    Ok(rust_code)
632}
633
634/// Compile LOGOS source and write output to a directory as a Cargo project.
635///
636/// Creates a complete Cargo project structure with:
637/// - `src/main.rs` containing the generated Rust code
638/// - `Cargo.toml` with runtime dependencies
639/// - `crates/` directory with runtime crate copies
640///
641/// # Arguments
642///
643/// * `source` - LOGOS source code as a string
644/// * `output_dir` - Directory to create the Cargo project in
645///
646/// # Errors
647///
648/// Returns [`CompileError`] if:
649/// - Compilation fails (wrapped as `CompileError::Parse`)
650/// - File system operations fail (wrapped as `CompileError::Io`)
651///
652/// # Example
653///
654/// ```no_run
655/// # use logicaffeine_compile::compile::{compile_to_dir, CompileError};
656/// # use std::path::Path;
657/// # fn main() -> Result<(), CompileError> {
658/// let source = "## Main\nShow \"Hello\".";
659/// compile_to_dir(source, Path::new("/tmp/my_project"))?;
660/// // Now /tmp/my_project is a buildable Cargo project
661/// # Ok(())
662/// # }
663/// ```
664pub fn compile_to_dir(source: &str, output_dir: &Path) -> Result<(), CompileError> {
665    let output = compile_program_full(source).map_err(CompileError::Parse)?;
666
667    // Create output directory structure
668    let src_dir = output_dir.join("src");
669    fs::create_dir_all(&src_dir).map_err(|e| CompileError::Io(e.to_string()))?;
670
671    // Write main.rs (codegen already includes the use statements)
672    let main_path = src_dir.join("main.rs");
673    let mut file = fs::File::create(&main_path).map_err(|e| CompileError::Io(e.to_string()))?;
674    file.write_all(output.rust_code.as_bytes()).map_err(|e| CompileError::Io(e.to_string()))?;
675
676    // Write Cargo.toml with runtime crate dependencies
677    let mut cargo_toml = String::from(r#"[package]
678name = "logos_output"
679version = "0.1.0"
680edition = "2021"
681
682[dependencies]
683logicaffeine-data = { path = "./crates/logicaffeine_data" }
684logicaffeine-system = { path = "./crates/logicaffeine_system", features = ["full"] }
685tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
686
687[target.'cfg(target_os = "linux")'.dependencies]
688logicaffeine-system = { path = "./crates/logicaffeine_system", features = ["full", "io-uring"] }
689"#);
690
691    // Append user-declared dependencies from ## Requires blocks
692    for dep in &output.dependencies {
693        if dep.features.is_empty() {
694            let _ = writeln!(cargo_toml, "{} = \"{}\"", dep.name, dep.version);
695        } else {
696            let feats = dep.features.iter()
697                .map(|f| format!("\"{}\"", f))
698                .collect::<Vec<_>>()
699                .join(", ");
700            let _ = writeln!(
701                cargo_toml,
702                "{} = {{ version = \"{}\", features = [{}] }}",
703                dep.name, dep.version, feats
704            );
705        }
706    }
707
708    cargo_toml.push_str("\n[profile.release]\nlto = true\nopt-level = 3\ncodegen-units = 1\npanic = \"abort\"\nstrip = true\n");
709
710    let cargo_path = output_dir.join("Cargo.toml");
711    let mut file = fs::File::create(&cargo_path).map_err(|e| CompileError::Io(e.to_string()))?;
712    file.write_all(cargo_toml.as_bytes()).map_err(|e| CompileError::Io(e.to_string()))?;
713
714    // Write .cargo/config.toml with target-cpu=native for optimal codegen.
715    // Enables SIMD auto-vectorization and CPU-specific instruction selection.
716    let cargo_config_dir = output_dir.join(".cargo");
717    fs::create_dir_all(&cargo_config_dir).map_err(|e| CompileError::Io(e.to_string()))?;
718    let config_content = "[build]\nrustflags = [\"-C\", \"target-cpu=native\"]\n";
719    let config_path = cargo_config_dir.join("config.toml");
720    fs::write(&config_path, config_content).map_err(|e| CompileError::Io(e.to_string()))?;
721
722    // Copy runtime crates to output directory
723    copy_runtime_crates(output_dir)?;
724
725    Ok(())
726}
727
728/// Copy the runtime crates to the output directory.
729/// Copies logicaffeine_data and logicaffeine_system.
730pub fn copy_runtime_crates(output_dir: &Path) -> Result<(), CompileError> {
731    let crates_dir = output_dir.join("crates");
732    fs::create_dir_all(&crates_dir).map_err(|e| CompileError::Io(e.to_string()))?;
733
734    // Find workspace root
735    let workspace_root = find_workspace_root()?;
736
737    // Copy logicaffeine_data
738    let data_src = workspace_root.join(CRATES_DATA_PATH);
739    let data_dest = crates_dir.join("logicaffeine_data");
740    copy_dir_recursive(&data_src, &data_dest)?;
741    deworkspace_cargo_toml(&data_dest.join("Cargo.toml"))?;
742
743    // Copy logicaffeine_system
744    let system_src = workspace_root.join(CRATES_SYSTEM_PATH);
745    let system_dest = crates_dir.join("logicaffeine_system");
746    copy_dir_recursive(&system_src, &system_dest)?;
747    deworkspace_cargo_toml(&system_dest.join("Cargo.toml"))?;
748
749    // Also need to copy logicaffeine_base since both crates depend on it
750    let base_src = workspace_root.join("crates/logicaffeine_base");
751    let base_dest = crates_dir.join("logicaffeine_base");
752    copy_dir_recursive(&base_src, &base_dest)?;
753    deworkspace_cargo_toml(&base_dest.join("Cargo.toml"))?;
754
755    Ok(())
756}
757
758/// Resolve workspace-inherited fields in a copied crate's Cargo.toml.
759///
760/// When runtime crates are copied to a standalone project, any fields using
761/// `*.workspace = true` won't resolve because there's no parent workspace.
762/// This rewrites them with concrete values (matching the workspace's settings).
763fn deworkspace_cargo_toml(cargo_toml_path: &Path) -> Result<(), CompileError> {
764    let content = fs::read_to_string(cargo_toml_path)
765        .map_err(|e| CompileError::Io(e.to_string()))?;
766
767    let mut result = String::with_capacity(content.len());
768    for line in content.lines() {
769        let trimmed = line.trim();
770        if trimmed == "edition.workspace = true" {
771            result.push_str("edition = \"2021\"");
772        } else if trimmed == "rust-version.workspace = true" {
773            result.push_str("rust-version = \"1.75\"");
774        } else if trimmed == "authors.workspace = true"
775            || trimmed == "repository.workspace = true"
776            || trimmed == "homepage.workspace = true"
777            || trimmed == "documentation.workspace = true"
778            || trimmed == "keywords.workspace = true"
779            || trimmed == "categories.workspace = true"
780            || trimmed == "license.workspace = true"
781        {
782            // Drop these lines — they're metadata not needed for compilation
783            continue;
784        } else if trimmed.contains(".workspace = true") {
785            // Catch-all: drop any other workspace-inherited fields
786            continue;
787        } else {
788            result.push_str(line);
789        }
790        result.push('\n');
791    }
792
793    fs::write(cargo_toml_path, result)
794        .map_err(|e| CompileError::Io(e.to_string()))?;
795
796    Ok(())
797}
798
799/// Find the workspace root directory.
800fn find_workspace_root() -> Result<std::path::PathBuf, CompileError> {
801    // 1. Explicit override via LOGOS_WORKSPACE env var
802    if let Ok(workspace) = std::env::var("LOGOS_WORKSPACE") {
803        let path = Path::new(&workspace);
804        if path.join("Cargo.toml").exists() && path.join("crates").exists() {
805            return Ok(path.to_path_buf());
806        }
807    }
808
809    // 2. Try CARGO_MANIFEST_DIR (works during cargo build of largo itself)
810    if let Ok(manifest_dir) = std::env::var("CARGO_MANIFEST_DIR") {
811        let path = Path::new(&manifest_dir);
812        if let Some(parent) = path.parent().and_then(|p| p.parent()) {
813            if parent.join("Cargo.toml").exists() {
814                return Ok(parent.to_path_buf());
815            }
816        }
817    }
818
819    // 3. Infer from the largo binary's own location
820    //    e.g. /workspace/target/release/largo → /workspace
821    if let Ok(exe) = std::env::current_exe() {
822        if let Some(dir) = exe.parent() {
823            // Walk up from the binary's directory
824            let mut candidate = dir.to_path_buf();
825            for _ in 0..5 {
826                if candidate.join("Cargo.toml").exists() && candidate.join("crates").exists() {
827                    return Ok(candidate);
828                }
829                if !candidate.pop() {
830                    break;
831                }
832            }
833        }
834    }
835
836    // 4. Fallback to current directory traversal
837    let mut current = std::env::current_dir()
838        .map_err(|e| CompileError::Io(e.to_string()))?;
839
840    loop {
841        if current.join("Cargo.toml").exists() && current.join("crates").exists() {
842            return Ok(current);
843        }
844        if !current.pop() {
845            return Err(CompileError::Io(
846                "Could not find workspace root. Set LOGOS_WORKSPACE env var or run from within the workspace.".to_string()
847            ));
848        }
849    }
850}
851
852/// Recursively copy a directory.
853/// Skips files that disappear during copy (race condition with parallel builds).
854fn copy_dir_recursive(src: &Path, dst: &Path) -> Result<(), CompileError> {
855    fs::create_dir_all(dst).map_err(|e| CompileError::Io(e.to_string()))?;
856
857    for entry in fs::read_dir(src).map_err(|e| CompileError::Io(e.to_string()))? {
858        let entry = entry.map_err(|e| CompileError::Io(e.to_string()))?;
859        let src_path = entry.path();
860        let file_name = entry.file_name();
861        let dst_path = dst.join(&file_name);
862
863        // Skip target directory, build artifacts, and lock files
864        if file_name == "target"
865            || file_name == ".git"
866            || file_name == "Cargo.lock"
867            || file_name == ".DS_Store"
868        {
869            continue;
870        }
871
872        // Skip files that start with a dot (hidden files)
873        if file_name.to_string_lossy().starts_with('.') {
874            continue;
875        }
876
877        // Check if path still exists (race condition protection)
878        if !src_path.exists() {
879            continue;
880        }
881
882        if src_path.is_dir() {
883            copy_dir_recursive(&src_path, &dst_path)?;
884        } else if file_name == "Cargo.toml" {
885            // Special handling for Cargo.toml: remove [workspace] line
886            // which can interfere with nested crate dependencies
887            match fs::read_to_string(&src_path) {
888                Ok(content) => {
889                    let filtered: String = content
890                        .lines()
891                        .filter(|line| !line.trim().starts_with("[workspace]"))
892                        .collect::<Vec<_>>()
893                        .join("\n");
894                    fs::write(&dst_path, filtered)
895                        .map_err(|e| CompileError::Io(e.to_string()))?;
896                }
897                Err(e) if e.kind() == std::io::ErrorKind::NotFound => continue,
898                Err(e) => return Err(CompileError::Io(e.to_string())),
899            }
900        } else {
901            match fs::copy(&src_path, &dst_path) {
902                Ok(_) => {}
903                Err(e) if e.kind() == std::io::ErrorKind::NotFound => continue,
904                Err(e) => return Err(CompileError::Io(e.to_string())),
905            }
906        }
907    }
908
909    Ok(())
910}
911
912/// Compile and run a LOGOS program end-to-end.
913///
914/// This function performs the full compilation workflow:
915/// 1. Compile LOGOS to Rust via [`compile_to_dir`]
916/// 2. Run `cargo build` with JSON diagnostics
917/// 3. Translate any rustc errors to LOGOS-friendly messages
918/// 4. Run the compiled program via `cargo run`
919///
920/// # Arguments
921///
922/// * `source` - LOGOS source code as a string
923/// * `output_dir` - Directory to create the temporary Cargo project in
924///
925/// # Returns
926///
927/// The stdout output of the executed program.
928///
929/// # Errors
930///
931/// Returns [`CompileError`] if:
932/// - Compilation fails (see [`compile_to_dir`])
933/// - Rust compilation fails (`CompileError::Build` or `CompileError::Ownership`)
934/// - The program crashes at runtime (`CompileError::Runtime`)
935///
936/// # Diagnostic Translation
937///
938/// When rustc reports errors (e.g., E0382 for use-after-move), this function
939/// uses the [`diagnostic`](crate::diagnostic) module to translate them into
940/// LOGOS-friendly Socratic error messages.
941///
942/// # Example
943///
944/// ```no_run
945/// # use logicaffeine_compile::compile::{compile_and_run, CompileError};
946/// # use std::path::Path;
947/// # fn main() -> Result<(), CompileError> {
948/// let source = "## Main\nShow \"Hello, World!\".";
949/// let output = compile_and_run(source, Path::new("/tmp/run"))?;
950/// assert_eq!(output.trim(), "Hello, World!");
951/// # Ok(())
952/// # }
953/// ```
954pub fn compile_and_run(source: &str, output_dir: &Path) -> Result<String, CompileError> {
955    // Pre-check: catch ownership errors (use-after-move) with friendly messages
956    // before codegen runs (codegen defensively clones, masking these errors)
957    compile_to_rust_checked(source).map_err(CompileError::Parse)?;
958
959    compile_to_dir(source, output_dir)?;
960
961    // Run cargo build with JSON message format for structured error parsing
962    let build_output = Command::new("cargo")
963        .arg("build")
964        .arg("--message-format=json")
965        .current_dir(output_dir)
966        .output()
967        .map_err(|e| CompileError::Io(e.to_string()))?;
968
969    if !build_output.status.success() {
970        let stderr = String::from_utf8_lossy(&build_output.stderr);
971        let stdout = String::from_utf8_lossy(&build_output.stdout);
972
973        // Try to parse JSON diagnostics and translate them
974        let diagnostics = parse_rustc_json(&stdout);
975
976        if !diagnostics.is_empty() {
977            // Create a basic source map with the LOGOS source
978            let source_map = SourceMap::new(source.to_string());
979            let interner = Interner::new();
980
981            if let Some(logos_error) = translate_diagnostics(&diagnostics, &source_map, &interner) {
982                return Err(CompileError::Ownership(logos_error));
983            }
984        }
985
986        // Fallback to raw error if translation fails
987        return Err(CompileError::Build(stderr.to_string()));
988    }
989
990    // Run the compiled program
991    let run_output = Command::new("cargo")
992        .arg("run")
993        .arg("--quiet")
994        .current_dir(output_dir)
995        .output()
996        .map_err(|e| CompileError::Io(e.to_string()))?;
997
998    if !run_output.status.success() {
999        let stderr = String::from_utf8_lossy(&run_output.stderr);
1000        return Err(CompileError::Runtime(stderr.to_string()));
1001    }
1002
1003    let stdout = String::from_utf8_lossy(&run_output.stdout);
1004    Ok(stdout.to_string())
1005}
1006
1007/// Compile a LOGOS source file.
1008/// For single-file compilation without dependencies.
1009pub fn compile_file(path: &Path) -> Result<String, CompileError> {
1010    let source = fs::read_to_string(path).map_err(|e| CompileError::Io(e.to_string()))?;
1011    compile_to_rust(&source).map_err(CompileError::Parse)
1012}
1013
1014/// Compile a multi-file LOGOS project with dependency resolution.
1015///
1016/// This function:
1017/// 1. Reads the entry file
1018/// 2. Scans for dependencies in the abstract (Markdown links)
1019/// 3. Recursively loads and discovers types from dependencies
1020/// 4. Compiles with the combined type registry
1021///
1022/// # Arguments
1023/// * `entry_file` - The main entry file to compile (root is derived from parent directory)
1024///
1025/// # Example
1026/// ```no_run
1027/// # use logicaffeine_compile::compile::compile_project;
1028/// # use std::path::Path;
1029/// let result = compile_project(Path::new("/project/main.md"));
1030/// ```
1031pub fn compile_project(entry_file: &Path) -> Result<CompileOutput, CompileError> {
1032    use crate::loader::Loader;
1033    use crate::analysis::discover_with_imports;
1034
1035    let root_path = entry_file.parent().unwrap_or(Path::new(".")).to_path_buf();
1036    let mut loader = Loader::new(root_path);
1037    let mut interner = Interner::new();
1038
1039    // Read the entry file
1040    let source = fs::read_to_string(entry_file)
1041        .map_err(|e| CompileError::Io(format!("Failed to read entry file: {}", e)))?;
1042
1043    // Discover types from entry file and all imports
1044    let type_registry = discover_with_imports(entry_file, &source, &mut loader, &mut interner)
1045        .map_err(|e| CompileError::Io(e))?;
1046
1047    // Now compile with the discovered types
1048    compile_to_rust_with_registry_full(&source, type_registry, &mut interner)
1049        .map_err(CompileError::Parse)
1050}
1051
1052/// Compile LOGOS source with a pre-populated type registry, returning full output.
1053/// Returns both generated Rust code and extracted dependencies.
1054fn compile_to_rust_with_registry_full(
1055    source: &str,
1056    type_registry: crate::analysis::TypeRegistry,
1057    interner: &mut Interner,
1058) -> Result<CompileOutput, ParseError> {
1059    let mut lexer = Lexer::new(source, interner);
1060    let tokens = lexer.tokenize();
1061
1062    // Discovery pass for policies (types already discovered)
1063    let policy_registry = {
1064        let mut discovery = DiscoveryPass::new(&tokens, interner);
1065        discovery.run_full().policies
1066    };
1067
1068    let codegen_registry = type_registry.clone();
1069    let codegen_policies = policy_registry.clone();
1070
1071    let mut world_state = WorldState::new();
1072    let expr_arena = Arena::new();
1073    let term_arena = Arena::new();
1074    let np_arena = Arena::new();
1075    let sym_arena = Arena::new();
1076    let role_arena = Arena::new();
1077    let pp_arena = Arena::new();
1078    let stmt_arena: Arena<Stmt> = Arena::new();
1079    let imperative_expr_arena: Arena<Expr> = Arena::new();
1080    let type_expr_arena: Arena<TypeExpr> = Arena::new();
1081
1082    let ast_ctx = AstContext::with_types(
1083        &expr_arena,
1084        &term_arena,
1085        &np_arena,
1086        &sym_arena,
1087        &role_arena,
1088        &pp_arena,
1089        &stmt_arena,
1090        &imperative_expr_arena,
1091        &type_expr_arena,
1092    );
1093
1094    let mut parser = Parser::new(tokens, &mut world_state, interner, ast_ctx, type_registry);
1095    let stmts = parser.parse_program()?;
1096
1097    // Extract dependencies before escape analysis
1098    let mut dependencies = extract_dependencies(&stmts, interner)?;
1099
1100    // FFI: Auto-inject wasm-bindgen dependency if any function is exported for WASM
1101    let needs_wasm_bindgen = stmts.iter().any(|stmt| {
1102        if let Stmt::FunctionDef { is_exported: true, export_target: Some(target), .. } = stmt {
1103            interner.resolve(*target).eq_ignore_ascii_case("wasm")
1104        } else {
1105            false
1106        }
1107    });
1108    if needs_wasm_bindgen && !dependencies.iter().any(|d| d.name == "wasm-bindgen") {
1109        dependencies.push(CrateDependency {
1110            name: "wasm-bindgen".to_string(),
1111            version: "0.2".to_string(),
1112            features: vec![],
1113        });
1114    }
1115
1116    let mut escape_checker = EscapeChecker::new(interner);
1117    escape_checker.check_program(&stmts).map_err(|e| {
1118        ParseError {
1119            kind: crate::error::ParseErrorKind::Custom(e.to_string()),
1120            span: e.span,
1121        }
1122    })?;
1123
1124    let type_env = crate::analysis::check_program(&stmts, interner, &codegen_registry)
1125        .map_err(|e| ParseError {
1126            kind: e.to_parse_error_kind(interner),
1127            span: crate::token::Span::default(),
1128        })?;
1129    let rust_code = codegen_program(&stmts, &codegen_registry, &codegen_policies, interner, &type_env);
1130
1131    // Universal ABI: Generate C header + bindings if any C exports exist
1132    let has_c = stmts.iter().any(|stmt| {
1133        if let Stmt::FunctionDef { is_exported: true, export_target, .. } = stmt {
1134            match export_target {
1135                None => true,
1136                Some(t) => interner.resolve(*t).eq_ignore_ascii_case("c"),
1137            }
1138        } else {
1139            false
1140        }
1141    });
1142
1143    let c_header = if has_c {
1144        Some(generate_c_header(&stmts, "module", interner, &codegen_registry))
1145    } else {
1146        None
1147    };
1148
1149    if has_c && !dependencies.iter().any(|d| d.name == "serde_json") {
1150        dependencies.push(CrateDependency {
1151            name: "serde_json".to_string(),
1152            version: "1".to_string(),
1153            features: vec![],
1154        });
1155    }
1156
1157    let python_bindings = if has_c {
1158        Some(generate_python_bindings(&stmts, "module", interner, &codegen_registry))
1159    } else {
1160        None
1161    };
1162
1163    let (typescript_bindings, typescript_types) = if has_c {
1164        let (js, dts) = generate_typescript_bindings(&stmts, "module", interner, &codegen_registry);
1165        (Some(js), Some(dts))
1166    } else {
1167        (None, None)
1168    };
1169
1170    Ok(CompileOutput { rust_code, dependencies, c_header, python_bindings, typescript_types, typescript_bindings })
1171}
1172
1173/// Errors that can occur during the LOGOS compilation pipeline.
1174///
1175/// This enum represents the different stages where compilation can fail,
1176/// from parsing through to runtime execution.
1177///
1178/// # Error Hierarchy
1179///
1180/// ```text
1181/// CompileError
1182/// ├── Parse      ← Lexing, parsing, or static analysis
1183/// ├── Io         ← File system operations
1184/// ├── Build      ← Rust compilation (cargo build)
1185/// ├── Ownership  ← Translated borrow checker errors
1186/// └── Runtime    ← Program execution failure
1187/// ```
1188///
1189/// # Error Translation
1190///
1191/// The `Ownership` variant contains LOGOS-friendly error messages translated
1192/// from rustc's borrow checker errors (E0382, E0505, E0597) using the
1193/// [`diagnostic`](crate::diagnostic) module.
1194#[derive(Debug)]
1195pub enum CompileError {
1196    /// Parsing or static analysis failed.
1197    ///
1198    /// This includes lexer errors, syntax errors, escape analysis failures,
1199    /// ownership analysis failures, and Z3 verification failures.
1200    Parse(ParseError),
1201
1202    /// File system operation failed.
1203    ///
1204    /// Typically occurs when reading source files or writing output.
1205    Io(String),
1206
1207    /// Rust compilation failed (`cargo build`).
1208    ///
1209    /// Contains the raw stderr output from rustc when diagnostic translation
1210    /// was not possible.
1211    Build(String),
1212
1213    /// Runtime execution failed.
1214    ///
1215    /// Contains stderr output from the executed program.
1216    Runtime(String),
1217
1218    /// Translated ownership/borrow checker error with LOGOS-friendly message.
1219    ///
1220    /// This variant is used when rustc reports errors like E0382 (use after move)
1221    /// and we can translate them into natural language error messages that
1222    /// reference the original LOGOS source.
1223    Ownership(LogosError),
1224}
1225
1226impl std::fmt::Display for CompileError {
1227    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1228        match self {
1229            CompileError::Parse(e) => write!(f, "Parse error: {:?}", e),
1230            CompileError::Io(e) => write!(f, "IO error: {}", e),
1231            CompileError::Build(e) => write!(f, "Build error: {}", e),
1232            CompileError::Runtime(e) => write!(f, "Runtime error: {}", e),
1233            CompileError::Ownership(e) => write!(f, "{}", e),
1234        }
1235    }
1236}
1237
1238impl std::error::Error for CompileError {}
1239
1240// ============================================================
1241// Futamura Projection Support — encode_program + verify_no_overhead
1242// ============================================================
1243
1244/// Encode a LogicAffeine program (given as source) into CProgram construction source.
1245///
1246/// Takes LogicAffeine source code (with or without `## Main` header) and returns
1247/// LogicAffeine source code that constructs the equivalent CProgram data structure.
1248/// The result defines a variable `prog` of type CProgram.
1249pub fn encode_program_source(source: &str) -> Result<String, ParseError> {
1250    let full_source = if source.contains("## Main") || source.contains("## To ") {
1251        source.to_string()
1252    } else {
1253        format!("## Main\n{}", source)
1254    };
1255
1256    let mut interner = Interner::new();
1257    let mut lexer = Lexer::new(&full_source, &mut interner);
1258    let tokens = lexer.tokenize();
1259
1260    let type_registry = {
1261        let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
1262        let result = discovery.run_full();
1263        result.types
1264    };
1265
1266    // Collect variant constructors before the parser takes ownership of type_registry
1267    let mut variant_constructors: HashMap<String, Vec<String>> = HashMap::new();
1268    for (_type_name, type_def) in type_registry.iter_types() {
1269        if let crate::analysis::TypeDef::Enum { variants, .. } = type_def {
1270            for variant in variants {
1271                let vname = interner.resolve(variant.name).to_string();
1272                let field_names: Vec<String> = variant.fields.iter()
1273                    .map(|f| interner.resolve(f.name).to_string())
1274                    .collect();
1275                variant_constructors.insert(vname, field_names);
1276            }
1277        }
1278    }
1279
1280    let mut world_state = WorldState::new();
1281    let expr_arena = Arena::new();
1282    let term_arena = Arena::new();
1283    let np_arena = Arena::new();
1284    let sym_arena = Arena::new();
1285    let role_arena = Arena::new();
1286    let pp_arena = Arena::new();
1287    let stmt_arena: Arena<Stmt> = Arena::new();
1288    let imperative_expr_arena: Arena<Expr> = Arena::new();
1289    let type_expr_arena: Arena<TypeExpr> = Arena::new();
1290
1291    let ast_ctx = AstContext::with_types(
1292        &expr_arena, &term_arena, &np_arena, &sym_arena,
1293        &role_arena, &pp_arena, &stmt_arena, &imperative_expr_arena,
1294        &type_expr_arena,
1295    );
1296
1297    let mut parser = crate::parser::Parser::new(
1298        tokens, &mut world_state, &mut interner, ast_ctx, type_registry,
1299    );
1300    let stmts = parser.parse_program()?;
1301
1302    let mut functions: Vec<(String, Vec<String>, Vec<&Stmt>)> = Vec::new();
1303    let mut main_stmts: Vec<&Stmt> = Vec::new();
1304
1305    for stmt in &stmts {
1306        if let Stmt::FunctionDef { name, params, body, .. } = stmt {
1307            let fn_name = interner.resolve(*name).to_string();
1308            let param_names: Vec<String> = params
1309                .iter()
1310                .map(|(name, _)| interner.resolve(*name).to_string())
1311                .collect();
1312            let body_stmts: Vec<&Stmt> = body.iter().collect();
1313            functions.push((fn_name, param_names, body_stmts));
1314        } else {
1315            main_stmts.push(stmt);
1316        }
1317    }
1318
1319    let mut counter = 0usize;
1320    let mut output = String::new();
1321
1322    // Build the funcMap directly (Map of Text to CFunc) with fixed name
1323    output.push_str("Let encodedFuncMap be a new Map of Text to CFunc.\n");
1324
1325    for (fn_name, params, body) in &functions {
1326        let body_var = encode_stmt_list_src(body, &mut counter, &mut output, &interner, &variant_constructors);
1327
1328        let params_var = format!("params_{}", counter);
1329        counter += 1;
1330        output.push_str(&format!("Let {} be a new Seq of Text.\n", params_var));
1331        for p in params {
1332            output.push_str(&format!("Push \"{}\" to {}.\n", p, params_var));
1333        }
1334
1335        let func_var = format!("func_{}", counter);
1336        counter += 1;
1337        output.push_str(&format!(
1338            "Let {} be a new CFuncDef with name \"{}\" and params {} and body {}.\n",
1339            func_var, fn_name, params_var, body_var
1340        ));
1341        output.push_str(&format!(
1342            "Set item \"{}\" of encodedFuncMap to {}.\n",
1343            fn_name, func_var
1344        ));
1345    }
1346
1347    // Build main statement list with fixed name
1348    let main_var = encode_stmt_list_src(&main_stmts, &mut counter, &mut output, &interner, &variant_constructors);
1349    output.push_str(&format!("Let encodedMain be {}.\n", main_var));
1350
1351    Ok(output)
1352}
1353
1354fn collect_free_vars_expr<'a>(expr: &'a Expr, interner: &Interner, bound: &HashSet<String>) -> HashSet<String> {
1355    let mut free = HashSet::new();
1356    match expr {
1357        Expr::Identifier(sym) => {
1358            let name = interner.resolve(*sym).to_string();
1359            if !bound.contains(&name) {
1360                free.insert(name);
1361            }
1362        }
1363        Expr::BinaryOp { left, right, .. } => {
1364            free.extend(collect_free_vars_expr(left, interner, bound));
1365            free.extend(collect_free_vars_expr(right, interner, bound));
1366        }
1367        Expr::Not { operand } => {
1368            free.extend(collect_free_vars_expr(operand, interner, bound));
1369        }
1370        Expr::Copy { expr: inner } => {
1371            free.extend(collect_free_vars_expr(inner, interner, bound));
1372        }
1373        Expr::CallExpr { callee, args } => {
1374            free.extend(collect_free_vars_expr(callee, interner, bound));
1375            for a in args {
1376                free.extend(collect_free_vars_expr(a, interner, bound));
1377            }
1378        }
1379        Expr::Index { collection, index } => {
1380            free.extend(collect_free_vars_expr(collection, interner, bound));
1381            free.extend(collect_free_vars_expr(index, interner, bound));
1382        }
1383        Expr::InterpolatedString(parts) => {
1384            for part in parts {
1385                if let StringPart::Expr { value, .. } = part {
1386                    free.extend(collect_free_vars_expr(value, interner, bound));
1387                }
1388            }
1389        }
1390        Expr::Closure { params, body, .. } => {
1391            let mut inner_bound = bound.clone();
1392            for (sym, _) in params {
1393                inner_bound.insert(interner.resolve(*sym).to_string());
1394            }
1395            match body {
1396                ClosureBody::Expression(e) => {
1397                    free.extend(collect_free_vars_expr(e, interner, &inner_bound));
1398                }
1399                ClosureBody::Block(stmts) => {
1400                    for s in stmts.iter() {
1401                        free.extend(collect_free_vars_stmt(s, interner, &inner_bound));
1402                    }
1403                }
1404            }
1405        }
1406        _ => {}
1407    }
1408    free
1409}
1410
1411fn collect_free_vars_stmt<'a>(stmt: &'a Stmt, interner: &Interner, bound: &HashSet<String>) -> HashSet<String> {
1412    let mut free = HashSet::new();
1413    match stmt {
1414        Stmt::Let { var, value, .. } => {
1415            free.extend(collect_free_vars_expr(value, interner, bound));
1416        }
1417        Stmt::Set { target, value, .. } => {
1418            let n = interner.resolve(*target).to_string();
1419            if !bound.contains(&n) {
1420                free.insert(n);
1421            }
1422            free.extend(collect_free_vars_expr(value, interner, bound));
1423        }
1424        Stmt::Show { object, .. } => {
1425            free.extend(collect_free_vars_expr(object, interner, bound));
1426        }
1427        Stmt::Return { value } => {
1428            if let Some(v) = value {
1429                free.extend(collect_free_vars_expr(v, interner, bound));
1430            }
1431        }
1432        _ => {}
1433    }
1434    free
1435}
1436
1437fn encode_expr_src(expr: &Expr, counter: &mut usize, output: &mut String, interner: &Interner, variants: &HashMap<String, Vec<String>>) -> String {
1438    let var = format!("e_{}", *counter);
1439    *counter += 1;
1440
1441    match expr {
1442        Expr::Literal(lit) => match lit {
1443            Literal::Number(n) => {
1444                output.push_str(&format!("Let {} be a new CInt with value {}.\n", var, n));
1445            }
1446            Literal::Boolean(b) => {
1447                output.push_str(&format!("Let {} be a new CBool with value {}.\n", var, b));
1448            }
1449            Literal::Text(s) => {
1450                let text = interner.resolve(*s);
1451                output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", var, text));
1452            }
1453            Literal::Float(f) => {
1454                output.push_str(&format!("Let {} be a new CFloat with value {}.\n", var, f));
1455            }
1456            Literal::Duration(nanos) => {
1457                let millis = nanos / 1_000_000;
1458                let amount_var = format!("e_{}", *counter);
1459                *counter += 1;
1460                output.push_str(&format!("Let {} be a new CInt with value {}.\n", amount_var, millis));
1461                output.push_str(&format!("Let {} be a new CDuration with amount {} and unit \"milliseconds\".\n", var, amount_var));
1462            }
1463            Literal::Nothing => {
1464                output.push_str(&format!("Let {} be a new CText with value \"nothing\".\n", var));
1465            }
1466            _ => {
1467                output.push_str(&format!("Let {} be a new CText with value \"unsupported\".\n", var));
1468            }
1469        },
1470        Expr::Identifier(sym) => {
1471            let name = interner.resolve(*sym);
1472            output.push_str(&format!("Let {} be a new CVar with name \"{}\".\n", var, name));
1473        }
1474        Expr::BinaryOp { op, left, right } => {
1475            let left_var = encode_expr_src(left, counter, output, interner, variants);
1476            let right_var = encode_expr_src(right, counter, output, interner, variants);
1477            let op_str = match op {
1478                BinaryOpKind::Add => "+",
1479                BinaryOpKind::Subtract => "-",
1480                BinaryOpKind::Multiply => "*",
1481                BinaryOpKind::Divide => "/",
1482                BinaryOpKind::Modulo => "%",
1483                BinaryOpKind::Eq => "==",
1484                BinaryOpKind::NotEq => "!=",
1485                BinaryOpKind::Lt => "<",
1486                BinaryOpKind::Gt => ">",
1487                BinaryOpKind::LtEq => "<=",
1488                BinaryOpKind::GtEq => ">=",
1489                BinaryOpKind::And => "&&",
1490                BinaryOpKind::Or => "||",
1491                BinaryOpKind::Concat => "+",
1492                BinaryOpKind::BitXor => "^",
1493                BinaryOpKind::Shl => "<<",
1494                BinaryOpKind::Shr => ">>",
1495            };
1496            output.push_str(&format!(
1497                "Let {} be a new CBinOp with op \"{}\" and left {} and right {}.\n",
1498                var, op_str, left_var, right_var
1499            ));
1500        }
1501        Expr::Not { operand } => {
1502            let inner_var = encode_expr_src(operand, counter, output, interner, variants);
1503            output.push_str(&format!("Let {} be a new CNot with inner {}.\n", var, inner_var));
1504        }
1505        Expr::Call { function, args } => {
1506            let fn_name = interner.resolve(*function);
1507            if let Some(field_names) = variants.get(fn_name) {
1508                // Variant constructor call — encode as CNewVariant
1509                let names_var = format!("nvNames_{}", *counter);
1510                *counter += 1;
1511                output.push_str(&format!("Let {} be a new Seq of Text.\n", names_var));
1512                let vals_var = format!("nvVals_{}", *counter);
1513                *counter += 1;
1514                output.push_str(&format!("Let {} be a new Seq of CExpr.\n", vals_var));
1515                for (i, arg) in args.iter().enumerate() {
1516                    let fname = field_names.get(i).map(|s| s.as_str()).unwrap_or("value");
1517                    output.push_str(&format!("Push \"{}\" to {}.\n", fname, names_var));
1518                    let arg_var = encode_expr_src(arg, counter, output, interner, variants);
1519                    output.push_str(&format!("Push {} to {}.\n", arg_var, vals_var));
1520                }
1521                output.push_str(&format!(
1522                    "Let {} be a new CNewVariant with tag \"{}\" and fnames {} and fvals {}.\n",
1523                    var, fn_name, names_var, vals_var
1524                ));
1525            } else {
1526                let args_var = format!("callArgs_{}", *counter);
1527                *counter += 1;
1528                output.push_str(&format!("Let {} be a new Seq of CExpr.\n", args_var));
1529                for arg in args {
1530                    let arg_var = encode_expr_src(arg, counter, output, interner, variants);
1531                    output.push_str(&format!("Push {} to {}.\n", arg_var, args_var));
1532                }
1533                output.push_str(&format!(
1534                    "Let {} be a new CCall with name \"{}\" and args {}.\n",
1535                    var, fn_name, args_var
1536                ));
1537            }
1538        }
1539        Expr::Index { collection, index } => {
1540            let coll_var = encode_expr_src(collection, counter, output, interner, variants);
1541            let idx_var = encode_expr_src(index, counter, output, interner, variants);
1542            output.push_str(&format!(
1543                "Let {} be a new CIndex with coll {} and idx {}.\n",
1544                var, coll_var, idx_var
1545            ));
1546        }
1547        Expr::Length { collection } => {
1548            let coll_var = encode_expr_src(collection, counter, output, interner, variants);
1549            output.push_str(&format!("Let {} be a new CLen with target {}.\n", var, coll_var));
1550        }
1551        Expr::FieldAccess { object, field } => {
1552            let obj_var = encode_expr_src(object, counter, output, interner, variants);
1553            let field_name = interner.resolve(*field);
1554            let key_var = format!("e_{}", *counter);
1555            *counter += 1;
1556            output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", key_var, field_name));
1557            output.push_str(&format!(
1558                "Let {} be a new CMapGet with target {} and key {}.\n",
1559                var, obj_var, key_var
1560            ));
1561        }
1562        Expr::NewVariant { variant, fields, .. } => {
1563            let variant_name = interner.resolve(*variant);
1564            let names_var = format!("nvNames_{}", *counter);
1565            *counter += 1;
1566            output.push_str(&format!("Let {} be a new Seq of Text.\n", names_var));
1567            let vals_var = format!("nvVals_{}", *counter);
1568            *counter += 1;
1569            output.push_str(&format!("Let {} be a new Seq of CExpr.\n", vals_var));
1570            for (field_name, field_expr) in fields {
1571                let fname = interner.resolve(*field_name);
1572                output.push_str(&format!("Push \"{}\" to {}.\n", fname, names_var));
1573                let field_var = encode_expr_src(field_expr, counter, output, interner, variants);
1574                output.push_str(&format!("Push {} to {}.\n", field_var, vals_var));
1575            }
1576            output.push_str(&format!(
1577                "Let {} be a new CNewVariant with tag \"{}\" and fnames {} and fvals {}.\n",
1578                var, variant_name, names_var, vals_var
1579            ));
1580        }
1581        Expr::New { type_name, init_fields, .. } => {
1582            let tn = interner.resolve(*type_name);
1583            if tn == "Seq" || tn == "List" {
1584                output.push_str(&format!("Let {} be a new CNewSeq.\n", var));
1585            } else if tn == "Set" {
1586                output.push_str(&format!("Let {} be a new CNewSet.\n", var));
1587            } else if init_fields.is_empty() {
1588                let names_var = format!("nvNames_{}", *counter);
1589                *counter += 1;
1590                output.push_str(&format!("Let {} be a new Seq of Text.\n", names_var));
1591                let vals_var = format!("nvVals_{}", *counter);
1592                *counter += 1;
1593                output.push_str(&format!("Let {} be a new Seq of CExpr.\n", vals_var));
1594                output.push_str(&format!(
1595                    "Let {} be a new CNewVariant with tag \"{}\" and fnames {} and fvals {}.\n",
1596                    var, tn, names_var, vals_var
1597                ));
1598            } else {
1599                let names_var = format!("nvNames_{}", *counter);
1600                *counter += 1;
1601                output.push_str(&format!("Let {} be a new Seq of Text.\n", names_var));
1602                let vals_var = format!("nvVals_{}", *counter);
1603                *counter += 1;
1604                output.push_str(&format!("Let {} be a new Seq of CExpr.\n", vals_var));
1605                for (field_name, field_expr) in init_fields {
1606                    let fname = interner.resolve(*field_name);
1607                    output.push_str(&format!("Push \"{}\" to {}.\n", fname, names_var));
1608                    let field_var = encode_expr_src(field_expr, counter, output, interner, variants);
1609                    output.push_str(&format!("Push {} to {}.\n", field_var, vals_var));
1610                }
1611                output.push_str(&format!(
1612                    "Let {} be a new CNewVariant with tag \"{}\" and fnames {} and fvals {}.\n",
1613                    var, tn, names_var, vals_var
1614                ));
1615            }
1616        }
1617        Expr::InterpolatedString(parts) => {
1618            if parts.is_empty() {
1619                output.push_str(&format!("Let {} be a new CText with value \"\".\n", var));
1620            } else {
1621                let mut part_vars: Vec<String> = Vec::new();
1622                for part in parts {
1623                    match part {
1624                        StringPart::Literal(sym) => {
1625                            let text = interner.resolve(*sym);
1626                            let pv = format!("e_{}", *counter);
1627                            *counter += 1;
1628                            output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", pv, text));
1629                            part_vars.push(pv);
1630                        }
1631                        StringPart::Expr { value, .. } => {
1632                            let pv = encode_expr_src(value, counter, output, interner, variants);
1633                            part_vars.push(pv);
1634                        }
1635                    }
1636                }
1637                if part_vars.len() == 1 {
1638                    output.push_str(&format!("Let {} be {}.\n", var, part_vars[0]));
1639                } else {
1640                    let mut acc = part_vars[0].clone();
1641                    for pv in &part_vars[1..] {
1642                        let concat_var = format!("e_{}", *counter);
1643                        *counter += 1;
1644                        output.push_str(&format!(
1645                            "Let {} be a new CBinOp with op \"+\" and left {} and right {}.\n",
1646                            concat_var, acc, pv
1647                        ));
1648                        acc = concat_var;
1649                    }
1650                    output.push_str(&format!("Let {} be {}.\n", var, acc));
1651                }
1652            }
1653        }
1654        Expr::Range { start, end } => {
1655            let start_var = encode_expr_src(start, counter, output, interner, variants);
1656            let end_var = encode_expr_src(end, counter, output, interner, variants);
1657            output.push_str(&format!(
1658                "Let {} be a new CRange with start {} and end {}.\n",
1659                var, start_var, end_var
1660            ));
1661        }
1662        Expr::Slice { collection, start, end } => {
1663            let coll_var = encode_expr_src(collection, counter, output, interner, variants);
1664            let start_var = encode_expr_src(start, counter, output, interner, variants);
1665            let end_var = encode_expr_src(end, counter, output, interner, variants);
1666            output.push_str(&format!(
1667                "Let {} be a new CSlice with coll {} and startIdx {} and endIdx {}.\n",
1668                var, coll_var, start_var, end_var
1669            ));
1670        }
1671        Expr::Copy { expr } => {
1672            let inner_var = encode_expr_src(expr, counter, output, interner, variants);
1673            output.push_str(&format!("Let {} be a new CCopy with target {}.\n", var, inner_var));
1674        }
1675        Expr::Contains { collection, value } => {
1676            let coll_var = encode_expr_src(collection, counter, output, interner, variants);
1677            let val_var = encode_expr_src(value, counter, output, interner, variants);
1678            output.push_str(&format!(
1679                "Let {} be a new CContains with coll {} and elem {}.\n",
1680                var, coll_var, val_var
1681            ));
1682        }
1683        Expr::Union { left, right } => {
1684            let left_var = encode_expr_src(left, counter, output, interner, variants);
1685            let right_var = encode_expr_src(right, counter, output, interner, variants);
1686            output.push_str(&format!(
1687                "Let {} be a new CUnion with left {} and right {}.\n",
1688                var, left_var, right_var
1689            ));
1690        }
1691        Expr::Intersection { left, right } => {
1692            let left_var = encode_expr_src(left, counter, output, interner, variants);
1693            let right_var = encode_expr_src(right, counter, output, interner, variants);
1694            output.push_str(&format!(
1695                "Let {} be a new CIntersection with left {} and right {}.\n",
1696                var, left_var, right_var
1697            ));
1698        }
1699        Expr::OptionSome { value } => {
1700            let inner_var = encode_expr_src(value, counter, output, interner, variants);
1701            output.push_str(&format!(
1702                "Let {} be a new COptionSome with inner {}.\n",
1703                var, inner_var
1704            ));
1705        }
1706        Expr::OptionNone => {
1707            output.push_str(&format!("Let {} be a new COptionNone.\n", var));
1708        }
1709        Expr::Tuple(elems) => {
1710            let items_var = format!("tupItems_{}", *counter);
1711            *counter += 1;
1712            output.push_str(&format!("Let {} be a new Seq of CExpr.\n", items_var));
1713            for elem in elems {
1714                let elem_var = encode_expr_src(elem, counter, output, interner, variants);
1715                output.push_str(&format!("Push {} to {}.\n", elem_var, items_var));
1716            }
1717            output.push_str(&format!(
1718                "Let {} be a new CTuple with items {}.\n",
1719                var, items_var
1720            ));
1721        }
1722        Expr::Closure { params, body, .. } => {
1723            let params_var = format!("clp_{}", *counter);
1724            *counter += 1;
1725            output.push_str(&format!("Let {} be a new Seq of Text.\n", params_var));
1726            let mut param_names = HashSet::new();
1727            for (sym, _) in params {
1728                let name = interner.resolve(*sym);
1729                param_names.insert(name.to_string());
1730                output.push_str(&format!("Push \"{}\" to {}.\n", name, params_var));
1731            }
1732            let body_var = format!("clb_{}", *counter);
1733            *counter += 1;
1734            output.push_str(&format!("Let {} be a new Seq of CStmt.\n", body_var));
1735            match body {
1736                ClosureBody::Expression(e) => {
1737                    let ret_expr = encode_expr_src(e, counter, output, interner, variants);
1738                    let ret_var = format!("s_{}", *counter);
1739                    *counter += 1;
1740                    output.push_str(&format!("Let {} be a new CReturn with expr {}.\n", ret_var, ret_expr));
1741                    output.push_str(&format!("Push {} to {}.\n", ret_var, body_var));
1742                }
1743                ClosureBody::Block(stmts) => {
1744                    for s in stmts.iter() {
1745                        let sv = encode_stmt_src(s, counter, output, interner, variants);
1746                        output.push_str(&format!("Push {} to {}.\n", sv, body_var));
1747                    }
1748                }
1749            }
1750            let bound: HashSet<String> = param_names;
1751            let free = collect_free_vars_expr(expr, interner, &bound);
1752            let cap_var = format!("clc_{}", *counter);
1753            *counter += 1;
1754            output.push_str(&format!("Let {} be a new Seq of Text.\n", cap_var));
1755            for fv in &free {
1756                output.push_str(&format!("Push \"{}\" to {}.\n", fv, cap_var));
1757            }
1758            output.push_str(&format!(
1759                "Let {} be a new CClosure with params {} and body {} and captured {}.\n",
1760                var, params_var, body_var, cap_var
1761            ));
1762        }
1763        Expr::CallExpr { callee, args } => {
1764            let callee_var = encode_expr_src(callee, counter, output, interner, variants);
1765            let args_var = format!("cea_{}", *counter);
1766            *counter += 1;
1767            output.push_str(&format!("Let {} be a new Seq of CExpr.\n", args_var));
1768            for a in args {
1769                let av = encode_expr_src(a, counter, output, interner, variants);
1770                output.push_str(&format!("Push {} to {}.\n", av, args_var));
1771            }
1772            output.push_str(&format!(
1773                "Let {} be a new CCallExpr with target {} and args {}.\n",
1774                var, callee_var, args_var
1775            ));
1776        }
1777        Expr::Give { value } => {
1778            let inner_var = encode_expr_src(value, counter, output, interner, variants);
1779            output.push_str(&format!("Let {} be {}.\n", var, inner_var));
1780        }
1781        Expr::Escape { code, .. } => {
1782            let code_str = interner.resolve(*code);
1783            output.push_str(&format!(
1784                "Let {} be a new CEscExpr with code \"{}\".\n",
1785                var, code_str.replace('\"', "\\\"")
1786            ));
1787        }
1788        _ => {
1789            output.push_str(&format!("Let {} be a new CText with value \"unsupported\".\n", var));
1790        }
1791    }
1792
1793    var
1794}
1795
1796fn encode_stmt_src(stmt: &Stmt, counter: &mut usize, output: &mut String, interner: &Interner, variants: &HashMap<String, Vec<String>>) -> String {
1797    let var = format!("s_{}", *counter);
1798    *counter += 1;
1799
1800    match stmt {
1801        Stmt::Let { var: name, value, .. } => {
1802            let name_str = interner.resolve(*name);
1803            let expr_var = encode_expr_src(value, counter, output, interner, variants);
1804            output.push_str(&format!(
1805                "Let {} be a new CLet with name \"{}\" and expr {}.\n",
1806                var, name_str, expr_var
1807            ));
1808        }
1809        Stmt::Set { target, value } => {
1810            let name_str = interner.resolve(*target);
1811            let expr_var = encode_expr_src(value, counter, output, interner, variants);
1812            output.push_str(&format!(
1813                "Let {} be a new CSet with name \"{}\" and expr {}.\n",
1814                var, name_str, expr_var
1815            ));
1816        }
1817        Stmt::If { cond, then_block, else_block } => {
1818            let cond_var = encode_expr_src(cond, counter, output, interner, variants);
1819            let then_stmts: Vec<&Stmt> = then_block.iter().collect();
1820            let then_var = encode_stmt_list_src(&then_stmts, counter, output, interner, variants);
1821            let else_var = if let Some(els) = else_block {
1822                let else_stmts: Vec<&Stmt> = els.iter().collect();
1823                encode_stmt_list_src(&else_stmts, counter, output, interner, variants)
1824            } else {
1825                let empty_var = format!("emptyBlock_{}", *counter);
1826                *counter += 1;
1827                output.push_str(&format!("Let {} be a new Seq of CStmt.\n", empty_var));
1828                empty_var
1829            };
1830            output.push_str(&format!(
1831                "Let {} be a new CIf with cond {} and thenBlock {} and elseBlock {}.\n",
1832                var, cond_var, then_var, else_var
1833            ));
1834        }
1835        Stmt::While { cond, body, .. } => {
1836            let cond_var = encode_expr_src(cond, counter, output, interner, variants);
1837            let body_stmts: Vec<&Stmt> = body.iter().collect();
1838            let body_var = encode_stmt_list_src(&body_stmts, counter, output, interner, variants);
1839            output.push_str(&format!(
1840                "Let {} be a new CWhile with cond {} and body {}.\n",
1841                var, cond_var, body_var
1842            ));
1843        }
1844        Stmt::Return { value } => {
1845            if let Some(expr) = value {
1846                let expr_var = encode_expr_src(expr, counter, output, interner, variants);
1847                output.push_str(&format!("Let {} be a new CReturn with expr {}.\n", var, expr_var));
1848            } else {
1849                let nothing_var = format!("e_{}", *counter);
1850                *counter += 1;
1851                output.push_str(&format!("Let {} be a new CInt with value 0.\n", nothing_var));
1852                output.push_str(&format!("Let {} be a new CReturn with expr {}.\n", var, nothing_var));
1853            }
1854        }
1855        Stmt::Show { object, .. } => {
1856            let expr_var = encode_expr_src(object, counter, output, interner, variants);
1857            output.push_str(&format!("Let {} be a new CShow with expr {}.\n", var, expr_var));
1858        }
1859        Stmt::Call { function, args } => {
1860            let fn_name = interner.resolve(*function);
1861            let args_var = format!("callSArgs_{}", *counter);
1862            *counter += 1;
1863            output.push_str(&format!("Let {} be a new Seq of CExpr.\n", args_var));
1864            for arg in args {
1865                let arg_var = encode_expr_src(arg, counter, output, interner, variants);
1866                output.push_str(&format!("Push {} to {}.\n", arg_var, args_var));
1867            }
1868            output.push_str(&format!(
1869                "Let {} be a new CCallS with name \"{}\" and args {}.\n",
1870                var, fn_name, args_var
1871            ));
1872        }
1873        Stmt::Push { value, collection } => {
1874            let val_var = encode_expr_src(value, counter, output, interner, variants);
1875            let coll_name = extract_ident_name(collection, interner);
1876            output.push_str(&format!(
1877                "Let {} be a new CPush with expr {} and target \"{}\".\n",
1878                var, val_var, coll_name
1879            ));
1880        }
1881        Stmt::SetIndex { collection, index, value } => {
1882            let coll_name = extract_ident_name(collection, interner);
1883            let idx_var = encode_expr_src(index, counter, output, interner, variants);
1884            let val_var = encode_expr_src(value, counter, output, interner, variants);
1885            output.push_str(&format!(
1886                "Let {} be a new CSetIdx with target \"{}\" and idx {} and val {}.\n",
1887                var, coll_name, idx_var, val_var
1888            ));
1889        }
1890        Stmt::SetField { object, field, value } => {
1891            let map_name = extract_ident_name(object, interner);
1892            let field_name = interner.resolve(*field);
1893            let key_var = format!("e_{}", *counter);
1894            *counter += 1;
1895            output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", key_var, field_name));
1896            let val_var = encode_expr_src(value, counter, output, interner, variants);
1897            output.push_str(&format!(
1898                "Let {} be a new CMapSet with target \"{}\" and key {} and val {}.\n",
1899                var, map_name, key_var, val_var
1900            ));
1901        }
1902        Stmt::Pop { collection, .. } => {
1903            let coll_name = extract_ident_name(collection, interner);
1904            output.push_str(&format!(
1905                "Let {} be a new CPop with target \"{}\".\n",
1906                var, coll_name
1907            ));
1908        }
1909        Stmt::Add { value, collection } => {
1910            let val_var = encode_expr_src(value, counter, output, interner, variants);
1911            let coll_name = extract_ident_name(collection, interner);
1912            output.push_str(&format!(
1913                "Let {} be a new CAdd with elem {} and target \"{}\".\n",
1914                var, val_var, coll_name
1915            ));
1916        }
1917        Stmt::Remove { value, collection } => {
1918            let val_var = encode_expr_src(value, counter, output, interner, variants);
1919            let coll_name = extract_ident_name(collection, interner);
1920            output.push_str(&format!(
1921                "Let {} be a new CRemove with elem {} and target \"{}\".\n",
1922                var, val_var, coll_name
1923            ));
1924        }
1925        Stmt::Inspect { .. } => {
1926            return String::new(); // Handled by encode_stmts_src
1927        }
1928        Stmt::Repeat { .. } => {
1929            return String::new(); // Handled by encode_stmts_src
1930        }
1931        Stmt::Break => {
1932            output.push_str(&format!("Let {} be a new CBreak.\n", var));
1933        }
1934        Stmt::RuntimeAssert { condition, .. } => {
1935            let cond_var = encode_expr_src(condition, counter, output, interner, variants);
1936            let msg_var = format!("e_{}", *counter);
1937            *counter += 1;
1938            output.push_str(&format!("Let {} be a new CText with value \"assertion failed\".\n", msg_var));
1939            output.push_str(&format!(
1940                "Let {} be a new CRuntimeAssert with cond {} and msg {}.\n",
1941                var, cond_var, msg_var
1942            ));
1943        }
1944        Stmt::Give { object, recipient } => {
1945            let expr_var = encode_expr_src(object, counter, output, interner, variants);
1946            let target_name = extract_ident_name(recipient, interner);
1947            output.push_str(&format!(
1948                "Let {} be a new CGive with expr {} and target \"{}\".\n",
1949                var, expr_var, target_name
1950            ));
1951        }
1952        Stmt::Escape { code, .. } => {
1953            let code_str = interner.resolve(*code);
1954            output.push_str(&format!(
1955                "Let {} be a new CEscStmt with code \"{}\".\n",
1956                var, code_str.replace('\"', "\\\"")
1957            ));
1958        }
1959        Stmt::Sleep { milliseconds } => {
1960            let dur_var = encode_expr_src(milliseconds, counter, output, interner, variants);
1961            output.push_str(&format!(
1962                "Let {} be a new CSleep with duration {}.\n",
1963                var, dur_var
1964            ));
1965        }
1966        Stmt::ReadFrom { var: read_var, source } => {
1967            let var_name = interner.resolve(*read_var);
1968            match source {
1969                ReadSource::Console => {
1970                    output.push_str(&format!(
1971                        "Let {} be a new CReadConsole with target \"{}\".\n",
1972                        var, var_name
1973                    ));
1974                }
1975                ReadSource::File(path_expr) => {
1976                    let path_var = encode_expr_src(path_expr, counter, output, interner, variants);
1977                    output.push_str(&format!(
1978                        "Let {} be a new CReadFile with path {} and target \"{}\".\n",
1979                        var, path_var, var_name
1980                    ));
1981                }
1982            }
1983        }
1984        Stmt::WriteFile { content, path } => {
1985            let path_var = encode_expr_src(path, counter, output, interner, variants);
1986            let content_var = encode_expr_src(content, counter, output, interner, variants);
1987            output.push_str(&format!(
1988                "Let {} be a new CWriteFile with path {} and content {}.\n",
1989                var, path_var, content_var
1990            ));
1991        }
1992        Stmt::Check { source_text, .. } => {
1993            let pred_var = format!("e_{}", *counter);
1994            *counter += 1;
1995            output.push_str(&format!("Let {} be a new CBool with value true.\n", pred_var));
1996            let msg_var = format!("e_{}", *counter);
1997            *counter += 1;
1998            output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", msg_var, source_text.replace('\"', "\\\"")));
1999            output.push_str(&format!(
2000                "Let {} be a new CCheck with predicate {} and msg {}.\n",
2001                var, pred_var, msg_var
2002            ));
2003        }
2004        Stmt::Assert { .. } => {
2005            let prop_var = format!("e_{}", *counter);
2006            *counter += 1;
2007            output.push_str(&format!("Let {} be a new CBool with value true.\n", prop_var));
2008            output.push_str(&format!(
2009                "Let {} be a new CAssert with proposition {}.\n",
2010                var, prop_var
2011            ));
2012        }
2013        Stmt::Trust { justification, .. } => {
2014            let prop_var = format!("e_{}", *counter);
2015            *counter += 1;
2016            output.push_str(&format!("Let {} be a new CBool with value true.\n", prop_var));
2017            let just_str = interner.resolve(*justification);
2018            output.push_str(&format!(
2019                "Let {} be a new CTrust with proposition {} and justification \"{}\".\n",
2020                var, prop_var, just_str
2021            ));
2022        }
2023        Stmt::Require { crate_name, .. } => {
2024            let dep_name = interner.resolve(*crate_name);
2025            output.push_str(&format!(
2026                "Let {} be a new CRequire with dependency \"{}\".\n",
2027                var, dep_name
2028            ));
2029        }
2030        Stmt::MergeCrdt { source, target } => {
2031            let source_var = encode_expr_src(source, counter, output, interner, variants);
2032            let target_name = match target {
2033                Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2034                _ => "unknown".to_string(),
2035            };
2036            output.push_str(&format!(
2037                "Let {} be a new CMerge with target \"{}\" and other {}.\n",
2038                var, target_name, source_var
2039            ));
2040        }
2041        Stmt::IncreaseCrdt { object, field, amount } => {
2042            let amount_var = encode_expr_src(amount, counter, output, interner, variants);
2043            let target_name = match object {
2044                Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2045                _ => "unknown".to_string(),
2046            };
2047            output.push_str(&format!(
2048                "Let {} be a new CIncrease with target \"{}\" and amount {}.\n",
2049                var, target_name, amount_var
2050            ));
2051        }
2052        Stmt::DecreaseCrdt { object, field, amount } => {
2053            let amount_var = encode_expr_src(amount, counter, output, interner, variants);
2054            let target_name = match object {
2055                Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2056                _ => "unknown".to_string(),
2057            };
2058            output.push_str(&format!(
2059                "Let {} be a new CDecrease with target \"{}\" and amount {}.\n",
2060                var, target_name, amount_var
2061            ));
2062        }
2063        Stmt::AppendToSequence { sequence, value } => {
2064            let value_var = encode_expr_src(value, counter, output, interner, variants);
2065            let target_name = match sequence {
2066                Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2067                _ => "unknown".to_string(),
2068            };
2069            output.push_str(&format!(
2070                "Let {} be a new CAppendToSeq with target \"{}\" and value {}.\n",
2071                var, target_name, value_var
2072            ));
2073        }
2074        Stmt::ResolveConflict { object, .. } => {
2075            let target_name = match object {
2076                Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2077                _ => "unknown".to_string(),
2078            };
2079            output.push_str(&format!(
2080                "Let {} be a new CResolve with target \"{}\".\n",
2081                var, target_name
2082            ));
2083        }
2084        Stmt::Sync { var: sync_var, topic } => {
2085            let topic_var = encode_expr_src(topic, counter, output, interner, variants);
2086            let var_name = interner.resolve(*sync_var);
2087            output.push_str(&format!(
2088                "Let {} be a new CSync with target \"{}\" and channel {}.\n",
2089                var, var_name, topic_var
2090            ));
2091        }
2092        Stmt::Mount { var: mount_var, path } => {
2093            let path_var = encode_expr_src(path, counter, output, interner, variants);
2094            let var_name = interner.resolve(*mount_var);
2095            output.push_str(&format!(
2096                "Let {} be a new CMount with target \"{}\" and path {}.\n",
2097                var, var_name, path_var
2098            ));
2099        }
2100        Stmt::Concurrent { tasks } => {
2101            let branches_var = format!("e_{}", *counter);
2102            *counter += 1;
2103            output.push_str(&format!("Let {} be a new Seq of Seq of CStmt.\n", branches_var));
2104            let branch_var = format!("e_{}", *counter);
2105            *counter += 1;
2106            output.push_str(&format!("Let {} be a new Seq of CStmt.\n", branch_var));
2107            for stmt in tasks.iter() {
2108                let sv = encode_stmt_src(stmt, counter, output, interner, variants);
2109                if !sv.is_empty() {
2110                    output.push_str(&format!("Push {} to {}.\n", sv, branch_var));
2111                }
2112            }
2113            output.push_str(&format!("Push {} to {}.\n", branch_var, branches_var));
2114            output.push_str(&format!(
2115                "Let {} be a new CConcurrent with branches {}.\n",
2116                var, branches_var
2117            ));
2118        }
2119        Stmt::Parallel { tasks } => {
2120            let branches_var = format!("e_{}", *counter);
2121            *counter += 1;
2122            output.push_str(&format!("Let {} be a new Seq of Seq of CStmt.\n", branches_var));
2123            let branch_var = format!("e_{}", *counter);
2124            *counter += 1;
2125            output.push_str(&format!("Let {} be a new Seq of CStmt.\n", branch_var));
2126            for stmt in tasks.iter() {
2127                let sv = encode_stmt_src(stmt, counter, output, interner, variants);
2128                if !sv.is_empty() {
2129                    output.push_str(&format!("Push {} to {}.\n", sv, branch_var));
2130                }
2131            }
2132            output.push_str(&format!("Push {} to {}.\n", branch_var, branches_var));
2133            output.push_str(&format!(
2134                "Let {} be a new CParallel with branches {}.\n",
2135                var, branches_var
2136            ));
2137        }
2138        Stmt::LaunchTask { function, args } | Stmt::LaunchTaskWithHandle { function, args, .. } => {
2139            let func_name = interner.resolve(*function);
2140            let args_var = format!("e_{}", *counter);
2141            *counter += 1;
2142            output.push_str(&format!("Let {} be a new Seq of CExpr.\n", args_var));
2143            for arg in args {
2144                let av = encode_expr_src(arg, counter, output, interner, variants);
2145                output.push_str(&format!("Push {} to {}.\n", av, args_var));
2146            }
2147            let body_var = format!("e_{}", *counter);
2148            *counter += 1;
2149            output.push_str(&format!("Let {} be a new Seq of CStmt.\n", body_var));
2150            let call_var = format!("e_{}", *counter);
2151            *counter += 1;
2152            output.push_str(&format!(
2153                "Let {} be a new CCallS with name \"{}\" and args {}.\n",
2154                call_var, func_name, args_var
2155            ));
2156            output.push_str(&format!("Push {} to {}.\n", call_var, body_var));
2157            let handle_name = if let Stmt::LaunchTaskWithHandle { handle, .. } = stmt {
2158                interner.resolve(*handle).to_string()
2159            } else {
2160                "_task".to_string()
2161            };
2162            output.push_str(&format!(
2163                "Let {} be a new CLaunchTask with body {} and handle \"{}\".\n",
2164                var, body_var, handle_name
2165            ));
2166        }
2167        Stmt::StopTask { handle } => {
2168            let handle_var = encode_expr_src(handle, counter, output, interner, variants);
2169            output.push_str(&format!(
2170                "Let {} be a new CStopTask with handle {}.\n",
2171                var, handle_var
2172            ));
2173        }
2174        Stmt::CreatePipe { var: pipe_var, capacity, .. } => {
2175            let cap = capacity.unwrap_or(32);
2176            let cap_var = format!("e_{}", *counter);
2177            *counter += 1;
2178            output.push_str(&format!("Let {} be a new CInt with value {}.\n", cap_var, cap));
2179            let pipe_name = interner.resolve(*pipe_var);
2180            output.push_str(&format!(
2181                "Let {} be a new CCreatePipe with name \"{}\" and capacity {}.\n",
2182                var, pipe_name, cap_var
2183            ));
2184        }
2185        Stmt::SendPipe { value, pipe } => {
2186            let val_var = encode_expr_src(value, counter, output, interner, variants);
2187            let pipe_name = match pipe {
2188                Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2189                _ => "pipe".to_string(),
2190            };
2191            output.push_str(&format!(
2192                "Let {} be a new CSendPipe with chan \"{}\" and value {}.\n",
2193                var, pipe_name, val_var
2194            ));
2195        }
2196        Stmt::ReceivePipe { var: recv_var, pipe } => {
2197            let recv_name = interner.resolve(*recv_var);
2198            let pipe_name = match pipe {
2199                Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2200                _ => "pipe".to_string(),
2201            };
2202            output.push_str(&format!(
2203                "Let {} be a new CReceivePipe with chan \"{}\" and target \"{}\".\n",
2204                var, pipe_name, recv_name
2205            ));
2206        }
2207        Stmt::TrySendPipe { value, pipe, .. } => {
2208            let val_var = encode_expr_src(value, counter, output, interner, variants);
2209            let pipe_name = match pipe {
2210                Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2211                _ => "pipe".to_string(),
2212            };
2213            output.push_str(&format!(
2214                "Let {} be a new CTrySendPipe with chan \"{}\" and value {}.\n",
2215                var, pipe_name, val_var
2216            ));
2217        }
2218        Stmt::TryReceivePipe { var: recv_var, pipe } => {
2219            let recv_name = interner.resolve(*recv_var);
2220            let pipe_name = match pipe {
2221                Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2222                _ => "pipe".to_string(),
2223            };
2224            output.push_str(&format!(
2225                "Let {} be a new CTryReceivePipe with chan \"{}\" and target \"{}\".\n",
2226                var, pipe_name, recv_name
2227            ));
2228        }
2229        Stmt::Select { branches } => {
2230            let branches_var = format!("e_{}", *counter);
2231            *counter += 1;
2232            output.push_str(&format!("Let {} be a new Seq of CSelectBranch.\n", branches_var));
2233            for branch in branches {
2234                match branch {
2235                    SelectBranch::Receive { var: recv_var, pipe, body } => {
2236                        let recv_name = interner.resolve(*recv_var);
2237                        let pipe_name = match pipe {
2238                            Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2239                            _ => "pipe".to_string(),
2240                        };
2241                        let body_var = format!("e_{}", *counter);
2242                        *counter += 1;
2243                        output.push_str(&format!("Let {} be a new Seq of CStmt.\n", body_var));
2244                        for stmt in body.iter() {
2245                            let sv = encode_stmt_src(stmt, counter, output, interner, variants);
2246                            if !sv.is_empty() {
2247                                output.push_str(&format!("Push {} to {}.\n", sv, body_var));
2248                            }
2249                        }
2250                        let branch_var = format!("e_{}", *counter);
2251                        *counter += 1;
2252                        output.push_str(&format!(
2253                            "Let {} be a new CSelectRecv with chan \"{}\" and var \"{}\" and body {}.\n",
2254                            branch_var, pipe_name, recv_name, body_var
2255                        ));
2256                        output.push_str(&format!("Push {} to {}.\n", branch_var, branches_var));
2257                    }
2258                    SelectBranch::Timeout { milliseconds, body } => {
2259                        let dur_var = encode_expr_src(milliseconds, counter, output, interner, variants);
2260                        let body_var = format!("e_{}", *counter);
2261                        *counter += 1;
2262                        output.push_str(&format!("Let {} be a new Seq of CStmt.\n", body_var));
2263                        for stmt in body.iter() {
2264                            let sv = encode_stmt_src(stmt, counter, output, interner, variants);
2265                            if !sv.is_empty() {
2266                                output.push_str(&format!("Push {} to {}.\n", sv, body_var));
2267                            }
2268                        }
2269                        let branch_var = format!("e_{}", *counter);
2270                        *counter += 1;
2271                        output.push_str(&format!(
2272                            "Let {} be a new CSelectTimeout with duration {} and body {}.\n",
2273                            branch_var, dur_var, body_var
2274                        ));
2275                        output.push_str(&format!("Push {} to {}.\n", branch_var, branches_var));
2276                    }
2277                }
2278            }
2279            output.push_str(&format!(
2280                "Let {} be a new CSelect with branches {}.\n",
2281                var, branches_var
2282            ));
2283        }
2284        Stmt::Spawn { agent_type, name } => {
2285            let agent_name = interner.resolve(*agent_type);
2286            let target_name = interner.resolve(*name);
2287            output.push_str(&format!(
2288                "Let {} be a new CSpawn with agentType \"{}\" and target \"{}\".\n",
2289                var, agent_name, target_name
2290            ));
2291        }
2292        Stmt::SendMessage { message, destination } => {
2293            let target_var = encode_expr_src(destination, counter, output, interner, variants);
2294            let msg_var = encode_expr_src(message, counter, output, interner, variants);
2295            output.push_str(&format!(
2296                "Let {} be a new CSendMessage with target {} and msg {}.\n",
2297                var, target_var, msg_var
2298            ));
2299        }
2300        Stmt::AwaitMessage { into, .. } => {
2301            let await_name = interner.resolve(*into);
2302            output.push_str(&format!(
2303                "Let {} be a new CAwaitMessage with target \"{}\".\n",
2304                var, await_name
2305            ));
2306        }
2307        Stmt::Listen { address } => {
2308            let addr_var = encode_expr_src(address, counter, output, interner, variants);
2309            output.push_str(&format!(
2310                "Let {} be a new CListen with addr {} and handler \"default\".\n",
2311                var, addr_var
2312            ));
2313        }
2314        Stmt::ConnectTo { address } => {
2315            let addr_var = encode_expr_src(address, counter, output, interner, variants);
2316            output.push_str(&format!(
2317                "Let {} be a new CConnectTo with addr {} and target \"conn\".\n",
2318                var, addr_var
2319            ));
2320        }
2321        Stmt::Zone { name, body, .. } => {
2322            let zone_name = interner.resolve(*name);
2323            let body_var = format!("e_{}", *counter);
2324            *counter += 1;
2325            output.push_str(&format!("Let {} be a new Seq of CStmt.\n", body_var));
2326            for stmt in body.iter() {
2327                let sv = encode_stmt_src(stmt, counter, output, interner, variants);
2328                if !sv.is_empty() {
2329                    output.push_str(&format!("Push {} to {}.\n", sv, body_var));
2330                }
2331            }
2332            output.push_str(&format!(
2333                "Let {} be a new CZone with name \"{}\" and kind \"heap\" and body {}.\n",
2334                var, zone_name, body_var
2335            ));
2336        }
2337        Stmt::LetPeerAgent { var: pa_var, address } => {
2338            let addr_var = encode_expr_src(address, counter, output, interner, variants);
2339            let pa_name = interner.resolve(*pa_var);
2340            output.push_str(&format!(
2341                "Let {} be a new CConnectTo with addr {} and target \"{}\".\n",
2342                var, addr_var, pa_name
2343            ));
2344        }
2345        _ => {
2346            return String::new();
2347        }
2348    }
2349
2350    var
2351}
2352
2353fn encode_stmts_src(stmt: &Stmt, counter: &mut usize, output: &mut String, interner: &Interner, variants: &HashMap<String, Vec<String>>) -> Vec<String> {
2354    match stmt {
2355        Stmt::Inspect { target, arms, .. } => {
2356            let mut otherwise_stmts: Vec<&Stmt> = Vec::new();
2357            let mut variant_arms: Vec<(&MatchArm, Vec<&Stmt>)> = Vec::new();
2358
2359            for arm in arms {
2360                if arm.variant.is_none() {
2361                    otherwise_stmts = arm.body.iter().collect();
2362                } else {
2363                    let body_refs: Vec<&Stmt> = arm.body.iter().collect();
2364                    variant_arms.push((arm, body_refs));
2365                }
2366            }
2367
2368            if variant_arms.is_empty() {
2369                let mut result = Vec::new();
2370                for s in &otherwise_stmts {
2371                    for v in encode_stmts_src(s, counter, output, interner, variants) {
2372                        result.push(v);
2373                    }
2374                }
2375                return result;
2376            }
2377
2378            // Flat CIf encoding: each arm becomes an independent CIf with empty else.
2379            // Since Inspect arms are mutually exclusive (exactly one tag matches),
2380            // flat CIf is semantically equivalent to nested CIf chains but avoids
2381            // deep nesting that the interpreter's inline CIf handler can't navigate.
2382            let has_otherwise = !otherwise_stmts.is_empty();
2383            let mut result = Vec::new();
2384
2385            // If there's an Otherwise block, track whether any arm matched
2386            let matched_var_name = if has_otherwise {
2387                let name = format!("__inspectMatched_{}", *counter);
2388                *counter += 1;
2389                let false_expr = format!("e_{}", *counter);
2390                *counter += 1;
2391                output.push_str(&format!("Let {} be a new CBool with value false.\n", false_expr));
2392                let let_stmt = format!("s_{}", *counter);
2393                *counter += 1;
2394                output.push_str(&format!(
2395                    "Let {} be a new CLet with name \"{}\" and expr {}.\n",
2396                    let_stmt, name, false_expr
2397                ));
2398                result.push(let_stmt);
2399                Some(name)
2400            } else {
2401                None
2402            };
2403
2404            // Each variant arm becomes: CIf(tag == "Variant", [bindings + body], [])
2405            for (arm, body_stmts) in &variant_arms {
2406                let variant_name = interner.resolve(arm.variant.unwrap());
2407
2408                // Condition: tag == "VariantName"
2409                let tag_target = encode_expr_src(target, counter, output, interner, variants);
2410                let tag_key = format!("e_{}", *counter);
2411                *counter += 1;
2412                output.push_str(&format!("Let {} be a new CText with value \"__tag\".\n", tag_key));
2413                let tag_get = format!("e_{}", *counter);
2414                *counter += 1;
2415                output.push_str(&format!(
2416                    "Let {} be a new CMapGet with target {} and key {}.\n",
2417                    tag_get, tag_target, tag_key
2418                ));
2419                let variant_text = format!("e_{}", *counter);
2420                *counter += 1;
2421                output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", variant_text, variant_name));
2422                let cond_var = format!("e_{}", *counter);
2423                *counter += 1;
2424                output.push_str(&format!(
2425                    "Let {} be a new CBinOp with op \"==\" and left {} and right {}.\n",
2426                    cond_var, tag_get, variant_text
2427                ));
2428
2429                // Then-block: [optionally set matched flag, bindings, body]
2430                let then_list = format!("stmtList_{}", *counter);
2431                *counter += 1;
2432                output.push_str(&format!("Let {} be a new Seq of CStmt.\n", then_list));
2433
2434                // Set matched flag if needed
2435                if let Some(ref mname) = matched_var_name {
2436                    let true_expr = format!("e_{}", *counter);
2437                    *counter += 1;
2438                    output.push_str(&format!("Let {} be a new CBool with value true.\n", true_expr));
2439                    let set_stmt = format!("s_{}", *counter);
2440                    *counter += 1;
2441                    output.push_str(&format!(
2442                        "Let {} be a new CSet with name \"{}\" and expr {}.\n",
2443                        set_stmt, mname, true_expr
2444                    ));
2445                    output.push_str(&format!("Push {} to {}.\n", set_stmt, then_list));
2446                }
2447
2448                // Bindings
2449                for (field_name, binding_name) in &arm.bindings {
2450                    let field_str = interner.resolve(*field_name);
2451                    let bind_str = interner.resolve(*binding_name);
2452                    let bind_target = encode_expr_src(target, counter, output, interner, variants);
2453                    let fkey = format!("e_{}", *counter);
2454                    *counter += 1;
2455                    output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", fkey, field_str));
2456                    let fget = format!("e_{}", *counter);
2457                    *counter += 1;
2458                    output.push_str(&format!(
2459                        "Let {} be a new CMapGet with target {} and key {}.\n",
2460                        fget, bind_target, fkey
2461                    ));
2462                    let bind_let = format!("s_{}", *counter);
2463                    *counter += 1;
2464                    output.push_str(&format!(
2465                        "Let {} be a new CLet with name \"{}\" and expr {}.\n",
2466                        bind_let, bind_str, fget
2467                    ));
2468                    output.push_str(&format!("Push {} to {}.\n", bind_let, then_list));
2469                }
2470
2471                // Body statements (use encode_stmts_src for Inspect/Repeat)
2472                for body_stmt in body_stmts {
2473                    match body_stmt {
2474                        Stmt::Inspect { .. } | Stmt::Repeat { .. } => {
2475                            let vars = encode_stmts_src(body_stmt, counter, output, interner, variants);
2476                            for v in vars {
2477                                output.push_str(&format!("Push {} to {}.\n", v, then_list));
2478                            }
2479                        }
2480                        _ => {
2481                            let bvar = encode_stmt_src(body_stmt, counter, output, interner, variants);
2482                            if !bvar.is_empty() {
2483                                output.push_str(&format!("Push {} to {}.\n", bvar, then_list));
2484                            }
2485                        }
2486                    }
2487                }
2488
2489                // Empty else block
2490                let empty_else = format!("stmtList_{}", *counter);
2491                *counter += 1;
2492                output.push_str(&format!("Let {} be a new Seq of CStmt.\n", empty_else));
2493
2494                // CIf node
2495                let if_var = format!("s_{}", *counter);
2496                *counter += 1;
2497                output.push_str(&format!(
2498                    "Let {} be a new CIf with cond {} and thenBlock {} and elseBlock {}.\n",
2499                    if_var, cond_var, then_list, empty_else
2500                ));
2501
2502                result.push(if_var);
2503            }
2504
2505            // Otherwise: CIf(CNot(__inspectMatched), otherwise_body, [])
2506            if let Some(ref mname) = matched_var_name {
2507                let matched_ref = format!("e_{}", *counter);
2508                *counter += 1;
2509                output.push_str(&format!("Let {} be a new CVar with name \"{}\".\n", matched_ref, mname));
2510                let not_matched = format!("e_{}", *counter);
2511                *counter += 1;
2512                output.push_str(&format!("Let {} be a new CNot with inner {}.\n", not_matched, matched_ref));
2513
2514                let otherwise_block = encode_stmt_list_src(&otherwise_stmts, counter, output, interner, variants);
2515                let empty_else = format!("stmtList_{}", *counter);
2516                *counter += 1;
2517                output.push_str(&format!("Let {} be a new Seq of CStmt.\n", empty_else));
2518
2519                let otherwise_if = format!("s_{}", *counter);
2520                *counter += 1;
2521                output.push_str(&format!(
2522                    "Let {} be a new CIf with cond {} and thenBlock {} and elseBlock {}.\n",
2523                    otherwise_if, not_matched, otherwise_block, empty_else
2524                ));
2525                result.push(otherwise_if);
2526            }
2527
2528            result
2529        }
2530        Stmt::Repeat { pattern, iterable, body, .. } => {
2531            // Lower to: Let idx = CInt(1). CWhile(idx <= CLen(coll), [Let var = CIndex(coll, idx), ...body..., Set idx = idx + 1])
2532            let loop_var_name = match pattern {
2533                Pattern::Identifier(sym) => interner.resolve(*sym).to_string(),
2534                Pattern::Tuple(syms) => {
2535                    if let Some(s) = syms.first() {
2536                        interner.resolve(*s).to_string()
2537                    } else {
2538                        "item".to_string()
2539                    }
2540                }
2541            };
2542
2543            // Generate a unique index variable name
2544            let idx_name = format!("__idx_{}", *counter);
2545            *counter += 1;
2546
2547            // Let idx = CInt(1) — 1-based indexing
2548            let idx_init_expr = format!("e_{}", *counter);
2549            *counter += 1;
2550            output.push_str(&format!("Let {} be a new CInt with value 1.\n", idx_init_expr));
2551            let idx_init = format!("s_{}", *counter);
2552            *counter += 1;
2553            output.push_str(&format!(
2554                "Let {} be a new CLet with name \"{}\" and expr {}.\n",
2555                idx_init, idx_name, idx_init_expr
2556            ));
2557
2558            // While condition: idx <= length of coll (fresh iterable encoding)
2559            let iter_for_len = encode_expr_src(iterable, counter, output, interner, variants);
2560            let idx_var_expr = format!("e_{}", *counter);
2561            *counter += 1;
2562            output.push_str(&format!("Let {} be a new CVar with name \"{}\".\n", idx_var_expr, idx_name));
2563            let len_expr = format!("e_{}", *counter);
2564            *counter += 1;
2565            output.push_str(&format!("Let {} be a new CLen with target {}.\n", len_expr, iter_for_len));
2566            let while_cond = format!("e_{}", *counter);
2567            *counter += 1;
2568            output.push_str(&format!(
2569                "Let {} be a new CBinOp with op \"<=\" and left {} and right {}.\n",
2570                while_cond, idx_var_expr, len_expr
2571            ));
2572
2573            // While body
2574            let while_body_list = format!("stmtList_{}", *counter);
2575            *counter += 1;
2576            output.push_str(&format!("Let {} be a new Seq of CStmt.\n", while_body_list));
2577
2578            // Let loop_var = CIndex(coll, idx) (fresh iterable encoding)
2579            let iter_for_index = encode_expr_src(iterable, counter, output, interner, variants);
2580            let idx_ref = format!("e_{}", *counter);
2581            *counter += 1;
2582            output.push_str(&format!("Let {} be a new CVar with name \"{}\".\n", idx_ref, idx_name));
2583            let elem_expr = format!("e_{}", *counter);
2584            *counter += 1;
2585            output.push_str(&format!(
2586                "Let {} be a new CIndex with coll {} and idx {}.\n",
2587                elem_expr, iter_for_index, idx_ref
2588            ));
2589            let elem_let = format!("s_{}", *counter);
2590            *counter += 1;
2591            output.push_str(&format!(
2592                "Let {} be a new CLet with name \"{}\" and expr {}.\n",
2593                elem_let, loop_var_name, elem_expr
2594            ));
2595            output.push_str(&format!("Push {} to {}.\n", elem_let, while_body_list));
2596
2597            // Encode body statements (use encode_stmts_src for Inspect/Repeat, encode_stmt_src for others)
2598            let body_refs: Vec<&Stmt> = body.iter().collect();
2599            for body_stmt in &body_refs {
2600                match body_stmt {
2601                    Stmt::Inspect { .. } | Stmt::Repeat { .. } => {
2602                        let vars = encode_stmts_src(body_stmt, counter, output, interner, variants);
2603                        for v in vars {
2604                            output.push_str(&format!("Push {} to {}.\n", v, while_body_list));
2605                        }
2606                    }
2607                    _ => {
2608                        let bvar = encode_stmt_src(body_stmt, counter, output, interner, variants);
2609                        if !bvar.is_empty() {
2610                            output.push_str(&format!("Push {} to {}.\n", bvar, while_body_list));
2611                        }
2612                    }
2613                }
2614            }
2615
2616            // Set idx = idx + 1
2617            let idx_ref2 = format!("e_{}", *counter);
2618            *counter += 1;
2619            output.push_str(&format!("Let {} be a new CVar with name \"{}\".\n", idx_ref2, idx_name));
2620            let one_expr = format!("e_{}", *counter);
2621            *counter += 1;
2622            output.push_str(&format!("Let {} be a new CInt with value 1.\n", one_expr));
2623            let inc_expr = format!("e_{}", *counter);
2624            *counter += 1;
2625            output.push_str(&format!(
2626                "Let {} be a new CBinOp with op \"+\" and left {} and right {}.\n",
2627                inc_expr, idx_ref2, one_expr
2628            ));
2629            let inc_set = format!("s_{}", *counter);
2630            *counter += 1;
2631            output.push_str(&format!(
2632                "Let {} be a new CSet with name \"{}\" and expr {}.\n",
2633                inc_set, idx_name, inc_expr
2634            ));
2635            output.push_str(&format!("Push {} to {}.\n", inc_set, while_body_list));
2636
2637            // CWhile node
2638            let while_var = format!("s_{}", *counter);
2639            *counter += 1;
2640            output.push_str(&format!(
2641                "Let {} be a new CWhile with cond {} and body {}.\n",
2642                while_var, while_cond, while_body_list
2643            ));
2644
2645            vec![idx_init, while_var]
2646        }
2647        _ => {
2648            let v = encode_stmt_src(stmt, counter, output, interner, variants);
2649            if v.is_empty() {
2650                vec![]
2651            } else {
2652                vec![v]
2653            }
2654        }
2655    }
2656}
2657
2658fn encode_stmt_list_src(stmts: &[&Stmt], counter: &mut usize, output: &mut String, interner: &Interner, variants: &HashMap<String, Vec<String>>) -> String {
2659    let list_var = format!("stmtList_{}", *counter);
2660    *counter += 1;
2661    output.push_str(&format!("Let {} be a new Seq of CStmt.\n", list_var));
2662
2663    for stmt in stmts {
2664        for stmt_var in encode_stmts_src(stmt, counter, output, interner, variants) {
2665            output.push_str(&format!("Push {} to {}.\n", stmt_var, list_var));
2666        }
2667    }
2668
2669    list_var
2670}
2671
2672fn extract_ident_name(expr: &Expr, interner: &Interner) -> String {
2673    match expr {
2674        Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2675        _ => "unknown".to_string(),
2676    }
2677}
2678
2679/// First Futamura Projection: PE(interpreter, program) = compiled_program
2680///
2681/// Specializes the interpreter with respect to a fixed program, producing
2682/// a compiled version with no interpretive overhead. For a self-interpreter
2683/// (where source and target language are the same), this produces the
2684/// program itself, with static optimizations applied.
2685///
2686/// The pipeline:
2687/// 1. Parse the program source
2688/// 2. Run the optimizer (fold, propagate, PE, DCE)
2689/// 3. Decompile the optimized AST back to source
2690/// 4. Verify no interpretive overhead remains
2691pub fn projection1_source(_core_types: &str, _interpreter: &str, program: &str) -> Result<String, String> {
2692    let full_source = if program.contains("## Main") || program.contains("## To ") {
2693        program.to_string()
2694    } else {
2695        format!("## Main\n{}", program)
2696    };
2697
2698    let mut interner = Interner::new();
2699    let mut lexer = Lexer::new(&full_source, &mut interner);
2700    let tokens = lexer.tokenize();
2701
2702    let type_registry = {
2703        let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
2704        let result = discovery.run_full();
2705        result.types
2706    };
2707
2708    let mut world_state = WorldState::new();
2709    let expr_arena = Arena::new();
2710    let term_arena = Arena::new();
2711    let np_arena = Arena::new();
2712    let sym_arena = Arena::new();
2713    let role_arena = Arena::new();
2714    let pp_arena = Arena::new();
2715    let stmt_arena: Arena<Stmt> = Arena::new();
2716    let imperative_expr_arena: Arena<Expr> = Arena::new();
2717    let type_expr_arena: Arena<TypeExpr> = Arena::new();
2718
2719    let ast_ctx = AstContext::with_types(
2720        &expr_arena, &term_arena, &np_arena, &sym_arena,
2721        &role_arena, &pp_arena, &stmt_arena, &imperative_expr_arena,
2722        &type_expr_arena,
2723    );
2724
2725    let mut parser = crate::parser::Parser::new(
2726        tokens, &mut world_state, &mut interner, ast_ctx, type_registry,
2727    );
2728    let stmts = parser.parse_program().map_err(|e| format!("Parse error: {:?}", e))?;
2729
2730    // First Futamura Projection: PE(interpreter, program) = compiled_program.
2731    // Use the projection-safe optimizer: fold + propagate + PE + CTFE.
2732    // This preserves control-flow structure (If/While) while still folding
2733    // constants, propagating values, and specializing function calls.
2734    let optimized = crate::optimize::optimize_for_projection(
2735        stmts, &imperative_expr_arena, &stmt_arena, &mut interner,
2736    );
2737
2738    let mut output = String::new();
2739
2740    for stmt in &optimized {
2741        if matches!(stmt, Stmt::FunctionDef { .. }) {
2742            decompile_stmt(stmt, &interner, &mut output, 0);
2743            output.push('\n');
2744        }
2745    }
2746
2747    output.push_str("## Main\n");
2748    for stmt in &optimized {
2749        if !matches!(stmt, Stmt::FunctionDef { .. }) {
2750            decompile_stmt(stmt, &interner, &mut output, 0);
2751        }
2752    }
2753
2754    Ok(output)
2755}
2756
2757fn decompile_stmt(stmt: &Stmt, interner: &Interner, out: &mut String, indent: usize) {
2758    let pad = "    ".repeat(indent);
2759    match stmt {
2760        Stmt::FunctionDef { name, params, body, return_type, .. } => {
2761            let fn_name = interner.resolve(*name);
2762            let param_strs: Vec<String> = params
2763                .iter()
2764                .map(|(name, ty)| {
2765                    let pname = interner.resolve(*name);
2766                    format!("{}: {}", pname, decompile_type_expr(ty, interner))
2767                })
2768                .collect();
2769            let ret_str = if let Some(rt) = return_type {
2770                format!(" -> {}", decompile_type_expr(rt, interner))
2771            } else {
2772                String::new()
2773            };
2774            out.push_str(&format!("{}## To {} ({}){}:\n", pad, fn_name, param_strs.join(", "), ret_str));
2775            for s in body.iter() {
2776                decompile_stmt(s, interner, out, indent + 1);
2777            }
2778        }
2779        Stmt::Let { var, value, mutable, .. } => {
2780            let name = interner.resolve(*var);
2781            let expr_str = decompile_expr(value, interner);
2782            if *mutable {
2783                out.push_str(&format!("{}Let mutable {} be {}.\n", pad, name, expr_str));
2784            } else {
2785                out.push_str(&format!("{}Let {} be {}.\n", pad, name, expr_str));
2786            }
2787        }
2788        Stmt::Set { target, value } => {
2789            let name = interner.resolve(*target);
2790            let expr_str = decompile_expr(value, interner);
2791            out.push_str(&format!("{}Set {} to {}.\n", pad, name, expr_str));
2792        }
2793        Stmt::Show { object, .. } => {
2794            let expr_str = decompile_expr(object, interner);
2795            out.push_str(&format!("{}Show {}.\n", pad, expr_str));
2796        }
2797        Stmt::Return { value } => {
2798            if let Some(expr) = value {
2799                let expr_str = decompile_expr(expr, interner);
2800                out.push_str(&format!("{}Return {}.\n", pad, expr_str));
2801            } else {
2802                out.push_str(&format!("{}Return.\n", pad));
2803            }
2804        }
2805        Stmt::If { cond, then_block, else_block } => {
2806            let cond_str = decompile_expr(cond, interner);
2807            out.push_str(&format!("{}If {}:\n", pad, cond_str));
2808            for s in then_block.iter() {
2809                decompile_stmt(s, interner, out, indent + 1);
2810            }
2811            if let Some(els) = else_block {
2812                out.push_str(&format!("{}Otherwise:\n", pad));
2813                for s in els.iter() {
2814                    decompile_stmt(s, interner, out, indent + 1);
2815                }
2816            }
2817        }
2818        Stmt::While { cond, body, .. } => {
2819            let cond_str = decompile_expr(cond, interner);
2820            out.push_str(&format!("{}While {}:\n", pad, cond_str));
2821            for s in body.iter() {
2822                decompile_stmt(s, interner, out, indent + 1);
2823            }
2824        }
2825        Stmt::Call { function, args } => {
2826            let fn_name = interner.resolve(*function);
2827            let arg_strs: Vec<String> = args.iter().map(|a| decompile_expr(a, interner)).collect();
2828            if arg_strs.is_empty() {
2829                out.push_str(&format!("{}{}().\n", pad, fn_name));
2830            } else {
2831                out.push_str(&format!("{}{}({}).\n", pad, fn_name, arg_strs.join(", ")));
2832            }
2833        }
2834        Stmt::Push { value, collection } => {
2835            let val_str = decompile_expr(value, interner);
2836            let coll_str = decompile_expr(collection, interner);
2837            out.push_str(&format!("{}Push {} to {}.\n", pad, val_str, coll_str));
2838        }
2839        Stmt::SetIndex { collection, index, value } => {
2840            let coll_str = decompile_expr(collection, interner);
2841            let idx_str = decompile_expr(index, interner);
2842            let val_str = decompile_expr(value, interner);
2843            out.push_str(&format!("{}Set item {} of {} to {}.\n", pad, idx_str, coll_str, val_str));
2844        }
2845        Stmt::SetField { object, field, value } => {
2846            let obj_str = decompile_expr(object, interner);
2847            let field_name = interner.resolve(*field);
2848            let val_str = decompile_expr(value, interner);
2849            out.push_str(&format!("{}Set {} of {} to {}.\n", pad, field_name, obj_str, val_str));
2850        }
2851        Stmt::Repeat { pattern, iterable, body, .. } => {
2852            let var_name = match pattern {
2853                Pattern::Identifier(sym) => interner.resolve(*sym).to_string(),
2854                Pattern::Tuple(syms) => {
2855                    syms.iter().map(|s| interner.resolve(*s).to_string()).collect::<Vec<_>>().join(", ")
2856                }
2857            };
2858            let iter_str = decompile_expr(iterable, interner);
2859            out.push_str(&format!("{}Repeat for {} in {}:\n", pad, var_name, iter_str));
2860            for s in body.iter() {
2861                decompile_stmt(s, interner, out, indent + 1);
2862            }
2863        }
2864        Stmt::Inspect { target, arms, .. } => {
2865            let target_str = decompile_expr(target, interner);
2866            out.push_str(&format!("{}Inspect {}:\n", pad, target_str));
2867            for arm in arms {
2868                if let Some(variant) = arm.variant {
2869                    let variant_name = interner.resolve(variant);
2870                    let bindings: Vec<String> = arm.bindings.iter()
2871                        .map(|(_, b)| interner.resolve(*b).to_string())
2872                        .collect();
2873                    if bindings.is_empty() {
2874                        out.push_str(&format!("{}    When {}:\n", pad, variant_name));
2875                    } else {
2876                        out.push_str(&format!("{}    When {}({}):\n", pad, variant_name, bindings.join(", ")));
2877                    }
2878                } else {
2879                    out.push_str(&format!("{}    Otherwise:\n", pad));
2880                }
2881                for s in arm.body.iter() {
2882                    decompile_stmt(s, interner, out, indent + 2);
2883                }
2884            }
2885        }
2886        Stmt::Pop { collection, into } => {
2887            let coll_str = decompile_expr(collection, interner);
2888            if let Some(target) = into {
2889                let target_name = interner.resolve(*target);
2890                out.push_str(&format!("{}Pop from {} into {}.\n", pad, coll_str, target_name));
2891            } else {
2892                out.push_str(&format!("{}Pop from {}.\n", pad, coll_str));
2893            }
2894        }
2895        Stmt::Break => {
2896            out.push_str(&format!("{}Break.\n", pad));
2897        }
2898        Stmt::RuntimeAssert { condition } => {
2899            let cond_str = decompile_expr(condition, interner);
2900            out.push_str(&format!("{}Assert that {}.\n", pad, cond_str));
2901        }
2902        Stmt::Add { value, collection } => {
2903            let val_str = decompile_expr(value, interner);
2904            let coll_str = decompile_expr(collection, interner);
2905            out.push_str(&format!("{}Add {} to {}.\n", pad, val_str, coll_str));
2906        }
2907        Stmt::Remove { value, collection } => {
2908            let val_str = decompile_expr(value, interner);
2909            let coll_str = decompile_expr(collection, interner);
2910            out.push_str(&format!("{}Remove {} from {}.\n", pad, val_str, coll_str));
2911        }
2912        Stmt::Zone { name, body, .. } => {
2913            let zone_name = interner.resolve(*name);
2914            out.push_str(&format!("{}Inside a new zone called \"{}\":\n", pad, zone_name));
2915            for s in body.iter() {
2916                decompile_stmt(s, interner, out, indent + 1);
2917            }
2918        }
2919        Stmt::ReadFrom { var, .. } => {
2920            let var_name = interner.resolve(*var);
2921            out.push_str(&format!("{}Read {} from the console.\n", pad, var_name));
2922        }
2923        Stmt::WriteFile { content, path } => {
2924            let content_str = decompile_expr(content, interner);
2925            let path_str = decompile_expr(path, interner);
2926            out.push_str(&format!("{}Write {} to file {}.\n", pad, content_str, path_str));
2927        }
2928        Stmt::Sleep { milliseconds } => {
2929            let ms = decompile_expr(milliseconds, interner);
2930            out.push_str(&format!("{}Sleep {}.\n", pad, ms));
2931        }
2932        _ => {
2933            // Remaining system-level statements (CRDT, networking, concurrency)
2934            // are not produced by the optimizer and don't appear in P1 residuals.
2935        }
2936    }
2937}
2938
2939fn decompile_expr(expr: &Expr, interner: &Interner) -> String {
2940    match expr {
2941        Expr::Literal(lit) => match lit {
2942            Literal::Number(n) => n.to_string(),
2943            Literal::Float(f) => format!("{}", f),
2944            Literal::Boolean(b) => if *b { "true".to_string() } else { "false".to_string() },
2945            Literal::Text(s) => format!("\"{}\"", interner.resolve(*s)),
2946            Literal::Nothing => "nothing".to_string(),
2947            Literal::Char(c) => format!("'{}'", c),
2948            Literal::Duration(ns) => format!("{}", ns),
2949            Literal::Date(days) => format!("{}", days),
2950            Literal::Moment(ns) => format!("{}", ns),
2951            Literal::Span { months, days } => format!("{} months {} days", months, days),
2952            Literal::Time(ns) => format!("{}", ns),
2953        },
2954        Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2955        Expr::BinaryOp { op, left, right } => {
2956            let l = if matches!(left, Expr::BinaryOp { .. }) {
2957                format!("({})", decompile_expr(left, interner))
2958            } else {
2959                decompile_expr(left, interner)
2960            };
2961            let r = if matches!(right, Expr::BinaryOp { .. }) {
2962                format!("({})", decompile_expr(right, interner))
2963            } else {
2964                decompile_expr(right, interner)
2965            };
2966            // Shift operations are introduced by bit-strength optimization.
2967            // Map back to equivalent multiply/divide for LOGOS source.
2968            if matches!(op, BinaryOpKind::Shl) {
2969                // n << k = n * 2^k
2970                if let Expr::Literal(Literal::Number(k)) = right {
2971                    let multiplier = 1i64 << k;
2972                    return format!("{} * {}", l, multiplier);
2973                }
2974            }
2975            if matches!(op, BinaryOpKind::Shr) {
2976                // n >> k = n / 2^k
2977                if let Expr::Literal(Literal::Number(k)) = right {
2978                    let divisor = 1i64 << k;
2979                    return format!("{} / {}", l, divisor);
2980                }
2981            }
2982            let op_str = match op {
2983                BinaryOpKind::Add => "+",
2984                BinaryOpKind::Subtract => "-",
2985                BinaryOpKind::Multiply => "*",
2986                BinaryOpKind::Divide => "/",
2987                BinaryOpKind::Modulo => "%",
2988                BinaryOpKind::Eq => "equals",
2989                BinaryOpKind::NotEq => "is not",
2990                BinaryOpKind::Lt => "is less than",
2991                BinaryOpKind::Gt => "is greater than",
2992                BinaryOpKind::LtEq => "is at most",
2993                BinaryOpKind::GtEq => "is at least",
2994                BinaryOpKind::And => "and",
2995                BinaryOpKind::Or => "or",
2996                BinaryOpKind::Concat => "+",
2997                BinaryOpKind::BitXor => "+",
2998                BinaryOpKind::Shl => "*",
2999                BinaryOpKind::Shr => "/",
3000            };
3001            format!("{} {} {}", l, op_str, r)
3002        }
3003        Expr::Not { operand } => {
3004            let inner = decompile_expr(operand, interner);
3005            format!("not {}", inner)
3006        }
3007        Expr::Call { function, args } => {
3008            let fn_name = interner.resolve(*function);
3009            let arg_strs: Vec<String> = args.iter().map(|a| decompile_expr(a, interner)).collect();
3010            if arg_strs.is_empty() {
3011                format!("{}()", fn_name)
3012            } else {
3013                format!("{}({})", fn_name, arg_strs.join(", "))
3014            }
3015        }
3016        Expr::Index { collection, index } => {
3017            let coll = decompile_expr(collection, interner);
3018            let idx = decompile_expr(index, interner);
3019            format!("item {} of {}", idx, coll)
3020        }
3021        Expr::Length { collection } => {
3022            let coll = decompile_expr(collection, interner);
3023            format!("length of {}", coll)
3024        }
3025        Expr::FieldAccess { object, field } => {
3026            let obj = decompile_expr(object, interner);
3027            let field_name = interner.resolve(*field);
3028            format!("{} of {}", field_name, obj)
3029        }
3030        Expr::New { type_name, .. } => {
3031            let tn = interner.resolve(*type_name);
3032            format!("a new {}", tn)
3033        }
3034        Expr::NewVariant { variant, fields, .. } => {
3035            let vn = interner.resolve(*variant);
3036            if fields.is_empty() {
3037                format!("a new {}", vn)
3038            } else {
3039                let parts: Vec<String> = fields.iter().map(|(name, val)| {
3040                    let n = interner.resolve(*name);
3041                    let v = decompile_expr(val, interner);
3042                    format!("{} {}", n, v)
3043                }).collect();
3044                format!("a new {} with {}", vn, parts.join(" and "))
3045            }
3046        }
3047        Expr::InterpolatedString(parts) => {
3048            let mut result = String::new();
3049            for part in parts {
3050                match part {
3051                    StringPart::Literal(sym) => {
3052                        result.push_str(&interner.resolve(*sym));
3053                    }
3054                    StringPart::Expr { value, debug, .. } => {
3055                        let expr_str = decompile_expr(value, interner);
3056                        if *debug {
3057                            result.push_str(&format!("{{{}=}}", expr_str));
3058                        } else {
3059                            result.push_str(&format!("{{{}}}", expr_str));
3060                        }
3061                    }
3062                }
3063            }
3064            format!("\"{}\"", result)
3065        }
3066        Expr::Slice { collection, start, end } => {
3067            let coll = decompile_expr(collection, interner);
3068            let s = decompile_expr(start, interner);
3069            let e = decompile_expr(end, interner);
3070            format!("{} {} through {}", coll, s, e)
3071        }
3072        Expr::Copy { expr } => {
3073            let inner = decompile_expr(expr, interner);
3074            format!("copy of {}", inner)
3075        }
3076        Expr::Give { value } => {
3077            let inner = decompile_expr(value, interner);
3078            format!("Give {}", inner)
3079        }
3080        Expr::Contains { collection, value } => {
3081            let coll = decompile_expr(collection, interner);
3082            let val = decompile_expr(value, interner);
3083            format!("{} contains {}", coll, val)
3084        }
3085        Expr::Union { left, right } => {
3086            let l = decompile_expr(left, interner);
3087            let r = decompile_expr(right, interner);
3088            format!("{} union {}", l, r)
3089        }
3090        Expr::Intersection { left, right } => {
3091            let l = decompile_expr(left, interner);
3092            let r = decompile_expr(right, interner);
3093            format!("{} intersection {}", l, r)
3094        }
3095        Expr::List(elems) => {
3096            let parts: Vec<String> = elems.iter().map(|e| decompile_expr(e, interner)).collect();
3097            format!("[{}]", parts.join(", "))
3098        }
3099        Expr::Tuple(elems) => {
3100            let parts: Vec<String> = elems.iter().map(|e| decompile_expr(e, interner)).collect();
3101            format!("({})", parts.join(", "))
3102        }
3103        Expr::Range { start, end } => {
3104            let s = decompile_expr(start, interner);
3105            let e = decompile_expr(end, interner);
3106            format!("{} to {}", s, e)
3107        }
3108        Expr::OptionSome { value } => {
3109            let inner = decompile_expr(value, interner);
3110            format!("some {}", inner)
3111        }
3112        Expr::OptionNone => "none".to_string(),
3113        Expr::WithCapacity { value, capacity } => {
3114            let val = decompile_expr(value, interner);
3115            let cap = decompile_expr(capacity, interner);
3116            format!("{} with capacity {}", val, cap)
3117        }
3118        Expr::Escape { language, code } => {
3119            let lang = interner.resolve(*language);
3120            let src = interner.resolve(*code);
3121            format!("Escape to {}:\n{}", lang, src)
3122        }
3123        Expr::ManifestOf { zone } => {
3124            let z = decompile_expr(zone, interner);
3125            format!("the manifest of {}", z)
3126        }
3127        Expr::ChunkAt { index, zone } => {
3128            let idx = decompile_expr(index, interner);
3129            let z = decompile_expr(zone, interner);
3130            format!("the chunk at {} in {}", idx, z)
3131        }
3132        Expr::Closure { params, body, return_type } => {
3133            let param_strs: Vec<String> = params.iter().map(|(name, ty)| {
3134                let n = interner.resolve(*name);
3135                let t = decompile_type_expr(ty, interner);
3136                format!("{}: {}", n, t)
3137            }).collect();
3138            let ret = if let Some(rt) = return_type {
3139                format!(" -> {}", decompile_type_expr(rt, interner))
3140            } else {
3141                String::new()
3142            };
3143            match body {
3144                ClosureBody::Expression(expr) => {
3145                    let e = decompile_expr(expr, interner);
3146                    format!("({}){} -> {}", param_strs.join(", "), ret, e)
3147                }
3148                ClosureBody::Block(_) => {
3149                    format!("({}){} -> [block]", param_strs.join(", "), ret)
3150                }
3151            }
3152        }
3153        Expr::CallExpr { callee, args } => {
3154            let c = decompile_expr(callee, interner);
3155            let arg_strs: Vec<String> = args.iter().map(|a| decompile_expr(a, interner)).collect();
3156            format!("{}({})", c, arg_strs.join(", "))
3157        }
3158    }
3159}
3160
3161fn decompile_type_expr(ty: &TypeExpr, interner: &Interner) -> String {
3162    match ty {
3163        TypeExpr::Primitive(sym) => interner.resolve(*sym).to_string(),
3164        TypeExpr::Named(sym) => interner.resolve(*sym).to_string(),
3165        TypeExpr::Generic { base, params } => {
3166            let base_str = interner.resolve(*base);
3167            let param_strs: Vec<String> = params.iter().map(|p| decompile_type_expr(p, interner)).collect();
3168            format!("{} of {}", base_str, param_strs.join(" and "))
3169        }
3170        TypeExpr::Function { inputs, output } => {
3171            let in_strs: Vec<String> = inputs.iter().map(|t| decompile_type_expr(t, interner)).collect();
3172            let out_str = decompile_type_expr(output, interner);
3173            format!("fn({}) -> {}", in_strs.join(", "), out_str)
3174        }
3175        TypeExpr::Refinement { base, .. } => {
3176            decompile_type_expr(base, interner)
3177        }
3178        TypeExpr::Persistent { inner } => {
3179            format!("Persistent {}", decompile_type_expr(inner, interner))
3180        }
3181    }
3182}
3183
3184/// Verify that a LogicAffeine program has no interpretive overhead.
3185///
3186/// Checks the AST for patterns that indicate unresolved interpreter dispatch:
3187/// - Inspect on CStmt/CExpr/CVal variants
3188/// - References to Core constructor types (CInt, CShow, etc.)
3189/// - Environment lookups on literal strings
3190pub fn verify_no_overhead_source(source: &str) -> Result<(), String> {
3191    let mut interner = Interner::new();
3192    let mut lexer = Lexer::new(source, &mut interner);
3193    let tokens = lexer.tokenize();
3194
3195    let type_registry = {
3196        let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
3197        let result = discovery.run_full();
3198        result.types
3199    };
3200
3201    let mut world_state = WorldState::new();
3202    let expr_arena = Arena::new();
3203    let term_arena = Arena::new();
3204    let np_arena = Arena::new();
3205    let sym_arena = Arena::new();
3206    let role_arena = Arena::new();
3207    let pp_arena = Arena::new();
3208    let stmt_arena: Arena<Stmt> = Arena::new();
3209    let imperative_expr_arena: Arena<Expr> = Arena::new();
3210    let type_expr_arena: Arena<TypeExpr> = Arena::new();
3211
3212    let ast_ctx = AstContext::with_types(
3213        &expr_arena, &term_arena, &np_arena, &sym_arena,
3214        &role_arena, &pp_arena, &stmt_arena, &imperative_expr_arena,
3215        &type_expr_arena,
3216    );
3217
3218    let mut parser = crate::parser::Parser::new(
3219        tokens, &mut world_state, &mut interner, ast_ctx, type_registry,
3220    );
3221    let stmts = parser.parse_program().map_err(|e| format!("Parse error: {:?}", e))?;
3222
3223    verify_no_overhead_stmts(&stmts, &interner)
3224}
3225
3226const CORE_VARIANT_NAMES: &[&str] = &[
3227    "CInt", "CBool", "CText", "CVar", "CBinOp", "CNot",
3228    "CCall", "CIndex", "CLen", "CMapGet",
3229    "CLet", "CSet", "CIf", "CWhile", "CReturn", "CShow",
3230    "CCallS", "CPush", "CSetIdx", "CMapSet",
3231    "CFuncDef", "CProg",
3232    "VInt", "VBool", "VText", "VSeq", "VMap", "VError", "VNothing",
3233];
3234
3235fn verify_no_overhead_stmts(stmts: &[Stmt], interner: &Interner) -> Result<(), String> {
3236    for stmt in stmts {
3237        check_stmt_overhead(stmt, interner)?;
3238    }
3239    Ok(())
3240}
3241
3242fn check_stmt_overhead(stmt: &Stmt, interner: &Interner) -> Result<(), String> {
3243    match stmt {
3244        Stmt::Inspect { arms, .. } => {
3245            for arm in arms {
3246                if let Some(variant) = arm.variant {
3247                    let variant_name = interner.resolve(variant);
3248                    if CORE_VARIANT_NAMES.contains(&variant_name) {
3249                        return Err(format!(
3250                            "Interpretive overhead: Inspect dispatches on Core variant '{}'",
3251                            variant_name
3252                        ));
3253                    }
3254                }
3255                for s in arm.body.iter() {
3256                    check_stmt_overhead(s, interner)?;
3257                }
3258            }
3259        }
3260        Stmt::If { cond, then_block, else_block } => {
3261            check_expr_overhead(cond, interner)?;
3262            for s in then_block.iter() {
3263                check_stmt_overhead(s, interner)?;
3264            }
3265            if let Some(els) = else_block {
3266                for s in els.iter() {
3267                    check_stmt_overhead(s, interner)?;
3268                }
3269            }
3270        }
3271        Stmt::While { cond, body, .. } => {
3272            check_expr_overhead(cond, interner)?;
3273            for s in body.iter() {
3274                check_stmt_overhead(s, interner)?;
3275            }
3276        }
3277        Stmt::FunctionDef { body, .. } => {
3278            for s in body.iter() {
3279                check_stmt_overhead(s, interner)?;
3280            }
3281        }
3282        Stmt::Repeat { body, .. } => {
3283            for s in body.iter() {
3284                check_stmt_overhead(s, interner)?;
3285            }
3286        }
3287        Stmt::Let { value, .. } | Stmt::Set { value, .. } | Stmt::Show { object: value, .. } => {
3288            check_expr_overhead(value, interner)?;
3289        }
3290        Stmt::Return { value } => {
3291            if let Some(v) = value {
3292                check_expr_overhead(v, interner)?;
3293            }
3294        }
3295        _ => {}
3296    }
3297    Ok(())
3298}
3299
3300fn check_expr_overhead(expr: &Expr, interner: &Interner) -> Result<(), String> {
3301    match expr {
3302        Expr::Index { collection, index } => {
3303            // Check for `item X of env` where X is a literal string (env lookup overhead)
3304            if let Expr::Identifier(coll_sym) = collection {
3305                let coll_name = interner.resolve(*coll_sym);
3306                if coll_name == "env" {
3307                    if let Expr::Literal(Literal::Text(_)) = index {
3308                        return Err(
3309                            "Interpretive overhead: environment lookup 'item ... of env' on literal key".to_string()
3310                        );
3311                    }
3312                }
3313            }
3314            check_expr_overhead(collection, interner)?;
3315            check_expr_overhead(index, interner)?;
3316        }
3317        Expr::New { type_name, .. } => {
3318            let tn = interner.resolve(*type_name);
3319            if CORE_VARIANT_NAMES.contains(&tn) {
3320                return Err(format!(
3321                    "Interpretive overhead: Core type constructor 'new {}'", tn
3322                ));
3323            }
3324        }
3325        Expr::NewVariant { variant, .. } => {
3326            let vn = interner.resolve(*variant);
3327            if CORE_VARIANT_NAMES.contains(&vn) {
3328                return Err(format!(
3329                    "Interpretive overhead: Core variant constructor '{}'", vn
3330                ));
3331            }
3332        }
3333        Expr::Call { function, args } => {
3334            let fn_name = interner.resolve(*function);
3335            if CORE_VARIANT_NAMES.contains(&fn_name) {
3336                return Err(format!(
3337                    "Interpretive overhead: Core variant call '{}'", fn_name
3338                ));
3339            }
3340            for a in args {
3341                check_expr_overhead(a, interner)?;
3342            }
3343        }
3344        Expr::BinaryOp { left, right, .. } => {
3345            check_expr_overhead(left, interner)?;
3346            check_expr_overhead(right, interner)?;
3347        }
3348        Expr::Not { operand } => {
3349            check_expr_overhead(operand, interner)?;
3350        }
3351        Expr::Length { collection } => {
3352            check_expr_overhead(collection, interner)?;
3353        }
3354        Expr::FieldAccess { object, .. } => {
3355            check_expr_overhead(object, interner)?;
3356        }
3357        _ => {}
3358    }
3359    Ok(())
3360}
3361
3362/// Returns the source text of the partial evaluator written in LogicAffeine.
3363///
3364/// This PE operates on CProgram representations using explicit environments
3365/// and static dispatch. It is first-order (no closures) and uses only
3366/// literal string function names (no dynamic dispatch).
3367pub fn pe_source_text() -> &'static str {
3368    include_str!("optimize/pe_source.logos")
3369}
3370
3371const CORE_TYPES_FOR_PE: &str = r#"
3372## A CExpr is one of:
3373    A CInt with value Int.
3374    A CFloat with value Real.
3375    A CBool with value Bool.
3376    A CText with value Text.
3377    A CVar with name Text.
3378    A CBinOp with op Text and left CExpr and right CExpr.
3379    A CNot with inner CExpr.
3380    A CCall with name Text and args Seq of CExpr.
3381    A CIndex with coll CExpr and idx CExpr.
3382    A CLen with target CExpr.
3383    A CMapGet with target CExpr and key CExpr.
3384    A CNewSeq.
3385    A CNewVariant with tag Text and fnames Seq of Text and fvals Seq of CExpr.
3386    A CList with items Seq of CExpr.
3387    A CRange with start CExpr and end CExpr.
3388    A CSlice with coll CExpr and startIdx CExpr and endIdx CExpr.
3389    A CCopy with target CExpr.
3390    A CNewSet.
3391    A CContains with coll CExpr and elem CExpr.
3392    A CUnion with left CExpr and right CExpr.
3393    A CIntersection with left CExpr and right CExpr.
3394    A COptionSome with inner CExpr.
3395    A COptionNone.
3396    A CTuple with items Seq of CExpr.
3397    A CNew with typeName Text and fieldNames Seq of Text and fields Seq of CExpr.
3398    A CFieldAccess with target CExpr and field Text.
3399    A CClosure with params Seq of Text and body Seq of CStmt and captured Seq of Text.
3400    A CCallExpr with target CExpr and args Seq of CExpr.
3401    A CInterpolatedString with parts Seq of CStringPart.
3402    A CDuration with amount CExpr and unit Text.
3403    A CTimeNow.
3404    A CDateToday.
3405    A CEscExpr with code Text.
3406
3407## A CStringPart is one of:
3408    A CLiteralPart with value Text.
3409    A CExprPart with expr CExpr.
3410
3411## A CStmt is one of:
3412    A CLet with name Text and expr CExpr.
3413    A CSet with name Text and expr CExpr.
3414    A CIf with cond CExpr and thenBlock Seq of CStmt and elseBlock Seq of CStmt.
3415    A CWhile with cond CExpr and body Seq of CStmt.
3416    A CReturn with expr CExpr.
3417    A CShow with expr CExpr.
3418    A CCallS with name Text and args Seq of CExpr.
3419    A CPush with expr CExpr and target Text.
3420    A CSetIdx with target Text and idx CExpr and val CExpr.
3421    A CMapSet with target Text and key CExpr and val CExpr.
3422    A CPop with target Text.
3423    A CRepeat with var Text and coll CExpr and body Seq of CStmt.
3424    A CRepeatRange with var Text and start CExpr and end CExpr and body Seq of CStmt.
3425    A CBreak.
3426    A CAdd with elem CExpr and target Text.
3427    A CRemove with elem CExpr and target Text.
3428    A CSetField with target Text and field Text and val CExpr.
3429    A CStructDef with name Text and fieldNames Seq of Text.
3430    A CInspect with target CExpr and arms Seq of CMatchArm.
3431    A CEnumDef with name Text and variants Seq of Text.
3432    A CRuntimeAssert with cond CExpr and msg CExpr.
3433    A CGive with expr CExpr and target Text.
3434    A CEscStmt with code Text.
3435    A CSleep with duration CExpr.
3436    A CReadConsole with target Text.
3437    A CReadFile with path CExpr and target Text.
3438    A CWriteFile with path CExpr and content CExpr.
3439    A CCheck with predicate CExpr and msg CExpr.
3440    A CAssert with proposition CExpr.
3441    A CTrust with proposition CExpr and justification Text.
3442    A CRequire with dependency Text.
3443    A CMerge with target Text and other CExpr.
3444    A CIncrease with target Text and amount CExpr.
3445    A CDecrease with target Text and amount CExpr.
3446    A CAppendToSeq with target Text and value CExpr.
3447    A CResolve with target Text.
3448    A CSync with target Text and channel CExpr.
3449    A CMount with target Text and path CExpr.
3450    A CConcurrent with branches Seq of Seq of CStmt.
3451    A CParallel with branches Seq of Seq of CStmt.
3452    A CLaunchTask with body Seq of CStmt and handle Text.
3453    A CStopTask with handle CExpr.
3454    A CSelect with branches Seq of CSelectBranch.
3455    A CCreatePipe with name Text and capacity CExpr.
3456    A CSendPipe with chan Text and value CExpr.
3457    A CReceivePipe with chan Text and target Text.
3458    A CTrySendPipe with chan Text and value CExpr.
3459    A CTryReceivePipe with chan Text and target Text.
3460    A CSpawn with agentType Text and target Text.
3461    A CSendMessage with target CExpr and msg CExpr.
3462    A CAwaitMessage with target Text.
3463    A CListen with addr CExpr and handler Text.
3464    A CConnectTo with addr CExpr and target Text.
3465    A CZone with name Text and kind Text and body Seq of CStmt.
3466
3467## A CSelectBranch is one of:
3468    A CSelectRecv with chan Text and var Text and body Seq of CStmt.
3469    A CSelectTimeout with duration CExpr and body Seq of CStmt.
3470
3471## A CMatchArm is one of:
3472    A CWhen with variantName Text and bindings Seq of Text and body Seq of CStmt.
3473    A COtherwise with body Seq of CStmt.
3474
3475## A CFunc is one of:
3476    A CFuncDef with name Text and params Seq of Text and body Seq of CStmt.
3477
3478## A CProgram is one of:
3479    A CProg with funcs Seq of CFunc and main Seq of CStmt.
3480
3481## A CVal is one of:
3482    A VInt with value Int.
3483    A VFloat with value Real.
3484    A VBool with value Bool.
3485    A VText with value Text.
3486    A VSeq with items Seq of CVal.
3487    A VMap with entries Map of Text to CVal.
3488    A VError with msg Text.
3489    A VNothing.
3490    A VSet with items Seq of CVal.
3491    A VOption with inner CVal and present Bool.
3492    A VTuple with items Seq of CVal.
3493    A VStruct with typeName Text and fields Map of Text to CVal.
3494    A VVariant with typeName Text and variantName Text and fields Seq of CVal.
3495    A VClosure with params Seq of Text and body Seq of CStmt and capturedEnv Map of Text to CVal.
3496    A VDuration with millis Int.
3497    A VDate with year Int and month Int and day Int.
3498    A VMoment with millis Int.
3499    A VSpan with startMillis Int and endMillis Int.
3500    A VTime with hour Int and minute Int and second Int.
3501    A VCrdt with kind Text and state Map of Text to CVal.
3502"#;
3503
3504/// Encodes the partial evaluator as CProgram construction source code.
3505///
3506/// Returns PE function definitions (so peBlock etc. are callable) followed by
3507/// LOGOS statements that construct the PE's functions as CFunc data in
3508/// `encodedFuncMap` and its main block in `encodedMain`.
3509/// The parser handles `## To` blocks anywhere in the token stream, so
3510/// function definitions placed after `## Main` are parsed correctly.
3511pub fn quote_pe_source() -> Result<String, String> {
3512    let pe_source = pe_source_text();
3513    let full_source = format!("{}\n{}", CORE_TYPES_FOR_PE, pe_source);
3514    let encoded = encode_program_source(&full_source).map_err(|e| format!("Failed to encode PE: {:?}", e))?;
3515    Ok(format!("{}\n{}", pe_source, encoded))
3516}
3517
3518/// Second Futamura Projection: PE(PE, interpreter) = compiler
3519///
3520/// Specializes the partial evaluator with respect to a fixed interpreter,
3521/// producing a compiler that takes any CProgram as input and produces
3522/// optimized residual CStmt/CExpr data.
3523///
3524/// For the Core self-interpreter (which is the identity evaluator on CProgram
3525/// data), PE(PE, int) resolves to the PE itself operating directly on program
3526/// data — the interpreter's dispatch loop is the CExpr/CStmt case analysis
3527/// that the PE already implements. The result is the PE with its entry points
3528/// renamed to compileExpr/compileBlock: these ARE the specialized compiler
3529/// functions with no PE dispatch overhead (BTA, memoization, etc. are absent
3530/// because the interpreter's representation IS the PE's representation).
3531pub fn projection2_source() -> Result<String, String> {
3532    let pe_source = pe_source_text();
3533
3534    let compiler_source = replace_word(&replace_word(&pe_source, "peExpr", "compileExpr"), "peBlock", "compileBlock");
3535
3536    Ok(format!("{}\n{}", CORE_TYPES_FOR_PE, compiler_source))
3537}
3538
3539/// Third Futamura Projection: PE(PE, PE) = compiler_generator
3540///
3541/// Specializes the partial evaluator with respect to itself, producing a
3542/// compiler generator (cogen). Feed it any interpreter → it produces a
3543/// compiler for that interpreter's language.
3544///
3545/// Chain: cogen(int) → compiler → compiler(P) → compiled
3546///
3547/// For the CExpr/CStmt representation, PE(PE, PE) yields the PE with entry
3548/// points renamed to cogenExpr/cogenBlock. This works because the PE's
3549/// self-application is idempotent: the PE already operates on the same
3550/// representation it would specialize, so PE(PE, PE) = PE (up to naming).
3551/// The cogen handles different interpreters (Core, RPN, etc.) by processing
3552/// their encoded CProgram representations.
3553pub fn projection3_source() -> Result<String, String> {
3554    let pe_source = pe_source_text();
3555
3556    let cogen_source = replace_word(&replace_word(&pe_source, "peExpr", "cogenExpr"), "peBlock", "cogenBlock");
3557
3558    Ok(format!("{}\n{}", CORE_TYPES_FOR_PE, cogen_source))
3559}
3560
3561fn replace_word(source: &str, from: &str, to: &str) -> String {
3562    let mut result = String::with_capacity(source.len());
3563    let mut remaining = source;
3564    while let Some(pos) = remaining.find(from) {
3565        let before = if pos > 0 { remaining.as_bytes()[pos - 1] } else { b' ' };
3566        let after_pos = pos + from.len();
3567        let after = if after_pos < remaining.len() { remaining.as_bytes()[after_pos] } else { b' ' };
3568        let is_word = !before.is_ascii_alphanumeric() && before != b'_'
3569            && !after.is_ascii_alphanumeric() && after != b'_';
3570        result.push_str(&remaining[..pos]);
3571        if is_word {
3572            result.push_str(to);
3573        } else {
3574            result.push_str(from);
3575        }
3576        remaining = &remaining[after_pos..];
3577    }
3578    result.push_str(remaining);
3579    result
3580}
3581
3582#[cfg(test)]
3583mod tests {
3584    use super::*;
3585
3586    #[test]
3587    fn test_compile_let_statement() {
3588        let source = "## Main\nLet x be 5.";
3589        let result = compile_to_rust(source);
3590        assert!(result.is_ok(), "Should compile: {:?}", result);
3591        let rust = result.unwrap();
3592        assert!(rust.contains("fn main()"));
3593        assert!(rust.contains("let x = 5;"));
3594    }
3595
3596    #[test]
3597    fn test_compile_return_statement() {
3598        let source = "## Main\nReturn 42.";
3599        let result = compile_to_rust(source);
3600        assert!(result.is_ok(), "Should compile: {:?}", result);
3601        let rust = result.unwrap();
3602        assert!(rust.contains("return 42;"));
3603    }
3604
3605}