MicroLua

MicroLua is a tiny Lua runtime for embedded systems and other places where portability, a small footprint, and predictable memory use matter.

In a local macOS size build with similar build flags, MicroLua's compiler/parser-less runtime artifacts total 218 KiB, or 259 KiB with the parser. That is about 2.8x smaller than Lua 5.5.0's runtime artifacts at 612 KiB.

Download

Clone the source repository:

git clone https://github.com/lus-lang/microlua.git
cd microlua

You can also download a source archive from the repository host and unpack it locally. The build instructions below assume that the current directory is the MicroLua source tree.

Compile

MicroLua uses Meson and Ninja. A debug build allows libc in the core and is the usual build for development:

meson setup builddir
ninja -C builddir

A release build compiles the core static library in freestanding mode:

meson setup builddir-release --buildtype=release
ninja -C builddir-release

In release mode, the core library is compiled with freestanding flags. The command-line executable still uses libc for file and console I/O.

For embedded systems that should not carry the source parser, build a bytecode-only runtime:

meson setup build-bytecode -Dcompiler=false --buildtype=release
ninja -C build-bytecode

The bytecode-only library omits the lexer and parser. It accepts precompiled MicroLua bytecode through MLuaLoadBytecode, MLuaDoBytecode, MLuaLoadBuffer, or MLuaDoBuffer.

Port Configuration

Port settings live in src/MLuaConfig.h. Use -Dport=generic64, generic32, cortex-m, or riscv32 for bundled presets, or provide a board-specific header:

meson setup build-board -Dport_header=path/to/my_board_mlua.h

A port header can override pointer size, heap alignment, default stack and frame sizes, GC threshold, math hooks, and compiler support.

Build Outputs

builddir/mlua
The command-line interpreter and REPL.
builddir/libmicrolua.a
The embeddable MicroLua static library.
builddir-release/libmicrolua.a
The freestanding release static library.
build-bytecode/mlua
The bytecode-only command-line runner.
build-bytecode/libmicrolua.a
The embeddable bytecode-only static library without the lexer or parser.

Use

Command Line

./builddir/mlua script.lua
./builddir/mlua
./builddir/mlua -e 'print(1 + 2 * 3)'
./builddir/mlua -o chunk.mlu script.lua
./build-bytecode/mlua chunk.mlu

Running mlua with no file starts the interactive REPL. The REPL evaluates one line as one chunk, so local variables do not persist across lines. Put multi-line programs in a file.

Use -o on a compiler-enabled host build to create a portable .mlu bytecode file. Bytecode has a versioned header with an explicit endianness byte and fixed-width serialized fields, so it can be moved across supported endian and pointer-size targets with a compatible MicroLua bytecode version and numeric format.

Options

Option Meaning
-h, --help Show command-line help.
-e EXPR, --eval EXPR Evaluate a source chunk supplied on the command line. Requires a compiler-enabled build.
-i, --interactive Enter interactive mode. Requires a compiler-enabled build.
-I FILE, --include FILE Include a file through the REPL's require support. Requires a compiler-enabled build.
-d, --dump Print memory usage statistics.
--memory-limit N Run with a heap limited to N bytes.
--no-column Omit column information from debug output.
-o FILE Compile a source file or -e chunk and write MicroLua bytecode to FILE. Requires a compiler-enabled build.
-v, --version Print version information.

Small Example

local total = 0
for i = 1, 10 do
  total = total + i
end
print(total)

Embed MicroLua

Link your application with libmicrolua.a and include the headers from src/. The core can run on a caller-provided heap and does not require libc in the freestanding release build.

The embedder controls host integration. Output and module loading are available only when the embedder installs callbacks.

MLuaSetOutput
Installs the callback used by functions such as print and error.
MLuaSetRequirer
Installs the callback used by require.

Optional io, os, dofile, and loadfile support lives in src/extensions/MLuaStdLib.c and requires libc.

A common embedded workflow is to compile source on a development host with mlua -o app.mlu app.lua, include the resulting bytes in firmware or external storage, then call MLuaDoBytecode or MLuaLoadBytecode on the target. A bytecode-only build rejects source-only workflows such as -e, the REPL, load, and loadfile.

