From 71e4f3812982dba2efb471283d310224e8db363e Mon Sep 17 00:00:00 2001 From: "Kartik K. Agaram" Date: Wed, 3 Mar 2021 22:09:50 -0800 Subject: 7842 - new directory organization Baremetal is now the default build target and therefore has its sources at the top-level. Baremetal programs build using the phase-2 Mu toolchain that requires a Linux kernel. This phase-2 codebase which used to be at the top-level is now under the linux/ directory. Finally, the phase-2 toolchain, while self-hosting, has a way to bootstrap from a C implementation, which is now stored in linux/bootstrap. The bootstrap C implementation uses some literate programming tools that are now in linux/bootstrap/tools. So the whole thing has gotten inverted. Each directory should build one artifact and include the main sources (along with standard library). Tools used for building it are relegated to sub-directories, even though those tools are often useful in their own right, and have had lots of interesting programs written using them. A couple of things have gotten dropped in this process: - I had old ways to run on just a Linux kernel, or with a Soso kernel. No more. - I had some old tooling for running a single test at the cursor. I haven't used that lately. Maybe I'll bring it back one day. The reorg isn't done yet. Still to do: - redo documentation everywhere. All the README files, all other markdown, particularly vocabulary.md. - clean up how-to-run comments at the start of programs everywhere - rethink what to do with the html/ directory. Do we even want to keep supporting it? In spite of these shortcomings, all the scripts at the top-level, linux/ and linux/bootstrap are working. The names of the scripts also feel reasonable. This is a good milestone to take stock at. --- shell/cell.mu | 89 +++ shell/eval.mu | 0 shell/gap-buffer.mu | 781 +++++++++++++++++++++++++ shell/grapheme-stack.mu | 280 +++++++++ shell/main.mu | 22 + shell/parse.mu | 136 +++++ shell/print.mu | 260 +++++++++ shell/read.mu | 15 + shell/sandbox.mu | 263 +++++++++ shell/tokenize.mu | 422 ++++++++++++++ shell/trace.mu | 1449 +++++++++++++++++++++++++++++++++++++++++++++++ shell/vimrc.vim | 2 + 12 files changed, 3719 insertions(+) create mode 100644 shell/cell.mu create mode 100644 shell/eval.mu create mode 100644 shell/gap-buffer.mu create mode 100644 shell/grapheme-stack.mu create mode 100644 shell/main.mu create mode 100644 shell/parse.mu create mode 100644 shell/print.mu create mode 100644 shell/read.mu create mode 100644 shell/sandbox.mu create mode 100644 shell/tokenize.mu create mode 100644 shell/trace.mu create mode 100644 shell/vimrc.vim (limited to 'shell') diff --git a/shell/cell.mu b/shell/cell.mu new file mode 100644 index 00000000..59558fb9 --- /dev/null +++ b/shell/cell.mu @@ -0,0 +1,89 @@ +type cell { + type: int + # type 0: pair + left: (handle cell) + right: (handle cell) + # type 1: number + number-data: float + # type 2: symbol + # type 3: string + text-data: (handle stream byte) + # TODO: array, (associative) table, stream +} + +fn allocate-symbol _out: (addr handle cell) { + var out/eax: (addr handle cell) <- copy _out + allocate out + var out-addr/eax: (addr cell) <- lookup *out + var type/ecx: (addr int) <- get out-addr, type + copy-to *type, 2/symbol + var dest-ah/eax: (addr handle stream byte) <- get out-addr, text-data + populate-stream dest-ah, 0x40/max-symbol-size +} + +fn initialize-symbol _out: (addr handle cell), val: (addr array byte) { + var out/eax: (addr handle cell) <- copy _out + var out-addr/eax: (addr cell) <- lookup *out + var dest-ah/eax: (addr handle stream byte) <- get out-addr, text-data + var dest/eax: (addr stream byte) <- lookup *dest-ah + write dest, val +} + +fn new-symbol out: (addr handle cell), val: (addr array byte) { + allocate-symbol out + initialize-symbol out, val +} + +fn allocate-number _out: (addr handle cell) { + var out/eax: (addr handle cell) <- copy _out + allocate out + var out-addr/eax: (addr cell) <- lookup *out + var type/ecx: (addr int) <- get out-addr, type + copy-to *type, 1/number +} + +fn initialize-integer _out: (addr handle cell), n: int { + var out/eax: (addr handle cell) <- copy _out + var out-addr/eax: (addr cell) <- lookup *out + var dest-ah/eax: (addr float) <- get out-addr, number-data + var src/xmm0: float <- convert n + copy-to *dest-ah, src +} + +fn new-integer out: (addr handle cell), n: int { + allocate-number out + initialize-integer out, n +} + +fn initialize-float _out: (addr handle cell), n: float { + var out/eax: (addr handle cell) <- copy _out + var out-addr/eax: (addr cell) <- lookup *out + var dest-ah/eax: (addr float) <- get out-addr, number-data + var src/xmm0: float <- copy n + copy-to *dest-ah, src +} + +fn new-float out: (addr handle cell), n: float { + allocate-number out + initialize-float out, n +} + +fn allocate-pair _out: (addr handle cell) { + var out/eax: (addr handle cell) <- copy _out + allocate out + # new cells have type pair by default +} + +fn initialize-pair _out: (addr handle cell), left: (handle cell), right: (handle cell) { + var out/eax: (addr handle cell) <- copy _out + var out-addr/eax: (addr cell) <- lookup *out + var dest-ah/ecx: (addr handle cell) <- get out-addr, left + copy-handle left, dest-ah + dest-ah <- get out-addr, right + copy-handle right, dest-ah +} + +fn new-pair out: (addr handle cell), left: (handle cell), right: (handle cell) { + allocate-pair out + initialize-pair out, left, right +} diff --git a/shell/eval.mu b/shell/eval.mu new file mode 100644 index 00000000..e69de29b diff --git a/shell/gap-buffer.mu b/shell/gap-buffer.mu new file mode 100644 index 00000000..81537e9c --- /dev/null +++ b/shell/gap-buffer.mu @@ -0,0 +1,781 @@ +# primitive for editing a single word + +type gap-buffer { + left: grapheme-stack + right: grapheme-stack + # some fields for scanning incrementally through a gap-buffer + left-read-index: int + right-read-index: int +} + +fn initialize-gap-buffer _self: (addr gap-buffer), max-word-size: int { + var self/esi: (addr gap-buffer) <- copy _self + var left/eax: (addr grapheme-stack) <- get self, left + initialize-grapheme-stack left, max-word-size + var right/eax: (addr grapheme-stack) <- get self, right + initialize-grapheme-stack right, max-word-size +} + +# just for tests +fn initialize-gap-buffer-with self: (addr gap-buffer), s: (addr array byte) { + initialize-gap-buffer self, 0x10/max-word-size + var stream-storage: (stream byte 0x10/max-word-size) + var stream/ecx: (addr stream byte) <- address stream-storage + write stream, s + { + var done?/eax: boolean <- stream-empty? stream + compare done?, 0/false + break-if-!= + var g/eax: grapheme <- read-grapheme stream + add-grapheme-at-gap self, g + loop + } +} + +fn emit-gap-buffer _self: (addr gap-buffer), out: (addr stream byte) { + var self/esi: (addr gap-buffer) <- copy _self + clear-stream out + var left/eax: (addr grapheme-stack) <- get self, left + emit-stack-from-bottom left, out + var right/eax: (addr grapheme-stack) <- get self, right + emit-stack-from-top right, out +} + +# dump stack from bottom to top +fn emit-stack-from-bottom _self: (addr grapheme-stack), out: (addr stream byte) { + var self/esi: (addr grapheme-stack) <- copy _self + var data-ah/edi: (addr handle array grapheme) <- get self, data + var _data/eax: (addr array grapheme) <- lookup *data-ah + var data/edi: (addr array grapheme) <- copy _data + var top-addr/ecx: (addr int) <- get self, top + var i/eax: int <- copy 0 + { + compare i, *top-addr + break-if->= + var g/edx: (addr grapheme) <- index data, i + write-grapheme out, *g + i <- increment + loop + } +} + +# dump stack from top to bottom +fn emit-stack-from-top _self: (addr grapheme-stack), out: (addr stream byte) { + var self/esi: (addr grapheme-stack) <- copy _self + var data-ah/edi: (addr handle array grapheme) <- get self, data + var _data/eax: (addr array grapheme) <- lookup *data-ah + var data/edi: (addr array grapheme) <- copy _data + var top-addr/ecx: (addr int) <- get self, top + var i/eax: int <- copy *top-addr + i <- decrement + { + compare i, 0 + break-if-< + var g/edx: (addr grapheme) <- index data, i + write-grapheme out, *g + i <- decrement + loop + } +} + +# We implicitly render everything editable in a single color, and assume the +# cursor is a single other color. +fn render-gap-buffer-wrapping-right-then-down screen: (addr screen), _gap: (addr gap-buffer), xmin: int, ymin: int, xmax: int, ymax: int, render-cursor?: boolean -> _/eax: int, _/ecx: int { + var gap/esi: (addr gap-buffer) <- copy _gap + var left/edx: (addr grapheme-stack) <- get gap, left + var x2/eax: int <- copy 0 + var y2/ecx: int <- copy 0 + x2, y2 <- render-stack-from-bottom-wrapping-right-then-down screen, left, xmin, ymin, xmax, ymax, xmin, ymin + var right/edx: (addr grapheme-stack) <- get gap, right + x2, y2 <- render-stack-from-top-wrapping-right-then-down screen, right, xmin, ymin, xmax, ymax, x2, y2, render-cursor? + # decide whether we still need to print a cursor + var bg/ebx: int <- copy 0 + compare render-cursor?, 0/false + { + break-if-= + # if the right side is empty, grapheme stack didn't print the cursor + var empty?/eax: boolean <- grapheme-stack-empty? right + compare empty?, 0/false + break-if-= + bg <- copy 7/cursor + } + # print a grapheme either way so that cursor position doesn't affect printed width + var space/edx: grapheme <- copy 0x20 + x2, y2 <- render-grapheme screen, space, xmin, ymin, xmax, ymax, x2, y2, 3/fg=cyan, bg + return x2, y2 +} + +fn render-gap-buffer screen: (addr screen), gap: (addr gap-buffer), x: int, y: int, render-cursor?: boolean -> _/eax: int { + var _width/eax: int <- copy 0 + var _height/ecx: int <- copy 0 + _width, _height <- screen-size screen + var width/edx: int <- copy _width + var height/ebx: int <- copy _height + var x2/eax: int <- copy 0 + var y2/ecx: int <- copy 0 + x2, y2 <- render-gap-buffer-wrapping-right-then-down screen, gap, x, y, width, height, render-cursor? + return x2 # y2? yolo +} + +fn gap-buffer-length _gap: (addr gap-buffer) -> _/eax: int { + var gap/esi: (addr gap-buffer) <- copy _gap + var left/eax: (addr grapheme-stack) <- get gap, left + var tmp/eax: (addr int) <- get left, top + var left-length/ecx: int <- copy *tmp + var right/esi: (addr grapheme-stack) <- get gap, right + tmp <- get right, top + var result/eax: int <- copy *tmp + result <- add left-length + return result +} + +fn add-grapheme-at-gap _self: (addr gap-buffer), g: grapheme { + var self/esi: (addr gap-buffer) <- copy _self + var left/eax: (addr grapheme-stack) <- get self, left + push-grapheme-stack left, g +} + +fn gap-to-start self: (addr gap-buffer) { + { + var curr/eax: grapheme <- gap-left self + compare curr, -1 + loop-if-!= + } +} + +fn gap-to-end self: (addr gap-buffer) { + { + var curr/eax: grapheme <- gap-right self + compare curr, -1 + loop-if-!= + } +} + +fn gap-at-start? _self: (addr gap-buffer) -> _/eax: boolean { + var self/esi: (addr gap-buffer) <- copy _self + var left/eax: (addr grapheme-stack) <- get self, left + var result/eax: boolean <- grapheme-stack-empty? left + return result +} + +fn gap-at-end? _self: (addr gap-buffer) -> _/eax: boolean { + var self/esi: (addr gap-buffer) <- copy _self + var right/eax: (addr grapheme-stack) <- get self, right + var result/eax: boolean <- grapheme-stack-empty? right + return result +} + +fn gap-right _self: (addr gap-buffer) -> _/eax: grapheme { + var self/esi: (addr gap-buffer) <- copy _self + var g/eax: grapheme <- copy 0 + var right/ecx: (addr grapheme-stack) <- get self, right + g <- pop-grapheme-stack right + compare g, -1 + { + break-if-= + var left/ecx: (addr grapheme-stack) <- get self, left + push-grapheme-stack left, g + } + return g +} + +fn gap-left _self: (addr gap-buffer) -> _/eax: grapheme { + var self/esi: (addr gap-buffer) <- copy _self + var g/eax: grapheme <- copy 0 + { + var left/ecx: (addr grapheme-stack) <- get self, left + g <- pop-grapheme-stack left + } + compare g, -1 + { + break-if-= + var right/ecx: (addr grapheme-stack) <- get self, right + push-grapheme-stack right, g + } + return g +} + +fn index-of-gap _self: (addr gap-buffer) -> _/eax: int { + var self/eax: (addr gap-buffer) <- copy _self + var left/eax: (addr grapheme-stack) <- get self, left + var top-addr/eax: (addr int) <- get left, top + var result/eax: int <- copy *top-addr + return result +} + +fn first-grapheme-in-gap-buffer _self: (addr gap-buffer) -> _/eax: grapheme { + var self/esi: (addr gap-buffer) <- copy _self + # try to read from left + var left/eax: (addr grapheme-stack) <- get self, left + var top-addr/ecx: (addr int) <- get left, top + compare *top-addr, 0 + { + break-if-<= + var data-ah/eax: (addr handle array grapheme) <- get left, data + var data/eax: (addr array grapheme) <- lookup *data-ah + var result-addr/eax: (addr grapheme) <- index data, 0 + return *result-addr + } + # try to read from right + var right/eax: (addr grapheme-stack) <- get self, right + top-addr <- get right, top + compare *top-addr, 0 + { + break-if-<= + var data-ah/eax: (addr handle array grapheme) <- get right, data + var data/eax: (addr array grapheme) <- lookup *data-ah + var top/ecx: int <- copy *top-addr + top <- decrement + var result-addr/eax: (addr grapheme) <- index data, top + return *result-addr + } + # give up + return -1 +} + +fn grapheme-before-cursor-in-gap-buffer _self: (addr gap-buffer) -> _/eax: grapheme { + var self/esi: (addr gap-buffer) <- copy _self + # try to read from left + var left/ecx: (addr grapheme-stack) <- get self, left + var top-addr/edx: (addr int) <- get left, top + compare *top-addr, 0 + { + break-if-<= + var result/eax: grapheme <- pop-grapheme-stack left + push-grapheme-stack left, result + return result + } + # give up + return -1 +} + +fn delete-before-gap _self: (addr gap-buffer) { + var self/eax: (addr gap-buffer) <- copy _self + var left/eax: (addr grapheme-stack) <- get self, left + var dummy/eax: grapheme <- pop-grapheme-stack left +} + +fn pop-after-gap _self: (addr gap-buffer) -> _/eax: grapheme { + var self/eax: (addr gap-buffer) <- copy _self + var right/eax: (addr grapheme-stack) <- get self, right + var result/eax: grapheme <- pop-grapheme-stack right + return result +} + +fn gap-buffer-equal? _self: (addr gap-buffer), s: (addr array byte) -> _/eax: boolean { + var self/esi: (addr gap-buffer) <- copy _self + # complication: graphemes may be multiple bytes + # so don't rely on length + # instead turn the expected result into a stream and arrange to read from it in order + var stream-storage: (stream byte 0x10/max-word-size) + var expected-stream/ecx: (addr stream byte) <- address stream-storage + write expected-stream, s + # compare left + var left/edx: (addr grapheme-stack) <- get self, left + var result/eax: boolean <- prefix-match? left, expected-stream + compare result, 0/false + { + break-if-!= + return result + } + # compare right + var right/edx: (addr grapheme-stack) <- get self, right + result <- suffix-match? right, expected-stream + compare result, 0/false + { + break-if-!= + return result + } + # ensure there's nothing left over + result <- stream-empty? expected-stream + return result +} + +fn test-gap-buffer-equal-from-end { + var _g: gap-buffer + var g/esi: (addr gap-buffer) <- address _g + initialize-gap-buffer g, 0x10 + # + var c/eax: grapheme <- copy 0x61/a + add-grapheme-at-gap g, c + add-grapheme-at-gap g, c + add-grapheme-at-gap g, c + # gap is at end (right is empty) + var result/eax: boolean <- gap-buffer-equal? g, "aaa" + check result, "F - test-gap-buffer-equal-from-end" +} + +fn test-gap-buffer-equal-from-middle { + var _g: gap-buffer + var g/esi: (addr gap-buffer) <- address _g + initialize-gap-buffer g, 0x10 + # + var c/eax: grapheme <- copy 0x61/a + add-grapheme-at-gap g, c + add-grapheme-at-gap g, c + add-grapheme-at-gap g, c + var dummy/eax: grapheme <- gap-left g + # gap is in the middle + var result/eax: boolean <- gap-buffer-equal? g, "aaa" + check result, "F - test-gap-buffer-equal-from-middle" +} + +fn test-gap-buffer-equal-from-start { + var _g: gap-buffer + var g/esi: (addr gap-buffer) <- address _g + initialize-gap-buffer g, 0x10 + # + var c/eax: grapheme <- copy 0x61/a + add-grapheme-at-gap g, c + add-grapheme-at-gap g, c + add-grapheme-at-gap g, c + var dummy/eax: grapheme <- gap-left g + dummy <- gap-left g + dummy <- gap-left g + # gap is at the start + var result/eax: boolean <- gap-buffer-equal? g, "aaa" + check result, "F - test-gap-buffer-equal-from-start" +} + +fn test-gap-buffer-equal-fails { + # g = "aaa" + var _g: gap-buffer + var g/esi: (addr gap-buffer) <- address _g + initialize-gap-buffer g, 0x10 + var c/eax: grapheme <- copy 0x61/a + add-grapheme-at-gap g, c + add-grapheme-at-gap g, c + add-grapheme-at-gap g, c + # + var result/eax: boolean <- gap-buffer-equal? g, "aa" + check-not result, "F - test-gap-buffer-equal-fails" +} + +fn gap-buffers-equal? self: (addr gap-buffer), g: (addr gap-buffer) -> _/eax: boolean { + var tmp/eax: int <- gap-buffer-length self + var len/ecx: int <- copy tmp + var leng/eax: int <- gap-buffer-length g + compare len, leng + { + break-if-= + return 0/false + } + var i/edx: int <- copy 0 + { + compare i, len + break-if->= + { + var tmp/eax: grapheme <- gap-index self, i + var curr/ecx: grapheme <- copy tmp + var currg/eax: grapheme <- gap-index g, i + compare curr, currg + break-if-= + return 0/false + } + i <- increment + loop + } + return 1/true +} + +fn gap-index _self: (addr gap-buffer), _n: int -> _/eax: grapheme { + var self/esi: (addr gap-buffer) <- copy _self + var n/ebx: int <- copy _n + # if n < left->length, index into left + var left/edi: (addr grapheme-stack) <- get self, left + var left-len-a/edx: (addr int) <- get left, top + compare n, *left-len-a + { + break-if->= + var data-ah/eax: (addr handle array grapheme) <- get left, data + var data/eax: (addr array grapheme) <- lookup *data-ah + var result/eax: (addr grapheme) <- index data, n + return *result + } + # shrink n + n <- subtract *left-len-a + # if n < right->length, index into right + var right/edi: (addr grapheme-stack) <- get self, right + var right-len-a/edx: (addr int) <- get right, top + compare n, *right-len-a + { + break-if->= + var data-ah/eax: (addr handle array grapheme) <- get right, data + var data/eax: (addr array grapheme) <- lookup *data-ah + # idx = right->len - n - 1 + var idx/ebx: int <- copy n + idx <- subtract *right-len-a + idx <- negate + idx <- subtract 1 + var result/eax: (addr grapheme) <- index data, idx + return *result + } + # error + abort "gap-index: out of bounds" + return 0 +} + +fn test-gap-buffers-equal? { + var _a: gap-buffer + var a/esi: (addr gap-buffer) <- address _a + initialize-gap-buffer-with a, "abc" + var _b: gap-buffer + var b/edi: (addr gap-buffer) <- address _b + initialize-gap-buffer-with b, "abc" + var _c: gap-buffer + var c/ebx: (addr gap-buffer) <- address _c + initialize-gap-buffer-with c, "ab" + var _d: gap-buffer + var d/edx: (addr gap-buffer) <- address _d + initialize-gap-buffer-with d, "abd" + # + var result/eax: boolean <- gap-buffers-equal? a, a + check result, "F - test-gap-buffers-equal? - reflexive" + result <- gap-buffers-equal? a, b + check result, "F - test-gap-buffers-equal? - equal" + # length not equal + result <- gap-buffers-equal? a, c + check-not result, "F - test-gap-buffers-equal? - not equal" + # contents not equal + result <- gap-buffers-equal? a, d + check-not result, "F - test-gap-buffers-equal? - not equal 2" + result <- gap-buffers-equal? d, a + check-not result, "F - test-gap-buffers-equal? - not equal 3" +} + +fn test-gap-buffer-index { + var gap-storage: gap-buffer + var gap/esi: (addr gap-buffer) <- address gap-storage + initialize-gap-buffer-with gap, "abc" + # gap is at end, all contents are in left + var g/eax: grapheme <- gap-index gap, 0 + var x/ecx: int <- copy g + check-ints-equal x, 0x61/a, "F - test-gap-index/left-1" + var g/eax: grapheme <- gap-index gap, 1 + var x/ecx: int <- copy g + check-ints-equal x, 0x62/b, "F - test-gap-index/left-2" + var g/eax: grapheme <- gap-index gap, 2 + var x/ecx: int <- copy g + check-ints-equal x, 0x63/c, "F - test-gap-index/left-3" + # now check when everything is to the right + gap-to-start gap + rewind-gap-buffer gap + var g/eax: grapheme <- gap-index gap, 0 + var x/ecx: int <- copy g + check-ints-equal x, 0x61/a, "F - test-gap-index/right-1" + var g/eax: grapheme <- gap-index gap, 1 + var x/ecx: int <- copy g + check-ints-equal x, 0x62/b, "F - test-gap-index/right-2" + var g/eax: grapheme <- gap-index gap, 2 + var x/ecx: int <- copy g + check-ints-equal x, 0x63/c, "F - test-gap-index/right-3" +} + +fn copy-gap-buffer _src-ah: (addr handle gap-buffer), _dest-ah: (addr handle gap-buffer) { + # obtain src-a, dest-a + var src-ah/eax: (addr handle gap-buffer) <- copy _src-ah + var _src-a/eax: (addr gap-buffer) <- lookup *src-ah + var src-a/esi: (addr gap-buffer) <- copy _src-a + var dest-ah/eax: (addr handle gap-buffer) <- copy _dest-ah + var _dest-a/eax: (addr gap-buffer) <- lookup *dest-ah + var dest-a/edi: (addr gap-buffer) <- copy _dest-a + # copy left grapheme-stack + var src/ecx: (addr grapheme-stack) <- get src-a, left + var dest/edx: (addr grapheme-stack) <- get dest-a, left + copy-grapheme-stack src, dest + # copy right grapheme-stack + src <- get src-a, right + dest <- get dest-a, right + copy-grapheme-stack src, dest +} + +fn gap-buffer-is-decimal-integer? _self: (addr gap-buffer) -> _/eax: boolean { + var self/esi: (addr gap-buffer) <- copy _self + var curr/ecx: (addr grapheme-stack) <- get self, left + var result/eax: boolean <- grapheme-stack-is-decimal-integer? curr + { + compare result, 0/false + break-if-= + curr <- get self, right + result <- grapheme-stack-is-decimal-integer? curr + } + return result +} + +fn test-render-gap-buffer-without-cursor { + # setup + var gap-storage: gap-buffer + var gap/esi: (addr gap-buffer) <- address gap-storage + initialize-gap-buffer-with gap, "abc" + # setup: screen + var screen-on-stack: screen + var screen/edi: (addr screen) <- address screen-on-stack + initialize-screen screen, 5, 4 + # + var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 0/no-cursor + check-screen-row screen, 0/y, "abc ", "F - test-render-gap-buffer-without-cursor" + check-ints-equal x, 4, "F - test-render-gap-buffer-without-cursor: result" + # abc + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " ", "F - test-render-gap-buffer-without-cursor: bg" +} + +fn test-render-gap-buffer-with-cursor-at-end { + # setup + var gap-storage: gap-buffer + var gap/esi: (addr gap-buffer) <- address gap-storage + initialize-gap-buffer-with gap, "abc" + gap-to-end gap + # setup: screen + var screen-on-stack: screen + var screen/edi: (addr screen) <- address screen-on-stack + initialize-screen screen, 5, 4 + # + var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor + check-screen-row screen, 0/y, "abc ", "F - test-render-gap-buffer-with-cursor-at-end" + # we've drawn one extra grapheme for the cursor + check-ints-equal x, 4, "F - test-render-gap-buffer-with-cursor-at-end: result" + # abc + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " |", "F - test-render-gap-buffer-with-cursor-at-end: bg" +} + +fn test-render-gap-buffer-with-cursor-in-middle { + # setup + var gap-storage: gap-buffer + var gap/esi: (addr gap-buffer) <- address gap-storage + initialize-gap-buffer-with gap, "abc" + gap-to-end gap + var dummy/eax: grapheme <- gap-left gap + # setup: screen + var screen-on-stack: screen + var screen/edi: (addr screen) <- address screen-on-stack + initialize-screen screen, 5, 4 + # + var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor + check-screen-row screen, 0/y, "abc ", "F - test-render-gap-buffer-with-cursor-in-middle" + check-ints-equal x, 4, "F - test-render-gap-buffer-with-cursor-in-middle: result" + # abc + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " | ", "F - test-render-gap-buffer-with-cursor-in-middle: bg" +} + +fn test-render-gap-buffer-with-cursor-at-start { + var gap-storage: gap-buffer + var gap/esi: (addr gap-buffer) <- address gap-storage + initialize-gap-buffer-with gap, "abc" + gap-to-start gap + # setup: screen + var screen-on-stack: screen + var screen/edi: (addr screen) <- address screen-on-stack + initialize-screen screen, 5, 4 + # + var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor + check-screen-row screen, 0/y, "abc ", "F - test-render-gap-buffer-with-cursor-at-start" + check-ints-equal x, 4, "F - test-render-gap-buffer-with-cursor-at-start: result" + # abc + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "| ", "F - test-render-gap-buffer-with-cursor-at-start: bg" +} + +## some primitives for scanning through a gap buffer +# don't modify the gap buffer while scanning +# this includes moving the cursor around + +# restart scan without affecting gap-buffer contents +fn rewind-gap-buffer _self: (addr gap-buffer) { + var self/esi: (addr gap-buffer) <- copy _self + var dest/eax: (addr int) <- get self, left-read-index + copy-to *dest, 0 + dest <- get self, right-read-index + copy-to *dest, 0 +} + +fn gap-buffer-scan-done? _self: (addr gap-buffer) -> _/eax: boolean { + var self/esi: (addr gap-buffer) <- copy _self + # more in left? + var left/eax: (addr grapheme-stack) <- get self, left + var left-size/eax: int <- grapheme-stack-length left + var left-read-index/ecx: (addr int) <- get self, left-read-index + compare *left-read-index, left-size + { + break-if->= + return 0/false + } + # more in right? + var right/eax: (addr grapheme-stack) <- get self, right + var right-size/eax: int <- grapheme-stack-length right + var right-read-index/ecx: (addr int) <- get self, right-read-index + compare *right-read-index, right-size + { + break-if->= + return 0/false + } + # + return 1/true +} + +fn peek-from-gap-buffer _self: (addr gap-buffer) -> _/eax: grapheme { + var self/esi: (addr gap-buffer) <- copy _self + # more in left? + var left/ecx: (addr grapheme-stack) <- get self, left + var left-size/eax: int <- grapheme-stack-length left + var left-read-index-a/edx: (addr int) <- get self, left-read-index + compare *left-read-index-a, left-size + { + break-if->= + var left-data-ah/eax: (addr handle array grapheme) <- get left, data + var left-data/eax: (addr array grapheme) <- lookup *left-data-ah + var left-read-index/ecx: int <- copy *left-read-index-a + var result/eax: (addr grapheme) <- index left-data, left-read-index + return *result + } + # more in right? + var right/ecx: (addr grapheme-stack) <- get self, right + var _right-size/eax: int <- grapheme-stack-length right + var right-size/ebx: int <- copy _right-size + var right-read-index-a/edx: (addr int) <- get self, right-read-index + compare *right-read-index-a, right-size + { + break-if->= + # read the right from reverse + var right-data-ah/eax: (addr handle array grapheme) <- get right, data + var right-data/eax: (addr array grapheme) <- lookup *right-data-ah + var right-read-index/ebx: int <- copy right-size + right-read-index <- subtract *right-read-index-a + right-read-index <- subtract 1 + var result/eax: (addr grapheme) <- index right-data, right-read-index + return *result + } + # if we get here there's nothing left + return 0/nul +} + +fn read-from-gap-buffer _self: (addr gap-buffer) -> _/eax: grapheme { + var self/esi: (addr gap-buffer) <- copy _self + # more in left? + var left/ecx: (addr grapheme-stack) <- get self, left + var left-size/eax: int <- grapheme-stack-length left + var left-read-index-a/edx: (addr int) <- get self, left-read-index + compare *left-read-index-a, left-size + { + break-if->= + var left-data-ah/eax: (addr handle array grapheme) <- get left, data + var left-data/eax: (addr array grapheme) <- lookup *left-data-ah + var left-read-index/ecx: int <- copy *left-read-index-a + var result/eax: (addr grapheme) <- index left-data, left-read-index + increment *left-read-index-a + return *result + } + # more in right? + var right/ecx: (addr grapheme-stack) <- get self, right + var _right-size/eax: int <- grapheme-stack-length right + var right-size/ebx: int <- copy _right-size + var right-read-index-a/edx: (addr int) <- get self, right-read-index + compare *right-read-index-a, right-size + { + break-if->= + # read the right from reverse + var right-data-ah/eax: (addr handle array grapheme) <- get right, data + var right-data/eax: (addr array grapheme) <- lookup *right-data-ah + var right-read-index/ebx: int <- copy right-size + right-read-index <- subtract *right-read-index-a + right-read-index <- subtract 1 + var result/eax: (addr grapheme) <- index right-data, right-read-index + increment *right-read-index-a + return *result + } + # if we get here there's nothing left + return 0/nul +} + +fn test-read-from-gap-buffer { + var gap-storage: gap-buffer + var gap/esi: (addr gap-buffer) <- address gap-storage + initialize-gap-buffer-with gap, "abc" + # gap is at end, all contents are in left + var done?/eax: boolean <- gap-buffer-scan-done? gap + check-not done?, "F - test-read-from-gap-buffer/left-1/done" + var g/eax: grapheme <- read-from-gap-buffer gap + var x/ecx: int <- copy g + check-ints-equal x, 0x61/a, "F - test-read-from-gap-buffer/left-1" + var done?/eax: boolean <- gap-buffer-scan-done? gap + check-not done?, "F - test-read-from-gap-buffer/left-2/done" + var g/eax: grapheme <- read-from-gap-buffer gap + var x/ecx: int <- copy g + check-ints-equal x, 0x62/b, "F - test-read-from-gap-buffer/left-2" + var done?/eax: boolean <- gap-buffer-scan-done? gap + check-not done?, "F - test-read-from-gap-buffer/left-3/done" + var g/eax: grapheme <- read-from-gap-buffer gap + var x/ecx: int <- copy g + check-ints-equal x, 0x63/c, "F - test-read-from-gap-buffer/left-3" + var done?/eax: boolean <- gap-buffer-scan-done? gap + check done?, "F - test-read-from-gap-buffer/left-4/done" + var g/eax: grapheme <- read-from-gap-buffer gap + var x/ecx: int <- copy g + check-ints-equal x, 0/nul, "F - test-read-from-gap-buffer/left-4" + # now check when everything is to the right + gap-to-start gap + rewind-gap-buffer gap + var done?/eax: boolean <- gap-buffer-scan-done? gap + check-not done?, "F - test-read-from-gap-buffer/right-1/done" + var g/eax: grapheme <- read-from-gap-buffer gap + var x/ecx: int <- copy g + check-ints-equal x, 0x61/a, "F - test-read-from-gap-buffer/right-1" + var done?/eax: boolean <- gap-buffer-scan-done? gap + check-not done?, "F - test-read-from-gap-buffer/right-2/done" + var g/eax: grapheme <- read-from-gap-buffer gap + var x/ecx: int <- copy g + check-ints-equal x, 0x62/b, "F - test-read-from-gap-buffer/right-2" + var done?/eax: boolean <- gap-buffer-scan-done? gap + check-not done?, "F - test-read-from-gap-buffer/right-3/done" + var g/eax: grapheme <- read-from-gap-buffer gap + var x/ecx: int <- copy g + check-ints-equal x, 0x63/c, "F - test-read-from-gap-buffer/right-3" + var done?/eax: boolean <- gap-buffer-scan-done? gap + check done?, "F - test-read-from-gap-buffer/right-4/done" + var g/eax: grapheme <- read-from-gap-buffer gap + var x/ecx: int <- copy g + check-ints-equal x, 0/nul, "F - test-read-from-gap-buffer/right-4" +} + +fn skip-whitespace-from-gap-buffer self: (addr gap-buffer) { + var done?/eax: boolean <- gap-buffer-scan-done? self + compare done?, 0/false + break-if-!= + var g/eax: grapheme <- peek-from-gap-buffer self + { + compare g, 0x20/space + break-if-= + compare g, 0xa/newline + break-if-= + return + } + g <- read-from-gap-buffer self + loop +} + +fn edit-gap-buffer self: (addr gap-buffer), key: grapheme { + var g/edx: grapheme <- copy key + { + compare g, 8/backspace + break-if-!= + delete-before-gap self + return + } + # arrow keys + { + compare g, 4/ctrl-d + break-if-!= + # ctrl-d: cursor down + return + } + { + compare g, 0x15/ctrl-u + break-if-!= + # ctrl-u: cursor up + return + } + # default: insert character + add-grapheme-at-gap self, g +} + +fn cursor-on-final-line? self: (addr gap-buffer) -> _/eax: boolean { + return 1/true +} diff --git a/shell/grapheme-stack.mu b/shell/grapheme-stack.mu new file mode 100644 index 00000000..456df0cb --- /dev/null +++ b/shell/grapheme-stack.mu @@ -0,0 +1,280 @@ +# grapheme stacks are the smallest unit of editable text + +type grapheme-stack { + data: (handle array grapheme) + top: int +} + +fn initialize-grapheme-stack _self: (addr grapheme-stack), n: int { + var self/esi: (addr grapheme-stack) <- copy _self + var d/edi: (addr handle array grapheme) <- get self, data + populate d, n + var top/eax: (addr int) <- get self, top + copy-to *top, 0 +} + +fn clear-grapheme-stack _self: (addr grapheme-stack) { + var self/esi: (addr grapheme-stack) <- copy _self + var top/eax: (addr int) <- get self, top + copy-to *top, 0 +} + +fn grapheme-stack-empty? _self: (addr grapheme-stack) -> _/eax: boolean { + var self/esi: (addr grapheme-stack) <- copy _self + var top/eax: (addr int) <- get self, top + compare *top, 0 + { + break-if-!= + return 1/true + } + return 0/false +} + +fn grapheme-stack-length _self: (addr grapheme-stack) -> _/eax: int { + var self/esi: (addr grapheme-stack) <- copy _self + var top/eax: (addr int) <- get self, top + return *top +} + +fn push-grapheme-stack _self: (addr grapheme-stack), _val: grapheme { + var self/esi: (addr grapheme-stack) <- copy _self + var top-addr/ecx: (addr int) <- get self, top + var data-ah/edx: (addr handle array grapheme) <- get self, data + var data/eax: (addr array grapheme) <- lookup *data-ah + var top/edx: int <- copy *top-addr + var dest-addr/edx: (addr grapheme) <- index data, top + var val/eax: grapheme <- copy _val + copy-to *dest-addr, val + add-to *top-addr, 1 +} + +fn pop-grapheme-stack _self: (addr grapheme-stack) -> _/eax: grapheme { + var self/esi: (addr grapheme-stack) <- copy _self + var top-addr/ecx: (addr int) <- get self, top + { + compare *top-addr, 0 + break-if-> + return -1 + } + subtract-from *top-addr, 1 + var data-ah/edx: (addr handle array grapheme) <- get self, data + var data/eax: (addr array grapheme) <- lookup *data-ah + var top/edx: int <- copy *top-addr + var result-addr/eax: (addr grapheme) <- index data, top + return *result-addr +} + +fn copy-grapheme-stack _src: (addr grapheme-stack), dest: (addr grapheme-stack) { + var src/esi: (addr grapheme-stack) <- copy _src + var data-ah/edi: (addr handle array grapheme) <- get src, data + var _data/eax: (addr array grapheme) <- lookup *data-ah + var data/edi: (addr array grapheme) <- copy _data + var top-addr/ecx: (addr int) <- get src, top + var i/eax: int <- copy 0 + { + compare i, *top-addr + break-if->= + var g/edx: (addr grapheme) <- index data, i + push-grapheme-stack dest, *g + i <- increment + loop + } +} + +# dump stack to screen from bottom to top +# colors hardcoded +fn render-stack-from-bottom-wrapping-right-then-down screen: (addr screen), _self: (addr grapheme-stack), xmin: int, ymin: int, xmax: int, ymax: int, _x: int, _y: int -> _/eax: int, _/ecx: int { + var self/esi: (addr grapheme-stack) <- copy _self + var data-ah/edi: (addr handle array grapheme) <- get self, data + var _data/eax: (addr array grapheme) <- lookup *data-ah + var data/edi: (addr array grapheme) <- copy _data + var x/eax: int <- copy _x + var y/ecx: int <- copy _y + var top-addr/edx: (addr int) <- get self, top + var i/ebx: int <- copy 0 + { + compare i, *top-addr + break-if->= + { + var g/edx: (addr grapheme) <- index data, i + x, y <- render-grapheme screen, *g, xmin, ymin, xmax, ymax, x, y, 3/fg=cyan, 0/bg + } + i <- increment + loop + } + return x, y +} + +# helper for small words +fn render-stack-from-bottom screen: (addr screen), self: (addr grapheme-stack), x: int, y: int -> _/eax: int { + var _width/eax: int <- copy 0 + var _height/ecx: int <- copy 0 + _width, _height <- screen-size screen + var width/edx: int <- copy _width + var height/ebx: int <- copy _height + var x2/eax: int <- copy 0 + var y2/ecx: int <- copy 0 + x2, y2 <- render-stack-from-bottom-wrapping-right-then-down screen, self, x, y, width, height, x, y + return x2 # y2? yolo +} + +# dump stack to screen from top to bottom +# optionally render a 'cursor' with the top grapheme +fn render-stack-from-top-wrapping-right-then-down screen: (addr screen), _self: (addr grapheme-stack), xmin: int, ymin: int, xmax: int, ymax: int, _x: int, _y: int, render-cursor?: boolean -> _/eax: int, _/ecx: int { + var self/esi: (addr grapheme-stack) <- copy _self + var data-ah/edi: (addr handle array grapheme) <- get self, data + var _data/eax: (addr array grapheme) <- lookup *data-ah + var data/edi: (addr array grapheme) <- copy _data + var x/eax: int <- copy _x + var y/ecx: int <- copy _y + var top-addr/edx: (addr int) <- get self, top + var i/ebx: int <- copy *top-addr + i <- decrement + # if render-cursor?, peel off first iteration + { + compare render-cursor?, 0/false + break-if-= + compare i, 0 + break-if-< + { + var g/edx: (addr grapheme) <- index data, i + x, y <- render-grapheme screen, *g, xmin, ymin, xmax, ymax, x, y, 3/fg=cyan, 7/bg=cursor + } + i <- decrement + } + # remaining iterations + { + compare i, 0 + break-if-< + { + var g/edx: (addr grapheme) <- index data, i + x, y <- render-grapheme screen, *g, xmin, ymin, xmax, ymax, x, y, 3/fg=cyan, 0/bg=cursor + } + i <- decrement + loop + } + return x, y +} + +# helper for small words +fn render-stack-from-top screen: (addr screen), self: (addr grapheme-stack), x: int, y: int, render-cursor?: boolean -> _/eax: int { + var _width/eax: int <- copy 0 + var _height/ecx: int <- copy 0 + _width, _height <- screen-size screen + var width/edx: int <- copy _width + var height/ebx: int <- copy _height + var x2/eax: int <- copy 0 + var y2/ecx: int <- copy 0 + x2, y2 <- render-stack-from-top-wrapping-right-then-down screen, self, x, y, width, height, x, y, render-cursor? + return x2 # y2? yolo +} + +fn test-render-grapheme-stack { + # setup: gs = "abc" + var gs-storage: grapheme-stack + var gs/edi: (addr grapheme-stack) <- address gs-storage + initialize-grapheme-stack gs, 5 + var g/eax: grapheme <- copy 0x61/a + push-grapheme-stack gs, g + g <- copy 0x62/b + push-grapheme-stack gs, g + g <- copy 0x63/c + push-grapheme-stack gs, g + # setup: screen + var screen-on-stack: screen + var screen/esi: (addr screen) <- address screen-on-stack + initialize-screen screen, 5, 4 + # + var x/eax: int <- render-stack-from-bottom screen, gs, 0/x, 0/y + check-screen-row screen, 0/y, "abc ", "F - test-render-grapheme-stack from bottom" + check-ints-equal x, 3, "F - test-render-grapheme-stack from bottom: result" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " ", "F - test-render-grapheme-stack from bottom: bg" + # + var x/eax: int <- render-stack-from-top screen, gs, 0/x, 1/y, 0/cursor=false + check-screen-row screen, 1/y, "cba ", "F - test-render-grapheme-stack from top without cursor" + check-ints-equal x, 3, "F - test-render-grapheme-stack from top without cursor: result" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-render-grapheme-stack from top without cursor: bg" + # + var x/eax: int <- render-stack-from-top screen, gs, 0/x, 2/y, 1/cursor=true + check-screen-row screen, 2/y, "cba ", "F - test-render-grapheme-stack from top with cursor" + check-ints-equal x, 3, "F - test-render-grapheme-stack from top without cursor: result" + check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "| ", "F - test-render-grapheme-stack from top with cursor: bg" +} + +# compare from bottom +# beware: modifies 'stream', which must be disposed of after a false result +fn prefix-match? _self: (addr grapheme-stack), s: (addr stream byte) -> _/eax: boolean { + var self/esi: (addr grapheme-stack) <- copy _self + var data-ah/edi: (addr handle array grapheme) <- get self, data + var _data/eax: (addr array grapheme) <- lookup *data-ah + var data/edi: (addr array grapheme) <- copy _data + var top-addr/ecx: (addr int) <- get self, top + var i/ebx: int <- copy 0 + { + compare i, *top-addr + break-if->= + # if curr != expected, return false + { + var curr-a/edx: (addr grapheme) <- index data, i + var expected/eax: grapheme <- read-grapheme s + { + compare expected, *curr-a + break-if-= + return 0/false + } + } + i <- increment + loop + } + return 1 # true +} + +# compare from bottom +# beware: modifies 'stream', which must be disposed of after a false result +fn suffix-match? _self: (addr grapheme-stack), s: (addr stream byte) -> _/eax: boolean { + var self/esi: (addr grapheme-stack) <- copy _self + var data-ah/edi: (addr handle array grapheme) <- get self, data + var _data/eax: (addr array grapheme) <- lookup *data-ah + var data/edi: (addr array grapheme) <- copy _data + var top-addr/eax: (addr int) <- get self, top + var i/ebx: int <- copy *top-addr + i <- decrement + { + compare i, 0 + break-if-< + { + var curr-a/edx: (addr grapheme) <- index data, i + var expected/eax: grapheme <- read-grapheme s + # if curr != expected, return false + { + compare expected, *curr-a + break-if-= + return 0/false + } + } + i <- decrement + loop + } + return 1 # true +} + +fn grapheme-stack-is-decimal-integer? _self: (addr grapheme-stack) -> _/eax: boolean { + var self/esi: (addr grapheme-stack) <- copy _self + var data-ah/eax: (addr handle array grapheme) <- get self, data + var _data/eax: (addr array grapheme) <- lookup *data-ah + var data/edx: (addr array grapheme) <- copy _data + var top-addr/ecx: (addr int) <- get self, top + var i/ebx: int <- copy 0 + var result/eax: boolean <- copy 1/true + $grapheme-stack-is-integer?:loop: { + compare i, *top-addr + break-if->= + var g/edx: (addr grapheme) <- index data, i + result <- is-decimal-digit? *g + compare result, 0/false + break-if-= + i <- increment + loop + } + return result +} diff --git a/shell/main.mu b/shell/main.mu new file mode 100644 index 00000000..eb437b67 --- /dev/null +++ b/shell/main.mu @@ -0,0 +1,22 @@ +# Experimental Mu shell +# A Lisp with indent-sensitivity and infix, no macros. Commas are ignored. + +fn main { + var sandbox-storage: sandbox + var sandbox/esi: (addr sandbox) <- address sandbox-storage + initialize-sandbox sandbox + var width/eax: int <- copy 0 + var height/ecx: int <- copy 0 + width, height <- screen-size 0/screen + { + render-sandbox 0/screen, sandbox, 2/x, 2/y, width, height + { + var key/eax: byte <- read-key 0/keyboard + compare key, 0 + loop-if-= + # no way to quit right now; just reboot + edit-sandbox sandbox, key + } + loop + } +} diff --git a/shell/parse.mu b/shell/parse.mu new file mode 100644 index 00000000..a0045eb3 --- /dev/null +++ b/shell/parse.mu @@ -0,0 +1,136 @@ +fn parse-input tokens: (addr stream cell), out: (addr handle cell), trace: (addr trace) { + rewind-stream tokens + var empty?/eax: boolean <- stream-empty? tokens + compare empty?, 0/false + { + break-if-= + error trace, "nothing to parse" + return + } + var close-paren?/eax: boolean <- parse-sexpression tokens, out, trace + { + compare close-paren?, 0/false + break-if-= + error trace, "')' is not a valid expression" + return + } + { + var empty?/eax: boolean <- stream-empty? tokens + compare empty?, 0/false + break-if-!= + error trace, "unexpected tokens at end; only type in a single expression at a time" + } +} + +# return value: true if close-paren was encountered +fn parse-sexpression tokens: (addr stream cell), _out: (addr handle cell), trace: (addr trace) -> _/eax: boolean { + trace-text trace, "read", "parse" + trace-lower trace + var curr-token-storage: cell + var curr-token/ecx: (addr cell) <- address curr-token-storage + var empty?/eax: boolean <- stream-empty? tokens + compare empty?, 0/false + { + break-if-= + error trace, "end of stream; never found a balancing ')'" + return 1/true + } + read-from-stream tokens, curr-token + $parse-sexpression:type-check: { + # not bracket -> parse atom + var is-bracket-token?/eax: boolean <- is-bracket-token? curr-token + compare is-bracket-token?, 0/false + { + break-if-!= + parse-atom curr-token, _out, trace + break $parse-sexpression:type-check + } + # open paren -> parse list + var is-open-paren?/eax: boolean <- is-open-paren-token? curr-token + compare is-open-paren?, 0/false + { + break-if-= + var curr/esi: (addr handle cell) <- copy _out + $parse-sexpression:list-loop: { + allocate-pair curr + var curr-addr/eax: (addr cell) <- lookup *curr + var left/ecx: (addr handle cell) <- get curr-addr, left + { + var is-close-paren?/eax: boolean <- parse-sexpression tokens, left, trace + compare is-close-paren?, 0/false + break-if-!= $parse-sexpression:list-loop + } + # + curr <- get curr-addr, right + loop + } + break $parse-sexpression:type-check + } + # close paren -> parse list + var is-close-paren?/eax: boolean <- is-close-paren-token? curr-token + compare is-close-paren?, 0/false + { + break-if-= + trace-higher trace + return 1/true + } + # otherwise abort + var stream-storage: (stream byte 0x40) + 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 + var curr-token-data/eax: (addr stream byte) <- lookup *curr-token-data-ah + rewind-stream curr-token-data + write-stream stream, curr-token-data + trace trace, "error", stream + } + trace-higher trace + return 0/false +} + +fn parse-atom _curr-token: (addr cell), _out: (addr handle cell), trace: (addr trace) { + trace-text trace, "read", "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 + # number + var is-number-token?/eax: boolean <- is-number-token? curr-token + compare is-number-token?, 0/false + { + break-if-= + rewind-stream curr-token-data + var _val/eax: int <- parse-decimal-int-from-stream curr-token-data + var val/ecx: int <- copy _val + var val-float/xmm0: float <- convert val + allocate-number _out + var out/eax: (addr handle cell) <- copy _out + var out-addr/eax: (addr cell) <- lookup *out + var dest/edi: (addr float) <- get out-addr, number-data + copy-to *dest, val-float + { + var stream-storage: (stream byte 0x40) + var stream/ecx: (addr stream byte) <- address stream-storage + write stream, "=> number " + print-number out-addr, stream, 0/no-trace + trace trace, "read", stream + } + return + } + # default: symbol + # just copy token data + allocate-symbol _out + var out/eax: (addr handle cell) <- copy _out + var out-addr/eax: (addr cell) <- lookup *out + var curr-token-data-ah/ecx: (addr handle stream byte) <- get curr-token, text-data + 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/ecx: (addr stream byte) <- address stream-storage + write stream, "=> symbol " + print-symbol out-addr, stream, 0/no-trace + trace trace, "read", stream + } +} diff --git a/shell/print.mu b/shell/print.mu new file mode 100644 index 00000000..32f5e725 --- /dev/null +++ b/shell/print.mu @@ -0,0 +1,260 @@ +fn print-cell _in: (addr handle cell), out: (addr stream byte), trace: (addr trace) { + trace-text trace, "print", "print-cell" + trace-lower trace + var in/eax: (addr handle cell) <- copy _in + var in-addr/eax: (addr cell) <- lookup *in + { + var is-nil?/eax: boolean <- is-nil? in-addr + compare is-nil?, 0/false + break-if-= + write out, "()" + trace-higher trace + return + } + var in-type/ecx: (addr int) <- get in-addr, type + compare *in-type, 0/pair + { + break-if-!= + print-list in-addr, out, trace + trace-higher trace + return + } + compare *in-type, 1/number + { + break-if-!= + print-number in-addr, out, trace + trace-higher trace + return + } + compare *in-type, 2/symbol + { + break-if-!= + print-symbol in-addr, out, trace + trace-higher trace + return + } +} + +fn print-symbol _in: (addr cell), out: (addr stream byte), trace: (addr trace) { + trace-text trace, "print", "symbol" + var in/esi: (addr cell) <- copy _in + var data-ah/eax: (addr handle stream byte) <- get in, text-data + var _data/eax: (addr stream byte) <- lookup *data-ah + var data/esi: (addr stream byte) <- copy _data + rewind-stream data + write-stream out, data + # trace + rewind-stream data + var stream-storage: (stream byte 0x40) + var stream/ecx: (addr stream byte) <- address stream-storage + write stream, "=> symbol " + write-stream stream, data + trace trace, "print", stream +} + +fn print-number _in: (addr cell), out: (addr stream byte), trace: (addr trace) { + var in/esi: (addr cell) <- copy _in + var val/eax: (addr float) <- get in, number-data + write-float-decimal-approximate out, *val, 3/precision + # trace + var stream-storage: (stream byte 0x40) + var stream/ecx: (addr stream byte) <- address stream-storage + write stream, "=> number " + write-float-decimal-approximate stream, *val, 3/precision + trace trace, "print", stream +} + +fn print-list _in: (addr cell), out: (addr stream byte), trace: (addr trace) { + var curr/esi: (addr cell) <- copy _in + write out, "(" + $print-list:loop: { + var left/ecx: (addr handle cell) <- get curr, left + { + var left-addr/eax: (addr cell) <- lookup *left + var left-is-nil?/eax: boolean <- is-nil? left-addr + compare left-is-nil?, 0/false + { + break-if-= + trace-text trace, "print", "left is null" + break $print-list:loop + } + } + print-cell left, out, trace + var right/ecx: (addr handle cell) <- get curr, right + var right-addr/eax: (addr cell) <- lookup *right + { + compare right-addr, 0 + break-if-!= + abort "null encountered" + } + { + var right-is-nil?/eax: boolean <- is-nil? right-addr + compare right-is-nil?, 0/false + { + break-if-= + trace-text trace, "print", "right is null" + break $print-list:loop + } + } + write out, " " + var right-type-addr/edx: (addr int) <- get right-addr, type + { + compare *right-type-addr, 0/pair + break-if-= + write out, ". " + print-cell right, out, trace + break $print-list:loop + } + curr <- copy right-addr + loop + } + write out, ")" +} + +# Most lisps intern nil, but we don't really have globals yet, so we'll be +# less efficient for now. +fn is-nil? _in: (addr cell) -> _/eax: boolean { + var in/esi: (addr cell) <- copy _in + # if type != pair, return false + var type/eax: (addr int) <- get in, type + compare *type, 0/pair + { + break-if-= + return 0/false + } + # if left != null, return false + var left-ah/eax: (addr handle cell) <- get in, left + var left/eax: (addr cell) <- lookup *left-ah + compare left, 0 + { + break-if-= + return 0/false + } + # if right != null, return false + var right-ah/eax: (addr handle cell) <- get in, right + var right/eax: (addr cell) <- lookup *right-ah + compare right, 0 + { + break-if-= + return 0/false + } + return 1/true +} + +fn test-print-cell-zero { + var num-storage: (handle cell) + var num/esi: (addr handle cell) <- address num-storage + new-integer num, 0 + var out-storage: (stream byte 0x40) + var out/edi: (addr stream byte) <- address out-storage + print-cell num, out, 0/no-trace + check-stream-equal out, "0", "F - test-print-cell-zero" +} + +fn test-print-cell-integer { + var num-storage: (handle cell) + var num/esi: (addr handle cell) <- address num-storage + new-integer num, 1 + var out-storage: (stream byte 0x40) + var out/edi: (addr stream byte) <- address out-storage + print-cell num, out, 0/no-trace + check-stream-equal out, "1", "F - test-print-cell-integer" +} + +fn test-print-cell-integer-2 { + var num-storage: (handle cell) + var num/esi: (addr handle cell) <- address num-storage + new-integer num, 0x30 + var out-storage: (stream byte 0x40) + var out/edi: (addr stream byte) <- address out-storage + print-cell num, out, 0/no-trace + check-stream-equal out, "48", "F - test-print-cell-integer-2" +} + +fn test-print-cell-fraction { + var num-storage: (handle cell) + var num/esi: (addr handle cell) <- address num-storage + var val/xmm0: float <- rational 1, 2 + new-float num, val + var out-storage: (stream byte 0x40) + var out/edi: (addr stream byte) <- address out-storage + print-cell num, out, 0/no-trace + check-stream-equal out, "0.5", "F - test-print-cell-fraction" +} + +fn test-print-cell-symbol { + var sym-storage: (handle cell) + var sym/esi: (addr handle cell) <- address sym-storage + new-symbol sym, "abc" + var out-storage: (stream byte 0x40) + var out/edi: (addr stream byte) <- address out-storage + print-cell sym, out, 0/no-trace + check-stream-equal out, "abc", "F - test-print-cell-symbol" +} + +fn test-print-cell-nil-list { + var nil-storage: (handle cell) + var nil/esi: (addr handle cell) <- address nil-storage + allocate-pair nil + var out-storage: (stream byte 0x40) + var out/edi: (addr stream byte) <- address out-storage + print-cell nil, out, 0/no-trace + check-stream-equal out, "()", "F - test-print-cell-nil-list" +} + +fn test-print-cell-singleton-list { + # list + var left-storage: (handle cell) + var left/ecx: (addr handle cell) <- address left-storage + new-symbol left, "abc" + var nil-storage: (handle cell) + var nil/edx: (addr handle cell) <- address nil-storage + allocate-pair nil + var list-storage: (handle cell) + var list/esi: (addr handle cell) <- address list-storage + new-pair list, *left, *nil + # + var out-storage: (stream byte 0x40) + var out/edi: (addr stream byte) <- address out-storage + print-cell list, out, 0/no-trace + check-stream-equal out, "(abc)", "F - test-print-cell-singleton-list" +} + +fn test-print-cell-list { + # list = cons "abc", nil + var left-storage: (handle cell) + var left/ecx: (addr handle cell) <- address left-storage + new-symbol left, "abc" + var nil-storage: (handle cell) + var nil/edx: (addr handle cell) <- address nil-storage + allocate-pair nil + var list-storage: (handle cell) + var list/esi: (addr handle cell) <- address list-storage + new-pair list, *left, *nil + # list = cons 64, list + new-integer left, 0x40 + new-pair list, *left, *list + # + var out-storage: (stream byte 0x40) + var out/edi: (addr stream byte) <- address out-storage + print-cell list, out, 0/no-trace + check-stream-equal out, "(64 abc)", "F - test-print-cell-list" +} + +fn test-print-dotted-list { + # list = cons 64, "abc" + var left-storage: (handle cell) + var left/ecx: (addr handle cell) <- address left-storage + new-symbol left, "abc" + var right-storage: (handle cell) + var right/edx: (addr handle cell) <- address right-storage + new-integer right, 0x40 + var list-storage: (handle cell) + var list/esi: (addr handle cell) <- address list-storage + new-pair list, *left, *right + # + var out-storage: (stream byte 0x40) + var out/edi: (addr stream byte) <- address out-storage + print-cell list, out, 0/no-trace + check-stream-equal out, "(abc . 64)", "F - test-print-dotted-list" +} diff --git a/shell/read.mu b/shell/read.mu new file mode 100644 index 00000000..d3e1dc86 --- /dev/null +++ b/shell/read.mu @@ -0,0 +1,15 @@ +# out is not allocated +fn read-cell in: (addr gap-buffer), out: (addr handle cell), trace: (addr trace) { + var tokens-storage: (stream cell 0x100) + var tokens/ecx: (addr stream cell) <- address tokens-storage + tokenize in, tokens, trace + var error?/eax: boolean <- has-errors? trace + compare error?, 0/false + { + break-if-= + return + } + # TODO: insert parens + # TODO: transform infix + parse-input tokens, out, trace +} diff --git a/shell/sandbox.mu b/shell/sandbox.mu new file mode 100644 index 00000000..49c2a5f9 --- /dev/null +++ b/shell/sandbox.mu @@ -0,0 +1,263 @@ +type sandbox { + data: (handle gap-buffer) + value: (handle stream byte) + trace: (handle trace) + cursor-in-trace?: boolean +} + +fn initialize-sandbox _self: (addr sandbox) { + var self/esi: (addr sandbox) <- copy _self + var data-ah/eax: (addr handle gap-buffer) <- get self, data + allocate data-ah + var data/eax: (addr gap-buffer) <- lookup *data-ah + initialize-gap-buffer data, 0x1000/4KB + var value-ah/eax: (addr handle stream byte) <- get self, value + populate-stream value-ah, 0x1000/4KB + var trace-ah/eax: (addr handle trace) <- get self, trace + allocate trace-ah + var trace/eax: (addr trace) <- lookup *trace-ah + initialize-trace trace, 0x1000/lines, 0x80/visible-lines +} + +## some helpers for tests + +fn initialize-sandbox-with _self: (addr sandbox), s: (addr array byte) { + var self/esi: (addr sandbox) <- copy _self + var data-ah/eax: (addr handle gap-buffer) <- get self, data + allocate data-ah + var data/eax: (addr gap-buffer) <- lookup *data-ah + initialize-gap-buffer-with data, s +} + +fn allocate-sandbox-with _out: (addr handle sandbox), s: (addr array byte) { + var out/eax: (addr handle sandbox) <- copy _out + allocate out + var out-addr/eax: (addr sandbox) <- lookup *out + initialize-sandbox-with out-addr, s +} + +## + +fn render-sandbox screen: (addr screen), _self: (addr sandbox), xmin: int, ymin: int, xmax: int, ymax: int { + clear-screen screen + var self/esi: (addr sandbox) <- copy _self + # data + var data-ah/eax: (addr handle gap-buffer) <- get self, data + var _data/eax: (addr gap-buffer) <- lookup *data-ah + var data/edx: (addr gap-buffer) <- copy _data + var x/eax: int <- copy xmin + var y/ecx: int <- copy ymin + var cursor-in-sandbox?/ebx: boolean <- copy 0/false + { + var cursor-in-trace?/eax: (addr boolean) <- get self, cursor-in-trace? + compare *cursor-in-trace?, 0/false + break-if-!= + cursor-in-sandbox? <- copy 1/true + } + x, y <- render-gap-buffer-wrapping-right-then-down screen, data, x, y, xmax, ymax, cursor-in-sandbox? + y <- increment + # trace + var trace-ah/eax: (addr handle trace) <- get self, trace + var _trace/eax: (addr trace) <- lookup *trace-ah + var trace/edx: (addr trace) <- copy _trace + var cursor-in-trace?/eax: (addr boolean) <- get self, cursor-in-trace? + y <- render-trace screen, trace, xmin, y, xmax, ymax, *cursor-in-trace? + # value + $render-sandbox:value: { + var value-ah/eax: (addr handle stream byte) <- get self, value + var _value/eax: (addr stream byte) <- lookup *value-ah + var value/esi: (addr stream byte) <- copy _value + rewind-stream value + var done?/eax: boolean <- stream-empty? value + compare done?, 0/false + break-if-!= + var x/eax: int <- copy 0 + x, y <- draw-text-wrapping-right-then-down screen, "=> ", xmin, y, xmax, ymax, xmin, y, 7/fg, 0/bg + var x2/edx: int <- copy x + var dummy/eax: int <- draw-stream-rightward screen, value, x2, xmax, y, 7/fg=grey, 0/bg + } + # render menu + var cursor-in-trace?/eax: (addr boolean) <- get self, cursor-in-trace? + compare *cursor-in-trace?, 0/false + { + break-if-= + render-trace-menu screen + return + } + render-sandbox-menu screen +} + +fn render-sandbox-menu screen: (addr screen) { + var width/eax: int <- copy 0 + var height/ecx: int <- copy 0 + width, height <- screen-size screen + var y/ecx: int <- copy height + y <- decrement + set-cursor-position screen, 0/x, y + draw-text-rightward-from-cursor screen, " ctrl-s ", width, 0/fg, 7/bg=grey + draw-text-rightward-from-cursor screen, " run sandbox ", width, 7/fg, 0/bg + draw-text-rightward-from-cursor screen, " ctrl-d ", width, 0/fg, 7/bg=grey + draw-text-rightward-from-cursor screen, " cursor down ", width, 7/fg, 0/bg + draw-text-rightward-from-cursor screen, " ctrl-u ", width, 0/fg, 7/bg=grey + draw-text-rightward-from-cursor screen, " cursor up ", width, 7/fg, 0/bg + draw-text-rightward-from-cursor screen, " tab ", width, 0/fg, 9/bg=blue + draw-text-rightward-from-cursor screen, " move to trace ", width, 7/fg, 0/bg +} + +fn edit-sandbox _self: (addr sandbox), key: byte { + var self/esi: (addr sandbox) <- copy _self + var g/edx: grapheme <- copy key + # running code + { + compare g, 0x12/ctrl-r + break-if-!= + # ctrl-r: run function outside sandbox + # required: fn (addr screen), (addr keyboard) + # Mu will pass in the real screen and keyboard. + return + } + { + compare g, 0x13/ctrl-s + break-if-!= + # ctrl-s: run sandbox(es) + var data-ah/eax: (addr handle gap-buffer) <- get self, data + var _data/eax: (addr gap-buffer) <- lookup *data-ah + var data/ecx: (addr gap-buffer) <- copy _data + var value-ah/eax: (addr handle stream byte) <- get self, value + var _value/eax: (addr stream byte) <- lookup *value-ah + var value/edx: (addr stream byte) <- copy _value + var trace-ah/eax: (addr handle trace) <- get self, trace + var trace/eax: (addr trace) <- lookup *trace-ah + clear-trace trace + run data, value, trace + return + } + # tab + var cursor-in-trace?/eax: (addr boolean) <- get self, cursor-in-trace? + { + compare g, 9/tab + break-if-!= + # if cursor in input, switch to trace + { + compare *cursor-in-trace?, 0/false + break-if-!= + copy-to *cursor-in-trace?, 1/true + return + } + # if cursor in trace, switch to input + copy-to *cursor-in-trace?, 0/false + return + } + # if cursor in trace, send cursor to trace + { + compare *cursor-in-trace?, 0/false + break-if-= + var trace-ah/eax: (addr handle trace) <- get self, trace + var trace/eax: (addr trace) <- lookup *trace-ah + edit-trace trace, g + return + } + # otherwise send cursor to input + var data-ah/eax: (addr handle gap-buffer) <- get self, data + var data/eax: (addr gap-buffer) <- lookup *data-ah + edit-gap-buffer data, g + return +} + +fn run in: (addr gap-buffer), out: (addr stream byte), trace: (addr trace) { + var read-result-storage: (handle cell) + var read-result/esi: (addr handle cell) <- address read-result-storage + read-cell in, read-result, trace + var error?/eax: boolean <- has-errors? trace + { + compare error?, 0/false + break-if-= + return + } + # TODO: eval + clear-stream out + print-cell read-result, out, trace + mark-lines-dirty trace +} + +fn test-run-integer { + var sandbox-storage: sandbox + var sandbox/esi: (addr sandbox) <- address sandbox-storage + initialize-sandbox sandbox + # type "1" + edit-sandbox sandbox, 0x31/1 + # eval + edit-sandbox sandbox, 0x13/ctrl-s + # setup: screen + var screen-on-stack: screen + var screen/edi: (addr screen) <- address screen-on-stack + initialize-screen screen, 0x80/width, 0x10/height + # + render-sandbox screen, sandbox, 0/x, 0/y, 0x80/width, 0x10/height + check-screen-row screen, 0/y, "1 ", "F - test-run-integer/0" + check-screen-row screen, 1/y, "... ", "F - test-run-integer/1" + check-screen-row screen, 2/y, "=> 1 ", "F - test-run-integer/2" +} + +fn test-run-error-invalid-integer { + var sandbox-storage: sandbox + var sandbox/esi: (addr sandbox) <- address sandbox-storage + initialize-sandbox sandbox + # type "1a" + edit-sandbox sandbox, 0x31/1 + edit-sandbox sandbox, 0x61/a + # eval + edit-sandbox sandbox, 0x13/ctrl-s + # setup: screen + var screen-on-stack: screen + var screen/edi: (addr screen) <- address screen-on-stack + initialize-screen screen, 0x80/width, 0x10/height + # + render-sandbox screen, sandbox, 0/x, 0/y, 0x80/width, 0x10/height + check-screen-row screen, 0/y, "1a ", "F - test-run-error-invalid-integer/0" + check-screen-row screen, 1/y, "... ", "F - test-run-error-invalid-integer/0" + check-screen-row screen, 2/y, "invalid number ", "F - test-run-error-invalid-integer/2" +} + +fn test-run-move-cursor-into-trace { + var sandbox-storage: sandbox + var sandbox/esi: (addr sandbox) <- address sandbox-storage + initialize-sandbox sandbox + # type "12" + edit-sandbox sandbox, 0x31/1 + edit-sandbox sandbox, 0x32/2 + # eval + edit-sandbox sandbox, 0x13/ctrl-s + # setup: screen + var screen-on-stack: screen + var screen/edi: (addr screen) <- address screen-on-stack + initialize-screen screen, 0x80/width, 0x10/height + # + render-sandbox screen, sandbox, 0/x, 0/y, 0x80/width, 0x10/height + check-screen-row screen, 0/y, "12 ", "F - test-run-move-cursor-into-trace/pre-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " | ", "F - test-run-move-cursor-into-trace/pre-0/cursor" + check-screen-row screen, 1/y, "... ", "F - test-run-move-cursor-into-trace/pre-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-run-move-cursor-into-trace/pre-1/cursor" + check-screen-row screen, 2/y, "=> 12 ", "F - test-run-move-cursor-into-trace/pre-2" + check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, " ", "F - test-run-move-cursor-into-trace/pre-2/cursor" + # move cursor into trace + edit-sandbox sandbox, 9/tab + # + render-sandbox screen, sandbox, 0/x, 0/y, 0x80/width, 0x10/height + check-screen-row screen, 0/y, "12 ", "F - test-run-move-cursor-into-trace/trace-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " ", "F - test-run-move-cursor-into-trace/trace-0/cursor" + check-screen-row screen, 1/y, "... ", "F - test-run-move-cursor-into-trace/trace-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "||| ", "F - test-run-move-cursor-into-trace/trace-1/cursor" + check-screen-row screen, 2/y, "=> 12 ", "F - test-run-move-cursor-into-trace/trace-2" + check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, " ", "F - test-run-move-cursor-into-trace/trace-2/cursor" + # move cursor into input + edit-sandbox sandbox, 9/tab + # + render-sandbox screen, sandbox, 0/x, 0/y, 0x80/width, 0x10/height + check-screen-row screen, 0/y, "12 ", "F - test-run-move-cursor-into-trace/input-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " | ", "F - test-run-move-cursor-into-trace/input-0/cursor" + check-screen-row screen, 1/y, "... ", "F - test-run-move-cursor-into-trace/input-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-run-move-cursor-into-trace/input-1/cursor" + check-screen-row screen, 2/y, "=> 12 ", "F - test-run-move-cursor-into-trace/input-2" + check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, " ", "F - test-run-move-cursor-into-trace/input-2/cursor" +} diff --git a/shell/tokenize.mu b/shell/tokenize.mu new file mode 100644 index 00000000..7beedf23 --- /dev/null +++ b/shell/tokenize.mu @@ -0,0 +1,422 @@ +# We reuse the cell data structure for tokenization +# Token cells are special, though. They have no type, they're always atoms, +# they always have text-data. + +fn tokenize in: (addr gap-buffer), out: (addr stream cell), trace: (addr trace) { + trace-text trace, "read", "tokenize" + trace-lower trace + rewind-gap-buffer in + var token-storage: cell + var token/edx: (addr cell) <- address token-storage + { + var done?/eax: boolean <- gap-buffer-scan-done? in + compare done?, 0/false + break-if-!= + # initialize token data each iteration to avoid aliasing + var dest-ah/eax: (addr handle stream byte) <- get token, text-data + populate-stream dest-ah, 0x40/max-token-size + # + next-token in, token, trace + var error?/eax: boolean <- has-errors? trace + compare error?, 0/false + { + break-if-= + return + } + write-to-stream out, token # shallow-copy text-data + loop + } + trace-higher trace +} + +fn next-token in: (addr gap-buffer), _out-cell: (addr cell), trace: (addr trace) { + trace-text trace, "read", "next-token" + trace-lower trace + var out-cell/eax: (addr cell) <- copy _out-cell + var out-ah/eax: (addr handle stream byte) <- get out-cell, text-data + var _out/eax: (addr stream byte) <- lookup *out-ah + var out/edi: (addr stream byte) <- copy _out + $next-token:body: { + clear-stream out + skip-whitespace-from-gap-buffer in + var g/eax: grapheme <- peek-from-gap-buffer in + { + 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 + } + # digit + { + var digit?/eax: boolean <- is-decimal-digit? g + compare digit?, 0/false + break-if-= + next-number-token in, out, trace + break $next-token:body + } + # other symbol char + { + var symbol?/eax: boolean <- is-symbol-grapheme? g + compare symbol?, 0/false + break-if-= + next-symbol-token in, out, trace + break $next-token:body + } + # brackets are always single-char tokens + { + var bracket?/eax: boolean <- is-bracket-grapheme? g + compare bracket?, 0/false + break-if-= + var g/eax: grapheme <- read-from-gap-buffer in + next-bracket-token g, out, trace + break $next-token:body + } + } + trace-higher trace + var stream-storage: (stream byte 0x40) + var stream/eax: (addr stream byte) <- address stream-storage + write stream, "=> " + rewind-stream out + write-stream stream, out + trace trace, "read", 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-lower trace + $next-symbol-token:loop: { + var done?/eax: boolean <- gap-buffer-scan-done? in + compare done?, 0/false + break-if-!= + var g/eax: grapheme <- peek-from-gap-buffer in + { + 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 + } + # if non-symbol, return + { + var symbol-grapheme?/eax: boolean <- is-symbol-grapheme? g + compare symbol-grapheme?, 0/false + break-if-!= + trace-text trace, "read", "stop" + break $next-symbol-token:loop + } + var g/eax: grapheme <- read-from-gap-buffer in + write-grapheme out, g + loop + } + trace-higher trace + 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 +} + +fn next-number-token in: (addr gap-buffer), out: (addr stream byte), trace: (addr trace) { + trace-text trace, "read", "looking for a number" + trace-lower trace + $next-number-token:loop: { + var done?/eax: boolean <- gap-buffer-scan-done? in + compare done?, 0/false + break-if-!= + var g/eax: grapheme <- peek-from-gap-buffer in + { + 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 + } + # if not symbol grapheme, return + { + var symbol-grapheme?/eax: boolean <- is-symbol-grapheme? g + compare symbol-grapheme?, 0/false + break-if-!= + trace-text trace, "read", "stop" + break $next-number-token:loop + } + # if not digit grapheme, abort + { + var digit?/eax: boolean <- is-decimal-digit? g + compare digit?, 0/false + break-if-!= + error trace, "invalid number" + return + } + trace-text trace, "read", "append" + var g/eax: grapheme <- read-from-gap-buffer in + write-grapheme out, g + loop + } + trace-higher trace +} + +fn next-bracket-token g: grapheme, out: (addr stream byte), trace: (addr trace) { + trace-text trace, "read", "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 +} + +fn is-symbol-grapheme? g: grapheme -> _/eax: boolean { + ## whitespace + compare g, 9/tab + { + break-if-!= + return 0/false + } + compare g, 0xa/newline + { + break-if-!= + return 0/false + } + compare g, 0x20/space + { + break-if-!= + return 0/false + } + ## quotes + compare g, 0x22/double-quote + { + break-if-!= + return 0/false + } + compare g, 0x27/single-quote + { + break-if-!= + return 0/false + } + compare g, 0x60/backquote + { + break-if-!= + return 0/false + } + ## brackets + compare g, 0x28/open-paren + { + break-if-!= + return 0/false + } + compare g, 0x29/close-paren + { + break-if-!= + return 0/false + } + compare g, 0x5b/open-square-bracket + { + break-if-!= + return 0/false + } + compare g, 0x5d/close-square-bracket + { + break-if-!= + return 0/false + } + compare g, 0x7b/open-curly-bracket + { + break-if-!= + return 0/false + } + compare g, 0x7d/close-curly-bracket + { + break-if-!= + return 0/false + } + # - other punctuation + # '!' is a symbol char + compare g, 0x23/hash + { + break-if-!= + return 0/false + } + # '$' is a symbol char + compare g, 0x25/percent + { + break-if-!= + return 0/false + } + compare g, 0x26/ampersand + { + break-if-!= + return 0/false + } + compare g, 0x2a/asterisk + { + break-if-!= + return 0/false + } + compare g, 0x2b/plus + { + break-if-!= + return 0/false + } + compare g, 0x2c/comma + { + break-if-!= + return 0/false + } + # '-' is a symbol char + compare g, 0x2e/period + { + break-if-!= + return 0/false + } + compare g, 0x2f/slash + { + break-if-!= + return 0/false + } + compare g, 0x2f/slash + { + break-if-!= + return 0/false + } + compare g, 0x3a/colon + { + break-if-!= + return 0/false + } + compare g, 0x3b/semi-colon + { + break-if-!= + return 0/false + } + compare g, 0x3c/less-than + { + break-if-!= + return 0/false + } + compare g, 0x3d/equal + { + break-if-!= + return 0/false + } + compare g, 0x3e/greater-than + { + break-if-!= + return 0/false + } + # '?' is a symbol char + compare g, 0x40/at-sign + { + break-if-!= + return 0/false + } + compare g, 0x5c/backslash + { + break-if-!= + return 0/false + } + compare g, 0x5e/caret + { + break-if-!= + return 0/false + } + # '_' is a symbol char + compare g, 0x7c/vertical-line + { + break-if-!= + return 0/false + } + compare g, 0x7e/tilde + { + break-if-!= + return 0/false + } + return 1/true +} + +fn is-bracket-grapheme? g: grapheme -> _/eax: boolean { + compare g, 0x28/open-paren + { + break-if-!= + return 1/true + } + compare g, 0x29/close-paren + { + break-if-!= + return 1/true + } + compare g, 0x5b/open-square-bracket + { + break-if-!= + return 1/true + } + compare g, 0x5d/close-square-bracket + { + break-if-!= + return 1/true + } + compare g, 0x7b/open-curly-bracket + { + break-if-!= + return 1/true + } + compare g, 0x7d/close-curly-bracket + { + break-if-!= + return 1/true + } + return 0/false +} + +fn is-number-token? _in: (addr cell) -> _/eax: boolean { + var in/eax: (addr cell) <- copy _in + var in-data-ah/eax: (addr handle stream byte) <- get in, text-data + var in-data/eax: (addr stream byte) <- lookup *in-data-ah + rewind-stream in-data + var g/eax: grapheme <- read-grapheme in-data + var result/eax: boolean <- is-decimal-digit? g + return result +} + +fn is-bracket-token? _in: (addr cell) -> _/eax: boolean { + var in/eax: (addr cell) <- copy _in + var in-data-ah/eax: (addr handle stream byte) <- get in, text-data + var in-data/eax: (addr stream byte) <- lookup *in-data-ah + rewind-stream in-data + var g/eax: grapheme <- read-grapheme in-data + var result/eax: boolean <- is-bracket-grapheme? g + return result +} + +fn is-open-paren-token? _in: (addr cell) -> _/eax: boolean { + var in/eax: (addr cell) <- copy _in + var in-data-ah/eax: (addr handle stream byte) <- get in, text-data + var in-data/eax: (addr stream byte) <- lookup *in-data-ah + rewind-stream in-data + var g/eax: grapheme <- read-grapheme in-data + compare g, 0x28/open-paren + { + break-if-!= + return 1/true + } + return 0/false +} + +fn is-close-paren-token? _in: (addr cell) -> _/eax: boolean { + var in/eax: (addr cell) <- copy _in + var in-data-ah/eax: (addr handle stream byte) <- get in, text-data + var in-data/eax: (addr stream byte) <- lookup *in-data-ah + rewind-stream in-data + var g/eax: grapheme <- read-grapheme in-data + compare g, 0x29/open-paren + { + break-if-!= + return 1/true + } + return 0/false +} diff --git a/shell/trace.mu b/shell/trace.mu new file mode 100644 index 00000000..7fd52ceb --- /dev/null +++ b/shell/trace.mu @@ -0,0 +1,1449 @@ +# A trace records the evolution of a computation. +# An integral part of the Mu Shell is facilities for browsing traces. + +type trace { + # steady-state life cycle of a trace: + # reload loop: + # there are already some visible lines + # append a bunch of new trace lines to the trace + # render loop: + # rendering displays trace lines that match visible lines + # rendering computes cursor-line based on the cursor-y coordinate + # edit-trace updates cursor-y coordinate + # edit-trace might add/remove lines to visible + curr-depth: int # depth that will be assigned to next line appended + data: (handle array trace-line) + first-free: int + visible: (handle array trace-line) + recompute-visible?: boolean + top-line-index: int # index into data + cursor-y: int # row index on screen + cursor-line-index: int # index into data +} + +type trace-line { + depth: int + label: (handle array byte) + data: (handle array byte) + visible?: boolean +} + +fn initialize-trace _self: (addr trace), capacity: int, visible-capacity: int { + var self/esi: (addr trace) <- copy _self + compare self, 0 + break-if-= + var trace-ah/eax: (addr handle array trace-line) <- get self, data + populate trace-ah, capacity + var visible-ah/eax: (addr handle array trace-line) <- get self, visible + populate visible-ah, visible-capacity +} + +fn clear-trace _self: (addr trace) { + var self/eax: (addr trace) <- copy _self + compare self, 0 + break-if-= + var len/edx: (addr int) <- get self, first-free + copy-to *len, 0 + # might leak memory; existing elements won't be used anymore +} + +fn mark-lines-dirty _self: (addr trace) { + var self/eax: (addr trace) <- copy _self + var dest/edx: (addr boolean) <- get self, recompute-visible? + copy-to *dest, 1/true +} + +fn mark-lines-clean _self: (addr trace) { + var self/eax: (addr trace) <- copy _self + var dest/edx: (addr boolean) <- get self, recompute-visible? + copy-to *dest, 0/false +} + +fn has-errors? _self: (addr trace) -> _/eax: boolean { + var self/eax: (addr trace) <- copy _self + var max/edx: (addr int) <- get self, first-free + var trace-ah/eax: (addr handle array trace-line) <- get self, data + var _trace/eax: (addr array trace-line) <- lookup *trace-ah + var trace/esi: (addr array trace-line) <- copy _trace + var i/ecx: int <- copy 0 + { + compare i, *max + break-if->= + var offset/eax: (offset trace-line) <- compute-offset trace, i + var curr/eax: (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 + var is-error?/eax: boolean <- string-equal? curr-label, "error" + compare is-error?, 0/false + { + break-if-= + return 1/true + } + i <- increment + loop + } + return 0/false +} + +fn trace _self: (addr trace), label: (addr array byte), message: (addr stream byte) { + var self/esi: (addr trace) <- copy _self + compare self, 0 + break-if-= + var data-ah/eax: (addr handle array trace-line) <- get self, data + var data/eax: (addr array trace-line) <- lookup *data-ah + var index-addr/edi: (addr int) <- get self, first-free + var index/ecx: int <- copy *index-addr + var offset/ecx: (offset trace-line) <- compute-offset data, index + var dest/eax: (addr trace-line) <- index data, offset + var depth/ecx: (addr int) <- get self, curr-depth + rewind-stream message + initialize-trace-line *depth, label, message, dest + increment *index-addr +} + +fn trace-text self: (addr trace), label: (addr array byte), s: (addr array byte) { + compare self, 0 + break-if-= + var data-storage: (stream byte 0x100) + var data/eax: (addr stream byte) <- address data-storage + write data, s + trace self, label, data +} + +fn error self: (addr trace), message: (addr array byte) { + trace-text self, "error", message +} + +fn initialize-trace-line depth: int, label: (addr array byte), data: (addr stream byte), _out: (addr trace-line) { + var out/edi: (addr trace-line) <- copy _out + # depth + var src/eax: int <- copy depth + var dest/ecx: (addr int) <- get out, depth + copy-to *dest, src + # label + var dest/eax: (addr handle array byte) <- get out, label + copy-array-object label, dest + # data + var dest/eax: (addr handle array byte) <- get out, data + stream-to-array data, dest +} + +fn trace-lower _self: (addr trace) { + var self/esi: (addr trace) <- copy _self + compare self, 0 + break-if-= + var depth/eax: (addr int) <- get self, curr-depth + increment *depth +} + +fn trace-higher _self: (addr trace) { + var self/esi: (addr trace) <- copy _self + compare self, 0 + break-if-= + var depth/eax: (addr int) <- get self, curr-depth + decrement *depth +} + +fn render-trace screen: (addr screen), _self: (addr trace), xmin: int, ymin: int, xmax: int, ymax: int, show-cursor?: boolean -> _/ecx: int { + var already-hiding-lines?: boolean + var y/ecx: int <- copy ymin + var self/esi: (addr trace) <- copy _self + compare self, 0 + { + break-if-!= + return ymin + } + clamp-cursor-to-top self, y + 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 + $render-trace:loop: { + compare i, max + break-if->= + $render-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 + var bg/edi: int <- copy 0/black + compare show-cursor?, 0/false + { + break-if-= + var cursor-y/eax: (addr int) <- get self, cursor-y + compare *cursor-y, y + break-if-!= + bg <- copy 7/cursor-line-bg + var cursor-line-index/eax: (addr int) <- get self, cursor-line-index + copy-to *cursor-line-index, i + } + # always display errors + var is-error?/eax: boolean <- string-equal? curr-label, "error" + { + compare is-error?, 0/false + break-if-= + y <- render-trace-line screen, curr, xmin, y, xmax, ymax, 0xc/fg=trace-error, bg + copy-to already-hiding-lines?, 0/false + break $render-trace:iter + } + # display expanded lines + var display?/eax: boolean <- should-render? self, curr + { + compare display?, 0/false + break-if-= + y <- render-trace-line screen, curr, xmin, y, xmax, ymax, 9/fg=blue, bg + copy-to already-hiding-lines?, 0/false + break $render-trace:iter + } + # ignore the rest + compare already-hiding-lines?, 0/false + { + break-if-!= + var x/eax: int <- copy xmin + x, y <- draw-text-wrapping-right-then-down screen, "...", xmin, ymin, xmax, ymax, x, y, 9/fg=trace, bg + y <- increment + copy-to already-hiding-lines?, 1/true + } + } + i <- increment + loop + } + # prevent cursor from going too far down + clamp-cursor-to-bottom self, y, screen, xmin, ymin, xmax, ymax + mark-lines-clean self + return y +} + +fn render-trace-line screen: (addr screen), _self: (addr trace-line), xmin: int, ymin: int, xmax: int, ymax: int, fg: int, bg: int -> _/ecx: int { + var self/esi: (addr trace-line) <- copy _self + var xsave/edx: int <- copy xmin + var y/ecx: int <- copy ymin + var label-ah/eax: (addr handle array byte) <- get self, label + var _label/eax: (addr array byte) <- lookup *label-ah + var label/ebx: (addr array byte) <- copy _label + var is-error?/eax: boolean <- string-equal? label, "error" + compare is-error?, 0/false + { + break-if-!= + var x/eax: int <- copy xsave + { + var depth/edx: (addr int) <- get self, depth + x, y <- draw-int32-decimal-wrapping-right-then-down screen, *depth, xmin, ymin, xmax, ymax, x, y, fg, bg + x, y <- draw-text-wrapping-right-then-down screen, " ", xmin, ymin, xmax, ymax, x, y, fg, bg + # don't show label in UI; it's just for tests + } + xsave <- copy x + } + var data-ah/eax: (addr handle array byte) <- get self, data + var _data/eax: (addr array byte) <- lookup *data-ah + var data/ebx: (addr array byte) <- copy _data + var x/eax: int <- copy xsave + x, y <- draw-text-wrapping-right-then-down screen, data, xmin, ymin, xmax, ymax, x, y, fg, bg + y <- increment + return y +} + +fn should-render? _self: (addr trace), _line: (addr trace-line) -> _/eax: boolean { + var self/esi: (addr trace) <- copy _self + # if visible? is already cached, just return it + var dest/edx: (addr boolean) <- get self, recompute-visible? + compare *dest, 0/false + { + break-if-!= + var line/eax: (addr trace-line) <- copy _line + var result/eax: (addr boolean) <- get line, visible? + return *result + } + # recompute + var candidates-ah/eax: (addr handle array trace-line) <- get self, visible + var candidates/eax: (addr array trace-line) <- lookup *candidates-ah + var i/ecx: int <- copy 0 + var len/edx: int <- length candidates + { + compare i, len + break-if->= + { + var curr-offset/ecx: (offset trace-line) <- compute-offset candidates, i + var curr/ecx: (addr trace-line) <- index candidates, curr-offset + var match?/eax: boolean <- trace-lines-equal? curr, _line + compare match?, 0/false + break-if-= + var line/eax: (addr trace-line) <- copy _line + var dest/eax: (addr boolean) <- get line, visible? + copy-to *dest, 1/true + return 1/true + } + i <- increment + loop + } + var line/eax: (addr trace-line) <- copy _line + var dest/eax: (addr boolean) <- get line, visible? + copy-to *dest, 0/false + return 0/false +} + +# this is probably super-inefficient, string comparing every trace line +# against every visible line on every render +fn trace-lines-equal? _a: (addr trace-line), _b: (addr trace-line) -> _/eax: boolean { + var a/esi: (addr trace-line) <- copy _a + var b/edi: (addr trace-line) <- copy _b + var a-depth/ecx: (addr int) <- get a, depth + var b-depth/edx: (addr int) <- get b, depth + var benchmark/eax: int <- copy *b-depth + compare *a-depth, benchmark + { + break-if-= + return 0/false + } + var a-label-ah/eax: (addr handle array byte) <- get a, label + var _a-label/eax: (addr array byte) <- lookup *a-label-ah + var a-label/ecx: (addr array byte) <- copy _a-label + var b-label-ah/ebx: (addr handle array byte) <- get b, label + var b-label/eax: (addr array byte) <- lookup *b-label-ah + var label-match?/eax: boolean <- string-equal? a-label, b-label + { + compare label-match?, 0/false + break-if-!= + return 0/false + } + var a-data-ah/eax: (addr handle array byte) <- get a, data + var _a-data/eax: (addr array byte) <- lookup *a-data-ah + var a-data/ecx: (addr array byte) <- copy _a-data + var b-data-ah/ebx: (addr handle array byte) <- get b, data + var b-data/eax: (addr array byte) <- lookup *b-data-ah + var data-match?/eax: boolean <- string-equal? a-data, b-data + return data-match? +} + +fn clamp-cursor-to-top _self: (addr trace), _y: int { + var y/ecx: int <- copy _y + var self/esi: (addr trace) <- copy _self + var cursor-y/eax: (addr int) <- get self, cursor-y + compare *cursor-y, y + break-if->= + copy-to *cursor-y, y +} + +# extremely hacky; consider deleting test-render-trace-empty-3 when you clean this up +fn clamp-cursor-to-bottom _self: (addr trace), _y: int, screen: (addr screen), xmin: int, ymin: int, xmax: int, ymax: int { + var y/ebx: int <- copy _y + compare y, ymin + { + break-if-> + return + } + y <- decrement + var self/esi: (addr trace) <- copy _self + var cursor-y/eax: (addr int) <- get self, cursor-y + compare *cursor-y, y + break-if-<= + copy-to *cursor-y, y + # redraw cursor-line + # TODO: ugly duplication + var trace-ah/eax: (addr handle array trace-line) <- get self, data + var trace/eax: (addr array trace-line) <- lookup *trace-ah + var cursor-line-index-addr/ecx: (addr int) <- get self, cursor-line-index + var cursor-line-index/ecx: int <- copy *cursor-line-index-addr + var first-free/edx: (addr int) <- get self, first-free + compare cursor-line-index, *first-free + { + break-if-< + return + } + var cursor-offset/ecx: (offset trace-line) <- compute-offset trace, cursor-line-index + var cursor-line/ecx: (addr trace-line) <- index trace, cursor-offset + var display?/eax: boolean <- should-render? self, cursor-line + { + compare display?, 0/false + break-if-= + var dummy/ecx: int <- render-trace-line screen, cursor-line, xmin, y, xmax, ymax, 9/fg=blue, 7/cursor-line-bg + return + } + var dummy1/eax: int <- copy 0 + var dummy2/ecx: int <- copy 0 + dummy1, dummy2 <- draw-text-wrapping-right-then-down screen, "...", xmin, ymin, xmax, ymax, xmin, y, 9/fg=trace, 7/cursor-line-bg +} + +fn test-render-trace-empty { + var t-storage: trace + var t/esi: (addr trace) <- address t-storage + initialize-trace t, 0x10, 0x10 + # setup: screen + var screen-on-stack: screen + var screen/edi: (addr screen) <- address screen-on-stack + initialize-screen screen, 5/width, 4/height + # + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 5/xmax, 4/ymax, 0/no-cursor + # + check-ints-equal y, 0, "F - test-render-trace-empty/cursor" + check-screen-row screen, 0/y, " ", "F - test-render-trace-empty" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " ", "F - test-render-trace-empty/bg" +} + +fn test-render-trace-empty-2 { + var t-storage: trace + var t/esi: (addr trace) <- address t-storage + initialize-trace t, 0x10, 0x10 + # setup: screen + var screen-on-stack: screen + var screen/edi: (addr screen) <- address screen-on-stack + initialize-screen screen, 5/width, 4/height + # + var y/ecx: int <- render-trace screen, t, 0/xmin, 2/ymin, 5/xmax, 4/ymax, 0/no-cursor # cursor below top row + # + check-ints-equal y, 2, "F - test-render-trace-empty-2/cursor" + check-screen-row screen, 2/y, " ", "F - test-render-trace-empty-2" + check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, " ", "F - test-render-trace-empty-2/bg" +} + +fn test-render-trace-empty-3 { + var t-storage: trace + var t/esi: (addr trace) <- address t-storage + initialize-trace t, 0x10, 0x10 + # setup: screen + var screen-on-stack: screen + var screen/edi: (addr screen) <- address screen-on-stack + initialize-screen screen, 5/width, 4/height + # + var y/ecx: int <- render-trace screen, t, 0/xmin, 2/ymin, 5/xmax, 4/ymax, 1/show-cursor # try show cursor + # still no cursor to show + check-ints-equal y, 2, "F - test-render-trace-empty-3/cursor" + check-screen-row screen, 1/y, " ", "F - test-render-trace-empty-3/line-above-cursor" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-render-trace-empty-3/bg-for-line-above-cursor" + check-screen-row screen, 2/y, " ", "F - test-render-trace-empty-3" + check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, " ", "F - test-render-trace-empty-3/bg" +} + +fn test-render-trace-collapsed-by-default { + var t-storage: trace + var t/esi: (addr trace) <- address t-storage + initialize-trace t, 0x10, 0x10 + trace-text t, "l", "data" + # setup: screen + var screen-on-stack: screen + var screen/edi: (addr screen) <- address screen-on-stack + initialize-screen screen, 5/width, 4/height + # + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 5/xmax, 4/ymax, 0/no-cursor + # + check-ints-equal y, 1, "F - test-render-trace-collapsed-by-default/cursor" + check-screen-row screen, 0/y, "... ", "F - test-render-trace-collapsed-by-default" +} + +fn test-render-trace-error { + var t-storage: trace + var t/esi: (addr trace) <- address t-storage + initialize-trace t, 0x10, 0x10 + error t, "error" + # setup: screen + var screen-on-stack: screen + var screen/edi: (addr screen) <- address screen-on-stack + initialize-screen screen, 0xa/width, 4/height + # + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0xa/xmax, 4/ymax, 0/no-cursor + # + check-ints-equal y, 1, "F - test-render-trace-error/cursor" + check-screen-row screen, 0/y, "error", "F - test-render-trace-error" +} + +fn test-render-trace-error-at-start { + var t-storage: trace + var t/esi: (addr trace) <- address t-storage + initialize-trace t, 0x10, 0x10 + # + error t, "error" + trace-text t, "l", "data" + # setup: screen + var screen-on-stack: screen + var screen/edi: (addr screen) <- address screen-on-stack + initialize-screen screen, 0xa/width, 4/height + # + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0xa/xmax, 4/ymax, 0/no-cursor + # + check-ints-equal y, 2, "F - test-render-trace-error-at-start/cursor" + check-screen-row screen, 0/y, "error", "F - test-render-trace-error-at-start/0" + check-screen-row screen, 1/y, "... ", "F - test-render-trace-error-at-start/1" +} + +fn test-render-trace-error-at-end { + var t-storage: trace + var t/esi: (addr trace) <- address t-storage + initialize-trace t, 0x10, 0x10 + # + trace-text t, "l", "data" + error t, "error" + # setup: screen + var screen-on-stack: screen + var screen/edi: (addr screen) <- address screen-on-stack + initialize-screen screen, 0xa/width, 4/height + # + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0xa/xmax, 4/ymax, 0/no-cursor + # + check-ints-equal y, 2, "F - test-render-trace-error-at-end/cursor" + check-screen-row screen, 0/y, "... ", "F - test-render-trace-error-at-end/0" + check-screen-row screen, 1/y, "error", "F - test-render-trace-error-at-end/1" +} + +fn test-render-trace-error-in-the-middle { + var t-storage: trace + var t/esi: (addr trace) <- address t-storage + initialize-trace t, 0x10, 0x10 + # + trace-text t, "l", "line 1" + error t, "error" + trace-text t, "l", "line 3" + # setup: screen + var screen-on-stack: screen + var screen/edi: (addr screen) <- address screen-on-stack + initialize-screen screen, 0xa/width, 4/height + # + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0xa/xmax, 4/ymax, 0/no-cursor + # + check-ints-equal y, 3, "F - test-render-trace-error-in-the-middle/cursor" + check-screen-row screen, 0/y, "... ", "F - test-render-trace-error-in-the-middle/0" + check-screen-row screen, 1/y, "error", "F - test-render-trace-error-in-the-middle/1" + check-screen-row screen, 2/y, "... ", "F - test-render-trace-error-in-the-middle/2" +} + +fn test-render-trace-cursor-in-single-line { + var t-storage: trace + var t/esi: (addr trace) <- address t-storage + initialize-trace t, 0x10, 0x10 + # + trace-text t, "l", "line 1" + error t, "error" + trace-text t, "l", "line 3" + # setup: screen + var screen-on-stack: screen + var screen/edi: (addr screen) <- address screen-on-stack + initialize-screen screen, 0xa/width, 4/height + # + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0xa/xmax, 4/ymax, 1/show-cursor + # + check-screen-row screen, 0/y, "... ", "F - test-render-trace-cursor-in-single-line/0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||| ", "F - test-render-trace-cursor-in-single-line/0/cursor" + check-screen-row screen, 1/y, "error ", "F - test-render-trace-cursor-in-single-line/1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-render-trace-cursor-in-single-line/1/cursor" + check-screen-row screen, 2/y, "... ", "F - test-render-trace-cursor-in-single-line/2" + check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, " ", "F - test-render-trace-cursor-in-single-line/2/cursor" +} + +fn render-trace-menu screen: (addr screen) { + var width/eax: int <- copy 0 + var height/ecx: int <- copy 0 + width, height <- screen-size screen + var y/ecx: int <- copy height + y <- decrement + set-cursor-position screen, 0/x, y + draw-text-rightward-from-cursor screen, " ctrl-s ", width, 0/fg, 7/bg=grey + draw-text-rightward-from-cursor screen, " run sandbox ", width, 7/fg, 0/bg + draw-text-rightward-from-cursor screen, " ctrl-d ", width, 0/fg, 7/bg=grey + draw-text-rightward-from-cursor screen, " cursor down ", width, 7/fg, 0/bg + draw-text-rightward-from-cursor screen, " ctrl-u ", width, 0/fg, 7/bg=grey + draw-text-rightward-from-cursor screen, " cursor up ", width, 7/fg, 0/bg + draw-text-rightward-from-cursor screen, " tab ", width, 0/fg, 3/bg=cyan + draw-text-rightward-from-cursor screen, " move to sandbox ", width, 7/fg, 0/bg + draw-text-rightward-from-cursor screen, " enter ", width, 0/fg, 7/bg=grey + draw-text-rightward-from-cursor screen, " expand ", width, 7/fg, 0/bg + draw-text-rightward-from-cursor screen, " backspace ", width, 0/fg, 7/bg=grey + draw-text-rightward-from-cursor screen, " collapse ", width, 7/fg, 0/bg +} + +fn edit-trace _self: (addr trace), key: grapheme { + var self/esi: (addr trace) <- copy _self + # cursor down + { + compare key, 4/ctrl-d + break-if-!= + var cursor-y/eax: (addr int) <- get self, cursor-y + increment *cursor-y + return + } + # cursor up + { + compare key, 0x15/ctrl-u + break-if-!= + var cursor-y/eax: (addr int) <- get self, cursor-y + decrement *cursor-y + return + } + # enter = expand + { + compare key, 0xa/newline + break-if-!= + expand self + return + } + # backspace = collapse + { + compare key, 8/backspace + break-if-!= + collapse self + return + } +} + +fn expand _self: (addr trace) { + var self/esi: (addr trace) <- copy _self + 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 cursor-line-index-addr/ecx: (addr int) <- get self, cursor-line-index + var cursor-line-index/ecx: int <- copy *cursor-line-index-addr + var cursor-line-offset/eax: (offset trace-line) <- compute-offset trace, cursor-line-index + var cursor-line/edx: (addr trace-line) <- index trace, cursor-line-offset + var cursor-line-visible?/eax: (addr boolean) <- get cursor-line, visible? + var cursor-line-depth/ebx: (addr int) <- get cursor-line, depth + var target-depth/ebx: int <- copy *cursor-line-depth + # if cursor-line is already visible, increment target-depth + compare *cursor-line-visible?, 0/false + { + break-if-= + target-depth <- increment + } + # reveal the run of lines starting at cursor-line-index with depth target-depth + var i/ecx: int <- copy cursor-line-index + var max/edx: (addr int) <- get self, first-free + { + compare i, *max + break-if->= + var curr-line-offset/eax: (offset trace-line) <- compute-offset trace, i + var curr-line/edx: (addr trace-line) <- index trace, curr-line-offset + var curr-line-depth/eax: (addr int) <- get curr-line, depth + compare *curr-line-depth, target-depth + break-if-< + { + break-if-!= + var curr-line-visible?/eax: (addr boolean) <- get curr-line, visible? + copy-to *curr-line-visible?, 1/true + reveal-trace-line self, curr-line + } + i <- increment + loop + } +} + +fn collapse _self: (addr trace) { + var self/esi: (addr trace) <- copy _self + 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 cursor-line-index-addr/ecx: (addr int) <- get self, cursor-line-index + var cursor-line-index/ecx: int <- copy *cursor-line-index-addr + var cursor-line-offset/eax: (offset trace-line) <- compute-offset trace, cursor-line-index + var cursor-line/edx: (addr trace-line) <- index trace, cursor-line-offset + var cursor-line-visible?/eax: (addr boolean) <- get cursor-line, visible? + # if cursor-line is not visible, do nothing + compare *cursor-line-visible?, 0/false + { + break-if-!= + return + } + # hide all lines between previous and next line with a lower depth + var cursor-line-depth/ebx: (addr int) <- get cursor-line, depth + var cursor-y/edx: (addr int) <- get self, cursor-y + var target-depth/ebx: int <- copy *cursor-line-depth + var i/ecx: int <- copy cursor-line-index + $collapse:loop1: { + compare i, 0 + break-if-< + var curr-line-offset/eax: (offset trace-line) <- compute-offset trace, i + var curr-line/eax: (addr trace-line) <- index trace, curr-line-offset + { + var curr-line-depth/eax: (addr int) <- get curr-line, depth + compare *curr-line-depth, target-depth + break-if-< $collapse:loop1 + } + # if cursor-line is visible, decrement cursor-y + { + var curr-line-visible?/eax: (addr boolean) <- get curr-line, visible? + compare *curr-line-visible?, 0/false + break-if-= + decrement *cursor-y + } + i <- decrement + loop + } + i <- increment + var max/edx: (addr int) <- get self, first-free + $collapse:loop2: { + compare i, *max + break-if->= + var curr-line-offset/eax: (offset trace-line) <- compute-offset trace, i + var curr-line/edx: (addr trace-line) <- index trace, curr-line-offset + var curr-line-depth/eax: (addr int) <- get curr-line, depth + compare *curr-line-depth, target-depth + break-if-< + { + hide-trace-line self, curr-line + var curr-line-visible?/eax: (addr boolean) <- get curr-line, visible? + copy-to *curr-line-visible?, 0/false + } + i <- increment + loop + } +} + +# the 'visible' array is not required to be in order +# elements can also be deleted out of order +# so it can have holes +# however, lines in it always have visible? set +# we'll use visible? being unset as a sign of emptiness +fn reveal-trace-line _self: (addr trace), line: (addr trace-line) { + var self/esi: (addr trace) <- copy _self + var visible-ah/eax: (addr handle array trace-line) <- get self, visible + var visible/eax: (addr array trace-line) <- lookup *visible-ah + var i/ecx: int <- copy 0 + var len/edx: int <- length visible + { + compare i, len + break-if->= + var curr-offset/edx: (offset trace-line) <- compute-offset visible, i + var curr/edx: (addr trace-line) <- index visible, curr-offset + var curr-visible?/eax: (addr boolean) <- get curr, visible? + compare *curr-visible?, 0/false + { + break-if-!= + # empty slot found + copy-object line, curr + return + } + i <- increment + loop + } + abort "too many visible lines; increase size of array trace.visible" +} + +fn hide-trace-line _self: (addr trace), line: (addr trace-line) { + var self/esi: (addr trace) <- copy _self + var visible-ah/eax: (addr handle array trace-line) <- get self, visible + var visible/eax: (addr array trace-line) <- lookup *visible-ah + var i/ecx: int <- copy 0 + var len/edx: int <- length visible + { + compare i, len + break-if->= + var curr-offset/edx: (offset trace-line) <- compute-offset visible, i + var curr/edx: (addr trace-line) <- index visible, curr-offset + var found?/eax: boolean <- trace-lines-equal? curr, line + compare found?, 0/false + { + break-if-= + clear-object curr + } + i <- increment + loop + } +} + +fn test-cursor-down-and-up-within-trace { + var t-storage: trace + var t/esi: (addr trace) <- address t-storage + initialize-trace t, 0x10, 0x10 + # + trace-text t, "l", "line 1" + error t, "error" + trace-text t, "l", "line 3" + # setup: screen + var screen-on-stack: screen + var screen/edi: (addr screen) <- address screen-on-stack + initialize-screen screen, 0xa/width, 4/height + # + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0xa/xmax, 4/ymax, 1/show-cursor + # + check-screen-row screen, 0/y, "... ", "F - test-cursor-down-and-up-within-trace/pre-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||| ", "F - test-cursor-down-and-up-within-trace/pre-0/cursor" + check-screen-row screen, 1/y, "error ", "F - test-cursor-down-and-up-within-trace/pre-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-cursor-down-and-up-within-trace/pre-1/cursor" + check-screen-row screen, 2/y, "... ", "F - test-cursor-down-and-up-within-trace/pre-2" + check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, " ", "F - test-cursor-down-and-up-within-trace/pre-2/cursor" + # cursor down + edit-trace t, 4/ctrl-d + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0xa/xmax, 4/ymax, 1/show-cursor + # + check-screen-row screen, 0/y, "... ", "F - test-cursor-down-and-up-within-trace/down-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " ", "F - test-cursor-down-and-up-within-trace/down-0/cursor" + check-screen-row screen, 1/y, "error ", "F - test-cursor-down-and-up-within-trace/down-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "||||| ", "F - test-cursor-down-and-up-within-trace/down-1/cursor" + check-screen-row screen, 2/y, "... ", "F - test-cursor-down-and-up-within-trace/down-2" + check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, " ", "F - test-cursor-down-and-up-within-trace/down-2/cursor" + # cursor up + edit-trace t, 0x15/ctrl-u + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0xa/xmax, 4/ymax, 1/show-cursor + # + check-screen-row screen, 0/y, "... ", "F - test-cursor-down-and-up-within-trace/up-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||| ", "F - test-cursor-down-and-up-within-trace/up-0/cursor" + check-screen-row screen, 1/y, "error ", "F - test-cursor-down-and-up-within-trace/up-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-cursor-down-and-up-within-trace/up-1/cursor" + check-screen-row screen, 2/y, "... ", "F - test-cursor-down-and-up-within-trace/up-2" + check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, " ", "F - test-cursor-down-and-up-within-trace/up-2/cursor" +} + +fn test-cursor-down-past-bottom-of-trace { + var t-storage: trace + var t/esi: (addr trace) <- address t-storage + initialize-trace t, 0x10, 0x10 + # + trace-text t, "l", "line 1" + error t, "error" + trace-text t, "l", "line 3" + # setup: screen + var screen-on-stack: screen + var screen/edi: (addr screen) <- address screen-on-stack + initialize-screen screen, 0xa/width, 4/height + # + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0xa/xmax, 4/ymax, 1/show-cursor + # + check-screen-row screen, 0/y, "... ", "F - test-cursor-down-past-bottom-of-trace/pre-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||| ", "F - test-cursor-down-past-bottom-of-trace/pre-0/cursor" + check-screen-row screen, 1/y, "error ", "F - test-cursor-down-past-bottom-of-trace/pre-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-cursor-down-past-bottom-of-trace/pre-1/cursor" + check-screen-row screen, 2/y, "... ", "F - test-cursor-down-past-bottom-of-trace/pre-2" + check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, " ", "F - test-cursor-down-past-bottom-of-trace/pre-2/cursor" + # cursor down several times + edit-trace t, 4/ctrl-d + edit-trace t, 4/ctrl-d + edit-trace t, 4/ctrl-d + edit-trace t, 4/ctrl-d + edit-trace t, 4/ctrl-d + # hack: we do need to render to make this test pass; we're mixing state management with rendering + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0xa/xmax, 4/ymax, 1/show-cursor + # cursor clamps at bottom + check-screen-row screen, 0/y, "... ", "F - test-cursor-down-past-bottom-of-trace/down-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " ", "F - test-cursor-down-past-bottom-of-trace/down-0/cursor" + check-screen-row screen, 1/y, "error ", "F - test-cursor-down-past-bottom-of-trace/down-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-cursor-down-past-bottom-of-trace/down-1/cursor" + check-screen-row screen, 2/y, "... ", "F - test-cursor-down-past-bottom-of-trace/down-2" + check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "||| ", "F - test-cursor-down-past-bottom-of-trace/down-2/cursor" +} + +fn test-expand-within-trace { + var t-storage: trace + var t/esi: (addr trace) <- address t-storage + initialize-trace t, 0x10, 0x10 + # + trace-text t, "l", "line 1" + trace-text t, "l", "line 2" + # setup: screen + var screen-on-stack: screen + var screen/edi: (addr screen) <- address screen-on-stack + initialize-screen screen, 0x10/width, 4/height + # + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor + # + check-screen-row screen, 0/y, "... ", "F - test-expand-within-trace/pre-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||| ", "F - test-expand-within-trace/pre-0/cursor" + check-screen-row screen, 1/y, " ", "F - test-expand-within-trace/pre-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-expand-within-trace/pre-1/cursor" + # expand + edit-trace t, 0xa/enter + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor + # + check-screen-row screen, 0/y, "0 line 1 ", "F - test-expand-within-trace/expand-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||||||| ", "F - test-expand-within-trace/expand-0/cursor" + check-screen-row screen, 1/y, "0 line 2 ", "F - test-expand-within-trace/expand-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-expand-within-trace/expand-1/cursor" + check-screen-row screen, 2/y, " ", "F - test-expand-within-trace/expand-2" + check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, " ", "F - test-expand-within-trace/expand-2/cursor" +} + +fn test-trace-expand-skips-lower-depth { + var t-storage: trace + var t/esi: (addr trace) <- address t-storage + initialize-trace t, 0x10, 0x10 + # + trace-text t, "l", "line 1" + trace-lower t + trace-text t, "l", "line 2" + # setup: screen + var screen-on-stack: screen + var screen/edi: (addr screen) <- address screen-on-stack + initialize-screen screen, 0x10/width, 4/height + # + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor + # + check-screen-row screen, 0/y, "... ", "F - test-trace-expand-skips-lower-depth/pre-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||| ", "F - test-trace-expand-skips-lower-depth/pre-0/cursor" + check-screen-row screen, 1/y, " ", "F - test-trace-expand-skips-lower-depth/pre-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-trace-expand-skips-lower-depth/pre-1/cursor" + # expand + edit-trace t, 0xa/enter + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor + # + check-screen-row screen, 0/y, "0 line 1 ", "F - test-trace-expand-skips-lower-depth/expand-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||||||| ", "F - test-trace-expand-skips-lower-depth/expand-0/cursor" + check-screen-row screen, 1/y, "... ", "F - test-trace-expand-skips-lower-depth/expand-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-trace-expand-skips-lower-depth/expand-1/cursor" + check-screen-row screen, 2/y, " ", "F - test-trace-expand-skips-lower-depth/expand-2" + check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, " ", "F - test-trace-expand-skips-lower-depth/expand-2/cursor" +} + +fn test-trace-expand-continues-past-lower-depth { + var t-storage: trace + var t/esi: (addr trace) <- address t-storage + initialize-trace t, 0x10, 0x10 + # + trace-text t, "l", "line 1" + trace-lower t + trace-text t, "l", "line 1.1" + trace-higher t + trace-text t, "l", "line 2" + # setup: screen + var screen-on-stack: screen + var screen/edi: (addr screen) <- address screen-on-stack + initialize-screen screen, 0x10/width, 4/height + # + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor + # + check-screen-row screen, 0/y, "... ", "F - test-trace-expand-continues-past-lower-depth/pre-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||| ", "F - test-trace-expand-continues-past-lower-depth/pre-0/cursor" + check-screen-row screen, 1/y, " ", "F - test-trace-expand-continues-past-lower-depth/pre-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-trace-expand-continues-past-lower-depth/pre-1/cursor" + # expand + edit-trace t, 0xa/enter + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor + # + check-screen-row screen, 0/y, "0 line 1 ", "F - test-trace-expand-continues-past-lower-depth/expand-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||||||| ", "F - test-trace-expand-continues-past-lower-depth/expand-0/cursor" + # TODO: might be too wasteful to show every place where lines are hidden + check-screen-row screen, 1/y, "... ", "F - test-trace-expand-continues-past-lower-depth/expand-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-trace-expand-continues-past-lower-depth/expand-1/cursor" + check-screen-row screen, 2/y, "0 line 2 ", "F - test-trace-expand-continues-past-lower-depth/expand-2" + check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, " ", "F - test-trace-expand-continues-past-lower-depth/expand-2/cursor" +} + +fn test-trace-expand-stops-at-higher-depth { + var t-storage: trace + var t/esi: (addr trace) <- address t-storage + initialize-trace t, 0x10, 0x10 + # + trace-text t, "l", "line 1.1" + trace-lower t + trace-text t, "l", "line 1.1.1" + trace-higher t + trace-text t, "l", "line 1.2" + trace-higher t + trace-text t, "l", "line 2" + trace-lower t + trace-text t, "l", "line 2.1" + # setup: screen + var screen-on-stack: screen + var screen/edi: (addr screen) <- address screen-on-stack + initialize-screen screen, 0x10/width, 8/height + # + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 8/ymax, 1/show-cursor + # + check-screen-row screen, 0/y, "... ", "F - test-trace-expand-stops-at-higher-depth/pre-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||| ", "F - test-trace-expand-stops-at-higher-depth/pre-0/cursor" + check-screen-row screen, 1/y, " ", "F - test-trace-expand-stops-at-higher-depth/pre-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-trace-expand-stops-at-higher-depth/pre-1/cursor" + # expand + edit-trace t, 0xa/enter + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 8/ymax, 1/show-cursor + # + check-screen-row screen, 0/y, "0 line 1.1 ", "F - test-trace-expand-stops-at-higher-depth/expand-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||||||||| ", "F - test-trace-expand-stops-at-higher-depth/expand-0/cursor" + check-screen-row screen, 1/y, "... ", "F - test-trace-expand-stops-at-higher-depth/expand-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-trace-expand-stops-at-higher-depth/expand-1/cursor" + check-screen-row screen, 2/y, "0 line 1.2 ", "F - test-trace-expand-stops-at-higher-depth/expand-2" + check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, " ", "F - test-trace-expand-stops-at-higher-depth/expand-2/cursor" + check-screen-row screen, 3/y, "... ", "F - test-trace-expand-stops-at-higher-depth/expand-3" + check-background-color-in-screen-row screen, 7/bg=cursor, 3/y, " ", "F - test-trace-expand-stops-at-higher-depth/expand-3/cursor" + check-screen-row screen, 4/y, " ", "F - test-trace-expand-stops-at-higher-depth/expand-4" + check-background-color-in-screen-row screen, 7/bg=cursor, 4/y, " ", "F - test-trace-expand-stops-at-higher-depth/expand-4/cursor" +} + +fn test-trace-expand-twice { + var t-storage: trace + var t/esi: (addr trace) <- address t-storage + initialize-trace t, 0x10, 0x10 + # + trace-text t, "l", "line 1" + trace-lower t + trace-text t, "l", "line 1.1" + trace-higher t + trace-text t, "l", "line 2" + # setup: screen + var screen-on-stack: screen + var screen/edi: (addr screen) <- address screen-on-stack + initialize-screen screen, 0x10/width, 4/height + # + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor + # + check-screen-row screen, 0/y, "... ", "F - test-trace-expand-twice/pre-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||| ", "F - test-trace-expand-twice/pre-0/cursor" + check-screen-row screen, 1/y, " ", "F - test-trace-expand-twice/pre-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-trace-expand-twice/pre-1/cursor" + # expand + edit-trace t, 0xa/enter + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor + # + check-screen-row screen, 0/y, "0 line 1 ", "F - test-trace-expand-twice/expand-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||||||| ", "F - test-trace-expand-twice/expand-0/cursor" + check-screen-row screen, 1/y, "... ", "F - test-trace-expand-twice/expand-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-trace-expand-twice/expand-1/cursor" + check-screen-row screen, 2/y, "0 line 2 ", "F - test-trace-expand-twice/expand-2" + check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, " ", "F - test-trace-expand-twice/expand-2/cursor" + # cursor down + edit-trace t, 4/ctrl-d + # hack: we need to render here to make this test pass; we're mixing state management with rendering + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor + # + check-screen-row screen, 0/y, "0 line 1 ", "F - test-trace-expand-twice/down-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " ", "F - test-trace-expand-twice/down-0/cursor" + check-screen-row screen, 1/y, "... ", "F - test-trace-expand-twice/down-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "||| ", "F - test-trace-expand-twice/down-1/cursor" + check-screen-row screen, 2/y, "0 line 2 ", "F - test-trace-expand-twice/down-2" + check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, " ", "F - test-trace-expand-twice/down-2/cursor" + # expand again + edit-trace t, 0xa/enter + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor + # + check-screen-row screen, 0/y, "0 line 1 ", "F - test-trace-expand-twice/expand2-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " ", "F - test-trace-expand-twice/expand2-0/cursor" + check-screen-row screen, 1/y, "1 line 1.1 ", "F - test-trace-expand-twice/expand2-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "|||||||||| ", "F - test-trace-expand-twice/expand2-1/cursor" + check-screen-row screen, 2/y, "0 line 2 ", "F - test-trace-expand-twice/expand2-2" + check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, " ", "F - test-trace-expand-twice/expand2-2/cursor" +} + +fn test-trace-refresh-cursor { + var t-storage: trace + var t/esi: (addr trace) <- address t-storage + initialize-trace t, 0x10, 0x10 + # + trace-text t, "l", "line 1" + trace-text t, "l", "line 2" + trace-text t, "l", "line 3" + # setup: screen + var screen-on-stack: screen + var screen/edi: (addr screen) <- address screen-on-stack + initialize-screen screen, 0x10/width, 4/height + # + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor + # + check-screen-row screen, 0/y, "... ", "F - test-trace-refresh-cursor/pre-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||| ", "F - test-trace-refresh-cursor/pre-0/cursor" + check-screen-row screen, 1/y, " ", "F - test-trace-refresh-cursor/pre-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-trace-refresh-cursor/pre-1/cursor" + # expand + edit-trace t, 0xa/enter + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor + # + check-screen-row screen, 0/y, "0 line 1 ", "F - test-trace-refresh-cursor/expand-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||||||| ", "F - test-trace-refresh-cursor/expand-0/cursor" + check-screen-row screen, 1/y, "0 line 2 ", "F - test-trace-refresh-cursor/expand-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-trace-refresh-cursor/expand-1/cursor" + check-screen-row screen, 2/y, "0 line 3 ", "F - test-trace-refresh-cursor/expand-2" + check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, " ", "F - test-trace-refresh-cursor/expand-2/cursor" + # cursor down + edit-trace t, 4/ctrl-d + edit-trace t, 4/ctrl-d + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor + # + check-screen-row screen, 0/y, "0 line 1 ", "F - test-trace-refresh-cursor/down-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " ", "F - test-trace-refresh-cursor/down-0/cursor" + check-screen-row screen, 1/y, "0 line 2 ", "F - test-trace-refresh-cursor/down-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-trace-refresh-cursor/down-1/cursor" + check-screen-row screen, 2/y, "0 line 3 ", "F - test-trace-refresh-cursor/down-2" + check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "|||||||| ", "F - test-trace-refresh-cursor/down-2/cursor" + # recreate trace + clear-trace t + trace-text t, "l", "line 1" + trace-text t, "l", "line 2" + trace-text t, "l", "line 3" + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor + # cursor remains unchanged + check-screen-row screen, 0/y, "0 line 1 ", "F - test-trace-refresh-cursor/refresh-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " ", "F - test-trace-refresh-cursor/refresh-0/cursor" + check-screen-row screen, 1/y, "0 line 2 ", "F - test-trace-refresh-cursor/refresh-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-trace-refresh-cursor/refresh-1/cursor" + check-screen-row screen, 2/y, "0 line 3 ", "F - test-trace-refresh-cursor/refresh-2" + check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "|||||||| ", "F - test-trace-refresh-cursor/refresh-2/cursor" +} + +fn test-trace-preserve-cursor-on-refresh { + var t-storage: trace + var t/esi: (addr trace) <- address t-storage + initialize-trace t, 0x10, 0x10 + # + trace-text t, "l", "line 1" + trace-text t, "l", "line 2" + trace-text t, "l", "line 3" + # setup: screen + var screen-on-stack: screen + var screen/edi: (addr screen) <- address screen-on-stack + initialize-screen screen, 0x10/width, 4/height + # + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor + # + check-screen-row screen, 0/y, "... ", "F - test-trace-preserve-cursor-on-refresh/pre-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||| ", "F - test-trace-preserve-cursor-on-refresh/pre-0/cursor" + check-screen-row screen, 1/y, " ", "F - test-trace-preserve-cursor-on-refresh/pre-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-trace-preserve-cursor-on-refresh/pre-1/cursor" + # expand + edit-trace t, 0xa/enter + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor + # + check-screen-row screen, 0/y, "0 line 1 ", "F - test-trace-preserve-cursor-on-refresh/expand-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||||||| ", "F - test-trace-preserve-cursor-on-refresh/expand-0/cursor" + check-screen-row screen, 1/y, "0 line 2 ", "F - test-trace-preserve-cursor-on-refresh/expand-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-trace-preserve-cursor-on-refresh/expand-1/cursor" + check-screen-row screen, 2/y, "0 line 3 ", "F - test-trace-preserve-cursor-on-refresh/expand-2" + check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, " ", "F - test-trace-preserve-cursor-on-refresh/expand-2/cursor" + # cursor down + edit-trace t, 4/ctrl-d + edit-trace t, 4/ctrl-d + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor + # + check-screen-row screen, 0/y, "0 line 1 ", "F - test-trace-preserve-cursor-on-refresh/down-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " ", "F - test-trace-preserve-cursor-on-refresh/down-0/cursor" + check-screen-row screen, 1/y, "0 line 2 ", "F - test-trace-preserve-cursor-on-refresh/down-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-trace-preserve-cursor-on-refresh/down-1/cursor" + check-screen-row screen, 2/y, "0 line 3 ", "F - test-trace-preserve-cursor-on-refresh/down-2" + check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "|||||||| ", "F - test-trace-preserve-cursor-on-refresh/down-2/cursor" + # recreate trace with slightly different lines + clear-trace t + trace-text t, "l", "line 4" + trace-text t, "l", "line 5" + trace-text t, "l", "line 3" # cursor line is unchanged + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor + # cursor remains unchanged + check-screen-row screen, 0/y, "0 line 4 ", "F - test-trace-preserve-cursor-on-refresh/refresh-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " ", "F - test-trace-preserve-cursor-on-refresh/refresh-0/cursor" + check-screen-row screen, 1/y, "0 line 5 ", "F - test-trace-preserve-cursor-on-refresh/refresh-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-trace-preserve-cursor-on-refresh/refresh-1/cursor" + check-screen-row screen, 2/y, "0 line 3 ", "F - test-trace-preserve-cursor-on-refresh/refresh-2" + check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "|||||||| ", "F - test-trace-preserve-cursor-on-refresh/refresh-2/cursor" +} + +fn test-trace-keep-cursor-visible-on-refresh { + var t-storage: trace + var t/esi: (addr trace) <- address t-storage + initialize-trace t, 0x10, 0x10 + # + trace-text t, "l", "line 1" + trace-text t, "l", "line 2" + trace-text t, "l", "line 3" + # setup: screen + var screen-on-stack: screen + var screen/edi: (addr screen) <- address screen-on-stack + initialize-screen screen, 0x10/width, 4/height + # + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor + # + check-screen-row screen, 0/y, "... ", "F - test-trace-keep-cursor-visible-on-refresh/pre-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||| ", "F - test-trace-keep-cursor-visible-on-refresh/pre-0/cursor" + check-screen-row screen, 1/y, " ", "F - test-trace-keep-cursor-visible-on-refresh/pre-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-trace-keep-cursor-visible-on-refresh/pre-1/cursor" + # expand + edit-trace t, 0xa/enter + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor + # + check-screen-row screen, 0/y, "0 line 1 ", "F - test-trace-keep-cursor-visible-on-refresh/expand-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||||||| ", "F - test-trace-keep-cursor-visible-on-refresh/expand-0/cursor" + check-screen-row screen, 1/y, "0 line 2 ", "F - test-trace-keep-cursor-visible-on-refresh/expand-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-trace-keep-cursor-visible-on-refresh/expand-1/cursor" + check-screen-row screen, 2/y, "0 line 3 ", "F - test-trace-keep-cursor-visible-on-refresh/expand-2" + check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, " ", "F - test-trace-keep-cursor-visible-on-refresh/expand-2/cursor" + # cursor down + edit-trace t, 4/ctrl-d + edit-trace t, 4/ctrl-d + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor + # + check-screen-row screen, 0/y, "0 line 1 ", "F - test-trace-keep-cursor-visible-on-refresh/down-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " ", "F - test-trace-keep-cursor-visible-on-refresh/down-0/cursor" + check-screen-row screen, 1/y, "0 line 2 ", "F - test-trace-keep-cursor-visible-on-refresh/down-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-trace-keep-cursor-visible-on-refresh/down-1/cursor" + check-screen-row screen, 2/y, "0 line 3 ", "F - test-trace-keep-cursor-visible-on-refresh/down-2" + check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "|||||||| ", "F - test-trace-keep-cursor-visible-on-refresh/down-2/cursor" + # recreate trace with entirely different lines + clear-trace t + trace-text t, "l", "line 4" + trace-text t, "l", "line 5" + trace-text t, "l", "line 6" + mark-lines-dirty t + clear-screen screen + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor + # trace collapses, and cursor bumps up + check-screen-row screen, 0/y, "... ", "F - test-trace-keep-cursor-visible-on-refresh/refresh-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||| ", "F - test-trace-keep-cursor-visible-on-refresh/refresh-0/cursor" + check-screen-row screen, 1/y, " ", "F - test-trace-keep-cursor-visible-on-refresh/refresh-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-trace-keep-cursor-visible-on-refresh/refresh-1/cursor" + check-screen-row screen, 2/y, " ", "F - test-trace-keep-cursor-visible-on-refresh/refresh-2" + check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, " ", "F - test-trace-keep-cursor-visible-on-refresh/refresh-2/cursor" +} + +fn test-trace-collapse-at-top { + var t-storage: trace + var t/esi: (addr trace) <- address t-storage + initialize-trace t, 0x10, 0x10 + # + trace-text t, "l", "line 1" + trace-lower t + trace-text t, "l", "line 1.1" + trace-higher t + trace-text t, "l", "line 2" + # setup: screen + var screen-on-stack: screen + var screen/edi: (addr screen) <- address screen-on-stack + initialize-screen screen, 0x10/width, 4/height + # + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor + # + check-screen-row screen, 0/y, "... ", "F - test-trace-collapse-at-top/pre-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||| ", "F - test-trace-collapse-at-top/pre-0/cursor" + check-screen-row screen, 1/y, " ", "F - test-trace-collapse-at-top/pre-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-trace-collapse-at-top/pre-1/cursor" + # expand + edit-trace t, 0xa/enter + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor + # + check-screen-row screen, 0/y, "0 line 1 ", "F - test-trace-collapse-at-top/expand-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||||||| ", "F - test-trace-collapse-at-top/expand-0/cursor" + check-screen-row screen, 1/y, "... ", "F - test-trace-collapse-at-top/expand-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-trace-collapse-at-top/expand-1/cursor" + check-screen-row screen, 2/y, "0 line 2 ", "F - test-trace-collapse-at-top/expand-2" + check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, " ", "F - test-trace-collapse-at-top/expand-2/cursor" + # collapse + edit-trace t, 8/backspace + # hack: we need to render here to make this test pass; we're mixing state management with rendering + clear-screen screen + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor + # + check-ints-equal y, 1, "F - test-trace-collapse-at-top/post-0/y" + check-screen-row screen, 0/y, "... ", "F - test-trace-collapse-at-top/post-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||| ", "F - test-trace-collapse-at-top/post-0/cursor" + check-screen-row screen, 1/y, " ", "F - test-trace-collapse-at-top/post-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-trace-collapse-at-top/post-1/cursor" +} + +fn test-trace-collapse { + var t-storage: trace + var t/esi: (addr trace) <- address t-storage + initialize-trace t, 0x10, 0x10 + # + trace-text t, "l", "line 1" + trace-text t, "l", "line 2" + # setup: screen + var screen-on-stack: screen + var screen/edi: (addr screen) <- address screen-on-stack + initialize-screen screen, 0x10/width, 4/height + # + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor + # + check-screen-row screen, 0/y, "... ", "F - test-trace-collapse/pre-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||| ", "F - test-trace-collapse/pre-0/cursor" + check-screen-row screen, 1/y, " ", "F - test-trace-collapse/pre-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-trace-collapse/pre-1/cursor" + # expand + edit-trace t, 0xa/enter + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor + # + check-screen-row screen, 0/y, "0 line 1 ", "F - test-trace-collapse/expand-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||||||| ", "F - test-trace-collapse/expand-0/cursor" + check-screen-row screen, 1/y, "0 line 2 ", "F - test-trace-collapse/expand-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-trace-collapse/expand-1/cursor" + # cursor down + edit-trace t, 4/ctrl-d + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor + # collapse + edit-trace t, 8/backspace + clear-screen screen + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor + # + check-ints-equal y, 1, "F - test-trace-collapse/post-0/y" + check-screen-row screen, 0/y, "... ", "F - test-trace-collapse/post-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||| ", "F - test-trace-collapse/post-0/cursor" + check-screen-row screen, 1/y, " ", "F - test-trace-collapse/post-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-trace-collapse/post-1/cursor" +} + +fn test-trace-collapse-skips-invisible-lines { + var t-storage: trace + var t/esi: (addr trace) <- address t-storage + initialize-trace t, 0x10, 0x10 + # + trace-text t, "l", "line 1" + trace-lower t + trace-text t, "l", "line 1.1" + trace-higher t + trace-text t, "l", "line 2" + # setup: screen + var screen-on-stack: screen + var screen/edi: (addr screen) <- address screen-on-stack + initialize-screen screen, 0x10/width, 4/height + # + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor + # + check-screen-row screen, 0/y, "... ", "F - test-trace-collapse-skips-invisible-lines/pre-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||| ", "F - test-trace-collapse-skips-invisible-lines/pre-0/cursor" + check-screen-row screen, 1/y, " ", "F - test-trace-collapse-skips-invisible-lines/pre-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-trace-collapse-skips-invisible-lines/pre-1/cursor" + # expand + edit-trace t, 0xa/enter + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor + # two visible lines with an invisible line in between + check-screen-row screen, 0/y, "0 line 1 ", "F - test-trace-collapse-skips-invisible-lines/expand-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||||||| ", "F - test-trace-collapse-skips-invisible-lines/expand-0/cursor" + check-screen-row screen, 1/y, "... ", "F - test-trace-collapse-skips-invisible-lines/expand-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-trace-collapse-skips-invisible-lines/expand-1/cursor" + check-screen-row screen, 2/y, "0 line 2 ", "F - test-trace-collapse-skips-invisible-lines/expand-2" + check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, " ", "F - test-trace-collapse-skips-invisible-lines/expand-2/cursor" + # cursor down to second visible line + edit-trace t, 4/ctrl-d + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor + edit-trace t, 4/ctrl-d + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor + # collapse + edit-trace t, 8/backspace + clear-screen screen + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor + # + check-ints-equal y, 1, "F - test-trace-collapse-skips-invisible-lines/post-0/y" + var cursor-y/eax: (addr int) <- get t, cursor-y + check-ints-equal *cursor-y, 0, "F - test-trace-collapse-skips-invisible-lines/post-0/cursor-y" + check-screen-row screen, 0/y, "... ", "F - test-trace-collapse-skips-invisible-lines/post-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||| ", "F - test-trace-collapse-skips-invisible-lines/post-0/cursor" + check-screen-row screen, 1/y, " ", "F - test-trace-collapse-skips-invisible-lines/post-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-trace-collapse-skips-invisible-lines/post-1/cursor" +} + +fn test-trace-collapse-two-levels { + var t-storage: trace + var t/esi: (addr trace) <- address t-storage + initialize-trace t, 0x10, 0x10 + # + trace-text t, "l", "line 1" + trace-lower t + trace-text t, "l", "line 1.1" + trace-higher t + trace-text t, "l", "line 2" + # setup: screen + var screen-on-stack: screen + var screen/edi: (addr screen) <- address screen-on-stack + initialize-screen screen, 0x10/width, 4/height + # + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor + # + check-screen-row screen, 0/y, "... ", "F - test-trace-collapse-two-levels/pre-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||| ", "F - test-trace-collapse-two-levels/pre-0/cursor" + check-screen-row screen, 1/y, " ", "F - test-trace-collapse-two-levels/pre-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-trace-collapse-two-levels/pre-1/cursor" + # expand + edit-trace t, 0xa/enter + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor + # two visible lines with an invisible line in between + check-screen-row screen, 0/y, "0 line 1 ", "F - test-trace-collapse-two-levels/expand-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||||||| ", "F - test-trace-collapse-two-levels/expand-0/cursor" + check-screen-row screen, 1/y, "... ", "F - test-trace-collapse-two-levels/expand-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-trace-collapse-two-levels/expand-1/cursor" + check-screen-row screen, 2/y, "0 line 2 ", "F - test-trace-collapse-two-levels/expand-2" + check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, " ", "F - test-trace-collapse-two-levels/expand-2/cursor" + # cursor down to ellipses + edit-trace t, 4/ctrl-d + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor + # expand + edit-trace t, 0xa/enter + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor + # two visible lines with an invisible line in between + check-screen-row screen, 0/y, "0 line 1 ", "F - test-trace-collapse-two-levels/expand2-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " ", "F - test-trace-collapse-two-levels/expand2-0/cursor" + check-screen-row screen, 1/y, "1 line 1.1 ", "F - test-trace-collapse-two-levels/expand2-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "|||||||||| ", "F - test-trace-collapse-two-levels/expand2-1/cursor" + check-screen-row screen, 2/y, "0 line 2 ", "F - test-trace-collapse-two-levels/expand2-2" + check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, " ", "F - test-trace-collapse-two-levels/expand2-2/cursor" + # cursor down to second visible line + edit-trace t, 4/ctrl-d + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor + # collapse + edit-trace t, 8/backspace + clear-screen screen + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor + # + check-ints-equal y, 1, "F - test-trace-collapse-two-levels/post-0/y" + var cursor-y/eax: (addr int) <- get t, cursor-y + check-ints-equal *cursor-y, 0, "F - test-trace-collapse-two-levels/post-0/cursor-y" + check-screen-row screen, 0/y, "... ", "F - test-trace-collapse-two-levels/post-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||| ", "F - test-trace-collapse-two-levels/post-0/cursor" + check-screen-row screen, 1/y, " ", "F - test-trace-collapse-two-levels/post-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-trace-collapse-two-levels/post-1/cursor" +} + +fn test-trace-collapse-nested-level { + var t-storage: trace + var t/esi: (addr trace) <- address t-storage + initialize-trace t, 0x10, 0x10 + # + trace-text t, "l", "line 1" + trace-lower t + trace-text t, "l", "line 1.1" + trace-higher t + trace-text t, "l", "line 2" + trace-lower t + trace-text t, "l", "line 2.1" + trace-text t, "l", "line 2.2" + trace-higher t + # setup: screen + var screen-on-stack: screen + var screen/edi: (addr screen) <- address screen-on-stack + initialize-screen screen, 0x10/width, 8/height + # + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 8/ymax, 1/show-cursor + # + check-screen-row screen, 0/y, "... ", "F - test-trace-collapse-nested-level/pre-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||| ", "F - test-trace-collapse-nested-level/pre-0/cursor" + check-screen-row screen, 1/y, " ", "F - test-trace-collapse-nested-level/pre-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-trace-collapse-nested-level/pre-1/cursor" + # expand + edit-trace t, 0xa/enter + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 8/ymax, 1/show-cursor + # two visible lines with an invisible line in between + check-screen-row screen, 0/y, "0 line 1 ", "F - test-trace-collapse-nested-level/expand-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||||||| ", "F - test-trace-collapse-nested-level/expand-0/cursor" + check-screen-row screen, 1/y, "... ", "F - test-trace-collapse-nested-level/expand-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-trace-collapse-nested-level/expand-1/cursor" + check-screen-row screen, 2/y, "0 line 2 ", "F - test-trace-collapse-nested-level/expand-2" + check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, " ", "F - test-trace-collapse-nested-level/expand-2/cursor" + check-screen-row screen, 3/y, "... ", "F - test-trace-collapse-nested-level/expand-3" + check-background-color-in-screen-row screen, 7/bg=cursor, 3/y, " ", "F - test-trace-collapse-nested-level/expand-3/cursor" + # cursor down to bottom + edit-trace t, 4/ctrl-d + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 8/ymax, 1/show-cursor + edit-trace t, 4/ctrl-d + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 8/ymax, 1/show-cursor + edit-trace t, 4/ctrl-d + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 8/ymax, 1/show-cursor + # expand + edit-trace t, 0xa/enter + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 8/ymax, 1/show-cursor + # two visible lines with an invisible line in between + check-screen-row screen, 0/y, "0 line 1 ", "F - test-trace-collapse-nested-level/expand2-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " ", "F - test-trace-collapse-nested-level/expand2-0/cursor" + check-screen-row screen, 1/y, "... ", "F - test-trace-collapse-nested-level/expand2-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-trace-collapse-nested-level/expand2-1/cursor" + check-screen-row screen, 2/y, "0 line 2 ", "F - test-trace-collapse-nested-level/expand2-2" + check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, " ", "F - test-trace-collapse-nested-level/expand2-2/cursor" + check-screen-row screen, 3/y, "1 line 2.1 ", "F - test-trace-collapse-nested-level/expand2-3" + check-background-color-in-screen-row screen, 7/bg=cursor, 3/y, "|||||||||| ", "F - test-trace-collapse-nested-level/expand2-3/cursor" + check-screen-row screen, 4/y, "1 line 2.2 ", "F - test-trace-collapse-nested-level/expand2-4" + check-background-color-in-screen-row screen, 7/bg=cursor, 4/y, " ", "F - test-trace-collapse-nested-level/expand2-4/cursor" + # collapse + edit-trace t, 8/backspace + clear-screen screen + var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 8/ymax, 1/show-cursor + # + check-ints-equal y, 4, "F - test-trace-collapse-nested-level/post-0/y" + var cursor-y/eax: (addr int) <- get t, cursor-y + check-ints-equal *cursor-y, 2, "F - test-trace-collapse-nested-level/post-0/cursor-y" + check-screen-row screen, 0/y, "0 line 1 ", "F - test-trace-collapse-nested-level/post-0" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " ", "F - test-trace-collapse-nested-level/post-0/cursor" + check-screen-row screen, 1/y, "... ", "F - test-trace-collapse-nested-level/post-1" + check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, " ", "F - test-trace-collapse-nested-level/post-1/cursor" + check-screen-row screen, 2/y, "0 line 2 ", "F - test-trace-collapse-nested-level/post-2" + check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "|||||||| ", "F - test-trace-collapse-nested-level/post-2/cursor" + check-screen-row screen, 3/y, "... ", "F - test-trace-collapse-nested-level/post-3" + check-background-color-in-screen-row screen, 7/bg=cursor, 3/y, " ", "F - test-trace-collapse-nested-level/post-3/cursor" +} diff --git a/shell/vimrc.vim b/shell/vimrc.vim new file mode 100644 index 00000000..348fe364 --- /dev/null +++ b/shell/vimrc.vim @@ -0,0 +1,2 @@ +" when opening files in this directory, load vimrc from cwd (top-level) +source vimrc.vim -- cgit 1.4.1-2-gfad0