diff options
-rwxr-xr-x | apps/braces | bin | 41115 -> 43032 bytes | |||
-rw-r--r-- | apps/braces.subx | 345 |
2 files changed, 331 insertions, 14 deletions
diff --git a/apps/braces b/apps/braces index 775571e8..06ffb9fe 100755 --- a/apps/braces +++ b/apps/braces Binary files differdiff --git a/apps/braces.subx b/apps/braces.subx index 3fad21d7..0b70c7a0 100644 --- a/apps/braces.subx +++ b/apps/braces.subx @@ -1,8 +1,8 @@ # Structured control flow using break/loop rather than jump. # # To run (on Linux): -# $ ./ntranslate init.linux 0*.subx apps/subx-params.subx apps/calls.subx -# $ mv a.elf apps/calls +# $ ./ntranslate init.linux 0*.subx apps/subx-params.subx apps/braces.subx +# $ mv a.elf apps/braces # # Example 1: # $ cat x.subx @@ -10,7 +10,7 @@ # 7c/if-lesser break/disp8 # 74/if-equal loop/disp8 # } -# $ cat x.subx |apps/calls +# $ cat x.subx |apps/braces # _loop1: # 7c/if-lesser _break1/disp8 # 74/if-equal _loop1/disp8 @@ -24,7 +24,7 @@ # { # 74/if-equal loop/disp8 # } -# $ cat x.subx |apps/calls +# $ cat x.subx |apps/braces # _loop1: # 7c/if-lesser _break1/disp8 # _break1: @@ -40,7 +40,7 @@ # } # 7c/if-lesser loop/disp8 # } -# $ cat x.subx |apps/calls +# $ cat x.subx |apps/braces # _loop1: # _loop2: # 74/if-equal _loop2/disp8 @@ -89,34 +89,289 @@ subx-braces: # in : (address buffered-file), out : (address buffered-file) -> < # 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-expression(line) + # 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) - # write(out, "_break" top) + # print(out, "_break" top) # word-slice->start += len("break") # else if slice-starts-with?(word-slice, "loop/") # var top = top(label-stack) - # write(out, "_loop" top) + # print(out, "_loop" top) # word-slice->start += len("loop") - # write(out, word-slice " ") - # write(out, "\n") + # print(out, word-slice " ") + # print(out, "\n") # flush(out) # . prolog 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers -$subx-braces:loop: - eb/jump $subx-braces:loop/disp8 + 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 + # . epilog + 89/<- %esp 5/r32/ebp + 5d/pop-to-ebp + c3/return + +test-subx-braces-passes-most-words-through: + # . prolog + 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") + # . epilog + 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: + # + # . prolog + 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") + # . epilog + 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: + # + # . prolog + 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") # . epilog 89/<- %esp 5/r32/ebp 5d/pop-to-ebp @@ -330,6 +585,68 @@ test-pop: 58/pop-to-eax (check-ints-equal %eax 8 "F - test-pop: length") # clean up - 58/pop-to-eax - 58/pop-to-eax + 81 0/subop/add %esp 8/imm32 + c3/return + +top: # s : (address stack) -> n/eax : int + # . prolog + 55/push-ebp + 89/<- %ebp 4/r32/esp + # . save registers + 51/push-ecx + 56/push-esi + # esi = s + 8b/-> *(ebp+8) 6/r32/esi + # if (s->top <= 0) abort + 81 7/subop/compare *esi 0/imm32 + 7e/jump-if-lesser-or-equal $top:abort/disp8 + # eax = s->data[s->top - 4] + 8b/-> *esi 1/r32/ecx/top + 81 5/subop/subtract %ecx 4/imm32 + 8b/-> *(esi+ecx+8) 0/r32/eax +$top:end: + # . restore registers + 5e/pop-to-esi + 59/pop-to-ecx + # . epilog + 89/<- %esp 5/r32/ebp + 5d/pop-to-ebp + c3/return + +$top:abort: + # print(stderr, "error: top: nothing left in stack") + # . write-buffered(Stderr, "error: top: nothing left in stack") + # . . push args + 68/push "error: top: nothing left in stack"/imm32 + 68/push Stderr/imm32 + # . . call + e8/call write-buffered/disp32 + # . . discard args + 81 0/subop/add %esp 8/imm32 + # . flush(Stderr) + # . . push args + 68/push Stderr/imm32 + # . . call + e8/call flush/disp32 + # . . discard args + 81 0/subop/add %esp 4/imm32 + # . syscall(exit, 1) + bb/copy-to-ebx 1/imm32 + b8/copy-to-eax 1/imm32/exit + cd/syscall 0x80/imm8 + # never gets here + +test-top: + # var ecx : (address stack) = stack of size 8 containing just 42 + 68/push 0/imm32 + 68/push 42/imm32 + 68/push 8/imm32/length + 68/push 4/imm32/top + 89/<- %ecx 4/r32/esp + # + (top %ecx) # => eax + # result + (check-ints-equal %eax 42 "F - test-top: result") + # clean up + 81 0/subop/add %esp 0x10/imm32 c3/return |