Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
13458aac11 | ||
|
|
9d466d7896 |
23 changed files with 339 additions and 2572 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,2 +1 @@
|
||||||
/target
|
/target
|
||||||
/proptest-regressions
|
|
||||||
|
|
|
||||||
489
Cargo.lock
generated
489
Cargo.lock
generated
|
|
@ -2,39 +2,12 @@
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 4
|
version = 4
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "allocator-api2"
|
|
||||||
version = "0.2.21"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bit-set"
|
|
||||||
version = "0.8.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3"
|
|
||||||
dependencies = [
|
|
||||||
"bit-vec",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bit-vec"
|
|
||||||
version = "0.8.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bitflags"
|
|
||||||
version = "2.7.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1be3f42a67d6d345ecd59f675f3f012d6974981560836e938c22b424b85ce1be"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
|
|
@ -47,15 +20,6 @@ version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "deranged"
|
|
||||||
version = "0.3.11"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
|
|
||||||
dependencies = [
|
|
||||||
"powerfmt",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "derive_more"
|
name = "derive_more"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
|
|
@ -77,49 +41,6 @@ dependencies = [
|
||||||
"unicode-xid",
|
"unicode-xid",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "equivalent"
|
|
||||||
version = "1.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "errno"
|
|
||||||
version = "0.3.10"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"windows-sys",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "fastrand"
|
|
||||||
version = "2.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "fnv"
|
|
||||||
version = "1.0.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "foldhash"
|
|
||||||
version = "0.1.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "generic-array"
|
|
||||||
version = "1.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e8c8444bc9d71b935156cc0ccab7f622180808af7867b1daae6547d773591703"
|
|
||||||
dependencies = [
|
|
||||||
"typenum",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.2.15"
|
version = "0.2.15"
|
||||||
|
|
@ -131,29 +52,6 @@ dependencies = [
|
||||||
"wasi",
|
"wasi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hashbrown"
|
|
||||||
version = "0.15.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
|
||||||
dependencies = [
|
|
||||||
"allocator-api2",
|
|
||||||
"equivalent",
|
|
||||||
"foldhash",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "itoa"
|
|
||||||
version = "1.0.14"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "lazy_static"
|
|
||||||
version = "1.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.169"
|
version = "0.2.169"
|
||||||
|
|
@ -161,26 +59,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "mathvis"
|
||||||
version = "0.4.15"
|
version = "0.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "mathvis-old"
|
|
||||||
version = "0.4.0"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"derive_more",
|
"derive_more",
|
||||||
"generic-array",
|
|
||||||
"hashbrown",
|
|
||||||
"nom",
|
"nom",
|
||||||
"num",
|
"num",
|
||||||
"paste",
|
|
||||||
"proptest",
|
|
||||||
"rand",
|
"rand",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tracing",
|
|
||||||
"tracing-subscriber",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -205,16 +91,6 @@ dependencies = [
|
||||||
"minimal-lexical",
|
"minimal-lexical",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "nu-ansi-term"
|
|
||||||
version = "0.46.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
|
|
||||||
dependencies = [
|
|
||||||
"overload",
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num"
|
name = "num"
|
||||||
version = "0.4.3"
|
version = "0.4.3"
|
||||||
|
|
@ -248,12 +124,6 @@ dependencies = [
|
||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num-conv"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-integer"
|
name = "num-integer"
|
||||||
version = "0.1.46"
|
version = "0.1.46"
|
||||||
|
|
@ -294,45 +164,6 @@ dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num_threads"
|
|
||||||
version = "0.1.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "once_cell"
|
|
||||||
version = "1.20.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "overload"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "paste"
|
|
||||||
version = "1.0.15"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pin-project-lite"
|
|
||||||
version = "0.2.16"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "powerfmt"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppv-lite86"
|
name = "ppv-lite86"
|
||||||
version = "0.2.20"
|
version = "0.2.20"
|
||||||
|
|
@ -344,39 +175,13 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.93"
|
version = "1.0.92"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
|
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "proptest"
|
|
||||||
version = "1.6.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "14cae93065090804185d3b75f0bf93b8eeda30c7a9b4a33d3bdb3988d6229e50"
|
|
||||||
dependencies = [
|
|
||||||
"bit-set",
|
|
||||||
"bit-vec",
|
|
||||||
"bitflags",
|
|
||||||
"lazy_static",
|
|
||||||
"num-traits",
|
|
||||||
"rand",
|
|
||||||
"rand_chacha",
|
|
||||||
"rand_xorshift",
|
|
||||||
"regex-syntax",
|
|
||||||
"rusty-fork",
|
|
||||||
"tempfile",
|
|
||||||
"unarray",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "quick-error"
|
|
||||||
version = "1.2.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.38"
|
version = "1.0.38"
|
||||||
|
|
@ -416,75 +221,6 @@ dependencies = [
|
||||||
"getrandom",
|
"getrandom",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_xorshift"
|
|
||||||
version = "0.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f"
|
|
||||||
dependencies = [
|
|
||||||
"rand_core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "regex-syntax"
|
|
||||||
version = "0.8.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustix"
|
|
||||||
version = "0.38.43"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags",
|
|
||||||
"errno",
|
|
||||||
"libc",
|
|
||||||
"linux-raw-sys",
|
|
||||||
"windows-sys",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rusty-fork"
|
|
||||||
version = "0.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f"
|
|
||||||
dependencies = [
|
|
||||||
"fnv",
|
|
||||||
"quick-error",
|
|
||||||
"tempfile",
|
|
||||||
"wait-timeout",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde"
|
|
||||||
version = "1.0.217"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
|
|
||||||
dependencies = [
|
|
||||||
"serde_derive",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde_derive"
|
|
||||||
version = "1.0.217"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "sharded-slab"
|
|
||||||
version = "0.1.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
|
|
||||||
dependencies = [
|
|
||||||
"lazy_static",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.96"
|
version = "2.0.96"
|
||||||
|
|
@ -496,20 +232,6 @@ dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tempfile"
|
|
||||||
version = "3.15.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"fastrand",
|
|
||||||
"getrandom",
|
|
||||||
"once_cell",
|
|
||||||
"rustix",
|
|
||||||
"windows-sys",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "2.0.11"
|
version = "2.0.11"
|
||||||
|
|
@ -530,105 +252,6 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "thread_local"
|
|
||||||
version = "1.1.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"once_cell",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "time"
|
|
||||||
version = "0.3.37"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21"
|
|
||||||
dependencies = [
|
|
||||||
"deranged",
|
|
||||||
"itoa",
|
|
||||||
"libc",
|
|
||||||
"num-conv",
|
|
||||||
"num_threads",
|
|
||||||
"powerfmt",
|
|
||||||
"serde",
|
|
||||||
"time-core",
|
|
||||||
"time-macros",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "time-core"
|
|
||||||
version = "0.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "time-macros"
|
|
||||||
version = "0.2.19"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de"
|
|
||||||
dependencies = [
|
|
||||||
"num-conv",
|
|
||||||
"time-core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tracing"
|
|
||||||
version = "0.1.41"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
|
|
||||||
dependencies = [
|
|
||||||
"pin-project-lite",
|
|
||||||
"tracing-attributes",
|
|
||||||
"tracing-core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tracing-attributes"
|
|
||||||
version = "0.1.28"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tracing-core"
|
|
||||||
version = "0.1.33"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
|
|
||||||
dependencies = [
|
|
||||||
"once_cell",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tracing-subscriber"
|
|
||||||
version = "0.3.19"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
|
|
||||||
dependencies = [
|
|
||||||
"nu-ansi-term",
|
|
||||||
"sharded-slab",
|
|
||||||
"thread_local",
|
|
||||||
"time",
|
|
||||||
"tracing-core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "typenum"
|
|
||||||
version = "1.17.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unarray"
|
|
||||||
version = "0.1.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.14"
|
version = "1.0.14"
|
||||||
|
|
@ -641,116 +264,12 @@ version = "0.2.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wait-timeout"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.11.0+wasi-snapshot-preview1"
|
version = "0.11.0+wasi-snapshot-preview1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
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.59.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
|
||||||
dependencies = [
|
|
||||||
"windows-targets",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-targets"
|
|
||||||
version = "0.52.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
|
||||||
dependencies = [
|
|
||||||
"windows_aarch64_gnullvm",
|
|
||||||
"windows_aarch64_msvc",
|
|
||||||
"windows_i686_gnu",
|
|
||||||
"windows_i686_gnullvm",
|
|
||||||
"windows_i686_msvc",
|
|
||||||
"windows_x86_64_gnu",
|
|
||||||
"windows_x86_64_gnullvm",
|
|
||||||
"windows_x86_64_msvc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_aarch64_gnullvm"
|
|
||||||
version = "0.52.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_aarch64_msvc"
|
|
||||||
version = "0.52.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_i686_gnu"
|
|
||||||
version = "0.52.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_i686_gnullvm"
|
|
||||||
version = "0.52.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_i686_msvc"
|
|
||||||
version = "0.52.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_gnu"
|
|
||||||
version = "0.52.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_gnullvm"
|
|
||||||
version = "0.52.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_msvc"
|
|
||||||
version = "0.52.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy"
|
name = "zerocopy"
|
||||||
version = "0.7.35"
|
version = "0.7.35"
|
||||||
|
|
|
||||||
16
Cargo.toml
16
Cargo.toml
|
|
@ -1,27 +1,15 @@
|
||||||
[package]
|
[package]
|
||||||
name = "mathvis"
|
name = "mathvis"
|
||||||
version = "0.4.0"
|
version = "0.3.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
derive_more = {version = "1.0.0", features = ["display", "as_ref", "deref"]}
|
derive_more = {version = "1.0.0", features = ["display"]}
|
||||||
nom = "7.1.3"
|
nom = "7.1.3"
|
||||||
num = "0.4.3"
|
num = "0.4.3"
|
||||||
thiserror = "2.0.11"
|
thiserror = "2.0.11"
|
||||||
generic-array = "1.2.0"
|
|
||||||
tracing = { version = "0.1.41", default-features = false, features = ["attributes"] }
|
|
||||||
hashbrown = "0.15.2"
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
paste = "1.0.15"
|
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
tracing-subscriber = { version = "0.3.19", default-features = false, features = ["std", "fmt", "time", "local-time", "ansi"] }
|
|
||||||
proptest = "1.6.0"
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = []
|
|
||||||
|
|
||||||
[lints.rust]
|
|
||||||
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(verbose_logging)'] }
|
|
||||||
|
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "mathvis-old"
|
|
||||||
version = "0.4.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
derive_more = {version = "1.0.0", features = ["display", "as_ref", "deref"]}
|
|
||||||
nom = "7.1.3"
|
|
||||||
num = "0.4.3"
|
|
||||||
thiserror = "2.0.11"
|
|
||||||
generic-array = "1.2.0"
|
|
||||||
tracing = { version = "0.1.41", default-features = false, features = ["attributes"] }
|
|
||||||
hashbrown = "0.15.2"
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
paste = "1.0.15"
|
|
||||||
rand = "0.8.5"
|
|
||||||
tracing-subscriber = { version = "0.3.19", default-features = false, features = ["std", "fmt", "time", "local-time", "ansi"] }
|
|
||||||
proptest = "1.6.0"
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = []
|
|
||||||
|
|
||||||
[lints.rust]
|
|
||||||
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(verbose_logging)'] }
|
|
||||||
|
|
@ -1,300 +0,0 @@
|
||||||
//! This module defines lexer and structs relevant to it.
|
|
||||||
|
|
||||||
use alloc::{
|
|
||||||
borrow::ToOwned,
|
|
||||||
string::{String, ToString},
|
|
||||||
vec::Vec,
|
|
||||||
};
|
|
||||||
use nom::{
|
|
||||||
branch::alt,
|
|
||||||
bytes::complete::tag,
|
|
||||||
character::complete::{alpha1, alphanumeric1, char as pchar, one_of},
|
|
||||||
combinator::{map, recognize},
|
|
||||||
error::Error,
|
|
||||||
multi::{fold_many1, many0, many0_count},
|
|
||||||
number::complete::double,
|
|
||||||
sequence::{pair, preceded},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
display_opt,
|
|
||||||
lexer_v2::{
|
|
||||||
number::Number,
|
|
||||||
token::{SpecialKind, Token},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
use core::str::FromStr;
|
|
||||||
|
|
||||||
type Res<'l, T = OldToken> = nom::IResult<&'l str, T, Error<&'l str>>;
|
|
||||||
|
|
||||||
/// Represents type of grouping.
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
|
||||||
pub enum GroupingType {
|
|
||||||
/// ()
|
|
||||||
Parentheses,
|
|
||||||
/// []
|
|
||||||
Brackets,
|
|
||||||
/// {}
|
|
||||||
Braces,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents operators (not necessary corresponding to actual [`crate::evaluation_tree::Operator`]s).
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
|
||||||
pub enum Operator {
|
|
||||||
Plus,
|
|
||||||
Minus,
|
|
||||||
Star,
|
|
||||||
Slash,
|
|
||||||
Cap,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents a single token.
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
pub enum OldToken {
|
|
||||||
GroupOpen(GroupingType),
|
|
||||||
GroupClose(GroupingType),
|
|
||||||
Operator(Operator),
|
|
||||||
Comma,
|
|
||||||
ImaginaryUnit,
|
|
||||||
Number(f64),
|
|
||||||
Identifier(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, thiserror::Error)]
|
|
||||||
#[error("Unsupported token: {_0}")]
|
|
||||||
pub struct UnsupportedToken<'s>(Token<&'s str, &'s str, &'s str>);
|
|
||||||
|
|
||||||
impl<'s> TryFrom<Token<&'s str, &'s str, &'s str>> for OldToken {
|
|
||||||
type Error = UnsupportedToken<'s>;
|
|
||||||
|
|
||||||
fn try_from(value: Token<&'s str, &'s str, &'s str>) -> Result<Self, Self::Error> {
|
|
||||||
let token = match value {
|
|
||||||
Token::Grouping {
|
|
||||||
kind: crate::lexer_v2::token::GroupingKind::Parentheses,
|
|
||||||
side: crate::lexer_v2::token::GroupingSide::Left,
|
|
||||||
} => Self::GroupOpen(GroupingType::Parentheses),
|
|
||||||
Token::Grouping {
|
|
||||||
kind: crate::lexer_v2::token::GroupingKind::Brackets,
|
|
||||||
side: crate::lexer_v2::token::GroupingSide::Left,
|
|
||||||
} => Self::GroupOpen(GroupingType::Brackets),
|
|
||||||
Token::Grouping {
|
|
||||||
kind: crate::lexer_v2::token::GroupingKind::Braces,
|
|
||||||
side: crate::lexer_v2::token::GroupingSide::Left,
|
|
||||||
} => Self::GroupOpen(GroupingType::Braces),
|
|
||||||
Token::Grouping {
|
|
||||||
kind: crate::lexer_v2::token::GroupingKind::Parentheses,
|
|
||||||
side: crate::lexer_v2::token::GroupingSide::Right,
|
|
||||||
} => Self::GroupClose(GroupingType::Parentheses),
|
|
||||||
Token::Grouping {
|
|
||||||
kind: crate::lexer_v2::token::GroupingKind::Brackets,
|
|
||||||
side: crate::lexer_v2::token::GroupingSide::Right,
|
|
||||||
} => Self::GroupClose(GroupingType::Brackets),
|
|
||||||
Token::Grouping {
|
|
||||||
kind: crate::lexer_v2::token::GroupingKind::Braces,
|
|
||||||
side: crate::lexer_v2::token::GroupingSide::Right,
|
|
||||||
} => Self::GroupClose(GroupingType::Braces),
|
|
||||||
Token::Special(SpecialKind::Comma) => Self::Comma,
|
|
||||||
Token::Special(SpecialKind::Plus) => Self::Operator(Operator::Plus),
|
|
||||||
Token::Special(SpecialKind::Minus) => Self::Operator(Operator::Minus),
|
|
||||||
Token::Special(SpecialKind::Star) => Self::Operator(Operator::Star),
|
|
||||||
Token::Special(SpecialKind::Slash) => Self::Operator(Operator::Slash),
|
|
||||||
Token::Special(SpecialKind::Cap) => Self::Operator(Operator::Cap),
|
|
||||||
Token::Number(Number::Integer { int, exp }) => Self::Number(
|
|
||||||
f64::from_str(&format!("{int}.0{}", display_opt(&exp)))
|
|
||||||
.map_err(|_| UnsupportedToken(value))?,
|
|
||||||
),
|
|
||||||
Token::Number(Number::Decimal {
|
|
||||||
int,
|
|
||||||
point,
|
|
||||||
frac,
|
|
||||||
exp,
|
|
||||||
}) => Self::Number(
|
|
||||||
f64::from_str(&format!("{int}{point}{frac}{}", display_opt(&exp)))
|
|
||||||
.map_err(|_| UnsupportedToken(value))?,
|
|
||||||
),
|
|
||||||
Token::Number(Number::DecimalInt { int, point, exp }) => Self::Number(
|
|
||||||
f64::from_str(&format!("{int}{point}0{}", display_opt(&exp)))
|
|
||||||
.map_err(|_| UnsupportedToken(value))?,
|
|
||||||
),
|
|
||||||
Token::Number(Number::DecimalFraction { point, frac, exp }) => Self::Number(
|
|
||||||
f64::from_str(&format!("0{point}{frac}{}", display_opt(&exp)))
|
|
||||||
.map_err(|_| UnsupportedToken(value))?,
|
|
||||||
),
|
|
||||||
Token::Word(word) if *word == "i" => Self::ImaginaryUnit,
|
|
||||||
Token::Word(word) => Self::Identifier(word.to_string()),
|
|
||||||
_ => return Err(UnsupportedToken(value)),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(token)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents token type (used for debug)
|
|
||||||
#[derive(Debug, PartialEq, derive_more::Display)]
|
|
||||||
pub enum TokenType {
|
|
||||||
Grouping,
|
|
||||||
Operator,
|
|
||||||
Comma,
|
|
||||||
Constant,
|
|
||||||
Identifier,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn paren_open(i: &str) -> Res {
|
|
||||||
map(one_of("([{"), |c| {
|
|
||||||
OldToken::GroupOpen(match c {
|
|
||||||
'(' => GroupingType::Parentheses,
|
|
||||||
'[' => GroupingType::Brackets,
|
|
||||||
'{' => GroupingType::Braces,
|
|
||||||
_ => unreachable!(),
|
|
||||||
})
|
|
||||||
})(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn paren_close(i: &str) -> Res {
|
|
||||||
map(one_of(")]}"), |c| {
|
|
||||||
OldToken::GroupClose(match c {
|
|
||||||
')' => GroupingType::Parentheses,
|
|
||||||
']' => GroupingType::Brackets,
|
|
||||||
'}' => GroupingType::Braces,
|
|
||||||
_ => unreachable!(),
|
|
||||||
})
|
|
||||||
})(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn operator(i: &str) -> Res {
|
|
||||||
map(one_of("+-*/^"), |c| {
|
|
||||||
OldToken::Operator(match c {
|
|
||||||
'+' => Operator::Plus,
|
|
||||||
'-' => Operator::Minus,
|
|
||||||
'*' => Operator::Star,
|
|
||||||
'/' => Operator::Slash,
|
|
||||||
'^' => Operator::Cap,
|
|
||||||
_ => unreachable!(),
|
|
||||||
})
|
|
||||||
})(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn comma(i: &str) -> Res {
|
|
||||||
map(pchar(','), |_| OldToken::Comma)(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn imaginary_unit(i: &str) -> Res {
|
|
||||||
map(pchar('i'), |_| OldToken::ImaginaryUnit)(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
// source: https://github.com/rust-bakery/nom/blob/main/doc/nom_recipes.md#identifiers
|
|
||||||
fn identifier(i: &str) -> Res {
|
|
||||||
map(
|
|
||||||
recognize(pair(
|
|
||||||
alt((alpha1, tag("_"))),
|
|
||||||
many0_count(alt((alphanumeric1, tag("_")))),
|
|
||||||
)),
|
|
||||||
|name: &str| OldToken::Identifier(name.to_owned()),
|
|
||||||
)(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn number(i: &str) -> Res {
|
|
||||||
map(double, OldToken::Number)(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn whitespace(i: &str) -> Res<()> {
|
|
||||||
Ok((many0(one_of(" \t\x0c\n"))(i)?.0, ()))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// lexes the input
|
|
||||||
pub fn lex(i: &str) -> Res<Vec<OldToken>> {
|
|
||||||
fold_many1(
|
|
||||||
preceded(
|
|
||||||
whitespace,
|
|
||||||
alt((
|
|
||||||
operator,
|
|
||||||
number,
|
|
||||||
paren_open,
|
|
||||||
paren_close,
|
|
||||||
comma,
|
|
||||||
imaginary_unit,
|
|
||||||
identifier,
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
Vec::new,
|
|
||||||
|mut acc, token| {
|
|
||||||
acc.push(token);
|
|
||||||
acc
|
|
||||||
},
|
|
||||||
)(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use alloc::borrow::ToOwned;
|
|
||||||
use alloc::vec::Vec;
|
|
||||||
|
|
||||||
use crate::lexer::GroupingType;
|
|
||||||
use crate::lexer_v2::token::Token;
|
|
||||||
use crate::lexer_v2::StrTokenParser;
|
|
||||||
|
|
||||||
use super::lex;
|
|
||||||
use super::OldToken;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn tokens() {
|
|
||||||
// arrange
|
|
||||||
let input = "2 + x^2 - (3x^2 - 7y / sin[z])";
|
|
||||||
|
|
||||||
// act
|
|
||||||
let tokens = lex(input);
|
|
||||||
|
|
||||||
// assert
|
|
||||||
assert_eq!(
|
|
||||||
tokens,
|
|
||||||
Ok((
|
|
||||||
"",
|
|
||||||
vec![
|
|
||||||
OldToken::Number(2f64),
|
|
||||||
OldToken::Operator(crate::lexer::Operator::Plus),
|
|
||||||
OldToken::Identifier("x".to_owned()),
|
|
||||||
OldToken::Operator(crate::lexer::Operator::Cap),
|
|
||||||
OldToken::Number(2f64),
|
|
||||||
OldToken::Operator(crate::lexer::Operator::Minus),
|
|
||||||
OldToken::GroupOpen(GroupingType::Parentheses),
|
|
||||||
OldToken::Number(3f64),
|
|
||||||
OldToken::Identifier("x".to_owned()),
|
|
||||||
OldToken::Operator(crate::lexer::Operator::Cap),
|
|
||||||
OldToken::Number(2f64),
|
|
||||||
OldToken::Operator(crate::lexer::Operator::Minus),
|
|
||||||
OldToken::Number(7f64),
|
|
||||||
OldToken::Identifier("y".to_owned()),
|
|
||||||
OldToken::Operator(crate::lexer::Operator::Slash),
|
|
||||||
OldToken::Identifier("sin".to_owned()),
|
|
||||||
OldToken::GroupOpen(GroupingType::Brackets),
|
|
||||||
OldToken::Identifier("z".to_owned()),
|
|
||||||
OldToken::GroupClose(GroupingType::Brackets),
|
|
||||||
OldToken::GroupClose(GroupingType::Parentheses),
|
|
||||||
]
|
|
||||||
))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn tokens_new_to_old_same() {
|
|
||||||
crate::log_init();
|
|
||||||
|
|
||||||
// arrange
|
|
||||||
let input = "2 + x^2 - (3x^2 - 7y / sin[z])";
|
|
||||||
|
|
||||||
// act
|
|
||||||
let tokens = lex(input).expect("Must be a valid input").1;
|
|
||||||
let new_tokens = StrTokenParser::<Token>::new(input)
|
|
||||||
.inspect(|new_token| trace!(?new_token))
|
|
||||||
.map(|p| {
|
|
||||||
p.expect("Should not encounter errors")
|
|
||||||
.try_into()
|
|
||||||
.expect("Should not have unsupported tokens")
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
// assert
|
|
||||||
assert_eq!(tokens, new_tokens);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,68 +0,0 @@
|
||||||
use core::marker::PhantomData;
|
|
||||||
|
|
||||||
pub mod number;
|
|
||||||
pub mod token;
|
|
||||||
pub mod word;
|
|
||||||
|
|
||||||
pub trait TokenInput {
|
|
||||||
fn is_empty(&self) -> bool;
|
|
||||||
fn on_invalid(self) -> Self;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Parseable<'s>: Sized + 's {
|
|
||||||
fn try_parse(input: &'s str) -> Option<(Self, &'s str)>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'s, T> StrTokenParser<'s, T> {
|
|
||||||
pub fn new(input: &'s str) -> Self {
|
|
||||||
Self {
|
|
||||||
input,
|
|
||||||
_phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct StrTokenParser<'s, T> {
|
|
||||||
input: &'s str,
|
|
||||||
_phantom: PhantomData<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn skip_err(s: &str) -> (&str, &str) {
|
|
||||||
let next_whitespace = s
|
|
||||||
.char_indices()
|
|
||||||
.find_map(|(pos, ch)| ch.is_whitespace().then_some(pos))
|
|
||||||
.unwrap_or(s.len());
|
|
||||||
s.split_at(next_whitespace)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'s, T: Parseable<'s>> Iterator for StrTokenParser<'s, T> {
|
|
||||||
type Item = Result<T, &'s str>;
|
|
||||||
|
|
||||||
#[tracing::instrument(level = "trace", skip(self), name = "token_parser", fields(input = self.input))]
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
let ws = self.input.trim_start();
|
|
||||||
trace!(ws);
|
|
||||||
if ws.is_empty() {
|
|
||||||
// no more tokens, exhausted the input
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some((token, rest)) = T::try_parse(ws) {
|
|
||||||
// successful token parse, return the token
|
|
||||||
trace!("Parsed token");
|
|
||||||
self.input = rest;
|
|
||||||
Some(Ok(token))
|
|
||||||
} else {
|
|
||||||
// failed to parse the token
|
|
||||||
//
|
|
||||||
// skip to next whitespace character
|
|
||||||
let (erroneous, new_input) = skip_err(ws);
|
|
||||||
self.input = new_input;
|
|
||||||
trace!(erroneous);
|
|
||||||
Some(Err(erroneous))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod prop_tests;
|
|
||||||
|
|
@ -1,221 +0,0 @@
|
||||||
use core::{fmt::Display, ops::Not};
|
|
||||||
|
|
||||||
use crate::display_opt;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn valid_sign(ch: char) -> bool {
|
|
||||||
matches!(ch, '+' | '-')
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn valid_exponent_marker(ch: char) -> bool {
|
|
||||||
matches!(ch, 'e' | 'E')
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn valid_decimal_point(ch: char) -> bool {
|
|
||||||
matches!(ch, '.')
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(
|
|
||||||
Debug,
|
|
||||||
Clone,
|
|
||||||
Copy,
|
|
||||||
Hash,
|
|
||||||
PartialEq,
|
|
||||||
Eq,
|
|
||||||
derive_more::AsRef,
|
|
||||||
derive_more::Deref,
|
|
||||||
derive_more::Display,
|
|
||||||
)]
|
|
||||||
#[display("{_0}")]
|
|
||||||
pub struct Digits<D>(D);
|
|
||||||
|
|
||||||
impl<'s> Digits<&'s str> {
|
|
||||||
#[tracing::instrument(level = "trace", ret, skip(input), fields(digits_input = input))]
|
|
||||||
fn parse(input: &'s str) -> Option<(Self, &'s str)> {
|
|
||||||
let end_pos = input
|
|
||||||
.char_indices()
|
|
||||||
.find_map(|(pos, ch)| ch.is_ascii_digit().not().then_some(pos))
|
|
||||||
.unwrap_or(input.len());
|
|
||||||
|
|
||||||
if end_pos == 0 {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some((Self(&input[..end_pos]), &input[end_pos..]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, derive_more::Display)]
|
|
||||||
#[display("{e}{}{digits}", if let Some(s) = sign.as_ref() { s as &dyn Display } else { &"" as &dyn Display })]
|
|
||||||
pub struct Exponent<C, D> {
|
|
||||||
e: C,
|
|
||||||
sign: Option<C>,
|
|
||||||
digits: Digits<D>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'s> Exponent<&'s str, &'s str> {
|
|
||||||
#[tracing::instrument(level = "trace", ret, skip(input), fields(exponent_input = input))]
|
|
||||||
fn parse(input: &'s str) -> Option<(Self, &'s str)> {
|
|
||||||
let mut char_inds = input.char_indices();
|
|
||||||
// if there's no first char, there's no exponent
|
|
||||||
let (_, e_marker) = char_inds.next()?;
|
|
||||||
if !valid_exponent_marker(e_marker) {
|
|
||||||
// does not start with an exponent marker, bail
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// exponent marker cannot go by itself
|
|
||||||
let (mut pos, maybe_sign) = char_inds.next()?;
|
|
||||||
let e = &input[..pos];
|
|
||||||
// figure out sign
|
|
||||||
let sign;
|
|
||||||
if valid_sign(maybe_sign) {
|
|
||||||
// second char is a valid sign, query for next one
|
|
||||||
let (sign_end, _) = char_inds.next()?;
|
|
||||||
sign = Some(&input[pos..sign_end]);
|
|
||||||
pos = sign_end;
|
|
||||||
} else {
|
|
||||||
sign = None;
|
|
||||||
}
|
|
||||||
let (digits, rest) = Digits::parse(&input[pos..])?;
|
|
||||||
Some((Self { e, sign, digits }, rest))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, derive_more::Display)]
|
|
||||||
pub enum Number<C, D> {
|
|
||||||
#[display("int({int}e{})", display_opt(exp))]
|
|
||||||
Integer {
|
|
||||||
int: Digits<D>,
|
|
||||||
exp: Option<Exponent<C, D>>,
|
|
||||||
},
|
|
||||||
#[display("float({int}{point}{frac}{})", display_opt(exp))]
|
|
||||||
Decimal {
|
|
||||||
int: Digits<D>,
|
|
||||||
point: C,
|
|
||||||
frac: Digits<D>,
|
|
||||||
exp: Option<Exponent<C, D>>,
|
|
||||||
},
|
|
||||||
#[display("float({int}{point}{})", display_opt(exp))]
|
|
||||||
DecimalInt {
|
|
||||||
int: Digits<D>,
|
|
||||||
point: C,
|
|
||||||
// no frac part
|
|
||||||
exp: Option<Exponent<C, D>>,
|
|
||||||
},
|
|
||||||
#[display("float({point}{})", display_opt(exp))]
|
|
||||||
DecimalFraction {
|
|
||||||
// no int part
|
|
||||||
point: C,
|
|
||||||
frac: Digits<D>,
|
|
||||||
exp: Option<Exponent<C, D>>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'s> Number<&'s str, &'s str> {
|
|
||||||
#[tracing::instrument(level = "trace", skip(input), fields(number_input = input), ret)]
|
|
||||||
pub fn parse(mut input: &'s str) -> Option<(Self, &'s str)> {
|
|
||||||
let int_part: Option<Digits<&str>>;
|
|
||||||
|
|
||||||
let frac_part: Option<Digits<&str>>;
|
|
||||||
let exp: Option<Exponent<&str, &str>>;
|
|
||||||
|
|
||||||
if let Some((digits, rest)) = Digits::parse(input) {
|
|
||||||
int_part = Some(digits);
|
|
||||||
input = rest;
|
|
||||||
trace!(?int_part, input);
|
|
||||||
} else {
|
|
||||||
int_part = None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let point = 'point: {
|
|
||||||
// try parsing decimal point. if it's only a (optional) sign and integer part - it's a normal integer
|
|
||||||
let mut char_inds = input.char_indices();
|
|
||||||
let Some((_, maybe_point)) = char_inds.next() else {
|
|
||||||
break 'point None;
|
|
||||||
};
|
|
||||||
trace!(%maybe_point);
|
|
||||||
|
|
||||||
// try parsing the sign
|
|
||||||
if !valid_decimal_point(maybe_point) {
|
|
||||||
break 'point None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is a valid point char, find there it ends
|
|
||||||
let point;
|
|
||||||
if let Some((point_end, _)) = char_inds.next() {
|
|
||||||
point = &input[..point_end];
|
|
||||||
input = &input[point_end..];
|
|
||||||
} else {
|
|
||||||
point = input;
|
|
||||||
input = "";
|
|
||||||
};
|
|
||||||
Some(point)
|
|
||||||
};
|
|
||||||
trace!(?point, input);
|
|
||||||
|
|
||||||
// try parsing fractional part
|
|
||||||
if let Some((digits, rest)) = Digits::parse(input) {
|
|
||||||
frac_part = Some(digits);
|
|
||||||
input = rest;
|
|
||||||
trace!(?frac_part, input);
|
|
||||||
} else {
|
|
||||||
frac_part = None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// try parsing exponent
|
|
||||||
if let Some((exponent, rest)) = Exponent::parse(input) {
|
|
||||||
exp = Some(exponent);
|
|
||||||
input = rest;
|
|
||||||
trace!(?exp, input);
|
|
||||||
} else {
|
|
||||||
exp = None;
|
|
||||||
}
|
|
||||||
|
|
||||||
trace!(?int_part, ?point, ?frac_part);
|
|
||||||
match (int_part, point, frac_part) {
|
|
||||||
(Some(int), None, None) => Some((Self::Integer { int, exp }, input)),
|
|
||||||
(Some(int), Some(point), Some(frac)) => Some((
|
|
||||||
Self::Decimal {
|
|
||||||
int,
|
|
||||||
point,
|
|
||||||
frac,
|
|
||||||
exp,
|
|
||||||
},
|
|
||||||
input,
|
|
||||||
)),
|
|
||||||
(None, Some(point), Some(frac)) => {
|
|
||||||
Some((Self::DecimalFraction { point, frac, exp }, input))
|
|
||||||
}
|
|
||||||
(Some(int), Some(point), None) => Some((Self::DecimalInt { int, point, exp }, input)),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! number {
|
|
||||||
(? $input:literal) => {
|
|
||||||
'number: {
|
|
||||||
let Some((number, rest)) = $crate::lexer_v2::number::Number::parse(stringify!($input))
|
|
||||||
else {
|
|
||||||
break 'number None;
|
|
||||||
};
|
|
||||||
if !rest.trim().is_empty() {
|
|
||||||
break 'number None;
|
|
||||||
}
|
|
||||||
Some(number)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
($input:literal) => {
|
|
||||||
number!(?$input).expect("Invalid number")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod prop_tests;
|
|
||||||
|
|
@ -1,121 +0,0 @@
|
||||||
use core::ops::Not;
|
|
||||||
|
|
||||||
use super::{Digits, Exponent, Number};
|
|
||||||
|
|
||||||
use proptest::{prelude::ProptestConfig, prop_assert_eq, proptest};
|
|
||||||
|
|
||||||
use alloc::string::String;
|
|
||||||
|
|
||||||
proptest! {
|
|
||||||
#![proptest_config(ProptestConfig {
|
|
||||||
cases: 10_000,
|
|
||||||
max_default_size_range: 200,
|
|
||||||
..ProptestConfig::default()
|
|
||||||
})]
|
|
||||||
#[test]
|
|
||||||
fn digits_never_panics(input: String) {
|
|
||||||
let _ = Digits::parse(&input);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn digits_never_parse_letters(s in "[^0-9]+") {
|
|
||||||
prop_assert_eq!(Digits::parse(&s), None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn digits_always_parse_digits(digits in "[0-9]+", garbage in "[^0-9]+") {
|
|
||||||
let input = format!("{digits}{garbage}");
|
|
||||||
prop_assert_eq!(Digits::parse(&input), Some((Digits(digits.as_str()), garbage.as_str())));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn exponent_never_panics(input: String) {
|
|
||||||
let _ = Exponent::parse(&input);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn exponent_valid_parses(e in "[eE]", sign in "[+-]?", digits in "[0-9]+", sep in "[^0-9]", garbage: String) {
|
|
||||||
let input = format!("{e}{sign}{digits}{sep}{garbage}");
|
|
||||||
let garbage = format!("{sep}{garbage}");
|
|
||||||
prop_assert_eq!(
|
|
||||||
Exponent::parse(&input),
|
|
||||||
Some((
|
|
||||||
Exponent{
|
|
||||||
e: e.as_str(),
|
|
||||||
sign: sign.is_empty().not().then_some(sign.as_str()),
|
|
||||||
digits: Digits(digits.as_str())
|
|
||||||
},
|
|
||||||
garbage.as_str())
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn number_never_panics(input: String) {
|
|
||||||
let _ = Number::parse(&input);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn number_parses_ints(digits in "[0-9]+", sep in "[^eE.0-9]", garbage: String) {
|
|
||||||
let input = format!("{digits}{sep}{garbage}");
|
|
||||||
let garbage = format!("{sep}{garbage}");
|
|
||||||
prop_assert_eq!(
|
|
||||||
Number::parse(&input),
|
|
||||||
Some((Number::Integer { int: Digits(digits.as_str()), exp: None }, garbage.as_str()))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn number_parses_doubles(mut part1 in "[0-9]+", mut part2 in "[0-9]*", swap_parts: bool, e in "[eE]", exponent_sign in "[+-]?", exponent in "[0-9]+", sep in "[^0-9]", garbage: String) {
|
|
||||||
if swap_parts {
|
|
||||||
core::mem::swap(&mut part1, &mut part2);
|
|
||||||
}
|
|
||||||
let input = format!("{part1}.{part2}{e}{exponent_sign}{exponent}{sep}{garbage}");
|
|
||||||
let garbage = format!("{sep}{garbage}");
|
|
||||||
let expected = match(part1.is_empty(), part2.is_empty()) {
|
|
||||||
(false, false) => Number::Decimal {
|
|
||||||
int: Digits(part1.as_str()),
|
|
||||||
point: ".",
|
|
||||||
frac: Digits(part2.as_str()),
|
|
||||||
exp: Some(Exponent {
|
|
||||||
e: e.as_str(),
|
|
||||||
sign: exponent_sign.is_empty().not().then_some(exponent_sign.as_str()),
|
|
||||||
digits: Digits(exponent.as_str())
|
|
||||||
})
|
|
||||||
},
|
|
||||||
(true, false) => Number::DecimalFraction {
|
|
||||||
point: ".",
|
|
||||||
frac: Digits(part2.as_str()),
|
|
||||||
exp: Some(Exponent {
|
|
||||||
e: e.as_str(),
|
|
||||||
sign: exponent_sign.is_empty().not().then_some(exponent_sign.as_str()),
|
|
||||||
digits: Digits(exponent.as_str())
|
|
||||||
})
|
|
||||||
},
|
|
||||||
(false, true) => Number::DecimalInt {
|
|
||||||
int: Digits(part1.as_str()),
|
|
||||||
point: ".",
|
|
||||||
exp: Some(Exponent {
|
|
||||||
e: e.as_str(),
|
|
||||||
sign: exponent_sign.is_empty().not().then_some(exponent_sign.as_str()),
|
|
||||||
digits: Digits(exponent.as_str())
|
|
||||||
})
|
|
||||||
},
|
|
||||||
(true, true) => unreachable!(),
|
|
||||||
};
|
|
||||||
prop_assert_eq!(
|
|
||||||
Number::parse(&input),
|
|
||||||
Some((
|
|
||||||
expected,
|
|
||||||
garbage.as_str()
|
|
||||||
))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn not_parses_garbage(input in "[^0-9.]") {
|
|
||||||
prop_assert_eq!(
|
|
||||||
Number::parse(&input),
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,335 +0,0 @@
|
||||||
use super::{Digits, Exponent, Number};
|
|
||||||
|
|
||||||
macro_rules! test_digits_parse {
|
|
||||||
($name:ident, $input:literal, $expected:expr) => {
|
|
||||||
::paste::paste! {
|
|
||||||
#[test]
|
|
||||||
fn [< $name _for_digits >]() {
|
|
||||||
$crate::log_init();
|
|
||||||
|
|
||||||
assert_eq!(Digits::parse($input), $expected);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
test_digits_parse!(empty_should_fail, "", None);
|
|
||||||
test_digits_parse!(whitespace_should_fail, " ", None);
|
|
||||||
test_digits_parse!(letter_should_fail, "a", None);
|
|
||||||
test_digits_parse!(minus_should_fail, "-", None);
|
|
||||||
test_digits_parse!(minus_one_should_fail, "-1", None);
|
|
||||||
|
|
||||||
test_digits_parse!(zero_should_ok, "0", Some((Digits("0"), "")));
|
|
||||||
test_digits_parse!(p42_should_ok, "42", Some((Digits("42"), "")));
|
|
||||||
test_digits_parse!(
|
|
||||||
number_space_should_leave_space,
|
|
||||||
"424233 ",
|
|
||||||
Some((Digits("424233"), " "))
|
|
||||||
);
|
|
||||||
test_digits_parse!(
|
|
||||||
number_space_letter_should_leave_space_letter,
|
|
||||||
"424233 l",
|
|
||||||
Some((Digits("424233"), " l"))
|
|
||||||
);
|
|
||||||
test_digits_parse!(
|
|
||||||
number_letter_should_leave_letter,
|
|
||||||
"2p",
|
|
||||||
Some((Digits("2"), "p"))
|
|
||||||
);
|
|
||||||
test_digits_parse!(
|
|
||||||
big_number,
|
|
||||||
"9934023759839048593537636598927402740240740720909742094----bbb",
|
|
||||||
Some((
|
|
||||||
Digits("9934023759839048593537636598927402740240740720909742094"),
|
|
||||||
"----bbb"
|
|
||||||
))
|
|
||||||
);
|
|
||||||
|
|
||||||
macro_rules! test_exponent_parse {
|
|
||||||
($name:ident, $input:literal, $expected:expr) => {
|
|
||||||
::paste::paste! {
|
|
||||||
#[test]
|
|
||||||
fn [< $name _for_exponent >]() {
|
|
||||||
$crate::log_init();
|
|
||||||
|
|
||||||
assert_eq!(Exponent::parse($input), $expected);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
test_exponent_parse!(empty_should_fail, "", None);
|
|
||||||
test_exponent_parse!(minus_should_fail, "-", None);
|
|
||||||
test_exponent_parse!(number_should_fail, "423", None);
|
|
||||||
test_exponent_parse!(only_e_should_fail, "e", None);
|
|
||||||
test_exponent_parse!(only_cap_e_should_fail, "E", None);
|
|
||||||
|
|
||||||
test_exponent_parse!(
|
|
||||||
e1_should_ok,
|
|
||||||
"e1",
|
|
||||||
Some((
|
|
||||||
Exponent {
|
|
||||||
e: "e",
|
|
||||||
sign: None,
|
|
||||||
digits: Digits("1"),
|
|
||||||
},
|
|
||||||
""
|
|
||||||
))
|
|
||||||
);
|
|
||||||
test_exponent_parse!(
|
|
||||||
emore_should_ok,
|
|
||||||
"e92324l",
|
|
||||||
Some((
|
|
||||||
Exponent {
|
|
||||||
e: "e",
|
|
||||||
sign: None,
|
|
||||||
digits: Digits("92324"),
|
|
||||||
},
|
|
||||||
"l"
|
|
||||||
))
|
|
||||||
);
|
|
||||||
test_exponent_parse!(
|
|
||||||
emore_minus_should_ok,
|
|
||||||
"e-92324l",
|
|
||||||
Some((
|
|
||||||
Exponent {
|
|
||||||
e: "e",
|
|
||||||
sign: Some("-"),
|
|
||||||
digits: Digits("92324"),
|
|
||||||
},
|
|
||||||
"l"
|
|
||||||
))
|
|
||||||
);
|
|
||||||
test_exponent_parse!(
|
|
||||||
emore_plus_should_ok,
|
|
||||||
"e+92324l",
|
|
||||||
Some((
|
|
||||||
Exponent {
|
|
||||||
e: "e",
|
|
||||||
sign: Some("+"),
|
|
||||||
digits: Digits("92324"),
|
|
||||||
},
|
|
||||||
"l"
|
|
||||||
))
|
|
||||||
);
|
|
||||||
test_exponent_parse!(emore_plus_plus_should_fail, "e++92324l", None);
|
|
||||||
|
|
||||||
test_exponent_parse!(
|
|
||||||
emore_plus_num_plus_should_ok,
|
|
||||||
"E+92+",
|
|
||||||
Some((
|
|
||||||
Exponent {
|
|
||||||
e: "E",
|
|
||||||
sign: Some("+"),
|
|
||||||
digits: Digits("92")
|
|
||||||
},
|
|
||||||
"+"
|
|
||||||
))
|
|
||||||
);
|
|
||||||
|
|
||||||
macro_rules! test_number_parse {
|
|
||||||
($name:ident, $input:literal, $expected:expr) => {
|
|
||||||
::paste::paste! {
|
|
||||||
#[test]
|
|
||||||
fn [< $name _for_number >]() {
|
|
||||||
$crate::log_init();
|
|
||||||
|
|
||||||
assert_eq!(Number::parse($input), $expected);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
test_number_parse!(empty_should_fail, "", None);
|
|
||||||
test_number_parse!(word_shold_fail, "word", None);
|
|
||||||
|
|
||||||
test_number_parse!(
|
|
||||||
int_should_ok,
|
|
||||||
"1",
|
|
||||||
Some((
|
|
||||||
Number::Integer {
|
|
||||||
int: Digits("1"),
|
|
||||||
exp: None
|
|
||||||
},
|
|
||||||
""
|
|
||||||
))
|
|
||||||
);
|
|
||||||
test_number_parse!(
|
|
||||||
int_should_ok2,
|
|
||||||
"99",
|
|
||||||
Some((
|
|
||||||
Number::Integer {
|
|
||||||
int: Digits("99"),
|
|
||||||
exp: None
|
|
||||||
},
|
|
||||||
""
|
|
||||||
))
|
|
||||||
);
|
|
||||||
|
|
||||||
test_number_parse!(
|
|
||||||
int_exp,
|
|
||||||
"99E2",
|
|
||||||
Some((
|
|
||||||
Number::Integer {
|
|
||||||
int: Digits("99"),
|
|
||||||
exp: Some(Exponent {
|
|
||||||
e: "E",
|
|
||||||
sign: None,
|
|
||||||
digits: Digits("2")
|
|
||||||
})
|
|
||||||
},
|
|
||||||
""
|
|
||||||
))
|
|
||||||
);
|
|
||||||
|
|
||||||
test_number_parse!(
|
|
||||||
int_exp_minus,
|
|
||||||
"99E-2",
|
|
||||||
Some((
|
|
||||||
Number::Integer {
|
|
||||||
int: Digits("99"),
|
|
||||||
exp: Some(Exponent {
|
|
||||||
e: "E",
|
|
||||||
sign: Some("-"),
|
|
||||||
digits: Digits("2")
|
|
||||||
})
|
|
||||||
},
|
|
||||||
""
|
|
||||||
))
|
|
||||||
);
|
|
||||||
test_number_parse!(
|
|
||||||
int_exp_plus,
|
|
||||||
"99E+2",
|
|
||||||
Some((
|
|
||||||
Number::Integer {
|
|
||||||
int: Digits("99"),
|
|
||||||
exp: Some(Exponent {
|
|
||||||
e: "E",
|
|
||||||
sign: Some("+"),
|
|
||||||
digits: Digits("2")
|
|
||||||
})
|
|
||||||
},
|
|
||||||
""
|
|
||||||
))
|
|
||||||
);
|
|
||||||
test_number_parse!(
|
|
||||||
int_plus_plus_should_no_exp,
|
|
||||||
"99E++2",
|
|
||||||
Some((
|
|
||||||
Number::Integer {
|
|
||||||
int: Digits("99"),
|
|
||||||
exp: None
|
|
||||||
},
|
|
||||||
"E++2"
|
|
||||||
))
|
|
||||||
);
|
|
||||||
|
|
||||||
test_number_parse!(
|
|
||||||
dec_int_only,
|
|
||||||
"2.",
|
|
||||||
Some((
|
|
||||||
Number::DecimalInt {
|
|
||||||
int: Digits("2"),
|
|
||||||
point: ".",
|
|
||||||
exp: None
|
|
||||||
},
|
|
||||||
""
|
|
||||||
))
|
|
||||||
);
|
|
||||||
|
|
||||||
test_number_parse!(
|
|
||||||
dec_int_only_exp,
|
|
||||||
"2.E-3-",
|
|
||||||
Some((
|
|
||||||
Number::DecimalInt {
|
|
||||||
int: Digits("2"),
|
|
||||||
point: ".",
|
|
||||||
exp: Some(Exponent {
|
|
||||||
e: "E",
|
|
||||||
sign: Some("-"),
|
|
||||||
digits: Digits("3")
|
|
||||||
})
|
|
||||||
},
|
|
||||||
"-"
|
|
||||||
))
|
|
||||||
);
|
|
||||||
|
|
||||||
test_number_parse!(
|
|
||||||
dec_int_full,
|
|
||||||
"3.141592--",
|
|
||||||
Some((
|
|
||||||
Number::Decimal {
|
|
||||||
int: Digits("3"),
|
|
||||||
point: ".",
|
|
||||||
frac: Digits("141592"),
|
|
||||||
exp: None
|
|
||||||
},
|
|
||||||
"--"
|
|
||||||
))
|
|
||||||
);
|
|
||||||
|
|
||||||
test_number_parse!(
|
|
||||||
dec_int_full_exp,
|
|
||||||
"3141.592e-3+3-",
|
|
||||||
Some((
|
|
||||||
Number::Decimal {
|
|
||||||
int: Digits("3141"),
|
|
||||||
point: ".",
|
|
||||||
frac: Digits("592"),
|
|
||||||
exp: Some(Exponent {
|
|
||||||
e: "e",
|
|
||||||
sign: Some("-"),
|
|
||||||
digits: Digits("3")
|
|
||||||
})
|
|
||||||
},
|
|
||||||
"+3-"
|
|
||||||
))
|
|
||||||
);
|
|
||||||
|
|
||||||
test_number_parse!(
|
|
||||||
dec_int_frac_exp,
|
|
||||||
".592e3+3-",
|
|
||||||
Some((
|
|
||||||
Number::DecimalFraction {
|
|
||||||
point: ".",
|
|
||||||
frac: Digits("592"),
|
|
||||||
exp: Some(Exponent {
|
|
||||||
e: "e",
|
|
||||||
sign: None,
|
|
||||||
digits: Digits("3")
|
|
||||||
})
|
|
||||||
},
|
|
||||||
"+3-"
|
|
||||||
))
|
|
||||||
);
|
|
||||||
|
|
||||||
test_number_parse!(
|
|
||||||
dec_int_frac,
|
|
||||||
".592_3+3-",
|
|
||||||
Some((
|
|
||||||
Number::DecimalFraction {
|
|
||||||
point: ".",
|
|
||||||
frac: Digits("592"),
|
|
||||||
exp: None
|
|
||||||
},
|
|
||||||
"_3+3-"
|
|
||||||
))
|
|
||||||
);
|
|
||||||
|
|
||||||
test_number_parse!(
|
|
||||||
dec_int_frac_exp2,
|
|
||||||
".592E-37",
|
|
||||||
Some((
|
|
||||||
Number::DecimalFraction {
|
|
||||||
point: ".",
|
|
||||||
frac: Digits("592"),
|
|
||||||
exp: Some(Exponent {
|
|
||||||
e: "E",
|
|
||||||
sign: Some("-"),
|
|
||||||
digits: Digits("37")
|
|
||||||
})
|
|
||||||
},
|
|
||||||
""
|
|
||||||
))
|
|
||||||
);
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
use super::{token::SpannedToken, StrTokenParser};
|
|
||||||
|
|
||||||
use proptest::{prelude::ProptestConfig, proptest};
|
|
||||||
|
|
||||||
use alloc::string::String;
|
|
||||||
|
|
||||||
proptest! {
|
|
||||||
#![proptest_config(ProptestConfig {
|
|
||||||
cases: 30_000,
|
|
||||||
max_default_size_range: 500,
|
|
||||||
..ProptestConfig::default()
|
|
||||||
})]
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parser_never_panics(input: String) {
|
|
||||||
let parser = StrTokenParser::<'_, SpannedToken<&str>>::new(&input);
|
|
||||||
let _ = parser.count();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,463 +0,0 @@
|
||||||
use super::{number::Number, word::Word, Parseable};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
|
|
||||||
pub struct SpannedToken<Span, D = &'static str, C = &'static str, W = &'static str> {
|
|
||||||
token: Token<D, C, W>,
|
|
||||||
span: Span,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'s> Parseable<'s> for SpannedToken<&'s str, &'s str, &'s str, &'s str> {
|
|
||||||
#[inline]
|
|
||||||
fn try_parse(input: &'s str) -> Option<(Self, &'s str)> {
|
|
||||||
Token::try_parse(input).map(|(token, rest)| {
|
|
||||||
(
|
|
||||||
Self {
|
|
||||||
token,
|
|
||||||
span: &input[..input.len() - rest.len()],
|
|
||||||
},
|
|
||||||
rest,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Grouping symbol kind
|
|
||||||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, derive_more::Display)]
|
|
||||||
pub enum GroupingKind {
|
|
||||||
#[display("paren")]
|
|
||||||
Parentheses,
|
|
||||||
#[display("bracket")]
|
|
||||||
Brackets,
|
|
||||||
#[display("brace")]
|
|
||||||
Braces,
|
|
||||||
#[display("angle")]
|
|
||||||
Angle,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Grouping symbol kind
|
|
||||||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, derive_more::Display)]
|
|
||||||
pub enum GroupingSide {
|
|
||||||
#[display("left")]
|
|
||||||
Left,
|
|
||||||
#[display("right")]
|
|
||||||
Right,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents operators (not necessary corresponding to actual [`crate::evaluation_tree::Operator`]s).
|
|
||||||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, derive_more::Display)]
|
|
||||||
pub enum SpecialKind {
|
|
||||||
#[display(".")]
|
|
||||||
Dot,
|
|
||||||
#[display(",")]
|
|
||||||
Comma,
|
|
||||||
#[display("+")]
|
|
||||||
Plus,
|
|
||||||
#[display("-")]
|
|
||||||
Minus,
|
|
||||||
#[display("*")]
|
|
||||||
Star,
|
|
||||||
#[display("/")]
|
|
||||||
Slash,
|
|
||||||
#[display("%")]
|
|
||||||
Percent,
|
|
||||||
#[display("^")]
|
|
||||||
Cap,
|
|
||||||
#[display("|")]
|
|
||||||
Pipe,
|
|
||||||
#[display("&")]
|
|
||||||
Umpersand,
|
|
||||||
#[display(":")]
|
|
||||||
Colon,
|
|
||||||
#[display("!")]
|
|
||||||
Bang,
|
|
||||||
#[display("=")]
|
|
||||||
Equals,
|
|
||||||
#[display("?")]
|
|
||||||
Question,
|
|
||||||
#[display("@")]
|
|
||||||
At,
|
|
||||||
#[display("~")]
|
|
||||||
Tilda,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents a single token.
|
|
||||||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, derive_more::Display)]
|
|
||||||
pub enum Token<C = &'static str, D = &'static str, W = &'static str> {
|
|
||||||
#[display("{kind}_{side}")]
|
|
||||||
Grouping {
|
|
||||||
kind: GroupingKind,
|
|
||||||
side: GroupingSide,
|
|
||||||
},
|
|
||||||
#[display("{_0}")]
|
|
||||||
Special(SpecialKind),
|
|
||||||
#[display("{_0}")]
|
|
||||||
Number(Number<D, C>),
|
|
||||||
#[display("{_0}")]
|
|
||||||
Word(Word<W>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'s> Parseable<'s> for Token<&'s str, &'s str, &'s str> {
|
|
||||||
fn try_parse(input: &'s str) -> Option<(Self, &'s str)> {
|
|
||||||
// if there's no char, there's no token
|
|
||||||
let c = input.chars().next()?; // (short-circuit)
|
|
||||||
|
|
||||||
// try parsing word
|
|
||||||
//
|
|
||||||
// can't be confused with any other token, and mot of the tokens are expected to be words
|
|
||||||
if let Some((word, rest)) = Word::parse(input) {
|
|
||||||
return Some((Token::word(word), rest));
|
|
||||||
}
|
|
||||||
|
|
||||||
// try parsing number
|
|
||||||
//
|
|
||||||
// (same idea)
|
|
||||||
if let Some((number, rest)) = Number::parse(input) {
|
|
||||||
return Some((Token::number(number), rest));
|
|
||||||
}
|
|
||||||
|
|
||||||
// token is not a number and not a word, so it is a one-character token
|
|
||||||
let token = match c {
|
|
||||||
'(' => Token::left_paren(),
|
|
||||||
')' => Token::right_paren(),
|
|
||||||
'[' => Token::left_bracket(),
|
|
||||||
']' => Token::right_bracket(),
|
|
||||||
'{' => Token::left_brace(),
|
|
||||||
'}' => Token::right_brace(),
|
|
||||||
'.' => Token::dot(),
|
|
||||||
',' => Token::comma(),
|
|
||||||
'+' => Token::plus(),
|
|
||||||
'-' => Token::minus(),
|
|
||||||
'*' => Token::star(),
|
|
||||||
'/' => Token::slash(),
|
|
||||||
'%' => Token::percent(),
|
|
||||||
'^' => Token::cap(),
|
|
||||||
'|' => Token::pipe(),
|
|
||||||
'&' => Token::umpersand(),
|
|
||||||
':' => Token::colon(),
|
|
||||||
'!' => Token::bang(),
|
|
||||||
'=' => Token::equals(),
|
|
||||||
'?' => Token::question(),
|
|
||||||
'@' => Token::at(),
|
|
||||||
'~' => Token::tilda(),
|
|
||||||
// not a token
|
|
||||||
_ => return None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let next_pos = input
|
|
||||||
.char_indices()
|
|
||||||
// position or the char after first
|
|
||||||
.nth(1)
|
|
||||||
// or end of the line, if that's the only char
|
|
||||||
.map_or(input.len(), |(pos, _)| pos);
|
|
||||||
|
|
||||||
Some((token, &input[next_pos..]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// helper constructors and macro
|
|
||||||
impl<C, D, W> Token<C, D, W> {
|
|
||||||
pub fn dot() -> Self {
|
|
||||||
Token::Special(SpecialKind::Dot)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn comma() -> Self {
|
|
||||||
Token::Special(SpecialKind::Comma)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn left_paren() -> Self {
|
|
||||||
Self::Grouping {
|
|
||||||
kind: GroupingKind::Parentheses,
|
|
||||||
side: GroupingSide::Left,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn right_paren() -> Self {
|
|
||||||
Self::Grouping {
|
|
||||||
kind: GroupingKind::Parentheses,
|
|
||||||
side: GroupingSide::Right,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn left_bracket() -> Self {
|
|
||||||
Self::Grouping {
|
|
||||||
kind: GroupingKind::Brackets,
|
|
||||||
side: GroupingSide::Left,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn right_bracket() -> Self {
|
|
||||||
Self::Grouping {
|
|
||||||
kind: GroupingKind::Brackets,
|
|
||||||
side: GroupingSide::Right,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn left_brace() -> Self {
|
|
||||||
Self::Grouping {
|
|
||||||
kind: GroupingKind::Braces,
|
|
||||||
side: GroupingSide::Left,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn right_brace() -> Self {
|
|
||||||
Self::Grouping {
|
|
||||||
kind: GroupingKind::Braces,
|
|
||||||
side: GroupingSide::Right,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn left_angle() -> Self {
|
|
||||||
Self::Grouping {
|
|
||||||
kind: GroupingKind::Angle,
|
|
||||||
side: GroupingSide::Left,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn right_angle() -> Self {
|
|
||||||
Self::Grouping {
|
|
||||||
kind: GroupingKind::Angle,
|
|
||||||
side: GroupingSide::Right,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn plus() -> Self {
|
|
||||||
Token::Special(SpecialKind::Plus)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn minus() -> Self {
|
|
||||||
Token::Special(SpecialKind::Minus)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn star() -> Self {
|
|
||||||
Token::Special(SpecialKind::Star)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn slash() -> Self {
|
|
||||||
Token::Special(SpecialKind::Slash)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn percent() -> Self {
|
|
||||||
Token::Special(SpecialKind::Percent)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cap() -> Self {
|
|
||||||
Token::Special(SpecialKind::Cap)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pipe() -> Self {
|
|
||||||
Token::Special(SpecialKind::Pipe)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn umpersand() -> Self {
|
|
||||||
Token::Special(SpecialKind::Umpersand)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn colon() -> Self {
|
|
||||||
Token::Special(SpecialKind::Colon)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn bang() -> Self {
|
|
||||||
Token::Special(SpecialKind::Bang)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn equals() -> Self {
|
|
||||||
Token::Special(SpecialKind::Equals)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn question() -> Self {
|
|
||||||
Token::Special(SpecialKind::Question)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn at() -> Self {
|
|
||||||
Token::Special(SpecialKind::At)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn tilda() -> Self {
|
|
||||||
Token::Special(SpecialKind::Tilda)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn number(n: Number<D, C>) -> Self {
|
|
||||||
Token::Number(n)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn word(w: Word<W>) -> Self {
|
|
||||||
Token::Word(w)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! tokens_inner {
|
|
||||||
() => {
|
|
||||||
arr![]
|
|
||||||
};
|
|
||||||
( ( $($inner:tt)* ) $($rest:tt)* ) => {
|
|
||||||
Concat::concat(
|
|
||||||
Concat::concat(
|
|
||||||
Concat::concat(
|
|
||||||
arr![Token::left_paren()],
|
|
||||||
tokens_inner!($($inner)*)
|
|
||||||
),
|
|
||||||
arr![Token::right_paren()],
|
|
||||||
),
|
|
||||||
tokens_inner!($($rest)*)
|
|
||||||
)
|
|
||||||
};
|
|
||||||
( [ $($inner:tt)* ] $($rest:tt)* ) => {
|
|
||||||
Concat::concat(
|
|
||||||
Concat::concat(
|
|
||||||
Concat::concat(
|
|
||||||
arr![Token::left_bracket()],
|
|
||||||
tokens_inner!($($inner)*)
|
|
||||||
),
|
|
||||||
arr![Token::right_bracket()],
|
|
||||||
),
|
|
||||||
tokens_inner!($($rest)*)
|
|
||||||
)
|
|
||||||
};
|
|
||||||
( { $($inner:tt)* } $($rest:tt)* ) => {
|
|
||||||
Concat::concat(
|
|
||||||
Concat::concat(
|
|
||||||
Concat::concat(
|
|
||||||
arr![Token::left_brace()],
|
|
||||||
tokens_inner!($($inner)*)
|
|
||||||
),
|
|
||||||
arr![Token::right_brace()],
|
|
||||||
),
|
|
||||||
tokens_inner!($($rest)*)
|
|
||||||
)
|
|
||||||
};
|
|
||||||
(. $($rest:tt)*) => {
|
|
||||||
Concat::concat(
|
|
||||||
arr![Token::dot()],
|
|
||||||
tokens_inner!($($rest)*)
|
|
||||||
)
|
|
||||||
};
|
|
||||||
(, $($rest:tt)*) => {
|
|
||||||
Concat::concat(
|
|
||||||
arr![Token::comma()],
|
|
||||||
tokens_inner!($($rest)*)
|
|
||||||
)
|
|
||||||
};
|
|
||||||
(+ $($rest:tt)*) => {
|
|
||||||
Concat::concat(
|
|
||||||
arr![Token::plus()],
|
|
||||||
tokens_inner!($($rest)*)
|
|
||||||
)
|
|
||||||
};
|
|
||||||
(- $($rest:tt)*) => {
|
|
||||||
Concat::concat(
|
|
||||||
arr![Token::minus()],
|
|
||||||
tokens_inner!($($rest)*)
|
|
||||||
)
|
|
||||||
};
|
|
||||||
(* $($rest:tt)*) => {
|
|
||||||
Concat::concat(
|
|
||||||
arr![Token::star()],
|
|
||||||
tokens_inner!($($rest)*)
|
|
||||||
)
|
|
||||||
};
|
|
||||||
(/ $($rest:tt)*) => {
|
|
||||||
Concat::concat(
|
|
||||||
arr![Token::slash()],
|
|
||||||
tokens_inner!($($rest)*)
|
|
||||||
)
|
|
||||||
};
|
|
||||||
(% $($rest:tt)*) => {
|
|
||||||
Concat::concat(
|
|
||||||
arr![Token::percent()],
|
|
||||||
tokens_inner!($($rest)*)
|
|
||||||
)
|
|
||||||
};
|
|
||||||
(^ $($rest:tt)*) => {
|
|
||||||
Concat::concat(
|
|
||||||
arr![Token::cap()],
|
|
||||||
tokens_inner!($($rest)*)
|
|
||||||
)
|
|
||||||
};
|
|
||||||
(| $($rest:tt)*) => {
|
|
||||||
Concat::concat(
|
|
||||||
arr![Token::pipe()],
|
|
||||||
tokens_inner!($($rest)*)
|
|
||||||
)
|
|
||||||
};
|
|
||||||
(& $($rest:tt)*) => {
|
|
||||||
Concat::concat(
|
|
||||||
arr![Token::umpersand()],
|
|
||||||
tokens_inner!($($rest)*)
|
|
||||||
)
|
|
||||||
};
|
|
||||||
(: $($rest:tt)*) => {
|
|
||||||
Concat::concat(
|
|
||||||
arr![Token::colon()],
|
|
||||||
tokens_inner!($($rest)*)
|
|
||||||
)
|
|
||||||
};
|
|
||||||
(! $($rest:tt)*) => {
|
|
||||||
Concat::concat(
|
|
||||||
arr![Token::bang()],
|
|
||||||
tokens_inner!($($rest)*)
|
|
||||||
)
|
|
||||||
};
|
|
||||||
(= $($rest:tt)*) => {
|
|
||||||
Concat::concat(
|
|
||||||
arr![Token::equals()],
|
|
||||||
tokens_inner!($($rest)*)
|
|
||||||
)
|
|
||||||
};
|
|
||||||
(? $($rest:tt)*) => {
|
|
||||||
Concat::concat(
|
|
||||||
arr![Token::question()],
|
|
||||||
tokens_inner!($($rest)*)
|
|
||||||
)
|
|
||||||
};
|
|
||||||
(@ $($rest:tt)*) => {
|
|
||||||
Concat::concat(
|
|
||||||
arr![Token::at()],
|
|
||||||
tokens_inner!($($rest)*)
|
|
||||||
)
|
|
||||||
};
|
|
||||||
(~ $($rest:tt)*) => {
|
|
||||||
Concat::concat(
|
|
||||||
arr![Token::tilda()],
|
|
||||||
tokens_inner!($($rest)*)
|
|
||||||
)
|
|
||||||
};
|
|
||||||
(< $($rest:tt)*) => {
|
|
||||||
Concat::concat(
|
|
||||||
arr![Token::left_angle()],
|
|
||||||
tokens_inner!($($rest)*)
|
|
||||||
)
|
|
||||||
};
|
|
||||||
(> $($rest:tt)*) => {
|
|
||||||
Concat::concat(
|
|
||||||
arr![Token::right_angle()],
|
|
||||||
tokens_inner!($($rest)*)
|
|
||||||
)
|
|
||||||
};
|
|
||||||
($token:ident $($rest:tt)*) => {
|
|
||||||
Concat::concat(
|
|
||||||
arr![Token::word(word!($token))],
|
|
||||||
tokens_inner!($($rest)*)
|
|
||||||
)
|
|
||||||
};
|
|
||||||
($token:literal $($rest:tt)*) => {
|
|
||||||
Concat::concat(
|
|
||||||
arr![Token::number(number!($token))],
|
|
||||||
tokens_inner!($($rest)*)
|
|
||||||
)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! tokens {
|
|
||||||
($($rest:tt)*) => {{
|
|
||||||
#[allow(unused_imports)]
|
|
||||||
use generic_array::{arr, sequence::Concat};
|
|
||||||
#[allow(unused_imports)]
|
|
||||||
use $crate::{lexer_v2::token::Token, word, tokens_inner, number};
|
|
||||||
tokens_inner!($($rest)*)
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests;
|
|
||||||
|
|
@ -1,166 +0,0 @@
|
||||||
use super::super::StrTokenParser;
|
|
||||||
use super::Token;
|
|
||||||
use crate::{number, word};
|
|
||||||
|
|
||||||
use alloc::vec::Vec;
|
|
||||||
|
|
||||||
macro_rules! test_tokens {
|
|
||||||
($(#[$($attr:tt)+])* $name:ident, ($($input:tt)*), [$($expected:expr),*]) => {
|
|
||||||
::paste::paste! {
|
|
||||||
#[test]
|
|
||||||
$(
|
|
||||||
#[$($attr)*]
|
|
||||||
)*
|
|
||||||
fn [< $name _tokens >]() {
|
|
||||||
$crate::log_init();
|
|
||||||
|
|
||||||
let input = tokens!($($input)*);
|
|
||||||
let expected = generic_array::GenericArray::<Token<&'static str, &'static str, &'static str>, _>::from_array([$($expected),*]);
|
|
||||||
|
|
||||||
assert_eq!(input, expected);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
test_tokens!(empty, (), []);
|
|
||||||
|
|
||||||
test_tokens!(dot, (.), [Token::dot()]);
|
|
||||||
test_tokens!(comma, (,), [Token::comma()]);
|
|
||||||
test_tokens!(plus, (+), [Token::plus()]);
|
|
||||||
test_tokens!(minus, (-), [Token::minus()]);
|
|
||||||
test_tokens!(star, (*), [Token::star()]);
|
|
||||||
test_tokens!(slash, (/), [Token::slash()]);
|
|
||||||
test_tokens!(percent, (%), [Token::percent()]);
|
|
||||||
test_tokens!(cap, (^), [Token::cap()]);
|
|
||||||
test_tokens!(pipe, (|), [Token::pipe()]);
|
|
||||||
test_tokens!(umpersand, (&), [Token::umpersand()]);
|
|
||||||
test_tokens!(colon, (:), [Token::colon()]);
|
|
||||||
test_tokens!(bang, (!), [Token::bang()]);
|
|
||||||
test_tokens!(equals, (=), [Token::equals()]);
|
|
||||||
test_tokens!(question, (?), [Token::question()]);
|
|
||||||
test_tokens!(at, (@), [Token::at()]);
|
|
||||||
test_tokens!(tilda, (~), [Token::tilda()]);
|
|
||||||
test_tokens!(left_angle, (<), [Token::left_angle()]);
|
|
||||||
test_tokens!(right_angle, (>), [Token::right_angle()]);
|
|
||||||
|
|
||||||
test_tokens!(word, (word), [Token::word(word!(word))]);
|
|
||||||
test_tokens!(word2, (w0r3d), [Token::word(word!(w0r3d))]);
|
|
||||||
test_tokens!(
|
|
||||||
long_word,
|
|
||||||
(SOME_REALLY_LONG_WORD_1111111),
|
|
||||||
[Token::word(word!(SOME_REALLY_LONG_WORD_1111111))]
|
|
||||||
);
|
|
||||||
test_tokens!(
|
|
||||||
#[should_panic = "Invalid word"]
|
|
||||||
invalid_word,
|
|
||||||
(r#not_a_valid_word),
|
|
||||||
[Token::word(word!(not_a_valid_word))]
|
|
||||||
);
|
|
||||||
|
|
||||||
test_tokens!(number, (1), [Token::number(number!(1))]);
|
|
||||||
test_tokens!(number2, (1.), [Token::number(number!(1.))]);
|
|
||||||
test_tokens!(number3, (1.), [Token::number(number!(1.))]);
|
|
||||||
test_tokens!(number4, (1.0E0), [Token::number(number!(1.0E0))]);
|
|
||||||
|
|
||||||
test_tokens!(
|
|
||||||
negative_number,
|
|
||||||
(-24.003E6),
|
|
||||||
[Token::minus(), Token::number(number!(24.003E6))]
|
|
||||||
);
|
|
||||||
|
|
||||||
test_tokens!(
|
|
||||||
very_much_positive_number,
|
|
||||||
(+ + + 42.0E-5),
|
|
||||||
[Token::plus(), Token::plus(), Token::plus(), Token::number(number!(42.0E-5))]
|
|
||||||
);
|
|
||||||
|
|
||||||
test_tokens!(
|
|
||||||
random_expr,
|
|
||||||
(3 + 4 - 6 + twelve - 00 - ONLY),
|
|
||||||
[
|
|
||||||
Token::number(number!(3)),
|
|
||||||
Token::plus(),
|
|
||||||
Token::number(number!(4)),
|
|
||||||
Token::minus(),
|
|
||||||
Token::number(number!(6)),
|
|
||||||
Token::plus(),
|
|
||||||
Token::word(word!(twelve)),
|
|
||||||
Token::minus(),
|
|
||||||
Token::number(number!(00)),
|
|
||||||
Token::minus(),
|
|
||||||
Token::word(word!(ONLY))
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
test_tokens!(
|
|
||||||
parens,
|
|
||||||
((2 + 3.4e-21) | [a - _BB45] {} / 10),
|
|
||||||
[
|
|
||||||
Token::left_paren(),
|
|
||||||
Token::number(number!(2)),
|
|
||||||
Token::plus(),
|
|
||||||
Token::number(number!(3.4e-21)),
|
|
||||||
Token::right_paren(),
|
|
||||||
Token::pipe(),
|
|
||||||
Token::left_bracket(),
|
|
||||||
Token::word(word!(a)),
|
|
||||||
Token::minus(),
|
|
||||||
Token::word(word!(_BB45)),
|
|
||||||
Token::right_bracket(),
|
|
||||||
Token::left_brace(),
|
|
||||||
Token::right_brace(),
|
|
||||||
Token::slash(),
|
|
||||||
Token::number(number!(10))
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
macro_rules! test_token_parser {
|
|
||||||
($name:ident, $input:literal, [$($token:expr),*$(,)?]) => {
|
|
||||||
::paste::paste! {
|
|
||||||
#[test]
|
|
||||||
fn [< $name _tokens_iter >]() {
|
|
||||||
$crate::log_init();
|
|
||||||
|
|
||||||
let iter = StrTokenParser::<'_, Token>::new($input);
|
|
||||||
|
|
||||||
let parsed_tokens = iter.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
assert_eq!(parsed_tokens.as_slice(), &[$($token),*]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
test_token_parser!(empty_parses, "", []);
|
|
||||||
test_token_parser!(plus_pares, "+", [Ok(Token::plus())]);
|
|
||||||
test_token_parser!(
|
|
||||||
expr_parses,
|
|
||||||
"+3 - b4_A[AA/ #iim 3e-67+ (17.00E-67c ]}- an mee#34\nb) \t ---",
|
|
||||||
[
|
|
||||||
Ok(Token::plus()),
|
|
||||||
Ok(Token::number(number!(3))),
|
|
||||||
Ok(Token::minus()),
|
|
||||||
Ok(Token::word(word!(b4_A))),
|
|
||||||
Ok(Token::left_bracket()),
|
|
||||||
Ok(Token::word(word!(AA))),
|
|
||||||
Ok(Token::slash()),
|
|
||||||
Err("#iim"),
|
|
||||||
Ok(Token::number(number!(3e-67))),
|
|
||||||
Ok(Token::plus()),
|
|
||||||
Ok(Token::left_paren()),
|
|
||||||
Ok(Token::number(number!(17.00E-67))),
|
|
||||||
Ok(Token::word(word!(c))),
|
|
||||||
Ok(Token::right_bracket()),
|
|
||||||
Ok(Token::right_brace()),
|
|
||||||
Ok(Token::minus()),
|
|
||||||
Ok(Token::word(word!(an))),
|
|
||||||
Ok(Token::word(word!(mee))),
|
|
||||||
Err("#34"),
|
|
||||||
Ok(Token::word(word!(b))),
|
|
||||||
Ok(Token::right_paren()),
|
|
||||||
Ok(Token::minus()),
|
|
||||||
Ok(Token::minus()),
|
|
||||||
Ok(Token::minus()),
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
@ -1,70 +0,0 @@
|
||||||
use core::ops::Not;
|
|
||||||
|
|
||||||
#[derive(
|
|
||||||
Debug,
|
|
||||||
Clone,
|
|
||||||
Copy,
|
|
||||||
Hash,
|
|
||||||
PartialEq,
|
|
||||||
Eq,
|
|
||||||
derive_more::AsRef,
|
|
||||||
derive_more::Deref,
|
|
||||||
derive_more::Display,
|
|
||||||
)]
|
|
||||||
#[display("{_0}")]
|
|
||||||
pub struct Word<W>(W);
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn valid_start(ch: char) -> bool {
|
|
||||||
matches!(ch, 'a'..='z' | 'A'..='Z' | '_')
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn valid_continue(ch: char) -> bool {
|
|
||||||
matches!(ch, 'a'..='z' | 'A'..='Z' | '_' | '0'..='9')
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'s> Word<&'s str> {
|
|
||||||
pub fn parse(input: &'s str) -> Option<(Self, &'s str)> {
|
|
||||||
let mut char_inds = input
|
|
||||||
// be a good (cat)boy and iterate over characters
|
|
||||||
.char_indices();
|
|
||||||
// get first non-whitespace char (if there's none, we are done)
|
|
||||||
let (_, first_char) = char_inds.next()?;
|
|
||||||
if !valid_start(first_char) {
|
|
||||||
// first non-whitespace is not a valid started for word; bail
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
// word will end, once we
|
|
||||||
let end_pos = char_inds
|
|
||||||
// find next first char that is not a valid continue
|
|
||||||
.find_map(|(pos, ch)| valid_continue(ch).not().then_some(pos))
|
|
||||||
// if there none, word is up to the end of string
|
|
||||||
.unwrap_or(input.len());
|
|
||||||
Some((Self(&input[..end_pos]), &input[end_pos..]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! word {
|
|
||||||
(? $input:ident) => {
|
|
||||||
'word: {
|
|
||||||
let Some((word, rest)) = $crate::lexer_v2::word::Word::parse(stringify!($input)) else {
|
|
||||||
break 'word None;
|
|
||||||
};
|
|
||||||
if !rest.trim().is_empty() {
|
|
||||||
break 'word None;
|
|
||||||
}
|
|
||||||
Some(word)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
($input:ident) => {
|
|
||||||
word!(?$input).expect("Invalid word")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod prop_tests;
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
use super::Word;
|
|
||||||
|
|
||||||
use proptest::{prelude::ProptestConfig, prop_assert_eq, proptest};
|
|
||||||
|
|
||||||
use alloc::string::String;
|
|
||||||
|
|
||||||
proptest! {
|
|
||||||
#![proptest_config(ProptestConfig {
|
|
||||||
cases: 10_000,
|
|
||||||
max_default_size_range: 200,
|
|
||||||
..ProptestConfig::default()
|
|
||||||
})]
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn never_panics(input: String) {
|
|
||||||
let _ = Word::parse(&input);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parses_words(word in "[a-zA-Z_][a-zA-Z0-9_]*", sep in "[^a-zA-Z0-9_]", garbage: String) {
|
|
||||||
let input = format!("{word}{sep}{garbage}");
|
|
||||||
let garbage = format!("{sep}{garbage}");
|
|
||||||
|
|
||||||
prop_assert_eq!(
|
|
||||||
Word::parse(&input),
|
|
||||||
Some((Word(word.as_str()), garbage.as_str()))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn not_parses_garbage(input in "[^a-zA-Z_]*") {
|
|
||||||
prop_assert_eq!(
|
|
||||||
Word::parse(&input),
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
use super::Word;
|
|
||||||
|
|
||||||
macro_rules! test_word_parse {
|
|
||||||
($name:ident, $input:literal, $expected:expr) => {
|
|
||||||
#[test]
|
|
||||||
fn $name() {
|
|
||||||
assert_eq!(Word::parse($input), $expected);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
test_word_parse!(empty_should_fail, "", None);
|
|
||||||
test_word_parse!(number_should_fail, "1", None);
|
|
||||||
test_word_parse!(whitespace_should_fail, " ", None);
|
|
||||||
test_word_parse!(non_ascii_should_fail, "слово", None);
|
|
||||||
test_word_parse!(minus_should_fail, "-", None);
|
|
||||||
|
|
||||||
test_word_parse!(letter_should_ok, "a", Some((Word("a"), "")));
|
|
||||||
test_word_parse!(letter_should_ok2, "U", Some((Word("U"), "")));
|
|
||||||
test_word_parse!(underscore_should_ok, "_", Some((Word("_"), "")));
|
|
||||||
test_word_parse!(
|
|
||||||
letter_space_should_leave_space,
|
|
||||||
"a ",
|
|
||||||
Some((Word("a"), " "))
|
|
||||||
);
|
|
||||||
test_word_parse!(
|
|
||||||
letter_something_should_keep_something,
|
|
||||||
"a something",
|
|
||||||
Some((Word("a"), " something"))
|
|
||||||
);
|
|
||||||
|
|
||||||
test_word_parse!(
|
|
||||||
a_minus_b_should_leave_minus_b,
|
|
||||||
"a-b",
|
|
||||||
Some((Word("a"), "-b"))
|
|
||||||
);
|
|
||||||
|
|
@ -1,57 +0,0 @@
|
||||||
#![doc = include_str!("../../README.md")]
|
|
||||||
#![no_std]
|
|
||||||
|
|
||||||
use core::fmt::Display;
|
|
||||||
|
|
||||||
pub mod evaluation_tree;
|
|
||||||
pub mod lexer;
|
|
||||||
pub mod parser;
|
|
||||||
|
|
||||||
pub mod lexer_v2;
|
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
extern crate tracing;
|
|
||||||
|
|
||||||
// #[cfg(test)] // <-- TODO: make optional
|
|
||||||
#[macro_use]
|
|
||||||
extern crate alloc;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
#[allow(unused)]
|
|
||||||
pub(crate) fn log_init() {
|
|
||||||
use tracing::level_filters::LevelFilter;
|
|
||||||
use tracing_subscriber::fmt::{self, format::FmtSpan, time::OffsetTime};
|
|
||||||
|
|
||||||
#[cfg(verbose_logging)]
|
|
||||||
if fmt::fmt()
|
|
||||||
.with_max_level(LevelFilter::TRACE)
|
|
||||||
.with_span_events(FmtSpan::ACTIVE)
|
|
||||||
.with_line_number(true)
|
|
||||||
.with_level(true)
|
|
||||||
.with_target(true)
|
|
||||||
.with_timer(OffsetTime::local_rfc_3339().expect("Failed to get local timer"))
|
|
||||||
.compact()
|
|
||||||
.try_init()
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
info!("Log up!");
|
|
||||||
} else {
|
|
||||||
eprintln!("Log is up?");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(
|
|
||||||
clippy::ref_option,
|
|
||||||
reason = "Use case. Don't want to call `.as_ref()` on each site"
|
|
||||||
)]
|
|
||||||
pub(crate) fn display_opt<T: Display>(t: &Option<T>) -> &dyn Display {
|
|
||||||
if let Some(t) = t.as_ref() {
|
|
||||||
t as &dyn Display
|
|
||||||
} else {
|
|
||||||
&"" as &dyn Display
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
#[macro_use]
|
|
||||||
extern crate std;
|
|
||||||
|
|
@ -16,16 +16,14 @@
|
||||||
//!
|
//!
|
||||||
//! Variables/functions can be unregistered too. Although, there's not much use for it apart from debugging.
|
//! Variables/functions can be unregistered too. Although, there's not much use for it apart from debugging.
|
||||||
|
|
||||||
use core::fmt::Debug;
|
use std::{
|
||||||
|
collections::{HashMap, HashSet},
|
||||||
use alloc::{borrow::ToOwned, boxed::Box, string::String, vec::Vec};
|
fmt::Debug,
|
||||||
|
};
|
||||||
|
|
||||||
use num::complex::Complex64;
|
use num::complex::Complex64;
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use hashbrown::{HashMap, HashSet};
|
|
||||||
|
|
||||||
/// Represents types of arguments.
|
/// Represents types of arguments.
|
||||||
///
|
///
|
||||||
/// - [`ArgType::Variable`] stands for regular complex variable.s
|
/// - [`ArgType::Variable`] stands for regular complex variable.s
|
||||||
|
|
@ -77,7 +75,7 @@ impl AsRef<Args> for ArgsErased {
|
||||||
pub struct ArgsRefErased<'l>(pub &'l Args);
|
pub struct ArgsRefErased<'l>(pub &'l Args);
|
||||||
|
|
||||||
impl Debug for ArgsRefErased<'_> {
|
impl Debug for ArgsRefErased<'_> {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
fn display_map<V>(map: &HashMap<String, V>) -> String {
|
fn display_map<V>(map: &HashMap<String, V>) -> String {
|
||||||
format!(
|
format!(
|
||||||
"{{{}}}",
|
"{{{}}}",
|
||||||
|
|
@ -96,7 +94,7 @@ impl Debug for ArgsRefErased<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for ArgsErased {
|
impl Debug for ArgsErased {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
ArgsRefErased(&self.0).fmt(f)
|
ArgsRefErased(&self.0).fmt(f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -27,7 +27,6 @@ impl Operator for Negation {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod negation_tests {
|
mod negation_tests {
|
||||||
|
|
||||||
use alloc::borrow::ToOwned;
|
|
||||||
use num::complex::Complex64;
|
use num::complex::Complex64;
|
||||||
|
|
||||||
use crate::evaluation_tree::{
|
use crate::evaluation_tree::{
|
||||||
|
|
@ -112,7 +111,6 @@ impl Operator for Addition {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod addition_tests {
|
mod addition_tests {
|
||||||
|
|
||||||
use alloc::borrow::ToOwned;
|
|
||||||
use num::complex::Complex64;
|
use num::complex::Complex64;
|
||||||
|
|
||||||
use crate::evaluation_tree::{
|
use crate::evaluation_tree::{
|
||||||
|
|
@ -6,7 +6,6 @@
|
||||||
//!
|
//!
|
||||||
//! In presented API, only [`NamedFunction`]s are used for now. Although, I might add lots of default functions that will automatically resolve to their [`Functional`] counterparts during parsing.
|
//! In presented API, only [`NamedFunction`]s are used for now. Although, I might add lots of default functions that will automatically resolve to their [`Functional`] counterparts during parsing.
|
||||||
|
|
||||||
use alloc::string::String;
|
|
||||||
use num::complex::Complex64;
|
use num::complex::Complex64;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
|
|
@ -6,11 +6,10 @@
|
||||||
//! [`EvaluationTree`] can be created by parsing with [`EvaluationTree::from_tokens`] function.
|
//! [`EvaluationTree`] can be created by parsing with [`EvaluationTree::from_tokens`] function.
|
||||||
//! If you need some custom structure, [`EvaluationTree`] can be ignored and bare [`Node`]s used instead, as they both implement [`Evaluatable`] anyway.
|
//! If you need some custom structure, [`EvaluationTree`] can be ignored and bare [`Node`]s used instead, as they both implement [`Evaluatable`] anyway.
|
||||||
|
|
||||||
use alloc::{boxed::Box, string::String};
|
|
||||||
use num::{complex::Complex64, Complex};
|
use num::{complex::Complex64, Complex};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
lexer::OldToken,
|
lexer::Token,
|
||||||
parser::{parse, GenerationError},
|
parser::{parse, GenerationError},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -49,7 +48,7 @@ pub struct EvaluationTree(Node);
|
||||||
|
|
||||||
impl EvaluationTree {
|
impl EvaluationTree {
|
||||||
/// Attempts to parse a tree from token slice. May fail with [`GenerationError`] to indicate incorrect syntax.
|
/// Attempts to parse a tree from token slice. May fail with [`GenerationError`] to indicate incorrect syntax.
|
||||||
pub fn from_tokens(tokens: &[OldToken]) -> Result<Self, GenerationError> {
|
pub fn from_tokens(tokens: &[Token]) -> Result<Self, GenerationError> {
|
||||||
Ok(Self(parse(tokens)?))
|
Ok(Self(parse(tokens)?))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -183,7 +182,6 @@ where
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use alloc::borrow::ToOwned;
|
|
||||||
use num::complex::Complex64;
|
use num::complex::Complex64;
|
||||||
|
|
||||||
use crate::evaluation_tree::args::{Args, MissingError};
|
use crate::evaluation_tree::args::{Args, MissingError};
|
||||||
189
src/lexer.rs
Normal file
189
src/lexer.rs
Normal file
|
|
@ -0,0 +1,189 @@
|
||||||
|
//! This module defines lexer and structs relevant to it.
|
||||||
|
|
||||||
|
use nom::{
|
||||||
|
branch::alt,
|
||||||
|
bytes::complete::tag,
|
||||||
|
character::complete::{alpha1, alphanumeric1, char as pchar, one_of},
|
||||||
|
combinator::{map, recognize},
|
||||||
|
error::Error,
|
||||||
|
multi::{fold_many1, many0, many0_count},
|
||||||
|
number::complete::double,
|
||||||
|
sequence::{pair, preceded},
|
||||||
|
};
|
||||||
|
|
||||||
|
type Res<'l, T = Token> = nom::IResult<&'l str, T, Error<&'l str>>;
|
||||||
|
|
||||||
|
/// Represents type of grouping.
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
pub enum GroupingType {
|
||||||
|
/// ()
|
||||||
|
Parentheses,
|
||||||
|
/// []
|
||||||
|
Brackets,
|
||||||
|
/// {}
|
||||||
|
Braces,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents operators (not necessary corresponding to actual [`crate::evaluation_tree::Operator`]s).
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
pub enum Operator {
|
||||||
|
Plus,
|
||||||
|
Minus,
|
||||||
|
Star,
|
||||||
|
Slash,
|
||||||
|
Cap,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a single token.
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum Token {
|
||||||
|
GroupOpen(GroupingType),
|
||||||
|
GroupClose(GroupingType),
|
||||||
|
Operator(Operator),
|
||||||
|
Comma,
|
||||||
|
ImaginaryUnit,
|
||||||
|
Number(f64),
|
||||||
|
Identifier(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents token type (used for debug)
|
||||||
|
#[derive(Debug, PartialEq, derive_more::Display)]
|
||||||
|
pub enum TokenType {
|
||||||
|
Grouping,
|
||||||
|
Operator,
|
||||||
|
Comma,
|
||||||
|
Constant,
|
||||||
|
Identifier,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn paren_open(i: &str) -> Res {
|
||||||
|
map(one_of("([{"), |c| {
|
||||||
|
Token::GroupOpen(match c {
|
||||||
|
'(' => GroupingType::Parentheses,
|
||||||
|
'[' => GroupingType::Brackets,
|
||||||
|
'{' => GroupingType::Braces,
|
||||||
|
_ => unreachable!(),
|
||||||
|
})
|
||||||
|
})(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn paren_close(i: &str) -> Res {
|
||||||
|
map(one_of(")]}"), |c| {
|
||||||
|
Token::GroupClose(match c {
|
||||||
|
')' => GroupingType::Parentheses,
|
||||||
|
']' => GroupingType::Brackets,
|
||||||
|
'}' => GroupingType::Braces,
|
||||||
|
_ => unreachable!(),
|
||||||
|
})
|
||||||
|
})(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn operator(i: &str) -> Res {
|
||||||
|
map(one_of("+-*/^"), |c| {
|
||||||
|
Token::Operator(match c {
|
||||||
|
'+' => Operator::Plus,
|
||||||
|
'-' => Operator::Minus,
|
||||||
|
'*' => Operator::Star,
|
||||||
|
'/' => Operator::Slash,
|
||||||
|
'^' => Operator::Cap,
|
||||||
|
_ => unreachable!(),
|
||||||
|
})
|
||||||
|
})(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn comma(i: &str) -> Res {
|
||||||
|
map(pchar(','), |_| Token::Comma)(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn imaginary_unit(i: &str) -> Res {
|
||||||
|
map(pchar('i'), |_| Token::ImaginaryUnit)(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// source: https://github.com/rust-bakery/nom/blob/main/doc/nom_recipes.md#identifiers
|
||||||
|
fn identifier(i: &str) -> Res {
|
||||||
|
map(
|
||||||
|
recognize(pair(
|
||||||
|
alt((alpha1, tag("_"))),
|
||||||
|
many0_count(alt((alphanumeric1, tag("_")))),
|
||||||
|
)),
|
||||||
|
|name: &str| Token::Identifier(name.to_owned()),
|
||||||
|
)(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn number(i: &str) -> Res {
|
||||||
|
map(double, Token::Number)(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn whitespace(i: &str) -> Res<()> {
|
||||||
|
Ok((many0(one_of(" \t\x0c\n"))(i)?.0, ()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// lexes the input
|
||||||
|
pub fn lex(i: &str) -> Res<Vec<Token>> {
|
||||||
|
fold_many1(
|
||||||
|
preceded(
|
||||||
|
whitespace,
|
||||||
|
alt((
|
||||||
|
operator,
|
||||||
|
number,
|
||||||
|
paren_open,
|
||||||
|
paren_close,
|
||||||
|
comma,
|
||||||
|
imaginary_unit,
|
||||||
|
identifier,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
Vec::new,
|
||||||
|
|mut acc, token| {
|
||||||
|
acc.push(token);
|
||||||
|
acc
|
||||||
|
},
|
||||||
|
)(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::lexer::GroupingType;
|
||||||
|
|
||||||
|
use super::lex;
|
||||||
|
use super::Token;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tokens() {
|
||||||
|
// arrange
|
||||||
|
let input = "2 + x^2 - (3x^2 - 7y / sin[z])";
|
||||||
|
|
||||||
|
// act
|
||||||
|
let tokens = lex(input);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
assert_eq!(
|
||||||
|
tokens,
|
||||||
|
Ok((
|
||||||
|
"",
|
||||||
|
vec![
|
||||||
|
Token::Number(2f64),
|
||||||
|
Token::Operator(crate::lexer::Operator::Plus),
|
||||||
|
Token::Identifier("x".to_owned()),
|
||||||
|
Token::Operator(crate::lexer::Operator::Cap),
|
||||||
|
Token::Number(2f64),
|
||||||
|
Token::Operator(crate::lexer::Operator::Minus),
|
||||||
|
Token::GroupOpen(GroupingType::Parentheses),
|
||||||
|
Token::Number(3f64),
|
||||||
|
Token::Identifier("x".to_owned()),
|
||||||
|
Token::Operator(crate::lexer::Operator::Cap),
|
||||||
|
Token::Number(2f64),
|
||||||
|
Token::Operator(crate::lexer::Operator::Minus),
|
||||||
|
Token::Number(7f64),
|
||||||
|
Token::Identifier("y".to_owned()),
|
||||||
|
Token::Operator(crate::lexer::Operator::Slash),
|
||||||
|
Token::Identifier("sin".to_owned()),
|
||||||
|
Token::GroupOpen(GroupingType::Brackets),
|
||||||
|
Token::Identifier("z".to_owned()),
|
||||||
|
Token::GroupClose(GroupingType::Brackets),
|
||||||
|
Token::GroupClose(GroupingType::Parentheses),
|
||||||
|
]
|
||||||
|
))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
6
src/lib.rs
Normal file
6
src/lib.rs
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
#![doc = include_str!("../README.md")]
|
||||||
|
|
||||||
|
pub mod evaluation_tree;
|
||||||
|
pub mod lexer;
|
||||||
|
pub mod parser;
|
||||||
|
|
||||||
|
|
@ -1,11 +1,6 @@
|
||||||
//! This module defines parser and relevant structs.
|
//! This module defines parser and relevant structs.
|
||||||
|
|
||||||
use core::num::NonZeroUsize;
|
use std::num::NonZeroUsize;
|
||||||
|
|
||||||
use alloc::{
|
|
||||||
string::{String, ToString},
|
|
||||||
vec::Vec,
|
|
||||||
};
|
|
||||||
|
|
||||||
use nom::{
|
use nom::{
|
||||||
branch::alt,
|
branch::alt,
|
||||||
|
|
@ -14,7 +9,6 @@ use nom::{
|
||||||
sequence::tuple,
|
sequence::tuple,
|
||||||
Err, Needed,
|
Err, Needed,
|
||||||
};
|
};
|
||||||
|
|
||||||
use num::complex::Complex64;
|
use num::complex::Complex64;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
|
@ -24,7 +18,7 @@ use crate::{
|
||||||
functions::NamedFunction,
|
functions::NamedFunction,
|
||||||
Constant, Node, Variable,
|
Constant, Node, Variable,
|
||||||
},
|
},
|
||||||
lexer::{OldToken, Operator, TokenType},
|
lexer::{Operator, Token, TokenType},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Represents error occurred during tree generation.
|
/// Represents error occurred during tree generation.
|
||||||
|
|
@ -32,13 +26,13 @@ use crate::{
|
||||||
pub enum GenerationError<'l> {
|
pub enum GenerationError<'l> {
|
||||||
/// Error emitted by `nom`
|
/// Error emitted by `nom`
|
||||||
#[error("{0:?}")]
|
#[error("{0:?}")]
|
||||||
NomError(NError<&'l [OldToken]>),
|
NomError(NError<&'l [Token]>),
|
||||||
/// There are no more tokens, but some were expected
|
/// There are no more tokens, but some were expected
|
||||||
#[error("No tokens")]
|
#[error("No tokens")]
|
||||||
Empty(Needed),
|
Empty(Needed),
|
||||||
/// Unexpected token type encountered
|
/// Unexpected token type encountered
|
||||||
#[error("Bad token: {} expected, but {:?} found", .0, .1)]
|
#[error("Bad token: {} expected, but {:?} found", .0, .1)]
|
||||||
BadToken(TokenType, &'l OldToken),
|
BadToken(TokenType, &'l Token),
|
||||||
/// There's an unpaired grouping
|
/// There's an unpaired grouping
|
||||||
#[error("Paren was left unclosed")]
|
#[error("Paren was left unclosed")]
|
||||||
UnpairedGrouping,
|
UnpairedGrouping,
|
||||||
|
|
@ -47,12 +41,12 @@ pub enum GenerationError<'l> {
|
||||||
NoToken,
|
NoToken,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'l> ParseError<&'l [OldToken]> for GenerationError<'l> {
|
impl<'l> ParseError<&'l [Token]> for GenerationError<'l> {
|
||||||
fn from_error_kind(input: &'l [OldToken], kind: nom::error::ErrorKind) -> Self {
|
fn from_error_kind(input: &'l [Token], kind: nom::error::ErrorKind) -> Self {
|
||||||
Self::NomError(NError::new(input, kind))
|
Self::NomError(NError::new(input, kind))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn append(_input: &[OldToken], _kind: nom::error::ErrorKind, other: Self) -> Self {
|
fn append(_input: &[Token], _kind: nom::error::ErrorKind, other: Self) -> Self {
|
||||||
// not really sure what this is about,
|
// not really sure what this is about,
|
||||||
// but it seems that `nom` itself implements this function in a similar fashion,
|
// but it seems that `nom` itself implements this function in a similar fashion,
|
||||||
// so I guess it's not much of a harm.
|
// so I guess it's not much of a harm.
|
||||||
|
|
@ -61,10 +55,10 @@ impl<'l> ParseError<&'l [OldToken]> for GenerationError<'l> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type Res<'l, T = Node<Complex64>> = Result<(&'l [OldToken], T), Err<GenerationError<'l>>>;
|
type Res<'l, T = Node<Complex64>> = Result<(&'l [Token], T), Err<GenerationError<'l>>>;
|
||||||
|
|
||||||
/// parses tokens into a node.
|
/// parses tokens into a node.
|
||||||
pub fn parse(tokens: &[OldToken]) -> Result<Node<Complex64>, GenerationError> {
|
pub fn parse(tokens: &[Token]) -> Result<Node<Complex64>, GenerationError> {
|
||||||
all_consuming(node)(tokens)
|
all_consuming(node)(tokens)
|
||||||
.map(|(_, n)| n)
|
.map(|(_, n)| n)
|
||||||
.map_err(|err| match err {
|
.map_err(|err| match err {
|
||||||
|
|
@ -73,12 +67,12 @@ pub fn parse(tokens: &[OldToken]) -> Result<Node<Complex64>, GenerationError> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node(tokens: &[OldToken]) -> Res {
|
fn node(tokens: &[Token]) -> Res {
|
||||||
assert_parens(tokens)?;
|
assert_parens(tokens)?;
|
||||||
alt((all_consuming(map_parser(paren, raw_node)), raw_node))(tokens)
|
alt((all_consuming(map_parser(paren, raw_node)), raw_node))(tokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn raw_node(tokens: &[OldToken]) -> Res {
|
fn raw_node(tokens: &[Token]) -> Res {
|
||||||
alt((
|
alt((
|
||||||
all_consuming(function_call),
|
all_consuming(function_call),
|
||||||
all_consuming(variable),
|
all_consuming(variable),
|
||||||
|
|
@ -87,7 +81,7 @@ fn raw_node(tokens: &[OldToken]) -> Res {
|
||||||
))(tokens)
|
))(tokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bin_operator(tokens: &[OldToken]) -> Res {
|
fn bin_operator(tokens: &[Token]) -> Res {
|
||||||
let (_, (node1, op, node2)) = find_bin_operator(tokens)?;
|
let (_, (node1, op, node2)) = find_bin_operator(tokens)?;
|
||||||
let (_, node1) = node(node1)?;
|
let (_, node1) = node(node1)?;
|
||||||
let (_, node2) = node(node2)?;
|
let (_, node2) = node(node2)?;
|
||||||
|
|
@ -103,7 +97,7 @@ fn bin_operator(tokens: &[OldToken]) -> Res {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_bin_operator(tokens: &[OldToken]) -> Res<(&[OldToken], Operator, &[OldToken])> {
|
fn find_bin_operator(tokens: &[Token]) -> Res<(&[Token], Operator, &[Token])> {
|
||||||
static PREDESCENCE: [Operator; 5] = [
|
static PREDESCENCE: [Operator; 5] = [
|
||||||
Operator::Plus,
|
Operator::Plus,
|
||||||
Operator::Minus,
|
Operator::Minus,
|
||||||
|
|
@ -118,9 +112,9 @@ fn find_bin_operator(tokens: &[OldToken]) -> Res<(&[OldToken], Operator, &[OldTo
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter_map(|(i, token)| {
|
.filter_map(|(i, token)| {
|
||||||
match token {
|
match token {
|
||||||
OldToken::GroupOpen(_) => inner += 1,
|
Token::GroupOpen(_) => inner += 1,
|
||||||
OldToken::GroupClose(_) => inner -= 1,
|
Token::GroupClose(_) => inner -= 1,
|
||||||
OldToken::Operator(op) if op == operator => {
|
Token::Operator(op) if op == operator => {
|
||||||
if inner == 0 {
|
if inner == 0 {
|
||||||
return Some(i);
|
return Some(i);
|
||||||
}
|
}
|
||||||
|
|
@ -141,7 +135,7 @@ fn find_bin_operator(tokens: &[OldToken]) -> Res<(&[OldToken], Operator, &[OldTo
|
||||||
Err(Err::Error(GenerationError::NoToken))
|
Err(Err::Error(GenerationError::NoToken))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn constant(tokens: &[OldToken]) -> Res {
|
fn constant(tokens: &[Token]) -> Res {
|
||||||
let (rest, val) = alt((
|
let (rest, val) = alt((
|
||||||
map(
|
map(
|
||||||
tuple((opt(sign), real_part, sign, imaginary_part)),
|
tuple((opt(sign), real_part, sign, imaginary_part)),
|
||||||
|
|
@ -171,11 +165,11 @@ fn constant(tokens: &[OldToken]) -> Res {
|
||||||
Ok((rest, Constant(val).into()))
|
Ok((rest, Constant(val).into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn real_part(tokens: &[OldToken]) -> Res<f64> {
|
fn real_part(tokens: &[Token]) -> Res<f64> {
|
||||||
number(tokens)
|
number(tokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn imaginary_part(tokens: &[OldToken]) -> Res<f64> {
|
fn imaginary_part(tokens: &[Token]) -> Res<f64> {
|
||||||
alt((
|
alt((
|
||||||
map(tuple((number, opt(star), imaginary_unit)), |(n, _, ())| n),
|
map(tuple((number, opt(star), imaginary_unit)), |(n, _, ())| n),
|
||||||
map(tuple((imaginary_unit, star, number)), |((), (), n)| n),
|
map(tuple((imaginary_unit, star, number)), |((), (), n)| n),
|
||||||
|
|
@ -183,7 +177,7 @@ fn imaginary_part(tokens: &[OldToken]) -> Res<f64> {
|
||||||
))(tokens)
|
))(tokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn function_call(tokens: &[OldToken]) -> Res {
|
fn function_call(tokens: &[Token]) -> Res {
|
||||||
map(
|
map(
|
||||||
tuple((
|
tuple((
|
||||||
opt(sign),
|
opt(sign),
|
||||||
|
|
@ -208,7 +202,7 @@ fn function_call(tokens: &[OldToken]) -> Res {
|
||||||
)(tokens)
|
)(tokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn variable(tokens: &[OldToken]) -> Res {
|
fn variable(tokens: &[Token]) -> Res {
|
||||||
map(tuple((opt(sign), identifier)), |(minus, name)| {
|
map(tuple((opt(sign), identifier)), |(minus, name)| {
|
||||||
if let Some(true) = minus {
|
if let Some(true) = minus {
|
||||||
Negation::new(Variable::new(name)).into()
|
Negation::new(Variable::new(name)).into()
|
||||||
|
|
@ -220,14 +214,14 @@ fn variable(tokens: &[OldToken]) -> Res {
|
||||||
|
|
||||||
// general tokens
|
// general tokens
|
||||||
|
|
||||||
fn assert_parens(tokens: &[OldToken]) -> Res<()> {
|
fn assert_parens(tokens: &[Token]) -> Res<()> {
|
||||||
let mut stack = Vec::new();
|
let mut stack = Vec::new();
|
||||||
for token in tokens {
|
for token in tokens {
|
||||||
if let OldToken::GroupOpen(t) = token {
|
if let Token::GroupOpen(t) = token {
|
||||||
stack.push(t);
|
stack.push(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let OldToken::GroupClose(t) = token {
|
if let Token::GroupClose(t) = token {
|
||||||
let Some(&e) = stack.last() else {
|
let Some(&e) = stack.last() else {
|
||||||
return Err(Err::Failure(GenerationError::UnpairedGrouping));
|
return Err(Err::Failure(GenerationError::UnpairedGrouping));
|
||||||
};
|
};
|
||||||
|
|
@ -244,13 +238,13 @@ fn assert_parens(tokens: &[OldToken]) -> Res<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn number(tokens: &[OldToken]) -> Res<f64> {
|
fn number(tokens: &[Token]) -> Res<f64> {
|
||||||
let token = tokens
|
let token = tokens
|
||||||
.first()
|
.first()
|
||||||
.ok_or(Err::Error(GenerationError::Empty(Needed::Size(
|
.ok_or(Err::Error(GenerationError::Empty(Needed::Size(
|
||||||
NonZeroUsize::try_from(1).expect("1 is not zero"),
|
NonZeroUsize::try_from(1).expect("1 is not zero"),
|
||||||
))))?;
|
))))?;
|
||||||
let OldToken::Number(val) = token else {
|
let Token::Number(val) = token else {
|
||||||
return Err(Err::Error(GenerationError::BadToken(
|
return Err(Err::Error(GenerationError::BadToken(
|
||||||
TokenType::Constant,
|
TokenType::Constant,
|
||||||
token,
|
token,
|
||||||
|
|
@ -259,13 +253,13 @@ fn number(tokens: &[OldToken]) -> Res<f64> {
|
||||||
Ok((&tokens[1..], *val))
|
Ok((&tokens[1..], *val))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn plus(tokens: &[OldToken]) -> Res<()> {
|
fn plus(tokens: &[Token]) -> Res<()> {
|
||||||
let t = tokens
|
let t = tokens
|
||||||
.first()
|
.first()
|
||||||
.ok_or(Err::Error(GenerationError::Empty(Needed::Size(
|
.ok_or(Err::Error(GenerationError::Empty(Needed::Size(
|
||||||
NonZeroUsize::try_from(1).expect("1 is not zero"),
|
NonZeroUsize::try_from(1).expect("1 is not zero"),
|
||||||
))))?;
|
))))?;
|
||||||
let OldToken::Operator(Operator::Plus) = t else {
|
let Token::Operator(Operator::Plus) = t else {
|
||||||
return Err(Err::Error(GenerationError::BadToken(
|
return Err(Err::Error(GenerationError::BadToken(
|
||||||
TokenType::Operator,
|
TokenType::Operator,
|
||||||
t,
|
t,
|
||||||
|
|
@ -274,13 +268,13 @@ fn plus(tokens: &[OldToken]) -> Res<()> {
|
||||||
Ok((&tokens[1..], ()))
|
Ok((&tokens[1..], ()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn minus(tokens: &[OldToken]) -> Res<()> {
|
fn minus(tokens: &[Token]) -> Res<()> {
|
||||||
let t = tokens
|
let t = tokens
|
||||||
.first()
|
.first()
|
||||||
.ok_or(Err::Error(GenerationError::Empty(Needed::Size(
|
.ok_or(Err::Error(GenerationError::Empty(Needed::Size(
|
||||||
NonZeroUsize::try_from(1).expect("1 is not zero"),
|
NonZeroUsize::try_from(1).expect("1 is not zero"),
|
||||||
))))?;
|
))))?;
|
||||||
let OldToken::Operator(Operator::Minus) = t else {
|
let Token::Operator(Operator::Minus) = t else {
|
||||||
return Err(Err::Error(GenerationError::BadToken(
|
return Err(Err::Error(GenerationError::BadToken(
|
||||||
TokenType::Operator,
|
TokenType::Operator,
|
||||||
t,
|
t,
|
||||||
|
|
@ -289,13 +283,13 @@ fn minus(tokens: &[OldToken]) -> Res<()> {
|
||||||
Ok((&tokens[1..], ()))
|
Ok((&tokens[1..], ()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn star(tokens: &[OldToken]) -> Res<()> {
|
fn star(tokens: &[Token]) -> Res<()> {
|
||||||
let t = tokens
|
let t = tokens
|
||||||
.first()
|
.first()
|
||||||
.ok_or(Err::Error(GenerationError::Empty(Needed::Size(
|
.ok_or(Err::Error(GenerationError::Empty(Needed::Size(
|
||||||
NonZeroUsize::try_from(1).expect("1 is not zero"),
|
NonZeroUsize::try_from(1).expect("1 is not zero"),
|
||||||
))))?;
|
))))?;
|
||||||
let OldToken::Operator(Operator::Star) = t else {
|
let Token::Operator(Operator::Star) = t else {
|
||||||
return Err(Err::Error(GenerationError::BadToken(
|
return Err(Err::Error(GenerationError::BadToken(
|
||||||
TokenType::Operator,
|
TokenType::Operator,
|
||||||
t,
|
t,
|
||||||
|
|
@ -304,13 +298,13 @@ fn star(tokens: &[OldToken]) -> Res<()> {
|
||||||
Ok((&tokens[1..], ()))
|
Ok((&tokens[1..], ()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn imaginary_unit(tokens: &[OldToken]) -> Res<()> {
|
fn imaginary_unit(tokens: &[Token]) -> Res<()> {
|
||||||
let t = tokens
|
let t = tokens
|
||||||
.first()
|
.first()
|
||||||
.ok_or(Err::Error(GenerationError::Empty(Needed::Size(
|
.ok_or(Err::Error(GenerationError::Empty(Needed::Size(
|
||||||
NonZeroUsize::try_from(1).expect("1 is not zero"),
|
NonZeroUsize::try_from(1).expect("1 is not zero"),
|
||||||
))))?;
|
))))?;
|
||||||
let OldToken::ImaginaryUnit = t else {
|
let Token::ImaginaryUnit = t else {
|
||||||
return Err(Err::Error(GenerationError::BadToken(
|
return Err(Err::Error(GenerationError::BadToken(
|
||||||
TokenType::Operator,
|
TokenType::Operator,
|
||||||
t,
|
t,
|
||||||
|
|
@ -319,13 +313,13 @@ fn imaginary_unit(tokens: &[OldToken]) -> Res<()> {
|
||||||
Ok((&tokens[1..], ()))
|
Ok((&tokens[1..], ()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn identifier(tokens: &[OldToken]) -> Res<String> {
|
fn identifier(tokens: &[Token]) -> Res<String> {
|
||||||
let t = tokens
|
let t = tokens
|
||||||
.first()
|
.first()
|
||||||
.ok_or(Err::Error(GenerationError::Empty(Needed::Size(
|
.ok_or(Err::Error(GenerationError::Empty(Needed::Size(
|
||||||
NonZeroUsize::try_from(1).expect("1 is not zero"),
|
NonZeroUsize::try_from(1).expect("1 is not zero"),
|
||||||
))))?;
|
))))?;
|
||||||
let OldToken::Identifier(name) = t else {
|
let Token::Identifier(name) = t else {
|
||||||
return Err(Err::Error(GenerationError::BadToken(
|
return Err(Err::Error(GenerationError::BadToken(
|
||||||
TokenType::Operator,
|
TokenType::Operator,
|
||||||
t,
|
t,
|
||||||
|
|
@ -334,21 +328,21 @@ fn identifier(tokens: &[OldToken]) -> Res<String> {
|
||||||
Ok((&tokens[1..], name.to_string()))
|
Ok((&tokens[1..], name.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn comma_separated(tokens: &[OldToken]) -> Res<(Node<Complex64>, Node<Complex64>)> {
|
fn comma_separated(tokens: &[Token]) -> Res<(Node<Complex64>, Node<Complex64>)> {
|
||||||
let (_, (arg1, arg2)) = find_comma(tokens)?;
|
let (_, (arg1, arg2)) = find_comma(tokens)?;
|
||||||
let (_, arg1) = node(arg1)?;
|
let (_, arg1) = node(arg1)?;
|
||||||
let (_, arg2) = node(arg2)?;
|
let (_, arg2) = node(arg2)?;
|
||||||
Ok((&[], (arg1, arg2)))
|
Ok((&[], (arg1, arg2)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_comma(tokens: &[OldToken]) -> Res<(&[OldToken], &[OldToken])> {
|
fn find_comma(tokens: &[Token]) -> Res<(&[Token], &[Token])> {
|
||||||
let mut inline = Vec::new();
|
let mut inline = Vec::new();
|
||||||
for (i, token) in tokens.iter().enumerate() {
|
for (i, token) in tokens.iter().enumerate() {
|
||||||
match token {
|
match token {
|
||||||
OldToken::GroupOpen(t) => {
|
Token::GroupOpen(t) => {
|
||||||
inline.push(t);
|
inline.push(t);
|
||||||
}
|
}
|
||||||
OldToken::GroupClose(t) => {
|
Token::GroupClose(t) => {
|
||||||
if &t
|
if &t
|
||||||
!= inline
|
!= inline
|
||||||
.last()
|
.last()
|
||||||
|
|
@ -359,7 +353,7 @@ fn find_comma(tokens: &[OldToken]) -> Res<(&[OldToken], &[OldToken])> {
|
||||||
|
|
||||||
inline.pop();
|
inline.pop();
|
||||||
}
|
}
|
||||||
OldToken::Comma => {
|
Token::Comma => {
|
||||||
if inline.is_empty() {
|
if inline.is_empty() {
|
||||||
return Ok((&[], (&tokens[..i], &tokens[i + 1..])));
|
return Ok((&[], (&tokens[..i], &tokens[i + 1..])));
|
||||||
}
|
}
|
||||||
|
|
@ -370,17 +364,17 @@ fn find_comma(tokens: &[OldToken]) -> Res<(&[OldToken], &[OldToken])> {
|
||||||
Err(Err::Error(GenerationError::NoToken))
|
Err(Err::Error(GenerationError::NoToken))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sign(tokens: &[OldToken]) -> Res<bool> {
|
fn sign(tokens: &[Token]) -> Res<bool> {
|
||||||
alt((map(plus, |()| false), map(minus, |()| true)))(tokens)
|
alt((map(plus, |()| false), map(minus, |()| true)))(tokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paren(tokens: &[OldToken]) -> Res<&[OldToken]> {
|
fn paren(tokens: &[Token]) -> Res<&[Token]> {
|
||||||
let t = tokens
|
let t = tokens
|
||||||
.first()
|
.first()
|
||||||
.ok_or(Err::Error(GenerationError::Empty(Needed::Size(
|
.ok_or(Err::Error(GenerationError::Empty(Needed::Size(
|
||||||
NonZeroUsize::try_from(1).expect("1 is not zero"),
|
NonZeroUsize::try_from(1).expect("1 is not zero"),
|
||||||
))))?;
|
))))?;
|
||||||
let OldToken::GroupOpen(t) = t else {
|
let Token::GroupOpen(t) = t else {
|
||||||
return Err(Err::Error(GenerationError::BadToken(
|
return Err(Err::Error(GenerationError::BadToken(
|
||||||
TokenType::Grouping,
|
TokenType::Grouping,
|
||||||
t,
|
t,
|
||||||
|
|
@ -390,12 +384,12 @@ fn paren(tokens: &[OldToken]) -> Res<&[OldToken]> {
|
||||||
let mut inner = 1;
|
let mut inner = 1;
|
||||||
for i in 1..tokens.len() {
|
for i in 1..tokens.len() {
|
||||||
let token = &tokens[i];
|
let token = &tokens[i];
|
||||||
if let OldToken::GroupOpen(e) = token {
|
if let Token::GroupOpen(e) = token {
|
||||||
if t == e {
|
if t == e {
|
||||||
inner += 1;
|
inner += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let OldToken::GroupClose(e) = token {
|
if let Token::GroupClose(e) = token {
|
||||||
if t == e {
|
if t == e {
|
||||||
inner -= 1;
|
inner -= 1;
|
||||||
|
|
||||||
|
|
@ -413,7 +407,6 @@ fn paren(tokens: &[OldToken]) -> Res<&[OldToken]> {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use alloc::borrow::ToOwned;
|
|
||||||
use num::complex::Complex64;
|
use num::complex::Complex64;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
|
||||||
|
|
@ -424,7 +417,7 @@ mod tests {
|
||||||
functions::NamedFunction,
|
functions::NamedFunction,
|
||||||
Constant, Evaluatable, Node, Variable,
|
Constant, Evaluatable, Node, Variable,
|
||||||
},
|
},
|
||||||
lexer::{GroupingType, OldToken, Operator},
|
lexer::{GroupingType, Operator, Token},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::parse;
|
use super::parse;
|
||||||
|
|
@ -489,122 +482,122 @@ mod tests {
|
||||||
|
|
||||||
// following tests reinforce that complex constant recognition works as expected:
|
// following tests reinforce that complex constant recognition works as expected:
|
||||||
test! {constant1,
|
test! {constant1,
|
||||||
tokens = [OldToken::Number(1.0f64)],
|
tokens = [Token::Number(1.0f64)],
|
||||||
expected = Constant::from(1.0f64)}
|
expected = Constant::from(1.0f64)}
|
||||||
test! {constant2,
|
test! {constant2,
|
||||||
tokens = [OldToken::ImaginaryUnit],
|
tokens = [Token::ImaginaryUnit],
|
||||||
expected = Constant::from(1.0f64).tr()}
|
expected = Constant::from(1.0f64).tr()}
|
||||||
test! {constant3,
|
test! {constant3,
|
||||||
tokens = [
|
tokens = [
|
||||||
OldToken::Number(1.0f64),
|
Token::Number(1.0f64),
|
||||||
OldToken::Operator(Operator::Star),
|
Token::Operator(Operator::Star),
|
||||||
OldToken::ImaginaryUnit
|
Token::ImaginaryUnit
|
||||||
], expected = Constant::from(1.0f64).tr()}
|
], expected = Constant::from(1.0f64).tr()}
|
||||||
test! {constant4,
|
test! {constant4,
|
||||||
tokens = [
|
tokens = [
|
||||||
OldToken::Number(1.0f64),
|
Token::Number(1.0f64),
|
||||||
OldToken::Operator(Operator::Plus),
|
Token::Operator(Operator::Plus),
|
||||||
OldToken::ImaginaryUnit
|
Token::ImaginaryUnit
|
||||||
], expected = Constant(Complex64::new(1.0, 1.0))}
|
], expected = Constant(Complex64::new(1.0, 1.0))}
|
||||||
test! {constant5,
|
test! {constant5,
|
||||||
tokens = [
|
tokens = [
|
||||||
OldToken::Operator(Operator::Minus),
|
Token::Operator(Operator::Minus),
|
||||||
OldToken::Number(1.0f64),
|
Token::Number(1.0f64),
|
||||||
OldToken::Operator(Operator::Plus),
|
Token::Operator(Operator::Plus),
|
||||||
OldToken::Number(1.0),
|
Token::Number(1.0),
|
||||||
OldToken::Operator(Operator::Star),
|
Token::Operator(Operator::Star),
|
||||||
OldToken::ImaginaryUnit
|
Token::ImaginaryUnit
|
||||||
], expected = Constant(Complex64::new(-1.0, 1.0))}
|
], expected = Constant(Complex64::new(-1.0, 1.0))}
|
||||||
test! {constant6,
|
test! {constant6,
|
||||||
tokens = [
|
tokens = [
|
||||||
OldToken::Operator(Operator::Minus),
|
Token::Operator(Operator::Minus),
|
||||||
OldToken::Number(1.0f64),
|
Token::Number(1.0f64),
|
||||||
OldToken::Operator(Operator::Plus),
|
Token::Operator(Operator::Plus),
|
||||||
OldToken::Number(1.0), OldToken::ImaginaryUnit
|
Token::Number(1.0), Token::ImaginaryUnit
|
||||||
], expected = Constant(Complex64::new(-1.0, 1.0))}
|
], expected = Constant(Complex64::new(-1.0, 1.0))}
|
||||||
test! {constant7,
|
test! {constant7,
|
||||||
tokens = [
|
tokens = [
|
||||||
OldToken::Operator(Operator::Minus),
|
Token::Operator(Operator::Minus),
|
||||||
OldToken::ImaginaryUnit,
|
Token::ImaginaryUnit,
|
||||||
OldToken::Operator(Operator::Star),
|
Token::Operator(Operator::Star),
|
||||||
OldToken::Number(1.0f64),
|
Token::Number(1.0f64),
|
||||||
OldToken::Operator(Operator::Plus),
|
Token::Operator(Operator::Plus),
|
||||||
OldToken::Number(1.0)
|
Token::Number(1.0)
|
||||||
], expected = Constant(Complex64::new(1.0, -1.0))}
|
], expected = Constant(Complex64::new(1.0, -1.0))}
|
||||||
test! {constant8,
|
test! {constant8,
|
||||||
tokens = [
|
tokens = [
|
||||||
OldToken::Operator(Operator::Minus),
|
Token::Operator(Operator::Minus),
|
||||||
OldToken::ImaginaryUnit,
|
Token::ImaginaryUnit,
|
||||||
OldToken::Operator(Operator::Plus),
|
Token::Operator(Operator::Plus),
|
||||||
OldToken::Number(1.0)
|
Token::Number(1.0)
|
||||||
], expected = Constant(Complex64::new(1.0, -1.0))}
|
], expected = Constant(Complex64::new(1.0, -1.0))}
|
||||||
test! {constant9,
|
test! {constant9,
|
||||||
tokens = [
|
tokens = [
|
||||||
OldToken::Operator(Operator::Minus),
|
Token::Operator(Operator::Minus),
|
||||||
OldToken::ImaginaryUnit
|
Token::ImaginaryUnit
|
||||||
], expected = Constant(Complex64::new(0.0, -1.0))}
|
], expected = Constant(Complex64::new(0.0, -1.0))}
|
||||||
|
|
||||||
// following tests reinforce that variable recognition works as expected:
|
// following tests reinforce that variable recognition works as expected:
|
||||||
test! {variable1,
|
test! {variable1,
|
||||||
tokens = [OldToken::Identifier("x".to_owned())],
|
tokens = [Token::Identifier("x".to_owned())],
|
||||||
expected = Variable::new("x")}
|
expected = Variable::new("x")}
|
||||||
test! {variable2,
|
test! {variable2,
|
||||||
tokens = [OldToken::Identifier("a_123".to_owned())],
|
tokens = [Token::Identifier("a_123".to_owned())],
|
||||||
expected = Variable::new("a_123")}
|
expected = Variable::new("a_123")}
|
||||||
|
|
||||||
// following tests reinforce that function calls work as expected:
|
// following tests reinforce that function calls work as expected:
|
||||||
test! {function1,
|
test! {function1,
|
||||||
tokens = [
|
tokens = [
|
||||||
OldToken::Identifier("sin".to_owned()),
|
Token::Identifier("sin".to_owned()),
|
||||||
OldToken::GroupOpen(GroupingType::Parentheses),
|
Token::GroupOpen(GroupingType::Parentheses),
|
||||||
OldToken::Identifier("x".to_owned()),
|
Token::Identifier("x".to_owned()),
|
||||||
OldToken::GroupClose(GroupingType::Parentheses)
|
Token::GroupClose(GroupingType::Parentheses)
|
||||||
], expected = NamedFunction::new("sin", Variable::new("x"))}
|
], expected = NamedFunction::new("sin", Variable::new("x"))}
|
||||||
|
|
||||||
test! {function2,
|
test! {function2,
|
||||||
tokens = [
|
tokens = [
|
||||||
OldToken::Identifier("sum_sq".to_owned()),
|
Token::Identifier("sum_sq".to_owned()),
|
||||||
OldToken::GroupOpen(GroupingType::Parentheses),
|
Token::GroupOpen(GroupingType::Parentheses),
|
||||||
OldToken::Identifier("x".to_owned()),
|
Token::Identifier("x".to_owned()),
|
||||||
OldToken::Comma,
|
Token::Comma,
|
||||||
OldToken::Identifier("y".to_owned()),
|
Token::Identifier("y".to_owned()),
|
||||||
OldToken::GroupClose(GroupingType::Parentheses)
|
Token::GroupClose(GroupingType::Parentheses)
|
||||||
], expected = NamedFunction::new("sum_sq", (Variable::new("x"), Variable::new("y")))}
|
], expected = NamedFunction::new("sum_sq", (Variable::new("x"), Variable::new("y")))}
|
||||||
|
|
||||||
test! {function3,
|
test! {function3,
|
||||||
tokens = [
|
tokens = [
|
||||||
OldToken::Operator(Operator::Minus),
|
Token::Operator(Operator::Minus),
|
||||||
OldToken::Identifier("sin".to_owned()),
|
Token::Identifier("sin".to_owned()),
|
||||||
OldToken::GroupOpen(GroupingType::Parentheses),
|
Token::GroupOpen(GroupingType::Parentheses),
|
||||||
OldToken::Identifier("x".to_owned()),
|
Token::Identifier("x".to_owned()),
|
||||||
OldToken::GroupClose(GroupingType::Parentheses)
|
Token::GroupClose(GroupingType::Parentheses)
|
||||||
], expected = Negation::new(NamedFunction::new("sin", Variable::new("x")))}
|
], expected = Negation::new(NamedFunction::new("sin", Variable::new("x")))}
|
||||||
|
|
||||||
// following tests reinforce correct binary operator parsing
|
// following tests reinforce correct binary operator parsing
|
||||||
// x + y * 2
|
// x + y * 2
|
||||||
test! {bin_operator1, tokens = [
|
test! {bin_operator1, tokens = [
|
||||||
OldToken::Identifier("x".to_owned()),
|
Token::Identifier("x".to_owned()),
|
||||||
OldToken::Operator(Operator::Plus),
|
Token::Operator(Operator::Plus),
|
||||||
OldToken::Identifier("y".to_owned()),
|
Token::Identifier("y".to_owned()),
|
||||||
OldToken::Operator(Operator::Star),
|
Token::Operator(Operator::Star),
|
||||||
OldToken::Number(2f64)
|
Token::Number(2f64)
|
||||||
], expected = Addition::new(Variable::new("x"), Multiplication::new(Variable::new("y"), Constant(2f64.into())))}
|
], expected = Addition::new(Variable::new("x"), Multiplication::new(Variable::new("y"), Constant(2f64.into())))}
|
||||||
|
|
||||||
// [-x + 4] * sin{y}^2
|
// [-x + 4] * sin{y}^2
|
||||||
test! {bin_operator2, tokens = [
|
test! {bin_operator2, tokens = [
|
||||||
OldToken::GroupOpen(GroupingType::Brackets),
|
Token::GroupOpen(GroupingType::Brackets),
|
||||||
OldToken::Operator(Operator::Minus),
|
Token::Operator(Operator::Minus),
|
||||||
OldToken::Identifier("x".to_owned()),
|
Token::Identifier("x".to_owned()),
|
||||||
OldToken::Operator(Operator::Plus),
|
Token::Operator(Operator::Plus),
|
||||||
OldToken::Number(4f64),
|
Token::Number(4f64),
|
||||||
OldToken::GroupClose(GroupingType::Brackets),
|
Token::GroupClose(GroupingType::Brackets),
|
||||||
OldToken::Operator(Operator::Star),
|
Token::Operator(Operator::Star),
|
||||||
OldToken::Identifier("sin".to_owned()),
|
Token::Identifier("sin".to_owned()),
|
||||||
OldToken::GroupOpen(GroupingType::Braces),
|
Token::GroupOpen(GroupingType::Braces),
|
||||||
OldToken::Identifier("y".to_owned()),
|
Token::Identifier("y".to_owned()),
|
||||||
OldToken::GroupClose(GroupingType::Braces),
|
Token::GroupClose(GroupingType::Braces),
|
||||||
OldToken::Operator(Operator::Cap),
|
Token::Operator(Operator::Cap),
|
||||||
OldToken::Number(2f64)
|
Token::Number(2f64)
|
||||||
], expected = Multiplication::new(
|
], expected = Multiplication::new(
|
||||||
Addition::new(
|
Addition::new(
|
||||||
Negation::new(Variable::new("x")),
|
Negation::new(Variable::new("x")),
|
||||||
|
|
@ -621,24 +614,24 @@ mod tests {
|
||||||
|
|
||||||
// z ^ [z] + sin(1.0 / e ^ {i * phi})
|
// z ^ [z] + sin(1.0 / e ^ {i * phi})
|
||||||
test! {bin_operator3, tokens = [
|
test! {bin_operator3, tokens = [
|
||||||
OldToken::Identifier("z".to_owned()),
|
Token::Identifier("z".to_owned()),
|
||||||
OldToken::Operator(Operator::Cap),
|
Token::Operator(Operator::Cap),
|
||||||
OldToken::GroupOpen(GroupingType::Brackets),
|
Token::GroupOpen(GroupingType::Brackets),
|
||||||
OldToken::Identifier("z".to_owned()),
|
Token::Identifier("z".to_owned()),
|
||||||
OldToken::GroupClose(GroupingType::Brackets),
|
Token::GroupClose(GroupingType::Brackets),
|
||||||
OldToken::Operator(Operator::Plus),
|
Token::Operator(Operator::Plus),
|
||||||
OldToken::Identifier("sin".to_owned()),
|
Token::Identifier("sin".to_owned()),
|
||||||
OldToken::GroupOpen(GroupingType::Parentheses),
|
Token::GroupOpen(GroupingType::Parentheses),
|
||||||
OldToken::Number(1.0),
|
Token::Number(1.0),
|
||||||
OldToken::Operator(Operator::Slash),
|
Token::Operator(Operator::Slash),
|
||||||
OldToken::Identifier("e".to_owned()),
|
Token::Identifier("e".to_owned()),
|
||||||
OldToken::Operator(Operator::Cap),
|
Token::Operator(Operator::Cap),
|
||||||
OldToken::GroupOpen(GroupingType::Braces),
|
Token::GroupOpen(GroupingType::Braces),
|
||||||
OldToken::ImaginaryUnit,
|
Token::ImaginaryUnit,
|
||||||
OldToken::Operator(Operator::Star),
|
Token::Operator(Operator::Star),
|
||||||
OldToken::Identifier("phi".to_owned()),
|
Token::Identifier("phi".to_owned()),
|
||||||
OldToken::GroupClose(GroupingType::Braces),
|
Token::GroupClose(GroupingType::Braces),
|
||||||
OldToken::GroupClose(GroupingType::Parentheses)
|
Token::GroupClose(GroupingType::Parentheses)
|
||||||
], expected = Addition::new(
|
], expected = Addition::new(
|
||||||
Exponentiation::new(
|
Exponentiation::new(
|
||||||
Variable::new("z"),
|
Variable::new("z"),
|
||||||
Loading…
Reference in a new issue