fin prob
This commit is contained in:
parent
508c4fa1b8
commit
5d2747e26f
44 changed files with 3527 additions and 2276 deletions
88
Cargo.lock
generated
88
Cargo.lock
generated
|
@ -13,9 +13,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.12"
|
||||
version = "0.6.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96b09b5178381e0874812a9b157f7fe84982617e48f71f4e3235482775e5b540"
|
||||
checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
|
@ -114,7 +114,7 @@ dependencies = [
|
|||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.48",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -125,9 +125,9 @@ checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
|
|||
|
||||
[[package]]
|
||||
name = "clipboard-win"
|
||||
version = "5.1.0"
|
||||
version = "5.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ec832972fefb8cf9313b45a0d1945e29c9c251f1d4c6eafc5fe2124c02d2e81"
|
||||
checksum = "12f9a0700e0127ba15d1d52dd742097f821cd9c65939303a44d970465040a297"
|
||||
dependencies = [
|
||||
"error-code",
|
||||
]
|
||||
|
@ -166,9 +166,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "error-code"
|
||||
version = "3.0.0"
|
||||
version = "3.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "281e452d3bad4005426416cdba5ccfd4f5c1280e10099e21db27f7c1c28347fc"
|
||||
checksum = "a0474425d51df81997e2f90a21591180b38eccf27292d755f3e30750225c175b"
|
||||
|
||||
[[package]]
|
||||
name = "fd-lock"
|
||||
|
@ -221,12 +221,23 @@ checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
|
|||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.20"
|
||||
version = "0.4.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
|
||||
|
||||
[[package]]
|
||||
name = "matrix"
|
||||
name = "matrix-bin"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"ctrlc",
|
||||
"matrix-lang",
|
||||
"matrix-std",
|
||||
"rustyline",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matrix-lang"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
|
@ -235,32 +246,21 @@ dependencies = [
|
|||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matrix-bin"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"ctrlc",
|
||||
"matrix",
|
||||
"matrix-stdlib",
|
||||
"rustyline",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matrix-macros"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"matrix",
|
||||
"matrix-lang",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matrix-stdlib"
|
||||
name = "matrix-std"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"matrix",
|
||||
"matrix-lang",
|
||||
"matrix-macros",
|
||||
"os_info",
|
||||
"rand",
|
||||
|
@ -490,7 +490,7 @@ checksum = "e5af959c8bf6af1aff6d2b463a57f71aae53d1332da58419e30ad8dc7011d951"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.48",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -510,7 +510,7 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.48",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -538,9 +538,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.48"
|
||||
version = "2.0.52"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
|
||||
checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -610,9 +610,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.0"
|
||||
version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
|
||||
checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
|
@ -625,42 +625,42 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.0"
|
||||
version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
|
||||
checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.0"
|
||||
version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
|
||||
checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.0"
|
||||
version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
|
||||
checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.0"
|
||||
version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
|
||||
checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.0"
|
||||
version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
|
||||
checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.0"
|
||||
version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
|
||||
checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.0"
|
||||
version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
|
||||
checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
[workspace]
|
||||
resolver = "2"
|
||||
members = [ "matrix", "matrix-bin" , "matrix-macros", "matrix-stdlib"]
|
||||
members = [ "matrix-lang", "matrix-bin" , "matrix-macros", "matrix-std"]
|
||||
|
|
385
matrix-bin/Cargo.lock
generated
385
matrix-bin/Cargo.lock
generated
|
@ -1,385 +0,0 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clipboard-win"
|
||||
version = "5.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ec832972fefb8cf9313b45a0d1945e29c9c251f1d4c6eafc5fe2124c02d2e81"
|
||||
dependencies = [
|
||||
"error-code",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "endian-type"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "error-code"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "281e452d3bad4005426416cdba5ccfd4f5c1280e10099e21db27f7c1c28347fc"
|
||||
|
||||
[[package]]
|
||||
name = "fd-lock"
|
||||
version = "4.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e5768da2206272c81ef0b5e951a41862938a6070da63bcea197899942d3b947"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "home"
|
||||
version = "0.5.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.153"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||
|
||||
[[package]]
|
||||
name = "matrix"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"num-complex",
|
||||
"num-rational",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matrix-repl"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"matrix",
|
||||
"rustyline",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
|
||||
|
||||
[[package]]
|
||||
name = "nibble_vec"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43"
|
||||
dependencies = [
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.27.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-complex"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.46"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-rational"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-bigint",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "radix_trie"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd"
|
||||
dependencies = [
|
||||
"endian-type",
|
||||
"nibble_vec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustyline"
|
||||
version = "13.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02a2d683a4ac90aeef5b1013933f6d977bd37d51ff3f4dad829d4931a7e6be86"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"clipboard-win",
|
||||
"fd-lock",
|
||||
"home",
|
||||
"libc",
|
||||
"log",
|
||||
"memchr",
|
||||
"nix",
|
||||
"radix_trie",
|
||||
"unicode-segmentation",
|
||||
"unicode-width",
|
||||
"utf8parse",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
|
|
@ -10,6 +10,6 @@ path = "src/main.rs"
|
|||
[dependencies]
|
||||
clap = { version = "4", features = [ "derive" ] }
|
||||
ctrlc = "3"
|
||||
matrix = { path = "../matrix" }
|
||||
matrix-stdlib = { path = "../matrix-stdlib" }
|
||||
matrix-lang = { path = "../matrix-lang" }
|
||||
matrix-std = { path = "../matrix-std" }
|
||||
rustyline = { version = "13", features = [ "derive" ] }
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use std::{borrow::Cow, rc::Rc, cell::RefCell};
|
||||
|
||||
use matrix::{lex::{Lexer, TokenData, Token}, vm::Vm};
|
||||
use rustyline::{validate::{Validator, ValidationResult, ValidationContext}, highlight::Highlighter, Helper, Hinter, completion::Completer};
|
||||
use matrix_lang::prelude::*;
|
||||
|
||||
#[derive(Helper, Hinter)]
|
||||
pub struct MatrixHelper {
|
||||
|
@ -205,6 +204,7 @@ impl Highlighter for MatrixHelper {
|
|||
T::Else |
|
||||
T::While |
|
||||
T::Let |
|
||||
T::Const |
|
||||
T::Function |
|
||||
T::Continue |
|
||||
T::Break |
|
||||
|
@ -290,12 +290,12 @@ impl Completer for MatrixHelper {
|
|||
}
|
||||
|
||||
let _ = (line, pos, ctx);
|
||||
let globals = self.vm.borrow().global_names();
|
||||
let globals = self.vm.borrow().globals();
|
||||
let names: Vec<Rc<str>> = globals
|
||||
.borrow()
|
||||
.clone()
|
||||
.into_iter()
|
||||
.filter(|n| n.starts_with(&buf))
|
||||
.filter_map(|n| if n.name.starts_with(&buf) { Some(n.name.clone()) } else { None })
|
||||
.collect();
|
||||
|
||||
if buf.is_empty() {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::{path::PathBuf, io::{self, Read, IsTerminal}, fs, cell::RefCell, rc::Rc};
|
||||
use clap::{Parser as ArgParser, ColorChoice};
|
||||
use matrix::{compiler::{Compiler, CompilerBuilder}, vm::Vm, parse::{Parser, ParserBuilder}, value::Value};
|
||||
use matrix_lang::prelude::*;
|
||||
use repl::Repl;
|
||||
|
||||
mod repl;
|
||||
|
@ -12,16 +12,20 @@ pub struct Args {
|
|||
/// A path to a input program. Uses stdin if not specified.
|
||||
file: Option<PathBuf>,
|
||||
|
||||
/// Runs a repl, loading the provided program first
|
||||
/// Compiles the given program
|
||||
#[arg(short, long)]
|
||||
repl: bool,
|
||||
compile: bool,
|
||||
|
||||
/// Optional output for compiled output
|
||||
#[arg(short, long)]
|
||||
output: Option<PathBuf>,
|
||||
|
||||
/// Print out debug information
|
||||
#[arg(short, long)]
|
||||
debug: bool,
|
||||
|
||||
/// Choses color
|
||||
#[arg(short, long)]
|
||||
#[arg(long)]
|
||||
color: Option<ColorChoice>,
|
||||
|
||||
/// Disables optimizations
|
||||
|
@ -29,18 +33,21 @@ pub struct Args {
|
|||
disable_optimizations: bool,
|
||||
}
|
||||
|
||||
pub enum Mode {
|
||||
Repl,
|
||||
Execute(String),
|
||||
Compile(String, PathBuf),
|
||||
}
|
||||
|
||||
pub struct State<'a> {
|
||||
parser: Parser,
|
||||
compiler: Compiler<'a>,
|
||||
vm: Rc<RefCell<Vm>>,
|
||||
repl: bool,
|
||||
color: bool,
|
||||
#[allow(unused)]
|
||||
debug: bool,
|
||||
}
|
||||
|
||||
impl<'a> State<'a> {
|
||||
pub fn new (args: Args) -> (Self, Option<String>) {
|
||||
pub fn new (args: Args) -> (Self, Mode) {
|
||||
|
||||
let stdin = read_stdin();
|
||||
|
||||
|
@ -53,20 +60,43 @@ impl<'a> State<'a> {
|
|||
file = None;
|
||||
}
|
||||
|
||||
let repl = args.repl || file.is_none();
|
||||
let mode;
|
||||
let repl;
|
||||
if args.compile {
|
||||
let path = match (args.output, args.file) {
|
||||
(Some(path), _) => path,
|
||||
(None, Some(path)) => {
|
||||
let mut path = path.clone();
|
||||
path.set_extension("matc");
|
||||
path
|
||||
},
|
||||
(None, None) => {
|
||||
PathBuf::from("matc.out")
|
||||
}
|
||||
};
|
||||
let file = file.unwrap_or(String::new());
|
||||
mode = Mode::Compile(file, path);
|
||||
repl = false;
|
||||
} else if let Some(file) = file {
|
||||
mode = Mode::Execute(file);
|
||||
repl = false;
|
||||
} else {
|
||||
mode = Mode::Repl;
|
||||
repl = true;
|
||||
}
|
||||
|
||||
let mut vm = Vm::new();
|
||||
let parser = ParserBuilder::new()
|
||||
.optimize(!args.disable_optimizations)
|
||||
.build();
|
||||
let mut vm = Vm::new();
|
||||
let compiler = CompilerBuilder::new()
|
||||
.repl(repl)
|
||||
.debug(args.debug)
|
||||
.names(vm.names())
|
||||
.globals(vm.global_names())
|
||||
.globals(vm.globals())
|
||||
.build();
|
||||
|
||||
matrix_stdlib::load(&mut vm);
|
||||
matrix_std::load(&mut vm);
|
||||
|
||||
let color = match args.color {
|
||||
Some(ColorChoice::Auto) | None => {
|
||||
|
@ -76,21 +106,40 @@ impl<'a> State<'a> {
|
|||
Some(ColorChoice::Never) => false,
|
||||
};
|
||||
|
||||
(Self { parser, vm: Rc::new(RefCell::new(vm)), compiler, repl, debug: args.debug, color }, file)
|
||||
(Self {
|
||||
parser,
|
||||
vm: Rc::new(RefCell::new(vm)),
|
||||
compiler,
|
||||
color,
|
||||
}, mode)
|
||||
}
|
||||
|
||||
pub fn execute(&mut self, code: String) -> matrix::Result<Value> {
|
||||
let ast = self.parser.parse(code)?;
|
||||
let fun = self.compiler.compile(&ast)?;
|
||||
pub fn execute(&mut self, fun: Rc<Function>) -> Result<Value> {
|
||||
let val = self.vm.try_borrow_mut().unwrap().run(fun)?;
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
pub fn compile(&mut self, code: String) -> Result<Rc<Function>> {
|
||||
let ast = self.parser.parse(code)?;
|
||||
let fun = self.compiler.compile(&ast)?;
|
||||
Ok(fun)
|
||||
}
|
||||
|
||||
pub fn load_program(&mut self, body: String) -> Result<Rc<Function>> {
|
||||
match Program::load(&body)? {
|
||||
Some(fun) => {
|
||||
Ok(fun)
|
||||
},
|
||||
None => {
|
||||
self.compile(body)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn error(err: matrix::Error, state: &State) {
|
||||
pub fn error(err: Exception, state: &State) {
|
||||
if state.color {
|
||||
println!("\x1b[31m\x1b[1mError:\x1b[0m {err}");
|
||||
println!("\x1b[31mError:\x1b[0m {err}");
|
||||
} else {
|
||||
println!("Error: {err}");
|
||||
}
|
||||
|
@ -106,19 +155,33 @@ fn read_stdin() -> String {
|
|||
buffer
|
||||
}
|
||||
|
||||
fn handle_mode(state: &mut State, mode: Mode) -> Result<()> {
|
||||
match mode {
|
||||
Mode::Repl => {
|
||||
let mut repl = Repl::new(state);
|
||||
repl.run();
|
||||
},
|
||||
Mode::Execute(body) => {
|
||||
let fun = state.load_program(body)?;
|
||||
state.execute(fun)?;
|
||||
},
|
||||
Mode::Compile(body, path) => {
|
||||
let fun = state.compile(body)?;
|
||||
let mut file = File::create(path).map_err(|e|
|
||||
exception!(IO_EXCEPTION, "{e}")
|
||||
)?;
|
||||
Program::save(fun, &mut file)?;
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
||||
let args = Args::parse();
|
||||
let (mut state, file) = State::new(args);
|
||||
let (mut state, mode) = State::new(args);
|
||||
|
||||
if let Some(file) = file {
|
||||
if let Err(err) = state.execute(file) {
|
||||
error(err, &state);
|
||||
}
|
||||
if let Err(e) = handle_mode(&mut state, mode) {
|
||||
error(e, &state);
|
||||
}
|
||||
|
||||
if state.repl {
|
||||
Repl::new(state).run();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,21 +1,26 @@
|
|||
use std::{io::Write, sync::atomic::Ordering};
|
||||
|
||||
use matrix::{value::Value, vm::Interupt};
|
||||
use rustyline::{Config, EditMode, ColorMode, Editor, CompletionType};
|
||||
use rustyline::{Config, EditMode, ColorMode, Editor, CompletionType, error::ReadlineError};
|
||||
use matrix_lang::prelude::*;
|
||||
|
||||
use crate::{State, helper::MatrixHelper};
|
||||
|
||||
pub struct Repl<'a> {
|
||||
state: State<'a>
|
||||
pub struct Repl<'s, 'a> {
|
||||
state: &'s mut State<'a>
|
||||
}
|
||||
|
||||
impl<'a> Repl<'a> {
|
||||
impl<'s, 'a> Repl<'s, 'a> {
|
||||
|
||||
pub fn new(state: State<'a>) -> Self {
|
||||
pub fn new(state: &'s mut State<'a>) -> Self {
|
||||
Self { state }
|
||||
}
|
||||
|
||||
pub fn run(&mut self) {
|
||||
fn execute(&mut self, line: String) -> Result<Value> {
|
||||
let fun = self.state.compile(line)?;
|
||||
let val = self.state.execute(fun)?;
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
pub fn run(&mut self) {
|
||||
|
||||
let interupt = self.state.vm.borrow().interupt();
|
||||
ctrlc::set_handler(move || {
|
||||
|
@ -23,26 +28,33 @@ impl<'a> Repl<'a> {
|
|||
}).unwrap();
|
||||
|
||||
let config = Config::builder()
|
||||
.indent_size(4)
|
||||
.edit_mode(EditMode::Emacs)
|
||||
.check_cursor_position(true)
|
||||
.completion_type(CompletionType::List)
|
||||
.edit_mode(EditMode::Emacs)
|
||||
.color_mode(if self.state.color { ColorMode::Enabled } else { ColorMode::Disabled })
|
||||
.build();
|
||||
|
||||
let helper = MatrixHelper::new(self.state.vm.clone());
|
||||
|
||||
let histfile = std::env::var("MATRIX_HISTORY").ok();
|
||||
|
||||
let mut rl = Editor::with_config(config).unwrap();
|
||||
rl.set_helper(Some(helper));
|
||||
if let Some(hf) = &histfile {
|
||||
rl.load_history(hf).ok();
|
||||
}
|
||||
|
||||
loop {
|
||||
let Ok(line) = rl.readline(">> ") else {
|
||||
break;
|
||||
let line = match rl.readline(">> ") {
|
||||
Ok(line) => line,
|
||||
Err(ReadlineError::Eof) => break,
|
||||
Err(_) => continue,
|
||||
};
|
||||
if let Err(_) = rl.add_history_entry(&line) {
|
||||
break;
|
||||
};
|
||||
match self.state.execute(line) {
|
||||
Err(err) => crate::error(err, &self.state),
|
||||
|
||||
rl.add_history_entry(&line).ok();
|
||||
|
||||
match self.execute(line) {
|
||||
Ok(val) => {
|
||||
if val != Value::Nil {
|
||||
if self.state.color {
|
||||
|
@ -52,8 +64,13 @@ impl<'a> Repl<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let _ = std::io::stdout().flush();
|
||||
Err(err) => crate::error(err, &self.state),
|
||||
};
|
||||
std::io::stdout().flush().ok();
|
||||
}
|
||||
|
||||
if let Some(hf) = &histfile {
|
||||
rl.save_history(hf).ok();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
0
matrix/Cargo.lock → matrix-lang/Cargo.lock
generated
0
matrix/Cargo.lock → matrix-lang/Cargo.lock
generated
|
@ -1,5 +1,5 @@
|
|||
[package]
|
||||
name = "matrix"
|
||||
name = "matrix-lang"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
|
@ -1,42 +1,70 @@
|
|||
use std::{rc::Rc, ops::{Neg, Not}, fmt::Debug};
|
||||
use crate::{lex::{Position, TokenData}, value::{Value, InlineList, InlineMatrix, InlineTable}, Result};
|
||||
use std::{ops::{Neg, Not}, fmt::{Debug, Display}};
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
pub type AstName = (Rc<str>, Position);
|
||||
pub type InlineList = Vec<Expr>;
|
||||
pub type InlineMatrix = (usize, usize, Vec<Expr>);
|
||||
pub type InlineTable = Vec<(Expr, Expr)>;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub enum UnaryOp {
|
||||
// normal math
|
||||
Negate,
|
||||
Negate = 1,
|
||||
// equality
|
||||
Not,
|
||||
Not = 2,
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for UnaryOp {
|
||||
type Error = Exception;
|
||||
|
||||
fn try_from(value: u8) -> std::prelude::v1::Result<Self, Self::Error> {
|
||||
if value < 1 || value > 2 {
|
||||
Err(exception!(BINARY_EXCEPTION, "cannot convert {value} to UnaryOp"))
|
||||
} else {
|
||||
unsafe { Ok(std::mem::transmute(value)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub enum BinaryOp {
|
||||
// normal math
|
||||
Add,
|
||||
Subtract,
|
||||
Multiply,
|
||||
Divide,
|
||||
Modulo,
|
||||
Power,
|
||||
Add = 1,
|
||||
Subtract = 2,
|
||||
Multiply = 3,
|
||||
Divide = 4,
|
||||
Modulo = 5,
|
||||
Power = 6,
|
||||
// binary math
|
||||
BitwiseAnd,
|
||||
BitwiseOr,
|
||||
BitwiseXor,
|
||||
BitwiseShiftLeft,
|
||||
BitwiseShiftRight,
|
||||
BitwiseAnd = 7,
|
||||
BitwiseOr = 8,
|
||||
BitwiseXor = 9,
|
||||
BitwiseShiftLeft = 10,
|
||||
BitwiseShiftRight = 11,
|
||||
// equality
|
||||
Equals,
|
||||
NotEquals,
|
||||
GreaterEquals,
|
||||
LessEquals,
|
||||
GreaterThan,
|
||||
LessThan,
|
||||
Equals = 12,
|
||||
NotEquals = 13,
|
||||
GreaterEquals = 14,
|
||||
LessEquals = 15,
|
||||
GreaterThan = 16,
|
||||
LessThan = 17,
|
||||
// iter
|
||||
Range,
|
||||
RangeEq
|
||||
Range = 18,
|
||||
RangeEq = 19
|
||||
}
|
||||
|
||||
pub type AstName = (Rc<str>, Position);
|
||||
impl TryFrom<u8> for BinaryOp {
|
||||
type Error = Exception;
|
||||
|
||||
fn try_from(value: u8) -> std::prelude::v1::Result<Self, Self::Error> {
|
||||
if value < 1 || value > 19 {
|
||||
Err(exception!(BINARY_EXCEPTION, "cannot convert {value} to BinaryOp"))
|
||||
} else {
|
||||
unsafe { Ok(std::mem::transmute(value)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum ExprData {
|
||||
|
@ -75,6 +103,7 @@ pub enum ExprData {
|
|||
Try(Box<Expr>, AstName, Box<Expr>),
|
||||
|
||||
Let(AstName, Box<Expr>),
|
||||
Const(AstName, Box<Expr>),
|
||||
|
||||
Pipeline(Box<Expr>, Box<Expr>),
|
||||
|
||||
|
@ -83,22 +112,24 @@ pub enum ExprData {
|
|||
Return(Box<Expr>),
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct Expr {
|
||||
pub data: ExprData,
|
||||
pub pos: Position
|
||||
impl Display for Expr {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{self:?}")
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Expr {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
if f.alternate() {
|
||||
write!(f, "{:#?}", self.data)
|
||||
} else {
|
||||
write!(f, "{:?}", self.data)
|
||||
}
|
||||
write!(f, "{:?}", self.data)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct Expr {
|
||||
pub data: ExprData,
|
||||
pub pos: Position
|
||||
}
|
||||
|
||||
impl From<(ExprData, Position)> for Expr {
|
||||
fn from(value: (ExprData, Position)) -> Self {
|
||||
Self { data: value.0, pos: value.1 }
|
||||
|
@ -377,6 +408,9 @@ pub fn optimize(mut expr: Expr) -> Result<Expr> {
|
|||
E::Let(ident, expr) => {
|
||||
E::Let(ident, Box::new(optimize(*expr)?))
|
||||
},
|
||||
E::Const(ident, expr) => {
|
||||
E::Const(ident, Box::new(optimize(*expr)?))
|
||||
},
|
||||
E::Assign(lhs, rhs) => {
|
||||
let lhs = Box::new(optimize(*lhs)?);
|
||||
let rhs = Box::new(optimize(*rhs)?);
|
160
matrix-lang/src/binary/deserialize.rs
Normal file
160
matrix-lang/src/binary/deserialize.rs
Normal file
|
@ -0,0 +1,160 @@
|
|||
use crate::prelude::*;
|
||||
|
||||
use super::{prim::VarInt, Deserialize, Deserializer};
|
||||
|
||||
macro_rules! error {
|
||||
($($arg:tt)*) => {
|
||||
exception!(BINARY_EXCEPTION, $($arg)*)
|
||||
};
|
||||
}
|
||||
|
||||
impl Deserialize for Program {
|
||||
fn deserialize<S: Deserializer>(s: &mut S) -> Result<Self> {
|
||||
for _ in 0..5 { s.read::<u8>()?; } // skip header
|
||||
let version: u8 = s.read()?;
|
||||
if version != 0 {
|
||||
return Err(error!("invalid program version {version}"))
|
||||
}
|
||||
let fun = <Rc<Function>>::deserialize(s)?;
|
||||
Ok(Self { version, fun })
|
||||
}
|
||||
}
|
||||
|
||||
impl Deserialize for Instruction {
|
||||
fn deserialize<S: Deserializer>(s: &mut S) -> Result<Self> {
|
||||
use Instruction as I;
|
||||
let ins: u8 = s.read()?;
|
||||
let ins = match ins {
|
||||
0 => I::NoOp,
|
||||
1 => I::CreateLocal,
|
||||
2 => I::LoadLocal(s.read()?),
|
||||
3 => I::StoreLocal(s.read()?),
|
||||
4 => I::DiscardLocals(s.read()?),
|
||||
5 => I::LoadGlobal(s.read()?),
|
||||
6 => I::StoreGlobal(s.read()?),
|
||||
7 => I::Const(s.read()?),
|
||||
8 => I::Int(s.read()?),
|
||||
9 => I::True,
|
||||
10 => I::False,
|
||||
11 => I::Nil,
|
||||
12 => I::Dup,
|
||||
13 => I::Discard(s.read()?),
|
||||
14 => I::UnaryOp(UnaryOp::try_from(s.read::<u8>()?)?),
|
||||
15 => I::BinaryOp(BinaryOp::try_from(s.read::<u8>()?)?),
|
||||
16 => I::NewList(s.read()?),
|
||||
17 => I::NewTable(s.read()?),
|
||||
18 => I::NewMatrix(s.read()?, s.read()?),
|
||||
19 => I::Field(s.read()?),
|
||||
20 => I::StoreField(s.read()?),
|
||||
21 => I::Index(s.read()?),
|
||||
22 => I::StoreIndex(s.read()?),
|
||||
23 => I::Jump(s.read()?),
|
||||
24 => I::JumpTrue(s.read()?),
|
||||
25 => I::JumpFalse(s.read()?),
|
||||
26 => I::JumpNil(s.read()?),
|
||||
27 => I::IterCreate,
|
||||
28 => I::IterNext,
|
||||
29 => I::Try(s.read()?),
|
||||
30 => I::TryEnd,
|
||||
31 => I::Call(s.read()?),
|
||||
32 => I::Return,
|
||||
n => return Err(error!("invalid instruction op code {n}"))
|
||||
};
|
||||
Ok(ins)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Deserialize> Deserialize for Vec<T> {
|
||||
fn deserialize<S: Deserializer>(s: &mut S) -> Result<Self> {
|
||||
let len = s.read::<VarInt>()?.0;
|
||||
let mut vec = Vec::with_capacity(len);
|
||||
for _ in 0..len {
|
||||
let v = T::deserialize(s)?;
|
||||
vec.push(v);
|
||||
}
|
||||
Ok(vec)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Deserialize> Deserialize for Gc<T> {
|
||||
fn deserialize<S: Deserializer>(s: &mut S) -> Result<Self> {
|
||||
Ok(Gc::new(T::deserialize(s)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Deserialize> Deserialize for Rc<T> {
|
||||
fn deserialize<S: Deserializer>(s: &mut S) -> Result<Self> {
|
||||
Ok(Rc::new(T::deserialize(s)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl Deserialize for Position {
|
||||
fn deserialize<S: Deserializer>(s: &mut S) -> Result<Self> {
|
||||
let row = s.read::<VarInt>()?.0;
|
||||
let col = s.read::<VarInt>()?.0;
|
||||
Ok(Self { row, col })
|
||||
}
|
||||
}
|
||||
|
||||
impl Deserialize for Chunk {
|
||||
fn deserialize<S: Deserializer>(s: &mut S) -> Result<Self> {
|
||||
let constants = <Vec<Value>>::deserialize(s)?;
|
||||
let code = <Vec<Instruction>>::deserialize(s)?;
|
||||
let pos = <Vec<Position>>::deserialize(s)?;
|
||||
Ok(Self { constants, code, pos })
|
||||
}
|
||||
}
|
||||
|
||||
impl Deserialize for Value {
|
||||
fn deserialize<S: Deserializer>(s: &mut S) -> Result<Self> {
|
||||
use Value as V;
|
||||
let ty = s.read::<u8>()?;
|
||||
let value = match ty {
|
||||
0 => V::Nil,
|
||||
1 => V::Bool(s.read()?),
|
||||
2 => V::Int(s.read()?),
|
||||
3 => V::Float(s.read()?),
|
||||
4 => V::Ratio(Rational64::new(s.read()?, s.read()?)),
|
||||
5 => V::Complex(Complex64::new(s.read()?, s.read()?)),
|
||||
6 => V::to_regex(s.read::<String>()?.as_str())?,
|
||||
7 => V::String(s.read::<String>()?.into()),
|
||||
8 => V::List(<Vec<Value>>::deserialize(s)?.into()),
|
||||
9 => {
|
||||
let domain = s.read()?;
|
||||
let codomain = s.read()?;
|
||||
let values = <Vec<Value>>::deserialize(s)?;
|
||||
V::Matrix(Matrix::new(domain, codomain, values).into())
|
||||
},
|
||||
10 => {
|
||||
let len = s.read::<VarInt>()?.0;
|
||||
let mut table = ValueMap::new();
|
||||
for _ in 0..len {
|
||||
let key = <Value>::deserialize(s)?;
|
||||
let value = <Value>::deserialize(s)?;
|
||||
table.insert(key, value)?;
|
||||
}
|
||||
V::Table(table.into())
|
||||
},
|
||||
11 => V::Function(<Rc<Function>>::deserialize(s)?),
|
||||
12 => V::Range((s.read()?, s.read()?, s.read()?).into()),
|
||||
13 => V::Iter(<Rc<Function>>::deserialize(s)?),
|
||||
n => return Err(error!("invalid value code {n}"))
|
||||
};
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deserialize for Function {
|
||||
fn deserialize<S: Deserializer>(s: &mut S) -> Result<Self> {
|
||||
let name = s.read::<String>()?;
|
||||
let arity = s.read()?;
|
||||
let variadic = s.read()?;
|
||||
let chunk = <Chunk>::deserialize(s)?;
|
||||
Ok(Function {
|
||||
name: Rc::from(name.as_str()),
|
||||
arity,
|
||||
variadic,
|
||||
fun: InnerFunction::Compiled(chunk.into())
|
||||
})
|
||||
}
|
||||
}
|
154
matrix-lang/src/binary/mod.rs
Normal file
154
matrix-lang/src/binary/mod.rs
Normal file
|
@ -0,0 +1,154 @@
|
|||
use crate::prelude::*;
|
||||
use std::io::{self, Read, Write};
|
||||
|
||||
mod serialize;
|
||||
mod deserialize;
|
||||
mod prim;
|
||||
|
||||
pub struct Program {
|
||||
version: u8,
|
||||
fun: Rc<Function>
|
||||
}
|
||||
|
||||
const PROGRAM_HEADER: [u8; 5] = [0x00, 0x4d, 0x41, 0x54, 0x0a];
|
||||
|
||||
impl Program {
|
||||
pub fn load(body: &str) -> Result<Option<Rc<Function>>> {
|
||||
let mut bytes = body.as_bytes();
|
||||
if bytes.len() < 6 {
|
||||
return Ok(None)
|
||||
}
|
||||
let header = &bytes[0..5];
|
||||
if header != &PROGRAM_HEADER {
|
||||
return Ok(None)
|
||||
}
|
||||
let mut s = ProgramDeserializer::from(&mut bytes);
|
||||
let program = <Self>::deserialize(&mut s)?;
|
||||
s.finish()?;
|
||||
Ok(Some(program.fun.clone()))
|
||||
}
|
||||
|
||||
pub fn save<W: Write>(fun: Rc<Function>, w: &mut W) -> Result<()> {
|
||||
let mut s = ProgramSerializer::from(w);
|
||||
let p = Program {
|
||||
version: 0,
|
||||
fun
|
||||
};
|
||||
s.serialize(&p)?;
|
||||
s.finish()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Primitive : Sized {
|
||||
fn write<W: Write>(&self, w: &mut W) -> io::Result<()>;
|
||||
fn read<R: Read>(r: &mut R) -> io::Result<Self>;
|
||||
}
|
||||
|
||||
pub trait Serialize : Sized {
|
||||
fn serialize<S: Serializer>(&self, s: &mut S) -> Result<()>;
|
||||
}
|
||||
|
||||
pub trait Serializer : Sized {
|
||||
fn serialize<S: Serialize>(&mut self, val: &S) -> Result<()> {
|
||||
val.serialize(self)
|
||||
}
|
||||
fn write<P: Primitive>(&mut self, val: P) -> Result<()>;
|
||||
}
|
||||
|
||||
pub trait Deserialize : Sized {
|
||||
fn deserialize<S: Deserializer>(s: &mut S) -> Result<Self>;
|
||||
}
|
||||
|
||||
pub trait Deserializer : Sized {
|
||||
fn deserialize<D: Deserialize>(&mut self) -> Result<D> {
|
||||
D::deserialize(self)
|
||||
}
|
||||
fn read<P: Primitive>(&mut self) -> Result<P>;
|
||||
}
|
||||
|
||||
macro_rules! error {
|
||||
($($arg:tt)*) => {
|
||||
exception!(BINARY_EXCEPTION, $($arg)*)
|
||||
};
|
||||
}
|
||||
|
||||
pub struct ProgramSerializer<'w, W: Write> {
|
||||
writer: &'w mut W,
|
||||
checksum: u64,
|
||||
}
|
||||
|
||||
impl<'w, W: Write> ProgramSerializer<'w, W> {
|
||||
fn finish(self) -> Result<()> {
|
||||
let bytes = self.checksum.to_le_bytes();
|
||||
self.writer.write(&bytes).map_err(|e| error!("{e}"))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'w, W: Write> Serializer for ProgramSerializer<'w, W> {
|
||||
fn write<P: Primitive>(&mut self, val: P) -> Result<()> {
|
||||
val.write(self).map_err(|e| error!("{e}"))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'w, W: Write> Write for ProgramSerializer<'w, W> {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
for b in buf {
|
||||
self.checksum %= 0xf1e3beef;
|
||||
self.checksum += *b as u64;
|
||||
}
|
||||
self.writer.write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.writer.flush()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'w, W: Write> From<&'w mut W> for ProgramSerializer<'w, W> {
|
||||
fn from(writer: &'w mut W) -> Self {
|
||||
Self { writer, checksum: 0xfe }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ProgramDeserializer<'r, R: Read> {
|
||||
reader: &'r mut R,
|
||||
checksum: u64,
|
||||
}
|
||||
|
||||
impl<'r, R: Read> ProgramDeserializer<'r, R> {
|
||||
fn finish(self) -> Result<()> {
|
||||
let mut bytes = [0u8; 8];
|
||||
self.reader.read_exact(&mut bytes).map_err(|e| error!("{e}"))?;
|
||||
let checksum = u64::from_le_bytes(bytes);
|
||||
if self.checksum != checksum {
|
||||
return Err(error!("checksum doesnt match"))
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r, R: Read> Deserializer for ProgramDeserializer<'r, R> {
|
||||
fn read<P: Primitive>(&mut self) -> Result<P> {
|
||||
P::read(self).map_err(|e| error!("{e}"))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r, R: Read> Read for ProgramDeserializer<'r, R> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
let c = self.reader.read(buf)?;
|
||||
for i in 0..c {
|
||||
let b = buf[i];
|
||||
self.checksum %= 0xf1e3beef;
|
||||
self.checksum += b as u64;
|
||||
}
|
||||
Ok(c)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r, R: Read> From<&'r mut R> for ProgramDeserializer<'r, R> {
|
||||
fn from(reader: &'r mut R) -> Self {
|
||||
Self { reader, checksum: 0xfe }
|
||||
}
|
||||
}
|
109
matrix-lang/src/binary/prim.rs
Normal file
109
matrix-lang/src/binary/prim.rs
Normal file
|
@ -0,0 +1,109 @@
|
|||
use super::Primitive;
|
||||
use std::io::{self, Read, Write};
|
||||
|
||||
macro_rules! marshal_number {
|
||||
($type:ident, $byte:expr) => {
|
||||
impl Primitive for $type {
|
||||
fn write<W: Write>(&self, write: &mut W) -> io::Result<()> {
|
||||
write.write(&self.to_le_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read<R: Read>(read: &mut R) -> io::Result<Self> {
|
||||
let mut bytes = [0u8; $byte];
|
||||
read.read_exact(&mut bytes)?;
|
||||
Ok(Self::from_le_bytes(bytes))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
marshal_number!(u8, 1);
|
||||
marshal_number!(u16, 2);
|
||||
marshal_number!(i16, 2);
|
||||
marshal_number!(u32, 4);
|
||||
marshal_number!(i64, 8);
|
||||
marshal_number!(f64, 8);
|
||||
|
||||
impl Primitive for bool {
|
||||
fn write<W: Write>(&self, write: &mut W) -> io::Result<()> {
|
||||
if *self {
|
||||
write.write(&[1u8; 1])?;
|
||||
} else {
|
||||
write.write(&[0u8; 1])?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read<R: Read>(read: &mut R) -> io::Result<Self> {
|
||||
let mut bytes = [0u8; 1];
|
||||
read.read_exact(&mut bytes)?;
|
||||
if bytes[0] == 1 {
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Primitive for String {
|
||||
fn write<W: Write>(&self, write: &mut W) -> io::Result<()> {
|
||||
let bytes = self.as_bytes();
|
||||
let len = bytes.len();
|
||||
write.write(&(len as u32).to_le_bytes())?;
|
||||
write.write(&bytes)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read<R: Read>(read: &mut R) -> io::Result<Self> {
|
||||
let len = usize::read(read)?;
|
||||
let mut bytes = vec![0u8; len];
|
||||
read.read_exact(&mut bytes)?;
|
||||
Ok(String::from_utf8_lossy(bytes.as_slice()).to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl Primitive for usize {
|
||||
fn write<W: Write>(&self, write: &mut W) -> io::Result<()> {
|
||||
(*self as u32).write(write)
|
||||
}
|
||||
|
||||
fn read<R: Read>(read: &mut R) -> io::Result<Self> {
|
||||
Ok(u32::read(read)? as usize)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VarInt(pub usize);
|
||||
|
||||
impl Primitive for VarInt {
|
||||
fn write<W: Write>(&self, write: &mut W) -> io::Result<()> {
|
||||
let mut data = self.0;
|
||||
loop {
|
||||
let mut byte = (data & 0x7F) as u8;
|
||||
data >>= 7;
|
||||
if data != 0 {
|
||||
byte += 0x80;
|
||||
}
|
||||
write.write(&[byte; 1])?;
|
||||
if data == 0 {
|
||||
return Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn read<R: Read>(read: &mut R) -> io::Result<Self> {
|
||||
let mut buf = [0];
|
||||
let mut result = 0usize;
|
||||
for count in 0..8 {
|
||||
if read.read(&mut buf)? != 1 {
|
||||
return Ok(Self(0))
|
||||
}
|
||||
let byte = buf[0];
|
||||
result |= ((byte & 0x7F) as usize) << (7 * count);
|
||||
if byte & 0x80 == 0 {
|
||||
return Ok(Self(result))
|
||||
}
|
||||
}
|
||||
Ok(Self(0))
|
||||
}
|
||||
}
|
283
matrix-lang/src/binary/serialize.rs
Normal file
283
matrix-lang/src/binary/serialize.rs
Normal file
|
@ -0,0 +1,283 @@
|
|||
use crate::{prelude::*, binary::PROGRAM_HEADER};
|
||||
use std::ops::Deref;
|
||||
use super::{prim::VarInt, Program, Serializer, Serialize};
|
||||
|
||||
macro_rules! error {
|
||||
($($arg:tt)*) => {
|
||||
exception!(BINARY_EXCEPTION, $($arg)*)
|
||||
};
|
||||
}
|
||||
|
||||
impl Serialize for Program {
|
||||
fn serialize<S: Serializer>(&self, s: &mut S) -> Result<()> {
|
||||
for b in PROGRAM_HEADER {
|
||||
s.write(b)?;
|
||||
}
|
||||
s.write(self.version)?;
|
||||
s.serialize(self.fun.as_ref())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Instruction {
|
||||
fn serialize<S: Serializer>(&self, s: &mut S) -> Result<()> {
|
||||
use Instruction as I;
|
||||
match self {
|
||||
I::NoOp => {
|
||||
s.write(0u8)?;
|
||||
},
|
||||
I::CreateLocal => {
|
||||
s.write(1u8)?;
|
||||
},
|
||||
I::LoadLocal(idx) => {
|
||||
s.write(2u8)?;
|
||||
s.write(*idx)?;
|
||||
},
|
||||
I::StoreLocal(idx) => {
|
||||
s.write(3u8)?;
|
||||
s.write(*idx)?;
|
||||
},
|
||||
I::DiscardLocals(idx) => {
|
||||
s.write(4u8)?;
|
||||
s.write(*idx)?;
|
||||
},
|
||||
I::LoadGlobal(idx) => {
|
||||
s.write(5u8)?;
|
||||
s.write(*idx)?;
|
||||
}
|
||||
I::StoreGlobal(idx) => {
|
||||
s.write(6u8)?;
|
||||
s.write(*idx)?;
|
||||
},
|
||||
I::Const(idx) => {
|
||||
s.write(7u8)?;
|
||||
s.write(*idx)?;
|
||||
},
|
||||
I::Int(i) => {
|
||||
s.write(8u8)?;
|
||||
s.write(*i)?;
|
||||
},
|
||||
I::True => {
|
||||
s.write(9u8)?;
|
||||
},
|
||||
I::False => {
|
||||
s.write(10u8)?;
|
||||
},
|
||||
I::Nil => {
|
||||
s.write(11u8)?;
|
||||
},
|
||||
I::Dup => {
|
||||
s.write(12u8)?;
|
||||
},
|
||||
I::Discard(idx) => {
|
||||
s.write(13u8)?;
|
||||
s.write(*idx)?;
|
||||
},
|
||||
I::UnaryOp(op) => {
|
||||
s.write(14u8)?;
|
||||
s.write(*op as u8)?;
|
||||
},
|
||||
I::BinaryOp(op) => {
|
||||
s.write(15u8)?;
|
||||
s.write(*op as u8)?;
|
||||
},
|
||||
I::NewList(len) => {
|
||||
s.write(16u8)?;
|
||||
s.write(*len)?;
|
||||
},
|
||||
I::NewTable(len) => {
|
||||
s.write(17u8)?;
|
||||
s.write(*len)?;
|
||||
},
|
||||
I::NewMatrix(d, c) => {
|
||||
s.write(18u8)?;
|
||||
s.write(*d)?;
|
||||
s.write(*c)?;
|
||||
},
|
||||
I::Field(idx) => {
|
||||
s.write(19u8)?;
|
||||
s.write(*idx)?;
|
||||
},
|
||||
I::StoreField(idx) => {
|
||||
s.write(20u8)?;
|
||||
s.write(*idx)?;
|
||||
},
|
||||
I::Index(idx) => {
|
||||
s.write(21u8)?;
|
||||
s.write(*idx)?;
|
||||
},
|
||||
I::StoreIndex(idx) => {
|
||||
s.write(22u8)?;
|
||||
s.write(*idx)?;
|
||||
},
|
||||
I::Jump(ip) => {
|
||||
s.write(23u8)?;
|
||||
s.write(*ip)?;
|
||||
},
|
||||
I::JumpTrue(ip) => {
|
||||
s.write(24u8)?;
|
||||
s.write(*ip)?;
|
||||
},
|
||||
I::JumpFalse(ip) => {
|
||||
s.write(25u8)?;
|
||||
s.write(*ip)?;
|
||||
},
|
||||
I::JumpNil(ip) => {
|
||||
s.write(26u8)?;
|
||||
s.write(*ip)?;
|
||||
},
|
||||
I::IterCreate => {
|
||||
s.write(27u8)?;
|
||||
},
|
||||
I::IterNext => {
|
||||
s.write(28u8)?;
|
||||
},
|
||||
I::Try(ip) => {
|
||||
s.write(29u8)?;
|
||||
s.write(*ip)?;
|
||||
},
|
||||
I::TryEnd => {
|
||||
s.write(30u8)?;
|
||||
},
|
||||
I::Call(arity) => {
|
||||
s.write(31u8)?;
|
||||
s.write(*arity)?;
|
||||
},
|
||||
I::Return => {
|
||||
s.write(32u8)?;
|
||||
},
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Serialize> Serialize for Vec<T> {
|
||||
fn serialize<S: Serializer>(&self, s: &mut S) -> Result<()> {
|
||||
s.write(VarInt(self.len()))?;
|
||||
for val in self {
|
||||
val.serialize(s)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Serialize + Deref> Serialize for Gc<T> {
|
||||
fn serialize<S: Serializer>(&self, s: &mut S) -> Result<()> {
|
||||
self.deref().serialize(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Serialize + Deref> Serialize for Rc<T> {
|
||||
fn serialize<S: Serializer>(&self, s: &mut S) -> Result<()> {
|
||||
self.deref().serialize(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Position {
|
||||
fn serialize<S: Serializer>(&self, s: &mut S) -> Result<()> {
|
||||
s.write(VarInt(self.row))?;
|
||||
s.write(VarInt(self.col))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Chunk {
|
||||
fn serialize<S: Serializer>(&self, s: &mut S) -> Result<()> {
|
||||
self.constants.serialize(s)?;
|
||||
self.code.serialize(s)?;
|
||||
self.pos.serialize(s)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Value {
|
||||
fn serialize<S: Serializer>(&self, s: &mut S) -> Result<()> {
|
||||
use Value as V;
|
||||
match self {
|
||||
V::Nil => {
|
||||
s.write(0u8)?;
|
||||
},
|
||||
V::Bool(b) => {
|
||||
s.write(1u8)?;
|
||||
s.write(*b)?;
|
||||
},
|
||||
V::Int(i) => {
|
||||
s.write(2u8)?;
|
||||
s.write(*i)?;
|
||||
},
|
||||
V::Float(f) => {
|
||||
s.write(3u8)?;
|
||||
s.write(*f)?;
|
||||
},
|
||||
V::Ratio(r) => {
|
||||
s.write(4u8)?;
|
||||
s.write(*r.numer())?;
|
||||
s.write(*r.denom())?;
|
||||
},
|
||||
V::Complex(c) => {
|
||||
s.write(5u8)?;
|
||||
s.write(c.re)?;
|
||||
s.write(c.im)?;
|
||||
},
|
||||
V::Regex(r) => {
|
||||
s.write(6u8)?;
|
||||
s.write(r.to_string())?;
|
||||
},
|
||||
V::String(str) => {
|
||||
s.write(7u8)?;
|
||||
s.write(str.to_string())?;
|
||||
},
|
||||
V::List(l) => {
|
||||
s.write(8u8)?;
|
||||
l.serialize(s)?;
|
||||
},
|
||||
V::Matrix(m) => {
|
||||
s.write(9u8)?;
|
||||
s.write(m.domain)?;
|
||||
s.write(m.codomain)?;
|
||||
m.values.serialize(s)?;
|
||||
},
|
||||
V::Table(t) => {
|
||||
s.write(10u8)?;
|
||||
s.write(VarInt(t.len()))?;
|
||||
for (key, value) in t.entries() {
|
||||
key.serialize(s)?;
|
||||
value.serialize(s)?;
|
||||
}
|
||||
},
|
||||
V::Function(f) => {
|
||||
s.write(11u8)?;
|
||||
f.serialize(s)?;
|
||||
},
|
||||
V::Range(r) => {
|
||||
s.write(12u8)?;
|
||||
s.write(r.0)?;
|
||||
s.write(r.1)?;
|
||||
s.write(r.2)?;
|
||||
},
|
||||
V::Iter(f) => {
|
||||
s.write(13u8)?;
|
||||
f.serialize(s)?;
|
||||
},
|
||||
V::File(_) => return Err(error!("cannot compile file")),
|
||||
V::Exception(_) => return Err(error!("cannot compile exception")),
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Function {
|
||||
fn serialize<S: Serializer>(&self, s: &mut S) -> Result<()> {
|
||||
s.write(self.name.to_string())?;
|
||||
s.write(self.arity)?;
|
||||
s.write(self.variadic)?;
|
||||
use InnerFunction as F;
|
||||
match &self.fun {
|
||||
F::Compiled(c) => {
|
||||
c.serialize(s)?;
|
||||
},
|
||||
F::Native(_) => return Err(error!("cannot compile native function")),
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{value::Value, ast::{UnaryOp, BinaryOp}, vm::{Vm, StackFrame}, Result, lex::Position};
|
||||
use std::{fmt::{Debug, Display}, rc::Rc};
|
||||
use std::fmt::{Debug, Display};
|
||||
use crate::prelude::*;
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct Chunk {
|
||||
|
@ -18,53 +18,6 @@ impl Chunk {
|
|||
}
|
||||
}
|
||||
|
||||
impl Debug for Chunk {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Chunk({})", self.code.len())
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Chunk {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
writeln!(f, "constants: ")?;
|
||||
for (i, c) in self.constants.iter().enumerate() {
|
||||
writeln!(f, " {i:04}: {c}")?;
|
||||
}
|
||||
writeln!(f, "code:")?;
|
||||
for (i, ins) in self.code.iter().enumerate() {
|
||||
writeln!(f, " {i:04}: {ins}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Function {
|
||||
pub name: Rc<str>,
|
||||
pub arity: usize,
|
||||
pub variadic: bool,
|
||||
pub fun: InnerFunction
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum InnerFunction {
|
||||
Compiled(Rc<Chunk>),
|
||||
Native(Rc<dyn Fn((&mut Vm, &mut StackFrame), Vec<Value>) -> Result<Value>>),
|
||||
}
|
||||
|
||||
impl Debug for Function {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
use InnerFunction as F;
|
||||
match self.fun {
|
||||
F::Compiled(_) => {
|
||||
write!(f, "[Function {}]", self.name)
|
||||
},
|
||||
F::Native(_) => {
|
||||
write!(f, "[NativeFunction {}]", self.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[repr(align(4))]
|
||||
pub enum Instruction {
|
||||
|
@ -113,6 +66,26 @@ pub enum Instruction {
|
|||
Return,
|
||||
}
|
||||
|
||||
impl Debug for Chunk {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "[Chunk {}]", self.code.len())
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Chunk {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
writeln!(f, "constants: ")?;
|
||||
for (i, c) in self.constants.iter().enumerate() {
|
||||
writeln!(f, " {i:04}: {c}")?;
|
||||
}
|
||||
writeln!(f, "code:")?;
|
||||
for (i, ins) in self.code.iter().enumerate() {
|
||||
writeln!(f, " {i:04}: {ins}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Instruction {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
use Instruction::*;
|
|
@ -1,14 +1,14 @@
|
|||
use std::{fmt::Display, rc::Rc, cell::RefCell};
|
||||
use crate::{ast::{Expr, ExprData, AstName}, chunk::{Function, Instruction, InnerFunction}, chunk::{Chunk, self}, value::Value, Result, lex::Position};
|
||||
use crate::prelude::*;
|
||||
|
||||
use Instruction as I;
|
||||
use Value as V;
|
||||
use ExprData as E;
|
||||
|
||||
pub type NamesTable = Rc<RefCell<Vec<Rc<str>>>>;
|
||||
pub type GlobalsTable = Rc<RefCell<Vec<Global>>>;
|
||||
|
||||
pub struct CompilerBuilder<'c> {
|
||||
globals: NamesTable,
|
||||
globals: GlobalsTable,
|
||||
names: NamesTable,
|
||||
repl: bool,
|
||||
debug: bool,
|
||||
|
@ -39,7 +39,7 @@ impl<'c> CompilerBuilder<'c> {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn globals(mut self, globals: NamesTable) -> Self {
|
||||
pub fn globals(mut self, globals: GlobalsTable) -> Self {
|
||||
self.globals = globals;
|
||||
self
|
||||
}
|
||||
|
@ -81,9 +81,9 @@ pub struct Compiler<'c> {
|
|||
name: Rc<str>,
|
||||
parent: Option<&'c Compiler<'c>>,
|
||||
|
||||
locals: Vec<Rc<Local>>,
|
||||
globals: Rc<RefCell<Vec<Rc<str>>>>,
|
||||
names: Rc<RefCell<Vec<Rc<str>>>>,
|
||||
locals: Vec<Local>,
|
||||
globals: GlobalsTable,
|
||||
names: NamesTable,
|
||||
|
||||
root_is_block: bool,
|
||||
|
||||
|
@ -97,50 +97,25 @@ pub struct Compiler<'c> {
|
|||
debug: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Local {
|
||||
name: Rc<str>,
|
||||
idx: usize,
|
||||
scope: usize
|
||||
scope: usize,
|
||||
is_const: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum InnerError {
|
||||
Undefined(Rc<str>),
|
||||
Redefined(Rc<str>),
|
||||
InvAssign(Expr),
|
||||
InvContinue,
|
||||
InvBreak,
|
||||
NotImplemented(&'static str),
|
||||
#[derive(Clone)]
|
||||
pub struct Global {
|
||||
pub name: Rc<str>,
|
||||
pub idx: usize,
|
||||
pub is_const: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Error {
|
||||
pos: Position,
|
||||
err: InnerError,
|
||||
}
|
||||
|
||||
impl std::error::Error for self::Error {}
|
||||
|
||||
impl Display for self::Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
use InnerError as E;
|
||||
write!(f, "parse failed at {}:{}, ", self.pos.row, self.pos.col)?;
|
||||
match &self.err {
|
||||
E::Undefined(name) => write!(f, "value {name} is undefined"),
|
||||
E::Redefined(name) => write!(f, "cannot redefine {name} in the same scope"),
|
||||
E::InvAssign(expr) => write!(f, "cannot assign to {expr:?}"),
|
||||
E::InvContinue => write!(f, "cannot continue outside a loop"),
|
||||
E::InvBreak => write!(f, "cannot break outside a loop"),
|
||||
E::NotImplemented(str) => write!(f, "{str} is not implemented yet")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn error<T>(err: InnerError, pos: Position) -> Result<T> {
|
||||
Err(self::Error {
|
||||
pos,
|
||||
err
|
||||
}.into())
|
||||
macro_rules! error {
|
||||
($($arg:tt)*) => {
|
||||
exception!(COMPILE_EXCEPTION, $($arg)*)
|
||||
};
|
||||
}
|
||||
|
||||
impl<'c> Compiler<'c> {
|
||||
|
@ -185,35 +160,35 @@ impl<'c> Compiler<'c> {
|
|||
};
|
||||
}
|
||||
|
||||
fn create_local(&mut self, name: Rc<str>) {
|
||||
let local = Local { name, idx: self.locals.len(), scope: self.scopes.len()};
|
||||
self.locals.push(Rc::new(local));
|
||||
fn create_local(&mut self, name: Rc<str>, is_const: bool) -> Local {
|
||||
let local = Local { name, idx: self.locals.len(), scope: self.scopes.len(), is_const };
|
||||
self.locals.push(local.clone());
|
||||
local
|
||||
}
|
||||
|
||||
fn create_global(&mut self, name: Rc<str>) -> usize {
|
||||
self.globals.borrow_mut().push(name);
|
||||
let c = self.globals.borrow().len() - 1;
|
||||
c
|
||||
fn create_global(&mut self, name: Rc<str>, is_const: bool) -> Global {
|
||||
let global = Global { name, idx: self.globals.borrow().len(), is_const };
|
||||
self.globals.borrow_mut().push(global.clone());
|
||||
global
|
||||
}
|
||||
|
||||
fn create_local_checked(&mut self, name: Rc<str>, pos: Position) -> Result<()> {
|
||||
fn create_local_checked(&mut self, name: Rc<str>, is_const: bool, pos: Position) -> Result<Local> {
|
||||
if let Some(local) = self.find_local(&name) {
|
||||
if local.scope == self.scopes.len() {
|
||||
return error(InnerError::Redefined(name), pos)
|
||||
return Err(error!("redefined {name}").pos(pos))
|
||||
}
|
||||
};
|
||||
self.create_local(name);
|
||||
Ok(())
|
||||
Ok(self.create_local(name, is_const))
|
||||
}
|
||||
|
||||
fn create_global_checked(&mut self, name: Rc<str>, pos: Position) -> Result<usize> {
|
||||
fn create_global_checked(&mut self, name: Rc<str>, is_const: bool, pos: Position) -> Result<Global> {
|
||||
if let Some(_) = self.find_global(&name) {
|
||||
return error(InnerError::Redefined(name).into(), pos)
|
||||
return Err(error!("redefined {name}").pos(pos))
|
||||
}
|
||||
Ok(self.create_global(name))
|
||||
Ok(self.create_global(name, is_const))
|
||||
}
|
||||
|
||||
fn find_local(&self, name: &str) -> Option<Rc<Local>> {
|
||||
fn find_local(&self, name: &str) -> Option<Local> {
|
||||
for local in self.locals.iter().rev() {
|
||||
if local.name.as_ref() == name {
|
||||
return Some(local.clone())
|
||||
|
@ -222,13 +197,13 @@ impl<'c> Compiler<'c> {
|
|||
None
|
||||
}
|
||||
|
||||
fn find_global(&self, name: &str) -> Option<usize> {
|
||||
fn find_global(&self, name: &str) -> Option<Global> {
|
||||
if let Some(parent) = self.parent {
|
||||
return parent.find_global(name)
|
||||
}
|
||||
for (i, global) in self.globals.borrow().iter().enumerate() {
|
||||
if global.as_ref() == name {
|
||||
return Some(i)
|
||||
for global in self.globals.borrow().iter() {
|
||||
if global.name.as_ref() == name {
|
||||
return Some(global.clone())
|
||||
}
|
||||
}
|
||||
None
|
||||
|
@ -294,7 +269,7 @@ impl<'c> Compiler<'c> {
|
|||
let chunk = self.compile_function(name.clone(), params, body)?;
|
||||
let arity = params.len() - if *varadic { 1 } else { 0 };
|
||||
let fun = Value::Function(Rc::new(
|
||||
chunk::Function {
|
||||
Function {
|
||||
name: name.0.clone(),
|
||||
arity,
|
||||
fun: InnerFunction::Compiled(chunk.into()),
|
||||
|
@ -304,10 +279,10 @@ impl<'c> Compiler<'c> {
|
|||
self.emit_const(fun, expr.pos);
|
||||
self.emit(I::Dup, expr.pos);
|
||||
if self.can_make_globals() {
|
||||
let idx = self.create_global_checked(name.0.clone(), name.1)?;
|
||||
self.emit(I::StoreGlobal(idx as u16), expr.pos);
|
||||
let global = self.create_global_checked(name.0.clone(), false, name.1)?;
|
||||
self.emit(I::StoreGlobal(global.idx as u16), expr.pos);
|
||||
} else {
|
||||
self.create_local_checked(name.0.clone(), name.1)?;
|
||||
self.create_local_checked(name.0.clone(), false, name.1)?;
|
||||
self.emit(I::CreateLocal, expr.pos);
|
||||
}
|
||||
},
|
||||
|
@ -316,7 +291,7 @@ impl<'c> Compiler<'c> {
|
|||
let chunk = self.compile_function(name.clone(), params, body)?;
|
||||
let arity = params.len() - if *varadic { 1 } else { 0 };
|
||||
let fun = Value::Function(Rc::new(
|
||||
chunk::Function {
|
||||
Function {
|
||||
name: name.0.clone(),
|
||||
arity,
|
||||
fun: InnerFunction::Compiled(chunk.into()),
|
||||
|
@ -341,7 +316,7 @@ impl<'c> Compiler<'c> {
|
|||
let jmpidx2 = self.emit_temp(expr.pos);
|
||||
self.re_emit(I::Try(self.cur()), jmpidx);
|
||||
self.begin_scope();
|
||||
self.create_local(err.0.clone());
|
||||
self.create_local(err.0.clone(), true);
|
||||
self.emit(I::CreateLocal, err.1);
|
||||
self.compile_expr(catch)?;
|
||||
self.end_scope();
|
||||
|
@ -378,7 +353,7 @@ impl<'c> Compiler<'c> {
|
|||
let jumpidx = self.emit_temp(expr.pos);
|
||||
self.loop_top.push((top as usize, self.scopes.len()));
|
||||
self.begin_scope();
|
||||
self.create_local(name.0.clone());
|
||||
self.create_local(name.0.clone(), true);
|
||||
self.emit(I::CreateLocal, name.1);
|
||||
self.compile_expr(expr)?;
|
||||
self.emit(I::Discard(1), expr.pos);
|
||||
|
@ -407,10 +382,21 @@ impl<'c> Compiler<'c> {
|
|||
self.compile_expr(expr)?;
|
||||
self.emit(I::Dup, expr.pos);
|
||||
if self.can_make_globals() {
|
||||
let global = self.create_global_checked(name.0.clone(), name.1)?;
|
||||
self.emit(I::StoreGlobal(global as u16), expr.pos);
|
||||
let global = self.create_global_checked(name.0.clone(), false, name.1)?;
|
||||
self.emit(I::StoreGlobal(global.idx as u16), expr.pos);
|
||||
} else {
|
||||
self.create_local_checked(name.0.clone(), name.1)?;
|
||||
self.create_local_checked(name.0.clone(), false, name.1)?;
|
||||
self.emit(I::CreateLocal, expr.pos);
|
||||
}
|
||||
},
|
||||
E::Const(name, expr) => {
|
||||
self.compile_expr(expr)?;
|
||||
self.emit(I::Dup, expr.pos);
|
||||
if self.can_make_globals() {
|
||||
let global = self.create_global_checked(name.0.clone(), true, name.1)?;
|
||||
self.emit(I::StoreGlobal(global.idx as u16), expr.pos);
|
||||
} else {
|
||||
self.create_local_checked(name.0.clone(), true, name.1)?;
|
||||
self.emit(I::CreateLocal, expr.pos);
|
||||
}
|
||||
},
|
||||
|
@ -420,7 +406,7 @@ impl<'c> Compiler<'c> {
|
|||
self.collapse_scopes(scope);
|
||||
self.emit(I::Jump(top as u16), expr.pos);
|
||||
} else {
|
||||
return error(InnerError::InvContinue, expr.pos)
|
||||
return Err(error!("invalid continue outside loop").pos(expr.pos))
|
||||
}
|
||||
},
|
||||
E::Break => {
|
||||
|
@ -430,7 +416,7 @@ impl<'c> Compiler<'c> {
|
|||
let tmpidx = self.emit_temp(expr.pos);
|
||||
self.loop_bot.push(tmpidx);
|
||||
} else {
|
||||
return error(InnerError::InvBreak, expr.pos)
|
||||
return Err(error!("invalid break outside loop").pos(expr.pos))
|
||||
}
|
||||
},
|
||||
E::Return(expr) => {
|
||||
|
@ -444,9 +430,9 @@ impl<'c> Compiler<'c> {
|
|||
} else if let Some(local) = self.find_local(name) {
|
||||
self.emit(I::LoadLocal(local.idx as u16), expr.pos);
|
||||
} else if let Some(global) = self.find_global(name) {
|
||||
self.emit(I::LoadGlobal(global as u16), expr.pos);
|
||||
self.emit(I::LoadGlobal(global.idx as u16), expr.pos);
|
||||
} else {
|
||||
return error(InnerError::Undefined(name.clone()), expr.pos)
|
||||
return Err(error!("variable '{name}' is undefined").pos(expr.pos))
|
||||
};
|
||||
},
|
||||
E::Assign(lhs, rhs) => {
|
||||
|
@ -455,14 +441,20 @@ impl<'c> Compiler<'c> {
|
|||
match &lhs.data {
|
||||
E::Ident(name) if name.as_ref() != "_" => {
|
||||
if let Some(local) = self.find_local(&name) {
|
||||
if local.is_const {
|
||||
return Err(error!("cannot assign to const '{name}'").pos(lhs.pos))
|
||||
}
|
||||
self.emit(I::StoreLocal(local.idx as u16), lhs.pos);
|
||||
} else if let Some(global) = self.find_global(&name) {
|
||||
self.emit(I::StoreGlobal(global as u16), lhs.pos);
|
||||
if global.is_const {
|
||||
return Err(error!("cannot assign to const '{name}'").pos(lhs.pos))
|
||||
}
|
||||
self.emit(I::StoreGlobal(global.idx as u16), lhs.pos);
|
||||
} else if self.can_make_globals() {
|
||||
let global = self.create_global_checked(name.clone(), lhs.pos)?;
|
||||
self.emit(I::StoreGlobal(global as u16), lhs.pos);
|
||||
let global = self.create_global_checked(name.clone(), false, lhs.pos)?;
|
||||
self.emit(I::StoreGlobal(global.idx as u16), lhs.pos);
|
||||
} else {
|
||||
self.create_local_checked(name.clone(), lhs.pos)?;
|
||||
self.create_local_checked(name.clone(), false, lhs.pos)?;
|
||||
self.emit(I::CreateLocal, lhs.pos);
|
||||
}
|
||||
},
|
||||
|
@ -478,7 +470,7 @@ impl<'c> Compiler<'c> {
|
|||
let name = self.get_name(ident.0.clone());
|
||||
self.emit(I::StoreField(name as u16), expr.pos);
|
||||
}
|
||||
_ => return error(InnerError::InvAssign(*lhs.clone()), lhs.pos)
|
||||
_ => return Err(error!("assignment to {lhs} is not allowed").pos(lhs.pos))
|
||||
}
|
||||
}
|
||||
E::UnaryOp(expr, op) => {
|
||||
|
@ -554,7 +546,7 @@ impl<'c> Compiler<'c> {
|
|||
fn compile_function(&mut self, name: AstName, params: &Vec<AstName>, body: &Box<Expr>) -> Result<Chunk> {
|
||||
let mut compiler = self.child(name.0);
|
||||
for (name, pos) in params {
|
||||
compiler.create_local(name.clone());
|
||||
compiler.create_local(name.clone(), false);
|
||||
compiler.emit(I::CreateLocal, *pos);
|
||||
}
|
||||
compiler.compile_expr(body)?;
|
||||
|
@ -611,7 +603,7 @@ impl<'c> Compiler<'c> {
|
|||
};
|
||||
|
||||
if self.loop_bot.len() > 0 {
|
||||
return error(InnerError::InvBreak, pos)
|
||||
return Err(error!("invalid break outside loop").pos(pos))
|
||||
}
|
||||
|
||||
if self.debug {
|
|
@ -1,6 +1,5 @@
|
|||
use std::{rc::Rc, fmt::Debug};
|
||||
use regex::Regex;
|
||||
use crate::Result;
|
||||
use std::fmt::{Debug, Display};
|
||||
use crate::prelude::*;
|
||||
|
||||
pub struct RegexToken {
|
||||
regex: Regex
|
||||
|
@ -30,6 +29,36 @@ impl From<RegexToken> for Regex {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
|
||||
pub struct Position {
|
||||
pub row: usize,
|
||||
pub col: usize,
|
||||
}
|
||||
|
||||
impl Default for Position {
|
||||
fn default() -> Self {
|
||||
Self { row: 1, col: 1 }
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Position {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}:{}", self.row, self.col)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for TokenData {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{self:?}")
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Token {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.data)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum TokenData {
|
||||
//syntax
|
||||
|
@ -106,6 +135,7 @@ pub enum TokenData {
|
|||
Else,
|
||||
While,
|
||||
Let,
|
||||
Const,
|
||||
Function,
|
||||
True,
|
||||
False,
|
||||
|
@ -133,48 +163,6 @@ pub struct Token {
|
|||
pub blen: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
UnexpectedCharacter(char),
|
||||
ExpectedChar(char, char),
|
||||
InvalidCodepoint,
|
||||
UnexpectedEof,
|
||||
InvalidDigit(char),
|
||||
InvalidStringEscape(char),
|
||||
InvalidRegex(anyhow::Error),
|
||||
InvalidNumber(String),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
use Error::*;
|
||||
match self {
|
||||
UnexpectedCharacter(char) => write!(f, "Unexpected char: '{char}'"),
|
||||
UnexpectedEof => write!(f, "Unexpected end of file"),
|
||||
ExpectedChar(expected, got) => write!(f, "Expected char: '{expected}', instead got: '{got}'"),
|
||||
InvalidCodepoint => write!(f, "Invalid codepoint"),
|
||||
InvalidDigit(char) => write!(f, "Invalid digit: '{char}'"),
|
||||
InvalidStringEscape(char) => write!(f, "Invalid string escape: '\\{char}"),
|
||||
InvalidRegex(err) => write!(f, "{err}"),
|
||||
InvalidNumber(num) => write!(f, "Invalid number: '{num}'")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
|
||||
pub struct Position {
|
||||
pub row: usize,
|
||||
pub col: usize,
|
||||
}
|
||||
|
||||
impl Default for Position {
|
||||
fn default() -> Self {
|
||||
Self { row: 1, col: 1 }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Lexer {
|
||||
pub index: usize,
|
||||
len: usize,
|
||||
|
@ -203,6 +191,12 @@ impl<T: Into<String>> From<T> for Lexer {
|
|||
}
|
||||
}
|
||||
|
||||
macro_rules! error {
|
||||
($($arg:tt)*) => {
|
||||
exception!(PARSE_EXCEPTION, $($arg)*)
|
||||
};
|
||||
}
|
||||
|
||||
impl Lexer {
|
||||
pub fn new<T: Into<String>>(input: T) -> Self {
|
||||
let data: Vec<char> = input.into().chars().collect();
|
||||
|
@ -239,7 +233,7 @@ impl Lexer {
|
|||
fn next_not_eof(&mut self) -> Result<char> {
|
||||
let c = self.next();
|
||||
if c == '\0' {
|
||||
return Err(Error::UnexpectedEof.into())
|
||||
return Err(error!("unexpected end of file"))
|
||||
}
|
||||
Ok(c)
|
||||
}
|
||||
|
@ -247,7 +241,7 @@ impl Lexer {
|
|||
fn next_expect(&mut self, expected: char) -> Result<char> {
|
||||
let c = self.next();
|
||||
if c != expected {
|
||||
return Err(Error::ExpectedChar(expected, c).into())
|
||||
return Err(error!("expected character '{c}'"))
|
||||
}
|
||||
Ok(c)
|
||||
}
|
||||
|
@ -259,7 +253,6 @@ impl Lexer {
|
|||
}
|
||||
|
||||
fn lex_string(&mut self, delimit: char) -> Result<Rc<str>> {
|
||||
use Error::*;
|
||||
|
||||
let mut buf = String::new();
|
||||
|
||||
|
@ -291,8 +284,8 @@ impl Lexer {
|
|||
let n1 = self.next_not_eof()?;
|
||||
let n2 = self.next_not_eof()?;
|
||||
buf.push(char::from_u32(
|
||||
n1.to_digit(16).ok_or(InvalidDigit(n1))? * 16 +
|
||||
n2.to_digit(16).ok_or(InvalidDigit(n2))?
|
||||
n1.to_digit(16).ok_or(error!("invalid digit '{n1}'"))? * 16 +
|
||||
n2.to_digit(16).ok_or(error!("invalid digit '{n2}'"))?
|
||||
).unwrap());
|
||||
},
|
||||
'u' => {
|
||||
|
@ -302,15 +295,15 @@ impl Lexer {
|
|||
let c = self.next_not_eof()?;
|
||||
if c == '}' { break }
|
||||
if n >= 0x1000_0000_u32 {
|
||||
return Err(InvalidCodepoint.into())
|
||||
return Err(error!("invalid utf8 codepoint '{n}'"))
|
||||
}
|
||||
n = n * 16 + c.to_digit(16).ok_or::<crate::Error>(InvalidDigit(c).into())?;
|
||||
n = n * 16 + c.to_digit(16).ok_or(error!("invalid digit '{c}'"))?;
|
||||
}
|
||||
let ch = char::from_u32(n).ok_or::<crate::Error>(InvalidCodepoint.into())?;
|
||||
let ch = char::from_u32(n).ok_or(error!("invalid codepoint '{n}'"))?;
|
||||
buf.push(ch);
|
||||
|
||||
},
|
||||
_ => return Err(InvalidStringEscape(next).into())
|
||||
_ => return Err(error!("invalid string escape '\\{next}'"))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -318,13 +311,12 @@ impl Lexer {
|
|||
}
|
||||
|
||||
fn lex_ident(&mut self, initial: char) -> Result<TokenData> {
|
||||
use Error as E;
|
||||
use TokenData as T;
|
||||
|
||||
let mut buf = std::string::String::new();
|
||||
|
||||
if !initial.is_initial_ident() {
|
||||
return Err(E::UnexpectedCharacter(initial).into())
|
||||
return Err(error!("unexpected character '{initial}'"))
|
||||
}
|
||||
|
||||
buf.push(initial);
|
||||
|
@ -342,6 +334,7 @@ impl Lexer {
|
|||
"else" => T::Else,
|
||||
"while" => T::While,
|
||||
"let" => T::Let,
|
||||
"const" => T::Const,
|
||||
"fn" | "function" => T::Function,
|
||||
"true" => T::True,
|
||||
"false" => T::False,
|
||||
|
@ -364,7 +357,6 @@ impl Lexer {
|
|||
|
||||
fn lex_radix(&mut self, radix: i64, radix_char: char) -> Result<TokenData> {
|
||||
use TokenData as T;
|
||||
use Error as E;
|
||||
|
||||
let mut n = 0i64;
|
||||
let mut char_found = false;
|
||||
|
@ -375,7 +367,7 @@ impl Lexer {
|
|||
n = n * radix + (i as i64);
|
||||
char_found = true;
|
||||
} else if self.peek().is_ident() {
|
||||
return Err(E::InvalidDigit(self.peek()).into())
|
||||
return Err(error!("invalid digit '{}'", self.peek()))
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
@ -384,13 +376,11 @@ impl Lexer {
|
|||
if char_found {
|
||||
Ok(T::Int(n))
|
||||
} else {
|
||||
Err(E::InvalidNumber(format!("0{radix_char}")).into())
|
||||
Err(error!("invalid number radix specifier '0{radix_char}'"))
|
||||
}
|
||||
}
|
||||
|
||||
fn lex_number(&mut self, initial: char) -> Result<TokenData> {
|
||||
use Error as E;
|
||||
|
||||
if initial == '0' {
|
||||
match self.peek() {
|
||||
'x' => {
|
||||
|
@ -459,7 +449,7 @@ impl Lexer {
|
|||
}
|
||||
|
||||
if self.peek().is_ident() {
|
||||
return Err(E::UnexpectedCharacter(self.peek()).into())
|
||||
return Err(error!("unexpected character '{}'", self.peek()))
|
||||
}
|
||||
|
||||
if let Ok(int) = buf.parse::<i64>() {
|
||||
|
@ -478,23 +468,11 @@ impl Lexer {
|
|||
return Ok(T::Float(float))
|
||||
}
|
||||
|
||||
Err(E::InvalidNumber(buf).into())
|
||||
Err(error!("invalid number '{buf}'"))
|
||||
}
|
||||
|
||||
fn peek_token_impl(&mut self, ignore_newlines: bool) -> Result<Token> {
|
||||
let idx = self.index;
|
||||
let pos = self.pos;
|
||||
let bidx = self.byte_len;
|
||||
let token = self.next_token_impl(ignore_newlines);
|
||||
self.index = idx;
|
||||
self.pos = pos;
|
||||
self.byte_len = bidx;
|
||||
token
|
||||
}
|
||||
|
||||
fn next_token_impl(&mut self, ignore_newlines: bool) -> Result<Token> {
|
||||
fn read_token(&mut self, ignore_newlines: bool) -> Result<Token> {
|
||||
use TokenData as T;
|
||||
use Error as E;
|
||||
|
||||
self.skip_whitespace(ignore_newlines);
|
||||
|
||||
|
@ -705,7 +683,7 @@ impl Lexer {
|
|||
self.next();
|
||||
T::Regex(regex::Regex::new(&self.lex_string(next)?)
|
||||
.map(|e| e.into())
|
||||
.map_err(|e| E::InvalidRegex(e.into()))?)
|
||||
.map_err(|e| error!("invalid regex: '{e}'"))?)
|
||||
}
|
||||
_ => {
|
||||
self.lex_ident(char)?
|
||||
|
@ -744,7 +722,6 @@ impl Lexer {
|
|||
let str_end = self.index;
|
||||
let byte_end = self.byte_len;
|
||||
let str = self.data[str_start..str_end].to_owned().into_iter().collect();
|
||||
|
||||
Ok(Token {
|
||||
data,
|
||||
pos,
|
||||
|
@ -754,19 +731,47 @@ impl Lexer {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn peek_token(&mut self) -> Result<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)
|
||||
let pos = self.pos;
|
||||
match self.read_token(true) {
|
||||
Ok(token) => Ok(token),
|
||||
Err(e) => Err(e.pos(pos)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_token_nl(&mut self) -> Result<Token> {
|
||||
self.next_token_impl(false)
|
||||
let pos = self.pos;
|
||||
match self.read_token(false) {
|
||||
Ok(token) => Ok(token),
|
||||
Err(e) => Err(e.pos(pos)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn peek_token(&mut self) -> Result<Token> {
|
||||
let idx = self.index;
|
||||
let pos = self.pos;
|
||||
let bidx = self.byte_len;
|
||||
let token = self.read_token(true);
|
||||
self.index = idx;
|
||||
self.pos = pos;
|
||||
self.byte_len = bidx;
|
||||
match token {
|
||||
Ok(token) => Ok(token),
|
||||
Err(e) => Err(e.pos(pos)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn peek_token_nl(&mut self) -> Result<Token> {
|
||||
let idx = self.index;
|
||||
let pos = self.pos;
|
||||
let bidx = self.byte_len;
|
||||
let token = self.read_token(false);
|
||||
self.index = idx;
|
||||
self.pos = pos;
|
||||
self.byte_len = bidx;
|
||||
match token {
|
||||
Ok(token) => Ok(token),
|
||||
Err(e) => Err(e.pos(pos)),
|
||||
}
|
||||
}
|
||||
}
|
42
matrix-lang/src/lib.rs
Normal file
42
matrix-lang/src/lib.rs
Normal file
|
@ -0,0 +1,42 @@
|
|||
mod compiler;
|
||||
mod value;
|
||||
mod lex;
|
||||
mod vm;
|
||||
mod parse;
|
||||
mod chunk;
|
||||
mod ast;
|
||||
mod binary;
|
||||
|
||||
pub mod prelude;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! iter {
|
||||
($fn:expr) => {
|
||||
$crate::prelude::Value::Iter(
|
||||
::std::rc::Rc::new(
|
||||
$crate::prelude::Function {
|
||||
name: ::std::rc::Rc::from("<iterator>"),
|
||||
arity: 0,
|
||||
variadic: false,
|
||||
fun: $crate::prelude::InnerFunction::Native(
|
||||
::std::rc::Rc::new($fn
|
||||
))}))
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! native {
|
||||
($name:expr, $arity:expr, $varadic:expr, $fn:expr) => {
|
||||
$crate::prelude::Value::Function(
|
||||
::std::rc::Rc::new( $crate::prelude::Function {
|
||||
name: std::rc::Rc::from($name),
|
||||
arity: $arity,
|
||||
variadic: $varadic,
|
||||
fun: $crate::prelude::InnerFunction::Native(
|
||||
::std::rc::Rc::new($fn)
|
||||
)
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,4 @@
|
|||
use std::{fmt::Display, rc::Rc};
|
||||
use num_complex::Complex64;
|
||||
use crate::{lex::{Lexer, self, Token, TokenData, Position}, ast::{Expr, BinaryOp, UnaryOp, optimize, ExprData, AstName}, value::{Value, self}, Result};
|
||||
use crate::prelude::*;
|
||||
|
||||
use Value as V;
|
||||
use ExprData as E;
|
||||
|
@ -33,38 +31,6 @@ pub struct Parser {
|
|||
optimize: bool
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
LexerError(crate::lex::Error),
|
||||
UnexpectedToken(Token),
|
||||
ExpectedToken(TokenData, Position),
|
||||
MatrixInvDomain(usize, usize, usize),
|
||||
NotAssignable(Expr),
|
||||
ValueError(value::Error),
|
||||
}
|
||||
|
||||
impl Display for Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
use Error::*;
|
||||
match self {
|
||||
LexerError(err) => write!(f, "{err}"),
|
||||
UnexpectedToken(tok) => write!(f, "Unexpected token: '{:?}' at {}:{}", tok.data, tok.pos.row, tok.pos.col),
|
||||
ExpectedToken(tok, pos) => write!(f, "Expected token: '{tok:?}' at {}:{}", pos.row, pos.col),
|
||||
MatrixInvDomain(row, should, was) => write!(f, "In row {row} of matrix, domain was expected to be {should} but was given {was}"),
|
||||
NotAssignable(expr) => write!(f, "{expr:?} is not assignable"),
|
||||
ValueError(err) => write!(f, "{err}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<lex::Error> for Error {
|
||||
fn from(value: lex::Error) -> Self {
|
||||
Self::LexerError(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {}
|
||||
|
||||
macro_rules! expr_parser {
|
||||
($parser:ident, $pattern:pat, $fn:ident) => {{
|
||||
let mut expr = $parser.$fn()?;
|
||||
|
@ -99,12 +65,18 @@ macro_rules! expr_parser_reverse {
|
|||
}};
|
||||
}
|
||||
|
||||
macro_rules! error {
|
||||
($($arg:tt)*) => {
|
||||
exception!(PARSE_EXCEPTION, $($arg)*)
|
||||
};
|
||||
}
|
||||
|
||||
impl Parser {
|
||||
|
||||
fn force_token(&mut self, tok: TokenData) -> Result<TokenData> {
|
||||
let next = self.lexer.next_token()?;
|
||||
if next.data != tok {
|
||||
Err(Error::ExpectedToken(tok, next.pos).into())
|
||||
Err(error!("expected token '{tok}'").pos(next.pos))
|
||||
} else {
|
||||
Ok(tok)
|
||||
}
|
||||
|
@ -113,7 +85,7 @@ impl Parser {
|
|||
fn force_token_nl(&mut self, tok: TokenData) -> Result<TokenData> {
|
||||
let next = self.lexer.next_token_nl()?;
|
||||
if next.data != tok {
|
||||
Err(Error::ExpectedToken(tok, next.pos).into())
|
||||
Err(error!("expected token '{tok}'").pos(next.pos))
|
||||
} else {
|
||||
Ok(tok)
|
||||
}
|
||||
|
@ -135,7 +107,7 @@ impl Parser {
|
|||
match next.data {
|
||||
T::Comma => continue,
|
||||
T::RightParen => break,
|
||||
_ => return Err(Error::UnexpectedToken(next).into())
|
||||
_ => return Err(error!("unexpected token '{next}'").pos(next.pos))
|
||||
};
|
||||
}
|
||||
Ok(params)
|
||||
|
@ -157,7 +129,7 @@ impl Parser {
|
|||
match next.data {
|
||||
T::SemiColon => continue,
|
||||
T::RightBrack => break,
|
||||
_ => return Err(Error::UnexpectedToken(next).into())
|
||||
_ => return Err(error!("unexpected token '{next}'").pos(next.pos))
|
||||
};
|
||||
}
|
||||
Ok(indicies)
|
||||
|
@ -193,7 +165,7 @@ impl Parser {
|
|||
match next.data {
|
||||
T::SemiColon => continue,
|
||||
T::RightBrack => break,
|
||||
_ => return Err(Error::UnexpectedToken(next).into()),
|
||||
_ => return Err(error!("unexpected token '{next}'").pos(next.pos))
|
||||
};
|
||||
}
|
||||
if parts.len() == 1 {
|
||||
|
@ -201,9 +173,9 @@ impl Parser {
|
|||
} else {
|
||||
let codomain = parts.len();
|
||||
let domain = parts[0].len();
|
||||
for (i, part) in parts.iter().enumerate() {
|
||||
for part in parts.iter() {
|
||||
if part.len() != domain {
|
||||
return Err(Error::MatrixInvDomain(i, domain, part.len()).into())
|
||||
return Err(error!("matrix row domains do not match: {} != {}", domain, part.len()).pos(pos))
|
||||
}
|
||||
}
|
||||
let mut data = Vec::new();
|
||||
|
@ -225,7 +197,7 @@ impl Parser {
|
|||
},
|
||||
T::Ident(ident) => (E::Literal(V::String(ident.to_string().into())), tok.pos).into(),
|
||||
T::String(string) => (E::Literal(V::String(string.to_string().into())), tok.pos).into(),
|
||||
_ => return Err(Error::UnexpectedToken(tok).into())
|
||||
t => return Err(error!("unexpected token '{t}'").pos(tok.pos))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -247,7 +219,7 @@ impl Parser {
|
|||
match next.data {
|
||||
T::Comma => continue,
|
||||
T::RightBrace => break,
|
||||
_ => return Err(Error::UnexpectedToken(next).into())
|
||||
_ => return Err(error!("unexpected token '{next}'").pos(next.pos))
|
||||
}
|
||||
}
|
||||
Ok((E::Table(table), pos).into())
|
||||
|
@ -273,7 +245,7 @@ impl Parser {
|
|||
}
|
||||
}
|
||||
T::LeftParen => (),
|
||||
_ => return Err(Error::UnexpectedToken(tok).into()),
|
||||
t => return Err(error!("unexpected token '{t}'").pos(tok.pos))
|
||||
}
|
||||
|
||||
let mut params = Vec::new();
|
||||
|
@ -295,7 +267,7 @@ impl Parser {
|
|||
}
|
||||
T::Comma => continue,
|
||||
T::RightParen => break,
|
||||
_ => return Err(Error::UnexpectedToken(next).into()),
|
||||
_ => return Err(error!("unexpected token '{next}'").pos(next.pos))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -307,7 +279,7 @@ impl Parser {
|
|||
if let T::Ident(ident) = next.data {
|
||||
Ok((ident, next.pos))
|
||||
} else {
|
||||
Err(Error::UnexpectedToken(next).into())
|
||||
Err(error!("unexpected token '{next}'").pos(next.pos))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -327,7 +299,7 @@ impl Parser {
|
|||
if let T::Ident(ident) = next.data {
|
||||
Ok((ident, next.pos))
|
||||
} else {
|
||||
Err(Error::UnexpectedToken(next).into())
|
||||
Err(error!("unexpected token '{next}'").pos(next.pos))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -419,6 +391,15 @@ impl Parser {
|
|||
}
|
||||
}
|
||||
|
||||
fn parse_const(&mut self) -> Result<Expr> {
|
||||
let pos = self.lexer.peek_token()?.pos;
|
||||
self.force_token(T::Const)?;
|
||||
let ident = self.parse_ident_nl()?;
|
||||
self.force_token(T::Assign)?;
|
||||
let expr = self.parse_expr()?;
|
||||
Ok((E::Const(ident, Box::new(expr)), pos).into())
|
||||
}
|
||||
|
||||
fn parse_return(&mut self) -> Result<Expr> {
|
||||
let pos = self.lexer.peek_token()?.pos;
|
||||
self.force_token(T::Return)?;
|
||||
|
@ -431,7 +412,10 @@ impl Parser {
|
|||
self.force_token(T::LeftBrace)?;
|
||||
loop {
|
||||
let expr = match self.lexer.peek_token()?.data {
|
||||
T::RightBrace => break,
|
||||
T::RightBrace => {
|
||||
self.lexer.next_token()?;
|
||||
break
|
||||
},
|
||||
T::SemiColon => {
|
||||
self.lexer.next_token()?;
|
||||
continue;
|
||||
|
@ -443,12 +427,9 @@ impl Parser {
|
|||
match next.data {
|
||||
T::SemiColon => continue,
|
||||
T::RightBrace => break,
|
||||
_ => return Err(Error::ExpectedToken(T::SemiColon, next.pos).into())
|
||||
_ => return Err(error!("expected a semicolon").pos(next.pos))
|
||||
}
|
||||
}
|
||||
if self.lexer.peek_token()?.data == T::RightBrace {
|
||||
self.lexer.next_token()?;
|
||||
}
|
||||
Ok((E::Block(block), pos).into())
|
||||
}
|
||||
|
||||
|
@ -474,7 +455,7 @@ impl Parser {
|
|||
T::True => E::Literal(V::Bool(true)),
|
||||
T::False => E::Literal(V::Bool(false)),
|
||||
T::Ident(ident) => E::Ident(ident),
|
||||
_ => return Err(Error::UnexpectedToken(tok).into()),
|
||||
t => return Err(error!("unexpected token '{t}'").pos(tok.pos))
|
||||
};
|
||||
Ok((data, tok.pos).into())
|
||||
}
|
||||
|
@ -488,6 +469,7 @@ impl Parser {
|
|||
While => self.parse_while(),
|
||||
For => self.parse_for(),
|
||||
Let => self.parse_let(),
|
||||
Const => self.parse_const(),
|
||||
LeftBrace => self.parse_block(),
|
||||
Return => self.parse_return(),
|
||||
If => self.parse_if(),
|
||||
|
@ -752,11 +734,11 @@ impl Parser {
|
|||
};
|
||||
let expr = self.parse_expr()?;
|
||||
block.push(expr);
|
||||
let next = self.lexer.next_token()?;
|
||||
let next = self.lexer.next_token_nl()?;
|
||||
match next.data {
|
||||
T::Eof => break,
|
||||
T::SemiColon => continue,
|
||||
_ => return Err(Error::ExpectedToken(T::SemiColon, next.pos).into())
|
||||
_ => return Err(error!("expected a semicolon").pos(next.pos))
|
||||
};
|
||||
}
|
||||
Ok((E::Block(block), Position::default()).into())
|
60
matrix-lang/src/prelude.rs
Normal file
60
matrix-lang/src/prelude.rs
Normal file
|
@ -0,0 +1,60 @@
|
|||
pub type Result<T> = std::result::Result<T, Exception>;
|
||||
|
||||
pub use crate::value::Value as Value;
|
||||
pub use crate::value::exception::Exception as Exception;
|
||||
pub use crate::value::matrix::Matrix as Matrix;
|
||||
pub use crate::value::gc::Gc as Gc;
|
||||
pub use crate::value::hash::ValueMap as ValueMap;
|
||||
pub use crate::value::function::Function as Function;
|
||||
pub use crate::value::function::InnerFunction as InnerFunction;
|
||||
|
||||
pub use num_complex::Complex64 as Complex64;
|
||||
pub use num_rational::Rational64 as Rational64;
|
||||
pub use regex::Regex as Regex;
|
||||
pub use std::fs::File as File;
|
||||
|
||||
pub use std::rc::Rc as Rc;
|
||||
pub use std::cell::RefCell as RefCell;
|
||||
|
||||
pub use crate::exception as exception;
|
||||
pub use crate::native as native;
|
||||
pub use crate::iter as iter;
|
||||
|
||||
pub use crate::value::exception::HASH_EXCEPTION as HASH_EXCEPTION;
|
||||
pub use crate::value::exception::VALUE_EXCEPTION as VALUE_EXCEPTION;
|
||||
pub use crate::value::exception::PARSE_EXCEPTION as PARSE_EXCEPTION;
|
||||
pub use crate::value::exception::COMPILE_EXCEPTION as COMPILE_EXCEPTION;
|
||||
pub use crate::value::exception::RUNTIME_EXCEPTION as RUNTIME_EXCEPTION;
|
||||
pub use crate::value::exception::BINARY_EXECPTION as BINARY_EXCEPTION;
|
||||
pub use crate::value::exception::IO_EXECPTION as IO_EXCEPTION;
|
||||
|
||||
pub use crate::value::clone::ValueClone as ValueClone;
|
||||
pub use crate::value::hash::TryHash as TryHash;
|
||||
|
||||
pub use crate::vm::Vm as Vm;
|
||||
pub use crate::vm::StackFrame as StackFrame;
|
||||
pub use crate::vm::Interupt as Interupt;
|
||||
|
||||
pub use crate::lex::Lexer as Lexer;
|
||||
pub use crate::lex::Position as Position;
|
||||
pub use crate::lex::Token as Token;
|
||||
pub use crate::lex::TokenData as TokenData;
|
||||
pub use crate::parse::Parser as Parser;
|
||||
pub use crate::parse::ParserBuilder as ParserBuilder;
|
||||
pub use crate::compiler::Compiler as Compiler;
|
||||
pub use crate::compiler::CompilerBuilder as CompilerBuilder;
|
||||
pub use crate::compiler::NamesTable as NamesTable;
|
||||
pub use crate::compiler::GlobalsTable as GlobalsTable;
|
||||
pub use crate::compiler::Global as Global;
|
||||
|
||||
pub use crate::ast::AstName as AstName;
|
||||
pub use crate::ast::Expr as Expr;
|
||||
pub use crate::ast::ExprData as ExprData;
|
||||
pub use crate::ast::BinaryOp as BinaryOp;
|
||||
pub use crate::ast::UnaryOp as UnaryOp;
|
||||
pub use crate::ast::optimize as optimize;
|
||||
|
||||
pub use crate::chunk::Chunk as Chunk;
|
||||
pub use crate::chunk::Instruction as Instruction;
|
||||
|
||||
pub use crate::binary::Program as Program;
|
54
matrix-lang/src/value/clone.rs
Normal file
54
matrix-lang/src/value/clone.rs
Normal file
|
@ -0,0 +1,54 @@
|
|||
use crate::prelude::*;
|
||||
|
||||
pub trait ValueClone {
|
||||
fn deep_clone(&self) -> Self;
|
||||
fn shallow_clone(&self) -> Self;
|
||||
}
|
||||
|
||||
impl ValueClone for Value {
|
||||
fn deep_clone(&self) -> Self {
|
||||
use Value as V;
|
||||
match self {
|
||||
V::List(l) => V::List(l.deep_clone()),
|
||||
V::Table(t) => V::Table(t.deep_clone()),
|
||||
V::Matrix(m) => V::Matrix(m.deep_clone()),
|
||||
_ => self.clone()
|
||||
}
|
||||
}
|
||||
|
||||
fn shallow_clone(&self) -> Self {
|
||||
use Value as V;
|
||||
match self {
|
||||
V::List(l) => V::List(l.shallow_clone()),
|
||||
V::Table(t) => V::Table(t.shallow_clone()),
|
||||
V::Matrix(m) => V::Matrix(m.shallow_clone()),
|
||||
_ => self.clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueClone for Vec<Value> {
|
||||
fn deep_clone(&self) -> Self {
|
||||
let mut vals = Vec::new();
|
||||
for val in self {
|
||||
vals.push(val.deep_clone())
|
||||
}
|
||||
vals
|
||||
}
|
||||
|
||||
fn shallow_clone(&self) -> Self {
|
||||
self.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueClone for Matrix {
|
||||
fn deep_clone(&self) -> Self {
|
||||
let values = self.values.deep_clone();
|
||||
Self::new(self.domain, self.codomain, values)
|
||||
}
|
||||
|
||||
fn shallow_clone(&self) -> Self {
|
||||
let values = self.values.shallow_clone();
|
||||
Self::new(self.domain, self.codomain, values)
|
||||
}
|
||||
}
|
373
matrix-lang/src/value/comp.rs
Normal file
373
matrix-lang/src/value/comp.rs
Normal file
|
@ -0,0 +1,373 @@
|
|||
use std::{ops::{Add, Sub, Mul, Div, Shl, Shr, BitOr, BitAnd, BitXor, Neg, Not}, cmp::Ordering};
|
||||
use crate::prelude::*;
|
||||
|
||||
fn ratio_to_f64(r: Rational64) -> f64 {
|
||||
*r.numer() as f64 / *r.denom() as f64
|
||||
}
|
||||
|
||||
macro_rules! error {
|
||||
($($arg:tt)*) => {
|
||||
exception!(VALUE_EXCEPTION, $($arg)*)
|
||||
};
|
||||
}
|
||||
|
||||
///
|
||||
/// MATH OPERATIONS
|
||||
///
|
||||
|
||||
fn ipow(n: i64, d: i64, p: i64) -> Result<(i64, i64)> {
|
||||
Ok(match (n, d, p) {
|
||||
(0, _, 0) => Err(error!("cannot exponent 0 ** 0"))?,
|
||||
(0, _, _) => (0, 1),
|
||||
(_, _, 0) => (1, 1),
|
||||
(1, 1, _) => (1, 1),
|
||||
(n, d, p) if p < 0 => (d.pow((-p) as u32), n.pow((-p) as u32)),
|
||||
(n, d, p) => (n.pow(p as u32), d.pow(p as u32)),
|
||||
})
|
||||
}
|
||||
|
||||
fn promote(a: Value, b: Value) -> (Value, Value) {
|
||||
use Value as V;
|
||||
match (&a, &b) {
|
||||
(V::Int(x), V::Ratio(..)) => (V::Ratio((*x).into()), b),
|
||||
(V::Int(x), V::Float(..)) => (V::Float(*x as f64), b),
|
||||
(V::Int(x), V::Complex(..)) => (V::Complex((*x as f64).into()), b),
|
||||
(V::Ratio(x), V::Float(..)) => (V::Float(ratio_to_f64(*x)), b),
|
||||
(V::Ratio(x), V::Complex(..)) => (V::Complex(ratio_to_f64(*x).into()), b),
|
||||
(V::Float(x), V::Complex(..)) => (V::Complex((*x).into()), b),
|
||||
(V::Ratio(..), V::Int(y)) => (a, V::Ratio((*y).into())),
|
||||
(V::Float(..), V::Int(y)) => (a, V::Float(*y as f64)),
|
||||
(V::Complex(..), V::Int(y)) => (a, V::Complex((*y as f64).into())),
|
||||
(V::Float(..), V::Ratio(y)) => (a, V::Float(ratio_to_f64(*y))),
|
||||
(V::Complex(..), V::Ratio(y)) => (a, V::Complex(ratio_to_f64(*y).into())),
|
||||
(V::Complex(..), V::Float(y)) => (a, V::Complex((*y).into())),
|
||||
(V::List(l1), V::List(l2)) if l1.len() > 0 && l2.len() > 0
|
||||
=> (V::Matrix(Matrix::from_list(l1.to_vec()).into()), V::Matrix(Matrix::from_list(l2.to_vec()).into())),
|
||||
(_, V::List(l)) if l.len() > 0
|
||||
=> (a, V::Matrix(Matrix::from_list(l.to_vec()).into())),
|
||||
(V::List(l), _) if l.len() > 0
|
||||
=> (V::Matrix(Matrix::from_list(l.to_vec()).into()), b),
|
||||
_ => (a, b),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Add for Value {
|
||||
type Output = Result<Self>;
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
use Value::*;
|
||||
match promote(self, rhs) {
|
||||
(Int(x), Int(y)) => Ok(Int(x + y)),
|
||||
(Float(x), Float(y)) => Ok(Float(x + y)),
|
||||
(Ratio(x), Ratio(y)) => Ok(Ratio(x + y)),
|
||||
(Complex(x), Complex(y)) => Ok(Complex(x + y)),
|
||||
(Matrix(x), Matrix(y)) => Ok(Matrix((x + y)?.into())),
|
||||
(Matrix(x), r) => Ok(Matrix(x.increment(r)?.into())),
|
||||
(l, Matrix(y)) => Ok(Matrix(y.increment(l)?.into())),
|
||||
(String(str), value) => Ok(String(Rc::from(
|
||||
format!("{str}{value}")
|
||||
))),
|
||||
(value, String(str)) => Ok(String(Rc::from(
|
||||
format!("{value}{str}")
|
||||
))),
|
||||
(List(mut l1), List(l2)) => {
|
||||
l1.extend_from_slice(&l2);
|
||||
Ok(List(l1))
|
||||
},
|
||||
(l, r) => Err(error!("cannot add {l:?} + {r:?}"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for Value {
|
||||
type Output = Result<Self>;
|
||||
fn sub(self, rhs: Self) -> Self::Output {
|
||||
use Value::*;
|
||||
match promote(self, rhs) {
|
||||
(Int(x), Int(y)) => Ok(Int(x - y)),
|
||||
(Float(x), Float(y)) => Ok(Float(x - y)),
|
||||
(Ratio(x), Ratio(y)) => Ok(Ratio(x - y)),
|
||||
(Complex(x), Complex(y)) => Ok(Complex(x - y)),
|
||||
(Matrix(x), Matrix(y)) => Ok(Matrix((x - y)?.into())),
|
||||
(Matrix(x), r) => Ok(Matrix(x.decrement(r)?.into())),
|
||||
(l, r) => Err(error!("cannot subtract {l:?} - {r:?}"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul for Value {
|
||||
type Output = Result<Self>;
|
||||
fn mul(self, rhs: Value) -> Self::Output {
|
||||
use Value::*;
|
||||
match promote(self, rhs) {
|
||||
(Int(x), Int(y)) => Ok(Int(x * y)),
|
||||
(Float(x), Float(y)) => Ok(Float(x * y)),
|
||||
(Ratio(x), Ratio(y)) => Ok(Ratio(x * y)),
|
||||
(Complex(x), Complex(y)) => Ok(Complex(x * y)),
|
||||
(Matrix(x), Matrix(y)) => Ok(Matrix((x * y)?.into())),
|
||||
(Matrix(x), r) => Ok(Matrix(x.scale(r)?.into())),
|
||||
(l, Matrix(y)) => Ok(Matrix(y.scale(l)?.into())),
|
||||
(l, r) => Err(error!("cannot multiply {l:?} * {r:?}"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Div for Value {
|
||||
type Output = Result<Self>;
|
||||
fn div(self, rhs: Value) -> Self::Output {
|
||||
use Value::*;
|
||||
match promote(self, rhs) {
|
||||
(Int(_), Int(0)) => Err(error!("cannot divide by zero")),
|
||||
(Int(x), Int(y)) => Ok(Ratio(Rational64::new(x, y))),
|
||||
(Float(x), Float(y)) => Ok(Float(x / y)),
|
||||
(Ratio(x), Ratio(y)) => Ok(Ratio(x / y)),
|
||||
(Complex(x), Complex(y)) => Ok(Complex(x / y)),
|
||||
(l, r) => Err(error!("cannot divide {l:?} / {r:?}"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BitOr for Value {
|
||||
type Output = Result<Self>;
|
||||
fn bitor(self, rhs: Value) -> Self::Output {
|
||||
use Value::*;
|
||||
match promote(self, rhs) {
|
||||
(Int(x), Int(y)) => Ok(Int(x | y)),
|
||||
(l, r) => Err(error!("cannot bitwise or {l:?} | {r:?}"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BitAnd for Value {
|
||||
type Output = Result<Self>;
|
||||
fn bitand(self, rhs: Value) -> Self::Output {
|
||||
use Value::*;
|
||||
match promote(self, rhs) {
|
||||
(Int(x), Int(y)) => Ok(Int(x & y)),
|
||||
(l, r) => Err(error!("cannot bitwise and {l:?} & {r:?}"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BitXor for Value {
|
||||
type Output = Result<Self>;
|
||||
fn bitxor(self, rhs: Value) -> Self::Output {
|
||||
use Value::*;
|
||||
match promote(self, rhs) {
|
||||
(Int(x), Int(y)) => Ok(Int(x ^ y)),
|
||||
(l, r) => Err(error!("cannot bitwise xor {l:?} ^ {r:?}"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Shl for Value {
|
||||
type Output = Result<Self>;
|
||||
fn shl(self, rhs: Value) -> Self::Output {
|
||||
use Value::*;
|
||||
match promote(self, rhs) {
|
||||
(Int(x), Int(y)) => Ok(Int(x << y)),
|
||||
(l, r) => Err(error!("cannot bitwise shift left {l:?} << {r:?}"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Shr for Value {
|
||||
type Output = Result<Self>;
|
||||
fn shr(self, rhs: Value) -> Self::Output {
|
||||
use Value::*;
|
||||
match promote(self, rhs) {
|
||||
(Int(x), Int(y)) => Ok(Int(x >> y)),
|
||||
(l, r) => Err(error!("cannot bitwise shift right {l:?} >> {r:?}"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Value {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
use Value::*;
|
||||
match (self, other) {
|
||||
(Nil, Nil) => true,
|
||||
(Bool(a), Bool(b)) => a == b,
|
||||
(Int(a), Int(b)) => *a == *b,
|
||||
(Ratio(a), Ratio(b)) => *a == *b,
|
||||
(Float(a), Float(b)) => *a == *b,
|
||||
(Complex(a), Complex(b)) => *a == *b,
|
||||
(Int(a), Ratio(b)) => Rational64::from(*a) == *b,
|
||||
(Ratio(a), Int(b)) => *a == Rational64::from(*b),
|
||||
(Int(a), Float(b)) => *a as f64 == *b,
|
||||
(Float(a), Int(b)) => *a == *b as f64,
|
||||
(Int(a), Complex(b)) => Complex64::from(*a as f64) == *b,
|
||||
(Complex(a), Int(b)) => *a == Complex64::from(*b as f64),
|
||||
(Ratio(a), Float(b)) => ratio_to_f64(*a) == *b,
|
||||
(Float(a), Ratio(b)) => *a == ratio_to_f64(*b),
|
||||
(Ratio(a), Complex(b)) => Complex64::from(ratio_to_f64(*a)) == *b,
|
||||
(Complex(a), Ratio(b)) => *a == Complex64::from(ratio_to_f64(*b)),
|
||||
(Float(a), Complex(b)) => Complex64::from(*a) == *b,
|
||||
(Complex(a), Float(b)) => *a == Complex64::from(*b),
|
||||
(String(a), String(b)) => *a == *b,
|
||||
(List(a), List(b)) => *a == *b,
|
||||
(Matrix(a), Matrix(b)) => a == b,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Value {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
use Value::*;
|
||||
match (self, other) {
|
||||
(Nil, Nil) => Some(Ordering::Equal),
|
||||
(Bool(a), Bool(b)) => a.partial_cmp(b),
|
||||
(Int(a), Int(b)) => a.partial_cmp(b),
|
||||
(Ratio(a), Ratio(b)) => a.partial_cmp(b),
|
||||
(Float(a), Float(b)) => a.partial_cmp(b),
|
||||
(Int(a), Ratio(b)) => Rational64::from(*a).partial_cmp(b),
|
||||
(Ratio(a), Int(b)) => a.partial_cmp(&Rational64::from(*b)),
|
||||
(Int(a), Float(b)) => (*a as f64).partial_cmp(b),
|
||||
(Float(a), Int(b)) => a.partial_cmp(&(*b as f64)),
|
||||
(Ratio(a), Float(b)) => ratio_to_f64(*a).partial_cmp(b),
|
||||
(Float(a), Ratio(b)) => a.partial_cmp(&ratio_to_f64(*b)),
|
||||
(String(a), String(b)) => a.partial_cmp(b),
|
||||
(List(a), List(b)) => a.partial_cmp(b),
|
||||
(Matrix(a), Matrix(b)) => a.values.partial_cmp(&b.values),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for Value {
|
||||
type Output = Value;
|
||||
|
||||
fn neg(self) -> Self::Output {
|
||||
use Value::*;
|
||||
match self {
|
||||
Bool(b) => Bool(!b),
|
||||
Int(i) => Int(-i),
|
||||
Float(f) => Float(-f),
|
||||
Ratio(r) => Ratio(-r),
|
||||
Complex(c) => Complex(-c),
|
||||
_ => return Float(f64::NAN)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Not for Value {
|
||||
type Output = bool;
|
||||
|
||||
fn not(self) -> Self::Output {
|
||||
use Value as V;
|
||||
match self {
|
||||
V::Nil => true,
|
||||
V::Bool(b) => !b,
|
||||
V::Int(i) => i == 0,
|
||||
V::Float(f) => f == 0.0,
|
||||
V::Ratio(r) => *(r.numer()) == 0 || *(r.denom()) == 0,
|
||||
V::Complex(c) => !c.is_normal(),
|
||||
V::Regex(_) => false,
|
||||
V::List(_) => false,
|
||||
V::Matrix(_) => false,
|
||||
V::Table(_) => false,
|
||||
V::String(s) => s.as_ref() == "",
|
||||
V::Function(_) => false,
|
||||
V::Iter(_) => false,
|
||||
V::Range(_) => false,
|
||||
V::File(_) => false,
|
||||
V::Exception(_) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Value {
|
||||
|
||||
pub fn modulo(self, rhs: Value) -> Result<Self> {
|
||||
use Value::*;
|
||||
match promote(self, rhs) {
|
||||
(Int(x), Int(y)) => Ok(Int(x % y)),
|
||||
(Float(x), Float(y)) => Ok(Float(x % y)),
|
||||
(Ratio(x), Ratio(y)) => Ok(Ratio(x % y)),
|
||||
(Complex(x), Complex(y)) => Ok(Complex(x % y)),
|
||||
(l, r) => Err(error!("cannot modulo: {l:?} % {r:?}"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pow(self, rhs: Value) -> Result<Self> {
|
||||
use Value::*;
|
||||
if let (Ratio(x), Int(y)) = (&self, &rhs) {
|
||||
return Ok(Ratio(ipow(*(*x).numer(), *(*x).denom(), *y)?.into()));
|
||||
}
|
||||
match promote(self, rhs) {
|
||||
(Int(x), Int(y)) => Ok(Ratio(ipow(x, 1, y)?.into())),
|
||||
(Float(x), Float(y)) => Ok(Float(x.powf(y))),
|
||||
(Ratio(x), Ratio(y)) => Ok(Float(ratio_to_f64(x).powf(ratio_to_f64(y)))),
|
||||
(Complex(x), Complex(y)) => Ok(Complex(x.powc(y))),
|
||||
(l, r) => Err(error!("cannot exponent: {l:?} ** {r:?}"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn floaty(self) -> Self {
|
||||
use Value as V;
|
||||
match self {
|
||||
V::Int(i) => V::Float(i as f64),
|
||||
V::Ratio(r) => V::Float(ratio_to_f64(r)),
|
||||
a => a
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_zero(&self) -> bool {
|
||||
use Value as V;
|
||||
match self {
|
||||
V::Int(i) => *i == 0,
|
||||
V::Float(f) => *f == 0.0 || *f == -0.0,
|
||||
V::Ratio(r) => *r.numer() == 0,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn binary_op(op: BinaryOp, lhs: Value, rhs: Value) -> Result<Self> {
|
||||
use BinaryOp::*;
|
||||
match op {
|
||||
Add => lhs + rhs,
|
||||
Subtract => lhs - rhs,
|
||||
Multiply => lhs * rhs,
|
||||
Divide => lhs / rhs,
|
||||
Modulo => lhs.modulo(rhs),
|
||||
Power => lhs.pow(rhs),
|
||||
BitwiseAnd => lhs & rhs,
|
||||
BitwiseOr => lhs | rhs,
|
||||
BitwiseXor => lhs ^ rhs,
|
||||
BitwiseShiftLeft => lhs << rhs,
|
||||
BitwiseShiftRight => lhs >> rhs,
|
||||
Equals => Ok(Self::Bool(lhs == rhs)),
|
||||
NotEquals => Ok(Self::Bool(lhs != rhs)),
|
||||
GreaterEquals => Ok(Self::Bool(lhs >= rhs)),
|
||||
LessEquals => Ok(Self::Bool(lhs <= rhs)),
|
||||
GreaterThan => Ok(Self::Bool(lhs > rhs)),
|
||||
LessThan => Ok(Self::Bool(lhs < rhs)),
|
||||
Range | RangeEq => {
|
||||
let Value::Int(lhs) = lhs else {
|
||||
return Err(error!("range can only take [Int]'s"))
|
||||
};
|
||||
let Value::Int(rhs) = rhs else {
|
||||
return Err(error!("range can only take [Int]'s"))
|
||||
};
|
||||
Ok(Self::Range(Rc::new((lhs, rhs, op == RangeEq))))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unary_op(op: UnaryOp, val: Value) -> Value {
|
||||
use UnaryOp::*;
|
||||
match op {
|
||||
Negate => -val,
|
||||
Not => Self::Bool(!val),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_regex(value: &str) -> Result<Self> {
|
||||
match Regex::new(value) {
|
||||
Ok(r) => Ok(Self::Regex(r.into())),
|
||||
Err(e) => Err(error!("{e}")),
|
||||
}
|
||||
}
|
||||
}
|
78
matrix-lang/src/value/exception.rs
Normal file
78
matrix-lang/src/value/exception.rs
Normal file
|
@ -0,0 +1,78 @@
|
|||
use std::{fmt::{Debug, Display}, error::Error};
|
||||
use crate::prelude::*;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! exception {
|
||||
($type:expr) => {
|
||||
$crate::prelude::Exception::new($type)
|
||||
};
|
||||
($type:expr, $($arg:tt)*) => {
|
||||
$crate::prelude::Exception::msg($type, format!($($arg)*).as_str())
|
||||
};
|
||||
}
|
||||
|
||||
pub const HASH_EXCEPTION: &'static str = "hash";
|
||||
pub const VALUE_EXCEPTION: &'static str = "value";
|
||||
pub const PARSE_EXCEPTION: &'static str = "parse";
|
||||
pub const COMPILE_EXCEPTION: &'static str = "compile";
|
||||
pub const RUNTIME_EXCEPTION: &'static str = "runtime";
|
||||
pub const BINARY_EXECPTION: &'static str = "binary";
|
||||
pub const IO_EXECPTION: &'static str = "io";
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Exception(Gc<ExceptionInner>);
|
||||
|
||||
#[derive(Clone)]
|
||||
struct ExceptionInner {
|
||||
ty: Rc<str>,
|
||||
msg: Rc<str>,
|
||||
trace: Vec<(Rc<str>, Position)>
|
||||
}
|
||||
|
||||
impl Exception {
|
||||
pub fn msg(ty: &str, msg: &str) -> Self {
|
||||
Self(Gc::new(ExceptionInner { ty: ty.into(), msg: msg.into(), trace: Vec::new() }))
|
||||
}
|
||||
|
||||
pub fn pos(mut self, pos: Position) -> Self {
|
||||
self.0.trace.push((
|
||||
Rc::from("<root>"),
|
||||
pos
|
||||
));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn trace(mut self, block: Rc<str>, pos: Position) -> Self {
|
||||
self.0.trace.push((
|
||||
block,
|
||||
pos
|
||||
));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Exception {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let ty = self.0.ty.as_ref();
|
||||
let msg = self.0.msg.as_ref();
|
||||
write!(f, "{}\n Type <{}>", msg, ty)?;
|
||||
for (block, pos) in self.0.trace.iter() {
|
||||
write!(f, "\n In {block} at {pos}\n")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Exception {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "[Exception {}]", self.0.ty.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for Exception {}
|
||||
|
||||
impl<T> From<Exception> for Result<T> {
|
||||
fn from(value: Exception) -> Self {
|
||||
Err(value)
|
||||
}
|
||||
}
|
186
matrix-lang/src/value/fmt.rs
Normal file
186
matrix-lang/src/value/fmt.rs
Normal file
|
@ -0,0 +1,186 @@
|
|||
use std::fmt::{Debug, Display};
|
||||
use crate::prelude::*;
|
||||
|
||||
use Value as V;
|
||||
|
||||
impl Debug for Value {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
V::Nil => write!(f, "[Nil]"),
|
||||
V::Int(i) => write!(f, "[Int {i}]"),
|
||||
V::Bool(b) => write!(f, "[Bool {b}]"),
|
||||
V::Float(vf) => write!(f, "[Float {vf}]"),
|
||||
V::Ratio(r) => write!(f, "[Ratio {r}]"),
|
||||
V::Complex(c) => write!(f, "[Complex {c}]"),
|
||||
V::Regex(r) => write!(f, "[Regex /{r}/]"),
|
||||
V::String(s) => write!(f, "[String '{s}']"),
|
||||
V::List(l) => write!(f, "[List {}]", l.len()),
|
||||
V::Matrix(m) => write!(f, "[Matirx {}x{}]", m.domain, m.codomain),
|
||||
V::Table(t) => write!(f, "[Table {}]", t.len()),
|
||||
V::Function(vf) => write!(f, "[Function {}]", vf.name),
|
||||
V::Range(r) => write!(f, "[Range {:?}..{:?}]", r.0, r.1),
|
||||
V::Iter(_) => write!(f, "[Iterator]"),
|
||||
V::Exception(_) => write!(f, "[Error]"),
|
||||
V::File(_) => write!(f, "[File]"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Value {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let red;
|
||||
let green;
|
||||
let yellow;
|
||||
let blue;
|
||||
let pink;
|
||||
let cyan;
|
||||
let clear;
|
||||
|
||||
if f.alternate() {
|
||||
red = "\x1b[31m";
|
||||
green = "\x1b[32m";
|
||||
yellow = "\x1b[33m";
|
||||
blue = "\x1b[34m";
|
||||
pink = "\x1b[35m";
|
||||
cyan = "\x1b[36m";
|
||||
clear = "\x1b[0m";
|
||||
} else {
|
||||
red = "";
|
||||
green = "";
|
||||
yellow = "";
|
||||
blue = "";
|
||||
pink = "";
|
||||
cyan = "";
|
||||
clear = "";
|
||||
}
|
||||
|
||||
match self {
|
||||
V::Nil => {write!(f, "{blue}nil{clear}")?;},
|
||||
V::Bool(b) => {write!(f, "{yellow}{b}{clear}")?;},
|
||||
V::Int(i) => {write!(f, "{yellow}{i}{clear}")?;},
|
||||
V::Float(l) => {write!(f, "{yellow}{l}{clear}")?;},
|
||||
V::Ratio(r) => {write!(f, "{yellow}{r}{clear}")?;},
|
||||
V::Complex(c) => {write!(f, "{yellow}{c}{clear}")?;},
|
||||
V::Regex(r) => {write!(f, "/{red}{r}{clear}/")?;},
|
||||
V::String(s) => {
|
||||
if f.alternate() {
|
||||
write!(f, "{green}'{s}'{clear}")?;
|
||||
} else {
|
||||
write!(f, "{s}")?;
|
||||
}
|
||||
}
|
||||
V::List(l) => {
|
||||
if l.len() < 1 {
|
||||
write!(f, "[]")?;
|
||||
return Ok(())
|
||||
}
|
||||
write!(f, "[ ")?;
|
||||
for (i, el) in l.iter().enumerate() {
|
||||
if i != 0 {
|
||||
write!(f, " ")?;
|
||||
}
|
||||
if f.alternate() {
|
||||
write!(f, "{el:#}")?;
|
||||
} else {
|
||||
write!(f, "{el}")?;
|
||||
}
|
||||
}
|
||||
write!(f, " ]")?;
|
||||
},
|
||||
V::Matrix(m) => {
|
||||
if f.alternate() {
|
||||
write!(f, "{m:#}")?;
|
||||
} else {
|
||||
write!(f, "{m}")?;
|
||||
}
|
||||
},
|
||||
V::Table(t) => {
|
||||
if f.alternate() {
|
||||
write!(f, "{t:#}")?;
|
||||
} else {
|
||||
write!(f, "{t}")?;
|
||||
}
|
||||
},
|
||||
V::Function(fun) => {
|
||||
write!(f, "{cyan}{fun}{clear}")?;
|
||||
}
|
||||
V::Range(r) => {
|
||||
if f.alternate() {
|
||||
write!(f, "{:#}..{:#}", r.0, r.1)?;
|
||||
} else {
|
||||
write!(f, "{}..{}", r.0, r.1)?;
|
||||
}
|
||||
}
|
||||
V::Iter(_) => {write!(f, "{pink}[Iterator]{clear}")?;},
|
||||
V::File(_) => {write!(f, "{pink}[File]{clear}")?;},
|
||||
V::Exception(e) => {write!(f, "{red}{e}{clear}")?;},
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Matrix {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "[Matrix {}x{}]", self.domain, self.codomain)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Matrix {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let mut max_cols = vec![0; self.domain];
|
||||
let mut vals: Vec<String> = Vec::with_capacity(self.domain * self.codomain);
|
||||
for row in 0..self.codomain {
|
||||
for col in 0..self.domain {
|
||||
let idx = col + row * self.domain;
|
||||
let el = &self.values[idx];
|
||||
let s = match f.alternate() {
|
||||
true => format!("{:#}", el),
|
||||
false => format!("{}", el)
|
||||
};
|
||||
max_cols[col] = max_cols[col].max(s.len());
|
||||
vals.push(s);
|
||||
}
|
||||
}
|
||||
|
||||
write!(f, "\n")?;
|
||||
for row in 0..self.codomain {
|
||||
for col in 0..self.domain {
|
||||
let idx = col + row * self.domain;
|
||||
let s = vals[idx].as_str();
|
||||
let width = max_cols[col];
|
||||
write!(f, " {s:>width$}")?;
|
||||
}
|
||||
write!(f, "\n")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for ValueMap {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "[Table {}]", self.len())
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ValueMap {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
if self.len() < 1 {
|
||||
write!(f, "{{}}")?;
|
||||
return Ok(())
|
||||
}
|
||||
write!(f, "{{ ")?;
|
||||
for (i, (key, value)) in self.entries().enumerate() {
|
||||
if i != 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
if f.alternate() {
|
||||
write!(f, "{key:#} = {value:#}")?
|
||||
} else {
|
||||
write!(f, "{key} = {value}")?
|
||||
}
|
||||
}
|
||||
write!(f, " }}")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
43
matrix-lang/src/value/function.rs
Normal file
43
matrix-lang/src/value/function.rs
Normal file
|
@ -0,0 +1,43 @@
|
|||
use crate::prelude::*;
|
||||
use std::fmt::{Debug, Display};
|
||||
|
||||
pub struct Function {
|
||||
pub name: Rc<str>,
|
||||
pub arity: usize,
|
||||
pub variadic: bool,
|
||||
pub fun: InnerFunction
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum InnerFunction {
|
||||
Compiled(Rc<Chunk>),
|
||||
Native(Rc<dyn Fn((&mut Vm, &mut StackFrame), Vec<Value>) -> Result<Value>>),
|
||||
}
|
||||
|
||||
impl Debug for Function {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
use InnerFunction as F;
|
||||
match self.fun {
|
||||
F::Compiled(_) => {
|
||||
write!(f, "[Function {}]", self.name)
|
||||
},
|
||||
F::Native(_) => {
|
||||
write!(f, "[Builtin {}]", self.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Function {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
use InnerFunction as F;
|
||||
match self.fun {
|
||||
F::Compiled(_) => {
|
||||
write!(f, "[Function {}]", self.name)
|
||||
},
|
||||
F::Native(_) => {
|
||||
write!(f, "[Builtin {}]", self.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
use std::{ops::{Index, IndexMut, Deref, DerefMut, Add, Sub, Mul}, marker::PhantomData, ptr::NonNull, fmt::{Debug, Display}};
|
||||
use crate::prelude::*;
|
||||
|
||||
pub struct Gc<T> {
|
||||
ptr: NonNull<GcInner<T>>,
|
||||
|
@ -24,13 +25,6 @@ impl<T> Gc<T> {
|
|||
}
|
||||
|
||||
impl <T: Clone> Gc<T> {
|
||||
pub fn clone_inside(&self) -> Self {
|
||||
unsafe {
|
||||
let data = self.ptr.as_ref().data.clone();
|
||||
Self::new(data)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_inner(self) -> T {
|
||||
unsafe {
|
||||
self.ptr.as_ref().data.clone()
|
||||
|
@ -44,6 +38,22 @@ impl <T: Clone> Gc<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl <T: ValueClone> ValueClone for Gc<T> {
|
||||
fn deep_clone(&self) -> Self {
|
||||
unsafe {
|
||||
let data = self.ptr.as_ref().data.deep_clone();
|
||||
Self::new(data)
|
||||
}
|
||||
}
|
||||
|
||||
fn shallow_clone(&self) -> Self {
|
||||
unsafe {
|
||||
let data = self.ptr.as_ref().data.shallow_clone();
|
||||
Self::new(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<T> for Gc<T> {
|
||||
fn from(value: T) -> Self {
|
||||
Gc::new(value)
|
240
matrix-lang/src/value/hash.rs
Normal file
240
matrix-lang/src/value/hash.rs
Normal file
|
@ -0,0 +1,240 @@
|
|||
use crate::prelude::*;
|
||||
use std::hash::{Hash, DefaultHasher, Hasher};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ValueMap {
|
||||
values: Vec<ValueEntry>,
|
||||
size: usize,
|
||||
used: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
enum ValueEntry {
|
||||
Empty,
|
||||
Taken((Value, Value)),
|
||||
Gravestone(Value),
|
||||
}
|
||||
|
||||
impl Default for ValueMap {
|
||||
fn default() -> Self {
|
||||
ValueMap {
|
||||
values: vec![ValueEntry::Empty; 8],
|
||||
size: 8,
|
||||
used: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueMap {
|
||||
|
||||
fn get_index(values: &Vec<ValueEntry>, size: usize, key: &Value, hash: usize) -> usize {
|
||||
let mut nearest_grave = None;
|
||||
let start = hash & size;
|
||||
for offset in 0..size {
|
||||
let idx = (start + offset) % size;
|
||||
match &values[idx] {
|
||||
ValueEntry::Empty => return idx,
|
||||
ValueEntry::Taken((marker, _)) => {
|
||||
if marker.eq(key) { return idx };
|
||||
continue;
|
||||
},
|
||||
ValueEntry::Gravestone(grave) => {
|
||||
if grave.eq(key) { return idx };
|
||||
if nearest_grave.is_none() {
|
||||
nearest_grave = Some(idx);
|
||||
}
|
||||
continue;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
match nearest_grave {
|
||||
Some(idx) => idx,
|
||||
None => panic!("cannot get value map index: full!!!"),
|
||||
}
|
||||
}
|
||||
|
||||
fn expand(&mut self) -> Result<()> {
|
||||
let pairs = self.values.iter()
|
||||
.filter_map(|e| {
|
||||
match e {
|
||||
ValueEntry::Taken(s) => Some(s.clone()),
|
||||
_ => None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<(Value, Value)>>();
|
||||
|
||||
let new_used = pairs.len();
|
||||
let new_size = self.size * 2 + pairs.len();
|
||||
let mut new_values = vec![ValueEntry::Empty; new_size];
|
||||
|
||||
for (key, value) in pairs.into_iter() {
|
||||
let hash = self.hash(&key)?;
|
||||
let idx = ValueMap::get_index(&new_values, new_size, &key, hash);
|
||||
new_values[idx] = ValueEntry::Taken((key, value));
|
||||
}
|
||||
|
||||
self.used = new_used;
|
||||
self.size = new_size;
|
||||
self.values = new_values;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn hash(&self, key: &Value) -> Result<usize> {
|
||||
let mut hasher = DefaultHasher::new();
|
||||
key.try_hash(&mut hasher)?;
|
||||
Ok(hasher.finish() as usize)
|
||||
}
|
||||
|
||||
pub fn get<'a>(&'a self, key: &Value) -> Result<Option<&'a Value>> {
|
||||
let hash = self.hash(key)?;
|
||||
let idx = ValueMap::get_index(&self.values, self.size, key, hash);
|
||||
match &self.values[idx] {
|
||||
ValueEntry::Taken((_, value)) => Ok(Some(value)),
|
||||
_ => Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, key: Value, value: Value) -> Result<()> {
|
||||
if self.used * 3 >= self.size * 2 {
|
||||
self.expand()?;
|
||||
}
|
||||
let key = key.deep_clone();
|
||||
let hash = self.hash(&key)?;
|
||||
let idx = ValueMap::get_index(&self.values, self.size, &key, hash);
|
||||
self.values[idx] = ValueEntry::Taken((key, value));
|
||||
self.used += 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, key: &Value) -> Result<Option<Value>> {
|
||||
let hash = self.hash(key)?;
|
||||
let idx = ValueMap::get_index(&self.values, self.size, key, hash);
|
||||
let mut value = ValueEntry::Gravestone(key.clone());
|
||||
std::mem::swap(&mut value, &mut self.values[idx]);
|
||||
match value {
|
||||
ValueEntry::Taken((_, v)) => {
|
||||
self.used -= 1;
|
||||
Ok(Some(v))
|
||||
}
|
||||
_ => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn entries<'a>(&'a self) -> ValueMapIterator<'a> {
|
||||
ValueMapIterator::new(self)
|
||||
}
|
||||
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.used
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ValueMapIterator<'a> {
|
||||
map: &'a ValueMap,
|
||||
index: usize
|
||||
}
|
||||
|
||||
impl<'a> ValueMapIterator<'a> {
|
||||
fn new(map: &'a ValueMap) -> Self {
|
||||
Self { map, index: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for ValueMapIterator<'a> {
|
||||
type Item = (&'a Value, &'a Value);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let mut idx = self.index;
|
||||
loop {
|
||||
if idx >= self.map.size {
|
||||
return None
|
||||
}
|
||||
use ValueEntry as E;
|
||||
let E::Taken((k, v)) = &self.map.values[idx] else {
|
||||
idx += 1;
|
||||
continue;
|
||||
};
|
||||
|
||||
self.index = idx + 1;
|
||||
return Some((k, v))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait TryHash {
|
||||
fn try_hash<H: std::hash::Hasher>(&self, state: &mut H) -> Result<()>;
|
||||
}
|
||||
|
||||
impl TryHash for Value {
|
||||
fn try_hash<H: std::hash::Hasher>(&self, state: &mut H) -> Result<()> {
|
||||
use Value::*;
|
||||
match self {
|
||||
Nil => 0x23845.hash(state),
|
||||
Bool(b) => b.hash(state),
|
||||
Int(i) => i.hash(state),
|
||||
Ratio(r) => r.hash(state),
|
||||
Regex(r) => r.as_str().hash(state),
|
||||
String(s) => s.hash(state),
|
||||
List(l) => {
|
||||
for val in l.iter() {
|
||||
val.try_hash(state)?;
|
||||
}
|
||||
}
|
||||
Matrix(m) => {
|
||||
m.domain.hash(state);
|
||||
m.codomain.hash(state);
|
||||
for val in m.values.iter() {
|
||||
val.try_hash(state)?;
|
||||
}
|
||||
},
|
||||
_ => return Err(exception!(HASH_EXCEPTION, "cannot hash {self:?}"))
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueClone for ValueEntry {
|
||||
fn deep_clone(&self) -> Self {
|
||||
use ValueEntry as E;
|
||||
match self {
|
||||
E::Empty => E::Empty,
|
||||
E::Taken((k, v)) => E::Taken((k.deep_clone(), v.deep_clone())),
|
||||
E::Gravestone(g) => E::Gravestone(g.deep_clone()),
|
||||
}
|
||||
}
|
||||
|
||||
fn shallow_clone(&self) -> Self {
|
||||
use ValueEntry as E;
|
||||
match self {
|
||||
E::Empty => E::Empty,
|
||||
E::Taken((k, v)) => E::Taken((k.clone(), v.clone())),
|
||||
E::Gravestone(g) => E::Gravestone(g.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueClone for ValueMap {
|
||||
fn deep_clone(&self) -> Self {
|
||||
let values = self.values.iter().map(|e| e.deep_clone()).collect();
|
||||
Self {
|
||||
values,
|
||||
size: self.size,
|
||||
used: self.used
|
||||
}
|
||||
}
|
||||
|
||||
fn shallow_clone(&self) -> Self {
|
||||
let values = self.values.iter().map(|e| e.clone()).collect();
|
||||
Self {
|
||||
values,
|
||||
size: self.size,
|
||||
used: self.used
|
||||
}
|
||||
}
|
||||
}
|
230
matrix-lang/src/value/index.rs
Normal file
230
matrix-lang/src/value/index.rs
Normal file
|
@ -0,0 +1,230 @@
|
|||
use std::cmp::Ordering;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
macro_rules! error {
|
||||
($($arg:tt)*) => {
|
||||
Err(exception!(VALUE_EXCEPTION, $($arg)*))
|
||||
};
|
||||
}
|
||||
|
||||
impl Value {
|
||||
pub fn val_cmp(&self, other: &Self) -> Result<Ordering> {
|
||||
self.partial_cmp(other)
|
||||
.map_or_else(|| error!("cannot compare: {self:?} and {other:?}"), Ok)
|
||||
}
|
||||
|
||||
fn index_single(&self, index: &Value) -> Result<Self> {
|
||||
use Value as V;
|
||||
match (self, index) {
|
||||
(V::Table(t), index) => {
|
||||
Ok(t.get(index)?.unwrap_or(&Value::Nil).clone())
|
||||
},
|
||||
(V::List(l), V::Int(i)) => {
|
||||
if *i < 0 || *i as usize >= l.len() {
|
||||
return error!("{i} out of bounds for {self:?}")
|
||||
}
|
||||
Ok(l[*i as usize].clone())
|
||||
},
|
||||
(V::Matrix(m), V::Int(i)) => {
|
||||
if *i < 0 || *i as usize >= m.values.len() {
|
||||
return error!("{i} out of bounds for {self:?}")
|
||||
}
|
||||
Ok(m.values[*i as usize].clone())
|
||||
},
|
||||
_ => return error!("{index:?} cant index {self:?}")
|
||||
}
|
||||
}
|
||||
|
||||
fn index_multiple(&self, indexes: &Vec<Value>) -> Result<Self> {
|
||||
use Value as V;
|
||||
match self {
|
||||
V::List(..) => {
|
||||
let mut ret = Vec::new();
|
||||
for index in indexes {
|
||||
let res = self.index_single(index)?;
|
||||
ret.push(res);
|
||||
}
|
||||
Ok(V::List(ret.into()))
|
||||
}
|
||||
V::Table(..) => {
|
||||
let mut ret = ValueMap::new();
|
||||
for index in indexes {
|
||||
let res = self.index_single(index)?;
|
||||
ret.insert(index.clone(), res)?;
|
||||
}
|
||||
Ok(V::Table(ret.into()))
|
||||
}
|
||||
V::Matrix(m) => {
|
||||
let err = || error!("{self:?} can be index by [Int] or [Int;Int]");
|
||||
if indexes.len() != 2 {
|
||||
return err()
|
||||
}
|
||||
let lhs = indexes[0].clone();
|
||||
let rhs = indexes[1].clone();
|
||||
match (lhs, rhs) {
|
||||
(V::Nil, V::Nil) => {
|
||||
Ok(V::Matrix(m.shallow_clone()))
|
||||
},
|
||||
(V::Int(row), V::Nil) => {
|
||||
let Some((_, row)) = m.rows().enumerate().filter(|(idx, _)| *idx as i64 == row).next() else {
|
||||
return err();
|
||||
};
|
||||
let row: Vec<Value> = row.into_iter().map(|e| e.clone()).collect();
|
||||
Ok(V::Matrix(Matrix::new(row.len(), 1, row).into()))
|
||||
},
|
||||
(V::Nil, V::Int(col)) => {
|
||||
let Some((_, col)) = m.cols().enumerate().filter(|(idx, _)| *idx as i64 == col).next() else {
|
||||
return err();
|
||||
};
|
||||
let col: Vec<Value> = col.into_iter().map(|e| e.clone()).collect();
|
||||
Ok(V::Matrix(Matrix::new(1, col.len(), col).into()))
|
||||
},
|
||||
(V::Int(row), V::Int(col)) => {
|
||||
if row < 0 || col < 0 {
|
||||
return err();
|
||||
}
|
||||
m.get(row as usize, col as usize)
|
||||
}
|
||||
_ => return err()
|
||||
}
|
||||
}
|
||||
_ => return error!("cannot index {self:?}")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn index(&self, index: &Vec<Value>) -> Result<Self> {
|
||||
if index.len() == 0 {
|
||||
Ok(self.shallow_clone())
|
||||
} else if index.len() == 1 {
|
||||
self.index_single(&index[0])
|
||||
} else {
|
||||
self.index_multiple(index)
|
||||
}
|
||||
}
|
||||
|
||||
fn store_index_single(&mut self, index: &Value, store: Value) -> Result<()> {
|
||||
use Value as V;
|
||||
let err = format!("{self:?}");
|
||||
match (self, index) {
|
||||
(V::Table(t), index) => {
|
||||
t.insert(index.clone(), store)
|
||||
},
|
||||
(V::List(l), V::Int(i)) => {
|
||||
if *i < 0 || *i as usize >= l.len() {
|
||||
return error!("{i} out of bounds for {err}")
|
||||
}
|
||||
l[*i as usize] = store;
|
||||
Ok(())
|
||||
},
|
||||
(V::Matrix(m), V::Int(i)) => {
|
||||
if *i < 0 || *i as usize >= m.values.len() {
|
||||
return error!("{i} out of bounds for {err}")
|
||||
}
|
||||
m.values[*i as usize] = store;
|
||||
Ok(())
|
||||
},
|
||||
_ => return error!("{index:?} cant index {err}")
|
||||
}
|
||||
}
|
||||
|
||||
fn store_index_multiple(&mut self, indexes: &Vec<Value>, store: Value) -> Result<()> {
|
||||
use Value as V;
|
||||
match self {
|
||||
V::List(..) => {
|
||||
for index in indexes {
|
||||
self.store_index_single(index, store.clone())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
V::Table(..) => {
|
||||
for index in indexes {
|
||||
self.store_index_single(index, store.clone())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
_ => return error!("cannot index {self:?}")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn store_index(&mut self, index: &Vec<Value>, store: Value) -> Result<()> {
|
||||
if index.len() == 0 {
|
||||
Ok(())
|
||||
} else if index.len() == 1 {
|
||||
self.store_index_single(&index[0], store)
|
||||
} else {
|
||||
self.store_index_multiple(index, store)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn store_field_access(&mut self, ident: &str, val: Value) -> Result<()> {
|
||||
use Value as V;
|
||||
match self {
|
||||
V::Table(t) => {
|
||||
let key = V::String(Rc::from(ident));
|
||||
Ok(t.insert(key, val)?)
|
||||
},
|
||||
_ => return error!("cannot field access assign {self:?}")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn field_access(&self, ident: &str) -> Result<Self> {
|
||||
use Value as V;
|
||||
match self {
|
||||
V::Table(t) => {
|
||||
let key = V::String(Rc::from(ident));
|
||||
Ok(t.get(&key)?.unwrap_or(&V::Nil).clone())
|
||||
},
|
||||
_ => return error!("cannot field access {self:?}")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl Value {
|
||||
|
||||
pub fn into_iter_fn(self) -> Result<Rc<Function>> {
|
||||
let Value::Iter(iter) = self.into_iter()? else {
|
||||
return error!("bypassed iter check")
|
||||
};
|
||||
Ok(iter)
|
||||
}
|
||||
|
||||
pub fn into_iter(self) -> Result<Self> {
|
||||
use Value as V;
|
||||
Ok(match self {
|
||||
V::Iter(..) => self,
|
||||
V::List(l) => {
|
||||
let iter = RefCell::new(l.into_inner().into_iter());
|
||||
iter!(move |_,_| {
|
||||
match iter.borrow_mut().next() {
|
||||
Some(v) => Ok(v),
|
||||
None => Ok(V::Nil),
|
||||
}
|
||||
})
|
||||
},
|
||||
V::Range(r) => {
|
||||
let r = (*r).clone();
|
||||
let lhs = RefCell::new(r.0);
|
||||
let rhs = r.1;
|
||||
iter!(move |_,_| {
|
||||
let val = *lhs.borrow();
|
||||
let next = *lhs.borrow() + 1;
|
||||
if (!r.2 && *lhs.borrow() < rhs) || (r.2 && *lhs.borrow() <= rhs) {
|
||||
*lhs.borrow_mut() = next;
|
||||
return Ok(Value::Int(val))
|
||||
}
|
||||
Ok(Value::Nil)
|
||||
})
|
||||
},
|
||||
V::Function(f) => {
|
||||
if f.arity > 0 || f.variadic {
|
||||
return error!("iterator functions cannot be varadic or take arguments")
|
||||
}
|
||||
V::Iter(f)
|
||||
},
|
||||
val => return error!("cannot turn {val:?} into an iterator")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
337
matrix-lang/src/value/matrix.rs
Normal file
337
matrix-lang/src/value/matrix.rs
Normal file
|
@ -0,0 +1,337 @@
|
|||
use std::ops::{Add, Sub, Mul};
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Matrix {
|
||||
pub domain: usize,
|
||||
pub codomain: usize,
|
||||
pub values: Vec<Value>
|
||||
}
|
||||
|
||||
macro_rules! error {
|
||||
($($arg:tt)*) => {
|
||||
exception!(VALUE_EXCEPTION, $($arg)*)
|
||||
};
|
||||
}
|
||||
|
||||
impl Matrix {
|
||||
pub fn new(
|
||||
domain: usize,
|
||||
codomain: usize,
|
||||
values: Vec<Value>
|
||||
) -> Self {
|
||||
Self {
|
||||
domain,
|
||||
codomain,
|
||||
values
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_list(
|
||||
values: Vec<Value>
|
||||
) -> Self {
|
||||
Self {
|
||||
domain: values.len(),
|
||||
codomain: 1,
|
||||
values
|
||||
}
|
||||
}
|
||||
|
||||
pub fn empty(
|
||||
domain: usize,
|
||||
codomain: usize
|
||||
) -> Self {
|
||||
let values = (0..(domain * codomain)).into_iter()
|
||||
.map(|_| Value::Int(0))
|
||||
.collect();
|
||||
Self {
|
||||
domain,
|
||||
codomain,
|
||||
values
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self, row: usize, col: usize) -> Result<Value> {
|
||||
if row >= self.codomain || col >= self.domain {
|
||||
return Err(error!("[{};{}] out of bounds for [Matrix {}x{}]", row, col, self.domain, self.codomain))
|
||||
}
|
||||
let idx = col + row * self.domain;
|
||||
Ok(self.values[idx].clone())
|
||||
}
|
||||
|
||||
|
||||
|
||||
pub fn set(&mut self, row: usize, col: usize, val: Value) -> Result<()> {
|
||||
if row >= self.codomain || col >= self.domain {
|
||||
return Err(error!("[{};{}] out of bounds for [Matrix {}x{}]", row, col, self.domain, self.codomain))
|
||||
}
|
||||
let idx = col + row * self.domain;
|
||||
self.values[idx] = val;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn rows<'a>(&'a self) -> MatrixRows<'a> {
|
||||
MatrixRows {
|
||||
matrix: self,
|
||||
row: 0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cols<'a>(&'a self) -> MatrixCols<'a> {
|
||||
MatrixCols {
|
||||
matrix: self,
|
||||
col: 0
|
||||
}
|
||||
}
|
||||
|
||||
// SPLCIE DOMAIN
|
||||
pub fn splice_cols(&self, col_start: usize, col_end: usize) -> Result<Self> {
|
||||
if col_start <= col_end || col_end > self.domain {
|
||||
return Err(error!("[_;{}..{}] invalid for [Matrix {}x{}]", col_start, col_end, self.domain, self.codomain))
|
||||
}
|
||||
|
||||
let mut cols = Vec::new();
|
||||
|
||||
for (i, col) in self.cols().enumerate() {
|
||||
if i >= col_start && i < col_end {
|
||||
cols.push(col);
|
||||
}
|
||||
}
|
||||
|
||||
let domain = cols.len();
|
||||
let codomain = cols[0].len();
|
||||
let mut res = Self::empty(domain, codomain);
|
||||
|
||||
for i in 0..domain {
|
||||
for j in 0..codomain {
|
||||
res.set(j, i, cols[i][j].clone())?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
// SPLICE CODOMAIN
|
||||
pub fn splice_rows(&self, row_start: usize, row_end: usize) -> Result<Self> {
|
||||
if row_start <= row_end || row_end > self.codomain {
|
||||
return Err(error!("[{}..{};_] invalid for [Matrix {}x{}]", row_start, row_end, self.domain, self.codomain))
|
||||
}
|
||||
|
||||
let mut rows = Vec::new();
|
||||
|
||||
for (i, row) in self.rows().enumerate() {
|
||||
if i >= row_start && i < row_end {
|
||||
rows.push(row);
|
||||
}
|
||||
}
|
||||
|
||||
let domain = rows[0].len();
|
||||
let codomain = rows.len();
|
||||
let mut res = Self::empty(domain, codomain);
|
||||
|
||||
for i in 0..domain {
|
||||
for j in 0..codomain {
|
||||
res.set(j, i, rows[j][i].clone())?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
pub fn increment(&self, increment: Value) -> Result<Self> {
|
||||
let values = self.values.iter()
|
||||
.map(|v| v.clone() + increment.clone())
|
||||
.collect::<Result<Vec<Value>>>()?;
|
||||
Ok(Matrix::new(self.domain, self.codomain, values))
|
||||
}
|
||||
|
||||
pub fn decrement(&self, decrement: Value) -> Result<Self> {
|
||||
let values = self.values.iter()
|
||||
.map(|v| v.clone() - decrement.clone())
|
||||
.collect::<Result<Vec<Value>>>()?;
|
||||
Ok(Matrix::new(self.domain, self.codomain, values))
|
||||
}
|
||||
|
||||
pub fn scale(&self, scale: Value) -> Result<Self> {
|
||||
let values = self.values.iter()
|
||||
.map(|v| v.clone() * scale.clone())
|
||||
.collect::<Result<Vec<Value>>>()?;
|
||||
Ok(Matrix::new(self.domain, self.codomain, values))
|
||||
}
|
||||
|
||||
pub fn join_right(&self, other: &Matrix) -> Result<Self> {
|
||||
if self.codomain != other.codomain {
|
||||
return Err(error!("matrix codomain's do not match"))
|
||||
}
|
||||
let mut r1 = self.rows();
|
||||
let mut r2 = other.rows();
|
||||
let mut rows = Vec::new();
|
||||
loop {
|
||||
let Some(r1) = r1.next() else { break; };
|
||||
let Some(r2) = r2.next() else { break; };
|
||||
|
||||
let mut row = r1
|
||||
.into_iter()
|
||||
.map(|v| v.clone())
|
||||
.collect::<Vec<Value>>();
|
||||
|
||||
row.extend(r2.into_iter().map(|v| v.clone()));
|
||||
|
||||
rows.push(row);
|
||||
}
|
||||
|
||||
let values = rows
|
||||
.into_iter()
|
||||
.reduce(|mut a,b| {a.extend(b); a})
|
||||
.unwrap();
|
||||
|
||||
Ok(Matrix::new(self.domain + other.domain, self.codomain, values))
|
||||
}
|
||||
|
||||
pub fn join_bottom(&self, other: &Matrix) -> Result<Self> {
|
||||
if self.domain != other.domain {
|
||||
return Err(error!("matrix domain's do not match"))
|
||||
}
|
||||
let mut values = self.values.clone();
|
||||
values.extend(other.values.clone());
|
||||
Ok(Matrix::new(self.domain, self.codomain + other.codomain, values))
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Matrix {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
if self.domain != other.domain || self.codomain != other.codomain {
|
||||
return false
|
||||
}
|
||||
|
||||
for i in 0..self.values.len() {
|
||||
if self.values[i] != other.values[i] {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for Matrix {
|
||||
type Output = Result<Self>;
|
||||
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
if self.domain != rhs.domain || self.codomain != rhs.codomain {
|
||||
return Err(error!("cannot add {self:?} + {rhs:?}"))
|
||||
}
|
||||
let mut res = Matrix::empty(self.domain, self.codomain);
|
||||
for col in 0..self.domain {
|
||||
for row in 0..self.codomain {
|
||||
let add = self.get(row, col)? + rhs.get(row, col)?;
|
||||
res.set(row, col, add?)?;
|
||||
}
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for Matrix {
|
||||
type Output = Result<Self>;
|
||||
|
||||
fn sub(self, rhs: Self) -> Self::Output {
|
||||
if self.domain != rhs.domain || self.codomain != rhs.codomain {
|
||||
return Err(error!("cannot subtract {self:?} - {rhs:?}"))
|
||||
}
|
||||
let mut res = Matrix::empty(self.domain, self.codomain);
|
||||
for col in 0..self.domain {
|
||||
for row in 0..self.codomain {
|
||||
let sub = self.get(row, col)? - rhs.get(row, col)?;
|
||||
res.set(row, col, sub?)?;
|
||||
}
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
fn dot(lhs: Vec<&Value>, rhs: Vec<&Value>) -> Result<Value> {
|
||||
let len = lhs.len();
|
||||
|
||||
let mut res = Value::Int(0);
|
||||
for i in 0..len {
|
||||
let val = (lhs[i].clone() * rhs[i].clone())?;
|
||||
res = (res + val)?;
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
impl Mul for Matrix {
|
||||
type Output = Result<Self>;
|
||||
|
||||
fn mul(self, rhs: Self) -> Self::Output {
|
||||
if self.domain != rhs.codomain {
|
||||
return Err(error!("cannot multiply {self:?} * {rhs:?}"))
|
||||
}
|
||||
let mut res = Self::empty(rhs.domain, self.codomain);
|
||||
for (i, row) in self.rows().enumerate() {
|
||||
for (j, col) in rhs.cols().enumerate() {
|
||||
let dot = dot(row.clone(), col.clone())?;
|
||||
res.set(i, j, dot)?;
|
||||
}
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MatrixRows<'a> {
|
||||
matrix: &'a Matrix,
|
||||
row: usize
|
||||
}
|
||||
|
||||
impl<'a> Iterator for MatrixRows<'a> {
|
||||
type Item = Vec<&'a Value>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.row >= self.matrix.codomain {
|
||||
return None
|
||||
}
|
||||
|
||||
let row_start = self.row * self.matrix.domain;
|
||||
let row_end = row_start + self.matrix.domain;
|
||||
|
||||
let res = self.matrix.values
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(idx, _)| *idx >= row_start && *idx < row_end)
|
||||
.map(|v| v.1)
|
||||
.collect();
|
||||
|
||||
self.row += 1;
|
||||
|
||||
Some(res)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MatrixCols<'a> {
|
||||
matrix: &'a Matrix,
|
||||
col: usize
|
||||
}
|
||||
|
||||
impl<'a> Iterator for MatrixCols<'a> {
|
||||
type Item = Vec<&'a Value>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.col >= self.matrix.domain {
|
||||
return None
|
||||
}
|
||||
|
||||
let res = self.matrix.values
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(idx, _)| *idx % self.matrix.domain == self.col)
|
||||
.map(|v| v.1)
|
||||
.collect();
|
||||
|
||||
self.col += 1;
|
||||
|
||||
Some(res)
|
||||
}
|
||||
}
|
42
matrix-lang/src/value/mod.rs
Normal file
42
matrix-lang/src/value/mod.rs
Normal file
|
@ -0,0 +1,42 @@
|
|||
pub mod comp;
|
||||
pub mod gc;
|
||||
pub mod matrix;
|
||||
pub mod hash;
|
||||
pub mod exception;
|
||||
pub mod function;
|
||||
pub mod fmt;
|
||||
pub mod index;
|
||||
pub mod clone;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Value {
|
||||
Nil,
|
||||
|
||||
Bool(bool),
|
||||
Int(i64),
|
||||
Float(f64),
|
||||
Ratio(Rational64),
|
||||
Complex(Complex64),
|
||||
|
||||
Regex(Rc<Regex>),
|
||||
String(Rc<str>),
|
||||
|
||||
List(Gc<Vec<Value>>),
|
||||
Matrix(Gc<Matrix>),
|
||||
Table(Gc<ValueMap>),
|
||||
|
||||
Function(Rc<Function>),
|
||||
Range(Rc<(i64, i64, bool)>),
|
||||
Iter(Rc<Function>),
|
||||
File(Rc<RefCell<File>>),
|
||||
|
||||
Exception(Exception),
|
||||
}
|
||||
|
||||
impl From<&str> for Value {
|
||||
fn from(value: &str) -> Self {
|
||||
Value::String(Rc::from(value))
|
||||
}
|
||||
}
|
522
matrix-lang/src/value/value.rs
Normal file
522
matrix-lang/src/value/value.rs
Normal file
|
@ -0,0 +1,522 @@
|
|||
use std::{
|
||||
collections::HashMap,
|
||||
rc::Rc,
|
||||
hash::Hash,
|
||||
fmt::{Display, Debug},
|
||||
ops::{Add, Neg, Not, Sub, Div, Mul, BitOr, BitAnd, BitXor, Shl, Shr},
|
||||
cmp::Ordering,
|
||||
cell::RefCell, fs::File
|
||||
};
|
||||
|
||||
use crate::iter;
|
||||
use num_complex::Complex64;
|
||||
use num_rational::Rational64;
|
||||
use regex::Regex;
|
||||
|
||||
use crate::{ast::{Expr, BinaryOp, UnaryOp}, chunk::Function, Result, gc::Gc};
|
||||
|
||||
|
||||
pub type InlineList = Vec<Expr>;
|
||||
pub type InlineMatrix = (usize, usize, Vec<Expr>);
|
||||
pub type InlineTable = Vec<(Expr, Expr)>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Error(String);
|
||||
|
||||
impl Display for self::Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for self::Error {}
|
||||
|
||||
macro_rules! error {
|
||||
($($arg:tt)*) => {
|
||||
Err(self::Error(format!($($arg)*)).into())
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
impl From<&str> for Value {
|
||||
fn from(value: &str) -> Self {
|
||||
Value::String(value.into())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Debug for Value {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
use Value as V;
|
||||
match self {
|
||||
V::Nil => write!(f, "[Nil]"),
|
||||
V::Bool(b) => write!(f, "[Bool {b}]"),
|
||||
V::Int(i) => write!(f, "[Int {i}]"),
|
||||
V::Float(vf) => write!(f, "[Float {vf}]"),
|
||||
V::Ratio(r) => write!(f, "[Ratio {r}]"),
|
||||
V::Complex(c) => write!(f, "[Complex {c}]"),
|
||||
V::Regex(r) => write!(f, "[Regex /{r}/]"),
|
||||
V::String(s) => write!(f, "[String '{s}']"),
|
||||
V::List(l) => write!(f, "[List {}]", l.len()),
|
||||
V::Matrix(m) => write!(f, "[Matirx {}x{}]", m.domain, m.codomain),
|
||||
V::Table(t) => write!(f, "[Table {}]", t.0.len()),
|
||||
V::Function(vf) => write!(f, "[Function {}]", vf.name),
|
||||
V::Range(r) => write!(f, "[Range {:?}..{:?}]", r.0, r.1),
|
||||
V::Iter(_) => write!(f, "[Iterator]"),
|
||||
V::Error(_) => write!(f, "[Error]"),
|
||||
V::File(_) => write!(f, "[File]"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Value {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
use Value as V;
|
||||
|
||||
let red;
|
||||
let green;
|
||||
let yellow;
|
||||
let blue;
|
||||
let pink;
|
||||
let cyan;
|
||||
let clear;
|
||||
|
||||
if f.alternate() {
|
||||
red = "\x1b[31m";
|
||||
green = "\x1b[32m";
|
||||
yellow = "\x1b[33m";
|
||||
blue = "\x1b[34m";
|
||||
pink = "\x1b[35m";
|
||||
cyan = "\x1b[36m";
|
||||
clear = "\x1b[0m";
|
||||
} else {
|
||||
red = "";
|
||||
green = "";
|
||||
yellow = "";
|
||||
blue = "";
|
||||
pink = "";
|
||||
cyan = "";
|
||||
clear = "";
|
||||
}
|
||||
|
||||
match self {
|
||||
V::Nil => {write!(f, "{blue}nil{clear}")?;},
|
||||
V::Bool(b) => {write!(f, "{yellow}{b}{clear}")?;},
|
||||
V::Int(i) => {write!(f, "{yellow}{i}{clear}")?;},
|
||||
V::Float(l) => {write!(f, "{yellow}{l}{clear}")?;},
|
||||
V::Ratio(r) => {write!(f, "{yellow}{r}{clear}")?;},
|
||||
V::Complex(c) => {write!(f, "{yellow}{c}{clear}")?;},
|
||||
V::Regex(r) => {write!(f, "/{red}{r}{clear}/")?;},
|
||||
V::String(s) => {
|
||||
if f.alternate() {
|
||||
write!(f, "{green}'{s}'{clear}")?;
|
||||
} else {
|
||||
write!(f, "{s}")?;
|
||||
}
|
||||
}
|
||||
V::List(l) => {
|
||||
write!(f, "[")?;
|
||||
for (i, el) in l.iter().enumerate() {
|
||||
if i != 0 {
|
||||
write!(f, " ")?;
|
||||
}
|
||||
if f.alternate() {
|
||||
write!(f, "{el:#}")?;
|
||||
} else {
|
||||
write!(f, "{el}")?;
|
||||
}
|
||||
}
|
||||
write!(f, "]")?;
|
||||
},
|
||||
V::Matrix(m) => {
|
||||
if f.alternate() {
|
||||
write!(f, "{m:#}")?;
|
||||
} else {
|
||||
write!(f, "{m}")?;
|
||||
}
|
||||
},
|
||||
V::Table(t) => {
|
||||
write!(f, "{{")?;
|
||||
for (i, (key, val)) in t.0.iter().enumerate() {
|
||||
if i != 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
if f.alternate() {
|
||||
write!(f, "{key:#} = {val:#}")?;
|
||||
} else {
|
||||
write!(f, "{key} = {val}")?;
|
||||
}
|
||||
}
|
||||
write!(f, "}}")?;
|
||||
},
|
||||
V::Function(fun) => {
|
||||
use crate::chunk::InnerFunction as F;
|
||||
match fun.fun {
|
||||
F::Compiled(_) => write!(f, "{cyan}[Function {}]{clear}", fun.name)?,
|
||||
F::Native(_) => write!(f, "{cyan}[Builtin {}]{clear}", fun.name)?,
|
||||
};
|
||||
},
|
||||
V::Range(r) => {
|
||||
if f.alternate() {
|
||||
write!(f, "{:#}..{:#}", r.0, r.1)?;
|
||||
} else {
|
||||
write!(f, "{}..{}", r.0, r.1)?;
|
||||
}
|
||||
}
|
||||
V::Iter(_) => {write!(f, "{pink}[Iterator]{clear}")?;},
|
||||
V::File(_) => {write!(f, "{pink}[File]{clear}")?;},
|
||||
V::Error(e) => {write!(f, "{red}{e}{clear}")?;},
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Value {
|
||||
pub fn can_hash(&self) -> Result<()> {
|
||||
use Value::*;
|
||||
match self {
|
||||
Nil => {},
|
||||
Bool(_) => {},
|
||||
Int(_) => {},
|
||||
Ratio(_) => {},
|
||||
Regex(_) => {},
|
||||
String(_) => {}
|
||||
List(l) => {
|
||||
for val in l.iter() {
|
||||
val.can_hash()?;
|
||||
}
|
||||
}
|
||||
Matrix(m) => {
|
||||
for val in m.values.iter() {
|
||||
val.can_hash()?;
|
||||
}
|
||||
},
|
||||
_ => return error!("cannot hash {self:?}")
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn is_zero(&self) -> bool {
|
||||
use Value as V;
|
||||
match self {
|
||||
V::Int(i) => *i == 0,
|
||||
V::Float(f) => *f == 0.0 || *f == -0.0,
|
||||
V::Ratio(r) => *r.numer() == 0,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Value {
|
||||
pub fn val_cmp(&self, other: &Self) -> Result<Ordering> {
|
||||
self.partial_cmp(other)
|
||||
.map_or_else(|| error!("cannot compare: {self:?} and {other:?}"), Ok)
|
||||
}
|
||||
|
||||
fn index_single(&self, index: &Value) -> Result<Self> {
|
||||
use Value as V;
|
||||
match (self, index) {
|
||||
(V::Table(t), index) => {
|
||||
Ok(t.get(index)?.unwrap_or(&Value::Nil).clone())
|
||||
},
|
||||
(V::List(l), V::Int(i)) => {
|
||||
if *i < 0 || *i as usize >= l.len() {
|
||||
return error!("{i} out of bounds for {self:?}")
|
||||
}
|
||||
Ok(l[*i as usize].clone())
|
||||
},
|
||||
(V::Matrix(m), V::Int(i)) => {
|
||||
if *i < 0 || *i as usize >= m.values.len() {
|
||||
return error!("{i} out of bounds for {self:?}")
|
||||
}
|
||||
Ok(m.values[*i as usize].clone())
|
||||
},
|
||||
_ => return error!("{index:?} cant index {self:?}")
|
||||
}
|
||||
}
|
||||
|
||||
fn index_multiple(&self, indexes: &Vec<Value>) -> Result<Self> {
|
||||
use Value as V;
|
||||
match self {
|
||||
V::List(..) => {
|
||||
let mut ret = Vec::new();
|
||||
for index in indexes {
|
||||
let res = self.index_single(index)?;
|
||||
ret.push(res);
|
||||
}
|
||||
Ok(V::List(ret.into()))
|
||||
}
|
||||
V::Table(..) => {
|
||||
let mut ret = ValueMap::new();
|
||||
for index in indexes {
|
||||
let res = self.index_single(index)?;
|
||||
ret.insert(index.clone(), res)?;
|
||||
}
|
||||
Ok(V::Table(ret.into()))
|
||||
}
|
||||
V::Matrix(m) => {
|
||||
let err = || error!("{self:?} can be index by [Int] or [Int;Int]");
|
||||
if indexes.len() != 2 {
|
||||
return err()
|
||||
}
|
||||
let lhs = indexes[0].clone();
|
||||
let rhs = indexes[1].clone();
|
||||
match (lhs, rhs) {
|
||||
(V::Nil, V::Nil) => {
|
||||
Ok(V::Matrix(m.clone_inside()))
|
||||
},
|
||||
(V::Int(row), V::Nil) => {
|
||||
let Some((_, row)) = m.rows().enumerate().filter(|(idx, _)| *idx as i64 == row).next() else {
|
||||
return err();
|
||||
};
|
||||
let row: Vec<Value> = row.into_iter().map(|e| e.clone()).collect();
|
||||
Ok(V::Matrix(Matrix::new(row.len(), 1, row).into()))
|
||||
},
|
||||
(V::Nil, V::Int(col)) => {
|
||||
let Some((_, col)) = m.cols().enumerate().filter(|(idx, _)| *idx as i64 == col).next() else {
|
||||
return err();
|
||||
};
|
||||
let col: Vec<Value> = col.into_iter().map(|e| e.clone()).collect();
|
||||
Ok(V::Matrix(Matrix::new(1, col.len(), col).into()))
|
||||
},
|
||||
(V::Int(row), V::Int(col)) => {
|
||||
if row < 0 || col < 0 {
|
||||
return err();
|
||||
}
|
||||
m.get(row as usize, col as usize)
|
||||
}
|
||||
_ => return err()
|
||||
}
|
||||
}
|
||||
_ => return error!("cannot index {self:?}")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn index(&self, index: &Vec<Value>) -> Result<Self> {
|
||||
if index.len() == 0 {
|
||||
Ok(self.clone_inside())
|
||||
} else if index.len() == 1 {
|
||||
self.index_single(&index[0])
|
||||
} else {
|
||||
self.index_multiple(index)
|
||||
}
|
||||
}
|
||||
|
||||
fn store_index_single(&mut self, index: &Value, store: Value) -> Result<()> {
|
||||
use Value as V;
|
||||
let err = format!("{self:?}");
|
||||
match (self, index) {
|
||||
(V::Table(t), index) => {
|
||||
t.insert(index.clone(), store)
|
||||
},
|
||||
(V::List(l), V::Int(i)) => {
|
||||
if *i < 0 || *i as usize >= l.len() {
|
||||
return error!("{i} out of bounds for {err}")
|
||||
}
|
||||
l[*i as usize] = store;
|
||||
Ok(())
|
||||
},
|
||||
(V::Matrix(m), V::Int(i)) => {
|
||||
if *i < 0 || *i as usize >= m.values.len() {
|
||||
return error!("{i} out of bounds for {err}")
|
||||
}
|
||||
m.values[*i as usize] = store;
|
||||
Ok(())
|
||||
},
|
||||
_ => return error!("{index:?} cant index {err}")
|
||||
}
|
||||
}
|
||||
|
||||
fn store_index_multiple(&mut self, indexes: &Vec<Value>, store: Value) -> Result<()> {
|
||||
use Value as V;
|
||||
match self {
|
||||
V::List(..) => {
|
||||
for index in indexes {
|
||||
self.store_index_single(index, store.clone())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
V::Table(..) => {
|
||||
for index in indexes {
|
||||
self.store_index_single(index, store.clone())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
_ => return error!("cannot index {self:?}")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn store_index(&mut self, index: &Vec<Value>, store: Value) -> Result<()> {
|
||||
if index.len() == 0 {
|
||||
Ok(())
|
||||
} else if index.len() == 1 {
|
||||
self.store_index_single(&index[0], store)
|
||||
} else {
|
||||
self.store_index_multiple(index, store)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn store_field_access(&mut self, ident: &str, val: Value) -> Result<()> {
|
||||
use Value as V;
|
||||
match self {
|
||||
V::Table(t) => {
|
||||
let key = V::String(Rc::from(ident));
|
||||
Ok(t.insert(key, val)?)
|
||||
},
|
||||
_ => return error!("cannot field access assign {self:?}")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn field_access(&self, ident: &str) -> Result<Self> {
|
||||
use Value as V;
|
||||
match self {
|
||||
V::Table(t) => {
|
||||
let key = V::String(Rc::from(ident));
|
||||
Ok(t.get(&key)?.unwrap_or(&V::Nil).clone())
|
||||
},
|
||||
_ => return error!("cannot field access {self:?}")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn binary_op(op: BinaryOp, lhs: Value, rhs: Value) -> Result<Self> {
|
||||
use BinaryOp::*;
|
||||
match op {
|
||||
Add => lhs + rhs,
|
||||
Subtract => lhs - rhs,
|
||||
Multiply => lhs * rhs,
|
||||
Divide => lhs / rhs,
|
||||
Modulo => lhs.modulo(rhs),
|
||||
Power => lhs.pow(rhs),
|
||||
BitwiseAnd => lhs & rhs,
|
||||
BitwiseOr => lhs | rhs,
|
||||
BitwiseXor => lhs ^ rhs,
|
||||
BitwiseShiftLeft => lhs << rhs,
|
||||
BitwiseShiftRight => lhs >> rhs,
|
||||
Equals => Ok(Self::Bool(lhs == rhs)),
|
||||
NotEquals => Ok(Self::Bool(lhs != rhs)),
|
||||
GreaterEquals => Ok(Self::Bool(lhs >= rhs)),
|
||||
LessEquals => Ok(Self::Bool(lhs <= rhs)),
|
||||
GreaterThan => Ok(Self::Bool(lhs > rhs)),
|
||||
LessThan => Ok(Self::Bool(lhs < rhs)),
|
||||
Range | RangeEq => {
|
||||
let Value::Int(lhs) = lhs else {
|
||||
return error!("range can only take [Int]'s")
|
||||
};
|
||||
let Value::Int(rhs) = rhs else {
|
||||
return error!("range can only take [Int]'s")
|
||||
};
|
||||
Ok(Self::Range(Rc::new((lhs, rhs, op == RangeEq))))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unary_op(op: UnaryOp, val: Value) -> Value {
|
||||
use UnaryOp::*;
|
||||
match op {
|
||||
Negate => -val,
|
||||
Not => Self::Bool(!val),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for Value {
|
||||
type Output = Value;
|
||||
|
||||
fn neg(self) -> Self::Output {
|
||||
use Value::*;
|
||||
match self {
|
||||
Bool(b) => Bool(!b),
|
||||
Int(i) => Int(-i),
|
||||
Float(f) => Float(-f),
|
||||
Ratio(r) => Ratio(-r),
|
||||
Complex(c) => Complex(-c),
|
||||
_ => return Float(f64::NAN)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Not for Value {
|
||||
type Output = bool;
|
||||
|
||||
fn not(self) -> Self::Output {
|
||||
use Value as V;
|
||||
match self {
|
||||
V::Nil => true,
|
||||
V::Bool(b) => !b,
|
||||
V::Int(i) => i == 0,
|
||||
V::Float(f) => f == 0.0,
|
||||
V::Ratio(r) => *(r.numer()) == 0 || *(r.denom()) == 0,
|
||||
V::Complex(c) => !c.is_normal(),
|
||||
V::Regex(_) => false,
|
||||
V::List(_) => false,
|
||||
V::Matrix(_) => false,
|
||||
V::Table(_) => false,
|
||||
V::String(s) => s.as_ref() == "",
|
||||
V::Function(_) => false,
|
||||
V::Iter(_) => false,
|
||||
V::Range(_) => false,
|
||||
V::File(_) => false,
|
||||
V::Error(_) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Value {
|
||||
|
||||
pub fn into_iter_fn(self) -> Result<Rc<Function>> {
|
||||
let Value::Iter(iter) = self.into_iter()? else {
|
||||
return error!("bypassed iter check")
|
||||
};
|
||||
Ok(iter)
|
||||
}
|
||||
|
||||
pub fn into_iter(self) -> Result<Self> {
|
||||
use Value as V;
|
||||
Ok(match self {
|
||||
V::Iter(..) => self,
|
||||
V::List(l) => {
|
||||
let iter = RefCell::new(l.into_inner().into_iter());
|
||||
iter!(move |_,_| {
|
||||
match iter.borrow_mut().next() {
|
||||
Some(v) => Ok(v),
|
||||
None => Ok(V::Nil),
|
||||
}
|
||||
})
|
||||
},
|
||||
V::Range(r) => {
|
||||
let r = (*r).clone();
|
||||
let lhs = RefCell::new(r.0);
|
||||
let rhs = r.1;
|
||||
iter!(move |_,_| {
|
||||
let val = *lhs.borrow();
|
||||
let next = *lhs.borrow() + 1;
|
||||
if (!r.2 && *lhs.borrow() < rhs) || (r.2 && *lhs.borrow() <= rhs) {
|
||||
*lhs.borrow_mut() = next;
|
||||
return Ok(Value::Int(val))
|
||||
}
|
||||
Ok(Value::Nil)
|
||||
})
|
||||
},
|
||||
V::Function(f) => {
|
||||
if f.arity > 0 || f.variadic {
|
||||
return error!("iterator functions cannot be varadic or take arguments")
|
||||
}
|
||||
V::Iter(f)
|
||||
},
|
||||
val => return error!("cannot turn {val:?} into an iterator")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn dot(lhs: Vec<&Value>, rhs: Vec<&Value>) -> Result<Value> {
|
||||
let len = lhs.len();
|
||||
|
||||
let mut res = Value::Int(0);
|
||||
for i in 0..len {
|
||||
let val = (lhs[i].clone() * rhs[i].clone())?;
|
||||
res = (res + val)?;
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
}
|
|
@ -1,33 +1,6 @@
|
|||
use std::{rc::Rc, fmt::{Debug, Display}, usize, ops::{Index, IndexMut}, collections::HashMap, cell::RefCell, sync::{atomic::{AtomicUsize, Ordering}, Arc}};
|
||||
use crate::{value::{Value, self, ValueMap, Matrix}, Result, gc::Gc, chunk::{Function, Instruction, Chunk, InnerFunction}, compiler::NamesTable, native};
|
||||
use std::{fmt::Display, ops::{IndexMut, Index}, sync::{atomic::{AtomicUsize, Ordering}, Arc}, collections::HashMap};
|
||||
use crate::prelude::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
ValueError(value::Error),
|
||||
NotFunction(Value),
|
||||
InvArity(usize, usize, Rc<str>),
|
||||
ExecNative,
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {}
|
||||
|
||||
impl Display for Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
use Error::*;
|
||||
match self {
|
||||
ValueError(err) => write!(f, "{err}"),
|
||||
NotFunction(v) => write!(f, "{v:?} is not a function"),
|
||||
InvArity(wanted, had, name) => write!(f, "function {name} takes {wanted} params but was given {had}"),
|
||||
ExecNative => write!(f, "cannot execute a native function"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<value::Error> for Error {
|
||||
fn from(value: value::Error) -> Self {
|
||||
Error::ValueError(value)
|
||||
}
|
||||
}
|
||||
pub struct Stack<T> {
|
||||
inner: Vec<T>
|
||||
}
|
||||
|
@ -99,12 +72,12 @@ pub struct StackFrame {
|
|||
}
|
||||
|
||||
struct VmError {
|
||||
err: crate::Error,
|
||||
err: Exception,
|
||||
frames: Vec<StackFrame>
|
||||
}
|
||||
|
||||
impl From<crate::Error> for VmError {
|
||||
fn from(err: crate::Error) -> Self {
|
||||
impl From<Exception> for VmError {
|
||||
fn from(err: Exception) -> Self {
|
||||
VmError {
|
||||
err,
|
||||
frames: Vec::new()
|
||||
|
@ -112,15 +85,6 @@ impl From<crate::Error> for VmError {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<self::Error> for VmError {
|
||||
fn from(err: self::Error) -> Self {
|
||||
VmError {
|
||||
err: err.into(),
|
||||
frames: Vec::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type VmResult<T> = std::result::Result<T, VmError>;
|
||||
|
||||
impl StackFrame {
|
||||
|
@ -166,10 +130,16 @@ pub struct Vm {
|
|||
trystack: Vec<TryScope>,
|
||||
globals: Rc<RefCell<HashMap<u16, Value>>>,
|
||||
names: NamesTable,
|
||||
global_names: NamesTable,
|
||||
global_names: GlobalsTable,
|
||||
interupt: Arc<AtomicUsize>,
|
||||
}
|
||||
|
||||
macro_rules! error {
|
||||
($($arg:tt)*) => {
|
||||
exception!(RUNTIME_EXCEPTION, $($arg)*)
|
||||
};
|
||||
}
|
||||
|
||||
impl Vm {
|
||||
|
||||
fn push(&mut self, val: Value) {
|
||||
|
@ -188,7 +158,7 @@ impl Vm {
|
|||
self.names.clone()
|
||||
}
|
||||
|
||||
pub fn global_names(&self) -> NamesTable {
|
||||
pub fn globals(&self) -> GlobalsTable {
|
||||
self.global_names.clone()
|
||||
}
|
||||
|
||||
|
@ -210,7 +180,7 @@ impl Vm {
|
|||
|
||||
pub fn load_global(&mut self, value: Value, name: &str) {
|
||||
let idx = self.global_names.borrow().len();
|
||||
self.global_names.borrow_mut().push(name.into());
|
||||
self.global_names.borrow_mut().push(Global { idx, name: Rc::from(name), is_const: true });
|
||||
self.globals.borrow_mut().insert(idx as u16, value);
|
||||
}
|
||||
|
||||
|
@ -220,7 +190,7 @@ impl Vm {
|
|||
|
||||
fn init_frame(&mut self, fun: Rc<Function>) -> Result<StackFrame> {
|
||||
let InnerFunction::Compiled(ref compiled_chunk) = fun.fun else {
|
||||
return Err(self::Error::ExecNative.into())
|
||||
return Err(error!("cannot create stack frame on builtin function"))
|
||||
};
|
||||
Ok(StackFrame::new(
|
||||
self,
|
||||
|
@ -240,7 +210,7 @@ impl Vm {
|
|||
match ins {
|
||||
I::NoOp => {},
|
||||
I::CreateLocal => self.locals.push(self.stack.pop()),
|
||||
I::LoadLocal(idx) => {self.stack.push(self.locals[idx as usize].clone());},
|
||||
I::LoadLocal(idx) => {self.stack.push(self.locals[frame.lp + idx as usize].clone());},
|
||||
I::StoreLocal(idx) => self.locals[frame.bp + idx as usize] = self.pop(),
|
||||
I::DiscardLocals(count) => {self.locals.truncate(self.locals.len() - count as usize)},
|
||||
I::LoadGlobal(idx) => {
|
||||
|
@ -325,11 +295,12 @@ impl Vm {
|
|||
|
||||
let fun = self.pop();
|
||||
let Value::Function(fun) = fun else {
|
||||
return Err(Error::NotFunction(fun).into())
|
||||
Err(error!("cannot call {fun} (not a function)"))?
|
||||
};
|
||||
|
||||
if !fun.variadic && arity > fun.arity {
|
||||
return Err(Error::InvArity(fun.arity, arity as usize, fun.name.clone()).into())
|
||||
let needs = fun.arity;
|
||||
Err(error!("function {fun} takes {needs} args, given {arity}"))?
|
||||
}
|
||||
|
||||
let mut params = self.stack.split_off(self.stack.len() - arity).inner;
|
||||
|
@ -416,14 +387,14 @@ impl Vm {
|
|||
Ok(None)
|
||||
}
|
||||
|
||||
fn stack_trace(&mut self, frames: Vec<StackFrame>) -> String {
|
||||
let mut trace = String::from("\x1b[33m\x1b[1mStack Trace:\x1b[0m\n");
|
||||
fn stack_trace(&mut self, frames: Vec<StackFrame>, e: Exception) -> Exception {
|
||||
let mut e = e;
|
||||
for frame in frames {
|
||||
let ip = frame.ip - 1;
|
||||
let pos = frame.body.pos[ip];
|
||||
trace.push_str(&format!(" {} at {}:{}\n", &frame.name, pos.row, pos.col));
|
||||
e = e.trace(frame.name, pos);
|
||||
}
|
||||
trace
|
||||
e
|
||||
}
|
||||
|
||||
fn exec_fn(&mut self, frame: &mut StackFrame, fun: Rc<Function>, params: Vec<Value>) -> VmResult<Value> {
|
||||
|
@ -438,7 +409,8 @@ impl Vm {
|
|||
self.stack.push(param);
|
||||
}
|
||||
let mut new_frame = StackFrame::new(self, name, body.clone(), params_len, frame.depth + 1);
|
||||
self.exec(&mut new_frame)
|
||||
let res = self.exec(&mut new_frame);
|
||||
res
|
||||
},
|
||||
InnerFunction::Native(native_fun) => {
|
||||
Ok(native_fun((self, frame), params)?)
|
||||
|
@ -462,7 +434,7 @@ impl Vm {
|
|||
self.stack.truncate(catch.stack_len);
|
||||
self.locals.truncate(catch.locals_len);
|
||||
frame.ip = catch.err_idx;
|
||||
self.stack.push(Value::Error(err.err));
|
||||
self.stack.push(Value::Exception(err.err));
|
||||
} else {
|
||||
let mut err = err;
|
||||
err.frames.push(frame.clone());
|
||||
|
@ -478,13 +450,12 @@ impl Vm {
|
|||
}
|
||||
|
||||
pub fn run(&mut self, fun: Rc<Function>) -> Result<Value> {
|
||||
let mut frame = self.init_frame(fun)?;
|
||||
self.interupt.store(0, Ordering::SeqCst);
|
||||
self.stack = Stack::new();
|
||||
self.locals = Stack::new();
|
||||
let mut frame = self.init_frame(fun)?;
|
||||
self.exec(&mut frame).map_err(|e| {
|
||||
let trace = self.stack_trace(e.frames);
|
||||
(e.err, trace).into()
|
||||
self.stack_trace(e.frames, e.err)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -9,4 +9,4 @@ proc-macro = true
|
|||
[dependencies]
|
||||
syn = { version = "1", features = ["full"] }
|
||||
quote = "1"
|
||||
matrix = { path = "../matrix" }
|
||||
matrix-lang = { path = "../matrix-lang" }
|
||||
|
|
|
@ -37,12 +37,12 @@ pub fn native_func(input: TokenStream, annotated_item: TokenStream) -> TokenStre
|
|||
assert!(itemfn.sig.variadic.is_none(), "item must not be variadic");
|
||||
|
||||
let expanded = quote! {
|
||||
#visibility fn #name() -> ::std::rc::Rc< ::matrix::chunk::Function> {
|
||||
::std::rc::Rc::new( ::matrix::chunk::Function {
|
||||
#visibility fn #name() -> ::std::rc::Rc< ::matrix_lang::prelude::Function> {
|
||||
::std::rc::Rc::new( ::matrix_lang::prelude::Function {
|
||||
name: ::std::rc::Rc::from( #name_str ),
|
||||
arity: #arity,
|
||||
variadic: #variadic,
|
||||
fun: ::matrix::chunk::InnerFunction::Native(
|
||||
fun: ::matrix_lang::prelude::InnerFunction::Native(
|
||||
::std::rc::Rc::new(
|
||||
|#inputs| #output #block
|
||||
)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[package]
|
||||
name = "matrix-stdlib"
|
||||
name = "matrix-std"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
|
@ -7,7 +7,7 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
anyhow = "1"
|
||||
matrix = { path = "../matrix" }
|
||||
matrix-lang = { path = "../matrix-lang" }
|
||||
matrix-macros = { path = "../matrix-macros" }
|
||||
os_info = "3"
|
||||
rand = "0.8"
|
|
@ -1,10 +1,8 @@
|
|||
use std::hash::{DefaultHasher, Hash, Hasher};
|
||||
|
||||
use matrix::{vm::Vm, value::Value, unpack_args, Result, unpack_varargs};
|
||||
use matrix_macros::native_func;
|
||||
use std::hash::{DefaultHasher, Hasher};
|
||||
use rand::Rng;
|
||||
use crate::{VmArgs, next, error};
|
||||
|
||||
use matrix_lang::prelude::*;
|
||||
use matrix_macros::native_func;
|
||||
use crate::{VmArgs, next, error, unpack_args, unpack_varargs};
|
||||
|
||||
fn to_radix(r: i64, mut n: i64) -> String {
|
||||
let mut result = String::new();
|
||||
|
@ -123,11 +121,8 @@ fn remove(_: VmArgs, args: Vec<Value>) -> Result<Value> {
|
|||
#[native_func(1)]
|
||||
fn hash(_: VmArgs, args: Vec<Value>) -> Result<Value> {
|
||||
let [value] = unpack_args!(args);
|
||||
if let Err(e) = value.can_hash() {
|
||||
return Err(e)
|
||||
};
|
||||
let mut hasher = DefaultHasher::new();
|
||||
value.hash(&mut hasher);
|
||||
value.try_hash(&mut hasher)?;
|
||||
let fin = hasher.finish();
|
||||
Ok(Value::Int(fin as u32 as i64))
|
||||
}
|
||||
|
@ -201,8 +196,10 @@ fn sort(_: VmArgs, args: Vec<Value>) -> Result<Value> {
|
|||
let Value::List(mut list) = value else {
|
||||
return error!("sort requires a list")
|
||||
};
|
||||
let hi = list.len() - 1;
|
||||
quicksort(list.as_mut(), 0, hi);
|
||||
if list.len() > 1 {
|
||||
let hi = list.len() - 1;
|
||||
quicksort(list.as_mut(), 0, hi);
|
||||
}
|
||||
Ok(Value::List(list))
|
||||
}
|
||||
|
|
@ -1,8 +1,7 @@
|
|||
use std::{io::{self, Read, Write}, cell::RefCell, fs::OpenOptions, rc::Rc};
|
||||
|
||||
use matrix::{value::Value, self, vm::Vm, Result, unpack_varargs, iter, unpack_args};
|
||||
use matrix_lang::prelude::*;
|
||||
use matrix_macros::native_func;
|
||||
use crate::{error, VmArgs};
|
||||
use crate::{error, VmArgs, unpack_varargs, unpack_args};
|
||||
|
||||
#[native_func(0..)]
|
||||
fn print(_: VmArgs, args: Vec<Value>) -> Result<Value> {
|
|
@ -1,7 +1,7 @@
|
|||
use std::{cell::RefCell, rc::Rc};
|
||||
use matrix::{iter, vm::Vm, value::Value, Result, unpack_varargs, unpack_args};
|
||||
use matrix_lang::prelude::*;
|
||||
use matrix_macros::native_func;
|
||||
use crate::{error, next, VmArgs};
|
||||
use crate::{error, next, VmArgs, unpack_args, unpack_varargs};
|
||||
|
||||
use Value as V;
|
||||
|
||||
|
@ -54,7 +54,7 @@ fn range(_: VmArgs, args: Vec<Value>) -> Result<Value> {
|
|||
Ok(iter!(move |_,_| {
|
||||
let curr = *(i.borrow());
|
||||
*(i.borrow_mut()) = curr + step;
|
||||
if curr >= max {
|
||||
if (step >= 0 && curr >= max) || (step <= 0 && curr <= max) {
|
||||
return Ok(V::Nil)
|
||||
} else {
|
||||
return Ok(V::Int(curr))
|
||||
|
@ -235,7 +235,7 @@ fn lines(_: VmArgs, args: Vec<Value>) -> Result<Value> {
|
|||
let Value::String(str) = str else {
|
||||
return error!("lines arg must be a string")
|
||||
};
|
||||
let lines: Vec<Rc<str>> = str.split_inclusive("\n").map(|s| Rc::from(s)).collect();
|
||||
let lines: Vec<Rc<str>> = str.split("\n").map(|s| Rc::from(s)).collect();
|
||||
let res = RefCell::new(lines.into_iter());
|
||||
Ok(iter!(move |_,_| {
|
||||
match res.borrow_mut().next() {
|
||||
|
@ -245,7 +245,7 @@ fn lines(_: VmArgs, args: Vec<Value>) -> Result<Value> {
|
|||
}))
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
#[native_func(2)]
|
||||
fn skip((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> {
|
||||
let [count, iter] = unpack_args!(args);
|
||||
let iter = iter.into_iter_fn()?;
|
||||
|
@ -291,25 +291,32 @@ fn alternate(_: VmArgs, args: Vec<Value>) -> Result<Value> {
|
|||
}
|
||||
|
||||
#[native_func(2)]
|
||||
fn intersperse(_: VmArgs, args: Vec<Value>) -> Result<Value> {
|
||||
fn intersperse((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> {
|
||||
let [value, iter] = unpack_args!(args);
|
||||
let iter = iter.into_iter_fn()?;
|
||||
let flag = RefCell::new(Some(0));
|
||||
let next = RefCell::new(next!(vm, frame, iter)?);
|
||||
Ok(iter!(move |(vm, frame),_| {
|
||||
let f = *flag.borrow();
|
||||
match f {
|
||||
Some(0) => {
|
||||
let val = next!(vm, frame, iter)?;
|
||||
if val == Value::Nil {
|
||||
let val = next.borrow();
|
||||
if *val == Value::Nil {
|
||||
*flag.borrow_mut() = None;
|
||||
} else {
|
||||
*flag.borrow_mut() = Some(1);
|
||||
}
|
||||
Ok(val)
|
||||
Ok(val.clone())
|
||||
},
|
||||
Some(1) => {
|
||||
*next.borrow_mut() = next!(vm, frame, iter)?;
|
||||
if *next.borrow() == Value::Nil {
|
||||
*flag.borrow_mut() = None;
|
||||
return Ok(Value::Nil)
|
||||
} else {
|
||||
*flag.borrow_mut() = Some(0);
|
||||
}
|
||||
let val = value.clone();
|
||||
*flag.borrow_mut() = Some(0);
|
||||
Ok(val)
|
||||
},
|
||||
_ => Ok(Value::Nil)
|
||||
|
@ -331,6 +338,7 @@ fn zip(_: VmArgs, args: Vec<Value>) -> Result<Value> {
|
|||
let vr = next!(vm, frame, ir)?;
|
||||
if vl == Value::Nil || vr == Value::Nil {
|
||||
*flag.borrow_mut() = false;
|
||||
return Ok(Value::Nil)
|
||||
}
|
||||
Ok(Value::List(vec![vl, vr].into()))
|
||||
},
|
||||
|
@ -401,7 +409,7 @@ fn take((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> {
|
|||
Ok(Value::List(values.into()))
|
||||
}
|
||||
|
||||
#[native_func(2)]
|
||||
#[native_func(1)]
|
||||
fn last((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> {
|
||||
let [iter] = unpack_args!(args);
|
||||
let iter = iter.into_iter_fn()?;
|
||||
|
@ -414,7 +422,7 @@ fn last((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> {
|
|||
Ok(last)
|
||||
}
|
||||
|
||||
#[native_func(2)]
|
||||
#[native_func(1)]
|
||||
fn next((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> {
|
||||
let [iter] = unpack_args!(args);
|
||||
let iter = iter.into_iter_fn()?;
|
50
matrix-std/src/lib.rs
Normal file
50
matrix-std/src/lib.rs
Normal file
|
@ -0,0 +1,50 @@
|
|||
mod core;
|
||||
mod sys;
|
||||
mod math;
|
||||
mod io;
|
||||
mod iter;
|
||||
|
||||
use matrix_lang::prelude::*;
|
||||
pub(crate) type VmArgs<'a, 'b> = (&'a mut Vm, &'b mut StackFrame);
|
||||
|
||||
macro_rules! error {
|
||||
($($arg:tt)*) => {
|
||||
Err(::matrix_lang::prelude::exception!(::matrix_lang::prelude::RUNTIME_EXCEPTION, $($arg)*))
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! next {
|
||||
($vm:expr, $frame:expr, $iter:expr) => {
|
||||
$vm.run_fn($frame, $iter.clone(), vec![])
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! unpack_args {
|
||||
($e:expr) => {
|
||||
$e.try_into().expect("bypassed arity check")
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! unpack_varargs {
|
||||
($e:expr) => {{
|
||||
let mut args = $e;
|
||||
let matrix_lang::prelude::Value::List(varargs) = args.pop().expect("bypassed arity check") else {
|
||||
panic!("bypassed arity check")
|
||||
};
|
||||
let varargs = varargs.into_inner();
|
||||
(args.try_into().expect("bypassed arity check"), varargs)
|
||||
}};
|
||||
}
|
||||
|
||||
pub(crate) use error;
|
||||
pub(crate) use next;
|
||||
pub(crate) use unpack_args;
|
||||
pub(crate) use unpack_varargs;
|
||||
|
||||
pub fn load(vm: &mut Vm) {
|
||||
core::load(vm);
|
||||
sys::load(vm);
|
||||
io::load(vm);
|
||||
iter::load(vm);
|
||||
math::load(vm);
|
||||
}
|
|
@ -1,9 +1,8 @@
|
|||
use core::f64;
|
||||
use std::f64::{consts::{PI, E, TAU}, NAN, INFINITY};
|
||||
|
||||
use matrix::{vm::Vm, value::{Value, Matrix}, Result, unpack_args, Rational64, Complex64};
|
||||
use matrix_lang::prelude::*;
|
||||
use matrix_macros::native_func;
|
||||
use crate::{error, VmArgs};
|
||||
use crate::{error, VmArgs, unpack_args};
|
||||
|
||||
#[native_func(1)]
|
||||
fn trans(_: VmArgs, args: Vec<Value>) -> Result<Value> {
|
||||
|
@ -280,7 +279,7 @@ macro_rules! trig {
|
|||
fn $type(_: VmArgs, args: Vec<Value>) -> Result<Value> {
|
||||
use Value as V;
|
||||
let [value] = unpack_args!(args);
|
||||
match value.promote_trig() {
|
||||
match value.floaty() {
|
||||
V::Float(f) => Ok(V::Float(f.$type())),
|
||||
V::Complex(c) => Ok(V::Complex(c.$type())),
|
||||
v => error!("cannot compute {} on {v}", stringify!($type))
|
||||
|
@ -295,7 +294,7 @@ macro_rules! trigf {
|
|||
fn $str(_: VmArgs, args: Vec<Value>) -> Result<Value> {
|
||||
use Value as V;
|
||||
let [value] = unpack_args!(args);
|
||||
match value.promote_trig() {
|
||||
match value.floaty() {
|
||||
V::Float(f) => Ok(V::Float(f.$type())),
|
||||
v => error!("cannot compute {} on {v}", stringify!($str))
|
||||
}
|
||||
|
@ -307,7 +306,7 @@ macro_rules! trigf {
|
|||
fn log(_: VmArgs, args: Vec<Value>) -> Result<Value> {
|
||||
use Value as V;
|
||||
let [base, value] = unpack_args!(args);
|
||||
match (base.promote_trig(), value.promote_trig()) {
|
||||
match (base.floaty(), value.floaty()) {
|
||||
(V::Float(base), V::Float(arg)) => Ok(V::Float(arg.log(base))),
|
||||
(V::Float(base), V::Complex(arg)) => Ok(V::Complex(arg.log(base))),
|
||||
(V::Complex(base), V::Float(arg)) => Ok(V::Complex(arg.ln() / base.ln())),
|
||||
|
@ -463,7 +462,7 @@ fn im(_: VmArgs, args: Vec<Value>) -> Result<Value> {
|
|||
#[native_func(1)]
|
||||
fn cis(_: VmArgs, args: Vec<Value>) -> Result<Value> {
|
||||
let [value] = unpack_args!(args);
|
||||
match value.promote_trig() {
|
||||
match value.floaty() {
|
||||
Value::Float(f) => Ok(Value::Complex(Complex64::cis(f))),
|
||||
Value::Complex(c) => Ok((Value::Complex(Complex64::cis(c.re)) * Value::Float((-c.im).exp()))?),
|
||||
_ => error!("cis can only take floats")
|
|
@ -1,9 +1,9 @@
|
|||
use std::{process::{exit, Command, Stdio, Child}, env, rc::Rc, io::{Read, self}, cell::RefCell, fs::{File, self}, os::fd::FromRawFd, sync::OnceLock, path::PathBuf};
|
||||
|
||||
use matrix::{vm::Vm, value::{Value, ValueMap}, unpack_args, Result, gc::Gc};
|
||||
use matrix_lang::prelude::*;
|
||||
use matrix_macros::native_func;
|
||||
use os_info::Info;
|
||||
use crate::{VmArgs, error};
|
||||
use crate::{VmArgs, error, unpack_args};
|
||||
|
||||
#[native_func(1)]
|
||||
fn sys_exit(_: VmArgs, args: Vec<Value>) -> Result<Value> {
|
|
@ -1,32 +0,0 @@
|
|||
use matrix::vm::{Vm, StackFrame};
|
||||
|
||||
mod core;
|
||||
mod sys;
|
||||
mod math;
|
||||
mod io;
|
||||
mod iter;
|
||||
|
||||
pub(crate) type VmArgs<'a, 'b> = (&'a mut Vm, &'b mut StackFrame);
|
||||
|
||||
macro_rules! error {
|
||||
($($arg:tt)*) => {
|
||||
Err(format!($($arg)*).into())
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! next {
|
||||
($vm:expr, $frame:expr, $iter:expr) => {
|
||||
$vm.run_fn($frame, $iter.clone(), vec![])
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) use error;
|
||||
pub(crate) use next;
|
||||
|
||||
pub fn load(vm: &mut Vm) {
|
||||
core::load(vm);
|
||||
sys::load(vm);
|
||||
io::load(vm);
|
||||
iter::load(vm);
|
||||
math::load(vm);
|
||||
}
|
|
@ -1,122 +0,0 @@
|
|||
use std::{fmt::Display, rc::Rc};
|
||||
|
||||
pub mod compiler;
|
||||
pub mod value;
|
||||
pub mod gc;
|
||||
pub mod lex;
|
||||
pub mod vm;
|
||||
pub mod parse;
|
||||
pub mod chunk;
|
||||
pub mod ast;
|
||||
|
||||
pub use ::num_complex::Complex64 as Complex64;
|
||||
pub use ::num_rational::Rational64 as Rational64;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Error(Rc<ErrorInner>);
|
||||
|
||||
impl std::error::Error for Error {}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum ErrorInner {
|
||||
Lex(lex::Error),
|
||||
Parse(parse::Error),
|
||||
Value(value::Error),
|
||||
Compile(compiler::Error),
|
||||
Runtime(vm::Error),
|
||||
External(anyhow::Error),
|
||||
Traced(Box<Error>, String),
|
||||
Any(String),
|
||||
}
|
||||
|
||||
impl Display for crate::Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
use crate::ErrorInner::*;
|
||||
match self.0.as_ref() {
|
||||
Lex(err) => write!(f, "{err}"),
|
||||
Parse(err) => write!(f, "{err}"),
|
||||
Value(err) => write!(f, "{err}"),
|
||||
Compile(err) => write!(f, "{err}"),
|
||||
Runtime(err) => write!(f, "{err}"),
|
||||
External(err) => write!(f, "{err}"),
|
||||
Traced(err, trace) => write!(f, "{err}\n{trace}"),
|
||||
Any(err) => write!(f, "{err}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! from_error {
|
||||
($struct:ty, $tuple:tt) => {
|
||||
impl From<$struct> for crate::Error {
|
||||
fn from(value: $struct) -> Self {
|
||||
crate::Error(Rc::new(ErrorInner::$tuple(value)))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
from_error!(lex::Error, Lex);
|
||||
from_error!(parse::Error, Parse);
|
||||
from_error!(value::Error, Value);
|
||||
from_error!(compiler::Error, Compile);
|
||||
from_error!(vm::Error, Runtime);
|
||||
from_error!(anyhow::Error, External);
|
||||
from_error!(String, Any);
|
||||
|
||||
impl From<(Error, String)> for Error {
|
||||
fn from(value: (Error, String)) -> Self {
|
||||
Self(Rc::new(ErrorInner::Traced(Box::new(value.0), value.1)))
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! iter {
|
||||
($fn:expr) => {
|
||||
$crate::value::Value::Iter(
|
||||
::std::rc::Rc::new(
|
||||
$crate::chunk::Function {
|
||||
name: ::std::rc::Rc::from("<iterator>"),
|
||||
arity: 0,
|
||||
variadic: false,
|
||||
fun: $crate::chunk::InnerFunction::Native(
|
||||
::std::rc::Rc::new($fn
|
||||
))}))
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! native {
|
||||
($name:expr, $arity:expr, $varadic:expr, $fn:expr) => {
|
||||
$crate::value::Value::Function(
|
||||
::std::rc::Rc::new( $crate::chunk::Function {
|
||||
name: std::rc::Rc::from($name),
|
||||
arity: $arity,
|
||||
variadic: $varadic,
|
||||
fun: $crate::chunk::InnerFunction::Native(
|
||||
::std::rc::Rc::new($fn)
|
||||
)
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! unpack_args {
|
||||
($e:expr) => {
|
||||
$e.try_into().expect("bypassed arity check")
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! unpack_varargs {
|
||||
($e:expr) => {{
|
||||
let mut args = $e;
|
||||
let $crate::value::Value::List(varargs) = args.pop().expect("bypassed arity check") else {
|
||||
panic!("bypassed arity check")
|
||||
};
|
||||
let varargs = varargs.into_inner();
|
||||
(args.try_into().expect("bypassed arity check"), varargs)
|
||||
}};
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, crate::Error>;
|
1223
matrix/src/value.rs
1223
matrix/src/value.rs
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue