about summary refs log blame commit diff stats
path: root/subx/apps/dquotes.subx
blob: 0060e55c53f0fb3f3220991a6d0eeed0edfac905 (plain) (tree)




















                                                                                                                                                 









                                                                                                                                                                  
 
                                                  












































                                                                                                                                                                                      



                                                                              






                                                                                 
                                      

                                                                 
                                                      




                                                                                            
                                                                       
                
                                                   

                            
                                                




                                                                                                                                                                       













                                                                                                                                                                         

                          




























                                                                                                                                                                          
                                  



                                                                                                                                                                     
                                      






                                                                                                                                                                    
                                           



                   
                                       












                                                                                                                                                                  
                                                      








                                                                                                                                                                               
                                              

                                       
                                                     









                                                                                                                                                                      

                                     
                      
                                           



                                                                                                                                                                      
                                        

                                                                                                                                                                  
                  








                                                                                                                                                                      

                                     










                                                                                                                                                                      
               














                                                                                                                                                                      
             













                                                                                                                                                                       









                                                                                                                          



                                                                                                                                                                       
















                                                                                                                                                                         






                                                                                                                                                                                 








                                                                                                                                                                      






                                                                                                                                                                      
















                                                                                                                                                                      















                                                                                                                                                                                 
                                        






                                                                                                                                                                      

                                                                                                                                                                                    

                            
                                                                                                                                                                  
                         
                 




                                                                                                                                                                       







































                                                                                                                                                                       
                                  



                                                  
                                  
















                                                                                                                                                                  
                                                  
                   
                                  




































                                                                                                                                                                  
                                                  
                   
                                  






























                                                                                                                                                                  
                   



                                   
                   



























                                                                                                                                                                     
                                                                  
                   
                                                                
                     




                                                                                                                                                                  
                                                                  
                   
                                                                
                     




                                                                                                                                                                  
                                                                              
                   
                                                                
                                 




                                                                                                                                                                  
                                                                  
                   
                                                                
                     






                                                                                                                                                                  
                                                                







                                                                                                                                                                  
                                                                





                                                                                                                                                                  
                                                                      
                   
                                                                
                         




                                                                                                                                                                  
                                                                              
                   
                                                                
                                 






                                                                                                                                                                  
                                                                










                                                                                                                                                                       








































                                                                                                                                                                       
                                  




































                                                                                                                                                                  
                   













                                                                                                       
































                                                                                                                                                                     
                                                                              

                                                                 
                                 
















                                                                                                                                                                  
                                  




                                                                                                                                                                  
                                                                         

                                                                 
                            







                                                                                                                                                                  
                              







                                                                                                                                                                  
                                           







                                                                                                                                                                  
                              







                                                                                                                                                                  
                                                











                                                                                                                                                                       
                
                                                                     
                                   





                                      
                                         






                                 



                                                                                                                                                                       







                                                                                                                                                                             

                                                                                                                                                                            
                                      
                                                                       
                   
               

               
                                                   

                                                                                                                                                                  
                                 

                             
               













                                                                                                                                                                     

                                
               









                                                                                                                                                                               










                                                                                                                                                                               














































                                                                                                                                                                     
                              
                         







































                                                                                                                                                                            




                                                                                                                                                                       











                                                                                                                                                                       
                               
                                        



































                                                                                                                                                                       
                                                                               

                                                      
                                                     






















                                                                                                                                                                       

                          


































                                                                                                                                                                       
                                                                

                                                            
                                      























                                                                                                                                                                       
                                              



































                                                                                                                                                                       
                                                                                                                                                                    

                                                                                        
                                                   






















                                                                                                                                                                       
                                               



































                                                                                                                                                                       
                                                                             

                                                                               
                                                   









                                                                                                                                                                       

                                                                       
                
                                  
                          








                                                      
                           

                                        



                                                                                                                                                                       


               



                                                                                                                                                                             
                            


                                                                                                                                                                            



                                                                                                                                                                       
             
                           
                                        
                                                                 





                                                                                                                                                                               
               
               





                                                                                                                                                                       
                                      
                               
                              

                                                                                                                                                                           
                                              
                             
                                                                                                                                                                               

                                               


                                                 
                    
                        

                                                                                                                                                                        
                   
               
                                                                                                                                                                     
              
                                        
                      
                                                                                                                                                                  
                   

                                                                                                                                                                  
                         

                 


                 




                                                                                                                                                                       




















                                                                                                                                                                       







                                                                                                                                                                             


















































                                                                                                                                                                       







                                                                                                                                                                             


















































                                                                                                                                                                       







                                                                                                                                                                             
























                                                                                                                                                                       




                                                                                                                                                                       























































                                                                                                                                                                             




                                                                                                                                                                       





















                                                                                                                                                                       
                                                        
                                                   























































                                                                                                                                                                       

                                                     
                                                                           



















                                                                                                                                                                             
                            




                                                                                                                                                                             
                                                                   


                                                                                                                                                                        

                                           









                                                                                                                                                                                        

                                                                             




                                                                                                                                                                              
                                                                                                                                                                        

                                                                                                                                                                            

                                              





                                                                                                                                                                                        

                                                                 
                       
                   

               
                               
                      
                                                                                                                                                                  
                  
                                  
                                                                            









                                                                                                                                                                              
                         









                                                                                                                                                                       
                         



                                                                                                                                                                       
                                        
                   
                                     




                                                                                                                                                                  

                          
                                                                                                                                                                       
                                       

                         
                                     



                                                                                                                                                                  
                                                    

                   
                                     
              
                                       

                                                                                                                                                                  

                                                        
                                                                               






                                                                                                                                                                     

                                                                       
                   
                                                        
                      
                                                
                                                                                                                                                                        
                                                                                                                                                                                




                                                                                                                                                                  

                                                                     
                   
                                                      
                       
                                              
                                                                                                                                                                            
                                                                                                                                                                                









                                                                                                                                                                       
                                               



                                                                                                                                                                       
                                        
                   
                                     




                                                                                                                                                                  

                          
                                                                                                                                                                       
                                        

                          
                                     



                                                                                                                                                                  
                                                    

                   
                                     
              
                                       

                                                                                                                                                                  

                                                        
                                                                                                     






                                                                                                                                                                     

                                                                       
                   
                                                                              
                      
                                                
                                                                                                                                                                        
                                                                                                                                                                                




                                                                                                                                                                  

                                                                     
                   
                                                                            
                       
                                              
                                                                                                                                                                            
                                                                                                                                                                                









                                                                                                                                                                       
                                                     



                                                                                                                                                                       
                                        
                   
                                     




                                                                                                                                                                  

                          
                                                                                                                                                                       
                                         
                                                    

                   
                                     
              
                                       



                                                                                                                                                                  
                                                                             













                                                                                                                                                                              
                                              



                                                                                                                                                                       
                                        
                   
                                     




                                                                                                                                                                  

                          
                                                                                                                                                                       
                                                  
                   
                                    
                                     



                                                                                                                                                                  
                                                    

                   
                                     
              
                                       

                                                                                                                                                                  

                                                                       
                   
                                                                             
                      
                                                
                                                                                                                                                                        
                                                                                                                                                                                




                                                                                                                                                                  

                                                                      
                   
                                                                           
                       
                                              
                                                                                                                                                                            
                                                                                                                                                                                









                                                                                                                                                                       
                                                     



                                                                                                                                                                       
                                        
                   
                                     




                                                                                                                                                                  

                          
                                                                                                                                                                       
                                                
                   
                                  
                                     



                                                                                                                                                                  
                                                    

                   
                                     
              
                                       

                                                                                                                                                                  

                                                                       
                   
                                                                                    
                      
                                                
                                                                                                                                                                        
                                                                                                                                                                                




                                                                                                                                                                  

                                                                     
                   
                                                                                  
                       
                                              
                                                                                                                                                                            
                                                                                                                                                                                









                                                                                                                                                                       
                                                                        
                                             




                                                                                                                                                                       


               

                                                                                                                                                                            

                                                                                   
                                                                                                                                                                            
                                                                                                                                                                              
               
                                      
                                                                                                                                                                            
                                                                                                                                                                              
               


                                        
                                                                                                                                                                  


                                                                                                                                                                             
                                                                                                                                                                            

                         


                 



































































































































































                                                                                                                                                                       












































                                                                                                                                                                        




                                                                                                                                                                       


               

                                                                                                                                                                            

                                                                                                                                                                             

                                                                                                                                                                 
                         
                    
                           


                                                                                                                                                                            








                                                                                                                                                                               
                                                              

                             


                               
                                             
                            
                       
                    
                                  
                 
                                                                                                                                                                       

                          


                 










































                                                                                                                                                                             
                                      



























































                                                                                                                                                                             
































                                                                                                                                                                             
















































































































                                                                                                                                                                               


             
                  
 
                                                                    

         













                                                       

                                             
               
                      


                                                 
                            


                                                                
                             
 





                                 
                                 
                                      
 
                            
# Translate literal strings within double quotes.
# Replace them with references to new variables in the data segment.
#
# To run (from the subx/ directory):
#   $ ./subx translate *.subx apps/dquote.subx -o apps/dquote
#   $ cat x
#   == code
#   ab "cd ef"/imm32
#   $ cat x  |./subx run apps/dquote
#   == code
#   ab __string1/imm32
#   == data
#   __string1:
#     5/imm32
#     0x63/c 0x64/d 0x20/  0x65/e 0x66/f