Documentation

Language

MicroLua follows Lua syntax where that syntax fits its footprint. Supported statements include local declarations, assignments, function definitions, if, while, repeat, numeric for, generic for, do blocks, break, and return.

Supported values include nil, booleans, numbers, strings, tables, functions, C functions, and coroutines. Strings and source files are UTF-8. Numeric literals keep the Lua 5.3 integer/float distinction: math.type(1) is "integer" and math.type(1.0) is "float".

Supported operators include arithmetic +, -, *, /, %, exponentiation ^, length #, concatenation .., comparisons, and, or, and not.

Functions support lexical closures, upvalues, varargs, multiple return values, and proper tail calls for plain return f(...) style calls.

Tables

Tables have an array part and a hash part. Array keys must remain contiguous: assigning a value past #t + 1 is a runtime error because it would create a hole. Assigning nil past the end is a no-op.

MicroLua does not have metatables. It provides table.forward as a small table-delegation mechanism for lookup forwarding.

Base Globals

Function Arguments Returns Description
assertv: any [, message: string]All original arguments, or raises an error.Checks that v is not false or nil. If the check fails, raises message or "assertion failed!".
errormessage: stringDoes not return.Raises a runtime error using the string value of message, or "error" when no string is supplied.
loadchunk: string [, chunkname: string [, mode: string [, env: any]]]Compiled function, or nil, message.Compiles a string chunk. MicroLua accepts the extra Lua-style parameters but only uses the chunk name.
loadstringchunk: string [, chunkname: string [, mode: string [, env: any]]]Compiled function, or nil, message.Alias of load.
nexttable: table [, key: any]next_key, value, or nil.Returns the next key and value after key. Pass nil or omit key to start iteration.
pairstable: tableiterator, table, nil.Returns a generic-for iterator triple using next.
ipairstable: tableiterator, table, 0.Returns a generic-for iterator triple over array indices starting at 1.
pcallfunc: function, ...: anytrue, ... on success; false, message on error.Calls func in protected mode with the remaining arguments.
selectindex: integer|string, ...: anySelected values, a count, or no values.If index is "#", returns the number of extra arguments. Otherwise returns arguments from the numeric index onward; negative indices count from the end.
tonumbervalue: number|string [, base: integer]Number, or nil.Converts numbers or numeric strings. Base conversion is supported for integer strings.
tostringvalue: anyString.Converts a value to its string representation.
typevalue: anyString.Returns the MicroLua type name.
xpcallfunc: function, handler: functiontrue, ... on success; false, handled_error on error.Protected call that invokes handler with the error value when func fails.
print...: anyNo values.Writes tab-separated values through the embedder output callback. Available when the embedder installs output support; the REPL does.
requirename: stringModule result.Loads a module through the embedder requirer callback. Available only when that callback is installed; the REPL installs one.
unpacktable: table [, i: integer [, j: integer]]Values table[i] through table[j].Global compatibility alias of table.unpack.

math

