initial commit
This commit is contained in:
commit
59fa4bad6b
15 changed files with 1441 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
/target
|
||||||
|
/.vscode
|
468
Cargo.lock
generated
Normal file
468
Cargo.lock
generated
Normal file
|
@ -0,0 +1,468 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.0.73"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
|
||||||
|
|
||||||
|
[[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 = "4.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c4ab1b92798304eedc095b53942963240037c0516452cb11aeba709d420b2219"
|
||||||
|
dependencies = [
|
||||||
|
"error-code",
|
||||||
|
"str-buf",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "complexpr"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
|
"num-complex",
|
||||||
|
"num-rational",
|
||||||
|
"num-traits",
|
||||||
|
"rustyline",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dirs-next"
|
||||||
|
version = "2.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"dirs-sys-next",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dirs-sys-next"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"redox_users",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[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.2.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
|
||||||
|
dependencies = [
|
||||||
|
"errno-dragonfly",
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "errno-dragonfly"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "error-code"
|
||||||
|
version = "2.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "64f18991e7bf11e7ffee451b5318b5c1a73c52d0d0ada6e5a3017c8c1ced6a21"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"str-buf",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fd-lock"
|
||||||
|
version = "3.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e11dcc7e4d79a8c89b9ab4c6f5c30b1fc4a83c420792da3542fd31179ed5f517"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"rustix",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.2.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "io-lifetimes"
|
||||||
|
version = "0.7.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1ea37f355c05dde75b84bba2d767906ad522e97cd9e2eef2be7a4ab7fb442c06"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.132"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linux-raw-sys"
|
||||||
|
version = "0.0.46"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d4d2456c373231a208ad294c33dc5bff30051eafd954cd4caae83a712b12854d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||||
|
|
||||||
|
[[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.24.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "195cdbc1741b8134346d515b3a56a1c94b0912758009cfd53f99ea0f57b065fc"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-bigint"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-complex"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "97fbc387afefefd5e9e39493299f3069e14a140dd34dc19b4c1c1a8fddb6a790"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-integer"
|
||||||
|
version = "0.1.45"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-rational"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"num-bigint",
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-traits"
|
||||||
|
version = "0.2.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.43"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[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 = "redox_syscall"
|
||||||
|
version = "0.2.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_users"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
"redox_syscall",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustix"
|
||||||
|
version = "0.35.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72c825b8aa8010eb9ee99b75f05e10180b9278d161583034d7574c9d617aeada"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"errno",
|
||||||
|
"io-lifetimes",
|
||||||
|
"libc",
|
||||||
|
"linux-raw-sys",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustyline"
|
||||||
|
version = "10.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1d1cd5ae51d3f7bf65d7969d579d502168ef578f289452bd8ccc91de28fda20e"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"cfg-if",
|
||||||
|
"clipboard-win",
|
||||||
|
"dirs-next",
|
||||||
|
"fd-lock",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"memchr",
|
||||||
|
"nix",
|
||||||
|
"radix_trie",
|
||||||
|
"scopeguard",
|
||||||
|
"unicode-segmentation",
|
||||||
|
"unicode-width",
|
||||||
|
"utf8parse",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scopeguard"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smallvec"
|
||||||
|
version = "1.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "str-buf"
|
||||||
|
version = "1.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "1.0.99"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "1.0.34"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8c1b05ca9d106ba7d2e31a9dab4a64e7be2cce415321966ea3132c49a656e252"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "1.0.34"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e8f2591983642de85c921015f3f070c665a197ed69e417af436115e3a1407487"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-segmentation"
|
||||||
|
version = "1.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-width"
|
||||||
|
version = "0.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utf8parse"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.11.0+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||||
|
|
||||||
|
[[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.36.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_msvc",
|
||||||
|
"windows_i686_gnu",
|
||||||
|
"windows_i686_msvc",
|
||||||
|
"windows_x86_64_gnu",
|
||||||
|
"windows_x86_64_msvc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.36.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.36.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.36.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.36.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.36.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
|
13
Cargo.toml
Normal file
13
Cargo.toml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
[package]
|
||||||
|
name = "complexpr"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
lazy_static = "1.4.0"
|
||||||
|
num-complex = "0.4.1"
|
||||||
|
num-rational = "0.4.0"
|
||||||
|
num-traits = "0.2.15"
|
||||||
|
rustyline = "10.0.0"
|
28
idea.cxpr
Normal file
28
idea.cxpr
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
let square = x -> x^2;
|
||||||
|
let also_square(x) = x^2
|
||||||
|
|
||||||
|
let primes = range(100) |: filter(is_prime);
|
||||||
|
let prime_squares = range(100) |: map(square);
|
||||||
|
let also_prime_squares = range(100) |? is_prime |> square;
|
||||||
|
|
||||||
|
#
|
||||||
|
let primes_and_prime_squares = primes |& prime_squares;
|
||||||
|
|
||||||
|
if 49 <~ data {
|
||||||
|
println("49 is the square of a prime");
|
||||||
|
} else {
|
||||||
|
println("49 is not the square of a prime");
|
||||||
|
}
|
||||||
|
|
||||||
|
# LET IDENT(square) EQ IDENT(x) ARROW IDENT(x) CARET INT(2) SEMICOLON
|
||||||
|
#
|
||||||
|
# LET IDENT(data) EQ IDENT(range) LPAREN INT(100) RPAREN PIPECOLON IDENT(filter) LPAREN IDENT(is_prime) RPAREN PIPECOLON IDENT(map) LPAREN IDENT(square) RPAREN SEMICOLON
|
||||||
|
# LET IDENT(also_data) EQ IDENT(range) LPAREN INT(100) RPAREN PIPEQUESTION IDENT(is_prime) PIPEPOINT IDENT(square) SEMICOLON
|
||||||
|
|
||||||
|
# D = x:(fold(map(factors(x), n:(to_int(fold(factors(x),mul)/n))), add))
|
||||||
|
|
||||||
|
let D = x -> {
|
||||||
|
let facts = factors(x);
|
||||||
|
let product = facts |: foldl(1, mul);
|
||||||
|
return facts |> (n -> product / n) |: foldl(0, add);
|
||||||
|
}
|
37
src/bin/main.rs
Normal file
37
src/bin/main.rs
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
use std::{rc::Rc, cell::RefCell, fs::{File, self}, io::{BufReader, Read}};
|
||||||
|
|
||||||
|
use complexpr::{eval::Environment, interpreter::interpret, value::Value};
|
||||||
|
use rustyline::{self, error::ReadlineError};
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let args: Vec<String> = std::env::args().collect();
|
||||||
|
if args.len() == 2 {
|
||||||
|
let fname = &args[1];
|
||||||
|
let src = fs::read_to_string(fname)?;
|
||||||
|
interpret(&src, None)?;
|
||||||
|
} else {
|
||||||
|
repl()?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn repl() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let mut rl = rustyline::Editor::<()>::new()?;
|
||||||
|
let env = Rc::new(RefCell::new(Environment::new()));
|
||||||
|
loop {
|
||||||
|
let readline = rl.readline(">> ");
|
||||||
|
match readline {
|
||||||
|
Ok(line) => {
|
||||||
|
let result = interpret(&line, Some(env.clone()));
|
||||||
|
match result {
|
||||||
|
Ok(Value::Nil) => (),
|
||||||
|
Ok(value) => println!("{:?}", value),
|
||||||
|
Err(e) => println!("Error: {}", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(ReadlineError::Eof) => break,
|
||||||
|
Err(_) => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
108
src/eval.rs
Normal file
108
src/eval.rs
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
use std::{collections::HashMap, rc::Rc, cell::RefCell};
|
||||||
|
|
||||||
|
use num_complex::Complex64;
|
||||||
|
|
||||||
|
use crate::{value::Value, expr::{Stmt, Expr}, token::{TokenType, Token, OpType}};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Environment {
|
||||||
|
parent: Option<Rc<RefCell<Environment>>>,
|
||||||
|
map: HashMap<Rc<str>, Value>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Environment {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self { parent: None, map: HashMap::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn extend(parent: Rc<RefCell<Self>>) -> Self {
|
||||||
|
Self { parent: Some(parent), map: HashMap::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, name: &str) -> Result<Value,()> {
|
||||||
|
match self.map.get(name) {
|
||||||
|
Some(v) => Ok(v.clone()),
|
||||||
|
None => match self.parent {
|
||||||
|
Some(ref p) => p.borrow().get(name),
|
||||||
|
None => Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn declare(&mut self, name: Rc<str>, value: Value) {
|
||||||
|
self.map.insert(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set(&mut self, name: Rc<str>, value: Value) -> Result<(),()> {
|
||||||
|
match self.map.contains_key(&name) {
|
||||||
|
true => { self.map.insert(name, value); Ok(()) },
|
||||||
|
false => match self.parent {
|
||||||
|
Some(ref mut p) => p.borrow_mut().set(name, value),
|
||||||
|
None => Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn eval_stmt(stmt: &Stmt, env: Rc<RefCell<Environment>>) -> Result<(), String> {
|
||||||
|
match stmt {
|
||||||
|
Stmt::Expr{ expr }
|
||||||
|
=> drop(eval_expr(expr, env)),
|
||||||
|
Stmt::Let { lhs: Token{ty: TokenType::Ident(s),..}, rhs: None }
|
||||||
|
=> env.borrow_mut().declare(s.clone(), Value::Nil),
|
||||||
|
Stmt::Let { lhs: Token{ty: TokenType::Ident(s),..}, rhs: Some(rhs) } => {
|
||||||
|
let r = eval_expr(rhs, env.clone())?;
|
||||||
|
env.borrow_mut().declare(s.clone(), r)
|
||||||
|
},
|
||||||
|
Stmt::If { conditions, bodies, else_clause }
|
||||||
|
=> todo!(), // TODO if statements
|
||||||
|
_ => unreachable!()
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn eval_expr(expr: &Expr, env: Rc<RefCell<Environment>>) -> Result<Value, String> {
|
||||||
|
match expr {
|
||||||
|
Expr::Literal { value } => match &value.ty {
|
||||||
|
TokenType::Nil => Ok(Value::Nil),
|
||||||
|
TokenType::True => Ok(Value::Bool(true)),
|
||||||
|
TokenType::False => Ok(Value::Bool(false)),
|
||||||
|
TokenType::Int(n) => Ok(Value::Int(*n)),
|
||||||
|
TokenType::Float(f) => Ok(Value::Float(*f)),
|
||||||
|
TokenType::ImFloat(f) => Ok(Value::Complex(Complex64::new(0.0, *f))),
|
||||||
|
TokenType::String(s) => Ok(Value::String(s.clone())),
|
||||||
|
_ => todo!()
|
||||||
|
},
|
||||||
|
Expr::Ident { value } => if let Token { ty: TokenType::Ident(name), ..} = value {
|
||||||
|
env.borrow_mut().get(name).map_err(|_| "Variable not defined in scope".into())
|
||||||
|
} else { unreachable!() },
|
||||||
|
Expr::Binary { lhs, rhs, op } => match op.ty.get_op_type() {
|
||||||
|
Some(OpType::Assignment) => {
|
||||||
|
let r = eval_expr(rhs, env.clone())?;
|
||||||
|
if let Expr::Ident{value: Token{ty: TokenType::Ident(name),..}} = &**lhs {
|
||||||
|
if op.ty == TokenType::Equal {
|
||||||
|
env.borrow_mut().set(name.clone(), r).map_err(|_| "Variable not declared before assignment")?;
|
||||||
|
Ok(Value::Nil)
|
||||||
|
} else {
|
||||||
|
todo!() // TODO +=, -=, etc
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Some(OpType::Additive) | Some(OpType::Multiplicative) => {
|
||||||
|
let l = eval_expr(lhs, env.clone())?;
|
||||||
|
let r = eval_expr(rhs, env)?;
|
||||||
|
match op.ty {
|
||||||
|
TokenType::Plus => &l + &r,
|
||||||
|
TokenType::Minus => &l - &r,
|
||||||
|
TokenType::Star => &l * &r,
|
||||||
|
TokenType::Slash => &l / &r,
|
||||||
|
_ => todo!() // TODO other operations
|
||||||
|
}
|
||||||
|
},
|
||||||
|
o => todo!("{:?}", o) // TODO other operations
|
||||||
|
},
|
||||||
|
e => todo!("{:?}", e) // TODO other expression types
|
||||||
|
}
|
||||||
|
}
|
51
src/expr.rs
Normal file
51
src/expr.rs
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use crate::{token::{Token, OpType}};
|
||||||
|
|
||||||
|
pub enum Stmt {
|
||||||
|
Expr { expr: Expr },
|
||||||
|
Let { lhs: Token, rhs: Option<Expr> },
|
||||||
|
If { conditions: Vec<Expr>, bodies: Vec<Vec<Stmt>>, else_clause: Option<Vec<Stmt>> }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Stmt {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Expr { expr } => write!(f, "{:?}", expr),
|
||||||
|
Self::Let { lhs, rhs } => write!(f, "(let {:?} = {:?})", lhs, rhs),
|
||||||
|
_ => todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Expr {
|
||||||
|
Binary { lhs: Box<Expr>, rhs: Box<Expr>, op: Token },
|
||||||
|
Unary { arg: Box<Expr>, op: Token },
|
||||||
|
Ident { value: Token },
|
||||||
|
Literal { value: Token },
|
||||||
|
List { items: Vec<Expr> },
|
||||||
|
FuncCall { func: Box<Expr>, args: Vec<Expr> }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Expr {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Binary { lhs: left, rhs: right, op} => write!(f, "({:?} {:?} {:?})", op, left, right),
|
||||||
|
Self::Unary { arg, op} => write!(f, "({:?} {:?})", op, arg),
|
||||||
|
Self::Ident { value, .. } => write!(f, "(ident {:?})", value),
|
||||||
|
Self::Literal { value, .. } => write!(f, "(lit {:?})", value),
|
||||||
|
Self::List { items } => write!(f, "(list {:?})", items),
|
||||||
|
Self::FuncCall { func, args } => write!(f, "(call {:?} {:?})", func, args),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Expr {
|
||||||
|
pub fn is_lvalue(&self) -> bool {
|
||||||
|
matches!(self, Expr::Ident{..})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_assignment(&self) -> bool {
|
||||||
|
matches!(self, Expr::Binary{op, ..} if op.ty.get_op_type() == Some(OpType::Assignment))
|
||||||
|
}
|
||||||
|
}
|
26
src/interpreter.rs
Normal file
26
src/interpreter.rs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
|
use crate::{value::Value, lexer::Lexer, parser::Parser, eval::{Environment, eval_stmt, eval_expr}, expr::Stmt};
|
||||||
|
|
||||||
|
pub fn interpret(src: &str, env: Option<Rc<RefCell<Environment>>>) -> Result<Value, Box<dyn std::error::Error>> {
|
||||||
|
let mut lexer = Lexer::new(src, None);
|
||||||
|
lexer.lex()?;
|
||||||
|
let mut parser = Parser::new(lexer.into_tokens());
|
||||||
|
let ast = parser.parse()?;
|
||||||
|
let environ;
|
||||||
|
if let Some(env) = env {
|
||||||
|
environ = env;
|
||||||
|
} else {
|
||||||
|
environ = Rc::new(RefCell::new(Environment::new()))
|
||||||
|
}
|
||||||
|
let mut result = Value::Nil;
|
||||||
|
for stmt in ast {
|
||||||
|
if let Stmt::Expr{expr} = stmt {
|
||||||
|
result = eval_expr(&expr, environ.clone())?;
|
||||||
|
} else {
|
||||||
|
eval_stmt(&stmt, environ.clone())?;
|
||||||
|
result = Value::Nil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(result)
|
||||||
|
}
|
250
src/lexer.rs
Normal file
250
src/lexer.rs
Normal file
|
@ -0,0 +1,250 @@
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use crate::{ParserError, Position, token::{Token, TokenType}};
|
||||||
|
|
||||||
|
pub struct Lexer {
|
||||||
|
// name of file being lexed
|
||||||
|
filename: Option<Rc<str>>,
|
||||||
|
line: usize,
|
||||||
|
col: usize,
|
||||||
|
tokens: Vec<Token>,
|
||||||
|
start: usize,
|
||||||
|
current: usize,
|
||||||
|
code: Vec<char>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Lexer {
|
||||||
|
pub fn new(code: &str, filename: Option<String>) -> Self {
|
||||||
|
Self { filename: filename.map(|s| Rc::from(s)), line: 1, col: 1, tokens: vec![], start: 0, current: 0, code: code.chars().collect() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_tokens(self) -> Vec<Token> {
|
||||||
|
self.tokens
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next(&mut self) -> char {
|
||||||
|
let c = self.code[self.current];
|
||||||
|
self.advance(c == '\n');
|
||||||
|
c
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expect(&mut self, chars: &[char]) -> Option<char> {
|
||||||
|
for c in chars {
|
||||||
|
if self.code[self.current] == *c {
|
||||||
|
self.advance(*c == '\n');
|
||||||
|
return Some(*c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn at_end(&self) -> bool {
|
||||||
|
self.current >= self.code.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn peek(&self) -> char {
|
||||||
|
self.code[self.current]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_token<S>(&mut self, ty: TokenType, text: S) where S: Into<String> {
|
||||||
|
self.tokens.push(Token {
|
||||||
|
ty,
|
||||||
|
text: text.into(),
|
||||||
|
pos: Position {
|
||||||
|
file: self.filename.clone(),
|
||||||
|
pos: self.start,
|
||||||
|
line: self.line,
|
||||||
|
col: self.col - (self.current - self.start)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mk_error<S>(&self, msg: S) -> ParserError where S: Into<String> {
|
||||||
|
ParserError {
|
||||||
|
pos: Position { file: self.filename.clone(), pos: self.start, line: self.line, col: self.col},
|
||||||
|
message: msg.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn advance(&mut self, newline: bool) {
|
||||||
|
if newline {
|
||||||
|
self.line += 1;
|
||||||
|
self.col = 1;
|
||||||
|
} else {
|
||||||
|
self.col += 1;
|
||||||
|
}
|
||||||
|
self.current += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lex(&mut self) -> Result<(), ParserError> {
|
||||||
|
while !self.at_end() {
|
||||||
|
self.start = self.current;
|
||||||
|
match self.next() {
|
||||||
|
'+' => match self.expect(&['=']) {
|
||||||
|
Some('=') => self.add_token(TokenType::PlusEqual, "+="),
|
||||||
|
_ => self.add_token(TokenType::Plus, "+"),
|
||||||
|
},
|
||||||
|
'-' => match self.expect(&['=', '>']) {
|
||||||
|
Some('=') => self.add_token(TokenType::MinusEqual, "-="),
|
||||||
|
Some('>') => self.add_token(TokenType::Arrow, "->"),
|
||||||
|
_ => self.add_token(TokenType::Minus, "-"),
|
||||||
|
},
|
||||||
|
'*' => match self.expect(&['=']) {
|
||||||
|
Some('=') => self.add_token(TokenType::StarEqual, "*="),
|
||||||
|
_ => self.add_token(TokenType::Star, "*"),
|
||||||
|
},
|
||||||
|
'%' => match self.expect(&['=']) {
|
||||||
|
Some('=') => self.add_token(TokenType::PercentEqual, "%="),
|
||||||
|
_ => self.add_token(TokenType::Percent, "%"),
|
||||||
|
},
|
||||||
|
'/' => match self.expect(&['=', '/']) {
|
||||||
|
Some('=') => self.add_token(TokenType::SlashEqual, "/="),
|
||||||
|
Some('/') => match self.expect(&['=']) {
|
||||||
|
Some('=') => self.add_token(TokenType::DoubleSlashEqual, "//="),
|
||||||
|
_ => self.add_token(TokenType::DoubleSlash, "//")
|
||||||
|
}
|
||||||
|
_ => self.add_token(TokenType::Slash, "/"),
|
||||||
|
},
|
||||||
|
'^' => match self.expect(&['=']) {
|
||||||
|
Some('=') => self.add_token(TokenType::CaretEqual, "^="),
|
||||||
|
_ => self.add_token(TokenType::Caret, "^"),
|
||||||
|
},
|
||||||
|
'=' => match self.expect(&['=']) {
|
||||||
|
Some('=') => self.add_token(TokenType::DoubleEqual, "=="),
|
||||||
|
_ => self.add_token(TokenType::Equal, "=")
|
||||||
|
},
|
||||||
|
'!' => match self.expect(&['=']) {
|
||||||
|
Some('=') => self.add_token(TokenType::BangEqual, "!="),
|
||||||
|
_ => self.add_token(TokenType::Bang, "!")
|
||||||
|
},
|
||||||
|
'>' => match self.expect(&['=']) {
|
||||||
|
Some('=') => self.add_token(TokenType::GreaterEqual, ">="),
|
||||||
|
_ => self.add_token(TokenType::Greater, ">")
|
||||||
|
},
|
||||||
|
'<' => match self.expect(&['=']) {
|
||||||
|
Some('=') => self.add_token(TokenType::LessEqual, "<="),
|
||||||
|
_ => self.add_token(TokenType::Less, "<")
|
||||||
|
},
|
||||||
|
'&' => match self.expect(&['&']) {
|
||||||
|
Some('&') => self.add_token(TokenType::DoubleAmper, "&&"),
|
||||||
|
_ => self.add_token(TokenType::Amper, "&"),
|
||||||
|
},
|
||||||
|
'|' => match self.expect(&['|', ':', '?', '>', '&']) {
|
||||||
|
Some('|') => self.add_token(TokenType::DoublePipe, "||"),
|
||||||
|
Some(':') => self.add_token(TokenType::PipeColon, "|:"),
|
||||||
|
Some('?') => self.add_token(TokenType::PipeQuestion, "|?"),
|
||||||
|
Some('>') => self.add_token(TokenType::PipePoint, "|>"),
|
||||||
|
Some('&') => self.add_token(TokenType::PipeAmper, "|&"),
|
||||||
|
_ => self.add_token(TokenType::Pipe, "|"),
|
||||||
|
},
|
||||||
|
',' => self.add_token(TokenType::Comma, ","),
|
||||||
|
';' => self.add_token(TokenType::Semicolon, ";"),
|
||||||
|
'(' => self.add_token(TokenType::LParen, "("),
|
||||||
|
')' => self.add_token(TokenType::RParen, ")"),
|
||||||
|
'[' => self.add_token(TokenType::LBrack, "["),
|
||||||
|
']' => self.add_token(TokenType::RBrack, "]"),
|
||||||
|
'{' => self.add_token(TokenType::LBrace, "{"),
|
||||||
|
'}' => self.add_token(TokenType::RBrace, "}"),
|
||||||
|
'#' => {
|
||||||
|
while !self.at_end() && self.peek() != '\n' {
|
||||||
|
self.advance(false);
|
||||||
|
}
|
||||||
|
self.advance(true);
|
||||||
|
},
|
||||||
|
'"' => self.string()?,
|
||||||
|
' ' | '\t' | '\r' | '\n' => (),
|
||||||
|
'0'..='9' => self.number()?,
|
||||||
|
'a'..='z' | 'A'..='Z' | '_' => self.ident()?,
|
||||||
|
c => return Err(self.mk_error(format!("Unexpected character: {}", c)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn string(&mut self) -> Result<(), ParserError> {
|
||||||
|
let mut s = String::new();
|
||||||
|
while !self.at_end() && self.peek() != '"' {
|
||||||
|
if self.peek() == '\\' {
|
||||||
|
self.advance(false);
|
||||||
|
// TODO more escape codes! \xHH, \u{HH..} or maybe \uhhhh \Uhhhhhhhh
|
||||||
|
match self.peek() {
|
||||||
|
'0' => s.push('\0'),
|
||||||
|
'n' => s.push('\n'),
|
||||||
|
't' => s.push('\t'),
|
||||||
|
'r' => s.push('\r'),
|
||||||
|
'e' => s.push('\x1b'),
|
||||||
|
'\\' => s.push('\\'),
|
||||||
|
'"' => s.push('"'),
|
||||||
|
'\n' => (),
|
||||||
|
c => return Err(self.mk_error(format!("Unknown escape code \\{}", c)))
|
||||||
|
}
|
||||||
|
self.advance(self.peek() == '\n')
|
||||||
|
} else {
|
||||||
|
s.push(self.peek());
|
||||||
|
self.advance(self.peek() == '\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.at_end() {
|
||||||
|
return Err(self.mk_error("Unexpected EOF while parsing string"))
|
||||||
|
}
|
||||||
|
self.advance(false);
|
||||||
|
self.add_token(TokenType::String(Rc::from(s)), self.code[self.start..self.current].iter().collect::<String>());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn number(&mut self) -> Result<(), ParserError> {
|
||||||
|
let mut has_dot = false;
|
||||||
|
while !self.at_end() && (self.peek().is_numeric() || self.peek() == '.') {
|
||||||
|
if self.peek() == '.' {
|
||||||
|
if has_dot {
|
||||||
|
return Err(self.mk_error("Numeric literals cannot contain two decimal points"))
|
||||||
|
} else {
|
||||||
|
has_dot = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.advance(false);
|
||||||
|
}
|
||||||
|
let is_imag = !self.at_end() && self.peek() == 'i';
|
||||||
|
if is_imag { self.advance(false); }
|
||||||
|
let literal = self.code[self.start..self.current].iter().collect::<String>();
|
||||||
|
if is_imag {
|
||||||
|
match literal[..literal.len()-1].parse::<f64>() {
|
||||||
|
Ok(num) => self.add_token(TokenType::ImFloat(num), literal),
|
||||||
|
Err(e) => return Err(self.mk_error(format!("Error parsing float: {}", e)))
|
||||||
|
}
|
||||||
|
} else if has_dot {
|
||||||
|
match literal.parse::<f64>() {
|
||||||
|
Ok(num) => self.add_token(TokenType::Float(num), literal),
|
||||||
|
Err(e) => return Err(self.mk_error(format!("Error parsing float: {}", e)))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match literal.parse::<i64>() {
|
||||||
|
Ok(num) => self.add_token(TokenType::Int(num), literal),
|
||||||
|
Err(e) => return Err(self.mk_error(format!("Error parsing float: {}", e)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ident(&mut self) -> Result<(), ParserError> {
|
||||||
|
while !self.at_end() && (self.peek().is_ascii_alphanumeric() || self.peek() == '_') {
|
||||||
|
self.advance(false);
|
||||||
|
}
|
||||||
|
let literal = self.code[self.start..self.current].iter().collect::<String>();
|
||||||
|
let token_ty = match literal.as_ref() {
|
||||||
|
"true" => TokenType::True,
|
||||||
|
"false" => TokenType::False,
|
||||||
|
"nil" => TokenType::Nil,
|
||||||
|
"if" => TokenType::If,
|
||||||
|
"elif" => TokenType::Elif,
|
||||||
|
"else" => TokenType::Else,
|
||||||
|
"while" => TokenType::While,
|
||||||
|
"for" => TokenType::For,
|
||||||
|
"let" => TokenType::Let,
|
||||||
|
"return" => TokenType::Return,
|
||||||
|
s => TokenType::Ident(Rc::from(s))
|
||||||
|
};
|
||||||
|
self.add_token(token_ty, literal);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
55
src/lib.rs
Normal file
55
src/lib.rs
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
use std::{rc::Rc, error::Error, fmt};
|
||||||
|
|
||||||
|
pub mod token;
|
||||||
|
pub mod expr;
|
||||||
|
pub mod lexer;
|
||||||
|
pub mod parser;
|
||||||
|
pub mod value;
|
||||||
|
pub mod eval;
|
||||||
|
pub mod interpreter;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Position {
|
||||||
|
pub pos: usize,
|
||||||
|
pub line: usize,
|
||||||
|
pub col: usize,
|
||||||
|
pub file: Option<Rc<str>>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ParserError {
|
||||||
|
pub message: String,
|
||||||
|
pub pos: Position
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct RuntimeError {
|
||||||
|
pub message: String,
|
||||||
|
pub pos: Position
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ParserError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "Error: {}\n In {} at {},{}",
|
||||||
|
self.message,
|
||||||
|
self.pos.file.as_ref().map(|o| o.as_ref()).unwrap_or("<unknown>"),
|
||||||
|
self.pos.line,
|
||||||
|
self.pos.col
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for RuntimeError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "Error: {}\n In {} at {},{}",
|
||||||
|
self.message,
|
||||||
|
self.pos.file.as_ref().map(|o| o.as_ref()).unwrap_or("<unknown>"),
|
||||||
|
self.pos.line,
|
||||||
|
self.pos.col
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for ParserError {}
|
||||||
|
impl Error for RuntimeError {}
|
231
src/parser.rs
Normal file
231
src/parser.rs
Normal file
|
@ -0,0 +1,231 @@
|
||||||
|
use crate::{token::{Token, TokenType, OpType}, ParserError, expr::{Stmt, Expr}};
|
||||||
|
|
||||||
|
pub struct Parser {
|
||||||
|
tokens: Vec<Token>,
|
||||||
|
idx: usize
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parser {
|
||||||
|
pub fn new(tokens: Vec<Token>) -> Self {
|
||||||
|
Self { tokens, idx: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn at_end(&self) -> bool {
|
||||||
|
self.idx >= self.tokens.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn peek(&self) -> &Token {
|
||||||
|
&self.tokens[self.idx]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next(&mut self) -> Token {
|
||||||
|
let t = self.tokens[self.idx].clone();
|
||||||
|
self.idx += 1;
|
||||||
|
t
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mk_error<S>(&self, msg: S) -> ParserError where S: Into<String> {
|
||||||
|
let token = if self.at_end() {
|
||||||
|
self.tokens.last().unwrap()
|
||||||
|
} else {
|
||||||
|
self.peek()
|
||||||
|
};
|
||||||
|
ParserError { pos: token.pos.clone(), message: msg.into() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn err_on_eof(&self) -> Result<(), ParserError> {
|
||||||
|
if self.at_end() {
|
||||||
|
Err(self.mk_error("Unexpected EOF"))
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(&mut self) -> Result<Vec<Stmt>, ParserError> {
|
||||||
|
let mut stmts = vec![];
|
||||||
|
while !self.at_end() {
|
||||||
|
stmts.push(self.statement()?);
|
||||||
|
}
|
||||||
|
Ok(stmts)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn statement(&mut self) -> Result<Stmt, ParserError> {
|
||||||
|
let next = self.peek();
|
||||||
|
|
||||||
|
match next.ty {
|
||||||
|
// let statement
|
||||||
|
TokenType::Let => {
|
||||||
|
self.next();
|
||||||
|
let expr = self.assignment()?;
|
||||||
|
|
||||||
|
// must be followed by an assignment expression
|
||||||
|
if let Expr::Binary{lhs, rhs, op: Token{ty: TokenType::Equal,..}} = expr {
|
||||||
|
if let Expr::Ident{value: tok} = *lhs {
|
||||||
|
if self.at_end() {
|
||||||
|
return Ok(Stmt::Let{lhs: tok, rhs: Some(*rhs)})
|
||||||
|
}
|
||||||
|
let next = self.next();
|
||||||
|
return match next.ty {
|
||||||
|
TokenType::Semicolon => Ok(Stmt::Let{lhs: tok, rhs: Some(*rhs)}),
|
||||||
|
_ => Err(self.mk_error("Missing semicolon after 'let' statement".to_owned()))
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
Err(self.mk_error("Invalid expression after 'let'".to_owned()))
|
||||||
|
}
|
||||||
|
} else if let Expr::Ident{value: tok} = expr {
|
||||||
|
if self.at_end() {
|
||||||
|
return Ok(Stmt::Let{lhs: tok, rhs: None})
|
||||||
|
}
|
||||||
|
let next = self.next();
|
||||||
|
return match next.ty {
|
||||||
|
TokenType::Semicolon => Ok(Stmt::Let{lhs: tok, rhs: None}),
|
||||||
|
_ => Err(self.mk_error("Missing semicolon after 'let' statement".to_owned()))
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
Err(self.mk_error("Invalid expression after 'let'".to_owned()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if statement
|
||||||
|
TokenType::If => {
|
||||||
|
self.next();
|
||||||
|
return self.ifstmt()
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallback to an expression terminated with a semicolon
|
||||||
|
_ => {
|
||||||
|
let expr = self.assignment()?;
|
||||||
|
if self.at_end() {
|
||||||
|
return Ok(Stmt::Expr{expr})
|
||||||
|
}
|
||||||
|
|
||||||
|
let next = self.next();
|
||||||
|
|
||||||
|
return match next.ty {
|
||||||
|
TokenType::Semicolon => Ok(Stmt::Expr{expr}),
|
||||||
|
_ => Err(self.mk_error("Missing semicolon after statement"))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generic method for left-associative operators
|
||||||
|
fn expr(&mut self, op_type: OpType, next_level: fn(&mut Parser) -> Result<Expr, ParserError>) -> Result<Expr, ParserError> {
|
||||||
|
let mut expr = next_level(self)?;
|
||||||
|
while !self.at_end() && self.peek().ty.get_op_type() == Some(op_type) {
|
||||||
|
let op = self.next();
|
||||||
|
let right = next_level(self)?;
|
||||||
|
expr = Expr::Binary { lhs: Box::new(expr), rhs: Box::new(right), op };
|
||||||
|
}
|
||||||
|
Ok(expr)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn commalist(&mut self, terminator: TokenType, parse_item: fn(&mut Parser) -> Result<Expr, ParserError>) -> Result<Vec<Expr>, ParserError> {
|
||||||
|
let mut items = vec![];
|
||||||
|
while self.peek().ty != terminator {
|
||||||
|
let expr = parse_item(self)?;
|
||||||
|
items.push(expr);
|
||||||
|
if self.peek().ty == TokenType::Comma {
|
||||||
|
self.next();
|
||||||
|
} else if self.peek().ty == terminator {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
return Err(self.mk_error(format!("Expected Comma or {:?} after list", terminator)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.next();
|
||||||
|
Ok(items)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assignment(&mut self) -> Result<Expr, ParserError> {
|
||||||
|
let mut stack= vec![];
|
||||||
|
let mut expr = self.pipeline()?;
|
||||||
|
while !self.at_end() && self.peek().ty.get_op_type() == Some(OpType::Assignment) {
|
||||||
|
let op = self.next();
|
||||||
|
stack.push((expr, op));
|
||||||
|
expr = self.pipeline()?;
|
||||||
|
}
|
||||||
|
while let Some(item) = stack.pop() {
|
||||||
|
if !item.0.is_lvalue() {
|
||||||
|
return Err(self.mk_error("Invalid LValue for assignment operation"))
|
||||||
|
}
|
||||||
|
expr = Expr::Binary{ lhs: Box::new(item.0), rhs: Box::new(expr), op: item.1 };
|
||||||
|
}
|
||||||
|
Ok(expr)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pipeline(&mut self) -> Result<Expr, ParserError> {
|
||||||
|
self.expr(OpType::Pipeline, Self::additive)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn additive(&mut self) -> Result<Expr, ParserError> {
|
||||||
|
self.expr(OpType::Additive, Self::multiplicative)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn multiplicative(&mut self) -> Result<Expr, ParserError> {
|
||||||
|
self.expr(OpType::Multiplicative, Self::exponential)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Right associative, so cannot use self.expr(..)
|
||||||
|
fn exponential(&mut self) -> Result<Expr, ParserError> {
|
||||||
|
let mut stack= vec![];
|
||||||
|
let mut expr = self.unary()?;
|
||||||
|
while !self.at_end() && self.peek().ty == TokenType::Caret {
|
||||||
|
let op = self.next();
|
||||||
|
stack.push((expr, op));
|
||||||
|
expr = self.unary()?;
|
||||||
|
}
|
||||||
|
while let Some(item) = stack.pop() {
|
||||||
|
expr = Expr::Binary{ lhs: Box::new(item.0), rhs: Box::new(expr), op: item.1 };
|
||||||
|
}
|
||||||
|
Ok(expr)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unary(&mut self) -> Result<Expr, ParserError> {
|
||||||
|
self.err_on_eof()?;
|
||||||
|
if matches!(self.peek().ty, TokenType::Bang | TokenType::Minus) {
|
||||||
|
let op = self.next();
|
||||||
|
Ok(Expr::Unary { arg: Box::new(self.fncall()?), op })
|
||||||
|
} else {
|
||||||
|
self.fncall()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fncall(&mut self) -> Result<Expr, ParserError> {
|
||||||
|
let expr = self.expr_base()?;
|
||||||
|
if !self.at_end() && self.peek().ty == TokenType::LParen {
|
||||||
|
self.next();
|
||||||
|
let args = self.commalist(TokenType::RParen, Self::assignment)?;
|
||||||
|
Ok(Expr::FuncCall { func: Box::new(expr), args })
|
||||||
|
} else {
|
||||||
|
Ok(expr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expr_base(&mut self) -> Result<Expr, ParserError> {
|
||||||
|
self.err_on_eof()?;
|
||||||
|
let next = self.next();
|
||||||
|
if matches!(next.ty,
|
||||||
|
TokenType::True | TokenType::False | TokenType::Nil
|
||||||
|
| TokenType::Int(_) | TokenType::Float(_) | TokenType::ImFloat(_)
|
||||||
|
| TokenType::String(_)
|
||||||
|
) {
|
||||||
|
Ok(Expr::Literal { value: next })
|
||||||
|
} else if let TokenType::Ident(..) = next.ty {
|
||||||
|
Ok(Expr::Ident { value: next })
|
||||||
|
} else if next.ty == TokenType::LParen {
|
||||||
|
let expr = self.assignment()?;
|
||||||
|
if self.at_end() || TokenType::RParen != self.next().ty {
|
||||||
|
Err(self.mk_error("Left parenthesis never closed"))
|
||||||
|
} else {
|
||||||
|
Ok(expr)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(self.mk_error(format!("Unexpected token: {:?}", next.ty)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ifstmt(&mut self) -> Result<Stmt, ParserError> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
70
src/token.rs
Normal file
70
src/token.rs
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
use std::{fmt, rc::Rc};
|
||||||
|
|
||||||
|
use crate::Position;
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Token {
|
||||||
|
pub ty: TokenType,
|
||||||
|
pub text: String,
|
||||||
|
pub pos: Position
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Token {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{:?} @ {},{}", self.ty, self.pos.line, self.pos.col)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub enum TokenType {
|
||||||
|
Int(i64), Float(f64), ImFloat(f64), String(Rc<str>),
|
||||||
|
Ident(Rc<str>),
|
||||||
|
|
||||||
|
Plus, Minus, Star, Slash, Percent, DoubleSlash, Caret,
|
||||||
|
Bang, Amper, Pipe, DoubleAmper, DoublePipe,
|
||||||
|
|
||||||
|
Equal, PlusEqual, MinusEqual, StarEqual, SlashEqual, PercentEqual, DoubleSlashEqual, CaretEqual,
|
||||||
|
DoubleEqual, BangEqual, Greater, GreaterEqual, Less, LessEqual,
|
||||||
|
|
||||||
|
Arrow, PipeColon, PipePoint, PipeQuestion, PipeAmper,
|
||||||
|
|
||||||
|
Comma, Semicolon,
|
||||||
|
|
||||||
|
LParen, RParen, LBrack, RBrack, LBrace, RBrace,
|
||||||
|
|
||||||
|
True, False, Nil, If, Elif, Else, For, While, Let, Return
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TokenType {
|
||||||
|
pub fn get_op_type(&self) -> Option<OpType> {
|
||||||
|
match self {
|
||||||
|
Self::Plus | Self::Minus => Some(OpType::Additive),
|
||||||
|
|
||||||
|
Self::Star | Self::Slash | Self::DoubleSlash
|
||||||
|
| Self::Percent => Some(OpType::Multiplicative),
|
||||||
|
|
||||||
|
Self::Caret => Some(OpType::Exponential),
|
||||||
|
|
||||||
|
Self::PipeColon | Self::PipeAmper | Self::PipePoint
|
||||||
|
| Self::PipeQuestion => Some(OpType::Pipeline),
|
||||||
|
|
||||||
|
Self::Equal | Self::PlusEqual | Self::MinusEqual
|
||||||
|
| Self::StarEqual | Self::SlashEqual | Self::DoubleSlashEqual
|
||||||
|
| Self::CaretEqual | Self::PercentEqual => Some(OpType::Assignment),
|
||||||
|
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone,Copy,Debug,PartialEq)]
|
||||||
|
pub enum OpType {
|
||||||
|
Assignment, Pipeline, Additive, Multiplicative, Exponential
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OpType {
|
||||||
|
pub fn is_right_associative(&self) -> bool {
|
||||||
|
matches!(self, OpType::Exponential | OpType::Assignment)
|
||||||
|
}
|
||||||
|
}
|
80
src/value.rs
Normal file
80
src/value.rs
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
use std::{rc::Rc, collections::HashMap, ops::*};
|
||||||
|
|
||||||
|
type Rational = num_rational::Ratio<i64>;
|
||||||
|
type Complex = num_complex::Complex64;
|
||||||
|
|
||||||
|
macro_rules! value_from {
|
||||||
|
($variant:ident, $($kind:ty)*) => {
|
||||||
|
$(
|
||||||
|
impl From<$kind> for Value {
|
||||||
|
fn from(x: $kind) -> Self {
|
||||||
|
Self::$variant(x.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_numeric_op {
|
||||||
|
($optrait:ty, $fnname:ident, { $($bonus:tt)* }) => {
|
||||||
|
impl $optrait for &Value {
|
||||||
|
type Output = Result<Value, String>;
|
||||||
|
fn $fnname(self, other: Self) -> Self::Output {
|
||||||
|
use Value::*;
|
||||||
|
use num_traits::ToPrimitive;
|
||||||
|
const RATIO_CAST_FAIL: &'static str = "Failed to cast Rational to Float";
|
||||||
|
match (self, other) {
|
||||||
|
(Int(a), Int(b)) => Ok(a.$fnname(b).into()),
|
||||||
|
(Rational(a), Int(b)) => Ok(a.$fnname(b).into()),
|
||||||
|
(Int(a), Rational(b)) => Ok(self::Rational::from(*a).$fnname(b).into()),
|
||||||
|
(Float(a), Int(b)) => Ok(a.$fnname(*b as f64).into()),
|
||||||
|
(Int(a), Float(b)) => Ok((*a as f64).$fnname(b).into()),
|
||||||
|
(Float(a), Rational(b)) => Ok(a.$fnname(b.to_f64().ok_or(RATIO_CAST_FAIL)?).into()),
|
||||||
|
(Rational(a), Float(b)) => Ok(a.to_f64().ok_or(RATIO_CAST_FAIL)?.$fnname(b).into()),
|
||||||
|
(Float(a), Float(b)) => Ok(a.$fnname(b).into()),
|
||||||
|
(Int(a), Complex(b)) => Ok(self::Complex::from(*a as f64).$fnname(b).into()),
|
||||||
|
(Complex(a), Int(b)) => Ok(a.$fnname(self::Complex::from(*b as f64)).into()),
|
||||||
|
(Float(a), Complex(b)) => Ok(self::Complex::from(a).$fnname(b).into()),
|
||||||
|
(Complex(a), Float(b)) => Ok(a.$fnname(self::Complex::from(b)).into()),
|
||||||
|
(Rational(a), Complex(b)) => Ok(self::Complex::from(a.to_f64().ok_or(RATIO_CAST_FAIL)?).$fnname(b).into()),
|
||||||
|
(Complex(a), Rational(b)) => Ok(a.$fnname(self::Complex::from(b.to_f64().ok_or(RATIO_CAST_FAIL)?)).into()),
|
||||||
|
(Complex(a), Complex(b)) => Ok(a.$fnname(b).into()),
|
||||||
|
$($bonus)*
|
||||||
|
(lhs, rhs) => Err(format!("Unsupported operation '{}' between {:?} and {:?}", stringify!($fnname), lhs, rhs))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum Value {
|
||||||
|
Nil,
|
||||||
|
Int(i64), Float(f64), Complex(Complex), Rational(Rational),
|
||||||
|
Bool(bool),
|
||||||
|
String(Rc<str>),
|
||||||
|
List(Rc<Vec<Value>>), Map(Rc<HashMap<Value,Value>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
value_from!(Int, u8 u16 u32 i8 i16 i32 i64);
|
||||||
|
value_from!(Float, f32 f64);
|
||||||
|
value_from!(Complex, Complex);
|
||||||
|
value_from!(Rational, Rational);
|
||||||
|
value_from!(Bool, bool);
|
||||||
|
value_from!(String, String Rc<str>);
|
||||||
|
value_from!(List, Vec<Value>);
|
||||||
|
value_from!(Map, HashMap<Value,Value>);
|
||||||
|
|
||||||
|
impl_numeric_op!(Add, add, {
|
||||||
|
(String(a), String(b)) => Ok(((**a).to_owned() + b).into()),
|
||||||
|
(List(a), List(b)) => {
|
||||||
|
let mut a = (**a).clone();
|
||||||
|
a.append(&mut (**b).clone());
|
||||||
|
Ok(a.into())
|
||||||
|
},
|
||||||
|
});
|
||||||
|
impl_numeric_op!(Sub, sub, {});
|
||||||
|
impl_numeric_op!(Mul, mul, {});
|
||||||
|
impl_numeric_op!(Div, div, {});
|
||||||
|
impl_numeric_op!(Rem, rem, {});
|
3
tests/code.cxpr
Normal file
3
tests/code.cxpr
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
let a = 1;
|
||||||
|
let b = 2;
|
||||||
|
a = a + b;
|
19
tests/test.rs
Normal file
19
tests/test.rs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
#![cfg(test)]
|
||||||
|
|
||||||
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
|
use complexpr::{lexer::Lexer, parser::Parser, eval::{Environment, eval_stmt}};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
pub fn test() {
|
||||||
|
let mut lexer = Lexer::new("let a = 1 + 1; let b = a + 1;", None);
|
||||||
|
lexer.lex().unwrap();
|
||||||
|
let mut parser = Parser::new(lexer.into_tokens());
|
||||||
|
let ast = parser.parse().unwrap();
|
||||||
|
let env = Rc::new(RefCell::new(Environment::new()));
|
||||||
|
for stmt in ast {
|
||||||
|
eval_stmt(&stmt, env.clone()).unwrap();
|
||||||
|
}
|
||||||
|
println!("{:?}", env);
|
||||||
|
todo!("end of tests")
|
||||||
|
}
|
Loading…
Reference in a new issue