https://github.com/akkartik/mu/blob/master/apps/tile/rpn.mu
  1 fn evaluate defs: (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-stream-to-real-screen curr-stream
 15 #?     print-string-to-real-screen "\n"
 16     $evaluate:process-word: {
 17       # if curr-stream is an operator, perform it
 18       {
 19         var is-add?/eax: boolean <- stream-data-equal? curr-stream, "+"
 20         compare is-add?, 0
 21         break-if-=
 22         var _b/eax: int <- pop-int-from-value-stack out
 23         var b/edx: int <- copy _b
 24         var a/eax: int <- pop-int-from-value-stack out
 25         a <- add b
 26         push-int-to-value-stack out, a
 27         break $evaluate:process-word
 28       }
 29       {
 30         var is-sub?/eax: boolean <- stream-data-equal? curr-stream, "-"
 31         compare is-sub?, 0
 32         break-if-=
 33         var _b/eax: int <- pop-int-from-value-stack out
 34         var b/edx: int <- copy _b
 35         var a/eax: int <- pop-int-from-value-stack out
 36         a <- subtract b
 37         push-int-to-value-stack out, a
 38         break $evaluate:process-word
 39       }
 40       {
 41         var is-mul?/eax: boolean <- stream-data-equal? curr-stream, "*"
 42         compare is-mul?, 0
 43         break-if-=
 44         var _b/eax: int <- pop-int-from-value-stack out
 45         var b/edx: int <- copy _b
 46         var a/eax: int <- pop-int-from-value-stack out
 47         a <- multiply b
 48         push-int-to-value-stack out, a
 49         break $evaluate:process-word
 50       }
 51       # if curr-stream is a known function name, call it appropriately
 52       {
 53         var callee-h: (handle function)
 54         var callee-ah/eax: (addr handle function) <- address callee-h
 55         find-function defs, curr-stream, callee-ah
 56         var callee/eax: (addr function) <- lookup *callee-ah
 57         compare callee, 0
 58         break-if-=
 59         perform-call callee, out, defs
 60         break $evaluate:process-word
 61       }
 62       # if it's a name, push its value
 63       {
 64         compare bindings, 0
 65         break-if-=
 66         var tmp: (handle array byte)
 67         var curr-string-ah/edx: (addr handle array byte) <- address tmp
 68         stream-to-string curr-stream, curr-string-ah  # unfortunate leak
 69         var curr-string/eax: (addr array byte) <- lookup *curr-string-ah
 70         var val-storage: (handle value)
 71         var val-ah/edi: (addr handle value) <- address val-storage
 72         lookup-binding bindings, curr-string, val-ah
 73         var val/eax: (addr value) <- lookup *val-ah
 74         compare val, 0
 75         break-if-=
 76 #?         print-string-to-real-screen "value of "
 77 #?         print-string-to-real-screen curr-string
 78 #?         print-string-to-real-screen " is "
 79 #?         print-int32-hex-to-real-screen result
 80 #?         print-string-to-real-screen "\n"
 81         push-value-stack out, val
 82         break $evaluate:process-word
 83       }
 84       # otherwise assume it's a literal int and push it
 85       {
 86         var n/eax: int <- parse-decimal-int-from-stream curr-stream
 87         push-int-to-value-stack out, n
 88       }
 89     }
 90     # termination check
 91     compare curr, end
 92     break-if-=
 93     # update
 94     var next-word-ah/edx: (addr handle word) <- get curr, next
 95     curr <- lookup *next-word-ah
 96     #
 97     loop
 98   }
 99 }
