https://github.com/akkartik/mu/blob/main/linux/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 }
  8 
  9 ## some helpers for creating words. mostly for tests
 10 
 11 fn initialize-word-with _self: (addr word), s: (addr array byte) {
 12   var self/esi: (addr word) <- copy _self
 13   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
 14   allocate data-ah
 15   var data/eax: (addr gap-buffer) <- lookup *data-ah
 16   initialize-gap-buffer-with data, s
 17 }
 18 
 19 fn allocate-word-with _out: (addr handle word), s: (addr array byte) {
 20   var out/eax: (addr handle word) <- copy _out
 21   allocate out
 22   var out-addr/eax: (addr word) <- lookup *out
 23   initialize-word-with out-addr, s
 24 }
 25 
 26 # just for tests for now
 27 # TODO: handle existing next
 28 # one implication of handles: append must take a handle
 29 fn append-word-with self-h: (handle word), s: (addr array byte) {
 30   var self/eax: (addr word) <- lookup self-h
 31   var next-ah/eax: (addr handle word) <- get self, next
 32   allocate-word-with next-ah, s
 33   var next/eax: (addr word) <- lookup *next-ah
 34   var prev-ah/eax: (addr handle word) <- get next, prev
 35   copy-handle self-h, prev-ah
 36 }
 37 
 38 # just for tests for now
 39 # TODO: handle existing prev
 40 fn prepend-word-with self-h: (handle word), s: (addr array byte) {
 41   var self/eax: (addr word) <- lookup self-h
 42   var prev-ah/eax: (addr handle word) <- get self, prev
 43   allocate-word-with prev-ah, s
 44   var prev/eax: (addr word) <- lookup *prev-ah
 45   var next-ah/eax: (addr handle word) <- get prev, next
 46   copy-handle self-h, next-ah
 47 }
 48 
 49 ## real primitives
 50 
 51 fn move-word-contents _src-ah: (addr handle word), _dest-ah: (addr handle word) {
 52   var dest-ah/eax: (addr handle word) <- copy _dest-ah
 53   var _dest/eax: (addr word) <- lookup *dest-ah
 54   var dest/edi: (addr word) <- copy _dest
 55   var src-ah/eax: (addr handle word) <- copy _src-ah
 56   var _src/eax: (addr word) <- lookup *src-ah
 57   var src/esi: (addr word) <- copy _src
 58   cursor-to-start src
 59   var src-data-ah/eax: (addr handle gap-buffer) <- get src, scalar-data
 60   var src-data/eax: (addr gap-buffer) <- lookup *src-data-ah
 61   var src-stack/ecx: (addr grapheme-stack) <- get src-data, right
 62   {
 63     var done?/eax: boolean <- grapheme-stack-empty? src-stack
 64     compare done?, 0/false
 65     break-if-!=
 66     var g/eax: grapheme <- pop-grapheme-stack src-stack
 67 #?     print-grapheme 0, g
 68 #?     print-string 0, "\n"
 69     add-grapheme-to-word dest, g
 70     loop
 71   }
 72 }
 73 
 74 fn copy-word-contents-before-cursor _src-ah: (addr handle word), _dest-ah: (addr handle word) {
 75   var dest-ah/eax: (addr handle word) <- copy _dest-ah
 76   var _dest/eax: (addr word) <- lookup *dest-ah
 77   var dest/edi: (addr word) <- copy _dest
 78   var src-ah/eax: (addr handle word) <- copy _src-ah
 79   var src/eax: (addr word) <- lookup *src-ah
 80   var src-data-ah/eax: (addr handle gap-buffer) <- get src, scalar-data
 81   var src-data/eax: (addr gap-buffer) <- lookup *src-data-ah
 82   var src-stack/ecx: (addr grapheme-stack) <- get src-data, left
 83   var src-stack-data-ah/eax: (addr handle array grapheme) <- get src-stack, data
 84   var _src-stack-data/eax: (addr array grapheme) <- lookup *src-stack-data-ah
 85   var src-stack-data/edx: (addr array grapheme) <- copy _src-stack-data
 86   var top-addr/ecx: (addr int) <- get src-stack, top
 87   var i/eax: int <- copy 0
 88   {
 89     compare i, *top-addr
 90     break-if->=
 91     var g/edx: (addr grapheme) <- index src-stack-data, i
 92     add-grapheme-to-word dest, *g
 93     i <- increment
 94     loop
 95   }
 96 }
 97 
 98 fn word-equal? _self: (addr word), s: (addr array byte) -> _/eax: boolean {
 99   var self/esi: (addr word) <- copy _self
100   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
101   var data/eax: (addr gap-buffer) <- lookup *data-ah
102   var result/eax: boolean <- gap-buffer-equal? data, s
103   return result
104 }
105 
106 fn word-length _self: (addr word) -> _/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   var result/eax: int <- gap-buffer-length data
111   return result
112 }
113 
114 fn final-word _in: (addr handle word), out: (addr handle word) {
115   var curr-h: (handle word)
116   var curr-ah/esi: (addr handle word) <- address curr-h
117   copy-object _in, curr-ah
118   var curr/eax: (addr word) <- copy 0
119   var next/edi: (addr handle word) <- copy 0
120   {
121     curr <- lookup *curr-ah
122     next <- get curr, next
123     curr <- lookup *next
124     compare curr, 0
125     break-if-=
126     copy-object next, curr-ah
127     loop
128   }
129   copy-object curr-ah, out  # modify 'out' right at the end, just in case it's same as 'in'
130 }
131 
132 fn first-grapheme _self: (addr word) -> _/eax: grapheme {
133   var self/esi: (addr word) <- copy _self
134   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
135   var data/eax: (addr gap-buffer) <- lookup *data-ah
136   var result/eax: grapheme <- first-grapheme-in-gap-buffer data
137   return result
138 }
139 
140 fn grapheme-before-cursor _self: (addr word) -> _/eax: grapheme {
141   var self/esi: (addr word) <- copy _self
142   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
143   var data/eax: (addr gap-buffer) <- lookup *data-ah
144   var result/eax: grapheme <- grapheme-before-cursor-in-gap-buffer data
145   return result
146 }
147 
148 fn add-grapheme-to-word _self: (addr word), c: 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   add-grapheme-at-gap data, c
153 }
154 
155 fn cursor-at-start? _self: (addr word) -> _/eax: boolean {
156   var self/esi: (addr word) <- copy _self
157   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
158   var data/eax: (addr gap-buffer) <- lookup *data-ah
159   var result/eax: boolean <- gap-at-start? data
160   return result
161 }
162 
163 fn cursor-at-end? _self: (addr word) -> _/eax: boolean {
164   var self/esi: (addr word) <- copy _self
165   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
166   var data/eax: (addr gap-buffer) <- lookup *data-ah
167   var result/eax: boolean <- gap-at-end? data
168   return result
169 }
170 
171 fn cursor-left _self: (addr word) {
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 dummy/eax: grapheme <- gap-left data
176 }
177 
178 fn cursor-right _self: (addr word) {
179   var self/esi: (addr word) <- copy _self
180   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
181   var data/eax: (addr gap-buffer) <- lookup *data-ah
182   var dummy/eax: grapheme <- gap-right data
183 }
184 
185 fn cursor-to-start _self: (addr word) {
186   var self/esi: (addr word) <- copy _self
187   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
188   var data/eax: (addr gap-buffer) <- lookup *data-ah
189   gap-to-start data
190 }
191 
192 fn cursor-to-end _self: (addr word) {
193   var self/esi: (addr word) <- copy _self
194   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
195   var data/eax: (addr gap-buffer) <- lookup *data-ah
196   gap-to-end data
197 }
198 
199 fn cursor-index _self: (addr word) -> _/eax: int {
200   var self/esi: (addr word) <- copy _self
201   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
202   var data/eax: (addr gap-buffer) <- lookup *data-ah
203   var result/eax: int <- gap-index data
204   return result
205 }
206 
207 fn delete-before-cursor _self: (addr word) {
208   var self/esi: (addr word) <- copy _self
209   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
210   var data/eax: (addr gap-buffer) <- lookup *data-ah
211   delete-before-gap data
212 }
213 
214 fn pop-after-cursor _self: (addr word) -> _/eax: grapheme {
215   var self/esi: (addr word) <- copy _self
216   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
217   var data/eax: (addr gap-buffer) <- lookup *data-ah
218   var result/eax: grapheme <- pop-after-gap data
219   return result
220 }
221 
222 fn delete-next _self: (addr word) {
223   var self/esi: (addr word) <- copy _self
224   var next-ah/edi: (addr handle word) <- get self, next
225   var next/eax: (addr word) <- lookup *next-ah
226   compare next, 0
227   break-if-=
228   var next-next-ah/ecx: (addr handle word) <- get next, next
229   var self-ah/esi: (addr handle word) <- get next, prev
230   copy-object next-next-ah, next-ah
231   var new-next/eax: (addr word) <- lookup *next-next-ah
232   compare new-next, 0
233   break-if-=
234   var dest/eax: (addr handle word) <- get new-next, prev
235   copy-object self-ah, dest
236 }
237 
238 fn print-word screen: (addr screen), _self: (addr word) {
239   var self/esi: (addr word) <- copy _self
240   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
241   var data/eax: (addr gap-buffer) <- lookup *data-ah
242   render-gap-buffer screen, data
243 }
244 
245 fn print-words-in-reverse screen: (addr screen), _words-ah: (addr handle word) {
246   var words-ah/eax: (addr handle word) <- copy _words-ah
247   var words-a/eax: (addr word) <- lookup *words-ah
248   compare words-a, 0
249   break-if-=
250   # recurse
251   var next-ah/ecx: (addr handle word) <- get words-a, next
252   print-words-in-reverse screen, next-ah
253   # print
254   print-word screen, words-a
255   print-string screen, " "
256 }
257 
258 # Gotcha with some word operations: ensure dest-ah isn't in the middle of some
259 # existing chain of words. There are two pointers to patch, and you'll forget
260 # to do the other one.
261 fn copy-words _src-ah: (addr handle word), _dest-ah: (addr handle word) {
262   var src-ah/eax: (addr handle word) <- copy _src-ah
263   var src-a/eax: (addr word) <- lookup *src-ah
264   compare src-a, 0
265   break-if-=
266   # copy
267   var dest-ah/edi: (addr handle word) <- copy _dest-ah
268   copy-word src-a, dest-ah
269   # recurse
270   var rest: (handle word)
271   var rest-ah/ecx: (addr handle word) <- address rest
272   var next-src-ah/esi: (addr handle word) <- get src-a, next
273   copy-words next-src-ah, rest-ah
274   chain-words dest-ah, rest-ah
275 }
276 
277 fn copy-words-in-reverse _src-ah: (addr handle word), _dest-ah: (addr handle word) {
278   var src-ah/eax: (addr handle word) <- copy _src-ah
279   var _src-a/eax: (addr word) <- lookup *src-ah
280   var src-a/esi: (addr word) <- copy _src-a
281   compare src-a, 0
282   break-if-=
283   # recurse
284   var next-src-ah/ecx: (addr handle word) <- get src-a, next
285   var dest-ah/edi: (addr handle word) <- copy _dest-ah
286   copy-words-in-reverse next-src-ah, dest-ah
287   #
288   copy-word-at-end src-a, dest-ah
289 }
290 
291 fn copy-word-at-end src: (addr word), _dest-ah: (addr handle word) {
292   var dest-ah/edi: (addr handle word) <- copy _dest-ah
293   # if dest is null, copy and return
294   var dest-a/eax: (addr word) <- lookup *dest-ah
295   compare dest-a, 0
296   {
297     break-if-!=
298     copy-word src, dest-ah
299     return
300   }
301   # copy current word
302   var new: (handle word)
303   var new-ah/ecx: (addr handle word) <- address new
304   copy-word src, new-ah
305   # append it at the end
306   var curr-ah/edi: (addr handle word) <- copy dest-ah
307   {
308     var curr-a/eax: (addr word) <- lookup *curr-ah  # curr-a guaranteed not to be null
309     var next-ah/ecx: (addr handle word) <- get curr-a, next
310     var next-a/eax: (addr word) <- lookup *next-ah
311     compare next-a, 0
312     break-if-=
313     curr-ah <- copy next-ah
314     loop
315   }
316   chain-words curr-ah, new-ah
317 }
318 
319 fn append-word-at-end-with _dest-ah: (addr handle word), s: (addr array byte) {
320   var dest-ah/edi: (addr handle word) <- copy _dest-ah
321   # if dest is null, copy and return
322   var dest-a/eax: (addr word) <- lookup *dest-ah
323   compare dest-a, 0
324   {
325     break-if-!=
326     allocate-word-with dest-ah, s
327     return
328   }
329   # otherwise append at end
330   var curr-ah/edi: (addr handle word) <- copy dest-ah
331   {
332     var curr-a/eax: (addr word) <- lookup *curr-ah  # curr-a guaranteed not to be null
333     var next-ah/ecx: (addr handle word) <- get curr-a, next
334     var next-a/eax: (addr word) <- lookup *next-ah
335     compare next-a, 0
336     break-if-=
337     curr-ah <- copy next-ah
338     loop
339   }
340   append-word-with *curr-ah, s
341 }
342 
343 fn copy-word _src-a: (addr word), _dest-ah: (addr handle word) {
344   var dest-ah/eax: (addr handle word) <- copy _dest-ah
345   allocate dest-ah
346   var _dest-a/eax: (addr word) <- lookup *dest-ah
347   var dest-a/eax: (addr word) <- copy _dest-a
348   initialize-word dest-a
349   var dest/edi: (addr handle gap-buffer) <- get dest-a, scalar-data
350   var src-a/eax: (addr word) <- copy _src-a
351   var src/eax: (addr handle gap-buffer) <- get src-a, scalar-data
352   copy-gap-buffer src, dest
353 }
354 
355 # one implication of handles: append must take a handle
356 fn append-word _self-ah: (addr handle word) {
357   var saved-self-storage: (handle word)
358   var saved-self/eax: (addr handle word) <- address saved-self-storage
359   copy-object _self-ah, saved-self
360 #?   {
361 #?     print-string 0, "self-ah is "
362 #?     var foo/eax: int <- copy _self-ah
363 #?     print-int32-hex 0, foo
364 #?     print-string 0, "\n"
365 #?   }
366   var self-ah/esi: (addr handle word) <- copy _self-ah
367   var _self/eax: (addr word) <- lookup *self-ah
368   var self/ebx: (addr word) <- copy _self
369 #?   {
370 #?     print-string 0, "0: self is "
371 #?     var self-ah/eax: (addr handle word) <- copy _self-ah
372 #?     var self/eax: (addr word) <- lookup *self-ah
373 #?     var foo/eax: int <- copy self
374 #?     print-int32-hex 0, foo
375 #?     print-string 0, "\n"
376 #?   }
377   # allocate new handle
378   var new: (handle word)
379   var new-ah/ecx: (addr handle word) <- address new
380   allocate new-ah
381   var new-addr/eax: (addr word) <- lookup new
382   initialize-word new-addr
383 #?   {
384 #?     print-string 0, "new is "
385 #?     var foo/eax: int <- copy new-addr
386 #?     print-int32-hex 0, foo
387 #?     print-string 0, "\n"
388 #?   }
389   # new->next = self->next
390   var src/esi: (addr handle word) <- get self, next
391 #?   {
392 #?     print-string 0, "src is "
393 #?     var foo/eax: int <- copy src
394 #?     print-int32-hex 0, foo
395 #?     print-string 0, "\n"
396 #?   }
397   var dest/edi: (addr handle word) <- get new-addr, next
398   copy-object src, dest
399   # new->next->prev = new
400   {
401     var next-addr/eax: (addr word) <- lookup *src
402     compare next-addr, 0
403     break-if-=
404 #?     {
405 #?       print-string 0, "next-addr is "
406 #?       var foo/eax: int <- copy next-addr
407 #?       print-int32-hex 0, foo
408 #?       print-string 0, "\n"
409 #?     }
410     dest <- get next-addr, prev
411 #? #?     {
412 #? #?       print-string 0, "self-ah is "
413 #? #?       var foo/eax: int <- copy _self-ah
414 #? #?       print-int32-hex 0, foo
415 #? #?       print-string 0, "\n"
416 #? #?       print-string 0, "2: self is "
417 #? #?       var self-ah/eax: (addr handle word) <- copy _self-ah
418 #? #?       var self/eax: (addr word) <- lookup *self-ah
419 #? #?       var foo/eax: int <- copy self
420 #? #?       print-int32-hex 0, foo
421 #? #?       print-string 0, "\n"
422 #? #?     }
423 #?     {
424 #?       print-string 0, "copying new to "
425 #?       var foo/eax: int <- copy dest
426 #?       print-int32-hex 0, foo
427 #?       print-string 0, "\n"
428 #?     }
429     copy-object new-ah, dest
430 #?     {
431 #?       print-string 0, "4: self is "
432 #?       var self-ah/eax: (addr handle word) <- copy _self-ah
433 #?       var self/eax: (addr word) <- lookup *self-ah
434 #?       var foo/eax: int <- copy self
435 #?       print-int32-hex 0, foo
436 #?       print-string 0, "\n"
437 #?     }
438   }
439   # new->prev = saved-self
440   dest <- get new-addr, prev
441 #?   {
442 #?     print-string 0, "copying "
443 #?     var self-ah/esi: (addr handle word) <- copy _self-ah
444 #?     var self/eax: (addr word) <- lookup *self-ah
445 #?     var foo/eax: int <- copy self
446 #?     print-int32-hex 0, foo
447 #?     print-string 0, " to "
448 #?     foo <- copy dest
449 #?     print-int32-hex 0, foo
450 #?     print-string 0, "\n"
451 #?   }
452   var saved-self-ah/eax: (addr handle word) <- address saved-self-storage
453   copy-object saved-self-ah, dest
454   # self->next = new
455   dest <- get self, next
456   copy-object new-ah, dest
457 }
458 
459 fn chain-words _self-ah: (addr handle word), _next: (addr handle word) {
460   var self-ah/esi: (addr handle word) <- copy _self-ah
461   var _self/eax: (addr word) <- lookup *self-ah
462   var self/ecx: (addr word) <- copy _self
463   var dest/edx: (addr handle word) <- get self, next
464   var next-ah/edi: (addr handle word) <- copy _next
465   copy-object next-ah, dest
466   var next/eax: (addr word) <- lookup *next-ah
467   compare next, 0
468   break-if-=
469   dest <- get next, prev
470   copy-object self-ah, dest
471 }
472 
473 fn emit-word _self: (addr word), out: (addr stream byte) {
474   var self/esi: (addr word) <- copy _self
475   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
476   var data/eax: (addr gap-buffer) <- lookup *data-ah
477   emit-gap-buffer data, out
478 }
479 
480 fn word-to-string _self: (addr word), out: (addr handle array byte) {
481   var self/esi: (addr word) <- copy _self
482   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
483   var data/eax: (addr gap-buffer) <- lookup *data-ah
484   gap-buffer-to-string data, out
485 }
486 
487 fn word-is-decimal-integer? _self: (addr word) -> _/eax: boolean {
488   var self/eax: (addr word) <- copy _self
489   var data-ah/eax: (addr handle gap-buffer) <- get self, scalar-data
490   var data/eax: (addr gap-buffer) <- lookup *data-ah
491   var result/eax: boolean <- gap-buffer-is-decimal-integer? data
492   return result
493 }
494 
495 # ABSOLUTELY GHASTLY
496 fn word-exists? _haystack-ah: (addr handle word), _needle: (addr word) -> _/ebx: boolean {
497   var needle-name-storage: (handle array byte)
498   var needle-name-ah/eax: (addr handle array byte) <- address needle-name-storage
499   word-to-string _needle, needle-name-ah  # profligate leak
500   var _needle-name/eax: (addr array byte) <- lookup *needle-name-ah
501   var needle-name/edi: (addr array byte) <- copy _needle-name
502   # base case
503   var haystack-ah/esi: (addr handle word) <- copy _haystack-ah
504   var curr/eax: (addr word) <- lookup *haystack-ah
505   compare curr, 0
506   {
507     break-if-!=
508     return 0/false
509   }
510   # check curr
511   var curr-name-storage: (handle array byte)
512   var curr-name-ah/ecx: (addr handle array byte) <- address curr-name-storage
513   word-to-string curr, curr-name-ah  # profligate leak
514   var curr-name/eax: (addr array byte) <- lookup *curr-name-ah
515   var found?/eax: boolean <- string-equal? needle-name, curr-name
516   compare found?, 0
517   {
518     break-if-=
519     return 1/true
520   }
521   # recurse
522   var curr/eax: (addr word) <- lookup *haystack-ah
523   var next-haystack-ah/eax: (addr handle word) <- get curr, next
524   var result/ebx: boolean <- word-exists? next-haystack-ah, _needle
525   return result
526 }
527 
528 fn word-list-length words: (addr handle word) -> _/eax: int {
529   var curr-ah/esi: (addr handle word) <- copy words
530   var result/edi: int <- copy 0
531   {
532     var curr/eax: (addr word) <- lookup *curr-ah
533     compare curr, 0
534     break-if-=
535     {
536       var word-len/eax: int <- word-length curr
537       result <- add word-len
538       result <- add 1/inter-word-margin
539     }
540     curr-ah <- get curr, next
541     loop
542   }
543   return result
544 }
545 
546 # out-ah already has a word allocated and initialized
547 fn parse-words in: (addr array byte), out-ah: (addr handle word) {
548   var in-stream: (stream byte 0x100)
549   var in-stream-a/esi: (addr stream byte) <- address in-stream
550   write in-stream-a, in
551   var cursor-word-ah/ebx: (addr handle word) <- copy out-ah
552   $parse-words:loop: {
553     var done?/eax: boolean <- stream-empty? in-stream-a
554     compare done?, 0/false
555     break-if-!=
556     var _g/eax: grapheme <- read-grapheme in-stream-a
557     var g/ecx: grapheme <- copy _g
558     # if not space, insert
559     compare g, 0x20/space
560     {
561       break-if-=
562       var cursor-word/eax: (addr word) <- lookup *cursor-word-ah
563       add-grapheme-to-word cursor-word, g
564       loop $parse-words:loop
565     }
566     # otherwise insert word after and move cursor to it
567     append-word cursor-word-ah
568     var cursor-word/eax: (addr word) <- lookup *cursor-word-ah
569     cursor-to-start cursor-word  # reset cursor in each function
570     cursor-word-ah <- get cursor-word, next
571     loop
572   }
573 }