From 67437b827367e4aab19681300fbd8b1a89b67b25 Mon Sep 17 00:00:00 2001 From: Brandon Lucas Date: Sat, 14 Feb 2026 03:37:35 -0500 Subject: [PATCH] feat: replace Cranelift JIT with C backend Remove Cranelift JIT compiler and expose the existing C backend as the compilation target. Generated C code can be compiled with GCC/Clang. Changes: - Remove cranelift-* dependencies from Cargo.toml - Delete src/compiler.rs (565 lines of Cranelift code) - Add compile_to_c() function with -o and --run flags - Fix C backend name mangling (main -> main_lux) to avoid conflicts - Update CLI help text and documentation Usage: lux compile # Output C to stdout lux compile -o out.c # Write to file lux compile --run # Compile and execute C backend supports: functions, basic types, operators, if/then/else, records, enums, Console.print. Future work: closures, lists, patterns. Co-Authored-By: Claude Opus 4.5 --- Cargo.lock | 350 +----------------------- Cargo.toml | 6 - SKILLS.md | 9 +- docs/OVERVIEW.md | 19 +- docs/ROADMAP.md | 9 +- src/codegen/c_backend.rs | 34 ++- src/compiler.rs | 565 --------------------------------------- src/main.rs | 139 ++++++---- 8 files changed, 132 insertions(+), 999 deletions(-) delete mode 100644 src/compiler.rs diff --git a/Cargo.lock b/Cargo.lock index 8d888ce..5cf0cdd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,18 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "ahash" -version = "0.8.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy", -] - [[package]] name = "anyhow" version = "1.0.101" @@ -26,12 +14,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" -[[package]] -name = "autocfg" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" - [[package]] name = "base64" version = "0.21.7" @@ -56,12 +38,6 @@ version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - [[package]] name = "bytes" version = "1.11.1" @@ -131,114 +107,6 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" -[[package]] -name = "cranelift-bforest" -version = "0.95.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1277fbfa94bc82c8ec4af2ded3e639d49ca5f7f3c7eeab2c66accd135ece4e70" -dependencies = [ - "cranelift-entity", -] - -[[package]] -name = "cranelift-codegen" -version = "0.95.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6e8c31ad3b2270e9aeec38723888fe1b0ace3bea2b06b3f749ccf46661d3220" -dependencies = [ - "bumpalo", - "cranelift-bforest", - "cranelift-codegen-meta", - "cranelift-codegen-shared", - "cranelift-entity", - "cranelift-isle", - "gimli", - "hashbrown 0.13.2", - "log", - "regalloc2", - "smallvec", - "target-lexicon", -] - -[[package]] -name = "cranelift-codegen-meta" -version = "0.95.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8ac5ac30d62b2d66f12651f6b606dbdfd9c2cfd0908de6b387560a277c5c9da" -dependencies = [ - "cranelift-codegen-shared", -] - -[[package]] -name = "cranelift-codegen-shared" -version = "0.95.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd82b8b376247834b59ed9bdc0ddeb50f517452827d4a11bccf5937b213748b8" - -[[package]] -name = "cranelift-entity" -version = "0.95.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40099d38061b37e505e63f89bab52199037a72b931ad4868d9089ff7268660b0" - -[[package]] -name = "cranelift-frontend" -version = "0.95.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a25d9d0a0ae3079c463c34115ec59507b4707175454f0eee0891e83e30e82d" -dependencies = [ - "cranelift-codegen", - "log", - "smallvec", - "target-lexicon", -] - -[[package]] -name = "cranelift-isle" -version = "0.95.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80de6a7d0486e4acbd5f9f87ec49912bf4c8fb6aea00087b989685460d4469ba" - -[[package]] -name = "cranelift-jit" -version = "0.95.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ca96b05988aa057eda09a817a6e31915fabd7f476b513123aff08053cd193dd" -dependencies = [ - "anyhow", - "cranelift-codegen", - "cranelift-entity", - "cranelift-module", - "cranelift-native", - "libc", - "log", - "region", - "target-lexicon", - "wasmtime-jit-icache-coherence", - "windows-sys 0.45.0", -] - -[[package]] -name = "cranelift-module" -version = "0.95.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5112c0be9cc5da064e0620570d67852f11ce44f2e572a58ecf7f11df73978b8" -dependencies = [ - "anyhow", - "cranelift-codegen", -] - -[[package]] -name = "cranelift-native" -version = "0.95.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb6b03e0e03801c4b3fd8ce0758a94750c07a44e7944cc0ffbf0d3f2e7c79b00" -dependencies = [ - "cranelift-codegen", - "libc", - "target-lexicon", -] - [[package]] name = "crossbeam-channel" version = "0.5.15" @@ -302,12 +170,6 @@ version = "3.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" -[[package]] -name = "fallible-iterator" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" - [[package]] name = "fastrand" version = "2.3.0" @@ -415,15 +277,6 @@ dependencies = [ "slab", ] -[[package]] -name = "fxhash" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" -dependencies = [ - "byteorder", -] - [[package]] name = "getrandom" version = "0.2.17" @@ -448,17 +301,6 @@ dependencies = [ "wasip3", ] -[[package]] -name = "gimli" -version = "0.27.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" -dependencies = [ - "fallible-iterator", - "indexmap 1.9.3", - "stable_deref_trait", -] - [[package]] name = "h2" version = "0.3.27" @@ -471,28 +313,13 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 2.13.0", + "indexmap", "slab", "tokio", "tokio-util", "tracing", ] -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hashbrown" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" -dependencies = [ - "ahash", -] - [[package]] name = "hashbrown" version = "0.15.5" @@ -702,16 +529,6 @@ dependencies = [ "icu_properties", ] -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", -] - [[package]] name = "indexmap" version = "2.13.0" @@ -806,10 +623,6 @@ dependencies = [ name = "lux" version = "0.1.0" dependencies = [ - "cranelift-codegen", - "cranelift-frontend", - "cranelift-jit", - "cranelift-module", "lsp-server", "lsp-types", "rand", @@ -817,21 +630,11 @@ dependencies = [ "rustyline", "serde", "serde_json", - "target-lexicon", "tempfile", "thiserror", "tiny_http", ] -[[package]] -name = "mach" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" -dependencies = [ - "libc", -] - [[package]] name = "memchr" version = "2.8.0" @@ -1059,30 +862,6 @@ dependencies = [ "getrandom 0.2.17", ] -[[package]] -name = "regalloc2" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80535183cae11b149d618fbd3c37e38d7cda589d82d7769e196ca9a9042d7621" -dependencies = [ - "fxhash", - "log", - "slice-group-by", - "smallvec", -] - -[[package]] -name = "region" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877e54ea2adcd70d80e9179344c97f93ef0dffd6b03e1f4529e6e83ab2fa9ae0" -dependencies = [ - "bitflags 1.3.2", - "libc", - "mach", - "winapi", -] - [[package]] name = "reqwest" version = "0.11.27" @@ -1295,12 +1074,6 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" -[[package]] -name = "slice-group-by" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" - [[package]] name = "smallvec" version = "1.15.1" @@ -1382,12 +1155,6 @@ dependencies = [ "libc", ] -[[package]] -name = "target-lexicon" -version = "0.12.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" - [[package]] name = "tempfile" version = "3.25.0" @@ -1566,12 +1333,6 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - [[package]] name = "want" version = "0.3.1" @@ -1681,7 +1442,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" dependencies = [ "anyhow", - "indexmap 2.13.0", + "indexmap", "wasm-encoder", "wasmparser", ] @@ -1694,21 +1455,10 @@ checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ "bitflags 2.10.0", "hashbrown 0.15.5", - "indexmap 2.13.0", + "indexmap", "semver", ] -[[package]] -name = "wasmtime-jit-icache-coherence" -version = "8.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aecae978b13f7f67efb23bd827373ace4578f2137ec110bbf6a4a7cde4121bbd" -dependencies = [ - "cfg-if", - "libc", - "windows-sys 0.45.0", -] - [[package]] name = "web-sys" version = "0.3.85" @@ -1719,43 +1469,12 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - [[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - [[package]] name = "windows-sys" version = "0.48.0" @@ -1801,21 +1520,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - [[package]] name = "windows-targets" version = "0.48.5" @@ -1864,12 +1568,6 @@ dependencies = [ "windows_x86_64_msvc 0.53.1", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -1888,12 +1586,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -1912,12 +1604,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -1948,12 +1634,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -1972,12 +1652,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -1996,12 +1670,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -2020,12 +1688,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -2082,7 +1744,7 @@ checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" dependencies = [ "anyhow", "heck", - "indexmap 2.13.0", + "indexmap", "prettyplease", "syn", "wasm-metadata", @@ -2113,7 +1775,7 @@ checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", "bitflags 2.10.0", - "indexmap 2.13.0", + "indexmap", "log", "serde", "serde_derive", @@ -2132,7 +1794,7 @@ checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" dependencies = [ "anyhow", "id-arena", - "indexmap 2.13.0", + "indexmap", "log", "semver", "serde", diff --git a/Cargo.toml b/Cargo.toml index 90f35fe..c6c2224 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,12 +16,6 @@ rand = "0.8" reqwest = { version = "0.11", features = ["blocking", "json"] } tiny_http = "0.12" -# Cranelift for native compilation -cranelift-codegen = "0.95" -cranelift-frontend = "0.95" -cranelift-module = "0.95" -cranelift-jit = "0.95" -target-lexicon = "0.12" [dev-dependencies] tempfile = "3" diff --git a/SKILLS.md b/SKILLS.md index 323bcfe..4408add 100644 --- a/SKILLS.md +++ b/SKILLS.md @@ -614,16 +614,15 @@ fn main() = **Goal**: Compile to a real target -- [x] JIT compiler (Cranelift) - works for numeric code (~160x speedup) -- [x] C backend scaffolding -- [ ] Full JIT (strings, lists, ADTs, effects) +- [x] C backend (basic functions, Console.print) +- [ ] C backend extensions (closures, lists, pattern variables) - [ ] JavaScript backend - [ ] WASM backend - [ ] Optimization passes **Deliverable**: Compiled programs that run natively or in browser -**Current**: `lux compile ` JIT compiles numeric code. C backend in progress. +**Current**: `lux compile ` compiles to C code. Compile and run with `--run` flag. ### Phase 3: Schema Evolution ⚠️ PARTIAL @@ -719,7 +718,7 @@ fn abs(x: Int): Int ### Pragmatics (In Progress) -- Primary target: Tree-walking interpreter (default), JIT for performance +- Primary target: Tree-walking interpreter (default), C backend for compilation - FFI: Shell commands via Process effect - [ ] JavaScript backend for browser use diff --git a/docs/OVERVIEW.md b/docs/OVERVIEW.md index 80b3714..f6db315 100644 --- a/docs/OVERVIEW.md +++ b/docs/OVERVIEW.md @@ -181,7 +181,7 @@ fn processAny(x: Int @latest): Int = x // any version ### Planned (Not Yet Fully Implemented) -- **Full Compilation**: JIT works for numeric code, strings/lists/ADTs missing +- **Full C Backend**: Basic functions work, closures/lists/pattern variables pending - **Auto-migration Generation**: Migration bodies stored, execution pending --- @@ -234,7 +234,7 @@ Quick iteration with type inference and a REPL. | Limitation | Description | |------------|-------------| -| **Limited JIT** | Cranelift JIT works for numeric code only | +| **Limited C Backend** | Basic functions work, closures/lists pending | | **No Package Manager** | Can't share/publish packages yet | | **New Paradigm** | Effects require learning new concepts | | **Small Ecosystem** | No community packages yet | @@ -297,7 +297,7 @@ Quick iteration with type inference and a REPL. ### Not a Good Fit (Yet) - Large production applications (early stage) -- Performance-critical code (JIT limited to numeric) +- Performance-critical code (C backend still basic) - Web frontend development (no JS compilation) - Systems programming (no low-level control) @@ -348,13 +348,12 @@ Source Code ├─────────────────────────┐ ▼ ▼ ┌─────────────┐ ┌──────────────┐ -│ Interpreter │ │ JIT Compiler │ -│ (default) │ │ (Cranelift) │ +│ Interpreter │ │ C Backend │ +│ (default) │ │ (compile) │ └─────────────┘ └──────────────┘ │ │ ▼ ▼ -Values + Effects Native Code - (~160x speedup) +Values + Effects C Code → GCC/Clang ``` --- @@ -370,12 +369,12 @@ Values + Effects Native Code - ✅ HTTP Server Effect (listen, accept, respond, stop) - ✅ Test Effect (native testing framework) - ✅ Console.readLine and Console.readInt -- ✅ JIT Compiler for numeric code (~160x speedup) +- ✅ C Backend (basic functions, Console.print) - ✅ Watch mode / hot reload - ✅ Formatter **In Progress:** -1. **Behavioral Type Verification** - Total, idempotent, deterministic checking +1. **C Backend Extensions** - Closures, lists, pattern variable binding 2. **Schema Evolution** - Type system integration, auto-migration 3. **Error Message Quality** - Context lines shown, suggestions partial @@ -383,4 +382,4 @@ Values + Effects Native Code 4. **SQL Effect** - Database access 5. **Package Manager** - Share code (manifest parsing exists) 6. **JavaScript Backend** - Run in browsers -7. **Full JIT** - Strings, lists, ADTs, effects +7. **Behavioral Type Verification** - Total, idempotent, deterministic checking diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 19574d4..e599e18 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -67,10 +67,10 @@ | Task | Priority | Effort | Status | |------|----------|--------|--------| | Elm-quality error messages | P1 | 2 weeks | ⚠️ Partial (context shown, suggestions missing) | -| Full JS compilation | P2 | 4 weeks | ⚠️ JIT for numeric code only | +| Full JS compilation | P2 | 4 weeks | ❌ Missing | | Hot reload / watch mode | P2 | — | ✅ Complete | | Debugger improvements | P3 | 2 weeks | ✅ Basic | -| JIT CLI integration | P1 | — | ✅ Complete (`lux compile`) | +| C backend CLI integration | P1 | — | ✅ Complete (`lux compile`) | ### Success Criteria for Use Case 1 - [ ] Can build a REST API with database access @@ -220,7 +220,8 @@ | Task | Priority | Effort | Status | |------|----------|--------|--------| -| Extend JIT (strings, lists) | P1 | 2 weeks | ❌ Missing | +| C backend (basic) | P1 | — | ✅ Complete (functions, Console.print) | +| Extend C backend (closures, lists) | P1 | 2 weeks | ❌ Missing | | JS backend | P2 | 4 weeks | ❌ Missing | | WASM backend | P3 | 4 weeks | ❌ Missing | @@ -296,7 +297,7 @@ - ✅ JSON parsing/serialization (parse, stringify, get, object, array) **Tooling:** -- ✅ JIT compiler (~160x speedup, CLI: `lux compile`) +- ✅ C backend (functions, Console.print, CLI: `lux compile`) - ✅ REPL with history - ✅ Basic LSP server - ✅ Formatter diff --git a/src/codegen/c_backend.rs b/src/codegen/c_backend.rs index 6336714..eaae92b 100644 --- a/src/codegen/c_backend.rs +++ b/src/codegen/c_backend.rs @@ -82,7 +82,10 @@ impl CBackend { self.emit_function(f)?; } Declaration::Let(let_decl) => { - self.emit_global_let(&let_decl.name, &let_decl.value)?; + // Skip run expressions - they're handled in the main wrapper + if !matches!(&let_decl.value, Expr::Run { .. }) { + self.emit_global_let(&let_decl.name, &let_decl.value)?; + } } _ => {} } @@ -245,12 +248,18 @@ impl CBackend { Ok(()) } + fn mangle_name(&self, name: &str) -> String { + // Add suffix to avoid conflicts with C keywords and standard library + format!("{}_lux", name) + } + 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)); + let mangled = self.mangle_name(&f.name.name); + self.writeln(&format!("{} {}({});", ret_type, mangled, params)); } } self.writeln(""); @@ -260,8 +269,9 @@ impl CBackend { 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); - self.writeln(&format!("{} {}({}) {{", ret_type, func.name.name, params)); + self.writeln(&format!("{} {}({}) {{", ret_type, mangled, params)); self.indent += 1; // Emit function body @@ -365,7 +375,14 @@ impl CBackend { 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)) + // Mangle user-defined function names + let c_func_name = if self.functions.contains(&func_name) { + self.mangle_name(&func_name) + } else { + func_name + }; + + Ok(format!("{}({})", c_func_name, args_str)) } Expr::Block { statements, result, .. } => { @@ -517,7 +534,8 @@ impl CBackend { 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() { - self.writeln(&format!("{}();", fn_name.name)); + let mangled = self.mangle_name(&fn_name.name); + self.writeln(&format!("{}();", mangled)); } } } @@ -602,7 +620,7 @@ mod tests { 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("LuxInt add_lux(LuxInt a, LuxInt b)")); assert!(c_code.contains("return (a + b)")); } @@ -613,8 +631,8 @@ mod tests { 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))")); + assert!(c_code.contains("LuxInt factorial_lux(LuxInt n)")); + assert!(c_code.contains("factorial_lux((n - 1))")); } #[test] diff --git a/src/compiler.rs b/src/compiler.rs deleted file mode 100644 index d934b3e..0000000 --- a/src/compiler.rs +++ /dev/null @@ -1,565 +0,0 @@ -//! Cranelift-based native compiler for Lux -//! -//! This module compiles Lux programs to native machine code using Cranelift. -//! Currently supports a subset of the language for performance-critical code. - -#![allow(dead_code)] - -use crate::ast::{Expr, Program, Declaration, FunctionDecl, BinaryOp, UnaryOp, LiteralKind, Statement}; -use cranelift_codegen::ir::{AbiParam, InstBuilder, Value, types}; -use cranelift_codegen::ir::condcodes::IntCC; -use cranelift_codegen::Context; -use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext, Variable}; -use cranelift_jit::{JITBuilder, JITModule}; -use cranelift_module::{Module, Linkage, FuncId}; -use std::collections::HashMap; - -/// Errors that can occur during compilation -#[derive(Debug)] -pub struct CompileError { - pub message: String, -} - -impl std::fmt::Display for CompileError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Compile error: {}", self.message) - } -} - -impl std::error::Error for CompileError {} - -/// The JIT compiler for Lux -pub struct JitCompiler { - /// The Cranelift JIT module - module: JITModule, - /// Builder context (reusable) - builder_context: FunctionBuilderContext, - /// Cranelift context (reusable) - ctx: Context, - /// Compiled function pointers - functions: HashMap, - /// Function IDs for linking - func_ids: HashMap, -} - -impl JitCompiler { - /// Create a new JIT compiler - pub fn new() -> Result { - let builder = JITBuilder::new(cranelift_module::default_libcall_names()) - .map_err(|e| CompileError { message: e.to_string() })?; - - let module = JITModule::new(builder); - - Ok(Self { - module, - builder_context: FunctionBuilderContext::new(), - ctx: Context::new(), - functions: HashMap::new(), - func_ids: HashMap::new(), - }) - } - - /// Compile a Lux function to native code - pub fn compile_function(&mut self, func: &FunctionDecl) -> Result<*const u8, CompileError> { - // Check if already compiled - if let Some(ptr) = self.functions.get(&func.name.name) { - return Ok(*ptr); - } - - // Create function signature - let mut sig = self.module.make_signature(); - for _ in &func.params { - sig.params.push(AbiParam::new(types::I64)); - } - sig.returns.push(AbiParam::new(types::I64)); - - // Declare the function - let func_id = self.module - .declare_function(&func.name.name, Linkage::Local, &sig) - .map_err(|e| CompileError { message: e.to_string() })?; - - self.func_ids.insert(func.name.name.clone(), func_id); - - // Clear context for reuse - self.ctx.clear(); - self.ctx.func.signature = sig; - - // Clone func_ids for use in closure - let func_ids = self.func_ids.clone(); - - // Build the function body - { - let mut builder = FunctionBuilder::new(&mut self.ctx.func, &mut self.builder_context); - - // Create entry block - let entry_block = builder.create_block(); - builder.append_block_params_for_function_params(entry_block); - builder.switch_to_block(entry_block); - builder.seal_block(entry_block); - - // Map parameter names to values - let mut variables: HashMap = HashMap::new(); - let params = builder.block_params(entry_block).to_vec(); - - for (i, param) in func.params.iter().enumerate() { - let var = Variable::from_u32(i as u32); - builder.declare_var(var, types::I64); - builder.def_var(var, params[i]); - variables.insert(param.name.name.clone(), var); - } - - // Compile the function body - let var_count = variables.len(); - let result = compile_expr(&mut builder, &func.body, &mut variables, var_count, &func_ids, &mut self.module)?; - - // Return the result - builder.ins().return_(&[result]); - - builder.finalize(); - } - - // Compile to machine code - self.module - .define_function(func_id, &mut self.ctx) - .map_err(|e| CompileError { message: e.to_string() })?; - - self.module.clear_context(&mut self.ctx); - - // Finalize and get the function pointer - self.module.finalize_definitions() - .map_err(|e| CompileError { message: e.to_string() })?; - - let ptr = self.module.get_finalized_function(func_id); - self.functions.insert(func.name.name.clone(), ptr); - - Ok(ptr) - } - - /// Compile and run a simple function that takes no args and returns an i64 - pub fn compile_and_run(&mut self, func: &FunctionDecl) -> Result { - let ptr = self.compile_function(func)?; - - // Cast to function pointer and call - let func_ptr: fn() -> i64 = unsafe { std::mem::transmute(ptr) }; - Ok(func_ptr()) - } - - /// Compile a program and return pointers to all compiled functions - pub fn compile_program(&mut self, program: &Program) -> Result<(), CompileError> { - // First pass: declare all functions - for decl in &program.declarations { - if let Declaration::Function(func) = decl { - let mut sig = self.module.make_signature(); - for _ in &func.params { - sig.params.push(AbiParam::new(types::I64)); - } - sig.returns.push(AbiParam::new(types::I64)); - - let func_id = self.module - .declare_function(&func.name.name, Linkage::Local, &sig) - .map_err(|e| CompileError { message: e.to_string() })?; - - self.func_ids.insert(func.name.name.clone(), func_id); - } - } - - // Second pass: compile all functions - for decl in &program.declarations { - if let Declaration::Function(func) = decl { - self.compile_function_body(func)?; - } - } - - // Finalize - self.module.finalize_definitions() - .map_err(|e| CompileError { message: e.to_string() })?; - - // Store function pointers - for (name, func_id) in &self.func_ids { - let ptr = self.module.get_finalized_function(*func_id); - self.functions.insert(name.clone(), ptr); - } - - Ok(()) - } - - /// Compile a function body (assumes function is already declared) - fn compile_function_body(&mut self, func: &FunctionDecl) -> Result<(), CompileError> { - let func_id = *self.func_ids.get(&func.name.name).ok_or_else(|| CompileError { - message: format!("Function not declared: {}", func.name.name), - })?; - - // Create signature - let mut sig = self.module.make_signature(); - for _ in &func.params { - sig.params.push(AbiParam::new(types::I64)); - } - sig.returns.push(AbiParam::new(types::I64)); - - // Clear and set up context - self.ctx.clear(); - self.ctx.func.signature = sig; - - // Clone func_ids for use in closure - let func_ids = self.func_ids.clone(); - - // Build function - { - let mut builder = FunctionBuilder::new(&mut self.ctx.func, &mut self.builder_context); - - let entry_block = builder.create_block(); - builder.append_block_params_for_function_params(entry_block); - builder.switch_to_block(entry_block); - builder.seal_block(entry_block); - - let mut variables: HashMap = HashMap::new(); - let params = builder.block_params(entry_block).to_vec(); - - for (i, param) in func.params.iter().enumerate() { - let var = Variable::from_u32(i as u32); - builder.declare_var(var, types::I64); - builder.def_var(var, params[i]); - variables.insert(param.name.name.clone(), var); - } - - let var_count = variables.len(); - let result = compile_expr(&mut builder, &func.body, &mut variables, var_count, &func_ids, &mut self.module)?; - builder.ins().return_(&[result]); - - builder.finalize(); - } - - // Define the function - self.module - .define_function(func_id, &mut self.ctx) - .map_err(|e| CompileError { message: e.to_string() })?; - - self.module.clear_context(&mut self.ctx); - - Ok(()) - } - - /// Get a compiled function pointer by name - pub fn get_function(&self, name: &str) -> Option<*const u8> { - self.functions.get(name).copied() - } - - /// Call a compiled function with given i64 arguments - pub unsafe fn call_function(&self, name: &str, args: &[i64]) -> Result { - let ptr = self.get_function(name).ok_or_else(|| CompileError { - message: format!("Function not found: {}", name), - })?; - - match args.len() { - 0 => { - let f: fn() -> i64 = std::mem::transmute(ptr); - Ok(f()) - } - 1 => { - let f: fn(i64) -> i64 = std::mem::transmute(ptr); - Ok(f(args[0])) - } - 2 => { - let f: fn(i64, i64) -> i64 = std::mem::transmute(ptr); - Ok(f(args[0], args[1])) - } - 3 => { - let f: fn(i64, i64, i64) -> i64 = std::mem::transmute(ptr); - Ok(f(args[0], args[1], args[2])) - } - _ => Err(CompileError { - message: format!("Too many arguments: {}", args.len()), - }), - } - } -} - -impl Default for JitCompiler { - fn default() -> Self { - Self::new().expect("Failed to create JIT compiler") - } -} - -/// Compile an expression to Cranelift IR (free function to avoid borrow issues) -fn compile_expr( - builder: &mut FunctionBuilder, - expr: &Expr, - variables: &mut HashMap, - next_var: usize, - func_ids: &HashMap, - module: &mut JITModule, -) -> Result { - match expr { - Expr::Literal(lit) => { - match &lit.kind { - LiteralKind::Int(n) => { - Ok(builder.ins().iconst(types::I64, *n)) - } - LiteralKind::Bool(b) => { - Ok(builder.ins().iconst(types::I64, if *b { 1 } else { 0 })) - } - _ => Err(CompileError { - message: "Unsupported literal type".to_string() - }), - } - } - - Expr::Var(ident) => { - let var = variables.get(&ident.name).ok_or_else(|| CompileError { - message: format!("Undefined variable: {}", ident.name), - })?; - Ok(builder.use_var(*var)) - } - - Expr::BinaryOp { op, left, right, .. } => { - let lhs = compile_expr(builder, left, variables, next_var, func_ids, module)?; - let rhs = compile_expr(builder, right, variables, next_var, func_ids, module)?; - - let result = match op { - BinaryOp::Add => builder.ins().iadd(lhs, rhs), - BinaryOp::Sub => builder.ins().isub(lhs, rhs), - BinaryOp::Mul => builder.ins().imul(lhs, rhs), - BinaryOp::Div => builder.ins().sdiv(lhs, rhs), - BinaryOp::Mod => builder.ins().srem(lhs, rhs), - BinaryOp::Eq => { - let cmp = builder.ins().icmp(IntCC::Equal, lhs, rhs); - builder.ins().uextend(types::I64, cmp) - } - BinaryOp::Ne => { - let cmp = builder.ins().icmp(IntCC::NotEqual, lhs, rhs); - builder.ins().uextend(types::I64, cmp) - } - BinaryOp::Lt => { - let cmp = builder.ins().icmp(IntCC::SignedLessThan, lhs, rhs); - builder.ins().uextend(types::I64, cmp) - } - BinaryOp::Le => { - let cmp = builder.ins().icmp(IntCC::SignedLessThanOrEqual, lhs, rhs); - builder.ins().uextend(types::I64, cmp) - } - BinaryOp::Gt => { - let cmp = builder.ins().icmp(IntCC::SignedGreaterThan, lhs, rhs); - builder.ins().uextend(types::I64, cmp) - } - BinaryOp::Ge => { - let cmp = builder.ins().icmp(IntCC::SignedGreaterThanOrEqual, lhs, rhs); - builder.ins().uextend(types::I64, cmp) - } - BinaryOp::And => builder.ins().band(lhs, rhs), - BinaryOp::Or => builder.ins().bor(lhs, rhs), - _ => return Err(CompileError { - message: format!("Unsupported binary operator: {:?}", op), - }), - }; - Ok(result) - } - - Expr::UnaryOp { op, operand, .. } => { - let val = compile_expr(builder, operand, variables, next_var, func_ids, module)?; - let result = match op { - UnaryOp::Neg => builder.ins().ineg(val), - UnaryOp::Not => { - let one = builder.ins().iconst(types::I64, 1); - builder.ins().bxor(val, one) - } - }; - Ok(result) - } - - Expr::If { condition, then_branch, else_branch, .. } => { - let cond_val = compile_expr(builder, condition, variables, next_var, func_ids, module)?; - - // Create blocks - let then_block = builder.create_block(); - let else_block = builder.create_block(); - let merge_block = builder.create_block(); - - // Add block parameter for the result - builder.append_block_param(merge_block, types::I64); - - // Branch based on condition - let zero = builder.ins().iconst(types::I64, 0); - let cmp = builder.ins().icmp(IntCC::NotEqual, cond_val, zero); - builder.ins().brif(cmp, then_block, &[], else_block, &[]); - - // Then block - builder.switch_to_block(then_block); - builder.seal_block(then_block); - let then_val = compile_expr(builder, then_branch, variables, next_var, func_ids, module)?; - builder.ins().jump(merge_block, &[then_val]); - - // Else block - builder.switch_to_block(else_block); - builder.seal_block(else_block); - let else_val = compile_expr(builder, else_branch, variables, next_var, func_ids, module)?; - builder.ins().jump(merge_block, &[else_val]); - - // Merge block - builder.switch_to_block(merge_block); - builder.seal_block(merge_block); - - Ok(builder.block_params(merge_block)[0]) - } - - Expr::Let { name, value, body, .. } => { - // Compile the value - let val = compile_expr(builder, value, variables, next_var, func_ids, module)?; - - // Create a new variable - let var = Variable::from_u32(next_var as u32); - builder.declare_var(var, types::I64); - builder.def_var(var, val); - variables.insert(name.name.clone(), var); - - // Compile the body with the new variable in scope - compile_expr(builder, body, variables, next_var + 1, func_ids, module) - } - - Expr::Call { func, args, .. } => { - // Check if it's a direct function call - if let Expr::Var(name) = func.as_ref() { - // Look up the function - let func_id = *func_ids.get(&name.name).ok_or_else(|| CompileError { - message: format!("Unknown function: {}", name.name), - })?; - - // Compile arguments - let mut arg_values = Vec::new(); - for arg in args { - arg_values.push(compile_expr(builder, arg, variables, next_var, func_ids, module)?); - } - - // Get function reference - let func_ref = module.declare_func_in_func(func_id, builder.func); - - // Make the call - let call = builder.ins().call(func_ref, &arg_values); - Ok(builder.inst_results(call)[0]) - } else { - Err(CompileError { - message: "Only direct function calls supported".to_string(), - }) - } - } - - Expr::Block { statements, result: block_result, .. } => { - let mut current_var = next_var; - // Compile all statements - for stmt in statements { - match stmt { - Statement::Let { name, value, .. } => { - let val = compile_expr(builder, value, variables, current_var, func_ids, module)?; - let var = Variable::from_u32(current_var as u32); - builder.declare_var(var, types::I64); - builder.def_var(var, val); - variables.insert(name.name.clone(), var); - current_var += 1; - } - Statement::Expr(expr) => { - compile_expr(builder, expr, variables, current_var, func_ids, module)?; - } - } - } - // Compile and return the result expression - compile_expr(builder, block_result, variables, current_var, func_ids, module) - } - - expr => { - use crate::ast::LiteralKind; - let expr_type = match expr { - Expr::Literal(lit) => match &lit.kind { - LiteralKind::String(_) => "String literal", - LiteralKind::Float(_) => "Float literal", - LiteralKind::Char(_) => "Char literal", - LiteralKind::Unit => "Unit literal", - _ => "Literal", - }, - Expr::EffectOp { effect, operation, .. } => { - return Err(CompileError { - message: format!("Effect operation '{}.{}' - effects are not supported in JIT", - effect.name, operation.name), - }); - } - Expr::Field { .. } => "Field access (records)", - Expr::Lambda { .. } => "Lambda/closure", - Expr::Match { .. } => "Match expression", - Expr::List { .. } => "List literal", - Expr::Record { .. } => "Record literal", - Expr::Tuple { .. } => "Tuple literal", - Expr::Run { .. } => "Run expression (effects)", - Expr::Resume { .. } => "Resume expression (effects)", - _ => "Unknown expression", - }; - Err(CompileError { - message: format!("Unsupported in JIT: {}", expr_type), - }) - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::parser::Parser; - - fn parse_function(src: &str) -> FunctionDecl { - let program = Parser::parse_source(src).expect("Parse error"); - match &program.declarations[0] { - Declaration::Function(f) => f.clone(), - _ => panic!("Expected function"), - } - } - - #[test] - fn test_simple_arithmetic() { - let func = parse_function("fn test(): Int = 1 + 2 * 3"); - let mut jit = JitCompiler::new().unwrap(); - let result = jit.compile_and_run(&func).unwrap(); - assert_eq!(result, 7); - } - - #[test] - fn test_conditionals() { - let func = parse_function("fn test(): Int = if 1 > 0 then 42 else 0"); - let mut jit = JitCompiler::new().unwrap(); - let result = jit.compile_and_run(&func).unwrap(); - assert_eq!(result, 42); - } - - #[test] - fn test_let_binding() { - let func = parse_function("fn test(): Int = { let x = 10; let y = 20; x + y }"); - let mut jit = JitCompiler::new().unwrap(); - let result = jit.compile_and_run(&func).unwrap(); - assert_eq!(result, 30); - } - - #[test] - fn test_recursive_fibonacci() { - use std::time::Instant; - - // Compile a program with recursive fibonacci - let src = r#" - fn fib(n: Int): Int = if n <= 1 then n else fib(n - 1) + fib(n - 2) - "#; - let program = Parser::parse_source(src).expect("Parse error"); - - let mut jit = JitCompiler::new().unwrap(); - - let compile_start = Instant::now(); - jit.compile_program(&program).unwrap(); - let compile_time = compile_start.elapsed(); - - // Call fib(30) - let exec_start = Instant::now(); - let result = unsafe { jit.call_function("fib", &[30]).unwrap() }; - let exec_time = exec_start.elapsed(); - - println!("\n=== JIT Benchmark ==="); - println!("Compile time: {:?}", compile_time); - println!("Execute fib(30) = {} in {:?}", result, exec_time); - println!("Total: {:?}", compile_time + exec_time); - - assert_eq!(result, 832040); - } -} diff --git a/src/main.rs b/src/main.rs index ec99373..b2b068f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,6 @@ mod ast; mod codegen; -mod compiler; mod debugger; mod diagnostics; mod exhaustiveness; @@ -141,13 +140,17 @@ fn main() { handle_pkg_command(&args[2..]); } "compile" => { - // Compile and run with JIT + // Compile to C code if args.len() < 3 { - eprintln!("Usage: lux compile [--benchmark]"); + eprintln!("Usage: lux compile [-o output.c] [--run]"); std::process::exit(1); } - let benchmark = args.iter().any(|a| a == "--benchmark"); - compile_file(&args[2], benchmark); + let run_after = args.iter().any(|a| a == "--run"); + let output_path = args.iter() + .position(|a| a == "-o") + .and_then(|i| args.get(i + 1)) + .map(|s| s.as_str()); + compile_to_c(&args[2], output_path, run_after); } path => { // Run a file @@ -166,7 +169,9 @@ fn print_help() { println!("Usage:"); println!(" lux Start the REPL"); println!(" lux Run a file (interpreter)"); - println!(" lux compile Compile and run with JIT (--benchmark for timing)"); + println!(" lux compile Compile to C code (stdout)"); + println!(" lux compile -o out.c Compile to C file"); + println!(" lux compile --run Compile and execute"); println!(" lux fmt Format a file (--check to verify only)"); println!(" lux check Type check without running"); println!(" lux test [pattern] Run tests (optional pattern filter)"); @@ -257,11 +262,11 @@ fn check_file(path: &str) { println!("{}: OK", path); } -fn compile_file(path: &str, benchmark: bool) { - use compiler::JitCompiler; +fn compile_to_c(path: &str, output_path: Option<&str>, run_after: bool) { + use codegen::c_backend::CBackend; use modules::ModuleLoader; use std::path::Path; - use std::time::Instant; + use std::process::Command; let file_path = Path::new(path); let source = match std::fs::read_to_string(file_path) { @@ -272,8 +277,7 @@ fn compile_file(path: &str, benchmark: bool) { } }; - // Parse - let parse_start = Instant::now(); + // Parse with module loading let mut loader = ModuleLoader::new(); if let Some(parent) = file_path.parent() { loader.add_search_path(parent.to_path_buf()); @@ -286,10 +290,8 @@ fn compile_file(path: &str, benchmark: bool) { 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 { @@ -298,63 +300,86 @@ fn compile_file(path: &str, benchmark: bool) { } 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, + // Generate C code + let mut backend = CBackend::new(); + let c_code = match backend.generate(&program) { + Ok(code) => code, Err(e) => { - eprintln!("JIT initialization error: {}", e); + eprintln!("C codegen error: {}", e); + eprintln!(); + eprintln!("Note: The C backend currently supports:"); + eprintln!(" - Integer, Float, Bool, String, Char, Unit literals"); + eprintln!(" - Arithmetic and comparison operators"); + eprintln!(" - If/then/else conditionals"); + eprintln!(" - Let bindings and blocks"); + eprintln!(" - Function definitions and calls"); + eprintln!(" - Records and enums (basic)"); + eprintln!(" - Console.print effect"); + eprintln!(); + eprintln!("Not yet supported: closures, lists, pattern variable binding,"); + eprintln!("other effects, higher-order functions."); 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(); + // Determine output + if run_after { + // Write to temp file, compile, and run + let temp_c = std::env::temp_dir().join("lux_output.c"); + let temp_bin = std::env::temp_dir().join("lux_output"); - // 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(); + if let Err(e) = std::fs::write(&temp_c, &c_code) { + eprintln!("Error writing temp file: {}", e); + std::process::exit(1); + } - match result { - Ok(value) => { - println!("{}", value); + // Try to find a C compiler + let cc = std::env::var("CC").unwrap_or_else(|_| "cc".to_string()); - 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); + let compile_result = Command::new(&cc) + .args(["-O2", "-o"]) + .arg(&temp_bin) + .arg(&temp_c) + .output(); + + match compile_result { + Ok(output) => { + if !output.status.success() { + eprintln!("C compilation failed:"); + eprintln!("{}", String::from_utf8_lossy(&output.stderr)); + std::process::exit(1); + } + } + Err(e) => { + eprintln!("Failed to run C compiler '{}': {}", cc, e); + eprintln!("Make sure gcc or clang is installed, or set CC environment variable."); + std::process::exit(1); } } - Err(e) => { - eprintln!("Execution error: {}", e); + + // Run the compiled binary + let run_result = Command::new(&temp_bin).status(); + match run_result { + Ok(status) => { + std::process::exit(status.code().unwrap_or(1)); + } + Err(e) => { + eprintln!("Failed to run compiled binary: {}", e); + std::process::exit(1); + } + } + } else if let Some(out_path) = output_path { + // Write to specified file + if let Err(e) = std::fs::write(out_path, &c_code) { + eprintln!("Error writing file '{}': {}", out_path, e); std::process::exit(1); } + eprintln!("Compiled to {}", out_path); + eprintln!("Compile with: cc -O2 -o output {}", out_path); + } else { + // Print to stdout + println!("{}", c_code); } }