about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2021-05-06 21:24:34 -0700
committerKartik K. Agaram <vc@akkartik.com>2021-05-06 21:38:02 -0700
commita1cfadc749e91abde00e56859b3d5122205a6ca4 (patch)
treedc8267cfc041b0ba5c4823ba79c3243a87630538
parentf882130c863d7ed05483a7ecc5e31a399aca241b (diff)
downloadmu-a1cfadc749e91abde00e56859b3d5122205a6ca4.tar.gz
first passing test for macroexpand
In the process I spent a long time tracking down a stray TODO in 108write.subx
that I thought would abort but didn't since the switch to baremetal.

Then after I reintroduced that assertion I had to go track down a bunch
of buffer sizes. Stream sizes continue to be a huge mess.
-rw-r--r--108write.subx10
-rw-r--r--shell/gap-buffer.mu4
-rw-r--r--shell/macroexpand.mu175
-rw-r--r--shell/parse.mu16
-rw-r--r--shell/tokenize.mu52
-rw-r--r--shell/trace.mu33
6 files changed, 249 insertions, 41 deletions
diff --git a/108write.subx b/108write.subx
index 43e9bf9b..88e05668 100644
--- a/108write.subx
+++ b/108write.subx
@@ -194,7 +194,7 @@ $_append-4:loop:
     73/jump-if-addr>=  $_append-4:end/disp8
     # if (out >= outend) abort  # just to catch test failures fast
     39/compare                      3/mod/direct    7/rm32/edi    .           .             .           2/r32/edx   .               .                 # compare edi with edx
-    73/jump-if-addr>=  $_append-4:end/disp8  # TODO: abort
+    0f 83/jump-if-addr>=  $_append-4:abort/disp32
     # *out = *in
     8a/copy-byte                    0/mod/indirect  6/rm32/esi    .           .             .           3/r32/BL    .               .                 # copy byte at *esi to BL
     88/copy-byte                    0/mod/indirect  7/rm32/edi    .           .             .           3/r32/BL    .               .                 # copy byte at BL to *edi
@@ -217,4 +217,12 @@ $_append-4:end:
     5d/pop-to-ebp
     c3/return
 
+$_append-4:abort:
+    (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "_append-4: stream full at " 3 0)  # 3=cyan
+    (draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0 %eax 3 0)
+    {
+      eb/jump loop/disp8
+    }
+    # never gets here
+
 # . . vim:nowrap:textwidth=0
