@@ -281,11 +281,15 @@ class AnalyzerContext:
281
281
"_is_quoted" ,
282
282
"_opts" ,
283
283
"_recur_points" ,
284
+ "_should_macroexpand" ,
284
285
"_st" ,
285
286
)
286
287
287
288
def __init__ (
288
- self , filename : Optional [str ] = None , opts : Optional [Mapping [str , bool ]] = None
289
+ self ,
290
+ filename : Optional [str ] = None ,
291
+ opts : Optional [Mapping [str , bool ]] = None ,
292
+ should_macroexpand : bool = True ,
289
293
) -> None :
290
294
self ._filename = Maybe (filename ).or_else_get (DEFAULT_COMPILER_FILE_PATH )
291
295
self ._func_ctx : Deque [bool ] = collections .deque ([])
@@ -294,6 +298,7 @@ def __init__(
294
298
Maybe (opts ).map (lmap .map ).or_else_get (lmap .Map .empty ()) # type: ignore
295
299
)
296
300
self ._recur_points : Deque [RecurPoint ] = collections .deque ([])
301
+ self ._should_macroexpand = should_macroexpand
297
302
self ._st = collections .deque ([SymbolTable ("<Top>" )])
298
303
299
304
@property
@@ -339,6 +344,10 @@ def quoted(self):
339
344
yield
340
345
self ._is_quoted .pop ()
341
346
347
+ @property
348
+ def should_macroexpand (self ) -> bool :
349
+ return self ._should_macroexpand
350
+
342
351
@property
343
352
def is_async_ctx (self ) -> bool :
344
353
try :
@@ -1614,25 +1623,26 @@ def _invoke_ast(ctx: AnalyzerContext, form: Union[llist.List, ISeq]) -> Node:
1614
1623
1615
1624
if fn .op == NodeOp .VAR and isinstance (fn , VarRef ):
1616
1625
if _is_macro (fn .var ):
1617
- try :
1618
- macro_env = ctx .symbol_table .as_env_map ()
1619
- expanded = fn .var .value (macro_env , form , * form .rest )
1620
- expanded_ast = _analyze_form (ctx , expanded )
1621
-
1622
- # Verify that macroexpanded code also does not have any
1623
- # non-tail recur forms
1624
- if ctx .recur_point is not None :
1625
- _assert_recur_is_tail (expanded_ast )
1626
-
1627
- return expanded_ast .assoc (
1628
- raw_forms = cast (vec .Vector , expanded_ast .raw_forms ).cons (form )
1629
- )
1630
- except Exception as e :
1631
- raise CompilerException (
1632
- "error occurred during macroexpansion" ,
1633
- form = form ,
1634
- phase = CompilerPhase .MACROEXPANSION ,
1635
- ) from e
1626
+ if ctx .should_macroexpand :
1627
+ try :
1628
+ macro_env = ctx .symbol_table .as_env_map ()
1629
+ expanded = fn .var .value (macro_env , form , * form .rest )
1630
+ expanded_ast = _analyze_form (ctx , expanded )
1631
+
1632
+ # Verify that macroexpanded code also does not have any
1633
+ # non-tail recur forms
1634
+ if ctx .recur_point is not None :
1635
+ _assert_recur_is_tail (expanded_ast )
1636
+
1637
+ return expanded_ast .assoc (
1638
+ raw_forms = cast (vec .Vector , expanded_ast .raw_forms ).cons (form )
1639
+ )
1640
+ except Exception as e :
1641
+ raise CompilerException (
1642
+ "error occurred during macroexpansion" ,
1643
+ form = form ,
1644
+ phase = CompilerPhase .MACROEXPANSION ,
1645
+ ) from e
1636
1646
1637
1647
return Invoke (
1638
1648
form = form ,
@@ -2458,3 +2468,27 @@ def analyze_form(ctx: AnalyzerContext, form: ReaderForm) -> Node:
2458
2468
"""Take a Lisp form as an argument and produce a Basilisp syntax
2459
2469
tree matching the clojure.tools.analyzer AST spec."""
2460
2470
return _analyze_form (ctx , form ).assoc (top_level = True )
2471
+
2472
+
2473
+ def macroexpand_1 (form : ReaderForm ) -> ReaderForm :
2474
+ """Macroexpand form one time. Returns the macroexpanded form. The return
2475
+ value may still represent a macro. Does not macroexpand child forms."""
2476
+ ctx = AnalyzerContext ("<Macroexpand>" , should_macroexpand = False )
2477
+ maybe_macro = analyze_form (ctx , form )
2478
+ if maybe_macro .op == NodeOp .INVOKE :
2479
+ assert isinstance (maybe_macro , Invoke )
2480
+
2481
+ fn = maybe_macro .fn
2482
+ if fn .op == NodeOp .VAR and isinstance (fn , VarRef ):
2483
+ if _is_macro (fn .var ):
2484
+ assert isinstance (form , ISeq )
2485
+ macro_env = ctx .symbol_table .as_env_map ()
2486
+ return fn .var .value (macro_env , form , * form .rest )
2487
+ return maybe_macro .form
2488
+
2489
+
2490
+ def macroexpand (form : ReaderForm ) -> ReaderForm :
2491
+ """Repeatedly macroexpand form as by macroexpand-1 until form no longer
2492
+ represents a macro. Returns the expanded form. Does not macroexpand child
2493
+ forms."""
2494
+ return analyze_form (AnalyzerContext ("<Macroexpand>" ), form ).form
0 commit comments