== code
#   instruction                     effective address                                                   register    displacement    immediate
# . op          subop               mod             rm32          base        index         scale       r32
# . 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

Entry:
    # initialize heap
    # . Heap = new-segment(64KB)
    # . . push args
    68/push  Heap/imm32
    68/push  0x10000/imm32/64KB
    # . . call
    e8/call  new-segment/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP

    # run tests if necessary, convert stdin if not
    # . prolog
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # - if argc > 1 and argv[1] == "test", then return run_tests()
    # . argc > 1
    81          7/subop/compare     1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0/disp8         1/imm32           # compare *EBP
    7e/jump-if-lesser-or-equal  $run-main/disp8
    # . argv[1] == "test"
    # . . push args
    68/push  "test"/imm32
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    # . . call
    e8/call  kernel-string-equal?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . check result
    3d/compare-EAX-and  1/imm32
    75/jump-if-not-equal  $run-main/disp8
    # . run-tests()
    e8/call  run-tests/disp32
    8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
    eb/jump  $main:end/disp8
$run-main:
    # - otherwise convert stdin
    # var ed/EAX : exit-descriptor
    81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
    89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EAX
    # configure ed to really exit()
    # . ed->target = 0
    c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # copy to *EAX
    # return convert(Stdin, 1/stdout, 2/stderr, ed)
    # . . push args
    50/push-EAX/ed
    68/push  Stderr/imm32
    68/push  Stdout/imm32
    68/push  Stdin/imm32
    # . . call
    e8/call  convert/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
    # . syscall(exit, 0)
    bb/copy-to-EBX  0/imm32
$main:end:
    b8/copy-to-EAX  1/imm32/exit
    cd/syscall  0x80/imm8

# conceptual hierarchy within a line:
#   line = words separated by ' ', maybe followed by comment starting with '#'
#   word = datum until '/', then 0 or more metadata separated by '/'

convert:  # in : (address buffered-file), out : (address buffered-file) -> <void>
    # pseudocode:
    #   var line = new-stream(512, 1)
    #   var new-data-segment = new-stream(Heap, Segment-size, 1)
    #   write-stream(new-data-segment, "== data\n")
    #   while true
    #     clear-stream(line)
    #     read-line-buffered(in, line)
    #     if (line->write == 0) break               # end of file
    #     while true
    #       var word-slice = next-word-or-string(line)
    #       if slice-empty?(word-slice)             # end of line
    #         break
    #       if slice-starts-with?(word-slice, "#")  # comment
    #         continue
    #       if slice-starts-with?(word-slice, '"')  # string literal <== what we're here for
    #         process-string-literal(word-slice, out, new-data-segment)
    #       else
    #         write-slice-buffered(out, word-slice)
    #       write(out, " ")
    #     write(out, "\n\n")
    #   write-stream-data(out, new-data-segment)
    #   flush(out)
    #
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # . save registers
    50/push-EAX
    51/push-ECX
    52/push-EDX
    53/push-EBX
    56/push-ESI
    57/push-EDI
    # var line/ECX : (address stream byte) = stream(512)
    81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x200/imm32       # subtract from ESP
    68/push  0x200/imm32/length
    68/push  0/imm32/read
    68/push  0/imm32/write
    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    # var word-slice/EDX = {0, 0}
    68/push  0/imm32/end
    68/push  0/imm32/start
    89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
    # new-data-segment/EDI = new-stream(Heap, Segment-size, 1)
    # . EAX = new-stream(Heap, Segment-size, 1)
    # . . push args
    68/push  1/imm32
    ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Segment-size/disp32               # push *Segment-size
    68/push  Heap/imm32
    # . . call
    e8/call  new-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . EDI = EAX
    89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to EDI
    # write(new-data-segment, "== data\n")
    # . . push args
    68/push  "== data\n"/imm32
    57/push-EDI
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
$convert:line-loop:
    # clear-stream(line)
    # . . push args
    51/push-ECX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # read-line-buffered(in, line)
    # . . push args
    51/push-ECX
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    # . . call
    e8/call  read-line-buffered/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
$convert:check0:
    # if (line->write == 0) break
    81          7/subop/compare     0/mod/indirect  1/rm32/ECX    .           .             .           .           .               0/imm32           # compare *ECX
    0f 84/jump-if-equal  $convert:break/disp32
$convert:word-loop:
    # next-word-or-string(line, word-slice)
    # . . push args
    52/push-EDX
    51/push-ECX
    # . . call
    e8/call  next-word-or-string/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
$convert:check1:
    # if (slice-empty?(word-slice)) break
    # . EAX = slice-empty?(word-slice)
    # . . push args
    52/push-EDX
    # . . call
    e8/call  slice-empty?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . if (EAX != 0) break
    3d/compare-EAX-and  0/imm32
    0f 85/jump-if-not-equal  $convert:next-line/disp32
$convert:check-for-comment:
    # if (slice-starts-with?(word-slice, "#")) continue
    # . start/ESI = word-slice->start
    8b/copy                         0/mod/indirect  2/rm32/EDX    .           .             .           6/r32/ESI   .               .                 # copy *EDX to ESI
    # . c/EAX = *start
    31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
    8a/copy-byte                    0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/AL    .               .                 # copy byte at *ESI to AL
    # . if (EAX == '#') continue
    3d/compare-EAX-and  0x23/imm32/hash
    74/jump-if-equal  $convert:word-loop/disp8
$convert:check-for-string-literal:
    3d/compare-EAX-and  0x22/imm32/hash
    75/jump-if-not-equal  $convert:regular-word/disp8
$convert:string-literal:
    # process-string-literal(word-slice, out, new-data-segment)
    # . . push args
    57/push-EDI
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    52/push-EDX
    # . . call
    e8/call  process-string-literal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # continue
    eb/jump  $convert:next-word/disp8
$convert:regular-word:
    # write-slice-buffered(out, word-slice)
    # . . push args
    52/push-EDX
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    # . . call
    e8/call  write-slice-buffered/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # fall through
$convert:next-word:
    # write-buffered(out, " ")
    # . . push args
    68/push  " "/imm32
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    # . . call
    e8/call  write-buffered/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # loop
    eb/jump  $convert:word-loop/disp8
$convert:next-line:
    # write-buffered(out, "\n")
    # . . push args
    68/push  Newline/imm32
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    # . . call
    e8/call  write-buffered/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # loop
    e9/jump  $convert:line-loop/disp32
