Generated Lua

Generated Lua is part of the Lux developer experience. GMod errors happen in Lua stack traces, so emitted code must be legal, readable, and source correlated.

Single-file Output

Single-file compile output is a direct Lua chunk. It is useful for language examples and backend debugging.

Single-file output shape
fn add(a, b) a + b

print(add(1, 2))

Project builds use module factories instead of a single top-level chunk.

Module Factories

GMod project modules are emitted as factories. The loader supplies __lux_import, and the factory returns an export table.

GMod module factory
fn helper(x) {
  x + 1
}

export { publicApi = helper }

Modules do not expose a global import function. Import stays inside the generated loader and module factories.

Export Tables

Exports map public names to internal bindings.

Export alias
local player_inventory = {}

export { p_inv = player_inventory }

The internal name remains private. Other modules can import p_inv, not player_inventory.

Source Comment Modes

luxc compile examples\features.lux --source-comments none
luxc compile examples\features.lux --source-comments readable
luxc compile examples\features.lux --source-comments boundary
luxc compile examples\features.lux --source-comments dense
  • none: production-friendly output with sidecar maps only.
  • readable: comments at review anchors such as functions and branch blocks.
  • boundary: comments whenever the mapped source line changes.
  • dense: comments for every mapped generated line.

Use readable while inspecting generated Lua. Use none when generated files should be smaller and source maps are available.

Source Maps

Source maps connect generated Lua positions back to Lux source. They are used by luxc map-error:

luxc map-error generated\hud.lua.map.json 42

Expected output shape:

generated/lua/lux/client/my_addon/hud.lua:42:9
  -> src/hud/cl_paint.lux:18:3

Macro expansions and host transforms carry expansion spans so diagnostics can distinguish user call sites from macro implementation code.

match Lowering

Value-position match lowers to a temporary and branches. Lux avoids wrapping ordinary matches in closures.

match in value position
local mode = match kind {
  FillKind.Solid => "solid"
  FillKind.Linear => "linear"
}

Return position can emit direct returns when that preserves evaluation order.

Optional Access Lowering

Optional access lowers to temporaries so receivers and indexes are evaluated only when needed.

Optional access and nil coalescing
local ammo = weapon?.Clip1() ?? 0

The emitted shape may be compacted, but it must preserve nil-only coalescing and single evaluation of side-effectful expressions.

Local Limits

Lua 5.1 has a hard local-variable limit per function. Lux must never emit a chunk that GMod rejects with "function has more than 200 local variables".

The backend narrows temporary scopes and reports deterministic diagnostics when generated code cannot be made legal:

error[CODEGEN_LOCAL_LIMIT]:
generated function would exceed Lua 5.1 local variable limit

When this happens, split the function, move repeated expression work into a helper, or reduce generated temporary pressure in the source expression.