about summary refs log tree commit diff stats
path: root/apps
diff options
context:
space:
mode:
authorKartik Agaram <vc@akkartik.com>2020-07-10 23:41:34 -0700
committerKartik Agaram <vc@akkartik.com>2020-07-10 23:41:34 -0700
commitc5a3f65502d6931a5fb888a5c5327c0659578a57 (patch)
treeb0d58841b786eea6ceb420de635574d9b8853708 /apps
parent229d63aad2286135440fccc452e95e1cbb502318 (diff)
downloadmu-c5a3f65502d6931a5fb888a5c5327c0659578a57.tar.gz
6630 - define type signatures for SubX functions
This was easier than I'd feared.
Diffstat (limited to 'apps')
-rwxr-xr-xapps/mubin341189 -> 344970 bytes
-rw-r--r--apps/mu.subx350
2 files changed, 340 insertions, 10 deletions
diff --git a/apps/mu b/apps/mu
index 4e8d6741..e1e82141 100755
--- a/apps/mu
+++ b/apps/mu
Binary files differdiff --git a/apps/mu.subx b/apps/mu.subx
index edd8a02a..d8c38e0a 100644
--- a/apps/mu.subx
+++ b/apps/mu.subx
@@ -243,6 +243,10 @@ _Program-types:  # (handle typeinfo)
   0/imm32
 _Program-types->payload:
   0/imm32
+_Program-signatures:  # (handle function)
+  0/imm32
+_Program-signatures->payload:
+  0/imm32
 
 # Some constants for simulating the data structures described above.
 # Many constants here come with a type in a comment.
@@ -850,7 +854,6 @@ test-convert-function-with-literal-arg-2:
     5d/pop-to-ebp
     c3/return
 
-# HERE
 test-convert-function-call-with-literal-arg:
     # . prologue
     55/push-ebp
@@ -910,6 +913,48 @@ test-convert-function-call-with-literal-arg:
     5d/pop-to-ebp
     c3/return
 
+test-convert-function-call-with-signature:
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-input-stream)
+    (clear-stream $_test-input-buffered-file->buffer)
+    (clear-stream _test-output-stream)
+    (clear-stream $_test-output-buffered-file->buffer)
+    #
+    (write _test-input-stream "fn main -> result/ebx: int {\n")
+    (write _test-input-stream "  result <- do-add 3 4\n")
+    (write _test-input-stream "}\n")
+    (write _test-input-stream "sig do-add a: int, b: int -> result/ebx: int\n")
+    # convert
+    (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 0)
+    (flush _test-output-buffered-file)
+#?     # dump _test-output-stream {{{
+#?     (write 2 "^")
+#?     (write-stream 2 _test-output-stream)
+#?     (write 2 "$\n")
+#?     (rewind-stream _test-output-stream)
+#?     # }}}
+    # check output
+    (check-next-stream-line-equal _test-output-stream "main:"                   "F - test-convert-function-call-with-signature/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-call-with-signature/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-call-with-signature/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-call-with-signature/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-call-with-signature/4")
+    (check-next-stream-line-equal _test-output-stream "$main:0x00000001:loop:"  "F - test-convert-function-call-with-signature/5")
+    (check-next-stream-line-equal _test-output-stream "    (do-add 3 4)"        "F - test-convert-function-call-with-signature/6")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-call-with-signature/7")
+    (check-next-stream-line-equal _test-output-stream "$main:0x00000001:break:" "F - test-convert-function-call-with-signature/8")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-call-with-signature/9")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-call-with-signature/10")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-call-with-signature/11")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-call-with-signature/12")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
 test-convert-function-with-local-var-in-mem:
     # . prologue
     55/push-ebp
@@ -5921,6 +5966,7 @@ Curr-block-depth:  # (addr int)
 parse-mu:  # in: (addr buffered-file), err: (addr buffered-file), ed: (addr exit-descriptor)
     # pseudocode
     #   var curr-function: (addr handle function) = Program->functions
+    #   var curr-signature: (addr handle function) = Program->signatures
     #   var curr-type: (addr handle typeinfo) = Program->types
     #   var line: (stream byte 512)
     #   var word-slice: slice