$convert:break:
    # write-stream-data(out, new-data-segment)
    # . . push args
    57/push-EDI
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    # . . call
    e8/call  write-stream-data/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # flush(out)
    # . . push args
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
$convert:end:
    # . reclaim locals
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x214/imm32       # add to ESP
    # . restore registers
    5f/pop-to-EDI
    5e/pop-to-ESI
    5b/pop-to-EBX
    5a/pop-to-EDX
    59/pop-to-ECX
    58/pop-to-EAX
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

# Write out 'string-literal' in a new format to 'out-segment', assign it a new
# label, and write the new label out to 'out'.
process-string-literal:  # string-literal : (address slice), out : (address buffered-file), out-segment : (address stream)
    # pseudocode:
    #   print(out-segment, "_string#{Next-string-literal}:\n")
    #   emit-string-literal-data(out-segment, string-literal)
    #   print(out, "_string#{Next-string-literal}")
    #   emit-metadata(out, string-literal)
    #   ++ *Next-string-literal
    #
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # . save registers
    51/push-ECX
    # var int32-stream/ECX = stream(10)  # number of decimal digits a 32-bit number can have
    81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xa/imm32         # subtract from ESP
    68/push  0xa/imm32/decimal-digits-in-32bit-number
    68/push  0/imm32/read
    68/push  0/imm32/write
    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    # print(out-segment, "_string#{Next-string-literal}:\n")
    # . write(out-segment, "_string")
    # . . push args
    68/push  "_string"/imm32
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . print-int32-decimal(out-segment, *Next-string-literal)
    # . . push args
    ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Next-string-literal/disp32        # push *Next-string-literal
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
    # . . call
    e8/call  print-int32-decimal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . write(out-segment, ":\n")
    # . . push args
    68/push  ":\n"/imm32
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # emit-string-literal-data(out-segment, string-literal)
    # . . push args
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x8/disp8       .                 # push *(EBP+8)
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
    # . . call
    e8/call  emit-string-literal-data/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # write(out-segment, "\n")
    # . . push args
    68/push  Newline/imm32
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # print(out, "_string#{Next-string-literal}")
    # . write-buffered(out, "_string")
    # . . push args
    68/push  "_string"/imm32
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    # . . call
    e8/call  write-buffered/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . print-int32-decimal(int32-stream, *Next-string-literal)
    # . . push args
    ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Next-string-literal/disp32        # push *Next-string-literal
    51/push-ECX
    # . . call
    e8/call  print-int32-decimal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . write-stream-data(out, int32-stream)
    # . . push args
    51/push-ECX
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    # . . call
    e8/call  write-stream-data/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # emit-metadata(out, string-literal)
    # . . push args
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    # . . call
    e8/call  emit-metadata/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # ++ *Next-string-literal
    ff          0/subop/increment   0/mod/indirect  5/rm32/.disp32            .             .           .           Next-string-literal/disp32        # increment *Num-test-failures
$process-string-literal:end:
    # . reclaim locals
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x16/imm32        # add to ESP
    # . restore registers
    59/pop-to-ECX
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-convert-is-idempotent-by-default:
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-input-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-input-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # initialize input (meta comments in parens)
    #   # comment 1
    #     # comment 2 indented
    #   == code 0x1  (new segment)
    #   # comment 3 inside a segment
    #   1
    #                         (empty line)
    #   2 3 # comment 4 inline with other contents
    #   == data 0x2  (new segment)
    #   4 5/imm32
    # . write(_test-input-stream, "# comment 1\n")
    # . . push args
    68/push  "# comment 1\n"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . write(_test-input-stream, "  # comment 2 indented\n")
    # . . push args
    68/push  "  # comment 2 indented\n"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . write(_test-input-stream, "== code 0x1\n")
    # . . push args
    68/push  "== code 0x1\n"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . write(_test-input-stream, "# comment 3 inside a segment\n")
    # . . push args
    68/push  "# comment 3 inside a segment\n"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . write(_test-input-stream, "1\n")
    # . . push args
    68/push  "1\n"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . write(_test-input-stream, "\n")  # empty line
    # . . push args
    68/push  "\n"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . write(_test-input-stream, "2 3 # comment 4 inline with other contents\n")
    # . . push args
    68/push  "2 3 # comment 4 inline with other contents\n"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . write(_test-input-stream, "== data 0x2\n")
    # . . push args
    68/push  "== data 0x2\n"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . write(_test-input-stream, "4 5/imm32\n")
    # . . push args
    68/push  "4 5/imm32\n"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # convert(_test-input-buffered-file, _test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    68/push  _test-input-buffered-file/imm32
    # . . call
    e8/call  convert/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # check output
    #     (comment dropped for now)
    #     (comment dropped for now)
    #   == code 0x1
    #     (comment dropped for now)
    #   1
    #     (comment dropped for now)
    #   2 3
    #   == data 0x2
    #   4 5/imm32
    # We don't care right now what exactly happens to comments. Trailing spaces are also minor details.
#?     # dump output {{{
#?     # . write(2/stderr, "result: ^")
#?     # . . push args
#?     68/push  "result: ^"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write-stream(2/stderr, _test-output-stream)
#?     # . . push args
#?     68/push  _test-output-stream/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
    # . check-next-stream-line-equal(_test-output-stream, "", msg)
    # . . push args
    68/push  "F - test-convert-is-idempotent-by-default/0"/imm32
    68/push  ""/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-next-stream-line-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . check-next-stream-line-equal(_test-output-stream, "", msg)
    # . . push args
    68/push  "F - test-convert-is-idempotent-by-default/1"/imm32
    68/push  ""/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-next-stream-line-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . check-next-stream-line-equal(_test-output-stream, "== code 0x1 ", msg)
    # . . push args
    68/push  "F - test-convert-is-idempotent-by-default/2"/imm32
    68/push  "== code 0x1 "/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-next-stream-line-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . check-next-stream-line-equal(_test-output-stream, "", msg)
    # . . push args
    68/push  "F - test-convert-is-idempotent-by-default/3"/imm32
    68/push  ""/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-next-stream-line-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . check-next-stream-line-equal(_test-output-stream, "1 ", msg)
    # . . push args
    68/push  "F - test-convert-is-idempotent-by-default/4"/imm32
    68/push  "1 "/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-next-stream-line-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . check-next-stream-line-equal(_test-output-stream, "", msg)
    # . . push args
    68/push  "F - test-convert-is-idempotent-by-default/5"/imm32
    68/push  ""/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-next-stream-line-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . check-next-stream-line-equal(_test-output-stream, "2 3 ", msg)
    # . . push args
    68/push  "F - test-convert-is-idempotent-by-default/6"/imm32
    68/push  "2 3 "/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-next-stream-line-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . check-next-stream-line-equal(_test-output-stream, "== data 0x2 ", msg)
    # . . push args
    68/push  "F - test-convert-is-idempotent-by-default/7"/imm32
    68/push  "== data 0x2 "/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-next-stream-line-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . check-next-stream-line-equal(_test-output-stream, "4 5/imm32 ", msg)
    # . . push args
    68/push  "F - test-convert-is-idempotent-by-default/8"/imm32
    68/push  "4 5/imm32 "/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-next-stream-line-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-convert-processes-string-literals:
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-input-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-input-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # initialize input (meta comments in parens)
    #   == code  (new segment)
    #   1 "a"/x
    #   2 "bc"/y
    68/push  "== code 0x1\n"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . write(_test-input-stream, "1 \"a\"/x\n")
    # . . push args
    68/push  "1 \"a\"/x\n"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . write(_test-input-stream, "2 \"bc\"/y\n")
    # . . push args
    68/push  "2 \"bc\"/y\n"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # convert(_test-input-buffered-file, _test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    68/push  _test-input-buffered-file/imm32
    # . . call
    e8/call  convert/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # check output
    #   == code 0x1
    #   1 _string1/x
    #   2 _string2/y
    #   == data
    #   _string1:
    #   1/imm32 61/a
    #   _string2:
    #   2/imm32 62/b 63/c
    # We don't care right now what exactly happens to comments. Trailing spaces are also minor details.
    #
    # Open question: how to make this check more robust.
    # We don't actually care what the auto-generated string variables are
    # called. We just want to make sure instructions using string literals
    # switch to a string variable with the right value.
    # (Modifying string literals completely off the radar for now.)
