luazig is a reimplementation of Lua 5.5.0 in Zig, continuously validated against the PUC Lua reference implementation.
The goal is not to write a similar language, but to gradually achieve drop-in compatibility with PUC Lua: the same observable behavior on the official test suite, honest limitations, clean architecture, and a public Zig-facing embedding API.
- Implement Lua 5.5.0 in Zig with behavior as close as possible to PUC Lua.
- Pass the official upstream
testes/*.luasuite without test-specific hacks or harness workarounds. - Keep the reference implementation close at hand and compare
refvszigdirectly. - Develop a public Zig embedding API semantically close to the Lua C API.
- Use the current system Zig as the primary toolchain.
- Follow a PUC-first architectural approach when it does not lead to a clearly worse solution.
The project is in a pre-release / parity-focused state.
| Metric | Result |
|---|---|
Upstream matrix (testes/*.lua) |
30/31 pass (exit code parity) |
Differential output (--diff mode) |
0 output_diff — all behavioral differences resolved |
Smoke tests (tests/smoke/*.lua) |
49/49 pass |
big.lua |
both_fail — pre-existing (expects coroutine.wrap harness in all.lua) |
Matrix is run with _port=true; _soft=true prelude (disables non-portable OS/shell/locale checks and resource-heavy branches).
Geomean slowdown vs PUC Lua: 2.76x (lower is better; 1.0x = parity).
| Workload | Zig/PUC |
|---|---|
| string_concat | 1.56x |
| dynamic_load | 1.65x |
| comparisons | 1.75x |
| float_arith | 2.36x |
| int_arith | 2.61x |
| global_arith | 2.65x |
| branch_loop | 2.50x |
| field_access | 2.80x |
| temp_table_alloc | 2.96x |
| string_loop | 3.18x |
| mixed_arith | 3.26x |
| metamethod_add | 3.39x |
| lua_calls | 3.65x |
| coroutine_yield | 3.69x |
| array_access | 3.79x |
| hash_access | 4.15x |
See STATUS.md for detailed profiling methodology, hotspot analysis, and optimization history.
The bytecode VM (--vm=bc, default) is the only actively developed backend. The IR VM has been fully removed from the codebase.
zigfrom system toolchain.- C toolchain for reference Lua:
make,gccor compatible compiler. - Initialized upstream test suite submodule.
On Arch Linux:
sudo pacman -S --needed zig gcc makeVerify Zig:
zig versionInitialize submodule:
git submodule update --init --recursiveBuild the reference Lua in C:
make lua-c
./build/lua-c/lua -vBuild the Zig implementation:
zig build -Doptimize=ReleaseFast
./zig-out/bin/luazig --help
./zig-out/bin/luazigc --helpRun the full release gate:
tools/release_gate.shReference implementation:
./build/lua-c/lua./build/lua-c/luac
Zig implementation:
./zig-out/bin/luazig./zig-out/bin/luazigc
src/bin/ CLI entrypoints: luazig, luazigc
src/lua/ Language implementation: lexer, parser, AST, codegen, VM, stdlib, API
src/util/ Utility wrappers, including Zig std.Io stdio layer
lua-5.5.0/ Vendored PUC Lua 5.5.0: src/ (reference C) and testes/ (upstream test corpus)
tools/ Differential runners, release gate, perf tooling
tools/perf/ Core perf baselines and current snapshots
Runtime path:
src/lua/lexer.zig— reads source bytes, produces tokens.src/lua/parser.zig— builds AST.src/lua/codegen_bc.zig— compiles AST to bytecode (Proto).src/lua/vm.zig:runBytecode()— executes bytecode on a shared stack.src/lua/api.zig— public Zig-facing API and testC compatibility layer.src/lua/c_api.zig— C ABI (lua_*functions) for dlopen-based C extension loading.src/lua/dump.zig/src/lua/undump.zig— binary chunk serialization (string.dump/ load).
The test strategy is based on differential testing: the same upstream Lua test is run on both PUC Lua and luazig, then exit code and output are compared.
| Tool | Purpose |
|---|---|
tools/run_tests.py |
Targeted differential runner for specific suites |
tools/testes_matrix.py |
Per-file matrix over lua-5.5.0/testes/*.lua |
tools/testes_matrix.py --diff |
Adds normalized stdout comparison (detects behavioral differences even when exit codes match) |
tools/smoke_compare.py |
Runs tests/smoke/*.lua with both engines, compares stdout+stderr+exit byte-for-byte |
tools/api_regression_lane.py |
Zig unit/integration tests + testC lane |
tools/perf_compare.py |
Main perf gate: 16 micro-benchmarks, geomean Zig/PUC ratio, regression check |
tools/release_gate.sh |
Unified command for checking release readiness |
Run the full safe matrix:
python3 tools/testes_matrix.py --no-build --timeout 120Run the matrix with differential output comparison:
python3 tools/testes_matrix.py --diffRun smoke tests:
python3 tools/smoke_compare.pyRun the perf gate:
python3 tools/perf_compare.py # run + compare vs baseline
python3 tools/perf_compare.py --no-build # skip rebuild
python3 tools/perf_compare.py --update-baseline # rewrite baselineThe matrix runs upstream tests with the prelude _port=true; _soft=true:
_port=truedisables non-portable OS/shell/locale/filesystem checks._soft=truedisables or shortens resource-heavy branches.big.luain this mode returns early (if _soft then return 'a' end); standalone execution without_softrequires acoroutine.wrapharness (as inall.lua).
The main command for checking the current state:
tools/release_gate.shIt runs:
zig build test -Doptimize=Debug- Official
testClane - Targeted parity suites
- Iterative dispatch stress under 1-MB host stack
- Full safe matrix
- Core perf snapshot + perf guard
Expected result: green on all correctness lanes (build/unit, testC, differential smoke, iterative-dispatch stress, and upstream matrix).
For development history, architectural decisions, detailed performance analysis, GC design, and the full work log, see STATUS.md.
Same license as PUC Lua (MIT).