From 9a8bbf4114acf5039739d97e7c1b518e647cdb54 Mon Sep 17 00:00:00 2001
From: ElKowar <5300871+elkowar@users.noreply.github.com>
Date: Tue, 2 Mar 2021 10:27:52 +0100
Subject: [PATCH] Expression language (#124)
* Add AST
* add make-shift testing parser, and make stuff ocmpile
* add proper expression parser
* make string format use '
* Add empty doc page for expressions
* add tests
* Clean up file structure and add unary operators
* Write documentation
---
Cargo.lock | 309 ++++++++++++++---------
Cargo.toml | 3 +-
docs/content/main/expression_language.md | 33 +++
src/config/eww_config.rs | 1 +
src/eww_state.rs | 2 +-
src/main.rs | 3 +
src/value/{ => attr_value}/attr_value.rs | 63 +++--
src/value/attr_value/attr_value_expr.rs | 179 +++++++++++++
src/value/attr_value/mod.rs | 5 +
src/value/attr_value/parser.rs | 209 +++++++++++++++
src/value/mod.rs | 2 +-
src/value/primitive.rs | 19 +-
src/widgets/widget_node.rs | 1 -
13 files changed, 678 insertions(+), 151 deletions(-)
create mode 100644 docs/content/main/expression_language.md
rename src/value/{ => attr_value}/attr_value.rs (71%)
create mode 100644 src/value/attr_value/attr_value_expr.rs
create mode 100644 src/value/attr_value/mod.rs
create mode 100644 src/value/attr_value/parser.rs
diff --git a/Cargo.lock b/Cargo.lock
index 7dde666..76b6f6a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -26,9 +26,15 @@ dependencies = [
[[package]]
name = "anyhow"
-version = "1.0.37"
+version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ee67c11feeac938fae061b232e38e0b6d94f97a9df10e6271319325ac4c56a86"
+checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1"
+
+[[package]]
+name = "arrayvec"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
[[package]]
name = "async-stream"
@@ -47,8 +53,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3548b8efc9f8e8a5a0a2808c5bd8451a9031b9e5b879a79590304ae928b0a70"
dependencies = [
"proc-macro2",
- "quote 1.0.8",
- "syn 1.0.58",
+ "quote 1.0.9",
+ "syn 1.0.60",
]
[[package]]
@@ -117,16 +123,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
-name = "byteorder"
-version = "1.3.4"
+name = "bitvec"
+version = "0.19.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
+checksum = "a7ba35e9565969edb811639dbebfe34edc0368e472c5018474c8eb2543397f81"
+dependencies = [
+ "funty",
+ "radium",
+ "tap",
+ "wyz",
+]
+
+[[package]]
+name = "byteorder"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b"
[[package]]
name = "bytes"
-version = "1.0.0"
+version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ad1f8e949d755f9d79112b5bb46938e0ef9d3804a0b16dfab13aafcaa5f0fa72"
+checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040"
[[package]]
name = "cairo-rs"
@@ -195,12 +213,12 @@ checksum = "b9e769b5c8c8283982a987c6e948e540254f1058d5a74b8794914d4ef5fc2a24"
[[package]]
name = "ctor"
-version = "0.1.17"
+version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "373c88d9506e2e9230f6107701b7d8425f4cb3f6df108ec3042a26e936666da5"
+checksum = "e8f45d9ad417bcef4817d614a501ab55cdd96a6fdb24f49aab89a54acfd66b19"
dependencies = [
- "quote 1.0.8",
- "syn 1.0.58",
+ "quote 1.0.9",
+ "syn 1.0.60",
]
[[package]]
@@ -220,8 +238,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41cb0e6161ad61ed084a36ba71fbba9e3ac5aee3606fb607fe08da6acbcf3d8c"
dependencies = [
"proc-macro2",
- "quote 1.0.8",
- "syn 1.0.58",
+ "quote 1.0.9",
+ "syn 1.0.60",
]
[[package]]
@@ -282,6 +300,7 @@ dependencies = [
"log",
"maplit",
"nix 0.19.1",
+ "nom",
"num",
"pretty_assertions",
"pretty_env_logger",
@@ -306,15 +325,21 @@ checksum = "0c635fdc695a9cbf89115695b04c27c864fa1bf5a94a253798a7bd0752fad5e5"
dependencies = [
"proc-macro-error",
"proc-macro2",
- "quote 1.0.8",
- "syn 1.0.58",
+ "quote 1.0.9",
+ "syn 1.0.60",
]
[[package]]
-name = "futures"
-version = "0.3.8"
+name = "funty"
+version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b3b0c040a1fe6529d30b3c5944b280c7f0dcb2930d2c3062bca967b602583d0"
+checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7"
+
+[[package]]
+name = "futures"
+version = "0.3.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da9052a1a50244d8d5aa9bf55cbc2fb6f357c86cc52e46c62ed390a7180cf150"
dependencies = [
"futures-channel",
"futures-core",
@@ -327,9 +352,9 @@ dependencies = [
[[package]]
name = "futures-channel"
-version = "0.3.8"
+version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4b7109687aa4e177ef6fe84553af6280ef2778bdb7783ba44c9dc3399110fe64"
+checksum = "f2d31b7ec7efab6eefc7c57233bb10b847986139d88cc2f5a02a1ae6871a1846"
dependencies = [
"futures-core",
"futures-sink",
@@ -337,15 +362,15 @@ dependencies = [
[[package]]
name = "futures-core"
-version = "0.3.8"
+version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "847ce131b72ffb13b6109a221da9ad97a64cbe48feb1028356b836b47b8f1748"
+checksum = "79e5145dde8da7d1b3892dad07a9c98fc04bc39892b1ecc9692cf53e2b780a65"
[[package]]
name = "futures-executor"
-version = "0.3.8"
+version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4caa2b2b68b880003057c1dd49f1ed937e38f22fcf6c212188a121f08cf40a65"
+checksum = "e9e59fdc009a4b3096bf94f740a0f2424c082521f20a9b08c5c07c48d90fd9b9"
dependencies = [
"futures-core",
"futures-task",
@@ -354,42 +379,42 @@ dependencies = [
[[package]]
name = "futures-io"
-version = "0.3.8"
+version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "611834ce18aaa1bd13c4b374f5d653e1027cf99b6b502584ff8c9a64413b30bb"
+checksum = "28be053525281ad8259d47e4de5de657b25e7bac113458555bb4b70bc6870500"
[[package]]
name = "futures-macro"
-version = "0.3.8"
+version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77408a692f1f97bcc61dc001d752e00643408fbc922e4d634c655df50d595556"
+checksum = "c287d25add322d9f9abdcdc5927ca398917996600182178774032e9f8258fedd"
dependencies = [
"proc-macro-hack",
"proc-macro2",
- "quote 1.0.8",
- "syn 1.0.58",
+ "quote 1.0.9",
+ "syn 1.0.60",
]
[[package]]
name = "futures-sink"
-version = "0.3.8"
+version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f878195a49cee50e006b02b93cf7e0a95a38ac7b776b4c4d9cc1207cd20fcb3d"
+checksum = "caf5c69029bda2e743fddd0582d1083951d65cc9539aebf8812f36c3491342d6"
[[package]]
name = "futures-task"
-version = "0.3.8"
+version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c554eb5bf48b2426c4771ab68c6b14468b6e76cc90996f528c3338d761a4d0d"
+checksum = "13de07eb8ea81ae445aca7b69f5f7bf15d7bf4912d8ca37d6645c77ae8a58d86"
dependencies = [
"once_cell",
]
[[package]]
name = "futures-util"
-version = "0.3.8"
+version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d304cff4a7b99cfb7986f7d43fbe93d175e72e704a8860787cc95e9ffd85cbd2"
+checksum = "632a8cd0f2a4b3fdea1657f08bde063848c3bd00f9bbf6e256b8be78802e624b"
dependencies = [
"futures-channel",
"futures-core",
@@ -398,7 +423,7 @@ dependencies = [
"futures-sink",
"futures-task",
"memchr",
- "pin-project",
+ "pin-project-lite",
"pin-utils",
"proc-macro-hack",
"proc-macro-nested",
@@ -595,8 +620,8 @@ dependencies = [
"proc-macro-crate",
"proc-macro-error",
"proc-macro2",
- "quote 1.0.8",
- "syn 1.0.58",
+ "quote 1.0.9",
+ "syn 1.0.60",
]
[[package]]
@@ -713,9 +738,9 @@ dependencies = [
[[package]]
name = "hermit-abi"
-version = "0.1.17"
+version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8"
+checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
dependencies = [
"libc",
]
@@ -754,9 +779,9 @@ dependencies = [
[[package]]
name = "inotify-sys"
-version = "0.1.4"
+version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4563555856585ab3180a5bf0b2f9f8d301a728462afffc8195b3f5394229c55"
+checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb"
dependencies = [
"libc",
]
@@ -804,10 +829,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
-name = "libc"
-version = "0.2.81"
+name = "lexical-core"
+version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb"
+checksum = "21f866863575d0e1d654fbeeabdc927292fdf862873dc3c96c6f753357e13374"
+dependencies = [
+ "arrayvec",
+ "bitflags",
+ "cfg-if 1.0.0",
+ "ryu",
+ "static_assertions",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.86"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7282d924be3275cec7f6756ff4121987bc6481325397dde6ba3e7802b1a8b1c"
[[package]]
name = "lock_api"
@@ -820,11 +858,11 @@ dependencies = [
[[package]]
name = "log"
-version = "0.4.11"
+version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
+checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
- "cfg-if 0.1.10",
+ "cfg-if 1.0.0",
]
[[package]]
@@ -886,6 +924,19 @@ dependencies = [
"libc",
]
+[[package]]
+name = "nom"
+version = "6.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7413f999671bd4745a7b624bd370a569fb6bc574b23c83a3c5ed2e453f3d5e2"
+dependencies = [
+ "bitvec",
+ "funty",
+ "lexical-core",
+ "memchr",
+ "version_check",
+]
+
[[package]]
name = "ntapi"
version = "0.3.6"
@@ -1036,9 +1087,9 @@ dependencies = [
[[package]]
name = "parking_lot_core"
-version = "0.8.2"
+version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9ccb628cad4f84851442432c60ad8e1f607e29752d0bf072cbd0baf28aa34272"
+checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018"
dependencies = [
"cfg-if 1.0.0",
"instant",
@@ -1088,8 +1139,8 @@ dependencies = [
"phf_shared",
"proc-macro-hack",
"proc-macro2",
- "quote 1.0.8",
- "syn 1.0.58",
+ "quote 1.0.9",
+ "syn 1.0.60",
]
[[package]]
@@ -1101,31 +1152,11 @@ dependencies = [
"siphasher",
]
-[[package]]
-name = "pin-project"
-version = "1.0.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a83804639aad6ba65345661744708855f9fbcb71176ea8d28d05aeb11d975e7"
-dependencies = [
- "pin-project-internal",
-]
-
-[[package]]
-name = "pin-project-internal"
-version = "1.0.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b7bcc46b8f73443d15bc1c5fecbb315718491fa9187fa483f0e359323cde8b3a"
-dependencies = [
- "proc-macro2",
- "quote 1.0.8",
- "syn 1.0.58",
-]
-
[[package]]
name = "pin-project-lite"
-version = "0.2.1"
+version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e36743d754ccdf9954c2e352ce2d4b106e024c814f6499c2dadff80da9a442d8"
+checksum = "439697af366c49a6d0a010c56a0d97685bc140ce0d377b13a2ea2aa42d64a827"
[[package]]
name = "pin-utils"
@@ -1184,8 +1215,8 @@ checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
- "quote 1.0.8",
- "syn 1.0.58",
+ "quote 1.0.9",
+ "syn 1.0.60",
"version_check",
]
@@ -1196,7 +1227,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
- "quote 1.0.8",
+ "quote 1.0.9",
"version_check",
]
@@ -1208,9 +1239,9 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
[[package]]
name = "proc-macro-nested"
-version = "0.1.6"
+version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a"
+checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086"
[[package]]
name = "proc-macro2"
@@ -1235,13 +1266,19 @@ checksum = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
[[package]]
name = "quote"
-version = "1.0.8"
+version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df"
+checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
dependencies = [
"proc-macro2",
]
+[[package]]
+name = "radium"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8"
+
[[package]]
name = "rand"
version = "0.7.3"
@@ -1295,15 +1332,18 @@ dependencies = [
[[package]]
name = "redox_syscall"
-version = "0.1.57"
+version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
+checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9"
+dependencies = [
+ "bitflags",
+]
[[package]]
name = "regex"
-version = "1.4.2"
+version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c"
+checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a"
dependencies = [
"aho-corasick",
"memchr",
@@ -1313,9 +1353,9 @@ dependencies = [
[[package]]
name = "regex-syntax"
-version = "0.6.21"
+version = "0.6.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189"
+checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581"
[[package]]
name = "roxmltree"
@@ -1326,6 +1366,12 @@ dependencies = [
"xmlparser",
]
+[[package]]
+name = "ryu"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
+
[[package]]
name = "scopeguard"
version = "1.1.0"
@@ -1334,22 +1380,22 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "serde"
-version = "1.0.118"
+version = "1.0.123"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06c64263859d87aa2eb554587e2d23183398d617427327cf2b3d0ed8c69e4800"
+checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.118"
+version = "1.0.123"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c84d3526699cd55261af4b941e4e725444df67aa4f9e6a3564f18030d12672df"
+checksum = "9391c295d64fc0abb2c556bad848f33cb8296276b1ad2677d1ae1ace4f258f31"
dependencies = [
"proc-macro2",
- "quote 1.0.8",
- "syn 1.0.58",
+ "quote 1.0.9",
+ "syn 1.0.60",
]
[[package]]
@@ -1385,9 +1431,9 @@ checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
[[package]]
name = "smallvec"
-version = "1.6.0"
+version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a55ca5f3b68e41c979bf8c46a6f1da892ca4db8f94023ce0bd32407573b1ac0"
+checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
[[package]]
name = "smart-default"
@@ -1396,8 +1442,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "133659a15339456eeeb07572eb02a91c91e9815e9cbc89566944d2c8d3efdbf6"
dependencies = [
"proc-macro2",
- "quote 1.0.8",
- "syn 1.0.58",
+ "quote 1.0.9",
+ "syn 1.0.60",
]
[[package]]
@@ -1411,6 +1457,12 @@ dependencies = [
"winapi",
]
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
[[package]]
name = "strsim"
version = "0.8.0"
@@ -1437,8 +1489,8 @@ dependencies = [
"heck",
"proc-macro-error",
"proc-macro2",
- "quote 1.0.8",
- "syn 1.0.58",
+ "quote 1.0.9",
+ "syn 1.0.60",
]
[[package]]
@@ -1455,8 +1507,8 @@ checksum = "87c85aa3f8ea653bfd3ddf25f7ee357ee4d204731f6aa9ad04002306f6e2774c"
dependencies = [
"heck",
"proc-macro2",
- "quote 1.0.8",
- "syn 1.0.58",
+ "quote 1.0.9",
+ "syn 1.0.60",
]
[[package]]
@@ -1472,12 +1524,12 @@ dependencies = [
[[package]]
name = "syn"
-version = "1.0.58"
+version = "1.0.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cc60a3d73ea6594cd712d830cc1f0390fd71542d8c8cd24e70cc54cdfd5e05d5"
+checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081"
dependencies = [
"proc-macro2",
- "quote 1.0.8",
+ "quote 1.0.9",
"unicode-xid 0.2.1",
]
@@ -1505,6 +1557,12 @@ dependencies = [
"version-compare",
]
+[[package]]
+name = "tap"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
+
[[package]]
name = "termcolor"
version = "1.1.2"
@@ -1539,24 +1597,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9be73a2caec27583d0046ef3796c3794f868a5bc813db689eed00c7631275cd1"
dependencies = [
"proc-macro2",
- "quote 1.0.8",
- "syn 1.0.58",
+ "quote 1.0.9",
+ "syn 1.0.60",
]
[[package]]
name = "thread_local"
-version = "1.0.1"
+version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
+checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd"
dependencies = [
- "lazy_static",
+ "once_cell",
]
[[package]]
name = "tokio"
-version = "1.0.1"
+version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d258221f566b6c803c7b4714abadc080172b272090cdc5e244a6d4dd13c3a6bd"
+checksum = "e8190d04c665ea9e6b6a0dc45523ade572c088d2e6566244c1122671dbf4ae3a"
dependencies = [
"autocfg",
"bytes",
@@ -1574,20 +1632,20 @@ dependencies = [
[[package]]
name = "tokio-macros"
-version = "1.0.0"
+version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "42517d2975ca3114b22a16192634e8241dc5cc1f130be194645970cc1c371494"
+checksum = "caf7b11a536f46a809a8a9f0bb4237020f70ecbf115b842360afb127ea2fda57"
dependencies = [
"proc-macro2",
- "quote 1.0.8",
- "syn 1.0.58",
+ "quote 1.0.9",
+ "syn 1.0.60",
]
[[package]]
name = "tokio-stream"
-version = "0.1.1"
+version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e4cdeb73537e63f98adcd73138af75e3f368ccaecffaa29d7eb61b9f5a440457"
+checksum = "1981ad97df782ab506a1f43bf82c967326960d278acf3bf8279809648c3ff3ea"
dependencies = [
"futures-core",
"pin-project-lite",
@@ -1596,9 +1654,9 @@ dependencies = [
[[package]]
name = "tokio-util"
-version = "0.6.0"
+version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "36135b7e7da911f5f8b9331209f7fab4cc13498f3fff52f72a710c78187e3148"
+checksum = "ebb7cb2f00c5ae8df755b252306272cd1790d39728363936e01827e11f0b017b"
dependencies = [
"bytes",
"futures-core",
@@ -1606,7 +1664,6 @@ dependencies = [
"log",
"pin-project-lite",
"tokio",
- "tokio-stream",
]
[[package]]
@@ -1712,6 +1769,12 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+[[package]]
+name = "wyz"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214"
+
[[package]]
name = "x11"
version = "2.18.2"
diff --git a/Cargo.toml b/Cargo.toml
index dc8241d..0df621b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -55,8 +55,9 @@ futures-core = "0.3"
futures-util = "0.3"
tokio-util = "0.6"
-dyn-clone = "1.0"
+nom = "6.1"
+dyn-clone = "1.0"
[target.'cfg(target_os="linux")'.dependencies]
inotify = "0.9"
diff --git a/docs/content/main/expression_language.md b/docs/content/main/expression_language.md
new file mode 100644
index 0000000..9984d0e
--- /dev/null
+++ b/docs/content/main/expression_language.md
@@ -0,0 +1,33 @@
++++
+title = "Eww expressions"
+slug = "Embedded eww expression language"
+weight = 6
++++
+
+# The embedded Eww expression-language
+
+Within variable references, you can make use of a small, built-in expression language.
+This can be used whereever you can use variable-references (`{{varname}}`).
+
+## Example
+
+```xml
+
+
+Some math: {{12 + 2 * 10}}
+```
+
+## Syntax
+
+The expression language supports:
+- simple mathematical operations (`+`, `-`, `*`, `/`, `%`)
+- comparisons (`==`, `!=`, `>`, `<`)
+- boolean operations (`||`, `&&`, `!`)
+- conditionals (`if condition then 'value' else 'other value'`)
+- numbers, strings, booleans and variable references (`12`, `'hi'`, `true`, `some_variable`)
+ - strings can contain other expressions again: `'foo {{some_variable}} bar'`
+
diff --git a/src/config/eww_config.rs b/src/config/eww_config.rs
index 87df525..100ab99 100644
--- a/src/config/eww_config.rs
+++ b/src/config/eww_config.rs
@@ -121,6 +121,7 @@ impl RawEwwConfig {
pub fn read_from_file>(path: P) -> Result {
let result: Result<_> = try {
let content = util::replace_env_var_references(std::fs::read_to_string(path.as_ref())?);
+ let content = content.replace("&", "&");
let document = roxmltree::Document::parse(&content).map_err(|e| anyhow!(e))?;
let root_node = XmlNode::from(document.root_element());
let root_element = root_node.as_element()?;
diff --git a/src/eww_state.rs b/src/eww_state.rs
index 40bed5a..29f3db5 100644
--- a/src/eww_state.rs
+++ b/src/eww_state.rs
@@ -121,7 +121,7 @@ impl EwwState {
.iter()
.map(|element| match element {
AttrValueElement::Primitive(primitive) => Ok(primitive.clone()),
- AttrValueElement::VarRef(var_name) => self.lookup(var_name).cloned(),
+ AttrValueElement::Expr(expr) => expr.clone().eval(&self.variables_state),
})
.collect()
}
diff --git a/src/main.rs b/src/main.rs
index f846401..922a8e8 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,4 +1,7 @@
#![feature(trace_macros)]
+#![feature(box_syntax)]
+#![feature(or_patterns)]
+#![feature(box_patterns)]
#![feature(slice_concat_trait)]
#![feature(result_cloned)]
#![feature(iterator_fold_self)]
diff --git a/src/value/attr_value.rs b/src/value/attr_value/attr_value.rs
similarity index 71%
rename from src/value/attr_value.rs
rename to src/value/attr_value/attr_value.rs
index 1c7545f..3bd0be0 100644
--- a/src/value/attr_value.rs
+++ b/src/value/attr_value/attr_value.rs
@@ -1,7 +1,9 @@
use anyhow::*;
-use std::{collections::HashMap, iter::FromIterator};
+use itertools::Itertools;
+use serde::{Deserialize, Serialize};
+use std::{collections::HashMap, fmt, iter::FromIterator};
-use super::*;
+use super::super::*;
/// A value assigned to an attribute in a widget.
/// This can be a primitive String that contains any amount of variable
@@ -9,6 +11,12 @@ use super::*;
#[derive(Serialize, Deserialize, Clone, PartialEq, derive_more::Into, derive_more::From, Default)]
pub struct AttrValue(Vec);
+impl fmt::Display for AttrValue {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{}", self.iter().map(|x| format!("{}", x)).join(""))
+ }
+}
+
impl fmt::Debug for AttrValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "AttrValue({:?})", self.0)
@@ -40,7 +48,7 @@ impl AttrValue {
}
pub fn var_refs(&self) -> impl Iterator- {
- self.0.iter().filter_map(|x| x.as_var_ref())
+ self.0.iter().filter_map(|x| Some(x.as_expr()?.var_refs())).flatten()
}
/// resolve partially.
@@ -48,12 +56,16 @@ impl AttrValue {
/// If a referenced variable is not found in the given hashmap, returns the var-ref unchanged.
pub fn resolve_one_level(self, variables: &HashMap) -> AttrValue {
self.into_iter()
- .flat_map(|entry| match entry {
- AttrValueElement::VarRef(var_name) => match variables.get(&var_name) {
- Some(value) => value.0.clone(),
- _ => vec![AttrValueElement::VarRef(var_name)],
- },
- _ => vec![entry],
+ .map(|entry| match entry {
+ AttrValueElement::Expr(expr) => AttrValueElement::Expr(expr.map_terminals_into(|child_expr| match child_expr {
+ AttrValueExpr::VarRef(var_name) => match variables.get(&var_name) {
+ Some(value) => AttrValueExpr::Literal(value.clone()),
+ None => AttrValueExpr::VarRef(var_name),
+ },
+ other => other,
+ })),
+
+ _ => entry,
})
.collect()
}
@@ -65,10 +77,7 @@ impl AttrValue {
self.into_iter()
.map(|element| match element {
AttrValueElement::Primitive(x) => Ok(x),
- AttrValueElement::VarRef(var_name) => variables
- .get(&var_name)
- .cloned()
- .with_context(|| format!("Unknown variable '{}' referenced", var_name)),
+ AttrValueElement::Expr(expr) => expr.eval(variables),
})
.collect()
}
@@ -85,7 +94,7 @@ impl AttrValue {
if c == '}' {
curly_count -= 1;
if curly_count == 0 {
- elements.push(AttrValueElement::VarRef(VarName(std::mem::take(varref))));
+ elements.push(AttrValueElement::Expr(AttrValueExpr::parse(varref).unwrap()));
cur_varref = None
}
} else {
@@ -120,14 +129,22 @@ impl AttrValue {
#[derive(Clone, PartialEq, Serialize, Deserialize)]
pub enum AttrValueElement {
Primitive(PrimitiveValue),
- VarRef(VarName),
+ Expr(AttrValueExpr),
+}
+impl fmt::Display for AttrValueElement {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ AttrValueElement::Primitive(x) => write!(f, "{}", x),
+ AttrValueElement::Expr(x) => write!(f, "{{{{{}}}}}", x),
+ }
+ }
}
impl fmt::Debug for AttrValueElement {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AttrValueElement::Primitive(x) => write!(f, "Primitive({:?})", x),
- AttrValueElement::VarRef(x) => write!(f, "VarRef({:?})", x),
+ AttrValueElement::Expr(x) => write!(f, "Expr({:?})", x),
}
}
}
@@ -137,9 +154,9 @@ impl AttrValueElement {
AttrValueElement::Primitive(PrimitiveValue::from_string(s))
}
- pub fn as_var_ref(&self) -> Option<&VarName> {
+ pub fn as_expr(&self) -> Option<&AttrValueExpr> {
match self {
- AttrValueElement::VarRef(x) => Some(&x),
+ AttrValueElement::Expr(x) => Some(&x),
_ => None,
}
}
@@ -163,12 +180,12 @@ mod test {
assert_eq!(
output,
AttrValue(vec![
- AttrValueElement::VarRef(VarName("foo".to_owned())),
- AttrValueElement::VarRef(VarName("bar".to_owned())),
+ AttrValueElement::Expr(AttrValueExpr::VarRef(VarName("foo".to_owned()))),
+ AttrValueElement::Expr(AttrValueExpr::VarRef(VarName("bar".to_owned()))),
AttrValueElement::primitive("b{}azb{a}z".to_owned()),
- AttrValueElement::VarRef(VarName("bat".to_owned())),
+ AttrValueElement::Expr(AttrValueExpr::VarRef(VarName("bat".to_owned()))),
AttrValueElement::primitive("{}quok".to_owned()),
- AttrValueElement::VarRef(VarName("test".to_owned())),
+ AttrValueElement::Expr(AttrValueExpr::VarRef(VarName("test".to_owned()))),
]),
)
}
@@ -177,7 +194,7 @@ mod test {
assert_eq!(
AttrValue(
vec![
- AttrValueElement::VarRef(VarName("var".to_owned())),
+ AttrValueElement::Expr(AttrValueExpr::VarRef(VarName("var".to_owned()))),
AttrValueElement::primitive("something".to_owned())
]
.into()
diff --git a/src/value/attr_value/attr_value_expr.rs b/src/value/attr_value/attr_value_expr.rs
new file mode 100644
index 0000000..43bc7ee
--- /dev/null
+++ b/src/value/attr_value/attr_value_expr.rs
@@ -0,0 +1,179 @@
+use super::super::*;
+use anyhow::*;
+use serde::{Deserialize, Serialize};
+use std::collections::HashMap;
+
+#[derive(Clone, PartialEq, Serialize, Deserialize, Debug)]
+pub enum BinOp {
+ Plus,
+ Minus,
+ Times,
+ Div,
+ Mod,
+ Equals,
+ NotEquals,
+ And,
+ Or,
+ GT,
+ LT,
+}
+
+impl std::fmt::Display for BinOp {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ BinOp::Plus => write!(f, "+"),
+ BinOp::Minus => write!(f, "-"),
+ BinOp::Times => write!(f, "*"),
+ BinOp::Div => write!(f, "/"),
+ BinOp::Mod => write!(f, "%"),
+ BinOp::Equals => write!(f, "=="),
+ BinOp::NotEquals => write!(f, "!="),
+ BinOp::And => write!(f, "&&"),
+ BinOp::Or => write!(f, "||"),
+ BinOp::GT => write!(f, ">"),
+ BinOp::LT => write!(f, "<"),
+ }
+ }
+}
+
+#[derive(Clone, PartialEq, Serialize, Deserialize, Debug)]
+pub enum UnaryOp {
+ Not,
+}
+
+impl std::fmt::Display for UnaryOp {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ UnaryOp::Not => write!(f, "!"),
+ }
+ }
+}
+
+#[derive(Clone, PartialEq, Serialize, Deserialize)]
+pub enum AttrValueExpr {
+ Literal(AttrValue),
+ VarRef(VarName),
+ BinOp(Box, BinOp, Box),
+ UnaryOp(UnaryOp, Box),
+ IfElse(Box, Box, Box),
+}
+
+impl std::fmt::Display for AttrValueExpr {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ AttrValueExpr::VarRef(x) => write!(f, "{}", x),
+ AttrValueExpr::Literal(x) => write!(f, "\"{}\"", x),
+ AttrValueExpr::BinOp(l, op, r) => write!(f, "({} {} {})", l, op, r),
+ AttrValueExpr::UnaryOp(op, x) => write!(f, "{}{}", op, x),
+ AttrValueExpr::IfElse(a, b, c) => write!(f, "(if {} then {} else {})", a, b, c),
+ }
+ }
+}
+
+impl std::fmt::Debug for AttrValueExpr {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{}", self)
+ }
+}
+
+impl AttrValueExpr {
+ pub fn map_terminals_into(self, f: impl Fn(Self) -> Self) -> Self {
+ use AttrValueExpr::*;
+ match self {
+ BinOp(box a, op, box b) => BinOp(box f(a), op, box f(b)),
+ IfElse(box a, box b, box c) => IfElse(box f(a), box f(b), box f(c)),
+ other => f(other),
+ }
+ }
+
+ /// resolve variable references in the expression. Fails if a variable cannot be resolved.
+ pub fn resolve_refs(self, variables: &HashMap) -> Result {
+ use AttrValueExpr::*;
+ match self {
+ Literal(x) => Ok(AttrValueExpr::Literal(x)),
+ VarRef(ref name) => Ok(Literal(AttrValue::from_primitive(
+ variables
+ .get(name)
+ .with_context(|| format!("Unknown variable {} referenced in {:?}", &name, &self))?
+ .clone(),
+ ))),
+ BinOp(box a, op, box b) => Ok(BinOp(box a.resolve_refs(variables)?, op, box b.resolve_refs(variables)?)),
+ UnaryOp(op, box x) => Ok(UnaryOp(op, box x.resolve_refs(variables)?)),
+ IfElse(box a, box b, box c) => Ok(IfElse(
+ box a.resolve_refs(variables)?,
+ box b.resolve_refs(variables)?,
+ box c.resolve_refs(variables)?,
+ )),
+ }
+ }
+
+ pub fn var_refs(&self) -> Vec<&VarName> {
+ use AttrValueExpr::*;
+ match self {
+ Literal(_) => vec![],
+ VarRef(name) => vec![name],
+ BinOp(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
+ }
+ }
+ }
+
+ pub fn eval(self, values: &HashMap) -> Result {
+ match self {
+ AttrValueExpr::Literal(x) => x.resolve_fully(&values),
+ AttrValueExpr::VarRef(ref name) => values.get(name).cloned().context(format!(
+ "Got unresolved variable {} while trying to evaluate expression {:?}",
+ &name, &self
+ )),
+ AttrValueExpr::BinOp(a, op, b) => {
+ let a = a.eval(values)?;
+ let b = b.eval(values)?;
+ Ok(match op {
+ BinOp::Equals => PrimitiveValue::from(a == b),
+ BinOp::NotEquals => PrimitiveValue::from(a != b),
+ BinOp::And => PrimitiveValue::from(a.as_bool()? && b.as_bool()?),
+ BinOp::Or => PrimitiveValue::from(a.as_bool()? || b.as_bool()?),
+
+ BinOp::Plus => PrimitiveValue::from(a.as_f64()? + b.as_f64()?),
+ BinOp::Minus => PrimitiveValue::from(a.as_f64()? - b.as_f64()?),
+ BinOp::Times => PrimitiveValue::from(a.as_f64()? * b.as_f64()?),
+ BinOp::Div => PrimitiveValue::from(a.as_f64()? / b.as_f64()?),
+ BinOp::Mod => PrimitiveValue::from(a.as_f64()? % b.as_f64()?),
+ BinOp::GT => PrimitiveValue::from(a.as_f64()? > b.as_f64()?),
+ BinOp::LT => PrimitiveValue::from(a.as_f64()? < b.as_f64()?),
+ })
+ }
+ AttrValueExpr::UnaryOp(op, a) => {
+ let a = a.eval(values)?;
+ Ok(match op {
+ UnaryOp::Not => PrimitiveValue::from(!a.as_bool()?),
+ })
+ }
+ AttrValueExpr::IfElse(cond, yes, no) => {
+ if cond.eval(values)?.as_bool()? {
+ yes.eval(values)
+ } else {
+ no.eval(values)
+ }
+ }
+ }
+ }
+
+ pub fn parse<'a>(s: &'a str) -> Result {
+ let parsed = match parser::parse(s) {
+ Ok((_, x)) => Ok(x),
+ Err(nom::Err::Error(e) | nom::Err::Failure(e)) => Err(anyhow!(nom::error::convert_error(s, e))),
+ Err(nom::Err::Incomplete(_)) => Err(anyhow!("Parsing incomplete")),
+ };
+ parsed.context("Failed to parse expression")
+ }
+}
diff --git a/src/value/attr_value/mod.rs b/src/value/attr_value/mod.rs
new file mode 100644
index 0000000..077a689
--- /dev/null
+++ b/src/value/attr_value/mod.rs
@@ -0,0 +1,5 @@
+pub mod attr_value;
+pub mod attr_value_expr;
+pub mod parser;
+pub use attr_value::*;
+pub use attr_value_expr::*;
diff --git a/src/value/attr_value/parser.rs b/src/value/attr_value/parser.rs
new file mode 100644
index 0000000..14ca0d3
--- /dev/null
+++ b/src/value/attr_value/parser.rs
@@ -0,0 +1,209 @@
+use super::*;
+
+use nom::{
+ branch::*,
+ bytes::complete::{tag, take_while},
+ character::complete::{multispace0 as multispace, *},
+ combinator::{map, map_res, *},
+ error::{context, ParseError, VerboseError},
+ multi::many0,
+ sequence::{delimited, preceded, *},
+ IResult, Parser,
+};
+
+use super::super::*;
+
+fn ws<'a, P, O, E: ParseError<&'a str>>(p: P) -> impl FnMut(&'a str) -> IResult<&'a str, O, E>
+where
+ P: Parser<&'a str, O, E>,
+{
+ delimited(multispace, p, multispace)
+}
+
+fn parse_num(i: &str) -> IResult<&str, i32, VerboseError<&str>> {
+ alt((
+ map_res(digit1, |s: &str| s.parse::()),
+ map_res(preceded(tag("-"), digit1), |s: &str| s.parse::().map(|x| x * -1)),
+ ))(i)
+}
+
+fn parse_stringlit(i: &str) -> IResult<&str, &str, VerboseError<&str>> {
+ alt((
+ delimited(tag("'"), take_while(|c| c != '\''), tag("'")),
+ delimited(tag("\""), take_while(|c| c != '"'), tag("\"")),
+ ))(i)
+}
+
+fn parse_bool(i: &str) -> IResult<&str, &str, VerboseError<&str>> {
+ alt((tag("true"), tag("false")))(i)
+}
+
+fn parse_literal(i: &str) -> IResult<&str, &str, VerboseError<&str>> {
+ alt((parse_bool, parse_stringlit, recognize(parse_num)))(i)
+}
+
+fn parse_identifier(i: &str) -> IResult<&str, &str, VerboseError<&str>> {
+ verify(
+ recognize(pair(
+ alt((alpha1, tag("_"), tag("-"))),
+ many0(alt((alphanumeric1, tag("_"), tag("-")))),
+ )),
+ |x| !["if", "then", "else"].contains(x),
+ )(i)
+}
+
+fn parse_unary_op(i: &str) -> IResult<&str, UnaryOp, VerboseError<&str>> {
+ value(UnaryOp::Not, tag("!"))(i)
+}
+
+/////////////////
+// actual tree //
+/////////////////
+
+fn parse_factor(i: &str) -> IResult<&str, AttrValueExpr, VerboseError<&str>> {
+ let (i, unary_op) = opt(parse_unary_op)(i)?;
+ let (i, factor) = alt((
+ context("expression", ws(delimited(tag("("), parse_expr, tag(")")))),
+ context("if-expression", ws(parse_ifelse)),
+ context(
+ "literal",
+ map(ws(parse_literal), |x| AttrValueExpr::Literal(AttrValue::parse_string(x))),
+ ),
+ context(
+ "identifier",
+ map(ws(parse_identifier), |x| AttrValueExpr::VarRef(VarName(x.to_string()))),
+ ),
+ ))(i)?;
+ Ok((
+ i,
+ match unary_op {
+ Some(op) => AttrValueExpr::UnaryOp(op, box factor),
+ None => factor,
+ },
+ ))
+}
+
+fn parse_term3(i: &str) -> IResult<&str, AttrValueExpr, VerboseError<&str>> {
+ let (i, initial) = parse_factor(i)?;
+ let (i, remainder) = many0(alt((
+ map(preceded(tag("*"), parse_factor), |x| (BinOp::Times, x)),
+ map(preceded(tag("/"), parse_factor), |x| (BinOp::Div, x)),
+ map(preceded(tag("%"), parse_factor), |x| (BinOp::Mod, x)),
+ )))(i)?;
+
+ let exprs = remainder
+ .into_iter()
+ .fold(initial, |acc, (op, expr)| AttrValueExpr::BinOp(box acc, op, box expr));
+
+ Ok((i, exprs))
+}
+fn parse_term2(i: &str) -> IResult<&str, AttrValueExpr, VerboseError<&str>> {
+ let (i, initial) = parse_term3(i)?;
+ let (i, remainder) = many0(alt((
+ map(preceded(tag("+"), parse_term3), |x| (BinOp::Plus, x)),
+ map(preceded(tag("-"), parse_term3), |x| (BinOp::Minus, x)),
+ )))(i)?;
+
+ let exprs = remainder
+ .into_iter()
+ .fold(initial, |acc, (op, expr)| AttrValueExpr::BinOp(box acc, op, box expr));
+
+ Ok((i, exprs))
+}
+
+fn parse_term1(i: &str) -> IResult<&str, AttrValueExpr, VerboseError<&str>> {
+ let (i, initial) = parse_term2(i)?;
+ let (i, remainder) = many0(alt((
+ map(preceded(tag("=="), parse_term2), |x| (BinOp::Equals, x)),
+ map(preceded(tag("!="), parse_term2), |x| (BinOp::NotEquals, x)),
+ map(preceded(tag(">"), parse_term2), |x| (BinOp::GT, x)),
+ map(preceded(tag("<"), parse_term2), |x| (BinOp::LT, x)),
+ )))(i)?;
+
+ let exprs = remainder
+ .into_iter()
+ .fold(initial, |acc, (op, expr)| AttrValueExpr::BinOp(box acc, op, box expr));
+
+ Ok((i, exprs))
+}
+pub fn parse_expr(i: &str) -> IResult<&str, AttrValueExpr, VerboseError<&str>> {
+ let (i, initial) = parse_term1(i)?;
+ let (i, remainder) = many0(alt((
+ map(preceded(tag("&&"), parse_term1), |x| (BinOp::And, x)),
+ map(preceded(tag("||"), parse_term1), |x| (BinOp::Or, x)),
+ )))(i)?;
+
+ let exprs = remainder
+ .into_iter()
+ .fold(initial, |acc, (op, expr)| AttrValueExpr::BinOp(box acc, op, box expr));
+
+ Ok((i, exprs))
+}
+
+fn parse_ifelse(i: &str) -> IResult<&str, AttrValueExpr, VerboseError<&str>> {
+ let (i, _) = tag("if")(i)?;
+ let (i, a) = context("condition", ws(parse_expr))(i)?;
+ let (i, _) = tag("then")(i)?;
+ let (i, b) = context("true-case", ws(parse_expr))(i)?;
+ let (i, _) = tag("else")(i)?;
+ let (i, c) = context("false-case", ws(parse_expr))(i)?;
+ Ok((i, AttrValueExpr::IfElse(box a, box b, box c)))
+}
+
+pub fn parse<'a>(i: &'a str) -> IResult<&'a str, AttrValueExpr, VerboseError<&'a str>> {
+ complete(parse_expr)(i)
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ use pretty_assertions::assert_eq;
+ #[test]
+ fn test_parser() {
+ assert_eq!(
+ AttrValueExpr::Literal(AttrValue::from_primitive("12")),
+ AttrValueExpr::parse("12").unwrap()
+ );
+ assert_eq!(
+ AttrValueExpr::UnaryOp(UnaryOp::Not, box AttrValueExpr::Literal(AttrValue::from_primitive("false"))),
+ AttrValueExpr::parse("!false").unwrap()
+ );
+ assert_eq!(
+ AttrValueExpr::BinOp(
+ box AttrValueExpr::Literal(AttrValue::from_primitive("12")),
+ BinOp::Plus,
+ box AttrValueExpr::Literal(AttrValue::from_primitive("2"))
+ ),
+ AttrValueExpr::parse("12 + 2").unwrap()
+ );
+ assert_eq!(
+ AttrValueExpr::UnaryOp(
+ UnaryOp::Not,
+ box AttrValueExpr::BinOp(
+ box AttrValueExpr::Literal(AttrValue::from_primitive("1")),
+ BinOp::Equals,
+ box AttrValueExpr::Literal(AttrValue::from_primitive("2"))
+ )
+ ),
+ AttrValueExpr::parse("!(1 == 2)").unwrap()
+ );
+ assert_eq!(
+ AttrValueExpr::IfElse(
+ box AttrValueExpr::VarRef(VarName("a".to_string())),
+ box AttrValueExpr::VarRef(VarName("b".to_string())),
+ box AttrValueExpr::VarRef(VarName("c".to_string())),
+ ),
+ AttrValueExpr::parse("if a then b else c").unwrap()
+ );
+ }
+ #[test]
+ fn test_complex() {
+ let parsed =
+ AttrValueExpr::parse(r#"if hi > 12 + 2 * 2 && 12 == 15 then "foo" else if !true then 'hi' else "{{bruh}}""#).unwrap();
+
+ assert_eq!(
+ r#"(if ((hi > ("12" + ("2" * "2"))) && ("12" == "15")) then "foo" else (if !"true" then "hi" else "{{bruh}}"))"#,
+ format!("{}", parsed),
+ )
+ }
+}
diff --git a/src/value/mod.rs b/src/value/mod.rs
index 5e9c239..cd002af 100644
--- a/src/value/mod.rs
+++ b/src/value/mod.rs
@@ -1,11 +1,11 @@
use derive_more::*;
use serde::{Deserialize, Serialize};
-use std::fmt;
pub mod attr_value;
pub mod coords;
pub mod primitive;
pub use attr_value::*;
+pub use attr_value_expr::*;
pub use coords::*;
pub use primitive::*;
diff --git a/src/value/primitive.rs b/src/value/primitive.rs
index 9dd6ba2..98891f1 100644
--- a/src/value/primitive.rs
+++ b/src/value/primitive.rs
@@ -5,7 +5,7 @@ use std::{convert::TryFrom, fmt, iter::FromIterator};
use crate::impl_try_from;
-#[derive(Clone, PartialEq, Deserialize, Serialize, derive_more::From, Default)]
+#[derive(Clone, Deserialize, Serialize, derive_more::From, Default)]
pub struct PrimitiveValue(String);
impl fmt::Display for PrimitiveValue {
@@ -19,6 +19,17 @@ impl fmt::Debug for PrimitiveValue {
}
}
+/// 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 for PrimitiveValue {
+ 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 for PrimitiveValue {
fn from_iter>(iter: T) -> Self {
PrimitiveValue(iter.into_iter().join(""))
@@ -54,6 +65,12 @@ impl From for PrimitiveValue {
}
}
+impl From for PrimitiveValue {
+ fn from(s: f64) -> Self {
+ PrimitiveValue(s.to_string())
+ }
+}
+
impl From<&str> for PrimitiveValue {
fn from(s: &str) -> Self {
PrimitiveValue(s.to_string())
diff --git a/src/widgets/widget_node.rs b/src/widgets/widget_node.rs
index 0fc98b7..a43263c 100644
--- a/src/widgets/widget_node.rs
+++ b/src/widgets/widget_node.rs
@@ -125,7 +125,6 @@ mod test {
use super::*;
use crate::config::xml_ext::*;
use maplit::hashmap;
- use pretty_assertions::assert_eq;
#[test]
fn test_generic_generate() {
let w_def1 = {