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