From e1fd8abd4fa133480d13a7e70f95f7cf6b95a0d1 Mon Sep 17 00:00:00 2001 From: Nathan Braswell Date: Mon, 19 Apr 2021 01:39:04 -0400 Subject: [PATCH] Started working on basic wasm encoding/serialization. Added binary_file_writing, generalized arity comparison operators, bitwise operators, parsing of hex numbers, and wasm w/ memory section deserialization --- comp_wasm.kp | 11 +++++ k_prime.krak | 117 ++++++++++++++++++++++++++++++++++++++++++------- prelude.kp | 27 ++++++++---- stdlib/io.krak | 2 +- wasm.kp | 46 +++++++++++++++++++ 5 files changed, 177 insertions(+), 26 deletions(-) create mode 100644 comp_wasm.kp create mode 100644 wasm.kp diff --git a/comp_wasm.kp b/comp_wasm.kp new file mode 100644 index 0000000..664f4c3 --- /dev/null +++ b/comp_wasm.kp @@ -0,0 +1,11 @@ +(with_import "./wasm.kp" +(let ( + _ (println "args" *ARGV*) + (_ _ in out) (cond (!= (len *ARGV*) 4) (error "wrong number of params") + true *ARGV*) + _ (println "in" in "out" out) + wasm_code [ + ] + _ (write_file out (wasm_to_binary wasm_code)) + return_code 0 +) return_code )) diff --git a/k_prime.krak b/k_prime.krak index 74f5078..921bbc7 100644 --- a/k_prime.krak +++ b/k_prime.krak @@ -716,7 +716,7 @@ fun main(argc: int, argv: **char): int { } return KPResult::Ok(kpString(to_ret)); })), kpNil(), ret_0_sym) - grammar.add_to_nonterminal(atom, vec(grammar.add_terminal("-|(([a-z]|[A-Z]|_|\\*|/|\\?|\\+|!|=|&|<|>|%)([a-z]|[A-Z]|_|[0-9]|\\*|\\?|\\+|-|!|=|&|<|>|%)*)", kpNil(), fun(_: ref KPValue, input: ref str, l: int, r: int): KPResult { + grammar.add_to_nonterminal(atom, vec(grammar.add_terminal("-|(([a-z]|[A-Z]|_|\\*|/|\\?|\\+|!|=|&|\\||<|>|%)([a-z]|[A-Z]|_|[0-9]|\\*|\\?|\\+|-|!|=|&|\\||<|>|%)*)", kpNil(), fun(_: ref KPValue, input: ref str, l: int, r: int): KPResult { var s = input.slice(l,r) if s == "true" { return KPResult::Ok(kpTrue()); @@ -1005,6 +1005,39 @@ fun main(argc: int, argv: **char): int { return make_pair(null(), KPResult::Ok(kpInt(to_ret))) })); + env->set(str("&"), make_builtin_combiner(str("&"), 1, false, fun(params: vec, dynamic_env: *KPEnv): pair<*KPEnv, KPResult> { + if params.size != 2 { + return make_pair(null(), KPResult::Err(kpString(str("Need 2 params to &")))) + } + if !params[0].is_int() { return make_pair(null(), KPResult::Err(kpString(str("called & with first not an int")))); } + if !params[1].is_int() { return make_pair(null(), KPResult::Err(kpString(str("called & with second not an int")))); } + return make_pair(null(), KPResult::Ok(kpInt(params[0].get_int() & params[1].get_int()))) + })); + env->set(str("|"), make_builtin_combiner(str("|"), 1, false, fun(params: vec, dynamic_env: *KPEnv): pair<*KPEnv, KPResult> { + if params.size != 2 { + return make_pair(null(), KPResult::Err(kpString(str("Need 2 params to |")))) + } + if !params[0].is_int() { return make_pair(null(), KPResult::Err(kpString(str("called | with first not an int")))); } + if !params[1].is_int() { return make_pair(null(), KPResult::Err(kpString(str("called | with second not an int")))); } + return make_pair(null(), KPResult::Ok(kpInt(params[0].get_int() | params[1].get_int()))) + })); + env->set(str("<<"), make_builtin_combiner(str("<<"), 1, false, fun(params: vec, dynamic_env: *KPEnv): pair<*KPEnv, KPResult> { + if params.size != 2 { + return make_pair(null(), KPResult::Err(kpString(str("Need 2 params to <<")))) + } + if !params[0].is_int() { return make_pair(null(), KPResult::Err(kpString(str("called << with first not an int")))); } + if !params[1].is_int() { return make_pair(null(), KPResult::Err(kpString(str("called << with second not an int")))); } + return make_pair(null(), KPResult::Ok(kpInt(params[0].get_int() << params[1].get_int()))) + })); + env->set(str(">>"), make_builtin_combiner(str(">>"), 1, false, fun(params: vec, dynamic_env: *KPEnv): pair<*KPEnv, KPResult> { + if params.size != 2 { + return make_pair(null(), KPResult::Err(kpString(str("Need 2 params to >>")))) + } + if !params[0].is_int() { return make_pair(null(), KPResult::Err(kpString(str("called >> with first not an int")))); } + if !params[1].is_int() { return make_pair(null(), KPResult::Err(kpString(str("called >> with second not an int")))); } + return make_pair(null(), KPResult::Ok(kpInt(params[0].get_int() >> params[1].get_int()))) + })); + env->set(str("="), make_builtin_combiner(str("="), 1, false, fun(params: vec, dynamic_env: *KPEnv): pair<*KPEnv, KPResult> { if params.size != 2 { return make_pair(null(), KPResult::Err(kpString(str("Need 2 params to =")))) @@ -1018,36 +1051,56 @@ fun main(argc: int, argv: **char): int { return make_pair(null(), KPResult::Ok(kpBool(!params[0].equals(params[1])))) })); env->set(str("<"), make_builtin_combiner(str("<"), 1, false, fun(params: vec, dynamic_env: *KPEnv): pair<*KPEnv, KPResult> { - if params.size != 2 { - return make_pair(null(), KPResult::Err(kpString(str("Need 2 params to <")))) + if params.size <= 1 { + return make_pair(null(), KPResult::Err(kpString(str("Need 2 or more params to <")))) } if !params[0].is_int() { return make_pair(null(), KPResult::Err(kpString(str("called < with first not an int")))); } - if !params[1].is_int() { return make_pair(null(), KPResult::Err(kpString(str("called < with second not an int")))); } - return make_pair(null(), KPResult::Ok(kpBool(params[0].get_int() < params[1].get_int()))) + for (var i = 0; i < params.size - 1; i++;) { + if !params[i+1].is_int() { return make_pair(null(), KPResult::Err(kpString(str("called < with not an int")))); } + if !(params[i].get_int() < params[i+1].get_int()) { + return make_pair(null(), KPResult::Ok(kpBool(false))) + } + } + return make_pair(null(), KPResult::Ok(kpBool(true))) })); env->set(str("<="), make_builtin_combiner(str("<="), 1, false, fun(params: vec, dynamic_env: *KPEnv): pair<*KPEnv, KPResult> { - if params.size != 2 { - return make_pair(null(), KPResult::Err(kpString(str("Need 2 params to <=")))) + if params.size <= 1 { + return make_pair(null(), KPResult::Err(kpString(str("Need 2 or more params to <=")))) } if !params[0].is_int() { return make_pair(null(), KPResult::Err(kpString(str("called <= with first not an int")))); } - if !params[1].is_int() { return make_pair(null(), KPResult::Err(kpString(str("called <= with second not an int")))); } - return make_pair(null(), KPResult::Ok(kpBool(params[0].get_int() <= params[1].get_int()))) + for (var i = 0; i < params.size - 1; i++;) { + if !params[i+1].is_int() { return make_pair(null(), KPResult::Err(kpString(str("called <= with not an int")))); } + if !(params[i].get_int() <= params[i+1].get_int()) { + return make_pair(null(), KPResult::Ok(kpBool(false))) + } + } + return make_pair(null(), KPResult::Ok(kpBool(true))) })); env->set(str(">"), make_builtin_combiner(str(">"), 1, false, fun(params: vec, dynamic_env: *KPEnv): pair<*KPEnv, KPResult> { - if params.size != 2 { - return make_pair(null(), KPResult::Err(kpString(str("Need 2 params to >")))) + if params.size <= 1 { + return make_pair(null(), KPResult::Err(kpString(str("Need 2 or more params to >")))) } if !params[0].is_int() { return make_pair(null(), KPResult::Err(kpString(str("called > with first not an int")))); } - if !params[1].is_int() { return make_pair(null(), KPResult::Err(kpString(str("called > with second not an int")))); } - return make_pair(null(), KPResult::Ok(kpBool(params[0].get_int() > params[1].get_int()))) + for (var i = 0; i < params.size - 1; i++;) { + if !params[i+1].is_int() { return make_pair(null(), KPResult::Err(kpString(str("called > with not an int")))); } + if !(params[i].get_int() > params[i+1].get_int()) { + return make_pair(null(), KPResult::Ok(kpBool(false))) + } + } + return make_pair(null(), KPResult::Ok(kpBool(true))) })); env->set(str(">="), make_builtin_combiner(str(">="), 1, false, fun(params: vec, dynamic_env: *KPEnv): pair<*KPEnv, KPResult> { - if params.size != 2 { - return make_pair(null(), KPResult::Err(kpString(str("Need 2 params to >=")))) + if params.size <= 1 { + return make_pair(null(), KPResult::Err(kpString(str("Need 2 or more params to >=")))) } if !params[0].is_int() { return make_pair(null(), KPResult::Err(kpString(str("called >= with first not an int")))); } - if !params[1].is_int() { return make_pair(null(), KPResult::Err(kpString(str("called >= with second not an int")))); } - return make_pair(null(), KPResult::Ok(kpBool(params[0].get_int() >= params[1].get_int()))) + for (var i = 0; i < params.size - 1; i++;) { + if !params[i+1].is_int() { return make_pair(null(), KPResult::Err(kpString(str("called >= with not an int")))); } + if !(params[i].get_int() >= params[i+1].get_int()) { + return make_pair(null(), KPResult::Ok(kpBool(false))) + } + } + return make_pair(null(), KPResult::Ok(kpBool(true))) })); env->set(str("and"), make_builtin_combiner(str("and"), 0, false, fun(params: vec, dynamic_env: *KPEnv): pair<*KPEnv, KPResult> { @@ -1319,6 +1372,36 @@ fun main(argc: int, argv: **char): int { return make_pair(null(), KPResult::Ok(kpString(get_line(params[0].get_string(), 1024)))) } })); + env->set(str("write_file"), make_builtin_combiner(str("write_file"), 1, false, fun(params: vec, dynamic_env: *KPEnv): pair<*KPEnv, KPResult> { + if params.size != 2 { + return make_pair(null(), KPResult::Err(kpString(str("write_file with not a two params")))) + } else { + if !params[0].is_string() { + return make_pair(null(), KPResult::Err(kpString(str("write_file with first param not a (path) string")))) + } + if params[1].is_string() { + write_file(params[0].get_string(), params[1].get_string()) + } else if params[1].is_array() { + var arc = params[1].get_array_rc() + var size = arc.get().size + var out_vec = vec() + for (var i = 0; i < size; i++;) { + if !arc.get()[i].is_int() { + return make_pair(null(), KPResult::Err(kpString(str("write_file with vec member ") + i + "(" + pr_str(arc.get()[i], true) + ") isn't int"))) + } + var int_out = arc.get()[i].get_int() + if int_out < 0 || int_out > 255 { + return make_pair(null(), KPResult::Err(kpString(str("write_file with vec member ") + i + "(" + int_out + ") is out of 0-255 byte range " + int_out))) + } + out_vec.add((int_out) cast char) + } + write_file_binary(params[0].get_string(), out_vec) + } else { + return make_pair(null(), KPResult::Err(kpString(str("write_file with second param not a string or array")))) + } + return make_pair(null(), KPResult::Ok(kpNil())) + } + })); env->set(str("empty_env"), kpEnv(new()->construct())) // Launch into new kraken for interface and self-hosting features diff --git a/prelude.kp b/prelude.kp index 18fc7f3..e8950a2 100644 --- a/prelude.kp +++ b/prelude.kp @@ -138,16 +138,27 @@ string-to-int (lambda (s) (let ( - helper (rec-lambda recurse (s i result) + c0 (idx "0" 0) + c9 (idx "9" 0) + ca (idx "a" 0) + cz (idx "z" 0) + cA (idx "A" 0) + cZ (idx "Z" 0) + helper (rec-lambda recurse (s i radix result) (if (< i (len s)) - (recurse s (+ i 1) (+ (* 10 result) (- (idx s i) (idx "0" 0)))) + (let (c (idx s i)) + (cond (<= c0 c c9) (recurse s (+ i 1) radix (+ (* radix result) (- (idx s i) c0))) + (<= ca c cz) (recurse s (+ i 1) radix (+ (* radix result) (+ 10 (- (idx s i) ca)))) + (<= cA c cZ) (recurse s (+ i 1) radix (+ (* radix result) (+ 10 (- (idx s i) cA)))) + true (error "Impossible char in string-to-int")) + ) result ) )) - (if (= (idx s 0) (idx "-" 0)) - (- (helper s 1 0)) - (helper s 0 0) - ))) + (cond (= (idx s 0) (idx "-" 0)) (- (helper s 1 10 0)) + (and (> (len s) 2) (or (= "0x" (slice s 0 2)) (= "0X" (slice s 0 2)))) (helper s 2 16 0) + true (helper s 0 10 0)) + )) unescape-str (lambda (s) (let ( helper (rec-lambda recurse (s i r) @@ -168,10 +179,10 @@ (array (quote WS) (array "( | | |(;[ -~]* ))+") (lambda (x) nil)) - (array (quote number) (array "-?[0-9]+") (lambda (x) (string-to-int x))) + (array (quote number) (array "(0(x|X)([0-9]|[a-f]|[A-F])+)|(-?[0-9]+)") (lambda (x) (string-to-int x))) (array (quote string) (array "\"([#-[]| |[]-~]|(\\\\\\\\)|(\\\\n)|(\\\\t)|(\\*)|(\\\\0)| |[ -!]|(\\\\\"))*\"") (lambda (x) (unescape-str x))) - (array (quote bool_nil_symbol) (array "-|(([a-z]|[A-Z]|_|\\*|/|\\?|\\+|!|=|&|<|>|%)([a-z]|[A-Z]|_|[0-9]|\\*|\\?|\\+|-|!|=|&|<|>|%)*)") (lambda (x) (cond (= "true" x) true + (array (quote bool_nil_symbol) (array "-|(([a-z]|[A-Z]|_|\\*|/|\\?|\\+|!|=|&|\\||<|>|%)([a-z]|[A-Z]|_|[0-9]|\\*|\\?|\\+|-|!|=|&|\\||<|>|%)*)") (lambda (x) (cond (= "true" x) true (= "false" x) false (= "nil" x) nil true (str-to-symbol x)))) diff --git a/stdlib/io.krak b/stdlib/io.krak index fa5487e..b9e6d50 100644 --- a/stdlib/io.krak +++ b/stdlib/io.krak @@ -125,7 +125,7 @@ fun write_file_binary(path: str::str, vdata: vec::vec) { defer delete(char_path) var data = vdata.getBackingMemory() var size = vdata.size - var fp = fopen(char_path, "w") + var fp = fopen(char_path, "wb") fwrite((data) cast *void, (1) cast ulong, (size) cast ulong, fp) fclose(fp) } diff --git a/wasm.kp b/wasm.kp new file mode 100644 index 0000000..6d5dd2c --- /dev/null +++ b/wasm.kp @@ -0,0 +1,46 @@ + +(let ( + ; Vectors and Values + ; Bytes encode themselves + encode_u_LEB128 (rec-lambda recurse (x) + (cond (< x 0x80) [x] + true (cons (| (& x 0x7F) 0x1) (recurse (>> x 8)))) + ) + encode_s8_LEB128 (lambda (x) (encode_u_LEB128 (& x 255))) + encode_vector (lambda (enc v) + (concat (encode_u_LEB128 (len v)) (flat_map enc v) ) + ) + encode_floating_point (lambda (x) (error "unimplemented")) + encode_name (lambda (name) + (encode_vector (lambda (x) [x]) name) + ) + + ; Types + ; TODO + encode_limits (lambda (x) + (cond (= 1 (len x)) (concat [0x00] (encode_u_LEB128 (idx x 0))) + (= 2 (len x)) (concat [0x01] (encode_u_LEB128 (idx x 0)) (encode_u_LEB128 (idx x 1))) + true (error "trying to encode bad limits")) + ) + + ; Instructions + ; TODO + + ; Modules + encode_memory_section (lambda (x) + (let ( + encoded (encode_vector encode_limits x) + ) (concat [0x05] (encode_u_LEB128 (len encoded)) encoded )) + ) + + + wasm_to_binary (lambda (wasm_code) + (let ( + magic [ 0x00 0x61 0x73 0x6D ] + version [ 0x01 0x00 0x00 0x00 ] + memory (encode_memory_section [ [0x20 0x30] ]) + ) (concat magic version memory)) + ) +) + (provide wasm_to_binary) +)