https://github.com/akkartik/mu/blob/main/linux/dquotes.subx

       1 # Translate literal strings within double quotes.
       2 # Replace them with references to new variables in the data segment.
       3 #
       4 # To run:
       5 #   $ bootstrap/bootstrap translate [01]*.subx subx-params.subx dquotes.subx  -o dquotes
       6 #   $ cat x
       7 #   == code
       8 #   ab "cd ef"/imm32
       9 #   $ cat x  |bootstrap/bootstrap run dquotes
      10 #   == code
      11 #   ab __string1/imm32
      12 #   == data
      13 #   __string1:
      14 #     5/imm32
      15 #     0x63/c 0x64/d 0x20/  0x65/e 0x66/f
      16
      17 == code
      18 #   instruction                     effective address                                                   register    displacement    immediate
      19 # . op          subop               mod             rm32          base        index         scale       r32
      20 # . 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
      21
      22 Entry:  # run tests if necessary, convert stdin if not
      23     # . prologue
      24     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
      25
      26     # initialize heap
      27     # . Heap = new-segment(Heap-size)
      28     # . . push args
      29     68/push  Heap/imm32
      30     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Heap-size/disp32                  # push *Heap-size
      31     # . . call
      32     e8/call  new-segment/disp32
      33     # . . discard args
      34     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
      35
      36     # - if argc > 1 and argv[1] == "test", then return run-tests()
      37     # if (argc <= 1) goto interactive
      38     81          7/subop/compare     1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0/disp8         1/imm32           # compare *ebp
      39     7e/jump-if-<=  $subx-dquotes-main:interactive/disp8
      40     # if (!kernel-string-equal?(argv[1], "test")) goto interactive
      41     # . eax = kernel-string-equal?(argv[1], "test")
      42     # . . push args
      43     68/push  "test"/imm32
      44     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
      45     # . . call
      46     e8/call  kernel-string-equal?/disp32
      47     # . . discard args
      48     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
      49     # . if (eax == false) goto interactive
      50     3d/compare-eax-and  0/imm32/false
      51     74/jump-if-=  $subx-dquotes-main:interactive/disp8
      52     # run-tests()
      53     e8/call  run-tests/disp32
      54     # syscall(exit, *Num-test-failures)
      55     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/ebx   Num-test-failures/disp32          # copy *Num-test-failures to ebx
      56     eb/jump  $subx-dquotes-main:end/disp8
      57 $subx-dquotes-main:interactive:
      58     # - otherwise convert stdin
      59     # var ed/eax: exit-descriptor
      60     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
      61     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           4/r32/esp   .               .                 # copy esp to eax
      62     # configure ed to really exit()
      63     # . ed->target = 0
      64     c7          0/subop/copy        0/mod/direct    0/rm32/eax    .           .             .           .           .               0/imm32           # copy to *eax
      65     # subx-dquotes(Stdin, Stdout, Stderr, ed)
      66     # . . push args
      67     50/push-eax/ed
      68     68/push  Stderr/imm32
      69     68/push  Stdout/imm32
      70     68/push  Stdin/imm32
      71     # . . call
      72     e8/call  subx-dquotes/disp32
      73     # . . discard args
      74     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
      75     # syscall(exit, 0)
      76     bb/copy-to-ebx  0/imm32
      77 $subx-dquotes-main:end:
      78     e8/call  syscall_exit/disp32
      79
      80 # conceptual hierarchy within a line:
      81 #   line = words separated by ' ', maybe followed by comment starting with '#'
      82 #   word = datum until '/', then 0 or more metadata separated by '/'
      83
      84 subx-dquotes:  # in: (addr buffered-file), out: (addr buffered-file)
      85     # pseudocode:
      86     #   var line: (stream byte 512)
      87     #   var new-data-segment-handle: (handle stream byte)
      88     #   new-stream(Heap, Segment-size, 1, new-data-segment-handle)
      89     #   var new-data-segment: (addr stream byte) = lookup(new-data-segment-handle)
      90     #
      91     #   write(new-data-segment, "== data\n")
      92     #      # TODO: When it was originally written dquotes ran before assort, so
      93     #      # it assumes lots of segment headers, and emits a new segment of its
      94     #      # own. We've since had to reorder the phases (see the explanation
      95     #      # for a.assort2 in translate_subx). We could clean up a.assort2 if we
      96     #      # conditionally emit the previous line. But this would require
      97     #      # teaching dquotes to parse segment headers, so maybe that's not
      98     #      # best..
      99     #
     100     #   while true
     101     #     clear-stream(line)
     102     #     read-line-buffered(in, line)
     103     #     if (line->write == 0) break               # end of file
     104     #     while true
     105     #       var word-slice = next-word-or-string(line)
     106     #       if slice-empty?(word-slice)             # end of line
     107     #         break
     108     #       if slice-starts-with?(word-slice, "#")  # comment
     109     #         continue
     110     #       if slice-starts-with?(word-slice, '"')  # string literal <== what we're here for
     111     #         process-string-literal(word-slice, out, new-data-segment)
     112     #       else
     113     #         write-slice-buffered(out, word-slice)
     114     #       write(out, " ")
     115     #     write(out, "\n\n")
     116     #   write-stream-data(out, new-data-segment)
     117     #   flush(out)
     118     #
     119     # . prologue
     120     55/push-ebp
     121     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     122     # . save registers
     123     50/push-eax
     124     51/push-ecx
     125     52/push-edx
     126     53/push-ebx
     127     56/push-esi
     128     57/push-edi
     129     # var line/ecx: (stream byte 512)
     130     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x200/imm32       # subtract from esp
     131     68/push  0x200/imm32/length
     132     68/push  0/imm32/read
     133     68/push  0/imm32/write
     134     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
     135     # var word-slice/edx: slice
     136     68/push  0/imm32/end
     137     68/push  0/imm32/start
     138     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
     139     # var new-data-segment-handle/edi: (handle stream byte)
     140     68/push  0/imm32
     141     68/push  0/imm32
     142     89/copy                         3/mod/direct    7/rm32/edi    .           .             .           4/r32/esp   .               .                 # copy esp to edi
     143     # new-stream(Heap, Segment-size, 1, new-data-segment-handle)
     144     # . . push args
     145     57/push-edi
     146     68/push  1/imm32
     147     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Segment-size/disp32               # push *Segment-size
     148     68/push  Heap/imm32
     149     # . . call
     150     e8/call  new-stream/disp32
     151     # . . discard args
     152     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
     153     # var new-data-segment/edi: (addr stream byte) = lookup(*new-data-segment-handle)
     154     # . eax = lookup(*new-data-segment-handle)
     155     # . . push args
     156     ff          6/subop/push        1/mod/*+disp8   7/rm32/edi    .           .             .           .           4/disp8         .                 # push *(edi+4)
     157     ff          6/subop/push        0/mod/indirect  7/rm32/edi    .           .             .           .           .               .                 # push *edi
     158     # . . call
     159     e8/call  lookup/disp32
     160     # . . discard args
     161     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     162     # . new-data-segment = eax
     163     89/copy                         3/mod/direct    7/rm32/edi    .           .             .           0/r32/eax   .               .                 # copy eax to edi
     164     # write(new-data-segment, "== data\n")
     165     # . . push args
     166     68/push  "== data\n"/imm32
     167     57/push-edi
     168     # . . call
     169     e8/call  write/disp32
     170     # . . discard args
     171     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     172 $subx-dquotes:line-loop:
     173     # clear-stream(line)
     174     # . . push args
     175     51/push-ecx
     176     # . . call
     177     e8/call  clear-stream/disp32
     178     # . . discard args
     179     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     180     # read-line-buffered(in, line)
     181     # . . push args
     182     51/push-ecx
     183     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
     184     # . . call
     185     e8/call  read-line-buffered/disp32
     186     # . . discard args
     187     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     188 $subx-dquotes:check0:
     189     # if (line->write == 0) break
     190     81          7/subop/compare     0/mod/indirect  1/rm32/ecx    .           .             .           .           .               0/imm32           # compare *ecx
     191     0f 84/jump-if-=  $subx-dquotes:break/disp32
     192 $subx-dquotes:word-loop:
     193     # next-word-or-string(line, word-slice)
     194     # . . push args
     195     52/push-edx
     196     51/push-ecx
     197     # . . call
     198     e8/call  next-word-or-string/disp32
     199     # . . discard args
     200     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     201 $subx-dquotes:check1:
     202     # if (slice-empty?(word-slice)) break
     203     # . eax = slice-empty?(word-slice)
     204     # . . push args
     205     52/push-edx
     206     # . . call
     207     e8/call  slice-empty?/disp32
     208     # . . discard args
     209     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     210     # . if (eax != false) break
     211     3d/compare-eax-and  0/imm32/false
     212     0f 85/jump-if-!=  $subx-dquotes:next-line/disp32
     213 $subx-dquotes:check-for-comment:
     214     # if (slice-starts-with?(word-slice, "#")) continue
     215     # . var start/esi: (addr byte) = word-slice->start
     216     8b/copy                         0/mod/indirect  2/rm32/edx    .           .             .           6/r32/esi   .               .                 # copy *edx to esi
     217     # . var c/eax: byte = *start
     218     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
     219     8a/copy-byte                    0/mod/indirect  6/rm32/esi    .           .             .           0/r32/AL    .               .                 # copy byte at *esi to AL
     220     # . if (c == '#') continue
     221     3d/compare-eax-and  0x23/imm32/hash
     222     74/jump-if-=  $subx-dquotes:word-loop/disp8
     223 $subx-dquotes:check-for-string-literal:
     224     # if (slice-starts-with?(word-slice, '"')) continue
     225     3d/compare-eax-and  0x22/imm32/dquote
     226     75/jump-if-!=  $subx-dquotes:regular-word/disp8
     227 $subx-dquotes:string-literal:
     228     # process-string-literal(word-slice, out, new-data-segment)
     229     # . . push args
     230     57/push-edi
     231     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     232     52/push-edx
     233     # . . call
     234     e8/call  process-string-literal/disp32
     235     # . . discard args
     236     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     237     # continue
     238     eb/jump  $subx-dquotes:next-word/disp8
     239 $subx-dquotes:regular-word:
     240     # write-slice-buffered(out, word-slice)
     241     # . . push args
     242     52/push-edx
     243     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     244     # . . call
     245     e8/call  write-slice-buffered/disp32
     246     # . . discard args
     247     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     248     # fall through
     249 $subx-dquotes:next-word:
     250     # write-buffered(out, " ")
     251     # . . push args
     252     68/push  Space/imm32
     253     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     254     # . . call
     255     e8/call  write-buffered/disp32
     256     # . . discard args
     257     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     258     # loop
     259     eb/jump  $subx-dquotes:word-loop/disp8
     260 $subx-dquotes:next-line:
     261     # write-buffered(out, "\n")
     262     # . . push args
     263     68/push  Newline/imm32
     264     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     265     # . . call
     266     e8/call  write-buffered/disp32
     267     # . . discard args
     268     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     269     # loop
     270     e9/jump  $subx-dquotes:line-loop/disp32
     271 $subx-dquotes:break:
     272     # write-stream-data(out, new-data-segment)
     273     # . . push args
     274     57/push-edi
     275     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     276     # . . call
     277     e8/call  write-stream-data/disp32
     278     # . . discard args
     279     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     280     # flush(out)
     281     # . . push args
     282     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     283     # . . call
     284     e8/call  flush/disp32
     285     # . . discard args
     286     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     287 $subx-dquotes:end:
     288     # . reclaim locals
     289     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x21c/imm32       # add to esp
     290     # . restore registers
     291     5f/pop-to-edi
     292     5e/pop-to-esi
     293     5b/pop-to-ebx
     294     5a/pop-to-edx
     295     59/pop-to-ecx
     296     58/pop-to-eax
     297     # . epilogue
     298     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
     299     5d/pop-to-ebp
     300     c3/return
     301
     302 # Write out 'string-literal' in a new format to 'out-segment', assign it a new
     303 # label, and write the new label out to 'out'.
     304 process-string-literal:  # string-literal: (addr slice), out: (addr buffered-file), out-segment: (addr stream byte)
     305     # pseudocode:
     306     #   print(out-segment, "_string#{Next-string-literal}:\n")
     307     #   emit-string-literal-data(out-segment, string-literal)
     308     #   print(out, "_string#{Next-string-literal}")
     309     #   emit-metadata(out, string-literal)
     310     #   ++ *Next-string-literal
     311     #
     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     51/push-ecx
     317     # var int32-stream/ecx: (stream byte 10)  # number of decimal digits a 32-bit number can have
     318     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0xa/imm32         # subtract from esp
     319     68/push  0xa/imm32/decimal-digits-in-32bit-number
     320     68/push  0/imm32/read
     321     68/push  0/imm32/write
     322     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
     323     # print(out-segment, "_string#{Next-string-literal}:\n")
     324     # . write(out-segment, "_string")
     325     # . . push args
     326     68/push  "_string"/imm32
     327     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
     328     # . . call
     329     e8/call  write/disp32
     330     # . . discard args
     331     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     332     # . write-int32-decimal(out-segment, *Next-string-literal)
     333     # . . push args
     334     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Next-string-literal/disp32        # push *Next-string-literal
     335     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
     336     # . . call
     337     e8/call  write-int32-decimal/disp32
     338     # . . discard args
     339     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     340     # . write(out-segment, ":\n")
     341     # . . push args
     342     68/push  ":\n"/imm32
     343     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
     344     # . . call
     345     e8/call  write/disp32
     346     # . . discard args
     347     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     348     # emit-string-literal-data(out-segment, string-literal)
     349     # . . push args
     350     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
     351     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
     352     # . . call
     353     e8/call  emit-string-literal-data/disp32
     354     # . . discard args
     355     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     356     # write(out-segment, "\n")
     357     # . . push args
     358     68/push  Newline/imm32
     359     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
     360     # . . call
     361     e8/call  write/disp32
     362     # . . discard args
     363     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     364     # print(out, "_string#{Next-string-literal}")
     365     # . write-buffered(out, "_string")
     366     # . . push args
     367     68/push  "_string"/imm32
     368     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     369     # . . call
     370     e8/call  write-buffered/disp32
     371     # . . discard args
     372     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     373     # . write-int32-decimal(int32-stream, *Next-string-literal)
     374     # . . push args
     375     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Next-string-literal/disp32        # push *Next-string-literal
     376     51/push-ecx
     377     # . . call
     378     e8/call  write-int32-decimal/disp32
     379     # . . discard args
     380     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     381     # . write-stream-data(out, int32-stream)
     382     # . . push args
     383     51/push-ecx
     384     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     385     # . . call
     386     e8/call  write-stream-data/disp32
     387     # . . discard args
     388     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     389     # emit-metadata(out, string-literal)
     390     # . . push args
     391     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
     392     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     393     # . . call
     394     e8/call  emit-metadata/disp32
     395     # . . discard args
     396     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     397     # ++ *Next-string-literal
     398     ff          0/subop/increment   0/mod/indirect  5/rm32/.disp32            .             .           .           Next-string-literal/disp32        # increment *Num-test-failures
     399 $process-string-literal:end:
     400     # . reclaim locals
     401     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x16/imm32        # add to esp
     402     # . restore registers
     403     59/pop-to-ecx
     404     # . epilogue
     405     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
     406     5d/pop-to-ebp
     407     c3/return
     408
     409 test-subx-dquotes-is-idempotent-by-default:
     410     # . prologue
     411     55/push-ebp
     412     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     413     # setup
     414     # . clear-stream(_test-input-stream)
     415     # . . push args
     416     68/push  _test-input-stream/imm32
     417     # . . call
     418     e8/call  clear-stream/disp32
     419     # . . discard args
     420     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     421     # . clear-stream($_test-input-buffered-file->buffer)
     422     # . . push args
     423     68/push  $_test-input-buffered-file->buffer/imm32
     424     # . . call
     425     e8/call  clear-stream/disp32
     426     # . . discard args
     427     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     428     # . clear-stream(_test-output-stream)
     429     # . . push args
     430     68/push  _test-output-stream/imm32
     431     # . . call
     432     e8/call  clear-stream/disp32
     433     # . . discard args
     434     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     435     # . clear-stream($_test-output-buffered-file->buffer)
     436     # . . push args
     437     68/push  $_test-output-buffered-file->buffer/imm32
     438     # . . call
     439     e8/call  clear-stream/disp32
     440     # . . discard args
     441     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     442     # initialize input (meta comments in parens)
     443     #   # comment 1
     444     #     # comment 2 indented
     445     #   == code 0x1  (new segment)
     446     #   # comment 3 inside a segment
     447     #   1
     448     #                         (empty line)
     449     #   2 3 # comment 4 inline with other contents
     450     #   == data 0x2  (new segment)
     451     #   4 5/imm32
     452     # . write(_test-input-stream, "# comment 1\n")
     453     # . . push args
     454     68/push  "# comment 1\n"/imm32
     455     68/push  _test-input-stream/imm32
     456     # . . call
     457     e8/call  write/disp32
     458     # . . discard args
     459     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     460     # . write(_test-input-stream, "  # comment 2 indented\n")
     461     # . . push args
     462     68/push  "  # comment 2 indented\n"/imm32
     463     68/push  _test-input-stream/imm32
     464     # . . call
     465     e8/call  write/disp32
     466     # . . discard args
     467     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     468     # . write(_test-input-stream, "== code 0x1\n")
     469     # . . push args
     470     68/push  "== code 0x1\n"/imm32
     471     68/push  _test-input-stream/imm32
     472     # . . call
     473     e8/call  write/disp32
     474     # . . discard args
     475     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     476     # . write(_test-input-stream, "# comment 3 inside a segment\n")
     477     # . . push args
     478     68/push  "# comment 3 inside a segment\n"/imm32
     479     68/push  _test-input-stream/imm32
     480     # . . call
     481     e8/call  write/disp32
     482     # . . discard args
     483     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     484     # . write(_test-input-stream, "1\n")
     485     # . . push args
     486     68/push  "1\n"/imm32
     487     68/push  _test-input-stream/imm32
     488     # . . call
     489     e8/call  write/disp32
     490     # . . discard args
     491     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     492     # . write(_test-input-stream, "\n")  # empty line
     493     # . . push args
     494     68/push  Newline/imm32
     495     68/push  _test-input-stream/imm32
     496     # . . call
     497     e8/call  write/disp32
     498     # . . discard args
     499     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     500     # . write(_test-input-stream, "2 3 # comment 4 inline with other contents\n")
     501     # . . push args
     502     68/push  "2 3 # comment 4 inline with other contents\n"/imm32
     503     68/push  _test-input-stream/imm32
     504     # . . call
     505     e8/call  write/disp32
     506     # . . discard args
     507     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     508     # . write(_test-input-stream, "== data 0x2\n")
     509     # . . push args
     510     68/push  "== data 0x2\n"/imm32
     511     68/push  _test-input-stream/imm32
     512     # . . call
     513     e8/call  write/disp32
     514     # . . discard args
     515     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     516     # . write(_test-input-stream, "4 5/imm32\n")
     517     # . . push args
     518     68/push  "4 5/imm32\n"/imm32
     519     68/push  _test-input-stream/imm32
     520     # . . call
     521     e8/call  write/disp32
     522     # . . discard args
     523     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     524     # subx-dquotes(_test-input-buffered-file, _test-output-buffered-file)
     525     # . . push args
     526     68/push  _test-output-buffered-file/imm32
     527     68/push  _test-input-buffered-file/imm32
     528     # . . call
     529     e8/call  subx-dquotes/disp32
     530     # . . discard args
     531     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     532     # . flush(_test-output-buffered-file)
     533     # . . push args
     534     68/push  _test-output-buffered-file/imm32
     535     # . . call
     536     e8/call  flush/disp32
     537     # . . discard args
     538     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     539     # check output
     540     #     (comment dropped for now)
     541     #     (comment dropped for now)
     542     #   == code 0x1
     543     #     (comment dropped for now)
     544     #   1
     545     #     (comment dropped for now)
     546     #   2 3
     547     #   == data 0x2
     548     #   4 5/imm32
     549     # We don't care right now what exactly happens to comments. Trailing spaces are also minor details.
     550 +-- 26 lines: #?     # dump output ------------------------------------------------------------------------------------------------------------------------------------------------------
     576     # . check-next-stream-line-equal(_test-output-stream, "", msg)
     577     # . . push args
     578     68/push  "F - test-subx-dquotes-is-idempotent-by-default/0"/imm32
     579     68/push  ""/imm32
     580     68/push  _test-output-stream/imm32
     581     # . . call
     582     e8/call  check-next-stream-line-equal/disp32
     583     # . . discard args
     584     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     585     # . check-next-stream-line-equal(_test-output-stream, "", msg)
     586     # . . push args
     587     68/push  "F - test-subx-dquotes-is-idempotent-by-default/1"/imm32
     588     68/push  ""/imm32
     589     68/push  _test-output-stream/imm32
     590     # . . call
     591     e8/call  check-next-stream-line-equal/disp32
     592     # . . discard args
     593     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     594     # . check-next-stream-line-equal(_test-output-stream, "== code 0x1 ", msg)
     595     # . . push args
     596     68/push  "F - test-subx-dquotes-is-idempotent-by-default/2"/imm32
     597     68/push  "== code 0x1 "/imm32
     598     68/push  _test-output-stream/imm32
     599     # . . call
     600     e8/call  check-next-stream-line-equal/disp32
     601     # . . discard args
     602     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     603     # . check-next-stream-line-equal(_test-output-stream, "", msg)
     604     # . . push args
     605     68/push  "F - test-subx-dquotes-is-idempotent-by-default/3"/imm32
     606     68/push  ""/imm32
     607     68/push  _test-output-stream/imm32
     608     # . . call
     609     e8/call  check-next-stream-line-equal/disp32
     610     # . . discard args
     611     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     612     # . check-next-stream-line-equal(_test-output-stream, "1 ", msg)
     613     # . . push args
     614     68/push  "F - test-subx-dquotes-is-idempotent-by-default/4"/imm32
     615     68/push  "1 "/imm32
     616     68/push  _test-output-stream/imm32
     617     # . . call
     618     e8/call  check-next-stream-line-equal/disp32
     619     # . . discard args
     620     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     621     # . check-next-stream-line-equal(_test-output-stream, "", msg)
     622     # . . push args
     623     68/push  "F - test-subx-dquotes-is-idempotent-by-default/5"/imm32
     624     68/push  ""/imm32
     625     68/push  _test-output-stream/imm32
     626     # . . call
     627     e8/call  check-next-stream-line-equal/disp32
     628     # . . discard args
     629     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     630     # . check-next-stream-line-equal(_test-output-stream, "2 3 ", msg)
     631     # . . push args
     632     68/push  "F - test-subx-dquotes-is-idempotent-by-default/6"/imm32
     633     68/push  "2 3 "/imm32
     634     68/push  _test-output-stream/imm32
     635     # . . call
     636     e8/call  check-next-stream-line-equal/disp32
     637     # . . discard args
     638     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     639     # . check-next-stream-line-equal(_test-output-stream, "== data 0x2 ", msg)
     640     # . . push args
     641     68/push  "F - test-subx-dquotes-is-idempotent-by-default/7"/imm32
     642     68/push  "== data 0x2 "/imm32
     643     68/push  _test-output-stream/imm32
     644     # . . call
     645     e8/call  check-next-stream-line-equal/disp32
     646     # . . discard args
     647     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     648     # . check-next-stream-line-equal(_test-output-stream, "4 5/imm32 ", msg)
     649     # . . push args
     650     68/push  "F - test-subx-dquotes-is-idempotent-by-default/8"/imm32
     651     68/push  "4 5/imm32 "/imm32
     652     68/push  _test-output-stream/imm32
     653     # . . call
     654     e8/call  check-next-stream-line-equal/disp32
     655     # . . discard args
     656     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     657     # . epilogue
     658     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
     659     5d/pop-to-ebp
     660     c3/return
     661
     662 test-subx-dquotes-processes-string-literals:
     663     # . prologue
     664     55/push-ebp
     665     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     666     # setup
     667     # . clear-stream(_test-input-stream)
     668     # . . push args
     669     68/push  _test-input-stream/imm32
     670     # . . call
     671     e8/call  clear-stream/disp32
     672     # . . discard args
     673     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     674     # . clear-stream($_test-input-buffered-file->buffer)
     675     # . . push args
     676     68/push  $_test-input-buffered-file->buffer/imm32
     677     # . . call
     678     e8/call  clear-stream/disp32
     679     # . . discard args
     680     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     681     # . clear-stream(_test-output-stream)
     682     # . . push args
     683     68/push  _test-output-stream/imm32
     684     # . . call
     685     e8/call  clear-stream/disp32
     686     # . . discard args
     687     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     688     # . clear-stream($_test-output-buffered-file->buffer)
     689     # . . push args
     690     68/push  $_test-output-buffered-file->buffer/imm32
     691     # . . call
     692     e8/call  clear-stream/disp32
     693     # . . discard args
     694     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     695     # initialize input (meta comments in parens)
     696     #   == code  (new segment)
     697     #   1 "a"/x
     698     #   2 "bc"/y
     699     68/push  "== code 0x1\n"/imm32
     700     68/push  _test-input-stream/imm32
     701     # . . call
     702     e8/call  write/disp32
     703     # . . discard args
     704     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     705     # . write(_test-input-stream, "1 \"a\"/x\n")
     706     # . . push args
     707     68/push  "1 \"a\"/x\n"/imm32
     708     68/push  _test-input-stream/imm32
     709     # . . call
     710     e8/call  write/disp32
     711     # . . discard args
     712     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     713     # . write(_test-input-stream, "2 \"bc\"/y\n")
     714     # . . push args
     715     68/push  "2 \"bc\"/y\n"/imm32
     716     68/push  _test-input-stream/imm32
     717     # . . call
     718     e8/call  write/disp32
     719     # . . discard args
     720     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     721     # subx-dquotes(_test-input-buffered-file, _test-output-buffered-file)
     722     # . . push args
     723     68/push  _test-output-buffered-file/imm32
     724     68/push  _test-input-buffered-file/imm32
     725     # . . call
     726     e8/call  subx-dquotes/disp32
     727     # . . discard args
     728     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     729     # . flush(_test-output-buffered-file)
     730     # . . push args
     731     68/push  _test-output-buffered-file/imm32
     732     # . . call
     733     e8/call  flush/disp32
     734     # . . discard args
     735     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     736     # check output
     737     #   == code 0x1
     738     #   1 _string1/x
     739     #   2 _string2/y
     740     #   == data
     741     #   _string1:
     742     #   1/imm32 61/a
     743     #   _string2:
     744     #   2/imm32 62/b 63/c
     745     # We don't care right now what exactly happens to comments. Trailing spaces are also minor details.
     746     #
     747     # Open question: how to make this check more robust.
     748     # We don't actually care what the auto-generated string variables are
     749     # called. We just want to make sure instructions using string literals
     750     # switch to a string variable with the right value.
     751     # (Modifying string literals completely off the radar for now.)
     752 +-- 33 lines: #?     # dump output ------------------------------------------------------------------------------------------------------------------------------------------------------
     785     # . check-next-stream-line-equal(_test-output-stream, "== code 0x1 ", msg)
     786     # . . push args
     787     68/push  "F - test-subx-dquotes-processes-string-literals/0"/imm32
     788     68/push  "== code 0x1 "/imm32
     789     68/push  _test-output-stream/imm32
     790     # . . call
     791     e8/call  check-next-stream-line-equal/disp32
     792     # . . discard args
     793     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     794     # . check-next-stream-line-equal(_test-output-stream, "1 _string1/x ", msg)
     795     # . . push args
     796     68/push  "F - test-subx-dquotes-processes-string-literals/1"/imm32
     797     68/push  "1 _string1/x "/imm32
     798     68/push  _test-output-stream/imm32
     799     # . . call
     800     e8/call  check-next-stream-line-equal/disp32
     801     # . . discard args
     802     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     803     # . check-next-stream-line-equal(_test-output-stream, "2 _string2/y ", msg)
     804     # . . push args
     805     68/push  "F - test-subx-dquotes-processes-string-literals/2"/imm32
     806     68/push  "2 _string2/y "/imm32
     807     68/push  _test-output-stream/imm32
     808     # . . call
     809     e8/call  check-next-stream-line-equal/disp32
     810     # . . discard args
     811     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     812     # . check-next-stream-line-equal(_test-output-stream, "== data", msg)
     813     # . . push args
     814     68/push  "F - test-subx-dquotes-processes-string-literals/3"/imm32
     815     68/push  "== data"/imm32
     816     68/push  _test-output-stream/imm32
     817     # . . call
     818     e8/call  check-next-stream-line-equal/disp32
     819     # . . discard args
     820     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     821     # . check-next-stream-line-equal(_test-output-stream, "_string1: ", msg)
     822     # . . push args
     823     68/push  "F - test-subx-dquotes-processes-string-literals/4"/imm32
     824     68/push  "_string1:"/imm32
     825     68/push  _test-output-stream/imm32
     826     # . . call
     827     e8/call  check-next-stream-line-equal/disp32
     828     # . . discard args
     829     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     830     # . check-next-stream-line-equal(_test-output-stream, "1/imm32 61/a ", msg)
     831     # . . push args
     832     68/push  "F - test-subx-dquotes-processes-string-literals/5"/imm32
     833     68/push  "0x00000001/imm32 61/a "/imm32
     834     68/push  _test-output-stream/imm32
     835     # . . call
     836     e8/call  check-next-stream-line-equal/disp32
     837     # . . discard args
     838     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     839     # . check-next-stream-line-equal(_test-output-stream, "_string2: ", msg)
     840     # . . push args
     841     68/push  "F - test-subx-dquotes-processes-string-literals/6"/imm32
     842     68/push  "_string2:"/imm32
     843     68/push  _test-output-stream/imm32
     844     # . . call
     845     e8/call  check-next-stream-line-equal/disp32
     846     # . . discard args
     847     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     848     # . check-next-stream-line-equal(_test-output-stream, "2/imm32 62/b 63/c ", msg)
     849     # . . push args
     850     68/push  "F - test-subx-dquotes-processes-string-literals/7"/imm32
     851     68/push  "0x00000002/imm32 62/b 63/c "/imm32
     852     68/push  _test-output-stream/imm32
     853     # . . call
     854     e8/call  check-next-stream-line-equal/disp32
     855     # . . discard args
     856     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     857     # . epilogue
     858     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
     859     5d/pop-to-ebp
     860     c3/return
     861
     862 # generate the data segment contents byte by byte for a given slice
     863 emit-string-literal-data:  # out: (addr stream byte), word: (addr slice)
     864     # pseudocode
     865     #   len = string-length-at-start-of-slice(word->start, word->end)
     866     #   print(out, "#{len}/imm32 ")
     867     #   curr = word->start
     868     #   ++curr  # skip '"'
     869     #   idx = 0
     870     #   while true
     871     #     if (curr >= word->end) break
     872     #     c = *curr
     873     #     if (c == '"') break
     874     #     if (c == '\') {
     875     #       ++curr
     876     #       c = *curr
     877     #       if (c == 'n')
     878     #         c = newline
     879     #     }
     880     #     append-byte-hex(out, c)
     881     #     if c is alphanumeric:
     882     #       write(out, "/")
     883     #       append-byte(out, c)
     884     #     write(out, " ")
     885     #     ++curr
     886     #     ++idx
     887     #     if idx >= 0x40
     888     #       idx = 0
     889     #       write(out, "\n")
     890     #
     891     # . prologue
     892     55/push-ebp
     893     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     894     # . save registers
     895     50/push-eax
     896     51/push-ecx
     897     52/push-edx
     898     53/push-ebx
     899     56/push-esi
     900     # esi = word
     901     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   0xc/disp8       .                 # copy *(ebp+12) to esi
     902     # var idx/ebx: int = 0
     903     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
     904     # var curr/edx: (addr byte) = word->start
     905     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           2/r32/edx   .               .                 # copy *esi to edx
     906     # var max/esi: (addr byte) = word->end
     907     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           6/r32/esi   4/disp8         .                 # copy *(esi+4) to esi
     908 $emit-string-literal-data:emit-length:
     909     # var len/eax: int = string-length-at-start-of-slice(word->start, word->end)
     910     # . . push args
     911     56/push-esi
     912     52/push-edx
     913     # . . call
     914     e8/call  string-length-at-start-of-slice/disp32
     915     # . . discard args
     916     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     917     # print(out, "#{len}/imm32 ")
     918     # . write-int32-hex(out, len)
     919     # . . push args
     920     50/push-eax
     921     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
     922     # . . call
     923     e8/call  write-int32-hex/disp32
     924     # . . discard args
     925     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     926     # . write(out, "/imm32 ")
     927     # . . push args
     928     68/push  "/imm32 "/imm32
     929     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
     930     # . . call
     931     e8/call  write/disp32
     932     # . . discard args
     933     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     934 $emit-string-literal-data:loop-init:
     935     # ++curr  # skip initial '"'
     936     42/increment-edx
     937     # var c/ecx: byte = 0
     938     31/xor                          3/mod/direct    1/rm32/ecx    .           .             .           1/r32/ecx   .               .                 # clear ecx
     939 $emit-string-literal-data:loop:
     940     # if (curr >= max) break
     941     39/compare                      3/mod/direct    2/rm32/edx    .           .             .           6/r32/esi   .               .                 # compare edx with esi
     942     0f 83/jump-if-addr>=  $emit-string-literal-data:end/disp32
     943     # CL = *curr
     944     8a/copy-byte                    0/mod/indirect  2/rm32/edx    .           .             .           1/r32/CL    .               .                 # copy byte at *edx to CL
     945     # if (c == '"') break
     946     81          7/subop/compare     3/mod/direct    1/rm32/ecx    .           .             .           .           .               0x22/imm32/dquote # compare ecx
     947     0f 84/jump-if-=  $emit-string-literal-data:end/disp32
     948     # if (c != '\') goto emit
     949     81          7/subop/compare     3/mod/direct    1/rm32/ecx    .           .             .           .           .               0x5c/imm32/backslash  # compare ecx
     950     75/jump-if-!=  $emit-string-literal-data:emit/disp8
     951     # ++curr
     952     42/increment-edx
     953     # if (curr >= max) break
     954     39/compare                      3/mod/direct    2/rm32/edx    .           .             .           6/r32/esi   .               .                 # compare edx with esi
     955     0f 83/jump-if-addr>=  $emit-string-literal-data:end/disp32
     956     # c = *curr
     957     8a/copy-byte                    0/mod/indirect  2/rm32/edx    .           .             .           1/r32/CL    .               .                 # copy byte at *edx to CL
     958     # if (c == 'n') c = newline
     959     81          7/subop/compare     3/mod/direct    1/rm32/ecx    .           .             .           .           .               0x6e/imm32/n      # compare ecx
     960     75/jump-if-!=  $emit-string-literal-data:emit/disp8
     961     b9/copy-to-ecx  0x0a/imm32/newline
     962 $emit-string-literal-data:emit:
     963     # append-byte-hex(out, CL)
     964     # . . push args
     965     51/push-ecx
     966     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
     967     # . . call
     968     e8/call  append-byte-hex/disp32
     969     # . . discard args
     970     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     971     # if (alphanumeric?(*curr)) print(out, "/#{*curr}")
     972     # . var eax: boolean = alphanumeric?(CL)
     973     # . . push args
     974     51/push-ecx
     975     # . . call
     976     e8/call  alphanumeric?/disp32
     977     # . . discard args
     978     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     979     # . if (eax == false) goto char-done
     980     3d/compare-eax-and  0/imm32/false
     981     74/jump-if-=  $emit-string-literal-data:char-done/disp8
     982     # . write(out, "/")
     983     # . . push args
     984     68/push  Slash/imm32
     985     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
     986     # . . call
     987     e8/call  write/disp32
     988     # . . discard args
     989     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     990     # . append-byte(out, *curr)
     991     # . . push args
     992     51/push-ecx
     993     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
     994     # . . call
     995     e8/call  append-byte/disp32
     996     # . . discard args
     997     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     998 $emit-string-literal-data:char-done:
     999     # write(out, " ")
    1000     # . . push args
    1001     68/push  Space/imm32
    1002     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    1003     # . . call
    1004     e8/call  write/disp32
    1005     # . . discard args
    1006     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1007     # ++curr
    1008     42/increment-edx
    1009     # ++ idx
    1010     43/increment-ebx
    1011     # if (idx < 0x40) continue
    1012     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x40/imm32        # compare ebx
    1013     7c/jump-if-<  $emit-string-literal-data:next-char/disp8
    1014     # idx = 0
    1015     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
    1016     # write(out, "\n")
    1017     # . . push args
    1018     68/push  Newline/imm32
    1019     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    1020     # . . call
    1021     e8/call  write/disp32
    1022     # . . discard args
    1023     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1024 $emit-string-literal-data:next-char:
    1025     e9/jump $emit-string-literal-data:loop/disp32
    1026 $emit-string-literal-data:end:
    1027     # . restore registers
    1028     5e/pop-to-esi
    1029     5b/pop-to-ebx
    1030     5a/pop-to-edx
    1031     59/pop-to-ecx
    1032     58/pop-to-eax
    1033     # . epilogue
    1034     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1035     5d/pop-to-ebp
    1036     c3/return
    1037
    1038 alphanumeric?:  # c: int -> eax: boolean
    1039     # . prologue
    1040     55/push-ebp
    1041     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1042     # eax = c
    1043     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   8/disp8         .                 # copy *(ebp+8) to eax
    1044     # if (c < '0') return false
    1045     3d/compare-eax-with  0x30/imm32/0
    1046     7c/jump-if-<  $alphanumeric?:false/disp8
    1047     # if (c <= '9') return true
    1048     3d/compare-eax-with  0x39/imm32/9
    1049     7e/jump-if-<=  $alphanumeric?:true/disp8
    1050     # if (c < 'A') return false
    1051     3d/compare-eax-with  0x41/imm32/A
    1052     7c/jump-if-<  $alphanumeric?:false/disp8
    1053     # if (c <= 'Z') return true
    1054     3d/compare-eax-with  0x5a/imm32/Z
    1055     7e/jump-if-<=  $alphanumeric?:true/disp8
    1056     # if (c < 'a') return false
    1057     3d/compare-eax-with  0x61/imm32/a
    1058     7c/jump-if-<  $alphanumeric?:false/disp8
    1059     # if (c <= 'z') return true
    1060     3d/compare-eax-with  0x7a/imm32/z
    1061     7e/jump-if-<=  $alphanumeric?:true/disp8
    1062     # return false
    1063 $alphanumeric?:false:
    1064     b8/copy-to-eax  0/imm32/false
    1065     eb/jump  $alphanumeric?:end/disp8
    1066 $alphanumeric?:true:
    1067     b8/copy-to-eax  1/imm32/true
    1068 $alphanumeric?:end:
    1069     # . epilogue
    1070     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1071     5d/pop-to-ebp
    1072     c3/return
    1073
    1074 test-emit-string-literal-data:
    1075     # . prologue
    1076     55/push-ebp
    1077     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1078     # setup
    1079     # . clear-stream(_test-output-stream)
    1080     # . . push args
    1081     68/push  _test-output-stream/imm32
    1082     # . . call
    1083     e8/call  clear-stream/disp32
    1084     # . . discard args
    1085     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1086     # var slice/ecx = '"abc"/d'
    1087     68/push  _test-slice-abc-limit/imm32
    1088     68/push  _test-slice-abc/imm32
    1089     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
    1090     # emit-string-literal-data(_test-output-stream, slice)
    1091     # . . push args
    1092     51/push-ecx
    1093     68/push  _test-output-stream/imm32
    1094     # . . call
    1095     e8/call  emit-string-literal-data/disp32
    1096     # . . discard args
    1097     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1098 +-- 26 lines: #?     # dump output ------------------------------------------------------------------------------------------------------------------------------------------------------
    1124     # . check-stream-equal(_test-output-stream, "3/imm32 61/a 62/b 63/c ", msg)
    1125     # . . push args
    1126     68/push  "F - test-emit-string-literal-data"/imm32
    1127     68/push  "0x00000003/imm32 61/a 62/b 63/c "/imm32
    1128     68/push  _test-output-stream/imm32
    1129     # . . call
    1130     e8/call  check-stream-equal/disp32
    1131     # . . discard args
    1132     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1133     # . epilogue
    1134     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1135     5d/pop-to-ebp
    1136     c3/return
    1137
    1138 test-emit-string-literal-data-empty:
    1139     # . prologue
    1140     55/push-ebp
    1141     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1142     # setup
    1143     # . clear-stream(_test-output-stream)
    1144     # . . push args
    1145     68/push  _test-output-stream/imm32
    1146     # . . call
    1147     e8/call  clear-stream/disp32
    1148     # . . discard args
    1149     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1150     # var slice/ecx = '""'
    1151     68/push  0/imm32/end
    1152     68/push  0/imm32/start
    1153     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
    1154     # emit-string-literal-data(_test-output-stream, slice)
    1155     # . . push args
    1156     51/push-ecx
    1157     68/push  _test-output-stream/imm32
    1158     # . . call
    1159     e8/call  emit-string-literal-data/disp32
    1160     # . . discard args
    1161     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1162 +-- 26 lines: #?     # dump output ------------------------------------------------------------------------------------------------------------------------------------------------------
    1188     # . check-stream-equal(_test-output-stream, "0/imm32 ", msg)
    1189     # . . push args
    1190     68/push  "F - test-emit-string-literal-data-empty"/imm32
    1191     68/push  "0x00000000/imm32 "/imm32
    1192     68/push  _test-output-stream/imm32
    1193     # . . call
    1194     e8/call  check-stream-equal/disp32
    1195     # . . discard args
    1196     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1197     # . epilogue
    1198     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1199     5d/pop-to-ebp
    1200     c3/return
    1201
    1202 # just to keep things simple
    1203 test-emit-string-literal-data-no-metadata-for-non-alphanumerics:
    1204     # . prologue
    1205     55/push-ebp
    1206     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1207     # setup
    1208     # . clear-stream(_test-output-stream)
    1209     # . . push args
    1210     68/push  _test-output-stream/imm32
    1211     # . . call
    1212     e8/call  clear-stream/disp32
    1213     # . . discard args
    1214     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1215     # var slice/ecx = '"a b"'
    1216     68/push  _test-slice-a-space-b-limit/imm32
    1217     68/push  _test-slice-a-space-b/imm32
    1218     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
    1219     # emit-string-literal-data(_test-output-stream, slice)
    1220     # . . push args
    1221     51/push-ecx
    1222     68/push  _test-output-stream/imm32
    1223     # . . call
    1224     e8/call  emit-string-literal-data/disp32
    1225     # . . discard args
    1226     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1227 +-- 26 lines: #?     # dump output ------------------------------------------------------------------------------------------------------------------------------------------------------
    1253     # . check-stream-equal(_test-output-stream, "3/imm32 61/a 20 62/b ", msg)  # ideally we'd like to say '20/space' but that requires managing names for codepoints
    1254     # . . push args
    1255     68/push  "F - test-emit-string-literal-data-no-metadata-for-non-alphanumerics"/imm32
    1256     68/push  "0x00000003/imm32 61/a 20 62/b "/imm32
    1257     68/push  _test-output-stream/imm32
    1258     # . . call
    1259     e8/call  check-stream-equal/disp32
    1260     # . . discard args
    1261     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1262     # . epilogue
    1263     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1264     5d/pop-to-ebp
    1265     c3/return
    1266
    1267 test-emit-string-literal-data-handles-escape-sequences:
    1268     # . prologue
    1269     55/push-ebp
    1270     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1271     # setup
    1272     # . clear-stream(_test-output-stream)
    1273     # . . push args
    1274     68/push  _test-output-stream/imm32
    1275     # . . call
    1276     e8/call  clear-stream/disp32
    1277     # . . discard args
    1278     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1279     # var slice/ecx = '"a\"b"'
    1280     68/push  _test-slice-a-dquote-b-limit/imm32
    1281     68/push  _test-slice-a-dquote-b/imm32
    1282     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
    1283     # emit-string-literal-data(_test-output-stream, slice)
    1284     # . . push args
    1285     51/push-ecx
    1286     68/push  _test-output-stream/imm32
    1287     # . . call
    1288     e8/call  emit-string-literal-data/disp32
    1289     # . . discard args
    1290     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1291 +-- 26 lines: #?     # dump output ------------------------------------------------------------------------------------------------------------------------------------------------------
    1317     # . check-stream-equal(_test-output-stream, "3/imm32 61/a 22 62/b ", msg)
    1318     # . . push args
    1319     68/push  "F - test-emit-string-literal-data-handles-escape-sequences"/imm32
    1320     68/push  "0x00000003/imm32 61/a 22 62/b "/imm32
    1321     68/push  _test-output-stream/imm32
    1322     # . . call
    1323     e8/call  check-stream-equal/disp32
    1324     # . . discard args
    1325     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1326     # . epilogue
    1327     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1328     5d/pop-to-ebp
    1329     c3/return
    1330
    1331 test-emit-string-literal-data-handles-newline-escape:
    1332     # . prologue
    1333     55/push-ebp
    1334     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1335     # setup
    1336     # . clear-stream(_test-output-stream)
    1337     # . . push args
    1338     68/push  _test-output-stream/imm32
    1339     # . . call
    1340     e8/call  clear-stream/disp32
    1341     # . . discard args
    1342     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1343     # var slice/ecx = '"a\nb"'
    1344     68/push  _test-slice-a-newline-b-limit/imm32
    1345     68/push  _test-slice-a-newline-b/imm32
    1346     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
    1347     # emit-string-literal-data(_test-output-stream, slice)
    1348     # . . push args
    1349     51/push-ecx
    1350     68/push  _test-output-stream/imm32
    1351     # . . call
    1352     e8/call  emit-string-literal-data/disp32
    1353     # . . discard args
    1354     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1355 +-- 26 lines: #?     # dump output ------------------------------------------------------------------------------------------------------------------------------------------------------
    1381     # . check-stream-equal(_test-output-stream, "3/imm32 61/a 0a 62/b ", msg)
    1382     # . . push args
    1383     68/push  "F - test-emit-string-literal-data-handles-newline-escape"/imm32
    1384     68/push  "0x00000003/imm32 61/a 0a 62/b "/imm32
    1385     68/push  _test-output-stream/imm32
    1386     # . . call
    1387     e8/call  check-stream-equal/disp32
    1388     # . . discard args
    1389     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1390     # . epilogue
    1391     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1392     5d/pop-to-ebp
    1393     c3/return
    1394
    1395 # emit everything from a word except the initial datum
    1396 emit-metadata:  # out: (addr buffered-file), word: (addr slice)
    1397     # pseudocode
    1398     #   var slice: slice = {0, word->end}
    1399     #   curr = word->start
    1400     #   if *curr == '"'
    1401     #     curr = skip-string-in-slice(curr, word->end)
    1402     #   else
    1403     #     while true
    1404     #       if curr == word->end
    1405     #         return
    1406     #       if *curr == '/'
    1407     #         break
    1408     #       ++curr
    1409     #   slice->start = curr
    1410     #   write-slice-buffered(out, slice)
    1411     #
    1412     # . prologue
    1413     55/push-ebp
    1414     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1415     # . save registers
    1416     50/push-eax
    1417     51/push-ecx
    1418     52/push-edx
    1419     53/push-ebx
    1420     56/push-esi
    1421     # esi = word
    1422     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   0xc/disp8       .                 # copy *(ebp+12) to esi
    1423     # var curr/ecx: (addr byte) = word->start
    1424     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # copy *esi to ecx
    1425     # var end/edx: (addr byte) = word->end
    1426     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           2/r32/edx   4/disp8         .                 # copy *(esi+4) to edx
    1427     # var slice/ebx: slice = {0, end}
    1428     52/push-edx
    1429     68/push  0/imm32
    1430     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
    1431     # eax = 0
    1432     b8/copy-to-eax  0/imm32
    1433 $emit-metadata:check-for-string-literal:
    1434     # -  if (*curr == '"') curr = skip-string-in-slice(curr, end)
    1435     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
    1436     3d/compare-eax-and  0x22/imm32/dquote
    1437     75/jump-if-!=  $emit-metadata:skip-datum-loop/disp8
    1438 $emit-metadata:skip-string-literal:
    1439     # . eax = skip-string-in-slice(curr, end)
    1440     # . . push args
    1441     52/push-edx
    1442     51/push-ecx
    1443     # . . call
    1444     e8/call  skip-string-in-slice/disp32
    1445     # . . discard args
    1446     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1447     # . curr = eax
    1448     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy eax to ecx
    1449     eb/jump  $emit-metadata:emit/disp8
    1450 $emit-metadata:skip-datum-loop:
    1451     # - otherwise scan for '/'
    1452     # if (curr == end) return
    1453     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx and edx
    1454     74/jump-if-=  $emit-metadata:end/disp8
    1455     # if (*curr == '/') break
    1456     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
    1457     3d/compare-eax-and  0x2f/imm32/slash
    1458     74/jump-if-=  $emit-metadata:emit/disp8
    1459     # ++curr
    1460     41/increment-ecx
    1461     eb/jump  $emit-metadata:skip-datum-loop/disp8
    1462 $emit-metadata:emit:
    1463     # slice->start = ecx
    1464     89/copy                         0/mod/indirect  3/rm32/ebx    .           .             .           1/r32/ecx   .               .                 # copy ecx to *ebx
    1465     # write-slice-buffered(out, slice)
    1466     # . . push args
    1467     53/push-ebx
    1468     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    1469     # . . call
    1470     e8/call  write-slice-buffered/disp32
    1471     # . . discard args
    1472     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           8/imm32      .                    # add to esp
    1473 $emit-metadata:end:
    1474     # . reclaim locals
    1475     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           8/imm32      .                    # add to esp
    1476     # . restore registers
    1477     5e/pop-to-esi
    1478     5b/pop-to-ebx
    1479     5a/pop-to-edx
    1480     59/pop-to-ecx
    1481     58/pop-to-eax
    1482     # . epilogue
    1483     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1484     5d/pop-to-ebp
    1485     c3/return
    1486
    1487 test-emit-metadata:
    1488     # . prologue
    1489     55/push-ebp
    1490     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1491     # setup
    1492     # . clear-stream(_test-output-stream)
    1493     # . . push args
    1494     68/push  _test-output-stream/imm32
    1495     # . . call
    1496     e8/call  clear-stream/disp32
    1497     # . . discard args
    1498     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1499     # . clear-stream($_test-output-buffered-file->buffer)
    1500     # . . push args
    1501     68/push  $_test-output-buffered-file->buffer/imm32
    1502     # . . call
    1503     e8/call  clear-stream/disp32
    1504     # . . discard args
    1505     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1506     # (eax..ecx) = "abc/def"
    1507     b8/copy-to-eax  "abc/def"/imm32
    1508     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
    1509     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
    1510     05/add-to-eax  4/imm32
    1511     # var slice/ecx = {eax, ecx}
    1512     51/push-ecx
    1513     50/push-eax
    1514     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
    1515     # emit-metadata(_test-output-buffered-file, slice)
    1516     # . . push args
    1517     51/push-ecx
    1518     68/push  _test-output-buffered-file/imm32
    1519     # . . call
    1520     e8/call  emit-metadata/disp32
    1521     # . . discard args
    1522     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1523     # flush(_test-output-buffered-file)
    1524     # . . push args
    1525     68/push  _test-output-buffered-file/imm32
    1526     # . . call
    1527     e8/call  flush/disp32
    1528     # . . discard args
    1529     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1530     # check-stream-equal(_test-output-stream, "/def", msg)  # important that there's no leading space
    1531     # . . push args
    1532     68/push  "F - test-emit-metadata"/imm32
    1533     68/push  "/def"/imm32
    1534     68/push  _test-output-stream/imm32
    1535     # . . call
    1536     e8/call  check-stream-equal/disp32
    1537     # . . discard args
    1538     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1539     # . epilogue
    1540     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1541     5d/pop-to-ebp
    1542     c3/return
    1543
    1544 test-emit-metadata-none:
    1545     # . prologue
    1546     55/push-ebp
    1547     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1548     # setup
    1549     # . clear-stream(_test-output-stream)
    1550     # . . push args
    1551     68/push  _test-output-stream/imm32
    1552     # . . call
    1553     e8/call  clear-stream/disp32
    1554     # . . discard args
    1555     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1556     # . clear-stream($_test-output-buffered-file->buffer)
    1557     # . . push args
    1558     68/push  $_test-output-buffered-file->buffer/imm32
    1559     # . . call
    1560     e8/call  clear-stream/disp32
    1561     # . . discard args
    1562     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1563     # (eax..ecx) = "abc"
    1564     b8/copy-to-eax  "abc"/imm32
    1565     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
    1566     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
    1567     05/add-to-eax  4/imm32
    1568     # var slice/ecx = {eax, ecx}
    1569     51/push-ecx
    1570     50/push-eax
    1571     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
    1572     # emit-metadata(_test-output-buffered-file, slice)
    1573     # . . push args
    1574     51/push-ecx
    1575     68/push  _test-output-buffered-file/imm32
    1576     # . . call
    1577     e8/call  emit-metadata/disp32
    1578     # . . discard args
    1579     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1580     # flush(_test-output-buffered-file)
    1581     # . . push args
    1582     68/push  _test-output-buffered-file/imm32
    1583     # . . call
    1584     e8/call  flush/disp32
    1585     # . . discard args
    1586     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1587     # check-stream-equal(_test-output-stream, "", msg)
    1588     # . . push args
    1589     68/push  "F - test-emit-metadata-none"/imm32
    1590     68/push  ""/imm32
    1591     68/push  _test-output-stream/imm32
    1592     # . . call
    1593     e8/call  check-stream-equal/disp32
    1594     # . . discard args
    1595     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1596     # . epilogue
    1597     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1598     5d/pop-to-ebp
    1599     c3/return
    1600
    1601 test-emit-metadata-multiple:
    1602     # . prologue
    1603     55/push-ebp
    1604     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1605     # setup
    1606     # . clear-stream(_test-output-stream)
    1607     # . . push args
    1608     68/push  _test-output-stream/imm32
    1609     # . . call
    1610     e8/call  clear-stream/disp32
    1611     # . . discard args
    1612     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1613     # . clear-stream($_test-output-buffered-file->buffer)
    1614     # . . push args
    1615     68/push  $_test-output-buffered-file->buffer/imm32
    1616     # . . call
    1617     e8/call  clear-stream/disp32
    1618     # . . discard args
    1619     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1620     # (eax..ecx) = "abc/def/ghi"
    1621     b8/copy-to-eax  "abc/def/ghi"/imm32
    1622     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
    1623     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
    1624     05/add-to-eax  4/imm32
    1625     # var slice/ecx = {eax, ecx}
    1626     51/push-ecx
    1627     50/push-eax
    1628     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
    1629     # emit-metadata(_test-output-buffered-file, slice)
    1630     # . . push args
    1631     51/push-ecx
    1632     68/push  _test-output-buffered-file/imm32
    1633     # . . call
    1634     e8/call  emit-metadata/disp32
    1635     # . . discard args
    1636     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1637     # flush(_test-output-buffered-file)
    1638     # . . push args
    1639     68/push  _test-output-buffered-file/imm32
    1640     # . . call
    1641     e8/call  flush/disp32
    1642     # . . discard args
    1643     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1644     # check-stream-equal(_test-output-stream, "/def/ghi", msg)  # important that there's no leading space
    1645     # . . push args
    1646     68/push  "F - test-emit-metadata-multiple"/imm32
    1647     68/push  "/def/ghi"/imm32
    1648     68/push  _test-output-stream/imm32
    1649     # . . call
    1650     e8/call  check-stream-equal/disp32
    1651     # . . discard args
    1652     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1653     # . epilogue
    1654     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1655     5d/pop-to-ebp
    1656     c3/return
    1657
    1658 test-emit-metadata-when-no-datum:
    1659     # . prologue
    1660     55/push-ebp
    1661     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1662     # setup
    1663     # . clear-stream(_test-output-stream)
    1664     # . . push args
    1665     68/push  _test-output-stream/imm32
    1666     # . . call
    1667     e8/call  clear-stream/disp32
    1668     # . . discard args
    1669     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1670     # . clear-stream($_test-output-buffered-file->buffer)
    1671     # . . push args
    1672     68/push  $_test-output-buffered-file->buffer/imm32
    1673     # . . call
    1674     e8/call  clear-stream/disp32
    1675     # . . discard args
    1676     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1677     # var slice/ecx = "/abc"
    1678     b8/copy-to-eax  "/abc"/imm32
    1679     # . push end/ecx
    1680     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
    1681     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
    1682     51/push-ecx
    1683     # . push curr/eax
    1684     05/add-to-eax  4/imm32
    1685     50/push-eax
    1686     # . save stack pointer
    1687     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
    1688     # emit-metadata(_test-output-buffered-file, slice)
    1689     # . . push args
    1690     51/push-ecx
    1691     68/push  _test-output-buffered-file/imm32
    1692     # . . call
    1693     e8/call  emit-metadata/disp32
    1694     # . . discard args
    1695     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1696     # flush(_test-output-buffered-file)
    1697     # . . push args
    1698     68/push  _test-output-buffered-file/imm32
    1699     # . . call
    1700     e8/call  flush/disp32
    1701     # . . discard args
    1702     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1703     # check-stream-equal(_test-output-stream, "/abc", msg)  # nothing skipped
    1704     # . . push args
    1705     68/push  "F - test-emit-metadata-when-no-datum"/imm32
    1706     68/push  "/abc"/imm32
    1707     68/push  _test-output-stream/imm32
    1708     # . . call
    1709     e8/call  check-stream-equal/disp32
    1710     # . . discard args
    1711     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1712     # . epilogue
    1713     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1714     5d/pop-to-ebp
    1715     c3/return
    1716
    1717 test-emit-metadata-in-string-literal:
    1718     # . prologue
    1719     55/push-ebp
    1720     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1721     # setup
    1722     # . clear-stream(_test-output-stream)
    1723     # . . push args
    1724     68/push  _test-output-stream/imm32
    1725     # . . call
    1726     e8/call  clear-stream/disp32
    1727     # . . discard args
    1728     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1729     # . clear-stream($_test-output-buffered-file->buffer)
    1730     # . . push args
    1731     68/push  $_test-output-buffered-file->buffer/imm32
    1732     # . . call
    1733     e8/call  clear-stream/disp32
    1734     # . . discard args
    1735     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1736     # var slice/ecx = "\"abc/def\"/ghi"
    1737     68/push  _test-slice-literal-string-with-limit/imm32
    1738     68/push  _test-slice-literal-string/imm32/start
    1739     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
    1740     # emit-metadata(_test-output-buffered-file, slice)
    1741     # . . push args
    1742     51/push-ecx
    1743     68/push  _test-output-buffered-file/imm32
    1744     # . . call
    1745     e8/call  emit-metadata/disp32
    1746     # . . discard args
    1747     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1748     # flush(_test-output-buffered-file)
    1749     # . . push args
    1750     68/push  _test-output-buffered-file/imm32
    1751     # . . call
    1752     e8/call  flush/disp32
    1753     # . . discard args
    1754     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1755 +-- 26 lines: #?     # dump output ------------------------------------------------------------------------------------------------------------------------------------------------------
    1781     # check-stream-equal(_test-output-stream, "/ghi", msg)  # important that there's no leading space
    1782     # . . push args
    1783     68/push  "F - test-emit-metadata-in-string-literal"/imm32
    1784     68/push  "/ghi"/imm32
    1785     68/push  _test-output-stream/imm32
    1786     # . . call
    1787     e8/call  check-stream-equal/disp32
    1788     # . . discard args
    1789     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1790     # . epilogue
    1791     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1792     5d/pop-to-ebp
    1793     c3/return
    1794
    1795 string-length-at-start-of-slice:  # curr: (addr byte), end: (addr byte) -> length/eax
    1796     # . prologue
    1797     55/push-ebp
    1798     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1799     # . save registers
    1800     51/push-ecx
    1801     52/push-edx
    1802     53/push-ebx
    1803     # ecx = curr
    1804     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
    1805     # edx = end
    1806     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         2/r32/edx   0xc/disp8         .               # copy *(ebp+12) to edx
    1807     # var length/eax: int = 0
    1808     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
    1809     # var c/ebx: byte = 0
    1810     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
    1811     # skip initial dquote
    1812     41/increment-ecx
    1813 $string-length-at-start-of-slice:loop:
    1814     # if (curr >= end) return length
    1815     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
    1816     73/jump-if-addr>=  $string-length-at-start-of-slice:end/disp8
    1817     # c = *curr
    1818     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           3/r32/BL    .               .                 # copy byte at *ecx to BL
    1819 $string-length-at-start-of-slice:dquote:
    1820     # if (c == '"') break
    1821     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x22/imm32/dquote # compare ebx
    1822     74/jump-if-=  $string-length-at-start-of-slice:end/disp8
    1823 $string-length-at-start-of-slice:check-for-escape:
    1824     # if (c == '\') escape next char
    1825     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x5c/imm32/backslash # compare ebx
    1826     75/jump-if-!=  $string-length-at-start-of-slice:continue/disp8
    1827 $string-length-at-start-of-slice:escape:
    1828     # increment curr but not result
    1829     41/increment-ecx
    1830 $string-length-at-start-of-slice:continue:
    1831     # ++result
    1832     40/increment-eax
    1833     # ++curr
    1834     41/increment-ecx
    1835     eb/jump  $string-length-at-start-of-slice:loop/disp8
    1836 $string-length-at-start-of-slice:end:
    1837     # . restore registers
    1838     5b/pop-to-ebx
    1839     5a/pop-to-edx
    1840     59/pop-to-ecx
    1841     # . epilogue
    1842     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1843     5d/pop-to-ebp
    1844     c3/return
    1845
    1846 test-string-length-at-start-of-slice:
    1847     # . prologue
    1848     55/push-ebp
    1849     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1850     # setup: (eax..ecx) = "\"abc\" def"
    1851     b8/copy-to-eax  "\"abc\" def"/imm32
    1852     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
    1853     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
    1854     05/add-to-eax  4/imm32
    1855     # eax = string-length-at-start-of-slice(eax, ecx)
    1856     # . . push args
    1857     51/push-ecx
    1858     50/push-eax
    1859     # . . call
    1860     e8/call  string-length-at-start-of-slice/disp32
    1861     # . . discard args
    1862     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1863     # check-ints-equal(eax, 3, msg)
    1864     # . . push args
    1865     68/push  "F - test-string-length-at-start-of-slice"/imm32
    1866     68/push  3/imm32
    1867     50/push-eax
    1868     # . . call
    1869     e8/call  check-ints-equal/disp32
    1870     # . . discard args
    1871     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1872     # . epilogue
    1873     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1874     5d/pop-to-ebp
    1875     c3/return
    1876
    1877 test-string-length-at-start-of-slice-escaped:
    1878     # . prologue
    1879     55/push-ebp
    1880     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1881     # setup: (eax..ecx) = "\"ab\\c\" def"
    1882     b8/copy-to-eax  "\"ab\\c\" def"/imm32
    1883     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
    1884     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
    1885     05/add-to-eax  4/imm32
    1886     # eax = string-length-at-start-of-slice(eax, ecx)
    1887     # . . push args
    1888     51/push-ecx
    1889     50/push-eax
    1890     # . . call
    1891     e8/call  string-length-at-start-of-slice/disp32
    1892     # . . discard args
    1893     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1894     # check-ints-equal(eax, 3, msg)
    1895     # . . push args
    1896     68/push  "F - test-string-length-at-start-of-slice-escaped"/imm32
    1897     68/push  3/imm32
    1898     50/push-eax
    1899     # . . call
    1900     e8/call  check-ints-equal/disp32
    1901     # . . discard args
    1902     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1903     # . epilogue
    1904     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1905     5d/pop-to-ebp
    1906     c3/return
    1907
    1908 == data
    1909
    1910 Next-string-literal:  # tracks the next auto-generated variable name
    1911   1/imm32
    1912
    1913 _test-slice-abc:
    1914   22/dquote 61/a 62/b 63/c 22/dquote  # "abc"
    1915   2f/slash 64/d
    1916 _test-slice-abc-limit:
    1917
    1918 _test-slice-a-space-b:
    1919   22/dquote 61/a 20/space 62/b 22/dquote  # "a b"
    1920 _test-slice-a-space-b-limit:
    1921
    1922 _test-slice-a-dquote-b:
    1923   22/dquote 61/a 5c/backslash 22/dquote 62/b 22/dquote  # "a\"b"
    1924 _test-slice-a-dquote-b-limit:
    1925
    1926 _test-slice-a-newline-b:
    1927   22/dquote 61/a 5c/backslash 6e/n 62/b 22/dquote  # "a\nb"
    1928 _test-slice-a-newline-b-limit:
    1929
    1930 # "abc/def"/ghi
    1931 _test-slice-literal-string:
    1932   22/dquote
    1933   61/a 62/b 63/c  # abc
    1934   2f/slash 64/d 65/e 66/f  # /def
    1935   22/dquote
    1936   2f/slash 67/g 68/h 69/i  # /ghi
    1937 _test-slice-literal-string-with-limit:
    1938
    1939 # . . vim:nowrap:textwidth=0