# 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 = new-stream(512, 1) # var label-stack : (address stack) = new-stack(32*4) # 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 : (address stream byte) = stream(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 : (address stack) 81 5/subop/subtract %esp 0x80/imm32 68/push 0x80/imm32/length 68/push 0/imm32/top 89/<- %edx 4/r32/esp # next-label-id/ebx = 1 c7 0/subop/copy %ebx 1/imm32 # var word-slice/edi = {0, 0} 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 == 0) goto next check 3d/compare-eax-and 0/imm32 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 == 0) goto next check 3d/compare-eax-and 0/imm32 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+4) b8/copy-to-eax _test-input-buffered-file/imm32 05/add-to-eax 4/imm32 (clear-stream %eax) # . clear-stream(_test-output-buffered-file+4) b8/copy-to-eax _test-output-buffered-file/imm32 05/add-to-eax 4/imm32 (clear-stream %eax) # 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+4) b8/copy-to-eax _test-input-buffered-file/imm32 05/add-to-eax 4/imm32 (clear-stream %eax) # . clear-stream(_test-output-buffered-file+4) b8/copy-to-eax _test-output-buffered-file/imm32 05/add-to-eax 4/imm32 (clear-stream %eax) # 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+4) b8/copy-to-eax _test-input-buffered-file/imm32 05/add-to-eax 4/imm32 (clear-stream %eax) # . clear-stream(_test-output-buffered-file+4) b8/copy-to-eax _test-output-buffered-file/imm32 05/add-to-eax 4/imm32 (clear-stream %eax) # 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