100 
101 fn find-function first: (addr handle function), name: (addr stream byte), out: (addr handle function) {
102   var curr/esi: (addr handle function) <- copy first
103   $find-function:loop: {
104     var _f/eax: (addr function) <- lookup *curr
105     var f/ecx: (addr function) <- copy _f
106     compare f, 0
107     break-if-=
108     var curr-name-ah/eax: (addr handle array byte) <- get f, name
109     var curr-name/eax: (addr array byte) <- lookup *curr-name-ah
110     var done?/eax: boolean <- stream-data-equal? name, curr-name
111     compare done?, 0  # false
112     {
113       break-if-=
114       copy-handle *curr, out
115       break $find-function:loop
116     }
117     curr <- get f, next
118     loop
119   }
120 }
121 
122 fn perform-call _callee: (addr function), caller-stack: (addr value-stack), defs: (addr handle function) {
123   var callee/ecx: (addr function) <- copy _callee
124   # create bindings for args
125   var table-storage: table
126   var table/esi: (addr table) <- address table-storage
127   initialize-table table, 0x10
128   bind-args callee, caller-stack, table
129   # obtain body
130   var body-ah/eax: (addr handle line) <- get callee, body
131   var body/eax: (addr line) <- lookup *body-ah
132   # perform call
133   var stack-storage: value-stack
134   var stack/edi: (addr value-stack) <- address stack-storage
135   initialize-value-stack stack, 0x10
136 #?   print-string-to-real-screen "about to enter recursive eval\n"
137   evaluate defs, table, body, 0, stack
138 #?   print-string-to-real-screen "exited recursive eval\n"
139   # stitch result from stack into caller
140   var result/eax: int <- pop-int-from-value-stack stack
141   push-int-to-value-stack caller-stack, result
142 }
143 
144 fn bind-args _callee: (addr function), caller-stack: (addr value-stack), table: (addr table) {
145   var callee/ecx: (addr function) <- copy _callee
146   var curr-arg-ah/eax: (addr handle word) <- get callee, args
147   var curr-arg/eax: (addr word) <- lookup *curr-arg-ah
148   #
149   var curr-key-storage: (handle array byte)
150   var curr-key/edx: (addr handle array byte) <- address curr-key-storage
151   {
152     compare curr-arg, 0
153     break-if-=
154     # create binding
155     word-to-string curr-arg, curr-key
156     {
157 #?       var tmp/eax: (addr array byte) <- lookup *curr-key
158 #?       print-string-to-real-screen "binding "
159 #?       print-string-to-real-screen tmp
160 #?       print-string-to-real-screen " to "
161       var curr-val/eax: int <- pop-int-from-value-stack caller-stack
162 #?       print-int32-decimal-to-real-screen curr-val
163 #?       print-string-to-real-screen "\n"
164       bind-int-in-table table, curr-key, curr-val
165     }
166     #
167     var next-arg-ah/edx: (addr handle word) <- get curr-arg, next
168     curr-arg <- lookup *next-arg-ah
169     loop
170   }
171 }
172 
173 # Copy of 'simplify' that just tracks the maximum stack depth needed
174 # Doesn't actually need to simulate the stack, since every word has a predictable effect.
175 fn max-stack-depth first-word: (addr word), final-word: (addr word) -> result/edi: int {
176   var curr-word/eax: (addr word) <- copy first-word
177   var curr-depth/ecx: int <- copy 0
178   result <- copy 0
179   $max-stack-depth:loop: {
180     $max-stack-depth:process-word: {
181       # handle operators
182       {
183         var is-add?/eax: boolean <- word-equal? curr-word, "+"
184         compare is-add?, 0
185         break-if-=
186         curr-depth <- decrement
187         break $max-stack-depth:process-word
188       }
189       {
190         var is-sub?/eax: boolean <- word-equal? curr-word, "-"
191         compare is-sub?, 0
192         break-if-=
193         curr-depth <- decrement
194         break $max-stack-depth:process-word
195       }
196       {
197         var is-mul?/eax: boolean <- word-equal? curr-word, "*"
198         compare is-mul?, 0
199         break-if-=
200         curr-depth <- decrement
201         break $max-stack-depth:process-word
202       }
203       # otherwise it's an int (do we need error-checking?)
204       curr-depth <- increment
205       # update max depth if necessary
206       {
207         compare curr-depth, result
208         break-if-<=
209         result <- copy curr-depth
210       }
211     }
212     # if curr-word == final-word break
213     compare curr-word, final-word
214     break-if-=
215     # curr-word = curr-word->next
216     var next-word-ah/edx: (addr handle word) <- get curr-word, next
217     curr-word <- lookup *next-word-ah
218     #
219     loop
220   }
221 }