-
Added
Declaration::Macrovariant (src/execution_request/declaration.rs)- New enum variant to represent macro declarations
- Stores the macro function expression that will be executed at bundle time
- Added handling in
into_module_item()to emit macros as regular variables (temporary)
-
Implemented
createMacro()detection (src/execution_request/get_module_declarations.rs)- Added
extract_macro_function()helper that detects the pattern:createMacro(fn) - Modified variable declaration parsing to check if init expression is a
createMacro()call - When detected, creates
Declaration::Macroinstead ofDeclaration::VarInit
- Added
-
Updated reference handling (
src/execution_request/get_references_from_declaration.rs)- Added
Declaration::Macrocase toget_references_from_declaration() - Added
Declaration::Macrocase torename_references_in_declaration() - Macros are treated like regular expressions for reference tracking
- Added
-
Added comprehensive test (
src/execution_request/tests.rs)- Test verifies that
closure = createMacro(...)is detected as a Macro - Test verifies that
createMacroitself is NOT detected as a Macro - ✅ All tests pass
- Test verifies that
Before:
export const closure = createMacro((input) => { ... });Was parsed as Declaration::VarInit(CallExpr) - a regular variable with a function call.
After:
export const closure = createMacro((input) => { ... });Is now parsed as Declaration::Macro(ArrowExpr) - the bundler knows this is a compile-time macro.
✅ Successfully detected 'closure' as a Macro!
✅ createMacro correctly identified as regular function
See STEP2_IMPLEMENTATION.md for full details.
-
Added Closure type (
src/execution_request/closure.rs)Closurestruct withexpression: Exprandreferences: HashMap<String, FuneeIdentifier>- Represents a captured expression with its out-of-scope references
-
Added Declaration::ClosureValue variant (
src/execution_request/declaration.rs)- New variant to represent captured closures
- Added to reference handling (
get_references_from_declaration.rs) - Emits as expression (TODO: emit proper Closure construction)
-
Implemented macro call detection (
src/execution_request/detect_macro_calls.rs)MacroCallstruct captures macro name and argumentsMacroCallFindervisitor walks AST to find macro callsfind_macro_calls()function detects calls where callee is a known macro
-
Implemented closure capture logic (
src/execution_request/capture_closure.rs)capture_closure()takes an expression and scope references- Analyzes expression to find all variable references
- Filters to only out-of-scope references
- Returns
Closurewith expression + reference map
-
Two-pass graph construction (
src/execution_request/source_graph.rs)- Pass 1: Build graph normally, track macro definitions
- Pass 2:
process_macro_calls()finds macro calls and captures arguments - For each macro call argument, creates a
ClosureValuenode in the graph
When code like const addClosure = closure(add) is processed:
- Graph construction identifies
closureas a macro - Second pass detects the call
closure(add) - Argument
addis captured withcapture_closure() - Creates
Closure { expression: Ident("add"), references: {"add": ...} } - Adds a
ClosureValuenode to the graph
✅ test_macro_call_argument_captured_as_closure - PASS
✅ Closure correctly captures identifier and references
✅ All 8 tests passing
const add = (a, b) => a + b;
const addClosure = closure(add); // Should capture add's ASTExpected result:
addClosureis a Closure objectaddClosure.expressioncontains the arrow function ASTaddClosure.referencesis empty (no external refs)
const multiplier = 2;
const mult = (x) => x * multiplier;
const multClosure = closure(mult); // Should capture mult's AST + multiplier refExpected result:
multClosure.expressioncontains the arrow function ASTmultClosure.referencescontains{ multiplier: { uri: "...", name: "multiplier" } }
const inner = closure((x) => x + 1);
const outer = closure(inner); // Macro result as input to another macro-
How to execute macros at bundle time?
- Option A: Embed Deno runtime and execute in a sandbox
- Option B: Convert to a simpler representation we can evaluate
- Option C: Use quickjs or similar lightweight JS engine
-
How to handle async macros?
- The example macro returns a Closure synchronously
- Do we need to support async macro functions?
-
Error handling
- What if the macro function throws an error?
- How do we show helpful error messages with source locations?
-
Macro composition
- Can macros call other macros?
- How do we handle circular macro dependencies?
Execute macro functions at bundle time with captured Closures and transform the code.
When a macro call like closure(add) is processed:
- Get the macro function from
Declaration::Macro - Get the captured
ClosureValuearguments - Execute the macro function (in JS runtime) with Closures as arguments
- Get back a transformed Closure result
- Replace the macro call expression with the result
-
JS Execution at Bundle Time
- Need to execute user's macro functions during Rust bundling
- Options:
- Embed deno_core runtime
- Use quickjs
- Two-pass bundling (bundle → execute → bundle)
-
AST Serialization
- Need to pass Closure objects (Rust AST) to JS macro functions
- Need to receive transformed Closure results back
- Options:
- Serialize AST to JSON
- Emit to code string, parse in JS, emit back
-
Macro Result Integration
- Replace macro call node with result expression
- Merge result's references into calling scope
- Handle reference conflicts (IIFE wrapping)
// Simple identity macro
const add = (a, b) => a + b;
const addClosure = closure(add);
// After macro execution: addClosure should be the Closure object
// containing add's AST
// Macro that transforms the expression
const traced = trace((x) => x + 1);
// After macro execution: traced should be transformed code
// e.g., (x) => { console.log('calling'); return x + 1; }- ✅ Macro declarations are detected
- ✅ Bundler knows which functions are macros
- ✅ Macro calls ARE detected
- ✅ Arguments are captured as Closures (not bundled normally)
- ⏳ Macros are NOT yet executed at bundle time
- ⏳ Closure objects are emitted as plain expressions (need proper Closure construction)
- ⏳ Macro transformations are not applied
Implement macro execution with embedded JS runtime
Priority tasks:
- Set up deno_core or similar JS runtime for macro execution
- Implement AST serialization (Rust ↔ JS)
- Execute macro functions with Closure arguments
- Replace macro call sites with transformed results
- Handle reference conflicts and merging
See DESIGN-MACROS.md for detailed design of macro execution phase.