about summary refs log tree commit diff stats
path: root/linux/tile/rpn.mu
diff options
context:
space:
mode:
Diffstat (limited to 'linux/tile/rpn.mu')
-rw-r--r--linux/tile/rpn.mu911
1 files changed, 911 insertions, 0 deletions
diff --git a/linux/tile/rpn.mu b/linux/tile/rpn.mu
new file mode 100644
index 00000000..bf81308b
--- /dev/null
+++ b/linux/tile/rpn.mu
@@ -0,0 +1,911 @@
+fn evaluate functions: (addr handle function), bindings: (addr table), scratch: (addr line), end: (addr word), out: (addr value-stack) {
+  var line/eax: (addr line) <- copy scratch
+  var word-ah/eax: (addr handle word) <- get line, data
+  var curr/eax: (addr word) <- lookup *word-ah
+  var curr-stream-storage: (stream byte 0x10)
+  var curr-stream/edi: (addr stream byte) <- address curr-stream-storage
+  clear-value-stack out
+  $evaluate:loop: {
+    # precondition (should never hit)
+    compare curr, 0
+    break-if-=
+    # update curr-stream
+    emit-word curr, curr-stream
+#?     print-string-to-real-screen "eval: "
+#?     print-stream-to-real-screen curr-stream
+#?     print-string-to-real-screen "\n"
+    $evaluate:process-word: {
+      ### if curr-stream is an operator, perform it
+      ## numbers
+      {
+        var is-add?/eax: boolean <- stream-data-equal? curr-stream, "+"
+        compare is-add?, 0
+        break-if-=
+        var _b/xmm0: float <- pop-number-from-value-stack out
+        var b/xmm1: float <- copy _b
+        var a/xmm0: float <- pop-number-from-value-stack out
+        a <- add b
+        push-number-to-value-stack out, a
+        break $evaluate:process-word
+      }
+      {
+        var is-sub?/eax: boolean <- stream-data-equal? curr-stream, "-"
+        compare is-sub?, 0
+        break-if-=
+        var _b/xmm0: float <- pop-number-from-value-stack out
+        var b/xmm1: float <- copy _b
+        var a/xmm0: float <- pop-number-from-value-stack out
+        a <- subtract b
+        push-number-to-value-stack out, a
+        break $evaluate:process-word
+      }
+      {
+        var is-mul?/eax: boolean <- stream-data-equal? curr-stream, "*"
+        compare is-mul?, 0
+        break-if-=
+        var _b/xmm0: float <- pop-number-from-value-stack out
+        var b/xmm1: float <- copy _b
+        var a/xmm0: float <- pop-number-from-value-stack out
+        a <- multiply b
+        push-number-to-value-stack out, a
+        break $evaluate:process-word
+      }
+      {
+        var is-div?/eax: boolean <- stream-data-equal? curr-stream, "/"
+        compare is-div?, 0
+        break-if-=
+        var _b/xmm0: float <- pop-number-from-value-stack out
+        var b/xmm1: float <- copy _b
+        var a/xmm0: float <- pop-number-from-value-stack out
+        a <- divide b
+        push-number-to-value-stack out, a
+        break $evaluate:process-word
+      }
+      {
+        var is-sqrt?/eax: boolean <- stream-data-equal? curr-stream, "sqrt"
+        compare is-sqrt?, 0
+        break-if-=
+        var a/xmm0: float <- pop-number-from-value-stack out
+        a <- square-root a
+        push-number-to-value-stack out, a
+        break $evaluate:process-word
+      }
+      ## strings/arrays
+      {
+        var is-len?/eax: boolean <- stream-data-equal? curr-stream, "len"
+        compare is-len?, 0
+        break-if-=
+#?         print-string 0, "is len\n"
+        # pop target-val from out
+        var out2/esi: (addr value-stack) <- copy out
+        var top-addr/ecx: (addr int) <- get out2, top
+        compare *top-addr, 0
+        break-if-<=
+#?         print-string 0, "stack has stuff\n"
+        var data-ah/eax: (addr handle array value) <- get out2, data
+        var data/eax: (addr array value) <- lookup *data-ah
+        var top/edx: int <- copy *top-addr
+        top <- decrement
+        var dest-offset/edx: (offset value) <- compute-offset data, top
+        var target-val/edx: (addr value) <- index data, dest-offset
+        # check target-val is a string or array
+        var target-type-addr/eax: (addr int) <- get target-val, type
+        compare *target-type-addr, 1/string
+        {
+          break-if-!=
+          # compute length
+          var src-ah/eax: (addr handle array byte) <- get target-val, text-data
+          var src/eax: (addr array byte) <- lookup *src-ah
+          var result/ebx: int <- length src
+          var result-f/xmm0: float <- convert result
+          # save result into target-val
+          var type-addr/eax: (addr int) <- get target-val, type
+          copy-to *type-addr, 0/int
+          var target-string-ah/eax: (addr handle array byte) <- get target-val, text-data
+          clear-object target-string-ah
+          var target/eax: (addr float) <- get target-val, number-data
+          copy-to *target, result-f
+          break $evaluate:process-word
+        }
+        compare *target-type-addr, 2/array
+        {
+          break-if-!=
+          # compute length
+          var src-ah/eax: (addr handle array value) <- get target-val, array-data
+          var src/eax: (addr array value) <- lookup *src-ah
+          var result/ebx: int <- length src
+          var result-f/xmm0: float <- convert result
+          # save result into target-val
+          var type-addr/eax: (addr int) <- get target-val, type
+          copy-to *type-addr, 0/int
+          var target-array-ah/eax: (addr handle array value) <- get target-val, array-data
+          clear-object target-array-ah
+          var target/eax: (addr float) <- get target-val, number-data
+          copy-to *target, result-f
+          break $evaluate:process-word
+        }
+      }
+      ## files
+      {
+        var is-open?/eax: boolean <- stream-data-equal? curr-stream, "open"
+        compare is-open?, 0
+        break-if-=
+        # pop target-val from out
+        var out2/esi: (addr value-stack) <- copy out
+        var top-addr/ecx: (addr int) <- get out2, top
+        compare *top-addr, 0
+        break-if-<=
+        var data-ah/eax: (addr handle array value) <- get out2, data
+        var data/eax: (addr array value) <- lookup *data-ah
+        var top/edx: int <- copy *top-addr
+        top <- decrement
+        var dest-offset/edx: (offset value) <- compute-offset data, top
+        var target-val/edx: (addr value) <- index data, dest-offset
+        # check target-val is a string
+        var target-type-addr/eax: (addr int) <- get target-val, type
+        compare *target-type-addr, 1/string
+        break-if-!=
+        # open target-val as a filename and save the handle in target-val
+        var src-ah/eax: (addr handle array byte) <- get target-val, text-data
+        var src/eax: (addr array byte) <- lookup *src-ah
+        var result-ah/ecx: (addr handle buffered-file) <- get target-val, file-data
+        open src, 0, result-ah  # write? = false
+        # save result into target-val
+        var type-addr/eax: (addr int) <- get target-val, type
+        copy-to *type-addr, 3/file
+        var target-string-ah/eax: (addr handle array byte) <- get target-val, text-data
+        var filename-ah/ecx: (addr handle array byte) <- get target-val, filename
+        copy-object target-string-ah, filename-ah
+        clear-object target-string-ah
+        break $evaluate:process-word
+      }
+      {
+        var is-read?/eax: boolean <- stream-data-equal? curr-stream, "read"
+        compare is-read?, 0
+        break-if-=
+        # pop target-val from out
+        var out2/esi: (addr value-stack) <- copy out
+        var top-addr/ecx: (addr int) <- get out2, top
+        compare *top-addr, 0
+        break-if-<=
+        var data-ah/eax: (addr handle array value) <- get out2, data
+        var data/eax: (addr array value) <- lookup *data-ah
+        var top/edx: int <- copy *top-addr
+        top <- decrement
+        var dest-offset/edx: (offset value) <- compute-offset data, top
+        var target-val/edx: (addr value) <- index data, dest-offset
+        # check target-val is a file
+        var target-type-addr/eax: (addr int) <- get target-val, type
+        compare *target-type-addr, 3/file
+        break-if-!=
+        # read a line from the file and save in target-val
+        # read target-val as a filename and save the handle in target-val
+        var file-ah/eax: (addr handle buffered-file) <- get target-val, file-data
+        var file/eax: (addr buffered-file) <- lookup *file-ah
+        var s: (stream byte 0x100)
+        var s-addr/ecx: (addr stream byte) <- address s
+        read-line-buffered file, s-addr
+        var target/eax: (addr handle array byte) <- get target-val, text-data
+        stream-to-array s-addr, target
+        # save result into target-val
+        var type-addr/eax: (addr int) <- get target-val, type
+        copy-to *type-addr, 1/string
+        var target-file-ah/eax: (addr handle buffered-file) <- get target-val, file-data
+        clear-object target-file-ah
+        break $evaluate:process-word
+      }
+      {
+        var is-slurp?/eax: boolean <- stream-data-equal? curr-stream, "slurp"
+        compare is-slurp?, 0
+        break-if-=
+        # pop target-val from out
+        var out2/esi: (addr value-stack) <- copy out
+        var top-addr/ecx: (addr int) <- get out2, top
+        compare *top-addr, 0
+        break-if-<=
+        var data-ah/eax: (addr handle array value) <- get out2, data
+        var data/eax: (addr array value) <- lookup *data-ah
+        var top/edx: int <- copy *top-addr
+        top <- decrement
+        var dest-offset/edx: (offset value) <- compute-offset data, top
+        var target-val/edx: (addr value) <- index data, dest-offset
+        # check target-val is a file
+        var target-type-addr/eax: (addr int) <- get target-val, type
+        compare *target-type-addr, 3/file
+        break-if-!=
+        # slurp all contents from file and save in target-val
+        # read target-val as a filename and save the handle in target-val
+        var file-ah/eax: (addr handle buffered-file) <- get target-val, file-data
+        var file/eax: (addr buffered-file) <- lookup *file-ah
+        var s: (stream byte 0x100)
+        var s-addr/ecx: (addr stream byte) <- address s
+        slurp file, s-addr
+        var target/eax: (addr handle array byte) <- get target-val, text-data
+        stream-to-array s-addr, target
+        # save result into target-val
+        var type-addr/eax: (addr int) <- get target-val, type
+        copy-to *type-addr, 1/string
+        var target-file-ah/eax: (addr handle buffered-file) <- get target-val, file-data
+        clear-object target-file-ah
+        break $evaluate:process-word
+      }
+      {
+        var is-lines?/eax: boolean <- stream-data-equal? curr-stream, "lines"
+        compare is-lines?, 0
+        break-if-=
+        # pop target-val from out
+        var out2/esi: (addr value-stack) <- copy out
+        var top-addr/ecx: (addr int) <- get out2, top
+        compare *top-addr, 0
+        break-if-<=
+        var data-ah/eax: (addr handle array value) <- get out2, data
+        var data/eax: (addr array value) <- lookup *data-ah
+        var top/edx: int <- copy *top-addr
+        top <- decrement
+        var dest-offset/edx: (offset value) <- compute-offset data, top
+        var target-val/edx: (addr value) <- index data, dest-offset
+        # check target-val is a file
+        var target-type-addr/eax: (addr int) <- get target-val, type
+        compare *target-type-addr, 3/file
+        break-if-!=
+        # read all lines from file and save as an array of strings in target-val
+        # read target-val as a filename and save the handle in target-val
+        var file-ah/eax: (addr handle buffered-file) <- get target-val, file-data
+        var file/eax: (addr buffered-file) <- lookup *file-ah
+        var s: (stream byte 0x100)
+        var s-addr/ecx: (addr stream byte) <- address s
+        slurp file, s-addr
+        var tmp-ah/eax: (addr handle array byte) <- get target-val, text-data
+        stream-to-array s-addr, tmp-ah
+        var tmp/eax: (addr array byte) <- lookup *tmp-ah
+#?         enable-screen-type-mode
+#?         print-string 0, tmp
+        var h: (handle array (handle array byte))
+        {
+          var ah/edx: (addr handle array (handle array byte)) <- address h
+          split-string tmp, 0xa, ah
+        }
+        var target/eax: (addr handle array value) <- get target-val, array-data
+        save-lines h, target
+        # save result into target-val
+        var type-addr/eax: (addr int) <- get target-val, type
+        copy-to *type-addr, 2/array
+        var target-file-ah/eax: (addr handle buffered-file) <- get target-val, file-data
+        var empty-file: (handle buffered-file)
+        copy-handle empty-file, target-file-ah
+        var target-text-ah/eax: (addr handle array byte) <- get target-val, text-data
+        var empty-text: (handle array byte)
+        copy-handle empty-text, target-text-ah
+        break $evaluate:process-word
+      }
+      ## screens
+      {
+        var is-fake-screen?/eax: boolean <- stream-data-equal? curr-stream, "fake-screen"
+        compare is-fake-screen?, 0
+        break-if-=
+        var out2/esi: (addr value-stack) <- copy out
+        var top-addr/ecx: (addr int) <- get out2, top
+        compare *top-addr, 0
+        break-if-<=
+        # pop width and height from out
+        var nrows-f/xmm0: float <- pop-number-from-value-stack out2
+        var nrows/edx: int <- convert nrows-f
+        var ncols-f/xmm0: float <- pop-number-from-value-stack out2
+        var ncols/ebx: int <- convert ncols-f
+        # define a new screen with those dimensions
+        var screen-h: (handle screen)
+        var screen-ah/eax: (addr handle screen) <- address screen-h
+        allocate screen-ah
+        var screen/eax: (addr screen) <- lookup screen-h
+        initialize-screen screen, nrows, ncols
+        # push screen to stack
+        var data-ah/eax: (addr handle array value) <- get out2, data
+        var data/eax: (addr array value) <- lookup *data-ah
+        var top/edx: int <- copy *top-addr
+        increment *top-addr
+        var dest-offset/edx: (offset value) <- compute-offset data, top
+        var target-val/edx: (addr value) <- index data, dest-offset
+        var type/eax: (addr int) <- get target-val, type
+        copy-to *type, 4/screen
+        var dest/eax: (addr handle screen) <- get target-val, screen-data
+        copy-handle screen-h, dest
+        break $evaluate:process-word
+      }
+      {
+        var is-print?/eax: boolean <- stream-data-equal? curr-stream, "print"
+        compare is-print?, 0
+        break-if-=
+        var out2/esi: (addr value-stack) <- copy out
+        var top-addr/ecx: (addr int) <- get out2, top
+        compare *top-addr, 0
+        break-if-<=
+        # pop string from out
+        var top-addr/ecx: (addr int) <- get out2, top
+        compare *top-addr, 0
+        break-if-<=
+        decrement *top-addr
+        var data-ah/eax: (addr handle array value) <- get out2, data
+        var _data/eax: (addr array value) <- lookup *data-ah
+        var data/edi: (addr array value) <- copy _data
+        var top/eax: int <- copy *top-addr
+        var dest-offset/edx: (offset value) <- compute-offset data, top
+        var s/esi: (addr value) <- index data, dest-offset
+        # select target screen from top of out (but don't pop it)
+        compare *top-addr, 0
+        break-if-<=
+        var top/eax: int <- copy *top-addr
+        top <- decrement
+        var dest-offset/edx: (offset value) <- compute-offset data, top
+        var target-val/edx: (addr value) <- index data, dest-offset
+        var type/eax: (addr int) <- get target-val, type
+        compare *type, 4/screen
+        break-if-!=
+        # print string to target screen
+        var dest-ah/eax: (addr handle screen) <- get target-val, screen-data
+        var dest/eax: (addr screen) <- lookup *dest-ah
+        var r/ecx: (addr int) <- get dest, cursor-row
+        var c/edx: (addr int) <- get dest, cursor-col
+        render-value-at dest, *r, *c, s, 0
+        break $evaluate:process-word
+      }
+      {
+        var is-move?/eax: boolean <- stream-data-equal? curr-stream, "move"
+        compare is-move?, 0
+        break-if-=
+        var out2/esi: (addr value-stack) <- copy out
+        # pop args
+        var r-f/xmm0: float <- pop-number-from-value-stack out2
+        var r/ecx: int <- convert r-f
+        var c-f/xmm0: float <- pop-number-from-value-stack out2
+        var c/edx: int <- convert c-f
+        # select screen from top of out (but don't pop it)
+        var top-addr/ebx: (addr int) <- get out2, top
+        compare *top-addr, 0
+        break-if-<=
+        var data-ah/eax: (addr handle array value) <- get out2, data
+        var _data/eax: (addr array value) <- lookup *data-ah
+        var data/edi: (addr array value) <- copy _data
+        var top/eax: int <- copy *top-addr
+        top <- decrement
+        var target-offset/eax: (offset value) <- compute-offset data, top
+        var target-val/ebx: (addr value) <- index data, target-offset
+        var type/eax: (addr int) <- get target-val, type
+        compare *type, 4/screen
+        break-if-!=
+        var target-ah/eax: (addr handle screen) <- get target-val, screen-data
+        var target/eax: (addr screen) <- lookup *target-ah
+        move-cursor target, r, c
+        break $evaluate:process-word
+      }
+      {
+        var is-up?/eax: boolean <- stream-data-equal? curr-stream, "up"
+        compare is-up?, 0
+        break-if-=
+        var out2/esi: (addr value-stack) <- copy out
+        var top-addr/ebx: (addr int) <- get out2, top
+        compare *top-addr, 0
+        break-if-<=
+        # pop args
+        var d-f/xmm0: float <- pop-number-from-value-stack out2
+        var d/ecx: int <- convert d-f
+        # select screen from top of out (but don't pop it)
+        compare *top-addr, 0
+        break-if-<=
+        var data-ah/eax: (addr handle array value) <- get out2, data
+        var _data/eax: (addr array value) <- lookup *data-ah
+        var data/edi: (addr array value) <- copy _data
+        var top/eax: int <- copy *top-addr
+        top <- decrement
+        var target-offset/eax: (offset value) <- compute-offset data, top
+        var target-val/ebx: (addr value) <- index data, target-offset
+        var type/eax: (addr int) <- get target-val, type
+        compare *type, 4/screen
+        break-if-!=
+        var target-ah/eax: (addr handle screen) <- get target-val, screen-data
+        var _target/eax: (addr screen) <- lookup *target-ah
+        var target/edi: (addr screen) <- copy _target
+        var r/edx: (addr int) <- get target, cursor-row
+        var c/eax: (addr int) <- get target, cursor-col
+        var col/eax: int <- copy *c
+        {
+          compare d, 0
+          break-if-<=
+          compare *r, 1
+          break-if-<=
+          print-string target "│"
+          decrement *r
+          move-cursor target, *r, col
+          d <- decrement
+          loop
+        }
+        break $evaluate:process-word
+      }
+      {
+        var is-down?/eax: boolean <- stream-data-equal? curr-stream, "down"
+        compare is-down?, 0
+        break-if-=
+        var out2/esi: (addr value-stack) <- copy out
+        var top-addr/ebx: (addr int) <- get out2, top
+        compare *top-addr, 0
+        break-if-<=
+        # pop args
+        var d-f/xmm0: float <- pop-number-from-value-stack out2
+        var d/ecx: int <- convert d-f
+        # select screen from top of out (but don't pop it)
+        compare *top-addr, 0
+        break-if-<=
+        var data-ah/eax: (addr handle array value) <- get out2, data
+        var _data/eax: (addr array value) <- lookup *data-ah
+        var data/edi: (addr array value) <- copy _data
+        var top/eax: int <- copy *top-addr
+        top <- decrement
+        var target-offset/eax: (offset value) <- compute-offset data, top
+        var target-val/ebx: (addr value) <- index data, target-offset
+        var type/eax: (addr int) <- get target-val, type
+        compare *type, 4/screen
+        break-if-!=
+        var target-ah/eax: (addr handle screen) <- get target-val, screen-data
+        var _target/eax: (addr screen) <- lookup *target-ah
+        var target/edi: (addr screen) <- copy _target
+        var bound-a/ebx: (addr int) <- get target, num-rows
+        var bound/ebx: int <- copy *bound-a
+        var r/edx: (addr int) <- get target, cursor-row
+        var c/eax: (addr int) <- get target, cursor-col
+        var col/eax: int <- copy *c
+        {
+          compare d, 0
+          break-if-<=
+          compare *r, bound
+          break-if->=
+          print-string target "│"
+          increment *r
+          move-cursor target, *r, col
+          d <- decrement
+          loop
+        }
+        break $evaluate:process-word
+      }
+      {
+        var is-left?/eax: boolean <- stream-data-equal? curr-stream, "left"
+        compare is-left?, 0
+        break-if-=
+        var out2/esi: (addr value-stack) <- copy out
+        var top-addr/ebx: (addr int) <- get out2, top
+        compare *top-addr, 0
+        break-if-<=
+        # pop args
+        var d-f/xmm0: float <- pop-number-from-value-stack out2
+        var d/ecx: int <- convert d-f
+        # select screen from top of out (but don't pop it)
+        compare *top-addr, 0
+        break-if-<=
+        var data-ah/eax: (addr handle array value) <- get out2, data
+        var _data/eax: (addr array value) <- lookup *data-ah
+        var data/edi: (addr array value) <- copy _data
+        var top/eax: int <- copy *top-addr
+        top <- decrement
+        var target-offset/eax: (offset value) <- compute-offset data, top
+        var target-val/ebx: (addr value) <- index data, target-offset
+        var type/eax: (addr int) <- get target-val, type
+        compare *type, 4/screen
+        break-if-!=
+        var target-ah/eax: (addr handle screen) <- get target-val, screen-data
+        var _target/eax: (addr screen) <- lookup *target-ah
+        var target/edi: (addr screen) <- copy _target
+        var c/edx: (addr int) <- get target, cursor-col
+        var r/eax: (addr int) <- get target, cursor-row
+        var row/eax: int <- copy *r
+        {
+          compare d, 0
+          break-if-<=
+          compare *c, 1
+          break-if-<=
+          print-string target "─"
+          decrement *c
+          decrement *c  # second one to undo the print above
+          move-cursor target, row, *c
+          d <- decrement
+          loop
+        }
+        break $evaluate:process-word
+      }
+      {
+        var is-right?/eax: boolean <- stream-data-equal? curr-stream, "right"
+        compare is-right?, 0
+        break-if-=
+        var out2/esi: (addr value-stack) <- copy out
+        var top-addr/ebx: (addr int) <- get out2, top
+        compare *top-addr, 0
+        break-if-<=
+        # pop args
+        var _d/xmm0: float <- pop-number-from-value-stack out2
+        var d/ecx: int <- convert _d
+        # select screen from top of out (but don't pop it)
+        compare *top-addr, 0
+        break-if-<=
+        var data-ah/eax: (addr handle array value) <- get out2, data
+        var _data/eax: (addr array value) <- lookup *data-ah
+        var data/edi: (addr array value) <- copy _data
+        var top/eax: int <- copy *top-addr
+        top <- decrement
+        var target-offset/eax: (offset value) <- compute-offset data, top
+        var target-val/ebx: (addr value) <- index data, target-offset
+        var type/eax: (addr int) <- get target-val, type
+        compare *type, 4/screen
+        break-if-!=
+        var target-ah/eax: (addr handle screen) <- get target-val, screen-data
+        var _target/eax: (addr screen) <- lookup *target-ah
+        var target/edi: (addr screen) <- copy _target
+        var bound-a/ebx: (addr int) <- get target, num-rows
+        var bound/ebx: int <- copy *bound-a
+        var c/edx: (addr int) <- get target, cursor-col
+        var r/eax: (addr int) <- get target, cursor-row
+        var row/eax: int <- copy *r
+        {
+          compare d, 0
+          break-if-<=
+          compare *c, bound
+          break-if->=
+          print-string target "─"
+          # no increment; the print took care of it
+          move-cursor target, row, *c
+          d <- decrement
+          loop
+        }
+        break $evaluate:process-word
+      }
+      ## HACKS: we're trying to avoid turning this into Forth
+      {
+        var is-dup?/eax: boolean <- stream-data-equal? curr-stream, "dup"
+        compare is-dup?, 0
+        break-if-=
+        # read src-val from out
+        var out2/esi: (addr value-stack) <- copy out
+        var top-addr/ecx: (addr int) <- get out2, top
+        compare *top-addr, 0
+        break-if-<=
+        var data-ah/eax: (addr handle array value) <- get out2, data
+        var data/eax: (addr array value) <- lookup *data-ah
+        var top/ecx: int <- copy *top-addr
+        top <- decrement
+        var offset/edx: (offset value) <- compute-offset data, top
+        var src-val/edx: (addr value) <- index data, offset
+        # push a copy of it
+        top <- increment
+        var offset/ebx: (offset value) <- compute-offset data, top
+        var target-val/ebx: (addr value) <- index data, offset
+        copy-object src-val, target-val
+        # commit
+        var top-addr/ecx: (addr int) <- get out2, top
+        increment *top-addr
+        break $evaluate:process-word
+      }
+      {
+        var is-swap?/eax: boolean <- stream-data-equal? curr-stream, "swap"
+        compare is-swap?, 0
+        break-if-=
+        # read top-val from out
+        var out2/esi: (addr value-stack) <- copy out
+        var top-addr/ecx: (addr int) <- get out2, top
+        compare *top-addr, 0
+        break-if-<=
+        var data-ah/eax: (addr handle array value) <- get out2, data
+        var data/eax: (addr array value) <- lookup *data-ah
+        var top/ecx: int <- copy *top-addr
+        top <- decrement
+        var offset/edx: (offset value) <- compute-offset data, top
+        var top-val/edx: (addr value) <- index data, offset
+        # read next val from out
+        top <- decrement
+        var offset/ebx: (offset value) <- compute-offset data, top
+        var pen-top-val/ebx: (addr value) <- index data, offset
+        # swap
+        var tmp: value
+        var tmp-a/eax: (addr value) <- address tmp
+        copy-object top-val, tmp-a
+        copy-object pen-top-val, top-val
+        copy-object tmp-a, pen-top-val
+        break $evaluate:process-word
+      }
+      ### if curr-stream defines a binding, save top of stack to bindings
+      {
+        var done?/eax: boolean <- stream-empty? curr-stream
+        compare done?, 0/false
+        break-if-!=
+        var new-byte/eax: byte <- read-byte curr-stream
+        compare new-byte, 0x3d/=
+        break-if-!=
+        # pop target-val from out
+        var out2/esi: (addr value-stack) <- copy out
+        var top-addr/ecx: (addr int) <- get out2, top
+        compare *top-addr, 0
+        break-if-<=
+        var data-ah/eax: (addr handle array value) <- get out2, data
+        var data/eax: (addr array value) <- lookup *data-ah
+        var top/edx: int <- copy *top-addr
+        top <- decrement
+        var dest-offset/edx: (offset value) <- compute-offset data, top
+        var target-val/edx: (addr value) <- index data, dest-offset
+        # create binding from curr-stream to target-val
+        var key-h: (handle array byte)
+        var key/ecx: (addr handle array byte) <- address key-h
+        stream-to-array curr-stream, key
+        bind-in-table bindings, key, target-val
+        break $evaluate:process-word
+      }
+      rewind-stream curr-stream
+      ### if curr-stream is a known function name, call it appropriately
+      {
+        var callee-h: (handle function)
+        var callee-ah/eax: (addr handle function) <- address callee-h
+        find-function functions, curr-stream, callee-ah
+        var callee/eax: (addr function) <- lookup *callee-ah
+        compare callee, 0
+        break-if-=
+        perform-call callee, out, functions
+        break $evaluate:process-word
+      }
+      ### if it's a name, push its value
+      {
+        compare bindings, 0
+        break-if-=
+        var tmp: (handle array byte)
+        var curr-string-ah/edx: (addr handle array byte) <- address tmp
+        stream-to-array curr-stream, curr-string-ah  # unfortunate leak
+        var curr-string/eax: (addr array byte) <- lookup *curr-string-ah
+        var val-storage: (handle value)
+        var val-ah/edi: (addr handle value) <- address val-storage
+        lookup-binding bindings, curr-string, val-ah
+        var val/eax: (addr value) <- lookup *val-ah
+        compare val, 0
+        break-if-=
+        push-value-stack out, val
+        break $evaluate:process-word
+      }
+      ### if the word starts with a quote and ends with a quote, turn it into a string
+      {
+        var start/eax: byte <- stream-first curr-stream
+        compare start, 0x22/double-quote
+        break-if-!=
+        var end/eax: byte <- stream-final curr-stream
+        compare end, 0x22/double-quote
+        break-if-!=
+        var h: (handle array byte)
+        var s/eax: (addr handle array byte) <- address h
+        unquote-stream-to-array curr-stream, s  # leak
+        push-string-to-value-stack out, *s
+        break $evaluate:process-word
+      }
+      ### if the word starts with a '[' and ends with a ']', turn it into an array
+      {
+        var start/eax: byte <- stream-first curr-stream
+        compare start, 0x5b/[
+        break-if-!=
+        var end/eax: byte <- stream-final curr-stream
+        compare end, 0x5d/]
+        break-if-!=
+        # wastefully create a new input string to strip quotes
+        var h: (handle array value)
+        var input-ah/eax: (addr handle array byte) <- address h
+        unquote-stream-to-array curr-stream, input-ah  # leak
+        # wastefully parse input into int-array
+        # TODO: support parsing arrays of other types
+        var input/eax: (addr array byte) <- lookup *input-ah
+        var h2: (handle array int)
+        var int-array-ah/esi: (addr handle array int) <- address h2
+        parse-array-of-decimal-ints input, int-array-ah  # leak
+        var _int-array/eax: (addr array int) <- lookup *int-array-ah
+        var int-array/esi: (addr array int) <- copy _int-array
+        var len/ebx: int <- length int-array
+        # push value-array of same size as int-array
+        var h3: (handle array value)
+        var value-array-ah/eax: (addr handle array value) <- address h3
+        populate value-array-ah, len
+        push-array-to-value-stack out, *value-array-ah
+        # copy int-array into value-array
+        var _value-array/eax: (addr array value) <- lookup *value-array-ah
+        var value-array/edi: (addr array value) <- copy _value-array
+        var i/eax: int <- copy 0
+        {
+          compare i, len
+          break-if->=
+          var src-addr/ecx: (addr int) <- index int-array, i
+          var src/ecx: int <- copy *src-addr
+          var src-f/xmm0: float <- convert src
+          var dest-offset/edx: (offset value) <- compute-offset value-array, i
+          var dest-val/edx: (addr value) <- index value-array, dest-offset
+          var dest/edx: (addr float) <- get dest-val, number-data
+          copy-to *dest, src-f
+          i <- increment
+          loop
+        }
+        break $evaluate:process-word
+      }
+      ### otherwise assume it's a literal number and push it
+      {
+        var n/eax: int <- parse-decimal-int-from-stream curr-stream
+        var n-f/xmm0: float <- convert n
+        push-number-to-value-stack out, n-f
+      }
+    }
+    # termination check
+    compare curr, end
+    break-if-=
+    # update
+    var next-word-ah/edx: (addr handle word) <- get curr, next
+    curr <- lookup *next-word-ah
+    #
+    loop
+  }
+  # process next line if necessary
+  var line/eax: (addr line) <- copy scratch
+  var next-line-ah/eax: (addr handle line) <- get line, next
+  var next-line/eax: (addr line) <- lookup *next-line-ah
+  compare next-line, 0
+  break-if-=
+  evaluate functions, bindings, next-line, end, out
+}
+
+fn test-evaluate {
+  var line-storage: line
+  var line/esi: (addr line) <- address line-storage
+  var first-word-ah/eax: (addr handle word) <- get line-storage, data
+  allocate-word-with first-word-ah, "3"
+  append-word-with *first-word-ah, "=a"
+  var next-line-ah/eax: (addr handle line) <- get line-storage, next
+  allocate next-line-ah
+  var next-line/eax: (addr line) <- lookup *next-line-ah
+  var first-word-ah/eax: (addr handle word) <- get next-line, data
+  allocate-word-with first-word-ah, "a"
+  var functions-storage: (handle function)
+  var functions/ecx: (addr handle function) <- address functions-storage
+  var table-storage: table
+  var table/ebx: (addr table) <- address table-storage
+  initialize-table table, 0x10
+  var stack-storage: value-stack
+  var stack/edi: (addr value-stack) <- address stack-storage
+  initialize-value-stack stack, 0x10
+  evaluate functions, table, line, 0, stack
+  var x-f/xmm0: float <- pop-number-from-value-stack stack
+  var x/eax: int <- convert x-f
+  check-ints-equal x, 3, "F - test-evaluate"
+}
+
+fn find-function first: (addr handle function), name: (addr stream byte), out: (addr handle function) {
+  var curr/esi: (addr handle function) <- copy first
+  $find-function:loop: {
+    var _f/eax: (addr function) <- lookup *curr
+    var f/ecx: (addr function) <- copy _f
+    compare f, 0
+    break-if-=
+    var curr-name-ah/eax: (addr handle array byte) <- get f, name
+    var curr-name/eax: (addr array byte) <- lookup *curr-name-ah
+    var done?/eax: boolean <- stream-data-equal? name, curr-name
+    compare done?, 0/false
+    {
+      break-if-=
+      copy-handle *curr, out
+      break $find-function:loop
+    }
+    curr <- get f, next
+    loop
+  }
+}
+
+fn perform-call _callee: (addr function), caller-stack: (addr value-stack), functions: (addr handle function) {
+  var callee/ecx: (addr function) <- copy _callee
+  # create bindings for args
+  var table-storage: table
+  var table/esi: (addr table) <- address table-storage
+  initialize-table table, 0x10
+  bind-args callee, caller-stack, table
+  # obtain body
+  var body-ah/eax: (addr handle line) <- get callee, body
+  var body/eax: (addr line) <- lookup *body-ah
+  # perform call
+  var stack-storage: value-stack
+  var stack/edi: (addr value-stack) <- address stack-storage
+  initialize-value-stack stack, 0x10
+#?   print-string-to-real-screen "about to enter recursive eval\n"
+  evaluate functions, table, body, 0, stack
+#?   print-string-to-real-screen "exited recursive eval\n"
+  # pop target-val from out
+  var top-addr/ecx: (addr int) <- get stack, top
+  compare *top-addr, 0
+  break-if-<=
+  var data-ah/eax: (addr handle array value) <- get stack, data
+  var data/eax: (addr array value) <- lookup *data-ah
+  var top/edx: int <- copy *top-addr
+  top <- decrement
+  var dest-offset/edx: (offset value) <- compute-offset data, top
+  var target-val/edx: (addr value) <- index data, dest-offset
+  # stitch target-val into caller-stack
+  push-value-stack caller-stack, target-val
+}
+
+# pop args from the caller-stack and bind them to successive args
+# implies: function args are stored in reverse order
+fn bind-args _callee: (addr function), _caller-stack: (addr value-stack), table: (addr table) {
+  var callee/ecx: (addr function) <- copy _callee
+  var curr-arg-ah/eax: (addr handle word) <- get callee, args
+  var curr-arg/eax: (addr word) <- lookup *curr-arg-ah
+  #
+  var curr-key-storage: (handle array byte)
+  var curr-key/edx: (addr handle array byte) <- address curr-key-storage
+  {
+    compare curr-arg, 0
+    break-if-=
+    # create binding
+    word-to-string curr-arg, curr-key
+    {
+      # pop target-val from caller-stack
+      var caller-stack/esi: (addr value-stack) <- copy _caller-stack
+      var top-addr/ecx: (addr int) <- get caller-stack, top
+      compare *top-addr, 0
+      break-if-<=
+      decrement *top-addr
+      var data-ah/eax: (addr handle array value) <- get caller-stack, data
+      var data/eax: (addr array value) <- lookup *data-ah
+      var top/ebx: int <- copy *top-addr
+      var dest-offset/ebx: (offset value) <- compute-offset data, top
+      var target-val/ebx: (addr value) <- index data, dest-offset
+      # create binding from curr-key to target-val
+      bind-in-table table, curr-key, target-val
+    }
+    #
+    var next-arg-ah/edx: (addr handle word) <- get curr-arg, next
+    curr-arg <- lookup *next-arg-ah
+    loop
+  }
+}
+
+# Copy of 'simplify' that just tracks the maximum stack depth needed
+# Doesn't actually need to simulate the stack, since every word has a predictable effect.
+fn max-stack-depth first-word: (addr word), final-word: (addr word) -> _/edi: int {
+  var curr-word/eax: (addr word) <- copy first-word
+  var curr-depth/ecx: int <- copy 0
+  var result/edi: int <- copy 0
+  $max-stack-depth:loop: {
+    $max-stack-depth:process-word: {
+      # handle operators
+      {
+        var is-add?/eax: boolean <- word-equal? curr-word, "+"
+        compare is-add?, 0
+        break-if-=
+        curr-depth <- decrement
+        break $max-stack-depth:process-word
+      }
+      {
+        var is-sub?/eax: boolean <- word-equal? curr-word, "-"
+        compare is-sub?, 0
+        break-if-=
+        curr-depth <- decrement
+        break $max-stack-depth:process-word
+      }
+      {
+        var is-mul?/eax: boolean <- word-equal? curr-word, "*"
+        compare is-mul?, 0
+        break-if-=
+        curr-depth <- decrement
+        break $max-stack-depth:process-word
+      }
+      # otherwise it's an int (do we need error-checking?)
+      curr-depth <- increment
+      # update max depth if necessary
+      {
+        compare curr-depth, result
+        break-if-<=
+        result <- copy curr-depth
+      }
+    }
+    # if curr-word == final-word break
+    compare curr-word, final-word
+    break-if-=
+    # curr-word = curr-word->next
+    var next-word-ah/edx: (addr handle word) <- get curr-word, next
+    curr-word <- lookup *next-word-ah
+    #
+    loop
+  }
+  return result
+}