6.0 KiB
Macro System (Phase 16) — Quickstart
Status: MVP available behind environment gates (default OFF). This page describes how to enable and use the initial features (derive, test runner, expansion dump) and the developer‑facing Pattern/Quote API preview.
Enabling/disabling macros
- Default: ON
- Disable:
NYASH_MACRO_DISABLE=1orNYASH_MACRO_ENABLE=0|false|off - Debug trace:
NYASH_MACRO_TRACE=1 - CLI shortcut for debug:
--expand(prints pre/post AST, and sets trace)
Derive (MVP)
- Provided derives:
Equals,ToString - Control via env:
NYASH_MACRO_DERIVE=Equals,ToString(default when unset)NYASH_MACRO_DERIVE_ALL=1(force all derives)
- Behavior:
- Injects
equals(__ny_other)andtoString()into Box declarations when not already present. - Public-only: derives operate on public fields only (Philosophy: Box independence / loose coupling).
- No changes to MIR instruction set; expansion happens on AST, then compiled as usual.
- Injects
Example
NYASH_MACRO_ENABLE=1 ./target/release/nyash --backend vm apps/APP/main.hako
Test runner (MVP)
- CLI:
--run-tests→ enable macro engine and inject a test harness main - Discovery targets:
- Top‑level functions with names
test_* - Box static/instance methods
test_*
- Top‑level functions with names
- Filtering:
--test-filter SUBSTRorNYASH_TEST_FILTER=SUBSTR - Entry policy when a main exists:
--test-entry wrap→ rename originalmainto__ny_orig_main, run tests then call original--test-entry override→ replace entry with test harness only- Force apply even if a main exists:
NYASH_TEST_FORCE=1
- Return policy in wrap mode:
--test-return {tests|original}orNYASH_TEST_RETURNtests(default): harness returns number of failed testsoriginal: return the original main result (tests still run)
- Parameterized tests (MVP):
NYASH_TEST_ARGS_DEFAULTS=1injects integer0defaults for each parameter (static/instance) - Exit code: number of failed tests (0 = green)
Examples
# run all tests in a file
./target/release/nyash --run-tests apps/tests/my_tests.hako
# filter + wrap entry + default arg injection
NYASH_MACRO_ENABLE=1 NYASH_TEST_ARGS_DEFAULTS=1 \
./target/release/nyash --run-tests --test-filter http --test-entry wrap apps/tests/my_tests.hako
Expansion dump
./target/release/nyash --expand --dump-ast apps/tests/ternary_basic.hako
Shows pre/post expansion AST (debug only).
Core Normalization (always-on when macros enabled)
Certain language sugars are normalized before MIR across all runners when the macro gate is enabled. These are not user macros and do not require any registration:
-
for sugar → Loop
for(fn(){ init }, cond, fn(){ step }, fn(){ body })- Emits:
init; loop(cond){ body; step } init/stepalso accept a single Assignment or Local instead offn(){..}.
-
foreach sugar → Loop
foreach(array_expr, "x", fn(){ body_with_x })- Expands into an index-based loop with
__ny_i, and substitutesxwitharray_expr.get(__ny_i)inside body.
Normalization order (within the core pass):
- for/foreach → 2) match(PeekExpr) → 3) loop tail alignment (carrier-like ordering; break/continue segments supported).
Notes:
- Backward-compat function names
ny_for/ny_foreachare also accepted butfor/foreachare preferred. - This pass is part of the language pipeline; it is orthogonal to user-defined macros.
Developer API (preview)
- Pattern/Quote primitives are available to bootstrap macro authorship.
- TemplatePattern:
- Bind placeholders using
$nameinside a template AST. - OrPattern: alternation of templates.
- Variadic
$...namesupported at any position inside call/array argument lists; binds the variable-length segment to a pseudo list (ASTNode::Program).
- Bind placeholders using
- Unquote:
- Replace
$namewith the bound AST. - Splice
$...nameinto call/array argument lists. - Array/Map nodes participate in pattern/unquote (map keys must match literally; values can bind via
$name).
- Replace
MacroCtx (PoC)
User macros executed via the PyVM sandbox receive a second argument ctx in expand(json, ctx):
- Shape (string):
{ "caps": { "io": bool, "net": bool, "env": bool } } - Policy: all caps default to false. The sandbox disables plugins and exposes a minimal Box API.
- Do not print to stdout from macros: the child process stdout is reserved for the expanded JSON.
- For diagnostics use stderr in the future; for now prefer silent operation or trace via the parent process.
Example (identity):
static box MacroBoxSpec {
name() { return "MacroCtxDemo" }
expand(json, ctx) { return json }
}
JSON test args (advanced)
For --run-tests, you can supply per-test arguments and instance construction details via NYASH_TEST_ARGS_JSON.
Shapes:
- Simple list (as before):
{ "test_name": [1, "s", true], "Box.method": [ 0, 1 ] } - Detailed object:
{ "Box.method": { "args": [ ... ], "instance": { "ctor": "new|birth", "args": [ ... ], "type_args": ["T", "U"] } } }
- Typed values inside args:
{ "i": 1 },{ "f": 1.2 },{ "s": "x" },{ "b": true },null- Arrays/Maps as value:
{ "array": [1, 2, 3] },{ "map": { "k": 1, "k2": {"s":"v"} } } - Call/method/var literals:
{ "call": "fn", "args": [...] },{ "method": "m", "object": {"var":"obj"}, "args": [...] },{ "var": "name" }
Diagnostics:
- When JSON cannot be mapped or arity mismatches, warnings are printed with
[macro][test][args]and the specific test may be skipped unlessNYASH_TEST_ARGS_DEFAULTS=1is set.
Notes
- Textual
quote(code)is supported as a convenience via the existing parser, but$nameplaceholders should be built in AST templates directly. - This API will evolve; treat as experimental until the stable
macro_boxinterface is finalized.
Expansion Safety (Depth/Cycle)
- The macro engine applies a bounded number of expansion passes and stops on fixpoint.
- Environment tuning:
NYASH_MACRO_MAX_PASSES(default 32)NYASH_MACRO_CYCLE_WINDOW(default 8) — detect cycles across recent statesNYASH_MACRO_TRACE=1— pass-by-pass logging