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