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>
6656 lines
316 KiB
Rust
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(¶m.name.name);
|
|
if let Ok(c_type) = self.type_expr_to_c(¶m.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(¶m.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, ¶m_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(¶m.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 = ¶ms[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 = ¶ms[0];
|
|
let p2 = ¶ms[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"));
|
|
}
|
|
}
|