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