Function Arguments Returns Description
math.absx: numberNumber.Absolute value.
math.acosx: numberNumber.Arc cosine in radians.
math.asinx: numberNumber.Arc sine in radians.
math.atany: number [, x: number]Number.Arc tangent. With two arguments, uses atan2(y, x).
math.atan2y: number [, x: number]Number.Alias of math.atan in MicroLua.
math.ceilx: numberNumber.Smallest integer not less than x.
math.cosx: numberNumber.Cosine of x radians.
math.coshx: numberNumber.Hyperbolic cosine.
math.degx: numberNumber.Converts radians to degrees.
math.expx: numberNumber.Returns e raised to x.
math.floorx: numberNumber.Largest integer not greater than x.
math.fmodx: number, y: numberNumber.C-style floating remainder of x / y.
math.frexpx: numbermantissa, exponent.Splits x into a normalized fraction and exponent.
math.ldexpmantissa: number, exponent: integerNumber.Returns mantissa * 2^exponent.
math.logx: number [, base: number]Number.Natural logarithm, or logarithm in the supplied base.
math.log10x: numberNumber.Base-10 logarithm.
math.max...: numberNumber, or nil when called without arguments.Maximum numeric argument.
math.min...: numberNumber, or nil when called without arguments.Minimum numeric argument.
math.modx: number, y: numberNumber.Alias of math.fmod.
math.modfx: numberinteger_part, fractional_part.Splits a number into integer and fractional parts.
math.powx: number, y: numberNumber.Returns x raised to y.
math.radx: numberNumber.Converts degrees to radians.
math.random[m: integer [, n: integer]]Number or integer.With no arguments, returns a float in [0, 1). With one argument, returns an integer in [1, m]. With two, returns an integer in [m, n].
math.randomseedseed: integerNo values.Seeds MicroLua's xorshift64 pseudo-random generator. Seed zero is changed to one.
math.sinx: numberNumber.Sine of x radians.
math.sinhx: numberNumber.Hyperbolic sine.
math.sqrtx: numberNumber.Square root.
math.tanx: numberNumber.Tangent of x radians.
math.tanhx: numberNumber.Hyperbolic tangent.
math.tointegerx: numberInteger, or nil.Returns x as an integer if it is exactly representable as a MicroLua integer.
math.typex: any"integer", "float", or nil.Reports whether a number is stored as an integer or float.
math.ulta: integer, b: integerBoolean.Unsigned less-than comparison of two integer values.

Available constants: pi, huge, maxinteger, and mininteger.

string

string.len, string.sub, string.byte, string.char, and string.reverse operate on codepoints. Pattern matching is byte-based, and case conversion is ASCII-only.

Function Arguments Returns Description
string.bytes: string [, i: integer [, j: integer]]Codepoints from i through j.Returns UTF-8 codepoints at codepoint positions. Negative indices count from the end.
string.char...: integerString.Encodes up to 256 numeric codepoints as UTF-8.
string.dumpfunc: functionBytecode string, or nil, message.Serializes a Lua closure in MicroLua bytecode format. C functions cannot be dumped.
string.finds: string, pattern: string [, init: integer [, plain: boolean]]start, end [, captures...], or nil.Finds a byte-position match. With plain truthy, searches for literal text. Otherwise uses MicroLua patterns.
string.formatformat: string, ...: anyString.Formats values into a bounded buffer using MicroLua's formatter.
string.lens: stringInteger.Returns the number of UTF-8 codepoints.
string.lowers: stringString.ASCII-only lowercase conversion. Non-ASCII bytes are preserved.
string.matchs: string, pattern: string [, init: integer]Captures, whole match, or nil.Matches a MicroLua pattern. Captures are returned when present; otherwise the matched substring is returned.
string.packformat: string, ...: integer|stringBinary string.Packs values using supported format codes: b, B, h, H, i, I, l, L, n, N, cN, z, and ignored endian markers.
string.packsizeformat: stringInteger, or nil, message.Returns the static packed size for fixed-size formats. Variable-length s and z formats return nil, message.
string.reps: string, n: integerString.Repeats s n times. Non-positive counts return an empty string.
string.reverses: stringString.Reverses the sequence of UTF-8 codepoints.
string.subs: string [, i: integer [, j: integer]]String.Returns a codepoint-indexed substring. Negative indices count from the end.
string.unpackformat: string, data: string [, pos: integer]Unpacked values followed by next position.Unpacks values from data starting at 1-based byte position pos.
string.uppers: stringString.ASCII-only uppercase conversion. Non-ASCII bytes are preserved.

Supported pattern features include anchors ^ and $, classes such as %a, %d, %s, %w, %l, %u, %p, %c, and %x, character sets, captures, and the *, +, -, and ? quantifiers.

table

