-
Notifications
You must be signed in to change notification settings - Fork 3
Macros
Macros in Kairo are token-level substitutions they operate on raw tokens before parsing, similar to
C/C++ #define but with scoping and balanced-delimiter requirements. Macros are identified by the !
suffix on their name.
For AST-level transformations with type awareness, see Attributes.
A macro is defined with the macro keyword, a name ending in !, optional parameters, and a body.
The body must have balanced delimiters:
macro double!(x) {
x + x
}
macro greeting! {
"hello, world"
}
At every use site, the preprocessor replaces the macro invocation with the body, substituting parameters:
var a = double!(5) // replaced with: 5 + 5
var b = greeting! // replaced with: "hello, world"
Parameters are typeless they accept any sequence of tokens. Multiple parameters are comma-separated:
macro clamp!(value, lo, hi) {
if value < lo { lo } else if value > hi { hi } else { value }
}
var x = clamp!(temperature, 0, 100)
Macros can be passed to other macros. The inner macro is expanded at the final substitution site:
macro tag! { "debug" }
macro repeat!(m) { m! m! m! }
var labels = repeat!(tag) // becomes: "debug" "debug" "debug"
Kairo provides a set of compiler-intrinsic macros for common tasks.
| Macro | Description |
|---|---|
concat!(a, b) |
Join tokens into a single token |
stringify!(a) |
Convert a token to a string literal |
unstringify!(s) |
Convert a string literal back to tokens |
var name = concat!(my, _var) // becomes: my_var
var s = stringify!(some_ident) // becomes: "some_ident"
Warning
unstringify! converts a string into raw tokens that are injected into the source. This is a
potential injection risk only use with trusted, compile-time-known strings.
| Macro | Description |
|---|---|
count!(...args) |
Number of arguments |
first!(...args) |
First argument |
last!(...args) |
Last argument |
rest!(...args) |
All arguments except the first |
init!(...args) |
All arguments except the last |
count!(a, b, c) // 3
first!(a, b, c) // a
last!(a, b, c) // c
rest!(a, b, c) // b, c
init!(a, b, c) // a, b
| Macro | Description |
|---|---|
file! |
Current file path as a string literal |
line! |
Current line number as an integer literal |
column! |
Current column number as an integer literal |
module_path! |
Current module path as a string literal |
std::println(f"logged from {file!}:{line!}")
| Macro | Description |
|---|---|
error!(msg) |
Emit a compile error with the given message |
warning!(msg) |
Emit a compile warning |
note!(msg) |
Emit a compile note |
eval if platform == "wasm" {
error!("WebAssembly is not supported yet")
}
An optional error code can be passed as a second argument:
warning!("deprecated API", "W0042")
| Macro | Description |
|---|---|
include!(path) |
Paste file contents as tokens |
include_str!(path) |
File contents as a string literal |
embed!(path) |
File contents as a byte array ([byte]) |
unique_id! |
Generate a unique identifier |
todo!(msg) |
Runtime panic placeholder with optional message |
unreachable!(msg) |
Assert a code path is unreachable |
// Embed a shader as a string
eval VERTEX_SHADER = include_str!("shaders/vertex.glsl")
// Embed a binary resource
eval ICON_DATA = embed!("assets/icon.png")
// Mark unfinished code
fn process() -> Config {
todo!("implement config parsing")
}
todo!() panics at runtime with the given message. unreachable!() is undefined behavior if
reached the optimizer assumes the code path is dead.
| Macro | Description |
|---|---|
defined!(name) |
Check if a macro with the given name exists |
eval if defined!(DEBUG_MODE) {
fn log(msg: string) { std::println(f"[DEBUG] {msg}") }
} else {
fn log(msg: string) { }
}
| Macro | Description |
|---|---|
undef!(name) |
Remove a macro definition |
macro TEMP! { 42 }
var x = TEMP! // 42
undef!(TEMP)
// var y = TEMP! // compile error: TEMP is not defined
Some macros are compiler intrinsics that perform operations beyond token substitution:
| Macro | Description | Details |
|---|---|---|
label!(name) |
Declare a jump target | Control Flow |
jump!(name) |
Unconditional jump to a label | Control Flow |
forget!(ptr) |
Drop a pointer from AMT tracking | Unsafe |
unwrap!(expr) |
Force-unwrap a nullable, panic on null | Variables |
mref!(T) |
Produce an rvalue reference type (&&T) |
Classes |
These use macro syntax (name!) to make their usage explicit and searchable in a codebase, but
they are not user-definable they are built into the compiler.
Unlike C/C++ #define, Kairo macros respect scope. A macro defined inside a module or block is
only visible within that scope:
module internal {
macro BUFFER_SIZE! { 4096 }
var buf: [u8; BUFFER_SIZE!]
}
// BUFFER_SIZE! is not visible here
Top-level macros follow the same visibility rules as other declarations:
pub macro MAX_RETRIES! { 3 } // visible to importers
priv macro INTERNAL_FLAG! { true } // file-scoped
| Macros | Attributes | |
|---|---|---|
| Operates on | Raw tokens | AST nodes |
| Type awareness | No | Yes |
| Expansion time | Before parsing | After parsing |
| Can modify structure | Token substitution only | Can transform the AST |
| Syntax | name!(args) |
@name on declarations |
| Hygiene | Scoped, balanced delimiters | Full AST hygiene |
Use macros for simple substitutions, constants, and conditional compilation. Use attributes for code transformations that need type information or structural awareness.
See Attributes for the AST-level transformation system.
// Define a macro
macro square!(x) { x * x }
var s = square!(5) // 25
// No-parameter macro
macro VERSION! { "1.0.0" }
std::println(f"version: {VERSION!}")
// Built-in macros
std::println(f"file: {file!}, line: {line!}")
eval SHADER = include_str!("shader.glsl")
eval ICON = embed!("icon.png")
// Variadic helpers
var n = count!(a, b, c, d) // 4
// Diagnostics
eval if sizeof(usize) < 8 {
error!("64-bit platform required")
}
// Conditional compilation
macro DEBUG! { true }
eval if defined!(DEBUG) {
fn trace(msg: string) { std::println(f"[TRACE] {msg}") }
}
// Scoped macros
module config {
priv macro DEFAULT_PORT! { 8080 }
pub eval PORT = DEFAULT_PORT!
}
This wiki mirrors the language reference at kairolang.org/docs. To edit a page, edit the source at kairo-web/src/content/docs/language changes sync automatically.
Start here: Primitives
1. Fundamentals
2. Functions & Control Flow
3. Types
4. Modules & Metaprogramming
5. Memory & Safety
6. Interop & Concurrency