#?     # dump output {{{
#?     # . write(2/stderr, "result: ^")
#?     # . . push args
#?     68/push  "result: ^"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write-stream(2/stderr, _test-output-stream)
#?     # . . push args
#?     68/push  _test-output-stream/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . rewind-stream(_test-output-stream)
#?     # . . push args
#?     68/push  _test-output-stream/imm32
#?     # . . call
#?     e8/call  rewind-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # }}}
    # . check-next-stream-line-equal(_test-output-stream, "== code 0x1 ", msg)
    # . . push args
    68/push  "F - test-convert-processes-string-literals/0"/imm32
    68/push  "== code 0x1 "/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-next-stream-line-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . check-next-stream-line-equal(_test-output-stream, "1 _string1/x ", msg)
    # . . push args
    68/push  "F - test-convert-processes-string-literals/1"/imm32
    68/push  "1 _string1/x "/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-next-stream-line-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . check-next-stream-line-equal(_test-output-stream, "2 _string2/y ", msg)
    # . . push args
    68/push  "F - test-convert-processes-string-literals/2"/imm32
    68/push  "2 _string2/y "/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-next-stream-line-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . check-next-stream-line-equal(_test-output-stream, "== data", msg)
    # . . push args
    68/push  "F - test-convert-processes-string-literals/3"/imm32
    68/push  "== data"/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-next-stream-line-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . check-next-stream-line-equal(_test-output-stream, "_string1: ", msg)
    # . . push args
    68/push  "F - test-convert-processes-string-literals/4"/imm32
    68/push  "_string1:"/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-next-stream-line-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . check-next-stream-line-equal(_test-output-stream, "1/imm32 61/a ", msg)
    # . . push args
    68/push  "F - test-convert-processes-string-literals/5"/imm32
    68/push  "0x00000001/imm32 61/a "/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-next-stream-line-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . check-next-stream-line-equal(_test-output-stream, "_string2: ", msg)
    # . . push args
    68/push  "F - test-convert-processes-string-literals/6"/imm32
    68/push  "_string2:"/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-next-stream-line-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . check-next-stream-line-equal(_test-output-stream, "2/imm32 62/b 63/c ", msg)
    # . . push args
    68/push  "F - test-convert-processes-string-literals/7"/imm32
    68/push  "0x00000002/imm32 62/b 63/c "/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-next-stream-line-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

# generate the data segment contents byte by byte for a given slice
emit-string-literal-data:  # out : (address stream), word : (address slice)
    # pseudocode
    #   len = string-length-at-start-of-slice(word->start, word->end)
    #   print(out, "#{len}/imm32 ")
    #   curr = word->start
    #   ++curr  # skip '"'
    #   while true
    #     if (curr >= word->end) break
    #     c = *curr
    #     if (c == '"') break
    #     if (c == '\') ++curr, c = *curr
    #     append-byte-hex(out, c)
    #     if c is alphanumeric:
    #       write(out, "/")
    #       append-byte(out, c)
    #     write(out, " ")
    #     ++curr
    #
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # . save registers
    50/push-EAX
    51/push-ECX
    52/push-EDX
    56/push-ESI
    # ESI = word
    8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
    # curr/EDX = word->start
    8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           2/r32/EDX   .               .                 # copy *ESI to EDX
    # max/ESI = word->end
    8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           6/r32/ESI   4/disp8         .                 # copy *(ESI+4) to ESI
$emit-string-literal-data:emit-length:
    # len/EAX = string-length-at-start-of-slice(word->start, word->end)
    # . . push args
    56/push-ESI
    52/push-EDX
    # . . call
    e8/call  string-length-at-start-of-slice/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # print(out, "#{len}/imm32 ")
    # . print-int32(out, len)
    # . . push args
    50/push-EAX
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    # . . call
    e8/call  print-int32/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . write(out, "/imm32 ")
    # . . push args
    68/push  "/imm32 "/imm32
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
$emit-string-literal-data:loop-init:
    # ++curr  # skip initial '"'
    42/increment-EDX
    # c/ECX = 0
    31/xor                          3/mod/direct    1/rm32/ECX    .           .             .           1/r32/ECX   .               .                 # clear ECX
$emit-string-literal-data:loop:
    # if (curr >= max) break
    39/compare                      3/mod/direct    2/rm32/EDX    .           .             .           6/r32/ESI   .               .                 # compare EDX with ESI
    7d/jump-if-greater-or-equal  $emit-string-literal-data:end/disp8
    # CL = *curr
    8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           1/r32/CL    .               .                 # copy byte at *EDX to CL
    # if (ECX == '"') break
    81          7/subop/compare     3/mod/direct    1/rm32/ECX    .           .             .           .           .               0x22/imm32/dquote # compare ECX
    74/jump-if-equal  $emit-string-literal-data:end/disp8
    # if (ECX == '\') ++curr, ECX = *curr
    81          7/subop/compare     3/mod/direct    1/rm32/ECX    .           .             .           .           .               0x5c/imm32/backslash  # compare ECX
    75/jump-if-not-equal  $emit-string-literal-data:emit/disp8
    # . ++curr
    42/increment-EDX
    # . if (curr >= max) break
    39/compare                      3/mod/direct    2/rm32/EDX    .           .             .           6/r32/ESI   .               .                 # compare EDX with ESI
    7d/jump-if-greater-or-equal  $emit-string-literal-data:end/disp8
    # . CL = *curr
    8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           1/r32/CL    .               .                 # copy byte at *EDX to CL
$emit-string-literal-data:emit:
    # append-byte-hex(out, CL)
    # . . push args
    51/push-ECX
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    # . . call
    e8/call  append-byte-hex/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # if (is-alphanumeric?(*curr)) print(out, "/#{*curr}")
    # . EAX = is-alphanumeric?(CL)
    # . . push args
    51/push-ECX
    # . . call
    e8/call  is-alphanumeric?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . if (EAX == 0) goto char-done
    3d/compare-EAX-and  0/imm32
    74/jump-if-equal  $emit-string-literal-data:char-done/disp8
    # . write(out, "/")
    # . . push args
    68/push  Slash/imm32
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . append-byte(out, *curr)
    # . . push args
    51/push-ECX
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    # . . call
    e8/call  append-byte/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
$emit-string-literal-data:char-done:
    # write(out, " ")
    # . . push args
    68/push  Space/imm32
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # ++curr
    42/increment-EDX
    eb/jump $emit-string-literal-data:loop/disp8
$emit-string-literal-data:end:
    # . restore registers
    5e/pop-to-ESI
    5a/pop-to-EDX
    59/pop-to-ECX
    58/pop-to-EAX
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

is-alphanumeric?:  # c : int -> EAX : boolean
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # EAX = c
    8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   8/disp8         .                 # copy *(EBP+8) to EAX
    # if (EAX < '0') return false
    3d/compare-EAX-with  0x30/imm32/0
    7c/jump-if-lesser  $is-alphanumeric?:false/disp8
    # if (EAX <= '9') return true
    3d/compare-EAX-with  0x39/imm32/9
    7e/jump-if-lesser-or-equal  $is-alphanumeric?:true/disp8
    # if (EAX < 'A') return false
    3d/compare-EAX-with  0x41/imm32/A
    7c/jump-if-lesser  $is-alphanumeric?:false/disp8
    # if (EAX <= 'Z') return true
    3d/compare-EAX-with  0x5a/imm32/Z
    7e/jump-if-lesser-or-equal  $is-alphanumeric?:true/disp8
    # if (EAX < 'a') return false
    3d/compare-EAX-with  0x61/imm32/a
    7c/jump-if-lesser  $is-alphanumeric?:false/disp8
    # if (EAX <= 'z') return true
    3d/compare-EAX-with  0x7a/imm32/z
    7e/jump-if-lesser-or-equal  $is-alphanumeric?:true/disp8
    # return false
