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