diff --git a/html/101screen.subx.html b/html/101screen.subx.html index 775988d0..53bdb2a7 100644 --- a/html/101screen.subx.html +++ b/html/101screen.subx.html @@ -1,29 +1,29 @@ - +
- ++ 1 # Inserting and deleting in arrays. + 2 # + 3 # The primitives here just do the work of making space and compacting. + 4 + 5 fn slide-up _a: (addr array int), start: int, end: int, target: int { + 6 var a/esi: (addr array int) <- copy _a + 7 var src-idx/ecx: int <- copy start + 8 var dest-idx/edx: int <- copy target + 9 # if start == target, nothing to do + 10 { + 11 compare src-idx, dest-idx + 12 break-if-!= + 13 return + 14 } + 15 # if start < target, abort + 16 { + 17 compare src-idx, dest-idx + 18 break-if-> + 19 abort "slide-up: target > start; use slide-down instead" + 20 } + 21 # perform the copy + 22 { + 23 compare src-idx, end + 24 break-if->= + 25 var dest/ebx: (addr int) <- index a, dest-idx + 26 var src/eax: (addr int) <- index a, src-idx + 27 var val/eax: int <- copy *src + 28 copy-to *dest, val + 29 src-idx <- increment + 30 dest-idx <- increment + 31 loop + 32 } + 33 } + 34 + 35 fn slide-down _a: (addr array int), start: int, end: int, target: int { + 36 var a/esi: (addr array int) <- copy _a + 37 var src-idx/ecx: int <- copy end + 38 src-idx <- decrement + 39 var dest-idx/edx: int <- copy target + 40 dest-idx <- add end + 41 dest-idx <- subtract start + 42 dest-idx <- decrement + 43 # if start == target, nothing to do + 44 { + 45 compare src-idx, dest-idx + 46 break-if-!= + 47 return + 48 } + 49 # if start > target, abort + 50 { + 51 compare src-idx, dest-idx + 52 break-if-< + 53 abort "slide-down: target < start; use slide-down instead" + 54 } + 55 # perform the copy + 56 { + 57 compare src-idx, start + 58 break-if-< + 59 var dest/ebx: (addr int) <- index a, dest-idx + 60 var src/eax: (addr int) <- index a, src-idx + 61 var val/eax: int <- copy *src + 62 copy-to *dest, val + 63 src-idx <- decrement + 64 dest-idx <- decrement + 65 loop + 66 } + 67 } + 68 + 69 fn test-slide-up { + 70 check-slide-up "0 1 2 3 0", 1/start 1/end, 0/target, "0 1 2 3 0", "F - test-slide-up/empty-interval" + 71 check-slide-up "0 1 2 3 0", 1/start 2/end, 0/target, "1 1 2 3 0", "F - test-slide-up/single-non-overlapping" + 72 check-slide-up "0 0 0 1 2 3 0", 3/start 6/end, 0/target, "1 2 3 1 2 3 0", "F - test-slide-up/multiple-non-overlapping" + 73 check-slide-up "0 1 2 3 0", 1/start 4/end, 0/target, "1 2 3 3 0", "F - test-slide-up/overlapping" + 74 } + 75 + 76 fn test-slide-down { + 77 check-slide-down "0 1 2 3 0", 1/start 1/end, 4/target, "0 1 2 3 0", "F - test-slide-down/empty-interval" + 78 check-slide-down "0 1 2 3 0", 1/start 2/end, 4/target, "0 1 2 3 1", "F - test-slide-down/single-non-overlapping" + 79 check-slide-down "0 1 2 3 0 0 0", 1/start 4/end, 4/target, "0 1 2 3 1 2 3", "F - test-slide-down/multiple-non-overlapping" + 80 check-slide-down "0 1 2 3 0", 1/start 4/end, 2/target, "0 1 1 2 3", "F - test-slide-down/overlapping" + 81 } + 82 + 83 # Return the index that val is at. + 84 # If not found, return len-1. + 85 # That way the result is always a valid index to pass into slide-down. + 86 fn find-slide-down-slot-in-array _a: (addr array int), _val: int -> _/ecx: int { + 87 var a/esi: (addr array int) <- copy _a + 88 var val/ebx: int <- copy _val + 89 var max/edx: int <- length a + 90 max <- decrement + 91 var i/ecx: int <- copy 0 + 92 { + 93 compare i, max + 94 break-if->= + 95 var curr/eax: (addr int) <- index a, i + 96 compare *curr, val + 97 break-if-= + 98 i <- increment + 99 loop +100 } +101 return i +102 } +103 +104 # helpers for tests +105 fn check-slide-up before: (addr array byte), start: int, end: int, target: int, after: (addr array byte), msg: (addr array byte) { +106 var arr-h: (handle array int) +107 var arr-ah/eax: (addr handle array int) <- address arr-h +108 parse-array-of-decimal-ints before, arr-ah +109 var arr/eax: (addr array int) <- lookup *arr-ah +110 slide-up arr, start, end, target +111 check-array-equal arr, after, msg +112 } +113 +114 fn check-slide-down before: (addr array byte), start: int, end: int, target: int, after: (addr array byte), msg: (addr array byte) { +115 var arr-h: (handle array int) +116 var arr-ah/eax: (addr handle array int) <- address arr-h +117 parse-array-of-decimal-ints before, arr-ah +118 var arr/eax: (addr array int) <- lookup *arr-ah +119 slide-down arr, start, end, target +120 check-array-equal arr, after, msg +121 } ++ + + diff --git a/html/513grapheme-stack.mu.html b/html/513grapheme-stack.mu.html new file mode 100644 index 00000000..2b7ce0c3 --- /dev/null +++ b/html/513grapheme-stack.mu.html @@ -0,0 +1,624 @@ + + + + +
+ 1 # grapheme stacks are the smallest unit of editable text + 2 + 3 type grapheme-stack { + 4 data: (handle array grapheme) + 5 top: int + 6 } + 7 + 8 fn initialize-grapheme-stack _self: (addr grapheme-stack), n: int { + 9 var self/esi: (addr grapheme-stack) <- copy _self + 10 var d/edi: (addr handle array grapheme) <- get self, data + 11 populate d, n + 12 var top/eax: (addr int) <- get self, top + 13 copy-to *top, 0 + 14 } + 15 + 16 fn clear-grapheme-stack _self: (addr grapheme-stack) { + 17 var self/esi: (addr grapheme-stack) <- copy _self + 18 var top/eax: (addr int) <- get self, top + 19 copy-to *top, 0 + 20 } + 21 + 22 fn grapheme-stack-empty? _self: (addr grapheme-stack) -> _/eax: boolean { + 23 var self/esi: (addr grapheme-stack) <- copy _self + 24 var top/eax: (addr int) <- get self, top + 25 compare *top, 0 + 26 { + 27 break-if-!= + 28 return 1/true + 29 } + 30 return 0/false + 31 } + 32 + 33 fn grapheme-stack-length _self: (addr grapheme-stack) -> _/eax: int { + 34 var self/esi: (addr grapheme-stack) <- copy _self + 35 var top/eax: (addr int) <- get self, top + 36 return *top + 37 } + 38 + 39 fn push-grapheme-stack _self: (addr grapheme-stack), _val: grapheme { + 40 var self/esi: (addr grapheme-stack) <- copy _self + 41 var top-addr/ecx: (addr int) <- get self, top + 42 var data-ah/edx: (addr handle array grapheme) <- get self, data + 43 var data/eax: (addr array grapheme) <- lookup *data-ah + 44 var top/edx: int <- copy *top-addr + 45 var dest-addr/edx: (addr grapheme) <- index data, top + 46 var val/eax: grapheme <- copy _val + 47 copy-to *dest-addr, val + 48 add-to *top-addr, 1 + 49 } + 50 + 51 fn pop-grapheme-stack _self: (addr grapheme-stack) -> _/eax: grapheme { + 52 var self/esi: (addr grapheme-stack) <- copy _self + 53 var top-addr/ecx: (addr int) <- get self, top + 54 { + 55 compare *top-addr, 0 + 56 break-if-> + 57 return -1 + 58 } + 59 subtract-from *top-addr, 1 + 60 var data-ah/edx: (addr handle array grapheme) <- get self, data + 61 var data/eax: (addr array grapheme) <- lookup *data-ah + 62 var top/edx: int <- copy *top-addr + 63 var result-addr/eax: (addr grapheme) <- index data, top + 64 return *result-addr + 65 } + 66 + 67 fn copy-grapheme-stack _src: (addr grapheme-stack), dest: (addr grapheme-stack) { + 68 var src/esi: (addr grapheme-stack) <- copy _src + 69 var data-ah/edi: (addr handle array grapheme) <- get src, data + 70 var _data/eax: (addr array grapheme) <- lookup *data-ah + 71 var data/edi: (addr array grapheme) <- copy _data + 72 var top-addr/ecx: (addr int) <- get src, top + 73 var i/eax: int <- copy 0 + 74 { + 75 compare i, *top-addr + 76 break-if->= + 77 var g/edx: (addr grapheme) <- index data, i + 78 push-grapheme-stack dest, *g + 79 i <- increment + 80 loop + 81 } + 82 } + 83 + 84 # dump stack to screen from bottom to top + 85 # hardcoded colors: + 86 # matching paren + 87 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 { + 88 var self/esi: (addr grapheme-stack) <- copy _self + 89 var matching-open-paren-index/edx: int <- get-matching-open-paren-index self, highlight-matching-open-paren?, open-paren-depth + 90 var data-ah/edi: (addr handle array grapheme) <- get self, data + 91 var _data/eax: (addr array grapheme) <- lookup *data-ah + 92 var data/edi: (addr array grapheme) <- copy _data + 93 var x/eax: int <- copy _x + 94 var y/ecx: int <- copy _y + 95 var top-addr/esi: (addr int) <- get self, top + 96 var i/ebx: int <- copy 0 + 97 { + 98 compare i, *top-addr + 99 break-if->= +100 { +101 var g/esi: (addr grapheme) <- index data, i +102 var fg: int +103 { +104 var tmp/eax: int <- copy color +105 copy-to fg, tmp +106 } +107 { +108 compare i, matching-open-paren-index +109 break-if-!= +110 copy-to fg, 0xf/highlight +111 } +112 x, y <- render-grapheme screen, *g, xmin, ymin, xmax, ymax, x, y, fg, background-color +113 } +114 i <- increment +115 loop +116 } +117 return x, y +118 } +119 +120 # helper for small words +121 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 { +122 var _width/eax: int <- copy 0 +123 var _height/ecx: int <- copy 0 +124 _width, _height <- screen-size screen +125 var width/edx: int <- copy _width +126 var height/ebx: int <- copy _height +127 var x2/eax: int <- copy 0 +128 var y2/ecx: int <- copy 0 +129 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 +130 return x2 # y2? yolo +131 } +132 +133 # dump stack to screen from top to bottom +134 # optionally render a 'cursor' with the top grapheme +135 # hard-coded colors: +136 # matching paren +137 # cursor +138 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 { +139 var self/esi: (addr grapheme-stack) <- copy _self +140 var matching-close-paren-index/edx: int <- get-matching-close-paren-index self, render-cursor? +141 var data-ah/eax: (addr handle array grapheme) <- get self, data +142 var _data/eax: (addr array grapheme) <- lookup *data-ah +143 var data/edi: (addr array grapheme) <- copy _data +144 var x/eax: int <- copy _x +145 var y/ecx: int <- copy _y +146 var top-addr/ebx: (addr int) <- get self, top +147 var i/ebx: int <- copy *top-addr +148 i <- decrement +149 # if render-cursor?, peel off first iteration +150 { +151 compare render-cursor?, 0/false +152 break-if-= +153 compare i, 0 +154 break-if-< +155 var g/esi: (addr grapheme) <- index data, i +156 x, y <- render-grapheme screen, *g, xmin, ymin, xmax, ymax, x, y, background-color, color +157 i <- decrement +158 } +159 # remaining iterations +160 { +161 compare i, 0 +162 break-if-< +163 # highlight matching paren if needed +164 var fg: int +165 { +166 var tmp/eax: int <- copy color +167 copy-to fg, tmp +168 } +169 compare i, matching-close-paren-index +170 { +171 break-if-!= +172 copy-to fg, 0xf/highlight +173 } +174 # +175 var g/esi: (addr grapheme) <- index data, i +176 x, y <- render-grapheme screen, *g, xmin, ymin, xmax, ymax, x, y, fg, background-color +177 i <- decrement +178 loop +179 } +180 return x, y +181 } +182 +183 # helper for small words +184 fn render-stack-from-top screen: (addr screen), self: (addr grapheme-stack), x: int, y: int, render-cursor?: boolean -> _/eax: int { +185 var _width/eax: int <- copy 0 +186 var _height/ecx: int <- copy 0 +187 _width, _height <- screen-size screen +188 var width/edx: int <- copy _width +189 var height/ebx: int <- copy _height +190 var x2/eax: int <- copy 0 +191 var y2/ecx: int <- copy 0 +192 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 +193 return x2 # y2? yolo +194 } +195 +196 fn test-render-grapheme-stack { +197 # setup: gs = "abc" +198 var gs-storage: grapheme-stack +199 var gs/edi: (addr grapheme-stack) <- address gs-storage +200 initialize-grapheme-stack gs, 5 +201 var g/eax: grapheme <- copy 0x61/a +202 push-grapheme-stack gs, g +203 g <- copy 0x62/b +204 push-grapheme-stack gs, g +205 g <- copy 0x63/c +206 push-grapheme-stack gs, g +207 # setup: screen +208 var screen-on-stack: screen +209 var screen/esi: (addr screen) <- address screen-on-stack +210 initialize-screen screen, 5, 4, 0/no-pixel-graphics +211 # +212 var x/eax: int <- render-stack-from-bottom screen, gs, 0/x, 0/y, 0/no-highlight-matching-open-paren, 0/open-paren-depth +213 check-screen-row screen, 0/y, "abc ", "F - test-render-grapheme-stack from bottom" +214 check-ints-equal x, 3, "F - test-render-grapheme-stack from bottom: result" +215 check-background-color-in-screen-row screen, 3/bg=reverse, 0/y, " ", "F - test-render-grapheme-stack from bottom: bg" +216 # +217 var x/eax: int <- render-stack-from-top screen, gs, 0/x, 1/y, 0/cursor=false +218 check-screen-row screen, 1/y, "cba ", "F - test-render-grapheme-stack from top without cursor" +219 check-ints-equal x, 3, "F - test-render-grapheme-stack from top without cursor: result" +220 check-background-color-in-screen-row screen, 3/bg=reverse, 1/y, " ", "F - test-render-grapheme-stack from top without cursor: bg" +221 # +222 var x/eax: int <- render-stack-from-top screen, gs, 0/x, 2/y, 1/cursor=true +223 check-screen-row screen, 2/y, "cba ", "F - test-render-grapheme-stack from top with cursor" +224 check-ints-equal x, 3, "F - test-render-grapheme-stack from top with cursor: result" +225 check-background-color-in-screen-row screen, 3/bg=reverse, 2/y, "| ", "F - test-render-grapheme-stack from top with cursor: bg" +226 } +227 +228 fn test-render-grapheme-stack-while-highlighting-matching-close-paren { +229 # setup: gs = "(b)" +230 var gs-storage: grapheme-stack +231 var gs/edi: (addr grapheme-stack) <- address gs-storage +232 initialize-grapheme-stack gs, 5 +233 var g/eax: grapheme <- copy 0x29/close-paren +234 push-grapheme-stack gs, g +235 g <- copy 0x62/b +236 push-grapheme-stack gs, g +237 g <- copy 0x28/open-paren +238 push-grapheme-stack gs, g +239 # setup: screen +240 var screen-on-stack: screen +241 var screen/esi: (addr screen) <- address screen-on-stack +242 initialize-screen screen, 5, 4, 0/no-pixel-graphics +243 # +244 var x/eax: int <- render-stack-from-top screen, gs, 0/x, 2/y, 1/cursor=true +245 check-screen-row screen, 2/y, "(b) ", "F - test-render-grapheme-stack-while-highlighting-matching-close-paren" +246 check-background-color-in-screen-row screen, 3/bg=reverse, 2/y, "| ", "F - test-render-grapheme-stack-while-highlighting-matching-close-paren: cursor" +247 check-screen-row-in-color screen, 0xf/fg=white, 2/y, " ) ", "F - test-render-grapheme-stack-while-highlighting-matching-close-paren: matching paren" +248 } +249 +250 fn test-render-grapheme-stack-while-highlighting-matching-close-paren-2 { +251 # setup: gs = "(a (b)) c" +252 var gs-storage: grapheme-stack +253 var gs/edi: (addr grapheme-stack) <- address gs-storage +254 initialize-grapheme-stack gs, 0x10 +255 var g/eax: grapheme <- copy 0x63/c +256 push-grapheme-stack gs, g +257 g <- copy 0x20/space +258 push-grapheme-stack gs, g +259 g <- copy 0x29/close-paren +260 push-grapheme-stack gs, g +261 g <- copy 0x29/close-paren +262 push-grapheme-stack gs, g +263 g <- copy 0x62/b +264 push-grapheme-stack gs, g +265 g <- copy 0x28/open-paren +266 push-grapheme-stack gs, g +267 g <- copy 0x20/space +268 push-grapheme-stack gs, g +269 g <- copy 0x61/a +270 push-grapheme-stack gs, g +271 g <- copy 0x28/open-paren +272 push-grapheme-stack gs, g +273 # setup: screen +274 var screen-on-stack: screen +275 var screen/esi: (addr screen) <- address screen-on-stack +276 initialize-screen screen, 5, 4, 0/no-pixel-graphics +277 # +278 var x/eax: int <- render-stack-from-top screen, gs, 0/x, 2/y, 1/cursor=true +279 check-screen-row screen, 2/y, "(a (b)) c ", "F - test-render-grapheme-stack-while-highlighting-matching-close-paren-2" +280 check-background-color-in-screen-row screen, 3/bg=reverse, 2/y, "| ", "F - test-render-grapheme-stack-while-highlighting-matching-close-paren-2: cursor" +281 check-screen-row-in-color screen, 0xf/fg=white, 2/y, " ) ", "F - test-render-grapheme-stack-while-highlighting-matching-close-paren-2: matching paren" +282 } +283 +284 fn test-render-grapheme-stack-while-highlighting-matching-open-paren-with-close-paren-at-end { +285 # setup: gs = "(b)" +286 var gs-storage: grapheme-stack +287 var gs/edi: (addr grapheme-stack) <- address gs-storage +288 initialize-grapheme-stack gs, 5 +289 var g/eax: grapheme <- copy 0x28/open-paren +290 push-grapheme-stack gs, g +291 g <- copy 0x62/b +292 push-grapheme-stack gs, g +293 g <- copy 0x29/close-paren +294 push-grapheme-stack gs, g +295 # setup: screen +296 var screen-on-stack: screen +297 var screen/esi: (addr screen) <- address screen-on-stack +298 initialize-screen screen, 5, 4, 0/no-pixel-graphics +299 # +300 var x/eax: int <- render-stack-from-bottom screen, gs, 0/x, 2/y, 1/highlight-matching-open-paren, 1/open-paren-depth +301 check-screen-row screen, 2/y, "(b) ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren-with-close-paren-at-end" +302 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" +303 } +304 +305 fn test-render-grapheme-stack-while-highlighting-matching-open-paren-with-close-paren-at-end-2 { +306 # setup: gs = "a((b))" +307 var gs-storage: grapheme-stack +308 var gs/edi: (addr grapheme-stack) <- address gs-storage +309 initialize-grapheme-stack gs, 0x10 +310 var g/eax: grapheme <- copy 0x61/a +311 push-grapheme-stack gs, g +312 g <- copy 0x28/open-paren +313 push-grapheme-stack gs, g +314 g <- copy 0x28/open-paren +315 push-grapheme-stack gs, g +316 g <- copy 0x62/b +317 push-grapheme-stack gs, g +318 g <- copy 0x29/close-paren +319 push-grapheme-stack gs, g +320 g <- copy 0x29/close-paren +321 push-grapheme-stack gs, g +322 # setup: screen +323 var screen-on-stack: screen +324 var screen/esi: (addr screen) <- address screen-on-stack +325 initialize-screen screen, 5, 4, 0/no-pixel-graphics +326 # +327 var x/eax: int <- render-stack-from-bottom screen, gs, 0/x, 2/y, 1/highlight-matching-open-paren, 1/open-paren-depth +328 check-screen-row screen, 2/y, "a((b)) ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren-with-close-paren-at-end-2" +329 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" +330 } +331 +332 fn test-render-grapheme-stack-while-highlighting-matching-open-paren { +333 # setup: gs = "(b" +334 var gs-storage: grapheme-stack +335 var gs/edi: (addr grapheme-stack) <- address gs-storage +336 initialize-grapheme-stack gs, 5 +337 var g/eax: grapheme <- copy 0x28/open-paren +338 push-grapheme-stack gs, g +339 g <- copy 0x62/b +340 push-grapheme-stack gs, g +341 # setup: screen +342 var screen-on-stack: screen +343 var screen/esi: (addr screen) <- address screen-on-stack +344 initialize-screen screen, 5, 4, 0/no-pixel-graphics +345 # +346 var x/eax: int <- render-stack-from-bottom screen, gs, 0/x, 2/y, 1/highlight-matching-open-paren, 0/open-paren-depth +347 check-screen-row screen, 2/y, "(b ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren" +348 check-screen-row-in-color screen, 0xf/fg=white, 2/y, "( ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren: matching paren" +349 } +350 +351 fn test-render-grapheme-stack-while-highlighting-matching-open-paren-2 { +352 # setup: gs = "a((b)" +353 var gs-storage: grapheme-stack +354 var gs/edi: (addr grapheme-stack) <- address gs-storage +355 initialize-grapheme-stack gs, 0x10 +356 var g/eax: grapheme <- copy 0x61/a +357 push-grapheme-stack gs, g +358 g <- copy 0x28/open-paren +359 push-grapheme-stack gs, g +360 g <- copy 0x28/open-paren +361 push-grapheme-stack gs, g +362 g <- copy 0x62/b +363 push-grapheme-stack gs, g +364 g <- copy 0x29/close-paren +365 push-grapheme-stack gs, g +366 # setup: screen +367 var screen-on-stack: screen +368 var screen/esi: (addr screen) <- address screen-on-stack +369 initialize-screen screen, 5, 4, 0/no-pixel-graphics +370 # +371 var x/eax: int <- render-stack-from-bottom screen, gs, 0/x, 2/y, 1/highlight-matching-open-paren, 0/open-paren-depth +372 check-screen-row screen, 2/y, "a((b) ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren-2" +373 check-screen-row-in-color screen, 0xf/fg=white, 2/y, " ( ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren-2: matching paren" +374 } +375 +376 # return the index of the matching close-paren of the grapheme at cursor (top of stack) +377 # or top index if there's no matching close-paren +378 fn get-matching-close-paren-index _self: (addr grapheme-stack), render-cursor?: boolean -> _/edx: int { +379 var self/esi: (addr grapheme-stack) <- copy _self +380 var top-addr/edx: (addr int) <- get self, top +381 # if not rendering cursor, return +382 compare render-cursor?, 0/false +383 { +384 break-if-!= +385 return *top-addr +386 } +387 var data-ah/eax: (addr handle array grapheme) <- get self, data +388 var data/eax: (addr array grapheme) <- lookup *data-ah +389 var i/ecx: int <- copy *top-addr +390 # if stack is empty, return +391 compare i, 0 +392 { +393 break-if-> +394 return *top-addr +395 } +396 # if cursor is not '(' return +397 i <- decrement +398 var g/esi: (addr grapheme) <- index data, i +399 compare *g, 0x28/open-paren +400 { +401 break-if-= +402 return *top-addr +403 } +404 # otherwise scan to matching paren +405 var paren-count/ebx: int <- copy 1 +406 i <- decrement +407 { +408 compare i, 0 +409 break-if-< +410 var g/esi: (addr grapheme) <- index data, i +411 compare *g, 0x28/open-paren +412 { +413 break-if-!= +414 paren-count <- increment +415 } +416 compare *g, 0x29/close-paren +417 { +418 break-if-!= +419 compare paren-count, 1 +420 { +421 break-if-!= +422 return i +423 } +424 paren-count <- decrement +425 } +426 i <- decrement +427 loop +428 } +429 return *top-addr +430 } +431 +432 # return the index of the first open-paren at the given depth +433 # or top index if there's no matching close-paren +434 fn get-matching-open-paren-index _self: (addr grapheme-stack), control: boolean, depth: int -> _/edx: int { +435 var self/esi: (addr grapheme-stack) <- copy _self +436 var top-addr/edx: (addr int) <- get self, top +437 # if not rendering cursor, return +438 compare control, 0/false +439 { +440 break-if-!= +441 return *top-addr +442 } +443 var data-ah/eax: (addr handle array grapheme) <- get self, data +444 var data/eax: (addr array grapheme) <- lookup *data-ah +445 var i/ecx: int <- copy *top-addr +446 # if stack is empty, return +447 compare i, 0 +448 { +449 break-if-> +450 return *top-addr +451 } +452 # scan to matching open paren +453 var paren-count/ebx: int <- copy 0 +454 i <- decrement +455 { +456 compare i, 0 +457 break-if-< +458 var g/esi: (addr grapheme) <- index data, i +459 compare *g, 0x29/close-paren +460 { +461 break-if-!= +462 paren-count <- increment +463 } +464 compare *g, 0x28/open-paren +465 { +466 break-if-!= +467 compare paren-count, depth +468 { +469 break-if-!= +470 return i +471 } +472 paren-count <- decrement +473 } +474 i <- decrement +475 loop +476 } +477 return *top-addr +478 } +479 +480 # compare from bottom +481 # beware: modifies 'stream', which must be disposed of after a false result +482 fn prefix-match? _self: (addr grapheme-stack), s: (addr stream byte) -> _/eax: boolean { +483 var self/esi: (addr grapheme-stack) <- copy _self +484 var data-ah/edi: (addr handle array grapheme) <- get self, data +485 var _data/eax: (addr array grapheme) <- lookup *data-ah +486 var data/edi: (addr array grapheme) <- copy _data +487 var top-addr/ecx: (addr int) <- get self, top +488 var i/ebx: int <- copy 0 +489 { +490 compare i, *top-addr +491 break-if->= +492 # if curr != expected, return false +493 { +494 var curr-a/edx: (addr grapheme) <- index data, i +495 var expected/eax: grapheme <- read-grapheme s +496 { +497 compare expected, *curr-a +498 break-if-= +499 return 0/false +500 } +501 } +502 i <- increment +503 loop +504 } +505 return 1 # true +506 } +507 +508 # compare from bottom +509 # beware: modifies 'stream', which must be disposed of after a false result +510 fn suffix-match? _self: (addr grapheme-stack), s: (addr stream byte) -> _/eax: boolean { +511 var self/esi: (addr grapheme-stack) <- copy _self +512 var data-ah/edi: (addr handle array grapheme) <- get self, data +513 var _data/eax: (addr array grapheme) <- lookup *data-ah +514 var data/edi: (addr array grapheme) <- copy _data +515 var top-addr/eax: (addr int) <- get self, top +516 var i/ebx: int <- copy *top-addr +517 i <- decrement +518 { +519 compare i, 0 +520 break-if-< +521 { +522 var curr-a/edx: (addr grapheme) <- index data, i +523 var expected/eax: grapheme <- read-grapheme s +524 # if curr != expected, return false +525 { +526 compare expected, *curr-a +527 break-if-= +528 return 0/false +529 } +530 } +531 i <- decrement +532 loop +533 } +534 return 1 # true +535 } +536 +537 fn grapheme-stack-is-decimal-integer? _self: (addr grapheme-stack) -> _/eax: boolean { +538 var self/esi: (addr grapheme-stack) <- copy _self +539 var data-ah/eax: (addr handle array grapheme) <- get self, data +540 var _data/eax: (addr array grapheme) <- lookup *data-ah +541 var data/edx: (addr array grapheme) <- copy _data +542 var top-addr/ecx: (addr int) <- get self, top +543 var i/ebx: int <- copy 0 +544 var result/eax: boolean <- copy 1/true +545 $grapheme-stack-is-integer?:loop: { +546 compare i, *top-addr +547 break-if->= +548 var g/edx: (addr grapheme) <- index data, i +549 result <- decimal-digit? *g +550 compare result, 0/false +551 break-if-= +552 i <- increment +553 loop +554 } +555 return result +556 } ++ + + diff --git a/html/514gap-buffer.mu.html b/html/514gap-buffer.mu.html new file mode 100644 index 00000000..f0cac176 --- /dev/null +++ b/html/514gap-buffer.mu.html @@ -0,0 +1,1538 @@ + + + + +
+ 1 # primitive for editing text + 2 + 3 type gap-buffer { + 4 left: grapheme-stack + 5 right: grapheme-stack + 6 # some fields for scanning incrementally through a gap-buffer + 7 left-read-index: int + 8 right-read-index: int + 9 } + 10 + 11 fn initialize-gap-buffer _self: (addr gap-buffer), capacity: int { + 12 var self/esi: (addr gap-buffer) <- copy _self + 13 var left/eax: (addr grapheme-stack) <- get self, left + 14 initialize-grapheme-stack left, capacity + 15 var right/eax: (addr grapheme-stack) <- get self, right + 16 initialize-grapheme-stack right, capacity + 17 } + 18 + 19 fn clear-gap-buffer _self: (addr gap-buffer) { + 20 var self/esi: (addr gap-buffer) <- copy _self + 21 var left/eax: (addr grapheme-stack) <- get self, left + 22 clear-grapheme-stack left + 23 var right/eax: (addr grapheme-stack) <- get self, right + 24 clear-grapheme-stack right + 25 } + 26 + 27 fn gap-buffer-empty? _self: (addr gap-buffer) -> _/eax: boolean { + 28 var self/esi: (addr gap-buffer) <- copy _self + 29 # if !empty?(left) return false + 30 { + 31 var left/eax: (addr grapheme-stack) <- get self, left + 32 var result/eax: boolean <- grapheme-stack-empty? left + 33 compare result, 0/false + 34 break-if-!= + 35 return 0/false + 36 } + 37 # return empty?(right) + 38 var left/eax: (addr grapheme-stack) <- get self, left + 39 var result/eax: boolean <- grapheme-stack-empty? left + 40 return result + 41 } + 42 + 43 fn gap-buffer-capacity _gap: (addr gap-buffer) -> _/edx: int { + 44 var gap/esi: (addr gap-buffer) <- copy _gap + 45 var left/eax: (addr grapheme-stack) <- get gap, left + 46 var left-data-ah/eax: (addr handle array grapheme) <- get left, data + 47 var left-data/eax: (addr array grapheme) <- lookup *left-data-ah + 48 var result/eax: int <- length left-data + 49 return result + 50 } + 51 + 52 # just for tests + 53 fn initialize-gap-buffer-with self: (addr gap-buffer), keys: (addr array byte) { + 54 initialize-gap-buffer self, 0x40/capacity + 55 var input-stream-storage: (stream byte 0x40/capacity) + 56 var input-stream/ecx: (addr stream byte) <- address input-stream-storage + 57 write input-stream, keys + 58 { + 59 var done?/eax: boolean <- stream-empty? input-stream + 60 compare done?, 0/false + 61 break-if-!= + 62 var g/eax: grapheme <- read-grapheme input-stream + 63 add-grapheme-at-gap self, g + 64 loop + 65 } + 66 } + 67 + 68 fn load-gap-buffer-from-stream self: (addr gap-buffer), in: (addr stream byte) { + 69 rewind-stream in + 70 { + 71 var done?/eax: boolean <- stream-empty? in + 72 compare done?, 0/false + 73 break-if-!= + 74 var key/eax: byte <- read-byte in + 75 compare key, 0/null + 76 break-if-= + 77 var g/eax: grapheme <- copy key + 78 edit-gap-buffer self, g + 79 loop + 80 } + 81 } + 82 + 83 fn emit-gap-buffer self: (addr gap-buffer), out: (addr stream byte) { + 84 clear-stream out + 85 append-gap-buffer self, out + 86 } + 87 + 88 fn append-gap-buffer _self: (addr gap-buffer), out: (addr stream byte) { + 89 var self/esi: (addr gap-buffer) <- copy _self + 90 var left/eax: (addr grapheme-stack) <- get self, left + 91 emit-stack-from-bottom left, out + 92 var right/eax: (addr grapheme-stack) <- get self, right + 93 emit-stack-from-top right, out + 94 } + 95 + 96 # dump stack from bottom to top + 97 fn emit-stack-from-bottom _self: (addr grapheme-stack), out: (addr stream byte) { + 98 var self/esi: (addr grapheme-stack) <- copy _self + 99 var data-ah/edi: (addr handle array grapheme) <- get self, data + 100 var _data/eax: (addr array grapheme) <- lookup *data-ah + 101 var data/edi: (addr array grapheme) <- copy _data + 102 var top-addr/ecx: (addr int) <- get self, top + 103 var i/eax: int <- copy 0 + 104 { + 105 compare i, *top-addr + 106 break-if->= + 107 var g/edx: (addr grapheme) <- index data, i + 108 write-grapheme out, *g + 109 i <- increment + 110 loop + 111 } + 112 } + 113 + 114 # dump stack from top to bottom + 115 fn emit-stack-from-top _self: (addr grapheme-stack), out: (addr stream byte) { + 116 var self/esi: (addr grapheme-stack) <- copy _self + 117 var data-ah/edi: (addr handle array grapheme) <- get self, data + 118 var _data/eax: (addr array grapheme) <- lookup *data-ah + 119 var data/edi: (addr array grapheme) <- copy _data + 120 var top-addr/ecx: (addr int) <- get self, top + 121 var i/eax: int <- copy *top-addr + 122 i <- decrement + 123 { + 124 compare i, 0 + 125 break-if-< + 126 var g/edx: (addr grapheme) <- index data, i + 127 write-grapheme out, *g + 128 i <- decrement + 129 loop + 130 } + 131 } + 132 + 133 fn word-at-gap _self: (addr gap-buffer), out: (addr stream byte) { + 134 var self/esi: (addr gap-buffer) <- copy _self + 135 clear-stream out + 136 { + 137 var g/eax: grapheme <- grapheme-at-gap self + 138 var at-word?/eax: boolean <- is-ascii-word-grapheme? g + 139 compare at-word?, 0/false + 140 break-if-!= + 141 return + 142 } + 143 var left/ecx: (addr grapheme-stack) <- get self, left + 144 var left-index/eax: int <- top-most-word left + 145 emit-stack-from-index left, left-index, out + 146 var right/ecx: (addr grapheme-stack) <- get self, right + 147 var right-index/eax: int <- top-most-word right + 148 emit-stack-to-index right, right-index, out + 149 } + 150 + 151 fn test-word-at-gap-single-word-with-gap-at-end { + 152 var _g: gap-buffer + 153 var g/esi: (addr gap-buffer) <- address _g + 154 initialize-gap-buffer-with g, "abc" + 155 # gap is at end (right is empty) + 156 var out-storage: (stream byte 0x10) + 157 var out/eax: (addr stream byte) <- address out-storage + 158 word-at-gap g, out + 159 check-stream-equal out, "abc", "F - test-word-at-gap-single-word-with-gap-at-end" + 160 } + 161 + 162 fn test-word-at-gap-single-word-with-gap-at-start { + 163 var _g: gap-buffer + 164 var g/esi: (addr gap-buffer) <- address _g + 165 initialize-gap-buffer-with g, "abc" + 166 gap-to-start g + 167 # + 168 var out-storage: (stream byte 0x10) + 169 var out/eax: (addr stream byte) <- address out-storage + 170 word-at-gap g, out + 171 check-stream-equal out, "abc", "F - test-word-at-gap-single-word-with-gap-at-start" + 172 } + 173 + 174 fn test-word-at-gap-multiple-words-with-gap-at-non-word-grapheme-at-end { + 175 var _g: gap-buffer + 176 var g/esi: (addr gap-buffer) <- address _g + 177 initialize-gap-buffer-with g, "abc " + 178 # gap is at end (right is empty) + 179 var out-storage: (stream byte 0x10) + 180 var out/eax: (addr stream byte) <- address out-storage + 181 word-at-gap g, out + 182 check-stream-equal out, "", "F - test-word-at-gap-multiple-words-with-gap-at-non-word-grapheme-at-end" + 183 } + 184 + 185 fn test-word-at-gap-multiple-words-with-gap-at-non-word-grapheme-at-start { + 186 var _g: gap-buffer + 187 var g/esi: (addr gap-buffer) <- address _g + 188 initialize-gap-buffer-with g, " abc" + 189 gap-to-start g + 190 # + 191 var out-storage: (stream byte 0x10) + 192 var out/eax: (addr stream byte) <- address out-storage + 193 word-at-gap g, out + 194 check-stream-equal out, "", "F - test-word-at-gap-multiple-words-with-gap-at-non-word-grapheme-at-start" + 195 } + 196 + 197 fn test-word-at-gap-multiple-words-with-gap-at-end { + 198 var _g: gap-buffer + 199 var g/esi: (addr gap-buffer) <- address _g + 200 initialize-gap-buffer-with g, "a bc d" + 201 # gap is at end (right is empty) + 202 var out-storage: (stream byte 0x10) + 203 var out/eax: (addr stream byte) <- address out-storage + 204 word-at-gap g, out + 205 check-stream-equal out, "d", "F - test-word-at-gap-multiple-words-with-gap-at-end" + 206 } + 207 + 208 fn test-word-at-gap-multiple-words-with-gap-at-initial-word { + 209 var _g: gap-buffer + 210 var g/esi: (addr gap-buffer) <- address _g + 211 initialize-gap-buffer-with g, "a bc d" + 212 gap-to-start g + 213 # + 214 var out-storage: (stream byte 0x10) + 215 var out/eax: (addr stream byte) <- address out-storage + 216 word-at-gap g, out + 217 check-stream-equal out, "a", "F - test-word-at-gap-multiple-words-with-gap-at-initial-word" + 218 } + 219 + 220 fn test-word-at-gap-multiple-words-with-gap-at-final-word { + 221 var _g: gap-buffer + 222 var g/esi: (addr gap-buffer) <- address _g + 223 initialize-gap-buffer-with g, "a bc d" + 224 var dummy/eax: grapheme <- gap-left g + 225 # gap is at final word + 226 var out-storage: (stream byte 0x10) + 227 var out/eax: (addr stream byte) <- address out-storage + 228 word-at-gap g, out + 229 check-stream-equal out, "d", "F - test-word-at-gap-multiple-words-with-gap-at-final-word" + 230 } + 231 + 232 fn test-word-at-gap-multiple-words-with-gap-at-final-non-word { + 233 var _g: gap-buffer + 234 var g/esi: (addr gap-buffer) <- address _g + 235 initialize-gap-buffer-with g, "abc " + 236 var dummy/eax: grapheme <- gap-left g + 237 # gap is at final word + 238 var out-storage: (stream byte 0x10) + 239 var out/eax: (addr stream byte) <- address out-storage + 240 word-at-gap g, out + 241 check-stream-equal out, "", "F - test-word-at-gap-multiple-words-with-gap-at-final-non-word" + 242 } + 243 + 244 fn grapheme-at-gap _self: (addr gap-buffer) -> _/eax: grapheme { + 245 # send top of right most of the time + 246 var self/esi: (addr gap-buffer) <- copy _self + 247 var right/edi: (addr grapheme-stack) <- get self, right + 248 var data-ah/eax: (addr handle array grapheme) <- get right, data + 249 var data/eax: (addr array grapheme) <- lookup *data-ah + 250 var top-addr/ecx: (addr int) <- get right, top + 251 { + 252 compare *top-addr, 0 + 253 break-if-<= + 254 var top/ecx: int <- copy *top-addr + 255 top <- decrement + 256 var result/eax: (addr grapheme) <- index data, top + 257 return *result + 258 } + 259 # send top of left only if right is empty + 260 var left/edi: (addr grapheme-stack) <- get self, left + 261 var data-ah/eax: (addr handle array grapheme) <- get left, data + 262 var data/eax: (addr array grapheme) <- lookup *data-ah + 263 var top-addr/ecx: (addr int) <- get left, top + 264 { + 265 compare *top-addr, 0 + 266 break-if-<= + 267 var top/ecx: int <- copy *top-addr + 268 top <- decrement + 269 var result/eax: (addr grapheme) <- index data, top + 270 return *result + 271 } + 272 # send null if everything is empty + 273 return 0 + 274 } + 275 + 276 fn top-most-word _self: (addr grapheme-stack) -> _/eax: int { + 277 var self/esi: (addr grapheme-stack) <- copy _self + 278 var data-ah/edi: (addr handle array grapheme) <- get self, data + 279 var _data/eax: (addr array grapheme) <- lookup *data-ah + 280 var data/edi: (addr array grapheme) <- copy _data + 281 var top-addr/ecx: (addr int) <- get self, top + 282 var i/ebx: int <- copy *top-addr + 283 i <- decrement + 284 { + 285 compare i, 0 + 286 break-if-< + 287 var g/edx: (addr grapheme) <- index data, i + 288 var is-word?/eax: boolean <- is-ascii-word-grapheme? *g + 289 compare is-word?, 0/false + 290 break-if-= + 291 i <- decrement + 292 loop + 293 } + 294 i <- increment + 295 return i + 296 } + 297 + 298 fn emit-stack-from-index _self: (addr grapheme-stack), start: int, out: (addr stream byte) { + 299 var self/esi: (addr grapheme-stack) <- copy _self + 300 var data-ah/edi: (addr handle array grapheme) <- get self, data + 301 var _data/eax: (addr array grapheme) <- lookup *data-ah + 302 var data/edi: (addr array grapheme) <- copy _data + 303 var top-addr/ecx: (addr int) <- get self, top + 304 var i/eax: int <- copy start + 305 { + 306 compare i, *top-addr + 307 break-if->= + 308 var g/edx: (addr grapheme) <- index data, i + 309 write-grapheme out, *g + 310 i <- increment + 311 loop + 312 } + 313 } + 314 + 315 fn emit-stack-to-index _self: (addr grapheme-stack), end: int, out: (addr stream byte) { + 316 var self/esi: (addr grapheme-stack) <- copy _self + 317 var data-ah/edi: (addr handle array grapheme) <- get self, data + 318 var _data/eax: (addr array grapheme) <- lookup *data-ah + 319 var data/edi: (addr array grapheme) <- copy _data + 320 var top-addr/ecx: (addr int) <- get self, top + 321 var i/eax: int <- copy *top-addr + 322 i <- decrement + 323 { + 324 compare i, 0 + 325 break-if-< + 326 compare i, end + 327 break-if-< + 328 var g/edx: (addr grapheme) <- index data, i + 329 write-grapheme out, *g + 330 i <- decrement + 331 loop + 332 } + 333 } + 334 + 335 fn is-ascii-word-grapheme? g: grapheme -> _/eax: boolean { + 336 compare g, 0x21/! + 337 { + 338 break-if-!= + 339 return 1/true + 340 } + 341 compare g, 0x30/0 + 342 { + 343 break-if->= + 344 return 0/false + 345 } + 346 compare g, 0x39/9 + 347 { + 348 break-if-> + 349 return 1/true + 350 } + 351 compare g, 0x3f/? + 352 { + 353 break-if-!= + 354 return 1/true + 355 } + 356 compare g, 0x41/A + 357 { + 358 break-if->= + 359 return 0/false + 360 } + 361 compare g, 0x5a/Z + 362 { + 363 break-if-> + 364 return 1/true + 365 } + 366 compare g, 0x5f/_ + 367 { + 368 break-if-!= + 369 return 1/true + 370 } + 371 compare g, 0x61/a + 372 { + 373 break-if->= + 374 return 0/false + 375 } + 376 compare g, 0x7a/z + 377 { + 378 break-if-> + 379 return 1/true + 380 } + 381 return 0/false + 382 } + 383 + 384 # We implicitly render everything editable in a single color, and assume the + 385 # cursor is a single other color. + 386 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 { + 387 var gap/esi: (addr gap-buffer) <- copy _gap + 388 var left/edx: (addr grapheme-stack) <- get gap, left + 389 var highlight-matching-open-paren?/ebx: boolean <- copy 0/false + 390 var matching-open-paren-depth/edi: int <- copy 0 + 391 highlight-matching-open-paren?, matching-open-paren-depth <- highlight-matching-open-paren? gap, render-cursor? + 392 var x2/eax: int <- copy 0 + 393 var y2/ecx: int <- copy 0 + 394 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 + 395 var right/edx: (addr grapheme-stack) <- get gap, right + 396 x2, y2 <- render-stack-from-top-wrapping-right-then-down screen, right, xmin, ymin, xmax, ymax, x2, y2, render-cursor?, color, background-color + 397 # decide whether we still need to print a cursor + 398 var fg/edi: int <- copy color + 399 var bg/ebx: int <- copy background-color + 400 compare render-cursor?, 0/false + 401 { + 402 break-if-= + 403 # if the right side is empty, grapheme stack didn't print the cursor + 404 var empty?/eax: boolean <- grapheme-stack-empty? right + 405 compare empty?, 0/false + 406 break-if-= + 407 # swap foreground and background + 408 fg <- copy background-color + 409 bg <- copy color + 410 } + 411 # print a grapheme either way so that cursor position doesn't affect printed width + 412 var space/edx: grapheme <- copy 0x20 + 413 x2, y2 <- render-grapheme screen, space, xmin, ymin, xmax, ymax, x2, y2, fg, bg + 414 return x2, y2 + 415 } + 416 + 417 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 { + 418 var _width/eax: int <- copy 0 + 419 var _height/ecx: int <- copy 0 + 420 _width, _height <- screen-size screen + 421 var width/edx: int <- copy _width + 422 var height/ebx: int <- copy _height + 423 var x2/eax: int <- copy 0 + 424 var y2/ecx: int <- copy 0 + 425 x2, y2 <- render-gap-buffer-wrapping-right-then-down screen, gap, x, y, width, height, render-cursor?, color, background-color + 426 return x2 # y2? yolo + 427 } + 428 + 429 fn gap-buffer-length _gap: (addr gap-buffer) -> _/eax: int { + 430 var gap/esi: (addr gap-buffer) <- copy _gap + 431 var left/eax: (addr grapheme-stack) <- get gap, left + 432 var tmp/eax: (addr int) <- get left, top + 433 var left-length/ecx: int <- copy *tmp + 434 var right/esi: (addr grapheme-stack) <- get gap, right + 435 tmp <- get right, top + 436 var result/eax: int <- copy *tmp + 437 result <- add left-length + 438 return result + 439 } + 440 + 441 fn add-grapheme-at-gap _self: (addr gap-buffer), g: grapheme { + 442 var self/esi: (addr gap-buffer) <- copy _self + 443 var left/eax: (addr grapheme-stack) <- get self, left + 444 push-grapheme-stack left, g + 445 } + 446 + 447 fn add-code-point-at-gap self: (addr gap-buffer), c: code-point { + 448 var g/eax: grapheme <- copy c + 449 add-grapheme-at-gap self, g + 450 } + 451 + 452 fn gap-to-start self: (addr gap-buffer) { + 453 { + 454 var curr/eax: grapheme <- gap-left self + 455 compare curr, -1 + 456 loop-if-!= + 457 } + 458 } + 459 + 460 fn gap-to-end self: (addr gap-buffer) { + 461 { + 462 var curr/eax: grapheme <- gap-right self + 463 compare curr, -1 + 464 loop-if-!= + 465 } + 466 } + 467 + 468 fn gap-at-start? _self: (addr gap-buffer) -> _/eax: boolean { + 469 var self/esi: (addr gap-buffer) <- copy _self + 470 var left/eax: (addr grapheme-stack) <- get self, left + 471 var result/eax: boolean <- grapheme-stack-empty? left + 472 return result + 473 } + 474 + 475 fn gap-at-end? _self: (addr gap-buffer) -> _/eax: boolean { + 476 var self/esi: (addr gap-buffer) <- copy _self + 477 var right/eax: (addr grapheme-stack) <- get self, right + 478 var result/eax: boolean <- grapheme-stack-empty? right + 479 return result + 480 } + 481 + 482 fn gap-right _self: (addr gap-buffer) -> _/eax: grapheme { + 483 var self/esi: (addr gap-buffer) <- copy _self + 484 var g/eax: grapheme <- copy 0 + 485 var right/ecx: (addr grapheme-stack) <- get self, right + 486 g <- pop-grapheme-stack right + 487 compare g, -1 + 488 { + 489 break-if-= + 490 var left/ecx: (addr grapheme-stack) <- get self, left + 491 push-grapheme-stack left, g + 492 } + 493 return g + 494 } + 495 + 496 fn gap-left _self: (addr gap-buffer) -> _/eax: grapheme { + 497 var self/esi: (addr gap-buffer) <- copy _self + 498 var g/eax: grapheme <- copy 0 + 499 { + 500 var left/ecx: (addr grapheme-stack) <- get self, left + 501 g <- pop-grapheme-stack left + 502 } + 503 compare g, -1 + 504 { + 505 break-if-= + 506 var right/ecx: (addr grapheme-stack) <- get self, right + 507 push-grapheme-stack right, g + 508 } + 509 return g + 510 } + 511 + 512 fn index-of-gap _self: (addr gap-buffer) -> _/eax: int { + 513 var self/eax: (addr gap-buffer) <- copy _self + 514 var left/eax: (addr grapheme-stack) <- get self, left + 515 var top-addr/eax: (addr int) <- get left, top + 516 var result/eax: int <- copy *top-addr + 517 return result + 518 } + 519 + 520 fn first-grapheme-in-gap-buffer _self: (addr gap-buffer) -> _/eax: grapheme { + 521 var self/esi: (addr gap-buffer) <- copy _self + 522 # try to read from left + 523 var left/eax: (addr grapheme-stack) <- get self, left + 524 var top-addr/ecx: (addr int) <- get left, top + 525 compare *top-addr, 0 + 526 { + 527 break-if-<= + 528 var data-ah/eax: (addr handle array grapheme) <- get left, data + 529 var data/eax: (addr array grapheme) <- lookup *data-ah + 530 var result-addr/eax: (addr grapheme) <- index data, 0 + 531 return *result-addr + 532 } + 533 # try to read from right + 534 var right/eax: (addr grapheme-stack) <- get self, right + 535 top-addr <- get right, top + 536 compare *top-addr, 0 + 537 { + 538 break-if-<= + 539 var data-ah/eax: (addr handle array grapheme) <- get right, data + 540 var data/eax: (addr array grapheme) <- lookup *data-ah + 541 var top/ecx: int <- copy *top-addr + 542 top <- decrement + 543 var result-addr/eax: (addr grapheme) <- index data, top + 544 return *result-addr + 545 } + 546 # give up + 547 return -1 + 548 } + 549 + 550 fn grapheme-before-cursor-in-gap-buffer _self: (addr gap-buffer) -> _/eax: grapheme { + 551 var self/esi: (addr gap-buffer) <- copy _self + 552 # try to read from left + 553 var left/ecx: (addr grapheme-stack) <- get self, left + 554 var top-addr/edx: (addr int) <- get left, top + 555 compare *top-addr, 0 + 556 { + 557 break-if-<= + 558 var result/eax: grapheme <- pop-grapheme-stack left + 559 push-grapheme-stack left, result + 560 return result + 561 } + 562 # give up + 563 return -1 + 564 } + 565 + 566 fn delete-before-gap _self: (addr gap-buffer) { + 567 var self/eax: (addr gap-buffer) <- copy _self + 568 var left/eax: (addr grapheme-stack) <- get self, left + 569 var dummy/eax: grapheme <- pop-grapheme-stack left + 570 } + 571 + 572 fn pop-after-gap _self: (addr gap-buffer) -> _/eax: grapheme { + 573 var self/eax: (addr gap-buffer) <- copy _self + 574 var right/eax: (addr grapheme-stack) <- get self, right + 575 var result/eax: grapheme <- pop-grapheme-stack right + 576 return result + 577 } + 578 + 579 fn gap-buffer-equal? _self: (addr gap-buffer), s: (addr array byte) -> _/eax: boolean { + 580 var self/esi: (addr gap-buffer) <- copy _self + 581 # complication: graphemes may be multiple bytes + 582 # so don't rely on length + 583 # instead turn the expected result into a stream and arrange to read from it in order + 584 var stream-storage: (stream byte 0x10/capacity) + 585 var expected-stream/ecx: (addr stream byte) <- address stream-storage + 586 write expected-stream, s + 587 # compare left + 588 var left/edx: (addr grapheme-stack) <- get self, left + 589 var result/eax: boolean <- prefix-match? left, expected-stream + 590 compare result, 0/false + 591 { + 592 break-if-!= + 593 return result + 594 } + 595 # compare right + 596 var right/edx: (addr grapheme-stack) <- get self, right + 597 result <- suffix-match? right, expected-stream + 598 compare result, 0/false + 599 { + 600 break-if-!= + 601 return result + 602 } + 603 # ensure there's nothing left over + 604 result <- stream-empty? expected-stream + 605 return result + 606 } + 607 + 608 fn test-gap-buffer-equal-from-end { + 609 var _g: gap-buffer + 610 var g/esi: (addr gap-buffer) <- address _g + 611 initialize-gap-buffer g, 0x10 + 612 # + 613 add-code-point-at-gap g, 0x61/a + 614 add-code-point-at-gap g, 0x61/a + 615 add-code-point-at-gap g, 0x61/a + 616 # gap is at end (right is empty) + 617 var result/eax: boolean <- gap-buffer-equal? g, "aaa" + 618 check result, "F - test-gap-buffer-equal-from-end" + 619 } + 620 + 621 fn test-gap-buffer-equal-from-middle { + 622 var _g: gap-buffer + 623 var g/esi: (addr gap-buffer) <- address _g + 624 initialize-gap-buffer g, 0x10 + 625 # + 626 add-code-point-at-gap g, 0x61/a + 627 add-code-point-at-gap g, 0x61/a + 628 add-code-point-at-gap g, 0x61/a + 629 var dummy/eax: grapheme <- gap-left g + 630 # gap is in the middle + 631 var result/eax: boolean <- gap-buffer-equal? g, "aaa" + 632 check result, "F - test-gap-buffer-equal-from-middle" + 633 } + 634 + 635 fn test-gap-buffer-equal-from-start { + 636 var _g: gap-buffer + 637 var g/esi: (addr gap-buffer) <- address _g + 638 initialize-gap-buffer g, 0x10 + 639 # + 640 add-code-point-at-gap g, 0x61/a + 641 add-code-point-at-gap g, 0x61/a + 642 add-code-point-at-gap g, 0x61/a + 643 var dummy/eax: grapheme <- gap-left g + 644 dummy <- gap-left g + 645 dummy <- gap-left g + 646 # gap is at the start + 647 var result/eax: boolean <- gap-buffer-equal? g, "aaa" + 648 check result, "F - test-gap-buffer-equal-from-start" + 649 } + 650 + 651 fn test-gap-buffer-equal-fails { + 652 # g = "aaa" + 653 var _g: gap-buffer + 654 var g/esi: (addr gap-buffer) <- address _g + 655 initialize-gap-buffer g, 0x10 + 656 add-code-point-at-gap g, 0x61/a + 657 add-code-point-at-gap g, 0x61/a + 658 add-code-point-at-gap g, 0x61/a + 659 # + 660 var result/eax: boolean <- gap-buffer-equal? g, "aa" + 661 check-not result, "F - test-gap-buffer-equal-fails" + 662 } + 663 + 664 fn gap-buffers-equal? self: (addr gap-buffer), g: (addr gap-buffer) -> _/eax: boolean { + 665 var tmp/eax: int <- gap-buffer-length self + 666 var len/ecx: int <- copy tmp + 667 var leng/eax: int <- gap-buffer-length g + 668 compare len, leng + 669 { + 670 break-if-= + 671 return 0/false + 672 } + 673 var i/edx: int <- copy 0 + 674 { + 675 compare i, len + 676 break-if->= + 677 { + 678 var tmp/eax: grapheme <- gap-index self, i + 679 var curr/ecx: grapheme <- copy tmp + 680 var currg/eax: grapheme <- gap-index g, i + 681 compare curr, currg + 682 break-if-= + 683 return 0/false + 684 } + 685 i <- increment + 686 loop + 687 } + 688 return 1/true + 689 } + 690 + 691 fn gap-index _self: (addr gap-buffer), _n: int -> _/eax: grapheme { + 692 var self/esi: (addr gap-buffer) <- copy _self + 693 var n/ebx: int <- copy _n + 694 # if n < left->length, index into left + 695 var left/edi: (addr grapheme-stack) <- get self, left + 696 var left-len-a/edx: (addr int) <- get left, top + 697 compare n, *left-len-a + 698 { + 699 break-if->= + 700 var data-ah/eax: (addr handle array grapheme) <- get left, data + 701 var data/eax: (addr array grapheme) <- lookup *data-ah + 702 var result/eax: (addr grapheme) <- index data, n + 703 return *result + 704 } + 705 # shrink n + 706 n <- subtract *left-len-a + 707 # if n < right->length, index into right + 708 var right/edi: (addr grapheme-stack) <- get self, right + 709 var right-len-a/edx: (addr int) <- get right, top + 710 compare n, *right-len-a + 711 { + 712 break-if->= + 713 var data-ah/eax: (addr handle array grapheme) <- get right, data + 714 var data/eax: (addr array grapheme) <- lookup *data-ah + 715 # idx = right->len - n - 1 + 716 var idx/ebx: int <- copy n + 717 idx <- subtract *right-len-a + 718 idx <- negate + 719 idx <- subtract 1 + 720 var result/eax: (addr grapheme) <- index data, idx + 721 return *result + 722 } + 723 # error + 724 abort "gap-index: out of bounds" + 725 return 0 + 726 } + 727 + 728 fn test-gap-buffers-equal? { + 729 var _a: gap-buffer + 730 var a/esi: (addr gap-buffer) <- address _a + 731 initialize-gap-buffer-with a, "abc" + 732 var _b: gap-buffer + 733 var b/edi: (addr gap-buffer) <- address _b + 734 initialize-gap-buffer-with b, "abc" + 735 var _c: gap-buffer + 736 var c/ebx: (addr gap-buffer) <- address _c + 737 initialize-gap-buffer-with c, "ab" + 738 var _d: gap-buffer + 739 var d/edx: (addr gap-buffer) <- address _d + 740 initialize-gap-buffer-with d, "abd" + 741 # + 742 var result/eax: boolean <- gap-buffers-equal? a, a + 743 check result, "F - test-gap-buffers-equal? - reflexive" + 744 result <- gap-buffers-equal? a, b + 745 check result, "F - test-gap-buffers-equal? - equal" + 746 # length not equal + 747 result <- gap-buffers-equal? a, c + 748 check-not result, "F - test-gap-buffers-equal? - not equal" + 749 # contents not equal + 750 result <- gap-buffers-equal? a, d + 751 check-not result, "F - test-gap-buffers-equal? - not equal 2" + 752 result <- gap-buffers-equal? d, a + 753 check-not result, "F - test-gap-buffers-equal? - not equal 3" + 754 } + 755 + 756 fn test-gap-buffer-index { + 757 var gap-storage: gap-buffer + 758 var gap/esi: (addr gap-buffer) <- address gap-storage + 759 initialize-gap-buffer-with gap, "abc" + 760 # gap is at end, all contents are in left + 761 var g/eax: grapheme <- gap-index gap, 0 + 762 var x/ecx: int <- copy g + 763 check-ints-equal x, 0x61/a, "F - test-gap-index/left-1" + 764 var g/eax: grapheme <- gap-index gap, 1 + 765 var x/ecx: int <- copy g + 766 check-ints-equal x, 0x62/b, "F - test-gap-index/left-2" + 767 var g/eax: grapheme <- gap-index gap, 2 + 768 var x/ecx: int <- copy g + 769 check-ints-equal x, 0x63/c, "F - test-gap-index/left-3" + 770 # now check when everything is to the right + 771 gap-to-start gap + 772 rewind-gap-buffer gap + 773 var g/eax: grapheme <- gap-index gap, 0 + 774 var x/ecx: int <- copy g + 775 check-ints-equal x, 0x61/a, "F - test-gap-index/right-1" + 776 var g/eax: grapheme <- gap-index gap, 1 + 777 var x/ecx: int <- copy g + 778 check-ints-equal x, 0x62/b, "F - test-gap-index/right-2" + 779 var g/eax: grapheme <- gap-index gap, 2 + 780 var x/ecx: int <- copy g + 781 check-ints-equal x, 0x63/c, "F - test-gap-index/right-3" + 782 } + 783 + 784 fn copy-gap-buffer _src-ah: (addr handle gap-buffer), _dest-ah: (addr handle gap-buffer) { + 785 # obtain src-a, dest-a + 786 var src-ah/eax: (addr handle gap-buffer) <- copy _src-ah + 787 var _src-a/eax: (addr gap-buffer) <- lookup *src-ah + 788 var src-a/esi: (addr gap-buffer) <- copy _src-a + 789 var dest-ah/eax: (addr handle gap-buffer) <- copy _dest-ah + 790 var _dest-a/eax: (addr gap-buffer) <- lookup *dest-ah + 791 var dest-a/edi: (addr gap-buffer) <- copy _dest-a + 792 # copy left grapheme-stack + 793 var src/ecx: (addr grapheme-stack) <- get src-a, left + 794 var dest/edx: (addr grapheme-stack) <- get dest-a, left + 795 copy-grapheme-stack src, dest + 796 # copy right grapheme-stack + 797 src <- get src-a, right + 798 dest <- get dest-a, right + 799 copy-grapheme-stack src, dest + 800 } + 801 + 802 fn gap-buffer-is-decimal-integer? _self: (addr gap-buffer) -> _/eax: boolean { + 803 var self/esi: (addr gap-buffer) <- copy _self + 804 var curr/ecx: (addr grapheme-stack) <- get self, left + 805 var result/eax: boolean <- grapheme-stack-is-decimal-integer? curr + 806 { + 807 compare result, 0/false + 808 break-if-= + 809 curr <- get self, right + 810 result <- grapheme-stack-is-decimal-integer? curr + 811 } + 812 return result + 813 } + 814 + 815 fn test-render-gap-buffer-without-cursor { + 816 # setup + 817 var gap-storage: gap-buffer + 818 var gap/esi: (addr gap-buffer) <- address gap-storage + 819 initialize-gap-buffer-with gap, "abc" + 820 # setup: screen + 821 var screen-on-stack: screen + 822 var screen/edi: (addr screen) <- address screen-on-stack + 823 initialize-screen screen, 5, 4, 0/no-pixel-graphics + 824 # + 825 var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 0/no-cursor, 3/fg, 0xc5/bg=blue-bg + 826 check-screen-row screen, 0/y, "abc ", "F - test-render-gap-buffer-without-cursor" + 827 check-ints-equal x, 4, "F - test-render-gap-buffer-without-cursor: result" + 828 # abc + 829 check-background-color-in-screen-row screen, 3/bg=reverse, 0/y, " ", "F - test-render-gap-buffer-without-cursor: bg" + 830 } + 831 + 832 fn test-render-gap-buffer-with-cursor-at-end { + 833 # setup + 834 var gap-storage: gap-buffer + 835 var gap/esi: (addr gap-buffer) <- address gap-storage + 836 initialize-gap-buffer-with gap, "abc" + 837 gap-to-end gap + 838 # setup: screen + 839 var screen-on-stack: screen + 840 var screen/edi: (addr screen) <- address screen-on-stack + 841 initialize-screen screen, 5, 4, 0/no-pixel-graphics + 842 # + 843 var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor, 3/fg, 0xc5/bg=blue-bg + 844 check-screen-row screen, 0/y, "abc ", "F - test-render-gap-buffer-with-cursor-at-end" + 845 # we've drawn one extra grapheme for the cursor + 846 check-ints-equal x, 4, "F - test-render-gap-buffer-with-cursor-at-end: result" + 847 # abc + 848 check-background-color-in-screen-row screen, 3/bg=reverse, 0/y, " |", "F - test-render-gap-buffer-with-cursor-at-end: bg" + 849 } + 850 + 851 fn test-render-gap-buffer-with-cursor-in-middle { + 852 # setup + 853 var gap-storage: gap-buffer + 854 var gap/esi: (addr gap-buffer) <- address gap-storage + 855 initialize-gap-buffer-with gap, "abc" + 856 gap-to-end gap + 857 var dummy/eax: grapheme <- gap-left gap + 858 # setup: screen + 859 var screen-on-stack: screen + 860 var screen/edi: (addr screen) <- address screen-on-stack + 861 initialize-screen screen, 5, 4, 0/no-pixel-graphics + 862 # + 863 var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor, 3/fg, 0xc5/bg=blue-bg + 864 check-screen-row screen, 0/y, "abc ", "F - test-render-gap-buffer-with-cursor-in-middle" + 865 check-ints-equal x, 4, "F - test-render-gap-buffer-with-cursor-in-middle: result" + 866 # abc + 867 check-background-color-in-screen-row screen, 3/bg=reverse, 0/y, " | ", "F - test-render-gap-buffer-with-cursor-in-middle: bg" + 868 } + 869 + 870 fn test-render-gap-buffer-with-cursor-at-start { + 871 var gap-storage: gap-buffer + 872 var gap/esi: (addr gap-buffer) <- address gap-storage + 873 initialize-gap-buffer-with gap, "abc" + 874 gap-to-start gap + 875 # setup: screen + 876 var screen-on-stack: screen + 877 var screen/edi: (addr screen) <- address screen-on-stack + 878 initialize-screen screen, 5, 4, 0/no-pixel-graphics + 879 # + 880 var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor, 3/fg, 0xc5/bg=blue-bg + 881 check-screen-row screen, 0/y, "abc ", "F - test-render-gap-buffer-with-cursor-at-start" + 882 check-ints-equal x, 4, "F - test-render-gap-buffer-with-cursor-at-start: result" + 883 # abc + 884 check-background-color-in-screen-row screen, 3/bg=reverse, 0/y, "| ", "F - test-render-gap-buffer-with-cursor-at-start: bg" + 885 } + 886 + 887 fn test-render-gap-buffer-highlight-matching-close-paren { + 888 var gap-storage: gap-buffer + 889 var gap/esi: (addr gap-buffer) <- address gap-storage + 890 initialize-gap-buffer-with gap, "(a)" + 891 gap-to-start gap + 892 # setup: screen + 893 var screen-on-stack: screen + 894 var screen/edi: (addr screen) <- address screen-on-stack + 895 initialize-screen screen, 5, 4, 0/no-pixel-graphics + 896 # + 897 var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor, 3/fg, 0xc5/bg=blue-bg + 898 check-screen-row screen, 0/y, "(a) ", "F - test-render-gap-buffer-highlight-matching-close-paren" + 899 check-ints-equal x, 4, "F - test-render-gap-buffer-highlight-matching-close-paren: result" + 900 check-background-color-in-screen-row screen, 3/bg=reverse, 0/y, "| ", "F - test-render-gap-buffer-highlight-matching-close-paren: cursor" + 901 check-screen-row-in-color screen, 0xf/fg=highlight, 0/y, " ) ", "F - test-render-gap-buffer-highlight-matching-close-paren: matching paren" + 902 } + 903 + 904 fn test-render-gap-buffer-highlight-matching-open-paren { + 905 var gap-storage: gap-buffer + 906 var gap/esi: (addr gap-buffer) <- address gap-storage + 907 initialize-gap-buffer-with gap, "(a)" + 908 gap-to-end gap + 909 var dummy/eax: grapheme <- gap-left gap + 910 # setup: screen + 911 var screen-on-stack: screen + 912 var screen/edi: (addr screen) <- address screen-on-stack + 913 initialize-screen screen, 5, 4, 0/no-pixel-graphics + 914 # + 915 var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor, 3/fg, 0xc5/bg=blue-bg + 916 check-screen-row screen, 0/y, "(a) ", "F - test-render-gap-buffer-highlight-matching-open-paren" + 917 check-ints-equal x, 4, "F - test-render-gap-buffer-highlight-matching-open-paren: result" + 918 check-background-color-in-screen-row screen, 3/bg=reverse, 0/y, " | ", "F - test-render-gap-buffer-highlight-matching-open-paren: cursor" + 919 check-screen-row-in-color screen, 0xf/fg=highlight, 0/y, "( ", "F - test-render-gap-buffer-highlight-matching-open-paren: matching paren" + 920 } + 921 + 922 fn test-render-gap-buffer-highlight-matching-open-paren-of-end { + 923 var gap-storage: gap-buffer + 924 var gap/esi: (addr gap-buffer) <- address gap-storage + 925 initialize-gap-buffer-with gap, "(a)" + 926 gap-to-end gap + 927 # setup: screen + 928 var screen-on-stack: screen + 929 var screen/edi: (addr screen) <- address screen-on-stack + 930 initialize-screen screen, 5, 4, 0/no-pixel-graphics + 931 # + 932 var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor, 3/fg, 0xc5/bg=blue-bg + 933 check-screen-row screen, 0/y, "(a) ", "F - test-render-gap-buffer-highlight-matching-open-paren-of-end" + 934 check-ints-equal x, 4, "F - test-render-gap-buffer-highlight-matching-open-paren-of-end: result" + 935 check-background-color-in-screen-row screen, 3/bg=reverse, 0/y, " |", "F - test-render-gap-buffer-highlight-matching-open-paren-of-end: cursor" + 936 check-screen-row-in-color screen, 0xf/fg=highlight, 0/y, "( ", "F - test-render-gap-buffer-highlight-matching-open-paren-of-end: matching paren" + 937 } + 938 + 939 # should I highlight a matching open paren? And if so, at what depth from top of left? + 940 # basically there are two cases to disambiguate here: + 941 # Usually the cursor is at top of right. Highlight first '(' at depth 0 from top of left. + 942 # If right is empty, match the ')' _before_ cursor. Highlight first '(' at depth _1_ from top of left. + 943 fn highlight-matching-open-paren? _gap: (addr gap-buffer), render-cursor?: boolean -> _/ebx: boolean, _/edi: int { + 944 # if not rendering cursor, return + 945 compare render-cursor?, 0/false + 946 { + 947 break-if-!= + 948 return 0/false, 0 + 949 } + 950 var gap/esi: (addr gap-buffer) <- copy _gap + 951 var stack/edi: (addr grapheme-stack) <- get gap, right + 952 var top-addr/eax: (addr int) <- get stack, top + 953 var top-index/ecx: int <- copy *top-addr + 954 compare top-index, 0 + 955 { + 956 break-if-> + 957 # if cursor at end, return (char before cursor == ')', 1) + 958 stack <- get gap, left + 959 top-addr <- get stack, top + 960 top-index <- copy *top-addr + 961 compare top-index, 0 + 962 { + 963 break-if-> + 964 return 0/false, 0 + 965 } + 966 top-index <- decrement + 967 var data-ah/eax: (addr handle array grapheme) <- get stack, data + 968 var data/eax: (addr array grapheme) <- lookup *data-ah + 969 var g/eax: (addr grapheme) <- index data, top-index + 970 compare *g, 0x29/close-paren + 971 { + 972 break-if-= + 973 return 0/false, 0 + 974 } + 975 return 1/true, 1 + 976 } + 977 # cursor is not at end; return (char at cursor == ')') + 978 top-index <- decrement + 979 var data-ah/eax: (addr handle array grapheme) <- get stack, data + 980 var data/eax: (addr array grapheme) <- lookup *data-ah + 981 var g/eax: (addr grapheme) <- index data, top-index + 982 compare *g, 0x29/close-paren + 983 { + 984 break-if-= + 985 return 0/false, 0 + 986 } + 987 return 1/true, 0 + 988 } + 989 + 990 fn test-highlight-matching-open-paren { + 991 var gap-storage: gap-buffer + 992 var gap/esi: (addr gap-buffer) <- address gap-storage + 993 initialize-gap-buffer-with gap, "(a)" + 994 gap-to-end gap + 995 var highlight-matching-open-paren?/ebx: boolean <- copy 0/false + 996 var open-paren-depth/edi: int <- copy 0 + 997 highlight-matching-open-paren?, open-paren-depth <- highlight-matching-open-paren? gap, 0/no-cursor + 998 check-not highlight-matching-open-paren?, "F - test-highlight-matching-open-paren: no cursor" + 999 highlight-matching-open-paren?, open-paren-depth <- highlight-matching-open-paren? gap, 1/render-cursor +1000 check highlight-matching-open-paren?, "F - test-highlight-matching-open-paren: at end immediately after ')'" +1001 check-ints-equal open-paren-depth, 1, "F - test-highlight-matching-open-paren: depth at end immediately after ')'" +1002 var dummy/eax: grapheme <- gap-left gap +1003 highlight-matching-open-paren?, open-paren-depth <- highlight-matching-open-paren? gap, 1/render-cursor +1004 check highlight-matching-open-paren?, "F - test-highlight-matching-open-paren: on ')'" +1005 dummy <- gap-left gap +1006 highlight-matching-open-paren?, open-paren-depth <- highlight-matching-open-paren? gap, 1/render-cursor +1007 check-not highlight-matching-open-paren?, "F - test-highlight-matching-open-paren: not on ')'" +1008 } +1009 +1010 ## some primitives for scanning through a gap buffer +1011 # don't modify the gap buffer while scanning +1012 # this includes moving the cursor around +1013 +1014 # restart scan without affecting gap-buffer contents +1015 fn rewind-gap-buffer _self: (addr gap-buffer) { +1016 var self/esi: (addr gap-buffer) <- copy _self +1017 var dest/eax: (addr int) <- get self, left-read-index +1018 copy-to *dest, 0 +1019 dest <- get self, right-read-index +1020 copy-to *dest, 0 +1021 } +1022 +1023 fn gap-buffer-scan-done? _self: (addr gap-buffer) -> _/eax: boolean { +1024 var self/esi: (addr gap-buffer) <- copy _self +1025 # more in left? +1026 var left/eax: (addr grapheme-stack) <- get self, left +1027 var left-size/eax: int <- grapheme-stack-length left +1028 var left-read-index/ecx: (addr int) <- get self, left-read-index +1029 compare *left-read-index, left-size +1030 { +1031 break-if->= +1032 return 0/false +1033 } +1034 # more in right? +1035 var right/eax: (addr grapheme-stack) <- get self, right +1036 var right-size/eax: int <- grapheme-stack-length right +1037 var right-read-index/ecx: (addr int) <- get self, right-read-index +1038 compare *right-read-index, right-size +1039 { +1040 break-if->= +1041 return 0/false +1042 } +1043 # +1044 return 1/true +1045 } +1046 +1047 fn peek-from-gap-buffer _self: (addr gap-buffer) -> _/eax: grapheme { +1048 var self/esi: (addr gap-buffer) <- copy _self +1049 # more in left? +1050 var left/ecx: (addr grapheme-stack) <- get self, left +1051 var left-size/eax: int <- grapheme-stack-length left +1052 var left-read-index-a/edx: (addr int) <- get self, left-read-index +1053 compare *left-read-index-a, left-size +1054 { +1055 break-if->= +1056 var left-data-ah/eax: (addr handle array grapheme) <- get left, data +1057 var left-data/eax: (addr array grapheme) <- lookup *left-data-ah +1058 var left-read-index/ecx: int <- copy *left-read-index-a +1059 var result/eax: (addr grapheme) <- index left-data, left-read-index +1060 return *result +1061 } +1062 # more in right? +1063 var right/ecx: (addr grapheme-stack) <- get self, right +1064 var _right-size/eax: int <- grapheme-stack-length right +1065 var right-size/ebx: int <- copy _right-size +1066 var right-read-index-a/edx: (addr int) <- get self, right-read-index +1067 compare *right-read-index-a, right-size +1068 { +1069 break-if->= +1070 # read the right from reverse +1071 var right-data-ah/eax: (addr handle array grapheme) <- get right, data +1072 var right-data/eax: (addr array grapheme) <- lookup *right-data-ah +1073 var right-read-index/ebx: int <- copy right-size +1074 right-read-index <- subtract *right-read-index-a +1075 right-read-index <- subtract 1 +1076 var result/eax: (addr grapheme) <- index right-data, right-read-index +1077 return *result +1078 } +1079 # if we get here there's nothing left +1080 return 0/nul +1081 } +1082 +1083 fn read-from-gap-buffer _self: (addr gap-buffer) -> _/eax: grapheme { +1084 var self/esi: (addr gap-buffer) <- copy _self +1085 # more in left? +1086 var left/ecx: (addr grapheme-stack) <- get self, left +1087 var left-size/eax: int <- grapheme-stack-length left +1088 var left-read-index-a/edx: (addr int) <- get self, left-read-index +1089 compare *left-read-index-a, left-size +1090 { +1091 break-if->= +1092 var left-data-ah/eax: (addr handle array grapheme) <- get left, data +1093 var left-data/eax: (addr array grapheme) <- lookup *left-data-ah +1094 var left-read-index/ecx: int <- copy *left-read-index-a +1095 var result/eax: (addr grapheme) <- index left-data, left-read-index +1096 increment *left-read-index-a +1097 return *result +1098 } +1099 # more in right? +1100 var right/ecx: (addr grapheme-stack) <- get self, right +1101 var _right-size/eax: int <- grapheme-stack-length right +1102 var right-size/ebx: int <- copy _right-size +1103 var right-read-index-a/edx: (addr int) <- get self, right-read-index +1104 compare *right-read-index-a, right-size +1105 { +1106 break-if->= +1107 # read the right from reverse +1108 var right-data-ah/eax: (addr handle array grapheme) <- get right, data +1109 var right-data/eax: (addr array grapheme) <- lookup *right-data-ah +1110 var right-read-index/ebx: int <- copy right-size +1111 right-read-index <- subtract *right-read-index-a +1112 right-read-index <- subtract 1 +1113 var result/eax: (addr grapheme) <- index right-data, right-read-index +1114 increment *right-read-index-a +1115 return *result +1116 } +1117 # if we get here there's nothing left +1118 return 0/nul +1119 } +1120 +1121 fn put-back-from-gap-buffer _self: (addr gap-buffer) { +1122 var self/esi: (addr gap-buffer) <- copy _self +1123 # more in right? +1124 var right/eax: (addr grapheme-stack) <- get self, right +1125 var right-size/eax: int <- grapheme-stack-length right +1126 var right-read-index-a/eax: (addr int) <- get self, right-read-index +1127 compare *right-read-index-a, 0 +1128 { +1129 break-if-<= +1130 decrement *right-read-index-a +1131 return +1132 } +1133 # more in left? +1134 var left/eax: (addr grapheme-stack) <- get self, left +1135 var left-size/eax: int <- grapheme-stack-length left +1136 var left-read-index-a/eax: (addr int) <- get self, left-read-index +1137 decrement *left-read-index-a +1138 } +1139 +1140 fn test-read-from-gap-buffer { +1141 var gap-storage: gap-buffer +1142 var gap/esi: (addr gap-buffer) <- address gap-storage +1143 initialize-gap-buffer-with gap, "abc" +1144 # gap is at end, all contents are in left +1145 var done?/eax: boolean <- gap-buffer-scan-done? gap +1146 check-not done?, "F - test-read-from-gap-buffer/left-1/done" +1147 var g/eax: grapheme <- read-from-gap-buffer gap +1148 var x/ecx: int <- copy g +1149 check-ints-equal x, 0x61/a, "F - test-read-from-gap-buffer/left-1" +1150 var done?/eax: boolean <- gap-buffer-scan-done? gap +1151 check-not done?, "F - test-read-from-gap-buffer/left-2/done" +1152 var g/eax: grapheme <- read-from-gap-buffer gap +1153 var x/ecx: int <- copy g +1154 check-ints-equal x, 0x62/b, "F - test-read-from-gap-buffer/left-2" +1155 var done?/eax: boolean <- gap-buffer-scan-done? gap +1156 check-not done?, "F - test-read-from-gap-buffer/left-3/done" +1157 var g/eax: grapheme <- read-from-gap-buffer gap +1158 var x/ecx: int <- copy g +1159 check-ints-equal x, 0x63/c, "F - test-read-from-gap-buffer/left-3" +1160 var done?/eax: boolean <- gap-buffer-scan-done? gap +1161 check done?, "F - test-read-from-gap-buffer/left-4/done" +1162 var g/eax: grapheme <- read-from-gap-buffer gap +1163 var x/ecx: int <- copy g +1164 check-ints-equal x, 0/nul, "F - test-read-from-gap-buffer/left-4" +1165 # now check when everything is to the right +1166 gap-to-start gap +1167 rewind-gap-buffer gap +1168 var done?/eax: boolean <- gap-buffer-scan-done? gap +1169 check-not done?, "F - test-read-from-gap-buffer/right-1/done" +1170 var g/eax: grapheme <- read-from-gap-buffer gap +1171 var x/ecx: int <- copy g +1172 check-ints-equal x, 0x61/a, "F - test-read-from-gap-buffer/right-1" +1173 var done?/eax: boolean <- gap-buffer-scan-done? gap +1174 check-not done?, "F - test-read-from-gap-buffer/right-2/done" +1175 var g/eax: grapheme <- read-from-gap-buffer gap +1176 var x/ecx: int <- copy g +1177 check-ints-equal x, 0x62/b, "F - test-read-from-gap-buffer/right-2" +1178 var done?/eax: boolean <- gap-buffer-scan-done? gap +1179 check-not done?, "F - test-read-from-gap-buffer/right-3/done" +1180 var g/eax: grapheme <- read-from-gap-buffer gap +1181 var x/ecx: int <- copy g +1182 check-ints-equal x, 0x63/c, "F - test-read-from-gap-buffer/right-3" +1183 var done?/eax: boolean <- gap-buffer-scan-done? gap +1184 check done?, "F - test-read-from-gap-buffer/right-4/done" +1185 var g/eax: grapheme <- read-from-gap-buffer gap +1186 var x/ecx: int <- copy g +1187 check-ints-equal x, 0/nul, "F - test-read-from-gap-buffer/right-4" +1188 } +1189 +1190 fn skip-spaces-from-gap-buffer self: (addr gap-buffer) { +1191 var done?/eax: boolean <- gap-buffer-scan-done? self +1192 compare done?, 0/false +1193 break-if-!= +1194 var g/eax: grapheme <- peek-from-gap-buffer self +1195 { +1196 compare g, 0x20/space +1197 break-if-= +1198 return +1199 } +1200 g <- read-from-gap-buffer self +1201 loop +1202 } +1203 +1204 fn edit-gap-buffer self: (addr gap-buffer), key: grapheme { +1205 var g/edx: grapheme <- copy key +1206 { +1207 compare g, 8/backspace +1208 break-if-!= +1209 delete-before-gap self +1210 return +1211 } +1212 { +1213 compare g, 0x80/left-arrow +1214 break-if-!= +1215 var dummy/eax: grapheme <- gap-left self +1216 return +1217 } +1218 { +1219 compare g, 0x83/right-arrow +1220 break-if-!= +1221 var dummy/eax: grapheme <- gap-right self +1222 return +1223 } +1224 { +1225 compare g, 6/ctrl-f +1226 break-if-!= +1227 gap-to-start-of-next-word self +1228 return +1229 } +1230 { +1231 compare g, 2/ctrl-b +1232 break-if-!= +1233 gap-to-end-of-previous-word self +1234 return +1235 } +1236 { +1237 compare g, 1/ctrl-a +1238 break-if-!= +1239 gap-to-previous-start-of-line self +1240 return +1241 } +1242 { +1243 compare g, 5/ctrl-e +1244 break-if-!= +1245 gap-to-next-end-of-line self +1246 return +1247 } +1248 { +1249 compare g, 0x81/down-arrow +1250 break-if-!= +1251 gap-down self +1252 return +1253 } +1254 { +1255 compare g, 0x82/up-arrow +1256 break-if-!= +1257 gap-up self +1258 return +1259 } +1260 { +1261 compare g, 0x15/ctrl-u +1262 break-if-!= +1263 clear-gap-buffer self +1264 return +1265 } +1266 { +1267 compare g, 9/tab +1268 break-if-!= +1269 # tab = 2 spaces +1270 add-code-point-at-gap self, 0x20/space +1271 add-code-point-at-gap self, 0x20/space +1272 return +1273 } +1274 # default: insert character +1275 add-grapheme-at-gap self, g +1276 } +1277 +1278 fn gap-to-start-of-next-word self: (addr gap-buffer) { +1279 var curr/eax: grapheme <- copy 0 +1280 # skip to next space +1281 { +1282 curr <- gap-right self +1283 compare curr, -1 +1284 break-if-= +1285 compare curr, 0x20/space +1286 break-if-= +1287 compare curr, 0xa/newline +1288 break-if-= +1289 loop +1290 } +1291 # skip past spaces +1292 { +1293 curr <- gap-right self +1294 compare curr, -1 +1295 break-if-= +1296 compare curr, 0x20/space +1297 loop-if-= +1298 compare curr, 0xa/space +1299 loop-if-= +1300 curr <- gap-left self +1301 break +1302 } +1303 } +1304 +1305 fn gap-to-end-of-previous-word self: (addr gap-buffer) { +1306 var curr/eax: grapheme <- copy 0 +1307 # skip to previous space +1308 { +1309 curr <- gap-left self +1310 compare curr, -1 +1311 break-if-= +1312 compare curr, 0x20/space +1313 break-if-= +1314 compare curr, 0xa/newline +1315 break-if-= +1316 loop +1317 } +1318 # skip past all spaces but one +1319 { +1320 curr <- gap-left self +1321 compare curr, -1 +1322 break-if-= +1323 compare curr, 0x20/space +1324 loop-if-= +1325 compare curr, 0xa/space +1326 loop-if-= +1327 curr <- gap-right self +1328 break +1329 } +1330 } +1331 +1332 fn gap-to-previous-start-of-line self: (addr gap-buffer) { +1333 # skip past immediate newline +1334 var dummy/eax: grapheme <- gap-left self +1335 # skip to previous newline +1336 { +1337 dummy <- gap-left self +1338 { +1339 compare dummy, -1 +1340 break-if-!= +1341 return +1342 } +1343 { +1344 compare dummy, 0xa/newline +1345 break-if-!= +1346 dummy <- gap-right self +1347 return +1348 } +1349 loop +1350 } +1351 } +1352 +1353 fn gap-to-next-end-of-line self: (addr gap-buffer) { +1354 # skip past immediate newline +1355 var dummy/eax: grapheme <- gap-right self +1356 # skip to next newline +1357 { +1358 dummy <- gap-right self +1359 { +1360 compare dummy, -1 +1361 break-if-!= +1362 return +1363 } +1364 { +1365 compare dummy, 0xa/newline +1366 break-if-!= +1367 dummy <- gap-left self +1368 return +1369 } +1370 loop +1371 } +1372 } +1373 +1374 fn gap-up self: (addr gap-buffer) { +1375 # compute column +1376 var col/edx: int <- count-columns-to-start-of-line self +1377 # +1378 gap-to-previous-start-of-line self +1379 # skip ahead by up to col on previous line +1380 var i/ecx: int <- copy 0 +1381 { +1382 compare i, col +1383 break-if->= +1384 var curr/eax: grapheme <- gap-right self +1385 { +1386 compare curr, -1 +1387 break-if-!= +1388 return +1389 } +1390 compare curr, 0xa/newline +1391 { +1392 break-if-!= +1393 curr <- gap-left self +1394 return +1395 } +1396 i <- increment +1397 loop +1398 } +1399 } +1400 +1401 fn gap-down self: (addr gap-buffer) { +1402 # compute column +1403 var col/edx: int <- count-columns-to-start-of-line self +1404 # skip to start of next line +1405 gap-to-end-of-line self +1406 var dummy/eax: grapheme <- gap-right self +1407 # skip ahead by up to col on previous line +1408 var i/ecx: int <- copy 0 +1409 { +1410 compare i, col +1411 break-if->= +1412 var curr/eax: grapheme <- gap-right self +1413 { +1414 compare curr, -1 +1415 break-if-!= +1416 return +1417 } +1418 compare curr, 0xa/newline +1419 { +1420 break-if-!= +1421 curr <- gap-left self +1422 return +1423 } +1424 i <- increment +1425 loop +1426 } +1427 } +1428 +1429 fn count-columns-to-start-of-line self: (addr gap-buffer) -> _/edx: int { +1430 var count/edx: int <- copy 0 +1431 var dummy/eax: grapheme <- copy 0 +1432 # skip to previous newline +1433 { +1434 dummy <- gap-left self +1435 { +1436 compare dummy, -1 +1437 break-if-!= +1438 return count +1439 } +1440 { +1441 compare dummy, 0xa/newline +1442 break-if-!= +1443 dummy <- gap-right self +1444 return count +1445 } +1446 count <- increment +1447 loop +1448 } +1449 return count +1450 } +1451 +1452 fn gap-to-end-of-line self: (addr gap-buffer) { +1453 var dummy/eax: grapheme <- copy 0 +1454 # skip to next newline +1455 { +1456 dummy <- gap-right self +1457 { +1458 compare dummy, -1 +1459 break-if-!= +1460 return +1461 } +1462 { +1463 compare dummy, 0xa/newline +1464 break-if-!= +1465 dummy <- gap-left self +1466 return +1467 } +1468 loop +1469 } +1470 } ++ + + diff --git a/html/apps/color-game.mu.html b/html/apps/color-game.mu.html index 07fac07f..7838733a 100644 --- a/html/apps/color-game.mu.html +++ b/html/apps/color-game.mu.html @@ -1,36 +1,35 @@ - + - +