Function Arguments Returns Description
table.concattable: table [, sep: string [, i: integer [, j: integer]]]String.Concatenates array entries from i through j, placing sep between entries.
table.forwardtable: table, forward: table|nilNo values.Sets MicroLua's table lookup forwarding target. This is the small substitute for metatable __index.
table.inserttable: table, value: any or table: table, pos: integer, value: anyNo values.Inserts at the end or at pos. Positions outside 1..#table+1 are errors because they would create holes.
table.maxntable: tableNumber.Returns the largest positive numeric key present in the table, or zero for non-tables/no numeric keys.
table.pack...: anyTable.Returns a table containing the arguments at integer keys and an n field with the argument count.
table.removetable: table [, pos: integer]Removed value, or nil.Removes and returns the entry at pos, defaulting to the last array entry, then shifts later entries down.
table.sorttable: table [, comp: function]No values.Sorts the array part in place. comp(a, b), when supplied, should return true when a comes before b.
table.unpacktable: table [, i: integer [, j: integer]]Values table[i] through table[j].Returns array entries from i through j. Negative or zero starts are clamped to one; inverted ranges return no values.

table.pack and table.unpack follow the Lua 5.2 naming. unpack is also available as a global compatibility alias.

coroutine

Function Arguments Returns Description
coroutine.closeco: coroutinetrue, or false, message.Closes a suspended or dead coroutine and drops its saved state. Closing a running coroutine is an error.
coroutine.createfunc: functionCoroutine.Creates a suspended coroutine that will run func.
coroutine.isyieldableNo arguments.Boolean.Returns true inside a coroutine when not crossing a C-call boundary.
coroutine.resumeco: coroutine, ...: anytrue, ... on success/yield; false, message on error.Starts or resumes co, passing the remaining arguments into the coroutine.
coroutine.runningNo arguments.co, false inside a coroutine; nil, true on the main thread.Reports the currently running coroutine and whether execution is on the main thread.
coroutine.statusco: coroutineString.Returns "suspended", "running", "normal", or "dead".
coroutine.yield...: anyResume arguments when the coroutine is resumed again.Suspends the current coroutine and transfers the arguments to its resumer.

Coroutines can yield from nested Lua calls. Yielding across a C-call boundary is an error.

Optional io and os

These are not part of the freestanding core. They are available in the REPL build through src/extensions/MLuaStdLib.c.

Function Arguments Returns Description
io.closefile: fileBoolean.Closes a file handle created by io.open.
io.linesfile: fileiterator, file, nil.Returns an iterator triple that reads lines from a file handle.
io.openpath: string [, mode: string]File handle, or nil, message.Opens a file with the supplied C fopen mode, defaulting to read mode.
io.read[format: string]Read value, or no value at EOF.Reads from standard input. Supported formats include line, *l, *L, *n, and *a.
io.write...: anyNo values.Writes values to standard output.
file:read[format: string]Read value, or no value at EOF.Reads from an opened file handle using the same formats as io.read.
file:write...: anyThe file handle.Writes values to the file and returns the handle for chaining.
file:closeself: fileBoolean.Closes the file handle.
file:linesself: fileiterator, file, nil.Returns an iterator triple over lines in the file.
os.clockNo arguments.Number.Returns CPU time in seconds according to the host C library.
os.date[format: string]String or table.Formats the current local time. "*t" returns a table with date/time fields.
os.exit[code: integer]Does not return.Terminates the host process with the integer exit code, or zero.
os.getenvname: stringString, or nil.Returns the value of an environment variable.
os.timeNo arguments.Number.Returns the current time as a host timestamp.
dofilepath: stringAll chunk return values.Loads and executes a file.
loadfilepath: stringCompiled function, or nil, message.Loads and compiles a file without executing it.

Limitations

Tests and Benchmarks

Run all tests in a debug build:

meson test -C builddir

Run the freestanding release suite, including the libc-free symbol guard:

meson test -C builddir-release

Run only interpreter, smoke, guard, CLI, or security suites:

meson test -C builddir --suite interpreter
meson test -C builddir --suite smoke
meson test -C builddir --suite bytecode
meson test -C builddir --suite guard
meson test -C builddir --suite cli
meson test -C builddir --suite security

Compare MicroLua against a local Lua 5.5 binary:

python3 bench/bench.py
python3 bench/bench.py --quick

The benchmark checks byte-identical output before reporting timing, native heap, and binary-size results. It skips cleanly when Lua 5.5 is not available.

Compatibility Reference

MicroLua intentionally selects a small subset and a few later additions from Lua. For background on standard Lua behavior, see the official manuals at lua.org/manual. This page documents only the features implemented by MicroLua.