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,12 +221,23 @@ 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 = [ + "clap", + "ctrlc", + "matrix-lang", + "matrix-std", + "rustyline", +] + +[[package]] +name = "matrix-lang" version = "0.1.0" dependencies = [ "anyhow", @@ -235,32 +246,21 @@ dependencies = [ "regex", ] -[[package]] -name = "matrix-bin" -version = "0.1.0" -dependencies = [ - "clap", - "ctrlc", - "matrix", - "matrix-stdlib", - "rustyline", -] - [[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; + let line = match rl.readline(">> ") { + Ok(line) => line, + Err(ReadlineError::Eof) => break, + Err(_) => continue, }; - if let Err(_) = rl.add_history_entry(&line) { - break; - }; - 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/Cargo.lock b/matrix-lang/Cargo.lock similarity index 100% rename from matrix/Cargo.lock rename to matrix-lang/Cargo.lock diff --git a/matrix/Cargo.toml b/matrix-lang/Cargo.toml similarity index 91% rename from matrix/Cargo.toml rename to matrix-lang/Cargo.toml index 64e9210..067f77c 100644 --- a/matrix/Cargo.toml +++ b/matrix-lang/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "matrix" +name = "matrix-lang" version = "0.1.0" edition = "2021" diff --git a/matrix/src/ast.rs b/matrix-lang/src/ast.rs similarity index 88% rename from matrix/src/ast.rs rename to matrix-lang/src/ast.rs index de63630..5720f76 100644 --- a/matrix/src/ast.rs +++ b/matrix-lang/src/ast.rs @@ -1,42 +1,70 @@ -use std::{rc::Rc, ops::{Neg, Not}, fmt::Debug}; -use crate::{lex::{Position, TokenData}, value::{Value, InlineList, InlineMatrix, InlineTable}, Result}; +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, + Negate = 1, // equality - Not, + 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, - Subtract, - Multiply, - Divide, - Modulo, - Power, + Add = 1, + Subtract = 2, + Multiply = 3, + Divide = 4, + Modulo = 5, + Power = 6, // binary math - BitwiseAnd, - BitwiseOr, - BitwiseXor, - BitwiseShiftLeft, - BitwiseShiftRight, + BitwiseAnd = 7, + BitwiseOr = 8, + BitwiseXor = 9, + BitwiseShiftLeft = 10, + BitwiseShiftRight = 11, // equality - Equals, - NotEquals, - GreaterEquals, - LessEquals, - GreaterThan, - LessThan, + Equals = 12, + NotEquals = 13, + GreaterEquals = 14, + LessEquals = 15, + GreaterThan = 16, + LessThan = 17, // iter - Range, - RangeEq + Range = 18, + RangeEq = 19 } -pub type AstName = (Rc, Position); +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 { @@ -75,6 +103,7 @@ pub enum ExprData { Try(Box, AstName, Box), Let(AstName, Box), + Const(AstName, Box), Pipeline(Box, Box), @@ -83,22 +112,24 @@ pub enum ExprData { Return(Box), } -#[derive(Clone, PartialEq)] -pub struct Expr { - pub data: ExprData, - pub pos: Position +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 { - if f.alternate() { - write!(f, "{:#?}", self.data) - } else { - write!(f, "{:?}", self.data) - } + 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 } @@ -377,6 +408,9 @@ pub fn optimize(mut expr: Expr) -> Result { 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)?); 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/src/chunk.rs b/matrix-lang/src/chunk.rs similarity index 82% rename from matrix/src/chunk.rs rename to matrix-lang/src/chunk.rs index 495b787..2fc3d9e 100644 --- a/matrix/src/chunk.rs +++ b/matrix-lang/src/chunk.rs @@ -1,5 +1,5 @@ -use crate::{value::Value, ast::{UnaryOp, BinaryOp}, vm::{Vm, StackFrame}, Result, lex::Position}; -use std::{fmt::{Debug, Display}, rc::Rc}; +use std::fmt::{Debug, Display}; +use crate::prelude::*; #[derive(Clone, Default)] pub struct Chunk { @@ -18,53 +18,6 @@ impl Chunk { } } -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 { @@ -113,6 +66,26 @@ pub enum Instruction { 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::*; diff --git a/matrix/src/compiler.rs b/matrix-lang/src/compiler.rs similarity index 82% rename from matrix/src/compiler.rs rename to matrix-lang/src/compiler.rs index 6b6a94b..95c6ccf 100644 --- a/matrix/src/compiler.rs +++ b/matrix-lang/src/compiler.rs @@ -1,14 +1,14 @@ -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 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: NamesTable, + globals: GlobalsTable, names: NamesTable, repl: bool, debug: bool, @@ -39,7 +39,7 @@ impl<'c> CompilerBuilder<'c> { self } - pub fn globals(mut self, globals: NamesTable) -> Self { + pub fn globals(mut self, globals: GlobalsTable) -> Self { self.globals = globals; self } @@ -81,9 +81,9 @@ pub struct Compiler<'c> { name: Rc, parent: Option<&'c Compiler<'c>>, - locals: Vec>, - globals: Rc>>>, - names: Rc>>>, + locals: Vec, + globals: GlobalsTable, + names: NamesTable, root_is_block: bool, @@ -97,50 +97,25 @@ pub struct Compiler<'c> { debug: bool, } +#[derive(Clone)] struct Local { name: Rc, idx: usize, - scope: usize + scope: usize, + is_const: bool, } -#[derive(Debug, Clone)] -pub enum InnerError { - Undefined(Rc), - Redefined(Rc), - InvAssign(Expr), - InvContinue, - InvBreak, - NotImplemented(&'static str), +#[derive(Clone)] +pub struct Global { + pub name: Rc, + pub idx: usize, + pub is_const: bool, } -#[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()) +macro_rules! error { + ($($arg:tt)*) => { + exception!(COMPILE_EXCEPTION, $($arg)*) + }; } impl<'c> Compiler<'c> { @@ -185,35 +160,35 @@ impl<'c> Compiler<'c> { }; } - 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_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) -> usize { - self.globals.borrow_mut().push(name); - let c = self.globals.borrow().len() - 1; - c + 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, pos: Position) -> Result<()> { + 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 error(InnerError::Redefined(name), pos) + return Err(error!("redefined {name}").pos(pos)) } }; - self.create_local(name); - Ok(()) + Ok(self.create_local(name, is_const)) } - fn create_global_checked(&mut self, name: Rc, pos: Position) -> Result { + fn create_global_checked(&mut self, name: Rc, is_const: bool, pos: Position) -> Result { if let Some(_) = self.find_global(&name) { - return error(InnerError::Redefined(name).into(), pos) + return Err(error!("redefined {name}").pos(pos)) } - Ok(self.create_global(name)) + Ok(self.create_global(name, is_const)) } - fn find_local(&self, name: &str) -> Option> { + fn find_local(&self, name: &str) -> Option { for local in self.locals.iter().rev() { if local.name.as_ref() == name { return Some(local.clone()) @@ -222,13 +197,13 @@ impl<'c> Compiler<'c> { None } - fn find_global(&self, name: &str) -> Option { + 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) + for global in self.globals.borrow().iter() { + if global.name.as_ref() == name { + return Some(global.clone()) } } None @@ -294,7 +269,7 @@ impl<'c> Compiler<'c> { 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 { + Function { name: name.0.clone(), arity, fun: InnerFunction::Compiled(chunk.into()), @@ -304,10 +279,10 @@ impl<'c> Compiler<'c> { 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); + 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(), name.1)?; + self.create_local_checked(name.0.clone(), false, name.1)?; self.emit(I::CreateLocal, expr.pos); } }, @@ -316,7 +291,7 @@ impl<'c> Compiler<'c> { 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 { + Function { name: name.0.clone(), arity, fun: InnerFunction::Compiled(chunk.into()), @@ -341,7 +316,7 @@ impl<'c> Compiler<'c> { 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.create_local(err.0.clone(), true); self.emit(I::CreateLocal, err.1); self.compile_expr(catch)?; self.end_scope(); @@ -378,7 +353,7 @@ impl<'c> Compiler<'c> { 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.create_local(name.0.clone(), true); self.emit(I::CreateLocal, name.1); self.compile_expr(expr)?; self.emit(I::Discard(1), expr.pos); @@ -407,10 +382,21 @@ impl<'c> Compiler<'c> { 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); + 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(), name.1)?; + 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); } }, @@ -420,7 +406,7 @@ impl<'c> Compiler<'c> { self.collapse_scopes(scope); self.emit(I::Jump(top as u16), expr.pos); } else { - return error(InnerError::InvContinue, expr.pos) + return Err(error!("invalid continue outside loop").pos(expr.pos)) } }, E::Break => { @@ -430,7 +416,7 @@ impl<'c> Compiler<'c> { let tmpidx = self.emit_temp(expr.pos); self.loop_bot.push(tmpidx); } else { - return error(InnerError::InvBreak, expr.pos) + return Err(error!("invalid break outside loop").pos(expr.pos)) } }, E::Return(expr) => { @@ -444,9 +430,9 @@ impl<'c> Compiler<'c> { } 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); + self.emit(I::LoadGlobal(global.idx as u16), expr.pos); } else { - return error(InnerError::Undefined(name.clone()), expr.pos) + return Err(error!("variable '{name}' is undefined").pos(expr.pos)) }; }, E::Assign(lhs, rhs) => { @@ -455,14 +441,20 @@ impl<'c> Compiler<'c> { 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) { - self.emit(I::StoreGlobal(global as u16), lhs.pos); + 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(), lhs.pos)?; - self.emit(I::StoreGlobal(global as u16), lhs.pos); + 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(), lhs.pos)?; + self.create_local_checked(name.clone(), false, lhs.pos)?; self.emit(I::CreateLocal, lhs.pos); } }, @@ -478,7 +470,7 @@ impl<'c> Compiler<'c> { 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) + _ => return Err(error!("assignment to {lhs} is not allowed").pos(lhs.pos)) } } E::UnaryOp(expr, op) => { @@ -554,7 +546,7 @@ impl<'c> Compiler<'c> { 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.create_local(name.clone(), false); compiler.emit(I::CreateLocal, *pos); } compiler.compile_expr(body)?; @@ -611,7 +603,7 @@ impl<'c> Compiler<'c> { }; if self.loop_bot.len() > 0 { - return error(InnerError::InvBreak, pos) + return Err(error!("invalid break outside loop").pos(pos)) } if self.debug { diff --git a/matrix/src/lex.rs b/matrix-lang/src/lex.rs similarity index 87% rename from matrix/src/lex.rs rename to matrix-lang/src/lex.rs index 8a07234..b2487ad 100644 --- a/matrix/src/lex.rs +++ b/matrix-lang/src/lex.rs @@ -1,6 +1,5 @@ -use std::{rc::Rc, fmt::Debug}; -use regex::Regex; -use crate::Result; +use std::fmt::{Debug, Display}; +use crate::prelude::*; pub struct RegexToken { regex: Regex @@ -30,6 +29,36 @@ impl From for 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 @@ -106,6 +135,7 @@ pub enum TokenData { Else, While, Let, + Const, Function, True, False, @@ -133,48 +163,6 @@ pub struct Token { 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, @@ -203,6 +191,12 @@ impl> From for Lexer { } } +macro_rules! error { + ($($arg:tt)*) => { + exception!(PARSE_EXCEPTION, $($arg)*) + }; +} + impl Lexer { pub fn new>(input: T) -> Self { let data: Vec = input.into().chars().collect(); @@ -239,7 +233,7 @@ impl Lexer { fn next_not_eof(&mut self) -> Result { let c = self.next(); if c == '\0' { - return Err(Error::UnexpectedEof.into()) + return Err(error!("unexpected end of file")) } Ok(c) } @@ -247,7 +241,7 @@ impl Lexer { fn next_expect(&mut self, expected: char) -> Result { let c = self.next(); if c != expected { - return Err(Error::ExpectedChar(expected, c).into()) + return Err(error!("expected character '{c}'")) } Ok(c) } @@ -259,7 +253,6 @@ impl Lexer { } fn lex_string(&mut self, delimit: char) -> Result> { - use Error::*; let mut buf = String::new(); @@ -291,8 +284,8 @@ impl Lexer { 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))? + n1.to_digit(16).ok_or(error!("invalid digit '{n1}'"))? * 16 + + n2.to_digit(16).ok_or(error!("invalid digit '{n2}'"))? ).unwrap()); }, 'u' => { @@ -302,15 +295,15 @@ impl Lexer { let c = self.next_not_eof()?; if c == '}' { break } if n >= 0x1000_0000_u32 { - return Err(InvalidCodepoint.into()) + return Err(error!("invalid utf8 codepoint '{n}'")) } - n = n * 16 + c.to_digit(16).ok_or::(InvalidDigit(c).into())?; + n = n * 16 + c.to_digit(16).ok_or(error!("invalid digit '{c}'"))?; } - let ch = char::from_u32(n).ok_or::(InvalidCodepoint.into())?; + let ch = char::from_u32(n).ok_or(error!("invalid codepoint '{n}'"))?; buf.push(ch); }, - _ => return Err(InvalidStringEscape(next).into()) + _ => return Err(error!("invalid string escape '\\{next}'")) } } @@ -318,13 +311,12 @@ impl Lexer { } 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()) + return Err(error!("unexpected character '{initial}'")) } buf.push(initial); @@ -342,6 +334,7 @@ impl Lexer { "else" => T::Else, "while" => T::While, "let" => T::Let, + "const" => T::Const, "fn" | "function" => T::Function, "true" => T::True, "false" => T::False, @@ -364,7 +357,6 @@ impl Lexer { 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; @@ -375,7 +367,7 @@ impl Lexer { n = n * radix + (i as i64); char_found = true; } else if self.peek().is_ident() { - return Err(E::InvalidDigit(self.peek()).into()) + return Err(error!("invalid digit '{}'", self.peek())) } else { break; } @@ -384,13 +376,11 @@ impl Lexer { if char_found { Ok(T::Int(n)) } else { - Err(E::InvalidNumber(format!("0{radix_char}")).into()) + Err(error!("invalid number radix specifier '0{radix_char}'")) } } fn lex_number(&mut self, initial: char) -> Result { - use Error as E; - if initial == '0' { match self.peek() { 'x' => { @@ -459,7 +449,7 @@ impl Lexer { } if self.peek().is_ident() { - return Err(E::UnexpectedCharacter(self.peek()).into()) + return Err(error!("unexpected character '{}'", self.peek())) } if let Ok(int) = buf.parse::() { @@ -478,23 +468,11 @@ impl Lexer { return Ok(T::Float(float)) } - Err(E::InvalidNumber(buf).into()) + Err(error!("invalid number '{buf}'")) } - 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 { + fn read_token(&mut self, ignore_newlines: bool) -> Result { use TokenData as T; - use Error as E; self.skip_whitespace(ignore_newlines); @@ -705,7 +683,7 @@ impl Lexer { self.next(); T::Regex(regex::Regex::new(&self.lex_string(next)?) .map(|e| e.into()) - .map_err(|e| E::InvalidRegex(e.into()))?) + .map_err(|e| error!("invalid regex: '{e}'"))?) } _ => { self.lex_ident(char)? @@ -744,7 +722,6 @@ impl Lexer { 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, @@ -754,19 +731,47 @@ impl Lexer { }) } - 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) + 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 { - self.next_token_impl(false) + 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/src/parse.rs b/matrix-lang/src/parse.rs similarity index 90% rename from matrix/src/parse.rs rename to matrix-lang/src/parse.rs index d967130..3a4c5f2 100644 --- a/matrix/src/parse.rs +++ b/matrix-lang/src/parse.rs @@ -1,6 +1,4 @@ -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 crate::prelude::*; use Value as V; use ExprData as E; @@ -33,38 +31,6 @@ pub struct Parser { 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()?; @@ -99,12 +65,18 @@ macro_rules! expr_parser_reverse { }}; } +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::ExpectedToken(tok, next.pos).into()) + Err(error!("expected token '{tok}'").pos(next.pos)) } else { Ok(tok) } @@ -113,7 +85,7 @@ impl Parser { 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()) + Err(error!("expected token '{tok}'").pos(next.pos)) } else { Ok(tok) } @@ -135,7 +107,7 @@ impl Parser { match next.data { T::Comma => continue, T::RightParen => break, - _ => return Err(Error::UnexpectedToken(next).into()) + _ => return Err(error!("unexpected token '{next}'").pos(next.pos)) }; } Ok(params) @@ -157,7 +129,7 @@ impl Parser { match next.data { T::SemiColon => continue, T::RightBrack => break, - _ => return Err(Error::UnexpectedToken(next).into()) + _ => return Err(error!("unexpected token '{next}'").pos(next.pos)) }; } Ok(indicies) @@ -193,7 +165,7 @@ impl Parser { match next.data { T::SemiColon => continue, T::RightBrack => break, - _ => return Err(Error::UnexpectedToken(next).into()), + _ => return Err(error!("unexpected token '{next}'").pos(next.pos)) }; } if parts.len() == 1 { @@ -201,9 +173,9 @@ impl Parser { } else { let codomain = parts.len(); let domain = parts[0].len(); - for (i, part) in parts.iter().enumerate() { + for part in parts.iter() { if part.len() != domain { - return Err(Error::MatrixInvDomain(i, domain, part.len()).into()) + return Err(error!("matrix row domains do not match: {} != {}", domain, part.len()).pos(pos)) } } let mut data = Vec::new(); @@ -225,7 +197,7 @@ impl Parser { }, 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()) + t => return Err(error!("unexpected token '{t}'").pos(tok.pos)) }) } @@ -247,7 +219,7 @@ impl Parser { match next.data { T::Comma => continue, T::RightBrace => break, - _ => return Err(Error::UnexpectedToken(next).into()) + _ => return Err(error!("unexpected token '{next}'").pos(next.pos)) } } Ok((E::Table(table), pos).into()) @@ -273,7 +245,7 @@ impl Parser { } } T::LeftParen => (), - _ => return Err(Error::UnexpectedToken(tok).into()), + t => return Err(error!("unexpected token '{t}'").pos(tok.pos)) } let mut params = Vec::new(); @@ -295,7 +267,7 @@ impl Parser { } T::Comma => continue, T::RightParen => break, - _ => return Err(Error::UnexpectedToken(next).into()), + _ => return Err(error!("unexpected token '{next}'").pos(next.pos)) } } @@ -307,7 +279,7 @@ impl Parser { if let T::Ident(ident) = next.data { Ok((ident, next.pos)) } else { - Err(Error::UnexpectedToken(next).into()) + Err(error!("unexpected token '{next}'").pos(next.pos)) } } @@ -327,7 +299,7 @@ impl Parser { if let T::Ident(ident) = next.data { Ok((ident, next.pos)) } else { - Err(Error::UnexpectedToken(next).into()) + Err(error!("unexpected token '{next}'").pos(next.pos)) } } @@ -419,6 +391,15 @@ impl Parser { } } + 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)?; @@ -431,7 +412,10 @@ impl Parser { self.force_token(T::LeftBrace)?; loop { let expr = match self.lexer.peek_token()?.data { - T::RightBrace => break, + T::RightBrace => { + self.lexer.next_token()?; + break + }, T::SemiColon => { self.lexer.next_token()?; continue; @@ -443,12 +427,9 @@ impl Parser { match next.data { T::SemiColon => continue, T::RightBrace => break, - _ => return Err(Error::ExpectedToken(T::SemiColon, next.pos).into()) + _ => return Err(error!("expected a semicolon").pos(next.pos)) } } - if self.lexer.peek_token()?.data == T::RightBrace { - self.lexer.next_token()?; - } Ok((E::Block(block), pos).into()) } @@ -474,7 +455,7 @@ impl Parser { 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()), + t => return Err(error!("unexpected token '{t}'").pos(tok.pos)) }; Ok((data, tok.pos).into()) } @@ -488,6 +469,7 @@ impl Parser { 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(), @@ -752,11 +734,11 @@ impl Parser { }; let expr = self.parse_expr()?; block.push(expr); - let next = self.lexer.next_token()?; + let next = self.lexer.next_token_nl()?; match next.data { T::Eof => break, T::SemiColon => continue, - _ => return Err(Error::ExpectedToken(T::SemiColon, next.pos).into()) + _ => return Err(error!("expected a semicolon").pos(next.pos)) }; } Ok((E::Block(block), Position::default()).into()) 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/src/gc.rs b/matrix-lang/src/value/gc.rs similarity index 91% rename from matrix/src/gc.rs rename to matrix-lang/src/value/gc.rs index 7af020b..5ef8b80 100644 --- a/matrix/src/gc.rs +++ b/matrix-lang/src/value/gc.rs @@ -1,4 +1,5 @@ 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>, @@ -24,13 +25,6 @@ impl Gc { } 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() @@ -44,6 +38,22 @@ impl Gc { } } +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) 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/src/vm.rs b/matrix-lang/src/vm.rs similarity index 85% rename from matrix/src/vm.rs rename to matrix-lang/src/vm.rs index e511adf..bac6341 100644 --- a/matrix/src/vm.rs +++ b/matrix-lang/src/vm.rs @@ -1,33 +1,6 @@ -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}; +use std::{fmt::Display, ops::{IndexMut, Index}, sync::{atomic::{AtomicUsize, Ordering}, Arc}, collections::HashMap}; +use crate::prelude::*; -#[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 } @@ -99,12 +72,12 @@ pub struct StackFrame { } struct VmError { - err: crate::Error, + err: Exception, frames: Vec } -impl From for VmError { - fn from(err: crate::Error) -> Self { +impl From for VmError { + fn from(err: Exception) -> Self { VmError { err, frames: Vec::new() @@ -112,15 +85,6 @@ impl From for VmError { } } -impl From for VmError { - fn from(err: self::Error) -> Self { - VmError { - err: err.into(), - frames: Vec::new() - } - } -} - type VmResult = std::result::Result; impl StackFrame { @@ -166,10 +130,16 @@ pub struct Vm { trystack: Vec, globals: Rc>>, names: NamesTable, - global_names: NamesTable, + global_names: GlobalsTable, interupt: Arc, } +macro_rules! error { + ($($arg:tt)*) => { + exception!(RUNTIME_EXCEPTION, $($arg)*) + }; +} + impl Vm { fn push(&mut self, val: Value) { @@ -188,7 +158,7 @@ impl Vm { self.names.clone() } - pub fn global_names(&self) -> NamesTable { + pub fn globals(&self) -> GlobalsTable { self.global_names.clone() } @@ -210,7 +180,7 @@ impl Vm { 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.global_names.borrow_mut().push(Global { idx, name: Rc::from(name), is_const: true }); self.globals.borrow_mut().insert(idx as u16, value); } @@ -220,7 +190,7 @@ impl Vm { fn init_frame(&mut self, fun: Rc) -> Result { let InnerFunction::Compiled(ref compiled_chunk) = fun.fun else { - return Err(self::Error::ExecNative.into()) + return Err(error!("cannot create stack frame on builtin function")) }; Ok(StackFrame::new( self, @@ -240,7 +210,7 @@ impl Vm { 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::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) => { @@ -325,11 +295,12 @@ impl Vm { let fun = self.pop(); let Value::Function(fun) = fun else { - return Err(Error::NotFunction(fun).into()) + Err(error!("cannot call {fun} (not a function)"))? }; if !fun.variadic && arity > fun.arity { - return Err(Error::InvArity(fun.arity, arity as usize, fun.name.clone()).into()) + 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; @@ -416,14 +387,14 @@ impl Vm { Ok(None) } - fn stack_trace(&mut self, frames: Vec) -> String { - let mut trace = String::from("\x1b[33m\x1b[1mStack Trace:\x1b[0m\n"); + 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]; - trace.push_str(&format!(" {} at {}:{}\n", &frame.name, pos.row, pos.col)); + e = e.trace(frame.name, pos); } - trace + e } fn exec_fn(&mut self, frame: &mut StackFrame, fun: Rc, params: Vec) -> VmResult { @@ -438,7 +409,8 @@ impl Vm { self.stack.push(param); } let mut new_frame = StackFrame::new(self, name, body.clone(), params_len, frame.depth + 1); - self.exec(&mut new_frame) + let res = self.exec(&mut new_frame); + res }, InnerFunction::Native(native_fun) => { Ok(native_fun((self, frame), params)?) @@ -462,7 +434,7 @@ impl Vm { self.stack.truncate(catch.stack_len); self.locals.truncate(catch.locals_len); frame.ip = catch.err_idx; - self.stack.push(Value::Error(err.err)); + self.stack.push(Value::Exception(err.err)); } else { let mut err = err; err.frames.push(frame.clone()); @@ -478,13 +450,12 @@ impl Vm { } 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(); + let mut frame = self.init_frame(fun)?; self.exec(&mut frame).map_err(|e| { - let trace = self.stack_trace(e.frames); - (e.err, trace).into() + 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-stdlib/Cargo.toml b/matrix-std/Cargo.toml similarity index 79% rename from matrix-stdlib/Cargo.toml rename to matrix-std/Cargo.toml index 6256cfa..476272c 100644 --- a/matrix-stdlib/Cargo.toml +++ b/matrix-std/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "matrix-stdlib" +name = "matrix-std" version = "0.1.0" edition = "2021" @@ -7,7 +7,7 @@ edition = "2021" [dependencies] anyhow = "1" -matrix = { path = "../matrix" } +matrix-lang = { path = "../matrix-lang" } matrix-macros = { path = "../matrix-macros" } os_info = "3" rand = "0.8" diff --git a/matrix-stdlib/src/core.rs b/matrix-std/src/core.rs similarity index 96% rename from matrix-stdlib/src/core.rs rename to matrix-std/src/core.rs index 2c76497..69b6d97 100644 --- a/matrix-stdlib/src/core.rs +++ b/matrix-std/src/core.rs @@ -1,10 +1,8 @@ -use std::hash::{DefaultHasher, Hash, Hasher}; - -use matrix::{vm::Vm, value::Value, unpack_args, Result, unpack_varargs}; -use matrix_macros::native_func; +use std::hash::{DefaultHasher, Hasher}; use rand::Rng; -use crate::{VmArgs, next, error}; - +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(); @@ -123,11 +121,8 @@ fn remove(_: VmArgs, args: Vec) -> Result { #[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); + value.try_hash(&mut hasher)?; let fin = hasher.finish(); Ok(Value::Int(fin as u32 as i64)) } @@ -201,8 +196,10 @@ fn sort(_: VmArgs, args: Vec) -> Result { let Value::List(mut list) = value else { return error!("sort requires a list") }; - let hi = list.len() - 1; - quicksort(list.as_mut(), 0, hi); + if list.len() > 1 { + let hi = list.len() - 1; + quicksort(list.as_mut(), 0, hi); + } Ok(Value::List(list)) } diff --git a/matrix-stdlib/src/io.rs b/matrix-std/src/io.rs similarity index 97% rename from matrix-stdlib/src/io.rs rename to matrix-std/src/io.rs index d72248c..19ff074 100644 --- a/matrix-stdlib/src/io.rs +++ b/matrix-std/src/io.rs @@ -1,8 +1,7 @@ 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_lang::prelude::*; use matrix_macros::native_func; -use crate::{error, VmArgs}; +use crate::{error, VmArgs, unpack_varargs, unpack_args}; #[native_func(0..)] fn print(_: VmArgs, args: Vec) -> Result { diff --git a/matrix-stdlib/src/iter.rs b/matrix-std/src/iter.rs similarity index 94% rename from matrix-stdlib/src/iter.rs rename to matrix-std/src/iter.rs index 630e52c..638755c 100644 --- a/matrix-stdlib/src/iter.rs +++ b/matrix-std/src/iter.rs @@ -1,7 +1,7 @@ use std::{cell::RefCell, rc::Rc}; -use matrix::{iter, vm::Vm, value::Value, Result, unpack_varargs, unpack_args}; +use matrix_lang::prelude::*; use matrix_macros::native_func; -use crate::{error, next, VmArgs}; +use crate::{error, next, VmArgs, unpack_args, unpack_varargs}; use Value as V; @@ -54,7 +54,7 @@ fn range(_: VmArgs, args: Vec) -> Result { Ok(iter!(move |_,_| { let curr = *(i.borrow()); *(i.borrow_mut()) = curr + step; - if curr >= max { + if (step >= 0 && curr >= max) || (step <= 0 && curr <= max) { return Ok(V::Nil) } else { return Ok(V::Int(curr)) @@ -235,7 +235,7 @@ fn lines(_: VmArgs, args: Vec) -> Result { 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 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() { @@ -245,7 +245,7 @@ fn lines(_: VmArgs, args: Vec) -> Result { })) } -#[native_func(1)] +#[native_func(2)] fn skip((vm, frame): VmArgs, args: Vec) -> Result { let [count, iter] = unpack_args!(args); let iter = iter.into_iter_fn()?; @@ -291,25 +291,32 @@ fn alternate(_: VmArgs, args: Vec) -> Result { } #[native_func(2)] -fn intersperse(_: VmArgs, args: Vec) -> Result { +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!(vm, frame, iter)?; - if val == Value::Nil { + let val = next.borrow(); + if *val == Value::Nil { *flag.borrow_mut() = None; } else { *flag.borrow_mut() = Some(1); } - Ok(val) + 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(); - *flag.borrow_mut() = Some(0); Ok(val) }, _ => Ok(Value::Nil) @@ -331,6 +338,7 @@ fn zip(_: VmArgs, args: Vec) -> Result { 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())) }, @@ -401,7 +409,7 @@ fn take((vm, frame): VmArgs, args: Vec) -> Result { Ok(Value::List(values.into())) } -#[native_func(2)] +#[native_func(1)] fn last((vm, frame): VmArgs, args: Vec) -> Result { let [iter] = unpack_args!(args); let iter = iter.into_iter_fn()?; @@ -414,7 +422,7 @@ fn last((vm, frame): VmArgs, args: Vec) -> Result { Ok(last) } -#[native_func(2)] +#[native_func(1)] fn next((vm, frame): VmArgs, args: Vec) -> Result { let [iter] = unpack_args!(args); let iter = iter.into_iter_fn()?; 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-stdlib/src/math.rs b/matrix-std/src/math.rs similarity index 98% rename from matrix-stdlib/src/math.rs rename to matrix-std/src/math.rs index 3f33951..111544c 100644 --- a/matrix-stdlib/src/math.rs +++ b/matrix-std/src/math.rs @@ -1,9 +1,8 @@ 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_lang::prelude::*; use matrix_macros::native_func; -use crate::{error, VmArgs}; +use crate::{error, VmArgs, unpack_args}; #[native_func(1)] fn trans(_: VmArgs, args: Vec) -> Result { @@ -280,7 +279,7 @@ macro_rules! trig { fn $type(_: VmArgs, args: Vec) -> Result { use Value as V; let [value] = unpack_args!(args); - match value.promote_trig() { + 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)) @@ -295,7 +294,7 @@ macro_rules! trigf { fn $str(_: VmArgs, args: Vec) -> Result { use Value as V; let [value] = unpack_args!(args); - match value.promote_trig() { + match value.floaty() { V::Float(f) => Ok(V::Float(f.$type())), v => error!("cannot compute {} on {v}", stringify!($str)) } @@ -307,7 +306,7 @@ macro_rules! trigf { fn log(_: VmArgs, args: Vec) -> Result { use Value as V; let [base, value] = unpack_args!(args); - match (base.promote_trig(), value.promote_trig()) { + 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())), @@ -463,7 +462,7 @@ fn im(_: VmArgs, args: Vec) -> Result { #[native_func(1)] fn cis(_: VmArgs, args: Vec) -> Result { let [value] = unpack_args!(args); - match value.promote_trig() { + 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") diff --git a/matrix-stdlib/src/sys.rs b/matrix-std/src/sys.rs similarity index 98% rename from matrix-stdlib/src/sys.rs rename to matrix-std/src/sys.rs index d30226f..609e72d 100644 --- a/matrix-stdlib/src/sys.rs +++ b/matrix-std/src/sys.rs @@ -1,9 +1,9 @@ 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_lang::prelude::*; use matrix_macros::native_func; use os_info::Info; -use crate::{VmArgs, error}; +use crate::{VmArgs, error, unpack_args}; #[native_func(1)] fn sys_exit(_: VmArgs, args: Vec) -> Result { 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/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/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(()) - } -}