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