diff options
author | Kartik Agaram <vc@akkartik.com> | 2020-09-19 09:02:40 -0700 |
---|---|---|
committer | Kartik Agaram <vc@akkartik.com> | 2020-09-19 09:02:40 -0700 |
commit | a0deaa1cb1920339b0e10cb53c8806c35ed94445 (patch) | |
tree | c304675358923b461995f47474179168a0417fa0 /apps/tile | |
parent | c9093dbb083f4d96b985d87a526ac16cf9ab54a2 (diff) | |
download | mu-a0deaa1cb1920339b0e10cb53c8806c35ed94445.tar.gz |
6801 - snapshot: RPN structured editor
There's some worrisome memory corruption here between the call to max-stack-depth and the callee picking up its args. All this code is incredibly ugly as I start to wrestle with the challenges of structured editors. I keep wanting to keep business logic separate from rendering, but there are feedback loops from wanting to know where to render the cursor. And I haven't even started trying to avoid full-screen renders yet. That'll complect things even more. For now the data path for every iteration of the render loop is: process key compute max depth needed (or any other global information needed for rendering) render
Diffstat (limited to 'apps/tile')
-rw-r--r-- | apps/tile/environment.mu | 181 | ||||
-rw-r--r-- | apps/tile/gap-buffer.mu | 256 | ||||
-rw-r--r-- | apps/tile/int-stack.mu | 49 | ||||
-rw-r--r-- | apps/tile/rpn.mu | 94 | ||||
-rw-r--r-- | apps/tile/word.mu | 95 |
5 files changed, 583 insertions, 92 deletions
diff --git a/apps/tile/environment.mu b/apps/tile/environment.mu index bd2a1814..7814676a 100644 --- a/apps/tile/environment.mu +++ b/apps/tile/environment.mu @@ -1,26 +1,14 @@ type environment { screen: (handle screen) - buf: gap-buffer - cursor-row: int - cursor-col: int + cursor-word: (handle word) } fn initialize-environment _env: (addr environment) { var env/esi: (addr environment) <- copy _env - var screen-ah/edi: (addr handle screen) <- get env, screen - var _screen/eax: (addr screen) <- lookup *screen-ah - var screen/edi: (addr screen) <- copy _screen - { - var cursor-col/eax: (addr int) <- get env, cursor-col - copy-to *cursor-col, 3 - } - { - var cursor-row/eax: (addr int) <- get env, cursor-row - copy-to *cursor-row, 3 - } - # buf - var gap/eax: (addr gap-buffer) <- get env, buf - initialize-gap-buffer gap + var cursor-word-ah/eax: (addr handle word) <- get env, cursor-word + allocate cursor-word-ah + var cursor-word/eax: (addr word) <- lookup *cursor-word-ah + initialize-word cursor-word } fn initialize-environment-with-fake-screen _self: (addr environment), nrows: int, ncols: int { @@ -34,13 +22,23 @@ fn initialize-environment-with-fake-screen _self: (addr environment), nrows: int fn render-loop _self: (addr environment) { var self/esi: (addr environment) <- copy _self - render self + # initial render + { + var screen-ah/edi: (addr handle screen) <- get self, screen + var screen/eax: (addr screen) <- lookup *screen-ah + move-cursor screen, 3, 3 + } # $interactive:loop: { var key/eax: grapheme <- read-key-from-real-keyboard compare key, 0x71 # 'q' break-if-= process self, key + var max-depth/eax: int <- compute-max-depth self + print-string-to-real-screen "ZZ: " + print-int32-decimal-to-real-screen max-depth + print-string-to-real-screen "\n" +#? render self, max-depth loop } } @@ -48,54 +46,48 @@ fn render-loop _self: (addr environment) { fn process _self: (addr environment), key: grapheme { $process:body: { var self/esi: (addr environment) <- copy _self - var screen-ah/edi: (addr handle screen) <- get self, screen - var _screen/eax: (addr screen) <- lookup *screen-ah - var screen/edi: (addr screen) <- copy _screen - var buf/ebx: (addr gap-buffer) <- get self, buf + var cursor-word-ah/eax: (addr handle word) <- get self, cursor-word + var _cursor-word/eax: (addr word) <- lookup *cursor-word-ah + var cursor-word/ecx: (addr word) <- copy _cursor-word compare key, 0x445b1b # left-arrow { break-if-!= - var char-skipped/eax: grapheme <- gap-left buf - compare char-skipped, -1 - { - break-if-= - var cursor-row/eax: (addr int) <- get self, cursor-row - var cursor-col/ecx: (addr int) <- get self, cursor-col - decrement *cursor-col - move-cursor screen, *cursor-row, *cursor-col - } + # TODO: + # gap-left cursor-word + # or + # cursor-word = cursor-word->prev + # gap-to-end cursor-word break $process:body } compare key, 0x435b1b # right-arrow { break-if-!= - var char-skipped/eax: grapheme <- gap-right buf - compare char-skipped, -1 - { - break-if-= - var cursor-row/eax: (addr int) <- get self, cursor-row - var cursor-col/ecx: (addr int) <- get self, cursor-col - increment *cursor-col - move-cursor screen, *cursor-row, *cursor-col - } + # TODO: + # gap-right cursor-word + # or + # cursor-word = cursor-word->next + # gap-to-start cursor-word + break $process:body + } + compare key, 0x20 # space + { + break-if-!= + # TODO: new word break $process:body } - var g/ecx: grapheme <- copy key + var g/edx: grapheme <- copy key var print?/eax: boolean <- real-grapheme? key { compare print?, 0 # false break-if-= - add-grapheme-at-gap buf, g - var cursor-col/eax: (addr int) <- get self, cursor-col - increment *cursor-col - render self + add-grapheme-to-word cursor-word, g break $process:body } # silently ignore other hotkeys } } -fn render _env: (addr environment) { +fn render _env: (addr environment), max-depth: int { var env/esi: (addr environment) <- copy _env var screen-ah/edi: (addr handle screen) <- get env, screen var _screen/eax: (addr screen) <- lookup *screen-ah @@ -103,13 +95,92 @@ fn render _env: (addr environment) { # prepare screen clear-screen screen move-cursor screen, 3, 3 - # render input area - var buf/ecx: (addr gap-buffer) <- get env, buf - render-gap-buffer screen, buf -#? # render stacks -#? render-all-stacks screen - # update cursor - var cursor-row/eax: (addr int) <- get env, cursor-row - var cursor-col/ecx: (addr int) <- get env, cursor-col - move-cursor screen, *cursor-row, *cursor-col + # cursor-word + var cursor-word-ah/esi: (addr handle word) <- get env, cursor-word + var _cursor-word/eax: (addr word) <- lookup *cursor-word-ah + var cursor-word/ebx: (addr word) <- copy _cursor-word + # curr-word + var curr-word/eax: (addr word) <- first-word cursor-word + # first-word + var first-word: (addr word) + copy-to first-word, curr-word + # cursor-col + var cursor-col: int + var cursor-col-a: (addr int) + var tmp/ecx: (addr int) <- address cursor-col + copy-to cursor-col-a, tmp + # curr-col + var curr-col/ecx: int <- copy 3 + { + compare curr-word, 0 + break-if-= + move-cursor screen, 3, curr-col + print-word screen, curr-word + curr-col <- render-stack screen, first-word, curr-word, max-depth, curr-col, cursor-word, cursor-col-a + var next-word-ah/edx: (addr handle word) <- get curr-word, next + curr-word <- lookup *next-word-ah + loop + } + move-cursor screen, 3, *cursor-col-a +} + +# Render the stack result from interpreting first-world to final-word (inclusive) +# with the bottom-left corner at botleft-row, botleft-col. +# +# Outputs: +# - Return the farthest column written. +# - If final-word is same as cursor-word, do some additional computation to set +# cursor-col-a. +fn render-stack screen: (addr screen), first-word: (addr word), final-word: (addr word), botleft-row: int, botleft-col: int, cursor-word: (addr word), cursor-col-a: (addr int) -> right-col/ecx: int { + print-word screen, first-word +} + +# We could be a little faster by not using 'first-word' (since max is commutative), +# but this way the code follows the pattern of 'render'. Let's see if that's a net win. +fn compute-max-depth _env: (addr environment) -> result/eax: int { + var env/esi: (addr environment) <- copy _env + # cursor-word + var cursor-word-ah/esi: (addr handle word) <- get env, cursor-word + var cursor-word/eax: (addr word) <- lookup *cursor-word-ah + { + var foo/eax: int <- copy cursor-word + print-string-to-real-screen "cursor-word: " + print-int32-hex-to-real-screen foo + print-string-to-real-screen "\n" + } + # curr-word + var curr-word/eax: (addr word) <- first-word cursor-word + { + var foo/eax: int <- copy curr-word + print-string-to-real-screen "curr-word: " + print-int32-hex-to-real-screen foo + print-string-to-real-screen "\n" + } + # first-word + var first-word: (addr word) + copy-to first-word, curr-word + # + var out/ebx: int <- copy 0 + { + compare curr-word, 0 + break-if-= + { + var a/eax: int <- copy first-word + print-string-to-real-screen "outside max-stack-depth: " + print-int32-hex-to-real-screen a + print-string-to-real-screen "\n" + } + var curr-max-depth/edi: int <- max-stack-depth first-word, curr-word + compare curr-max-depth, out + { + break-if-<= + out <- copy curr-max-depth + } + var next-word-ah/edx: (addr handle word) <- get curr-word, next + curr-word <- lookup *next-word-ah + loop + } + print-int32-decimal-to-real-screen out + print-string-to-real-screen "\n" + result <- copy out } diff --git a/apps/tile/gap-buffer.mu b/apps/tile/gap-buffer.mu index 9d6b7161..1503690b 100644 --- a/apps/tile/gap-buffer.mu +++ b/apps/tile/gap-buffer.mu @@ -11,6 +11,22 @@ fn initialize-gap-buffer _self: (addr gap-buffer) { initialize-grapheme-stack right, 0x10 } +# just for tests +fn initialize-gap-buffer-with self: (addr gap-buffer), s: (addr array byte) { + initialize-gap-buffer self + var stream-storage: (stream byte 0x10) + 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 render-gap-buffer screen: (addr screen), _gap: (addr gap-buffer) { var gap/esi: (addr gap-buffer) <- copy _gap var left/eax: (addr grapheme-stack) <- get gap, left @@ -30,49 +46,26 @@ fn gap-buffer-length _gap: (addr gap-buffer) -> result/eax: int { result <- add left-length } -# dump stack to screen from bottom to top -# don't move the cursor or anything -fn render-stack-from-bottom _self: (addr grapheme-stack), screen: (addr screen) { - 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 - print-grapheme screen, *g - i <- increment - loop - } +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 } -# dump stack to screen from top to bottom -# don't move the cursor or anything -fn render-stack-from-top _self: (addr grapheme-stack), screen: (addr screen) { - 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 +fn gap-to-start self: (addr gap-buffer) { { - compare i, 0 - break-if-< - var g/edx: (addr grapheme) <- index data, i - print-grapheme screen, *g - i <- decrement - loop + var curr/eax: grapheme <- gap-left self + compare curr, -1 + loop-if-!= } } -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-end self: (addr gap-buffer) { + { + var curr/eax: grapheme <- gap-right self + compare curr, -1 + loop-if-!= + } } fn gap-right _self: (addr gap-buffer) -> result/eax: grapheme { @@ -112,6 +105,79 @@ $gap-left:body: { } } +fn gap-buffer-equal? _self: (addr gap-buffer), s: (addr array byte) -> result/eax: boolean { +$gap-buffer-equal?:body: { + 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) + 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 + result <- prefix-match? left, expected-stream + compare result, 0 # false + break-if-= $gap-buffer-equal?:body + # compare right + var right/edx: (addr grapheme-stack) <- get self, right + result <- suffix-match? right, expected-stream + compare result, 0 # false + break-if-= $gap-buffer-equal?:body + # ensure there's nothing left over + result <- stream-empty? expected-stream +} +} + +fn test-gap-buffer-equal-from-end? { + var _g: gap-buffer + var g/esi: (addr gap-buffer) <- address _g + initialize-gap-buffer g + # + 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" + var result/eax: int <- copy _result + check-ints-equal result, 1, "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 + # + 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" + var result/eax: int <- copy _result + check-ints-equal result, 1, "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 + # + 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" + var result/eax: int <- copy _result + check-ints-equal result, 1, "F - test-gap-buffer-equal-from-start?" +} + type grapheme-stack { data: (handle array grapheme) top: int @@ -125,6 +191,20 @@ fn initialize-grapheme-stack _self: (addr grapheme-stack), n: int { copy-to *top, 0 } +fn grapheme-stack-empty? _self: (addr grapheme-stack) -> result/eax: boolean { +$grapheme-stack-empty?:body: { + var self/esi: (addr grapheme-stack) <- copy _self + var top/eax: (addr int) <- get self, top + compare *top, 0 + { + break-if-= + result <- copy 1 # false + break $grapheme-stack-empty?:body + } + result <- copy 0 # false +} +} + 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 @@ -155,3 +235,105 @@ $pop-grapheme-stack:body: { val <- copy *result-addr } } + +# dump stack to screen from bottom to top +# don't move the cursor or anything +fn render-stack-from-bottom _self: (addr grapheme-stack), screen: (addr screen) { + 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 + print-grapheme screen, *g + i <- increment + loop + } +} + +# dump stack to screen from top to bottom +# don't move the cursor or anything +fn render-stack-from-top _self: (addr grapheme-stack), screen: (addr screen) { + 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 + print-grapheme screen, *g + i <- decrement + loop + } +} + +# 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) -> result/eax: boolean { +$prefix-match?:body: { + 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-= + result <- copy 0 # false + break $prefix-match?:body + } + } + i <- increment + loop + } + result <- copy 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) -> result/eax: boolean { +$suffix-match?:body: { + 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-= + result <- copy 0 # false + break $suffix-match?:body + } + } + i <- decrement + loop + } + result <- copy 1 # true +} +} diff --git a/apps/tile/int-stack.mu b/apps/tile/int-stack.mu new file mode 100644 index 00000000..5e7a2223 --- /dev/null +++ b/apps/tile/int-stack.mu @@ -0,0 +1,49 @@ +type int-stack { + data: (handle array int) + top: int +} + +fn initialize-int-stack _self: (addr int-stack), n: int { + var self/esi: (addr int-stack) <- copy _self + var d/edi: (addr handle array int) <- get self, data + populate d, n + var top/eax: (addr int) <- get self, top + copy-to *top, 0 +} + +fn clear-int-stack _self: (addr int-stack) { + var self/esi: (addr int-stack) <- copy _self + var top/eax: (addr int) <- get self, top + copy-to *top, 0 +} + +fn push-int-stack _self: (addr int-stack), _val: int { + var self/esi: (addr int-stack) <- copy _self + var top-addr/ecx: (addr int) <- get self, top + var data-ah/edx: (addr handle array int) <- get self, data + var data/eax: (addr array int) <- lookup *data-ah + var top/edx: int <- copy *top-addr + var dest-addr/edx: (addr int) <- index data, top + var val/eax: int <- copy _val + copy-to *dest-addr, val + add-to *top-addr, 1 +} + +fn pop-int-stack _self: (addr int-stack) -> val/eax: int { +$pop-int-stack:body: { + var self/esi: (addr int-stack) <- copy _self + var top-addr/ecx: (addr int) <- get self, top + { + compare *top-addr, 0 + break-if-> + val <- copy 0 + break $pop-int-stack:body + } + subtract-from *top-addr, 1 + var data-ah/edx: (addr handle array int) <- get self, data + var data/eax: (addr array int) <- lookup *data-ah + var top/edx: int <- copy *top-addr + var result-addr/eax: (addr int) <- index data, top + val <- copy *result-addr +} +} diff --git a/apps/tile/rpn.mu b/apps/tile/rpn.mu new file mode 100644 index 00000000..a70d9fd5 --- /dev/null +++ b/apps/tile/rpn.mu @@ -0,0 +1,94 @@ +fn simplify in: (addr stream byte), out: (addr int-stack) { + var word-storage: slice + var word/ecx: (addr slice) <- address word-storage + clear-int-stack out + $simplify:word-loop: { + next-word in, word + var done?/eax: boolean <- slice-empty? word + compare done?, 0 + break-if-!= + # if word is an operator, perform it + { + var is-add?/eax: boolean <- slice-equal? word, "+" + compare is-add?, 0 + break-if-= + var _b/eax: int <- pop-int-stack out + var b/edx: int <- copy _b + var a/eax: int <- pop-int-stack out + a <- add b + push-int-stack out, a + loop $simplify:word-loop + } + { + var is-sub?/eax: boolean <- slice-equal? word, "-" + compare is-sub?, 0 + break-if-= + var _b/eax: int <- pop-int-stack out + var b/edx: int <- copy _b + var a/eax: int <- pop-int-stack out + a <- subtract b + push-int-stack out, a + loop $simplify:word-loop + } + { + var is-mul?/eax: boolean <- slice-equal? word, "*" + compare is-mul?, 0 + break-if-= + var _b/eax: int <- pop-int-stack out + var b/edx: int <- copy _b + var a/eax: int <- pop-int-stack out + a <- multiply b + push-int-stack out, a + loop $simplify:word-loop + } + # otherwise it's an int + var n/eax: int <- parse-decimal-int-from-slice word + push-int-stack out, n + loop + } +} + +# Copy of 'simplify' that just tracks the maximum stack depth needed +# Doesn't actually need to simulate the stack, since every word has a predictable effect. +fn max-stack-depth first-word: (addr word), final-word: (addr word) -> result/edi: int { + var a/eax: int <- copy first-word + print-string-to-real-screen "inside max-stack-depth: " + print-int32-hex-to-real-screen a + print-string-to-real-screen "\n" + var curr-word/esi: (addr word) <- copy first-word + var curr-depth/ecx: int <- copy 0 + result <- copy 0 + $max-stack-depth:word-loop: { + # handle operators + { + var is-add?/eax: boolean <- word-equal? curr-word, "+" + compare is-add?, 0 + break-if-= + curr-depth <- decrement + loop $max-stack-depth:word-loop + } + { + var is-sub?/eax: boolean <- word-equal? curr-word, "-" + compare is-sub?, 0 + break-if-= + curr-depth <- decrement + loop $max-stack-depth:word-loop + } + { + var is-mul?/eax: boolean <- word-equal? curr-word, "*" + compare is-mul?, 0 + break-if-= + curr-depth <- decrement + loop $max-stack-depth:word-loop + } + # otherwise it's an int (do we need error-checking?) + curr-depth <- increment + # update max depth if necessary + { + compare curr-depth, result + break-if-<= + result <- copy curr-depth + } + loop + } +} diff --git a/apps/tile/word.mu b/apps/tile/word.mu new file mode 100644 index 00000000..c87a2242 --- /dev/null +++ b/apps/tile/word.mu @@ -0,0 +1,95 @@ +type word { + data: gap-buffer + next: (handle word) + prev: (handle word) +} + +fn initialize-word _self: (addr word) { + var self/esi: (addr word) <- copy _self + var data/eax: (addr gap-buffer) <- get self, data + initialize-gap-buffer data +} + +## some helpers for creating words. mostly for tests + +fn initialize-word-with _self: (addr word), s: (addr array byte) { + var self/esi: (addr word) <- copy _self + var data/eax: (addr gap-buffer) <- get self, data + initialize-gap-buffer-with data, s +} + +fn allocate-word-with _out: (addr handle word), s: (addr array byte) { + var out/eax: (addr handle word) <- copy _out + allocate out + var out-addr/eax: (addr word) <- lookup *out + initialize-word-with out-addr, s +} + +# just for tests for now +# TODO: handle existing next +# one implication of handles: append must take a handle +fn append-word-with self-h: (handle word), s: (addr array byte) { + var self/eax: (addr word) <- lookup self-h + var next-ah/eax: (addr handle word) <- get self, next + allocate-word-with next-ah, s + var next/eax: (addr word) <- lookup *next-ah + var prev-ah/eax: (addr handle word) <- get next, prev + copy-handle self-h, prev-ah +} + +# just for tests for now +# TODO: handle existing prev +fn prepend-word-with self-h: (handle word), s: (addr array byte) { + var self/eax: (addr word) <- lookup self-h + var prev-ah/eax: (addr handle word) <- get self, prev + allocate-word-with prev-ah, s + var prev/eax: (addr word) <- lookup *prev-ah + var next-ah/eax: (addr handle word) <- get prev, next + copy-handle self-h, next-ah +} + +## real primitives + +fn word-equal? _self: (addr word), s: (addr array byte) -> result/eax: boolean { + var self/esi: (addr word) <- copy _self + var data/eax: (addr gap-buffer) <- get self, data + result <- gap-buffer-equal? data, s +} + +fn first-word _self: (addr word) -> result/eax: (addr word) { + var self/esi: (addr word) <- copy _self + var out/edi: (addr word) <- copy self + var prev/esi: (addr handle word) <- get self, prev + { + var curr/eax: (addr word) <- lookup *prev + compare curr, 0 + break-if-= + out <- copy curr + prev <- get curr, prev + loop + } + result <- copy out +} + +fn final-word _self: (addr word), out: (addr handle word) { + var self/esi: (addr word) <- copy _self + var next/esi: (addr handle word) <- get self, next + { + copy-object next, out + var curr/eax: (addr word) <- lookup *next + compare curr, 0 + loop-if-!= + } +} + +fn add-grapheme-to-word _self: (addr word), c: grapheme { + var self/esi: (addr word) <- copy _self + var data/eax: (addr gap-buffer) <- get self, data + add-grapheme-at-gap data, c +} + +fn print-word screen: (addr screen), _self: (addr word) { + var self/esi: (addr word) <- copy _self + var data/eax: (addr gap-buffer) <- get self, data + render-gap-buffer screen, data +} |