From 8765064d6910c65ea5ed5526b27e9752df000c3d Mon Sep 17 00:00:00 2001 From: "Kartik K. Agaram" Date: Sun, 15 Aug 2021 21:09:17 -0700 Subject: move gap buffer code to top-level Now that it's been used in a second app without needing any changes. --- shell/gap-buffer.mu | 1470 ----------------------------------------------- shell/grapheme-stack.mu | 556 ------------------ 2 files changed, 2026 deletions(-) delete mode 100644 shell/gap-buffer.mu delete mode 100644 shell/grapheme-stack.mu (limited to 'shell') diff --git a/shell/gap-buffer.mu b/shell/gap-buffer.mu deleted file mode 100644 index 0077d6fd..00000000 --- a/shell/gap-buffer.mu +++ /dev/null @@ -1,1470 +0,0 @@ -# primitive for editing text - -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), capacity: int { - var self/esi: (addr gap-buffer) <- copy _self - var left/eax: (addr grapheme-stack) <- get self, left - initialize-grapheme-stack left, capacity - var right/eax: (addr grapheme-stack) <- get self, right - initialize-grapheme-stack right, capacity -} - -fn clear-gap-buffer _self: (addr gap-buffer) { - var self/esi: (addr gap-buffer) <- copy _self - var left/eax: (addr grapheme-stack) <- get self, left - clear-grapheme-stack left - var right/eax: (addr grapheme-stack) <- get self, right - clear-grapheme-stack right -} - -fn gap-buffer-empty? _self: (addr gap-buffer) -> _/eax: boolean { - var self/esi: (addr gap-buffer) <- copy _self - # if !empty?(left) return false - { - var left/eax: (addr grapheme-stack) <- get self, left - var result/eax: boolean <- grapheme-stack-empty? left - compare result, 0/false - break-if-!= - return 0/false - } - # return empty?(right) - var left/eax: (addr grapheme-stack) <- get self, left - var result/eax: boolean <- grapheme-stack-empty? left - return result -} - -fn gap-buffer-capacity _gap: (addr gap-buffer) -> _/edx: int { - var gap/esi: (addr gap-buffer) <- copy _gap - var left/eax: (addr grapheme-stack) <- get gap, left - var left-data-ah/eax: (addr handle array grapheme) <- get left, data - var left-data/eax: (addr array grapheme) <- lookup *left-data-ah - var result/eax: int <- length left-data - return result -} - -# just for tests -fn initialize-gap-buffer-with self: (addr gap-buffer), keys: (addr array byte) { - initialize-gap-buffer self, 0x40/capacity - var input-stream-storage: (stream byte 0x40/capacity) - var input-stream/ecx: (addr stream byte) <- address input-stream-storage - write input-stream, keys - { - var done?/eax: boolean <- stream-empty? input-stream - compare done?, 0/false - break-if-!= - var g/eax: grapheme <- read-grapheme input-stream - add-grapheme-at-gap self, g - loop - } -} - -fn load-gap-buffer-from-stream self: (addr gap-buffer), in: (addr stream byte) { - rewind-stream in - { - var done?/eax: boolean <- stream-empty? in - compare done?, 0/false - break-if-!= - var key/eax: byte <- read-byte in - compare key, 0/null - break-if-= - var g/eax: grapheme <- copy key - edit-gap-buffer self, g - loop - } -} - -fn emit-gap-buffer self: (addr gap-buffer), out: (addr stream byte) { - clear-stream out - append-gap-buffer self, out -} - -fn append-gap-buffer _self: (addr gap-buffer), out: (addr stream byte) { - var self/esi: (addr gap-buffer) <- copy _self - 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 - } -} - -fn word-at-gap _self: (addr gap-buffer), out: (addr stream byte) { - var self/esi: (addr gap-buffer) <- copy _self - clear-stream out - { - var g/eax: grapheme <- grapheme-at-gap self - var at-word?/eax: boolean <- is-ascii-word-grapheme? g - compare at-word?, 0/false - break-if-!= - return - } - var left/ecx: (addr grapheme-stack) <- get self, left - var left-index/eax: int <- top-most-word left - emit-stack-from-index left, left-index, out - var right/ecx: (addr grapheme-stack) <- get self, right - var right-index/eax: int <- top-most-word right - emit-stack-to-index right, right-index, out -} - -fn test-word-at-gap-single-word-with-gap-at-end { - var _g: gap-buffer - var g/esi: (addr gap-buffer) <- address _g - initialize-gap-buffer-with g, "abc" - # gap is at end (right is empty) - var out-storage: (stream byte 0x10) - var out/eax: (addr stream byte) <- address out-storage - word-at-gap g, out - check-stream-equal out, "abc", "F - test-word-at-gap-single-word-with-gap-at-end" -} - -fn test-word-at-gap-single-word-with-gap-at-start { - var _g: gap-buffer - var g/esi: (addr gap-buffer) <- address _g - initialize-gap-buffer-with g, "abc" - gap-to-start g - # - var out-storage: (stream byte 0x10) - var out/eax: (addr stream byte) <- address out-storage - word-at-gap g, out - check-stream-equal out, "abc", "F - test-word-at-gap-single-word-with-gap-at-start" -} - -fn test-word-at-gap-multiple-words-with-gap-at-non-word-grapheme-at-end { - var _g: gap-buffer - var g/esi: (addr gap-buffer) <- address _g - initialize-gap-buffer-with g, "abc " - # gap is at end (right is empty) - var out-storage: (stream byte 0x10) - var out/eax: (addr stream byte) <- address out-storage - word-at-gap g, out - check-stream-equal out, "", "F - test-word-at-gap-multiple-words-with-gap-at-non-word-grapheme-at-end" -} - -fn test-word-at-gap-multiple-words-with-gap-at-non-word-grapheme-at-start { - var _g: gap-buffer - var g/esi: (addr gap-buffer) <- address _g - initialize-gap-buffer-with g, " abc" - gap-to-start g - # - var out-storage: (stream byte 0x10) - var out/eax: (addr stream byte) <- address out-storage - word-at-gap g, out - check-stream-equal out, "", "F - test-word-at-gap-multiple-words-with-gap-at-non-word-grapheme-at-start" -} - -fn test-word-at-gap-multiple-words-with-gap-at-end { - var _g: gap-buffer - var g/esi: (addr gap-buffer) <- address _g - initialize-gap-buffer-with g, "a bc d" - # gap is at end (right is empty) - var out-storage: (stream byte 0x10) - var out/eax: (addr stream byte) <- address out-storage - word-at-gap g, out - check-stream-equal out, "d", "F - test-word-at-gap-multiple-words-with-gap-at-end" -} - -fn test-word-at-gap-multiple-words-with-gap-at-initial-word { - var _g: gap-buffer - var g/esi: (addr gap-buffer) <- address _g - initialize-gap-buffer-with g, "a bc d" - gap-to-start g - # - var out-storage: (stream byte 0x10) - var out/eax: (addr stream byte) <- address out-storage - word-at-gap g, out - check-stream-equal out, "a", "F - test-word-at-gap-multiple-words-with-gap-at-initial-word" -} - -fn test-word-at-gap-multiple-words-with-gap-at-final-word { - var _g: gap-buffer - var g/esi: (addr gap-buffer) <- address _g - initialize-gap-buffer-with g, "a bc d" - var dummy/eax: grapheme <- gap-left g - # gap is at final word - var out-storage: (stream byte 0x10) - var out/eax: (addr stream byte) <- address out-storage - word-at-gap g, out - check-stream-equal out, "d", "F - test-word-at-gap-multiple-words-with-gap-at-final-word" -} - -fn test-word-at-gap-multiple-words-with-gap-at-final-non-word { - var _g: gap-buffer - var g/esi: (addr gap-buffer) <- address _g - initialize-gap-buffer-with g, "abc " - var dummy/eax: grapheme <- gap-left g - # gap is at final word - var out-storage: (stream byte 0x10) - var out/eax: (addr stream byte) <- address out-storage - word-at-gap g, out - check-stream-equal out, "", "F - test-word-at-gap-multiple-words-with-gap-at-final-non-word" -} - -fn grapheme-at-gap _self: (addr gap-buffer) -> _/eax: grapheme { - # send top of right most of the time - var self/esi: (addr gap-buffer) <- copy _self - var right/edi: (addr grapheme-stack) <- get self, right - var data-ah/eax: (addr handle array grapheme) <- get right, data - var data/eax: (addr array grapheme) <- lookup *data-ah - var top-addr/ecx: (addr int) <- get right, top - { - compare *top-addr, 0 - break-if-<= - var top/ecx: int <- copy *top-addr - top <- decrement - var result/eax: (addr grapheme) <- index data, top - return *result - } - # send top of left only if right is empty - var left/edi: (addr grapheme-stack) <- get self, left - var data-ah/eax: (addr handle array grapheme) <- get left, data - var data/eax: (addr array grapheme) <- lookup *data-ah - var top-addr/ecx: (addr int) <- get left, top - { - compare *top-addr, 0 - break-if-<= - var top/ecx: int <- copy *top-addr - top <- decrement - var result/eax: (addr grapheme) <- index data, top - return *result - } - # send null if everything is empty - return 0 -} - -fn top-most-word _self: (addr grapheme-stack) -> _/eax: 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 top-addr/ecx: (addr int) <- get self, top - var i/ebx: int <- copy *top-addr - i <- decrement - { - compare i, 0 - break-if-< - var g/edx: (addr grapheme) <- index data, i - var is-word?/eax: boolean <- is-ascii-word-grapheme? *g - compare is-word?, 0/false - break-if-= - i <- decrement - loop - } - i <- increment - return i -} - -fn emit-stack-from-index _self: (addr grapheme-stack), start: int, 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 start - { - compare i, *top-addr - break-if->= - var g/edx: (addr grapheme) <- index data, i - write-grapheme out, *g - i <- increment - loop - } -} - -fn emit-stack-to-index _self: (addr grapheme-stack), end: int, 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-< - compare i, end - break-if-< - var g/edx: (addr grapheme) <- index data, i - write-grapheme out, *g - i <- decrement - loop - } -} - -fn is-ascii-word-grapheme? g: grapheme -> _/eax: boolean { - compare g, 0x21/! - { - break-if-!= - return 1/true - } - compare g, 0x30/0 - { - break-if->= - return 0/false - } - compare g, 0x39/9 - { - break-if-> - return 1/true - } - compare g, 0x3f/? - { - break-if-!= - return 1/true - } - compare g, 0x41/A - { - break-if->= - return 0/false - } - compare g, 0x5a/Z - { - break-if-> - return 1/true - } - compare g, 0x5f/_ - { - break-if-!= - return 1/true - } - compare g, 0x61/a - { - break-if->= - return 0/false - } - compare g, 0x7a/z - { - break-if-> - return 1/true - } - return 0/false -} - -# 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, color: int, background-color: int -> _/eax: int, _/ecx: int { - var gap/esi: (addr gap-buffer) <- copy _gap - var left/edx: (addr grapheme-stack) <- get gap, left - var highlight-matching-open-paren?/ebx: boolean <- copy 0/false - var matching-open-paren-depth/edi: int <- copy 0 - highlight-matching-open-paren?, matching-open-paren-depth <- highlight-matching-open-paren? gap, render-cursor? - 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, highlight-matching-open-paren?, matching-open-paren-depth, color, background-color - 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?, color, background-color - # decide whether we still need to print a cursor - var fg/edi: int <- copy color - var bg/ebx: int <- copy background-color - 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-= - # swap foreground and background - fg <- copy background-color - bg <- copy color - } - # 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, fg, bg - return x2, y2 -} - -fn render-gap-buffer screen: (addr screen), gap: (addr gap-buffer), x: int, y: int, render-cursor?: boolean, color: int, background-color: 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-gap-buffer-wrapping-right-then-down screen, gap, x, y, width, height, render-cursor?, color, background-color - 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 add-code-point-at-gap self: (addr gap-buffer), c: code-point { - var g/eax: grapheme <- copy c - add-grapheme-at-gap self, 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/capacity) - 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 - # - add-code-point-at-gap g, 0x61/a - add-code-point-at-gap g, 0x61/a - add-code-point-at-gap g, 0x61/a - # 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 - # - add-code-point-at-gap g, 0x61/a - add-code-point-at-gap g, 0x61/a - add-code-point-at-gap g, 0x61/a - 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 - # - add-code-point-at-gap g, 0x61/a - add-code-point-at-gap g, 0x61/a - add-code-point-at-gap g, 0x61/a - 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 - add-code-point-at-gap g, 0x61/a - add-code-point-at-gap g, 0x61/a - add-code-point-at-gap g, 0x61/a - # - 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, 0/no-pixel-graphics - # - var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 0/no-cursor, 3/fg, 0xc5/bg=blue-bg - 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, 3/bg=reverse, 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, 0/no-pixel-graphics - # - var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor, 3/fg, 0xc5/bg=blue-bg - 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, 3/bg=reverse, 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, 0/no-pixel-graphics - # - var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor, 3/fg, 0xc5/bg=blue-bg - 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, 3/bg=reverse, 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, 0/no-pixel-graphics - # - var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor, 3/fg, 0xc5/bg=blue-bg - 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, 3/bg=reverse, 0/y, "| ", "F - test-render-gap-buffer-with-cursor-at-start: bg" -} - -fn test-render-gap-buffer-highlight-matching-close-paren { - var gap-storage: gap-buffer - var gap/esi: (addr gap-buffer) <- address gap-storage - initialize-gap-buffer-with gap, "(a)" - 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, 0/no-pixel-graphics - # - var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor, 3/fg, 0xc5/bg=blue-bg - check-screen-row screen, 0/y, "(a) ", "F - test-render-gap-buffer-highlight-matching-close-paren" - check-ints-equal x, 4, "F - test-render-gap-buffer-highlight-matching-close-paren: result" - check-background-color-in-screen-row screen, 3/bg=reverse, 0/y, "| ", "F - test-render-gap-buffer-highlight-matching-close-paren: cursor" - check-screen-row-in-color screen, 0xf/fg=highlight, 0/y, " ) ", "F - test-render-gap-buffer-highlight-matching-close-paren: matching paren" -} - -fn test-render-gap-buffer-highlight-matching-open-paren { - var gap-storage: gap-buffer - var gap/esi: (addr gap-buffer) <- address gap-storage - initialize-gap-buffer-with gap, "(a)" - 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, 0/no-pixel-graphics - # - var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor, 3/fg, 0xc5/bg=blue-bg - check-screen-row screen, 0/y, "(a) ", "F - test-render-gap-buffer-highlight-matching-open-paren" - check-ints-equal x, 4, "F - test-render-gap-buffer-highlight-matching-open-paren: result" - check-background-color-in-screen-row screen, 3/bg=reverse, 0/y, " | ", "F - test-render-gap-buffer-highlight-matching-open-paren: cursor" - check-screen-row-in-color screen, 0xf/fg=highlight, 0/y, "( ", "F - test-render-gap-buffer-highlight-matching-open-paren: matching paren" -} - -fn test-render-gap-buffer-highlight-matching-open-paren-of-end { - var gap-storage: gap-buffer - var gap/esi: (addr gap-buffer) <- address gap-storage - initialize-gap-buffer-with gap, "(a)" - 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, 0/no-pixel-graphics - # - var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor, 3/fg, 0xc5/bg=blue-bg - check-screen-row screen, 0/y, "(a) ", "F - test-render-gap-buffer-highlight-matching-open-paren-of-end" - check-ints-equal x, 4, "F - test-render-gap-buffer-highlight-matching-open-paren-of-end: result" - check-background-color-in-screen-row screen, 3/bg=reverse, 0/y, " |", "F - test-render-gap-buffer-highlight-matching-open-paren-of-end: cursor" - check-screen-row-in-color screen, 0xf/fg=highlight, 0/y, "( ", "F - test-render-gap-buffer-highlight-matching-open-paren-of-end: matching paren" -} - -# should I highlight a matching open paren? And if so, at what depth from top of left? -# basically there are two cases to disambiguate here: -# Usually the cursor is at top of right. Highlight first '(' at depth 0 from top of left. -# If right is empty, match the ')' _before_ cursor. Highlight first '(' at depth _1_ from top of left. -fn highlight-matching-open-paren? _gap: (addr gap-buffer), render-cursor?: boolean -> _/ebx: boolean, _/edi: int { - # if not rendering cursor, return - compare render-cursor?, 0/false - { - break-if-!= - return 0/false, 0 - } - var gap/esi: (addr gap-buffer) <- copy _gap - var stack/edi: (addr grapheme-stack) <- get gap, right - var top-addr/eax: (addr int) <- get stack, top - var top-index/ecx: int <- copy *top-addr - compare top-index, 0 - { - break-if-> - # if cursor at end, return (char before cursor == ')', 1) - stack <- get gap, left - top-addr <- get stack, top - top-index <- copy *top-addr - compare top-index, 0 - { - break-if-> - return 0/false, 0 - } - top-index <- decrement - var data-ah/eax: (addr handle array grapheme) <- get stack, data - var data/eax: (addr array grapheme) <- lookup *data-ah - var g/eax: (addr grapheme) <- index data, top-index - compare *g, 0x29/close-paren - { - break-if-= - return 0/false, 0 - } - return 1/true, 1 - } - # cursor is not at end; return (char at cursor == ')') - top-index <- decrement - var data-ah/eax: (addr handle array grapheme) <- get stack, data - var data/eax: (addr array grapheme) <- lookup *data-ah - var g/eax: (addr grapheme) <- index data, top-index - compare *g, 0x29/close-paren - { - break-if-= - return 0/false, 0 - } - return 1/true, 0 -} - -fn test-highlight-matching-open-paren { - var gap-storage: gap-buffer - var gap/esi: (addr gap-buffer) <- address gap-storage - initialize-gap-buffer-with gap, "(a)" - gap-to-end gap - var highlight-matching-open-paren?/ebx: boolean <- copy 0/false - var open-paren-depth/edi: int <- copy 0 - highlight-matching-open-paren?, open-paren-depth <- highlight-matching-open-paren? gap, 0/no-cursor - check-not highlight-matching-open-paren?, "F - test-highlight-matching-open-paren: no cursor" - highlight-matching-open-paren?, open-paren-depth <- highlight-matching-open-paren? gap, 1/render-cursor - check highlight-matching-open-paren?, "F - test-highlight-matching-open-paren: at end immediately after ')'" - check-ints-equal open-paren-depth, 1, "F - test-highlight-matching-open-paren: depth at end immediately after ')'" - var dummy/eax: grapheme <- gap-left gap - highlight-matching-open-paren?, open-paren-depth <- highlight-matching-open-paren? gap, 1/render-cursor - check highlight-matching-open-paren?, "F - test-highlight-matching-open-paren: on ')'" - dummy <- gap-left gap - highlight-matching-open-paren?, open-paren-depth <- highlight-matching-open-paren? gap, 1/render-cursor - check-not highlight-matching-open-paren?, "F - test-highlight-matching-open-paren: not on ')'" -} - -## 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 put-back-from-gap-buffer _self: (addr gap-buffer) { - var self/esi: (addr gap-buffer) <- copy _self - # 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-a/eax: (addr int) <- get self, right-read-index - compare *right-read-index-a, 0 - { - break-if-<= - decrement *right-read-index-a - return - } - # 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-a/eax: (addr int) <- get self, left-read-index - decrement *left-read-index-a -} - -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-spaces-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-= - 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 - } - { - compare g, 0x80/left-arrow - break-if-!= - var dummy/eax: grapheme <- gap-left self - return - } - { - compare g, 0x83/right-arrow - break-if-!= - var dummy/eax: grapheme <- gap-right self - return - } - { - compare g, 6/ctrl-f - break-if-!= - gap-to-start-of-next-word self - return - } - { - compare g, 2/ctrl-b - break-if-!= - gap-to-end-of-previous-word self - return - } - { - compare g, 1/ctrl-a - break-if-!= - gap-to-previous-start-of-line self - return - } - { - compare g, 5/ctrl-e - break-if-!= - gap-to-next-end-of-line self - return - } - { - compare g, 0x81/down-arrow - break-if-!= - gap-down self - return - } - { - compare g, 0x82/up-arrow - break-if-!= - gap-up self - return - } - { - compare g, 0x15/ctrl-u - break-if-!= - clear-gap-buffer self - return - } - { - compare g, 9/tab - break-if-!= - # tab = 2 spaces - add-code-point-at-gap self, 0x20/space - add-code-point-at-gap self, 0x20/space - return - } - # default: insert character - add-grapheme-at-gap self, g -} - -fn gap-to-start-of-next-word self: (addr gap-buffer) { - var curr/eax: grapheme <- copy 0 - # skip to next space - { - curr <- gap-right self - compare curr, -1 - break-if-= - compare curr, 0x20/space - break-if-= - compare curr, 0xa/newline - break-if-= - loop - } - # skip past spaces - { - curr <- gap-right self - compare curr, -1 - break-if-= - compare curr, 0x20/space - loop-if-= - compare curr, 0xa/space - loop-if-= - curr <- gap-left self - break - } -} - -fn gap-to-end-of-previous-word self: (addr gap-buffer) { - var curr/eax: grapheme <- copy 0 - # skip to previous space - { - curr <- gap-left self - compare curr, -1 - break-if-= - compare curr, 0x20/space - break-if-= - compare curr, 0xa/newline - break-if-= - loop - } - # skip past all spaces but one - { - curr <- gap-left self - compare curr, -1 - break-if-= - compare curr, 0x20/space - loop-if-= - compare curr, 0xa/space - loop-if-= - curr <- gap-right self - break - } -} - -fn gap-to-previous-start-of-line self: (addr gap-buffer) { - # skip past immediate newline - var dummy/eax: grapheme <- gap-left self - # skip to previous newline - { - dummy <- gap-left self - { - compare dummy, -1 - break-if-!= - return - } - { - compare dummy, 0xa/newline - break-if-!= - dummy <- gap-right self - return - } - loop - } -} - -fn gap-to-next-end-of-line self: (addr gap-buffer) { - # skip past immediate newline - var dummy/eax: grapheme <- gap-right self - # skip to next newline - { - dummy <- gap-right self - { - compare dummy, -1 - break-if-!= - return - } - { - compare dummy, 0xa/newline - break-if-!= - dummy <- gap-left self - return - } - loop - } -} - -fn gap-up self: (addr gap-buffer) { - # compute column - var col/edx: int <- count-columns-to-start-of-line self - # - gap-to-previous-start-of-line self - # skip ahead by up to col on previous line - var i/ecx: int <- copy 0 - { - compare i, col - break-if->= - var curr/eax: grapheme <- gap-right self - { - compare curr, -1 - break-if-!= - return - } - compare curr, 0xa/newline - { - break-if-!= - curr <- gap-left self - return - } - i <- increment - loop - } -} - -fn gap-down self: (addr gap-buffer) { - # compute column - var col/edx: int <- count-columns-to-start-of-line self - # skip to start of next line - gap-to-end-of-line self - var dummy/eax: grapheme <- gap-right self - # skip ahead by up to col on previous line - var i/ecx: int <- copy 0 - { - compare i, col - break-if->= - var curr/eax: grapheme <- gap-right self - { - compare curr, -1 - break-if-!= - return - } - compare curr, 0xa/newline - { - break-if-!= - curr <- gap-left self - return - } - i <- increment - loop - } -} - -fn count-columns-to-start-of-line self: (addr gap-buffer) -> _/edx: int { - var count/edx: int <- copy 0 - var dummy/eax: grapheme <- copy 0 - # skip to previous newline - { - dummy <- gap-left self - { - compare dummy, -1 - break-if-!= - return count - } - { - compare dummy, 0xa/newline - break-if-!= - dummy <- gap-right self - return count - } - count <- increment - loop - } - return count -} - -fn gap-to-end-of-line self: (addr gap-buffer) { - var dummy/eax: grapheme <- copy 0 - # skip to next newline - { - dummy <- gap-right self - { - compare dummy, -1 - break-if-!= - return - } - { - compare dummy, 0xa/newline - break-if-!= - dummy <- gap-left self - return - } - loop - } -} diff --git a/shell/grapheme-stack.mu b/shell/grapheme-stack.mu deleted file mode 100644 index 13f0e31c..00000000 --- a/shell/grapheme-stack.mu +++ /dev/null @@ -1,556 +0,0 @@ -# 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 -# hardcoded colors: -# matching paren -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, highlight-matching-open-paren?: boolean, open-paren-depth: int, color: int, background-color: int -> _/eax: int, _/ecx: int { - var self/esi: (addr grapheme-stack) <- copy _self - var matching-open-paren-index/edx: int <- get-matching-open-paren-index self, highlight-matching-open-paren?, open-paren-depth - 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/esi: (addr int) <- get self, top - var i/ebx: int <- copy 0 - { - compare i, *top-addr - break-if->= - { - var g/esi: (addr grapheme) <- index data, i - var fg: int - { - var tmp/eax: int <- copy color - copy-to fg, tmp - } - { - compare i, matching-open-paren-index - break-if-!= - copy-to fg, 0xf/highlight - } - x, y <- render-grapheme screen, *g, xmin, ymin, xmax, ymax, x, y, fg, background-color - } - 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, highlight-matching-open-paren?: boolean, open-paren-depth: 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, highlight-matching-open-paren?, open-paren-depth, 3/fg=cyan, 0xc5/bg=blue-bg - return x2 # y2? yolo -} - -# dump stack to screen from top to bottom -# optionally render a 'cursor' with the top grapheme -# hard-coded colors: -# matching paren -# cursor -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, color: int, background-color: int -> _/eax: int, _/ecx: int { - var self/esi: (addr grapheme-stack) <- copy _self - var matching-close-paren-index/edx: int <- get-matching-close-paren-index self, render-cursor? - var data-ah/eax: (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/ebx: (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/esi: (addr grapheme) <- index data, i - x, y <- render-grapheme screen, *g, xmin, ymin, xmax, ymax, x, y, background-color, color - i <- decrement - } - # remaining iterations - { - compare i, 0 - break-if-< - # highlight matching paren if needed - var fg: int - { - var tmp/eax: int <- copy color - copy-to fg, tmp - } - compare i, matching-close-paren-index - { - break-if-!= - copy-to fg, 0xf/highlight - } - # - var g/esi: (addr grapheme) <- index data, i - x, y <- render-grapheme screen, *g, xmin, ymin, xmax, ymax, x, y, fg, background-color - 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?, 3/fg=cyan, 0xc5/bg=blue-bg - 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, 0/no-pixel-graphics - # - var x/eax: int <- render-stack-from-bottom screen, gs, 0/x, 0/y, 0/no-highlight-matching-open-paren, 0/open-paren-depth - 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, 3/bg=reverse, 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, 3/bg=reverse, 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 with cursor: result" - check-background-color-in-screen-row screen, 3/bg=reverse, 2/y, "| ", "F - test-render-grapheme-stack from top with cursor: bg" -} - -fn test-render-grapheme-stack-while-highlighting-matching-close-paren { - # setup: gs = "(b)" - var gs-storage: grapheme-stack - var gs/edi: (addr grapheme-stack) <- address gs-storage - initialize-grapheme-stack gs, 5 - var g/eax: grapheme <- copy 0x29/close-paren - push-grapheme-stack gs, g - g <- copy 0x62/b - push-grapheme-stack gs, g - g <- copy 0x28/open-paren - 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, 0/no-pixel-graphics - # - var x/eax: int <- render-stack-from-top screen, gs, 0/x, 2/y, 1/cursor=true - check-screen-row screen, 2/y, "(b) ", "F - test-render-grapheme-stack-while-highlighting-matching-close-paren" - check-background-color-in-screen-row screen, 3/bg=reverse, 2/y, "| ", "F - test-render-grapheme-stack-while-highlighting-matching-close-paren: cursor" - check-screen-row-in-color screen, 0xf/fg=white, 2/y, " ) ", "F - test-render-grapheme-stack-while-highlighting-matching-close-paren: matching paren" -} - -fn test-render-grapheme-stack-while-highlighting-matching-close-paren-2 { - # setup: gs = "(a (b)) c" - var gs-storage: grapheme-stack - var gs/edi: (addr grapheme-stack) <- address gs-storage - initialize-grapheme-stack gs, 0x10 - var g/eax: grapheme <- copy 0x63/c - push-grapheme-stack gs, g - g <- copy 0x20/space - push-grapheme-stack gs, g - g <- copy 0x29/close-paren - push-grapheme-stack gs, g - g <- copy 0x29/close-paren - push-grapheme-stack gs, g - g <- copy 0x62/b - push-grapheme-stack gs, g - g <- copy 0x28/open-paren - push-grapheme-stack gs, g - g <- copy 0x20/space - push-grapheme-stack gs, g - g <- copy 0x61/a - push-grapheme-stack gs, g - g <- copy 0x28/open-paren - 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, 0/no-pixel-graphics - # - var x/eax: int <- render-stack-from-top screen, gs, 0/x, 2/y, 1/cursor=true - check-screen-row screen, 2/y, "(a (b)) c ", "F - test-render-grapheme-stack-while-highlighting-matching-close-paren-2" - check-background-color-in-screen-row screen, 3/bg=reverse, 2/y, "| ", "F - test-render-grapheme-stack-while-highlighting-matching-close-paren-2: cursor" - check-screen-row-in-color screen, 0xf/fg=white, 2/y, " ) ", "F - test-render-grapheme-stack-while-highlighting-matching-close-paren-2: matching paren" -} - -fn test-render-grapheme-stack-while-highlighting-matching-open-paren-with-close-paren-at-end { - # setup: gs = "(b)" - var gs-storage: grapheme-stack - var gs/edi: (addr grapheme-stack) <- address gs-storage - initialize-grapheme-stack gs, 5 - var g/eax: grapheme <- copy 0x28/open-paren - push-grapheme-stack gs, g - g <- copy 0x62/b - push-grapheme-stack gs, g - g <- copy 0x29/close-paren - 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, 0/no-pixel-graphics - # - var x/eax: int <- render-stack-from-bottom screen, gs, 0/x, 2/y, 1/highlight-matching-open-paren, 1/open-paren-depth - check-screen-row screen, 2/y, "(b) ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren-with-close-paren-at-end" - check-screen-row-in-color screen, 0xf/fg=white, 2/y, "( ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren-with-close-paren-at-end: matching paren" -} - -fn test-render-grapheme-stack-while-highlighting-matching-open-paren-with-close-paren-at-end-2 { - # setup: gs = "a((b))" - var gs-storage: grapheme-stack - var gs/edi: (addr grapheme-stack) <- address gs-storage - initialize-grapheme-stack gs, 0x10 - var g/eax: grapheme <- copy 0x61/a - push-grapheme-stack gs, g - g <- copy 0x28/open-paren - push-grapheme-stack gs, g - g <- copy 0x28/open-paren - push-grapheme-stack gs, g - g <- copy 0x62/b - push-grapheme-stack gs, g - g <- copy 0x29/close-paren - push-grapheme-stack gs, g - g <- copy 0x29/close-paren - 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, 0/no-pixel-graphics - # - var x/eax: int <- render-stack-from-bottom screen, gs, 0/x, 2/y, 1/highlight-matching-open-paren, 1/open-paren-depth - check-screen-row screen, 2/y, "a((b)) ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren-with-close-paren-at-end-2" - check-screen-row-in-color screen, 0xf/fg=white, 2/y, " ( ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren-with-close-paren-at-end-2: matching paren" -} - -fn test-render-grapheme-stack-while-highlighting-matching-open-paren { - # setup: gs = "(b" - var gs-storage: grapheme-stack - var gs/edi: (addr grapheme-stack) <- address gs-storage - initialize-grapheme-stack gs, 5 - var g/eax: grapheme <- copy 0x28/open-paren - push-grapheme-stack gs, g - g <- copy 0x62/b - 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, 0/no-pixel-graphics - # - var x/eax: int <- render-stack-from-bottom screen, gs, 0/x, 2/y, 1/highlight-matching-open-paren, 0/open-paren-depth - check-screen-row screen, 2/y, "(b ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren" - check-screen-row-in-color screen, 0xf/fg=white, 2/y, "( ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren: matching paren" -} - -fn test-render-grapheme-stack-while-highlighting-matching-open-paren-2 { - # setup: gs = "a((b)" - var gs-storage: grapheme-stack - var gs/edi: (addr grapheme-stack) <- address gs-storage - initialize-grapheme-stack gs, 0x10 - var g/eax: grapheme <- copy 0x61/a - push-grapheme-stack gs, g - g <- copy 0x28/open-paren - push-grapheme-stack gs, g - g <- copy 0x28/open-paren - push-grapheme-stack gs, g - g <- copy 0x62/b - push-grapheme-stack gs, g - g <- copy 0x29/close-paren - 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, 0/no-pixel-graphics - # - var x/eax: int <- render-stack-from-bottom screen, gs, 0/x, 2/y, 1/highlight-matching-open-paren, 0/open-paren-depth - check-screen-row screen, 2/y, "a((b) ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren-2" - check-screen-row-in-color screen, 0xf/fg=white, 2/y, " ( ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren-2: matching paren" -} - -# return the index of the matching close-paren of the grapheme at cursor (top of stack) -# or top index if there's no matching close-paren -fn get-matching-close-paren-index _self: (addr grapheme-stack), render-cursor?: boolean -> _/edx: int { - var self/esi: (addr grapheme-stack) <- copy _self - var top-addr/edx: (addr int) <- get self, top - # if not rendering cursor, return - compare render-cursor?, 0/false - { - break-if-!= - return *top-addr - } - var data-ah/eax: (addr handle array grapheme) <- get self, data - var data/eax: (addr array grapheme) <- lookup *data-ah - var i/ecx: int <- copy *top-addr - # if stack is empty, return - compare i, 0 - { - break-if-> - return *top-addr - } - # if cursor is not '(' return - i <- decrement - var g/esi: (addr grapheme) <- index data, i - compare *g, 0x28/open-paren - { - break-if-= - return *top-addr - } - # otherwise scan to matching paren - var paren-count/ebx: int <- copy 1 - i <- decrement - { - compare i, 0 - break-if-< - var g/esi: (addr grapheme) <- index data, i - compare *g, 0x28/open-paren - { - break-if-!= - paren-count <- increment - } - compare *g, 0x29/close-paren - { - break-if-!= - compare paren-count, 1 - { - break-if-!= - return i - } - paren-count <- decrement - } - i <- decrement - loop - } - return *top-addr -} - -# return the index of the first open-paren at the given depth -# or top index if there's no matching close-paren -fn get-matching-open-paren-index _self: (addr grapheme-stack), control: boolean, depth: int -> _/edx: int { - var self/esi: (addr grapheme-stack) <- copy _self - var top-addr/edx: (addr int) <- get self, top - # if not rendering cursor, return - compare control, 0/false - { - break-if-!= - return *top-addr - } - var data-ah/eax: (addr handle array grapheme) <- get self, data - var data/eax: (addr array grapheme) <- lookup *data-ah - var i/ecx: int <- copy *top-addr - # if stack is empty, return - compare i, 0 - { - break-if-> - return *top-addr - } - # scan to matching open paren - var paren-count/ebx: int <- copy 0 - i <- decrement - { - compare i, 0 - break-if-< - var g/esi: (addr grapheme) <- index data, i - compare *g, 0x29/close-paren - { - break-if-!= - paren-count <- increment - } - compare *g, 0x28/open-paren - { - break-if-!= - compare paren-count, depth - { - break-if-!= - return i - } - paren-count <- decrement - } - i <- decrement - loop - } - return *top-addr -} - -# 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 <- decimal-digit? *g - compare result, 0/false - break-if-= - i <- increment - loop - } - return result -} -- cgit 1.4.1-2-gfad0