https://github.com/akkartik/mu/blob/master/apps/tile/word.mu
  1 fn initialize-word _self: (addr word) {
  2   var self/esi: (addr word) <- copy _self
  3   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
  4   allocate data-ah
  5   var data/eax: (addr gap-buffer) <- lookup *data-ah
  6   initialize-gap-buffer data
  7   # TODO: sometimes initialize box-data rather than scalar-data
  8 }
  9 
 10 ## some helpers for creating words. mostly for tests
 11 
 12 fn initialize-word-with _self: (addr word), s: (addr array byte) {
 13   var self/esi: (addr word) <- copy _self
 14   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
 15   allocate data-ah
 16   var data/eax: (addr gap-buffer) <- lookup *data-ah
 17   initialize-gap-buffer-with data, s
 18 }
 19 
 20 fn allocate-word-with _out: (addr handle word), s: (addr array byte) {
 21   var out/eax: (addr handle word) <- copy _out
 22   allocate out
 23   var out-addr/eax: (addr word) <- lookup *out
 24   initialize-word-with out-addr, s
 25 }
 26 
 27 # just for tests for now
 28 # TODO: handle existing next
 29 # one implication of handles: append must take a handle
 30 fn append-word-with self-h: (handle word), s: (addr array byte) {
 31   var self/eax: (addr word) <- lookup self-h
 32   var next-ah/eax: (addr handle word) <- get self, next
 33   allocate-word-with next-ah, s
 34   var next/eax: (addr word) <- lookup *next-ah
 35   var prev-ah/eax: (addr handle word) <- get next, prev
 36   copy-handle self-h, prev-ah
 37 }
 38 
 39 # just for tests for now
 40 # TODO: handle existing prev
 41 fn prepend-word-with self-h: (handle word), s: (addr array byte) {
 42   var self/eax: (addr word) <- lookup self-h
 43   var prev-ah/eax: (addr handle word) <- get self, prev
 44   allocate-word-with prev-ah, s
 45   var prev/eax: (addr word) <- lookup *prev-ah
 46   var next-ah/eax: (addr handle word) <- get prev, next
 47   copy-handle self-h, next-ah
 48 }
 49 
 50 ## real primitives
 51 
 52 fn move-word-contents _src-ah: (addr handle word), _dest-ah: (addr handle word) {
 53   var dest-ah/eax: (addr handle word) <- copy _dest-ah
 54   var _dest/eax: (addr word) <- lookup *dest-ah
 55   var dest/edi: (addr word) <- copy _dest
 56   var src-ah/eax: (addr handle word) <- copy _src-ah
 57   var _src/eax: (addr word) <- lookup *src-ah
 58   var src/esi: (addr word) <- copy _src
 59   cursor-to-start src
 60   var src-data-ah/eax: (addr handle gap-buffer) <- get src, scalar-data
 61   var src-data/eax: (addr gap-buffer) <- lookup *src-data-ah
 62   var src-stack/ecx: (addr grapheme-stack) <- get src-data, right
 63   {
 64     var done?/eax: boolean <- grapheme-stack-empty? src-stack
 65     compare done?, 0  # false
 66     break-if-!=
 67     var g/eax: grapheme <- pop-grapheme-stack src-stack
 68 #?     print-grapheme 0, g
 69 #?     print-string 0, "\n"
 70     add-grapheme-to-word dest, g
 71     loop
 72   }
 73 }
 74 
 75 fn copy-word-contents-before-cursor _src-ah: (addr handle word), _dest-ah: (addr handle word) {
 76   var dest-ah/eax: (addr handle word) <- copy _dest-ah
 77   var _dest/eax: (addr word) <- lookup *dest-ah
 78   var dest/edi: (addr word) <- copy _dest
 79   var src-ah/eax: (addr handle word) <- copy _src-ah
 80   var src/eax: (addr word) <- lookup *src-ah
 81   var src-data-ah/eax: (addr handle gap-buffer) <- get src, scalar-data
 82   var src-data/eax: (addr gap-buffer) <- lookup *src-data-ah
 83   var src-stack/ecx: (addr grapheme-stack) <- get src-data, left
 84   var src-stack-data-ah/eax: (addr handle array grapheme) <- get src-stack, data
 85   var _src-stack-data/eax: (addr array grapheme) <- lookup *src-stack-data-ah
 86   var src-stack-data/edx: (addr array grapheme) <- copy _src-stack-data
 87   var top-addr/ecx: (addr int) <- get src-stack, top
 88   var i/eax: int <- copy 0
 89   {
 90     compare i, *top-addr
 91     break-if->=
 92     var g/edx: (addr grapheme) <- index src-stack-data, i
 93     add-grapheme-to-word dest, *g
 94     i <- increment
 95     loop
 96   }
 97 }
 98 
 99 fn word-equal? _self: (addr word), s: (addr array byte) -> result/eax: boolean {
100   var self/esi: (addr word) <- copy _self
101   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
102   var data/eax: (addr gap-buffer) <- lookup *data-ah
103   result <- gap-buffer-equal? data, s
104 }
105 
106 fn word-length _self: (addr word) -> result/eax: int {
107   var self/esi: (addr word) <- copy _self
108   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
109   var data/eax: (addr gap-buffer) <- lookup *data-ah
110   result <- gap-buffer-length data
111 }
112 
113 fn first-word _in: (addr handle word), out: (addr handle word) {
114   var curr-ah/esi: (addr handle word) <- copy _in
115   var curr/eax: (addr word) <- lookup *curr-ah
116   var prev/edi: (addr handle word) <- copy 0
117   {
118     prev <- get curr, prev
119     var curr/eax: (addr word) <- lookup *prev
120     compare curr, 0
121     break-if-=
122     copy-object prev, curr-ah
123     loop
124   }
125   copy-object curr-ah, out
126 }
127 
128 fn final-word _in: (addr handle word), out: (addr handle word) {
129   var curr-h: (handle word)
130   var curr-ah/esi: (addr handle word) <- address curr-h
131   copy-object _in, curr-ah
132   var curr/eax: (addr word) <- copy 0
133   var next/edi: (addr handle word) <- copy 0
134   {
135     curr <- lookup *curr-ah
136     next <- get curr, next
137     curr <- lookup *next
138     compare curr, 0
139     break-if-=
140     copy-object next, curr-ah
141     loop
142   }
143   copy-object curr-ah, out
144 }
145 
146 fn first-grapheme _self: (addr word) -> result/eax: grapheme {
147   var self/esi: (addr word) <- copy _self
148   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
149   var data/eax: (addr gap-buffer) <- lookup *data-ah
150   result <- first-grapheme-in-gap-buffer data
151 }
152 
153 fn add-grapheme-to-word _self: (addr word), c: grapheme {
154   var self/esi: (addr word) <- copy _self
155   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
156   var data/eax: (addr gap-buffer) <- lookup *data-ah
157   add-grapheme-at-gap data, c
158 }
159 
160 fn cursor-at-start? _self: (addr word) -> result/eax: boolean {
161   var self/esi: (addr word) <- copy _self
162   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
163   var data/eax: (addr gap-buffer) <- lookup *data-ah
164   result <- gap-at-start? data
165 }
166 
167 fn cursor-at-end? _self: (addr word) -> result/eax: boolean {
168   var self/esi: (addr word) <- copy _self
169   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
170   var data/eax: (addr gap-buffer) <- lookup *data-ah
171   result <- gap-at-end? data
172 }
173 
174 fn cursor-left _self: (addr word) {
175   var self/esi: (addr word) <- copy _self
176   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
177   var data/eax: (addr gap-buffer) <- lookup *data-ah
178   var dummy/eax: grapheme <- gap-left data
179 }
180 
181 fn cursor-right _self: (addr word) {
182   var self/esi: (addr word) <- copy _self
183   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
184   var data/eax: (addr gap-buffer) <- lookup *data-ah
185   var dummy/eax: grapheme <- gap-right data
186 }
187 
188 fn cursor-to-start _self: (addr word) {
189   var self/esi: (addr word) <- copy _self
190   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
191   var data/eax: (addr gap-buffer) <- lookup *data-ah
192   gap-to-start data
193 }
194 
195 fn cursor-to-end _self: (addr word) {
196   var self/esi: (addr word) <- copy _self
197   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
198   var data/eax: (addr gap-buffer) <- lookup *data-ah
199   gap-to-end data
200 }
201 
202 fn cursor-index _self: (addr word) -> result/eax: int {
203   var self/esi: (addr word) <- copy _self
204   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
205   var data/eax: (addr gap-buffer) <- lookup *data-ah
206   result <- gap-index data
207 }
208 
209 fn delete-before-cursor _self: (addr word) {
210   var self/esi: (addr word) <- copy _self
211   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
212   var data/eax: (addr gap-buffer) <- lookup *data-ah
213   delete-before-gap data
214 }
215 
216 fn delete-next _self: (addr word) {
217 $delete-next:body: {
218   var self/esi: (addr word) <- copy _self
219   var next-ah/edi: (addr handle word) <- get self, next
220   var next/eax: (addr word) <- lookup *next-ah
221   compare next, 0
222   break-if-= $delete-next:body
223   var next-next-ah/ecx: (addr handle word) <- get next, next
224   var self-ah/esi: (addr handle word) <- get next, prev
225   copy-object next-next-ah, next-ah
226   var new-next/eax: (addr word) <- lookup *next-next-ah
227   compare new-next, 0
228   break-if-= $delete-next:body
229   var dest/eax: (addr handle word) <- get new-next, prev
230   copy-object self-ah, dest
231 }
232 }
233 
234 fn print-word screen: (addr screen), _self: (addr word) {
235   var self/esi: (addr word) <- copy _self
236   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
237   var data/eax: (addr gap-buffer) <- lookup *data-ah
238   render-gap-buffer screen, data
239 }
240 
241 fn print-words screen: (addr screen), _words-ah: (addr handle word) {
242   var words-ah/eax: (addr handle word) <- copy _words-ah
243   var words-a/eax: (addr word) <- lookup *words-ah
244   compare words-a, 0
245   break-if-=
246   # print
247   print-word screen, words-a
248   print-string screen, " "
249   # recurse
250   var next-ah/eax: (addr handle word) <- get words-a, next
251   print-words screen, next-ah
252 }
253 
254 fn print-words-in-reverse screen: (addr screen), _words-ah: (addr handle word) {
255   var words-ah/eax: (addr handle word) <- copy _words-ah
256   var words-a/eax: (addr word) <- lookup *words-ah
257   compare words-a, 0
258   break-if-=
259   # recurse
260   var next-ah/ecx: (addr handle word) <- get words-a, next
261   print-words screen, next-ah
262   # print
263   print-word screen, words-a
264   print-string screen, " "
265 }
266 
267 # Gotcha with some word operations: ensure dest-ah isn't in the middle of some
268 # existing chain of words. There are two pointers to patch, and you'll forget
269 # to do the other one.
270 fn copy-words _src-ah: (addr handle word), _dest-ah: (addr handle word) {
271   var src-ah/eax: (addr handle word) <- copy _src-ah
272   var src-a/eax: (addr word) <- lookup *src-ah
273   compare src-a, 0
274   break-if-=
275   # copy
276   var dest-ah/edi: (addr handle word) <- copy _dest-ah
277   copy-word src-a, dest-ah
278   # recurse
279   var rest: (handle word)
280   var rest-ah/ecx: (addr handle word) <- address rest
281   var next-src-ah/esi: (addr handle word) <- get src-a, next
282   copy-words next-src-ah, rest-ah
283   chain-words dest-ah, rest-ah
284 }
285 
286 fn copy-words-in-reverse _src-ah: (addr handle word), _dest-ah: (addr handle word) {
287   var src-ah/eax: (addr handle word) <- copy _src-ah
288   var _src-a/eax: (addr word) <- lookup *src-ah
289   var src-a/esi: (addr word) <- copy _src-a
290   compare src-a, 0
291   break-if-=
292   # recurse
293   var next-src-ah/ecx: (addr handle word) <- get src-a, next
294   var dest-ah/edi: (addr handle word) <- copy _dest-ah
295   copy-words-in-reverse next-src-ah, dest-ah
296   #
297   copy-word-at-end src-a, dest-ah
298 }
299 
300 fn copy-word-at-end src: (addr word), _dest-ah: (addr handle word) {
301 $copy-word-at-end:body: {
302   var dest-ah/edi: (addr handle word) <- copy _dest-ah
303   # if dest is null, copy and return
304   var dest-a/eax: (addr word) <- lookup *dest-ah
305   compare dest-a, 0
306   {
307     break-if-!=
308     copy-word src, dest-ah
309     break $copy-word-at-end:body
310   }
311   # copy current word
312   var new: (handle word)
313   var new-ah/ecx: (addr handle word) <- address new
314   copy-word src, new-ah
315   # append it at the end
316   var curr-ah/edi: (addr handle word) <- copy dest-ah
317   {
318     var curr-a/eax: (addr word) <- lookup *curr-ah  # curr-a guaranteed not to be null
319     var next-ah/ecx: (addr handle word) <- get curr-a, next
320     var next-a/eax: (addr word) <- lookup *next-ah
321     compare next-a, 0
322     break-if-=
323     curr-ah <- copy next-ah
324     loop
325   }
326   chain-words curr-ah, new-ah
327 }
328 }
329 
330 fn append-word-at-end-with _dest-ah: (addr handle word), s: (addr array byte) {
331 $append-word-at-end-with:body: {
332   var dest-ah/edi: (addr handle word) <- copy _dest-ah
333   # if dest is null, copy and return
334   var dest-a/eax: (addr word) <- lookup *dest-ah
335   compare dest-a, 0
336   {
337     break-if-!=
338     allocate-word-with dest-ah, s
339     break $append-word-at-end-with:body
340   }
341   # otherwise append at end
342   var curr-ah/edi: (addr handle word) <- copy dest-ah
343   {
344     var curr-a/eax: (addr word) <- lookup *curr-ah  # curr-a guaranteed not to be null
345     var next-ah/ecx: (addr handle word) <- get curr-a, next
346     var next-a/eax: (addr word) <- lookup *next-ah
347     compare next-a, 0
348     break-if-=
349     curr-ah <- copy next-ah
350     loop
351   }
352   append-word-with *curr-ah, s
353 }
354 }
355 
356 fn copy-word _src-a: (addr word), _dest-ah: (addr handle word) {
357   var dest-ah/eax: (addr handle word) <- copy _dest-ah
358   allocate dest-ah
359   var _dest-a/eax: (addr word) <- lookup *dest-ah
360   var dest-a/eax: (addr word) <- copy _dest-a
361   initialize-word dest-a
362   var dest/edi: (addr handle gap-buffer) <- get dest-a, scalar-data
363   var src-a/eax: (addr word) <- copy _src-a
364   var src/eax: (addr handle gap-buffer) <- get src-a, scalar-data
365   copy-gap-buffer src, dest
366 }
367 
368 # one implication of handles: append must take a handle
369 fn append-word _self-ah: (addr handle word) {
370   var self-ah/esi: (addr handle word) <- copy _self-ah
371   var _self/eax: (addr word) <- lookup *self-ah
372   var self/ebx: (addr word) <- copy _self
373   # allocate new handle
374   var new: (handle word)
375   var new-ah/ecx: (addr handle word) <- address new
376   allocate new-ah
377   var new-addr/eax: (addr word) <- lookup new
378   initialize-word new-addr
379   # new->next = self->next
380   var src/esi: (addr handle word) <- get self, next
381   var dest/edi: (addr handle word) <- get new-addr, next
382   copy-object src, dest
383   # new->next->prev = new
384   {
385     var next-addr/eax: (addr word) <- lookup *src
386     compare next-addr, 0
387     break-if-=
388     dest <- get next-addr, prev
389     copy-object new-ah, dest
390   }
391   # new->prev = self
392   dest <- get new-addr, prev
393   copy-object _self-ah, dest
394   # self->next = new
395   dest <- get self, next
396   copy-object new-ah, dest
397 }
398 
399 fn chain-words _self-ah: (addr handle word), _next: (addr handle word) {
400   var self-ah/esi: (addr handle word) <- copy _self-ah
401   var _self/eax: (addr word) <- lookup *self-ah
402   var self/ecx: (addr word) <- copy _self
403   var dest/edx: (addr handle word) <- get self, next
404   var next-ah/edi: (addr handle word) <- copy _next
405   copy-object next-ah, dest
406   var next/eax: (addr word) <- lookup *next-ah
407   compare next, 0
408   break-if-=
409   dest <- get next, prev
410   copy-object self-ah, dest
411 }
412 
413 fn emit-word _self: (addr word), out: (addr stream byte) {
414   var self/esi: (addr word) <- copy _self
415   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
416   var data/eax: (addr gap-buffer) <- lookup *data-ah
417   emit-gap-buffer data, out
418 }
419 
420 fn word-to-string _self: (addr word), out: (addr handle array byte) {
421   var self/esi: (addr word) <- copy _self
422   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
423   var data/eax: (addr gap-buffer) <- lookup *data-ah
424   gap-buffer-to-string data, out
425 }
426 
427 fn word-is-decimal-integer? _self: (addr word) -> result/eax: boolean {
428   var self/eax: (addr word) <- copy _self
429   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
430   var data/eax: (addr gap-buffer) <- lookup *data-ah
431   result <- gap-buffer-is-decimal-integer? data
432 }
433 
434 # ABSOLUTELY GHASTLY
435 fn word-exists? _haystack-ah: (addr handle word), _needle: (addr word) -> result/ebx: boolean {
436   var needle-name-storage: (handle addr byte)
437   var needle-name-ah/eax: (addr handle array byte) <- address needle-name-storage
438   word-to-string _needle, needle-name-ah  # profligate leak
439   var _needle-name/eax: (addr array byte) <- lookup *needle-name-ah
440   var needle-name/edi: (addr array byte) <- copy _needle-name
441   # base case
442   result <- copy 0   # false
443   var haystack-ah/esi: (addr handle word) <- copy _haystack-ah
444   var curr/eax: (addr word) <- lookup *haystack-ah
445   compare curr, 0
446   break-if-=
447   # check curr
448   var curr-name-storage: (handle addr byte)
449   var curr-name-ah/ecx: (addr handle array byte) <- address curr-name-storage
450   word-to-string curr, curr-name-ah  # profligate leak
451   var curr-name/eax: (addr array byte) <- lookup *curr-name-ah
452   var found?/eax: boolean <- string-equal? needle-name, curr-name
453   result <- copy found?
454   compare result, 0
455   break-if-!=
456   # recurse
457   var curr/eax: (addr word) <- lookup *haystack-ah
458   var next-haystack-ah/eax: (addr handle word) <- get curr, next
459   result <- word-exists? next-haystack-ah, _needle
460 }