https://github.com/akkartik/mu/blob/main/baremetal/shell/grapheme-stack.mu
  1 # grapheme stacks are the smallest unit of editable text
  2 # they are typically rendered horizontally
  3 
  4 type grapheme-stack {
  5   data: (handle array grapheme)
  6   top: int
  7 }
  8 
  9 fn initialize-grapheme-stack _self: (addr grapheme-stack), n: int {
 10   var self/esi: (addr grapheme-stack) <- copy _self
 11   var d/edi: (addr handle array grapheme) <- get self, data
 12   populate d, n
 13   var top/eax: (addr int) <- get self, top
 14   copy-to *top, 0
 15 }
 16 
 17 fn clear-grapheme-stack _self: (addr grapheme-stack) {
 18   var self/esi: (addr grapheme-stack) <- copy _self
 19   var top/eax: (addr int) <- get self, top
 20   copy-to *top, 0
 21 }
 22 
 23 fn grapheme-stack-empty? _self: (addr grapheme-stack) -> _/eax: boolean {
 24   var self/esi: (addr grapheme-stack) <- copy _self
 25   var top/eax: (addr int) <- get self, top
 26   compare *top, 0
 27   {
 28     break-if-!=
 29     return 1/true
 30   }
 31   return 0/false
 32 }
 33 
 34 fn push-grapheme-stack _self: (addr grapheme-stack), _val: grapheme {
 35   var self/esi: (addr grapheme-stack) <- copy _self
 36   var top-addr/ecx: (addr int) <- get self, top
 37   var data-ah/edx: (addr handle array grapheme) <- get self, data
 38   var data/eax: (addr array grapheme) <- lookup *data-ah
 39   var top/edx: int <- copy *top-addr
 40   var dest-addr/edx: (addr grapheme) <- index data, top
 41   var val/eax: grapheme <- copy _val
 42   copy-to *dest-addr, val
 43   add-to *top-addr, 1
 44 }
 45 
 46 fn pop-grapheme-stack _self: (addr grapheme-stack) -> _/eax: grapheme {
 47   var self/esi: (addr grapheme-stack) <- copy _self
 48   var top-addr/ecx: (addr int) <- get self, top
 49   {
 50     compare *top-addr, 0
 51     break-if->
 52     return -1
 53   }
 54   subtract-from *top-addr, 1
 55   var data-ah/edx: (addr handle array grapheme) <- get self, data
 56   var data/eax: (addr array grapheme) <- lookup *data-ah
 57   var top/edx: int <- copy *top-addr
 58   var result-addr/eax: (addr grapheme) <- index data, top
 59   return *result-addr
 60 }
 61 
 62 fn copy-grapheme-stack _src: (addr grapheme-stack), dest: (addr grapheme-stack) {
 63   var src/esi: (addr grapheme-stack) <- copy _src
 64   var data-ah/edi: (addr handle array grapheme) <- get src, data
 65   var _data/eax: (addr array grapheme) <- lookup *data-ah
 66   var data/edi: (addr array grapheme) <- copy _data
 67   var top-addr/ecx: (addr int) <- get src, top
 68   var i/eax: int <- copy 0
 69   {
 70     compare i, *top-addr
 71     break-if->=
 72     var g/edx: (addr grapheme) <- index data, i
 73     push-grapheme-stack dest, *g
 74     i <- increment
 75     loop
 76   }
 77 }
 78 
 79 # dump stack to screen from bottom to top
 80 # colors hardcoded
 81 fn render-stack-from-bottom screen: (addr screen), _self: (addr grapheme-stack), x: int, y: int -> _/eax: int {
 82   var self/esi: (addr grapheme-stack) <- copy _self
 83   var data-ah/edi: (addr handle array grapheme) <- get self, data
 84   var _data/eax: (addr array grapheme) <- lookup *data-ah
 85   var data/edi: (addr array grapheme) <- copy _data
 86   var top-addr/ecx: (addr int) <- get self, top
 87   var i/eax: int <- copy 0
 88   {
 89     compare i, *top-addr
 90     break-if->=
 91     var g/edx: (addr grapheme) <- index data, i
 92     draw-grapheme screen, *g, x, y, 3/fg=cyan, 0/bg
 93     i <- increment
 94     increment x  # assume left to right
 95     loop
 96   }
 97   return x
 98 }
 99 
