Files
lux/src/codegen/c_backend.rs
Brandon Lucas 4e43d3d50d fix: C backend String.indexOf/lastIndexOf compilation (issue 8)
Three bugs fixed:
- Global let bindings always typed as LuxInt; now inferred from value
- Option inner type not tracked for function params; added
  var_option_inner_types map so match extraction uses correct type
- indexOf/lastIndexOf stored ints as (void*)(intptr_t) but extraction
  expected boxed pointers; now uses lux_box_int consistently

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 21:10:52 -05:00

6656 lines
316 KiB
Rust

//! C code generation backend for Lux
//!
//! Compiles Lux programs to C code that can be compiled with GCC/Clang.
//!
//! ## Compilation Strategy
//!
//! Lux source → Parse → Type check → Generate C → Invoke cc/gcc/clang → Binary
//!
//! This approach is similar to Koka, Nim, and Chicken Scheme. It leverages
//! decades of C compiler optimizations (GCC/Clang) without reimplementing them.
//!
//! ## Runtime Type Representations
//!
//! | Lux Type | C Type |
//! |----------|--------|
//! | Int | `int64_t` (LuxInt) |
//! | Float | `double` (LuxFloat) |
//! | Bool | `bool` (LuxBool) |
//! | String | `char*` (LuxString) |
//! | Closure | `struct {void* env, void* fn_ptr}` (LuxClosure) |
//! | ADT | Tagged union (enum tag + union of variant structs) |
//! | List | `struct {void** elements, int64_t length, capacity}` (LuxList) |
//!
//! ## Supported Features
//!
//! - **Functions**: Direct function calls with name mangling (`foo` → `foo_lux`)
//! - **Closures**: Heap-allocated environment struct + function pointer
//! - **ADTs**: Tagged unions with exhaustive pattern matching
//! - **Pattern Matching**: Compiles to if/else chains checking tags
//! - **Lists**: Dynamic arrays with void* boxing for generic elements
//!
//! ## Current Limitations
//!
//! - **Memory**: No deallocation - everything leaks (GC/RC not yet implemented)
//! - **Effects**: Only `Console.print` supported (hardcoded to printf)
//! - **If/else side effects**: Uses ternary `?:`, so both branches execute
//! during codegen if they contain effects like Console.print
//!
//! ## Future Work (see docs/C_BACKEND.md)
//!
//! - Evidence passing for zero-cost effects (like Koka)
//! - Perceus-style reference counting for memory management
//! - More effects (File, Http, etc.)
use crate::ast::*;
use crate::modules::Module;
use std::collections::{HashSet, HashMap};
use std::fmt::Write;
/// C code generation errors
#[derive(Debug, Clone)]
pub struct CGenError {
pub message: String,
#[allow(dead_code)]
pub span: Option<Span>,
}
/// Information about a closure to be emitted
#[derive(Debug, Clone)]
struct ClosureInfo {
id: usize,
env_fields: Vec<(String, String)>, // (var_name, c_type)
params: Vec<(String, String)>, // (param_name, c_type)
return_type: String,
body: Expr,
}
/// Information about an RC-managed variable in scope
#[derive(Debug, Clone)]
struct RcVariable {
name: String, // Variable name in generated C code
c_type: String, // C type (for documentation/debugging)
adt_type_name: Option<String>, // If Some, this is an ADT that needs field cleanup
}
impl std::fmt::Display for CGenError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "C codegen error: {}", self.message)
}
}
impl std::error::Error for CGenError {}
/// Behavioral properties for a function
#[derive(Debug, Clone, Default)]
struct FunctionBehavior {
is_pure: bool,
is_total: bool,
is_idempotent: bool,
is_deterministic: bool,
is_commutative: bool,
}
/// The C backend code generator
pub struct CBackend {
/// Generated C code
output: String,
/// Current indentation level
indent: usize,
/// Known function names for forward declarations
functions: HashSet<String>,
/// Type definitions we've emitted
types_emitted: HashSet<String>,
/// Counter for generating unique names
name_counter: usize,
/// Effects used in the program (for evidence struct)
effects_used: HashSet<String>,
/// Closures to emit (collected during expression generation)
closures: Vec<ClosureInfo>,
/// Variables in scope (for free variable analysis)
#[allow(dead_code)]
local_vars: HashSet<String>,
/// Functions that return closures
closure_returning_functions: HashSet<String>,
/// Mapping from variant names to their parent type name
variant_to_type: HashMap<String, String>,
/// Mapping from (type_name, variant_name) to field types
variant_field_types: HashMap<(String, String), Vec<String>>,
/// Functions that use effects (have evidence parameter)
effectful_functions: HashSet<String>,
/// Whether we're currently inside an effectful function (has evidence available)
has_evidence: bool,
/// Stack of scopes for RC management - each scope contains variables that need decref
rc_scopes: Vec<Vec<RcVariable>>,
/// Whether to emit memory tracking code for debugging
debug_rc: bool,
/// Mapping from function names to their C return types
function_return_types: HashMap<String, String>,
/// Mapping from function names to their parameter types
function_param_types: HashMap<String, Vec<String>>,
/// Type tags for ADT types (starting at 100)
adt_type_tags: HashMap<String, i32>,
/// Next available ADT type tag
next_adt_tag: i32,
/// ADT types that have pointer fields (need drop functions)
adt_with_pointers: HashSet<String>,
/// Variable types for type inference (variable name -> C type)
var_types: HashMap<String, String>,
/// Behavioral properties for functions (for optimization)
function_behaviors: HashMap<String, FunctionBehavior>,
/// Whether to enable behavioral type optimizations
enable_behavioral_optimizations: bool,
/// Mapping from (module_name, func_name) to mangled C function name for imported modules
module_functions: HashMap<(String, String), String>,
/// Set of module names that have been imported (for resolving calls)
imported_modules: HashSet<String>,
/// Variable name renames: Lux name → C variable name (for let binding name mangling)
var_renames: HashMap<String, String>,
/// Inner type for Option-typed variables (variable name -> inner C type, e.g. "LuxInt")
var_option_inner_types: HashMap<String, String>,
}
impl CBackend {
pub fn new() -> Self {
Self {
output: String::new(),
indent: 0,
functions: HashSet::new(),
types_emitted: HashSet::new(),
name_counter: 0,
effects_used: HashSet::new(),
closures: Vec::new(),
local_vars: HashSet::new(),
closure_returning_functions: HashSet::new(),
variant_to_type: HashMap::new(),
variant_field_types: HashMap::new(),
effectful_functions: HashSet::new(),
has_evidence: false,
rc_scopes: Vec::new(),
debug_rc: false, // Set to true to enable RC leak tracking
function_return_types: HashMap::new(),
function_param_types: HashMap::new(),
adt_type_tags: HashMap::new(),
next_adt_tag: 100, // ADT tags start at 100
adt_with_pointers: HashSet::new(),
var_types: HashMap::new(),
function_behaviors: HashMap::new(),
enable_behavioral_optimizations: true,
module_functions: HashMap::new(),
imported_modules: HashSet::new(),
var_renames: HashMap::new(),
var_option_inner_types: HashMap::new(),
}
}
/// Collect behavioral properties from function declaration
fn collect_behavioral_properties(&mut self, f: &FunctionDecl) {
let mut behavior = FunctionBehavior::default();
for prop in &f.properties {
match prop {
BehavioralProperty::Pure => behavior.is_pure = true,
BehavioralProperty::Total => behavior.is_total = true,
BehavioralProperty::Idempotent => behavior.is_idempotent = true,
BehavioralProperty::Deterministic => behavior.is_deterministic = true,
BehavioralProperty::Commutative => behavior.is_commutative = true,
}
}
if behavior.is_pure || behavior.is_total || behavior.is_idempotent
|| behavior.is_deterministic || behavior.is_commutative {
self.function_behaviors.insert(f.name.name.clone(), behavior);
}
}
/// Generate C code from a Lux program
pub fn generate(&mut self, program: &Program, modules: &HashMap<String, Module>) -> Result<String, CGenError> {
self.output.clear();
self.closures.clear();
self.emit_prelude();
// Register built-in variant → type mappings
self.variant_to_type.insert("Some".to_string(), "Option".to_string());
self.variant_to_type.insert("None".to_string(), "Option".to_string());
self.variant_to_type.insert("Ok".to_string(), "Result".to_string());
self.variant_to_type.insert("Err".to_string(), "Result".to_string());
// Register built-in variant field types
self.variant_field_types.insert(("Option".to_string(), "Some".to_string()), vec!["void*".to_string()]);
self.variant_field_types.insert(("Option".to_string(), "None".to_string()), vec![]);
self.variant_field_types.insert(("Result".to_string(), "Ok".to_string()), vec!["void*".to_string()]);
self.variant_field_types.insert(("Result".to_string(), "Err".to_string()), vec!["void*".to_string()]);
// Process imported modules before the main program
self.process_imported_modules(program, modules)?;
// First pass: collect all function names, types, and effects
for decl in &program.declarations {
match decl {
Declaration::Function(f) => {
self.functions.insert(f.name.name.clone());
// Store function return type for call inference
if let Ok(ret_type) = self.type_expr_to_c(&f.return_type) {
self.function_return_types.insert(f.name.name.clone(), ret_type);
}
// Store function parameter types for usage inference
let param_types: Vec<String> = f.params.iter()
.filter_map(|p| self.type_expr_to_c(&p.typ).ok())
.collect();
self.function_param_types.insert(f.name.name.clone(), param_types);
// Check if this function returns a closure
if matches!(&f.return_type, TypeExpr::Function { .. }) {
self.closure_returning_functions.insert(f.name.name.clone());
}
// Check if this function uses effects (evidence passing)
if !f.effects.is_empty() {
self.effectful_functions.insert(f.name.name.clone());
}
// Collect behavioral properties for optimization
self.collect_behavioral_properties(f);
}
Declaration::Type(t) => {
self.collect_type(t)?;
}
_ => {}
}
}
// Emit type definitions (modules first, then main program)
self.emit_module_type_definitions(modules, program)?;
self.emit_type_definitions(program)?;
// Emit ADT drop function (after type definitions so we know the ADT structure)
self.emit_adt_drop_function();
// Emit forward declarations for module functions, then main program
self.emit_module_forward_declarations()?;
self.emit_forward_declarations(program)?;
// Generate function bodies to a temporary buffer
// This collects closures that need to be emitted
let saved_output = std::mem::take(&mut self.output);
// Emit module function bodies first
self.emit_module_function_bodies(modules, program)?;
for decl in &program.declarations {
match decl {
Declaration::Function(f) => {
self.emit_function(f)?;
}
Declaration::Let(let_decl) => {
// Skip run expressions - they're handled in the main wrapper
if !matches!(&let_decl.value, Expr::Run { .. }) {
self.emit_global_let(let_decl)?;
}
}
_ => {}
}
}
let function_output = std::mem::replace(&mut self.output, saved_output);
// Now emit closure definitions (collected during function generation)
self.emit_closures()?;
// Append the function definitions
self.output.push_str(&function_output);
// Emit main wrapper if there's a main function or top-level expressions
self.emit_main_wrapper(program)?;
Ok(self.output.clone())
}
/// Process imported modules: collect function names, types, and mappings
fn process_imported_modules(&mut self, program: &Program, modules: &HashMap<String, Module>) -> Result<(), CGenError> {
// Build mapping from import alias → module path
let mut import_aliases: HashMap<String, String> = HashMap::new();
for import in &program.imports {
let module_path = import.path.segments.iter()
.map(|s| s.name.as_str())
.collect::<Vec<_>>()
.join("/");
let alias = if let Some(ref a) = import.alias {
a.name.clone()
} else {
import.path.segments.last()
.map(|s| s.name.clone())
.unwrap_or_else(|| module_path.clone())
};
import_aliases.insert(alias.clone(), module_path.clone());
self.imported_modules.insert(alias);
}
// Process each imported module (and transitive imports)
let mut processed = HashSet::new();
for import in &program.imports {
let module_path = import.path.segments.iter()
.map(|s| s.name.as_str())
.collect::<Vec<_>>()
.join("/");
let alias = if let Some(ref a) = import.alias {
a.name.clone()
} else {
import.path.segments.last()
.map(|s| s.name.clone())
.unwrap_or_else(|| module_path.clone())
};
self.process_single_module(&alias, &module_path, modules, &mut processed)?;
}
Ok(())
}
/// Process a single module and its transitive imports
fn process_single_module(
&mut self,
alias: &str,
module_path: &str,
modules: &HashMap<String, Module>,
processed: &mut HashSet<String>,
) -> Result<(), CGenError> {
if processed.contains(module_path) {
return Ok(());
}
processed.insert(module_path.to_string());
let module = match modules.get(module_path) {
Some(m) => m,
None => return Ok(()), // Module not found - might be a built-in
};
// Process transitive imports first
for sub_import in &module.program.imports {
let sub_path = sub_import.path.segments.iter()
.map(|s| s.name.as_str())
.collect::<Vec<_>>()
.join("/");
let sub_alias = sub_import.path.segments.last()
.map(|s| s.name.clone())
.unwrap_or_else(|| sub_path.clone());
self.process_single_module(&sub_alias, &sub_path, modules, processed)?;
}
// Collect types from the module
for decl in &module.program.declarations {
if let Declaration::Type(t) = decl {
self.collect_type(t)?;
}
}
// Collect functions from the module
for decl in &module.program.declarations {
if let Declaration::Function(f) = decl {
if f.visibility == Visibility::Public || module.exports.contains(&f.name.name) {
let mangled = format!("{}_{}_lux", alias, f.name.name);
self.functions.insert(mangled.clone());
self.module_functions.insert(
(alias.to_string(), f.name.name.clone()),
mangled.clone(),
);
// Store return type
if let Ok(ret_type) = self.type_expr_to_c(&f.return_type) {
self.function_return_types.insert(mangled.clone(), ret_type);
}
// Store param types
let param_types: Vec<String> = f.params.iter()
.filter_map(|p| self.type_expr_to_c(&p.typ).ok())
.collect();
self.function_param_types.insert(mangled.clone(), param_types);
// Check for closures
if matches!(&f.return_type, TypeExpr::Function { .. }) {
self.closure_returning_functions.insert(mangled.clone());
}
// Check for effects
if !f.effects.is_empty() {
self.effectful_functions.insert(mangled.clone());
}
self.collect_behavioral_properties(f);
}
}
}
Ok(())
}
/// Emit type definitions from imported modules
fn emit_module_type_definitions(&mut self, modules: &HashMap<String, Module>, program: &Program) -> Result<(), CGenError> {
let mut processed = HashSet::new();
for import in &program.imports {
let module_path = import.path.segments.iter()
.map(|s| s.name.as_str())
.collect::<Vec<_>>()
.join("/");
self.emit_module_types_recursive(&module_path, modules, &mut processed)?;
}
Ok(())
}
fn emit_module_types_recursive(
&mut self,
module_path: &str,
modules: &HashMap<String, Module>,
processed: &mut HashSet<String>,
) -> Result<(), CGenError> {
if processed.contains(module_path) {
return Ok(());
}
processed.insert(module_path.to_string());
let module = match modules.get(module_path) {
Some(m) => m,
None => return Ok(()),
};
// Process transitive imports first
for sub_import in &module.program.imports {
let sub_path = sub_import.path.segments.iter()
.map(|s| s.name.as_str())
.collect::<Vec<_>>()
.join("/");
self.emit_module_types_recursive(&sub_path, modules, processed)?;
}
for decl in &module.program.declarations {
if let Declaration::Type(t) = decl {
self.emit_type_def(t)?;
}
}
Ok(())
}
/// Emit forward declarations for module functions
fn emit_module_forward_declarations(&mut self) -> Result<(), CGenError> {
if self.module_functions.is_empty() {
return Ok(());
}
self.writeln("// === Imported Module Forward Declarations ===");
// Forward declarations are emitted from stored info
// We iterate module_functions and use function_return_types/function_param_types
let entries: Vec<_> = self.module_functions.values().cloned().collect();
for mangled in &entries {
let ret_type = self.function_return_types.get(mangled)
.cloned()
.unwrap_or_else(|| "LuxInt".to_string());
let param_types = self.function_param_types.get(mangled)
.cloned()
.unwrap_or_default();
let is_effectful = self.effectful_functions.contains(mangled);
let params_str = if param_types.is_empty() && !is_effectful {
"void".to_string()
} else {
let mut parts = Vec::new();
if is_effectful {
parts.push("LuxEvidence* ev".to_string());
}
for (i, pt) in param_types.iter().enumerate() {
parts.push(format!("{} param{}", pt, i));
}
if parts.is_empty() {
"void".to_string()
} else {
parts.join(", ")
}
};
self.writeln(&format!("{} {}({});", ret_type, mangled, params_str));
}
self.writeln("");
Ok(())
}
/// Emit function bodies for imported modules
fn emit_module_function_bodies(
&mut self,
modules: &HashMap<String, Module>,
program: &Program,
) -> Result<(), CGenError> {
if self.module_functions.is_empty() {
return Ok(());
}
self.writeln("// === Imported Module Function Bodies ===");
self.writeln("");
let mut processed = HashSet::new();
for import in &program.imports {
let module_path = import.path.segments.iter()
.map(|s| s.name.as_str())
.collect::<Vec<_>>()
.join("/");
let alias = if let Some(ref a) = import.alias {
a.name.clone()
} else {
import.path.segments.last()
.map(|s| s.name.clone())
.unwrap_or_else(|| module_path.clone())
};
self.emit_module_bodies_recursive(&alias, &module_path, modules, &mut processed)?;
}
Ok(())
}
fn emit_module_bodies_recursive(
&mut self,
alias: &str,
module_path: &str,
modules: &HashMap<String, Module>,
processed: &mut HashSet<String>,
) -> Result<(), CGenError> {
if processed.contains(module_path) {
return Ok(());
}
processed.insert(module_path.to_string());
let module = match modules.get(module_path) {
Some(m) => m.clone(),
None => return Ok(()),
};
// Process transitive imports first
for sub_import in &module.program.imports {
let sub_path = sub_import.path.segments.iter()
.map(|s| s.name.as_str())
.collect::<Vec<_>>()
.join("/");
let sub_alias = sub_import.path.segments.last()
.map(|s| s.name.clone())
.unwrap_or_else(|| sub_path.clone());
self.emit_module_bodies_recursive(&sub_alias, &sub_path, modules, processed)?;
}
for decl in &module.program.declarations {
if let Declaration::Function(f) = decl {
if f.visibility == Visibility::Public || module.exports.contains(&f.name.name) {
let mangled = format!("{}_{}_lux", alias, f.name.name);
self.emit_function_with_name(f, &mangled)?;
}
}
}
Ok(())
}
/// Emit a function body with a custom mangled name
fn emit_function_with_name(&mut self, func: &FunctionDecl, mangled_name: &str) -> Result<(), CGenError> {
let ret_type = self.type_expr_to_c(&func.return_type)?;
let params = self.emit_params(&func.params)?;
let is_effectful = !func.effects.is_empty();
let full_params = if is_effectful {
if params == "void" {
"LuxEvidence* ev".to_string()
} else {
format!("LuxEvidence* ev, {}", params)
}
} else {
params
};
self.writeln(&format!("{} {}({}) {{", ret_type, mangled_name, full_params));
self.indent += 1;
// Register parameter types so match/option inference can use them
for p in &func.params {
if let Ok(c_type) = self.type_expr_to_c(&p.typ) {
let escaped = self.escape_c_keyword(&p.name.name);
self.var_types.insert(escaped.clone(), c_type);
// Track Option inner types for match pattern extraction
if let Some(inner) = self.option_inner_type_from_type_expr(&p.typ) {
self.var_option_inner_types.insert(escaped, inner);
}
}
}
let old_has_evidence = self.has_evidence;
if is_effectful {
self.has_evidence = true;
}
let body_result = self.emit_expr(&func.body)?;
if ret_type != "void" && ret_type != "LuxUnit" {
self.writeln(&format!("return {};", body_result));
} else if ret_type == "LuxUnit" {
self.writeln(&format!("(void){};", body_result));
self.writeln("return 0;");
}
self.has_evidence = old_has_evidence;
self.indent -= 1;
self.writeln("}");
self.writeln("");
Ok(())
}
/// Emit all collected closure definitions
fn emit_closures(&mut self) -> Result<(), CGenError> {
if self.closures.is_empty() {
return Ok(());
}
self.writeln("// === Closure Definitions ===");
self.writeln("");
// Take closures to avoid borrow issues
let closures = std::mem::take(&mut self.closures);
for closure in &closures {
// Emit environment struct
if closure.env_fields.is_empty() {
// No captured variables - no env struct needed
} else {
self.writeln(&format!("typedef struct {{"));
self.indent += 1;
for (name, typ) in &closure.env_fields {
self.writeln(&format!("{} {};", typ, name));
}
self.indent -= 1;
self.writeln(&format!("}} LuxEnv_{};", closure.id));
self.writeln("");
}
// Emit lambda implementation
let params_str = if closure.params.is_empty() {
"void* _env".to_string()
} else {
let ps: Vec<String> = closure.params.iter()
.map(|(name, typ)| format!("{} {}", typ, name))
.collect();
format!("void* _env, {}", ps.join(", "))
};
self.writeln(&format!("static {} lambda_{}({}) {{",
closure.return_type, closure.id, params_str));
self.indent += 1;
// Cast env pointer if we have captured variables
if !closure.env_fields.is_empty() {
self.writeln(&format!("LuxEnv_{}* env = (LuxEnv_{}*)_env;",
closure.id, closure.id));
}
// Emit body with env-> substitution for captured variables
let body_result = self.emit_closure_body(&closure.body, &closure.env_fields)?;
self.writeln(&format!("return {};", body_result));
self.indent -= 1;
self.writeln("}");
self.writeln("");
}
// Restore closures (in case more are collected)
self.closures = closures;
Ok(())
}
/// Emit closure body with environment variable substitution
fn emit_closure_body(&mut self, expr: &Expr, env_fields: &[(String, String)]) -> Result<String, CGenError> {
// Create a set of captured variable names for quick lookup
let captured: HashSet<&str> = env_fields.iter().map(|(n, _)| n.as_str()).collect();
self.emit_expr_with_env(expr, &captured)
}
/// Emit expression, substituting captured variables with env->name
fn emit_expr_with_env(&mut self, expr: &Expr, captured: &HashSet<&str>) -> Result<String, CGenError> {
match expr {
Expr::Var(ident) => {
// Check if this is a unit constructor (no-argument variant)
if let Some(type_name) = self.variant_to_type.get(&ident.name).cloned() {
let variant_name = &ident.name;
return Ok(format!("({}){{{}_TAG_{}}}", type_name, type_name, variant_name.to_uppercase()));
}
let escaped = self.escape_c_keyword(&ident.name);
if captured.contains(ident.name.as_str()) {
Ok(format!("env->{}", escaped))
} else if let Some(renamed) = self.var_renames.get(&ident.name) {
Ok(renamed.clone())
} else if self.functions.contains(&ident.name) {
Ok(self.mangle_name(&ident.name))
} else {
Ok(escaped)
}
}
Expr::Literal(lit) => self.emit_literal(lit),
Expr::BinaryOp { op, left, right, .. } => {
let l = self.emit_expr_with_env(left, captured)?;
let r = self.emit_expr_with_env(right, captured)?;
// Check for string comparison - use strcmp instead of pointer comparison
if matches!(op, BinaryOp::Eq | BinaryOp::Ne) {
let left_is_string = self.infer_expr_type(left).as_deref() == Some("LuxString");
let right_is_string = self.infer_expr_type(right).as_deref() == Some("LuxString");
if left_is_string || right_is_string {
if matches!(op, BinaryOp::Eq) {
return Ok(format!("(strcmp({}, {}) == 0)", l, r));
} else {
return Ok(format!("(strcmp({}, {}) != 0)", l, r));
}
}
}
// Check for string concatenation - use lux_string_concat instead of +
if matches!(op, BinaryOp::Add | BinaryOp::Concat) {
let left_is_string = self.infer_expr_type(left).as_deref() == Some("LuxString");
let right_is_string = self.infer_expr_type(right).as_deref() == Some("LuxString");
if left_is_string || right_is_string || matches!(op, BinaryOp::Concat) {
return Ok(format!("lux_string_concat({}, {})", l, r));
}
}
let op_str = match op {
BinaryOp::Add => "+",
BinaryOp::Sub => "-",
BinaryOp::Mul => "*",
BinaryOp::Div => "/",
BinaryOp::Mod => "%",
BinaryOp::Eq => "==",
BinaryOp::Ne => "!=",
BinaryOp::Lt => "<",
BinaryOp::Le => "<=",
BinaryOp::Gt => ">",
BinaryOp::Ge => ">=",
BinaryOp::And => "&&",
BinaryOp::Or => "||",
_ => return Err(CGenError {
message: format!("Unsupported binary operator in closure"),
span: None,
}),
};
Ok(format!("({} {} {})", l, op_str, r))
}
Expr::UnaryOp { op, operand, .. } => {
let o = self.emit_expr_with_env(operand, captured)?;
let op_str = match op {
UnaryOp::Neg => "-",
UnaryOp::Not => "!",
};
Ok(format!("({}{})", op_str, o))
}
Expr::If { condition, then_branch, else_branch, .. } => {
let c = self.emit_expr_with_env(condition, captured)?;
let t = self.emit_expr_with_env(then_branch, captured)?;
let e = self.emit_expr_with_env(else_branch, captured)?;
Ok(format!("({} ? {} : {})", c, t, e))
}
Expr::Call { func, args, .. } => {
match func.as_ref() {
Expr::Var(ident) if self.variant_to_type.contains_key(&ident.name) => {
// ADT constructor call inside closure
let type_name = self.variant_to_type.get(&ident.name).unwrap().clone();
let variant_name = ident.name.clone();
let variant_lower = variant_name.to_lowercase();
let variant_upper = variant_name.to_uppercase();
// Emit args with env substitution for captured variables
let arg_strs: Result<Vec<_>, _> = args.iter()
.map(|a| self.emit_expr_with_env(a, captured))
.collect();
let arg_values = arg_strs?;
if arg_values.is_empty() {
Ok(format!("({}){{{}_TAG_{}}}", type_name, type_name, variant_upper))
} else {
let field_inits: Vec<String> = arg_values.iter()
.enumerate()
.map(|(i, v)| format!(".field{} = {}", i, v))
.collect();
Ok(format!("({}){{{}_TAG_{}, .data.{} = {{{}}}}}",
type_name, type_name, variant_upper,
variant_lower, field_inits.join(", ")))
}
}
Expr::Var(ident) if self.functions.contains(&ident.name) => {
let arg_strs: Result<Vec<_>, _> = args.iter()
.map(|a| self.emit_expr_with_env(a, captured))
.collect();
let args_str = arg_strs?.join(", ");
let c_func_name = self.mangle_name(&ident.name);
// Pass evidence if this function uses effects
let is_effectful = self.effectful_functions.contains(&ident.name);
if is_effectful {
// Inside closures, use default evidence
if args_str.is_empty() {
Ok(format!("{}(&default_evidence)", c_func_name))
} else {
Ok(format!("{}(&default_evidence, {})", c_func_name, args_str))
}
} else {
Ok(format!("{}({})", c_func_name, args_str))
}
}
_ => {
let arg_strs: Result<Vec<_>, _> = args.iter()
.map(|a| self.emit_expr_with_env(a, captured))
.collect();
let args_str = arg_strs?.join(", ");
let closure_expr = self.emit_expr_with_env(func, captured)?;
let param_types: Vec<&str> = args.iter().map(|_| "LuxInt").collect();
let params_str = if param_types.is_empty() {
String::new()
} else {
format!(", {}", param_types.join(", "))
};
let args_with_env = if args_str.is_empty() {
format!("({})->env", closure_expr)
} else {
format!("({})->env, {}", closure_expr, args_str)
};
Ok(format!("((LuxInt(*)(void*{}))({})->fn_ptr)({})",
params_str, closure_expr, args_with_env))
}
}
}
_ => {
// For other expressions, fall back to regular emit_expr
// This may not handle captured variables correctly for complex expressions
self.emit_expr(expr)
}
}
}
fn emit_prelude(&mut self) {
self.writeln("// Generated by Lux compiler");
self.writeln("// Do not edit - regenerate from .lux source");
self.writeln("");
self.writeln("#include <stdint.h>");
self.writeln("#include <stdbool.h>");
self.writeln("#include <stdio.h>");
self.writeln("#include <stdlib.h>");
self.writeln("#include <string.h>");
self.writeln("#include <math.h>");
self.writeln("");
self.writeln("// === Lux Runtime Types ===");
self.writeln("");
self.writeln("typedef int64_t LuxInt;");
self.writeln("typedef double LuxFloat;");
self.writeln("typedef bool LuxBool;");
self.writeln("typedef char* LuxString;");
self.writeln("typedef void* LuxUnit;");
self.writeln("");
self.writeln("// Forward struct declarations for drop specialization");
self.writeln("typedef struct LuxList_s LuxList;");
self.writeln("typedef struct LuxClosure_s LuxClosure;");
self.writeln("");
self.writeln("// Closure representation: env pointer + function pointer");
self.writeln("struct LuxClosure_s { void* env; void* fn_ptr; };");
self.writeln("");
self.writeln("// List struct body (typedef declared above)");
self.writeln("struct LuxList_s {");
self.writeln(" void** elements;");
self.writeln(" int64_t length;");
self.writeln(" int64_t capacity;");
self.writeln("};");
self.writeln("");
self.writeln("// Map struct (linear-scan key-value table, string keys)");
self.writeln("typedef struct {");
self.writeln(" LuxString* keys;");
self.writeln(" void** values;");
self.writeln(" int64_t length;");
self.writeln(" int64_t capacity;");
self.writeln("} LuxMap;");
self.writeln("");
self.writeln("// === Reference Counting Infrastructure ===");
self.writeln("// Perceus-inspired RC system for automatic memory management.");
self.writeln("// See docs/REFERENCE_COUNTING.md for details.");
self.writeln("");
self.writeln("// Type tags for polymorphic drop - identifies the type of RC object");
self.writeln("typedef enum {");
self.writeln(" LUX_TAG_STRING = 1,");
self.writeln(" LUX_TAG_LIST = 2,");
self.writeln(" LUX_TAG_CLOSURE = 3,");
self.writeln(" LUX_TAG_BOXED_INT = 4,");
self.writeln(" LUX_TAG_BOXED_BOOL = 5,");
self.writeln(" LUX_TAG_BOXED_FLOAT = 6,");
self.writeln(" LUX_TAG_ENV = 7, // Closure environment");
self.writeln(" LUX_TAG_ADT = 100 // ADT types start at 100");
self.writeln("} LuxTypeTag;");
self.writeln("");
self.writeln("// Magic number to identify RC-managed memory (allows safe decref of static strings)");
self.writeln("#define LUX_RC_MAGIC 0x4C555852 // 'LUXR' in ASCII");
self.writeln("");
self.writeln("// RC header placed before every heap-allocated object");
self.writeln("typedef struct {");
self.writeln(" int32_t magic; // Magic number to identify RC-managed memory");
self.writeln(" int32_t rc; // Reference count");
self.writeln(" int32_t tag; // Type tag for polymorphic drop");
self.writeln(" int32_t _pad; // Padding for alignment");
self.writeln("} LuxRcHeader;");
self.writeln("");
self.writeln("// Get the RC header from an object pointer");
self.writeln("#define LUX_RC_HEADER(ptr) (((LuxRcHeader*)(ptr)) - 1)");
self.writeln("");
self.writeln("// Check if a pointer is RC-managed (has valid magic number)");
self.writeln("#define LUX_IS_RC_MANAGED(ptr) (ptr && LUX_RC_HEADER(ptr)->magic == LUX_RC_MAGIC)");
self.writeln("");
self.writeln("// Forward declarations of drop functions");
self.writeln("static void lux_drop(void* ptr, int32_t tag);");
self.writeln("static void lux_drop_adt(void* ptr, int32_t tag);");
self.writeln("");
// Memory tracking counters (must be before lux_rc_alloc which uses them)
if self.debug_rc {
self.writeln("// Memory tracking counters");
self.writeln("static int64_t lux_rc_alloc_count = 0;");
self.writeln("static int64_t lux_rc_free_count = 0;");
self.writeln("");
self.writeln("// FBIP (Functional But In-Place) optimization counters");
self.writeln("static int64_t lux_fbip_reuse_count = 0;");
self.writeln("static int64_t lux_fbip_copy_count = 0;");
self.writeln("");
}
self.writeln("// Allocate RC-managed memory with initial refcount of 1");
self.writeln("static void* lux_rc_alloc(size_t size, int32_t tag) {");
self.writeln(" LuxRcHeader* hdr = (LuxRcHeader*)malloc(sizeof(LuxRcHeader) + size);");
self.writeln(" if (!hdr) return NULL;");
self.writeln(" hdr->magic = LUX_RC_MAGIC;");
self.writeln(" hdr->rc = 1;");
self.writeln(" hdr->tag = tag;");
if self.debug_rc {
self.writeln(" lux_rc_alloc_count++;");
}
self.writeln(" return hdr + 1; // Return pointer after header");
self.writeln("}");
self.writeln("");
self.writeln("// Increment reference count (only for RC-managed memory)");
self.writeln("static inline void lux_incref(void* ptr) {");
self.writeln(" if (LUX_IS_RC_MANAGED(ptr)) LUX_RC_HEADER(ptr)->rc++;");
self.writeln("}");
self.writeln("");
self.writeln("// Decrement reference count, call drop if zero (generic)");
self.writeln("static inline void lux_decref(void* ptr) {");
self.writeln(" if (LUX_IS_RC_MANAGED(ptr)) {");
self.writeln(" LuxRcHeader* hdr = LUX_RC_HEADER(ptr);");
self.writeln(" if (--hdr->rc == 0) {");
self.writeln(" lux_drop(ptr, hdr->tag);");
self.writeln(" }");
self.writeln(" }");
self.writeln("}");
self.writeln("");
// Forward declarations for specialized decref functions (defined after struct bodies)
self.writeln("// Forward declarations for drop specialization (defined after struct bodies)");
self.writeln("static inline void lux_decref_list(LuxList* list);");
self.writeln("static inline void lux_decref_closure(LuxClosure* closure);");
self.writeln("static inline void lux_decref_string(LuxString str);");
self.writeln("static inline void lux_decref_boxed(void* ptr);");
self.writeln("");
self.writeln("// Get current reference count (for debugging)");
self.writeln("static inline int32_t lux_refcount(void* ptr) {");
self.writeln(" return ptr ? LUX_RC_HEADER(ptr)->rc : 0;");
self.writeln("}");
self.writeln("");
// Memory leak check function (only if debug_rc is enabled)
if self.debug_rc {
self.writeln("// === Memory Tracking (Debug) ===");
self.writeln("static void lux_rc_check_leaks(void) {");
self.writeln(" if (lux_rc_alloc_count != lux_rc_free_count) {");
self.writeln(" fprintf(stderr, \"[RC] LEAK DETECTED: %lld allocs, %lld frees, %lld leaked\\n\",");
self.writeln(" (long long)lux_rc_alloc_count, (long long)lux_rc_free_count,");
self.writeln(" (long long)(lux_rc_alloc_count - lux_rc_free_count));");
self.writeln(" } else {");
self.writeln(" fprintf(stderr, \"[RC] No leaks: %lld allocs, %lld frees\\n\",");
self.writeln(" (long long)lux_rc_alloc_count, (long long)lux_rc_free_count);");
self.writeln(" }");
self.writeln(" // Print FBIP optimization stats");
self.writeln(" if (lux_fbip_reuse_count > 0 || lux_fbip_copy_count > 0) {");
self.writeln(" fprintf(stderr, \"[FBIP] %lld reuses, %lld copies\\n\",");
self.writeln(" (long long)lux_fbip_reuse_count, (long long)lux_fbip_copy_count);");
self.writeln(" }");
self.writeln("}");
self.writeln("");
}
self.writeln("// === String Operations ===");
self.writeln("// Dynamically created strings are RC-managed.");
self.writeln("// Static string literals from source code are NOT RC-managed.");
self.writeln("");
self.writeln("static LuxString lux_string_concat(LuxString a, LuxString b) {");
self.writeln(" size_t len_a = strlen(a);");
self.writeln(" size_t len_b = strlen(b);");
self.writeln(" LuxString result = (LuxString)lux_rc_alloc(len_a + len_b + 1, LUX_TAG_STRING);");
self.writeln(" memcpy(result, a, len_a);");
self.writeln(" memcpy(result + len_a, b, len_b + 1);");
self.writeln(" return result;");
self.writeln("}");
self.writeln("");
self.writeln("static LuxString lux_int_to_string(LuxInt n) {");
self.writeln(" char buffer[32];");
self.writeln(" snprintf(buffer, sizeof(buffer), \"%lld\", (long long)n);");
self.writeln(" size_t len = strlen(buffer);");
self.writeln(" LuxString result = (LuxString)lux_rc_alloc(len + 1, LUX_TAG_STRING);");
self.writeln(" memcpy(result, buffer, len + 1);");
self.writeln(" return result;");
self.writeln("}");
self.writeln("");
self.writeln("static LuxString lux_float_to_string(LuxFloat f) {");
self.writeln(" char buffer[64];");
self.writeln(" snprintf(buffer, sizeof(buffer), \"%g\", f);");
self.writeln(" size_t len = strlen(buffer);");
self.writeln(" LuxString result = (LuxString)lux_rc_alloc(len + 1, LUX_TAG_STRING);");
self.writeln(" memcpy(result, buffer, len + 1);");
self.writeln(" return result;");
self.writeln("}");
self.writeln("");
self.writeln("static LuxBool lux_string_eq(LuxString a, LuxString b) {");
self.writeln(" return strcmp(a, b) == 0;");
self.writeln("}");
self.writeln("");
self.writeln("// Alias for memoization key comparison");
self.writeln("static inline LuxBool lux_string_equals(LuxString a, LuxString b) {");
self.writeln(" return strcmp(a, b) == 0;");
self.writeln("}");
self.writeln("");
self.writeln("// String hash for memoization (djb2 algorithm)");
self.writeln("static inline size_t lux_string_hash(LuxString s) {");
self.writeln(" size_t hash = 5381;");
self.writeln(" unsigned char c;");
self.writeln(" while ((c = (unsigned char)*s++)) {");
self.writeln(" hash = ((hash << 5) + hash) + c;");
self.writeln(" }");
self.writeln(" return hash;");
self.writeln("}");
self.writeln("");
self.writeln("static LuxBool lux_string_contains(LuxString haystack, LuxString needle) {");
self.writeln(" return strstr(haystack, needle) != NULL;");
self.writeln("}");
self.writeln("");
self.writeln("static LuxString lux_string_dup(LuxString s) {");
self.writeln(" if (!s) return \"\";");
self.writeln(" size_t len = strlen(s);");
self.writeln(" LuxString result = (LuxString)lux_rc_alloc(len + 1, LUX_TAG_STRING);");
self.writeln(" memcpy(result, s, len + 1);");
self.writeln(" return result;");
self.writeln("}");
self.writeln("");
self.writeln("// === Built-in Effect Implementations ===");
self.writeln("");
self.writeln("static void lux_console_print(LuxString msg) {");
self.writeln(" printf(\"%s\\n\", msg);");
self.writeln("}");
self.writeln("");
self.writeln("static LuxString lux_console_readLine(void) {");
self.writeln(" char buffer[4096];");
self.writeln(" if (fgets(buffer, sizeof(buffer), stdin)) {");
self.writeln(" size_t len = strlen(buffer);");
self.writeln(" if (len > 0 && buffer[len-1] == '\\n') buffer[len-1] = '\\0';");
self.writeln(" len = strlen(buffer);");
self.writeln(" LuxString result = (LuxString)lux_rc_alloc(len + 1, LUX_TAG_STRING);");
self.writeln(" memcpy(result, buffer, len + 1);");
self.writeln(" return result;");
self.writeln(" }");
self.writeln(" LuxString result = (LuxString)lux_rc_alloc(1, LUX_TAG_STRING);");
self.writeln(" result[0] = '\\0';");
self.writeln(" return result;");
self.writeln("}");
self.writeln("");
// Forward declare Option type (needed by LuxProcessHandler)
self.writeln("// Built-in Option type (forward declaration for use in handlers)");
self.writeln("typedef enum { Option_TAG_NONE, Option_TAG_SOME } Option_Tag;");
self.writeln("typedef struct { void* field0; } Option_Some_Data;");
self.writeln("typedef struct { Option_Tag tag; union { Option_Some_Data some; } data; } Option;");
self.writeln("");
// Built-in Result type
self.writeln("// Built-in Result type");
self.writeln("typedef enum { Result_TAG_OK, Result_TAG_ERR } Result_Tag;");
self.writeln("typedef struct { void* field0; } Result_Ok_Data;");
self.writeln("typedef struct { void* field0; } Result_Err_Data;");
self.writeln("typedef struct { Result_Tag tag; union { Result_Ok_Data ok; Result_Err_Data err; } data; } Result;");
self.writeln("");
self.writeln("// === Evidence Passing Types ===");
self.writeln("// These enable O(1) effect handler lookup instead of O(n) stack search.");
self.writeln("// See docs/EVIDENCE_PASSING.md for details.");
self.writeln("");
self.writeln("// Handler struct for Console effect");
self.writeln("typedef struct {");
self.writeln(" void (*print)(void* env, LuxString msg);");
self.writeln(" LuxString (*readLine)(void* env);");
self.writeln(" void* env;");
self.writeln("} LuxConsoleHandler;");
self.writeln("");
self.writeln("// Handler struct for State effect");
self.writeln("typedef struct {");
self.writeln(" void* (*get)(void* env);");
self.writeln(" void (*put)(void* env, void* value);");
self.writeln(" void* env;");
self.writeln("} LuxStateHandler;");
self.writeln("");
self.writeln("// Handler struct for Reader effect");
self.writeln("typedef struct {");
self.writeln(" void* (*ask)(void* env);");
self.writeln(" void* env;");
self.writeln("} LuxReaderHandler;");
self.writeln("");
self.writeln("// Handler struct for Random effect");
self.writeln("typedef struct {");
self.writeln(" LuxInt (*randInt)(void* env, LuxInt min, LuxInt max);");
self.writeln(" LuxFloat (*randFloat)(void* env);");
self.writeln(" LuxBool (*randBool)(void* env);");
self.writeln(" void* env;");
self.writeln("} LuxRandomHandler;");
self.writeln("");
self.writeln("// Handler struct for Time effect");
self.writeln("typedef struct {");
self.writeln(" LuxInt (*now)(void* env);");
self.writeln(" void (*sleep)(void* env, LuxInt ms);");
self.writeln(" void* env;");
self.writeln("} LuxTimeHandler;");
self.writeln("");
self.writeln("// Handler struct for File effect");
self.writeln("typedef struct {");
self.writeln(" LuxString (*read)(void* env, LuxString path);");
self.writeln(" void (*write)(void* env, LuxString path, LuxString content);");
self.writeln(" void (*append)(void* env, LuxString path, LuxString content);");
self.writeln(" LuxBool (*exists)(void* env, LuxString path);");
self.writeln(" void (*delete_file)(void* env, LuxString path);");
self.writeln(" LuxBool (*isDir)(void* env, LuxString path);");
self.writeln(" void (*mkdir)(void* env, LuxString path);");
self.writeln(" void (*copy)(void* env, LuxString src, LuxString dst);");
self.writeln(" LuxList* (*readDir)(void* env, LuxString path);");
self.writeln(" void* env;");
self.writeln("} LuxFileHandler;");
self.writeln("");
self.writeln("// Handler struct for Http effect");
self.writeln("typedef struct {");
self.writeln(" LuxString (*get)(void* env, LuxString url);");
self.writeln(" LuxString (*post)(void* env, LuxString url, LuxString body);");
self.writeln(" LuxString (*put)(void* env, LuxString url, LuxString body);");
self.writeln(" LuxString (*delete_req)(void* env, LuxString url);");
self.writeln(" void* env;");
self.writeln("} LuxHttpHandler;");
self.writeln("");
self.writeln("// Handler struct for Process effect");
self.writeln("typedef struct {");
self.writeln(" LuxString (*exec)(void* env, LuxString cmd);");
self.writeln(" Option (*getenv_fn)(void* env, LuxString name);");
self.writeln(" LuxList* (*args)(void* env);");
self.writeln(" LuxString (*cwd)(void* env);");
self.writeln(" void* env;");
self.writeln("} LuxProcessHandler;");
self.writeln("");
self.writeln("// Evidence struct - passed to effectful functions");
self.writeln("// Contains pointers to current handlers for each effect type");
self.writeln("typedef struct {");
self.writeln(" LuxConsoleHandler* console;");
self.writeln(" LuxStateHandler* state;");
self.writeln(" LuxReaderHandler* reader;");
self.writeln(" LuxRandomHandler* random;");
self.writeln(" LuxTimeHandler* time;");
self.writeln(" LuxFileHandler* file;");
self.writeln(" LuxHttpHandler* http;");
self.writeln(" LuxProcessHandler* process;");
self.writeln("} LuxEvidence;");
self.writeln("");
self.writeln("// Default Console handler using built-in implementations");
self.writeln("static void default_console_print(void* env, LuxString msg) {");
self.writeln(" (void)env;");
self.writeln(" lux_console_print(msg);");
self.writeln("}");
self.writeln("");
self.writeln("static LuxString default_console_readLine(void* env) {");
self.writeln(" (void)env;");
self.writeln(" return lux_console_readLine();");
self.writeln("}");
self.writeln("");
self.writeln("static LuxConsoleHandler default_console_handler = {");
self.writeln(" .print = default_console_print,");
self.writeln(" .readLine = default_console_readLine,");
self.writeln(" .env = NULL");
self.writeln("};");
self.writeln("");
self.writeln("// === Random Effect Built-in Implementations ===");
self.writeln("");
self.writeln("static unsigned int lux_rand_state = 12345;");
self.writeln("");
self.writeln("static LuxInt lux_random_int(LuxInt min, LuxInt max) {");
self.writeln(" // Simple LCG random number generator");
self.writeln(" lux_rand_state = lux_rand_state * 1103515245 + 12345;");
self.writeln(" LuxInt range = max - min + 1;");
self.writeln(" if (range <= 0) return min;");
self.writeln(" return min + (LuxInt)((lux_rand_state >> 16) % range);");
self.writeln("}");
self.writeln("");
self.writeln("static LuxFloat lux_random_float(void) {");
self.writeln(" lux_rand_state = lux_rand_state * 1103515245 + 12345;");
self.writeln(" return (double)(lux_rand_state >> 16) / 32767.0;");
self.writeln("}");
self.writeln("");
self.writeln("static LuxBool lux_random_bool(void) {");
self.writeln(" return lux_random_int(0, 1) == 1;");
self.writeln("}");
self.writeln("");
self.writeln("static LuxInt default_random_int(void* env, LuxInt min, LuxInt max) {");
self.writeln(" (void)env;");
self.writeln(" return lux_random_int(min, max);");
self.writeln("}");
self.writeln("");
self.writeln("static LuxFloat default_random_float(void* env) {");
self.writeln(" (void)env;");
self.writeln(" return lux_random_float();");
self.writeln("}");
self.writeln("");
self.writeln("static LuxBool default_random_bool(void* env) {");
self.writeln(" (void)env;");
self.writeln(" return lux_random_bool();");
self.writeln("}");
self.writeln("");
self.writeln("static LuxRandomHandler default_random_handler = {");
self.writeln(" .randInt = default_random_int,");
self.writeln(" .randFloat = default_random_float,");
self.writeln(" .randBool = default_random_bool,");
self.writeln(" .env = NULL");
self.writeln("};");
self.writeln("");
self.writeln("// === Time Effect Built-in Implementations ===");
self.writeln("");
self.writeln("#include <time.h>");
self.writeln("");
self.writeln("static LuxInt lux_time_now(void) {");
self.writeln(" struct timespec ts;");
self.writeln(" clock_gettime(CLOCK_REALTIME, &ts);");
self.writeln(" return (LuxInt)(ts.tv_sec * 1000 + ts.tv_nsec / 1000000);");
self.writeln("}");
self.writeln("");
self.writeln("static void lux_time_sleep(LuxInt ms) {");
self.writeln(" struct timespec ts;");
self.writeln(" ts.tv_sec = ms / 1000;");
self.writeln(" ts.tv_nsec = (ms % 1000) * 1000000;");
self.writeln(" nanosleep(&ts, NULL);");
self.writeln("}");
self.writeln("");
self.writeln("static LuxInt default_time_now(void* env) {");
self.writeln(" (void)env;");
self.writeln(" return lux_time_now();");
self.writeln("}");
self.writeln("");
self.writeln("static void default_time_sleep(void* env, LuxInt ms) {");
self.writeln(" (void)env;");
self.writeln(" lux_time_sleep(ms);");
self.writeln("}");
self.writeln("");
self.writeln("static LuxTimeHandler default_time_handler = {");
self.writeln(" .now = default_time_now,");
self.writeln(" .sleep = default_time_sleep,");
self.writeln(" .env = NULL");
self.writeln("};");
self.writeln("");
self.writeln("// === File Effect Built-in Implementations ===");
self.writeln("");
self.writeln("#include <sys/stat.h>");
self.writeln("#include <unistd.h>");
self.writeln("#include <errno.h>");
self.writeln("");
self.writeln("static LuxString lux_file_read(LuxString path) {");
self.writeln(" FILE* f = fopen(path, \"r\");");
self.writeln(" if (!f) {");
self.writeln(" LuxString empty = (LuxString)lux_rc_alloc(1, LUX_TAG_STRING);");
self.writeln(" empty[0] = '\\0';");
self.writeln(" return empty;");
self.writeln(" }");
self.writeln(" fseek(f, 0, SEEK_END);");
self.writeln(" long size = ftell(f);");
self.writeln(" fseek(f, 0, SEEK_SET);");
self.writeln(" LuxString content = (LuxString)lux_rc_alloc(size + 1, LUX_TAG_STRING);");
self.writeln(" size_t read_size = fread(content, 1, size, f);");
self.writeln(" content[read_size] = '\\0';");
self.writeln(" fclose(f);");
self.writeln(" return content;");
self.writeln("}");
self.writeln("");
self.writeln("static void lux_file_write(LuxString path, LuxString content) {");
self.writeln(" FILE* f = fopen(path, \"w\");");
self.writeln(" if (f) {");
self.writeln(" fputs(content, f);");
self.writeln(" fclose(f);");
self.writeln(" }");
self.writeln("}");
self.writeln("");
self.writeln("static void lux_file_append(LuxString path, LuxString content) {");
self.writeln(" FILE* f = fopen(path, \"a\");");
self.writeln(" if (f) {");
self.writeln(" fputs(content, f);");
self.writeln(" fclose(f);");
self.writeln(" }");
self.writeln("}");
self.writeln("");
self.writeln("static LuxBool lux_file_exists(LuxString path) {");
self.writeln(" return access(path, F_OK) == 0;");
self.writeln("}");
self.writeln("");
self.writeln("static void lux_file_delete(LuxString path) {");
self.writeln(" remove(path);");
self.writeln("}");
self.writeln("");
self.writeln("static LuxBool lux_file_isDir(LuxString path) {");
self.writeln(" struct stat st;");
self.writeln(" if (stat(path, &st) == 0) {");
self.writeln(" return S_ISDIR(st.st_mode);");
self.writeln(" }");
self.writeln(" return false;");
self.writeln("}");
self.writeln("");
self.writeln("static void lux_file_mkdir(LuxString path) {");
self.writeln(" mkdir(path, 0755);");
self.writeln("}");
self.writeln("");
self.writeln("static void lux_file_copy(LuxString src, LuxString dst) {");
self.writeln(" FILE* fin = fopen(src, \"rb\");");
self.writeln(" if (!fin) return;");
self.writeln(" FILE* fout = fopen(dst, \"wb\");");
self.writeln(" if (!fout) { fclose(fin); return; }");
self.writeln(" char buf[4096];");
self.writeln(" size_t n;");
self.writeln(" while ((n = fread(buf, 1, sizeof(buf), fin)) > 0) {");
self.writeln(" fwrite(buf, 1, n, fout);");
self.writeln(" }");
self.writeln(" fclose(fin);");
self.writeln(" fclose(fout);");
self.writeln("}");
self.writeln("");
self.writeln("#include <dirent.h>");
self.writeln("// Forward declarations needed by lux_file_readDir");
self.writeln("static LuxList* lux_list_new(int64_t capacity);");
self.writeln("static void lux_list_push(LuxList* list, void* element);");
self.writeln("static LuxList* lux_file_readDir(LuxString path) {");
self.writeln(" LuxList* result = lux_list_new(16);");
self.writeln(" DIR* dir = opendir(path);");
self.writeln(" if (!dir) return result;");
self.writeln(" struct dirent* entry;");
self.writeln(" while ((entry = readdir(dir)) != NULL) {");
self.writeln(" if (entry->d_name[0] == '.' && (entry->d_name[1] == '\\0' || (entry->d_name[1] == '.' && entry->d_name[2] == '\\0'))) continue;");
self.writeln(" size_t len = strlen(entry->d_name);");
self.writeln(" LuxString name = (LuxString)lux_rc_alloc(len + 1, LUX_TAG_STRING);");
self.writeln(" memcpy(name, entry->d_name, len + 1);");
self.writeln(" lux_list_push(result, (void*)name);");
self.writeln(" }");
self.writeln(" closedir(dir);");
self.writeln(" return result;");
self.writeln("}");
self.writeln("");
self.writeln("static LuxString default_file_read(void* env, LuxString path) {");
self.writeln(" (void)env;");
self.writeln(" return lux_file_read(path);");
self.writeln("}");
self.writeln("");
self.writeln("static void default_file_write(void* env, LuxString path, LuxString content) {");
self.writeln(" (void)env;");
self.writeln(" lux_file_write(path, content);");
self.writeln("}");
self.writeln("");
self.writeln("static void default_file_append(void* env, LuxString path, LuxString content) {");
self.writeln(" (void)env;");
self.writeln(" lux_file_append(path, content);");
self.writeln("}");
self.writeln("");
self.writeln("static LuxBool default_file_exists(void* env, LuxString path) {");
self.writeln(" (void)env;");
self.writeln(" return lux_file_exists(path);");
self.writeln("}");
self.writeln("");
self.writeln("static void default_file_delete(void* env, LuxString path) {");
self.writeln(" (void)env;");
self.writeln(" lux_file_delete(path);");
self.writeln("}");
self.writeln("");
self.writeln("static LuxBool default_file_isDir(void* env, LuxString path) {");
self.writeln(" (void)env;");
self.writeln(" return lux_file_isDir(path);");
self.writeln("}");
self.writeln("");
self.writeln("static void default_file_mkdir(void* env, LuxString path) {");
self.writeln(" (void)env;");
self.writeln(" lux_file_mkdir(path);");
self.writeln("}");
self.writeln("");
self.writeln("static void default_file_copy(void* env, LuxString src, LuxString dst) {");
self.writeln(" (void)env;");
self.writeln(" lux_file_copy(src, dst);");
self.writeln("}");
self.writeln("");
self.writeln("static LuxList* default_file_readDir(void* env, LuxString path) {");
self.writeln(" (void)env;");
self.writeln(" return lux_file_readDir(path);");
self.writeln("}");
self.writeln("");
self.writeln("static LuxFileHandler default_file_handler = {");
self.writeln(" .read = default_file_read,");
self.writeln(" .write = default_file_write,");
self.writeln(" .append = default_file_append,");
self.writeln(" .exists = default_file_exists,");
self.writeln(" .delete_file = default_file_delete,");
self.writeln(" .isDir = default_file_isDir,");
self.writeln(" .mkdir = default_file_mkdir,");
self.writeln(" .copy = default_file_copy,");
self.writeln(" .readDir = default_file_readDir,");
self.writeln(" .env = NULL");
self.writeln("};");
self.writeln("");
self.writeln("// === Http Effect Built-in Implementations ===");
self.writeln("");
self.writeln("#include <sys/socket.h>");
self.writeln("#include <netinet/in.h>");
self.writeln("#include <netdb.h>");
self.writeln("#include <arpa/inet.h>");
self.writeln("");
self.writeln("// Parse URL into host, port, and path");
self.writeln("static int lux_http_parse_url(const char* url, char* host, int* port, char* path) {");
self.writeln(" *port = 80;");
self.writeln(" path[0] = '/'; path[1] = '\\0';");
self.writeln(" const char* start = url;");
self.writeln(" if (strncmp(url, \"http://\", 7) == 0) start = url + 7;");
self.writeln(" else if (strncmp(url, \"https://\", 8) == 0) { start = url + 8; *port = 443; }");
self.writeln(" const char* slash = strchr(start, '/');");
self.writeln(" const char* colon = strchr(start, ':');");
self.writeln(" if (colon && (!slash || colon < slash)) {");
self.writeln(" strncpy(host, start, colon - start);");
self.writeln(" host[colon - start] = '\\0';");
self.writeln(" *port = atoi(colon + 1);");
self.writeln(" } else if (slash) {");
self.writeln(" strncpy(host, start, slash - start);");
self.writeln(" host[slash - start] = '\\0';");
self.writeln(" } else {");
self.writeln(" strcpy(host, start);");
self.writeln(" }");
self.writeln(" if (slash) strcpy(path, slash);");
self.writeln(" return 0;");
self.writeln("}");
self.writeln("");
self.writeln("// Helper to create RC-managed error string");
self.writeln("static LuxString lux_rc_string(const char* s) {");
self.writeln(" size_t len = strlen(s);");
self.writeln(" LuxString result = (LuxString)lux_rc_alloc(len + 1, LUX_TAG_STRING);");
self.writeln(" memcpy(result, s, len + 1);");
self.writeln(" return result;");
self.writeln("}");
self.writeln("");
self.writeln("static LuxString lux_http_request(const char* method, LuxString url, LuxString body) {");
self.writeln(" char host[256], path[1024];");
self.writeln(" int port;");
self.writeln(" lux_http_parse_url(url, host, &port, path);");
self.writeln("");
self.writeln(" struct hostent* server = gethostbyname(host);");
self.writeln(" if (!server) return lux_rc_string(\"Error: Could not resolve host\");");
self.writeln("");
self.writeln(" int sockfd = socket(AF_INET, SOCK_STREAM, 0);");
self.writeln(" if (sockfd < 0) return lux_rc_string(\"Error: Could not create socket\");");
self.writeln("");
self.writeln(" struct sockaddr_in serv_addr;");
self.writeln(" memset(&serv_addr, 0, sizeof(serv_addr));");
self.writeln(" serv_addr.sin_family = AF_INET;");
self.writeln(" memcpy(&serv_addr.sin_addr.s_addr, server->h_addr, server->h_length);");
self.writeln(" serv_addr.sin_port = htons(port);");
self.writeln("");
self.writeln(" if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {");
self.writeln(" close(sockfd);");
self.writeln(" return lux_rc_string(\"Error: Connection failed\");");
self.writeln(" }");
self.writeln("");
self.writeln(" char request[4096];");
self.writeln(" if (body && strlen(body) > 0) {");
self.writeln(" snprintf(request, sizeof(request),");
self.writeln(" \"%s %s HTTP/1.1\\r\\n\"");
self.writeln(" \"Host: %s\\r\\n\"");
self.writeln(" \"Content-Type: application/x-www-form-urlencoded\\r\\n\"");
self.writeln(" \"Content-Length: %zu\\r\\n\"");
self.writeln(" \"Connection: close\\r\\n\\r\\n%s\",");
self.writeln(" method, path, host, strlen(body), body);");
self.writeln(" } else {");
self.writeln(" snprintf(request, sizeof(request),");
self.writeln(" \"%s %s HTTP/1.1\\r\\n\"");
self.writeln(" \"Host: %s\\r\\n\"");
self.writeln(" \"Connection: close\\r\\n\\r\\n\",");
self.writeln(" method, path, host);");
self.writeln(" }");
self.writeln("");
self.writeln(" send(sockfd, request, strlen(request), 0);");
self.writeln("");
self.writeln(" char* response = (char*)malloc(65536);");
self.writeln(" size_t total = 0;");
self.writeln(" ssize_t n;");
self.writeln(" while ((n = recv(sockfd, response + total, 65536 - total - 1, 0)) > 0) {");
self.writeln(" total += n;");
self.writeln(" if (total >= 65535) break;");
self.writeln(" }");
self.writeln(" response[total] = '\\0';");
self.writeln(" close(sockfd);");
self.writeln("");
self.writeln(" // Find body (after \\r\\n\\r\\n)");
self.writeln(" char* body_start = strstr(response, \"\\r\\n\\r\\n\");");
self.writeln(" if (body_start) {");
self.writeln(" body_start += 4;");
self.writeln(" LuxString result = lux_rc_string(body_start);");
self.writeln(" free(response);");
self.writeln(" return result;");
self.writeln(" }");
self.writeln(" // If no body separator, wrap the whole response");
self.writeln(" LuxString result = lux_rc_string(response);");
self.writeln(" free(response);");
self.writeln(" return result;");
self.writeln("}");
self.writeln("");
self.writeln("static LuxString lux_http_get(LuxString url) {");
self.writeln(" return lux_http_request(\"GET\", url, NULL);");
self.writeln("}");
self.writeln("");
self.writeln("static LuxString lux_http_post(LuxString url, LuxString body) {");
self.writeln(" return lux_http_request(\"POST\", url, body);");
self.writeln("}");
self.writeln("");
self.writeln("static LuxString lux_http_put(LuxString url, LuxString body) {");
self.writeln(" return lux_http_request(\"PUT\", url, body);");
self.writeln("}");
self.writeln("");
self.writeln("static LuxString lux_http_delete(LuxString url) {");
self.writeln(" return lux_http_request(\"DELETE\", url, NULL);");
self.writeln("}");
self.writeln("");
self.writeln("static LuxString default_http_get(void* env, LuxString url) {");
self.writeln(" (void)env;");
self.writeln(" return lux_http_get(url);");
self.writeln("}");
self.writeln("");
self.writeln("static LuxString default_http_post(void* env, LuxString url, LuxString body) {");
self.writeln(" (void)env;");
self.writeln(" return lux_http_post(url, body);");
self.writeln("}");
self.writeln("");
self.writeln("static LuxString default_http_put(void* env, LuxString url, LuxString body) {");
self.writeln(" (void)env;");
self.writeln(" return lux_http_put(url, body);");
self.writeln("}");
self.writeln("");
self.writeln("static LuxString default_http_delete(void* env, LuxString url) {");
self.writeln(" (void)env;");
self.writeln(" return lux_http_delete(url);");
self.writeln("}");
self.writeln("");
self.writeln("static LuxHttpHandler default_http_handler = {");
self.writeln(" .get = default_http_get,");
self.writeln(" .post = default_http_post,");
self.writeln(" .put = default_http_put,");
self.writeln(" .delete_req = default_http_delete,");
self.writeln(" .env = NULL");
self.writeln("};");
self.writeln("");
self.writeln("// === Process Effect Built-in Implementations ===");
self.writeln("");
self.writeln("// Forward declarations for list functions used by Process/String");
self.writeln("static LuxList* lux_list_new(int64_t capacity);");
self.writeln("static void lux_list_push(LuxList* list, void* element);");
self.writeln("");
self.writeln("static LuxString lux_process_exec(LuxString cmd) {");
self.writeln(" FILE* fp = popen(cmd, \"r\");");
self.writeln(" if (!fp) return \"\";");
self.writeln(" char buffer[4096];");
self.writeln(" size_t total_len = 0;");
self.writeln(" size_t capacity = 4096;");
self.writeln(" char* temp = (char*)malloc(capacity);");
self.writeln(" if (!temp) { pclose(fp); return \"\"; }");
self.writeln(" temp[0] = '\\0';");
self.writeln(" while (fgets(buffer, sizeof(buffer), fp) != NULL) {");
self.writeln(" size_t chunk_len = strlen(buffer);");
self.writeln(" if (total_len + chunk_len + 1 > capacity) {");
self.writeln(" capacity *= 2;");
self.writeln(" char* new_temp = (char*)realloc(temp, capacity);");
self.writeln(" if (!new_temp) { free(temp); pclose(fp); return \"\"; }");
self.writeln(" temp = new_temp;");
self.writeln(" }");
self.writeln(" strcpy(temp + total_len, buffer);");
self.writeln(" total_len += chunk_len;");
self.writeln(" }");
self.writeln(" pclose(fp);");
self.writeln(" // Copy to RC-managed string");
self.writeln(" LuxString result = (LuxString)lux_rc_alloc(total_len + 1, LUX_TAG_STRING);");
self.writeln(" if (!result) { free(temp); return \"\"; }");
self.writeln(" memcpy(result, temp, total_len + 1);");
self.writeln(" free(temp);");
self.writeln(" return result;");
self.writeln("}");
self.writeln("");
self.writeln("static Option lux_process_env(LuxString name) {");
self.writeln(" char* value = getenv(name);");
self.writeln(" if (value) {");
self.writeln(" Option result;");
self.writeln(" result.tag = Option_TAG_SOME;");
self.writeln(" result.data.some.field0 = (void*)lux_string_dup(value);");
self.writeln(" return result;");
self.writeln(" } else {");
self.writeln(" Option result;");
self.writeln(" result.tag = Option_TAG_NONE;");
self.writeln(" return result;");
self.writeln(" }");
self.writeln("}");
self.writeln("");
self.writeln("// Global argc/argv for Process.args()");
self.writeln("static int lux_argc = 0;");
self.writeln("static char** lux_argv = NULL;");
self.writeln("");
self.writeln("static LuxList* lux_process_args(void) {");
self.writeln(" LuxList* list = lux_list_new(lux_argc > 0 ? lux_argc : 1);");
self.writeln(" for (int i = 0; i < lux_argc; i++) {");
self.writeln(" lux_list_push(list, (void*)lux_string_dup(lux_argv[i]));");
self.writeln(" }");
self.writeln(" return list;");
self.writeln("}");
self.writeln("");
self.writeln("static LuxString lux_process_cwd(void) {");
self.writeln(" char* cwd = getcwd(NULL, 0);");
self.writeln(" if (!cwd) return \"\";");
self.writeln(" return cwd;");
self.writeln("}");
self.writeln("");
self.writeln("static LuxString default_process_exec(void* env, LuxString cmd) {");
self.writeln(" (void)env;");
self.writeln(" return lux_process_exec(cmd);");
self.writeln("}");
self.writeln("");
self.writeln("static Option default_process_env(void* env, LuxString name) {");
self.writeln(" (void)env;");
self.writeln(" return lux_process_env(name);");
self.writeln("}");
self.writeln("");
self.writeln("static LuxList* default_process_args(void* env) {");
self.writeln(" (void)env;");
self.writeln(" return lux_process_args();");
self.writeln("}");
self.writeln("");
self.writeln("static LuxString default_process_cwd(void* env) {");
self.writeln(" (void)env;");
self.writeln(" return lux_process_cwd();");
self.writeln("}");
self.writeln("");
self.writeln("static LuxProcessHandler default_process_handler = {");
self.writeln(" .exec = default_process_exec,");
self.writeln(" .getenv_fn = default_process_env,");
self.writeln(" .args = default_process_args,");
self.writeln(" .cwd = default_process_cwd,");
self.writeln(" .env = NULL");
self.writeln("};");
self.writeln("");
self.writeln("// === String Built-in Functions ===");
self.writeln("");
self.writeln("static LuxString lux_string_trim(LuxString s) {");
self.writeln(" if (!s) return \"\";");
self.writeln(" const char* start = s;");
self.writeln(" const char* end = s + strlen(s) - 1;");
self.writeln(" while (*start && (*start == ' ' || *start == '\\t' || *start == '\\n' || *start == '\\r')) start++;");
self.writeln(" while (end > start && (*end == ' ' || *end == '\\t' || *end == '\\n' || *end == '\\r')) end--;");
self.writeln(" size_t len = end - start + 1;");
self.writeln(" LuxString result = (LuxString)lux_rc_alloc(len + 1, LUX_TAG_STRING);");
self.writeln(" if (!result) return \"\";");
self.writeln(" strncpy(result, start, len);");
self.writeln(" result[len] = '\\0';");
self.writeln(" return result;");
self.writeln("}");
self.writeln("");
self.writeln("static LuxInt lux_string_length(LuxString s) {");
self.writeln(" if (!s) return 0;");
self.writeln(" return (LuxInt)strlen(s);");
self.writeln("}");
self.writeln("");
self.writeln("static LuxList* lux_string_lines(LuxString s) {");
self.writeln(" LuxList* list = lux_list_new(8);");
self.writeln(" if (!s || !*s) return list;");
self.writeln(" const char* start = s;");
self.writeln(" const char* p = s;");
self.writeln(" while (*p) {");
self.writeln(" if (*p == '\\n') {");
self.writeln(" size_t len = p - start;");
self.writeln(" LuxString line = (LuxString)lux_rc_alloc(len + 1, LUX_TAG_STRING);");
self.writeln(" if (line) {");
self.writeln(" strncpy(line, start, len);");
self.writeln(" line[len] = '\\0';");
self.writeln(" lux_list_push(list, (void*)line);");
self.writeln(" }");
self.writeln(" start = p + 1;");
self.writeln(" }");
self.writeln(" p++;");
self.writeln(" }");
self.writeln(" if (start != p) {");
self.writeln(" size_t len = p - start;");
self.writeln(" LuxString line = (LuxString)lux_rc_alloc(len + 1, LUX_TAG_STRING);");
self.writeln(" if (line) {");
self.writeln(" strncpy(line, start, len);");
self.writeln(" line[len] = '\\0';");
self.writeln(" lux_list_push(list, (void*)line);");
self.writeln(" }");
self.writeln(" }");
self.writeln(" return list;");
self.writeln("}");
self.writeln("");
self.writeln("static LuxList* lux_string_split(LuxString s, LuxString sep) {");
self.writeln(" LuxList* list = lux_list_new(8);");
self.writeln(" if (!s || !*s) return list;");
self.writeln(" if (!sep || !*sep) {");
self.writeln(" lux_list_push(list, (void*)lux_string_dup(s));");
self.writeln(" return list;");
self.writeln(" }");
self.writeln(" size_t sep_len = strlen(sep);");
self.writeln(" const char* start = s;");
self.writeln(" const char* found;");
self.writeln(" while ((found = strstr(start, sep)) != NULL) {");
self.writeln(" size_t len = found - start;");
self.writeln(" LuxString part = (LuxString)lux_rc_alloc(len + 1, LUX_TAG_STRING);");
self.writeln(" if (part) {");
self.writeln(" strncpy(part, start, len);");
self.writeln(" part[len] = '\\0';");
self.writeln(" lux_list_push(list, (void*)part);");
self.writeln(" }");
self.writeln(" start = found + sep_len;");
self.writeln(" }");
self.writeln(" if (*start) {");
self.writeln(" lux_list_push(list, (void*)lux_string_dup(start));");
self.writeln(" }");
self.writeln(" return list;");
self.writeln("}");
self.writeln("");
// Note: lux_string_contains is already defined in the String Operations section
self.writeln("");
self.writeln("static Option lux_string_parseInt(LuxString s) {");
self.writeln(" Option result;");
self.writeln(" if (!s || !*s) {");
self.writeln(" result.tag = Option_TAG_NONE;");
self.writeln(" return result;");
self.writeln(" }");
self.writeln(" char* endptr;");
self.writeln(" errno = 0;");
self.writeln(" long long val = strtoll(s, &endptr, 10);");
self.writeln(" if (errno != 0 || endptr == s || *endptr != '\\0') {");
self.writeln(" result.tag = Option_TAG_NONE;");
self.writeln(" return result;");
self.writeln(" }");
self.writeln(" result.tag = Option_TAG_SOME;");
self.writeln(" LuxInt* boxed = (LuxInt*)lux_rc_alloc(sizeof(LuxInt), LUX_TAG_BOXED_INT);");
self.writeln(" *boxed = (LuxInt)val;");
self.writeln(" result.data.some.field0 = (void*)boxed;");
self.writeln(" return result;");
self.writeln("}");
self.writeln("");
self.writeln("static Option lux_string_parseFloat(LuxString s) {");
self.writeln(" Option result;");
self.writeln(" if (!s || !*s) {");
self.writeln(" result.tag = Option_TAG_NONE;");
self.writeln(" return result;");
self.writeln(" }");
self.writeln(" char* endptr;");
self.writeln(" errno = 0;");
self.writeln(" double val = strtod(s, &endptr);");
self.writeln(" if (errno != 0 || endptr == s || *endptr != '\\0') {");
self.writeln(" result.tag = Option_TAG_NONE;");
self.writeln(" return result;");
self.writeln(" }");
self.writeln(" result.tag = Option_TAG_SOME;");
self.writeln(" LuxFloat* boxed = (LuxFloat*)malloc(sizeof(LuxFloat));");
self.writeln(" *boxed = val;");
self.writeln(" result.data.some.field0 = (void*)boxed;");
self.writeln(" return result;");
self.writeln("}");
self.writeln("");
self.writeln("static LuxString lux_string_from_char(char c) {");
self.writeln(" LuxString result = (LuxString)lux_rc_alloc(2, LUX_TAG_STRING);");
self.writeln(" if (!result) return \"\";");
self.writeln(" result[0] = c;");
self.writeln(" result[1] = '\\0';");
self.writeln(" return result;");
self.writeln("}");
self.writeln("");
self.writeln("static LuxList* lux_string_chars(LuxString s) {");
self.writeln(" size_t len = s ? strlen(s) : 0;");
self.writeln(" LuxList* list = lux_list_new(len);");
self.writeln(" for (size_t i = 0; i < len; i++) {");
self.writeln(" LuxString ch = lux_string_from_char(s[i]);");
self.writeln(" lux_list_push(list, (void*)ch);");
self.writeln(" }");
self.writeln(" return list;");
self.writeln("}");
self.writeln("");
self.writeln("static LuxString lux_string_substring(LuxString s, LuxInt start, LuxInt len) {");
self.writeln(" if (!s) return \"\";");
self.writeln(" size_t slen = strlen(s);");
self.writeln(" if (start < 0) start = 0;");
self.writeln(" if ((size_t)start >= slen) return \"\";");
self.writeln(" if (len < 0) len = 0;");
self.writeln(" if ((size_t)(start + len) > slen) len = slen - start;");
self.writeln(" LuxString result = (LuxString)lux_rc_alloc(len + 1, LUX_TAG_STRING);");
self.writeln(" if (!result) return \"\";");
self.writeln(" strncpy(result, s + start, len);");
self.writeln(" result[len] = '\\0';");
self.writeln(" return result;");
self.writeln("}");
self.writeln("");
self.writeln("static LuxString lux_string_to_upper(LuxString s) {");
self.writeln(" if (!s) return \"\";");
self.writeln(" size_t len = strlen(s);");
self.writeln(" LuxString result = (LuxString)lux_rc_alloc(len + 1, LUX_TAG_STRING);");
self.writeln(" if (!result) return \"\";");
self.writeln(" for (size_t i = 0; i < len; i++) {");
self.writeln(" result[i] = (s[i] >= 'a' && s[i] <= 'z') ? s[i] - 32 : s[i];");
self.writeln(" }");
self.writeln(" result[len] = '\\0';");
self.writeln(" return result;");
self.writeln("}");
self.writeln("");
self.writeln("static LuxString lux_string_to_lower(LuxString s) {");
self.writeln(" if (!s) return \"\";");
self.writeln(" size_t len = strlen(s);");
self.writeln(" LuxString result = (LuxString)lux_rc_alloc(len + 1, LUX_TAG_STRING);");
self.writeln(" if (!result) return \"\";");
self.writeln(" for (size_t i = 0; i < len; i++) {");
self.writeln(" result[i] = (s[i] >= 'A' && s[i] <= 'Z') ? s[i] + 32 : s[i];");
self.writeln(" }");
self.writeln(" result[len] = '\\0';");
self.writeln(" return result;");
self.writeln("}");
self.writeln("");
self.writeln("static LuxString lux_string_replace(LuxString s, LuxString from, LuxString to) {");
self.writeln(" if (!s || !from || !*from) return s ? lux_string_dup(s) : \"\";");
self.writeln(" if (!to) to = \"\";");
self.writeln(" size_t from_len = strlen(from);");
self.writeln(" size_t to_len = strlen(to);");
self.writeln(" size_t count = 0;");
self.writeln(" const char* p = s;");
self.writeln(" while ((p = strstr(p, from)) != NULL) { count++; p += from_len; }");
self.writeln(" size_t new_len = strlen(s) + count * (to_len - from_len);");
self.writeln(" LuxString result = (LuxString)lux_rc_alloc(new_len + 1, LUX_TAG_STRING);");
self.writeln(" if (!result) return \"\";");
self.writeln(" char* out = result;");
self.writeln(" p = s;");
self.writeln(" const char* found;");
self.writeln(" while ((found = strstr(p, from)) != NULL) {");
self.writeln(" size_t prefix_len = found - p;");
self.writeln(" memcpy(out, p, prefix_len);");
self.writeln(" out += prefix_len;");
self.writeln(" memcpy(out, to, to_len);");
self.writeln(" out += to_len;");
self.writeln(" p = found + from_len;");
self.writeln(" }");
self.writeln(" strcpy(out, p);");
self.writeln(" return result;");
self.writeln("}");
self.writeln("");
self.writeln("static LuxBool lux_string_starts_with(LuxString s, LuxString prefix) {");
self.writeln(" if (!s || !prefix) return 0;");
self.writeln(" size_t prefix_len = strlen(prefix);");
self.writeln(" if (strlen(s) < prefix_len) return 0;");
self.writeln(" return strncmp(s, prefix, prefix_len) == 0;");
self.writeln("}");
self.writeln("");
self.writeln("static LuxBool lux_string_ends_with(LuxString s, LuxString suffix) {");
self.writeln(" if (!s || !suffix) return 0;");
self.writeln(" size_t s_len = strlen(s);");
self.writeln(" size_t suffix_len = strlen(suffix);");
self.writeln(" if (s_len < suffix_len) return 0;");
self.writeln(" return strcmp(s + s_len - suffix_len, suffix) == 0;");
self.writeln("}");
self.writeln("");
self.writeln("static LuxString lux_string_join(LuxList* list, LuxString sep) {");
self.writeln(" if (!list || list->length == 0) return \"\";");
self.writeln(" if (!sep) sep = \"\";");
self.writeln(" size_t sep_len = strlen(sep);");
self.writeln(" size_t total_len = 0;");
self.writeln(" for (int64_t i = 0; i < list->length; i++) {");
self.writeln(" LuxString elem = (LuxString)list->elements[i];");
self.writeln(" if (elem) total_len += strlen(elem);");
self.writeln(" if (i > 0) total_len += sep_len;");
self.writeln(" }");
self.writeln(" LuxString result = (LuxString)lux_rc_alloc(total_len + 1, LUX_TAG_STRING);");
self.writeln(" if (!result) return \"\";");
self.writeln(" char* out = result;");
self.writeln(" for (int64_t i = 0; i < list->length; i++) {");
self.writeln(" if (i > 0) { strcpy(out, sep); out += sep_len; }");
self.writeln(" LuxString elem = (LuxString)list->elements[i];");
self.writeln(" if (elem) { strcpy(out, elem); out += strlen(elem); }");
self.writeln(" }");
self.writeln(" *out = '\\0';");
self.writeln(" return result;");
self.writeln("}");
self.writeln("");
self.writeln("// Default evidence with built-in handlers");
self.writeln("static LuxEvidence default_evidence = {");
self.writeln(" .console = &default_console_handler,");
self.writeln(" .state = NULL,");
self.writeln(" .reader = NULL,");
self.writeln(" .random = &default_random_handler,");
self.writeln(" .time = &default_time_handler,");
self.writeln(" .file = &default_file_handler,");
self.writeln(" .http = &default_http_handler,");
self.writeln(" .process = &default_process_handler");
self.writeln("};");
self.writeln("");
self.writeln("// === List Operations ===");
self.writeln("// (LuxList struct defined earlier, before string functions)");
// Emit specialized decref implementations (now that types are defined)
self.emit_specialized_decref_implementations();
self.writeln("// === List Operations ===");
self.writeln("// All lists are RC-managed. Elements are also RC-managed.");
self.writeln("");
self.writeln("static LuxList* lux_list_new(int64_t capacity) {");
self.writeln(" LuxList* list = (LuxList*)lux_rc_alloc(sizeof(LuxList), LUX_TAG_LIST);");
self.writeln(" if (!list) return NULL;");
self.writeln(" list->capacity = capacity > 0 ? capacity : 4;");
self.writeln(" list->elements = (void**)malloc(sizeof(void*) * list->capacity);");
self.writeln(" list->length = 0;");
self.writeln(" return list;");
self.writeln("}");
self.writeln("");
self.writeln("static void lux_list_push(LuxList* list, void* element) {");
self.writeln(" if (list->length >= list->capacity) {");
self.writeln(" list->capacity *= 2;");
self.writeln(" list->elements = (void**)realloc(list->elements, sizeof(void*) * list->capacity);");
self.writeln(" }");
self.writeln(" list->elements[list->length++] = element;");
self.writeln("}");
self.writeln("");
self.writeln("static int64_t lux_list_length(LuxList* list) { return list->length; }");
self.writeln("static LuxBool lux_list_isEmpty(LuxList* list) { return list->length == 0; }");
self.writeln("");
self.writeln("static LuxList* lux_list_concat(LuxList* a, LuxList* b) {");
self.writeln(" LuxList* result = lux_list_new(a->length + b->length);");
self.writeln(" for (int64_t i = 0; i < a->length; i++) {");
self.writeln(" lux_incref(a->elements[i]);");
self.writeln(" result->elements[i] = a->elements[i];");
self.writeln(" }");
self.writeln(" for (int64_t i = 0; i < b->length; i++) {");
self.writeln(" lux_incref(b->elements[i]);");
self.writeln(" result->elements[a->length + i] = b->elements[i];");
self.writeln(" }");
self.writeln(" result->length = a->length + b->length;");
self.writeln(" return result;");
self.writeln("}");
self.writeln("");
self.writeln("static LuxList* lux_list_reverse(LuxList* list) {");
self.writeln(" // FBIP: If rc=1, reverse in-place instead of copying");
self.writeln(" if (LUX_RC_HEADER(list)->rc == 1) {");
if self.debug_rc {
self.writeln(" lux_fbip_reuse_count++;");
}
self.writeln(" // In-place reversal - just swap element pointers");
self.writeln(" int64_t n = list->length;");
self.writeln(" for (int64_t i = 0; i < n / 2; i++) {");
self.writeln(" void* tmp = list->elements[i];");
self.writeln(" list->elements[i] = list->elements[n - 1 - i];");
self.writeln(" list->elements[n - 1 - i] = tmp;");
self.writeln(" }");
self.writeln(" return list; // Reuse same list");
self.writeln(" }");
if self.debug_rc {
self.writeln(" lux_fbip_copy_count++;");
}
self.writeln(" // rc > 1: Allocate new list (standard path)");
self.writeln(" LuxList* result = lux_list_new(list->length);");
self.writeln(" for (int64_t i = 0; i < list->length; i++) {");
self.writeln(" lux_incref(list->elements[list->length - 1 - i]);");
self.writeln(" result->elements[i] = list->elements[list->length - 1 - i];");
self.writeln(" }");
self.writeln(" result->length = list->length;");
self.writeln(" return result;");
self.writeln("}");
self.writeln("");
self.writeln("static LuxList* lux_list_take(LuxList* list, int64_t n) {");
self.writeln(" if (n <= 0) return lux_list_new(0);");
self.writeln(" if (n >= list->length) {");
self.writeln(" lux_incref(list); // Return same list");
self.writeln(" return list;");
self.writeln(" }");
self.writeln(" // FBIP: If rc=1, truncate in-place");
self.writeln(" if (LUX_RC_HEADER(list)->rc == 1) {");
if self.debug_rc {
self.writeln(" lux_fbip_reuse_count++;");
}
self.writeln(" // Decref elements we're dropping");
self.writeln(" for (int64_t i = n; i < list->length; i++) {");
self.writeln(" lux_decref(list->elements[i]);");
self.writeln(" }");
self.writeln(" list->length = n;");
self.writeln(" return list; // Reuse same list");
self.writeln(" }");
if self.debug_rc {
self.writeln(" lux_fbip_copy_count++;");
}
self.writeln(" // rc > 1: Allocate new list");
self.writeln(" LuxList* result = lux_list_new(n);");
self.writeln(" for (int64_t i = 0; i < n; i++) {");
self.writeln(" lux_incref(list->elements[i]);");
self.writeln(" result->elements[i] = list->elements[i];");
self.writeln(" }");
self.writeln(" result->length = n;");
self.writeln(" return result;");
self.writeln("}");
self.writeln("");
self.writeln("static LuxList* lux_list_drop(LuxList* list, int64_t n) {");
self.writeln(" if (n >= list->length) return lux_list_new(0);");
self.writeln(" if (n <= 0) {");
self.writeln(" lux_incref(list); // Return same list with bumped refcount");
self.writeln(" return list;");
self.writeln(" }");
self.writeln(" int64_t new_len = list->length - n;");
self.writeln(" // FBIP: If rc=1, shift elements in-place");
self.writeln(" if (LUX_RC_HEADER(list)->rc == 1) {");
if self.debug_rc {
self.writeln(" lux_fbip_reuse_count++;");
}
self.writeln(" // Decref elements we're dropping");
self.writeln(" for (int64_t i = 0; i < n; i++) {");
self.writeln(" lux_decref(list->elements[i]);");
self.writeln(" }");
self.writeln(" // Shift remaining elements to front");
self.writeln(" for (int64_t i = 0; i < new_len; i++) {");
self.writeln(" list->elements[i] = list->elements[n + i];");
self.writeln(" }");
self.writeln(" list->length = new_len;");
self.writeln(" return list; // Reuse same list");
self.writeln(" }");
if self.debug_rc {
self.writeln(" lux_fbip_copy_count++;");
}
self.writeln(" // rc > 1: Allocate new list");
self.writeln(" LuxList* result = lux_list_new(new_len);");
self.writeln(" for (int64_t i = 0; i < new_len; i++) {");
self.writeln(" lux_incref(list->elements[n + i]);");
self.writeln(" result->elements[i] = list->elements[n + i];");
self.writeln(" }");
self.writeln(" result->length = new_len;");
self.writeln(" return result;");
self.writeln("}");
self.writeln("");
self.writeln("static LuxList* lux_list_range(int64_t start, int64_t end) {");
self.writeln(" if (end <= start) return lux_list_new(0);");
self.writeln(" int64_t len = end - start;");
self.writeln(" LuxList* result = lux_list_new(len);");
self.writeln(" for (int64_t i = 0; i < len; i++) {");
self.writeln(" LuxInt* p = (LuxInt*)lux_rc_alloc(sizeof(LuxInt), LUX_TAG_BOXED_INT);");
self.writeln(" *p = start + i;");
self.writeln(" result->elements[i] = p;");
self.writeln(" }");
self.writeln(" result->length = len;");
self.writeln(" return result;");
self.writeln("}");
self.writeln("");
// === Map Runtime Functions ===
self.writeln("static LuxMap* lux_map_new(int64_t capacity) {");
self.writeln(" LuxMap* map = (LuxMap*)malloc(sizeof(LuxMap));");
self.writeln(" map->capacity = capacity > 0 ? capacity : 8;");
self.writeln(" map->keys = (LuxString*)calloc(map->capacity, sizeof(LuxString));");
self.writeln(" map->values = (void**)calloc(map->capacity, sizeof(void*));");
self.writeln(" map->length = 0;");
self.writeln(" return map;");
self.writeln("}");
self.writeln("");
self.writeln("static int64_t lux_map_find(LuxMap* map, LuxString key) {");
self.writeln(" for (int64_t i = 0; i < map->length; i++) {");
self.writeln(" if (map->keys[i] && strcmp(map->keys[i], key) == 0) return i;");
self.writeln(" }");
self.writeln(" return -1;");
self.writeln("}");
self.writeln("");
self.writeln("static LuxMap* lux_map_clone(LuxMap* map) {");
self.writeln(" LuxMap* result = lux_map_new(map->capacity);");
self.writeln(" result->length = map->length;");
self.writeln(" for (int64_t i = 0; i < map->length; i++) {");
self.writeln(" result->keys[i] = lux_string_dup(map->keys[i]);");
self.writeln(" result->values[i] = map->values[i];");
self.writeln(" lux_incref(map->values[i]);");
self.writeln(" }");
self.writeln(" return result;");
self.writeln("}");
self.writeln("");
self.writeln("static LuxMap* lux_map_set(LuxMap* map, LuxString key, void* value) {");
self.writeln(" LuxMap* result = lux_map_clone(map);");
self.writeln(" int64_t idx = lux_map_find(result, key);");
self.writeln(" if (idx >= 0) {");
self.writeln(" lux_decref(result->values[idx]);");
self.writeln(" result->values[idx] = value;");
self.writeln(" lux_incref(value);");
self.writeln(" } else {");
self.writeln(" if (result->length >= result->capacity) {");
self.writeln(" result->capacity *= 2;");
self.writeln(" result->keys = (LuxString*)realloc(result->keys, sizeof(LuxString) * result->capacity);");
self.writeln(" result->values = (void**)realloc(result->values, sizeof(void*) * result->capacity);");
self.writeln(" }");
self.writeln(" result->keys[result->length] = lux_string_dup(key);");
self.writeln(" result->values[result->length] = value;");
self.writeln(" lux_incref(value);");
self.writeln(" result->length++;");
self.writeln(" }");
self.writeln(" return result;");
self.writeln("}");
self.writeln("");
self.writeln("static int64_t lux_map_size(LuxMap* map) { return map->length; }");
self.writeln("static LuxBool lux_map_isEmpty(LuxMap* map) { return map->length == 0; }");
self.writeln("");
self.writeln("static LuxBool lux_map_contains(LuxMap* map, LuxString key) {");
self.writeln(" return lux_map_find(map, key) >= 0;");
self.writeln("}");
self.writeln("");
self.writeln("static LuxMap* lux_map_remove(LuxMap* map, LuxString key) {");
self.writeln(" LuxMap* result = lux_map_new(map->capacity);");
self.writeln(" for (int64_t i = 0; i < map->length; i++) {");
self.writeln(" if (strcmp(map->keys[i], key) != 0) {");
self.writeln(" result->keys[result->length] = lux_string_dup(map->keys[i]);");
self.writeln(" result->values[result->length] = map->values[i];");
self.writeln(" lux_incref(map->values[i]);");
self.writeln(" result->length++;");
self.writeln(" }");
self.writeln(" }");
self.writeln(" return result;");
self.writeln("}");
self.writeln("");
self.writeln("static Option lux_option_none(void) { return (Option){Option_TAG_NONE}; }");
self.writeln("static Option lux_option_some(void* value) { return (Option){Option_TAG_SOME, .data.some = {value}}; }");
self.writeln("");
self.writeln("// === Boxing/Unboxing ===");
self.writeln("// All boxed values are RC-managed.");
self.writeln("");
self.writeln("static void* lux_box_int(LuxInt n) {");
self.writeln(" LuxInt* p = (LuxInt*)lux_rc_alloc(sizeof(LuxInt), LUX_TAG_BOXED_INT);");
self.writeln(" *p = n;");
self.writeln(" return p;");
self.writeln("}");
self.writeln("static LuxInt lux_unbox_int(void* p) { return *(LuxInt*)p; }");
self.writeln("");
self.writeln("static void* lux_box_bool(LuxBool b) {");
self.writeln(" LuxBool* p = (LuxBool*)lux_rc_alloc(sizeof(LuxBool), LUX_TAG_BOXED_BOOL);");
self.writeln(" *p = b;");
self.writeln(" return p;");
self.writeln("}");
self.writeln("static LuxBool lux_unbox_bool(void* p) { return *(LuxBool*)p; }");
self.writeln("");
self.writeln("static void* lux_box_float(LuxFloat f) {");
self.writeln(" LuxFloat* p = (LuxFloat*)lux_rc_alloc(sizeof(LuxFloat), LUX_TAG_BOXED_FLOAT);");
self.writeln(" *p = f;");
self.writeln(" return p;");
self.writeln("}");
self.writeln("static LuxFloat lux_unbox_float(void* p) { return *(LuxFloat*)p; }");
self.writeln("");
self.writeln("static void* lux_box_string(LuxString s) {");
self.writeln(" // Strings are already char*, so we allocate a new RC-managed string");
self.writeln(" size_t len = strlen(s);");
self.writeln(" LuxString p = (LuxString)lux_rc_alloc(len + 1, LUX_TAG_STRING);");
self.writeln(" memcpy(p, s, len + 1);");
self.writeln(" return p;");
self.writeln("}");
self.writeln("static LuxString lux_unbox_string(void* p) { return (LuxString)p; }");
self.writeln("");
self.writeln("// String indexOf / lastIndexOf — return Option<Int>");
self.writeln("static Option lux_string_indexOf(LuxString s, LuxString needle) {");
self.writeln(" char* pos = strstr(s, needle);");
self.writeln(" if (pos) return lux_option_some(lux_box_int((LuxInt)(pos - s)));");
self.writeln(" return lux_option_none();");
self.writeln("}");
self.writeln("static Option lux_string_lastIndexOf(LuxString s, LuxString needle) {");
self.writeln(" char* last = NULL;");
self.writeln(" char* pos = strstr(s, needle);");
self.writeln(" while (pos) { last = pos; pos = strstr(pos + 1, needle); }");
self.writeln(" if (last) return lux_option_some(lux_box_int((LuxInt)(last - s)));");
self.writeln(" return lux_option_none();");
self.writeln("}");
self.writeln("");
self.writeln("// === Polymorphic Drop Function ===");
self.writeln("// Called when an object's refcount reaches zero.");
self.writeln("// Recursively decrefs any owned references before freeing.");
self.writeln("");
self.writeln("static void lux_drop(void* ptr, int32_t tag) {");
self.writeln(" if (!ptr) return;");
self.writeln(" switch (tag) {");
self.writeln(" case LUX_TAG_STRING:");
self.writeln(" // Strings are just char arrays, no sub-references");
self.writeln(" break;");
self.writeln(" case LUX_TAG_LIST: {");
self.writeln(" LuxList* list = (LuxList*)ptr;");
self.writeln(" // Decref each element (they're all boxed/RC-managed)");
self.writeln(" for (int64_t i = 0; i < list->length; i++) {");
self.writeln(" lux_decref(list->elements[i]);");
self.writeln(" }");
self.writeln(" free(list->elements);");
self.writeln(" break;");
self.writeln(" }");
self.writeln(" case LUX_TAG_CLOSURE: {");
self.writeln(" LuxClosure* closure = (LuxClosure*)ptr;");
self.writeln(" // Decref the environment if it's RC-managed");
self.writeln(" lux_decref(closure->env);");
self.writeln(" break;");
self.writeln(" }");
self.writeln(" case LUX_TAG_BOXED_INT:");
self.writeln(" case LUX_TAG_BOXED_BOOL:");
self.writeln(" case LUX_TAG_BOXED_FLOAT:");
self.writeln(" // Primitive boxes have no sub-references");
self.writeln(" break;");
self.writeln(" case LUX_TAG_ENV:");
self.writeln(" // Environment structs may contain RC pointers");
self.writeln(" // For now, just free - proper drop needs type info");
self.writeln(" break;");
self.writeln(" default:");
self.writeln(" // ADT types (tag >= 100) - call generated drop function");
self.writeln(" lux_drop_adt(ptr, tag);");
self.writeln(" break;");
self.writeln(" }");
self.writeln(" // Free the object and its RC header");
if self.debug_rc {
self.writeln(" lux_rc_free_count++;");
}
self.writeln(" free(LUX_RC_HEADER(ptr));");
self.writeln("}");
self.writeln("");
self.writeln("// === Forward Declarations ===");
self.writeln("");
}
/// Emit specialized decref implementations (must be called after type definitions)
fn emit_specialized_decref_implementations(&mut self) {
self.writeln("// === Specialized Decref Implementations (Drop Specialization) ===");
self.writeln("// These avoid the polymorphic lux_drop dispatch when type is known");
self.writeln("");
self.writeln("// Specialized decref for lists - inline drop logic");
self.writeln("static inline void lux_decref_list(LuxList* list) {");
self.writeln(" if (list) {");
self.writeln(" LuxRcHeader* hdr = LUX_RC_HEADER(list);");
self.writeln(" if (--hdr->rc == 0) {");
self.writeln(" // Inline list drop - decref each element");
self.writeln(" for (int64_t i = 0; i < list->length; i++) {");
self.writeln(" lux_decref(list->elements[i]);");
self.writeln(" }");
self.writeln(" free(list->elements);");
if self.debug_rc {
self.writeln(" lux_rc_free_count++;");
}
self.writeln(" free(hdr);");
self.writeln(" }");
self.writeln(" }");
self.writeln("}");
self.writeln("");
self.writeln("// Specialized decref for closures - inline drop logic");
self.writeln("static inline void lux_decref_closure(LuxClosure* closure) {");
self.writeln(" if (closure) {");
self.writeln(" LuxRcHeader* hdr = LUX_RC_HEADER(closure);");
self.writeln(" if (--hdr->rc == 0) {");
self.writeln(" // Inline closure drop - decref environment");
self.writeln(" lux_decref(closure->env);");
if self.debug_rc {
self.writeln(" lux_rc_free_count++;");
}
self.writeln(" free(hdr);");
self.writeln(" }");
self.writeln(" }");
self.writeln("}");
self.writeln("");
self.writeln("// Specialized decref for strings - safe for both RC and static strings");
self.writeln("static inline void lux_decref_string(LuxString str) {");
self.writeln(" // Check magic number to distinguish RC-managed from static strings");
self.writeln(" // Static string literals don't have our header, so we skip them");
self.writeln(" if (LUX_IS_RC_MANAGED(str)) {");
self.writeln(" LuxRcHeader* hdr = LUX_RC_HEADER(str);");
self.writeln(" if (--hdr->rc == 0) {");
if self.debug_rc {
self.writeln(" lux_rc_free_count++;");
}
self.writeln(" free(hdr);");
self.writeln(" }");
self.writeln(" }");
self.writeln("}");
self.writeln("");
self.writeln("// Specialized decref for boxed primitives - no sub-references");
self.writeln("static inline void lux_decref_boxed(void* ptr) {");
self.writeln(" if (ptr) {");
self.writeln(" LuxRcHeader* hdr = LUX_RC_HEADER(ptr);");
self.writeln(" if (--hdr->rc == 0) {");
if self.debug_rc {
self.writeln(" lux_rc_free_count++;");
}
self.writeln(" free(hdr);");
self.writeln(" }");
self.writeln(" }");
self.writeln("}");
self.writeln("");
}
fn collect_type(&mut self, _type_decl: &TypeDecl) -> Result<(), CGenError> {
// Collect type info for later emission
Ok(())
}
fn emit_type_definitions(&mut self, program: &Program) -> Result<(), CGenError> {
for decl in &program.declarations {
if let Declaration::Type(t) = decl {
self.emit_type_def(t)?;
}
}
Ok(())
}
/// Emit the lux_drop_adt function that handles dropping ADT types
fn emit_adt_drop_function(&mut self) {
self.writeln("// === ADT Drop Function ===");
self.writeln("// Generated based on ADT type definitions.");
self.writeln("static void lux_drop_adt(void* ptr, int32_t tag) {");
self.indent += 1;
if self.adt_with_pointers.is_empty() {
// No ADTs with pointer fields - nothing to do
self.writeln("// No ADTs with pointer fields");
self.writeln("(void)ptr; (void)tag; // Unused");
} else {
self.writeln("switch (tag) {");
self.indent += 1;
// Generate cases for each ADT type with pointer fields
// Clone to avoid borrow issues
let adt_tags = self.adt_type_tags.clone();
let variant_field_types = self.variant_field_types.clone();
for (adt_name, tag) in &adt_tags {
if !self.adt_with_pointers.contains(adt_name) {
continue;
}
self.writeln(&format!("case {}: {{", tag));
self.indent += 1;
self.writeln(&format!("{}* adt = ({}*)ptr;", adt_name, adt_name));
// Find all variants of this ADT and their pointer fields
let mut variant_cases: Vec<(String, Vec<(usize, String)>)> = Vec::new();
for ((type_name, variant_name), field_types) in &variant_field_types {
if type_name != adt_name {
continue;
}
let pointer_fields: Vec<(usize, String)> = field_types.iter()
.enumerate()
.filter(|(_, t)| t.ends_with('*'))
.map(|(i, t)| (i, t.clone()))
.collect();
if !pointer_fields.is_empty() {
variant_cases.push((variant_name.clone(), pointer_fields));
}
}
if !variant_cases.is_empty() {
self.writeln("switch (adt->tag) {");
self.indent += 1;
for (variant_name, pointer_fields) in &variant_cases {
let variant_upper = variant_name.to_uppercase();
let variant_lower = variant_name.to_lowercase();
self.writeln(&format!("case {}_TAG_{}: {{", adt_name, variant_upper));
self.indent += 1;
for (field_idx, _field_type) in pointer_fields {
self.writeln(&format!("lux_decref(adt->data.{}.field{});", variant_lower, field_idx));
}
self.writeln("break;");
self.indent -= 1;
self.writeln("}");
}
self.writeln("default: break;");
self.indent -= 1;
self.writeln("}");
}
self.writeln("break;");
self.indent -= 1;
self.writeln("}");
}
self.writeln("default: break;");
self.indent -= 1;
self.writeln("}");
}
self.indent -= 1;
self.writeln("}");
self.writeln("");
}
fn emit_type_def(&mut self, type_decl: &TypeDecl) -> Result<(), CGenError> {
let name = &type_decl.name.name;
if self.types_emitted.contains(name) {
return Ok(());
}
self.types_emitted.insert(name.clone());
match &type_decl.definition {
TypeDef::Record(fields) => {
self.writeln(&format!("typedef struct {} {{", name));
self.indent += 1;
for field in fields {
let c_type = self.type_to_c(&field.typ)?;
self.writeln(&format!("{} {};", c_type, field.name.name));
}
self.indent -= 1;
self.writeln(&format!("}} {};", name));
self.writeln("");
}
TypeDef::Enum(variants) => {
// Record variant -> type mapping for pattern matching
for variant in variants {
self.variant_to_type.insert(variant.name.name.clone(), name.clone());
// Also record field types for pattern binding type inference
// Use self-check for recursive types (which become pointers)
let field_types: Vec<String> = match &variant.fields {
VariantFields::Tuple(fields) => {
fields.iter().map(|f| {
self.type_to_c_with_self_check(f, name).unwrap_or_else(|_| "LuxInt".to_string())
}).collect()
}
VariantFields::Record(fields) => {
fields.iter().map(|f| {
self.type_to_c_with_self_check(&f.typ, name).unwrap_or_else(|_| "LuxInt".to_string())
}).collect()
}
VariantFields::Unit => vec![],
};
// Track if this variant has pointer fields
let has_pointers = field_types.iter().any(|t| t.ends_with('*'));
if has_pointers {
self.adt_with_pointers.insert(name.clone());
}
self.variant_field_types.insert(
(name.clone(), variant.name.name.clone()),
field_types
);
}
// Assign a type tag to this ADT
if !self.adt_type_tags.contains_key(name) {
let tag = self.next_adt_tag;
self.adt_type_tags.insert(name.clone(), tag);
self.next_adt_tag += 1;
}
// Forward declare the main struct for recursive types
self.writeln(&format!("struct {};", name));
self.writeln(&format!("typedef struct {} {};", name, name));
self.writeln("");
// Emit tag enum
self.writeln(&format!("typedef enum {}_Tag {{", name));
self.indent += 1;
for (i, variant) in variants.iter().enumerate() {
let comma = if i < variants.len() - 1 { "," } else { "" };
self.writeln(&format!("{}_TAG_{}{}", name, variant.name.name.to_uppercase(), comma));
}
self.indent -= 1;
self.writeln(&format!("}} {}_Tag;", name));
self.writeln("");
// Emit variant structs
for variant in variants {
let has_fields = !matches!(&variant.fields, VariantFields::Unit);
if has_fields {
self.writeln(&format!("typedef struct {}_{}_Data {{", name, variant.name.name));
self.indent += 1;
match &variant.fields {
VariantFields::Tuple(fields) => {
for (i, field) in fields.iter().enumerate() {
let c_type = self.type_to_c_with_self_check(field, name)?;
self.writeln(&format!("{} field{};", c_type, i));
}
}
VariantFields::Record(fields) => {
for field in fields {
let c_type = self.type_to_c_with_self_check(&field.typ, name)?;
self.writeln(&format!("{} {};", c_type, field.name.name));
}
}
VariantFields::Unit => {}
}
self.indent -= 1;
self.writeln(&format!("}} {}_{}_Data;", name, variant.name.name));
self.writeln("");
}
}
// Emit main union struct (typedef already created above)
self.writeln(&format!("struct {} {{", name));
self.indent += 1;
self.writeln(&format!("{}_Tag tag;", name));
self.writeln("union {");
self.indent += 1;
for variant in variants {
let has_fields = !matches!(&variant.fields, VariantFields::Unit);
if has_fields {
self.writeln(&format!("{}_{}_Data {};", name, variant.name.name, variant.name.name.to_lowercase()));
}
}
self.indent -= 1;
self.writeln("} data;");
self.indent -= 1;
self.writeln("};");
self.writeln("");
}
TypeDef::Alias(_) => {
// Type aliases are handled during type resolution
}
}
Ok(())
}
fn mangle_name(&self, name: &str) -> String {
// Add suffix to avoid conflicts with C keywords and standard library
format!("{}_lux", name)
}
/// Escape C reserved keywords by adding underscore prefix
fn escape_c_keyword(&self, name: &str) -> String {
// C reserved keywords that might conflict with Lux variable names
const C_KEYWORDS: &[&str] = &[
// C89/C90 keywords
"auto", "break", "case", "char", "const", "continue", "default",
"do", "double", "else", "enum", "extern", "float", "for", "goto",
"if", "int", "long", "register", "return", "short", "signed",
"sizeof", "static", "struct", "switch", "typedef", "union",
"unsigned", "void", "volatile", "while",
// C99 keywords
"inline", "restrict", "_Bool", "_Complex", "_Imaginary",
// C11 keywords
"_Alignas", "_Alignof", "_Atomic", "_Generic", "_Noreturn",
"_Static_assert", "_Thread_local",
// Common type aliases and macros that might conflict
"bool", "true", "false", "NULL",
// Standard library identifiers that might conflict
"stdin", "stdout", "stderr", "errno", "main",
];
if C_KEYWORDS.contains(&name) {
format!("_{}", name)
} else {
name.to_string()
}
}
/// Check if an expression is a string expression (for concatenation detection)
#[allow(dead_code)]
fn is_string_expr(&self, expr: &Expr) -> bool {
match expr {
// String literals
Expr::Literal(lit) => matches!(lit.kind, LiteralKind::String(_)),
// Variables - check if we know their type
Expr::Var(ident) => {
let escaped = self.escape_c_keyword(&ident.name);
self.var_types.get(&escaped).map(|t| t == "LuxString").unwrap_or(false)
}
// toString function call or other string-returning functions
Expr::Call { func, .. } => {
if let Expr::Var(ident) = func.as_ref() {
if ident.name == "toString" {
return true;
}
// Check function return type
if let Some(ret_type) = self.function_return_types.get(&ident.name) {
return ret_type == "LuxString";
}
}
false
}
// String concatenation with +
Expr::BinaryOp { op, left, right, .. } => {
matches!(op, BinaryOp::Add)
&& (self.is_string_expr(left) || self.is_string_expr(right))
}
// Effect operations that return strings
Expr::EffectOp { effect, operation, .. } => {
match (effect.name.as_str(), operation.name.as_str()) {
("Process", "exec") | ("Process", "cwd") => true,
("Console", "readLine") => true,
("File", "read") => true,
("Http", "get") | ("Http", "post") | ("Http", "put") | ("Http", "delete") => true,
("String", "trim") => true,
_ => false,
}
}
// Field access on String module
Expr::Field { object, field, .. } => {
if let Expr::Var(module) = object.as_ref() {
module.name == "String" && matches!(field.name.as_str(), "trim")
} else {
false
}
}
_ => false,
}
}
fn emit_forward_declarations(&mut self, program: &Program) -> Result<(), CGenError> {
for decl in &program.declarations {
if let Declaration::Function(f) = decl {
let ret_type = self.type_expr_to_c(&f.return_type)?;
let params = self.emit_params(&f.params)?;
let mangled = self.mangle_name(&f.name.name);
// Add evidence parameter for effectful functions
let full_params = if !f.effects.is_empty() {
if params == "void" {
"LuxEvidence* ev".to_string()
} else {
format!("LuxEvidence* ev, {}", params)
}
} else {
params
};
self.writeln(&format!("{} {}({});", ret_type, mangled, full_params));
}
}
self.writeln("");
Ok(())
}
fn emit_function(&mut self, func: &FunctionDecl) -> Result<(), CGenError> {
let ret_type = self.type_expr_to_c(&func.return_type)?;
let params = self.emit_params(&func.params)?;
let mangled = self.mangle_name(&func.name.name);
// Check if this function uses effects (needs evidence parameter)
let is_effectful = !func.effects.is_empty();
// Build parameter list with evidence if needed
let full_params = if is_effectful {
if params == "void" {
"LuxEvidence* ev".to_string()
} else {
format!("LuxEvidence* ev, {}", params)
}
} else {
params.clone()
};
// Check for behavioral optimizations
let behavior = self.function_behaviors.get(&func.name.name).cloned();
let use_memoization = self.enable_behavioral_optimizations
&& behavior.as_ref().map_or(false, |b| b.is_pure)
&& !func.params.is_empty()
&& ret_type != "void"
&& ret_type != "LuxUnit";
let use_idempotent = self.enable_behavioral_optimizations
&& behavior.as_ref().map_or(false, |b| b.is_idempotent && !b.is_pure)
&& ret_type != "void"
&& ret_type != "LuxUnit";
let use_commutative = self.enable_behavioral_optimizations
&& behavior.as_ref().map_or(false, |b| b.is_commutative)
&& func.params.len() == 2;
let is_deterministic = behavior.as_ref().map_or(false, |b| b.is_deterministic);
self.writeln(&format!("{} {}({}) {{", ret_type, mangled, full_params));
self.indent += 1;
// Emit deterministic attribute hint
if is_deterministic {
self.emit_deterministic_attribute(&func.name.name);
}
// Emit commutative optimization (normalize argument order for better CSE)
if use_commutative {
self.emit_commutative_optimization(&func.name.name, &func.params)?;
}
// Emit memoization check for pure functions
if use_memoization {
self.emit_memoization_lookup(&func.name.name, &func.params, &ret_type)?;
}
// Emit idempotent check (for non-pure idempotent functions)
if use_idempotent {
self.emit_idempotent_check(&func.name.name, &func.params, &ret_type)?;
}
// Set evidence availability for expression generation
let prev_has_evidence = self.has_evidence;
if is_effectful {
self.has_evidence = true;
}
// Push function scope for RC tracking
self.push_rc_scope();
// Register function parameter types in var_types so closures can look them up
for param in &func.params {
let escaped = self.escape_c_keyword(&param.name.name);
if let Ok(c_type) = self.type_expr_to_c(&param.typ) {
self.var_types.insert(escaped.clone(), c_type);
// Track Option inner types for match pattern extraction
if let Some(inner) = self.option_inner_type_from_type_expr(&param.typ) {
self.var_option_inner_types.insert(escaped, inner);
}
}
}
// Emit function body
let result = self.emit_expr(&func.body)?;
// Restore previous evidence state
self.has_evidence = prev_has_evidence;
// For non-void functions, we need to save result, decref locals, then return
if ret_type != "void" && ret_type != "LuxUnit" {
// Check if result is a local RC variable we should skip decref'ing
let skip_var = if let Expr::Var(ident) = &func.body {
if self.is_var_in_current_rc_scope(&ident.name) {
Some(ident.name.clone())
} else {
None
}
} else if let Expr::Block { result: block_result, .. } = &func.body {
// For blocks, check if the result expression is a var in scope
if let Expr::Var(ident) = block_result.as_ref() {
if self.is_var_in_current_rc_scope(&ident.name) {
Some(ident.name.clone())
} else {
None
}
} else {
None
}
} else {
None
};
// Also check if result is an RC temp (like _str_concat_N) that we should skip
let skip_var = skip_var.or_else(|| {
if self.is_rc_temp(&result) {
Some(result.clone())
} else {
None
}
});
// Check if result is an RC type that we need to keep alive
let is_rc_result = self.is_rc_type(&ret_type);
let has_rc_locals = !self.rc_scopes.last().map_or(true, |s| s.is_empty());
if let Some(ref var_name) = skip_var {
// Result is a local variable or RC temp - skip decref'ing it and just return
self.pop_rc_scope_except(Some(var_name));
if use_memoization {
self.emit_memoization_store(&func.name.name, &func.params, &result)?;
}
if use_idempotent {
self.emit_idempotent_store(&func.name.name, &func.params, &result)?;
}
self.writeln(&format!("return {};", result));
} else if is_rc_result && has_rc_locals {
// Result is from a call or complex expression - use incref/decref pattern
self.writeln(&format!("{} _result = {};", ret_type, result));
self.writeln("lux_incref(_result);");
self.pop_rc_scope(); // Emit decrefs for all local RC vars
self.writeln("lux_decref(_result); // Balance the incref");
if use_memoization {
self.emit_memoization_store(&func.name.name, &func.params, "_result")?;
}
if use_idempotent {
self.emit_idempotent_store(&func.name.name, &func.params, "_result")?;
}
self.writeln("return _result;");
} else {
// No RC locals or non-RC result - simple cleanup
self.pop_rc_scope();
if use_memoization {
self.emit_memoization_store(&func.name.name, &func.params, &result)?;
}
if use_idempotent {
self.emit_idempotent_store(&func.name.name, &func.params, &result)?;
}
self.writeln(&format!("return {};", result));
}
} else {
// Void function - just cleanup
self.pop_rc_scope();
}
self.indent -= 1;
self.writeln("}");
self.writeln("");
Ok(())
}
fn emit_params(&self, params: &[Parameter]) -> Result<String, CGenError> {
if params.is_empty() {
return Ok("void".to_string());
}
let param_strs: Result<Vec<_>, _> = params.iter().map(|p| {
let c_type = self.type_expr_to_c(&p.typ)?;
let escaped_name = self.escape_c_keyword(&p.name.name);
Ok(format!("{} {}", c_type, escaped_name))
}).collect();
Ok(param_strs?.join(", "))
}
fn emit_expr(&mut self, expr: &Expr) -> Result<String, CGenError> {
match expr {
Expr::Literal(lit) => self.emit_literal(lit),
Expr::Var(ident) => {
// Check if this is a unit constructor (no-argument variant)
if let Some(type_name) = self.variant_to_type.get(&ident.name) {
// This is a constructor - emit struct literal
let variant_name = &ident.name;
Ok(format!("({}){{{}_TAG_{}}}", type_name, type_name, variant_name.to_uppercase()))
} else if let Some(renamed) = self.var_renames.get(&ident.name) {
// Variable has been renamed by a let binding
Ok(renamed.clone())
} else if self.functions.contains(&ident.name) {
// Function used as a value — wrap in a closure struct
let mangled = self.mangle_name(&ident.name);
let temp = format!("_fn_ref_{}", self.fresh_name());
self.writeln(&format!("LuxClosure* {} = lux_rc_alloc(sizeof(LuxClosure), LUX_TAG_CLOSURE);", temp));
self.writeln(&format!("{}->env = NULL;", temp));
self.writeln(&format!("{}->fn_ptr = (void*){};", temp, mangled));
Ok(temp)
} else {
// Escape C reserved keywords
Ok(self.escape_c_keyword(&ident.name))
}
}
Expr::BinaryOp { op, left, right, .. } => {
// Check if args are function calls returning String - need to track for cleanup
let left_is_string_call = if let Expr::Call { func, .. } = left.as_ref() {
if let Expr::Var(ident) = func.as_ref() {
self.function_return_types.get(&ident.name)
.map(|t| t == "LuxString")
.unwrap_or(false)
} else { false }
} else { false };
let right_is_string_call = if let Expr::Call { func, .. } = right.as_ref() {
if let Expr::Var(ident) = func.as_ref() {
self.function_return_types.get(&ident.name)
.map(|t| t == "LuxString")
.unwrap_or(false)
} else { false }
} else { false };
let l = self.emit_expr(left)?;
let r = self.emit_expr(right)?;
// Check for string concatenation
if matches!(op, BinaryOp::Add) {
let left_is_string = self.infer_expr_type(left).as_deref() == Some("LuxString");
let right_is_string = self.infer_expr_type(right).as_deref() == Some("LuxString");
if left_is_string || right_is_string {
// If args are function calls returning String, store in temp for cleanup
let (actual_l, l_temp) = if left_is_string_call && !self.is_rc_temp(&l) {
let temp = format!("_concat_arg_{}", self.fresh_name());
self.writeln(&format!("LuxString {} = {};", temp, l));
(temp.clone(), Some(temp))
} else {
(l.clone(), None)
};
let (actual_r, r_temp) = if right_is_string_call && !self.is_rc_temp(&r) {
let temp = format!("_concat_arg_{}", self.fresh_name());
self.writeln(&format!("LuxString {} = {};", temp, r));
(temp.clone(), Some(temp))
} else {
(r.clone(), None)
};
// String concat returns RC-managed string - track it
let temp = format!("_str_concat_{}", self.fresh_name());
self.writeln(&format!("LuxString {} = lux_string_concat({}, {});", temp, actual_l, actual_r));
self.register_rc_var(&temp, "LuxString");
// Decref any temporary strings used as inputs
// Now safe for both RC-managed and static strings due to magic check
if self.is_rc_temp(&l) {
self.writeln(&format!("lux_decref_string({});", l));
self.unregister_rc_var(&l);
} else if let Some(ref temp_l) = l_temp {
self.writeln(&format!("lux_decref_string({});", temp_l));
}
if self.is_rc_temp(&r) {
self.writeln(&format!("lux_decref_string({});", r));
self.unregister_rc_var(&r);
} else if let Some(ref temp_r) = r_temp {
self.writeln(&format!("lux_decref_string({});", temp_r));
}
return Ok(temp);
}
}
// Check for string comparison - use strcmp instead of pointer comparison
if matches!(op, BinaryOp::Eq | BinaryOp::Ne) {
let left_is_string = self.infer_expr_type(left).as_deref() == Some("LuxString");
let right_is_string = self.infer_expr_type(right).as_deref() == Some("LuxString");
if left_is_string || right_is_string {
if matches!(op, BinaryOp::Eq) {
return Ok(format!("(strcmp({}, {}) == 0)", l, r));
} else {
return Ok(format!("(strcmp({}, {}) != 0)", l, r));
}
}
}
// String concatenation for ++ and +
if matches!(op, BinaryOp::Add | BinaryOp::Concat) {
let left_is_string = self.infer_expr_type(left).as_deref() == Some("LuxString");
let right_is_string = self.infer_expr_type(right).as_deref() == Some("LuxString");
if left_is_string || right_is_string || matches!(op, BinaryOp::Concat) {
return Ok(format!("lux_string_concat({}, {})", l, r));
}
}
let op_str = match op {
BinaryOp::Add => "+",
BinaryOp::Concat => unreachable!("handled above"),
BinaryOp::Sub => "-",
BinaryOp::Mul => "*",
BinaryOp::Div => "/",
BinaryOp::Mod => "%",
BinaryOp::Eq => "==",
BinaryOp::Ne => "!=",
BinaryOp::Lt => "<",
BinaryOp::Le => "<=",
BinaryOp::Gt => ">",
BinaryOp::Ge => ">=",
BinaryOp::And => "&&",
BinaryOp::Or => "||",
BinaryOp::Pipe => {
// Pipe operator - for now, just call the right side with left as argument
return Ok(format!("{}({})", r, l));
}
};
Ok(format!("({} {} {})", l, op_str, r))
}
Expr::UnaryOp { op, operand, .. } => {
let val = self.emit_expr(operand)?;
let op_str = match op {
UnaryOp::Neg => "-",
UnaryOp::Not => "!",
};
Ok(format!("({}{})", op_str, val))
}
Expr::If { condition, then_branch, else_branch, .. } => {
// Check if this is a void/Unit-returning if (for side effects only)
let then_type = self.infer_expr_type(then_branch);
let else_type = self.infer_expr_type(else_branch);
let is_void = then_type.as_ref().map(|t| t == "void").unwrap_or(false)
|| else_type.as_ref().map(|t| t == "void").unwrap_or(false)
|| matches!(else_branch.as_ref(), Expr::Literal(lit) if matches!(lit.kind, LiteralKind::Unit));
if is_void {
// Void/Unit-returning if - emit as simple if statement
let cond = self.emit_expr(condition)?;
self.writeln(&format!("if ({}) {{", cond));
self.indent += 1;
let then_val = self.emit_expr(then_branch)?;
// Only emit if it's not just a Unit literal
if then_val != "0" && !then_val.is_empty() {
self.writeln(&format!("{};", then_val));
}
self.indent -= 1;
// Check if else branch is a Unit literal (meaning no else was provided)
let is_missing_else = matches!(else_branch.as_ref(), Expr::Literal(lit) if matches!(lit.kind, LiteralKind::Unit));
if !is_missing_else {
self.writeln("} else {");
self.indent += 1;
let else_val = self.emit_expr(else_branch)?;
if else_val != "0" && !else_val.is_empty() {
self.writeln(&format!("{};", else_val));
}
self.indent -= 1;
}
self.writeln("}");
Ok("0".to_string()) // Return dummy value for void expression
} else {
// Check if branches create RC values - if so, use if-statement to avoid
// allocating both when only one is needed
let then_creates_rc = self.expr_creates_rc_value(then_branch);
let else_creates_rc = self.expr_creates_rc_value(else_branch);
let then_emits_stmts = self.expr_emits_statements(then_branch);
let else_emits_stmts = self.expr_emits_statements(else_branch);
if then_creates_rc || else_creates_rc || then_emits_stmts || else_emits_stmts {
// Use if-statement pattern to avoid allocating unused branch
let result_type = self.infer_expr_type(then_branch)
.or_else(|| self.infer_expr_type(else_branch))
.unwrap_or_else(|| "LuxInt".to_string());
let result_var = format!("_if_result_{}", self.fresh_name());
// Declare result variable
self.writeln(&format!("{} {};", result_type, result_var));
// Emit condition
let cond = self.emit_expr(condition)?;
self.writeln(&format!("if ({}) {{", cond));
self.indent += 1;
// Push RC scope for then branch so temps are cleaned up within the branch
self.push_rc_scope();
// Emit then branch
let then_val = self.emit_expr(then_branch)?;
self.writeln(&format!("{} = {};", result_var, then_val));
// Pop RC scope - cleanup temps before leaving the branch
// Skip the then_val if it's being assigned to result (ownership transfer)
self.pop_rc_scope_except(Some(&then_val));
self.indent -= 1;
self.writeln("} else {");
self.indent += 1;
// Push RC scope for else branch
self.push_rc_scope();
// Emit else branch
let else_val = self.emit_expr(else_branch)?;
self.writeln(&format!("{} = {};", result_var, else_val));
// Pop RC scope - cleanup temps before leaving the branch
self.pop_rc_scope_except(Some(&else_val));
self.indent -= 1;
self.writeln("}");
Ok(result_var)
} else {
// Simple ternary for non-RC values
let cond = self.emit_expr(condition)?;
let then_val = self.emit_expr(then_branch)?;
let else_val = self.emit_expr(else_branch)?;
Ok(format!("({} ? {} : {})", cond, then_val, else_val))
}
}
}
Expr::Let { name, value, body, .. } => {
let val = self.emit_expr(value)?;
// Handle underscore pattern - just evaluate the expression, discard result
if name.name == "_" {
// Just emit the expression for its side effects
self.writeln(&format!("(void){};", val));
// Continue with body
let body_result = self.emit_expr(body)?;
return Ok(body_result);
}
let var_name = format!("{}_{}", name.name, self.fresh_name());
// Infer the type from the value expression
let var_type = self.infer_expr_type(value).unwrap_or_else(|| "LuxInt".to_string());
// Track Option inner type for match pattern extraction
if var_type == "Option" {
if let Some(inner) = self.infer_option_inner_type(value) {
self.var_option_inner_types.insert(var_name.clone(), inner);
}
}
self.writeln(&format!("{} {} = {};", var_type, var_name, val));
// Register the variable rename so nested expressions can find it
let old_rename = self.var_renames.insert(name.name.clone(), var_name.clone());
self.var_types.insert(var_name.clone(), var_type);
let body_result = self.emit_expr(body)?;
// Restore previous rename (or remove)
if let Some(prev) = old_rename {
self.var_renames.insert(name.name.clone(), prev);
} else {
self.var_renames.remove(&name.name);
}
Ok(body_result)
}
Expr::Call { func, args, .. } => {
// Check for module calls: List, String, Int, Float, and user-defined modules
if let Expr::Field { object, field, .. } = func.as_ref() {
if let Expr::Var(module_name) = object.as_ref() {
if module_name.name == "List" {
return self.emit_list_operation(&field.name, args);
}
if module_name.name == "Map" {
return self.emit_map_operation(&field.name, args);
}
// Int module
if module_name.name == "Int" && field.name == "toString" {
let arg = self.emit_expr(&args[0])?;
let temp = format!("_int_to_string_{}", self.fresh_name());
self.writeln(&format!("LuxString {} = lux_int_to_string({});", temp, arg));
self.register_rc_var(&temp, "LuxString");
return Ok(temp);
}
if module_name.name == "Int" && field.name == "toFloat" {
let arg = self.emit_expr(&args[0])?;
return Ok(format!("((LuxFloat){})", arg));
}
// Float module
if module_name.name == "Float" && field.name == "toString" {
let arg = self.emit_expr(&args[0])?;
let temp = format!("_float_to_string_{}", self.fresh_name());
self.writeln(&format!("LuxString {} = lux_float_to_string({});", temp, arg));
self.register_rc_var(&temp, "LuxString");
return Ok(temp);
}
if module_name.name == "Float" && field.name == "toInt" {
let arg = self.emit_expr(&args[0])?;
return Ok(format!("((LuxInt){})", arg));
}
// Math module
if module_name.name == "Math" {
return self.emit_math_operation(&field.name, args);
}
// Check for user-defined module function
let key = (module_name.name.clone(), field.name.clone());
if let Some(c_name) = self.module_functions.get(&key).cloned() {
let arg_strs: Result<Vec<_>, _> = args.iter().map(|a| self.emit_expr(a)).collect();
let args_str = arg_strs?.join(", ");
let is_effectful = self.effectful_functions.contains(&c_name);
let call_expr = if is_effectful && self.has_evidence {
if args_str.is_empty() {
format!("{}(ev)", c_name)
} else {
format!("{}(ev, {})", c_name, args_str)
}
} else if is_effectful {
if args_str.is_empty() {
format!("{}(&default_evidence)", c_name)
} else {
format!("{}(&default_evidence, {})", c_name, args_str)
}
} else {
format!("{}({})", c_name, args_str)
};
return Ok(call_expr);
}
}
}
// Check for built-in functions like toString
if let Expr::Var(ident) = func.as_ref() {
if ident.name == "toString" {
// toString converts Int to String - returns RC-managed string
let arg = self.emit_expr(&args[0])?;
let temp = format!("_to_string_{}", self.fresh_name());
self.writeln(&format!("LuxString {} = lux_int_to_string({});", temp, arg));
self.register_rc_var(&temp, "LuxString");
return Ok(temp);
}
}
let arg_strs: Result<Vec<_>, _> = args.iter().map(|a| self.emit_expr(a)).collect();
let args_str = arg_strs?.join(", ");
match func.as_ref() {
Expr::Var(ident) if self.functions.contains(&ident.name) => {
// Direct call to a known function
let c_func_name = self.mangle_name(&ident.name);
// Pass evidence if this function uses effects and we have evidence
let is_effectful = self.effectful_functions.contains(&ident.name);
let call_expr = if is_effectful && self.has_evidence {
if args_str.is_empty() {
format!("{}(ev)", c_func_name)
} else {
format!("{}(ev, {})", c_func_name, args_str)
}
} else if is_effectful {
// Calling effectful function but don't have evidence - use default
if args_str.is_empty() {
format!("{}(&default_evidence)", c_func_name)
} else {
format!("{}(&default_evidence, {})", c_func_name, args_str)
}
} else {
format!("{}({})", c_func_name, args_str)
};
Ok(call_expr)
}
Expr::Var(ident) if self.variant_to_type.contains_key(&ident.name) => {
// ADT constructor call - create struct with tag and data
let type_name = self.variant_to_type.get(&ident.name).unwrap().clone();
let variant_name = ident.name.clone();
let variant_lower = variant_name.to_lowercase();
let variant_upper = variant_name.to_uppercase();
// Look up field types to handle pointer fields
let field_types = self.variant_field_types
.get(&(type_name.clone(), variant_name.clone()))
.cloned()
.unwrap_or_default();
// Generate struct initialization
let arg_values: Result<Vec<_>, _> = args.iter().map(|a| self.emit_expr(a)).collect();
let arg_values = arg_values?;
// Build field initializers, handling pointer fields
// Clone adt_type_tags to avoid borrow issues
let adt_tags = self.adt_type_tags.clone();
let field_inits: Vec<String> = arg_values.iter()
.enumerate()
.map(|(i, v)| {
let field_type = field_types.get(i).map(|s| s.as_str()).unwrap_or("LuxInt");
if field_type.ends_with('*') {
// Pointer field - allocate with RC and copy
let base_type = &field_type[..field_type.len()-1];
// Look up type tag for this ADT (default to 100 if not found)
let type_tag = adt_tags.get(base_type).copied().unwrap_or(100);
format!(".field{} = ({}*)memcpy(lux_rc_alloc(sizeof({}), {}), &({}), sizeof({}))",
i, base_type, base_type, type_tag, v, base_type)
} else {
format!(".field{} = {}", i, v)
}
})
.collect();
if field_inits.is_empty() {
// Unit variant
Ok(format!("({}){{{}_TAG_{}}}", type_name, type_name, variant_upper))
} else {
// Variant with data
Ok(format!("({}){{{}_TAG_{}, .data.{} = {{{}}}}}",
type_name, type_name, variant_upper,
variant_lower, field_inits.join(", ")))
}
}
_ => {
// Indirect call - treat as closure
let closure_expr = self.emit_expr(func)?;
// Build the cast for the function pointer
// For now, assume all args are LuxInt and return LuxInt
let param_types: Vec<&str> = args.iter().map(|_| "LuxInt").collect();
let params_str = if param_types.is_empty() {
String::new()
} else {
format!(", {}", param_types.join(", "))
};
let args_with_env = if args_str.is_empty() {
format!("({})->env", closure_expr)
} else {
format!("({})->env, {}", closure_expr, args_str)
};
Ok(format!("((LuxInt(*)(void*{}))({})->fn_ptr)({})",
params_str, closure_expr, args_with_env))
}
}
}
Expr::Lambda { params, body, return_type, .. } => {
// Find free variables in the lambda body
let param_names: HashSet<String> = params.iter()
.map(|p| p.name.name.clone())
.collect();
let free_vars = self.find_free_vars(body, &param_names);
// Generate unique closure ID
let id = self.fresh_name();
// Determine parameter types (escape C keywords)
let param_pairs: Vec<(String, String)> = params.iter()
.map(|p| {
let typ = self.type_expr_to_c(&p.typ)
.unwrap_or_else(|_| "LuxInt".to_string());
(self.escape_c_keyword(&p.name.name), typ)
})
.collect();
// Determine return type (default to LuxInt)
let ret_type = return_type.as_ref()
.map(|t| self.type_expr_to_c(t).unwrap_or_else(|_| "LuxInt".to_string()))
.unwrap_or_else(|| "LuxInt".to_string());
// Determine captured variable types from var_types (fallback to LuxInt)
let env_fields: Vec<(String, String)> = free_vars.iter()
.map(|v| {
let escaped = self.escape_c_keyword(v);
let typ = self.var_types.get(&escaped)
.or_else(|| self.var_types.get(v.as_str()))
.cloned()
.unwrap_or_else(|| "LuxInt".to_string());
(escaped, typ)
})
.collect();
// Store closure info for later emission
self.closures.push(ClosureInfo {
id,
env_fields: env_fields.clone(),
params: param_pairs,
return_type: ret_type,
body: (**body).clone(),
});
// Generate code to create the closure at runtime
let temp_env = format!("_env_{}", id);
let temp_closure = format!("_closure_{}", id);
// Allocate and initialize environment struct (RC-managed)
if env_fields.is_empty() {
self.writeln(&format!("LuxClosure* {} = lux_rc_alloc(sizeof(LuxClosure), LUX_TAG_CLOSURE);", temp_closure));
self.writeln(&format!("{}->env = NULL;", temp_closure));
} else {
// Allocate RC-managed environment
self.writeln(&format!("LuxEnv_{}* {} = lux_rc_alloc(sizeof(LuxEnv_{}), LUX_TAG_ENV);", id, temp_env, id));
for (name, _) in &env_fields {
self.writeln(&format!("{}->{} = {};", temp_env, name, name));
}
// Allocate RC-managed closure
self.writeln(&format!("LuxClosure* {} = lux_rc_alloc(sizeof(LuxClosure), LUX_TAG_CLOSURE);", temp_closure));
self.writeln(&format!("{}->env = {};", temp_closure, temp_env));
}
self.writeln(&format!("{}->fn_ptr = (void*)lambda_{};", temp_closure, id));
Ok(temp_closure)
}
Expr::Block { statements, result, .. } => {
// Push a scope for this block's local variables
self.push_rc_scope();
for stmt in statements {
match stmt {
Statement::Let { name, value, .. } => {
// Handle underscore pattern - just evaluate the expression, discard result
if name.name == "_" {
let val = self.emit_expr(value)?;
// Just evaluate for side effects, don't store
if !val.is_empty() && !val.starts_with("(void)") {
self.writeln(&format!("(void){};", val));
}
continue;
}
// First, infer type from value expression (before emitting)
let inferred_type = self.infer_expr_type(value);
// Check for ownership transfer: assigning from another variable
let source_var = if let Expr::Var(ident) = value {
let escaped_source = self.escape_c_keyword(&ident.name);
// Check if source is an RC-tracked variable
if self.is_rc_tracked(&escaped_source) {
Some(escaped_source)
} else {
None
}
} else {
None
};
let val = self.emit_expr(value)?;
// Determine final type
let typ = if let Some(t) = inferred_type {
t
} else if val.starts_with("_closure_") || self.is_closure_returning_call(value) {
"LuxClosure*".to_string()
} else {
"LuxInt".to_string()
};
let escaped_name = self.escape_c_keyword(&name.name);
self.writeln(&format!("{} {} = {};", typ, escaped_name, val));
// Record variable type for future inference
self.var_types.insert(escaped_name.clone(), typ.clone());
// Track Option inner type for match pattern extraction
if typ == "Option" {
if let Some(inner) = self.infer_option_inner_type(value) {
self.var_option_inner_types.insert(escaped_name.clone(), inner);
}
}
// Handle ownership transfer or RC registration
if let Some(source_name) = source_var {
// Ownership transfer: unregister source, register dest
self.unregister_rc_var(&source_name);
self.register_rc_var(&escaped_name, &typ);
} else if self.is_rc_temp(&val) {
// Value is already an RC temp - transfer ownership to bound variable
self.unregister_rc_var(&val);
self.register_rc_var(&escaped_name, &typ);
} else if self.expr_creates_rc_value(value) {
// Register RC variable if it creates a new RC value
self.register_rc_var(&escaped_name, &typ);
} else if let Some(adt_name) = self.expr_creates_adt_with_pointers(value) {
// ADT with pointer fields - needs field cleanup at scope exit
self.register_rc_var_with_adt(&escaped_name, &typ, Some(adt_name));
}
}
Statement::Expr(e) => {
// Emit expression - if it's a function call that returns void/unit,
// we need to emit it as a statement
let expr_str = self.emit_expr(e)?;
// If emit_expr didn't write the call itself (for non-void returns),
// and it's a function call, emit it as a statement
if !expr_str.is_empty() && expr_str != "NULL" {
// Check if this is a function call expression
if let Expr::Call { func, .. } = e {
if let Expr::Var(ident) = func.as_ref() {
if self.functions.contains(&ident.name) {
// It's a function call - emit as statement
self.writeln(&format!("{};", expr_str));
}
}
}
}
}
}
}
// Emit the result expression
let result_val = self.emit_expr(result)?;
// Check if result references RC-tracked variables from this scope
// If so, we need to save the result before cleanup to avoid use-after-free
let result_uses_rc_vars = self.expr_uses_rc_vars_from_scope(result);
// Infer result type to check if it's void
let result_type = self.infer_expr_type(result);
let is_void = result_type.as_ref().map_or(false, |t| t == "void" || t == "LuxUnit");
// Check if the result is a variable from this scope - if so, skip decref'ing it
let skip_var = if let Expr::Var(ident) = result.as_ref() {
if self.is_var_in_current_rc_scope(&ident.name) {
Some(ident.name.as_str())
} else {
None
}
} else {
None
};
// If result uses RC vars that would be freed, save it first (but not for void results)
let final_result = if result_uses_rc_vars && skip_var.is_none() && !self.is_rc_temp(&result_val) && !is_void {
let typ = result_type.unwrap_or_else(|| "LuxInt".to_string());
let temp = format!("_block_result_{}", self.fresh_name());
self.writeln(&format!("{} {} = {};", typ, temp, result_val));
self.pop_rc_scope_except(None);
temp
} else {
self.pop_rc_scope_except(skip_var);
result_val
};
Ok(final_result)
}
Expr::EffectOp { effect, operation, args, .. } => {
self.effects_used.insert(effect.name.clone());
// List module operations (treated as effect by parser but handled specially)
if effect.name == "List" {
return self.emit_list_operation(&operation.name, args);
}
// Int module
if effect.name == "Int" {
match operation.name.as_str() {
"toString" => {
let arg = self.emit_expr(&args[0])?;
let temp = format!("_int_to_string_{}", self.fresh_name());
self.writeln(&format!("LuxString {} = lux_int_to_string({});", temp, arg));
self.register_rc_var(&temp, "LuxString");
return Ok(temp);
}
"toFloat" => {
let arg = self.emit_expr(&args[0])?;
return Ok(format!("((LuxFloat){})", arg));
}
_ => {}
}
}
// Float module
if effect.name == "Float" {
match operation.name.as_str() {
"toString" => {
let arg = self.emit_expr(&args[0])?;
let temp = format!("_float_to_string_{}", self.fresh_name());
self.writeln(&format!("LuxString {} = lux_float_to_string({});", temp, arg));
self.register_rc_var(&temp, "LuxString");
return Ok(temp);
}
"toInt" => {
let arg = self.emit_expr(&args[0])?;
return Ok(format!("((LuxInt){})", arg));
}
_ => {}
}
}
// Math module (treated as effect by parser but handled as direct C calls)
if effect.name == "Math" {
return self.emit_math_operation(&operation.name, args);
}
// Map module
if effect.name == "Map" {
return self.emit_map_operation(&operation.name, args);
}
// Built-in Console effect
if effect.name == "Console" {
if operation.name == "print" {
// Check if arg is a function call that returns an RC type (like String)
// If so, we need to store the result and decref it after printing
let needs_rc_cleanup = if let Expr::Call { func, .. } = &args[0] {
if let Expr::Var(ident) = func.as_ref() {
// Check if function returns LuxString
self.function_return_types.get(&ident.name)
.map(|t| t == "LuxString" || self.is_rc_type(t))
.unwrap_or(false)
} else {
false
}
} else {
false
};
let arg = self.emit_expr(&args[0])?;
// If it's a function call returning RC type, store in temp for cleanup
let actual_arg = if needs_rc_cleanup && !self.is_rc_temp(&arg) {
let temp = format!("_print_arg_{}", self.fresh_name());
self.writeln(&format!("LuxString {} = {};", temp, arg));
temp
} else {
arg.clone()
};
if self.has_evidence {
self.writeln(&format!("ev->console->print(ev->console->env, {});", actual_arg));
} else {
self.writeln(&format!("lux_console_print({});", actual_arg));
}
// Immediately decref string concat temps or function return temps after use
if self.is_rc_temp(&arg) {
self.writeln(&format!("lux_decref_string({});", arg));
self.unregister_rc_var(&arg);
} else if needs_rc_cleanup {
self.writeln(&format!("lux_decref_string({});", actual_arg));
}
return Ok("NULL".to_string());
} else if operation.name == "readLine" {
let temp = format!("_readLine_{}", self.fresh_name());
if self.has_evidence {
self.writeln(&format!("LuxString {} = ev->console->readLine(ev->console->env);", temp));
} else {
self.writeln(&format!("LuxString {} = lux_console_readLine();", temp));
}
self.register_rc_var(&temp, "LuxString");
return Ok(temp);
}
}
// Built-in Random effect
if effect.name == "Random" {
match operation.name.as_str() {
"int" => {
let min = self.emit_expr(&args[0])?;
let max = self.emit_expr(&args[1])?;
if self.has_evidence {
return Ok(format!("ev->random->randInt(ev->random->env, {}, {})", min, max));
} else {
return Ok(format!("lux_random_int({}, {})", min, max));
}
}
"float" => {
if self.has_evidence {
return Ok("ev->random->randFloat(ev->random->env)".to_string());
} else {
return Ok("lux_random_float()".to_string());
}
}
"bool" => {
if self.has_evidence {
return Ok("ev->random->randBool(ev->random->env)".to_string());
} else {
return Ok("lux_random_bool()".to_string());
}
}
_ => {}
}
}
// Built-in Time effect
if effect.name == "Time" {
match operation.name.as_str() {
"now" => {
if self.has_evidence {
return Ok("ev->time->now(ev->time->env)".to_string());
} else {
return Ok("lux_time_now()".to_string());
}
}
"sleep" => {
let ms = self.emit_expr(&args[0])?;
if self.has_evidence {
self.writeln(&format!("ev->time->sleep(ev->time->env, {});", ms));
} else {
self.writeln(&format!("lux_time_sleep({});", ms));
}
return Ok("NULL".to_string());
}
_ => {}
}
}
// Built-in File effect
if effect.name == "File" {
match operation.name.as_str() {
"read" => {
let path = self.emit_expr(&args[0])?;
let temp = format!("_file_read_{}", self.fresh_name());
if self.has_evidence {
self.writeln(&format!("LuxString {} = ev->file->read(ev->file->env, {});", temp, path));
} else {
self.writeln(&format!("LuxString {} = lux_file_read({});", temp, path));
}
self.register_rc_var(&temp, "LuxString");
return Ok(temp);
}
"write" => {
let path = self.emit_expr(&args[0])?;
let content = self.emit_expr(&args[1])?;
if self.has_evidence {
self.writeln(&format!("ev->file->write(ev->file->env, {}, {});", path, content));
} else {
self.writeln(&format!("lux_file_write({}, {});", path, content));
}
return Ok("NULL".to_string());
}
"append" => {
let path = self.emit_expr(&args[0])?;
let content = self.emit_expr(&args[1])?;
if self.has_evidence {
self.writeln(&format!("ev->file->append(ev->file->env, {}, {});", path, content));
} else {
self.writeln(&format!("lux_file_append({}, {});", path, content));
}
return Ok("NULL".to_string());
}
"exists" => {
let path = self.emit_expr(&args[0])?;
if self.has_evidence {
return Ok(format!("ev->file->exists(ev->file->env, {})", path));
} else {
return Ok(format!("lux_file_exists({})", path));
}
}
"delete" => {
let path = self.emit_expr(&args[0])?;
if self.has_evidence {
self.writeln(&format!("ev->file->delete_file(ev->file->env, {});", path));
} else {
self.writeln(&format!("lux_file_delete({});", path));
}
return Ok("NULL".to_string());
}
"isDir" => {
let path = self.emit_expr(&args[0])?;
if self.has_evidence {
return Ok(format!("ev->file->isDir(ev->file->env, {})", path));
} else {
return Ok(format!("lux_file_isDir({})", path));
}
}
"mkdir" => {
let path = self.emit_expr(&args[0])?;
if self.has_evidence {
self.writeln(&format!("ev->file->mkdir(ev->file->env, {});", path));
} else {
self.writeln(&format!("lux_file_mkdir({});", path));
}
return Ok("NULL".to_string());
}
"copy" => {
let src = self.emit_expr(&args[0])?;
let dst = self.emit_expr(&args[1])?;
if self.has_evidence {
self.writeln(&format!("ev->file->copy(ev->file->env, {}, {});", src, dst));
} else {
self.writeln(&format!("lux_file_copy({}, {});", src, dst));
}
return Ok("NULL".to_string());
}
"readDir" | "listDir" => {
let path = self.emit_expr(&args[0])?;
let temp = format!("_readdir_{}", self.fresh_name());
if self.has_evidence {
self.writeln(&format!("LuxList* {} = ev->file->readDir(ev->file->env, {});", temp, path));
} else {
self.writeln(&format!("LuxList* {} = lux_file_readDir({});", temp, path));
}
self.register_rc_var(&temp, "LuxList*");
return Ok(temp);
}
_ => {}
}
}
// Built-in Http effect
if effect.name == "Http" {
match operation.name.as_str() {
"get" => {
let url = self.emit_expr(&args[0])?;
let temp = format!("_http_get_{}", self.fresh_name());
if self.has_evidence {
self.writeln(&format!("LuxString {} = ev->http->get(ev->http->env, {});", temp, url));
} else {
self.writeln(&format!("LuxString {} = lux_http_get({});", temp, url));
}
self.register_rc_var(&temp, "LuxString");
return Ok(temp);
}
"post" => {
let url = self.emit_expr(&args[0])?;
let body = self.emit_expr(&args[1])?;
let temp = format!("_http_post_{}", self.fresh_name());
if self.has_evidence {
self.writeln(&format!("LuxString {} = ev->http->post(ev->http->env, {}, {});", temp, url, body));
} else {
self.writeln(&format!("LuxString {} = lux_http_post({}, {});", temp, url, body));
}
self.register_rc_var(&temp, "LuxString");
return Ok(temp);
}
"put" => {
let url = self.emit_expr(&args[0])?;
let body = self.emit_expr(&args[1])?;
let temp = format!("_http_put_{}", self.fresh_name());
if self.has_evidence {
self.writeln(&format!("LuxString {} = ev->http->put(ev->http->env, {}, {});", temp, url, body));
} else {
self.writeln(&format!("LuxString {} = lux_http_put({}, {});", temp, url, body));
}
self.register_rc_var(&temp, "LuxString");
return Ok(temp);
}
"delete" => {
let url = self.emit_expr(&args[0])?;
let temp = format!("_http_delete_{}", self.fresh_name());
if self.has_evidence {
self.writeln(&format!("LuxString {} = ev->http->delete_req(ev->http->env, {});", temp, url));
} else {
self.writeln(&format!("LuxString {} = lux_http_delete({});", temp, url));
}
self.register_rc_var(&temp, "LuxString");
return Ok(temp);
}
_ => {}
}
}
// Built-in Process effect
if effect.name == "Process" {
match operation.name.as_str() {
"exec" => {
let cmd = self.emit_expr(&args[0])?;
let temp = format!("_exec_{}", self.fresh_name());
if self.has_evidence {
self.writeln(&format!("LuxString {} = ev->process->exec(ev->process->env, {});", temp, cmd));
} else {
self.writeln(&format!("LuxString {} = lux_process_exec({});", temp, cmd));
}
self.register_rc_var(&temp, "LuxString");
return Ok(temp);
}
"env" => {
let name = self.emit_expr(&args[0])?;
// Process.env returns static string or NULL, not RC-managed
if self.has_evidence {
return Ok(format!("ev->process->getenv_fn(ev->process->env, {})", name));
} else {
return Ok(format!("lux_process_env({})", name));
}
}
"args" => {
let temp = format!("_args_{}", self.fresh_name());
if self.has_evidence {
self.writeln(&format!("LuxList* {} = ev->process->args(ev->process->env);", temp));
} else {
self.writeln(&format!("LuxList* {} = lux_process_args();", temp));
}
self.register_rc_var(&temp, "LuxList*");
return Ok(temp);
}
"cwd" => {
let temp = format!("_cwd_{}", self.fresh_name());
if self.has_evidence {
self.writeln(&format!("LuxString {} = ev->process->cwd(ev->process->env);", temp));
} else {
self.writeln(&format!("LuxString {} = lux_process_cwd();", temp));
}
self.register_rc_var(&temp, "LuxString");
return Ok(temp);
}
_ => {}
}
}
// Built-in String module (not an effect, but handled similarly)
if effect.name == "String" {
match operation.name.as_str() {
"trim" => {
let s = self.emit_expr(&args[0])?;
// Create temp variable and track for cleanup (returns RC-managed string)
let temp = format!("_trim_{}", self.fresh_name());
self.writeln(&format!("LuxString {} = lux_string_trim({});", temp, s));
self.register_rc_var(&temp, "LuxString");
return Ok(temp);
}
"length" => {
let s = self.emit_expr(&args[0])?;
return Ok(format!("lux_string_length({})", s));
}
"lines" => {
let s = self.emit_expr(&args[0])?;
// Create temp variable and track for cleanup (returns RC-managed list)
let temp = format!("_lines_{}", self.fresh_name());
self.writeln(&format!("LuxList* {} = lux_string_lines({});", temp, s));
self.register_rc_var(&temp, "LuxList*");
return Ok(temp);
}
"split" => {
let s = self.emit_expr(&args[0])?;
let sep = self.emit_expr(&args[1])?;
// Create temp variable and track for cleanup (returns RC-managed list)
let temp = format!("_split_{}", self.fresh_name());
self.writeln(&format!("LuxList* {} = lux_string_split({}, {});", temp, s, sep));
self.register_rc_var(&temp, "LuxList*");
return Ok(temp);
}
"contains" => {
let s = self.emit_expr(&args[0])?;
let sub = self.emit_expr(&args[1])?;
return Ok(format!("lux_string_contains({}, {})", s, sub));
}
"parseInt" => {
let s = self.emit_expr(&args[0])?;
return Ok(format!("lux_string_parseInt({})", s));
}
"parseFloat" => {
let s = self.emit_expr(&args[0])?;
return Ok(format!("lux_string_parseFloat({})", s));
}
"fromChar" => {
let c = self.emit_expr(&args[0])?;
// Create temp variable and track for cleanup (returns RC-managed string)
let temp = format!("_fromchar_{}", self.fresh_name());
self.writeln(&format!("LuxString {} = lux_string_from_char({});", temp, c));
self.register_rc_var(&temp, "LuxString");
return Ok(temp);
}
"chars" => {
let s = self.emit_expr(&args[0])?;
// Create temp variable and track for cleanup (returns RC-managed list)
let temp = format!("_chars_{}", self.fresh_name());
self.writeln(&format!("LuxList* {} = lux_string_chars({});", temp, s));
self.register_rc_var(&temp, "LuxList*");
return Ok(temp);
}
"substring" => {
let s = self.emit_expr(&args[0])?;
let start = self.emit_expr(&args[1])?;
let len = self.emit_expr(&args[2])?;
let temp = format!("_substr_{}", self.fresh_name());
self.writeln(&format!("LuxString {} = lux_string_substring({}, {}, {});", temp, s, start, len));
self.register_rc_var(&temp, "LuxString");
return Ok(temp);
}
"toUpper" => {
let s = self.emit_expr(&args[0])?;
let temp = format!("_upper_{}", self.fresh_name());
self.writeln(&format!("LuxString {} = lux_string_to_upper({});", temp, s));
self.register_rc_var(&temp, "LuxString");
return Ok(temp);
}
"toLower" => {
let s = self.emit_expr(&args[0])?;
let temp = format!("_lower_{}", self.fresh_name());
self.writeln(&format!("LuxString {} = lux_string_to_lower({});", temp, s));
self.register_rc_var(&temp, "LuxString");
return Ok(temp);
}
"replace" => {
let s = self.emit_expr(&args[0])?;
let from = self.emit_expr(&args[1])?;
let to = self.emit_expr(&args[2])?;
let temp = format!("_replace_{}", self.fresh_name());
self.writeln(&format!("LuxString {} = lux_string_replace({}, {}, {});", temp, s, from, to));
self.register_rc_var(&temp, "LuxString");
return Ok(temp);
}
"startsWith" => {
let s = self.emit_expr(&args[0])?;
let prefix = self.emit_expr(&args[1])?;
return Ok(format!("lux_string_starts_with({}, {})", s, prefix));
}
"endsWith" => {
let s = self.emit_expr(&args[0])?;
let suffix = self.emit_expr(&args[1])?;
return Ok(format!("lux_string_ends_with({}, {})", s, suffix));
}
"join" => {
let list = self.emit_expr(&args[0])?;
let sep = self.emit_expr(&args[1])?;
let temp = format!("_join_{}", self.fresh_name());
self.writeln(&format!("LuxString {} = lux_string_join({}, {});", temp, list, sep));
self.register_rc_var(&temp, "LuxString");
return Ok(temp);
}
"indexOf" => {
let s = self.emit_expr(&args[0])?;
let needle = self.emit_expr(&args[1])?;
let temp = format!("_indexOf_{}", self.fresh_name());
self.writeln(&format!("Option {} = lux_string_indexOf({}, {});", temp, s, needle));
return Ok(temp);
}
"lastIndexOf" => {
let s = self.emit_expr(&args[0])?;
let needle = self.emit_expr(&args[1])?;
let temp = format!("_lastIndexOf_{}", self.fresh_name());
self.writeln(&format!("Option {} = lux_string_lastIndexOf({}, {});", temp, s, needle));
return Ok(temp);
}
_ => {}
}
}
// Check for user-defined module function (via EffectOp path)
{
let key = (effect.name.clone(), operation.name.clone());
if let Some(c_name) = self.module_functions.get(&key).cloned() {
let arg_strs: Result<Vec<_>, _> = args.iter().map(|a| self.emit_expr(a)).collect();
let args_str = arg_strs?.join(", ");
let is_effectful = self.effectful_functions.contains(&c_name);
let call_expr = if is_effectful && self.has_evidence {
if args_str.is_empty() {
format!("{}(ev)", c_name)
} else {
format!("{}(ev, {})", c_name, args_str)
}
} else if is_effectful {
if args_str.is_empty() {
format!("{}(&default_evidence)", c_name)
} else {
format!("{}(&default_evidence, {})", c_name, args_str)
}
} else {
format!("{}({})", c_name, args_str)
};
return Ok(call_expr);
}
}
// For other effects, emit generic evidence-passing call
let arg_strs: Result<Vec<_>, _> = args.iter().map(|a| self.emit_expr(a)).collect();
if self.has_evidence {
Ok(format!("ev->{}->{}(ev->{}->env{}{})",
effect.name.to_lowercase(),
operation.name,
effect.name.to_lowercase(),
if arg_strs.as_ref().map_or(true, |v| v.is_empty()) { "" } else { ", " },
arg_strs?.join(", ")))
} else {
Ok(format!("lux_{}__{}({})",
effect.name.to_lowercase(),
operation.name,
arg_strs?.join(", ")))
}
}
Expr::Record {
spread, fields, ..
} => {
if let Some(spread_expr) = spread {
// Evaluate spread source, then override fields
let base = self.emit_expr(spread_expr)?;
if fields.is_empty() {
Ok(base)
} else {
// Copy spread into a temp, then override fields
let temp = format!("_spread_{}", self.fresh_name());
self.writeln(&format!("__auto_type {} = {};", temp, base));
for (name, val) in fields {
let v = self.emit_expr(val)?;
self.writeln(&format!("{}.{} = {};", temp, name.name, v));
}
Ok(temp)
}
} else {
let field_strs: Result<Vec<_>, _> = fields
.iter()
.map(|(name, val)| {
let v = self.emit_expr(val)?;
Ok(format!(".{} = {}", name.name, v))
})
.collect();
Ok(format!("{{ {} }}", field_strs?.join(", ")))
}
}
Expr::Field { object, field, .. } => {
let obj = self.emit_expr(object)?;
Ok(format!("{}.{}", obj, field.name))
}
Expr::TupleIndex { object, index, .. } => {
let obj = self.emit_expr(object)?;
Ok(format!("{}.__{}", obj, index))
}
Expr::Match { scrutinee, arms, .. } => {
self.emit_match(scrutinee, arms)
}
Expr::List { elements, .. } => {
self.emit_list_literal(elements)
}
_ => Err(CGenError {
message: format!("Unsupported expression type in C backend"),
span: None,
}),
}
}
fn emit_list_literal(&mut self, elements: &[Expr]) -> Result<String, CGenError> {
let list_var = format!("_list_{}", self.fresh_name());
let len = elements.len();
self.writeln(&format!("LuxList* {} = lux_list_new({});", list_var, len));
for (i, elem) in elements.iter().enumerate() {
let elem_val = self.emit_expr(elem)?;
let boxed = self.box_value(&elem_val, self.infer_expr_type(elem).as_deref());
self.writeln(&format!("{}->elements[{}] = {};", list_var, i, boxed));
}
self.writeln(&format!("{}->length = {};", list_var, len));
Ok(list_var)
}
fn box_value(&self, val: &str, type_hint: Option<&str>) -> String {
match type_hint {
Some("LuxInt") => format!("lux_box_int({})", val),
Some("LuxBool") => format!("lux_box_bool({})", val),
Some("LuxString") => format!("lux_box_string({})", val),
Some("LuxFloat") => format!("lux_box_float({})", val),
Some(t) if t.ends_with('*') => val.to_string(), // Already a pointer
Some(t) if t != "void" => {
// Non-primitive struct type — allocate on heap and copy
let tag = self.adt_type_tags.get(t).copied().unwrap_or(100);
format!("(void*)memcpy(lux_rc_alloc(sizeof({}), {}), &({}), sizeof({}))", t, tag, val, t)
}
_ => format!("(void*)(intptr_t)({})", val), // Unknown type — cast to void*
}
}
#[allow(dead_code)]
fn unbox_value(&self, val: &str, type_hint: Option<&str>) -> String {
match type_hint {
Some("LuxInt") => format!("lux_unbox_int({})", val),
Some("LuxBool") => format!("lux_unbox_bool({})", val),
Some("LuxString") => format!("lux_unbox_string({})", val),
_ => format!("lux_unbox_int({})", val), // Default to int unboxing
}
}
/// Emit code for Math module operations (Math.sin, Math.cos, etc.)
fn emit_math_operation(&mut self, op: &str, args: &[Expr]) -> Result<String, CGenError> {
match op {
"abs" => {
let x = self.emit_expr(&args[0])?;
Ok(format!("fabs({})", x))
}
"min" => {
let a = self.emit_expr(&args[0])?;
let b = self.emit_expr(&args[1])?;
Ok(format!("fmin({}, {})", a, b))
}
"max" => {
let a = self.emit_expr(&args[0])?;
let b = self.emit_expr(&args[1])?;
Ok(format!("fmax({}, {})", a, b))
}
"sqrt" => {
let x = self.emit_expr(&args[0])?;
Ok(format!("sqrt({})", x))
}
"pow" => {
let base = self.emit_expr(&args[0])?;
let exp = self.emit_expr(&args[1])?;
Ok(format!("pow({}, {})", base, exp))
}
"floor" => {
let x = self.emit_expr(&args[0])?;
Ok(format!("(int64_t)floor({})", x))
}
"ceil" => {
let x = self.emit_expr(&args[0])?;
Ok(format!("(int64_t)ceil({})", x))
}
"round" => {
let x = self.emit_expr(&args[0])?;
Ok(format!("(int64_t)round({})", x))
}
"sin" => {
let x = self.emit_expr(&args[0])?;
Ok(format!("sin({})", x))
}
"cos" => {
let x = self.emit_expr(&args[0])?;
Ok(format!("cos({})", x))
}
"atan2" => {
let y = self.emit_expr(&args[0])?;
let x = self.emit_expr(&args[1])?;
Ok(format!("atan2({}, {})", y, x))
}
_ => Err(CGenError {
message: format!("Math.{} not supported in C backend", op),
span: None,
}),
}
}
/// Emit code for List module operations (List.map, List.filter, etc.)
fn emit_list_operation(&mut self, op: &str, args: &[Expr]) -> Result<String, CGenError> {
match op {
// Simple operations - direct C function calls
"length" => {
if args.len() != 1 {
return Err(CGenError { message: "List.length takes 1 argument".to_string(), span: None });
}
let list = self.emit_expr(&args[0])?;
Ok(format!("lux_list_length({})", list))
}
"isEmpty" => {
if args.len() != 1 {
return Err(CGenError { message: "List.isEmpty takes 1 argument".to_string(), span: None });
}
let list = self.emit_expr(&args[0])?;
Ok(format!("lux_list_isEmpty({})", list))
}
"concat" => {
if args.len() != 2 {
return Err(CGenError { message: "List.concat takes 2 arguments".to_string(), span: None });
}
let list1 = self.emit_expr(&args[0])?;
let list2 = self.emit_expr(&args[1])?;
Ok(format!("lux_list_concat({}, {})", list1, list2))
}
"reverse" => {
if args.len() != 1 {
return Err(CGenError { message: "List.reverse takes 1 argument".to_string(), span: None });
}
let list = self.emit_expr(&args[0])?;
Ok(format!("lux_list_reverse({})", list))
}
"range" => {
if args.len() != 2 {
return Err(CGenError { message: "List.range takes 2 arguments".to_string(), span: None });
}
let start = self.emit_expr(&args[0])?;
let end = self.emit_expr(&args[1])?;
Ok(format!("lux_list_range({}, {})", start, end))
}
"take" => {
if args.len() != 2 {
return Err(CGenError { message: "List.take takes 2 arguments".to_string(), span: None });
}
let list = self.emit_expr(&args[0])?;
let n = self.emit_expr(&args[1])?;
Ok(format!("lux_list_take({}, {})", list, n))
}
"drop" => {
if args.len() != 2 {
return Err(CGenError { message: "List.drop takes 2 arguments".to_string(), span: None });
}
let list = self.emit_expr(&args[0])?;
let n = self.emit_expr(&args[1])?;
Ok(format!("lux_list_drop({}, {})", list, n))
}
// Access operations - return Option
"head" => {
if args.len() != 1 {
return Err(CGenError { message: "List.head takes 1 argument".to_string(), span: None });
}
let list = self.emit_expr(&args[0])?;
let result_var = format!("_head_{}", self.fresh_name());
// Need to incref the element since it's being extracted from the list
self.writeln(&format!("Option {};", result_var));
self.writeln(&format!("if ({0}->length > 0) {{", list));
self.indent += 1;
self.writeln(&format!("lux_incref({0}->elements[0]);", list));
self.writeln(&format!("{0} = lux_option_some({1}->elements[0]);", result_var, list));
self.indent -= 1;
self.writeln("} else {");
self.indent += 1;
self.writeln(&format!("{} = lux_option_none();", result_var));
self.indent -= 1;
self.writeln("}");
Ok(result_var)
}
"tail" => {
if args.len() != 1 {
return Err(CGenError { message: "List.tail takes 1 argument".to_string(), span: None });
}
let list = self.emit_expr(&args[0])?;
let result_var = format!("_tail_{}", self.fresh_name());
self.writeln(&format!("Option {};", result_var));
self.writeln(&format!("if ({0}->length > 0) {{", list));
self.indent += 1;
self.writeln(&format!("LuxList* _tail_list = lux_list_new({0}->length - 1);", list));
self.writeln(&format!("for (int64_t i = 1; i < {0}->length; i++) {{", list));
self.indent += 1;
// Incref each element being copied to the new list
self.writeln(&format!("lux_incref({0}->elements[i]);", list));
self.writeln(&format!("_tail_list->elements[i-1] = {0}->elements[i];", list));
self.indent -= 1;
self.writeln("}");
self.writeln(&format!("_tail_list->length = {0}->length - 1;", list));
self.writeln(&format!("{} = lux_option_some(_tail_list);", result_var));
self.indent -= 1;
self.writeln("} else {");
self.indent += 1;
self.writeln(&format!("{} = lux_option_none();", result_var));
self.indent -= 1;
self.writeln("}");
Ok(result_var)
}
"get" => {
if args.len() != 2 {
return Err(CGenError { message: "List.get takes 2 arguments".to_string(), span: None });
}
let list = self.emit_expr(&args[0])?;
let idx = self.emit_expr(&args[1])?;
let result_var = format!("_get_{}", self.fresh_name());
// Need to incref the element since it's being extracted from the list
self.writeln(&format!("Option {};", result_var));
self.writeln(&format!("if ({0} >= 0 && {0} < {1}->length) {{", idx, list));
self.indent += 1;
self.writeln(&format!("lux_incref({0}->elements[{1}]);", list, idx));
self.writeln(&format!("{0} = lux_option_some({1}->elements[{2}]);", result_var, list, idx));
self.indent -= 1;
self.writeln("} else {");
self.indent += 1;
self.writeln(&format!("{} = lux_option_none();", result_var));
self.indent -= 1;
self.writeln("}");
Ok(result_var)
}
// Higher-order operations - inline loops
// Type-aware: detects element types from the callback to use correct boxing/unboxing
"map" => {
if args.len() != 2 {
return Err(CGenError { message: "List.map takes 2 arguments".to_string(), span: None });
}
// Infer types BEFORE emitting (which consumes args via emit_expr)
let elem_type = self.infer_callback_param_type(&args[1], 0);
let ret_type = self.infer_callback_return_type(&args[1]);
let elem_is_prim = Self::is_primitive_c_type(&elem_type);
let ret_is_prim = Self::is_primitive_c_type(&ret_type);
let list = self.emit_expr(&args[0])?;
let closure = self.emit_expr(&args[1])?;
let id = self.fresh_name();
let result_var = format!("_map_{}", id);
let i_var = format!("_i_{}", id);
let elem_var = format!("_elem_{}", id);
let fn_var = format!("_fn_{}", id);
let mapped_var = format!("_mapped_{}", id);
// Build the call expression and result store based on types
let arg_pass = if elem_is_prim {
format!("{}({})", Self::unbox_fn_for_type(&elem_type), elem_var)
} else {
elem_var.clone()
};
let fn_cast = if elem_is_prim {
format!("({}(*)(void*, {}))", ret_type, elem_type)
} else {
format!("(void*(*)(void*, void*))")
};
let result_decl_type = if ret_is_prim { ret_type.clone() } else { "void*".to_string() };
let result_store = |mapped: &str| -> String {
if ret_is_prim {
format!("{}({})", Self::box_fn_for_type(&ret_type), mapped)
} else {
mapped.to_string()
}
};
// FBIP: Check if we can reuse the list in-place
self.writeln(&format!("LuxList* {};", result_var));
self.writeln(&format!("if (LUX_RC_HEADER({})->rc == 1) {{", list));
self.indent += 1;
if self.debug_rc {
self.writeln("lux_fbip_reuse_count++;");
}
self.writeln("// FBIP: Reuse list in-place");
self.writeln(&format!("{} = {};", result_var, list));
self.writeln(&format!("for (int64_t {} = 0; {} < {}->length; {}++) {{", i_var, i_var, list, i_var));
self.indent += 1;
self.writeln(&format!("void* {} = {}->elements[{}];", elem_var, list, i_var));
self.writeln(&format!("LuxClosure* {} = (LuxClosure*){};", fn_var, closure));
self.writeln(&format!("{} {} = ({}{}->fn_ptr)({}->env, {});",
result_decl_type, mapped_var, fn_cast, fn_var, fn_var, arg_pass));
self.writeln(&format!("lux_decref({}); // Decref old element", elem_var));
self.writeln(&format!("{}->elements[{}] = {};", result_var, i_var, result_store(&mapped_var)));
self.indent -= 1;
self.writeln("}");
self.indent -= 1;
self.writeln("} else {");
self.indent += 1;
if self.debug_rc {
self.writeln("lux_fbip_copy_count++;");
}
self.writeln("// Allocate new list");
self.writeln(&format!("{} = lux_list_new({}->length);", result_var, list));
self.writeln(&format!("for (int64_t {} = 0; {} < {}->length; {}++) {{", i_var, i_var, list, i_var));
self.indent += 1;
self.writeln(&format!("void* {} = {}->elements[{}];", elem_var, list, i_var));
self.writeln(&format!("LuxClosure* {} = (LuxClosure*){};", fn_var, closure));
self.writeln(&format!("{} {} = ({}{}->fn_ptr)({}->env, {});",
result_decl_type, mapped_var, fn_cast, fn_var, fn_var, arg_pass));
self.writeln(&format!("{}->elements[{}] = {};", result_var, i_var, result_store(&mapped_var)));
self.indent -= 1;
self.writeln("}");
self.writeln(&format!("{}->length = {}->length;", result_var, list));
self.indent -= 1;
self.writeln("}");
// Decref the closure if it was a temporary (inline lambda or fn ref)
if closure.starts_with("_closure_") || closure.starts_with("_fn_ref_") {
self.writeln(&format!("lux_decref_closure({});", closure));
}
Ok(result_var)
}
"filter" => {
if args.len() != 2 {
return Err(CGenError { message: "List.filter takes 2 arguments".to_string(), span: None });
}
let elem_type = self.infer_callback_param_type(&args[1], 0);
let elem_is_prim = Self::is_primitive_c_type(&elem_type);
let list = self.emit_expr(&args[0])?;
let closure = self.emit_expr(&args[1])?;
let id = self.fresh_name();
let result_var = format!("_filter_{}", id);
let count_var = format!("_count_{}", id);
let i_var = format!("_i_{}", id);
let elem_var = format!("_elem_{}", id);
let fn_var = format!("_fn_{}", id);
let keep_var = format!("_keep_{}", id);
let arg_pass = if elem_is_prim {
format!("{}({})", Self::unbox_fn_for_type(&elem_type), elem_var)
} else {
elem_var.clone()
};
let fn_cast = if elem_is_prim {
format!("(LuxBool(*)(void*, {}))", elem_type)
} else {
"(LuxBool(*)(void*, void*))".to_string()
};
// FBIP: Check if we can filter in-place
self.writeln(&format!("LuxList* {};", result_var));
self.writeln(&format!("int64_t {} = 0;", count_var));
self.writeln(&format!("if (LUX_RC_HEADER({})->rc == 1) {{", list));
self.indent += 1;
if self.debug_rc {
self.writeln("lux_fbip_reuse_count++;");
}
self.writeln("// FBIP: Filter in-place");
self.writeln(&format!("{} = {};", result_var, list));
self.writeln(&format!("for (int64_t {} = 0; {} < {}->length; {}++) {{", i_var, i_var, list, i_var));
self.indent += 1;
self.writeln(&format!("void* {} = {}->elements[{}];", elem_var, list, i_var));
self.writeln(&format!("LuxClosure* {} = (LuxClosure*){};", fn_var, closure));
self.writeln(&format!("LuxBool {} = ({}{}->fn_ptr)({}->env, {});", keep_var, fn_cast, fn_var, fn_var, arg_pass));
self.writeln(&format!("if ({}) {{", keep_var));
self.indent += 1;
self.writeln(&format!("{}->elements[{}++] = {};", result_var, count_var, elem_var));
self.indent -= 1;
self.writeln("} else {");
self.indent += 1;
self.writeln(&format!("lux_decref({}); // Decref filtered-out element", elem_var));
self.indent -= 1;
self.writeln("}");
self.indent -= 1;
self.writeln("}");
self.writeln(&format!("{}->length = {};", result_var, count_var));
self.indent -= 1;
self.writeln("} else {");
self.indent += 1;
if self.debug_rc {
self.writeln("lux_fbip_copy_count++;");
}
self.writeln("// Allocate new list");
self.writeln(&format!("{} = lux_list_new({}->length);", result_var, list));
self.writeln(&format!("for (int64_t {} = 0; {} < {}->length; {}++) {{", i_var, i_var, list, i_var));
self.indent += 1;
self.writeln(&format!("void* {} = {}->elements[{}];", elem_var, list, i_var));
self.writeln(&format!("LuxClosure* {} = (LuxClosure*){};", fn_var, closure));
self.writeln(&format!("LuxBool {} = ({}{}->fn_ptr)({}->env, {});", keep_var, fn_cast, fn_var, fn_var, arg_pass));
self.writeln(&format!("if ({}) {{", keep_var));
self.indent += 1;
// Incref the element since it's now shared between lists
self.writeln(&format!("lux_incref({});", elem_var));
self.writeln(&format!("{}->elements[{}++] = {};", result_var, count_var, elem_var));
self.indent -= 1;
self.writeln("}");
self.indent -= 1;
self.writeln("}");
self.writeln(&format!("{}->length = {};", result_var, count_var));
self.indent -= 1;
self.writeln("}");
// Decref the closure if it was a temporary (inline lambda or fn ref)
if closure.starts_with("_closure_") || closure.starts_with("_fn_ref_") {
self.writeln(&format!("lux_decref_closure({});", closure));
}
Ok(result_var)
}
"fold" | "foldLeft" => {
if args.len() != 3 {
return Err(CGenError { message: "List.fold takes 3 arguments".to_string(), span: None });
}
// Infer accumulator type from init, element type from callback
let init_type = self.infer_expr_type(&args[1]);
let elem_type = self.infer_callback_param_type(&args[2], 1);
let elem_is_prim = Self::is_primitive_c_type(&elem_type);
let list = self.emit_expr(&args[0])?;
let init = self.emit_expr(&args[1])?;
let closure = self.emit_expr(&args[2])?;
let id = self.fresh_name();
let result_var = format!("_fold_{}", id);
let i_var = format!("_i_{}", id);
let elem_var = format!("_elem_{}", id);
let fn_var = format!("_fn_{}", id);
let acc_c_type = match init_type.as_deref() {
Some(t) if t == "LuxInt" || t == "LuxBool" || t == "LuxString" ||
t == "LuxFloat" || t.ends_with('*') => t.to_string(),
Some(t) if t != "void" => t.to_string(), // Struct types like FMState, BState
_ => "void*".to_string(),
};
let arg_pass = if elem_is_prim {
format!("{}({})", Self::unbox_fn_for_type(&elem_type), elem_var)
} else {
elem_var.clone()
};
// Build fn pointer cast: acc_type (*)(void*, acc_type, elem_type_or_void*)
let elem_c_in_cast = if elem_is_prim { elem_type.clone() } else { "void*".to_string() };
let fn_cast = format!("({} (*)(void*, {}, {}))", acc_c_type, acc_c_type, elem_c_in_cast);
self.writeln(&format!("{} {} = ({})({});", acc_c_type, result_var, acc_c_type, init));
self.writeln(&format!("for (int64_t {} = 0; {} < {}->length; {}++) {{", i_var, i_var, list, i_var));
self.indent += 1;
self.writeln(&format!("void* {} = {}->elements[{}];", elem_var, list, i_var));
self.writeln(&format!("LuxClosure* {} = (LuxClosure*){};", fn_var, closure));
self.writeln(&format!("{} = ({}{}->fn_ptr)({}->env, {}, {});",
result_var, fn_cast, fn_var, fn_var, result_var, arg_pass));
self.indent -= 1;
self.writeln("}");
// Decref the closure if it was a temporary (inline lambda or fn ref)
if closure.starts_with("_closure_") || closure.starts_with("_fn_ref_") {
self.writeln(&format!("lux_decref_closure({});", closure));
}
Ok(result_var)
}
"find" => {
if args.len() != 2 {
return Err(CGenError { message: "List.find takes 2 arguments".to_string(), span: None });
}
let elem_type = self.infer_callback_param_type(&args[1], 0);
let elem_is_prim = Self::is_primitive_c_type(&elem_type);
let list = self.emit_expr(&args[0])?;
let closure = self.emit_expr(&args[1])?;
let id = self.fresh_name();
let result_var = format!("_find_{}", id);
let i_var = format!("_i_{}", id);
let elem_var = format!("_elem_{}", id);
let fn_var = format!("_fn_{}", id);
let matches_var = format!("_matches_{}", id);
let arg_pass = if elem_is_prim {
format!("{}({})", Self::unbox_fn_for_type(&elem_type), elem_var)
} else {
elem_var.clone()
};
let fn_cast = if elem_is_prim {
format!("(LuxBool(*)(void*, {}))", elem_type)
} else {
"(LuxBool(*)(void*, void*))".to_string()
};
self.writeln(&format!("Option {} = lux_option_none();", result_var));
self.writeln(&format!("for (int64_t {} = 0; {} < {}->length; {}++) {{", i_var, i_var, list, i_var));
self.indent += 1;
self.writeln(&format!("void* {} = {}->elements[{}];", elem_var, list, i_var));
self.writeln(&format!("LuxClosure* {} = (LuxClosure*){};", fn_var, closure));
self.writeln(&format!("LuxBool {} = ({}{}->fn_ptr)({}->env, {});", matches_var, fn_cast, fn_var, fn_var, arg_pass));
self.writeln(&format!("if ({}) {{", matches_var));
self.indent += 1;
self.writeln(&format!("{} = lux_option_some({});", result_var, elem_var));
self.writeln("break;");
self.indent -= 1;
self.writeln("}");
self.indent -= 1;
self.writeln("}");
// Decref the closure if it was a temporary (inline lambda or fn ref)
if closure.starts_with("_closure_") || closure.starts_with("_fn_ref_") {
self.writeln(&format!("lux_decref_closure({});", closure));
}
Ok(result_var)
}
"any" => {
if args.len() != 2 {
return Err(CGenError { message: "List.any takes 2 arguments".to_string(), span: None });
}
let elem_type = self.infer_callback_param_type(&args[1], 0);
let elem_is_prim = Self::is_primitive_c_type(&elem_type);
let list = self.emit_expr(&args[0])?;
let closure = self.emit_expr(&args[1])?;
let id = self.fresh_name();
let result_var = format!("_any_{}", id);
let i_var = format!("_i_{}", id);
let elem_var = format!("_elem_{}", id);
let fn_var = format!("_fn_{}", id);
let arg_pass = if elem_is_prim {
format!("{}({})", Self::unbox_fn_for_type(&elem_type), elem_var)
} else {
elem_var.clone()
};
let fn_cast = if elem_is_prim {
format!("(LuxBool(*)(void*, {}))", elem_type)
} else {
"(LuxBool(*)(void*, void*))".to_string()
};
self.writeln(&format!("LuxBool {} = 0;", result_var));
self.writeln(&format!("for (int64_t {} = 0; {} < {}->length; {}++) {{", i_var, i_var, list, i_var));
self.indent += 1;
self.writeln(&format!("void* {} = {}->elements[{}];", elem_var, list, i_var));
self.writeln(&format!("LuxClosure* {} = (LuxClosure*){};", fn_var, closure));
self.writeln(&format!("if (({}{}->fn_ptr)({}->env, {})) {{", fn_cast, fn_var, fn_var, arg_pass));
self.indent += 1;
self.writeln(&format!("{} = 1;", result_var));
self.writeln("break;");
self.indent -= 1;
self.writeln("}");
self.indent -= 1;
self.writeln("}");
// Decref the closure if it was a temporary (inline lambda or fn ref)
if closure.starts_with("_closure_") || closure.starts_with("_fn_ref_") {
self.writeln(&format!("lux_decref_closure({});", closure));
}
Ok(result_var)
}
"all" => {
if args.len() != 2 {
return Err(CGenError { message: "List.all takes 2 arguments".to_string(), span: None });
}
let elem_type = self.infer_callback_param_type(&args[1], 0);
let elem_is_prim = Self::is_primitive_c_type(&elem_type);
let list = self.emit_expr(&args[0])?;
let closure = self.emit_expr(&args[1])?;
let id = self.fresh_name();
let result_var = format!("_all_{}", id);
let i_var = format!("_i_{}", id);
let elem_var = format!("_elem_{}", id);
let fn_var = format!("_fn_{}", id);
let arg_pass = if elem_is_prim {
format!("{}({})", Self::unbox_fn_for_type(&elem_type), elem_var)
} else {
elem_var.clone()
};
let fn_cast = if elem_is_prim {
format!("(LuxBool(*)(void*, {}))", elem_type)
} else {
"(LuxBool(*)(void*, void*))".to_string()
};
self.writeln(&format!("LuxBool {} = 1;", result_var));
self.writeln(&format!("for (int64_t {} = 0; {} < {}->length; {}++) {{", i_var, i_var, list, i_var));
self.indent += 1;
self.writeln(&format!("void* {} = {}->elements[{}];", elem_var, list, i_var));
self.writeln(&format!("LuxClosure* {} = (LuxClosure*){};", fn_var, closure));
self.writeln(&format!("if (!({}{}->fn_ptr)({}->env, {})) {{", fn_cast, fn_var, fn_var, arg_pass));
self.indent += 1;
self.writeln(&format!("{} = 0;", result_var));
self.writeln("break;");
self.indent -= 1;
self.writeln("}");
self.indent -= 1;
self.writeln("}");
// Decref the closure if it was a temporary (inline lambda or fn ref)
if closure.starts_with("_closure_") || closure.starts_with("_fn_ref_") {
self.writeln(&format!("lux_decref_closure({});", closure));
}
Ok(result_var)
}
_ => Err(CGenError {
message: format!("Unsupported List operation: {}", op),
span: None,
}),
}
}
/// Emit code for Map module operations
fn emit_map_operation(&mut self, op: &str, args: &[Expr]) -> Result<String, CGenError> {
match op {
"new" => {
let temp = format!("_map_new_{}", self.fresh_name());
self.writeln(&format!("LuxMap* {} = lux_map_new(8);", temp));
Ok(temp)
}
"set" => {
let map = self.emit_expr(&args[0])?;
let key = self.emit_expr(&args[1])?;
let val = self.emit_expr(&args[2])?;
let boxed_val = self.box_value(&val, None);
let temp = format!("_map_set_{}", self.fresh_name());
self.writeln(&format!("LuxMap* {} = lux_map_set({}, {}, {});", temp, map, key, boxed_val));
Ok(temp)
}
"get" => {
let map = self.emit_expr(&args[0])?;
let key = self.emit_expr(&args[1])?;
let idx_temp = format!("_map_idx_{}", self.fresh_name());
let result_temp = format!("_map_get_{}", self.fresh_name());
self.writeln(&format!("int64_t {} = lux_map_find({}, {});", idx_temp, map, key));
self.writeln(&format!("Option {};", result_temp));
self.writeln(&format!("if ({} >= 0) {{", idx_temp));
self.indent += 1;
self.writeln(&format!("lux_incref({}->values[{}]);", map, idx_temp));
self.writeln(&format!("{} = lux_option_some({}->values[{}]);", result_temp, map, idx_temp));
self.indent -= 1;
self.writeln("} else {");
self.indent += 1;
self.writeln(&format!("{} = lux_option_none();", result_temp));
self.indent -= 1;
self.writeln("}");
Ok(result_temp)
}
"contains" => {
let map = self.emit_expr(&args[0])?;
let key = self.emit_expr(&args[1])?;
Ok(format!("lux_map_contains({}, {})", map, key))
}
"remove" => {
let map = self.emit_expr(&args[0])?;
let key = self.emit_expr(&args[1])?;
let temp = format!("_map_rm_{}", self.fresh_name());
self.writeln(&format!("LuxMap* {} = lux_map_remove({}, {});", temp, map, key));
Ok(temp)
}
"keys" => {
let map = self.emit_expr(&args[0])?;
let temp = format!("_map_keys_{}", self.fresh_name());
self.writeln(&format!("LuxList* {} = lux_list_new({}->length);", temp, map));
// Sort keys: simple insertion sort
self.writeln(&format!("for (int64_t _i = 0; _i < {}->length; _i++) {{", map));
self.indent += 1;
self.writeln(&format!("LuxString _ks = lux_string_dup({}->keys[_i]);", map));
self.writeln(&format!("lux_list_push({}, _ks);", temp));
self.indent -= 1;
self.writeln("}");
// Sort via bubble sort (small N)
self.writeln(&format!("for (int64_t _i = 0; _i < {}->length; _i++)", temp));
self.writeln(&format!(" for (int64_t _j = _i+1; _j < {}->length; _j++)", temp));
self.writeln(&format!(" if (strcmp({}->elements[_i], {}->elements[_j]) > 0) {{", temp, temp));
self.writeln(&format!(" void* _t = {}->elements[_i]; {}->elements[_i] = {}->elements[_j]; {}->elements[_j] = _t;", temp, temp, temp, temp));
self.writeln(" }");
Ok(temp)
}
"values" => {
let map = self.emit_expr(&args[0])?;
let temp = format!("_map_vals_{}", self.fresh_name());
self.writeln(&format!("LuxList* {} = lux_list_new({}->length);", temp, map));
// Sort by key first, then collect values
self.writeln(&format!("int64_t* _idx = (int64_t*)malloc(sizeof(int64_t) * {}->length);", map));
self.writeln(&format!("for (int64_t _i = 0; _i < {}->length; _i++) _idx[_i] = _i;", map));
self.writeln(&format!("for (int64_t _i = 0; _i < {}->length; _i++)", map));
self.writeln(&format!(" for (int64_t _j = _i+1; _j < {}->length; _j++)", map));
self.writeln(&format!(" if (strcmp({}->keys[_idx[_i]], {}->keys[_idx[_j]]) > 0) {{ int64_t _t = _idx[_i]; _idx[_i] = _idx[_j]; _idx[_j] = _t; }}", map, map));
self.writeln(&format!("for (int64_t _i = 0; _i < {}->length; _i++) {{", map));
self.indent += 1;
self.writeln(&format!("lux_incref({}->values[_idx[_i]]);", map));
self.writeln(&format!("lux_list_push({}, {}->values[_idx[_i]]);", temp, map));
self.indent -= 1;
self.writeln("}");
self.writeln("free(_idx);");
Ok(temp)
}
"size" => {
let map = self.emit_expr(&args[0])?;
Ok(format!("lux_map_size({})", map))
}
"isEmpty" => {
let map = self.emit_expr(&args[0])?;
Ok(format!("lux_map_isEmpty({})", map))
}
"fromList" => {
let list = self.emit_expr(&args[0])?;
let temp = format!("_map_fl_{}", self.fresh_name());
self.writeln(&format!("LuxMap* {} = lux_map_new({}->length);", temp, list));
self.writeln(&format!("for (int64_t _i = 0; _i < {}->length; _i++) {{", list));
self.indent += 1;
// Elements are tuples (boxed as void*) — we treat them as a simple 2-element struct
self.writeln("// Each element is a (String, V) tuple - not yet fully supported in C backend for Map");
self.indent -= 1;
self.writeln("}");
Ok(temp)
}
"toList" => {
let map = self.emit_expr(&args[0])?;
let temp = format!("_map_tl_{}", self.fresh_name());
self.writeln(&format!("LuxList* {} = lux_list_new({}->length);", temp, map));
self.writeln("// Map.toList not fully supported in C backend yet");
Ok(temp)
}
"merge" => {
let m1 = self.emit_expr(&args[0])?;
let m2 = self.emit_expr(&args[1])?;
let temp = format!("_map_merge_{}", self.fresh_name());
self.writeln(&format!("LuxMap* {} = lux_map_clone({});", temp, m1));
self.writeln(&format!("for (int64_t _i = 0; _i < {}->length; _i++) {{", m2));
self.indent += 1;
self.writeln(&format!("LuxMap* _next = lux_map_set({}, {}->keys[_i], {}->values[_i]);", temp, m2, m2));
self.writeln(&format!("free({}->keys); free({}->values); free({});", temp, temp, temp));
self.writeln(&format!("{} = _next;", temp));
self.indent -= 1;
self.writeln("}");
Ok(temp)
}
_ => Err(CGenError {
message: format!("Unsupported Map operation: {}", op),
span: None,
}),
}
}
fn emit_expr_with_substitution(&mut self, expr: &Expr, from: &str, to: &str) -> Result<String, CGenError> {
// Simple substitution - in a real implementation, this would be more sophisticated
match expr {
Expr::Var(ident) if ident.name == from => Ok(to.to_string()),
_ => self.emit_expr(expr),
}
}
fn emit_match(&mut self, expr: &Expr, arms: &[MatchArm]) -> Result<String, CGenError> {
let scrutinee = self.emit_expr(expr)?;
let scrutinee_var = format!("scrutinee_{}", self.fresh_name());
let result_var = format!("match_result_{}", self.fresh_name());
// Infer the type name from the first constructor pattern we find
let type_name = self.infer_type_name_from_arms(arms);
// Infer the result type from match arm bodies
// Try all arms since variables bound in patterns may not have known types yet
let result_type = self.infer_match_result_type(arms);
let is_void = result_type == "void";
// Infer scrutinee type: first from constructor patterns, then from scrutinee expr, then from literal patterns
let scrutinee_type = if let Some(ref tn) = type_name {
tn.clone()
} else if let Some(expr_type) = self.infer_expr_type(expr) {
expr_type
} else if self.has_string_literal_patterns(arms) {
"LuxString".to_string()
} else {
"LuxInt".to_string()
};
self.writeln(&format!("{} {} = {};", scrutinee_type, scrutinee_var, scrutinee));
// Don't declare a result variable for void-returning matches
if !is_void {
self.writeln(&format!("{} {};", result_type, result_var));
}
for (i, arm) in arms.iter().enumerate() {
let condition = self.pattern_to_condition(&arm.pattern, &scrutinee_var, type_name.as_deref())?;
if i == 0 {
self.writeln(&format!("if ({}) {{", condition));
} else {
self.writeln(&format!("}} else if ({}) {{", condition));
}
self.indent += 1;
// Push RC scope for this match arm so temp variables are cleaned up within the arm
self.push_rc_scope();
// Extract and emit variable bindings from the pattern
let bindings = self.extract_pattern_bindings(&arm.pattern, &scrutinee_var, type_name.as_deref());
for (var_name, c_expr, c_type) in &bindings {
// For void* from generic types like Option, try to infer the actual type
let actual_type = if c_type == "void*" {
// First try to infer from the scrutinee expression - this gives us the Option<T> type
let scrutinee_inner_type = self.infer_option_inner_type(expr);
if let Some(inner_type) = scrutinee_inner_type {
inner_type
} else {
// Try usage-based inference first — this looks at how the variable
// is actually used in the body, which is more reliable than body return type
if let Some(usage_type) = self.infer_var_type_from_usage(&arm.body, &var_name) {
usage_type
} else if let Expr::Var(v) = &arm.body {
// If body is just this variable (identity match), use the match result type
if v.name == *var_name && result_type != "LuxInt" && result_type != "void" {
result_type.clone()
} else {
"LuxString".to_string()
}
} else {
"LuxString".to_string()
}
}
} else {
c_type.clone()
};
// Emit the binding with proper casting
if c_type == "void*" {
// Cast from void* to actual type
if Self::is_primitive_c_type(&actual_type) {
// For primitive types stored as boxed void*, dereference
self.writeln(&format!("{} {} = *({}*)({});", actual_type, var_name, actual_type, c_expr));
} else if !actual_type.ends_with('*') && actual_type != "void" {
// Struct types: cast to pointer and dereference
self.writeln(&format!("{} {} = *({}*)({});", actual_type, var_name, actual_type, c_expr));
} else {
self.writeln(&format!("{} {} = ({})({});", actual_type, var_name, actual_type, c_expr));
}
self.var_types.insert(var_name.clone(), actual_type);
} else if actual_type.ends_with('*') && actual_type != "void*" {
// Dereference pointer types (but not void*)
let base_type = &actual_type[..actual_type.len()-1];
self.writeln(&format!("{} {} = *({});", base_type, var_name, c_expr));
self.var_types.insert(var_name.clone(), base_type.to_string());
} else {
self.writeln(&format!("{} {} = {};", actual_type, var_name, c_expr));
self.var_types.insert(var_name.clone(), actual_type);
}
}
let body = self.emit_expr(&arm.body)?;
if is_void {
// For void expressions, just emit the body without assignment
self.writeln(&format!("{};", body));
self.pop_rc_scope();
} else {
self.writeln(&format!("{} = {};", result_var, body));
// Pop RC scope, skip the body value if it's being assigned (ownership transfer)
self.pop_rc_scope_except(Some(&body));
}
self.indent -= 1;
}
self.writeln("}");
// For void matches, return a dummy NULL
if is_void {
Ok("NULL".to_string())
} else {
Ok(result_var)
}
}
/// Infer the result type of a match expression by checking all arm bodies
/// Returns the first concrete type found, or "LuxInt" as default
fn infer_match_result_type(&self, arms: &[MatchArm]) -> String {
// For pattern-bound variables, prioritize the pattern's field type info
// over var_types which may have stale entries from earlier functions
let mut found_void = false;
let mut found_void_ptr = false;
let mut best_type: Option<String> = None;
for arm in arms {
// First: for pattern-bound variables, use the pattern's type info
if let Expr::Var(body_ident) = &arm.body {
if let Some(t) = self.infer_pattern_binding_type(&arm.pattern, &body_ident.name) {
match t.as_str() {
"void" => { found_void = true; continue; }
"void*" => { found_void_ptr = true; continue; }
_ => return t,
}
}
}
// Second: try general expression type inference
if let Some(t) = self.infer_expr_type(&arm.body) {
match t.as_str() {
"void" => { found_void = true; }
_ => {
if best_type.is_none() {
best_type = Some(t);
}
}
}
}
}
if let Some(t) = best_type {
return t;
}
if found_void_ptr {
return "void*".to_string();
}
if found_void {
return "void".to_string();
}
// Default fallback
"LuxInt".to_string()
}
/// Infer the type of a pattern-bound variable by looking at what the pattern
/// destructs. E.g. Ok(x) in a Result match -> x is void* (generic).
fn infer_pattern_binding_type(&self, pattern: &Pattern, var_name: &str) -> Option<String> {
match pattern {
Pattern::Constructor { name, fields, .. } => {
// Check if any field binds the variable
for (i, field) in fields.iter().enumerate() {
if let Pattern::Var(ident) = field {
if ident.name == var_name {
// Look up the field type from variant_field_types
if let Some(type_name) = self.variant_to_type.get(&name.name) {
if let Some(field_types) = self.variant_field_types.get(&(type_name.clone(), name.name.clone())) {
if let Some(ft) = field_types.get(i) {
return Some(ft.clone());
}
}
}
}
}
}
None
}
Pattern::Var(ident) => {
if ident.name == var_name {
// This is a wildcard-like binding, type comes from scrutinee
None
} else {
None
}
}
_ => None,
}
}
/// Check if any arm has a string literal pattern
fn has_string_literal_patterns(&self, arms: &[MatchArm]) -> bool {
for arm in arms {
if let Pattern::Literal(lit) = &arm.pattern {
if matches!(&lit.kind, LiteralKind::String(_)) {
return true;
}
}
}
false
}
/// Try to infer the type name from match arms by looking at constructor patterns
fn infer_type_name_from_arms(&self, arms: &[MatchArm]) -> Option<String> {
for arm in arms {
if let Pattern::Constructor { name, .. } = &arm.pattern {
// Look up the variant in our mapping
if let Some(type_name) = self.variant_to_type.get(&name.name) {
return Some(type_name.clone());
}
// Fallback for built-in types that might not be in the map
match name.name.as_str() {
"Some" | "None" => return Some("Option".to_string()),
"Ok" | "Err" => return Some("Result".to_string()),
_ => {}
}
}
}
None
}
/// Try to infer the C type of an expression
fn infer_expr_type(&self, expr: &Expr) -> Option<String> {
match expr {
Expr::Literal(lit) => {
match &lit.kind {
LiteralKind::Int(_) => Some("LuxInt".to_string()),
LiteralKind::Float(_) => Some("LuxFloat".to_string()),
LiteralKind::Bool(_) => Some("LuxBool".to_string()),
LiteralKind::String(_) => Some("LuxString".to_string()),
LiteralKind::Char(_) => Some("char".to_string()),
LiteralKind::Unit => Some("void".to_string()),
}
}
Expr::Var(ident) => {
// Check if it's a unit constructor
if let Some(type_name) = self.variant_to_type.get(&ident.name) {
Some(type_name.clone())
} else {
// Check if we know the variable's type
let escaped = self.escape_c_keyword(&ident.name);
self.var_types.get(&escaped).cloned()
}
}
Expr::Call { func, .. } => {
// Check if calling a constructor
if let Expr::Var(ident) = func.as_ref() {
// toString returns LuxString
if ident.name == "toString" {
return Some("LuxString".to_string());
}
if let Some(type_name) = self.variant_to_type.get(&ident.name) {
return Some(type_name.clone());
}
// Check if calling a known function - use its return type
if let Some(ret_type) = self.function_return_types.get(&ident.name) {
return Some(ret_type.clone());
}
}
// Check module method calls like String.substring, List.map, etc.
if let Expr::Field { object, field, .. } = func.as_ref() {
if let Expr::Var(module) = object.as_ref() {
match module.name.as_str() {
"String" => match field.name.as_str() {
"length" => return Some("LuxInt".to_string()),
"indexOf" | "lastIndexOf" => return Some("Option".to_string()),
"substring" | "trim" | "toLower" | "toUpper" | "replace"
| "join" | "fromChar" | "repeat" => return Some("LuxString".to_string()),
"split" | "lines" | "chars" => return Some("LuxList*".to_string()),
"startsWith" | "endsWith" | "contains" => return Some("LuxBool".to_string()),
"parseInt" | "parseFloat" => return Some("Option".to_string()),
_ => {}
},
"List" => match field.name.as_str() {
"map" | "filter" | "concat" | "reverse" | "take" | "drop"
| "range" | "sort" | "sortBy" | "flatten" | "intersperse" => return Some("LuxList*".to_string()),
"head" | "tail" | "get" | "find" | "last" => return Some("Option".to_string()),
"length" => return Some("LuxInt".to_string()),
"fold" | "foldLeft" => {
// Fold return type is the type of the init value (2nd arg)
if let Expr::Call { args: call_args, .. } = expr {
if call_args.len() >= 2 {
if let Some(init_type) = self.infer_expr_type(&call_args[1]) {
return Some(init_type);
}
}
}
return None;
}
"isEmpty" | "any" | "all" | "contains" => return Some("LuxBool".to_string()),
_ => {}
},
"Int" => match field.name.as_str() {
"toString" => return Some("LuxString".to_string()),
"parse" => return Some("Option".to_string()),
"abs" | "min" | "max" => return Some("LuxInt".to_string()),
"toFloat" => return Some("LuxFloat".to_string()),
_ => {}
},
"Float" => match field.name.as_str() {
"toString" => return Some("LuxString".to_string()),
"parse" => return Some("Option".to_string()),
"toInt" => return Some("LuxInt".to_string()),
_ => return Some("LuxFloat".to_string()),
},
_ => {
// Check user-defined module functions
let key = (module.name.clone(), field.name.clone());
if let Some(c_name) = self.module_functions.get(&key) {
if let Some(ret_type) = self.function_return_types.get(c_name) {
return Some(ret_type.clone());
}
}
}
}
}
}
None
}
Expr::BinaryOp { op, left, right, .. } => {
// String concatenation with + returns LuxString
if matches!(op, BinaryOp::Add) {
let left_type = self.infer_expr_type(left);
let right_type = self.infer_expr_type(right);
if left_type.as_deref() == Some("LuxString") || right_type.as_deref() == Some("LuxString") {
return Some("LuxString".to_string());
}
return left_type.or(right_type);
}
// Comparison ops return bool
if matches!(op, BinaryOp::Eq | BinaryOp::Ne | BinaryOp::Lt | BinaryOp::Le | BinaryOp::Gt | BinaryOp::Ge) {
return Some("LuxBool".to_string());
}
if matches!(op, BinaryOp::And | BinaryOp::Or) {
return Some("LuxBool".to_string());
}
// For other binary ops, infer from operands
self.infer_expr_type(left).or_else(|| self.infer_expr_type(right))
}
Expr::Block { result, .. } => {
// Type of block is the type of the result expression
self.infer_expr_type(result)
}
Expr::If { then_branch, else_branch, .. } => {
// Type of if is the type of either branch (they should match)
self.infer_expr_type(then_branch)
.or_else(|| self.infer_expr_type(else_branch))
}
Expr::List { .. } => Some("LuxList*".to_string()),
Expr::EffectOp { effect, operation, args, .. } => {
// Int module
if effect.name == "Int" {
match operation.name.as_str() {
"toString" => return Some("LuxString".to_string()),
"toFloat" => return Some("LuxFloat".to_string()),
_ => return None,
}
}
// Float module
if effect.name == "Float" {
match operation.name.as_str() {
"toString" => return Some("LuxString".to_string()),
"toInt" => return Some("LuxInt".to_string()),
_ => return None,
}
}
// List operations have known return types
if effect.name == "List" {
match operation.name.as_str() {
// Operations returning lists
"map" | "filter" | "concat" | "reverse" | "take" | "drop" | "range" => Some("LuxList*".to_string()),
// Operations returning Option
"head" | "tail" | "get" | "find" => Some("Option".to_string()),
// Operations returning Int
"length" => Some("LuxInt".to_string()),
// Fold returns the type of the init value
"fold" | "foldLeft" => {
if args.len() >= 2 {
self.infer_expr_type(&args[1])
} else {
None
}
}
// Operations returning Bool
"isEmpty" | "any" | "all" => Some("LuxBool".to_string()),
_ => None,
}
} else if effect.name == "Random" {
match operation.name.as_str() {
"int" => Some("LuxInt".to_string()),
"float" => Some("LuxFloat".to_string()),
"bool" => Some("LuxBool".to_string()),
_ => None,
}
} else if effect.name == "Time" {
match operation.name.as_str() {
"now" => Some("LuxInt".to_string()),
"sleep" => Some("void".to_string()),
_ => None,
}
} else if effect.name == "File" {
match operation.name.as_str() {
"read" => Some("LuxString".to_string()),
"write" | "append" | "delete" | "mkdir" | "copy" => Some("void".to_string()),
"exists" | "isDir" => Some("LuxBool".to_string()),
"readDir" | "listDir" => Some("LuxList*".to_string()),
_ => None,
}
} else if effect.name == "Http" {
match operation.name.as_str() {
"get" | "post" | "put" | "delete" => Some("LuxString".to_string()),
_ => None,
}
} else if effect.name == "Console" {
match operation.name.as_str() {
"print" => Some("void".to_string()),
"readLine" => Some("LuxString".to_string()),
"readInt" => Some("LuxInt".to_string()),
_ => None,
}
} else if effect.name == "Process" {
match operation.name.as_str() {
"exec" | "cwd" => Some("LuxString".to_string()),
"env" => Some("Option".to_string()),
"args" => Some("LuxList*".to_string()),
_ => None,
}
} else if effect.name == "String" {
match operation.name.as_str() {
"trim" | "substring" | "toLower" | "toUpper" | "replace"
| "join" | "fromChar" | "repeat" => Some("LuxString".to_string()),
"length" => Some("LuxInt".to_string()),
"indexOf" | "lastIndexOf" => Some("Option".to_string()),
"lines" | "split" | "chars" => Some("LuxList*".to_string()),
"contains" | "startsWith" | "endsWith" => Some("LuxBool".to_string()),
"parseInt" | "parseFloat" => Some("Option".to_string()),
_ => None,
}
} else {
// Check user-defined module functions
let key = (effect.name.clone(), operation.name.clone());
if let Some(c_name) = self.module_functions.get(&key) {
self.function_return_types.get(c_name).cloned()
} else {
None
}
}
}
Expr::Match { arms, .. } => {
// Type of match is the type of its arms
Some(self.infer_match_result_type(arms))
}
Expr::Field { object, field, .. } => {
// Check if it's a record field access — look up object type + field
if let Some(obj_type) = self.infer_expr_type(object) {
// Look through variant_field_types for record fields
// For now, check var_types for the field access result
let field_key = format!("{}.{}", obj_type, field.name);
if let Some(t) = self.var_types.get(&field_key) {
return Some(t.clone());
}
}
None
}
Expr::Let { body, .. } => {
// Type of a let expression is the type of its body
self.infer_expr_type(body)
}
Expr::Lambda { .. } => Some("LuxClosure*".to_string()),
_ => None,
}
}
fn pattern_to_condition(&self, pattern: &Pattern, scrutinee: &str, type_name: Option<&str>) -> Result<String, CGenError> {
match pattern {
Pattern::Wildcard(_) => Ok("1".to_string()),
Pattern::Var(_) => Ok("1".to_string()), // Var always matches, binding handled separately
Pattern::Literal(lit) => {
let lit_val = self.emit_literal_value(&lit.kind)?;
// For strings, use strcmp instead of pointer comparison
if matches!(&lit.kind, LiteralKind::String(_)) {
Ok(format!("strcmp({}, {}) == 0", scrutinee, lit_val))
} else {
Ok(format!("{} == {}", scrutinee, lit_val))
}
}
Pattern::Constructor { name, fields, .. } => {
// Get the type name for proper tag generation
let tn = type_name.unwrap_or("Option"); // Default fallback
let tag_check = format!("{}.tag == {}_TAG_{}", scrutinee, tn, name.name.to_uppercase());
// If there are nested patterns, we need to check those too
if fields.is_empty() {
Ok(tag_check)
} else {
let mut conditions = vec![tag_check];
let variant_lower = name.name.to_lowercase();
for (i, field_pattern) in fields.iter().enumerate() {
// Access the field data
let field_access = format!("{}.data.{}.field{}", scrutinee, variant_lower, i);
let field_condition = self.pattern_to_condition(field_pattern, &field_access, None)?;
if field_condition != "1" {
conditions.push(field_condition);
}
}
Ok(conditions.join(" && "))
}
}
Pattern::Tuple { elements, .. } => {
// For tuples, check each element pattern
let mut conditions = Vec::new();
for (i, elem_pattern) in elements.iter().enumerate() {
let elem_access = format!("{}.field{}", scrutinee, i);
let elem_condition = self.pattern_to_condition(elem_pattern, &elem_access, None)?;
if elem_condition != "1" {
conditions.push(elem_condition);
}
}
if conditions.is_empty() {
Ok("1".to_string())
} else {
Ok(conditions.join(" && "))
}
}
Pattern::Record { .. } => Ok("1".to_string()), // TODO: record pattern matching
}
}
/// Try to infer the inner type of an Option expression
/// For List.tail, List.head, etc., returns the element/list type
fn infer_option_inner_type(&self, expr: &Expr) -> Option<String> {
match expr {
Expr::EffectOp { effect, operation, .. } => {
if effect.name == "List" {
match operation.name.as_str() {
"head" | "get" | "find" => {
// These return Option<T> where T is the element type
// Return None to let usage-based inference determine the actual type
None
}
"tail" => {
// tail returns Option<List<T>> - so inner type is LuxList*
Some("LuxList*".to_string())
}
_ => None,
}
} else if effect.name == "Process" && operation.name == "env" {
Some("LuxString".to_string())
} else if effect.name == "String" {
match operation.name.as_str() {
"parseInt" => Some("LuxInt".to_string()),
"parseFloat" => Some("LuxFloat".to_string()),
"indexOf" | "lastIndexOf" => Some("LuxInt".to_string()),
_ => None,
}
} else {
None
}
}
Expr::Call { func, args, .. } => {
if let Expr::Field { object, field, .. } = func.as_ref() {
if let Expr::Var(module) = object.as_ref() {
match module.name.as_str() {
"List" => match field.name.as_str() {
"head" | "get" | "find" => None,
"tail" => Some("LuxList*".to_string()),
_ => None,
},
"String" => match field.name.as_str() {
"indexOf" | "lastIndexOf" => Some("LuxInt".to_string()),
"parseInt" => Some("LuxInt".to_string()),
"parseFloat" => Some("LuxFloat".to_string()),
_ => None,
},
_ => None,
}
} else {
None
}
} else if let Expr::Var(ident) = func.as_ref() {
// User function that returns Option — check if this is String.indexOf/lastIndexOf
// emitted as a direct call, or try to look at the function's implementation
if let Some(ret) = self.function_return_types.get(&ident.name) {
if ret == "Option" {
// Check the function's param types to infer what kind of Option it returns
// For functions like findClosing that take string and int args and return Option<Int>,
// try to infer from the first non-string argument type
if let Some(param_types) = self.function_param_types.get(&ident.name) {
// Functions operating on string positions typically return Option<Int>
let has_int_params = param_types.iter().any(|t| t == "LuxInt");
if has_int_params {
return Some("LuxInt".to_string());
}
}
// Also check: if any of the call args are Int literals, likely Option<Int>
for arg in args {
if let Some(t) = self.infer_expr_type(arg) {
if t == "LuxInt" {
return Some("LuxInt".to_string());
}
}
}
}
}
None
} else {
None
}
}
Expr::Var(ident) => {
let escaped = self.escape_c_keyword(&ident.name);
// Check renamed variables first, then original name
let lookup_name = self.var_renames.get(&ident.name).unwrap_or(&escaped);
self.var_option_inner_types.get(lookup_name).cloned()
.or_else(|| self.var_option_inner_types.get(&escaped).cloned())
}
_ => None,
}
}
/// Infer a variable's type by looking at how it's used in an expression
fn infer_var_type_from_usage(&self, expr: &Expr, var_name: &str) -> Option<String> {
match expr {
Expr::Call { func, args, .. } => {
// Check if the variable is used as an argument
for (i, arg) in args.iter().enumerate() {
if let Expr::Var(ident) = arg {
if ident.name == var_name {
// Try to get the expected parameter type from function signature
if let Expr::Var(func_ident) = func.as_ref() {
if let Some(param_types) = self.function_param_types.get(&func_ident.name) {
if let Some(param_type) = param_types.get(i) {
return Some(param_type.clone());
}
}
}
// Check module method calls (String.substring, etc.)
if let Expr::Field { object, field, .. } = func.as_ref() {
if let Expr::Var(module) = object.as_ref() {
match (module.name.as_str(), field.name.as_str()) {
("String", "substring") if i >= 1 => return Some("LuxInt".to_string()),
("String", "indexOf" | "lastIndexOf") => return Some("LuxString".to_string()),
("String", "replace") => return Some("LuxString".to_string()),
("List", "get") if i == 1 => return Some("LuxInt".to_string()),
("List", "take" | "drop") if i == 1 => return Some("LuxInt".to_string()),
_ => {}
}
}
}
}
}
// Recursively check arguments
if let Some(t) = self.infer_var_type_from_usage(arg, var_name) {
return Some(t);
}
}
// Check the function itself recursively
if let Some(t) = self.infer_var_type_from_usage(func, var_name) {
return Some(t);
}
None
}
Expr::BinaryOp { op, left, right, .. } => {
// If var is used in arithmetic, it's likely LuxInt
let is_var_in_left = matches!(left.as_ref(), Expr::Var(id) if id.name == var_name);
let is_var_in_right = matches!(right.as_ref(), Expr::Var(id) if id.name == var_name);
if (is_var_in_left || is_var_in_right) &&
matches!(op, BinaryOp::Add | BinaryOp::Sub | BinaryOp::Mul | BinaryOp::Div | BinaryOp::Mod |
BinaryOp::Lt | BinaryOp::Le | BinaryOp::Gt | BinaryOp::Ge) {
// Check the other operand — if it's an Int literal or known Int, this var is Int
let other = if is_var_in_left { right } else { left };
let other_type = self.infer_expr_type(other);
if matches!(other_type.as_deref(), Some("LuxInt") | Some("LuxFloat")) {
return other_type;
}
}
self.infer_var_type_from_usage(left, var_name)
.or_else(|| self.infer_var_type_from_usage(right, var_name))
}
Expr::Block { statements, result, .. } => {
for stmt in statements {
match stmt {
crate::ast::Statement::Expr(e) => {
if let Some(t) = self.infer_var_type_from_usage(e, var_name) {
return Some(t);
}
}
crate::ast::Statement::Let { value, .. } => {
if let Some(t) = self.infer_var_type_from_usage(value, var_name) {
return Some(t);
}
}
}
}
self.infer_var_type_from_usage(result, var_name)
}
Expr::If { condition, then_branch, else_branch, .. } => {
self.infer_var_type_from_usage(condition, var_name)
.or_else(|| self.infer_var_type_from_usage(then_branch, var_name))
.or_else(|| self.infer_var_type_from_usage(else_branch, var_name))
}
Expr::Match { scrutinee, arms, .. } => {
if let Some(t) = self.infer_var_type_from_usage(scrutinee, var_name) {
return Some(t);
}
for arm in arms {
if let Some(t) = self.infer_var_type_from_usage(&arm.body, var_name) {
return Some(t);
}
}
None
}
Expr::Let { value, body, .. } => {
self.infer_var_type_from_usage(value, var_name)
.or_else(|| self.infer_var_type_from_usage(body, var_name))
}
_ => None,
}
}
/// Extract variable bindings from a pattern.
/// Returns a list of (var_name, c_expression, c_type) triples.
fn extract_pattern_bindings(&self, pattern: &Pattern, scrutinee: &str, expected_type: Option<&str>) -> Vec<(String, String, String)> {
match pattern {
Pattern::Wildcard(_) => vec![],
Pattern::Var(ident) => {
let typ = expected_type.unwrap_or("LuxInt").to_string();
let escaped_name = self.escape_c_keyword(&ident.name);
vec![(escaped_name, scrutinee.to_string(), typ)]
}
Pattern::Literal(_) => vec![],
Pattern::Constructor { name, fields, .. } => {
let mut bindings = Vec::new();
let variant_lower = name.name.to_lowercase();
// Look up field types for this variant
let type_name = self.variant_to_type.get(&name.name);
let field_types = type_name.and_then(|tn| {
self.variant_field_types.get(&(tn.clone(), name.name.clone()))
});
for (i, field_pattern) in fields.iter().enumerate() {
let field_access = format!("{}.data.{}.field{}", scrutinee, variant_lower, i);
let mut field_type = field_types.and_then(|ft| ft.get(i)).map(|s| s.as_str());
// Special handling for built-in Option type
// Option fields are void* in C but we need to preserve type info
if name.name == "Some" && field_type.is_none() {
// For Option<T>, the inner value should be treated as void*
// but we can infer more specific types from context
// Default to void* for safety (requires cast at use site)
field_type = Some("void*");
}
bindings.extend(self.extract_pattern_bindings(field_pattern, &field_access, field_type));
}
bindings
}
Pattern::Tuple { elements, .. } => {
let mut bindings = Vec::new();
for (i, elem_pattern) in elements.iter().enumerate() {
let elem_access = format!("{}.field{}", scrutinee, i);
bindings.extend(self.extract_pattern_bindings(elem_pattern, &elem_access, None));
}
bindings
}
Pattern::Record { fields, .. } => {
let mut bindings = Vec::new();
for (field_name, field_pattern) in fields {
let field_access = format!("{}.{}", scrutinee, field_name.name);
bindings.extend(self.extract_pattern_bindings(field_pattern, &field_access, None));
}
bindings
}
}
}
fn emit_literal(&self, lit: &Literal) -> Result<String, CGenError> {
self.emit_literal_value(&lit.kind)
}
fn emit_literal_value(&self, kind: &LiteralKind) -> Result<String, CGenError> {
match kind {
LiteralKind::Int(n) => Ok(format!("{}", n)),
LiteralKind::Float(f) => Ok(format!("{}", f)),
LiteralKind::Bool(b) => Ok(if *b { "true" } else { "false" }.to_string()),
LiteralKind::String(s) => Ok(format!("\"{}\"", escape_c_string(s))),
LiteralKind::Char(c) => Ok(format!("'{}'", c)),
LiteralKind::Unit => Ok("NULL".to_string()),
}
}
fn emit_global_let(&mut self, let_decl: &crate::ast::LetDecl) -> Result<(), CGenError> {
// Infer type from the value expression (or type annotation)
let typ = if let Some(ref type_expr) = let_decl.typ {
self.type_expr_to_c(type_expr).unwrap_or_else(|_| "LuxInt".to_string())
} else {
self.infer_expr_type(&let_decl.value).unwrap_or_else(|| "LuxInt".to_string())
};
let default = match typ.as_str() {
"LuxString" => "NULL",
"LuxBool" => "false",
"LuxFloat" => "0.0",
"LuxList*" | "LuxClosure*" => "NULL",
"Option" => "{0}",
"Result" => "{0}",
_ if typ.ends_with('*') => "NULL",
_ => "0",
};
self.writeln(&format!("static {} {} = {};", typ, let_decl.name.name, default));
self.var_types.insert(let_decl.name.name.clone(), typ);
self.writeln("");
Ok(())
}
fn emit_main_wrapper(&mut self, program: &Program) -> Result<(), CGenError> {
// Check if there's a main function
let has_main = program.declarations.iter().any(|d| {
matches!(d, Declaration::Function(f) if f.name.name == "main")
});
// Check for top-level run expressions or let bindings
let has_run = program.declarations.iter().any(|d| {
matches!(d, Declaration::Let(let_decl) if matches!(&let_decl.value, Expr::Run { .. }))
});
let has_global_lets = program.declarations.iter().any(|d| {
matches!(d, Declaration::Let(let_decl) if !matches!(&let_decl.value, Expr::Run { .. }))
});
if has_main || has_run || has_global_lets {
self.writeln("int main(int argc, char** argv) {");
self.indent += 1;
// Store argc/argv for Process.args()
self.writeln("lux_argc = argc;");
self.writeln("lux_argv = argv;");
self.writeln("");
// Initialize top-level let bindings (non-run) inside main
for decl in &program.declarations {
if let Declaration::Let(let_decl) = decl {
if !matches!(&let_decl.value, Expr::Run { .. }) {
let val = self.emit_expr(&let_decl.value)?;
self.writeln(&format!("{} = {};", let_decl.name.name, val));
}
}
}
// Execute top-level let bindings with run expressions
// Track if main was already called via a run expression
let mut main_called_via_run = false;
for decl in &program.declarations {
if let Declaration::Let(let_decl) = decl {
if matches!(&let_decl.value, Expr::Run { .. }) {
if let Expr::Run { expr, .. } = &let_decl.value {
if let Expr::Call { func, .. } = expr.as_ref() {
if let Expr::Var(fn_name) = func.as_ref() {
let mangled = self.mangle_name(&fn_name.name);
// Track if this is a call to main
if fn_name.name == "main" {
main_called_via_run = true;
}
// Pass default evidence if function uses effects
if self.effectful_functions.contains(&fn_name.name) {
self.writeln(&format!("{}(&default_evidence);", mangled));
} else {
self.writeln(&format!("{}();", mangled));
}
}
}
}
}
}
}
// If there's a main function and it wasn't already called via run, call it
if has_main && !main_called_via_run {
// Check if main uses effects (Console typically)
if self.effectful_functions.contains("main") {
self.writeln("main_lux(&default_evidence);");
} else {
self.writeln("main_lux();");
}
}
// Check for memory leaks in debug mode
if self.debug_rc {
self.writeln("lux_rc_check_leaks();");
}
self.writeln("return 0;");
self.indent -= 1;
self.writeln("}");
}
Ok(())
}
fn type_to_c(&self, type_expr: &TypeExpr) -> Result<String, CGenError> {
self.type_expr_to_c(type_expr)
}
/// Convert type to C, using pointers for self-referential types
fn type_to_c_with_self_check(&self, type_expr: &TypeExpr, parent_type: &str) -> Result<String, CGenError> {
if let TypeExpr::Named(ident) = type_expr {
if ident.name == parent_type {
// Self-referential type - use pointer
return Ok(format!("{}*", parent_type));
}
}
self.type_expr_to_c(type_expr)
}
/// Extract the inner C type from an Option<T> type expression
fn option_inner_type_from_type_expr(&self, type_expr: &TypeExpr) -> Option<String> {
if let TypeExpr::App(base, args) = type_expr {
if let TypeExpr::Named(name) = base.as_ref() {
if name.name == "Option" {
if let Some(inner) = args.first() {
return self.type_expr_to_c(inner).ok();
}
}
}
}
None
}
fn type_expr_to_c(&self, type_expr: &TypeExpr) -> Result<String, CGenError> {
match type_expr {
TypeExpr::Named(ident) => {
match ident.name.as_str() {
"Int" => Ok("LuxInt".to_string()),
"Float" => Ok("LuxFloat".to_string()),
"Bool" => Ok("LuxBool".to_string()),
"String" => Ok("LuxString".to_string()),
"Unit" => Ok("void".to_string()),
other => Ok(other.to_string()),
}
}
TypeExpr::App(base, _) => {
// Handle generic types
if let TypeExpr::Named(name) = base.as_ref() {
match name.name.as_str() {
"List" => Ok("LuxList*".to_string()),
"Option" => Ok("Option".to_string()),
_ => Ok("void*".to_string()),
}
} else {
Ok("void*".to_string())
}
}
TypeExpr::Unit => Ok("void".to_string()),
TypeExpr::Versioned { base, .. } => self.type_expr_to_c(base),
TypeExpr::Function { .. } => Ok("LuxClosure*".to_string()),
TypeExpr::Tuple(_) => Ok("void*".to_string()),
TypeExpr::Record(_) => Ok("void*".to_string()),
}
}
/// Infer the C type of a callback's parameter at the given index
fn infer_callback_param_type(&self, callback_expr: &Expr, param_index: usize) -> String {
match callback_expr {
Expr::Lambda { params, .. } => {
if let Some(param) = params.get(param_index) {
self.type_expr_to_c(&param.typ).unwrap_or_else(|_| "void*".to_string())
} else {
"void*".to_string()
}
}
Expr::Var(ident) => {
if let Some(types) = self.function_param_types.get(&ident.name) {
types.get(param_index).cloned().unwrap_or_else(|| "void*".to_string())
} else {
"void*".to_string()
}
}
_ => "void*".to_string(),
}
}
/// Infer the C return type of a callback expression
fn infer_callback_return_type(&self, callback_expr: &Expr) -> String {
match callback_expr {
Expr::Lambda { return_type, .. } => {
return_type.as_ref()
.and_then(|t| self.type_expr_to_c(t).ok())
.unwrap_or_else(|| "void*".to_string())
}
Expr::Var(ident) => {
self.function_return_types.get(&ident.name)
.cloned()
.unwrap_or_else(|| "void*".to_string())
}
_ => "void*".to_string(),
}
}
/// Check if a C type is a primitive (non-pointer) type requiring boxing/unboxing
fn is_primitive_c_type(typ: &str) -> bool {
matches!(typ, "LuxInt" | "LuxBool" | "LuxFloat" | "char")
}
/// Get the unbox function for a primitive C type
fn unbox_fn_for_type(typ: &str) -> &str {
match typ {
"LuxInt" => "lux_unbox_int",
"LuxBool" => "lux_unbox_bool",
"LuxFloat" => "lux_unbox_float",
_ => "lux_unbox_int",
}
}
/// Get the box function for a primitive C type
fn box_fn_for_type(typ: &str) -> &str {
match typ {
"LuxInt" => "lux_box_int",
"LuxBool" => "lux_box_bool",
"LuxFloat" => "lux_box_float",
"LuxString" => "lux_box_string",
_ => "",
}
}
fn fresh_name(&mut self) -> usize {
self.name_counter += 1;
self.name_counter
}
// === RC Scope Management ===
/// Push a new scope onto the RC scope stack
fn push_rc_scope(&mut self) {
self.rc_scopes.push(Vec::new());
}
/// Pop the current scope and emit decref calls for all variables
fn pop_rc_scope(&mut self) {
self.pop_rc_scope_except(None);
}
/// Pop the current scope and emit decref calls, skipping the named variable
/// This is used when returning a variable from a block - we don't want to decref it
fn pop_rc_scope_except(&mut self, skip_var: Option<&str>) {
if let Some(scope) = self.rc_scopes.pop() {
// Clone data we need to avoid borrow issues
let variant_field_types = self.variant_field_types.clone();
// Decref in reverse order (LIFO - last allocated, first freed)
for var in scope.iter().rev() {
// Skip the variable we're returning (if any)
if let Some(skip) = skip_var {
if var.name == skip {
continue;
}
}
if let Some(adt_name) = &var.adt_type_name {
// ADT with pointer fields - need to decref the fields
self.emit_adt_field_cleanup(&var.name, adt_name, &variant_field_types);
} else {
// Use specialized decref based on type (drop specialization)
self.emit_specialized_decref(&var.name, &var.c_type);
}
}
}
}
/// Emit a specialized decref call based on the known type (drop specialization)
/// This avoids the polymorphic lux_drop dispatch when we know the type at compile time
fn emit_specialized_decref(&mut self, var_name: &str, c_type: &str) {
match c_type {
"LuxList*" => {
self.writeln(&format!("lux_decref_list({});", var_name));
}
"LuxClosure*" => {
self.writeln(&format!("lux_decref_closure({});", var_name));
}
"LuxString" => {
self.writeln(&format!("lux_decref_string({});", var_name));
}
// Boxed primitives
"void*" => {
self.writeln(&format!("lux_decref_boxed({});", var_name));
}
// Fall back to generic decref for unknown types
_ => {
self.writeln(&format!("lux_decref({});", var_name));
}
}
}
/// Emit cleanup code for an ADT variable's pointer fields
fn emit_adt_field_cleanup(&mut self, var_name: &str, adt_name: &str, variant_field_types: &HashMap<(String, String), Vec<String>>) {
// Find all variants of this ADT with pointer fields
let mut has_cleanup = false;
for ((type_name, variant_name), field_types) in variant_field_types {
if type_name != adt_name {
continue;
}
let pointer_fields: Vec<(usize, &String)> = field_types.iter()
.enumerate()
.filter(|(_, t)| t.ends_with('*'))
.collect();
if !pointer_fields.is_empty() {
if !has_cleanup {
self.writeln(&format!("switch ({}.tag) {{", var_name));
self.indent += 1;
has_cleanup = true;
}
let variant_upper = variant_name.to_uppercase();
let variant_lower = variant_name.to_lowercase();
self.writeln(&format!("case {}_TAG_{}: {{", adt_name, variant_upper));
self.indent += 1;
for (field_idx, _) in &pointer_fields {
self.writeln(&format!("lux_decref({}.data.{}.field{});", var_name, variant_lower, field_idx));
}
self.writeln("break;");
self.indent -= 1;
self.writeln("}");
}
}
if has_cleanup {
self.writeln("default: break;");
self.indent -= 1;
self.writeln("}");
}
}
/// Register an RC-managed variable in the current scope
fn register_rc_var(&mut self, name: &str, c_type: &str) {
self.register_rc_var_with_adt(name, c_type, None);
}
fn register_rc_var_with_adt(&mut self, name: &str, c_type: &str, adt_type_name: Option<String>) {
if let Some(scope) = self.rc_scopes.last_mut() {
scope.push(RcVariable {
name: name.to_string(),
c_type: c_type.to_string(),
adt_type_name,
});
}
}
/// Check if a variable is tracked for RC cleanup in any scope
fn is_rc_tracked(&self, name: &str) -> bool {
self.rc_scopes.iter().any(|scope| scope.iter().any(|var| var.name == name))
}
/// Check if a value is a temporary RC variable (starts with _ and is tracked)
fn is_rc_temp(&self, name: &str) -> bool {
name.starts_with("_") && self.is_rc_tracked(name)
}
/// Remove a variable from RC tracking (for ownership transfer)
fn unregister_rc_var(&mut self, name: &str) {
for scope in self.rc_scopes.iter_mut() {
scope.retain(|var| var.name != name);
}
}
/// Emit decrefs for all variables in all scopes (for early return)
#[allow(dead_code)]
fn emit_all_scope_cleanup(&mut self) {
// Collect all decrefs first to avoid borrow issues
let decrefs: Vec<String> = self.rc_scopes.iter().rev()
.flat_map(|scope| scope.iter().rev())
.map(|var| format!("lux_decref({});", var.name))
.collect();
for decref in decrefs {
self.writeln(&decref);
}
}
/// Check if a C type needs RC management
fn is_rc_type(&self, c_type: &str) -> bool {
// Pointer types that are RC-managed
matches!(c_type, "LuxList*" | "LuxClosure*" | "void*")
|| c_type.ends_with("*") && c_type != "LuxString"
// Note: LuxString (char*) needs special handling - only dynamic strings are RC
}
/// Check if a variable name is in the current RC scope
fn is_var_in_current_rc_scope(&self, name: &str) -> bool {
if let Some(scope) = self.rc_scopes.last() {
scope.iter().any(|var| var.name == name)
} else {
false
}
}
/// Check if an expression references RC-tracked variables from the current scope
fn expr_uses_rc_vars_from_scope(&self, expr: &Expr) -> bool {
match expr {
Expr::Var(ident) => self.is_var_in_current_rc_scope(&ident.name),
Expr::Call { func, args, .. } => {
self.expr_uses_rc_vars_from_scope(func)
|| args.iter().any(|a| self.expr_uses_rc_vars_from_scope(a))
}
Expr::BinaryOp { left, right, .. } => {
self.expr_uses_rc_vars_from_scope(left)
|| self.expr_uses_rc_vars_from_scope(right)
}
Expr::UnaryOp { operand, .. } => self.expr_uses_rc_vars_from_scope(operand),
Expr::Field { object, .. } | Expr::TupleIndex { object, .. } => self.expr_uses_rc_vars_from_scope(object),
Expr::EffectOp { args, .. } => {
args.iter().any(|a| self.expr_uses_rc_vars_from_scope(a))
}
Expr::If { condition, then_branch, else_branch, .. } => {
self.expr_uses_rc_vars_from_scope(condition)
|| self.expr_uses_rc_vars_from_scope(then_branch)
|| self.expr_uses_rc_vars_from_scope(else_branch)
}
_ => false,
}
}
/// Check if an expression will emit C statements (variable declarations, etc.)
/// which means it cannot be safely used in a ternary expression.
fn expr_emits_statements(&self, expr: &Expr) -> bool {
match expr {
Expr::Block { statements, result, .. } => {
!statements.is_empty() || self.expr_emits_statements(result)
}
Expr::Let { .. } => true,
Expr::Match { .. } => true,
Expr::If { then_branch, else_branch, .. } => {
self.expr_emits_statements(then_branch) || self.expr_emits_statements(else_branch)
}
Expr::Call { func, args, .. } => {
// Constructor calls with field access (e.g. BState(...)) may be fine,
// but calls to functions returning strings will emit concat temps
if let Expr::Var(ident) = func.as_ref() {
if let Some(ret_type) = self.function_return_types.get(&ident.name) {
if ret_type == "LuxString" {
return true;
}
}
}
// Check if any argument emits statements
args.iter().any(|a| self.expr_emits_statements(a))
}
Expr::BinaryOp { op, left, right, .. } => {
// String concatenation emits temp variables
if matches!(op, BinaryOp::Add) {
let lt = self.infer_expr_type(left);
let rt = self.infer_expr_type(right);
if lt.as_deref() == Some("LuxString") || rt.as_deref() == Some("LuxString") {
return true;
}
}
self.expr_emits_statements(left) || self.expr_emits_statements(right)
}
Expr::Lambda { .. } => true,
Expr::List { .. } => true,
Expr::EffectOp { .. } => true,
_ => false,
}
}
/// Check if an expression creates a new RC-managed value that needs tracking
fn expr_creates_rc_value(&self, expr: &Expr) -> bool {
match expr {
// List literals create new RC lists
Expr::List { .. } => true,
// Lambdas create RC-managed closures
Expr::Lambda { .. } => true,
// Calls to List.* / String.* that return RC values, or user functions
Expr::Call { func, .. } => {
if let Expr::Field { object, field, .. } = func.as_ref() {
if let Expr::Var(module) = object.as_ref() {
if module.name == "List" {
// These List operations return new lists
return matches!(field.name.as_str(),
"map" | "filter" | "concat" | "reverse" |
"take" | "drop" | "range"
);
}
if module.name == "String" {
// These String operations return new RC strings
return matches!(field.name.as_str(),
"trim" | "lines" | "split"
);
}
}
}
// Check if calling a user-defined function that returns an RC type
if let Expr::Var(fn_name) = func.as_ref() {
if let Some(ret_type) = self.function_return_types.get(&fn_name.name) {
// Include LuxString since our string functions now return RC strings
return ret_type.ends_with('*') || ret_type == "LuxString";
}
}
false
}
// Effect operations that return RC values
Expr::EffectOp { effect, operation, .. } => {
if effect.name == "List" {
matches!(operation.name.as_str(),
"map" | "filter" | "concat" | "reverse" |
"take" | "drop" | "range"
)
} else if effect.name == "Process" {
// Process.exec returns RC-managed string
matches!(operation.name.as_str(), "exec")
} else if effect.name == "Console" {
// Console.readLine returns RC-managed string
matches!(operation.name.as_str(), "readLine")
} else if effect.name == "File" {
// File.read returns RC-managed string
matches!(operation.name.as_str(), "read")
} else if effect.name == "Http" {
// Http operations return RC-managed strings
matches!(operation.name.as_str(), "get" | "post" | "put" | "delete")
} else if effect.name == "String" {
// String.trim, lines, split return RC values
matches!(operation.name.as_str(), "trim" | "lines" | "split")
} else {
false
}
}
// Variable references don't create new values - they borrow
Expr::Var(_) => false,
// Literals don't need RC (primitives or static strings)
Expr::Literal(_) => false,
_ => false,
}
}
/// Check if an expression creates an ADT with pointer fields that need cleanup
/// Returns Some(adt_type_name) if so, None otherwise
fn expr_creates_adt_with_pointers(&self, expr: &Expr) -> Option<String> {
match expr {
// ADT constructor call
Expr::Call { func, .. } => {
if let Expr::Var(ident) = func.as_ref() {
// Check if this is an ADT constructor
if let Some(adt_name) = self.variant_to_type.get(&ident.name) {
// Check if this ADT has pointer fields
if self.adt_with_pointers.contains(adt_name) {
return Some(adt_name.clone());
}
}
}
None
}
_ => None,
}
}
/// Check if an expression is a call to a function that returns a closure
fn is_closure_returning_call(&self, expr: &Expr) -> bool {
match expr {
Expr::Lambda { .. } => true,
Expr::Call { func, .. } => {
// Check if we're calling a function known to return a closure
if let Expr::Var(ident) = func.as_ref() {
self.closure_returning_functions.contains(&ident.name)
} else {
false
}
}
_ => false,
}
}
/// Find free variables in an expression (variables not in bound set)
fn find_free_vars(&self, expr: &Expr, bound: &HashSet<String>) -> Vec<String> {
let mut free = Vec::new();
self.collect_free_vars(expr, bound, &mut free);
// Remove duplicates while preserving order
let mut seen = HashSet::new();
free.retain(|v| seen.insert(v.clone()));
free
}
fn collect_free_vars(&self, expr: &Expr, bound: &HashSet<String>, free: &mut Vec<String>) {
match expr {
Expr::Var(ident) => {
// If not bound locally, not a known function, and not a constructor, it's a free variable
if !bound.contains(&ident.name)
&& !self.functions.contains(&ident.name)
&& !self.variant_to_type.contains_key(&ident.name)
{
free.push(ident.name.clone());
}
}
Expr::Literal(_) => {}
Expr::BinaryOp { left, right, .. } => {
self.collect_free_vars(left, bound, free);
self.collect_free_vars(right, bound, free);
}
Expr::UnaryOp { operand, .. } => {
self.collect_free_vars(operand, bound, free);
}
Expr::If { condition, then_branch, else_branch, .. } => {
self.collect_free_vars(condition, bound, free);
self.collect_free_vars(then_branch, bound, free);
self.collect_free_vars(else_branch, bound, free);
}
Expr::Call { func, args, .. } => {
self.collect_free_vars(func, bound, free);
for arg in args {
self.collect_free_vars(arg, bound, free);
}
}
Expr::Block { statements, result, .. } => {
let mut inner_bound = bound.clone();
for stmt in statements {
match stmt {
Statement::Let { name, value, .. } => {
self.collect_free_vars(value, &inner_bound, free);
inner_bound.insert(name.name.clone());
}
Statement::Expr(e) => {
self.collect_free_vars(e, &inner_bound, free);
}
}
}
self.collect_free_vars(result, &inner_bound, free);
}
Expr::Lambda { params, body, .. } => {
let mut inner_bound = bound.clone();
for p in params {
inner_bound.insert(p.name.name.clone());
}
self.collect_free_vars(body, &inner_bound, free);
}
Expr::Record { spread, fields, .. } => {
if let Some(spread_expr) = spread {
self.collect_free_vars(spread_expr, bound, free);
}
for (_, val) in fields {
self.collect_free_vars(val, bound, free);
}
}
Expr::Field { object, .. } | Expr::TupleIndex { object, .. } => {
self.collect_free_vars(object, bound, free);
}
Expr::Match { scrutinee, arms, .. } => {
self.collect_free_vars(scrutinee, bound, free);
for arm in arms {
// TODO: Handle pattern bindings
self.collect_free_vars(&arm.body, bound, free);
}
}
Expr::EffectOp { args, .. } => {
for arg in args {
self.collect_free_vars(arg, bound, free);
}
}
Expr::Run { expr, .. } => {
self.collect_free_vars(expr, bound, free);
}
Expr::Tuple { elements, .. } => {
for e in elements {
self.collect_free_vars(e, bound, free);
}
}
Expr::List { elements, .. } => {
for e in elements {
self.collect_free_vars(e, bound, free);
}
}
Expr::Resume { value, .. } => {
self.collect_free_vars(value, bound, free);
}
Expr::Let { value, body, name, .. } => {
self.collect_free_vars(value, bound, free);
let mut inner_bound = bound.clone();
inner_bound.insert(name.name.clone());
self.collect_free_vars(body, &inner_bound, free);
}
}
}
/// Emit memoization lookup code for pure functions
///
/// For pure functions, we generate a static memo table that caches results.
/// This is a simple linear cache for now - a hash table would be more efficient
/// for functions called with many different arguments.
fn emit_memoization_lookup(&mut self, func_name: &str, params: &[Parameter], ret_type: &str) -> Result<(), CGenError> {
let mangled = self.mangle_name(func_name);
let memo_size = 64; // Fixed size memo table
// Generate a hash expression from parameters
let hash_expr = if params.len() == 1 {
let p = &params[0];
let c_type = self.type_expr_to_c(&p.typ)?;
match c_type.as_str() {
"LuxInt" => format!("((size_t){} & {})", self.escape_c_keyword(&p.name.name), memo_size - 1),
"LuxString" => format!("(lux_string_hash({}) & {})", self.escape_c_keyword(&p.name.name), memo_size - 1),
"LuxBool" => format!("((size_t){} & {})", self.escape_c_keyword(&p.name.name), memo_size - 1),
_ => format!("((size_t)(uintptr_t){} & {})", self.escape_c_keyword(&p.name.name), memo_size - 1),
}
} else {
// For multiple params, combine hashes
let mut parts = Vec::new();
for (i, p) in params.iter().enumerate() {
let c_type = self.type_expr_to_c(&p.typ)?;
let hash = match c_type.as_str() {
"LuxInt" => format!("(size_t){}", self.escape_c_keyword(&p.name.name)),
"LuxString" => format!("lux_string_hash({})", self.escape_c_keyword(&p.name.name)),
"LuxBool" => format!("(size_t){}", self.escape_c_keyword(&p.name.name)),
_ => format!("(size_t)(uintptr_t){}", self.escape_c_keyword(&p.name.name)),
};
// Mix with prime numbers for better distribution
let prime = [31, 37, 41, 43, 47, 53, 59, 61][i % 8];
parts.push(format!("({} * {})", hash, prime));
}
format!("(({}) & {})", parts.join(" ^ "), memo_size - 1)
};
// Emit static memo table
self.writeln(&format!("// Memoization for pure function {}", func_name));
self.writeln(&format!("static struct {{ bool valid; {} result; {} key; }} _memo_{}[{}];",
ret_type, self.generate_key_type(params)?, mangled, memo_size));
self.writeln(&format!("size_t _memo_idx = {};", hash_expr));
// Check if cached
self.writeln(&format!("if (_memo_{}[_memo_idx].valid && {}) {{",
mangled, self.generate_key_compare(params, &format!("_memo_{}[_memo_idx]", mangled))?));
self.indent += 1;
self.writeln(&format!("return _memo_{}[_memo_idx].result;", mangled));
self.indent -= 1;
self.writeln("}");
Ok(())
}
/// Emit memoization store code for pure functions
fn emit_memoization_store(&mut self, func_name: &str, params: &[Parameter], result_expr: &str) -> Result<(), CGenError> {
let mangled = self.mangle_name(func_name);
// Store result in memo table
self.writeln(&format!("_memo_{}[_memo_idx].valid = true;", mangled));
self.writeln(&format!("_memo_{}[_memo_idx].result = {};", mangled, result_expr));
for p in params {
let name = self.escape_c_keyword(&p.name.name);
self.writeln(&format!("_memo_{}[_memo_idx].key_{} = {};", mangled, name, name));
}
Ok(())
}
/// Generate the key type for memoization (stores all parameter values)
fn generate_key_type(&self, params: &[Parameter]) -> Result<String, CGenError> {
let mut fields = Vec::new();
for p in params {
let c_type = self.type_expr_to_c(&p.typ)?;
let name = self.escape_c_keyword(&p.name.name);
fields.push(format!("{} key_{}", c_type, name));
}
Ok(fields.join("; "))
}
/// Generate key comparison expression for memoization lookup
fn generate_key_compare(&self, params: &[Parameter], memo_entry: &str) -> Result<String, CGenError> {
let mut comparisons = Vec::new();
for p in params {
let c_type = self.type_expr_to_c(&p.typ)?;
let name = self.escape_c_keyword(&p.name.name);
let cmp = match c_type.as_str() {
"LuxString" => format!("lux_string_equals({}.key_{}, {})", memo_entry, name, name),
_ => format!("{}.key_{} == {}", memo_entry, name, name),
};
comparisons.push(cmp);
}
Ok(comparisons.join(" && "))
}
/// Emit idempotent function optimization
///
/// For idempotent functions (f(f(x)) = f(x)), we track if the function
/// has already been called with the same arguments to avoid redundant computation.
/// This is useful for initialization functions, setters with same value, etc.
fn emit_idempotent_check(&mut self, func_name: &str, params: &[Parameter], ret_type: &str) -> Result<(), CGenError> {
let mangled = self.mangle_name(func_name);
self.writeln(&format!("// Idempotent optimization for {}", func_name));
self.writeln(&format!("static bool _idem_{}_called = false;", mangled));
self.writeln(&format!("static {} _idem_{}_result;", ret_type, mangled));
// For idempotent functions with no params, just return the cached result
if params.is_empty() {
self.writeln(&format!("if (_idem_{}_called) {{ return _idem_{}_result; }}", mangled, mangled));
} else {
// For params, we still use memoization-like caching
self.writeln(&format!("static {} _idem_{}_key;", self.generate_key_type(params)?, mangled));
let key_compare = self.generate_key_compare_inline(params, &format!("_idem_{}_key", mangled))?;
self.writeln(&format!("if (_idem_{}_called && {}) {{ return _idem_{}_result; }}", mangled, key_compare, mangled));
}
Ok(())
}
/// Emit idempotent function store
fn emit_idempotent_store(&mut self, func_name: &str, params: &[Parameter], result_expr: &str) -> Result<(), CGenError> {
let mangled = self.mangle_name(func_name);
self.writeln(&format!("_idem_{}_called = true;", mangled));
self.writeln(&format!("_idem_{}_result = {};", mangled, result_expr));
for p in params {
let name = self.escape_c_keyword(&p.name.name);
self.writeln(&format!("_idem_{}_key_{} = {};", mangled, name, name));
}
Ok(())
}
/// Generate key comparison expression inline (for idempotent check)
fn generate_key_compare_inline(&self, params: &[Parameter], prefix: &str) -> Result<String, CGenError> {
let mut comparisons = Vec::new();
for p in params {
let c_type = self.type_expr_to_c(&p.typ)?;
let name = self.escape_c_keyword(&p.name.name);
let cmp = match c_type.as_str() {
"LuxString" => format!("lux_string_equals({}.{}, {})", prefix, name, name),
_ => format!("{}_{} == {}", prefix, name, name),
};
comparisons.push(cmp);
}
Ok(comparisons.join(" && "))
}
/// Emit deterministic function hint as a comment
///
/// Deterministic functions always produce the same output for the same inputs.
/// This hint helps C compilers (GCC/Clang) with optimization.
fn emit_deterministic_attribute(&mut self, func_name: &str) {
// GCC and Clang support __attribute__((const)) for pure functions without side effects
// that only depend on their arguments (not even global state)
self.writeln(&format!("// OPTIMIZATION: {} is deterministic - output depends only on inputs", func_name));
}
/// Emit commutative function hint
///
/// For commutative functions (f(a, b) = f(b, a)), we can normalize argument order
/// to improve common subexpression elimination (CSE).
fn emit_commutative_optimization(&mut self, func_name: &str, params: &[Parameter]) -> Result<(), CGenError> {
if params.len() != 2 {
return Ok(()); // Commutativity only makes sense for binary functions
}
let p1 = &params[0];
let p2 = &params[1];
let c_type1 = self.type_expr_to_c(&p1.typ)?;
let c_type2 = self.type_expr_to_c(&p2.typ)?;
// Only do swapping for comparable types
if c_type1 == c_type2 && matches!(c_type1.as_str(), "LuxInt" | "LuxFloat") {
let name1 = self.escape_c_keyword(&p1.name.name);
let name2 = self.escape_c_keyword(&p2.name.name);
self.writeln(&format!("// Commutative optimization for {}: normalize argument order", func_name));
self.writeln(&format!("if ({} > {}) {{", name1, name2));
self.indent += 1;
self.writeln(&format!("{} _swap_tmp = {}; {} = {}; {} = _swap_tmp;",
c_type1, name1, name1, name2, name2));
self.indent -= 1;
self.writeln("}");
}
Ok(())
}
fn writeln(&mut self, line: &str) {
let indent = " ".repeat(self.indent);
writeln!(self.output, "{}{}", indent, line).unwrap();
}
}
impl Default for CBackend {
fn default() -> Self {
Self::new()
}
}
/// Escape a string for use as a C string literal
fn escape_c_string(s: &str) -> String {
let mut result = String::with_capacity(s.len() * 2);
for c in s.chars() {
match c {
'\\' => result.push_str("\\\\"),
'"' => result.push_str("\\\""),
'\n' => result.push_str("\\n"),
'\r' => result.push_str("\\r"),
'\t' => result.push_str("\\t"),
'\0' => result.push_str("\\0"),
c if c.is_ascii_control() => {
// Escape other control characters as hex
result.push_str(&format!("\\x{:02x}", c as u8));
}
c => result.push(c),
}
}
result
}
#[cfg(test)]
mod tests {
use super::*;
use crate::parser::Parser;
fn generate(source: &str) -> Result<String, CGenError> {
let program = Parser::parse_source(source).expect("Parse error");
let mut backend = CBackend::new();
let modules = HashMap::new();
backend.generate(&program, &modules)
}
#[test]
fn test_simple_function() {
let source = r#"
fn add(a: Int, b: Int): Int = a + b
"#;
let c_code = generate(source).unwrap();
assert!(c_code.contains("LuxInt add_lux(LuxInt a, LuxInt b)"));
assert!(c_code.contains("return (a + b)"));
}
#[test]
fn test_factorial() {
let source = r#"
fn factorial(n: Int): Int =
if n <= 1 then 1 else n * factorial(n - 1)
"#;
let c_code = generate(source).unwrap();
assert!(c_code.contains("LuxInt factorial_lux(LuxInt n)"));
assert!(c_code.contains("factorial_lux((n - 1))"));
}
#[test]
fn test_console_effect() {
let source = r#"
fn greet(): Unit with {Console} = Console.print("Hello")
"#;
let c_code = generate(source).unwrap();
assert!(c_code.contains("lux_console_print"));
}
#[test]
fn test_closure_basic() {
let source = r#"
fn makeAdder(n: Int): fn(Int): Int =
fn(x: Int): Int => x + n
"#;
let c_code = generate(source).unwrap();
// Should have closure type
assert!(c_code.contains("LuxClosure"));
// Should have environment struct
assert!(c_code.contains("LuxEnv_"));
// Should have lambda function
assert!(c_code.contains("lambda_"));
// Function should return LuxClosure*
assert!(c_code.contains("LuxClosure* makeAdder_lux"));
}
#[test]
fn test_closure_call() {
let source = r#"
fn makeAdder(n: Int): fn(Int): Int =
fn(x: Int): Int => x + n
fn test(): Int = {
let add5 = makeAdder(5)
add5(10)
}
"#;
let c_code = generate(source).unwrap();
// Local should be typed as LuxClosure*
assert!(c_code.contains("LuxClosure* add5"));
// Should have indirect call syntax
assert!(c_code.contains("->fn_ptr"));
assert!(c_code.contains("->env"));
}
#[test]
fn test_pure_function_memoization() {
let source = r#"
fn fib(n: Int): Int is pure =
if n <= 1 then n else fib(n - 1) + fib(n - 2)
"#;
let c_code = generate(source).unwrap();
// Pure function should have memoization infrastructure
assert!(c_code.contains("// Memoization for pure function fib"));
assert!(c_code.contains("_memo_fib_lux"));
assert!(c_code.contains("_memo_idx"));
// Should check cache before computation
assert!(c_code.contains(".valid &&"));
// Should store result in cache
assert!(c_code.contains(".valid = true"));
assert!(c_code.contains(".result ="));
}
#[test]
fn test_non_pure_function_no_memoization() {
let source = r#"
fn add(a: Int, b: Int): Int = a + b
"#;
let c_code = generate(source).unwrap();
// Non-pure function should NOT have memoization
assert!(!c_code.contains("// Memoization for pure function add"));
assert!(!c_code.contains("_memo_add_lux"));
}
}