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-common.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 run-main
  38     81          7/subop/compare     1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0/disp8         1/imm32           # compare *ebp
  39     7e/jump-if-lesser-or-equal  $run-main/disp8
  40     # if (!kernel-string-equal?(argv[1], "test")) goto run-main
  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 run-main
  50     3d/compare-eax-and  0/imm32
  51     74/jump-if-equal  $run-main/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  $main:end/disp8
  57 $run-main:
  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     # convert(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  convert/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 $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 convert:  # 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 $convert: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 $convert: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  $convert:break/disp32
 169 $convert: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 $convert: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  $convert:next-line/disp32
 190 $convert: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  $convert:word-loop/disp8
 200 $convert: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  $convert:regular-word/disp8
 204 $convert: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  $convert:next-word/disp8
 216 $convert: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 $convert:next-word:
 227     # write-buffered(out, " ")
 228     # . . push args
 229     68/push  " "/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  $convert:word-loop/disp8
 237 $convert: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  $convert:line-loop/disp32
 248 $convert: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 $convert: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-convert-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  "\n"/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     # convert(_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  convert/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-convert-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-convert-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-convert-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-convert-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-convert-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-convert-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-convert-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-convert-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-convert-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-convert-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     # convert(_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  convert/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-convert-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-convert-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-convert-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-convert-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-convert-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-convert-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-convert-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-convert-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 # (re)compute the bounds of the next word or string literal in the line
