commit cdc6147a35206b290a2ea2d7d400b0899b5e0e43 Author: Freya Murphy Date: Thu Feb 15 23:07:30 2024 -0500 new lang => lexer and parser done 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; +pub type Matrix = (u16, u16, Vec); +pub type Table = HashMap, Value>; + +pub type InlineList = Vec; +pub type InlineMatrix = (u16, u16, Vec); +pub type InlineTable = Vec<(Expr, Expr)>; + +pub type RcList = Rc; +pub type RcMatrix = Rc; +pub type RcString = Rc; +pub type RcTable = Rc; +pub type RcRegex = Rc; + +#[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 for UnaryOp { + type Error = (); + + fn try_from(value: Token) -> Result { + 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 for BinaryOp { + type Error = (); + + fn try_from(value: Token) -> Result { + 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), + + UnaryOp(Box, UnaryOp), + BinaryOp(Box, Box, BinaryOp), + + Let(Rc, Box), + + Index(Box, Vec), + FnCall(Box, Vec), + + List(InlineList), + Matrix(InlineMatrix), + Table(InlineTable), + + Block(Vec), + Function(Rc, Vec>, Vec), + + Continue, + Break, + Return(Box), + + If(Box, Box, Option>), + Loop(Vec), + While(Box, Vec), + DoWhile(Box, Vec) +} + +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 for RegexToken { + fn from(regex: Regex) -> Self { + Self { regex } + } +} + +impl From for Regex { + fn from(value: RegexToken) -> Self { + value.regex + } +} + +#[derive(Debug, PartialEq)] +pub enum 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), + Ident(Rc), + + 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 = std::result::Result; + +pub struct Lexer { + pub index: usize, + len: usize, + data: Vec, +} + +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> From for Lexer { + fn from(value: T) -> Self { + Self::new(value) + } +} + +impl Lexer { + pub fn new>(input: T) -> Self { + let data: Vec = input.into().chars().collect(); + Self { index: 0, len: data.len(), data } + } + + 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 { + let c = self.next(); + if c == '\0' { + return Err(Error::UnexpectedEof) + } else { + return Ok(c) + } + } + + fn next_expect(&mut self, expected: char) -> Result { + 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> { + 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 { + 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 { + 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 { + 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::() { + use Token::*; + if self.peek() == 'i' { + self.next(); + return Ok(Complex(int as f64)) + } + return Ok(Int(int)) + } + + if let Ok(float) = buf.parse::() { + 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 { + 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> { + 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 { + 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 { + self.peek_token_impl(true) + } + + pub fn next_token(&mut self) -> Result { + self.next_token_impl(true) + } + + pub fn peek_token_nl(&mut self) -> Result { + self.peek_token_impl(false) + } + + pub fn next_token_nl(&mut self) -> Result { + self.next_token_impl(false) + } +} diff --git a/matrix/src/lib.rs b/matrix/src/lib.rs 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 = std::result::Result; + +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 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 { + 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 { + let next = self.lexer.next_token_nl()?; + if next != tok { + Err(Error::ExpectedToken(tok)) + } else { + Ok(tok) + } + } + + fn parse_list(&mut self) -> Result { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + math_expr_parser_reverse!( + self, + Token::Power, + parse_math_expr_unary, + parse_math_expr_pow + ) + } + + fn parse_math_expr_mult(&mut self) -> Result { + math_expr_parser!(self, Token::Multiply | Token::Divide | Token::Modulo, parse_math_expr_pow) + } + + fn parse_math_expr_add(&mut self) -> Result { + math_expr_parser!(self, Token::Add | Token::Subtract, parse_math_expr_mult) + } + + fn parse_math_expr_shift(&mut self) -> Result { + math_expr_parser!( + self, + Token::BitwiseShiftLeft | Token::BitwiseShiftRight, + parse_math_expr_add + ) + } + + fn parse_math_expr_bit_and(&mut self) -> Result { + math_expr_parser!(self, Token::BitwiseAnd, parse_math_expr_shift) + } + + fn parse_math_expr_bit_or(&mut self) -> Result { + math_expr_parser!(self, Token::BitwiseOr, parse_math_expr_bit_and) + } + + fn parse_math_expr_compare(&mut self) -> Result { + 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 { + math_expr_parser!(self, Token::Add, parse_math_expr_compare) + } + + fn parse_math_expr_or(&mut self) -> Result { + math_expr_parser!(self, Token::Or, parse_math_expr_and) + } + + fn parse_math_expr(&mut self) -> Result { + math_expr_parser_reverse!( + self, + Token::Assign, + parse_math_expr_or, + parse_math_expr + ) + } + + fn parse_params(&mut self) -> Result>> { + 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> { + if let Token::Ident(ident) = self.lexer.next_token()? { + Ok(ident) + } else { + Err(Error::ExpectedTokenName("Ident")) + } + } + + fn parse_ident_nl(&mut self) -> Result> { + if let Token::Ident(ident) = self.lexer.next_token_nl()? { + Ok(ident) + } else { + Err(Error::ExpectedTokenName("Ident")) + } + } + + fn parse_function(&mut self) -> Result { + 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 { + 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 { + 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 { + self.force_token(Token::Loop)?; + let block = self.parse_block()?; + Ok(Expr::Loop(block)) + } + + fn parse_if(&mut self) -> Result { + 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 { + 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 { + self.force_token(Token::Return)?; + Ok(Expr::Return(Box::new(self.parse_math_expr()?))) + } + + fn parse_expr(&mut self) -> Result { + 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> { + 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 { + if self.lexer.peek_token()? == Token::Function { + self.parse_function() + } else { + self.parse_expr() + } + } + + pub fn parse>(into: T) -> Result { + 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)) + } +}