https://github.com/akkartik/mu/blob/master/subx/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 (from the subx/ directory):
   5 #   $ ./subx translate *.subx apps/dquote.subx -o apps/dquote
   6 #   $ cat x
   7 #   == code
   8 #   ab "cd ef"/imm32
   9 #   $ cat x  |./subx run apps/dquote
  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:
  23     # initialize heap
  24     # . Heap = new-segment(64KB)
  25     # . . push args
  26     68/push  Heap/imm32
  27     68/push  0x10000/imm32/64KB
  28     # . . call
  29     e8/call  new-segment/disp32
  30     # . . discard args
  31     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
  32 
  33     # run tests if necessary, convert stdin if not
  34     # . prolog
  35     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
  36     # - if argc > 1 and argv[1] == "test", then return run_tests()
  37     # . argc > 1
  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     # . argv[1] == "test"
  41     # . . push args
  42     68/push  "test"/imm32
  43     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
  44     # . . call
  45     e8/call  kernel-string-equal?/disp32
  46     # . . discard args
  47     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
  48     # . check result
  49     3d/compare-EAX-and  1/imm32
  50     75/jump-if-not-equal  $run-main/disp8
  51     # . run-tests()
  52     e8/call  run-tests/disp32
  53     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
  54     eb/jump  $main:end/disp8
  55 $run-main:
  56     # - otherwise convert stdin
  57     # var ed/EAX : exit-descriptor
  58     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
  59     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EAX
  60     # configure ed to really exit()
  61     # . ed->target = 0
  62     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # copy to *EAX
  63     # return convert(Stdin, 1/stdout, 2/stderr, ed)
  64     # . . push args
  65     50/push-EAX/ed
  66     68/push  Stderr/imm32
  67     68/push  Stdout/imm32
  68     68/push  Stdin/imm32
  69     # . . call
  70     e8/call  convert/disp32
  71     # . . discard args
  72     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
  73     # . syscall(exit, 0)
  74     bb/copy-to-EBX  0/imm32
  75 $main:end:
  76     b8/copy-to-EAX  1/imm32/exit
  77     cd/syscall  0x80/imm8
  78 
  79 # conceptual hierarchy within a line:
  80 #   line = words separated by ' ', maybe followed by comment starting with '#'
  81 #   word = datum until '/', then 0 or more metadata separated by '/'
  82 
  83 convert:  # in : (address buffered-file), out : (address buffered-file) -> <void>
  84     # pseudocode:
  85     #   var line = new-stream(512, 1)
  86     #   var new-data-segment = new-stream(Heap, Segment-size, 1)
  87     #   write-stream(new-data-segment, "== data\n")
  88     #   while true
  89     #     clear-stream(line)
  90     #     read-line-buffered(in, line)
  91     #     if (line->write == 0) break               # end of file
  92     #     while true
  93     #       var word-slice = next-word-or-string(line)
  94     #       if slice-empty?(word-slice)             # end of line
  95     #         break
  96     #       if slice-starts-with?(word-slice, "#")  # comment
  97     #         continue
  98     #       if slice-starts-with?(word-slice, '"')  # string literal <== what we're here for
  99     #         process-string-literal(word-slice, out, new-data-segment)
 100     #       else
 101     #         write-slice-buffered(out, word-slice)
 102     #       write(out, " ")
 103     #     write(out, "\n\n")
 104     #   write-stream-data(out, new-data-segment)
 105     #   flush(out)
 106     #
 107     # . prolog
 108     55/push-EBP
 109     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 110     # . save registers
 111     50/push-EAX
 112     51/push-ECX
 113     52/push-EDX
 114     53/push-EBX
 115     56/push-ESI
 116     57/push-EDI
 117     # var line/ECX : (address stream byte) = stream(512)
 118     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x200/imm32       # subtract from ESP
 119     68/push  0x200/imm32/length
 120     68/push  0/imm32/read
 121     68/push  0/imm32/write
 122     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
 123     # var word-slice/EDX = {0, 0}
 124     68/push  0/imm32/end
 125     68/push  0/imm32/start
 126     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
 127     # new-data-segment/EDI = new-stream(Heap, Segment-size, 1)
 128     # . EAX = new-stream(Heap, Segment-size, 1)
 129     # . . push args
 130     68/push  1/imm32
 131     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Segment-size/disp32               # push *Segment-size
 132     68/push  Heap/imm32
 133     # . . call
 134     e8/call  new-stream/disp32
 135     # . . discard args
 136     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 137     # . EDI = EAX
 138     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to EDI
 139     # write(new-data-segment, "== data\n")
 140     # . . push args
 141     68/push  "== data\n"/imm32
 142     57/push-EDI
 143     # . . call
 144     e8/call  write/disp32
 145     # . . discard args
 146     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 147 $convert:line-loop:
 148     # clear-stream(line)
 149     # . . push args
 150     51/push-ECX
 151     # . . call
 152     e8/call  clear-stream/disp32
 153     # . . discard args
 154     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 155     # read-line-buffered(in, line)
 156     # . . push args
 157     51/push-ECX
 158     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 159     # . . call
 160     e8/call  read-line-buffered/disp32
 161     # . . discard args
 162     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 163 $convert:check0:
 164     # if (line->write == 0) break
 165     81          7/subop/compare     0/mod/indirect  1/rm32/ECX    .           .             .           .           .               0/imm32           # compare *ECX
 166     0f 84/jump-if-equal  $convert:break/disp32
 167 $convert:word-loop:
 168     # next-word-or-string(line, word-slice)
 169     # . . push args
 170     52/push-EDX
 171     51/push-ECX
 172     # . . call
 173     e8/call  next-word-or-string/disp32
 174     # . . discard args
 175     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 176 $convert:check1:
 177     # if (slice-empty?(word-slice)) break
 178     # . EAX = slice-empty?(word-slice)
 179     # . . push args
 180     52/push-EDX
 181     # . . call
 182     e8/call  slice-empty?/disp32
 183     # . . discard args
 184     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 185     # . if (EAX != 0) break
 186     3d/compare-EAX-and  0/imm32
 187     0f 85/jump-if-not-equal  $convert:next-line/disp32
 188 $convert:check-for-comment:
 189     # if (slice-starts-with?(word-slice, "#")) continue
 190     # . start/ESI = word-slice->start
 191     8b/copy                         0/mod/indirect  2/rm32/EDX    .           .             .           6/r32/ESI   .               .                 # copy *EDX to ESI
 192     # . c/EAX = *start
 193     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
 194     8a/copy-byte                    0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/AL    .               .                 # copy byte at *ESI to AL
 195     # . if (EAX == '#') continue
 196     3d/compare-EAX-and  0x23/imm32/hash
 197     74/jump-if-equal  $convert:word-loop/disp8
 198 $convert:check-for-string-literal:
 199     3d/compare-EAX-and  0x22/imm32/hash
 200     75/jump-if-not-equal  $convert:regular-word/disp8
 201 $convert:string-literal:
 202     # process-string-literal(word-slice, out, new-data-segment)
 203     # . . push args
 204     57/push-EDI
 205     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 206     52/push-EDX
 207     # . . call
 208     e8/call  process-string-literal/disp32
 209     # . . discard args
 210     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 211     # continue
 212     eb/jump  $convert:next-word/disp8
 213 $convert:regular-word:
 214     # write-slice-buffered(out, word-slice)
 215     # . . push args
 216     52/push-EDX
 217     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 218     # . . call
 219     e8/call  write-slice-buffered/disp32
 220     # . . discard args
 221     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 222     # fall through
 223 $convert:next-word:
 224     # write-buffered(out, " ")
 225     # . . push args
 226     68/push  " "/imm32
 227     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 228     # . . call
 229     e8/call  write-buffered/disp32
 230     # . . discard args
 231     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 232     # loop
 233     eb/jump  $convert:word-loop/disp8
 234 $convert:next-line:
 235     # write-buffered(out, "\n")
 236     # . . push args
 237     68/push  Newline/imm32
 238     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 239     # . . call
 240     e8/call  write-buffered/disp32
 241     # . . discard args
 242     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 243     # loop
 244     e9/jump  $convert:line-loop/disp32
 245 $convert:break:
 246     # write-stream-data(out, new-data-segment)
 247     # . . push args
 248     57/push-EDI
 249     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 250     # . . call
 251     e8/call  write-stream-data/disp32
 252     # . . discard args
 253     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 254     # flush(out)
 255     # . . push args
 256     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 257     # . . call
 258     e8/call  flush/disp32
 259     # . . discard args
 260     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 261 $convert:end:
 262     # . reclaim locals
 263     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x214/imm32       # add to ESP
 264     # . restore registers
 265     5f/pop-to-EDI
 266     5e/pop-to-ESI
 267     5b/pop-to-EBX
 268     5a/pop-to-EDX
 269     59/pop-to-ECX
 270     58/pop-to-EAX
 271     # . epilog
 272     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 273     5d/pop-to-EBP
 274     c3/return
 275 
 276 # Write out 'string-literal' in a new format to 'out-segment', assign it a new
 277 # label, and write the new label out to 'out'.
 278 process-string-literal:  # string-literal : (address slice), out : (address buffered-file), out-segment : (address stream)
 279     # pseudocode:
 280     #   print(out-segment, "_string#{Next-string-literal}:\n")
 281     #   emit-string-literal-data(out-segment, string-literal)
 282     #   print(out, "_string#{Next-string-literal}")
 283     #   emit-metadata(out, string-literal)
 284     #   ++ *Next-string-literal
 285     #
 286     # . prolog
 287     55/push-EBP
 288     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 289     # . save registers
 290     51/push-ECX
 291     # var int32-stream/ECX = stream(10)  # number of decimal digits a 32-bit number can have
 292     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xa/imm32         # subtract from ESP
 293     68/push  0xa/imm32/decimal-digits-in-32bit-number
 294     68/push  0/imm32/read
 295     68/push  0/imm32/write
 296     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
 297     # print(out-segment, "_string#{Next-string-literal}:\n")
 298     # . write(out-segment, "_string")
 299     # . . push args
 300     68/push  "_string"/imm32
 301     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
 302     # . . call
 303     e8/call  write/disp32
 304     # . . discard args
 305     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 306     # . print-int32-decimal(out-segment, *Next-string-literal)
 307     # . . push args
 308     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Next-string-literal/disp32        # push *Next-string-literal
 309     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
 310     # . . call
 311     e8/call  print-int32-decimal/disp32
 312     # . . discard args
 313     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 314     # . write(out-segment, ":\n")
 315     # . . push args
 316     68/push  ":\n"/imm32
 317     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
 318     # . . call
 319     e8/call  write/disp32
 320     # . . discard args
 321     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 322     # emit-string-literal-data(out-segment, string-literal)
 323     # . . push args
 324     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x8/disp8       .                 # push *(EBP+8)
 325     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
 326     # . . call
 327     e8/call  emit-string-literal-data/disp32
 328     # . . discard args
 329     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 330     # write(out-segment, "\n")
 331     # . . push args
 332     68/push  Newline/imm32
 333     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
 334     # . . call
 335     e8/call  write/disp32
 336     # . . discard args
 337     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 338     # print(out, "_string#{Next-string-literal}")
 339     # . write-buffered(out, "_string")
 340     # . . push args
 341     68/push  "_string"/imm32
 342     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 343     # . . call
 344     e8/call  write-buffered/disp32
 345     # . . discard args
 346     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 347     # . print-int32-decimal(int32-stream, *Next-string-literal)
 348     # . . push args
 349     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Next-string-literal/disp32        # push *Next-string-literal
 350     51/push-ECX
 351     # . . call
 352     e8/call  print-int32-decimal/disp32
 353     # . . discard args
 354     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 355     # . write-stream-data(out, int32-stream)
 356     # . . push args
 357     51/push-ECX
 358     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 359     # . . call
 360     e8/call  write-stream-data/disp32
 361     # . . discard args
 362     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 363     # emit-metadata(out, string-literal)
 364     # . . push args
 365     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 366     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 367     # . . call
 368     e8/call  emit-metadata/disp32
 369     # . . discard args
 370     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 371     # ++ *Next-string-literal
 372     ff          0/subop/increment   0/mod/indirect  5/rm32/.disp32            .             .           .           Next-string-literal/disp32        # increment *Num-test-failures
 373 $process-string-literal:end:
 374     # . reclaim locals
 375     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x16/imm32        # add to ESP
 376     # . restore registers
 377     59/pop-to-ECX
 378     # . epilog
 379     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 380     5d/pop-to-EBP
 381     c3/return
 382 
 383 test-convert-is-idempotent-by-default:
 384     # . prolog
 385     55/push-EBP
 386     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 387     # setup
 388     # . clear-stream(_test-input-stream)
 389     # . . push args
 390     68/push  _test-input-stream/imm32
 391     # . . call
 392     e8/call  clear-stream/disp32
 393     # . . discard args
 394     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 395     # . clear-stream(_test-input-buffered-file+4)
 396     # . . push args
 397     b8/copy-to-EAX  _test-input-buffered-file/imm32
 398     05/add-to-EAX  4/imm32
 399     50/push-EAX
 400     # . . call
 401     e8/call  clear-stream/disp32
 402     # . . discard args
 403     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 404     # . clear-stream(_test-output-stream)
 405     # . . push args
 406     68/push  _test-output-stream/imm32
 407     # . . call
 408     e8/call  clear-stream/disp32
 409     # . . discard args
 410     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 411     # . clear-stream(_test-output-buffered-file+4)
 412     # . . push args
 413     b8/copy-to-EAX  _test-output-buffered-file/imm32
 414     05/add-to-EAX  4/imm32
 415     50/push-EAX
 416     # . . call
 417     e8/call  clear-stream/disp32
 418     # . . discard args
 419     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 420     # initialize input (meta comments in parens)
 421     #   # comment 1
 422     #     # comment 2 indented
 423     #   == code 0x1  (new segment)
 424     #   # comment 3 inside a segment
 425     #   1
 426     #                         (empty line)
 427     #   2 3 # comment 4 inline with other contents
 428     #   == data 0x2  (new segment)
 429     #   4 5/imm32
 430     # . write(_test-input-stream, "# comment 1\n")
 431     # . . push args
 432     68/push  "# comment 1\n"/imm32
 433     68/push  _test-input-stream/imm32
 434     # . . call
 435     e8/call  write/disp32
 436     # . . discard args
 437     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 438     # . write(_test-input-stream, "  # comment 2 indented\n")
 439     # . . push args
 440     68/push  "  # comment 2 indented\n"/imm32
 441     68/push  _test-input-stream/imm32
 442     # . . call
 443     e8/call  write/disp32
 444     # . . discard args
 445     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 446     # . write(_test-input-stream, "== code 0x1\n")
 447     # . . push args
 448     68/push  "== code 0x1\n"/imm32
 449     68/push  _test-input-stream/imm32
 450     # . . call
 451     e8/call  write/disp32
 452     # . . discard args
 453     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 454     # . write(_test-input-stream, "# comment 3 inside a segment\n")
 455     # . . push args
 456     68/push  "# comment 3 inside a segment\n"/imm32
 457     68/push  _test-input-stream/imm32
 458     # . . call
 459     e8/call  write/disp32
 460     # . . discard args
 461     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 462     # . write(_test-input-stream, "1\n")
 463     # . . push args
 464     68/push  "1\n"/imm32
 465     68/push  _test-input-stream/imm32
 466     # . . call
 467     e8/call  write/disp32
 468     # . . discard args
 469     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 470     # . write(_test-input-stream, "\n")  # empty line
 471     # . . push args
 472     68/push  "\n"/imm32
 473     68/push  _test-input-stream/imm32
 474     # . . call
 475     e8/call  write/disp32
 476     # . . discard args
 477     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 478     # . write(_test-input-stream, "2 3 # comment 4 inline with other contents\n")
 479     # . . push args
 480     68/push  "2 3 # comment 4 inline with other contents\n"/imm32
 481     68/push  _test-input-stream/imm32
 482     # . . call
 483     e8/call  write/disp32
 484     # . . discard args
 485     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 486     # . write(_test-input-stream, "== data 0x2\n")
 487     # . . push args
 488     68/push  "== data 0x2\n"/imm32
 489     68/push  _test-input-stream/imm32
 490     # . . call
 491     e8/call  write/disp32
 492     # . . discard args
 493     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 494     # . write(_test-input-stream, "4 5/imm32\n")
 495     # . . push args
 496     68/push  "4 5/imm32\n"/imm32
 497     68/push  _test-input-stream/imm32
 498     # . . call
 499     e8/call  write/disp32
 500     # . . discard args
 501     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 502     # convert(_test-input-buffered-file, _test-output-buffered-file)
 503     # . . push args
 504     68/push  _test-output-buffered-file/imm32
 505     68/push  _test-input-buffered-file/imm32
 506     # . . call
 507     e8/call  convert/disp32
 508     # . . discard args
 509     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 510     # . flush(_test-output-buffered-file)
 511     # . . push args
 512     68/push  _test-output-buffered-file/imm32
 513     # . . call
 514     e8/call  flush/disp32
 515     # . . discard args
 516     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 517     # check output
 518     #     (comment dropped for now)
 519     #     (comment dropped for now)
 520     #   == code 0x1
 521     #     (comment dropped for now)
 522     #   1
 523     #     (comment dropped for now)
 524     #   2 3
 525     #   == data 0x2
 526     #   4 5/imm32
 527     # We don't care right now what exactly happens to comments. Trailing spaces are also minor details.
 528 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
 554     # . check-next-stream-line-equal(_test-output-stream, "", msg)
 555     # . . push args
 556     68/push  "F - test-convert-is-idempotent-by-default/0"/imm32
 557     68/push  ""/imm32
 558     68/push  _test-output-stream/imm32
 559     # . . call
 560     e8/call  check-next-stream-line-equal/disp32
 561     # . . discard args
 562     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 563     # . check-next-stream-line-equal(_test-output-stream, "", msg)
 564     # . . push args
 565     68/push  "F - test-convert-is-idempotent-by-default/1"/imm32
 566     68/push  ""/imm32
 567     68/push  _test-output-stream/imm32
 568     # . . call
 569     e8/call  check-next-stream-line-equal/disp32
 570     # . . discard args
 571     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 572     # . check-next-stream-line-equal(_test-output-stream, "== code 0x1 ", msg)
 573     # . . push args
 574     68/push  "F - test-convert-is-idempotent-by-default/2"/imm32
 575     68/push  "== code 0x1 "/imm32
 576     68/push  _test-output-stream/imm32
 577     # . . call
 578     e8/call  check-next-stream-line-equal/disp32
 579     # . . discard args
 580     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 581     # . check-next-stream-line-equal(_test-output-stream, "", msg)
 582     # . . push args
 583     68/push  "F - test-convert-is-idempotent-by-default/3"/imm32
 584     68/push  ""/imm32
 585     68/push  _test-output-stream/imm32
 586     # . . call
 587     e8/call  check-next-stream-line-equal/disp32
 588     # . . discard args
 589     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 590     # . check-next-stream-line-equal(_test-output-stream, "1 ", msg)
 591     # . . push args
 592     68/push  "F - test-convert-is-idempotent-by-default/4"/imm32
 593     68/push  "1 "/imm32
 594     68/push  _test-output-stream/imm32
 595     # . . call
 596     e8/call  check-next-stream-line-equal/disp32
 597     # . . discard args
 598     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 599     # . check-next-stream-line-equal(_test-output-stream, "", msg)
 600     # . . push args
 601     68/push  "F - test-convert-is-idempotent-by-default/5"/imm32
 602     68/push  ""/imm32
 603     68/push  _test-output-stream/imm32
 604     # . . call
 605     e8/call  check-next-stream-line-equal/disp32
 606     # . . discard args
 607     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 608     # . check-next-stream-line-equal(_test-output-stream, "2 3 ", msg)
 609     # . . push args
 610     68/push  "F - test-convert-is-idempotent-by-default/6"/imm32
 611     68/push  "2 3 "/imm32
 612     68/push  _test-output-stream/imm32
 613     # . . call
 614     e8/call  check-next-stream-line-equal/disp32
 615     # . . discard args
 616     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 617     # . check-next-stream-line-equal(_test-output-stream, "== data 0x2 ", msg)
 618     # . . push args
 619     68/push  "F - test-convert-is-idempotent-by-default/7"/imm32
 620     68/push  "== data 0x2 "/imm32
 621     68/push  _test-output-stream/imm32
 622     # . . call
 623     e8/call  check-next-stream-line-equal/disp32
 624     # . . discard args
 625     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 626     # . check-next-stream-line-equal(_test-output-stream, "4 5/imm32 ", msg)
 627     # . . push args
 628     68/push  "F - test-convert-is-idempotent-by-default/8"/imm32
 629     68/push  "4 5/imm32 "/imm32
 630     68/push  _test-output-stream/imm32
 631     # . . call
 632     e8/call  check-next-stream-line-equal/disp32
 633     # . . discard args
 634     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 635     # . epilog
 636     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 637     5d/pop-to-EBP
 638     c3/return
 639 
 640 test-convert-processes-string-literals:
 641     # . prolog
 642     55/push-EBP
 643     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 644     # setup
 645     # . clear-stream(_test-input-stream)
 646     # . . push args
 647     68/push  _test-input-stream/imm32
 648     # . . call
 649     e8/call  clear-stream/disp32
 650     # . . discard args
 651     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 652     # . clear-stream(_test-input-buffered-file+4)
 653     # . . push args
 654     b8/copy-to-EAX  _test-input-buffered-file/imm32
 655     05/add-to-EAX  4/imm32
 656     50/push-EAX
 657     # . . call
 658     e8/call  clear-stream/disp32
 659     # . . discard args
 660     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 661     # . clear-stream(_test-output-stream)
 662     # . . push args
 663     68/push  _test-output-stream/imm32
 664     # . . call
 665     e8/call  clear-stream/disp32
 666     # . . discard args
 667     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 668     # . clear-stream(_test-output-buffered-file+4)
 669     # . . push args
 670     b8/copy-to-EAX  _test-output-buffered-file/imm32
 671     05/add-to-EAX  4/imm32
 672     50/push-EAX
 673     # . . call
 674     e8/call  clear-stream/disp32
 675     # . . discard args
 676     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 677     # initialize input (meta comments in parens)
 678     #   == code  (new segment)
 679     #   1 "a"/x
 680     #   2 "bc"/y
 681     68/push  "== code 0x1\n"/imm32
 682     68/push  _test-input-stream/imm32
 683     # . . call
 684     e8/call  write/disp32
 685     # . . discard args
 686     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 687     # . write(_test-input-stream, "1 \"a\"/x\n")
 688     # . . push args
 689     68/push  "1 \"a\"/x\n"/imm32
 690     68/push  _test-input-stream/imm32
 691     # . . call
 692     e8/call  write/disp32
 693     # . . discard args
 694     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 695     # . write(_test-input-stream, "2 \"bc\"/y\n")
 696     # . . push args
 697     68/push  "2 \"bc\"/y\n"/imm32
 698     68/push  _test-input-stream/imm32
 699     # . . call
 700     e8/call  write/disp32
 701     # . . discard args
 702     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 703     # convert(_test-input-buffered-file, _test-output-buffered-file)
 704     # . . push args
 705     68/push  _test-output-buffered-file/imm32
 706     68/push  _test-input-buffered-file/imm32
 707     # . . call
 708     e8/call  convert/disp32
 709     # . . discard args
 710     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 711     # . flush(_test-output-buffered-file)
 712     # . . push args
 713     68/push  _test-output-buffered-file/imm32
 714     # . . call
 715     e8/call  flush/disp32
 716     # . . discard args
 717     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 718     # check output
 719     #   == code 0x1
 720     #   1 _string1/x
 721     #   2 _string2/y
 722     #   == data
 723     #   _string1:
 724     #   1/imm32 61/a
 725     #   _string2:
 726     #   2/imm32 62/b 63/c
 727     # We don't care right now what exactly happens to comments. Trailing spaces are also minor details.
 728     #
 729     # Open question: how to make this check more robust.
 730     # We don't actually care what the auto-generated string variables are
 731     # called. We just want to make sure instructions using string literals
 732     # switch to a string variable with the right value.
 733     # (Modifying string literals completely off the radar for now.)
 734 +-- 33 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
 767     # . check-next-stream-line-equal(_test-output-stream, "== code 0x1 ", msg)
 768     # . . push args
 769     68/push  "F - test-convert-processes-string-literals/0"/imm32
 770     68/push  "== code 0x1 "/imm32
 771     68/push  _test-output-stream/imm32
 772     # . . call
 773     e8/call  check-next-stream-line-equal/disp32
 774     # . . discard args
 775     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 776     # . check-next-stream-line-equal(_test-output-stream, "1 _string1/x ", msg)
 777     # . . push args
 778     68/push  "F - test-convert-processes-string-literals/1"/imm32
 779     68/push  "1 _string1/x "/imm32
 780     68/push  _test-output-stream/imm32
 781     # . . call
 782     e8/call  check-next-stream-line-equal/disp32
 783     # . . discard args
 784     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 785     # . check-next-stream-line-equal(_test-output-stream, "2 _string2/y ", msg)
 786     # . . push args
 787     68/push  "F - test-convert-processes-string-literals/2"/imm32
 788     68/push  "2 _string2/y "/imm32
 789     68/push  _test-output-stream/imm32
 790     # . . call
 791     e8/call  check-next-stream-line-equal/disp32
 792     # . . discard args
 793     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 794     # . check-next-stream-line-equal(_test-output-stream, "== data", msg)
 795     # . . push args
 796     68/push  "F - test-convert-processes-string-literals/3"/imm32
 797     68/push  "== data"/imm32
 798     68/push  _test-output-stream/imm32
 799     # . . call
 800     e8/call  check-next-stream-line-equal/disp32
 801     # . . discard args
 802     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 803     # . check-next-stream-line-equal(_test-output-stream, "_string1: ", msg)
 804     # . . push args
 805     68/push  "F - test-convert-processes-string-literals/4"/imm32
 806     68/push  "_string1:"/imm32
 807     68/push  _test-output-stream/imm32
 808     # . . call
 809     e8/call  check-next-stream-line-equal/disp32
 810     # . . discard args
 811     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 812     # . check-next-stream-line-equal(_test-output-stream, "1/imm32 61/a ", msg)
 813     # . . push args
 814     68/push  "F - test-convert-processes-string-literals/5"/imm32
 815     68/push  "0x00000001/imm32 61/a "/imm32
 816     68/push  _test-output-stream/imm32
 817     # . . call
 818     e8/call  check-next-stream-line-equal/disp32
 819     # . . discard args
 820     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 821     # . check-next-stream-line-equal(_test-output-stream, "_string2: ", msg)
 822     # . . push args
 823     68/push  "F - test-convert-processes-string-literals/6"/imm32
 824     68/push  "_string2:"/imm32
 825     68/push  _test-output-stream/imm32
 826     # . . call
 827     e8/call  check-next-stream-line-equal/disp32
 828     # . . discard args
 829     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 830     # . check-next-stream-line-equal(_test-output-stream, "2/imm32 62/b 63/c ", msg)
 831     # . . push args
 832     68/push  "F - test-convert-processes-string-literals/7"/imm32
 833     68/push  "0x00000002/imm32 62/b 63/c "/imm32
 834     68/push  _test-output-stream/imm32
 835     # . . call
 836     e8/call  check-next-stream-line-equal/disp32
 837     # . . discard args
 838     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 839     # . epilog
 840     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 841     5d/pop-to-EBP
 842     c3/return
 843 
 844 # generate the data segment contents byte by byte for a given slice
 845 emit-string-literal-data:  # out : (address stream), word : (address slice)
 846     # pseudocode
 847     #   len = string-length-at-start-of-slice(word->start, word->end)
 848     #   print(out, "#{len}/imm32 ")
 849     #   curr = word->start
 850     #   ++curr  # skip '"'
 851     #   while true
 852     #     if (curr >= word->end) break
 853     #     c = *curr
 854     #     if (c == '"') break
 855     #     if (c == '\') ++curr, c = *curr
 856     #     append-byte-hex(out, c)
 857     #     if c is alphanumeric:
 858     #       write(out, "/")
 859     #       append-byte(out, c)
 860     #     write(out, " ")
 861     #     ++curr
 862     #
 863     # . prolog
 864     55/push-EBP
 865     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 866     # . save registers
 867     50/push-EAX
 868     51/push-ECX
 869     52/push-EDX
 870     56/push-ESI
 871     # ESI = word
 872     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
 873     # curr/EDX = word->start
 874     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           2/r32/EDX   .               .                 # copy *ESI to EDX
 875     # max/ESI = word->end
 876     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           6/r32/ESI   4/disp8         .                 # copy *(ESI+4) to ESI
 877 $emit-string-literal-data:emit-length:
 878     # len/EAX = string-length-at-start-of-slice(word->start, word->end)
 879     # . . push args
 880     56/push-ESI
 881     52/push-EDX
 882     # . . call
 883     e8/call  string-length-at-start-of-slice/disp32
 884     # . . discard args
 885     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 886     # print(out, "#{len}/imm32 ")
 887     # . print-int32(out, len)
 888     # . . push args
 889     50/push-EAX
 890     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 891     # . . call
 892     e8/call  print-int32/disp32
 893     # . . discard args
 894     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 895     # . write(out, "/imm32 ")
 896     # . . push args
 897     68/push  "/imm32 "/imm32
 898     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 899     # . . call
 900     e8/call  write/disp32
 901     # . . discard args
 902     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 903 $emit-string-literal-data:loop-init:
 904     # ++curr  # skip initial '"'
 905     42/increment-EDX
 906     # c/ECX = 0
 907     31/xor                          3/mod/direct    1/rm32/ECX    .           .             .           1/r32/ECX   .               .                 # clear ECX
 908 $emit-string-literal-data:loop:
 909     # if (curr >= max) break
 910     39/compare                      3/mod/direct    2/rm32/EDX    .           .             .           6/r32/ESI   .               .                 # compare EDX with ESI
 911     73/jump-if-greater-or-equal-unsigned  $emit-string-literal-data:end/disp8
 912     # CL = *curr
 913     8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           1/r32/CL    .               .                 # copy byte at *EDX to CL
 914     # if (ECX == '"') break
 915     81          7/subop/compare     3/mod/direct    1/rm32/ECX    .           .             .           .           .               0x22/imm32/dquote # compare ECX
 916     74/jump-if-equal  $emit-string-literal-data:end/disp8
 917     # if (ECX == '\') ++curr, ECX = *curr
 918     81          7/subop/compare     3/mod/direct    1/rm32/ECX    .           .             .           .           .               0x5c/imm32/backslash  # compare ECX
 919     75/jump-if-not-equal  $emit-string-literal-data:emit/disp8
 920     # . ++curr
 921     42/increment-EDX
 922     # . if (curr >= max) break
 923     39/compare                      3/mod/direct    2/rm32/EDX    .           .             .           6/r32/ESI   .               .                 # compare EDX with ESI
 924     73/jump-if-greater-or-equal-unsigned  $emit-string-literal-data:end/disp8
 925     # . CL = *curr
 926     8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           1/r32/CL    .               .                 # copy byte at *EDX to CL
 927 $emit-string-literal-data:emit:
 928     # append-byte-hex(out, CL)
 929     # . . push args
 930     51/push-ECX
 931     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 932     # . . call
 933     e8/call  append-byte-hex/disp32
 934     # . . discard args
 935     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 936     # if (is-alphanumeric?(*curr)) print(out, "/#{*curr}")
 937     # . EAX = is-alphanumeric?(CL)
 938     # . . push args
 939     51/push-ECX
 940     # . . call
 941     e8/call  is-alphanumeric?/disp32
 942     # . . discard args
 943     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 944     # . if (EAX == 0) goto char-done
 945     3d/compare-EAX-and  0/imm32
 946     74/jump-if-equal  $emit-string-literal-data:char-done/disp8
 947     # . write(out, "/")
 948     # . . push args
 949     68/push  Slash/imm32
 950     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 951     # . . call
 952     e8/call  write/disp32
 953     # . . discard args
 954     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 955     # . append-byte(out, *curr)
 956     # . . push args
 957     51/push-ECX
 958     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 959     # . . call
 960     e8/call  append-byte/disp32
 961     # . . discard args
 962     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 963 $emit-string-literal-data:char-done:
 964     # write(out, " ")
 965     # . . push args
 966     68/push  Space/imm32
 967     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 968     # . . call
 969     e8/call  write/disp32
 970     # . . discard args
 971     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 972     # ++curr
 973     42/increment-EDX
 974     eb/jump $emit-string-literal-data:loop/disp8
 975 $emit-string-literal-data:end:
 976     # . restore registers
 977     5e/pop-to-ESI
 978     5a/pop-to-EDX
 979     59/pop-to-ECX
 980     58/pop-to-EAX
 981     # . epilog
 982     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 983     5d/pop-to-EBP
 984     c3/return
 985 
 986 is-alphanumeric?:  # c : int -> EAX : boolean
 987     # . prolog
 988     55/push-EBP
 989     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 990     # EAX = c
 991     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   8/disp8         .                 # copy *(EBP+8) to EAX
 992     # if (EAX < '0') return false
 993     3d/compare-EAX-with  0x30/imm32/0
 994     7c/jump-if-lesser  $is-alphanumeric?:false/disp8
 995     # if (EAX <= '9') return true
 996     3d/compare-EAX-with  0x39/imm32/9
 997     7e/jump-if-lesser-or-equal  $is-alphanumeric?:true/disp8
 998     # if (EAX < 'A') return false
 999     3d/compare-EAX-with  0x41/imm32/A
