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