@@ -5941,6 +5987,11 @@ parse-mu:  # in: (addr buffered-file), err: (addr buffered-file), ed: (addr exit
     #       assert(vars->top == 0)
     #       *curr-function = new-function
     #       curr-function = &new-function->next
+    #     else if slice-equal?(word-slice, "sig")
+    #       var new-function: (handle function) = allocate(function)
+    #       populate-mu-function-signature(line, new-function)
+    #       *curr-signature = new-function
+    #       curr-signature = &new-function->next
     #     else if slice-equal?(word-slice, "type")
     #       word-slice = next-mu-token(line)
     #       type-id = pos-or-insert-slice(Type-id, word-slice)
@@ -5953,6 +6004,8 @@ parse-mu:  # in: (addr buffered-file), err: (addr buffered-file), ed: (addr exit
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
+    # var curr-signature: (addr handle function) at *(ebp-4)
+    68/push _Program-signatures/imm32
     # . save registers
     50/push-eax
     51/push-ecx
@@ -6037,6 +6090,46 @@ $parse-mu:fn:
         #
         e9/jump $parse-mu:line-loop/disp32
       }
+      # if (slice-equal?(word-slice, "sig")) parse a function signature
+      # Function signatures are for providing types to SubX functions.
+      {
+$parse-mu:sig:
+        (slice-equal? %edx "sig")  # => eax
+        3d/compare-eax-and 0/imm32/false
+        0f 84/jump-if-= break/disp32
+        # edi = curr-function
+        57/push-edi
+$bb:
+        8b/-> *(ebp-4) 7/r32/edi
+        # var new-function/esi: (handle function)
+        68/push 0/imm32
+        68/push 0/imm32
+        89/<- %esi 4/r32/esp
+        # populate-mu-function(line, in, vars, new-function)
+        (allocate Heap *Function-size %esi)
+        # var new-function-addr/eax: (addr function)
+        (lookup *esi *(esi+4))  # => eax
+        #
+        (populate-mu-function-signature %ecx %eax *(ebp+0xc) *(ebp+0x10))
+        # *curr-signature = new-function
+        8b/-> *esi 0/r32/eax
+        89/<- *edi 0/r32/eax
+        8b/-> *(esi+4) 0/r32/eax
+        89/<- *(edi+4) 0/r32/eax
+        # curr-signature = &new-function->next
+        # . var tmp/eax: (addr function) = lookup(new-function)
+        (lookup *esi *(esi+4))  # => eax
+        # . curr-function = &tmp->next
+        8d/copy-address *(eax+0x20) 7/r32/edi  # Function-next
+        # reclaim new-function
+        81 0/subop/add %esp 8/imm32
+        # save curr-function
+        89/<- *(ebp-4) 7/r32/edi
+        # restore edi
+        5f/pop-to-edi
+        #
+        e9/jump $parse-mu:line-loop/disp32
+      }
       # if (slice-equal?(word-slice, "type")) parse a type (struct/record) definition
       {
 $parse-mu:type:
@@ -6080,6 +6173,8 @@ $parse-mu:end:
     5a/pop-to-edx
     59/pop-to-ecx
     58/pop-to-eax
+    # . reclaim local
+    81 0/subop/add %esp 4/imm32
     # . epilogue
     89/<- %esp 5/r32/ebp
     5d/pop-to-ebp
@@ -6167,7 +6262,6 @@ populate-mu-function-header:  # first-line: (addr stream byte), out: (addr funct
     # read function name
     (next-mu-token *(ebp+8) %ecx)
     # error checking
-    # TODO: error if word-slice starts with 'break' or 'loop'
     # if (word-slice == '{') abort
     (slice-equal? %ecx "{")   # => eax
     3d/compare-eax-and 0/imm32/false
@@ -6274,17 +6368,28 @@ $populate-mu-function-header:error1:
     # never gets here
 
 $populate-mu-function-header:error2:
-    # error("function inout '" var "' cannot be in a register")
-    (write-buffered *(ebp+0x14) "function inout '")
-    (write-buffered *(ebp+0x14) *ebx)  # Var-name
+    # error("fn " fn ": function inout '" var "' cannot be in a register")
+    (write-buffered *(ebp+0x14) "fn ")
+    50/push-eax
+    (lookup *edi *(edi+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x14) %eax)
+    58/pop-to-eax
+    (write-buffered *(ebp+0x14) ": function inout '")
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
     (write-buffered *(ebp+0x14) "' cannot be in a register")
     (flush *(ebp+0x14))
     (stop *(ebp+0x18) 1)
     # never gets here
 
 $populate-mu-function-header:error3:
-    # error("function output '" var "' must be in a register")
-    (write-buffered *(ebp+0x14) "function output '")
+    # error("fn " fn ": function output '" var "' must be in a register")
+    (write-buffered *(ebp+0x14) "fn ")
+    50/push-eax
+    (lookup *edi *(edi+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x14) %eax)
+    58/pop-to-eax
+    (write-buffered *(ebp+0x14) ": function output '")
     (lookup *ebx *(ebx+4))  # => eax
     (lookup *eax *(eax+4))  # Var-name Var-name => eax
     (write-buffered *(ebp+0x14) %eax)
@@ -6296,6 +6401,203 @@ $populate-mu-function-header:error3:
     (stop *(ebp+0x18) 1)
     # never gets here
 
+# scenarios considered:
+# ✓ fn foo
+# ✗ fn foo {
+# ✓ fn foo x
+# ✓ fn foo x: int
+# ✓ fn foo x: int -> y/eax: int
+# TODO:
+#   disallow outputs of type `(... addr ...)`
+#   disallow inputs of type `(... addr ... addr ...)`
+populate-mu-function-signature:  # first-line: (addr stream byte), out: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # pseudocode:
+    #   var word-slice: slice
+    #   next-mu-token(first-line, word-slice)
+    #   assert(word-slice not in '{' '}' '->')
+    #   out->name = slice-to-string(word-slice)
+    #   ## inouts
+    #   while true
+    #     word-slice = next-mu-token(first-line)
+    #     if slice-empty?(word-slice) break
+    #     if (word-slice == '->') break
+    #     assert(word-slice not in '{' '}')
+    #     var v: (handle var) = parse-var-with-type(word-slice, first-line)
+    #     assert(v->register == null)
+    #     # v->block-depth is implicitly 0
+    #     out->inouts = append(v, out->inouts)
+    #   ## outputs
+    #   while true
+    #     word-slice = next-mu-token(first-line)
+    #     if slice-empty?(word-slice) break
+    #     assert(word-slice not in '{' '}' '->')
+    #     var v: (handle var) = parse-var-with-type(word-slice, first-line)
+    #     assert(v->register != null)
+    #     out->outputs = append(v, out->outputs)
+    #
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    57/push-edi
+    # edi = out
+    8b/-> *(ebp+0xc) 7/r32/edi
+    # var word-slice/ecx: slice
+    68/push 0/imm32/end
+    68/push 0/imm32/start
+    89/<- %ecx 4/r32/esp
+    # var v/ebx: (handle var)
+    68/push 0/imm32
+    68/push 0/imm32
+    89/<- %ebx 4/r32/esp
+    # read function name
+    (next-mu-token *(ebp+8) %ecx)
+    # error checking
+    # if (word-slice == '{') abort
+    (slice-equal? %ecx "{")   # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $populate-mu-function-signature:error1/disp32
+    # if (word-slice == '->') abort
+    (slice-equal? %ecx "->")   # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $populate-mu-function-signature:error1/disp32
+    # if (word-slice == '}') abort
+    (slice-equal? %ecx "}")   # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $populate-mu-function-signature:error1/disp32
+    # save function name
+    (slice-to-string Heap %ecx %edi)  # Function-name
+    # save function inouts
+    {
+$populate-mu-function-signature:check-for-inout:
+      (next-mu-token *(ebp+8) %ecx)
+      (slice-empty? %ecx)  # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 85/jump-if-!= break/disp32
+      # if (word-slice == '->') break
+      (slice-equal? %ecx "->")   # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 85/jump-if-!= break/disp32
+      # if (word-slice == '{') abort
+      (slice-equal? %ecx "{")   # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 85/jump-if-!= $populate-mu-function-signature:error1/disp32
+      # if (word-slice == '}') abort
+      (slice-equal? %ecx "}")   # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 85/jump-if-!= $populate-mu-function-signature:error1/disp32
+      # v = parse-var-with-type(word-slice, first-line)
+      (parse-var-with-type %ecx *(ebp+8) %ebx *(ebp+0x10) *(ebp+0x14))
+      # assert(v->register == null)
+      # . eax: (addr var) = lookup(v)
+      (lookup *ebx *(ebx+4))  # => eax
+      81 7/subop/compare *(eax+0x18) 0/imm32  # Var-register
+      0f 85/jump-if-!= $populate-mu-function-signature:error2/disp32
+      # v->block-depth is implicitly 0
+      #
+      # out->inouts = append(v, out->inouts)
+      8d/copy-address *(edi+8) 0/r32/eax  # Function-inouts
+      (append-list Heap  *ebx *(ebx+4)  *(edi+8) *(edi+0xc)  %eax)  # Function-inouts, Function-inouts
+      #
+      e9/jump loop/disp32
+    }
+    # save function outputs
+    {
+$populate-mu-function-signature:check-for-out:
+      (next-mu-token *(ebp+8) %ecx)
+      (slice-empty? %ecx)  # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 85/jump-if-!= break/disp32
+      # if (word-slice == '{') abort
+      (slice-equal? %ecx "{")   # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 85/jump-if-!= $populate-mu-function-signature:error1/disp32
+      # if (word-slice == '->') abort
+      (slice-equal? %ecx "->")   # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 85/jump-if-!= $populate-mu-function-signature:error1/disp32
+      # if (word-slice == '}') abort
+      (slice-equal? %ecx "}")   # => eax
+      3d/compare-eax-and 0/imm32/false
+      0f 85/jump-if-!= $populate-mu-function-signature:error1/disp32
+      # v = parse-var-with-type(word-slice, first-line)
+      (parse-var-with-type %ecx *(ebp+8) %ebx *(ebp+0x10) *(ebp+0x14))
+      # assert(var->register != null)
+      # . eax: (addr var) = lookup(v)
+      (lookup *ebx *(ebx+4))  # => eax
+      81 7/subop/compare *(eax+0x18) 0/imm32  # Var-register
+      0f 84/jump-if-= $populate-mu-function-signature:error3/disp32
+      # out->outputs = append(v, out->outputs)
+      8d/copy-address *(edi+0x10) 0/r32/eax  # Function-outputs
+      (append-list Heap  *ebx *(ebx+4)  *(edi+0x10) *(edi+0x14)  %eax)  # Function-outputs, Function-outputs
+      #
+      e9/jump loop/disp32
+    }
+$populate-mu-function-signature:done:
+    (check-no-tokens-left *(ebp+8))
+$populate-mu-function-signature:end:
+    # . reclaim locals
+    81 0/subop/add %esp 0x10/imm32
+    # . restore registers
+    5f/pop-to-edi
+    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
+
+$populate-mu-function-signature:error1:
+    # error("function signature not in form 'fn <name> {'")
+    (write-buffered *(ebp+0x10) "function signature not in form 'fn <name> [inouts] [-> outputs] {' -- '")
+    (flush *(ebp+0x10))
+    (rewind-stream *(ebp+8))
+    (write-stream-data *(ebp+0x10) *(ebp+8))
+    (write-buffered *(ebp+0x10) "'\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$populate-mu-function-signature:error2:
+    # error("fn " fn ": function inout '" var "' cannot be in a register")
+    (write-buffered *(ebp+0x10) "fn ")
+    50/push-eax
+    (lookup *edi *(edi+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    58/pop-to-eax
+    (write-buffered *(ebp+0x10) ": function inout '")
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' cannot be in a register")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
+$populate-mu-function-signature:error3:
+    # error("fn " fn ": function output '" var "' must be in a register")
+    (write-buffered *(ebp+0x10) "fn ")
+    50/push-eax
+    (lookup *edi *(edi+4))  # Function-name Function-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    58/pop-to-eax
+    (write-buffered *(ebp+0x10) ": function output '")
+    (lookup *ebx *(ebx+4))  # => eax
+    (lookup *eax *(eax+4))  # Var-name Var-name => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "' must be in a register, in instruction '")
+    (rewind-stream *(ebp+8))
+    (write-stream-data *(ebp+0x10) *(ebp+8))
+    (write-buffered *(ebp+0x10) "'\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
 test-function-header-with-arg:
     # . prologue
     55/push-ebp
@@ -10478,13 +10780,21 @@ check-mu-types:  # err: (addr buffered-file), ed: (addr exit-descriptor)
     89/<- %ebp 4/r32/esp
     # . save registers
     50/push-eax
-    # var curr/eax: (addr function) = *Program->functions
+    # var curr/eax: (addr function) = lookup(Program->functions)
     (lookup *_Program-functions *_Program-functions->payload)  # => eax
     {
 $check-mu-types:loop:
       # if (curr == null) break
       3d/compare-eax-and 0/imm32
       0f 84/jump-if-= break/disp32
+#?       # dump curr->name {{{
+#?       50/push-eax
+#?       (lookup *eax *(eax+4))  # Function-name Function-name => eax
+#?       (write-buffered Stderr %eax)
+#?       (write-buffered Stderr Newline)
+#?       (flush Stderr)
+#?       58/pop-to-eax
+#?       # }}}
       (check-mu-function %eax *(ebp+8) *(ebp+0xc))
       # curr = lookup(curr->next)
       (lookup *(eax+0x20) *(eax+0x24))  # Function-next Function-next => eax
@@ -10610,7 +10920,7 @@ check-mu-stmt:  # stmt: (addr stmt), fn: (addr function), err: (addr buffered-fi
     {
       74/jump-if-= break/disp8
       (check-mu-primitive *(ebp+8) *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))
-      eb/jump $check-mu-stmt:end/disp8
+      e9/jump $check-mu-stmt:end/disp32
     }
     # - otherwise find a function to check against
     # var f/eax: (addr function) = lookup(*Program->functions)
@@ -10622,7 +10932,17 @@ check-mu-stmt:  # stmt: (addr stmt), fn: (addr function), err: (addr buffered-fi
       (check-mu-call *(ebp+8) %eax *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))
       eb/jump $check-mu-stmt:end/disp8
     }
-    # TODO: error on unknown function. We need to first type-check calls to SubX functions.
+    # var f/eax: (addr function) = lookup(*Program->signatures)
+    (lookup *_Program-signatures *_Program-signatures->payload)  # => eax
+    (find-matching-function %eax *(ebp+8))  # => eax
+    3d/compare-eax-and 0/imm32
+    {
+      74/jump-if-= break/disp8
+      (check-mu-call *(ebp+8) %eax *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))
+      eb/jump $check-mu-stmt:end/disp8
+    }
+    # - otherwise abort
+    e9/jump $check-mu-stmt:unknown-call/disp32
 $check-mu-stmt:end:
     # . restore registers
     58/pop-to-eax
@@ -10631,6 +10951,16 @@ $check-mu-stmt:end:
     5d/pop-to-ebp
     c3/return
 
+$check-mu-stmt:unknown-call:
+    (write-buffered *(ebp+0x10) "unknown function '")
+    8b/-> *(ebp+8) 0/r32/eax
+    (lookup *(eax+4) *(eax+8))  # Stmt1-operation Stmt1-operation => eax
+    (write-buffered *(ebp+0x10) %eax)
+    (write-buffered *(ebp+0x10) "'\n")
+    (flush *(ebp+0x10))
+    (stop *(ebp+0x14) 1)
+    # never gets here
+
 has-primitive-name?:  # stmt: (addr stmt) -> result/eax: boolean
     # . prologue
     55/push-ebp