about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--055stream.subx3
-rwxr-xr-xapps/bracesbin0 -> 39594 bytes
-rw-r--r--apps/braces.subx337
-rwxr-xr-xtest_apps27
4 files changed, 357 insertions, 10 deletions
diff --git a/055stream.subx b/055stream.subx
index d4985a27..32b389bd 100644
--- a/055stream.subx
+++ b/055stream.subx
@@ -33,13 +33,12 @@ clear-stream:  # f : (address stream) -> <void>
     c7          0/subop/copy        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         0/imm32           # copy to *(eax+4)
     # eax = f->data
     81          0/subop/add         3/mod/direct    0/rm32/eax    .           .             .           .           .               0xc/imm32         # add to eax
-    # while (true)
 $clear-stream:loop:
     # if (eax >= ecx) break
     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # compare eax with ecx
     73/jump-if-greater-or-equal-unsigned  $clear-stream:end/disp8
     # *eax = 0
-    c6          0/subop/copy        0/mod/direct    0/rm32/eax    .           .             .           .           .               0/imm8            # copy byte to *eax
+    c6          0/subop/copy-byte   0/mod/direct    0/rm32/eax    .           .             .           .           .               0/imm8            # copy byte to *eax
     # ++eax
     40/increment-eax
     eb/jump  $clear-stream:loop/disp8
diff --git a/apps/braces b/apps/braces
new file mode 100755
index 00000000..c3f88cd8
--- /dev/null
+++ b/apps/braces
Binary files differdiff --git a/apps/braces.subx b/apps/braces.subx
new file mode 100644
index 00000000..e090692f
--- /dev/null
+++ b/apps/braces.subx
@@ -0,0 +1,337 @@
+# Structured control flow using break/loop rather than jump.
+#
+# To run (on Linux):
+#   $ ./ntranslate init.linux 0*.subx apps/subx-common.subx apps/calls.subx
+#   $ mv a.elf apps/calls
+#
+# Example 1:
+#   $ cat x.subx
+#   {
+#     7c/if-lesser break/disp8
+#     74/if-equal loop/disp8
+#   }
+#   $ cat x.subx |apps/calls
+#   _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/calls
+#   _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/calls
+#   _loop1:
+#     _loop2:
+#       74/if-equal _loop2/disp8
+#     _break2:
+#     7c/if-lesser _loop1/disp8
+#   _break1:
+
+== code
+
+Entry:  # run tests if necessary, a REPL if not
+    # . prolog
+    89/<- %ebp 4/r32/esp
+    # initialize heap
+    (new-segment Heap-size Heap)
+    # if (argc <= 1) goto run-main
+    81 7/subop/compare *ebp 1/imm32
+    7e/jump-if-lesser-or-equal $run-main/disp8
+    # if (argv[1] != "test")) goto run-main
+    (kernel-string-equal? *(ebp+8) "test")  # => eax
+    3d/compare-eax-and 0/imm32
+    74/jump-if-equal $run-main/disp8
+    #
+    (run-tests)
+    # syscall(exit, *Num-test-failures)
+    8b/-> *Num-test-failures 3/r32/ebx
+    eb/jump $main:end/disp8
+$run-main:
+    (convert Stdin Stdout)
+    # syscall(exit, 0)
+    bb/copy-to-ebx 0/imm32
+$main:end:
+    b8/copy-to-eax 1/imm32/exit
+    cd/syscall 0x80/imm8
+
+convert:  # in : (address buffered-file), out : (address buffered-file) -> <void>
+    # 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
+    #     if line->data[line->read] == '}'
+    #       var top = pop(label-stack)
+    #       print(out, "_break" top ":\n")
+    #     while true
+    #       var word-slice : (address slice) = next-word-or-expression(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)
+    #         word-slice->start += len("break")
+    #       else if slice-starts-with?(word-slice, "loop/")
+    #         var top = top(label-stack)
+    #         write(out, "_loop" top)
+    #         word-slice->start += len("loop")
+    #       write(out, word-slice " ")
+    #     write(out, "\n")
+    #   flush(out)
+    # . prolog
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+$convert:loop:
+    eb/jump $convert:loop/disp8
+$convert:end:
+    # . restore registers
+    # . epilog
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+# let's just put stack primitives here for now
+# we need to think about how to maintain layers of the library at different levels of syntax sugar
+
+# A stack looks like this:
+#   top: int
+#   data: (array byte)  # prefixed by length as usual
+
+clear-stack:  # s : (address stack)
+    # . prolog
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    # eax = s
+    8b/-> *(ebp+8) 0/r32/eax
+    # ecx = s->length
+    8b/-> *(eax+4) 1/r32/ecx
+    # ecx = &s->data[s->length]
+    8d/copy-address *(eax+ecx+8) 1/r32/ecx
+    # s->top = 0
+    c7/copy 0/subop/copy *eax 0/imm32
+    # eax = s->data
+    81 0/subop/add %eax 8/imm32
+$clear-stack:loop:
+    # if (eax >= ecx) break
+    39/compare %eax 1/r32/ecx
+    73/jump-if-greater-or-equal-unsigned $clear-stack:end/disp8
+    # *eax = 0
+    c6 0/subop/copy-byte *eax 0/imm8
+    # ++eax
+    40/increment-eax
+    eb/jump $clear-stack:loop/disp8
+$clear-stack:end:
+    # . restore registers
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilog
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-clear-stack:
+    # var ecx : (address stack) = stack of size 8 with random data in it
+    68/push 34/imm32
+    68/push 35/imm32
+    68/push 8/imm32/length
+    68/push 14/imm32/top
+    89/<- %ecx 4/r32/esp
+    # clear
+    (clear-stack %ecx)
+    # top should be 0
+    58/pop-to-eax
+    (check-ints-equal %eax 0 "F - test-clear-stack: top")
+    # length should remain 8
+    58/pop-to-eax
+    (check-ints-equal %eax 8 "F - test-clear-stack: length")
+    # first word is 0
+    58/pop-to-eax
+    (check-ints-equal %eax 0 "F - test-clear-stack: data[0..3]")
+    # second word is 0
+    58/pop-to-eax
+    (check-ints-equal %eax 0 "F - test-clear-stack: data[4..7]")
+    c3/return
+
+push:  # s : (address stack), n : int
+    # . prolog
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    56/push-esi
+    # esi = s
+    8b/-> *(ebp+8) 6/r32/esi
+    # ecx = s->top
+    8b/-> *esi 1/r32/ecx
+    # if (s->top >= s->length) abort
+    39/compare *(esi+4) 1/r32/ecx
+    7e/jump-if-lesser-or-equal $push:abort/disp8
+    # s->data[s->top] = n
+    8b/-> *(ebp+0xc) 0/r32/eax
+    89/<- *(esi+ecx+8) 0/r32/eax
+    # s->top += 4
+    81 0/subop/add *esi 4/imm32
+$push:end:
+    # . restore registers
+    5e/pop-to-esi
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilog
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$push:abort:
+    # print(stderr, "error: push: no space left")
+    # . write-buffered(Stderr, "error: push: no space left")
+    # . . push args
+    68/push "error: push: no space left"/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-push:
+    # var ecx : (address stack) = empty stack of size 8
+    68/push 0/imm32
+    68/push 0/imm32
+    68/push 8/imm32/length
+    68/push 0/imm32/top
+    89/<- %ecx 4/r32/esp
+    #
+    (push %ecx 42)
+    # top
+    58/pop-to-eax
+    (check-ints-equal %eax 4 "F - test-push: top")
+    # length
+    58/pop-to-eax
+    (check-ints-equal %eax 8 "F - test-push: length")
+    # first word is 42
+    58/pop-to-eax
+    (check-ints-equal %eax 42 "F - test-push: data[0..3]")
+    # second word is 0
+    58/pop-to-eax
+    (check-ints-equal %eax 0 "F - test-push: data[4..7]")
+    c3/return
+
+pop:  # 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 $pop:abort/disp8
+    # s->top -= 4
+    81 5/subop/subtract *esi 4/imm32
+    # eax = s->data[s->top]
+    8b/-> *esi 1/r32/ecx/top
+    8b/-> *(esi+ecx+8) 0/r32/eax
+$pop:end:
+    # . restore registers
+    5e/pop-to-esi
+    59/pop-to-ecx
+    # . epilog
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$pop:abort:
+    # print(stderr, "error: pop: nothing left in stack")
+    # . write-buffered(Stderr, "error: pop: nothing left in stack")
+    # . . push args
+    68/push "error: pop: 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-pop:
+    # 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
+    #
+    (pop %ecx)  # => eax
+    # result
+    (check-ints-equal %eax 42 "F - test-pop: result")
+    # top
+    58/pop-to-eax
+    (check-ints-equal %eax 0 "F - test-pop: top")
+    # length
+    58/pop-to-eax
+    (check-ints-equal %eax 8 "F - test-pop: length")
+    # clean up
+    58/pop-to-eax
+    58/pop-to-eax
+    c3/return
+
+== data
diff --git a/test_apps b/test_apps
index a10538d1..29bd6075 100755
--- a/test_apps
+++ b/test_apps
@@ -309,14 +309,9 @@ test `uname` = 'Linux'  &&  {
   echo
 }
 
