https://github.com/akkartik/mu/blob/master/apps/calls.subx
   1 # Function calls in a single line.
   2 #
   3 # To run (on Linux):
   4 #   $ ./translate_subx init.linux 0*.subx apps/subx-params.subx apps/calls.subx
   5 #   $ mv a.elf apps/calls
   6 #
   7 # Example 1:
   8 #   $ echo '(foo %eax)'                         |  apps/calls
   9 #   # . (foo %eax)                                      # output has comments
  10 #   ff 6/subop/push %eax                                # push
  11 #   e8/call foo/disp32                                  # call
  12 #   81 0/subop/add %esp 4/imm32                         # undo push
  13 #
  14 # Example 2:
  15 #   $ echo '(foo Var1 *(eax + 4) "blah")'       |  apps/calls
  16 #   # . (foo Var1 *(eax + 4) "blah")
  17 #   68/push "blah"/imm32
  18 #   ff 6/subop/push *(eax + 4)                          # push args in..
  19 #   68/push Var1/imm32                                  # ..reverse order
  20 #   e8/call foo/disp32
  21 #   81 0/subop/add %esp 0xc/imm32                       # undo pushes
  22 #
  23 # Calls always begin with '(' as the first non-whitespace on a line.
  24 
  25 == code
  26 
  27 Entry:  # run tests if necessary, convert stdin if not
  28     # . prologue
  29     89/<- %ebp 4/r32/esp
  30 
  31     # initialize heap
  32     # . Heap = new-segment(Heap-size)
  33     # . . push args
  34     68/push Heap/imm32
  35     ff 6/subop/push *Heap-size
  36     # . . call
  37     e8/call new-segment/disp32
  38     # . . discard args
  39     81 0/subop/add %esp 8/imm32
  40 
  41     # - if argc > 1 and argv[1] == "test", then return run_tests()
  42     # if (argc <= 1) goto run-main
  43     81 7/subop/compare *ebp 1/imm32
  44     7e/jump-if-<= $subx-calls-main:interactive/disp8
  45     # if (!kernel-string-equal?(argv[1], "test")) goto run-main
  46     # . eax = kernel-string-equal?(argv[1], "test")
  47     # . . push args
  48     68/push "test"/imm32
  49     ff 6/subop/push *(ebp+8)
  50     # . . call
  51     e8/call kernel-string-equal?/disp32
  52     # . . discard args
  53     81 0/subop/add %esp 8/imm32
  54     # . if (eax == false) goto run-main
  55     3d/compare-eax-and 0/imm32/false
  56     74/jump-if-= $subx-calls-main:interactive/disp8
  57     # run-tests()
  58     e8/call run-tests/disp32
  59     # syscall(exit, *Num-test-failures)
  60     8b/-> *Num-test-failures 3/r32/ebx
  61     eb/jump $subx-calls-main:end/disp8
  62 $subx-calls-main:interactive:
  63     # - otherwise convert stdin
  64     # subx-calls(Stdin, Stdout)
  65     # . . push args
  66     68/push Stdout/imm32
  67     68/push Stdin/imm32
  68     # . . call
  69     e8/call subx-calls/disp32
  70     # . . discard args
  71     81 0/subop/add %esp 8/imm32
  72     # syscall(exit, 0)
  73     bb/copy-to-ebx 0/imm32
  74 $subx-calls-main:end:
  75     e8/call syscall_exit/disp32
  76 
  77 subx-calls:  # in: (addr buffered-file), out: (addr buffered-file)
  78     # pseudocode:
  79     #   var line: (stream byte 512)
  80     #   var words: (stream slice 16)  # at most function name and 15 args
  81     #   while true
  82     #     clear-stream(line)
  83     #     read-line-buffered(in, line)
  84     #     if (line->write == 0) break                           # end of file
  85     #     skip-chars-matching(line, ' ')
  86     #     if line->data[line->read] != '('
  87     #       write-stream-data(out, line)
  88     #       continue
  89     #     # emit comment
  90     #     write-buffered(out, "# . ")
  91     #     write-stream-data(out, line)
  92     #     # emit code
  93     #     ++line->read to skip '('
  94     #     clear-stream(words)
  95     #     words = parse-line(line)
  96     #     emit-call(out, words)
  97     #   flush(out)
  98     #
  99     # . prologue
 100     55/push-ebp
 101     89/<- %ebp 4/r32/esp
 102     # . save registers
 103     50/push-eax
 104     51/push-ecx
 105     52/push-edx
 106     56/push-esi
 107     # var line/esi: (stream byte 512)
 108     81 5/subop/subtract %esp 0x200/imm32
 109     68/push 0x200/imm32/length
 110     68/push 0/imm32/read
 111     68/push 0/imm32/write
 112     89/<- %esi 4/r32/esp
 113     # var words/edx: (stream slice 128)  # 16 rows * 8 bytes/row
 114     81 5/subop/subtract %esp 0x80/imm32
 115     68/push 0x80/imm32/length
 116     68/push 0/imm32/read
 117     68/push 0/imm32/write
 118     89/<- %edx 4/r32/esp
 119 $subx-calls:loop:
 120     # clear-stream(line)
 121     # . . push args
 122     56/push-esi
 123     # . . call
 124     e8/call clear-stream/disp32
 125     # . . discard args
 126     81 0/subop/add %esp 4/imm32
 127     # read-line-buffered(in, line)
 128     # . . push args
 129     56/push-esi
 130     ff 6/subop/push *(ebp+8)
 131     # . . call
 132     e8/call read-line-buffered/disp32
 133     # . . discard args
 134     81 0/subop/add %esp 8/imm32
 135 $subx-calls:check0:
 136     # if (line->write == 0) break
 137     81 7/subop/compare *esi 0/imm32
 138     0f 84/jump-if-= $subx-calls:break/disp32
 139     # skip-chars-matching(line, ' ')
 140     # . . push args
 141     68/push 0x20/imm32/space
 142     56/push-esi
 143     # . . call
 144     e8/call skip-chars-matching/disp32
 145     # . . discard args
 146     81 0/subop/add %esp 8/imm32
 147     # if (line->data[line->read] == '(') goto convert-call
 148     # . ecx = line->read
 149     8b/-> *(esi+4) 1/r32/ecx
 150     # . eax = line->data[line->read]
 151     31/xor-with %eax 0/r32/eax
 152     8a/copy-byte *(esi+ecx+0xc) 0/r32/AL
 153     # . if (eax == '(') goto convert-call
 154     3d/compare-eax-and 0x28/imm32/open-paren
 155     74/jump-if-= $subx-calls:convert-call/disp8
 156 $subx-calls:pass-through:
 157     # write-stream-data(out, line)
 158     # . . push args
 159     56/push-esi
 160     ff 6/subop/push *(ebp+0xc)
 161     # . . call
 162     e8/call write-stream-data/disp32
 163     # . . discard args
 164     81 0/subop/add %esp 8/imm32
 165     # continue
 166     eb/jump $subx-calls:loop/disp8
 167 $subx-calls:convert-call:
 168     # - emit comment
 169     # write-buffered(out, "# . ")
 170     # . . push args
 171     68/push "# . "/imm32
 172     ff 6/subop/push *(ebp+0xc)
 173     # . . call
 174     e8/call write-buffered/disp32
 175     # . . discard args
 176     81 0/subop/add %esp 8/imm32
 177     # write-stream-data(out, line)
 178     # . . push args
 179     56/push-esi
 180     ff 6/subop/push *(ebp+0xc)
 181     # . . call
 182     e8/call write-stream-data/disp32
 183     # . . discard args
 184     81 0/subop/add %esp 8/imm32
 185     # - emit code
 186     # ++line->read to skip '('
 187     ff 0/subop/increment *(esi+4)
 188     # clear-stream(words)
 189     # . . push args
 190     52/push-edx
 191     # . . call
 192     e8/call clear-stream/disp32
 193     # . . discard args
 194     81 0/subop/add %esp 4/imm32
 195     # words = parse-line(line)
 196     # . . push args
 197     52/push-edx
 198     56/push-esi
 199     # . . call
 200     e8/call parse-line/disp32
 201     # . . discard args
 202     81 0/subop/add %esp 8/imm32
 203     # emit-call(out, words)
 204     # . . push args
 205     52/push-edx
 206     ff 6/subop/push *(ebp+0xc)
 207     # . . call
 208     e8/call emit-call/disp32
 209     # . . discard args
 210     81 0/subop/add %esp 8/imm32
 211     # loop
 212     e9/jump $subx-calls:loop/disp32
 213 $subx-calls:break:
 214     # flush(out)
 215     # . . push args
 216     ff 6/subop/push *(ebp+0xc)
 217     # . . call
 218     e8/call flush/disp32
 219     # . . discard args
 220     81 0/subop/add %esp 4/imm32
 221 $subx-calls:end:
 222     # . reclaim locals
 223     81 0/subop/add %esp 0x298/imm32  # 0x20c + 0x8c
 224     # . restore registers
 225     5e/pop-to-esi
 226     5a/pop-to-edx
 227     59/pop-to-ecx
 228     58/pop-to-eax
 229     # . epilogue
 230     89/<- %esp 5/r32/ebp
 231     5d/pop-to-ebp
 232     c3/return
 233 
 234 parse-line:  # line: (addr stream byte), words: (addr stream slice)
 235     # pseudocode:
 236     #   var word-slice: slice
 237     #   while true
 238     #     word-slice = next-word-string-or-expression-without-metadata(line)
 239     #     if slice-empty?(word-slice)
 240     #       break                                 # end of line
 241     #     write-int(words, word-slice->start)
 242     #     write-int(words, word-slice->end)
 243     #
 244     # . prologue
 245     55/push-ebp
 246     89/<- %ebp 4/r32/esp
 247     # . save registers
 248     51/push-ecx
 249     # var word-slice/ecx: slice
 250     68/push 0/imm32/end
 251     68/push 0/imm32/start
 252     89/<- %ecx 4/r32/esp
 253 $parse-line:loop:
 254     # word-slice = next-word-string-or-expression-without-metadata(line)
 255     # . . push args
 256     51/push-ecx
 257     ff 6/subop/push *(ebp+8)
 258     # . . call
 259     e8/call next-word-string-or-expression-without-metadata/disp32
 260     # . . discard args
 261     81 0/subop/add %esp 8/imm32
 262 $parse-line:check1:
 263     # if (slice-empty?(word-slice)) break
 264     # . eax = slice-empty?(word-slice)
 265     # . . push args
 266     51/push-ecx
 267     # . . call
 268     e8/call slice-empty?/disp32
 269     # . . discard args
 270     81 0/subop/add %esp 4/imm32
 271     # . if (eax != false) break
 272     3d/compare-eax-and 0/imm32/false
 273     0f 85/jump-if-!= $parse-line:end/disp32
 274 +-- 40 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 314 $parse-line:write-word:
 315     # write-int(words, word-slice->start)
 316     # . . push args
 317     ff 6/subop/push *ecx
 318     ff 6/subop/push *(ebp+0xc)
 319     # . . call
 320     e8/call write-int/disp32
 321     # . . discard args
 322     81 0/subop/add %esp 8/imm32
 323     # write-int(words, word-slice->end)
 324     # . . push args
 325     ff 6/subop/push *(ecx+4)
 326     ff 6/subop/push *(ebp+0xc)
 327     # . . call
 328     e8/call write-int/disp32
 329     # . . discard args
 330     81 0/subop/add %esp 8/imm32
 331     # loop
 332     e9/jump $parse-line:loop/disp32
 333 $parse-line:end:
 334     # . reclaim locals
 335     81 0/subop/add %esp 8/imm32
 336     # . restore registers
 337     59/pop-to-ecx
 338     # . epilogue
 339     89/<- %esp 5/r32/ebp
 340     5d/pop-to-ebp
 341     c3/return
 342 
 343 emit-call:  # out: (addr buffered-file), words: (addr stream slice)
 344     # pseudocode:
 345     #   if (words->write < 8) abort
 346     #   curr = &words->data[words->write-8]
 347     #   min = words->data
 348     #   # emit pushes
 349     #   while true
 350     #     if (curr <= min) break
 351     #     if slice-starts-with?(curr, "%x")    # floating-point register
 352     #       write-buffered(out, "81 5/subop/subtract %esp 4/imm32\n")
 353     #       write-buffered(out, "f3 0f 11/<- *esp ")
 354     #       write-byte-buffered(out, *(curr->start+4))
 355     #       write-buffered(out, "/x32\n")
 356     #     else if *curr->start in '%' '*'
 357     #       write-buffered(out, "ff 6/subop/push ")
 358     #       write-slice-buffered(out, curr)
 359     #       write-buffered(out, "\n")
 360     #     else
 361     #       write-buffered(out, "68/push ")
 362     #       write-slice-buffered(out, curr)
 363     #       write-buffered(out, "/imm32\n")
 364     #     curr -= 8
 365     #   # emit call
 366     #   write-buffered(out, "e8/call ")
 367     #   write-slice-buffered(out, curr)
 368     #   write-buffered(out, "/disp32\n")
 369     #   # emit pops
 370     #   write-buffered(out, "81 0/subop/add %esp ")
 371     #   write-int32-hex-buffered(out, words->write >> 1 - 4)
 372     #   write-buffered(out, "/imm32\n")
 373     #
 374     # . prologue
 375     55/push-ebp
 376     89/<- %ebp 4/r32/esp
 377     # . save registers
 378     50/push-eax
 379     51/push-ecx
 380     52/push-edx
 381     56/push-esi
 382     # esi = words
 383     8b/-> *(ebp+0xc) 6/r32/esi
 384     # if (words->write < 8) abort
 385     # . ecx = words->write - 8
 386     8b/-> *esi 1/r32/ecx
 387     81 5/subop/subtract %ecx 8/imm32
 388     0f 8c/jump-if-< $emit-call:error1/disp32
 389     # var curr/ecx: (addr slice) = &words->data[words->write-8]
 390     8d/copy-address *(esi+ecx+0xc) 1/r32/ecx
 391     # var min/edx: (addr byte) = words->data
 392     8d/copy-address *(esi+0xc) 2/r32/edx
 393     # - emit pushes
 394 $emit-call:push-loop:
 395     # if (curr <= min) break
 396     39/compare %ecx 2/r32/edx
 397     0f 8e/jump-if-<= $emit-call:call-instruction/disp32
 398     # if !slice-starts-with?(curr, "%x") goto next check
 399     # . eax = slice-starts-with?(curr, "%x")
 400     # . . push args
 401     68/push "%x"/imm32
 402     51/push-ecx
 403     # . . call
 404     e8/call slice-starts-with?/disp32
 405     # . . discard args
 406     81 0/subop/add %esp 8/imm32
 407     # . if (eax != 0) goto next check
 408     3d/compare-eax-and 0/imm32/false
 409     74/jump-if-= $emit-call:push-int/disp8
 410     # emit floating-point push
 411     # . write-buffered(out, "81 5/subop/subtract %esp 4/imm32\n")
 412     # . . push args
 413     68/push "81 5/subop/subtract %esp 4/imm32\n"/imm32
 414     ff 6/subop/push *(ebp+8)
 415     # . . call
 416     e8/call write-buffered/disp32
 417     # . . discard args
 418     81 0/subop/add %esp 8/imm32
 419     # . write-buffered(out, "f3 0f 11/<- *esp ")
 420     # . . push args
 421     68/push "f3 0f 11/<- *esp "/imm32
 422     ff 6/subop/push *(ebp+8)
 423     # . . call
 424     e8/call write-buffered/disp32
 425     # . . discard args
 426     81 0/subop/add %esp 8/imm32
 427     # . write-byte-buffered(out, *(curr->start+4))
 428     # . . eax = *(curr->start+4)
 429     8b/-> *ecx 0/r32/eax
 430     8a/copy-byte *(eax+4) 0/r32/eax
 431     81 4/subop/and %eax 0xff/imm32
 432     # . . push args
 433     50/push-eax
 434     ff 6/subop/push *(ebp+8)
 435     # . . call
 436     e8/call write-byte-buffered/disp32
 437     # . . discard args
 438     81 0/subop/add %esp 8/imm32
 439     # . write-buffered(out, "/x32\n")
 440     68/push "/x32\n"/imm32
 441     ff 6/subop/push *(ebp+8)
 442     # . . call
 443     e8/call write-buffered/disp32
 444     # . . discard args
 445     81 0/subop/add %esp 8/imm32
 446     # . continue
 447     e9/jump $emit-call:next-push/disp32
 448 $emit-call:push-int:
 449     # if (*curr->start in '%' '*') goto push-rm32
 450     # . var start/eax: (addr byte) = curr->start
 451     8b/-> *ecx 0/r32/eax
 452     # . var c/eax: byte = *eax
 453     8b/-> *eax 0/r32/eax
 454     81 4/subop/and %eax 0xff/imm32
 455     # . if (c == '%') goto push-rm32
 456     3d/compare-eax-and 0x25/imm32/percent
 457     74/jump-if-= $emit-call:push-rm32/disp8
 458     # . if (c == '*') goto push-rm32
 459     3d/compare-eax-and 0x2a/imm32/asterisk
 460     74/jump-if-= $emit-call:push-rm32/disp8
 461 $emit-call:push-imm32:
 462     # write-buffered(out, "68/push ")
 463     # . . push args
 464     68/push "68/push "/imm32
 465     ff 6/subop/push *(ebp+8)
 466     # . . call
 467     e8/call write-buffered/disp32
 468     # . . discard args
 469     81 0/subop/add %esp 8/imm32
 470     # write-slice-buffered(out, curr)
 471     # . . push args
 472     51/push-ecx
 473     ff 6/subop/push *(ebp+8)
 474     # . . call
 475     e8/call write-slice-buffered/disp32
 476     # . . discard args
 477     81 0/subop/add %esp 8/imm32
 478     # write-buffered(out, "/imm32\n")
 479     68/push "/imm32\n"/imm32
 480     ff 6/subop/push *(ebp+8)
 481     # . . call
 482     e8/call write-buffered/disp32
 483     # . . discard args
 484     81 0/subop/add %esp 8/imm32
 485     # continue
 486     eb/jump $emit-call:next-push/disp8
 487 $emit-call:push-rm32:
 488     # write-buffered(out, "ff 6/subop/push ")
 489     # . . push args
 490     68/push "ff 6/subop/push "/imm32
 491     ff 6/subop/push *(ebp+8)
 492     # . . call
 493     e8/call write-buffered/disp32
 494     # . . discard args
 495     81 0/subop/add %esp 8/imm32
 496     # write-slice-buffered(out, curr)
 497     # . . push args
 498     51/push-ecx
 499     ff 6/subop/push *(ebp+8)
 500     # . . call
 501     e8/call write-slice-buffered/disp32
 502     # . . discard args
 503     81 0/subop/add %esp 8/imm32
 504     # write-buffered(out, "\n")
 505     68/push Newline/imm32
 506     ff 6/subop/push *(ebp+8)
 507     # . . call
 508     e8/call write-buffered/disp32
 509     # . . discard args
 510     81 0/subop/add %esp 8/imm32
 511 $emit-call:next-push:
 512     # curr -= 8
 513     81 5/subop/subtract %ecx 8/imm32
 514     # loop
 515     e9/jump $emit-call:push-loop/disp32
 516 $emit-call:call-instruction:
 517     # write-buffered(out, "e8/call ")
 518     68/push "e8/call "/imm32
 519     ff 6/subop/push *(ebp+8)
 520     # . . call
 521     e8/call write-buffered/disp32
 522     # . . discard args
 523     81 0/subop/add %esp 8/imm32
 524     # write-slice-buffered(out, curr)
 525     # . . push args
 526     51/push-ecx
 527     ff 6/subop/push *(ebp+8)
 528     # . . call
 529     e8/call write-slice-buffered/disp32
 530     # . . discard args
 531     81 0/subop/add %esp 8/imm32
 532     # write-buffered(out, "/disp32\n")
 533     68/push "/disp32\n"/imm32
 534     ff 6/subop/push *(ebp+8)
 535     # . . call
 536     e8/call write-buffered/disp32
 537     # . . discard args
 538     81 0/subop/add %esp 8/imm32
 539 $emit-call:pop-instruction:
 540     # write-buffered(out, "81 0/subop/add %esp ")
 541     68/push "81 0/subop/add %esp "/imm32
 542     ff 6/subop/push *(ebp+8)
 543     # . . call
 544     e8/call write-buffered/disp32
 545     # . . discard args
 546     81 0/subop/add %esp 8/imm32
 547     # write-int32-hex-buffered(out, words->write >> 1 - 4)
 548     # . . push args
 549     8b/-> *esi 0/r32/eax
 550     c1/shift 7/subop/arith-right %eax 1/imm8
 551     2d/subtract-from-eax 4/imm32
 552     50/push-eax
 553     ff 6/subop/push *(ebp+8)
 554     # . . call
 555     e8/call write-int32-hex-buffered/disp32
 556     # . . discard args
 557     81 0/subop/add %esp 8/imm32
 558     # write-buffered(out, "/imm32\n")
 559     68/push "/imm32\n"/imm32
 560     ff 6/subop/push *(ebp+8)
 561     # . . call
 562     e8/call write-buffered/disp32
 563     # . . discard args
 564     81 0/subop/add %esp 8/imm32
 565 $emit-call:end:
 566     # . restore registers
 567     5e/pop-to-esi
 568     5a/pop-to-edx
 569     59/pop-to-ecx
 570     58/pop-to-eax
 571     # . epilogue
 572     89/<- %esp 5/r32/ebp
 573     5d/pop-to-ebp
 574     c3/return
 575 
 576 $emit-call:error1:
 577     # print(stderr, "error: calls.subx: '()' is not a valid call")
 578     # . write-buffered(Stderr, "error: calls.subx: '()' is not a valid call")
 579     # . . push args
 580     68/push "error: calls.subx: '()' is not a valid call"/imm32
 581     68/push Stderr/imm32
 582     # . . call
 583     e8/call write-buffered/disp32
 584     # . . discard args
 585     81 0/subop/add %esp 8/imm32
 586     # . flush(Stderr)
 587     # . . push args
 588     68/push Stderr/imm32
 589     # . . call
 590     e8/call flush/disp32
 591     # . . discard args
 592     81 0/subop/add %esp 4/imm32
 593     # . syscall(exit, 1)
 594     bb/copy-to-ebx 1/imm32
 595     e8/call syscall_exit/disp32
 596     # never gets here
 597 
 598 test-subx-calls-passes-most-lines-through:
 599     # . prologue
 600     55/push-ebp
 601     89/<- %ebp 4/r32/esp
 602     # setup
 603     # . clear-stream(_test-input-stream)
 604     # . . push args
 605     68/push _test-input-stream/imm32
 606     # . . call
 607     e8/call clear-stream/disp32
 608     # . . discard args
 609     81 0/subop/add %esp 4/imm32
 610     # . clear-stream($_test-input-buffered-file->buffer)
 611     # . . push args
 612     68/push  $_test-input-buffered-file->buffer/imm32
 613     # . . call
 614     e8/call clear-stream/disp32
 615     # . . discard args
 616     81 0/subop/add %esp 4/imm32
 617     # . clear-stream(_test-output-stream)
 618     # . . push args
 619     68/push _test-output-stream/imm32
 620     # . . call
 621     e8/call clear-stream/disp32
 622     # . . discard args
 623     81 0/subop/add %esp 4/imm32
 624     # . clear-stream($_test-output-buffered-file->buffer)
 625     # . . push args
 626     68/push  $_test-output-buffered-file->buffer/imm32
 627     # . . call
 628     e8/call clear-stream/disp32
 629     # . . discard args
 630     81 0/subop/add %esp 4/imm32
 631     # . write(_test-input-stream, "== abcd 0x1\n")
 632     # . . push args
 633     68/push "== abcd 0x1\n"/imm32
 634     68/push _test-input-stream/imm32
 635     # . . call
 636     e8/call write/disp32
 637     # . . discard args
 638     81 0/subop/add %esp 8/imm32
 639     # subx-calls(_test-input-buffered-file, _test-output-buffered-file)
 640     # . . push args
 641     68/push _test-output-buffered-file/imm32
 642     68/push _test-input-buffered-file/imm32
 643     # . . call
 644     e8/call subx-calls/disp32
 645     # . . discard args
 646     81 0/subop/add %esp 8/imm32
 647     # check that the line just passed through
 648     # . flush(_test-output-buffered-file)
 649     # . . push args
 650     68/push _test-output-buffered-file/imm32
 651     # . . call
 652     e8/call flush/disp32
 653     # . . discard args
 654     81 0/subop/add %esp 4/imm32
 655     # . check-stream-equal(_test-output-stream, "== abcd 0x1\n", msg)
 656     # . . push args
 657     68/push "F - test-subx-calls-passes-most-lines-through"/imm32
 658     68/push "== abcd 0x1\n"/imm32
 659     68/push _test-output-stream/imm32
 660     # . . call
 661     e8/call check-stream-equal/disp32
 662     # . . discard args
 663     81 0/subop/add %esp 0xc/imm32
 664     # . epilogue
 665     89/<- %esp 5/r32/ebp
 666     5d/pop-to-ebp
 667     c3/return
 668 
 669 test-subx-calls-processes-calls:
 670     # . prologue
 671     55/push-ebp
 672     89/<- %ebp 4/r32/esp
 673     # setup
 674     # . clear-stream(_test-input-stream)
 675     # . . push args
 676     68/push _test-input-stream/imm32
 677     # . . call
 678     e8/call clear-stream/disp32
 679     # . . discard args
 680     81 0/subop/add %esp 4/imm32
 681     # . clear-stream($_test-input-buffered-file->buffer)
 682     # . . push args
 683     68/push  $_test-input-buffered-file->buffer/imm32
 684     # . . call
 685     e8/call clear-stream/disp32
 686     # . . discard args
 687     81 0/subop/add %esp 4/imm32
 688     # . clear-stream(_test-output-stream)
 689     # . . push args
 690     68/push _test-output-stream/imm32
 691     # . . call
 692     e8/call clear-stream/disp32
 693     # . . discard args
 694     81 0/subop/add %esp 4/imm32
 695     # . clear-stream($_test-output-buffered-file->buffer)
 696     # . . push args
 697     68/push  $_test-output-buffered-file->buffer/imm32
 698     # . . call
 699     e8/call clear-stream/disp32
 700     # . . discard args
 701     81 0/subop/add %esp 4/imm32
 702     # . write(_test-input-stream, "(foo %eax)\n")
 703     # . . push args
 704     68/push "(foo %eax)\n"/imm32
 705     68/push _test-input-stream/imm32
 706     # . . call
 707     e8/call write/disp32
 708     # . . discard args
 709     81 0/subop/add %esp 8/imm32
 710     # subx-calls(_test-input-buffered-file, _test-output-buffered-file)
 711     # . . push args
 712     68/push _test-output-buffered-file/imm32
 713     68/push _test-input-buffered-file/imm32
 714     # . . call
 715     e8/call subx-calls/disp32
 716     # . . discard args
 717     81 0/subop/add %esp 8/imm32
 718     # check that the line just passed through
 719     # . flush(_test-output-buffered-file)
 720     # . . push args
 721     68/push _test-output-buffered-file/imm32
 722     # . . call
 723     e8/call flush/disp32
 724     # . . discard args
 725     81 0/subop/add %esp 4/imm32
 726 +-- 33 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------------------------------------------------------------
 759     # . check-next-stream-line-equal(_test-output-stream, "# . (foo %eax)", msg)
 760     # . . push args
 761     68/push "F - test-subx-calls-processes-calls: comment"/imm32
 762     68/push "# . (foo %eax)"/imm32
 763     68/push _test-output-stream/imm32
 764     # . . call
 765     e8/call check-next-stream-line-equal/disp32
 766     # . . discard args
 767     81 0/subop/add %esp 0xc/imm32
 768     # . check-next-stream-line-equal(_test-output-stream, "ff 6/subop/push %eax", msg)
 769     # . . push args
 770     68/push "F - test-subx-calls-processes-calls: arg 0"/imm32
 771     68/push "ff 6/subop/push %eax"/imm32
 772     68/push _test-output-stream/imm32
 773     # . . call
 774     e8/call check-next-stream-line-equal/disp32
 775     # . . discard args
 776     81 0/subop/add %esp 0xc/imm32
 777     # . check-next-stream-line-equal(_test-output-stream, "e8/call foo/disp32", msg)
 778     # . . push args
 779     68/push "F - test-subx-calls-processes-calls: call"/imm32
 780     68/push "e8/call foo/disp32"/imm32
 781     68/push _test-output-stream/imm32
 782     # . . call
 783     e8/call check-next-stream-line-equal/disp32
 784     # . . discard args
 785     81 0/subop/add %esp 0xc/imm32
 786     # . check-next-stream-line-equal(_test-output-stream, "81 0/subop/add %esp 4/imm32", msg)
 787     # . . push args
 788     68/push "F - test-subx-calls-processes-calls: pops"/imm32
 789     68/push "81 0/subop/add %esp 0x00000004/imm32"/imm32
 790     68/push _test-output-stream/imm32
 791     # . . call
 792     e8/call check-next-stream-line-equal/disp32
 793     # . . discard args
 794     81 0/subop/add %esp 0xc/imm32
 795     # . epilogue
 796     89/<- %esp 5/r32/ebp
 797     5d/pop-to-ebp
 798     c3/return
 799 
 800 next-word-string-or-expression-without-metadata:  # line: (addr stream byte), out: (addr slice)
 801     # pseudocode:
 802     #   skip-chars-matching(line, ' ')
 803     #   if line->read >= line->write              # end of line
 804     #     out = {0, 0}
 805     #     return
 806     #   out->start = &line->data[line->read]
 807     #   if line->data[line->read] == '#'          # comment
 808     #     out->end = &line->data[line->write]     # skip to end of line
 809     #     return
 810     #   if line->data[line->read] == '"'          # string literal
 811     #     skip-string(line)
 812     #     out->end = &line->data[line->read]      # no metadata
 813     #     return
 814     #   if line->data[line->read] == '*'          # expression
 815     #     if line->data[line->read + 1] == ' '
 816     #       abort
 817     #     if line->data[line->read + 1] == '('
 818     #       skip-until-close-paren(line)
 819     #       if (line->data[line->read] != ')'
 820     #         abort
 821     #       ++line->read to skip ')'
 822     #     out->end = &line->data[line->read]
 823     #     return
 824     #   if line->data[line->read] == ')'
 825     #     ++line->read to skip ')'
 826     #     # make sure there's nothing else of importance
 827     #     if line->read >= line->write
 828     #       out = {0, 0}
 829     #       return
 830     #     if line->data[line->read] != ' '
 831     #       abort
 832     #     skip-chars-matching-whitespace(line)
 833     #     if line->read >= line->write
 834     #       out = {0, 0}
 835     #       return
 836     #     if line->data[line->read] == '#'        # only thing permitted after ')' is a comment
 837     #       out = {0, 0}
 838     #       return
 839     #     abort
 840     #   # default case: read a word -- but no metadata
 841     #   while true
 842     #     if line->read >= line->write
 843     #       break
 844     #     if line->data[line->read] == ' '
 845     #       break
 846     #     if line->data[line->read] == ')'
 847     #       break
 848     #     if line->data[line->read] == '/'
 849     #       abort
 850     #     ++line->read
 851     #   out->end = &line->data[line->read]
 852     #
 853     # registers:
 854     #   ecx: often line->read
 855     #   eax: often line->data[line->read]
 856     #
 857     # . prologue
 858     55/push-ebp
 859     89/<- %ebp 4/r32/esp
 860     # . save registers
 861     50/push-eax
 862     51/push-ecx
 863     56/push-esi
 864     57/push-edi
 865     # esi = line
 866     8b/-> *(ebp+8) 6/r32/esi
 867     # edi = out
 868     8b/-> *(ebp+0xc) 7/r32/edi
 869     # skip-chars-matching(line, ' ')
 870     # . . push args
 871     68/push 0x20/imm32/space
 872     ff 6/subop/push *(ebp+8)
 873     # . . call
 874     e8/call skip-chars-matching/disp32
 875     # . . discard args
 876     81 0/subop/add %esp 8/imm32
 877 $next-word-string-or-expression-without-metadata:check0:
 878     # if (line->read >= line->write) abort because we didn't encounter a final ')'
 879     # . ecx = line->read
 880     8b/-> *(esi+4) 1/r32/ecx
 881     # . if (ecx >= line->write) abort
 882     3b/compare<- *esi 1/r32/ecx
 883     0f 8d/jump-if->= $next-word-string-or-expression-without-metadata:error0/disp32
 884 $next-word-string-or-expression-without-metadata:check-for-comment:
 885     # out->start = &line->data[line->read]
 886     8d/copy-address *(esi+ecx+0xc) 0/r32/eax
 887     89/<- *edi 0/r32/eax
 888     # if (line->data[line->read] != '#') goto next check
 889     # . var eax: byte = line->data[line->read]
 890     31/xor-with %eax 0/r32/eax
 891     8a/copy-byte *(esi+ecx+0xc) 0/r32/AL
 892     # . if (eax != '#') goto next check
 893     3d/compare-eax-and 0x23/imm32/pound
 894     75/jump-if-!= $next-word-string-or-expression-without-metadata:check-for-string-literal/disp8
 895 $next-word-string-or-expression-without-metadata:comment:
 896     # out->end = &line->data[line->write]
 897     8b/-> *esi 0/r32/eax
 898     8d/copy-address *(esi+eax+0xc) 0/r32/eax
 899     89/<- *(edi+4) 0/r32/eax
 900     # line->read = line->write  # skip rest of line
 901     8b/-> *esi 0/r32/eax
 902     89/<- *(esi+4) 0/r32/eax
 903     # return
 904     e9/jump $next-word-string-or-expression-without-metadata:end/disp32
 905 $next-word-string-or-expression-without-metadata:check-for-string-literal:
 906     # if (line->data[line->read] != '"') goto next check
 907     3d/compare-eax-and 0x22/imm32/dquote
 908     75/jump-if-!= $next-word-string-or-expression-without-metadata:check-for-expression/disp8
 909 $next-word-string-or-expression-without-metadata:string-literal:
 910     # skip-string(line)
 911     # . . push args
 912     56/push-esi
 913     # . . call
 914     e8/call skip-string/disp32
 915     # . . discard args
 916     81 0/subop/add %esp 4/imm32
 917     # out->end = &line->data[line->read]
 918     8b/-> *(esi+4) 1/r32/ecx
 919     8d/copy-address *(esi+ecx+0xc) 0/r32/eax
 920     89/<- *(edi+4) 0/r32/eax
 921     # return
 922     e9/jump $next-word-string-or-expression-without-metadata:end/disp32
 923 $next-word-string-or-expression-without-metadata:check-for-expression:
 924     # if (line->data[line->read] != '*') goto next check
 925     3d/compare-eax-and 0x2a/imm32/asterisk
 926     75/jump-if-!= $next-word-string-or-expression-without-metadata:check-for-end-of-call/disp8
 927     # if (line->data[line->read + 1] == ' ') goto error1
 928     8a/copy-byte *(esi+ecx+0xd) 0/r32/AL
 929     3d/compare-eax-and 0x20/imm32/space
 930     0f 84/jump-if-= $next-word-string-or-expression-without-metadata:error1/disp32
 931     # if (line->data[line->read + 1] != '(') goto regular-word
 932     3d/compare-eax-and 0x28/imm32/open-paren
 933     0f 85/jump-if-!= $next-word-string-or-expression-without-metadata:regular-word-without-metadata/disp32
 934 $next-word-string-or-expression-without-metadata:paren:
 935     # skip-until-close-paren(line)
 936     # . . push args
 937     56/push-esi
 938     # . . call
 939     e8/call skip-until-close-paren/disp32
 940     # . . discard args
 941     81 0/subop/add %esp 4/imm32
 942     # if (line->data[line->read] != ')') goto error2
 943     # . eax = line->data[line->read]
 944     8b/-> *(esi+4) 1/r32/ecx
 945     8a/copy-byte *(esi+ecx+0xc) 0/r32/AL
 946     # . if (eax != ')') goto error2
 947     3d/compare-eax-and 0x29/imm32/close-paren
 948     0f 85/jump-if-!= $next-word-string-or-expression-without-metadata:error2/disp32
 949     # ++line->read to skip ')'
 950     ff 0/subop/increment *(esi+4)
 951     # out->end = &line->data[line->read]
 952     8b/-> *(esi+4) 1/r32/ecx
 953     8d/copy-address *(esi+ecx+0xc) 0/r32/eax
 954     89/<- *(edi+4) 0/r32/eax
 955     # return
 956     e9/jump $next-word-string-or-expression-without-metadata:end/disp32
 957 $next-word-string-or-expression-without-metadata:check-for-end-of-call:
 958     # if (line->data[line->read] != ')') goto next check
 959     3d/compare-eax-and 0x29/imm32/close-paren
 960     75/jump-if-!= $next-word-string-or-expression-without-metadata:regular-word-without-metadata/disp8
 961     # ++line->read to skip ')'
 962     ff 0/subop/increment *(esi+4)
 963     # - error checking: make sure there's nothing else of importance on the line
 964     # if (line->read >= line->write) return out = {0, 0}
 965     # . ecx = line->read
 966     8b/-> *(esi+4) 1/r32/ecx
 967     # . if (ecx >= line->write) return {0, 0}
 968     3b/compare<- *esi 1/r32/ecx
 969     0f 8d/jump-if->= $next-word-string-or-expression-without-metadata:return-eol/disp32
 970     # if (line->data[line->read] == '/') goto error3
 971     # . eax = line->data[line->read]
 972     8a/copy-byte *(esi+ecx+0xc) 0/r32/AL
 973     # . if (eax == '/') goto error3
 974     3d/compare-eax-and 0x2f/imm32/slash
 975     0f 84/jump-if-= $next-word-string-or-expression-without-metadata:error3/disp32
 976     # skip-chars-matching-whitespace(line)
 977     # . . push args
 978     56/push-esi
 979     # . . call
 980     e8/call skip-chars-matching-whitespace/disp32
 981     # . . discard args
 982     81 0/subop/add %esp 4/imm32
 983     # if (line->read >= line->write) return out = {0, 0}
 984     # . ecx = line->read
 985     8b/-> *(esi+4) 1/r32/ecx
 986     # . if (ecx >= line->write) return {0, 0}
 987     3b/compare<- *esi 1/r32/ecx
 988     0f 8d/jump-if->= $next-word-string-or-expression-without-metadata:return-eol/disp32
 989     # if (line->data[line->read] == '#') return out = {0, 0}
 990     # . eax = line->data[line->read]
 991     8b/-> *(esi+4) 1/r32/ecx
 992     8a/copy-byte *(esi+ecx+0xc) 0/r32/AL
 993     # . if (eax == '#') return out = {0, 0}
 994     3d/compare-eax-and 0x23/imm32/pound
 995     74/jump-if-= $next-word-string-or-expression-without-metadata:return-eol/disp8
 996     # otherwise goto error4
 997     e9/jump $next-word-string-or-expression-without-metadata:error4/disp32
 998 $next-word-string-or-expression-without-metadata:regular-word-without-metadata:
 999     # if (line->read >= line->write) break
