https://github.com/akkartik/mu/blob/main/apps/tile/rpn.mu
  1 fn evaluate functions: (addr handle function), bindings: (addr table), scratch: (addr line), end: (addr word), out: (addr value-stack) {
  2   var line/eax: (addr line) <- copy scratch
  3   var word-ah/eax: (addr handle word) <- get line, data
  4   var curr/eax: (addr word) <- lookup *word-ah
  5   var curr-stream-storage: (stream byte 0x10)
  6   var curr-stream/edi: (addr stream byte) <- address curr-stream-storage
  7   clear-value-stack out
  8   $evaluate:loop: {
  9     # precondition (should never hit)
 10     compare curr, 0
 11     break-if-=
 12     # update curr-stream
 13     emit-word curr, curr-stream
 14 #?     print-string-to-real-screen "eval: "
 15 #?     print-stream-to-real-screen curr-stream
 16 #?     print-string-to-real-screen "\n"
 17     $evaluate:process-word: {
 18       ### if curr-stream is an operator, perform it
 19       ## numbers
 20       {
 21         var is-add?/eax: boolean <- stream-data-equal? curr-stream, "+"
 22         compare is-add?, 0
 23         break-if-=
 24         var _b/xmm0: float <- pop-number-from-value-stack out
 25         var b/xmm1: float <- copy _b
 26         var a/xmm0: float <- pop-number-from-value-stack out
 27         a <- add b
 28         push-number-to-value-stack out, a
 29         break $evaluate:process-word
 30       }
 31       {
 32         var is-sub?/eax: boolean <- stream-data-equal? curr-stream, "-"
 33         compare is-sub?, 0
 34         break-if-=
 35         var _b/xmm0: float <- pop-number-from-value-stack out
 36         var b/xmm1: float <- copy _b
 37         var a/xmm0: float <- pop-number-from-value-stack out
 38         a <- subtract b
 39         push-number-to-value-stack out, a
 40         break $evaluate:process-word
 41       }
 42       {
 43         var is-mul?/eax: boolean <- stream-data-equal? curr-stream, "*"
 44         compare is-mul?, 0
 45         break-if-=
 46         var _b/xmm0: float <- pop-number-from-value-stack out
 47         var b/xmm1: float <- copy _b
 48         var a/xmm0: float <- pop-number-from-value-stack out
 49         a <- multiply b
 50         push-number-to-value-stack out, a
 51         break $evaluate:process-word
 52       }
 53       {
 54         var is-div?/eax: boolean <- stream-data-equal? curr-stream, "/"
 55         compare is-div?, 0
 56         break-if-=
 57         var _b/xmm0: float <- pop-number-from-value-stack out
 58         var b/xmm1: float <- copy _b
 59         var a/xmm0: float <- pop-number-from-value-stack out
 60         a <- divide b
 61         push-number-to-value-stack out, a
 62         break $evaluate:process-word
 63       }
 64       {
 65         var is-sqrt?/eax: boolean <- stream-data-equal? curr-stream, "sqrt"
 66         compare is-sqrt?, 0
 67         break-if-=
 68         var a/xmm0: float <- pop-number-from-value-stack out
 69         a <- square-root a
 70         push-number-to-value-stack out, a
 71         break $evaluate:process-word
 72       }
 73       ## strings/arrays
 74       {
 75         var is-len?/eax: boolean <- stream-data-equal? curr-stream, "len"
 76         compare is-len?, 0
 77         break-if-=
 78 #?         print-string 0, "is len\n"
 79         # pop target-val from out
 80         var out2/esi: (addr value-stack) <- copy out
 81         var top-addr/ecx: (addr int) <- get out2, top
 82         compare *top-addr, 0
 83         break-if-<=
 84 #?         print-string 0, "stack has stuff\n"
 85         var data-ah/eax: (addr handle array value) <- get out2, data
 86         var data/eax: (addr array value) <- lookup *data-ah
 87         var top/edx: int <- copy *top-addr
 88         top <- decrement
 89         var dest-offset/edx: (offset value) <- compute-offset data, top
 90         var target-val/edx: (addr value) <- index data, dest-offset
 91         # check target-val is a string or array
 92         var target-type-addr/eax: (addr int) <- get target-val, type
 93         compare *target-type-addr, 1  # string
 94         {
 95           break-if-!=
 96           # compute length
 97           var src-ah/eax: (addr handle array byte) <- get target-val, text-data
 98           var src/eax: (addr array byte) <- lookup *src-ah
 99           var result/ebx: int <- length src
100           var result-f/xmm0: float <- convert result
101           # save result into target-val
102           var type-addr/eax: (addr int) <- get target-val, type
103           copy-to *type-addr, 0  # int
104           var target-string-ah/eax: (addr handle array byte) <- get target-val, text-data
105           clear-object target-string-ah
106           var target/eax: (addr float) <- get target-val, number-data
107           copy-to *target, result-f
108           break $evaluate:process-word
109         }
110         compare *target-type-addr, 2  # array of ints
111         {
112           break-if-!=
113           # compute length
114           var src-ah/eax: (addr handle array value) <- get target-val, array-data
115           var src/eax: (addr array value) <- lookup *src-ah
116           var result/ebx: int <- length src
117           var result-f/xmm0: float <- convert result
118           # save result into target-val
119           var type-addr/eax: (addr int) <- get target-val, type
120           copy-to *type-addr, 0  # int
121           var target-array-ah/eax: (addr handle array value) <- get target-val, array-data
122           clear-object target-array-ah
123           var target/eax: (addr float) <- get target-val, number-data
124           copy-to *target, result-f
125           break $evaluate:process-word
126         }
127       }
128       ## files
129       {
130         var is-open?/eax: boolean <- stream-data-equal? curr-stream, "open"
131         compare is-open?, 0
132         break-if-=
133         # pop target-val from out
134         var out2/esi: (addr value-stack) <- copy out
135         var top-addr/ecx: (addr int) <- get out2, top
136         compare *top-addr, 0
137         break-if-<=
138         var data-ah/eax: (addr handle array value) <- get out2, data
139         var data/eax: (addr array value) <- lookup *data-ah
140         var top/edx: int <- copy *top-addr
141         top <- decrement
142         var dest-offset/edx: (offset value) <- compute-offset data, top
143         var target-val/edx: (addr value) <- index data, dest-offset
144         # check target-val is a string
145         var target-type-addr/eax: (addr int) <- get target-val, type
146         compare *target-type-addr, 1  # string
147         break-if-!=
148         # open target-val as a filename and save the handle in target-val
149         var src-ah/eax: (addr handle array byte) <- get target-val, text-data
150         var src/eax: (addr array byte) <- lookup *src-ah
151         var result-ah/ecx: (addr handle buffered-file) <- get target-val, file-data
152         open src, 0, result-ah  # write? = false
153         # save result into target-val
154         var type-addr/eax: (addr int) <- get target-val, type
155         copy-to *type-addr, 3  # file
156         var target-string-ah/eax: (addr handle array byte) <- get target-val, text-data
157         var filename-ah/ecx: (addr handle array byte) <- get target-val, filename
158         copy-object target-string-ah, filename-ah
159         clear-object target-string-ah
160         break $evaluate:process-word
161       }
162       {
163         var is-read?/eax: boolean <- stream-data-equal? curr-stream, "read"
164         compare is-read?, 0
165         break-if-=
166         # pop target-val from out
167         var out2/esi: (addr value-stack) <- copy out
168         var top-addr/ecx: (addr int) <- get out2, top
169         compare *top-addr, 0
170         break-if-<=
171         var data-ah/eax: (addr handle array value) <- get out2, data
172         var data/eax: (addr array value) <- lookup *data-ah
173         var top/edx: int <- copy *top-addr
174         top <- decrement
175         var dest-offset/edx: (offset value) <- compute-offset data, top
176         var target-val/edx: (addr value) <- index data, dest-offset
177         # check target-val is a file
178         var target-type-addr/eax: (addr int) <- get target-val, type
179         compare *target-type-addr, 3  # file
180         break-if-!=
181         # read a line from the file and save in target-val
182         # read target-val as a filename and save the handle in target-val
183         var file-ah/eax: (addr handle buffered-file) <- get target-val, file-data
184         var file/eax: (addr buffered-file) <- lookup *file-ah
185         var s: (stream byte 0x100)
186         var s-addr/ecx: (addr stream byte) <- address s
187         read-line-buffered file, s-addr
188         var target/eax: (addr handle array byte) <- get target-val, text-data
189         stream-to-array s-addr, target
190         # save result into target-val
191         var type-addr/eax: (addr int) <- get target-val, type
192         copy-to *type-addr, 1  # string
193         var target-file-ah/eax: (addr handle buffered-file) <- get target-val, file-data
194         clear-object target-file-ah
195         break $evaluate:process-word
196       }
197       {
198         var is-slurp?/eax: boolean <- stream-data-equal? curr-stream, "slurp"
199         compare is-slurp?, 0
200         break-if-=
201         # pop target-val from out
202         var out2/esi: (addr value-stack) <- copy out
203         var top-addr/ecx: (addr int) <- get out2, top
204         compare *top-addr, 0
205         break-if-<=
206         var data-ah/eax: (addr handle array value) <- get out2, data
207         var data/eax: (addr array value) <- lookup *data-ah
208         var top/edx: int <- copy *top-addr
209         top <- decrement
210         var dest-offset/edx: (offset value) <- compute-offset data, top
211         var target-val/edx: (addr value) <- index data, dest-offset
212         # check target-val is a file
213         var target-type-addr/eax: (addr int) <- get target-val, type
214         compare *target-type-addr, 3  # file
215         break-if-!=
216         # slurp all contents from file and save in target-val
217         # read target-val as a filename and save the handle in target-val
218         var file-ah/eax: (addr handle buffered-file) <- get target-val, file-data
219         var file/eax: (addr buffered-file) <- lookup *file-ah
220         var s: (stream byte 0x100)
221         var s-addr/ecx: (addr stream byte) <- address s
222         slurp file, s-addr
223         var target/eax: (addr handle array byte) <- get target-val, text-data
224         stream-to-array s-addr, target
225         # save result into target-val
226         var type-addr/eax: (addr int) <- get target-val, type
227         copy-to *type-addr, 1  # string
228         var target-file-ah/eax: (addr handle buffered-file) <- get target-val, file-data
229         clear-object target-file-ah
230         break $evaluate:process-word
231       }
232       {
233         var is-lines?/eax: boolean <- stream-data-equal? curr-stream, "lines"
234         compare is-lines?, 0
235         break-if-=
236         # pop target-val from out
237         var out2/esi: (addr value-stack) <- copy out
238         var top-addr/ecx: (addr int) <- get out2, top
239         compare *top-addr, 0
240         break-if-<=
241         var data-ah/eax: (addr handle array value) <- get out2, data
242         var data/eax: (addr array value) <- lookup *data-ah
243         var top/edx: int <- copy *top-addr
244         top <- decrement
245         var dest-offset/edx: (offset value) <- compute-offset data, top
246         var target-val/edx: (addr value) <- index data, dest-offset
247         # check target-val is a file
248         var target-type-addr/eax: (addr int) <- get target-val, type
249         compare *target-type-addr, 3  # file
250         break-if-!=
251         # read all lines from file and save as an array of strings in target-val
252         # read target-val as a filename and save the handle in target-val
253         var file-ah/eax: (addr handle buffered-file) <- get target-val, file-data
254         var file/eax: (addr buffered-file) <- lookup *file-ah
255         var s: (stream byte 0x100)
256         var s-addr/ecx: (addr stream byte) <- address s
257         slurp file, s-addr
258         var tmp-ah/eax: (addr handle array byte) <- get target-val, text-data
259         stream-to-array s-addr, tmp-ah
260         var tmp/eax: (addr array byte) <- lookup *tmp-ah
261 #?         enable-screen-type-mode
262 #?         print-string 0, tmp
263         var h: (handle array (handle array byte))
264         {
265           var ah/edx: (addr handle array (handle array byte)) <- address h
266           split-string tmp, 0xa, ah
267         }
268         var target/eax: (addr handle array value) <- get target-val, array-data
269         save-lines h, target
270         # save result into target-val
271         var type-addr/eax: (addr int) <- get target-val, type
272         copy-to *type-addr, 2  # array
273         var target-file-ah/eax: (addr handle buffered-file) <- get target-val, file-data
274         var empty-file: (handle buffered-file)
275         copy-handle empty-file, target-file-ah
276         var target-text-ah/eax: (addr handle array byte) <- get target-val, text-data
277         var empty-text: (handle array byte)
278         copy-handle empty-text, target-text-ah
279         break $evaluate:process-word
280       }
281       ## screens
282       {
283         var is-fake-screen?/eax: boolean <- stream-data-equal? curr-stream, "fake-screen"
284         compare is-fake-screen?, 0
285         break-if-=
286         var out2/esi: (addr value-stack) <- copy out
287         var top-addr/ecx: (addr int) <- get out2, top
288         compare *top-addr, 0
289         break-if-<=
290         # pop width and height from out
291         var nrows-f/xmm0: float <- pop-number-from-value-stack out2
292         var nrows/edx: int <- convert nrows-f
293         var ncols-f/xmm0: float <- pop-number-from-value-stack out2
294         var ncols/ebx: int <- convert ncols-f
295         # define a new screen with those dimensions
296         var screen-h: (handle screen)
297         var screen-ah/eax: (addr handle screen) <- address screen-h
298         allocate screen-ah
299         var screen/eax: (addr screen) <- lookup screen-h
300         initialize-screen screen, nrows, ncols
301         # push screen to stack
302         var data-ah/eax: (addr handle array value) <- get out2, data
303         var data/eax: (addr array value) <- lookup *data-ah
304         var top/edx: int <- copy *top-addr
305         increment *top-addr
306         var dest-offset/edx: (offset value) <- compute-offset data, top
307         var target-val/edx: (addr value) <- index data, dest-offset
308         var type/eax: (addr int) <- get target-val, type
309         copy-to *type, 4  # screen
310         var dest/eax: (addr handle screen) <- get target-val, screen-data
311         copy-handle screen-h, dest
312         break $evaluate:process-word
313       }
314       {
315         var is-print?/eax: boolean <- stream-data-equal? curr-stream, "print"
316         compare is-print?, 0
317         break-if-=
318         var out2/esi: (addr value-stack) <- copy out
319         var top-addr/ecx: (addr int) <- get out2, top
320         compare *top-addr, 0
321         break-if-<=
322         # pop string from out
323         var top-addr/ecx: (addr int) <- get out2, top
324         compare *top-addr, 0
325         break-if-<=
326         decrement *top-addr
327         var data-ah/eax: (addr handle array value) <- get out2, data
328         var _data/eax: (addr array value) <- lookup *data-ah
329         var data/edi: (addr array value) <- copy _data
330         var top/eax: int <- copy *top-addr
331         var dest-offset/edx: (offset value) <- compute-offset data, top
332         var s/esi: (addr value) <- index data, dest-offset
333         # select target screen from top of out (but don't pop it)
334         compare *top-addr, 0
335         break-if-<=
336         var top/eax: int <- copy *top-addr
337         top <- decrement
338         var dest-offset/edx: (offset value) <- compute-offset data, top
339         var target-val/edx: (addr value) <- index data, dest-offset
340         var type/eax: (addr int) <- get target-val, type
341         compare *type, 4  # screen
342         break-if-!=
343         # print string to target screen
344         var dest-ah/eax: (addr handle screen) <- get target-val, screen-data
345         var dest/eax: (addr screen) <- lookup *dest-ah
346         var r/ecx: (addr int) <- get dest, cursor-row
347         var c/edx: (addr int) <- get dest, cursor-col
348         render-value-at dest, *r, *c, s, 0
349         break $evaluate:process-word
350       }
351       {
352         var is-move?/eax: boolean <- stream-data-equal? curr-stream, "move"
353         compare is-move?, 0
354         break-if-=
355         var out2/esi: (addr value-stack) <- copy out
356         # pop args
357         var r-f/xmm0: float <- pop-number-from-value-stack out2
358         var r/ecx: int <- convert r-f
359         var c-f/xmm0: float <- pop-number-from-value-stack out2
360         var c/edx: int <- convert c-f
361         # select screen from top of out (but don't pop it)
362         var top-addr/ebx: (addr int) <- get out2, top
363         compare *top-addr, 0
364         break-if-<=
365         var data-ah/eax: (addr handle array value) <- get out2, data
366         var _data/eax: (addr array value) <- lookup *data-ah
367         var data/edi: (addr array value) <- copy _data
368         var top/eax: int <- copy *top-addr
369         top <- decrement
370         var target-offset/eax: (offset value) <- compute-offset data, top
371         var target-val/ebx: (addr value) <- index data, target-offset
372         var type/eax: (addr int) <- get target-val, type
373         compare *type, 4  # screen
374         break-if-!=
375         var target-ah/eax: (addr handle screen) <- get target-val, screen-data
376         var target/eax: (addr screen) <- lookup *target-ah
377         move-cursor target, r, c
378         break $evaluate:process-word
379       }
380       {
381         var is-up?/eax: boolean <- stream-data-equal? curr-stream, "up"
382         compare is-up?, 0
383         break-if-=
384         var out2/esi: (addr value-stack) <- copy out
385         var top-addr/ebx: (addr int) <- get out2, top
386         compare *top-addr, 0
387         break-if-<=
388         # pop args
389         var d-f/xmm0: float <- pop-number-from-value-stack out2
390         var d/ecx: int <- convert d-f
391         # select screen from top of out (but don't pop it)
392         compare *top-addr, 0
393         break-if-<=
394         var data-ah/eax: (addr handle array value) <- get out2, data
395         var _data/eax: (addr array value) <- lookup *data-ah
396         var data/edi: (addr array value) <- copy _data
397         var top/eax: int <- copy *top-addr
398         top <- decrement
399         var target-offset/eax: (offset value) <- compute-offset data, top
400         var target-val/ebx: (addr value) <- index data, target-offset
401         var type/eax: (addr int) <- get target-val, type
402         compare *type, 4  # screen
403         break-if-!=
404         var target-ah/eax: (addr handle screen) <- get target-val, screen-data
405         var _target/eax: (addr screen) <- lookup *target-ah
406         var target/edi: (addr screen) <- copy _target
407         var r/edx: (addr int) <- get target, cursor-row
408         var c/eax: (addr int) <- get target, cursor-col
409         var col/eax: int <- copy *c
410         {
411           compare d, 0
412           break-if-<=
413           compare *r, 1
414           break-if-<=
415           print-string target "│"
416           decrement *r
417           move-cursor target, *r, col
418           d <- decrement
419           loop
420         }
421         break $evaluate:process-word
422       }
423       {
424         var is-down?/eax: boolean <- stream-data-equal? curr-stream, "down"
425         compare is-down?, 0
426         break-if-=
427         var out2/esi: (addr value-stack) <- copy out
428         var top-addr/ebx: (addr int) <- get out2, top
429         compare *top-addr, 0
430         break-if-<=
431         # pop args
432         var d-f/xmm0: float <- pop-number-from-value-stack out2
433         var d/ecx: int <- convert d-f
434         # select screen from top of out (but don't pop it)
435         compare *top-addr, 0
436         break-if-<=
437         var data-ah/eax: (addr handle array value) <- get out2, data
438         var _data/eax: (addr array value) <- lookup *data-ah
439         var data/edi: (addr array value) <- copy _data
440         var top/eax: int <- copy *top-addr
441         top <- decrement
442         var target-offset/eax: (offset value) <- compute-offset data, top
443         var target-val/ebx: (addr value) <- index data, target-offset
444         var type/eax: (addr int) <- get target-val, type
445         compare *type, 4  # screen
446         break-if-!=
447         var target-ah/eax: (addr handle screen) <- get target-val, screen-data
448         var _target/eax: (addr screen) <- lookup *target-ah
449         var target/edi: (addr screen) <- copy _target
450         var bound-a/ebx: (addr int) <- get target, num-rows
451         var bound/ebx: int <- copy *bound-a
452         var r/edx: (addr int) <- get target, cursor-row
453         var c/eax: (addr int) <- get target, cursor-col
454         var col/eax: int <- copy *c
455         {
456           compare d, 0
457           break-if-<=
458           compare *r, bound
459           break-if->=
460           print-string target "│"
461           increment *r
462           move-cursor target, *r, col
463           d <- decrement
464           loop
465         }
466         break $evaluate:process-word
467       }
468       {
469         var is-left?/eax: boolean <- stream-data-equal? curr-stream, "left"
470         compare is-left?, 0
471         break-if-=
472         var out2/esi: (addr value-stack) <- copy out
473         var top-addr/ebx: (addr int) <- get out2, top
474         compare *top-addr, 0
475         break-if-<=
476         # pop args
477         var d-f/xmm0: float <- pop-number-from-value-stack out2
478         var d/ecx: int <- convert d-f
479         # select screen from top of out (but don't pop it)
480         compare *top-addr, 0
481         break-if-<=
482         var data-ah/eax: (addr handle array value) <- get out2, data
483         var _data/eax: (addr array value) <- lookup *data-ah
484         var data/edi: (addr array value) <- copy _data
485         var top/eax: int <- copy *top-addr
486         top <- decrement
487         var target-offset/eax: (offset value) <- compute-offset data, top
488         var target-val/ebx: (addr value) <- index data, target-offset
489         var type/eax: (addr int) <- get target-val, type
490         compare *type, 4  # screen
491         break-if-!=
492         var target-ah/eax: (addr handle screen) <- get target-val, screen-data
493         var _target/eax: (addr screen) <- lookup *target-ah
494         var target/edi: (addr screen) <- copy _target
495         var c/edx: (addr int) <- get target, cursor-col
496         var r/eax: (addr int) <- get target, cursor-row
497         var row/eax: int <- copy *r
498         {
499           compare d, 0
500           break-if-<=
501           compare *c, 1
502           break-if-<=
503           print-string target "─"
504           decrement *c
505           decrement *c  # second one to undo the print above
506           move-cursor target, row, *c
507           d <- decrement
508           loop
509         }
510         break $evaluate:process-word
511       }
512       {
513         var is-right?/eax: boolean <- stream-data-equal? curr-stream, "right"
514         compare is-right?, 0
515         break-if-=
516         var out2/esi: (addr value-stack) <- copy out
517         var top-addr/ebx: (addr int) <- get out2, top
518         compare *top-addr, 0
519         break-if-<=
520         # pop args
521         var _d/xmm0: float <- pop-number-from-value-stack out2
522         var d/ecx: int <- convert _d
523         # select screen from top of out (but don't pop it)
524         compare *top-addr, 0
525         break-if-<=
526         var data-ah/eax: (addr handle array value) <- get out2, data
527         var _data/eax: (addr array value) <- lookup *data-ah
528         var data/edi: (addr array value) <- copy _data
529         var top/eax: int <- copy *top-addr
530         top <- decrement
531         var target-offset/eax: (offset value) <- compute-offset data, top
532         var target-val/ebx: (addr value) <- index data, target-offset
533         var type/eax: (addr int) <- get target-val, type
534         compare *type, 4  # screen
535         break-if-!=
536         var target-ah/eax: (addr handle screen) <- get target-val, screen-data
537         var _target/eax: (addr screen) <- lookup *target-ah
538         var target/edi: (addr screen) <- copy _target
539         var bound-a/ebx: (addr int) <- get target, num-rows
540         var bound/ebx: int <- copy *bound-a
541         var c/edx: (addr int) <- get target, cursor-col
542         var r/eax: (addr int) <- get target, cursor-row
543         var row/eax: int <- copy *r
544         {
545           compare d, 0
546           break-if-<=
547           compare *c, bound
548           break-if->=
549           print-string target "─"
550           # no increment; the print took care of it
551           move-cursor target, row, *c
552           d <- decrement
553           loop
554         }
555         break $evaluate:process-word
556       }
557       ## HACKS: we're trying to avoid turning this into Forth
558       {
559         var is-dup?/eax: boolean <- stream-data-equal? curr-stream, "dup"
560         compare is-dup?, 0
561         break-if-=
562         # read src-val from out
563         var out2/esi: (addr value-stack) <- copy out
564         var top-addr/ecx: (addr int) <- get out2, top
565         compare *top-addr, 0
566         break-if-<=
567         var data-ah/eax: (addr handle array value) <- get out2, data
568         var data/eax: (addr array value) <- lookup *data-ah
569         var top/ecx: int <- copy *top-addr
570         top <- decrement
571         var offset/edx: (offset value) <- compute-offset data, top
572         var src-val/edx: (addr value) <- index data, offset
573         # push a copy of it
574         top <- increment
575         var offset/ebx: (offset value) <- compute-offset data, top
576         var target-val/ebx: (addr value) <- index data, offset
577         copy-object src-val, target-val
578         # commit
579         var top-addr/ecx: (addr int) <- get out2, top
580         increment *top-addr
581         break $evaluate:process-word
582       }
583       {
584         var is-swap?/eax: boolean <- stream-data-equal? curr-stream, "swap"
585         compare is-swap?, 0
586         break-if-=
587         # read top-val from out
588         var out2/esi: (addr value-stack) <- copy out
589         var top-addr/ecx: (addr int) <- get out2, top
590         compare *top-addr, 0
591         break-if-<=
592         var data-ah/eax: (addr handle array value) <- get out2, data
593         var data/eax: (addr array value) <- lookup *data-ah
594         var top/ecx: int <- copy *top-addr
595         top <- decrement
596         var offset/edx: (offset value) <- compute-offset data, top
597         var top-val/edx: (addr value) <- index data, offset
598         # read next val from out
599         top <- decrement
600         var offset/ebx: (offset value) <- compute-offset data, top
601         var pen-top-val/ebx: (addr value) <- index data, offset
602         # swap
603         var tmp: value
604         var tmp-a/eax: (addr value) <- address tmp
605         copy-object top-val, tmp-a
606         copy-object pen-top-val, top-val
607         copy-object tmp-a, pen-top-val
608         break $evaluate:process-word
609       }
610       ### if curr-stream defines a binding, save top of stack to bindings
611       {
612         var done?/eax: boolean <- stream-empty? curr-stream
613         compare done?, 0  # false
614         break-if-!=
615         var new-byte/eax: byte <- read-byte curr-stream
616         compare new-byte, 0x3d  # '='
617         break-if-!=
618         # pop target-val from out
619         var out2/esi: (addr value-stack) <- copy out
620         var top-addr/ecx: (addr int) <- get out2, top
621         compare *top-addr, 0
622         break-if-<=
623         var data-ah/eax: (addr handle array value) <- get out2, data
624         var data/eax: (addr array value) <- lookup *data-ah
625         var top/edx: int <- copy *top-addr
626         top <- decrement
627         var dest-offset/edx: (offset value) <- compute-offset data, top
628         var target-val/edx: (addr value) <- index data, dest-offset
629         # create binding from curr-stream to target-val
630         var key-h: (handle array byte)
631         var key/ecx: (addr handle array byte) <- address key-h
632         stream-to-array curr-stream, key
633         bind-in-table bindings, key, target-val
634         break $evaluate:process-word
635       }
636       rewind-stream curr-stream
637       ### if curr-stream is a known function name, call it appropriately
638       {
639         var callee-h: (handle function)
640         var callee-ah/eax: (addr handle function) <- address callee-h
641         find-function functions, curr-stream, callee-ah
642         var callee/eax: (addr function) <- lookup *callee-ah
643         compare callee, 0
644         break-if-=
645         perform-call callee, out, functions
646         break $evaluate:process-word
647       }
648       ### if it's a name, push its value
649       {
650         compare bindings, 0
651         break-if-=
652         var tmp: (handle array byte)
653         var curr-string-ah/edx: (addr handle array byte) <- address tmp
654         stream-to-array curr-stream, curr-string-ah  # unfortunate leak
655         var curr-string/eax: (addr array byte) <- lookup *curr-string-ah
656         var val-storage: (handle value)
657         var val-ah/edi: (addr handle value) <- address val-storage
658         lookup-binding bindings, curr-string, val-ah
659         var val/eax: (addr value) <- lookup *val-ah
660         compare val, 0
661         break-if-=
662         push-value-stack out, val
663         break $evaluate:process-word
664       }
665       ### if the word starts with a quote and ends with a quote, turn it into a string
666       {
667         var start/eax: byte <- stream-first curr-stream
668         compare start, 0x22  # double-quote
669         break-if-!=
670         var end/eax: byte <- stream-final curr-stream
671         compare end, 0x22  # double-quote
672         break-if-!=
673         var h: (handle array byte)
674         var s/eax: (addr handle array byte) <- address h
675         unquote-stream-to-array curr-stream, s  # leak
676         push-string-to-value-stack out, *s
677         break $evaluate:process-word
678       }
679       ### if the word starts with a '[' and ends with a ']', turn it into an array
680       {
681         var start/eax: byte <- stream-first curr-stream
682         compare start, 0x5b  # '['
683         break-if-!=
684         var end/eax: byte <- stream-final curr-stream
685         compare end, 0x5d  # ']'
686         break-if-!=
687         # wastefully create a new input string to strip quotes
688         var h: (handle array value)
689         var input-ah/eax: (addr handle array byte) <- address h
690         unquote-stream-to-array curr-stream, input-ah  # leak
691         # wastefully parse input into int-array
692         # TODO: support parsing arrays of other types
693         var input/eax: (addr array byte) <- lookup *input-ah
694         var h2: (handle array int)
695         var int-array-ah/esi: (addr handle array int) <- address h2
696         parse-array-of-decimal-ints input, int-array-ah  # leak
697         var _int-array/eax: (addr array int) <- lookup *int-array-ah
698         var int-array/esi: (addr array int) <- copy _int-array
699         var len/ebx: int <- length int-array
700         # push value-array of same size as int-array
701         var h3: (handle array value)
702         var value-array-ah/eax: (addr handle array value) <- address h3
703         populate value-array-ah, len
704         push-array-to-value-stack out, *value-array-ah
705         # copy int-array into value-array
706         var _value-array/eax: (addr array value) <- lookup *value-array-ah
707         var value-array/edi: (addr array value) <- copy _value-array
708         var i/eax: int <- copy 0
709         {
710           compare i, len
711           break-if->=
712           var src-addr/ecx: (addr int) <- index int-array, i
713           var src/ecx: int <- copy *src-addr
714           var src-f/xmm0: float <- convert src
715           var dest-offset/edx: (offset value) <- compute-offset value-array, i
716           var dest-val/edx: (addr value) <- index value-array, dest-offset
717           var dest/edx: (addr float) <- get dest-val, number-data
718           copy-to *dest, src-f
719           i <- increment
720           loop
721         }
722         break $evaluate:process-word
723       }
724       ### otherwise assume it's a literal number and push it
725       {
726         var n/eax: int <- parse-decimal-int-from-stream curr-stream
727         var n-f/xmm0: float <- convert n
728         push-number-to-value-stack out, n-f
729       }
730     }
731     # termination check
732     compare curr, end
733     break-if-=
734     # update
735     var next-word-ah/edx: (addr handle word) <- get curr, next
736     curr <- lookup *next-word-ah
737     #
738     loop
739   }
740   # process next line if necessary
741   var line/eax: (addr line) <- copy scratch
742   var next-line-ah/eax: (addr handle line) <- get line, next
743   var next-line/eax: (addr line) <- lookup *next-line-ah
744   compare next-line, 0
745   break-if-=
746   evaluate functions, bindings, next-line, end, out
747 }
748 
749 fn test-evaluate {
750   var line-storage: line
751   var line/esi: (addr line) <- address line-storage
752   var first-word-ah/eax: (addr handle word) <- get line-storage, data
753   allocate-word-with first-word-ah, "3"
754   append-word-with *first-word-ah, "=a"
755   var next-line-ah/eax: (addr handle line) <- get line-storage, next
756   allocate next-line-ah
757   var next-line/eax: (addr line) <- lookup *next-line-ah
758   var first-word-ah/eax: (addr handle word) <- get next-line, data
759   allocate-word-with first-word-ah, "a"
760   var functions-storage: (handle function)
761   var functions/ecx: (addr handle function) <- address functions-storage
762   var table-storage: table
763   var table/ebx: (addr table) <- address table-storage
764   initialize-table table, 0x10
765   var stack-storage: value-stack
766   var stack/edi: (addr value-stack) <- address stack-storage
767   initialize-value-stack stack, 0x10
768   evaluate functions, table, line, 0, stack
769   var x-f/xmm0: float <- pop-number-from-value-stack stack
770   var x/eax: int <- convert x-f
771   check-ints-equal x, 3, "F - test-evaluate"
772 }
773 
774 fn find-function first: (addr handle function), name: (addr stream byte), out: (addr handle function) {
775   var curr/esi: (addr handle function) <- copy first
776   $find-function:loop: {
777     var _f/eax: (addr function) <- lookup *curr
778     var f/ecx: (addr function) <- copy _f
779     compare f, 0
780     break-if-=
781     var curr-name-ah/eax: (addr handle array byte) <- get f, name
782     var curr-name/eax: (addr array byte) <- lookup *curr-name-ah
783     var done?/eax: boolean <- stream-data-equal? name, curr-name
784     compare done?, 0  # false
785     {
786       break-if-=
787       copy-handle *curr, out
788       break $find-function:loop
789     }
790     curr <- get f, next
791     loop
792   }
793 }
794 
795 fn perform-call _callee: (addr function), caller-stack: (addr value-stack), functions: (addr handle function) {
796   var callee/ecx: (addr function) <- copy _callee
797   # create bindings for args
798   var table-storage: table
799   var table/esi: (addr table) <- address table-storage
800   initialize-table table, 0x10
801   bind-args callee, caller-stack, table
802   # obtain body
803   var body-ah/eax: (addr handle line) <- get callee, body
804   var body/eax: (addr line) <- lookup *body-ah
805   # perform call
806   var stack-storage: value-stack
807   var stack/edi: (addr value-stack) <- address stack-storage
808   initialize-value-stack stack, 0x10
809 #?   print-string-to-real-screen "about to enter recursive eval\n"
810   evaluate functions, table, body, 0, stack
811 #?   print-string-to-real-screen "exited recursive eval\n"
812   # pop target-val from out
813   var top-addr/ecx: (addr int) <- get stack, top
814   compare *top-addr, 0
815   break-if-<=
816   var data-ah/eax: (addr handle array value) <- get stack, data
817   var data/eax: (addr array value) <- lookup *data-ah
818   var top/edx: int <- copy *top-addr
819   top <- decrement
820   var dest-offset/edx: (offset value) <- compute-offset data, top
821   var target-val/edx: (addr value) <- index data, dest-offset
822   # stitch target-val into caller-stack
823   push-value-stack caller-stack, target-val
824 }
825 
826 # pop args from the caller-stack and bind them to successive args
827 # implies: function args are stored in reverse order
828 fn bind-args _callee: (addr function), _caller-stack: (addr value-stack), table: (addr table) {
829   var callee/ecx: (addr function) <- copy _callee
830   var curr-arg-ah/eax: (addr handle word) <- get callee, args
831   var curr-arg/eax: (addr word) <- lookup *curr-arg-ah
832   #
833   var curr-key-storage: (handle array byte)
834   var curr-key/edx: (addr handle array byte) <- address curr-key-storage
835   {
836     compare curr-arg, 0
837     break-if-=
838     # create binding
839     word-to-string curr-arg, curr-key
840     {
841       # pop target-val from caller-stack
842       var caller-stack/esi: (addr value-stack) <- copy _caller-stack
843       var top-addr/ecx: (addr int) <- get caller-stack, top
844       compare *top-addr, 0
845       break-if-<=
846       decrement *top-addr
847       var data-ah/eax: (addr handle array value) <- get caller-stack, data
848       var data/eax: (addr array value) <- lookup *data-ah
849       var top/ebx: int <- copy *top-addr
850       var dest-offset/ebx: (offset value) <- compute-offset data, top
851       var target-val/ebx: (addr value) <- index data, dest-offset
852       # create binding from curr-key to target-val
853       bind-in-table table, curr-key, target-val
854     }
855     #
856     var next-arg-ah/edx: (addr handle word) <- get curr-arg, next
857     curr-arg <- lookup *next-arg-ah
858     loop
859   }
860 }
861 
862 # Copy of 'simplify' that just tracks the maximum stack depth needed
863 # Doesn't actually need to simulate the stack, since every word has a predictable effect.
864 fn max-stack-depth first-word: (addr word), final-word: (addr word) -> _/edi: int {
865   var curr-word/eax: (addr word) <- copy first-word
866   var curr-depth/ecx: int <- copy 0
867   var result/edi: int <- copy 0
868   $max-stack-depth:loop: {
869     $max-stack-depth:process-word: {
870       # handle operators
871       {
872         var is-add?/eax: boolean <- word-equal? curr-word, "+"
873         compare is-add?, 0
874         break-if-=
875         curr-depth <- decrement
876         break $max-stack-depth:process-word
877       }
878       {
879         var is-sub?/eax: boolean <- word-equal? curr-word, "-"
880         compare is-sub?, 0
881         break-if-=
882         curr-depth <- decrement
883         break $max-stack-depth:process-word
884       }
885       {
886         var is-mul?/eax: boolean <- word-equal? curr-word, "*"
887         compare is-mul?, 0
888         break-if-=
889         curr-depth <- decrement
890         break $max-stack-depth:process-word
891       }
892       # otherwise it's an int (do we need error-checking?)
893       curr-depth <- increment
894       # update max depth if necessary
895       {
896         compare curr-depth, result
897         break-if-<=
898         result <- copy curr-depth
899       }
900     }
901     # if curr-word == final-word break
902     compare curr-word, final-word
903     break-if-=
904     # curr-word = curr-word->next
905     var next-word-ah/edx: (addr handle word) <- get curr-word, next
906     curr-word <- lookup *next-word-ah
907     #
908     loop
909   }
910   return result
911 }