1000     7c/jump-if-lesser  $is-alphanumeric?:false/disp8
1001     # if (EAX <= 'Z') return true
1002     3d/compare-EAX-with  0x5a/imm32/Z
1003     7e/jump-if-lesser-or-equal  $is-alphanumeric?:true/disp8
1004     # if (EAX < 'a') return false
1005     3d/compare-EAX-with  0x61/imm32/a
1006     7c/jump-if-lesser  $is-alphanumeric?:false/disp8
1007     # if (EAX <= 'z') return true
1008     3d/compare-EAX-with  0x7a/imm32/z
1009     7e/jump-if-lesser-or-equal  $is-alphanumeric?:true/disp8
1010     # return false
1011 $is-alphanumeric?:false:
1012     b8/copy-to-EAX  0/imm32/false
1013     eb/jump  $is-alphanumeric?:end/disp8
1014 $is-alphanumeric?:true:
1015     b8/copy-to-EAX  1/imm32/true
1016 $is-alphanumeric?:end:
1017     # . epilog
1018     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1019     5d/pop-to-EBP
1020     c3/return
1021 
1022 test-emit-string-literal-data:
1023     # . prolog
1024     55/push-EBP
1025     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1026     # setup
1027     # . clear-stream(_test-output-stream)
1028     # . . push args
1029     68/push  _test-output-stream/imm32
1030     # . . call
1031     e8/call  clear-stream/disp32
1032     # . . discard args
1033     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1034     # var slice/ECX = '"abc"/d'
1035     68/push  _test-slice-abc-limit/imm32
1036     68/push  _test-slice-abc/imm32
1037     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
1038     # emit-string-literal-data(_test-output-stream, slice)
1039     # . . push args
1040     51/push-ECX
1041     68/push  _test-output-stream/imm32
1042     # . . call
1043     e8/call  emit-string-literal-data/disp32
1044     # . . discard args
1045     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1046 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
1072     # . check-stream-equal(_test-output-stream, "3/imm32 61/a 62/b 63/c ", msg)
1073     # . . push args
1074     68/push  "F - test-emit-string-literal-data"/imm32
1075     68/push  "0x00000003/imm32 61/a 62/b 63/c "/imm32
1076     68/push  _test-output-stream/imm32
1077     # . . call
1078     e8/call  check-stream-equal/disp32
1079     # . . discard args
1080     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1081     # . epilog
1082     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1083     5d/pop-to-EBP
1084     c3/return
1085 
1086 test-emit-string-literal-data-empty:
1087     # . prolog
1088     55/push-EBP
1089     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1090     # setup
1091     # . clear-stream(_test-output-stream)
1092     # . . push args
1093     68/push  _test-output-stream/imm32
1094     # . . call
1095     e8/call  clear-stream/disp32
1096     # . . discard args
1097     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1098     # var slice/ECX = '""'
1099     68/push  0/imm32/end
1100     68/push  0/imm32/start
1101     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
1102     # emit-string-literal-data(_test-output-stream, slice)
1103     # . . push args
1104     51/push-ECX
1105     68/push  _test-output-stream/imm32
1106     # . . call
1107     e8/call  emit-string-literal-data/disp32
1108     # . . discard args
1109     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1110 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
1136     # . check-stream-equal(_test-output-stream, "0/imm32 ", msg)
1137     # . . push args
1138     68/push  "F - test-emit-string-literal-data-empty"/imm32
1139     68/push  "0x00000000/imm32 "/imm32
1140     68/push  _test-output-stream/imm32
1141     # . . call
1142     e8/call  check-stream-equal/disp32
1143     # . . discard args
1144     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1145     # . epilog
1146     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1147     5d/pop-to-EBP
1148     c3/return
1149 
1150 # just to keep things simple
1151 test-emit-string-literal-data-no-metadata-for-non-alphanumerics:
1152     # . prolog
1153     55/push-EBP
1154     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1155     # setup
1156     # . clear-stream(_test-output-stream)
1157     # . . push args
1158     68/push  _test-output-stream/imm32
1159     # . . call
1160     e8/call  clear-stream/disp32
1161     # . . discard args
1162     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1163     # var slice/ECX = '"a b"'
1164     68/push  _test-slice-a-space-b-limit/imm32
1165     68/push  _test-slice-a-space-b/imm32
1166     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
1167     # emit-string-literal-data(_test-output-stream, slice)
1168     # . . push args
1169     51/push-ECX
1170     68/push  _test-output-stream/imm32
1171     # . . call
1172     e8/call  emit-string-literal-data/disp32
1173     # . . discard args
1174     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1175 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
1201     # . 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
1202     # . . push args
1203     68/push  "F - test-emit-string-literal-data-no-metadata-for-non-alphanumerics"/imm32
1204     68/push  "0x00000003/imm32 61/a 20 62/b "/imm32
1205     68/push  _test-output-stream/imm32
1206     # . . call
1207     e8/call  check-stream-equal/disp32
1208     # . . discard args
1209     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1210     # . epilog
1211     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1212     5d/pop-to-EBP
1213     c3/return
1214 
1215 test-emit-string-literal-data-handles-escape-sequences:
1216     # . prolog
1217     55/push-EBP
1218     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1219     # setup
1220     # . clear-stream(_test-output-stream)
1221     # . . push args
1222     68/push  _test-output-stream/imm32
1223     # . . call
1224     e8/call  clear-stream/disp32
1225     # . . discard args
1226     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1227     # var slice/ECX = '"a\"b"'
1228     68/push  _test-slice-a-dquote-b-limit/imm32
1229     68/push  _test-slice-a-dquote-b/imm32
1230     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
1231     # emit-string-literal-data(_test-output-stream, slice)
1232     # . . push args
1233     51/push-ECX
1234     68/push  _test-output-stream/imm32
1235     # . . call
1236     e8/call  emit-string-literal-data/disp32
1237     # . . discard args
1238     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1239 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
1265     # . check-stream-equal(_test-output-stream, "3/imm32 61/a 22 62/b ", msg)
1266     # . . push args
1267     68/push  "F - test-emit-string-literal-data-handles-escape-sequences"/imm32
1268     68/push  "0x00000003/imm32 61/a 22 62/b "/imm32
1269     68/push  _test-output-stream/imm32
1270     # . . call
1271     e8/call  check-stream-equal/disp32
1272     # . . discard args
1273     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1274     # . epilog
1275     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1276     5d/pop-to-EBP
1277     c3/return
1278 
1279 # emit everything from a word except the initial datum
1280 emit-metadata:  # out : (address buffered-file), word : (address slice)
1281     # pseudocode
1282     #   var slice = {0, word->end}
1283     #   curr = word->start
1284     #   if *curr == '"'
1285     #     curr = skip-string-in-slice(curr, word->end)
1286     #   else
1287     #     while true
1288     #       if curr == word->end
1289     #         return
1290     #       if *curr == '/'
1291     #         break
1292     #       ++curr
1293     #   slice->start = curr
1294     #   write-slice-buffered(out, slice)
1295     #
1296     # . prolog
1297     55/push-EBP
1298     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1299     # . save registers
1300     50/push-EAX
1301     51/push-ECX
1302     52/push-EDX
1303     53/push-EBX
1304     56/push-ESI
1305     # ESI = word
1306     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
1307     # curr/ECX = word->start
1308     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # copy *ESI to ECX
1309     # end/EDX = word->end
1310     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           2/r32/EDX   4/disp8         .                 # copy *(ESI+4) to EDX
1311     # var slice/EBX = {0, end}
1312     52/push-EDX
1313     68/push  0/imm32
1314     89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBX
1315     # EAX = 0
1316     b8/copy-to-EAX  0/imm32
1317 $emit-metadata:check-for-string-literal:
1318     # -  if (*curr == '"') curr = skip-string-in-slice(curr, end)
1319     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
1320     3d/compare-EAX-and  0x22/imm32/dquote
1321     75/jump-if-not-equal  $emit-metadata:skip-datum-loop/disp8
1322 $emit-metadata:skip-string-literal:
1323     # . EAX = skip-string-in-slice(curr, end)
1324     # . . push args
1325     52/push-EDX
1326     51/push-ECX
1327     # . . call
1328     e8/call  skip-string-in-slice/disp32
1329     # . . discard args
1330     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1331     # . curr = EAX
1332     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy EAX to ECX
1333     eb/jump  $emit-metadata:emit/disp8
1334 $emit-metadata:skip-datum-loop:
1335     # - otherwise scan for '/'
1336     # if (curr == end) return
1337     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX and EDX
1338     74/jump-if-equal  $emit-metadata:end/disp8
1339     # if (*curr == '/') break
1340     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
1341     3d/compare-EAX-and  0x2f/imm32/slash
1342     74/jump-if-equal  $emit-metadata:emit/disp8
1343     # ++curr
1344     41/increment-ECX
1345     eb/jump  $emit-metadata:skip-datum-loop/disp8
1346 $emit-metadata:emit:
1347     # slice->start = ECX
1348     89/copy                         0/mod/indirect  3/rm32/EBX    .           .             .           1/r32/ECX   .               .                 # copy ECX to *EBX
1349     # write-slice-buffered(out, slice)
1350     # . . push args
1351     53/push-EBX
1352     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
1353     # . . call
1354     e8/call  write-slice-buffered/disp32
1355     # . . discard args
1356     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           8/imm32      .                    # add to ESP
1357 $emit-metadata:end:
1358     # . reclaim locals
1359     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           8/imm32      .                    # add to ESP
1360     # . restore registers
1361     5e/pop-to-ESI
1362     5b/pop-to-EBX
1363     5a/pop-to-EDX
1364     59/pop-to-ECX
1365     58/pop-to-EAX
1366     # . epilog
1367     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1368     5d/pop-to-EBP
1369     c3/return
1370 
1371 test-emit-metadata:
1372     # . prolog
1373     55/push-EBP
1374     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1375     # setup
1376     # . clear-stream(_test-output-stream)
1377     # . . push args
1378     68/push  _test-output-stream/imm32
1379     # . . call
1380     e8/call  clear-stream/disp32
1381     # . . discard args
1382     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1383     # . clear-stream(_test-output-buffered-file+4)
1384     # . . push args
1385     b8/copy-to-EAX  _test-output-buffered-file/imm32
1386     05/add-to-EAX  4/imm32
1387     50/push-EAX
1388     # . . call
1389     e8/call  clear-stream/disp32
1390     # . . discard args
1391     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1392     # (EAX..ECX) = "abc/def"
1393     b8/copy-to-EAX  "abc/def"/imm32
1394     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
1395     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
1396     05/add-to-EAX  4/imm32
1397     # var slice/ECX = {EAX, ECX}
1398     51/push-ECX
1399     50/push-EAX
1400     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
1401     # emit-metadata(_test-output-buffered-file, slice)
1402     # . . push args
1403     51/push-ECX
1404     68/push  _test-output-buffered-file/imm32
1405     # . . call
1406     e8/call  emit-metadata/disp32
1407     # . . discard args
1408     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1409     # flush(_test-output-buffered-file)
1410     # . . push args
1411     68/push  _test-output-buffered-file/imm32
1412     # . . call
1413     e8/call  flush/disp32
1414     # . . discard args
1415     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1416     # check-stream-equal(_test-output-stream, "/def", msg)  # important that there's no leading space
1417     # . . push args
1418     68/push  "F - test-emit-metadata"/imm32
1419     68/push  "/def"/imm32
1420     68/push  _test-output-stream/imm32
1421     # . . call
1422     e8/call  check-stream-equal/disp32
1423     # . . discard args
1424     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1425     # . epilog
1426     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1427     5d/pop-to-EBP
1428     c3/return
1429 
1430 test-emit-metadata-none:
1431     # . prolog
1432     55/push-EBP
1433     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1434     # setup
1435     # . clear-stream(_test-output-stream)
1436     # . . push args
1437     68/push  _test-output-stream/imm32
1438     # . . call
1439     e8/call  clear-stream/disp32
1440     # . . discard args
1441     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1442     # . clear-stream(_test-output-buffered-file+4)
1443     # . . push args
1444     b8/copy-to-EAX  _test-output-buffered-file/imm32
1445     05/add-to-EAX  4/imm32
1446     50/push-EAX
1447     # . . call
1448     e8/call  clear-stream/disp32
1449     # . . discard args
1450     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1451     # (EAX..ECX) = "abc"
1452     b8/copy-to-EAX  "abc"/imm32
1453     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
1454     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
1455     05/add-to-EAX  4/imm32
1456     # var slice/ECX = {EAX, ECX}
1457     51/push-ECX
1458     50/push-EAX
1459     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
1460     # emit-metadata(_test-output-buffered-file, slice)
1461     # . . push args
1462     51/push-ECX
1463     68/push  _test-output-buffered-file/imm32
1464     # . . call
1465     e8/call  emit-metadata/disp32
1466     # . . discard args
1467     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1468     # flush(_test-output-buffered-file)
1469     # . . push args
1470     68/push  _test-output-buffered-file/imm32
1471     # . . call
1472     e8/call  flush/disp32
1473     # . . discard args
1474     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1475     # check-stream-equal(_test-output-stream, "", msg)
1476     # . . push args
1477     68/push  "F - test-emit-metadata-none"/imm32
1478     68/push  ""/imm32
1479     68/push  _test-output-stream/imm32
1480     # . . call
1481     e8/call  check-stream-equal/disp32
1482     # . . discard args
1483     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1484     # . epilog
1485     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1486     5d/pop-to-EBP
1487     c3/return
1488 
1489 test-emit-metadata-multiple:
1490     # . prolog
1491     55/push-EBP
1492     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1493     # setup
1494     # . clear-stream(_test-output-stream)
1495     # . . push args
1496     68/push  _test-output-stream/imm32
1497     # . . call
1498     e8/call  clear-stream/disp32
1499     # . . discard args
1500     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1501     # . clear-stream(_test-output-buffered-file+4)
1502     # . . push args
1503     b8/copy-to-EAX  _test-output-buffered-file/imm32
1504     05/add-to-EAX  4/imm32
1505     50/push-EAX
1506     # . . call
1507     e8/call  clear-stream/disp32
1508     # . . discard args
1509     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1510     # (EAX..ECX) = "abc/def/ghi"
1511     b8/copy-to-EAX  "abc/def/ghi"/imm32
1512     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
1513     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
1514     05/add-to-EAX  4/imm32
1515     # var slice/ECX = {EAX, ECX}
1516     51/push-ECX
1517     50/push-EAX
1518     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
1519     # emit-metadata(_test-output-buffered-file, slice)
1520     # . . push args
1521     51/push-ECX
1522     68/push  _test-output-buffered-file/imm32
1523     # . . call
1524     e8/call  emit-metadata/disp32
1525     # . . discard args
1526     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1527     # flush(_test-output-buffered-file)
1528     # . . push args
1529     68/push  _test-output-buffered-file/imm32
1530     # . . call
1531     e8/call  flush/disp32
1532     # . . discard args
1533     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1534     # check-stream-equal(_test-output-stream, "/def/ghi", msg)  # important that there's no leading space
1535     # . . push args
1536     68/push  "F - test-emit-metadata-multiple"/imm32
1537     68/push  "/def/ghi"/imm32
1538     68/push  _test-output-stream/imm32
1539     # . . call
1540     e8/call  check-stream-equal/disp32
1541     # . . discard args
1542     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1543     # . epilog
1544     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1545     5d/pop-to-EBP
1546     c3/return
1547 
1548 test-emit-metadata-when-no-datum:
1549     # . prolog
1550     55/push-EBP
1551     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1552     # setup
1553     # . clear-stream(_test-output-stream)
1554     # . . push args
1555     68/push  _test-output-stream/imm32
1556     # . . call
1557     e8/call  clear-stream/disp32
1558     # . . discard args
1559     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1560     # . clear-stream(_test-output-buffered-file+4)
1561     # . . push args
1562     b8/copy-to-EAX  _test-output-buffered-file/imm32
1563     05/add-to-EAX  4/imm32
1564     50/push-EAX
1565     # . . call
1566     e8/call  clear-stream/disp32
1567     # . . discard args
1568     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1569     # var slice/ECX = "/abc"
1570     b8/copy-to-EAX  "/abc"/imm32
1571     # . push end/ECX
1572     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
1573     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
1574     51/push-ECX
1575     # . push curr/EAX
1576     05/add-to-EAX  4/imm32
1577     50/push-EAX
1578     # . save stack pointer
1579     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
1580     # emit-metadata(_test-output-buffered-file, slice)
1581     # . . push args
1582     51/push-ECX
1583     68/push  _test-output-buffered-file/imm32
1584     # . . call
1585     e8/call  emit-metadata/disp32
1586     # . . discard args
1587     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1588     # flush(_test-output-buffered-file)
1589     # . . push args
1590     68/push  _test-output-buffered-file/imm32
1591     # . . call
1592     e8/call  flush/disp32
1593     # . . discard args
1594     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1595     # check-stream-equal(_test-output-stream, "/abc", msg)  # nothing skipped
1596     # . . push args
1597     68/push  "F - test-emit-metadata-when-no-datum"/imm32
1598     68/push  "/abc"/imm32
1599     68/push  _test-output-stream/imm32
1600     # . . call
1601     e8/call  check-stream-equal/disp32
1602     # . . discard args
1603     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1604     # . epilog
1605     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1606     5d/pop-to-EBP
1607     c3/return
1608 
1609 test-emit-metadata-in-string-literal:
1610     # . prolog
1611     55/push-EBP
1612     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1613     # setup
1614     # . clear-stream(_test-output-stream)
1615     # . . push args
1616     68/push  _test-output-stream/imm32
1617     # . . call
1618     e8/call  clear-stream/disp32
1619     # . . discard args
1620     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1621     # . clear-stream(_test-output-buffered-file+4)
1622     # . . push args
1623     b8/copy-to-EAX  _test-output-buffered-file/imm32
1624     05/add-to-EAX  4/imm32
1625     50/push-EAX
1626     # . . call
1627     e8/call  clear-stream/disp32
1628     # . . discard args
1629     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1630     # var slice/ECX = "\"abc/def\"/ghi"
1631     68/push  _test-slice-literal-string-with-limit/imm32
1632     68/push  _test-slice-literal-string/imm32/start
1633     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
1634     # emit-metadata(_test-output-buffered-file, slice)
1635     # . . push args
1636     51/push-ECX
1637     68/push  _test-output-buffered-file/imm32
1638     # . . call
1639     e8/call  emit-metadata/disp32
1640     # . . discard args
1641     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1642     # flush(_test-output-buffered-file)
1643     # . . push args
1644     68/push  _test-output-buffered-file/imm32
1645     # . . call
1646     e8/call  flush/disp32
1647     # . . discard args
1648     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1649 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
1675     # check-stream-equal(_test-output-stream, "/ghi", msg)  # important that there's no leading space
1676     # . . push args
1677     68/push  "F - test-emit-metadata-in-string-literal"/imm32
1678     68/push  "/ghi"/imm32
1679     68/push  _test-output-stream/imm32
1680     # . . call
1681     e8/call  check-stream-equal/disp32
1682     # . . discard args
1683     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1684     # . epilog
1685     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1686     5d/pop-to-EBP
1687     c3/return
1688 
1689 # (re)compute the bounds of the next word in the line
1690 # return empty string on reaching end of file
1691 next-word-or-string:  # line : (address stream byte), out : (address slice)
1692     # . prolog
1693     55/push-EBP
1694     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1695     # . save registers
1696     50/push-EAX
1697     51/push-ECX
1698     56/push-ESI
1699     57/push-EDI
1700     # ESI = line
1701     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
1702     # EDI = out
1703     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   0xc/disp8       .                 # copy *(EBP+12) to EDI
1704     # skip-chars-matching(line, ' ')
1705     # . . push args
1706     68/push  0x20/imm32/space
1707     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
1708     # . . call
1709     e8/call  skip-chars-matching/disp32
1710     # . . discard args
1711     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1712 $next-word-or-string:check0:
1713     # if (line->read >= line->write) clear out and return
1714     # . EAX = line->read
1715     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ESI+4) to EAX
1716     # . if (EAX < line->write) goto next check
1717     3b/compare                      0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # compare EAX with *ESI
1718     7c/jump-if-lesser  $next-word-or-string:check-for-comment/disp8
1719     # . return out = {0, 0}
1720     c7          0/subop/copy        0/mod/direct    7/rm32/EDI    .           .             .           .           .               0/imm32           # copy to *EDI
1721     c7          0/subop/copy        1/mod/*+disp8   7/rm32/EDI    .           .             .           .           4/disp8         0/imm32           # copy to *(EDI+4)
1722     eb/jump  $next-word-or-string:end/disp8
1723 $next-word-or-string:check-for-comment:
1724     # out->start = &line->data[line->read]
1725     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
1726     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
1727     89/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EDI
1728     # if line->data[line->read] == '#'
1729     # . EAX = line->data[line->read]
1730     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
1731     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
1732     # . compare
1733     3d/compare-EAX-and  0x23/imm32/pound
1734     75/jump-if-not-equal  $next-word-or-string:check-for-string-literal/disp8
1735 $next-word-or-string:comment:
1736     # out->end = &line->data[line->write]
1737     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
1738     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
1739     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
1740     # line->read = line->write  # skip rest of line
1741     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
1742     89/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(ESI+4)
1743     # return
1744     eb/jump  $next-word-or-string:end/disp8
1745 $next-word-or-string:check-for-string-literal:
1746     # if line->data[line->read] == '"'
1747     # . EAX = line->data[line->read]
1748     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
1749     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
1750     # . compare
1751     3d/compare-EAX-and  0x22/imm32/dquote
1752     75/jump-if-not-equal  $next-word-or-string:regular-word/disp8
1753 $next-word-or-string:string-literal:
1754     # skip-string(line)
1755     # . . push args
1756     56/push-ESI
1757     # . . call
1758     e8/call  skip-string/disp32
1759     # . . discard args
1760     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1761     # fall through
1762 $next-word-or-string:regular-word:
1763     # skip-chars-not-matching-whitespace(line)  # including trailing newline
1764     # . . push args
1765     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
1766     # . . call
1767     e8/call  skip-chars-not-matching-whitespace/disp32
1768     # . . discard args
1769     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1770     # out->end = &line->data[line->read]
1771     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
1772     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
1773     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
1774 $next-word-or-string:end:
1775     # . restore registers
1776     5f/pop-to-EDI
1777     5e/pop-to-ESI
1778     59/pop-to-ECX
1779     58/pop-to-EAX
1780     # . epilog
1781     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1782     5d/pop-to-EBP
1783     c3/return
1784 
1785 test-next-word-or-string:
1786     # . prolog
1787     55/push-EBP
1788     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1789     # setup
1790     # . clear-stream(_test-input-stream)
1791     # . . push args
1792     68/push  _test-input-stream/imm32
1793     # . . call
1794     e8/call  clear-stream/disp32
1795     # . . discard args
1796     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1797     # var slice/ECX = {0, 0}
1798     68/push  0/imm32/end
1799     68/push  0/imm32/start
1800     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
1801     # write(_test-input-stream, "  ab")
1802     # . . push args
1803     68/push  "  ab"/imm32
1804     68/push  _test-input-stream/imm32
1805     # . . call
1806     e8/call  write/disp32
1807     # . . discard args
1808     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1809     # next-word-or-string(_test-input-stream, slice)
1810     # . . push args
1811     51/push-ECX
1812     68/push  _test-input-stream/imm32
1813     # . . call
1814     e8/call  next-word-or-string/disp32
1815     # . . discard args
1816     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1817     # check-ints-equal(_test-input-stream->read, 4, msg)
1818     # . . push args
1819     68/push  "F - test-next-word-or-string/updates-stream-read-correctly"/imm32
1820     68/push  4/imm32
1821     b8/copy-to-EAX  _test-input-stream/imm32
1822     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
1823     # . . call
1824     e8/call  check-ints-equal/disp32
1825     # . . discard args
1826     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1827     # check-ints-equal(slice->start - _test-input-stream->data, 2, msg)
1828     # . check-ints-equal(slice->start - _test-input-stream, 14, msg)
1829     # . . push args
1830     68/push  "F - test-next-word-or-string: start"/imm32
1831     68/push  0xe/imm32
1832     # . . push slice->start - _test-input-stream
1833     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
1834     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
1835     50/push-EAX
1836     # . . call
1837     e8/call  check-ints-equal/disp32
1838     # . . discard args
1839     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1840     # check-ints-equal(slice->end - _test-input-stream->data, 4, msg)
1841     # . check-ints-equal(slice->end - _test-input-stream, 16, msg)
1842     # . . push args
1843     68/push  "F - test-next-word-or-string: end"/imm32
1844     68/push  0x10/imm32
1845     # . . push slice->end - _test-input-stream
1846     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
1847     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
1848     50/push-EAX
1849     # . . call
1850     e8/call  check-ints-equal/disp32
1851     # . . discard args
1852     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1853     # . epilog
1854     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1855     5d/pop-to-EBP
1856     c3/return
1857 
1858 test-next-word-or-string-returns-whole-comment:
1859     # . prolog
1860     55/push-EBP
1861     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1862     # setup
1863     # . clear-stream(_test-input-stream)
1864     # . . push args
1865     68/push  _test-input-stream/imm32
1866     # . . call
1867     e8/call  clear-stream/disp32
1868     # . . discard args
1869     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1870     # var slice/ECX = {0, 0}
1871     68/push  0/imm32/end
1872     68/push  0/imm32/start
1873     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
1874     # write(_test-input-stream, "  # a")
1875     # . . push args
1876     68/push  "  # a"/imm32
1877     68/push  _test-input-stream/imm32
1878     # . . call
1879     e8/call  write/disp32
1880     # . . discard args
1881     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1882     # next-word-or-string(_test-input-stream, slice)
1883     # . . push args
1884     51/push-ECX
1885     68/push  _test-input-stream/imm32
1886     # . . call
1887     e8/call  next-word-or-string/disp32
1888     # . . discard args
1889     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1890     # check-ints-equal(_test-input-stream->read, 5, msg)
1891     # . . push args
1892     68/push  "F - test-next-word-or-string-returns-whole-comment/updates-stream-read-correctly"/imm32
1893     68/push  5/imm32
1894     b8/copy-to-EAX  _test-input-stream/imm32
1895     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
1896     # . . call
1897     e8/call  check-ints-equal/disp32
1898     # . . discard args
1899     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1900     # check-ints-equal(slice->start - _test-input-stream->data, 2, msg)
1901     # . check-ints-equal(slice->start - _test-input-stream, 14, msg)
1902     # . . push args
1903     68/push  "F - test-next-word-or-string-returns-whole-comment: start"/imm32
1904     68/push  0xe/imm32
1905     # . . push slice->start - _test-input-stream
1906     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
1907     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
1908     50/push-EAX
1909     # . . call
1910     e8/call  check-ints-equal/disp32
1911     # . . discard args
1912     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1913     # check-ints-equal(slice->end - _test-input-stream->data, 5, msg)
1914     # . check-ints-equal(slice->end - _test-input-stream, 17, msg)
1915     # . . push args
1916     68/push  "F - test-next-word-or-string-returns-whole-comment: end"/imm32
1917     68/push  0x11/imm32
1918     # . . push slice->end - _test-input-stream
1919     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
1920     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
1921     50/push-EAX
1922     # . . call
1923     e8/call  check-ints-equal/disp32
1924     # . . discard args
1925     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1926     # . epilog
1927     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1928     5d/pop-to-EBP
1929     c3/return
1930 
1931 test-next-word-or-string-returns-empty-string-on-eof:
1932     # . prolog
1933     55/push-EBP
1934     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1935     # setup
1936     # . clear-stream(_test-input-stream)
1937     # . . push args
1938     68/push  _test-input-stream/imm32
1939     # . . call
1940     e8/call  clear-stream/disp32
1941     # . . discard args
1942     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1943     # var slice/ECX = {0, 0}
1944     68/push  0/imm32/end
1945     68/push  0/imm32/start
1946     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
1947     # write nothing to _test-input-stream
1948     # next-word-or-string(_test-input-stream, slice)
1949     # . . push args
1950     51/push-ECX
1951     68/push  _test-input-stream/imm32
1952     # . . call
1953     e8/call  next-word-or-string/disp32
1954     # . . discard args
1955     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1956     # check-ints-equal(slice->end - slice->start, 0, msg)
1957     # . . push args
1958     68/push  "F - test-next-word-or-string-returns-empty-string-on-eof"/imm32
1959     68/push  0/imm32
1960     # . . push slice->end - slice->start
1961     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
1962     2b/subtract                     0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract *ECX from EAX
1963     50/push-EAX
1964     # . . call
1965     e8/call  check-ints-equal/disp32
1966     # . . discard args
1967     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1968     # . epilog
1969     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1970     5d/pop-to-EBP
1971     c3/return
1972 
1973 test-next-word-or-string-returns-whole-string:
1974     # . prolog
1975     55/push-EBP
1976     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1977     # setup
1978     # . clear-stream(_test-input-stream)
1979     # . . push args
1980     68/push  _test-input-stream/imm32
1981     # . . call
1982     e8/call  clear-stream/disp32
1983     # . . discard args
1984     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1985     # var slice/ECX = {0, 0}
1986     68/push  0/imm32/end
1987     68/push  0/imm32/start
1988     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
1989     # write(_test-input-stream, " \"a b\"/imm32 ")
1990     # . . push args
1991     68/push  " \"a b\"/imm32 "/imm32
1992     68/push  _test-input-stream/imm32
1993     # . . call
1994     e8/call  write/disp32
1995     # . . discard args
1996     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1997     # next-word-or-string(_test-input-stream, slice)
1998     # . . push args
1999     51/push-ECX
2000     68/push  _test-input-stream/imm32
2001     # . . call
2002     e8/call  next-word-or-string/disp32
2003     # . . discard args
2004     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2005     # check-ints-equal(slice->start - _test-input-stream->data, 1, msg)
2006     # . check-ints-equal(slice->start - _test-input-stream, 13, msg)
2007     # . . push args
2008     68/push  "F - test-next-word-or-string-returns-whole-string: start"/imm32
2009     68/push  0xd/imm32
2010     # . . push slice->start - _test-input-stream
2011     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
2012     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
2013     50/push-EAX
2014     # . . call
2015     e8/call  check-ints-equal/disp32
2016     # . . discard args
2017     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2018     # check-ints-equal(slice->end - _test-input-stream->data, 12, msg)
2019     # . check-ints-equal(slice->end - _test-input-stream, 24, msg)
2020     # . . push args
2021     68/push  "F - test-next-word-or-string-returns-whole-string: end"/imm32
2022     68/push  0x18/imm32
2023     # . . push slice->end - _test-input-stream
2024     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
2025     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
2026     50/push-EAX
2027     # . . call
2028     e8/call  check-ints-equal/disp32
2029     # . . discard args
2030     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2031     # . epilog
2032     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2033     5d/pop-to-EBP
2034     c3/return
2035 
2036 test-next-word-or-string-returns-string-with-escapes:
2037     # . prolog
2038     55/push-EBP
2039     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2040     # setup
2041     # . clear-stream(_test-input-stream)
2042     # . . push args
2043     68/push  _test-input-stream/imm32
2044     # . . call
2045     e8/call  clear-stream/disp32
2046     # . . discard args
2047     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2048     # var slice/ECX = {0, 0}
2049     68/push  0/imm32/end
2050     68/push  0/imm32/start
2051     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
2052     # write(_test-input-stream, " \"a\\\"b\"/x")
2053     # . . push args
2054     68/push  " \"a\\\"b\"/x"/imm32
2055     68/push  _test-input-stream/imm32
2056     # . . call
2057     e8/call  write/disp32
2058     # . . discard args
2059     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2060     # next-word-or-string(_test-input-stream, slice)
2061     # . . push args
2062     51/push-ECX
2063     68/push  _test-input-stream/imm32
2064     # . . call
2065     e8/call  next-word-or-string/disp32
2066     # . . discard args
2067     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2068     # check-ints-equal(slice->start - _test-input-stream->data, 1, msg)
2069     # . check-ints-equal(slice->start - _test-input-stream, 13, msg)
2070     # . . push args
2071     68/push  "F - test-next-word-or-string-returns-string-with-escapes: start"/imm32
2072     68/push  0xd/imm32
2073     # . . push slice->start - _test-input-stream
2074     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
2075     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
2076     50/push-EAX
2077     # . . call
2078     e8/call  check-ints-equal/disp32
2079     # . . discard args
2080     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2081     # check-ints-equal(slice->end - _test-input-stream->data, 9, msg)
2082     # . check-ints-equal(slice->end - _test-input-stream, 21, msg)
2083     # . . push args
2084     68/push  "F - test-next-word-or-string-returns-string-with-escapes: end"/imm32
2085     68/push  0x15/imm32
2086     # . . push slice->end - _test-input-stream
2087     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
2088     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
2089     50/push-EAX
2090     # . . call
2091     e8/call  check-ints-equal/disp32
2092     # . . discard args
2093     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2094     # . epilog
2095     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2096     5d/pop-to-EBP
2097     c3/return
2098 
2099 # update line->read to end of string literal surrounded by double quotes
2100 # line->read must start out at a double-quote
2101 skip-string:  # line : (address stream)
2102     # . prolog
2103     55/push-EBP
2104     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2105     # . save registers
2106     50/push-EAX
2107     51/push-ECX
2108     52/push-EDX
2109     # ECX = line
2110     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         1/r32/ECX   8/disp8         .                 # copy *(EBP+8) to ECX
2111     # EAX = skip-string-in-slice(&line->data[line->read], &line->data[line->write])
2112     # . . push &line->data[line->write]
2113     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .                         2/r32/EDX   8/disp8         .                 # copy *(ECX+8) to EDX
2114     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    1/base/ECX  2/index/EDX   .           2/r32/EDX   0xc/disp8       .                 # copy ECX+EDX+12 to EDX
2115     52/push-EDX
2116     # . . push &line->data[line->read]
2117     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .                         2/r32/EDX   4/disp8         .                 # copy *(ECX+4) to EDX
2118     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    1/base/ECX  2/index/EDX   .           2/r32/EDX   0xc/disp8       .                 # copy ECX+EDX+12 to EDX
2119     52/push-EDX
2120     # . . call
2121     e8/call  skip-string-in-slice/disp32
2122     # . . discard args
2123     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2124     # line->read = EAX - line->data
2125     29/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # subtract ECX from EAX
2126     2d/subtract-from-EAX  0xc/imm32
2127     89/copy                         1/mod/*+disp8   1/rm32/ECX    .           .                         0/r32/EAX   4/disp8         .                 # copy EAX to *(ECX+4)
2128 $skip-string:end:
2129     # . restore registers
2130     5a/pop-to-EDX
2131     59/pop-to-ECX
2132     58/pop-to-EAX
2133     # . epilog
2134     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2135     5d/pop-to-EBP
2136     c3/return
2137 
2138 test-skip-string:
2139     # . prolog
2140     55/push-EBP
2141     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2142     # setup
2143     # . clear-stream(_test-input-stream)
2144     # . . push args
2145     68/push  _test-input-stream/imm32
2146     # . . call
2147     e8/call  clear-stream/disp32
2148     # . . discard args
2149     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2150     # . write(_test-input-stream, "\"abc\" def")
2151     # .                   indices:  0123 45
2152     # . . push args
2153     68/push  "\"abc\" def"/imm32
2154     68/push  _test-input-stream/imm32
2155     # . . call
2156     e8/call  write/disp32
2157     # . . discard args
2158     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2159     # precondition: line->read == 0
2160     # . . push args
2161     68/push  "F - test-skip-string/precondition"/imm32
2162     68/push  0/imm32
2163     b8/copy-to-EAX  _test-input-stream/imm32
2164     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
2165     # . . call
2166     e8/call  check-ints-equal/disp32
2167     # . . discard args
2168     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2169     # skip-string(_test-input-stream)
2170     # . . push args
2171     68/push  _test-input-stream/imm32
2172     # . . call
2173     e8/call  skip-string/disp32
2174     # . . discard args
2175     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2176     # check-ints-equal(line->read, 5, msg)
2177     # . . push args
2178     68/push  "F - test-skip-string"/imm32
2179     68/push  5/imm32
2180     b8/copy-to-EAX  _test-input-stream/imm32
2181     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
2182     # . . call
2183     e8/call  check-ints-equal/disp32
2184     # . . discard args
2185     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2186     # . epilog
2187     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2188     5d/pop-to-EBP
2189     c3/return
2190 
2191 test-skip-string-ignores-spaces:
2192     # . prolog
2193     55/push-EBP
2194     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2195     # setup
2196     # . clear-stream(_test-input-stream)
2197     # . . push args
2198     68/push  _test-input-stream/imm32
2199     # . . call
2200     e8/call  clear-stream/disp32
2201     # . . discard args
2202     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2203     # . write(_test-input-stream, "\"a b\"/yz")
2204     # .                   indices:  0123 45
2205     # . . push args
2206     68/push  "\"a b\"/yz"/imm32
2207     68/push  _test-input-stream/imm32
2208     # . . call
2209     e8/call  write/disp32
2210     # . . discard args
2211     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2212     # precondition: line->read == 0
2213     # . . push args
2214     68/push  "F - test-skip-string-ignores-spaces/precondition"/imm32
2215     68/push  0/imm32
2216     b8/copy-to-EAX  _test-input-stream/imm32
2217     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
2218     # . . call
2219     e8/call  check-ints-equal/disp32
2220     # . . discard args
2221     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2222     # skip-string(_test-input-stream)
2223     # . . push args
2224     68/push  _test-input-stream/imm32
2225     # . . call
2226     e8/call  skip-string/disp32
2227     # . . discard args
2228     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2229     # check-ints-equal(line->read, 5, msg)
2230     # . . push args
2231     68/push  "F - test-skip-string-ignores-spaces"/imm32
2232     68/push  5/imm32
2233     b8/copy-to-EAX  _test-input-stream/imm32
2234     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
2235     # . . call
2236     e8/call  check-ints-equal/disp32
2237     # . . discard args
2238     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2239     # . epilog
2240     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2241     5d/pop-to-EBP
2242     c3/return
2243 
2244 test-skip-string-ignores-escapes:
2245     # . prolog
2246     55/push-EBP
2247     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2248     # setup
2249     # . clear-stream(_test-input-stream)
2250     # . . push args
2251     68/push  _test-input-stream/imm32
2252     # . . call
2253     e8/call  clear-stream/disp32
2254     # . . discard args
2255     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2256     # . write(_test-input-stream, "\"a\\\"b\"/yz")
2257     # .                   indices:  01 2 34 56
2258     # . . push args
2259     68/push  "\"a\\\"b\"/yz"/imm32
2260     68/push  _test-input-stream/imm32
2261     # . . call
2262     e8/call  write/disp32
2263     # . . discard args
2264     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2265     # precondition: line->read == 0
2266     # . . push args
2267     68/push  "F - test-skip-string-ignores-escapes/precondition"/imm32
2268     68/push  0/imm32
2269     b8/copy-to-EAX  _test-input-stream/imm32
2270     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
2271     # . . call
2272     e8/call  check-ints-equal/disp32
2273     # . . discard args
2274     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2275     # skip-string(_test-input-stream)
2276     # . . push args
2277     68/push  _test-input-stream/imm32
2278     # . . call
2279     e8/call  skip-string/disp32
2280     # . . discard args
2281     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2282     # check-ints-equal(line->read, 6, msg)
2283     # . . push args
2284     68/push  "F - test-skip-string-ignores-escapes"/imm32
2285     68/push  6/imm32
2286     b8/copy-to-EAX  _test-input-stream/imm32
2287     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
2288     # . . call
2289     e8/call  check-ints-equal/disp32
2290     # . . discard args
2291     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2292     # . epilog
2293     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2294     5d/pop-to-EBP
2295     c3/return
2296 
2297 test-skip-string-works-from-mid-stream:
2298     # . prolog
2299     55/push-EBP
2300     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2301     # setup
2302     # . clear-stream(_test-input-stream)
2303     # . . push args
2304     68/push  _test-input-stream/imm32
2305     # . . call
2306     e8/call  clear-stream/disp32
2307     # . . discard args
2308     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2309     # . write(_test-input-stream, "0 \"a\\\"b\"/yz")
2310     # .                   indices:  01 2 34 56
2311     # . . push args
2312     68/push  "0 \"a\\\"b\"/yz"/imm32
2313     68/push  _test-input-stream/imm32
2314     # . . call
2315     e8/call  write/disp32
2316     # . . discard args
2317     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2318     # precondition: line->read == 2
2319     c7          0/subop/copy        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         2/imm32           # copy to *(EAX+4)
2320     # skip-string(_test-input-stream)
2321     # . . push args
2322     68/push  _test-input-stream/imm32
2323     # . . call
2324     e8/call  skip-string/disp32
2325     # . . discard args
2326     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2327     # check-ints-equal(line->read, 8, msg)
2328     # . . push args
2329     68/push  "F - test-skip-string-works-from-mid-stream"/imm32
2330     68/push  8/imm32
2331     b8/copy-to-EAX  _test-input-stream/imm32
2332     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
2333     # . . call
2334     e8/call  check-ints-equal/disp32
2335     # . . discard args
2336     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2337     # . epilog
2338     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2339     5d/pop-to-EBP
2340     c3/return
2341 
2342 skip-string-in-slice:  # curr : (address byte), end : (address byte) -> new_curr/EAX
2343     # . prolog
2344     55/push-EBP
2345     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2346     # . save registers
2347     51/push-ECX
2348     52/push-EDX
2349     53/push-EBX
2350     # ECX = curr
2351     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         1/r32/ECX   8/disp8         .                 # copy *(EBP+8) to ECX
2352     # EDX = end
2353     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         2/r32/EDX   0xc/disp8         .               # copy *(EBP+12) to EDX
2354     # EAX = 0
2355     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
2356     # skip initial dquote
2357     41/increment-ECX
2358 $skip-string-in-slice:loop:
2359     # if (curr >= end) return curr
2360     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
2361     73/jump-if-greater-unsigned-or-equal  $skip-string-in-slice:return-curr/disp8
2362     # AL = *curr
2363     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
2364 $skip-string-in-slice:dquote:
2365     # if (EAX == '"') break
2366     3d/compare-EAX-and  0x22/imm32/double-quote
2367     74/jump-if-equal  $skip-string-in-slice:break/disp8
2368 $skip-string-in-slice:check-for-escape:
2369     # if (EAX == '\') escape next char
2370     3d/compare-EAX-and  0x5c/imm32/backslash
2371     75/jump-if-not-equal  $skip-string-in-slice:continue/disp8
2372 $skip-string-in-slice:escape:
2373     41/increment-ECX
2374 $skip-string-in-slice:continue:
2375     # ++curr
2376     41/increment-ECX
2377     eb/jump  $skip-string-in-slice:loop/disp8
2378 $skip-string-in-slice:break:
2379     # skip final dquote
2380     41/increment-ECX
2381 $skip-string-in-slice:return-curr:
2382     # return curr
2383     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy ECX to EAX
2384 $skip-string-in-slice:end:
2385     # . restore registers
2386     5b/pop-to-EBX
2387     5a/pop-to-EDX
2388     59/pop-to-ECX
2389     # . epilog
2390     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2391     5d/pop-to-EBP
2392     c3/return
2393 
2394 test-skip-string-in-slice:
2395     # . prolog
2396     55/push-EBP
2397     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2398     # setup: (EAX..ECX) = "\"abc\" def"
2399     b8/copy-to-EAX  "\"abc\" def"/imm32
2400     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
2401     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
2402     05/add-to-EAX  4/imm32
2403     # EAX = skip-string-in-slice(EAX, ECX)
2404     # . . push args
2405     51/push-ECX
2406     50/push-EAX
2407     # . . call
2408     e8/call  skip-string-in-slice/disp32
2409     # . . discard args
2410     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2411     # check-ints-equal(ECX-EAX, 4, msg)  # number of chars remaining after the string literal
2412     # . . push args
2413     68/push  "F - test-skip-string-in-slice"/imm32
2414     68/push  4/imm32
2415     # . . push ECX-EAX
2416     29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from ECX
2417     51/push-ECX
2418     # . . call
2419     e8/call  check-ints-equal/disp32
2420     # . . discard args
2421     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2422     # . epilog
2423     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2424     5d/pop-to-EBP
2425     c3/return
2426 
2427 test-skip-string-in-slice-ignores-spaces:
2428     # . prolog
2429     55/push-EBP
2430     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2431     # setup: (EAX..ECX) = "\"a b\"/yz"
2432     b8/copy-to-EAX  "\"a b\"/yz"/imm32
2433     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
2434     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
2435     05/add-to-EAX  4/imm32
2436     # EAX = skip-string-in-slice(EAX, ECX)
2437     # . . push args
2438     51/push-ECX
2439     50/push-EAX
2440     # . . call
2441     e8/call  skip-string-in-slice/disp32
2442     # . . discard args
2443     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2444     # check-ints-equal(ECX-EAX, 3, msg)  # number of chars remaining after the string literal
2445     # . . push args
2446     68/push  "F - test-skip-string-in-slice-ignores-spaces"/imm32
2447     68/push  3/imm32
2448     # . . push ECX-EAX
2449     29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from ECX
2450     51/push-ECX
2451     # . . call
2452     e8/call  check-ints-equal/disp32
2453     # . . discard args
2454     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2455     # . epilog
2456     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2457     5d/pop-to-EBP
2458     c3/return
2459 
2460 test-skip-string-in-slice-ignores-escapes:
2461     # . prolog
2462     55/push-EBP
2463     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2464     # setup: (EAX..ECX) = "\"a\\\"b\"/yz"
2465     b8/copy-to-EAX  "\"a\\\"b\"/yz"/imm32
2466     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
2467     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
2468     05/add-to-EAX  4/imm32
2469     # EAX = skip-string-in-slice(EAX, ECX)
2470     # . . push args
2471     51/push-ECX
2472     50/push-EAX
2473     # . . call
2474     e8/call  skip-string-in-slice/disp32
2475     # . . discard args
2476     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2477     # check-ints-equal(ECX-EAX, 3, msg)  # number of chars remaining after the string literal
2478     # . . push args
2479     68/push  "F - test-skip-string-in-slice-ignores-escapes"/imm32
2480     68/push  3/imm32
2481     # . . push ECX-EAX
2482     29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from ECX
2483     51/push-ECX
2484     # . . call
2485     e8/call  check-ints-equal/disp32
2486     # . . discard args
2487     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2488     # . epilog
2489     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2490     5d/pop-to-EBP
2491     c3/return
2492 
2493 test-skip-string-in-slice-stops-at-end:
2494     # . prolog
2495     55/push-EBP
2496     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2497     # setup: (EAX..ECX) = "\"abc"  # unbalanced dquote
2498     b8/copy-to-EAX  "\"abc"/imm32
2499     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
2500     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
2501     05/add-to-EAX  4/imm32
2502     # EAX = skip-string-in-slice(EAX, ECX)
2503     # . . push args
2504     51/push-ECX
2505     50/push-EAX
2506     # . . call
2507     e8/call  skip-string-in-slice/disp32
2508     # . . discard args
2509     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2510     # check-ints-equal(ECX-EAX, 0, msg)  # skipped to end of slice
2511     # . . push args
2512     68/push  "F - test-skip-string-in-slice-stops-at-end"/imm32
2513     68/push  0/imm32
2514     # . . push ECX-EAX
2515     29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from ECX
2516     51/push-ECX
2517     # . . call
2518     e8/call  check-ints-equal/disp32
2519     # . . discard args
2520     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2521     # . epilog
2522     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2523     5d/pop-to-EBP
2524     c3/return
2525 
2526 string-length-at-start-of-slice:  # curr : (address byte), end : (address byte) -> length/EAX
2527     # . prolog
2528     55/push-EBP
2529     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2530     # . save registers
2531     51/push-ECX
2532     52/push-EDX
2533     53/push-EBX
2534     # ECX = curr
2535     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         1/r32/ECX   8/disp8         .                 # copy *(EBP+8) to ECX
2536     # EDX = end
2537     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         2/r32/EDX   0xc/disp8         .               # copy *(EBP+12) to EDX
2538     # length/EAX = 0
2539     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
2540     # EBX = 0
2541     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
2542     # skip initial dquote
2543     41/increment-ECX
2544 $string-length-at-start-of-slice:loop:
2545     # if (curr >= end) return length
2546     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
2547     73/jump-if-greater-unsigned-or-equal  $string-length-at-start-of-slice:end/disp8
2548     # BL = *curr
2549     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           3/r32/BL    .               .                 # copy byte at *ECX to BL
2550 $string-length-at-start-of-slice:dquote:
2551     # if (EBX == '"') break
2552     81          7/subop/compare     3/mod/direct    3/rm32/EBX    .           .             .           .           .               0x22/imm32/dquote # compare EBX
2553     74/jump-if-equal  $string-length-at-start-of-slice:end/disp8
2554 $string-length-at-start-of-slice:check-for-escape:
2555     # if (EBX == '\') escape next char
2556     81          7/subop/compare     3/mod/direct    3/rm32/EBX    .           .             .           .           .               0x5c/imm32/backslash # compare EBX
2557     75/jump-if-not-equal  $string-length-at-start-of-slice:continue/disp8
2558 $string-length-at-start-of-slice:escape:
2559     # increment curr but not result
2560     41/increment-ECX
2561 $string-length-at-start-of-slice:continue:
2562     # ++result
2563     40/increment-EAX
2564     # ++curr
2565     41/increment-ECX
2566     eb/jump  $string-length-at-start-of-slice:loop/disp8
2567 $string-length-at-start-of-slice:end:
2568     # . restore registers
2569     5b/pop-to-EBX
2570     5a/pop-to-EDX
2571     59/pop-to-ECX
2572     # . epilog
2573     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2574     5d/pop-to-EBP
2575     c3/return
2576 
2577 test-string-length-at-start-of-slice:
2578     # . prolog
2579     55/push-EBP
2580     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2581     # setup: (EAX..ECX) = "\"abc\" def"
2582     b8/copy-to-EAX  "\"abc\" def"/imm32
2583     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
2584     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
2585     05/add-to-EAX  4/imm32
2586     # EAX = string-length-at-start-of-slice(EAX, ECX)
2587     # . . push args
2588     51/push-ECX
2589     50/push-EAX
2590     # . . call
2591     e8/call  string-length-at-start-of-slice/disp32
2592     # . . discard args
2593     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2594     # check-ints-equal(EAX, 3, msg)
2595     # . . push args
2596     68/push  "F - test-string-length-at-start-of-slice"/imm32
2597     68/push  3/imm32
2598     50/push-EAX
2599     # . . call
2600     e8/call  check-ints-equal/disp32
2601     # . . discard args
2602     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2603     # . epilog
2604     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2605     5d/pop-to-EBP
2606     c3/return
2607 
2608 test-string-length-at-start-of-slice-escaped:
2609     # . prolog
2610     55/push-EBP
2611     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2612     # setup: (EAX..ECX) = "\"ab\\c\" def"
2613     b8/copy-to-EAX  "\"ab\\c\" def"/imm32
2614     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
2615     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
2616     05/add-to-EAX  4/imm32
2617     # EAX = string-length-at-start-of-slice(EAX, ECX)
2618     # . . push args
2619     51/push-ECX
2620     50/push-EAX
2621     # . . call
2622     e8/call  string-length-at-start-of-slice/disp32
2623     # . . discard args
2624     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2625     # check-ints-equal(EAX, 3, msg)
2626     # . . push args
2627     68/push  "F - test-string-length-at-start-of-slice-escaped"/imm32
2628     68/push  3/imm32
2629     50/push-EAX
2630     # . . call
2631     e8/call  check-ints-equal/disp32
2632     # . . discard args
2633     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2634     # . epilog
2635     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2636     5d/pop-to-EBP
2637     c3/return
2638 
2639 == data
2640 
2641 Segment-size:
2642   0x1000/imm32/4KB
2643 
2644 Next-string-literal:  # tracks the next auto-generated variable name
2645   1/imm32
2646 
2647 # length-prefixed string containing just a single space
2648 Space:
2649     # size
2650     1/imm32
2651     # data
2652     20/space
2653 
2654 # length-prefixed string containing just a single slash
2655 Slash:
2656     # size
2657     1/imm32
2658     # data
2659     2f/slash
2660 
2661 _test-slice-abc:
2662   22/dquote 61/a 62/b 63/c 22/dquote  # "abc"
2663   2f/slash 64/d
2664 _test-slice-abc-limit:
2665 
2666 _test-slice-a-space-b:
2667   22/dquote 61/a 20/space 62/b 22/dquote  # "a b"
2668 _test-slice-a-space-b-limit:
2669 
2670 _test-slice-a-dquote-b:
2671   22/dquote 61/a 5c/backslash 22/dquote 62/b 22/dquote  # "a\"b"
2672 _test-slice-a-dquote-b-limit:
2673 
2674 # "abc/def"/ghi
2675 _test-slice-literal-string:
2676   22/dquote
2677   61/a 62/b 63/c  # abc
2678   2f/slash 64/d 65/e 66/f  # /def
2679   22/dquote
2680   2f/slash 67/g 68/h 69/i  # /ghi
2681 _test-slice-literal-string-with-limit:
2682 
2683 # . . vim:nowrap:textwidth=0