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