Home | Getting Started | Language Guide | Builtins | FFI | Macros | Examples
fy macros are words that run at compile time. When the compiler encounters a macro during compilation, it executes the macro immediately instead of emitting a call. The macro can inspect and modify the compiler's output, enabling custom syntax and compile-time computation.
macro: name body ;The body is compiled into a self-contained function that uses a separate macro data stack (isolated from the runtime stack). When invoked during compilation, the macro can:
- Read the last compiled quote with
peek-quote - Remove the last emitted push with
unpush - Emit literal values with
emit-lit - Emit word calls with
emit-word
These words are only available during macro execution:
| Word | Stack Effect | Description |
|---|---|---|
emit-lit |
value -- |
Emit code that pushes value as a literal |
emit-word |
name -- |
Emit a call to the named word |
peek-quote |
-- quote|0 |
Get the last quote literal pushed by the compiler |
unpush |
-- |
Remove the last quote push from compiled output |
The const macro evaluates a quote at compile time and bakes the result as a literal:
macro: const peek-quote unpush do emit-lit ;How it works:
peek-quote— get the quote the compiler just pushed (e.g.,[2 3 +])unpush— remove the push instruction from the compiled outputdo— execute the quote at compile time, producing a resultemit-lit— emit machine code to push that result as a constant
Usage:
[2 3 +] const . ( compiles as if you wrote: 5 . ): cfib dup 1 <= [ drop 1 ] [ dup 1- cfib swap 2 - cfib + ] ifte ;
macro: const peek-quote unpush do emit-lit ;
[10 cfib] const . ( computed at compile time! prints 89 )The Fibonacci value is computed during compilation. At runtime, it's just a literal push — zero overhead.
From examples/objc.fy, macros create custom syntax for Objective-C message sending:
macro: @class peek-quote unpush do cls emit-lit ;
macro: @sel peek-quote unpush do sel emit-lit ;This enables:
[NSWindow] @class
[alloc] @sel
msg0Which at compile time resolves the class and selector, baking the pointers as constants.
When the compiler sees a word marked as immediate (which is what macro: sets):
- It does not emit a call instruction
- Instead, it calls the macro function immediately
- The macro runs with access to
Builtins.compilerPtr— a pointer to the active compiler - The macro can call
emit-litandemit-wordto inject code into the current compilation - The macro can call
peek-quoteandunpushto inspect and modify recently compiled code
This happens at the ARM64 machine code level — macros don't manipulate an AST or bytecode, they directly control what machine instructions get emitted.
Macros can inspect quote contents using the standard quote operations:
macro: my-macro
peek-quote ( get the quote )
dup qlen ( check its length )
0 swap qnth ( get first element )
dup word? ( is it a word? )
[ word->str ] ( convert to string if so )
[ drop "not-a-word" ]
ifte
( ... do something with the string ... )
emit-lit
;Macros execute on a separate data stack (macro_data_stack_mem, 8KB) so they don't interfere with runtime stack state. This means macros can push and pop freely without corrupting the program being compiled.
- Macros can only emit literals (
emit-lit) and word calls (emit-word) - Macros cannot emit arbitrary machine code or control flow
peek-quoteonly sees the most recently compiled quote literal- Macros run in sequence with compilation — they see the compiler state at the point where they appear