Changelog
View SourceAll notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Unreleased
[v1.0.0-rc.0] - 2026-05-26
This is the first release candidate for 1.0.0. The library has been
rewritten on a new Elixir-native Lua 5.3 virtual machine, and the public
API is intended to be stable. Please report any regressions before final.
Added
- New Elixir-native Lua 5.3 virtual machine: lexer, parser, compiler, and register-based executor, with no Erlang or C dependencies.
- Standard library:
string(includingstring.formatwidth/precision,string.pack/unpack/packsize, and the full pattern engine forfind/match/gmatch/gsub),table,math(includingmath.fmod),debug,iostubs (sandboxed),os(sandboxed),package/require. _Gglobal table and Lua 5.3_ENVsemantics for global access.- Full metamethod dispatch:
__index,__newindex,__call, plus the arithmetic, comparison (including~=via__eqand<=/>=falling back through__lt), length, concat, andtostringmetamethods. - Varargs (
...), multiple returns, genericfor,goto/label,break, protected calls (pcall,xpcall). userdatasupport for passing arbitrary Elixir terms across the boundary.- Beautiful Lua-style stack traces and error messages with source line
tracking. Every runtime error carries line and source info (#214, #215),
and
attempt to call/attempt to indexerrors name the offending callee/target (#228). Inspectprotocol support for VM values returned across theLua.eval!/2boundary via display structs for tables, closures, userdata, and native functions (#218).- Mix tasks:
mix lua.eval,mix lua.suite,mix lua.bench(#220). - Lua 5.3 official test suite integration with per-file rationale for
suite files that are deferred as intentional non-goals (
main.lua,files.lua,attrib.lua,verybig.lua— shell-out, file I/O, and filesystemrequiresemantics that conflict with a sandboxed embedded VM) (#216). - Benchmark harness comparing against Luerl and PUC-Lua, with quick mode
and multi-
ninputs (#230) and asetup_luaport.shhelper (#225).
Changed
- VM backend: Luerl is no longer a runtime dependency. The library now
runs on its own Elixir-native VM. Luerl is kept only as a
:benchmark-env dependency for performance comparison. - Encoded value tags now use the new VM's internal representation:
{:tref, integer()}for tables (replacing:luerl.tref()),{:udref, integer()}for userdata (replacing:luerl.usdref()),{:native_func, fun}for Elixir-defined Lua callables (replacing:luerl.erl_func()), and{:lua_closure, _, _}for compiled Lua functions. - Parser error messages have a new format. The old Luerl-style
"Line 1: syntax error before: ';'"is now produced by the new parser (e.g."Expected expression"); user-visible string contents differ. - Chunks no longer require a separate "load" step —
Lua.Chunknow holds a compiled prototype and is reusable acrossLua.eval!/2calls. - 64-bit integer arithmetic and bitwise ops wrap on overflow per Lua 5.3 §3.4.1, instead of widening to bignums (Luerl's behaviour).
Lua.RuntimeExceptionandLua.CompilerExceptionare now publicly documented; user code can pattern-match and rescue them.
Removed
- The
{module(), atom(), list()}MFA encoding form is no longer accepted byLua.encode!/2. Use a function literal or adefluacallback instead.
Performance
- Right-size register tuple allocations (#153).
- O(N²) → O(N) upvalue collection in the closure handler (#154).
- O(1) upvalue access by storing upvalues as a tuple (#155).
- Fully tail-recursive CPS executor with line tracking moved off the heap (#156).
- Fast-path the executor dispatch loop (#223).
- Fast-path
Numeric.to_signed_int64for in-range integers (#227).
Fixed
- 64-bit integer overflow wrapping for arithmetic and bitwise ops (#177).
- Empty/missing-key table reads now return
nilper Lua 5.3 §3.4.11 (#179, #200). - Long-string
[[ ... ]]lexer handles embedded]and bracket levels like[==[ ... ]==], includingmain.lua-style headers (#180). - Comment tokens no longer leak past the lexer in expression lists (#182).
- Stdlib modules are pre-populated in
package.loadedsorequire"io"resolves (#184); module sentinel is set before executing required modules (#191). - For-loop variable now binds per statement, fixing register reuse (#195).
- Closure-handler crash on missing upvalue cells in
get_open_upvalueandset_open_upvalue(#196). _ENVsemantics for global variable access (#197).- Hex literal and string coercion in bitwise ops (#198);
math.fmodimplemented forbitwise.luaverification (#199). - Function declaration assigned to in-scope local rather than shadowing it (#185).
- Multi-return expansion no longer overflows the register tuple (#189).
- Pattern engine threads VM state through
gsubcallbacks and preserves capture order (#188, #190). - Atom values encode to strings (#158).
- Files containing only comments load successfully.
- Unicode characters supported in Lua scripts.
pairssurvives mid-iteration deletion by tracking dead keys (#202).- Metamethod closures receive operands through varargs (#203).
- Float division by zero yields ±
math.hugeinstead of raising (#204);//and%with a float-zero divisor returninf/nan(#211). - Lexer treats vertical tab and form feed as whitespace (#206).
- Table-library functions (
insert,remove,concat, etc.) honor__index,__newindex, and__len(#208). - Numeric
forcoerces string control values per Lua 5.3 §3.3.5 (#209). iois now exposed as a table of sandboxed stubs (#210).~=routes through the__eqmetamethod (#212);<=/>=fall back through__ltper Lua 5.3 §3.4.4 (#213).- Parser threads position info through bare-expression and unexpected-end errors (#222).
- Internal Lua VM frames pruned from
Lua.RuntimeExceptionstacks by default; opt back in withLua.new(debug: true)(#221). - Line number attribution for the first line of a chunk (#240).
string.packno longer emits compile warnings (#224).
[v0.4.0] - 2025-12-06
Changed
- Upgrade to Luerl 1.5.1
Fixed
- Warnings on Elixir 1.19
[v0.3.0] - 2025-06-09
Added
- Guards for encoded Lua values in
defluafunctionsis_table/1is_userdata/1is_lua_func/1is_erl_func/1is_mfa/1
Fixed
defluafunction can now specify guards when using or not using state
[v0.2.1] - 2025-05-14
Added
Lua.encode_list!/2andLua.decode_list!/2for encoding and decoding function arguments and return values
Fixed
- Ensure that list return values are properly encoded
[v0.2.0] - 2025-05-14
Changed
- Any data returned from a
defluafunction, or a function set byLua.set!/3is now validated. If the data is not an identity value, or an encoded value, it will raise an exception. In the past,Luaand Luerl would happily accept bad values, causing downstream problems in the program. This led to unexpected behavior, where depending on if the data passed was decoded or not, the program would succeed or fail.
[v0.1.1] - 2025-05-13
Added
Lua.put_private/3,Lua.get_private/2,Lua.get_private!/2, andLua.delete_private/2for working with private state
[v0.1.0] - 2025-05-12
Fixed
- Errors now correctly propagate state updates
- Fixed version requirements issues, causing references to undefined
luerl_new - Allow Unicode characters to be used in Lua scripts
- Files with only comments can be loaded
Changed
- Upgrade to Luerl 1.4.1
- Tables must now be explicitly decoded when receiving as arguments
defluaand other Elixir callbacks