https://github.com/akkartik/mu/blob/main/shell/grapheme-stack.mu
  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 # colors hardcoded
 86 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 -> _/eax: int, _/ecx: int {
 87   var self/esi: (addr grapheme-stack) <- copy _self
 88   var data-ah/edi: (addr handle array grapheme) <- get self, data
 89   var _data/eax: (addr array grapheme) <- lookup *data-ah
 90   var data/edi: (addr array grapheme) <- copy _data
 91   var x/eax: int <- copy _x
 92   var y/ecx: int <- copy _y
 93   var top-addr/edx: (addr int) <- get self, top
 94   var i/ebx: int <- copy 0
 95   {
 96     compare i, *top-addr
 97     break-if->=
 98     {
 99       var g/edx: (addr grapheme) <- index data, i
100       x, y <- render-grapheme screen, *g, xmin, ymin, xmax, ymax, x, y, 3/fg=cyan, 0/bg
101     }
102     i <- increment
103     loop
104   }
105   return x, y
106 }
107 
108 # helper for small words
109 fn render-stack-from-bottom screen: (addr screen), self: (addr grapheme-stack), x: int, y: int -> _/eax: int {
110   var _width/eax: int <- copy 0
111   var _height/ecx: int <- copy 0
112   _width, _height <- screen-size screen
113   var width/edx: int <- copy _width
114   var height/ebx: int <- copy _height
115   var x2/eax: int <- copy 0
116   var y2/ecx: int <- copy 0
117   x2, y2 <- render-stack-from-bottom-wrapping-right-then-down screen, self, x, y, width, height, x, y
118   return x2  # y2? yolo
119 }
120 
121 # dump stack to screen from top to bottom
122 # optionally render a 'cursor' with the top grapheme
123 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 -> _/eax: int, _/ecx: int {
124   var self/esi: (addr grapheme-stack) <- copy _self
125   var data-ah/edi: (addr handle array grapheme) <- get self, data
126   var _data/eax: (addr array grapheme) <- lookup *data-ah
127   var data/edi: (addr array grapheme) <- copy _data
128   var x/eax: int <- copy _x
129   var y/ecx: int <- copy _y
130   var top-addr/edx: (addr int) <- get self, top
131   var i/ebx: int <- copy *top-addr
132   i <- decrement
133   # if render-cursor?, peel off first iteration
134   {
135     compare render-cursor?, 0/false
136     break-if-=
137     compare i, 0
138     break-if-<
139     {
140       var g/edx: (addr grapheme) <- index data, i
141       x, y <- render-grapheme screen, *g, xmin, ymin, xmax, ymax, x, y, 3/fg=cyan, 7/bg=cursor
142     }
143     i <- decrement
144   }
145   # remaining iterations
146   {
147     compare i, 0
148     break-if-<
149     {
150       var g/edx: (addr grapheme) <- index data, i
151       x, y <- render-grapheme screen, *g, xmin, ymin, xmax, ymax, x, y, 3/fg=cyan, 0/bg=cursor
152     }
153     i <- decrement
154     loop
155   }
156   return x, y
157 }
158 
159 # helper for small words
160 fn render-stack-from-top screen: (addr screen), self: (addr grapheme-stack), x: int, y: int, render-cursor?: boolean -> _/eax: int {
161   var _width/eax: int <- copy 0
162   var _height/ecx: int <- copy 0
163   _width, _height <- screen-size screen
164   var width/edx: int <- copy _width
165   var height/ebx: int <- copy _height
166   var x2/eax: int <- copy 0
167   var y2/ecx: int <- copy 0
168   x2, y2 <- render-stack-from-top-wrapping-right-then-down screen, self, x, y, width, height, x, y, render-cursor?
169   return x2  # y2? yolo
170 }
171 
172 fn test-render-grapheme-stack {
173   # setup: gs = "abc"
174   var gs-storage: grapheme-stack
175   var gs/edi: (addr grapheme-stack) <- address gs-storage
176   initialize-grapheme-stack gs, 5
177   var g/eax: grapheme <- copy 0x61/a
178   push-grapheme-stack gs, g
179   g <- copy 0x62/b
180   push-grapheme-stack gs, g
181   g <- copy 0x63/c
182   push-grapheme-stack gs, g
183   # setup: screen
184   var screen-on-stack: screen
185   var screen/esi: (addr screen) <- address screen-on-stack
186   initialize-screen screen, 5, 4
187   #
188   var x/eax: int <- render-stack-from-bottom screen, gs, 0/x, 0/y
189   check-screen-row screen, 0/y, "abc ", "F - test-render-grapheme-stack from bottom"
190   check-ints-equal x, 3, "F - test-render-grapheme-stack from bottom: result"
191   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "   ", "F - test-render-grapheme-stack from bottom: bg"
192   #
193   var x/eax: int <- render-stack-from-top screen, gs, 0/x, 1/y, 0/cursor=false
194   check-screen-row screen, 1/y, "cba ", "F - test-render-grapheme-stack from top without cursor"
195   check-ints-equal x, 3, "F - test-render-grapheme-stack from top without cursor: result"
196   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "   ", "F - test-render-grapheme-stack from top without cursor: bg"
197   #
198   var x/eax: int <- render-stack-from-top screen, gs, 0/x, 2/y, 1/cursor=true
199   check-screen-row screen, 2/y, "cba ", "F - test-render-grapheme-stack from top with cursor"
200   check-ints-equal x, 3, "F - test-render-grapheme-stack from top without cursor: result"
201   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "|   ", "F - test-render-grapheme-stack from top with cursor: bg"
202 }
203 
204 # compare from bottom
205 # beware: modifies 'stream', which must be disposed of after a false result
206 fn prefix-match? _self: (addr grapheme-stack), s: (addr stream byte) -> _/eax: boolean {
207   var self/esi: (addr grapheme-stack) <- copy _self
208   var data-ah/edi: (addr handle array grapheme) <- get self, data
209   var _data/eax: (addr array grapheme) <- lookup *data-ah
210   var data/edi: (addr array grapheme) <- copy _data
211   var top-addr/ecx: (addr int) <- get self, top
212   var i/ebx: int <- copy 0
213   {
214     compare i, *top-addr
215     break-if->=
216     # if curr != expected, return false
217     {
218       var curr-a/edx: (addr grapheme) <- index data, i
219       var expected/eax: grapheme <- read-grapheme s
220       {
221         compare expected, *curr-a
222         break-if-=
223         return 0/false
224       }
225     }
226     i <- increment
227     loop
228   }
229   return 1   # true
230 }
231 
232 # compare from bottom
233 # beware: modifies 'stream', which must be disposed of after a false result
234 fn suffix-match? _self: (addr grapheme-stack), s: (addr stream byte) -> _/eax: boolean {
235   var self/esi: (addr grapheme-stack) <- copy _self
236   var data-ah/edi: (addr handle array grapheme) <- get self, data
237   var _data/eax: (addr array grapheme) <- lookup *data-ah
238   var data/edi: (addr array grapheme) <- copy _data
239   var top-addr/eax: (addr int) <- get self, top
240   var i/ebx: int <- copy *top-addr
241   i <- decrement
242   {
243     compare i, 0
244     break-if-<
245     {
246       var curr-a/edx: (addr grapheme) <- index data, i
247       var expected/eax: grapheme <- read-grapheme s
248       # if curr != expected, return false
249       {
250         compare expected, *curr-a
251         break-if-=
252         return 0/false
253       }
254     }
255     i <- decrement
256     loop
257   }
258   return 1   # true
259 }
260 
261 fn grapheme-stack-is-decimal-integer? _self: (addr grapheme-stack) -> _/eax: boolean {
262   var self/esi: (addr grapheme-stack) <- copy _self
263   var data-ah/eax: (addr handle array grapheme) <- get self, data
264   var _data/eax: (addr array grapheme) <- lookup *data-ah
265   var data/edx: (addr array grapheme) <- copy _data
266   var top-addr/ecx: (addr int) <- get self, top
267   var i/ebx: int <- copy 0
268   var result/eax: boolean <- copy 1/true
269   $grapheme-stack-is-integer?:loop: {
270     compare i, *top-addr
271     break-if->=
272     var g/edx: (addr grapheme) <- index data, i
273     result <- decimal-digit? *g
274     compare result, 0/false
275     break-if-=
276     i <- increment
277     loop
278   }
279   return result
280 }