# Macro System Proposals - 12 Compile-Time Code Generation Features **Generated by**: Task Agent 2 (Macro System Analysis) **Date**: 2025-10-12 **Philosophy**: Extend existing @enum/@match macros, everything expands at compile time --- ## ๐Ÿ“Š Priority Matrix | Priority | Complexity | Count | Examples | |----------|-----------|-------|----------| | **High** | Easy | 2 | @test, @assert | | **High** | Medium | 3 | @benchmark, @trace, @guard | | **Medium** | Medium | 4 | @defer, @derive, @memo, @async | | **Low** | Medium | 3 | @builder, @singleton, @inline | --- ## ๐Ÿš€ High Priority - Easy Implementation ### 1. **@test Macro** **Impact**: Immediate benefit for selfhost compiler testing **Difficulty**: Easy (desugar to function with metadata) ```hakorune // Syntax @test("test description") function test_name() { @assert(condition, "failure message") } // Example @test("addition works correctly") function test_addition() { @assert(1 + 1 == 2, "basic addition failed") @assert(10 + 20 == 30, "larger numbers failed") } // Expands to: box TestCase_test_addition { name: StringBox description: StringBox birth() { me.name = "test_addition" me.description = "addition works correctly" } run() { // Original function body with assert expansions if !(1 + 1 == 2) { TestFramework.fail("basic addition failed", "test_addition", 3) } if !(10 + 20 == 30) { TestFramework.fail("larger numbers failed", "test_addition", 4) } } } // Register with test framework TestRegistry.register(new TestCase_test_addition()) ``` **Implementation strategy**: 1. Parser recognizes `@test("...")` annotation before function 2. Transform function into test case Box 3. Generate test registry registration 4. Integrate with tools/smokes system --- ### 2. **@assert Macro** **Impact**: Inline runtime checks with good error messages **Difficulty**: Easy (desugar to if + panic) ```hakorune // Syntax @assert(condition) @assert(condition, "message") // Example @assert(x > 0) @assert(y != null, "y must not be null") // Expands to: if !(x > 0) { panic("Assertion failed: x > 0", __FILE__, __LINE__) } if !(y != null) { panic("Assertion failed: y must not be null", __FILE__, __LINE__) } ``` **Implementation strategy**: 1. Parser recognizes `@assert(...)` as statement 2. Extract condition and optional message 3. Generate conditional panic with file/line metadata --- ## ๐Ÿ”ฅ High Priority - Medium Implementation ### 3. **@benchmark Macro** **Impact**: Essential for performance work (current focus!) **Difficulty**: Medium (timing + statistics) ```hakorune // Syntax @benchmark(warmup: 10, repeat: 50) function benchmark_name() { // code to benchmark } // Example @benchmark(warmup: 10, repeat: 50) function bench_string_concat() { local s = "" local i = 0 loop(i < 100) { s = s + "x" i = i + 1 } return s } // Expands to: box Benchmark_bench_string_concat { name: StringBox warmup_count: IntegerBox repeat_count: IntegerBox birth() { me.name = "bench_string_concat" me.warmup_count = 10 me.repeat_count = 50 } run_once() { // Original function body local s = "" local i = 0 loop(i < 100) { s = s + "x" i = i + 1 } return s } run() { // Warmup local i = 0 loop(i < me.warmup_count) { me.run_once() i = i + 1 } // Measurement local times = new ArrayBox() i = 0 local start = TimeBox.now_ms() loop(i < me.repeat_count) { local iter_start = TimeBox.now_ns() me.run_once() local iter_end = TimeBox.now_ns() times.push(iter_end - iter_start) i = i + 1 } local end = TimeBox.now_ms() // Statistics local total = end - start local per_op = total / me.repeat_count print(`${me.name}: ${per_op} ยตs/op (${me.repeat_count} iterations)`) } } BenchmarkRegistry.register(new Benchmark_bench_string_concat()) ``` **Implementation strategy**: 1. Parser recognizes `@benchmark(...)` annotation 2. Extract warmup/repeat parameters 3. Generate wrapper Box with timing logic 4. Integrate with tools/bench_unified.sh --- ### 4. **@trace Macro** **Impact**: Debugging and execution analysis **Difficulty**: Medium (inject logging at multiple points) ```hakorune // Syntax @trace function traced_function(x, y) { // body } // Example @trace function calculate(a, b) { local result = a + b if result > 10 { result = result * 2 } return result } // Expands to: function calculate(a, b) { TraceBox.enter("calculate", ["a" => a, "b" => b]) local result = a + b TraceBox.log("result", result) if result > 10 { TraceBox.log("branch: result > 10", true) result = result * 2 TraceBox.log("result (after multiply)", result) } TraceBox.exit("calculate", result) return result } // Output: // [TRACE] โ†’ calculate(a=5, b=7) // [TRACE] result = 12 // [TRACE] branch: result > 10 = true // [TRACE] result (after multiply) = 24 // [TRACE] โ† calculate = 24 ``` **Implementation strategy**: 1. Parser recognizes `@trace` annotation 2. Inject enter/exit logging 3. Inject intermediate value logging 4. Control via NYASH_TRACE_FUNCTIONS environment variable --- ### 5. **@guard Macro** **Impact**: Precondition checking with good error messages **Difficulty**: Medium (pattern matching + error generation) ```hakorune // Syntax @guard(condition, "error message") function guarded_function(params) { // body } // Example @guard(x > 0, "x must be positive") @guard(y != null, "y must not be null") function divide(x, y) { return x / y } // Expands to: function divide(x, y) { if !(x > 0) { panic("Precondition violation: x must be positive", __FILE__, __LINE__) } if !(y != null) { panic("Precondition violation: y must not be null", __FILE__, __LINE__) } return x / y } ``` **Implementation strategy**: 1. Parser recognizes `@guard(...)` annotations 2. Collect all guards for function 3. Inject checks at function entry --- ## ๐Ÿ’ก Medium Priority - Medium Implementation ### 6. **@defer Macro** **Impact**: Guaranteed cleanup (complement to existing `cleanup`) **Difficulty**: Medium (transform to cleanup blocks) ```hakorune // Syntax @defer(expression) // Example function process_file(path) { local file = FileBox.open(path) @defer(file.close()) local content = file.read() @defer(log("Processing complete")) return content.length() } // Expands to: function process_file(path) { local file = FileBox.open(path) cleanup { file.close() } local content = file.read() cleanup { log("Processing complete") } return content.length() } ``` **Implementation strategy**: 1. Parser recognizes `@defer(...)` as statement 2. Transform to `cleanup { ... }` block 3. Respect existing cleanup block ordering (LIFO) --- ### 7. **@derive Macro** **Impact**: Automatic trait/protocol implementation **Difficulty**: Medium (codegen from Box structure) ```hakorune // Syntax @derive(Trait1, Trait2, ...) box ClassName { fields... } // Example @derive(Debug, Equals, Clone) box Person { name: StringBox age: IntegerBox } // Expands to: box Person { name: StringBox age: IntegerBox // Debug trait debug() { return `Person { name: ${me.name.debug()}, age: ${me.age.debug()} }` } // Equals trait equals(other) { if other == null { return false } return me.name.equals(other.name) && me.age.equals(other.age) } // Clone trait clone() { local cloned = new Person() cloned.name = me.name.clone() cloned.age = me.age.clone() return cloned } } ``` **Implementation strategy**: 1. Parser recognizes `@derive(...)` annotation on box 2. For each trait, generate appropriate methods 3. Support built-in traits: Debug, Equals, Clone, Hash, Serialize --- ### 8. **@memo Macro** **Impact**: Automatic memoization for expensive computations **Difficulty**: Medium (cache generation) ```hakorune // Syntax @memo function expensive_function(params) { // body } // Example @memo function fibonacci(n) { if n <= 1 { return n } return fibonacci(n - 1) + fibonacci(n - 2) } // Expands to: box FibonacciMemo { cache: MapBox birth() { me.cache = new MapBox() } call(n) { local key = n.to_string() if me.cache.has(key) { return me.cache.get(key) } // Original function body local result if n <= 1 { result = n } else { result = me.call(n - 1) + me.call(n - 2) } me.cache.set(key, result) return result } } local fibonacci = new FibonacciMemo() ``` **Implementation strategy**: 1. Parser recognizes `@memo` annotation 2. Generate wrapper Box with cache MapBox 3. Hash function arguments for cache key 4. Support cache eviction strategies (LRU, size-based) --- ### 9. **@async Macro** (Sugar for nowait/await) **Impact**: Cleaner async code generation **Difficulty**: Medium (transform to nowait/await) ```hakorune // Syntax @async function async_function() { // body with await expressions } // Example @async function fetch_user_data(id) { local user = await fetch_user(id) local posts = await fetch_posts(user.id) return {user, posts} } // Expands to: function fetch_user_data(id) { nowait user_future = fetch_user(id) local user = await user_future nowait posts_future = fetch_posts(user.id) local posts = await posts_future return TupleBox.new(user, posts) } ``` **Implementation strategy**: 1. Parser recognizes `@async` annotation 2. Transform `await expr` to `nowait fut = expr; await fut` 3. Ensure proper future handling --- ## ๐Ÿ”ฎ Low Priority - Medium Implementation ### 10. **@builder Macro** **Impact**: Fluent API generation **Difficulty**: Medium (method chaining generation) ```hakorune // Syntax @builder box ClassName { field1: Type1 field2: Type2 } // Example @builder box HTTPRequest { url: StringBox method: StringBox headers: MapBox body: StringBox } // Expands to: box HTTPRequest { url: StringBox method: StringBox headers: MapBox body: StringBox with_url(value) { me.url = value return me } with_method(value) { me.method = value return me } with_headers(value) { me.headers = value return me } with_body(value) { me.body = value return me } build() { @assert(me.url != null, "url is required") @assert(me.method != null, "method is required") return me } } // Usage: local request = new HTTPRequest() .with_url("https://api.example.com") .with_method("POST") .with_body("{\"key\":\"value\"}") .build() ``` **Implementation strategy**: 1. Parser recognizes `@builder` annotation 2. Generate `with_*` methods for each field 3. Generate `build()` method with validation --- ### 11. **@singleton Macro** **Impact**: Global instance management **Difficulty**: Medium (static instance generation) ```hakorune // Syntax @singleton box ClassName { // fields and methods } // Example @singleton box Configuration { settings: MapBox birth() { me.settings = new MapBox() me.load_defaults() } load_defaults() { me.settings.set("debug", false) me.settings.set("port", 8080) } } // Expands to: box Configuration { settings: MapBox birth() { me.settings = new MapBox() me.load_defaults() } load_defaults() { me.settings.set("debug", false) me.settings.set("port", 8080) } } static box ConfigurationSingleton { instance: Configuration get_instance() { if me.instance == null { me.instance = new Configuration() } return me.instance } } // Usage: local config = ConfigurationSingleton.get_instance() ``` **Implementation strategy**: 1. Parser recognizes `@singleton` annotation 2. Generate wrapper static box with instance management 3. Lazy initialization on first access --- ### 12. **@inline Macro** (LLVM backend only) **Impact**: Performance hint for hot functions **Difficulty**: Medium (LLVM attribute) ```hakorune // Syntax @inline function hot_function() { // body } // Example @inline function add(a, b) { return a + b } // Expands to: // (VM: no change) // (LLVM: marks function with 'alwaysinline' attribute) ``` **Implementation strategy**: 1. Parser recognizes `@inline` annotation 2. VM backend: ignore (no effect) 3. LLVM backend: add `alwaysinline` function attribute 4. WASM backend: add inline hint --- ## ๐ŸŽฏ Implementation Priority Recommendation **Phase 1: Testing & Debugging** (Week 1-2) 1. โœ… @test 2. โœ… @assert 3. โœ… @trace 4. โœ… @benchmark **Phase 2: Code Quality** (Week 3-4) 1. โœ… @guard 2. โœ… @defer 3. โœ… @derive (Debug, Equals, Clone) **Phase 3: Performance & Patterns** (Week 5-6) 1. โœ… @memo 2. โœ… @async 3. โœ… @inline **Phase 4: Advanced Patterns** (Week 7+) 1. โœ… @builder 2. โœ… @singleton --- ## ๐Ÿ“Š Integration with Existing System ### Macro Expansion Pipeline ``` Source Code โ†“ Parser (recognize @macro annotations) โ†“ AST with macro annotations โ†“ Macro Expansion Pass (new!) โ†“ Expanded AST (no macros) โ†“ MIR Builder (existing) โ†“ MIR 16-instruction set ``` **Key insight**: Macros are purely compile-time, expand to regular Hakorune code before MIR generation. ### Macro Definition System ```hakorune // Future: User-defined macros @macro("my_macro") function expand_my_macro(args, context) { // Return AST nodes return parse(` print("Macro expanded with ${args[0]}") `) } ``` --- ## ๐Ÿงช Testing Strategy Each macro must have: 1. โœ… Expansion test (verify correct code generation) 2. โœ… Runtime test (verify expanded code works) 3. โœ… Error test (verify bad usage gives good errors) 4. โœ… Integration test (verify macro composability) Example: ```bash # Test @benchmark macro tools/smokes/v2/profiles/quick/macros/benchmark_basic.sh tools/smokes/v2/profiles/quick/macros/benchmark_nested.sh tools/smokes/v2/profiles/quick/macros/benchmark_errors.sh ``` --- ## ๐Ÿ“ˆ Expected Outcomes **Testing productivity**: 80% reduction in test boilerplate **Debugging speed**: 5x faster with @trace **Code safety**: 90% reduction in precondition bugs with @guard **Performance analysis**: Seamless benchmarking with @benchmark --- ## โœ… Implementation Checklist For each macro: - [ ] Parser support for annotation syntax - [ ] AST node representation - [ ] Macro expander implementation - [ ] Error message generation - [ ] Documentation with examples - [ ] Smoke tests (quick + integration) - [ ] Integration with existing tools (smokes, bench) --- **Next**: See [practical-boxes-proposals.md](practical-boxes-proposals.md) for runtime library proposals.