diff options
-rw-r--r-- | 400.mu | 159 | ||||
-rw-r--r-- | 400.txt | 1 | ||||
-rwxr-xr-x | apps/mu | bin | 341189 -> 344970 bytes | |||
-rw-r--r-- | apps/mu.subx | 350 | ||||
-rw-r--r-- | mu.vim | 2 |
5 files changed, 500 insertions, 12 deletions
diff --git a/400.mu b/400.mu new file mode 100644 index 00000000..04e93b67 --- /dev/null +++ b/400.mu @@ -0,0 +1,159 @@ +# The 4xx series is for primitives implemented in Mu. + +# Signatures for major SubX functions defined so far. + +# autogenerated +sig run-tests + +# init.linux +# TODO: make this OS-specific +# TODO: include result type at least, even if register args are too much +sig syscall_exit # status/ebx: int +sig syscall_read # fd/ebx: int, buf/ecx: addr, size/edx: int -> nbytes-or-error/eax: int +sig syscall_write # fd/ebx: int, buf/ecx: addr, size/edx: int -> nbytes-or-error/eax: int +sig syscall_open # filename/ebx: (addr kernel-string), flags/ecx: int, dummy=0x180/edx -> fd-or-error/eax: int +sig syscall_close # fd/ebx: int -> status/eax +sig syscall_creat # filename/ebx: (addr kernel-string) -> fd-or-error/eax: int +sig syscall_unlink # filename/ebx: (addr kernel-string) -> status/eax: int +sig syscall_rename # source/ebx: (addr kernel-string), dest/ecx: (addr kernel-string) -> status/eax: int +sig syscall_mmap # arg/ebx: (addr mmap_arg_struct) -> status/eax: int +sig syscall_ioctl # fd/ebx: int, cmd/ecx: int, arg/edx: (addr _) +sig syscall_nanosleep # req/ebx: (addr timespec) +sig syscall_clock_gettime # clock/ebx: int, out/ecx: (addr timespec) + +# Generated using: +# grep -h '^[a-z]' [0-9]*.subx |grep -v '^test-' +# Functions we don't want to make accessible from Mu are commented out. +# Many functions here may not be usable yet because of missing features +# (global variable support, type definitions for stuff like `stream`) +sig check-ints-equal a: int, b: int, msg: (addr array byte) +sig kernel-string-equal? s: (addr kernel-string), benchmark: (addr array byte) -> result/eax: boolean +sig new-segment len: int, ad: (addr allocation-descriptor) +sig string-equal? s: (addr array byte), benchmark: (addr array byte) -> result/eax: boolean +sig string-starts-with? s: (addr array byte), benchmark: (addr array byte) -> result/eax: boolean +sig check-strings-equal s: (addr array byte), expected: (addr array byte), msg: (addr array byte) +sig clear-stream f: (addr stream byte) +sig rewind-stream f: (addr stream byte) +sig initialize-trace-stream n: int +sig trace line: (addr array byte) +sig check-trace-contains line: (addr string), msg: (addr string) +sig check-trace-scans-to line: (addr string), msg: (addr string) +sig trace-scan line: (addr array byte) -> result/eax: boolean +sig next-line-matches? t: (addr stream byte), line: (addr array byte) -> result/eax: boolean +sig skip-next-line t: (addr stream byte) +sig clear-trace-stream +#sig write f: fd or (addr stream byte), s: (addr array byte) +sig stream-data-equal? f: (addr stream byte), s: (addr array byte) -> result/eax: boolean +sig check-stream-equal f: (addr stream byte), s: (addr array byte), msg: (addr array byte) +sig next-stream-line-equal? f: (addr stream byte), s: (addr array byte) -> result/eax: boolean +sig check-next-stream-line-equal +sig tailor-exit-descriptor ed: (addr exit-descriptor), nbytes: int +sig stop ed: (addr exit-descriptor), value: int +#sig read f: fd or (addr stream byte), s: (addr stream byte) -> num-bytes-read/eax: int +sig read-byte-buffered f: (addr buffered-file) -> byte-or-Eof/eax: int +#sig write-stream f: fd or (addr stream byte), s: (addr stream byte) +#sig error ed: (addr exit-descriptor), out: fd or (addr stream byte), msg: (addr array byte) +sig write-byte-buffered f: (addr buffered-file), n: int +sig flush f: (addr buffered-file) +sig append-byte f: (addr stream byte), n: int +sig write-buffered f: (addr buffered-file), msg: (addr array byte) +#sig to-hex-char in/eax: int -> out/eax: int +sig append-byte-hex f: (addr stream byte), n: int +sig write-byte-hex-buffered f: (addr buffered-file), n: int +sig write-int32-hex f: (addr stream byte), n: int +sig write-int32-hex-buffered f: (addr buffered-file), n: int +sig is-hex-int? in: (addr slice) -> result/eax: boolean +sig parse-hex-int in: (addr array byte) -> result/eax: int +sig parse-hex-int-from-slice in: (addr slice) -> result/eax: int +#sig parse-hex-int-helper start: (addr byte), end: (addr byte) -> result/eax: int +sig is-hex-digit? c: byte -> result/eax: boolean +#sig from-hex-char in/eax: byte -> out/eax: nibble +sig error-byte ed: (addr exit-descriptor), out: (addr buffered-file), msg: (addr array byte), n: byte +#sig allocate ad: (addr allocation-descriptor), n: int, out: (addr handle) +#sig allocate-raw ad: (addr allocation-descriptor), n: int, out: (addr handle) +sig lookup h: (handle T) -> result/eax: (addr T) +sig handle-equal? a: handle, b: handle -> result/eax: boolean +sig copy-handle src: handle, dest: (addr handle) +sig allocate-region ad: (addr allocation-descriptor), n: int, out: (addr handle allocation-descriptor) +sig allocate-array ad: (addr allocation-descriptor), n: int, out: (addr handle) +sig copy-array ad: (addr allocation-descriptor), src: (addr array), out: (addr handle) +sig zero-out start: (addr byte), len: int +sig new-stream ad: (addr allocation-descriptor), length: int, elemsize: int, out: (addr handle stream _) +sig read-line-buffered f: (addr buffered-file), s: (addr stream byte) +sig read-line f: (addr stream byte), s: (addr stream byte) +sig slice-empty? s: (addr slice) -> result/eax: boolean +sig slice-equal? s: (addr slice), p: (addr array byte) -> result/eax: boolean +sig slice-starts-with? s: (addr slice), head: (addr array byte) -> result/eax: boolean +sig write-slice out: (addr stream byte), s: (addr slice) +sig write-slice-buffered out: (addr buffered-file), s: (addr slice) +sig slice-to-string ad: (addr allocation-descriptor), in: (addr slice), out: (addr handle array byte) +sig next-token in: (addr stream byte), delimiter: byte, out: (addr slice) +sig next-token-from-slice start: (addr byte), end: (addr byte), delimiter: byte, out: (addr slice) +sig skip-chars-matching in: (addr stream byte), delimiter: byte +sig skip-chars-matching-whitespace in: (addr stream byte) +sig skip-chars-not-matching in: (addr stream byte), delimiter: byte +sig skip-chars-not-matching-whitespace in: (addr stream byte) +sig skip-chars-matching-in-slice curr: (addr byte), end: (addr byte), delimiter: byte -> curr/eax: (addr byte) +sig skip-chars-matching-whitespace-in-slice curr: (addr byte), end: (addr byte) -> curr/eax: (addr byte) +sig skip-chars-not-matching-in-slice curr: (addr byte), end: (addr byte), delimiter: byte -> curr/eax: (addr byte) +sig skip-chars-not-matching-whitespace-in-slice curr: (addr byte), end: (addr byte) -> curr/eax: (addr byte) +sig skip-string line: (addr stream byte) +sig skip-string-in-slice curr: (addr byte), end: (addr byte) -> new_curr/eax: (addr byte) +sig skip-until-close-paren line: (addr stream byte) +sig skip-until-close-paren-in-slice curr: (addr byte), end: (addr byte) -> new_curr/eax: (addr byte) +sig write-stream-data f: (addr buffered-file), s: (addr stream byte) +sig write-int32-decimal out: (addr stream byte), n: int32 +sig is-decimal-digit? c: byte -> result/eax: boolean +sig to-decimal-digit in: byte -> out/eax: int +sig next-word line: (addr stream byte), out: (addr slice) +sig has-metadata? word: (addr slice), s: (addr string) -> result/eax: boolean +sig is-valid-name? in: (addr slice) -> result/eax: boolean +sig is-label? word: (addr slice) -> result/eax: boolean +sig emit-hex out: (addr buffered-file), n: int, width: int +sig emit out: (addr buffered-file), word: (addr slice), width: int +#sig get table: (addr stream {(handle array byte), T}), key: (addr array byte), row-size: int, abort-message-prefix: (addr array byte) -> result/eax: (addr T) +#sig get-slice table: (addr stream {(handle array byte), T}), key: (addr slice), row-size: int, abort-message-prefix: (addr array byte) -> result/eax: (addr T) +#sig get-or-insert table: (addr stream {(handle array byte), T}), key: (addr array byte), row-size: int, ad: (addr allocation-descriptor) -> result/eax: (addr T) +#sig get-or-insert-handle table: (addr stream {(handle array byte), T}), key: (handle array byte), row-size: int -> result/eax: (addr T) +#sig get-or-insert-slice table: (addr stream {(handle array byte), T}), key: (addr slice), row-size: int, ad: (addr allocation-descriptor) -> result/eax: (addr T) +#sig get-or-stop table: (addr stream {(handle array byte), T}), key: (addr array byte), row-size: int, +#sig get-slice-or-stop table: (addr stream {(handle array byte), _}), key: (addr slice), row-size: int, +#sig maybe-get table: (addr stream {(handle array byte), T}), key: (addr array byte), row-size: int -> result/eax: (addr T) +#sig maybe-get-slice table: (addr stream {(handle array byte), T}), key: (addr slice), row-size: int -> result/eax: (addr T) +sig slurp f: (addr buffered-file), s: (addr stream byte) +sig compute-width word: (addr array byte) -> result/eax: int +sig compute-width-of-slice s: (addr slice) -> result/eax: int +sig emit-hex-array out: (addr buffered-file), arr: (addr array byte) +sig next-word-or-string line: (addr stream byte), out: (addr slice) +sig write-int out: (addr stream byte), n: int +sig clear-stack s: (addr stack) +sig push s: (addr stack), n: int +sig pop s: (addr stack) -> n/eax: int +sig top s: (addr stack) -> n/eax: int +sig array-equal? a: (addr array int), b: (addr array int) -> result/eax: boolean +sig parse-array-of-ints ad: (addr allocation-descriptor), s: (addr string), out: (addr handle array int) +sig check-array-equal a: (addr array int), expected: (addr string), msg: (addr string) +#sig push-n-zero-bytes n: int +sig kernel-string-to-string ad: (addr allocation-descriptor), in: (addr kernel-string), out: (addr handle array byte) +sig kernel-string-length in: (addr kernel-string) -> result/eax: int +sig enable-screen-grid-mode +sig enable-screen-type-mode +sig screen-size -> nrows/eax: int, ncols/ecx: int +sig clear-screen +sig move-cursor-on-screen row: int, column: int +sig print-string-to-screen s: (addr array byte) +sig print-byte-to-screen c: byte +sig print-int32-hex-to-screen n: int +sig reset-formatting-on-screen +sig start-color-on-screen fg: int, bg: int +sig start-bold-on-screen +sig start-underline-on-screen +sig start-reverse-video-on-screen +sig start-blinking-on-screen +sig hide-cursor-on-screen +sig show-cursor-on-screen +sig enable-keyboard-immediate-mode +sig enable-keyboard-type-mode +sig read-key -> result/eax: byte +sig open filename: (addr array byte), write?: boolean, out: (addr handle buffered-file) +sig size in: (addr array _) -> result/eax: int diff --git a/400.txt b/400.txt deleted file mode 100644 index 81548ce5..00000000 --- a/400.txt +++ /dev/null @@ -1 +0,0 @@ -The 4xx series is for primitives implemented in Mu. 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 diff --git a/mu.vim b/mu.vim index bbbaebec..afe7704b 100644 --- a/mu.vim +++ b/mu.vim @@ -53,7 +53,7 @@ syntax match muControl "\<loop\>\|\<loop-if[^ ]*" highlight link muControl PreProc syntax match muKeyword " -> " -syntax keyword muKeyword fn type var +syntax keyword muKeyword fn type var sig highlight link muKeyword PreProc syntax match muFunction "\(fn\s*\)\@<=\(\S\+\)" |