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