$is-alphanumeric?:false:
    b8/copy-to-EAX  0/imm32/false
    eb/jump  $is-alphanumeric?:end/disp8
$is-alphanumeric?:true:
    b8/copy-to-EAX  1/imm32/true
$is-alphanumeric?:end:
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-emit-string-literal-data:
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # var slice/ECX = '"abc"/d'
    68/push  _test-slice-abc-limit/imm32
    68/push  _test-slice-abc/imm32
    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    # emit-string-literal-data(_test-output-stream, slice)
    # . . push args
    51/push-ECX
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  emit-string-literal-data/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # dump output {{{
#?     # . write(2/stderr, "result: ^")
#?     # . . push args
#?     68/push  "result: ^"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write-stream(2/stderr, _test-output-stream)
#?     # . . push args
#?     68/push  _test-output-stream/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
    # . check-stream-equal(_test-output-stream, "3/imm32 61/a 62/b 63/c ", msg)
    # . . push args
    68/push  "F - test-emit-string-literal-data"/imm32
    68/push  "0x00000003/imm32 61/a 62/b 63/c "/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-emit-string-literal-data-empty:
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # var slice/ECX = '""'
    68/push  0/imm32/end
    68/push  0/imm32/start
    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    # emit-string-literal-data(_test-output-stream, slice)
    # . . push args
    51/push-ECX
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  emit-string-literal-data/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # dump output {{{
#?     # . write(2/stderr, "result: ^")
#?     # . . push args
#?     68/push  "result: ^"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write-stream(2/stderr, _test-output-stream)
#?     # . . push args
#?     68/push  _test-output-stream/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
    # . check-stream-equal(_test-output-stream, "0/imm32 ", msg)
    # . . push args
    68/push  "F - test-emit-string-literal-data-empty"/imm32
    68/push  "0x00000000/imm32 "/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

# just to keep things simple
test-emit-string-literal-data-no-metadata-for-non-alphanumerics:
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # var slice/ECX = '"a b"'
    68/push  _test-slice-a-space-b-limit/imm32
    68/push  _test-slice-a-space-b/imm32
    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    # emit-string-literal-data(_test-output-stream, slice)
    # . . push args
    51/push-ECX
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  emit-string-literal-data/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # dump output {{{
#?     # . write(2/stderr, "result: ^")
#?     # . . push args
#?     68/push  "result: ^"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write-stream(2/stderr, _test-output-stream)
#?     # . . push args
#?     68/push  _test-output-stream/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
    # . 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
    # . . push args
    68/push  "F - test-emit-string-literal-data-no-metadata-for-non-alphanumerics"/imm32
    68/push  "0x00000003/imm32 61/a 20 62/b "/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-emit-string-literal-data-handles-escape-sequences:
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # var slice/ECX = '"a\"b"'
    68/push  _test-slice-a-dquote-b-limit/imm32
    68/push  _test-slice-a-dquote-b/imm32
    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    # emit-string-literal-data(_test-output-stream, slice)
    # . . push args
    51/push-ECX
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  emit-string-literal-data/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # dump output {{{
#?     # . write(2/stderr, "result: ^")
#?     # . . push args
#?     68/push  "result: ^"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write-stream(2/stderr, _test-output-stream)
#?     # . . push args
#?     68/push  _test-output-stream/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
    # . check-stream-equal(_test-output-stream, "3/imm32 61/a 22 62/b ", msg)
    # . . push args
    68/push  "F - test-emit-string-literal-data-handles-escape-sequences"/imm32
    68/push  "0x00000003/imm32 61/a 22 62/b "/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

# emit everything from a word except the initial datum
emit-metadata:  # out : (address buffered-file), word : (address slice)
    # pseudocode
    #   var slice = {0, word->end}
    #   curr = word->start
    #   if *curr == '"'
    #     curr = skip-string-in-slice(curr, word->end)
    #   else
    #     while true
    #       if curr == word->end
    #         return
    #       if *curr == '/'
    #         break
    #       ++curr
    #   slice->start = curr
    #   write-slice-buffered(out, slice)
    #
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # . save registers
    50/push-EAX
    51/push-ECX
    52/push-EDX
    53/push-EBX
    56/push-ESI
    # ESI = word
    8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
    # curr/ECX = word->start
    8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # copy *ESI to ECX
    # end/EDX = word->end
    8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           2/r32/EDX   4/disp8         .                 # copy *(ESI+4) to EDX
    # var slice/EBX = {0, end}
    52/push-EDX
    68/push  0/imm32
    89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBX
    # EAX = 0
    b8/copy-to-EAX  0/imm32
$emit-metadata:check-for-string-literal:
    # -  if (*curr == '"') curr = skip-string-in-slice(curr, end)
    8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
    3d/compare-EAX-and  0x22/imm32/dquote
    75/jump-if-not-equal  $emit-metadata:skip-datum-loop/disp8
$emit-metadata:skip-string-literal:
    # . EAX = skip-string-in-slice(curr, end)
    # . . push args
    52/push-EDX
    51/push-ECX
    # . . call
    e8/call  skip-string-in-slice/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . curr = EAX
    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy EAX to ECX
    eb/jump  $emit-metadata:emit/disp8
$emit-metadata:skip-datum-loop:
    # - otherwise scan for '/'
    # if (curr == end) return
    39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX and EDX
    74/jump-if-equal  $emit-metadata:end/disp8
    # if (*curr == '/') break
    8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
    3d/compare-EAX-and  0x2f/imm32/slash
    74/jump-if-equal  $emit-metadata:emit/disp8
    # ++curr
    41/increment-ECX
    eb/jump  $emit-metadata:skip-datum-loop/disp8
$emit-metadata:emit:
    # slice->start = ECX
    89/copy                         0/mod/indirect  3/rm32/EBX    .           .             .           1/r32/ECX   .               .                 # copy ECX to *EBX
    # write-slice-buffered(out, slice)
    # . . push args
    53/push-EBX
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    # . . call
    e8/call  write-slice-buffered/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           8/imm32      .                    # add to ESP