-# Only native runs beyond this point. We start using syntax that the emulator
-# doesn't support.
-test $EMULATED  &&  echo "skipping remaining runs in emulated mode"
-test $NATIVE  ||  exit 0
-
 echo calls
-cat 0*.subx apps/subx-common.subx apps/calls.subx  |  apps/sigils  > a.sigils
-./subx translate init.$OS a.sigils -o apps/calls
+cat init.$OS 0*.subx apps/subx-common.subx apps/calls.subx  |  apps/sigils  > a.sigils
+./subx translate a.sigils -o apps/calls
 [ "$1" != record ]  &&  git diff --exit-code apps/calls
 ./subx run apps/calls test
 echo
@@ -325,6 +320,22 @@ test `uname` = 'Linux'  &&  {
   echo
 }
 
+echo braces
+cat init.$OS 0*.subx apps/subx-common.subx apps/braces.subx  |  apps/calls  |  apps/sigils  > a.sigils
+./subx translate a.sigils -o apps/braces
+[ "$1" != record ]  &&  git diff --exit-code apps/braces
+./subx run apps/braces test
+echo
+test `uname` = 'Linux'  &&  {
+  apps/braces test
+  echo
+}
+
+# Only native runs beyond this point. We start using syntax that the emulator
+# doesn't support.
+test $EMULATED  &&  echo "skipping remaining runs in emulated mode"
+test $NATIVE  ||  exit 0
+
 echo "== translating using SubX"
 
 # example programs
@@ -347,7 +358,7 @@ done
 
 # Phases of the self-hosted SubX translator.
 
-for app in hex survey pack assort dquotes tests sigils calls
+for app in hex survey pack assort dquotes tests sigils calls braces
 do
   echo $app
   ./ntranslate init.$OS 0*.subx apps/subx-common.subx apps/$app.subx