Merge branch 'master' of ../simplexpr into config_rework
This commit is contained in:
commit
810dbb1368
42 changed files with 1927 additions and 0 deletions
656
crates/simplexpr/Cargo.lock
generated
Normal file
656
crates/simplexpr/Cargo.lock
generated
Normal file
|
@ -0,0 +1,656 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ascii-canvas"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6"
|
||||
dependencies = [
|
||||
"term",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
|
||||
[[package]]
|
||||
name = "beef"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6736e2428df2ca2848d846c43e88745121a6654696e349ce0054a420815a7409"
|
||||
|
||||
[[package]]
|
||||
name = "bit-set"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de"
|
||||
dependencies = [
|
||||
"bit-vec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit-vec"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "console"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3993e6445baa160675931ec041a5e03ca84b9c6e32a056150d3aa2bdda0a1f45"
|
||||
dependencies = [
|
||||
"encode_unicode",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"terminal_size",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crunchy"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
||||
|
||||
[[package]]
|
||||
name = "diff"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499"
|
||||
|
||||
[[package]]
|
||||
name = "dirs-next"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"dirs-sys-next",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys-next"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"redox_users",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dtoa"
|
||||
version = "0.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
|
||||
|
||||
[[package]]
|
||||
name = "ena"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7402b94a93c24e742487327a7cd839dc9d36fec9de9fb25b09f2dae459f36c3"
|
||||
dependencies = [
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encode_unicode"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
|
||||
|
||||
[[package]]
|
||||
name = "fixedbitset"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d"
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
|
||||
dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "insta"
|
||||
version = "1.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4a1b21a2971cea49ca4613c0e9fe8225ecaf5de64090fddc6002284726e9244"
|
||||
dependencies = [
|
||||
"console",
|
||||
"lazy_static",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_yaml",
|
||||
"similar",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
|
||||
|
||||
[[package]]
|
||||
name = "lalrpop"
|
||||
version = "0.19.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b15174f1c529af5bf1283c3bc0058266b483a67156f79589fab2a25e23cf8988"
|
||||
dependencies = [
|
||||
"ascii-canvas",
|
||||
"atty",
|
||||
"bit-set",
|
||||
"diff",
|
||||
"ena",
|
||||
"itertools",
|
||||
"lalrpop-util",
|
||||
"petgraph",
|
||||
"pico-args",
|
||||
"regex",
|
||||
"regex-syntax",
|
||||
"string_cache",
|
||||
"term",
|
||||
"tiny-keccak",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lalrpop-util"
|
||||
version = "0.19.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3e58cce361efcc90ba8a0a5f982c741ff86b603495bb15a998412e957dcd278"
|
||||
dependencies = [
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790"
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "logos"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "427e2abca5be13136da9afdbf874e6b34ad9001dd70f2b103b083a85daa7b345"
|
||||
dependencies = [
|
||||
"logos-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "logos-derive"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56a7d287fd2ac3f75b11f19a1c8a874a7d55744bd91f7a1b3e7cf87d4343c36d"
|
||||
dependencies = [
|
||||
"beef",
|
||||
"fnv",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex-syntax",
|
||||
"syn",
|
||||
"utf8-ranges",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "maplit"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc"
|
||||
|
||||
[[package]]
|
||||
name = "new_debug_unreachable"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
|
||||
|
||||
[[package]]
|
||||
name = "petgraph"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7"
|
||||
dependencies = [
|
||||
"fixedbitset",
|
||||
"indexmap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7"
|
||||
dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pico-args"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468"
|
||||
|
||||
[[package]]
|
||||
name = "precomputed-hash"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ab49abadf3f9e1c4bc499e8845e152ad87d2ad2d30371841171169e9d75feee"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"redox_syscall",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61b3909d758bb75c79f23d4736fac9433868679d3ad2ea7a61e3c25cfda9a088"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.126"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.126"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_yaml"
|
||||
version = "0.8.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15654ed4ab61726bf918a39cb8d98a2e2995b002387807fa6ba58fdf7f59bb23"
|
||||
dependencies = [
|
||||
"dtoa",
|
||||
"linked-hash-map",
|
||||
"serde",
|
||||
"yaml-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "similar"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ad1d488a557b235fc46dae55512ffbfc429d2482b08b4d9435ab07384ca8aec"
|
||||
|
||||
[[package]]
|
||||
name = "simplexpr"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"insta",
|
||||
"itertools",
|
||||
"lalrpop",
|
||||
"lalrpop-util",
|
||||
"logos",
|
||||
"maplit",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"strum",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cbce6d4507c7e4a3962091436e56e95290cb71fa302d0d270e32130b75fbff27"
|
||||
|
||||
[[package]]
|
||||
name = "string_cache"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ddb1139b5353f96e429e1a5e19fbaf663bddedaa06d1dbd49f82e352601209a"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"new_debug_unreachable",
|
||||
"phf_shared",
|
||||
"precomputed-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strum"
|
||||
version = "0.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aaf86bbcfd1fa9670b7a129f64fc0c9fcbbfe4f1bc4210e9e98fe71ffc12cde2"
|
||||
dependencies = [
|
||||
"strum_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strum_macros"
|
||||
version = "0.21.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d06aaeeee809dbc59eb4556183dd927df67db1540de5be8d3ec0b6636358a5ec"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.73"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "term"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f"
|
||||
dependencies = [
|
||||
"dirs-next",
|
||||
"rustversion",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "terminal_size"
|
||||
version = "0.1.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93119e4feac1cbe6c798c34d3a53ea0026b0b1de6a120deef895137c0529bfe2"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "060d69a0afe7796bf42e9e2ff91f5ee691fb15c53d38b4b62a9a53eb23164745"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiny-keccak"
|
||||
version = "2.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
|
||||
dependencies = [
|
||||
"crunchy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "utf8-ranges"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4ae116fef2b7fea257ed6440d3cfcff7f190865f170cdad00bb6465bf18ecba"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.10.2+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
||||
|
||||
[[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 = "yaml-rust"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
|
||||
dependencies = [
|
||||
"linked-hash-map",
|
||||
]
|
29
crates/simplexpr/Cargo.toml
Normal file
29
crates/simplexpr/Cargo.toml
Normal file
|
@ -0,0 +1,29 @@
|
|||
[package]
|
||||
name = "simplexpr"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
authors = ["elkowar <5300871+elkowar@users.noreply.github.com>"]
|
||||
|
||||
|
||||
build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
lalrpop-util = "0.19.5"
|
||||
regex = "1"
|
||||
itertools = "0.10"
|
||||
thiserror = "1.0"
|
||||
maplit = "1.0"
|
||||
logos = "0.12"
|
||||
|
||||
serde = {version = "1.0", features = ["derive"]}
|
||||
serde_json = "1.0"
|
||||
|
||||
strum = { version = "0.21", features = ["derive"] }
|
||||
|
||||
|
||||
|
||||
[build-dependencies]
|
||||
lalrpop = "0.19.5"
|
||||
|
||||
[dev-dependencies]
|
||||
insta = "1.7"
|
6
crates/simplexpr/README.md
Normal file
6
crates/simplexpr/README.md
Normal file
|
@ -0,0 +1,6 @@
|
|||
# simplexpr
|
||||
|
||||
simplexpr is a parser and interpreter for a simple expression syntax that can be embedded into other applications or crates.
|
||||
It is being developed to be used in [eww](https://github.com/elkowar/eww), but may also other uses.
|
||||
|
||||
For now, this is highly experimental, unstable, and ugly. You most definitely do not want to use this crate.
|
4
crates/simplexpr/build.rs
Normal file
4
crates/simplexpr/build.rs
Normal file
|
@ -0,0 +1,4 @@
|
|||
extern crate lalrpop;
|
||||
fn main() {
|
||||
lalrpop::Configuration::new().log_verbose().process_current_dir().unwrap();
|
||||
}
|
1
crates/simplexpr/rust-toolchain
Normal file
1
crates/simplexpr/rust-toolchain
Normal file
|
@ -0,0 +1 @@
|
|||
nightly
|
14
crates/simplexpr/rustfmt.toml
Normal file
14
crates/simplexpr/rustfmt.toml
Normal file
|
@ -0,0 +1,14 @@
|
|||
unstable_features = true
|
||||
fn_single_line = false
|
||||
max_width = 130
|
||||
reorder_impl_items = true
|
||||
merge_imports = true
|
||||
normalize_comments = true
|
||||
use_field_init_shorthand = true
|
||||
#wrap_comments = true
|
||||
combine_control_expr = false
|
||||
condense_wildcard_suffixes = true
|
||||
format_code_in_doc_comments = true
|
||||
format_macro_matchers = true
|
||||
format_strings = true
|
||||
use_small_heuristics = "Max"
|
0
crates/simplexpr/src/EvalError.rs
Normal file
0
crates/simplexpr/src/EvalError.rs
Normal file
93
crates/simplexpr/src/ast.rs
Normal file
93
crates/simplexpr/src/ast.rs
Normal file
|
@ -0,0 +1,93 @@
|
|||
use crate::dynval::DynVal;
|
||||
use itertools::Itertools;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// stores the left and right end of a span, and a given file identifier.
|
||||
#[derive(Eq, PartialEq, Clone, Copy, Serialize, Deserialize)]
|
||||
pub struct Span(pub usize, pub usize, pub usize);
|
||||
|
||||
impl std::fmt::Display for Span {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}..{}", self.0, self.1)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Span {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}..{}", self.0, self.1)
|
||||
}
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug, strum::EnumString, strum::Display)]
|
||||
pub enum BinOp {
|
||||
#[strum(serialize = "+") ] Plus,
|
||||
#[strum(serialize = "-") ] Minus,
|
||||
#[strum(serialize = "*") ] Times,
|
||||
#[strum(serialize = "/") ] Div,
|
||||
#[strum(serialize = "%") ] Mod,
|
||||
#[strum(serialize = "==")] Equals,
|
||||
#[strum(serialize = "!=")] NotEquals,
|
||||
#[strum(serialize = "&&")] And,
|
||||
#[strum(serialize = "||")] Or,
|
||||
#[strum(serialize = ">") ] GT,
|
||||
#[strum(serialize = "<") ] LT,
|
||||
#[strum(serialize = "?:")] Elvis,
|
||||
#[strum(serialize = "=~")] RegexMatch,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug, strum::EnumString, strum::Display)]
|
||||
pub enum UnaryOp {
|
||||
#[strum(serialize = "!")]
|
||||
Not,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum SimplExpr {
|
||||
Literal(Span, DynVal),
|
||||
VarRef(Span, String),
|
||||
BinOp(Span, Box<SimplExpr>, BinOp, Box<SimplExpr>),
|
||||
UnaryOp(Span, UnaryOp, Box<SimplExpr>),
|
||||
IfElse(Span, Box<SimplExpr>, Box<SimplExpr>, Box<SimplExpr>),
|
||||
JsonAccess(Span, Box<SimplExpr>, Box<SimplExpr>),
|
||||
FunctionCall(Span, String, Vec<SimplExpr>),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for SimplExpr {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
SimplExpr::VarRef(_, x) => write!(f, "{}", x),
|
||||
SimplExpr::Literal(_, x) => write!(f, "\"{}\"", x),
|
||||
SimplExpr::BinOp(_, l, op, r) => write!(f, "({} {} {})", l, op, r),
|
||||
SimplExpr::UnaryOp(_, op, x) => write!(f, "{}{}", op, x),
|
||||
SimplExpr::IfElse(_, a, b, c) => write!(f, "(if {} then {} else {})", a, b, c),
|
||||
SimplExpr::JsonAccess(_, value, index) => write!(f, "{}[{}]", value, index),
|
||||
SimplExpr::FunctionCall(_, function_name, args) => {
|
||||
write!(f, "{}({})", function_name, args.iter().join(", "))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
impl SimplExpr {
|
||||
pub fn literal(span: Span, s: String) -> Self {
|
||||
Self::Literal(span, DynVal(s, Some(span)))
|
||||
}
|
||||
|
||||
pub fn span(&self) -> Span {
|
||||
match self {
|
||||
SimplExpr::Literal(span, _) => *span,
|
||||
SimplExpr::VarRef(span, _) => *span,
|
||||
SimplExpr::BinOp(span, ..) => *span,
|
||||
SimplExpr::UnaryOp(span, ..) => *span,
|
||||
SimplExpr::IfElse(span, ..) => *span,
|
||||
SimplExpr::JsonAccess(span, ..) => *span,
|
||||
SimplExpr::FunctionCall(span, ..) => *span,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for SimplExpr {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self)
|
||||
}
|
||||
}
|
218
crates/simplexpr/src/dynval.rs
Normal file
218
crates/simplexpr/src/dynval.rs
Normal file
|
@ -0,0 +1,218 @@
|
|||
use crate::ast::Span;
|
||||
use itertools::Itertools;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{fmt, iter::FromIterator, str::FromStr};
|
||||
|
||||
pub type Result<T> = std::result::Result<T, ConversionError>;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[error("Failed to turn {value} into a value of type {target_type}")]
|
||||
pub struct ConversionError {
|
||||
pub value: DynVal,
|
||||
pub target_type: &'static str,
|
||||
pub source: Option<Box<dyn std::error::Error>>,
|
||||
}
|
||||
|
||||
impl ConversionError {
|
||||
fn new(value: DynVal, target_type: &'static str, source: impl std::error::Error + 'static) -> Self {
|
||||
ConversionError { value, target_type, source: Some(Box::new(source)) }
|
||||
}
|
||||
|
||||
pub fn span(&self) -> Option<Span> {
|
||||
self.value.1
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Serialize, Default, Eq)]
|
||||
pub struct DynVal(pub String, pub Option<Span>);
|
||||
|
||||
impl From<String> for DynVal {
|
||||
fn from(s: String) -> Self {
|
||||
DynVal(s, None)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for DynVal {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
impl fmt::Debug for DynVal {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "\"{}\"", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Manually implement equality, to allow for values in different formats (i.e. "1" and "1.0") to still be considered as equal.
|
||||
impl std::cmp::PartialEq<Self> for DynVal {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
if let (Ok(a), Ok(b)) = (self.as_f64(), other.as_f64()) {
|
||||
a == b
|
||||
} else {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<DynVal> for DynVal {
|
||||
fn from_iter<T: IntoIterator<Item = DynVal>>(iter: T) -> Self {
|
||||
DynVal(iter.into_iter().join(""), None)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::str::FromStr for DynVal {
|
||||
type Err = ConversionError;
|
||||
|
||||
/// parses the value, trying to turn it into a number and a boolean first,
|
||||
/// before deciding that it is a string.
|
||||
fn from_str(s: &str) -> Result<Self> {
|
||||
Ok(DynVal::from_string(s.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait FromDynVal: Sized {
|
||||
type Err;
|
||||
fn from_dynval(x: &DynVal) -> std::result::Result<Self, Self::Err>;
|
||||
}
|
||||
|
||||
impl<E, T: FromStr<Err = E>> FromDynVal for T {
|
||||
type Err = E;
|
||||
|
||||
fn from_dynval(x: &DynVal) -> std::result::Result<Self, Self::Err> {
|
||||
x.0.parse()
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_dynval_from {
|
||||
($($t:ty),*) => {
|
||||
$(impl From<$t> for DynVal {
|
||||
fn from(x: $t) -> Self { DynVal(x.to_string(), None) }
|
||||
})*
|
||||
};
|
||||
}
|
||||
|
||||
impl_dynval_from!(bool, i32, u32, f32, u8, f64, &str);
|
||||
|
||||
impl From<&serde_json::Value> for DynVal {
|
||||
fn from(v: &serde_json::Value) -> Self {
|
||||
DynVal(
|
||||
v.as_str()
|
||||
.map(|x| x.to_string())
|
||||
.or_else(|| serde_json::to_string(v).ok())
|
||||
.unwrap_or_else(|| "<invalid json value>".to_string()),
|
||||
None,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl DynVal {
|
||||
pub fn at(self, span: Span) -> Self {
|
||||
DynVal(self.0, Some(span))
|
||||
}
|
||||
|
||||
pub fn span(&self) -> Option<Span> {
|
||||
self.1
|
||||
}
|
||||
|
||||
pub fn from_string(s: String) -> Self {
|
||||
DynVal(s, None)
|
||||
}
|
||||
|
||||
pub fn read_as<E, T: FromDynVal<Err = E>>(&self) -> std::result::Result<T, E> {
|
||||
T::from_dynval(self)
|
||||
}
|
||||
|
||||
pub fn into_inner(self) -> String {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// This will never fail
|
||||
pub fn as_string(&self) -> Result<String> {
|
||||
Ok(self.0.to_owned())
|
||||
}
|
||||
|
||||
pub fn as_f64(&self) -> Result<f64> {
|
||||
self.0.parse().map_err(|e| ConversionError::new(self.clone(), "f64", e))
|
||||
}
|
||||
|
||||
pub fn as_i32(&self) -> Result<i32> {
|
||||
self.0.parse().map_err(|e| ConversionError::new(self.clone(), "i32", e))
|
||||
}
|
||||
|
||||
pub fn as_bool(&self) -> Result<bool> {
|
||||
self.0.parse().map_err(|e| ConversionError::new(self.clone(), "bool", e))
|
||||
}
|
||||
|
||||
pub fn as_duration(&self) -> Result<std::time::Duration> {
|
||||
use std::time::Duration;
|
||||
let s = &self.0;
|
||||
if s.ends_with("ms") {
|
||||
Ok(Duration::from_millis(
|
||||
s.trim_end_matches("ms").parse().map_err(|e| ConversionError::new(self.clone(), "integer", e))?,
|
||||
))
|
||||
} else if s.ends_with('s') {
|
||||
Ok(Duration::from_secs(
|
||||
s.trim_end_matches('s').parse().map_err(|e| ConversionError::new(self.clone(), "integer", e))?,
|
||||
))
|
||||
} else if s.ends_with('m') {
|
||||
Ok(Duration::from_secs(
|
||||
s.trim_end_matches('m').parse::<u64>().map_err(|e| ConversionError::new(self.clone(), "integer", e))? * 60,
|
||||
))
|
||||
} else if s.ends_with('h') {
|
||||
Ok(Duration::from_secs(
|
||||
s.trim_end_matches('h').parse::<u64>().map_err(|e| ConversionError::new(self.clone(), "integer", e))? * 60 * 60,
|
||||
))
|
||||
} else {
|
||||
Err(ConversionError { value: self.clone(), target_type: "duration", source: None })
|
||||
}
|
||||
}
|
||||
|
||||
// pub fn as_vec(&self) -> Result<Vec<String>> {
|
||||
// match self.0.strip_prefix('[').and_then(|x| x.strip_suffix(']')) {
|
||||
// Some(content) => {
|
||||
// let mut items: Vec<String> = content.split(',').map(|x: &str| x.to_string()).collect();
|
||||
// let mut removed = 0;
|
||||
// for times_ran in 0..items.len() {
|
||||
//// escapes `,` if there's a `\` before em
|
||||
// if items[times_ran - removed].ends_with('\\') {
|
||||
// items[times_ran - removed].pop();
|
||||
// let it = items.remove((times_ran + 1) - removed);
|
||||
// items[times_ran - removed] += ",";
|
||||
// items[times_ran - removed] += ⁢
|
||||
// removed += 1;
|
||||
//}
|
||||
// Ok(items)
|
||||
//}
|
||||
// None => Err(ConversionError { value: self.clone(), target_type: "vec", source: None }),
|
||||
//}
|
||||
|
||||
pub fn as_json_value(&self) -> Result<serde_json::Value> {
|
||||
serde_json::from_str::<serde_json::Value>(&self.0)
|
||||
.map_err(|e| ConversionError::new(self.clone(), "json-value", Box::new(e)))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
// use super::*;
|
||||
// use pretty_assertions::assert_eq;
|
||||
//#[test]
|
||||
// fn test_parse_vec() {
|
||||
// assert_eq!(vec![""], parse_vec("[]".to_string()).unwrap(), "should be able to parse empty lists");
|
||||
// assert_eq!(vec!["hi"], parse_vec("[hi]".to_string()).unwrap(), "should be able to parse single element list");
|
||||
// assert_eq!(
|
||||
// vec!["hi", "ho", "hu"],
|
||||
// parse_vec("[hi,ho,hu]".to_string()).unwrap(),
|
||||
//"should be able to parse three element list"
|
||||
//);
|
||||
// assert_eq!(vec!["hi,ho"], parse_vec("[hi\\,ho]".to_string()).unwrap(), "should be able to parse list with escaped comma");
|
||||
// assert_eq!(
|
||||
// vec!["hi,ho", "hu"],
|
||||
// parse_vec("[hi\\,ho,hu]".to_string()).unwrap(),
|
||||
//"should be able to parse two element list with escaped comma"
|
||||
//);
|
||||
// assert!(parse_vec("".to_string()).is_err(), "Should fail when parsing empty string");
|
||||
// assert!(parse_vec("[a,b".to_string()).is_err(), "Should fail when parsing unclosed list");
|
||||
// assert!(parse_vec("a]".to_string()).is_err(), "Should fail when parsing unopened list");
|
||||
//}
|
||||
}
|
66
crates/simplexpr/src/error.rs
Normal file
66
crates/simplexpr/src/error.rs
Normal file
|
@ -0,0 +1,66 @@
|
|||
use crate::{
|
||||
ast::Span,
|
||||
dynval,
|
||||
parser::lexer::{self, LexicalError},
|
||||
};
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("Parse error: {source}")]
|
||||
ParseError { file_id: usize, source: lalrpop_util::ParseError<usize, lexer::Token, lexer::LexicalError> },
|
||||
|
||||
#[error("Type error: {0}")]
|
||||
ConversionError(#[from] dynval::ConversionError),
|
||||
|
||||
#[error("{1}")]
|
||||
Spanned(Span, Box<dyn std::error::Error>),
|
||||
|
||||
#[error(transparent)]
|
||||
Eval(#[from] crate::eval::EvalError),
|
||||
|
||||
#[error(transparent)]
|
||||
Other(#[from] Box<dyn std::error::Error>),
|
||||
}
|
||||
|
||||
impl Error {
|
||||
pub fn from_parse_error(file_id: usize, err: lalrpop_util::ParseError<usize, lexer::Token, lexer::LexicalError>) -> Self {
|
||||
Error::ParseError { file_id, source: err }
|
||||
}
|
||||
|
||||
pub fn at(self, span: Span) -> Self {
|
||||
Self::Spanned(span, Box::new(self))
|
||||
}
|
||||
|
||||
pub fn get_span(&self) -> Option<Span> {
|
||||
match self {
|
||||
Self::ParseError { file_id, source } => get_parse_error_span(*file_id, source),
|
||||
Self::Spanned(span, _) => Some(*span),
|
||||
Self::Eval(err) => err.span(),
|
||||
Self::ConversionError(err) => err.span(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_parse_error_span(
|
||||
file_id: usize,
|
||||
err: &lalrpop_util::ParseError<usize, lexer::Token, lexer::LexicalError>,
|
||||
) -> Option<Span> {
|
||||
match err {
|
||||
lalrpop_util::ParseError::InvalidToken { location } => Some(Span(*location, *location, file_id)),
|
||||
lalrpop_util::ParseError::UnrecognizedEOF { location, expected: _ } => Some(Span(*location, *location, file_id)),
|
||||
lalrpop_util::ParseError::UnrecognizedToken { token, expected: _ } => Some(Span(token.0, token.2, file_id)),
|
||||
lalrpop_util::ParseError::ExtraToken { token } => Some(Span(token.0, token.2, file_id)),
|
||||
lalrpop_util::ParseError::User { error: LexicalError(l, r) } => Some(Span(*l, *r, file_id)),
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! spanned {
|
||||
($err:ty, $span:expr, $block:expr) => {{
|
||||
let span = $span;
|
||||
let result: Result<_, $err> = try { $block };
|
||||
result.at(span)
|
||||
}};
|
||||
}
|
223
crates/simplexpr/src/eval.rs
Normal file
223
crates/simplexpr/src/eval.rs
Normal file
|
@ -0,0 +1,223 @@
|
|||
use itertools::Itertools;
|
||||
|
||||
use crate::{
|
||||
ast::{BinOp, SimplExpr, Span, UnaryOp},
|
||||
dynval::{ConversionError, DynVal},
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum EvalError {
|
||||
#[error("Tried to reference variable `{0}`, but we cannot access variables here")]
|
||||
NoVariablesAllowed(String),
|
||||
|
||||
#[error("Invalid regex: {0}")]
|
||||
InvalidRegex(#[from] regex::Error),
|
||||
|
||||
#[error("got unresolved variable `{0}`")]
|
||||
UnresolvedVariable(VarName),
|
||||
|
||||
#[error("Type error: {0}")]
|
||||
ConversionError(#[from] ConversionError),
|
||||
|
||||
#[error("Incorrect number of arguments given to function: {0}")]
|
||||
WrongArgCount(String),
|
||||
|
||||
#[error("Unknown function {0}")]
|
||||
UnknownFunction(String),
|
||||
|
||||
#[error("Unknown variable {0}")]
|
||||
UnknownVariable(String),
|
||||
|
||||
#[error("Unable to index into value {0}")]
|
||||
CannotIndex(String),
|
||||
|
||||
#[error("{1}")]
|
||||
Spanned(Span, Box<EvalError>),
|
||||
}
|
||||
|
||||
impl EvalError {
|
||||
pub fn span(&self) -> Option<Span> {
|
||||
match self {
|
||||
EvalError::Spanned(span, _) => Some(*span),
|
||||
EvalError::ConversionError(err) => err.span(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn at(self, span: Span) -> Self {
|
||||
Self::Spanned(span, Box::new(self))
|
||||
}
|
||||
}
|
||||
|
||||
type VarName = String;
|
||||
|
||||
pub trait FunctionSource {
|
||||
type Err;
|
||||
fn run_fn(&self, name: &str, args: &[DynVal]) -> Result<DynVal, Self::Err>;
|
||||
}
|
||||
|
||||
impl SimplExpr {
|
||||
pub fn map_terminals_into(self, f: impl Fn(Self) -> Self) -> Self {
|
||||
use SimplExpr::*;
|
||||
match self {
|
||||
BinOp(span, box a, op, box b) => BinOp(span, box f(a), op, box f(b)),
|
||||
UnaryOp(span, op, box a) => UnaryOp(span, op, box f(a)),
|
||||
IfElse(span, box a, box b, box c) => IfElse(span, box f(a), box f(b), box f(c)),
|
||||
JsonAccess(span, box a, box b) => JsonAccess(span, box f(a), box f(b)),
|
||||
FunctionCall(span, name, args) => FunctionCall(span, name, args.into_iter().map(f).collect()),
|
||||
other => f(other),
|
||||
}
|
||||
}
|
||||
|
||||
/// resolve variable references in the expression. Fails if a variable cannot be resolved.
|
||||
pub fn resolve_refs(self, variables: &HashMap<VarName, DynVal>) -> Result<Self, EvalError> {
|
||||
use SimplExpr::*;
|
||||
match self {
|
||||
// Literal(x) => Ok(Literal(AttrValue::from_primitive(x.resolve_fully(&variables)?))),
|
||||
Literal(span, x) => Ok(Literal(span, x)),
|
||||
BinOp(span, box a, op, box b) => Ok(BinOp(span, box a.resolve_refs(variables)?, op, box b.resolve_refs(variables)?)),
|
||||
UnaryOp(span, op, box x) => Ok(UnaryOp(span, op, box x.resolve_refs(variables)?)),
|
||||
IfElse(span, box a, box b, box c) => {
|
||||
Ok(IfElse(span, box a.resolve_refs(variables)?, box b.resolve_refs(variables)?, box c.resolve_refs(variables)?))
|
||||
}
|
||||
JsonAccess(span, box a, box b) => {
|
||||
Ok(JsonAccess(span, box a.resolve_refs(variables)?, box b.resolve_refs(variables)?))
|
||||
}
|
||||
FunctionCall(span, function_name, args) => Ok(FunctionCall(
|
||||
span,
|
||||
function_name,
|
||||
args.into_iter().map(|a| a.resolve_refs(variables)).collect::<Result<_, EvalError>>()?,
|
||||
)),
|
||||
VarRef(span, ref name) => match variables.get(name) {
|
||||
Some(value) => Ok(Literal(span, value.clone())),
|
||||
None => Err(EvalError::UnknownVariable(name.to_string()).at(span)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn var_refs(&self) -> Vec<&String> {
|
||||
use SimplExpr::*;
|
||||
match self {
|
||||
Literal(..) => Vec::new(),
|
||||
VarRef(_, name) => vec![name],
|
||||
BinOp(_, box a, _, box b) | JsonAccess(_, box a, box b) => {
|
||||
let mut refs = a.var_refs();
|
||||
refs.append(&mut b.var_refs());
|
||||
refs
|
||||
}
|
||||
UnaryOp(_, _, box x) => x.var_refs(),
|
||||
IfElse(_, box a, box b, box c) => {
|
||||
let mut refs = a.var_refs();
|
||||
refs.append(&mut b.var_refs());
|
||||
refs.append(&mut c.var_refs());
|
||||
refs
|
||||
}
|
||||
FunctionCall(_, _, args) => args.iter().flat_map(|a| a.var_refs()).collect_vec(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eval_no_vars(&self) -> Result<DynVal, EvalError> {
|
||||
match self.eval(&HashMap::new()) {
|
||||
Ok(x) => Ok(x),
|
||||
Err(EvalError::UnknownVariable(name)) => Err(EvalError::NoVariablesAllowed(name)),
|
||||
Err(x) => Err(x),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eval(&self, values: &HashMap<VarName, DynVal>) -> Result<DynVal, EvalError> {
|
||||
let span = self.span();
|
||||
let value = match self {
|
||||
SimplExpr::Literal(_, x) => Ok(x.clone()),
|
||||
SimplExpr::VarRef(span, ref name) => {
|
||||
Ok(values.get(name).cloned().ok_or_else(|| EvalError::UnresolvedVariable(name.to_string()).at(*span))?.at(*span))
|
||||
}
|
||||
SimplExpr::BinOp(_, a, op, b) => {
|
||||
let a = a.eval(values)?;
|
||||
let b = b.eval(values)?;
|
||||
Ok(match op {
|
||||
BinOp::Equals => DynVal::from(a == b),
|
||||
BinOp::NotEquals => DynVal::from(a != b),
|
||||
BinOp::And => DynVal::from(a.as_bool()? && b.as_bool()?),
|
||||
BinOp::Or => DynVal::from(a.as_bool()? || b.as_bool()?),
|
||||
BinOp::Plus => match a.as_f64() {
|
||||
Ok(num) => DynVal::from(num + b.as_f64()?),
|
||||
Err(_) => DynVal::from(format!("{}{}", a.as_string()?, b.as_string()?)),
|
||||
},
|
||||
BinOp::Minus => DynVal::from(a.as_f64()? - b.as_f64()?),
|
||||
BinOp::Times => DynVal::from(a.as_f64()? * b.as_f64()?),
|
||||
BinOp::Div => DynVal::from(a.as_f64()? / b.as_f64()?),
|
||||
BinOp::Mod => DynVal::from(a.as_f64()? % b.as_f64()?),
|
||||
BinOp::GT => DynVal::from(a.as_f64()? > b.as_f64()?),
|
||||
BinOp::LT => DynVal::from(a.as_f64()? < b.as_f64()?),
|
||||
#[allow(clippy::useless_conversion)]
|
||||
BinOp::Elvis => DynVal::from(if a.0.is_empty() { b } else { a }),
|
||||
BinOp::RegexMatch => {
|
||||
let regex = regex::Regex::new(&b.as_string()?)?;
|
||||
DynVal::from(regex.is_match(&a.as_string()?))
|
||||
}
|
||||
})
|
||||
}
|
||||
SimplExpr::UnaryOp(_, op, a) => {
|
||||
let a = a.eval(values)?;
|
||||
Ok(match op {
|
||||
UnaryOp::Not => DynVal::from(!a.as_bool()?),
|
||||
})
|
||||
}
|
||||
SimplExpr::IfElse(_, cond, yes, no) => {
|
||||
if cond.eval(values)?.as_bool()? {
|
||||
yes.eval(values)
|
||||
} else {
|
||||
no.eval(values)
|
||||
}
|
||||
}
|
||||
SimplExpr::JsonAccess(span, val, index) => {
|
||||
let val = val.eval(values)?;
|
||||
let index = index.eval(values)?;
|
||||
match val.as_json_value()? {
|
||||
serde_json::Value::Array(val) => {
|
||||
let index = index.as_i32()?;
|
||||
let indexed_value = val.get(index as usize).unwrap_or(&serde_json::Value::Null);
|
||||
Ok(DynVal::from(indexed_value))
|
||||
}
|
||||
serde_json::Value::Object(val) => {
|
||||
let indexed_value = val
|
||||
.get(&index.as_string()?)
|
||||
.or_else(|| val.get(&index.as_i32().ok()?.to_string()))
|
||||
.unwrap_or(&serde_json::Value::Null);
|
||||
Ok(DynVal::from(indexed_value))
|
||||
}
|
||||
_ => Err(EvalError::CannotIndex(format!("{}", val)).at(*span)),
|
||||
}
|
||||
}
|
||||
SimplExpr::FunctionCall(span, function_name, args) => {
|
||||
let args = args.into_iter().map(|a| a.eval(values)).collect::<Result<_, EvalError>>()?;
|
||||
call_expr_function(&function_name, args).map_err(|e| e.at(*span))
|
||||
}
|
||||
};
|
||||
Ok(value?.at(span))
|
||||
}
|
||||
}
|
||||
|
||||
fn call_expr_function(name: &str, args: Vec<DynVal>) -> Result<DynVal, EvalError> {
|
||||
match name {
|
||||
"round" => match args.as_slice() {
|
||||
[num, digits] => {
|
||||
let num = num.as_f64()?;
|
||||
let digits = digits.as_i32()?;
|
||||
Ok(DynVal::from(format!("{:.1$}", num, digits as usize)))
|
||||
}
|
||||
_ => Err(EvalError::WrongArgCount(name.to_string())),
|
||||
},
|
||||
"replace" => match args.as_slice() {
|
||||
[string, pattern, replacement] => {
|
||||
let string = string.as_string()?;
|
||||
let pattern = regex::Regex::new(&pattern.as_string()?)?;
|
||||
let replacement = replacement.as_string()?;
|
||||
Ok(DynVal::from(pattern.replace_all(&string, replacement.replace("$", "$$").replace("\\", "$")).into_owned()))
|
||||
}
|
||||
_ => Err(EvalError::WrongArgCount(name.to_string())),
|
||||
},
|
||||
_ => Err(EvalError::UnknownFunction(name.to_string())),
|
||||
}
|
||||
}
|
21
crates/simplexpr/src/lib.rs
Normal file
21
crates/simplexpr/src/lib.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
#![feature(box_patterns)]
|
||||
#![feature(box_syntax)]
|
||||
#![feature(try_blocks)]
|
||||
pub mod ast;
|
||||
pub mod dynval;
|
||||
pub mod error;
|
||||
pub mod eval;
|
||||
pub mod parser;
|
||||
|
||||
pub use ast::{SimplExpr, Span};
|
||||
|
||||
use lalrpop_util::lalrpop_mod;
|
||||
|
||||
lalrpop_mod!(
|
||||
#[allow(clippy::all)]
|
||||
pub simplexpr_parser
|
||||
);
|
||||
|
||||
pub fn parse_string(file_id: usize, s: &str) -> Result<SimplExpr, error::Error> {
|
||||
parser::parse_string(file_id, s)
|
||||
}
|
3
crates/simplexpr/src/parser/lalrpop_helpers.rs
Normal file
3
crates/simplexpr/src/parser/lalrpop_helpers.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
pub fn b<T>(x: T) -> Box<T> {
|
||||
Box::new(x)
|
||||
}
|
78
crates/simplexpr/src/parser/lexer.rs
Normal file
78
crates/simplexpr/src/parser/lexer.rs
Normal file
|
@ -0,0 +1,78 @@
|
|||
use logos::Logos;
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[derive(Logos, Debug, PartialEq, Eq, Clone, strum::Display, strum::EnumString)]
|
||||
pub enum Token {
|
||||
#[strum(serialize = "+") ] #[token("+") ] Plus,
|
||||
#[strum(serialize = "-") ] #[token("-") ] Minus,
|
||||
#[strum(serialize = "*") ] #[token("*") ] Times,
|
||||
#[strum(serialize = "/") ] #[token("/") ] Div,
|
||||
#[strum(serialize = "%") ] #[token("%") ] Mod,
|
||||
#[strum(serialize = "==")] #[token("==")] Equals,
|
||||
#[strum(serialize = "!=")] #[token("!=")] NotEquals,
|
||||
#[strum(serialize = "&&")] #[token("&&")] And,
|
||||
#[strum(serialize = "||")] #[token("||")] Or,
|
||||
#[strum(serialize = ">") ] #[token(">") ] GT,
|
||||
#[strum(serialize = "<") ] #[token("<") ] LT,
|
||||
#[strum(serialize = "?:")] #[token("?:")] Elvis,
|
||||
#[strum(serialize = "=~")] #[token("=~")] RegexMatch,
|
||||
|
||||
#[strum(serialize = "!") ] #[token("!") ] Not,
|
||||
|
||||
#[strum(serialize = ",") ] #[token(",") ] Comma,
|
||||
#[strum(serialize = "?") ] #[token("?") ] Question,
|
||||
#[strum(serialize = ":") ] #[token(":") ] Colon,
|
||||
#[strum(serialize = "(") ] #[token("(") ] LPren,
|
||||
#[strum(serialize = ")") ] #[token(")") ] RPren,
|
||||
#[strum(serialize = "[") ] #[token("[") ] LBrack,
|
||||
#[strum(serialize = "]") ] #[token("]") ] RBrack,
|
||||
#[strum(serialize = ".") ] #[token(".") ] Dot,
|
||||
#[strum(serialize = "true") ] #[token("true") ] True,
|
||||
#[strum(serialize = "false")] #[token("false")] False,
|
||||
|
||||
#[regex(r"[a-zA-Z_-]+", |x| x.slice().to_string())]
|
||||
Ident(String),
|
||||
#[regex(r"[+-]?(?:[0-9]+[.])?[0-9]+", |x| x.slice().to_string())]
|
||||
NumLit(String),
|
||||
#[regex(r#""(?:[^"\\]|\\.)*""#, |x| x.slice().to_string())]
|
||||
StrLit(String),
|
||||
|
||||
|
||||
#[error]
|
||||
#[regex(r"[ \t\n\f]+", logos::skip)]
|
||||
Error,
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
||||
pub struct LexicalError(pub usize, pub usize);
|
||||
|
||||
impl std::fmt::Display for LexicalError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Lexical error at {}..{}", self.0, self.1)
|
||||
}
|
||||
}
|
||||
|
||||
pub type SpannedResult<Tok, Loc, Error> = Result<(Loc, Tok, Loc), Error>;
|
||||
|
||||
pub struct Lexer<'input> {
|
||||
lexer: logos::SpannedIter<'input, Token>,
|
||||
}
|
||||
|
||||
impl<'input> Lexer<'input> {
|
||||
pub fn new(text: &'input str) -> Self {
|
||||
Lexer { lexer: logos::Lexer::new(text).spanned() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'input> Iterator for Lexer<'input> {
|
||||
type Item = SpannedResult<Token, usize, LexicalError>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let (token, range) = self.lexer.next()?;
|
||||
if token == Token::Error {
|
||||
Some(Err(LexicalError(range.start, range.end)))
|
||||
} else {
|
||||
Some(Ok((range.start, token, range.end)))
|
||||
}
|
||||
}
|
||||
}
|
47
crates/simplexpr/src/parser/mod.rs
Normal file
47
crates/simplexpr/src/parser/mod.rs
Normal file
|
@ -0,0 +1,47 @@
|
|||
pub mod lalrpop_helpers;
|
||||
pub mod lexer;
|
||||
|
||||
use crate::{
|
||||
ast::SimplExpr,
|
||||
error::{Error, Result},
|
||||
};
|
||||
|
||||
pub fn parse_string(file_id: usize, s: &str) -> Result<SimplExpr> {
|
||||
let lexer = lexer::Lexer::new(s);
|
||||
let parser = crate::simplexpr_parser::ExprParser::new();
|
||||
parser.parse(file_id, lexer).map_err(|e| Error::from_parse_error(file_id, e))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
macro_rules! test_parser {
|
||||
($($text:literal),* $(,)?) => {{
|
||||
let p = crate::simplexpr_parser::ExprParser::new();
|
||||
use crate::parser::lexer::Lexer;
|
||||
::insta::with_settings!({sort_maps => true}, {
|
||||
$(
|
||||
::insta::assert_debug_snapshot!(p.parse(0, Lexer::new($text)));
|
||||
)*
|
||||
});
|
||||
}}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
test_parser!(
|
||||
"1",
|
||||
"2 + 5",
|
||||
"2 * 5 + 1 * 1 + 3",
|
||||
"(1 + 2) * 2",
|
||||
"1 + true ? 2 : 5",
|
||||
"1 + true ? 2 : 5 + 2",
|
||||
"1 + (true ? 2 : 5) + 2",
|
||||
"foo(1, 2)",
|
||||
"! false || ! true",
|
||||
"\"foo\" + 12.4",
|
||||
"hi[\"ho\"]",
|
||||
"foo.bar.baz",
|
||||
"foo.bar[2 + 2] * asdf[foo.bar]",
|
||||
);
|
||||
}
|
||||
}
|
121
crates/simplexpr/src/simplexpr_parser.lalrpop
Normal file
121
crates/simplexpr/src/simplexpr_parser.lalrpop
Normal file
|
@ -0,0 +1,121 @@
|
|||
use crate::ast::{SimplExpr::{self, *}, Span, BinOp::*, UnaryOp::*};
|
||||
use crate::parser::lexer::{Token, LexicalError};
|
||||
use crate::parser::lalrpop_helpers::*;
|
||||
use lalrpop_util::ParseError;
|
||||
|
||||
|
||||
grammar(fid: usize);
|
||||
|
||||
extern {
|
||||
type Location = usize;
|
||||
type Error = LexicalError;
|
||||
|
||||
enum Token {
|
||||
"+" => Token::Plus,
|
||||
"-" => Token::Minus,
|
||||
"*" => Token::Times,
|
||||
"/" => Token::Div,
|
||||
"%" => Token::Mod,
|
||||
"==" => Token::Equals,
|
||||
"!=" => Token::NotEquals,
|
||||
"&&" => Token::And,
|
||||
"||" => Token::Or,
|
||||
">" => Token::GT,
|
||||
"<" => Token::LT,
|
||||
"?:" => Token::Elvis,
|
||||
"=~" => Token::RegexMatch,
|
||||
|
||||
"!" => Token::Not,
|
||||
|
||||
"," => Token::Comma,
|
||||
"?" => Token::Question,
|
||||
":" => Token::Colon,
|
||||
"(" => Token::LPren,
|
||||
")" => Token::RPren,
|
||||
"[" => Token::LBrack,
|
||||
"]" => Token::RBrack,
|
||||
"." => Token::Dot,
|
||||
|
||||
"true" => Token::True,
|
||||
"false" => Token::False,
|
||||
|
||||
"identifier" => Token::Ident(<String>),
|
||||
"number" => Token::NumLit(<String>),
|
||||
"string" => Token::StrLit(<String>),
|
||||
|
||||
"lexer_error" => Token::Error,
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Comma<T>: Vec<T> = {
|
||||
<mut v:(<T> ",")*> <e:T?> => match e {
|
||||
None => v,
|
||||
Some(e) => {
|
||||
v.push(e);
|
||||
v
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub Expr: SimplExpr = {
|
||||
|
||||
#[precedence(level="0")]
|
||||
<l:@L> "lexer_error" <r:@R> =>? {
|
||||
Err(ParseError::User { error: LexicalError(l, r) })
|
||||
},
|
||||
|
||||
<Literal>,
|
||||
<l:@L> <ident:"identifier"> <r:@R> => VarRef(Span(l, r, fid), ident.to_string()),
|
||||
"(" <ExprReset> ")",
|
||||
|
||||
#[precedence(level="1")] #[assoc(side="right")]
|
||||
<l:@L> <ident:"identifier"> "(" <args: Comma<ExprReset>> ")" <r:@R> => FunctionCall(Span(l, r, fid), ident, args),
|
||||
<l:@L> <value:Expr> "[" <index: ExprReset> "]" <r:@R> => JsonAccess(Span(l, r, fid), b(value), b(index)),
|
||||
|
||||
<l:@L> <value:Expr> "." <lit_l:@L> <index:"identifier"> <r:@R> => {
|
||||
JsonAccess(Span(l, r, fid), b(value), b(Literal(Span(lit_l, r, fid), index.into())))
|
||||
},
|
||||
|
||||
#[precedence(level="2")] #[assoc(side="right")]
|
||||
<l:@L> "!" <e:Expr> <r:@R> => UnaryOp(Span(l, r, fid), Not, b(e)),
|
||||
|
||||
#[precedence(level="3")] #[assoc(side="left")]
|
||||
<l:@L> <le:Expr> "*" <re:Expr> <r:@R> => BinOp(Span(l, r, fid), b(le), Times, b(re)),
|
||||
<l:@L> <le:Expr> "/" <re:Expr> <r:@R> => BinOp(Span(l, r, fid), b(le), Div, b(re)),
|
||||
<l:@L> <le:Expr> "%" <re:Expr> <r:@R> => BinOp(Span(l, r, fid), b(le), Mod, b(re)),
|
||||
|
||||
#[precedence(level="4")] #[assoc(side="left")]
|
||||
<l:@L> <le:Expr> "+" <re:Expr> <r:@R> => BinOp(Span(l, r, fid), b(le), Plus, b(re)),
|
||||
<l:@L> <le:Expr> "-" <re:Expr> <r:@R> => BinOp(Span(l, r, fid), b(le), Minus, b(re)),
|
||||
|
||||
#[precedence(level="5")] #[assoc(side="left")]
|
||||
<l:@L> <le:Expr> "==" <re:Expr> <r:@R> => BinOp(Span(l, r, fid), b(le), Equals, b(re)),
|
||||
<l:@L> <le:Expr> "!=" <re:Expr> <r:@R> => BinOp(Span(l, r, fid), b(le), NotEquals, b(re)),
|
||||
<l:@L> <le:Expr> "<" <re:Expr> <r:@R> => BinOp(Span(l, r, fid), b(le), GT, b(re)),
|
||||
<l:@L> <le:Expr> ">" <re:Expr> <r:@R> => BinOp(Span(l, r, fid), b(le), LT, b(re)),
|
||||
<l:@L> <le:Expr> "=~" <re:Expr> <r:@R> => BinOp(Span(l, r, fid), b(le), RegexMatch, b(re)),
|
||||
|
||||
#[precedence(level="6")] #[assoc(side="left")]
|
||||
<l:@L> <le:Expr> "&&" <re:Expr> <r:@R> => BinOp(Span(l, r, fid), b(le), And, b(re)),
|
||||
<l:@L> <le:Expr> "||" <re:Expr> <r:@R> => BinOp(Span(l, r, fid), b(le), Or, b(re)),
|
||||
<l:@L> <le:Expr> "?:" <re:Expr> <r:@R> => BinOp(Span(l, r, fid), b(le), Elvis, b(re)),
|
||||
|
||||
#[precedence(level="7")] #[assoc(side="right")]
|
||||
<l:@L> <cond:Expr> "?" <then:ExprReset> ":" <els:Expr> <r:@R> => {
|
||||
IfElse(Span(l, r, fid), b(cond), b(then), b(els))
|
||||
},
|
||||
};
|
||||
|
||||
ExprReset = <Expr>;
|
||||
|
||||
Literal: SimplExpr = {
|
||||
<l:@L> <x:StrLit> <r:@R> => SimplExpr::literal(Span(l, r, fid), x),
|
||||
<l:@L> <x:"number"> <r:@R> => SimplExpr::literal(Span(l, r, fid), x),
|
||||
<l:@L> "true" <r:@R> => SimplExpr::literal(Span(l, r, fid), "true".into()),
|
||||
<l:@L> "false" <r:@R> => SimplExpr::literal(Span(l, r, fid), "false".into()),
|
||||
}
|
||||
|
||||
StrLit: String = {
|
||||
<x:"string"> => x[1..x.len() - 1].to_owned(),
|
||||
};
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
source: src/lib.rs
|
||||
expression: "p.parse(Lexer::new(\"\\\"foo\\\" + 12.4\"))"
|
||||
|
||||
---
|
||||
Ok(
|
||||
("foo" + "12.4"),
|
||||
)
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
source: src/lib.rs
|
||||
expression: "p.parse(Lexer::new(\"hi[\\\"ho\\\"]\"))"
|
||||
|
||||
---
|
||||
Ok(
|
||||
hi["ho"],
|
||||
)
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
source: src/lib.rs
|
||||
expression: "p.parse(Lexer::new(\"foo.bar.baz\"))"
|
||||
|
||||
---
|
||||
Ok(
|
||||
foo["bar"]["baz"],
|
||||
)
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
source: src/lib.rs
|
||||
expression: "p.parse(Lexer::new(\"foo.bar[2 + 2] * asdf[foo.bar]\"))"
|
||||
|
||||
---
|
||||
Ok(
|
||||
(foo["bar"][("2" + "2")] * asdf[foo["bar"]]),
|
||||
)
|
|
@ -0,0 +1,30 @@
|
|||
---
|
||||
source: src/lib.rs
|
||||
expression: "p.parse(Lexer::new(\"1 + (true ? 2 : 5) + 2\"))"
|
||||
|
||||
---
|
||||
Ok(
|
||||
BinOp(
|
||||
BinOp(
|
||||
Literal(
|
||||
"1",
|
||||
),
|
||||
Plus,
|
||||
IfElse(
|
||||
Literal(
|
||||
"true",
|
||||
),
|
||||
Literal(
|
||||
"2",
|
||||
),
|
||||
Literal(
|
||||
"5",
|
||||
),
|
||||
),
|
||||
),
|
||||
Plus,
|
||||
Literal(
|
||||
"2",
|
||||
),
|
||||
),
|
||||
)
|
|
@ -0,0 +1,13 @@
|
|||
---
|
||||
source: src/lib.rs
|
||||
expression: "Lexer::new(\"foo(1, 2)\").filter_map(|x|\n x.ok()).map(|(_, x, _)|\n match x {\n Token::Ident(x) |\n Token::NumLit(x) |\n Token::StrLit(x) =>\n format!(\"{}\", x),\n x =>\n format!(\"{}\", x),\n }).collect::<Vec<_>>()"
|
||||
|
||||
---
|
||||
[
|
||||
"foo",
|
||||
"LPren",
|
||||
"1",
|
||||
"Comma",
|
||||
"2",
|
||||
"RPren",
|
||||
]
|
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
source: src/lib.rs
|
||||
expression: "p.parse(Lexer::new(\"foo(1, 2)\"))"
|
||||
|
||||
---
|
||||
Ok(
|
||||
FunctionCall(
|
||||
"foo",
|
||||
[
|
||||
Literal(
|
||||
"1",
|
||||
),
|
||||
Literal(
|
||||
"2",
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
source: src/lib.rs
|
||||
expression: "Lexer::new(\"! false || ! true\").filter_map(|x|\n x.ok()).map(|(_, x, _)|\n match x {\n Token::Ident(x)\n |\n Token::NumLit(x)\n |\n Token::StrLit(x)\n =>\n format!(\"{}\",\n x),\n x =>\n format!(\"{}\",\n x),\n }).collect::<Vec<_>>()"
|
||||
|
||||
---
|
||||
[
|
||||
"!",
|
||||
"False",
|
||||
"||",
|
||||
"!",
|
||||
"True",
|
||||
]
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
source: src/lib.rs
|
||||
expression: "p.parse(Lexer::new(\"! false || ! true\"))"
|
||||
|
||||
---
|
||||
Ok(
|
||||
BinOp(
|
||||
UnaryOp(
|
||||
Not,
|
||||
Literal(
|
||||
"false",
|
||||
),
|
||||
),
|
||||
Or,
|
||||
UnaryOp(
|
||||
Not,
|
||||
Literal(
|
||||
"true",
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
source: src/lib.rs
|
||||
expression: "Lexer::new(\"\\\"foo\\\" + 12.4\").filter_map(|x|\n x.ok()).map(|(_, x, _)|\n match x {\n Token::Ident(x)\n |\n Token::NumLit(x)\n |\n Token::StrLit(x)\n =>\n format!(\"{}\",\n x),\n x =>\n format!(\"{}\",\n x),\n }).collect::<Vec<_>>()"
|
||||
|
||||
---
|
||||
[
|
||||
"\"foo\"",
|
||||
"+",
|
||||
"12.4",
|
||||
]
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
source: src/lib.rs
|
||||
expression: "p.parse(Lexer::new(\"2 + 5\"))"
|
||||
|
||||
---
|
||||
Ok(
|
||||
("2" + "5"),
|
||||
)
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
source: src/lib.rs
|
||||
expression: "p.parse(Lexer::new(\"\\\"foo\\\" + 12.4\"))"
|
||||
|
||||
---
|
||||
Ok(
|
||||
BinOp(
|
||||
Literal(
|
||||
"foo",
|
||||
),
|
||||
Plus,
|
||||
Literal(
|
||||
"12.4",
|
||||
),
|
||||
),
|
||||
)
|
|
@ -0,0 +1,11 @@
|
|||
---
|
||||
source: src/lib.rs
|
||||
expression: "Lexer::new(\"hi[\\\"ho\\\"]\").filter_map(|x|\n x.ok()).map(|(_, x, _)|\n match x {\n Token::Ident(x) |\n Token::NumLit(x) |\n Token::StrLit(x)\n =>\n format!(\"{}\", x),\n x =>\n format!(\"{}\", x),\n }).collect::<Vec<_>>()"
|
||||
|
||||
---
|
||||
[
|
||||
"hi",
|
||||
"LBrack",
|
||||
"\"ho\"",
|
||||
"RBrack",
|
||||
]
|
|
@ -0,0 +1,15 @@
|
|||
---
|
||||
source: src/lib.rs
|
||||
expression: "p.parse(Lexer::new(\"hi[\\\"ho\\\"]\"))"
|
||||
|
||||
---
|
||||
Ok(
|
||||
JsonAccess(
|
||||
VarRef(
|
||||
"hi",
|
||||
),
|
||||
Literal(
|
||||
"ho",
|
||||
),
|
||||
),
|
||||
)
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
source: src/lib.rs
|
||||
expression: "Lexer::new(\"foo.bar.baz\").filter_map(|x|\n x.ok()).map(|(_, x, _)|\n match x {\n Token::Ident(x) |\n Token::NumLit(x)\n |\n Token::StrLit(x)\n =>\n format!(\"{}\", x),\n x =>\n format!(\"{}\", x),\n }).collect::<Vec<_>>()"
|
||||
|
||||
---
|
||||
[
|
||||
"foo",
|
||||
"Dot",
|
||||
"bar",
|
||||
"Dot",
|
||||
"baz",
|
||||
]
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
source: src/lib.rs
|
||||
expression: "p.parse(Lexer::new(\"foo.bar.baz\"))"
|
||||
|
||||
---
|
||||
Ok(
|
||||
JsonAccess(
|
||||
JsonAccess(
|
||||
VarRef(
|
||||
"foo",
|
||||
),
|
||||
Literal(
|
||||
"bar",
|
||||
),
|
||||
),
|
||||
Literal(
|
||||
"baz",
|
||||
),
|
||||
),
|
||||
)
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
source: src/lib.rs
|
||||
expression: "Lexer::new(\"foo.bar[2 + 2] * asdf[foo.bar]\").filter_map(|x|\n x.ok()).map(|(_,\n x,\n _)|\n match x\n {\n Token::Ident(x)\n |\n Token::NumLit(x)\n |\n Token::StrLit(x)\n =>\n format!(\"{}\",\n x),\n x\n =>\n format!(\"{}\",\n x),\n }).collect::<Vec<_>>()"
|
||||
|
||||
---
|
||||
[
|
||||
"foo",
|
||||
"Dot",
|
||||
"bar",
|
||||
"LBrack",
|
||||
"2",
|
||||
"+",
|
||||
"2",
|
||||
"RBrack",
|
||||
"*",
|
||||
"asdf",
|
||||
"LBrack",
|
||||
"foo",
|
||||
"Dot",
|
||||
"bar",
|
||||
"RBrack",
|
||||
]
|
|
@ -0,0 +1,42 @@
|
|||
---
|
||||
source: src/lib.rs
|
||||
expression: "p.parse(Lexer::new(\"foo.bar[2 + 2] * asdf[foo.bar]\"))"
|
||||
|
||||
---
|
||||
Ok(
|
||||
BinOp(
|
||||
JsonAccess(
|
||||
JsonAccess(
|
||||
VarRef(
|
||||
"foo",
|
||||
),
|
||||
Literal(
|
||||
"bar",
|
||||
),
|
||||
),
|
||||
BinOp(
|
||||
Literal(
|
||||
"2",
|
||||
),
|
||||
Plus,
|
||||
Literal(
|
||||
"2",
|
||||
),
|
||||
),
|
||||
),
|
||||
Times,
|
||||
JsonAccess(
|
||||
VarRef(
|
||||
"asdf",
|
||||
),
|
||||
JsonAccess(
|
||||
VarRef(
|
||||
"foo",
|
||||
),
|
||||
Literal(
|
||||
"bar",
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
source: src/lib.rs
|
||||
expression: "p.parse(Lexer::new(\"2 * 5 + 1 * 1 + 3\"))"
|
||||
|
||||
---
|
||||
Ok(
|
||||
((("2" * "5") + ("1" * "1")) + "3"),
|
||||
)
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
source: src/lib.rs
|
||||
expression: "p.parse(Lexer::new(\"(1 + 2) * 2\"))"
|
||||
|
||||
---
|
||||
Ok(
|
||||
(("1" + "2") * "2"),
|
||||
)
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
source: src/lib.rs
|
||||
expression: "p.parse(Lexer::new(\"1 + true ? 2 : 5\"))"
|
||||
|
||||
---
|
||||
Ok(
|
||||
(if ("1" + "true") then "2" else "5"),
|
||||
)
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
source: src/lib.rs
|
||||
expression: "p.parse(Lexer::new(\"1 + true ? 2 : 5 + 2\"))"
|
||||
|
||||
---
|
||||
Ok(
|
||||
(if ("1" + "true") then "2" else ("5" + "2")),
|
||||
)
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
source: src/lib.rs
|
||||
expression: "p.parse(Lexer::new(\"1 + (true ? 2 : 5) + 2\"))"
|
||||
|
||||
---
|
||||
Ok(
|
||||
(("1" + (if "true" then "2" else "5")) + "2"),
|
||||
)
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
source: src/lib.rs
|
||||
expression: "p.parse(Lexer::new(\"foo(1, 2)\"))"
|
||||
|
||||
---
|
||||
Ok(
|
||||
foo("1", "2"),
|
||||
)
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
source: src/lib.rs
|
||||
expression: "p.parse(Lexer::new(\"! false || ! true\"))"
|
||||
|
||||
---
|
||||
Ok(
|
||||
(!"false" || !"true"),
|
||||
)
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
source: src/lib.rs
|
||||
expression: "p.parse(Lexer::new(\"1\"))"
|
||||
|
||||
---
|
||||
Ok(
|
||||
"1",
|
||||
)
|
Loading…
Add table
Reference in a new issue