summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Cargo.lock385
-rw-r--r--Cargo.toml3
-rw-r--r--matrix-repl/Cargo.lock385
-rw-r--r--matrix-repl/Cargo.toml10
-rw-r--r--matrix-repl/src/main.rs18
-rw-r--r--matrix/Cargo.lock119
-rw-r--r--matrix/Cargo.toml12
-rw-r--r--matrix/src/ast.rs162
-rw-r--r--matrix/src/lex.rs595
-rw-r--r--matrix/src/lib.rs5
-rw-r--r--matrix/src/parse.rs451
12 files changed, 2146 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b60de5b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+**/target
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..f61fea6
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,385 @@
+# 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/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..cc3bc3f
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,3 @@
+[workspace]
+resolver = "2"
+members = [ "matrix", "matrix-repl" ]
diff --git a/matrix-repl/Cargo.lock b/matrix-repl/Cargo.lock
new file mode 100644
index 0000000..f61fea6
--- /dev/null
+++ b/matrix-repl/Cargo.lock
@@ -0,0 +1,385 @@
+# 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-repl/Cargo.toml b/matrix-repl/Cargo.toml
new file mode 100644
index 0000000..f1ec1d3
--- /dev/null
+++ b/matrix-repl/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "matrix-repl"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+matrix = { path = "../matrix" }
+rustyline = "13"
diff --git a/matrix-repl/src/main.rs b/matrix-repl/src/main.rs
new file mode 100644
index 0000000..9c3d5f6
--- /dev/null
+++ b/matrix-repl/src/main.rs
@@ -0,0 +1,18 @@
+use matrix::parse::Parser;
+
+fn main() {
+
+ let mut rl = rustyline::DefaultEditor::new().unwrap();
+
+ loop {
+ let Ok(line) = rl.readline(">> ") else {
+ break;
+ };
+ let ast = Parser::parse(line);
+ match ast {
+ Ok(ast) => println!("{ast:?}"),
+ Err(err) => println!("{err}")
+ }
+ }
+
+}
diff --git a/matrix/Cargo.lock b/matrix/Cargo.lock
new file mode 100644
index 0000000..ed6e714
--- /dev/null
+++ b/matrix/Cargo.lock
@@ -0,0 +1,119 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.79"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "matrix"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "num-complex",
+ "num-rational",
+ "regex",
+]
+
+[[package]]
+name = "memchr"
+version = "2.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
+
+[[package]]
+name = "num-bigint"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0"
+dependencies = [
+ "autocfg",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-complex"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "num-integer"
+version = "0.1.46"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "num-rational"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
+dependencies = [
+ "autocfg",
+ "num-bigint",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "regex"
+version = "1.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
diff --git a/matrix/Cargo.toml b/matrix/Cargo.toml
new file mode 100644
index 0000000..64e9210
--- /dev/null
+++ b/matrix/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "matrix"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+anyhow = "1"
+num-complex = "0.4"
+num-rational = "0.4"
+regex = "1"
diff --git a/matrix/src/ast.rs b/matrix/src/ast.rs
new file mode 100644
index 0000000..b4d2c24
--- /dev/null
+++ b/matrix/src/ast.rs
@@ -0,0 +1,162 @@
+use std::{rc::Rc, collections::HashMap};
+
+use regex::Regex;
+use num_rational::Rational64;
+use num_complex::Complex64;
+
+use crate::lex::Token;
+
+pub type List = Vec<Value>;
+pub type Matrix = (u16, u16, Vec<Value>);
+pub type Table = HashMap<Rc<str>, Value>;
+
+pub type InlineList = Vec<Expr>;
+pub type InlineMatrix = (u16, u16, Vec<Expr>);
+pub type InlineTable = Vec<(Expr, Expr)>;
+
+pub type RcList = Rc<List>;
+pub type RcMatrix = Rc<Matrix>;
+pub type RcString = Rc<str>;
+pub type RcTable = Rc<Table>;
+pub type RcRegex = Rc<Regex>;
+
+#[derive(Debug)]
+pub enum Value {
+ Nil,
+ Bool(bool),
+ Int(i64),
+ Float(f64),
+ Ratio(Rational64),
+ Complex(Complex64),
+ Regex(RcRegex),
+ String(RcString),
+ List(RcList),
+ Matrix(RcMatrix),
+ Table(RcTable),
+}
+
+#[derive(Debug)]
+pub enum UnaryOp {
+ // normal math
+ Negate,
+ // equality
+ Not,
+}
+
+impl TryFrom<Token> for UnaryOp {
+ type Error = ();
+
+ fn try_from(value: Token) -> Result<Self, Self::Error> {
+ use Token::*;
+ Ok(match value {
+ Subtract => Self::Negate,
+ Not => Self::Not,
+ _ => return Err(())
+ })
+ }
+}
+
+#[derive(Debug)]
+pub enum BinaryOp {
+ // normal math
+ Add,
+ Subtract,
+ Multiply,
+ Divide,
+ Modulo,
+ Power,
+ // binary math
+ BitwiseAnd,
+ BitwiseOr,
+ BitwiseXor,
+ BitwiseShiftLeft,
+ BitwiseShiftRight,
+ // equality
+ Equals,
+ NotEquals,
+ GreaterEquals,
+ LessEquals,
+ GreaterThan,
+ LessThan,
+ And,
+ Or,
+ // assignment
+ Assign
+}
+
+impl TryFrom<Token> for BinaryOp {
+ type Error = ();
+
+ fn try_from(value: Token) -> Result<Self, Self::Error> {
+ use Token::*;
+ Ok(match value {
+ Equal => BinaryOp::Equals,
+ NotEqual => Self::Equals,
+ GreaterEqual => Self::GreaterEquals,
+ LessEqual => Self::LessEquals,
+ GreaterThan => Self::GreaterThan,
+ LessThan => Self::LessThan,
+ And => Self::And,
+ Or => Self::Or,
+ BitwiseShiftLeft => Self::BitwiseShiftLeft,
+ BitwiseShiftRight => Self::BitwiseShiftRight,
+ BitwiseAnd => Self::BitwiseAnd,
+ BitwiseOr => Self::BitwiseOr,
+ BitwiseXor => Self::BitwiseXor,
+ Add => Self::Add,
+ Subtract => Self::Subtract,
+ Multiply => Self::Multiply,
+ Divide => Self::Divide,
+ Modulo => Self::Modulo,
+ Power => Self::Power,
+ Assign => Self::Assign,
+ _ => return Err(())
+ })
+ }
+}
+
+#[derive(Debug)]
+pub enum Expr {
+ Literal(Value),
+ Ident(Rc<str>),
+
+ UnaryOp(Box<Expr>, UnaryOp),
+ BinaryOp(Box<Expr>, Box<Expr>, BinaryOp),
+
+ Let(Rc<str>, Box<Expr>),
+
+ Index(Box<Expr>, Vec<Expr>),
+ FnCall(Box<Expr>, Vec<Expr>),
+
+ List(InlineList),
+ Matrix(InlineMatrix),
+ Table(InlineTable),
+
+ Block(Vec<Expr>),
+ Function(Rc<str>, Vec<Rc<str>>, Vec<Expr>),
+
+ Continue,
+ Break,
+ Return(Box<Expr>),
+
+ If(Box<Expr>, Box<Expr>, Option<Box<Expr>>),
+ Loop(Vec<Expr>),
+ While(Box<Expr>, Vec<Expr>),
+ DoWhile(Box<Expr>, Vec<Expr>)
+}
+
+impl Expr {
+ pub fn optimize(self) -> Self {
+ use Expr::*;
+ match self {
+ Block(mut block) => {
+ if block.len() == 1 {
+ block.pop().unwrap().optimize()
+ } else {
+ Block(block.into_iter().map(|e| e.optimize()).collect())
+ }
+ }
+ _ => self
+ }
+ }
+}
diff --git a/matrix/src/lex.rs b/matrix/src/lex.rs
new file mode 100644
index 0000000..e49ccba
--- /dev/null
+++ b/matrix/src/lex.rs
@@ -0,0 +1,595 @@
+use std::{rc::Rc, fmt::Debug};
+use regex::Regex;
+
+pub struct RegexToken {
+ regex: Regex
+}
+
+impl Debug for RegexToken {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{:?}", self.regex)
+ }
+}
+
+impl PartialEq for RegexToken {
+ fn eq(&self, other: &Self) -> bool {
+ self.regex.as_str().eq(other.regex.as_str())
+ }
+}
+
+impl From<Regex> for RegexToken {
+ fn from(regex: Regex) -> Self {
+ Self { regex }
+ }
+}
+
+impl From<RegexToken> for Regex {
+ fn from(value: RegexToken) -> Self {
+ value.regex
+ }
+}
+
+#[derive(Debug, PartialEq)]
+pub enum Token {
+ //syntax
+ LeftParen,
+ RightParen,
+ LeftBrack,
+ RightBrack,
+ LeftBrace,
+ RightBrace,
+ Assign,
+ Access,
+ SemiColon,
+ Arrow,
+ ThinArrow,
+ Comma,
+
+ // equality
+ Equal,
+ NotEqual,
+ GreaterEqual,
+ LessEqual,
+ GreaterThan,
+ LessThan,
+
+ And,
+ Or,
+ Not,
+
+ BitwiseShiftLeft,
+ BitwiseShiftRight,
+ BitwiseAnd,
+ BitwiseOr,
+ BitwiseXor,
+
+ // math
+ Regex(RegexToken),
+ Int(i64),
+ Float(f64),
+ Complex(f64),
+ String(Rc<str>),
+ Ident(Rc<str>),
+
+ Add,
+ Subtract,
+ Multiply,
+ Divide,
+ Modulo,
+ Power,
+
+ // keywords
+ If,
+ Else,
+ While,
+ Let,
+ Function,
+ True,
+ False,
+ Nil,
+ Continue,
+ Break,
+ Do,
+ Loop,
+ Return,
+
+ // eof
+ Eof,
+}
+
+impl Token {
+
+}
+
+impl Token {
+ /// Returns `true` if the token is [`Regex`].
+ ///
+ /// [`Regex`]: Token::Regex
+ #[must_use]
+ pub fn is_regex(&self) -> bool {
+ matches!(self, Self::Regex(..))
+ }
+}
+
+#[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 {}
+
+pub type Result<T> = std::result::Result<T, self::Error>;
+
+pub struct Lexer {
+ pub index: usize,
+ len: usize,
+ data: Vec<char>,
+}
+
+trait IsIdent {
+ fn is_initial_ident(&self) -> bool;
+ fn is_ident(&self) -> bool;
+}
+
+impl IsIdent for char {
+ fn is_initial_ident(&self) -> bool {
+ return self.is_alphabetic() || *self == '_';
+ }
+ fn is_ident(&self) -> bool {
+ return self.is_alphanumeric() || *self == '_';
+ }
+}
+
+impl<T: Into<String>> From<T> for Lexer {
+ fn from(value: T) -> Self {
+ Self::new(value)
+ }
+}
+
+impl Lexer {
+ pub fn new<T: Into<String>>(input: T) -> Self {
+ let data: Vec<char> = input.into().chars().collect();
+ Self { index: 0, len: data.len(), data }
+ }
+
+ fn peek(&self) -> char {
+ if self.index >= self.len {
+ return '\0';
+ }
+ return self.data[self.index];
+ }
+
+ fn next(&mut self) -> char {
+ let c = self.peek();
+ self.index += 1;
+ return c;
+ }
+
+ fn next_not_eof(&mut self) -> Result<char> {
+ let c = self.next();
+ if c == '\0' {
+ return Err(Error::UnexpectedEof)
+ } else {
+ return Ok(c)
+ }
+ }
+
+ fn next_expect(&mut self, expected: char) -> Result<char> {
+ let c = self.next();
+ if c != expected {
+ return Err(Error::ExpectedChar(expected, c))
+ } else {
+ return Ok(c)
+ }
+ }
+
+ fn skip_whitespace(&mut self, ignore_newlines: bool) {
+ while self.peek().is_whitespace() && (!ignore_newlines || self.peek() != '\n') {
+ self.next();
+ }
+ }
+
+ fn lex_string(&mut self, delimit: char) -> Result<Rc<str>> {
+ use Error::*;
+
+ let mut buf = String::new();
+
+ loop {
+ let c = self.next_not_eof()?;
+
+ if c == delimit {
+ break;
+ }
+
+ if c != '\\' {
+ buf.push(c);
+ continue;
+ }
+
+ let next = self.next_not_eof()?;
+ match next {
+ '\'' | '\"' | '\\' => buf.push(c),
+ '0' => buf.push('\0'),
+ 'a' => buf.push('\x07'),
+ 'b' => buf.push('\x08'),
+ 't' => buf.push('\t'),
+ 'n' => buf.push('\n'),
+ 'v' => buf.push('\x0b'),
+ 'f' => buf.push('\x0c'),
+ 'r' => buf.push('\r'),
+ 'e' => buf.push('\x1b'),
+ 'x' => {
+ let n1 = self.next_not_eof()?;
+ let n2 = self.next_not_eof()?;
+ buf.push(char::from_u32(
+ n1.to_digit(16).ok_or(InvalidDigit(n1))? * 16 +
+ n2.to_digit(16).ok_or(InvalidDigit(n2))?
+ ).unwrap())
+ },
+ 'u' => {
+ self.next_expect('{')?;
+ let mut n = 0u32;
+ loop {
+ let c = self.next_not_eof()?;
+ if c == '}' { break }
+ if n >= 0x10000000u32 {
+ return Err(InvalidCodepoint)
+ }
+ n = n * 16 + c.to_digit(16).ok_or(InvalidDigit(c))?;
+ }
+ let ch = char::from_u32(n).ok_or(InvalidCodepoint)?;
+ buf.push(ch);
+
+ },
+ _ => return Err(InvalidStringEscape(next))
+ }
+ }
+
+ Ok(buf.into())
+ }
+
+ fn lex_ident(&mut self, initial: char) -> Result<Token> {
+ use Error::*;
+ use Token::*;
+
+ let mut buf = std::string::String::new();
+
+ if !initial.is_initial_ident() {
+ return Err(UnexpectedCharacter(initial))
+ }
+
+ buf.push(initial);
+
+ loop {
+ if self.peek().is_ident() {
+ buf.push(self.next());
+ } else {
+ break;
+ }
+ }
+
+ Ok(match buf.as_str() {
+ "if" => If,
+ "else" => Else,
+ "while" => While,
+ "let" => Let,
+ "function" => Function,
+ "true" => True,
+ "false" => False,
+ "nil" => Nil,
+ "continue" => Continue,
+ "break" => Break,
+ "do" => Do,
+ "loop" => Loop,
+ "and" => And,
+ "or" => Or,
+ "not" => Not,
+ "return" => Return,
+ _ => Ident(buf.into())
+ })
+ }
+
+ fn lex_radix(&mut self, radix: i64, radix_char: char) -> Result<Token> {
+ use Token::*;
+ use Error::*;
+
+ let mut n = 0i64;
+ let mut char_found = false;
+
+ loop {
+ if let Some(i) = self.peek().to_digit(radix as u32) {
+ self.next();
+ n = n * radix + (i as i64);
+ char_found = true;
+ } else if self.peek().is_ident() {
+ return Err(InvalidDigit(self.peek()))
+ } else {
+ break;
+ }
+ }
+
+ if char_found {
+ return Ok(Int(n))
+ } else {
+ return Err(InvalidNumber(format!("0{radix_char}")))
+ }
+ }
+
+ fn lex_number(&mut self, initial: char) -> Result<Token> {
+ use Error::*;
+
+ if initial == '0' {
+ match self.peek() {
+ 'x' => {
+ self.next();
+ return self.lex_radix(16, 'x')
+ }
+ 'o' => {
+ self.next();
+ return self.lex_radix(8, 'o');
+ }
+ _ => ()
+ }
+ }
+
+ let mut buf = String::new();
+ buf.push(initial);
+
+ if initial != '.' {
+ loop {
+ if !self.peek().is_digit(10) { break; }
+ buf.push(self.next());
+ }
+
+ if self.peek() == '.' {
+ buf.push(self.next());
+ }
+ }
+
+ loop {
+ if !self.peek().is_digit(10) { break; }
+ buf.push(self.next());
+ }
+
+ if self.peek() == 'e' || self.peek() == 'E' {
+ buf.push(self.next());
+ if self.peek() == '+' || self.peek() == '-' {
+ buf.push(self.next());
+ }
+
+ loop {
+ if !self.peek().is_digit(10) { break; }
+ buf.push(self.next());
+ }
+ }
+
+ let complex = self.peek() == 'i';
+ if complex {
+ self.next();
+ }
+
+ if self.peek().is_ident() || self.peek() == '.' {
+ return Err(UnexpectedCharacter(self.peek()))
+ }
+
+ if let Ok(int) = buf.parse::<i64>() {
+ use Token::*;
+ if self.peek() == 'i' {
+ self.next();
+ return Ok(Complex(int as f64))
+ }
+ return Ok(Int(int))
+ }
+
+ if let Ok(float) = buf.parse::<f64>() {
+ use Token::*;
+ if self.peek() == 'i' {
+ self.next();
+ return Ok(Complex(float))
+ }
+ return Ok(Float(float))
+ }
+
+ Err(Error::InvalidNumber(buf))
+ }
+
+ pub fn reset(&mut self) {
+ self.index = 0;
+ }
+
+ fn peek_token_impl(&mut self, ignore_newlines: bool) -> Result<Token> {
+ let idx = self.index;
+ let token = self.next_token_impl(ignore_newlines);
+ self.index = idx;
+ token
+ }
+
+ pub fn peek_multiple_tokens(&mut self, count: usize) -> Result<Vec<Token>> {
+ let mut tokens = Vec::new();
+ let idx = self.index;
+ for _ in 0..count {
+ tokens.push(self.next_token_impl(false)?);
+ }
+ self.index = idx;
+ Ok(tokens)
+ }
+
+ fn next_token_impl(&mut self, ignore_newlines: bool) -> Result<Token> {
+ use Token::*;
+ use Error::*;
+
+ self.skip_whitespace(ignore_newlines);
+ let char = self.next();
+ let next = self.peek();
+
+ if char == '\0' {
+ return Ok(if ignore_newlines { Eof } else { SemiColon });
+ }
+
+ Ok(match char {
+ '(' => LeftParen,
+ ')' => RightParen,
+ '[' => LeftBrack,
+ ']' => RightBrack,
+ '{' => LeftBrace,
+ '}' => RightBrace,
+ ';' => SemiColon,
+ '+' => Add,
+ ',' => Comma,
+ '*' => {
+ match next {
+ '*' => {
+ self.next();
+ Power
+ }
+ _ => Multiply
+ }
+ },
+ '/' => Divide,
+ '%' => Modulo,
+ '!' => {
+ match next {
+ '=' => {
+ self.next();
+ NotEqual
+ }
+ _ => Not
+ }
+ }
+ '&' => {
+ match next {
+ '&' => {
+ self.next();
+ And
+ }
+ _ => BitwiseAnd
+ }
+ },
+ '|' => {
+ match next {
+ '|' => {
+ self.next();
+ Or
+ }
+ _ => BitwiseOr
+ }
+ },
+ '-' => {
+ match next {
+ '>' => {
+ self.next();
+ ThinArrow
+ }
+ _ => Subtract
+ }
+ },
+ '=' => {
+ match next {
+ '>' => {
+ self.next();
+ Arrow
+ }
+ '=' => {
+ self.next();
+ Equal
+ }
+ _ => Assign
+ }
+ },
+ '>' => {
+ match next {
+ '>' => {
+ self.next();
+ BitwiseShiftRight
+ }
+ '=' => {
+ self.next();
+ GreaterEqual
+ }
+ _ => GreaterThan
+ }
+ },
+ '<' => {
+ match next {
+ '<' => {
+ self.next();
+ BitwiseShiftLeft
+ }
+ '=' => {
+ self.next();
+ LessEqual
+ }
+ _ => LessThan
+ }
+ },
+ '^' => BitwiseXor,
+ '\'' | '\"' => String(self.lex_string(char)?),
+ 'r' => {
+ match next {
+ '\'' | '\"' => {
+ self.next();
+ Regex(regex::Regex::new(&self.lex_string(next)?)
+ .map(|e| e.into())
+ .map_err(|e| InvalidRegex(e.into()))?)
+ }
+ _ => {
+ self.lex_ident(char)?
+ }
+ }
+ },
+ '.' => {
+ if next.is_digit(10) {
+ self.lex_number(char)?
+ } else {
+ Access
+ }
+ },
+ _ => {
+ if char.is_digit(10) {
+ self.lex_number(char)?
+ } else {
+ self.lex_ident(char)?
+ }
+ },
+ })
+ }
+
+ pub fn peek_token(&mut self) -> Result<Token> {
+ self.peek_token_impl(true)
+ }
+
+ pub fn next_token(&mut self) -> Result<Token> {
+ self.next_token_impl(true)
+ }
+
+ pub fn peek_token_nl(&mut self) -> Result<Token> {
+ self.peek_token_impl(false)
+ }
+
+ pub fn next_token_nl(&mut self) -> Result<Token> {
+ self.next_token_impl(false)
+ }
+}
diff --git a/matrix/src/lib.rs b/matrix/src/lib.rs
new file mode 100644
index 0000000..d93b89b
--- /dev/null
+++ b/matrix/src/lib.rs
@@ -0,0 +1,5 @@
+
+mod ast;
+mod lex;
+
+pub mod parse;
diff --git a/matrix/src/parse.rs b/matrix/src/parse.rs
new file mode 100644
index 0000000..57ee2bf
--- /dev/null
+++ b/matrix/src/parse.rs
@@ -0,0 +1,451 @@
+use std::{fmt::Display, rc::Rc};
+use num_complex::Complex64;
+
+use crate::{lex::{Lexer, self, Token}, ast::{Expr, Value, BinaryOp, UnaryOp}};
+
+pub struct Parser {
+ lexer: Lexer
+}
+
+#[derive(Debug)]
+pub enum Error {
+ LexerError(crate::lex::Error),
+ UnexpectedToken(Token),
+ ExpectedToken(Token),
+ ExpectedTokenName(&'static str),
+}
+
+pub type Result<T> = std::result::Result<T, 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: '{tok:?}'"),
+ ExpectedToken(tok) => write!(f, "Expected token: '{tok:?}'"),
+ ExpectedTokenName(name) => write!(f, "Expected {name} token"),
+ }
+ }
+}
+
+impl From<lex::Error> for Error {
+ fn from(value: lex::Error) -> Self {
+ Self::LexerError(value)
+ }
+}
+
+impl std::error::Error for Error {}
+
+macro_rules! math_expr_parser {
+ ($parser:ident, $pattern:pat, $fn:ident) => {{
+ let mut math_expr = $parser.$fn()?;
+ loop {
+ let tok = $parser.lexer.peek_token_nl()?;
+ match tok {
+ $pattern => {
+ $parser.lexer.next_token_nl()?;
+ let temp = $parser.$fn()?;
+ math_expr = Expr::BinaryOp(Box::new(math_expr), Box::new(temp), BinaryOp::try_from(tok).unwrap())
+ }
+ _ => break
+ }
+ }
+ Ok(math_expr)
+ }};
+}
+
+macro_rules! math_expr_parser_reverse {
+ ($parser:ident, $pattern:pat, $fn:ident, $cur:ident) => {{
+ let math_expr = $parser.$fn()?;
+ let tok = $parser.lexer.peek_token_nl()?;
+ Ok(match tok {
+ $pattern => {
+ $parser.lexer.next_token_nl()?;
+ Expr::BinaryOp(Box::new(math_expr), Box::new($parser.$cur()?), BinaryOp::try_from(tok).unwrap())
+ }
+ _ => math_expr
+ })
+ }};
+}
+
+impl Parser {
+
+ fn force_token(&mut self, tok: Token) -> Result<Token> {
+ let next = self.lexer.next_token()?;
+ if next != tok {
+ Err(Error::ExpectedToken(tok))
+ } else {
+ Ok(tok)
+ }
+ }
+
+ fn force_token_nl(&mut self, tok: Token) -> Result<Token> {
+ let next = self.lexer.next_token_nl()?;
+ if next != tok {
+ Err(Error::ExpectedToken(tok))
+ } else {
+ Ok(tok)
+ }
+ }
+
+ fn parse_list(&mut self) -> Result<Expr> {
+ self.force_token(Token::LeftBrack)?;
+ let mut list = Vec::new();
+ if self.lexer.peek_token()? == Token::RightBrack {
+ self.lexer.next_token()?;
+ return Ok(Expr::List(list))
+ }
+ loop {
+ let expr = self.parse_math_expr()?;
+ list.push(expr);
+ let next = self.lexer.next_token()?;
+ match next {
+ Token::Comma => continue,
+ Token::RightBrack => break,
+ _ => return Err(Error::UnexpectedToken(next))
+ }
+ }
+ Ok(Expr::List(list))
+ }
+
+ fn parse_table_key(&mut self) -> Result<Expr> {
+ let tok = self.lexer.next_token()?;
+ Ok(match tok {
+ Token::LeftBrack => {
+ let expr = self.parse_math_expr()?;
+ self.force_token(Token::RightBrack)?;
+ expr
+ },
+ Token::Ident(ident) => Expr::Ident(ident),
+ Token::String(string) => Expr::Literal(Value::String(string)),
+ _ => return Err(Error::UnexpectedToken(tok))
+ })
+ }
+
+ fn parse_table(&mut self) -> Result<Expr> {
+ self.force_token(Token::LeftBrace)?;
+ let mut table = Vec::new();
+ if self.lexer.peek_token()? == Token::RightBrace {
+ self.lexer.next_token()?;
+ return Ok(Expr::Table(table))
+ }
+ loop {
+ let key = self.parse_table_key()?;
+ self.force_token(Token::Assign)?;
+ let value = self.parse_math_expr()?;
+ table.push((key, value));
+ let next = self.lexer.next_token()?;
+ match next {
+ Token::Comma => continue,
+ Token::RightBrace => break,
+ _ => return Err(Error::UnexpectedToken(next))
+ }
+ }
+ Ok(Expr::Table(table))
+ }
+
+ fn parse_paren(&mut self) -> Result<Expr> {
+ self.force_token(Token::LeftParen)?;
+ let math_expr = self.parse_math_expr()?;
+ self.force_token(Token::RightParen)?;
+ Ok(math_expr)
+ }
+
+ fn parse_term(&mut self) -> Result<Expr> {
+ use Token::*;
+ let tok = self.lexer.peek_token()?;
+ match tok {
+ LeftBrack => return self.parse_list(),
+ LeftBrace => return self.parse_table(),
+ LeftParen => return self.parse_paren(),
+ _ => ()
+ }
+ self.lexer.next_token()?;
+ Ok(match tok {
+ Int(i) => Expr::Literal(Value::Int(i)),
+ Float(f) => Expr::Literal(Value::Float(f)),
+ Complex(c) => Expr::Literal(Value::Complex(Complex64::new(0.0, c))),
+ Regex(r) => Expr::Literal(Value::Regex(Rc::new(r.into()))),
+ String(s) => Expr::Literal(Value::String(s)),
+ True => Expr::Literal(Value::Bool(true)),
+ False => Expr::Literal(Value::Bool(false)),
+ Ident(ident) => Expr::Ident(ident),
+ _ => return Err(Error::UnexpectedToken(tok)),
+ })
+ }
+
+ fn parse_math_expr_unary(&mut self) -> Result<Expr> {
+ let tok = self.lexer.peek_token_nl()?;
+ Ok(match tok {
+ Token::Not => {
+ self.lexer.next_token()?;
+ Expr::UnaryOp(Box::new(self.parse_math_expr_unary()?), UnaryOp::Not)
+ }
+ Token::Subtract => {
+ self.lexer.next_token()?;
+ Expr::UnaryOp(Box::new(self.parse_math_expr_unary()?), UnaryOp::Negate)
+ }
+ _ => self.parse_term()?
+ })
+ }
+
+ fn parse_math_expr_pow(&mut self) -> Result<Expr> {
+ math_expr_parser_reverse!(
+ self,
+ Token::Power,
+ parse_math_expr_unary,
+ parse_math_expr_pow
+ )
+ }
+
+ fn parse_math_expr_mult(&mut self) -> Result<Expr> {
+ math_expr_parser!(self, Token::Multiply | Token::Divide | Token::Modulo, parse_math_expr_pow)
+ }
+
+ fn parse_math_expr_add(&mut self) -> Result<Expr> {
+ math_expr_parser!(self, Token::Add | Token::Subtract, parse_math_expr_mult)
+ }
+
+ fn parse_math_expr_shift(&mut self) -> Result<Expr> {
+ math_expr_parser!(
+ self,
+ Token::BitwiseShiftLeft | Token::BitwiseShiftRight,
+ parse_math_expr_add
+ )
+ }
+
+ fn parse_math_expr_bit_and(&mut self) -> Result<Expr> {
+ math_expr_parser!(self, Token::BitwiseAnd, parse_math_expr_shift)
+ }
+
+ fn parse_math_expr_bit_or(&mut self) -> Result<Expr> {
+ math_expr_parser!(self, Token::BitwiseOr, parse_math_expr_bit_and)
+ }
+
+ fn parse_math_expr_compare(&mut self) -> Result<Expr> {
+ math_expr_parser!(
+ self,
+ Token::Equal | Token::NotEqual |
+ Token::LessThan | Token::GreaterThan |
+ Token::LessEqual | Token::GreaterEqual,
+ parse_math_expr_bit_or
+ )
+ }
+
+ fn parse_math_expr_and(&mut self) -> Result<Expr> {
+ math_expr_parser!(self, Token::Add, parse_math_expr_compare)
+ }
+
+ fn parse_math_expr_or(&mut self) -> Result<Expr> {
+ math_expr_parser!(self, Token::Or, parse_math_expr_and)
+ }
+
+ fn parse_math_expr(&mut self) -> Result<Expr> {
+ math_expr_parser_reverse!(
+ self,
+ Token::Assign,
+ parse_math_expr_or,
+ parse_math_expr
+ )
+ }
+
+ fn parse_params(&mut self) -> Result<Vec<Rc<str>>> {
+ use Token::*;
+ let tok = self.lexer.next_token()?;
+ match tok {
+ Ident(ident) => return Ok(vec![ident]),
+ LeftParen => (),
+ _ => return Err(Error::UnexpectedToken(tok)),
+ }
+
+ let mut params = Vec::new();
+
+ if self.lexer.peek_token()? == Token::RightParen {
+ self.lexer.next_token()?;
+ return Ok(params);
+ }
+
+ loop {
+ let ident = self.parse_ident()?;
+ params.push(ident);
+ let next = self.lexer.next_token()?;
+ match next {
+ Comma => continue,
+ RightParen => break,
+ _ => return Err(Error::UnexpectedToken(next)),
+ }
+ }
+
+ Ok(params)
+ }
+
+ fn parse_ident(&mut self) -> Result<Rc<str>> {
+ if let Token::Ident(ident) = self.lexer.next_token()? {
+ Ok(ident)
+ } else {
+ Err(Error::ExpectedTokenName("Ident"))
+ }
+ }
+
+ fn parse_ident_nl(&mut self) -> Result<Rc<str>> {
+ if let Token::Ident(ident) = self.lexer.next_token_nl()? {
+ Ok(ident)
+ } else {
+ Err(Error::ExpectedTokenName("Ident"))
+ }
+ }
+
+ fn parse_function(&mut self) -> Result<Expr> {
+ self.force_token(Token::Function)?;
+ let ident = self.parse_ident()?;
+ let params = match self.lexer.peek_token()? {
+ Token::LeftBrace => vec![],
+ _ => self.parse_params()?,
+ };
+ let block = self.parse_block()?;
+ Ok(Expr::Function(ident, params, block))
+ }
+
+ fn parse_do_while(&mut self) -> Result<Expr> {
+ self.force_token(Token::Do)?;
+ let block = self.parse_block()?;
+ self.force_token(Token::While)?;
+ let math_expr = self.parse_math_expr()?;
+ Ok(Expr::DoWhile(Box::new(math_expr), block))
+ }
+
+ fn parse_while(&mut self) -> Result<Expr> {
+ self.force_token(Token::While)?;
+ let math_expr = self.parse_math_expr()?;
+ let block = self.parse_block()?;
+ Ok(Expr::While(Box::new(math_expr), block))
+ }
+
+ fn parse_loop(&mut self) -> Result<Expr> {
+ self.force_token(Token::Loop)?;
+ let block = self.parse_block()?;
+ Ok(Expr::Loop(block))
+ }
+
+ fn parse_if(&mut self) -> Result<Expr> {
+ self.force_token(Token::If)?;
+ let math_expr = Box::new(self.parse_expr()?);
+ let expr = Box::new(self.parse_expr()?);
+
+ if self.lexer.peek_token()? != Token::Else {
+ return Ok(Expr::If(math_expr, expr, None))
+ }
+ self.lexer.next_token()?;
+
+ if self.lexer.peek_token()? == Token::If {
+ Ok(Expr::If(math_expr, expr, Some(Box::new(self.parse_if()?))))
+ } else {
+ Ok(Expr::If(math_expr, expr, Some(Box::new(self.parse_expr()?))))
+ }
+ }
+
+ fn parse_let(&mut self) -> Result<Expr> {
+ self.force_token(Token::Let)?;
+ let ident = self.parse_ident_nl()?;
+ if self.lexer.peek_token_nl()? == Token::Assign {
+ self.force_token_nl(Token::Assign)?;
+ Ok(Expr::Let(ident, Box::new(self.parse_math_expr()?)))
+ } else {
+ Ok(Expr::Let(ident, Box::new(Expr::Literal(Value::Nil))))
+ }
+ }
+
+ fn parse_return(&mut self) -> Result<Expr> {
+ self.force_token(Token::Return)?;
+ Ok(Expr::Return(Box::new(self.parse_math_expr()?)))
+ }
+
+ fn parse_expr(&mut self) -> Result<Expr> {
+ use Token::*;
+ match self.lexer.peek_token()? {
+ Do => self.parse_do_while(),
+ While => self.parse_while(),
+ Let => self.parse_let(),
+ LeftBrace => {
+ let idx = self.lexer.index;
+ if let Ok(table) = self.parse_math_expr() {
+ Ok(table)
+ } else {
+ self.lexer.index = idx;
+ Ok(Expr::Block(self.parse_block()?))
+ }
+ },
+ Return => self.parse_return(),
+ If => self.parse_if(),
+ Loop => self.parse_loop(),
+ Break => {
+ self.lexer.next_token()?;
+ Ok(Expr::Break)
+ },
+ Continue => {
+ self.lexer.next_token()?;
+ Ok(Expr::Continue)
+ },
+ _ => {
+ let math_expr = self.parse_math_expr()?;
+ Ok(math_expr)
+ }
+ }
+ }
+
+ fn parse_block(&mut self) -> Result<Vec<Expr>> {
+ let mut block = Vec::new();
+ self.force_token(Token::LeftBrace)?;
+ loop {
+ let expr = match self.lexer.peek_token()? {
+ Token::RightBrace => break,
+ Token::SemiColon => {
+ self.lexer.next_token()?;
+ continue;
+ }
+ _ => self.parse_expr()?
+ };
+ block.push(expr);
+ let next = self.lexer.next_token()?;
+ match next {
+ Token::SemiColon => continue,
+ Token::RightBrace => break,
+ _ => return Err(Error::UnexpectedToken(next))
+ }
+ }
+ self.force_token(Token::RightBrace)?;
+ Ok(block)
+ }
+
+ fn parse_root_expr(&mut self) -> Result<Expr> {
+ if self.lexer.peek_token()? == Token::Function {
+ self.parse_function()
+ } else {
+ self.parse_expr()
+ }
+ }
+
+ pub fn parse<T: Into<Lexer>>(into: T) -> Result<Expr> {
+ let mut parser = Self { lexer: into.into() };
+ let mut block = Vec::new();
+ loop {
+ let expr = match parser.lexer.peek_token()? {
+ Token::Eof => break,
+ Token::SemiColon => {
+ parser.lexer.next_token()?;
+ continue;
+ }
+ _ => parser.parse_root_expr()?
+ };
+ block.push(expr);
+ let next = parser.lexer.next_token()?;
+ match next {
+ Token::SemiColon => continue,
+ Token::Eof => break,
+ _ => return Err(Error::UnexpectedToken(next))
+ }
+ }
+ Ok(Expr::Block(block))
+ }
+}