Macros and Host Transforms
Lux deliberately separates two extension mechanisms:
- macros expand explicit macro calls at compile time;
- host transforms rewrite calls imported from a target module.
Both mechanisms live in Lux packages. They are compiler extensions, not runtime parsers hidden inside GLua hooks.
Package Phases
A Lux package may participate in three phases:
Runtime imports produce Lua locals. Macro imports produce compile-time bindings. Host transforms attach rewrite rules to normal runtime imports from a specific module.
Keeping the phases separate prevents a macro helper from accidentally becoming a runtime global and lets diagnostics say which phase failed.
Using Macros
Import macros with import macro. Macro calls expand before runtime codegen.
Namespace macro imports are also supported.
Macro bindings are not runtime values.
Writing Macros
Macro packages use the compiler macro API. MVP documentation shows the shape, not a promise that every helper is stable forever.
Prefer macro helper APIs such as m.expectArgCount, m.fail, m.expr, and
m.stmts over hand-built AST tables. That keeps diagnostics stable and gives
errors the original call span.
exprWithSetup
Some expression macros need temporary statements before the final expression.
exprWithSetup lets the macro return both.
The compiler chooses a hygienic temporary name when the macro asks for one. Avoid hard-coding user-visible names unless the macro intentionally declares an API.
Statement Macros
Statement macros expand into one or more statements. They cannot be used where a value is required.
Using a statement macro in expression position is a compile-time error.
Realm-safe Macros
Macro expansion must respect realm checks. A server-only macro cannot make client artifacts evaluate server-only arguments.
This is why macros participate in the same realm availability model as normal bindings.
What Host Transforms Are
A host transform rewrites calls that target an imported API. It is useful when a runtime library wants a natural call surface while the generated Lua should be denser or more GLua-native.
The transform is tied to the resolved imported binding, not the spelling alone. A local binding with the same name is not rewritten.
That rule prevents accidental rewrites in large modules.
Writing Host Transforms
A host transform package declares which module it transforms and exports rewrite handlers.
Transforms should report user-facing errors with source spans from the original call. A transform error should not look like an internal compiler crash.
call.imported and call.local
Host transform callbacks can see both the public imported name and the local alias chosen by the user.
Use call.imported to decide semantics. Use call.local only for diagnostics
that should echo the user's source spelling.
Choosing Macro vs Host Transform
Use a macro when the source explicitly opts into a compile-time operation:
- compile-time constants such as colors or materials;
- syntax-like helpers for hooks or commands;
- compile-time validation of literal arguments.
Use a host transform when a runtime API should stay readable but the compiler can produce better Lua for known calls:
- UI tree builders;
- dense draw command builders;
- domain-specific call lowering for packages.
Avoid using host transforms as invisible magic for ordinary functions. The more surprising the rewrite, the more it should be an explicit macro.
Diagnostics and Errors
Macro diagnostics distinguish user mistakes from macro author mistakes:
- invalid macro call position;
- invalid arity;
- statement expansion where an expression is required;
- macro returned a non-AST value;
- internal macro helper misuse.
When a macro author calls m.fail, the diagnostic points at the macro call. When
the macro package itself crashes, Lux reports that as a macro implementation
error with both the macro package span and use-site span when possible.
Source Maps
Generated Lua should map back to the user's Lux source, not only to macro package internals. Macro expansions therefore carry expansion spans:
- user call span for diagnostics caused by user arguments;
- macro definition span for errors in macro code;
- generated Lua span for runtime stack correlation.
This is especially important in GMod, where production errors usually arrive as Lua stack traces.