diff --git a/docs/COMPILATION_STRATEGY.md b/docs/COMPILATION_STRATEGY.md
new file mode 100644
index 0000000..c3dce35
--- /dev/null
+++ b/docs/COMPILATION_STRATEGY.md
@@ -0,0 +1,345 @@
+# Lux Compilation Strategy
+
+## Vision
+
+Lux should compile to native code with zero-cost effects AND compile to JavaScript for frontend development - all while eliminating runtime overhead like Svelte does.
+
+## Current State
+
+| Component | Status |
+|-----------|--------|
+| Interpreter | Full-featured, all language constructs |
+| JIT (Cranelift) | Integer arithmetic only, ~160x speedup |
+| Targets | Native (via Cranelift JIT) |
+
+## Target Architecture
+
+```
+ ┌──→ C Backend ──→ GCC/Clang ──→ Native Binary (servers, CLI)
+ │
+Lux Source ──→ HIR ──→ MIR ──→ JS Backend ──→ Optimized JS (no runtime, like Svelte)
+ │
+ └──→ Wasm Backend ──→ WebAssembly (browsers, edge)
+```
+
+## Compilation Targets Comparison
+
+### What Other Languages Do
+
+| Language | Backend | Runtime | Effect Strategy |
+|----------|---------|---------|-----------------|
+| **Koka** | C (Perceus) | None | Evidence passing |
+| **Elm** | JS | Small runtime | No effects (pure) |
+| **Gleam** | Erlang/JS | BEAM/JS runtime | No effects |
+| **Svelte** | JS | **No runtime** | Compile-time reactivity |
+| **ReScript** | JS | Minimal | No effects |
+| **PureScript** | JS | Small runtime | Monad transformers |
+
+### Key Insight: Svelte's No-Runtime Approach
+
+Svelte compiles components to vanilla JavaScript that directly manipulates the DOM. No virtual DOM diffing, no framework code shipped to the browser.
+
+```svelte
+
+
+
+```
+
+Compiles to:
+```javascript
+// Direct DOM manipulation, no runtime
+function create_fragment(ctx) {
+ let button;
+ return {
+ c() {
+ button = element("button");
+ button.textContent = ctx[0];
+ },
+ m(target, anchor) {
+ insert(target, button, anchor);
+ listen(button, "click", ctx[1]);
+ },
+ p(ctx, dirty) {
+ if (dirty & 1) set_data(button, ctx[0]);
+ }
+ };
+}
+```
+
+**We can do the same for Lux effects on the frontend.**
+
+## Phase 1: C Backend (Native Compilation)
+
+### Goal
+Compile Lux to C code that can be compiled by GCC/Clang for native execution.
+
+### Why C?
+1. **Portability**: C compilers exist for every platform
+2. **Performance**: Leverage decades of GCC/Clang optimizations
+3. **No runtime**: Like Koka, compile effects away
+4. **Proven path**: Koka, Nim, Chicken Scheme all do this successfully
+
+### Implementation Plan
+
+#### Step 1.1: Basic C Code Generation
+- Integer arithmetic and comparisons
+- Function definitions and calls
+- If/else expressions
+- Let bindings
+
+#### Step 1.2: Data Structures
+- Records → C structs
+- ADTs → Tagged unions
+- Lists → Linked lists or arrays
+- Strings → UTF-8 byte arrays
+
+#### Step 1.3: Effect Compilation (Evidence Passing)
+Transform:
+```lux
+fn greet(name: String): Unit with {Console} =
+ Console.print("Hello, " + name)
+```
+
+To:
+```c
+void greet(Evidence* ev, LuxString name) {
+ LuxString msg = lux_string_concat("Hello, ", name);
+ ev->console->print(ev, msg);
+}
+```
+
+#### Step 1.4: Memory Management (Perceus-style)
+- Compile-time reference counting insertion
+- Reuse analysis for in-place updates
+- No garbage collector needed
+
+### File Structure
+```
+src/
+ codegen/
+ mod.rs # Code generation module
+ c_backend.rs # C code generation
+ c_runtime.h # Minimal C runtime header
+ c_runtime.c # Runtime support (RC, strings, etc.)
+```
+
+## Phase 2: JavaScript Backend (Frontend)
+
+### Goal
+Compile Lux to optimized JavaScript with NO runtime, like Svelte.
+
+### Effect Mapping
+
+| Lux Effect | JS Compilation |
+|------------|----------------|
+| `Console.print` | `console.log()` |
+| `Dom.getElementById` | `document.getElementById()` |
+| `Dom.addEventListener` | Direct event binding |
+| `Http.get` | `fetch()` with async/await |
+| `State.get/set` | Compile-time reactivity |
+
+### No-Runtime Strategy
+
+Instead of shipping a runtime, compile effects to direct DOM manipulation:
+
+```lux
+effect Dom {
+ fn getElementById(id: String): Element
+ fn setTextContent(el: Element, text: String): Unit
+ fn addEventListener(el: Element, event: String, handler: fn(): Unit): Unit
+}
+
+fn counter(): Unit with {Dom} = {
+ let btn = Dom.getElementById("btn")
+ let count = 0
+ Dom.addEventListener(btn, "click", fn() => {
+ count = count + 1
+ Dom.setTextContent(btn, toString(count))
+ })
+}
+```
+
+Compiles to:
+```javascript
+// No runtime needed - direct DOM calls
+function counter() {
+ const btn = document.getElementById("btn");
+ let count = 0;
+ btn.addEventListener("click", () => {
+ count = count + 1;
+ btn.textContent = String(count);
+ });
+}
+```
+
+### Reactive State (Like Svelte)
+
+For reactive state, compile to fine-grained updates:
+
+```lux
+effect Reactive {
+ fn signal(initial: T): Signal
+ fn derived(compute: fn(): T): Signal
+}
+
+let count = Reactive.signal(0)
+let doubled = Reactive.derived(fn() => count.get() * 2)
+```
+
+Compiles to:
+```javascript
+// Compile-time reactivity, no virtual DOM
+let count = 0;
+let doubled;
+const count_subscribers = new Set();
+
+function set_count(value) {
+ count = value;
+ doubled = count * 2; // Statically known dependency
+ count_subscribers.forEach(fn => fn());
+}
+```
+
+## Phase 3: WebAssembly Backend
+
+### Goal
+Compile to Wasm for browser and edge deployment.
+
+### Strategy
+- Reuse C backend: `C → Emscripten → Wasm`
+- Or direct Wasm generation via Cranelift
+
+## Phase 4: Zero-Cost Effects (Evidence Passing)
+
+### Current Problem
+Effects use runtime handler lookup - O(n) per effect operation.
+
+### Solution: Evidence Passing
+Transform effect operations to direct function calls at compile time.
+
+Before (runtime lookup):
+```
+performEffect("Console", "print", [msg])
+ → search handler stack
+ → invoke handler
+```
+
+After (compile-time):
+```
+evidence.console.print(msg)
+ → direct function call
+```
+
+### Implementation
+
+```rust
+// Transform AST to evidence-passing form
+fn transform_to_evidence(expr: Expr, effects: &[Effect]) -> Expr {
+ match expr {
+ Expr::EffectOp { effect, operation, args } => {
+ // Transform Console.print(x) to ev.console.print(x)
+ Expr::Call {
+ func: Expr::Field {
+ object: Expr::Field {
+ object: Expr::Var("__evidence__"),
+ field: effect.to_lowercase(),
+ },
+ field: operation,
+ },
+ args,
+ }
+ }
+ // ... recurse on other expressions
+ }
+}
+```
+
+## Phase 5: FBIP (Functional But In-Place)
+
+### Goal
+Detect when functional updates can be performed in-place.
+
+### Example
+```lux
+fn increment(tree: Tree): Tree =
+ match tree {
+ Leaf(n) => Leaf(n + 1),
+ Node(l, r) => Node(increment(l), increment(r))
+ }
+```
+
+With FBIP analysis, if `tree` has refcount 1, reuse the memory:
+```c
+Tree increment(Tree tree) {
+ if (tree->tag == LEAF) {
+ if (is_unique(tree)) {
+ tree->leaf.value += 1; // In-place!
+ return tree;
+ } else {
+ return make_leaf(tree->leaf.value + 1);
+ }
+ }
+ // ...
+}
+```
+
+## Performance Targets
+
+| Metric | Target | How |
+|--------|--------|-----|
+| Effect overhead | 0% | Evidence passing |
+| Memory allocation | Minimal | Perceus RC + FBIP |
+| JS bundle size | No runtime | Direct compilation |
+| Native vs C | <5% overhead | Good C codegen |
+
+## Milestones
+
+### v0.2.0 - C Backend (Basic)
+- [ ] Integer/bool expressions → C
+- [ ] Functions → C functions
+- [ ] If/else → C conditionals
+- [ ] Let bindings → C variables
+- [ ] Basic main() generation
+- [ ] Build with GCC/Clang
+
+### v0.3.0 - C Backend (Full)
+- [ ] Strings → C strings
+- [ ] Records → C structs
+- [ ] ADTs → Tagged unions
+- [ ] Pattern matching → Switch/if chains
+- [ ] Lists → Linked structures
+- [ ] Effect compilation (basic)
+
+### v0.4.0 - Evidence Passing
+- [ ] Effect analysis
+- [ ] Evidence vector generation
+- [ ] Transform effect ops to direct calls
+- [ ] Handler compilation
+
+### v0.5.0 - JavaScript Backend
+- [ ] Basic expressions → JS
+- [ ] Functions → JS functions
+- [ ] Effects → Direct DOM/API calls
+- [ ] No runtime bundle
+
+### v0.6.0 - Reactive Frontend
+- [ ] Reactive primitives
+- [ ] Fine-grained DOM updates
+- [ ] Compile-time dependency tracking
+- [ ] Svelte-like output
+
+### v0.7.0 - Memory Optimization
+- [ ] Reference counting insertion
+- [ ] Reuse analysis
+- [ ] FBIP detection
+- [ ] In-place updates
+
+## References
+
+- [Koka: Evidence Passing](https://github.com/koka-lang/koka)
+- [Perceus: Garbage Free Reference Counting](https://www.microsoft.com/en-us/research/publication/perceus-garbage-free-reference-counting-with-reuse/)
+- [FBIP: Functional But In-Place](https://www.microsoft.com/en-us/research/publication/fp2-fully-in-place-functional-programming/)
+- [Svelte: No Virtual DOM](https://svelte.dev/blog/virtual-dom-is-pure-overhead)
+- [Eff: Efficient Compilation of Handlers](https://dl.acm.org/doi/10.1145/3485479)
diff --git a/src/codegen/c_backend.rs b/src/codegen/c_backend.rs
new file mode 100644
index 0000000..5424d2b
--- /dev/null
+++ b/src/codegen/c_backend.rs
@@ -0,0 +1,624 @@
+//! C code generation backend for Lux
+//!
+//! Compiles Lux programs to C code that can be compiled with GCC/Clang.
+//! Inspired by Koka's approach: effects compile to evidence passing,
+//! no garbage collector needed with Perceus-style reference counting.
+
+use crate::ast::*;
+use std::collections::{HashMap, HashSet};
+use std::fmt::Write;
+
+/// C code generation errors
+#[derive(Debug, Clone)]
+pub struct CGenError {
+ pub message: String,
+ pub span: Option,
+}
+
+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 {}
+
+/// 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,
+ /// Type definitions we've emitted
+ types_emitted: HashSet,
+ /// Counter for generating unique names
+ name_counter: usize,
+ /// Effects used in the program (for evidence struct)
+ effects_used: HashSet,
+}
+
+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(),
+ }
+ }
+
+ /// Generate C code from a Lux program
+ pub fn generate(&mut self, program: &Program) -> Result {
+ self.output.clear();
+ self.emit_prelude();
+
+ // First pass: collect all function names and types
+ for decl in &program.declarations {
+ match decl {
+ Declaration::Function(f) => {
+ self.functions.insert(f.name.name.clone());
+ }
+ Declaration::Type(t) => {
+ self.collect_type(t)?;
+ }
+ _ => {}
+ }
+ }
+
+ // Emit type definitions
+ self.emit_type_definitions(program)?;
+
+ // Emit forward declarations
+ self.emit_forward_declarations(program)?;
+
+ // Emit function definitions
+ for decl in &program.declarations {
+ match decl {
+ Declaration::Function(f) => {
+ self.emit_function(f)?;
+ }
+ Declaration::Let { name, value, .. } => {
+ self.emit_global_let(name, value)?;
+ }
+ _ => {}
+ }
+ }
+
+ // Emit main wrapper if there's a main function or top-level expressions
+ self.emit_main_wrapper(program)?;
+
+ Ok(self.output.clone())
+ }
+
+ 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 ");
+ self.writeln("#include ");
+ self.writeln("#include ");
+ self.writeln("#include ");
+ self.writeln("#include ");
+ 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("// === String Operations ===");
+ 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 = malloc(len_a + len_b + 1);");
+ 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(" return strdup(buffer);");
+ 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("static LuxBool lux_string_contains(LuxString haystack, LuxString needle) {");
+ self.writeln(" return strstr(haystack, needle) != NULL;");
+ self.writeln("}");
+ self.writeln("");
+ self.writeln("// === Console Effect (built-in) ===");
+ self.writeln("");
+ self.writeln("static void lux_console_print(LuxString msg) {");
+ self.writeln(" printf(\"%s\\n\", msg);");
+ self.writeln("}");
+ self.writeln("");
+ self.writeln("// === Forward Declarations ===");
+ 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(())
+ }
+
+ 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 {
+ TypeDefinition::Record(fields) => {
+ self.writeln(&format!("typedef struct {} {{", name));
+ self.indent += 1;
+ for field in fields {
+ let c_type = self.type_to_c(&field.field_type)?;
+ self.writeln(&format!("{} {};", c_type, field.name.name));
+ }
+ self.indent -= 1;
+ self.writeln(&format!("}} {};", name));
+ self.writeln("");
+ }
+ TypeDefinition::Adt(variants) => {
+ // 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 {
+ if !variant.fields.is_empty() {
+ self.writeln(&format!("typedef struct {}_{}_Data {{", name, variant.name.name));
+ self.indent += 1;
+ for (i, field) in variant.fields.iter().enumerate() {
+ let c_type = self.type_to_c(field)?;
+ self.writeln(&format!("{} field{};", c_type, i));
+ }
+ self.indent -= 1;
+ self.writeln(&format!("}} {}_{}_Data;", name, variant.name.name));
+ self.writeln("");
+ }
+ }
+
+ // Emit main union struct
+ self.writeln(&format!("typedef struct {} {{", name));
+ self.indent += 1;
+ self.writeln(&format!("{}_Tag tag;", name));
+ self.writeln("union {");
+ self.indent += 1;
+ for variant in variants {
+ if !variant.fields.is_empty() {
+ self.writeln(&format!("{}_{}_Data {};", name, variant.name.name, variant.name.name.to_lowercase()));
+ }
+ }
+ self.indent -= 1;
+ self.writeln("} data;");
+ self.indent -= 1;
+ self.writeln(&format!("}} {};", name));
+ self.writeln("");
+ }
+ TypeDefinition::Alias(_) => {
+ // Type aliases are handled during type resolution
+ }
+ }
+ Ok(())
+ }
+
+ 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)?;
+ self.writeln(&format!("{} {}({});", ret_type, f.name.name, 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)?;
+
+ self.writeln(&format!("{} {}({}) {{", ret_type, func.name.name, params));
+ self.indent += 1;
+
+ // Emit function body
+ let result = self.emit_expr(&func.body)?;
+
+ if ret_type != "void" {
+ self.writeln(&format!("return {};", result));
+ }
+
+ self.indent -= 1;
+ self.writeln("}");
+ self.writeln("");
+ Ok(())
+ }
+
+ fn emit_params(&self, params: &[Parameter]) -> Result {
+ if params.is_empty() {
+ return Ok("void".to_string());
+ }
+
+ let param_strs: Result, _> = params.iter().map(|p| {
+ let c_type = self.type_expr_to_c(&p.param_type)?;
+ Ok(format!("{} {}", c_type, p.name.name))
+ }).collect();
+
+ Ok(param_strs?.join(", "))
+ }
+
+ fn emit_expr(&mut self, expr: &Expr) -> Result {
+ match expr {
+ Expr::Literal(lit) => self.emit_literal(lit),
+
+ Expr::Var(ident) => Ok(ident.name.clone()),
+
+ Expr::BinaryOp { op, left, right, .. } => {
+ let l = self.emit_expr(left)?;
+ let r = self.emit_expr(right)?;
+
+ 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 => "||",
+ BinaryOp::Concat => {
+ return Ok(format!("lux_string_concat({}, {})", l, r));
+ }
+ _ => return Err(CGenError {
+ message: format!("Unsupported binary operator: {:?}", op),
+ span: None,
+ }),
+ };
+
+ 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, .. } => {
+ 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)?;
+ let var_name = format!("{}_{}", name.name, self.fresh_name());
+
+ // For simple cases, we can use a compound literal or statement expression
+ // For now, emit as a block
+ self.writeln(&format!("LuxInt {} = {};", var_name, val));
+
+ // Substitute the name in the body
+ // For now, assume the variable is directly usable
+ let body_result = self.emit_expr_with_substitution(body, &name.name, &var_name)?;
+ Ok(body_result)
+ }
+
+ Expr::Call { func, args, .. } => {
+ let func_name = match func.as_ref() {
+ Expr::Var(ident) => ident.name.clone(),
+ _ => return Err(CGenError {
+ message: "Only direct function calls supported".to_string(),
+ span: None,
+ }),
+ };
+
+ let arg_strs: Result, _> = args.iter().map(|a| self.emit_expr(a)).collect();
+ let args_str = arg_strs?.join(", ");
+
+ Ok(format!("{}({})", func_name, args_str))
+ }
+
+ Expr::Block { statements, result, .. } => {
+ for stmt in statements {
+ match stmt {
+ Statement::Let { name, value, .. } => {
+ let val = self.emit_expr(value)?;
+ self.writeln(&format!("LuxInt {} = {};", name.name, val));
+ }
+ Statement::Expr(e) => {
+ let _ = self.emit_expr(e)?;
+ }
+ }
+ }
+ self.emit_expr(result)
+ }
+
+ Expr::EffectOp { effect, operation, args, .. } => {
+ self.effects_used.insert(effect.name.clone());
+
+ // Built-in effects
+ if effect.name == "Console" && operation.name == "print" {
+ let arg = self.emit_expr(&args[0])?;
+ self.writeln(&format!("lux_console_print({});", arg));
+ return Ok("NULL".to_string());
+ }
+
+ // For other effects, emit evidence-passing call
+ let arg_strs: Result, _> = args.iter().map(|a| self.emit_expr(a)).collect();
+ Ok(format!("ev_{}__{}({})",
+ effect.name.to_lowercase(),
+ operation.name,
+ arg_strs?.join(", ")))
+ }
+
+ Expr::Record { fields, .. } => {
+ let field_strs: Result, _> = 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::Match { expr, arms, .. } => {
+ self.emit_match(expr, arms)
+ }
+
+ Expr::Constructor { name, args, .. } => {
+ // ADT constructor - need to determine the type
+ // For now, assume it's a simple constructor call
+ if args.is_empty() {
+ Ok(format!("/* {} */ 0", name.name))
+ } else {
+ let arg_strs: Result, _> = args.iter().map(|a| self.emit_expr(a)).collect();
+ Ok(format!("/* {}({}) */", name.name, arg_strs?.join(", ")))
+ }
+ }
+
+ _ => Err(CGenError {
+ message: format!("Unsupported expression type in C backend"),
+ span: None,
+ }),
+ }
+ }
+
+ fn emit_expr_with_substitution(&mut self, expr: &Expr, from: &str, to: &str) -> Result {
+ // 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 {
+ let scrutinee = self.emit_expr(expr)?;
+ let result_var = format!("match_result_{}", self.fresh_name());
+
+ self.writeln(&format!("LuxInt {};", result_var));
+
+ for (i, arm) in arms.iter().enumerate() {
+ let condition = self.pattern_to_condition(&arm.pattern, &scrutinee)?;
+
+ if i == 0 {
+ self.writeln(&format!("if ({}) {{", condition));
+ } else {
+ self.writeln(&format!("}} else if ({}) {{", condition));
+ }
+
+ self.indent += 1;
+ let body = self.emit_expr(&arm.body)?;
+ self.writeln(&format!("{} = {};", result_var, body));
+ self.indent -= 1;
+ }
+
+ self.writeln("}");
+ Ok(result_var)
+ }
+
+ fn pattern_to_condition(&self, pattern: &Pattern, scrutinee: &str) -> Result {
+ match pattern {
+ Pattern::Wildcard(_) => Ok("1".to_string()),
+ Pattern::Var(ident, _) => Ok(format!("(1) /* bind {} = {} */", ident.name, scrutinee)),
+ Pattern::Literal(lit, _) => {
+ let lit_val = self.emit_literal_value(lit)?;
+ Ok(format!("{} == {}", scrutinee, lit_val))
+ }
+ Pattern::Constructor { name, .. } => {
+ Ok(format!("{}.tag == TAG_{}", scrutinee, name.name.to_uppercase()))
+ }
+ _ => Ok("1".to_string()),
+ }
+ }
+
+ fn emit_literal(&self, lit: &Literal) -> Result {
+ self.emit_literal_value(&lit.kind)
+ }
+
+ fn emit_literal_value(&self, kind: &LiteralKind) -> Result {
+ 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!("\"{}\"", s.replace("\"", "\\\""))),
+ LiteralKind::Char(c) => Ok(format!("'{}'", c)),
+ LiteralKind::Unit => Ok("NULL".to_string()),
+ }
+ }
+
+ fn emit_global_let(&mut self, name: &Ident, value: &Expr) -> Result<(), CGenError> {
+ let val = self.emit_expr(value)?;
+ self.writeln(&format!("static LuxInt {} = {};", name.name, val));
+ 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
+ let has_run = program.declarations.iter().any(|d| {
+ matches!(d, Declaration::Let { value, .. } if matches!(value.as_ref(), Expr::Run { .. }))
+ });
+
+ if has_main || has_run {
+ self.writeln("int main(int argc, char** argv) {");
+ self.indent += 1;
+
+ // Execute top-level let bindings with run expressions
+ for decl in &program.declarations {
+ if let Declaration::Let { name, value, .. } = decl {
+ if matches!(value.as_ref(), Expr::Run { .. }) {
+ if let Expr::Run { expr, .. } = value.as_ref() {
+ if let Expr::Call { func, .. } = expr.as_ref() {
+ if let Expr::Var(fn_name) = func.as_ref() {
+ self.writeln(&format!("{}();", fn_name.name));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ self.writeln("return 0;");
+ self.indent -= 1;
+ self.writeln("}");
+ }
+ Ok(())
+ }
+
+ fn type_to_c(&self, type_expr: &TypeExpr) -> Result {
+ self.type_expr_to_c(type_expr)
+ }
+
+ fn type_expr_to_c(&self, type_expr: &TypeExpr) -> Result {
+ 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::Generic { name, .. } => {
+ // For now, use void* for generic types
+ match name.name.as_str() {
+ "List" => Ok("void*".to_string()),
+ "Option" => Ok("void*".to_string()),
+ _ => Ok("void*".to_string()),
+ }
+ }
+ TypeExpr::Function { .. } => Ok("void*".to_string()),
+ TypeExpr::Tuple(_) => Ok("void*".to_string()),
+ TypeExpr::Record(_) => Ok("void*".to_string()),
+ _ => Ok("void*".to_string()),
+ }
+ }
+
+ fn fresh_name(&mut self) -> usize {
+ self.name_counter += 1;
+ self.name_counter
+ }
+
+ 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()
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::parser::Parser;
+
+ fn generate(source: &str) -> Result {
+ let program = Parser::parse_source(source).expect("Parse error");
+ let mut backend = CBackend::new();
+ backend.generate(&program)
+ }
+
+ #[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(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(LuxInt n)"));
+ assert!(c_code.contains("factorial((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"));
+ }
+}
diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs
new file mode 100644
index 0000000..5e2a278
--- /dev/null
+++ b/src/codegen/mod.rs
@@ -0,0 +1,10 @@
+//! Code generation backends for Lux
+//!
+//! This module provides compilation to various targets:
+//! - C: For native compilation via GCC/Clang
+//! - JavaScript: For frontend/browser deployment (planned)
+//! - WebAssembly: For portable deployment (planned)
+
+pub mod c_backend;
+
+pub use c_backend::CBackend;