1000     # . ecx = line->read
1001     8b/-> *(esi+4) 1/r32/ecx
1002     # . if (ecx >= line->write) break
1003     3b/compare<- *esi 1/r32/ecx
1004     7d/jump-if->= $next-word-string-or-expression-without-metadata:regular-word-break/disp8
1005     # if (line->data[line->read] == ' ') break
1006     # . eax = line->data[line->read]
1007     8b/-> *(esi+4) 1/r32/ecx
1008     8a/copy-byte *(esi+ecx+0xc) 0/r32/AL
1009     # . if (eax == ' ') break
1010     3d/compare-eax-and 0x20/imm32/space
1011     74/jump-if-= $next-word-string-or-expression-without-metadata:regular-word-break/disp8
1012     # if (line->data[line->read] == ')') break
1013     3d/compare-eax-and 0x29/imm32/close-paren
1014     0f 84/jump-if-= $next-word-string-or-expression-without-metadata:regular-word-break/disp32
1015     # if (line->data[line->read] == '/') goto error5
1016     3d/compare-eax-and 0x2f/imm32/slash
1017     0f 84/jump-if-= $next-word-string-or-expression-without-metadata:error5/disp32
1018     # ++line->read
1019     ff 0/subop/increment *(esi+4)
1020     # loop
1021     e9/jump $next-word-string-or-expression-without-metadata:regular-word-without-metadata/disp32
1022 $next-word-string-or-expression-without-metadata:regular-word-break:
1023     # out->end = &line->data[line->read]
1024     8b/-> *(esi+4) 1/r32/ecx
1025     8d/copy-address *(esi+ecx+0xc) 0/r32/eax
1026     89/<- *(edi+4) 0/r32/eax
1027     eb/jump $next-word-string-or-expression-without-metadata:end/disp8
1028 $next-word-string-or-expression-without-metadata:return-eol:
1029     # return out = {0, 0}
1030     c7 0/subop/copy *edi 0/imm32
1031     c7 0/subop/copy *(edi+4) 0/imm32
1032 $next-word-string-or-expression-without-metadata:end:
1033     # . restore registers
1034     5f/pop-to-edi
1035     5e/pop-to-esi
1036     59/pop-to-ecx
1037     58/pop-to-eax
1038     # . epilogue
1039     89/<- %esp 5/r32/ebp
1040     5d/pop-to-ebp
1041     c3/return
1042 
1043 $next-word-string-or-expression-without-metadata:error0:
1044     # print(stderr, "error: missing final ')' in '" line "'")
1045     # . write-buffered(Stderr, "error: missing final ')' in '")
1046     # . . push args
1047     68/push "error: missing final ')' in '"/imm32
1048     68/push Stderr/imm32
1049     # . . call
1050     e8/call write-buffered/disp32
1051     # . . discard args
1052     81 0/subop/add %esp 8/imm32
1053     # . write-stream-data(Stderr, line)
1054     # . . push args
1055     56/push-esi
1056     68/push Stderr/imm32
1057     # . . call
1058     e8/call write-stream-data/disp32
1059     # . . discard args
1060     81 0/subop/add %esp 8/imm32
1061     # . write-buffered(Stderr, "'")
1062     # . . push args
1063     68/push "'"/imm32
1064     68/push Stderr/imm32
1065     # . . call
1066     e8/call write-buffered/disp32
1067     # . . discard args
1068     81 0/subop/add %esp 8/imm32
1069     # . flush(Stderr)
1070     # . . push args
1071     68/push Stderr/imm32
1072     # . . call
1073     e8/call flush/disp32
1074     # . . discard args
1075     81 0/subop/add %esp 4/imm32
1076     # . syscall(exit, 1)
1077     bb/copy-to-ebx 1/imm32
1078     e8/call  syscall_exit/disp32
1079     # never gets here
1080 
1081 $next-word-string-or-expression-without-metadata:error1:
1082     # print(stderr, "error: no space allowed after '*' in '" line "'")
1083     # . write-buffered(Stderr, "error: no space allowed after '*' in '")
1084     # . . push args
1085     68/push "error: no space allowed after '*' in '"/imm32
1086     68/push Stderr/imm32
1087     # . . call
1088     e8/call write-buffered/disp32
1089     # . . discard args
1090     81 0/subop/add %esp 8/imm32
1091     # . write-stream-data(Stderr, line)
1092     # . . push args
1093     56/push-esi
1094     68/push Stderr/imm32
1095     # . . call
1096     e8/call write-stream-data/disp32
1097     # . . discard args
1098     81 0/subop/add %esp 8/imm32
1099     # . write-buffered(Stderr, "'")
1100     # . . push args
1101     68/push "'"/imm32
1102     68/push Stderr/imm32
1103     # . . call
1104     e8/call write-buffered/disp32
1105     # . . discard args
1106     81 0/subop/add %esp 8/imm32
1107     # . flush(Stderr)
1108     # . . push args
1109     68/push Stderr/imm32
1110     # . . call
1111     e8/call flush/disp32
1112     # . . discard args
1113     81 0/subop/add %esp 4/imm32
1114     # . syscall(exit, 1)
1115     bb/copy-to-ebx 1/imm32
1116     e8/call syscall_exit/disp32
1117     # never gets here
1118 
1119 $next-word-string-or-expression-without-metadata:error2:
1120     # print(stderr, "error: *(...) expression must be all on a single line in '" line "'")
1121     # . write-buffered(Stderr, "error: *(...) expression must be all on a single line in '")
1122     # . . push args
1123     68/push "error: *(...) expression must be all on a single line in '"/imm32
1124     68/push Stderr/imm32
1125     # . . call
1126     e8/call write-buffered/disp32
1127     # . . discard args
1128     81 0/subop/add %esp 8/imm32
1129     # . write-stream-data(Stderr, line)
1130     # . . push args
1131     56/push-esi
1132     68/push Stderr/imm32
1133     # . . call
1134     e8/call write-stream-data/disp32
1135     # . . discard args
1136     81 0/subop/add %esp 8/imm32
1137     # . write-buffered(Stderr, "'")
1138     # . . push args
1139     68/push "'"/imm32
1140     68/push Stderr/imm32
1141     # . . call
1142     e8/call write-buffered/disp32
1143     # . . discard args
1144     81 0/subop/add %esp 8/imm32
1145     # . flush(Stderr)
1146     # . . push args
1147     68/push Stderr/imm32
1148     # . . call
1149     e8/call flush/disp32
1150     # . . discard args
1151     81 0/subop/add %esp 4/imm32
1152     # . syscall(exit, 1)
1153     bb/copy-to-ebx 1/imm32
1154     e8/call syscall_exit/disp32
1155     # never gets here
1156 
1157 $next-word-string-or-expression-without-metadata:error3:
1158     # print(stderr, "error: no metadata after calls; just use a comment (in '" line "')")
1159     # . write-buffered(Stderr, "error: no metadata after calls; just use a comment (in '")
1160     # . . push args
1161     68/push "error: no metadata after calls; just use a comment (in '"/imm32
1162     68/push Stderr/imm32
1163     # . . call
1164     e8/call write-buffered/disp32
1165     # . . discard args
1166     81 0/subop/add %esp 8/imm32
1167     # . write-stream-data(Stderr, line)
1168     # . . push args
1169     56/push-esi
1170     68/push Stderr/imm32
1171     # . . call
1172     e8/call write-stream-data/disp32
1173     # . . discard args
1174     81 0/subop/add %esp 8/imm32
1175     # . write-buffered(Stderr, "')")
1176     # . . push args
1177     68/push "')"/imm32
1178     68/push Stderr/imm32
1179     # . . call
1180     e8/call write-buffered/disp32
1181     # . . discard args
1182     81 0/subop/add %esp 8/imm32
1183     # . flush(Stderr)
1184     # . . push args
1185     68/push Stderr/imm32
1186     # . . call
1187     e8/call flush/disp32
1188     # . . discard args
1189     81 0/subop/add %esp 4/imm32
1190     # . syscall(exit, 1)
1191     bb/copy-to-ebx 1/imm32
1192     e8/call syscall_exit/disp32
1193     # never gets here
1194 
1195 $next-word-string-or-expression-without-metadata:error4:
1196     # print(stderr, "error: unexpected text after end of call in '" line "'")
1197     # . write-buffered(Stderr, "error: unexpected text after end of call in '")
1198     # . . push args
1199     68/push "error: unexpected text after end of call in '"/imm32
1200     68/push Stderr/imm32
1201     # . . call
1202     e8/call write-buffered/disp32
1203     # . . discard args
1204     81 0/subop/add %esp 8/imm32
1205     # . write-stream-data(Stderr, line)
1206     # . . push args
1207     56/push-esi
1208     68/push Stderr/imm32
1209     # . . call
1210     e8/call write-stream-data/disp32
1211     # . . discard args
1212     81 0/subop/add %esp 8/imm32
1213     # . write-buffered(Stderr, "'")
1214     # . . push args
1215     68/push "'"/imm32
1216     68/push Stderr/imm32
1217     # . . call
1218     e8/call write-buffered/disp32
1219     # . . discard args
1220     81 0/subop/add %esp 8/imm32
1221     # . flush(Stderr)
1222     # . . push args
1223     68/push Stderr/imm32
1224     # . . call
1225     e8/call flush/disp32
1226     # . . discard args
1227     81 0/subop/add %esp 4/imm32
1228     # . syscall(exit, 1)
1229     bb/copy-to-ebx 1/imm32
1230     e8/call syscall_exit/disp32
1231     # never gets here
1232 
1233 $next-word-string-or-expression-without-metadata:error5:
1234     # print(stderr, "error: no metadata anywhere in calls (in '" line "')")
1235     # . write-buffered(Stderr, "error: no metadata anywhere in calls (in '")
1236     # . . push args
1237     68/push "error: no metadata anywhere in calls (in '"/imm32
1238     68/push Stderr/imm32
1239     # . . call
1240     e8/call write-buffered/disp32
1241     # . . discard args
1242     81 0/subop/add %esp 8/imm32
1243     # . write-stream-data(Stderr, line)
1244     # . . push args
1245     56/push-esi
1246     68/push Stderr/imm32
1247     # . . call
1248     e8/call write-stream-data/disp32
1249     # . . discard args
1250     81 0/subop/add %esp 8/imm32
1251     # . write-buffered(Stderr, "')")
1252     # . . push args
1253     68/push "')"/imm32
1254     68/push Stderr/imm32
1255     # . . call
1256     e8/call write-buffered/disp32
1257     # . . discard args
1258     81 0/subop/add %esp 8/imm32
1259     # . flush(Stderr)
1260     # . . push args
1261     68/push Stderr/imm32
1262     # . . call
1263     e8/call flush/disp32
1264     # . . discard args
1265     81 0/subop/add %esp 4/imm32
1266     # . syscall(exit, 1)
1267     bb/copy-to-ebx 1/imm32
1268     e8/call syscall_exit/disp32
1269     # never gets here
1270 
1271 test-next-word-string-or-expression-without-metadata:
1272     # . prologue
1273     55/push-ebp
1274     89/<- %ebp 4/r32/esp
1275     # setup
1276     # . clear-stream(_test-input-stream)
1277     # . . push args
1278     68/push _test-input-stream/imm32
1279     # . . call
1280     e8/call clear-stream/disp32
1281     # . . discard args
1282     81 0/subop/add %esp 4/imm32
1283     # var slice/ecx: slice
1284     68/push 0/imm32/end
1285     68/push 0/imm32/start
1286     89/<- %ecx 4/r32/esp
1287     # write(_test-input-stream, "  ab")
1288     # . . push args
1289     68/push "  ab"/imm32
1290     68/push _test-input-stream/imm32
1291     # . . call
1292     e8/call write/disp32
1293     # . . discard args
1294     81 0/subop/add %esp 8/imm32
1295     # next-word-string-or-expression-without-metadata(_test-input-stream, slice)
1296     # . . push args
1297     51/push-ecx
1298     68/push _test-input-stream/imm32
1299     # . . call
1300     e8/call next-word-string-or-expression-without-metadata/disp32
1301     # . . discard args
1302     81 0/subop/add %esp 8/imm32
1303     # check-ints-equal(_test-input-stream->read, 4, msg)
1304     # . . push args
1305     68/push "F - test-next-word-string-or-expression-without-metadata/updates-stream-read-correctly"/imm32
1306     68/push 4/imm32
1307     b8/copy-to-eax _test-input-stream/imm32
1308     ff 6/subop/push *(eax+4)
1309     # . . call
1310     e8/call check-ints-equal/disp32
1311     # . . discard args
1312     81 0/subop/add %esp 0xc/imm32
1313     # check-ints-equal(slice->start - _test-input-stream->data, 2, msg)
1314     # . check-ints-equal(slice->start - _test-input-stream, 14, msg)
1315     # . . push args
1316     68/push "F - test-next-word-string-or-expression-without-metadata: start"/imm32
1317     68/push 0xe/imm32
1318     # . . push slice->start - _test-input-stream
1319     8b/-> *ecx 0/r32/eax
1320     81 5/subop/subtract %eax _test-input-stream/imm32
1321     50/push-eax
1322     # . . call
1323     e8/call check-ints-equal/disp32
1324     # . . discard args
1325     81 0/subop/add %esp 0xc/imm32
1326     # check-ints-equal(slice->end - _test-input-stream->data, 4, msg)
1327     # . check-ints-equal(slice->end - _test-input-stream, 16, msg)
1328     # . . push args
1329     68/push "F - test-next-word-string-or-expression-without-metadata: end"/imm32
1330     68/push 0x10/imm32
1331     # . . push slice->end - _test-input-stream
1332     8b/-> *(ecx+4) 0/r32/eax
1333     81 5/subop/subtract %eax _test-input-stream/imm32
1334     50/push-eax
1335     # . . call
1336     e8/call check-ints-equal/disp32
1337     # . . discard args
1338     81 0/subop/add %esp 0xc/imm32
1339     # . epilogue
1340     89/<- %esp 5/r32/ebp
1341     5d/pop-to-ebp
1342     c3/return
1343 
1344 test-next-word-string-or-expression-without-metadata-returns-whole-comment:
1345     # . prologue
1346     55/push-ebp
1347     89/<- %ebp 4/r32/esp
1348     # setup
1349     # . clear-stream(_test-input-stream)
1350     # . . push args
1351     68/push _test-input-stream/imm32
1352     # . . call
1353     e8/call clear-stream/disp32
1354     # . . discard args
1355     81 0/subop/add %esp 4/imm32
1356     # var slice/ecx: slice
1357     68/push 0/imm32/end
1358     68/push 0/imm32/start
1359     89/<- %ecx 4/r32/esp
1360     # write(_test-input-stream, "  # a")
1361     # . . push args
1362     68/push "  # a"/imm32
1363     68/push _test-input-stream/imm32
1364     # . . call
1365     e8/call write/disp32
1366     # . . discard args
1367     81 0/subop/add %esp 8/imm32
1368     # next-word-string-or-expression-without-metadata(_test-input-stream, slice)
1369     # . . push args
1370     51/push-ecx
1371     68/push _test-input-stream/imm32
1372     # . . call
1373     e8/call next-word-string-or-expression-without-metadata/disp32
1374     # . . discard args
1375     81 0/subop/add %esp 8/imm32
1376     # check-ints-equal(_test-input-stream->read, 5, msg)
1377     # . . push args
1378     68/push "F - test-next-word-string-or-expression-without-metadata-returns-whole-comment/updates-stream-read-correctly"/imm32
1379     68/push 5/imm32
1380     b8/copy-to-eax _test-input-stream/imm32
1381     ff 6/subop/push *(eax+4)
1382     # . . call
1383     e8/call check-ints-equal/disp32
1384     # . . discard args
1385     81 0/subop/add %esp 0xc/imm32
1386     # check-ints-equal(slice->start - _test-input-stream->data, 2, msg)
1387     # . check-ints-equal(slice->start - _test-input-stream, 14, msg)
1388     # . . push args
1389     68/push "F - test-next-word-string-or-expression-without-metadata-returns-whole-comment: start"/imm32
1390     68/push 0xe/imm32
1391     # . . push slice->start - _test-input-stream
1392     8b/-> *ecx 0/r32/eax
1393     81 5/subop/subtract %eax _test-input-stream/imm32
1394     50/push-eax
1395     # . . call
1396     e8/call check-ints-equal/disp32
1397     # . . discard args
1398     81 0/subop/add %esp 0xc/imm32
1399     # check-ints-equal(slice->end - _test-input-stream->data, 5, msg)
1400     # . check-ints-equal(slice->end - _test-input-stream, 17, msg)
1401     # . . push args
1402     68/push "F - test-next-word-string-or-expression-without-metadata-returns-whole-comment: end"/imm32
1403     68/push 0x11/imm32
1404     # . . push slice->end - _test-input-stream
1405     8b/-> *(ecx+4) 0/r32/eax
1406     81 5/subop/subtract %eax _test-input-stream/imm32
1407     50/push-eax
1408     # . . call
1409     e8/call check-ints-equal/disp32
1410     # . . discard args
1411     81 0/subop/add %esp 0xc/imm32
1412     # . epilogue
1413     89/<- %esp 5/r32/ebp
1414     5d/pop-to-ebp
1415     c3/return
1416 
1417 test-next-word-string-or-expression-without-metadata-returns-string-literal:
1418     # . prologue
1419     55/push-ebp
1420     89/<- %ebp 4/r32/esp
1421     # setup
1422     # . clear-stream(_test-input-stream)
1423     # . . push args
1424     68/push _test-input-stream/imm32
1425     # . . call
1426     e8/call clear-stream/disp32
1427     # . . discard args
1428     81 0/subop/add %esp 4/imm32
1429     # var slice/ecx: slice
1430     68/push 0/imm32/end
1431     68/push 0/imm32/start
1432     89/<- %ecx 4/r32/esp
1433     # write(_test-input-stream, " \"a b\" ")
1434     # . . push args
1435     68/push " \"a b\" "/imm32
1436     68/push _test-input-stream/imm32
1437     # . . call
1438     e8/call write/disp32
1439     # . . discard args
1440     81 0/subop/add %esp 8/imm32
1441     # next-word-string-or-expression-without-metadata(_test-input-stream, slice)
1442     # . . push args
1443     51/push-ecx
1444     68/push _test-input-stream/imm32
1445     # . . call
1446     e8/call next-word-string-or-expression-without-metadata/disp32
1447     # . . discard args
1448     81 0/subop/add %esp 8/imm32
1449     # check-ints-equal(slice->start - _test-input-stream->data, 1, msg)
1450     # . check-ints-equal(slice->start - _test-input-stream, 13, msg)
1451     # . . push args
1452     68/push "F - test-next-word-string-or-expression-without-metadata-returns-string-literal: start"/imm32
1453     68/push 0xd/imm32
1454     # . . push slice->start - _test-input-stream
1455     8b/-> *ecx 0/r32/eax
1456     81 5/subop/subtract %eax _test-input-stream/imm32
1457     50/push-eax
1458     # . . call
1459     e8/call check-ints-equal/disp32
1460     # . . discard args
1461     81 0/subop/add %esp 0xc/imm32
1462     # check-ints-equal(slice->end - _test-input-stream->data, 6, msg)
1463     # . check-ints-equal(slice->end - _test-input-stream, 18, msg)
1464     # . . push args
1465     68/push "F - test-next-word-string-or-expression-without-metadata-returns-string-literal: end"/imm32
1466     68/push 0x12/imm32
1467     # . . push slice->end - _test-input-stream
1468     8b/-> *(ecx+4) 0/r32/eax
1469     81 5/subop/subtract %eax _test-input-stream/imm32
1470     50/push-eax
1471     # . . call
1472     e8/call check-ints-equal/disp32
1473     # . . discard args
1474     81 0/subop/add %esp 0xc/imm32
1475     # . epilogue
1476     89/<- %esp 5/r32/ebp
1477     5d/pop-to-ebp
1478     c3/return
1479 
1480 test-next-word-string-or-expression-without-metadata-returns-string-with-escapes:
1481     # . prologue
1482     55/push-ebp
1483     89/<- %ebp 4/r32/esp
1484     # setup
1485     # . clear-stream(_test-input-stream)
1486     # . . push args
1487     68/push _test-input-stream/imm32
1488     # . . call
1489     e8/call clear-stream/disp32
1490     # . . discard args
1491     81 0/subop/add %esp 4/imm32
1492     # var slice/ecx: slice
1493     68/push 0/imm32/end
1494     68/push 0/imm32/start
1495     89/<- %ecx 4/r32/esp
1496     # write(_test-input-stream, " \"a\\\"b\"")
1497     # . . push args
1498     68/push " \"a\\\"b\""/imm32
1499     68/push _test-input-stream/imm32
1500     # . . call
1501     e8/call write/disp32
1502     # . . discard args
1503     81 0/subop/add %esp 8/imm32
1504     # next-word-string-or-expression-without-metadata(_test-input-stream, slice)
1505     # . . push args
1506     51/push-ecx
1507     68/push _test-input-stream/imm32
1508     # . . call
1509     e8/call next-word-string-or-expression-without-metadata/disp32
1510     # . . discard args
1511     81 0/subop/add %esp 8/imm32
1512     # check-ints-equal(slice->start - _test-input-stream->data, 1, msg)
1513     # . check-ints-equal(slice->start - _test-input-stream, 13, msg)
1514     # . . push args
1515     68/push "F - test-next-word-string-or-expression-without-metadata-returns-string-with-escapes: start"/imm32
1516     68/push 0xd/imm32
1517     # . . push slice->start - _test-input-stream
1518     8b/-> *ecx 0/r32/eax
1519     81 5/subop/subtract %eax _test-input-stream/imm32
1520     50/push-eax
1521     # . . call
1522     e8/call check-ints-equal/disp32
1523     # . . discard args
1524     81 0/subop/add %esp 0xc/imm32
1525     # check-ints-equal(slice->end - _test-input-stream->data, 7, msg)
1526     # . check-ints-equal(slice->end - _test-input-stream, 19, msg)
1527     # . . push args
1528     68/push "F - test-next-word-string-or-expression-without-metadata-returns-string-with-escapes: end"/imm32
1529     68/push 0x13/imm32
1530     # . . push slice->end - _test-input-stream
1531     8b/-> *(ecx+4) 0/r32/eax
1532     81 5/subop/subtract %eax _test-input-stream/imm32
1533     50/push-eax
1534     # . . call
1535     e8/call check-ints-equal/disp32
1536     # . . discard args
1537     81 0/subop/add %esp 0xc/imm32
1538     # . epilogue
1539     89/<- %esp 5/r32/ebp
1540     5d/pop-to-ebp
1541     c3/return
1542 
1543 test-next-word-string-or-expression-without-metadata-returns-whole-expression:
1544     # . prologue
1545     55/push-ebp
1546     89/<- %ebp 4/r32/esp
1547     # setup
1548     # . clear-stream(_test-input-stream)
1549     # . . push args
1550     68/push _test-input-stream/imm32
1551     # . . call
1552     e8/call clear-stream/disp32
1553     # . . discard args
1554     81 0/subop/add %esp 4/imm32
1555     # var slice/ecx: slice
1556     68/push 0/imm32/end
1557     68/push 0/imm32/start
1558     89/<- %ecx 4/r32/esp
1559     # write(_test-input-stream, " *(a b) ")
1560     # . . push args
1561     68/push " *(a b) "/imm32
1562     68/push _test-input-stream/imm32
1563     # . . call
1564     e8/call write/disp32
1565     # . . discard args
1566     81 0/subop/add %esp 8/imm32
1567     # next-word-string-or-expression-without-metadata(_test-input-stream, slice)
1568     # . . push args
1569     51/push-ecx
1570     68/push _test-input-stream/imm32
1571     # . . call
1572     e8/call next-word-string-or-expression-without-metadata/disp32
1573     # . . discard args
1574     81 0/subop/add %esp 8/imm32
1575     # check-ints-equal(slice->start - _test-input-stream->data, 1, msg)
1576     # . check-ints-equal(slice->start - _test-input-stream, 13, msg)
1577     # . . push args
1578     68/push "F - test-next-word-string-or-expression-without-metadata-returns-whole-expression: start"/imm32
1579     68/push 0xd/imm32
1580     # . . push slice->start - _test-input-stream
1581     8b/-> *ecx 0/r32/eax
1582     81 5/subop/subtract %eax _test-input-stream/imm32
1583     50/push-eax
1584     # . . call
1585     e8/call check-ints-equal/disp32
1586     # . . discard args
1587     81 0/subop/add %esp 0xc/imm32
1588     # check-ints-equal(slice->end - _test-input-stream->data, 7, msg)
1589     # . check-ints-equal(slice->end - _test-input-stream, 19, msg)
1590     # . . push args
1591     68/push "F - test-next-word-string-or-expression-without-metadata-returns-whole-expression: end"/imm32
1592     68/push 0x13/imm32
1593     # . . push slice->end - _test-input-stream
1594     8b/-> *(ecx+4) 0/r32/eax
1595     81 5/subop/subtract %eax _test-input-stream/imm32
1596     50/push-eax
1597     # . . call
1598     e8/call check-ints-equal/disp32
1599     # . . discard args
1600     81 0/subop/add %esp 0xc/imm32
1601     # . epilogue
1602     89/<- %esp 5/r32/ebp
1603     5d/pop-to-ebp
1604     c3/return
1605 
1606 test-next-word-string-or-expression-without-metadata-returns-eol-on-trailing-close-paren:
1607     # . prologue
1608     55/push-ebp
1609     89/<- %ebp 4/r32/esp
1610     # setup
1611     # . clear-stream(_test-input-stream)
1612     # . . push args
1613     68/push _test-input-stream/imm32
1614     # . . call
1615     e8/call clear-stream/disp32
1616     # . . discard args
1617     81 0/subop/add %esp 4/imm32
1618     # var slice/ecx: slice
1619     68/push 0/imm32/end
1620     68/push 0/imm32/start
1621     89/<- %ecx 4/r32/esp
1622     # write(_test-input-stream, " ) ")
1623     # . . push args
1624     68/push " ) "/imm32
1625     68/push _test-input-stream/imm32
1626     # . . call
1627     e8/call write/disp32
1628     # . . discard args
1629     81 0/subop/add %esp 8/imm32
1630     # next-word-string-or-expression-without-metadata(_test-input-stream, slice)
1631     # . . push args
1632     51/push-ecx
1633     68/push _test-input-stream/imm32
1634     # . . call
1635     e8/call next-word-string-or-expression-without-metadata/disp32
1636     # . . discard args
1637     81 0/subop/add %esp 8/imm32
1638     # check-ints-equal(slice->start, 0, msg)
1639     # . . push args
1640     68/push "F - test-next-word-string-or-expression-without-metadata-returns-eol-on-trailing-close-paren: start"/imm32
1641     68/push 0/imm32
1642     ff 6/subop/push *ecx
1643     # . . call
1644     e8/call check-ints-equal/disp32
1645     # . . discard args
1646     81 0/subop/add %esp 0xc/imm32
1647     # check-ints-equal(slice->end, 0, msg)
1648     # . . push args
1649     68/push "F - test-next-word-string-or-expression-without-metadata-returns-eol-on-trailing-close-paren: end"/imm32
1650     68/push 0/imm32
1651     ff 6/subop/push *(ecx+4)
1652     # . . call
1653     e8/call check-ints-equal/disp32
1654     # . . discard args
1655     81 0/subop/add %esp 0xc/imm32
1656     # . epilogue
1657     89/<- %esp 5/r32/ebp
1658     5d/pop-to-ebp
1659     c3/return
1660 
1661 test-next-word-string-or-expression-without-metadata-handles-comment-after-trailing-close-paren:
1662     # . prologue
1663     55/push-ebp
1664     89/<- %ebp 4/r32/esp
1665     # setup
1666     # . clear-stream(_test-input-stream)
1667     # . . push args
1668     68/push _test-input-stream/imm32
1669     # . . call
1670     e8/call clear-stream/disp32
1671     # . . discard args
1672     81 0/subop/add %esp 4/imm32
1673     # var slice/ecx: slice
1674     68/push 0/imm32/end
1675     68/push 0/imm32/start
1676     89/<- %ecx 4/r32/esp
1677     # write(_test-input-stream, " ) # abc ")
1678     # . . push args
1679     68/push " ) # abc "/imm32
1680     68/push _test-input-stream/imm32
1681     # . . call
1682     e8/call write/disp32
1683     # . . discard args
1684     81 0/subop/add %esp 8/imm32
1685     # next-word-string-or-expression-without-metadata(_test-input-stream, slice)
1686     # . . push args
1687     51/push-ecx
1688     68/push _test-input-stream/imm32
1689     # . . call
1690     e8/call next-word-string-or-expression-without-metadata/disp32
1691     # . . discard args
1692     81 0/subop/add %esp 8/imm32
1693     # check-ints-equal(slice->start, 0, msg)
1694     # . . push args
1695     68/push "F - test-next-word-string-or-expression-without-metadata-handles-comment-after-trailing-close-paren: start"/imm32
1696     68/push 0/imm32
1697     ff 6/subop/push *ecx
1698     # . . call
1699     e8/call check-ints-equal/disp32
1700     # . . discard args
1701     81 0/subop/add %esp 0xc/imm32
1702     # check-ints-equal(slice->end, 0, msg)
1703     # . . push args
1704     68/push "F - test-next-word-string-or-expression-without-metadata-handles-comment-after-trailing-close-paren: end"/imm32
1705     68/push 0/imm32
1706     ff 6/subop/push *(ecx+4)
1707     # . . call
1708     e8/call check-ints-equal/disp32
1709     # . . discard args
1710     81 0/subop/add %esp 0xc/imm32
1711     # . epilogue
1712     89/<- %esp 5/r32/ebp
1713     5d/pop-to-ebp
1714     c3/return
1715 
1716 test-next-word-string-or-expression-without-metadata-handles-newline-after-trailing-close-paren:
1717     # . prologue
1718     55/push-ebp
1719     89/<- %ebp 4/r32/esp
1720     # setup
1721     # . clear-stream(_test-input-stream)
1722     # . . push args
1723     68/push _test-input-stream/imm32
1724     # . . call
1725     e8/call clear-stream/disp32
1726     # . . discard args
1727     81 0/subop/add %esp 4/imm32
1728     # var slice/ecx: slice
1729     68/push 0/imm32/end
1730     68/push 0/imm32/start
1731     89/<- %ecx 4/r32/esp
1732     # write(_test-input-stream, " )\n")
1733     # . . push args
1734     68/push " )\n"/imm32
1735     68/push _test-input-stream/imm32
1736     # . . call
1737     e8/call write/disp32
1738     # . . discard args
1739     81 0/subop/add %esp 8/imm32
1740     # next-word-string-or-expression-without-metadata(_test-input-stream, slice)
1741     # . . push args
1742     51/push-ecx
1743     68/push _test-input-stream/imm32
1744     # . . call
1745     e8/call next-word-string-or-expression-without-metadata/disp32
1746     # . . discard args
1747     81 0/subop/add %esp 8/imm32
1748     # check-ints-equal(slice->start, 0, msg)
1749     # . . push args
1750     68/push "F - test-next-word-string-or-expression-without-metadata-handles-newline-after-trailing-close-paren: start"/imm32
1751     68/push 0/imm32
1752     ff 6/subop/push *ecx
1753     # . . call
1754     e8/call check-ints-equal/disp32
1755     # . . discard args
1756     81 0/subop/add %esp 0xc/imm32
1757     # check-ints-equal(slice->end, 0, msg)
1758     # . . push args
1759     68/push "F - test-next-word-string-or-expression-without-metadata-handles-newline-after-trailing-close-paren: end"/imm32
1760     68/push 0/imm32
1761     ff 6/subop/push *(ecx+4)
1762     # . . call
1763     e8/call check-ints-equal/disp32
1764     # . . discard args
1765     81 0/subop/add %esp 0xc/imm32
1766     # . epilogue
1767     89/<- %esp 5/r32/ebp
1768     5d/pop-to-ebp
1769     c3/return
1770 
1771 test-next-word-string-or-expression-without-metadata-stops-at-close-paren:
1772     # . prologue
1773     55/push-ebp
1774     89/<- %ebp 4/r32/esp
1775     # setup
1776     # . clear-stream(_test-input-stream)
1777     # . . push args
1778     68/push _test-input-stream/imm32
1779     # . . call
1780     e8/call clear-stream/disp32
1781     # . . discard args
1782     81 0/subop/add %esp 4/imm32
1783     # var slice/ecx: slice
1784     68/push 0/imm32/end
1785     68/push 0/imm32/start
1786     89/<- %ecx 4/r32/esp
1787     # write(_test-input-stream, " abc) # def")
1788     # . . push args
1789     68/push " abc) # def"/imm32
1790     68/push _test-input-stream/imm32
1791     # . . call
1792     e8/call write/disp32
1793     # . . discard args
1794     81 0/subop/add %esp 8/imm32
1795     # next-word-string-or-expression-without-metadata(_test-input-stream, slice)
1796     # . . push args
1797     51/push-ecx
1798     68/push _test-input-stream/imm32
1799     # . . call
1800     e8/call next-word-string-or-expression-without-metadata/disp32
1801     # . . discard args
1802     81 0/subop/add %esp 8/imm32
1803     # check-ints-equal(slice->start - _test-input-stream->data, 1, msg)
1804     # . check-ints-equal(slice->start - _test-input-stream, 13, msg)
1805     # . . push args
1806     68/push "F - test-next-word-string-or-expression-without-metadata-stops-at-close-paren: start"/imm32
1807     68/push 0xd/imm32
1808     # . . push slice->start - _test-input-stream
1809     8b/-> *ecx 0/r32/eax
1810     81 5/subop/subtract %eax _test-input-stream/imm32
1811     50/push-eax
1812     # . . call
1813     e8/call check-ints-equal/disp32
1814     # . . discard args
1815     81 0/subop/add %esp 0xc/imm32
1816     # check-ints-equal(slice->end - _test-input-stream->data, 4, msg)
1817     # . check-ints-equal(slice->end - _test-input-stream, 16, msg)
1818     # . . push args
1819     68/push "F - test-next-word-string-or-expression-without-metadata-stops-at-close-paren: end"/imm32
1820     68/push 0x10/imm32
1821     # . . push slice->end - _test-input-stream
1822     8b/-> *(ecx+4) 0/r32/eax
1823     81 5/subop/subtract %eax _test-input-stream/imm32
1824     50/push-eax
1825     # . . call
1826     e8/call check-ints-equal/disp32
1827     # . . discard args
1828     81 0/subop/add %esp 0xc/imm32
1829     # . epilogue
1830     89/<- %esp 5/r32/ebp
1831     5d/pop-to-ebp
1832     c3/return