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, is_native, .. } = stmt {
1307            if *is_native {
1308                continue; // Skip native function declarations — they have no encodable body
1309            }
1310            let fn_name = interner.resolve(*name).to_string();
1311            let param_names: Vec<String> = params
1312                .iter()
1313                .map(|(name, _)| interner.resolve(*name).to_string())
1314                .collect();
1315            let body_stmts: Vec<&Stmt> = body.iter().collect();
1316            functions.push((fn_name, param_names, body_stmts));
1317        } else {
1318            main_stmts.push(stmt);
1319        }
1320    }
1321
1322    let mut counter = 0usize;
1323    let mut output = String::new();
1324
1325    // Build the funcMap directly (Map of Text to CFunc) with fixed name
1326    output.push_str("Let encodedFuncMap be a new Map of Text to CFunc.\n");
1327
1328    for (fn_name, params, body) in &functions {
1329        let body_var = encode_stmt_list_src(body, &mut counter, &mut output, &interner, &variant_constructors);
1330
1331        let params_var = format!("params_{}", counter);
1332        counter += 1;
1333        output.push_str(&format!("Let {} be a new Seq of Text.\n", params_var));
1334        for p in params {
1335            output.push_str(&format!("Push \"{}\" to {}.\n", p, params_var));
1336        }
1337
1338        let func_var = format!("func_{}", counter);
1339        counter += 1;
1340        output.push_str(&format!(
1341            "Let {} be a new CFuncDef with name \"{}\" and params {} and body {}.\n",
1342            func_var, fn_name, params_var, body_var
1343        ));
1344        output.push_str(&format!(
1345            "Set item \"{}\" of encodedFuncMap to {}.\n",
1346            fn_name, func_var
1347        ));
1348    }
1349
1350    // Build main statement list with fixed name
1351    let main_var = encode_stmt_list_src(&main_stmts, &mut counter, &mut output, &interner, &variant_constructors);
1352    output.push_str(&format!("Let encodedMain be {}.\n", main_var));
1353
1354    Ok(output)
1355}
1356
1357/// Compact encoding: inlines simple expressions (literals, variables) to reduce
1358/// encoding size by ~3x. Same semantics as encode_program_source but produces
1359/// fewer Let statements.
1360pub fn encode_program_source_compact(source: &str) -> Result<String, ParseError> {
1361    let full_source = if source.contains("## Main") || source.contains("## To ") {
1362        source.to_string()
1363    } else {
1364        format!("## Main\n{}", source)
1365    };
1366
1367    let mut interner = Interner::new();
1368    let mut lexer = Lexer::new(&full_source, &mut interner);
1369    let tokens = lexer.tokenize();
1370
1371    let type_registry = {
1372        let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
1373        let result = discovery.run_full();
1374        result.types
1375    };
1376
1377    let mut variant_constructors: HashMap<String, Vec<String>> = HashMap::new();
1378    for (_type_name, type_def) in type_registry.iter_types() {
1379        if let crate::analysis::TypeDef::Enum { variants, .. } = type_def {
1380            for variant in variants {
1381                let vname = interner.resolve(variant.name).to_string();
1382                let field_names: Vec<String> = variant.fields.iter()
1383                    .map(|f| interner.resolve(f.name).to_string())
1384                    .collect();
1385                variant_constructors.insert(vname, field_names);
1386            }
1387        }
1388    }
1389
1390    let mut world_state = WorldState::new();
1391    let expr_arena = Arena::new();
1392    let term_arena = Arena::new();
1393    let np_arena = Arena::new();
1394    let sym_arena = Arena::new();
1395    let role_arena = Arena::new();
1396    let pp_arena = Arena::new();
1397    let stmt_arena: Arena<Stmt> = Arena::new();
1398    let imperative_expr_arena: Arena<Expr> = Arena::new();
1399    let type_expr_arena: Arena<TypeExpr> = Arena::new();
1400
1401    let ast_ctx = AstContext::with_types(
1402        &expr_arena, &term_arena, &np_arena, &sym_arena,
1403        &role_arena, &pp_arena, &stmt_arena, &imperative_expr_arena,
1404        &type_expr_arena,
1405    );
1406
1407    let mut parser = crate::parser::Parser::new(
1408        tokens, &mut world_state, &mut interner, ast_ctx, type_registry,
1409    );
1410    let stmts = parser.parse_program()?;
1411
1412    let mut functions: Vec<(String, Vec<String>, Vec<&Stmt>)> = Vec::new();
1413    let mut main_stmts: Vec<&Stmt> = Vec::new();
1414
1415    for stmt in &stmts {
1416        if let Stmt::FunctionDef { name, params, body, is_native, .. } = stmt {
1417            if *is_native { continue; }
1418            let fn_name = interner.resolve(*name).to_string();
1419            let param_names: Vec<String> = params
1420                .iter()
1421                .map(|(name, _)| interner.resolve(*name).to_string())
1422                .collect();
1423            let body_stmts: Vec<&Stmt> = body.iter().collect();
1424            functions.push((fn_name, param_names, body_stmts));
1425        } else {
1426            main_stmts.push(stmt);
1427        }
1428    }
1429
1430    let mut counter = 0usize;
1431    let mut output = String::new();
1432
1433    output.push_str("Let encodedFuncMap be a new Map of Text to CFunc.\n");
1434
1435    for (fn_name, params, body) in &functions {
1436        let body_var = encode_stmt_list_compact(body, &mut counter, &mut output, &interner, &variant_constructors);
1437
1438        let params_var = format!("params_{}", counter);
1439        counter += 1;
1440        output.push_str(&format!("Let {} be a new Seq of Text.\n", params_var));
1441        for p in params {
1442            output.push_str(&format!("Push \"{}\" to {}.\n", p, params_var));
1443        }
1444
1445        let func_var = format!("func_{}", counter);
1446        counter += 1;
1447        output.push_str(&format!(
1448            "Let {} be a new CFuncDef with name \"{}\" and params {} and body {}.\n",
1449            func_var, fn_name, params_var, body_var
1450        ));
1451        output.push_str(&format!(
1452            "Set item \"{}\" of encodedFuncMap to {}.\n",
1453            fn_name, func_var
1454        ));
1455    }
1456
1457    let main_var = encode_stmt_list_compact(&main_stmts, &mut counter, &mut output, &interner, &variant_constructors);
1458    output.push_str(&format!("Let encodedMain be {}.\n", main_var));
1459
1460    Ok(output)
1461}
1462
1463/// Returns an inline expression string for simple expressions (no Let variable needed).
1464/// Returns None for complex expressions that require a Let variable.
1465fn try_inline_expr(expr: &Expr, interner: &Interner) -> Option<String> {
1466    match expr {
1467        Expr::Literal(lit) => match lit {
1468            Literal::Number(n) => Some(format!("(a new CInt with value {})", n)),
1469            Literal::Boolean(b) => Some(format!("(a new CBool with value {})", b)),
1470            Literal::Text(s) => {
1471                let text = interner.resolve(*s);
1472                Some(format!("(a new CText with value \"{}\")", text))
1473            }
1474            Literal::Float(f) => Some(format!("(a new CFloat with value {})", f)),
1475            Literal::Nothing => Some("(a new CText with value \"nothing\")".to_string()),
1476            _ => None,
1477        },
1478        Expr::Identifier(sym) => {
1479            let name = interner.resolve(*sym);
1480            Some(format!("(a new CVar with name \"{}\")", name))
1481        }
1482        Expr::Not { operand } => {
1483            if let Some(inner) = try_inline_expr(operand, interner) {
1484                Some(format!("(a new CNot with inner {})", inner))
1485            } else {
1486                None
1487            }
1488        }
1489        Expr::OptionNone => Some("(a new COptionNone)".to_string()),
1490        _ => None,
1491    }
1492}
1493
1494fn encode_expr_compact(expr: &Expr, counter: &mut usize, output: &mut String, interner: &Interner, variants: &HashMap<String, Vec<String>>) -> String {
1495    // Try inline first
1496    if let Some(inline) = try_inline_expr(expr, interner) {
1497        return inline;
1498    }
1499
1500    // Fall back to Let variable (reuse encode_expr_src logic but with compact children)
1501    let var = format!("e_{}", *counter);
1502    *counter += 1;
1503
1504    match expr {
1505        Expr::BinaryOp { op, left, right } => {
1506            let left_var = encode_expr_compact(left, counter, output, interner, variants);
1507            let right_var = encode_expr_compact(right, counter, output, interner, variants);
1508            let op_str = match op {
1509                BinaryOpKind::Add => "+",
1510                BinaryOpKind::Subtract => "-",
1511                BinaryOpKind::Multiply => "*",
1512                BinaryOpKind::Divide => "/",
1513                BinaryOpKind::Modulo => "%",
1514                BinaryOpKind::Eq => "==",
1515                BinaryOpKind::NotEq => "!=",
1516                BinaryOpKind::Lt => "<",
1517                BinaryOpKind::Gt => ">",
1518                BinaryOpKind::LtEq => "<=",
1519                BinaryOpKind::GtEq => ">=",
1520                BinaryOpKind::And => "&&",
1521                BinaryOpKind::Or => "||",
1522                BinaryOpKind::Concat => "+",
1523                BinaryOpKind::BitXor => "^",
1524                BinaryOpKind::Shl => "<<",
1525                BinaryOpKind::Shr => ">>",
1526            };
1527            output.push_str(&format!(
1528                "Let {} be a new CBinOp with op \"{}\" and left {} and right {}.\n",
1529                var, op_str, left_var, right_var
1530            ));
1531        }
1532        Expr::Call { function, args } => {
1533            let fn_name = interner.resolve(*function);
1534            if let Some(field_names) = variants.get(fn_name) {
1535                let names_var = format!("nvNames_{}", *counter);
1536                *counter += 1;
1537                output.push_str(&format!("Let {} be a new Seq of Text.\n", names_var));
1538                let vals_var = format!("nvVals_{}", *counter);
1539                *counter += 1;
1540                output.push_str(&format!("Let {} be a new Seq of CExpr.\n", vals_var));
1541                for (i, arg) in args.iter().enumerate() {
1542                    let fname = field_names.get(i).map(|s| s.as_str()).unwrap_or("value");
1543                    output.push_str(&format!("Push \"{}\" to {}.\n", fname, names_var));
1544                    let arg_var = encode_expr_compact(arg, counter, output, interner, variants);
1545                    output.push_str(&format!("Push {} to {}.\n", arg_var, vals_var));
1546                }
1547                output.push_str(&format!(
1548                    "Let {} be a new CNewVariant with tag \"{}\" and fnames {} and fvals {}.\n",
1549                    var, fn_name, names_var, vals_var
1550                ));
1551            } else {
1552                let args_var = format!("callArgs_{}", *counter);
1553                *counter += 1;
1554                output.push_str(&format!("Let {} be a new Seq of CExpr.\n", args_var));
1555                for arg in args {
1556                    let arg_var = encode_expr_compact(arg, counter, output, interner, variants);
1557                    output.push_str(&format!("Push {} to {}.\n", arg_var, args_var));
1558                }
1559                output.push_str(&format!(
1560                    "Let {} be a new CCall with name \"{}\" and args {}.\n",
1561                    var, fn_name, args_var
1562                ));
1563            }
1564        }
1565        Expr::Index { collection, index } => {
1566            let coll_var = encode_expr_compact(collection, counter, output, interner, variants);
1567            let idx_var = encode_expr_compact(index, counter, output, interner, variants);
1568            output.push_str(&format!(
1569                "Let {} be a new CIndex with coll {} and idx {}.\n",
1570                var, coll_var, idx_var
1571            ));
1572        }
1573        Expr::Length { collection } => {
1574            let coll_var = encode_expr_compact(collection, counter, output, interner, variants);
1575            output.push_str(&format!("Let {} be a new CLen with target {}.\n", var, coll_var));
1576        }
1577        Expr::FieldAccess { object, field } => {
1578            let obj_var = encode_expr_compact(object, counter, output, interner, variants);
1579            let field_name = interner.resolve(*field);
1580            output.push_str(&format!(
1581                "Let {} be a new CMapGet with target {} and key (a new CText with value \"{}\").\n",
1582                var, obj_var, field_name
1583            ));
1584        }
1585        Expr::NewVariant { variant, fields, .. } => {
1586            let variant_name = interner.resolve(*variant);
1587            let names_var = format!("nvNames_{}", *counter);
1588            *counter += 1;
1589            output.push_str(&format!("Let {} be a new Seq of Text.\n", names_var));
1590            let vals_var = format!("nvVals_{}", *counter);
1591            *counter += 1;
1592            output.push_str(&format!("Let {} be a new Seq of CExpr.\n", vals_var));
1593            for (field_name, field_expr) in fields {
1594                let fname = interner.resolve(*field_name);
1595                output.push_str(&format!("Push \"{}\" to {}.\n", fname, names_var));
1596                let field_var = encode_expr_compact(field_expr, counter, output, interner, variants);
1597                output.push_str(&format!("Push {} to {}.\n", field_var, vals_var));
1598            }
1599            output.push_str(&format!(
1600                "Let {} be a new CNewVariant with tag \"{}\" and fnames {} and fvals {}.\n",
1601                var, variant_name, names_var, vals_var
1602            ));
1603        }
1604        Expr::New { type_name, init_fields, .. } => {
1605            let tn = interner.resolve(*type_name);
1606            if tn == "Seq" || tn == "List" {
1607                output.push_str(&format!("Let {} be a new CNewSeq.\n", var));
1608            } else if tn == "Set" {
1609                output.push_str(&format!("Let {} be a new CNewSet.\n", var));
1610            } else {
1611                let names_var = format!("nvNames_{}", *counter);
1612                *counter += 1;
1613                output.push_str(&format!("Let {} be a new Seq of Text.\n", names_var));
1614                let vals_var = format!("nvVals_{}", *counter);
1615                *counter += 1;
1616                output.push_str(&format!("Let {} be a new Seq of CExpr.\n", vals_var));
1617                for (field_name, field_expr) in init_fields {
1618                    let fname = interner.resolve(*field_name);
1619                    output.push_str(&format!("Push \"{}\" to {}.\n", fname, names_var));
1620                    let field_var = encode_expr_compact(field_expr, counter, output, interner, variants);
1621                    output.push_str(&format!("Push {} to {}.\n", field_var, vals_var));
1622                }
1623                output.push_str(&format!(
1624                    "Let {} be a new CNewVariant with tag \"{}\" and fnames {} and fvals {}.\n",
1625                    var, tn, names_var, vals_var
1626                ));
1627            }
1628        }
1629        Expr::InterpolatedString(parts) => {
1630            if parts.is_empty() {
1631                output.push_str(&format!("Let {} be (a new CText with value \"\").\n", var));
1632            } else {
1633                let mut part_vars: Vec<String> = Vec::new();
1634                for part in parts {
1635                    match part {
1636                        StringPart::Literal(sym) => {
1637                            let text = interner.resolve(*sym);
1638                            part_vars.push(format!("(a new CText with value \"{}\")", text));
1639                        }
1640                        StringPart::Expr { value, .. } => {
1641                            let pv = encode_expr_compact(value, counter, output, interner, variants);
1642                            part_vars.push(pv);
1643                        }
1644                    }
1645                }
1646                if part_vars.len() == 1 {
1647                    output.push_str(&format!("Let {} be {}.\n", var, part_vars[0]));
1648                } else {
1649                    let mut acc = part_vars[0].clone();
1650                    for pv in &part_vars[1..] {
1651                        let concat_var = format!("e_{}", *counter);
1652                        *counter += 1;
1653                        output.push_str(&format!(
1654                            "Let {} be a new CBinOp with op \"+\" and left {} and right {}.\n",
1655                            concat_var, acc, pv
1656                        ));
1657                        acc = concat_var;
1658                    }
1659                    output.push_str(&format!("Let {} be {}.\n", var, acc));
1660                }
1661            }
1662        }
1663        Expr::Range { start, end } => {
1664            let start_var = encode_expr_compact(start, counter, output, interner, variants);
1665            let end_var = encode_expr_compact(end, counter, output, interner, variants);
1666            output.push_str(&format!(
1667                "Let {} be a new CRange with start {} and end {}.\n",
1668                var, start_var, end_var
1669            ));
1670        }
1671        Expr::Copy { expr } => {
1672            let inner_var = encode_expr_compact(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_compact(collection, counter, output, interner, variants);
1677            let val_var = encode_expr_compact(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::OptionSome { value } => {
1684            let inner_var = encode_expr_compact(value, counter, output, interner, variants);
1685            output.push_str(&format!(
1686                "Let {} be a new COptionSome with inner {}.\n", var, inner_var
1687            ));
1688        }
1689        Expr::Tuple(elems) => {
1690            let items_var = format!("tupItems_{}", *counter);
1691            *counter += 1;
1692            output.push_str(&format!("Let {} be a new Seq of CExpr.\n", items_var));
1693            for elem in elems {
1694                let elem_var = encode_expr_compact(elem, counter, output, interner, variants);
1695                output.push_str(&format!("Push {} to {}.\n", elem_var, items_var));
1696            }
1697            output.push_str(&format!(
1698                "Let {} be a new CTuple with items {}.\n", var, items_var
1699            ));
1700        }
1701        Expr::Closure { params, body, .. } => {
1702            let params_var = format!("clp_{}", *counter);
1703            *counter += 1;
1704            output.push_str(&format!("Let {} be a new Seq of Text.\n", params_var));
1705            let mut param_names = HashSet::new();
1706            for (sym, _) in params {
1707                let name = interner.resolve(*sym);
1708                param_names.insert(name.to_string());
1709                output.push_str(&format!("Push \"{}\" to {}.\n", name, params_var));
1710            }
1711            let body_var = format!("clb_{}", *counter);
1712            *counter += 1;
1713            output.push_str(&format!("Let {} be a new Seq of CStmt.\n", body_var));
1714            match body {
1715                ClosureBody::Expression(e) => {
1716                    let ret_expr = encode_expr_compact(e, counter, output, interner, variants);
1717                    let ret_var = format!("s_{}", *counter);
1718                    *counter += 1;
1719                    output.push_str(&format!("Let {} be a new CReturn with expr {}.\n", ret_var, ret_expr));
1720                    output.push_str(&format!("Push {} to {}.\n", ret_var, body_var));
1721                }
1722                ClosureBody::Block(stmts) => {
1723                    for s in stmts.iter() {
1724                        let sv = encode_stmt_compact(s, counter, output, interner, variants);
1725                        output.push_str(&format!("Push {} to {}.\n", sv, body_var));
1726                    }
1727                }
1728            }
1729            let bound: HashSet<String> = param_names;
1730            let free = collect_free_vars_expr(expr, interner, &bound);
1731            let cap_var = format!("clc_{}", *counter);
1732            *counter += 1;
1733            output.push_str(&format!("Let {} be a new Seq of Text.\n", cap_var));
1734            for fv in &free {
1735                output.push_str(&format!("Push \"{}\" to {}.\n", fv, cap_var));
1736            }
1737            output.push_str(&format!(
1738                "Let {} be a new CClosure with params {} and body {} and captured {}.\n",
1739                var, params_var, body_var, cap_var
1740            ));
1741        }
1742        Expr::CallExpr { callee, args } => {
1743            let callee_var = encode_expr_compact(callee, counter, output, interner, variants);
1744            let args_var = format!("cea_{}", *counter);
1745            *counter += 1;
1746            output.push_str(&format!("Let {} be a new Seq of CExpr.\n", args_var));
1747            for a in args {
1748                let av = encode_expr_compact(a, counter, output, interner, variants);
1749                output.push_str(&format!("Push {} to {}.\n", av, args_var));
1750            }
1751            output.push_str(&format!(
1752                "Let {} be a new CCallExpr with target {} and args {}.\n",
1753                var, callee_var, args_var
1754            ));
1755        }
1756        Expr::Slice { collection, start, end } => {
1757            let coll_var = encode_expr_compact(collection, counter, output, interner, variants);
1758            let start_var = encode_expr_compact(start, counter, output, interner, variants);
1759            let end_var = encode_expr_compact(end, counter, output, interner, variants);
1760            output.push_str(&format!(
1761                "Let {} be a new CSlice with coll {} and startIdx {} and endIdx {}.\n",
1762                var, coll_var, start_var, end_var
1763            ));
1764        }
1765        Expr::Union { left, right } => {
1766            let left_var = encode_expr_compact(left, counter, output, interner, variants);
1767            let right_var = encode_expr_compact(right, counter, output, interner, variants);
1768            output.push_str(&format!(
1769                "Let {} be a new CUnion with left {} and right {}.\n",
1770                var, left_var, right_var
1771            ));
1772        }
1773        Expr::Intersection { left, right } => {
1774            let left_var = encode_expr_compact(left, counter, output, interner, variants);
1775            let right_var = encode_expr_compact(right, counter, output, interner, variants);
1776            output.push_str(&format!(
1777                "Let {} be a new CIntersection with left {} and right {}.\n",
1778                var, left_var, right_var
1779            ));
1780        }
1781        Expr::Give { value } => {
1782            let inner_var = encode_expr_compact(value, counter, output, interner, variants);
1783            output.push_str(&format!("Let {} be {}.\n", var, inner_var));
1784        }
1785        Expr::Escape { code, .. } => {
1786            let code_str = interner.resolve(*code);
1787            output.push_str(&format!(
1788                "Let {} be a new CEscExpr with code \"{}\".\n",
1789                var, code_str.replace('\"', "\\\"")
1790            ));
1791        }
1792        _ => {
1793            // For unsupported expressions, use the non-compact version
1794            output.push_str(&format!("Let {} be (a new CText with value \"unsupported\").\n", var));
1795        }
1796    }
1797
1798    var
1799}
1800
1801fn encode_stmt_compact(stmt: &Stmt, counter: &mut usize, output: &mut String, interner: &Interner, variants: &HashMap<String, Vec<String>>) -> String {
1802    let var = format!("s_{}", *counter);
1803    *counter += 1;
1804
1805    match stmt {
1806        Stmt::Let { var: name, value, .. } => {
1807            let name_str = interner.resolve(*name);
1808            let expr_var = encode_expr_compact(value, counter, output, interner, variants);
1809            output.push_str(&format!(
1810                "Let {} be a new CLet with name \"{}\" and expr {}.\n",
1811                var, name_str, expr_var
1812            ));
1813        }
1814        Stmt::Set { target, value } => {
1815            let name_str = interner.resolve(*target);
1816            let expr_var = encode_expr_compact(value, counter, output, interner, variants);
1817            output.push_str(&format!(
1818                "Let {} be a new CSet with name \"{}\" and expr {}.\n",
1819                var, name_str, expr_var
1820            ));
1821        }
1822        Stmt::If { cond, then_block, else_block } => {
1823            let cond_var = encode_expr_compact(cond, counter, output, interner, variants);
1824            let then_stmts: Vec<&Stmt> = then_block.iter().collect();
1825            let then_var = encode_stmt_list_compact(&then_stmts, counter, output, interner, variants);
1826            let else_var = if let Some(els) = else_block {
1827                let else_stmts: Vec<&Stmt> = els.iter().collect();
1828                encode_stmt_list_compact(&else_stmts, counter, output, interner, variants)
1829            } else {
1830                let empty_var = format!("emptyBlock_{}", *counter);
1831                *counter += 1;
1832                output.push_str(&format!("Let {} be a new Seq of CStmt.\n", empty_var));
1833                empty_var
1834            };
1835            output.push_str(&format!(
1836                "Let {} be a new CIf with cond {} and thenBlock {} and elseBlock {}.\n",
1837                var, cond_var, then_var, else_var
1838            ));
1839        }
1840        Stmt::While { cond, body, .. } => {
1841            let cond_var = encode_expr_compact(cond, counter, output, interner, variants);
1842            let body_stmts: Vec<&Stmt> = body.iter().collect();
1843            let body_var = encode_stmt_list_compact(&body_stmts, counter, output, interner, variants);
1844            output.push_str(&format!(
1845                "Let {} be a new CWhile with cond {} and body {}.\n",
1846                var, cond_var, body_var
1847            ));
1848        }
1849        Stmt::Return { value } => {
1850            if let Some(val) = value {
1851                let expr_var = encode_expr_compact(val, counter, output, interner, variants);
1852                output.push_str(&format!("Let {} be a new CReturn with expr {}.\n", var, expr_var));
1853            } else {
1854                output.push_str(&format!("Let {} be a new CReturn with expr (a new CText with value \"nothing\").\n", var));
1855            }
1856        }
1857        Stmt::Show { object, .. } => {
1858            let expr_var = encode_expr_compact(object, counter, output, interner, variants);
1859            output.push_str(&format!("Let {} be a new CShow with expr {}.\n", var, expr_var));
1860        }
1861        Stmt::Repeat { pattern, iterable, body } => {
1862            let var_str = match pattern {
1863                Pattern::Identifier(sym) => interner.resolve(*sym).to_string(),
1864                Pattern::Tuple(syms) => {
1865                    if let Some(s) = syms.first() {
1866                        interner.resolve(*s).to_string()
1867                    } else {
1868                        "item".to_string()
1869                    }
1870                }
1871            };
1872            let coll_var = encode_expr_compact(iterable, counter, output, interner, variants);
1873            let body_stmts: Vec<&Stmt> = body.iter().collect();
1874            let body_var = encode_stmt_list_compact(&body_stmts, counter, output, interner, variants);
1875            output.push_str(&format!(
1876                "Let {} be a new CRepeat with var \"{}\" and coll {} and body {}.\n",
1877                var, var_str, coll_var, body_var
1878            ));
1879        }
1880        Stmt::Push { value, collection } => {
1881            let coll_name = extract_ident_name(collection, interner);
1882            let expr_var = encode_expr_compact(value, counter, output, interner, variants);
1883            output.push_str(&format!(
1884                "Let {} be a new CPush with expr {} and target \"{}\".\n",
1885                var, expr_var, coll_name
1886            ));
1887        }
1888        Stmt::SetIndex { collection, index, value } => {
1889            let target_str = extract_ident_name(collection, interner);
1890            let idx_var = encode_expr_compact(index, counter, output, interner, variants);
1891            let val_var = encode_expr_compact(value, counter, output, interner, variants);
1892            output.push_str(&format!(
1893                "Let {} be a new CSetIdx with target \"{}\" and idx {} and val {}.\n",
1894                var, target_str, idx_var, val_var
1895            ));
1896        }
1897        Stmt::SetField { object, field, value } => {
1898            let target_str = extract_ident_name(object, interner);
1899            let field_str = interner.resolve(*field);
1900            let val_var = encode_expr_compact(value, counter, output, interner, variants);
1901            output.push_str(&format!(
1902                "Let {} be a new CSetField with target \"{}\" and field \"{}\" and val {}.\n",
1903                var, target_str, field_str, val_var
1904            ));
1905        }
1906        Stmt::Break => {
1907            output.push_str(&format!("Let {} be a new CBreak.\n", var));
1908        }
1909        Stmt::Inspect { target, arms, .. } => {
1910            let target_var = encode_expr_compact(target, counter, output, interner, variants);
1911            let arms_var = format!("arms_{}", *counter);
1912            *counter += 1;
1913            output.push_str(&format!("Let {} be a new Seq of CMatchArm.\n", arms_var));
1914            for arm in arms {
1915                if let Some(variant_sym) = arm.variant {
1916                    let vname = interner.resolve(variant_sym);
1917                    let bindings_var = format!("bindings_{}", *counter);
1918                    *counter += 1;
1919                    output.push_str(&format!("Let {} be a new Seq of Text.\n", bindings_var));
1920                    for (_, binding_name) in &arm.bindings {
1921                        let bn = interner.resolve(*binding_name);
1922                        output.push_str(&format!("Push \"{}\" to {}.\n", bn, bindings_var));
1923                    }
1924                    let body_stmts: Vec<&Stmt> = arm.body.iter().collect();
1925                    let body_var = encode_stmt_list_compact(&body_stmts, counter, output, interner, variants);
1926                    let arm_var = format!("arm_{}", *counter);
1927                    *counter += 1;
1928                    output.push_str(&format!(
1929                        "Let {} be a new CWhen with variantName \"{}\" and bindings {} and body {}.\n",
1930                        arm_var, vname, bindings_var, body_var
1931                    ));
1932                    output.push_str(&format!("Push {} to {}.\n", arm_var, arms_var));
1933                } else {
1934                    let body_stmts: Vec<&Stmt> = arm.body.iter().collect();
1935                    let body_var = encode_stmt_list_compact(&body_stmts, counter, output, interner, variants);
1936                    let arm_var = format!("arm_{}", *counter);
1937                    *counter += 1;
1938                    output.push_str(&format!(
1939                        "Let {} be a new COtherwise with body {}.\n",
1940                        arm_var, body_var
1941                    ));
1942                    output.push_str(&format!("Push {} to {}.\n", arm_var, arms_var));
1943                }
1944            }
1945            output.push_str(&format!(
1946                "Let {} be a new CInspect with target {} and arms {}.\n",
1947                var, target_var, arms_var
1948            ));
1949        }
1950        _ => {
1951            // Delegate to non-compact encoder for unsupported statements
1952            return encode_stmt_src(stmt, counter, output, interner, variants);
1953        }
1954    }
1955
1956    var
1957}
1958
1959fn encode_stmt_list_compact(stmts: &[&Stmt], counter: &mut usize, output: &mut String, interner: &Interner, variants: &HashMap<String, Vec<String>>) -> String {
1960    let list_var = format!("stmts_{}", *counter);
1961    *counter += 1;
1962    output.push_str(&format!("Let {} be a new Seq of CStmt.\n", list_var));
1963    for s in stmts {
1964        let sv = encode_stmt_compact(s, counter, output, interner, variants);
1965        output.push_str(&format!("Push {} to {}.\n", sv, list_var));
1966    }
1967    list_var
1968}
1969
1970fn collect_free_vars_expr<'a>(expr: &'a Expr, interner: &Interner, bound: &HashSet<String>) -> HashSet<String> {
1971    let mut free = HashSet::new();
1972    match expr {
1973        Expr::Identifier(sym) => {
1974            let name = interner.resolve(*sym).to_string();
1975            if !bound.contains(&name) {
1976                free.insert(name);
1977            }
1978        }
1979        Expr::BinaryOp { left, right, .. } => {
1980            free.extend(collect_free_vars_expr(left, interner, bound));
1981            free.extend(collect_free_vars_expr(right, interner, bound));
1982        }
1983        Expr::Not { operand } => {
1984            free.extend(collect_free_vars_expr(operand, interner, bound));
1985        }
1986        Expr::Copy { expr: inner } => {
1987            free.extend(collect_free_vars_expr(inner, interner, bound));
1988        }
1989        Expr::CallExpr { callee, args } => {
1990            free.extend(collect_free_vars_expr(callee, interner, bound));
1991            for a in args {
1992                free.extend(collect_free_vars_expr(a, interner, bound));
1993            }
1994        }
1995        Expr::Index { collection, index } => {
1996            free.extend(collect_free_vars_expr(collection, interner, bound));
1997            free.extend(collect_free_vars_expr(index, interner, bound));
1998        }
1999        Expr::InterpolatedString(parts) => {
2000            for part in parts {
2001                if let StringPart::Expr { value, .. } = part {
2002                    free.extend(collect_free_vars_expr(value, interner, bound));
2003                }
2004            }
2005        }
2006        Expr::Closure { params, body, .. } => {
2007            let mut inner_bound = bound.clone();
2008            for (sym, _) in params {
2009                inner_bound.insert(interner.resolve(*sym).to_string());
2010            }
2011            match body {
2012                ClosureBody::Expression(e) => {
2013                    free.extend(collect_free_vars_expr(e, interner, &inner_bound));
2014                }
2015                ClosureBody::Block(stmts) => {
2016                    for s in stmts.iter() {
2017                        free.extend(collect_free_vars_stmt(s, interner, &inner_bound));
2018                    }
2019                }
2020            }
2021        }
2022        _ => {}
2023    }
2024    free
2025}
2026
2027fn collect_free_vars_stmt<'a>(stmt: &'a Stmt, interner: &Interner, bound: &HashSet<String>) -> HashSet<String> {
2028    let mut free = HashSet::new();
2029    match stmt {
2030        Stmt::Let { var, value, .. } => {
2031            free.extend(collect_free_vars_expr(value, interner, bound));
2032        }
2033        Stmt::Set { target, value, .. } => {
2034            let n = interner.resolve(*target).to_string();
2035            if !bound.contains(&n) {
2036                free.insert(n);
2037            }
2038            free.extend(collect_free_vars_expr(value, interner, bound));
2039        }
2040        Stmt::Show { object, .. } => {
2041            free.extend(collect_free_vars_expr(object, interner, bound));
2042        }
2043        Stmt::Return { value } => {
2044            if let Some(v) = value {
2045                free.extend(collect_free_vars_expr(v, interner, bound));
2046            }
2047        }
2048        _ => {}
2049    }
2050    free
2051}
2052
2053fn encode_expr_src(expr: &Expr, counter: &mut usize, output: &mut String, interner: &Interner, variants: &HashMap<String, Vec<String>>) -> String {
2054    let var = format!("e_{}", *counter);
2055    *counter += 1;
2056
2057    match expr {
2058        Expr::Literal(lit) => match lit {
2059            Literal::Number(n) => {
2060                output.push_str(&format!("Let {} be a new CInt with value {}.\n", var, n));
2061            }
2062            Literal::Boolean(b) => {
2063                output.push_str(&format!("Let {} be a new CBool with value {}.\n", var, b));
2064            }
2065            Literal::Text(s) => {
2066                let text = interner.resolve(*s);
2067                output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", var, text));
2068            }
2069            Literal::Float(f) => {
2070                output.push_str(&format!("Let {} be a new CFloat with value {}.\n", var, f));
2071            }
2072            Literal::Duration(nanos) => {
2073                let millis = nanos / 1_000_000;
2074                let amount_var = format!("e_{}", *counter);
2075                *counter += 1;
2076                output.push_str(&format!("Let {} be a new CInt with value {}.\n", amount_var, millis));
2077                output.push_str(&format!("Let {} be a new CDuration with amount {} and unit \"milliseconds\".\n", var, amount_var));
2078            }
2079            Literal::Nothing => {
2080                output.push_str(&format!("Let {} be a new CText with value \"nothing\".\n", var));
2081            }
2082            _ => {
2083                output.push_str(&format!("Let {} be a new CText with value \"unsupported\".\n", var));
2084            }
2085        },
2086        Expr::Identifier(sym) => {
2087            let name = interner.resolve(*sym);
2088            output.push_str(&format!("Let {} be a new CVar with name \"{}\".\n", var, name));
2089        }
2090        Expr::BinaryOp { op, left, right } => {
2091            let left_var = encode_expr_src(left, counter, output, interner, variants);
2092            let right_var = encode_expr_src(right, counter, output, interner, variants);
2093            let op_str = match op {
2094                BinaryOpKind::Add => "+",
2095                BinaryOpKind::Subtract => "-",
2096                BinaryOpKind::Multiply => "*",
2097                BinaryOpKind::Divide => "/",
2098                BinaryOpKind::Modulo => "%",
2099                BinaryOpKind::Eq => "==",
2100                BinaryOpKind::NotEq => "!=",
2101                BinaryOpKind::Lt => "<",
2102                BinaryOpKind::Gt => ">",
2103                BinaryOpKind::LtEq => "<=",
2104                BinaryOpKind::GtEq => ">=",
2105                BinaryOpKind::And => "&&",
2106                BinaryOpKind::Or => "||",
2107                BinaryOpKind::Concat => "+",
2108                BinaryOpKind::BitXor => "^",
2109                BinaryOpKind::Shl => "<<",
2110                BinaryOpKind::Shr => ">>",
2111            };
2112            output.push_str(&format!(
2113                "Let {} be a new CBinOp with op \"{}\" and left {} and right {}.\n",
2114                var, op_str, left_var, right_var
2115            ));
2116        }
2117        Expr::Not { operand } => {
2118            let inner_var = encode_expr_src(operand, counter, output, interner, variants);
2119            output.push_str(&format!("Let {} be a new CNot with inner {}.\n", var, inner_var));
2120        }
2121        Expr::Call { function, args } => {
2122            let fn_name = interner.resolve(*function);
2123            if let Some(field_names) = variants.get(fn_name) {
2124                // Variant constructor call — encode as CNewVariant
2125                let names_var = format!("nvNames_{}", *counter);
2126                *counter += 1;
2127                output.push_str(&format!("Let {} be a new Seq of Text.\n", names_var));
2128                let vals_var = format!("nvVals_{}", *counter);
2129                *counter += 1;
2130                output.push_str(&format!("Let {} be a new Seq of CExpr.\n", vals_var));
2131                for (i, arg) in args.iter().enumerate() {
2132                    let fname = field_names.get(i).map(|s| s.as_str()).unwrap_or("value");
2133                    output.push_str(&format!("Push \"{}\" to {}.\n", fname, names_var));
2134                    let arg_var = encode_expr_src(arg, counter, output, interner, variants);
2135                    output.push_str(&format!("Push {} to {}.\n", arg_var, vals_var));
2136                }
2137                output.push_str(&format!(
2138                    "Let {} be a new CNewVariant with tag \"{}\" and fnames {} and fvals {}.\n",
2139                    var, fn_name, names_var, vals_var
2140                ));
2141            } else {
2142                let args_var = format!("callArgs_{}", *counter);
2143                *counter += 1;
2144                output.push_str(&format!("Let {} be a new Seq of CExpr.\n", args_var));
2145                for arg in args {
2146                    let arg_var = encode_expr_src(arg, counter, output, interner, variants);
2147                    output.push_str(&format!("Push {} to {}.\n", arg_var, args_var));
2148                }
2149                output.push_str(&format!(
2150                    "Let {} be a new CCall with name \"{}\" and args {}.\n",
2151                    var, fn_name, args_var
2152                ));
2153            }
2154        }
2155        Expr::Index { collection, index } => {
2156            let coll_var = encode_expr_src(collection, counter, output, interner, variants);
2157            let idx_var = encode_expr_src(index, counter, output, interner, variants);
2158            output.push_str(&format!(
2159                "Let {} be a new CIndex with coll {} and idx {}.\n",
2160                var, coll_var, idx_var
2161            ));
2162        }
2163        Expr::Length { collection } => {
2164            let coll_var = encode_expr_src(collection, counter, output, interner, variants);
2165            output.push_str(&format!("Let {} be a new CLen with target {}.\n", var, coll_var));
2166        }
2167        Expr::FieldAccess { object, field } => {
2168            let obj_var = encode_expr_src(object, counter, output, interner, variants);
2169            let field_name = interner.resolve(*field);
2170            let key_var = format!("e_{}", *counter);
2171            *counter += 1;
2172            output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", key_var, field_name));
2173            output.push_str(&format!(
2174                "Let {} be a new CMapGet with target {} and key {}.\n",
2175                var, obj_var, key_var
2176            ));
2177        }
2178        Expr::NewVariant { variant, fields, .. } => {
2179            let variant_name = interner.resolve(*variant);
2180            let names_var = format!("nvNames_{}", *counter);
2181            *counter += 1;
2182            output.push_str(&format!("Let {} be a new Seq of Text.\n", names_var));
2183            let vals_var = format!("nvVals_{}", *counter);
2184            *counter += 1;
2185            output.push_str(&format!("Let {} be a new Seq of CExpr.\n", vals_var));
2186            for (field_name, field_expr) in fields {
2187                let fname = interner.resolve(*field_name);
2188                output.push_str(&format!("Push \"{}\" to {}.\n", fname, names_var));
2189                let field_var = encode_expr_src(field_expr, counter, output, interner, variants);
2190                output.push_str(&format!("Push {} to {}.\n", field_var, vals_var));
2191            }
2192            output.push_str(&format!(
2193                "Let {} be a new CNewVariant with tag \"{}\" and fnames {} and fvals {}.\n",
2194                var, variant_name, names_var, vals_var
2195            ));
2196        }
2197        Expr::New { type_name, init_fields, .. } => {
2198            let tn = interner.resolve(*type_name);
2199            if tn == "Seq" || tn == "List" {
2200                output.push_str(&format!("Let {} be a new CNewSeq.\n", var));
2201            } else if tn == "Set" {
2202                output.push_str(&format!("Let {} be a new CNewSet.\n", var));
2203            } else if init_fields.is_empty() {
2204                let names_var = format!("nvNames_{}", *counter);
2205                *counter += 1;
2206                output.push_str(&format!("Let {} be a new Seq of Text.\n", names_var));
2207                let vals_var = format!("nvVals_{}", *counter);
2208                *counter += 1;
2209                output.push_str(&format!("Let {} be a new Seq of CExpr.\n", vals_var));
2210                output.push_str(&format!(
2211                    "Let {} be a new CNewVariant with tag \"{}\" and fnames {} and fvals {}.\n",
2212                    var, tn, names_var, vals_var
2213                ));
2214            } else {
2215                let names_var = format!("nvNames_{}", *counter);
2216                *counter += 1;
2217                output.push_str(&format!("Let {} be a new Seq of Text.\n", names_var));
2218                let vals_var = format!("nvVals_{}", *counter);
2219                *counter += 1;
2220                output.push_str(&format!("Let {} be a new Seq of CExpr.\n", vals_var));
2221                for (field_name, field_expr) in init_fields {
2222                    let fname = interner.resolve(*field_name);
2223                    output.push_str(&format!("Push \"{}\" to {}.\n", fname, names_var));
2224                    let field_var = encode_expr_src(field_expr, counter, output, interner, variants);
2225                    output.push_str(&format!("Push {} to {}.\n", field_var, vals_var));
2226                }
2227                output.push_str(&format!(
2228                    "Let {} be a new CNewVariant with tag \"{}\" and fnames {} and fvals {}.\n",
2229                    var, tn, names_var, vals_var
2230                ));
2231            }
2232        }
2233        Expr::InterpolatedString(parts) => {
2234            if parts.is_empty() {
2235                output.push_str(&format!("Let {} be a new CText with value \"\".\n", var));
2236            } else {
2237                let mut part_vars: Vec<String> = Vec::new();
2238                for part in parts {
2239                    match part {
2240                        StringPart::Literal(sym) => {
2241                            let text = interner.resolve(*sym);
2242                            let pv = format!("e_{}", *counter);
2243                            *counter += 1;
2244                            output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", pv, text));
2245                            part_vars.push(pv);
2246                        }
2247                        StringPart::Expr { value, .. } => {
2248                            let pv = encode_expr_src(value, counter, output, interner, variants);
2249                            part_vars.push(pv);
2250                        }
2251                    }
2252                }
2253                if part_vars.len() == 1 {
2254                    output.push_str(&format!("Let {} be {}.\n", var, part_vars[0]));
2255                } else {
2256                    let mut acc = part_vars[0].clone();
2257                    for pv in &part_vars[1..] {
2258                        let concat_var = format!("e_{}", *counter);
2259                        *counter += 1;
2260                        output.push_str(&format!(
2261                            "Let {} be a new CBinOp with op \"+\" and left {} and right {}.\n",
2262                            concat_var, acc, pv
2263                        ));
2264                        acc = concat_var;
2265                    }
2266                    output.push_str(&format!("Let {} be {}.\n", var, acc));
2267                }
2268            }
2269        }
2270        Expr::Range { start, end } => {
2271            let start_var = encode_expr_src(start, counter, output, interner, variants);
2272            let end_var = encode_expr_src(end, counter, output, interner, variants);
2273            output.push_str(&format!(
2274                "Let {} be a new CRange with start {} and end {}.\n",
2275                var, start_var, end_var
2276            ));
2277        }
2278        Expr::Slice { collection, start, end } => {
2279            let coll_var = encode_expr_src(collection, counter, output, interner, variants);
2280            let start_var = encode_expr_src(start, counter, output, interner, variants);
2281            let end_var = encode_expr_src(end, counter, output, interner, variants);
2282            output.push_str(&format!(
2283                "Let {} be a new CSlice with coll {} and startIdx {} and endIdx {}.\n",
2284                var, coll_var, start_var, end_var
2285            ));
2286        }
2287        Expr::Copy { expr } => {
2288            let inner_var = encode_expr_src(expr, counter, output, interner, variants);
2289            output.push_str(&format!("Let {} be a new CCopy with target {}.\n", var, inner_var));
2290        }
2291        Expr::Contains { collection, value } => {
2292            let coll_var = encode_expr_src(collection, counter, output, interner, variants);
2293            let val_var = encode_expr_src(value, counter, output, interner, variants);
2294            output.push_str(&format!(
2295                "Let {} be a new CContains with coll {} and elem {}.\n",
2296                var, coll_var, val_var
2297            ));
2298        }
2299        Expr::Union { left, right } => {
2300            let left_var = encode_expr_src(left, counter, output, interner, variants);
2301            let right_var = encode_expr_src(right, counter, output, interner, variants);
2302            output.push_str(&format!(
2303                "Let {} be a new CUnion with left {} and right {}.\n",
2304                var, left_var, right_var
2305            ));
2306        }
2307        Expr::Intersection { left, right } => {
2308            let left_var = encode_expr_src(left, counter, output, interner, variants);
2309            let right_var = encode_expr_src(right, counter, output, interner, variants);
2310            output.push_str(&format!(
2311                "Let {} be a new CIntersection with left {} and right {}.\n",
2312                var, left_var, right_var
2313            ));
2314        }
2315        Expr::OptionSome { value } => {
2316            let inner_var = encode_expr_src(value, counter, output, interner, variants);
2317            output.push_str(&format!(
2318                "Let {} be a new COptionSome with inner {}.\n",
2319                var, inner_var
2320            ));
2321        }
2322        Expr::OptionNone => {
2323            output.push_str(&format!("Let {} be a new COptionNone.\n", var));
2324        }
2325        Expr::Tuple(elems) => {
2326            let items_var = format!("tupItems_{}", *counter);
2327            *counter += 1;
2328            output.push_str(&format!("Let {} be a new Seq of CExpr.\n", items_var));
2329            for elem in elems {
2330                let elem_var = encode_expr_src(elem, counter, output, interner, variants);
2331                output.push_str(&format!("Push {} to {}.\n", elem_var, items_var));
2332            }
2333            output.push_str(&format!(
2334                "Let {} be a new CTuple with items {}.\n",
2335                var, items_var
2336            ));
2337        }
2338        Expr::Closure { params, body, .. } => {
2339            let params_var = format!("clp_{}", *counter);
2340            *counter += 1;
2341            output.push_str(&format!("Let {} be a new Seq of Text.\n", params_var));
2342            let mut param_names = HashSet::new();
2343            for (sym, _) in params {
2344                let name = interner.resolve(*sym);
2345                param_names.insert(name.to_string());
2346                output.push_str(&format!("Push \"{}\" to {}.\n", name, params_var));
2347            }
2348            let body_var = format!("clb_{}", *counter);
2349            *counter += 1;
2350            output.push_str(&format!("Let {} be a new Seq of CStmt.\n", body_var));
2351            match body {
2352                ClosureBody::Expression(e) => {
2353                    let ret_expr = encode_expr_src(e, counter, output, interner, variants);
2354                    let ret_var = format!("s_{}", *counter);
2355                    *counter += 1;
2356                    output.push_str(&format!("Let {} be a new CReturn with expr {}.\n", ret_var, ret_expr));
2357                    output.push_str(&format!("Push {} to {}.\n", ret_var, body_var));
2358                }
2359                ClosureBody::Block(stmts) => {
2360                    for s in stmts.iter() {
2361                        let sv = encode_stmt_src(s, counter, output, interner, variants);
2362                        output.push_str(&format!("Push {} to {}.\n", sv, body_var));
2363                    }
2364                }
2365            }
2366            let bound: HashSet<String> = param_names;
2367            let free = collect_free_vars_expr(expr, interner, &bound);
2368            let cap_var = format!("clc_{}", *counter);
2369            *counter += 1;
2370            output.push_str(&format!("Let {} be a new Seq of Text.\n", cap_var));
2371            for fv in &free {
2372                output.push_str(&format!("Push \"{}\" to {}.\n", fv, cap_var));
2373            }
2374            output.push_str(&format!(
2375                "Let {} be a new CClosure with params {} and body {} and captured {}.\n",
2376                var, params_var, body_var, cap_var
2377            ));
2378        }
2379        Expr::CallExpr { callee, args } => {
2380            let callee_var = encode_expr_src(callee, counter, output, interner, variants);
2381            let args_var = format!("cea_{}", *counter);
2382            *counter += 1;
2383            output.push_str(&format!("Let {} be a new Seq of CExpr.\n", args_var));
2384            for a in args {
2385                let av = encode_expr_src(a, counter, output, interner, variants);
2386                output.push_str(&format!("Push {} to {}.\n", av, args_var));
2387            }
2388            output.push_str(&format!(
2389                "Let {} be a new CCallExpr with target {} and args {}.\n",
2390                var, callee_var, args_var
2391            ));
2392        }
2393        Expr::Give { value } => {
2394            let inner_var = encode_expr_src(value, counter, output, interner, variants);
2395            output.push_str(&format!("Let {} be {}.\n", var, inner_var));
2396        }
2397        Expr::Escape { code, .. } => {
2398            let code_str = interner.resolve(*code);
2399            output.push_str(&format!(
2400                "Let {} be a new CEscExpr with code \"{}\".\n",
2401                var, code_str.replace('\"', "\\\"")
2402            ));
2403        }
2404        _ => {
2405            output.push_str(&format!("Let {} be a new CText with value \"unsupported\".\n", var));
2406        }
2407    }
2408
2409    var
2410}
2411
2412fn encode_stmt_src(stmt: &Stmt, counter: &mut usize, output: &mut String, interner: &Interner, variants: &HashMap<String, Vec<String>>) -> String {
2413    let var = format!("s_{}", *counter);
2414    *counter += 1;
2415
2416    match stmt {
2417        Stmt::Let { var: name, value, .. } => {
2418            let name_str = interner.resolve(*name);
2419            let expr_var = encode_expr_src(value, counter, output, interner, variants);
2420            output.push_str(&format!(
2421                "Let {} be a new CLet with name \"{}\" and expr {}.\n",
2422                var, name_str, expr_var
2423            ));
2424        }
2425        Stmt::Set { target, value } => {
2426            let name_str = interner.resolve(*target);
2427            let expr_var = encode_expr_src(value, counter, output, interner, variants);
2428            output.push_str(&format!(
2429                "Let {} be a new CSet with name \"{}\" and expr {}.\n",
2430                var, name_str, expr_var
2431            ));
2432        }
2433        Stmt::If { cond, then_block, else_block } => {
2434            let cond_var = encode_expr_src(cond, counter, output, interner, variants);
2435            let then_stmts: Vec<&Stmt> = then_block.iter().collect();
2436            let then_var = encode_stmt_list_src(&then_stmts, counter, output, interner, variants);
2437            let else_var = if let Some(els) = else_block {
2438                let else_stmts: Vec<&Stmt> = els.iter().collect();
2439                encode_stmt_list_src(&else_stmts, counter, output, interner, variants)
2440            } else {
2441                let empty_var = format!("emptyBlock_{}", *counter);
2442                *counter += 1;
2443                output.push_str(&format!("Let {} be a new Seq of CStmt.\n", empty_var));
2444                empty_var
2445            };
2446            output.push_str(&format!(
2447                "Let {} be a new CIf with cond {} and thenBlock {} and elseBlock {}.\n",
2448                var, cond_var, then_var, else_var
2449            ));
2450        }
2451        Stmt::While { cond, body, .. } => {
2452            let cond_var = encode_expr_src(cond, counter, output, interner, variants);
2453            let body_stmts: Vec<&Stmt> = body.iter().collect();
2454            let body_var = encode_stmt_list_src(&body_stmts, counter, output, interner, variants);
2455            output.push_str(&format!(
2456                "Let {} be a new CWhile with cond {} and body {}.\n",
2457                var, cond_var, body_var
2458            ));
2459        }
2460        Stmt::Return { value } => {
2461            if let Some(expr) = value {
2462                let expr_var = encode_expr_src(expr, counter, output, interner, variants);
2463                output.push_str(&format!("Let {} be a new CReturn with expr {}.\n", var, expr_var));
2464            } else {
2465                let nothing_var = format!("e_{}", *counter);
2466                *counter += 1;
2467                output.push_str(&format!("Let {} be a new CInt with value 0.\n", nothing_var));
2468                output.push_str(&format!("Let {} be a new CReturn with expr {}.\n", var, nothing_var));
2469            }
2470        }
2471        Stmt::Show { object, .. } => {
2472            let expr_var = encode_expr_src(object, counter, output, interner, variants);
2473            output.push_str(&format!("Let {} be a new CShow with expr {}.\n", var, expr_var));
2474        }
2475        Stmt::Call { function, args } => {
2476            let fn_name = interner.resolve(*function);
2477            let args_var = format!("callSArgs_{}", *counter);
2478            *counter += 1;
2479            output.push_str(&format!("Let {} be a new Seq of CExpr.\n", args_var));
2480            for arg in args {
2481                let arg_var = encode_expr_src(arg, counter, output, interner, variants);
2482                output.push_str(&format!("Push {} to {}.\n", arg_var, args_var));
2483            }
2484            output.push_str(&format!(
2485                "Let {} be a new CCallS with name \"{}\" and args {}.\n",
2486                var, fn_name, args_var
2487            ));
2488        }
2489        Stmt::Push { value, collection } => {
2490            let val_var = encode_expr_src(value, counter, output, interner, variants);
2491            let coll_name = extract_ident_name(collection, interner);
2492            output.push_str(&format!(
2493                "Let {} be a new CPush with expr {} and target \"{}\".\n",
2494                var, val_var, coll_name
2495            ));
2496        }
2497        Stmt::SetIndex { collection, index, value } => {
2498            let coll_name = extract_ident_name(collection, interner);
2499            let idx_var = encode_expr_src(index, counter, output, interner, variants);
2500            let val_var = encode_expr_src(value, counter, output, interner, variants);
2501            output.push_str(&format!(
2502                "Let {} be a new CSetIdx with target \"{}\" and idx {} and val {}.\n",
2503                var, coll_name, idx_var, val_var
2504            ));
2505        }
2506        Stmt::SetField { object, field, value } => {
2507            let map_name = extract_ident_name(object, interner);
2508            let field_name = interner.resolve(*field);
2509            let key_var = format!("e_{}", *counter);
2510            *counter += 1;
2511            output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", key_var, field_name));
2512            let val_var = encode_expr_src(value, counter, output, interner, variants);
2513            output.push_str(&format!(
2514                "Let {} be a new CMapSet with target \"{}\" and key {} and val {}.\n",
2515                var, map_name, key_var, val_var
2516            ));
2517        }
2518        Stmt::Pop { collection, .. } => {
2519            let coll_name = extract_ident_name(collection, interner);
2520            output.push_str(&format!(
2521                "Let {} be a new CPop with target \"{}\".\n",
2522                var, coll_name
2523            ));
2524        }
2525        Stmt::Add { value, collection } => {
2526            let val_var = encode_expr_src(value, counter, output, interner, variants);
2527            let coll_name = extract_ident_name(collection, interner);
2528            output.push_str(&format!(
2529                "Let {} be a new CAdd with elem {} and target \"{}\".\n",
2530                var, val_var, coll_name
2531            ));
2532        }
2533        Stmt::Remove { value, collection } => {
2534            let val_var = encode_expr_src(value, counter, output, interner, variants);
2535            let coll_name = extract_ident_name(collection, interner);
2536            output.push_str(&format!(
2537                "Let {} be a new CRemove with elem {} and target \"{}\".\n",
2538                var, val_var, coll_name
2539            ));
2540        }
2541        Stmt::Inspect { .. } => {
2542            return String::new(); // Handled by encode_stmts_src
2543        }
2544        Stmt::Repeat { .. } => {
2545            return String::new(); // Handled by encode_stmts_src
2546        }
2547        Stmt::Break => {
2548            output.push_str(&format!("Let {} be a new CBreak.\n", var));
2549        }
2550        Stmt::RuntimeAssert { condition, .. } => {
2551            let cond_var = encode_expr_src(condition, counter, output, interner, variants);
2552            let msg_var = format!("e_{}", *counter);
2553            *counter += 1;
2554            output.push_str(&format!("Let {} be a new CText with value \"assertion failed\".\n", msg_var));
2555            output.push_str(&format!(
2556                "Let {} be a new CRuntimeAssert with cond {} and msg {}.\n",
2557                var, cond_var, msg_var
2558            ));
2559        }
2560        Stmt::Give { object, recipient } => {
2561            let expr_var = encode_expr_src(object, counter, output, interner, variants);
2562            let target_name = extract_ident_name(recipient, interner);
2563            output.push_str(&format!(
2564                "Let {} be a new CGive with expr {} and target \"{}\".\n",
2565                var, expr_var, target_name
2566            ));
2567        }
2568        Stmt::Escape { code, .. } => {
2569            let code_str = interner.resolve(*code);
2570            output.push_str(&format!(
2571                "Let {} be a new CEscStmt with code \"{}\".\n",
2572                var, code_str.replace('\"', "\\\"")
2573            ));
2574        }
2575        Stmt::Sleep { milliseconds } => {
2576            let dur_var = encode_expr_src(milliseconds, counter, output, interner, variants);
2577            output.push_str(&format!(
2578                "Let {} be a new CSleep with duration {}.\n",
2579                var, dur_var
2580            ));
2581        }
2582        Stmt::ReadFrom { var: read_var, source } => {
2583            let var_name = interner.resolve(*read_var);
2584            match source {
2585                ReadSource::Console => {
2586                    output.push_str(&format!(
2587                        "Let {} be a new CReadConsole with target \"{}\".\n",
2588                        var, var_name
2589                    ));
2590                }
2591                ReadSource::File(path_expr) => {
2592                    let path_var = encode_expr_src(path_expr, counter, output, interner, variants);
2593                    output.push_str(&format!(
2594                        "Let {} be a new CReadFile with path {} and target \"{}\".\n",
2595                        var, path_var, var_name
2596                    ));
2597                }
2598            }
2599        }
2600        Stmt::WriteFile { content, path } => {
2601            let path_var = encode_expr_src(path, counter, output, interner, variants);
2602            let content_var = encode_expr_src(content, counter, output, interner, variants);
2603            output.push_str(&format!(
2604                "Let {} be a new CWriteFile with path {} and content {}.\n",
2605                var, path_var, content_var
2606            ));
2607        }
2608        Stmt::Check { source_text, .. } => {
2609            let pred_var = format!("e_{}", *counter);
2610            *counter += 1;
2611            output.push_str(&format!("Let {} be a new CBool with value true.\n", pred_var));
2612            let msg_var = format!("e_{}", *counter);
2613            *counter += 1;
2614            output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", msg_var, source_text.replace('\"', "\\\"")));
2615            output.push_str(&format!(
2616                "Let {} be a new CCheck with predicate {} and msg {}.\n",
2617                var, pred_var, msg_var
2618            ));
2619        }
2620        Stmt::Assert { .. } => {
2621            let prop_var = format!("e_{}", *counter);
2622            *counter += 1;
2623            output.push_str(&format!("Let {} be a new CBool with value true.\n", prop_var));
2624            output.push_str(&format!(
2625                "Let {} be a new CAssert with proposition {}.\n",
2626                var, prop_var
2627            ));
2628        }
2629        Stmt::Trust { justification, .. } => {
2630            let prop_var = format!("e_{}", *counter);
2631            *counter += 1;
2632            output.push_str(&format!("Let {} be a new CBool with value true.\n", prop_var));
2633            let just_str = interner.resolve(*justification);
2634            output.push_str(&format!(
2635                "Let {} be a new CTrust with proposition {} and justification \"{}\".\n",
2636                var, prop_var, just_str
2637            ));
2638        }
2639        Stmt::Require { crate_name, .. } => {
2640            let dep_name = interner.resolve(*crate_name);
2641            output.push_str(&format!(
2642                "Let {} be a new CRequire with dependency \"{}\".\n",
2643                var, dep_name
2644            ));
2645        }
2646        Stmt::MergeCrdt { source, target } => {
2647            let source_var = encode_expr_src(source, counter, output, interner, variants);
2648            let target_name = match target {
2649                Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2650                _ => "unknown".to_string(),
2651            };
2652            output.push_str(&format!(
2653                "Let {} be a new CMerge with target \"{}\" and other {}.\n",
2654                var, target_name, source_var
2655            ));
2656        }
2657        Stmt::IncreaseCrdt { object, field, amount } => {
2658            let amount_var = encode_expr_src(amount, counter, output, interner, variants);
2659            let target_name = match object {
2660                Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2661                _ => "unknown".to_string(),
2662            };
2663            output.push_str(&format!(
2664                "Let {} be a new CIncrease with target \"{}\" and amount {}.\n",
2665                var, target_name, amount_var
2666            ));
2667        }
2668        Stmt::DecreaseCrdt { object, field, amount } => {
2669            let amount_var = encode_expr_src(amount, counter, output, interner, variants);
2670            let target_name = match object {
2671                Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2672                _ => "unknown".to_string(),
2673            };
2674            output.push_str(&format!(
2675                "Let {} be a new CDecrease with target \"{}\" and amount {}.\n",
2676                var, target_name, amount_var
2677            ));
2678        }
2679        Stmt::AppendToSequence { sequence, value } => {
2680            let value_var = encode_expr_src(value, counter, output, interner, variants);
2681            let target_name = match sequence {
2682                Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2683                _ => "unknown".to_string(),
2684            };
2685            output.push_str(&format!(
2686                "Let {} be a new CAppendToSeq with target \"{}\" and value {}.\n",
2687                var, target_name, value_var
2688            ));
2689        }
2690        Stmt::ResolveConflict { object, .. } => {
2691            let target_name = match object {
2692                Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2693                _ => "unknown".to_string(),
2694            };
2695            output.push_str(&format!(
2696                "Let {} be a new CResolve with target \"{}\".\n",
2697                var, target_name
2698            ));
2699        }
2700        Stmt::Sync { var: sync_var, topic } => {
2701            let topic_var = encode_expr_src(topic, counter, output, interner, variants);
2702            let var_name = interner.resolve(*sync_var);
2703            output.push_str(&format!(
2704                "Let {} be a new CSync with target \"{}\" and channel {}.\n",
2705                var, var_name, topic_var
2706            ));
2707        }
2708        Stmt::Mount { var: mount_var, path } => {
2709            let path_var = encode_expr_src(path, counter, output, interner, variants);
2710            let var_name = interner.resolve(*mount_var);
2711            output.push_str(&format!(
2712                "Let {} be a new CMount with target \"{}\" and path {}.\n",
2713                var, var_name, path_var
2714            ));
2715        }
2716        Stmt::Concurrent { tasks } => {
2717            let branches_var = format!("e_{}", *counter);
2718            *counter += 1;
2719            output.push_str(&format!("Let {} be a new Seq of Seq of CStmt.\n", branches_var));
2720            let branch_var = format!("e_{}", *counter);
2721            *counter += 1;
2722            output.push_str(&format!("Let {} be a new Seq of CStmt.\n", branch_var));
2723            for stmt in tasks.iter() {
2724                let sv = encode_stmt_src(stmt, counter, output, interner, variants);
2725                if !sv.is_empty() {
2726                    output.push_str(&format!("Push {} to {}.\n", sv, branch_var));
2727                }
2728            }
2729            output.push_str(&format!("Push {} to {}.\n", branch_var, branches_var));
2730            output.push_str(&format!(
2731                "Let {} be a new CConcurrent with branches {}.\n",
2732                var, branches_var
2733            ));
2734        }
2735        Stmt::Parallel { tasks } => {
2736            let branches_var = format!("e_{}", *counter);
2737            *counter += 1;
2738            output.push_str(&format!("Let {} be a new Seq of Seq of CStmt.\n", branches_var));
2739            let branch_var = format!("e_{}", *counter);
2740            *counter += 1;
2741            output.push_str(&format!("Let {} be a new Seq of CStmt.\n", branch_var));
2742            for stmt in tasks.iter() {
2743                let sv = encode_stmt_src(stmt, counter, output, interner, variants);
2744                if !sv.is_empty() {
2745                    output.push_str(&format!("Push {} to {}.\n", sv, branch_var));
2746                }
2747            }
2748            output.push_str(&format!("Push {} to {}.\n", branch_var, branches_var));
2749            output.push_str(&format!(
2750                "Let {} be a new CParallel with branches {}.\n",
2751                var, branches_var
2752            ));
2753        }
2754        Stmt::LaunchTask { function, args } | Stmt::LaunchTaskWithHandle { function, args, .. } => {
2755            let func_name = interner.resolve(*function);
2756            let args_var = format!("e_{}", *counter);
2757            *counter += 1;
2758            output.push_str(&format!("Let {} be a new Seq of CExpr.\n", args_var));
2759            for arg in args {
2760                let av = encode_expr_src(arg, counter, output, interner, variants);
2761                output.push_str(&format!("Push {} to {}.\n", av, args_var));
2762            }
2763            let body_var = format!("e_{}", *counter);
2764            *counter += 1;
2765            output.push_str(&format!("Let {} be a new Seq of CStmt.\n", body_var));
2766            let call_var = format!("e_{}", *counter);
2767            *counter += 1;
2768            output.push_str(&format!(
2769                "Let {} be a new CCallS with name \"{}\" and args {}.\n",
2770                call_var, func_name, args_var
2771            ));
2772            output.push_str(&format!("Push {} to {}.\n", call_var, body_var));
2773            let handle_name = if let Stmt::LaunchTaskWithHandle { handle, .. } = stmt {
2774                interner.resolve(*handle).to_string()
2775            } else {
2776                "_task".to_string()
2777            };
2778            output.push_str(&format!(
2779                "Let {} be a new CLaunchTask with body {} and handle \"{}\".\n",
2780                var, body_var, handle_name
2781            ));
2782        }
2783        Stmt::StopTask { handle } => {
2784            let handle_var = encode_expr_src(handle, counter, output, interner, variants);
2785            output.push_str(&format!(
2786                "Let {} be a new CStopTask with handle {}.\n",
2787                var, handle_var
2788            ));
2789        }
2790        Stmt::CreatePipe { var: pipe_var, capacity, .. } => {
2791            let cap = capacity.unwrap_or(32);
2792            let cap_var = format!("e_{}", *counter);
2793            *counter += 1;
2794            output.push_str(&format!("Let {} be a new CInt with value {}.\n", cap_var, cap));
2795            let pipe_name = interner.resolve(*pipe_var);
2796            output.push_str(&format!(
2797                "Let {} be a new CCreatePipe with name \"{}\" and capacity {}.\n",
2798                var, pipe_name, cap_var
2799            ));
2800        }
2801        Stmt::SendPipe { value, pipe } => {
2802            let val_var = encode_expr_src(value, counter, output, interner, variants);
2803            let pipe_name = match pipe {
2804                Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2805                _ => "pipe".to_string(),
2806            };
2807            output.push_str(&format!(
2808                "Let {} be a new CSendPipe with chan \"{}\" and value {}.\n",
2809                var, pipe_name, val_var
2810            ));
2811        }
2812        Stmt::ReceivePipe { var: recv_var, pipe } => {
2813            let recv_name = interner.resolve(*recv_var);
2814            let pipe_name = match pipe {
2815                Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2816                _ => "pipe".to_string(),
2817            };
2818            output.push_str(&format!(
2819                "Let {} be a new CReceivePipe with chan \"{}\" and target \"{}\".\n",
2820                var, pipe_name, recv_name
2821            ));
2822        }
2823        Stmt::TrySendPipe { value, pipe, .. } => {
2824            let val_var = encode_expr_src(value, counter, output, interner, variants);
2825            let pipe_name = match pipe {
2826                Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2827                _ => "pipe".to_string(),
2828            };
2829            output.push_str(&format!(
2830                "Let {} be a new CTrySendPipe with chan \"{}\" and value {}.\n",
2831                var, pipe_name, val_var
2832            ));
2833        }
2834        Stmt::TryReceivePipe { var: recv_var, pipe } => {
2835            let recv_name = interner.resolve(*recv_var);
2836            let pipe_name = match pipe {
2837                Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2838                _ => "pipe".to_string(),
2839            };
2840            output.push_str(&format!(
2841                "Let {} be a new CTryReceivePipe with chan \"{}\" and target \"{}\".\n",
2842                var, pipe_name, recv_name
2843            ));
2844        }
2845        Stmt::Select { branches } => {
2846            let branches_var = format!("e_{}", *counter);
2847            *counter += 1;
2848            output.push_str(&format!("Let {} be a new Seq of CSelectBranch.\n", branches_var));
2849            for branch in branches {
2850                match branch {
2851                    SelectBranch::Receive { var: recv_var, pipe, body } => {
2852                        let recv_name = interner.resolve(*recv_var);
2853                        let pipe_name = match pipe {
2854                            Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
2855                            _ => "pipe".to_string(),
2856                        };
2857                        let body_var = format!("e_{}", *counter);
2858                        *counter += 1;
2859                        output.push_str(&format!("Let {} be a new Seq of CStmt.\n", body_var));
2860                        for stmt in body.iter() {
2861                            let sv = encode_stmt_src(stmt, counter, output, interner, variants);
2862                            if !sv.is_empty() {
2863                                output.push_str(&format!("Push {} to {}.\n", sv, body_var));
2864                            }
2865                        }
2866                        let branch_var = format!("e_{}", *counter);
2867                        *counter += 1;
2868                        output.push_str(&format!(
2869                            "Let {} be a new CSelectRecv with chan \"{}\" and var \"{}\" and body {}.\n",
2870                            branch_var, pipe_name, recv_name, body_var
2871                        ));
2872                        output.push_str(&format!("Push {} to {}.\n", branch_var, branches_var));
2873                    }
2874                    SelectBranch::Timeout { milliseconds, body } => {
2875                        let dur_var = encode_expr_src(milliseconds, counter, output, interner, variants);
2876                        let body_var = format!("e_{}", *counter);
2877                        *counter += 1;
2878                        output.push_str(&format!("Let {} be a new Seq of CStmt.\n", body_var));
2879                        for stmt in body.iter() {
2880                            let sv = encode_stmt_src(stmt, counter, output, interner, variants);
2881                            if !sv.is_empty() {
2882                                output.push_str(&format!("Push {} to {}.\n", sv, body_var));
2883                            }
2884                        }
2885                        let branch_var = format!("e_{}", *counter);
2886                        *counter += 1;
2887                        output.push_str(&format!(
2888                            "Let {} be a new CSelectTimeout with duration {} and body {}.\n",
2889                            branch_var, dur_var, body_var
2890                        ));
2891                        output.push_str(&format!("Push {} to {}.\n", branch_var, branches_var));
2892                    }
2893                }
2894            }
2895            output.push_str(&format!(
2896                "Let {} be a new CSelect with branches {}.\n",
2897                var, branches_var
2898            ));
2899        }
2900        Stmt::Spawn { agent_type, name } => {
2901            let agent_name = interner.resolve(*agent_type);
2902            let target_name = interner.resolve(*name);
2903            output.push_str(&format!(
2904                "Let {} be a new CSpawn with agentType \"{}\" and target \"{}\".\n",
2905                var, agent_name, target_name
2906            ));
2907        }
2908        Stmt::SendMessage { message, destination } => {
2909            let target_var = encode_expr_src(destination, counter, output, interner, variants);
2910            let msg_var = encode_expr_src(message, counter, output, interner, variants);
2911            output.push_str(&format!(
2912                "Let {} be a new CSendMessage with target {} and msg {}.\n",
2913                var, target_var, msg_var
2914            ));
2915        }
2916        Stmt::AwaitMessage { into, .. } => {
2917            let await_name = interner.resolve(*into);
2918            output.push_str(&format!(
2919                "Let {} be a new CAwaitMessage with target \"{}\".\n",
2920                var, await_name
2921            ));
2922        }
2923        Stmt::Listen { address } => {
2924            let addr_var = encode_expr_src(address, counter, output, interner, variants);
2925            output.push_str(&format!(
2926                "Let {} be a new CListen with addr {} and handler \"default\".\n",
2927                var, addr_var
2928            ));
2929        }
2930        Stmt::ConnectTo { address } => {
2931            let addr_var = encode_expr_src(address, counter, output, interner, variants);
2932            output.push_str(&format!(
2933                "Let {} be a new CConnectTo with addr {} and target \"conn\".\n",
2934                var, addr_var
2935            ));
2936        }
2937        Stmt::Zone { name, body, .. } => {
2938            let zone_name = interner.resolve(*name);
2939            let body_var = format!("e_{}", *counter);
2940            *counter += 1;
2941            output.push_str(&format!("Let {} be a new Seq of CStmt.\n", body_var));
2942            for stmt in body.iter() {
2943                let sv = encode_stmt_src(stmt, counter, output, interner, variants);
2944                if !sv.is_empty() {
2945                    output.push_str(&format!("Push {} to {}.\n", sv, body_var));
2946                }
2947            }
2948            output.push_str(&format!(
2949                "Let {} be a new CZone with name \"{}\" and kind \"heap\" and body {}.\n",
2950                var, zone_name, body_var
2951            ));
2952        }
2953        Stmt::LetPeerAgent { var: pa_var, address } => {
2954            let addr_var = encode_expr_src(address, counter, output, interner, variants);
2955            let pa_name = interner.resolve(*pa_var);
2956            output.push_str(&format!(
2957                "Let {} be a new CConnectTo with addr {} and target \"{}\".\n",
2958                var, addr_var, pa_name
2959            ));
2960        }
2961        _ => {
2962            return String::new();
2963        }
2964    }
2965
2966    var
2967}
2968
2969fn encode_stmts_src(stmt: &Stmt, counter: &mut usize, output: &mut String, interner: &Interner, variants: &HashMap<String, Vec<String>>) -> Vec<String> {
2970    match stmt {
2971        Stmt::Inspect { target, arms, .. } => {
2972            let mut otherwise_stmts: Vec<&Stmt> = Vec::new();
2973            let mut variant_arms: Vec<(&MatchArm, Vec<&Stmt>)> = Vec::new();
2974
2975            for arm in arms {
2976                if arm.variant.is_none() {
2977                    otherwise_stmts = arm.body.iter().collect();
2978                } else {
2979                    let body_refs: Vec<&Stmt> = arm.body.iter().collect();
2980                    variant_arms.push((arm, body_refs));
2981                }
2982            }
2983
2984            if variant_arms.is_empty() {
2985                let mut result = Vec::new();
2986                for s in &otherwise_stmts {
2987                    for v in encode_stmts_src(s, counter, output, interner, variants) {
2988                        result.push(v);
2989                    }
2990                }
2991                return result;
2992            }
2993
2994            // Flat CIf encoding: each arm becomes an independent CIf with empty else.
2995            // Since Inspect arms are mutually exclusive (exactly one tag matches),
2996            // flat CIf is semantically equivalent to nested CIf chains but avoids
2997            // deep nesting that the interpreter's inline CIf handler can't navigate.
2998            let has_otherwise = !otherwise_stmts.is_empty();
2999            let mut result = Vec::new();
3000
3001            // If there's an Otherwise block, track whether any arm matched
3002            let matched_var_name = if has_otherwise {
3003                let name = format!("__inspectMatched_{}", *counter);
3004                *counter += 1;
3005                let false_expr = format!("e_{}", *counter);
3006                *counter += 1;
3007                output.push_str(&format!("Let {} be a new CBool with value false.\n", false_expr));
3008                let let_stmt = format!("s_{}", *counter);
3009                *counter += 1;
3010                output.push_str(&format!(
3011                    "Let {} be a new CLet with name \"{}\" and expr {}.\n",
3012                    let_stmt, name, false_expr
3013                ));
3014                result.push(let_stmt);
3015                Some(name)
3016            } else {
3017                None
3018            };
3019
3020            // Each variant arm becomes: CIf(tag == "Variant", [bindings + body], [])
3021            for (arm, body_stmts) in &variant_arms {
3022                let variant_name = interner.resolve(arm.variant.unwrap());
3023
3024                // Condition: tag == "VariantName"
3025                let tag_target = encode_expr_src(target, counter, output, interner, variants);
3026                let tag_key = format!("e_{}", *counter);
3027                *counter += 1;
3028                output.push_str(&format!("Let {} be a new CText with value \"__tag\".\n", tag_key));
3029                let tag_get = format!("e_{}", *counter);
3030                *counter += 1;
3031                output.push_str(&format!(
3032                    "Let {} be a new CMapGet with target {} and key {}.\n",
3033                    tag_get, tag_target, tag_key
3034                ));
3035                let variant_text = format!("e_{}", *counter);
3036                *counter += 1;
3037                output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", variant_text, variant_name));
3038                let cond_var = format!("e_{}", *counter);
3039                *counter += 1;
3040                output.push_str(&format!(
3041                    "Let {} be a new CBinOp with op \"==\" and left {} and right {}.\n",
3042                    cond_var, tag_get, variant_text
3043                ));
3044
3045                // Then-block: [optionally set matched flag, bindings, body]
3046                let then_list = format!("stmtList_{}", *counter);
3047                *counter += 1;
3048                output.push_str(&format!("Let {} be a new Seq of CStmt.\n", then_list));
3049
3050                // Set matched flag if needed
3051                if let Some(ref mname) = matched_var_name {
3052                    let true_expr = format!("e_{}", *counter);
3053                    *counter += 1;
3054                    output.push_str(&format!("Let {} be a new CBool with value true.\n", true_expr));
3055                    let set_stmt = format!("s_{}", *counter);
3056                    *counter += 1;
3057                    output.push_str(&format!(
3058                        "Let {} be a new CSet with name \"{}\" and expr {}.\n",
3059                        set_stmt, mname, true_expr
3060                    ));
3061                    output.push_str(&format!("Push {} to {}.\n", set_stmt, then_list));
3062                }
3063
3064                // Bindings
3065                for (field_name, binding_name) in &arm.bindings {
3066                    let field_str = interner.resolve(*field_name);
3067                    let bind_str = interner.resolve(*binding_name);
3068                    let bind_target = encode_expr_src(target, counter, output, interner, variants);
3069                    let fkey = format!("e_{}", *counter);
3070                    *counter += 1;
3071                    output.push_str(&format!("Let {} be a new CText with value \"{}\".\n", fkey, field_str));
3072                    let fget = format!("e_{}", *counter);
3073                    *counter += 1;
3074                    output.push_str(&format!(
3075                        "Let {} be a new CMapGet with target {} and key {}.\n",
3076                        fget, bind_target, fkey
3077                    ));
3078                    let bind_let = format!("s_{}", *counter);
3079                    *counter += 1;
3080                    output.push_str(&format!(
3081                        "Let {} be a new CLet with name \"{}\" and expr {}.\n",
3082                        bind_let, bind_str, fget
3083                    ));
3084                    output.push_str(&format!("Push {} to {}.\n", bind_let, then_list));
3085                }
3086
3087                // Body statements (use encode_stmts_src for Inspect/Repeat)
3088                for body_stmt in body_stmts {
3089                    match body_stmt {
3090                        Stmt::Inspect { .. } | Stmt::Repeat { .. } => {
3091                            let vars = encode_stmts_src(body_stmt, counter, output, interner, variants);
3092                            for v in vars {
3093                                output.push_str(&format!("Push {} to {}.\n", v, then_list));
3094                            }
3095                        }
3096                        _ => {
3097                            let bvar = encode_stmt_src(body_stmt, counter, output, interner, variants);
3098                            if !bvar.is_empty() {
3099                                output.push_str(&format!("Push {} to {}.\n", bvar, then_list));
3100                            }
3101                        }
3102                    }
3103                }
3104
3105                // Empty else block
3106                let empty_else = format!("stmtList_{}", *counter);
3107                *counter += 1;
3108                output.push_str(&format!("Let {} be a new Seq of CStmt.\n", empty_else));
3109
3110                // CIf node
3111                let if_var = format!("s_{}", *counter);
3112                *counter += 1;
3113                output.push_str(&format!(
3114                    "Let {} be a new CIf with cond {} and thenBlock {} and elseBlock {}.\n",
3115                    if_var, cond_var, then_list, empty_else
3116                ));
3117
3118                result.push(if_var);
3119            }
3120
3121            // Otherwise: CIf(CNot(__inspectMatched), otherwise_body, [])
3122            if let Some(ref mname) = matched_var_name {
3123                let matched_ref = format!("e_{}", *counter);
3124                *counter += 1;
3125                output.push_str(&format!("Let {} be a new CVar with name \"{}\".\n", matched_ref, mname));
3126                let not_matched = format!("e_{}", *counter);
3127                *counter += 1;
3128                output.push_str(&format!("Let {} be a new CNot with inner {}.\n", not_matched, matched_ref));
3129
3130                let otherwise_block = encode_stmt_list_src(&otherwise_stmts, counter, output, interner, variants);
3131                let empty_else = format!("stmtList_{}", *counter);
3132                *counter += 1;
3133                output.push_str(&format!("Let {} be a new Seq of CStmt.\n", empty_else));
3134
3135                let otherwise_if = format!("s_{}", *counter);
3136                *counter += 1;
3137                output.push_str(&format!(
3138                    "Let {} be a new CIf with cond {} and thenBlock {} and elseBlock {}.\n",
3139                    otherwise_if, not_matched, otherwise_block, empty_else
3140                ));
3141                result.push(otherwise_if);
3142            }
3143
3144            result
3145        }
3146        Stmt::Repeat { pattern, iterable, body, .. } => {
3147            // Lower to: Let idx = CInt(1). CWhile(idx <= CLen(coll), [Let var = CIndex(coll, idx), ...body..., Set idx = idx + 1])
3148            let loop_var_name = match pattern {
3149                Pattern::Identifier(sym) => interner.resolve(*sym).to_string(),
3150                Pattern::Tuple(syms) => {
3151                    if let Some(s) = syms.first() {
3152                        interner.resolve(*s).to_string()
3153                    } else {
3154                        "item".to_string()
3155                    }
3156                }
3157            };
3158
3159            // Generate a unique index variable name
3160            let idx_name = format!("__idx_{}", *counter);
3161            *counter += 1;
3162
3163            // Let idx = CInt(1) — 1-based indexing
3164            let idx_init_expr = format!("e_{}", *counter);
3165            *counter += 1;
3166            output.push_str(&format!("Let {} be a new CInt with value 1.\n", idx_init_expr));
3167            let idx_init = format!("s_{}", *counter);
3168            *counter += 1;
3169            output.push_str(&format!(
3170                "Let {} be a new CLet with name \"{}\" and expr {}.\n",
3171                idx_init, idx_name, idx_init_expr
3172            ));
3173
3174            // While condition: idx <= length of coll (fresh iterable encoding)
3175            let iter_for_len = encode_expr_src(iterable, counter, output, interner, variants);
3176            let idx_var_expr = format!("e_{}", *counter);
3177            *counter += 1;
3178            output.push_str(&format!("Let {} be a new CVar with name \"{}\".\n", idx_var_expr, idx_name));
3179            let len_expr = format!("e_{}", *counter);
3180            *counter += 1;
3181            output.push_str(&format!("Let {} be a new CLen with target {}.\n", len_expr, iter_for_len));
3182            let while_cond = format!("e_{}", *counter);
3183            *counter += 1;
3184            output.push_str(&format!(
3185                "Let {} be a new CBinOp with op \"<=\" and left {} and right {}.\n",
3186                while_cond, idx_var_expr, len_expr
3187            ));
3188
3189            // While body
3190            let while_body_list = format!("stmtList_{}", *counter);
3191            *counter += 1;
3192            output.push_str(&format!("Let {} be a new Seq of CStmt.\n", while_body_list));
3193
3194            // Let loop_var = CIndex(coll, idx) (fresh iterable encoding)
3195            let iter_for_index = encode_expr_src(iterable, counter, output, interner, variants);
3196            let idx_ref = format!("e_{}", *counter);
3197            *counter += 1;
3198            output.push_str(&format!("Let {} be a new CVar with name \"{}\".\n", idx_ref, idx_name));
3199            let elem_expr = format!("e_{}", *counter);
3200            *counter += 1;
3201            output.push_str(&format!(
3202                "Let {} be a new CIndex with coll {} and idx {}.\n",
3203                elem_expr, iter_for_index, idx_ref
3204            ));
3205            let elem_let = format!("s_{}", *counter);
3206            *counter += 1;
3207            output.push_str(&format!(
3208                "Let {} be a new CLet with name \"{}\" and expr {}.\n",
3209                elem_let, loop_var_name, elem_expr
3210            ));
3211            output.push_str(&format!("Push {} to {}.\n", elem_let, while_body_list));
3212
3213            // Encode body statements (use encode_stmts_src for Inspect/Repeat, encode_stmt_src for others)
3214            let body_refs: Vec<&Stmt> = body.iter().collect();
3215            for body_stmt in &body_refs {
3216                match body_stmt {
3217                    Stmt::Inspect { .. } | Stmt::Repeat { .. } => {
3218                        let vars = encode_stmts_src(body_stmt, counter, output, interner, variants);
3219                        for v in vars {
3220                            output.push_str(&format!("Push {} to {}.\n", v, while_body_list));
3221                        }
3222                    }
3223                    _ => {
3224                        let bvar = encode_stmt_src(body_stmt, counter, output, interner, variants);
3225                        if !bvar.is_empty() {
3226                            output.push_str(&format!("Push {} to {}.\n", bvar, while_body_list));
3227                        }
3228                    }
3229                }
3230            }
3231
3232            // Set idx = idx + 1
3233            let idx_ref2 = format!("e_{}", *counter);
3234            *counter += 1;
3235            output.push_str(&format!("Let {} be a new CVar with name \"{}\".\n", idx_ref2, idx_name));
3236            let one_expr = format!("e_{}", *counter);
3237            *counter += 1;
3238            output.push_str(&format!("Let {} be a new CInt with value 1.\n", one_expr));
3239            let inc_expr = format!("e_{}", *counter);
3240            *counter += 1;
3241            output.push_str(&format!(
3242                "Let {} be a new CBinOp with op \"+\" and left {} and right {}.\n",
3243                inc_expr, idx_ref2, one_expr
3244            ));
3245            let inc_set = format!("s_{}", *counter);
3246            *counter += 1;
3247            output.push_str(&format!(
3248                "Let {} be a new CSet with name \"{}\" and expr {}.\n",
3249                inc_set, idx_name, inc_expr
3250            ));
3251            output.push_str(&format!("Push {} to {}.\n", inc_set, while_body_list));
3252
3253            // CWhile node
3254            let while_var = format!("s_{}", *counter);
3255            *counter += 1;
3256            output.push_str(&format!(
3257                "Let {} be a new CWhile with cond {} and body {}.\n",
3258                while_var, while_cond, while_body_list
3259            ));
3260
3261            vec![idx_init, while_var]
3262        }
3263        _ => {
3264            let v = encode_stmt_src(stmt, counter, output, interner, variants);
3265            if v.is_empty() {
3266                vec![]
3267            } else {
3268                vec![v]
3269            }
3270        }
3271    }
3272}
3273
3274fn encode_stmt_list_src(stmts: &[&Stmt], counter: &mut usize, output: &mut String, interner: &Interner, variants: &HashMap<String, Vec<String>>) -> String {
3275    let list_var = format!("stmtList_{}", *counter);
3276    *counter += 1;
3277    output.push_str(&format!("Let {} be a new Seq of CStmt.\n", list_var));
3278
3279    for stmt in stmts {
3280        for stmt_var in encode_stmts_src(stmt, counter, output, interner, variants) {
3281            output.push_str(&format!("Push {} to {}.\n", stmt_var, list_var));
3282        }
3283    }
3284
3285    list_var
3286}
3287
3288fn extract_ident_name(expr: &Expr, interner: &Interner) -> String {
3289    match expr {
3290        Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
3291        _ => "unknown".to_string(),
3292    }
3293}
3294
3295/// First Futamura Projection: PE(interpreter, program) = compiled_program
3296///
3297/// Specializes the interpreter with respect to a fixed program, producing
3298/// a compiled version with no interpretive overhead. For a self-interpreter
3299/// (where source and target language are the same), this produces the
3300/// program itself, with static optimizations applied.
3301///
3302/// The pipeline:
3303/// 1. Parse the program source
3304/// 2. Run the optimizer (fold, propagate, PE, DCE)
3305/// 3. Decompile the optimized AST back to source
3306/// 4. Verify no interpretive overhead remains
3307pub fn projection1_source(_core_types: &str, _interpreter: &str, program: &str) -> Result<String, String> {
3308    let full_source = if program.contains("## Main") || program.contains("## To ") {
3309        program.to_string()
3310    } else {
3311        format!("## Main\n{}", program)
3312    };
3313
3314    let mut interner = Interner::new();
3315    let mut lexer = Lexer::new(&full_source, &mut interner);
3316    let tokens = lexer.tokenize();
3317
3318    let type_registry = {
3319        let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
3320        let result = discovery.run_full();
3321        result.types
3322    };
3323
3324    let mut world_state = WorldState::new();
3325    let expr_arena = Arena::new();
3326    let term_arena = Arena::new();
3327    let np_arena = Arena::new();
3328    let sym_arena = Arena::new();
3329    let role_arena = Arena::new();
3330    let pp_arena = Arena::new();
3331    let stmt_arena: Arena<Stmt> = Arena::new();
3332    let imperative_expr_arena: Arena<Expr> = Arena::new();
3333    let type_expr_arena: Arena<TypeExpr> = Arena::new();
3334
3335    let ast_ctx = AstContext::with_types(
3336        &expr_arena, &term_arena, &np_arena, &sym_arena,
3337        &role_arena, &pp_arena, &stmt_arena, &imperative_expr_arena,
3338        &type_expr_arena,
3339    );
3340
3341    let mut parser = crate::parser::Parser::new(
3342        tokens, &mut world_state, &mut interner, ast_ctx, type_registry,
3343    );
3344    let stmts = parser.parse_program().map_err(|e| format!("Parse error: {:?}", e))?;
3345
3346    // First Futamura Projection: PE(interpreter, program) = compiled_program.
3347    // Use the projection-safe optimizer: fold + propagate + PE + CTFE.
3348    // This preserves control-flow structure (If/While) while still folding
3349    // constants, propagating values, and specializing function calls.
3350    let optimized = crate::optimize::optimize_for_projection(
3351        stmts, &imperative_expr_arena, &stmt_arena, &mut interner,
3352    );
3353
3354    let mut output = String::new();
3355
3356    for stmt in &optimized {
3357        if matches!(stmt, Stmt::FunctionDef { .. }) {
3358            decompile_stmt(stmt, &interner, &mut output, 0);
3359            output.push('\n');
3360        }
3361    }
3362
3363    output.push_str("## Main\n");
3364    for stmt in &optimized {
3365        if !matches!(stmt, Stmt::FunctionDef { .. }) {
3366            decompile_stmt(stmt, &interner, &mut output, 0);
3367        }
3368    }
3369
3370    Ok(output)
3371}
3372
3373fn decompile_stmt(stmt: &Stmt, interner: &Interner, out: &mut String, indent: usize) {
3374    let pad = "    ".repeat(indent);
3375    match stmt {
3376        Stmt::FunctionDef { name, params, body, return_type, .. } => {
3377            let fn_name = interner.resolve(*name);
3378            let param_strs: Vec<String> = params
3379                .iter()
3380                .map(|(name, ty)| {
3381                    let pname = interner.resolve(*name);
3382                    format!("{}: {}", pname, decompile_type_expr(ty, interner))
3383                })
3384                .collect();
3385            let ret_str = if let Some(rt) = return_type {
3386                format!(" -> {}", decompile_type_expr(rt, interner))
3387            } else {
3388                String::new()
3389            };
3390            out.push_str(&format!("{}## To {} ({}){}:\n", pad, fn_name, param_strs.join(", "), ret_str));
3391            for s in body.iter() {
3392                decompile_stmt(s, interner, out, indent + 1);
3393            }
3394        }
3395        Stmt::Let { var, value, mutable, .. } => {
3396            let name = interner.resolve(*var);
3397            let expr_str = decompile_expr(value, interner);
3398            if *mutable {
3399                out.push_str(&format!("{}Let mutable {} be {}.\n", pad, name, expr_str));
3400            } else {
3401                out.push_str(&format!("{}Let {} be {}.\n", pad, name, expr_str));
3402            }
3403        }
3404        Stmt::Set { target, value } => {
3405            let name = interner.resolve(*target);
3406            let expr_str = decompile_expr(value, interner);
3407            out.push_str(&format!("{}Set {} to {}.\n", pad, name, expr_str));
3408        }
3409        Stmt::Show { object, .. } => {
3410            let expr_str = decompile_expr(object, interner);
3411            out.push_str(&format!("{}Show {}.\n", pad, expr_str));
3412        }
3413        Stmt::Return { value } => {
3414            if let Some(expr) = value {
3415                let expr_str = decompile_expr(expr, interner);
3416                out.push_str(&format!("{}Return {}.\n", pad, expr_str));
3417            } else {
3418                out.push_str(&format!("{}Return.\n", pad));
3419            }
3420        }
3421        Stmt::If { cond, then_block, else_block } => {
3422            let cond_str = decompile_expr(cond, interner);
3423            out.push_str(&format!("{}If {}:\n", pad, cond_str));
3424            for s in then_block.iter() {
3425                decompile_stmt(s, interner, out, indent + 1);
3426            }
3427            if let Some(els) = else_block {
3428                out.push_str(&format!("{}Otherwise:\n", pad));
3429                for s in els.iter() {
3430                    decompile_stmt(s, interner, out, indent + 1);
3431                }
3432            }
3433        }
3434        Stmt::While { cond, body, .. } => {
3435            let cond_str = decompile_expr(cond, interner);
3436            out.push_str(&format!("{}While {}:\n", pad, cond_str));
3437            for s in body.iter() {
3438                decompile_stmt(s, interner, out, indent + 1);
3439            }
3440        }
3441        Stmt::Call { function, args } => {
3442            let fn_name = interner.resolve(*function);
3443            let arg_strs: Vec<String> = args.iter().map(|a| decompile_expr(a, interner)).collect();
3444            if arg_strs.is_empty() {
3445                out.push_str(&format!("{}{}().\n", pad, fn_name));
3446            } else {
3447                out.push_str(&format!("{}{}({}).\n", pad, fn_name, arg_strs.join(", ")));
3448            }
3449        }
3450        Stmt::Push { value, collection } => {
3451            let val_str = decompile_expr(value, interner);
3452            let coll_str = decompile_expr(collection, interner);
3453            out.push_str(&format!("{}Push {} to {}.\n", pad, val_str, coll_str));
3454        }
3455        Stmt::SetIndex { collection, index, value } => {
3456            let coll_str = decompile_expr(collection, interner);
3457            let idx_str = decompile_expr(index, interner);
3458            let val_str = decompile_expr(value, interner);
3459            out.push_str(&format!("{}Set item {} of {} to {}.\n", pad, idx_str, coll_str, val_str));
3460        }
3461        Stmt::SetField { object, field, value } => {
3462            let obj_str = decompile_expr(object, interner);
3463            let field_name = interner.resolve(*field);
3464            let val_str = decompile_expr(value, interner);
3465            out.push_str(&format!("{}Set {} of {} to {}.\n", pad, field_name, obj_str, val_str));
3466        }
3467        Stmt::Repeat { pattern, iterable, body, .. } => {
3468            let var_name = match pattern {
3469                Pattern::Identifier(sym) => interner.resolve(*sym).to_string(),
3470                Pattern::Tuple(syms) => {
3471                    syms.iter().map(|s| interner.resolve(*s).to_string()).collect::<Vec<_>>().join(", ")
3472                }
3473            };
3474            let iter_str = decompile_expr(iterable, interner);
3475            out.push_str(&format!("{}Repeat for {} in {}:\n", pad, var_name, iter_str));
3476            for s in body.iter() {
3477                decompile_stmt(s, interner, out, indent + 1);
3478            }
3479        }
3480        Stmt::Inspect { target, arms, .. } => {
3481            let target_str = decompile_expr(target, interner);
3482            out.push_str(&format!("{}Inspect {}:\n", pad, target_str));
3483            for arm in arms {
3484                if let Some(variant) = arm.variant {
3485                    let variant_name = interner.resolve(variant);
3486                    let bindings: Vec<String> = arm.bindings.iter()
3487                        .map(|(_, b)| interner.resolve(*b).to_string())
3488                        .collect();
3489                    if bindings.is_empty() {
3490                        out.push_str(&format!("{}    When {}:\n", pad, variant_name));
3491                    } else {
3492                        out.push_str(&format!("{}    When {}({}):\n", pad, variant_name, bindings.join(", ")));
3493                    }
3494                } else {
3495                    out.push_str(&format!("{}    Otherwise:\n", pad));
3496                }
3497                for s in arm.body.iter() {
3498                    decompile_stmt(s, interner, out, indent + 2);
3499                }
3500            }
3501        }
3502        Stmt::Pop { collection, into } => {
3503            let coll_str = decompile_expr(collection, interner);
3504            if let Some(target) = into {
3505                let target_name = interner.resolve(*target);
3506                out.push_str(&format!("{}Pop from {} into {}.\n", pad, coll_str, target_name));
3507            } else {
3508                out.push_str(&format!("{}Pop from {}.\n", pad, coll_str));
3509            }
3510        }
3511        Stmt::Break => {
3512            out.push_str(&format!("{}Break.\n", pad));
3513        }
3514        Stmt::RuntimeAssert { condition } => {
3515            let cond_str = decompile_expr(condition, interner);
3516            out.push_str(&format!("{}Assert that {}.\n", pad, cond_str));
3517        }
3518        Stmt::Add { value, collection } => {
3519            let val_str = decompile_expr(value, interner);
3520            let coll_str = decompile_expr(collection, interner);
3521            out.push_str(&format!("{}Add {} to {}.\n", pad, val_str, coll_str));
3522        }
3523        Stmt::Remove { value, collection } => {
3524            let val_str = decompile_expr(value, interner);
3525            let coll_str = decompile_expr(collection, interner);
3526            out.push_str(&format!("{}Remove {} from {}.\n", pad, val_str, coll_str));
3527        }
3528        Stmt::Zone { name, body, .. } => {
3529            let zone_name = interner.resolve(*name);
3530            out.push_str(&format!("{}Inside a new zone called \"{}\":\n", pad, zone_name));
3531            for s in body.iter() {
3532                decompile_stmt(s, interner, out, indent + 1);
3533            }
3534        }
3535        Stmt::ReadFrom { var, .. } => {
3536            let var_name = interner.resolve(*var);
3537            out.push_str(&format!("{}Read {} from the console.\n", pad, var_name));
3538        }
3539        Stmt::WriteFile { content, path } => {
3540            let content_str = decompile_expr(content, interner);
3541            let path_str = decompile_expr(path, interner);
3542            out.push_str(&format!("{}Write {} to file {}.\n", pad, content_str, path_str));
3543        }
3544        Stmt::Sleep { milliseconds } => {
3545            let ms = decompile_expr(milliseconds, interner);
3546            out.push_str(&format!("{}Sleep {}.\n", pad, ms));
3547        }
3548        _ => {
3549            // Remaining system-level statements (CRDT, networking, concurrency)
3550            // are not produced by the optimizer and don't appear in P1 residuals.
3551        }
3552    }
3553}
3554
3555fn decompile_expr(expr: &Expr, interner: &Interner) -> String {
3556    match expr {
3557        Expr::Literal(lit) => match lit {
3558            Literal::Number(n) => n.to_string(),
3559            Literal::Float(f) => format!("{}", f),
3560            Literal::Boolean(b) => if *b { "true".to_string() } else { "false".to_string() },
3561            Literal::Text(s) => format!("\"{}\"", interner.resolve(*s)),
3562            Literal::Nothing => "nothing".to_string(),
3563            Literal::Char(c) => format!("'{}'", c),
3564            Literal::Duration(ns) => format!("{}", ns),
3565            Literal::Date(days) => format!("{}", days),
3566            Literal::Moment(ns) => format!("{}", ns),
3567            Literal::Span { months, days } => format!("{} months {} days", months, days),
3568            Literal::Time(ns) => format!("{}", ns),
3569        },
3570        Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
3571        Expr::BinaryOp { op, left, right } => {
3572            let l = if matches!(left, Expr::BinaryOp { .. }) {
3573                format!("({})", decompile_expr(left, interner))
3574            } else {
3575                decompile_expr(left, interner)
3576            };
3577            let r = if matches!(right, Expr::BinaryOp { .. }) {
3578                format!("({})", decompile_expr(right, interner))
3579            } else {
3580                decompile_expr(right, interner)
3581            };
3582            // Shift operations are introduced by bit-strength optimization.
3583            // Map back to equivalent multiply/divide for LOGOS source.
3584            if matches!(op, BinaryOpKind::Shl) {
3585                // n << k = n * 2^k
3586                if let Expr::Literal(Literal::Number(k)) = right {
3587                    let multiplier = 1i64 << k;
3588                    return format!("{} * {}", l, multiplier);
3589                }
3590            }
3591            if matches!(op, BinaryOpKind::Shr) {
3592                // n >> k = n / 2^k
3593                if let Expr::Literal(Literal::Number(k)) = right {
3594                    let divisor = 1i64 << k;
3595                    return format!("{} / {}", l, divisor);
3596                }
3597            }
3598            let op_str = match op {
3599                BinaryOpKind::Add => "+",
3600                BinaryOpKind::Subtract => "-",
3601                BinaryOpKind::Multiply => "*",
3602                BinaryOpKind::Divide => "/",
3603                BinaryOpKind::Modulo => "%",
3604                BinaryOpKind::Eq => "equals",
3605                BinaryOpKind::NotEq => "is not",
3606                BinaryOpKind::Lt => "is less than",
3607                BinaryOpKind::Gt => "is greater than",
3608                BinaryOpKind::LtEq => "is at most",
3609                BinaryOpKind::GtEq => "is at least",
3610                BinaryOpKind::And => "and",
3611                BinaryOpKind::Or => "or",
3612                BinaryOpKind::Concat => "+",
3613                BinaryOpKind::BitXor => "+",
3614                BinaryOpKind::Shl => "*",
3615                BinaryOpKind::Shr => "/",
3616            };
3617            format!("{} {} {}", l, op_str, r)
3618        }
3619        Expr::Not { operand } => {
3620            let inner = decompile_expr(operand, interner);
3621            format!("not {}", inner)
3622        }
3623        Expr::Call { function, args } => {
3624            let fn_name = interner.resolve(*function);
3625            let arg_strs: Vec<String> = args.iter().map(|a| decompile_expr(a, interner)).collect();
3626            if arg_strs.is_empty() {
3627                format!("{}()", fn_name)
3628            } else {
3629                format!("{}({})", fn_name, arg_strs.join(", "))
3630            }
3631        }
3632        Expr::Index { collection, index } => {
3633            let coll = decompile_expr(collection, interner);
3634            let idx = decompile_expr(index, interner);
3635            format!("item {} of {}", idx, coll)
3636        }
3637        Expr::Length { collection } => {
3638            let coll = decompile_expr(collection, interner);
3639            format!("length of {}", coll)
3640        }
3641        Expr::FieldAccess { object, field } => {
3642            let obj = decompile_expr(object, interner);
3643            let field_name = interner.resolve(*field);
3644            format!("{} of {}", field_name, obj)
3645        }
3646        Expr::New { type_name, .. } => {
3647            let tn = interner.resolve(*type_name);
3648            format!("a new {}", tn)
3649        }
3650        Expr::NewVariant { variant, fields, .. } => {
3651            let vn = interner.resolve(*variant);
3652            if fields.is_empty() {
3653                format!("a new {}", vn)
3654            } else {
3655                let parts: Vec<String> = fields.iter().map(|(name, val)| {
3656                    let n = interner.resolve(*name);
3657                    let v = decompile_expr(val, interner);
3658                    format!("{} {}", n, v)
3659                }).collect();
3660                format!("a new {} with {}", vn, parts.join(" and "))
3661            }
3662        }
3663        Expr::InterpolatedString(parts) => {
3664            let mut result = String::new();
3665            for part in parts {
3666                match part {
3667                    StringPart::Literal(sym) => {
3668                        result.push_str(&interner.resolve(*sym));
3669                    }
3670                    StringPart::Expr { value, debug, .. } => {
3671                        let expr_str = decompile_expr(value, interner);
3672                        if *debug {
3673                            result.push_str(&format!("{{{}=}}", expr_str));
3674                        } else {
3675                            result.push_str(&format!("{{{}}}", expr_str));
3676                        }
3677                    }
3678                }
3679            }
3680            format!("\"{}\"", result)
3681        }
3682        Expr::Slice { collection, start, end } => {
3683            let coll = decompile_expr(collection, interner);
3684            let s = decompile_expr(start, interner);
3685            let e = decompile_expr(end, interner);
3686            format!("{} {} through {}", coll, s, e)
3687        }
3688        Expr::Copy { expr } => {
3689            let inner = decompile_expr(expr, interner);
3690            format!("copy of {}", inner)
3691        }
3692        Expr::Give { value } => {
3693            let inner = decompile_expr(value, interner);
3694            format!("Give {}", inner)
3695        }
3696        Expr::Contains { collection, value } => {
3697            let coll = decompile_expr(collection, interner);
3698            let val = decompile_expr(value, interner);
3699            format!("{} contains {}", coll, val)
3700        }
3701        Expr::Union { left, right } => {
3702            let l = decompile_expr(left, interner);
3703            let r = decompile_expr(right, interner);
3704            format!("{} union {}", l, r)
3705        }
3706        Expr::Intersection { left, right } => {
3707            let l = decompile_expr(left, interner);
3708            let r = decompile_expr(right, interner);
3709            format!("{} intersection {}", l, r)
3710        }
3711        Expr::List(elems) => {
3712            let parts: Vec<String> = elems.iter().map(|e| decompile_expr(e, interner)).collect();
3713            format!("[{}]", parts.join(", "))
3714        }
3715        Expr::Tuple(elems) => {
3716            let parts: Vec<String> = elems.iter().map(|e| decompile_expr(e, interner)).collect();
3717            format!("({})", parts.join(", "))
3718        }
3719        Expr::Range { start, end } => {
3720            let s = decompile_expr(start, interner);
3721            let e = decompile_expr(end, interner);
3722            format!("{} to {}", s, e)
3723        }
3724        Expr::OptionSome { value } => {
3725            let inner = decompile_expr(value, interner);
3726            format!("some {}", inner)
3727        }
3728        Expr::OptionNone => "none".to_string(),
3729        Expr::WithCapacity { value, capacity } => {
3730            let val = decompile_expr(value, interner);
3731            let cap = decompile_expr(capacity, interner);
3732            format!("{} with capacity {}", val, cap)
3733        }
3734        Expr::Escape { language, code } => {
3735            let lang = interner.resolve(*language);
3736            let src = interner.resolve(*code);
3737            format!("Escape to {}:\n{}", lang, src)
3738        }
3739        Expr::ManifestOf { zone } => {
3740            let z = decompile_expr(zone, interner);
3741            format!("the manifest of {}", z)
3742        }
3743        Expr::ChunkAt { index, zone } => {
3744            let idx = decompile_expr(index, interner);
3745            let z = decompile_expr(zone, interner);
3746            format!("the chunk at {} in {}", idx, z)
3747        }
3748        Expr::Closure { params, body, return_type } => {
3749            let param_strs: Vec<String> = params.iter().map(|(name, ty)| {
3750                let n = interner.resolve(*name);
3751                let t = decompile_type_expr(ty, interner);
3752                format!("{}: {}", n, t)
3753            }).collect();
3754            let ret = if let Some(rt) = return_type {
3755                format!(" -> {}", decompile_type_expr(rt, interner))
3756            } else {
3757                String::new()
3758            };
3759            match body {
3760                ClosureBody::Expression(expr) => {
3761                    let e = decompile_expr(expr, interner);
3762                    format!("({}){} -> {}", param_strs.join(", "), ret, e)
3763                }
3764                ClosureBody::Block(_) => {
3765                    format!("({}){} -> [block]", param_strs.join(", "), ret)
3766                }
3767            }
3768        }
3769        Expr::CallExpr { callee, args } => {
3770            let c = decompile_expr(callee, interner);
3771            let arg_strs: Vec<String> = args.iter().map(|a| decompile_expr(a, interner)).collect();
3772            format!("{}({})", c, arg_strs.join(", "))
3773        }
3774    }
3775}
3776
3777fn decompile_type_expr(ty: &TypeExpr, interner: &Interner) -> String {
3778    match ty {
3779        TypeExpr::Primitive(sym) => interner.resolve(*sym).to_string(),
3780        TypeExpr::Named(sym) => interner.resolve(*sym).to_string(),
3781        TypeExpr::Generic { base, params } => {
3782            let base_str = interner.resolve(*base);
3783            let param_strs: Vec<String> = params.iter().map(|p| decompile_type_expr(p, interner)).collect();
3784            format!("{} of {}", base_str, param_strs.join(" and "))
3785        }
3786        TypeExpr::Function { inputs, output } => {
3787            let in_strs: Vec<String> = inputs.iter().map(|t| decompile_type_expr(t, interner)).collect();
3788            let out_str = decompile_type_expr(output, interner);
3789            format!("fn({}) -> {}", in_strs.join(", "), out_str)
3790        }
3791        TypeExpr::Refinement { base, .. } => {
3792            decompile_type_expr(base, interner)
3793        }
3794        TypeExpr::Persistent { inner } => {
3795            format!("Persistent {}", decompile_type_expr(inner, interner))
3796        }
3797    }
3798}
3799
3800/// Verify that a LogicAffeine program has no interpretive overhead.
3801///
3802/// Checks the AST for patterns that indicate unresolved interpreter dispatch:
3803/// - Inspect on CStmt/CExpr/CVal variants
3804/// - References to Core constructor types (CInt, CShow, etc.)
3805/// - Environment lookups on literal strings
3806pub fn verify_no_overhead_source(source: &str) -> Result<(), String> {
3807    let mut interner = Interner::new();
3808    let mut lexer = Lexer::new(source, &mut interner);
3809    let tokens = lexer.tokenize();
3810
3811    let type_registry = {
3812        let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
3813        let result = discovery.run_full();
3814        result.types
3815    };
3816
3817    let mut world_state = WorldState::new();
3818    let expr_arena = Arena::new();
3819    let term_arena = Arena::new();
3820    let np_arena = Arena::new();
3821    let sym_arena = Arena::new();
3822    let role_arena = Arena::new();
3823    let pp_arena = Arena::new();
3824    let stmt_arena: Arena<Stmt> = Arena::new();
3825    let imperative_expr_arena: Arena<Expr> = Arena::new();
3826    let type_expr_arena: Arena<TypeExpr> = Arena::new();
3827
3828    let ast_ctx = AstContext::with_types(
3829        &expr_arena, &term_arena, &np_arena, &sym_arena,
3830        &role_arena, &pp_arena, &stmt_arena, &imperative_expr_arena,
3831        &type_expr_arena,
3832    );
3833
3834    let mut parser = crate::parser::Parser::new(
3835        tokens, &mut world_state, &mut interner, ast_ctx, type_registry,
3836    );
3837    let stmts = parser.parse_program().map_err(|e| format!("Parse error: {:?}", e))?;
3838
3839    verify_no_overhead_stmts(&stmts, &interner)
3840}
3841
3842const CORE_VARIANT_NAMES: &[&str] = &[
3843    "CInt", "CBool", "CText", "CVar", "CBinOp", "CNot",
3844    "CCall", "CIndex", "CLen", "CMapGet",
3845    "CLet", "CSet", "CIf", "CWhile", "CReturn", "CShow",
3846    "CCallS", "CPush", "CSetIdx", "CMapSet",
3847    "CFuncDef", "CProg",
3848    "VInt", "VBool", "VText", "VSeq", "VMap", "VError", "VNothing",
3849];
3850
3851fn verify_no_overhead_stmts(stmts: &[Stmt], interner: &Interner) -> Result<(), String> {
3852    for stmt in stmts {
3853        check_stmt_overhead(stmt, interner)?;
3854    }
3855    Ok(())
3856}
3857
3858fn check_stmt_overhead(stmt: &Stmt, interner: &Interner) -> Result<(), String> {
3859    match stmt {
3860        Stmt::Inspect { arms, .. } => {
3861            for arm in arms {
3862                if let Some(variant) = arm.variant {
3863                    let variant_name = interner.resolve(variant);
3864                    if CORE_VARIANT_NAMES.contains(&variant_name) {
3865                        return Err(format!(
3866                            "Interpretive overhead: Inspect dispatches on Core variant '{}'",
3867                            variant_name
3868                        ));
3869                    }
3870                }
3871                for s in arm.body.iter() {
3872                    check_stmt_overhead(s, interner)?;
3873                }
3874            }
3875        }
3876        Stmt::If { cond, then_block, else_block } => {
3877            check_expr_overhead(cond, interner)?;
3878            for s in then_block.iter() {
3879                check_stmt_overhead(s, interner)?;
3880            }
3881            if let Some(els) = else_block {
3882                for s in els.iter() {
3883                    check_stmt_overhead(s, interner)?;
3884                }
3885            }
3886        }
3887        Stmt::While { cond, body, .. } => {
3888            check_expr_overhead(cond, interner)?;
3889            for s in body.iter() {
3890                check_stmt_overhead(s, interner)?;
3891            }
3892        }
3893        Stmt::FunctionDef { body, .. } => {
3894            for s in body.iter() {
3895                check_stmt_overhead(s, interner)?;
3896            }
3897        }
3898        Stmt::Repeat { body, .. } => {
3899            for s in body.iter() {
3900                check_stmt_overhead(s, interner)?;
3901            }
3902        }
3903        Stmt::Let { value, .. } | Stmt::Set { value, .. } | Stmt::Show { object: value, .. } => {
3904            check_expr_overhead(value, interner)?;
3905        }
3906        Stmt::Return { value } => {
3907            if let Some(v) = value {
3908                check_expr_overhead(v, interner)?;
3909            }
3910        }
3911        _ => {}
3912    }
3913    Ok(())
3914}
3915
3916fn check_expr_overhead(expr: &Expr, interner: &Interner) -> Result<(), String> {
3917    match expr {
3918        Expr::Index { collection, index } => {
3919            // Check for `item X of env` where X is a literal string (env lookup overhead)
3920            if let Expr::Identifier(coll_sym) = collection {
3921                let coll_name = interner.resolve(*coll_sym);
3922                if coll_name == "env" {
3923                    if let Expr::Literal(Literal::Text(_)) = index {
3924                        return Err(
3925                            "Interpretive overhead: environment lookup 'item ... of env' on literal key".to_string()
3926                        );
3927                    }
3928                }
3929            }
3930            check_expr_overhead(collection, interner)?;
3931            check_expr_overhead(index, interner)?;
3932        }
3933        Expr::New { type_name, .. } => {
3934            let tn = interner.resolve(*type_name);
3935            if CORE_VARIANT_NAMES.contains(&tn) {
3936                return Err(format!(
3937                    "Interpretive overhead: Core type constructor 'new {}'", tn
3938                ));
3939            }
3940        }
3941        Expr::NewVariant { variant, .. } => {
3942            let vn = interner.resolve(*variant);
3943            if CORE_VARIANT_NAMES.contains(&vn) {
3944                return Err(format!(
3945                    "Interpretive overhead: Core variant constructor '{}'", vn
3946                ));
3947            }
3948        }
3949        Expr::Call { function, args } => {
3950            let fn_name = interner.resolve(*function);
3951            if CORE_VARIANT_NAMES.contains(&fn_name) {
3952                return Err(format!(
3953                    "Interpretive overhead: Core variant call '{}'", fn_name
3954                ));
3955            }
3956            for a in args {
3957                check_expr_overhead(a, interner)?;
3958            }
3959        }
3960        Expr::BinaryOp { left, right, .. } => {
3961            check_expr_overhead(left, interner)?;
3962            check_expr_overhead(right, interner)?;
3963        }
3964        Expr::Not { operand } => {
3965            check_expr_overhead(operand, interner)?;
3966        }
3967        Expr::Length { collection } => {
3968            check_expr_overhead(collection, interner)?;
3969        }
3970        Expr::FieldAccess { object, .. } => {
3971            check_expr_overhead(object, interner)?;
3972        }
3973        _ => {}
3974    }
3975    Ok(())
3976}
3977
3978/// Returns the source text of the partial evaluator written in LogicAffeine.
3979///
3980/// This PE operates on CProgram representations using explicit environments
3981/// and static dispatch. It is first-order (no closures) and uses only
3982/// literal string function names (no dynamic dispatch).
3983pub fn pe_source_text() -> &'static str {
3984    include_str!("optimize/pe_source.logos")
3985}
3986
3987pub fn decompile_source_text() -> &'static str {
3988    include_str!("optimize/decompile_source.logos")
3989}
3990
3991pub fn pe_bti_source_text() -> &'static str {
3992    include_str!("optimize/pe_bti_source.logos")
3993}
3994
3995pub fn pe_mini_source_text() -> &'static str {
3996    include_str!("optimize/pe_mini_source.logos")
3997}
3998
3999const CORE_TYPES_FOR_PE: &str = r#"
4000## A CExpr is one of:
4001    A CInt with value Int.
4002    A CFloat with value Real.
4003    A CBool with value Bool.
4004    A CText with value Text.
4005    A CVar with name Text.
4006    A CBinOp with op Text and left CExpr and right CExpr.
4007    A CNot with inner CExpr.
4008    A CCall with name Text and args Seq of CExpr.
4009    A CIndex with coll CExpr and idx CExpr.
4010    A CLen with target CExpr.
4011    A CMapGet with target CExpr and key CExpr.
4012    A CNewSeq.
4013    A CNewVariant with tag Text and fnames Seq of Text and fvals Seq of CExpr.
4014    A CList with items Seq of CExpr.
4015    A CRange with start CExpr and end CExpr.
4016    A CSlice with coll CExpr and startIdx CExpr and endIdx CExpr.
4017    A CCopy with target CExpr.
4018    A CNewSet.
4019    A CContains with coll CExpr and elem CExpr.
4020    A CUnion with left CExpr and right CExpr.
4021    A CIntersection with left CExpr and right CExpr.
4022    A COptionSome with inner CExpr.
4023    A COptionNone.
4024    A CTuple with items Seq of CExpr.
4025    A CNew with typeName Text and fieldNames Seq of Text and fields Seq of CExpr.
4026    A CFieldAccess with target CExpr and field Text.
4027    A CClosure with params Seq of Text and body Seq of CStmt and captured Seq of Text.
4028    A CCallExpr with target CExpr and args Seq of CExpr.
4029    A CInterpolatedString with parts Seq of CStringPart.
4030    A CDuration with amount CExpr and unit Text.
4031    A CTimeNow.
4032    A CDateToday.
4033    A CEscExpr with code Text.
4034
4035## A CStringPart is one of:
4036    A CLiteralPart with value Text.
4037    A CExprPart with expr CExpr.
4038
4039## A CStmt is one of:
4040    A CLet with name Text and expr CExpr.
4041    A CSet with name Text and expr CExpr.
4042    A CIf with cond CExpr and thenBlock Seq of CStmt and elseBlock Seq of CStmt.
4043    A CWhile with cond CExpr and body Seq of CStmt.
4044    A CReturn with expr CExpr.
4045    A CShow with expr CExpr.
4046    A CCallS with name Text and args Seq of CExpr.
4047    A CPush with expr CExpr and target Text.
4048    A CSetIdx with target Text and idx CExpr and val CExpr.
4049    A CMapSet with target Text and key CExpr and val CExpr.
4050    A CPop with target Text.
4051    A CRepeat with var Text and coll CExpr and body Seq of CStmt.
4052    A CRepeatRange with var Text and start CExpr and end CExpr and body Seq of CStmt.
4053    A CBreak.
4054    A CAdd with elem CExpr and target Text.
4055    A CRemove with elem CExpr and target Text.
4056    A CSetField with target Text and field Text and val CExpr.
4057    A CStructDef with name Text and fieldNames Seq of Text.
4058    A CInspect with target CExpr and arms Seq of CMatchArm.
4059    A CEnumDef with name Text and variants Seq of Text.
4060    A CRuntimeAssert with cond CExpr and msg CExpr.
4061    A CGive with expr CExpr and target Text.
4062    A CEscStmt with code Text.
4063    A CSleep with duration CExpr.
4064    A CReadConsole with target Text.
4065    A CReadFile with path CExpr and target Text.
4066    A CWriteFile with path CExpr and content CExpr.
4067    A CCheck with predicate CExpr and msg CExpr.
4068    A CAssert with proposition CExpr.
4069    A CTrust with proposition CExpr and justification Text.
4070    A CRequire with dependency Text.
4071    A CMerge with target Text and other CExpr.
4072    A CIncrease with target Text and amount CExpr.
4073    A CDecrease with target Text and amount CExpr.
4074    A CAppendToSeq with target Text and value CExpr.
4075    A CResolve with target Text.
4076    A CSync with target Text and channel CExpr.
4077    A CMount with target Text and path CExpr.
4078    A CConcurrent with branches Seq of Seq of CStmt.
4079    A CParallel with branches Seq of Seq of CStmt.
4080    A CLaunchTask with body Seq of CStmt and handle Text.
4081    A CStopTask with handle CExpr.
4082    A CSelect with branches Seq of CSelectBranch.
4083    A CCreatePipe with name Text and capacity CExpr.
4084    A CSendPipe with chan Text and value CExpr.
4085    A CReceivePipe with chan Text and target Text.
4086    A CTrySendPipe with chan Text and value CExpr.
4087    A CTryReceivePipe with chan Text and target Text.
4088    A CSpawn with agentType Text and target Text.
4089    A CSendMessage with target CExpr and msg CExpr.
4090    A CAwaitMessage with target Text.
4091    A CListen with addr CExpr and handler Text.
4092    A CConnectTo with addr CExpr and target Text.
4093    A CZone with name Text and kind Text and body Seq of CStmt.
4094
4095## A CSelectBranch is one of:
4096    A CSelectRecv with chan Text and var Text and body Seq of CStmt.
4097    A CSelectTimeout with duration CExpr and body Seq of CStmt.
4098
4099## A CMatchArm is one of:
4100    A CWhen with variantName Text and bindings Seq of Text and body Seq of CStmt.
4101    A COtherwise with body Seq of CStmt.
4102
4103## A CFunc is one of:
4104    A CFuncDef with name Text and params Seq of Text and body Seq of CStmt.
4105
4106## A CProgram is one of:
4107    A CProg with funcs Seq of CFunc and main Seq of CStmt.
4108
4109## A PEState is one of:
4110    A PEStateR with env Map of Text to CVal and funcs Map of Text to CFunc and depth Int and staticEnv Map of Text to CExpr and specResults Map of Text to CExpr and onStack Seq of Text.
4111
4112## A CVal is one of:
4113    A VInt with value Int.
4114    A VFloat with value Real.
4115    A VBool with value Bool.
4116    A VText with value Text.
4117    A VSeq with items Seq of CVal.
4118    A VMap with entries Map of Text to CVal.
4119    A VError with msg Text.
4120    A VNothing.
4121    A VSet with items Seq of CVal.
4122    A VOption with inner CVal and present Bool.
4123    A VTuple with items Seq of CVal.
4124    A VStruct with typeName Text and fields Map of Text to CVal.
4125    A VVariant with typeName Text and variantName Text and fields Seq of CVal.
4126    A VClosure with params Seq of Text and body Seq of CStmt and capturedEnv Map of Text to CVal.
4127    A VDuration with millis Int.
4128    A VDate with year Int and month Int and day Int.
4129    A VMoment with millis Int.
4130    A VSpan with startMillis Int and endMillis Int.
4131    A VTime with hour Int and minute Int and second Int.
4132    A VCrdt with kind Text and state Map of Text to CVal.
4133"#;
4134
4135/// Encodes the partial evaluator as CProgram construction source code.
4136///
4137/// Returns PE function definitions (so peBlock etc. are callable) followed by
4138/// LOGOS statements that construct the PE's functions as CFunc data in
4139/// `encodedFuncMap` and its main block in `encodedMain`.
4140/// The parser handles `## To` blocks anywhere in the token stream, so
4141/// function definitions placed after `## Main` are parsed correctly.
4142pub fn quote_pe_source() -> Result<String, String> {
4143    let pe_source = pe_source_text();
4144    let full_source = format!("{}\n{}", CORE_TYPES_FOR_PE, pe_source);
4145    let encoded = encode_program_source(&full_source).map_err(|e| format!("Failed to encode PE: {:?}", e))?;
4146    Ok(format!("{}\n{}", pe_source, encoded))
4147}
4148
4149/// Second Futamura Projection: PE(PE, interpreter) = compiler
4150///
4151/// Specializes the partial evaluator with respect to a fixed interpreter,
4152/// producing a compiler that takes any CProgram as input and produces
4153/// optimized residual CStmt/CExpr data.
4154///
4155/// For the Core self-interpreter (which is the identity evaluator on CProgram
4156/// data), PE(PE, int) resolves to the PE itself operating directly on program
4157/// data — the interpreter's dispatch loop is the CExpr/CStmt case analysis
4158/// that the PE already implements. The result is the PE with its entry points
4159/// renamed to compileExpr/compileBlock: these ARE the specialized compiler
4160/// functions with no PE dispatch overhead (BTA, memoization, etc. are absent
4161/// because the interpreter's representation IS the PE's representation).
4162pub fn projection2_source() -> Result<String, String> {
4163    let pe_source = pe_source_text();
4164
4165    let compiler_source = replace_word(&replace_word(&pe_source, "peExpr", "compileExpr"), "peBlock", "compileBlock");
4166
4167    Ok(format!("{}\n{}", CORE_TYPES_FOR_PE, compiler_source))
4168}
4169
4170/// Third Futamura Projection: PE(PE, PE) = compiler_generator
4171///
4172/// Specializes the partial evaluator with respect to itself, producing a
4173/// compiler generator (cogen). Feed it any interpreter → it produces a
4174/// compiler for that interpreter's language.
4175///
4176/// Chain: cogen(int) → compiler → compiler(P) → compiled
4177///
4178/// For the CExpr/CStmt representation, PE(PE, PE) yields the PE with entry
4179/// points renamed to cogenExpr/cogenBlock. This works because the PE's
4180/// self-application is idempotent: the PE already operates on the same
4181/// representation it would specialize, so PE(PE, PE) = PE (up to naming).
4182/// The cogen handles different interpreters (Core, RPN, etc.) by processing
4183/// their encoded CProgram representations.
4184pub fn projection3_source() -> Result<String, String> {
4185    let pe_source = pe_source_text();
4186
4187    let cogen_source = replace_word(&replace_word(&pe_source, "peExpr", "cogenExpr"), "peBlock", "cogenBlock");
4188
4189    Ok(format!("{}\n{}", CORE_TYPES_FOR_PE, cogen_source))
4190}
4191
4192/// Compile and run LOGOS source, returning stdout.
4193///
4194/// Uses the full compilation pipeline: LOGOS → Rust → binary → execute.
4195/// This is the library-side equivalent of the test infrastructure's `run_logos()`.
4196pub fn run_logos_source(source: &str) -> Result<String, String> {
4197    let compile_output = compile_program_full(source)
4198        .map_err(|e| format!("Compilation failed: {:?}", e))?;
4199
4200    // Create temp directory using std (no tempfile crate needed)
4201    let temp_base = std::env::temp_dir().join("logos_run_source");
4202    std::fs::create_dir_all(&temp_base)
4203        .map_err(|e| format!("mkdir failed: {}", e))?;
4204
4205    let pkg_name = format!(
4206        "logos_run_{}_{}",
4207        std::process::id(),
4208        std::time::SystemTime::now()
4209            .duration_since(std::time::UNIX_EPOCH)
4210            .unwrap()
4211            .as_nanos()
4212    );
4213    let project_dir = temp_base.join(&pkg_name);
4214
4215    // Find workspace root relative to this crate
4216    let manifest_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
4217    let workspace_root = manifest_dir.parent().unwrap().parent().unwrap();
4218
4219    let cargo_toml = format!(
4220        r#"[package]
4221name = "{}"
4222version = "0.1.0"
4223edition = "2021"
4224
4225[dependencies]
4226logicaffeine-data = {{ path = "{}/crates/logicaffeine_data" }}
4227logicaffeine-system = {{ path = "{}/crates/logicaffeine_system", features = ["full"] }}
4228tokio = {{ version = "1", features = ["rt-multi-thread", "macros"] }}
4229serde = {{ version = "1", features = ["derive"] }}
4230rayon = "1"
4231"#,
4232        pkg_name,
4233        workspace_root.display(),
4234        workspace_root.display(),
4235    );
4236
4237    std::fs::create_dir_all(project_dir.join("src"))
4238        .map_err(|e| format!("mkdir failed: {}", e))?;
4239    std::fs::write(project_dir.join("Cargo.toml"), cargo_toml)
4240        .map_err(|e| format!("Write Cargo.toml failed: {}", e))?;
4241    std::fs::write(project_dir.join("src/main.rs"), &compile_output.rust_code)
4242        .map_err(|e| format!("Write main.rs failed: {}", e))?;
4243
4244    // Use a shared target dir for caching
4245    let target_dir = std::env::temp_dir().join("logos_e2e_cache");
4246    std::fs::create_dir_all(&target_dir)
4247        .map_err(|e| format!("mkdir target failed: {}", e))?;
4248
4249    let output = std::process::Command::new("cargo")
4250        .args(["run", "--quiet"])
4251        .current_dir(&project_dir)
4252        .env("CARGO_TARGET_DIR", &target_dir)
4253        .env("RUST_MIN_STACK", "268435456")
4254        .output()
4255        .map_err(|e| format!("cargo run failed: {}", e))?;
4256
4257    // Clean up temp project dir
4258    let _ = std::fs::remove_dir_all(&project_dir);
4259
4260    if !output.status.success() {
4261        return Err(format!(
4262            "Execution failed:\nstderr: {}\nstdout: {}",
4263            String::from_utf8_lossy(&output.stderr),
4264            String::from_utf8_lossy(&output.stdout),
4265        ));
4266    }
4267
4268    Ok(String::from_utf8_lossy(&output.stdout).to_string())
4269}
4270
4271/// Real Futamura Projection 1: pe(program) = compiled_program
4272///
4273/// Encodes the program as CProgram data, runs the LOGOS PE on it,
4274/// decompiles the residual back to LOGOS source.
4275pub fn projection1_source_real(core_types: &str, _interpreter: &str, program: &str) -> Result<String, String> {
4276    let full_source = if program.contains("## Main") || program.contains("## To ") {
4277        program.to_string()
4278    } else {
4279        format!("## Main\n{}", program)
4280    };
4281
4282    // Step 1: Encode the program as CProgram construction source
4283    let encoded = encode_program_source(&full_source)
4284        .map_err(|e| format!("Failed to encode program: {:?}", e))?;
4285
4286    // Step 2: Get PE source and decompile source
4287    let pe_source = pe_source_text();
4288    let decompile_source = decompile_source_text();
4289
4290    // Step 3: Build the combined source
4291    let actual_core_types = if core_types.is_empty() { CORE_TYPES_FOR_PE } else { core_types };
4292
4293    let driver = r#"
4294    Let state be makePeState(a new Map of Text to CVal, encodedFuncMap, 200).
4295    Let residual be peBlock(encodedMain, state).
4296    Let source be decompileBlock(residual, 0).
4297    Show source.
4298"#;
4299
4300    let combined = format!(
4301        "{}\n{}\n{}\n## Main\n{}\n{}",
4302        actual_core_types,
4303        pe_source,
4304        decompile_source,
4305        encoded,
4306        driver,
4307    );
4308
4309    // Step 4: Compile and run to get the decompiled residual
4310    let raw_residual = run_logos_source(&combined)?;
4311    let trimmed = raw_residual.trim();
4312
4313    // Step 5: Wrap in ## Main if needed
4314    if trimmed.is_empty() {
4315        return Ok("## Main\n".to_string());
4316    }
4317
4318    // Check if the residual already has function definitions
4319    if trimmed.contains("## To ") {
4320        Ok(trimmed.to_string())
4321    } else {
4322        Ok(format!("## Main\n{}", trimmed))
4323    }
4324}
4325
4326/// Real Futamura Projection 2: PE(PE, interpreter) = compiler
4327///
4328/// Returns pe_bti with entry points renamed to compileExpr/compileBlock/extractReturn.
4329/// pe_bti is the BTI-refined PE used as the practical compiler, supporting
4330/// full function inlining and all PE optimizations.
4331///
4332/// Genuine self-application PE(pe_source, pe_mini) is verified by tests
4333/// (genuine_p2_pe_mini_full_scale_dynamic_target). The genuine P2 output
4334/// can be obtained via genuine_projection2_residual().
4335///
4336/// Returns ONLY the compiler source (no type definitions). Callers must
4337/// prepend appropriate CORE_TYPES for the BTI state representation.
4338pub fn projection2_source_real(_core_types: &str, _interpreter: &str) -> Result<String, String> {
4339    let pe_bti = pe_bti_source_text();
4340
4341    // Rename entry points: peExprB→compileExpr, peBlockB→compileBlock, extractReturnB→extractReturn
4342    let compiler = replace_word(
4343        &replace_word(
4344            &replace_word(pe_bti, "peExprB", "compileExpr"),
4345            "peBlockB", "compileBlock",
4346        ),
4347        "extractReturnB", "extractReturn",
4348    );
4349
4350    Ok(compiler.to_string())
4351}
4352
4353/// Run genuine PE(pe_source, pe_mini(targetExpr)) and return the specialized
4354/// compiler residual as LOGOS source code.
4355///
4356/// This is the actual Futamura Projection 2: the outer PE (pe_source) specializes
4357/// pe_mini's peExprM with known state (empty env, empty funcs, depth 200).
4358/// The result is a specialized compiler function that takes a target CExpr and
4359/// compiles it with pe_mini's dispatch logic partially evaluated away.
4360///
4361/// Returns the decompiled LOGOS source of the genuine P2 residual, including
4362/// specialized function definitions extracted from peFuncs.
4363pub fn genuine_projection2_residual() -> Result<String, String> {
4364    let pe_mini = pe_mini_source_text();
4365    let pe = pe_source_text();
4366    let decompile = decompile_source_text();
4367
4368    // Build pe_mini program with a free variable target
4369    let program = format!(
4370        "{}\n{}\n## Main\n    Let env be a new Map of Text to CVal.\n    Let funcs be a new Map of Text to CFunc.\n    Let state be makePeState(env, funcs, 200).\n    Let result be peExprM(targetExpr, state).\n    Inspect result:\n        When CInt (v):\n            Show v.\n        Otherwise:\n            Show \"dynamic\".\n",
4371        CORE_TYPES_FOR_PE, pe_mini
4372    );
4373
4374    // Encode pe_mini + driver as CProgram data (compact encoding)
4375    let encoded = encode_program_source_compact(&program)
4376        .map_err(|e| format!("Failed to encode pe_mini: {:?}", e))?;
4377
4378    // Driver: run PE, then decompile residual + specialized functions
4379    let driver = r#"    Let state be makePeState(a new Map of Text to CVal, encodedFuncMap, 200).
4380    Let residual be peBlock(encodedMain, state).
4381    Let nl be chr(10).
4382    Let mutable output be "".
4383    Let specFuncs be peFuncs(state).
4384    Let specNames be collectCallNames(residual).
4385    Repeat for sn in specNames:
4386        Let snKey be "{sn}".
4387        If specFuncs contains snKey:
4388            Let fdef be item snKey of specFuncs.
4389            Let funcSrc be decompileFunc(fdef).
4390            If the length of funcSrc is greater than 0:
4391                Set output to "{output}{funcSrc}{nl}".
4392    Let mainSrc be decompileBlock(residual, 0).
4393    Set output to "{output}## Main{nl}{mainSrc}".
4394    Show output.
4395"#;
4396    let combined = format!("{}\n{}\n{}\n## Main\n{}\n{}", CORE_TYPES_FOR_PE, pe, decompile, encoded, driver);
4397
4398    let result = run_logos_source(&combined)?;
4399    Ok(result)
4400}
4401
4402/// Run genuine PE(pe_source, pe_bti(targetExpr)) and return the specialized
4403/// cogen residual as LOGOS source code.
4404///
4405/// This is the actual Futamura Projection 3: the outer PE (pe_source) specializes
4406/// pe_bti (a full PE with memoization) with known state (empty env, empty funcs,
4407/// depth 200). pe_bti is structurally identical to pe_source with renamed entry
4408/// points (peExprB, peBlockB, etc.) — so this is genuinely PE(PE, PE).
4409///
4410/// The result is a specialized cogen: pe_bti's dispatch partially evaluated away,
4411/// producing a program that takes a target CExpr and compiles it.
4412pub fn genuine_projection3_residual() -> Result<String, String> {
4413    let pe_bti = pe_bti_source_text();
4414    let pe = pe_source_text();
4415    let decompile = decompile_source_text();
4416
4417    // pe_bti uses memoCache/callGuard instead of specResults/onStack
4418    let bti_types = CORE_TYPES_FOR_PE
4419        .replace("specResults", "memoCache")
4420        .replace("onStack", "callGuard");
4421
4422    // Build pe_bti program with a free variable target
4423    let program = format!(
4424        "{}\n{}\n## Main\n    Let env be a new Map of Text to CVal.\n    Let funcs be a new Map of Text to CFunc.\n    Let state be makePeState(env, funcs, 200).\n    Let result be peExprB(targetExpr, state).\n    Inspect result:\n        When CInt (v):\n            Show v.\n        Otherwise:\n            Show \"dynamic\".\n",
4425        bti_types, pe_bti
4426    );
4427
4428    // Encode pe_bti + driver as CProgram data (compact encoding)
4429    let encoded = encode_program_source_compact(&program)
4430        .map_err(|e| format!("Failed to encode pe_bti: {:?}", e))?;
4431
4432    // Driver: run PE, then decompile residual + specialized functions
4433    let driver = r#"    Let state be makePeState(a new Map of Text to CVal, encodedFuncMap, 200).
4434    Let residual be peBlock(encodedMain, state).
4435    Let nl be chr(10).
4436    Let mutable output be "".
4437    Let specFuncs be peFuncs(state).
4438    Let specNames be collectCallNames(residual).
4439    Repeat for sn in specNames:
4440        Let snKey be "{sn}".
4441        If specFuncs contains snKey:
4442            Let fdef be item snKey of specFuncs.
4443            Let funcSrc be decompileFunc(fdef).
4444            If the length of funcSrc is greater than 0:
4445                Set output to "{output}{funcSrc}{nl}".
4446    Let mainSrc be decompileBlock(residual, 0).
4447    Set output to "{output}## Main{nl}{mainSrc}".
4448    Show output.
4449"#;
4450    let combined = format!("{}\n{}\n{}\n## Main\n{}\n{}", CORE_TYPES_FOR_PE, pe, decompile, encoded, driver);
4451
4452    let result = run_logos_source(&combined)?;
4453    Ok(result)
4454}
4455
4456/// Real Futamura Projection 3: PE(PE, PE) = compiler_generator (cogen)
4457///
4458/// Returns the minimal PE (pe_mini) with entry points renamed to
4459/// cogenExpr/cogenBlock/extractReturn. The pe_mini is a clean-room
4460/// minimal PE with no memoization, no staticEnv, no specialization.
4461///
4462/// Returns ONLY the cogen source (no type definitions). Callers must
4463/// prepend appropriate CORE_TYPES.
4464pub fn projection3_source_real(_core_types: &str) -> Result<String, String> {
4465    let pe_mini = pe_mini_source_text();
4466
4467    // Rename entry points: peExprM→cogenExpr, peBlockM→cogenBlock, extractReturnM→extractReturn
4468    let cogen = replace_word(
4469        &replace_word(
4470            &replace_word(pe_mini, "peExprM", "cogenExpr"),
4471            "peBlockM", "cogenBlock",
4472        ),
4473        "extractReturnM", "extractReturn",
4474    );
4475
4476    Ok(cogen.to_string())
4477}
4478
4479fn replace_word(source: &str, from: &str, to: &str) -> String {
4480    let mut result = String::with_capacity(source.len());
4481    let mut remaining = source;
4482    while let Some(pos) = remaining.find(from) {
4483        let before = if pos > 0 { remaining.as_bytes()[pos - 1] } else { b' ' };
4484        let after_pos = pos + from.len();
4485        let after = if after_pos < remaining.len() { remaining.as_bytes()[after_pos] } else { b' ' };
4486        let is_word = !before.is_ascii_alphanumeric() && before != b'_'
4487            && !after.is_ascii_alphanumeric() && after != b'_';
4488        result.push_str(&remaining[..pos]);
4489        if is_word {
4490            result.push_str(to);
4491        } else {
4492            result.push_str(from);
4493        }
4494        remaining = &remaining[after_pos..];
4495    }
4496    result.push_str(remaining);
4497    result
4498}
4499
4500#[cfg(test)]
4501mod tests {
4502    use super::*;
4503
4504    #[test]
4505    fn test_compile_let_statement() {
4506        let source = "## Main\nLet x be 5.";
4507        let result = compile_to_rust(source);
4508        assert!(result.is_ok(), "Should compile: {:?}", result);
4509        let rust = result.unwrap();
4510        assert!(rust.contains("fn main()"));
4511        assert!(rust.contains("let x = 5;"));
4512    }
4513
4514    #[test]
4515    fn test_compile_return_statement() {
4516        let source = "## Main\nReturn 42.";
4517        let result = compile_to_rust(source);
4518        assert!(result.is_ok(), "Should compile: {:?}", result);
4519        let rust = result.unwrap();
4520        assert!(rust.contains("return 42;"));
4521    }
4522
4523}