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