100 # dump stack to screen from top to bottom
101 # optionally render a 'cursor' with the top grapheme
102 fn render-stack-from-top screen: (addr screen), _self: (addr grapheme-stack), x: int, y: int, render-cursor?: boolean -> _/eax: int {
103   var self/esi: (addr grapheme-stack) <- copy _self
104   var data-ah/edi: (addr handle array grapheme) <- get self, data
105   var _data/eax: (addr array grapheme) <- lookup *data-ah
106   var data/edi: (addr array grapheme) <- copy _data
107   var top-addr/ecx: (addr int) <- get self, top
108   var i/eax: int <- copy *top-addr
109   i <- decrement
110   # if render-cursor?, peel off first iteration
111   {
112     compare render-cursor?, 0/false
113     break-if-=
114     compare i, 0
115     break-if-<
116     var g/edx: (addr grapheme) <- index data, i
117     draw-grapheme screen, *g, x, y, 3/fg=cyan, 7/bg=cursor
118     i <- decrement
119     increment x  # assume left to right
120   }
121   # remaining iterations
122   {
123     compare i, 0
124     break-if-<
125     var g/edx: (addr grapheme) <- index data, i
126     draw-grapheme screen, *g, x, y, 3/fg=cyan, 0/bg
127     i <- decrement
128     increment x  # assume left to right
129     loop
130   }
131   return x
132 }
133 
134 fn test-render-grapheme-stack {
135   # setup: gs = "abc"
136   var gs-storage: grapheme-stack
137   var gs/edi: (addr grapheme-stack) <- address gs-storage
138   initialize-grapheme-stack gs, 5
139   var g/eax: grapheme <- copy 0x61/a
140   push-grapheme-stack gs, g
141   g <- copy 0x62/b
142   push-grapheme-stack gs, g
143   g <- copy 0x63/c
144   push-grapheme-stack gs, g
145   # setup: screen
146   var screen-on-stack: screen
147   var screen/esi: (addr screen) <- address screen-on-stack
148   initialize-screen screen, 5, 4
149   #
150   var x/eax: int <- render-stack-from-bottom screen, gs, 0/x, 0/y
151   check-screen-row screen, 0/y, "abc ", "F - test-render-grapheme-stack from bottom"
152   check-ints-equal x, 3, "F - test-render-grapheme-stack from bottom: result"
153   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "   ", "F - test-render-grapheme-stack from bottom: bg"
154   #
155   var x/eax: int <- render-stack-from-top screen, gs, 0/x, 1/y, 0/cursor=false
156   check-screen-row screen, 1/y, "cba ", "F - test-render-grapheme-stack from top without cursor"
157   check-ints-equal x, 3, "F - test-render-grapheme-stack from top without cursor: result"
158   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "   ", "F - test-render-grapheme-stack from top without cursor: bg"
159   #
160   var x/eax: int <- render-stack-from-top screen, gs, 0/x, 2/y, 1/cursor=true
161   check-screen-row screen, 2/y, "cba ", "F - test-render-grapheme-stack from top with cursor"
162   check-ints-equal x, 3, "F - test-render-grapheme-stack from top without cursor: result"
163   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "|   ", "F - test-render-grapheme-stack from top with cursor: bg"
164 }
165 
166 # compare from bottom
167 # beware: modifies 'stream', which must be disposed of after a false result
168 fn prefix-match? _self: (addr grapheme-stack), s: (addr stream byte) -> _/eax: boolean {
169   var self/esi: (addr grapheme-stack) <- copy _self
170   var data-ah/edi: (addr handle array grapheme) <- get self, data
171   var _data/eax: (addr array grapheme) <- lookup *data-ah
172   var data/edi: (addr array grapheme) <- copy _data
173   var top-addr/ecx: (addr int) <- get self, top
174   var i/ebx: int <- copy 0
175   {
176     compare i, *top-addr
177     break-if->=
178     # if curr != expected, return false
179     {
180       var curr-a/edx: (addr grapheme) <- index data, i
181       var expected/eax: grapheme <- read-grapheme s
182       {
183         compare expected, *curr-a
184         break-if-=
185         return 0/false
186       }
187     }
188     i <- increment
189     loop
190   }
191   return 1   # true
192 }
193 
194 # compare from bottom
195 # beware: modifies 'stream', which must be disposed of after a false result
196 fn suffix-match? _self: (addr grapheme-stack), s: (addr stream byte) -> _/eax: boolean {
197   var self/esi: (addr grapheme-stack) <- copy _self
198   var data-ah/edi: (addr handle array grapheme) <- get self, data
199   var _data/eax: (addr array grapheme) <- lookup *data-ah
200   var data/edi: (addr array grapheme) <- copy _data
201   var top-addr/eax: (addr int) <- get self, top
202   var i/ebx: int <- copy *top-addr
203   i <- decrement
204   {
205     compare i, 0
206     break-if-<
207     {
208       var curr-a/edx: (addr grapheme) <- index data, i
209       var expected/eax: grapheme <- read-grapheme s
210       # if curr != expected, return false
211       {
212         compare expected, *curr-a
213         break-if-=
214         return 0/false
215       }
216     }
217     i <- decrement
218     loop
219   }
220   return 1   # true
221 }
222 
223 fn grapheme-stack-is-decimal-integer? _self: (addr grapheme-stack) -> _/eax: boolean {
224   var self/esi: (addr grapheme-stack) <- copy _self
225   var data-ah/eax: (addr handle array grapheme) <- get self, data
226   var _data/eax: (addr array grapheme) <- lookup *data-ah
227   var data/edx: (addr array grapheme) <- copy _data
228   var top-addr/ecx: (addr int) <- get self, top
229   var i/ebx: int <- copy 0
230   var result/eax: boolean <- copy 1/true
231   $grapheme-stack-is-integer?:loop: {
232     compare i, *top-addr
233     break-if->=
234     var g/edx: (addr grapheme) <- index data, i
235     result <- is-decimal-digit? *g
236     compare result, 0/false
237     break-if-=
238     i <- increment
239     loop
240   }
241   return result
242 }