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-ah` or `env`
   3 # ignore args past 'trace' on a first reading; they're for the environment not the language
   4 # 'call-number' is just for showing intermediate progress; this is a _slow_ interpreter
   5 fn evaluate _in-ah: (addr handle cell), _out-ah: (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-ah/esi: (addr handle cell) <- copy _in-ah
  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-ah
  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/eax: (addr cell) <- lookup *in-ah
  68   {
  69     var nil?/eax: boolean <- nil? in
  70     compare nil?, 0/false
  71     break-if-=
  72     # nil is a literal
  73     trace-text trace, "eval", "nil"
  74     copy-object _in-ah, _out-ah
  75     trace-higher trace
  76     return
  77   }
  78   var in-type/ecx: (addr int) <- get in, 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-ah, _out-ah
  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-ah, _out-ah
  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, 0/bg
 102     lookup-symbol in, _out-ah, env-h, globals, trace, screen-cell, keyboard-cell
 103     debug-print "z", 7/fg, 0/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-ah, _out-ah
 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-ah, _out-ah
 120     trace-higher trace
 121     return
 122   }
 123   # 'in' is a syntax tree
 124   $evaluate:literal-function: {
 125     # trees starting with "litfn" are literals
 126     var expr/esi: (addr cell) <- copy in
 127     var in/edx: (addr cell) <- copy in
 128     var first-ah/ecx: (addr handle cell) <- get in, left
 129     var first/eax: (addr cell) <- lookup *first-ah
 130     var litfn?/eax: boolean <- litfn? first
 131     compare litfn?, 0/false
 132     break-if-=
 133     trace-text trace, "eval", "literal function"
 134     copy-object _in-ah, _out-ah
 135     trace-higher trace
 136     return
 137   }
 138   $evaluate:literal-macro: {
 139     # trees starting with "litmac" are literals
 140     var expr/esi: (addr cell) <- copy in
 141     var in/edx: (addr cell) <- copy in
 142     var first-ah/ecx: (addr handle cell) <- get in, left
 143     var first/eax: (addr cell) <- lookup *first-ah
 144     var litmac?/eax: boolean <- litmac? first
 145     compare litmac?, 0/false
 146     break-if-=
 147     trace-text trace, "eval", "literal macro"
 148     copy-object _in-ah, _out-ah
 149     trace-higher trace
 150     return
 151   }
 152   $evaluate:anonymous-function: {
 153     # trees starting with "fn" are anonymous functions
 154     var expr/esi: (addr cell) <- copy in
 155     var in/edx: (addr cell) <- copy in
 156     var first-ah/ecx: (addr handle cell) <- get in, left
 157     var first/eax: (addr cell) <- lookup *first-ah
 158     var fn?/eax: boolean <- fn? first
 159     compare fn?, 0/false
 160     break-if-=
 161     # turn (fn ...) into (litfn env ...)
 162     trace-text trace, "eval", "anonymous function"
 163     var rest-ah/eax: (addr handle cell) <- get in, right
 164     var tmp: (handle cell)
 165     var tmp-ah/edi: (addr handle cell) <- address tmp
 166     new-pair tmp-ah, env-h, *rest-ah
 167     var litfn: (handle cell)
 168     var litfn-ah/eax: (addr handle cell) <- address litfn
 169     new-symbol litfn-ah, "litfn"
 170     new-pair _out-ah, *litfn-ah, *tmp-ah
 171     trace-higher trace
 172     return
 173   }
 174   # builtins with "special" evaluation rules
 175   $evaluate:quote: {
 176     # trees starting with single quote create literals
 177     var expr/esi: (addr cell) <- copy in
 178     # if its first elem is not "'", break
 179     var first-ah/ecx: (addr handle cell) <- get in, left
 180     var rest-ah/edx: (addr handle cell) <- get in, right
 181     var first/eax: (addr cell) <- lookup *first-ah
 182     var quote?/eax: boolean <- symbol-equal? first, "'"
 183     compare quote?, 0/false
 184     break-if-=
 185     #
 186     trace-text trace, "eval", "quote"
 187     copy-object rest-ah, _out-ah
 188     trace-higher trace
 189     return
 190   }
 191   $evaluate:backquote: {
 192     # trees starting with single backquote create literals
 193     var expr/esi: (addr cell) <- copy in
 194     # if its first elem is not "'", break
 195     var first-ah/ecx: (addr handle cell) <- get in, left
 196     var rest-ah/edx: (addr handle cell) <- get in, right
 197     var first/eax: (addr cell) <- lookup *first-ah
 198     var backquote?/eax: boolean <- symbol-equal? first, "`"
 199     compare backquote?, 0/false
 200     break-if-=
 201     #
 202     trace-text trace, "eval", "backquote"
 203     debug-print "`(", 7/fg, 0/bg
 204     evaluate-backquote rest-ah, _out-ah, env-h, globals, trace, screen-cell, keyboard-cell, call-number
 205     debug-print ")", 7/fg, 0/bg
 206     trace-higher trace
 207     return
 208   }
 209   $evaluate:def: {
 210     # trees starting with "def" define globals
 211     var expr/esi: (addr cell) <- copy in
 212     # if its first elem is not "def", break
 213     var first-ah/ecx: (addr handle cell) <- get in, left
 214     var rest-ah/edx: (addr handle cell) <- get in, right
 215     var first/eax: (addr cell) <- lookup *first-ah
 216     var def?/eax: boolean <- symbol-equal? first, "def"
 217     compare def?, 0/false
 218     break-if-=
 219     #
 220     trace-text trace, "eval", "def"
 221     trace-text trace, "eval", "evaluating second arg"
 222     var rest/eax: (addr cell) <- lookup *rest-ah
 223     var first-arg-ah/ecx: (addr handle cell) <- get rest, left
 224     {
 225       var first-arg/eax: (addr cell) <- lookup *first-arg-ah
 226       var first-arg-type/eax: (addr int) <- get first-arg, type
 227       compare *first-arg-type, 2/symbol
 228       break-if-=
 229       error trace, "first arg to def must be a symbol"
 230       trace-higher trace
 231       return
 232     }
 233     rest-ah <- get rest, right
 234     rest <- lookup *rest-ah
 235     var second-arg-ah/edx: (addr handle cell) <- get rest, left
 236     debug-print "P", 4/fg, 0/bg
 237     increment call-number
 238     evaluate second-arg-ah, _out-ah, env-h, globals, trace, screen-cell, keyboard-cell, call-number
 239     debug-print "Q", 4/fg, 0/bg
 240     trace-text trace, "eval", "saving global binding"
 241     var first-arg/eax: (addr cell) <- lookup *first-arg-ah
 242     var first-arg-data-ah/eax: (addr handle stream byte) <- get first-arg, text-data
 243     var first-arg-data/eax: (addr stream byte) <- lookup *first-arg-data-ah
 244     var tmp-string: (handle array byte)
 245     var tmp-ah/edx: (addr handle array byte) <- address tmp-string
 246     rewind-stream first-arg-data
 247     stream-to-array first-arg-data, tmp-ah
 248     var first-arg-data-string/eax: (addr array byte) <- lookup *tmp-ah
 249     var out-ah/edi: (addr handle cell) <- copy _out-ah
 250     assign-or-create-global globals, first-arg-data-string, *out-ah, trace
 251     trace-higher trace
 252     return
 253   }
 254   $evaluate:set: {
 255     # trees starting with "set" mutate bindings
 256     var expr/esi: (addr cell) <- copy in
 257     # if its first elem is not "set", break
 258     var first-ah/ecx: (addr handle cell) <- get in, left
 259     var rest-ah/edx: (addr handle cell) <- get in, right
 260     var first/eax: (addr cell) <- lookup *first-ah
 261     var set?/eax: boolean <- symbol-equal? first, "set"
 262     compare set?, 0/false
 263     break-if-=
 264     #
 265     trace-text trace, "eval", "set"
 266     trace-text trace, "eval", "evaluating second arg"
 267     var rest/eax: (addr cell) <- lookup *rest-ah
 268     var first-arg-ah/ecx: (addr handle cell) <- get rest, left
 269     {
 270       var first-arg/eax: (addr cell) <- lookup *first-arg-ah
 271       var first-arg-type/eax: (addr int) <- get first-arg, type
 272       compare *first-arg-type, 2/symbol
 273       break-if-=
 274       error trace, "first arg to set must be a symbol"
 275       trace-higher trace
 276       return
 277     }
 278     rest-ah <- get rest, right
 279     rest <- lookup *rest-ah
 280     var second-arg-ah/edx: (addr handle cell) <- get rest, left
 281     debug-print "P", 4/fg, 0/bg
 282     increment call-number
 283     evaluate second-arg-ah, _out-ah, env-h, globals, trace, screen-cell, keyboard-cell, call-number
 284     debug-print "Q", 4/fg, 0/bg
 285     trace-text trace, "eval", "mutating binding"
 286     var first-arg/eax: (addr cell) <- lookup *first-arg-ah
 287     var first-arg-data-ah/eax: (addr handle stream byte) <- get first-arg, text-data
 288     var first-arg-data/eax: (addr stream byte) <- lookup *first-arg-data-ah
 289     mutate-binding first-arg-data, _out-ah, env-h, globals, trace
 290     trace-higher trace
 291     return
 292   }
 293   $evaluate:and: {
 294     var expr/esi: (addr cell) <- copy in
 295     # if its first elem is not "and", break
 296     var first-ah/ecx: (addr handle cell) <- get in, left
 297     var rest-ah/edx: (addr handle cell) <- get in, right
 298     var first/eax: (addr cell) <- lookup *first-ah
 299     var and?/eax: boolean <- symbol-equal? first, "and"
 300     compare and?, 0/false
 301     break-if-=
 302     #
 303     trace-text trace, "eval", "and"
 304     trace-text trace, "eval", "evaluating first arg"
 305     var rest/eax: (addr cell) <- lookup *rest-ah
 306     var first-arg-ah/ecx: (addr handle cell) <- get rest, left
 307     debug-print "R2", 4/fg, 0/bg
 308     increment call-number
 309     evaluate first-arg-ah, _out-ah, env-h, globals, trace, screen-cell, keyboard-cell, call-number
 310     debug-print "S2", 4/fg, 0/bg
 311     # if first arg is nil, short-circuit
 312     var out-ah/eax: (addr handle cell) <- copy _out-ah
 313     var out/eax: (addr cell) <- lookup *out-ah
 314     var nil?/eax: boolean <- nil? out
 315     compare nil?, 0/false
 316     {
 317       break-if-=
 318       return
 319     }
 320     var rest/eax: (addr cell) <- lookup *rest-ah
 321     rest-ah <- get rest, right
 322     rest <- lookup *rest-ah
 323     var second-ah/eax: (addr handle cell) <- get rest, left
 324     debug-print "T2", 4/fg, 0/bg
 325     increment call-number
 326     evaluate second-ah, _out-ah, env-h, globals, trace, screen-cell, keyboard-cell, call-number
 327     debug-print "U2", 4/fg, 0/bg
 328     trace-higher trace
 329     return
 330   }
 331   $evaluate:or: {
 332     var expr/esi: (addr cell) <- copy in
 333     # if its first elem is not "or", break
 334     var first-ah/ecx: (addr handle cell) <- get in, left
 335     var rest-ah/edx: (addr handle cell) <- get in, right
 336     var first/eax: (addr cell) <- lookup *first-ah
 337     var or?/eax: boolean <- symbol-equal? first, "or"
 338     compare or?, 0/false
 339     break-if-=
 340     #
 341     trace-text trace, "eval", "or"
 342     trace-text trace, "eval", "evaluating first arg"
 343     var rest/eax: (addr cell) <- lookup *rest-ah
 344     var first-arg-ah/ecx: (addr handle cell) <- get rest, left
 345     debug-print "R2", 4/fg, 0/bg
 346     increment call-number
 347     evaluate first-arg-ah, _out-ah, env-h, globals, trace, screen-cell, keyboard-cell, call-number
 348     debug-print "S2", 4/fg, 0/bg
 349     # if first arg is not nil, short-circuit
 350     var out-ah/eax: (addr handle cell) <- copy _out-ah
 351     var out/eax: (addr cell) <- lookup *out-ah
 352     var nil?/eax: boolean <- nil? out
 353     compare nil?, 0/false
 354     {
 355       break-if-!=
 356       return
 357     }
 358     var rest/eax: (addr cell) <- lookup *rest-ah
 359     rest-ah <- get rest, right
 360     rest <- lookup *rest-ah
 361     var second-ah/eax: (addr handle cell) <- get rest, left
 362     debug-print "T2", 4/fg, 0/bg
 363     increment call-number
 364     evaluate second-ah, _out-ah, env-h, globals, trace, screen-cell, keyboard-cell, call-number
 365     debug-print "U2", 4/fg, 0/bg
 366     trace-higher trace
 367     return
 368   }
 369   $evaluate:if: {
 370     # trees starting with "if" are conditionals
 371     var expr/esi: (addr cell) <- copy in
 372     # if its first elem is not "if", break
 373     var first-ah/ecx: (addr handle cell) <- get in, left
 374     var rest-ah/edx: (addr handle cell) <- get in, right
 375     var first/eax: (addr cell) <- lookup *first-ah
 376     var if?/eax: boolean <- symbol-equal? first, "if"
 377     compare if?, 0/false
 378     break-if-=
 379     #
 380     trace-text trace, "eval", "if"
 381     trace-text trace, "eval", "evaluating first arg"
 382     var rest/eax: (addr cell) <- lookup *rest-ah
 383     var first-arg-ah/ecx: (addr handle cell) <- get rest, left
 384     var guard-h: (handle cell)
 385     var guard-ah/esi: (addr handle cell) <- address guard-h
 386     debug-print "R", 4/fg, 0/bg
 387     increment call-number
 388     evaluate first-arg-ah, guard-ah, env-h, globals, trace, screen-cell, keyboard-cell, call-number
 389     debug-print "S", 4/fg, 0/bg
 390     rest-ah <- get rest, right
 391     rest <- lookup *rest-ah
 392     var branch-ah/edi: (addr handle cell) <- get rest, left
 393     var guard-a/eax: (addr cell) <- lookup *guard-ah
 394     var skip-to-third-arg?/eax: boolean <- nil? guard-a
 395     compare skip-to-third-arg?, 0/false
 396     {
 397       break-if-=
 398       trace-text trace, "eval", "skipping to third arg"
 399       var rest/eax: (addr cell) <- lookup *rest-ah
 400       rest-ah <- get rest, right
 401       rest <- lookup *rest-ah
 402       branch-ah <- get rest, left
 403     }
 404     debug-print "T", 4/fg, 0/bg
 405     increment call-number
 406     evaluate branch-ah, _out-ah, env-h, globals, trace, screen-cell, keyboard-cell, call-number
 407     debug-print "U", 4/fg, 0/bg
 408     trace-higher trace
 409     return
 410   }
 411   $evaluate:while: {
 412     # trees starting with "while" are loops
 413     var expr/esi: (addr cell) <- copy in
 414     # if its first elem is not "while", break
 415     var first-ah/ecx: (addr handle cell) <- get in, left
 416     var rest-ah/edx: (addr handle cell) <- get in, right
 417     var first/eax: (addr cell) <- lookup *first-ah
 418     var first-type/ecx: (addr int) <- get first, type
 419     compare *first-type, 2/symbol
 420     break-if-!=
 421     var sym-data-ah/eax: (addr handle stream byte) <- get first, text-data
 422     var sym-data/eax: (addr stream byte) <- lookup *sym-data-ah
 423     var while?/eax: boolean <- stream-data-equal? sym-data, "while"
 424     compare while?, 0/false
 425     break-if-=
 426     #
 427     trace-text trace, "eval", "while"
 428     var rest/eax: (addr cell) <- lookup *rest-ah
 429     var first-arg-ah/ecx: (addr handle cell) <- get rest, left
 430     rest-ah <- get rest, right
 431     var guard-h: (handle cell)
 432     var guard-ah/esi: (addr handle cell) <- address guard-h
 433     $evaluate:while:loop-execution: {
 434       {
 435         compare trace, 0
 436         break-if-=
 437         var error?/eax: boolean <- has-errors? trace
 438         compare error?, 0/false
 439         break-if-!= $evaluate:while:loop-execution
 440       }
 441       trace-text trace, "eval", "loop termination check"
 442       debug-print "V", 4/fg, 0/bg
 443       increment call-number
 444       evaluate first-arg-ah, guard-ah, env-h, globals, trace, screen-cell, keyboard-cell, call-number
 445       debug-print "W", 4/fg, 0/bg
 446       var guard-a/eax: (addr cell) <- lookup *guard-ah
 447       var done?/eax: boolean <- nil? guard-a
 448       compare done?, 0/false
 449       break-if-!=
 450       evaluate-exprs rest-ah, _out-ah, env-h, globals, trace, screen-cell, keyboard-cell, call-number
 451       loop
 452     }
 453     trace-text trace, "eval", "loop terminated"
 454     trace-higher trace
 455     return
 456   }
 457   trace-text trace, "eval", "function call"
 458   trace-text trace, "eval", "evaluating list elements"
 459   trace-lower trace
 460   var evaluated-list-storage: (handle cell)
 461   var evaluated-list-ah/esi: (addr handle cell) <- address evaluated-list-storage
 462   var curr-out-ah/edx: (addr handle cell) <- copy evaluated-list-ah
 463   var curr/ecx: (addr cell) <- copy in
 464   $evaluate-list:loop: {
 465     allocate-pair curr-out-ah
 466     var nil?/eax: boolean <- nil? curr
 467     compare nil?, 0/false
 468     break-if-!=
 469     # eval left
 470     var curr-out/eax: (addr cell) <- lookup *curr-out-ah
 471     var left-out-ah/edi: (addr handle cell) <- get curr-out, left
 472     var left-ah/esi: (addr handle cell) <- get curr, left
 473     debug-print "A", 4/fg, 0/bg
 474     increment call-number
 475     evaluate left-ah, left-out-ah, env-h, globals, trace, screen-cell, keyboard-cell, call-number
 476     debug-print "B", 4/fg, 0/bg
 477     # a trip wire in case we're running without a trace (e.g. when loading the initial state from disk)
 478     {
 479       compare trace, 0
 480       break-if-!=
 481       var left-out/eax: (addr cell) <- lookup *left-out-ah
 482       compare left-out, 0
 483       {
 484         break-if-!=
 485         abort "there was some error (likely in syntax): eval-list"
 486       }
 487     }
 488     #
 489     curr-out-ah <- get curr-out, right
 490     var right-ah/eax: (addr handle cell) <- get curr, right
 491     var right/eax: (addr cell) <- lookup *right-ah
 492     curr <- copy right
 493     loop
 494   }
 495   trace-higher trace
 496   var evaluated-list/eax: (addr cell) <- lookup *evaluated-list-ah
 497   var function-ah/ecx: (addr handle cell) <- get evaluated-list, left
 498   var args-ah/edx: (addr handle cell) <- get evaluated-list, right
 499   debug-print "C", 4/fg, 0/bg
 500   apply function-ah, args-ah, _out-ah, globals, trace, screen-cell, keyboard-cell, call-number
 501   debug-print "Y", 4/fg, 0/bg
 502   trace-higher trace
 503 +-- 11 lines: # trace "=> " _out-ah -----------------------------------------------------------------------------------------------------------------------------------------------------
 514   debug-print "Z", 4/fg, 0/bg
 515 }
 516 
 517 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 {
 518   var f-ah/eax: (addr handle cell) <- copy _f-ah
 519   var _f/eax: (addr cell) <- lookup *f-ah
 520   var f/esi: (addr cell) <- copy _f
 521   # call primitive functions
 522   {
 523     var f-type/eax: (addr int) <- get f, type
 524     compare *f-type, 4/primitive-function
 525     break-if-!=
 526     apply-primitive f, args-ah, out, globals, trace
 527     return
 528   }
 529   # if it's not a primitive function it must be an anonymous function
 530 +-- 14 lines: # trace "apply anonymous function " f " in environment " env --------------------------------------------------------------------------------------------------------------
 544   trace-lower trace
 545   {
 546     var f-type/ecx: (addr int) <- get f, type
 547     compare *f-type, 0/pair
 548     break-if-!=
 549     var first-ah/eax: (addr handle cell) <- get f, left
 550     var first/eax: (addr cell) <- lookup *first-ah
 551     var litfn?/eax: boolean <- litfn? first
 552     compare litfn?, 0/false
 553     break-if-=
 554     var rest-ah/esi: (addr handle cell) <- get f, right
 555     var rest/eax: (addr cell) <- lookup *rest-ah
 556     var callee-env-ah/edx: (addr handle cell) <- get rest, left
 557     rest-ah <- get rest, right
 558     rest <- lookup *rest-ah
 559     var params-ah/ecx: (addr handle cell) <- get rest, left
 560     var body-ah/eax: (addr handle cell) <- get rest, right
 561     debug-print "D", 7/fg, 0/bg
 562     apply-function params-ah, args-ah, body-ah, out, *callee-env-ah, globals, trace, screen-cell, keyboard-cell, call-number
 563     debug-print "Y", 7/fg, 0/bg
 564     trace-higher trace
 565     return
 566   }
 567   error trace, "unknown function"
 568 }
 569 
 570 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 {
 571   # push bindings for params to env
 572   var new-env-h: (handle cell)
 573   var new-env-ah/esi: (addr handle cell) <- address new-env-h
 574   push-bindings params-ah, args-ah, env-h, new-env-ah, trace
 575   #
 576   evaluate-exprs body-ah, out, new-env-h, globals, trace, screen-cell, keyboard-cell, call-number
 577 }
 578 
 579 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 {
 580   # eval all exprs, writing result to `out` each time
 581   var exprs-ah/ecx: (addr handle cell) <- copy _exprs-ah
 582   $evaluate-exprs:loop: {
 583     var exprs/eax: (addr cell) <- lookup *exprs-ah
 584     # stop when exprs is nil
 585     {
 586       var exprs-nil?/eax: boolean <- nil? exprs
 587       compare exprs-nil?, 0/false
 588       break-if-!= $evaluate-exprs:loop
 589     }
 590     # evaluate each expression, writing result to `out`
 591     {
 592       var curr-ah/eax: (addr handle cell) <- get exprs, left
 593       debug-print "E", 7/fg, 0/bg
 594       increment call-number
 595       evaluate curr-ah, out, env-h, globals, trace, screen-cell, keyboard-cell, call-number
 596       debug-print "X", 7/fg, 0/bg
 597     }
 598     #
 599     exprs-ah <- get exprs, right
 600     loop
 601   }
 602   # `out` contains result of evaluating final expression
 603 }
 604 
 605 # Bind params to corresponding args and add the bindings to old-env. Return
 606 # the result in env-ah.
 607 #
 608 # We never modify old-env, but we point to it. This way other parts of the
 609 # interpreter can continue using old-env, and everything works harmoniously
 610 # even though no cells are copied around.
 611 #
 612 # env should always be a DAG (ignoring internals of values). It doesn't have
 613 # to be a tree (some values may be shared), but there are also no cycles.
 614 #
 615 # Learn more: https://en.wikipedia.org/wiki/Persistent_data_structure
 616 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) {
 617   var params-ah/edx: (addr handle cell) <- copy _params-ah
 618   var args-ah/ebx: (addr handle cell) <- copy _args-ah
 619   var _params/eax: (addr cell) <- lookup *params-ah
 620   var params/esi: (addr cell) <- copy _params
 621   {
 622     var params-nil?/eax: boolean <- nil? params
 623     compare params-nil?, 0/false
 624     break-if-=
 625     # nil is a literal
 626     trace-text trace, "eval", "done with push-bindings"
 627     copy-handle old-env-h, env-ah
 628     return
 629   }
 630   # Params can only be symbols or pairs. Args can be anything.
 631 +-- 16 lines: # trace "pushing bindings from " params " to " args -----------------------------------------------------------------------------------------------------------------------
 647   trace-lower trace
 648   var params-type/eax: (addr int) <- get params, type
 649   compare *params-type, 2/symbol
 650   {
 651     break-if-!=
 652     trace-text trace, "eval", "symbol; binding to all remaining args"
 653     # create a new binding
 654     var new-binding-storage: (handle cell)
 655     var new-binding-ah/eax: (addr handle cell) <- address new-binding-storage
 656     new-pair new-binding-ah, *params-ah, *args-ah
 657     # push it to env
 658     new-pair env-ah, *new-binding-ah, old-env-h
 659     trace-higher trace
 660     return
 661   }
 662   compare *params-type, 0/pair
 663   {
 664     break-if-=
 665     error trace, "cannot bind a non-symbol"
 666     trace-higher trace
 667     return
 668   }
 669   var _args/eax: (addr cell) <- lookup *args-ah
 670   var args/edi: (addr cell) <- copy _args
 671   # params is now a pair, so args must be also
 672   var args-type/eax: (addr int) <- get args, type
 673   compare *args-type, 0/pair
 674   {
 675     break-if-=
 676     error trace, "args not in a proper list"
 677     trace-higher trace
 678     return
 679   }
 680   var intermediate-env-storage: (handle cell)
 681   var intermediate-env-ah/edx: (addr handle cell) <- address intermediate-env-storage
 682   var first-param-ah/eax: (addr handle cell) <- get params, left
 683   var first-arg-ah/ecx: (addr handle cell) <- get args, left
 684   push-bindings first-param-ah, first-arg-ah, old-env-h, intermediate-env-ah, trace
 685   var remaining-params-ah/eax: (addr handle cell) <- get params, right
 686   var remaining-args-ah/ecx: (addr handle cell) <- get args, right
 687   push-bindings remaining-params-ah, remaining-args-ah, *intermediate-env-ah, env-ah, trace
 688   trace-higher trace
 689 }
 690 
 691 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) {
 692   # trace sym
 693   {
 694     compare trace, 0
 695     break-if-=
 696     var stream-storage: (stream byte 0x800)  # pessimistically sized just for the large alist loaded from disk in `main`
 697     var stream/ecx: (addr stream byte) <- address stream-storage
 698     write stream, "look up "
 699     var sym2/eax: (addr cell) <- copy sym
 700     var sym-data-ah/eax: (addr handle stream byte) <- get sym2, text-data
 701     var sym-data/eax: (addr stream byte) <- lookup *sym-data-ah
 702     rewind-stream sym-data
 703     write-stream stream, sym-data
 704     write stream, " in "
 705     var env-ah/eax: (addr handle cell) <- address env-h
 706     print-cell env-ah, stream, 0/no-trace
 707     trace trace, "eval", stream
 708   }
 709   trace-lower trace
 710   var _env/eax: (addr cell) <- lookup env-h
 711   var env/ebx: (addr cell) <- copy _env
 712   # if env is not a list, abort
 713   {
 714     var env-type/ecx: (addr int) <- get env, type
 715     compare *env-type, 0/pair
 716     break-if-=
 717     error trace, "eval found a non-list environment"
 718     trace-higher trace
 719     return
 720   }
 721   # if env is nil, look up in globals
 722   {
 723     var env-nil?/eax: boolean <- nil? env
 724     compare env-nil?, 0/false
 725     break-if-=
 726     debug-print "b", 7/fg, 0/bg
 727     lookup-symbol-in-globals sym, out, globals, trace, screen-cell, keyboard-cell
 728     debug-print "x", 7/fg, 0/bg
 729     trace-higher trace
 730 +-- 15 lines: # trace "=> " out " (global)" ---------------------------------------------------------------------------------------------------------------------------------------------
 745     debug-print "y", 7/fg, 0/bg
 746     return
 747   }
 748   # check car
 749   var env-head-storage: (handle cell)
 750   var env-head-ah/eax: (addr handle cell) <- address env-head-storage
 751   car env, env-head-ah, 0/no-trace
 752   var _env-head/eax: (addr cell) <- lookup *env-head-ah
 753   var env-head/ecx: (addr cell) <- copy _env-head
 754   # if car is not a list, abort
 755   {
 756     var env-head-type/eax: (addr int) <- get env-head, type
 757     compare *env-head-type, 0/pair
 758     break-if-=
 759     error trace, "environment is not a list of (key . value) pairs"
 760     trace-higher trace
 761     return
 762   }
 763   # check key
 764   var curr-key-storage: (handle cell)
 765   var curr-key-ah/eax: (addr handle cell) <- address curr-key-storage
 766   car env-head, curr-key-ah, trace
 767   var curr-key/eax: (addr cell) <- lookup *curr-key-ah
 768   # if key is not a symbol, abort
 769   {
 770     var curr-key-type/eax: (addr int) <- get curr-key, type
 771     compare *curr-key-type, 2/symbol
 772     break-if-=
 773     error trace, "environment contains a binding for a non-symbol"
 774     trace-higher trace
 775     return
 776   }
 777   # if key matches sym, return val
 778   var match?/eax: boolean <- cell-isomorphic? curr-key, sym, trace
 779   compare match?, 0/false
 780   {
 781     break-if-=
 782     cdr env-head, out, 0/no-trace
 783 +-- 15 lines: # trace "=> " out " (match)" ----------------------------------------------------------------------------------------------------------------------------------------------
 798     trace-higher trace
 799     return
 800   }
 801   # otherwise recurse
 802   var env-tail-storage: (handle cell)
 803   var env-tail-ah/eax: (addr handle cell) <- address env-tail-storage
 804   cdr env, env-tail-ah, trace
 805   lookup-symbol sym, out, *env-tail-ah, globals, trace, screen-cell, keyboard-cell
 806   trace-higher trace
 807 +-- 15 lines: # trace "=> " out " (recurse)" --------------------------------------------------------------------------------------------------------------------------------------------
 822 }
 823 
 824 fn test-lookup-symbol-in-env {
 825   # tmp = (a . 3)
 826   var val-storage: (handle cell)
 827   var val-ah/ecx: (addr handle cell) <- address val-storage
 828   new-integer val-ah, 3
 829   var key-storage: (handle cell)
 830   var key-ah/edx: (addr handle cell) <- address key-storage
 831   new-symbol key-ah, "a"
 832   var env-storage: (handle cell)
 833   var env-ah/ebx: (addr handle cell) <- address env-storage
 834   new-pair env-ah, *key-ah, *val-ah
 835   # env = ((a . 3))
 836   var nil-storage: (handle cell)
 837   var nil-ah/ecx: (addr handle cell) <- address nil-storage
 838   allocate-pair nil-ah
 839   new-pair env-ah, *env-ah, *nil-ah
 840   # lookup sym(a) in env tmp
 841   var tmp-storage: (handle cell)
 842   var tmp-ah/edx: (addr handle cell) <- address tmp-storage
 843   new-symbol tmp-ah, "a"
 844   var in/eax: (addr cell) <- lookup *tmp-ah
 845   lookup-symbol in, tmp-ah, *env-ah, 0/no-globals, 0/no-trace, 0/no-screen, 0/no-keyboard
 846   var result/eax: (addr cell) <- lookup *tmp-ah
 847   var result-type/edx: (addr int) <- get result, type
 848   check-ints-equal *result-type, 1/number, "F - test-lookup-symbol-in-env/0"
 849   var result-value-addr/eax: (addr float) <- get result, number-data
 850   var result-value/eax: int <- convert *result-value-addr
 851   check-ints-equal result-value, 3, "F - test-lookup-symbol-in-env/1"
 852 }
 853 
 854 fn test-lookup-symbol-in-globals {
 855   var globals-storage: global-table
 856   var globals/edi: (addr global-table) <- address globals-storage
 857   initialize-globals globals
 858   # env = nil
 859   var nil-storage: (handle cell)
 860   var nil-ah/ecx: (addr handle cell) <- address nil-storage
 861   allocate-pair nil-ah
 862   # lookup sym(a), env
 863   var tmp-storage: (handle cell)
 864   var tmp-ah/ebx: (addr handle cell) <- address tmp-storage
 865   new-symbol tmp-ah, "+"
 866   var in/eax: (addr cell) <- lookup *tmp-ah
 867   lookup-symbol in, tmp-ah, *nil-ah, globals, 0/no-trace, 0/no-screen, 0/no-keyboard
 868   var result/eax: (addr cell) <- lookup *tmp-ah
 869   var result-type/edx: (addr int) <- get result, type
 870   check-ints-equal *result-type, 4/primitive-function, "F - test-lookup-symbol-in-globals/0"
 871   var result-value/eax: (addr int) <- get result, index-data
 872   check-ints-equal *result-value, 1/add, "F - test-lookup-symbol-in-globals/1"
 873 }
 874 
 875 fn mutate-binding name: (addr stream byte), val: (addr handle cell), env-h: (handle cell), globals: (addr global-table), trace: (addr trace) {
 876   # trace name
 877   {
 878     compare trace, 0
 879     break-if-=
 880     var stream-storage: (stream byte 0x800)  # pessimistically sized just for the large alist loaded from disk in `main`
 881     var stream/ecx: (addr stream byte) <- address stream-storage
 882     write stream, "bind "
 883     rewind-stream name
 884     write-stream stream, name
 885     write stream, " to "
 886     print-cell val, stream, 0/no-trace
 887     write stream, " in "
 888     var env-ah/eax: (addr handle cell) <- address env-h
 889     print-cell env-ah, stream, 0/no-trace
 890     trace trace, "eval", stream
 891   }
 892   trace-lower trace
 893   var _env/eax: (addr cell) <- lookup env-h
 894   var env/ebx: (addr cell) <- copy _env
 895   # if env is not a list, abort
 896   {
 897     var env-type/ecx: (addr int) <- get env, type
 898     compare *env-type, 0/pair
 899     break-if-=
 900     error trace, "eval found a non-list environment"
 901     trace-higher trace
 902     return
 903   }
 904   # if env is nil, look in globals
 905   {
 906     var env-nil?/eax: boolean <- nil? env
 907     compare env-nil?, 0/false
 908     break-if-=
 909     debug-print "b", 3/fg, 0/bg
 910     mutate-binding-in-globals name, val, globals, trace
 911     debug-print "x", 3/fg, 0/bg
 912     trace-higher trace
 913 +-- 15 lines: # trace "=> " val " (global)" ---------------------------------------------------------------------------------------------------------------------------------------------
 928     debug-print "y", 3/fg, 0/bg
 929     return
 930   }
 931   # check car
 932   var env-head-storage: (handle cell)
 933   var env-head-ah/eax: (addr handle cell) <- address env-head-storage
 934   car env, env-head-ah, 0/no-trace
 935   var _env-head/eax: (addr cell) <- lookup *env-head-ah
 936   var env-head/ecx: (addr cell) <- copy _env-head
 937   # if car is not a list, abort
 938   {
 939     var env-head-type/eax: (addr int) <- get env-head, type
 940     compare *env-head-type, 0/pair
 941     break-if-=
 942     error trace, "environment is not a list of (key . value) pairs"
 943     trace-higher trace
 944     return
 945   }
 946   # check key
 947   var curr-key-storage: (handle cell)
 948   var curr-key-ah/eax: (addr handle cell) <- address curr-key-storage
 949   car env-head, curr-key-ah, trace
 950   var curr-key/eax: (addr cell) <- lookup *curr-key-ah
 951   # if key is not a symbol, abort
 952   {
 953     var curr-key-type/eax: (addr int) <- get curr-key, type
 954     compare *curr-key-type, 2/symbol
 955     break-if-=
 956     error trace, "environment contains a binding for a non-symbol"
 957     trace-higher trace
 958     return
 959   }
 960   # if key matches name, return val
 961   var curr-key-data-ah/eax: (addr handle stream byte) <- get curr-key, text-data
 962   var curr-key-data/eax: (addr stream byte) <- lookup *curr-key-data-ah
 963   var match?/eax: boolean <- streams-data-equal? curr-key-data, name
 964   compare match?, 0/false
 965   {
 966     break-if-=
 967     var dest/eax: (addr handle cell) <- get env-head, right
 968     copy-object val, dest
 969     trace-text trace, "eval", "=> done"
 970     trace-higher trace
 971     return
 972   }
 973   # otherwise recurse
 974   var env-tail-storage: (handle cell)
 975   var env-tail-ah/eax: (addr handle cell) <- address env-tail-storage
 976   cdr env, env-tail-ah, trace
 977   mutate-binding name, val, *env-tail-ah, globals, trace
 978   trace-higher trace
 979 }
 980 
 981 fn car _in: (addr cell), out: (addr handle cell), trace: (addr trace) {
 982   trace-text trace, "eval", "car"
 983   trace-lower trace
 984   var in/eax: (addr cell) <- copy _in
 985   # if in is not a list, abort
 986   {
 987     var in-type/ecx: (addr int) <- get in, type
 988     compare *in-type, 0/pair
 989     break-if-=
 990     error trace, "car on a non-list"
 991     trace-higher trace
 992     return
 993   }
 994   # if in is nil, abort
 995   {
 996     var in-nil?/eax: boolean <- nil? in
 997     compare in-nil?, 0/false
 998     break-if-=
 999     error trace, "car on nil"
1000     trace-higher trace
1001     return
1002   }
1003   var in-left/eax: (addr handle cell) <- get in, left
1004   copy-object in-left, out
1005   trace-higher trace
1006   return
1007 }
1008 
1009 fn cdr _in: (addr cell), out: (addr handle cell), trace: (addr trace) {
1010   trace-text trace, "eval", "cdr"
1011   trace-lower trace
1012   var in/eax: (addr cell) <- copy _in
1013   # if in is not a list, abort
1014   {
1015     var in-type/ecx: (addr int) <- get in, type
1016     compare *in-type, 0/pair
1017     break-if-=
1018     error trace, "car on a non-list"
1019     trace-higher trace
1020     return
1021   }
1022   # if in is nil, abort
1023   {
1024     var in-nil?/eax: boolean <- nil? in
1025     compare in-nil?, 0/false
1026     break-if-=
1027     error trace, "car on nil"
1028     trace-higher trace
1029     return
1030   }
1031   var in-right/eax: (addr handle cell) <- get in, right
1032   copy-object in-right, out
1033   trace-higher trace
1034   return
1035 }
1036 
1037 fn cell-isomorphic? _a: (addr cell), _b: (addr cell), trace: (addr trace) -> _/eax: boolean {
1038   trace-text trace, "eval", "cell-isomorphic?"
1039   trace-lower trace
1040   var a/esi: (addr cell) <- copy _a
1041   var b/edi: (addr cell) <- copy _b
1042   # if types don't match, return false
1043   var a-type-addr/eax: (addr int) <- get a, type
1044   var b-type-addr/ecx: (addr int) <- get b, type
1045   var b-type/ecx: int <- copy *b-type-addr
1046   compare b-type, *a-type-addr
1047   {
1048     break-if-=
1049     trace-higher trace
1050     trace-text trace, "eval", "=> false (type)"
1051     return 0/false
1052   }
1053   # if types are number, compare number-data
1054   # TODO: exactly comparing floats is a bad idea
1055   compare b-type, 1/number
1056   {
1057     break-if-!=
1058     var a-val-addr/eax: (addr float) <- get a, number-data
1059     var b-val-addr/ecx: (addr float) <- get b, number-data
1060     var a-val/xmm0: float <- copy *a-val-addr
1061     compare a-val, *b-val-addr
1062     {
1063       break-if-=
1064       trace-higher trace
1065       trace-text trace, "eval", "=> false (numbers)"
1066       return 0/false
1067     }
1068     trace-higher trace
1069     trace-text trace, "eval", "=> true (numbers)"
1070     return 1/true
1071   }
1072   $cell-isomorphic?:text-data: {
1073     {
1074       compare b-type, 2/symbol
1075       break-if-=
1076       compare b-type, 3/stream
1077       break-if-=
1078       break $cell-isomorphic?:text-data
1079     }
1080     var b-val-ah/eax: (addr handle stream byte) <- get b, text-data
1081     var _b-val/eax: (addr stream byte) <- lookup *b-val-ah
1082     var b-val/ecx: (addr stream byte) <- copy _b-val
1083     var a-val-ah/eax: (addr handle stream byte) <- get a, text-data
1084     var a-val/eax: (addr stream byte) <- lookup *a-val-ah
1085     var tmp-array: (handle array byte)
1086     var tmp-ah/edx: (addr handle array byte) <- address tmp-array
1087     rewind-stream a-val
1088     stream-to-array a-val, tmp-ah
1089     var tmp/eax: (addr array byte) <- lookup *tmp-ah
1090     var match?/eax: boolean <- stream-data-equal? b-val, tmp
1091     trace-higher trace
1092     {
1093       compare match?, 0/false
1094       break-if-=
1095       trace-text trace, "eval", "=> true (symbols)"
1096     }
1097     {
1098       compare match?, 0/false
1099       break-if-!=
1100       trace-text trace, "eval", "=> false (symbols)"
1101     }
1102     return match?
1103   }
1104   # if objects are primitive functions, compare index-data
1105   compare b-type, 4/primitive
1106   {
1107     break-if-!=
1108     var a-val-addr/eax: (addr int) <- get a, index-data
1109     var b-val-addr/ecx: (addr int) <- get b, index-data
1110     var a-val/eax: int <- copy *a-val-addr
1111     compare a-val, *b-val-addr
1112     {
1113       break-if-=
1114       trace-higher trace
1115       trace-text trace, "eval", "=> false (primitives)"
1116       return 0/false
1117     }
1118     trace-higher trace
1119     trace-text trace, "eval", "=> true (primitives)"
1120     return 1/true
1121   }
1122   # if objects are screens, check if they're the same object
1123   compare b-type, 5/screen
1124   {
1125     break-if-!=
1126     var a-val-addr/eax: (addr handle screen) <- get a, screen-data
1127     var b-val-addr/ecx: (addr handle screen) <- get b, screen-data
1128     var result/eax: boolean <- handle-equal? *a-val-addr, *b-val-addr
1129     compare result, 0/false
1130     return result
1131   }
1132   # if objects are keyboards, check if they have the same contents
1133   compare b-type, 6/keyboard
1134   {
1135     break-if-!=
1136     var a-val-addr/ecx: (addr handle gap-buffer) <- get a, keyboard-data
1137     var _a/eax: (addr gap-buffer) <- lookup *a-val-addr
1138     var a/ecx: (addr gap-buffer) <- copy _a
1139     var b-val-addr/eax: (addr handle gap-buffer) <- get b, keyboard-data
1140     var b/eax: (addr gap-buffer) <- lookup *b-val-addr
1141     var result/eax: boolean <- gap-buffers-equal? a, b
1142     return result
1143   }
1144   # if a is nil, b should be nil
1145   {
1146     # (assumes nil? returns 0 or 1)
1147     var _b-nil?/eax: boolean <- nil? b
1148     var b-nil?/ecx: boolean <- copy _b-nil?
1149     var a-nil?/eax: boolean <- nil? a
1150     # a == nil and b == nil => return true
1151     {
1152       compare a-nil?, 0/false
1153       break-if-=
1154       compare b-nil?, 0/false
1155       break-if-=
1156       trace-higher trace
1157       trace-text trace, "eval", "=> true (nils)"
1158       return 1/true
1159     }
1160     # a == nil => return false
1161     {
1162       compare a-nil?, 0/false
1163       break-if-=
1164       trace-higher trace
1165       trace-text trace, "eval", "=> false (b != nil)"
1166       return 0/false
1167     }
1168     # b == nil => return false
1169     {
1170       compare b-nil?, 0/false
1171       break-if-=
1172       trace-higher trace
1173       trace-text trace, "eval", "=> false (a != nil)"
1174       return 0/false
1175     }
1176   }
1177   # a and b are pairs
1178   var a-tmp-storage: (handle cell)
1179   var a-tmp-ah/edx: (addr handle cell) <- address a-tmp-storage
1180   var b-tmp-storage: (handle cell)
1181   var b-tmp-ah/ebx: (addr handle cell) <- address b-tmp-storage
1182   # if cars aren't equal, return false
1183   car a, a-tmp-ah, trace
1184   car b, b-tmp-ah, trace
1185   {
1186     var _a-tmp/eax: (addr cell) <- lookup *a-tmp-ah
1187     var a-tmp/ecx: (addr cell) <- copy _a-tmp
1188     var b-tmp/eax: (addr cell) <- lookup *b-tmp-ah
1189     var result/eax: boolean <- cell-isomorphic? a-tmp, b-tmp, trace
1190     compare result, 0/false
1191     break-if-!=
1192     trace-higher trace
1193     trace-text trace, "eval", "=> false (car mismatch)"
1194     return 0/false
1195   }
1196   # recurse on cdrs
1197   cdr a, a-tmp-ah, trace
1198   cdr b, b-tmp-ah, trace
1199   var _a-tmp/eax: (addr cell) <- lookup *a-tmp-ah
1200   var a-tmp/ecx: (addr cell) <- copy _a-tmp
1201   var b-tmp/eax: (addr cell) <- lookup *b-tmp-ah
1202   var result/eax: boolean <- cell-isomorphic? a-tmp, b-tmp, trace
1203   trace-higher trace
1204   return result
1205 }
1206 
1207 fn fn? _x: (addr cell) -> _/eax: boolean {
1208   var x/esi: (addr cell) <- copy _x
1209   var type/eax: (addr int) <- get x, type
1210   compare *type, 2/symbol
1211   {
1212     break-if-=
1213     return 0/false
1214   }
1215   var contents-ah/eax: (addr handle stream byte) <- get x, text-data
1216   var contents/eax: (addr stream byte) <- lookup *contents-ah
1217   var result/eax: boolean <- stream-data-equal? contents, "fn"
1218   return result
1219 }
1220 
1221 fn litfn? _x: (addr cell) -> _/eax: boolean {
1222   var x/esi: (addr cell) <- copy _x
1223   var type/eax: (addr int) <- get x, type
1224   compare *type, 2/symbol
1225   {
1226     break-if-=
1227     return 0/false
1228   }
1229   var contents-ah/eax: (addr handle stream byte) <- get x, text-data
1230   var contents/eax: (addr stream byte) <- lookup *contents-ah
1231   var result/eax: boolean <- stream-data-equal? contents, "litfn"
1232   return result
1233 }
1234 
1235 fn litmac? _x: (addr cell) -> _/eax: boolean {
1236   var x/esi: (addr cell) <- copy _x
1237   var type/eax: (addr int) <- get x, type
1238   compare *type, 2/symbol
1239   {
1240     break-if-=
1241     return 0/false
1242   }
1243   var contents-ah/eax: (addr handle stream byte) <- get x, text-data
1244   var contents/eax: (addr stream byte) <- lookup *contents-ah
1245   var result/eax: boolean <- stream-data-equal? contents, "litmac"
1246   return result
1247 }
1248 
1249 fn test-evaluate-is-well-behaved {
1250   var t-storage: trace
1251   var t/esi: (addr trace) <- address t-storage
1252   initialize-trace t, 0x10, 0/visible  # we don't use trace UI
1253   # env = nil
1254   var env-storage: (handle cell)
1255   var env-ah/ecx: (addr handle cell) <- address env-storage
1256   allocate-pair env-ah
1257   # eval sym(a), nil env
1258   var tmp-storage: (handle cell)
1259   var tmp-ah/edx: (addr handle cell) <- address tmp-storage
1260   new-symbol tmp-ah, "a"
1261   evaluate tmp-ah, tmp-ah, *env-ah, 0/no-globals, t, 0/no-screen, 0/no-keyboard, 0/call-number
1262   # doesn't die
1263   check-trace-contains t, "error", "unbound symbol: a", "F - test-evaluate-is-well-behaved"
1264 }
1265 
1266 fn test-evaluate-number {
1267   # env = nil
1268   var env-storage: (handle cell)
1269   var env-ah/ecx: (addr handle cell) <- address env-storage
1270   allocate-pair env-ah
1271   # tmp = 3
1272   var tmp-storage: (handle cell)
1273   var tmp-ah/edx: (addr handle cell) <- address tmp-storage
1274   new-integer tmp-ah, 3
1275   evaluate tmp-ah, tmp-ah, *env-ah, 0/no-globals, 0/no-trace, 0/no-screen, 0/no-keyboard, 0/call-number
1276   #
1277   var result/eax: (addr cell) <- lookup *tmp-ah
1278   var result-type/edx: (addr int) <- get result, type
1279   check-ints-equal *result-type, 1/number, "F - test-evaluate-number/0"
1280   var result-value-addr/eax: (addr float) <- get result, number-data
1281   var result-value/eax: int <- convert *result-value-addr
1282   check-ints-equal result-value, 3, "F - test-evaluate-number/1"
1283 }
1284 
1285 fn test-evaluate-symbol {
1286   # tmp = (a . 3)
1287   var val-storage: (handle cell)
1288   var val-ah/ecx: (addr handle cell) <- address val-storage
1289   new-integer val-ah, 3
1290   var key-storage: (handle cell)
1291   var key-ah/edx: (addr handle cell) <- address key-storage
1292   new-symbol key-ah, "a"
1293   var env-storage: (handle cell)
1294   var env-ah/ebx: (addr handle cell) <- address env-storage
1295   new-pair env-ah, *key-ah, *val-ah
1296   # env = ((a . 3))
1297   var nil-storage: (handle cell)
1298   var nil-ah/ecx: (addr handle cell) <- address nil-storage
1299   allocate-pair nil-ah
1300   new-pair env-ah, *env-ah, *nil-ah
1301   # eval sym(a), env
1302   var tmp-storage: (handle cell)
1303   var tmp-ah/edx: (addr handle cell) <- address tmp-storage
1304   new-symbol tmp-ah, "a"
1305   evaluate tmp-ah, tmp-ah, *env-ah, 0/no-globals, 0/no-trace, 0/no-screen, 0/no-keyboard, 0/call-number
1306   var result/eax: (addr cell) <- lookup *tmp-ah
1307   var result-type/edx: (addr int) <- get result, type
1308   check-ints-equal *result-type, 1/number, "F - test-evaluate-symbol/0"
1309   var result-value-addr/eax: (addr float) <- get result, number-data
1310   var result-value/eax: int <- convert *result-value-addr
1311   check-ints-equal result-value, 3, "F - test-evaluate-symbol/1"
1312 }
1313 
1314 fn test-evaluate-quote {
1315   # env = nil
1316   var nil-storage: (handle cell)
1317   var nil-ah/ecx: (addr handle cell) <- address nil-storage
1318   allocate-pair nil-ah
1319   # eval `a, env
1320   var tmp-storage: (handle cell)
1321   var tmp-ah/edx: (addr handle cell) <- address tmp-storage
1322   new-symbol tmp-ah, "'"
1323   var tmp2-storage: (handle cell)
1324   var tmp2-ah/ebx: (addr handle cell) <- address tmp2-storage
1325   new-symbol tmp2-ah, "a"
1326   new-pair tmp-ah, *tmp-ah, *tmp2-ah
1327   evaluate tmp-ah, tmp-ah, *nil-ah, 0/no-globals, 0/no-trace, 0/no-screen, 0/no-keyboard, 0/call-number
1328   var result/eax: (addr cell) <- lookup *tmp-ah
1329   var result-type/edx: (addr int) <- get result, type
1330   check-ints-equal *result-type, 2/symbol, "F - test-evaluate-quote/0"
1331   var sym?/eax: boolean <- symbol-equal? result, "a"
1332   check sym?, "F - test-evaluate-quote/1"
1333 }
1334 
1335 fn test-evaluate-primitive-function {
1336   var globals-storage: global-table
1337   var globals/edi: (addr global-table) <- address globals-storage
1338   initialize-globals globals
1339   var nil-storage: (handle cell)
1340   var nil-ah/ecx: (addr handle cell) <- address nil-storage
1341   allocate-pair nil-ah
1342   var add-storage: (handle cell)
1343   var add-ah/ebx: (addr handle cell) <- address add-storage
1344   new-symbol add-ah, "+"
1345   # eval +, nil env
1346   var tmp-storage: (handle cell)
1347   var tmp-ah/esi: (addr handle cell) <- address tmp-storage
1348   evaluate add-ah, tmp-ah, *nil-ah, globals, 0/no-trace, 0/no-screen, 0/no-keyboard, 0/call-number
1349   #
1350   var result/eax: (addr cell) <- lookup *tmp-ah
1351   var result-type/edx: (addr int) <- get result, type
1352   check-ints-equal *result-type, 4/primitive-function, "F - test-evaluate-primitive-function/0"
1353   var result-value/eax: (addr int) <- get result, index-data
1354   check-ints-equal *result-value, 1/add, "F - test-evaluate-primitive-function/1"
1355 }
1356 
1357 fn test-evaluate-primitive-function-call {
1358   var t-storage: trace
1359   var t/edi: (addr trace) <- address t-storage
1360   initialize-trace t, 0x100, 0/visible  # we don't use trace UI
1361   #
1362   var nil-storage: (handle cell)
1363   var nil-ah/ecx: (addr handle cell) <- address nil-storage
1364   allocate-pair nil-ah
1365   var one-storage: (handle cell)
1366   var one-ah/edx: (addr handle cell) <- address one-storage
1367   new-integer one-ah, 1
1368   var add-storage: (handle cell)
1369   var add-ah/ebx: (addr handle cell) <- address add-storage
1370   new-symbol add-ah, "+"
1371   # input is (+ 1 1)
1372   var tmp-storage: (handle cell)
1373   var tmp-ah/esi: (addr handle cell) <- address tmp-storage
1374   new-pair tmp-ah, *one-ah, *nil-ah
1375   new-pair tmp-ah, *one-ah, *tmp-ah
1376   new-pair tmp-ah, *add-ah, *tmp-ah
1377 #?   dump-cell tmp-ah
1378   #
1379   var globals-storage: global-table
1380   var globals/edx: (addr global-table) <- address globals-storage
1381   initialize-globals globals
1382   #
1383   evaluate tmp-ah, tmp-ah, *nil-ah, globals, t, 0/no-screen, 0/no-keyboard, 0/call-number
1384 #?   dump-trace t
1385   #
1386   var result/eax: (addr cell) <- lookup *tmp-ah
1387   var result-type/edx: (addr int) <- get result, type
1388   check-ints-equal *result-type, 1/number, "F - test-evaluate-primitive-function-call/0"
1389   var result-value-addr/eax: (addr float) <- get result, number-data
1390   var result-value/eax: int <- convert *result-value-addr
1391   check-ints-equal result-value, 2, "F - test-evaluate-primitive-function-call/1"
1392 }
1393 
1394 fn test-evaluate-backquote {
1395   # env = nil
1396   var nil-storage: (handle cell)
1397   var nil-ah/ecx: (addr handle cell) <- address nil-storage
1398   allocate-pair nil-ah
1399   # eval `a, env
1400   var tmp-storage: (handle cell)
1401   var tmp-ah/edx: (addr handle cell) <- address tmp-storage
1402   new-symbol tmp-ah, "`"
1403   var tmp2-storage: (handle cell)
1404   var tmp2-ah/ebx: (addr handle cell) <- address tmp2-storage
1405   new-symbol tmp2-ah, "a"
1406   new-pair tmp-ah, *tmp-ah, *tmp2-ah
1407   clear-object tmp2-ah
1408   evaluate tmp-ah, tmp2-ah, *nil-ah, 0/no-globals, 0/no-trace, 0/no-screen, 0/no-keyboard, 0/call-number
1409   var result/eax: (addr cell) <- lookup *tmp2-ah
1410   var result-type/edx: (addr int) <- get result, type
1411   check-ints-equal *result-type, 2/symbol, "F - test-evaluate-backquote/0"
1412   var sym?/eax: boolean <- symbol-equal? result, "a"
1413   check sym?, "F - test-evaluate-backquote/1"
1414 }
1415 
1416 fn evaluate-backquote _in-ah: (addr handle cell), _out-ah: (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 {
1417   # stack overflow?   # disable when enabling Really-debug-print
1418 #?   dump-cell-from-cursor-over-full-screen _in-ah
1419   check-stack
1420   {
1421     var screen-cell/eax: (addr handle cell) <- copy screen-cell
1422     compare screen-cell, 0
1423     break-if-=
1424     var screen-cell-addr/eax: (addr cell) <- lookup *screen-cell
1425     compare screen-cell-addr, 0
1426     break-if-=
1427     # if screen-cell exists, we're probably not in a test
1428     show-stack-state
1429   }
1430   # errors? skip
1431   {
1432     compare trace, 0
1433     break-if-=
1434     var error?/eax: boolean <- has-errors? trace
1435     compare error?, 0/false
1436     break-if-=
1437     return
1438   }
1439   var in-ah/esi: (addr handle cell) <- copy _in-ah
1440   var in/eax: (addr cell) <- lookup *in-ah
1441   {
1442     var nil?/eax: boolean <- nil? in
1443     compare nil?, 0/false
1444     break-if-=
1445     # nil is a literal
1446     trace-text trace, "eval", "backquote nil"
1447     copy-object _in-ah, _out-ah
1448     trace-higher trace
1449     return
1450   }
1451   var in-type/ecx: (addr int) <- get in, type
1452   compare *in-type, 0/pair
1453   {
1454     break-if-=
1455     # copy non-pairs directly
1456     # TODO: streams might need to be copied
1457     trace-text trace, "eval", "backquote atom"
1458     copy-object _in-ah, _out-ah
1459     trace-higher trace
1460     return
1461   }
1462   # 'in' is a pair
1463   debug-print "()", 4/fg, 0/bg
1464   var in-ah/esi: (addr handle cell) <- copy _in-ah
1465   var _in/eax: (addr cell) <- lookup *in-ah
1466   var in/ebx: (addr cell) <- copy _in
1467   var in-left-ah/ecx: (addr handle cell) <- get in, left
1468   debug-print "10", 4/fg, 0/bg
1469   # check for unquote
1470   $macroexpand-iter:unquote: {
1471     var in-left/eax: (addr cell) <- lookup *in-left-ah
1472     var unquote?/eax: boolean <- symbol-equal? in-left, ","
1473     compare unquote?, 0/false
1474     break-if-=
1475     trace-text trace, "eval", "unquote"
1476     var rest-ah/eax: (addr handle cell) <- get in, right
1477     increment call-number
1478     debug-print ",", 3/fg, 0/bg
1479     evaluate rest-ah, _out-ah, env-h, globals, trace, screen-cell, keyboard-cell, call-number
1480     debug-print ",)", 3/fg, 0/bg
1481     return
1482   }
1483   # check for unquote-splice in in-left
1484   debug-print "11", 4/fg, 0/bg
1485   var out-ah/edi: (addr handle cell) <- copy _out-ah
1486   $macroexpand-iter:unquote-splice: {
1487 #?     dump-cell-from-cursor-over-full-screen in-left-ah
1488     var in-left/eax: (addr cell) <- lookup *in-left-ah
1489     {
1490       debug-print "12", 4/fg, 0/bg
1491       {
1492         var in-left-is-nil?/eax: boolean <- nil? in-left
1493         compare in-left-is-nil?, 0/false
1494       }
1495       break-if-!= $macroexpand-iter:unquote-splice
1496       var in-left-type/ecx: (addr int) <- get in-left, type
1497       debug-print "13", 4/fg, 0/bg
1498       compare *in-left-type, 0/pair
1499       break-if-!= $macroexpand-iter:unquote-splice
1500       var in-left-left-ah/eax: (addr handle cell) <- get in-left, left
1501       debug-print "14", 4/fg, 0/bg
1502       var in-left-left/eax: (addr cell) <- lookup *in-left-left-ah
1503       debug-print "15", 4/fg, 0/bg
1504       var in-left-left-type/ecx: (addr int) <- get in-left-left, type
1505       var left-is-unquote-splice?/eax: boolean <- symbol-equal? in-left-left, ",@"
1506       debug-print "16", 4/fg, 0/bg
1507       compare left-is-unquote-splice?, 0/false
1508     }
1509     break-if-=
1510     debug-print "17", 4/fg, 0/bg
1511     trace-text trace, "eval", "unquote-splice"
1512     var in-unquote-payload-ah/eax: (addr handle cell) <- get in-left, right
1513     increment call-number
1514     evaluate in-unquote-payload-ah, out-ah, env-h, globals, trace, screen-cell, keyboard-cell, call-number
1515     # while (*out-ah != null) out-ah = cdr(out-ah)
1516     {
1517       var out/eax: (addr cell) <- lookup *out-ah
1518       {
1519         var done?/eax: boolean <- nil? out
1520         compare done?, 0/false
1521       }
1522       break-if-!=
1523       out-ah <- get out, right
1524       loop
1525     }
1526     # append result of in-right
1527     var in-right-ah/ecx: (addr handle cell) <- get in, right
1528     evaluate-backquote in-right-ah, out-ah, env-h, globals, trace, screen-cell, keyboard-cell, call-number
1529     return
1530   }
1531   debug-print "19", 4/fg, 0/bg
1532   # otherwise continue copying
1533   trace-text trace, "eval", "backquote: copy"
1534   var out-ah/edi: (addr handle cell) <- copy _out-ah
1535   allocate-pair out-ah
1536   debug-print "20", 7/fg, 0/bg
1537 #?   dump-cell-from-cursor-over-full-screen out-ah
1538   var out/eax: (addr cell) <- lookup *out-ah
1539   var out-left-ah/edx: (addr handle cell) <- get out, left
1540   debug-print "`(l", 3/fg, 0/bg
1541   evaluate-backquote in-left-ah, out-left-ah, env-h, globals, trace, screen-cell, keyboard-cell, call-number
1542   debug-print "`r)", 3/fg, 0/bg
1543   var in-right-ah/ecx: (addr handle cell) <- get in, right
1544   var out-right-ah/edx: (addr handle cell) <- get out, right
1545   debug-print "`r(", 3/fg, 0/bg
1546   evaluate-backquote in-right-ah, out-right-ah, env-h, globals, trace, screen-cell, keyboard-cell, call-number
1547   debug-print "`r)", 3/fg, 0/bg
1548 }
1549 
1550 fn test-evaluate-backquote-list {
1551   var nil-storage: (handle cell)
1552   var nil-ah/ecx: (addr handle cell) <- address nil-storage
1553   allocate-pair nil-ah
1554   var backquote-storage: (handle cell)
1555   var backquote-ah/edx: (addr handle cell) <- address backquote-storage
1556   new-symbol backquote-ah, "`"
1557   # input is `(a b)
1558   var a-storage: (handle cell)
1559   var a-ah/ebx: (addr handle cell) <- address a-storage
1560   new-symbol a-ah, "a"
1561   var b-storage: (handle cell)
1562   var b-ah/esi: (addr handle cell) <- address b-storage
1563   new-symbol b-ah, "b"
1564   var tmp-storage: (handle cell)
1565   var tmp-ah/eax: (addr handle cell) <- address tmp-storage
1566   new-pair tmp-ah, *b-ah, *nil-ah
1567   new-pair tmp-ah, *a-ah, *tmp-ah
1568   new-pair tmp-ah, *backquote-ah, *tmp-ah
1569   #
1570   evaluate tmp-ah, tmp-ah, *nil-ah, 0/no-globals, 0/no-trace, 0/no-screen, 0/no-keyboard, 0/call-number
1571   # result is (a b)
1572   var result/eax: (addr cell) <- lookup *tmp-ah
1573   {
1574     var result-type/eax: (addr int) <- get result, type
1575     check-ints-equal *result-type, 0/pair, "F - test-evaluate-backquote-list/0"
1576   }
1577   {
1578     var a1-ah/eax: (addr handle cell) <- get result, left
1579     var a1/eax: (addr cell) <- lookup *a1-ah
1580     var check1/eax: boolean <- symbol-equal? a1, "a"
1581     check check1, "F - test-evaluate-backquote-list/1"
1582   }
1583   var rest-ah/eax: (addr handle cell) <- get result, right
1584   var rest/eax: (addr cell) <- lookup *rest-ah
1585   {
1586     var a2-ah/eax: (addr handle cell) <- get rest, left
1587     var a2/eax: (addr cell) <- lookup *a2-ah
1588     var check2/eax: boolean <- symbol-equal? a2, "b"
1589     check check2, "F - test-evaluate-backquote-list/2"
1590   }
1591   var rest-ah/eax: (addr handle cell) <- get rest, right
1592   var rest/eax: (addr cell) <- lookup *rest-ah
1593   var check3/eax: boolean <- nil? rest
1594   check check3, "F - test-evaluate-backquote-list/3"
1595 }
1596 
1597 fn test-evaluate-backquote-list-with-unquote {
1598   var nil-h: (handle cell)
1599   var nil-ah/eax: (addr handle cell) <- address nil-h
1600   allocate-pair nil-ah
1601   var backquote-h: (handle cell)
1602   var backquote-ah/eax: (addr handle cell) <- address backquote-h
1603   new-symbol backquote-ah, "`"
1604   var unquote-h: (handle cell)
1605   var unquote-ah/eax: (addr handle cell) <- address unquote-h
1606   new-symbol unquote-ah, ","
1607   var a-h: (handle cell)
1608   var a-ah/eax: (addr handle cell) <- address a-h
1609   new-symbol a-ah, "a"
1610   var b-h: (handle cell)
1611   var b-ah/eax: (addr handle cell) <- address b-h
1612   new-symbol b-ah, "b"
1613   # env = ((b . 3))
1614   var val-h: (handle cell)
1615   var val-ah/eax: (addr handle cell) <- address val-h
1616   new-integer val-ah, 3
1617   var env-h: (handle cell)
1618   var env-ah/eax: (addr handle cell) <- address env-h
1619   new-pair env-ah, b-h, val-h
1620   new-pair env-ah, env-h, nil-h
1621   # input is `(a ,b)
1622   var tmp-h: (handle cell)
1623   var tmp-ah/eax: (addr handle cell) <- address tmp-h
1624   # tmp = cons(unquote, b)
1625   new-pair tmp-ah, unquote-h, b-h
1626   # tmp = cons(tmp, nil)
1627   new-pair tmp-ah, tmp-h, nil-h
1628   # tmp = cons(a, tmp)
1629   new-pair tmp-ah, a-h, tmp-h
1630   # tmp = cons(backquote, tmp)
1631   new-pair tmp-ah, backquote-h, tmp-h
1632   #
1633   evaluate tmp-ah, tmp-ah, env-h, 0/no-globals, 0/no-trace, 0/no-screen, 0/no-keyboard, 0/call-number
1634   # result is (a 3)
1635   var result/eax: (addr cell) <- lookup *tmp-ah
1636   {
1637     var result-type/eax: (addr int) <- get result, type
1638     check-ints-equal *result-type, 0/pair, "F - test-evaluate-backquote-list-with-unquote/0"
1639   }
1640   {
1641     var a1-ah/eax: (addr handle cell) <- get result, left
1642     var a1/eax: (addr cell) <- lookup *a1-ah
1643     var check1/eax: boolean <- symbol-equal? a1, "a"
1644     check check1, "F - test-evaluate-backquote-list-with-unquote/1"
1645   }
1646   var rest-ah/eax: (addr handle cell) <- get result, right
1647   var rest/eax: (addr cell) <- lookup *rest-ah
1648   {
1649     var a2-ah/eax: (addr handle cell) <- get rest, left
1650     var a2/eax: (addr cell) <- lookup *a2-ah
1651     var a2-value-addr/eax: (addr float) <- get a2, number-data
1652     var a2-value/eax: int <- convert *a2-value-addr
1653     check-ints-equal a2-value, 3, "F - test-evaluate-backquote-list-with-unquote/2"
1654   }
1655   var rest-ah/eax: (addr handle cell) <- get rest, right
1656   var rest/eax: (addr cell) <- lookup *rest-ah
1657   var check3/eax: boolean <- nil? rest
1658   check check3, "F - test-evaluate-backquote-list-with-unquote/3"
1659 }
1660 
1661 fn test-evaluate-backquote-list-with-unquote-splice {
1662   var nil-h: (handle cell)
1663   var nil-ah/eax: (addr handle cell) <- address nil-h
1664   allocate-pair nil-ah
1665   var backquote-h: (handle cell)
1666   var backquote-ah/eax: (addr handle cell) <- address backquote-h
1667   new-symbol backquote-ah, "`"
1668   var unquote-splice-h: (handle cell)
1669   var unquote-splice-ah/eax: (addr handle cell) <- address unquote-splice-h
1670   new-symbol unquote-splice-ah, ",@"
1671   var a-h: (handle cell)
1672   var a-ah/eax: (addr handle cell) <- address a-h
1673   new-symbol a-ah, "a"
1674   var b-h: (handle cell)
1675   var b-ah/eax: (addr handle cell) <- address b-h
1676   new-symbol b-ah, "b"
1677   # env = ((b . (a 3)))
1678   var val-h: (handle cell)
1679   var val-ah/eax: (addr handle cell) <- address val-h
1680   new-integer val-ah, 3
1681   new-pair val-ah, val-h, nil-h
1682   new-pair val-ah, a-h, val-h
1683   var env-h: (handle cell)
1684   var env-ah/eax: (addr handle cell) <- address env-h
1685   new-pair env-ah, b-h, val-h
1686   new-pair env-ah, env-h, nil-h
1687   # input is `(a ,@b b)
1688   var tmp-h: (handle cell)
1689   var tmp-ah/eax: (addr handle cell) <- address tmp-h
1690   # tmp = cons(b, nil)
1691   new-pair tmp-ah, b-h, nil-h
1692   # tmp2 = cons(unquote-splice, b)
1693   var tmp2-h: (handle cell)
1694   var tmp2-ah/ecx: (addr handle cell) <- address tmp2-h
1695   new-pair tmp2-ah, unquote-splice-h, b-h
1696   # tmp = cons(tmp2, tmp)
1697   new-pair tmp-ah, tmp2-h, tmp-h
1698   # tmp = cons(a, tmp)
1699   new-pair tmp-ah, a-h, tmp-h
1700   # tmp = cons(backquote, tmp)
1701   new-pair tmp-ah, backquote-h, tmp-h
1702 #?   dump-cell-from-cursor-over-full-screen tmp-ah
1703   #
1704   evaluate tmp-ah, tmp-ah, env-h, 0/no-globals, 0/no-trace, 0/no-screen, 0/no-keyboard, 0/call-number
1705   # result is (a a 3 b)
1706 #?   dump-cell-from-cursor-over-full-screen tmp-ah
1707   var result/eax: (addr cell) <- lookup *tmp-ah
1708   {
1709     var result-type/eax: (addr int) <- get result, type
1710     check-ints-equal *result-type, 0/pair, "F - test-evaluate-backquote-list-with-unquote-splice/0"
1711   }
1712   {
1713     var a1-ah/eax: (addr handle cell) <- get result, left
1714     var a1/eax: (addr cell) <- lookup *a1-ah
1715     var check1/eax: boolean <- symbol-equal? a1, "a"
1716     check check1, "F - test-evaluate-backquote-list-with-unquote-splice/1"
1717   }
1718   var rest-ah/eax: (addr handle cell) <- get result, right
1719   var rest/eax: (addr cell) <- lookup *rest-ah
1720   {
1721     var a2-ah/eax: (addr handle cell) <- get rest, left
1722     var a2/eax: (addr cell) <- lookup *a2-ah
1723     var check2/eax: boolean <- symbol-equal? a2, "a"
1724     check check2, "F - test-evaluate-backquote-list-with-unquote-splice/2"
1725   }
1726   var rest-ah/eax: (addr handle cell) <- get rest, right
1727   var rest/eax: (addr cell) <- lookup *rest-ah
1728   {
1729     var a3-ah/eax: (addr handle cell) <- get rest, left
1730     var a3/eax: (addr cell) <- lookup *a3-ah
1731     var a3-value-addr/eax: (addr float) <- get a3, number-data
1732     var a3-value/eax: int <- convert *a3-value-addr
1733     check-ints-equal a3-value, 3, "F - test-evaluate-backquote-list-with-unquote-splice/3"
1734   }
1735   var rest-ah/eax: (addr handle cell) <- get rest, right
1736   var rest/eax: (addr cell) <- lookup *rest-ah
1737   {
1738     var a4-ah/eax: (addr handle cell) <- get rest, left
1739     var a4/eax: (addr cell) <- lookup *a4-ah
1740     var check4/eax: boolean <- symbol-equal? a4, "b"
1741     check check4, "F - test-evaluate-backquote-list-with-unquote-splice/4"
1742   }
1743   var rest-ah/eax: (addr handle cell) <- get rest, right
1744   var rest/eax: (addr cell) <- lookup *rest-ah
1745   var check5/eax: boolean <- nil? rest
1746   check check5, "F - test-evaluate-backquote-list-with-unquote-splice/5"
1747 }