about summary refs log blame commit diff stats
path: root/apps/braces.subx
blob: 95b6a7eabc0b2034c0c9a56112a362c090ab192b (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12


                                                            

                                                                            






                              
                             












                                
                             














                                
                             









                                               
                

                        
                                 
                                     
                                   

                                                                  

                                                    
                                                        



                                       


                                       

                          
                      


                               
                                                                           
                 

                                                                        









                                                                             
                    


                                          
                    
                    
                                                                        





                                                                             
                                      


                                                           
                                     
                                              

                                      
                  
                


                        







                            
                                          




                                        
                                              



                                       
                                     
                                
                                      































































                                                                      

                                         













                                                             

                                         





















                                                       
                 

                                   
                         





                 
                




                                           
                




                                      

                                                      










                                                                                                              
                
















                        
                




                                      

                                                      










                                                                                                                                                                    
                




















                        
                




                                      

                                                      










                                                                                                                                                                                                         
                


                        
# Structured control flow using break/loop rather than jump.
#
# To run (on Linux):
#   $ ./ntranslate init.linux 0*.subx apps/subx-params.subx apps/braces.subx
#   $ mv a.elf apps/braces
#
# Example 1:
#   $ cat x.subx
#   {
#     7c/if-lesser break/disp8
#     74/if-equal loop/disp8
#   }
#   $ cat x.subx |apps/braces
#   _loop1:
#     7c/if-lesser _break1/disp8
#     74/if-equal _loop1/disp8
#   _break1:
#
# Example 2:
#   $ cat x.subx
#   {
#     7c/if-lesser break/disp8
#   }
#   {
#     74/if-equal loop/disp8
#   }
#   $ cat x.subx |apps/braces
#   _loop1:
#     7c/if-lesser _break1/disp8
#   _break1:
#   _loop2:
#     74/if-equal _loop2/disp8
#   _break2:
#
# Example 3:
#   $ cat x.subx
#   {
#     {
#       74/if-equal loop/disp8
#     }
#     7c/if-lesser loop/disp8
#   }
#   $ cat x.subx |apps/braces
#   _loop1:
#     _loop2:
#       74/if-equal _loop2/disp8
#     _break2:
#     7c/if-lesser _loop1/disp8
#   _break1:

== code

Entry:  # run tests if necessary, a REPL if not
    # . prologue
    89/<- %ebp 4/r32/esp
    # initialize heap
    (new-segment *Heap-size Heap)
    # if (argc <= 1) goto interactive
    81 7/subop/compare *ebp 1/imm32
    7e/jump-if-lesser-or-equal $subx-braces-main:interactive/disp8
    # if (argv[1] != "test")) goto interactive
    (kernel-string-equal? *(ebp+8) "test")  # => eax
    3d/compare-eax-and 0/imm32
    74/jump-if-equal $subx-braces-main:interactive/disp8
    #
    (run-tests)
    # syscall(exit, *Num-test-failures)
    8b/-> *Num-test-failures 3/r32/ebx
    eb/jump $subx-braces-main:end/disp8
$subx-braces-main:interactive:
    (subx-braces Stdin Stdout)
    # syscall(exit, 0)
    bb/copy-to-ebx 0/imm32
$subx-braces-main:end:
    b8/copy-to-eax 1/imm32/exit
    cd/syscall 0x80/imm8

subx-braces:  # in : (address buffered-file), out : (address buffered-file)
    # pseudocode:
    #   var line : (ref stream byte 512)
    #   var label-stack : (stack int 32)  # at most 32 levels of nesting
    #   var next-label-id : int = 1
    #   while true
    #     clear-stream(line)
    #     read-line-buffered(in, line)
    #     if (line->write == 0) break                           # end of file
    #     skip-chars-matching-whitespace(line)
    #     if line->data[line->read] == '{'
    #       print(out, "_loop" next-label-id ":\n")
    #       push(label-stack, next-label-id)
    #       ++next-label-id
    #       continue
    #     if line->data[line->read] == '}'
    #       var top = pop(label-stack)
    #       print(out, "_break" top ":\n")
    #       continue
    #     while true
    #       var word-slice : (address 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, "break/")
    #         var top = top(label-stack)
    #         print(out, "_break" top)
    #         word-slice->start += len("break")
    #       else if slice-starts-with?(word-slice, "loop/")
    #         var top = top(label-stack)
    #         print(out, "_loop" top)
    #         word-slice->start += len("loop")
    #       print(out, word-slice " ")
    #     print(out, "\n")
    #   flush(out)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    52/push-edx
    53/push-ebx
    56/push-esi
    57/push-edi
    # esi = in
    8b/-> *(ebp+8) 6/r32/esi
    # var line/ecx : (ref stream byte 512)
    81 5/subop/subtract %esp 0x200/imm32
    68/push 0x200/imm32/length
    68/push 0/imm32/read
    68/push 0/imm32/write
    89/<- %ecx 4/r32/esp
    # var label-stack/edx : (ref stack int 32)
    81 5/subop/subtract %esp 0x80/imm32
    68/push 0x80/imm32/length
    68/push 0/imm32/top
    89/<- %edx 4/r32/esp
    # var next-label-id/ebx : int = 1
    c7 0/subop/copy %ebx 1/imm32
    # var word-slice/edi : (ref slice)
    68/push 0/imm32/end
    68/push 0/imm32/start
    89/<- %edi 4/r32/esp
$subx-braces:line-loop:
    (clear-stream %ecx)
    (read-line-buffered %esi %ecx)
$subx-braces:check0:
    # if (line->write == 0) break
    81 7/subop/compare *ecx 0/imm32
    0f 84/jump-if-equal  $subx-braces:break/disp32
    (skip-chars-matching-whitespace %ecx)
$subx-braces:check-for-curly-open:
    # if (line->data[line->read] != '{') goto next check
    # . eax = line->data[line->read]
    8b/-> *(ecx+4) 0/r32/eax
    8a/copy-byte *(ecx+eax+0xc) 0/r32/AL
    81 4/subop/and %eax 0xff/imm32
    # . if (eax != '{') continue
    3d/compare-eax-and 0x7b/imm32/open-curly
    0f 85/jump-if-not-equal $subx-braces:check-for-curly-closed/disp32
$subx-braces:emit-curly-open:
    # print(out, "_loop" next-label-id ":")
    (write-buffered *(ebp+0xc) "_loop")
    (print-int32-buffered *(ebp+0xc) %ebx)
    (write-buffered *(ebp+0xc) ":")
    # push(label-stack, next-label-id)
    (push %edx %ebx)
    # ++next-label-id
    ff 0/subop/increment %ebx
    # continue
    e9/jump  $subx-braces:next-line/disp32
$subx-braces:check-for-curly-closed:
    # if (line->data[line->read] != '}') goto next check
    3d/compare-eax-and 0x7d/imm32/close-curly
    0f 85/jump-if-equal $subx-braces:word-loop/disp32
$subx-braces:emit-curly-closed:
    # eax = pop(label-stack)
    (pop %edx)
    # print(out, "_break" eax ":")
    (write-buffered *(ebp+0xc) "_break")
    (print-int32-buffered *(ebp+0xc) %eax)
    (write-buffered *(ebp+0xc) ":")
    # continue
    e9/jump  $subx-braces:next-line/disp32
$subx-braces:word-loop:
    (next-word-or-string %ecx %edi)
$subx-braces:check1:
    # if (slice-empty?(word-slice)) break
    (slice-empty? %edi)
    3d/compare-eax-and 0/imm32
    0f 85/jump-if-not-equal $subx-braces:next-line/disp32
$subx-braces:check-for-comment:
    # if (slice-starts-with?(word-slice, "#")) continue
    # . eax = *word-slice->start
    8b/-> *edi 0/r32/eax
    8a/copy-byte *eax 0/r32/AL
    81 4/subop/and %eax 0xff/imm32
    # . if (eax == '#') continue
    3d/compare-eax-and 0x23/imm32/hash
    74/jump-if-equal $subx-braces:word-loop/disp8
$subx-braces:check-for-break:
    # if (!slice-starts-with?(word-slice, "break/")) goto next check
    # . eax = slice-starts-with?(word-slice, "break/")
    (slice-starts-with? %edi "break/")
    # . if (eax == false) goto next check
    3d/compare-eax-and 0/imm32/false
    74/jump-if-equal $subx-braces:check-for-loop/disp8
$subx-braces:emit-break:
    (top %edx)
    # print(out, "_break" eax)
    (write-buffered *(ebp+0xc) "_break")
    (print-int32-buffered *(ebp+0xc) %eax)
    # word-slice->start += len("break")
    81 0/subop/add *edi 5/imm32/strlen
    # emit rest of word as usual
    eb/jump $subx-braces:emit-word-slice/disp8
$subx-braces:check-for-loop:
    # if (!slice-starts-with?(word-slice, "loop/")) emit word
    # . eax = slice-starts-with?(word-slice, "loop/")
    (slice-starts-with? %edi "loop/")
    # . if (eax == false) goto next check
    3d/compare-eax-and 0/imm32/false
    74/jump-if-equal $subx-braces:emit-word-slice/disp8
$subx-braces:emit-loop:
    (top %edx)
    # print(out, "_loop" eax)
    (write-buffered *(ebp+0xc) "_loop")
    (print-int32-buffered *(ebp+0xc) %eax)
    # word-slice->start += len("loop")
    81 0/subop/add *edi 4/imm32/strlen
    # fall through
$subx-braces:emit-word-slice:
    # print(out, word-slice " ")
    (write-slice-buffered *(ebp+0xc) %edi)
    (write-buffered *(ebp+0xc) Space)
    # loop to next word
    e9/jump $subx-braces:word-loop/disp32
$subx-braces:next-line:
    # print(out, "\n")
    (write-buffered *(ebp+0xc) Newline)
    # loop to next line
    e9/jump $subx-braces:line-loop/disp32
$subx-braces:break:
    (flush *(ebp+0xc))
$subx-braces:end:
    # . reclaim locals
    81 0/subop/add %esp 0x29c/imm32
    # . 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
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-subx-braces-passes-most-words-through:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream _test-output-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream $_test-output-buffered-file->buffer)
    # test
    (write _test-input-stream "== abcd 0x1")
    (subx-braces _test-input-buffered-file _test-output-buffered-file)
    # check that the line just passed through
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     # }}}
    (check-stream-equal _test-output-stream "== abcd 0x1 \n" "F - test-subx-braces-passes-most-words-through")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-subx-braces-1:
    # input:
    #   {
    #   ab break/imm32
    #   cd loop/imm32
    #   }
    #
    # output:
    #   _loop1:
    #   ab _break1/imm32
    #   cd _loop1/imm32
    #   _break1:
    #
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream _test-output-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream $_test-output-buffered-file->buffer)
    # test
    (write _test-input-stream "{\nab break/imm32\ncd loop/imm32\n}")
    (subx-braces _test-input-buffered-file _test-output-buffered-file)
    # check that the line just passed through
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     # }}}
    (check-stream-equal _test-output-stream "_loop0x00000001:\nab _break0x00000001/imm32 \ncd _loop0x00000001/imm32 \n_break0x00000001:\n" "F - test-subx-braces-1")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-subx-braces-2:
    # input:
    #   {
    #   {
    #   ab break/imm32
    #   }
    #   cd loop/imm32
    #   }
    #
    # output:
    #   _loop1:
    #   _loop2:
    #   ab _break2/imm32
    #   _break2:
    #   cd _loop1/imm32
    #   _break1:
    #
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream _test-output-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream $_test-output-buffered-file->buffer)
    # test
    (write _test-input-stream "{\n{\nab break/imm32\n}\ncd loop/imm32\n}")
    (subx-braces _test-input-buffered-file _test-output-buffered-file)
    # check that the line just passed through
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     # }}}
    (check-stream-equal _test-output-stream "_loop0x00000001:\n_loop0x00000002:\nab _break0x00000002/imm32 \n_break0x00000002:\ncd _loop0x00000001/imm32 \n_break0x00000001:\n" "F - test-subx-braces-2")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return