diff options
author | Kartik Agaram <vc@akkartik.com> | 2020-07-10 23:41:34 -0700 |
---|---|---|
committer | Kartik Agaram <vc@akkartik.com> | 2020-07-10 23:41:34 -0700 |
commit | c5a3f65502d6931a5fb888a5c5327c0659578a57 (patch) | |
tree | b0d58841b786eea6ceb420de635574d9b8853708 /apps | |
parent | 229d63aad2286135440fccc452e95e1cbb502318 (diff) | |
download | mu-c5a3f65502d6931a5fb888a5c5327c0659578a57.tar.gz |
6630 - define type signatures for SubX functions
This was easier than I'd feared.
Diffstat (limited to 'apps')
-rwxr-xr-x | apps/mu | bin | 341189 -> 344970 bytes | |||
-rw-r--r-- | apps/mu.subx | 350 |
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 |