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