feat: expose JIT compiler via CLI command

Add `lux compile <file>` command that compiles and runs Lux code using
the Cranelift JIT compiler. Includes --benchmark flag for timing.

- Add compile_file() function in main.rs
- Add jit_test.lux example with fib(30) + factorial(10)
- Update VISION.md status

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-13 21:16:39 -05:00
parent 26546f9a6a
commit 554a1e7c3e
3 changed files with 142 additions and 15 deletions

View File

@@ -128,11 +128,11 @@ fn retry<F>(action: F, times: Int): Result
| Feature | Effort | Why It Matters | | Feature | Effort | Why It Matters |
|---------|--------|----------------| |---------|--------|----------------|
| **Module System** | 2-3 weeks | Can't build real apps without imports | | **Module System** | Done | Imports, exports, aliases, selective imports |
| **Standard Library** | Done | List.map, String.split, Option.map, etc. | | **Standard Library** | Done | List.map, String.split, Option.map, etc. |
| **File/Network Effects** | 1-2 weeks | Real IO beyond Console | | **File/Network Effects** | 1-2 weeks | Real IO beyond Console |
| **Better Error Messages** | 2-3 weeks | Elm-quality diagnostics | | **Better Error Messages** | 2-3 weeks | Elm-quality diagnostics |
| **JS/WASM Compilation** | 4-6 weeks | Deploy to browsers/servers | | **Full Compilation** | 4-6 weeks | Cranelift JIT exists for numeric code; needs strings, ADTs, effects |
### Needed for Full Vision (Phase 3: Differentiation) ### Needed for Full Vision (Phase 3: Differentiation)
@@ -245,7 +245,7 @@ Hint: Maybe you need to parse the string?
## Immediate Next Steps ## Immediate Next Steps
1. ~~**Standard Library**~~ - Done! List, String, Option, Result operations 1. ~~**Standard Library**~~ - Done! List, String, Option, Result operations
2. **Module System** - `import`, `export`, namespaces 2. ~~**Module System**~~ - Done! Imports, exports, aliases, selective imports
3. **File Effect** - `FileSystem.read`, `FileSystem.write` 3. **File Effect** - `FileSystem.read`, `FileSystem.write`
4. **Error Message Overhaul** - Source snippets, suggestions, colors 4. **Error Message Overhaul** - Source snippets, suggestions, colors
5. **JavaScript Backend** - Compile to runnable JS 5. **JavaScript Backend** - Compile to runnable JS

16
examples/jit_test.lux Normal file
View File

@@ -0,0 +1,16 @@
// Test file for JIT compilation
// This uses only features the JIT supports: integers, arithmetic, conditionals, functions
fn fib(n: Int): Int =
if n <= 1 then n
else fib(n - 1) + fib(n - 2)
fn factorial(n: Int): Int =
if n <= 1 then 1
else n * factorial(n - 1)
fn main(): Int = {
let a = fib(30)
let b = factorial(10)
a + b
}

View File

