https://github.com/akkartik/mu/blob/master/apps/tile/gap-buffer.mu
  1 type gap-buffer {
  2   left: grapheme-stack
  3   right: grapheme-stack
  4 }
  5 
  6 fn initialize-gap-buffer _self: (addr gap-buffer) {
  7   var self/esi: (addr gap-buffer) <- copy _self
  8   var left/eax: (addr grapheme-stack) <- get self, left
  9   initialize-grapheme-stack left, 0x10  # max-word-size
 10   var right/eax: (addr grapheme-stack) <- get self, right
 11   initialize-grapheme-stack right, 0x10  # max-word-size
 12 }
 13 
 14 # just for tests
 15 fn initialize-gap-buffer-with self: (addr gap-buffer), s: (addr array byte) {
 16   initialize-gap-buffer self
 17   var stream-storage: (stream byte 0x10)  # max-word-size
 18   var stream/ecx: (addr stream byte) <- address stream-storage
 19   write stream, s
 20   {
 21     var done?/eax: boolean <- stream-empty? stream
 22     compare done?, 0  # false
 23     break-if-!=
 24     var g/eax: grapheme <- read-grapheme stream
 25     add-grapheme-at-gap self, g
 26     loop
 27   }
 28 }
 29 
 30 fn gap-buffer-to-string self: (addr gap-buffer), out: (addr handle array byte) {
 31   var s-storage: (stream byte 0x100)
 32   var s/ecx: (addr stream byte) <- address s-storage
 33   emit-gap-buffer self, s
 34   stream-to-array s, out
 35 }
 36 
 37 fn emit-gap-buffer _self: (addr gap-buffer), out: (addr stream byte) {
 38   var self/esi: (addr gap-buffer) <- copy _self
 39   clear-stream out
 40   var left/eax: (addr grapheme-stack) <- get self, left
 41   emit-stack-from-bottom left, out
 42   var right/eax: (addr grapheme-stack) <- get self, right
 43   emit-stack-from-top right, out
 44 }
 45 
 46 # dump stack from bottom to top
 47 fn emit-stack-from-bottom _self: (addr grapheme-stack), out: (addr stream byte) {
 48   var self/esi: (addr grapheme-stack) <- copy _self
 49   var data-ah/edi: (addr handle array grapheme) <- get self, data
 50   var _data/eax: (addr array grapheme) <- lookup *data-ah
 51   var data/edi: (addr array grapheme) <- copy _data
 52   var top-addr/ecx: (addr int) <- get self, top
 53   var i/eax: int <- copy 0
 54   {
 55     compare i, *top-addr
 56     break-if->=
 57     var g/edx: (addr grapheme) <- index data, i
 58     write-grapheme out, *g
 59     i <- increment
 60     loop
 61   }
 62 }
 63 
 64 # dump stack from top to bottom
 65 fn emit-stack-from-top _self: (addr grapheme-stack), out: (addr stream byte) {
 66   var self/esi: (addr grapheme-stack) <- copy _self
 67   var data-ah/edi: (addr handle array grapheme) <- get self, data
 68   var _data/eax: (addr array grapheme) <- lookup *data-ah
 69   var data/edi: (addr array grapheme) <- copy _data
 70   var top-addr/ecx: (addr int) <- get self, top
 71   var i/eax: int <- copy *top-addr
 72   i <- decrement
 73   {
 74     compare i, 0
 75     break-if-<
 76     var g/edx: (addr grapheme) <- index data, i
 77     write-grapheme out, *g
 78     i <- decrement
 79     loop
 80   }
 81 }
 82 
 83 fn render-gap-buffer screen: (addr screen), _gap: (addr gap-buffer) {
 84   var gap/esi: (addr gap-buffer) <- copy _gap
 85   var left/eax: (addr grapheme-stack) <- get gap, left
 86   render-stack-from-bottom left, screen
 87   var right/eax: (addr grapheme-stack) <- get gap, right
 88   render-stack-from-top right, screen
 89 }
 90 
 91 fn gap-buffer-length _gap: (addr gap-buffer) -> _/eax: int {
 92   var gap/esi: (addr gap-buffer) <- copy _gap
 93   var left/eax: (addr grapheme-stack) <- get gap, left
 94   var tmp/eax: (addr int) <- get left, top
 95   var left-length/ecx: int <- copy *tmp
 96   var right/esi: (addr grapheme-stack) <- get gap, right
 97   tmp <- get right, top
 98   var result/eax: int <- copy *tmp
 99   result <- add left-length
100   return result
101 }
102 
103 fn add-grapheme-at-gap _self: (addr gap-buffer), g: grapheme {
104   var self/esi: (addr gap-buffer) <- copy _self
105   var left/eax: (addr grapheme-stack) <- get self, left
106   push-grapheme-stack left, g
107 }
108 
109 fn gap-to-start self: (addr gap-buffer) {
110   {
111     var curr/eax: grapheme <- gap-left self
112     compare curr, -1
113     loop-if-!=
114   }
115 }
116 
117 fn gap-to-end self: (addr gap-buffer) {
118   {
119     var curr/eax: grapheme <- gap-right self
120     compare curr, -1
121     loop-if-!=
122   }
123 }
124 
125 fn gap-at-start? _self: (addr gap-buffer) -> _/eax: boolean {
126   var self/esi: (addr gap-buffer) <- copy _self
127   var left/eax: (addr grapheme-stack) <- get self, left
128   var result/eax: boolean <- grapheme-stack-empty? left
129   return result
130 }
131 
132 fn gap-at-end? _self: (addr gap-buffer) -> _/eax: boolean {
133   var self/esi: (addr gap-buffer) <- copy _self
134   var right/eax: (addr grapheme-stack) <- get self, right
135   var result/eax: boolean <- grapheme-stack-empty? right
136   return result
137 }
138 
139 fn gap-right _self: (addr gap-buffer) -> _/eax: grapheme {
140   var self/esi: (addr gap-buffer) <- copy _self
141   var g/eax: grapheme <- copy 0
142   var right/ecx: (addr grapheme-stack) <- get self, right
143   g <- pop-grapheme-stack right
144   compare g, -1
145   {
146     break-if-=
147     var left/ecx: (addr grapheme-stack) <- get self, left
148     push-grapheme-stack left, g
149   }
150   return g
151 }
152 
153 fn gap-left _self: (addr gap-buffer) -> _/eax: grapheme {
154   var self/esi: (addr gap-buffer) <- copy _self
155   var g/eax: grapheme <- copy 0
156   {
157     var left/ecx: (addr grapheme-stack) <- get self, left
158     g <- pop-grapheme-stack left
159   }
160   compare g, -1
161   {
162     break-if-=
163     var right/ecx: (addr grapheme-stack) <- get self, right
164     push-grapheme-stack right, g
165   }
166   return g
167 }
168 
169 fn gap-index _self: (addr gap-buffer) -> _/eax: int {
170   var self/eax: (addr gap-buffer) <- copy _self
171   var left/eax: (addr grapheme-stack) <- get self, left
172   var top-addr/eax: (addr int) <- get left, top
173   var result/eax: int <- copy *top-addr
174   return result
175 }
176 
177 fn first-grapheme-in-gap-buffer _self: (addr gap-buffer) -> _/eax: grapheme {
178   var self/esi: (addr gap-buffer) <- copy _self
179   # try to read from left
180   var left/eax: (addr grapheme-stack) <- get self, left
181   var top-addr/ecx: (addr int) <- get left, top
182   compare *top-addr, 0
183   {
184     break-if-<=
185     var data-ah/eax: (addr handle array grapheme) <- get left, data
186     var data/eax: (addr array grapheme) <- lookup *data-ah
187     var result-addr/eax: (addr grapheme) <- index data, 0
188     return *result-addr
189   }
190   # try to read from right
191   var right/eax: (addr grapheme-stack) <- get self, right
192   top-addr <- get right, top
193   compare *top-addr, 0
194   {
195     break-if-<=
196     var data-ah/eax: (addr handle array grapheme) <- get right, data
197     var data/eax: (addr array grapheme) <- lookup *data-ah
198     var top/ecx: int <- copy *top-addr
199     top <- decrement
200     var result-addr/eax: (addr grapheme) <- index data, top
201     return *result-addr
202   }
203   # give up
204   return -1
205 }
206 
207 fn grapheme-before-cursor-in-gap-buffer _self: (addr gap-buffer) -> _/eax: grapheme {
208   var self/esi: (addr gap-buffer) <- copy _self
209   # try to read from left
210   var left/ecx: (addr grapheme-stack) <- get self, left
211   var top-addr/edx: (addr int) <- get left, top
212   compare *top-addr, 0
213   {
214     break-if-<=
215     var result/eax: grapheme <- pop-grapheme-stack left
216     push-grapheme-stack left, result
217     return result
218   }
219   # give up
220   return -1
221 }
222 
223 fn delete-before-gap _self: (addr gap-buffer) {
224   var self/eax: (addr gap-buffer) <- copy _self
225   var left/eax: (addr grapheme-stack) <- get self, left
226   var dummy/eax: grapheme <- pop-grapheme-stack left
227 }
228 
229 fn pop-after-gap _self: (addr gap-buffer) -> _/eax: grapheme {
230   var self/eax: (addr gap-buffer) <- copy _self
231   var right/eax: (addr grapheme-stack) <- get self, right
232   var result/eax: grapheme <- pop-grapheme-stack right
233   return result
234 }
235 
236 fn gap-buffer-equal? _self: (addr gap-buffer), s: (addr array byte) -> _/eax: boolean {
237   var self/esi: (addr gap-buffer) <- copy _self
238   # complication: graphemes may be multiple bytes
239   # so don't rely on length
240   # instead turn the expected result into a stream and arrange to read from it in order
241   var stream-storage: (stream byte 0x10)  # max-word-size
242   var expected-stream/ecx: (addr stream byte) <- address stream-storage
243   write expected-stream, s
244   # compare left
245   var left/edx: (addr grapheme-stack) <- get self, left
246   var result/eax: boolean <- prefix-match? left, expected-stream
247   compare result, 0  # false
248   {
249     break-if-!=
250     return result
251   }
252   # compare right
253   var right/edx: (addr grapheme-stack) <- get self, right
254   result <- suffix-match? right, expected-stream
255   compare result, 0  # false
256   {
257     break-if-!=
258     return result
259   }
260   # ensure there's nothing left over
261   result <- stream-empty? expected-stream
262   return result
263 }
264 
265 fn test-gap-buffer-equal-from-end? {
266   var _g: gap-buffer
267   var g/esi: (addr gap-buffer) <- address _g
268   initialize-gap-buffer g
269   #
270   var c/eax: grapheme <- copy 0x61  # 'a'
271   add-grapheme-at-gap g, c
272   add-grapheme-at-gap g, c
273   add-grapheme-at-gap g, c
274   # gap is at end (right is empty)
275   var _result/eax: boolean <- gap-buffer-equal? g, "aaa"
276   var result/eax: int <- copy _result
277   check-ints-equal result, 1, "F - test-gap-buffer-equal-from-end?"
278 }
279 
280 fn test-gap-buffer-equal-from-middle? {
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   var dummy/eax: grapheme <- gap-left g
290   # gap is in the middle
291   var _result/eax: boolean <- gap-buffer-equal? g, "aaa"
292   var result/eax: int <- copy _result
293   check-ints-equal result, 1, "F - test-gap-buffer-equal-from-middle?"
294 }
295 
296 fn test-gap-buffer-equal-from-start? {
297   var _g: gap-buffer
298   var g/esi: (addr gap-buffer) <- address _g
299   initialize-gap-buffer g
300   #
301   var c/eax: grapheme <- copy 0x61  # 'a'
302   add-grapheme-at-gap g, c
303   add-grapheme-at-gap g, c
304   add-grapheme-at-gap g, c
305   var dummy/eax: grapheme <- gap-left g
306   dummy <- gap-left g
307   dummy <- gap-left g
308   # gap is at the start
309   var _result/eax: boolean <- gap-buffer-equal? g, "aaa"
310   var result/eax: int <- copy _result
311   check-ints-equal result, 1, "F - test-gap-buffer-equal-from-start?"
312 }
313 
314 fn copy-gap-buffer _src-ah: (addr handle gap-buffer), _dest-ah: (addr handle gap-buffer) {
315   # obtain src-a, dest-a
316   var src-ah/eax: (addr handle gap-buffer) <- copy _src-ah
317   var _src-a/eax: (addr gap-buffer) <- lookup *src-ah
318   var src-a/esi: (addr gap-buffer) <- copy _src-a
319   var dest-ah/eax: (addr handle gap-buffer) <- copy _dest-ah
320   var _dest-a/eax: (addr gap-buffer) <- lookup *dest-ah
321   var dest-a/edi: (addr gap-buffer) <- copy _dest-a
322   # copy left grapheme-stack
323   var src/ecx: (addr grapheme-stack) <- get src-a, left
324   var dest/edx: (addr grapheme-stack) <- get dest-a, left
325   copy-grapheme-stack src, dest
326   # copy right grapheme-stack
327   src <- get src-a, right
328   dest <- get dest-a, right
329   copy-grapheme-stack src, dest
330 }
331 
332 fn gap-buffer-is-decimal-integer? _self: (addr gap-buffer) -> _/eax: boolean {
333   var self/esi: (addr gap-buffer) <- copy _self
334   var curr/ecx: (addr grapheme-stack) <- get self, left
335   var result/eax: boolean <- grapheme-stack-is-decimal-integer? curr
336   {
337     compare result, 0  # false
338     break-if-=
339     curr <- get self, right
340     result <- grapheme-stack-is-decimal-integer? curr
341   }
342   return result
343 }