1 # grapheme stacks are the smallest unit of editable text
4 data: (handle array grapheme)
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
12 var top/eax: (addr int) <- get self, top
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
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
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
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
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
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
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
77 var g/edx: (addr grapheme) <- index data, i
78 push-grapheme-stack dest, *g
84 # dump stack to screen from bottom to top
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
103 var g/eax: (addr grapheme) <- index data, i
104 var tmp/eax: code-point <- to-code-point *g
109 var tmp/eax: int <- copy color
113 compare i, matching-open-paren-index
115 copy-to fg, 0xf/highlight
117 x, y <- render-code-point screen, c, xmin, ymin, xmax, ymax, x, y, fg, background-color # TODO: handle combining characters
125 # helper for small words
126 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 {
127 var _width/eax: int <- copy 0
128 var _height/ecx: int <- copy 0
129 _width, _height <- screen-size screen
130 var width/edx: int <- copy _width
131 var height/ebx: int <- copy _height
132 var x2/eax: int <- copy 0
133 var y2/ecx: int <- copy 0
134 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
138 # dump stack to screen from top to bottom
139 # optionally render a 'cursor' with the top grapheme
143 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 {
144 var self/esi: (addr grapheme-stack) <- copy _self
145 var matching-close-paren-index/edx: int <- get-matching-close-paren-index self, render-cursor?
146 var data-ah/eax: (addr handle array grapheme) <- get self, data
147 var _data/eax: (addr array grapheme) <- lookup *data-ah
148 var data/edi: (addr array grapheme) <- copy _data
149 var x/eax: int <- copy _x
150 var y/ecx: int <- copy _y
151 var top-addr/ebx: (addr int) <- get self, top
152 var i/ebx: int <- copy *top-addr
154 # if render-cursor?, peel off first iteration
156 compare render-cursor?, 0/false
162 var g/eax: (addr grapheme) <- index data, i
163 var tmp/eax: code-point <- to-code-point *g
166 x, y <- render-code-point screen, c, xmin, ymin, xmax, ymax, x, y, background-color, color
169 # remaining iterations
173 # highlight matching paren if needed
176 var tmp/eax: int <- copy color
179 compare i, matching-close-paren-index
182 copy-to fg, 0xf/highlight
187 var g/eax: (addr grapheme) <- index data, i
188 var tmp/eax: code-point <- to-code-point *g
191 x, y <- render-code-point screen, c, xmin, ymin, xmax, ymax, x, y, fg, background-color
198 # helper for small words
199 fn render-stack-from-top screen: (addr screen), self: (addr grapheme-stack), x: int, y: int, render-cursor?: boolean -> _/eax: int {
200 var _width/eax: int <- copy 0
201 var _height/ecx: int <- copy 0
202 _width, _height <- screen-size screen
203 var width/edx: int <- copy _width
204 var height/ebx: int <- copy _height
205 var x2/eax: int <- copy 0
206 var y2/ecx: int <- copy 0
207 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
211 fn test-render-grapheme-stack {
213 var gs-storage: grapheme-stack
214 var gs/edi: (addr grapheme-stack) <- address gs-storage
215 initialize-grapheme-stack gs, 5
216 var g/eax: grapheme <- copy 0x61/a
217 push-grapheme-stack gs, g
219 push-grapheme-stack gs, g
221 push-grapheme-stack gs, g
223 var screen-storage: screen
224 var screen/esi: (addr screen) <- address screen-storage
225 initialize-screen screen, 5, 4, 0/no-pixel-graphics
227 var x/eax: int <- render-stack-from-bottom screen, gs, 0/x, 0/y, 0/no-highlight-matching-open-paren, 0/open-paren-depth
228 check-screen-row screen, 0/y, "abc ", "F - test-render-grapheme-stack from bottom"
229 check-ints-equal x, 3, "F - test-render-grapheme-stack from bottom: result"
230 check-background-color-in-screen-row screen, 3/bg=reverse, 0/y, " ", "F - test-render-grapheme-stack from bottom: bg"
232 var x/eax: int <- render-stack-from-top screen, gs, 0/x, 1/y, 0/cursor=false
233 check-screen-row screen, 1/y, "cba ", "F - test-render-grapheme-stack from top without cursor"
234 check-ints-equal x, 3, "F - test-render-grapheme-stack from top without cursor: result"
235 check-background-color-in-screen-row screen, 3/bg=reverse, 1/y, " ", "F - test-render-grapheme-stack from top without cursor: bg"
237 var x/eax: int <- render-stack-from-top screen, gs, 0/x, 2/y, 1/cursor=true
238 check-screen-row screen, 2/y, "cba ", "F - test-render-grapheme-stack from top with cursor"
239 check-ints-equal x, 3, "F - test-render-grapheme-stack from top with cursor: result"
240 check-background-color-in-screen-row screen, 3/bg=reverse, 2/y, "| ", "F - test-render-grapheme-stack from top with cursor: bg"
243 fn test-render-grapheme-stack-while-highlighting-matching-close-paren {
245 var gs-storage: grapheme-stack
246 var gs/edi: (addr grapheme-stack) <- address gs-storage
247 initialize-grapheme-stack gs, 5
248 var g/eax: grapheme <- copy 0x29/close-paren
249 push-grapheme-stack gs, g
251 push-grapheme-stack gs, g
252 g <- copy 0x28/open-paren
253 push-grapheme-stack gs, g
255 var screen-storage: screen
256 var screen/esi: (addr screen) <- address screen-storage
257 initialize-screen screen, 5, 4, 0/no-pixel-graphics
259 var x/eax: int <- render-stack-from-top screen, gs, 0/x, 2/y, 1/cursor=true
260 check-screen-row screen, 2/y, "(b) ", "F - test-render-grapheme-stack-while-highlighting-matching-close-paren"
261 check-background-color-in-screen-row screen, 3/bg=reverse, 2/y, "| ", "F - test-render-grapheme-stack-while-highlighting-matching-close-paren: cursor"
262 check-screen-row-in-color screen, 0xf/fg=white, 2/y, " ) ", "F - test-render-grapheme-stack-while-highlighting-matching-close-paren: matching paren"
265 fn test-render-grapheme-stack-while-highlighting-matching-close-paren-2 {
266 # setup: gs = "(a (b)) c"
267 var gs-storage: grapheme-stack
268 var gs/edi: (addr grapheme-stack) <- address gs-storage
269 initialize-grapheme-stack gs, 0x10
270 var g/eax: grapheme <- copy 0x63/c
271 push-grapheme-stack gs, g
273 push-grapheme-stack gs, g
274 g <- copy 0x29/close-paren
275 push-grapheme-stack gs, g
276 g <- copy 0x29/close-paren
277 push-grapheme-stack gs, g
279 push-grapheme-stack gs, g
280 g <- copy 0x28/open-paren
281 push-grapheme-stack gs, g
283 push-grapheme-stack gs, g
285 push-grapheme-stack gs, g
286 g <- copy 0x28/open-paren
287 push-grapheme-stack gs, g
289 var screen-storage: screen
290 var screen/esi: (addr screen) <- address screen-storage
291 initialize-screen screen, 5, 4, 0/no-pixel-graphics
293 var x/eax: int <- render-stack-from-top screen, gs, 0/x, 2/y, 1/cursor=true
294 check-screen-row screen, 2/y, "(a (b)) c ", "F - test-render-grapheme-stack-while-highlighting-matching-close-paren-2"
295 check-background-color-in-screen-row screen, 3/bg=reverse, 2/y, "| ", "F - test-render-grapheme-stack-while-highlighting-matching-close-paren-2: cursor"
296 check-screen-row-in-color screen, 0xf/fg=white, 2/y, " ) ", "F - test-render-grapheme-stack-while-highlighting-matching-close-paren-2: matching paren"
299 fn test-render-grapheme-stack-while-highlighting-matching-open-paren-with-close-paren-at-end {
301 var gs-storage: grapheme-stack
302 var gs/edi: (addr grapheme-stack) <- address gs-storage
303 initialize-grapheme-stack gs, 5
304 var g/eax: grapheme <- copy 0x28/open-paren
305 push-grapheme-stack gs, g
307 push-grapheme-stack gs, g
308 g <- copy 0x29/close-paren
309 push-grapheme-stack gs, g
311 var screen-storage: screen
312 var screen/esi: (addr screen) <- address screen-storage
313 initialize-screen screen, 5, 4, 0/no-pixel-graphics
315 var x/eax: int <- render-stack-from-bottom screen, gs, 0/x, 2/y, 1/highlight-matching-open-paren, 1/open-paren-depth
316 check-screen-row screen, 2/y, "(b) ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren-with-close-paren-at-end"
317 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"
320 fn test-render-grapheme-stack-while-highlighting-matching-open-paren-with-close-paren-at-end-2 {
321 # setup: gs = "a((b))"
322 var gs-storage: grapheme-stack
323 var gs/edi: (addr grapheme-stack) <- address gs-storage
324 initialize-grapheme-stack gs, 0x10
325 var g/eax: grapheme <- copy 0x61/a
326 push-grapheme-stack gs, g
327 g <- copy 0x28/open-paren
328 push-grapheme-stack gs, g
329 g <- copy 0x28/open-paren
330 push-grapheme-stack gs, g
332 push-grapheme-stack gs, g
333 g <- copy 0x29/close-paren
334 push-grapheme-stack gs, g
335 g <- copy 0x29/close-paren
336 push-grapheme-stack gs, g
338 var screen-storage: screen
339 var screen/esi: (addr screen) <- address screen-storage
340 initialize-screen screen, 5, 4, 0/no-pixel-graphics
342 var x/eax: int <- render-stack-from-bottom screen, gs, 0/x, 2/y, 1/highlight-matching-open-paren, 1/open-paren-depth
343 check-screen-row screen, 2/y, "a((b)) ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren-with-close-paren-at-end-2"
344 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"
347 fn test-render-grapheme-stack-while-highlighting-matching-open-paren {
349 var gs-storage: grapheme-stack
350 var gs/edi: (addr grapheme-stack) <- address gs-storage
351 initialize-grapheme-stack gs, 5
352 var g/eax: grapheme <- copy 0x28/open-paren
353 push-grapheme-stack gs, g
355 push-grapheme-stack gs, g
357 var screen-storage: screen
358 var screen/esi: (addr screen) <- address screen-storage
359 initialize-screen screen, 5, 4, 0/no-pixel-graphics
361 var x/eax: int <- render-stack-from-bottom screen, gs, 0/x, 2/y, 1/highlight-matching-open-paren, 0/open-paren-depth
362 check-screen-row screen, 2/y, "(b ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren"
363 check-screen-row-in-color screen, 0xf/fg=white, 2/y, "( ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren: matching paren"
366 fn test-render-grapheme-stack-while-highlighting-matching-open-paren-2 {
367 # setup: gs = "a((b)"
368 var gs-storage: grapheme-stack
369 var gs/edi: (addr grapheme-stack) <- address gs-storage
370 initialize-grapheme-stack gs, 0x10
371 var g/eax: grapheme <- copy 0x61/a
372 push-grapheme-stack gs, g
373 g <- copy 0x28/open-paren
374 push-grapheme-stack gs, g
375 g <- copy 0x28/open-paren
376 push-grapheme-stack gs, g
378 push-grapheme-stack gs, g
379 g <- copy 0x29/close-paren
380 push-grapheme-stack gs, g
382 var screen-storage: screen
383 var screen/esi: (addr screen) <- address screen-storage
384 initialize-screen screen, 5, 4, 0/no-pixel-graphics
386 var x/eax: int <- render-stack-from-bottom screen, gs, 0/x, 2/y, 1/highlight-matching-open-paren, 0/open-paren-depth
387 check-screen-row screen, 2/y, "a((b) ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren-2"
388 check-screen-row-in-color screen, 0xf/fg=white, 2/y, " ( ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren-2: matching paren"
391 # return the index of the matching close-paren of the grapheme at cursor (top of stack)
392 # or top index if there's no matching close-paren
393 fn get-matching-close-paren-index _self: (addr grapheme-stack), render-cursor?: boolean -> _/edx: int {
394 var self/esi: (addr grapheme-stack) <- copy _self
395 var top-addr/edx: (addr int) <- get self, top
396 # if not rendering cursor, return
397 compare render-cursor?, 0/false
402 var data-ah/eax: (addr handle array grapheme) <- get self, data
403 var data/eax: (addr array grapheme) <- lookup *data-ah
404 var i/ecx: int <- copy *top-addr
405 # if stack is empty, return
411 # if cursor is not '(' return
413 var g/esi: (addr grapheme) <- index data, i
414 compare *g, 0x28/open-paren
419 # otherwise scan to matching paren
420 var paren-count/ebx: int <- copy 1
425 var g/esi: (addr grapheme) <- index data, i
426 compare *g, 0x28/open-paren
429 paren-count <- increment
431 compare *g, 0x29/close-paren
434 compare paren-count, 1
439 paren-count <- decrement
447 # return the index of the first open-paren at the given depth
448 # or top index if there's no matching close-paren
449 fn get-matching-open-paren-index _self: (addr grapheme-stack), control: boolean, depth: int -> _/edx: int {
450 var self/esi: (addr grapheme-stack) <- copy _self
451 var top-addr/edx: (addr int) <- get self, top
452 # if not rendering cursor, return
453 compare control, 0/false
458 var data-ah/eax: (addr handle array grapheme) <- get self, data
459 var data/eax: (addr array grapheme) <- lookup *data-ah
460 var i/ecx: int <- copy *top-addr
461 # if stack is empty, return
467 # scan to matching open paren
468 var paren-count/ebx: int <- copy 0
473 var g/esi: (addr grapheme) <- index data, i
474 compare *g, 0x29/close-paren
477 paren-count <- increment
479 compare *g, 0x28/open-paren
482 compare paren-count, depth
487 paren-count <- decrement
495 # compare from bottom
496 # beware: modifies 'stream', which must be disposed of after a false result
497 fn prefix-match? _self: (addr grapheme-stack), s: (addr stream byte) -> _/eax: boolean {
498 var self/esi: (addr grapheme-stack) <- copy _self
499 var data-ah/edi: (addr handle array grapheme) <- get self, data
500 var _data/eax: (addr array grapheme) <- lookup *data-ah
501 var data/edi: (addr array grapheme) <- copy _data
502 var top-addr/ecx: (addr int) <- get self, top
503 var i/ebx: int <- copy 0
507 # if curr != expected, return false
509 var curr-a/edx: (addr grapheme) <- index data, i
510 var expected/eax: grapheme <- read-grapheme s
512 compare expected, *curr-a
523 # compare from bottom
524 # beware: modifies 'stream', which must be disposed of after a false result
525 fn suffix-match? _self: (addr grapheme-stack), s: (addr stream byte) -> _/eax: boolean {
526 var self/esi: (addr grapheme-stack) <- copy _self
527 var data-ah/edi: (addr handle array grapheme) <- get self, data
528 var _data/eax: (addr array grapheme) <- lookup *data-ah
529 var data/edi: (addr array grapheme) <- copy _data
530 var top-addr/eax: (addr int) <- get self, top
531 var i/ebx: int <- copy *top-addr
537 var curr-a/edx: (addr grapheme) <- index data, i
538 var expected/eax: grapheme <- read-grapheme s
539 # if curr != expected, return false
541 compare expected, *curr-a
552 fn grapheme-stack-is-decimal-integer? _self: (addr grapheme-stack) -> _/eax: boolean {
553 var self/esi: (addr grapheme-stack) <- copy _self
554 var data-ah/eax: (addr handle array grapheme) <- get self, data
555 var _data/eax: (addr array grapheme) <- lookup *data-ah
556 var data/edx: (addr array grapheme) <- copy _data
557 var top-addr/ecx: (addr int) <- get self, top
558 var i/ebx: int <- copy 0
559 var result/eax: boolean <- copy 1/true
560 $grapheme-stack-is-integer?:loop: {
563 var g/edx: (addr grapheme) <- index data, i
564 result <- decimal-digit? *g
565 compare result, 0/false