Compile-time macros
mu includes a compile-time macro system that rewrites the AST before bytecode is emitted. Macros are defined in mu, so you can extend the language without inflating the host runtime.
Define a macro
import "macro"
macro.define("identity", fn(call, value) {
return value
})
Example: log + assert
import "macro"
import "ast"
macro.define("log", fn(_, value) {
printlnCall := ast.callExpression(ast.identifier("println"), [
ast.stringLiteral("[macro log]"),
value,
])
body := ast.blockStatement([
ast.expressionStatement(printlnCall),
ast.expressionStatement(value),
])
fnLiteral := ast.functionLiteral([ast.identifier("__macro_value")], false, body)
return ast.callExpression(fnLiteral, [value])
})
How it works
- Macro handlers run during compilation.
- The handler returns AST fragments.
- The compiler expands macro calls before generating bytecode.
Read more in docs/Macros.md and docs/MacroTutorial.md.
Next steps
- Review the language tour for syntax basics.
- Learn how macros fit into the pipeline in the runtime guide.
- Explore the FFI if you need native bindings.