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