@@ -139,6 +139,15 @@ fn main() {
// Package manager // Package manager
handle_pkg_command(&args[2..]); handle_pkg_command(&args[2..]);
} }
"compile" => {
// Compile and run with JIT
if args.len() < 3 {
eprintln!("Usage: lux compile <file.lux> [--benchmark]");
std::process::exit(1);
}
let benchmark = args.iter().any(|a| a == "--benchmark");
compile_file(&args[2], benchmark);
}
path => { path => {
// Run a file // Run a file
run_file(path); run_file(path);
@@ -154,18 +163,19 @@ fn print_help() {
println!("Lux {} - A functional language with first-class effects", VERSION); println!("Lux {} - A functional language with first-class effects", VERSION);
println!(); println!();
println!("Usage:"); println!("Usage:");
println!(" lux Start the REPL"); println!(" lux Start the REPL");
println!(" lux <file.lux> Run a file"); println!(" lux <file.lux> Run a file (interpreter)");
println!(" lux fmt <file.lux> Format a file (--check to verify only)"); println!(" lux compile <file.lux> Compile and run with JIT (--benchmark for timing)");
println!(" lux check <file.lux> Type check without running"); println!(" lux fmt <file.lux> Format a file (--check to verify only)");
println!(" lux test [pattern] Run tests (optional pattern filter)"); println!(" lux check <file.lux> Type check without running");
println!(" lux watch <file.lux> Watch and re-run on changes"); println!(" lux test [pattern] Run tests (optional pattern filter)");
println!(" lux debug <file.lux> Start interactive debugger"); println!(" lux watch <file.lux> Watch and re-run on changes");
println!(" lux init [name] Initialize a new project"); println!(" lux debug <file.lux> Start interactive debugger");
println!(" lux pkg <command> Package manager (install, add, remove, list, update)"); println!(" lux init [name] Initialize a new project");
println!(" lux --lsp Start LSP server (for IDE integration)"); println!(" lux pkg <command> Package manager (install, add, remove, list, update)");
println!(" lux --help Show this help"); println!(" lux --lsp Start LSP server (for IDE integration)");
println!(" lux --version Show version"); println!(" lux --help Show this help");
println!(" lux --version Show version");
} }
fn format_file(path: &str, check_only: bool) { fn format_file(path: &str, check_only: bool) {
@@ -246,6 +256,107 @@ fn check_file(path: &str) {
println!("{}: OK", path); println!("{}: OK", path);
} }
fn compile_file(path: &str, benchmark: bool) {
use compiler::JitCompiler;
use modules::ModuleLoader;
use std::path::Path;
use std::time::Instant;
let file_path = Path::new(path);
let source = match std::fs::read_to_string(file_path) {
Ok(s) => s,
Err(e) => {
eprintln!("Error reading file '{}': {}", path, e);
std::process::exit(1);
}
};
// Parse
let parse_start = Instant::now();
let mut loader = ModuleLoader::new();
if let Some(parent) = file_path.parent() {
loader.add_search_path(parent.to_path_buf());
}
let program = match loader.load_source(&source, Some(file_path)) {
Ok(p) => p,
Err(e) => {
eprintln!("Module error: {}", e);
std::process::exit(1);
}
};
let parse_time = parse_start.elapsed();
// Type check
let check_start = Instant::now();
let mut checker = TypeChecker::new();
if let Err(errors) = checker.check_program_with_modules(&program, &loader) {
for error in errors {
let diagnostic = error.to_diagnostic();
eprint!("{}", render(&diagnostic, &source, Some(path)));
}
std::process::exit(1);
}
let check_time = check_start.elapsed();
// Compile with JIT
let compile_start = Instant::now();
let mut jit = match JitCompiler::new() {
Ok(j) => j,
Err(e) => {
eprintln!("JIT initialization error: {}", e);
std::process::exit(1);
}
};
if let Err(e) = jit.compile_program(&program) {
eprintln!("Compilation error: {}", e);
eprintln!();
eprintln!("Note: The JIT compiler currently only supports:");
eprintln!(" - Integer arithmetic and comparisons");
eprintln!(" - Conditionals (if/then/else)");
eprintln!(" - Let bindings and blocks");
eprintln!(" - Function calls (including recursion)");
eprintln!();
eprintln!("Not yet supported: strings, floats, lists, records,");
eprintln!("pattern matching, effects, ADTs.");
std::process::exit(1);
}
let compile_time = compile_start.elapsed();
// Find main function or last expression
let exec_start = Instant::now();
let result = if jit.get_function("main").is_some() {
unsafe { jit.call_function("main", &[]) }
} else {
// Try to find any function to call
eprintln!("No 'main' function found.");
eprintln!("Define a function like: fn main(): Int = ...");
std::process::exit(1);
};
let exec_time = exec_start.elapsed();
match result {
Ok(value) => {
println!("{}", value);
if benchmark {
println!();
println!("=== JIT Benchmark ===");
println!("Parse time: {:?}", parse_time);
println!("Check time: {:?}", check_time);
println!("Compile time: {:?}", compile_time);
println!("Execute time: {:?}", exec_time);
println!("Total time: {:?}", parse_time + check_time + compile_time + exec_time);
}
}
Err(e) => {
eprintln!("Execution error: {}", e);
std::process::exit(1);
}
}
}
fn run_tests(args: &[String]) { fn run_tests(args: &[String]) {
use std::path::Path; use std::path::Path;
use std::fs; use std::fs;