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) -> _/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   var result/eax: boolean <- gap-buffer-equal? data, s
104   return result
105 }
106 
107 fn word-length _self: (addr word) -> _/eax: int {
108   var self/esi: (addr word) <- copy _self
109   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
110   var data/eax: (addr gap-buffer) <- lookup *data-ah
111   var result/eax: int <- gap-buffer-length data
112   return result
113 }
114 
115 fn first-word _in: (addr handle word), out: (addr handle word) {
116   var curr-ah/esi: (addr handle word) <- copy _in
117   var curr/eax: (addr word) <- lookup *curr-ah
118   var prev/edi: (addr handle word) <- copy 0
119   {
120     prev <- get curr, prev
121     var curr/eax: (addr word) <- lookup *prev
122     compare curr, 0
123     break-if-=
124     copy-object prev, curr-ah
125     loop
126   }
127   copy-object curr-ah, out
128 }
129 
130 fn final-word _in: (addr handle word), out: (addr handle word) {
131   var curr-h: (handle word)
132   var curr-ah/esi: (addr handle word) <- address curr-h
133   copy-object _in, curr-ah
134   var curr/eax: (addr word) <- copy 0
135   var next/edi: (addr handle word) <- copy 0
136   {
137     curr <- lookup *curr-ah
138     next <- get curr, next
139     curr <- lookup *next
140     compare curr, 0
141     break-if-=
142     copy-object next, curr-ah
143     loop
144   }
145   copy-object curr-ah, out
146 }
147 
148 fn first-grapheme _self: (addr word) -> _/eax: grapheme {
149   var self/esi: (addr word) <- copy _self
150   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
151   var data/eax: (addr gap-buffer) <- lookup *data-ah
152   var result/eax: grapheme <- first-grapheme-in-gap-buffer data
153   return result
154 }
155 
156 fn grapheme-before-cursor _self: (addr word) -> _/eax: grapheme {
157   var self/esi: (addr word) <- copy _self
158   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
159   var data/eax: (addr gap-buffer) <- lookup *data-ah
160   var result/eax: grapheme <- grapheme-before-cursor-in-gap-buffer data
161   return result
162 }
163 
164 fn add-grapheme-to-word _self: (addr word), c: grapheme {
165   var self/esi: (addr word) <- copy _self
166   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
167   var data/eax: (addr gap-buffer) <- lookup *data-ah
168   add-grapheme-at-gap data, c
169 }
170 
171 fn cursor-at-start? _self: (addr word) -> _/eax: boolean {
172   var self/esi: (addr word) <- copy _self
173   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
174   var data/eax: (addr gap-buffer) <- lookup *data-ah
175   var result/eax: boolean <- gap-at-start? data
176   return result
177 }
178 
179 fn cursor-at-end? _self: (addr word) -> _/eax: boolean {
180   var self/esi: (addr word) <- copy _self
181   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
182   var data/eax: (addr gap-buffer) <- lookup *data-ah
183   var result/eax: boolean <- gap-at-end? data
184   return result
185 }
186 
187 fn cursor-left _self: (addr word) {
188   var self/esi: (addr word) <- copy _self
189   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
190   var data/eax: (addr gap-buffer) <- lookup *data-ah
191   var dummy/eax: grapheme <- gap-left data
192 }
193 
194 fn cursor-right _self: (addr word) {
195   var self/esi: (addr word) <- copy _self
196   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
197   var data/eax: (addr gap-buffer) <- lookup *data-ah
198   var dummy/eax: grapheme <- gap-right data
199 }
200 
201 fn cursor-to-start _self: (addr word) {
202   var self/esi: (addr word) <- copy _self
203   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
204   var data/eax: (addr gap-buffer) <- lookup *data-ah
205   gap-to-start data
206 }
207 
208 fn cursor-to-end _self: (addr word) {
209   var self/esi: (addr word) <- copy _self
210   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
211   var data/eax: (addr gap-buffer) <- lookup *data-ah
212   gap-to-end data
213 }
214 
215 fn cursor-index _self: (addr word) -> _/eax: int {
216   var self/esi: (addr word) <- copy _self
217   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
218   var data/eax: (addr gap-buffer) <- lookup *data-ah
219   var result/eax: int <- gap-index data
220   return result
221 }
222 
223 fn delete-before-cursor _self: (addr word) {
224   var self/esi: (addr word) <- copy _self
225   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
226   var data/eax: (addr gap-buffer) <- lookup *data-ah
227   delete-before-gap data
228 }
229 
230 fn pop-after-cursor _self: (addr word) -> _/eax: grapheme {
231   var self/esi: (addr word) <- copy _self
232   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
233   var data/eax: (addr gap-buffer) <- lookup *data-ah
234   var result/eax: grapheme <- pop-after-gap data
235   return result
236 }
237 
238 fn delete-next _self: (addr word) {
239   var self/esi: (addr word) <- copy _self
240   var next-ah/edi: (addr handle word) <- get self, next
241   var next/eax: (addr word) <- lookup *next-ah
242   compare next, 0
243   break-if-=
244   var next-next-ah/ecx: (addr handle word) <- get next, next
245   var self-ah/esi: (addr handle word) <- get next, prev
246   copy-object next-next-ah, next-ah
247   var new-next/eax: (addr word) <- lookup *next-next-ah
248   compare new-next, 0
249   break-if-=
250   var dest/eax: (addr handle word) <- get new-next, prev
251   copy-object self-ah, dest
252 }
253 
254 fn print-word screen: (addr screen), _self: (addr word) {
255   var self/esi: (addr word) <- copy _self
256   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
257   var data/eax: (addr gap-buffer) <- lookup *data-ah
258   render-gap-buffer screen, data
259 }
260 
261 fn print-words screen: (addr screen), _words-ah: (addr handle word) {
262   var words-ah/eax: (addr handle word) <- copy _words-ah
263   var words-a/eax: (addr word) <- lookup *words-ah
264   compare words-a, 0
265   break-if-=
266   # print
267   print-word screen, words-a
268   print-string screen, " "
269   # recurse
270   var next-ah/eax: (addr handle word) <- get words-a, next
271   print-words screen, next-ah
272 }
273 
274 fn print-words-in-reverse screen: (addr screen), _words-ah: (addr handle word) {
275   var words-ah/eax: (addr handle word) <- copy _words-ah
276   var words-a/eax: (addr word) <- lookup *words-ah
277   compare words-a, 0
278   break-if-=
279   # recurse
280   var next-ah/ecx: (addr handle word) <- get words-a, next
281   print-words screen, next-ah
282   # print
283   print-word screen, words-a
284   print-string screen, " "
285 }
286 
287 # Gotcha with some word operations: ensure dest-ah isn't in the middle of some
288 # existing chain of words. There are two pointers to patch, and you'll forget
289 # to do the other one.
290 fn copy-words _src-ah: (addr handle word), _dest-ah: (addr handle word) {
291   var src-ah/eax: (addr handle word) <- copy _src-ah
292   var src-a/eax: (addr word) <- lookup *src-ah
293   compare src-a, 0
294   break-if-=
295   # copy
296   var dest-ah/edi: (addr handle word) <- copy _dest-ah
297   copy-word src-a, dest-ah
298   # recurse
299   var rest: (handle word)
300   var rest-ah/ecx: (addr handle word) <- address rest
301   var next-src-ah/esi: (addr handle word) <- get src-a, next
302   copy-words next-src-ah, rest-ah
303   chain-words dest-ah, rest-ah
304 }
305 
306 fn copy-words-in-reverse _src-ah: (addr handle word), _dest-ah: (addr handle word) {
307   var src-ah/eax: (addr handle word) <- copy _src-ah
308   var _src-a/eax: (addr word) <- lookup *src-ah
309   var src-a/esi: (addr word) <- copy _src-a
310   compare src-a, 0
311   break-if-=
312   # recurse
313   var next-src-ah/ecx: (addr handle word) <- get src-a, next
314   var dest-ah/edi: (addr handle word) <- copy _dest-ah
315   copy-words-in-reverse next-src-ah, dest-ah
316   #
317   copy-word-at-end src-a, dest-ah
318 }
319 
320 fn copy-word-at-end src: (addr word), _dest-ah: (addr handle word) {
321   var dest-ah/edi: (addr handle word) <- copy _dest-ah
322   # if dest is null, copy and return
323   var dest-a/eax: (addr word) <- lookup *dest-ah
324   compare dest-a, 0
325   {
326     break-if-!=
327     copy-word src, dest-ah
328     return
329   }
330   # copy current word
331   var new: (handle word)
332   var new-ah/ecx: (addr handle word) <- address new
333   copy-word src, new-ah
334   # append it at the end
335   var curr-ah/edi: (addr handle word) <- copy dest-ah
336   {
337     var curr-a/eax: (addr word) <- lookup *curr-ah  # curr-a guaranteed not to be null
338     var next-ah/ecx: (addr handle word) <- get curr-a, next
339     var next-a/eax: (addr word) <- lookup *next-ah
340     compare next-a, 0
341     break-if-=
342     curr-ah <- copy next-ah
343     loop
344   }
345   chain-words curr-ah, new-ah
346 }
347 
348 fn append-word-at-end-with _dest-ah: (addr handle word), s: (addr array byte) {
349   var dest-ah/edi: (addr handle word) <- copy _dest-ah
350   # if dest is null, copy and return
351   var dest-a/eax: (addr word) <- lookup *dest-ah
352   compare dest-a, 0
353   {
354     break-if-!=
355     allocate-word-with dest-ah, s
356     return
357   }
358   # otherwise append at end
359   var curr-ah/edi: (addr handle word) <- copy dest-ah
360   {
361     var curr-a/eax: (addr word) <- lookup *curr-ah  # curr-a guaranteed not to be null
362     var next-ah/ecx: (addr handle word) <- get curr-a, next
363     var next-a/eax: (addr word) <- lookup *next-ah
364     compare next-a, 0
365     break-if-=
366     curr-ah <- copy next-ah
367     loop
368   }
369   append-word-with *curr-ah, s
370 }
371 
372 fn copy-word _src-a: (addr word), _dest-ah: (addr handle word) {
373   var dest-ah/eax: (addr handle word) <- copy _dest-ah
374   allocate dest-ah
375   var _dest-a/eax: (addr word) <- lookup *dest-ah
376   var dest-a/eax: (addr word) <- copy _dest-a
377   initialize-word dest-a
378   var dest/edi: (addr handle gap-buffer) <- get dest-a, scalar-data
379   var src-a/eax: (addr word) <- copy _src-a
380   var src/eax: (addr handle gap-buffer) <- get src-a, scalar-data
381   copy-gap-buffer src, dest
382 }
383 
384 # one implication of handles: append must take a handle
385 fn append-word _self-ah: (addr handle word) {
386   var saved-self-storage: (handle word)
387   var saved-self/eax: (addr handle word) <- address saved-self-storage
388   copy-object _self-ah, saved-self
389 #?   {
390 #?     print-string 0, "self-ah is "
391 #?     var foo/eax: int <- copy _self-ah
392 #?     print-int32-hex 0, foo
393 #?     print-string 0, "\n"
394 #?   }
395   var self-ah/esi: (addr handle word) <- copy _self-ah
396   var _self/eax: (addr word) <- lookup *self-ah
397   var self/ebx: (addr word) <- copy _self
398 #?   {
399 #?     print-string 0, "0: self is "
400 #?     var self-ah/eax: (addr handle word) <- copy _self-ah
401 #?     var self/eax: (addr word) <- lookup *self-ah
402 #?     var foo/eax: int <- copy self
403 #?     print-int32-hex 0, foo
404 #?     print-string 0, "\n"
405 #?   }
406   # allocate new handle
407   var new: (handle word)
408   var new-ah/ecx: (addr handle word) <- address new
409   allocate new-ah
410   var new-addr/eax: (addr word) <- lookup new
411   initialize-word new-addr
412 #?   {
413 #?     print-string 0, "new is "
414 #?     var foo/eax: int <- copy new-addr
415 #?     print-int32-hex 0, foo
416 #?     print-string 0, "\n"
417 #?   }
418   # new->next = self->next
419   var src/esi: (addr handle word) <- get self, next
420 #?   {
421 #?     print-string 0, "src is "
422 #?     var foo/eax: int <- copy src
423 #?     print-int32-hex 0, foo
424 #?     print-string 0, "\n"
425 #?   }
426   var dest/edi: (addr handle word) <- get new-addr, next
427   copy-object src, dest
428   # new->next->prev = new
429   {
430     var next-addr/eax: (addr word) <- lookup *src
431     compare next-addr, 0
432     break-if-=
433 #?     {
434 #?       print-string 0, "next-addr is "
435 #?       var foo/eax: int <- copy next-addr
436 #?       print-int32-hex 0, foo
437 #?       print-string 0, "\n"
438 #?     }
439     dest <- get next-addr, prev
440 #? #?     {
441 #? #?       print-string 0, "self-ah is "
442 #? #?       var foo/eax: int <- copy _self-ah
443 #? #?       print-int32-hex 0, foo
444 #? #?       print-string 0, "\n"
445 #? #?       print-string 0, "2: self is "
446 #? #?       var self-ah/eax: (addr handle word) <- copy _self-ah
447 #? #?       var self/eax: (addr word) <- lookup *self-ah
448 #? #?       var foo/eax: int <- copy self
449 #? #?       print-int32-hex 0, foo
450 #? #?       print-string 0, "\n"
451 #? #?     }
452 #?     {
453 #?       print-string 0, "copying new to "
454 #?       var foo/eax: int <- copy dest
455 #?       print-int32-hex 0, foo
456 #?       print-string 0, "\n"
457 #?     }
458     copy-object new-ah, dest
459 #?     {
460 #?       print-string 0, "4: self is "
461 #?       var self-ah/eax: (addr handle word) <- copy _self-ah
462 #?       var self/eax: (addr word) <- lookup *self-ah
463 #?       var foo/eax: int <- copy self
464 #?       print-int32-hex 0, foo
465 #?       print-string 0, "\n"
466 #?     }
467   }
468   # new->prev = saved-self
469   dest <- get new-addr, prev
470 #?   {
471 #?     print-string 0, "copying "
472 #?     var self-ah/esi: (addr handle word) <- copy _self-ah
473 #?     var self/eax: (addr word) <- lookup *self-ah
474 #?     var foo/eax: int <- copy self
475 #?     print-int32-hex 0, foo
476 #?     print-string 0, " to "
477 #?     foo <- copy dest
478 #?     print-int32-hex 0, foo
479 #?     print-string 0, "\n"
480 #?   }
481   var saved-self-ah/eax: (addr handle word) <- address saved-self-storage
482   copy-object saved-self-ah, dest
483   # self->next = new
484   dest <- get self, next
485   copy-object new-ah, dest
486 }
487 
488 fn chain-words _self-ah: (addr handle word), _next: (addr handle word) {
489   var self-ah/esi: (addr handle word) <- copy _self-ah
490   var _self/eax: (addr word) <- lookup *self-ah
491   var self/ecx: (addr word) <- copy _self
492   var dest/edx: (addr handle word) <- get self, next
493   var next-ah/edi: (addr handle word) <- copy _next
494   copy-object next-ah, dest
495   var next/eax: (addr word) <- lookup *next-ah
496   compare next, 0
497   break-if-=
498   dest <- get next, prev
499   copy-object self-ah, dest
500 }
501 
502 fn emit-word _self: (addr word), out: (addr stream byte) {
503   var self/esi: (addr word) <- copy _self
504   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
505   var data/eax: (addr gap-buffer) <- lookup *data-ah
506   emit-gap-buffer data, out
507 }
508 
509 fn word-to-string _self: (addr word), out: (addr handle array byte) {
510   var self/esi: (addr word) <- copy _self
511   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
512   var data/eax: (addr gap-buffer) <- lookup *data-ah
513   gap-buffer-to-string data, out
514 }
515 
516 fn word-is-decimal-integer? _self: (addr word) -> _/eax: boolean {
517   var self/eax: (addr word) <- copy _self
518   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
519   var data/eax: (addr gap-buffer) <- lookup *data-ah
520   var result/eax: boolean <- gap-buffer-is-decimal-integer? data
521   return result
522 }
523 
524 # ABSOLUTELY GHASTLY
525 fn word-exists? _haystack-ah: (addr handle word), _needle: (addr word) -> _/ebx: boolean {
526   var needle-name-storage: (handle addr byte)
527   var needle-name-ah/eax: (addr handle array byte) <- address needle-name-storage
528   word-to-string _needle, needle-name-ah  # profligate leak
529   var _needle-name/eax: (addr array byte) <- lookup *needle-name-ah
530   var needle-name/edi: (addr array byte) <- copy _needle-name
531   # base case
532   var haystack-ah/esi: (addr handle word) <- copy _haystack-ah
533   var curr/eax: (addr word) <- lookup *haystack-ah
534   compare curr, 0
535   {
536     break-if-!=
537     return 0  # false
538   }
539   # check curr
540   var curr-name-storage: (handle addr byte)
541   var curr-name-ah/ecx: (addr handle array byte) <- address curr-name-storage
542   word-to-string curr, curr-name-ah  # profligate leak
543   var curr-name/eax: (addr array byte) <- lookup *curr-name-ah
544   var found?/eax: boolean <- string-equal? needle-name, curr-name
545   compare found?, 0
546   {
547     break-if-=
548     return 1  # true
549   }
550   # recurse
551   var curr/eax: (addr word) <- lookup *haystack-ah
552   var next-haystack-ah/eax: (addr handle word) <- get curr, next
553   var result/ebx: boolean <- word-exists? next-haystack-ah, _needle
554   return result
555 }