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