https://github.com/akkartik/mu/blob/main/baremetal/shell/gap-buffer.mu
1
2
3
4 type gap-buffer {
5 left: grapheme-stack
6 right: grapheme-stack
7 }
8
9 fn initialize-gap-buffer _self: (addr gap-buffer) {
10 var self/esi: (addr gap-buffer) <- copy _self
11 var left/eax: (addr grapheme-stack) <- get self, left
12 initialize-grapheme-stack left, 0x10/max-word-size
13 var right/eax: (addr grapheme-stack) <- get self, right
14 initialize-grapheme-stack right, 0x10/max-word-size
15 }
16
17
18 fn initialize-gap-buffer-with self: (addr gap-buffer), s: (addr array byte) {
19 initialize-gap-buffer self
20 var stream-storage: (stream byte 0x10/max-word-size)
21 var stream/ecx: (addr stream byte) <- address stream-storage
22 write stream, s
23 {
24 var done?/eax: boolean <- stream-empty? stream
25 compare done?, 0/false
26 break-if-!=
27 var g/eax: grapheme <- read-grapheme stream
28 add-grapheme-at-gap self, g
29 loop
30 }
31 }
32
33 fn emit-gap-buffer _self: (addr gap-buffer), out: (addr stream byte) {
34 var self/esi: (addr gap-buffer) <- copy _self
35 clear-stream out
36 var left/eax: (addr grapheme-stack) <- get self, left
37 emit-stack-from-bottom left, out
38 var right/eax: (addr grapheme-stack) <- get self, right
39 emit-stack-from-top right, out
40 }
41
42
43 fn emit-stack-from-bottom _self: (addr grapheme-stack), out: (addr stream byte) {
44 var self/esi: (addr grapheme-stack) <- copy _self
45 var data-ah/edi: (addr handle array grapheme) <- get self, data
46 var _data/eax: (addr array grapheme) <- lookup *data-ah
47 var data/edi: (addr array grapheme) <- copy _data
48 var top-addr/ecx: (addr int) <- get self, top
49 var i/eax: int <- copy 0
50 {
51 compare i, *top-addr
52 break-if->=
53 var g/edx: (addr grapheme) <- index data, i
54 write-grapheme out, *g
55 i <- increment
56 loop
57 }
58 }
59
60
61 fn emit-stack-from-top _self: (addr grapheme-stack), out: (addr stream byte) {
62 var self/esi: (addr grapheme-stack) <- copy _self
63 var data-ah/edi: (addr handle array grapheme) <- get self, data
64 var _data/eax: (addr array grapheme) <- lookup *data-ah
65 var data/edi: (addr array grapheme) <- copy _data
66 var top-addr/ecx: (addr int) <- get self, top
67 var i/eax: int <- copy *top-addr
68 i <- decrement
69 {
70 compare i, 0
71 break-if-<
72 var g/edx: (addr grapheme) <- index data, i
73 write-grapheme out, *g
74 i <- decrement
75 loop
76 }
77 }
78
79
80
81 fn render-gap-buffer screen: (addr screen), _gap: (addr gap-buffer), x: int, y: int, render-cursor?: boolean -> _/eax: int {
82 var gap/esi: (addr gap-buffer) <- copy _gap
83 var left/eax: (addr grapheme-stack) <- get gap, left
84 var x2/eax: int <- render-stack-from-bottom screen, left, x, y
85 var right/ecx: (addr grapheme-stack) <- get gap, right
86 x2 <- render-stack-from-top screen, right, x2, y, render-cursor?
87 var x3/ebx: int <- copy x2
88
89 var bg/edx: int <- copy 0
90 compare render-cursor?, 0/false
91 {
92 break-if-=
93
94 var empty?/eax: boolean <- grapheme-stack-empty? right
95 compare empty?, 0/false
96 break-if-=
97 bg <- copy 7/cursor
98 }
99
100 var space/eax: grapheme <- copy 0x20
101 draw-grapheme screen, space, x3, y, 3/fg=cyan, bg
102 x3 <- increment
103 return x3
104 }
105
106 fn gap-buffer-length _gap: (addr gap-buffer) -> _/eax: int {
107 var gap/esi: (addr gap-buffer) <- copy _gap
108 var left/eax: (addr grapheme-stack) <- get gap, left
109 var tmp/eax: (addr int) <- get left, top
110 var left-length/ecx: int <- copy *tmp
111 var right/esi: (addr grapheme-stack) <- get gap, right
112 tmp <- get right, top
113 var result/eax: int <- copy *tmp
114 result <- add left-length
115 return result
116 }
117
118 fn add-grapheme-at-gap _self: (addr gap-buffer), g: grapheme {
119 var self/esi: (addr gap-buffer) <- copy _self
120 var left/eax: (addr grapheme-stack) <- get self, left
121 push-grapheme-stack left, g
122 }
123
124 fn gap-to-start self: (addr gap-buffer) {
125 {
126 var curr/eax: grapheme <- gap-left self
127 compare curr, -1
128 loop-if-!=
129 }
130 }
131
132 fn gap-to-end self: (addr gap-buffer) {
133 {
134 var curr/eax: grapheme <- gap-right self
135 compare curr, -1
136 loop-if-!=
137 }
138 }
139
140 fn gap-at-start? _self: (addr gap-buffer) -> _/eax: boolean {
141 var self/esi: (addr gap-buffer) <- copy _self
142 var left/eax: (addr grapheme-stack) <- get self, left
143 var result/eax: boolean <- grapheme-stack-empty? left
144 return result
145 }
146
147 fn gap-at-end? _self: (addr gap-buffer) -> _/eax: boolean {
148 var self/esi: (addr gap-buffer) <- copy _self
149 var right/eax: (addr grapheme-stack) <- get self, right
150 var result/eax: boolean <- grapheme-stack-empty? right
151 return result
152 }
153
154 fn gap-right _self: (addr gap-buffer) -> _/eax: grapheme {
155 var self/esi: (addr gap-buffer) <- copy _self
156 var g/eax: grapheme <- copy 0
157 var right/ecx: (addr grapheme-stack) <- get self, right
158 g <- pop-grapheme-stack right
159 compare g, -1
160 {
161 break-if-=
162 var left/ecx: (addr grapheme-stack) <- get self, left
163 push-grapheme-stack left, g
164 }
165 return g
166 }
167
168 fn gap-left _self: (addr gap-buffer) -> _/eax: grapheme {
169 var self/esi: (addr gap-buffer) <- copy _self
170 var g/eax: grapheme <- copy 0
171 {
172 var left/ecx: (addr grapheme-stack) <- get self, left
173 g <- pop-grapheme-stack left
174 }
175 compare g, -1
176 {
177 break-if-=
178 var right/ecx: (addr grapheme-stack) <- get self, right
179 push-grapheme-stack right, g
180 }
181 return g
182 }
183
184 fn index-of-gap _self: (addr gap-buffer) -> _/eax: int {
185 var self/eax: (addr gap-buffer) <- copy _self
186 var left/eax: (addr grapheme-stack) <- get self, left
187 var top-addr/eax: (addr int) <- get left, top
188 var result/eax: int <- copy *top-addr
189 return result
190 }
191
192 fn first-grapheme-in-gap-buffer _self: (addr gap-buffer) -> _/eax: grapheme {
193 var self/esi: (addr gap-buffer) <- copy _self
194
195 var left/eax: (addr grapheme-stack) <- get self, left
196 var top-addr/ecx: (addr int) <- get left, top
197 compare *top-addr, 0
198 {
199 break-if-<=
200 var data-ah/eax: (addr handle array grapheme) <- get left, data
201 var data/eax: (addr array grapheme) <- lookup *data-ah
202 var result-addr/eax: (addr grapheme) <- index data, 0
203 return *result-addr
204 }
205
206 var right/eax: (addr grapheme-stack) <- get self, right
207 top-addr <- get right, top
208 compare *top-addr, 0
209 {
210 break-if-<=
211 var data-ah/eax: (addr handle array grapheme) <- get right, data
212 var data/eax: (addr array grapheme) <- lookup *data-ah
213 var top/ecx: int <- copy *top-addr
214 top <- decrement
215 var result-addr/eax: (addr grapheme) <- index data, top
216 return *result-addr
217 }
218
219 return -1
220 }
221
222 fn grapheme-before-cursor-in-gap-buffer _self: (addr gap-buffer) -> _/eax: grapheme {
223 var self/esi: (addr gap-buffer) <- copy _self
224
225 var left/ecx: (addr grapheme-stack) <- get self, left
226 var top-addr/edx: (addr int) <- get left, top
227 compare *top-addr, 0
228 {
229 break-if-<=
230 var result/eax: grapheme <- pop-grapheme-stack left
231 push-grapheme-stack left, result
232 return result
233 }
234
235 return -1
236 }
237
238 fn delete-before-gap _self: (addr gap-buffer) {
239 var self/eax: (addr gap-buffer) <- copy _self
240 var left/eax: (addr grapheme-stack) <- get self, left
241 var dummy/eax: grapheme <- pop-grapheme-stack left
242 }
243
244 fn pop-after-gap _self: (addr gap-buffer) -> _/eax: grapheme {
245 var self/eax: (addr gap-buffer) <- copy _self
246 var right/eax: (addr grapheme-stack) <- get self, right
247 var result/eax: grapheme <- pop-grapheme-stack right
248 return result
249 }
250
251 fn gap-buffer-equal? _self: (addr gap-buffer), s: (addr array byte) -> _/eax: boolean {
252 var self/esi: (addr gap-buffer) <- copy _self
253
254
255
256 var stream-storage: (stream byte 0x10/max-word-size)
257 var expected-stream/ecx: (addr stream byte) <- address stream-storage
258 write expected-stream, s
259
260 var left/edx: (addr grapheme-stack) <- get self, left
261 var result/eax: boolean <- prefix-match? left, expected-stream
262 compare result, 0/false
263 {
264 break-if-!=
265 return result
266 }
267
268 var right/edx: (addr grapheme-stack) <- get self, right
269 result <- suffix-match? right, expected-stream
270 compare result, 0/false
271 {
272 break-if-!=
273 return result
274 }
275
276 result <- stream-empty? expected-stream
277 return result
278 }
279
280 fn test-gap-buffer-equal-from-end {
281 var _g: gap-buffer
282 var g/esi: (addr gap-buffer) <- address _g
283 initialize-gap-buffer g
284
285 var c/eax: grapheme <- copy 0x61/a
286 add-grapheme-at-gap g, c
287 add-grapheme-at-gap g, c
288 add-grapheme-at-gap g, c
289
290 var result/eax: boolean <- gap-buffer-equal? g, "aaa"
291 check result, "F - test-gap-buffer-equal-from-end"
292 }
293
294 fn test-gap-buffer-equal-from-middle {
295 var _g: gap-buffer
296 var g/esi: (addr gap-buffer) <- address _g
297 initialize-gap-buffer g
298
299 var c/eax: grapheme <- copy 0x61/a
300 add-grapheme-at-gap g, c
301 add-grapheme-at-gap g, c
302 add-grapheme-at-gap g, c
303 var dummy/eax: grapheme <- gap-left g
304
305 var result/eax: boolean <- gap-buffer-equal? g, "aaa"
306 check result, "F - test-gap-buffer-equal-from-middle"
307 }
308
309 fn test-gap-buffer-equal-from-start {
310 var _g: gap-buffer
311 var g/esi: (addr gap-buffer) <- address _g
312 initialize-gap-buffer g
313
314 var c/eax: grapheme <- copy 0x61/a
315 add-grapheme-at-gap g, c
316 add-grapheme-at-gap g, c
317 add-grapheme-at-gap g, c
318 var dummy/eax: grapheme <- gap-left g
319 dummy <- gap-left g
320 dummy <- gap-left g
321
322 var result/eax: boolean <- gap-buffer-equal? g, "aaa"
323 check result, "F - test-gap-buffer-equal-from-start"
324 }
325
326 fn test-gap-buffer-equal-fails {
327
328 var _g: gap-buffer
329 var g/esi: (addr gap-buffer) <- address _g
330 initialize-gap-buffer g
331 var c/eax: grapheme <- copy 0x61/a
332 add-grapheme-at-gap g, c
333 add-grapheme-at-gap g, c
334 add-grapheme-at-gap g, c
335
336 var result/eax: boolean <- gap-buffer-equal? g, "aa"
337 check-not result, "F - test-gap-buffer-equal-fails"
338 }
339
340 fn gap-buffers-equal? self: (addr gap-buffer), g: (addr gap-buffer) -> _/eax: boolean {
341 var tmp/eax: int <- gap-buffer-length self
342 var len/ecx: int <- copy tmp
343 var leng/eax: int <- gap-buffer-length g
344 compare len, leng
345 {
346 break-if-=
347 return 0/false
348 }
349 var i/edx: int <- copy 0
350 {
351 compare i, len
352 break-if->=
353 {
354 var tmp/eax: grapheme <- gap-index self, i
355 var curr/ecx: grapheme <- copy tmp
356 var currg/eax: grapheme <- gap-index g, i
357 compare curr, currg
358 break-if-=
359 return 0/false
360 }
361 i <- increment
362 loop
363 }
364 return 1/true
365 }
366
367 fn gap-index _self: (addr gap-buffer), _n: int -> _/eax: grapheme {
368 var self/esi: (addr gap-buffer) <- copy _self
369 var n/ebx: int <- copy _n
370
371 var left/edi: (addr grapheme-stack) <- get self, left
372 var left-len-a/edx: (addr int) <- get left, top
373 compare n, *left-len-a
374 {
375 break-if->=
376 var data-ah/eax: (addr handle array grapheme) <- get left, data
377 var data/eax: (addr array grapheme) <- lookup *data-ah
378 var result/eax: (addr grapheme) <- index data, n
379 return *result
380 }
381
382 n <- subtract *left-len-a
383
384 var right/edi: (addr grapheme-stack) <- get self, right
385 var right-len-a/edx: (addr int) <- get right, top
386 compare n, *right-len-a
387 {
388 break-if->=
389 var data-ah/eax: (addr handle array grapheme) <- get right, data
390 var data/eax: (addr array grapheme) <- lookup *data-ah
391 var result/eax: (addr grapheme) <- index data, n
392 return *result
393 }
394
395 abort "gap-index: out of bounds"
396 return 0
397 }
398
399 fn test-gap-buffers-equal? {
400 var _a: gap-buffer
401 var a/esi: (addr gap-buffer) <- address _a
402 initialize-gap-buffer-with a, "abc"
403 var _b: gap-buffer
404 var b/edi: (addr gap-buffer) <- address _b
405 initialize-gap-buffer-with b, "abc"
406 var _c: gap-buffer
407 var c/ebx: (addr gap-buffer) <- address _c
408 initialize-gap-buffer-with c, "ab"
409 var _d: gap-buffer
410 var d/edx: (addr gap-buffer) <- address _d
411 initialize-gap-buffer-with d, "abd"
412
413 var result/eax: boolean <- gap-buffers-equal? a, a
414 check result, "F - test-gap-buffers-equal? - reflexive"
415 result <- gap-buffers-equal? a, b
416 check result, "F - test-gap-buffers-equal? - equal"
417
418 result <- gap-buffers-equal? a, c
419 check-not result, "F - test-gap-buffers-equal? - not equal"
420
421 result <- gap-buffers-equal? a, d
422 check-not result, "F - test-gap-buffers-equal? - not equal 2"
423 result <- gap-buffers-equal? d, a
424 check-not result, "F - test-gap-buffers-equal? - not equal 3"
425 }
426
427 fn copy-gap-buffer _src-ah: (addr handle gap-buffer), _dest-ah: (addr handle gap-buffer) {
428
429 var src-ah/eax: (addr handle gap-buffer) <- copy _src-ah
430 var _src-a/eax: (addr gap-buffer) <- lookup *src-ah
431 var src-a/esi: (addr gap-buffer) <- copy _src-a
432 var dest-ah/eax: (addr handle gap-buffer) <- copy _dest-ah
433 var _dest-a/eax: (addr gap-buffer) <- lookup *dest-ah
434 var dest-a/edi: (addr gap-buffer) <- copy _dest-a
435
436 var src/ecx: (addr grapheme-stack) <- get src-a, left
437 var dest/edx: (addr grapheme-stack) <- get dest-a, left
438 copy-grapheme-stack src, dest
439
440 src <- get src-a, right
441 dest <- get dest-a, right
442 copy-grapheme-stack src, dest
443 }
444
445 fn gap-buffer-is-decimal-integer? _self: (addr gap-buffer) -> _/eax: boolean {
446 var self/esi: (addr gap-buffer) <- copy _self
447 var curr/ecx: (addr grapheme-stack) <- get self, left
448 var result/eax: boolean <- grapheme-stack-is-decimal-integer? curr
449 {
450 compare result, 0/false
451 break-if-=
452 curr <- get self, right
453 result <- grapheme-stack-is-decimal-integer? curr
454 }
455 return result
456 }
457
458 fn test-render-gap-buffer-without-cursor {
459
460 var gap-storage: gap-buffer
461 var gap/esi: (addr gap-buffer) <- address gap-storage
462 initialize-gap-buffer-with gap, "abc"
463
464 var screen-on-stack: screen
465 var screen/edi: (addr screen) <- address screen-on-stack
466 initialize-screen screen, 5, 4
467
468 var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 0/no-cursor
469 check-screen-row screen, 0/y, "abc ", "F - test-render-gap-buffer-without-cursor"
470 check-ints-equal x, 4, "F - test-render-gap-buffer-without-cursor: result"
471
472 check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " ", "F - test-render-gap-buffer-without-cursor: bg"
473 }
474
475 fn test-render-gap-buffer-with-cursor-at-end {
476
477 var gap-storage: gap-buffer
478 var gap/esi: (addr gap-buffer) <- address gap-storage
479 initialize-gap-buffer-with gap, "abc"
480 gap-to-end gap
481
482 var screen-on-stack: screen
483 var screen/edi: (addr screen) <- address screen-on-stack
484 initialize-screen screen, 5, 4
485
486 var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor
487 check-screen-row screen, 0/y, "abc ", "F - test-render-gap-buffer-with-cursor-at-end"
488
489 check-ints-equal x, 4, "F - test-render-gap-buffer-with-cursor-at-end: result"
490
491 check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " |", "F - test-render-gap-buffer-with-cursor-at-end: bg"
492 }
493
494 fn test-render-gap-buffer-with-cursor-in-middle {
495
496 var gap-storage: gap-buffer
497 var gap/esi: (addr gap-buffer) <- address gap-storage
498 initialize-gap-buffer-with gap, "abc"
499 gap-to-end gap
500 var dummy/eax: grapheme <- gap-left gap
501
502 var screen-on-stack: screen
503 var screen/edi: (addr screen) <- address screen-on-stack
504 initialize-screen screen, 5, 4
505
506 var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor
507 check-screen-row screen, 0/y, "abc ", "F - test-render-gap-buffer-with-cursor-in-middle"
508 check-ints-equal x, 4, "F - test-render-gap-buffer-with-cursor-in-middle: result"
509
510 check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " | ", "F - test-render-gap-buffer-with-cursor-in-middle: bg"
511 }
512
513 fn test-render-gap-buffer-with-cursor-at-start {
514 var gap-storage: gap-buffer
515 var gap/esi: (addr gap-buffer) <- address gap-storage
516 initialize-gap-buffer-with gap, "abc"
517 gap-to-start gap
518
519 var screen-on-stack: screen
520 var screen/edi: (addr screen) <- address screen-on-stack
521 initialize-screen screen, 5, 4
522
523 var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor
524 check-screen-row screen, 0/y, "abc ", "F - test-render-gap-buffer-with-cursor-at-start"
525 check-ints-equal x, 4, "F - test-render-gap-buffer-with-cursor-at-start: result"
526
527 check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "| ", "F - test-render-gap-buffer-with-cursor-at-start: bg"
528 }