https://github.com/akkartik/mu/blob/master/073next-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 : (address stream), delimiter : byte, out : (address 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 = {0, 0}
  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 = {0, 0}
 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 : (address byte), end : (address byte), delimiter : byte, out : (address 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 : (address slice) = {0, 0}
 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 : (address slice) = {0, 0}
 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 : (address slice) = {0, 0}
 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 : (address stream), 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-greater-or-equal  $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-not-equal  $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 : (address stream)
 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-greater-or-equal  $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-equal  $skip-chars-matching-whitespace:body/disp8
 489     # if (eax == '\n') goto body
 490     3d/compare-eax-and  0x0a/imm32/newline
 491     74/jump-if-equal  $skip-chars-matching-whitespace:body/disp8
 492     # if (eax == '\t') goto body
 493     3d/compare-eax-and  0x09/imm32/tab
 494     74/jump-if-equal  $skip-chars-matching-whitespace:body/disp8
 495     # if (eax != '\r') break
 496     3d/compare-eax-and  0x0d/imm32/cr
 497     75/jump-if-not-equal  $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 : (address stream), 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-greater-or-equal  $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-equal  $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 : (address stream)
 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-greater-or-equal  $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-equal  $skip-chars-not-matching-whitespace:end/disp8
 741     # if (eax == '\n') break
 742     3d/compare-eax-and  0x0a/imm32/newline
 743     74/jump-if-equal  $skip-chars-not-matching-whitespace:end/disp8
 744     # if (eax == '\t') break
 745     3d/compare-eax-and  0x09/imm32/tab
 746     74/jump-if-equal  $skip-chars-not-matching-whitespace:end/disp8
 747     # if (eax == '\r') break
 748     3d/compare-eax-and  0x0d/imm32/cr
 749     74/jump-if-equal  $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 : (address byte), end : (address byte), delimiter : byte -> curr/eax
 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     # ebx = 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-greater-or-equal-unsigned  $skip-chars-matching-in-slice:end/disp8
 824     # if (*curr != delimiter) break
 825     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           3/r32/BL    .               .                 # copy byte at *eax to BL
 826     39/compare                      3/mod/direct    3/rm32/ebx    .           .             .           2/r32/edx   .               .                 # compare ebx and edx
 827     75/jump-if-not-equal  $skip-chars-matching-in-slice:end/disp8
 828     # ++curr
 829     40/increment-eax
 830     eb/jump  $skip-chars-matching-in-slice:loop/disp8
 831 $skip-chars-matching-in-slice:end:
 832     # . restore registers
 833     5b/pop-to-ebx
 834     5a/pop-to-edx
 835     59/pop-to-ecx
 836     # . epilogue
 837     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 838     5d/pop-to-ebp
 839     c3/return
 840 
 841 test-skip-chars-matching-in-slice:
 842     # (eax..ecx) = "  ab"
 843     b8/copy-to-eax  "  ab"/imm32
 844     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
 845     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
 846     05/add-to-eax  4/imm32
 847     # eax = skip-chars-matching-in-slice(eax, ecx, 0x20/space)
 848     # . . push args
 849     68/push  0x20/imm32/space
 850     51/push-ecx
 851     50/push-eax
 852     # . . call
 853     e8/call  skip-chars-matching-in-slice/disp32
 854     # . . discard args
 855     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 856     # check-ints-equal(ecx-eax, 2, msg)
 857     # . . push args
 858     68/push  "F - test-skip-chars-matching-in-slice"/imm32
 859     68/push  2/imm32
 860     # . . push ecx-eax
 861     29/subtract                     3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract eax from ecx
 862     51/push-ecx
 863     # . . call
 864     e8/call  check-ints-equal/disp32
 865     # . . discard args
 866     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 867     # end
 868     c3/return
 869 
 870 test-skip-chars-matching-in-slice-none:
 871     # (eax..ecx) = "ab"
 872     b8/copy-to-eax  "ab"/imm32
 873     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
 874     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
 875     05/add-to-eax  4/imm32
 876     # eax = skip-chars-matching-in-slice(eax, ecx, 0x20/space)
 877     # . . push args
 878     68/push  0x20/imm32/space
 879     51/push-ecx
 880     50/push-eax
 881     # . . call
 882     e8/call  skip-chars-matching-in-slice/disp32
 883     # . . discard args
 884     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 885     # check-ints-equal(ecx-eax, 2, msg)
 886     # . . push args
 887     68/push  "F - test-skip-chars-matching-in-slice-none"/imm32
 888     68/push  2/imm32
 889     # . . push ecx-eax
 890     29/subtract                     3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract eax from ecx
 891     51/push-ecx
 892     # . . call
 893     e8/call  check-ints-equal/disp32
 894     # . . discard args
 895     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 896     # end
 897     c3/return
 898 
 899 skip-chars-matching-whitespace-in-slice:  # in : (address stream)
 900     # . prologue
 901     55/push-ebp
 902     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 903     # . save registers
 904     51/push-ecx
 905     53/push-ebx
 906     # eax = curr
 907     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   8/disp8         .                 # copy *(ebp+8) to eax
 908     # ecx = end
 909     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   0xc/disp8       .                 # copy *(ebp+12) to ecx
 910     # ebx = 0
 911     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
 912 $skip-chars-matching-whitespace-in-slice:loop:
 913     # if (curr >= end) break
 914     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # compare eax with ecx
 915     0f 83/jump-if-greater-or-equal-unsigned  $skip-chars-matching-in-slice:end/disp32
 916     # ebx = *curr
 917     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           3/r32/BL    .               .                 # copy byte at *eax to BL
 918     # if (*curr == ' ') goto body
 919     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x20/imm32/space  # compare ebx
 920     74/jump-if-equal  $skip-chars-matching-whitespace-in-slice:body/disp8
 921     # if (*curr == '\n') goto body
 922     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x0a/imm32/newline  # compare ebx
 923     74/jump-if-equal  $skip-chars-matching-whitespace-in-slice:body/disp8
 924     # if (*curr == '\t') goto body
 925     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x09/imm32/tab    # compare ebx
 926     74/jump-if-equal  $skip-chars-matching-whitespace-in-slice:body/disp8
 927     # if (*curr != '\r') break
 928     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x0d/imm32/cr     # compare ebx
 929     75/jump-if-not-equal  $skip-chars-matching-whitespace-in-slice:end/disp8
 930 $skip-chars-matching-whitespace-in-slice:body:
 931     # ++curr
 932     40/increment-eax
 933     eb/jump  $skip-chars-matching-whitespace-in-slice:loop/disp8
 934 $skip-chars-matching-whitespace-in-slice:end:
 935     # . restore registers
 936     5b/pop-to-ebx
 937     59/pop-to-ecx
 938     # . epilogue
 939     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 940     5d/pop-to-ebp
 941     c3/return
 942 
 943 test-skip-chars-matching-whitespace-in-slice:
 944     # (eax..ecx) = " \nab"
 945     b8/copy-to-eax  " \nab"/imm32
 946     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
 947     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
 948     05/add-to-eax  4/imm32
 949     # eax = skip-chars-matching-whitespace-in-slice(eax, ecx)
 950     # . . push args
 951     51/push-ecx
 952     50/push-eax
 953     # . . call
 954     e8/call  skip-chars-matching-whitespace-in-slice/disp32
 955     # . . discard args
 956     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 957     # check-ints-equal(ecx-eax, 2, msg)
 958     # . . push args
 959     68/push  "F - test-skip-chars-matching-whitespace-in-slice"/imm32
 960     68/push  2/imm32
 961     # . . push ecx-eax
 962     29/subtract                     3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract eax from ecx
 963     51/push-ecx
 964     # . . call
 965     e8/call  check-ints-equal/disp32
 966     # . . discard args
 967     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 968     # end
 969     c3/return
 970 
 971 # minor fork of 'skip-chars-matching-in-slice'
 972 skip-chars-not-matching-in-slice:  # curr : (address byte), end : (address byte), delimiter : byte -> curr/eax
 973     # . prologue
 974     55/push-ebp
 975     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 976     # . save registers
 977     51/push-ecx
 978     52/push-edx
 979     53/push-ebx
 980     # eax = curr
 981     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   8/disp8         .                 # copy *(ebp+8) to eax
 982     # ecx = end
 983     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   0xc/disp8       .                 # copy *(ebp+12) to ecx
 984     # edx = delimiter
 985     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           2/r32/edx   0x10/disp8       .                 # copy *(ebp+16) to edx
 986     # ebx = 0
 987     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
 988 $skip-chars-not-matching-in-slice:loop:
 989     # if (curr >= end) break
 990     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # compare eax with ecx
 991     73/jump-if-greater-or-equal-unsigned  $skip-chars-not-matching-in-slice:end/disp8
 992     # if (*curr == delimiter) break
 993     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           3/r32/BL    .               .                 # copy byte at *eax to BL
 994     39/compare                      3/mod/direct    3/rm32/ebx    .           .             .           2/r32/edx   .               .                 # compare ebx and edx
 995     74/jump-if-equal  $skip-chars-not-matching-in-slice:end/disp8
 996     # ++curr
 997     40/increment-eax
 998     eb/jump  $skip-chars-not-matching-in-slice:loop/disp8
 999 $skip-chars-not-matching-in-slice:end:
1000     # . restore registers
1001     5b/pop-to-ebx
1002     5a/pop-to-edx
1003     59/pop-to-ecx
1004     # . epilogue
1005     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1006     5d/pop-to-ebp
1007     c3/return
1008 
1009 test-skip-chars-not-matching-in-slice:
1010     # (eax..ecx) = "ab "
1011     b8/copy-to-eax  "ab "/imm32
1012     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1013     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
1014     05/add-to-eax  4/imm32
1015     # eax = skip-chars-not-matching-in-slice(eax, ecx, 0x20/space)
1016     # . . push args
1017     68/push  0x20/imm32/space
1018     51/push-ecx
1019     50/push-eax
1020     # . . call
1021     e8/call  skip-chars-not-matching-in-slice/disp32
1022     # . . discard args
1023     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1024     # check-ints-equal(ecx-eax, 1, msg)
1025     # . . push args
1026     68/push  "F - test-skip-chars-not-matching-in-slice"/imm32
1027     68/push  1/imm32
1028     # . . push ecx-eax
1029     29/subtract                     3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract eax from ecx
1030     51/push-ecx
1031     # . . call
1032     e8/call  check-ints-equal/disp32
1033     # . . discard args
1034     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1035     # end
1036     c3/return
1037 
1038 test-skip-chars-not-matching-in-slice-none:
1039     # (eax..ecx) = " ab"
1040     b8/copy-to-eax  " ab"/imm32
1041     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1042     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
1043     05/add-to-eax  4/imm32
1044     # eax = skip-chars-not-matching-in-slice(eax, ecx, 0x20/space)
1045     # . . push args
1046     68/push  0x20/imm32/space
1047     51/push-ecx
1048     50/push-eax
1049     # . . call
1050     e8/call  skip-chars-not-matching-in-slice/disp32
1051     # . . discard args
1052     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1053     # check-ints-equal(ecx-eax, 3, msg)
1054     # . . push args
1055     68/push  "F - test-skip-chars-not-matching-in-slice-none"/imm32
1056     68/push  3/imm32
1057     # . . push ecx-eax
1058     29/subtract                     3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract eax from ecx
1059     51/push-ecx
1060     # . . call
1061     e8/call  check-ints-equal/disp32
1062     # . . discard args
1063     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1064     # end
1065     c3/return
1066 
1067 test-skip-chars-not-matching-in-slice-all:
1068     # (eax..ecx) = "ab"
1069     b8/copy-to-eax  "ab"/imm32
1070     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1071     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
1072     05/add-to-eax  4/imm32
1073     # eax = skip-chars-not-matching-in-slice(eax, ecx, 0x20/space)
1074     # . . push args
1075     68/push  0x20/imm32/space
1076     51/push-ecx
1077     50/push-eax
1078     # . . call
1079     e8/call  skip-chars-not-matching-in-slice/disp32
1080     # . . discard args
1081     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1082     # check-ints-equal(ecx-eax, 0, msg)
1083     # . . push args
1084     68/push  "F - test-skip-chars-not-matching-in-slice-all"/imm32
1085     68/push  0/imm32
1086     # . . push ecx-eax
1087     29/subtract                     3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract eax from ecx
1088     51/push-ecx
1089     # . . call
1090     e8/call  check-ints-equal/disp32
1091     # . . discard args
1092     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1093     # end
1094     c3/return
1095 
1096 skip-chars-not-matching-whitespace-in-slice:  # in : (address stream)
1097     # . prologue
1098     55/push-ebp
1099     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1100     # . save registers
1101     51/push-ecx
1102     53/push-ebx
1103     # eax = curr
1104     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   8/disp8         .                 # copy *(ebp+8) to eax
1105     # ecx = end
1106     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   0xc/disp8       .                 # copy *(ebp+12) to ecx
1107     # ebx = 0
1108     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
1109 $skip-chars-not-matching-whitespace-in-slice:loop:
1110     # if (curr >= end) break
1111     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # compare eax with ecx
1112     0f 83/jump-if-greater-or-equal-unsigned  $skip-chars-not-matching-in-slice:end/disp32
1113     # ebx = *curr
1114     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           3/r32/BL    .               .                 # copy byte at *eax to BL
1115     # if (*curr == ' ') break
1116     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x20/imm32/space  # compare ebx
1117     74/jump-if-equal  $skip-chars-not-matching-whitespace-in-slice:end/disp8
1118     # if (*curr == '\n') break
1119     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x0a/imm32/newline  # compare ebx
1120     74/jump-if-equal  $skip-chars-not-matching-whitespace-in-slice:end/disp8
1121     # if (*curr == '\t') break
1122     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x09/imm32/tab    # compare ebx
1123     74/jump-if-equal  $skip-chars-not-matching-whitespace-in-slice:end/disp8
1124     # if (*curr == '\r') break
1125     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x0d/imm32/cr     # compare ebx
1126     74/jump-if-equal  $skip-chars-not-matching-whitespace-in-slice:end/disp8
1127     # ++curr
1128     40/increment-eax
1129     eb/jump  $skip-chars-not-matching-whitespace-in-slice:loop/disp8
1130 $skip-chars-not-matching-whitespace-in-slice:end:
1131     # . restore registers
1132     5b/pop-to-ebx
1133     59/pop-to-ecx
1134     # . epilogue
1135     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1136     5d/pop-to-ebp
1137     c3/return
1138 
1139 test-skip-chars-not-matching-whitespace-in-slice:
1140     # (eax..ecx) = "ab\n"
1141     b8/copy-to-eax  "ab\n"/imm32
1142     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1143     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
1144     05/add-to-eax  4/imm32
1145     # eax = skip-chars-not-matching-whitespace-in-slice(eax, ecx)
1146     # . . push args
1147     51/push-ecx
1148     50/push-eax
1149     # . . call
1150     e8/call  skip-chars-not-matching-whitespace-in-slice/disp32
1151     # . . discard args
1152     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1153     # check-ints-equal(ecx-eax, 1, msg)
1154     # . . push args
1155     68/push  "F - test-skip-chars-not-matching-whitespace-in-slice"/imm32
1156     68/push  1/imm32
1157     # . . push ecx-eax
1158     29/subtract                     3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract eax from ecx
1159     51/push-ecx
1160     # . . call
1161     e8/call  check-ints-equal/disp32
1162     # . . discard args
1163     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1164     # end
1165     c3/return
1166 
1167 # update line->read to end of string literal surrounded by double quotes
1168 # line->read must start out at a double-quote
1169 skip-string:  # line : (address stream)
1170     # . prologue
1171     55/push-ebp
1172     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1173     # . save registers
1174     50/push-eax
1175     51/push-ecx
1176     52/push-edx
1177     # ecx = line
1178     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
1179     # eax = skip-string-in-slice(&line->data[line->read], &line->data[line->write])
1180     # . . push &line->data[line->write]
1181     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .                         2/r32/edx   8/disp8         .                 # copy *(ecx+8) to edx
1182     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
1183     52/push-edx
1184     # . . push &line->data[line->read]
1185     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .                         2/r32/edx   4/disp8         .                 # copy *(ecx+4) to edx
1186     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
1187     52/push-edx
1188     # . . call
1189     e8/call  skip-string-in-slice/disp32
1190     # . . discard args
1191     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1192     # line->read = eax - line->data
1193     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # subtract ecx from eax
1194     2d/subtract-from-eax  0xc/imm32
1195     89/copy                         1/mod/*+disp8   1/rm32/ecx    .           .                         0/r32/eax   4/disp8         .                 # copy eax to *(ecx+4)
1196 $skip-string:end:
1197     # . restore registers
1198     5a/pop-to-edx
1199     59/pop-to-ecx
1200     58/pop-to-eax
1201     # . epilogue
1202     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1203     5d/pop-to-ebp
1204     c3/return
1205 
1206 test-skip-string:
1207     # . prologue
1208     55/push-ebp
1209     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1210     # setup
1211     # . clear-stream(_test-input-stream)
1212     # . . push args
1213     68/push  _test-input-stream/imm32
1214     # . . call
1215     e8/call  clear-stream/disp32
1216     # . . discard args
1217     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1218     # . write(_test-input-stream, "\"abc\" def")
1219     # .                   indices:  0123 45
1220     # . . push args
1221     68/push  "\"abc\" def"/imm32
1222     68/push  _test-input-stream/imm32
1223     # . . call
1224     e8/call  write/disp32
1225     # . . discard args
1226     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1227     # precondition: line->read == 0
1228     # . . push args
1229     68/push  "F - test-skip-string/precondition"/imm32
1230     68/push  0/imm32
1231     b8/copy-to-eax  _test-input-stream/imm32
1232     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1233     # . . call
1234     e8/call  check-ints-equal/disp32
1235     # . . discard args
1236     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1237     # skip-string(_test-input-stream)
1238     # . . push args
1239     68/push  _test-input-stream/imm32
1240     # . . call
1241     e8/call  skip-string/disp32
1242     # . . discard args
1243     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1244     # check-ints-equal(line->read, 5, msg)
1245     # . . push args
1246     68/push  "F - test-skip-string"/imm32
1247     68/push  5/imm32
1248     b8/copy-to-eax  _test-input-stream/imm32
1249     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1250     # . . call
1251     e8/call  check-ints-equal/disp32
1252     # . . discard args
1253     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1254     # . epilogue
1255     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1256     5d/pop-to-ebp
1257     c3/return
1258 
1259 test-skip-string-ignores-spaces:
1260     # . prologue
1261     55/push-ebp
1262     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1263     # setup
1264     # . clear-stream(_test-input-stream)
1265     # . . push args
1266     68/push  _test-input-stream/imm32
1267     # . . call
1268     e8/call  clear-stream/disp32
1269     # . . discard args
1270     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1271     # . write(_test-input-stream, "\"a b\"/yz")
1272     # .                   indices:  0123 45
1273     # . . push args
1274     68/push  "\"a b\"/yz"/imm32
1275     68/push  _test-input-stream/imm32
1276     # . . call
1277     e8/call  write/disp32
1278     # . . discard args
1279     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1280     # precondition: line->read == 0
1281     # . . push args
1282     68/push  "F - test-skip-string-ignores-spaces/precondition"/imm32
1283     68/push  0/imm32
1284     b8/copy-to-eax  _test-input-stream/imm32
1285     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1286     # . . call
1287     e8/call  check-ints-equal/disp32
1288     # . . discard args
1289     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1290     # skip-string(_test-input-stream)
1291     # . . push args
1292     68/push  _test-input-stream/imm32
1293     # . . call
1294     e8/call  skip-string/disp32
1295     # . . discard args
1296     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1297     # check-ints-equal(line->read, 5, msg)
1298     # . . push args
1299     68/push  "F - test-skip-string-ignores-spaces"/imm32
1300     68/push  5/imm32
1301     b8/copy-to-eax  _test-input-stream/imm32
1302     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1303     # . . call
1304     e8/call  check-ints-equal/disp32
1305     # . . discard args
1306     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1307     # . epilogue
1308     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1309     5d/pop-to-ebp
1310     c3/return
1311 
1312 test-skip-string-ignores-escapes:
1313     # . prologue
1314     55/push-ebp
1315     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1316     # setup
1317     # . clear-stream(_test-input-stream)
1318     # . . push args
1319     68/push  _test-input-stream/imm32
1320     # . . call
1321     e8/call  clear-stream/disp32
1322     # . . discard args
1323     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1324     # . write(_test-input-stream, "\"a\\\"b\"/yz")
1325     # .                   indices:  01 2 34 56
1326     # . . push args
1327     68/push  "\"a\\\"b\"/yz"/imm32
1328     68/push  _test-input-stream/imm32
1329     # . . call
1330     e8/call  write/disp32
1331     # . . discard args
1332     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1333     # precondition: line->read == 0
1334     # . . push args
1335     68/push  "F - test-skip-string-ignores-escapes/precondition"/imm32
1336     68/push  0/imm32
1337     b8/copy-to-eax  _test-input-stream/imm32
1338     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1339     # . . call
1340     e8/call  check-ints-equal/disp32
1341     # . . discard args
1342     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1343     # skip-string(_test-input-stream)
1344     # . . push args
1345     68/push  _test-input-stream/imm32
1346     # . . call
1347     e8/call  skip-string/disp32
1348     # . . discard args
1349     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1350     # check-ints-equal(line->read, 6, msg)
1351     # . . push args
1352     68/push  "F - test-skip-string-ignores-escapes"/imm32
1353     68/push  6/imm32
1354     b8/copy-to-eax  _test-input-stream/imm32
1355     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1356     # . . call
1357     e8/call  check-ints-equal/disp32
1358     # . . discard args
1359     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1360     # . epilogue
1361     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1362     5d/pop-to-ebp
1363     c3/return
1364 
1365 test-skip-string-works-from-mid-stream:
1366     # . prologue
1367     55/push-ebp
1368     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1369     # setup
1370     # . clear-stream(_test-input-stream)
1371     # . . push args
1372     68/push  _test-input-stream/imm32
1373     # . . call
1374     e8/call  clear-stream/disp32
1375     # . . discard args
1376     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1377     # . write(_test-input-stream, "0 \"a\\\"b\"/yz")
1378     # .                   indices:  01 2 34 56
1379     # . . push args
1380     68/push  "0 \"a\\\"b\"/yz"/imm32
1381     68/push  _test-input-stream/imm32
1382     # . . call
1383     e8/call  write/disp32
1384     # . . discard args
1385     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1386     # precondition: line->read == 2
1387     b8/copy-to-eax  _test-input-stream/imm32
1388     c7          0/subop/copy        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         2/imm32           # copy to *(eax+4)
1389     # skip-string(_test-input-stream)
1390     # . . push args
1391     68/push  _test-input-stream/imm32
1392     # . . call
1393     e8/call  skip-string/disp32
1394     # . . discard args
1395     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1396     # check-ints-equal(line->read, 8, msg)
1397     # . . push args
1398     68/push  "F - test-skip-string-works-from-mid-stream"/imm32
1399     68/push  8/imm32
1400     b8/copy-to-eax  _test-input-stream/imm32
1401     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1402     # . . call
1403     e8/call  check-ints-equal/disp32
1404     # . . discard args
1405     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1406     # . epilogue
1407     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1408     5d/pop-to-ebp
1409     c3/return
1410 
1411 skip-string-in-slice:  # curr : (address byte), end : (address byte) -> new_curr/eax
1412     # . prologue
1413     55/push-ebp
1414     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1415     # . save registers
1416     51/push-ecx
1417     52/push-edx
1418     53/push-ebx
1419     # ecx = curr
1420     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
1421     # edx = end
1422     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         2/r32/edx   0xc/disp8         .               # copy *(ebp+12) to edx
1423     # eax = 0
1424     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
1425     # skip initial dquote
1426     41/increment-ecx
1427 $skip-string-in-slice:loop:
1428     # if (curr >= end) return curr
1429     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
1430     73/jump-if-greater-unsigned-or-equal  $skip-string-in-slice:return-curr/disp8
1431     # AL = *curr
1432     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
1433 $skip-string-in-slice:dquote:
1434     # if (eax == '"') break
1435     3d/compare-eax-and  0x22/imm32/double-quote
1436     74/jump-if-equal  $skip-string-in-slice:break/disp8
1437 $skip-string-in-slice:check-for-escape:
1438     # if (eax == '\') escape next char
1439     3d/compare-eax-and  0x5c/imm32/backslash
1440     75/jump-if-not-equal  $skip-string-in-slice:continue/disp8
1441 $skip-string-in-slice:escape:
1442     41/increment-ecx
1443 $skip-string-in-slice:continue:
1444     # ++curr
1445     41/increment-ecx
1446     eb/jump  $skip-string-in-slice:loop/disp8
1447 $skip-string-in-slice:break:
1448     # skip final dquote
1449     41/increment-ecx
1450 $skip-string-in-slice:return-curr:
1451     # return curr
1452     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy ecx to eax
1453 $skip-string-in-slice:end:
1454     # . restore registers
1455     5b/pop-to-ebx
1456     5a/pop-to-edx
1457     59/pop-to-ecx
1458     # . epilogue
1459     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1460     5d/pop-to-ebp
1461     c3/return
1462 
1463 test-skip-string-in-slice:
1464     # . prologue
1465     55/push-ebp
1466     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1467     # setup: (eax..ecx) = "\"abc\" def"
1468     b8/copy-to-eax  "\"abc\" def"/imm32
1469     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1470     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
1471     05/add-to-eax  4/imm32
1472     # eax = skip-string-in-slice(eax, ecx)
1473     # . . push args
1474     51/push-ecx
1475     50/push-eax
1476     # . . call
1477     e8/call  skip-string-in-slice/disp32
1478     # . . discard args
1479     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1480     # check-ints-equal(ecx-eax, 4, msg)  # number of chars remaining after the string literal
1481     # . . push args
1482     68/push  "F - test-skip-string-in-slice"/imm32
1483     68/push  4/imm32
1484     # . . push ecx-eax
1485     29/subtract                     3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract eax from ecx
1486     51/push-ecx
1487     # . . call
1488     e8/call  check-ints-equal/disp32
1489     # . . discard args
1490     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1491     # . epilogue
1492     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1493     5d/pop-to-ebp
1494     c3/return
1495 
1496 test-skip-string-in-slice-ignores-spaces:
1497     # . prologue
1498     55/push-ebp
1499     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1500     # setup: (eax..ecx) = "\"a b\"/yz"
1501     b8/copy-to-eax  "\"a b\"/yz"/imm32
1502     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1503     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
1504     05/add-to-eax  4/imm32
1505     # eax = skip-string-in-slice(eax, ecx)
1506     # . . push args
1507     51/push-ecx
1508     50/push-eax
1509     # . . call
1510     e8/call  skip-string-in-slice/disp32
1511     # . . discard args
1512     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1513     # check-ints-equal(ecx-eax, 3, msg)  # number of chars remaining after the string literal
1514     # . . push args
1515     68/push  "F - test-skip-string-in-slice-ignores-spaces"/imm32
1516     68/push  3/imm32
1517     # . . push ecx-eax
1518     29/subtract                     3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract eax from ecx
1519     51/push-ecx
1520     # . . call
1521     e8/call  check-ints-equal/disp32
1522     # . . discard args
1523     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1524     # . epilogue
1525     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1526     5d/pop-to-ebp
1527     c3/return
1528 
1529 test-skip-string-in-slice-ignores-escapes:
1530     # . prologue
1531     55/push-ebp
1532     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1533     # setup: (eax..ecx) = "\"a\\\"b\"/yz"
1534     b8/copy-to-eax  "\"a\\\"b\"/yz"/imm32
1535     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1536     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
1537     05/add-to-eax  4/imm32
1538     # eax = skip-string-in-slice(eax, ecx)
1539     # . . push args
1540     51/push-ecx
1541     50/push-eax
1542     # . . call
1543     e8/call  skip-string-in-slice/disp32
1544     # . . discard args
1545     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1546     # check-ints-equal(ecx-eax, 3, msg)  # number of chars remaining after the string literal
1547     # . . push args
1548     68/push  "F - test-skip-string-in-slice-ignores-escapes"/imm32
1549     68/push  3/imm32
1550     # . . push ecx-eax
1551     29/subtract                     3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract eax from ecx
1552     51/push-ecx
1553     # . . call
1554     e8/call  check-ints-equal/disp32
1555     # . . discard args
1556     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1557     # . epilogue
1558     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1559     5d/pop-to-ebp
1560     c3/return
1561 
1562 test-skip-string-in-slice-stops-at-end:
1563     # . prologue
1564     55/push-ebp
1565     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1566     # setup: (eax..ecx) = "\"abc"  # unbalanced dquote
1567     b8/copy-to-eax  "\"abc"/imm32
1568     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1569     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
1570     05/add-to-eax  4/imm32
1571     # eax = skip-string-in-slice(eax, ecx)
1572     # . . push args
1573     51/push-ecx
1574     50/push-eax
1575     # . . call
1576     e8/call  skip-string-in-slice/disp32
1577     # . . discard args
1578     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1579     # check-ints-equal(ecx-eax, 0, msg)  # skipped to end of slice
1580     # . . push args
1581     68/push  "F - test-skip-string-in-slice-stops-at-end"/imm32
1582     68/push  0/imm32
1583     # . . push ecx-eax
1584     29/subtract                     3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract eax from ecx
1585     51/push-ecx
1586     # . . call
1587     e8/call  check-ints-equal/disp32
1588     # . . discard args
1589     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1590     # . epilogue
1591     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1592     5d/pop-to-ebp
1593     c3/return
1594 
1595 # update line->read to ')'
1596 # line->read ends at ')'
1597 skip-until-close-paren:  # line : (address stream)
1598     # . prologue
1599     55/push-ebp
1600     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1601     # . save registers
1602     50/push-eax
1603     51/push-ecx
1604     52/push-edx
1605     # ecx = line
1606     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
1607     # eax = skip-until-close-paren-in-slice(&line->data[line->read], &line->data[line->write])
1608     # . . push &line->data[line->write]
1609     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .                         2/r32/edx   8/disp8         .                 # copy *(ecx+8) to edx
1610     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
1611     52/push-edx
1612     # . . push &line->data[line->read]
1613     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .                         2/r32/edx   4/disp8         .                 # copy *(ecx+4) to edx
1614     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
1615     52/push-edx
1616     # . . call
1617     e8/call  skip-until-close-paren-in-slice/disp32
1618     # . . discard args
1619     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1620     # line->read = eax - line->data
1621     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # subtract ecx from eax
1622     2d/subtract-from-eax  0xc/imm32
1623     89/copy                         1/mod/*+disp8   1/rm32/ecx    .           .                         0/r32/eax   4/disp8         .                 # copy eax to *(ecx+4)
1624 $skip-until-close-paren:end:
1625     # . restore registers
1626     5a/pop-to-edx
1627     59/pop-to-ecx
1628     58/pop-to-eax
1629     # . epilogue
1630     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1631     5d/pop-to-ebp
1632     c3/return
1633 
1634 test-skip-until-close-paren:
1635     # . prologue
1636     55/push-ebp
1637     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1638     # setup
1639     # . clear-stream(_test-input-stream)
1640     # . . push args
1641     68/push  _test-input-stream/imm32
1642     # . . call
1643     e8/call  clear-stream/disp32
1644     # . . discard args
1645     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1646     # . write(_test-input-stream, "*(abc) def")
1647     # .                   indices:  0123 45
1648     # . . push args
1649     68/push  "*(abc) def"/imm32
1650     68/push  _test-input-stream/imm32
1651     # . . call
1652     e8/call  write/disp32
1653     # . . discard args
1654     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1655     # precondition: line->read == 0
1656     # . . push args
1657     68/push  "F - test-skip-until-close-paren/precondition"/imm32
1658     68/push  0/imm32
1659     b8/copy-to-eax  _test-input-stream/imm32
1660     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1661     # . . call
1662     e8/call  check-ints-equal/disp32
1663     # . . discard args
1664     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1665     # skip-until-close-paren(_test-input-stream)
1666     # . . push args
1667     68/push  _test-input-stream/imm32
1668     # . . call
1669     e8/call  skip-until-close-paren/disp32
1670     # . . discard args
1671     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1672     # check-ints-equal(line->read, 5, msg)
1673     # . . push args
1674     68/push  "F - test-skip-until-close-paren"/imm32
1675     68/push  5/imm32
1676     b8/copy-to-eax  _test-input-stream/imm32
1677     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1678     # . . call
1679     e8/call  check-ints-equal/disp32
1680     # . . discard args
1681     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1682     # . epilogue
1683     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1684     5d/pop-to-ebp
1685     c3/return
1686 
1687 test-skip-until-close-paren-ignores-spaces:
1688     # . prologue
1689     55/push-ebp
1690     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1691     # setup
1692     # . clear-stream(_test-input-stream)
1693     # . . push args
1694     68/push  _test-input-stream/imm32
1695     # . . call
1696     e8/call  clear-stream/disp32
1697     # . . discard args
1698     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1699     # . write(_test-input-stream, "*(a b)/yz")
1700     # . . push args
1701     68/push  "*(a b)/yz"/imm32
1702     68/push  _test-input-stream/imm32
1703     # . . call
1704     e8/call  write/disp32
1705     # . . discard args
1706     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1707     # precondition: line->read == 0
1708     # . . push args
1709     68/push  "F - test-skip-until-close-paren-ignores-spaces/precondition"/imm32
1710     68/push  0/imm32
1711     b8/copy-to-eax  _test-input-stream/imm32
1712     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1713     # . . call
1714     e8/call  check-ints-equal/disp32
1715     # . . discard args
1716     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1717     # skip-until-close-paren(_test-input-stream)
1718     # . . push args
1719     68/push  _test-input-stream/imm32
1720     # . . call
1721     e8/call  skip-until-close-paren/disp32
1722     # . . discard args
1723     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1724     # check-ints-equal(line->read, 5, msg)
1725     # . . push args
1726     68/push  "F - test-skip-until-close-paren-ignores-spaces"/imm32
1727     68/push  5/imm32
1728     b8/copy-to-eax  _test-input-stream/imm32
1729     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1730     # . . call
1731     e8/call  check-ints-equal/disp32
1732     # . . discard args
1733     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1734     # . epilogue
1735     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1736     5d/pop-to-ebp
1737     c3/return
1738 
1739 test-skip-until-close-paren-works-from-mid-stream:
1740     # . prologue
1741     55/push-ebp
1742     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1743     # setup
1744     # . clear-stream(_test-input-stream)
1745     # . . push args
1746     68/push  _test-input-stream/imm32
1747     # . . call
1748     e8/call  clear-stream/disp32
1749     # . . discard args
1750     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1751     # . write(_test-input-stream, "0 *(a b)/yz")
1752     # . . push args
1753     68/push  "0 *(a b)/yz"/imm32
1754     68/push  _test-input-stream/imm32
1755     # . . call
1756     e8/call  write/disp32
1757     # . . discard args
1758     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1759     # precondition: _test-input-stream->read == 2
1760     b8/copy-to-eax  _test-input-stream/imm32
1761     c7          0/subop/copy        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         2/imm32           # copy to *(eax+4)
1762     # skip-until-close-paren(_test-input-stream)
1763     # . . push args
1764     68/push  _test-input-stream/imm32
1765     # . . call
1766     e8/call  skip-until-close-paren/disp32
1767     # . . discard args
1768     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1769     # check-ints-equal(_test-input-stream->read, 7, msg)
1770     # . . push args
1771     68/push  "F - test-skip-until-close-paren-works-from-mid-stream"/imm32
1772     68/push  7/imm32
1773     b8/copy-to-eax  _test-input-stream/imm32
1774     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1775     # . . call
1776     e8/call  check-ints-equal/disp32
1777     # . . discard args
1778     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1779     # . epilogue
1780     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1781     5d/pop-to-ebp
1782     c3/return
1783 
1784 skip-until-close-paren-in-slice:  # curr : (address byte), end : (address byte) -> new_curr/eax
1785     # . prologue
1786     55/push-ebp
1787     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1788     # . save registers
1789     51/push-ecx
1790     52/push-edx
1791     # ecx = curr
1792     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
1793     # edx = end
1794     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         2/r32/edx   0xc/disp8         .               # copy *(ebp+12) to edx
1795     # eax = 0
1796     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
1797     # skip initial dquote
1798     41/increment-ecx
1799 $skip-until-close-paren-in-slice:loop:
1800     # if (curr >= end) break
1801     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
1802     73/jump-if-greater-unsigned-or-equal  $skip-until-close-paren-in-slice:break/disp8
1803     # AL = *curr
1804     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
1805 $skip-until-close-paren-in-slice:check-close:
1806     # if (eax == ')') break
1807     3d/compare-eax-and  0x29/imm32/close-paren
1808     74/jump-if-equal  $skip-until-close-paren-in-slice:break/disp8
1809     # ++curr
1810     41/increment-ecx
1811     eb/jump  $skip-until-close-paren-in-slice:loop/disp8
1812 $skip-until-close-paren-in-slice:break:
1813     # return curr
1814     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy ecx to eax
1815 $skip-until-close-paren-in-slice:end:
1816     # . restore registers
1817     5a/pop-to-edx
1818     59/pop-to-ecx
1819     # . epilogue
1820     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1821     5d/pop-to-ebp
1822     c3/return
1823 
1824 test-skip-until-close-paren-in-slice:
1825     # . prologue
1826     55/push-ebp
1827     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1828     # setup: (eax..ecx) = "*(abc) def"
1829     b8/copy-to-eax  "*(abc) def"/imm32
1830     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1831     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
1832     05/add-to-eax  4/imm32
1833     # eax = skip-until-close-paren-in-slice(eax, ecx)
1834     # . . push args
1835     51/push-ecx
1836     50/push-eax
1837     # . . call
1838     e8/call  skip-until-close-paren-in-slice/disp32
1839     # . . discard args
1840     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1841     # check-ints-equal(ecx-eax, 5, msg)  # eax is at the ')'
1842     # . . push args
1843     68/push  "F - test-skip-until-close-paren-in-slice"/imm32
1844     68/push  5/imm32
1845     # . . push ecx-eax
1846     29/subtract                     3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract eax from ecx
1847     51/push-ecx
1848     # . . call
1849     e8/call  check-ints-equal/disp32
1850     # . . discard args
1851     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1852     # . epilogue
1853     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1854     5d/pop-to-ebp
1855     c3/return
1856 
1857 test-skip-until-close-paren-in-slice-ignores-spaces:
1858     # . prologue
1859     55/push-ebp
1860     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1861     # setup: (eax..ecx) = "*(a b)/yz"
1862     b8/copy-to-eax  "*(a b)/yz"/imm32
1863     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1864     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
1865     05/add-to-eax  4/imm32
1866     # eax = skip-until-close-paren-in-slice(eax, ecx)
1867     # . . push args
1868     51/push-ecx
1869     50/push-eax
1870     # . . call
1871     e8/call  skip-until-close-paren-in-slice/disp32
1872     # . . discard args
1873     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1874     # check-ints-equal(ecx-eax, 4, msg)  # eax is at the ')'
1875     # . . push args
1876     68/push  "F - test-skip-until-close-paren-in-slice-ignores-spaces"/imm32
1877     68/push  4/imm32
1878     # . . push ecx-eax
1879     29/subtract                     3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract eax from ecx
1880     51/push-ecx
1881     # . . call
1882     e8/call  check-ints-equal/disp32
1883     # . . discard args
1884     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1885     # . epilogue
1886     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1887     5d/pop-to-ebp
1888     c3/return
1889 
1890 test-skip-until-close-paren-in-slice-stops-at-end:
1891     # . prologue
1892     55/push-ebp
1893     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1894     # setup: (eax..ecx) = "*(abc"  # unbalanced dquote
1895     b8/copy-to-eax  "*(abc"/imm32
1896     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1897     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
1898     05/add-to-eax  4/imm32
1899     # eax = skip-until-close-paren-in-slice(eax, ecx)
1900     # . . push args
1901     51/push-ecx
1902     50/push-eax
1903     # . . call
1904     e8/call  skip-until-close-paren-in-slice/disp32
1905     # . . discard args
1906     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1907     # check-ints-equal(ecx-eax, 0, msg)  # skipped to end of slice
1908     # . . push args
1909     68/push  "F - test-skip-until-close-paren-in-slice-stops-at-end"/imm32
1910     68/push  0/imm32
1911     # . . push ecx-eax
1912     29/subtract                     3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract eax from ecx
1913     51/push-ecx
1914     # . . call
1915     e8/call  check-ints-equal/disp32
1916     # . . discard args
1917     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1918     # . epilogue
1919     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1920     5d/pop-to-ebp
1921     c3/return
1922 
1923 # . . vim:nowrap:textwidth=0