https://github.com/akkartik/mu/blob/master/apps/tile/data.mu
  1 type sandbox {
  2   setup: (handle line)
  3   data: (handle line)
  4   # display data
  5   cursor-call-path: (handle call-path-element)
  6   expanded-words: (handle call-path)
  7   partial-name-for-cursor-word: (handle word)  # only when renaming word
  8   partial-name-for-function: (handle word)  # only when defining function
  9   #
 10   next: (handle sandbox)
 11   prev: (handle sandbox)
 12 }
 13 
 14 type function {
 15   name: (handle array byte)
 16   args: (handle word)  # in reverse order
 17   body: (handle line)
 18   # some sort of indication of spatial location
 19   next: (handle function)
 20 }
 21 
 22 type line {
 23   name: (handle array byte)
 24   data: (handle word)
 25   result: (handle result)  # might be cached
 26   next: (handle line)
 27   prev: (handle line)
 28 }
 29 
 30 type word {
 31   # at most one of these will be non-null
 32   scalar-data: (handle gap-buffer)
 33   text-data: (handle array byte)
 34   box-data: (handle line)  # recurse
 35   #
 36   next: (handle word)
 37   prev: (handle word)
 38 }
 39 
 40 type value {
 41   scalar-data: int
 42   text-data: (handle array byte)
 43   box-data: (handle line)
 44 }
 45 
 46 type table {
 47   data: (handle array bind)
 48   next: (handle table)
 49 }
 50 
 51 type bind {
 52   key: (handle array byte)
 53   value: (handle value)  # I'd inline this but we sometimes want to return a specific value from a table
 54 }
 55 
 56 # A call-path is a data structure that can unambiguously refer to any specific
 57 # call arbitrarily deep inside the call hierarchy of a program.
 58 type call-path {
 59   data: (handle call-path-element)
 60   next: (handle call-path)
 61 }
 62 
 63 # A call-path element is a list of elements, each of which corresponds to some call.
 64 type call-path-element {
 65   word: (handle word)
 66   next: (handle call-path-element)
 67 }
 68 
 69 type result {
 70   data: value-stack
 71   error: (handle array byte)  # single error message for now
 72 }
 73 
 74 fn initialize-sandbox _sandbox: (addr sandbox) {
 75   var sandbox/esi: (addr sandbox) <- copy _sandbox
 76   var line-ah/eax: (addr handle line) <- get sandbox, data
 77   allocate line-ah
 78   var line/eax: (addr line) <- lookup *line-ah
 79   initialize-line line
 80   var word-ah/ecx: (addr handle word) <- get line, data
 81   var cursor-call-path-ah/eax: (addr handle call-path-element) <- get sandbox, cursor-call-path
 82   allocate cursor-call-path-ah
 83   var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah
 84   var dest/eax: (addr handle word) <- get cursor-call-path, word
 85   copy-object word-ah, dest
 86 }
 87 
 88 # initialize line with a single empty word
 89 fn initialize-line _line: (addr line) {
 90   var line/esi: (addr line) <- copy _line
 91   var word-ah/eax: (addr handle word) <- get line, data
 92   allocate word-ah
 93   var word/eax: (addr word) <- lookup *word-ah
 94   initialize-word word
 95 }
 96 
 97 fn create-primitive-functions _self: (addr handle function) {
 98   # x 2* = x 2 *
 99   var self/esi: (addr handle function) <- copy _self
100   allocate self
101   var _f/eax: (addr function) <- lookup *self
102   var f/esi: (addr function) <- copy _f
103   var name-ah/eax: (addr handle array byte) <- get f, name
104   populate-text-with name-ah, "2*"
105   var args-ah/eax: (addr handle word) <- get f, args
106   allocate args-ah
107   var args/eax: (addr word) <- lookup *args-ah
108   initialize-word-with args, "x"
109   var body-ah/eax: (addr handle line) <- get f, body
110   allocate body-ah
111   var body/eax: (addr line) <- lookup *body-ah
112   initialize-line body
113   var curr-word-ah/ecx: (addr handle word) <- get body, data
114   # *curr-word = "x"
115   allocate curr-word-ah
116   var tmp/eax: (addr word) <- lookup *curr-word-ah
117   var curr-word/edx: (addr word) <- copy tmp
118   initialize-word-with curr-word, "x"
119   # *curr-word->next = "2"
120   var next-word-ah/ebx: (addr handle word) <- get curr-word, next
121   allocate next-word-ah
122   tmp <- lookup *next-word-ah
123   initialize-word-with tmp, "2"
124   # *curr-word->next->prev = curr-word
125   var prev-word-ah/edi: (addr handle word) <- get tmp, prev
126   copy-object curr-word-ah, prev-word-ah
127   # curr-word = curr-word->next
128   curr-word-ah <- copy next-word-ah
129   curr-word <- copy tmp
130   # *curr-word->next = "*"
131   next-word-ah <- get curr-word, next
132   allocate next-word-ah
133   tmp <- lookup *next-word-ah
134   initialize-word-with tmp, "*"
135   # *curr-word->next->prev = curr-word
136   prev-word-ah <- get tmp, prev
137   copy-object curr-word-ah, prev-word-ah
138   tmp <- lookup *prev-word-ah
139   # x 1+ = x 1 +
140   var next/esi: (addr handle function) <- get f, next
141   allocate next
142   var _f/eax: (addr function) <- lookup *next
143   var f/esi: (addr function) <- copy _f
144   var name-ah/eax: (addr handle array byte) <- get f, name
145   populate-text-with name-ah, "1+"
146   var args-ah/eax: (addr handle word) <- get f, args
147   allocate args-ah
148   var args/eax: (addr word) <- lookup *args-ah
149   initialize-word-with args, "x"
150   var body-ah/eax: (addr handle line) <- get f, body
151   allocate body-ah
152   var body/eax: (addr line) <- lookup *body-ah
153   initialize-line body
154   var curr-word-ah/ecx: (addr handle word) <- get body, data
155   # *curr-word = "x"
156   allocate curr-word-ah
157   var tmp/eax: (addr word) <- lookup *curr-word-ah
158   curr-word <- copy tmp
159   initialize-word-with curr-word, "x"
160   # *curr-word->next = "1"
161   next-word-ah <- get curr-word, next
162   allocate next-word-ah
163   tmp <- lookup *next-word-ah
164   initialize-word-with tmp, "1"
165   # *curr-word->next->prev = curr-word
166   prev-word-ah <- get tmp, prev
167   copy-object curr-word-ah, prev-word-ah
168   # curr-word = curr-word->next
169   curr-word-ah <- copy next-word-ah
170   curr-word <- copy tmp
171   # *curr-word->next = "+"
172   next-word-ah <- get curr-word, next
173   allocate next-word-ah
174   tmp <- lookup *next-word-ah
175   initialize-word-with tmp, "+"
176   # *curr-word->next->prev = curr-word
177   prev-word-ah <- get tmp, prev
178   copy-object curr-word-ah, prev-word-ah
179   tmp <- lookup *prev-word-ah
180   # x 2+ = x 1+ 1+
181   var next/esi: (addr handle function) <- get f, next
182   allocate next
183   var _f/eax: (addr function) <- lookup *next
184   var f/esi: (addr function) <- copy _f
185   var name-ah/eax: (addr handle array byte) <- get f, name
186   populate-text-with name-ah, "2+"
187   var args-ah/eax: (addr handle word) <- get f, args
188   allocate args-ah
189   var args/eax: (addr word) <- lookup *args-ah
190   initialize-word-with args, "x"
191   var body-ah/eax: (addr handle line) <- get f, body
192   allocate body-ah
193   var body/eax: (addr line) <- lookup *body-ah
194   initialize-line body
195   var curr-word-ah/ecx: (addr handle word) <- get body, data
196   # *curr-word = "x"
197   allocate curr-word-ah
198   var tmp/eax: (addr word) <- lookup *curr-word-ah
199   curr-word <- copy tmp
200   initialize-word-with curr-word, "x"
201   # *curr-word->next = "1+"
202   next-word-ah <- get curr-word, next
203   allocate next-word-ah
204   tmp <- lookup *next-word-ah
205   initialize-word-with tmp, "1+"
206   # *curr-word->next->prev = curr-word
207   prev-word-ah <- get tmp, prev
208   copy-object curr-word-ah, prev-word-ah
209   # curr-word = curr-word->next
210   curr-word-ah <- copy next-word-ah
211   curr-word <- copy tmp
212   # *curr-word->next = "1+"
213   next-word-ah <- get curr-word, next
214   allocate next-word-ah
215   tmp <- lookup *next-word-ah
216   initialize-word-with tmp, "1+"
217   # *curr-word->next->prev = curr-word
218   prev-word-ah <- get tmp, prev
219   copy-object curr-word-ah, prev-word-ah
220   tmp <- lookup *prev-word-ah
221   # x square = x x *
222   var next/esi: (addr handle function) <- get f, next
223   allocate next
224   var _f/eax: (addr function) <- lookup *next
225   var f/esi: (addr function) <- copy _f
226   var name-ah/eax: (addr handle array byte) <- get f, name
227   populate-text-with name-ah, "square"
228   var args-ah/eax: (addr handle word) <- get f, args
229   allocate args-ah
230   var args/eax: (addr word) <- lookup *args-ah
231   initialize-word-with args, "x"
232   var body-ah/eax: (addr handle line) <- get f, body
233   allocate body-ah
234   var body/eax: (addr line) <- lookup *body-ah
235   initialize-line body
236   var curr-word-ah/ecx: (addr handle word) <- get body, data
237   # *curr-word = "x"
238   allocate curr-word-ah
239   var tmp/eax: (addr word) <- lookup *curr-word-ah
240   var curr-word/edx: (addr word) <- copy tmp
241   initialize-word-with curr-word, "x"
242   # *curr-word->next = "x"
243   var next-word-ah/ebx: (addr handle word) <- get curr-word, next
244   allocate next-word-ah
245   tmp <- lookup *next-word-ah
246   initialize-word-with tmp, "x"
247   # *curr-word->next->prev = curr-word
248   var prev-word-ah/edi: (addr handle word) <- get tmp, prev
249   copy-object curr-word-ah, prev-word-ah
250   # curr-word = curr-word->next
251   curr-word-ah <- copy next-word-ah
252   curr-word <- copy tmp
253   # *curr-word->next = "*"
254   next-word-ah <- get curr-word, next
255   allocate next-word-ah
256   tmp <- lookup *next-word-ah
257   initialize-word-with tmp, "*"
258   # *curr-word->next->prev = curr-word
259   prev-word-ah <- get tmp, prev
260   copy-object curr-word-ah, prev-word-ah
261   tmp <- lookup *prev-word-ah
262   # x 1- = x 1 -
263   var next/esi: (addr handle function) <- get f, next
264   allocate next
265   var _f/eax: (addr function) <- lookup *next
266   var f/esi: (addr function) <- copy _f
267   var name-ah/eax: (addr handle array byte) <- get f, name
268   populate-text-with name-ah, "1-"
269   var args-ah/eax: (addr handle word) <- get f, args
270   allocate args-ah
271   var args/eax: (addr word) <- lookup *args-ah
272   initialize-word-with args, "x"
273   var body-ah/eax: (addr handle line) <- get f, body
274   allocate body-ah
275   var body/eax: (addr line) <- lookup *body-ah
276   initialize-line body
277   var curr-word-ah/ecx: (addr handle word) <- get body, data
278   # *curr-word = "x"
279   allocate curr-word-ah
280   var tmp/eax: (addr word) <- lookup *curr-word-ah
281   curr-word <- copy tmp
282   initialize-word-with curr-word, "x"
283   # *curr-word->next = "1"
284   next-word-ah <- get curr-word, next
285   allocate next-word-ah
286   tmp <- lookup *next-word-ah
287   initialize-word-with tmp, "1"
288   # *curr-word->next->prev = curr-word
289   prev-word-ah <- get tmp, prev
290   copy-object curr-word-ah, prev-word-ah
291   # curr-word = curr-word->next
292   curr-word-ah <- copy next-word-ah
293   curr-word <- copy tmp
294   # *curr-word->next = "-"
295   next-word-ah <- get curr-word, next
296   allocate next-word-ah
297   tmp <- lookup *next-word-ah
298   initialize-word-with tmp, "-"
299   # *curr-word->next->prev = curr-word
300   prev-word-ah <- get tmp, prev
301   copy-object curr-word-ah, prev-word-ah
302   tmp <- lookup *prev-word-ah
303   # x y sub = x y -
304   var next/esi: (addr handle function) <- get f, next
305   allocate next
306   var _f/eax: (addr function) <- lookup *next
307   var f/esi: (addr function) <- copy _f
308   var name-ah/eax: (addr handle array byte) <- get f, name
309   populate-text-with name-ah, "sub"
310   # critical lesson: args are stored in reverse order
311   var args-ah/eax: (addr handle word) <- get f, args
312   allocate args-ah
313   var args/eax: (addr word) <- lookup *args-ah
314   initialize-word-with args, "y"
315   var next-arg-ah/eax: (addr handle word) <- get args, next
316   allocate next-arg-ah
317   var next-arg/eax: (addr word) <- lookup *next-arg-ah
318   initialize-word-with next-arg, "x"
319   var body-ah/eax: (addr handle line) <- get f, body
320   allocate body-ah
321   var body/eax: (addr line) <- lookup *body-ah
322   initialize-line body
323   var curr-word-ah/ecx: (addr handle word) <- get body, data
324   # *curr-word = "x"
325   allocate curr-word-ah
326   var tmp/eax: (addr word) <- lookup *curr-word-ah
327   curr-word <- copy tmp
328   initialize-word-with curr-word, "x"
329   # *curr-word->next = "y"
330   next-word-ah <- get curr-word, next
331   allocate next-word-ah
332   tmp <- lookup *next-word-ah
333   initialize-word-with tmp, "y"
334   # *curr-word->next->prev = curr-word
335   prev-word-ah <- get tmp, prev
336   copy-object curr-word-ah, prev-word-ah
337   # curr-word = curr-word->next
338   curr-word-ah <- copy next-word-ah
339   curr-word <- copy tmp
340   # *curr-word->next = "-"
341   next-word-ah <- get curr-word, next
342   allocate next-word-ah
343   tmp <- lookup *next-word-ah
344   initialize-word-with tmp, "-"
345   # *curr-word->next->prev = curr-word
346   prev-word-ah <- get tmp, prev
347   copy-object curr-word-ah, prev-word-ah
348   tmp <- lookup *prev-word-ah
349 }
350 
351 fn function-body functions: (addr handle function), _word: (addr handle word), out: (addr handle line) {
352   var function-name-storage: (handle array byte)
353   var function-name-ah/ecx: (addr handle array byte) <- address function-name-storage
354   var word-ah/esi: (addr handle word) <- copy _word
355   var word/eax: (addr word) <- lookup *word-ah
356   var gap-ah/eax: (addr handle gap-buffer) <- get word, scalar-data
357   var gap/eax: (addr gap-buffer) <- lookup *gap-ah
358   gap-buffer-to-string gap, function-name-ah
359   var _function-name/eax: (addr array byte) <- lookup *function-name-ah
360   var function-name/esi: (addr array byte) <- copy _function-name
361   var curr-ah/ecx: (addr handle function) <- copy functions
362   $function-body:loop: {
363     var _curr/eax: (addr function) <- lookup *curr-ah
364     var curr/edx: (addr function) <- copy _curr
365     compare curr, 0
366     break-if-=
367     var curr-name-ah/eax: (addr handle array byte) <- get curr, name
368     var curr-name/eax: (addr array byte) <- lookup *curr-name-ah
369     var found?/eax: boolean <- string-equal? curr-name, function-name
370     compare found?, 0  # false
371     {
372       break-if-=
373       var src/eax: (addr handle line) <- get curr, body
374       copy-object src, out
375       break $function-body:loop
376     }
377     curr-ah <- get curr, next
378     loop
379   }
380 }
381 
382 fn body-length functions: (addr handle function), function-name: (addr handle word) -> result/eax: int {
383   var body-storage: (handle line)
384   var body-ah/edi: (addr handle line) <- address body-storage
385   function-body functions, function-name, body-ah
386   var body/eax: (addr line) <- lookup *body-ah
387   result <- line-length body
388 }
389 
390 fn line-length _in: (addr line) -> result/eax: int {
391   var in/esi: (addr line) <- copy _in
392   var curr-ah/ecx: (addr handle word) <- get in, data
393   var out/edi: int <- copy 0
394   {
395     var curr/eax: (addr word) <- lookup *curr-ah
396     compare curr, 0
397     break-if-=
398     curr-ah <- get curr, next
399     out <- increment
400     loop
401   }
402   result <- copy out
403 }
404 
405 fn populate-text-with _out: (addr handle array byte), _in: (addr array byte) {
406   var in/esi: (addr array byte) <- copy _in
407   var n/ecx: int <- length in
408   var out/edx: (addr handle array byte) <- copy _out
409   populate out, n
410   var _out-addr/eax: (addr array byte) <- lookup *out
411   var out-addr/edx: (addr array byte) <- copy _out-addr
412   var i/eax: int <- copy 0
413   {
414     compare i, n
415     break-if->=
416     var src/esi: (addr byte) <- index in, i
417     var val/ecx: byte <- copy-byte *src
418     var dest/edi: (addr byte) <- index out-addr, i
419     copy-byte-to *dest, val
420     i <- increment
421     loop
422   }
423 }
424 
425 fn initialize-path-from-sandbox _in: (addr sandbox), _out: (addr handle call-path-element) {
426   var sandbox/esi: (addr sandbox) <- copy _in
427   var line-ah/eax: (addr handle line) <- get sandbox, data
428   var line/eax: (addr line) <- lookup *line-ah
429   var src/esi: (addr handle word) <- get line, data
430   var out-ah/edi: (addr handle call-path-element) <- copy _out
431   var out/eax: (addr call-path-element) <- lookup *out-ah
432   var dest/edi: (addr handle word) <- get out, word
433   copy-object src, dest
434 }
435 
436 fn initialize-path-from-line _line: (addr line), _out: (addr handle call-path-element) {
437   var line/eax: (addr line) <- copy _line
438   var src/esi: (addr handle word) <- get line, data
439   var out-ah/edi: (addr handle call-path-element) <- copy _out
440   var out/eax: (addr call-path-element) <- lookup *out-ah
441   var dest/edi: (addr handle word) <- get out, word
442   copy-object src, dest
443 }
444 
445 fn find-in-call-paths call-paths: (addr handle call-path), needle: (addr handle call-path-element) -> result/eax: boolean {
446   var curr-ah/esi: (addr handle call-path) <- copy call-paths
447   var out/edi: boolean <- copy 0  # false
448   $find-in-call-path:loop: {
449     var curr/eax: (addr call-path) <- lookup *curr-ah
450     compare curr, 0
451     break-if-=
452     {
453       var curr-data/eax: (addr handle call-path-element) <- get curr, data
454       var match?/eax: boolean <- call-path-element-match? curr-data, needle
455       compare match?, 0  # false
456       {
457         break-if-=
458         out <- copy 1  # true
459         break $find-in-call-path:loop
460       }
461     }
462     curr-ah <- get curr, next
463     loop
464   }
465   result <- copy out
466 }
467 
468 fn call-path-element-match? _x: (addr handle call-path-element), _y: (addr handle call-path-element) -> result/eax: boolean {
469 $call-path-element-match?:body: {
470   var x-ah/eax: (addr handle call-path-element) <- copy _x
471   var x-a/eax: (addr call-path-element) <- lookup *x-ah
472   var x/esi: (addr call-path-element) <- copy x-a
473   var y-ah/eax: (addr handle call-path-element) <- copy _y
474   var y-a/eax: (addr call-path-element) <- lookup *y-ah
475   var y/edi: (addr call-path-element) <- copy y-a
476   compare x, y
477   {
478     break-if-!=
479     result <- copy 1  # true
480     break $call-path-element-match?:body
481   }
482   compare x, 0
483   {
484     break-if-!=
485     result <- copy 0  # false
486     break $call-path-element-match?:body
487   }
488   compare y, 0
489   {
490     break-if-!=
491     result <- copy 0  # false
492     break $call-path-element-match?:body
493   }
494   # compare word addresses, not contents
495   var x-data-ah/ecx: (addr handle word) <- get x, word
496   var x-data-a/eax: (addr word) <- lookup *x-data-ah
497   var x-data/ecx: int <- copy x-data-a
498   var y-data-ah/eax: (addr handle word) <- get y, word
499   var y-data-a/eax: (addr word) <- lookup *y-data-ah
500   var y-data/eax: int <- copy y-data-a
501 #?   print-string 0, "match? "
502 #?   print-int32-hex 0, x-data
503 #?   print-string 0, " vs "
504 #?   print-int32-hex 0, y-data
505 #?   print-string 0, "\n"
506   compare x-data, y-data
507   {
508     break-if-=
509     result <- copy 0  # false
510     break $call-path-element-match?:body
511   }
512   var x-next/ecx: (addr handle call-path-element) <- get x, next
513   var y-next/eax: (addr handle call-path-element) <- get y, next
514   result <- call-path-element-match? x-next, y-next
515 }
516 }
517 
518 # order is irrelevant
519 fn insert-in-call-path list: (addr handle call-path), new: (addr handle call-path-element) {
520   var new-path-storage: (handle call-path)
521   var new-path-ah/edi: (addr handle call-path) <- address new-path-storage
522   allocate new-path-ah
523   var new-path/eax: (addr call-path) <- lookup *new-path-ah
524   var next/ecx: (addr handle call-path) <- get new-path, next
525   copy-object list, next
526   var dest/ecx: (addr handle call-path-element) <- get new-path, data
527   deep-copy-call-path-element new, dest
528   copy-object new-path-ah, list
529 }
530 
531 # assumes dest is initially clear
532 fn deep-copy-call-path-element _src: (addr handle call-path-element), _dest: (addr handle call-path-element) {
533   var src/esi: (addr handle call-path-element) <- copy _src
534   # if src is null, return
535   var _src-addr/eax: (addr call-path-element) <- lookup *src
536   compare _src-addr, 0
537   break-if-=
538   # allocate
539   var src-addr/esi: (addr call-path-element) <- copy _src-addr
540   var dest/eax: (addr handle call-path-element) <- copy _dest
541   allocate dest
542   # copy data
543   var dest-addr/eax: (addr call-path-element) <- lookup *dest
544   {
545     var dest-data-addr/ecx: (addr handle word) <- get dest-addr, word
546     var src-data-addr/eax: (addr handle word) <- get src-addr, word
547     copy-object src-data-addr, dest-data-addr
548   }
549   # recurse
550   var src-next/esi: (addr handle call-path-element) <- get src-addr, next
551   var dest-next/eax: (addr handle call-path-element) <- get dest-addr, next
552   deep-copy-call-path-element src-next, dest-next
553 }
554 
555 fn delete-in-call-path list: (addr handle call-path), needle: (addr handle call-path-element) {
556 $delete-in-call-path:body: {
557   var curr-ah/esi: (addr handle call-path) <- copy list
558   $delete-in-call-path:loop: {
559     var _curr/eax: (addr call-path) <- lookup *curr-ah
560     var curr/ecx: (addr call-path) <- copy _curr
561     compare curr, 0
562     break-if-=
563     {
564       var curr-data/eax: (addr handle call-path-element) <- get curr, data
565       var match?/eax: boolean <- call-path-element-match? curr-data, needle
566       compare match?, 0  # false
567       {
568         break-if-=
569         var next-ah/ecx: (addr handle call-path) <- get curr, next
570         copy-object next-ah, curr-ah
571         loop $delete-in-call-path:loop
572       }
573     }
574     curr-ah <- get curr, next
575     loop
576   }
577 }
578 }
579 
580 fn increment-final-element list: (addr handle call-path-element) {
581   var final-ah/eax: (addr handle call-path-element) <- copy list
582   var final/eax: (addr call-path-element) <- lookup *final-ah
583   var val-ah/ecx: (addr handle word) <- get final, word
584   var val/eax: (addr word) <- lookup *val-ah
585   var new-ah/edx: (addr handle word) <- get val, next
586   var target/eax: (addr word) <- lookup *new-ah
587   compare target, 0
588   break-if-=
589   copy-object new-ah, val-ah
590 }
591 
592 fn decrement-final-element list: (addr handle call-path-element) {
593   var final-ah/eax: (addr handle call-path-element) <- copy list
594   var final/eax: (addr call-path-element) <- lookup *final-ah
595   var val-ah/ecx: (addr handle word) <- get final, word
596   var val/eax: (addr word) <- lookup *val-ah
597   var new-ah/edx: (addr handle word) <- get val, prev
598   var target/eax: (addr word) <- lookup *new-ah
599   compare target, 0
600   break-if-=
601   copy-object new-ah, val-ah
602 }
603 
604 fn move-final-element-to-start-of-line list: (addr handle call-path-element) {
605   var final-ah/eax: (addr handle call-path-element) <- copy list
606   var final/eax: (addr call-path-element) <- lookup *final-ah
607   var val-ah/ecx: (addr handle word) <- get final, word
608   var val/eax: (addr word) <- lookup *val-ah
609   var new-ah/edx: (addr handle word) <- get val, prev
610   var target/eax: (addr word) <- lookup *new-ah
611   compare target, 0
612   break-if-=
613   copy-object new-ah, val-ah
614   move-final-element-to-start-of-line list
615 }
616 
617 fn push-to-call-path-element list: (addr handle call-path-element), new: (addr handle word) {
618   var new-element-storage: (handle call-path-element)
619   var new-element-ah/edi: (addr handle call-path-element) <- address new-element-storage
620   allocate new-element-ah
621   var new-element/eax: (addr call-path-element) <- lookup *new-element-ah
622   # save word
623   var dest/ecx: (addr handle word) <- get new-element, word
624   copy-object new, dest
625   # save next
626   var dest2/ecx: (addr handle call-path-element) <- get new-element, next
627   copy-object list, dest2
628   # return
629   copy-object new-element-ah, list
630 }
631 
632 fn drop-from-call-path-element _list: (addr handle call-path-element) {
633   var list-ah/esi: (addr handle call-path-element) <- copy _list
634   var list/eax: (addr call-path-element) <- lookup *list-ah
635   var next/eax: (addr handle call-path-element) <- get list, next
636   copy-object next, _list
637 }
638 
639 fn drop-nested-calls _list: (addr handle call-path-element) {
640   var list-ah/esi: (addr handle call-path-element) <- copy _list
641   var list/eax: (addr call-path-element) <- lookup *list-ah
642   var next-ah/edi: (addr handle call-path-element) <- get list, next
643   var next/eax: (addr call-path-element) <- lookup *next-ah
644   compare next, 0
645   break-if-=
646   copy-object next-ah, _list
647   drop-nested-calls _list
648 }
649 
650 fn dump-call-path-element screen: (addr screen), _x-ah: (addr handle call-path-element) {
651 $dump-call-path-element:body: {
652   var x-ah/ecx: (addr handle call-path-element) <- copy _x-ah
653   var _x/eax: (addr call-path-element) <- lookup *x-ah
654   var x/esi: (addr call-path-element) <- copy _x
655   var word-ah/eax: (addr handle word) <- get x, word
656   var word/eax: (addr word) <- lookup *word-ah
657   print-word screen, word
658   var next-ah/ecx: (addr handle call-path-element) <- get x, next
659   var next/eax: (addr call-path-element) <- lookup *next-ah
660   compare next, 0
661   {
662     break-if-=
663     print-string screen, " "
664     dump-call-path-element screen, next-ah
665     break $dump-call-path-element:body
666   }
667   {
668     break-if-!=
669     print-string screen, "\n"
670   }
671 }
672 }
673 
674 fn dump-call-paths screen: (addr screen), _x-ah: (addr handle call-path) {
675 $dump-call-paths:body: {
676   var x-ah/ecx: (addr handle call-path) <- copy _x-ah
677   var x/eax: (addr call-path) <- lookup *x-ah
678   compare x, 0
679   break-if-=
680   var src/ecx: (addr handle call-path-element) <- get x, data
681   dump-call-path-element screen, src
682   var next-ah/ecx: (addr handle call-path) <- get x, next
683   var next/eax: (addr call-path) <- lookup *next-ah
684   compare next, 0
685   {
686     break-if-=
687     dump-call-paths screen, next-ah
688     break $dump-call-paths:body
689   }
690 }
691 }