$emit-metadata:end:
    # . reclaim locals
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           8/imm32      .                    # add to ESP
    # . restore registers
    5e/pop-to-ESI
    5b/pop-to-EBX
    5a/pop-to-EDX
    59/pop-to-ECX
    58/pop-to-EAX
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-emit-metadata:
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # (EAX..ECX) = "abc/def"
    b8/copy-to-EAX  "abc/def"/imm32
    8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
    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
    05/add-to-EAX  4/imm32
    # var slice/ECX = {EAX, ECX}
    51/push-ECX
    50/push-EAX
    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    # emit-metadata(_test-output-buffered-file, slice)
    # . . push args
    51/push-ECX
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  emit-metadata/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # check-stream-equal(_test-output-stream, "/def", msg)  # important that there's no leading space
    # . . push args
    68/push  "F - test-emit-metadata"/imm32
    68/push  "/def"/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-emit-metadata-none:
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # (EAX..ECX) = "abc"
    b8/copy-to-EAX  "abc"/imm32
    8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
    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
    05/add-to-EAX  4/imm32
    # var slice/ECX = {EAX, ECX}
    51/push-ECX
    50/push-EAX
    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    # emit-metadata(_test-output-buffered-file, slice)
    # . . push args
    51/push-ECX
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  emit-metadata/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # check-stream-equal(_test-output-stream, "", msg)
    # . . push args
    68/push  "F - test-emit-metadata-none"/imm32
    68/push  ""/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-emit-metadata-multiple:
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # (EAX..ECX) = "abc/def/ghi"
    b8/copy-to-EAX  "abc/def/ghi"/imm32
    8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
    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
    05/add-to-EAX  4/imm32
    # var slice/ECX = {EAX, ECX}
    51/push-ECX
    50/push-EAX
    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    # emit-metadata(_test-output-buffered-file, slice)
    # . . push args
    51/push-ECX
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  emit-metadata/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # check-stream-equal(_test-output-stream, "/def/ghi", msg)  # important that there's no leading space
    # . . push args
    68/push  "F - test-emit-metadata-multiple"/imm32
    68/push  "/def/ghi"/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-emit-metadata-when-no-datum:
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # var slice/ECX = "/abc"
    b8/copy-to-EAX  "/abc"/imm32
    # . push end/ECX
    8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
    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
    51/push-ECX
    # . push curr/EAX
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . save stack pointer
    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    # emit-metadata(_test-output-buffered-file, slice)
    # . . push args
    51/push-ECX
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  emit-metadata/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # check-stream-equal(_test-output-stream, "/abc", msg)  # nothing skipped
    # . . push args
    68/push  "F - test-emit-metadata-when-no-datum"/imm32
    68/push  "/abc"/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-emit-metadata-in-string-literal:
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # var slice/ECX = "\"abc/def\"/ghi"
    68/push  _test-slice-literal-string-with-limit/imm32
    68/push  _test-slice-literal-string/imm32/start
    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    # emit-metadata(_test-output-buffered-file, slice)
    # . . push args
    51/push-ECX
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  emit-metadata/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # dump output {{{
#?     # . write(2/stderr, "result: ^")
#?     # . . push args
#?     68/push  "result: ^"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write-stream(2/stderr, _test-output-stream)
#?     # . . push args
#?     68/push  _test-output-stream/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
    # check-stream-equal(_test-output-stream, "/ghi", msg)  # important that there's no leading space
    # . . push args
    68/push  "F - test-emit-metadata-in-string-literal"/imm32
    68/push  "/ghi"/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

# (re)compute the bounds of the next word in the line
# return empty string on reaching end of file
next-word-or-string:  # line : (address stream byte), out : (address slice)
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # . save registers
    50/push-EAX
    51/push-ECX
    56/push-ESI
    57/push-EDI
    # ESI = line
    8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
    # EDI = out
    8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   0xc/disp8       .                 # copy *(EBP+12) to EDI
    # skip-chars-matching(line, ' ')
    # . . push args
    68/push  0x20/imm32/space
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    # . . call
    e8/call  skip-chars-matching/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
