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-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 defines a binding, save top of stack to bindings
 52       {
 53         var done?/eax: boolean <- stream-empty? curr-stream
 54         compare done?, 0  # false
 55         break-if-!=
 56         var new-byte/eax: byte <- read-byte curr-stream
 57         compare new-byte, 0x3d  # '='
 58         break-if-!=
 59         var key-h: (handle array byte)
 60         var key/ecx: (addr handle array byte) <- address key-h
 61         stream-to-string curr-stream, key
 62         var foo/eax: (addr array byte) <- lookup *key
 63         var val/eax: int <- pop-int-from-value-stack out
 64         bind-int-in-table bindings, key, val
 65         var line/eax: (addr line) <- copy scratch
 66         var next-line-ah/eax: (addr handle line) <- get line, next
 67         var next-line/eax: (addr line) <- lookup *next-line-ah
 68         compare next-line, 0
 69         break-if-= $evaluate:process-word
 70         evaluate functions, bindings, next-line, end, out
 71         break $evaluate:process-word
 72       }
 73       rewind-stream curr-stream
 74       # if curr-stream is a known function name, call it appropriately
 75       {
 76         var callee-h: (handle function)
 77         var callee-ah/eax: (addr handle function) <- address callee-h
 78         find-function functions, curr-stream, callee-ah
 79         var callee/eax: (addr function) <- lookup *callee-ah
 80         compare callee, 0
 81         break-if-=
 82         perform-call callee, out, functions
 83         break $evaluate:process-word
 84       }
 85       # if it's a name, push its value
 86       {
 87         compare bindings, 0
 88         break-if-=
 89         var tmp: (handle array byte)
 90         var curr-string-ah/edx: (addr handle array byte) <- address tmp
 91         stream-to-string curr-stream, curr-string-ah  # unfortunate leak
 92         var curr-string/eax: (addr array byte) <- lookup *curr-string-ah
 93         var val-storage: (handle value)
 94         var val-ah/edi: (addr handle value) <- address val-storage
 95         lookup-binding bindings, curr-string, val-ah
 96         var val/eax: (addr value) <- lookup *val-ah
 97         compare val, 0
 98         break-if-=
 99         push-value-stack out, val
100         break $evaluate:process-word
101       }
102       # otherwise assume it's a literal int and push it
103       {
104         var n/eax: int <- parse-decimal-int-from-stream curr-stream
105         push-int-to-value-stack out, n
106       }
107     }
108     # termination check
109     compare curr, end
110     break-if-=
111     # update
112     var next-word-ah/edx: (addr handle word) <- get curr, next
113     curr <- lookup *next-word-ah
114     #
115     loop
116   }
117 }
118 
119 fn test-evaluate {
120   var line-storage: line
121   var line/esi: (addr line) <- address line-storage
122   var first-word-ah/eax: (addr handle word) <- get line-storage, data
123   allocate-word-with first-word-ah, "3"
124   append-word-with *first-word-ah, "=a"
125   var next-line-ah/eax: (addr handle line) <- get line-storage, next
126   allocate next-line-ah
127   var next-line/eax: (addr line) <- lookup *next-line-ah
128   var first-word-ah/eax: (addr handle word) <- get next-line, data
129   allocate-word-with first-word-ah, "a"
130   var functions-storage: (handle function)
131   var functions/ecx: (addr handle function) <- address functions-storage
132   var table-storage: table
133   var table/ebx: (addr table) <- address table-storage
134   initialize-table table, 0x10
135   var stack-storage: value-stack
136   var stack/edi: (addr value-stack) <- address stack-storage
137   initialize-value-stack stack, 0x10
138   evaluate functions, table, line, 0, stack
139   var x/eax: int <- pop-int-from-value-stack stack
140   check-ints-equal x, 3, "F - test-evaluate"
141 }
142 
143 fn find-function first: (addr handle function), name: (addr stream byte), out: (addr handle function) {
144   var curr/esi: (addr handle function) <- copy first
145   $find-function:loop: {
146     var _f/eax: (addr function) <- lookup *curr
147     var f/ecx: (addr function) <- copy _f
148     compare f, 0
149     break-if-=
150     var curr-name-ah/eax: (addr handle array byte) <- get f, name
151     var curr-name/eax: (addr array byte) <- lookup *curr-name-ah
152     var done?/eax: boolean <- stream-data-equal? name, curr-name
153     compare done?, 0  # false
154     {
155       break-if-=
156       copy-handle *curr, out
157       break $find-function:loop
158     }
159     curr <- get f, next
160     loop
161   }
162 }
163 
164 fn perform-call _callee: (addr function), caller-stack: (addr value-stack), functions: (addr handle function) {
165   var callee/ecx: (addr function) <- copy _callee
166   # create bindings for args
167   var table-storage: table
168   var table/esi: (addr table) <- address table-storage
169   initialize-table table, 0x10
170   bind-args callee, caller-stack, table
171   # obtain body
172   var body-ah/eax: (addr handle line) <- get callee, body
173   var body/eax: (addr line) <- lookup *body-ah
174   # perform call
175   var stack-storage: value-stack
176   var stack/edi: (addr value-stack) <- address stack-storage
177   initialize-value-stack stack, 0x10
178 #?   print-string-to-real-screen "about to enter recursive eval\n"
179   evaluate functions, table, body, 0, stack
180 #?   print-string-to-real-screen "exited recursive eval\n"
181   # stitch result from stack into caller
182   var result/eax: int <- pop-int-from-value-stack stack
183   push-int-to-value-stack caller-stack, result
184 }
185 
186 # pop args from the caller-stack and bind them to successive args
187 # implies: function args are stored in reverse order
188 fn bind-args _callee: (addr function), caller-stack: (addr value-stack), table: (addr table) {
189   var callee/ecx: (addr function) <- copy _callee
190   var curr-arg-ah/eax: (addr handle word) <- get callee, args
191   var curr-arg/eax: (addr word) <- lookup *curr-arg-ah
192   #
193   var curr-key-storage: (handle array byte)
194   var curr-key/edx: (addr handle array byte) <- address curr-key-storage
195   {
196     compare curr-arg, 0
197     break-if-=
198     # create binding
199     word-to-string curr-arg, curr-key
200     {
201 #?       var tmp/eax: (addr array byte) <- lookup *curr-key
202 #?       print-string-to-real-screen "binding "
203 #?       print-string-to-real-screen tmp
204 #?       print-string-to-real-screen " to "
205       var curr-val/eax: int <- pop-int-from-value-stack caller-stack
206 #?       print-int32-decimal-to-real-screen curr-val
207 #?       print-string-to-real-screen "\n"
208       bind-int-in-table table, curr-key, curr-val
209     }
210     #
211     var next-arg-ah/edx: (addr handle word) <- get curr-arg, next
212     curr-arg <- lookup *next-arg-ah
213     loop
214   }
215 }
216 
217 # Copy of 'simplify' that just tracks the maximum stack depth needed
218 # Doesn't actually need to simulate the stack, since every word has a predictable effect.
219 fn max-stack-depth first-word: (addr word), final-word: (addr word) -> result/edi: int {
220   var curr-word/eax: (addr word) <- copy first-word
221   var curr-depth/ecx: int <- copy 0
222   result <- copy 0
223   $max-stack-depth:loop: {
224     $max-stack-depth:process-word: {
225       # handle operators
226       {
227         var is-add?/eax: boolean <- word-equal? curr-word, "+"
228         compare is-add?, 0
229         break-if-=
230         curr-depth <- decrement
231         break $max-stack-depth:process-word
232       }
233       {
234         var is-sub?/eax: boolean <- word-equal? curr-word, "-"
235         compare is-sub?, 0
236         break-if-=
237         curr-depth <- decrement
238         break $max-stack-depth:process-word
239       }
240       {
241         var is-mul?/eax: boolean <- word-equal? curr-word, "*"
242         compare is-mul?, 0
243         break-if-=
244         curr-depth <- decrement
245         break $max-stack-depth:process-word
246       }
247       # otherwise it's an int (do we need error-checking?)
248       curr-depth <- increment
249       # update max depth if necessary
250       {
251         compare curr-depth, result
252         break-if-<=
253         result <- copy curr-depth
254       }
255     }
256     # if curr-word == final-word break
257     compare curr-word, final-word
258     break-if-=
259     # curr-word = curr-word->next
260     var next-word-ah/edx: (addr handle word) <- get curr-word, next
261     curr-word <- lookup *next-word-ah
262     #
263     loop
264   }
265 }