https://github.com/akkartik/mu/blob/main/shell/macroexpand.mu
  1 fn macroexpand expr-ah: (addr handle cell), globals: (addr global-table), trace: (addr trace) {
  2 +-- 15 lines: # trace "macroexpand " expr-ah --------------------------------------------------------------------------------------------------------------------------------------------
 17   trace-lower trace
 18 #?   clear-screen 0
 19 #?   set-cursor-position 0, 0x20 0x20
 20   # loop until convergence
 21   {
 22     var error?/eax: boolean <- has-errors? trace
 23     compare error?, 0/false
 24     break-if-!=
 25     var expanded?/eax: boolean <- macroexpand-iter expr-ah, globals, trace
 26     compare expanded?, 0/false
 27     loop-if-!=
 28   }
 29   trace-higher trace
 30 +-- 15 lines: # trace "=> " expr-ah -----------------------------------------------------------------------------------------------------------------------------------------------------
 45 }
 46 
 47 # return true if we found any macros
 48 fn macroexpand-iter _expr-ah: (addr handle cell), globals: (addr global-table), trace: (addr trace) -> _/eax: boolean {
 49   var expr-ah/esi: (addr handle cell) <- copy _expr-ah
 50   {
 51     compare expr-ah, 0
 52     break-if-!=
 53     abort "macroexpand-iter: NULL expr-ah"
 54   }
 55 +-- 15 lines: # trace "macroexpand-iter " expr ------------------------------------------------------------------------------------------------------------------------------------------
 70   trace-lower trace
 71   debug-print "a", 7/fg, 0/bg
 72   # if expr is a non-pair, return
 73   var expr/eax: (addr cell) <- lookup *expr-ah
 74   {
 75     compare expr, 0
 76     break-if-!=
 77     abort "macroexpand-iter: NULL expr"
 78   }
 79   {
 80     var nil?/eax: boolean <- nil? expr
 81     compare nil?, 0/false
 82     break-if-=
 83     # nil is a literal
 84     trace-text trace, "mac", "nil"
 85     trace-higher trace
 86     return 0/false
 87   }
 88   debug-print "b", 7/fg, 0/bg
 89   {
 90     var expr-type/eax: (addr int) <- get expr, type
 91     compare *expr-type, 0/pair
 92     break-if-=
 93     # non-pairs are literals
 94     trace-text trace, "mac", "non-pair"
 95     trace-higher trace
 96     return 0/false
 97   }
 98   debug-print "c", 7/fg, 0/bg
 99   # if expr is a literal pair, return
100   var first-ah/ebx: (addr handle cell) <- get expr, left
101   var rest-ah/ecx: (addr handle cell) <- get expr, right
102   var first/eax: (addr cell) <- lookup *first-ah
103   {
104     var litfn?/eax: boolean <- litfn? first
105     compare litfn?, 0/false
106     break-if-=
107     # litfn is a literal
108     trace-text trace, "mac", "literal function"
109     trace-higher trace
110     return 0/false
111   }
112   debug-print "d", 7/fg, 0/bg
113   {
114     var litmac?/eax: boolean <- litmac? first
115     compare litmac?, 0/false
116     break-if-=
117     # litmac is a literal
118     trace-text trace, "mac", "literal macro"
119     trace-higher trace
120     return 0/false
121   }
122   debug-print "e", 7/fg, 0/bg
123   {
124     var litimg?/eax: boolean <- litimg? first
125     compare litimg?, 0/false
126     break-if-=
127     # litimg is a literal
128     trace-text trace, "mac", "literal image"
129     trace-higher trace
130     return 0/false
131   }
132   debug-print "f", 7/fg, 0/bg
133   var result/edi: boolean <- copy 0/false
134   # for each builtin, expand only what will later be evaluated
135   $macroexpand-iter:anonymous-function: {
136     var fn?/eax: boolean <- fn? first
137     compare fn?, 0/false
138     break-if-=
139     # fn: expand every expression in the body
140     trace-text trace, "mac", "anonymous function"
141     # skip parameters
142     var rest/eax: (addr cell) <- lookup *rest-ah
143     {
144       rest-ah <- get rest, right
145       rest <- lookup *rest-ah
146       {
147         var done?/eax: boolean <- nil? rest
148         compare done?, 0/false
149       }
150       break-if-!=
151       var curr-ah/eax: (addr handle cell) <- get rest, left
152       var macro-found?/eax: boolean <- macroexpand-iter curr-ah, globals, trace
153       result <- or macro-found?
154       {
155         var error?/eax: boolean <- has-errors? trace
156         compare error?, 0/false
157         break-if-=
158         trace-higher trace
159         return result
160       }
161       loop
162     }
163     trace-higher trace
164 +-- 15 lines: # trace "fn=> " _expr-ah --------------------------------------------------------------------------------------------------------------------------------------------------
179     return result
180   }
181   debug-print "g", 7/fg, 0/bg
182   # builtins with "special" evaluation rules
183   $macroexpand-iter:quote: {
184     # trees starting with single quote create literals
185     var quote?/eax: boolean <- symbol-equal? first, "'"
186     compare quote?, 0/false
187     break-if-=
188     #
189     trace-text trace, "mac", "quote"
190     trace-higher trace
191     return 0/false
192   }
193   debug-print "h", 7/fg, 0/bg
194   $macroexpand-iter:backquote: {
195     # nested backquote not supported for now
196     var backquote?/eax: boolean <- symbol-equal? first, "`"
197     compare backquote?, 0/false
198     break-if-=
199     #
200 #?     set-cursor-position 0/screen, 0x40/x 0x10/y
201 #?     dump-cell-from-cursor-over-full-screen rest-ah
202     var double-unquote-found?/eax: boolean <- look-for-double-unquote rest-ah
203     compare double-unquote-found?, 0/false
204     {
205       break-if-=
206       error trace, "double unquote not supported yet"
207     }
208     trace-higher trace
209     return 0/false
210   }
211   $macroexpand-iter:unquote: {
212     # nested backquote not supported for now
213     var unquote?/eax: boolean <- symbol-equal? first, ","
214     compare unquote?, 0/false
215     break-if-=
216     error trace, "unquote (,) must be within backquote (`)"
217     return 0/false
218   }
219   $macroexpand-iter:unquote-splice: {
220     # nested backquote not supported for now
221     var unquote-splice?/eax: boolean <- symbol-equal? first, ",@"
222     compare unquote-splice?, 0/false
223     break-if-=
224     error trace, "unquote (,@) must be within backquote (`)"
225     return 0/false
226   }
227   debug-print "i", 7/fg, 0/bg
228   $macroexpand-iter:define: {
229     # trees starting with "define" define globals
230     var define?/eax: boolean <- symbol-equal? first, "define"
231     compare define?, 0/false
232     break-if-=
233     #
234     trace-text trace, "mac", "define"
235     var rest/eax: (addr cell) <- lookup *rest-ah
236     rest-ah <- get rest, right  # skip name
237     rest <- lookup *rest-ah
238     var val-ah/edx: (addr handle cell) <- get rest, left
239     var macro-found?/eax: boolean <- macroexpand-iter val-ah, globals, trace
240     trace-higher trace
241 +-- 15 lines: # trace "define=> " _expr-ah ----------------------------------------------------------------------------------------------------------------------------------------------
256     return macro-found?
257   }
258   debug-print "j", 7/fg, 0/bg
259   $macroexpand-iter:set: {
260     # trees starting with "set" mutate bindings
261     var set?/eax: boolean <- symbol-equal? first, "set"
262     compare set?, 0/false
263     break-if-=
264     #
265     trace-text trace, "mac", "set"
266     var rest/eax: (addr cell) <- lookup *rest-ah
267     rest-ah <- get rest, right  # skip name
268     rest <- lookup *rest-ah
269     var val-ah/edx: (addr handle cell) <- get rest, left
270     var macro-found?/eax: boolean <- macroexpand-iter val-ah, globals, trace
271     trace-higher trace
272 +-- 15 lines: # trace "set=> " _expr-ah -------------------------------------------------------------------------------------------------------------------------------------------------
287     return macro-found?
288   }
289   debug-print "k", 7/fg, 0/bg
290   # 'and' is like a function for macroexpansion purposes
291   # 'or' is like a function for macroexpansion purposes
292   # 'if' is like a function for macroexpansion purposes
293   # 'while' is like a function for macroexpansion purposes
294   # if car(expr) is a symbol defined as a macro, expand it
295   {
296     var definition-h: (handle cell)
297     var definition-ah/edx: (addr handle cell) <- address definition-h
298     maybe-lookup-symbol-in-globals first, definition-ah, globals, trace
299     var definition/eax: (addr cell) <- lookup *definition-ah
300     compare definition, 0
301     break-if-=
302     # definition found
303     {
304       var definition-type/eax: (addr int) <- get definition, type
305       compare *definition-type, 0/pair
306     }
307     break-if-!=
308     # definition is a pair
309     {
310       var definition-car-ah/eax: (addr handle cell) <- get definition, left
311       var definition-car/eax: (addr cell) <- lookup *definition-car-ah
312       var macro?/eax: boolean <- litmac? definition-car
313       compare macro?, 0/false
314     }
315     break-if-=
316     # definition is a macro
317     var macro-definition-ah/eax: (addr handle cell) <- get definition, right
318     # TODO: check car(macro-definition) is litfn
319 #?     turn-on-debug-print
320     apply macro-definition-ah, rest-ah, expr-ah, globals, trace, 0/no-screen, 0/no-keyboard, 0/definitions-created, 0/call-number
321     trace-higher trace
322 +-- 15 lines: # trace "1=> " _expr-ah ---------------------------------------------------------------------------------------------------------------------------------------------------
337     return 1/true
338   }
339   # no macro found; process any macros within args
340   trace-text trace, "mac", "recursing into function definition"
341   var curr-ah/ebx: (addr handle cell) <- copy first-ah
342   $macroexpand-iter:loop: {
343     debug-print "l", 7/fg, 0/bg
344 #?     clear-screen 0/screen
345 #?     dump-trace trace
346     {
347       var foo/eax: (addr cell) <- lookup *curr-ah
348       compare foo, 0
349       break-if-!=
350       abort "macroexpand-iter: NULL in loop"
351     }
352     var macro-found?/eax: boolean <- macroexpand-iter curr-ah, globals, trace
353     result <- or macro-found?
354     var error?/eax: boolean <- has-errors? trace
355     compare error?, 0/false
356     break-if-!=
357     var rest/eax: (addr cell) <- lookup *rest-ah
358     {
359       var nil?/eax: boolean <- nil? rest
360       compare nil?, 0/false
361     }
362     break-if-!=
363     curr-ah <- get rest, left
364     rest-ah <- get rest, right
365     loop
366   }
367   trace-higher trace
368 +-- 15 lines: # trace "=> " _expr-ah ----------------------------------------------------------------------------------------------------------------------------------------------------
383   return result
384 }
385 
386 fn look-for-double-unquote _expr-ah: (addr handle cell) -> _/eax: boolean {
387   # if expr is a non-pair, return false
388   var expr-ah/eax: (addr handle cell) <- copy _expr-ah
389   var expr/eax: (addr cell) <- lookup *expr-ah
390   {
391     var nil?/eax: boolean <- nil? expr
392     compare nil?, 0/false
393     break-if-=
394     return 0/false
395   }
396   {
397     var expr-type/eax: (addr int) <- get expr, type
398     compare *expr-type, 0/pair
399     break-if-=
400     return 0/false
401   }
402   var cdr-ah/ecx: (addr handle cell) <- get expr, right
403   var car-ah/ebx: (addr handle cell) <- get expr, left
404   var car/eax: (addr cell) <- lookup *car-ah
405   # if car is unquote or unquote-splice, check if cadr is unquote or
406   # unquote-splice.
407   $look-for-double-unquote:check: {
408     # if car is not an unquote, break
409     {
410       {
411         var unquote?/eax: boolean <- symbol-equal? car, ","
412         compare unquote?, 0/false
413       }
414       break-if-!=
415       var unquote-splice?/eax: boolean <- symbol-equal? car, ",@"
416       compare unquote-splice?, 0/false
417       break-if-!=
418       break $look-for-double-unquote:check
419     }
420     # if cdr is not a pair, break
421     var cdr/eax: (addr cell) <- lookup *cdr-ah
422     var cdr-type/ecx: (addr int) <- get cdr, type
423     compare *cdr-type, 0/pair
424     break-if-!=
425     # if cadr is not an unquote, break
426     var cadr-ah/eax: (addr handle cell) <- get cdr, left
427     var cadr/eax: (addr cell) <- lookup *cadr-ah
428     {
429       {
430         var unquote?/eax: boolean <- symbol-equal? cadr, ","
431         compare unquote?, 0/false
432       }
433       break-if-!=
434       var unquote-splice?/eax: boolean <- symbol-equal? cadr, ",@"
435       compare unquote-splice?, 0/false
436       break-if-!=
437       break $look-for-double-unquote:check
438     }
439     # error
440     return 1/true
441   }
442   var result/eax: boolean <- look-for-double-unquote car-ah
443   compare result, 0/false
444   {
445     break-if-=
446     return result
447   }
448   result <- look-for-double-unquote cdr-ah
449   return result
450 }
451 
452 fn test-macroexpand {
453   var globals-storage: global-table
454   var globals/edx: (addr global-table) <- address globals-storage
455   initialize-globals globals
456   # new macro: m
457   var sandbox-storage: sandbox
458   var sandbox/esi: (addr sandbox) <- address sandbox-storage
459   initialize-sandbox-with sandbox, "(define m (litmac litfn () (a b) `(+ ,a ,b)))"
460   edit-sandbox sandbox, 0x13/ctrl-s, globals, 0/no-disk
461   # invoke macro
462   initialize-sandbox-with sandbox, "(m 3 4)"
463   var gap-ah/ecx: (addr handle gap-buffer) <- get sandbox, data
464   var gap/eax: (addr gap-buffer) <- lookup *gap-ah
465   var result-h: (handle cell)
466   var result-ah/ebx: (addr handle cell) <- address result-h
467   var trace-storage: trace
468   var trace/ecx: (addr trace) <- address trace-storage
469   initialize-trace trace, 1/only-errors, 0x10/capacity, 0/visible
470   read-cell gap, result-ah, trace
471   var dummy/eax: boolean <- macroexpand-iter result-ah, globals, trace
472   var error?/eax: boolean <- has-errors? trace
473   check-not error?, "F - test-macroexpand/error"
474 #?   dump-cell-from-cursor-over-full-screen result-ah, 4/fg 0/bg
475   var _result/eax: (addr cell) <- lookup *result-ah
476   var result/edi: (addr cell) <- copy _result
477   # expected
478   initialize-sandbox-with sandbox, "(+ 3 4)"
479   var expected-gap-ah/edx: (addr handle gap-buffer) <- get sandbox, data
480   var expected-gap/eax: (addr gap-buffer) <- lookup *expected-gap-ah
481   var expected-h: (handle cell)
482   var expected-ah/edx: (addr handle cell) <- address expected-h
483   read-cell expected-gap, expected-ah, trace
484 #?   dump-cell-from-cursor-over-full-screen expected-ah
485   var expected/eax: (addr cell) <- lookup *expected-ah
486   #
487   var assertion/eax: boolean <- cell-isomorphic? result, expected, trace
488   check assertion, "F - test-macroexpand"
489 }
490 
491 fn test-macroexpand-inside-anonymous-fn {
492   var globals-storage: global-table
493   var globals/edx: (addr global-table) <- address globals-storage
494   initialize-globals globals
495   # new macro: m
496   var sandbox-storage: sandbox
497   var sandbox/esi: (addr sandbox) <- address sandbox-storage
498   initialize-sandbox-with sandbox, "(define m (litmac litfn () (a b) `(+ ,a ,b)))"
499   edit-sandbox sandbox, 0x13/ctrl-s, globals, 0/no-disk
500   # invoke macro
501   initialize-sandbox-with sandbox, "(fn() (m 3 4))"
502   var gap-ah/ecx: (addr handle gap-buffer) <- get sandbox, data
503   var gap/eax: (addr gap-buffer) <- lookup *gap-ah
504   var result-h: (handle cell)
505   var result-ah/ebx: (addr handle cell) <- address result-h
506   var trace-storage: trace
507   var trace/ecx: (addr trace) <- address trace-storage
508   initialize-trace trace, 1/only-errors, 0x10/capacity, 0/visible
509   read-cell gap, result-ah, trace
510   var dummy/eax: boolean <- macroexpand-iter result-ah, globals, trace
511   var error?/eax: boolean <- has-errors? trace
512   check-not error?, "F - test-macroexpand-inside-anonymous-fn/error"
513 #?   dump-cell-from-cursor-over-full-screen result-ah
514   var _result/eax: (addr cell) <- lookup *result-ah
515   var result/edi: (addr cell) <- copy _result
516   # expected
517   initialize-sandbox-with sandbox, "(fn() (+ 3 4))"
518   var expected-gap-ah/edx: (addr handle gap-buffer) <- get sandbox, data
519   var expected-gap/eax: (addr gap-buffer) <- lookup *expected-gap-ah
520   var expected-h: (handle cell)
521   var expected-ah/edx: (addr handle cell) <- address expected-h
522   read-cell expected-gap, expected-ah, trace
523   var expected/eax: (addr cell) <- lookup *expected-ah
524   #
525   var assertion/eax: boolean <- cell-isomorphic? result, expected, trace
526   check assertion, "F - test-macroexpand-inside-anonymous-fn"
527 }
528 
529 fn test-macroexpand-inside-fn-call {
530   var globals-storage: global-table
531   var globals/edx: (addr global-table) <- address globals-storage
532   initialize-globals globals
533   # new macro: m
534   var sandbox-storage: sandbox
535   var sandbox/esi: (addr sandbox) <- address sandbox-storage
536   initialize-sandbox-with sandbox, "(define m (litmac litfn () (a b) `(+ ,a ,b)))"
537   edit-sandbox sandbox, 0x13/ctrl-s, globals, 0/no-disk
538   # invoke macro
539   initialize-sandbox-with sandbox, "((fn() (m 3 4)))"
540   var gap-ah/ecx: (addr handle gap-buffer) <- get sandbox, data
541   var gap/eax: (addr gap-buffer) <- lookup *gap-ah
542   var result-h: (handle cell)
543   var result-ah/ebx: (addr handle cell) <- address result-h
544   var trace-storage: trace
545   var trace/ecx: (addr trace) <- address trace-storage
546   initialize-trace trace, 1/only-errors, 0x10/capacity, 0/visible
547   read-cell gap, result-ah, trace
548   var dummy/eax: boolean <- macroexpand-iter result-ah, globals, trace
549   var error?/eax: boolean <- has-errors? trace
550   check-not error?, "F - test-macroexpand-inside-fn-call/error"
551 #?   dump-cell-from-cursor-over-full-screen result-ah
552   var _result/eax: (addr cell) <- lookup *result-ah
553   var result/edi: (addr cell) <- copy _result
554   # expected
555   initialize-sandbox-with sandbox, "((fn() (+ 3 4)))"
556   var expected-gap-ah/edx: (addr handle gap-buffer) <- get sandbox, data
557   var expected-gap/eax: (addr gap-buffer) <- lookup *expected-gap-ah
558   var expected-h: (handle cell)
559   var expected-ah/edx: (addr handle cell) <- address expected-h
560   read-cell expected-gap, expected-ah, trace
561 #?   dump-cell-from-cursor-over-full-screen expected-ah
562   var expected/eax: (addr cell) <- lookup *expected-ah
563   #
564   var assertion/eax: boolean <- cell-isomorphic? result, expected, trace
565   check assertion, "F - test-macroexpand-inside-fn-call"
566 }
567 
568 fn test-macroexpand-repeatedly-with-backquoted-arg {
569   var globals-storage: global-table
570   var globals/edx: (addr global-table) <- address globals-storage
571   initialize-globals globals
572   # macroexpand an expression with a backquote but no macro
573   var sandbox-storage: sandbox
574   var sandbox/esi: (addr sandbox) <- address sandbox-storage
575   initialize-sandbox-with sandbox, "(cons 1 `(3))"
576   var gap-ah/ecx: (addr handle gap-buffer) <- get sandbox, data
577   var gap/eax: (addr gap-buffer) <- lookup *gap-ah
578   var result-h: (handle cell)
579   var result-ah/ebx: (addr handle cell) <- address result-h
580   var trace-storage: trace
581   var trace/ecx: (addr trace) <- address trace-storage
582   initialize-trace trace, 1/only-errors, 0x10/capacity, 0/visible
583   read-cell gap, result-ah, trace
584   var dummy/eax: boolean <- macroexpand-iter result-ah, globals, trace
585   var error?/eax: boolean <- has-errors? trace
586   check-not error?, "F - test-macroexpand-repeatedly-with-backquoted-arg"
587   {
588     compare error?, 0/false
589     break-if-=
590     # we need space to display traces, so just stop rendering future tests on failure here
591     dump-trace trace
592     {
593       loop
594     }
595   }
596 }
597 
598 fn pending-test-macroexpand-inside-backquote-unquote {
599   var globals-storage: global-table
600   var globals/edx: (addr global-table) <- address globals-storage
601   initialize-globals globals
602   # new macro: m
603   var sandbox-storage: sandbox
604   var sandbox/esi: (addr sandbox) <- address sandbox-storage
605   initialize-sandbox-with sandbox, "(define m (litmac litfn () (a b) `(+ ,a ,b)))"
606   edit-sandbox sandbox, 0x13/ctrl-s, globals, 0/no-disk
607   # invoke macro
608   initialize-sandbox-with sandbox, "`(print [result is ] ,(m 3 4)))"
609   var gap-ah/ecx: (addr handle gap-buffer) <- get sandbox, data
610   var gap/eax: (addr gap-buffer) <- lookup *gap-ah
611   var result-h: (handle cell)
612   var result-ah/ebx: (addr handle cell) <- address result-h
613   var trace-storage: trace
614   var trace/ecx: (addr trace) <- address trace-storage
615   initialize-trace trace, 1/only-errors, 0x10/capacity, 0/visible
616   read-cell gap, result-ah, trace
617   var dummy/eax: boolean <- macroexpand-iter result-ah, globals, trace
618   var error?/eax: boolean <- has-errors? trace
619   check-not error?, "F - test-macroexpand-inside-backquote-unquote/error"
620 #?   dump-cell-from-cursor-over-full-screen result-ah
621   var _result/eax: (addr cell) <- lookup *result-ah
622   var result/edi: (addr cell) <- copy _result
623   # expected
624   initialize-sandbox-with sandbox, "`(print [result is ] ,(+ 3 4)))"
625   var expected-gap-ah/edx: (addr handle gap-buffer) <- get sandbox, data
626   var expected-gap/eax: (addr gap-buffer) <- lookup *expected-gap-ah
627   var expected-h: (handle cell)
628   var expected-ah/edx: (addr handle cell) <- address expected-h
629   read-cell expected-gap, expected-ah, trace
630   var expected/eax: (addr cell) <- lookup *expected-ah
631   #
632   var assertion/eax: boolean <- cell-isomorphic? result, expected, trace
633   check assertion, "F - test-macroexpand-inside-backquote-unquote"
634 }
635 
636 fn pending-test-macroexpand-inside-nested-backquote-unquote {
637   var globals-storage: global-table
638   var globals/edx: (addr global-table) <- address globals-storage
639   initialize-globals globals
640   # new macro: m
641   var sandbox-storage: sandbox
642   var sandbox/esi: (addr sandbox) <- address sandbox-storage
643   initialize-sandbox-with sandbox, "(define m (litmac litfn () (a b) `(+ ,a ,b)))"
644   edit-sandbox sandbox, 0x13/ctrl-s, globals, 0/no-disk
645   # invoke macro
646   initialize-sandbox-with sandbox, "`(a ,(m 3 4) `(b ,(m 3 4) ,,(m 3 4)))"
647   var gap-ah/ecx: (addr handle gap-buffer) <- get sandbox, data
648   var gap/eax: (addr gap-buffer) <- lookup *gap-ah
649   var result-h: (handle cell)
650   var result-ah/ebx: (addr handle cell) <- address result-h
651   var trace-storage: trace
652   var trace/ecx: (addr trace) <- address trace-storage
653   initialize-trace trace, 1/only-errors, 0x10/capacity, 0/visible
654   read-cell gap, result-ah, trace
655   var dummy/eax: boolean <- macroexpand-iter result-ah, globals, trace
656   var error?/eax: boolean <- has-errors? trace
657   check-not error?, "F - test-macroexpand-inside-nested-backquote-unquote/error"
658 #?   dump-cell-from-cursor-over-full-screen result-ah
659   var _result/eax: (addr cell) <- lookup *result-ah
660   var result/edi: (addr cell) <- copy _result
661   # expected
662   initialize-sandbox-with sandbox, "`(a ,(+ 3 4) `(b ,(m 3 4) ,,(+ 3 4)))"
663   var expected-gap-ah/edx: (addr handle gap-buffer) <- get sandbox, data
664   var expected-gap/eax: (addr gap-buffer) <- lookup *expected-gap-ah
665   var expected-h: (handle cell)
666   var expected-ah/edx: (addr handle cell) <- address expected-h
667   read-cell expected-gap, expected-ah, trace
668 #?   dump-cell-from-cursor-over-full-screen expected-ah
669   var expected/eax: (addr cell) <- lookup *expected-ah
670   #
671   var assertion/eax: boolean <- cell-isomorphic? result, expected, trace
672   check assertion, "F - test-macroexpand-inside-nested-backquote-unquote"
673 }
674 
675 # TODO: unquote-splice, nested and unnested