1 # Grapheme stacks are the smallest unit of editable text.
3 # (Currently they just support single-code-point graphemes.)
6 data: (handle array code-point-utf8)
10 fn initialize-grapheme-stack _self: (addr grapheme-stack), n: int {
11 var self/esi: (addr grapheme-stack) <- copy _self
12 var d/edi: (addr handle array code-point-utf8) <- get self, data
14 var top/eax: (addr int) <- get self, top
18 fn clear-grapheme-stack _self: (addr grapheme-stack) {
19 var self/esi: (addr grapheme-stack) <- copy _self
20 var top/eax: (addr int) <- get self, top
24 fn grapheme-stack-empty? _self: (addr grapheme-stack) -> _/eax: boolean {
25 var self/esi: (addr grapheme-stack) <- copy _self
26 var top/eax: (addr int) <- get self, top
35 fn grapheme-stack-length _self: (addr grapheme-stack) -> _/eax: int {
36 var self/esi: (addr grapheme-stack) <- copy _self
37 var top/eax: (addr int) <- get self, top
41 fn push-grapheme-stack _self: (addr grapheme-stack), _val: code-point-utf8 {
42 var self/esi: (addr grapheme-stack) <- copy _self
43 var top-addr/ecx: (addr int) <- get self, top
44 var data-ah/edx: (addr handle array code-point-utf8) <- get self, data
45 var data/eax: (addr array code-point-utf8) <- lookup *data-ah
46 var top/edx: int <- copy *top-addr
47 var dest-addr/edx: (addr code-point-utf8) <- index data, top
48 var val/eax: code-point-utf8 <- copy _val
49 copy-to *dest-addr, val
53 fn pop-grapheme-stack _self: (addr grapheme-stack) -> _/eax: code-point-utf8 {
54 var self/esi: (addr grapheme-stack) <- copy _self
55 var top-addr/ecx: (addr int) <- get self, top
61 subtract-from *top-addr, 1
62 var data-ah/edx: (addr handle array code-point-utf8) <- get self, data
63 var data/eax: (addr array code-point-utf8) <- lookup *data-ah
64 var top/edx: int <- copy *top-addr
65 var result-addr/eax: (addr code-point-utf8) <- index data, top
69 fn copy-grapheme-stack _src: (addr grapheme-stack), dest: (addr grapheme-stack) {
70 var src/esi: (addr grapheme-stack) <- copy _src
71 var data-ah/edi: (addr handle array code-point-utf8) <- get src, data
72 var _data/eax: (addr array code-point-utf8) <- lookup *data-ah
73 var data/edi: (addr array code-point-utf8) <- copy _data
74 var top-addr/ecx: (addr int) <- get src, top
75 var i/eax: int <- copy 0
79 var g/edx: (addr code-point-utf8) <- index data, i
80 push-grapheme-stack dest, *g
86 # dump stack to screen from bottom to top
89 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 {
90 var self/esi: (addr grapheme-stack) <- copy _self
91 var matching-open-paren-index/edx: int <- get-matching-open-paren-index self, highlight-matching-open-paren?, open-paren-depth
92 var data-ah/edi: (addr handle array code-point-utf8) <- get self, data
93 var _data/eax: (addr array code-point-utf8) <- lookup *data-ah
94 var data/edi: (addr array code-point-utf8) <- copy _data
95 var x/eax: int <- copy _x
96 var y/ecx: int <- copy _y
97 var top-addr/esi: (addr int) <- get self, top
98 var i/ebx: int <- copy 0
105 var g/eax: (addr code-point-utf8) <- index data, i
106 var tmp/eax: code-point <- to-code-point *g
111 var tmp/eax: int <- copy color
115 compare i, matching-open-paren-index
117 copy-to fg, 0xf/highlight
119 x, y <- render-code-point screen, c, xmin, ymin, xmax, ymax, x, y, fg, background-color # TODO: handle combining characters
127 # helper for small words
128 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 {
129 var _width/eax: int <- copy 0
130 var _height/ecx: int <- copy 0
131 _width, _height <- screen-size screen
132 var width/edx: int <- copy _width
133 var height/ebx: int <- copy _height
134 var x2/eax: int <- copy 0
135 var y2/ecx: int <- copy 0
136 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
140 # dump stack to screen from top to bottom
141 # optionally render a 'cursor' with the top code-point-utf8
145 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 {
146 var self/esi: (addr grapheme-stack) <- copy _self
147 var matching-close-paren-index/edx: int <- get-matching-close-paren-index self, render-cursor?
148 var data-ah/eax: (addr handle array code-point-utf8) <- get self, data
149 var _data/eax: (addr array code-point-utf8) <- lookup *data-ah
150 var data/edi: (addr array code-point-utf8) <- copy _data
151 var x/eax: int <- copy _x
152 var y/ecx: int <- copy _y
153 var top-addr/ebx: (addr int) <- get self, top
154 var i/ebx: int <- copy *top-addr
156 # if render-cursor?, peel off first iteration
158 compare render-cursor?, 0/false
164 var g/eax: (addr code-point-utf8) <- index data, i
165 var tmp/eax: code-point <- to-code-point *g
168 x, y <- render-code-point screen, c, xmin, ymin, xmax, ymax, x, y, background-color, color
171 # remaining iterations
175 # highlight matching paren if needed
178 var tmp/eax: int <- copy color
181 compare i, matching-close-paren-index
184 copy-to fg, 0xf/highlight
189 var g/eax: (addr code-point-utf8) <- index data, i
190 var tmp/eax: code-point <- to-code-point *g
193 x, y <- render-code-point screen, c, xmin, ymin, xmax, ymax, x, y, fg, background-color
200 # helper for small words
201 fn render-stack-from-top screen: (addr screen), self: (addr grapheme-stack), x: int, y: int, render-cursor?: boolean -> _/eax: int {
202 var _width/eax: int <- copy 0
203 var _height/ecx: int <- copy 0
204 _width, _height <- screen-size screen
205 var width/edx: int <- copy _width
206 var height/ebx: int <- copy _height
207 var x2/eax: int <- copy 0
208 var y2/ecx: int <- copy 0
209 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
213 fn test-render-grapheme-stack {
215 var gs-storage: grapheme-stack
216 var gs/edi: (addr grapheme-stack) <- address gs-storage
217 initialize-grapheme-stack gs, 5
218 var g/eax: code-point-utf8 <- copy 0x61/a
219 push-grapheme-stack gs, g
221 push-grapheme-stack gs, g
223 push-grapheme-stack gs, g
225 var screen-storage: screen
226 var screen/esi: (addr screen) <- address screen-storage
227 initialize-screen screen, 5, 4, 0/no-pixel-graphics
229 var x/eax: int <- render-stack-from-bottom screen, gs, 0/x, 0/y, 0/no-highlight-matching-open-paren, 0/open-paren-depth
230 check-screen-row screen, 0/y, "abc ", "F - test-render-grapheme-stack from bottom"
231 check-ints-equal x, 3, "F - test-render-grapheme-stack from bottom: result"
232 check-background-color-in-screen-row screen, 3/bg=reverse, 0/y, " ", "F - test-render-grapheme-stack from bottom: bg"
234 var x/eax: int <- render-stack-from-top screen, gs, 0/x, 1/y, 0/cursor=false
235 check-screen-row screen, 1/y, "cba ", "F - test-render-grapheme-stack from top without cursor"
236 check-ints-equal x, 3, "F - test-render-grapheme-stack from top without cursor: result"
237 check-background-color-in-screen-row screen, 3/bg=reverse, 1/y, " ", "F - test-render-grapheme-stack from top without cursor: bg"
239 var x/eax: int <- render-stack-from-top screen, gs, 0/x, 2/y, 1/cursor=true
240 check-screen-row screen, 2/y, "cba ", "F - test-render-grapheme-stack from top with cursor"
241 check-ints-equal x, 3, "F - test-render-grapheme-stack from top with cursor: result"
242 check-background-color-in-screen-row screen, 3/bg=reverse, 2/y, "| ", "F - test-render-grapheme-stack from top with cursor: bg"
245 fn test-render-grapheme-stack-while-highlighting-matching-close-paren {
247 var gs-storage: grapheme-stack
248 var gs/edi: (addr grapheme-stack) <- address gs-storage
249 initialize-grapheme-stack gs, 5
250 var g/eax: code-point-utf8 <- copy 0x29/close-paren
251 push-grapheme-stack gs, g
253 push-grapheme-stack gs, g
254 g <- copy 0x28/open-paren
255 push-grapheme-stack gs, g
257 var screen-storage: screen
258 var screen/esi: (addr screen) <- address screen-storage
259 initialize-screen screen, 5, 4, 0/no-pixel-graphics
261 var x/eax: int <- render-stack-from-top screen, gs, 0/x, 2/y, 1/cursor=true
262 check-screen-row screen, 2/y, "(b) ", "F - test-render-grapheme-stack-while-highlighting-matching-close-paren"
263 check-background-color-in-screen-row screen, 3/bg=reverse, 2/y, "| ", "F - test-render-grapheme-stack-while-highlighting-matching-close-paren: cursor"
264 check-screen-row-in-color screen, 0xf/fg=white, 2/y, " ) ", "F - test-render-grapheme-stack-while-highlighting-matching-close-paren: matching paren"
267 fn test-render-grapheme-stack-while-highlighting-matching-close-paren-2 {
268 # setup: gs = "(a (b)) c"
269 var gs-storage: grapheme-stack
270 var gs/edi: (addr grapheme-stack) <- address gs-storage
271 initialize-grapheme-stack gs, 0x10
272 var g/eax: code-point-utf8 <- copy 0x63/c
273 push-grapheme-stack gs, g
275 push-grapheme-stack gs, g
276 g <- copy 0x29/close-paren
277 push-grapheme-stack gs, g
278 g <- copy 0x29/close-paren
279 push-grapheme-stack gs, g
281 push-grapheme-stack gs, g
282 g <- copy 0x28/open-paren
283 push-grapheme-stack gs, g
285 push-grapheme-stack gs, g
287 push-grapheme-stack gs, g
288 g <- copy 0x28/open-paren
289 push-grapheme-stack gs, g
291 var screen-storage: screen
292 var screen/esi: (addr screen) <- address screen-storage
293 initialize-screen screen, 5, 4, 0/no-pixel-graphics
295 var x/eax: int <- render-stack-from-top screen, gs, 0/x, 2/y, 1/cursor=true
296 check-screen-row screen, 2/y, "(a (b)) c ", "F - test-render-grapheme-stack-while-highlighting-matching-close-paren-2"
297 check-background-color-in-screen-row screen, 3/bg=reverse, 2/y, "| ", "F - test-render-grapheme-stack-while-highlighting-matching-close-paren-2: cursor"
298 check-screen-row-in-color screen, 0xf/fg=white, 2/y, " ) ", "F - test-render-grapheme-stack-while-highlighting-matching-close-paren-2: matching paren"
301 fn test-render-grapheme-stack-while-highlighting-matching-open-paren-with-close-paren-at-end {
303 var gs-storage: grapheme-stack
304 var gs/edi: (addr grapheme-stack) <- address gs-storage
305 initialize-grapheme-stack gs, 5
306 var g/eax: code-point-utf8 <- copy 0x28/open-paren
307 push-grapheme-stack gs, g
309 push-grapheme-stack gs, g
310 g <- copy 0x29/close-paren
311 push-grapheme-stack gs, g
313 var screen-storage: screen
314 var screen/esi: (addr screen) <- address screen-storage
315 initialize-screen screen, 5, 4, 0/no-pixel-graphics
317 var x/eax: int <- render-stack-from-bottom screen, gs, 0/x, 2/y, 1/highlight-matching-open-paren, 1/open-paren-depth
318 check-screen-row screen, 2/y, "(b) ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren-with-close-paren-at-end"
319 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"
322 fn test-render-grapheme-stack-while-highlighting-matching-open-paren-with-close-paren-at-end-2 {
323 # setup: gs = "a((b))"
324 var gs-storage: grapheme-stack
325 var gs/edi: (addr grapheme-stack) <- address gs-storage
326 initialize-grapheme-stack gs, 0x10
327 var g/eax: code-point-utf8 <- copy 0x61/a
328 push-grapheme-stack gs, g
329 g <- copy 0x28/open-paren
330 push-grapheme-stack gs, g
331 g <- copy 0x28/open-paren
332 push-grapheme-stack gs, g
334 push-grapheme-stack gs, g
335 g <- copy 0x29/close-paren
336 push-grapheme-stack gs, g
337 g <- copy 0x29/close-paren
338 push-grapheme-stack gs, g
340 var screen-storage: screen
341 var screen/esi: (addr screen) <- address screen-storage
342 initialize-screen screen, 5, 4, 0/no-pixel-graphics
344 var x/eax: int <- render-stack-from-bottom screen, gs, 0/x, 2/y, 1/highlight-matching-open-paren, 1/open-paren-depth
345 check-screen-row screen, 2/y, "a((b)) ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren-with-close-paren-at-end-2"
346 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"
349 fn test-render-grapheme-stack-while-highlighting-matching-open-paren {
351 var gs-storage: grapheme-stack
352 var gs/edi: (addr grapheme-stack) <- address gs-storage
353 initialize-grapheme-stack gs, 5
354 var g/eax: code-point-utf8 <- copy 0x28/open-paren
355 push-grapheme-stack gs, g
357 push-grapheme-stack gs, g
359 var screen-storage: screen
360 var screen/esi: (addr screen) <- address screen-storage
361 initialize-screen screen, 5, 4, 0/no-pixel-graphics
363 var x/eax: int <- render-stack-from-bottom screen, gs, 0/x, 2/y, 1/highlight-matching-open-paren, 0/open-paren-depth
364 check-screen-row screen, 2/y, "(b ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren"
365 check-screen-row-in-color screen, 0xf/fg=white, 2/y, "( ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren: matching paren"
368 fn test-render-grapheme-stack-while-highlighting-matching-open-paren-2 {
369 # setup: gs = "a((b)"
370 var gs-storage: grapheme-stack
371 var gs/edi: (addr grapheme-stack) <- address gs-storage
372 initialize-grapheme-stack gs, 0x10
373 var g/eax: code-point-utf8 <- copy 0x61/a
374 push-grapheme-stack gs, g
375 g <- copy 0x28/open-paren
376 push-grapheme-stack gs, g
377 g <- copy 0x28/open-paren
378 push-grapheme-stack gs, g
380 push-grapheme-stack gs, g
381 g <- copy 0x29/close-paren
382 push-grapheme-stack gs, g
384 var screen-storage: screen
385 var screen/esi: (addr screen) <- address screen-storage
386 initialize-screen screen, 5, 4, 0/no-pixel-graphics
388 var x/eax: int <- render-stack-from-bottom screen, gs, 0/x, 2/y, 1/highlight-matching-open-paren, 0/open-paren-depth
389 check-screen-row screen, 2/y, "a((b) ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren-2"
390 check-screen-row-in-color screen, 0xf/fg=white, 2/y, " ( ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren-2: matching paren"
393 # return the index of the matching close-paren of the code-point-utf8 at cursor (top of stack)
394 # or top index if there's no matching close-paren
395 fn get-matching-close-paren-index _self: (addr grapheme-stack), render-cursor?: boolean -> _/edx: int {
396 var self/esi: (addr grapheme-stack) <- copy _self
397 var top-addr/edx: (addr int) <- get self, top
398 # if not rendering cursor, return
399 compare render-cursor?, 0/false
404 var data-ah/eax: (addr handle array code-point-utf8) <- get self, data
405 var data/eax: (addr array code-point-utf8) <- lookup *data-ah
406 var i/ecx: int <- copy *top-addr
407 # if stack is empty, return
413 # if cursor is not '(' return
415 var g/esi: (addr code-point-utf8) <- index data, i
416 compare *g, 0x28/open-paren
421 # otherwise scan to matching paren
422 var paren-count/ebx: int <- copy 1
427 var g/esi: (addr code-point-utf8) <- index data, i
428 compare *g, 0x28/open-paren
431 paren-count <- increment
433 compare *g, 0x29/close-paren
436 compare paren-count, 1
441 paren-count <- decrement
449 # return the index of the first open-paren at the given depth
450 # or top index if there's no matching close-paren
451 fn get-matching-open-paren-index _self: (addr grapheme-stack), control: boolean, depth: int -> _/edx: int {
452 var self/esi: (addr grapheme-stack) <- copy _self
453 var top-addr/edx: (addr int) <- get self, top
454 # if not rendering cursor, return
455 compare control, 0/false
460 var data-ah/eax: (addr handle array code-point-utf8) <- get self, data
461 var data/eax: (addr array code-point-utf8) <- lookup *data-ah
462 var i/ecx: int <- copy *top-addr
463 # if stack is empty, return
469 # scan to matching open paren
470 var paren-count/ebx: int <- copy 0
475 var g/esi: (addr code-point-utf8) <- index data, i
476 compare *g, 0x29/close-paren
479 paren-count <- increment
481 compare *g, 0x28/open-paren
484 compare paren-count, depth
489 paren-count <- decrement
497 # compare from bottom
498 # beware: modifies 'stream', which must be disposed of after a false result
499 fn prefix-match? _self: (addr grapheme-stack), s: (addr stream byte) -> _/eax: boolean {
500 var self/esi: (addr grapheme-stack) <- copy _self
501 var data-ah/edi: (addr handle array code-point-utf8) <- get self, data
502 var _data/eax: (addr array code-point-utf8) <- lookup *data-ah
503 var data/edi: (addr array code-point-utf8) <- copy _data
504 var top-addr/ecx: (addr int) <- get self, top
505 var i/ebx: int <- copy 0
509 # if curr != expected, return false
511 var curr-a/edx: (addr code-point-utf8) <- index data, i
512 var expected/eax: code-point-utf8 <- read-code-point-utf8 s
514 compare expected, *curr-a
525 # compare from bottom
526 # beware: modifies 'stream', which must be disposed of after a false result
527 fn suffix-match? _self: (addr grapheme-stack), s: (addr stream byte) -> _/eax: boolean {
528 var self/esi: (addr grapheme-stack) <- copy _self
529 var data-ah/edi: (addr handle array code-point-utf8) <- get self, data
530 var _data/eax: (addr array code-point-utf8) <- lookup *data-ah
531 var data/edi: (addr array code-point-utf8) <- copy _data
532 var top-addr/eax: (addr int) <- get self, top
533 var i/ebx: int <- copy *top-addr
539 var curr-a/edx: (addr code-point-utf8) <- index data, i
540 var expected/eax: code-point-utf8 <- read-code-point-utf8 s
541 # if curr != expected, return false
543 compare expected, *curr-a
554 fn grapheme-stack-is-decimal-integer? _self: (addr grapheme-stack) -> _/eax: boolean {
555 var self/esi: (addr grapheme-stack) <- copy _self
556 var data-ah/eax: (addr handle array code-point-utf8) <- get self, data
557 var _data/eax: (addr array code-point-utf8) <- lookup *data-ah
558 var data/edx: (addr array code-point-utf8) <- copy _data
559 var top-addr/ecx: (addr int) <- get self, top
560 var i/ebx: int <- copy 0
561 var result/eax: boolean <- copy 1/true
562 $grapheme-stack-is-integer?:loop: {
565 var g/edx: (addr code-point-utf8) <- index data, i
566 result <- decimal-digit? *g
567 compare result, 0/false