742 lines
15 KiB
Markdown
742 lines
15 KiB
Markdown
# 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.
|