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