https://github.com/akkartik/mu/blob/master/apps/calls.subx
   1 # Function calls in a single line.
   2 #
   3 # To run (on Linux):
   4 #   $ ./ntranslate 0*.subx apps/subx-common.subx apps/calls.subx
   5 #   $ mv a.elf apps/calls
   6 #   $ chmod +x apps/calls
   7 #
   8 # Example 1:
   9 #   $ echo '(foo %eax)'                         |  apps/calls
  10 #   # . (foo %eax)                                      # output has comments
  11 #   ff 6/subop/push %eax                                # push
  12 #   e8/call foo/disp32                                  # call
  13 #   81 0/subop/add %esp 4/imm32                         # undo push
  14 #
  15 # Example 2:
  16 #   $ echo '(foo Var1 *(eax + 4) "blah")'       |  apps/calls
  17 #   # . (foo Var1 *(eax + 4) "blah")
  18 #   68/push "blah"/imm32
  19 #   ff 6/subop/push *(eax + 4)                          # push args in..
  20 #   68/push Var1/imm32                                  # ..reverse order
  21 #   e8/call foo/disp32
  22 #   81 0/subop/add %esp 4/imm32                         # undo pushes
  23 #
  24 # Calls always begin with '(' as the first non-whitespace on a line.
  25 
  26 == code
  27 
  28 Entry:  # run tests if necessary, convert stdin if not
  29     # . prolog
  30     89/<- %ebp 4/r32/esp
  31 
  32     # initialize heap
  33     # . Heap = new-segment(Heap-size)
  34     # . . push args
  35     68/push Heap/imm32
  36     ff 6/subop/push *Heap-size
  37     # . . call
  38     e8/call new-segment/disp32
  39     # . . discard args
  40     81 0/subop/add %esp 8/imm32
  41 
  42     # - if argc > 1 and argv[1] == "test", then return run_tests()
  43     # if (argc <= 1) goto run-main
  44     81 7/subop/compare *ebp 1/imm32
  45     7e/jump-if-lesser-or-equal $run-main/disp8
  46     # if (!kernel-string-equal?(argv[1], "test")) goto run-main
  47     # . eax = kernel-string-equal?(argv[1], "test")
  48     # . . push args
  49     68/push "test"/imm32
  50     ff 6/subop/push *(ebp+8)
  51     # . . call
  52     e8/call kernel-string-equal?/disp32
  53     # . . discard args
  54     81 0/subop/add %esp 8/imm32
  55     # . if (eax == 0) goto run-main
  56     3d/compare-eax-and 0/imm32
  57     74/jump-if-equal $run-main/disp8
  58     # run-tests()
  59     e8/call run-tests/disp32
  60     # syscall(exit, *Num-test-failures)
  61     8b/-> *Num-test-failures 3/r32/ebx
  62     eb/jump $main:end/disp8
  63 $run-main:
  64     # - otherwise convert stdin
  65     # convert(Stdin, Stdout)
  66     # . . push args
  67     68/push Stdout/imm32
  68     68/push Stdin/imm32
  69     # . . call
  70     e8/call convert/disp32
  71     # . . discard args
  72     81 0/subop/add %esp 8/imm32
  73     # syscall(exit, 0)
  74     bb/copy-to-ebx 0/imm32
  75 $main:end:
  76     b8/copy-to-eax 1/imm32/exit
  77     cd/syscall 0x80/imm8
  78 
  79 convert:  # in : (address buffered-file), out : (address buffered-file) -> <void>
  80     # pseudocode:
  81     #   var line = new-stream(512, 1)
  82     #   var words : (address stream slice) = new-stream(16, 8)  # at most function name and 15 args
  83     #   while true
  84     #     clear-stream(line)
  85     #     read-line-buffered(in, line)
  86     #     if (line->write == 0) break                           # end of file
  87     #     skip-chars-matching-whitespace(line)
  88     #     if line->data[line->read] != '('
  89     #       write-stream-data(out, line)
  90     #       continue
  91     #     # emit comment
  92     #     write-buffered(out, "# . ")
  93     #     write-stream-data(out, line)
  94     #     # emit code
  95     #     ++line->read  # skip '('
  96     #     clear-stream(words)
  97     #     words = parse-line(line)
  98     #     emit-call(out, words)
  99     #   flush(out)
 100     #
 101     # . prolog
 102     55/push-ebp
 103     89/<- %ebp 4/r32/esp
 104     # . save registers
 105     # var line/ecx : (address stream byte) = stream(512)
 106     81 5/subop/subtract %esp 0x200/imm32
 107     68/push 0x200/imm32/length
 108     68/push 0/imm32/read
 109     68/push 0/imm32/write
 110     89/<- %ecx 4/r32/esp
 111     # var words/edx : (address stream slice) = stream(16, 8)
 112     81 5/subop/subtract %esp 0x80/imm32
 113     68/push 0x80/imm32/length
 114     68/push 0/imm32/read
 115     68/push 0/imm32/write
 116     89/<- %edx 4/r32/esp
 117 $convert:loop:
 118     # clear-stream(line)
 119     # . . push args
 120     51/push-ecx
 121     # . . call
 122     e8/call clear-stream/disp32
 123     # . . discard args
 124     81 0/subop/add %esp 4/imm32
 125     # read-line-buffered(in, line)
 126     # . . push args
 127     51/push-ecx
 128     ff 6/subop/push *(ebp+8)
 129     # . . call
 130     e8/call read-line-buffered/disp32
 131     # . . discard args
 132     81 0/subop/add %esp 8/imm32
 133 $convert:check0:
 134     # if (line->write == 0) break
 135     81 7/subop/compare *ecx 0/imm32
 136     0f 84/jump-if-equal $convert:break/disp32
 137     # TODO
 138     e9/jump $convert:loop/disp32
 139 $convert:break:
 140     # flush(out)
 141     # . . push args
 142     ff 6/subop/push *(ebp+0xc)
 143     # . . call
 144     e8/call flush/disp32
 145     # . . discard args
 146     81 0/subop/add %esp 4/imm32
 147 $convert:end:
 148     # . reclaim locals
 149     81 0/subop/add %esp 0x298/imm32  # 0x20c + 0x8c
 150     # . restore registers
 151     # . epilog
 152     89/<- %esp 5/r32/ebp
 153     5d/pop-to-ebp
 154     c3/return
 155 
 156 parse-line:  # line : (address stream byte), words : (address stream slice)
 157     # pseudocode:
 158     #   var word-slice : (address slice)
 159     #   while true
 160     #     word-slice = next-word-string-or-expression-without-metadata(line)
 161     #     if slice-empty?(word-slice)
 162     #       break                                 # end of line
 163     #     write-int(words, word-slice->start)
 164     #     write-int(words, word-slice->end)
 165     #
 166     # . prolog
 167     55/push-ebp
 168     89/<- %ebp 4/r32/esp
 169     # . save registers
 170 $parse-line:end:
 171     # . reclaim locals
 172     # . restore registers
 173     # . epilog
 174     89/<- %esp 5/r32/ebp
 175     5d/pop-to-ebp
 176     c3/return
 177 
 178 emit-call:  # out : (address buffered-file), words : (address stream slice)
 179     # pseudocode:
 180     #   if (words->write < 8) abort
 181     #   curr = &words->data[words->write-8]
 182     #   min = words->data
 183     #   # emit pushes
 184     #   while true
 185     #     if (curr <= min) break
 186     #     if *curr in '%' '*'
 187     #       write-buffered(out, "ff 6/subop/push ")
 188     #       write-slice-buffered(out, curr)
 189     #       write-buffered(out, "/imm32\n")
 190     #     else
 191     #       write-buffered(out, "68/push ")
 192     #       write-slice-buffered(out, curr)
 193     #       write-buffered(out, "/imm32\n")
 194     #     curr -= 8
 195     #   # emit call
 196     #   write-buffered(out, "e8/call ")
 197     #   write-slice-buffered(out, curr)
 198     #   write-buffered(out, "/disp32\n")
 199     #   # emit pops
 200     #   write-buffered(out, "81 0/subop/add %esp ")
 201     #   print-int32-buffered(out, words->write >> 1 - 4)
 202     #   write-buffered(out, "/imm32\n")
 203     #
 204     # . prolog
 205     55/push-ebp
 206     89/<- %ebp 4/r32/esp
 207     # . save registers
 208 $emit-call:end:
 209     # . reclaim locals
 210     # . restore registers
 211     # . epilog
 212     89/<- %esp 5/r32/ebp
 213     5d/pop-to-ebp
 214     c3/return
 215 
 216 next-word-string-or-expression-without-metadata:  # line : (address stream), out : (address slice)
 217     # pseudocode:
 218     #   skip-chars-matching(line, ' ')
 219     #   if line->read >= line->write              # end of line
 220     #     out = {0, 0}
 221     #     return
 222     #   out->start = &line->data[line->read]
 223     #   if line->data[line->read] == '#'          # comment
 224     #     out->end = &line->data[line->write]     # skip to end of line
 225     #     return
 226     #   if line->data[line->read] == '"'          # string literal
 227     #     skip-string(line)
 228     #     out->end = &line->data[line->read]        # no metadata
 229     #     return
 230     #   if line->data[line->read] == '*'          # expression
 231     #     if line->data[line->read + 1] == ' '
 232     #       abort
 233     #     if line->data[line->read + 1] == '('
 234     #       skip-until-close-paren(line)
 235     #       if (line->data[line->read] != ')'
 236     #         abort
 237     #       ++line->data[line->read] to skip ')'
 238     #     out->end = &line->data[line->read]
 239     #     return
 240     #   if line->data[line->read] == ')'
 241     #     ++line->read to skip ')'
 242     #     # make sure there's nothing else of importance
 243     #     if line->read >= line->write
 244     #       out = {0, 0}
 245     #       return
 246     #     if line->data[line->read] != ' '
 247     #       abort
 248     #     skip-chars-matching-whitespace(line)
 249     #     if line->read >= line->write
 250     #       out = {0, 0}
 251     #       return
 252     #     if line->data[line->read] == '#'        # only thing permitted after ')' is a comment
 253     #       out = {0, 0}
 254     #       return
 255     #     abort
 256     #   # default case: read a word -- but no metadata
 257     #   while true
 258     #     if line->read >= line->write
 259     #       break
 260     #     if line->data[line->read] == ' '
 261     #       break
 262     #     if line->data[line->read] == ')'
 263     #       break
 264     #     if line->data[line->read] == '/'
 265     #       abort
 266     #     ++line->read
 267     #   out->end = &line->data[line->read]
 268     #
 269     # registers:
 270     #   ecx: often line->read
 271     #   eax: often line->data[line->read]
 272     #
 273     # . prolog
 274     55/push-ebp
 275     89/<- %ebp 4/r32/esp
 276     # . save registers
 277     50/push-eax
 278     51/push-ecx
 279     56/push-esi
 280     57/push-edi
 281     # esi = line
 282     8b/-> *(ebp+8) 6/r32/esi
 283     # edi = out
 284     8b/-> *(ebp+0xc) 7/r32/edi
 285     # skip-chars-matching(line, ' ')
 286     # . . push args
 287     68/push 0x20/imm32/space
 288     ff 6/subop/push *(ebp+8)
 289     # . . call
 290     e8/call skip-chars-matching/disp32
 291     # . . discard args
 292     81 0/subop/add %esp 8/imm32
 293 $next-word-string-or-expression-without-metadata:check0:
 294     # if (line->read >= line->write) return out = {0, 0}
 295     # . ecx = line->read
 296     8b/-> *(esi+4) 1/r32/ecx
 297     # . if (ecx >= line->write) return out = {0, 0}
 298     3b/compare *esi 1/r32/ecx
 299     0f 8d/jump-if-greater-or-equal $next-word-string-or-expression-without-metadata:return-eol/disp32
 300 $next-word-string-or-expression-without-metadata:check-for-comment:
 301     # out->start = &line->data[line->read]
 302     8d/copy-address *(esi+ecx+0xc) 0/r32/eax
 303     89/<- *edi 0/r32/eax
 304     # if (line->data[line->read] != '#') goto next check
 305     # . eax = line->data[line->read]
 306     31/xor %eax 0/r32/eax
 307     8a/copy-byte *(esi+ecx+0xc) 0/r32/AL
 308     # . if (eax != '#') goto next check
 309     3d/compare-eax-and 0x23/imm32/pound
 310     75/jump-if-not-equal $next-word-string-or-expression-without-metadata:check-for-string-literal/disp8
 311 $next-word-string-or-expression-without-metadata:comment:
 312     # out->end = &line->data[line->write]
 313     8b/-> *esi 0/r32/eax
 314     8d/copy-address *(esi+eax+0xc) 0/r32/eax
 315     89/<- *(edi+4) 0/r32/eax
 316     # line->read = line->write  # skip rest of line
 317     8b/-> *esi 0/r32/eax
 318     89/<- *(esi+4) 0/r32/eax
 319     # return
 320     e9/jump $next-word-string-or-expression-without-metadata:end/disp32
 321 $next-word-string-or-expression-without-metadata:check-for-string-literal:
 322     # if (line->data[line->read] != '"') goto next check
 323     3d/compare-eax-and 0x22/imm32/dquote
 324     75/jump-if-not-equal $next-word-string-or-expression-without-metadata:check-for-expression/disp8
 325 $next-word-string-or-expression-without-metadata:string-literal:
 326     # skip-string(line)
 327     # . . push args
 328     56/push-esi
 329     # . . call
 330     e8/call skip-string/disp32
 331     # . . discard args
 332     81 0/subop/add %esp 4/imm32
 333     # out->end = &line->data[line->read]
 334     8b/-> *(esi+4) 1/r32/ecx
 335     8d/copy-address *(esi+ecx+0xc) 0/r32/eax
 336     89/<- *(edi+4) 0/r32/eax
 337     # return
 338     e9/jump $next-word-string-or-expression-without-metadata:end/disp32
 339 $next-word-string-or-expression-without-metadata:check-for-expression:
 340     # if (line->data[line->read] != '*') goto next check
 341     3d/compare-eax-and 0x2a/imm32/asterisk
 342     75/jump-if-not-equal $next-word-string-or-expression-without-metadata:check-for-end-of-call/disp8
 343     # if (line->data[line->read + 1] == ' ') goto error1
 344     8a/copy-byte *(esi+ecx+0xd) 0/r32/AL
 345     3d/compare-eax-and 0x20/imm32/space
 346     0f 84/jump-if-equal $next-word-string-or-expression-without-metadata:error1/disp32
 347     # if (line->data[line->read + 1] != '(') goto regular-word
 348     3d/compare-eax-and 0x28/imm32/open-paren
 349     0f 85/jump-if-not-equal $next-word-string-or-expression-without-metadata:regular-word-without-metadata/disp32
 350 $next-word-string-or-expression-without-metadata:paren:
 351     # skip-until-close-paren(line)
 352     # . . push args
 353     56/push-esi
 354     # . . call
 355     e8/call skip-until-close-paren/disp32
 356     # . . discard args
 357     81 0/subop/add %esp 4/imm32
 358     # if (line->data[line->read] != ')') goto error2
 359     # . eax = line->data[line->read]
 360     8b/-> *(esi+4) 1/r32/ecx
 361     8a/copy-byte *(esi+ecx+0xc) 0/r32/AL
 362     # . if (eax != ')') goto error2
 363     3d/compare-eax-and 0x29/imm32/close-paren
 364     0f 85/jump-if-not-equal $next-word-string-or-expression-without-metadata:error2/disp32
 365     # skip ')'
 366     ff 0/subop/increment *(esi+4)
 367     # out->end = &line->data[line->read]
 368     8b/-> *(esi+4) 1/r32/ecx
 369     8d/copy-address *(esi+ecx+0xc) 0/r32/eax
 370     89/<- *(edi+4) 0/r32/eax
 371     # return
 372     e9/jump $next-word-string-or-expression-without-metadata:end/disp32
 373 $next-word-string-or-expression-without-metadata:check-for-end-of-call:
 374     # if (line->data[line->read] != ')') goto next check
 375     3d/compare-eax-and 0x29/imm32/close-paren
 376     75/jump-if-not-equal $next-word-string-or-expression-without-metadata:regular-word-without-metadata/disp8
 377     # ++line->read to skip ')'
 378     ff 0/subop/increment *(esi+4)
 379     # - error checking: make sure there's nothing else of importance on the line
 380     # if (line->read >= line->write) return out = {0, 0}
 381     # . ecx = line->read
 382     8b/-> *(esi+4) 1/r32/ecx
 383     # . if (ecx >= line->write) return {0, 0}
 384     3b/compare *esi 1/r32/ecx
 385     0f 8d/jump-if-greater-or-equal $next-word-string-or-expression-without-metadata:return-eol/disp32
 386     # if (line->data[line->read] == '/') goto error3
 387     # . eax = line->data[line->read]
 388     8a/copy-byte *(esi+ecx+0xc) 0/r32/AL
 389     # . if (eax == '/') goto error3
 390     3d/compare-eax-and 0x2f/imm32/slash
 391     0f 84/jump-if-equal $next-word-string-or-expression-without-metadata:error3/disp32
 392     # if (line->data[line->read] != ' ') goto error4
 393     3d/compare-eax-and 0x20/imm32/space
 394     0f 85/jump-if-not-equal $next-word-string-or-expression-without-metadata:error4/disp32
 395     # skip-chars-matching-whitespace(line)
 396     # . . push args
 397     56/push-esi
 398     # . . call
 399     e8/call skip-chars-matching-whitespace/disp32
 400     # . . discard args
 401     81 0/subop/add %esp 4/imm32
 402     # if (line->read >= line->write) return out = {0, 0}
 403     # . ecx = line->read
 404     8b/-> *(esi+4) 1/r32/ecx
 405     # . if (ecx >= line->write) return {0, 0}
 406     3b/compare *esi 1/r32/ecx
 407     0f 8d/jump-if-greater-or-equal $next-word-string-or-expression-without-metadata:return-eol/disp32
 408     # if (line->data[line->read] == '#') return out = {0, 0}
 409     # . eax = line->data[line->read]
 410     8b/-> *(esi+4) 1/r32/ecx
 411     8a/copy-byte *(esi+ecx+0xc) 0/r32/AL
 412     # . if (eax == '#') return out = {0, 0}
 413     3d/compare-eax-and 0x23/imm32/pound
 414     74/jump-if-equal $next-word-string-or-expression-without-metadata:return-eol/disp8
 415     # otherwise goto error4
 416     e9/jump $next-word-string-or-expression-without-metadata:error4/disp32
 417 $next-word-string-or-expression-without-metadata:regular-word-without-metadata:
 418     # if (line->read >= line->write) break
 419     # . ecx = line->read
 420     8b/-> *(esi+4) 1/r32/ecx
 421     # . if (ecx >= line->write) break
 422     3b/compare *esi 1/r32/ecx
 423     7d/jump-if-greater-or-equal $next-word-string-or-expression-without-metadata:regular-word-break/disp8
 424     # if (line->data[line->read] == ' ') break
 425     # . eax = line->data[line->read]
 426     8b/-> *(esi+4) 1/r32/ecx
 427     8a/copy-byte *(esi+ecx+0xc) 0/r32/AL
 428     # . if (eax == ' ') break
 429     3d/compare-eax-and 0x20/imm32/space
 430     74/jump-if-equal $next-word-string-or-expression-without-metadata:regular-word-break/disp8
 431     # if (line->data[line->read] == ')') break
 432     3d/compare-eax-and 0x29/imm32/close-paren
 433     0f 84/jump-if-equal $next-word-string-or-expression-without-metadata:regular-word-break/disp32
 434     # if (line->data[line->read] == '/') goto error5
 435     3d/compare-eax-and 0x2f/imm32/slash
 436     0f 84/jump-if-equal $next-word-string-or-expression-without-metadata:error5/disp32
 437     # ++line->read
 438     ff 0/subop/increment *(esi+4)
 439     # loop
 440     e9/jump $next-word-string-or-expression-without-metadata:regular-word-without-metadata/disp32
 441 $next-word-string-or-expression-without-metadata:regular-word-break:
 442     # out->end = &line->data[line->read]
 443     8b/-> *(esi+4) 1/r32/ecx
 444     8d/copy-address *(esi+ecx+0xc) 0/r32/eax
 445     89/<- *(edi+4) 0/r32/eax
 446     eb/jump $next-word-string-or-expression-without-metadata:end/disp8
 447 $next-word-string-or-expression-without-metadata:return-eol:
 448     # return out = {0, 0}
 449     c7 0/subop/copy *edi 0/imm32
 450     c7 0/subop/copy *(edi+4) 0/imm32
 451 $next-word-string-or-expression-without-metadata:end:
 452     # . restore registers
 453     5f/pop-to-edi
 454     5e/pop-to-esi
 455     59/pop-to-ecx
 456     58/pop-to-eax
 457     # . epilog
 458     89/<- %esp 5/r32/ebp
 459     5d/pop-to-ebp
 460     c3/return
 461 
 462 $next-word-string-or-expression-without-metadata:error1:
 463     # print(stderr, "error: no space allowed after '*' in '" line "'")
 464     # . write-buffered(Stderr, "error: no space allowed after '*' in '")
 465     # . . push args
 466     68/push "error: no space allowed after '*' in '"/imm32
 467     68/push Stderr/imm32
 468     # . . call
 469     e8/call write-buffered/disp32
 470     # . . discard args
 471     81 0/subop/add %esp 8/imm32
 472     # . write-stream-data(Stderr, line)
 473     # . . push args
 474     56/push-esi
 475     68/push Stderr/imm32
 476     # . . call
 477     e8/call write-stream-data/disp32
 478     # . . discard args
 479     81 0/subop/add %esp 8/imm32
 480     # . write-buffered(Stderr, "'")
 481     # . . push args
 482     68/push "'"/imm32
 483     68/push Stderr/imm32
 484     # . . call
 485     e8/call write-buffered/disp32
 486     # . . discard args
 487     81 0/subop/add %esp 8/imm32
 488     # . flush(Stderr)
 489     # . . push args
 490     68/push Stderr/imm32
 491     # . . call
 492     e8/call flush/disp32
 493     # . . discard args
 494     81 0/subop/add %esp 4/imm32
 495     # . syscall(exit, 1)
 496     bb/copy-to-ebx 1/imm32
 497     b8/copy-to-eax 1/imm32/exit
 498     cd/syscall 0x80/imm8
 499     # never gets here
 500 
 501 $next-word-string-or-expression-without-metadata:error2:
 502     # print(stderr, "error: *(...) expression must be all on a single line in '" line "'")
 503     # . write-buffered(Stderr, "error: *(...) expression must be all on a single line in '")
 504     # . . push args
 505     68/push "error: *(...) expression must be all on a single line in '"/imm32
 506     68/push Stderr/imm32
 507     # . . call
 508     e8/call write-buffered/disp32
 509     # . . discard args
 510     81 0/subop/add %esp 8/imm32
 511     # . write-stream-data(Stderr, line)
 512     # . . push args
 513     56/push-esi
 514     68/push Stderr/imm32
 515     # . . call
 516     e8/call write-stream-data/disp32
 517     # . . discard args
 518     81 0/subop/add %esp 8/imm32
 519     # . write-buffered(Stderr, "'")
 520     # . . push args
 521     68/push "'"/imm32
 522     68/push Stderr/imm32
 523     # . . call
 524     e8/call write-buffered/disp32
 525     # . . discard args
 526     81 0/subop/add %esp 8/imm32
 527     # . flush(Stderr)
 528     # . . push args
 529     68/push Stderr/imm32
 530     # . . call
 531     e8/call flush/disp32
 532     # . . discard args
 533     81 0/subop/add %esp 4/imm32
 534     # . syscall(exit, 1)
 535     bb/copy-to-ebx 1/imm32
 536     b8/copy-to-eax 1/imm32/exit
 537     cd/syscall 0x80/imm8
 538     # never gets here
 539 
 540 $next-word-string-or-expression-without-metadata:error3:
 541     # print(stderr, "error: no metadata after calls; just use a comment (in '" line "')")
 542     # . write-buffered(Stderr, "error: no metadata after calls; just use a comment (in '")
 543     # . . push args
 544     68/push "error: no metadata after calls; just use a comment (in '"/imm32
 545     68/push Stderr/imm32
 546     # . . call
 547     e8/call write-buffered/disp32
 548     # . . discard args
 549     81 0/subop/add %esp 8/imm32
 550     # . write-stream-data(Stderr, line)
 551     # . . push args
 552     56/push-esi
 553     68/push Stderr/imm32
 554     # . . call
 555     e8/call write-stream-data/disp32
 556     # . . discard args
 557     81 0/subop/add %esp 8/imm32
 558     # . write-buffered(Stderr, "')")
 559     # . . push args
 560     68/push "')"/imm32
 561     68/push Stderr/imm32
 562     # . . call
 563     e8/call write-buffered/disp32
 564     # . . discard args
 565     81 0/subop/add %esp 8/imm32
 566     # . flush(Stderr)
 567     # . . push args
 568     68/push Stderr/imm32
 569     # . . call
 570     e8/call flush/disp32
 571     # . . discard args
 572     81 0/subop/add %esp 4/imm32
 573     # . syscall(exit, 1)
 574     bb/copy-to-ebx 1/imm32
 575     b8/copy-to-eax 1/imm32/exit
 576     cd/syscall 0x80/imm8
 577     # never gets here
 578 
 579 $next-word-string-or-expression-without-metadata:error4:
 580     # print(stderr, "error: unexpected text after end of call in '" line "'")
 581     # . write-buffered(Stderr, "error: unexpected text after end of call in '")
 582     # . . push args
 583     68/push "error: unexpected text after end of call in '"/imm32
 584     68/push Stderr/imm32
 585     # . . call
 586     e8/call write-buffered/disp32
 587     # . . discard args
 588     81 0/subop/add %esp 8/imm32
 589     # . write-stream-data(Stderr, line)
 590     # . . push args
 591     56/push-esi
 592     68/push Stderr/imm32
 593     # . . call
 594     e8/call write-stream-data/disp32
 595     # . . discard args
 596     81 0/subop/add %esp 8/imm32
 597     # . write-buffered(Stderr, "'")
 598     # . . push args
 599     68/push "'"/imm32
 600     68/push Stderr/imm32
 601     # . . call
 602     e8/call write-buffered/disp32
 603     # . . discard args
 604     81 0/subop/add %esp 8/imm32
 605     # . flush(Stderr)
 606     # . . push args
 607     68/push Stderr/imm32
 608     # . . call
 609     e8/call flush/disp32
 610     # . . discard args
 611     81 0/subop/add %esp 4/imm32
 612     # . syscall(exit, 1)
 613     bb/copy-to-ebx 1/imm32
 614     b8/copy-to-eax 1/imm32/exit
 615     cd/syscall 0x80/imm8
 616     # never gets here
 617 
 618 $next-word-string-or-expression-without-metadata:error5:
 619     # print(stderr, "error: no metadata anywhere in calls (in '" line "')")
 620     # . write-buffered(Stderr, "error: no metadata anywhere in calls (in '")
 621     # . . push args
 622     68/push "error: no metadata anywhere in calls (in '"/imm32
 623     68/push Stderr/imm32
 624     # . . call
 625     e8/call write-buffered/disp32
 626     # . . discard args
 627     81 0/subop/add %esp 8/imm32
 628     # . write-stream-data(Stderr, line)
 629     # . . push args
 630     56/push-esi
 631     68/push Stderr/imm32
 632     # . . call
 633     e8/call write-stream-data/disp32
 634     # . . discard args
 635     81 0/subop/add %esp 8/imm32
 636     # . write-buffered(Stderr, "')")
 637     # . . push args
 638     68/push "')"/imm32
 639     68/push Stderr/imm32
 640     # . . call
 641     e8/call write-buffered/disp32
 642     # . . discard args
 643     81 0/subop/add %esp 8/imm32
 644     # . flush(Stderr)
 645     # . . push args
 646     68/push Stderr/imm32
 647     # . . call
 648     e8/call flush/disp32
 649     # . . discard args
 650     81 0/subop/add %esp 4/imm32
 651     # . syscall(exit, 1)
 652     bb/copy-to-ebx 1/imm32
 653     b8/copy-to-eax 1/imm32/exit
 654     cd/syscall 0x80/imm8
 655     # never gets here
 656 
 657 test-next-word-string-or-expression-without-metadata:
 658     # . prolog
 659     55/push-ebp
 660     89/<- %ebp 4/r32/esp
 661     # setup
 662     # . clear-stream(_test-input-stream)
 663     # . . push args
 664     68/push _test-input-stream/imm32
 665     # . . call
 666     e8/call clear-stream/disp32
 667     # . . discard args
 668     81 0/subop/add %esp 4/imm32
 669     # var slice/ecx = {0, 0}
 670     68/push 0/imm32/end
 671     68/push 0/imm32/start
 672     89/<- %ecx 4/r32/esp
 673     # write(_test-input-stream, "  ab")
 674     # . . push args
 675     68/push "  ab"/imm32
 676     68/push _test-input-stream/imm32
 677     # . . call
 678     e8/call write/disp32
 679     # . . discard args
 680     81 0/subop/add %esp 8/imm32
 681     # next-word-string-or-expression-without-metadata(_test-input-stream, slice)
 682     # . . push args
 683     51/push-ecx
 684     68/push _test-input-stream/imm32
 685     # . . call
 686     e8/call next-word-string-or-expression-without-metadata/disp32
 687     # . . discard args
 688     81 0/subop/add %esp 8/imm32
 689     # check-ints-equal(_test-input-stream->read, 4, msg)
 690     # . . push args
 691     68/push "F - test-next-word-string-or-expression-without-metadata/updates-stream-read-correctly"/imm32
 692     68/push 4/imm32
 693     b8/copy-to-eax _test-input-stream/imm32
 694     ff 6/subop/push *(eax+4)
 695     # . . call
 696     e8/call check-ints-equal/disp32
 697     # . . discard args
 698     81 0/subop/add %esp 0xc/imm32
 699     # check-ints-equal(slice->start - _test-input-stream->data, 2, msg)
 700     # . check-ints-equal(slice->start - _test-input-stream, 14, msg)
 701     # . . push args
 702     68/push "F - test-next-word-string-or-expression-without-metadata: start"/imm32
 703     68/push 0xe/imm32
 704     # . . push slice->start - _test-input-stream
 705     8b/-> *ecx 0/r32/eax
 706     81 5/subop/subtract %eax _test-input-stream/imm32
 707     50/push-eax
 708     # . . call
 709     e8/call check-ints-equal/disp32
 710     # . . discard args
 711     81 0/subop/add %esp 0xc/imm32
 712     # check-ints-equal(slice->end - _test-input-stream->data, 4, msg)
 713     # . check-ints-equal(slice->end - _test-input-stream, 16, msg)
 714     # . . push args
 715     68/push "F - test-next-word-string-or-expression-without-metadata: end"/imm32
 716     68/push 0x10/imm32
 717     # . . push slice->end - _test-input-stream
 718     8b/-> *(ecx+4) 0/r32/eax
 719     81 5/subop/subtract %eax _test-input-stream/imm32
 720     50/push-eax
 721     # . . call
 722     e8/call check-ints-equal/disp32
 723     # . . discard args
 724     81 0/subop/add %esp 0xc/imm32
 725     # . epilog
 726     89/<- %esp 5/r32/ebp
 727     5d/pop-to-ebp
 728     c3/return
 729 
 730 test-next-word-string-or-expression-without-metadata-returns-whole-comment:
 731     # . prolog
 732     55/push-ebp
 733     89/<- %ebp 4/r32/esp
 734     # setup
 735     # . clear-stream(_test-input-stream)
 736     # . . push args
 737     68/push _test-input-stream/imm32
 738     # . . call
 739     e8/call clear-stream/disp32
 740     # . . discard args
 741     81 0/subop/add %esp 4/imm32
 742     # var slice/ecx = {0, 0}
 743     68/push 0/imm32/end
 744     68/push 0/imm32/start
 745     89/<- %ecx 4/r32/esp
 746     # write(_test-input-stream, "  # a")
 747     # . . push args
 748     68/push "  # a"/imm32
 749     68/push _test-input-stream/imm32
 750     # . . call
 751     e8/call write/disp32
 752     # . . discard args
 753     81 0/subop/add %esp 8/imm32
 754     # next-word-string-or-expression-without-metadata(_test-input-stream, slice)
 755     # . . push args
 756     51/push-ecx
 757     68/push _test-input-stream/imm32
 758     # . . call
 759     e8/call next-word-string-or-expression-without-metadata/disp32
 760     # . . discard args
 761     81 0/subop/add %esp 8/imm32
 762     # check-ints-equal(_test-input-stream->read, 5, msg)
 763     # . . push args
 764     68/push "F - test-next-word-string-or-expression-without-metadata-returns-whole-comment/updates-stream-read-correctly"/imm32
 765     68/push 5/imm32
 766     b8/copy-to-eax _test-input-stream/imm32
 767     ff 6/subop/push *(eax+4)
 768     # . . call
 769     e8/call check-ints-equal/disp32
 770     # . . discard args
 771     81 0/subop/add %esp 0xc/imm32
 772     # check-ints-equal(slice->start - _test-input-stream->data, 2, msg)
 773     # . check-ints-equal(slice->start - _test-input-stream, 14, msg)
 774     # . . push args
 775     68/push "F - test-next-word-string-or-expression-without-metadata-returns-whole-comment: start"/imm32
 776     68/push 0xe/imm32
 777     # . . push slice->start - _test-input-stream
 778     8b/-> *ecx 0/r32/eax
 779     81 5/subop/subtract %eax _test-input-stream/imm32
 780     50/push-eax
 781     # . . call
 782     e8/call check-ints-equal/disp32
 783     # . . discard args
 784     81 0/subop/add %esp 0xc/imm32
 785     # check-ints-equal(slice->end - _test-input-stream->data, 5, msg)
 786     # . check-ints-equal(slice->end - _test-input-stream, 17, msg)
 787     # . . push args
 788     68/push "F - test-next-word-string-or-expression-without-metadata-returns-whole-comment: end"/imm32
 789     68/push 0x11/imm32
 790     # . . push slice->end - _test-input-stream
 791     8b/-> *(ecx+4) 0/r32/eax
 792     81 5/subop/subtract %eax _test-input-stream/imm32
 793     50/push-eax
 794     # . . call
 795     e8/call check-ints-equal/disp32
 796     # . . discard args
 797     81 0/subop/add %esp 0xc/imm32
 798     # . epilog
 799     89/<- %esp 5/r32/ebp
 800     5d/pop-to-ebp
 801     c3/return
 802 
 803 test-next-word-string-or-expression-without-metadata-returns-empty-slice-on-eof:
 804     # . prolog
 805     55/push-ebp
 806     89/<- %ebp 4/r32/esp
 807     # setup
 808     # . clear-stream(_test-input-stream)
 809     # . . push args
 810     68/push _test-input-stream/imm32
 811     # . . call
 812     e8/call clear-stream/disp32
 813     # . . discard args
 814     81 0/subop/add %esp 4/imm32
 815     # var slice/ecx = {0, 0}
 816     68/push 0/imm32/end
 817     68/push 0/imm32/start
 818     89/<- %ecx 4/r32/esp
 819     # write nothing to _test-input-stream
 820     # next-word-string-or-expression-without-metadata(_test-input-stream, slice)
 821     # . . push args
 822     51/push-ecx
 823     68/push _test-input-stream/imm32
 824     # . . call
 825     e8/call next-word-string-or-expression-without-metadata/disp32
 826     # . . discard args
 827     81 0/subop/add %esp 8/imm32
 828     # check-ints-equal(slice->end - slice->start, 0, msg)
 829     # . . push args
 830     68/push "F - test-next-word-string-or-expression-without-metadata-returns-empty-expression-on-eof"/imm32
 831     68/push 0/imm32
 832     # . . push slice->end - slice->start
 833     8b/-> *(ecx+4) 0/r32/eax
 834     2b/subtract-> *ecx 0/r32/eax  # subtract *ecx from eax
 835     50/push-eax
 836     # . . call
 837     e8/call check-ints-equal/disp32
 838     # . . discard args
 839     81 0/subop/add %esp 0xc/imm32
 840     # . epilog
 841     89/<- %esp 5/r32/ebp
 842     5d/pop-to-ebp
 843     c3/return
 844 
 845 test-next-word-string-or-expression-without-metadata-returns-string-literal:
 846     # . prolog
 847     55/push-ebp
 848     89/<- %ebp 4/r32/esp
 849     # setup
 850     # . clear-stream(_test-input-stream)
 851     # . . push args
 852     68/push _test-input-stream/imm32
 853     # . . call
 854     e8/call clear-stream/disp32
 855     # . . discard args
 856     81 0/subop/add %esp 4/imm32
 857     # var slice/ecx = {0, 0}
 858     68/push 0/imm32/end
 859     68/push 0/imm32/start
 860     89/<- %ecx 4/r32/esp
 861     # write(_test-input-stream, " \"a b\" ")
 862     # . . push args
 863     68/push " \"a b\" "/imm32
 864     68/push _test-input-stream/imm32
 865     # . . call
 866     e8/call write/disp32
 867     # . . discard args
 868     81 0/subop/add %esp 8/imm32
 869     # next-word-string-or-expression-without-metadata(_test-input-stream, slice)
 870     # . . push args
 871     51/push-ecx
 872     68/push _test-input-stream/imm32
 873     # . . call
 874     e8/call next-word-string-or-expression-without-metadata/disp32
 875     # . . discard args
 876     81 0/subop/add %esp 8/imm32
 877     # check-ints-equal(slice->start - _test-input-stream->data, 1, msg)
 878     # . check-ints-equal(slice->start - _test-input-stream, 13, msg)
 879     # . . push args
 880     68/push "F - test-next-word-string-or-expression-without-metadata-returns-string-literal: start"/imm32
 881     68/push 0xd/imm32
 882     # . . push slice->start - _test-input-stream
 883     8b/-> *ecx 0/r32/eax
 884     81 5/subop/subtract %eax _test-input-stream/imm32
 885     50/push-eax
 886     # . . call
 887     e8/call check-ints-equal/disp32
 888     # . . discard args
 889     81 0/subop/add %esp 0xc/imm32
 890     # check-ints-equal(slice->end - _test-input-stream->data, 6, msg)
 891     # . check-ints-equal(slice->end - _test-input-stream, 18, msg)
 892     # . . push args
 893     68/push "F - test-next-word-string-or-expression-without-metadata-returns-string-literal: end"/imm32
 894     68/push 0x12/imm32
 895     # . . push slice->end - _test-input-stream
 896     8b/-> *(ecx+4) 0/r32/eax
 897     81 5/subop/subtract %eax _test-input-stream/imm32
 898     50/push-eax
 899     # . . call
 900     e8/call check-ints-equal/disp32
 901     # . . discard args
 902     81 0/subop/add %esp 0xc/imm32
 903     # . epilog
 904     89/<- %esp 5/r32/ebp
 905     5d/pop-to-ebp
 906     c3/return
 907 
 908 test-next-word-string-or-expression-without-metadata-returns-string-with-escapes:
 909     # . prolog
 910     55/push-ebp
 911     89/<- %ebp 4/r32/esp
 912     # setup
 913     # . clear-stream(_test-input-stream)
 914     # . . push args
 915     68/push _test-input-stream/imm32
 916     # . . call
 917     e8/call clear-stream/disp32
 918     # . . discard args
 919     81 0/subop/add %esp 4/imm32
 920     # var slice/ecx = {0, 0}
 921     68/push 0/imm32/end
 922     68/push 0/imm32/start
 923     89/<- %ecx 4/r32/esp
 924     # write(_test-input-stream, " \"a\\\"b\"")
 925     # . . push args
 926     68/push " \"a\\\"b\""/imm32
 927     68/push _test-input-stream/imm32
 928     # . . call
 929     e8/call write/disp32
 930     # . . discard args
 931     81 0/subop/add %esp 8/imm32
 932     # next-word-string-or-expression-without-metadata(_test-input-stream, slice)
 933     # . . push args
 934     51/push-ecx
 935     68/push _test-input-stream/imm32
 936     # . . call
 937     e8/call next-word-string-or-expression-without-metadata/disp32
 938     # . . discard args
 939     81 0/subop/add %esp 8/imm32
 940     # check-ints-equal(slice->start - _test-input-stream->data, 1, msg)
 941     # . check-ints-equal(slice->start - _test-input-stream, 13, msg)
 942     # . . push args
 943     68/push "F - test-next-word-string-or-expression-without-metadata-returns-string-with-escapes: start"/imm32
 944     68/push 0xd/imm32
 945     # . . push slice->start - _test-input-stream
 946     8b/-> *ecx 0/r32/eax
 947     81 5/subop/subtract %eax _test-input-stream/imm32
 948     50/push-eax
 949     # . . call
 950     e8/call check-ints-equal/disp32
 951     # . . discard args
 952     81 0/subop/add %esp 0xc/imm32
 953     # check-ints-equal(slice->end - _test-input-stream->data, 7, msg)
 954     # . check-ints-equal(slice->end - _test-input-stream, 19, msg)
 955     # . . push args
 956     68/push "F - test-next-word-string-or-expression-without-metadata-returns-string-with-escapes: end"/imm32
 957     68/push 0x13/imm32
 958     # . . push slice->end - _test-input-stream
 959     8b/-> *(ecx+4) 0/r32/eax
 960     81 5/subop/subtract %eax _test-input-stream/imm32
 961     50/push-eax
 962     # . . call
 963     e8/call check-ints-equal/disp32
 964     # . . discard args
 965     81 0/subop/add %esp 0xc/imm32
 966     # . epilog
 967     89/<- %esp 5/r32/ebp
 968     5d/pop-to-ebp
 969     c3/return
 970 
 971 test-next-word-string-or-expression-without-metadata-returns-whole-expression:
 972     # . prolog
 973     55/push-ebp
 974     89/<- %ebp 4/r32/esp
 975     # setup
 976     # . clear-stream(_test-input-stream)
 977     # . . push args
 978     68/push _test-input-stream/imm32
 979     # . . call
 980     e8/call clear-stream/disp32
 981     # . . discard args
 982     81 0/subop/add %esp 4/imm32
 983     # var slice/ecx = {0, 0}
 984     68/push 0/imm32/end
 985     68/push 0/imm32/start
 986     89/<- %ecx 4/r32/esp
 987     # write(_test-input-stream, " *(a b) ")
 988     # . . push args
 989     68/push " *(a b) "/imm32
 990     68/push _test-input-stream/imm32
 991     # . . call
 992     e8/call write/disp32
 993     # . . discard args
 994     81 0/subop/add %esp 8/imm32
 995     # next-word-string-or-expression-without-metadata(_test-input-stream, slice)
 996     # . . push args
 997     51/push-ecx
 998     68/push _test-input-stream/imm32
 999     # . . call
