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