https://github.com/akkartik/mu/blob/main/124next-token.subx

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