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