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) return out = {0, 0}
 824     # . ecx = line->read
 825     8b/-> *(esi+4) 1/r32/ecx
 826     # . if (ecx >= line->write) return out = {0, 0}
 827     3b/compare 1/r32/ecx *esi
 828     0f 8d/jump-if->= $next-word-string-or-expression-without-metadata:return-eol/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:error1:
 989     # print(stderr, "error: no space allowed after '*' in '" line "'")
 990     # . write-buffered(Stderr, "error: no space allowed after '*' in '")
 991     # . . push args
 992     68/push "error: no space allowed after '*' 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:error2:
1028     # print(stderr, "error: *(...) expression must be all on a single line in '" line "'")
1029     # . write-buffered(Stderr, "error: *(...) expression must be all on a single line in '")
1030     # . . push args
1031     68/push "error: *(...) expression must be all on a single line 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:error3:
1067     # print(stderr, "error: no metadata after calls; just use a comment (in '" line "')")
1068     # . write-buffered(Stderr, "error: no metadata after calls; just use a comment (in '")
1069     # . . push args
1070     68/push "error: no metadata after calls; just use a comment (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:error4:
1106     # print(stderr, "error: unexpected text after end of call in '" line "'")
1107     # . write-buffered(Stderr, "error: unexpected text after end of call in '")
1108     # . . push args
1109     68/push "error: unexpected text after end of call 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:error5:
1145     # print(stderr, "error: no metadata anywhere in calls (in '" line "')")
1146     # . write-buffered(Stderr, "error: no metadata anywhere in calls (in '")
1147     # . . push args
1148     68/push "error: no metadata anywhere in calls (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 test-next-word-string-or-expression-without-metadata:
1184     # . prologue
1185     55/push-ebp
1186     89/<- %ebp 4/r32/esp
1187     # setup
1188     # . clear-stream(_test-input-stream)
1189     # . . push args
1190     68/push _test-input-stream/imm32
1191     # . . call
1192     e8/call clear-stream/disp32
1193     # . . discard args
1194     81 0/subop/add %esp 4/imm32
1195     # var slice/ecx: slice
1196     68/push 0/imm32/end
1197     68/push 0/imm32/start
1198     89/<- %ecx 4/r32/esp
1199     # write(_test-input-stream, "  ab")
1200     # . . push args
1201     68/push "  ab"/imm32
1202     68/push _test-input-stream/imm32
1203     # . . call
1204     e8/call write/disp32
1205     # . . discard args
1206     81 0/subop/add %esp 8/imm32
1207     # next-word-string-or-expression-without-metadata(_test-input-stream, slice)
1208     # . . push args
1209     51/push-ecx
1210     68/push _test-input-stream/imm32
1211     # . . call
1212     e8/call next-word-string-or-expression-without-metadata/disp32
1213     # . . discard args
1214     81 0/subop/add %esp 8/imm32
1215     # check-ints-equal(_test-input-stream->read, 4, msg)
1216     # . . push args
1217     68/push "F - test-next-word-string-or-expression-without-metadata/updates-stream-read-correctly"/imm32
1218     68/push 4/imm32
1219     b8/copy-to-eax _test-input-stream/imm32
1220     ff 6/subop/push *(eax+4)
1221     # . . call
1222     e8/call check-ints-equal/disp32
1223     # . . discard args
1224     81 0/subop/add %esp 0xc/imm32
1225     # check-ints-equal(slice->start - _test-input-stream->data, 2, msg)
1226     # . check-ints-equal(slice->start - _test-input-stream, 14, msg)
1227     # . . push args
1228     68/push "F - test-next-word-string-or-expression-without-metadata: start"/imm32
1229     68/push 0xe/imm32
1230     # . . push slice->start - _test-input-stream
1231     8b/-> *ecx 0/r32/eax
1232     81 5/subop/subtract %eax _test-input-stream/imm32
1233     50/push-eax
1234     # . . call
1235     e8/call check-ints-equal/disp32
1236     # . . discard args
1237     81 0/subop/add %esp 0xc/imm32
1238     # check-ints-equal(slice->end - _test-input-stream->data, 4, msg)
1239     # . check-ints-equal(slice->end - _test-input-stream, 16, msg)
1240     # . . push args
1241     68/push "F - test-next-word-string-or-expression-without-metadata: end"/imm32
1242     68/push 0x10/imm32
1243     # . . push slice->end - _test-input-stream
1244     8b/-> *(ecx+4) 0/r32/eax
1245     81 5/subop/subtract %eax _test-input-stream/imm32
1246     50/push-eax
1247     # . . call
1248     e8/call check-ints-equal/disp32
1249     # . . discard args
1250     81 0/subop/add %esp 0xc/imm32
1251     # . epilogue
1252     89/<- %esp 5/r32/ebp
1253     5d/pop-to-ebp
1254     c3/return
1255 
1256 test-next-word-string-or-expression-without-metadata-returns-whole-comment:
1257     # . prologue
1258     55/push-ebp
1259     89/<- %ebp 4/r32/esp
1260     # setup
1261     # . clear-stream(_test-input-stream)
1262     # . . push args
1263     68/push _test-input-stream/imm32
1264     # . . call
1265     e8/call clear-stream/disp32
1266     # . . discard args
1267     81 0/subop/add %esp 4/imm32
1268     # var slice/ecx: slice
1269     68/push 0/imm32/end
1270     68/push 0/imm32/start
1271     89/<- %ecx 4/r32/esp
1272     # write(_test-input-stream, "  # a")
1273     # . . push args
1274     68/push "  # a"/imm32
1275     68/push _test-input-stream/imm32
1276     # . . call
1277     e8/call write/disp32
1278     # . . discard args
1279     81 0/subop/add %esp 8/imm32
1280     # next-word-string-or-expression-without-metadata(_test-input-stream, slice)
1281     # . . push args
1282     51/push-ecx
1283     68/push _test-input-stream/imm32
1284     # . . call
1285     e8/call next-word-string-or-expression-without-metadata/disp32
1286     # . . discard args
1287     81 0/subop/add %esp 8/imm32
1288     # check-ints-equal(_test-input-stream->read, 5, msg)
1289     # . . push args
1290     68/push "F - test-next-word-string-or-expression-without-metadata-returns-whole-comment/updates-stream-read-correctly"/imm32
1291     68/push 5/imm32
1292     b8/copy-to-eax _test-input-stream/imm32
1293     ff 6/subop/push *(eax+4)
1294     # . . call
1295     e8/call check-ints-equal/disp32
1296     # . . discard args
1297     81 0/subop/add %esp 0xc/imm32
1298     # check-ints-equal(slice->start - _test-input-stream->data, 2, msg)
1299     # . check-ints-equal(slice->start - _test-input-stream, 14, msg)
1300     # . . push args
1301     68/push "F - test-next-word-string-or-expression-without-metadata-returns-whole-comment: start"/imm32
1302     68/push 0xe/imm32
1303     # . . push slice->start - _test-input-stream
1304     8b/-> *ecx 0/r32/eax
1305     81 5/subop/subtract %eax _test-input-stream/imm32
1306     50/push-eax
1307     # . . call
1308     e8/call check-ints-equal/disp32
1309     # . . discard args
1310     81 0/subop/add %esp 0xc/imm32
1311     # check-ints-equal(slice->end - _test-input-stream->data, 5, msg)
1312     # . check-ints-equal(slice->end - _test-input-stream, 17, msg)
1313     # . . push args
1314     68/push "F - test-next-word-string-or-expression-without-metadata-returns-whole-comment: end"/imm32
1315     68/push 0x11/imm32
1316     # . . push slice->end - _test-input-stream
1317     8b/-> *(ecx+4) 0/r32/eax
1318     81 5/subop/subtract %eax _test-input-stream/imm32
1319     50/push-eax
1320     # . . call
1321     e8/call check-ints-equal/disp32
1322     # . . discard args
1323     81 0/subop/add %esp 0xc/imm32
1324     # . epilogue
1325     89/<- %esp 5/r32/ebp
1326     5d/pop-to-ebp
1327     c3/return
1328 
1329 test-next-word-string-or-expression-without-metadata-returns-empty-slice-on-eof:
1330     # . prologue
1331     55/push-ebp
1332     89/<- %ebp 4/r32/esp
1333     # setup
1334     # . clear-stream(_test-input-stream)
1335     # . . push args
1336     68/push _test-input-stream/imm32
1337     # . . call
1338     e8/call clear-stream/disp32
1339     # . . discard args
1340     81 0/subop/add %esp 4/imm32
1341     # var slice/ecx: slice
1342     68/push 0/imm32/end
1343     68/push 0/imm32/start
1344     89/<- %ecx 4/r32/esp
1345     # write nothing to _test-input-stream
1346     # next-word-string-or-expression-without-metadata(_test-input-stream, slice)
1347     # . . push args
1348     51/push-ecx
1349     68/push _test-input-stream/imm32
1350     # . . call
1351     e8/call next-word-string-or-expression-without-metadata/disp32
1352     # . . discard args
1353     81 0/subop/add %esp 8/imm32
1354     # check-ints-equal(slice->end - slice->start, 0, msg)
1355     # . . push args
1356     68/push "F - test-next-word-string-or-expression-without-metadata-returns-empty-expression-on-eof"/imm32
1357     68/push 0/imm32
1358     # . . push slice->end - slice->start
1359     8b/-> *(ecx+4) 0/r32/eax
1360     2b/subtract-> *ecx 0/r32/eax  # subtract *ecx from eax
1361     50/push-eax
1362     # . . call
1363     e8/call check-ints-equal/disp32
1364     # . . discard args
1365     81 0/subop/add %esp 0xc/imm32
1366     # . epilogue
1367     89/<- %esp 5/r32/ebp
1368     5d/pop-to-ebp
1369     c3/return
1370 
1371 test-next-word-string-or-expression-without-metadata-returns-string-literal:
1372     # . prologue
1373     55/push-ebp
1374     89/<- %ebp 4/r32/esp
1375     # setup
1376     # . clear-stream(_test-input-stream)
1377     # . . push args
1378     68/push _test-input-stream/imm32
1379     # . . call
1380     e8/call clear-stream/disp32
1381     # . . discard args
1382     81 0/subop/add %esp 4/imm32
1383     # var slice/ecx: slice
1384     68/push 0/imm32/end
1385     68/push 0/imm32/start
1386     89/<- %ecx 4/r32/esp
1387     # write(_test-input-stream, " \"a b\" ")
1388     # . . push args
1389     68/push " \"a b\" "/imm32
1390     68/push _test-input-stream/imm32
1391     # . . call
1392     e8/call write/disp32
1393     # . . discard args
1394     81 0/subop/add %esp 8/imm32
1395     # next-word-string-or-expression-without-metadata(_test-input-stream, slice)
1396     # . . push args
1397     51/push-ecx
1398     68/push _test-input-stream/imm32
1399     # . . call
1400     e8/call next-word-string-or-expression-without-metadata/disp32
1401     # . . discard args
1402     81 0/subop/add %esp 8/imm32
1403     # check-ints-equal(slice->start - _test-input-stream->data, 1, msg)
1404     # . check-ints-equal(slice->start - _test-input-stream, 13, msg)
1405     # . . push args
1406     68/push "F - test-next-word-string-or-expression-without-metadata-returns-string-literal: start"/imm32
1407     68/push 0xd/imm32
1408     # . . push slice->start - _test-input-stream
1409     8b/-> *ecx 0/r32/eax
1410     81 5/subop/subtract %eax _test-input-stream/imm32
1411     50/push-eax
1412     # . . call
1413     e8/call check-ints-equal/disp32
1414     # . . discard args
1415     81 0/subop/add %esp 0xc/imm32
1416     # check-ints-equal(slice->end - _test-input-stream->data, 6, msg)
1417     # . check-ints-equal(slice->end - _test-input-stream, 18, msg)
1418     # . . push args
1419     68/push "F - test-next-word-string-or-expression-without-metadata-returns-string-literal: end"/imm32
1420     68/push 0x12/imm32
1421     # . . push slice->end - _test-input-stream
1422     8b/-> *(ecx+4) 0/r32/eax
1423     81 5/subop/subtract %eax _test-input-stream/imm32
1424     50/push-eax
1425     # . . call
1426     e8/call check-ints-equal/disp32
1427     # . . discard args
1428     81 0/subop/add %esp 0xc/imm32
1429     # . epilogue
1430     89/<- %esp 5/r32/ebp
1431     5d/pop-to-ebp
1432     c3/return
1433 
1434 test-next-word-string-or-expression-without-metadata-returns-string-with-escapes:
1435     # . prologue
1436     55/push-ebp
1437     89/<- %ebp 4/r32/esp
1438     # setup
1439     # . clear-stream(_test-input-stream)
1440     # . . push args
1441     68/push _test-input-stream/imm32
1442     # . . call
1443     e8/call clear-stream/disp32
1444     # . . discard args
1445     81 0/subop/add %esp 4/imm32
1446     # var slice/ecx: slice
1447     68/push 0/imm32/end
1448     68/push 0/imm32/start
1449     89/<- %ecx 4/r32/esp
1450     # write(_test-input-stream, " \"a\\\"b\"")
1451     # . . push args
1452     68/push " \"a\\\"b\""/imm32
1453     68/push _test-input-stream/imm32
1454     # . . call
1455     e8/call write/disp32
1456     # . . discard args
1457     81 0/subop/add %esp 8/imm32
1458     # next-word-string-or-expression-without-metadata(_test-input-stream, slice)
1459     # . . push args
1460     51/push-ecx
1461     68/push _test-input-stream/imm32
1462     # . . call
1463     e8/call next-word-string-or-expression-without-metadata/disp32
1464     # . . discard args
1465     81 0/subop/add %esp 8/imm32
1466     # check-ints-equal(slice->start - _test-input-stream->data, 1, msg)
1467     # . check-ints-equal(slice->start - _test-input-stream, 13, msg)
1468     # . . push args
1469     68/push "F - test-next-word-string-or-expression-without-metadata-returns-string-with-escapes: start"/imm32
1470     68/push 0xd/imm32
1471     # . . push slice->start - _test-input-stream
1472     8b/-> *ecx 0/r32/eax
1473     81 5/subop/subtract %eax _test-input-stream/imm32
1474     50/push-eax
1475     # . . call
1476     e8/call check-ints-equal/disp32
1477     # . . discard args
1478     81 0/subop/add %esp 0xc/imm32
1479     # check-ints-equal(slice->end - _test-input-stream->data, 7, msg)
1480     # . check-ints-equal(slice->end - _test-input-stream, 19, msg)
1481     # . . push args
1482     68/push "F - test-next-word-string-or-expression-without-metadata-returns-string-with-escapes: end"/imm32
1483     68/push 0x13/imm32
1484     # . . push slice->end - _test-input-stream
1485     8b/-> *(ecx+4) 0/r32/eax
1486     81 5/subop/subtract %eax _test-input-stream/imm32
1487     50/push-eax
1488     # . . call
1489     e8/call check-ints-equal/disp32
1490     # . . discard args
1491     81 0/subop/add %esp 0xc/imm32
1492     # . epilogue
1493     89/<- %esp 5/r32/ebp
1494     5d/pop-to-ebp
1495     c3/return
1496 
1497 test-next-word-string-or-expression-without-metadata-returns-whole-expression:
1498     # . prologue
1499     55/push-ebp
1500     89/<- %ebp 4/r32/esp
1501     # setup
1502     # . clear-stream(_test-input-stream)
1503     # . . push args
1504     68/push _test-input-stream/imm32
1505     # . . call
1506     e8/call clear-stream/disp32
1507     # . . discard args
1508     81 0/subop/add %esp 4/imm32
1509     # var slice/ecx: slice
1510     68/push 0/imm32/end
1511     68/push 0/imm32/start
1512     89/<- %ecx 4/r32/esp
1513     # write(_test-input-stream, " *(a b) ")
1514     # . . push args
1515     68/push " *(a b) "/imm32
1516     68/push _test-input-stream/imm32
1517     # . . call
1518     e8/call write/disp32
1519     # . . discard args
1520     81 0/subop/add %esp 8/imm32
1521     # next-word-string-or-expression-without-metadata(_test-input-stream, slice)
1522     # . . push args
1523     51/push-ecx
1524     68/push _test-input-stream/imm32
1525     # . . call
1526     e8/call next-word-string-or-expression-without-metadata/disp32
1527     # . . discard args
1528     81 0/subop/add %esp 8/imm32
1529     # check-ints-equal(slice->start - _test-input-stream->data, 1, msg)
1530     # . check-ints-equal(slice->start - _test-input-stream, 13, msg)
1531     # . . push args
1532     68/push "F - test-next-word-string-or-expression-without-metadata-returns-whole-expression: start"/imm32
1533     68/push 0xd/imm32
1534     # . . push slice->start - _test-input-stream
1535     8b/-> *ecx 0/r32/eax
1536     81 5/subop/subtract %eax _test-input-stream/imm32
1537     50/push-eax
1538     # . . call
1539     e8/call check-ints-equal/disp32
1540     # . . discard args
1541     81 0/subop/add %esp 0xc/imm32
1542     # check-ints-equal(slice->end - _test-input-stream->data, 7, msg)
1543     # . check-ints-equal(slice->end - _test-input-stream, 19, msg)
1544     # . . push args
1545     68/push "F - test-next-word-string-or-expression-without-metadata-returns-whole-expression: end"/imm32
1546     68/push 0x13/imm32
1547     # . . push slice->end - _test-input-stream
1548     8b/-> *(ecx+4) 0/r32/eax
1549     81 5/subop/subtract %eax _test-input-stream/imm32
1550     50/push-eax
1551     # . . call
1552     e8/call check-ints-equal/disp32
1553     # . . discard args
1554     81 0/subop/add %esp 0xc/imm32
1555     # . epilogue
1556     89/<- %esp 5/r32/ebp
1557     5d/pop-to-ebp
1558     c3/return
1559 
1560 test-next-word-string-or-expression-without-metadata-returns-eol-on-trailing-close-paren:
1561     # . prologue
1562     55/push-ebp
1563     89/<- %ebp 4/r32/esp
1564     # setup
1565     # . clear-stream(_test-input-stream)
1566     # . . push args
1567     68/push _test-input-stream/imm32
1568     # . . call
1569     e8/call clear-stream/disp32
1570     # . . discard args
1571     81 0/subop/add %esp 4/imm32
1572     # var slice/ecx: slice
1573     68/push 0/imm32/end
1574     68/push 0/imm32/start
1575     89/<- %ecx 4/r32/esp
1576     # write(_test-input-stream, " ) ")
1577     # . . push args
1578     68/push " ) "/imm32
1579     68/push _test-input-stream/imm32
1580     # . . call
1581     e8/call write/disp32
1582     # . . discard args
1583     81 0/subop/add %esp 8/imm32
1584     # next-word-string-or-expression-without-metadata(_test-input-stream, slice)
1585     # . . push args
1586     51/push-ecx
1587     68/push _test-input-stream/imm32
1588     # . . call
1589     e8/call next-word-string-or-expression-without-metadata/disp32
1590     # . . discard args
1591     81 0/subop/add %esp 8/imm32
1592     # check-ints-equal(slice->start, 0, msg)
1593     # . . push args
1594     68/push "F - test-next-word-string-or-expression-without-metadata-returns-eol-on-trailing-close-paren: start"/imm32
1595     68/push 0/imm32
1596     ff 6/subop/push *ecx
1597     # . . call
1598     e8/call check-ints-equal/disp32
1599     # . . discard args
1600     81 0/subop/add %esp 0xc/imm32
1601     # check-ints-equal(slice->end, 0, msg)
1602     # . . push args
1603     68/push "F - test-next-word-string-or-expression-without-metadata-returns-eol-on-trailing-close-paren: end"/imm32
1604     68/push 0/imm32
1605     ff 6/subop/push *(ecx+4)
1606     # . . call
1607     e8/call check-ints-equal/disp32
1608     # . . discard args
1609     81 0/subop/add %esp 0xc/imm32
1610     # . epilogue
1611     89/<- %esp 5/r32/ebp
1612     5d/pop-to-ebp
1613     c3/return
1614 
1615 test-next-word-string-or-expression-without-metadata-handles-comment-after-trailing-close-paren:
1616     # . prologue
1617     55/push-ebp
1618     89/<- %ebp 4/r32/esp
1619     # setup
1620     # . clear-stream(_test-input-stream)
1621     # . . push args
1622     68/push _test-input-stream/imm32
1623     # . . call
1624     e8/call clear-stream/disp32
1625     # . . discard args
1626     81 0/subop/add %esp 4/imm32
1627     # var slice/ecx: slice
1628     68/push 0/imm32/end
1629     68/push 0/imm32/start
1630     89/<- %ecx 4/r32/esp
1631     # write(_test-input-stream, " ) # abc ")
1632     # . . push args
1633     68/push " ) # abc "/imm32
1634     68/push _test-input-stream/imm32
1635     # . . call
1636     e8/call write/disp32
1637     # . . discard args
1638     81 0/subop/add %esp 8/imm32
1639     # next-word-string-or-expression-without-metadata(_test-input-stream, slice)
1640     # . . push args
1641     51/push-ecx
1642     68/push _test-input-stream/imm32
1643     # . . call
1644     e8/call next-word-string-or-expression-without-metadata/disp32
1645     # . . discard args
1646     81 0/subop/add %esp 8/imm32
1647     # check-ints-equal(slice->start, 0, msg)
1648     # . . push args
1649     68/push "F - test-next-word-string-or-expression-without-metadata-handles-comment-after-trailing-close-paren: start"/imm32
1650     68/push 0/imm32
1651     ff 6/subop/push *ecx
1652     # . . call
1653     e8/call check-ints-equal/disp32
1654     # . . discard args
1655     81 0/subop/add %esp 0xc/imm32
1656     # check-ints-equal(slice->end, 0, msg)
1657     # . . push args
1658     68/push "F - test-next-word-string-or-expression-without-metadata-handles-comment-after-trailing-close-paren: end"/imm32
1659     68/push 0/imm32
1660     ff 6/subop/push *(ecx+4)
1661     # . . call
1662     e8/call check-ints-equal/disp32
1663     # . . discard args
1664     81 0/subop/add %esp 0xc/imm32
1665     # . epilogue
1666     89/<- %esp 5/r32/ebp
1667     5d/pop-to-ebp
1668     c3/return
1669 
1670 test-next-word-string-or-expression-without-metadata-handles-newline-after-trailing-close-paren:
1671     # . prologue
1672     55/push-ebp
1673     89/<- %ebp 4/r32/esp
1674     # setup
1675     # . clear-stream(_test-input-stream)
1676     # . . push args
1677     68/push _test-input-stream/imm32
1678     # . . call
1679     e8/call clear-stream/disp32
1680     # . . discard args
1681     81 0/subop/add %esp 4/imm32
1682     # var slice/ecx: slice
1683     68/push 0/imm32/end
1684     68/push 0/imm32/start
1685     89/<- %ecx 4/r32/esp
1686     # write(_test-input-stream, " )\n")
1687     # . . push args
1688     68/push " )\n"/imm32
1689     68/push _test-input-stream/imm32
1690     # . . call
1691     e8/call write/disp32
1692     # . . discard args
1693     81 0/subop/add %esp 8/imm32
1694     # next-word-string-or-expression-without-metadata(_test-input-stream, slice)
1695     # . . push args
1696     51/push-ecx
1697     68/push _test-input-stream/imm32
1698     # . . call
1699     e8/call next-word-string-or-expression-without-metadata/disp32
1700     # . . discard args
1701     81 0/subop/add %esp 8/imm32
1702     # check-ints-equal(slice->start, 0, msg)
1703     # . . push args
1704     68/push "F - test-next-word-string-or-expression-without-metadata-handles-newline-after-trailing-close-paren: start"/imm32
1705     68/push 0/imm32
1706     ff 6/subop/push *ecx
1707     # . . call
1708     e8/call check-ints-equal/disp32
1709     # . . discard args
1710     81 0/subop/add %esp 0xc/imm32
1711     # check-ints-equal(slice->end, 0, msg)
1712     # . . push args
1713     68/push "F - test-next-word-string-or-expression-without-metadata-handles-newline-after-trailing-close-paren: end"/imm32
1714     68/push 0/imm32
1715     ff 6/subop/push *(ecx+4)
1716     # . . call
1717     e8/call check-ints-equal/disp32
1718     # . . discard args
1719     81 0/subop/add %esp 0xc/imm32
1720     # . epilogue
1721     89/<- %esp 5/r32/ebp
1722     5d/pop-to-ebp
1723     c3/return
1724 
1725 test-next-word-string-or-expression-without-metadata-stops-at-close-paren:
1726     # . prologue
1727     55/push-ebp
1728     89/<- %ebp 4/r32/esp
1729     # setup
1730     # . clear-stream(_test-input-stream)
1731     # . . push args
1732     68/push _test-input-stream/imm32
1733     # . . call
1734     e8/call clear-stream/disp32
1735     # . . discard args
1736     81 0/subop/add %esp 4/imm32
1737     # var slice/ecx: slice
1738     68/push 0/imm32/end
1739     68/push 0/imm32/start
1740     89/<- %ecx 4/r32/esp
1741     # write(_test-input-stream, " abc) # def")
1742     # . . push args
1743     68/push " abc) # def"/imm32
1744     68/push _test-input-stream/imm32
1745     # . . call
1746     e8/call write/disp32
1747     # . . discard args
1748     81 0/subop/add %esp 8/imm32
1749     # next-word-string-or-expression-without-metadata(_test-input-stream, slice)
1750     # . . push args
1751     51/push-ecx
1752     68/push _test-input-stream/imm32
1753     # . . call
1754     e8/call next-word-string-or-expression-without-metadata/disp32
1755     # . . discard args
1756     81 0/subop/add %esp 8/imm32
1757     # check-ints-equal(slice->start - _test-input-stream->data, 1, msg)
1758     # . check-ints-equal(slice->start - _test-input-stream, 13, msg)
1759     # . . push args
1760     68/push "F - test-next-word-string-or-expression-without-metadata-stops-at-close-paren: start"/imm32
1761     68/push 0xd/imm32
1762     # . . push slice->start - _test-input-stream
1763     8b/-> *ecx 0/r32/eax
1764     81 5/subop/subtract %eax _test-input-stream/imm32
1765     50/push-eax
1766     # . . call
1767     e8/call check-ints-equal/disp32
1768     # . . discard args
1769     81 0/subop/add %esp 0xc/imm32
1770     # check-ints-equal(slice->end - _test-input-stream->data, 4, msg)
1771     # . check-ints-equal(slice->end - _test-input-stream, 16, msg)
1772     # . . push args
1773     68/push "F - test-next-word-string-or-expression-without-metadata-stops-at-close-paren: end"/imm32
1774     68/push 0x10/imm32
1775     # . . push slice->end - _test-input-stream
1776     8b/-> *(ecx+4) 0/r32/eax
1777     81 5/subop/subtract %eax _test-input-stream/imm32
1778     50/push-eax
1779     # . . call
1780     e8/call check-ints-equal/disp32
1781     # . . discard args
1782     81 0/subop/add %esp 0xc/imm32
1783     # . epilogue
1784     89/<- %esp 5/r32/ebp
1785     5d/pop-to-ebp
1786     c3/return