1000     e8/call next-word-string-or-expression-without-metadata/disp32
1001     # . . discard args
1002     81 0/subop/add %esp 8/imm32
1003     # check-ints-equal(slice->start - _test-input-stream->data, 1, msg)
1004     # . check-ints-equal(slice->start - _test-input-stream, 13, msg)
1005     # . . push args
1006     68/push "F - test-next-word-string-or-expression-without-metadata-returns-whole-expression: start"/imm32
1007     68/push 0xd/imm32
1008     # . . push slice->start - _test-input-stream
1009     8b/-> *ecx 0/r32/eax
1010     81 5/subop/subtract %eax _test-input-stream/imm32
1011     50/push-eax
1012     # . . call
1013     e8/call check-ints-equal/disp32
1014     # . . discard args
1015     81 0/subop/add %esp 0xc/imm32
1016     # check-ints-equal(slice->end - _test-input-stream->data, 7, msg)
1017     # . check-ints-equal(slice->end - _test-input-stream, 19, msg)
1018     # . . push args
1019     68/push "F - test-next-word-string-or-expression-without-metadata-returns-whole-expression: end"/imm32
1020     68/push 0x13/imm32
1021     # . . push slice->end - _test-input-stream
1022     8b/-> *(ecx+4) 0/r32/eax
1023     81 5/subop/subtract %eax _test-input-stream/imm32
1024     50/push-eax
1025     # . . call
1026     e8/call check-ints-equal/disp32
1027     # . . discard args
1028     81 0/subop/add %esp 0xc/imm32
1029     # . epilog
1030     89/<- %esp 5/r32/ebp
1031     5d/pop-to-ebp
1032     c3/return
1033 
1034 test-next-word-string-or-expression-without-metadata-returns-eol-on-trailing-close-paren:
1035     # . prolog
1036     55/push-ebp
1037     89/<- %ebp 4/r32/esp
1038     # setup
1039     # . clear-stream(_test-input-stream)
1040     # . . push args
1041     68/push _test-input-stream/imm32
1042     # . . call
1043     e8/call clear-stream/disp32
1044     # . . discard args
1045     81 0/subop/add %esp 4/imm32
1046     # var slice/ecx = {0, 0}
1047     68/push 0/imm32/end
1048     68/push 0/imm32/start
1049     89/<- %ecx 4/r32/esp
1050     # write(_test-input-stream, " ) ")
1051     # . . push args
1052     68/push " ) "/imm32
1053     68/push _test-input-stream/imm32
1054     # . . call
1055     e8/call write/disp32
1056     # . . discard args
1057     81 0/subop/add %esp 8/imm32
1058     # next-word-string-or-expression-without-metadata(_test-input-stream, slice)
1059     # . . push args
1060     51/push-ecx
1061     68/push _test-input-stream/imm32
1062     # . . call
1063     e8/call next-word-string-or-expression-without-metadata/disp32
1064     # . . discard args
1065     81 0/subop/add %esp 8/imm32
1066     # check-ints-equal(slice->start, 0, msg)
1067     # . . push args
1068     68/push "F - test-next-word-string-or-expression-without-metadata-returns-eol-on-trailing-close-paren: start"/imm32
1069     68/push 0/imm32
1070     ff 6/subop/push *ecx
1071     # . . call
1072     e8/call check-ints-equal/disp32
1073     # . . discard args
1074     81 0/subop/add %esp 0xc/imm32
1075     # check-ints-equal(slice->end, 0, msg)
1076     # . . push args
1077     68/push "F - test-next-word-string-or-expression-without-metadata-returns-eol-on-trailing-close-paren: end"/imm32
1078     68/push 0/imm32
1079     ff 6/subop/push *(ecx+4)
1080     # . . call
1081     e8/call check-ints-equal/disp32
1082     # . . discard args
1083     81 0/subop/add %esp 0xc/imm32
1084     # . epilog
1085     89/<- %esp 5/r32/ebp
1086     5d/pop-to-ebp
1087     c3/return
1088 
1089 test-next-word-string-or-expression-without-metadata-handles-comment-after-trailing-close-paren:
1090     # . prolog
1091     55/push-ebp
1092     89/<- %ebp 4/r32/esp
1093     # setup
1094     # . clear-stream(_test-input-stream)
1095     # . . push args
1096     68/push _test-input-stream/imm32
1097     # . . call
1098     e8/call clear-stream/disp32
1099     # . . discard args
1100     81 0/subop/add %esp 4/imm32
1101     # var slice/ecx = {0, 0}
1102     68/push 0/imm32/end
1103     68/push 0/imm32/start
1104     89/<- %ecx 4/r32/esp
1105     # write(_test-input-stream, " ) # abc ")
1106     # . . push args
1107     68/push " ) # abc "/imm32
1108     68/push _test-input-stream/imm32
1109     # . . call
1110     e8/call write/disp32
1111     # . . discard args
1112     81 0/subop/add %esp 8/imm32
1113     # next-word-string-or-expression-without-metadata(_test-input-stream, slice)
1114     # . . push args
1115     51/push-ecx
1116     68/push _test-input-stream/imm32
1117     # . . call
1118     e8/call next-word-string-or-expression-without-metadata/disp32
1119     # . . discard args
1120     81 0/subop/add %esp 8/imm32
1121     # check-ints-equal(slice->start, 0, msg)
1122     # . . push args
1123     68/push "F - test-next-word-string-or-expression-without-metadata-returns-eol-on-trailing-close-paren: start"/imm32
1124     68/push 0/imm32
1125     ff 6/subop/push *ecx
1126     # . . call
1127     e8/call check-ints-equal/disp32
1128     # . . discard args
1129     81 0/subop/add %esp 0xc/imm32
1130     # check-ints-equal(slice->end, 0, msg)
1131     # . . push args
1132     68/push "F - test-next-word-string-or-expression-without-metadata-returns-eol-on-trailing-close-paren: end"/imm32
1133     68/push 0/imm32
1134     ff 6/subop/push *(ecx+4)
1135     # . . call
1136     e8/call check-ints-equal/disp32
1137     # . . discard args
1138     81 0/subop/add %esp 0xc/imm32
1139     # . epilog
1140     89/<- %esp 5/r32/ebp
1141     5d/pop-to-ebp
1142     c3/return
1143 
1144 test-next-word-string-or-expression-without-metadata-stops-at-close-paren:
1145     # . prolog
1146     55/push-ebp
1147     89/<- %ebp 4/r32/esp
1148     # setup
1149     # . clear-stream(_test-input-stream)
1150     # . . push args
1151     68/push _test-input-stream/imm32
1152     # . . call
1153     e8/call clear-stream/disp32
1154     # . . discard args
1155     81 0/subop/add %esp 4/imm32
1156     # var slice/ecx = {0, 0}
1157     68/push 0/imm32/end
1158     68/push 0/imm32/start
1159     89/<- %ecx 4/r32/esp
1160     # write(_test-input-stream, " abc) # def")
1161     # . . push args
1162     68/push " abc) # def"/imm32
1163     68/push _test-input-stream/imm32
1164     # . . call
1165     e8/call write/disp32
1166     # . . discard args
1167     81 0/subop/add %esp 8/imm32
1168     # next-word-string-or-expression-without-metadata(_test-input-stream, slice)
1169     # . . push args
1170     51/push-ecx
1171     68/push _test-input-stream/imm32
1172     # . . call
1173     e8/call next-word-string-or-expression-without-metadata/disp32
1174     # . . discard args
1175     81 0/subop/add %esp 8/imm32
1176     # check-ints-equal(slice->start - _test-input-stream->data, 1, msg)
1177     # . check-ints-equal(slice->start - _test-input-stream, 13, msg)
1178     # . . push args
1179     68/push "F - test-next-word-string-or-expression-without-metadata-stops-at-close-paren: start"/imm32
1180     68/push 0xd/imm32
1181     # . . push slice->start - _test-input-stream
1182     8b/-> *ecx 0/r32/eax
1183     81 5/subop/subtract %eax _test-input-stream/imm32
1184     50/push-eax
1185     # . . call
1186     e8/call check-ints-equal/disp32
1187     # . . discard args
1188     81 0/subop/add %esp 0xc/imm32
1189     # check-ints-equal(slice->end - _test-input-stream->data, 4, msg)
1190     # . check-ints-equal(slice->end - _test-input-stream, 16, msg)
1191     # . . push args
1192     68/push "F - test-next-word-string-or-expression-without-metadata-stops-at-close-paren: end"/imm32
1193     68/push 0x10/imm32
1194     # . . push slice->end - _test-input-stream
1195     8b/-> *(ecx+4) 0/r32/eax
1196     81 5/subop/subtract %eax _test-input-stream/imm32
1197     50/push-eax
1198     # . . call
1199     e8/call check-ints-equal/disp32
1200     # . . discard args
1201     81 0/subop/add %esp 0xc/imm32
1202     # . epilog
1203     89/<- %esp 5/r32/ebp
1204     5d/pop-to-ebp
1205     c3/return
1206 
1207 # . . vim:nowrap:textwidth=0