$next-word-or-string:check0:
    # if (line->read >= line->write) clear out and return
    # . EAX = line->read
    8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ESI+4) to EAX
    # . if (EAX < line->write) goto next check
    3b/compare                      0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # compare EAX with *ESI
    7c/jump-if-lesser  $next-word-or-string:check-for-comment/disp8
    # . return out = {0, 0}
    c7          0/subop/copy        0/mod/direct    7/rm32/EDI    .           .             .           .           .               0/imm32           # copy to *EDI
    c7          0/subop/copy        1/mod/*+disp8   7/rm32/EDI    .           .             .           .           4/disp8         0/imm32           # copy to *(EDI+4)
    eb/jump  $next-word-or-string:end/disp8
$next-word-or-string:check-for-comment:
    # out->start = &line->data[line->read]
    8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
    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
    89/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EDI
    # if line->data[line->read] == '#'
    # . EAX = line->data[line->read]
    31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
    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
    # . compare
    3d/compare-EAX-and  0x23/imm32/pound
    75/jump-if-not-equal  $next-word-or-string:check-for-string-literal/disp8
$next-word-or-string:comment:
    # out->end = &line->data[line->write]
    8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
    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
    89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
    # line->read = line->write  # skip rest of line
    8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
    89/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(ESI+4)
    # return
    eb/jump  $next-word-or-string:end/disp8
$next-word-or-string:check-for-string-literal:
    # if line->data[line->read] == '"'
    # . EAX = line->data[line->read]
    31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
    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
    # . compare
    3d/compare-EAX-and  0x22/imm32/dquote
    75/jump-if-not-equal  $next-word-or-string:regular-word/disp8
$next-word-or-string:string-literal:
    # skip-string(line)
    # . . push args
    56/push-ESI
    # . . call
    e8/call  skip-string/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # fall through
$next-word-or-string:regular-word:
    # skip-chars-not-matching-whitespace(line)  # including trailing newline
    # . . push args
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    # . . call
    e8/call  skip-chars-not-matching-whitespace/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # out->end = &line->data[line->read]
    8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
    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
    89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
$next-word-or-string:end:
    # . restore registers
    5f/pop-to-EDI
    5e/pop-to-ESI
    59/pop-to-ECX
    58/pop-to-EAX
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-next-word-or-string:
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # var slice/ECX = {0, 0}
    68/push  0/imm32/end
    68/push  0/imm32/start
    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    # write(_test-input-stream, "  ab")
    # . . push args
    68/push  "  ab"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # next-word-or-string(_test-input-stream, slice)
    # . . push args
    51/push-ECX
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  next-word-or-string/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check-ints-equal(_test-input-stream->read, 4, msg)
    # . . push args
    68/push  "F - test-next-word-or-string/updates-stream-read-correctly"/imm32
    68/push  4/imm32
    b8/copy-to-EAX  _test-input-stream/imm32
    ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # check-ints-equal(slice->start - _test-input-stream->data, 2, msg)
    # . check-ints-equal(slice->start - _test-input-stream, 14, msg)
    # . . push args
    68/push  "F - test-next-word-or-string: start"/imm32
    68/push  0xe/imm32
    # . . push slice->start - _test-input-stream
    8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
    81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
    50/push-EAX
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # check-ints-equal(slice->end - _test-input-stream->data, 4, msg)
    # . check-ints-equal(slice->end - _test-input-stream, 16, msg)
    # . . push args
    68/push  "F - test-next-word-or-string: end"/imm32
    68/push  0x10/imm32
    # . . push slice->end - _test-input-stream
    8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
    81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
    50/push-EAX
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-next-word-or-string-returns-whole-comment:
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # var slice/ECX = {0, 0}
    68/push  0/imm32/end
    68/push  0/imm32/start
    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    # write(_test-input-stream, "  # a")
    # . . push args
    68/push  "  # a"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # next-word-or-string(_test-input-stream, slice)
    # . . push args
    51/push-ECX
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  next-word-or-string/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check-ints-equal(_test-input-stream->read, 5, msg)
    # . . push args
    68/push  "F - test-next-word-or-string-returns-whole-comment/updates-stream-read-correctly"/imm32
    68/push  5/imm32
    b8/copy-to-EAX  _test-input-stream/imm32
    ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # check-ints-equal(slice->start - _test-input-stream->data, 2, msg)
    # . check-ints-equal(slice->start - _test-input-stream, 14, msg)
    # . . push args
    68/push  "F - test-next-word-or-string-returns-whole-comment: start"/imm32
    68/push  0xe/imm32
    # . . push slice->start - _test-input-stream
    8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
    81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
    50/push-EAX
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # check-ints-equal(slice->end - _test-input-stream->data, 5, msg)
    # . check-ints-equal(slice->end - _test-input-stream, 17, msg)
    # . . push args
    68/push  "F - test-next-word-or-string-returns-whole-comment: end"/imm32
    68/push  0x11/imm32
    # . . push slice->end - _test-input-stream
    8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
    81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
    50/push-EAX
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-next-word-or-string-returns-empty-string-on-eof:
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # var slice/ECX = {0, 0}
    68/push  0/imm32/end
    68/push  0/imm32/start
    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    # write nothing to _test-input-stream
    # next-word-or-string(_test-input-stream, slice)
    # . . push args
    51/push-ECX
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  next-word-or-string/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check-ints-equal(slice->end - slice->start, 0, msg)
    # . . push args
    68/push  "F - test-next-word-or-string-returns-empty-string-on-eof"/imm32
    68/push  0/imm32
    # . . push slice->end - slice->start
    8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
    2b/subtract                     0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract *ECX from EAX
    50/push-EAX
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-next-word-or-string-returns-whole-string:
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # var slice/ECX = {0, 0}
    68/push  0/imm32/end
    68/push  0/imm32/start
    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    # write(_test-input-stream, " \"a b\"/imm32 ")
    # . . push args
    68/push  " \"a b\"/imm32 "/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # next-word-or-string(_test-input-stream, slice)
    # . . push args
    51/push-ECX
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  next-word-or-string/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check-ints-equal(slice->start - _test-input-stream->data, 1, msg)
    # . check-ints-equal(slice->start - _test-input-stream, 13, msg)
    # . . push args
    68/push  "F - test-next-word-or-string-returns-whole-string: start"/imm32
    68/push  0xd/imm32
    # . . push slice->start - _test-input-stream
    8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
    81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
    50/push-EAX
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # check-ints-equal(slice->end - _test-input-stream->data, 12, msg)
    # . check-ints-equal(slice->end - _test-input-stream, 24, msg)
    # . . push args
    68/push  "F - test-next-word-or-string-returns-whole-string: end"/imm32
    68/push  0x18/imm32
    # . . push slice->end - _test-input-stream
    8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
    81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
    50/push-EAX
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-next-word-or-string-returns-string-with-escapes:
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # var slice/ECX = {0, 0}
    68/push  0/imm32/end
    68/push  0/imm32/start
    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    # write(_test-input-stream, " \"a\\\"b\"/x")
    # . . push args
    68/push  " \"a\\\"b\"/x"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # next-word-or-string(_test-input-stream, slice)
    # . . push args
    51/push-ECX
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  next-word-or-string/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check-ints-equal(slice->start - _test-input-stream->data, 1, msg)
    # . check-ints-equal(slice->start - _test-input-stream, 13, msg)
    # . . push args
    68/push  "F - test-next-word-or-string-returns-string-with-escapes: start"/imm32
    68/push  0xd/imm32
    # . . push slice->start - _test-input-stream
    8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
    81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
    50/push-EAX
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # check-ints-equal(slice->end - _test-input-stream->data, 9, msg)
    # . check-ints-equal(slice->end - _test-input-stream, 21, msg)
    # . . push args
    68/push  "F - test-next-word-or-string-returns-string-with-escapes: end"/imm32
    68/push  0x15/imm32
    # . . push slice->end - _test-input-stream
    8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
    81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
    50/push-EAX
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

# update line->read to end of string literal surrounded by double quotes
# line->read must start out at a double-quote
skip-string:  # line : (address stream)
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # . save registers
    50/push-EAX
    51/push-ECX
    52/push-EDX
    # ECX = line
    8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         1/r32/ECX   8/disp8         .                 # copy *(EBP+8) to ECX
    # EAX = skip-string-in-slice(&line->data[line->read], &line->data[line->write])
    # . . push &line->data[line->write]
    8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .                         2/r32/EDX   8/disp8         .                 # copy *(ECX+8) to EDX
    8d/copy-address                 1/mod/*+disp8   4/rm32/sib    1/base/ECX  2/index/EDX   .           2/r32/EDX   0xc/disp8       .                 # copy ECX+EDX+12 to EDX
    52/push-EDX
    # . . push &line->data[line->read]
    8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .                         2/r32/EDX   4/disp8         .                 # copy *(ECX+4) to EDX
    8d/copy-address                 1/mod/*+disp8   4/rm32/sib    1/base/ECX  2/index/EDX   .           2/r32/EDX   0xc/disp8       .                 # copy ECX+EDX+12 to EDX
    52/push-EDX
    # . . call
    e8/call  skip-string-in-slice/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # line->read = EAX - line->data
    29/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # subtract ECX from EAX
    2d/subtract-from-EAX  0xc/imm32
    89/copy                         1/mod/*+disp8   1/rm32/ECX    .           .                         0/r32/EAX   4/disp8         .                 # copy EAX to *(ECX+4)
$skip-string:end:
    # . restore registers
    5a/pop-to-EDX
    59/pop-to-ECX
    58/pop-to-EAX
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-skip-string:
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . write(_test-input-stream, "\"abc\" def")
    # .                   indices:  0123 45
    # . . push args
    68/push  "\"abc\" def"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # precondition: line->read == 0
    # . . push args
    68/push  "F - test-skip-string/precondition"/imm32
    68/push  0/imm32
    b8/copy-to-EAX  _test-input-stream/imm32
    ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # skip-string(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  skip-string/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # check-ints-equal(line->read, 5, msg)
    # . . push args
    68/push  "F - test-skip-string"/imm32
    68/push  5/imm32
    b8/copy-to-EAX  _test-input-stream/imm32
    ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-skip-string-ignores-spaces:
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . write(_test-input-stream, "\"a b\"/yz")
    # .                   indices:  0123 45
    # . . push args
    68/push  "\"a b\"/yz"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # precondition: line->read == 0
    # . . push args
    68/push  "F - test-skip-string-ignores-spaces/precondition"/imm32
    68/push  0/imm32
    b8/copy-to-EAX  _test-input-stream/imm32
    ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # skip-string(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  skip-string/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # check-ints-equal(line->read, 5, msg)
    # . . push args
    68/push  "F - test-skip-string-ignores-spaces"/imm32
    68/push  5/imm32
    b8/copy-to-EAX  _test-input-stream/imm32
    ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-skip-string-ignores-escapes:
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . write(_test-input-stream, "\"a\\\"b\"/yz")
    # .                   indices:  01 2 34 56
    # . . push args
    68/push  "\"a\\\"b\"/yz"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # precondition: line->read == 0
    # . . push args
    68/push  "F - test-skip-string-ignores-escapes/precondition"/imm32
    68/push  0/imm32
    b8/copy-to-EAX  _test-input-stream/imm32
    ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # skip-string(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  skip-string/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # check-ints-equal(line->read, 6, msg)
    # . . push args
    68/push  "F - test-skip-string-ignores-escapes"/imm32
    68/push  6/imm32
    b8/copy-to-EAX  _test-input-stream/imm32
    ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-skip-string-works-from-mid-stream:
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . write(_test-input-stream, "0 \"a\\\"b\"/yz")
    # .                   indices:  01 2 34 56
    # . . push args
    68/push  "0 \"a\\\"b\"/yz"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # precondition: line->read == 2
    c7          0/subop/copy        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         2/imm32           # copy to *(EAX+4)
    # skip-string(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  skip-string/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # check-ints-equal(line->read, 8, msg)
    # . . push args
    68/push  "F - test-skip-string-works-from-mid-stream"/imm32
    68/push  8/imm32
    b8/copy-to-EAX  _test-input-stream/imm32
    ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

skip-string-in-slice:  # curr : (address byte), end : (address byte) -> new_curr/EAX
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # . save registers
    51/push-ECX
    52/push-EDX
    53/push-EBX
    # ECX = curr
    8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         1/r32/ECX   8/disp8         .                 # copy *(EBP+8) to ECX
    # EDX = end
    8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         2/r32/EDX   0xc/disp8         .               # copy *(EBP+12) to EDX
    # EAX = 0
    31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
    # skip initial dquote
    41/increment-ECX
$skip-string-in-slice:loop:
    # if (curr >= end) return curr
    39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
    73/jump-if-greater-unsigned-or-equal  $skip-string-in-slice:return-curr/disp8
    # AL = *curr
    8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
$skip-string-in-slice:dquote:
    # if (EAX == '"') break
    3d/compare-EAX-and  0x22/imm32/double-quote
    74/jump-if-equal  $skip-string-in-slice:break/disp8
$skip-string-in-slice:check-for-escape:
    # if (EAX == '\') escape next char
    3d/compare-EAX-and  0x5c/imm32/backslash
    75/jump-if-not-equal  $skip-string-in-slice:continue/disp8
$skip-string-in-slice:escape:
    41/increment-ECX
$skip-string-in-slice:continue:
    # ++curr
    41/increment-ECX
    eb/jump  $skip-string-in-slice:loop/disp8
$skip-string-in-slice:break:
    # skip final dquote
    41/increment-ECX
$skip-string-in-slice:return-curr:
    # return curr
    89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy ECX to EAX
$skip-string-in-slice:end:
    # . restore registers
    5b/pop-to-EBX
    5a/pop-to-EDX
    59/pop-to-ECX
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-skip-string-in-slice:
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup: (EAX..ECX) = "\"abc\" def"
    b8/copy-to-EAX  "\"abc\" def"/imm32
    8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
    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
    05/add-to-EAX  4/imm32
    # EAX = skip-string-in-slice(EAX, ECX)
    # . . push args
    51/push-ECX
    50/push-EAX
    # . . call
    e8/call  skip-string-in-slice/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check-ints-equal(ECX-EAX, 4, msg)  # number of chars remaining after the string literal
    # . . push args
    68/push  "F - test-skip-string-in-slice"/imm32
    68/push  4/imm32
    # . . push ECX-EAX
    29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from ECX
    51/push-ECX
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-skip-string-in-slice-ignores-spaces:
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup: (EAX..ECX) = "\"a b\"/yz"
    b8/copy-to-EAX  "\"a b\"/yz"/imm32
    8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
    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
    05/add-to-EAX  4/imm32
    # EAX = skip-string-in-slice(EAX, ECX)
    # . . push args
    51/push-ECX
    50/push-EAX
    # . . call
    e8/call  skip-string-in-slice/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check-ints-equal(ECX-EAX, 3, msg)  # number of chars remaining after the string literal
    # . . push args
    68/push  "F - test-skip-string-in-slice-ignores-spaces"/imm32
    68/push  3/imm32
    # . . push ECX-EAX
    29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from ECX
    51/push-ECX
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-skip-string-in-slice-ignores-escapes:
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup: (EAX..ECX) = "\"a\\\"b\"/yz"
    b8/copy-to-EAX  "\"a\\\"b\"/yz"/imm32
    8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
    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
    05/add-to-EAX  4/imm32
    # EAX = skip-string-in-slice(EAX, ECX)
    # . . push args
    51/push-ECX
    50/push-EAX
    # . . call
    e8/call  skip-string-in-slice/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check-ints-equal(ECX-EAX, 3, msg)  # number of chars remaining after the string literal
    # . . push args
    68/push  "F - test-skip-string-in-slice-ignores-escapes"/imm32
    68/push  3/imm32
    # . . push ECX-EAX
    29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from ECX
    51/push-ECX
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-skip-string-in-slice-stops-at-end:
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup: (EAX..ECX) = "\"abc"  # unbalanced dquote
    b8/copy-to-EAX  "\"abc"/imm32
    8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
    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
    05/add-to-EAX  4/imm32
    # EAX = skip-string-in-slice(EAX, ECX)
    # . . push args
    51/push-ECX
    50/push-EAX
    # . . call
    e8/call  skip-string-in-slice/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check-ints-equal(ECX-EAX, 0, msg)  # skipped to end of slice
    # . . push args
    68/push  "F - test-skip-string-in-slice-stops-at-end"/imm32
    68/push  0/imm32
    # . . push ECX-EAX
    29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from ECX
    51/push-ECX
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

string-length-at-start-of-slice:  # curr : (address byte), end : (address byte) -> length/EAX
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # . save registers
    51/push-ECX
    52/push-EDX
    53/push-EBX
    # ECX = curr
    8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         1/r32/ECX   8/disp8         .                 # copy *(EBP+8) to ECX
    # EDX = end
    8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         2/r32/EDX   0xc/disp8         .               # copy *(EBP+12) to EDX
    # length/EAX = 0
    31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
    # EBX = 0
    31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
    # skip initial dquote
    41/increment-ECX
$string-length-at-start-of-slice:loop:
    # if (curr >= end) return length
    39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
    73/jump-if-greater-unsigned-or-equal  $string-length-at-start-of-slice:end/disp8
    # BL = *curr
    8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           3/r32/BL    .               .                 # copy byte at *ECX to BL
$string-length-at-start-of-slice:dquote:
    # if (EBX == '"') break
    81          7/subop/compare     3/mod/direct    3/rm32/EBX    .           .             .           .           .               0x22/imm32/dquote # compare EBX
    74/jump-if-equal  $string-length-at-start-of-slice:end/disp8
$string-length-at-start-of-slice:check-for-escape:
    # if (EBX == '\') escape next char
    81          7/subop/compare     3/mod/direct    3/rm32/EBX    .           .             .           .           .               0x5c/imm32/backslash # compare EBX
    75/jump-if-not-equal  $string-length-at-start-of-slice:continue/disp8
$string-length-at-start-of-slice:escape:
    # increment curr but not result
    41/increment-ECX
$string-length-at-start-of-slice:continue:
    # ++result
    40/increment-EAX
    # ++curr
    41/increment-ECX
    eb/jump  $string-length-at-start-of-slice:loop/disp8
$string-length-at-start-of-slice:end:
    # . restore registers
    5b/pop-to-EBX
    5a/pop-to-EDX
    59/pop-to-ECX
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-string-length-at-start-of-slice:
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup: (EAX..ECX) = "\"abc\" def"
    b8/copy-to-EAX  "\"abc\" def"/imm32
    8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
    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
    05/add-to-EAX  4/imm32
    # EAX = string-length-at-start-of-slice(EAX, ECX)
    # . . push args
    51/push-ECX
    50/push-EAX
    # . . call
    e8/call  string-length-at-start-of-slice/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check-ints-equal(EAX, 3, msg)
    # . . push args
    68/push  "F - test-string-length-at-start-of-slice"/imm32
    68/push  3/imm32
    50/push-EAX
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-string-length-at-start-of-slice-escaped:
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup: (EAX..ECX) = "\"ab\\c\" def"
    b8/copy-to-EAX  "\"ab\\c\" def"/imm32
    8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
    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
    05/add-to-EAX  4/imm32
    # EAX = string-length-at-start-of-slice(EAX, ECX)
    # . . push args
    51/push-ECX
    50/push-EAX
    # . . call
    e8/call  string-length-at-start-of-slice/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check-ints-equal(EAX, 3, msg)
    # . . push args
    68/push  "F - test-string-length-at-start-of-slice-escaped"/imm32
    68/push  3/imm32
    50/push-EAX
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

== data

Segment-size:
  0x1000/imm32/4KB

Next-string-literal:  # tracks the next auto-generated variable name
  1/imm32

# length-prefixed string containing just a single space
Space:
    # size
    1/imm32
    # data
    20/space

# length-prefixed string containing just a single slash
Slash:
    # size
    1/imm32
    # data
    2f/slash

_test-slice-abc:
  22/dquote 61/a 62/b 63/c 22/dquote  # "abc"
  2f/slash 64/d
_test-slice-abc-limit:

_test-slice-a-space-b:
  22/dquote 61/a 20/space 62/b 22/dquote  # "a b"
_test-slice-a-space-b-limit:

_test-slice-a-dquote-b:
  22/dquote 61/a 5c/backslash 22/dquote 62/b 22/dquote  # "a\"b"
_test-slice-a-dquote-b-limit:

# "abc/def"/ghi
_test-slice-literal-string:
  22/dquote
  61/a 62/b 63/c  # abc
  2f/slash 64/d 65/e 66/f  # /def
  22/dquote
  2f/slash 67/g 68/h 69/i  # /ghi
_test-slice-literal-string-with-limit:

# . . vim:nowrap:textwidth=0