1791 # return empty string on reaching end of file
1792 next-word-or-string:  # line : (address stream byte), out : (address slice)
1793     # . prolog
1794     55/push-ebp
1795     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1796     # . save registers
1797     50/push-eax
1798     51/push-ecx
1799     56/push-esi
1800     57/push-edi
1801     # esi = line
1802     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
1803     # edi = out
1804     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           7/r32/edi   0xc/disp8       .                 # copy *(ebp+12) to edi
1805     # skip-chars-matching(line, ' ')
1806     # . . push args
1807     68/push  0x20/imm32/space
1808     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1809     # . . call
1810     e8/call  skip-chars-matching/disp32
1811     # . . discard args
1812     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1813 $next-word-or-string:check0:
1814     # if (line->read >= line->write) clear out and return
1815     # . eax = line->read
1816     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           0/r32/eax   4/disp8         .                 # copy *(esi+4) to eax
1817     # . if (eax < line->write) goto next check
1818     3b/compare                      0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # compare eax with *esi
1819     7c/jump-if-lesser  $next-word-or-string:check-for-comment/disp8
1820     # . return out = {0, 0}
1821     c7          0/subop/copy        0/mod/direct    7/rm32/edi    .           .             .           .           .               0/imm32           # copy to *edi
1822     c7          0/subop/copy        1/mod/*+disp8   7/rm32/edi    .           .             .           .           4/disp8         0/imm32           # copy to *(edi+4)
1823     eb/jump  $next-word-or-string:end/disp8
1824 $next-word-or-string:check-for-comment:
1825     # out->start = &line->data[line->read]
1826     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(esi+4) to ecx
1827     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/esi  1/index/ecx   .           0/r32/eax   0xc/disp8       .                 # copy esi+ecx+12 to eax
1828     89/copy                         0/mod/indirect  7/rm32/edi    .           .             .           0/r32/eax   .               .                 # copy eax to *edi
1829     # if (line->data[line->read] != '#') goto next check
1830     # . eax = line->data[line->read]
1831     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
1832     8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    6/base/esi  1/index/ecx   .           0/r32/AL    0xc/disp8       .                 # copy byte at *(esi+ecx+12) to AL
1833     # . compare
1834     3d/compare-eax-and  0x23/imm32/pound
1835     75/jump-if-not-equal  $next-word-or-string:check-for-string-literal/disp8
1836 $next-word-or-string:comment:
1837     # out->end = &line->data[line->write]
1838     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
1839     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/esi  0/index/eax   .           0/r32/eax   0xc/disp8       .                 # copy esi+eax+12 to eax
1840     89/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           0/r32/eax   4/disp8         .                 # copy eax to *(edi+4)
1841     # line->read = line->write  # skip rest of line
1842     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
1843     89/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           0/r32/eax   4/disp8         .                 # copy eax to *(esi+4)
1844     # return
1845     eb/jump  $next-word-or-string:end/disp8
1846 $next-word-or-string:check-for-string-literal:
1847     # if (line->data[line->read] != '"') goto next check
1848     # . eax = line->data[line->read]
1849     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
1850     8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    6/base/esi  1/index/ecx   .           0/r32/AL    0xc/disp8       .                 # copy byte at *(esi+ecx+12) to AL
1851     # . compare
1852     3d/compare-eax-and  0x22/imm32/dquote
1853     75/jump-if-not-equal  $next-word-or-string:regular-word/disp8
1854 $next-word-or-string:string-literal:
1855     # skip-string(line)
1856     # . . push args
1857     56/push-esi
1858     # . . call
1859     e8/call  skip-string/disp32
1860     # . . discard args
1861     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1862     # fall through
1863 $next-word-or-string:regular-word:
1864     # skip-chars-not-matching-whitespace(line)  # including trailing newline
1865     # . . push args
1866     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1867     # . . call
1868     e8/call  skip-chars-not-matching-whitespace/disp32
1869     # . . discard args
1870     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1871     # out->end = &line->data[line->read]
1872     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(esi+4) to ecx
1873     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/esi  1/index/ecx   .           0/r32/eax   0xc/disp8       .                 # copy esi+ecx+12 to eax
1874     89/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           0/r32/eax   4/disp8         .                 # copy eax to *(edi+4)
1875 $next-word-or-string:end:
1876     # . restore registers
1877     5f/pop-to-edi
1878     5e/pop-to-esi
1879     59/pop-to-ecx
1880     58/pop-to-eax
1881     # . epilog
1882     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1883     5d/pop-to-ebp
1884     c3/return
1885 
1886 test-next-word-or-string:
1887     # . prolog
1888     55/push-ebp
1889     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1890     # setup
1891     # . clear-stream(_test-input-stream)
1892     # . . push args
1893     68/push  _test-input-stream/imm32
1894     # . . call
1895     e8/call  clear-stream/disp32
1896     # . . discard args
1897     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1898     # var slice/ecx = {0, 0}
1899     68/push  0/imm32/end
1900     68/push  0/imm32/start
1901     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1902     # write(_test-input-stream, "  ab")
1903     # . . push args
1904     68/push  "  ab"/imm32
1905     68/push  _test-input-stream/imm32
1906     # . . call
1907     e8/call  write/disp32
1908     # . . discard args
1909     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1910     # next-word-or-string(_test-input-stream, slice)
1911     # . . push args
1912     51/push-ecx
1913     68/push  _test-input-stream/imm32
1914     # . . call
1915     e8/call  next-word-or-string/disp32
1916     # . . discard args
1917     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1918     # check-ints-equal(_test-input-stream->read, 4, msg)
1919     # . . push args
1920     68/push  "F - test-next-word-or-string/updates-stream-read-correctly"/imm32
1921     68/push  4/imm32
1922     b8/copy-to-eax  _test-input-stream/imm32
1923     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1924     # . . call
1925     e8/call  check-ints-equal/disp32
1926     # . . discard args
1927     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1928     # check-ints-equal(slice->start - _test-input-stream->data, 2, msg)
1929     # . check-ints-equal(slice->start - _test-input-stream, 14, msg)
1930     # . . push args
1931     68/push  "F - test-next-word-or-string: start"/imm32
1932     68/push  0xe/imm32
1933     # . . push slice->start - _test-input-stream
1934     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
1935     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
1936     50/push-eax
1937     # . . call
1938     e8/call  check-ints-equal/disp32
1939     # . . discard args
1940     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1941     # check-ints-equal(slice->end - _test-input-stream->data, 4, msg)
1942     # . check-ints-equal(slice->end - _test-input-stream, 16, msg)
1943     # . . push args
1944     68/push  "F - test-next-word-or-string: end"/imm32
1945     68/push  0x10/imm32
1946     # . . push slice->end - _test-input-stream
1947     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
1948     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
1949     50/push-eax
1950     # . . call
1951     e8/call  check-ints-equal/disp32
1952     # . . discard args
1953     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1954     # . epilog
1955     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1956     5d/pop-to-ebp
1957     c3/return
1958 
1959 test-next-word-or-string-returns-whole-comment:
1960     # . prolog
1961     55/push-ebp
1962     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1963     # setup
1964     # . clear-stream(_test-input-stream)
1965     # . . push args
1966     68/push  _test-input-stream/imm32
1967     # . . call
1968     e8/call  clear-stream/disp32
1969     # . . discard args
1970     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1971     # var slice/ecx = {0, 0}
1972     68/push  0/imm32/end
1973     68/push  0/imm32/start
1974     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1975     # write(_test-input-stream, "  # a")
1976     # . . push args
1977     68/push  "  # a"/imm32
1978     68/push  _test-input-stream/imm32
1979     # . . call
1980     e8/call  write/disp32
1981     # . . discard args
1982     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1983     # next-word-or-string(_test-input-stream, slice)
1984     # . . push args
1985     51/push-ecx
1986     68/push  _test-input-stream/imm32
1987     # . . call
1988     e8/call  next-word-or-string/disp32
1989     # . . discard args
1990     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1991     # check-ints-equal(_test-input-stream->read, 5, msg)
1992     # . . push args
1993     68/push  "F - test-next-word-or-string-returns-whole-comment/updates-stream-read-correctly"/imm32
1994     68/push  5/imm32
1995     b8/copy-to-eax  _test-input-stream/imm32
1996     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1997     # . . call
1998     e8/call  check-ints-equal/disp32
1999     # . . discard args
2000     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2001     # check-ints-equal(slice->start - _test-input-stream->data, 2, msg)
2002     # . check-ints-equal(slice->start - _test-input-stream, 14, msg)
2003     # . . push args
2004     68/push  "F - test-next-word-or-string-returns-whole-comment: start"/imm32
2005     68/push  0xe/imm32
2006     # . . push slice->start - _test-input-stream
2007     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
2008     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
2009     50/push-eax
2010     # . . call
2011     e8/call  check-ints-equal/disp32
2012     # . . discard args
2013     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2014     # check-ints-equal(slice->end - _test-input-stream->data, 5, msg)
2015     # . check-ints-equal(slice->end - _test-input-stream, 17, msg)
2016     # . . push args
2017     68/push  "F - test-next-word-or-string-returns-whole-comment: end"/imm32
2018     68/push  0x11/imm32
2019     # . . push slice->end - _test-input-stream
2020     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
2021     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
2022     50/push-eax
2023     # . . call
2024     e8/call  check-ints-equal/disp32
2025     # . . discard args
2026     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2027     # . epilog
2028     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2029     5d/pop-to-ebp
2030     c3/return
2031 
2032 test-next-word-or-string-returns-empty-slice-on-eof:
2033     # . prolog
2034     55/push-ebp
2035     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2036     # setup
2037     # . clear-stream(_test-input-stream)
2038     # . . push args
2039     68/push  _test-input-stream/imm32
2040     # . . call
2041     e8/call  clear-stream/disp32
2042     # . . discard args
2043     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2044     # var slice/ecx = {0, 0}
2045     68/push  0/imm32/end
2046     68/push  0/imm32/start
2047     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2048     # write nothing to _test-input-stream
2049     # next-word-or-string(_test-input-stream, slice)
2050     # . . push args
2051     51/push-ecx
2052     68/push  _test-input-stream/imm32
2053     # . . call
2054     e8/call  next-word-or-string/disp32
2055     # . . discard args
2056     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2057     # check-ints-equal(slice->end - slice->start, 0, msg)
2058     # . . push args
2059     68/push  "F - test-next-word-or-string-returns-empty-string-on-eof"/imm32
2060     68/push  0/imm32
2061     # . . push slice->end - slice->start
2062     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
2063     2b/subtract                     0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract *ecx from eax
2064     50/push-eax
2065     # . . call
2066     e8/call  check-ints-equal/disp32
2067     # . . discard args
2068     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2069     # . epilog
2070     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2071     5d/pop-to-ebp
2072     c3/return
2073 
2074 test-next-word-or-string-returns-string-literal:
2075     # . prolog
2076     55/push-ebp
2077     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2078     # setup
2079     # . clear-stream(_test-input-stream)
2080     # . . push args
2081     68/push  _test-input-stream/imm32
2082     # . . call
2083     e8/call  clear-stream/disp32
2084     # . . discard args
2085     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2086     # var slice/ecx = {0, 0}
2087     68/push  0/imm32/end
2088     68/push  0/imm32/start
2089     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2090     # write(_test-input-stream, " \"a b\"/imm32 ")
2091     # . . push args
2092     68/push  " \"a b\"/imm32 "/imm32
2093     68/push  _test-input-stream/imm32
2094     # . . call
2095     e8/call  write/disp32
2096     # . . discard args
2097     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2098     # next-word-or-string(_test-input-stream, slice)
2099     # . . push args
2100     51/push-ecx
2101     68/push  _test-input-stream/imm32
2102     # . . call
2103     e8/call  next-word-or-string/disp32
2104     # . . discard args
2105     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2106     # check-ints-equal(slice->start - _test-input-stream->data, 1, msg)
2107     # . check-ints-equal(slice->start - _test-input-stream, 13, msg)
2108     # . . push args
2109     68/push  "F - test-next-word-or-string-returns-string-literal: start"/imm32
2110     68/push  0xd/imm32
2111     # . . push slice->start - _test-input-stream
2112     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
2113     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
2114     50/push-eax
2115     # . . call
2116     e8/call  check-ints-equal/disp32
2117     # . . discard args
2118     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2119     # check-ints-equal(slice->end - _test-input-stream->data, 12, msg)
2120     # . check-ints-equal(slice->end - _test-input-stream, 24, msg)
2121     # . . push args
2122     68/push  "F - test-next-word-or-string-returns-string-literal: end"/imm32
2123     68/push  0x18/imm32
2124     # . . push slice->end - _test-input-stream
2125     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
2126     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
2127     50/push-eax
2128     # . . call
2129     e8/call  check-ints-equal/disp32
2130     # . . discard args
2131     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2132     # . epilog
2133     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2134     5d/pop-to-ebp
2135     c3/return
2136 
2137 test-next-word-or-string-returns-string-with-escapes:
2138     # . prolog
2139     55/push-ebp
2140     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2141     # setup
2142     # . clear-stream(_test-input-stream)
2143     # . . push args
2144     68/push  _test-input-stream/imm32
2145     # . . call
2146     e8/call  clear-stream/disp32
2147     # . . discard args
2148     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2149     # var slice/ecx = {0, 0}
2150     68/push  0/imm32/end
2151     68/push  0/imm32/start
2152     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2153     # write(_test-input-stream, " \"a\\\"b\"/x")
2154     # . . push args
2155     68/push  " \"a\\\"b\"/x"/imm32
2156     68/push  _test-input-stream/imm32
2157     # . . call
2158     e8/call  write/disp32
2159     # . . discard args
2160     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2161     # next-word-or-string(_test-input-stream, slice)
2162     # . . push args
2163     51/push-ecx
2164     68/push  _test-input-stream/imm32
2165     # . . call
2166     e8/call  next-word-or-string/disp32
2167     # . . discard args
2168     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2169     # check-ints-equal(slice->start - _test-input-stream->data, 1, msg)
2170     # . check-ints-equal(slice->start - _test-input-stream, 13, msg)
2171     # . . push args
2172     68/push  "F - test-next-word-or-string-returns-string-with-escapes: start"/imm32
2173     68/push  0xd/imm32
2174     # . . push slice->start - _test-input-stream
2175     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
2176     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
2177     50/push-eax
2178     # . . call
2179     e8/call  check-ints-equal/disp32
2180     # . . discard args
2181     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2182     # check-ints-equal(slice->end - _test-input-stream->data, 9, msg)
2183     # . check-ints-equal(slice->end - _test-input-stream, 21, msg)
2184     # . . push args
2185     68/push  "F - test-next-word-or-string-returns-string-with-escapes: end"/imm32
2186     68/push  0x15/imm32
2187     # . . push slice->end - _test-input-stream
2188     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
2189     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
2190     50/push-eax
2191     # . . call
2192     e8/call  check-ints-equal/disp32
2193     # . . discard args
2194     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2195     # . epilog
2196     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2197     5d/pop-to-ebp
2198     c3/return
2199 
2200 string-length-at-start-of-slice:  # curr : (address byte), end : (address byte) -> length/eax
2201     # . prolog
2202     55/push-ebp
2203     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2204     # . save registers
2205     51/push-ecx
2206     52/push-edx
2207     53/push-ebx
2208     # ecx = curr
2209     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
2210     # edx = end
2211     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         2/r32/edx   0xc/disp8         .               # copy *(ebp+12) to edx
2212     # length/eax = 0
2213     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
2214     # ebx = 0
2215     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
2216     # skip initial dquote
2217     41/increment-ecx
2218 $string-length-at-start-of-slice:loop:
2219     # if (curr >= end) return length
2220     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
2221     73/jump-if-greater-unsigned-or-equal  $string-length-at-start-of-slice:end/disp8
2222     # BL = *curr
2223     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           3/r32/BL    .               .                 # copy byte at *ecx to BL
2224 $string-length-at-start-of-slice:dquote:
2225     # if (ebx == '"') break
2226     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x22/imm32/dquote # compare ebx
2227     74/jump-if-equal  $string-length-at-start-of-slice:end/disp8
2228 $string-length-at-start-of-slice:check-for-escape:
2229     # if (ebx == '\') escape next char
2230     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x5c/imm32/backslash # compare ebx
2231     75/jump-if-not-equal  $string-length-at-start-of-slice:continue/disp8
2232 $string-length-at-start-of-slice:escape:
2233     # increment curr but not result
2234     41/increment-ecx
2235 $string-length-at-start-of-slice:continue:
2236     # ++result
2237     40/increment-eax
2238     # ++curr
2239     41/increment-ecx
2240     eb/jump  $string-length-at-start-of-slice:loop/disp8
2241 $string-length-at-start-of-slice:end:
2242     # . restore registers
2243     5b/pop-to-ebx
2244     5a/pop-to-edx
2245     59/pop-to-ecx
2246     # . epilog
2247     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2248     5d/pop-to-ebp
2249     c3/return
2250 
2251 test-string-length-at-start-of-slice:
2252     # . prolog
2253     55/push-ebp
2254     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2255     # setup: (eax..ecx) = "\"abc\" def"
2256     b8/copy-to-eax  "\"abc\" def"/imm32
2257     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
2258     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
2259     05/add-to-eax  4/imm32
2260     # eax = string-length-at-start-of-slice(eax, ecx)
2261     # . . push args
2262     51/push-ecx
2263     50/push-eax
2264     # . . call
2265     e8/call  string-length-at-start-of-slice/disp32
2266     # . . discard args
2267     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2268     # check-ints-equal(eax, 3, msg)
2269     # . . push args
2270     68/push  "F - test-string-length-at-start-of-slice"/imm32
2271     68/push  3/imm32
2272     50/push-eax
2273     # . . call
2274     e8/call  check-ints-equal/disp32
2275     # . . discard args
2276     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2277     # . epilog
2278     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2279     5d/pop-to-ebp
2280     c3/return
2281 
2282 test-string-length-at-start-of-slice-escaped:
2283     # . prolog
2284     55/push-ebp
2285     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2286     # setup: (eax..ecx) = "\"ab\\c\" def"
2287     b8/copy-to-eax  "\"ab\\c\" def"/imm32
2288     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
2289     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
2290     05/add-to-eax  4/imm32
2291     # eax = string-length-at-start-of-slice(eax, ecx)
2292     # . . push args
2293     51/push-ecx
2294     50/push-eax
2295     # . . call
2296     e8/call  string-length-at-start-of-slice/disp32
2297     # . . discard args
2298     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2299     # check-ints-equal(eax, 3, msg)
2300     # . . push args
2301     68/push  "F - test-string-length-at-start-of-slice-escaped"/imm32
2302     68/push  3/imm32
2303     50/push-eax
2304     # . . call
2305     e8/call  check-ints-equal/disp32
2306     # . . discard args
2307     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2308     # . epilog
2309     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2310     5d/pop-to-ebp
2311     c3/return
2312 
2313 == data
2314 
2315 Next-string-literal:  # tracks the next auto-generated variable name
2316   1/imm32
2317 
2318 # length-prefixed string containing just a single space
2319 Space:
2320     # size
2321     1/imm32
2322     # data
2323     20/space
2324 
2325 # length-prefixed string containing just a single slash
2326 Slash:
2327     # size
2328     1/imm32
2329     # data
2330     2f/slash
2331 
2332 _test-slice-abc:
2333   22/dquote 61/a 62/b 63/c 22/dquote  # "abc"
2334   2f/slash 64/d
2335 _test-slice-abc-limit:
2336 
2337 _test-slice-a-space-b:
2338   22/dquote 61/a 20/space 62/b 22/dquote  # "a b"
2339 _test-slice-a-space-b-limit:
2340 
2341 _test-slice-a-dquote-b:
2342   22/dquote 61/a 5c/backslash 22/dquote 62/b 22/dquote  # "a\"b"
2343 _test-slice-a-dquote-b-limit:
2344 
2345 _test-slice-a-newline-b:
2346   22/dquote 61/a 5c/backslash 6e/n 62/b 22/dquote  # "a\nb"
2347 _test-slice-a-newline-b-limit:
2348 
2349 # "abc/def"/ghi
2350 _test-slice-literal-string:
2351   22/dquote
2352   61/a 62/b 63/c  # abc
2353   2f/slash 64/d 65/e 66/f  # /def
2354   22/dquote
2355   2f/slash 67/g 68/h 69/i  # /ghi
2356 _test-slice-literal-string-with-limit:
2357 
2358 # . . vim:nowrap:textwidth=0