https://github.com/akkartik/mu/blob/main/shell/evaluate.mu
   1 # env is an alist of ((sym . val) (sym . val) ...)
   2 # we never modify `in` or `env`
   3 # ignore 'screen-cell' on a first reading; it's a hack for sandboxes
   4 # 'call-number' is just for showing intermediate progress; this is a _slow_ interpreter
   5 fn evaluate _in: (addr handle cell), out: (addr handle cell), env-h: (handle cell), globals: (addr global-table), trace: (addr trace), screen-cell: (addr handle cell), keyboard-cell: (addr handle cell), call-number: int {
   6   # stack overflow?   # disable when enabling Really-debug-print
   7   check-stack
   8   {
   9     var screen-cell/eax: (addr handle cell) <- copy screen-cell
  10     compare screen-cell, 0
  11     break-if-=
  12     var screen-cell-addr/eax: (addr cell) <- lookup *screen-cell
  13     compare screen-cell-addr, 0
  14     break-if-=
  15     # if screen-cell exists, we're probably not in a test
  16     show-stack-state
  17   }
  18   # errors? skip
  19   {
  20     compare trace, 0
  21     break-if-=
  22     var error?/eax: boolean <- has-errors? trace
  23     compare error?, 0/false
  24     break-if-=
  25     return
  26   }
  27   var in/esi: (addr handle cell) <- copy _in
  28   # show intermediate progress on screen if necessary
  29   {
  30     compare screen-cell, 0
  31     break-if-=
  32     var tmp/eax: int <- copy call-number
  33     tmp <- and 0xf  # every 16 calls to evaluate
  34     compare tmp, 0
  35     break-if-!=
  36     var screen-cell/eax: (addr handle cell) <- copy screen-cell
  37     var screen-cell-addr/eax: (addr cell) <- lookup *screen-cell
  38     compare screen-cell-addr, 0
  39     break-if-=
  40     var screen-obj-ah/eax: (addr handle screen) <- get screen-cell-addr, screen-data
  41     var screen-obj/eax: (addr screen) <- lookup *screen-obj-ah
  42     compare screen-obj, 0
  43     break-if-=
  44     var y/ecx: int <- render-screen 0/screen, screen-obj, 0x70/xmin, 1/ymin
  45   }
  46 #?   dump-cell in
  47 #?   {
  48 #?     var foo/eax: byte <- read-key 0/keyboard
  49 #?     compare foo, 0
  50 #?     loop-if-=
  51 #?   }
  52 +-- 14 lines: # trace "evaluate " in " in environment " env -----------------------------------------------------------------------------------------------------------------------------
  66   trace-lower trace
  67   var in-addr/eax: (addr cell) <- lookup *in
  68   {
  69     var nil?/eax: boolean <- nil? in-addr
  70     compare nil?, 0/false
  71     break-if-=
  72     # nil is a literal
  73     trace-text trace, "eval", "nil"
  74     copy-object _in, out
  75     trace-higher trace
  76     return
  77   }
  78   var in-type/ecx: (addr int) <- get in-addr, type
  79   compare *in-type, 1/number
  80   {
  81     break-if-!=
  82     # numbers are literals
  83     trace-text trace, "eval", "number"
  84     copy-object _in, out
  85     trace-higher trace
  86     return
  87   }
  88   compare *in-type, 3/stream
  89   {
  90     break-if-!=
  91     # streams are literals
  92     trace-text trace, "eval", "stream"
  93     copy-object _in, out
  94     trace-higher trace
  95     return
  96   }
  97   compare *in-type, 2/symbol
  98   {
  99     break-if-!=
 100     trace-text trace, "eval", "symbol"
 101     debug-print "a", 7/fg, 0xc5/bg=blue-bg
 102     lookup-symbol in-addr, out, env-h, globals, trace, screen-cell, keyboard-cell
 103     debug-print "z", 7/fg, 0xc5/bg=blue-bg
 104     trace-higher trace
 105     return
 106   }
 107   compare *in-type, 5/screen
 108   {
 109     break-if-!=
 110     trace-text trace, "eval", "screen"
 111     copy-object _in, out
 112     trace-higher trace
 113     return
 114   }
 115   compare *in-type, 6/keyboard
 116   {
 117     break-if-!=
 118     trace-text trace, "eval", "keyboard"
 119     copy-object _in, out
 120     trace-higher trace
 121     return
 122   }
 123   # in-addr is a syntax tree
 124   $evaluate:anonymous-function: {
 125     # trees starting with "fn" are anonymous functions
 126     var expr/esi: (addr cell) <- copy in-addr
 127     # if its first elem is not "fn", break
 128     var in-addr/edx: (addr cell) <- copy in-addr
 129     var first-ah/ecx: (addr handle cell) <- get in-addr, left
 130     var first/eax: (addr cell) <- lookup *first-ah
 131     var fn?/eax: boolean <- fn? first
 132     compare fn?, 0/false
 133     break-if-=
 134     # turn (fn ...) into (fn env ...)
 135     trace-text trace, "eval", "anonymous function"
 136     var rest-ah/eax: (addr handle cell) <- get in-addr, right
 137     var tmp: (handle cell)
 138     var tmp-ah/edi: (addr handle cell) <- address tmp
 139     new-pair tmp-ah, env-h, *rest-ah
 140     new-pair out, *first-ah, *tmp-ah
 141     trace-higher trace
 142     return
 143   }
 144   # builtins with "special" evaluation rules
 145   $evaluate:quote: {
 146     # trees starting with single quote create literals
 147     var expr/esi: (addr cell) <- copy in-addr
 148     # if its first elem is not "'", break
 149     var first-ah/ecx: (addr handle cell) <- get in-addr, left
 150     var rest-ah/edx: (addr handle cell) <- get in-addr, right
 151     var first/eax: (addr cell) <- lookup *first-ah
 152     var quote?/eax: boolean <- symbol-equal? first, "'"
 153     compare quote?, 0/false
 154     break-if-=
 155     #
 156     trace-text trace, "eval", "quote"
 157     copy-object rest-ah, out
 158     trace-higher trace
 159     return
 160   }
 161   $evaluate:def: {
 162     # trees starting with "def" define globals
 163     var expr/esi: (addr cell) <- copy in-addr
 164     # if its first elem is not "def", break
 165     var first-ah/ecx: (addr handle cell) <- get in-addr, left
 166     var rest-ah/edx: (addr handle cell) <- get in-addr, right
 167     var first/eax: (addr cell) <- lookup *first-ah
 168     var def?/eax: boolean <- symbol-equal? first, "def"
 169     compare def?, 0/false
 170     break-if-=
 171     #
 172     trace-text trace, "eval", "def"
 173     trace-text trace, "eval", "evaluating second arg"
 174     var rest/eax: (addr cell) <- lookup *rest-ah
 175     var first-arg-ah/ecx: (addr handle cell) <- get rest, left
 176     {
 177       var first-arg/eax: (addr cell) <- lookup *first-arg-ah
 178       var first-arg-type/eax: (addr int) <- get first-arg, type
 179       compare *first-arg-type, 2/symbol
 180       break-if-=
 181       error trace, "first arg to def must be a symbol"
 182       trace-higher trace
 183       return
 184     }
 185     rest-ah <- get rest, right
 186     rest <- lookup *rest-ah
 187     var second-arg-ah/edx: (addr handle cell) <- get rest, left
 188     debug-print "P", 4/fg, 0xc5/bg=blue-bg
 189     increment call-number
 190     evaluate second-arg-ah, out, env-h, globals, trace, screen-cell, keyboard-cell, call-number
 191     debug-print "Q", 4/fg, 0xc5/bg=blue-bg
 192     trace-text trace, "eval", "saving global binding"
 193     var first-arg/eax: (addr cell) <- lookup *first-arg-ah
 194     var first-arg-data-ah/eax: (addr handle stream byte) <- get first-arg, text-data
 195     var first-arg-data/eax: (addr stream byte) <- lookup *first-arg-data-ah
 196     var tmp-string: (handle array byte)
 197     var tmp-ah/edx: (addr handle array byte) <- address tmp-string
 198     rewind-stream first-arg-data
 199     stream-to-array first-arg-data, tmp-ah
 200     var first-arg-data-string/eax: (addr array byte) <- lookup *tmp-ah
 201     var out2/edi: (addr handle cell) <- copy out
 202     assign-or-create-global globals, first-arg-data-string, *out2, trace
 203     trace-higher trace
 204     return
 205   }
 206   $evaluate:set: {
 207     # trees starting with "set" mutate bindings
 208     var expr/esi: (addr cell) <- copy in-addr
 209     # if its first elem is not "set", break
 210     var first-ah/ecx: (addr handle cell) <- get in-addr, left
 211     var rest-ah/edx: (addr handle cell) <- get in-addr, right
 212     var first/eax: (addr cell) <- lookup *first-ah
 213     var set?/eax: boolean <- symbol-equal? first, "set"
 214     compare set?, 0/false
 215     break-if-=
 216     #
 217     trace-text trace, "eval", "set"
 218     trace-text trace, "eval", "evaluating second arg"
 219     var rest/eax: (addr cell) <- lookup *rest-ah
 220     var first-arg-ah/ecx: (addr handle cell) <- get rest, left
 221     {
 222       var first-arg/eax: (addr cell) <- lookup *first-arg-ah
 223       var first-arg-type/eax: (addr int) <- get first-arg, type
 224       compare *first-arg-type, 2/symbol
 225       break-if-=
 226       error trace, "first arg to set must be a symbol"
 227       trace-higher trace
 228       return
 229     }
 230     rest-ah <- get rest, right
 231     rest <- lookup *rest-ah
 232     var second-arg-ah/edx: (addr handle cell) <- get rest, left
 233     debug-print "P", 4/fg, 0xc5/bg=blue-bg
 234     increment call-number
 235     evaluate second-arg-ah, out, env-h, globals, trace, screen-cell, keyboard-cell, call-number
 236     debug-print "Q", 4/fg, 0xc5/bg=blue-bg
 237     trace-text trace, "eval", "mutating binding"
 238     var first-arg/eax: (addr cell) <- lookup *first-arg-ah
 239     var first-arg-data-ah/eax: (addr handle stream byte) <- get first-arg, text-data
 240     var first-arg-data/eax: (addr stream byte) <- lookup *first-arg-data-ah
 241     mutate-binding first-arg-data, out, env-h, globals, trace
 242     trace-higher trace
 243     return
 244   }
 245   $evaluate:and: {
 246     var expr/esi: (addr cell) <- copy in-addr
 247     # if its first elem is not "and", break
 248     var first-ah/ecx: (addr handle cell) <- get in-addr, left
 249     var rest-ah/edx: (addr handle cell) <- get in-addr, right
 250     var first/eax: (addr cell) <- lookup *first-ah
 251     var and?/eax: boolean <- symbol-equal? first, "and"
 252     compare and?, 0/false
 253     break-if-=
 254     #
 255     trace-text trace, "eval", "and"
 256     trace-text trace, "eval", "evaluating first arg"
 257     var rest/eax: (addr cell) <- lookup *rest-ah
 258     var first-arg-ah/ecx: (addr handle cell) <- get rest, left
 259     debug-print "R2", 4/fg, 0xc5/bg=blue-bg
 260     increment call-number
 261     evaluate first-arg-ah, out, env-h, globals, trace, screen-cell, keyboard-cell, call-number
 262     debug-print "S2", 4/fg, 0xc5/bg=blue-bg
 263     # if first arg is nil, short-circuit
 264     var out-ah/eax: (addr handle cell) <- copy out
 265     var out-a/eax: (addr cell) <- lookup *out-ah
 266     var nil?/eax: boolean <- nil? out-a
 267     compare nil?, 0/false
 268     {
 269       break-if-=
 270       return
 271     }
 272     var rest/eax: (addr cell) <- lookup *rest-ah
 273     rest-ah <- get rest, right
 274     rest <- lookup *rest-ah
 275     var second-ah/eax: (addr handle cell) <- get rest, left
 276     debug-print "T2", 4/fg, 0xc5/bg=blue-bg
 277     increment call-number
 278     evaluate second-ah, out, env-h, globals, trace, screen-cell, keyboard-cell, call-number
 279     debug-print "U2", 4/fg, 0xc5/bg=blue-bg
 280     trace-higher trace
 281     return
 282   }
 283   $evaluate:or: {
 284     var expr/esi: (addr cell) <- copy in-addr
 285     # if its first elem is not "or", break
 286     var first-ah/ecx: (addr handle cell) <- get in-addr, left
 287     var rest-ah/edx: (addr handle cell) <- get in-addr, right
 288     var first/eax: (addr cell) <- lookup *first-ah
 289     var or?/eax: boolean <- symbol-equal? first, "or"
 290     compare or?, 0/false
 291     break-if-=
 292     #
 293     trace-text trace, "eval", "or"
 294     trace-text trace, "eval", "evaluating first arg"
 295     var rest/eax: (addr cell) <- lookup *rest-ah
 296     var first-arg-ah/ecx: (addr handle cell) <- get rest, left
 297     debug-print "R2", 4/fg, 0xc5/bg=blue-bg
 298     increment call-number
 299     evaluate first-arg-ah, out, env-h, globals, trace, screen-cell, keyboard-cell, call-number
 300     debug-print "S2", 4/fg, 0xc5/bg=blue-bg
 301     # if first arg is not nil, short-circuit
 302     var out-ah/eax: (addr handle cell) <- copy out
 303     var out-a/eax: (addr cell) <- lookup *out-ah
 304     var nil?/eax: boolean <- nil? out-a
 305     compare nil?, 0/false
 306     {
 307       break-if-!=
 308       return
 309     }
 310     var rest/eax: (addr cell) <- lookup *rest-ah
 311     rest-ah <- get rest, right
 312     rest <- lookup *rest-ah
 313     var second-ah/eax: (addr handle cell) <- get rest, left
 314     debug-print "T2", 4/fg, 0xc5/bg=blue-bg
 315     increment call-number
 316     evaluate second-ah, out, env-h, globals, trace, screen-cell, keyboard-cell, call-number
 317     debug-print "U2", 4/fg, 0xc5/bg=blue-bg
 318     trace-higher trace
 319     return
 320   }
 321   $evaluate:if: {
 322     # trees starting with "if" are conditionals
 323     var expr/esi: (addr cell) <- copy in-addr
 324     # if its first elem is not "if", break
 325     var first-ah/ecx: (addr handle cell) <- get in-addr, left
 326     var rest-ah/edx: (addr handle cell) <- get in-addr, right
 327     var first/eax: (addr cell) <- lookup *first-ah
 328     var if?/eax: boolean <- symbol-equal? first, "if"
 329     compare if?, 0/false
 330     break-if-=
 331     #
 332     trace-text trace, "eval", "if"
 333     trace-text trace, "eval", "evaluating first arg"
 334     var rest/eax: (addr cell) <- lookup *rest-ah
 335     var first-arg-ah/ecx: (addr handle cell) <- get rest, left
 336     var guard-h: (handle cell)
 337     var guard-ah/esi: (addr handle cell) <- address guard-h
 338     debug-print "R", 4/fg, 0xc5/bg=blue-bg
 339     increment call-number
 340     evaluate first-arg-ah, guard-ah, env-h, globals, trace, screen-cell, keyboard-cell, call-number
 341     debug-print "S", 4/fg, 0xc5/bg=blue-bg
 342     rest-ah <- get rest, right
 343     rest <- lookup *rest-ah
 344     var branch-ah/edi: (addr handle cell) <- get rest, left
 345     var guard-a/eax: (addr cell) <- lookup *guard-ah
 346     var skip-to-third-arg?/eax: boolean <- nil? guard-a
 347     compare skip-to-third-arg?, 0/false
 348     {
 349       break-if-=
 350       trace-text trace, "eval", "skipping to third arg"
 351       var rest/eax: (addr cell) <- lookup *rest-ah
 352       rest-ah <- get rest, right
 353       rest <- lookup *rest-ah
 354       branch-ah <- get rest, left
 355     }
 356     debug-print "T", 4/fg, 0xc5/bg=blue-bg
 357     increment call-number
 358     evaluate branch-ah, out, env-h, globals, trace, screen-cell, keyboard-cell, call-number
 359     debug-print "U", 4/fg, 0xc5/bg=blue-bg
 360     trace-higher trace
 361     return
 362   }
 363   $evaluate:while: {
 364     # trees starting with "while" are loops
 365     var expr/esi: (addr cell) <- copy in-addr
 366     # if its first elem is not "while", break
 367     var first-ah/ecx: (addr handle cell) <- get in-addr, left
 368     var rest-ah/edx: (addr handle cell) <- get in-addr, right
 369     var first/eax: (addr cell) <- lookup *first-ah
 370     var first-type/ecx: (addr int) <- get first, type
 371     compare *first-type, 2/symbol
 372     break-if-!=
 373     var sym-data-ah/eax: (addr handle stream byte) <- get first, text-data
 374     var sym-data/eax: (addr stream byte) <- lookup *sym-data-ah
 375     var while?/eax: boolean <- stream-data-equal? sym-data, "while"
 376     compare while?, 0/false
 377     break-if-=
 378     #
 379     trace-text trace, "eval", "while"
 380     var rest/eax: (addr cell) <- lookup *rest-ah
 381     var first-arg-ah/ecx: (addr handle cell) <- get rest, left
 382     rest-ah <- get rest, right
 383     var guard-h: (handle cell)
 384     var guard-ah/esi: (addr handle cell) <- address guard-h
 385     $evaluate:while:loop-execution: {
 386       {
 387         compare trace, 0
 388         break-if-=
 389         var error?/eax: boolean <- has-errors? trace
 390         compare error?, 0/false
 391         break-if-!= $evaluate:while:loop-execution
 392       }
 393       trace-text trace, "eval", "loop termination check"
 394       debug-print "V", 4/fg, 0xc5/bg=blue-bg
 395       increment call-number
 396       evaluate first-arg-ah, guard-ah, env-h, globals, trace, screen-cell, keyboard-cell, call-number
 397       debug-print "W", 4/fg, 0xc5/bg=blue-bg
 398       var guard-a/eax: (addr cell) <- lookup *guard-ah
 399       var done?/eax: boolean <- nil? guard-a
 400       compare done?, 0/false
 401       break-if-!=
 402       evaluate-exprs rest-ah, out, env-h, globals, trace, screen-cell, keyboard-cell, call-number
 403       loop
 404     }
 405     trace-text trace, "eval", "loop terminated"
 406     trace-higher trace
 407     return
 408   }
 409   trace-text trace, "eval", "function call"
 410   trace-text trace, "eval", "evaluating list elements"
 411   trace-lower trace
 412   var evaluated-list-storage: (handle cell)
 413   var evaluated-list-ah/esi: (addr handle cell) <- address evaluated-list-storage
 414   var curr-out-ah/edx: (addr handle cell) <- copy evaluated-list-ah
 415   var curr/ecx: (addr cell) <- copy in-addr
 416   $evaluate-list:loop: {
 417     allocate-pair curr-out-ah
 418     var nil?/eax: boolean <- nil? curr
 419     compare nil?, 0/false
 420     break-if-!=
 421     # eval left
 422     var curr-out/eax: (addr cell) <- lookup *curr-out-ah
 423     var left-out-ah/edi: (addr handle cell) <- get curr-out, left
 424     var left-ah/esi: (addr handle cell) <- get curr, left
 425     debug-print "A", 4/fg, 0xc5/bg=blue-bg
 426     increment call-number
 427     evaluate left-ah, left-out-ah, env-h, globals, trace, screen-cell, keyboard-cell, call-number
 428     debug-print "B", 4/fg, 0xc5/bg=blue-bg
 429     #
 430     curr-out-ah <- get curr-out, right
 431     var right-ah/eax: (addr handle cell) <- get curr, right
 432     var right/eax: (addr cell) <- lookup *right-ah
 433     curr <- copy right
 434     loop
 435   }
 436   trace-higher trace
 437   var evaluated-list/eax: (addr cell) <- lookup *evaluated-list-ah
 438   var function-ah/ecx: (addr handle cell) <- get evaluated-list, left
 439   var args-ah/edx: (addr handle cell) <- get evaluated-list, right
 440   debug-print "C", 4/fg, 0xc5/bg=blue-bg
 441   apply function-ah, args-ah, out, globals, trace, screen-cell, keyboard-cell, call-number
 442   debug-print "Y", 4/fg, 0xc5/bg=blue-bg
 443   trace-higher trace
 444 +-- 11 lines: # trace "=> " out ---------------------------------------------------------------------------------------------------------------------------------------------------------
 455   debug-print "Z", 4/fg, 0xc5/bg=blue-bg
 456 }
 457 
 458 fn apply _f-ah: (addr handle cell), args-ah: (addr handle cell), out: (addr handle cell), globals: (addr global-table), trace: (addr trace), screen-cell: (addr handle cell), keyboard-cell: (addr handle cell), call-number: int {
 459   var f-ah/eax: (addr handle cell) <- copy _f-ah
 460   var _f/eax: (addr cell) <- lookup *f-ah
 461   var f/esi: (addr cell) <- copy _f
 462   # call primitive functions
 463   {
 464     var f-type/eax: (addr int) <- get f, type
 465     compare *f-type, 4/primitive-function
 466     break-if-!=
 467     apply-primitive f, args-ah, out, globals, trace
 468     return
 469   }
 470   # if it's not a primitive function it must be an anonymous function
 471 +-- 14 lines: # trace "apply anonymous function " f " in environment " env --------------------------------------------------------------------------------------------------------------
 485   trace-lower trace
 486   {
 487     var f-type/ecx: (addr int) <- get f, type
 488     compare *f-type, 0/pair
 489     break-if-!=
 490     var first-ah/eax: (addr handle cell) <- get f, left
 491     var first/eax: (addr cell) <- lookup *first-ah
 492     var fn?/eax: boolean <- fn? first
 493     compare fn?, 0/false
 494     break-if-=
 495     var rest-ah/esi: (addr handle cell) <- get f, right
 496     var rest/eax: (addr cell) <- lookup *rest-ah
 497     var callee-env-ah/edx: (addr handle cell) <- get rest, left
 498     rest-ah <- get rest, right
 499     rest <- lookup *rest-ah
 500     var params-ah/ecx: (addr handle cell) <- get rest, left
 501     var body-ah/eax: (addr handle cell) <- get rest, right
 502     debug-print "D", 7/fg, 0xc5/bg=blue-bg
 503     apply-function params-ah, args-ah, body-ah, out, *callee-env-ah, globals, trace, screen-cell, keyboard-cell, call-number
 504     debug-print "Y", 7/fg, 0xc5/bg=blue-bg
 505     trace-higher trace
 506     return
 507   }
 508   error trace, "unknown function"
 509 }
 510 
 511 fn apply-function params-ah: (addr handle cell), args-ah: (addr handle cell), body-ah: (addr handle cell), out: (addr handle cell), env-h: (handle cell), globals: (addr global-table), trace: (addr trace), screen-cell: (addr handle cell), keyboard-cell: (addr handle cell), call-number: int {
 512   # push bindings for params to env
 513   var new-env-h: (handle cell)
 514   var new-env-ah/esi: (addr handle cell) <- address new-env-h
 515   push-bindings params-ah, args-ah, env-h, new-env-ah, trace
 516   #
 517   evaluate-exprs body-ah, out, new-env-h, globals, trace, screen-cell, keyboard-cell, call-number
 518 }
 519 
 520 fn evaluate-exprs _exprs-ah: (addr handle cell), out: (addr handle cell), env-h: (handle cell), globals: (addr global-table), trace: (addr trace), screen-cell: (addr handle cell), keyboard-cell: (addr handle cell), call-number: int {
 521   # eval all exprs, writing result to `out` each time
 522   var exprs-ah/ecx: (addr handle cell) <- copy _exprs-ah
 523   $evaluate-exprs:loop: {
 524     var exprs/eax: (addr cell) <- lookup *exprs-ah
 525     # stop when exprs is nil
 526     {
 527       var exprs-nil?/eax: boolean <- nil? exprs
 528       compare exprs-nil?, 0/false
 529       break-if-!= $evaluate-exprs:loop
 530     }
 531     # evaluate each expression, writing result to `out`
 532     {
 533       var curr-ah/eax: (addr handle cell) <- get exprs, left
 534       debug-print "E", 7/fg, 0xc5/bg=blue-bg
 535       increment call-number
 536       evaluate curr-ah, out, env-h, globals, trace, screen-cell, keyboard-cell, call-number
 537       debug-print "X", 7/fg, 0xc5/bg=blue-bg
 538     }
 539     #
 540     exprs-ah <- get exprs, right
 541     loop
 542   }
 543   # `out` contains result of evaluating final expression
 544 }
 545 
 546 # Bind params to corresponding args and add the bindings to old-env. Return
 547 # the result in env-ah.
 548 #
 549 # We never modify old-env, but we point to it. This way other parts of the
 550 # interpreter can continue using old-env, and everything works harmoniously
 551 # even though no cells are copied around.
 552 #
 553 # env should always be a DAG (ignoring internals of values). It doesn't have
 554 # to be a tree (some values may be shared), but there are also no cycles.
 555 #
 556 # Learn more: https://en.wikipedia.org/wiki/Persistent_data_structure
 557 fn push-bindings _params-ah: (addr handle cell), _args-ah: (addr handle cell), old-env-h: (handle cell), env-ah: (addr handle cell), trace: (addr trace) {
 558   var params-ah/edx: (addr handle cell) <- copy _params-ah
 559   var args-ah/ebx: (addr handle cell) <- copy _args-ah
 560   var _params/eax: (addr cell) <- lookup *params-ah
 561   var params/esi: (addr cell) <- copy _params
 562   {
 563     var params-nil?/eax: boolean <- nil? params
 564     compare params-nil?, 0/false
 565     break-if-=
 566     # nil is a literal
 567     trace-text trace, "eval", "done with push-bindings"
 568     copy-handle old-env-h, env-ah
 569     return
 570   }
 571   # Params can only be symbols or pairs. Args can be anything.
 572 +-- 16 lines: # trace "pushing bindings from " params " to " args -----------------------------------------------------------------------------------------------------------------------
 588   trace-lower trace
 589   var params-type/eax: (addr int) <- get params, type
 590   compare *params-type, 2/symbol
 591   {
 592     break-if-!=
 593     trace-text trace, "eval", "symbol; binding to all remaining args"
 594     # create a new binding
 595     var new-binding-storage: (handle cell)
 596     var new-binding-ah/eax: (addr handle cell) <- address new-binding-storage
 597     new-pair new-binding-ah, *params-ah, *args-ah
 598     # push it to env
 599     new-pair env-ah, *new-binding-ah, old-env-h
 600     trace-higher trace
 601     return
 602   }
 603   compare *params-type, 0/pair
 604   {
 605     break-if-=
 606     error trace, "cannot bind a non-symbol"
 607     trace-higher trace
 608     return
 609   }
 610   var _args/eax: (addr cell) <- lookup *args-ah
 611   var args/edi: (addr cell) <- copy _args
 612   # params is now a pair, so args must be also
 613   var args-type/eax: (addr int) <- get args, type
 614   compare *args-type, 0/pair
 615   {
 616     break-if-=
 617     error trace, "args not in a proper list"
 618     trace-higher trace
 619     return
 620   }
 621   var intermediate-env-storage: (handle cell)
 622   var intermediate-env-ah/edx: (addr handle cell) <- address intermediate-env-storage
 623   var first-param-ah/eax: (addr handle cell) <- get params, left
 624   var first-arg-ah/ecx: (addr handle cell) <- get args, left
 625   push-bindings first-param-ah, first-arg-ah, old-env-h, intermediate-env-ah, trace
 626   var remaining-params-ah/eax: (addr handle cell) <- get params, right
 627   var remaining-args-ah/ecx: (addr handle cell) <- get args, right
 628   push-bindings remaining-params-ah, remaining-args-ah, *intermediate-env-ah, env-ah, trace
 629   trace-higher trace
 630 }
 631 
 632 fn lookup-symbol sym: (addr cell), out: (addr handle cell), env-h: (handle cell), globals: (addr global-table), trace: (addr trace), screen-cell: (addr handle cell), keyboard-cell: (addr handle cell) {
 633   # trace sym
 634   {
 635     compare trace, 0
 636     break-if-=
 637     var stream-storage: (stream byte 0x800)  # pessimistically sized just for the large alist loaded from disk in `main`
 638     var stream/ecx: (addr stream byte) <- address stream-storage
 639     write stream, "look up "
 640     var sym2/eax: (addr cell) <- copy sym
 641     var sym-data-ah/eax: (addr handle stream byte) <- get sym2, text-data
 642     var sym-data/eax: (addr stream byte) <- lookup *sym-data-ah
 643     rewind-stream sym-data
 644     write-stream stream, sym-data
 645     write stream, " in "
 646     var env-ah/eax: (addr handle cell) <- address env-h
 647     print-cell env-ah, stream, 0/no-trace
 648     trace trace, "eval", stream
 649   }
 650   trace-lower trace
 651   var _env/eax: (addr cell) <- lookup env-h
 652   var env/ebx: (addr cell) <- copy _env
 653   # if env is not a list, abort
 654   {
 655     var env-type/ecx: (addr int) <- get env, type
 656     compare *env-type, 0/pair
 657     break-if-=
 658     error trace, "eval found a non-list environment"
 659     trace-higher trace
 660     return
 661   }
 662   # if env is nil, look up in globals
 663   {
 664     var env-nil?/eax: boolean <- nil? env
 665     compare env-nil?, 0/false
 666     break-if-=
 667     debug-print "b", 7/fg, 0xc5/bg=blue-bg
 668     lookup-symbol-in-globals sym, out, globals, trace, screen-cell, keyboard-cell
 669     debug-print "x", 7/fg, 0xc5/bg=blue-bg
 670     trace-higher trace
 671 +-- 15 lines: # trace "=> " out " (global)" ---------------------------------------------------------------------------------------------------------------------------------------------
 686     debug-print "y", 7/fg, 0xc5/bg=blue-bg
 687     return
 688   }
 689   # check car
 690   var env-head-storage: (handle cell)
 691   var env-head-ah/eax: (addr handle cell) <- address env-head-storage
 692   car env, env-head-ah, 0/no-trace
 693   var _env-head/eax: (addr cell) <- lookup *env-head-ah
 694   var env-head/ecx: (addr cell) <- copy _env-head
 695   # if car is not a list, abort
 696   {
 697     var env-head-type/eax: (addr int) <- get env-head, type
 698     compare *env-head-type, 0/pair
 699     break-if-=
 700     error trace, "environment is not a list of (key . value) pairs"
 701     trace-higher trace
 702     return
 703   }
 704   # check key
 705   var curr-key-storage: (handle cell)
 706   var curr-key-ah/eax: (addr handle cell) <- address curr-key-storage
 707   car env-head, curr-key-ah, trace
 708   var curr-key/eax: (addr cell) <- lookup *curr-key-ah
 709   # if key is not a symbol, abort
 710   {
 711     var curr-key-type/eax: (addr int) <- get curr-key, type
 712     compare *curr-key-type, 2/symbol
 713     break-if-=
 714     error trace, "environment contains a binding for a non-symbol"
 715     trace-higher trace
 716     return
 717   }
 718   # if key matches sym, return val
 719   var match?/eax: boolean <- cell-isomorphic? curr-key, sym, trace
 720   compare match?, 0/false
 721   {
 722     break-if-=
 723     cdr env-head, out, 0/no-trace
 724 +-- 15 lines: # trace "=> " out " (match)" ----------------------------------------------------------------------------------------------------------------------------------------------
 739     trace-higher trace
 740     return
 741   }
 742   # otherwise recurse
 743   var env-tail-storage: (handle cell)
 744   var env-tail-ah/eax: (addr handle cell) <- address env-tail-storage
 745   cdr env, env-tail-ah, trace
 746   lookup-symbol sym, out, *env-tail-ah, globals, trace, screen-cell, keyboard-cell
 747   trace-higher trace
 748 +-- 15 lines: # trace "=> " out " (recurse)" --------------------------------------------------------------------------------------------------------------------------------------------
 763 }
 764 
 765 fn test-lookup-symbol-in-env {
 766   # tmp = (a . 3)
 767   var val-storage: (handle cell)
 768   var val-ah/ecx: (addr handle cell) <- address val-storage
 769   new-integer val-ah, 3
 770   var key-storage: (handle cell)
 771   var key-ah/edx: (addr handle cell) <- address key-storage
 772   new-symbol key-ah, "a"
 773   var env-storage: (handle cell)
 774   var env-ah/ebx: (addr handle cell) <- address env-storage
 775   new-pair env-ah, *key-ah, *val-ah
 776   # env = ((a . 3))
 777   var nil-storage: (handle cell)
 778   var nil-ah/ecx: (addr handle cell) <- address nil-storage
 779   allocate-pair nil-ah
 780   new-pair env-ah, *env-ah, *nil-ah
 781   # lookup sym(a) in env tmp
 782   var tmp-storage: (handle cell)
 783   var tmp-ah/edx: (addr handle cell) <- address tmp-storage
 784   new-symbol tmp-ah, "a"
 785   var in/eax: (addr cell) <- lookup *tmp-ah
 786   lookup-symbol in, tmp-ah, *env-ah, 0/no-globals, 0/no-trace, 0/no-screen, 0/no-keyboard
 787   var result/eax: (addr cell) <- lookup *tmp-ah
 788   var result-type/edx: (addr int) <- get result, type
 789   check-ints-equal *result-type, 1/number, "F - test-lookup-symbol-in-env/0"
 790   var result-value-addr/eax: (addr float) <- get result, number-data
 791   var result-value/eax: int <- convert *result-value-addr
 792   check-ints-equal result-value, 3, "F - test-lookup-symbol-in-env/1"
 793 }
 794 
 795 fn test-lookup-symbol-in-globals {
 796   var globals-storage: global-table
 797   var globals/edi: (addr global-table) <- address globals-storage
 798   initialize-globals globals
 799   # env = nil
 800   var nil-storage: (handle cell)
 801   var nil-ah/ecx: (addr handle cell) <- address nil-storage
 802   allocate-pair nil-ah
 803   # lookup sym(a), env
 804   var tmp-storage: (handle cell)
 805   var tmp-ah/ebx: (addr handle cell) <- address tmp-storage
 806   new-symbol tmp-ah, "+"
 807   var in/eax: (addr cell) <- lookup *tmp-ah
 808   lookup-symbol in, tmp-ah, *nil-ah, globals, 0/no-trace, 0/no-screen, 0/no-keyboard
 809   var result/eax: (addr cell) <- lookup *tmp-ah
 810   var result-type/edx: (addr int) <- get result, type
 811   check-ints-equal *result-type, 4/primitive-function, "F - test-lookup-symbol-in-globals/0"
 812   var result-value/eax: (addr int) <- get result, index-data
 813   check-ints-equal *result-value, 1/add, "F - test-lookup-symbol-in-globals/1"
 814 }
 815 
 816 fn mutate-binding name: (addr stream byte), val: (addr handle cell), env-h: (handle cell), globals: (addr global-table), trace: (addr trace) {
 817   # trace name
 818   {
 819     compare trace, 0
 820     break-if-=
 821     var stream-storage: (stream byte 0x800)  # pessimistically sized just for the large alist loaded from disk in `main`
 822     var stream/ecx: (addr stream byte) <- address stream-storage
 823     write stream, "bind "
 824     rewind-stream name
 825     write-stream stream, name
 826     write stream, " to "
 827     print-cell val, stream, 0/no-trace
 828     write stream, " in "
 829     var env-ah/eax: (addr handle cell) <- address env-h
 830     print-cell env-ah, stream, 0/no-trace
 831     trace trace, "eval", stream
 832   }
 833   trace-lower trace
 834   var _env/eax: (addr cell) <- lookup env-h
 835   var env/ebx: (addr cell) <- copy _env
 836   # if env is not a list, abort
 837   {
 838     var env-type/ecx: (addr int) <- get env, type
 839     compare *env-type, 0/pair
 840     break-if-=
 841     error trace, "eval found a non-list environment"
 842     trace-higher trace
 843     return
 844   }
 845   # if env is nil, look in globals
 846   {
 847     var env-nil?/eax: boolean <- nil? env
 848     compare env-nil?, 0/false
 849     break-if-=
 850     debug-print "b", 3/fg, 0xc5/bg=blue-bg
 851     mutate-binding-in-globals name, val, globals, trace
 852     debug-print "x", 3/fg, 0xc5/bg=blue-bg
 853     trace-higher trace
 854 +-- 15 lines: # trace "=> " val " (global)" ---------------------------------------------------------------------------------------------------------------------------------------------
 869     debug-print "y", 3/fg, 0xc5/bg=blue-bg
 870     return
 871   }
 872   # check car
 873   var env-head-storage: (handle cell)
 874   var env-head-ah/eax: (addr handle cell) <- address env-head-storage
 875   car env, env-head-ah, 0/no-trace
 876   var _env-head/eax: (addr cell) <- lookup *env-head-ah
 877   var env-head/ecx: (addr cell) <- copy _env-head
 878   # if car is not a list, abort
 879   {
 880     var env-head-type/eax: (addr int) <- get env-head, type
 881     compare *env-head-type, 0/pair
 882     break-if-=
 883     error trace, "environment is not a list of (key . value) pairs"
 884     trace-higher trace
 885     return
 886   }
 887   # check key
 888   var curr-key-storage: (handle cell)
 889   var curr-key-ah/eax: (addr handle cell) <- address curr-key-storage
 890   car env-head, curr-key-ah, trace
 891   var curr-key/eax: (addr cell) <- lookup *curr-key-ah
 892   # if key is not a symbol, abort
 893   {
 894     var curr-key-type/eax: (addr int) <- get curr-key, type
 895     compare *curr-key-type, 2/symbol
 896     break-if-=
 897     error trace, "environment contains a binding for a non-symbol"
 898     trace-higher trace
 899     return
 900   }
 901   # if key matches name, return val
 902   var curr-key-data-ah/eax: (addr handle stream byte) <- get curr-key, text-data
 903   var curr-key-data/eax: (addr stream byte) <- lookup *curr-key-data-ah
 904   var match?/eax: boolean <- streams-data-equal? curr-key-data, name
 905   compare match?, 0/false
 906   {
 907     break-if-=
 908     var dest/eax: (addr handle cell) <- get env-head, right
 909     copy-object val, dest
 910     trace-text trace, "eval", "=> done"
 911     trace-higher trace
 912     return
 913   }
 914   # otherwise recurse
 915   var env-tail-storage: (handle cell)
 916   var env-tail-ah/eax: (addr handle cell) <- address env-tail-storage
 917   cdr env, env-tail-ah, trace
 918   mutate-binding name, val, *env-tail-ah, globals, trace
 919   trace-higher trace
 920 }
 921 
 922 fn car _in: (addr cell), out: (addr handle cell), trace: (addr trace) {
 923   trace-text trace, "eval", "car"
 924   trace-lower trace
 925   var in/eax: (addr cell) <- copy _in
 926   # if in is not a list, abort
 927   {
 928     var in-type/ecx: (addr int) <- get in, type
 929     compare *in-type, 0/pair
 930     break-if-=
 931     error trace, "car on a non-list"
 932     trace-higher trace
 933     return
 934   }
 935   # if in is nil, abort
 936   {
 937     var in-nil?/eax: boolean <- nil? in
 938     compare in-nil?, 0/false
 939     break-if-=
 940     error trace, "car on nil"
 941     trace-higher trace
 942     return
 943   }
 944   var in-left/eax: (addr handle cell) <- get in, left
 945   copy-object in-left, out
 946   trace-higher trace
 947   return
 948 }
 949 
 950 fn cdr _in: (addr cell), out: (addr handle cell), trace: (addr trace) {
 951   trace-text trace, "eval", "cdr"
 952   trace-lower trace
 953   var in/eax: (addr cell) <- copy _in
 954   # if in is not a list, abort
 955   {
 956     var in-type/ecx: (addr int) <- get in, type
 957     compare *in-type, 0/pair
 958     break-if-=
 959     error trace, "car on a non-list"
 960     trace-higher trace
 961     return
 962   }
 963   # if in is nil, abort
 964   {
 965     var in-nil?/eax: boolean <- nil? in
 966     compare in-nil?, 0/false
 967     break-if-=
 968     error trace, "car on nil"
 969     trace-higher trace
 970     return
 971   }
 972   var in-right/eax: (addr handle cell) <- get in, right
 973   copy-object in-right, out
 974   trace-higher trace
 975   return
 976 }
 977 
 978 fn cell-isomorphic? _a: (addr cell), _b: (addr cell), trace: (addr trace) -> _/eax: boolean {
 979   trace-text trace, "eval", "cell-isomorphic?"
 980   trace-lower trace
 981   var a/esi: (addr cell) <- copy _a
 982   var b/edi: (addr cell) <- copy _b
 983   # if types don't match, return false
 984   var a-type-addr/eax: (addr int) <- get a, type
 985   var b-type-addr/ecx: (addr int) <- get b, type
 986   var b-type/ecx: int <- copy *b-type-addr
 987   compare b-type, *a-type-addr
 988   {
 989     break-if-=
 990     trace-higher trace
 991     trace-text trace, "eval", "=> false (type)"
 992     return 0/false
 993   }
 994   # if types are number, compare number-data
 995   # TODO: exactly comparing floats is a bad idea
 996   compare b-type, 1/number
 997   {
 998     break-if-!=
 999     var a-val-addr/eax: (addr float) <- get a, number-data
1000     var b-val-addr/ecx: (addr float) <- get b, number-data
1001     var a-val/xmm0: float <- copy *a-val-addr
1002     compare a-val, *b-val-addr
1003     {
1004       break-if-=
1005       trace-higher trace
1006       trace-text trace, "eval", "=> false (numbers)"
1007       return 0/false
1008     }
1009     trace-higher trace
1010     trace-text trace, "eval", "=> true (numbers)"
1011     return 1/true
1012   }
1013   compare b-type, 2/symbol
1014   {
1015     break-if-!=
1016     var b-val-ah/eax: (addr handle stream byte) <- get b, text-data
1017     var _b-val/eax: (addr stream byte) <- lookup *b-val-ah
1018     var b-val/ecx: (addr stream byte) <- copy _b-val
1019     var a-val-ah/eax: (addr handle stream byte) <- get a, text-data
1020     var a-val/eax: (addr stream byte) <- lookup *a-val-ah
1021     var tmp-array: (handle array byte)
1022     var tmp-ah/edx: (addr handle array byte) <- address tmp-array
1023     rewind-stream a-val
1024     stream-to-array a-val, tmp-ah
1025     var tmp/eax: (addr array byte) <- lookup *tmp-ah
1026     var match?/eax: boolean <- stream-data-equal? b-val, tmp
1027     trace-higher trace
1028     {
1029       compare match?, 0/false
1030       break-if-=
1031       trace-text trace, "eval", "=> true (symbols)"
1032     }
1033     {
1034       compare match?, 0/false
1035       break-if-!=
1036       trace-text trace, "eval", "=> false (symbols)"
1037     }
1038     return match?
1039   }
1040   # if a is nil, b should be nil
1041   {
1042     # (assumes nil? returns 0 or 1)
1043     var _b-nil?/eax: boolean <- nil? b
1044     var b-nil?/ecx: boolean <- copy _b-nil?
1045     var a-nil?/eax: boolean <- nil? a
1046     # a == nil and b == nil => return true
1047     {
1048       compare a-nil?, 0/false
1049       break-if-=
1050       compare b-nil?, 0/false
1051       break-if-=
1052       trace-higher trace
1053       trace-text trace, "eval", "=> true (nils)"
1054       return 1/true
1055     }
1056     # a == nil => return false
1057     {
1058       compare a-nil?, 0/false
1059       break-if-=
1060       trace-higher trace
1061       trace-text trace, "eval", "=> false (b != nil)"
1062       return 0/false
1063     }
1064     # b == nil => return false
1065     {
1066       compare b-nil?, 0/false
1067       break-if-=
1068       trace-higher trace
1069       trace-text trace, "eval", "=> false (a != nil)"
1070       return 0/false
1071     }
1072   }
1073   # a and b are pairs
1074   var a-tmp-storage: (handle cell)
1075   var a-tmp-ah/edx: (addr handle cell) <- address a-tmp-storage
1076   var b-tmp-storage: (handle cell)
1077   var b-tmp-ah/ebx: (addr handle cell) <- address b-tmp-storage
1078   # if cars aren't equal, return false
1079   car a, a-tmp-ah, trace
1080   car b, b-tmp-ah, trace
1081   {
1082     var _a-tmp/eax: (addr cell) <- lookup *a-tmp-ah
1083     var a-tmp/ecx: (addr cell) <- copy _a-tmp
1084     var b-tmp/eax: (addr cell) <- lookup *b-tmp-ah
1085     var result/eax: boolean <- cell-isomorphic? a-tmp, b-tmp, trace
1086     compare result, 0/false
1087     break-if-!=
1088     trace-higher trace
1089     trace-text trace, "eval", "=> false (car mismatch)"
1090     return 0/false
1091   }
1092   # recurse on cdrs
1093   cdr a, a-tmp-ah, trace
1094   cdr b, b-tmp-ah, trace
1095   var _a-tmp/eax: (addr cell) <- lookup *a-tmp-ah
1096   var a-tmp/ecx: (addr cell) <- copy _a-tmp
1097   var b-tmp/eax: (addr cell) <- lookup *b-tmp-ah
1098   var result/eax: boolean <- cell-isomorphic? a-tmp, b-tmp, trace
1099   trace-higher trace
1100   return result
1101 }
1102 
1103 fn fn? _x: (addr cell) -> _/eax: boolean {
1104   var x/esi: (addr cell) <- copy _x
1105   var type/eax: (addr int) <- get x, type
1106   compare *type, 2/symbol
1107   {
1108     break-if-=
1109     return 0/false
1110   }
1111   var contents-ah/eax: (addr handle stream byte) <- get x, text-data
1112   var contents/eax: (addr stream byte) <- lookup *contents-ah
1113   var result/eax: boolean <- stream-data-equal? contents, "fn"
1114   return result
1115 }
1116 
1117 fn test-evaluate-is-well-behaved {
1118   var t-storage: trace
1119   var t/esi: (addr trace) <- address t-storage
1120   initialize-trace t, 0x10, 0/visible  # we don't use trace UI
1121   # env = nil
1122   var env-storage: (handle cell)
1123   var env-ah/ecx: (addr handle cell) <- address env-storage
1124   allocate-pair env-ah
1125   # eval sym(a), nil env
1126   var tmp-storage: (handle cell)
1127   var tmp-ah/edx: (addr handle cell) <- address tmp-storage
1128   new-symbol tmp-ah, "a"
1129   evaluate tmp-ah, tmp-ah, *env-ah, 0/no-globals, t, 0/no-screen, 0/no-keyboard, 0/call-number
1130   # doesn't die
1131   check-trace-contains t, "error", "unbound symbol: a", "F - test-evaluate-is-well-behaved"
1132 }
1133 
1134 fn test-evaluate-number {
1135   # env = nil
1136   var env-storage: (handle cell)
1137   var env-ah/ecx: (addr handle cell) <- address env-storage
1138   allocate-pair env-ah
1139   # tmp = 3
1140   var tmp-storage: (handle cell)
1141   var tmp-ah/edx: (addr handle cell) <- address tmp-storage
1142   new-integer tmp-ah, 3
1143   evaluate tmp-ah, tmp-ah, *env-ah, 0/no-globals, 0/no-trace, 0/no-screen, 0/no-keyboard, 0/call-number
1144   #
1145   var result/eax: (addr cell) <- lookup *tmp-ah
1146   var result-type/edx: (addr int) <- get result, type
1147   check-ints-equal *result-type, 1/number, "F - test-evaluate-number/0"
1148   var result-value-addr/eax: (addr float) <- get result, number-data
1149   var result-value/eax: int <- convert *result-value-addr
1150   check-ints-equal result-value, 3, "F - test-evaluate-number/1"
1151 }
1152 
1153 fn test-evaluate-symbol {
1154   # tmp = (a . 3)
1155   var val-storage: (handle cell)
1156   var val-ah/ecx: (addr handle cell) <- address val-storage
1157   new-integer val-ah, 3
1158   var key-storage: (handle cell)
1159   var key-ah/edx: (addr handle cell) <- address key-storage
1160   new-symbol key-ah, "a"
1161   var env-storage: (handle cell)
1162   var env-ah/ebx: (addr handle cell) <- address env-storage
1163   new-pair env-ah, *key-ah, *val-ah
1164   # env = ((a . 3))
1165   var nil-storage: (handle cell)
1166   var nil-ah/ecx: (addr handle cell) <- address nil-storage
1167   allocate-pair nil-ah
1168   new-pair env-ah, *env-ah, *nil-ah
1169   # eval sym(a), env
1170   var tmp-storage: (handle cell)
1171   var tmp-ah/edx: (addr handle cell) <- address tmp-storage
1172   new-symbol tmp-ah, "a"
1173   evaluate tmp-ah, tmp-ah, *env-ah, 0/no-globals, 0/no-trace, 0/no-screen, 0/no-keyboard, 0/call-number
1174   var result/eax: (addr cell) <- lookup *tmp-ah
1175   var result-type/edx: (addr int) <- get result, type
1176   check-ints-equal *result-type, 1/number, "F - test-evaluate-symbol/0"
1177   var result-value-addr/eax: (addr float) <- get result, number-data
1178   var result-value/eax: int <- convert *result-value-addr
1179   check-ints-equal result-value, 3, "F - test-evaluate-symbol/1"
1180 }
1181 
1182 fn test-evaluate-primitive-function {
1183   var globals-storage: global-table
1184   var globals/edi: (addr global-table) <- address globals-storage
1185   initialize-globals globals
1186   var nil-storage: (handle cell)
1187   var nil-ah/ecx: (addr handle cell) <- address nil-storage
1188   allocate-pair nil-ah
1189   var add-storage: (handle cell)
1190   var add-ah/ebx: (addr handle cell) <- address add-storage
1191   new-symbol add-ah, "+"
1192   # eval +, nil env
1193   var tmp-storage: (handle cell)
1194   var tmp-ah/esi: (addr handle cell) <- address tmp-storage
1195   evaluate add-ah, tmp-ah, *nil-ah, globals, 0/no-trace, 0/no-screen, 0/no-keyboard, 0/call-number
1196   #
1197   var result/eax: (addr cell) <- lookup *tmp-ah
1198   var result-type/edx: (addr int) <- get result, type
1199   check-ints-equal *result-type, 4/primitive-function, "F - test-evaluate-primitive-function/0"
1200   var result-value/eax: (addr int) <- get result, index-data
1201   check-ints-equal *result-value, 1/add, "F - test-evaluate-primitive-function/1"
1202 }
1203 
1204 fn test-evaluate-primitive-function-call {
1205   var t-storage: trace
1206   var t/edi: (addr trace) <- address t-storage
1207   initialize-trace t, 0x100, 0/visible  # we don't use trace UI
1208   #
1209   var nil-storage: (handle cell)
1210   var nil-ah/ecx: (addr handle cell) <- address nil-storage
1211   allocate-pair nil-ah
1212   var one-storage: (handle cell)
1213   var one-ah/edx: (addr handle cell) <- address one-storage
1214   new-integer one-ah, 1
1215   var add-storage: (handle cell)
1216   var add-ah/ebx: (addr handle cell) <- address add-storage
1217   new-symbol add-ah, "+"
1218   # input is (+ 1 1)
1219   var tmp-storage: (handle cell)
1220   var tmp-ah/esi: (addr handle cell) <- address tmp-storage
1221   new-pair tmp-ah, *one-ah, *nil-ah
1222   new-pair tmp-ah, *one-ah, *tmp-ah
1223   new-pair tmp-ah, *add-ah, *tmp-ah
1224 #?   dump-cell tmp-ah
1225   #
1226   var globals-storage: global-table
1227   var globals/edx: (addr global-table) <- address globals-storage
1228   initialize-globals globals
1229   #
1230   evaluate tmp-ah, tmp-ah, *nil-ah, globals, t, 0/no-screen, 0/no-keyboard, 0/call-number
1231 #?   dump-trace t
1232   #
1233   var result/eax: (addr cell) <- lookup *tmp-ah
1234   var result-type/edx: (addr int) <- get result, type
1235   check-ints-equal *result-type, 1/number, "F - test-evaluate-primitive-function-call/0"
1236   var result-value-addr/eax: (addr float) <- get result, number-data
1237   var result-value/eax: int <- convert *result-value-addr
1238   check-ints-equal result-value, 2, "F - test-evaluate-primitive-function-call/1"
1239 }