A Rust implementation of Lua 5.1.1.
rilua is a from-scratch Lua 5.1.1 interpreter written in Rust. It targets behavioral equivalence with the PUC-Rio reference interpreter -- executed Lua code must produce identical results.
Part of the WoW Emulation project. Zero external dependencies -- only Rust's standard library.
rilua is built for the World of Warcraft emulation ecosystem:
- Addon development and testing -- Run and test WoW addons outside the game client without launching WoW
- Server-side scripting -- Embed in private server emulators (CMaNGOS, TrinityCore, AzerothCore) for scripted encounters, quests, and NPC behavior
- Client Lua environment emulation -- Reproduce the WoW client's Lua sandbox including restricted stdlib, taint system, and WoW-specific extensions (bit library, string functions, global aliases)
- Addon compatibility testing -- Automated test harness for verifying addons against the Lua 5.1.1 spec
It also serves as an embeddable Lua 5.1.1 interpreter for Rust applications
and as a readable reference implementation for studying Lua internals.
See docs/use-cases.md for details.
World of Warcraft's addon system uses Lua 5.1.1. Key 5.1-specific traits:
unpack is a global (moved to table.unpack in 5.2), all numbers are f64
(5.3 added integers), no goto keyword (added in 5.2). See
Warcraft Wiki: Lua.
rilua reproduces the PUC-Rio lua command-line interface:
# Run a Lua script
rilua script.lua
# Execute a string
rilua -e 'print("hello")'
# Interactive REPL
rilua -i
# All flags: -e stat, -l name, -i, -v, --, -
rilua -v
# Lua 5.1.1 Copyright (C) 1994-2006 Lua.org, PUC-Rioriluac reproduces the PUC-Rio luac bytecode compiler and lister:
# Compile to bytecode
riluac -o output.luac script.lua
# List bytecode instructions
riluac -l script.lua
# Detailed listing (constants, locals, upvalues)
riluac -l -l script.lua
# Syntax check only
riluac -p script.luaBinary chunks are cross-compatible with PUC-Rio in both directions.
rilua provides a Rust-idiomatic API with IntoLua/FromLua conversion
traits (inspired by mlua):
use rilua::{Lua, StdLib};
// Create interpreter with all standard libraries
let mut lua = Lua::new_with(StdLib::ALL)?;
// Execute Lua code
lua.exec("x = 1 + 2")?;
// Read and write globals with automatic type conversion
let x: f64 = lua.global("x")?;
assert_eq!(x, 3.0);
lua.set_global("greeting", "hello")?;
// Selective library loading for sandboxing
let mut sandbox = Lua::new_with(StdLib::BASE | StdLib::STRING | StdLib::TABLE)?;See docs/api.md for the full API reference.
All Lua 5.1.1 language features are implemented:
- Variables, assignments, local declarations
- Control flow:
if/elseif/else,while,repeat/until, numericfor, genericfor,break,return - Functions: closures, varargs (
...), multiple return values, tail calls, method syntax (obj:method()) - Tables: array and hash parts, constructors (
{1, 2, key = "val"}) - Metatables: all 17 metamethods (
__index,__newindex,__call,__add,__sub,__mul,__div,__mod,__pow,__unm,__eq,__lt,__le,__concat,__len,__gc,__tostring) - String metatable: method syntax (
("hello"):upper()) - Coroutines:
create,resume,yield,wrap,status,running - Environments:
setfenv/getfenv, per-closure global tables - Protected calls:
pcall,xpcallwith error objects and stack traces - Error messages with variable names (matching PUC-Rio format)
All 9 standard libraries with all functions:
| Library | Functions | Notes |
|---|---|---|
| base | 29 | print, assert, type, tostring, tonumber, pairs, ipairs, next, select, unpack, pcall, xpcall, error, loadstring, loadfile, dofile, load, setmetatable, getmetatable, rawget, rawset, rawequal, setfenv, getfenv, collectgarbage, newproxy, _G, _VERSION |
| string | 14 | len, byte, char, sub, rep, reverse, lower, upper, format, find, match, gmatch, gsub, dump. Pattern matching with all Lua 5.1.1 features. gfind alias included. |
| table | 9 | concat, insert, remove, sort, maxn, getn, setn, foreach, foreachi. Sort uses PUC-Rio's median-of-three quicksort. |
| math | 28 | abs through tanh, pi, huge, mod alias. |
| io | 18 | 11 library functions + 7 file methods. stdin/stdout/stderr handles. |
| os | 11 | clock, date, difftime, execute, exit, getenv, remove, rename, setlocale, time, tmpname. |
| debug | 14 | getinfo, getlocal, setlocal, getupvalue, setupvalue, traceback, getregistry, getmetatable, setmetatable, getfenv, setfenv, gethook, sethook, debug. |
| package | 9 | require, module, loaded, preload, loaders, config, path, cpath, seeall, loadlib. |
| coroutine | 6 | create, resume, yield, wrap, status, running. |
- 38 register-based opcodes matching PUC-Rio encoding
string.dumpand binary chunk loading- Binary chunks are cross-compatible with PUC-Rio (byte-identical output for simple programs, loadable in both directions)
- Non-UTF-8 source files supported (
\255,\0in string literals)
Arena-based incremental mark-sweep with generational indices:
- 5-state incremental collection (Pause, Propagate, SweepString, Sweep, Finalize)
- Write barriers (backward for tables, forward for upvalues)
__gcfinalizers with error propagation- Weak tables (
__mode= "k", "v", or "kv") collectgarbage()API: collect, stop, restart, count, step, setpause, setstepmul
debug.debug()interactive mode: Stub (returns immediately).- C library loading:
package.loadlibreturns "not supported" (incompatible ABI). Lua file loading viarequireworks. - SIGINT handling: No signal-based interruption of running code.
All 23 official Lua 5.1.1 test files pass, including the all.lua
runner which executes all tests sequentially with aggressive GC settings.
Tests: api, attrib, big, calls, checktable, closure, code, constructs,
db, errors, events, files, gc, literals, locals, main, math, nextvar,
pm, sort, strings, vararg, verybig.
The all.lua runner completes in ~17 seconds.
See docs/testing.md for details on running modes and the comparison
script.
Pipeline: Source -> Lexer -> Parser -> AST -> Compiler -> Proto -> VM
| Component | Description |
|---|---|
| Lexer | Tokenizer with one-token lookahead, byte-based (&[u8]) |
| Parser | Recursive descent producing typed AST |
| Compiler | AST walker emitting register-based bytecode into Proto |
| VM | Register-based dispatch, PUC-Rio's 38 opcodes, CallInfo chain |
| GC | Arena-based incremental mark-sweep, write barriers, finalizers |
| API | Trait-based Rust-idiomatic embedding (IntoLua/FromLua) |
See docs/architecture.md for design documentation.
Development tools (Rust 1.92.0, markdownlint) can be installed automatically with Mise:
mise install# Build
cargo build
# Run the interpreter
cargo run -- script.lua
# Run tests
cargo test
# Run quality gate
cargo fmt -- --check && cargo clippy --all-targets && cargo test && cargo doc --no-depsFive test layers: unit tests inside compiler and VM modules,
integration tests (Lua scripts with assert()), oracle comparison
tests (same Lua code run in both rilua and PUC-Rio, comparing output),
the PUC-Rio official test suite as a compatibility target, and
behavioral equivalence tests for edge cases.
PUC-Rio tests pass both individually and through the all.lua runner.
See docs/testing.md for the testing strategy and
lua.org/tests/ for the official test
documentation.
Dual-licensed under either:
- MIT License (LICENSE-MIT)
- Apache License, Version 2.0 (LICENSE-APACHE)