Lua
A light, quick little language: pleasant for small games, and it compiles fast.
How to run it on your phone
vml run examples/lua/test/sysinfo.lua
Compiling takes a while (one to two minutes for C, a few seconds for scripting languages). Once the program is running, the gamepad is at the bottom of the screen - the D-pad and the four action buttons are all there. Tap the back arrow in the top-right corner to return to the Shell.
What to know before you write
- Call
ui_*directly - Compiles fast, good for experimenting
Examples
| File | What it shows |
|---|---|
app/life.lua | Game of Life (double buffering plus eight-neighbour sum) |
test/sysinfo.lua | Device information |
Also in this folder: app/g2048.lua, app/snake.lua, demo/demo_std.lua, test/libs.lua, tty/demo_tty.lua, ui/demo_ui.lua
The rest of this page is taken straight from the VML source (third_party/vml/VMLPrepares/LuaCompiler/): the README covers what this front end supports and how to compile it, and the language reference covers the syntax itself. When upstream changes, regenerating this page brings it up to date.
Language reference
Lua language compiler specification
Version: v1.0 | Date: 2026-07-06 | Revised by: Shenzhen Tanso Intelligent Technology Co., Ltd.
Specification standard
| Field | Value |
|---|---|
| Target standard | Lua 5.1 subset (2006) |
| Release year | 2006 |
| Completeness | ~93% |
| MCU completeness | ~90% |
| Tests | 0 (the test directory is yet to be created) |
| Updates | 2026-05-18: removed goto (a Lua 5.2 feature); corrected completeness and test counts |
Keywords
and break do else elseif end false for function if in local nil not or repeat return then true until while
Overview
This compiler compiles the Lua language into VML (Virtual Machine Language) assembly. Lua is a lightweight, embeddable scripting language, widely used for game development, embedded systems and configuration scripts.
Language features
1. Basic Lua syntax
Variable declaration
local x = 10 -- local variable
global_var = 20 -- global variable
y = 3.14 -- number
name = "Lua" -- string
flag = true -- boolean
Comments
-- single-line comment
--[[
multi-line comment
can span several lines
]]
2. Data types
Literals
- Integer:
42(decimal) - Hexadecimal:
0xFF,0x3FF44000(prefix0x/0X) - Float:
3.14,1.5e-2 - String:
"hello",'world' - Boolean:
true,false - Nil:
nil
Basic types
- nil: the empty value
- boolean: boolean value (true/false)
- number: number (integer and float)
- string: string
- table: table (array and dictionary)
- function: function
- userdata: user data
- thread: coroutine
Type checking
type("hello") -- "string"
type(42) -- "number"
type(true) -- "boolean"
type(nil) -- "nil"
3. Tables
Array usage
local arr = {10, 20, 30, 40}
print(arr[1]) -- 10 (Lua indexes start at 1)
arr[5] = 50 -- add an element
Dictionary usage
local person = {
name = "Alice",
age = 25,
city = "Beijing"
}
print(person.name) -- "Alice"
person.job = "Engineer"
Mixed usage
local mixed = {
"apple", "banana", "cherry",
price = 100,
count = 3
}
4. Control structures
Conditionals
if score >= 90 then
grade = "A"
elseif score >= 80 then
grade = "B"
elseif score >= 70 then
grade = "C"
else
grade = "F"
end
Loops
-- while loop
local i = 1
while i <= 10 do
print(i)
i = i + 1
end
-- for loop (numeric)
for i = 1, 10 do
print(i)
end
for i = 10, 1, -1 do
print(i)
end
-- for loop (generic)
for k, v in pairs(table) do
print(k, v)
end
-- repeat loop
local i = 1
repeat
print(i)
i = i + 1
until i > 10
5. Functions
Function definition
function add(a, b)
return a + b
end
-- anonymous function
local multiply = function(a, b)
return a * b
end
-- multiple return values
function minmax(a, b)
if a < b then
return a, b
else
return b, a
end
end
Function calls
local result = add(10, 20)
local x, y = minmax(5, 3)
print(multiply(4, 5))
Variadic parameters
function sum(...)
local total = 0
local args = {...}
for i = 1, #args do
total = total + args[i]
end
return total
end
print(sum(1, 2, 3, 4, 5)) -- 15
6. Operators
Arithmetic operators
+- addition-- subtraction*- multiplication/- division%- modulo^- exponentiation-- negation (unary)
Relational operators
==- equal to~=- not equal to<- less than>- greater than<=- less than or equal>=- greater than or equal
Logical operators
and- logical andor- logical ornot- logical not
Other operators
..- string concatenation#- length operator (string or table)
7. Standard library functions
Basic functions
print("Hello") -- output
type(x) -- type check
tostring(123) -- convert to string
tonumber("456") -- convert to number
Math library
math.abs(-10) -- absolute value
math.floor(3.7) -- round down
math.ceil(3.2) -- round up
math.sqrt(16) -- square root
math.sin(math.pi/2) -- sine
math.random() -- random number
math.max(1, 5, 3) -- maximum
math.min(1, 5, 3) -- minimum
String library
string.len("hello") -- length
string.sub("hello", 2, 4) -- substring
string.upper("hello") -- uppercase
string.lower("HELLO") -- lowercase
string.reverse("abc") -- reverse
string.rep("x", 5) -- repeat
string.format("%d", 42) -- format
Table library
table.insert(t, 1, "x") -- insert
table.remove(t, 1) -- remove
table.concat(t, ", ") -- concatenate
table.sort(t) -- sort
8. Coroutines
-- create a coroutine
co = coroutine.create(function()
for i = 1, 3 do
print("coroutine", i)
coroutine.yield()
end
end)
-- run the coroutine
coroutine.resume(co)
coroutine.resume(co)
coroutine.resume(co)
9. Error handling
-- protected call with pcall
local success, result = pcall(function()
error("something went wrong")
end)
if not success then
print("Error:", result)
end
-- xpcall with an error handler function
xpcall(function()
error("test")
end, function(err)
print("Error handler:", err)
end)
10. Module system
Module definition
-- mymodule.lua
local M = {}
function M.add(a, b)
return a + b
end
function M.subtract(a, b)
return a - b
end
return M
Using a module
local mymodule = require("mymodule")
print(mymodule.add(10, 5))
print(mymodule.subtract(10, 5))
11. VML code generation conventions
Table implementation
Lua tables are implemented with a VML memory structure:
table structure:
offset 0: type tag (0x01 = table)
offset 4: array part size
offset 8: hash part size
offset 12: array data pointer
offset 16: hash data pointer
Function calling convention
function add(a, b) return a + b end
VML code:
LABEL add
; save the frame pointer
PUSH R14
MOVE R14, R13
; access the arguments: a at [R14+12], b at [R14+8]
MOVE R0, [R14+12] ; a
MOVE R1, [R14+8] ; b
ADD R0, R0, R1 ; a + b
; the return value is in R0
POP R14
RET
Closure implementation
function makeCounter()
local count = 0
return function()
count = count + 1
return count
end
end
The VML implementation uses a closure structure to store the upvalue (the count variable).
12. Example programs
Factorial
function factorial(n)
if n <= 1 then
return 1
else
return n * factorial(n - 1)
end
end
print("5! =", factorial(5)) -- 120
Fibonacci sequence
function fibonacci(n)
if n <= 2 then
return 1
else
return fibonacci(n-1) + fibonacci(n-2)
end
end
for i = 1, 10 do
print(i, fibonacci(i))
end
Simple game logic
-- game character definitions
local player = {
name = "Hero",
health = 100,
attack = 20,
defense = 10
}
local enemy = {
name = "Monster",
health = 50,
attack = 15,
defense = 5
}
-- combat function
function attack(attacker, defender)
local damage = attacker.attack - defender.defense
if damage < 0 then damage = 1 end
defender.health = defender.health - damage
print(attacker.name .. " attacks " .. defender.name .. " for " .. damage .. " damage")
return defender.health <= 0
end
-- combat loop
while true do
if attack(player, enemy) then
print("Player wins!")
break
end
if attack(enemy, player) then
print("Enemy wins!")
break
end
end
Floating-point and 64-bit compilation modes
The VML toolchain controls how floating point and 64-bit integers are handled through three compilation flags:
| Flag | Possible values | Default | Description |
|---|---|---|---|
--float32 | hard / soft / none | hard | 32-bit float handling mode |
--float64 | hard / soft / none | soft | 64-bit float (number) handling mode |
--int64 | hard / soft / none | soft | 64-bit integer handling mode |
32-bit float (float32)
Every number in Lua is of the number type (64-bit float by default). 32-bit float arithmetic is used for internal intermediate results:
hardmode (default): uses the VML native float instructionsMOVEF/ADDF/SUBF/MULF/DIVF/CMPF/NEGF, operating directly through the sixteen float registers F0-F15.softmode: uses the Q15.16 fixed-point software emulation librarysoftfloat.c, emulating float arithmetic through the__vml_float_add/sub/mul/div/neg/abs/cmpfunctions and so on.nonemode: floating-point arithmetic is disabled.
64-bit float (double)
Lua's number type is natively a 64-bit float (configurable to 32-bit), which VML handles as follows when compiling:
softmode (default): uses the IEEE 754 double-precision software emulation librarysoftdouble.c, emulating through the__vml_double_add/sub/mul/div/neg/abs/cmp,__vml_int2double/double2int,__vml_float2double/double2floatfunctions and so on. Compatible with every platform (MCU included).hardmode: uses the VML double-precision instructionsMOVED/ADDD/SUBD/MULD/DIVD/CMPD/NEGD, operating through the eight double-precision registers D0-D7.nonemode: floating-point arithmetic is disabled.
64-bit integer (int64)
The VML compilation layer supports a 64-bit integer extension for large-integer arithmetic:
softmode (default): uses the dual-register software emulation librarysoftint64.c, emulating 64-bit integer arithmetic through the__vml_i64_add/sub/neg/and/or/xor/not/shl/shrfunctions and so on.hardmode: reserved; a future VML version will support native 64-bit integer instructions.nonemode: degrades to 32-bit integers.
Software emulation libraries
The software emulation libraries above all live in the Lib/shared/ directory, are written in C, are compiled to VML by the C compiler, and are shared by every language:
| Library file | Purpose | Core functions |
|---|---|---|
softfloat.c | Q15.16 fixed-point 32-bit float emulation | __vml_float_add/sub/mul/div/neg/abs/cmp, __vml_int2float/float2int |
softdouble.c | IEEE 754 double-precision 64-bit float emulation | __vml_double_add/sub/mul/div/neg/abs/cmp, __vml_int2double/double2int, __vml_float2double/double2float |
softint64.c | 64-bit integer dual-register emulation | __vml_i64_add/sub/neg/and/or/xor/not/shl/shr |
13. Compilation limitations
Current implementation status
- Lexer: fully implemented, supports Lua 5.1 keywords and symbols
- Parser: fully implemented, supports Lua statements, expressions and functions
- Code generator: basically implemented, generates VML instructions
- Standard library: the
Lib/lua/directory; basic functions are available
Implemented
- ✅ Variable declaration (local/global)
- ✅ Control flow: if/elseif/else/while/for/repeat-until
- ✅ Functions: definition / multiple return values / variadic / anonymous functions
- ✅ Table: basic array and dictionary operations
- ✅ Standard library: print/type/tostring/tonumber
Core features yet to be implemented
- Closures and upvalue capture
- Coroutines — skipped in MCU mode
- Metatables and metamethods
- Garbage collection (a simplified version)
- Complete standard library: every math/string/table function
Technical challenges
- Dynamic typing: Lua is a dynamically typed language, while VML is a statically typed register machine
- Table implementation: an efficient mixed array and hash-table structure is needed
- Closures: functions can capture outer variables (upvalues)
- GC: garbage collection (a simplified reference-counting version)
14. Integration with the VML runtime
Lua programs interact with the VML runtime through system calls:
- SYSCALL 4: output a character (used by print)
- SYSCALL 5: input a character
- SYSCALL 6: output an integer
- SYSCALL 7: input an integer
- SYSCALL 130: allocate memory (used by tables)
- SYSCALL 131: free memory
- SYSCALL 132: garbage collection
Lua's simplicity and embeddability make it an ideal choice for game scripts, configuration files and embedded systems. Through the VML compiler, Lua programs can run on many hardware platforms.
🆕 String encoding (v1.65.19)
This language compiler uses the VML string system indirectly through the shared library (Lib/shared/).
| Directive | Width | Encoding | C type |
|---|---|---|---|
.string | 8-bit | UTF-8 | char* |
.wstring | 16-bit | UTF-16LE | wchar_t* |
.ustring | 32-bit | UTF-32LE | char32_t* |
MCU mode (default): strings are output as UTF-8 (.string) OS mode: the encoding can be determined through the VML_WSTRING macro
The shared library already provides wide-string conversion functions (wchar.h/uchar.h), which each language compiler can use as needed.
Compiler README
Lua 5.1 compiler
Path: VMLPrepares/LuaCompiler/ Completeness: ~93% | 🟢 production ready Standard library: Lib/lua/
Features
- ✅ Parsing + code generation (Lexer/Parser/CodeGenerator)
- ✅ Control flow (if/elseif/else/while/for/repeat-until)
- ✅ Functions: definition / multiple return values / variadic / anonymous functions
- ✅ Table: basic array and dictionary operations
- ⚠️ Closures/upvalues — partially supported
- ⚠️ Standard library: basic print/type/math functions
- ✅ POKE/PEEK memory access (MMIO)
- ✅ Shared built-in function library (builtins.vml)
Compilation modes
MCU mode (default --mode mcu)
MCU mode is tuned for microcontrollers and bare-metal environments (Arduino/STM32/8051 and so on), and automatically skips features that are incompatible with an operating system.
Skipped (no code is generated for this syntax):
- coroutine, dofile/require
Kept (implemented underneath by the BIOS):
- table, metatable
- POKE/PEEK memory-mapped I/O (MMIO)
- Basic type arithmetic, control flow, function calls
- printf/puts mapped to the UART
OS mode (--mode os, reserved)
OS mode targets environments that have an operating system (such as embedded Linux or an RTOS), and will support every language feature (file system, threads, asynchronous I/O, exceptions, reflection and so on).
RAM levels
--ram k: kilobyte level (2KB~64KB, such as 8051/PIC/AVR)--ram m: megabyte level (64KB~1MB, such as ARM Cortex-M, default)--ram g: gigabyte level (such as x86/DDR systems)--stack-size <bytes>: specify the stack size by hand (by default it is allocated automatically from--ram)
MCU-safe coding tips
- Limited stack space (256-4096 bytes is typical), so avoid deep recursion
- Deep recursion is not allowed (beyond 10 levels the stack must be evaluated)
- Dynamic loading (dofile/eval) is not allowed
- Floating-point arithmetic may require the soft-float library
Usage
dotnet run --project VMLTool -- input.lua -o output.vml
Tests
Test/Lu/ — 0 test files (the test directory is yet to be created)