From 5d2747e26f51cc2344a6bd95f93457248fdfebd8 Mon Sep 17 00:00:00 2001 From: Freya Murphy Date: Thu, 29 Feb 2024 17:04:28 -0500 Subject: fin prob --- Cargo.lock | 86 +-- Cargo.toml | 2 +- matrix-bin/Cargo.lock | 385 ----------- matrix-bin/Cargo.toml | 4 +- matrix-bin/src/helper.rs | 8 +- matrix-bin/src/main.rs | 119 +++- matrix-bin/src/repl.rs | 53 +- matrix-lang/Cargo.lock | 119 ++++ matrix-lang/Cargo.toml | 12 + matrix-lang/src/ast.rs | 482 +++++++++++++ matrix-lang/src/binary/deserialize.rs | 160 +++++ matrix-lang/src/binary/mod.rs | 154 +++++ matrix-lang/src/binary/prim.rs | 109 +++ matrix-lang/src/binary/serialize.rs | 283 ++++++++ matrix-lang/src/chunk.rs | 128 ++++ matrix-lang/src/compiler.rs | 628 +++++++++++++++++ matrix-lang/src/lex.rs | 777 +++++++++++++++++++++ matrix-lang/src/lib.rs | 42 ++ matrix-lang/src/parse.rs | 757 ++++++++++++++++++++ matrix-lang/src/prelude.rs | 60 ++ matrix-lang/src/value/clone.rs | 54 ++ matrix-lang/src/value/comp.rs | 373 ++++++++++ matrix-lang/src/value/exception.rs | 78 +++ matrix-lang/src/value/fmt.rs | 186 +++++ matrix-lang/src/value/function.rs | 43 ++ matrix-lang/src/value/gc.rs | 186 +++++ matrix-lang/src/value/hash.rs | 240 +++++++ matrix-lang/src/value/index.rs | 230 +++++++ matrix-lang/src/value/matrix.rs | 337 +++++++++ matrix-lang/src/value/mod.rs | 42 ++ matrix-lang/src/value/value.rs | 522 ++++++++++++++ matrix-lang/src/vm.rs | 461 +++++++++++++ matrix-macros/Cargo.toml | 2 +- matrix-macros/src/lib.rs | 6 +- matrix-std/Cargo.toml | 13 + matrix-std/src/core.rs | 295 ++++++++ matrix-std/src/io.rs | 174 +++++ matrix-std/src/iter.rs | 546 +++++++++++++++ matrix-std/src/lib.rs | 50 ++ matrix-std/src/math.rs | 688 +++++++++++++++++++ matrix-std/src/sys.rs | 252 +++++++ matrix-stdlib/Cargo.toml | 13 - matrix-stdlib/src/core.rs | 298 -------- matrix-stdlib/src/io.rs | 175 ----- matrix-stdlib/src/iter.rs | 538 --------------- matrix-stdlib/src/lib.rs | 32 - matrix-stdlib/src/math.rs | 689 ------------------- matrix-stdlib/src/sys.rs | 252 ------- matrix/Cargo.lock | 119 ---- matrix/Cargo.toml | 12 - matrix/src/ast.rs | 448 ------------ matrix/src/chunk.rs | 155 ----- matrix/src/compiler.rs | 636 ----------------- matrix/src/gc.rs | 176 ----- matrix/src/lex.rs | 772 --------------------- matrix/src/lib.rs | 122 ---- matrix/src/parse.rs | 775 --------------------- matrix/src/value.rs | 1223 --------------------------------- matrix/src/vm.rs | 490 ------------- 59 files changed, 8661 insertions(+), 7410 deletions(-) delete mode 100644 matrix-bin/Cargo.lock create mode 100644 matrix-lang/Cargo.lock create mode 100644 matrix-lang/Cargo.toml create mode 100644 matrix-lang/src/ast.rs create mode 100644 matrix-lang/src/binary/deserialize.rs create mode 100644 matrix-lang/src/binary/mod.rs create mode 100644 matrix-lang/src/binary/prim.rs create mode 100644 matrix-lang/src/binary/serialize.rs create mode 100644 matrix-lang/src/chunk.rs create mode 100644 matrix-lang/src/compiler.rs create mode 100644 matrix-lang/src/lex.rs create mode 100644 matrix-lang/src/lib.rs create mode 100644 matrix-lang/src/parse.rs create mode 100644 matrix-lang/src/prelude.rs create mode 100644 matrix-lang/src/value/clone.rs create mode 100644 matrix-lang/src/value/comp.rs create mode 100644 matrix-lang/src/value/exception.rs create mode 100644 matrix-lang/src/value/fmt.rs create mode 100644 matrix-lang/src/value/function.rs create mode 100644 matrix-lang/src/value/gc.rs create mode 100644 matrix-lang/src/value/hash.rs create mode 100644 matrix-lang/src/value/index.rs create mode 100644 matrix-lang/src/value/matrix.rs create mode 100644 matrix-lang/src/value/mod.rs create mode 100644 matrix-lang/src/value/value.rs create mode 100644 matrix-lang/src/vm.rs create mode 100644 matrix-std/Cargo.toml create mode 100644 matrix-std/src/core.rs create mode 100644 matrix-std/src/io.rs create mode 100644 matrix-std/src/iter.rs create mode 100644 matrix-std/src/lib.rs create mode 100644 matrix-std/src/math.rs create mode 100644 matrix-std/src/sys.rs delete mode 100644 matrix-stdlib/Cargo.toml delete mode 100644 matrix-stdlib/src/core.rs delete mode 100644 matrix-stdlib/src/io.rs delete mode 100644 matrix-stdlib/src/iter.rs delete mode 100644 matrix-stdlib/src/lib.rs delete mode 100644 matrix-stdlib/src/math.rs delete mode 100644 matrix-stdlib/src/sys.rs delete mode 100644 matrix/Cargo.lock delete mode 100644 matrix/Cargo.toml delete mode 100644 matrix/src/ast.rs delete mode 100644 matrix/src/chunk.rs delete mode 100644 matrix/src/compiler.rs delete mode 100644 matrix/src/gc.rs delete mode 100644 matrix/src/lex.rs delete mode 100644 matrix/src/lib.rs delete mode 100644 matrix/src/parse.rs delete mode 100644 matrix/src/value.rs delete mode 100644 matrix/src/vm.rs diff --git a/Cargo.lock b/Cargo.lock index 9a015a1..91b87c7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,9 +13,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.12" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b09b5178381e0874812a9b157f7fe84982617e48f71f4e3235482775e5b540" +checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" dependencies = [ "anstyle", "anstyle-parse", @@ -114,7 +114,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -125,9 +125,9 @@ checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "clipboard-win" -version = "5.1.0" +version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ec832972fefb8cf9313b45a0d1945e29c9c251f1d4c6eafc5fe2124c02d2e81" +checksum = "12f9a0700e0127ba15d1d52dd742097f821cd9c65939303a44d970465040a297" dependencies = [ "error-code", ] @@ -166,9 +166,9 @@ dependencies = [ [[package]] name = "error-code" -version = "3.0.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "281e452d3bad4005426416cdba5ccfd4f5c1280e10099e21db27f7c1c28347fc" +checksum = "a0474425d51df81997e2f90a21591180b38eccf27292d755f3e30750225c175b" [[package]] name = "fd-lock" @@ -221,46 +221,46 @@ checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "log" -version = "0.4.20" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] -name = "matrix" +name = "matrix-bin" version = "0.1.0" dependencies = [ - "anyhow", - "num-complex", - "num-rational", - "regex", + "clap", + "ctrlc", + "matrix-lang", + "matrix-std", + "rustyline", ] [[package]] -name = "matrix-bin" +name = "matrix-lang" version = "0.1.0" dependencies = [ - "clap", - "ctrlc", - "matrix", - "matrix-stdlib", - "rustyline", + "anyhow", + "num-complex", + "num-rational", + "regex", ] [[package]] name = "matrix-macros" version = "0.1.0" dependencies = [ - "matrix", + "matrix-lang", "quote", "syn 1.0.109", ] [[package]] -name = "matrix-stdlib" +name = "matrix-std" version = "0.1.0" dependencies = [ "anyhow", - "matrix", + "matrix-lang", "matrix-macros", "os_info", "rand", @@ -490,7 +490,7 @@ checksum = "e5af959c8bf6af1aff6d2b463a57f71aae53d1332da58419e30ad8dc7011d951" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -510,7 +510,7 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -538,9 +538,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.48" +version = "2.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" dependencies = [ "proc-macro2", "quote", @@ -610,9 +610,9 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", @@ -625,42 +625,42 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" diff --git a/Cargo.toml b/Cargo.toml index 7e1bcac..9375d89 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,3 @@ [workspace] resolver = "2" -members = [ "matrix", "matrix-bin" , "matrix-macros", "matrix-stdlib"] +members = [ "matrix-lang", "matrix-bin" , "matrix-macros", "matrix-std"] diff --git a/matrix-bin/Cargo.lock b/matrix-bin/Cargo.lock deleted file mode 100644 index f61fea6..0000000 --- a/matrix-bin/Cargo.lock +++ /dev/null @@ -1,385 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "aho-corasick" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" -dependencies = [ - "memchr", -] - -[[package]] -name = "anyhow" -version = "1.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "bitflags" -version = "2.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "clipboard-win" -version = "5.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ec832972fefb8cf9313b45a0d1945e29c9c251f1d4c6eafc5fe2124c02d2e81" -dependencies = [ - "error-code", -] - -[[package]] -name = "endian-type" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" - -[[package]] -name = "errno" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" -dependencies = [ - "libc", - "windows-sys", -] - -[[package]] -name = "error-code" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "281e452d3bad4005426416cdba5ccfd4f5c1280e10099e21db27f7c1c28347fc" - -[[package]] -name = "fd-lock" -version = "4.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e5768da2206272c81ef0b5e951a41862938a6070da63bcea197899942d3b947" -dependencies = [ - "cfg-if", - "rustix", - "windows-sys", -] - -[[package]] -name = "home" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" -dependencies = [ - "windows-sys", -] - -[[package]] -name = "libc" -version = "0.2.153" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" - -[[package]] -name = "linux-raw-sys" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" - -[[package]] -name = "log" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" - -[[package]] -name = "matrix" -version = "0.1.0" -dependencies = [ - "anyhow", - "num-complex", - "num-rational", - "regex", -] - -[[package]] -name = "matrix-repl" -version = "0.1.0" -dependencies = [ - "matrix", - "rustyline", -] - -[[package]] -name = "memchr" -version = "2.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" - -[[package]] -name = "nibble_vec" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" -dependencies = [ - "smallvec", -] - -[[package]] -name = "nix" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" -dependencies = [ - "bitflags", - "cfg-if", - "libc", -] - -[[package]] -name = "num-bigint" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-complex" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" -dependencies = [ - "autocfg", - "num-bigint", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" -dependencies = [ - "autocfg", -] - -[[package]] -name = "radix_trie" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" -dependencies = [ - "endian-type", - "nibble_vec", -] - -[[package]] -name = "regex" -version = "1.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" - -[[package]] -name = "rustix" -version = "0.38.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" -dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys", - "windows-sys", -] - -[[package]] -name = "rustyline" -version = "13.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02a2d683a4ac90aeef5b1013933f6d977bd37d51ff3f4dad829d4931a7e6be86" -dependencies = [ - "bitflags", - "cfg-if", - "clipboard-win", - "fd-lock", - "home", - "libc", - "log", - "memchr", - "nix", - "radix_trie", - "unicode-segmentation", - "unicode-width", - "utf8parse", - "winapi", -] - -[[package]] -name = "smallvec" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" - -[[package]] -name = "unicode-segmentation" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" - -[[package]] -name = "unicode-width" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" - -[[package]] -name = "utf8parse" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" diff --git a/matrix-bin/Cargo.toml b/matrix-bin/Cargo.toml index bdb8fb8..10c537e 100644 --- a/matrix-bin/Cargo.toml +++ b/matrix-bin/Cargo.toml @@ -10,6 +10,6 @@ path = "src/main.rs" [dependencies] clap = { version = "4", features = [ "derive" ] } ctrlc = "3" -matrix = { path = "../matrix" } -matrix-stdlib = { path = "../matrix-stdlib" } +matrix-lang = { path = "../matrix-lang" } +matrix-std = { path = "../matrix-std" } rustyline = { version = "13", features = [ "derive" ] } diff --git a/matrix-bin/src/helper.rs b/matrix-bin/src/helper.rs index 95e0848..df40946 100644 --- a/matrix-bin/src/helper.rs +++ b/matrix-bin/src/helper.rs @@ -1,7 +1,6 @@ use std::{borrow::Cow, rc::Rc, cell::RefCell}; - -use matrix::{lex::{Lexer, TokenData, Token}, vm::Vm}; use rustyline::{validate::{Validator, ValidationResult, ValidationContext}, highlight::Highlighter, Helper, Hinter, completion::Completer}; +use matrix_lang::prelude::*; #[derive(Helper, Hinter)] pub struct MatrixHelper { @@ -205,6 +204,7 @@ impl Highlighter for MatrixHelper { T::Else | T::While | T::Let | + T::Const | T::Function | T::Continue | T::Break | @@ -290,12 +290,12 @@ impl Completer for MatrixHelper { } let _ = (line, pos, ctx); - let globals = self.vm.borrow().global_names(); + let globals = self.vm.borrow().globals(); let names: Vec> = globals .borrow() .clone() .into_iter() - .filter(|n| n.starts_with(&buf)) + .filter_map(|n| if n.name.starts_with(&buf) { Some(n.name.clone()) } else { None }) .collect(); if buf.is_empty() { diff --git a/matrix-bin/src/main.rs b/matrix-bin/src/main.rs index 225b353..64d1b3f 100644 --- a/matrix-bin/src/main.rs +++ b/matrix-bin/src/main.rs @@ -1,6 +1,6 @@ use std::{path::PathBuf, io::{self, Read, IsTerminal}, fs, cell::RefCell, rc::Rc}; use clap::{Parser as ArgParser, ColorChoice}; -use matrix::{compiler::{Compiler, CompilerBuilder}, vm::Vm, parse::{Parser, ParserBuilder}, value::Value}; +use matrix_lang::prelude::*; use repl::Repl; mod repl; @@ -12,16 +12,20 @@ pub struct Args { /// A path to a input program. Uses stdin if not specified. file: Option, - /// Runs a repl, loading the provided program first + /// Compiles the given program #[arg(short, long)] - repl: bool, + compile: bool, + + /// Optional output for compiled output + #[arg(short, long)] + output: Option, /// Print out debug information #[arg(short, long)] debug: bool, /// Choses color - #[arg(short, long)] + #[arg(long)] color: Option, /// Disables optimizations @@ -29,18 +33,21 @@ pub struct Args { disable_optimizations: bool, } +pub enum Mode { + Repl, + Execute(String), + Compile(String, PathBuf), +} + pub struct State<'a> { parser: Parser, compiler: Compiler<'a>, vm: Rc>, - repl: bool, color: bool, - #[allow(unused)] - debug: bool, } impl<'a> State<'a> { - pub fn new (args: Args) -> (Self, Option) { + pub fn new (args: Args) -> (Self, Mode) { let stdin = read_stdin(); @@ -53,20 +60,43 @@ impl<'a> State<'a> { file = None; } - let repl = args.repl || file.is_none(); + let mode; + let repl; + if args.compile { + let path = match (args.output, args.file) { + (Some(path), _) => path, + (None, Some(path)) => { + let mut path = path.clone(); + path.set_extension("matc"); + path + }, + (None, None) => { + PathBuf::from("matc.out") + } + }; + let file = file.unwrap_or(String::new()); + mode = Mode::Compile(file, path); + repl = false; + } else if let Some(file) = file { + mode = Mode::Execute(file); + repl = false; + } else { + mode = Mode::Repl; + repl = true; + } + let mut vm = Vm::new(); let parser = ParserBuilder::new() .optimize(!args.disable_optimizations) .build(); - let mut vm = Vm::new(); let compiler = CompilerBuilder::new() .repl(repl) .debug(args.debug) .names(vm.names()) - .globals(vm.global_names()) + .globals(vm.globals()) .build(); - matrix_stdlib::load(&mut vm); + matrix_std::load(&mut vm); let color = match args.color { Some(ColorChoice::Auto) | None => { @@ -76,21 +106,40 @@ impl<'a> State<'a> { Some(ColorChoice::Never) => false, }; - (Self { parser, vm: Rc::new(RefCell::new(vm)), compiler, repl, debug: args.debug, color }, file) + (Self { + parser, + vm: Rc::new(RefCell::new(vm)), + compiler, + color, + }, mode) } - pub fn execute(&mut self, code: String) -> matrix::Result { - let ast = self.parser.parse(code)?; - let fun = self.compiler.compile(&ast)?; + pub fn execute(&mut self, fun: Rc) -> Result { let val = self.vm.try_borrow_mut().unwrap().run(fun)?; Ok(val) } + pub fn compile(&mut self, code: String) -> Result> { + let ast = self.parser.parse(code)?; + let fun = self.compiler.compile(&ast)?; + Ok(fun) + } + + pub fn load_program(&mut self, body: String) -> Result> { + match Program::load(&body)? { + Some(fun) => { + Ok(fun) + }, + None => { + self.compile(body) + }, + } + } } -pub fn error(err: matrix::Error, state: &State) { +pub fn error(err: Exception, state: &State) { if state.color { - println!("\x1b[31m\x1b[1mError:\x1b[0m {err}"); + println!("\x1b[31mError:\x1b[0m {err}"); } else { println!("Error: {err}"); } @@ -106,19 +155,33 @@ fn read_stdin() -> String { buffer } +fn handle_mode(state: &mut State, mode: Mode) -> Result<()> { + match mode { + Mode::Repl => { + let mut repl = Repl::new(state); + repl.run(); + }, + Mode::Execute(body) => { + let fun = state.load_program(body)?; + state.execute(fun)?; + }, + Mode::Compile(body, path) => { + let fun = state.compile(body)?; + let mut file = File::create(path).map_err(|e| + exception!(IO_EXCEPTION, "{e}") + )?; + Program::save(fun, &mut file)?; + } + }; + Ok(()) +} + fn main() { let args = Args::parse(); - let (mut state, file) = State::new(args); + let (mut state, mode) = State::new(args); - if let Some(file) = file { - if let Err(err) = state.execute(file) { - error(err, &state); - } + if let Err(e) = handle_mode(&mut state, mode) { + error(e, &state); } - - if state.repl { - Repl::new(state).run(); - } - } diff --git a/matrix-bin/src/repl.rs b/matrix-bin/src/repl.rs index f2964d4..fe9975f 100644 --- a/matrix-bin/src/repl.rs +++ b/matrix-bin/src/repl.rs @@ -1,21 +1,26 @@ use std::{io::Write, sync::atomic::Ordering}; - -use matrix::{value::Value, vm::Interupt}; -use rustyline::{Config, EditMode, ColorMode, Editor, CompletionType}; +use rustyline::{Config, EditMode, ColorMode, Editor, CompletionType, error::ReadlineError}; +use matrix_lang::prelude::*; use crate::{State, helper::MatrixHelper}; -pub struct Repl<'a> { - state: State<'a> +pub struct Repl<'s, 'a> { + state: &'s mut State<'a> } -impl<'a> Repl<'a> { +impl<'s, 'a> Repl<'s, 'a> { - pub fn new(state: State<'a>) -> Self { + pub fn new(state: &'s mut State<'a>) -> Self { Self { state } } - pub fn run(&mut self) { + fn execute(&mut self, line: String) -> Result { + let fun = self.state.compile(line)?; + let val = self.state.execute(fun)?; + Ok(val) + } + + pub fn run(&mut self) { let interupt = self.state.vm.borrow().interupt(); ctrlc::set_handler(move || { @@ -23,26 +28,33 @@ impl<'a> Repl<'a> { }).unwrap(); let config = Config::builder() + .indent_size(4) + .edit_mode(EditMode::Emacs) .check_cursor_position(true) .completion_type(CompletionType::List) - .edit_mode(EditMode::Emacs) .color_mode(if self.state.color { ColorMode::Enabled } else { ColorMode::Disabled }) .build(); let helper = MatrixHelper::new(self.state.vm.clone()); + let histfile = std::env::var("MATRIX_HISTORY").ok(); + let mut rl = Editor::with_config(config).unwrap(); rl.set_helper(Some(helper)); + if let Some(hf) = &histfile { + rl.load_history(hf).ok(); + } loop { - let Ok(line) = rl.readline(">> ") else { - break; - }; - if let Err(_) = rl.add_history_entry(&line) { - break; + let line = match rl.readline(">> ") { + Ok(line) => line, + Err(ReadlineError::Eof) => break, + Err(_) => continue, }; - match self.state.execute(line) { - Err(err) => crate::error(err, &self.state), + + rl.add_history_entry(&line).ok(); + + match self.execute(line) { Ok(val) => { if val != Value::Nil { if self.state.color { @@ -52,8 +64,13 @@ impl<'a> Repl<'a> { } } } - } - let _ = std::io::stdout().flush(); + Err(err) => crate::error(err, &self.state), + }; + std::io::stdout().flush().ok(); + } + + if let Some(hf) = &histfile { + rl.save_history(hf).ok(); } } diff --git a/matrix-lang/Cargo.lock b/matrix-lang/Cargo.lock new file mode 100644 index 0000000..ed6e714 --- /dev/null +++ b/matrix-lang/Cargo.lock @@ -0,0 +1,119 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "matrix" +version = "0.1.0" +dependencies = [ + "anyhow", + "num-complex", + "num-rational", + "regex", +] + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "regex" +version = "1.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" diff --git a/matrix-lang/Cargo.toml b/matrix-lang/Cargo.toml new file mode 100644 index 0000000..067f77c --- /dev/null +++ b/matrix-lang/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "matrix-lang" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1" +num-complex = "0.4" +num-rational = "0.4" +regex = "1" diff --git a/matrix-lang/src/ast.rs b/matrix-lang/src/ast.rs new file mode 100644 index 0000000..5720f76 --- /dev/null +++ b/matrix-lang/src/ast.rs @@ -0,0 +1,482 @@ +use std::{ops::{Neg, Not}, fmt::{Debug, Display}}; + +use crate::prelude::*; + +pub type AstName = (Rc, Position); +pub type InlineList = Vec; +pub type InlineMatrix = (usize, usize, Vec); +pub type InlineTable = Vec<(Expr, Expr)>; + +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub enum UnaryOp { + // normal math + Negate = 1, + // equality + Not = 2, +} + +impl TryFrom for UnaryOp { + type Error = Exception; + + fn try_from(value: u8) -> std::prelude::v1::Result { + if value < 1 || value > 2 { + Err(exception!(BINARY_EXCEPTION, "cannot convert {value} to UnaryOp")) + } else { + unsafe { Ok(std::mem::transmute(value)) } + } + } +} + +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub enum BinaryOp { + // normal math + Add = 1, + Subtract = 2, + Multiply = 3, + Divide = 4, + Modulo = 5, + Power = 6, + // binary math + BitwiseAnd = 7, + BitwiseOr = 8, + BitwiseXor = 9, + BitwiseShiftLeft = 10, + BitwiseShiftRight = 11, + // equality + Equals = 12, + NotEquals = 13, + GreaterEquals = 14, + LessEquals = 15, + GreaterThan = 16, + LessThan = 17, + // iter + Range = 18, + RangeEq = 19 +} + +impl TryFrom for BinaryOp { + type Error = Exception; + + fn try_from(value: u8) -> std::prelude::v1::Result { + if value < 1 || value > 19 { + Err(exception!(BINARY_EXCEPTION, "cannot convert {value} to BinaryOp")) + } else { + unsafe { Ok(std::mem::transmute(value)) } + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub enum ExprData { + NoOp, + + Literal(Value), + Ident(Rc), + + UnaryOp(Box, UnaryOp), + BinaryOp(Box, Box, BinaryOp), + + Index(Box, Vec), + FnCall(Box, Vec), + FieldAccess(Box, AstName), + + List(InlineList), + Matrix(InlineMatrix), + Table(InlineTable), + + And(Box, Box), + Or(Box, Box), + + Assign(Box, Box), + + If(Box, Box, Option>), + Function(AstName, Vec, Box, bool), + Lambda(Vec, Box, bool), + + Loop(Box), + While(Box, Box), + DoWhile(Box, Box), + For(AstName, Box, Box), + + Block(Vec), + + Try(Box, AstName, Box), + + Let(AstName, Box), + Const(AstName, Box), + + Pipeline(Box, Box), + + Continue, + Break, + Return(Box), +} + +impl Display for Expr { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{self:?}") + } +} + +impl Debug for Expr { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.data) + } +} + +#[derive(Clone, PartialEq)] +pub struct Expr { + pub data: ExprData, + pub pos: Position +} + +impl From<(ExprData, Position)> for Expr { + fn from(value: (ExprData, Position)) -> Self { + Self { data: value.0, pos: value.1 } + } +} + +impl Neg for BinaryOp { + type Output = Option; + fn neg(self) -> Self::Output { + use BinaryOp::*; + Some(match self { + Add => Subtract, + Subtract => Add, + _ => return None + }) + } +} + +impl Not for BinaryOp { + type Output = Option; + fn not(self) -> Self::Output { + use BinaryOp::*; + Some(match self { + Equals => NotEquals, + NotEquals => Equals, + GreaterEquals => LessThan, + LessEquals => GreaterThan, + GreaterThan => LessEquals, + LessThan => GreaterEquals, + _ => return None + }) + } +} + +impl Neg for Expr { + type Output = std::result::Result; + fn neg(mut self) -> Self::Output { + use ExprData as E; + let mut pos = self.pos; + let data = match self.data { + E::Literal(v) => E::Literal(-v), + E::BinaryOp(lhs, rhs, op) => { + let Some(op_n) = -op else { + return Err((E::BinaryOp(lhs, rhs, op), pos).into()); + }; + if let Ok(lhs) = -*lhs.clone() { + pos = lhs.pos; + E::BinaryOp(Box::new(lhs), rhs, op_n) + } else if let Ok(rhs) = -*rhs.clone() { + pos = rhs.pos; + E::BinaryOp(lhs, Box::new(rhs), op_n) + } else { + return Err((E::BinaryOp(lhs, rhs, op), pos).into()); + } + }, + E::UnaryOp(expr, op) => { + match op { + UnaryOp::Negate => { + pos = expr.pos; + expr.data + }, + _ => return Err((E::UnaryOp(expr, op), pos).into()) + } + } + data => return Err((data, pos).into()) + }; + self.data = data; + self.pos = pos; + Ok(self) + } +} + +impl Not for Expr { + type Output = std::result::Result; + fn not(mut self) -> Self::Output { + use ExprData as E; + let mut pos = self.pos; + let data = match self.data { + E::Literal(v) => E::Literal(Value::Bool(!v)), + E::UnaryOp(expr, op) => { + match op { + self::UnaryOp::Not => { + pos = expr.pos; + expr.data + }, + _ => return Err((E::UnaryOp(expr, op), pos).into()) + } + } + data => return Err((data, pos).into()) + }; + self.data = data; + self.pos = pos; + Ok(self) + } +} + +pub fn optimize(mut expr: Expr) -> Result { + use ExprData as E; + let mut pos = expr.pos; + let data: ExprData = match expr.data { + E::UnaryOp(expr, op) => { + let expr = optimize(*expr)?; + match match op { + UnaryOp::Negate => -expr, + UnaryOp::Not => !expr, + } { + Ok(expr) => { + pos = expr.pos; + expr.data + }, + Err(expr) => E::UnaryOp(Box::new(expr), op) + } + }, + E::BinaryOp(lhs, rhs, op) => { + let lhs = optimize(*lhs)?; + let rhs = optimize(*rhs)?; + if let (E::Literal(l), E::Literal(r)) = (lhs.clone().data, rhs.clone().data) { + match Value::binary_op(op, l, r) { + Err(err) => return Err(err), + Ok(value) => E::Literal(value), + } + } else { + E::BinaryOp(Box::new(lhs), Box::new(rhs), op) + } + }, + E::FnCall(ident, values) => { + E::FnCall(ident, values + .into_iter() + .map(optimize) + .collect::>>()?) + } + E::FieldAccess(expr, ident) => { + let expr = optimize(*expr)?; + E::FieldAccess(Box::new(expr), ident) + }, + E::List(list) => + E::List(list.into_iter() + .map(optimize) + .collect::>>()?), + E::Matrix(mat) => + E::Matrix((mat.0, mat.1, + mat.2.into_iter().map(optimize) + .collect::>>()?)), + E::Table(table) => + E::Table(table + .into_iter() + .map(|(k, v)| { + let k = optimize(k)?; + let v = optimize(v)?; + Ok((k, v)) + }).collect::>>()?), + E::And(lhs, rhs) => { + let lhs = optimize(*lhs)?; + let rhs = optimize(*rhs)?; + if let (E::Literal(l), r) = (lhs.clone().data, rhs.clone().data) { + match !!l.clone() { + true => { + pos = rhs.pos; + r + }, + false => { + pos = lhs.pos; + E::Literal(l) + }, + } + } else { + E::And(Box::new(lhs), Box::new(rhs)) + } + }, + E::Or(lhs, rhs) => { + let lhs = optimize(*lhs)?; + let rhs = optimize(*rhs)?; + if let (E::Literal(l), r) = (lhs.clone().data, rhs.clone().data) { + match !l.clone() { + true => { + pos = rhs.pos; + r + }, + false => { + pos = lhs.pos; + E::Literal(l) + }, + } + } else { + E::And(Box::new(lhs), Box::new(rhs)) + } + }, + E::Block(b) => { + let len = b.len(); + let b: Vec = + b.into_iter() + .map(optimize) + .collect::>>()? + .into_iter() + .enumerate() + .filter(|(i, e)| { + if let E::Literal(_) = e.data { + return i + 1 == len + } + E::NoOp != e.data + }) + .map(|e| e.1) + .collect(); + if b.is_empty() { + E::NoOp + } else { + E::Block(b) + } + }, + E::If(cond, block, else_block) => { + let cond = optimize(*cond)?; + let block = optimize(*block)?; + let else_block = else_block.map(|e| optimize(*e)).transpose()?; + if let E::Literal(lit) = cond.data { + if !!lit { + pos = block.pos; + block.data + } else if let Some(else_block) = else_block { + pos = else_block.pos; + else_block.data + } else { + E::NoOp + } + } else { + E::If(Box::new(cond), Box::new(block), else_block.map(|e| Box::new(e))) + } + }, + E::While(cond, block) => { + let cond = optimize(*cond)?; + let block = optimize(*block)?; + if let E::Literal(lit) = cond.data { + if !!lit { + E::Loop(Box::new(block)) + } else { + E::NoOp + } + } else { + E::While(Box::new(cond), Box::new(block)) + } + }, + E::For(name, cond, block) => { + let cond = optimize(*cond)?; + let block = optimize(*block)?; + E::For(name, Box::new(cond), Box::new(block)) + } + E::DoWhile(block, cond) => { + let cond = optimize(*cond)?; + let block = optimize(*block)?; + if let E::Literal(lit) = &cond.data { + if !!lit.clone() { + E::Loop(Box::new(block)) + } else { + E::DoWhile(Box::new(block), Box::new(cond)) + } + } else { + E::DoWhile(Box::new(block), Box::new(cond)) + } + } + E::Loop(block) => { + let block = Box::new(optimize(*block)?); + E::Loop(block) + }, + E::Try(expr, err, catch) => { + let expr = Box::new(optimize(*expr)?); + let catch = Box::new(optimize(*catch)?); + E::Try(expr, err, catch) + }, + E::Function(ident, params, stmt, varadic) => { + let stmt = Box::new(optimize(*stmt)?); + E::Function(ident, params, stmt, varadic) + } + E::Lambda(params, stmt, varadic) => { + let stmt = Box::new(optimize(*stmt)?); + E::Lambda(params, stmt, varadic) + }, + E::Let(ident, expr) => { + E::Let(ident, Box::new(optimize(*expr)?)) + }, + E::Const(ident, expr) => { + E::Const(ident, Box::new(optimize(*expr)?)) + }, + E::Assign(lhs, rhs) => { + let lhs = Box::new(optimize(*lhs)?); + let rhs = Box::new(optimize(*rhs)?); + E::Assign(lhs, rhs) + }, + E::Return(expr) => { + let expr = Box::new(optimize(*expr)?); + E::Return(expr) + }, + E::Pipeline(lhs, rhs) => { + let lhs = Box::new(optimize(*lhs)?); + let rhs = Box::new(optimize(*rhs)?); + E::Pipeline(lhs, rhs) + } + data => data + }; + expr.data = data; + expr.pos = pos; + Ok(expr) +} + +impl From for UnaryOp { + fn from(value: TokenData) -> Self { + use TokenData as T; + match value { + T::Subtract => Self::Negate, + T::Not => Self::Not, + _ => panic!("aaaaa") + } + } +} + +impl From for BinaryOp { + fn from(value: TokenData) -> Self { + use TokenData as T; + match value { + T::Equal => Self::Equals, + T::NotEqual => Self::NotEquals, + T::GreaterEqual => Self::GreaterEquals, + T::LessEqual => Self::LessEquals, + T::GreaterThan => Self::GreaterThan, + T::LessThan => Self::LessThan, + T::BitwiseShiftLeft => Self::BitwiseShiftLeft, + T::BitwiseShiftRight => Self::BitwiseShiftRight, + T::BitwiseAnd => Self::BitwiseAnd, + T::BitwiseOr => Self::BitwiseOr, + T::BitwiseXor => Self::BitwiseXor, + T::Add => Self::Add, + T::Subtract => Self::Subtract, + T::Multiply => Self::Multiply, + T::Divide => Self::Divide, + T::Modulo => Self::Modulo, + T::Power => Self::Power, + _ => panic!("aaaaa") + } + } +} + +impl Expr { + pub fn is_assignable(&self) -> bool { + use ExprData as E; + match self.data { + E::Ident(_) => true, + E::Index(_, _) => true, + E::FieldAccess(_, _) => true, + _ => false, + } + } +} diff --git a/matrix-lang/src/binary/deserialize.rs b/matrix-lang/src/binary/deserialize.rs new file mode 100644 index 0000000..679f6e5 --- /dev/null +++ b/matrix-lang/src/binary/deserialize.rs @@ -0,0 +1,160 @@ +use crate::prelude::*; + +use super::{prim::VarInt, Deserialize, Deserializer}; + +macro_rules! error { + ($($arg:tt)*) => { + exception!(BINARY_EXCEPTION, $($arg)*) + }; +} + +impl Deserialize for Program { + fn deserialize(s: &mut S) -> Result { + for _ in 0..5 { s.read::()?; } // skip header + let version: u8 = s.read()?; + if version != 0 { + return Err(error!("invalid program version {version}")) + } + let fun = >::deserialize(s)?; + Ok(Self { version, fun }) + } +} + +impl Deserialize for Instruction { + fn deserialize(s: &mut S) -> Result { + use Instruction as I; + let ins: u8 = s.read()?; + let ins = match ins { + 0 => I::NoOp, + 1 => I::CreateLocal, + 2 => I::LoadLocal(s.read()?), + 3 => I::StoreLocal(s.read()?), + 4 => I::DiscardLocals(s.read()?), + 5 => I::LoadGlobal(s.read()?), + 6 => I::StoreGlobal(s.read()?), + 7 => I::Const(s.read()?), + 8 => I::Int(s.read()?), + 9 => I::True, + 10 => I::False, + 11 => I::Nil, + 12 => I::Dup, + 13 => I::Discard(s.read()?), + 14 => I::UnaryOp(UnaryOp::try_from(s.read::()?)?), + 15 => I::BinaryOp(BinaryOp::try_from(s.read::()?)?), + 16 => I::NewList(s.read()?), + 17 => I::NewTable(s.read()?), + 18 => I::NewMatrix(s.read()?, s.read()?), + 19 => I::Field(s.read()?), + 20 => I::StoreField(s.read()?), + 21 => I::Index(s.read()?), + 22 => I::StoreIndex(s.read()?), + 23 => I::Jump(s.read()?), + 24 => I::JumpTrue(s.read()?), + 25 => I::JumpFalse(s.read()?), + 26 => I::JumpNil(s.read()?), + 27 => I::IterCreate, + 28 => I::IterNext, + 29 => I::Try(s.read()?), + 30 => I::TryEnd, + 31 => I::Call(s.read()?), + 32 => I::Return, + n => return Err(error!("invalid instruction op code {n}")) + }; + Ok(ins) + } +} + +impl Deserialize for Vec { + fn deserialize(s: &mut S) -> Result { + let len = s.read::()?.0; + let mut vec = Vec::with_capacity(len); + for _ in 0..len { + let v = T::deserialize(s)?; + vec.push(v); + } + Ok(vec) + } +} + +impl Deserialize for Gc { + fn deserialize(s: &mut S) -> Result { + Ok(Gc::new(T::deserialize(s)?)) + } +} + +impl Deserialize for Rc { + fn deserialize(s: &mut S) -> Result { + Ok(Rc::new(T::deserialize(s)?)) + } +} + +impl Deserialize for Position { + fn deserialize(s: &mut S) -> Result { + let row = s.read::()?.0; + let col = s.read::()?.0; + Ok(Self { row, col }) + } +} + +impl Deserialize for Chunk { + fn deserialize(s: &mut S) -> Result { + let constants = >::deserialize(s)?; + let code = >::deserialize(s)?; + let pos = >::deserialize(s)?; + Ok(Self { constants, code, pos }) + } +} + +impl Deserialize for Value { + fn deserialize(s: &mut S) -> Result { + use Value as V; + let ty = s.read::()?; + let value = match ty { + 0 => V::Nil, + 1 => V::Bool(s.read()?), + 2 => V::Int(s.read()?), + 3 => V::Float(s.read()?), + 4 => V::Ratio(Rational64::new(s.read()?, s.read()?)), + 5 => V::Complex(Complex64::new(s.read()?, s.read()?)), + 6 => V::to_regex(s.read::()?.as_str())?, + 7 => V::String(s.read::()?.into()), + 8 => V::List(>::deserialize(s)?.into()), + 9 => { + let domain = s.read()?; + let codomain = s.read()?; + let values = >::deserialize(s)?; + V::Matrix(Matrix::new(domain, codomain, values).into()) + }, + 10 => { + let len = s.read::()?.0; + let mut table = ValueMap::new(); + for _ in 0..len { + let key = ::deserialize(s)?; + let value = ::deserialize(s)?; + table.insert(key, value)?; + } + V::Table(table.into()) + }, + 11 => V::Function(>::deserialize(s)?), + 12 => V::Range((s.read()?, s.read()?, s.read()?).into()), + 13 => V::Iter(>::deserialize(s)?), + n => return Err(error!("invalid value code {n}")) + }; + Ok(value) + } +} + +impl Deserialize for Function { + fn deserialize(s: &mut S) -> Result { + let name = s.read::()?; + let arity = s.read()?; + let variadic = s.read()?; + let chunk = ::deserialize(s)?; + Ok(Function { + name: Rc::from(name.as_str()), + arity, + variadic, + fun: InnerFunction::Compiled(chunk.into()) + }) + } +} diff --git a/matrix-lang/src/binary/mod.rs b/matrix-lang/src/binary/mod.rs new file mode 100644 index 0000000..53b3fe5 --- /dev/null +++ b/matrix-lang/src/binary/mod.rs @@ -0,0 +1,154 @@ +use crate::prelude::*; +use std::io::{self, Read, Write}; + +mod serialize; +mod deserialize; +mod prim; + +pub struct Program { + version: u8, + fun: Rc +} + +const PROGRAM_HEADER: [u8; 5] = [0x00, 0x4d, 0x41, 0x54, 0x0a]; + +impl Program { + pub fn load(body: &str) -> Result>> { + let mut bytes = body.as_bytes(); + if bytes.len() < 6 { + return Ok(None) + } + let header = &bytes[0..5]; + if header != &PROGRAM_HEADER { + return Ok(None) + } + let mut s = ProgramDeserializer::from(&mut bytes); + let program = ::deserialize(&mut s)?; + s.finish()?; + Ok(Some(program.fun.clone())) + } + + pub fn save(fun: Rc, w: &mut W) -> Result<()> { + let mut s = ProgramSerializer::from(w); + let p = Program { + version: 0, + fun + }; + s.serialize(&p)?; + s.finish()?; + Ok(()) + } +} + +pub trait Primitive : Sized { + fn write(&self, w: &mut W) -> io::Result<()>; + fn read(r: &mut R) -> io::Result; +} + +pub trait Serialize : Sized { + fn serialize(&self, s: &mut S) -> Result<()>; +} + +pub trait Serializer : Sized { + fn serialize(&mut self, val: &S) -> Result<()> { + val.serialize(self) + } + fn write(&mut self, val: P) -> Result<()>; +} + +pub trait Deserialize : Sized { + fn deserialize(s: &mut S) -> Result; +} + +pub trait Deserializer : Sized { + fn deserialize(&mut self) -> Result { + D::deserialize(self) + } + fn read(&mut self) -> Result

; +} + +macro_rules! error { + ($($arg:tt)*) => { + exception!(BINARY_EXCEPTION, $($arg)*) + }; +} + +pub struct ProgramSerializer<'w, W: Write> { + writer: &'w mut W, + checksum: u64, +} + +impl<'w, W: Write> ProgramSerializer<'w, W> { + fn finish(self) -> Result<()> { + let bytes = self.checksum.to_le_bytes(); + self.writer.write(&bytes).map_err(|e| error!("{e}"))?; + Ok(()) + } +} + +impl<'w, W: Write> Serializer for ProgramSerializer<'w, W> { + fn write(&mut self, val: P) -> Result<()> { + val.write(self).map_err(|e| error!("{e}")) + } +} + +impl<'w, W: Write> Write for ProgramSerializer<'w, W> { + fn write(&mut self, buf: &[u8]) -> io::Result { + for b in buf { + self.checksum %= 0xf1e3beef; + self.checksum += *b as u64; + } + self.writer.write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + self.writer.flush() + } +} + +impl<'w, W: Write> From<&'w mut W> for ProgramSerializer<'w, W> { + fn from(writer: &'w mut W) -> Self { + Self { writer, checksum: 0xfe } + } +} + +pub struct ProgramDeserializer<'r, R: Read> { + reader: &'r mut R, + checksum: u64, +} + +impl<'r, R: Read> ProgramDeserializer<'r, R> { + fn finish(self) -> Result<()> { + let mut bytes = [0u8; 8]; + self.reader.read_exact(&mut bytes).map_err(|e| error!("{e}"))?; + let checksum = u64::from_le_bytes(bytes); + if self.checksum != checksum { + return Err(error!("checksum doesnt match")) + } + Ok(()) + } +} + +impl<'r, R: Read> Deserializer for ProgramDeserializer<'r, R> { + fn read(&mut self) -> Result

{ + P::read(self).map_err(|e| error!("{e}")) + } +} + +impl<'r, R: Read> Read for ProgramDeserializer<'r, R> { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let c = self.reader.read(buf)?; + for i in 0..c { + let b = buf[i]; + self.checksum %= 0xf1e3beef; + self.checksum += b as u64; + } + Ok(c) + } +} + +impl<'r, R: Read> From<&'r mut R> for ProgramDeserializer<'r, R> { + fn from(reader: &'r mut R) -> Self { + Self { reader, checksum: 0xfe } + } +} diff --git a/matrix-lang/src/binary/prim.rs b/matrix-lang/src/binary/prim.rs new file mode 100644 index 0000000..44e6898 --- /dev/null +++ b/matrix-lang/src/binary/prim.rs @@ -0,0 +1,109 @@ +use super::Primitive; +use std::io::{self, Read, Write}; + +macro_rules! marshal_number { + ($type:ident, $byte:expr) => { + impl Primitive for $type { + fn write(&self, write: &mut W) -> io::Result<()> { + write.write(&self.to_le_bytes())?; + Ok(()) + } + + fn read(read: &mut R) -> io::Result { + let mut bytes = [0u8; $byte]; + read.read_exact(&mut bytes)?; + Ok(Self::from_le_bytes(bytes)) + } + } + }; +} + +marshal_number!(u8, 1); +marshal_number!(u16, 2); +marshal_number!(i16, 2); +marshal_number!(u32, 4); +marshal_number!(i64, 8); +marshal_number!(f64, 8); + +impl Primitive for bool { + fn write(&self, write: &mut W) -> io::Result<()> { + if *self { + write.write(&[1u8; 1])?; + } else { + write.write(&[0u8; 1])?; + } + Ok(()) + } + + fn read(read: &mut R) -> io::Result { + let mut bytes = [0u8; 1]; + read.read_exact(&mut bytes)?; + if bytes[0] == 1 { + Ok(true) + } else { + Ok(false) + } + } +} + +impl Primitive for String { + fn write(&self, write: &mut W) -> io::Result<()> { + let bytes = self.as_bytes(); + let len = bytes.len(); + write.write(&(len as u32).to_le_bytes())?; + write.write(&bytes)?; + Ok(()) + } + + fn read(read: &mut R) -> io::Result { + let len = usize::read(read)?; + let mut bytes = vec![0u8; len]; + read.read_exact(&mut bytes)?; + Ok(String::from_utf8_lossy(bytes.as_slice()).to_string()) + } +} + +impl Primitive for usize { + fn write(&self, write: &mut W) -> io::Result<()> { + (*self as u32).write(write) + } + + fn read(read: &mut R) -> io::Result { + Ok(u32::read(read)? as usize) + } +} + +pub struct VarInt(pub usize); + +impl Primitive for VarInt { + fn write(&self, write: &mut W) -> io::Result<()> { + let mut data = self.0; + loop { + let mut byte = (data & 0x7F) as u8; + data >>= 7; + if data != 0 { + byte += 0x80; + } + write.write(&[byte; 1])?; + if data == 0 { + return Ok(()) + } + } + } + + fn read(read: &mut R) -> io::Result { + let mut buf = [0]; + let mut result = 0usize; + for count in 0..8 { + if read.read(&mut buf)? != 1 { + return Ok(Self(0)) + } + let byte = buf[0]; + result |= ((byte & 0x7F) as usize) << (7 * count); + if byte & 0x80 == 0 { + return Ok(Self(result)) + } + } + Ok(Self(0)) + } +} diff --git a/matrix-lang/src/binary/serialize.rs b/matrix-lang/src/binary/serialize.rs new file mode 100644 index 0000000..2f2b199 --- /dev/null +++ b/matrix-lang/src/binary/serialize.rs @@ -0,0 +1,283 @@ +use crate::{prelude::*, binary::PROGRAM_HEADER}; +use std::ops::Deref; +use super::{prim::VarInt, Program, Serializer, Serialize}; + +macro_rules! error { + ($($arg:tt)*) => { + exception!(BINARY_EXCEPTION, $($arg)*) + }; +} + +impl Serialize for Program { + fn serialize(&self, s: &mut S) -> Result<()> { + for b in PROGRAM_HEADER { + s.write(b)?; + } + s.write(self.version)?; + s.serialize(self.fun.as_ref())?; + Ok(()) + } +} + +impl Serialize for Instruction { + fn serialize(&self, s: &mut S) -> Result<()> { + use Instruction as I; + match self { + I::NoOp => { + s.write(0u8)?; + }, + I::CreateLocal => { + s.write(1u8)?; + }, + I::LoadLocal(idx) => { + s.write(2u8)?; + s.write(*idx)?; + }, + I::StoreLocal(idx) => { + s.write(3u8)?; + s.write(*idx)?; + }, + I::DiscardLocals(idx) => { + s.write(4u8)?; + s.write(*idx)?; + }, + I::LoadGlobal(idx) => { + s.write(5u8)?; + s.write(*idx)?; + } + I::StoreGlobal(idx) => { + s.write(6u8)?; + s.write(*idx)?; + }, + I::Const(idx) => { + s.write(7u8)?; + s.write(*idx)?; + }, + I::Int(i) => { + s.write(8u8)?; + s.write(*i)?; + }, + I::True => { + s.write(9u8)?; + }, + I::False => { + s.write(10u8)?; + }, + I::Nil => { + s.write(11u8)?; + }, + I::Dup => { + s.write(12u8)?; + }, + I::Discard(idx) => { + s.write(13u8)?; + s.write(*idx)?; + }, + I::UnaryOp(op) => { + s.write(14u8)?; + s.write(*op as u8)?; + }, + I::BinaryOp(op) => { + s.write(15u8)?; + s.write(*op as u8)?; + }, + I::NewList(len) => { + s.write(16u8)?; + s.write(*len)?; + }, + I::NewTable(len) => { + s.write(17u8)?; + s.write(*len)?; + }, + I::NewMatrix(d, c) => { + s.write(18u8)?; + s.write(*d)?; + s.write(*c)?; + }, + I::Field(idx) => { + s.write(19u8)?; + s.write(*idx)?; + }, + I::StoreField(idx) => { + s.write(20u8)?; + s.write(*idx)?; + }, + I::Index(idx) => { + s.write(21u8)?; + s.write(*idx)?; + }, + I::StoreIndex(idx) => { + s.write(22u8)?; + s.write(*idx)?; + }, + I::Jump(ip) => { + s.write(23u8)?; + s.write(*ip)?; + }, + I::JumpTrue(ip) => { + s.write(24u8)?; + s.write(*ip)?; + }, + I::JumpFalse(ip) => { + s.write(25u8)?; + s.write(*ip)?; + }, + I::JumpNil(ip) => { + s.write(26u8)?; + s.write(*ip)?; + }, + I::IterCreate => { + s.write(27u8)?; + }, + I::IterNext => { + s.write(28u8)?; + }, + I::Try(ip) => { + s.write(29u8)?; + s.write(*ip)?; + }, + I::TryEnd => { + s.write(30u8)?; + }, + I::Call(arity) => { + s.write(31u8)?; + s.write(*arity)?; + }, + I::Return => { + s.write(32u8)?; + }, + }; + Ok(()) + } +} + +impl Serialize for Vec { + fn serialize(&self, s: &mut S) -> Result<()> { + s.write(VarInt(self.len()))?; + for val in self { + val.serialize(s)?; + } + Ok(()) + } +} + +impl Serialize for Gc { + fn serialize(&self, s: &mut S) -> Result<()> { + self.deref().serialize(s) + } +} + +impl Serialize for Rc { + fn serialize(&self, s: &mut S) -> Result<()> { + self.deref().serialize(s) + } +} + +impl Serialize for Position { + fn serialize(&self, s: &mut S) -> Result<()> { + s.write(VarInt(self.row))?; + s.write(VarInt(self.col))?; + Ok(()) + } +} + +impl Serialize for Chunk { + fn serialize(&self, s: &mut S) -> Result<()> { + self.constants.serialize(s)?; + self.code.serialize(s)?; + self.pos.serialize(s)?; + Ok(()) + } +} + +impl Serialize for Value { + fn serialize(&self, s: &mut S) -> Result<()> { + use Value as V; + match self { + V::Nil => { + s.write(0u8)?; + }, + V::Bool(b) => { + s.write(1u8)?; + s.write(*b)?; + }, + V::Int(i) => { + s.write(2u8)?; + s.write(*i)?; + }, + V::Float(f) => { + s.write(3u8)?; + s.write(*f)?; + }, + V::Ratio(r) => { + s.write(4u8)?; + s.write(*r.numer())?; + s.write(*r.denom())?; + }, + V::Complex(c) => { + s.write(5u8)?; + s.write(c.re)?; + s.write(c.im)?; + }, + V::Regex(r) => { + s.write(6u8)?; + s.write(r.to_string())?; + }, + V::String(str) => { + s.write(7u8)?; + s.write(str.to_string())?; + }, + V::List(l) => { + s.write(8u8)?; + l.serialize(s)?; + }, + V::Matrix(m) => { + s.write(9u8)?; + s.write(m.domain)?; + s.write(m.codomain)?; + m.values.serialize(s)?; + }, + V::Table(t) => { + s.write(10u8)?; + s.write(VarInt(t.len()))?; + for (key, value) in t.entries() { + key.serialize(s)?; + value.serialize(s)?; + } + }, + V::Function(f) => { + s.write(11u8)?; + f.serialize(s)?; + }, + V::Range(r) => { + s.write(12u8)?; + s.write(r.0)?; + s.write(r.1)?; + s.write(r.2)?; + }, + V::Iter(f) => { + s.write(13u8)?; + f.serialize(s)?; + }, + V::File(_) => return Err(error!("cannot compile file")), + V::Exception(_) => return Err(error!("cannot compile exception")), + }; + Ok(()) + } +} + +impl Serialize for Function { + fn serialize(&self, s: &mut S) -> Result<()> { + s.write(self.name.to_string())?; + s.write(self.arity)?; + s.write(self.variadic)?; + use InnerFunction as F; + match &self.fun { + F::Compiled(c) => { + c.serialize(s)?; + }, + F::Native(_) => return Err(error!("cannot compile native function")), + }; + Ok(()) + } +} diff --git a/matrix-lang/src/chunk.rs b/matrix-lang/src/chunk.rs new file mode 100644 index 0000000..2fc3d9e --- /dev/null +++ b/matrix-lang/src/chunk.rs @@ -0,0 +1,128 @@ +use std::fmt::{Debug, Display}; +use crate::prelude::*; + +#[derive(Clone, Default)] +pub struct Chunk { + pub constants: Vec, + pub code: Vec, + pub pos: Vec, +} + +impl Chunk { + pub fn new() -> Self { + Self { + constants: Vec::new(), + code: Vec::new(), + pos: Vec::new(), + } + } +} + +#[derive(Clone, Debug)] +#[repr(align(4))] +pub enum Instruction { + NoOp, + + CreateLocal, + LoadLocal(u16), + StoreLocal(u16), + DiscardLocals(u16), + + LoadGlobal(u16), + StoreGlobal(u16), + + Const(u16), + Int(i16), + True, + False, + Nil, + + Dup, Discard(u16), + + UnaryOp(UnaryOp), + BinaryOp(BinaryOp), + + NewList(u16), + NewTable(u16), + NewMatrix(u16, u8), + + Field(u16), + StoreField(u16), + Index(u8), + StoreIndex(u8), + + Jump(u16), + JumpTrue(u16), + JumpFalse(u16), + JumpNil(u16), + + IterCreate, + IterNext, + + Try(u16), + TryEnd, + + Call(u8), + Return, +} + +impl Debug for Chunk { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "[Chunk {}]", self.code.len()) + } +} + +impl Display for Chunk { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!(f, "constants: ")?; + for (i, c) in self.constants.iter().enumerate() { + writeln!(f, " {i:04}: {c}")?; + } + writeln!(f, "code:")?; + for (i, ins) in self.code.iter().enumerate() { + writeln!(f, " {i:04}: {ins}")?; + } + Ok(()) + } +} + +impl Display for Instruction { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use Instruction::*; + match self { + NoOp => write!(f, "noop"), + CreateLocal => write!(f, "create local"), + LoadLocal(idx) => write!(f, "load local \x1b[33m{idx}\x1b[0m"), + StoreLocal(idx) => write!(f, "store local \x1b[33m{idx}\x1b[0m"), + DiscardLocals(count) => write!(f, "discard locals \x1b[35m{count}\x1b[0m"), + LoadGlobal(name) => write!(f, "load global \x1b[36m{name}\x1b[0m"), + StoreGlobal(name) => write!(f, "store global \x1b[36m{name}\x1b[0m"), + Const(idx) => write!(f, "const \x1b[33m{idx}\x1b[0m"), + Int(idx) => write!(f, "push \x1b[34m{idx}\x1b[0m"), + True => write!(f, "push \x1b[34mtrue\x1b[0m"), + False => write!(f, "push \x1b[34mfalse\x1b[0m"), + Nil => write!(f, "push \x1b[34mnil\x1b[0m"), + Dup => write!(f, "duplicate"), + Discard(count) => write!(f, "discard \x1b[35m{count}\x1b[0m"), + UnaryOp(op) => write!(f, "unary \x1b[32m{op:?}\x1b[0m"), + BinaryOp(op) => write!(f, "binary \x1b[32m{op:?}\x1b[0m"), + NewList(len) => write!(f, "list \x1b[35m{len}\x1b[0m"), + NewTable(len) => write!(f, "table \x1b[35m{len}\x1b[0m"), + NewMatrix(len, codomain) => write!(f, "matrix \x1b[35m{}\x1b[0mx\x1b[35m{}\x1b[0m", *len / (*codomain as u16), codomain), + Index(idx) => write!(f, "index \x1b[33m{idx}\x1b[0m"), + StoreIndex(idx) => write!(f, "store_index \x1b[33m{idx}\x1b[0m"), + Jump(idx) => write!(f, "jump \x1b[33m{idx}\x1b[0m"), + JumpTrue(idx) => write!(f, "jumpt \x1b[33m{idx}\x1b[0m"), + JumpFalse(idx) => write!(f, "jumpf \x1b[33m{idx}\x1b[0m"), + JumpNil(idx) => write!(f, "jumpn \x1b[33m{idx}\x1b[0m"), + Call(arity) => write!(f, "call \x1b[35m{arity}\x1b[0m"), + Return => write!(f, "return"), + IterCreate => write!(f, "iter create"), + IterNext => write!(f, "iter next"), + Field(name_idx) => write!(f, "field \x1b[33m{name_idx}\x1b[0m"), + StoreField(name_idx) => write!(f, "store field \x1b[33m{name_idx}\x1b[0m"), + Try(idx) => write!(f, "try \x1b[33m{idx}\x1b[0m"), + TryEnd => write!(f, "try end"), + } + } +} diff --git a/matrix-lang/src/compiler.rs b/matrix-lang/src/compiler.rs new file mode 100644 index 0000000..95c6ccf --- /dev/null +++ b/matrix-lang/src/compiler.rs @@ -0,0 +1,628 @@ +use crate::prelude::*; + +use Instruction as I; +use Value as V; +use ExprData as E; + +pub type NamesTable = Rc>>>; +pub type GlobalsTable = Rc>>; + +pub struct CompilerBuilder<'c> { + globals: GlobalsTable, + names: NamesTable, + repl: bool, + debug: bool, + name: Rc, + parent: Option<&'c Compiler<'c>>, +} + +impl<'c> CompilerBuilder<'c> { + + pub fn new() -> Self { + Self { + globals: Rc::new(RefCell::new(Vec::new())), + repl: false, + debug: false, + name: "".into(), + parent: None, + names: Rc::new(RefCell::new(Vec::new())) + } + } + + pub fn repl(mut self, repl: bool) -> Self { + self.repl = repl; + self + } + + pub fn debug(mut self, debug: bool) -> Self { + self.debug = debug; + self + } + + pub fn globals(mut self, globals: GlobalsTable) -> Self { + self.globals = globals; + self + } + + pub fn names(mut self, names: NamesTable) -> Self { + self.names = names; + self + } + + pub fn parent(mut self, parent: &'c Compiler) -> Self { + self.parent = Some(parent); + self + } + + pub fn name(mut self, name: Rc) -> Self { + self.name = name; + self + } + + pub fn build(self) -> Compiler<'c> { + Compiler { + name: self.name, + names: self.names, + parent: self.parent, + globals: self.globals, + repl: self.repl, + debug: self.debug, + scopes: Vec::new(), + locals: Vec::new(), + chunk: Chunk::new(), + loop_top: Vec::new(), + loop_bot: Vec::new(), + root_is_block: false, + } + } +} + +pub struct Compiler<'c> { + name: Rc, + parent: Option<&'c Compiler<'c>>, + + locals: Vec, + globals: GlobalsTable, + names: NamesTable, + + root_is_block: bool, + + scopes: Vec, + chunk: Chunk, + + loop_top: Vec<(usize, usize)>, + loop_bot: Vec, + + repl: bool, + debug: bool, +} + +#[derive(Clone)] +struct Local { + name: Rc, + idx: usize, + scope: usize, + is_const: bool, +} + +#[derive(Clone)] +pub struct Global { + pub name: Rc, + pub idx: usize, + pub is_const: bool, +} + +macro_rules! error { + ($($arg:tt)*) => { + exception!(COMPILE_EXCEPTION, $($arg)*) + }; +} + +impl<'c> Compiler<'c> { + + fn child(&'c self, name: Rc) -> Self { + CompilerBuilder::new() + .name(name) + .debug(self.debug) + .globals(self.globals.clone()) + .names(self.names.clone()) + .repl(false) + .parent(self) + .build() + } + + fn begin_scope(&mut self) { + if self.root_is_block { + self.root_is_block = false; + } else { + self.scopes.push(self.locals.len()) + } + } + + fn collapse_scopes(&mut self, top_scope: usize) { + let mut cutoff = usize::MAX; + while self.scopes.len() > top_scope { + cutoff = self.scopes.pop().unwrap() + } + if cutoff < self.locals.len() { + self.emit(Instruction::DiscardLocals((self.locals.len() - cutoff) as u16), self.last_pos()); + self.locals.truncate(cutoff); + }; + } + + fn end_scope(&mut self) { + let Some(cutoff) = self.scopes.pop() else { + return; + }; + if cutoff < self.locals.len() { + self.emit(Instruction::DiscardLocals((self.locals.len() - cutoff) as u16), self.last_pos()); + self.locals.truncate(cutoff); + }; + } + + fn create_local(&mut self, name: Rc, is_const: bool) -> Local { + let local = Local { name, idx: self.locals.len(), scope: self.scopes.len(), is_const }; + self.locals.push(local.clone()); + local + } + + fn create_global(&mut self, name: Rc, is_const: bool) -> Global { + let global = Global { name, idx: self.globals.borrow().len(), is_const }; + self.globals.borrow_mut().push(global.clone()); + global + } + + fn create_local_checked(&mut self, name: Rc, is_const: bool, pos: Position) -> Result { + if let Some(local) = self.find_local(&name) { + if local.scope == self.scopes.len() { + return Err(error!("redefined {name}").pos(pos)) + } + }; + Ok(self.create_local(name, is_const)) + } + + fn create_global_checked(&mut self, name: Rc, is_const: bool, pos: Position) -> Result { + if let Some(_) = self.find_global(&name) { + return Err(error!("redefined {name}").pos(pos)) + } + Ok(self.create_global(name, is_const)) + } + + fn find_local(&self, name: &str) -> Option { + for local in self.locals.iter().rev() { + if local.name.as_ref() == name { + return Some(local.clone()) + } + } + None + } + + fn find_global(&self, name: &str) -> Option { + if let Some(parent) = self.parent { + return parent.find_global(name) + } + for global in self.globals.borrow().iter() { + if global.name.as_ref() == name { + return Some(global.clone()) + } + } + None + } + + fn get_name(&mut self, name: Rc) -> usize { + // TODO: find name if already exists + let idx = self.names.borrow().len(); + self.names.borrow_mut().push(name); + idx + } + + fn emit_const(&mut self, val: Value, pos: Position) { + // TODO: find constant if already exists + self.chunk.constants.push(val); + let c = self.chunk.constants.len() - 1; + self.emit(Instruction::Const(c as u16), pos); + } + + fn can_make_globals(&self) -> bool { + self.repl && self.parent.is_none() && self.scopes.len() == 0 + } + + fn compile_value(&mut self, val: &Value, pos: Position) { + match val { + V::Nil => self.emit(I::Nil, pos), + V::Bool(b) => if *b { self.emit(I::True, pos) } else { self.emit(I::False, pos) }, + V::Int(i) => { + if let Ok(i) = i16::try_from(*i) { + self.emit(I::Int(i), pos); + } else { + self.emit_const(val.clone(), pos); + } + }, + _ => self.emit_const(val.clone(), pos), + } + } + + + + fn finish_loop(&mut self) { + self.loop_top.pop(); + while let Some(tmp) = self.loop_bot.pop() { + self.re_emit(I::Jump(self.cur()), tmp as usize); + } + } + + fn compile_expr(&mut self, expr: &Expr) -> Result<()> { + match &expr.data { + E::NoOp => self.emit(I::Nil, expr.pos), + E::If(cond, ifb, elseb) => { + self.compile_expr(cond)?; + let jmpidx = self.emit_temp(expr.pos); + self.compile_expr(ifb)?; + let jmpidx2 = self.emit_temp(expr.pos); + self.re_emit(I::JumpFalse(self.cur()), jmpidx); + if let Some(elseb) = elseb { + self.compile_expr(elseb)?; + } + self.re_emit(I::Jump(self.cur()), jmpidx2); + }, + E::Function(name, params, body, varadic) => { + let chunk = self.compile_function(name.clone(), params, body)?; + let arity = params.len() - if *varadic { 1 } else { 0 }; + let fun = Value::Function(Rc::new( + Function { + name: name.0.clone(), + arity, + fun: InnerFunction::Compiled(chunk.into()), + variadic: *varadic + } + )); + self.emit_const(fun, expr.pos); + self.emit(I::Dup, expr.pos); + if self.can_make_globals() { + let global = self.create_global_checked(name.0.clone(), false, name.1)?; + self.emit(I::StoreGlobal(global.idx as u16), expr.pos); + } else { + self.create_local_checked(name.0.clone(), false, name.1)?; + self.emit(I::CreateLocal, expr.pos); + } + }, + E::Lambda(params, body, varadic) => { + let name: AstName = (Rc::from(""), expr.pos); + let chunk = self.compile_function(name.clone(), params, body)?; + let arity = params.len() - if *varadic { 1 } else { 0 }; + let fun = Value::Function(Rc::new( + Function { + name: name.0.clone(), + arity, + fun: InnerFunction::Compiled(chunk.into()), + variadic: *varadic + } + )); + self.emit_const(fun, expr.pos); + }, + E::Loop(expr) => { + let idx = self.cur(); + self.loop_top.push((idx as usize, self.scopes.len())); + self.compile_expr(expr)?; + self.emit(I::Discard(1), expr.pos); + self.emit(I::Jump(idx), expr.pos); + self.finish_loop(); + self.emit(I::Nil, expr.pos); + }, + E::Try(expr, err, catch) => { + let jmpidx = self.emit_temp(expr.pos); + self.compile_expr(expr)?; + self.emit(I::TryEnd, expr.pos); + let jmpidx2 = self.emit_temp(expr.pos); + self.re_emit(I::Try(self.cur()), jmpidx); + self.begin_scope(); + self.create_local(err.0.clone(), true); + self.emit(I::CreateLocal, err.1); + self.compile_expr(catch)?; + self.end_scope(); + self.re_emit(I::Jump(self.cur() as u16), jmpidx2); + }, + E::While(cond, expr) => { + let top = self.cur(); + self.compile_expr(cond)?; + let jmpidx = self.emit_temp(expr.pos); + self.loop_top.push((top as usize, self.scopes.len())); + self.compile_expr(expr)?; + self.emit(I::Discard(1), expr.pos); + self.emit(I::Jump(top), expr.pos); + self.re_emit(I::JumpFalse(self.cur()), jmpidx); + self.finish_loop(); + self.emit(I::Nil, expr.pos); + }, + E::DoWhile(expr, cond) => { + let top = self.cur(); + self.loop_top.push((top as usize, self.scopes.len())); + self.compile_expr(expr)?; + self.emit(I::Discard(1), expr.pos); + self.compile_expr(cond)?; + self.emit(I::JumpTrue(top), expr.pos); + self.finish_loop(); + self.emit(I::Nil, expr.pos); + }, + E::For(name, cond, expr) => { + self.compile_expr(cond)?; + self.emit(I::IterCreate, cond.pos); + let top = self.cur(); + self.emit(I::IterNext, cond.pos); + self.emit(I::Dup, expr.pos); + let jumpidx = self.emit_temp(expr.pos); + self.loop_top.push((top as usize, self.scopes.len())); + self.begin_scope(); + self.create_local(name.0.clone(), true); + self.emit(I::CreateLocal, name.1); + self.compile_expr(expr)?; + self.emit(I::Discard(1), expr.pos); + self.end_scope(); + self.emit(I::Jump(top), expr.pos); + self.re_emit(I::JumpNil(self.cur()), jumpidx); + self.finish_loop(); + self.emit(I::Discard(2), expr.pos); + self.emit(I::Nil, expr.pos); + } + E::Block(block) => { + if block.len() == 0 { + self.emit(I::Nil, expr.pos); + return Ok(()); + } + self.begin_scope(); + for (i, expr) in block.iter().enumerate() { + self.compile_expr(expr)?; + if i + 1 != block.len() { + self.emit(I::Discard(1), expr.pos); + } + } + self.end_scope(); + }, + E::Let(name, expr) => { + self.compile_expr(expr)?; + self.emit(I::Dup, expr.pos); + if self.can_make_globals() { + let global = self.create_global_checked(name.0.clone(), false, name.1)?; + self.emit(I::StoreGlobal(global.idx as u16), expr.pos); + } else { + self.create_local_checked(name.0.clone(), false, name.1)?; + self.emit(I::CreateLocal, expr.pos); + } + }, + E::Const(name, expr) => { + self.compile_expr(expr)?; + self.emit(I::Dup, expr.pos); + if self.can_make_globals() { + let global = self.create_global_checked(name.0.clone(), true, name.1)?; + self.emit(I::StoreGlobal(global.idx as u16), expr.pos); + } else { + self.create_local_checked(name.0.clone(), true, name.1)?; + self.emit(I::CreateLocal, expr.pos); + } + }, + E::Continue => { + let top = self.loop_top.pop(); + if let Some((top, scope)) = top { + self.collapse_scopes(scope); + self.emit(I::Jump(top as u16), expr.pos); + } else { + return Err(error!("invalid continue outside loop").pos(expr.pos)) + } + }, + E::Break => { + let top = self.loop_top.pop(); + if let Some((_, scope)) = top { + self.collapse_scopes(scope); + let tmpidx = self.emit_temp(expr.pos); + self.loop_bot.push(tmpidx); + } else { + return Err(error!("invalid break outside loop").pos(expr.pos)) + } + }, + E::Return(expr) => { + self.compile_expr(expr)?; + self.emit(I::Return, expr.pos); + }, + E::Literal(val) => self.compile_value(val, expr.pos), + E::Ident(name) => { + if name.as_ref() == "_" { + self.emit(I::Nil, expr.pos); + } else if let Some(local) = self.find_local(name) { + self.emit(I::LoadLocal(local.idx as u16), expr.pos); + } else if let Some(global) = self.find_global(name) { + self.emit(I::LoadGlobal(global.idx as u16), expr.pos); + } else { + return Err(error!("variable '{name}' is undefined").pos(expr.pos)) + }; + }, + E::Assign(lhs, rhs) => { + self.compile_expr(rhs)?; + self.emit(I::Dup, rhs.pos); + match &lhs.data { + E::Ident(name) if name.as_ref() != "_" => { + if let Some(local) = self.find_local(&name) { + if local.is_const { + return Err(error!("cannot assign to const '{name}'").pos(lhs.pos)) + } + self.emit(I::StoreLocal(local.idx as u16), lhs.pos); + } else if let Some(global) = self.find_global(&name) { + if global.is_const { + return Err(error!("cannot assign to const '{name}'").pos(lhs.pos)) + } + self.emit(I::StoreGlobal(global.idx as u16), lhs.pos); + } else if self.can_make_globals() { + let global = self.create_global_checked(name.clone(), false, lhs.pos)?; + self.emit(I::StoreGlobal(global.idx as u16), lhs.pos); + } else { + self.create_local_checked(name.clone(), false, lhs.pos)?; + self.emit(I::CreateLocal, lhs.pos); + } + }, + E::Index(expr, params) => { + self.compile_expr(expr)?; + for param in params { + self.compile_expr(param)?; + } + self.emit(I::StoreIndex(params.len() as u8), expr.pos); + }, + E::FieldAccess(expr, ident) => { + self.compile_expr(expr)?; + let name = self.get_name(ident.0.clone()); + self.emit(I::StoreField(name as u16), expr.pos); + } + _ => return Err(error!("assignment to {lhs} is not allowed").pos(lhs.pos)) + } + } + E::UnaryOp(expr, op) => { + self.compile_expr(expr)?; + self.emit(I::UnaryOp(*op), expr.pos); + }, + E::BinaryOp(lhs, rhs, op) => { + self.compile_expr(lhs)?; + self.compile_expr(rhs)?; + self.emit(I::BinaryOp(*op), lhs.pos); + }, + E::Index(expr, params) => { + self.compile_expr(expr)?; + for param in params { + self.compile_expr(param)?; + } + self.emit(I::Index(params.len() as u8), expr.pos) + }, + E::FnCall(fun, params) => { + for expr in params { + self.compile_expr(expr)?; + } + self.compile_expr(fun)?; + self.emit(I::Call(params.len() as u8), expr.pos); + }, + E::FieldAccess(expr, field) => { + self.compile_expr(expr)?; + let idx = self.get_name(field.0.clone()); + self.emit(I::Field(idx as u16), expr.pos) + } + E::List(list) => { + for expr in list { + self.compile_expr(expr)?; + } + self.emit(I::NewList(list.len() as u16), expr.pos); + }, + E::Matrix(mat) => { + for expr in &mat.2 { + self.compile_expr(expr)?; + } + self.emit(I::NewMatrix(mat.2.len() as u16, mat.0 as u8), expr.pos); + }, + E::Table(table) => { + for (key, value) in table { + self.compile_expr(key)?; + self.compile_expr(value)?; + } + self.emit(I::NewTable(table.len() as u16), expr.pos); + }, + E::And(lhs, rhs) => { + self.compile_expr(lhs)?; + self.emit(I::Dup, lhs.pos); + let jmpidx = self.emit_temp(lhs.pos); + self.compile_expr(rhs)?; + self.re_emit(I::JumpFalse(self.cur()), jmpidx); + }, + E::Or(lhs, rhs) => { + self.compile_expr(lhs)?; + self.emit(I::Dup, lhs.pos); + let jmpidx = self.emit_temp(lhs.pos); + self.compile_expr(rhs)?; + self.re_emit(I::JumpTrue(self.cur()), jmpidx); + }, + E::Pipeline(lhs, rhs) => { + self.compile_expr(lhs)?; + self.compile_expr(rhs)?; + self.emit(I::Call(1), expr.pos); + } + }; + Ok(()) + } + + fn compile_function(&mut self, name: AstName, params: &Vec, body: &Box) -> Result { + let mut compiler = self.child(name.0); + for (name, pos) in params { + compiler.create_local(name.clone(), false); + compiler.emit(I::CreateLocal, *pos); + } + compiler.compile_expr(body)?; + compiler.finish(name.1)?; + Ok(compiler.chunk) + } + + fn cur(&self) -> u16 { + self.chunk.code.len() as u16 + } + + fn emit_temp(&mut self, pos: Position) -> usize { + let idx = self.chunk.code.len(); + self.emit(Instruction::NoOp, pos); + idx + } + + fn emit(&mut self, ins: Instruction, pos: Position) { + //println!("{}: {ins}", self.name); + self.chunk.code.push(ins); + self.chunk.pos.push(pos); + } + + fn re_emit(&mut self, ins: Instruction, idx: usize) { + //println!("{} at {}: {ins}", self.name, &self.chunk.code[idx]); + self.chunk.code[idx] = ins; + } + + fn last_pos(&self) -> Position { + if let Some(pos) = self.chunk.pos.last() { + *pos + } else { + Position::default() + } + } + + fn finish(&mut self, pos: Position) -> Result<()> { + let ins = match self.chunk.code.last() { + Some(ins) => ins.clone(), + None => { + self.emit(I::Nil, pos); + self.emit(I::Return, pos); + if self.debug { + println!("{}\n{}", self.name, self.chunk); + } + return Ok(()) + } + }; + match ins { + I::Return => {}, + _ => { + self.emit(I::Return, pos); + } + }; + + if self.loop_bot.len() > 0 { + return Err(error!("invalid break outside loop").pos(pos)) + } + + if self.debug { + println!("{}\n{}", self.name, self.chunk); + } + Ok(()) + } + + pub fn compile( + &mut self, + body: &Expr, + ) -> Result> { + if let ExprData::Block(_) = &body.data { + self.root_is_block = true; + } + self.chunk = Chunk::new(); + self.compile_expr(body)?; + self.finish(self.last_pos())?; + let fun = Function { name: self.name.clone(), fun: InnerFunction::Compiled(self.chunk.clone().into()), arity: 0, variadic: false }; + Ok(Rc::new(fun)) + } +} diff --git a/matrix-lang/src/lex.rs b/matrix-lang/src/lex.rs new file mode 100644 index 0000000..b2487ad --- /dev/null +++ b/matrix-lang/src/lex.rs @@ -0,0 +1,777 @@ +use std::fmt::{Debug, Display}; +use crate::prelude::*; + +pub struct RegexToken { + regex: Regex +} + +impl Debug for RegexToken { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.regex) + } +} + +impl PartialEq for RegexToken { + fn eq(&self, other: &Self) -> bool { + self.regex.as_str().eq(other.regex.as_str()) + } +} + +impl From for RegexToken { + fn from(regex: Regex) -> Self { + Self { regex } + } +} + +impl From for Regex { + fn from(value: RegexToken) -> Self { + value.regex + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Copy)] +pub struct Position { + pub row: usize, + pub col: usize, +} + +impl Default for Position { + fn default() -> Self { + Self { row: 1, col: 1 } + } +} + +impl Display for Position { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}:{}", self.row, self.col) + } +} + +impl Display for TokenData { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{self:?}") + } +} + +impl Display for Token { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.data) + } +} + +#[derive(Debug, PartialEq)] +pub enum TokenData { + //syntax + LeftParen, + RightParen, + LeftBrack, + RightBrack, + LeftBrace, + RightBrace, + Assign, + Access, + SemiColon, + Arrow, + ThinArrow, + Comma, + Range, + RangeEq, + Colon, + Backslash, + Varadic, + Pipe, + + // math + Regex(RegexToken), + Int(i64), + Float(f64), + Complex(f64), + String(Rc), + Ident(Rc), + + // equality + Equal, + NotEqual, + GreaterEqual, + LessEqual, + GreaterThan, + LessThan, + + And, + Or, + Not, + + BitwiseShiftLeft, + BitwiseShiftRight, + BitwiseAnd, + BitwiseOr, + BitwiseXor, + + Add, + Subtract, + Multiply, + Divide, + Modulo, + Power, + + AssignAnd, + AssignOr, + + AssignBitwiseShiftLeft, + AssignBitwiseShiftRight, + AssignBitwiseAnd, + AssignBitwiseOr, + AssignBitwiseXor, + + AssignAdd, + AssignSubtract, + AssignMultiply, + AssignDivide, + AssignModulo, + AssignPower, + + // keywords + If, + Else, + While, + Let, + Const, + Function, + True, + False, + Nil, + Continue, + Break, + Do, + Loop, + Return, + For, + In, + Try, + Catch, + + // eof + Eof, +} + +#[derive(Debug, PartialEq)] +pub struct Token { + pub data: TokenData, + pub pos: Position, + pub str: String, + pub bidx: usize, + pub blen: usize, +} + +pub struct Lexer { + pub index: usize, + len: usize, + data: Vec, + pos: Position, + byte_len: usize, +} + +trait IsIdent { + fn is_initial_ident(&self) -> bool; + fn is_ident(&self) -> bool; +} + +impl IsIdent for char { + fn is_initial_ident(&self) -> bool { + self.is_alphabetic() || *self == '_' + } + fn is_ident(&self) -> bool { + self.is_alphanumeric() || *self == '_' + } +} + +impl> From for Lexer { + fn from(value: T) -> Self { + Self::new(value) + } +} + +macro_rules! error { + ($($arg:tt)*) => { + exception!(PARSE_EXCEPTION, $($arg)*) + }; +} + +impl Lexer { + pub fn new>(input: T) -> Self { + let data: Vec = input.into().chars().collect(); + Self { + index: 0, + len: data.len(), + data, + pos: Position::default(), + byte_len: 0 + } + } + + fn peek(&self) -> char { + if self.index >= self.len { + return '\0'; + } + self.data[self.index] + } + + fn next(&mut self) -> char { + let c = self.peek(); + self.index += 1; + self.byte_len += c.len_utf8(); + + self.pos.col += 1; + if c == '\n' { + self.pos.col = 1; + self.pos.row += 1; + } + + c + } + + fn next_not_eof(&mut self) -> Result { + let c = self.next(); + if c == '\0' { + return Err(error!("unexpected end of file")) + } + Ok(c) + } + + fn next_expect(&mut self, expected: char) -> Result { + let c = self.next(); + if c != expected { + return Err(error!("expected character '{c}'")) + } + Ok(c) + } + + fn skip_whitespace(&mut self, ignore_newlines: bool) { + while self.peek().is_whitespace() && (ignore_newlines || self.peek() != '\n') { + self.next(); + } + } + + fn lex_string(&mut self, delimit: char) -> Result> { + + let mut buf = String::new(); + + loop { + let c = self.next_not_eof()?; + + if c == delimit { + break; + } + + if c != '\\' { + buf.push(c); + continue; + } + + let next = self.next_not_eof()?; + match next { + '\'' | '\"' | '\\' => buf.push(c), + '0' => buf.push('\0'), + 'a' => buf.push('\x07'), + 'b' => buf.push('\x08'), + 't' => buf.push('\t'), + 'n' => buf.push('\n'), + 'v' => buf.push('\x0b'), + 'f' => buf.push('\x0c'), + 'r' => buf.push('\r'), + 'e' => buf.push('\x1b'), + 'x' => { + let n1 = self.next_not_eof()?; + let n2 = self.next_not_eof()?; + buf.push(char::from_u32( + n1.to_digit(16).ok_or(error!("invalid digit '{n1}'"))? * 16 + + n2.to_digit(16).ok_or(error!("invalid digit '{n2}'"))? + ).unwrap()); + }, + 'u' => { + self.next_expect('{')?; + let mut n = 0u32; + loop { + let c = self.next_not_eof()?; + if c == '}' { break } + if n >= 0x1000_0000_u32 { + return Err(error!("invalid utf8 codepoint '{n}'")) + } + n = n * 16 + c.to_digit(16).ok_or(error!("invalid digit '{c}'"))?; + } + let ch = char::from_u32(n).ok_or(error!("invalid codepoint '{n}'"))?; + buf.push(ch); + + }, + _ => return Err(error!("invalid string escape '\\{next}'")) + } + } + + Ok(buf.into()) + } + + fn lex_ident(&mut self, initial: char) -> Result { + use TokenData as T; + + let mut buf = std::string::String::new(); + + if !initial.is_initial_ident() { + return Err(error!("unexpected character '{initial}'")) + } + + buf.push(initial); + + loop { + if self.peek().is_ident() { + buf.push(self.next()); + } else { + break; + } + } + + Ok(match buf.as_str() { + "if" => T::If, + "else" => T::Else, + "while" => T::While, + "let" => T::Let, + "const" => T::Const, + "fn" | "function" => T::Function, + "true" => T::True, + "false" => T::False, + "nil" => T::Nil, + "continue" => T::Continue, + "break" => T::Break, + "do" => T::Do, + "loop" => T::Loop, + "and" => T::And, + "or" => T::Or, + "not" => T::Not, + "return" => T::Return, + "for" => T::For, + "in" => T::In, + "try" => T::Try, + "catch" => T::Catch, + _ => T::Ident(buf.into()) + }) + } + + fn lex_radix(&mut self, radix: i64, radix_char: char) -> Result { + use TokenData as T; + + let mut n = 0i64; + let mut char_found = false; + + loop { + if let Some(i) = self.peek().to_digit(radix as u32) { + self.next(); + n = n * radix + (i as i64); + char_found = true; + } else if self.peek().is_ident() { + return Err(error!("invalid digit '{}'", self.peek())) + } else { + break; + } + } + + if char_found { + Ok(T::Int(n)) + } else { + Err(error!("invalid number radix specifier '0{radix_char}'")) + } + } + + fn lex_number(&mut self, initial: char) -> Result { + if initial == '0' { + match self.peek() { + 'x' => { + self.next(); + return self.lex_radix(16, 'x') + } + 'o' => { + self.next(); + return self.lex_radix(8, 'o'); + } + _ => () + } + } + + let mut buf = String::new(); + buf.push(initial); + + let mut pos = self.pos; + let mut idx = self.index; + let mut bidx = self.byte_len; + + if initial != '.' { + loop { + if !self.peek().is_ascii_digit() { break; } + buf.push(self.next()); + } + + if self.peek() == '.' { + pos = self.pos; + idx = self.index; + bidx = self.byte_len; + buf.push(self.next()); + } + } + + let last: char = buf.chars().last().unwrap(); + let is_range = initial != '.' && last == '.' && self.peek() == '.'; + + if is_range { + self.pos = pos; + self.index = idx; + self.byte_len = bidx; + buf.pop(); + } else { + loop { + if !self.peek().is_ascii_digit() { break; } + buf.push(self.next()); + } + + if self.peek() == 'e' || self.peek() == 'E' { + buf.push(self.next()); + if self.peek() == '+' || self.peek() == '-' { + buf.push(self.next()); + } + + loop { + if !self.peek().is_ascii_digit() { break; } + buf.push(self.next()); + } + } + } + + let complex = self.peek() == 'i'; + if complex { + self.next(); + } + + if self.peek().is_ident() { + return Err(error!("unexpected character '{}'", self.peek())) + } + + if let Ok(int) = buf.parse::() { + use TokenData as T; + if complex { + return Ok(T::Complex(int as f64)) + } + return Ok(T::Int(int)) + } + + if let Ok(float) = buf.parse::() { + use TokenData as T; + if complex { + return Ok(T::Complex(float)) + } + return Ok(T::Float(float)) + } + + Err(error!("invalid number '{buf}'")) + } + + fn read_token(&mut self, ignore_newlines: bool) -> Result { + use TokenData as T; + + self.skip_whitespace(ignore_newlines); + + let str_start = self.index; + let byte_start = self.byte_len; + + let pos = self.pos; + let char = self.next(); + let next = self.peek(); + + if char == '\0' { + let data = if ignore_newlines { T::Eof } else { T::SemiColon }; + return Ok(Token { + data, + pos, + str: String::new(), + bidx: byte_start, + blen: 0, + }) + } + + let data = match char { + '\n' => T::SemiColon, + '(' => T::LeftParen, + ')' => T::RightParen, + '[' => T::LeftBrack, + ']' => T::RightBrack, + '{' => T::LeftBrace, + '}' => T::RightBrace, + ':' => T::Colon, + '\\' => T::Backslash, + ';' => T::SemiColon, + '+' => { + match next { + '=' => { + self.next(); + T::AssignAdd + } + _ => T::Add + } + }, + '/' => { + match next { + '=' => { + self.next(); + T::AssignDivide + } + _ => T::Divide + } + }, + '%' => { + match next { + '=' => { + self.next(); + T::AssignModulo + } + _ => T::Modulo + } + }, + ',' => T::Comma, + '*' => { + match next { + '*' => { + self.next(); + match self.peek() { + '=' => { + self.next(); + T::AssignPower + }, + _ => T::Power + } + }, + '=' => { + self.next(); + T::AssignMultiply + } + _ => T::Multiply + } + }, + '!' => { + match next { + '=' => { + self.next(); + T::NotEqual + } + _ => T::Not + } + } + '&' => { + match next { + '&' => { + self.next(); + match self.peek() { + '=' => { + self.next(); + T::AssignAnd + }, + _ => T::And + } + }, + '=' => { + self.next(); + T::AssignBitwiseAnd + }, + _ => T::BitwiseAnd + } + }, + '|' => { + match next { + '|' => { + self.next(); + match self.peek() { + '=' => { + self.next(); + T::AssignOr + }, + _ => T::Or + } + }, + '=' => { + self.next(); + T::AssignBitwiseOr + }, + '>' => { + self.next(); + T::Pipe + }, + _ => T::BitwiseOr + } + }, + '-' => { + match next { + '>' => { + self.next(); + T::ThinArrow + }, + '=' => { + self.next(); + T::AssignSubtract + }, + _ => T::Subtract + } + }, + '=' => { + match next { + '>' => { + self.next(); + T::Arrow + } + '=' => { + self.next(); + T::Equal + } + _ => T::Assign + } + }, + '>' => { + match next { + '>' => { + self.next(); + match self.peek() { + '=' => { + self.next(); + T::AssignBitwiseShiftRight + }, + _ => T::BitwiseShiftRight + } + } + '=' => { + self.next(); + T::GreaterEqual + } + _ => T::GreaterThan + } + }, + '<' => { + match next { + '<' => { + self.next(); + match self.peek() { + '=' => { + self.next(); + T::AssignBitwiseShiftLeft + }, + _ => T::BitwiseShiftLeft + } + } + '=' => { + self.next(); + T::LessEqual + } + _ => T::LessThan + } + }, + '^' => { + match next { + '=' => { + self.next(); + T::AssignBitwiseXor + }, + _ => T::BitwiseXor + } + } + '\'' | '\"' => T::String(self.lex_string(char)?), + 'r' => { + match next { + '\'' | '\"' => { + self.next(); + T::Regex(regex::Regex::new(&self.lex_string(next)?) + .map(|e| e.into()) + .map_err(|e| error!("invalid regex: '{e}'"))?) + } + _ => { + self.lex_ident(char)? + } + } + }, + '.' => { + if next == '.' { + self.next(); + match self.peek() { + '.' => { + self.next(); + T::Varadic + }, + '=' => { + self.next(); + T::RangeEq + }, + _ => T::Range + } + } else if next.is_digit(10) { + self.lex_number(char)? + } else { + T::Access + } + }, + _ => { + if char.is_digit(10) { + self.lex_number(char)? + } else { + self.lex_ident(char)? + } + }, + }; + + let str_end = self.index; + let byte_end = self.byte_len; + let str = self.data[str_start..str_end].to_owned().into_iter().collect(); + Ok(Token { + data, + pos, + str, + bidx: byte_start, + blen: byte_end - byte_start + }) + } + + pub fn next_token(&mut self) -> Result { + let pos = self.pos; + match self.read_token(true) { + Ok(token) => Ok(token), + Err(e) => Err(e.pos(pos)), + } + } + + pub fn next_token_nl(&mut self) -> Result { + let pos = self.pos; + match self.read_token(false) { + Ok(token) => Ok(token), + Err(e) => Err(e.pos(pos)), + } + } + + pub fn peek_token(&mut self) -> Result { + let idx = self.index; + let pos = self.pos; + let bidx = self.byte_len; + let token = self.read_token(true); + self.index = idx; + self.pos = pos; + self.byte_len = bidx; + match token { + Ok(token) => Ok(token), + Err(e) => Err(e.pos(pos)), + } + } + + pub fn peek_token_nl(&mut self) -> Result { + let idx = self.index; + let pos = self.pos; + let bidx = self.byte_len; + let token = self.read_token(false); + self.index = idx; + self.pos = pos; + self.byte_len = bidx; + match token { + Ok(token) => Ok(token), + Err(e) => Err(e.pos(pos)), + } + } +} diff --git a/matrix-lang/src/lib.rs b/matrix-lang/src/lib.rs new file mode 100644 index 0000000..1452a09 --- /dev/null +++ b/matrix-lang/src/lib.rs @@ -0,0 +1,42 @@ +mod compiler; +mod value; +mod lex; +mod vm; +mod parse; +mod chunk; +mod ast; +mod binary; + +pub mod prelude; + +#[macro_export] +macro_rules! iter { + ($fn:expr) => { + $crate::prelude::Value::Iter( + ::std::rc::Rc::new( + $crate::prelude::Function { + name: ::std::rc::Rc::from(""), + arity: 0, + variadic: false, + fun: $crate::prelude::InnerFunction::Native( + ::std::rc::Rc::new($fn + ))})) + }; +} + +#[macro_export] +macro_rules! native { + ($name:expr, $arity:expr, $varadic:expr, $fn:expr) => { + $crate::prelude::Value::Function( + ::std::rc::Rc::new( $crate::prelude::Function { + name: std::rc::Rc::from($name), + arity: $arity, + variadic: $varadic, + fun: $crate::prelude::InnerFunction::Native( + ::std::rc::Rc::new($fn) + ) + }) + ) + } +} + diff --git a/matrix-lang/src/parse.rs b/matrix-lang/src/parse.rs new file mode 100644 index 0000000..3a4c5f2 --- /dev/null +++ b/matrix-lang/src/parse.rs @@ -0,0 +1,757 @@ +use crate::prelude::*; + +use Value as V; +use ExprData as E; +use TokenData as T; + +pub struct ParserBuilder { + optimize: bool +} + +impl ParserBuilder { + pub fn new() -> Self { + Self { optimize: true } + } + + pub fn optimize(mut self, optimize: bool) -> Self { + self.optimize = optimize; + self + } + + pub fn build(self) -> Parser { + Parser { + lexer: Lexer::new(""), + optimize: self.optimize + } + } +} + +pub struct Parser { + lexer: Lexer, + optimize: bool +} + +macro_rules! expr_parser { + ($parser:ident, $pattern:pat, $fn:ident) => {{ + let mut expr = $parser.$fn()?; + let pos = expr.pos; + loop { + let tok = $parser.lexer.peek_token_nl()?; + match tok.data { + $pattern => { + $parser.lexer.next_token_nl()?; + let temp = $parser.$fn()?; + expr = (E::BinaryOp(Box::new(expr), Box::new(temp), BinaryOp::from(tok.data)), pos).into() + } + _ => break + } + } + Ok(expr) + }}; +} + +macro_rules! expr_parser_reverse { + ($parser:ident, $pattern:pat, $fn:ident, $cur:ident) => {{ + let expr = $parser.$fn()?; + let tok = $parser.lexer.peek_token_nl()?; + let pos = tok.pos; + Ok(match tok.data { + $pattern => { + $parser.lexer.next_token_nl()?; + (E::BinaryOp(Box::new(expr), Box::new($parser.$cur()?), BinaryOp::from(tok.data)), pos).into() + } + _ => expr + }) + }}; +} + +macro_rules! error { + ($($arg:tt)*) => { + exception!(PARSE_EXCEPTION, $($arg)*) + }; +} + +impl Parser { + + fn force_token(&mut self, tok: TokenData) -> Result { + let next = self.lexer.next_token()?; + if next.data != tok { + Err(error!("expected token '{tok}'").pos(next.pos)) + } else { + Ok(tok) + } + } + + fn force_token_nl(&mut self, tok: TokenData) -> Result { + let next = self.lexer.next_token_nl()?; + if next.data != tok { + Err(error!("expected token '{tok}'").pos(next.pos)) + } else { + Ok(tok) + } + } + + fn parse_fn_call(&mut self) -> Result> { + self.force_token(T::LeftParen)?; + let mut params = Vec::new(); + loop { + let expr = match self.lexer.peek_token()?.data { + T::RightParen => { + self.lexer.next_token()?; + break + }, + _ => self.parse_expr()? + }; + params.push(expr); + let next = self.lexer.next_token()?; + match next.data { + T::Comma => continue, + T::RightParen => break, + _ => return Err(error!("unexpected token '{next}'").pos(next.pos)) + }; + } + Ok(params) + } + + fn parse_index(&mut self) -> Result> { + self.force_token(T::LeftBrack)?; + let mut indicies = Vec::new(); + loop { + let expr = match self.lexer.peek_token()?.data { + T::RightBrack => { + self.lexer.next_token()?; + break + }, + _ => self.parse_expr()? + }; + indicies.push(expr); + let next = self.lexer.next_token()?; + match next.data { + T::SemiColon => continue, + T::RightBrack => break, + _ => return Err(error!("unexpected token '{next}'").pos(next.pos)) + }; + } + Ok(indicies) + } + + fn parse_matrix_part(&mut self) -> Result> { + let mut part = Vec::new(); + loop { + let expr = match self.lexer.peek_token()?.data { + T::SemiColon => break, + T::RightBrack => break, + _ => self.parse_expr()? + }; + part.push(expr); + match self.lexer.peek_token()?.data { + T::Comma => { + self.lexer.next_token()?; + }, + _ => {}, + }; + } + Ok(part) + } + + fn parse_matrix(&mut self) -> Result { + let pos = self.lexer.peek_token()?.pos; + self.force_token(T::LeftBrack)?; + let mut parts = Vec::new(); + loop { + let part = self.parse_matrix_part()?; + parts.push(part); + let next = self.lexer.next_token()?; + match next.data { + T::SemiColon => continue, + T::RightBrack => break, + _ => return Err(error!("unexpected token '{next}'").pos(next.pos)) + }; + } + if parts.len() == 1 { + Ok((E::List(parts.pop().unwrap()), pos).into()) + } else { + let codomain = parts.len(); + let domain = parts[0].len(); + for part in parts.iter() { + if part.len() != domain { + return Err(error!("matrix row domains do not match: {} != {}", domain, part.len()).pos(pos)) + } + } + let mut data = Vec::new(); + parts.reverse(); + while let Some(part) = parts.pop() { + data.extend(part); + } + Ok((E::Matrix((domain, codomain, data)), pos).into()) + } + } + + fn parse_table_key(&mut self) -> Result { + let tok = self.lexer.next_token()?; + Ok(match tok.data { + T::LeftBrack => { + let expr = self.parse_expr()?; + self.force_token(T::RightBrack)?; + expr + }, + T::Ident(ident) => (E::Literal(V::String(ident.to_string().into())), tok.pos).into(), + T::String(string) => (E::Literal(V::String(string.to_string().into())), tok.pos).into(), + t => return Err(error!("unexpected token '{t}'").pos(tok.pos)) + }) + } + + fn parse_table(&mut self) -> Result { + let pos = self.lexer.peek_token()?.pos; + self.force_token(T::Colon)?; + self.force_token(T::LeftBrace)?; + let mut table = Vec::new(); + if self.lexer.peek_token()?.data == T::RightBrace { + self.lexer.next_token()?; + return Ok((E::Table(table), pos).into()) + } + loop { + let key = self.parse_table_key()?; + self.force_token(T::Assign)?; + let value = self.parse_expr()?; + table.push((key, value)); + let next = self.lexer.next_token()?; + match next.data { + T::Comma => continue, + T::RightBrace => break, + _ => return Err(error!("unexpected token '{next}'").pos(next.pos)) + } + } + Ok((E::Table(table), pos).into()) + } + + fn parse_paren(&mut self) -> Result { + self.force_token(T::LeftParen)?; + let expr = self.parse_expr()?; + self.force_token(T::RightParen)?; + Ok(expr) + } + + fn parse_params(&mut self) -> Result<(Vec<(Rc, Position)>, bool)> { + let tok = self.lexer.next_token()?; + match tok.data { + T::Ident(ident) => { + let params = vec![(ident, tok.pos)]; + if self.lexer.peek_token()?.data == T::Varadic { + self.lexer.next_token()?; + return Ok((params, true)) + } else { + return Ok((params, false)) + } + } + T::LeftParen => (), + t => return Err(error!("unexpected token '{t}'").pos(tok.pos)) + } + + let mut params = Vec::new(); + let mut varadic = false; + + if self.lexer.peek_token()?.data == T::RightParen { + return Ok((params, varadic)); + } + + loop { + let ident = self.parse_ident()?; + params.push(ident); + let next = self.lexer.next_token()?; + match next.data { + T::Varadic => { + varadic = true; + self.force_token(T::RightParen)?; + break; + } + T::Comma => continue, + T::RightParen => break, + _ => return Err(error!("unexpected token '{next}'").pos(next.pos)) + } + } + + Ok((params, varadic)) + } + + fn parse_ident(&mut self) -> Result { + let next = self.lexer.next_token()?; + if let T::Ident(ident) = next.data { + Ok((ident, next.pos)) + } else { + Err(error!("unexpected token '{next}'").pos(next.pos)) + } + } + + fn parse_wrapped_ident(&mut self) -> Result { + if self.lexer.peek_token()?.data == T::LeftParen { + self.lexer.next_token()?; + let ident = self.parse_ident()?; + self.force_token(T::RightParen)?; + Ok(ident) + } else { + self.parse_ident() + } + } + + fn parse_ident_nl(&mut self) -> Result { + let next = self.lexer.next_token_nl()?; + if let T::Ident(ident) = next.data { + Ok((ident, next.pos)) + } else { + Err(error!("unexpected token '{next}'").pos(next.pos)) + } + } + + fn parse_function(&mut self) -> Result { + let pos = self.lexer.peek_token()?.pos; + self.force_token(T::Function)?; + let ident = self.parse_ident()?; + let (params, varadic) = match self.lexer.peek_token()?.data { + T::LeftBrace => (vec![], false), + _ => self.parse_params()?, + }; + let expr = self.parse_expr()?; + Ok((E::Function(ident, params, Box::new(expr), varadic), pos).into()) + } + + fn parse_lambda(&mut self) -> Result { + let pos = self.lexer.peek_token()?.pos; + self.force_token(T::Backslash)?; + let (params, varadic) = match self.lexer.peek_token()?.data { + T::Arrow => (vec![], false), + _ => self.parse_params()?, + }; + self.force_token(T::Arrow)?; + let expr = self.parse_expr()?; + Ok((E::Lambda(params, Box::new(expr), varadic), pos).into()) + } + + fn parse_do_while(&mut self) -> Result { + let pos = self.lexer.peek_token()?.pos; + self.force_token(T::Do)?; + let expr = Box::new(self.parse_expr()?); + self.force_token(T::While)?; + let cond = Box::new(self.parse_expr()?); + Ok((E::DoWhile(expr, cond), pos).into()) + } + + fn parse_while(&mut self) -> Result { + let pos = self.lexer.peek_token()?.pos; + self.force_token(T::While)?; + let cond = Box::new(self.parse_expr()?); + let expr = Box::new(self.parse_expr()?); + Ok((E::While(cond, expr), pos).into()) + } + + fn parse_loop(&mut self) -> Result { + let pos = self.lexer.peek_token()?.pos; + self.force_token(T::Loop)?; + let expr = self.parse_expr()?; + Ok((E::Loop(Box::new(expr)), pos).into()) + } + + fn parse_for(&mut self) -> Result { + let pos = self.lexer.peek_token()?.pos; + self.force_token(T::For)?; + let name = self.parse_ident()?; + self.force_token(T::In)?; + let cond = Box::new(self.parse_expr()?); + let expr = Box::new(self.parse_expr()?); + Ok((E::For(name, cond, expr), pos).into()) + } + + fn parse_if(&mut self) -> Result { + let pos = self.lexer.peek_token()?.pos; + self.force_token(T::If)?; + let cond = Box::new(self.parse_expr()?); + let expr = Box::new(self.parse_expr()?); + + if self.lexer.peek_token()?.data != T::Else { + return Ok((E::If(cond, expr, None), pos).into()) + } + self.lexer.next_token()?; + + if self.lexer.peek_token()?.data == T::If { + Ok((E::If(cond, expr, Some(Box::new(self.parse_if()?))), pos).into()) + } else { + Ok((E::If(cond, expr, Some(Box::new(self.parse_expr()?))), pos).into()) + } + } + + fn parse_let(&mut self) -> Result { + let pos = self.lexer.peek_token()?.pos; + self.force_token(T::Let)?; + let ident = self.parse_ident_nl()?; + if self.lexer.peek_token_nl()?.data == T::Assign { + self.force_token_nl(T::Assign)?; + Ok((E::Let(ident, Box::new(self.parse_expr()?)), pos).into()) + } else { + Ok((E::Let(ident, Box::new((E::Literal(V::Nil), pos).into())), pos).into()) + } + } + + fn parse_const(&mut self) -> Result { + let pos = self.lexer.peek_token()?.pos; + self.force_token(T::Const)?; + let ident = self.parse_ident_nl()?; + self.force_token(T::Assign)?; + let expr = self.parse_expr()?; + Ok((E::Const(ident, Box::new(expr)), pos).into()) + } + + fn parse_return(&mut self) -> Result { + let pos = self.lexer.peek_token()?.pos; + self.force_token(T::Return)?; + Ok((E::Return(Box::new(self.parse_expr()?)), pos).into()) + } + + fn parse_block(&mut self) -> Result { + let mut block = Vec::new(); + let pos = self.lexer.peek_token()?.pos; + self.force_token(T::LeftBrace)?; + loop { + let expr = match self.lexer.peek_token()?.data { + T::RightBrace => { + self.lexer.next_token()?; + break + }, + T::SemiColon => { + self.lexer.next_token()?; + continue; + } + _ => self.parse_expr()? + }; + block.push(expr); + let next = self.lexer.next_token_nl()?; + match next.data { + T::SemiColon => continue, + T::RightBrace => break, + _ => return Err(error!("expected a semicolon").pos(next.pos)) + } + } + Ok((E::Block(block), pos).into()) + } + + fn parse_try(&mut self) -> Result { + let pos = self.lexer.peek_token()?.pos; + self.force_token(T::Try)?; + let expr = self.parse_expr()?; + self.force_token(T::Catch)?; + let ident = self.parse_wrapped_ident()?; + let catch = self.parse_expr()?; + Ok((E::Try(Box::new(expr), ident, Box::new(catch)), pos).into()) + } + + fn parse_value(&mut self) -> Result { + let tok = self.lexer.next_token()?; + let data = match tok.data { + T::Nil => E::Literal(V::Nil), + T::Int(i) => E::Literal(V::Int(i)), + T::Float(f) => E::Literal(V::Float(f)), + T::Complex(c) => E::Literal(V::Complex(Complex64::new(0.0, c))), + T::Regex(r) => E::Literal(V::Regex(Rc::new(r.into()))), + T::String(s) => E::Literal(V::String(s.to_string().into())), + T::True => E::Literal(V::Bool(true)), + T::False => E::Literal(V::Bool(false)), + T::Ident(ident) => E::Ident(ident), + t => return Err(error!("unexpected token '{t}'").pos(tok.pos)) + }; + Ok((data, tok.pos).into()) + } + + fn parse_term(&mut self) -> Result { + use T::*; + match self.lexer.peek_token()?.data { + Function => self.parse_function(), + Backslash => self.parse_lambda(), + Do => self.parse_do_while(), + While => self.parse_while(), + For => self.parse_for(), + Let => self.parse_let(), + Const => self.parse_const(), + LeftBrace => self.parse_block(), + Return => self.parse_return(), + If => self.parse_if(), + Loop => self.parse_loop(), + Try => self.parse_try(), + Break => { + let next = self.lexer.next_token()?; + Ok((E::Break, next.pos).into()) + }, + Continue => { + let next = self.lexer.next_token()?; + Ok((E::Continue, next.pos).into()) + }, + LeftBrack => self.parse_matrix(), + Colon => self.parse_table(), + LeftParen => self.parse_paren(), + _ => self.parse_value(), + } + } + + fn parse_expr_expr_access(&mut self) -> Result { + let mut expr = self.parse_term()?; + let pos = expr.pos; + loop { + let tok = self.lexer.peek_token()?; + match tok.data { + T::Access => { + self.force_token(T::Access)?; + let temp = self.parse_ident()?; + expr = (E::FieldAccess(Box::new(expr), temp), pos).into(); + }, + _ => break + } + } + Ok(expr) + } + + fn parse_expr_call(&mut self) -> Result { + let mut expr = self.parse_expr_expr_access()?; + let pos = expr.pos; + loop { + let tok = self.lexer.peek_token()?; + match tok.data { + T::LeftBrack => { + let index = self.parse_index()?; + expr = (E::Index(Box::new(expr), index), pos).into(); + }, + T::LeftParen => { + let params = self.parse_fn_call()?; + expr = (E::FnCall(Box::new(expr), params), pos).into(); + } + _ => break + } + } + Ok(expr) + } + + fn parse_expr_unary(&mut self) -> Result { + let tok = self.lexer.peek_token_nl()?; + Ok(match tok.data { + T::Not => { + self.lexer.next_token()?; + (E::UnaryOp(Box::new(self.parse_expr_unary()?), UnaryOp::Not), tok.pos).into() + } + T::Subtract => { + self.lexer.next_token()?; + (E::UnaryOp(Box::new(self.parse_expr_unary()?), UnaryOp::Negate), tok.pos).into() + } + _ => self.parse_expr_call()? + }) + } + + fn parse_expr_pow(&mut self) -> Result { + expr_parser_reverse!( + self, + T::Power, + parse_expr_unary, + parse_expr_pow + ) + } + + fn parse_expr_mult(&mut self) -> Result { + expr_parser!(self, T::Multiply | T::Divide | T::Modulo, parse_expr_pow) + } + + fn parse_expr_add(&mut self) -> Result { + expr_parser!(self, T::Add | T::Subtract, parse_expr_mult) + } + + fn parse_expr_shift(&mut self) -> Result { + expr_parser!( + self, + T::BitwiseShiftLeft | T::BitwiseShiftRight, + parse_expr_add + ) + } + + fn parse_expr_bit_and(&mut self) -> Result { + expr_parser!(self, T::BitwiseAnd, parse_expr_shift) + } + + fn parse_expr_bit_or(&mut self) -> Result { + expr_parser!(self, T::BitwiseOr, parse_expr_bit_and) + } + + fn parse_expr_compare(&mut self) -> Result { + expr_parser!( + self, + T::Equal | T::NotEqual | + T::LessThan | T::GreaterThan | + T::LessEqual | T::GreaterEqual, + parse_expr_bit_or + ) + } + + fn parse_expr_and(&mut self) -> Result { + let mut expr = self.parse_expr_compare()?; + let pos = expr.pos; + loop { + let tok = self.lexer.peek_token()?; + match tok.data { + T::And => { + self.force_token(T::And)?; + let temp = self.parse_expr_compare()?; + expr = (E::And(Box::new(expr), Box::new(temp)), pos).into(); + }, + _ => break + } + } + Ok(expr) + } + + fn parse_expr_or(&mut self) -> Result { + let mut expr = self.parse_expr_and()?; + let pos = expr.pos; + loop { + let tok = self.lexer.peek_token()?; + match tok.data { + T::Or => { + self.force_token(T::Or)?; + let temp = self.parse_expr_and()?; + expr = (E::Or(Box::new(expr), Box::new(temp)), pos).into(); + }, + _ => break + } + } + Ok(expr) + } + + fn parse_expr_range(&mut self) -> Result { + let expr = self.parse_expr_or()?; + let pos = expr.pos; + match self.lexer.peek_token()?.data { + T::Range => { + self.lexer.next_token()?; + let temp = self.parse_expr_or()?; + Ok((E::BinaryOp(Box::new(expr), Box::new(temp), BinaryOp::Range), pos).into()) + }, + T::RangeEq => { + self.lexer.next_token()?; + let temp = self.parse_expr_or()?; + Ok((E::BinaryOp(Box::new(expr), Box::new(temp), BinaryOp::RangeEq), pos).into()) + }, + _ => Ok(expr) + } + } + + fn parse_expr_op_assign(&mut self) -> Result { + use BinaryOp as B; + let expr = self.parse_expr_range()?; + let tok = self.lexer.peek_token_nl()?; + let pos = tok.pos; + let data: ExprData = match tok.data { + T::Assign => { + self.lexer.next_token_nl()?; + E::Assign(Box::new(expr.clone()),Box::new(self.parse_expr()?)) + }, + T::AssignAnd => { + self.lexer.next_token_nl()?; + E::Assign(Box::new(expr.clone()),Box::new((E::And(Box::new(expr), Box::new(self.parse_expr()?)), pos).into())) + }, + T::AssignOr => { + self.lexer.next_token_nl()?; + E::Assign(Box::new(expr.clone()),Box::new((E::Or(Box::new(expr), Box::new(self.parse_expr()?)),pos).into())) + }, + T::AssignAdd => { + self.lexer.next_token_nl()?; + E::Assign(Box::new(expr.clone()),Box::new((E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::Add),pos).into())) + }, + T::AssignSubtract => { + self.lexer.next_token_nl()?; + E::Assign(Box::new(expr.clone()),Box::new((E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::Subtract),pos).into())) + }, + T::AssignMultiply => { + self.lexer.next_token_nl()?; + E::Assign(Box::new(expr.clone()),Box::new((E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::Multiply),pos).into())) + }, + T::AssignDivide => { + self.lexer.next_token_nl()?; + E::Assign(Box::new(expr.clone()),Box::new((E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::Divide),pos).into())) + }, + T::AssignModulo => { + self.lexer.next_token_nl()?; + E::Assign(Box::new(expr.clone()),Box::new((E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::Modulo),pos).into())) + }, + T::AssignPower => { + self.lexer.next_token_nl()?; + E::Assign(Box::new(expr.clone()),Box::new((E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::Power),pos).into())) + }, + T::AssignBitwiseAnd => { + self.lexer.next_token_nl()?; + E::Assign(Box::new(expr.clone()),Box::new((E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::BitwiseAnd),pos).into())) + }, + T::AssignBitwiseOr => { + self.lexer.next_token_nl()?; + E::Assign(Box::new(expr.clone()),Box::new((E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::BitwiseOr),pos).into())) + }, + T::AssignBitwiseXor => { + self.lexer.next_token_nl()?; + E::Assign(Box::new(expr.clone()),Box::new((E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::BitwiseXor),pos).into())) + }, + T::AssignBitwiseShiftLeft => { + self.lexer.next_token_nl()?; + E::Assign(Box::new(expr.clone()),Box::new((E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::BitwiseShiftLeft),pos).into())) + }, + T::AssignBitwiseShiftRight => { + self.lexer.next_token_nl()?; + E::Assign(Box::new(expr.clone()),Box::new((E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::BitwiseShiftRight),pos).into())) + }, + _ => expr.data + }; + Ok((data, pos).into()) + } + + fn parse_expr(&mut self) -> Result { + let mut expr = self.parse_expr_op_assign()?; + let pos = expr.pos; + loop { + let tok = self.lexer.peek_token()?; + match tok.data { + T::Pipe => { + self.force_token(T::Pipe)?; + let temp = self.parse_expr_op_assign()?; + expr = (E::Pipeline(Box::new(expr), Box::new(temp)), pos).into(); + }, + _ => break + } + } + Ok(expr) + } + + fn parse_root(&mut self) -> Result { + let mut block = Vec::new(); + loop { + match self.lexer.peek_token()?.data { + T::Eof => break, + T::SemiColon => { + self.lexer.next_token()?; + continue + } + _ => {} + }; + let expr = self.parse_expr()?; + block.push(expr); + let next = self.lexer.next_token_nl()?; + match next.data { + T::Eof => break, + T::SemiColon => continue, + _ => return Err(error!("expected a semicolon").pos(next.pos)) + }; + } + Ok((E::Block(block), Position::default()).into()) + } + + pub fn parse>(&mut self, into: T) -> Result { + let lexer = Lexer::new(into); + self.lexer = lexer; + let ast = self.parse_root()?; + if self.optimize { + Ok(optimize(ast)?) + } else { + Ok(ast) + } + } +} diff --git a/matrix-lang/src/prelude.rs b/matrix-lang/src/prelude.rs new file mode 100644 index 0000000..c5af0c8 --- /dev/null +++ b/matrix-lang/src/prelude.rs @@ -0,0 +1,60 @@ +pub type Result = std::result::Result; + +pub use crate::value::Value as Value; +pub use crate::value::exception::Exception as Exception; +pub use crate::value::matrix::Matrix as Matrix; +pub use crate::value::gc::Gc as Gc; +pub use crate::value::hash::ValueMap as ValueMap; +pub use crate::value::function::Function as Function; +pub use crate::value::function::InnerFunction as InnerFunction; + +pub use num_complex::Complex64 as Complex64; +pub use num_rational::Rational64 as Rational64; +pub use regex::Regex as Regex; +pub use std::fs::File as File; + +pub use std::rc::Rc as Rc; +pub use std::cell::RefCell as RefCell; + +pub use crate::exception as exception; +pub use crate::native as native; +pub use crate::iter as iter; + +pub use crate::value::exception::HASH_EXCEPTION as HASH_EXCEPTION; +pub use crate::value::exception::VALUE_EXCEPTION as VALUE_EXCEPTION; +pub use crate::value::exception::PARSE_EXCEPTION as PARSE_EXCEPTION; +pub use crate::value::exception::COMPILE_EXCEPTION as COMPILE_EXCEPTION; +pub use crate::value::exception::RUNTIME_EXCEPTION as RUNTIME_EXCEPTION; +pub use crate::value::exception::BINARY_EXECPTION as BINARY_EXCEPTION; +pub use crate::value::exception::IO_EXECPTION as IO_EXCEPTION; + +pub use crate::value::clone::ValueClone as ValueClone; +pub use crate::value::hash::TryHash as TryHash; + +pub use crate::vm::Vm as Vm; +pub use crate::vm::StackFrame as StackFrame; +pub use crate::vm::Interupt as Interupt; + +pub use crate::lex::Lexer as Lexer; +pub use crate::lex::Position as Position; +pub use crate::lex::Token as Token; +pub use crate::lex::TokenData as TokenData; +pub use crate::parse::Parser as Parser; +pub use crate::parse::ParserBuilder as ParserBuilder; +pub use crate::compiler::Compiler as Compiler; +pub use crate::compiler::CompilerBuilder as CompilerBuilder; +pub use crate::compiler::NamesTable as NamesTable; +pub use crate::compiler::GlobalsTable as GlobalsTable; +pub use crate::compiler::Global as Global; + +pub use crate::ast::AstName as AstName; +pub use crate::ast::Expr as Expr; +pub use crate::ast::ExprData as ExprData; +pub use crate::ast::BinaryOp as BinaryOp; +pub use crate::ast::UnaryOp as UnaryOp; +pub use crate::ast::optimize as optimize; + +pub use crate::chunk::Chunk as Chunk; +pub use crate::chunk::Instruction as Instruction; + +pub use crate::binary::Program as Program; diff --git a/matrix-lang/src/value/clone.rs b/matrix-lang/src/value/clone.rs new file mode 100644 index 0000000..d5ac983 --- /dev/null +++ b/matrix-lang/src/value/clone.rs @@ -0,0 +1,54 @@ +use crate::prelude::*; + +pub trait ValueClone { + fn deep_clone(&self) -> Self; + fn shallow_clone(&self) -> Self; +} + +impl ValueClone for Value { + fn deep_clone(&self) -> Self { + use Value as V; + match self { + V::List(l) => V::List(l.deep_clone()), + V::Table(t) => V::Table(t.deep_clone()), + V::Matrix(m) => V::Matrix(m.deep_clone()), + _ => self.clone() + } + } + + fn shallow_clone(&self) -> Self { + use Value as V; + match self { + V::List(l) => V::List(l.shallow_clone()), + V::Table(t) => V::Table(t.shallow_clone()), + V::Matrix(m) => V::Matrix(m.shallow_clone()), + _ => self.clone() + } + } +} + +impl ValueClone for Vec { + fn deep_clone(&self) -> Self { + let mut vals = Vec::new(); + for val in self { + vals.push(val.deep_clone()) + } + vals + } + + fn shallow_clone(&self) -> Self { + self.clone() + } +} + +impl ValueClone for Matrix { + fn deep_clone(&self) -> Self { + let values = self.values.deep_clone(); + Self::new(self.domain, self.codomain, values) + } + + fn shallow_clone(&self) -> Self { + let values = self.values.shallow_clone(); + Self::new(self.domain, self.codomain, values) + } +} diff --git a/matrix-lang/src/value/comp.rs b/matrix-lang/src/value/comp.rs new file mode 100644 index 0000000..3557927 --- /dev/null +++ b/matrix-lang/src/value/comp.rs @@ -0,0 +1,373 @@ +use std::{ops::{Add, Sub, Mul, Div, Shl, Shr, BitOr, BitAnd, BitXor, Neg, Not}, cmp::Ordering}; +use crate::prelude::*; + +fn ratio_to_f64(r: Rational64) -> f64 { + *r.numer() as f64 / *r.denom() as f64 +} + +macro_rules! error { + ($($arg:tt)*) => { + exception!(VALUE_EXCEPTION, $($arg)*) + }; +} + +/// +/// MATH OPERATIONS +/// + +fn ipow(n: i64, d: i64, p: i64) -> Result<(i64, i64)> { + Ok(match (n, d, p) { + (0, _, 0) => Err(error!("cannot exponent 0 ** 0"))?, + (0, _, _) => (0, 1), + (_, _, 0) => (1, 1), + (1, 1, _) => (1, 1), + (n, d, p) if p < 0 => (d.pow((-p) as u32), n.pow((-p) as u32)), + (n, d, p) => (n.pow(p as u32), d.pow(p as u32)), + }) +} + +fn promote(a: Value, b: Value) -> (Value, Value) { + use Value as V; + match (&a, &b) { + (V::Int(x), V::Ratio(..)) => (V::Ratio((*x).into()), b), + (V::Int(x), V::Float(..)) => (V::Float(*x as f64), b), + (V::Int(x), V::Complex(..)) => (V::Complex((*x as f64).into()), b), + (V::Ratio(x), V::Float(..)) => (V::Float(ratio_to_f64(*x)), b), + (V::Ratio(x), V::Complex(..)) => (V::Complex(ratio_to_f64(*x).into()), b), + (V::Float(x), V::Complex(..)) => (V::Complex((*x).into()), b), + (V::Ratio(..), V::Int(y)) => (a, V::Ratio((*y).into())), + (V::Float(..), V::Int(y)) => (a, V::Float(*y as f64)), + (V::Complex(..), V::Int(y)) => (a, V::Complex((*y as f64).into())), + (V::Float(..), V::Ratio(y)) => (a, V::Float(ratio_to_f64(*y))), + (V::Complex(..), V::Ratio(y)) => (a, V::Complex(ratio_to_f64(*y).into())), + (V::Complex(..), V::Float(y)) => (a, V::Complex((*y).into())), + (V::List(l1), V::List(l2)) if l1.len() > 0 && l2.len() > 0 + => (V::Matrix(Matrix::from_list(l1.to_vec()).into()), V::Matrix(Matrix::from_list(l2.to_vec()).into())), + (_, V::List(l)) if l.len() > 0 + => (a, V::Matrix(Matrix::from_list(l.to_vec()).into())), + (V::List(l), _) if l.len() > 0 + => (V::Matrix(Matrix::from_list(l.to_vec()).into()), b), + _ => (a, b), + } +} + + +impl Add for Value { + type Output = Result; + fn add(self, rhs: Self) -> Self::Output { + use Value::*; + match promote(self, rhs) { + (Int(x), Int(y)) => Ok(Int(x + y)), + (Float(x), Float(y)) => Ok(Float(x + y)), + (Ratio(x), Ratio(y)) => Ok(Ratio(x + y)), + (Complex(x), Complex(y)) => Ok(Complex(x + y)), + (Matrix(x), Matrix(y)) => Ok(Matrix((x + y)?.into())), + (Matrix(x), r) => Ok(Matrix(x.increment(r)?.into())), + (l, Matrix(y)) => Ok(Matrix(y.increment(l)?.into())), + (String(str), value) => Ok(String(Rc::from( + format!("{str}{value}") + ))), + (value, String(str)) => Ok(String(Rc::from( + format!("{value}{str}") + ))), + (List(mut l1), List(l2)) => { + l1.extend_from_slice(&l2); + Ok(List(l1)) + }, + (l, r) => Err(error!("cannot add {l:?} + {r:?}")) + } + } +} + +impl Sub for Value { + type Output = Result; + fn sub(self, rhs: Self) -> Self::Output { + use Value::*; + match promote(self, rhs) { + (Int(x), Int(y)) => Ok(Int(x - y)), + (Float(x), Float(y)) => Ok(Float(x - y)), + (Ratio(x), Ratio(y)) => Ok(Ratio(x - y)), + (Complex(x), Complex(y)) => Ok(Complex(x - y)), + (Matrix(x), Matrix(y)) => Ok(Matrix((x - y)?.into())), + (Matrix(x), r) => Ok(Matrix(x.decrement(r)?.into())), + (l, r) => Err(error!("cannot subtract {l:?} - {r:?}")) + } + } +} + +impl Mul for Value { + type Output = Result; + fn mul(self, rhs: Value) -> Self::Output { + use Value::*; + match promote(self, rhs) { + (Int(x), Int(y)) => Ok(Int(x * y)), + (Float(x), Float(y)) => Ok(Float(x * y)), + (Ratio(x), Ratio(y)) => Ok(Ratio(x * y)), + (Complex(x), Complex(y)) => Ok(Complex(x * y)), + (Matrix(x), Matrix(y)) => Ok(Matrix((x * y)?.into())), + (Matrix(x), r) => Ok(Matrix(x.scale(r)?.into())), + (l, Matrix(y)) => Ok(Matrix(y.scale(l)?.into())), + (l, r) => Err(error!("cannot multiply {l:?} * {r:?}")) + } + } +} + +impl Div for Value { + type Output = Result; + fn div(self, rhs: Value) -> Self::Output { + use Value::*; + match promote(self, rhs) { + (Int(_), Int(0)) => Err(error!("cannot divide by zero")), + (Int(x), Int(y)) => Ok(Ratio(Rational64::new(x, y))), + (Float(x), Float(y)) => Ok(Float(x / y)), + (Ratio(x), Ratio(y)) => Ok(Ratio(x / y)), + (Complex(x), Complex(y)) => Ok(Complex(x / y)), + (l, r) => Err(error!("cannot divide {l:?} / {r:?}")) + } + } +} + +impl BitOr for Value { + type Output = Result; + fn bitor(self, rhs: Value) -> Self::Output { + use Value::*; + match promote(self, rhs) { + (Int(x), Int(y)) => Ok(Int(x | y)), + (l, r) => Err(error!("cannot bitwise or {l:?} | {r:?}")) + } + } +} + +impl BitAnd for Value { + type Output = Result; + fn bitand(self, rhs: Value) -> Self::Output { + use Value::*; + match promote(self, rhs) { + (Int(x), Int(y)) => Ok(Int(x & y)), + (l, r) => Err(error!("cannot bitwise and {l:?} & {r:?}")) + } + } +} + +impl BitXor for Value { + type Output = Result; + fn bitxor(self, rhs: Value) -> Self::Output { + use Value::*; + match promote(self, rhs) { + (Int(x), Int(y)) => Ok(Int(x ^ y)), + (l, r) => Err(error!("cannot bitwise xor {l:?} ^ {r:?}")) + } + } +} + +impl Shl for Value { + type Output = Result; + fn shl(self, rhs: Value) -> Self::Output { + use Value::*; + match promote(self, rhs) { + (Int(x), Int(y)) => Ok(Int(x << y)), + (l, r) => Err(error!("cannot bitwise shift left {l:?} << {r:?}")) + } + } +} + +impl Shr for Value { + type Output = Result; + fn shr(self, rhs: Value) -> Self::Output { + use Value::*; + match promote(self, rhs) { + (Int(x), Int(y)) => Ok(Int(x >> y)), + (l, r) => Err(error!("cannot bitwise shift right {l:?} >> {r:?}")) + } + } +} + +impl PartialEq for Value { + fn eq(&self, other: &Self) -> bool { + use Value::*; + match (self, other) { + (Nil, Nil) => true, + (Bool(a), Bool(b)) => a == b, + (Int(a), Int(b)) => *a == *b, + (Ratio(a), Ratio(b)) => *a == *b, + (Float(a), Float(b)) => *a == *b, + (Complex(a), Complex(b)) => *a == *b, + (Int(a), Ratio(b)) => Rational64::from(*a) == *b, + (Ratio(a), Int(b)) => *a == Rational64::from(*b), + (Int(a), Float(b)) => *a as f64 == *b, + (Float(a), Int(b)) => *a == *b as f64, + (Int(a), Complex(b)) => Complex64::from(*a as f64) == *b, + (Complex(a), Int(b)) => *a == Complex64::from(*b as f64), + (Ratio(a), Float(b)) => ratio_to_f64(*a) == *b, + (Float(a), Ratio(b)) => *a == ratio_to_f64(*b), + (Ratio(a), Complex(b)) => Complex64::from(ratio_to_f64(*a)) == *b, + (Complex(a), Ratio(b)) => *a == Complex64::from(ratio_to_f64(*b)), + (Float(a), Complex(b)) => Complex64::from(*a) == *b, + (Complex(a), Float(b)) => *a == Complex64::from(*b), + (String(a), String(b)) => *a == *b, + (List(a), List(b)) => *a == *b, + (Matrix(a), Matrix(b)) => a == b, + _ => false, + } + } +} + +impl PartialOrd for Value { + fn partial_cmp(&self, other: &Self) -> Option { + use Value::*; + match (self, other) { + (Nil, Nil) => Some(Ordering::Equal), + (Bool(a), Bool(b)) => a.partial_cmp(b), + (Int(a), Int(b)) => a.partial_cmp(b), + (Ratio(a), Ratio(b)) => a.partial_cmp(b), + (Float(a), Float(b)) => a.partial_cmp(b), + (Int(a), Ratio(b)) => Rational64::from(*a).partial_cmp(b), + (Ratio(a), Int(b)) => a.partial_cmp(&Rational64::from(*b)), + (Int(a), Float(b)) => (*a as f64).partial_cmp(b), + (Float(a), Int(b)) => a.partial_cmp(&(*b as f64)), + (Ratio(a), Float(b)) => ratio_to_f64(*a).partial_cmp(b), + (Float(a), Ratio(b)) => a.partial_cmp(&ratio_to_f64(*b)), + (String(a), String(b)) => a.partial_cmp(b), + (List(a), List(b)) => a.partial_cmp(b), + (Matrix(a), Matrix(b)) => a.values.partial_cmp(&b.values), + _ => None, + } + } +} + +impl Neg for Value { + type Output = Value; + + fn neg(self) -> Self::Output { + use Value::*; + match self { + Bool(b) => Bool(!b), + Int(i) => Int(-i), + Float(f) => Float(-f), + Ratio(r) => Ratio(-r), + Complex(c) => Complex(-c), + _ => return Float(f64::NAN) + } + } +} + +impl Not for Value { + type Output = bool; + + fn not(self) -> Self::Output { + use Value as V; + match self { + V::Nil => true, + V::Bool(b) => !b, + V::Int(i) => i == 0, + V::Float(f) => f == 0.0, + V::Ratio(r) => *(r.numer()) == 0 || *(r.denom()) == 0, + V::Complex(c) => !c.is_normal(), + V::Regex(_) => false, + V::List(_) => false, + V::Matrix(_) => false, + V::Table(_) => false, + V::String(s) => s.as_ref() == "", + V::Function(_) => false, + V::Iter(_) => false, + V::Range(_) => false, + V::File(_) => false, + V::Exception(_) => false, + } + } +} + + +impl Value { + + pub fn modulo(self, rhs: Value) -> Result { + use Value::*; + match promote(self, rhs) { + (Int(x), Int(y)) => Ok(Int(x % y)), + (Float(x), Float(y)) => Ok(Float(x % y)), + (Ratio(x), Ratio(y)) => Ok(Ratio(x % y)), + (Complex(x), Complex(y)) => Ok(Complex(x % y)), + (l, r) => Err(error!("cannot modulo: {l:?} % {r:?}")) + } + } + + pub fn pow(self, rhs: Value) -> Result { + use Value::*; + if let (Ratio(x), Int(y)) = (&self, &rhs) { + return Ok(Ratio(ipow(*(*x).numer(), *(*x).denom(), *y)?.into())); + } + match promote(self, rhs) { + (Int(x), Int(y)) => Ok(Ratio(ipow(x, 1, y)?.into())), + (Float(x), Float(y)) => Ok(Float(x.powf(y))), + (Ratio(x), Ratio(y)) => Ok(Float(ratio_to_f64(x).powf(ratio_to_f64(y)))), + (Complex(x), Complex(y)) => Ok(Complex(x.powc(y))), + (l, r) => Err(error!("cannot exponent: {l:?} ** {r:?}")) + } + } + + pub fn floaty(self) -> Self { + use Value as V; + match self { + V::Int(i) => V::Float(i as f64), + V::Ratio(r) => V::Float(ratio_to_f64(r)), + a => a + } + } + + pub fn is_zero(&self) -> bool { + use Value as V; + match self { + V::Int(i) => *i == 0, + V::Float(f) => *f == 0.0 || *f == -0.0, + V::Ratio(r) => *r.numer() == 0, + _ => false, + } + } + + pub fn binary_op(op: BinaryOp, lhs: Value, rhs: Value) -> Result { + use BinaryOp::*; + match op { + Add => lhs + rhs, + Subtract => lhs - rhs, + Multiply => lhs * rhs, + Divide => lhs / rhs, + Modulo => lhs.modulo(rhs), + Power => lhs.pow(rhs), + BitwiseAnd => lhs & rhs, + BitwiseOr => lhs | rhs, + BitwiseXor => lhs ^ rhs, + BitwiseShiftLeft => lhs << rhs, + BitwiseShiftRight => lhs >> rhs, + Equals => Ok(Self::Bool(lhs == rhs)), + NotEquals => Ok(Self::Bool(lhs != rhs)), + GreaterEquals => Ok(Self::Bool(lhs >= rhs)), + LessEquals => Ok(Self::Bool(lhs <= rhs)), + GreaterThan => Ok(Self::Bool(lhs > rhs)), + LessThan => Ok(Self::Bool(lhs < rhs)), + Range | RangeEq => { + let Value::Int(lhs) = lhs else { + return Err(error!("range can only take [Int]'s")) + }; + let Value::Int(rhs) = rhs else { + return Err(error!("range can only take [Int]'s")) + }; + Ok(Self::Range(Rc::new((lhs, rhs, op == RangeEq)))) + }, + } + } + + pub fn unary_op(op: UnaryOp, val: Value) -> Value { + use UnaryOp::*; + match op { + Negate => -val, + Not => Self::Bool(!val), + } + } + + pub fn to_regex(value: &str) -> Result { + match Regex::new(value) { + Ok(r) => Ok(Self::Regex(r.into())), + Err(e) => Err(error!("{e}")), + } + } +} diff --git a/matrix-lang/src/value/exception.rs b/matrix-lang/src/value/exception.rs new file mode 100644 index 0000000..0df6f5c --- /dev/null +++ b/matrix-lang/src/value/exception.rs @@ -0,0 +1,78 @@ +use std::{fmt::{Debug, Display}, error::Error}; +use crate::prelude::*; + +#[macro_export] +macro_rules! exception { + ($type:expr) => { + $crate::prelude::Exception::new($type) + }; + ($type:expr, $($arg:tt)*) => { + $crate::prelude::Exception::msg($type, format!($($arg)*).as_str()) + }; +} + +pub const HASH_EXCEPTION: &'static str = "hash"; +pub const VALUE_EXCEPTION: &'static str = "value"; +pub const PARSE_EXCEPTION: &'static str = "parse"; +pub const COMPILE_EXCEPTION: &'static str = "compile"; +pub const RUNTIME_EXCEPTION: &'static str = "runtime"; +pub const BINARY_EXECPTION: &'static str = "binary"; +pub const IO_EXECPTION: &'static str = "io"; + +#[derive(Clone)] +pub struct Exception(Gc); + +#[derive(Clone)] +struct ExceptionInner { + ty: Rc, + msg: Rc, + trace: Vec<(Rc, Position)> +} + +impl Exception { + pub fn msg(ty: &str, msg: &str) -> Self { + Self(Gc::new(ExceptionInner { ty: ty.into(), msg: msg.into(), trace: Vec::new() })) + } + + pub fn pos(mut self, pos: Position) -> Self { + self.0.trace.push(( + Rc::from(""), + pos + )); + self + } + + pub fn trace(mut self, block: Rc, pos: Position) -> Self { + self.0.trace.push(( + block, + pos + )); + self + } +} + +impl Display for Exception { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let ty = self.0.ty.as_ref(); + let msg = self.0.msg.as_ref(); + write!(f, "{}\n Type <{}>", msg, ty)?; + for (block, pos) in self.0.trace.iter() { + write!(f, "\n In {block} at {pos}\n")?; + } + Ok(()) + } +} + +impl Debug for Exception { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "[Exception {}]", self.0.ty.as_ref()) + } +} + +impl Error for Exception {} + +impl From for Result { + fn from(value: Exception) -> Self { + Err(value) + } +} diff --git a/matrix-lang/src/value/fmt.rs b/matrix-lang/src/value/fmt.rs new file mode 100644 index 0000000..f276bf1 --- /dev/null +++ b/matrix-lang/src/value/fmt.rs @@ -0,0 +1,186 @@ +use std::fmt::{Debug, Display}; +use crate::prelude::*; + +use Value as V; + +impl Debug for Value { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + V::Nil => write!(f, "[Nil]"), + V::Int(i) => write!(f, "[Int {i}]"), + V::Bool(b) => write!(f, "[Bool {b}]"), + V::Float(vf) => write!(f, "[Float {vf}]"), + V::Ratio(r) => write!(f, "[Ratio {r}]"), + V::Complex(c) => write!(f, "[Complex {c}]"), + V::Regex(r) => write!(f, "[Regex /{r}/]"), + V::String(s) => write!(f, "[String '{s}']"), + V::List(l) => write!(f, "[List {}]", l.len()), + V::Matrix(m) => write!(f, "[Matirx {}x{}]", m.domain, m.codomain), + V::Table(t) => write!(f, "[Table {}]", t.len()), + V::Function(vf) => write!(f, "[Function {}]", vf.name), + V::Range(r) => write!(f, "[Range {:?}..{:?}]", r.0, r.1), + V::Iter(_) => write!(f, "[Iterator]"), + V::Exception(_) => write!(f, "[Error]"), + V::File(_) => write!(f, "[File]"), + } + } +} + +impl Display for Value { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let red; + let green; + let yellow; + let blue; + let pink; + let cyan; + let clear; + + if f.alternate() { + red = "\x1b[31m"; + green = "\x1b[32m"; + yellow = "\x1b[33m"; + blue = "\x1b[34m"; + pink = "\x1b[35m"; + cyan = "\x1b[36m"; + clear = "\x1b[0m"; + } else { + red = ""; + green = ""; + yellow = ""; + blue = ""; + pink = ""; + cyan = ""; + clear = ""; + } + + match self { + V::Nil => {write!(f, "{blue}nil{clear}")?;}, + V::Bool(b) => {write!(f, "{yellow}{b}{clear}")?;}, + V::Int(i) => {write!(f, "{yellow}{i}{clear}")?;}, + V::Float(l) => {write!(f, "{yellow}{l}{clear}")?;}, + V::Ratio(r) => {write!(f, "{yellow}{r}{clear}")?;}, + V::Complex(c) => {write!(f, "{yellow}{c}{clear}")?;}, + V::Regex(r) => {write!(f, "/{red}{r}{clear}/")?;}, + V::String(s) => { + if f.alternate() { + write!(f, "{green}'{s}'{clear}")?; + } else { + write!(f, "{s}")?; + } + } + V::List(l) => { + if l.len() < 1 { + write!(f, "[]")?; + return Ok(()) + } + write!(f, "[ ")?; + for (i, el) in l.iter().enumerate() { + if i != 0 { + write!(f, " ")?; + } + if f.alternate() { + write!(f, "{el:#}")?; + } else { + write!(f, "{el}")?; + } + } + write!(f, " ]")?; + }, + V::Matrix(m) => { + if f.alternate() { + write!(f, "{m:#}")?; + } else { + write!(f, "{m}")?; + } + }, + V::Table(t) => { + if f.alternate() { + write!(f, "{t:#}")?; + } else { + write!(f, "{t}")?; + } + }, + V::Function(fun) => { + write!(f, "{cyan}{fun}{clear}")?; + } + V::Range(r) => { + if f.alternate() { + write!(f, "{:#}..{:#}", r.0, r.1)?; + } else { + write!(f, "{}..{}", r.0, r.1)?; + } + } + V::Iter(_) => {write!(f, "{pink}[Iterator]{clear}")?;}, + V::File(_) => {write!(f, "{pink}[File]{clear}")?;}, + V::Exception(e) => {write!(f, "{red}{e}{clear}")?;}, + }; + Ok(()) + } +} + +impl Debug for Matrix { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "[Matrix {}x{}]", self.domain, self.codomain) + } +} + +impl Display for Matrix { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut max_cols = vec![0; self.domain]; + let mut vals: Vec = Vec::with_capacity(self.domain * self.codomain); + for row in 0..self.codomain { + for col in 0..self.domain { + let idx = col + row * self.domain; + let el = &self.values[idx]; + let s = match f.alternate() { + true => format!("{:#}", el), + false => format!("{}", el) + }; + max_cols[col] = max_cols[col].max(s.len()); + vals.push(s); + } + } + + write!(f, "\n")?; + for row in 0..self.codomain { + for col in 0..self.domain { + let idx = col + row * self.domain; + let s = vals[idx].as_str(); + let width = max_cols[col]; + write!(f, " {s:>width$}")?; + } + write!(f, "\n")?; + } + + Ok(()) + } +} + +impl Debug for ValueMap { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "[Table {}]", self.len()) + } +} + +impl Display for ValueMap { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if self.len() < 1 { + write!(f, "{{}}")?; + return Ok(()) + } + write!(f, "{{ ")?; + for (i, (key, value)) in self.entries().enumerate() { + if i != 0 { + write!(f, ", ")?; + } + if f.alternate() { + write!(f, "{key:#} = {value:#}")? + } else { + write!(f, "{key} = {value}")? + } + } + write!(f, " }}")?; + Ok(()) + } +} diff --git a/matrix-lang/src/value/function.rs b/matrix-lang/src/value/function.rs new file mode 100644 index 0000000..38d8b0b --- /dev/null +++ b/matrix-lang/src/value/function.rs @@ -0,0 +1,43 @@ +use crate::prelude::*; +use std::fmt::{Debug, Display}; + +pub struct Function { + pub name: Rc, + pub arity: usize, + pub variadic: bool, + pub fun: InnerFunction +} + +#[derive(Clone)] +pub enum InnerFunction { + Compiled(Rc), + Native(Rc) -> Result>), +} + +impl Debug for Function { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use InnerFunction as F; + match self.fun { + F::Compiled(_) => { + write!(f, "[Function {}]", self.name) + }, + F::Native(_) => { + write!(f, "[Builtin {}]", self.name) + } + } + } +} + +impl Display for Function { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use InnerFunction as F; + match self.fun { + F::Compiled(_) => { + write!(f, "[Function {}]", self.name) + }, + F::Native(_) => { + write!(f, "[Builtin {}]", self.name) + } + } + } +} diff --git a/matrix-lang/src/value/gc.rs b/matrix-lang/src/value/gc.rs new file mode 100644 index 0000000..5ef8b80 --- /dev/null +++ b/matrix-lang/src/value/gc.rs @@ -0,0 +1,186 @@ +use std::{ops::{Index, IndexMut, Deref, DerefMut, Add, Sub, Mul}, marker::PhantomData, ptr::NonNull, fmt::{Debug, Display}}; +use crate::prelude::*; + +pub struct Gc { + ptr: NonNull>, + phantom: PhantomData> +} + +struct GcInner { + rc: usize, + data: T +} + +impl Gc { + pub fn new(data: T) -> Self { + let boxed = Box::new(GcInner { + rc: 1, + data, + }); + Self { + ptr: NonNull::new(Box::into_raw(boxed)).unwrap(), + phantom: PhantomData + } + } +} + +impl Gc { + pub fn into_inner(self) -> T { + unsafe { + self.ptr.as_ref().data.clone() + } + } + + fn data(&self) -> T { + unsafe { + self.ptr.as_ref().data.clone() + } + } +} + +impl ValueClone for Gc { + fn deep_clone(&self) -> Self { + unsafe { + let data = self.ptr.as_ref().data.deep_clone(); + Self::new(data) + } + } + + fn shallow_clone(&self) -> Self { + unsafe { + let data = self.ptr.as_ref().data.shallow_clone(); + Self::new(data) + } + } +} + +impl From for Gc { + fn from(value: T) -> Self { + Gc::new(value) + } +} + +impl, Idx> IndexMut for Gc { + fn index_mut(&mut self, index: Idx) -> &mut Self::Output { + self.deref_mut().index_mut(index) + } +} + +impl, Idx> Index for Gc { + type Output = T::Output; + + fn index(&self, index: Idx) -> &Self::Output { + self.deref().index(index) + } +} + +impl DerefMut for Gc { + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { + &mut self.ptr.as_mut().data + } + } +} + +impl Deref for Gc { + type Target = T; + + fn deref(&self) -> &Self::Target { + unsafe { + &self.ptr.as_ref().data + } + } +} + +impl PartialEq for Gc { + fn eq(&self, other: &Self) -> bool { + self.deref().eq(other.deref()) + } +} + +impl Iterator for Gc { + type Item = T::Item; + + fn next(&mut self) -> Option { + self.deref_mut().next() + } +} + +impl Eq for Gc {} + +impl Clone for Gc { + fn clone(&self) -> Self { + unsafe { + let inner = self.ptr.as_ptr().as_mut().unwrap(); + inner.rc += 1; + + Self { + ptr: self.ptr, + phantom: PhantomData + } + } + } +} + +impl Drop for Gc { + fn drop(&mut self) { + unsafe { + let inner = self.ptr.as_mut(); + if inner.rc > 1 { + inner.rc -= 1; + } else { + let _ = Box::from_raw(self.ptr.as_ptr()); + } + } + } +} + +impl Debug for Gc { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if f.alternate() { + write!(f, "{:#?}", self.deref()) + } else { + write!(f, "{:?}", self.deref()) + } + } +} + +impl Display for Gc { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if f.alternate() { + write!(f, "{:#}", self.deref()) + } else { + write!(f, "{}", self.deref()) + } + } +} + +impl Add for Gc { + type Output = T::Output; + + fn add(self, rhs: Self) -> Self::Output { + let a = self.data(); + let b = rhs.data(); + a + b + } +} + +impl Sub for Gc { + type Output = T::Output; + + fn sub(self, rhs: Self) -> Self::Output { + let a = self.data(); + let b = rhs.data(); + a - b + } +} + +impl Mul for Gc { + type Output = T::Output; + + fn mul(self, rhs: Self) -> Self::Output { + let a = self.data(); + let b = rhs.data(); + a * b + } +} diff --git a/matrix-lang/src/value/hash.rs b/matrix-lang/src/value/hash.rs new file mode 100644 index 0000000..35c1343 --- /dev/null +++ b/matrix-lang/src/value/hash.rs @@ -0,0 +1,240 @@ +use crate::prelude::*; +use std::hash::{Hash, DefaultHasher, Hasher}; + +#[derive(Clone)] +pub struct ValueMap { + values: Vec, + size: usize, + used: usize, +} + +#[derive(Clone)] +enum ValueEntry { + Empty, + Taken((Value, Value)), + Gravestone(Value), +} + +impl Default for ValueMap { + fn default() -> Self { + ValueMap { + values: vec![ValueEntry::Empty; 8], + size: 8, + used: 0, + } + } +} + +impl ValueMap { + + fn get_index(values: &Vec, size: usize, key: &Value, hash: usize) -> usize { + let mut nearest_grave = None; + let start = hash & size; + for offset in 0..size { + let idx = (start + offset) % size; + match &values[idx] { + ValueEntry::Empty => return idx, + ValueEntry::Taken((marker, _)) => { + if marker.eq(key) { return idx }; + continue; + }, + ValueEntry::Gravestone(grave) => { + if grave.eq(key) { return idx }; + if nearest_grave.is_none() { + nearest_grave = Some(idx); + } + continue; + }, + } + } + + match nearest_grave { + Some(idx) => idx, + None => panic!("cannot get value map index: full!!!"), + } + } + + fn expand(&mut self) -> Result<()> { + let pairs = self.values.iter() + .filter_map(|e| { + match e { + ValueEntry::Taken(s) => Some(s.clone()), + _ => None + } + }) + .collect::>(); + + let new_used = pairs.len(); + let new_size = self.size * 2 + pairs.len(); + let mut new_values = vec![ValueEntry::Empty; new_size]; + + for (key, value) in pairs.into_iter() { + let hash = self.hash(&key)?; + let idx = ValueMap::get_index(&new_values, new_size, &key, hash); + new_values[idx] = ValueEntry::Taken((key, value)); + } + + self.used = new_used; + self.size = new_size; + self.values = new_values; + + Ok(()) + } + + fn hash(&self, key: &Value) -> Result { + let mut hasher = DefaultHasher::new(); + key.try_hash(&mut hasher)?; + Ok(hasher.finish() as usize) + } + + pub fn get<'a>(&'a self, key: &Value) -> Result> { + let hash = self.hash(key)?; + let idx = ValueMap::get_index(&self.values, self.size, key, hash); + match &self.values[idx] { + ValueEntry::Taken((_, value)) => Ok(Some(value)), + _ => Ok(None) + } + } + + pub fn insert(&mut self, key: Value, value: Value) -> Result<()> { + if self.used * 3 >= self.size * 2 { + self.expand()?; + } + let key = key.deep_clone(); + let hash = self.hash(&key)?; + let idx = ValueMap::get_index(&self.values, self.size, &key, hash); + self.values[idx] = ValueEntry::Taken((key, value)); + self.used += 1; + Ok(()) + } + + pub fn remove(&mut self, key: &Value) -> Result> { + let hash = self.hash(key)?; + let idx = ValueMap::get_index(&self.values, self.size, key, hash); + let mut value = ValueEntry::Gravestone(key.clone()); + std::mem::swap(&mut value, &mut self.values[idx]); + match value { + ValueEntry::Taken((_, v)) => { + self.used -= 1; + Ok(Some(v)) + } + _ => Ok(None), + } + } + + pub fn entries<'a>(&'a self) -> ValueMapIterator<'a> { + ValueMapIterator::new(self) + } + + pub fn new() -> Self { + Self::default() + } + + pub fn len(&self) -> usize { + self.used + } +} + +pub struct ValueMapIterator<'a> { + map: &'a ValueMap, + index: usize +} + +impl<'a> ValueMapIterator<'a> { + fn new(map: &'a ValueMap) -> Self { + Self { map, index: 0 } + } +} + +impl<'a> Iterator for ValueMapIterator<'a> { + type Item = (&'a Value, &'a Value); + + fn next(&mut self) -> Option { + let mut idx = self.index; + loop { + if idx >= self.map.size { + return None + } + use ValueEntry as E; + let E::Taken((k, v)) = &self.map.values[idx] else { + idx += 1; + continue; + }; + + self.index = idx + 1; + return Some((k, v)) + } + } +} + +pub trait TryHash { + fn try_hash(&self, state: &mut H) -> Result<()>; +} + +impl TryHash for Value { + fn try_hash(&self, state: &mut H) -> Result<()> { + use Value::*; + match self { + Nil => 0x23845.hash(state), + Bool(b) => b.hash(state), + Int(i) => i.hash(state), + Ratio(r) => r.hash(state), + Regex(r) => r.as_str().hash(state), + String(s) => s.hash(state), + List(l) => { + for val in l.iter() { + val.try_hash(state)?; + } + } + Matrix(m) => { + m.domain.hash(state); + m.codomain.hash(state); + for val in m.values.iter() { + val.try_hash(state)?; + } + }, + _ => return Err(exception!(HASH_EXCEPTION, "cannot hash {self:?}")) + }; + Ok(()) + } +} + +impl ValueClone for ValueEntry { + fn deep_clone(&self) -> Self { + use ValueEntry as E; + match self { + E::Empty => E::Empty, + E::Taken((k, v)) => E::Taken((k.deep_clone(), v.deep_clone())), + E::Gravestone(g) => E::Gravestone(g.deep_clone()), + } + } + + fn shallow_clone(&self) -> Self { + use ValueEntry as E; + match self { + E::Empty => E::Empty, + E::Taken((k, v)) => E::Taken((k.clone(), v.clone())), + E::Gravestone(g) => E::Gravestone(g.clone()), + } + } +} + +impl ValueClone for ValueMap { + fn deep_clone(&self) -> Self { + let values = self.values.iter().map(|e| e.deep_clone()).collect(); + Self { + values, + size: self.size, + used: self.used + } + } + + fn shallow_clone(&self) -> Self { + let values = self.values.iter().map(|e| e.clone()).collect(); + Self { + values, + size: self.size, + used: self.used + } + } +} diff --git a/matrix-lang/src/value/index.rs b/matrix-lang/src/value/index.rs new file mode 100644 index 0000000..a5725e8 --- /dev/null +++ b/matrix-lang/src/value/index.rs @@ -0,0 +1,230 @@ +use std::cmp::Ordering; + +use crate::prelude::*; + +macro_rules! error { + ($($arg:tt)*) => { + Err(exception!(VALUE_EXCEPTION, $($arg)*)) + }; +} + +impl Value { + pub fn val_cmp(&self, other: &Self) -> Result { + self.partial_cmp(other) + .map_or_else(|| error!("cannot compare: {self:?} and {other:?}"), Ok) + } + + fn index_single(&self, index: &Value) -> Result { + use Value as V; + match (self, index) { + (V::Table(t), index) => { + Ok(t.get(index)?.unwrap_or(&Value::Nil).clone()) + }, + (V::List(l), V::Int(i)) => { + if *i < 0 || *i as usize >= l.len() { + return error!("{i} out of bounds for {self:?}") + } + Ok(l[*i as usize].clone()) + }, + (V::Matrix(m), V::Int(i)) => { + if *i < 0 || *i as usize >= m.values.len() { + return error!("{i} out of bounds for {self:?}") + } + Ok(m.values[*i as usize].clone()) + }, + _ => return error!("{index:?} cant index {self:?}") + } + } + + fn index_multiple(&self, indexes: &Vec) -> Result { + use Value as V; + match self { + V::List(..) => { + let mut ret = Vec::new(); + for index in indexes { + let res = self.index_single(index)?; + ret.push(res); + } + Ok(V::List(ret.into())) + } + V::Table(..) => { + let mut ret = ValueMap::new(); + for index in indexes { + let res = self.index_single(index)?; + ret.insert(index.clone(), res)?; + } + Ok(V::Table(ret.into())) + } + V::Matrix(m) => { + let err = || error!("{self:?} can be index by [Int] or [Int;Int]"); + if indexes.len() != 2 { + return err() + } + let lhs = indexes[0].clone(); + let rhs = indexes[1].clone(); + match (lhs, rhs) { + (V::Nil, V::Nil) => { + Ok(V::Matrix(m.shallow_clone())) + }, + (V::Int(row), V::Nil) => { + let Some((_, row)) = m.rows().enumerate().filter(|(idx, _)| *idx as i64 == row).next() else { + return err(); + }; + let row: Vec = row.into_iter().map(|e| e.clone()).collect(); + Ok(V::Matrix(Matrix::new(row.len(), 1, row).into())) + }, + (V::Nil, V::Int(col)) => { + let Some((_, col)) = m.cols().enumerate().filter(|(idx, _)| *idx as i64 == col).next() else { + return err(); + }; + let col: Vec = col.into_iter().map(|e| e.clone()).collect(); + Ok(V::Matrix(Matrix::new(1, col.len(), col).into())) + }, + (V::Int(row), V::Int(col)) => { + if row < 0 || col < 0 { + return err(); + } + m.get(row as usize, col as usize) + } + _ => return err() + } + } + _ => return error!("cannot index {self:?}") + } + } + + pub fn index(&self, index: &Vec) -> Result { + if index.len() == 0 { + Ok(self.shallow_clone()) + } else if index.len() == 1 { + self.index_single(&index[0]) + } else { + self.index_multiple(index) + } + } + + fn store_index_single(&mut self, index: &Value, store: Value) -> Result<()> { + use Value as V; + let err = format!("{self:?}"); + match (self, index) { + (V::Table(t), index) => { + t.insert(index.clone(), store) + }, + (V::List(l), V::Int(i)) => { + if *i < 0 || *i as usize >= l.len() { + return error!("{i} out of bounds for {err}") + } + l[*i as usize] = store; + Ok(()) + }, + (V::Matrix(m), V::Int(i)) => { + if *i < 0 || *i as usize >= m.values.len() { + return error!("{i} out of bounds for {err}") + } + m.values[*i as usize] = store; + Ok(()) + }, + _ => return error!("{index:?} cant index {err}") + } + } + + fn store_index_multiple(&mut self, indexes: &Vec, store: Value) -> Result<()> { + use Value as V; + match self { + V::List(..) => { + for index in indexes { + self.store_index_single(index, store.clone())?; + } + Ok(()) + } + V::Table(..) => { + for index in indexes { + self.store_index_single(index, store.clone())?; + } + Ok(()) + } + _ => return error!("cannot index {self:?}") + } + } + + pub fn store_index(&mut self, index: &Vec, store: Value) -> Result<()> { + if index.len() == 0 { + Ok(()) + } else if index.len() == 1 { + self.store_index_single(&index[0], store) + } else { + self.store_index_multiple(index, store) + } + } + + pub fn store_field_access(&mut self, ident: &str, val: Value) -> Result<()> { + use Value as V; + match self { + V::Table(t) => { + let key = V::String(Rc::from(ident)); + Ok(t.insert(key, val)?) + }, + _ => return error!("cannot field access assign {self:?}") + } + } + + pub fn field_access(&self, ident: &str) -> Result { + use Value as V; + match self { + V::Table(t) => { + let key = V::String(Rc::from(ident)); + Ok(t.get(&key)?.unwrap_or(&V::Nil).clone()) + }, + _ => return error!("cannot field access {self:?}") + } + } + +} + +impl Value { + + pub fn into_iter_fn(self) -> Result> { + let Value::Iter(iter) = self.into_iter()? else { + return error!("bypassed iter check") + }; + Ok(iter) + } + + pub fn into_iter(self) -> Result { + use Value as V; + Ok(match self { + V::Iter(..) => self, + V::List(l) => { + let iter = RefCell::new(l.into_inner().into_iter()); + iter!(move |_,_| { + match iter.borrow_mut().next() { + Some(v) => Ok(v), + None => Ok(V::Nil), + } + }) + }, + V::Range(r) => { + let r = (*r).clone(); + let lhs = RefCell::new(r.0); + let rhs = r.1; + iter!(move |_,_| { + let val = *lhs.borrow(); + let next = *lhs.borrow() + 1; + if (!r.2 && *lhs.borrow() < rhs) || (r.2 && *lhs.borrow() <= rhs) { + *lhs.borrow_mut() = next; + return Ok(Value::Int(val)) + } + Ok(Value::Nil) + }) + }, + V::Function(f) => { + if f.arity > 0 || f.variadic { + return error!("iterator functions cannot be varadic or take arguments") + } + V::Iter(f) + }, + val => return error!("cannot turn {val:?} into an iterator") + }) + } +} + diff --git a/matrix-lang/src/value/matrix.rs b/matrix-lang/src/value/matrix.rs new file mode 100644 index 0000000..91e3ec2 --- /dev/null +++ b/matrix-lang/src/value/matrix.rs @@ -0,0 +1,337 @@ +use std::ops::{Add, Sub, Mul}; + +use crate::prelude::*; + +#[derive(Clone)] +pub struct Matrix { + pub domain: usize, + pub codomain: usize, + pub values: Vec +} + +macro_rules! error { + ($($arg:tt)*) => { + exception!(VALUE_EXCEPTION, $($arg)*) + }; +} + +impl Matrix { + pub fn new( + domain: usize, + codomain: usize, + values: Vec + ) -> Self { + Self { + domain, + codomain, + values + } + } + + pub fn from_list( + values: Vec + ) -> Self { + Self { + domain: values.len(), + codomain: 1, + values + } + } + + pub fn empty( + domain: usize, + codomain: usize + ) -> Self { + let values = (0..(domain * codomain)).into_iter() + .map(|_| Value::Int(0)) + .collect(); + Self { + domain, + codomain, + values + } + } + + pub fn get(&self, row: usize, col: usize) -> Result { + if row >= self.codomain || col >= self.domain { + return Err(error!("[{};{}] out of bounds for [Matrix {}x{}]", row, col, self.domain, self.codomain)) + } + let idx = col + row * self.domain; + Ok(self.values[idx].clone()) + } + + + + pub fn set(&mut self, row: usize, col: usize, val: Value) -> Result<()> { + if row >= self.codomain || col >= self.domain { + return Err(error!("[{};{}] out of bounds for [Matrix {}x{}]", row, col, self.domain, self.codomain)) + } + let idx = col + row * self.domain; + self.values[idx] = val; + Ok(()) + } + + pub fn rows<'a>(&'a self) -> MatrixRows<'a> { + MatrixRows { + matrix: self, + row: 0 + } + } + + pub fn cols<'a>(&'a self) -> MatrixCols<'a> { + MatrixCols { + matrix: self, + col: 0 + } + } + + // SPLCIE DOMAIN + pub fn splice_cols(&self, col_start: usize, col_end: usize) -> Result { + if col_start <= col_end || col_end > self.domain { + return Err(error!("[_;{}..{}] invalid for [Matrix {}x{}]", col_start, col_end, self.domain, self.codomain)) + } + + let mut cols = Vec::new(); + + for (i, col) in self.cols().enumerate() { + if i >= col_start && i < col_end { + cols.push(col); + } + } + + let domain = cols.len(); + let codomain = cols[0].len(); + let mut res = Self::empty(domain, codomain); + + for i in 0..domain { + for j in 0..codomain { + res.set(j, i, cols[i][j].clone())?; + } + } + + Ok(res) + } + + // SPLICE CODOMAIN + pub fn splice_rows(&self, row_start: usize, row_end: usize) -> Result { + if row_start <= row_end || row_end > self.codomain { + return Err(error!("[{}..{};_] invalid for [Matrix {}x{}]", row_start, row_end, self.domain, self.codomain)) + } + + let mut rows = Vec::new(); + + for (i, row) in self.rows().enumerate() { + if i >= row_start && i < row_end { + rows.push(row); + } + } + + let domain = rows[0].len(); + let codomain = rows.len(); + let mut res = Self::empty(domain, codomain); + + for i in 0..domain { + for j in 0..codomain { + res.set(j, i, rows[j][i].clone())?; + } + } + + Ok(res) + } + + pub fn increment(&self, increment: Value) -> Result { + let values = self.values.iter() + .map(|v| v.clone() + increment.clone()) + .collect::>>()?; + Ok(Matrix::new(self.domain, self.codomain, values)) + } + + pub fn decrement(&self, decrement: Value) -> Result { + let values = self.values.iter() + .map(|v| v.clone() - decrement.clone()) + .collect::>>()?; + Ok(Matrix::new(self.domain, self.codomain, values)) + } + + pub fn scale(&self, scale: Value) -> Result { + let values = self.values.iter() + .map(|v| v.clone() * scale.clone()) + .collect::>>()?; + Ok(Matrix::new(self.domain, self.codomain, values)) + } + + pub fn join_right(&self, other: &Matrix) -> Result { + if self.codomain != other.codomain { + return Err(error!("matrix codomain's do not match")) + } + let mut r1 = self.rows(); + let mut r2 = other.rows(); + let mut rows = Vec::new(); + loop { + let Some(r1) = r1.next() else { break; }; + let Some(r2) = r2.next() else { break; }; + + let mut row = r1 + .into_iter() + .map(|v| v.clone()) + .collect::>(); + + row.extend(r2.into_iter().map(|v| v.clone())); + + rows.push(row); + } + + let values = rows + .into_iter() + .reduce(|mut a,b| {a.extend(b); a}) + .unwrap(); + + Ok(Matrix::new(self.domain + other.domain, self.codomain, values)) + } + + pub fn join_bottom(&self, other: &Matrix) -> Result { + if self.domain != other.domain { + return Err(error!("matrix domain's do not match")) + } + let mut values = self.values.clone(); + values.extend(other.values.clone()); + Ok(Matrix::new(self.domain, self.codomain + other.codomain, values)) + } +} + +impl PartialEq for Matrix { + fn eq(&self, other: &Self) -> bool { + if self.domain != other.domain || self.codomain != other.codomain { + return false + } + + for i in 0..self.values.len() { + if self.values[i] != other.values[i] { + return false; + } + } + + return true; + } +} + +impl Add for Matrix { + type Output = Result; + + fn add(self, rhs: Self) -> Self::Output { + if self.domain != rhs.domain || self.codomain != rhs.codomain { + return Err(error!("cannot add {self:?} + {rhs:?}")) + } + let mut res = Matrix::empty(self.domain, self.codomain); + for col in 0..self.domain { + for row in 0..self.codomain { + let add = self.get(row, col)? + rhs.get(row, col)?; + res.set(row, col, add?)?; + } + } + Ok(res) + } +} + +impl Sub for Matrix { + type Output = Result; + + fn sub(self, rhs: Self) -> Self::Output { + if self.domain != rhs.domain || self.codomain != rhs.codomain { + return Err(error!("cannot subtract {self:?} - {rhs:?}")) + } + let mut res = Matrix::empty(self.domain, self.codomain); + for col in 0..self.domain { + for row in 0..self.codomain { + let sub = self.get(row, col)? - rhs.get(row, col)?; + res.set(row, col, sub?)?; + } + } + Ok(res) + } +} + +fn dot(lhs: Vec<&Value>, rhs: Vec<&Value>) -> Result { + let len = lhs.len(); + + let mut res = Value::Int(0); + for i in 0..len { + let val = (lhs[i].clone() * rhs[i].clone())?; + res = (res + val)?; + } + + Ok(res) +} + +impl Mul for Matrix { + type Output = Result; + + fn mul(self, rhs: Self) -> Self::Output { + if self.domain != rhs.codomain { + return Err(error!("cannot multiply {self:?} * {rhs:?}")) + } + let mut res = Self::empty(rhs.domain, self.codomain); + for (i, row) in self.rows().enumerate() { + for (j, col) in rhs.cols().enumerate() { + let dot = dot(row.clone(), col.clone())?; + res.set(i, j, dot)?; + } + } + Ok(res) + } +} + +pub struct MatrixRows<'a> { + matrix: &'a Matrix, + row: usize +} + +impl<'a> Iterator for MatrixRows<'a> { + type Item = Vec<&'a Value>; + + fn next(&mut self) -> Option { + if self.row >= self.matrix.codomain { + return None + } + + let row_start = self.row * self.matrix.domain; + let row_end = row_start + self.matrix.domain; + + let res = self.matrix.values + .iter() + .enumerate() + .filter(|(idx, _)| *idx >= row_start && *idx < row_end) + .map(|v| v.1) + .collect(); + + self.row += 1; + + Some(res) + } +} + +pub struct MatrixCols<'a> { + matrix: &'a Matrix, + col: usize +} + +impl<'a> Iterator for MatrixCols<'a> { + type Item = Vec<&'a Value>; + + fn next(&mut self) -> Option { + if self.col >= self.matrix.domain { + return None + } + + let res = self.matrix.values + .iter() + .enumerate() + .filter(|(idx, _)| *idx % self.matrix.domain == self.col) + .map(|v| v.1) + .collect(); + + self.col += 1; + + Some(res) + } +} diff --git a/matrix-lang/src/value/mod.rs b/matrix-lang/src/value/mod.rs new file mode 100644 index 0000000..9094bb6 --- /dev/null +++ b/matrix-lang/src/value/mod.rs @@ -0,0 +1,42 @@ +pub mod comp; +pub mod gc; +pub mod matrix; +pub mod hash; +pub mod exception; +pub mod function; +pub mod fmt; +pub mod index; +pub mod clone; + +use crate::prelude::*; + +#[derive(Clone)] +pub enum Value { + Nil, + + Bool(bool), + Int(i64), + Float(f64), + Ratio(Rational64), + Complex(Complex64), + + Regex(Rc), + String(Rc), + + List(Gc>), + Matrix(Gc), + Table(Gc), + + Function(Rc), + Range(Rc<(i64, i64, bool)>), + Iter(Rc), + File(Rc>), + + Exception(Exception), +} + +impl From<&str> for Value { + fn from(value: &str) -> Self { + Value::String(Rc::from(value)) + } +} diff --git a/matrix-lang/src/value/value.rs b/matrix-lang/src/value/value.rs new file mode 100644 index 0000000..10d8398 --- /dev/null +++ b/matrix-lang/src/value/value.rs @@ -0,0 +1,522 @@ +use std::{ + collections::HashMap, + rc::Rc, + hash::Hash, + fmt::{Display, Debug}, + ops::{Add, Neg, Not, Sub, Div, Mul, BitOr, BitAnd, BitXor, Shl, Shr}, + cmp::Ordering, + cell::RefCell, fs::File +}; + +use crate::iter; +use num_complex::Complex64; +use num_rational::Rational64; +use regex::Regex; + +use crate::{ast::{Expr, BinaryOp, UnaryOp}, chunk::Function, Result, gc::Gc}; + + +pub type InlineList = Vec; +pub type InlineMatrix = (usize, usize, Vec); +pub type InlineTable = Vec<(Expr, Expr)>; + +#[derive(Debug)] +pub struct Error(String); + +impl Display for self::Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +impl std::error::Error for self::Error {} + +macro_rules! error { + ($($arg:tt)*) => { + Err(self::Error(format!($($arg)*)).into()) + }; +} + + +impl From<&str> for Value { + fn from(value: &str) -> Self { + Value::String(value.into()) + } +} + + +impl Debug for Value { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use Value as V; + match self { + V::Nil => write!(f, "[Nil]"), + V::Bool(b) => write!(f, "[Bool {b}]"), + V::Int(i) => write!(f, "[Int {i}]"), + V::Float(vf) => write!(f, "[Float {vf}]"), + V::Ratio(r) => write!(f, "[Ratio {r}]"), + V::Complex(c) => write!(f, "[Complex {c}]"), + V::Regex(r) => write!(f, "[Regex /{r}/]"), + V::String(s) => write!(f, "[String '{s}']"), + V::List(l) => write!(f, "[List {}]", l.len()), + V::Matrix(m) => write!(f, "[Matirx {}x{}]", m.domain, m.codomain), + V::Table(t) => write!(f, "[Table {}]", t.0.len()), + V::Function(vf) => write!(f, "[Function {}]", vf.name), + V::Range(r) => write!(f, "[Range {:?}..{:?}]", r.0, r.1), + V::Iter(_) => write!(f, "[Iterator]"), + V::Error(_) => write!(f, "[Error]"), + V::File(_) => write!(f, "[File]"), + } + } +} + +impl Display for Value { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use Value as V; + + let red; + let green; + let yellow; + let blue; + let pink; + let cyan; + let clear; + + if f.alternate() { + red = "\x1b[31m"; + green = "\x1b[32m"; + yellow = "\x1b[33m"; + blue = "\x1b[34m"; + pink = "\x1b[35m"; + cyan = "\x1b[36m"; + clear = "\x1b[0m"; + } else { + red = ""; + green = ""; + yellow = ""; + blue = ""; + pink = ""; + cyan = ""; + clear = ""; + } + + match self { + V::Nil => {write!(f, "{blue}nil{clear}")?;}, + V::Bool(b) => {write!(f, "{yellow}{b}{clear}")?;}, + V::Int(i) => {write!(f, "{yellow}{i}{clear}")?;}, + V::Float(l) => {write!(f, "{yellow}{l}{clear}")?;}, + V::Ratio(r) => {write!(f, "{yellow}{r}{clear}")?;}, + V::Complex(c) => {write!(f, "{yellow}{c}{clear}")?;}, + V::Regex(r) => {write!(f, "/{red}{r}{clear}/")?;}, + V::String(s) => { + if f.alternate() { + write!(f, "{green}'{s}'{clear}")?; + } else { + write!(f, "{s}")?; + } + } + V::List(l) => { + write!(f, "[")?; + for (i, el) in l.iter().enumerate() { + if i != 0 { + write!(f, " ")?; + } + if f.alternate() { + write!(f, "{el:#}")?; + } else { + write!(f, "{el}")?; + } + } + write!(f, "]")?; + }, + V::Matrix(m) => { + if f.alternate() { + write!(f, "{m:#}")?; + } else { + write!(f, "{m}")?; + } + }, + V::Table(t) => { + write!(f, "{{")?; + for (i, (key, val)) in t.0.iter().enumerate() { + if i != 0 { + write!(f, ", ")?; + } + if f.alternate() { + write!(f, "{key:#} = {val:#}")?; + } else { + write!(f, "{key} = {val}")?; + } + } + write!(f, "}}")?; + }, + V::Function(fun) => { + use crate::chunk::InnerFunction as F; + match fun.fun { + F::Compiled(_) => write!(f, "{cyan}[Function {}]{clear}", fun.name)?, + F::Native(_) => write!(f, "{cyan}[Builtin {}]{clear}", fun.name)?, + }; + }, + V::Range(r) => { + if f.alternate() { + write!(f, "{:#}..{:#}", r.0, r.1)?; + } else { + write!(f, "{}..{}", r.0, r.1)?; + } + } + V::Iter(_) => {write!(f, "{pink}[Iterator]{clear}")?;}, + V::File(_) => {write!(f, "{pink}[File]{clear}")?;}, + V::Error(e) => {write!(f, "{red}{e}{clear}")?;}, + }; + Ok(()) + } +} + +impl Value { + pub fn can_hash(&self) -> Result<()> { + use Value::*; + match self { + Nil => {}, + Bool(_) => {}, + Int(_) => {}, + Ratio(_) => {}, + Regex(_) => {}, + String(_) => {} + List(l) => { + for val in l.iter() { + val.can_hash()?; + } + } + Matrix(m) => { + for val in m.values.iter() { + val.can_hash()?; + } + }, + _ => return error!("cannot hash {self:?}") + } + Ok(()) + } + + pub fn is_zero(&self) -> bool { + use Value as V; + match self { + V::Int(i) => *i == 0, + V::Float(f) => *f == 0.0 || *f == -0.0, + V::Ratio(r) => *r.numer() == 0, + _ => false, + } + } +} + + +impl Value { + pub fn val_cmp(&self, other: &Self) -> Result { + self.partial_cmp(other) + .map_or_else(|| error!("cannot compare: {self:?} and {other:?}"), Ok) + } + + fn index_single(&self, index: &Value) -> Result { + use Value as V; + match (self, index) { + (V::Table(t), index) => { + Ok(t.get(index)?.unwrap_or(&Value::Nil).clone()) + }, + (V::List(l), V::Int(i)) => { + if *i < 0 || *i as usize >= l.len() { + return error!("{i} out of bounds for {self:?}") + } + Ok(l[*i as usize].clone()) + }, + (V::Matrix(m), V::Int(i)) => { + if *i < 0 || *i as usize >= m.values.len() { + return error!("{i} out of bounds for {self:?}") + } + Ok(m.values[*i as usize].clone()) + }, + _ => return error!("{index:?} cant index {self:?}") + } + } + + fn index_multiple(&self, indexes: &Vec) -> Result { + use Value as V; + match self { + V::List(..) => { + let mut ret = Vec::new(); + for index in indexes { + let res = self.index_single(index)?; + ret.push(res); + } + Ok(V::List(ret.into())) + } + V::Table(..) => { + let mut ret = ValueMap::new(); + for index in indexes { + let res = self.index_single(index)?; + ret.insert(index.clone(), res)?; + } + Ok(V::Table(ret.into())) + } + V::Matrix(m) => { + let err = || error!("{self:?} can be index by [Int] or [Int;Int]"); + if indexes.len() != 2 { + return err() + } + let lhs = indexes[0].clone(); + let rhs = indexes[1].clone(); + match (lhs, rhs) { + (V::Nil, V::Nil) => { + Ok(V::Matrix(m.clone_inside())) + }, + (V::Int(row), V::Nil) => { + let Some((_, row)) = m.rows().enumerate().filter(|(idx, _)| *idx as i64 == row).next() else { + return err(); + }; + let row: Vec = row.into_iter().map(|e| e.clone()).collect(); + Ok(V::Matrix(Matrix::new(row.len(), 1, row).into())) + }, + (V::Nil, V::Int(col)) => { + let Some((_, col)) = m.cols().enumerate().filter(|(idx, _)| *idx as i64 == col).next() else { + return err(); + }; + let col: Vec = col.into_iter().map(|e| e.clone()).collect(); + Ok(V::Matrix(Matrix::new(1, col.len(), col).into())) + }, + (V::Int(row), V::Int(col)) => { + if row < 0 || col < 0 { + return err(); + } + m.get(row as usize, col as usize) + } + _ => return err() + } + } + _ => return error!("cannot index {self:?}") + } + } + + pub fn index(&self, index: &Vec) -> Result { + if index.len() == 0 { + Ok(self.clone_inside()) + } else if index.len() == 1 { + self.index_single(&index[0]) + } else { + self.index_multiple(index) + } + } + + fn store_index_single(&mut self, index: &Value, store: Value) -> Result<()> { + use Value as V; + let err = format!("{self:?}"); + match (self, index) { + (V::Table(t), index) => { + t.insert(index.clone(), store) + }, + (V::List(l), V::Int(i)) => { + if *i < 0 || *i as usize >= l.len() { + return error!("{i} out of bounds for {err}") + } + l[*i as usize] = store; + Ok(()) + }, + (V::Matrix(m), V::Int(i)) => { + if *i < 0 || *i as usize >= m.values.len() { + return error!("{i} out of bounds for {err}") + } + m.values[*i as usize] = store; + Ok(()) + }, + _ => return error!("{index:?} cant index {err}") + } + } + + fn store_index_multiple(&mut self, indexes: &Vec, store: Value) -> Result<()> { + use Value as V; + match self { + V::List(..) => { + for index in indexes { + self.store_index_single(index, store.clone())?; + } + Ok(()) + } + V::Table(..) => { + for index in indexes { + self.store_index_single(index, store.clone())?; + } + Ok(()) + } + _ => return error!("cannot index {self:?}") + } + } + + pub fn store_index(&mut self, index: &Vec, store: Value) -> Result<()> { + if index.len() == 0 { + Ok(()) + } else if index.len() == 1 { + self.store_index_single(&index[0], store) + } else { + self.store_index_multiple(index, store) + } + } + + pub fn store_field_access(&mut self, ident: &str, val: Value) -> Result<()> { + use Value as V; + match self { + V::Table(t) => { + let key = V::String(Rc::from(ident)); + Ok(t.insert(key, val)?) + }, + _ => return error!("cannot field access assign {self:?}") + } + } + + pub fn field_access(&self, ident: &str) -> Result { + use Value as V; + match self { + V::Table(t) => { + let key = V::String(Rc::from(ident)); + Ok(t.get(&key)?.unwrap_or(&V::Nil).clone()) + }, + _ => return error!("cannot field access {self:?}") + } + } + + pub fn binary_op(op: BinaryOp, lhs: Value, rhs: Value) -> Result { + use BinaryOp::*; + match op { + Add => lhs + rhs, + Subtract => lhs - rhs, + Multiply => lhs * rhs, + Divide => lhs / rhs, + Modulo => lhs.modulo(rhs), + Power => lhs.pow(rhs), + BitwiseAnd => lhs & rhs, + BitwiseOr => lhs | rhs, + BitwiseXor => lhs ^ rhs, + BitwiseShiftLeft => lhs << rhs, + BitwiseShiftRight => lhs >> rhs, + Equals => Ok(Self::Bool(lhs == rhs)), + NotEquals => Ok(Self::Bool(lhs != rhs)), + GreaterEquals => Ok(Self::Bool(lhs >= rhs)), + LessEquals => Ok(Self::Bool(lhs <= rhs)), + GreaterThan => Ok(Self::Bool(lhs > rhs)), + LessThan => Ok(Self::Bool(lhs < rhs)), + Range | RangeEq => { + let Value::Int(lhs) = lhs else { + return error!("range can only take [Int]'s") + }; + let Value::Int(rhs) = rhs else { + return error!("range can only take [Int]'s") + }; + Ok(Self::Range(Rc::new((lhs, rhs, op == RangeEq)))) + }, + } + } + + pub fn unary_op(op: UnaryOp, val: Value) -> Value { + use UnaryOp::*; + match op { + Negate => -val, + Not => Self::Bool(!val), + } + } +} + +impl Neg for Value { + type Output = Value; + + fn neg(self) -> Self::Output { + use Value::*; + match self { + Bool(b) => Bool(!b), + Int(i) => Int(-i), + Float(f) => Float(-f), + Ratio(r) => Ratio(-r), + Complex(c) => Complex(-c), + _ => return Float(f64::NAN) + } + } +} + +impl Not for Value { + type Output = bool; + + fn not(self) -> Self::Output { + use Value as V; + match self { + V::Nil => true, + V::Bool(b) => !b, + V::Int(i) => i == 0, + V::Float(f) => f == 0.0, + V::Ratio(r) => *(r.numer()) == 0 || *(r.denom()) == 0, + V::Complex(c) => !c.is_normal(), + V::Regex(_) => false, + V::List(_) => false, + V::Matrix(_) => false, + V::Table(_) => false, + V::String(s) => s.as_ref() == "", + V::Function(_) => false, + V::Iter(_) => false, + V::Range(_) => false, + V::File(_) => false, + V::Error(_) => false, + } + } +} + +impl Value { + + pub fn into_iter_fn(self) -> Result> { + let Value::Iter(iter) = self.into_iter()? else { + return error!("bypassed iter check") + }; + Ok(iter) + } + + pub fn into_iter(self) -> Result { + use Value as V; + Ok(match self { + V::Iter(..) => self, + V::List(l) => { + let iter = RefCell::new(l.into_inner().into_iter()); + iter!(move |_,_| { + match iter.borrow_mut().next() { + Some(v) => Ok(v), + None => Ok(V::Nil), + } + }) + }, + V::Range(r) => { + let r = (*r).clone(); + let lhs = RefCell::new(r.0); + let rhs = r.1; + iter!(move |_,_| { + let val = *lhs.borrow(); + let next = *lhs.borrow() + 1; + if (!r.2 && *lhs.borrow() < rhs) || (r.2 && *lhs.borrow() <= rhs) { + *lhs.borrow_mut() = next; + return Ok(Value::Int(val)) + } + Ok(Value::Nil) + }) + }, + V::Function(f) => { + if f.arity > 0 || f.variadic { + return error!("iterator functions cannot be varadic or take arguments") + } + V::Iter(f) + }, + val => return error!("cannot turn {val:?} into an iterator") + }) + } +} + +fn dot(lhs: Vec<&Value>, rhs: Vec<&Value>) -> Result { + let len = lhs.len(); + + let mut res = Value::Int(0); + for i in 0..len { + let val = (lhs[i].clone() * rhs[i].clone())?; + res = (res + val)?; + } + + Ok(res) +} diff --git a/matrix-lang/src/vm.rs b/matrix-lang/src/vm.rs new file mode 100644 index 0000000..bac6341 --- /dev/null +++ b/matrix-lang/src/vm.rs @@ -0,0 +1,461 @@ +use std::{fmt::Display, ops::{IndexMut, Index}, sync::{atomic::{AtomicUsize, Ordering}, Arc}, collections::HashMap}; +use crate::prelude::*; + +pub struct Stack { + inner: Vec +} + +impl Stack { + pub fn new() -> Self { + Self { inner: vec![] } + } + + pub fn pop(&mut self) -> T { + self.inner.pop().expect("stack empty") + } + + pub fn push(&mut self, val: T) { + self.inner.push(val) + } + + pub fn split_off(&mut self, len: usize) -> Self { + let inner = self.inner.split_off(len); + Self { inner } + } + + pub fn truncate(&mut self, len: usize) { + self.inner.truncate(len); + } + + pub fn len(&self) -> usize { + self.inner.len() + } +} + +impl Display for Stack { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f,"[ ")?; + for (i, el) in self.inner.iter().enumerate() { + if i != 0 { + write!(f, ", ")?; + } + write!(f, "{el}")?; + } + write!(f, " ]")?; + Ok(()) + } +} + +impl IndexMut for Stack { + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + &mut self.inner[index] + } +} + +impl Index for Stack { + type Output = T; + + fn index(&self, index: usize) -> &Self::Output { + &self.inner[index] + } +} + +#[derive(Clone)] +pub struct StackFrame { + #[allow(dead_code)] + name: Rc, + body: Rc, + ip: usize, + bp: usize, + lp: usize, + depth: usize, +} + +struct VmError { + err: Exception, + frames: Vec +} + +impl From for VmError { + fn from(err: Exception) -> Self { + VmError { + err, + frames: Vec::new() + } + } +} + +type VmResult = std::result::Result; + +impl StackFrame { + fn new(vm: &Vm, name: Rc, body: Rc, arity: usize, depth: usize) -> Self { + Self { + name, + body, + bp: vm.stack.len() - arity, + lp: vm.locals.len(), + ip: 0, + depth + } + } +} + +impl Default for StackFrame { + fn default() -> Self { + StackFrame { + name: Rc::from(""), + body: Rc::new(Chunk::default()), + bp: 0, + lp: 0, + ip: 0, + depth: 0 + } + } +} + +pub enum Interupt { + KeyboardInterupt = 1 +} + +struct TryScope { + stack_len: usize, + locals_len: usize, + frame_depth: usize, + err_idx: usize, +} + +pub struct Vm { + stack: Stack, + locals: Stack, + trystack: Vec, + globals: Rc>>, + names: NamesTable, + global_names: GlobalsTable, + interupt: Arc, +} + +macro_rules! error { + ($($arg:tt)*) => { + exception!(RUNTIME_EXCEPTION, $($arg)*) + }; +} + +impl Vm { + + fn push(&mut self, val: Value) { + self.stack.push(val) + } + + fn pop(&mut self) -> Value { + self.stack.pop() + } + + fn top(&mut self) -> Value { + self.stack[self.stack.len() - 1].clone() + } + + pub fn names(&self) -> NamesTable { + self.names.clone() + } + + pub fn globals(&self) -> GlobalsTable { + self.global_names.clone() + } + + pub fn interupt(&self) -> Arc { + self.interupt.clone() + } + + pub fn new() -> Self { + Self { + stack: Stack::new(), + locals: Stack::new(), + trystack: Vec::new(), + globals: Rc::new(RefCell::new(HashMap::new())), + names: Rc::new(RefCell::new(Vec::new())), + global_names: Rc::new(RefCell::new(Vec::new())), + interupt: Arc::new(AtomicUsize::new(0)), + } + } + + pub fn load_global(&mut self, value: Value, name: &str) { + let idx = self.global_names.borrow().len(); + self.global_names.borrow_mut().push(Global { idx, name: Rc::from(name), is_const: true }); + self.globals.borrow_mut().insert(idx as u16, value); + } + + pub fn load_global_fn(&mut self, value: Rc, name: &str) { + self.load_global(Value::Function(value), name) + } + + fn init_frame(&mut self, fun: Rc) -> Result { + let InnerFunction::Compiled(ref compiled_chunk) = fun.fun else { + return Err(error!("cannot create stack frame on builtin function")) + }; + Ok(StackFrame::new( + self, + fun.name.clone(), + compiled_chunk.clone(), + fun.arity, + 0 + )) + } + + fn check_interupt(&self) -> bool { + self.interupt.load(Ordering::Relaxed) != 0 + } + + fn exec_ins(&mut self, frame: &mut StackFrame, ins: Instruction) -> VmResult> { + use Instruction as I; + match ins { + I::NoOp => {}, + I::CreateLocal => self.locals.push(self.stack.pop()), + I::LoadLocal(idx) => {self.stack.push(self.locals[frame.lp + idx as usize].clone());}, + I::StoreLocal(idx) => self.locals[frame.bp + idx as usize] = self.pop(), + I::DiscardLocals(count) => {self.locals.truncate(self.locals.len() - count as usize)}, + I::LoadGlobal(idx) => { + let val = self.globals + .borrow_mut() + .get(&idx) + .unwrap() + .clone(); + self.stack.push(val); + }, + I::StoreGlobal(idx) => { + let val = self.pop(); + self.globals.borrow_mut().insert(idx, val); + }, + I::Const(idx) => self.push(frame.body.constants[idx as usize].clone()), + I::Int(i) => self.push(Value::Int(i as i64)), + I::True => self.push(Value::Bool(true)), + I::False => self.push(Value::Bool(false)), + I::Nil => self.push(Value::Nil), + I::Dup => self.push(self.stack[self.stack.len() - 1].clone()), + I::Discard(count) => {self.stack.truncate(self.stack.len() - count as usize)}, + I::UnaryOp(op) => { + let val = self.pop(); + self.push(Value::unary_op(op, val)); + }, + I::BinaryOp(op) => { + let rhs = self.pop(); + let lhs = self.pop(); + self.push(Value::binary_op(op, lhs, rhs)?); + }, + I::NewList(items) => { + let list = self.stack.split_off(self.stack.len() - items as usize); + self.push(Value::List(list.inner.into())); + }, + I::NewTable(items) => { + let mut table = ValueMap::new(); + for _ in 0..items { + let value = self.pop(); + let key = self.pop(); + table.insert(key, value)?; + } + self.push(Value::Table(table.into())) + }, + I::NewMatrix(items, domain) => { + let values = self.stack.split_off(self.stack.len() - items as usize).inner; + let domain = domain as usize; + let codomain = values.len() / domain; + self.push(Value::Matrix(Gc::new(Matrix::new(domain, codomain, values)))); + } + I::Jump(idx) => { + if self.check_interupt() { + return Ok(Some(Value::Nil)) + } + frame.ip = idx as usize; + } + I::JumpTrue(idx) => { + if self.check_interupt() { + return Ok(Some(Value::Nil)) + } + if !!self.pop() { + frame.ip = idx as usize; + } + }, + I::JumpFalse(idx) => { + if self.check_interupt() { + return Ok(Some(Value::Nil)) + } + if !self.pop() { + frame.ip = idx as usize; + } + }, + I::JumpNil(idx) => { + if self.check_interupt() { + return Ok(Some(Value::Nil)) + } + if self.pop() == Value::Nil { + frame.ip = idx as usize; + } + }, + I::Call(arity) => { + let arity = arity as usize; + + let fun = self.pop(); + let Value::Function(fun) = fun else { + Err(error!("cannot call {fun} (not a function)"))? + }; + + if !fun.variadic && arity > fun.arity { + let needs = fun.arity; + Err(error!("function {fun} takes {needs} args, given {arity}"))? + } + + let mut params = self.stack.split_off(self.stack.len() - arity).inner; + + if params.len() < fun.arity { + let new_arity = fun.arity - arity; + let val = native!("", new_arity, false, move |(vm, vm_frame), args| { + let mut params = params.clone(); + params.extend(args); + vm.exec_fn(vm_frame, fun.clone(), params).map_err(|e| e.err) + }); + self.stack.push(val); + return Ok(None); + } + + if fun.variadic { + if arity == fun.arity { + params.push(Value::List(vec![].into())); + } else { + let count = arity - fun.arity; + let varadic_params = params.split_off(params.len() - count); + params.push(Value::List(varadic_params.into())); + } + } + + let res = self.exec_fn(frame, fun, params)?; + self.push(res); + }, + I::Return => { + if self.check_interupt() { + return Ok(Some(Value::Nil)) + } + let ret = self.pop(); + self.stack.truncate(frame.bp); + self.locals.truncate(frame.lp); + return Ok(Some(ret)) + }, + I::Field(name_idx) => { + let expr = self.pop(); + let name = self.names.borrow()[name_idx as usize].clone(); + self.push(expr.field_access(&name)?); + }, + I::StoreField(name_idx) => { + let mut expr = self.pop(); + let value = self.pop(); + let name = self.names.borrow()[name_idx as usize].clone(); + expr.store_field_access(&name, value)?; + }, + I::Index(count) => { + let index = self.stack.split_off(self.stack.len() - count as usize); + let collection = self.pop(); + let value = collection.index(&index.inner)?; + self.stack.push(value); + }, + I::StoreIndex(count) => { + let index = self.stack.split_off(self.stack.len() - count as usize); + let mut collection = self.pop(); + let value = self.pop(); + collection.store_index(&index.inner, value)?; + }, + I::IterCreate => { + let val = self.pop().into_iter(); + self.push(val?); + }, + I::IterNext => { + let Value::Iter(iter) = self.top() else { panic!("bypassed iter check"); }; + let val = self.exec_fn(frame, iter, vec![])?; + self.push(val); + }, + I::Try(idx) => { + let scope = TryScope { + err_idx: idx as usize, + frame_depth: frame.depth, + stack_len: self.stack.len(), + locals_len: self.locals.len(), + }; + self.trystack.push(scope); + }, + I::TryEnd => { + self.trystack.pop().unwrap(); + }, + }; + + Ok(None) + } + + fn stack_trace(&mut self, frames: Vec, e: Exception) -> Exception { + let mut e = e; + for frame in frames { + let ip = frame.ip - 1; + let pos = frame.body.pos[ip]; + e = e.trace(frame.name, pos); + } + e + } + + fn exec_fn(&mut self, frame: &mut StackFrame, fun: Rc, params: Vec) -> VmResult { + if self.check_interupt() { + return Ok(Value::Nil) + } + let name = fun.name.clone(); + let params_len = params.len(); + match &fun.fun { + InnerFunction::Compiled(body) => { + for param in params { + self.stack.push(param); + } + let mut new_frame = StackFrame::new(self, name, body.clone(), params_len, frame.depth + 1); + let res = self.exec(&mut new_frame); + res + }, + InnerFunction::Native(native_fun) => { + Ok(native_fun((self, frame), params)?) + } + } + } + + fn exec(&mut self, frame: &mut StackFrame) -> VmResult { + loop { + let ins = frame.body.code[frame.ip].clone(); + frame.ip += 1; + match self.exec_ins(frame, ins) { + Ok(Some(val)) => return Ok(val), + Ok(None) => {}, + Err(err) => { + if let Some(catch) = self.trystack.pop() { + if frame.depth != catch.frame_depth { + self.trystack.push(catch); + return Err(err); + } + self.stack.truncate(catch.stack_len); + self.locals.truncate(catch.locals_len); + frame.ip = catch.err_idx; + self.stack.push(Value::Exception(err.err)); + } else { + let mut err = err; + err.frames.push(frame.clone()); + return Err(err) + } + }, + } + } + } + + pub fn run_fn(&mut self, frame: &mut StackFrame, fun: Rc, params: Vec) -> Result { + self.exec_fn(frame, fun, params).map_err(|e| e.err) + } + + pub fn run(&mut self, fun: Rc) -> Result { + self.interupt.store(0, Ordering::SeqCst); + self.stack = Stack::new(); + self.locals = Stack::new(); + let mut frame = self.init_frame(fun)?; + self.exec(&mut frame).map_err(|e| { + self.stack_trace(e.frames, e.err) + }) + } +} diff --git a/matrix-macros/Cargo.toml b/matrix-macros/Cargo.toml index df84237..4aff09a 100644 --- a/matrix-macros/Cargo.toml +++ b/matrix-macros/Cargo.toml @@ -9,4 +9,4 @@ proc-macro = true [dependencies] syn = { version = "1", features = ["full"] } quote = "1" -matrix = { path = "../matrix" } +matrix-lang = { path = "../matrix-lang" } diff --git a/matrix-macros/src/lib.rs b/matrix-macros/src/lib.rs index b12d30b..a660053 100644 --- a/matrix-macros/src/lib.rs +++ b/matrix-macros/src/lib.rs @@ -37,12 +37,12 @@ pub fn native_func(input: TokenStream, annotated_item: TokenStream) -> TokenStre assert!(itemfn.sig.variadic.is_none(), "item must not be variadic"); let expanded = quote! { - #visibility fn #name() -> ::std::rc::Rc< ::matrix::chunk::Function> { - ::std::rc::Rc::new( ::matrix::chunk::Function { + #visibility fn #name() -> ::std::rc::Rc< ::matrix_lang::prelude::Function> { + ::std::rc::Rc::new( ::matrix_lang::prelude::Function { name: ::std::rc::Rc::from( #name_str ), arity: #arity, variadic: #variadic, - fun: ::matrix::chunk::InnerFunction::Native( + fun: ::matrix_lang::prelude::InnerFunction::Native( ::std::rc::Rc::new( |#inputs| #output #block ) diff --git a/matrix-std/Cargo.toml b/matrix-std/Cargo.toml new file mode 100644 index 0000000..476272c --- /dev/null +++ b/matrix-std/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "matrix-std" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1" +matrix-lang = { path = "../matrix-lang" } +matrix-macros = { path = "../matrix-macros" } +os_info = "3" +rand = "0.8" diff --git a/matrix-std/src/core.rs b/matrix-std/src/core.rs new file mode 100644 index 0000000..69b6d97 --- /dev/null +++ b/matrix-std/src/core.rs @@ -0,0 +1,295 @@ +use std::hash::{DefaultHasher, Hasher}; +use rand::Rng; +use matrix_lang::prelude::*; +use matrix_macros::native_func; +use crate::{VmArgs, next, error, unpack_args, unpack_varargs}; + +fn to_radix(r: i64, mut n: i64) -> String { + let mut result = String::new(); + let mut idx = 0; + if n == 0 { + result.push('0'); + return result + } else if n < 0 { + n = -n; + idx = 1; + result.push('-'); + } + while n != 0 { + let c = std::char::from_digit((n % r) as u32, r as u32).unwrap(); + result.insert(idx, c); + n /= r; + } + result +} + +#[native_func(1)] +fn bin(_: VmArgs, args: Vec) -> Result { + let [value] = unpack_args!(args); + match value { + Value::Int(n) => Ok(Value::String(to_radix(2, n).into())), + _ => error!("bin requires a integer agument") + } +} + +#[native_func(1)] +fn sex(_: VmArgs, args: Vec) -> Result { + let [value] = unpack_args!(args); + match value { + Value::Int(n) => Ok(Value::String(to_radix(6, n).into())), + _ => error!("sex requires a integer agument") + } +} + +#[native_func(1)] +fn oct(_: VmArgs, args: Vec) -> Result { + let [value] = unpack_args!(args); + match value { + Value::Int(n) => Ok(Value::String(to_radix(8, n).into())), + _ => error!("oct requires a integer agument") + } +} + +#[native_func(1)] +fn hex(_: VmArgs, args: Vec) -> Result { + let [value] = unpack_args!(args); + match value { + Value::Int(n) => Ok(Value::String(to_radix(16, n).into())), + _ => error!("hex requires a integer agument") + } +} + +#[native_func(2)] +fn radix(_: VmArgs, args: Vec) -> Result { + let [radix, value] = unpack_args!(args); + match (radix, value) { + (Value::Int(r), Value::Int(n)) => Ok(Value::String(to_radix(r, n).into())), + _ => error!("radix requires two integer aguments") + } +} + +#[native_func(1..)] +fn append(_: VmArgs, args: Vec) -> Result { + let ([list], args) = unpack_varargs!(args); + let Value::List(mut list) = list else { + return error!("append requires a list") + }; + for arg in args { + list.push(arg); + }; + Ok(Value::List(list)) +} + +#[native_func(2)] +fn push(_: VmArgs, args: Vec) -> Result { + let [list, value] = unpack_args!(args); + let Value::List(mut list) = list else { + return error!("push requires a list") + }; + list.push(value); + Ok(Value::List(list)) +} + +#[native_func(1)] +fn pop(_: VmArgs, args: Vec) -> Result { + let [list] = unpack_args!(args); + let Value::List(mut list) = list else { + return error!("pop requires a list") + }; + match list.pop() { + Some(v) => Ok(v), + None => Ok(Value::Nil) + } +} + +#[native_func(2)] +fn remove(_: VmArgs, args: Vec) -> Result { + let [list, index] = unpack_args!(args); + let Value::List(mut list) = list else { + return error!("remove requires a list") + }; + let Value::Int(i) = index else { + return error!("remove reuqires a int index"); + }; + if i < 0 || i as usize >= list.len() { + Ok(Value::Nil) + } else { + Ok(list.remove(i as usize)) + } +} + +#[native_func(1)] +fn hash(_: VmArgs, args: Vec) -> Result { + let [value] = unpack_args!(args); + let mut hasher = DefaultHasher::new(); + value.try_hash(&mut hasher)?; + let fin = hasher.finish(); + Ok(Value::Int(fin as u32 as i64)) +} + +#[native_func(1)] +fn ord(_: VmArgs, args: Vec) -> Result { + let [value] = unpack_args!(args); + let Value::String(str) = value else { + return error!("ord requires a 1 length string") + }; + if str.len() != 1 { + return error!("ord requires a 1 length string") + } + let char = str.chars().next().unwrap(); + Ok(Value::Int(char as i64)) +} + +#[native_func(1)] +fn chr(_: VmArgs, args: Vec) -> Result { + let [value] = unpack_args!(args); + let Value::Int(i) = value else { + return error!("chr requires an int") + }; + match char::from_u32(i as u32) { + Some(c) => Ok(Value::String(String::from(c).into())), + None => error!("unable to get char from: {}", i as u32) + } +} + +#[native_func(1)] +fn str(_: VmArgs, args: Vec) -> Result { + let [value] = unpack_args!(args); + Ok(Value::String(format!("{value}").into())) +} + +fn partition(list: &mut [Value], lo: usize, hi: usize) -> (usize, usize) { + let pivot = list[(lo + hi) / 2].clone(); + let mut lt = lo; + let mut eq = lo; + let mut gt = hi; + + while eq <= gt { + if list[eq] < pivot { + list.swap(eq, lt); + lt += 1; + eq += 1; + } else if list[eq] > pivot { + list.swap(eq, gt); + gt -= 1; + } else { + eq += 1; + } + } + + (lt, gt) +} + +fn quicksort(list: &mut [Value], lo: usize, hi: usize) { + if lo < hi { + let (lt, gt) = partition(list, lo, hi); + if lt > 0 { + quicksort(list, lo, lt - 1); + } + quicksort(list, gt + 1, hi); + } +} + +#[native_func(1)] +fn sort(_: VmArgs, args: Vec) -> Result { + let [value] = unpack_args!(args); + let Value::List(mut list) = value else { + return error!("sort requires a list") + }; + if list.len() > 1 { + let hi = list.len() - 1; + quicksort(list.as_mut(), 0, hi); + } + Ok(Value::List(list)) +} + +#[native_func(0)] +fn rand(_: VmArgs, _: Vec) -> Result { + let mut rng = rand::thread_rng(); + Ok(Value::Float(rng.gen())) +} + +#[native_func(0..)] +fn rand_range(_: VmArgs, args: Vec) -> Result { + use Value as V; + let ([], varargs) = unpack_varargs!(args); + let (min, max, step) = match varargs.as_slice() { + [V::Int(max)] => { + (0, *max, 1) + }, + [V::Int(min), V::Int(max)] => { + (*min, *max, 1) + }, + [V::Int(min), V::Int(max), V::Int(step)] => { + (*min, *max, *step) + }, + _ => return error!("rand_range takes 1 to 3 [Int]'s") + }; + if max <= min { + return Ok(Value::Int(min)) + } + let count = ((max - 1 - min) / step) + 1; + let rng = (rand::thread_rng().gen::() % (count as u32)) as i64; + let i = min + (rng * step); + Ok(Value::Int(i)) +} + +#[native_func(2)] +fn rand_int(_: VmArgs, args: Vec) -> Result { + use Value as V; + let (min, max) = match args.as_slice() { + [V::Int(min), V::Int(max)] => { + (*min, *max + 1) + }, + _ => return error!("rand_int takes 2 [Int]'s") + }; + if max <= min { + return Ok(Value::Int(min)) + } + + let count = max - min; + let rng = (rand::thread_rng().gen::() % (count as u32)) as i64; + let i = min + rng; + Ok(Value::Int(i)) +} + +#[native_func(1)] +fn rand_in((vm, frame): VmArgs, args: Vec) -> Result { + let [iter] = unpack_args!(args); + let iter = iter.into_iter_fn()?; + let mut vals = Vec::new(); + loop { + let val = next!(vm, frame, iter)?; + if val == Value::Nil { break } + vals.push(val); + } + if vals.is_empty() { + return Ok(Value::Nil) + } + let idx = rand::thread_rng().gen::() % vals.len(); + Ok(vals[idx].clone()) +} + +pub fn load(vm: &mut Vm) { + vm.load_global_fn(bin(), "bin"); + vm.load_global_fn(sex(), "sex"); + vm.load_global_fn(oct(), "oct"); + vm.load_global_fn(hex(), "hex"); + vm.load_global_fn(radix(), "radix"); + vm.load_global_fn(str(), "str"); + + vm.load_global_fn(append(), "append"); + vm.load_global_fn(push(), "push"); + vm.load_global_fn(pop(), "pop"); + vm.load_global_fn(remove(), "remove"); + + vm.load_global_fn(hash(), "hash"); + vm.load_global_fn(ord(), "ord"); + vm.load_global_fn(chr(), "chr"); + + vm.load_global_fn(sort(), "sort"); + vm.load_global_fn(rand(), "rand"); + vm.load_global_fn(rand_range(), "rand_range"); + vm.load_global_fn(rand_int(), "rand_int"); + vm.load_global_fn(rand_in(), "rand_in"); +} diff --git a/matrix-std/src/io.rs b/matrix-std/src/io.rs new file mode 100644 index 0000000..19ff074 --- /dev/null +++ b/matrix-std/src/io.rs @@ -0,0 +1,174 @@ +use std::{io::{self, Read, Write}, cell::RefCell, fs::OpenOptions, rc::Rc}; +use matrix_lang::prelude::*; +use matrix_macros::native_func; +use crate::{error, VmArgs, unpack_varargs, unpack_args}; + +#[native_func(0..)] +fn print(_: VmArgs, args: Vec) -> Result { + let ([], varags) = unpack_varargs!(args); + for (i, value) in varags.into_iter().enumerate() { + if i != 0 { + print!(" "); + } + print!("{value}"); + } + Ok(Value::Nil) +} + +#[native_func(0..)] +fn println(_: VmArgs, args: Vec) -> Result { + let ([], varags) = unpack_varargs!(args); + for (i, value) in varags.into_iter().enumerate() { + if i != 0 { + print!(" "); + } + print!("{value}"); + } + print!("\n"); + Ok(Value::Nil) +} + +#[native_func(0)] +fn readln(_: VmArgs, _: Vec) -> Result { + let mut input = String::new(); + match io::stdin().read_line(&mut input) { + Ok(_) => { + match input.pop() { + Some(c) if c == '\n' => {}, + Some(c) => input.push(c), + None => {} + }; + Ok(Value::String(input.into())) + }, + Err(err) => error!("cant read from stdin: {err}") + } +} + +#[native_func(1)] +fn input(_: VmArgs, args: Vec) -> Result { + let [prompt] = unpack_args!(args); + let mut input = String::new(); + print!("{prompt}"); + let _ = io::stdout().flush(); + match io::stdin().read_line(&mut input) { + Ok(_) => { + match input.pop() { + Some(c) if c == '\n' => {}, + Some(c) => input.push(c), + None => {} + }; + Ok(Value::String(input.into())) + }, + Err(err) => error!("cant read from stdin: {err}") + } +} + +#[native_func(0)] +fn readlines(_: VmArgs, _: Vec) -> Result { + let lines = RefCell::new(io::stdin().lines()); + Ok(iter!(move |_,_| { + match lines.borrow_mut().next() { + Some(Ok(line)) => Ok(Value::String(line.into())), + _ => Ok(Value::Nil) + } + })) +} + +#[native_func(2)] +fn file_open(_: VmArgs, args: Vec) -> Result { + let [path, mode] = unpack_args!(args); + let Value::String(mode) = mode else { + return error!("open mode must be a string") + }; + let Value::String(path) = path else { + return error!("open path must be a string") + }; + let file = match mode.as_ref() { + "r" => OpenOptions::new().read(true).open(path.as_ref()), + "w" => OpenOptions::new().write(true).create(true).truncate(true).open(path.as_ref()), + "a" => OpenOptions::new().write(true).create(true).append(true).open(path.as_ref()), + "r+" => OpenOptions::new().read(true).write(true).open(path.as_ref()), + "w+" => OpenOptions::new().read(true).write(true).create(true).truncate(true).open(path.as_ref()), + "a+" => OpenOptions::new().read(true).write(true).create(true).append(true).open(path.as_ref()), + _ => return error!("invalid open mode: {mode}") + }; + + match file { + Ok(file) => Ok(Value::File(Rc::new(RefCell::new(file)))), + Err(err) => return error!("cannot open '{path}': {err}") + } +} + +#[native_func(1)] +fn file_read(_: VmArgs, args: Vec) -> Result { + let [file] = unpack_args!(args); + let Value::File(file) = file else { + return error!("file read requires a file") + }; + let mut contents = String::new(); + if let Err(err) = file.try_borrow_mut().unwrap().read_to_string(&mut contents) { + return error!("cannot read file: '{err}'") + }; + Ok(Value::String(contents.into())) +} + +#[native_func(1)] +fn file_lines(_: VmArgs, args: Vec) -> Result { + let [file] = unpack_args!(args); + let Value::File(file) = file else { + return error!("file read requires a file") + }; + let mut contents = String::new(); + if let Err(err) = file.try_borrow_mut().unwrap().read_to_string(&mut contents) { + return error!("cannot read file: '{err}'") + }; + let lines: Vec> = contents.split_inclusive("\n").map(|s| Rc::from(s)).collect(); + let lines = RefCell::new(lines.into_iter()); + Ok(iter!(move |_,_| { + match lines.borrow_mut().next() { + Some(line) => Ok(Value::String(line)), + None => Ok(Value::Nil) + } + })) +} + +#[native_func(2)] +fn file_write(_: VmArgs, args: Vec) -> Result { + let [file, content] = unpack_args!(args); + let Value::File(file) = file else { + return error!("file write requires a file") + }; + let content = format!("{content}"); + if let Err(err) = file.try_borrow_mut().unwrap().write_all(content.as_bytes()) { + return error!("cannot write file: '{err}'") + }; + Ok(Value::Nil) +} + +#[native_func(0..)] +fn throw(_: VmArgs, args: Vec) -> Result { + let ([], varargs) = unpack_varargs!(args); + + let mut str = String::new(); + for (i, v) in varargs.into_iter().enumerate() { + if i != 0 { + str.push_str(" "); + } + str.push_str(&format!("{v}")); + } + + error!("{str}") +} + +pub fn load(vm: &mut Vm) { + vm.load_global_fn(print(), "print"); + vm.load_global_fn(println(), "println"); + vm.load_global_fn(readln(), "readln"); + vm.load_global_fn(input(), "input"); + vm.load_global_fn(readlines(), "readlines"); + vm.load_global_fn(file_open(), "file_open"); + vm.load_global_fn(file_read(), "file_read"); + vm.load_global_fn(file_lines(), "file_lines"); + vm.load_global_fn(file_write(), "file_write"); + vm.load_global_fn(throw(), "throw"); +} diff --git a/matrix-std/src/iter.rs b/matrix-std/src/iter.rs new file mode 100644 index 0000000..638755c --- /dev/null +++ b/matrix-std/src/iter.rs @@ -0,0 +1,546 @@ +use std::{cell::RefCell, rc::Rc}; +use matrix_lang::prelude::*; +use matrix_macros::native_func; +use crate::{error, next, VmArgs, unpack_args, unpack_varargs}; + +use Value as V; + +#[native_func(1)] +fn len(_: VmArgs, args: Vec) -> Result { + let [value] = unpack_args!(args); + let len = match value { + V::List(l) => l.len(), + V::Matrix(m) => m.values.len(), + V::String(s) => s.len(), + V::Table(t) => t.len(), + _ => 1 + }; + Ok(V::Int(len as i64)) +} + +#[native_func(1)] +fn sum((vm, frame): VmArgs, args: Vec) -> Result { + let [iter] = unpack_args!(args); + let iter = iter.into_iter_fn()?; + let mut res = next!(vm, frame, iter)?; + if res == Value::Nil { + return Ok(res) + }; + loop { + let next = next!(vm, frame, iter)?; + if next == Value::Nil { break } + res = (res + next)?; + } + Ok(res) +} + +#[native_func(0..)] +fn range(_: VmArgs, args: Vec) -> Result { + let ([], varargs) = unpack_varargs!(args); + let (min, max, step) = match varargs.as_slice() { + [V::Int(max)] => { + (0, *max, 1) + }, + [V::Int(min), V::Int(max)] => { + (*min, *max, 1) + }, + [V::Int(min), V::Int(max), V::Int(step)] => { + (*min, *max, *step) + }, + _ => return error!("range takes 1 to 3 [Int]'s") + }; + + let i = RefCell::new(min); + Ok(iter!(move |_,_| { + let curr = *(i.borrow()); + *(i.borrow_mut()) = curr + step; + if (step >= 0 && curr >= max) || (step <= 0 && curr <= max) { + return Ok(V::Nil) + } else { + return Ok(V::Int(curr)) + } + })) +} + +#[native_func(1)] +fn iter(_: VmArgs, args: Vec) -> Result { + let [value] = unpack_args!(args); + Ok(value.into_iter()?) +} + +#[native_func(1)] +fn once(_: VmArgs, args: Vec) -> Result { + let [value] = unpack_args!(args); + let first = RefCell::new(false); + Ok(iter!(move |_,_| { + if *first.borrow() == false { + *first.borrow_mut() = true; + Ok(value.clone()) + } else { + Ok(Value::Nil) + } + })) +} + +#[native_func(1)] +fn list((vm, frame): VmArgs, args: Vec) -> Result { + let [iter] = unpack_args!(args); + let iter = iter.into_iter_fn()?; + let mut list = Vec::new(); + loop { + let res = next!(vm, frame, iter)?; + if res == Value::Nil { break } + list.push(res); + } + Ok(Value::List(list.into())) +} + +#[native_func(2)] +fn map(_: VmArgs, args: Vec) -> Result { + let [fun, iter] = unpack_args!(args); + let iter = iter.into_iter_fn()?; + let Value::Function(fun) = fun else { + return error!("map 1st arg must be a function") + }; + Ok(iter!(move |(vm,frame),_| { + let input = next!(vm, frame, iter)?; + if input == Value::Nil { + return Ok(input) + } + vm.run_fn(frame, fun.clone(), vec![input]) + })) +} + +#[native_func(2)] +fn fold((vm, frame): VmArgs, args: Vec) -> Result { + let [fun, iter] = unpack_args!(args); + let iter = iter.into_iter_fn()?; + let Value::Function(fun) = fun else { + return error!("fold 1st arg must be a function") + }; + let mut res = next!(vm, frame, iter)?; + if res == Value::Nil { + return Ok(Value::Nil) + }; + loop { + let next = next!(vm, frame, iter)?; + if next == Value::Nil { + return Ok(res) + }; + res = vm.run_fn(frame, fun.clone(), vec![res, next])?; + } +} + +#[native_func(3)] +fn foldi((vm, frame): VmArgs, args: Vec) -> Result { + let [init, fun, iter] = unpack_args!(args); + let iter = iter.into_iter_fn()?; + let Value::Function(fun) = fun else { + return error!("foldi 1st arg must be a function") + }; + let mut res = init; + loop { + let next = next!(vm, frame, iter)?; + if next == Value::Nil { + return Ok(res) + }; + res = vm.run_fn(frame, fun.clone(), vec![res, next])?; + } +} + +#[native_func(1)] +fn count((vm, frame): VmArgs, args: Vec) -> Result { + let [iter] = unpack_args!(args); + let iter = iter.into_iter_fn()?; + let mut len = 0; + loop { + let res = vm.run_fn(frame, iter.clone(), vec![])?; + if res == Value::Nil { break }; + len += 1; + } + Ok(Value::Int(len)) +} + +#[native_func(3)] +fn scan(_: VmArgs, args: Vec) -> Result { + let [init, fun, iter] = unpack_args!(args); + let iter = iter.into_iter_fn()?; + let Value::Function(fun) = fun else { + return error!("scan 2nd arg must be a function") + }; + let res = RefCell::new(init); + Ok(iter!(move |(vm,frame),_| { + let next = next!(vm, frame, iter)?; + if next == Value::Nil { + return Ok(Value::Nil) + }; + let res_next = vm.run_fn(frame, fun.clone(), vec![res.borrow().clone(), next])?; + *res.borrow_mut() = res_next; + Ok(res.borrow().clone()) + })) +} + +#[native_func(2)] +fn filter(_: VmArgs, args: Vec) -> Result { + let [fun, iter] = unpack_args!(args); + let iter = iter.into_iter_fn()?; + let Value::Function(fun) = fun else { + return error!("filter 1st arg must be a function") + }; + Ok(iter!(move |(vm,frame),_| { + loop { + let next = next!(vm, frame, iter)?; + if next == Value::Nil { + return Ok(Value::Nil) + } + let res = vm.run_fn(frame, fun.clone(), vec![next.clone()])?; + if !!res { + return Ok(next) + } + } + })) +} + +#[native_func(0..)] +fn chain(_: VmArgs, args: Vec) -> Result { + let ([], iters) = unpack_varargs!(args); + + let mut chaind = Vec::new(); + for iter in iters { + chaind.push(iter.into_iter_fn()?); + } + + chaind.reverse(); + let chaind = RefCell::new(chaind); + Ok(iter!(move |(vm,frame), _| { + loop { + let curr = match chaind.borrow_mut().last() { + Some(iter) => iter.clone(), + None => return Ok(Value::Nil) + }; + match vm.run_fn(frame, curr.clone(), vec![]) { + Ok(Value::Nil) => { + chaind.borrow_mut().pop(); + continue; + }, + v => return v + }; + } + })) +} + +#[native_func(1)] +fn lines(_: VmArgs, args: Vec) -> Result { + let [str] = unpack_args!(args); + let Value::String(str) = str else { + return error!("lines arg must be a string") + }; + let lines: Vec> = str.split("\n").map(|s| Rc::from(s)).collect(); + let res = RefCell::new(lines.into_iter()); + Ok(iter!(move |_,_| { + match res.borrow_mut().next() { + Some(line) => Ok(Value::String(line)), + None => Ok(Value::Nil) + } + })) +} + +#[native_func(2)] +fn skip((vm, frame): VmArgs, args: Vec) -> Result { + let [count, iter] = unpack_args!(args); + let iter = iter.into_iter_fn()?; + let Value::Int(i) = count else { + return error!("skip count requires a int") + }; + for _ in 0..i { + next!(vm, frame, iter)?; + } + Ok(Value::Iter(iter)) +} + +#[native_func(2)] +fn alternate(_: VmArgs, args: Vec) -> Result { + let [il, ir] = unpack_args!(args); + let il = il.into_iter_fn()?; + let ir = ir.into_iter_fn()?; + let flag = RefCell::new(Some(0)); + Ok(iter!(move |(vm, frame),_| { + let f = *flag.borrow(); + match f { + Some(0) => { + let val = next!(vm, frame, il)?; + if val == Value::Nil { + *flag.borrow_mut() = None; + } else { + *flag.borrow_mut() = Some(1); + } + Ok(val) + }, + Some(1) => { + let val = next!(vm, frame, ir)?; + if val == Value::Nil { + *flag.borrow_mut() = None; + } else { + *flag.borrow_mut() = Some(0); + } + Ok(val) + }, + _ => Ok(Value::Nil) + } + })) +} + +#[native_func(2)] +fn intersperse((vm, frame): VmArgs, args: Vec) -> Result { + let [value, iter] = unpack_args!(args); + let iter = iter.into_iter_fn()?; + let flag = RefCell::new(Some(0)); + let next = RefCell::new(next!(vm, frame, iter)?); + Ok(iter!(move |(vm, frame),_| { + let f = *flag.borrow(); + match f { + Some(0) => { + let val = next.borrow(); + if *val == Value::Nil { + *flag.borrow_mut() = None; + } else { + *flag.borrow_mut() = Some(1); + } + Ok(val.clone()) + }, + Some(1) => { + *next.borrow_mut() = next!(vm, frame, iter)?; + if *next.borrow() == Value::Nil { + *flag.borrow_mut() = None; + return Ok(Value::Nil) + } else { + *flag.borrow_mut() = Some(0); + } + let val = value.clone(); + Ok(val) + }, + _ => Ok(Value::Nil) + } + })) +} + +#[native_func(2)] +fn zip(_: VmArgs, args: Vec) -> Result { + let [il, ir] = unpack_args!(args); + let il = il.into_iter_fn()?; + let ir = ir.into_iter_fn()?; + let flag = RefCell::new(true); + Ok(iter!(move |(vm, frame),_| { + let f = *flag.borrow(); + match f { + true => { + let vl = next!(vm, frame, il)?; + let vr = next!(vm, frame, ir)?; + if vl == Value::Nil || vr == Value::Nil { + *flag.borrow_mut() = false; + return Ok(Value::Nil) + } + Ok(Value::List(vec![vl, vr].into())) + }, + _ => Ok(Value::Nil) + } + })) +} + +#[native_func(1)] +fn unzip((vm, frame): VmArgs, args: Vec) -> Result { + let [iter] = unpack_args!(args); + let iter = iter.into_iter_fn()?; + let mut ll = Vec::new(); + let mut lr = Vec::new(); + loop { + let val = next!(vm, frame, iter)?; + if val == Value::Nil { + break; + } + let Value::List(vals) = val else { + return error!("unzip only works over a iterator of pairs"); + }; + let vals = vals.into_inner(); + if vals.len() != 2 { + return error!("unzip only works over a iterator of pairs"); + } + let [l, r] = vals.try_into().unwrap(); + ll.push(l); + lr.push(r); + } + let ll = Value::List(ll.into()); + let lr = Value::List(lr.into()); + Ok(Value::List(vec![ll, lr].into())) +} + +#[native_func(1)] +fn cycle((vm, frame): VmArgs, args: Vec) -> Result { + let [iter] = unpack_args!(args); + let iter = iter.into_iter_fn()?; + let mut values = Vec::new(); + loop { + let val = next!(vm, frame, iter)?; + if val == Value::Nil { break }; + values.push(val); + } + let idx = RefCell::new(0_usize); + Ok(iter!(move |_,_| { + let i = *idx.borrow(); + *idx.borrow_mut() += 1; + *idx.borrow_mut() %= values.len(); + Ok(values[i].clone()) + })) +} + +#[native_func(2)] +fn take((vm, frame): VmArgs, args: Vec) -> Result { + let [count, iter] = unpack_args!(args); + let iter = iter.into_iter_fn()?; + let Value::Int(count) = count else { + return error!("take requires an int amount to collect") + }; + let mut values = Vec::new(); + for _ in 0..count { + let val = next!(vm, frame, iter)?; + if val == Value::Nil { break }; + values.push(val); + } + Ok(Value::List(values.into())) +} + +#[native_func(1)] +fn last((vm, frame): VmArgs, args: Vec) -> Result { + let [iter] = unpack_args!(args); + let iter = iter.into_iter_fn()?; + let mut last = Value::Nil; + loop { + let val = next!(vm, frame, iter)?; + if val == Value::Nil { break }; + last = val; + } + Ok(last) +} + +#[native_func(1)] +fn next((vm, frame): VmArgs, args: Vec) -> Result { + let [iter] = unpack_args!(args); + let iter = iter.into_iter_fn()?; + let val = next!(vm, frame, iter)?; + Ok(val) +} + +#[native_func(1)] +fn min((vm, frame): VmArgs, args: Vec) -> Result { + let [iter] = unpack_args!(args); + let iter = iter.into_iter_fn()?; + let mut min = Value::Nil; + loop { + let val = next!(vm, frame, iter)?; + if val == Value::Nil { break }; + if min == Value::Nil || val < min { + min = val; + } + } + Ok(min) +} + +#[native_func(1)] +fn max((vm, frame): VmArgs, args: Vec) -> Result { + let [iter] = unpack_args!(args); + let iter = iter.into_iter_fn()?; + let mut max = Value::Nil; + loop { + let val = next!(vm, frame, iter)?; + if val == Value::Nil { break }; + if max == Value::Nil || val > max { + max = val; + } + } + Ok(max) +} + +#[native_func(1)] +fn rev((vm, frame): VmArgs, args: Vec) -> Result { + let [iter] = unpack_args!(args); + let iter = iter.into_iter_fn()?; + let mut values = Vec::new(); + loop { + let val = next!(vm, frame, iter)?; + if val == Value::Nil { break }; + values.push(val); + } + let values = RefCell::new(values); + Ok(iter!(move |_,_| { + match values.borrow_mut().pop() { + Some(v) => Ok(v), + None => Ok(Value::Nil) + } + })) +} + +#[native_func(1)] +fn enumerate(_: VmArgs, args: Vec) -> Result { + let [iter] = unpack_args!(args); + let iter = iter.into_iter_fn()?; + + let idx = RefCell::new(0_i64); + Ok(iter!(move |(vm, frame),_| { + let val = next!(vm, frame, iter)?; + if val == Value::Nil { + return Ok(Value::Nil) + }; + let curr = *idx.borrow(); + *idx.borrow_mut() += 1; + Ok(Value::List(vec![Value::Int(curr), val].into())) + })) +} + +#[native_func(1)] +fn iterable(_: VmArgs, args: Vec) -> Result { + let [value] = unpack_args!(args); + use Value as V; + match value { + V::Function(_) | + V::List(_) | + V::Range(_) | + V::Iter(_) + => Ok(V::Bool(true)), + _ => Ok(V::Bool(false)) + } +} + +pub fn load(vm: &mut Vm) { + vm.load_global_fn(take(), "take"); + vm.load_global_fn(unzip(), "unzip"); + vm.load_global_fn(count(), "count"); + vm.load_global_fn(len(), "len"); + vm.load_global_fn(sum(), "sum"); + vm.load_global_fn(min(), "min"); + vm.load_global_fn(max(), "max"); + vm.load_global_fn(next(), "next"); + vm.load_global_fn(last(), "last"); + + vm.load_global_fn(list(), "list"); + vm.load_global_fn(fold(), "fold"); + vm.load_global_fn(foldi(), "foldi"); + vm.load_global_fn(scan(), "scan"); + vm.load_global_fn(chain(), "chain"); + vm.load_global_fn(lines(), "lines"); + vm.load_global_fn(skip(), "skip"); + + vm.load_global_fn(once(), "once"); + vm.load_global_fn(iter(), "iter"); + vm.load_global_fn(range(), "range"); + vm.load_global_fn(map(), "map"); + vm.load_global_fn(filter(), "filter"); + vm.load_global_fn(skip(), "skip"); + vm.load_global_fn(zip(), "zip"); + vm.load_global_fn(cycle(), "cycle"); + vm.load_global_fn(alternate(), "alternate"); + vm.load_global_fn(intersperse(), "intersperse"); + vm.load_global_fn(rev(), "rev"); + vm.load_global_fn(enumerate(), "enumerate"); + + vm.load_global_fn(iterable(), "iterable"); +} diff --git a/matrix-std/src/lib.rs b/matrix-std/src/lib.rs new file mode 100644 index 0000000..af4ecab --- /dev/null +++ b/matrix-std/src/lib.rs @@ -0,0 +1,50 @@ +mod core; +mod sys; +mod math; +mod io; +mod iter; + +use matrix_lang::prelude::*; +pub(crate) type VmArgs<'a, 'b> = (&'a mut Vm, &'b mut StackFrame); + +macro_rules! error { + ($($arg:tt)*) => { + Err(::matrix_lang::prelude::exception!(::matrix_lang::prelude::RUNTIME_EXCEPTION, $($arg)*)) + }; +} + +macro_rules! next { + ($vm:expr, $frame:expr, $iter:expr) => { + $vm.run_fn($frame, $iter.clone(), vec![]) + }; +} + +macro_rules! unpack_args { + ($e:expr) => { + $e.try_into().expect("bypassed arity check") + }; +} + +macro_rules! unpack_varargs { + ($e:expr) => {{ + let mut args = $e; + let matrix_lang::prelude::Value::List(varargs) = args.pop().expect("bypassed arity check") else { + panic!("bypassed arity check") + }; + let varargs = varargs.into_inner(); + (args.try_into().expect("bypassed arity check"), varargs) + }}; +} + +pub(crate) use error; +pub(crate) use next; +pub(crate) use unpack_args; +pub(crate) use unpack_varargs; + +pub fn load(vm: &mut Vm) { + core::load(vm); + sys::load(vm); + io::load(vm); + iter::load(vm); + math::load(vm); +} diff --git a/matrix-std/src/math.rs b/matrix-std/src/math.rs new file mode 100644 index 0000000..111544c --- /dev/null +++ b/matrix-std/src/math.rs @@ -0,0 +1,688 @@ +use core::f64; +use std::f64::{consts::{PI, E, TAU}, NAN, INFINITY}; +use matrix_lang::prelude::*; +use matrix_macros::native_func; +use crate::{error, VmArgs, unpack_args}; + +#[native_func(1)] +fn trans(_: VmArgs, args: Vec) -> Result { + let [value] = unpack_args!(args); + let mat = match value { + Value::Matrix(m) => m, + Value::List(l) => Matrix::from_list(l.to_vec()).into(), + _ => return error!("trans must be given a matrix") + }; + let values = mat + .cols() + .reduce(|mut a, b| {a.extend(b); a}) + .unwrap() + .into_iter() + .map(|e| e.clone()) + .collect(); + Ok(Value::Matrix(Matrix::new(mat.codomain, mat.domain, values).into())) +} + +fn mat_gauss_row_operation( + r1: usize, + r2: usize, + scale: Value, + mat: &mut Matrix +) -> Result<()> { + for col in 0..mat.domain { + let r1v = mat.get(r1, col)?; + let r2v = mat.get(r2, col)?; + let res = (r1v - (r2v * scale.clone())?)?; + mat.set(r1, col, res)?; + } + Ok(()) +} + +fn mat_swap_rows( + r1: usize, + r2: usize, + mat: &mut Matrix +) -> Result<()> { + let cols = mat.domain; + for col in 0..cols { + let a = mat.get(r1, col)?; + let b = mat.get(r2, col)?; + mat.set(r2, col, a)?; + mat.set(r1, col, b)?; + } + Ok(()) +} + +fn mat_find_non_zero_col( + mat: &Matrix +) -> Option { + for (i,col) in mat.cols().enumerate() { + for val in col.iter() { + if **val != Value::Int(0) { + return Some(i) + } + } + } + return None +} + +fn mat_scale_pivot_row( + row: usize, + mat: &mut Matrix +) -> Result<()> { + let scale = mat.get(row, row)?; + if scale.is_zero() { + return Ok(()) + } + for col in 0..mat.domain { + let res = (mat.get(row, col)?.clone() / scale.clone())?; + mat.set(row, col, res)?; + } + Ok(()) +} + +fn mat_get_non_zero_pivot_row( + row: usize, + mat: &mut Matrix, +) -> Result<()> { + let col = row; + let test = mat.get(row, col)?; + if test.is_zero() { + for r in row..mat.codomain { + let cur = mat.get(r, col)?; + if !cur.is_zero() { + mat_swap_rows(row, r, mat)?; + break; + } + } + } + mat_scale_pivot_row(row, mat)?; + Ok(()) +} + +fn mat_rref(mat: Matrix, full_rref: bool) -> Result { + let mut mat = mat; + let Some(start) = mat_find_non_zero_col(&mat) else { + return Ok(mat) + }; + let end = mat.domain.min(mat.codomain); + for col in start..end { + let pivot_row = col; + mat_get_non_zero_pivot_row(pivot_row, &mut mat)?; + if mat.get(pivot_row, col)?.is_zero() { + break + } + let min = if full_rref { 0 } else { col }; + for row in min..mat.codomain { + if row == pivot_row { continue; }; + let scale = mat.get(row, col)?; + mat_gauss_row_operation(row, pivot_row, scale, &mut mat)?; + } + } + Ok(mat) +} + +#[native_func(1)] +fn rref(_: VmArgs, args: Vec) -> Result { + let [value] = unpack_args!(args); + let mat = match value { + Value::Matrix(m) => m, + Value::List(l) => Matrix::from_list(l.to_vec()).into(), + _ => return error!("rref must be given a matrix") + }; + Ok(Value::Matrix(mat_rref(mat.into_inner(), true)?.into())) +} + +#[native_func(1)] +fn mat_ref(_: VmArgs, args: Vec) -> Result { + let [value] = unpack_args!(args); + let mat = match value { + Value::Matrix(m) => m, + Value::List(l) => Matrix::from_list(l.to_vec()).into(), + _ => return error!("ref must be given a matrix") + }; + Ok(Value::Matrix(mat_rref(mat.into_inner(), false)?.into())) +} + +fn mat_det(mat: Matrix) -> Result { + if mat.domain == 1 { + return Ok(mat.get(0,0)?) + } + if mat.domain == 2 { + let a = mat.get(0,0)? * mat.get(1,1)?; + let b = mat.get(0,1)? * mat.get(1,0)?; + return Ok((a? - b?)?) + } + let mut res = Value::Int(0); + for col in 0..mat.domain { + let sub_values = mat.rows() + .skip(1) + .map(|r| + r.into_iter() + .enumerate() + .filter(|(idx,_)| *idx != col) + .map(|(_, v)| v.clone()) + .collect::>() + ) + .reduce(|mut a, b| {a.extend(b); a}) + .unwrap(); + let sub = Matrix::new(mat.domain - 1, mat.domain - 1, sub_values); + let val = mat.get(0, col)?; + let part = (val * mat_det(sub)?)?; + if col % 2 == 0 { + res = (res + part)?; + } else { + res = (res - part)?; + } + } + Ok(res) +} + +#[native_func(1)] +fn det(_: VmArgs, args: Vec) -> Result { + let [value] = unpack_args!(args); + let mat = match value { + Value::Matrix(m) if m.domain == m.codomain => m, + Value::List(l) if l.len() == 1 => Matrix::from_list(l.to_vec()).into(), + _ => return error!("det requires a square matrix") + }; + let mat = mat.into_inner(); + Ok(mat_det(mat)?) +} + +fn mat_ident(dim: usize) -> Matrix { + let len = dim * dim; + let mut values = vec![Value::Int(0); len]; + let mut idx = 0; + loop { + if idx >= len { break }; + values[idx] = Value::Int(1); + idx += dim + 1; + } + Matrix::new(dim, dim, values) +} + +#[native_func(1)] +fn ident(_: VmArgs, args: Vec) -> Result { + let [value] = unpack_args!(args); + let dim = match value { + Value::Int(i) if i > 0 => i, + Value::Ratio(r) + if *r.denom() == 1 && + *r.numer() > 0 + => *r.numer(), + _ => return error!("ident requries a positive [Int] dimension") + }; + Ok(Value::Matrix(mat_ident(dim as usize).into())) +} + +fn mat_splith(mat: Matrix) -> (Matrix, Matrix) { + let mut m1 = Vec::new(); + let mut m2 = Vec::new(); + + mat.rows() + .for_each(|r| { + let split = r.len() / 2; + r.into_iter().enumerate().for_each(|(i, v)| { + if i < split { + m1.push(v.clone()); + } else { + m2.push(v.clone()); + } + }) + }); + + let m1 = Matrix::new(mat.domain/2, mat.codomain, m1); + let m2 = Matrix::new(mat.domain/2, mat.codomain, m2); + (m1, m2) +} + +#[native_func(1)] +fn inv(_: VmArgs, args: Vec) -> Result { + let [value] = unpack_args!(args); + let mat = match value { + Value::Matrix(m) if m.domain == m.codomain => m, + Value::List(l) if l.len() == 1 => Matrix::from_list(l.to_vec()).into(), + _ => return error!("det requires a square matrix") + }; + let mat = mat.into_inner(); + let ident = mat_ident(mat.domain); + let joined = mat.join_right(&ident)?; + let refed = mat_rref(joined, true)?; + let (new_ident, new_inv) = mat_splith(refed); + + if new_ident == ident { + Ok(Value::Matrix(new_inv.into())) + } else { + error!("matrix does not have an inverse") + } +} + +macro_rules! mathr { + ($type:ident) => { + #[native_func(1)] + fn $type(_: VmArgs, args: Vec) -> Result { + use Value as V; + let [value] = unpack_args!(args); + match value { + V::Int(i) => Ok(V::Int(i)), + V::Ratio(r) => Ok(V::Ratio(r.$type())), + V::Float(f) => Ok(V::Float(f.$type())), + v => error!("cannot compute {} on {v}", stringify!($type)) + } + } + }; +} + +macro_rules! trig { + ($type:ident) => { + #[native_func(1)] + fn $type(_: VmArgs, args: Vec) -> Result { + use Value as V; + let [value] = unpack_args!(args); + match value.floaty() { + V::Float(f) => Ok(V::Float(f.$type())), + V::Complex(c) => Ok(V::Complex(c.$type())), + v => error!("cannot compute {} on {v}", stringify!($type)) + } + } + }; +} + +macro_rules! trigf { + ($type:ident, $str:ident) => { + #[native_func(1)] + fn $str(_: VmArgs, args: Vec) -> Result { + use Value as V; + let [value] = unpack_args!(args); + match value.floaty() { + V::Float(f) => Ok(V::Float(f.$type())), + v => error!("cannot compute {} on {v}", stringify!($str)) + } + } + }; +} + +#[native_func(2)] +fn log(_: VmArgs, args: Vec) -> Result { + use Value as V; + let [base, value] = unpack_args!(args); + match (base.floaty(), value.floaty()) { + (V::Float(base), V::Float(arg)) => Ok(V::Float(arg.log(base))), + (V::Float(base), V::Complex(arg)) => Ok(V::Complex(arg.log(base))), + (V::Complex(base), V::Float(arg)) => Ok(V::Complex(arg.ln() / base.ln())), + (V::Complex(base), V::Complex(arg)) => Ok(V::Complex(arg.ln() / base.ln())), + (base, arg) => error!("cannot compute log base {base} argument {arg}") + } +} + +#[native_func(1)] +fn abs(_: VmArgs, args: Vec) -> Result { + use Value as V; + let [value] = unpack_args!(args); + match value { + V::Int(i) => Ok(V::Int(i.abs())), + V::Float(f) => Ok(V::Float(f.abs())), + V::Ratio(r) => Ok(V::Ratio(Rational64::new(r.numer().abs(), r.denom().abs()))), + V::Complex(c) => Ok(V::Float(c.norm())), + arg => error!("cannot compute abs for {arg}") + } +} + +#[native_func(1)] +fn fract(_: VmArgs, args: Vec) -> Result { + use Value as V; + let [value] = unpack_args!(args); + match value { + V::Int(_) => Ok(V::Int(0)), + V::Float(f) => Ok(V::Float(f.fract())), + V::Ratio(r) => Ok(V::Ratio(r.fract())), + arg => error!("cannot compute fract for {arg}") + } +} + +#[native_func(1)] +fn sign(_: VmArgs, args: Vec) -> Result { + use Value as V; + let [value] = unpack_args!(args); + match value { + V::Int(i) => Ok(V::Int(i.signum())), + V::Ratio(r) => Ok(V::Int(r.numer().signum())), + V::Float(f) => Ok(V::Float(f.signum())), + arg => error!("cannot compute sign for {arg}") + } +} + +#[native_func(1)] +fn int(_: VmArgs, args: Vec) -> Result { + use Value as V; + let [value] = unpack_args!(args); + match value { + V::Int(i) => Ok(V::Int(i)), + V::Ratio(r) => Ok(V::Int(r.numer() / r.denom())), + V::Float(f) => Ok(V::Int(f as i64)), + V::Complex(c) => Ok(V::Int(c.re as i64)), + arg => error!("cannot cast {arg} to int") + } +} + +#[native_func(1)] +fn ratio(_: VmArgs, args: Vec) -> Result { + use Value as V; + let [value] = unpack_args!(args); + match value { + V::Int(i) => Ok(V::Ratio(Rational64::new(i, 1))), + V::Ratio(r) => Ok(V::Ratio(r)), + V::Float(f) => Ok(V::Ratio(Rational64::approximate_float(f).unwrap_or(Rational64::new(0, 1)))), + V::Complex(c) => Ok(V::Ratio(Rational64::approximate_float(c.re).unwrap_or(Rational64::new(0, 1)))), + arg => error!("cannot cast {arg} to ratio") + } +} + +#[native_func(1)] +fn float(_: VmArgs, args: Vec) -> Result { + use Value as V; + let [value] = unpack_args!(args); + match value { + V::Int(i) => Ok(V::Float(i as f64)), + V::Ratio(r) => Ok(V::Float((*r.numer() as f64) / (*r.denom() as f64))), + V::Float(f) => Ok(V::Float(f)), + V::Complex(c) => Ok(V::Float(c.re)), + arg => error!("cannot cast {arg} to float") + } +} + +#[native_func(1)] +fn complex(_: VmArgs, args: Vec) -> Result { + use Value as V; + let [value] = unpack_args!(args); + match value { + V::Int(i) => Ok(V::Complex(Complex64::new(i as f64, 0.0))), + V::Ratio(r) => Ok(V::Complex(Complex64::new((*r.numer() as f64) / (*r.denom() as f64), 0.0))), + V::Float(f) => Ok(V::Complex(Complex64::new(f, 0.0))), + V::Complex(c) => Ok(V::Complex(c)), + arg => error!("cannot cast {arg} to float") + } +} + +#[native_func(1)] +fn mat(_: VmArgs, args: Vec) -> Result { + use Value as V; + let [value] = unpack_args!(args); + match value { + V::List(l) => Ok(V::Matrix(Matrix::from_list(l.to_vec()).into())), + V::Matrix(m) => Ok(V::Matrix(m)), + arg => error!("cannot cast {arg} to mat") + } +} + +#[native_func(1)] +fn numer(_: VmArgs, args: Vec) -> Result { + use Value as V; + let [value] = unpack_args!(args); + match value { + V::Int(i) => Ok(V::Int(i)), + V::Ratio(r) => Ok(V::Int(*r.numer())), + _ => error!("numer can only take a integer or ratio") + } +} + +#[native_func(1)] +fn denom(_: VmArgs, args: Vec) -> Result { + use Value as V; + let [value] = unpack_args!(args); + match value { + V::Int(_) => Ok(V::Int(1)), + V::Ratio(r) => Ok(V::Int(*r.denom())), + _ => error!("denom can only take a integer or ratio") + } +} + +#[native_func(1)] +fn re(_: VmArgs, args: Vec) -> Result { + use Value as V; + let [value] = unpack_args!(args); + match value { + V::Int(_) | V::Ratio(_) | V::Float(_) => Ok(value), + V::Complex(c) => Ok(V::Float(c.re)), + _ => error!("re can only take a valid number") + } +} + +#[native_func(1)] +fn im(_: VmArgs, args: Vec) -> Result { + use Value as V; + let [value] = unpack_args!(args); + match value { + V::Int(_) | V::Ratio(_) | V::Float(_ )=> Ok(V::Int(0)), + V::Complex(c) => Ok(V::Float(c.im)), + _ => error!("re can only take a valid number") + } +} + +#[native_func(1)] +fn cis(_: VmArgs, args: Vec) -> Result { + let [value] = unpack_args!(args); + match value.floaty() { + Value::Float(f) => Ok(Value::Complex(Complex64::cis(f))), + Value::Complex(c) => Ok((Value::Complex(Complex64::cis(c.re)) * Value::Float((-c.im).exp()))?), + _ => error!("cis can only take floats") + } +} + +#[native_func(1)] +fn is_finite(_: VmArgs, args: Vec) -> Result { + use Value as V; + let [value] = unpack_args!(args); + match value { + V::Int(_) | V::Ratio(_) => Ok(V::Bool(true)), + V::Float(f) => Ok(V::Bool(f.is_finite())), + V::Complex(c) => Ok(V::Bool(c.is_finite())), + _ => error!("is_finite can only take a valid number") + } +} + +#[native_func(1)] +fn is_infinite(_: VmArgs, args: Vec) -> Result { + use Value as V; + let [value] = unpack_args!(args); + match value { + V::Int(_) | V::Ratio(_) => Ok(V::Bool(false)), + V::Float(f) => Ok(V::Bool(f.is_infinite())), + V::Complex(c) => Ok(V::Bool(c.is_infinite())), + _ => error!("is_infinite can only take a valid number") + } +} + +#[native_func(1)] +fn is_nan(_: VmArgs, args: Vec) -> Result { + use Value as V; + let [value] = unpack_args!(args); + match value { + V::Int(_) | V::Ratio(_) => Ok(V::Bool(false)), + V::Float(f) => Ok(V::Bool(f.is_nan())), + V::Complex(c) => Ok(V::Bool(c.is_nan())), + _ => error!("is_nan can only take a valid number") + } +} + +#[native_func(1)] +fn is_normal(_: VmArgs, args: Vec) -> Result { + use Value as V; + let [value] = unpack_args!(args); + match value { + V::Int(_) | V::Ratio(_) => Ok(V::Bool(true)), + V::Float(f) => Ok(V::Bool(f.is_normal())), + V::Complex(c) => Ok(V::Bool(c.is_normal())), + _ => error!("is_normal can only take a valid number") + } +} + +#[native_func(1)] +fn is_subnormal(_: VmArgs, args: Vec) -> Result { + use Value as V; + let [value] = unpack_args!(args); + match value { + V::Int(_) | V::Ratio(_) => Ok(V::Bool(false)), + V::Float(f) => Ok(V::Bool(f.is_subnormal())), + _ => error!("is_subnormal can only take subnormal") + } +} + +#[native_func(1)] +fn is_sign_positive(_: VmArgs, args: Vec) -> Result { + use Value as V; + let [value] = unpack_args!(args); + match value { + V::Int(i) => Ok(V::Bool(i > 0)), + V::Ratio(r) => Ok(V::Bool(*r.numer() > 0)), + V::Float(f) => Ok(V::Bool(f.is_sign_positive())), + _ => error!("is_sign_positive can only take a real number") + } +} + +#[native_func(1)] +fn is_sign_negative(_: VmArgs, args: Vec) -> Result { + use Value as V; + let [value] = unpack_args!(args); + match value { + V::Int(i) => Ok(V::Bool(i < 0)), + V::Ratio(r) => Ok(V::Bool(*r.numer() < 0)), + V::Float(f) => Ok(V::Bool(f.is_sign_negative())), + _ => error!("is_sign_negative can only take a real number") + } +} + +#[native_func(1)] +fn is_zero(_: VmArgs, args: Vec) -> Result { + use Value as V; + let [value] = unpack_args!(args); + match value { + V::Int(i) => Ok(V::Bool(i == 0)), + V::Ratio(r) => Ok(V::Bool(*r.numer() == 0 && *r.denom() != 0)), + V::Float(f) => Ok(V::Bool(f == 0.0)), + V::Complex(c) => Ok(V::Bool(c.re == 0.0 && c.im == 0.0)), + _ => error!("is_zero can only take a valid number") + } +} + +#[native_func(2)] +fn mat_joinh(_: VmArgs, args: Vec) -> Result { + let [l, r] = unpack_args!(args); + let (l, r) = match (l, r) { + (Value::List(l), Value::List(r)) => (Matrix::from_list(l.to_vec()), Matrix::from_list(r.to_vec())), + (Value::List(l), Value::Matrix(r)) => (Matrix::from_list(l.to_vec()), r.into_inner()), + (Value::Matrix(l), Value::List(r)) => (l.into_inner(), Matrix::from_list(r.to_vec())), + (Value::Matrix(l), Value::Matrix(r)) => (l.into_inner(), r.into_inner()), + _ => return error!("mat_joinh takes two matrices") + }; + let mat = l.join_right(&r)?; + Ok(Value::Matrix(mat.into())) +} + +#[native_func(2)] +fn mat_joinv(_: VmArgs, args: Vec) -> Result { + let [l, r] = unpack_args!(args); + let (l, r) = match (l, r) { + (Value::List(l), Value::List(r)) => (Matrix::from_list(l.to_vec()), Matrix::from_list(r.to_vec())), + (Value::List(l), Value::Matrix(r)) => (Matrix::from_list(l.to_vec()), r.into_inner()), + (Value::Matrix(l), Value::List(r)) => (l.into_inner(), Matrix::from_list(r.to_vec())), + (Value::Matrix(l), Value::Matrix(r)) => (l.into_inner(), r.into_inner()), + _ => return error!("mat_joinv takes two matrices") + }; + let mat = l.join_bottom(&r)?; + Ok(Value::Matrix(mat.into())) +} + +mathr!(floor); +mathr!(ceil); +mathr!(round); +mathr!(trunc); +trig!(sqrt); +trig!(cbrt); +trig!(ln); +trig!(log2); +trig!(log10); +trig!(exp); +trig!(exp2); +trig!(sin); +trig!(cos); +trig!(tan); +trig!(sinh); +trig!(cosh); +trig!(tanh); +trig!(asin); +trig!(acos); +trig!(atan); +trig!(asinh); +trig!(acosh); +trig!(atanh); +trigf!(to_degrees, deg); +trigf!(to_radians, rad); + +pub fn load(vm: &mut Vm) { + vm.load_global_fn(trans(), "trans"); + vm.load_global_fn(mat_ref(), "ref"); + vm.load_global_fn(rref(), "rref"); + vm.load_global_fn(det(), "det"); + vm.load_global_fn(ident(), "ident"); + vm.load_global_fn(inv(), "inv"); + vm.load_global_fn(mat_joinh(), "mat_joinh"); + vm.load_global_fn(mat_joinv(), "mat_joinv"); + + vm.load_global(Value::Float(PI), "pi"); + vm.load_global(Value::Float(TAU), "tau"); + vm.load_global(Value::Float(E), "e"); + vm.load_global(Value::Float(NAN), "nan"); + vm.load_global(Value::Float(NAN), "NaN"); + vm.load_global(Value::Float(INFINITY), "inf"); + + vm.load_global_fn(int(), "int"); + vm.load_global_fn(ratio(), "ratio"); + vm.load_global_fn(float(), "float"); + vm.load_global_fn(complex(), "complex"); + vm.load_global_fn(mat(), "mat"); + vm.load_global_fn(abs(), "abs"); + vm.load_global_fn(sign(), "sign"); + vm.load_global_fn(floor(), "floor"); + vm.load_global_fn(ceil(), "ceil"); + vm.load_global_fn(round(), "round"); + vm.load_global_fn(trunc(), "trunc"); + vm.load_global_fn(fract(), "fract"); + vm.load_global_fn(sqrt(), "sqrt"); + vm.load_global_fn(cbrt(), "cbrt"); + vm.load_global_fn(ln(), "ln"); + vm.load_global_fn(log(), "log"); + vm.load_global_fn(log2(), "log2"); + vm.load_global_fn(log10(), "log10"); + vm.load_global_fn(exp(), "exp"); + vm.load_global_fn(exp2(), "exp2"); + vm.load_global_fn(sin(), "sin"); + vm.load_global_fn(cos(), "cos"); + vm.load_global_fn(tan(), "tan"); + vm.load_global_fn(sinh(), "sinh"); + vm.load_global_fn(cosh(), "cosh"); + vm.load_global_fn(tanh(), "tanh"); + vm.load_global_fn(asin(), "asin"); + vm.load_global_fn(acos(), "acos"); + vm.load_global_fn(atan(), "atan"); + vm.load_global_fn(asinh(), "asinh"); + vm.load_global_fn(acosh(), "acosh"); + vm.load_global_fn(atanh(), "atanh"); + vm.load_global_fn(deg(), "deg"); + vm.load_global_fn(rad(), "rad"); + vm.load_global_fn(cis(), "cis"); + + vm.load_global_fn(denom(), "denom"); + vm.load_global_fn(numer(), "numer"); + vm.load_global_fn(re(), "re"); + vm.load_global_fn(im(), "im"); + + vm.load_global_fn(is_finite(), "is_finite"); + vm.load_global_fn(is_infinite(), "is_infinite"); + vm.load_global_fn(is_nan(), "is_nan"); + vm.load_global_fn(is_zero(), "is_zero"); + vm.load_global_fn(is_normal(), "is_normal"); + vm.load_global_fn(is_subnormal(), "is_subnormal"); + vm.load_global_fn(is_sign_negative(), "is_sign_negative"); + vm.load_global_fn(is_sign_positive(), "is_sign_positive"); +} diff --git a/matrix-std/src/sys.rs b/matrix-std/src/sys.rs new file mode 100644 index 0000000..609e72d --- /dev/null +++ b/matrix-std/src/sys.rs @@ -0,0 +1,252 @@ +use std::{process::{exit, Command, Stdio, Child}, env, rc::Rc, io::{Read, self}, cell::RefCell, fs::{File, self}, os::fd::FromRawFd, sync::OnceLock, path::PathBuf}; + +use matrix_lang::prelude::*; +use matrix_macros::native_func; +use os_info::Info; +use crate::{VmArgs, error, unpack_args}; + +#[native_func(1)] +fn sys_exit(_: VmArgs, args: Vec) -> Result { + let [value] = unpack_args!(args); + let Value::Int(i) = value else { + return error!("exit requires a int exit code") + }; + exit(i as i32); +} + +#[native_func(0)] +fn argv(_: VmArgs, _: Vec) -> Result { + Ok(Value::List( + Gc::new( + env::args() + .map(|a| Value::String(Rc::from(a.as_str()))) + .collect() + ))) +} + +#[native_func(1)] +fn env(_: VmArgs, args: Vec) -> Result { + let [value] = unpack_args!(args); + let Value::String(value) = value else { + return error!("env requires a string name") + }; + match std::env::var(value.as_ref()) { + Ok(v) => Ok(Value::String(v.into())), + Err(e) => error!("couldn't read env var: {e}") + } +} + +fn exec_impl(cmd: io::Result) -> Result { + let mut child = match cmd { + Ok(c) => c, + Err(e) => return error!("error executing command: {e}") + }; + let status = match child.wait() { + Ok(s) => s, + Err(e) => return error!("error executing command: {e}") + }; + + let stdout = match child.stdout { + Some(ref mut out) => { + let mut buf = String::new(); + let _ = out.read_to_string(&mut buf); + buf + }, + None => String::new() + }; + + let stderr = match child.stderr { + Some(ref mut err) => { + let mut buf = String::new(); + let _ = err.read_to_string(&mut buf); + buf + }, + None => String::new() + }; + + let mut res = ValueMap::new(); + res.insert(Value::from("success"), Value::Bool(status.success()))?; + res.insert(Value::from("code"), Value::Int(status.code().unwrap_or(0) as i64))?; + res.insert(Value::from("out"), Value::String(stdout.into()))?; + res.insert(Value::from("err"), Value::String(stderr.into()))?; + + Ok(Value::Table(res.into())) +} + +#[native_func(2)] +fn exec(_: VmArgs, args: Vec) -> Result { + let [cmd, args] = unpack_args!(args); + let (cmd, args) = match (cmd, args) { + (Value::String(s), Value::List(l)) => (s, l.into_inner()), + _ => return error!("exec requires a string cmd and string argument list") + }; + let mut sargs = Vec::new(); + for arg in args { + let Value::String(arg) = arg else { + return error!("exec requires a string cmd and string argument list") + }; + sargs.push(arg.to_string()); + }; + let cmd = Command::new(cmd.to_string()) + .args(sargs) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn(); + + exec_impl(cmd) +} + +#[native_func(1)] +fn system(_: VmArgs, args: Vec) -> Result { + let [cmd] = unpack_args!(args); + let Value::String(cmd) = cmd else { + return error!("system requires a full command argument string") + }; + let sh = String::from("/bin/sh"); + let args = vec!["-c".to_string(), cmd.to_string()]; + + let cmd = Command::new(sh) + .args(args) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn(); + + exec_impl(cmd) +} + +#[native_func(1)] +fn systemi(_: VmArgs, args: Vec) -> Result { + let [cmd] = unpack_args!(args); + let Value::String(cmd) = cmd else { + return error!("systemi requires a full command argument string") + }; + let sh = String::from("/bin/sh"); + let args = vec!["-c".to_string(), cmd.to_string()]; + + let cmd = Command::new(sh) + .args(args) + .stdin(Stdio::inherit()) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .spawn(); + + exec_impl(cmd) +} + +fn stdin() -> Value { + let f = unsafe { File::from_raw_fd(0) }; + Value::File(Rc::new(RefCell::new(f))) +} + +fn stdout() -> Value { + let f = unsafe { File::from_raw_fd(1) }; + Value::File(Rc::new(RefCell::new(f))) +} + +fn stderr() -> Value { + let f = unsafe { File::from_raw_fd(2) }; + Value::File(Rc::new(RefCell::new(f))) +} + +const OS_INFO: OnceLock = OnceLock::new(); + +#[native_func(0)] +fn os_type(_: VmArgs, _: Vec) -> Result { + Ok(Value::String(OS_INFO.get_or_init(|| os_info::get()).os_type().to_string().into())) +} + +#[native_func(0)] +fn os_version(_: VmArgs, _: Vec) -> Result { + Ok(Value::String(OS_INFO.get_or_init(|| os_info::get()).version().to_string().into())) +} + +#[native_func(0)] +fn os_edition(_: VmArgs, _: Vec) -> Result { + Ok(Value::String(OS_INFO.get_or_init(|| os_info::get()).edition().unwrap_or("Unknown").into())) +} + +#[native_func(0)] +fn os_bitness(_: VmArgs, _: Vec) -> Result { + Ok(Value::String(OS_INFO.get_or_init(|| os_info::get()).bitness().to_string().into())) +} + +#[native_func(0)] +fn os_arch(_: VmArgs, _: Vec) -> Result { + Ok(Value::String(OS_INFO.get_or_init(|| os_info::get()).architecture().unwrap_or("Unknown").into())) +} + +#[native_func(0)] +fn cwd(_: VmArgs, _: Vec) -> Result { + match env::current_dir() { + Ok(v) => Ok(Value::String(v.into_os_string().into_string().unwrap_or(String::new()).into())), + Err(e) => error!("cant get cwd: {e}") + } +} + +#[native_func(1)] +fn basename(_: VmArgs, args: Vec) -> Result { + let [value] = unpack_args!(args); + let Value::String(value) = value else { + return error!("basename requires a string path") + }; + let path = PathBuf::from(value.to_string()); + match path.file_name() { + Some(p) => Ok(Value::String(p.to_str().unwrap().into())), + None => Ok(Value::String(value.into())) + } +} + +#[native_func(1)] +fn dirname(_: VmArgs, args: Vec) -> Result { + let [value] = unpack_args!(args); + let Value::String(value) = value else { + return error!("basename requires a string path") + }; + let path = PathBuf::from(value.to_string()); + let parent = match path.parent() { + Some(p) => p, + None => path.as_path() + }; + let str = parent.as_os_str().to_str().unwrap(); + match str { + "" => Ok(Value::String(".".into())), + s => Ok(Value::String(s.into())) + } +} + +#[native_func(1)] +fn realpath(_: VmArgs, args: Vec) -> Result { + let [value] = unpack_args!(args); + let Value::String(value) = value else { + return error!("basename requires a string path") + }; + let path = match fs::canonicalize(value.as_ref()) { + Ok(p) => p, + Err(e) => return error!("could not get realpath: {e}") + }; + Ok(Value::String(path.to_str().unwrap().into())) +} + + +pub fn load(vm: &mut Vm) { + vm.load_global_fn(sys_exit(), "exit"); + vm.load_global_fn(argv(), "argv"); + vm.load_global_fn(exec(), "exec"); + vm.load_global_fn(system(), "system"); + vm.load_global_fn(systemi(), "systemi"); + vm.load_global_fn(env(), "env"); + vm.load_global(stdin(), "stdin"); + vm.load_global(stdout(), "stdout"); + vm.load_global(stderr(), "stderr"); + vm.load_global_fn(os_type(), "os_type"); + vm.load_global_fn(os_version(), "os_version"); + vm.load_global_fn(os_edition(), "os_edition"); + vm.load_global_fn(os_bitness(), "os_bitness"); + vm.load_global_fn(os_arch(), "os_arch"); + vm.load_global_fn(cwd(), "cwd"); + vm.load_global_fn(basename(), "basename"); + vm.load_global_fn(dirname(), "dirname"); + vm.load_global_fn(realpath(), "realpath"); +} diff --git a/matrix-stdlib/Cargo.toml b/matrix-stdlib/Cargo.toml deleted file mode 100644 index 6256cfa..0000000 --- a/matrix-stdlib/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "matrix-stdlib" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -anyhow = "1" -matrix = { path = "../matrix" } -matrix-macros = { path = "../matrix-macros" } -os_info = "3" -rand = "0.8" diff --git a/matrix-stdlib/src/core.rs b/matrix-stdlib/src/core.rs deleted file mode 100644 index 2c76497..0000000 --- a/matrix-stdlib/src/core.rs +++ /dev/null @@ -1,298 +0,0 @@ -use std::hash::{DefaultHasher, Hash, Hasher}; - -use matrix::{vm::Vm, value::Value, unpack_args, Result, unpack_varargs}; -use matrix_macros::native_func; -use rand::Rng; -use crate::{VmArgs, next, error}; - - -fn to_radix(r: i64, mut n: i64) -> String { - let mut result = String::new(); - let mut idx = 0; - if n == 0 { - result.push('0'); - return result - } else if n < 0 { - n = -n; - idx = 1; - result.push('-'); - } - while n != 0 { - let c = std::char::from_digit((n % r) as u32, r as u32).unwrap(); - result.insert(idx, c); - n /= r; - } - result -} - -#[native_func(1)] -fn bin(_: VmArgs, args: Vec) -> Result { - let [value] = unpack_args!(args); - match value { - Value::Int(n) => Ok(Value::String(to_radix(2, n).into())), - _ => error!("bin requires a integer agument") - } -} - -#[native_func(1)] -fn sex(_: VmArgs, args: Vec) -> Result { - let [value] = unpack_args!(args); - match value { - Value::Int(n) => Ok(Value::String(to_radix(6, n).into())), - _ => error!("sex requires a integer agument") - } -} - -#[native_func(1)] -fn oct(_: VmArgs, args: Vec) -> Result { - let [value] = unpack_args!(args); - match value { - Value::Int(n) => Ok(Value::String(to_radix(8, n).into())), - _ => error!("oct requires a integer agument") - } -} - -#[native_func(1)] -fn hex(_: VmArgs, args: Vec) -> Result { - let [value] = unpack_args!(args); - match value { - Value::Int(n) => Ok(Value::String(to_radix(16, n).into())), - _ => error!("hex requires a integer agument") - } -} - -#[native_func(2)] -fn radix(_: VmArgs, args: Vec) -> Result { - let [radix, value] = unpack_args!(args); - match (radix, value) { - (Value::Int(r), Value::Int(n)) => Ok(Value::String(to_radix(r, n).into())), - _ => error!("radix requires two integer aguments") - } -} - -#[native_func(1..)] -fn append(_: VmArgs, args: Vec) -> Result { - let ([list], args) = unpack_varargs!(args); - let Value::List(mut list) = list else { - return error!("append requires a list") - }; - for arg in args { - list.push(arg); - }; - Ok(Value::List(list)) -} - -#[native_func(2)] -fn push(_: VmArgs, args: Vec) -> Result { - let [list, value] = unpack_args!(args); - let Value::List(mut list) = list else { - return error!("push requires a list") - }; - list.push(value); - Ok(Value::List(list)) -} - -#[native_func(1)] -fn pop(_: VmArgs, args: Vec) -> Result { - let [list] = unpack_args!(args); - let Value::List(mut list) = list else { - return error!("pop requires a list") - }; - match list.pop() { - Some(v) => Ok(v), - None => Ok(Value::Nil) - } -} - -#[native_func(2)] -fn remove(_: VmArgs, args: Vec) -> Result { - let [list, index] = unpack_args!(args); - let Value::List(mut list) = list else { - return error!("remove requires a list") - }; - let Value::Int(i) = index else { - return error!("remove reuqires a int index"); - }; - if i < 0 || i as usize >= list.len() { - Ok(Value::Nil) - } else { - Ok(list.remove(i as usize)) - } -} - -#[native_func(1)] -fn hash(_: VmArgs, args: Vec) -> Result { - let [value] = unpack_args!(args); - if let Err(e) = value.can_hash() { - return Err(e) - }; - let mut hasher = DefaultHasher::new(); - value.hash(&mut hasher); - let fin = hasher.finish(); - Ok(Value::Int(fin as u32 as i64)) -} - -#[native_func(1)] -fn ord(_: VmArgs, args: Vec) -> Result { - let [value] = unpack_args!(args); - let Value::String(str) = value else { - return error!("ord requires a 1 length string") - }; - if str.len() != 1 { - return error!("ord requires a 1 length string") - } - let char = str.chars().next().unwrap(); - Ok(Value::Int(char as i64)) -} - -#[native_func(1)] -fn chr(_: VmArgs, args: Vec) -> Result { - let [value] = unpack_args!(args); - let Value::Int(i) = value else { - return error!("chr requires an int") - }; - match char::from_u32(i as u32) { - Some(c) => Ok(Value::String(String::from(c).into())), - None => error!("unable to get char from: {}", i as u32) - } -} - -#[native_func(1)] -fn str(_: VmArgs, args: Vec) -> Result { - let [value] = unpack_args!(args); - Ok(Value::String(format!("{value}").into())) -} - -fn partition(list: &mut [Value], lo: usize, hi: usize) -> (usize, usize) { - let pivot = list[(lo + hi) / 2].clone(); - let mut lt = lo; - let mut eq = lo; - let mut gt = hi; - - while eq <= gt { - if list[eq] < pivot { - list.swap(eq, lt); - lt += 1; - eq += 1; - } else if list[eq] > pivot { - list.swap(eq, gt); - gt -= 1; - } else { - eq += 1; - } - } - - (lt, gt) -} - -fn quicksort(list: &mut [Value], lo: usize, hi: usize) { - if lo < hi { - let (lt, gt) = partition(list, lo, hi); - if lt > 0 { - quicksort(list, lo, lt - 1); - } - quicksort(list, gt + 1, hi); - } -} - -#[native_func(1)] -fn sort(_: VmArgs, args: Vec) -> Result { - let [value] = unpack_args!(args); - let Value::List(mut list) = value else { - return error!("sort requires a list") - }; - let hi = list.len() - 1; - quicksort(list.as_mut(), 0, hi); - Ok(Value::List(list)) -} - -#[native_func(0)] -fn rand(_: VmArgs, _: Vec) -> Result { - let mut rng = rand::thread_rng(); - Ok(Value::Float(rng.gen())) -} - -#[native_func(0..)] -fn rand_range(_: VmArgs, args: Vec) -> Result { - use Value as V; - let ([], varargs) = unpack_varargs!(args); - let (min, max, step) = match varargs.as_slice() { - [V::Int(max)] => { - (0, *max, 1) - }, - [V::Int(min), V::Int(max)] => { - (*min, *max, 1) - }, - [V::Int(min), V::Int(max), V::Int(step)] => { - (*min, *max, *step) - }, - _ => return error!("rand_range takes 1 to 3 [Int]'s") - }; - if max <= min { - return Ok(Value::Int(min)) - } - let count = ((max - 1 - min) / step) + 1; - let rng = (rand::thread_rng().gen::() % (count as u32)) as i64; - let i = min + (rng * step); - Ok(Value::Int(i)) -} - -#[native_func(2)] -fn rand_int(_: VmArgs, args: Vec) -> Result { - use Value as V; - let (min, max) = match args.as_slice() { - [V::Int(min), V::Int(max)] => { - (*min, *max + 1) - }, - _ => return error!("rand_int takes 2 [Int]'s") - }; - if max <= min { - return Ok(Value::Int(min)) - } - - let count = max - min; - let rng = (rand::thread_rng().gen::() % (count as u32)) as i64; - let i = min + rng; - Ok(Value::Int(i)) -} - -#[native_func(1)] -fn rand_in((vm, frame): VmArgs, args: Vec) -> Result { - let [iter] = unpack_args!(args); - let iter = iter.into_iter_fn()?; - let mut vals = Vec::new(); - loop { - let val = next!(vm, frame, iter)?; - if val == Value::Nil { break } - vals.push(val); - } - if vals.is_empty() { - return Ok(Value::Nil) - } - let idx = rand::thread_rng().gen::() % vals.len(); - Ok(vals[idx].clone()) -} - -pub fn load(vm: &mut Vm) { - vm.load_global_fn(bin(), "bin"); - vm.load_global_fn(sex(), "sex"); - vm.load_global_fn(oct(), "oct"); - vm.load_global_fn(hex(), "hex"); - vm.load_global_fn(radix(), "radix"); - vm.load_global_fn(str(), "str"); - - vm.load_global_fn(append(), "append"); - vm.load_global_fn(push(), "push"); - vm.load_global_fn(pop(), "pop"); - vm.load_global_fn(remove(), "remove"); - - vm.load_global_fn(hash(), "hash"); - vm.load_global_fn(ord(), "ord"); - vm.load_global_fn(chr(), "chr"); - - vm.load_global_fn(sort(), "sort"); - vm.load_global_fn(rand(), "rand"); - vm.load_global_fn(rand_range(), "rand_range"); - vm.load_global_fn(rand_int(), "rand_int"); - vm.load_global_fn(rand_in(), "rand_in"); -} diff --git a/matrix-stdlib/src/io.rs b/matrix-stdlib/src/io.rs deleted file mode 100644 index d72248c..0000000 --- a/matrix-stdlib/src/io.rs +++ /dev/null @@ -1,175 +0,0 @@ -use std::{io::{self, Read, Write}, cell::RefCell, fs::OpenOptions, rc::Rc}; - -use matrix::{value::Value, self, vm::Vm, Result, unpack_varargs, iter, unpack_args}; -use matrix_macros::native_func; -use crate::{error, VmArgs}; - -#[native_func(0..)] -fn print(_: VmArgs, args: Vec) -> Result { - let ([], varags) = unpack_varargs!(args); - for (i, value) in varags.into_iter().enumerate() { - if i != 0 { - print!(" "); - } - print!("{value}"); - } - Ok(Value::Nil) -} - -#[native_func(0..)] -fn println(_: VmArgs, args: Vec) -> Result { - let ([], varags) = unpack_varargs!(args); - for (i, value) in varags.into_iter().enumerate() { - if i != 0 { - print!(" "); - } - print!("{value}"); - } - print!("\n"); - Ok(Value::Nil) -} - -#[native_func(0)] -fn readln(_: VmArgs, _: Vec) -> Result { - let mut input = String::new(); - match io::stdin().read_line(&mut input) { - Ok(_) => { - match input.pop() { - Some(c) if c == '\n' => {}, - Some(c) => input.push(c), - None => {} - }; - Ok(Value::String(input.into())) - }, - Err(err) => error!("cant read from stdin: {err}") - } -} - -#[native_func(1)] -fn input(_: VmArgs, args: Vec) -> Result { - let [prompt] = unpack_args!(args); - let mut input = String::new(); - print!("{prompt}"); - let _ = io::stdout().flush(); - match io::stdin().read_line(&mut input) { - Ok(_) => { - match input.pop() { - Some(c) if c == '\n' => {}, - Some(c) => input.push(c), - None => {} - }; - Ok(Value::String(input.into())) - }, - Err(err) => error!("cant read from stdin: {err}") - } -} - -#[native_func(0)] -fn readlines(_: VmArgs, _: Vec) -> Result { - let lines = RefCell::new(io::stdin().lines()); - Ok(iter!(move |_,_| { - match lines.borrow_mut().next() { - Some(Ok(line)) => Ok(Value::String(line.into())), - _ => Ok(Value::Nil) - } - })) -} - -#[native_func(2)] -fn file_open(_: VmArgs, args: Vec) -> Result { - let [path, mode] = unpack_args!(args); - let Value::String(mode) = mode else { - return error!("open mode must be a string") - }; - let Value::String(path) = path else { - return error!("open path must be a string") - }; - let file = match mode.as_ref() { - "r" => OpenOptions::new().read(true).open(path.as_ref()), - "w" => OpenOptions::new().write(true).create(true).truncate(true).open(path.as_ref()), - "a" => OpenOptions::new().write(true).create(true).append(true).open(path.as_ref()), - "r+" => OpenOptions::new().read(true).write(true).open(path.as_ref()), - "w+" => OpenOptions::new().read(true).write(true).create(true).truncate(true).open(path.as_ref()), - "a+" => OpenOptions::new().read(true).write(true).create(true).append(true).open(path.as_ref()), - _ => return error!("invalid open mode: {mode}") - }; - - match file { - Ok(file) => Ok(Value::File(Rc::new(RefCell::new(file)))), - Err(err) => return error!("cannot open '{path}': {err}") - } -} - -#[native_func(1)] -fn file_read(_: VmArgs, args: Vec) -> Result { - let [file] = unpack_args!(args); - let Value::File(file) = file else { - return error!("file read requires a file") - }; - let mut contents = String::new(); - if let Err(err) = file.try_borrow_mut().unwrap().read_to_string(&mut contents) { - return error!("cannot read file: '{err}'") - }; - Ok(Value::String(contents.into())) -} - -#[native_func(1)] -fn file_lines(_: VmArgs, args: Vec) -> Result { - let [file] = unpack_args!(args); - let Value::File(file) = file else { - return error!("file read requires a file") - }; - let mut contents = String::new(); - if let Err(err) = file.try_borrow_mut().unwrap().read_to_string(&mut contents) { - return error!("cannot read file: '{err}'") - }; - let lines: Vec> = contents.split_inclusive("\n").map(|s| Rc::from(s)).collect(); - let lines = RefCell::new(lines.into_iter()); - Ok(iter!(move |_,_| { - match lines.borrow_mut().next() { - Some(line) => Ok(Value::String(line)), - None => Ok(Value::Nil) - } - })) -} - -#[native_func(2)] -fn file_write(_: VmArgs, args: Vec) -> Result { - let [file, content] = unpack_args!(args); - let Value::File(file) = file else { - return error!("file write requires a file") - }; - let content = format!("{content}"); - if let Err(err) = file.try_borrow_mut().unwrap().write_all(content.as_bytes()) { - return error!("cannot write file: '{err}'") - }; - Ok(Value::Nil) -} - -#[native_func(0..)] -fn throw(_: VmArgs, args: Vec) -> Result { - let ([], varargs) = unpack_varargs!(args); - - let mut str = String::new(); - for (i, v) in varargs.into_iter().enumerate() { - if i != 0 { - str.push_str(" "); - } - str.push_str(&format!("{v}")); - } - - error!("{str}") -} - -pub fn load(vm: &mut Vm) { - vm.load_global_fn(print(), "print"); - vm.load_global_fn(println(), "println"); - vm.load_global_fn(readln(), "readln"); - vm.load_global_fn(input(), "input"); - vm.load_global_fn(readlines(), "readlines"); - vm.load_global_fn(file_open(), "file_open"); - vm.load_global_fn(file_read(), "file_read"); - vm.load_global_fn(file_lines(), "file_lines"); - vm.load_global_fn(file_write(), "file_write"); - vm.load_global_fn(throw(), "throw"); -} diff --git a/matrix-stdlib/src/iter.rs b/matrix-stdlib/src/iter.rs deleted file mode 100644 index 630e52c..0000000 --- a/matrix-stdlib/src/iter.rs +++ /dev/null @@ -1,538 +0,0 @@ -use std::{cell::RefCell, rc::Rc}; -use matrix::{iter, vm::Vm, value::Value, Result, unpack_varargs, unpack_args}; -use matrix_macros::native_func; -use crate::{error, next, VmArgs}; - -use Value as V; - -#[native_func(1)] -fn len(_: VmArgs, args: Vec) -> Result { - let [value] = unpack_args!(args); - let len = match value { - V::List(l) => l.len(), - V::Matrix(m) => m.values.len(), - V::String(s) => s.len(), - V::Table(t) => t.len(), - _ => 1 - }; - Ok(V::Int(len as i64)) -} - -#[native_func(1)] -fn sum((vm, frame): VmArgs, args: Vec) -> Result { - let [iter] = unpack_args!(args); - let iter = iter.into_iter_fn()?; - let mut res = next!(vm, frame, iter)?; - if res == Value::Nil { - return Ok(res) - }; - loop { - let next = next!(vm, frame, iter)?; - if next == Value::Nil { break } - res = (res + next)?; - } - Ok(res) -} - -#[native_func(0..)] -fn range(_: VmArgs, args: Vec) -> Result { - let ([], varargs) = unpack_varargs!(args); - let (min, max, step) = match varargs.as_slice() { - [V::Int(max)] => { - (0, *max, 1) - }, - [V::Int(min), V::Int(max)] => { - (*min, *max, 1) - }, - [V::Int(min), V::Int(max), V::Int(step)] => { - (*min, *max, *step) - }, - _ => return error!("range takes 1 to 3 [Int]'s") - }; - - let i = RefCell::new(min); - Ok(iter!(move |_,_| { - let curr = *(i.borrow()); - *(i.borrow_mut()) = curr + step; - if curr >= max { - return Ok(V::Nil) - } else { - return Ok(V::Int(curr)) - } - })) -} - -#[native_func(1)] -fn iter(_: VmArgs, args: Vec) -> Result { - let [value] = unpack_args!(args); - Ok(value.into_iter()?) -} - -#[native_func(1)] -fn once(_: VmArgs, args: Vec) -> Result { - let [value] = unpack_args!(args); - let first = RefCell::new(false); - Ok(iter!(move |_,_| { - if *first.borrow() == false { - *first.borrow_mut() = true; - Ok(value.clone()) - } else { - Ok(Value::Nil) - } - })) -} - -#[native_func(1)] -fn list((vm, frame): VmArgs, args: Vec) -> Result { - let [iter] = unpack_args!(args); - let iter = iter.into_iter_fn()?; - let mut list = Vec::new(); - loop { - let res = next!(vm, frame, iter)?; - if res == Value::Nil { break } - list.push(res); - } - Ok(Value::List(list.into())) -} - -#[native_func(2)] -fn map(_: VmArgs, args: Vec) -> Result { - let [fun, iter] = unpack_args!(args); - let iter = iter.into_iter_fn()?; - let Value::Function(fun) = fun else { - return error!("map 1st arg must be a function") - }; - Ok(iter!(move |(vm,frame),_| { - let input = next!(vm, frame, iter)?; - if input == Value::Nil { - return Ok(input) - } - vm.run_fn(frame, fun.clone(), vec![input]) - })) -} - -#[native_func(2)] -fn fold((vm, frame): VmArgs, args: Vec) -> Result { - let [fun, iter] = unpack_args!(args); - let iter = iter.into_iter_fn()?; - let Value::Function(fun) = fun else { - return error!("fold 1st arg must be a function") - }; - let mut res = next!(vm, frame, iter)?; - if res == Value::Nil { - return Ok(Value::Nil) - }; - loop { - let next = next!(vm, frame, iter)?; - if next == Value::Nil { - return Ok(res) - }; - res = vm.run_fn(frame, fun.clone(), vec![res, next])?; - } -} - -#[native_func(3)] -fn foldi((vm, frame): VmArgs, args: Vec) -> Result { - let [init, fun, iter] = unpack_args!(args); - let iter = iter.into_iter_fn()?; - let Value::Function(fun) = fun else { - return error!("foldi 1st arg must be a function") - }; - let mut res = init; - loop { - let next = next!(vm, frame, iter)?; - if next == Value::Nil { - return Ok(res) - }; - res = vm.run_fn(frame, fun.clone(), vec![res, next])?; - } -} - -#[native_func(1)] -fn count((vm, frame): VmArgs, args: Vec) -> Result { - let [iter] = unpack_args!(args); - let iter = iter.into_iter_fn()?; - let mut len = 0; - loop { - let res = vm.run_fn(frame, iter.clone(), vec![])?; - if res == Value::Nil { break }; - len += 1; - } - Ok(Value::Int(len)) -} - -#[native_func(3)] -fn scan(_: VmArgs, args: Vec) -> Result { - let [init, fun, iter] = unpack_args!(args); - let iter = iter.into_iter_fn()?; - let Value::Function(fun) = fun else { - return error!("scan 2nd arg must be a function") - }; - let res = RefCell::new(init); - Ok(iter!(move |(vm,frame),_| { - let next = next!(vm, frame, iter)?; - if next == Value::Nil { - return Ok(Value::Nil) - }; - let res_next = vm.run_fn(frame, fun.clone(), vec![res.borrow().clone(), next])?; - *res.borrow_mut() = res_next; - Ok(res.borrow().clone()) - })) -} - -#[native_func(2)] -fn filter(_: VmArgs, args: Vec) -> Result { - let [fun, iter] = unpack_args!(args); - let iter = iter.into_iter_fn()?; - let Value::Function(fun) = fun else { - return error!("filter 1st arg must be a function") - }; - Ok(iter!(move |(vm,frame),_| { - loop { - let next = next!(vm, frame, iter)?; - if next == Value::Nil { - return Ok(Value::Nil) - } - let res = vm.run_fn(frame, fun.clone(), vec![next.clone()])?; - if !!res { - return Ok(next) - } - } - })) -} - -#[native_func(0..)] -fn chain(_: VmArgs, args: Vec) -> Result { - let ([], iters) = unpack_varargs!(args); - - let mut chaind = Vec::new(); - for iter in iters { - chaind.push(iter.into_iter_fn()?); - } - - chaind.reverse(); - let chaind = RefCell::new(chaind); - Ok(iter!(move |(vm,frame), _| { - loop { - let curr = match chaind.borrow_mut().last() { - Some(iter) => iter.clone(), - None => return Ok(Value::Nil) - }; - match vm.run_fn(frame, curr.clone(), vec![]) { - Ok(Value::Nil) => { - chaind.borrow_mut().pop(); - continue; - }, - v => return v - }; - } - })) -} - -#[native_func(1)] -fn lines(_: VmArgs, args: Vec) -> Result { - let [str] = unpack_args!(args); - let Value::String(str) = str else { - return error!("lines arg must be a string") - }; - let lines: Vec> = str.split_inclusive("\n").map(|s| Rc::from(s)).collect(); - let res = RefCell::new(lines.into_iter()); - Ok(iter!(move |_,_| { - match res.borrow_mut().next() { - Some(line) => Ok(Value::String(line)), - None => Ok(Value::Nil) - } - })) -} - -#[native_func(1)] -fn skip((vm, frame): VmArgs, args: Vec) -> Result { - let [count, iter] = unpack_args!(args); - let iter = iter.into_iter_fn()?; - let Value::Int(i) = count else { - return error!("skip count requires a int") - }; - for _ in 0..i { - next!(vm, frame, iter)?; - } - Ok(Value::Iter(iter)) -} - -#[native_func(2)] -fn alternate(_: VmArgs, args: Vec) -> Result { - let [il, ir] = unpack_args!(args); - let il = il.into_iter_fn()?; - let ir = ir.into_iter_fn()?; - let flag = RefCell::new(Some(0)); - Ok(iter!(move |(vm, frame),_| { - let f = *flag.borrow(); - match f { - Some(0) => { - let val = next!(vm, frame, il)?; - if val == Value::Nil { - *flag.borrow_mut() = None; - } else { - *flag.borrow_mut() = Some(1); - } - Ok(val) - }, - Some(1) => { - let val = next!(vm, frame, ir)?; - if val == Value::Nil { - *flag.borrow_mut() = None; - } else { - *flag.borrow_mut() = Some(0); - } - Ok(val) - }, - _ => Ok(Value::Nil) - } - })) -} - -#[native_func(2)] -fn intersperse(_: VmArgs, args: Vec) -> Result { - let [value, iter] = unpack_args!(args); - let iter = iter.into_iter_fn()?; - let flag = RefCell::new(Some(0)); - Ok(iter!(move |(vm, frame),_| { - let f = *flag.borrow(); - match f { - Some(0) => { - let val = next!(vm, frame, iter)?; - if val == Value::Nil { - *flag.borrow_mut() = None; - } else { - *flag.borrow_mut() = Some(1); - } - Ok(val) - }, - Some(1) => { - let val = value.clone(); - *flag.borrow_mut() = Some(0); - Ok(val) - }, - _ => Ok(Value::Nil) - } - })) -} - -#[native_func(2)] -fn zip(_: VmArgs, args: Vec) -> Result { - let [il, ir] = unpack_args!(args); - let il = il.into_iter_fn()?; - let ir = ir.into_iter_fn()?; - let flag = RefCell::new(true); - Ok(iter!(move |(vm, frame),_| { - let f = *flag.borrow(); - match f { - true => { - let vl = next!(vm, frame, il)?; - let vr = next!(vm, frame, ir)?; - if vl == Value::Nil || vr == Value::Nil { - *flag.borrow_mut() = false; - } - Ok(Value::List(vec![vl, vr].into())) - }, - _ => Ok(Value::Nil) - } - })) -} - -#[native_func(1)] -fn unzip((vm, frame): VmArgs, args: Vec) -> Result { - let [iter] = unpack_args!(args); - let iter = iter.into_iter_fn()?; - let mut ll = Vec::new(); - let mut lr = Vec::new(); - loop { - let val = next!(vm, frame, iter)?; - if val == Value::Nil { - break; - } - let Value::List(vals) = val else { - return error!("unzip only works over a iterator of pairs"); - }; - let vals = vals.into_inner(); - if vals.len() != 2 { - return error!("unzip only works over a iterator of pairs"); - } - let [l, r] = vals.try_into().unwrap(); - ll.push(l); - lr.push(r); - } - let ll = Value::List(ll.into()); - let lr = Value::List(lr.into()); - Ok(Value::List(vec![ll, lr].into())) -} - -#[native_func(1)] -fn cycle((vm, frame): VmArgs, args: Vec) -> Result { - let [iter] = unpack_args!(args); - let iter = iter.into_iter_fn()?; - let mut values = Vec::new(); - loop { - let val = next!(vm, frame, iter)?; - if val == Value::Nil { break }; - values.push(val); - } - let idx = RefCell::new(0_usize); - Ok(iter!(move |_,_| { - let i = *idx.borrow(); - *idx.borrow_mut() += 1; - *idx.borrow_mut() %= values.len(); - Ok(values[i].clone()) - })) -} - -#[native_func(2)] -fn take((vm, frame): VmArgs, args: Vec) -> Result { - let [count, iter] = unpack_args!(args); - let iter = iter.into_iter_fn()?; - let Value::Int(count) = count else { - return error!("take requires an int amount to collect") - }; - let mut values = Vec::new(); - for _ in 0..count { - let val = next!(vm, frame, iter)?; - if val == Value::Nil { break }; - values.push(val); - } - Ok(Value::List(values.into())) -} - -#[native_func(2)] -fn last((vm, frame): VmArgs, args: Vec) -> Result { - let [iter] = unpack_args!(args); - let iter = iter.into_iter_fn()?; - let mut last = Value::Nil; - loop { - let val = next!(vm, frame, iter)?; - if val == Value::Nil { break }; - last = val; - } - Ok(last) -} - -#[native_func(2)] -fn next((vm, frame): VmArgs, args: Vec) -> Result { - let [iter] = unpack_args!(args); - let iter = iter.into_iter_fn()?; - let val = next!(vm, frame, iter)?; - Ok(val) -} - -#[native_func(1)] -fn min((vm, frame): VmArgs, args: Vec) -> Result { - let [iter] = unpack_args!(args); - let iter = iter.into_iter_fn()?; - let mut min = Value::Nil; - loop { - let val = next!(vm, frame, iter)?; - if val == Value::Nil { break }; - if min == Value::Nil || val < min { - min = val; - } - } - Ok(min) -} - -#[native_func(1)] -fn max((vm, frame): VmArgs, args: Vec) -> Result { - let [iter] = unpack_args!(args); - let iter = iter.into_iter_fn()?; - let mut max = Value::Nil; - loop { - let val = next!(vm, frame, iter)?; - if val == Value::Nil { break }; - if max == Value::Nil || val > max { - max = val; - } - } - Ok(max) -} - -#[native_func(1)] -fn rev((vm, frame): VmArgs, args: Vec) -> Result { - let [iter] = unpack_args!(args); - let iter = iter.into_iter_fn()?; - let mut values = Vec::new(); - loop { - let val = next!(vm, frame, iter)?; - if val == Value::Nil { break }; - values.push(val); - } - let values = RefCell::new(values); - Ok(iter!(move |_,_| { - match values.borrow_mut().pop() { - Some(v) => Ok(v), - None => Ok(Value::Nil) - } - })) -} - -#[native_func(1)] -fn enumerate(_: VmArgs, args: Vec) -> Result { - let [iter] = unpack_args!(args); - let iter = iter.into_iter_fn()?; - - let idx = RefCell::new(0_i64); - Ok(iter!(move |(vm, frame),_| { - let val = next!(vm, frame, iter)?; - if val == Value::Nil { - return Ok(Value::Nil) - }; - let curr = *idx.borrow(); - *idx.borrow_mut() += 1; - Ok(Value::List(vec![Value::Int(curr), val].into())) - })) -} - -#[native_func(1)] -fn iterable(_: VmArgs, args: Vec) -> Result { - let [value] = unpack_args!(args); - use Value as V; - match value { - V::Function(_) | - V::List(_) | - V::Range(_) | - V::Iter(_) - => Ok(V::Bool(true)), - _ => Ok(V::Bool(false)) - } -} - -pub fn load(vm: &mut Vm) { - vm.load_global_fn(take(), "take"); - vm.load_global_fn(unzip(), "unzip"); - vm.load_global_fn(count(), "count"); - vm.load_global_fn(len(), "len"); - vm.load_global_fn(sum(), "sum"); - vm.load_global_fn(min(), "min"); - vm.load_global_fn(max(), "max"); - vm.load_global_fn(next(), "next"); - vm.load_global_fn(last(), "last"); - - vm.load_global_fn(list(), "list"); - vm.load_global_fn(fold(), "fold"); - vm.load_global_fn(foldi(), "foldi"); - vm.load_global_fn(scan(), "scan"); - vm.load_global_fn(chain(), "chain"); - vm.load_global_fn(lines(), "lines"); - vm.load_global_fn(skip(), "skip"); - - vm.load_global_fn(once(), "once"); - vm.load_global_fn(iter(), "iter"); - vm.load_global_fn(range(), "range"); - vm.load_global_fn(map(), "map"); - vm.load_global_fn(filter(), "filter"); - vm.load_global_fn(skip(), "skip"); - vm.load_global_fn(zip(), "zip"); - vm.load_global_fn(cycle(), "cycle"); - vm.load_global_fn(alternate(), "alternate"); - vm.load_global_fn(intersperse(), "intersperse"); - vm.load_global_fn(rev(), "rev"); - vm.load_global_fn(enumerate(), "enumerate"); - - vm.load_global_fn(iterable(), "iterable"); -} diff --git a/matrix-stdlib/src/lib.rs b/matrix-stdlib/src/lib.rs deleted file mode 100644 index b4ab658..0000000 --- a/matrix-stdlib/src/lib.rs +++ /dev/null @@ -1,32 +0,0 @@ -use matrix::vm::{Vm, StackFrame}; - -mod core; -mod sys; -mod math; -mod io; -mod iter; - -pub(crate) type VmArgs<'a, 'b> = (&'a mut Vm, &'b mut StackFrame); - -macro_rules! error { - ($($arg:tt)*) => { - Err(format!($($arg)*).into()) - }; -} - -macro_rules! next { - ($vm:expr, $frame:expr, $iter:expr) => { - $vm.run_fn($frame, $iter.clone(), vec![]) - }; -} - -pub(crate) use error; -pub(crate) use next; - -pub fn load(vm: &mut Vm) { - core::load(vm); - sys::load(vm); - io::load(vm); - iter::load(vm); - math::load(vm); -} diff --git a/matrix-stdlib/src/math.rs b/matrix-stdlib/src/math.rs deleted file mode 100644 index 3f33951..0000000 --- a/matrix-stdlib/src/math.rs +++ /dev/null @@ -1,689 +0,0 @@ -use core::f64; -use std::f64::{consts::{PI, E, TAU}, NAN, INFINITY}; - -use matrix::{vm::Vm, value::{Value, Matrix}, Result, unpack_args, Rational64, Complex64}; -use matrix_macros::native_func; -use crate::{error, VmArgs}; - -#[native_func(1)] -fn trans(_: VmArgs, args: Vec) -> Result { - let [value] = unpack_args!(args); - let mat = match value { - Value::Matrix(m) => m, - Value::List(l) => Matrix::from_list(l.to_vec()).into(), - _ => return error!("trans must be given a matrix") - }; - let values = mat - .cols() - .reduce(|mut a, b| {a.extend(b); a}) - .unwrap() - .into_iter() - .map(|e| e.clone()) - .collect(); - Ok(Value::Matrix(Matrix::new(mat.codomain, mat.domain, values).into())) -} - -fn mat_gauss_row_operation( - r1: usize, - r2: usize, - scale: Value, - mat: &mut Matrix -) -> Result<()> { - for col in 0..mat.domain { - let r1v = mat.get(r1, col)?; - let r2v = mat.get(r2, col)?; - let res = (r1v - (r2v * scale.clone())?)?; - mat.set(r1, col, res)?; - } - Ok(()) -} - -fn mat_swap_rows( - r1: usize, - r2: usize, - mat: &mut Matrix -) -> Result<()> { - let cols = mat.domain; - for col in 0..cols { - let a = mat.get(r1, col)?; - let b = mat.get(r2, col)?; - mat.set(r2, col, a)?; - mat.set(r1, col, b)?; - } - Ok(()) -} - -fn mat_find_non_zero_col( - mat: &Matrix -) -> Option { - for (i,col) in mat.cols().enumerate() { - for val in col.iter() { - if **val != Value::Int(0) { - return Some(i) - } - } - } - return None -} - -fn mat_scale_pivot_row( - row: usize, - mat: &mut Matrix -) -> Result<()> { - let scale = mat.get(row, row)?; - if scale.is_zero() { - return Ok(()) - } - for col in 0..mat.domain { - let res = (mat.get(row, col)?.clone() / scale.clone())?; - mat.set(row, col, res)?; - } - Ok(()) -} - -fn mat_get_non_zero_pivot_row( - row: usize, - mat: &mut Matrix, -) -> Result<()> { - let col = row; - let test = mat.get(row, col)?; - if test.is_zero() { - for r in row..mat.codomain { - let cur = mat.get(r, col)?; - if !cur.is_zero() { - mat_swap_rows(row, r, mat)?; - break; - } - } - } - mat_scale_pivot_row(row, mat)?; - Ok(()) -} - -fn mat_rref(mat: Matrix, full_rref: bool) -> Result { - let mut mat = mat; - let Some(start) = mat_find_non_zero_col(&mat) else { - return Ok(mat) - }; - let end = mat.domain.min(mat.codomain); - for col in start..end { - let pivot_row = col; - mat_get_non_zero_pivot_row(pivot_row, &mut mat)?; - if mat.get(pivot_row, col)?.is_zero() { - break - } - let min = if full_rref { 0 } else { col }; - for row in min..mat.codomain { - if row == pivot_row { continue; }; - let scale = mat.get(row, col)?; - mat_gauss_row_operation(row, pivot_row, scale, &mut mat)?; - } - } - Ok(mat) -} - -#[native_func(1)] -fn rref(_: VmArgs, args: Vec) -> Result { - let [value] = unpack_args!(args); - let mat = match value { - Value::Matrix(m) => m, - Value::List(l) => Matrix::from_list(l.to_vec()).into(), - _ => return error!("rref must be given a matrix") - }; - Ok(Value::Matrix(mat_rref(mat.into_inner(), true)?.into())) -} - -#[native_func(1)] -fn mat_ref(_: VmArgs, args: Vec) -> Result { - let [value] = unpack_args!(args); - let mat = match value { - Value::Matrix(m) => m, - Value::List(l) => Matrix::from_list(l.to_vec()).into(), - _ => return error!("ref must be given a matrix") - }; - Ok(Value::Matrix(mat_rref(mat.into_inner(), false)?.into())) -} - -fn mat_det(mat: Matrix) -> Result { - if mat.domain == 1 { - return Ok(mat.get(0,0)?) - } - if mat.domain == 2 { - let a = mat.get(0,0)? * mat.get(1,1)?; - let b = mat.get(0,1)? * mat.get(1,0)?; - return Ok((a? - b?)?) - } - let mut res = Value::Int(0); - for col in 0..mat.domain { - let sub_values = mat.rows() - .skip(1) - .map(|r| - r.into_iter() - .enumerate() - .filter(|(idx,_)| *idx != col) - .map(|(_, v)| v.clone()) - .collect::>() - ) - .reduce(|mut a, b| {a.extend(b); a}) - .unwrap(); - let sub = Matrix::new(mat.domain - 1, mat.domain - 1, sub_values); - let val = mat.get(0, col)?; - let part = (val * mat_det(sub)?)?; - if col % 2 == 0 { - res = (res + part)?; - } else { - res = (res - part)?; - } - } - Ok(res) -} - -#[native_func(1)] -fn det(_: VmArgs, args: Vec) -> Result { - let [value] = unpack_args!(args); - let mat = match value { - Value::Matrix(m) if m.domain == m.codomain => m, - Value::List(l) if l.len() == 1 => Matrix::from_list(l.to_vec()).into(), - _ => return error!("det requires a square matrix") - }; - let mat = mat.into_inner(); - Ok(mat_det(mat)?) -} - -fn mat_ident(dim: usize) -> Matrix { - let len = dim * dim; - let mut values = vec![Value::Int(0); len]; - let mut idx = 0; - loop { - if idx >= len { break }; - values[idx] = Value::Int(1); - idx += dim + 1; - } - Matrix::new(dim, dim, values) -} - -#[native_func(1)] -fn ident(_: VmArgs, args: Vec) -> Result { - let [value] = unpack_args!(args); - let dim = match value { - Value::Int(i) if i > 0 => i, - Value::Ratio(r) - if *r.denom() == 1 && - *r.numer() > 0 - => *r.numer(), - _ => return error!("ident requries a positive [Int] dimension") - }; - Ok(Value::Matrix(mat_ident(dim as usize).into())) -} - -fn mat_splith(mat: Matrix) -> (Matrix, Matrix) { - let mut m1 = Vec::new(); - let mut m2 = Vec::new(); - - mat.rows() - .for_each(|r| { - let split = r.len() / 2; - r.into_iter().enumerate().for_each(|(i, v)| { - if i < split { - m1.push(v.clone()); - } else { - m2.push(v.clone()); - } - }) - }); - - let m1 = Matrix::new(mat.domain/2, mat.codomain, m1); - let m2 = Matrix::new(mat.domain/2, mat.codomain, m2); - (m1, m2) -} - -#[native_func(1)] -fn inv(_: VmArgs, args: Vec) -> Result { - let [value] = unpack_args!(args); - let mat = match value { - Value::Matrix(m) if m.domain == m.codomain => m, - Value::List(l) if l.len() == 1 => Matrix::from_list(l.to_vec()).into(), - _ => return error!("det requires a square matrix") - }; - let mat = mat.into_inner(); - let ident = mat_ident(mat.domain); - let joined = mat.join_right(&ident)?; - let refed = mat_rref(joined, true)?; - let (new_ident, new_inv) = mat_splith(refed); - - if new_ident == ident { - Ok(Value::Matrix(new_inv.into())) - } else { - error!("matrix does not have an inverse") - } -} - -macro_rules! mathr { - ($type:ident) => { - #[native_func(1)] - fn $type(_: VmArgs, args: Vec) -> Result { - use Value as V; - let [value] = unpack_args!(args); - match value { - V::Int(i) => Ok(V::Int(i)), - V::Ratio(r) => Ok(V::Ratio(r.$type())), - V::Float(f) => Ok(V::Float(f.$type())), - v => error!("cannot compute {} on {v}", stringify!($type)) - } - } - }; -} - -macro_rules! trig { - ($type:ident) => { - #[native_func(1)] - fn $type(_: VmArgs, args: Vec) -> Result { - use Value as V; - let [value] = unpack_args!(args); - match value.promote_trig() { - V::Float(f) => Ok(V::Float(f.$type())), - V::Complex(c) => Ok(V::Complex(c.$type())), - v => error!("cannot compute {} on {v}", stringify!($type)) - } - } - }; -} - -macro_rules! trigf { - ($type:ident, $str:ident) => { - #[native_func(1)] - fn $str(_: VmArgs, args: Vec) -> Result { - use Value as V; - let [value] = unpack_args!(args); - match value.promote_trig() { - V::Float(f) => Ok(V::Float(f.$type())), - v => error!("cannot compute {} on {v}", stringify!($str)) - } - } - }; -} - -#[native_func(2)] -fn log(_: VmArgs, args: Vec) -> Result { - use Value as V; - let [base, value] = unpack_args!(args); - match (base.promote_trig(), value.promote_trig()) { - (V::Float(base), V::Float(arg)) => Ok(V::Float(arg.log(base))), - (V::Float(base), V::Complex(arg)) => Ok(V::Complex(arg.log(base))), - (V::Complex(base), V::Float(arg)) => Ok(V::Complex(arg.ln() / base.ln())), - (V::Complex(base), V::Complex(arg)) => Ok(V::Complex(arg.ln() / base.ln())), - (base, arg) => error!("cannot compute log base {base} argument {arg}") - } -} - -#[native_func(1)] -fn abs(_: VmArgs, args: Vec) -> Result { - use Value as V; - let [value] = unpack_args!(args); - match value { - V::Int(i) => Ok(V::Int(i.abs())), - V::Float(f) => Ok(V::Float(f.abs())), - V::Ratio(r) => Ok(V::Ratio(Rational64::new(r.numer().abs(), r.denom().abs()))), - V::Complex(c) => Ok(V::Float(c.norm())), - arg => error!("cannot compute abs for {arg}") - } -} - -#[native_func(1)] -fn fract(_: VmArgs, args: Vec) -> Result { - use Value as V; - let [value] = unpack_args!(args); - match value { - V::Int(_) => Ok(V::Int(0)), - V::Float(f) => Ok(V::Float(f.fract())), - V::Ratio(r) => Ok(V::Ratio(r.fract())), - arg => error!("cannot compute fract for {arg}") - } -} - -#[native_func(1)] -fn sign(_: VmArgs, args: Vec) -> Result { - use Value as V; - let [value] = unpack_args!(args); - match value { - V::Int(i) => Ok(V::Int(i.signum())), - V::Ratio(r) => Ok(V::Int(r.numer().signum())), - V::Float(f) => Ok(V::Float(f.signum())), - arg => error!("cannot compute sign for {arg}") - } -} - -#[native_func(1)] -fn int(_: VmArgs, args: Vec) -> Result { - use Value as V; - let [value] = unpack_args!(args); - match value { - V::Int(i) => Ok(V::Int(i)), - V::Ratio(r) => Ok(V::Int(r.numer() / r.denom())), - V::Float(f) => Ok(V::Int(f as i64)), - V::Complex(c) => Ok(V::Int(c.re as i64)), - arg => error!("cannot cast {arg} to int") - } -} - -#[native_func(1)] -fn ratio(_: VmArgs, args: Vec) -> Result { - use Value as V; - let [value] = unpack_args!(args); - match value { - V::Int(i) => Ok(V::Ratio(Rational64::new(i, 1))), - V::Ratio(r) => Ok(V::Ratio(r)), - V::Float(f) => Ok(V::Ratio(Rational64::approximate_float(f).unwrap_or(Rational64::new(0, 1)))), - V::Complex(c) => Ok(V::Ratio(Rational64::approximate_float(c.re).unwrap_or(Rational64::new(0, 1)))), - arg => error!("cannot cast {arg} to ratio") - } -} - -#[native_func(1)] -fn float(_: VmArgs, args: Vec) -> Result { - use Value as V; - let [value] = unpack_args!(args); - match value { - V::Int(i) => Ok(V::Float(i as f64)), - V::Ratio(r) => Ok(V::Float((*r.numer() as f64) / (*r.denom() as f64))), - V::Float(f) => Ok(V::Float(f)), - V::Complex(c) => Ok(V::Float(c.re)), - arg => error!("cannot cast {arg} to float") - } -} - -#[native_func(1)] -fn complex(_: VmArgs, args: Vec) -> Result { - use Value as V; - let [value] = unpack_args!(args); - match value { - V::Int(i) => Ok(V::Complex(Complex64::new(i as f64, 0.0))), - V::Ratio(r) => Ok(V::Complex(Complex64::new((*r.numer() as f64) / (*r.denom() as f64), 0.0))), - V::Float(f) => Ok(V::Complex(Complex64::new(f, 0.0))), - V::Complex(c) => Ok(V::Complex(c)), - arg => error!("cannot cast {arg} to float") - } -} - -#[native_func(1)] -fn mat(_: VmArgs, args: Vec) -> Result { - use Value as V; - let [value] = unpack_args!(args); - match value { - V::List(l) => Ok(V::Matrix(Matrix::from_list(l.to_vec()).into())), - V::Matrix(m) => Ok(V::Matrix(m)), - arg => error!("cannot cast {arg} to mat") - } -} - -#[native_func(1)] -fn numer(_: VmArgs, args: Vec) -> Result { - use Value as V; - let [value] = unpack_args!(args); - match value { - V::Int(i) => Ok(V::Int(i)), - V::Ratio(r) => Ok(V::Int(*r.numer())), - _ => error!("numer can only take a integer or ratio") - } -} - -#[native_func(1)] -fn denom(_: VmArgs, args: Vec) -> Result { - use Value as V; - let [value] = unpack_args!(args); - match value { - V::Int(_) => Ok(V::Int(1)), - V::Ratio(r) => Ok(V::Int(*r.denom())), - _ => error!("denom can only take a integer or ratio") - } -} - -#[native_func(1)] -fn re(_: VmArgs, args: Vec) -> Result { - use Value as V; - let [value] = unpack_args!(args); - match value { - V::Int(_) | V::Ratio(_) | V::Float(_) => Ok(value), - V::Complex(c) => Ok(V::Float(c.re)), - _ => error!("re can only take a valid number") - } -} - -#[native_func(1)] -fn im(_: VmArgs, args: Vec) -> Result { - use Value as V; - let [value] = unpack_args!(args); - match value { - V::Int(_) | V::Ratio(_) | V::Float(_ )=> Ok(V::Int(0)), - V::Complex(c) => Ok(V::Float(c.im)), - _ => error!("re can only take a valid number") - } -} - -#[native_func(1)] -fn cis(_: VmArgs, args: Vec) -> Result { - let [value] = unpack_args!(args); - match value.promote_trig() { - Value::Float(f) => Ok(Value::Complex(Complex64::cis(f))), - Value::Complex(c) => Ok((Value::Complex(Complex64::cis(c.re)) * Value::Float((-c.im).exp()))?), - _ => error!("cis can only take floats") - } -} - -#[native_func(1)] -fn is_finite(_: VmArgs, args: Vec) -> Result { - use Value as V; - let [value] = unpack_args!(args); - match value { - V::Int(_) | V::Ratio(_) => Ok(V::Bool(true)), - V::Float(f) => Ok(V::Bool(f.is_finite())), - V::Complex(c) => Ok(V::Bool(c.is_finite())), - _ => error!("is_finite can only take a valid number") - } -} - -#[native_func(1)] -fn is_infinite(_: VmArgs, args: Vec) -> Result { - use Value as V; - let [value] = unpack_args!(args); - match value { - V::Int(_) | V::Ratio(_) => Ok(V::Bool(false)), - V::Float(f) => Ok(V::Bool(f.is_infinite())), - V::Complex(c) => Ok(V::Bool(c.is_infinite())), - _ => error!("is_infinite can only take a valid number") - } -} - -#[native_func(1)] -fn is_nan(_: VmArgs, args: Vec) -> Result { - use Value as V; - let [value] = unpack_args!(args); - match value { - V::Int(_) | V::Ratio(_) => Ok(V::Bool(false)), - V::Float(f) => Ok(V::Bool(f.is_nan())), - V::Complex(c) => Ok(V::Bool(c.is_nan())), - _ => error!("is_nan can only take a valid number") - } -} - -#[native_func(1)] -fn is_normal(_: VmArgs, args: Vec) -> Result { - use Value as V; - let [value] = unpack_args!(args); - match value { - V::Int(_) | V::Ratio(_) => Ok(V::Bool(true)), - V::Float(f) => Ok(V::Bool(f.is_normal())), - V::Complex(c) => Ok(V::Bool(c.is_normal())), - _ => error!("is_normal can only take a valid number") - } -} - -#[native_func(1)] -fn is_subnormal(_: VmArgs, args: Vec) -> Result { - use Value as V; - let [value] = unpack_args!(args); - match value { - V::Int(_) | V::Ratio(_) => Ok(V::Bool(false)), - V::Float(f) => Ok(V::Bool(f.is_subnormal())), - _ => error!("is_subnormal can only take subnormal") - } -} - -#[native_func(1)] -fn is_sign_positive(_: VmArgs, args: Vec) -> Result { - use Value as V; - let [value] = unpack_args!(args); - match value { - V::Int(i) => Ok(V::Bool(i > 0)), - V::Ratio(r) => Ok(V::Bool(*r.numer() > 0)), - V::Float(f) => Ok(V::Bool(f.is_sign_positive())), - _ => error!("is_sign_positive can only take a real number") - } -} - -#[native_func(1)] -fn is_sign_negative(_: VmArgs, args: Vec) -> Result { - use Value as V; - let [value] = unpack_args!(args); - match value { - V::Int(i) => Ok(V::Bool(i < 0)), - V::Ratio(r) => Ok(V::Bool(*r.numer() < 0)), - V::Float(f) => Ok(V::Bool(f.is_sign_negative())), - _ => error!("is_sign_negative can only take a real number") - } -} - -#[native_func(1)] -fn is_zero(_: VmArgs, args: Vec) -> Result { - use Value as V; - let [value] = unpack_args!(args); - match value { - V::Int(i) => Ok(V::Bool(i == 0)), - V::Ratio(r) => Ok(V::Bool(*r.numer() == 0 && *r.denom() != 0)), - V::Float(f) => Ok(V::Bool(f == 0.0)), - V::Complex(c) => Ok(V::Bool(c.re == 0.0 && c.im == 0.0)), - _ => error!("is_zero can only take a valid number") - } -} - -#[native_func(2)] -fn mat_joinh(_: VmArgs, args: Vec) -> Result { - let [l, r] = unpack_args!(args); - let (l, r) = match (l, r) { - (Value::List(l), Value::List(r)) => (Matrix::from_list(l.to_vec()), Matrix::from_list(r.to_vec())), - (Value::List(l), Value::Matrix(r)) => (Matrix::from_list(l.to_vec()), r.into_inner()), - (Value::Matrix(l), Value::List(r)) => (l.into_inner(), Matrix::from_list(r.to_vec())), - (Value::Matrix(l), Value::Matrix(r)) => (l.into_inner(), r.into_inner()), - _ => return error!("mat_joinh takes two matrices") - }; - let mat = l.join_right(&r)?; - Ok(Value::Matrix(mat.into())) -} - -#[native_func(2)] -fn mat_joinv(_: VmArgs, args: Vec) -> Result { - let [l, r] = unpack_args!(args); - let (l, r) = match (l, r) { - (Value::List(l), Value::List(r)) => (Matrix::from_list(l.to_vec()), Matrix::from_list(r.to_vec())), - (Value::List(l), Value::Matrix(r)) => (Matrix::from_list(l.to_vec()), r.into_inner()), - (Value::Matrix(l), Value::List(r)) => (l.into_inner(), Matrix::from_list(r.to_vec())), - (Value::Matrix(l), Value::Matrix(r)) => (l.into_inner(), r.into_inner()), - _ => return error!("mat_joinv takes two matrices") - }; - let mat = l.join_bottom(&r)?; - Ok(Value::Matrix(mat.into())) -} - -mathr!(floor); -mathr!(ceil); -mathr!(round); -mathr!(trunc); -trig!(sqrt); -trig!(cbrt); -trig!(ln); -trig!(log2); -trig!(log10); -trig!(exp); -trig!(exp2); -trig!(sin); -trig!(cos); -trig!(tan); -trig!(sinh); -trig!(cosh); -trig!(tanh); -trig!(asin); -trig!(acos); -trig!(atan); -trig!(asinh); -trig!(acosh); -trig!(atanh); -trigf!(to_degrees, deg); -trigf!(to_radians, rad); - -pub fn load(vm: &mut Vm) { - vm.load_global_fn(trans(), "trans"); - vm.load_global_fn(mat_ref(), "ref"); - vm.load_global_fn(rref(), "rref"); - vm.load_global_fn(det(), "det"); - vm.load_global_fn(ident(), "ident"); - vm.load_global_fn(inv(), "inv"); - vm.load_global_fn(mat_joinh(), "mat_joinh"); - vm.load_global_fn(mat_joinv(), "mat_joinv"); - - vm.load_global(Value::Float(PI), "pi"); - vm.load_global(Value::Float(TAU), "tau"); - vm.load_global(Value::Float(E), "e"); - vm.load_global(Value::Float(NAN), "nan"); - vm.load_global(Value::Float(NAN), "NaN"); - vm.load_global(Value::Float(INFINITY), "inf"); - - vm.load_global_fn(int(), "int"); - vm.load_global_fn(ratio(), "ratio"); - vm.load_global_fn(float(), "float"); - vm.load_global_fn(complex(), "complex"); - vm.load_global_fn(mat(), "mat"); - vm.load_global_fn(abs(), "abs"); - vm.load_global_fn(sign(), "sign"); - vm.load_global_fn(floor(), "floor"); - vm.load_global_fn(ceil(), "ceil"); - vm.load_global_fn(round(), "round"); - vm.load_global_fn(trunc(), "trunc"); - vm.load_global_fn(fract(), "fract"); - vm.load_global_fn(sqrt(), "sqrt"); - vm.load_global_fn(cbrt(), "cbrt"); - vm.load_global_fn(ln(), "ln"); - vm.load_global_fn(log(), "log"); - vm.load_global_fn(log2(), "log2"); - vm.load_global_fn(log10(), "log10"); - vm.load_global_fn(exp(), "exp"); - vm.load_global_fn(exp2(), "exp2"); - vm.load_global_fn(sin(), "sin"); - vm.load_global_fn(cos(), "cos"); - vm.load_global_fn(tan(), "tan"); - vm.load_global_fn(sinh(), "sinh"); - vm.load_global_fn(cosh(), "cosh"); - vm.load_global_fn(tanh(), "tanh"); - vm.load_global_fn(asin(), "asin"); - vm.load_global_fn(acos(), "acos"); - vm.load_global_fn(atan(), "atan"); - vm.load_global_fn(asinh(), "asinh"); - vm.load_global_fn(acosh(), "acosh"); - vm.load_global_fn(atanh(), "atanh"); - vm.load_global_fn(deg(), "deg"); - vm.load_global_fn(rad(), "rad"); - vm.load_global_fn(cis(), "cis"); - - vm.load_global_fn(denom(), "denom"); - vm.load_global_fn(numer(), "numer"); - vm.load_global_fn(re(), "re"); - vm.load_global_fn(im(), "im"); - - vm.load_global_fn(is_finite(), "is_finite"); - vm.load_global_fn(is_infinite(), "is_infinite"); - vm.load_global_fn(is_nan(), "is_nan"); - vm.load_global_fn(is_zero(), "is_zero"); - vm.load_global_fn(is_normal(), "is_normal"); - vm.load_global_fn(is_subnormal(), "is_subnormal"); - vm.load_global_fn(is_sign_negative(), "is_sign_negative"); - vm.load_global_fn(is_sign_positive(), "is_sign_positive"); -} diff --git a/matrix-stdlib/src/sys.rs b/matrix-stdlib/src/sys.rs deleted file mode 100644 index d30226f..0000000 --- a/matrix-stdlib/src/sys.rs +++ /dev/null @@ -1,252 +0,0 @@ -use std::{process::{exit, Command, Stdio, Child}, env, rc::Rc, io::{Read, self}, cell::RefCell, fs::{File, self}, os::fd::FromRawFd, sync::OnceLock, path::PathBuf}; - -use matrix::{vm::Vm, value::{Value, ValueMap}, unpack_args, Result, gc::Gc}; -use matrix_macros::native_func; -use os_info::Info; -use crate::{VmArgs, error}; - -#[native_func(1)] -fn sys_exit(_: VmArgs, args: Vec) -> Result { - let [value] = unpack_args!(args); - let Value::Int(i) = value else { - return error!("exit requires a int exit code") - }; - exit(i as i32); -} - -#[native_func(0)] -fn argv(_: VmArgs, _: Vec) -> Result { - Ok(Value::List( - Gc::new( - env::args() - .map(|a| Value::String(Rc::from(a.as_str()))) - .collect() - ))) -} - -#[native_func(1)] -fn env(_: VmArgs, args: Vec) -> Result { - let [value] = unpack_args!(args); - let Value::String(value) = value else { - return error!("env requires a string name") - }; - match std::env::var(value.as_ref()) { - Ok(v) => Ok(Value::String(v.into())), - Err(e) => error!("couldn't read env var: {e}") - } -} - -fn exec_impl(cmd: io::Result) -> Result { - let mut child = match cmd { - Ok(c) => c, - Err(e) => return error!("error executing command: {e}") - }; - let status = match child.wait() { - Ok(s) => s, - Err(e) => return error!("error executing command: {e}") - }; - - let stdout = match child.stdout { - Some(ref mut out) => { - let mut buf = String::new(); - let _ = out.read_to_string(&mut buf); - buf - }, - None => String::new() - }; - - let stderr = match child.stderr { - Some(ref mut err) => { - let mut buf = String::new(); - let _ = err.read_to_string(&mut buf); - buf - }, - None => String::new() - }; - - let mut res = ValueMap::new(); - res.insert(Value::from("success"), Value::Bool(status.success()))?; - res.insert(Value::from("code"), Value::Int(status.code().unwrap_or(0) as i64))?; - res.insert(Value::from("out"), Value::String(stdout.into()))?; - res.insert(Value::from("err"), Value::String(stderr.into()))?; - - Ok(Value::Table(res.into())) -} - -#[native_func(2)] -fn exec(_: VmArgs, args: Vec) -> Result { - let [cmd, args] = unpack_args!(args); - let (cmd, args) = match (cmd, args) { - (Value::String(s), Value::List(l)) => (s, l.into_inner()), - _ => return error!("exec requires a string cmd and string argument list") - }; - let mut sargs = Vec::new(); - for arg in args { - let Value::String(arg) = arg else { - return error!("exec requires a string cmd and string argument list") - }; - sargs.push(arg.to_string()); - }; - let cmd = Command::new(cmd.to_string()) - .args(sargs) - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .spawn(); - - exec_impl(cmd) -} - -#[native_func(1)] -fn system(_: VmArgs, args: Vec) -> Result { - let [cmd] = unpack_args!(args); - let Value::String(cmd) = cmd else { - return error!("system requires a full command argument string") - }; - let sh = String::from("/bin/sh"); - let args = vec!["-c".to_string(), cmd.to_string()]; - - let cmd = Command::new(sh) - .args(args) - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .spawn(); - - exec_impl(cmd) -} - -#[native_func(1)] -fn systemi(_: VmArgs, args: Vec) -> Result { - let [cmd] = unpack_args!(args); - let Value::String(cmd) = cmd else { - return error!("systemi requires a full command argument string") - }; - let sh = String::from("/bin/sh"); - let args = vec!["-c".to_string(), cmd.to_string()]; - - let cmd = Command::new(sh) - .args(args) - .stdin(Stdio::inherit()) - .stdout(Stdio::inherit()) - .stderr(Stdio::inherit()) - .spawn(); - - exec_impl(cmd) -} - -fn stdin() -> Value { - let f = unsafe { File::from_raw_fd(0) }; - Value::File(Rc::new(RefCell::new(f))) -} - -fn stdout() -> Value { - let f = unsafe { File::from_raw_fd(1) }; - Value::File(Rc::new(RefCell::new(f))) -} - -fn stderr() -> Value { - let f = unsafe { File::from_raw_fd(2) }; - Value::File(Rc::new(RefCell::new(f))) -} - -const OS_INFO: OnceLock = OnceLock::new(); - -#[native_func(0)] -fn os_type(_: VmArgs, _: Vec) -> Result { - Ok(Value::String(OS_INFO.get_or_init(|| os_info::get()).os_type().to_string().into())) -} - -#[native_func(0)] -fn os_version(_: VmArgs, _: Vec) -> Result { - Ok(Value::String(OS_INFO.get_or_init(|| os_info::get()).version().to_string().into())) -} - -#[native_func(0)] -fn os_edition(_: VmArgs, _: Vec) -> Result { - Ok(Value::String(OS_INFO.get_or_init(|| os_info::get()).edition().unwrap_or("Unknown").into())) -} - -#[native_func(0)] -fn os_bitness(_: VmArgs, _: Vec) -> Result { - Ok(Value::String(OS_INFO.get_or_init(|| os_info::get()).bitness().to_string().into())) -} - -#[native_func(0)] -fn os_arch(_: VmArgs, _: Vec) -> Result { - Ok(Value::String(OS_INFO.get_or_init(|| os_info::get()).architecture().unwrap_or("Unknown").into())) -} - -#[native_func(0)] -fn cwd(_: VmArgs, _: Vec) -> Result { - match env::current_dir() { - Ok(v) => Ok(Value::String(v.into_os_string().into_string().unwrap_or(String::new()).into())), - Err(e) => error!("cant get cwd: {e}") - } -} - -#[native_func(1)] -fn basename(_: VmArgs, args: Vec) -> Result { - let [value] = unpack_args!(args); - let Value::String(value) = value else { - return error!("basename requires a string path") - }; - let path = PathBuf::from(value.to_string()); - match path.file_name() { - Some(p) => Ok(Value::String(p.to_str().unwrap().into())), - None => Ok(Value::String(value.into())) - } -} - -#[native_func(1)] -fn dirname(_: VmArgs, args: Vec) -> Result { - let [value] = unpack_args!(args); - let Value::String(value) = value else { - return error!("basename requires a string path") - }; - let path = PathBuf::from(value.to_string()); - let parent = match path.parent() { - Some(p) => p, - None => path.as_path() - }; - let str = parent.as_os_str().to_str().unwrap(); - match str { - "" => Ok(Value::String(".".into())), - s => Ok(Value::String(s.into())) - } -} - -#[native_func(1)] -fn realpath(_: VmArgs, args: Vec) -> Result { - let [value] = unpack_args!(args); - let Value::String(value) = value else { - return error!("basename requires a string path") - }; - let path = match fs::canonicalize(value.as_ref()) { - Ok(p) => p, - Err(e) => return error!("could not get realpath: {e}") - }; - Ok(Value::String(path.to_str().unwrap().into())) -} - - -pub fn load(vm: &mut Vm) { - vm.load_global_fn(sys_exit(), "exit"); - vm.load_global_fn(argv(), "argv"); - vm.load_global_fn(exec(), "exec"); - vm.load_global_fn(system(), "system"); - vm.load_global_fn(systemi(), "systemi"); - vm.load_global_fn(env(), "env"); - vm.load_global(stdin(), "stdin"); - vm.load_global(stdout(), "stdout"); - vm.load_global(stderr(), "stderr"); - vm.load_global_fn(os_type(), "os_type"); - vm.load_global_fn(os_version(), "os_version"); - vm.load_global_fn(os_edition(), "os_edition"); - vm.load_global_fn(os_bitness(), "os_bitness"); - vm.load_global_fn(os_arch(), "os_arch"); - vm.load_global_fn(cwd(), "cwd"); - vm.load_global_fn(basename(), "basename"); - vm.load_global_fn(dirname(), "dirname"); - vm.load_global_fn(realpath(), "realpath"); -} diff --git a/matrix/Cargo.lock b/matrix/Cargo.lock deleted file mode 100644 index ed6e714..0000000 --- a/matrix/Cargo.lock +++ /dev/null @@ -1,119 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "aho-corasick" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" -dependencies = [ - "memchr", -] - -[[package]] -name = "anyhow" -version = "1.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "matrix" -version = "0.1.0" -dependencies = [ - "anyhow", - "num-complex", - "num-rational", - "regex", -] - -[[package]] -name = "memchr" -version = "2.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" - -[[package]] -name = "num-bigint" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-complex" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" -dependencies = [ - "autocfg", - "num-bigint", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" -dependencies = [ - "autocfg", -] - -[[package]] -name = "regex" -version = "1.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" diff --git a/matrix/Cargo.toml b/matrix/Cargo.toml deleted file mode 100644 index 64e9210..0000000 --- a/matrix/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "matrix" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -anyhow = "1" -num-complex = "0.4" -num-rational = "0.4" -regex = "1" diff --git a/matrix/src/ast.rs b/matrix/src/ast.rs deleted file mode 100644 index de63630..0000000 --- a/matrix/src/ast.rs +++ /dev/null @@ -1,448 +0,0 @@ -use std::{rc::Rc, ops::{Neg, Not}, fmt::Debug}; -use crate::{lex::{Position, TokenData}, value::{Value, InlineList, InlineMatrix, InlineTable}, Result}; - -#[derive(Debug, Clone, Copy, Eq, PartialEq)] -pub enum UnaryOp { - // normal math - Negate, - // equality - Not, -} - -#[derive(Debug, Clone, Copy, Eq, PartialEq)] -pub enum BinaryOp { - // normal math - Add, - Subtract, - Multiply, - Divide, - Modulo, - Power, - // binary math - BitwiseAnd, - BitwiseOr, - BitwiseXor, - BitwiseShiftLeft, - BitwiseShiftRight, - // equality - Equals, - NotEquals, - GreaterEquals, - LessEquals, - GreaterThan, - LessThan, - // iter - Range, - RangeEq -} - -pub type AstName = (Rc, Position); - -#[derive(Debug, Clone, PartialEq)] -pub enum ExprData { - NoOp, - - Literal(Value), - Ident(Rc), - - UnaryOp(Box, UnaryOp), - BinaryOp(Box, Box, BinaryOp), - - Index(Box, Vec), - FnCall(Box, Vec), - FieldAccess(Box, AstName), - - List(InlineList), - Matrix(InlineMatrix), - Table(InlineTable), - - And(Box, Box), - Or(Box, Box), - - Assign(Box, Box), - - If(Box, Box, Option>), - Function(AstName, Vec, Box, bool), - Lambda(Vec, Box, bool), - - Loop(Box), - While(Box, Box), - DoWhile(Box, Box), - For(AstName, Box, Box), - - Block(Vec), - - Try(Box, AstName, Box), - - Let(AstName, Box), - - Pipeline(Box, Box), - - Continue, - Break, - Return(Box), -} - -#[derive(Clone, PartialEq)] -pub struct Expr { - pub data: ExprData, - pub pos: Position -} - -impl Debug for Expr { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if f.alternate() { - write!(f, "{:#?}", self.data) - } else { - write!(f, "{:?}", self.data) - } - } -} - -impl From<(ExprData, Position)> for Expr { - fn from(value: (ExprData, Position)) -> Self { - Self { data: value.0, pos: value.1 } - } -} - -impl Neg for BinaryOp { - type Output = Option; - fn neg(self) -> Self::Output { - use BinaryOp::*; - Some(match self { - Add => Subtract, - Subtract => Add, - _ => return None - }) - } -} - -impl Not for BinaryOp { - type Output = Option; - fn not(self) -> Self::Output { - use BinaryOp::*; - Some(match self { - Equals => NotEquals, - NotEquals => Equals, - GreaterEquals => LessThan, - LessEquals => GreaterThan, - GreaterThan => LessEquals, - LessThan => GreaterEquals, - _ => return None - }) - } -} - -impl Neg for Expr { - type Output = std::result::Result; - fn neg(mut self) -> Self::Output { - use ExprData as E; - let mut pos = self.pos; - let data = match self.data { - E::Literal(v) => E::Literal(-v), - E::BinaryOp(lhs, rhs, op) => { - let Some(op_n) = -op else { - return Err((E::BinaryOp(lhs, rhs, op), pos).into()); - }; - if let Ok(lhs) = -*lhs.clone() { - pos = lhs.pos; - E::BinaryOp(Box::new(lhs), rhs, op_n) - } else if let Ok(rhs) = -*rhs.clone() { - pos = rhs.pos; - E::BinaryOp(lhs, Box::new(rhs), op_n) - } else { - return Err((E::BinaryOp(lhs, rhs, op), pos).into()); - } - }, - E::UnaryOp(expr, op) => { - match op { - UnaryOp::Negate => { - pos = expr.pos; - expr.data - }, - _ => return Err((E::UnaryOp(expr, op), pos).into()) - } - } - data => return Err((data, pos).into()) - }; - self.data = data; - self.pos = pos; - Ok(self) - } -} - -impl Not for Expr { - type Output = std::result::Result; - fn not(mut self) -> Self::Output { - use ExprData as E; - let mut pos = self.pos; - let data = match self.data { - E::Literal(v) => E::Literal(Value::Bool(!v)), - E::UnaryOp(expr, op) => { - match op { - self::UnaryOp::Not => { - pos = expr.pos; - expr.data - }, - _ => return Err((E::UnaryOp(expr, op), pos).into()) - } - } - data => return Err((data, pos).into()) - }; - self.data = data; - self.pos = pos; - Ok(self) - } -} - -pub fn optimize(mut expr: Expr) -> Result { - use ExprData as E; - let mut pos = expr.pos; - let data: ExprData = match expr.data { - E::UnaryOp(expr, op) => { - let expr = optimize(*expr)?; - match match op { - UnaryOp::Negate => -expr, - UnaryOp::Not => !expr, - } { - Ok(expr) => { - pos = expr.pos; - expr.data - }, - Err(expr) => E::UnaryOp(Box::new(expr), op) - } - }, - E::BinaryOp(lhs, rhs, op) => { - let lhs = optimize(*lhs)?; - let rhs = optimize(*rhs)?; - if let (E::Literal(l), E::Literal(r)) = (lhs.clone().data, rhs.clone().data) { - match Value::binary_op(op, l, r) { - Err(err) => return Err(err), - Ok(value) => E::Literal(value), - } - } else { - E::BinaryOp(Box::new(lhs), Box::new(rhs), op) - } - }, - E::FnCall(ident, values) => { - E::FnCall(ident, values - .into_iter() - .map(optimize) - .collect::>>()?) - } - E::FieldAccess(expr, ident) => { - let expr = optimize(*expr)?; - E::FieldAccess(Box::new(expr), ident) - }, - E::List(list) => - E::List(list.into_iter() - .map(optimize) - .collect::>>()?), - E::Matrix(mat) => - E::Matrix((mat.0, mat.1, - mat.2.into_iter().map(optimize) - .collect::>>()?)), - E::Table(table) => - E::Table(table - .into_iter() - .map(|(k, v)| { - let k = optimize(k)?; - let v = optimize(v)?; - Ok((k, v)) - }).collect::>>()?), - E::And(lhs, rhs) => { - let lhs = optimize(*lhs)?; - let rhs = optimize(*rhs)?; - if let (E::Literal(l), r) = (lhs.clone().data, rhs.clone().data) { - match !!l.clone() { - true => { - pos = rhs.pos; - r - }, - false => { - pos = lhs.pos; - E::Literal(l) - }, - } - } else { - E::And(Box::new(lhs), Box::new(rhs)) - } - }, - E::Or(lhs, rhs) => { - let lhs = optimize(*lhs)?; - let rhs = optimize(*rhs)?; - if let (E::Literal(l), r) = (lhs.clone().data, rhs.clone().data) { - match !l.clone() { - true => { - pos = rhs.pos; - r - }, - false => { - pos = lhs.pos; - E::Literal(l) - }, - } - } else { - E::And(Box::new(lhs), Box::new(rhs)) - } - }, - E::Block(b) => { - let len = b.len(); - let b: Vec = - b.into_iter() - .map(optimize) - .collect::>>()? - .into_iter() - .enumerate() - .filter(|(i, e)| { - if let E::Literal(_) = e.data { - return i + 1 == len - } - E::NoOp != e.data - }) - .map(|e| e.1) - .collect(); - if b.is_empty() { - E::NoOp - } else { - E::Block(b) - } - }, - E::If(cond, block, else_block) => { - let cond = optimize(*cond)?; - let block = optimize(*block)?; - let else_block = else_block.map(|e| optimize(*e)).transpose()?; - if let E::Literal(lit) = cond.data { - if !!lit { - pos = block.pos; - block.data - } else if let Some(else_block) = else_block { - pos = else_block.pos; - else_block.data - } else { - E::NoOp - } - } else { - E::If(Box::new(cond), Box::new(block), else_block.map(|e| Box::new(e))) - } - }, - E::While(cond, block) => { - let cond = optimize(*cond)?; - let block = optimize(*block)?; - if let E::Literal(lit) = cond.data { - if !!lit { - E::Loop(Box::new(block)) - } else { - E::NoOp - } - } else { - E::While(Box::new(cond), Box::new(block)) - } - }, - E::For(name, cond, block) => { - let cond = optimize(*cond)?; - let block = optimize(*block)?; - E::For(name, Box::new(cond), Box::new(block)) - } - E::DoWhile(block, cond) => { - let cond = optimize(*cond)?; - let block = optimize(*block)?; - if let E::Literal(lit) = &cond.data { - if !!lit.clone() { - E::Loop(Box::new(block)) - } else { - E::DoWhile(Box::new(block), Box::new(cond)) - } - } else { - E::DoWhile(Box::new(block), Box::new(cond)) - } - } - E::Loop(block) => { - let block = Box::new(optimize(*block)?); - E::Loop(block) - }, - E::Try(expr, err, catch) => { - let expr = Box::new(optimize(*expr)?); - let catch = Box::new(optimize(*catch)?); - E::Try(expr, err, catch) - }, - E::Function(ident, params, stmt, varadic) => { - let stmt = Box::new(optimize(*stmt)?); - E::Function(ident, params, stmt, varadic) - } - E::Lambda(params, stmt, varadic) => { - let stmt = Box::new(optimize(*stmt)?); - E::Lambda(params, stmt, varadic) - }, - E::Let(ident, expr) => { - E::Let(ident, Box::new(optimize(*expr)?)) - }, - E::Assign(lhs, rhs) => { - let lhs = Box::new(optimize(*lhs)?); - let rhs = Box::new(optimize(*rhs)?); - E::Assign(lhs, rhs) - }, - E::Return(expr) => { - let expr = Box::new(optimize(*expr)?); - E::Return(expr) - }, - E::Pipeline(lhs, rhs) => { - let lhs = Box::new(optimize(*lhs)?); - let rhs = Box::new(optimize(*rhs)?); - E::Pipeline(lhs, rhs) - } - data => data - }; - expr.data = data; - expr.pos = pos; - Ok(expr) -} - -impl From for UnaryOp { - fn from(value: TokenData) -> Self { - use TokenData as T; - match value { - T::Subtract => Self::Negate, - T::Not => Self::Not, - _ => panic!("aaaaa") - } - } -} - -impl From for BinaryOp { - fn from(value: TokenData) -> Self { - use TokenData as T; - match value { - T::Equal => Self::Equals, - T::NotEqual => Self::NotEquals, - T::GreaterEqual => Self::GreaterEquals, - T::LessEqual => Self::LessEquals, - T::GreaterThan => Self::GreaterThan, - T::LessThan => Self::LessThan, - T::BitwiseShiftLeft => Self::BitwiseShiftLeft, - T::BitwiseShiftRight => Self::BitwiseShiftRight, - T::BitwiseAnd => Self::BitwiseAnd, - T::BitwiseOr => Self::BitwiseOr, - T::BitwiseXor => Self::BitwiseXor, - T::Add => Self::Add, - T::Subtract => Self::Subtract, - T::Multiply => Self::Multiply, - T::Divide => Self::Divide, - T::Modulo => Self::Modulo, - T::Power => Self::Power, - _ => panic!("aaaaa") - } - } -} - -impl Expr { - pub fn is_assignable(&self) -> bool { - use ExprData as E; - match self.data { - E::Ident(_) => true, - E::Index(_, _) => true, - E::FieldAccess(_, _) => true, - _ => false, - } - } -} diff --git a/matrix/src/chunk.rs b/matrix/src/chunk.rs deleted file mode 100644 index 495b787..0000000 --- a/matrix/src/chunk.rs +++ /dev/null @@ -1,155 +0,0 @@ -use crate::{value::Value, ast::{UnaryOp, BinaryOp}, vm::{Vm, StackFrame}, Result, lex::Position}; -use std::{fmt::{Debug, Display}, rc::Rc}; - -#[derive(Clone, Default)] -pub struct Chunk { - pub constants: Vec, - pub code: Vec, - pub pos: Vec, -} - -impl Chunk { - pub fn new() -> Self { - Self { - constants: Vec::new(), - code: Vec::new(), - pos: Vec::new(), - } - } -} - -impl Debug for Chunk { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Chunk({})", self.code.len()) - } -} - -impl Display for Chunk { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - writeln!(f, "constants: ")?; - for (i, c) in self.constants.iter().enumerate() { - writeln!(f, " {i:04}: {c}")?; - } - writeln!(f, "code:")?; - for (i, ins) in self.code.iter().enumerate() { - writeln!(f, " {i:04}: {ins}")?; - } - Ok(()) - } -} - -pub struct Function { - pub name: Rc, - pub arity: usize, - pub variadic: bool, - pub fun: InnerFunction -} - -#[derive(Clone)] -pub enum InnerFunction { - Compiled(Rc), - Native(Rc) -> Result>), -} - -impl Debug for Function { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - use InnerFunction as F; - match self.fun { - F::Compiled(_) => { - write!(f, "[Function {}]", self.name) - }, - F::Native(_) => { - write!(f, "[NativeFunction {}]", self.name) - } - } - } -} - -#[derive(Clone, Debug)] -#[repr(align(4))] -pub enum Instruction { - NoOp, - - CreateLocal, - LoadLocal(u16), - StoreLocal(u16), - DiscardLocals(u16), - - LoadGlobal(u16), - StoreGlobal(u16), - - Const(u16), - Int(i16), - True, - False, - Nil, - - Dup, Discard(u16), - - UnaryOp(UnaryOp), - BinaryOp(BinaryOp), - - NewList(u16), - NewTable(u16), - NewMatrix(u16, u8), - - Field(u16), - StoreField(u16), - Index(u8), - StoreIndex(u8), - - Jump(u16), - JumpTrue(u16), - JumpFalse(u16), - JumpNil(u16), - - IterCreate, - IterNext, - - Try(u16), - TryEnd, - - Call(u8), - Return, -} - -impl Display for Instruction { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - use Instruction::*; - match self { - NoOp => write!(f, "noop"), - CreateLocal => write!(f, "create local"), - LoadLocal(idx) => write!(f, "load local \x1b[33m{idx}\x1b[0m"), - StoreLocal(idx) => write!(f, "store local \x1b[33m{idx}\x1b[0m"), - DiscardLocals(count) => write!(f, "discard locals \x1b[35m{count}\x1b[0m"), - LoadGlobal(name) => write!(f, "load global \x1b[36m{name}\x1b[0m"), - StoreGlobal(name) => write!(f, "store global \x1b[36m{name}\x1b[0m"), - Const(idx) => write!(f, "const \x1b[33m{idx}\x1b[0m"), - Int(idx) => write!(f, "push \x1b[34m{idx}\x1b[0m"), - True => write!(f, "push \x1b[34mtrue\x1b[0m"), - False => write!(f, "push \x1b[34mfalse\x1b[0m"), - Nil => write!(f, "push \x1b[34mnil\x1b[0m"), - Dup => write!(f, "duplicate"), - Discard(count) => write!(f, "discard \x1b[35m{count}\x1b[0m"), - UnaryOp(op) => write!(f, "unary \x1b[32m{op:?}\x1b[0m"), - BinaryOp(op) => write!(f, "binary \x1b[32m{op:?}\x1b[0m"), - NewList(len) => write!(f, "list \x1b[35m{len}\x1b[0m"), - NewTable(len) => write!(f, "table \x1b[35m{len}\x1b[0m"), - NewMatrix(len, codomain) => write!(f, "matrix \x1b[35m{}\x1b[0mx\x1b[35m{}\x1b[0m", *len / (*codomain as u16), codomain), - Index(idx) => write!(f, "index \x1b[33m{idx}\x1b[0m"), - StoreIndex(idx) => write!(f, "store_index \x1b[33m{idx}\x1b[0m"), - Jump(idx) => write!(f, "jump \x1b[33m{idx}\x1b[0m"), - JumpTrue(idx) => write!(f, "jumpt \x1b[33m{idx}\x1b[0m"), - JumpFalse(idx) => write!(f, "jumpf \x1b[33m{idx}\x1b[0m"), - JumpNil(idx) => write!(f, "jumpn \x1b[33m{idx}\x1b[0m"), - Call(arity) => write!(f, "call \x1b[35m{arity}\x1b[0m"), - Return => write!(f, "return"), - IterCreate => write!(f, "iter create"), - IterNext => write!(f, "iter next"), - Field(name_idx) => write!(f, "field \x1b[33m{name_idx}\x1b[0m"), - StoreField(name_idx) => write!(f, "store field \x1b[33m{name_idx}\x1b[0m"), - Try(idx) => write!(f, "try \x1b[33m{idx}\x1b[0m"), - TryEnd => write!(f, "try end"), - } - } -} diff --git a/matrix/src/compiler.rs b/matrix/src/compiler.rs deleted file mode 100644 index 6b6a94b..0000000 --- a/matrix/src/compiler.rs +++ /dev/null @@ -1,636 +0,0 @@ -use std::{fmt::Display, rc::Rc, cell::RefCell}; -use crate::{ast::{Expr, ExprData, AstName}, chunk::{Function, Instruction, InnerFunction}, chunk::{Chunk, self}, value::Value, Result, lex::Position}; - -use Instruction as I; -use Value as V; -use ExprData as E; - -pub type NamesTable = Rc>>>; - -pub struct CompilerBuilder<'c> { - globals: NamesTable, - names: NamesTable, - repl: bool, - debug: bool, - name: Rc, - parent: Option<&'c Compiler<'c>>, -} - -impl<'c> CompilerBuilder<'c> { - - pub fn new() -> Self { - Self { - globals: Rc::new(RefCell::new(Vec::new())), - repl: false, - debug: false, - name: "".into(), - parent: None, - names: Rc::new(RefCell::new(Vec::new())) - } - } - - pub fn repl(mut self, repl: bool) -> Self { - self.repl = repl; - self - } - - pub fn debug(mut self, debug: bool) -> Self { - self.debug = debug; - self - } - - pub fn globals(mut self, globals: NamesTable) -> Self { - self.globals = globals; - self - } - - pub fn names(mut self, names: NamesTable) -> Self { - self.names = names; - self - } - - pub fn parent(mut self, parent: &'c Compiler) -> Self { - self.parent = Some(parent); - self - } - - pub fn name(mut self, name: Rc) -> Self { - self.name = name; - self - } - - pub fn build(self) -> Compiler<'c> { - Compiler { - name: self.name, - names: self.names, - parent: self.parent, - globals: self.globals, - repl: self.repl, - debug: self.debug, - scopes: Vec::new(), - locals: Vec::new(), - chunk: Chunk::new(), - loop_top: Vec::new(), - loop_bot: Vec::new(), - root_is_block: false, - } - } -} - -pub struct Compiler<'c> { - name: Rc, - parent: Option<&'c Compiler<'c>>, - - locals: Vec>, - globals: Rc>>>, - names: Rc>>>, - - root_is_block: bool, - - scopes: Vec, - chunk: Chunk, - - loop_top: Vec<(usize, usize)>, - loop_bot: Vec, - - repl: bool, - debug: bool, -} - -struct Local { - name: Rc, - idx: usize, - scope: usize -} - -#[derive(Debug, Clone)] -pub enum InnerError { - Undefined(Rc), - Redefined(Rc), - InvAssign(Expr), - InvContinue, - InvBreak, - NotImplemented(&'static str), -} - -#[derive(Debug, Clone)] -pub struct Error { - pos: Position, - err: InnerError, -} - -impl std::error::Error for self::Error {} - -impl Display for self::Error { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - use InnerError as E; - write!(f, "parse failed at {}:{}, ", self.pos.row, self.pos.col)?; - match &self.err { - E::Undefined(name) => write!(f, "value {name} is undefined"), - E::Redefined(name) => write!(f, "cannot redefine {name} in the same scope"), - E::InvAssign(expr) => write!(f, "cannot assign to {expr:?}"), - E::InvContinue => write!(f, "cannot continue outside a loop"), - E::InvBreak => write!(f, "cannot break outside a loop"), - E::NotImplemented(str) => write!(f, "{str} is not implemented yet") - } - } -} - -fn error(err: InnerError, pos: Position) -> Result { - Err(self::Error { - pos, - err - }.into()) -} - -impl<'c> Compiler<'c> { - - fn child(&'c self, name: Rc) -> Self { - CompilerBuilder::new() - .name(name) - .debug(self.debug) - .globals(self.globals.clone()) - .names(self.names.clone()) - .repl(false) - .parent(self) - .build() - } - - fn begin_scope(&mut self) { - if self.root_is_block { - self.root_is_block = false; - } else { - self.scopes.push(self.locals.len()) - } - } - - fn collapse_scopes(&mut self, top_scope: usize) { - let mut cutoff = usize::MAX; - while self.scopes.len() > top_scope { - cutoff = self.scopes.pop().unwrap() - } - if cutoff < self.locals.len() { - self.emit(Instruction::DiscardLocals((self.locals.len() - cutoff) as u16), self.last_pos()); - self.locals.truncate(cutoff); - }; - } - - fn end_scope(&mut self) { - let Some(cutoff) = self.scopes.pop() else { - return; - }; - if cutoff < self.locals.len() { - self.emit(Instruction::DiscardLocals((self.locals.len() - cutoff) as u16), self.last_pos()); - self.locals.truncate(cutoff); - }; - } - - fn create_local(&mut self, name: Rc) { - let local = Local { name, idx: self.locals.len(), scope: self.scopes.len()}; - self.locals.push(Rc::new(local)); - } - - fn create_global(&mut self, name: Rc) -> usize { - self.globals.borrow_mut().push(name); - let c = self.globals.borrow().len() - 1; - c - } - - fn create_local_checked(&mut self, name: Rc, pos: Position) -> Result<()> { - if let Some(local) = self.find_local(&name) { - if local.scope == self.scopes.len() { - return error(InnerError::Redefined(name), pos) - } - }; - self.create_local(name); - Ok(()) - } - - fn create_global_checked(&mut self, name: Rc, pos: Position) -> Result { - if let Some(_) = self.find_global(&name) { - return error(InnerError::Redefined(name).into(), pos) - } - Ok(self.create_global(name)) - } - - fn find_local(&self, name: &str) -> Option> { - for local in self.locals.iter().rev() { - if local.name.as_ref() == name { - return Some(local.clone()) - } - } - None - } - - fn find_global(&self, name: &str) -> Option { - if let Some(parent) = self.parent { - return parent.find_global(name) - } - for (i, global) in self.globals.borrow().iter().enumerate() { - if global.as_ref() == name { - return Some(i) - } - } - None - } - - fn get_name(&mut self, name: Rc) -> usize { - // TODO: find name if already exists - let idx = self.names.borrow().len(); - self.names.borrow_mut().push(name); - idx - } - - fn emit_const(&mut self, val: Value, pos: Position) { - // TODO: find constant if already exists - self.chunk.constants.push(val); - let c = self.chunk.constants.len() - 1; - self.emit(Instruction::Const(c as u16), pos); - } - - fn can_make_globals(&self) -> bool { - self.repl && self.parent.is_none() && self.scopes.len() == 0 - } - - fn compile_value(&mut self, val: &Value, pos: Position) { - match val { - V::Nil => self.emit(I::Nil, pos), - V::Bool(b) => if *b { self.emit(I::True, pos) } else { self.emit(I::False, pos) }, - V::Int(i) => { - if let Ok(i) = i16::try_from(*i) { - self.emit(I::Int(i), pos); - } else { - self.emit_const(val.clone(), pos); - } - }, - _ => self.emit_const(val.clone(), pos), - } - } - - - - fn finish_loop(&mut self) { - self.loop_top.pop(); - while let Some(tmp) = self.loop_bot.pop() { - self.re_emit(I::Jump(self.cur()), tmp as usize); - } - } - - fn compile_expr(&mut self, expr: &Expr) -> Result<()> { - match &expr.data { - E::NoOp => self.emit(I::Nil, expr.pos), - E::If(cond, ifb, elseb) => { - self.compile_expr(cond)?; - let jmpidx = self.emit_temp(expr.pos); - self.compile_expr(ifb)?; - let jmpidx2 = self.emit_temp(expr.pos); - self.re_emit(I::JumpFalse(self.cur()), jmpidx); - if let Some(elseb) = elseb { - self.compile_expr(elseb)?; - } - self.re_emit(I::Jump(self.cur()), jmpidx2); - }, - E::Function(name, params, body, varadic) => { - let chunk = self.compile_function(name.clone(), params, body)?; - let arity = params.len() - if *varadic { 1 } else { 0 }; - let fun = Value::Function(Rc::new( - chunk::Function { - name: name.0.clone(), - arity, - fun: InnerFunction::Compiled(chunk.into()), - variadic: *varadic - } - )); - self.emit_const(fun, expr.pos); - self.emit(I::Dup, expr.pos); - if self.can_make_globals() { - let idx = self.create_global_checked(name.0.clone(), name.1)?; - self.emit(I::StoreGlobal(idx as u16), expr.pos); - } else { - self.create_local_checked(name.0.clone(), name.1)?; - self.emit(I::CreateLocal, expr.pos); - } - }, - E::Lambda(params, body, varadic) => { - let name: AstName = (Rc::from(""), expr.pos); - let chunk = self.compile_function(name.clone(), params, body)?; - let arity = params.len() - if *varadic { 1 } else { 0 }; - let fun = Value::Function(Rc::new( - chunk::Function { - name: name.0.clone(), - arity, - fun: InnerFunction::Compiled(chunk.into()), - variadic: *varadic - } - )); - self.emit_const(fun, expr.pos); - }, - E::Loop(expr) => { - let idx = self.cur(); - self.loop_top.push((idx as usize, self.scopes.len())); - self.compile_expr(expr)?; - self.emit(I::Discard(1), expr.pos); - self.emit(I::Jump(idx), expr.pos); - self.finish_loop(); - self.emit(I::Nil, expr.pos); - }, - E::Try(expr, err, catch) => { - let jmpidx = self.emit_temp(expr.pos); - self.compile_expr(expr)?; - self.emit(I::TryEnd, expr.pos); - let jmpidx2 = self.emit_temp(expr.pos); - self.re_emit(I::Try(self.cur()), jmpidx); - self.begin_scope(); - self.create_local(err.0.clone()); - self.emit(I::CreateLocal, err.1); - self.compile_expr(catch)?; - self.end_scope(); - self.re_emit(I::Jump(self.cur() as u16), jmpidx2); - }, - E::While(cond, expr) => { - let top = self.cur(); - self.compile_expr(cond)?; - let jmpidx = self.emit_temp(expr.pos); - self.loop_top.push((top as usize, self.scopes.len())); - self.compile_expr(expr)?; - self.emit(I::Discard(1), expr.pos); - self.emit(I::Jump(top), expr.pos); - self.re_emit(I::JumpFalse(self.cur()), jmpidx); - self.finish_loop(); - self.emit(I::Nil, expr.pos); - }, - E::DoWhile(expr, cond) => { - let top = self.cur(); - self.loop_top.push((top as usize, self.scopes.len())); - self.compile_expr(expr)?; - self.emit(I::Discard(1), expr.pos); - self.compile_expr(cond)?; - self.emit(I::JumpTrue(top), expr.pos); - self.finish_loop(); - self.emit(I::Nil, expr.pos); - }, - E::For(name, cond, expr) => { - self.compile_expr(cond)?; - self.emit(I::IterCreate, cond.pos); - let top = self.cur(); - self.emit(I::IterNext, cond.pos); - self.emit(I::Dup, expr.pos); - let jumpidx = self.emit_temp(expr.pos); - self.loop_top.push((top as usize, self.scopes.len())); - self.begin_scope(); - self.create_local(name.0.clone()); - self.emit(I::CreateLocal, name.1); - self.compile_expr(expr)?; - self.emit(I::Discard(1), expr.pos); - self.end_scope(); - self.emit(I::Jump(top), expr.pos); - self.re_emit(I::JumpNil(self.cur()), jumpidx); - self.finish_loop(); - self.emit(I::Discard(2), expr.pos); - self.emit(I::Nil, expr.pos); - } - E::Block(block) => { - if block.len() == 0 { - self.emit(I::Nil, expr.pos); - return Ok(()); - } - self.begin_scope(); - for (i, expr) in block.iter().enumerate() { - self.compile_expr(expr)?; - if i + 1 != block.len() { - self.emit(I::Discard(1), expr.pos); - } - } - self.end_scope(); - }, - E::Let(name, expr) => { - self.compile_expr(expr)?; - self.emit(I::Dup, expr.pos); - if self.can_make_globals() { - let global = self.create_global_checked(name.0.clone(), name.1)?; - self.emit(I::StoreGlobal(global as u16), expr.pos); - } else { - self.create_local_checked(name.0.clone(), name.1)?; - self.emit(I::CreateLocal, expr.pos); - } - }, - E::Continue => { - let top = self.loop_top.pop(); - if let Some((top, scope)) = top { - self.collapse_scopes(scope); - self.emit(I::Jump(top as u16), expr.pos); - } else { - return error(InnerError::InvContinue, expr.pos) - } - }, - E::Break => { - let top = self.loop_top.pop(); - if let Some((_, scope)) = top { - self.collapse_scopes(scope); - let tmpidx = self.emit_temp(expr.pos); - self.loop_bot.push(tmpidx); - } else { - return error(InnerError::InvBreak, expr.pos) - } - }, - E::Return(expr) => { - self.compile_expr(expr)?; - self.emit(I::Return, expr.pos); - }, - E::Literal(val) => self.compile_value(val, expr.pos), - E::Ident(name) => { - if name.as_ref() == "_" { - self.emit(I::Nil, expr.pos); - } else if let Some(local) = self.find_local(name) { - self.emit(I::LoadLocal(local.idx as u16), expr.pos); - } else if let Some(global) = self.find_global(name) { - self.emit(I::LoadGlobal(global as u16), expr.pos); - } else { - return error(InnerError::Undefined(name.clone()), expr.pos) - }; - }, - E::Assign(lhs, rhs) => { - self.compile_expr(rhs)?; - self.emit(I::Dup, rhs.pos); - match &lhs.data { - E::Ident(name) if name.as_ref() != "_" => { - if let Some(local) = self.find_local(&name) { - self.emit(I::StoreLocal(local.idx as u16), lhs.pos); - } else if let Some(global) = self.find_global(&name) { - self.emit(I::StoreGlobal(global as u16), lhs.pos); - } else if self.can_make_globals() { - let global = self.create_global_checked(name.clone(), lhs.pos)?; - self.emit(I::StoreGlobal(global as u16), lhs.pos); - } else { - self.create_local_checked(name.clone(), lhs.pos)?; - self.emit(I::CreateLocal, lhs.pos); - } - }, - E::Index(expr, params) => { - self.compile_expr(expr)?; - for param in params { - self.compile_expr(param)?; - } - self.emit(I::StoreIndex(params.len() as u8), expr.pos); - }, - E::FieldAccess(expr, ident) => { - self.compile_expr(expr)?; - let name = self.get_name(ident.0.clone()); - self.emit(I::StoreField(name as u16), expr.pos); - } - _ => return error(InnerError::InvAssign(*lhs.clone()), lhs.pos) - } - } - E::UnaryOp(expr, op) => { - self.compile_expr(expr)?; - self.emit(I::UnaryOp(*op), expr.pos); - }, - E::BinaryOp(lhs, rhs, op) => { - self.compile_expr(lhs)?; - self.compile_expr(rhs)?; - self.emit(I::BinaryOp(*op), lhs.pos); - }, - E::Index(expr, params) => { - self.compile_expr(expr)?; - for param in params { - self.compile_expr(param)?; - } - self.emit(I::Index(params.len() as u8), expr.pos) - }, - E::FnCall(fun, params) => { - for expr in params { - self.compile_expr(expr)?; - } - self.compile_expr(fun)?; - self.emit(I::Call(params.len() as u8), expr.pos); - }, - E::FieldAccess(expr, field) => { - self.compile_expr(expr)?; - let idx = self.get_name(field.0.clone()); - self.emit(I::Field(idx as u16), expr.pos) - } - E::List(list) => { - for expr in list { - self.compile_expr(expr)?; - } - self.emit(I::NewList(list.len() as u16), expr.pos); - }, - E::Matrix(mat) => { - for expr in &mat.2 { - self.compile_expr(expr)?; - } - self.emit(I::NewMatrix(mat.2.len() as u16, mat.0 as u8), expr.pos); - }, - E::Table(table) => { - for (key, value) in table { - self.compile_expr(key)?; - self.compile_expr(value)?; - } - self.emit(I::NewTable(table.len() as u16), expr.pos); - }, - E::And(lhs, rhs) => { - self.compile_expr(lhs)?; - self.emit(I::Dup, lhs.pos); - let jmpidx = self.emit_temp(lhs.pos); - self.compile_expr(rhs)?; - self.re_emit(I::JumpFalse(self.cur()), jmpidx); - }, - E::Or(lhs, rhs) => { - self.compile_expr(lhs)?; - self.emit(I::Dup, lhs.pos); - let jmpidx = self.emit_temp(lhs.pos); - self.compile_expr(rhs)?; - self.re_emit(I::JumpTrue(self.cur()), jmpidx); - }, - E::Pipeline(lhs, rhs) => { - self.compile_expr(lhs)?; - self.compile_expr(rhs)?; - self.emit(I::Call(1), expr.pos); - } - }; - Ok(()) - } - - fn compile_function(&mut self, name: AstName, params: &Vec, body: &Box) -> Result { - let mut compiler = self.child(name.0); - for (name, pos) in params { - compiler.create_local(name.clone()); - compiler.emit(I::CreateLocal, *pos); - } - compiler.compile_expr(body)?; - compiler.finish(name.1)?; - Ok(compiler.chunk) - } - - fn cur(&self) -> u16 { - self.chunk.code.len() as u16 - } - - fn emit_temp(&mut self, pos: Position) -> usize { - let idx = self.chunk.code.len(); - self.emit(Instruction::NoOp, pos); - idx - } - - fn emit(&mut self, ins: Instruction, pos: Position) { - //println!("{}: {ins}", self.name); - self.chunk.code.push(ins); - self.chunk.pos.push(pos); - } - - fn re_emit(&mut self, ins: Instruction, idx: usize) { - //println!("{} at {}: {ins}", self.name, &self.chunk.code[idx]); - self.chunk.code[idx] = ins; - } - - fn last_pos(&self) -> Position { - if let Some(pos) = self.chunk.pos.last() { - *pos - } else { - Position::default() - } - } - - fn finish(&mut self, pos: Position) -> Result<()> { - let ins = match self.chunk.code.last() { - Some(ins) => ins.clone(), - None => { - self.emit(I::Nil, pos); - self.emit(I::Return, pos); - if self.debug { - println!("{}\n{}", self.name, self.chunk); - } - return Ok(()) - } - }; - match ins { - I::Return => {}, - _ => { - self.emit(I::Return, pos); - } - }; - - if self.loop_bot.len() > 0 { - return error(InnerError::InvBreak, pos) - } - - if self.debug { - println!("{}\n{}", self.name, self.chunk); - } - Ok(()) - } - - pub fn compile( - &mut self, - body: &Expr, - ) -> Result> { - if let ExprData::Block(_) = &body.data { - self.root_is_block = true; - } - self.chunk = Chunk::new(); - self.compile_expr(body)?; - self.finish(self.last_pos())?; - let fun = Function { name: self.name.clone(), fun: InnerFunction::Compiled(self.chunk.clone().into()), arity: 0, variadic: false }; - Ok(Rc::new(fun)) - } -} diff --git a/matrix/src/gc.rs b/matrix/src/gc.rs deleted file mode 100644 index 7af020b..0000000 --- a/matrix/src/gc.rs +++ /dev/null @@ -1,176 +0,0 @@ -use std::{ops::{Index, IndexMut, Deref, DerefMut, Add, Sub, Mul}, marker::PhantomData, ptr::NonNull, fmt::{Debug, Display}}; - -pub struct Gc { - ptr: NonNull>, - phantom: PhantomData> -} - -struct GcInner { - rc: usize, - data: T -} - -impl Gc { - pub fn new(data: T) -> Self { - let boxed = Box::new(GcInner { - rc: 1, - data, - }); - Self { - ptr: NonNull::new(Box::into_raw(boxed)).unwrap(), - phantom: PhantomData - } - } -} - -impl Gc { - pub fn clone_inside(&self) -> Self { - unsafe { - let data = self.ptr.as_ref().data.clone(); - Self::new(data) - } - } - - pub fn into_inner(self) -> T { - unsafe { - self.ptr.as_ref().data.clone() - } - } - - fn data(&self) -> T { - unsafe { - self.ptr.as_ref().data.clone() - } - } -} - -impl From for Gc { - fn from(value: T) -> Self { - Gc::new(value) - } -} - -impl, Idx> IndexMut for Gc { - fn index_mut(&mut self, index: Idx) -> &mut Self::Output { - self.deref_mut().index_mut(index) - } -} - -impl, Idx> Index for Gc { - type Output = T::Output; - - fn index(&self, index: Idx) -> &Self::Output { - self.deref().index(index) - } -} - -impl DerefMut for Gc { - fn deref_mut(&mut self) -> &mut Self::Target { - unsafe { - &mut self.ptr.as_mut().data - } - } -} - -impl Deref for Gc { - type Target = T; - - fn deref(&self) -> &Self::Target { - unsafe { - &self.ptr.as_ref().data - } - } -} - -impl PartialEq for Gc { - fn eq(&self, other: &Self) -> bool { - self.deref().eq(other.deref()) - } -} - -impl Iterator for Gc { - type Item = T::Item; - - fn next(&mut self) -> Option { - self.deref_mut().next() - } -} - -impl Eq for Gc {} - -impl Clone for Gc { - fn clone(&self) -> Self { - unsafe { - let inner = self.ptr.as_ptr().as_mut().unwrap(); - inner.rc += 1; - - Self { - ptr: self.ptr, - phantom: PhantomData - } - } - } -} - -impl Drop for Gc { - fn drop(&mut self) { - unsafe { - let inner = self.ptr.as_mut(); - if inner.rc > 1 { - inner.rc -= 1; - } else { - let _ = Box::from_raw(self.ptr.as_ptr()); - } - } - } -} - -impl Debug for Gc { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if f.alternate() { - write!(f, "{:#?}", self.deref()) - } else { - write!(f, "{:?}", self.deref()) - } - } -} - -impl Display for Gc { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if f.alternate() { - write!(f, "{:#}", self.deref()) - } else { - write!(f, "{}", self.deref()) - } - } -} - -impl Add for Gc { - type Output = T::Output; - - fn add(self, rhs: Self) -> Self::Output { - let a = self.data(); - let b = rhs.data(); - a + b - } -} - -impl Sub for Gc { - type Output = T::Output; - - fn sub(self, rhs: Self) -> Self::Output { - let a = self.data(); - let b = rhs.data(); - a - b - } -} - -impl Mul for Gc { - type Output = T::Output; - - fn mul(self, rhs: Self) -> Self::Output { - let a = self.data(); - let b = rhs.data(); - a * b - } -} diff --git a/matrix/src/lex.rs b/matrix/src/lex.rs deleted file mode 100644 index 8a07234..0000000 --- a/matrix/src/lex.rs +++ /dev/null @@ -1,772 +0,0 @@ -use std::{rc::Rc, fmt::Debug}; -use regex::Regex; -use crate::Result; - -pub struct RegexToken { - regex: Regex -} - -impl Debug for RegexToken { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self.regex) - } -} - -impl PartialEq for RegexToken { - fn eq(&self, other: &Self) -> bool { - self.regex.as_str().eq(other.regex.as_str()) - } -} - -impl From for RegexToken { - fn from(regex: Regex) -> Self { - Self { regex } - } -} - -impl From for Regex { - fn from(value: RegexToken) -> Self { - value.regex - } -} - -#[derive(Debug, PartialEq)] -pub enum TokenData { - //syntax - LeftParen, - RightParen, - LeftBrack, - RightBrack, - LeftBrace, - RightBrace, - Assign, - Access, - SemiColon, - Arrow, - ThinArrow, - Comma, - Range, - RangeEq, - Colon, - Backslash, - Varadic, - Pipe, - - // math - Regex(RegexToken), - Int(i64), - Float(f64), - Complex(f64), - String(Rc), - Ident(Rc), - - // equality - Equal, - NotEqual, - GreaterEqual, - LessEqual, - GreaterThan, - LessThan, - - And, - Or, - Not, - - BitwiseShiftLeft, - BitwiseShiftRight, - BitwiseAnd, - BitwiseOr, - BitwiseXor, - - Add, - Subtract, - Multiply, - Divide, - Modulo, - Power, - - AssignAnd, - AssignOr, - - AssignBitwiseShiftLeft, - AssignBitwiseShiftRight, - AssignBitwiseAnd, - AssignBitwiseOr, - AssignBitwiseXor, - - AssignAdd, - AssignSubtract, - AssignMultiply, - AssignDivide, - AssignModulo, - AssignPower, - - // keywords - If, - Else, - While, - Let, - Function, - True, - False, - Nil, - Continue, - Break, - Do, - Loop, - Return, - For, - In, - Try, - Catch, - - // eof - Eof, -} - -#[derive(Debug, PartialEq)] -pub struct Token { - pub data: TokenData, - pub pos: Position, - pub str: String, - pub bidx: usize, - pub blen: usize, -} - -#[derive(Debug)] -pub enum Error { - UnexpectedCharacter(char), - ExpectedChar(char, char), - InvalidCodepoint, - UnexpectedEof, - InvalidDigit(char), - InvalidStringEscape(char), - InvalidRegex(anyhow::Error), - InvalidNumber(String), -} - -impl std::fmt::Display for Error { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - use Error::*; - match self { - UnexpectedCharacter(char) => write!(f, "Unexpected char: '{char}'"), - UnexpectedEof => write!(f, "Unexpected end of file"), - ExpectedChar(expected, got) => write!(f, "Expected char: '{expected}', instead got: '{got}'"), - InvalidCodepoint => write!(f, "Invalid codepoint"), - InvalidDigit(char) => write!(f, "Invalid digit: '{char}'"), - InvalidStringEscape(char) => write!(f, "Invalid string escape: '\\{char}"), - InvalidRegex(err) => write!(f, "{err}"), - InvalidNumber(num) => write!(f, "Invalid number: '{num}'") - } - } -} - -impl std::error::Error for Error {} - -#[derive(Debug, Clone, PartialEq, Eq, Copy)] -pub struct Position { - pub row: usize, - pub col: usize, -} - -impl Default for Position { - fn default() -> Self { - Self { row: 1, col: 1 } - } -} - -pub struct Lexer { - pub index: usize, - len: usize, - data: Vec, - pos: Position, - byte_len: usize, -} - -trait IsIdent { - fn is_initial_ident(&self) -> bool; - fn is_ident(&self) -> bool; -} - -impl IsIdent for char { - fn is_initial_ident(&self) -> bool { - self.is_alphabetic() || *self == '_' - } - fn is_ident(&self) -> bool { - self.is_alphanumeric() || *self == '_' - } -} - -impl> From for Lexer { - fn from(value: T) -> Self { - Self::new(value) - } -} - -impl Lexer { - pub fn new>(input: T) -> Self { - let data: Vec = input.into().chars().collect(); - Self { - index: 0, - len: data.len(), - data, - pos: Position::default(), - byte_len: 0 - } - } - - fn peek(&self) -> char { - if self.index >= self.len { - return '\0'; - } - self.data[self.index] - } - - fn next(&mut self) -> char { - let c = self.peek(); - self.index += 1; - self.byte_len += c.len_utf8(); - - self.pos.col += 1; - if c == '\n' { - self.pos.col = 1; - self.pos.row += 1; - } - - c - } - - fn next_not_eof(&mut self) -> Result { - let c = self.next(); - if c == '\0' { - return Err(Error::UnexpectedEof.into()) - } - Ok(c) - } - - fn next_expect(&mut self, expected: char) -> Result { - let c = self.next(); - if c != expected { - return Err(Error::ExpectedChar(expected, c).into()) - } - Ok(c) - } - - fn skip_whitespace(&mut self, ignore_newlines: bool) { - while self.peek().is_whitespace() && (ignore_newlines || self.peek() != '\n') { - self.next(); - } - } - - fn lex_string(&mut self, delimit: char) -> Result> { - use Error::*; - - let mut buf = String::new(); - - loop { - let c = self.next_not_eof()?; - - if c == delimit { - break; - } - - if c != '\\' { - buf.push(c); - continue; - } - - let next = self.next_not_eof()?; - match next { - '\'' | '\"' | '\\' => buf.push(c), - '0' => buf.push('\0'), - 'a' => buf.push('\x07'), - 'b' => buf.push('\x08'), - 't' => buf.push('\t'), - 'n' => buf.push('\n'), - 'v' => buf.push('\x0b'), - 'f' => buf.push('\x0c'), - 'r' => buf.push('\r'), - 'e' => buf.push('\x1b'), - 'x' => { - let n1 = self.next_not_eof()?; - let n2 = self.next_not_eof()?; - buf.push(char::from_u32( - n1.to_digit(16).ok_or(InvalidDigit(n1))? * 16 + - n2.to_digit(16).ok_or(InvalidDigit(n2))? - ).unwrap()); - }, - 'u' => { - self.next_expect('{')?; - let mut n = 0u32; - loop { - let c = self.next_not_eof()?; - if c == '}' { break } - if n >= 0x1000_0000_u32 { - return Err(InvalidCodepoint.into()) - } - n = n * 16 + c.to_digit(16).ok_or::(InvalidDigit(c).into())?; - } - let ch = char::from_u32(n).ok_or::(InvalidCodepoint.into())?; - buf.push(ch); - - }, - _ => return Err(InvalidStringEscape(next).into()) - } - } - - Ok(buf.into()) - } - - fn lex_ident(&mut self, initial: char) -> Result { - use Error as E; - use TokenData as T; - - let mut buf = std::string::String::new(); - - if !initial.is_initial_ident() { - return Err(E::UnexpectedCharacter(initial).into()) - } - - buf.push(initial); - - loop { - if self.peek().is_ident() { - buf.push(self.next()); - } else { - break; - } - } - - Ok(match buf.as_str() { - "if" => T::If, - "else" => T::Else, - "while" => T::While, - "let" => T::Let, - "fn" | "function" => T::Function, - "true" => T::True, - "false" => T::False, - "nil" => T::Nil, - "continue" => T::Continue, - "break" => T::Break, - "do" => T::Do, - "loop" => T::Loop, - "and" => T::And, - "or" => T::Or, - "not" => T::Not, - "return" => T::Return, - "for" => T::For, - "in" => T::In, - "try" => T::Try, - "catch" => T::Catch, - _ => T::Ident(buf.into()) - }) - } - - fn lex_radix(&mut self, radix: i64, radix_char: char) -> Result { - use TokenData as T; - use Error as E; - - let mut n = 0i64; - let mut char_found = false; - - loop { - if let Some(i) = self.peek().to_digit(radix as u32) { - self.next(); - n = n * radix + (i as i64); - char_found = true; - } else if self.peek().is_ident() { - return Err(E::InvalidDigit(self.peek()).into()) - } else { - break; - } - } - - if char_found { - Ok(T::Int(n)) - } else { - Err(E::InvalidNumber(format!("0{radix_char}")).into()) - } - } - - fn lex_number(&mut self, initial: char) -> Result { - use Error as E; - - if initial == '0' { - match self.peek() { - 'x' => { - self.next(); - return self.lex_radix(16, 'x') - } - 'o' => { - self.next(); - return self.lex_radix(8, 'o'); - } - _ => () - } - } - - let mut buf = String::new(); - buf.push(initial); - - let mut pos = self.pos; - let mut idx = self.index; - let mut bidx = self.byte_len; - - if initial != '.' { - loop { - if !self.peek().is_ascii_digit() { break; } - buf.push(self.next()); - } - - if self.peek() == '.' { - pos = self.pos; - idx = self.index; - bidx = self.byte_len; - buf.push(self.next()); - } - } - - let last: char = buf.chars().last().unwrap(); - let is_range = initial != '.' && last == '.' && self.peek() == '.'; - - if is_range { - self.pos = pos; - self.index = idx; - self.byte_len = bidx; - buf.pop(); - } else { - loop { - if !self.peek().is_ascii_digit() { break; } - buf.push(self.next()); - } - - if self.peek() == 'e' || self.peek() == 'E' { - buf.push(self.next()); - if self.peek() == '+' || self.peek() == '-' { - buf.push(self.next()); - } - - loop { - if !self.peek().is_ascii_digit() { break; } - buf.push(self.next()); - } - } - } - - let complex = self.peek() == 'i'; - if complex { - self.next(); - } - - if self.peek().is_ident() { - return Err(E::UnexpectedCharacter(self.peek()).into()) - } - - if let Ok(int) = buf.parse::() { - use TokenData as T; - if complex { - return Ok(T::Complex(int as f64)) - } - return Ok(T::Int(int)) - } - - if let Ok(float) = buf.parse::() { - use TokenData as T; - if complex { - return Ok(T::Complex(float)) - } - return Ok(T::Float(float)) - } - - Err(E::InvalidNumber(buf).into()) - } - - fn peek_token_impl(&mut self, ignore_newlines: bool) -> Result { - let idx = self.index; - let pos = self.pos; - let bidx = self.byte_len; - let token = self.next_token_impl(ignore_newlines); - self.index = idx; - self.pos = pos; - self.byte_len = bidx; - token - } - - fn next_token_impl(&mut self, ignore_newlines: bool) -> Result { - use TokenData as T; - use Error as E; - - self.skip_whitespace(ignore_newlines); - - let str_start = self.index; - let byte_start = self.byte_len; - - let pos = self.pos; - let char = self.next(); - let next = self.peek(); - - if char == '\0' { - let data = if ignore_newlines { T::Eof } else { T::SemiColon }; - return Ok(Token { - data, - pos, - str: String::new(), - bidx: byte_start, - blen: 0, - }) - } - - let data = match char { - '\n' => T::SemiColon, - '(' => T::LeftParen, - ')' => T::RightParen, - '[' => T::LeftBrack, - ']' => T::RightBrack, - '{' => T::LeftBrace, - '}' => T::RightBrace, - ':' => T::Colon, - '\\' => T::Backslash, - ';' => T::SemiColon, - '+' => { - match next { - '=' => { - self.next(); - T::AssignAdd - } - _ => T::Add - } - }, - '/' => { - match next { - '=' => { - self.next(); - T::AssignDivide - } - _ => T::Divide - } - }, - '%' => { - match next { - '=' => { - self.next(); - T::AssignModulo - } - _ => T::Modulo - } - }, - ',' => T::Comma, - '*' => { - match next { - '*' => { - self.next(); - match self.peek() { - '=' => { - self.next(); - T::AssignPower - }, - _ => T::Power - } - }, - '=' => { - self.next(); - T::AssignMultiply - } - _ => T::Multiply - } - }, - '!' => { - match next { - '=' => { - self.next(); - T::NotEqual - } - _ => T::Not - } - } - '&' => { - match next { - '&' => { - self.next(); - match self.peek() { - '=' => { - self.next(); - T::AssignAnd - }, - _ => T::And - } - }, - '=' => { - self.next(); - T::AssignBitwiseAnd - }, - _ => T::BitwiseAnd - } - }, - '|' => { - match next { - '|' => { - self.next(); - match self.peek() { - '=' => { - self.next(); - T::AssignOr - }, - _ => T::Or - } - }, - '=' => { - self.next(); - T::AssignBitwiseOr - }, - '>' => { - self.next(); - T::Pipe - }, - _ => T::BitwiseOr - } - }, - '-' => { - match next { - '>' => { - self.next(); - T::ThinArrow - }, - '=' => { - self.next(); - T::AssignSubtract - }, - _ => T::Subtract - } - }, - '=' => { - match next { - '>' => { - self.next(); - T::Arrow - } - '=' => { - self.next(); - T::Equal - } - _ => T::Assign - } - }, - '>' => { - match next { - '>' => { - self.next(); - match self.peek() { - '=' => { - self.next(); - T::AssignBitwiseShiftRight - }, - _ => T::BitwiseShiftRight - } - } - '=' => { - self.next(); - T::GreaterEqual - } - _ => T::GreaterThan - } - }, - '<' => { - match next { - '<' => { - self.next(); - match self.peek() { - '=' => { - self.next(); - T::AssignBitwiseShiftLeft - }, - _ => T::BitwiseShiftLeft - } - } - '=' => { - self.next(); - T::LessEqual - } - _ => T::LessThan - } - }, - '^' => { - match next { - '=' => { - self.next(); - T::AssignBitwiseXor - }, - _ => T::BitwiseXor - } - } - '\'' | '\"' => T::String(self.lex_string(char)?), - 'r' => { - match next { - '\'' | '\"' => { - self.next(); - T::Regex(regex::Regex::new(&self.lex_string(next)?) - .map(|e| e.into()) - .map_err(|e| E::InvalidRegex(e.into()))?) - } - _ => { - self.lex_ident(char)? - } - } - }, - '.' => { - if next == '.' { - self.next(); - match self.peek() { - '.' => { - self.next(); - T::Varadic - }, - '=' => { - self.next(); - T::RangeEq - }, - _ => T::Range - } - } else if next.is_digit(10) { - self.lex_number(char)? - } else { - T::Access - } - }, - _ => { - if char.is_digit(10) { - self.lex_number(char)? - } else { - self.lex_ident(char)? - } - }, - }; - - let str_end = self.index; - let byte_end = self.byte_len; - let str = self.data[str_start..str_end].to_owned().into_iter().collect(); - - Ok(Token { - data, - pos, - str, - bidx: byte_start, - blen: byte_end - byte_start - }) - } - - pub fn peek_token(&mut self) -> Result { - self.peek_token_impl(true) - } - - pub fn next_token(&mut self) -> Result { - self.next_token_impl(true) - } - - pub fn peek_token_nl(&mut self) -> Result { - self.peek_token_impl(false) - } - - pub fn next_token_nl(&mut self) -> Result { - self.next_token_impl(false) - } -} diff --git a/matrix/src/lib.rs b/matrix/src/lib.rs deleted file mode 100644 index 5a9be9f..0000000 --- a/matrix/src/lib.rs +++ /dev/null @@ -1,122 +0,0 @@ -use std::{fmt::Display, rc::Rc}; - -pub mod compiler; -pub mod value; -pub mod gc; -pub mod lex; -pub mod vm; -pub mod parse; -pub mod chunk; -pub mod ast; - -pub use ::num_complex::Complex64 as Complex64; -pub use ::num_rational::Rational64 as Rational64; - -#[derive(Debug, Clone)] -pub struct Error(Rc); - -impl std::error::Error for Error {} - -#[derive(Debug)] -enum ErrorInner { - Lex(lex::Error), - Parse(parse::Error), - Value(value::Error), - Compile(compiler::Error), - Runtime(vm::Error), - External(anyhow::Error), - Traced(Box, String), - Any(String), -} - -impl Display for crate::Error { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - use crate::ErrorInner::*; - match self.0.as_ref() { - Lex(err) => write!(f, "{err}"), - Parse(err) => write!(f, "{err}"), - Value(err) => write!(f, "{err}"), - Compile(err) => write!(f, "{err}"), - Runtime(err) => write!(f, "{err}"), - External(err) => write!(f, "{err}"), - Traced(err, trace) => write!(f, "{err}\n{trace}"), - Any(err) => write!(f, "{err}"), - } - } -} - -macro_rules! from_error { - ($struct:ty, $tuple:tt) => { - impl From<$struct> for crate::Error { - fn from(value: $struct) -> Self { - crate::Error(Rc::new(ErrorInner::$tuple(value))) - } - } - }; -} - -from_error!(lex::Error, Lex); -from_error!(parse::Error, Parse); -from_error!(value::Error, Value); -from_error!(compiler::Error, Compile); -from_error!(vm::Error, Runtime); -from_error!(anyhow::Error, External); -from_error!(String, Any); - -impl From<(Error, String)> for Error { - fn from(value: (Error, String)) -> Self { - Self(Rc::new(ErrorInner::Traced(Box::new(value.0), value.1))) - } -} - -#[macro_export] -macro_rules! iter { - ($fn:expr) => { - $crate::value::Value::Iter( - ::std::rc::Rc::new( - $crate::chunk::Function { - name: ::std::rc::Rc::from(""), - arity: 0, - variadic: false, - fun: $crate::chunk::InnerFunction::Native( - ::std::rc::Rc::new($fn - ))})) - }; -} - -#[macro_export] -macro_rules! native { - ($name:expr, $arity:expr, $varadic:expr, $fn:expr) => { - $crate::value::Value::Function( - ::std::rc::Rc::new( $crate::chunk::Function { - name: std::rc::Rc::from($name), - arity: $arity, - variadic: $varadic, - fun: $crate::chunk::InnerFunction::Native( - ::std::rc::Rc::new($fn) - ) - }) - ) - } -} - -#[macro_export] -macro_rules! unpack_args { - ($e:expr) => { - $e.try_into().expect("bypassed arity check") - }; -} - -#[macro_export] -macro_rules! unpack_varargs { - ($e:expr) => {{ - let mut args = $e; - let $crate::value::Value::List(varargs) = args.pop().expect("bypassed arity check") else { - panic!("bypassed arity check") - }; - let varargs = varargs.into_inner(); - (args.try_into().expect("bypassed arity check"), varargs) - }}; -} - -pub type Result = std::result::Result; diff --git a/matrix/src/parse.rs b/matrix/src/parse.rs deleted file mode 100644 index d967130..0000000 --- a/matrix/src/parse.rs +++ /dev/null @@ -1,775 +0,0 @@ -use std::{fmt::Display, rc::Rc}; -use num_complex::Complex64; -use crate::{lex::{Lexer, self, Token, TokenData, Position}, ast::{Expr, BinaryOp, UnaryOp, optimize, ExprData, AstName}, value::{Value, self}, Result}; - -use Value as V; -use ExprData as E; -use TokenData as T; - -pub struct ParserBuilder { - optimize: bool -} - -impl ParserBuilder { - pub fn new() -> Self { - Self { optimize: true } - } - - pub fn optimize(mut self, optimize: bool) -> Self { - self.optimize = optimize; - self - } - - pub fn build(self) -> Parser { - Parser { - lexer: Lexer::new(""), - optimize: self.optimize - } - } -} - -pub struct Parser { - lexer: Lexer, - optimize: bool -} - -#[derive(Debug)] -pub enum Error { - LexerError(crate::lex::Error), - UnexpectedToken(Token), - ExpectedToken(TokenData, Position), - MatrixInvDomain(usize, usize, usize), - NotAssignable(Expr), - ValueError(value::Error), -} - -impl Display for Error { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - use Error::*; - match self { - LexerError(err) => write!(f, "{err}"), - UnexpectedToken(tok) => write!(f, "Unexpected token: '{:?}' at {}:{}", tok.data, tok.pos.row, tok.pos.col), - ExpectedToken(tok, pos) => write!(f, "Expected token: '{tok:?}' at {}:{}", pos.row, pos.col), - MatrixInvDomain(row, should, was) => write!(f, "In row {row} of matrix, domain was expected to be {should} but was given {was}"), - NotAssignable(expr) => write!(f, "{expr:?} is not assignable"), - ValueError(err) => write!(f, "{err}"), - } - } -} - -impl From for Error { - fn from(value: lex::Error) -> Self { - Self::LexerError(value) - } -} - -impl std::error::Error for Error {} - -macro_rules! expr_parser { - ($parser:ident, $pattern:pat, $fn:ident) => {{ - let mut expr = $parser.$fn()?; - let pos = expr.pos; - loop { - let tok = $parser.lexer.peek_token_nl()?; - match tok.data { - $pattern => { - $parser.lexer.next_token_nl()?; - let temp = $parser.$fn()?; - expr = (E::BinaryOp(Box::new(expr), Box::new(temp), BinaryOp::from(tok.data)), pos).into() - } - _ => break - } - } - Ok(expr) - }}; -} - -macro_rules! expr_parser_reverse { - ($parser:ident, $pattern:pat, $fn:ident, $cur:ident) => {{ - let expr = $parser.$fn()?; - let tok = $parser.lexer.peek_token_nl()?; - let pos = tok.pos; - Ok(match tok.data { - $pattern => { - $parser.lexer.next_token_nl()?; - (E::BinaryOp(Box::new(expr), Box::new($parser.$cur()?), BinaryOp::from(tok.data)), pos).into() - } - _ => expr - }) - }}; -} - -impl Parser { - - fn force_token(&mut self, tok: TokenData) -> Result { - let next = self.lexer.next_token()?; - if next.data != tok { - Err(Error::ExpectedToken(tok, next.pos).into()) - } else { - Ok(tok) - } - } - - fn force_token_nl(&mut self, tok: TokenData) -> Result { - let next = self.lexer.next_token_nl()?; - if next.data != tok { - Err(Error::ExpectedToken(tok, next.pos).into()) - } else { - Ok(tok) - } - } - - fn parse_fn_call(&mut self) -> Result> { - self.force_token(T::LeftParen)?; - let mut params = Vec::new(); - loop { - let expr = match self.lexer.peek_token()?.data { - T::RightParen => { - self.lexer.next_token()?; - break - }, - _ => self.parse_expr()? - }; - params.push(expr); - let next = self.lexer.next_token()?; - match next.data { - T::Comma => continue, - T::RightParen => break, - _ => return Err(Error::UnexpectedToken(next).into()) - }; - } - Ok(params) - } - - fn parse_index(&mut self) -> Result> { - self.force_token(T::LeftBrack)?; - let mut indicies = Vec::new(); - loop { - let expr = match self.lexer.peek_token()?.data { - T::RightBrack => { - self.lexer.next_token()?; - break - }, - _ => self.parse_expr()? - }; - indicies.push(expr); - let next = self.lexer.next_token()?; - match next.data { - T::SemiColon => continue, - T::RightBrack => break, - _ => return Err(Error::UnexpectedToken(next).into()) - }; - } - Ok(indicies) - } - - fn parse_matrix_part(&mut self) -> Result> { - let mut part = Vec::new(); - loop { - let expr = match self.lexer.peek_token()?.data { - T::SemiColon => break, - T::RightBrack => break, - _ => self.parse_expr()? - }; - part.push(expr); - match self.lexer.peek_token()?.data { - T::Comma => { - self.lexer.next_token()?; - }, - _ => {}, - }; - } - Ok(part) - } - - fn parse_matrix(&mut self) -> Result { - let pos = self.lexer.peek_token()?.pos; - self.force_token(T::LeftBrack)?; - let mut parts = Vec::new(); - loop { - let part = self.parse_matrix_part()?; - parts.push(part); - let next = self.lexer.next_token()?; - match next.data { - T::SemiColon => continue, - T::RightBrack => break, - _ => return Err(Error::UnexpectedToken(next).into()), - }; - } - if parts.len() == 1 { - Ok((E::List(parts.pop().unwrap()), pos).into()) - } else { - let codomain = parts.len(); - let domain = parts[0].len(); - for (i, part) in parts.iter().enumerate() { - if part.len() != domain { - return Err(Error::MatrixInvDomain(i, domain, part.len()).into()) - } - } - let mut data = Vec::new(); - parts.reverse(); - while let Some(part) = parts.pop() { - data.extend(part); - } - Ok((E::Matrix((domain, codomain, data)), pos).into()) - } - } - - fn parse_table_key(&mut self) -> Result { - let tok = self.lexer.next_token()?; - Ok(match tok.data { - T::LeftBrack => { - let expr = self.parse_expr()?; - self.force_token(T::RightBrack)?; - expr - }, - T::Ident(ident) => (E::Literal(V::String(ident.to_string().into())), tok.pos).into(), - T::String(string) => (E::Literal(V::String(string.to_string().into())), tok.pos).into(), - _ => return Err(Error::UnexpectedToken(tok).into()) - }) - } - - fn parse_table(&mut self) -> Result { - let pos = self.lexer.peek_token()?.pos; - self.force_token(T::Colon)?; - self.force_token(T::LeftBrace)?; - let mut table = Vec::new(); - if self.lexer.peek_token()?.data == T::RightBrace { - self.lexer.next_token()?; - return Ok((E::Table(table), pos).into()) - } - loop { - let key = self.parse_table_key()?; - self.force_token(T::Assign)?; - let value = self.parse_expr()?; - table.push((key, value)); - let next = self.lexer.next_token()?; - match next.data { - T::Comma => continue, - T::RightBrace => break, - _ => return Err(Error::UnexpectedToken(next).into()) - } - } - Ok((E::Table(table), pos).into()) - } - - fn parse_paren(&mut self) -> Result { - self.force_token(T::LeftParen)?; - let expr = self.parse_expr()?; - self.force_token(T::RightParen)?; - Ok(expr) - } - - fn parse_params(&mut self) -> Result<(Vec<(Rc, Position)>, bool)> { - let tok = self.lexer.next_token()?; - match tok.data { - T::Ident(ident) => { - let params = vec![(ident, tok.pos)]; - if self.lexer.peek_token()?.data == T::Varadic { - self.lexer.next_token()?; - return Ok((params, true)) - } else { - return Ok((params, false)) - } - } - T::LeftParen => (), - _ => return Err(Error::UnexpectedToken(tok).into()), - } - - let mut params = Vec::new(); - let mut varadic = false; - - if self.lexer.peek_token()?.data == T::RightParen { - return Ok((params, varadic)); - } - - loop { - let ident = self.parse_ident()?; - params.push(ident); - let next = self.lexer.next_token()?; - match next.data { - T::Varadic => { - varadic = true; - self.force_token(T::RightParen)?; - break; - } - T::Comma => continue, - T::RightParen => break, - _ => return Err(Error::UnexpectedToken(next).into()), - } - } - - Ok((params, varadic)) - } - - fn parse_ident(&mut self) -> Result { - let next = self.lexer.next_token()?; - if let T::Ident(ident) = next.data { - Ok((ident, next.pos)) - } else { - Err(Error::UnexpectedToken(next).into()) - } - } - - fn parse_wrapped_ident(&mut self) -> Result { - if self.lexer.peek_token()?.data == T::LeftParen { - self.lexer.next_token()?; - let ident = self.parse_ident()?; - self.force_token(T::RightParen)?; - Ok(ident) - } else { - self.parse_ident() - } - } - - fn parse_ident_nl(&mut self) -> Result { - let next = self.lexer.next_token_nl()?; - if let T::Ident(ident) = next.data { - Ok((ident, next.pos)) - } else { - Err(Error::UnexpectedToken(next).into()) - } - } - - fn parse_function(&mut self) -> Result { - let pos = self.lexer.peek_token()?.pos; - self.force_token(T::Function)?; - let ident = self.parse_ident()?; - let (params, varadic) = match self.lexer.peek_token()?.data { - T::LeftBrace => (vec![], false), - _ => self.parse_params()?, - }; - let expr = self.parse_expr()?; - Ok((E::Function(ident, params, Box::new(expr), varadic), pos).into()) - } - - fn parse_lambda(&mut self) -> Result { - let pos = self.lexer.peek_token()?.pos; - self.force_token(T::Backslash)?; - let (params, varadic) = match self.lexer.peek_token()?.data { - T::Arrow => (vec![], false), - _ => self.parse_params()?, - }; - self.force_token(T::Arrow)?; - let expr = self.parse_expr()?; - Ok((E::Lambda(params, Box::new(expr), varadic), pos).into()) - } - - fn parse_do_while(&mut self) -> Result { - let pos = self.lexer.peek_token()?.pos; - self.force_token(T::Do)?; - let expr = Box::new(self.parse_expr()?); - self.force_token(T::While)?; - let cond = Box::new(self.parse_expr()?); - Ok((E::DoWhile(expr, cond), pos).into()) - } - - fn parse_while(&mut self) -> Result { - let pos = self.lexer.peek_token()?.pos; - self.force_token(T::While)?; - let cond = Box::new(self.parse_expr()?); - let expr = Box::new(self.parse_expr()?); - Ok((E::While(cond, expr), pos).into()) - } - - fn parse_loop(&mut self) -> Result { - let pos = self.lexer.peek_token()?.pos; - self.force_token(T::Loop)?; - let expr = self.parse_expr()?; - Ok((E::Loop(Box::new(expr)), pos).into()) - } - - fn parse_for(&mut self) -> Result { - let pos = self.lexer.peek_token()?.pos; - self.force_token(T::For)?; - let name = self.parse_ident()?; - self.force_token(T::In)?; - let cond = Box::new(self.parse_expr()?); - let expr = Box::new(self.parse_expr()?); - Ok((E::For(name, cond, expr), pos).into()) - } - - fn parse_if(&mut self) -> Result { - let pos = self.lexer.peek_token()?.pos; - self.force_token(T::If)?; - let cond = Box::new(self.parse_expr()?); - let expr = Box::new(self.parse_expr()?); - - if self.lexer.peek_token()?.data != T::Else { - return Ok((E::If(cond, expr, None), pos).into()) - } - self.lexer.next_token()?; - - if self.lexer.peek_token()?.data == T::If { - Ok((E::If(cond, expr, Some(Box::new(self.parse_if()?))), pos).into()) - } else { - Ok((E::If(cond, expr, Some(Box::new(self.parse_expr()?))), pos).into()) - } - } - - fn parse_let(&mut self) -> Result { - let pos = self.lexer.peek_token()?.pos; - self.force_token(T::Let)?; - let ident = self.parse_ident_nl()?; - if self.lexer.peek_token_nl()?.data == T::Assign { - self.force_token_nl(T::Assign)?; - Ok((E::Let(ident, Box::new(self.parse_expr()?)), pos).into()) - } else { - Ok((E::Let(ident, Box::new((E::Literal(V::Nil), pos).into())), pos).into()) - } - } - - fn parse_return(&mut self) -> Result { - let pos = self.lexer.peek_token()?.pos; - self.force_token(T::Return)?; - Ok((E::Return(Box::new(self.parse_expr()?)), pos).into()) - } - - fn parse_block(&mut self) -> Result { - let mut block = Vec::new(); - let pos = self.lexer.peek_token()?.pos; - self.force_token(T::LeftBrace)?; - loop { - let expr = match self.lexer.peek_token()?.data { - T::RightBrace => break, - T::SemiColon => { - self.lexer.next_token()?; - continue; - } - _ => self.parse_expr()? - }; - block.push(expr); - let next = self.lexer.next_token_nl()?; - match next.data { - T::SemiColon => continue, - T::RightBrace => break, - _ => return Err(Error::ExpectedToken(T::SemiColon, next.pos).into()) - } - } - if self.lexer.peek_token()?.data == T::RightBrace { - self.lexer.next_token()?; - } - Ok((E::Block(block), pos).into()) - } - - fn parse_try(&mut self) -> Result { - let pos = self.lexer.peek_token()?.pos; - self.force_token(T::Try)?; - let expr = self.parse_expr()?; - self.force_token(T::Catch)?; - let ident = self.parse_wrapped_ident()?; - let catch = self.parse_expr()?; - Ok((E::Try(Box::new(expr), ident, Box::new(catch)), pos).into()) - } - - fn parse_value(&mut self) -> Result { - let tok = self.lexer.next_token()?; - let data = match tok.data { - T::Nil => E::Literal(V::Nil), - T::Int(i) => E::Literal(V::Int(i)), - T::Float(f) => E::Literal(V::Float(f)), - T::Complex(c) => E::Literal(V::Complex(Complex64::new(0.0, c))), - T::Regex(r) => E::Literal(V::Regex(Rc::new(r.into()))), - T::String(s) => E::Literal(V::String(s.to_string().into())), - T::True => E::Literal(V::Bool(true)), - T::False => E::Literal(V::Bool(false)), - T::Ident(ident) => E::Ident(ident), - _ => return Err(Error::UnexpectedToken(tok).into()), - }; - Ok((data, tok.pos).into()) - } - - fn parse_term(&mut self) -> Result { - use T::*; - match self.lexer.peek_token()?.data { - Function => self.parse_function(), - Backslash => self.parse_lambda(), - Do => self.parse_do_while(), - While => self.parse_while(), - For => self.parse_for(), - Let => self.parse_let(), - LeftBrace => self.parse_block(), - Return => self.parse_return(), - If => self.parse_if(), - Loop => self.parse_loop(), - Try => self.parse_try(), - Break => { - let next = self.lexer.next_token()?; - Ok((E::Break, next.pos).into()) - }, - Continue => { - let next = self.lexer.next_token()?; - Ok((E::Continue, next.pos).into()) - }, - LeftBrack => self.parse_matrix(), - Colon => self.parse_table(), - LeftParen => self.parse_paren(), - _ => self.parse_value(), - } - } - - fn parse_expr_expr_access(&mut self) -> Result { - let mut expr = self.parse_term()?; - let pos = expr.pos; - loop { - let tok = self.lexer.peek_token()?; - match tok.data { - T::Access => { - self.force_token(T::Access)?; - let temp = self.parse_ident()?; - expr = (E::FieldAccess(Box::new(expr), temp), pos).into(); - }, - _ => break - } - } - Ok(expr) - } - - fn parse_expr_call(&mut self) -> Result { - let mut expr = self.parse_expr_expr_access()?; - let pos = expr.pos; - loop { - let tok = self.lexer.peek_token()?; - match tok.data { - T::LeftBrack => { - let index = self.parse_index()?; - expr = (E::Index(Box::new(expr), index), pos).into(); - }, - T::LeftParen => { - let params = self.parse_fn_call()?; - expr = (E::FnCall(Box::new(expr), params), pos).into(); - } - _ => break - } - } - Ok(expr) - } - - fn parse_expr_unary(&mut self) -> Result { - let tok = self.lexer.peek_token_nl()?; - Ok(match tok.data { - T::Not => { - self.lexer.next_token()?; - (E::UnaryOp(Box::new(self.parse_expr_unary()?), UnaryOp::Not), tok.pos).into() - } - T::Subtract => { - self.lexer.next_token()?; - (E::UnaryOp(Box::new(self.parse_expr_unary()?), UnaryOp::Negate), tok.pos).into() - } - _ => self.parse_expr_call()? - }) - } - - fn parse_expr_pow(&mut self) -> Result { - expr_parser_reverse!( - self, - T::Power, - parse_expr_unary, - parse_expr_pow - ) - } - - fn parse_expr_mult(&mut self) -> Result { - expr_parser!(self, T::Multiply | T::Divide | T::Modulo, parse_expr_pow) - } - - fn parse_expr_add(&mut self) -> Result { - expr_parser!(self, T::Add | T::Subtract, parse_expr_mult) - } - - fn parse_expr_shift(&mut self) -> Result { - expr_parser!( - self, - T::BitwiseShiftLeft | T::BitwiseShiftRight, - parse_expr_add - ) - } - - fn parse_expr_bit_and(&mut self) -> Result { - expr_parser!(self, T::BitwiseAnd, parse_expr_shift) - } - - fn parse_expr_bit_or(&mut self) -> Result { - expr_parser!(self, T::BitwiseOr, parse_expr_bit_and) - } - - fn parse_expr_compare(&mut self) -> Result { - expr_parser!( - self, - T::Equal | T::NotEqual | - T::LessThan | T::GreaterThan | - T::LessEqual | T::GreaterEqual, - parse_expr_bit_or - ) - } - - fn parse_expr_and(&mut self) -> Result { - let mut expr = self.parse_expr_compare()?; - let pos = expr.pos; - loop { - let tok = self.lexer.peek_token()?; - match tok.data { - T::And => { - self.force_token(T::And)?; - let temp = self.parse_expr_compare()?; - expr = (E::And(Box::new(expr), Box::new(temp)), pos).into(); - }, - _ => break - } - } - Ok(expr) - } - - fn parse_expr_or(&mut self) -> Result { - let mut expr = self.parse_expr_and()?; - let pos = expr.pos; - loop { - let tok = self.lexer.peek_token()?; - match tok.data { - T::Or => { - self.force_token(T::Or)?; - let temp = self.parse_expr_and()?; - expr = (E::Or(Box::new(expr), Box::new(temp)), pos).into(); - }, - _ => break - } - } - Ok(expr) - } - - fn parse_expr_range(&mut self) -> Result { - let expr = self.parse_expr_or()?; - let pos = expr.pos; - match self.lexer.peek_token()?.data { - T::Range => { - self.lexer.next_token()?; - let temp = self.parse_expr_or()?; - Ok((E::BinaryOp(Box::new(expr), Box::new(temp), BinaryOp::Range), pos).into()) - }, - T::RangeEq => { - self.lexer.next_token()?; - let temp = self.parse_expr_or()?; - Ok((E::BinaryOp(Box::new(expr), Box::new(temp), BinaryOp::RangeEq), pos).into()) - }, - _ => Ok(expr) - } - } - - fn parse_expr_op_assign(&mut self) -> Result { - use BinaryOp as B; - let expr = self.parse_expr_range()?; - let tok = self.lexer.peek_token_nl()?; - let pos = tok.pos; - let data: ExprData = match tok.data { - T::Assign => { - self.lexer.next_token_nl()?; - E::Assign(Box::new(expr.clone()),Box::new(self.parse_expr()?)) - }, - T::AssignAnd => { - self.lexer.next_token_nl()?; - E::Assign(Box::new(expr.clone()),Box::new((E::And(Box::new(expr), Box::new(self.parse_expr()?)), pos).into())) - }, - T::AssignOr => { - self.lexer.next_token_nl()?; - E::Assign(Box::new(expr.clone()),Box::new((E::Or(Box::new(expr), Box::new(self.parse_expr()?)),pos).into())) - }, - T::AssignAdd => { - self.lexer.next_token_nl()?; - E::Assign(Box::new(expr.clone()),Box::new((E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::Add),pos).into())) - }, - T::AssignSubtract => { - self.lexer.next_token_nl()?; - E::Assign(Box::new(expr.clone()),Box::new((E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::Subtract),pos).into())) - }, - T::AssignMultiply => { - self.lexer.next_token_nl()?; - E::Assign(Box::new(expr.clone()),Box::new((E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::Multiply),pos).into())) - }, - T::AssignDivide => { - self.lexer.next_token_nl()?; - E::Assign(Box::new(expr.clone()),Box::new((E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::Divide),pos).into())) - }, - T::AssignModulo => { - self.lexer.next_token_nl()?; - E::Assign(Box::new(expr.clone()),Box::new((E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::Modulo),pos).into())) - }, - T::AssignPower => { - self.lexer.next_token_nl()?; - E::Assign(Box::new(expr.clone()),Box::new((E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::Power),pos).into())) - }, - T::AssignBitwiseAnd => { - self.lexer.next_token_nl()?; - E::Assign(Box::new(expr.clone()),Box::new((E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::BitwiseAnd),pos).into())) - }, - T::AssignBitwiseOr => { - self.lexer.next_token_nl()?; - E::Assign(Box::new(expr.clone()),Box::new((E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::BitwiseOr),pos).into())) - }, - T::AssignBitwiseXor => { - self.lexer.next_token_nl()?; - E::Assign(Box::new(expr.clone()),Box::new((E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::BitwiseXor),pos).into())) - }, - T::AssignBitwiseShiftLeft => { - self.lexer.next_token_nl()?; - E::Assign(Box::new(expr.clone()),Box::new((E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::BitwiseShiftLeft),pos).into())) - }, - T::AssignBitwiseShiftRight => { - self.lexer.next_token_nl()?; - E::Assign(Box::new(expr.clone()),Box::new((E::BinaryOp(Box::new(expr), Box::new(self.parse_expr()?), B::BitwiseShiftRight),pos).into())) - }, - _ => expr.data - }; - Ok((data, pos).into()) - } - - fn parse_expr(&mut self) -> Result { - let mut expr = self.parse_expr_op_assign()?; - let pos = expr.pos; - loop { - let tok = self.lexer.peek_token()?; - match tok.data { - T::Pipe => { - self.force_token(T::Pipe)?; - let temp = self.parse_expr_op_assign()?; - expr = (E::Pipeline(Box::new(expr), Box::new(temp)), pos).into(); - }, - _ => break - } - } - Ok(expr) - } - - fn parse_root(&mut self) -> Result { - let mut block = Vec::new(); - loop { - match self.lexer.peek_token()?.data { - T::Eof => break, - T::SemiColon => { - self.lexer.next_token()?; - continue - } - _ => {} - }; - let expr = self.parse_expr()?; - block.push(expr); - let next = self.lexer.next_token()?; - match next.data { - T::Eof => break, - T::SemiColon => continue, - _ => return Err(Error::ExpectedToken(T::SemiColon, next.pos).into()) - }; - } - Ok((E::Block(block), Position::default()).into()) - } - - pub fn parse>(&mut self, into: T) -> Result { - let lexer = Lexer::new(into); - self.lexer = lexer; - let ast = self.parse_root()?; - if self.optimize { - Ok(optimize(ast)?) - } else { - Ok(ast) - } - } -} diff --git a/matrix/src/value.rs b/matrix/src/value.rs deleted file mode 100644 index f7d5170..0000000 --- a/matrix/src/value.rs +++ /dev/null @@ -1,1223 +0,0 @@ -use std::{ - collections::HashMap, - rc::Rc, - hash::Hash, - fmt::{Display, Debug}, - ops::{Add, Neg, Not, Sub, Div, Mul, BitOr, BitAnd, BitXor, Shl, Shr}, - cmp::Ordering, - cell::RefCell, fs::File -}; - -use crate::iter; -use num_complex::Complex64; -use num_rational::Rational64; -use regex::Regex; - -use crate::{ast::{Expr, BinaryOp, UnaryOp}, chunk::Function, Result, gc::Gc}; - -pub type List = Vec; -pub type Table = ValueMap; - -pub type InlineList = Vec; -pub type InlineMatrix = (usize, usize, Vec); -pub type InlineTable = Vec<(Expr, Expr)>; - -#[derive(Debug, Clone)] -pub struct ValueMap(HashMap); - -impl ValueMap { - pub fn new() -> Self { - Self(HashMap::new()) - } - pub fn get(&self, key: &Value) -> Result> { - key.can_hash()?; - Ok(self.0.get(key)) - } - pub fn insert(&mut self, key: Value, value: Value) -> Result<()> { - key.can_hash()?; - self.0.insert(key, value); - Ok(()) - } - pub fn len(&self) -> usize { - self.0.len() - } -} - -#[derive(Debug)] -pub struct Error(String); - -impl Display for self::Error { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.0) - } -} - -impl std::error::Error for self::Error {} - -macro_rules! error { - ($($arg:tt)*) => { - Err(self::Error(format!($($arg)*)).into()) - }; -} - -#[derive(Clone)] -pub enum Value { - Nil, - Bool(bool), - Int(i64), - Float(f64), - Ratio(Rational64), - Complex(Complex64), - Regex(Rc), - String(Rc), - List(Gc), - Matrix(Gc), - Table(Gc), - Function(Rc), - Range(Rc<(i64, i64, bool)>), - Iter(Rc), - File(Rc>), - Error(crate::Error), -} - -impl From<&str> for Value { - fn from(value: &str) -> Self { - Value::String(value.into()) - } -} - -impl Hash for Value { - fn hash(&self, state: &mut H) { - use Value::*; - match self { - Nil => 0x23845.hash(state), - Bool(b) => b.hash(state), - Int(i) => i.hash(state), - Ratio(r) => r.hash(state), - Regex(r) => r.as_str().hash(state), - String(s) => s.hash(state), - List(l) => { - for val in l.iter() { - val.hash(state); - } - } - Matrix(m) => { - m.domain.hash(state); - m.codomain.hash(state); - for val in m.values.iter() { - val.hash(state); - } - }, - _ => panic!("tried to hash {self:?}") - }; - } -} - -impl Debug for Value { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - use Value as V; - match self { - V::Nil => write!(f, "[Nil]"), - V::Bool(b) => write!(f, "[Bool {b}]"), - V::Int(i) => write!(f, "[Int {i}]"), - V::Float(vf) => write!(f, "[Float {vf}]"), - V::Ratio(r) => write!(f, "[Ratio {r}]"), - V::Complex(c) => write!(f, "[Complex {c}]"), - V::Regex(r) => write!(f, "[Regex /{r}/]"), - V::String(s) => write!(f, "[String '{s}']"), - V::List(l) => write!(f, "[List {}]", l.len()), - V::Matrix(m) => write!(f, "[Matirx {}x{}]", m.domain, m.codomain), - V::Table(t) => write!(f, "[Table {}]", t.0.len()), - V::Function(vf) => write!(f, "[Function {}]", vf.name), - V::Range(r) => write!(f, "[Range {:?}..{:?}]", r.0, r.1), - V::Iter(_) => write!(f, "[Iterator]"), - V::Error(_) => write!(f, "[Error]"), - V::File(_) => write!(f, "[File]"), - } - } -} - -impl Display for Value { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - use Value as V; - - let red; - let green; - let yellow; - let blue; - let pink; - let cyan; - let clear; - - if f.alternate() { - red = "\x1b[31m"; - green = "\x1b[32m"; - yellow = "\x1b[33m"; - blue = "\x1b[34m"; - pink = "\x1b[35m"; - cyan = "\x1b[36m"; - clear = "\x1b[0m"; - } else { - red = ""; - green = ""; - yellow = ""; - blue = ""; - pink = ""; - cyan = ""; - clear = ""; - } - - match self { - V::Nil => {write!(f, "{blue}nil{clear}")?;}, - V::Bool(b) => {write!(f, "{yellow}{b}{clear}")?;}, - V::Int(i) => {write!(f, "{yellow}{i}{clear}")?;}, - V::Float(l) => {write!(f, "{yellow}{l}{clear}")?;}, - V::Ratio(r) => {write!(f, "{yellow}{r}{clear}")?;}, - V::Complex(c) => {write!(f, "{yellow}{c}{clear}")?;}, - V::Regex(r) => {write!(f, "/{red}{r}{clear}/")?;}, - V::String(s) => { - if f.alternate() { - write!(f, "{green}'{s}'{clear}")?; - } else { - write!(f, "{s}")?; - } - } - V::List(l) => { - write!(f, "[")?; - for (i, el) in l.iter().enumerate() { - if i != 0 { - write!(f, " ")?; - } - if f.alternate() { - write!(f, "{el:#}")?; - } else { - write!(f, "{el}")?; - } - } - write!(f, "]")?; - }, - V::Matrix(m) => { - if f.alternate() { - write!(f, "{m:#}")?; - } else { - write!(f, "{m}")?; - } - }, - V::Table(t) => { - write!(f, "{{")?; - for (i, (key, val)) in t.0.iter().enumerate() { - if i != 0 { - write!(f, ", ")?; - } - if f.alternate() { - write!(f, "{key:#} = {val:#}")?; - } else { - write!(f, "{key} = {val}")?; - } - } - write!(f, "}}")?; - }, - V::Function(fun) => { - use crate::chunk::InnerFunction as F; - match fun.fun { - F::Compiled(_) => write!(f, "{cyan}[Function {}]{clear}", fun.name)?, - F::Native(_) => write!(f, "{cyan}[Builtin {}]{clear}", fun.name)?, - }; - }, - V::Range(r) => { - if f.alternate() { - write!(f, "{:#}..{:#}", r.0, r.1)?; - } else { - write!(f, "{}..{}", r.0, r.1)?; - } - } - V::Iter(_) => {write!(f, "{pink}[Iterator]{clear}")?;}, - V::File(_) => {write!(f, "{pink}[File]{clear}")?;}, - V::Error(e) => {write!(f, "{red}{e}{clear}")?;}, - }; - Ok(()) - } -} - -impl Value { - pub fn can_hash(&self) -> Result<()> { - use Value::*; - match self { - Nil => {}, - Bool(_) => {}, - Int(_) => {}, - Ratio(_) => {}, - Regex(_) => {}, - String(_) => {} - List(l) => { - for val in l.iter() { - val.can_hash()?; - } - } - Matrix(m) => { - for val in m.values.iter() { - val.can_hash()?; - } - }, - _ => return error!("cannot hash {self:?}") - } - Ok(()) - } - - pub fn is_zero(&self) -> bool { - use Value as V; - match self { - V::Int(i) => *i == 0, - V::Float(f) => *f == 0.0 || *f == -0.0, - V::Ratio(r) => *r.numer() == 0, - _ => false, - } - } -} - -fn ratio_to_f64(r: Rational64) -> f64 { - *r.numer() as f64 / *r.denom() as f64 -} - -impl Value { - pub fn promote_trig(self) -> Self { - use Value as V; - match self { - V::Int(i) => V::Float(i as f64), - V::Ratio(r) => V::Float(ratio_to_f64(r)), - a => a - } - } -} - -fn promote(a: Value, b: Value) -> (Value, Value) { - use Value as V; - match (&a, &b) { - (V::Int(x), V::Ratio(..)) => (V::Ratio((*x).into()), b), - (V::Int(x), V::Float(..)) => (V::Float(*x as f64), b), - (V::Int(x), V::Complex(..)) => (V::Complex((*x as f64).into()), b), - (V::Ratio(x), V::Float(..)) => (V::Float(ratio_to_f64(*x)), b), - (V::Ratio(x), V::Complex(..)) => (V::Complex(ratio_to_f64(*x).into()), b), - (V::Float(x), V::Complex(..)) => (V::Complex((*x).into()), b), - (V::Ratio(..), V::Int(y)) => (a, V::Ratio((*y).into())), - (V::Float(..), V::Int(y)) => (a, V::Float(*y as f64)), - (V::Complex(..), V::Int(y)) => (a, V::Complex((*y as f64).into())), - (V::Float(..), V::Ratio(y)) => (a, V::Float(ratio_to_f64(*y))), - (V::Complex(..), V::Ratio(y)) => (a, V::Complex(ratio_to_f64(*y).into())), - (V::Complex(..), V::Float(y)) => (a, V::Complex((*y).into())), - (V::List(l1), V::List(l2)) if l1.len() > 0 && l2.len() > 0 - => (V::Matrix(Matrix::from_list(l1.to_vec()).into()), V::Matrix(Matrix::from_list(l2.to_vec()).into())), - (_, V::List(l)) if l.len() > 0 - => (a, V::Matrix(Matrix::from_list(l.to_vec()).into())), - (V::List(l), _) if l.len() > 0 - => (V::Matrix(Matrix::from_list(l.to_vec()).into()), b), - _ => (a, b), - } -} - -impl Add for Value { - type Output = Result; - fn add(self, rhs: Self) -> Self::Output { - use Value::*; - match promote(self, rhs) { - (Int(x), Int(y)) => Ok(Int(x + y)), - (Float(x), Float(y)) => Ok(Float(x + y)), - (Ratio(x), Ratio(y)) => Ok(Ratio(x + y)), - (Complex(x), Complex(y)) => Ok(Complex(x + y)), - (Matrix(x), Matrix(y)) => Ok(Matrix((x + y)?.into())), - (Matrix(x), r) => Ok(Matrix(x.increment(r)?.into())), - (l, Matrix(y)) => Ok(Matrix(y.increment(l)?.into())), - (String(str), value) => Ok(String(Rc::from( - format!("{str}{value}") - ))), - (value, String(str)) => Ok(String(Rc::from( - format!("{value}{str}") - ))), - (List(mut l1), List(l2)) => { - l1.extend_from_slice(&l2); - Ok(List(l1)) - }, - (l, r) => error!("cannot add: {l:?} + {r:?}") - } - } -} - -impl Sub for Value { - type Output = Result; - fn sub(self, rhs: Self) -> Self::Output { - use Value::*; - match promote(self, rhs) { - (Int(x), Int(y)) => Ok(Int(x - y)), - (Float(x), Float(y)) => Ok(Float(x - y)), - (Ratio(x), Ratio(y)) => Ok(Ratio(x - y)), - (Complex(x), Complex(y)) => Ok(Complex(x - y)), - (Matrix(x), Matrix(y)) => Ok(Matrix((x - y)?.into())), - (Matrix(x), r) => Ok(Matrix(x.decrement(r)?.into())), - (l, r) => error!("cannot subtract: {l:?} - {r:?}") - } - } -} - -impl Mul for Value { - type Output = Result; - fn mul(self, rhs: Value) -> Self::Output { - use Value::*; - match promote(self, rhs) { - (Int(x), Int(y)) => Ok(Int(x * y)), - (Float(x), Float(y)) => Ok(Float(x * y)), - (Ratio(x), Ratio(y)) => Ok(Ratio(x * y)), - (Complex(x), Complex(y)) => Ok(Complex(x * y)), - (Matrix(x), Matrix(y)) => Ok(Matrix((x * y)?.into())), - (Matrix(x), r) => Ok(Matrix(x.scale(r)?.into())), - (l, Matrix(y)) => Ok(Matrix(y.scale(l)?.into())), - (l, r) => error!("cannot multiply: {l:?} * {r:?}") - } - } -} - -impl Div for Value { - type Output = Result; - fn div(self, rhs: Value) -> Self::Output { - use Value::*; - match promote(self, rhs) { - (Int(_), Int(0)) => error!("cannot divide by zero"), - (Int(x), Int(y)) => Ok(Ratio(Rational64::new(x, y))), - (Float(x), Float(y)) => Ok(Float(x / y)), - (Ratio(x), Ratio(y)) => Ok(Ratio(x / y)), - (Complex(x), Complex(y)) => Ok(Complex(x / y)), - (l, r) => error!("cannot divide: {l:?} / {r:?}") - } - } -} - -impl BitOr for Value { - type Output = Result; - fn bitor(self, rhs: Value) -> Self::Output { - use Value::*; - match promote(self, rhs) { - (Int(x), Int(y)) => Ok(Int(x | y)), - (l, r) => error!("cannot bitwise or: {l:?} | {r:?}") - } - } -} - -impl BitAnd for Value { - type Output = Result; - fn bitand(self, rhs: Value) -> Self::Output { - use Value::*; - match promote(self, rhs) { - (Int(x), Int(y)) => Ok(Int(x & y)), - (l, r) => error!("cannot bitwise and: {l:?} & {r:?}") - } - } -} - -impl BitXor for Value { - type Output = Result; - fn bitxor(self, rhs: Value) -> Self::Output { - use Value::*; - match promote(self, rhs) { - (Int(x), Int(y)) => Ok(Int(x ^ y)), - (l, r) => error!("cannot bitwise xor: {l:?} ^ {r:?}") - } - } -} - -impl Shl for Value { - type Output = Result; - fn shl(self, rhs: Value) -> Self::Output { - use Value::*; - match promote(self, rhs) { - (Int(x), Int(y)) => Ok(Int(x << y)), - (l, r) => error!("cannot bitwise shift left: {l:?} << {r:?}") - } - } -} - -impl Shr for Value { - type Output = Result; - fn shr(self, rhs: Value) -> Self::Output { - use Value::*; - match promote(self, rhs) { - (Int(x), Int(y)) => Ok(Int(x >> y)), - (l, r) => error!("cannot bitwise shift right: {l:?} >> {r:?}") - } - } -} - -fn ipow(n: i64, d: i64, p: i64) -> Result<(i64, i64)> { - Ok(match (n, d, p) { - (0, _, 0) => return error!("cannot exponent: 0 ** 0"), - (0, _, _) => (0, 1), - (_, _, 0) => (1, 1), - (1, 1, _) => (1, 1), - (n, d, p) if p < 0 => (d.pow((-p) as u32), n.pow((-p) as u32)), - (n, d, p) => (n.pow(p as u32), d.pow(p as u32)), - }) -} - -impl Value { - - pub fn modulo(self, rhs: Value) -> Result { - use Value::*; - match promote(self, rhs) { - (Int(x), Int(y)) => Ok(Int(x % y)), - (Float(x), Float(y)) => Ok(Float(x % y)), - (Ratio(x), Ratio(y)) => Ok(Ratio(x % y)), - (Complex(x), Complex(y)) => Ok(Complex(x % y)), - (l, r) => error!("cannot modulo: {l:?} % {r:?}") - } - } - - pub fn pow(self, rhs: Value) -> Result { - use Value::*; - if let (Ratio(x), Int(y)) = (&self, &rhs) { - return Ok(Ratio(ipow(*(*x).numer(), *(*x).denom(), *y)?.into())); - } - match promote(self, rhs) { - (Int(x), Int(y)) => Ok(Ratio(ipow(x, 1, y)?.into())), - (Float(x), Float(y)) => Ok(Float(x.powf(y))), - (Ratio(x), Ratio(y)) => Ok(Float(ratio_to_f64(x).powf(ratio_to_f64(y)))), - (Complex(x), Complex(y)) => Ok(Complex(x.powc(y))), - (l, r) => error!("cannot exponent: {l:?} ** {r:?}") - } - } - - pub fn clone_inside(&self) -> Self { - use Value as V; - match self { - V::List(l) => V::List(l.clone_inside()), - V::Table(t) => V::Table(t.clone_inside()), - V::Matrix(m) => V::Matrix(m.clone_inside()), - _ => self.clone() - } - } -} - -impl Eq for Value {} - -impl PartialEq for Value { - fn eq(&self, other: &Self) -> bool { - use Value::*; - match (self, other) { - (Nil, Nil) => true, - (Bool(a), Bool(b)) => a == b, - (Int(a), Int(b)) => *a == *b, - (Ratio(a), Ratio(b)) => *a == *b, - (Float(a), Float(b)) => *a == *b, - (Complex(a), Complex(b)) => *a == *b, - (Int(a), Ratio(b)) => Rational64::from(*a) == *b, - (Ratio(a), Int(b)) => *a == Rational64::from(*b), - (Int(a), Float(b)) => *a as f64 == *b, - (Float(a), Int(b)) => *a == *b as f64, - (Int(a), Complex(b)) => Complex64::from(*a as f64) == *b, - (Complex(a), Int(b)) => *a == Complex64::from(*b as f64), - (Ratio(a), Float(b)) => ratio_to_f64(*a) == *b, - (Float(a), Ratio(b)) => *a == ratio_to_f64(*b), - (Ratio(a), Complex(b)) => Complex64::from(ratio_to_f64(*a)) == *b, - (Complex(a), Ratio(b)) => *a == Complex64::from(ratio_to_f64(*b)), - (Float(a), Complex(b)) => Complex64::from(*a) == *b, - (Complex(a), Float(b)) => *a == Complex64::from(*b), - (String(a), String(b)) => *a == *b, - (List(a), List(b)) => *a == *b, - (Matrix(a), Matrix(b)) => a == b, - _ => false, - } - } -} - -impl PartialOrd for Value { - fn partial_cmp(&self, other: &Self) -> Option { - use Value::*; - match (self, other) { - (Nil, Nil) => Some(Ordering::Equal), - (Bool(a), Bool(b)) => a.partial_cmp(b), - (Int(a), Int(b)) => a.partial_cmp(b), - (Ratio(a), Ratio(b)) => a.partial_cmp(b), - (Float(a), Float(b)) => a.partial_cmp(b), - (Int(a), Ratio(b)) => Rational64::from(*a).partial_cmp(b), - (Ratio(a), Int(b)) => a.partial_cmp(&Rational64::from(*b)), - (Int(a), Float(b)) => (*a as f64).partial_cmp(b), - (Float(a), Int(b)) => a.partial_cmp(&(*b as f64)), - (Ratio(a), Float(b)) => ratio_to_f64(*a).partial_cmp(b), - (Float(a), Ratio(b)) => a.partial_cmp(&ratio_to_f64(*b)), - (String(a), String(b)) => a.partial_cmp(b), - (List(a), List(b)) => a.partial_cmp(b), - (Matrix(a), Matrix(b)) => a.domain.partial_cmp(&b.domain), - _ => None, - } - } -} - -impl Value { - pub fn val_cmp(&self, other: &Self) -> Result { - self.partial_cmp(other) - .map_or_else(|| error!("cannot compare: {self:?} and {other:?}"), Ok) - } - - fn index_single(&self, index: &Value) -> Result { - use Value as V; - match (self, index) { - (V::Table(t), index) => { - Ok(t.get(index)?.unwrap_or(&Value::Nil).clone()) - }, - (V::List(l), V::Int(i)) => { - if *i < 0 || *i as usize >= l.len() { - return error!("{i} out of bounds for {self:?}") - } - Ok(l[*i as usize].clone()) - }, - (V::Matrix(m), V::Int(i)) => { - if *i < 0 || *i as usize >= m.values.len() { - return error!("{i} out of bounds for {self:?}") - } - Ok(m.values[*i as usize].clone()) - }, - _ => return error!("{index:?} cant index {self:?}") - } - } - - fn index_multiple(&self, indexes: &Vec) -> Result { - use Value as V; - match self { - V::List(..) => { - let mut ret = Vec::new(); - for index in indexes { - let res = self.index_single(index)?; - ret.push(res); - } - Ok(V::List(ret.into())) - } - V::Table(..) => { - let mut ret = ValueMap::new(); - for index in indexes { - let res = self.index_single(index)?; - ret.insert(index.clone(), res)?; - } - Ok(V::Table(ret.into())) - } - V::Matrix(m) => { - let err = || error!("{self:?} can be index by [Int] or [Int;Int]"); - if indexes.len() != 2 { - return err() - } - let lhs = indexes[0].clone(); - let rhs = indexes[1].clone(); - match (lhs, rhs) { - (V::Nil, V::Nil) => { - Ok(V::Matrix(m.clone_inside())) - }, - (V::Int(row), V::Nil) => { - let Some((_, row)) = m.rows().enumerate().filter(|(idx, _)| *idx as i64 == row).next() else { - return err(); - }; - let row: Vec = row.into_iter().map(|e| e.clone()).collect(); - Ok(V::Matrix(Matrix::new(row.len(), 1, row).into())) - }, - (V::Nil, V::Int(col)) => { - let Some((_, col)) = m.cols().enumerate().filter(|(idx, _)| *idx as i64 == col).next() else { - return err(); - }; - let col: Vec = col.into_iter().map(|e| e.clone()).collect(); - Ok(V::Matrix(Matrix::new(1, col.len(), col).into())) - }, - (V::Int(row), V::Int(col)) => { - if row < 0 || col < 0 { - return err(); - } - m.get(row as usize, col as usize) - } - _ => return err() - } - } - _ => return error!("cannot index {self:?}") - } - } - - pub fn index(&self, index: &Vec) -> Result { - if index.len() == 0 { - Ok(self.clone_inside()) - } else if index.len() == 1 { - self.index_single(&index[0]) - } else { - self.index_multiple(index) - } - } - - fn store_index_single(&mut self, index: &Value, store: Value) -> Result<()> { - use Value as V; - let err = format!("{self:?}"); - match (self, index) { - (V::Table(t), index) => { - t.insert(index.clone(), store) - }, - (V::List(l), V::Int(i)) => { - if *i < 0 || *i as usize >= l.len() { - return error!("{i} out of bounds for {err}") - } - l[*i as usize] = store; - Ok(()) - }, - (V::Matrix(m), V::Int(i)) => { - if *i < 0 || *i as usize >= m.values.len() { - return error!("{i} out of bounds for {err}") - } - m.values[*i as usize] = store; - Ok(()) - }, - _ => return error!("{index:?} cant index {err}") - } - } - - fn store_index_multiple(&mut self, indexes: &Vec, store: Value) -> Result<()> { - use Value as V; - match self { - V::List(..) => { - for index in indexes { - self.store_index_single(index, store.clone())?; - } - Ok(()) - } - V::Table(..) => { - for index in indexes { - self.store_index_single(index, store.clone())?; - } - Ok(()) - } - _ => return error!("cannot index {self:?}") - } - } - - pub fn store_index(&mut self, index: &Vec, store: Value) -> Result<()> { - if index.len() == 0 { - Ok(()) - } else if index.len() == 1 { - self.store_index_single(&index[0], store) - } else { - self.store_index_multiple(index, store) - } - } - - pub fn store_field_access(&mut self, ident: &str, val: Value) -> Result<()> { - use Value as V; - match self { - V::Table(t) => { - let key = V::String(Rc::from(ident)); - Ok(t.insert(key, val)?) - }, - _ => return error!("cannot field access assign {self:?}") - } - } - - pub fn field_access(&self, ident: &str) -> Result { - use Value as V; - match self { - V::Table(t) => { - let key = V::String(Rc::from(ident)); - Ok(t.get(&key)?.unwrap_or(&V::Nil).clone()) - }, - _ => return error!("cannot field access {self:?}") - } - } - - pub fn binary_op(op: BinaryOp, lhs: Value, rhs: Value) -> Result { - use BinaryOp::*; - match op { - Add => lhs + rhs, - Subtract => lhs - rhs, - Multiply => lhs * rhs, - Divide => lhs / rhs, - Modulo => lhs.modulo(rhs), - Power => lhs.pow(rhs), - BitwiseAnd => lhs & rhs, - BitwiseOr => lhs | rhs, - BitwiseXor => lhs ^ rhs, - BitwiseShiftLeft => lhs << rhs, - BitwiseShiftRight => lhs >> rhs, - Equals => Ok(Self::Bool(lhs == rhs)), - NotEquals => Ok(Self::Bool(lhs != rhs)), - GreaterEquals => Ok(Self::Bool(lhs >= rhs)), - LessEquals => Ok(Self::Bool(lhs <= rhs)), - GreaterThan => Ok(Self::Bool(lhs > rhs)), - LessThan => Ok(Self::Bool(lhs < rhs)), - Range | RangeEq => { - let Value::Int(lhs) = lhs else { - return error!("range can only take [Int]'s") - }; - let Value::Int(rhs) = rhs else { - return error!("range can only take [Int]'s") - }; - Ok(Self::Range(Rc::new((lhs, rhs, op == RangeEq)))) - }, - } - } - - pub fn unary_op(op: UnaryOp, val: Value) -> Value { - use UnaryOp::*; - match op { - Negate => -val, - Not => Self::Bool(!val), - } - } -} - -impl Neg for Value { - type Output = Value; - - fn neg(self) -> Self::Output { - use Value::*; - match self { - Bool(b) => Bool(!b), - Int(i) => Int(-i), - Float(f) => Float(-f), - Ratio(r) => Ratio(-r), - Complex(c) => Complex(-c), - _ => return Float(f64::NAN) - } - } -} - -impl Not for Value { - type Output = bool; - - fn not(self) -> Self::Output { - use Value as V; - match self { - V::Nil => true, - V::Bool(b) => !b, - V::Int(i) => i == 0, - V::Float(f) => f == 0.0, - V::Ratio(r) => *(r.numer()) == 0 || *(r.denom()) == 0, - V::Complex(c) => !c.is_normal(), - V::Regex(_) => false, - V::List(_) => false, - V::Matrix(_) => false, - V::Table(_) => false, - V::String(s) => s.as_ref() == "", - V::Function(_) => false, - V::Iter(_) => false, - V::Range(_) => false, - V::File(_) => false, - V::Error(_) => false, - } - } -} - -impl Value { - - pub fn into_iter_fn(self) -> Result> { - let Value::Iter(iter) = self.into_iter()? else { - return error!("bypassed iter check") - }; - Ok(iter) - } - - pub fn into_iter(self) -> Result { - use Value as V; - Ok(match self { - V::Iter(..) => self, - V::List(l) => { - let iter = RefCell::new(l.into_inner().into_iter()); - iter!(move |_,_| { - match iter.borrow_mut().next() { - Some(v) => Ok(v), - None => Ok(V::Nil), - } - }) - }, - V::Range(r) => { - let r = (*r).clone(); - let lhs = RefCell::new(r.0); - let rhs = r.1; - iter!(move |_,_| { - let val = *lhs.borrow(); - let next = *lhs.borrow() + 1; - if (!r.2 && *lhs.borrow() < rhs) || (r.2 && *lhs.borrow() <= rhs) { - *lhs.borrow_mut() = next; - return Ok(Value::Int(val)) - } - Ok(Value::Nil) - }) - }, - V::Function(f) => { - if f.arity > 0 || f.variadic { - return error!("iterator functions cannot be varadic or take arguments") - } - V::Iter(f) - }, - val => return error!("cannot turn {val:?} into an iterator") - }) - } -} - -fn dot(lhs: Vec<&Value>, rhs: Vec<&Value>) -> Result { - let len = lhs.len(); - - let mut res = Value::Int(0); - for i in 0..len { - let val = (lhs[i].clone() * rhs[i].clone())?; - res = (res + val)?; - } - - Ok(res) -} - -#[derive(Clone)] -pub struct Matrix { - pub domain: usize, - pub codomain: usize, - pub values: Vec -} - -impl Matrix { - pub fn new( - domain: usize, - codomain: usize, - values: Vec - ) -> Self { - Self { - domain, - codomain, - values - } - } - - pub fn from_list( - values: Vec - ) -> Self { - Self { - domain: values.len(), - codomain: 1, - values - } - } - - pub fn empty( - domain: usize, - codomain: usize - ) -> Self { - let values = (0..(domain * codomain)).into_iter() - .map(|_| Value::Int(0)) - .collect(); - Self { - domain, - codomain, - values - } - } - - pub fn get(&self, row: usize, col: usize) -> Result { - if row >= self.codomain || col >= self.domain { - return error!("[{};{}] out of bounds for [Matrix {}x{}]", row, col, self.domain, self.codomain) - } - let idx = col + row * self.domain; - Ok(self.values[idx].clone()) - } - - - - pub fn set(&mut self, row: usize, col: usize, val: Value) -> Result<()> { - if row >= self.codomain || col >= self.domain { - return error!("[{};{}] out of bounds for [Matrix {}x{}]", row, col, self.domain, self.codomain) - } - let idx = col + row * self.domain; - self.values[idx] = val; - Ok(()) - } - - pub fn rows<'a>(&'a self) -> MatrixRows<'a> { - MatrixRows { - matrix: self, - row: 0 - } - } - - pub fn cols<'a>(&'a self) -> MatrixCols<'a> { - MatrixCols { - matrix: self, - col: 0 - } - } - - // SPLCIE DOMAIN - pub fn splice_cols(&self, col_start: usize, col_end: usize) -> Result { - if col_start <= col_end || col_end > self.domain { - return error!("[_;{}..{}] invalid for [Matrix {}x{}]", col_start, col_end, self.domain, self.codomain) - } - - let mut cols = Vec::new(); - - for (i, col) in self.cols().enumerate() { - if i >= col_start && i < col_end { - cols.push(col); - } - } - - let domain = cols.len(); - let codomain = cols[0].len(); - let mut res = Self::empty(domain, codomain); - - for i in 0..domain { - for j in 0..codomain { - res.set(j, i, cols[i][j].clone())?; - } - } - - Ok(res) - } - - // SPLICE CODOMAIN - pub fn splice_rows(&self, row_start: usize, row_end: usize) -> Result { - if row_start <= row_end || row_end > self.codomain { - return error!("[{}..{};_] invalid for [Matrix {}x{}]", row_start, row_end, self.domain, self.codomain) - } - - let mut rows = Vec::new(); - - for (i, row) in self.rows().enumerate() { - if i >= row_start && i < row_end { - rows.push(row); - } - } - - let domain = rows[0].len(); - let codomain = rows.len(); - let mut res = Self::empty(domain, codomain); - - for i in 0..domain { - for j in 0..codomain { - res.set(j, i, rows[j][i].clone())?; - } - } - - Ok(res) - } - - pub fn increment(&self, increment: Value) -> Result { - let values = self.values.iter() - .map(|v| v.clone() + increment.clone()) - .collect::>>()?; - Ok(Matrix::new(self.domain, self.codomain, values)) - } - - pub fn decrement(&self, decrement: Value) -> Result { - let values = self.values.iter() - .map(|v| v.clone() - decrement.clone()) - .collect::>>()?; - Ok(Matrix::new(self.domain, self.codomain, values)) - } - - pub fn scale(&self, scale: Value) -> Result { - let values = self.values.iter() - .map(|v| v.clone() * scale.clone()) - .collect::>>()?; - Ok(Matrix::new(self.domain, self.codomain, values)) - } - - pub fn join_right(&self, other: &Matrix) -> Result { - if self.codomain != other.codomain { - return error!("matrix codomain's do not match"); - } - let mut r1 = self.rows(); - let mut r2 = other.rows(); - let mut rows = Vec::new(); - loop { - let Some(r1) = r1.next() else { break; }; - let Some(r2) = r2.next() else { break; }; - - let mut row = r1 - .into_iter() - .map(|v| v.clone()) - .collect::>(); - - row.extend(r2.into_iter().map(|v| v.clone())); - - rows.push(row); - } - - let values = rows - .into_iter() - .reduce(|mut a,b| {a.extend(b); a}) - .unwrap(); - - Ok(Matrix::new(self.domain + other.domain, self.codomain, values)) - } - - pub fn join_bottom(&self, other: &Matrix) -> Result { - if self.domain != other.domain { - return error!("matrix domain's do not match"); - } - let mut values = self.values.clone(); - values.extend(other.values.clone()); - Ok(Matrix::new(self.domain, self.codomain + other.codomain, values)) - } -} - -impl Debug for Matrix { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "[Matrix {}x{}]", self.domain, self.codomain) - } -} - -impl PartialEq for Matrix { - fn eq(&self, other: &Self) -> bool { - if self.domain != other.domain || self.codomain != other.codomain { - return false - } - - for i in 0..self.values.len() { - if self.values[i] != other.values[i] { - return false; - } - } - - return true; - } -} - -impl Add for Matrix { - type Output = Result; - - fn add(self, rhs: Self) -> Self::Output { - if self.domain != rhs.domain || self.codomain != rhs.codomain { - return error!("cannot add: [Matrix {}x{}] + [Matrix {}x{}]", - self.domain, self.codomain, - rhs.domain, rhs.codomain) - } - let mut res = Matrix::empty(self.domain, self.codomain); - for col in 0..self.domain { - for row in 0..self.codomain { - let add = self.get(row, col)? + rhs.get(row, col)?; - res.set(row, col, add?)?; - } - } - Ok(res) - } -} - -impl Sub for Matrix { - type Output = Result; - - fn sub(self, rhs: Self) -> Self::Output { - if self.domain != rhs.domain || self.codomain != rhs.codomain { - return error!("cannot sub: [Matrix {}x{}] - [Matrix {}x{}]", - self.domain, self.codomain, - rhs.domain, rhs.codomain) - } - let mut res = Matrix::empty(self.domain, self.codomain); - for col in 0..self.domain { - for row in 0..self.codomain { - let sub = self.get(row, col)? - rhs.get(row, col)?; - res.set(row, col, sub?)?; - } - } - Ok(res) - } -} - -impl Mul for Matrix { - type Output = Result; - - fn mul(self, rhs: Self) -> Self::Output { - if self.domain != rhs.codomain { - return error!("cannot multiply: [Matrix {}x{}] * [Matrix {}x{}]", - self.domain, self.codomain, - rhs.domain, rhs.codomain) - } - let mut res = Self::empty(rhs.domain, self.codomain); - for (i, row) in self.rows().enumerate() { - for (j, col) in rhs.cols().enumerate() { - let dot = dot(row.clone(), col.clone())?; - res.set(i, j, dot)?; - } - } - Ok(res) - } -} - -pub struct MatrixRows<'a> { - matrix: &'a Matrix, - row: usize -} - -impl<'a> Iterator for MatrixRows<'a> { - type Item = Vec<&'a Value>; - - fn next(&mut self) -> Option { - if self.row >= self.matrix.codomain { - return None - } - - let row_start = self.row * self.matrix.domain; - let row_end = row_start + self.matrix.domain; - - let res = self.matrix.values - .iter() - .enumerate() - .filter(|(idx, _)| *idx >= row_start && *idx < row_end) - .map(|v| v.1) - .collect(); - - self.row += 1; - - Some(res) - } -} - -pub struct MatrixCols<'a> { - matrix: &'a Matrix, - col: usize -} - -impl<'a> Iterator for MatrixCols<'a> { - type Item = Vec<&'a Value>; - - fn next(&mut self) -> Option { - if self.col >= self.matrix.domain { - return None - } - - let res = self.matrix.values - .iter() - .enumerate() - .filter(|(idx, _)| *idx % self.matrix.domain == self.col) - .map(|v| v.1) - .collect(); - - self.col += 1; - - Some(res) - } -} - -impl Display for Matrix { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let mut max_cols = vec![0; self.domain]; - let mut vals: Vec = Vec::with_capacity(self.domain * self.codomain); - for row in 0..self.codomain { - for col in 0..self.domain { - let idx = col + row * self.domain; - let el = &self.values[idx]; - let s = match f.alternate() { - true => format!("{:#}", el), - false => format!("{}", el) - }; - max_cols[col] = max_cols[col].max(s.len()); - vals.push(s); - } - } - - write!(f, "\n")?; - for row in 0..self.codomain { - for col in 0..self.domain { - let idx = col + row * self.domain; - let s = vals[idx].as_str(); - let width = max_cols[col]; - write!(f, " {s:>width$}")?; - } - write!(f, "\n")?; - } - - Ok(()) - } -} diff --git a/matrix/src/vm.rs b/matrix/src/vm.rs deleted file mode 100644 index e511adf..0000000 --- a/matrix/src/vm.rs +++ /dev/null @@ -1,490 +0,0 @@ -use std::{rc::Rc, fmt::{Debug, Display}, usize, ops::{Index, IndexMut}, collections::HashMap, cell::RefCell, sync::{atomic::{AtomicUsize, Ordering}, Arc}}; -use crate::{value::{Value, self, ValueMap, Matrix}, Result, gc::Gc, chunk::{Function, Instruction, Chunk, InnerFunction}, compiler::NamesTable, native}; - -#[derive(Debug)] -pub enum Error { - ValueError(value::Error), - NotFunction(Value), - InvArity(usize, usize, Rc), - ExecNative, -} - -impl std::error::Error for Error {} - -impl Display for Error { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - use Error::*; - match self { - ValueError(err) => write!(f, "{err}"), - NotFunction(v) => write!(f, "{v:?} is not a function"), - InvArity(wanted, had, name) => write!(f, "function {name} takes {wanted} params but was given {had}"), - ExecNative => write!(f, "cannot execute a native function"), - } - } -} - -impl From for Error { - fn from(value: value::Error) -> Self { - Error::ValueError(value) - } -} -pub struct Stack { - inner: Vec -} - -impl Stack { - pub fn new() -> Self { - Self { inner: vec![] } - } - - pub fn pop(&mut self) -> T { - self.inner.pop().expect("stack empty") - } - - pub fn push(&mut self, val: T) { - self.inner.push(val) - } - - pub fn split_off(&mut self, len: usize) -> Self { - let inner = self.inner.split_off(len); - Self { inner } - } - - pub fn truncate(&mut self, len: usize) { - self.inner.truncate(len); - } - - pub fn len(&self) -> usize { - self.inner.len() - } -} - -impl Display for Stack { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f,"[ ")?; - for (i, el) in self.inner.iter().enumerate() { - if i != 0 { - write!(f, ", ")?; - } - write!(f, "{el}")?; - } - write!(f, " ]")?; - Ok(()) - } -} - -impl IndexMut for Stack { - fn index_mut(&mut self, index: usize) -> &mut Self::Output { - &mut self.inner[index] - } -} - -impl Index for Stack { - type Output = T; - - fn index(&self, index: usize) -> &Self::Output { - &self.inner[index] - } -} - -#[derive(Clone)] -pub struct StackFrame { - #[allow(dead_code)] - name: Rc, - body: Rc, - ip: usize, - bp: usize, - lp: usize, - depth: usize, -} - -struct VmError { - err: crate::Error, - frames: Vec -} - -impl From for VmError { - fn from(err: crate::Error) -> Self { - VmError { - err, - frames: Vec::new() - } - } -} - -impl From for VmError { - fn from(err: self::Error) -> Self { - VmError { - err: err.into(), - frames: Vec::new() - } - } -} - -type VmResult = std::result::Result; - -impl StackFrame { - fn new(vm: &Vm, name: Rc, body: Rc, arity: usize, depth: usize) -> Self { - Self { - name, - body, - bp: vm.stack.len() - arity, - lp: vm.locals.len(), - ip: 0, - depth - } - } -} - -impl Default for StackFrame { - fn default() -> Self { - StackFrame { - name: Rc::from(""), - body: Rc::new(Chunk::default()), - bp: 0, - lp: 0, - ip: 0, - depth: 0 - } - } -} - -pub enum Interupt { - KeyboardInterupt = 1 -} - -struct TryScope { - stack_len: usize, - locals_len: usize, - frame_depth: usize, - err_idx: usize, -} - -pub struct Vm { - stack: Stack, - locals: Stack, - trystack: Vec, - globals: Rc>>, - names: NamesTable, - global_names: NamesTable, - interupt: Arc, -} - -impl Vm { - - fn push(&mut self, val: Value) { - self.stack.push(val) - } - - fn pop(&mut self) -> Value { - self.stack.pop() - } - - fn top(&mut self) -> Value { - self.stack[self.stack.len() - 1].clone() - } - - pub fn names(&self) -> NamesTable { - self.names.clone() - } - - pub fn global_names(&self) -> NamesTable { - self.global_names.clone() - } - - pub fn interupt(&self) -> Arc { - self.interupt.clone() - } - - pub fn new() -> Self { - Self { - stack: Stack::new(), - locals: Stack::new(), - trystack: Vec::new(), - globals: Rc::new(RefCell::new(HashMap::new())), - names: Rc::new(RefCell::new(Vec::new())), - global_names: Rc::new(RefCell::new(Vec::new())), - interupt: Arc::new(AtomicUsize::new(0)), - } - } - - pub fn load_global(&mut self, value: Value, name: &str) { - let idx = self.global_names.borrow().len(); - self.global_names.borrow_mut().push(name.into()); - self.globals.borrow_mut().insert(idx as u16, value); - } - - pub fn load_global_fn(&mut self, value: Rc, name: &str) { - self.load_global(Value::Function(value), name) - } - - fn init_frame(&mut self, fun: Rc) -> Result { - let InnerFunction::Compiled(ref compiled_chunk) = fun.fun else { - return Err(self::Error::ExecNative.into()) - }; - Ok(StackFrame::new( - self, - fun.name.clone(), - compiled_chunk.clone(), - fun.arity, - 0 - )) - } - - fn check_interupt(&self) -> bool { - self.interupt.load(Ordering::Relaxed) != 0 - } - - fn exec_ins(&mut self, frame: &mut StackFrame, ins: Instruction) -> VmResult> { - use Instruction as I; - match ins { - I::NoOp => {}, - I::CreateLocal => self.locals.push(self.stack.pop()), - I::LoadLocal(idx) => {self.stack.push(self.locals[idx as usize].clone());}, - I::StoreLocal(idx) => self.locals[frame.bp + idx as usize] = self.pop(), - I::DiscardLocals(count) => {self.locals.truncate(self.locals.len() - count as usize)}, - I::LoadGlobal(idx) => { - let val = self.globals - .borrow_mut() - .get(&idx) - .unwrap() - .clone(); - self.stack.push(val); - }, - I::StoreGlobal(idx) => { - let val = self.pop(); - self.globals.borrow_mut().insert(idx, val); - }, - I::Const(idx) => self.push(frame.body.constants[idx as usize].clone()), - I::Int(i) => self.push(Value::Int(i as i64)), - I::True => self.push(Value::Bool(true)), - I::False => self.push(Value::Bool(false)), - I::Nil => self.push(Value::Nil), - I::Dup => self.push(self.stack[self.stack.len() - 1].clone()), - I::Discard(count) => {self.stack.truncate(self.stack.len() - count as usize)}, - I::UnaryOp(op) => { - let val = self.pop(); - self.push(Value::unary_op(op, val)); - }, - I::BinaryOp(op) => { - let rhs = self.pop(); - let lhs = self.pop(); - self.push(Value::binary_op(op, lhs, rhs)?); - }, - I::NewList(items) => { - let list = self.stack.split_off(self.stack.len() - items as usize); - self.push(Value::List(list.inner.into())); - }, - I::NewTable(items) => { - let mut table = ValueMap::new(); - for _ in 0..items { - let value = self.pop(); - let key = self.pop(); - table.insert(key, value)?; - } - self.push(Value::Table(table.into())) - }, - I::NewMatrix(items, domain) => { - let values = self.stack.split_off(self.stack.len() - items as usize).inner; - let domain = domain as usize; - let codomain = values.len() / domain; - self.push(Value::Matrix(Gc::new(Matrix::new(domain, codomain, values)))); - } - I::Jump(idx) => { - if self.check_interupt() { - return Ok(Some(Value::Nil)) - } - frame.ip = idx as usize; - } - I::JumpTrue(idx) => { - if self.check_interupt() { - return Ok(Some(Value::Nil)) - } - if !!self.pop() { - frame.ip = idx as usize; - } - }, - I::JumpFalse(idx) => { - if self.check_interupt() { - return Ok(Some(Value::Nil)) - } - if !self.pop() { - frame.ip = idx as usize; - } - }, - I::JumpNil(idx) => { - if self.check_interupt() { - return Ok(Some(Value::Nil)) - } - if self.pop() == Value::Nil { - frame.ip = idx as usize; - } - }, - I::Call(arity) => { - let arity = arity as usize; - - let fun = self.pop(); - let Value::Function(fun) = fun else { - return Err(Error::NotFunction(fun).into()) - }; - - if !fun.variadic && arity > fun.arity { - return Err(Error::InvArity(fun.arity, arity as usize, fun.name.clone()).into()) - } - - let mut params = self.stack.split_off(self.stack.len() - arity).inner; - - if params.len() < fun.arity { - let new_arity = fun.arity - arity; - let val = native!("", new_arity, false, move |(vm, vm_frame), args| { - let mut params = params.clone(); - params.extend(args); - vm.exec_fn(vm_frame, fun.clone(), params).map_err(|e| e.err) - }); - self.stack.push(val); - return Ok(None); - } - - if fun.variadic { - if arity == fun.arity { - params.push(Value::List(vec![].into())); - } else { - let count = arity - fun.arity; - let varadic_params = params.split_off(params.len() - count); - params.push(Value::List(varadic_params.into())); - } - } - - let res = self.exec_fn(frame, fun, params)?; - self.push(res); - }, - I::Return => { - if self.check_interupt() { - return Ok(Some(Value::Nil)) - } - let ret = self.pop(); - self.stack.truncate(frame.bp); - self.locals.truncate(frame.lp); - return Ok(Some(ret)) - }, - I::Field(name_idx) => { - let expr = self.pop(); - let name = self.names.borrow()[name_idx as usize].clone(); - self.push(expr.field_access(&name)?); - }, - I::StoreField(name_idx) => { - let mut expr = self.pop(); - let value = self.pop(); - let name = self.names.borrow()[name_idx as usize].clone(); - expr.store_field_access(&name, value)?; - }, - I::Index(count) => { - let index = self.stack.split_off(self.stack.len() - count as usize); - let collection = self.pop(); - let value = collection.index(&index.inner)?; - self.stack.push(value); - }, - I::StoreIndex(count) => { - let index = self.stack.split_off(self.stack.len() - count as usize); - let mut collection = self.pop(); - let value = self.pop(); - collection.store_index(&index.inner, value)?; - }, - I::IterCreate => { - let val = self.pop().into_iter(); - self.push(val?); - }, - I::IterNext => { - let Value::Iter(iter) = self.top() else { panic!("bypassed iter check"); }; - let val = self.exec_fn(frame, iter, vec![])?; - self.push(val); - }, - I::Try(idx) => { - let scope = TryScope { - err_idx: idx as usize, - frame_depth: frame.depth, - stack_len: self.stack.len(), - locals_len: self.locals.len(), - }; - self.trystack.push(scope); - }, - I::TryEnd => { - self.trystack.pop().unwrap(); - }, - }; - - Ok(None) - } - - fn stack_trace(&mut self, frames: Vec) -> String { - let mut trace = String::from("\x1b[33m\x1b[1mStack Trace:\x1b[0m\n"); - for frame in frames { - let ip = frame.ip - 1; - let pos = frame.body.pos[ip]; - trace.push_str(&format!(" {} at {}:{}\n", &frame.name, pos.row, pos.col)); - } - trace - } - - fn exec_fn(&mut self, frame: &mut StackFrame, fun: Rc, params: Vec) -> VmResult { - if self.check_interupt() { - return Ok(Value::Nil) - } - let name = fun.name.clone(); - let params_len = params.len(); - match &fun.fun { - InnerFunction::Compiled(body) => { - for param in params { - self.stack.push(param); - } - let mut new_frame = StackFrame::new(self, name, body.clone(), params_len, frame.depth + 1); - self.exec(&mut new_frame) - }, - InnerFunction::Native(native_fun) => { - Ok(native_fun((self, frame), params)?) - } - } - } - - fn exec(&mut self, frame: &mut StackFrame) -> VmResult { - loop { - let ins = frame.body.code[frame.ip].clone(); - frame.ip += 1; - match self.exec_ins(frame, ins) { - Ok(Some(val)) => return Ok(val), - Ok(None) => {}, - Err(err) => { - if let Some(catch) = self.trystack.pop() { - if frame.depth != catch.frame_depth { - self.trystack.push(catch); - return Err(err); - } - self.stack.truncate(catch.stack_len); - self.locals.truncate(catch.locals_len); - frame.ip = catch.err_idx; - self.stack.push(Value::Error(err.err)); - } else { - let mut err = err; - err.frames.push(frame.clone()); - return Err(err) - } - }, - } - } - } - - pub fn run_fn(&mut self, frame: &mut StackFrame, fun: Rc, params: Vec) -> Result { - self.exec_fn(frame, fun, params).map_err(|e| e.err) - } - - pub fn run(&mut self, fun: Rc) -> Result { - let mut frame = self.init_frame(fun)?; - self.interupt.store(0, Ordering::SeqCst); - self.stack = Stack::new(); - self.locals = Stack::new(); - self.exec(&mut frame).map_err(|e| { - let trace = self.stack_trace(e.frames); - (e.err, trace).into() - }) - } -} -- cgit v1.2.3-freya