diff --git a/shell/gap-buffer.mu b/shell/gap-buffer.mu
index fe6569d9..8dada84a 100644
--- a/shell/gap-buffer.mu
+++ b/shell/gap-buffer.mu
@@ -35,8 +35,8 @@ fn gap-buffer-capacity _gap: (addr gap-buffer) -> _/ecx: int {
 
 # just for tests
 fn initialize-gap-buffer-with self: (addr gap-buffer), s: (addr array byte) {
-  initialize-gap-buffer self, 0x10/capacity
-  var stream-storage: (stream byte 0x10/capacity)
+  initialize-gap-buffer self, 0x40/capacity
+  var stream-storage: (stream byte 0x40/capacity)
   var stream/ecx: (addr stream byte) <- address stream-storage
   write stream, s
   {
diff --git a/shell/macroexpand.mu b/shell/macroexpand.mu
index 8ab7d8e9..25fe5e0d 100644
--- a/shell/macroexpand.mu
+++ b/shell/macroexpand.mu
@@ -1,25 +1,118 @@
 fn macroexpand expr-ah: (addr handle cell), globals: (addr global-table), trace: (addr trace) {
+  # trace "macroexpand " expr-ah {{{
+  {
+    compare trace, 0
+    break-if-=
+    var stream-storage: (stream byte 0x200)
+    var stream/ecx: (addr stream byte) <- address stream-storage
+    write stream, "macroexpand "
+    print-cell expr-ah, stream, 0/no-trace
+    trace trace, "mac", stream
+  }
+  # }}}
   # loop until convergence
-  var expanded?/eax: boolean <- macroexpand-iter expr-ah, globals, trace
-  compare expanded?, 0/false
-  loop-if-!=
+  {
+    var expanded?/eax: boolean <- macroexpand-iter expr-ah, globals, trace
+    compare expanded?, 0/false
+    loop-if-!=
+  }
+  # trace "=> " expr-ah {{{
+  {
+    compare trace, 0
+    break-if-=
+    var stream-storage: (stream byte 0x200)
+    var stream/ecx: (addr stream byte) <- address stream-storage
+    write stream, "=> "
+    print-cell expr-ah, stream, 0/no-trace
+    trace trace, "mac", stream
+  }
+  # }}}
 }
 
 # return true if we found any macros
 fn macroexpand-iter _expr-ah: (addr handle cell), globals: (addr global-table), trace: (addr trace) -> _/eax: boolean {
-  # if car(expr) is a symbol defined as a macro, expand it
   var expr-ah/esi: (addr handle cell) <- copy _expr-ah
+  # trace "macroexpand-iter " expr {{{
+  {
+    compare trace, 0
+    break-if-=
+    var stream-storage: (stream byte 0x200)
+    var stream/ecx: (addr stream byte) <- address stream-storage
+    write stream, "macroexpand-iter "
+    print-cell expr-ah, stream, 0/no-trace
+    trace trace, "mac", stream
+  }
+  # }}}
+  # if expr is a non-pair, return
   var expr/eax: (addr cell) <- lookup *expr-ah
   {
+    var nil?/eax: boolean <- nil? expr
+    compare nil?, 0/false
+    break-if-=
+    # nil is a literal
+    trace-text trace, "mac", "nil"
+    trace-higher trace
+    return 0/false
+  }
+  {
     var expr-type/eax: (addr int) <- get expr, type
     compare *expr-type, 0/pair
     break-if-=
-    # not a pair
+    # non-pairs are literals
+    trace-text trace, "mac", "non-pair"
+    trace-higher trace
     return 0/false
   }
+  # if expr is a literal pair, return
   var first-ah/ebx: (addr handle cell) <- get expr, left
   var rest-ah/ecx: (addr handle cell) <- get expr, right
   var first/eax: (addr cell) <- lookup *first-ah
+  {
+    var litfn?/eax: boolean <- litfn? first
+    compare litfn?, 0/false
+    break-if-=
+    # litfn is a literal
+    trace-text trace, "mac", "literal function"
+    trace-higher trace
+    return 0/false
+  }
+  {
+    var litmac?/eax: boolean <- litmac? first
+    compare litmac?, 0/false
+    break-if-=
+    # litmac is a literal
+    trace-text trace, "mac", "literal macro"
+    trace-higher trace
+    return 0/false
+  }
+  var result/edi: boolean <- copy 0/false
+  # for each builtin, expand only what will later be evaluated
+  macroexpand-iter:anonymous-function: {
+    var first/eax: (addr cell) <- lookup *first-ah
+    var fn?/eax: boolean <- fn? first
+    compare fn?, 0/false
+    break-if-=
+    # fn: expand every expression in the body
+    trace-text trace, "mac", "anonymous function"
+    # skip parameters
+    var rest/eax: (addr cell) <- lookup *rest-ah
+    {
+      rest-ah <- get rest, right
+      rest <- lookup *rest-ah
+      {
+        var done?/eax: boolean <- nil? rest
+        compare done?, 0/false
+      }
+      break-if-!=
+      var curr-ah/eax: (addr handle cell) <- get rest, left
+      var macro-found?/eax: boolean <- macroexpand-iter curr-ah, globals, trace
+      result <- or macro-found?
+      loop
+    }
+    trace-higher trace
+    return result
+  }
+  # if car(expr) is a symbol defined as a macro, expand it
   var definition-h: (handle cell)
   var definition-ah/ebx: (addr handle cell) <- address definition-h
   maybe-lookup-symbol-in-globals first, definition-ah, globals, trace
@@ -51,3 +144,75 @@ fn macroexpand-iter _expr-ah: (addr handle cell), globals: (addr global-table),
   apply macro-definition-ah, rest-ah, expr-ah, globals, trace, 0/no-screen, 0/no-keyboard, 0/call-number
   return 1/true
 }
+
+fn test-macroexpand {
+  var globals-storage: global-table
+  var globals/edx: (addr global-table) <- address globals-storage
+  initialize-globals globals
+  # new macro: m
+  var sandbox-storage: sandbox
+  var sandbox/esi: (addr sandbox) <- address sandbox-storage
+  initialize-sandbox-with sandbox, "(def m (litmac litfn () (a b) `(+ ,a ,b)))"
+  edit-sandbox sandbox, 0x13/ctrl-s, globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
+  var trace-ah/eax: (addr handle trace) <- get sandbox, trace
+  var trace/eax: (addr trace) <- lookup *trace-ah
+  # invoke macro
+  initialize-sandbox-with sandbox, "(m 3 4)"
+  var gap-ah/ecx: (addr handle gap-buffer) <- get sandbox, data
+  var gap/eax: (addr gap-buffer) <- lookup *gap-ah
+  var result-h: (handle cell)
+  var result-ah/ebx: (addr handle cell) <- address result-h
+  read-cell gap, result-ah, 0/no-trace
+  var dummy/eax: boolean <- macroexpand-iter result-ah, globals, 0/no-trace
+#?   dump-cell-from-cursor-over-full-screen result-ah
+  var _result/eax: (addr cell) <- lookup *result-ah
+  var result/edi: (addr cell) <- copy _result
+  # expected
+  initialize-sandbox-with sandbox, "(+ 3 4)"
+  var expected-gap-ah/ecx: (addr handle gap-buffer) <- get sandbox, data
+  var expected-gap/eax: (addr gap-buffer) <- lookup *expected-gap-ah
+  var expected-h: (handle cell)
+  var expected-ah/ecx: (addr handle cell) <- address expected-h
+  read-cell expected-gap, expected-ah, 0/no-trace
+#?   dump-cell-from-cursor-over-full-screen expected-ah
+  var expected/eax: (addr cell) <- lookup *expected-ah
+  #
+  var assertion/eax: boolean <- cell-isomorphic? result, expected, 0/no-trace
+  check assertion, "F - test-macroexpand"
+}
+
+fn test-macroexpand-inside-anonymous-fn {
+  var globals-storage: global-table
+  var globals/edx: (addr global-table) <- address globals-storage
+  initialize-globals globals
+  # new macro: m
+  var sandbox-storage: sandbox
+  var sandbox/esi: (addr sandbox) <- address sandbox-storage
+  initialize-sandbox-with sandbox, "(def m (litmac litfn () (a b) `(+ ,a ,b)))"
+  edit-sandbox sandbox, 0x13/ctrl-s, globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
+  var trace-ah/eax: (addr handle trace) <- get sandbox, trace
+  var trace/eax: (addr trace) <- lookup *trace-ah
+  # invoke macro
+  initialize-sandbox-with sandbox, "(fn() (m 3 4))"
+  var gap-ah/ecx: (addr handle gap-buffer) <- get sandbox, data
+  var gap/eax: (addr gap-buffer) <- lookup *gap-ah
+  var result-h: (handle cell)
+  var result-ah/ebx: (addr handle cell) <- address result-h
+  read-cell gap, result-ah, 0/no-trace
+  var dummy/eax: boolean <- macroexpand-iter result-ah, globals, 0/no-trace
+#?   dump-cell-from-cursor-over-full-screen result-ah
+  var _result/eax: (addr cell) <- lookup *result-ah
+  var result/edi: (addr cell) <- copy _result
+  # expected
+  initialize-sandbox-with sandbox, "(fn() (+ 3 4))"
+  var expected-gap-ah/ecx: (addr handle gap-buffer) <- get sandbox, data
+  var expected-gap/eax: (addr gap-buffer) <- lookup *expected-gap-ah
+  var expected-h: (handle cell)
+  var expected-ah/ecx: (addr handle cell) <- address expected-h
+  read-cell expected-gap, expected-ah, 0/no-trace
+#?   dump-cell-from-cursor-over-full-screen expected-ah
+  var expected/eax: (addr cell) <- lookup *expected-ah
+  #
+  var assertion/eax: boolean <- cell-isomorphic? result, expected, 0/no-trace
+  check assertion, "F - test-macroexpand-inside-anonymous-fn"
+}
diff --git a/shell/parse.mu b/shell/parse.mu
index 283e1d49..eceebd7d 100644
--- a/shell/parse.mu
+++ b/shell/parse.mu
@@ -28,7 +28,7 @@ fn parse-input tokens: (addr stream cell), out: (addr handle cell), trace: (addr
 #   unmatched close-paren encountered?
 #   dot encountered? (only used internally by recursive calls)
 fn parse-sexpression tokens: (addr stream cell), _out: (addr handle cell), trace: (addr trace) -> _/eax: boolean, _/ecx: boolean {
-  trace-text trace, "read", "parse"
+  trace-text trace, "parse", "parse"
   trace-lower trace
   var curr-token-storage: cell
   var curr-token/ecx: (addr cell) <- address curr-token-storage
@@ -180,7 +180,7 @@ fn parse-sexpression tokens: (addr stream cell), _out: (addr handle cell), trace
       return 1/true, 0/false
     }
     # otherwise abort
-    var stream-storage: (stream byte 0x40)
+    var stream-storage: (stream byte 0x400)
     var stream/edx: (addr stream byte) <- address stream-storage
     write stream, "unexpected token "
     var curr-token-data-ah/eax: (addr handle stream byte) <- get curr-token, text-data
@@ -194,12 +194,12 @@ fn parse-sexpression tokens: (addr stream cell), _out: (addr handle cell), trace
 }
 
 fn parse-atom _curr-token: (addr cell), _out: (addr handle cell), trace: (addr trace) {
-  trace-text trace, "read", "parse atom"
+  trace-text trace, "parse", "parse atom"
   var curr-token/ecx: (addr cell) <- copy _curr-token
   var curr-token-data-ah/eax: (addr handle stream byte) <- get curr-token, text-data
   var _curr-token-data/eax: (addr stream byte) <- lookup *curr-token-data-ah
   var curr-token-data/esi: (addr stream byte) <- copy _curr-token-data
-  trace trace, "read", curr-token-data
+  trace trace, "parse", curr-token-data
   # number
   var number-token?/eax: boolean <- number-token? curr-token
   compare number-token?, 0/false
@@ -215,11 +215,11 @@ fn parse-atom _curr-token: (addr cell), _out: (addr handle cell), trace: (addr t
     var dest/edi: (addr float) <- get out-addr, number-data
     copy-to *dest, val-float
     {
-      var stream-storage: (stream byte 0x40)
+      var stream-storage: (stream byte 0x400)
       var stream/ecx: (addr stream byte) <- address stream-storage
       write stream, "=> number "
       print-number out-addr, stream, 0/no-trace
-      trace trace, "read", stream
+      trace trace, "parse", stream
     }
     return
   }
@@ -243,11 +243,11 @@ fn parse-atom _curr-token: (addr cell), _out: (addr handle cell), trace: (addr t
   var dest-ah/edx: (addr handle stream byte) <- get out-addr, text-data
   copy-object curr-token-data-ah, dest-ah
   {
-    var stream-storage: (stream byte 0x40)
+    var stream-storage: (stream byte 0x400)
     var stream/ecx: (addr stream byte) <- address stream-storage
     write stream, "=> symbol "
     print-symbol out-addr, stream, 0/no-trace
-    trace trace, "read", stream
+    trace trace, "parse", stream
   }
 }
 
diff --git a/shell/tokenize.mu b/shell/tokenize.mu
index 7572ffba..0ec4d57c 100644
--- a/shell/tokenize.mu
+++ b/shell/tokenize.mu
@@ -3,7 +3,7 @@
 # they always have text-data.
 
 fn tokenize in: (addr gap-buffer), out: (addr stream cell), trace: (addr trace) {
-  trace-text trace, "read", "tokenize"
+  trace-text trace, "tokenize", "tokenize"
   trace-lower trace
   rewind-gap-buffer in
   var token-storage: cell
@@ -19,7 +19,9 @@ fn tokenize in: (addr gap-buffer), out: (addr stream cell), trace: (addr trace)
     var dest-ah/eax: (addr handle stream byte) <- get token, text-data
     populate-stream dest-ah, 0x400/max-definition-size
     #
+#?     draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen "c", 7/fg 0/bg
     next-token in, token, trace
+#?     draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen "d", 7/fg 0/bg
     var skip?/eax: boolean <- comment-token? token
     compare skip?, 0/false
     loop-if-!=
@@ -204,7 +206,7 @@ fn test-tokenize-stream-literal-in-tree {
 }
 
 fn next-token in: (addr gap-buffer), _out-cell: (addr cell), trace: (addr trace) {
-  trace-text trace, "read", "next-token"
+  trace-text trace, "tokenize", "next-token"
   trace-lower trace
   var out-cell/eax: (addr cell) <- copy _out-cell
   {
@@ -217,13 +219,15 @@ fn next-token in: (addr gap-buffer), _out-cell: (addr cell), trace: (addr trace)
   $next-token:body: {
     clear-stream out
     var g/eax: grapheme <- peek-from-gap-buffer in
+#?     draw-grapheme-at-cursor 0/screen, g, 7/fg, 0/bg
+#?     move-cursor-rightward-and-downward 0/screen, 0, 0x80
     {
       var stream-storage: (stream byte 0x40)
       var stream/esi: (addr stream byte) <- address stream-storage
       write stream, "next: "
       var gval/eax: int <- copy g
       write-int32-hex stream, gval
-      trace trace, "read", stream
+      trace trace, "tokenize", stream
     }
     # comment
     {
@@ -317,16 +321,16 @@ fn next-token in: (addr gap-buffer), _out-cell: (addr cell), trace: (addr trace)
     }
   }
   trace-higher trace
-  var stream-storage: (stream byte 0x40)
+  var stream-storage: (stream byte 0x400)  # maximum possible token size (next-stream-token)
   var stream/eax: (addr stream byte) <- address stream-storage
   write stream, "=> "
   rewind-stream out
   write-stream stream, out
-  trace trace, "read", stream
+  trace trace, "tokenize", stream
 }
 
 fn next-symbol-token in: (addr gap-buffer), out: (addr stream byte), trace: (addr trace) {
-  trace-text trace, "read", "looking for a symbol"
+  trace-text trace, "tokenize", "looking for a symbol"
   trace-lower trace
   $next-symbol-token:loop: {
     var done?/eax: boolean <- gap-buffer-scan-done? in
@@ -339,14 +343,14 @@ fn next-symbol-token in: (addr gap-buffer), out: (addr stream byte), trace: (add
       write stream, "next: "
       var gval/eax: int <- copy g
       write-int32-hex stream, gval
-      trace trace, "read", stream
+      trace trace, "tokenize", stream
     }
     # if non-symbol, return
     {
       var symbol-grapheme?/eax: boolean <- symbol-grapheme? g
       compare symbol-grapheme?, 0/false
       break-if-!=
-      trace-text trace, "read", "stop"
+      trace-text trace, "tokenize", "stop"
       break $next-symbol-token:loop
     }
     var g/eax: grapheme <- read-from-gap-buffer in
@@ -359,11 +363,11 @@ fn next-symbol-token in: (addr gap-buffer), out: (addr stream byte), trace: (add
   write stream, "=> "
   rewind-stream out
   write-stream stream, out
-  trace trace, "read", stream
+  trace trace, "tokenize", stream
 }
 
 fn next-operator-token in: (addr gap-buffer), out: (addr stream byte), trace: (addr trace) {
-  trace-text trace, "read", "looking for a operator"
+  trace-text trace, "tokenize", "looking for a operator"
   trace-lower trace
   $next-operator-token:loop: {
     var done?/eax: boolean <- gap-buffer-scan-done? in
@@ -376,14 +380,14 @@ fn next-operator-token in: (addr gap-buffer), out: (addr stream byte), trace: (a
       write stream, "next: "
       var gval/eax: int <- copy g
       write-int32-hex stream, gval
-      trace trace, "read", stream
+      trace trace, "tokenize", stream
     }
     # if non-operator, return
     {
       var operator-grapheme?/eax: boolean <- operator-grapheme? g
       compare operator-grapheme?, 0/false
       break-if-!=
-      trace-text trace, "read", "stop"
+      trace-text trace, "tokenize", "stop"
       break $next-operator-token:loop
     }
     var g/eax: grapheme <- read-from-gap-buffer in
@@ -396,11 +400,11 @@ fn next-operator-token in: (addr gap-buffer), out: (addr stream byte), trace: (a
   write stream, "=> "
   rewind-stream out
   write-stream stream, out
-  trace trace, "read", stream
+  trace trace, "tokenize", stream
 }
 
 fn next-number-token in: (addr gap-buffer), out: (addr stream byte), trace: (addr trace) {
-  trace-text trace, "read", "looking for a number"
+  trace-text trace, "tokenize", "looking for a number"
   trace-lower trace
   $next-number-token:loop: {
     var done?/eax: boolean <- gap-buffer-scan-done? in
@@ -413,14 +417,14 @@ fn next-number-token in: (addr gap-buffer), out: (addr stream byte), trace: (add
       write stream, "next: "
       var gval/eax: int <- copy g
       write-int32-hex stream, gval
-      trace trace, "read", stream
+      trace trace, "tokenize", stream
     }
     # if not symbol grapheme, return
     {
       var symbol-grapheme?/eax: boolean <- symbol-grapheme? g
       compare symbol-grapheme?, 0/false
       break-if-!=
-      trace-text trace, "read", "stop"
+      trace-text trace, "tokenize", "stop"
       break $next-number-token:loop
     }
     # if not digit grapheme, abort
@@ -431,7 +435,7 @@ fn next-number-token in: (addr gap-buffer), out: (addr stream byte), trace: (add
       error trace, "invalid number"
       return
     }
-    trace-text trace, "read", "append"
+    trace-text trace, "tokenize", "append"
     var g/eax: grapheme <- read-from-gap-buffer in
     write-grapheme out, g
     loop
@@ -440,7 +444,7 @@ fn next-number-token in: (addr gap-buffer), out: (addr stream byte), trace: (add
 }
 
 fn next-stream-token in: (addr gap-buffer), out: (addr stream byte), trace: (addr trace) {
-  trace-text trace, "read", "stream"
+  trace-text trace, "tokenize", "stream"
   {
     var empty?/eax: boolean <- gap-buffer-scan-done? in
     compare empty?, 0/false
@@ -455,27 +459,27 @@ fn next-stream-token in: (addr gap-buffer), out: (addr stream byte), trace: (add
     write-grapheme out, g
     loop
   }
-  var stream-storage: (stream byte 0x40)
+  var stream-storage: (stream byte 0x400)  # max-definition-size
   var stream/esi: (addr stream byte) <- address stream-storage
   write stream, "=> "
   rewind-stream out
   write-stream stream, out
-  trace trace, "read", stream
+  trace trace, "tokenize", stream
 }
 
 fn next-bracket-token g: grapheme, out: (addr stream byte), trace: (addr trace) {
-  trace-text trace, "read", "bracket"
+  trace-text trace, "tokenize", "bracket"
   write-grapheme out, g
   var stream-storage: (stream byte 0x40)
   var stream/esi: (addr stream byte) <- address stream-storage
   write stream, "=> "
   rewind-stream out
   write-stream stream, out
-  trace trace, "read", stream
+  trace trace, "tokenize", stream
 }
 
 fn rest-of-line in: (addr gap-buffer), out: (addr stream byte), trace: (addr trace) {
-  trace-text trace, "read", "comment"
+  trace-text trace, "tokenize", "comment"
   {
     var empty?/eax: boolean <- gap-buffer-scan-done? in
     compare empty?, 0/false
@@ -494,7 +498,7 @@ fn rest-of-line in: (addr gap-buffer), out: (addr stream byte), trace: (addr tra
   write stream, "=> "
   rewind-stream out
   write-stream stream, out
-  trace trace, "read", stream
+  trace trace, "tokenize", stream
 }
 
 fn symbol-grapheme? g: grapheme -> _/eax: boolean {
diff --git a/shell/trace.mu b/shell/trace.mu
index 12285704..dfe82638 100644
--- a/shell/trace.mu
+++ b/shell/trace.mu
@@ -300,9 +300,40 @@ fn dump-trace _self: (addr trace) {
     $dump-trace:iter: {
       var offset/ebx: (offset trace-line) <- compute-offset trace, i
       var curr/ebx: (addr trace-line) <- index trace, offset
+      y <- render-trace-line 0/screen, curr, 0, y, 0x80/width, 0x30/height, 7/fg, 0/bg
+    }
+    i <- increment
+    loop
+  }
+}
+
+fn dump-trace-with-label _self: (addr trace), label: (addr array byte) {
+  var already-hiding-lines?: boolean
+  var y/ecx: int <- copy 0
+  var self/esi: (addr trace) <- copy _self
+  compare self, 0
+  {
+    break-if-!=
+    return
+  }
+  var trace-ah/eax: (addr handle array trace-line) <- get self, data
+  var _trace/eax: (addr array trace-line) <- lookup *trace-ah
+  var trace/edi: (addr array trace-line) <- copy _trace
+  var i/edx: int <- copy 0
+  var max-addr/ebx: (addr int) <- get self, first-free
+  var max/ebx: int <- copy *max-addr
+  $dump-trace:loop: {
+    compare i, max
+    break-if->=
+    $dump-trace:iter: {
+      var offset/ebx: (offset trace-line) <- compute-offset trace, i
+      var curr/ebx: (addr trace-line) <- index trace, offset
       var curr-label-ah/eax: (addr handle array byte) <- get curr, label
       var curr-label/eax: (addr array byte) <- lookup *curr-label-ah
-      y <- render-trace-line 0/screen, curr, 0, y, 0x80/width, 0x30/height, 7/fg, 0xc5/bg=blue-bg
+      var show?/eax: boolean <- string-equal? curr-label, label
+      compare show?, 0/false
+      break-if-=
+      y <- render-trace-line 0/screen, curr, 0, y, 0x80/width, 0x30/height, 7/fg, 0/bg
     }
     i <- increment
     loop