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