From 3b129957cd96d9b39d6a78b12b5832bd1cc3c542 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Fri, 14 May 2021 17:08:35 +0200 Subject: [PATCH 001/137] asdf --- .gitignore | 1 + Cargo.lock | 421 +++++++++++++++++++++++ Cargo.toml | 16 + build.rs | 4 + src/calc.lalrpop | 38 +++ src/calc.rs | 869 +++++++++++++++++++++++++++++++++++++++++++++++ src/lexer.rs | 85 +++++ src/main.rs | 42 +++ 8 files changed, 1476 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 build.rs create mode 100644 src/calc.lalrpop create mode 100644 src/calc.rs create mode 100644 src/lexer.rs create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..183c8e6 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,421 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "ascii-canvas" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff8eb72df928aafb99fe5d37b383f2fe25bd2a765e3e5f7c365916b6f2463a29" +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 = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[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 = "blake2b_simd" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587" +dependencies = [ + "arrayref", + "arrayvec", + "constant_time_eq", +] + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + +[[package]] +name = "crossbeam-utils" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4feb231f0d4d6af81aed15928e58ecf5816aa62a2393e2c82f46973e92a9a278" +dependencies = [ + "autocfg", + "cfg-if", + "lazy_static", +] + +[[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" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[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 = "fixedbitset" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d" + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hashbrown" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" + +[[package]] +name = "hermit-abi" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" +dependencies = [ + "libc", +] + +[[package]] +name = "indexmap" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "itertools" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d572918e350e82412fe766d24b15e6682fb2ed2bbe018280caa810397cb319" +dependencies = [ + "either", +] + +[[package]] +name = "lalrpop" +version = "0.19.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46962a8c71b91c3524b117dfdd70844d4265a173c4c9109f98171aebdcf1195f" +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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a708007b751af124d09e9c5d97515257902bc6b486a56b40bcafd939e8ff467" +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.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e" + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + +[[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 = "nomwut" +version = "0.1.0" +dependencies = [ + "lalrpop", + "lalrpop-util", + "regex", +] + +[[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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d7afeb98c5a10e0bffcc7fc16e105b04d06729fac5fd6384aebf7ff5cb5a67d" + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "redox_syscall" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" + +[[package]] +name = "redox_users" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d" +dependencies = [ + "getrandom", + "redox_syscall", + "rust-argon2", +] + +[[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 = "rust-argon2" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb" +dependencies = [ + "base64", + "blake2b_simd", + "constant_time_eq", + "crossbeam-utils", +] + +[[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 = "term" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42" +dependencies = [ + "byteorder", + "dirs", + "winapi", +] + +[[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-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[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" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..dcf74cc --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "nomwut" +version = "0.1.0" +authors = ["elkowar <5300871+elkowar@users.noreply.github.com>"] +edition = "2018" + + +build = "build.rs" +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +lalrpop-util = "0.19.5" +regex = "1" + +[build-dependencies] +lalrpop = "0.19.5" diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..57684be --- /dev/null +++ b/build.rs @@ -0,0 +1,4 @@ +extern crate lalrpop; +fn main() { + lalrpop::process_root().unwrap(); +} diff --git a/src/calc.lalrpop b/src/calc.lalrpop new file mode 100644 index 0000000..e67e552 --- /dev/null +++ b/src/calc.lalrpop @@ -0,0 +1,38 @@ +use std::str::FromStr; +use crate::lexer; +use crate::Expr; + +grammar; + +extern { + type Location = usize; + type Error = lexer::LexicalError; + + enum lexer::Tok { + "(" => lexer::Tok::LPren, + ")" => lexer::Tok::RPren, + Space => lexer::Tok::Space, + Int => lexer::Tok::Int(), + } +} + + +Sep: Vec = { + S)*> => match e { + None => v, + Some(e) => { + v.push(e); + v + } + } +} + + +pub Expr: Expr = { + "(" > ")" => Expr::List(elems), + => Expr::Number(n), +}; + + + +// vim:shiftwidth=4 diff --git a/src/calc.rs b/src/calc.rs new file mode 100644 index 0000000..7822044 --- /dev/null +++ b/src/calc.rs @@ -0,0 +1,869 @@ +// auto-generated: "lalrpop 0.19.5" +// sha3: f4883744b015673a40928edc8f19edfbcd87416d27a5af7681a47c80f6af451 +use std::str::FromStr; +use crate::lexer; +use crate::Expr; +#[allow(unused_extern_crates)] +extern crate lalrpop_util as __lalrpop_util; +#[allow(unused_imports)] +use self::__lalrpop_util::state_machine as __state_machine; +extern crate core; +extern crate alloc; + +#[cfg_attr(rustfmt, rustfmt_skip)] +mod __parse__Expr { + #![allow(non_snake_case, non_camel_case_types, unused_mut, unused_variables, unused_imports, unused_parens)] + + use std::str::FromStr; + use crate::lexer; + use crate::Expr; + #[allow(unused_extern_crates)] + extern crate lalrpop_util as __lalrpop_util; + #[allow(unused_imports)] + use self::__lalrpop_util::state_machine as __state_machine; + extern crate core; + extern crate alloc; + use super::__ToTriple; + #[allow(dead_code)] + pub(crate) enum __Symbol<> + { + Variant0(lexer::Tok), + Variant1(i32), + Variant2(Expr), + Variant3(alloc::vec::Vec), + Variant4(core::option::Option), + Variant5(Vec), + } + const __ACTION: &[i8] = &[ + // State 0 + 2, 0, 5, 0, + // State 1 + 2, -11, 5, 0, + // State 2 + 2, -13, 5, 0, + // State 3 + 0, 0, 0, 0, + // State 4 + 0, -7, 0, -7, + // State 5 + 0, -10, 0, 9, + // State 6 + 0, 10, 0, 0, + // State 7 + 0, -12, 0, 11, + // State 8 + -4, -4, -4, 0, + // State 9 + 0, -6, 0, -6, + // State 10 + -5, -5, -5, 0, + ]; + fn __action(state: i8, integer: usize) -> i8 { + __ACTION[(state as usize) * 4 + integer] + } + const __EOF_ACTION: &[i8] = &[ + // State 0 + 0, + // State 1 + 0, + // State 2 + 0, + // State 3 + -14, + // State 4 + -7, + // State 5 + 0, + // State 6 + 0, + // State 7 + 0, + // State 8 + 0, + // State 9 + -6, + // State 10 + 0, + ]; + fn __goto(state: i8, nt: usize) -> i8 { + match nt { + 2 => 2, + 3 => match state { + 1 => 5, + 2 => 7, + _ => 3, + }, + 5 => 6, + _ => 0, + } + } + fn __expected_tokens(__state: i8) -> alloc::vec::Vec { + const __TERMINAL: &[&str] = &[ + r###""(""###, + r###"")""###, + r###"Int"###, + r###"Space"###, + ]; + __TERMINAL.iter().enumerate().filter_map(|(index, terminal)| { + let next_state = __action(__state, index); + if next_state == 0 { + None + } else { + Some(alloc::string::ToString::to_string(terminal)) + } + }).collect() + } + pub(crate) struct __StateMachine<> + where + { + __phantom: core::marker::PhantomData<()>, + } + impl<> __state_machine::ParserDefinition for __StateMachine<> + where + { + type Location = usize; + type Error = lexer::LexicalError; + type Token = lexer::Tok; + type TokenIndex = usize; + type Symbol = __Symbol<>; + type Success = Expr; + type StateIndex = i8; + type Action = i8; + type ReduceIndex = i8; + type NonterminalIndex = usize; + + #[inline] + fn start_location(&self) -> Self::Location { + Default::default() + } + + #[inline] + fn start_state(&self) -> Self::StateIndex { + 0 + } + + #[inline] + fn token_to_index(&self, token: &Self::Token) -> Option { + __token_to_integer(token, core::marker::PhantomData::<()>) + } + + #[inline] + fn action(&self, state: i8, integer: usize) -> i8 { + __action(state, integer) + } + + #[inline] + fn error_action(&self, state: i8) -> i8 { + __action(state, 4 - 1) + } + + #[inline] + fn eof_action(&self, state: i8) -> i8 { + __EOF_ACTION[state as usize] + } + + #[inline] + fn goto(&self, state: i8, nt: usize) -> i8 { + __goto(state, nt) + } + + fn token_to_symbol(&self, token_index: usize, token: Self::Token) -> Self::Symbol { + __token_to_symbol(token_index, token, core::marker::PhantomData::<()>) + } + + fn expected_tokens(&self, state: i8) -> alloc::vec::Vec { + __expected_tokens(state) + } + + #[inline] + fn uses_error_recovery(&self) -> bool { + false + } + + #[inline] + fn error_recovery_symbol( + &self, + recovery: __state_machine::ErrorRecovery, + ) -> Self::Symbol { + panic!("error recovery not enabled for this grammar") + } + + fn reduce( + &mut self, + action: i8, + start_location: Option<&Self::Location>, + states: &mut alloc::vec::Vec, + symbols: &mut alloc::vec::Vec<__state_machine::SymbolTriple>, + ) -> Option<__state_machine::ParseResult> { + __reduce( + action, + start_location, + states, + symbols, + core::marker::PhantomData::<()>, + ) + } + + fn simulate_reduce(&self, action: i8) -> __state_machine::SimulatedReduce { + panic!("error recovery not enabled for this grammar") + } + } + fn __token_to_integer< + >( + __token: &lexer::Tok, + _: core::marker::PhantomData<()>, + ) -> Option + { + match *__token { + lexer::Tok::LPren if true => Some(0), + lexer::Tok::RPren if true => Some(1), + lexer::Tok::Int(_) if true => Some(2), + lexer::Tok::Space if true => Some(3), + _ => None, + } + } + fn __token_to_symbol< + >( + __token_index: usize, + __token: lexer::Tok, + _: core::marker::PhantomData<()>, + ) -> __Symbol<> + { + match __token_index { + 0 | 1 | 3 => __Symbol::Variant0(__token), + 2 => match __token { + lexer::Tok::Int(__tok0) if true => __Symbol::Variant1(__tok0), + _ => unreachable!(), + }, + _ => unreachable!(), + } + } + pub struct ExprParser { + _priv: (), + } + + impl ExprParser { + pub fn new() -> ExprParser { + ExprParser { + _priv: (), + } + } + + #[allow(dead_code)] + pub fn parse< + __TOKEN: __ToTriple<>, + __TOKENS: IntoIterator, + >( + &self, + __tokens0: __TOKENS, + ) -> Result> + { + let __tokens = __tokens0.into_iter(); + let mut __tokens = __tokens.map(|t| __ToTriple::to_triple(t)); + __state_machine::Parser::drive( + __StateMachine { + __phantom: core::marker::PhantomData::<()>, + }, + __tokens, + ) + } + } + pub(crate) fn __reduce< + >( + __action: i8, + __lookahead_start: Option<&usize>, + __states: &mut alloc::vec::Vec, + __symbols: &mut alloc::vec::Vec<(usize,__Symbol<>,usize)>, + _: core::marker::PhantomData<()>, + ) -> Option>> + { + let (__pop_states, __nonterminal) = match __action { + 0 => { + __reduce0(__lookahead_start, __symbols, core::marker::PhantomData::<()>) + } + 1 => { + __reduce1(__lookahead_start, __symbols, core::marker::PhantomData::<()>) + } + 2 => { + __reduce2(__lookahead_start, __symbols, core::marker::PhantomData::<()>) + } + 3 => { + __reduce3(__lookahead_start, __symbols, core::marker::PhantomData::<()>) + } + 4 => { + __reduce4(__lookahead_start, __symbols, core::marker::PhantomData::<()>) + } + 5 => { + __reduce5(__lookahead_start, __symbols, core::marker::PhantomData::<()>) + } + 6 => { + __reduce6(__lookahead_start, __symbols, core::marker::PhantomData::<()>) + } + 7 => { + __reduce7(__lookahead_start, __symbols, core::marker::PhantomData::<()>) + } + 8 => { + __reduce8(__lookahead_start, __symbols, core::marker::PhantomData::<()>) + } + 9 => { + __reduce9(__lookahead_start, __symbols, core::marker::PhantomData::<()>) + } + 10 => { + __reduce10(__lookahead_start, __symbols, core::marker::PhantomData::<()>) + } + 11 => { + __reduce11(__lookahead_start, __symbols, core::marker::PhantomData::<()>) + } + 12 => { + __reduce12(__lookahead_start, __symbols, core::marker::PhantomData::<()>) + } + 13 => { + // __Expr = Expr => ActionFn(0); + let __sym0 = __pop_Variant2(__symbols); + let __start = __sym0.0.clone(); + let __end = __sym0.2.clone(); + let __nt = super::__action0::<>(__sym0); + return Some(Ok(__nt)); + } + _ => panic!("invalid action code {}", __action) + }; + let __states_len = __states.len(); + __states.truncate(__states_len - __pop_states); + let __state = *__states.last().unwrap(); + let __next_state = __goto(__state, __nonterminal); + __states.push(__next_state); + None + } + #[inline(never)] + fn __symbol_type_mismatch() -> ! { + panic!("symbol type mismatch") + } + fn __pop_Variant2< + >( + __symbols: &mut alloc::vec::Vec<(usize,__Symbol<>,usize)> + ) -> (usize, Expr, usize) + { + match __symbols.pop() { + Some((__l, __Symbol::Variant2(__v), __r)) => (__l, __v, __r), + _ => __symbol_type_mismatch() + } + } + fn __pop_Variant5< + >( + __symbols: &mut alloc::vec::Vec<(usize,__Symbol<>,usize)> + ) -> (usize, Vec, usize) + { + match __symbols.pop() { + Some((__l, __Symbol::Variant5(__v), __r)) => (__l, __v, __r), + _ => __symbol_type_mismatch() + } + } + fn __pop_Variant3< + >( + __symbols: &mut alloc::vec::Vec<(usize,__Symbol<>,usize)> + ) -> (usize, alloc::vec::Vec, usize) + { + match __symbols.pop() { + Some((__l, __Symbol::Variant3(__v), __r)) => (__l, __v, __r), + _ => __symbol_type_mismatch() + } + } + fn __pop_Variant4< + >( + __symbols: &mut alloc::vec::Vec<(usize,__Symbol<>,usize)> + ) -> (usize, core::option::Option, usize) + { + match __symbols.pop() { + Some((__l, __Symbol::Variant4(__v), __r)) => (__l, __v, __r), + _ => __symbol_type_mismatch() + } + } + fn __pop_Variant1< + >( + __symbols: &mut alloc::vec::Vec<(usize,__Symbol<>,usize)> + ) -> (usize, i32, usize) + { + match __symbols.pop() { + Some((__l, __Symbol::Variant1(__v), __r)) => (__l, __v, __r), + _ => __symbol_type_mismatch() + } + } + fn __pop_Variant0< + >( + __symbols: &mut alloc::vec::Vec<(usize,__Symbol<>,usize)> + ) -> (usize, lexer::Tok, usize) + { + match __symbols.pop() { + Some((__l, __Symbol::Variant0(__v), __r)) => (__l, __v, __r), + _ => __symbol_type_mismatch() + } + } + pub(crate) fn __reduce0< + >( + __lookahead_start: Option<&usize>, + __symbols: &mut alloc::vec::Vec<(usize,__Symbol<>,usize)>, + _: core::marker::PhantomData<()>, + ) -> (usize, usize) + { + // ( Space) = Expr, Space => ActionFn(8); + assert!(__symbols.len() >= 2); + let __sym1 = __pop_Variant0(__symbols); + let __sym0 = __pop_Variant2(__symbols); + let __start = __sym0.0.clone(); + let __end = __sym1.2.clone(); + let __nt = super::__action8::<>(__sym0, __sym1); + __symbols.push((__start, __Symbol::Variant2(__nt), __end)); + (2, 0) + } + pub(crate) fn __reduce1< + >( + __lookahead_start: Option<&usize>, + __symbols: &mut alloc::vec::Vec<(usize,__Symbol<>,usize)>, + _: core::marker::PhantomData<()>, + ) -> (usize, usize) + { + // ( Space)* = => ActionFn(6); + let __start = __lookahead_start.cloned().or_else(|| __symbols.last().map(|s| s.2.clone())).unwrap_or_default(); + let __end = __start.clone(); + let __nt = super::__action6::<>(&__start, &__end); + __symbols.push((__start, __Symbol::Variant3(__nt), __end)); + (0, 1) + } + pub(crate) fn __reduce2< + >( + __lookahead_start: Option<&usize>, + __symbols: &mut alloc::vec::Vec<(usize,__Symbol<>,usize)>, + _: core::marker::PhantomData<()>, + ) -> (usize, usize) + { + // ( Space)* = ( Space)+ => ActionFn(7); + let __sym0 = __pop_Variant3(__symbols); + let __start = __sym0.0.clone(); + let __end = __sym0.2.clone(); + let __nt = super::__action7::<>(__sym0); + __symbols.push((__start, __Symbol::Variant3(__nt), __end)); + (1, 1) + } + pub(crate) fn __reduce3< + >( + __lookahead_start: Option<&usize>, + __symbols: &mut alloc::vec::Vec<(usize,__Symbol<>,usize)>, + _: core::marker::PhantomData<()>, + ) -> (usize, usize) + { + // ( Space)+ = Expr, Space => ActionFn(11); + assert!(__symbols.len() >= 2); + let __sym1 = __pop_Variant0(__symbols); + let __sym0 = __pop_Variant2(__symbols); + let __start = __sym0.0.clone(); + let __end = __sym1.2.clone(); + let __nt = super::__action11::<>(__sym0, __sym1); + __symbols.push((__start, __Symbol::Variant3(__nt), __end)); + (2, 2) + } + pub(crate) fn __reduce4< + >( + __lookahead_start: Option<&usize>, + __symbols: &mut alloc::vec::Vec<(usize,__Symbol<>,usize)>, + _: core::marker::PhantomData<()>, + ) -> (usize, usize) + { + // ( Space)+ = ( Space)+, Expr, Space => ActionFn(12); + assert!(__symbols.len() >= 3); + let __sym2 = __pop_Variant0(__symbols); + let __sym1 = __pop_Variant2(__symbols); + let __sym0 = __pop_Variant3(__symbols); + let __start = __sym0.0.clone(); + let __end = __sym2.2.clone(); + let __nt = super::__action12::<>(__sym0, __sym1, __sym2); + __symbols.push((__start, __Symbol::Variant3(__nt), __end)); + (3, 2) + } + pub(crate) fn __reduce5< + >( + __lookahead_start: Option<&usize>, + __symbols: &mut alloc::vec::Vec<(usize,__Symbol<>,usize)>, + _: core::marker::PhantomData<()>, + ) -> (usize, usize) + { + // Expr = "(", Sep, ")" => ActionFn(1); + assert!(__symbols.len() >= 3); + let __sym2 = __pop_Variant0(__symbols); + let __sym1 = __pop_Variant5(__symbols); + let __sym0 = __pop_Variant0(__symbols); + let __start = __sym0.0.clone(); + let __end = __sym2.2.clone(); + let __nt = super::__action1::<>(__sym0, __sym1, __sym2); + __symbols.push((__start, __Symbol::Variant2(__nt), __end)); + (3, 3) + } + pub(crate) fn __reduce6< + >( + __lookahead_start: Option<&usize>, + __symbols: &mut alloc::vec::Vec<(usize,__Symbol<>,usize)>, + _: core::marker::PhantomData<()>, + ) -> (usize, usize) + { + // Expr = Int => ActionFn(2); + let __sym0 = __pop_Variant1(__symbols); + let __start = __sym0.0.clone(); + let __end = __sym0.2.clone(); + let __nt = super::__action2::<>(__sym0); + __symbols.push((__start, __Symbol::Variant2(__nt), __end)); + (1, 3) + } + pub(crate) fn __reduce7< + >( + __lookahead_start: Option<&usize>, + __symbols: &mut alloc::vec::Vec<(usize,__Symbol<>,usize)>, + _: core::marker::PhantomData<()>, + ) -> (usize, usize) + { + // Expr? = Expr => ActionFn(4); + let __sym0 = __pop_Variant2(__symbols); + let __start = __sym0.0.clone(); + let __end = __sym0.2.clone(); + let __nt = super::__action4::<>(__sym0); + __symbols.push((__start, __Symbol::Variant4(__nt), __end)); + (1, 4) + } + pub(crate) fn __reduce8< + >( + __lookahead_start: Option<&usize>, + __symbols: &mut alloc::vec::Vec<(usize,__Symbol<>,usize)>, + _: core::marker::PhantomData<()>, + ) -> (usize, usize) + { + // Expr? = => ActionFn(5); + let __start = __lookahead_start.cloned().or_else(|| __symbols.last().map(|s| s.2.clone())).unwrap_or_default(); + let __end = __start.clone(); + let __nt = super::__action5::<>(&__start, &__end); + __symbols.push((__start, __Symbol::Variant4(__nt), __end)); + (0, 4) + } + pub(crate) fn __reduce9< + >( + __lookahead_start: Option<&usize>, + __symbols: &mut alloc::vec::Vec<(usize,__Symbol<>,usize)>, + _: core::marker::PhantomData<()>, + ) -> (usize, usize) + { + // Sep = Expr => ActionFn(15); + let __sym0 = __pop_Variant2(__symbols); + let __start = __sym0.0.clone(); + let __end = __sym0.2.clone(); + let __nt = super::__action15::<>(__sym0); + __symbols.push((__start, __Symbol::Variant5(__nt), __end)); + (1, 5) + } + pub(crate) fn __reduce10< + >( + __lookahead_start: Option<&usize>, + __symbols: &mut alloc::vec::Vec<(usize,__Symbol<>,usize)>, + _: core::marker::PhantomData<()>, + ) -> (usize, usize) + { + // Sep = => ActionFn(16); + let __start = __lookahead_start.cloned().or_else(|| __symbols.last().map(|s| s.2.clone())).unwrap_or_default(); + let __end = __start.clone(); + let __nt = super::__action16::<>(&__start, &__end); + __symbols.push((__start, __Symbol::Variant5(__nt), __end)); + (0, 5) + } + pub(crate) fn __reduce11< + >( + __lookahead_start: Option<&usize>, + __symbols: &mut alloc::vec::Vec<(usize,__Symbol<>,usize)>, + _: core::marker::PhantomData<()>, + ) -> (usize, usize) + { + // Sep = ( Space)+, Expr => ActionFn(17); + assert!(__symbols.len() >= 2); + let __sym1 = __pop_Variant2(__symbols); + let __sym0 = __pop_Variant3(__symbols); + let __start = __sym0.0.clone(); + let __end = __sym1.2.clone(); + let __nt = super::__action17::<>(__sym0, __sym1); + __symbols.push((__start, __Symbol::Variant5(__nt), __end)); + (2, 5) + } + pub(crate) fn __reduce12< + >( + __lookahead_start: Option<&usize>, + __symbols: &mut alloc::vec::Vec<(usize,__Symbol<>,usize)>, + _: core::marker::PhantomData<()>, + ) -> (usize, usize) + { + // Sep = ( Space)+ => ActionFn(18); + let __sym0 = __pop_Variant3(__symbols); + let __start = __sym0.0.clone(); + let __end = __sym0.2.clone(); + let __nt = super::__action18::<>(__sym0); + __symbols.push((__start, __Symbol::Variant5(__nt), __end)); + (1, 5) + } +} +pub use self::__parse__Expr::ExprParser; + +fn __action0< +>( + (_, __0, _): (usize, Expr, usize), +) -> Expr +{ + __0 +} + +fn __action1< +>( + (_, _, _): (usize, lexer::Tok, usize), + (_, elems, _): (usize, Vec, usize), + (_, _, _): (usize, lexer::Tok, usize), +) -> Expr +{ + Expr::List(elems) +} + +fn __action2< +>( + (_, n, _): (usize, i32, usize), +) -> Expr +{ + Expr::Number(n) +} + +fn __action3< +>( + (_, mut v, _): (usize, alloc::vec::Vec, usize), + (_, e, _): (usize, core::option::Option, usize), +) -> Vec +{ + match e { + None => v, + Some(e) => { + v.push(e); + v + } + } +} + +fn __action4< +>( + (_, __0, _): (usize, Expr, usize), +) -> core::option::Option +{ + Some(__0) +} + +fn __action5< +>( + __lookbehind: &usize, + __lookahead: &usize, +) -> core::option::Option +{ + None +} + +fn __action6< +>( + __lookbehind: &usize, + __lookahead: &usize, +) -> alloc::vec::Vec +{ + alloc::vec![] +} + +fn __action7< +>( + (_, v, _): (usize, alloc::vec::Vec, usize), +) -> alloc::vec::Vec +{ + v +} + +fn __action8< +>( + (_, __0, _): (usize, Expr, usize), + (_, _, _): (usize, lexer::Tok, usize), +) -> Expr +{ + __0 +} + +fn __action9< +>( + (_, __0, _): (usize, Expr, usize), +) -> alloc::vec::Vec +{ + alloc::vec![__0] +} + +fn __action10< +>( + (_, v, _): (usize, alloc::vec::Vec, usize), + (_, e, _): (usize, Expr, usize), +) -> alloc::vec::Vec +{ + { let mut v = v; v.push(e); v } +} + +fn __action11< +>( + __0: (usize, Expr, usize), + __1: (usize, lexer::Tok, usize), +) -> alloc::vec::Vec +{ + let __start0 = __0.0.clone(); + let __end0 = __1.2.clone(); + let __temp0 = __action8( + __0, + __1, + ); + let __temp0 = (__start0, __temp0, __end0); + __action9( + __temp0, + ) +} + +fn __action12< +>( + __0: (usize, alloc::vec::Vec, usize), + __1: (usize, Expr, usize), + __2: (usize, lexer::Tok, usize), +) -> alloc::vec::Vec +{ + let __start0 = __1.0.clone(); + let __end0 = __2.2.clone(); + let __temp0 = __action8( + __1, + __2, + ); + let __temp0 = (__start0, __temp0, __end0); + __action10( + __0, + __temp0, + ) +} + +fn __action13< +>( + __0: (usize, core::option::Option, usize), +) -> Vec +{ + let __start0 = __0.0.clone(); + let __end0 = __0.0.clone(); + let __temp0 = __action6( + &__start0, + &__end0, + ); + let __temp0 = (__start0, __temp0, __end0); + __action3( + __temp0, + __0, + ) +} + +fn __action14< +>( + __0: (usize, alloc::vec::Vec, usize), + __1: (usize, core::option::Option, usize), +) -> Vec +{ + let __start0 = __0.0.clone(); + let __end0 = __0.2.clone(); + let __temp0 = __action7( + __0, + ); + let __temp0 = (__start0, __temp0, __end0); + __action3( + __temp0, + __1, + ) +} + +fn __action15< +>( + __0: (usize, Expr, usize), +) -> Vec +{ + let __start0 = __0.0.clone(); + let __end0 = __0.2.clone(); + let __temp0 = __action4( + __0, + ); + let __temp0 = (__start0, __temp0, __end0); + __action13( + __temp0, + ) +} + +fn __action16< +>( + __lookbehind: &usize, + __lookahead: &usize, +) -> Vec +{ + let __start0 = __lookbehind.clone(); + let __end0 = __lookahead.clone(); + let __temp0 = __action5( + &__start0, + &__end0, + ); + let __temp0 = (__start0, __temp0, __end0); + __action13( + __temp0, + ) +} + +fn __action17< +>( + __0: (usize, alloc::vec::Vec, usize), + __1: (usize, Expr, usize), +) -> Vec +{ + let __start0 = __1.0.clone(); + let __end0 = __1.2.clone(); + let __temp0 = __action4( + __1, + ); + let __temp0 = (__start0, __temp0, __end0); + __action14( + __0, + __temp0, + ) +} + +fn __action18< +>( + __0: (usize, alloc::vec::Vec, usize), +) -> Vec +{ + let __start0 = __0.2.clone(); + let __end0 = __0.2.clone(); + let __temp0 = __action5( + &__start0, + &__end0, + ); + let __temp0 = (__start0, __temp0, __end0); + __action14( + __0, + __temp0, + ) +} + +pub trait __ToTriple<> { + fn to_triple(value: Self) -> Result<(usize,lexer::Tok,usize), __lalrpop_util::ParseError>; +} + +impl<> __ToTriple<> for (usize, lexer::Tok, usize) { + fn to_triple(value: Self) -> Result<(usize,lexer::Tok,usize), __lalrpop_util::ParseError> { + Ok(value) + } +} +impl<> __ToTriple<> for Result<(usize, lexer::Tok, usize), lexer::LexicalError> { + fn to_triple(value: Self) -> Result<(usize,lexer::Tok,usize), __lalrpop_util::ParseError> { + match value { + Ok(v) => Ok(v), + Err(error) => Err(__lalrpop_util::ParseError::User { error }), + } + } +} diff --git a/src/lexer.rs b/src/lexer.rs new file mode 100644 index 0000000..8deea40 --- /dev/null +++ b/src/lexer.rs @@ -0,0 +1,85 @@ +use std::str::CharIndices; + +pub type Spanned = Result<(Loc, Tok, Loc), Error>; + +#[derive(Copy, Clone, Debug)] +pub enum Tok { + LPren, + RPren, + Space, + Int(i32), +} + +#[derive(Debug, Copy, Clone)] +pub enum LexicalError { + InvalidDigit, + UnknownToken, +} + +impl std::fmt::Display for LexicalError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +pub struct Lexer<'input> { + chars: CharIndices<'input>, +} + +impl<'input> Lexer<'input> { + pub fn new(input: &'input str) -> Self { + Lexer { + chars: input.char_indices(), + } + } +} + +impl<'input> Iterator for Lexer<'input> { + type Item = Spanned; + + fn next(&mut self) -> Option { + let c = self.chars.next(); + match c { + Some((i, '(')) => Some(Ok((i, Tok::LPren, i + 1))), + Some((i, ')')) => Some(Ok((i, Tok::RPren, i + 1))), + Some((i, s)) if s.is_whitespace() => { + let mut last_space = i; + loop { + match self.chars.next() { + Some((i, next)) if next.is_whitespace() => { + last_space = i; + } + _ => { + break; + } + } + } + Some(Ok((i, Tok::Space, last_space + 1))) + } + Some((i, s)) if s.is_digit(10) || s == '-' => { + let mut end = i; + let mut digits = String::new(); + + loop { + match self.chars.next() { + Some((i, next)) if next.is_digit(10) => { + end = i; + digits.push(next); + } + _ => { + break; + } + } + } + + let num = match digits.parse::() { + Ok(num) => num, + Err(_err) => return Some(Err(LexicalError::InvalidDigit)), + }; + Some(Ok((i, Tok::Int(num), end + 1))) + } + Some((_, _)) => Some(Err(LexicalError::UnknownToken)), + None => None, + } + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..7c14565 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,42 @@ +#![allow(unused_imports)] + +use lalrpop_util::lalrpop_mod; + +mod lexer; + +lalrpop_mod!(pub calc); + +#[derive(Debug)] +pub enum Expr { + List(Vec), + Symbol(String), + Number(i32), +} + +fn main() {} + +#[allow(unused_macros)] +macro_rules! test_p { + ($e:expr) => { + let e = $e; + let lex = crate::lexer::Lexer::new(e); + let p = calc::ExprParser::new(); + match p.parse(lex) { + Ok(res) => println!("{:?}", res), + Err(e) => eprintln!("{:?}", e), + } + }; +} + +#[test] +fn calc() { + //assert!(calc::ExprParser::new().parse("(1 2 3)").is_ok()); + + test_p!("1"); + test_p!("(12)"); + test_p!("(1 2)"); + + println!("\n\n\n\n\n\n"); + + panic!() +} From 161de3dc0a440f3ecaee5f4e29c6b7624f5b9a33 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Fri, 14 May 2021 20:57:32 +0200 Subject: [PATCH 002/137] more stuff --- Cargo.lock | 1 + Cargo.toml | 1 + src/calc.lalrpop | 54 +-- src/calc.rs | 836 +++++++++++++++++++---------------------------- src/main.rs | 66 +++- 5 files changed, 424 insertions(+), 534 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 183c8e6..7806c6c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -266,6 +266,7 @@ checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" name = "nomwut" version = "0.1.0" dependencies = [ + "itertools", "lalrpop", "lalrpop-util", "regex", diff --git a/Cargo.toml b/Cargo.toml index dcf74cc..d938bfb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ build = "build.rs" [dependencies] lalrpop-util = "0.19.5" regex = "1" +itertools = "0.10" [build-dependencies] lalrpop = "0.19.5" diff --git a/src/calc.lalrpop b/src/calc.lalrpop index e67e552..48405fe 100644 --- a/src/calc.lalrpop +++ b/src/calc.lalrpop @@ -1,38 +1,42 @@ use std::str::FromStr; -use crate::lexer; +//use crate::lexer; use crate::Expr; +use crate::Sp; grammar; -extern { - type Location = usize; - type Error = lexer::LexicalError; - - enum lexer::Tok { - "(" => lexer::Tok::LPren, - ")" => lexer::Tok::RPren, - Space => lexer::Tok::Space, - Int => lexer::Tok::Int(), - } -} - - -Sep: Vec = { - S)*> => match e { - None => v, - Some(e) => { - v.push(e); - v - } - } -} +Span: Sp = { + <@L> <@R> => Sp(<>) +}; pub Expr: Expr = { - "(" > ")" => Expr::List(elems), - => Expr::Number(n), + "(" )>>)+> ")" => Expr::List(elems), + + "{" )>> )>>)*> "}" => Expr::Table(elems), + + => x, + => x, + => Expr::Str(x), + => Expr::Number(x), + Comment => Expr::Comment, }; +Keyword: Expr = => Expr::Keyword(<>.to_string()); +Symbol: Expr = /.*-+][^\s{}\(\)]*"> => Expr::Symbol(<>.to_string()); + +StrLit: String = { + r#""(?:[^"\\]|\\.)*""# => { + let val = <>; + val[1..val.len() - 1].to_owned() + }, +} + +Comment: () = r";[^\n\r]*"; + + +Num: i32 = => i32::from_str(<>).unwrap(); + // vim:shiftwidth=4 diff --git a/src/calc.rs b/src/calc.rs index 7822044..19225cc 100644 --- a/src/calc.rs +++ b/src/calc.rs @@ -1,7 +1,6 @@ // auto-generated: "lalrpop 0.19.5" -// sha3: f4883744b015673a40928edc8f19edfbcd87416d27a5af7681a47c80f6af451 +// sha3: 7f1eed9c7f5eae965f19f037e3f83cc3f8b8ac855ed1b7d5e579d3c668c23b2 use std::str::FromStr; -use crate::lexer; use crate::Expr; #[allow(unused_extern_crates)] extern crate lalrpop_util as __lalrpop_util; @@ -15,7 +14,6 @@ mod __parse__Expr { #![allow(non_snake_case, non_camel_case_types, unused_mut, unused_variables, unused_imports, unused_parens)] use std::str::FromStr; - use crate::lexer; use crate::Expr; #[allow(unused_extern_crates)] extern crate lalrpop_util as __lalrpop_util; @@ -23,40 +21,38 @@ mod __parse__Expr { use self::__lalrpop_util::state_machine as __state_machine; extern crate core; extern crate alloc; - use super::__ToTriple; + use self::__lalrpop_util::lexer::Token; #[allow(dead_code)] - pub(crate) enum __Symbol<> + pub(crate) enum __Symbol<'input> { - Variant0(lexer::Tok), - Variant1(i32), - Variant2(Expr), - Variant3(alloc::vec::Vec), - Variant4(core::option::Option), - Variant5(Vec), + Variant0(&'input str), + Variant1(Expr), + Variant2(alloc::vec::Vec), + Variant3(i32), } const __ACTION: &[i8] = &[ // State 0 - 2, 0, 5, 0, + 2, 0, 7, 8, // State 1 - 2, -11, 5, 0, + 2, 0, 7, 8, // State 2 - 2, -13, 5, 0, + 2, 11, 7, 8, // State 3 0, 0, 0, 0, // State 4 - 0, -7, 0, -7, + -5, -5, -5, -5, // State 5 - 0, -10, 0, 9, + -6, -6, -6, -6, // State 6 - 0, 10, 0, 0, + -7, -7, -7, -7, // State 7 - 0, -12, 0, 11, + -8, -8, -8, -8, // State 8 - -4, -4, -4, 0, + -2, -2, -2, -2, // State 9 - 0, -6, 0, -6, + -3, -3, -3, -3, // State 10 - -5, -5, -5, 0, + -4, -4, -4, -4, ]; fn __action(state: i8, integer: usize) -> i8 { __ACTION[(state as usize) * 4 + integer] @@ -69,31 +65,32 @@ mod __parse__Expr { // State 2 0, // State 3 - -14, + -9, // State 4 - -7, + -5, // State 5 - 0, + -6, // State 6 - 0, + -7, // State 7 - 0, + -8, // State 8 0, // State 9 - -6, - // State 10 0, + // State 10 + -4, ]; fn __goto(state: i8, nt: usize) -> i8 { match nt { - 2 => 2, - 3 => match state { - 1 => 5, - 2 => 7, + 1 => 2, + 2 => match state { + 1 => 8, + 2 => 9, _ => 3, }, - 5 => 6, + 3 => 4, + 4 => 5, _ => 0, } } @@ -101,8 +98,8 @@ mod __parse__Expr { const __TERMINAL: &[&str] = &[ r###""(""###, r###"")""###, - r###"Int"###, - r###"Space"###, + r###"r#":[^\\s]+"#"###, + r###"r#"[0-9]+"#"###, ]; __TERMINAL.iter().enumerate().filter_map(|(index, terminal)| { let next_state = __action(__state, index); @@ -113,19 +110,20 @@ mod __parse__Expr { } }).collect() } - pub(crate) struct __StateMachine<> + pub(crate) struct __StateMachine<'input> where { - __phantom: core::marker::PhantomData<()>, + input: &'input str, + __phantom: core::marker::PhantomData<(&'input ())>, } - impl<> __state_machine::ParserDefinition for __StateMachine<> + impl<'input> __state_machine::ParserDefinition for __StateMachine<'input> where { type Location = usize; - type Error = lexer::LexicalError; - type Token = lexer::Tok; + type Error = &'static str; + type Token = Token<'input>; type TokenIndex = usize; - type Symbol = __Symbol<>; + type Symbol = __Symbol<'input>; type Success = Expr; type StateIndex = i8; type Action = i8; @@ -144,7 +142,7 @@ mod __parse__Expr { #[inline] fn token_to_index(&self, token: &Self::Token) -> Option { - __token_to_integer(token, core::marker::PhantomData::<()>) + __token_to_integer(token, core::marker::PhantomData::<(&())>) } #[inline] @@ -168,7 +166,7 @@ mod __parse__Expr { } fn token_to_symbol(&self, token_index: usize, token: Self::Token) -> Self::Symbol { - __token_to_symbol(token_index, token, core::marker::PhantomData::<()>) + __token_to_symbol(token_index, token, core::marker::PhantomData::<(&())>) } fn expected_tokens(&self, state: i8) -> alloc::vec::Vec { @@ -196,11 +194,12 @@ mod __parse__Expr { symbols: &mut alloc::vec::Vec<__state_machine::SymbolTriple>, ) -> Option<__state_machine::ParseResult> { __reduce( + self.input, action, start_location, states, symbols, - core::marker::PhantomData::<()>, + core::marker::PhantomData::<(&())>, ) } @@ -209,120 +208,110 @@ mod __parse__Expr { } } fn __token_to_integer< + 'input, >( - __token: &lexer::Tok, - _: core::marker::PhantomData<()>, + __token: &Token<'input>, + _: core::marker::PhantomData<(&'input ())>, ) -> Option { match *__token { - lexer::Tok::LPren if true => Some(0), - lexer::Tok::RPren if true => Some(1), - lexer::Tok::Int(_) if true => Some(2), - lexer::Tok::Space if true => Some(3), + Token(2, _) if true => Some(0), + Token(3, _) if true => Some(1), + Token(0, _) if true => Some(2), + Token(1, _) if true => Some(3), _ => None, } } fn __token_to_symbol< + 'input, >( __token_index: usize, - __token: lexer::Tok, - _: core::marker::PhantomData<()>, - ) -> __Symbol<> + __token: Token<'input>, + _: core::marker::PhantomData<(&'input ())>, + ) -> __Symbol<'input> { match __token_index { - 0 | 1 | 3 => __Symbol::Variant0(__token), - 2 => match __token { - lexer::Tok::Int(__tok0) if true => __Symbol::Variant1(__tok0), + 0 | 1 | 2 | 3 => match __token { + Token(2, __tok0) | Token(3, __tok0) | Token(0, __tok0) | Token(1, __tok0) if true => __Symbol::Variant0(__tok0), _ => unreachable!(), }, _ => unreachable!(), } } pub struct ExprParser { + builder: __lalrpop_util::lexer::MatcherBuilder, _priv: (), } impl ExprParser { pub fn new() -> ExprParser { + let __builder = super::__intern_token::new_builder(); ExprParser { + builder: __builder, _priv: (), } } #[allow(dead_code)] pub fn parse< - __TOKEN: __ToTriple<>, - __TOKENS: IntoIterator, + 'input, >( &self, - __tokens0: __TOKENS, - ) -> Result> + input: &'input str, + ) -> Result, &'static str>> { - let __tokens = __tokens0.into_iter(); - let mut __tokens = __tokens.map(|t| __ToTriple::to_triple(t)); + let mut __tokens = self.builder.matcher(input); __state_machine::Parser::drive( __StateMachine { - __phantom: core::marker::PhantomData::<()>, + input, + __phantom: core::marker::PhantomData::<(&())>, }, __tokens, ) } } pub(crate) fn __reduce< + 'input, >( + input: &'input str, __action: i8, __lookahead_start: Option<&usize>, __states: &mut alloc::vec::Vec, - __symbols: &mut alloc::vec::Vec<(usize,__Symbol<>,usize)>, - _: core::marker::PhantomData<()>, - ) -> Option>> + __symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)>, + _: core::marker::PhantomData<(&'input ())>, + ) -> Option, &'static str>>> { let (__pop_states, __nonterminal) = match __action { 0 => { - __reduce0(__lookahead_start, __symbols, core::marker::PhantomData::<()>) + __reduce0(input, __lookahead_start, __symbols, core::marker::PhantomData::<(&())>) } 1 => { - __reduce1(__lookahead_start, __symbols, core::marker::PhantomData::<()>) + __reduce1(input, __lookahead_start, __symbols, core::marker::PhantomData::<(&())>) } 2 => { - __reduce2(__lookahead_start, __symbols, core::marker::PhantomData::<()>) + __reduce2(input, __lookahead_start, __symbols, core::marker::PhantomData::<(&())>) } 3 => { - __reduce3(__lookahead_start, __symbols, core::marker::PhantomData::<()>) + __reduce3(input, __lookahead_start, __symbols, core::marker::PhantomData::<(&())>) } 4 => { - __reduce4(__lookahead_start, __symbols, core::marker::PhantomData::<()>) + __reduce4(input, __lookahead_start, __symbols, core::marker::PhantomData::<(&())>) } 5 => { - __reduce5(__lookahead_start, __symbols, core::marker::PhantomData::<()>) + __reduce5(input, __lookahead_start, __symbols, core::marker::PhantomData::<(&())>) } 6 => { - __reduce6(__lookahead_start, __symbols, core::marker::PhantomData::<()>) + __reduce6(input, __lookahead_start, __symbols, core::marker::PhantomData::<(&())>) } 7 => { - __reduce7(__lookahead_start, __symbols, core::marker::PhantomData::<()>) + __reduce7(input, __lookahead_start, __symbols, core::marker::PhantomData::<(&())>) } 8 => { - __reduce8(__lookahead_start, __symbols, core::marker::PhantomData::<()>) - } - 9 => { - __reduce9(__lookahead_start, __symbols, core::marker::PhantomData::<()>) - } - 10 => { - __reduce10(__lookahead_start, __symbols, core::marker::PhantomData::<()>) - } - 11 => { - __reduce11(__lookahead_start, __symbols, core::marker::PhantomData::<()>) - } - 12 => { - __reduce12(__lookahead_start, __symbols, core::marker::PhantomData::<()>) - } - 13 => { // __Expr = Expr => ActionFn(0); - let __sym0 = __pop_Variant2(__symbols); + let __sym0 = __pop_Variant1(__symbols); let __start = __sym0.0.clone(); let __end = __sym0.2.clone(); - let __nt = super::__action0::<>(__sym0); + let __nt = super::__action0::<>(input, __sym0); return Some(Ok(__nt)); } _ => panic!("invalid action code {}", __action) @@ -338,60 +327,44 @@ mod __parse__Expr { fn __symbol_type_mismatch() -> ! { panic!("symbol type mismatch") } - fn __pop_Variant2< - >( - __symbols: &mut alloc::vec::Vec<(usize,__Symbol<>,usize)> - ) -> (usize, Expr, usize) - { - match __symbols.pop() { - Some((__l, __Symbol::Variant2(__v), __r)) => (__l, __v, __r), - _ => __symbol_type_mismatch() - } - } - fn __pop_Variant5< - >( - __symbols: &mut alloc::vec::Vec<(usize,__Symbol<>,usize)> - ) -> (usize, Vec, usize) - { - match __symbols.pop() { - Some((__l, __Symbol::Variant5(__v), __r)) => (__l, __v, __r), - _ => __symbol_type_mismatch() - } - } - fn __pop_Variant3< - >( - __symbols: &mut alloc::vec::Vec<(usize,__Symbol<>,usize)> - ) -> (usize, alloc::vec::Vec, usize) - { - match __symbols.pop() { - Some((__l, __Symbol::Variant3(__v), __r)) => (__l, __v, __r), - _ => __symbol_type_mismatch() - } - } - fn __pop_Variant4< - >( - __symbols: &mut alloc::vec::Vec<(usize,__Symbol<>,usize)> - ) -> (usize, core::option::Option, usize) - { - match __symbols.pop() { - Some((__l, __Symbol::Variant4(__v), __r)) => (__l, __v, __r), - _ => __symbol_type_mismatch() - } - } fn __pop_Variant1< + 'input, >( - __symbols: &mut alloc::vec::Vec<(usize,__Symbol<>,usize)> - ) -> (usize, i32, usize) + __symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)> + ) -> (usize, Expr, usize) { match __symbols.pop() { Some((__l, __Symbol::Variant1(__v), __r)) => (__l, __v, __r), _ => __symbol_type_mismatch() } } - fn __pop_Variant0< + fn __pop_Variant2< + 'input, >( - __symbols: &mut alloc::vec::Vec<(usize,__Symbol<>,usize)> - ) -> (usize, lexer::Tok, usize) + __symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)> + ) -> (usize, alloc::vec::Vec, usize) + { + match __symbols.pop() { + Some((__l, __Symbol::Variant2(__v), __r)) => (__l, __v, __r), + _ => __symbol_type_mismatch() + } + } + fn __pop_Variant3< + 'input, + >( + __symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)> + ) -> (usize, i32, usize) + { + match __symbols.pop() { + Some((__l, __Symbol::Variant3(__v), __r)) => (__l, __v, __r), + _ => __symbol_type_mismatch() + } + } + fn __pop_Variant0< + 'input, + >( + __symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)> + ) -> (usize, &'input str, usize) { match __symbols.pop() { Some((__l, __Symbol::Variant0(__v), __r)) => (__l, __v, __r), @@ -399,306 +372,256 @@ mod __parse__Expr { } } pub(crate) fn __reduce0< + 'input, >( + input: &'input str, __lookahead_start: Option<&usize>, - __symbols: &mut alloc::vec::Vec<(usize,__Symbol<>,usize)>, - _: core::marker::PhantomData<()>, + __symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)>, + _: core::marker::PhantomData<(&'input ())>, ) -> (usize, usize) { - // ( Space) = Expr, Space => ActionFn(8); - assert!(__symbols.len() >= 2); - let __sym1 = __pop_Variant0(__symbols); - let __sym0 = __pop_Variant2(__symbols); - let __start = __sym0.0.clone(); - let __end = __sym1.2.clone(); - let __nt = super::__action8::<>(__sym0, __sym1); - __symbols.push((__start, __Symbol::Variant2(__nt), __end)); - (2, 0) - } - pub(crate) fn __reduce1< - >( - __lookahead_start: Option<&usize>, - __symbols: &mut alloc::vec::Vec<(usize,__Symbol<>,usize)>, - _: core::marker::PhantomData<()>, - ) -> (usize, usize) - { - // ( Space)* = => ActionFn(6); - let __start = __lookahead_start.cloned().or_else(|| __symbols.last().map(|s| s.2.clone())).unwrap_or_default(); - let __end = __start.clone(); - let __nt = super::__action6::<>(&__start, &__end); - __symbols.push((__start, __Symbol::Variant3(__nt), __end)); - (0, 1) - } - pub(crate) fn __reduce2< - >( - __lookahead_start: Option<&usize>, - __symbols: &mut alloc::vec::Vec<(usize,__Symbol<>,usize)>, - _: core::marker::PhantomData<()>, - ) -> (usize, usize) - { - // ( Space)* = ( Space)+ => ActionFn(7); - let __sym0 = __pop_Variant3(__symbols); - let __start = __sym0.0.clone(); - let __end = __sym0.2.clone(); - let __nt = super::__action7::<>(__sym0); - __symbols.push((__start, __Symbol::Variant3(__nt), __end)); - (1, 1) - } - pub(crate) fn __reduce3< - >( - __lookahead_start: Option<&usize>, - __symbols: &mut alloc::vec::Vec<(usize,__Symbol<>,usize)>, - _: core::marker::PhantomData<()>, - ) -> (usize, usize) - { - // ( Space)+ = Expr, Space => ActionFn(11); - assert!(__symbols.len() >= 2); - let __sym1 = __pop_Variant0(__symbols); - let __sym0 = __pop_Variant2(__symbols); - let __start = __sym0.0.clone(); - let __end = __sym1.2.clone(); - let __nt = super::__action11::<>(__sym0, __sym1); - __symbols.push((__start, __Symbol::Variant3(__nt), __end)); - (2, 2) - } - pub(crate) fn __reduce4< - >( - __lookahead_start: Option<&usize>, - __symbols: &mut alloc::vec::Vec<(usize,__Symbol<>,usize)>, - _: core::marker::PhantomData<()>, - ) -> (usize, usize) - { - // ( Space)+ = ( Space)+, Expr, Space => ActionFn(12); - assert!(__symbols.len() >= 3); - let __sym2 = __pop_Variant0(__symbols); - let __sym1 = __pop_Variant2(__symbols); - let __sym0 = __pop_Variant3(__symbols); - let __start = __sym0.0.clone(); - let __end = __sym2.2.clone(); - let __nt = super::__action12::<>(__sym0, __sym1, __sym2); - __symbols.push((__start, __Symbol::Variant3(__nt), __end)); - (3, 2) - } - pub(crate) fn __reduce5< - >( - __lookahead_start: Option<&usize>, - __symbols: &mut alloc::vec::Vec<(usize,__Symbol<>,usize)>, - _: core::marker::PhantomData<()>, - ) -> (usize, usize) - { - // Expr = "(", Sep, ")" => ActionFn(1); - assert!(__symbols.len() >= 3); - let __sym2 = __pop_Variant0(__symbols); - let __sym1 = __pop_Variant5(__symbols); - let __sym0 = __pop_Variant0(__symbols); - let __start = __sym0.0.clone(); - let __end = __sym2.2.clone(); - let __nt = super::__action1::<>(__sym0, __sym1, __sym2); - __symbols.push((__start, __Symbol::Variant2(__nt), __end)); - (3, 3) - } - pub(crate) fn __reduce6< - >( - __lookahead_start: Option<&usize>, - __symbols: &mut alloc::vec::Vec<(usize,__Symbol<>,usize)>, - _: core::marker::PhantomData<()>, - ) -> (usize, usize) - { - // Expr = Int => ActionFn(2); + // () = Expr => ActionFn(8); let __sym0 = __pop_Variant1(__symbols); let __start = __sym0.0.clone(); let __end = __sym0.2.clone(); - let __nt = super::__action2::<>(__sym0); + let __nt = super::__action8::<>(input, __sym0); + __symbols.push((__start, __Symbol::Variant1(__nt), __end)); + (1, 0) + } + pub(crate) fn __reduce1< + 'input, + >( + input: &'input str, + __lookahead_start: Option<&usize>, + __symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)>, + _: core::marker::PhantomData<(&'input ())>, + ) -> (usize, usize) + { + // ()+ = Expr => ActionFn(9); + let __sym0 = __pop_Variant1(__symbols); + let __start = __sym0.0.clone(); + let __end = __sym0.2.clone(); + let __nt = super::__action9::<>(input, __sym0); __symbols.push((__start, __Symbol::Variant2(__nt), __end)); + (1, 1) + } + pub(crate) fn __reduce2< + 'input, + >( + input: &'input str, + __lookahead_start: Option<&usize>, + __symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)>, + _: core::marker::PhantomData<(&'input ())>, + ) -> (usize, usize) + { + // ()+ = ()+, Expr => ActionFn(10); + assert!(__symbols.len() >= 2); + let __sym1 = __pop_Variant1(__symbols); + let __sym0 = __pop_Variant2(__symbols); + let __start = __sym0.0.clone(); + let __end = __sym1.2.clone(); + let __nt = super::__action10::<>(input, __sym0, __sym1); + __symbols.push((__start, __Symbol::Variant2(__nt), __end)); + (2, 1) + } + pub(crate) fn __reduce3< + 'input, + >( + input: &'input str, + __lookahead_start: Option<&usize>, + __symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)>, + _: core::marker::PhantomData<(&'input ())>, + ) -> (usize, usize) + { + // Expr = "(", ()+, ")" => ActionFn(1); + assert!(__symbols.len() >= 3); + let __sym2 = __pop_Variant0(__symbols); + let __sym1 = __pop_Variant2(__symbols); + let __sym0 = __pop_Variant0(__symbols); + let __start = __sym0.0.clone(); + let __end = __sym2.2.clone(); + let __nt = super::__action1::<>(input, __sym0, __sym1, __sym2); + __symbols.push((__start, __Symbol::Variant1(__nt), __end)); + (3, 2) + } + pub(crate) fn __reduce4< + 'input, + >( + input: &'input str, + __lookahead_start: Option<&usize>, + __symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)>, + _: core::marker::PhantomData<(&'input ())>, + ) -> (usize, usize) + { + // Expr = Keyword => ActionFn(2); + let __sym0 = __pop_Variant1(__symbols); + let __start = __sym0.0.clone(); + let __end = __sym0.2.clone(); + let __nt = super::__action2::<>(input, __sym0); + __symbols.push((__start, __Symbol::Variant1(__nt), __end)); + (1, 2) + } + pub(crate) fn __reduce5< + 'input, + >( + input: &'input str, + __lookahead_start: Option<&usize>, + __symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)>, + _: core::marker::PhantomData<(&'input ())>, + ) -> (usize, usize) + { + // Expr = Num => ActionFn(3); + let __sym0 = __pop_Variant3(__symbols); + let __start = __sym0.0.clone(); + let __end = __sym0.2.clone(); + let __nt = super::__action3::<>(input, __sym0); + __symbols.push((__start, __Symbol::Variant1(__nt), __end)); + (1, 2) + } + pub(crate) fn __reduce6< + 'input, + >( + input: &'input str, + __lookahead_start: Option<&usize>, + __symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)>, + _: core::marker::PhantomData<(&'input ())>, + ) -> (usize, usize) + { + // Keyword = r#":[^\\s]+"# => ActionFn(4); + let __sym0 = __pop_Variant0(__symbols); + let __start = __sym0.0.clone(); + let __end = __sym0.2.clone(); + let __nt = super::__action4::<>(input, __sym0); + __symbols.push((__start, __Symbol::Variant1(__nt), __end)); (1, 3) } pub(crate) fn __reduce7< + 'input, >( + input: &'input str, __lookahead_start: Option<&usize>, - __symbols: &mut alloc::vec::Vec<(usize,__Symbol<>,usize)>, - _: core::marker::PhantomData<()>, + __symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)>, + _: core::marker::PhantomData<(&'input ())>, ) -> (usize, usize) { - // Expr? = Expr => ActionFn(4); - let __sym0 = __pop_Variant2(__symbols); + // Num = r#"[0-9]+"# => ActionFn(5); + let __sym0 = __pop_Variant0(__symbols); let __start = __sym0.0.clone(); let __end = __sym0.2.clone(); - let __nt = super::__action4::<>(__sym0); - __symbols.push((__start, __Symbol::Variant4(__nt), __end)); + let __nt = super::__action5::<>(input, __sym0); + __symbols.push((__start, __Symbol::Variant3(__nt), __end)); (1, 4) } - pub(crate) fn __reduce8< - >( - __lookahead_start: Option<&usize>, - __symbols: &mut alloc::vec::Vec<(usize,__Symbol<>,usize)>, - _: core::marker::PhantomData<()>, - ) -> (usize, usize) - { - // Expr? = => ActionFn(5); - let __start = __lookahead_start.cloned().or_else(|| __symbols.last().map(|s| s.2.clone())).unwrap_or_default(); - let __end = __start.clone(); - let __nt = super::__action5::<>(&__start, &__end); - __symbols.push((__start, __Symbol::Variant4(__nt), __end)); - (0, 4) - } - pub(crate) fn __reduce9< - >( - __lookahead_start: Option<&usize>, - __symbols: &mut alloc::vec::Vec<(usize,__Symbol<>,usize)>, - _: core::marker::PhantomData<()>, - ) -> (usize, usize) - { - // Sep = Expr => ActionFn(15); - let __sym0 = __pop_Variant2(__symbols); - let __start = __sym0.0.clone(); - let __end = __sym0.2.clone(); - let __nt = super::__action15::<>(__sym0); - __symbols.push((__start, __Symbol::Variant5(__nt), __end)); - (1, 5) - } - pub(crate) fn __reduce10< - >( - __lookahead_start: Option<&usize>, - __symbols: &mut alloc::vec::Vec<(usize,__Symbol<>,usize)>, - _: core::marker::PhantomData<()>, - ) -> (usize, usize) - { - // Sep = => ActionFn(16); - let __start = __lookahead_start.cloned().or_else(|| __symbols.last().map(|s| s.2.clone())).unwrap_or_default(); - let __end = __start.clone(); - let __nt = super::__action16::<>(&__start, &__end); - __symbols.push((__start, __Symbol::Variant5(__nt), __end)); - (0, 5) - } - pub(crate) fn __reduce11< - >( - __lookahead_start: Option<&usize>, - __symbols: &mut alloc::vec::Vec<(usize,__Symbol<>,usize)>, - _: core::marker::PhantomData<()>, - ) -> (usize, usize) - { - // Sep = ( Space)+, Expr => ActionFn(17); - assert!(__symbols.len() >= 2); - let __sym1 = __pop_Variant2(__symbols); - let __sym0 = __pop_Variant3(__symbols); - let __start = __sym0.0.clone(); - let __end = __sym1.2.clone(); - let __nt = super::__action17::<>(__sym0, __sym1); - __symbols.push((__start, __Symbol::Variant5(__nt), __end)); - (2, 5) - } - pub(crate) fn __reduce12< - >( - __lookahead_start: Option<&usize>, - __symbols: &mut alloc::vec::Vec<(usize,__Symbol<>,usize)>, - _: core::marker::PhantomData<()>, - ) -> (usize, usize) - { - // Sep = ( Space)+ => ActionFn(18); - let __sym0 = __pop_Variant3(__symbols); - let __start = __sym0.0.clone(); - let __end = __sym0.2.clone(); - let __nt = super::__action18::<>(__sym0); - __symbols.push((__start, __Symbol::Variant5(__nt), __end)); - (1, 5) - } } pub use self::__parse__Expr::ExprParser; +#[cfg_attr(rustfmt, rustfmt_skip)] +mod __intern_token { + #![allow(unused_imports)] + use std::str::FromStr; + use crate::Expr; + #[allow(unused_extern_crates)] + extern crate lalrpop_util as __lalrpop_util; + #[allow(unused_imports)] + use self::__lalrpop_util::state_machine as __state_machine; + extern crate core; + extern crate alloc; + pub fn new_builder() -> __lalrpop_util::lexer::MatcherBuilder { + let __strs: &[(&str, bool)] = &[ + ("^(:[\u{0}-\u{8}\u{e}-\u{1f}!-\u{84}\u{86}-\u{9f}¡-ᙿᚁ-\u{1fff}\u{200b}-‧\u{202a}-\u{202e}‰-⁞\u{2060}-\u{2fff}、-\u{10ffff}]+)", false), + ("^([0-9]+)", false), + ("^(\\()", false), + ("^(\\))", false), + (r"^(\s*)", true), + ]; + __lalrpop_util::lexer::MatcherBuilder::new(__strs.iter().copied()).unwrap() + } +} +pub(crate) use self::__lalrpop_util::lexer::Token; +#[allow(unused_variables)] fn __action0< + 'input, >( + input: &'input str, (_, __0, _): (usize, Expr, usize), ) -> Expr { __0 } +#[allow(unused_variables)] fn __action1< + 'input, >( - (_, _, _): (usize, lexer::Tok, usize), - (_, elems, _): (usize, Vec, usize), - (_, _, _): (usize, lexer::Tok, usize), + input: &'input str, + (_, _, _): (usize, &'input str, usize), + (_, elems, _): (usize, alloc::vec::Vec, usize), + (_, _, _): (usize, &'input str, usize), ) -> Expr { Expr::List(elems) } +#[allow(unused_variables)] fn __action2< + 'input, >( - (_, n, _): (usize, i32, usize), + input: &'input str, + (_, x, _): (usize, Expr, usize), ) -> Expr { - Expr::Number(n) + x } +#[allow(unused_variables)] fn __action3< + 'input, >( - (_, mut v, _): (usize, alloc::vec::Vec, usize), - (_, e, _): (usize, core::option::Option, usize), -) -> Vec -{ - match e { - None => v, - Some(e) => { - v.push(e); - v - } - } -} - -fn __action4< ->( - (_, __0, _): (usize, Expr, usize), -) -> core::option::Option -{ - Some(__0) -} - -fn __action5< ->( - __lookbehind: &usize, - __lookahead: &usize, -) -> core::option::Option -{ - None -} - -fn __action6< ->( - __lookbehind: &usize, - __lookahead: &usize, -) -> alloc::vec::Vec -{ - alloc::vec![] -} - -fn __action7< ->( - (_, v, _): (usize, alloc::vec::Vec, usize), -) -> alloc::vec::Vec -{ - v -} - -fn __action8< ->( - (_, __0, _): (usize, Expr, usize), - (_, _, _): (usize, lexer::Tok, usize), + input: &'input str, + (_, x, _): (usize, i32, usize), ) -> Expr { - __0 + Expr::Number(x) } -fn __action9< +#[allow(unused_variables)] +fn __action4< + 'input, >( + input: &'input str, + (_, __0, _): (usize, &'input str, usize), +) -> Expr +{ + Expr::Keyword(__0.to_string()) +} + +#[allow(unused_variables)] +fn __action5< + 'input, +>( + input: &'input str, + (_, __0, _): (usize, &'input str, usize), +) -> i32 +{ + i32::from_str(__0).unwrap() +} + +#[allow(unused_variables)] +fn __action6< + 'input, +>( + input: &'input str, (_, __0, _): (usize, Expr, usize), ) -> alloc::vec::Vec { alloc::vec![__0] } -fn __action10< +#[allow(unused_variables)] +fn __action7< + 'input, >( + input: &'input str, (_, v, _): (usize, alloc::vec::Vec, usize), (_, e, _): (usize, Expr, usize), ) -> alloc::vec::Vec @@ -706,161 +629,72 @@ fn __action10< { let mut v = v; v.push(e); v } } -fn __action11< +#[allow(unused_variables)] +fn __action8< + 'input, >( + input: &'input str, + (_, __0, _): (usize, Expr, usize), +) -> Expr +{ + __0 +} + +#[allow(unused_variables)] +fn __action9< + 'input, +>( + input: &'input str, __0: (usize, Expr, usize), - __1: (usize, lexer::Tok, usize), ) -> alloc::vec::Vec { let __start0 = __0.0.clone(); - let __end0 = __1.2.clone(); + let __end0 = __0.2.clone(); let __temp0 = __action8( + input, __0, - __1, ); let __temp0 = (__start0, __temp0, __end0); - __action9( + __action6( + input, __temp0, ) } -fn __action12< +#[allow(unused_variables)] +fn __action10< + 'input, >( + input: &'input str, __0: (usize, alloc::vec::Vec, usize), __1: (usize, Expr, usize), - __2: (usize, lexer::Tok, usize), ) -> alloc::vec::Vec -{ - let __start0 = __1.0.clone(); - let __end0 = __2.2.clone(); - let __temp0 = __action8( - __1, - __2, - ); - let __temp0 = (__start0, __temp0, __end0); - __action10( - __0, - __temp0, - ) -} - -fn __action13< ->( - __0: (usize, core::option::Option, usize), -) -> Vec -{ - let __start0 = __0.0.clone(); - let __end0 = __0.0.clone(); - let __temp0 = __action6( - &__start0, - &__end0, - ); - let __temp0 = (__start0, __temp0, __end0); - __action3( - __temp0, - __0, - ) -} - -fn __action14< ->( - __0: (usize, alloc::vec::Vec, usize), - __1: (usize, core::option::Option, usize), -) -> Vec -{ - let __start0 = __0.0.clone(); - let __end0 = __0.2.clone(); - let __temp0 = __action7( - __0, - ); - let __temp0 = (__start0, __temp0, __end0); - __action3( - __temp0, - __1, - ) -} - -fn __action15< ->( - __0: (usize, Expr, usize), -) -> Vec -{ - let __start0 = __0.0.clone(); - let __end0 = __0.2.clone(); - let __temp0 = __action4( - __0, - ); - let __temp0 = (__start0, __temp0, __end0); - __action13( - __temp0, - ) -} - -fn __action16< ->( - __lookbehind: &usize, - __lookahead: &usize, -) -> Vec -{ - let __start0 = __lookbehind.clone(); - let __end0 = __lookahead.clone(); - let __temp0 = __action5( - &__start0, - &__end0, - ); - let __temp0 = (__start0, __temp0, __end0); - __action13( - __temp0, - ) -} - -fn __action17< ->( - __0: (usize, alloc::vec::Vec, usize), - __1: (usize, Expr, usize), -) -> Vec { let __start0 = __1.0.clone(); let __end0 = __1.2.clone(); - let __temp0 = __action4( + let __temp0 = __action8( + input, __1, ); let __temp0 = (__start0, __temp0, __end0); - __action14( + __action7( + input, __0, __temp0, ) } -fn __action18< ->( - __0: (usize, alloc::vec::Vec, usize), -) -> Vec -{ - let __start0 = __0.2.clone(); - let __end0 = __0.2.clone(); - let __temp0 = __action5( - &__start0, - &__end0, - ); - let __temp0 = (__start0, __temp0, __end0); - __action14( - __0, - __temp0, - ) +pub trait __ToTriple<'input, > { + fn to_triple(value: Self) -> Result<(usize,Token<'input>,usize), __lalrpop_util::ParseError, &'static str>>; } -pub trait __ToTriple<> { - fn to_triple(value: Self) -> Result<(usize,lexer::Tok,usize), __lalrpop_util::ParseError>; -} - -impl<> __ToTriple<> for (usize, lexer::Tok, usize) { - fn to_triple(value: Self) -> Result<(usize,lexer::Tok,usize), __lalrpop_util::ParseError> { +impl<'input, > __ToTriple<'input, > for (usize, Token<'input>, usize) { + fn to_triple(value: Self) -> Result<(usize,Token<'input>,usize), __lalrpop_util::ParseError, &'static str>> { Ok(value) } } -impl<> __ToTriple<> for Result<(usize, lexer::Tok, usize), lexer::LexicalError> { - fn to_triple(value: Self) -> Result<(usize,lexer::Tok,usize), __lalrpop_util::ParseError> { +impl<'input, > __ToTriple<'input, > for Result<(usize, Token<'input>, usize), &'static str> { + fn to_triple(value: Self) -> Result<(usize,Token<'input>,usize), __lalrpop_util::ParseError, &'static str>> { match value { Ok(v) => Ok(v), Err(error) => Err(__lalrpop_util::ParseError::User { error }), diff --git a/src/main.rs b/src/main.rs index 7c14565..99bbd5d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,16 +1,50 @@ #![allow(unused_imports)] +use itertools::Itertools; + use lalrpop_util::lalrpop_mod; -mod lexer; +//mod lexer; lalrpop_mod!(pub calc); +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub struct Sp(pub usize, pub T, pub usize); + +impl std::fmt::Display for Sp { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "<{}- {} -{}>", self.0, self.1, self.2) + } +} + #[derive(Debug)] pub enum Expr { - List(Vec), + List(Vec>), + Table(Vec<(Sp, Sp)>), + Keyword(String), Symbol(String), + Str(String), Number(i32), + Comment, +} + +impl std::fmt::Display for Expr { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use Expr::*; + match self { + Number(x) => write!(f, "{}", x), + List(x) => write!(f, "({})", x.iter().map(|e| format!("{}", e)).join(" ")), + Table(x) => write!( + f, + "{{{}}}", + x.iter().map(|(k, v)| format!("{} {}", k, v)).join(" ") + ), + Keyword(x) => write!(f, "{}", x), + Symbol(x) => write!(f, "{}", x), + Str(x) => write!(f, "{}", x), + Comment => write!(f, ""), + } + } } fn main() {} @@ -19,22 +53,38 @@ fn main() {} macro_rules! test_p { ($e:expr) => { let e = $e; - let lex = crate::lexer::Lexer::new(e); let p = calc::ExprParser::new(); - match p.parse(lex) { - Ok(res) => println!("{:?}", res), - Err(e) => eprintln!("{:?}", e), + match p.parse(e) { + Ok(res) => println!("{}\n=> {}\n", e, res), + Err(e) => eprintln!("{}", e), } }; } #[test] fn calc() { - //assert!(calc::ExprParser::new().parse("(1 2 3)").is_ok()); - test_p!("1"); test_p!("(12)"); test_p!("(1 2)"); + test_p!("(1 :foo 1)"); + test_p!("(:foo 1)"); + test_p!("(:foo->: 1)"); + test_p!("(foo 1)"); + test_p!("(lol😄 1)"); + + test_p!(r#"(test "hi")"#); + test_p!(r#"(test "h\"i")"#); + test_p!(r#"(test " hi ")"#); + + test_p!("(+ (1 2 (* 2 5)))"); + + test_p!(r#"{:key value 12 "hi" (test) (1 2 3)}"#); + + test_p!(r#"; test"#); + test_p!( + r#"(f arg ; test + arg2)"# + ); println!("\n\n\n\n\n\n"); From 0beadb1108922d039a2d68da5d12bb2ec1cdea52 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Sat, 15 May 2021 09:51:52 +0200 Subject: [PATCH 003/137] lexer that don't work --- Cargo.lock | 72 +++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/calc.lalrpop | 83 +++++++++++++++++++++++++++++------------ src/lexer2.rs | 97 ++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 9 ++++- 5 files changed, 236 insertions(+), 26 deletions(-) create mode 100644 src/lexer2.rs diff --git a/Cargo.lock b/Cargo.lock index 7806c6c..6c82464 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -53,6 +53,12 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +[[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" @@ -152,6 +158,12 @@ 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.1.16" @@ -250,6 +262,30 @@ 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 = "memchr" version = "2.4.0" @@ -269,6 +305,7 @@ dependencies = [ "itertools", "lalrpop", "lalrpop-util", + "logos", "regex", ] @@ -303,6 +340,24 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" +[[package]] +name = "proc-macro2" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" +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.1.57" @@ -367,6 +422,17 @@ dependencies = [ "precomputed-hash", ] +[[package]] +name = "syn" +version = "1.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1e8cdbefb79a9a5a65e0db8b47b723ee907b7c7f8496c76a1770b5c310bab82" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + [[package]] name = "term" version = "0.5.2" @@ -393,6 +459,12 @@ 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 = "wasi" version = "0.9.0+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index d938bfb..325467a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ build = "build.rs" lalrpop-util = "0.19.5" regex = "1" itertools = "0.10" +logos = "0.12" [build-dependencies] lalrpop = "0.19.5" diff --git a/src/calc.lalrpop b/src/calc.lalrpop index 48405fe..6e998e6 100644 --- a/src/calc.lalrpop +++ b/src/calc.lalrpop @@ -2,40 +2,75 @@ use std::str::FromStr; //use crate::lexer; use crate::Expr; use crate::Sp; +use crate::lexer2 as lexer; +use logos; grammar; +extern { + type Location = usize; + type Error = lexer::LexicalError; + + enum lexer::Token { + "(" => lexer::Token::LPren, + ")" => lexer::Token::RPren, + "{" => lexer::Token::LCurl, + "}" => lexer::Token::RCurl, + Comment => lexer::Token::Comment, + Symbol => lexer::Token::Symbol(), + StringLit => lexer::Token::StringLit(), + Int => lexer::Token::Int(), + Keyword => lexer::Token::Keyword(), + } +} Span: Sp = { <@L> <@R> => Sp(<>) -}; - -pub Expr: Expr = { - "(" )>>)+> ")" => Expr::List(elems), - - "{" )>> )>>)*> "}" => Expr::Table(elems), - - => x, - => x, - => Expr::Str(x), - => Expr::Number(x), - Comment => Expr::Comment, -}; - -Keyword: Expr = => Expr::Keyword(<>.to_string()); -Symbol: Expr = /.*-+][^\s{}\(\)]*"> => Expr::Symbol(<>.to_string()); - -StrLit: String = { - r#""(?:[^"\\]|\\.)*""# => { - let val = <>; - val[1..val.len() - 1].to_owned() - }, } -Comment: () = r";[^\n\r]*"; +pub Expr: Expr = { + "(" )>)+> ")" => Expr::List(xs), + "{" )> Span<()>)+> "}" => Expr::Table(xs), + => Expr::Number(x), + => Expr::Str(x), + => Expr::Keyword(x), + => Expr::Symbol(x), + Comment => Expr::Comment, +} -Num: i32 = => i32::from_str(<>).unwrap(); + + +//Span: Sp = { +// <@L> <@R> => Sp(<>) +//}; +// +//pub Expr: Expr = { +// "(" )>>)+> ")" => Expr::List(elems), +// +// "{" )>> )>>)*> "}" => Expr::Table(elems), +// +// => x, +// => x, +// => Expr::Str(x), +// => Expr::Number(x), +// Comment => Expr::Comment, +//}; +// +//Keyword: Expr = => Expr::Keyword(<>.to_string()); +//Symbol: Expr = /.*-+][^\s{}\(\)]*"> => Expr::Symbol(<>.to_string()); +// +//StrLit: String = { +// r#""(?:[^"\\]|\\.)*""# => { +// let val = <>; +// val[1..val.len() - 1].to_owned() +// }, +//} +// +//Comment: () = r";[^\n\r]*"; +// +// +//Num: i32 = => i32::from_str(<>).unwrap(); diff --git a/src/lexer2.rs b/src/lexer2.rs new file mode 100644 index 0000000..2ff2252 --- /dev/null +++ b/src/lexer2.rs @@ -0,0 +1,97 @@ +use logos::{Lexer, Logos, SpannedIter}; + +#[derive(Debug, Eq, Clone, Copy, PartialEq)] +pub struct LexicalError; + +impl std::fmt::Display for LexicalError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Error") + } +} + +pub struct TokenStream<'inp> { + stream: SpannedIter<'inp, Token>, +} + +impl<'inp> TokenStream<'inp> { + pub fn new(s: &'inp str) -> Self { + TokenStream { + stream: Token::lexer(s).spanned(), + } + } +} + +impl<'inp> Iterator for TokenStream<'inp> { + type Item = Result<(usize, Token, usize), LexicalError>; + + fn next(&mut self) -> Option { + self.stream + .next() + .map(|(t, range)| Ok((range.start, t, range.end))) + } +} + +#[derive(Logos, Debug, PartialEq, Clone)] +pub enum Token { + #[token("(")] + LPren, + #[token(")")] + RPren, + #[token("{")] + LCurl, + #[token("}")] + RCurl, + #[regex(r#";[^\r\n]*"#)] + Comment, + + #[regex( + r"[+-]\d*[^\s{}\(\)\d]+|[a-zA-Z_!\?<>/.*][^\s{}\(\)]*", + |lex| lex.slice().parse() + )] + Symbol(String), + + #[regex(r#""(?:[^"\\]|\\.)*""#, parse_stringlit)] + StringLit(String), + + #[regex(r"[-+]?\d+", |lex| lex.slice().parse())] + Int(i32), + + #[regex(r#":[^\s{}\(\)]+"#, |lex| lex.slice().to_string())] + Keyword(String), + + //#[regex(r"\s+")] + //Space, + #[regex(r"[\t\n\f\s]+")] + #[error] + Error, +} + +impl std::fmt::Display for Token { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Token::LPren => write!(f, "("), + Token::RPren => write!(f, ")"), + Token::LCurl => write!(f, "{{"), + Token::RCurl => write!(f, "}}"), + Token::Comment => write!(f, ""), + Token::Symbol(x) => write!(f, "{}", x), + Token::StringLit(x) => write!(f, "\"{}\"", x), + Token::Int(x) => write!(f, "{}", x), + Token::Keyword(x) => write!(f, "{}", x), + Token::Error => write!(f, "IT GIB ERROR"), + } + } +} + +fn parse_stringlit(lex: &mut Lexer) -> Option { + let s = lex.slice(); + Some(s[1..(s.len() - 1)].to_string()) +} + +//#[test] +//fn test() { +//let toks: Vec<_> = Token::lexer("(+ 1)").spanned().collect(); +//dbg!(toks); + +//panic!(); +//} diff --git a/src/main.rs b/src/main.rs index 99bbd5d..d493c4f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,12 +3,14 @@ use itertools::Itertools; use lalrpop_util::lalrpop_mod; +use logos::{Lexer, Logos}; +mod lexer2; //mod lexer; lalrpop_mod!(pub calc); -#[derive(Debug, Eq, PartialEq, Clone, Copy)] +#[derive(Debug, Eq, PartialEq, Clone)] pub struct Sp(pub usize, pub T, pub usize); impl std::fmt::Display for Sp { @@ -53,8 +55,9 @@ fn main() {} macro_rules! test_p { ($e:expr) => { let e = $e; + let lex = lexer2::TokenStream::new(e); let p = calc::ExprParser::new(); - match p.parse(e) { + match p.parse(lex) { Ok(res) => println!("{}\n=> {}\n", e, res), Err(e) => eprintln!("{}", e), } @@ -76,6 +79,8 @@ fn calc() { test_p!(r#"(test "h\"i")"#); test_p!(r#"(test " hi ")"#); + test_p!(r#"(+)"#); + test_p!("(+ (1 2 (* 2 5)))"); test_p!(r#"{:key value 12 "hi" (test) (1 2 3)}"#); From 2515141eeb7bac4e7f5540920f2ea237fab28b85 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Sat, 15 May 2021 10:14:43 +0200 Subject: [PATCH 004/137] Revert "lexer that don't work" This reverts commit 0beadb1108922d039a2d68da5d12bb2ec1cdea52. --- Cargo.lock | 72 ----------------------------------- Cargo.toml | 1 - src/calc.lalrpop | 77 +++++++++++--------------------------- src/lexer2.rs | 97 ------------------------------------------------ src/main.rs | 9 +---- 5 files changed, 23 insertions(+), 233 deletions(-) delete mode 100644 src/lexer2.rs diff --git a/Cargo.lock b/Cargo.lock index 6c82464..7806c6c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -53,12 +53,6 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" -[[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" @@ -158,12 +152,6 @@ 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.1.16" @@ -262,30 +250,6 @@ 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 = "memchr" version = "2.4.0" @@ -305,7 +269,6 @@ dependencies = [ "itertools", "lalrpop", "lalrpop-util", - "logos", "regex", ] @@ -340,24 +303,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" -[[package]] -name = "proc-macro2" -version = "1.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" -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.1.57" @@ -422,17 +367,6 @@ dependencies = [ "precomputed-hash", ] -[[package]] -name = "syn" -version = "1.0.72" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1e8cdbefb79a9a5a65e0db8b47b723ee907b7c7f8496c76a1770b5c310bab82" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - [[package]] name = "term" version = "0.5.2" @@ -459,12 +393,6 @@ 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 = "wasi" version = "0.9.0+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index 325467a..d938bfb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,6 @@ build = "build.rs" lalrpop-util = "0.19.5" regex = "1" itertools = "0.10" -logos = "0.12" [build-dependencies] lalrpop = "0.19.5" diff --git a/src/calc.lalrpop b/src/calc.lalrpop index 6e998e6..48405fe 100644 --- a/src/calc.lalrpop +++ b/src/calc.lalrpop @@ -2,75 +2,40 @@ use std::str::FromStr; //use crate::lexer; use crate::Expr; use crate::Sp; -use crate::lexer2 as lexer; -use logos; grammar; -extern { - type Location = usize; - type Error = lexer::LexicalError; - - enum lexer::Token { - "(" => lexer::Token::LPren, - ")" => lexer::Token::RPren, - "{" => lexer::Token::LCurl, - "}" => lexer::Token::RCurl, - Comment => lexer::Token::Comment, - Symbol => lexer::Token::Symbol(), - StringLit => lexer::Token::StringLit(), - Int => lexer::Token::Int(), - Keyword => lexer::Token::Keyword(), - } -} Span: Sp = { <@L> <@R> => Sp(<>) -} +}; pub Expr: Expr = { - "(" )>)+> ")" => Expr::List(xs), - "{" )> Span<()>)+> "}" => Expr::Table(xs), - => Expr::Number(x), - => Expr::Str(x), - => Expr::Keyword(x), - => Expr::Symbol(x), + "(" )>>)+> ")" => Expr::List(elems), + + "{" )>> )>>)*> "}" => Expr::Table(elems), + + => x, + => x, + => Expr::Str(x), + => Expr::Number(x), Comment => Expr::Comment, +}; + +Keyword: Expr = => Expr::Keyword(<>.to_string()); +Symbol: Expr = /.*-+][^\s{}\(\)]*"> => Expr::Symbol(<>.to_string()); + +StrLit: String = { + r#""(?:[^"\\]|\\.)*""# => { + let val = <>; + val[1..val.len() - 1].to_owned() + }, } +Comment: () = r";[^\n\r]*"; - -//Span: Sp = { -// <@L> <@R> => Sp(<>) -//}; -// -//pub Expr: Expr = { -// "(" )>>)+> ")" => Expr::List(elems), -// -// "{" )>> )>>)*> "}" => Expr::Table(elems), -// -// => x, -// => x, -// => Expr::Str(x), -// => Expr::Number(x), -// Comment => Expr::Comment, -//}; -// -//Keyword: Expr = => Expr::Keyword(<>.to_string()); -//Symbol: Expr = /.*-+][^\s{}\(\)]*"> => Expr::Symbol(<>.to_string()); -// -//StrLit: String = { -// r#""(?:[^"\\]|\\.)*""# => { -// let val = <>; -// val[1..val.len() - 1].to_owned() -// }, -//} -// -//Comment: () = r";[^\n\r]*"; -// -// -//Num: i32 = => i32::from_str(<>).unwrap(); +Num: i32 = => i32::from_str(<>).unwrap(); diff --git a/src/lexer2.rs b/src/lexer2.rs deleted file mode 100644 index 2ff2252..0000000 --- a/src/lexer2.rs +++ /dev/null @@ -1,97 +0,0 @@ -use logos::{Lexer, Logos, SpannedIter}; - -#[derive(Debug, Eq, Clone, Copy, PartialEq)] -pub struct LexicalError; - -impl std::fmt::Display for LexicalError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Error") - } -} - -pub struct TokenStream<'inp> { - stream: SpannedIter<'inp, Token>, -} - -impl<'inp> TokenStream<'inp> { - pub fn new(s: &'inp str) -> Self { - TokenStream { - stream: Token::lexer(s).spanned(), - } - } -} - -impl<'inp> Iterator for TokenStream<'inp> { - type Item = Result<(usize, Token, usize), LexicalError>; - - fn next(&mut self) -> Option { - self.stream - .next() - .map(|(t, range)| Ok((range.start, t, range.end))) - } -} - -#[derive(Logos, Debug, PartialEq, Clone)] -pub enum Token { - #[token("(")] - LPren, - #[token(")")] - RPren, - #[token("{")] - LCurl, - #[token("}")] - RCurl, - #[regex(r#";[^\r\n]*"#)] - Comment, - - #[regex( - r"[+-]\d*[^\s{}\(\)\d]+|[a-zA-Z_!\?<>/.*][^\s{}\(\)]*", - |lex| lex.slice().parse() - )] - Symbol(String), - - #[regex(r#""(?:[^"\\]|\\.)*""#, parse_stringlit)] - StringLit(String), - - #[regex(r"[-+]?\d+", |lex| lex.slice().parse())] - Int(i32), - - #[regex(r#":[^\s{}\(\)]+"#, |lex| lex.slice().to_string())] - Keyword(String), - - //#[regex(r"\s+")] - //Space, - #[regex(r"[\t\n\f\s]+")] - #[error] - Error, -} - -impl std::fmt::Display for Token { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Token::LPren => write!(f, "("), - Token::RPren => write!(f, ")"), - Token::LCurl => write!(f, "{{"), - Token::RCurl => write!(f, "}}"), - Token::Comment => write!(f, ""), - Token::Symbol(x) => write!(f, "{}", x), - Token::StringLit(x) => write!(f, "\"{}\"", x), - Token::Int(x) => write!(f, "{}", x), - Token::Keyword(x) => write!(f, "{}", x), - Token::Error => write!(f, "IT GIB ERROR"), - } - } -} - -fn parse_stringlit(lex: &mut Lexer) -> Option { - let s = lex.slice(); - Some(s[1..(s.len() - 1)].to_string()) -} - -//#[test] -//fn test() { -//let toks: Vec<_> = Token::lexer("(+ 1)").spanned().collect(); -//dbg!(toks); - -//panic!(); -//} diff --git a/src/main.rs b/src/main.rs index d493c4f..99bbd5d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,14 +3,12 @@ use itertools::Itertools; use lalrpop_util::lalrpop_mod; -use logos::{Lexer, Logos}; -mod lexer2; //mod lexer; lalrpop_mod!(pub calc); -#[derive(Debug, Eq, PartialEq, Clone)] +#[derive(Debug, Eq, PartialEq, Clone, Copy)] pub struct Sp(pub usize, pub T, pub usize); impl std::fmt::Display for Sp { @@ -55,9 +53,8 @@ fn main() {} macro_rules! test_p { ($e:expr) => { let e = $e; - let lex = lexer2::TokenStream::new(e); let p = calc::ExprParser::new(); - match p.parse(lex) { + match p.parse(e) { Ok(res) => println!("{}\n=> {}\n", e, res), Err(e) => eprintln!("{}", e), } @@ -79,8 +76,6 @@ fn calc() { test_p!(r#"(test "h\"i")"#); test_p!(r#"(test " hi ")"#); - test_p!(r#"(+)"#); - test_p!("(+ (1 2 (* 2 5)))"); test_p!(r#"{:key value 12 "hi" (test) (1 2 3)}"#); From b388d04bfaec53a8cc969f1832d832727f28d22e Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Sat, 5 Jun 2021 18:35:43 +0200 Subject: [PATCH 005/137] asdf --- Cargo.lock | 7 +++++ Cargo.toml | 1 + src/config.rs | 80 ++++++++++++++++++++++++++++++++++++++++++++++++ src/lexer.rs | 85 --------------------------------------------------- src/main.rs | 18 +++++++++++ 5 files changed, 106 insertions(+), 85 deletions(-) create mode 100644 src/config.rs delete mode 100644 src/lexer.rs diff --git a/Cargo.lock b/Cargo.lock index 7806c6c..5d6f7ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9,6 +9,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "anyhow" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b" + [[package]] name = "arrayref" version = "0.3.6" @@ -266,6 +272,7 @@ checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" name = "nomwut" version = "0.1.0" dependencies = [ + "anyhow", "itertools", "lalrpop", "lalrpop-util", diff --git a/Cargo.toml b/Cargo.toml index d938bfb..8824444 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ build = "build.rs" lalrpop-util = "0.19.5" regex = "1" itertools = "0.10" +anyhow = "1.0" [build-dependencies] lalrpop = "0.19.5" diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..27f7293 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,80 @@ +use std::collections::HashMap; + +use super::*; +use anyhow::*; + +type VarName = String; +type AttrValue = String; +type AttrName = String; + +pub enum AstError { + UnexpectedNode, + InvalidDefinition, + WrongExprType, +} + +impl From for AstError { + fn from(_: WrongExprType) -> Self { + AstError::WrongExprType + } +} + +pub trait FromExpr: Sized { + fn from_expr(e: Expr) -> Result; + fn from_sp(e: Sp) -> Result { + Self::from_expr(e.1) + } +} + +pub enum DefType { + Widget, +} + +impl FromExpr for DefType { + fn from_expr(e: Expr) -> Result { + if let Expr::Symbol(sym) = e { + match sym.as_str() { + "defwidget" => Ok(DefType::Widget), + _ => Err(AstError::InvalidDefinition), + } + } else { + Err(AstError::UnexpectedNode) + } + } +} + +pub struct Definitional { + def_type: DefType, + name: String, + attrs: HashMap>, + children: Vec, +} + +impl FromExpr for Definitional { + fn from_expr(e: Expr) -> Result { + if let Expr::List(list) = e { + let mut iter = list.into_iter(); + let def_type = DefType::from_sp(iter.next().unwrap())?; + let name = iter.next().unwrap().1.str()?; + let mut attrs = HashMap::new(); + while let Some(Sp(_, Expr::Keyword(x), _)) = iter.next() { + attrs.insert(x, iter.next().unwrap()); + } + + let children = iter.map(T::from_sp).collect::, AstError>>()?; + Ok(Definitional { + def_type, + name, + attrs, + children, + }) + } else { + Err(AstError::UnexpectedNode) + } + } +} + +pub struct WidgetDefinition { + name: String, + argnames: Vec, +} diff --git a/src/lexer.rs b/src/lexer.rs deleted file mode 100644 index 8deea40..0000000 --- a/src/lexer.rs +++ /dev/null @@ -1,85 +0,0 @@ -use std::str::CharIndices; - -pub type Spanned = Result<(Loc, Tok, Loc), Error>; - -#[derive(Copy, Clone, Debug)] -pub enum Tok { - LPren, - RPren, - Space, - Int(i32), -} - -#[derive(Debug, Copy, Clone)] -pub enum LexicalError { - InvalidDigit, - UnknownToken, -} - -impl std::fmt::Display for LexicalError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self) - } -} - -pub struct Lexer<'input> { - chars: CharIndices<'input>, -} - -impl<'input> Lexer<'input> { - pub fn new(input: &'input str) -> Self { - Lexer { - chars: input.char_indices(), - } - } -} - -impl<'input> Iterator for Lexer<'input> { - type Item = Spanned; - - fn next(&mut self) -> Option { - let c = self.chars.next(); - match c { - Some((i, '(')) => Some(Ok((i, Tok::LPren, i + 1))), - Some((i, ')')) => Some(Ok((i, Tok::RPren, i + 1))), - Some((i, s)) if s.is_whitespace() => { - let mut last_space = i; - loop { - match self.chars.next() { - Some((i, next)) if next.is_whitespace() => { - last_space = i; - } - _ => { - break; - } - } - } - Some(Ok((i, Tok::Space, last_space + 1))) - } - Some((i, s)) if s.is_digit(10) || s == '-' => { - let mut end = i; - let mut digits = String::new(); - - loop { - match self.chars.next() { - Some((i, next)) if next.is_digit(10) => { - end = i; - digits.push(next); - } - _ => { - break; - } - } - } - - let num = match digits.parse::() { - Ok(num) => num, - Err(_err) => return Some(Err(LexicalError::InvalidDigit)), - }; - Some(Ok((i, Tok::Int(num), end + 1))) - } - Some((_, _)) => Some(Err(LexicalError::UnknownToken)), - None => None, - } - } -} diff --git a/src/main.rs b/src/main.rs index 99bbd5d..e9ea411 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,9 @@ #![allow(unused_imports)] +#![allow(unused)] + +mod config; + +use std::ops::Deref; use itertools::Itertools; @@ -17,6 +22,9 @@ impl std::fmt::Display for Sp { } } +#[derive(Debug, Clone, Copy)] +pub struct WrongExprType; + #[derive(Debug)] pub enum Expr { List(Vec>), @@ -28,6 +36,16 @@ pub enum Expr { Comment, } +impl Expr { + fn str(self) -> Result { + use Expr::*; + match self { + Str(x) => Ok(x), + _ => Err(WrongExprType), + } + } +} + impl std::fmt::Display for Expr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { use Expr::*; From 2f85889ad9fcf5149878c19f6c2c2855dfda7ce3 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Tue, 29 Jun 2021 13:18:50 +0200 Subject: [PATCH 006/137] arst --- src/config.rs | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/config.rs b/src/config.rs index 27f7293..2dfe97e 100644 --- a/src/config.rs +++ b/src/config.rs @@ -54,12 +54,10 @@ impl FromExpr for Definitional { fn from_expr(e: Expr) -> Result { if let Expr::List(list) = e { let mut iter = list.into_iter(); + let def_type = DefType::from_sp(iter.next().unwrap())?; let name = iter.next().unwrap().1.str()?; - let mut attrs = HashMap::new(); - while let Some(Sp(_, Expr::Keyword(x), _)) = iter.next() { - attrs.insert(x, iter.next().unwrap()); - } + let attrs = parse_key_values(&mut iter); let children = iter.map(T::from_sp).collect::, AstError>>()?; Ok(Definitional { @@ -78,3 +76,23 @@ pub struct WidgetDefinition { name: String, argnames: Vec, } + +pub fn parse_key_values(iter: impl Iterator>) -> HashMap> { + let mut attrs = HashMap::new(); + let mut iter = iter.multipeek(); + loop { + let next = iter.peek(); + let next2 = iter.peek(); + iter.reset_peek(); + if let (Some(Sp(_, Expr::Keyword(_), _)), Some(_)) = (next, next2) { + if let Some(Sp(_, Expr::Keyword(x), _)) = iter.next() { + attrs.insert(x.to_string(), iter.next().unwrap()); + } else { + unreachable!(); + } + } else { + break; + } + } + attrs +} From c93770786563a88f3262be9889ca05ad737084f5 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Tue, 29 Jun 2021 14:18:20 +0200 Subject: [PATCH 007/137] linked list thingy --- src/config.rs | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/config.rs b/src/config.rs index 2dfe97e..19f9cc2 100644 --- a/src/config.rs +++ b/src/config.rs @@ -2,6 +2,7 @@ use std::collections::HashMap; use super::*; use anyhow::*; +use std::collections::LinkedList; type VarName = String; type AttrValue = String; @@ -96,3 +97,52 @@ pub fn parse_key_values(iter: impl Iterator>) -> HashMap>, +} + +enum ExpressionElement { + Single(Sp), + KeyValue(Vec<(String, Sp)>), +} + +impl Iterator for SExpIterator { + type Item = ExpressionElement; + + fn next(&mut self) -> Option { + let mut data = vec![]; + loop { + match (self.elements.pop_front(), self.elements.pop_front()) { + (Some(Sp(kw_l, Expr::Keyword(kw), kw_r)), Some(value)) => { + data.push((kw, value)); + } + (Some(x), Some(y)) => { + self.elements.push_front(y); + self.elements.push_front(x); + break; + } + (Some(x), None) => { + self.elements.push_front(x); + break; + } + (None, None) => break, + (None, Some(_)) => unreachable!(), + } + } + if data.is_empty() { + Some(ExpressionElement::Single(self.elements.pop_front()?)) + } else { + Some(ExpressionElement::KeyValue(data)) + } + } +} + +/* + ( + foo + bar + :baz "hi" :bat "ho" + [rst arst arst rst]) + +*/ From 209123de3aeada8ad90a58923e9aac9a85aa1b63 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Tue, 29 Jun 2021 15:01:18 +0200 Subject: [PATCH 008/137] fast linked list thing --- src/config.rs | 37 +++++++++++++++++++------------------ src/main.rs | 6 ++++++ 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/src/config.rs b/src/config.rs index 19f9cc2..ba39bc5 100644 --- a/src/config.rs +++ b/src/config.rs @@ -113,28 +113,29 @@ impl Iterator for SExpIterator { fn next(&mut self) -> Option { let mut data = vec![]; loop { - match (self.elements.pop_front(), self.elements.pop_front()) { - (Some(Sp(kw_l, Expr::Keyword(kw), kw_r)), Some(value)) => { + let first_is_kw = self.elements.front().map_or(false, |x| x.1.is_keyword()); + if first_is_kw { + let (l, kw, r) = match self.elements.pop_front() { + Some(Sp(l, Expr::Keyword(kw), r)) => (l, kw, r), + _ => unreachable!(), + }; + if let Some(value) = self.elements.pop_front() { data.push((kw, value)); + } else { + return if data.is_empty() { + Some(ExpressionElement::Single(Sp(l, Expr::Keyword(kw), r))) + } else { + Some(ExpressionElement::KeyValue(data)) + }; } - (Some(x), Some(y)) => { - self.elements.push_front(y); - self.elements.push_front(x); - break; - } - (Some(x), None) => { - self.elements.push_front(x); - break; - } - (None, None) => break, - (None, Some(_)) => unreachable!(), + } else { + return if data.is_empty() { + Some(ExpressionElement::Single(self.elements.pop_front()?)) + } else { + Some(ExpressionElement::KeyValue(data)) + }; } } - if data.is_empty() { - Some(ExpressionElement::Single(self.elements.pop_front()?)) - } else { - Some(ExpressionElement::KeyValue(data)) - } } } diff --git a/src/main.rs b/src/main.rs index e9ea411..aa5b2b3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -44,6 +44,12 @@ impl Expr { _ => Err(WrongExprType), } } + fn is_keyword(&self) -> bool { + match self { + Expr::Keyword(_) => true, + _ => false, + } + } } impl std::fmt::Display for Expr { From 159ded782edd9a542c984f34a75c76b8b58f5d0c Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Tue, 29 Jun 2021 15:25:22 +0200 Subject: [PATCH 009/137] i hate this --- src/config.rs | 72 ++++++++++++++++++++++++--------------------------- 1 file changed, 34 insertions(+), 38 deletions(-) diff --git a/src/config.rs b/src/config.rs index ba39bc5..ed00a29 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::{collections::HashMap, iter::FromIterator}; use super::*; use anyhow::*; @@ -54,13 +54,15 @@ pub struct Definitional { impl FromExpr for Definitional { fn from_expr(e: Expr) -> Result { if let Expr::List(list) = e { - let mut iter = list.into_iter(); + let mut iter = SExpIterator::new(list); - let def_type = DefType::from_sp(iter.next().unwrap())?; - let name = iter.next().unwrap().1.str()?; - let attrs = parse_key_values(&mut iter); + let def_type = DefType::from_sp(iter.next().unwrap().as_single().unwrap())?; + let name = iter.next().unwrap().as_single().unwrap().1.str()?; + let attrs = iter.next().unwrap().as_key_value().unwrap(); - let children = iter.map(T::from_sp).collect::, AstError>>()?; + let children = iter + .map(|elem| T::from_sp(elem.as_single()?)) + .collect::, AstError>>()?; Ok(Definitional { def_type, name, @@ -78,40 +80,43 @@ pub struct WidgetDefinition { argnames: Vec, } -pub fn parse_key_values(iter: impl Iterator>) -> HashMap> { - let mut attrs = HashMap::new(); - let mut iter = iter.multipeek(); - loop { - let next = iter.peek(); - let next2 = iter.peek(); - iter.reset_peek(); - if let (Some(Sp(_, Expr::Keyword(_), _)), Some(_)) = (next, next2) { - if let Some(Sp(_, Expr::Keyword(x), _)) = iter.next() { - attrs.insert(x.to_string(), iter.next().unwrap()); - } else { - unreachable!(); - } - } else { - break; - } - } - attrs -} - struct SExpIterator { elements: LinkedList>, } +impl SExpIterator { + fn new(elements: Vec>) -> Self { + SExpIterator { + elements: LinkedList::from_iter(elements.into_iter()), + } + } +} + enum ExpressionElement { Single(Sp), - KeyValue(Vec<(String, Sp)>), + KeyValue(HashMap>), +} + +impl ExpressionElement { + fn as_single(self) -> Option> { + match self { + ExpressionElement::Single(x) => Some(x), + ExpressionElement::KeyValue(_) => None, + } + } + fn as_key_value(self) -> Option>> { + match self { + ExpressionElement::Single(_) => None, + ExpressionElement::KeyValue(x) => Some(x), + } + } } impl Iterator for SExpIterator { type Item = ExpressionElement; fn next(&mut self) -> Option { - let mut data = vec![]; + let mut data = HashMap::new(); loop { let first_is_kw = self.elements.front().map_or(false, |x| x.1.is_keyword()); if first_is_kw { @@ -120,7 +125,7 @@ impl Iterator for SExpIterator { _ => unreachable!(), }; if let Some(value) = self.elements.pop_front() { - data.push((kw, value)); + data.insert(kw, value); } else { return if data.is_empty() { Some(ExpressionElement::Single(Sp(l, Expr::Keyword(kw), r))) @@ -138,12 +143,3 @@ impl Iterator for SExpIterator { } } } - -/* - ( - foo - bar - :baz "hi" :bat "ho" - [rst arst arst rst]) - -*/ From e21983f92d9bc64fc7d7ed81eaaa9a3cb6a80caa Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Thu, 1 Jul 2021 19:57:10 +0200 Subject: [PATCH 010/137] asdf --- src/calc.rs | 703 --------------------------- src/config.rs | 158 +++--- src/main.rs | 54 +- src/{calc.lalrpop => parser.lalrpop} | 0 4 files changed, 121 insertions(+), 794 deletions(-) delete mode 100644 src/calc.rs rename src/{calc.lalrpop => parser.lalrpop} (100%) diff --git a/src/calc.rs b/src/calc.rs deleted file mode 100644 index 19225cc..0000000 --- a/src/calc.rs +++ /dev/null @@ -1,703 +0,0 @@ -// auto-generated: "lalrpop 0.19.5" -// sha3: 7f1eed9c7f5eae965f19f037e3f83cc3f8b8ac855ed1b7d5e579d3c668c23b2 -use std::str::FromStr; -use crate::Expr; -#[allow(unused_extern_crates)] -extern crate lalrpop_util as __lalrpop_util; -#[allow(unused_imports)] -use self::__lalrpop_util::state_machine as __state_machine; -extern crate core; -extern crate alloc; - -#[cfg_attr(rustfmt, rustfmt_skip)] -mod __parse__Expr { - #![allow(non_snake_case, non_camel_case_types, unused_mut, unused_variables, unused_imports, unused_parens)] - - use std::str::FromStr; - use crate::Expr; - #[allow(unused_extern_crates)] - extern crate lalrpop_util as __lalrpop_util; - #[allow(unused_imports)] - use self::__lalrpop_util::state_machine as __state_machine; - extern crate core; - extern crate alloc; - use self::__lalrpop_util::lexer::Token; - #[allow(dead_code)] - pub(crate) enum __Symbol<'input> - { - Variant0(&'input str), - Variant1(Expr), - Variant2(alloc::vec::Vec), - Variant3(i32), - } - const __ACTION: &[i8] = &[ - // State 0 - 2, 0, 7, 8, - // State 1 - 2, 0, 7, 8, - // State 2 - 2, 11, 7, 8, - // State 3 - 0, 0, 0, 0, - // State 4 - -5, -5, -5, -5, - // State 5 - -6, -6, -6, -6, - // State 6 - -7, -7, -7, -7, - // State 7 - -8, -8, -8, -8, - // State 8 - -2, -2, -2, -2, - // State 9 - -3, -3, -3, -3, - // State 10 - -4, -4, -4, -4, - ]; - fn __action(state: i8, integer: usize) -> i8 { - __ACTION[(state as usize) * 4 + integer] - } - const __EOF_ACTION: &[i8] = &[ - // State 0 - 0, - // State 1 - 0, - // State 2 - 0, - // State 3 - -9, - // State 4 - -5, - // State 5 - -6, - // State 6 - -7, - // State 7 - -8, - // State 8 - 0, - // State 9 - 0, - // State 10 - -4, - ]; - fn __goto(state: i8, nt: usize) -> i8 { - match nt { - 1 => 2, - 2 => match state { - 1 => 8, - 2 => 9, - _ => 3, - }, - 3 => 4, - 4 => 5, - _ => 0, - } - } - fn __expected_tokens(__state: i8) -> alloc::vec::Vec { - const __TERMINAL: &[&str] = &[ - r###""(""###, - r###"")""###, - r###"r#":[^\\s]+"#"###, - r###"r#"[0-9]+"#"###, - ]; - __TERMINAL.iter().enumerate().filter_map(|(index, terminal)| { - let next_state = __action(__state, index); - if next_state == 0 { - None - } else { - Some(alloc::string::ToString::to_string(terminal)) - } - }).collect() - } - pub(crate) struct __StateMachine<'input> - where - { - input: &'input str, - __phantom: core::marker::PhantomData<(&'input ())>, - } - impl<'input> __state_machine::ParserDefinition for __StateMachine<'input> - where - { - type Location = usize; - type Error = &'static str; - type Token = Token<'input>; - type TokenIndex = usize; - type Symbol = __Symbol<'input>; - type Success = Expr; - type StateIndex = i8; - type Action = i8; - type ReduceIndex = i8; - type NonterminalIndex = usize; - - #[inline] - fn start_location(&self) -> Self::Location { - Default::default() - } - - #[inline] - fn start_state(&self) -> Self::StateIndex { - 0 - } - - #[inline] - fn token_to_index(&self, token: &Self::Token) -> Option { - __token_to_integer(token, core::marker::PhantomData::<(&())>) - } - - #[inline] - fn action(&self, state: i8, integer: usize) -> i8 { - __action(state, integer) - } - - #[inline] - fn error_action(&self, state: i8) -> i8 { - __action(state, 4 - 1) - } - - #[inline] - fn eof_action(&self, state: i8) -> i8 { - __EOF_ACTION[state as usize] - } - - #[inline] - fn goto(&self, state: i8, nt: usize) -> i8 { - __goto(state, nt) - } - - fn token_to_symbol(&self, token_index: usize, token: Self::Token) -> Self::Symbol { - __token_to_symbol(token_index, token, core::marker::PhantomData::<(&())>) - } - - fn expected_tokens(&self, state: i8) -> alloc::vec::Vec { - __expected_tokens(state) - } - - #[inline] - fn uses_error_recovery(&self) -> bool { - false - } - - #[inline] - fn error_recovery_symbol( - &self, - recovery: __state_machine::ErrorRecovery, - ) -> Self::Symbol { - panic!("error recovery not enabled for this grammar") - } - - fn reduce( - &mut self, - action: i8, - start_location: Option<&Self::Location>, - states: &mut alloc::vec::Vec, - symbols: &mut alloc::vec::Vec<__state_machine::SymbolTriple>, - ) -> Option<__state_machine::ParseResult> { - __reduce( - self.input, - action, - start_location, - states, - symbols, - core::marker::PhantomData::<(&())>, - ) - } - - fn simulate_reduce(&self, action: i8) -> __state_machine::SimulatedReduce { - panic!("error recovery not enabled for this grammar") - } - } - fn __token_to_integer< - 'input, - >( - __token: &Token<'input>, - _: core::marker::PhantomData<(&'input ())>, - ) -> Option - { - match *__token { - Token(2, _) if true => Some(0), - Token(3, _) if true => Some(1), - Token(0, _) if true => Some(2), - Token(1, _) if true => Some(3), - _ => None, - } - } - fn __token_to_symbol< - 'input, - >( - __token_index: usize, - __token: Token<'input>, - _: core::marker::PhantomData<(&'input ())>, - ) -> __Symbol<'input> - { - match __token_index { - 0 | 1 | 2 | 3 => match __token { - Token(2, __tok0) | Token(3, __tok0) | Token(0, __tok0) | Token(1, __tok0) if true => __Symbol::Variant0(__tok0), - _ => unreachable!(), - }, - _ => unreachable!(), - } - } - pub struct ExprParser { - builder: __lalrpop_util::lexer::MatcherBuilder, - _priv: (), - } - - impl ExprParser { - pub fn new() -> ExprParser { - let __builder = super::__intern_token::new_builder(); - ExprParser { - builder: __builder, - _priv: (), - } - } - - #[allow(dead_code)] - pub fn parse< - 'input, - >( - &self, - input: &'input str, - ) -> Result, &'static str>> - { - let mut __tokens = self.builder.matcher(input); - __state_machine::Parser::drive( - __StateMachine { - input, - __phantom: core::marker::PhantomData::<(&())>, - }, - __tokens, - ) - } - } - pub(crate) fn __reduce< - 'input, - >( - input: &'input str, - __action: i8, - __lookahead_start: Option<&usize>, - __states: &mut alloc::vec::Vec, - __symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)>, - _: core::marker::PhantomData<(&'input ())>, - ) -> Option, &'static str>>> - { - let (__pop_states, __nonterminal) = match __action { - 0 => { - __reduce0(input, __lookahead_start, __symbols, core::marker::PhantomData::<(&())>) - } - 1 => { - __reduce1(input, __lookahead_start, __symbols, core::marker::PhantomData::<(&())>) - } - 2 => { - __reduce2(input, __lookahead_start, __symbols, core::marker::PhantomData::<(&())>) - } - 3 => { - __reduce3(input, __lookahead_start, __symbols, core::marker::PhantomData::<(&())>) - } - 4 => { - __reduce4(input, __lookahead_start, __symbols, core::marker::PhantomData::<(&())>) - } - 5 => { - __reduce5(input, __lookahead_start, __symbols, core::marker::PhantomData::<(&())>) - } - 6 => { - __reduce6(input, __lookahead_start, __symbols, core::marker::PhantomData::<(&())>) - } - 7 => { - __reduce7(input, __lookahead_start, __symbols, core::marker::PhantomData::<(&())>) - } - 8 => { - // __Expr = Expr => ActionFn(0); - let __sym0 = __pop_Variant1(__symbols); - let __start = __sym0.0.clone(); - let __end = __sym0.2.clone(); - let __nt = super::__action0::<>(input, __sym0); - return Some(Ok(__nt)); - } - _ => panic!("invalid action code {}", __action) - }; - let __states_len = __states.len(); - __states.truncate(__states_len - __pop_states); - let __state = *__states.last().unwrap(); - let __next_state = __goto(__state, __nonterminal); - __states.push(__next_state); - None - } - #[inline(never)] - fn __symbol_type_mismatch() -> ! { - panic!("symbol type mismatch") - } - fn __pop_Variant1< - 'input, - >( - __symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)> - ) -> (usize, Expr, usize) - { - match __symbols.pop() { - Some((__l, __Symbol::Variant1(__v), __r)) => (__l, __v, __r), - _ => __symbol_type_mismatch() - } - } - fn __pop_Variant2< - 'input, - >( - __symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)> - ) -> (usize, alloc::vec::Vec, usize) - { - match __symbols.pop() { - Some((__l, __Symbol::Variant2(__v), __r)) => (__l, __v, __r), - _ => __symbol_type_mismatch() - } - } - fn __pop_Variant3< - 'input, - >( - __symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)> - ) -> (usize, i32, usize) - { - match __symbols.pop() { - Some((__l, __Symbol::Variant3(__v), __r)) => (__l, __v, __r), - _ => __symbol_type_mismatch() - } - } - fn __pop_Variant0< - 'input, - >( - __symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)> - ) -> (usize, &'input str, usize) - { - match __symbols.pop() { - Some((__l, __Symbol::Variant0(__v), __r)) => (__l, __v, __r), - _ => __symbol_type_mismatch() - } - } - pub(crate) fn __reduce0< - 'input, - >( - input: &'input str, - __lookahead_start: Option<&usize>, - __symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)>, - _: core::marker::PhantomData<(&'input ())>, - ) -> (usize, usize) - { - // () = Expr => ActionFn(8); - let __sym0 = __pop_Variant1(__symbols); - let __start = __sym0.0.clone(); - let __end = __sym0.2.clone(); - let __nt = super::__action8::<>(input, __sym0); - __symbols.push((__start, __Symbol::Variant1(__nt), __end)); - (1, 0) - } - pub(crate) fn __reduce1< - 'input, - >( - input: &'input str, - __lookahead_start: Option<&usize>, - __symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)>, - _: core::marker::PhantomData<(&'input ())>, - ) -> (usize, usize) - { - // ()+ = Expr => ActionFn(9); - let __sym0 = __pop_Variant1(__symbols); - let __start = __sym0.0.clone(); - let __end = __sym0.2.clone(); - let __nt = super::__action9::<>(input, __sym0); - __symbols.push((__start, __Symbol::Variant2(__nt), __end)); - (1, 1) - } - pub(crate) fn __reduce2< - 'input, - >( - input: &'input str, - __lookahead_start: Option<&usize>, - __symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)>, - _: core::marker::PhantomData<(&'input ())>, - ) -> (usize, usize) - { - // ()+ = ()+, Expr => ActionFn(10); - assert!(__symbols.len() >= 2); - let __sym1 = __pop_Variant1(__symbols); - let __sym0 = __pop_Variant2(__symbols); - let __start = __sym0.0.clone(); - let __end = __sym1.2.clone(); - let __nt = super::__action10::<>(input, __sym0, __sym1); - __symbols.push((__start, __Symbol::Variant2(__nt), __end)); - (2, 1) - } - pub(crate) fn __reduce3< - 'input, - >( - input: &'input str, - __lookahead_start: Option<&usize>, - __symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)>, - _: core::marker::PhantomData<(&'input ())>, - ) -> (usize, usize) - { - // Expr = "(", ()+, ")" => ActionFn(1); - assert!(__symbols.len() >= 3); - let __sym2 = __pop_Variant0(__symbols); - let __sym1 = __pop_Variant2(__symbols); - let __sym0 = __pop_Variant0(__symbols); - let __start = __sym0.0.clone(); - let __end = __sym2.2.clone(); - let __nt = super::__action1::<>(input, __sym0, __sym1, __sym2); - __symbols.push((__start, __Symbol::Variant1(__nt), __end)); - (3, 2) - } - pub(crate) fn __reduce4< - 'input, - >( - input: &'input str, - __lookahead_start: Option<&usize>, - __symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)>, - _: core::marker::PhantomData<(&'input ())>, - ) -> (usize, usize) - { - // Expr = Keyword => ActionFn(2); - let __sym0 = __pop_Variant1(__symbols); - let __start = __sym0.0.clone(); - let __end = __sym0.2.clone(); - let __nt = super::__action2::<>(input, __sym0); - __symbols.push((__start, __Symbol::Variant1(__nt), __end)); - (1, 2) - } - pub(crate) fn __reduce5< - 'input, - >( - input: &'input str, - __lookahead_start: Option<&usize>, - __symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)>, - _: core::marker::PhantomData<(&'input ())>, - ) -> (usize, usize) - { - // Expr = Num => ActionFn(3); - let __sym0 = __pop_Variant3(__symbols); - let __start = __sym0.0.clone(); - let __end = __sym0.2.clone(); - let __nt = super::__action3::<>(input, __sym0); - __symbols.push((__start, __Symbol::Variant1(__nt), __end)); - (1, 2) - } - pub(crate) fn __reduce6< - 'input, - >( - input: &'input str, - __lookahead_start: Option<&usize>, - __symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)>, - _: core::marker::PhantomData<(&'input ())>, - ) -> (usize, usize) - { - // Keyword = r#":[^\\s]+"# => ActionFn(4); - let __sym0 = __pop_Variant0(__symbols); - let __start = __sym0.0.clone(); - let __end = __sym0.2.clone(); - let __nt = super::__action4::<>(input, __sym0); - __symbols.push((__start, __Symbol::Variant1(__nt), __end)); - (1, 3) - } - pub(crate) fn __reduce7< - 'input, - >( - input: &'input str, - __lookahead_start: Option<&usize>, - __symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)>, - _: core::marker::PhantomData<(&'input ())>, - ) -> (usize, usize) - { - // Num = r#"[0-9]+"# => ActionFn(5); - let __sym0 = __pop_Variant0(__symbols); - let __start = __sym0.0.clone(); - let __end = __sym0.2.clone(); - let __nt = super::__action5::<>(input, __sym0); - __symbols.push((__start, __Symbol::Variant3(__nt), __end)); - (1, 4) - } -} -pub use self::__parse__Expr::ExprParser; -#[cfg_attr(rustfmt, rustfmt_skip)] -mod __intern_token { - #![allow(unused_imports)] - use std::str::FromStr; - use crate::Expr; - #[allow(unused_extern_crates)] - extern crate lalrpop_util as __lalrpop_util; - #[allow(unused_imports)] - use self::__lalrpop_util::state_machine as __state_machine; - extern crate core; - extern crate alloc; - pub fn new_builder() -> __lalrpop_util::lexer::MatcherBuilder { - let __strs: &[(&str, bool)] = &[ - ("^(:[\u{0}-\u{8}\u{e}-\u{1f}!-\u{84}\u{86}-\u{9f}¡-ᙿᚁ-\u{1fff}\u{200b}-‧\u{202a}-\u{202e}‰-⁞\u{2060}-\u{2fff}、-\u{10ffff}]+)", false), - ("^([0-9]+)", false), - ("^(\\()", false), - ("^(\\))", false), - (r"^(\s*)", true), - ]; - __lalrpop_util::lexer::MatcherBuilder::new(__strs.iter().copied()).unwrap() - } -} -pub(crate) use self::__lalrpop_util::lexer::Token; - -#[allow(unused_variables)] -fn __action0< - 'input, ->( - input: &'input str, - (_, __0, _): (usize, Expr, usize), -) -> Expr -{ - __0 -} - -#[allow(unused_variables)] -fn __action1< - 'input, ->( - input: &'input str, - (_, _, _): (usize, &'input str, usize), - (_, elems, _): (usize, alloc::vec::Vec, usize), - (_, _, _): (usize, &'input str, usize), -) -> Expr -{ - Expr::List(elems) -} - -#[allow(unused_variables)] -fn __action2< - 'input, ->( - input: &'input str, - (_, x, _): (usize, Expr, usize), -) -> Expr -{ - x -} - -#[allow(unused_variables)] -fn __action3< - 'input, ->( - input: &'input str, - (_, x, _): (usize, i32, usize), -) -> Expr -{ - Expr::Number(x) -} - -#[allow(unused_variables)] -fn __action4< - 'input, ->( - input: &'input str, - (_, __0, _): (usize, &'input str, usize), -) -> Expr -{ - Expr::Keyword(__0.to_string()) -} - -#[allow(unused_variables)] -fn __action5< - 'input, ->( - input: &'input str, - (_, __0, _): (usize, &'input str, usize), -) -> i32 -{ - i32::from_str(__0).unwrap() -} - -#[allow(unused_variables)] -fn __action6< - 'input, ->( - input: &'input str, - (_, __0, _): (usize, Expr, usize), -) -> alloc::vec::Vec -{ - alloc::vec![__0] -} - -#[allow(unused_variables)] -fn __action7< - 'input, ->( - input: &'input str, - (_, v, _): (usize, alloc::vec::Vec, usize), - (_, e, _): (usize, Expr, usize), -) -> alloc::vec::Vec -{ - { let mut v = v; v.push(e); v } -} - -#[allow(unused_variables)] -fn __action8< - 'input, ->( - input: &'input str, - (_, __0, _): (usize, Expr, usize), -) -> Expr -{ - __0 -} - -#[allow(unused_variables)] -fn __action9< - 'input, ->( - input: &'input str, - __0: (usize, Expr, usize), -) -> alloc::vec::Vec -{ - let __start0 = __0.0.clone(); - let __end0 = __0.2.clone(); - let __temp0 = __action8( - input, - __0, - ); - let __temp0 = (__start0, __temp0, __end0); - __action6( - input, - __temp0, - ) -} - -#[allow(unused_variables)] -fn __action10< - 'input, ->( - input: &'input str, - __0: (usize, alloc::vec::Vec, usize), - __1: (usize, Expr, usize), -) -> alloc::vec::Vec -{ - let __start0 = __1.0.clone(); - let __end0 = __1.2.clone(); - let __temp0 = __action8( - input, - __1, - ); - let __temp0 = (__start0, __temp0, __end0); - __action7( - input, - __0, - __temp0, - ) -} - -pub trait __ToTriple<'input, > { - fn to_triple(value: Self) -> Result<(usize,Token<'input>,usize), __lalrpop_util::ParseError, &'static str>>; -} - -impl<'input, > __ToTriple<'input, > for (usize, Token<'input>, usize) { - fn to_triple(value: Self) -> Result<(usize,Token<'input>,usize), __lalrpop_util::ParseError, &'static str>> { - Ok(value) - } -} -impl<'input, > __ToTriple<'input, > for Result<(usize, Token<'input>, usize), &'static str> { - fn to_triple(value: Self) -> Result<(usize,Token<'input>,usize), __lalrpop_util::ParseError, &'static str>> { - match value { - Ok(v) => Ok(v), - Err(error) => Err(__lalrpop_util::ParseError::User { error }), - } - } -} diff --git a/src/config.rs b/src/config.rs index ed00a29..51c8a27 100644 --- a/src/config.rs +++ b/src/config.rs @@ -2,16 +2,28 @@ use std::{collections::HashMap, iter::FromIterator}; use super::*; use anyhow::*; +use itertools::Itertools; use std::collections::LinkedList; type VarName = String; type AttrValue = String; type AttrName = String; +#[derive(Debug, PartialEq, Eq)] pub enum AstError { UnexpectedNode, InvalidDefinition, - WrongExprType, + WrongExprType(Sp), + MissingNode, +} + +trait OptionAstErrorExt { + fn or_missing(self) -> Result; +} +impl OptionAstErrorExt for Option { + fn or_missing(self) -> Result { + self.ok_or(AstError::MissingNode) + } } impl From for AstError { @@ -27,6 +39,12 @@ pub trait FromExpr: Sized { } } +impl FromExpr for Expr { + fn from_expr(e: Expr) -> Result { + Ok(e) + } +} + pub enum DefType { Widget, } @@ -54,15 +72,13 @@ pub struct Definitional { impl FromExpr for Definitional { fn from_expr(e: Expr) -> Result { if let Expr::List(list) = e { - let mut iter = SExpIterator::new(list); + let mut iter = itertools::put_back(list.into_iter()); - let def_type = DefType::from_sp(iter.next().unwrap().as_single().unwrap())?; - let name = iter.next().unwrap().as_single().unwrap().1.str()?; - let attrs = iter.next().unwrap().as_key_value().unwrap(); + let def_type = DefType::from_sp(iter.next().or_missing()?)?; + let name = iter.next().or_missing()?.1.str()?; + let attrs = parse_key_values(&mut iter); - let children = iter - .map(|elem| T::from_sp(elem.as_single()?)) - .collect::, AstError>>()?; + let children = iter.map(T::from_sp).collect::, AstError>>()?; Ok(Definitional { def_type, name, @@ -74,72 +90,88 @@ impl FromExpr for Definitional { } } } - -pub struct WidgetDefinition { +#[derive(Debug, Eq, PartialEq)] +pub struct Element { name: String, - argnames: Vec, + attrs: HashMap>, + children: Vec, } -struct SExpIterator { - elements: LinkedList>, -} +impl FromExpr for Element> { + fn from_expr(e: Expr) -> Result { + if let Expr::List(list) = e { + let mut iter = itertools::put_back(list.into_iter()); -impl SExpIterator { - fn new(elements: Vec>) -> Self { - SExpIterator { - elements: LinkedList::from_iter(elements.into_iter()), + let name = iter.next().or_missing()?.1.str()?; + let attrs = parse_key_values(&mut iter); + + Ok(Element { + name, + attrs, + children: iter.collect_vec(), + }) + } else { + Err(AstError::UnexpectedNode) } } } -enum ExpressionElement { - Single(Sp), - KeyValue(HashMap>), -} - -impl ExpressionElement { - fn as_single(self) -> Option> { - match self { - ExpressionElement::Single(x) => Some(x), - ExpressionElement::KeyValue(_) => None, - } - } - fn as_key_value(self) -> Option>> { - match self { - ExpressionElement::Single(_) => None, - ExpressionElement::KeyValue(x) => Some(x), - } - } -} - -impl Iterator for SExpIterator { - type Item = ExpressionElement; - - fn next(&mut self) -> Option { - let mut data = HashMap::new(); - loop { - let first_is_kw = self.elements.front().map_or(false, |x| x.1.is_keyword()); - if first_is_kw { - let (l, kw, r) = match self.elements.pop_front() { - Some(Sp(l, Expr::Keyword(kw), r)) => (l, kw, r), - _ => unreachable!(), - }; - if let Some(value) = self.elements.pop_front() { +fn parse_key_values>>( + iter: &mut itertools::PutBack, +) -> HashMap> { + let mut data = HashMap::new(); + loop { + match iter.next() { + Some(Sp(l, Expr::Keyword(kw), r)) => match iter.next() { + Some(value) => { data.insert(kw, value); - } else { - return if data.is_empty() { - Some(ExpressionElement::Single(Sp(l, Expr::Keyword(kw), r))) - } else { - Some(ExpressionElement::KeyValue(data)) - }; } - } else { - return if data.is_empty() { - Some(ExpressionElement::Single(self.elements.pop_front()?)) - } else { - Some(ExpressionElement::KeyValue(data)) - }; + None => { + iter.put_back(Sp(l, Expr::Keyword(kw), r)); + return data; + } + }, + Some(expr) => { + iter.put_back(expr); + return data; } + None => return data, } } } + +#[cfg(test)] +mod test { + + use super::*; + + #[test] + fn test() { + let parser = parser::ExprParser::new(); + assert_eq!( + Element::>::from_expr( + parser + .parse("(box foo :bar 12 :baz \"hi\" foo (bar))") + .unwrap() + ) + .unwrap(), + Element { + name: "box".to_string(), + children: vec![ + Sp(1, Expr::Symbol("foo".to_string()), 2), + Sp( + 2, + Expr::List(vec![Sp(2, Expr::Symbol("bar".to_string()), 3)]), + 3 + ) + ], + attrs: { + let mut data = HashMap::new(); + data.insert("foo".to_string(), Sp(2, Expr::Number(12), 3)); + data.insert("bar".to_string(), Sp(2, Expr::Str("hi".to_string()), 3)); + data + }, + } + ); + } +} diff --git a/src/main.rs b/src/main.rs index aa5b2b3..400c56f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,7 +11,7 @@ use lalrpop_util::lalrpop_mod; //mod lexer; -lalrpop_mod!(pub calc); +lalrpop_mod!(pub parser); #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub struct Sp(pub usize, pub T, pub usize); @@ -22,10 +22,10 @@ impl std::fmt::Display for Sp { } } -#[derive(Debug, Clone, Copy)] -pub struct WrongExprType; +#[derive(Debug, Clone)] +pub struct WrongExprType(Sp); -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq, Clone)] pub enum Expr { List(Vec>), Table(Vec<(Sp, Sp)>), @@ -41,7 +41,7 @@ impl Expr { use Expr::*; match self { Str(x) => Ok(x), - _ => Err(WrongExprType), + x => Err(WrongExprType(x)), } } fn is_keyword(&self) -> bool { @@ -77,7 +77,7 @@ fn main() {} macro_rules! test_p { ($e:expr) => { let e = $e; - let p = calc::ExprParser::new(); + let p = parser::ExprParser::new(); match p.parse(e) { Ok(res) => println!("{}\n=> {}\n", e, res), Err(e) => eprintln!("{}", e), @@ -86,31 +86,29 @@ macro_rules! test_p { } #[test] -fn calc() { - test_p!("1"); - test_p!("(12)"); - test_p!("(1 2)"); - test_p!("(1 :foo 1)"); - test_p!("(:foo 1)"); - test_p!("(:foo->: 1)"); - test_p!("(foo 1)"); - test_p!("(lol😄 1)"); +fn test() { + //test_p!("1"); + //test_p!("(12)"); + //test_p!("(1 2)"); + //test_p!("(1 :foo 1)"); + //test_p!("(:foo 1)"); + //test_p!("(:foo->: 1)"); + //test_p!("(foo 1)"); + //test_p!("(lol😄 1)"); - test_p!(r#"(test "hi")"#); - test_p!(r#"(test "h\"i")"#); - test_p!(r#"(test " hi ")"#); + //test_p!(r#"(test "hi")"#); + //test_p!(r#"(test "h\"i")"#); + //test_p!(r#"(test " hi ")"#); - test_p!("(+ (1 2 (* 2 5)))"); + //test_p!("(+ (1 2 (* 2 5)))"); - test_p!(r#"{:key value 12 "hi" (test) (1 2 3)}"#); + //test_p!(r#"{:key value 12 "hi" (test) (1 2 3)}"#); - test_p!(r#"; test"#); - test_p!( - r#"(f arg ; test - arg2)"# - ); + //test_p!(r#"; test"#); + //test_p!( + //r#"(f arg ; test + //arg2)"# + //); - println!("\n\n\n\n\n\n"); - - panic!() + //println!("\n\n\n\n\n\n"); } diff --git a/src/calc.lalrpop b/src/parser.lalrpop similarity index 100% rename from src/calc.lalrpop rename to src/parser.lalrpop From a06927e356e94145d2c3b9be9d62a2ba765a9fae Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Thu, 1 Jul 2021 20:38:23 +0200 Subject: [PATCH 011/137] parser! --- Cargo.lock | 9 +++++ Cargo.toml | 1 + src/config.rs | 87 ++++++++++++++++------------------------------ src/error.rs | 18 ++++++++++ src/main.rs | 58 +++++++++++++++++-------------- src/parser.lalrpop | 23 +++++------- 6 files changed, 99 insertions(+), 97 deletions(-) create mode 100644 src/error.rs diff --git a/Cargo.lock b/Cargo.lock index 5d6f7ea..3cde16d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "aho-corasick" version = "0.7.18" @@ -256,6 +258,12 @@ dependencies = [ "cfg-if", ] +[[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" @@ -276,6 +284,7 @@ dependencies = [ "itertools", "lalrpop", "lalrpop-util", + "maplit", "regex", ] diff --git a/Cargo.toml b/Cargo.toml index 8824444..11018a4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ lalrpop-util = "0.19.5" regex = "1" itertools = "0.10" anyhow = "1.0" +maplit = "*" [build-dependencies] lalrpop = "0.19.5" diff --git a/src/config.rs b/src/config.rs index 51c8a27..cb3127b 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,6 +1,7 @@ use std::{collections::HashMap, iter::FromIterator}; use super::*; +use crate::error::*; use anyhow::*; use itertools::Itertools; use std::collections::LinkedList; @@ -9,34 +10,8 @@ type VarName = String; type AttrValue = String; type AttrName = String; -#[derive(Debug, PartialEq, Eq)] -pub enum AstError { - UnexpectedNode, - InvalidDefinition, - WrongExprType(Sp), - MissingNode, -} - -trait OptionAstErrorExt { - fn or_missing(self) -> Result; -} -impl OptionAstErrorExt for Option { - fn or_missing(self) -> Result { - self.ok_or(AstError::MissingNode) - } -} - -impl From for AstError { - fn from(_: WrongExprType) -> Self { - AstError::WrongExprType - } -} - pub trait FromExpr: Sized { fn from_expr(e: Expr) -> Result; - fn from_sp(e: Sp) -> Result { - Self::from_expr(e.1) - } } impl FromExpr for Expr { @@ -51,7 +26,7 @@ pub enum DefType { impl FromExpr for DefType { fn from_expr(e: Expr) -> Result { - if let Expr::Symbol(sym) = e { + if let Expr::Symbol(_, sym) = e { match sym.as_str() { "defwidget" => Ok(DefType::Widget), _ => Err(AstError::InvalidDefinition), @@ -65,20 +40,22 @@ impl FromExpr for DefType { pub struct Definitional { def_type: DefType, name: String, - attrs: HashMap>, + attrs: HashMap, children: Vec, } impl FromExpr for Definitional { fn from_expr(e: Expr) -> Result { - if let Expr::List(list) = e { + if let Expr::List(span, list) = e { let mut iter = itertools::put_back(list.into_iter()); - let def_type = DefType::from_sp(iter.next().or_missing()?)?; - let name = iter.next().or_missing()?.1.str()?; + let def_type = DefType::from_expr(iter.next().or_missing()?)?; + let name = iter.next().or_missing()?.as_str()?; let attrs = parse_key_values(&mut iter); - let children = iter.map(T::from_sp).collect::, AstError>>()?; + let children = iter + .map(T::from_expr) + .collect::, AstError>>()?; Ok(Definitional { def_type, name, @@ -93,16 +70,16 @@ impl FromExpr for Definitional { #[derive(Debug, Eq, PartialEq)] pub struct Element { name: String, - attrs: HashMap>, + attrs: HashMap, children: Vec, } -impl FromExpr for Element> { +impl FromExpr for Element { fn from_expr(e: Expr) -> Result { - if let Expr::List(list) = e { + if let Expr::List(span, list) = e { let mut iter = itertools::put_back(list.into_iter()); - let name = iter.next().or_missing()?.1.str()?; + let name = iter.next().or_missing()?.as_symbol()?; let attrs = parse_key_values(&mut iter); Ok(Element { @@ -116,18 +93,18 @@ impl FromExpr for Element> { } } -fn parse_key_values>>( +fn parse_key_values>( iter: &mut itertools::PutBack, -) -> HashMap> { +) -> HashMap { let mut data = HashMap::new(); loop { match iter.next() { - Some(Sp(l, Expr::Keyword(kw), r)) => match iter.next() { + Some(Expr::Keyword(span, kw)) => match iter.next() { Some(value) => { data.insert(kw, value); } None => { - iter.put_back(Sp(l, Expr::Keyword(kw), r)); + iter.put_back(Expr::Keyword(span, kw)); return data; } }, @@ -149,28 +126,24 @@ mod test { fn test() { let parser = parser::ExprParser::new(); assert_eq!( - Element::>::from_expr( - parser - .parse("(box foo :bar 12 :baz \"hi\" foo (bar))") - .unwrap() + Element::::from_expr( + parser.parse("(box :bar 12 :baz \"hi\" foo (bar))").unwrap() ) .unwrap(), Element { name: "box".to_string(), - children: vec![ - Sp(1, Expr::Symbol("foo".to_string()), 2), - Sp( - 2, - Expr::List(vec![Sp(2, Expr::Symbol("bar".to_string()), 3)]), - 3 - ) - ], - attrs: { - let mut data = HashMap::new(); - data.insert("foo".to_string(), Sp(2, Expr::Number(12), 3)); - data.insert("bar".to_string(), Sp(2, Expr::Str("hi".to_string()), 3)); - data + attrs: maplit::hashmap! { + ":bar".to_string() => Expr::Number(Span(10, 12), 12), + ":baz".to_string() => Expr::Str(Span(18, 22), "hi".to_string()), + }, + children: vec![ + Expr::Symbol(Span(23, 26), "foo".to_string()), + Expr::List( + Span(27, 32), + vec![Expr::Symbol(Span(28, 31), "bar".to_string())] + ), + ], } ); } diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..23948a0 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,18 @@ +use crate::Expr; + +#[derive(Debug, PartialEq, Eq)] +pub enum AstError { + UnexpectedNode, + InvalidDefinition, + WrongExprType(Expr), + MissingNode, +} + +pub trait OptionAstErrorExt { + fn or_missing(self) -> Result; +} +impl OptionAstErrorExt for Option { + fn or_missing(self) -> Result { + self.ok_or(AstError::MissingNode) + } +} diff --git a/src/main.rs b/src/main.rs index 400c56f..7bc52ee 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,8 @@ #![allow(unused)] mod config; +mod error; +use error::AstError; use std::ops::Deref; @@ -14,39 +16,43 @@ use lalrpop_util::lalrpop_mod; lalrpop_mod!(pub parser); #[derive(Debug, Eq, PartialEq, Clone, Copy)] -pub struct Sp(pub usize, pub T, pub usize); +pub struct Span(pub usize, pub usize); -impl std::fmt::Display for Sp { +impl std::fmt::Display for Span { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "<{}- {} -{}>", self.0, self.1, self.2) + write!(f, "<{}..{}>", self.0, self.1) } } -#[derive(Debug, Clone)] -pub struct WrongExprType(Sp); - #[derive(Debug, PartialEq, Eq, Clone)] pub enum Expr { - List(Vec>), - Table(Vec<(Sp, Sp)>), - Keyword(String), - Symbol(String), - Str(String), - Number(i32), + List(Span, Vec), + Table(Span, Vec<(Expr, Expr)>), + Keyword(Span, String), + Symbol(Span, String), + Str(Span, String), + Number(Span, i32), Comment, } -impl Expr { - fn str(self) -> Result { - use Expr::*; - match self { - Str(x) => Ok(x), - x => Err(WrongExprType(x)), +macro_rules! as_func { + ($name:ident<$t:ty> = $p:pat => $value:expr) => { + fn $name(self) -> Result<$t, AstError> { + match self { + $p => Ok($value), + x => Err(AstError::WrongExprType(x)), + } } - } + }; +} + +impl Expr { + as_func!(as_str = Expr::Str(_, x) => x); + as_func!(as_symbol = Expr::Symbol(_, x) => x); + fn is_keyword(&self) -> bool { match self { - Expr::Keyword(_) => true, + Expr::Keyword(_, _) => true, _ => false, } } @@ -56,16 +62,16 @@ impl std::fmt::Display for Expr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { use Expr::*; match self { - Number(x) => write!(f, "{}", x), - List(x) => write!(f, "({})", x.iter().map(|e| format!("{}", e)).join(" ")), - Table(x) => write!( + Number(_, x) => write!(f, "{}", x), + List(_, x) => write!(f, "({})", x.iter().map(|e| format!("{}", e)).join(" ")), + Table(_, x) => write!( f, "{{{}}}", x.iter().map(|(k, v)| format!("{} {}", k, v)).join(" ") ), - Keyword(x) => write!(f, "{}", x), - Symbol(x) => write!(f, "{}", x), - Str(x) => write!(f, "{}", x), + Keyword(_, x) => write!(f, "{}", x), + Symbol(_, x) => write!(f, "{}", x), + Str(_, x) => write!(f, "{}", x), Comment => write!(f, ""), } } diff --git a/src/parser.lalrpop b/src/parser.lalrpop index 48405fe..050b13b 100644 --- a/src/parser.lalrpop +++ b/src/parser.lalrpop @@ -1,34 +1,29 @@ use std::str::FromStr; //use crate::lexer; use crate::Expr; -use crate::Sp; +use crate::Span; grammar; -Span: Sp = { - <@L> <@R> => Sp(<>) -}; - pub Expr: Expr = { - "(" )>>)+> ")" => Expr::List(elems), + "(" )+> ")" => Expr::List(Span(l, r), elems), - "{" )>> )>>)*> "}" => Expr::Table(elems), + "{" )> <()>)*> "}" => Expr::Table(Span(l, r), elems), => x, => x, - => Expr::Str(x), - => Expr::Number(x), + => Expr::Str(Span(l, r), x), + => Expr::Number(Span(l, r), x), Comment => Expr::Comment, }; -Keyword: Expr = => Expr::Keyword(<>.to_string()); -Symbol: Expr = /.*-+][^\s{}\(\)]*"> => Expr::Symbol(<>.to_string()); +Keyword: Expr = => Expr::Keyword(Span(l, r), x.to_string()); +Symbol: Expr = /.*-+][^\s{}\(\)]*"> => Expr::Symbol(Span(l, r), x.to_string()); StrLit: String = { - r#""(?:[^"\\]|\\.)*""# => { - let val = <>; - val[1..val.len() - 1].to_owned() + => { + x[1..x.len() - 1].to_owned() }, } From 76ca07b4ddff0db5a0d3c9a29796b3356147cb68 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Fri, 2 Jul 2021 17:31:55 +0200 Subject: [PATCH 012/137] never sseen such clean error handling --- Cargo.lock | 57 ++++++++++++++++++++++--- Cargo.toml | 4 +- rust-toolchain | 1 + rustfmt.toml | 14 ++++++ src/config.rs | 68 ++++++++++------------------- src/error.rs | 52 +++++++++++++++++++---- src/main.rs | 104 ++++++++++++++++++++++++++++++--------------- src/parser.lalrpop | 2 +- 8 files changed, 204 insertions(+), 98 deletions(-) create mode 100644 rust-toolchain create mode 100644 rustfmt.toml diff --git a/Cargo.lock b/Cargo.lock index 3cde16d..3aaeee1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,12 +11,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "anyhow" -version = "1.0.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b" - [[package]] name = "arrayref" version = "0.3.6" @@ -280,12 +274,12 @@ checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" name = "nomwut" version = "0.1.0" dependencies = [ - "anyhow", "itertools", "lalrpop", "lalrpop-util", "maplit", "regex", + "thiserror", ] [[package]] @@ -319,6 +313,24 @@ 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.1.57" @@ -383,6 +395,17 @@ dependencies = [ "precomputed-hash", ] +[[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.5.2" @@ -394,6 +417,26 @@ dependencies = [ "winapi", ] +[[package]] +name = "thiserror" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa6f76457f59514c7eeb4e59d891395fab0b2fd1d40723ae737d64153392e9c6" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a36768c0fbf1bb15eca10defa29526bda730a2376c2ab4393ccfa16fb1a318d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tiny-keccak" version = "2.0.2" diff --git a/Cargo.toml b/Cargo.toml index 11018a4..dfcdf09 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,8 +12,8 @@ build = "build.rs" lalrpop-util = "0.19.5" regex = "1" itertools = "0.10" -anyhow = "1.0" -maplit = "*" +thiserror = "1.0" +maplit = "1.0" [build-dependencies] lalrpop = "0.19.5" diff --git a/rust-toolchain b/rust-toolchain new file mode 100644 index 0000000..bf867e0 --- /dev/null +++ b/rust-toolchain @@ -0,0 +1 @@ +nightly diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..edce9c8 --- /dev/null +++ b/rustfmt.toml @@ -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" diff --git a/src/config.rs b/src/config.rs index cb3127b..9941e54 100644 --- a/src/config.rs +++ b/src/config.rs @@ -2,7 +2,6 @@ use std::{collections::HashMap, iter::FromIterator}; use super::*; use crate::error::*; -use anyhow::*; use itertools::Itertools; use std::collections::LinkedList; @@ -11,11 +10,11 @@ type AttrValue = String; type AttrName = String; pub trait FromExpr: Sized { - fn from_expr(e: Expr) -> Result; + fn from_expr(e: Expr) -> AstResult; } impl FromExpr for Expr { - fn from_expr(e: Expr) -> Result { + fn from_expr(e: Expr) -> AstResult { Ok(e) } } @@ -25,14 +24,14 @@ pub enum DefType { } impl FromExpr for DefType { - fn from_expr(e: Expr) -> Result { - if let Expr::Symbol(_, sym) = e { + fn from_expr(e: Expr) -> AstResult { + if let Expr::Symbol(span, sym) = e { match sym.as_str() { "defwidget" => Ok(DefType::Widget), - _ => Err(AstError::InvalidDefinition), + _ => Err(AstError::InvalidDefinition(Some(span))), } } else { - Err(AstError::UnexpectedNode) + Err(AstError::WrongExprType(Some(e.span()), ExprType::Symbol, e)) } } } @@ -45,26 +44,18 @@ pub struct Definitional { } impl FromExpr for Definitional { - fn from_expr(e: Expr) -> Result { - if let Expr::List(span, list) = e { + fn from_expr(e: Expr) -> AstResult { + spanned!(e.span(), { + let list = e.as_list()?; let mut iter = itertools::put_back(list.into_iter()); - let def_type = DefType::from_expr(iter.next().or_missing()?)?; - let name = iter.next().or_missing()?.as_str()?; + let def_type = DefType::from_expr(iter.next().or_missing(ExprType::Symbol)?)?; + let name = iter.next().or_missing(ExprType::Str)?.as_str()?; let attrs = parse_key_values(&mut iter); - let children = iter - .map(T::from_expr) - .collect::, AstError>>()?; - Ok(Definitional { - def_type, - name, - attrs, - children, - }) - } else { - Err(AstError::UnexpectedNode) - } + let children = iter.map(|x| T::from_expr(x)).collect::>>()?; + Definitional { def_type, name, attrs, children } + }) } } #[derive(Debug, Eq, PartialEq)] @@ -75,27 +66,20 @@ pub struct Element { } impl FromExpr for Element { - fn from_expr(e: Expr) -> Result { - if let Expr::List(span, list) = e { + fn from_expr(e: Expr) -> AstResult { + spanned!(e.span(), { + let list = e.as_list()?; let mut iter = itertools::put_back(list.into_iter()); - let name = iter.next().or_missing()?.as_symbol()?; + let name = iter.next().or_missing(ExprType::Str)?.as_symbol()?; let attrs = parse_key_values(&mut iter); - Ok(Element { - name, - attrs, - children: iter.collect_vec(), - }) - } else { - Err(AstError::UnexpectedNode) - } + Element { name, attrs, children: iter.collect_vec() } + }) } } -fn parse_key_values>( - iter: &mut itertools::PutBack, -) -> HashMap { +fn parse_key_values>(iter: &mut itertools::PutBack) -> HashMap { let mut data = HashMap::new(); loop { match iter.next() { @@ -126,10 +110,7 @@ mod test { fn test() { let parser = parser::ExprParser::new(); assert_eq!( - Element::::from_expr( - parser.parse("(box :bar 12 :baz \"hi\" foo (bar))").unwrap() - ) - .unwrap(), + Element::::from_expr(parser.parse("(box :bar 12 :baz \"hi\" foo (bar))").unwrap()).unwrap(), Element { name: "box".to_string(), attrs: maplit::hashmap! { @@ -139,10 +120,7 @@ mod test { }, children: vec![ Expr::Symbol(Span(23, 26), "foo".to_string()), - Expr::List( - Span(27, 32), - vec![Expr::Symbol(Span(28, 31), "bar".to_string())] - ), + Expr::List(Span(27, 32), vec![Expr::Symbol(Span(28, 31), "bar".to_string())]), ], } ); diff --git a/src/error.rs b/src/error.rs index 23948a0..e472763 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,18 +1,52 @@ -use crate::Expr; +use crate::{Expr, ExprType, Span}; +use thiserror::Error; -#[derive(Debug, PartialEq, Eq)] +pub type AstResult = Result; + +#[derive(Debug, PartialEq, Eq, Error)] pub enum AstError { - UnexpectedNode, - InvalidDefinition, - WrongExprType(Expr), - MissingNode, + #[error("Definition invalid")] + InvalidDefinition(Option), + #[error("Expected a {1}, but got nothing")] + MissingNode(Option, ExprType), + #[error("Wrong type of expression: Expected {1} but got {2}")] + WrongExprType(Option, ExprType, Expr), +} + +pub fn spanned(span: Span, err: impl Into) -> AstError { + use AstError::*; + match err.into() { + AstError::InvalidDefinition(None) => AstError::InvalidDefinition(Some(span)), + AstError::MissingNode(None, x) => AstError::MissingNode(Some(span), x), + AstError::WrongExprType(None, x, y) => AstError::WrongExprType(Some(span), x, y), + x => x, + } } pub trait OptionAstErrorExt { - fn or_missing(self) -> Result; + fn or_missing(self, t: ExprType) -> Result; } impl OptionAstErrorExt for Option { - fn or_missing(self) -> Result { - self.ok_or(AstError::MissingNode) + fn or_missing(self, t: ExprType) -> Result { + self.ok_or(AstError::MissingNode(None, t)) } } + +pub trait AstResultExt { + fn at(self, span: Span) -> Result; +} + +impl> AstResultExt for Result { + fn at(self, span: Span) -> Result { + self.map_err(|err| spanned(span, err)) + } +} + +#[macro_export] +macro_rules! spanned { + ($span:expr, $block:expr) => {{ + let span = $span; + let result: Result<_, AstError> = try { $block }; + result.at(span) + }}; +} diff --git a/src/main.rs b/src/main.rs index 7bc52ee..cf327f3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,17 +1,18 @@ #![allow(unused_imports)] #![allow(unused)] +#![feature(try_blocks)] mod config; mod error; use error::AstError; -use std::ops::Deref; +use std::{fmt::Display, ops::Deref}; use itertools::Itertools; use lalrpop_util::lalrpop_mod; -//mod lexer; +// mod lexer; lalrpop_mod!(pub parser); @@ -24,6 +25,23 @@ impl std::fmt::Display for Span { } } +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +pub enum ExprType { + List, + Table, + Keyword, + Symbol, + Str, + Number, + Comment, +} + +impl Display for ExprType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + #[derive(Debug, PartialEq, Eq, Clone)] pub enum Expr { List(Span, Vec), @@ -32,28 +50,50 @@ pub enum Expr { Symbol(Span, String), Str(Span, String), Number(Span, i32), - Comment, + Comment(Span), +} + +impl From for ExprType { + fn from(x: Expr) -> Self { + match x { + Expr::List(..) => ExprType::List, + Expr::Table(..) => ExprType::Table, + Expr::Keyword(..) => ExprType::Keyword, + Expr::Symbol(..) => ExprType::Symbol, + Expr::Str(..) => ExprType::Str, + Expr::Number(..) => ExprType::Number, + Expr::Comment(_) => ExprType::Number, + } + } } macro_rules! as_func { - ($name:ident<$t:ty> = $p:pat => $value:expr) => { + ($exprtype:expr, $name:ident < $t:ty > = $p:pat => $value:expr) => { fn $name(self) -> Result<$t, AstError> { match self { $p => Ok($value), - x => Err(AstError::WrongExprType(x)), + x => Err(AstError::WrongExprType(Some(x.span()), $exprtype, x)), } } }; } impl Expr { - as_func!(as_str = Expr::Str(_, x) => x); - as_func!(as_symbol = Expr::Symbol(_, x) => x); + as_func!(ExprType::Str, as_str = Expr::Str(_, x) => x); - fn is_keyword(&self) -> bool { + as_func!(ExprType::Symbol, as_symbol = Expr::Symbol(_, x) => x); + + as_func!(ExprType::List, as_list> = Expr::List(_, x) => x); + + pub fn span(&self) -> Span { match self { - Expr::Keyword(_, _) => true, - _ => false, + Expr::List(span, _) => *span, + Expr::Table(span, _) => *span, + Expr::Keyword(span, _) => *span, + Expr::Symbol(span, _) => *span, + Expr::Str(span, _) => *span, + Expr::Number(span, _) => *span, + Expr::Comment(span) => *span, } } } @@ -64,15 +104,11 @@ impl std::fmt::Display for Expr { match self { Number(_, x) => write!(f, "{}", x), List(_, x) => write!(f, "({})", x.iter().map(|e| format!("{}", e)).join(" ")), - Table(_, x) => write!( - f, - "{{{}}}", - x.iter().map(|(k, v)| format!("{} {}", k, v)).join(" ") - ), + Table(_, x) => write!(f, "{{{}}}", x.iter().map(|(k, v)| format!("{} {}", k, v)).join(" ")), Keyword(_, x) => write!(f, "{}", x), Symbol(_, x) => write!(f, "{}", x), Str(_, x) => write!(f, "{}", x), - Comment => write!(f, ""), + Comment(_) => write!(f, ""), } } } @@ -93,28 +129,28 @@ macro_rules! test_p { #[test] fn test() { - //test_p!("1"); - //test_p!("(12)"); - //test_p!("(1 2)"); - //test_p!("(1 :foo 1)"); - //test_p!("(:foo 1)"); - //test_p!("(:foo->: 1)"); - //test_p!("(foo 1)"); - //test_p!("(lol😄 1)"); + // test_p!("1"); + // test_p!("(12)"); + // test_p!("(1 2)"); + // test_p!("(1 :foo 1)"); + // test_p!("(:foo 1)"); + // test_p!("(:foo->: 1)"); + // test_p!("(foo 1)"); + // test_p!("(lol😄 1)"); - //test_p!(r#"(test "hi")"#); - //test_p!(r#"(test "h\"i")"#); - //test_p!(r#"(test " hi ")"#); + // test_p!(r#"(test "hi")"#); + // test_p!(r#"(test "h\"i")"#); + // test_p!(r#"(test " hi ")"#); - //test_p!("(+ (1 2 (* 2 5)))"); + // test_p!("(+ (1 2 (* 2 5)))"); - //test_p!(r#"{:key value 12 "hi" (test) (1 2 3)}"#); + // test_p!(r#"{:key value 12 "hi" (test) (1 2 3)}"#); - //test_p!(r#"; test"#); - //test_p!( - //r#"(f arg ; test - //arg2)"# + // test_p!(r#"; test"#); + // test_p!( + // r#"(f arg ; test + // arg2)"# //); - //println!("\n\n\n\n\n\n"); + // println!("\n\n\n\n\n\n"); } diff --git a/src/parser.lalrpop b/src/parser.lalrpop index 050b13b..99e511c 100644 --- a/src/parser.lalrpop +++ b/src/parser.lalrpop @@ -15,7 +15,7 @@ pub Expr: Expr = { => x, => Expr::Str(Span(l, r), x), => Expr::Number(Span(l, r), x), - Comment => Expr::Comment, + Comment => Expr::Comment(Span(l, r)), }; Keyword: Expr = => Expr::Keyword(Span(l, r), x.to_string()); From dedf40d57ea57e7b856d6001f9315e8b6613f6d7 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Fri, 2 Jul 2021 18:20:39 +0200 Subject: [PATCH 013/137] more span information --- src/config.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/config.rs b/src/config.rs index 9941e54..e1053d4 100644 --- a/src/config.rs +++ b/src/config.rs @@ -41,10 +41,12 @@ pub struct Definitional { name: String, attrs: HashMap, children: Vec, + span: Span, } impl FromExpr for Definitional { fn from_expr(e: Expr) -> AstResult { + let span = e.span(); spanned!(e.span(), { let list = e.as_list()?; let mut iter = itertools::put_back(list.into_iter()); @@ -54,7 +56,7 @@ impl FromExpr for Definitional { let attrs = parse_key_values(&mut iter); let children = iter.map(|x| T::from_expr(x)).collect::>>()?; - Definitional { def_type, name, attrs, children } + Definitional { span, def_type, name, attrs, children } }) } } @@ -63,10 +65,12 @@ pub struct Element { name: String, attrs: HashMap, children: Vec, + span: Span, } impl FromExpr for Element { fn from_expr(e: Expr) -> AstResult { + let span = e.span(); spanned!(e.span(), { let list = e.as_list()?; let mut iter = itertools::put_back(list.into_iter()); @@ -74,7 +78,7 @@ impl FromExpr for Element { let name = iter.next().or_missing(ExprType::Str)?.as_symbol()?; let attrs = parse_key_values(&mut iter); - Element { name, attrs, children: iter.collect_vec() } + Element { span, name, attrs, children: iter.collect_vec() } }) } } @@ -112,6 +116,7 @@ mod test { assert_eq!( Element::::from_expr(parser.parse("(box :bar 12 :baz \"hi\" foo (bar))").unwrap()).unwrap(), Element { + span: Span(0, 33), name: "box".to_string(), attrs: maplit::hashmap! { ":bar".to_string() => Expr::Number(Span(10, 12), 12), From 6a4d7361f05d63a4f1e8a1a932a65c601b4f6101 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Fri, 2 Jul 2021 19:44:10 +0200 Subject: [PATCH 014/137] make more use of generics, add tests --- Cargo.lock | 157 ++++++++++++++++-- Cargo.toml | 5 +- src/config.rs | 66 ++++---- src/main.rs | 54 +++--- .../eww_config__config__test__test-2.snap | 31 ++++ .../eww_config__config__test__test.snap | 34 ++++ src/snapshots/eww_config__test-10.snap | 20 +++ src/snapshots/eww_config__test-11.snap | 20 +++ src/snapshots/eww_config__test-12.snap | 46 +++++ src/snapshots/eww_config__test-13.snap | 60 +++++++ src/snapshots/eww_config__test-14.snap | 10 ++ src/snapshots/eww_config__test-15.snap | 27 +++ src/snapshots/eww_config__test-2.snap | 16 ++ src/snapshots/eww_config__test-3.snap | 20 +++ src/snapshots/eww_config__test-4.snap | 24 +++ src/snapshots/eww_config__test-5.snap | 20 +++ src/snapshots/eww_config__test-6.snap | 20 +++ src/snapshots/eww_config__test-7.snap | 20 +++ src/snapshots/eww_config__test-8.snap | 20 +++ src/snapshots/eww_config__test-9.snap | 20 +++ src/snapshots/eww_config__test.snap | 11 ++ 21 files changed, 627 insertions(+), 74 deletions(-) create mode 100644 src/snapshots/eww_config__config__test__test-2.snap create mode 100644 src/snapshots/eww_config__config__test__test.snap create mode 100644 src/snapshots/eww_config__test-10.snap create mode 100644 src/snapshots/eww_config__test-11.snap create mode 100644 src/snapshots/eww_config__test-12.snap create mode 100644 src/snapshots/eww_config__test-13.snap create mode 100644 src/snapshots/eww_config__test-14.snap create mode 100644 src/snapshots/eww_config__test-15.snap create mode 100644 src/snapshots/eww_config__test-2.snap create mode 100644 src/snapshots/eww_config__test-3.snap create mode 100644 src/snapshots/eww_config__test-4.snap create mode 100644 src/snapshots/eww_config__test-5.snap create mode 100644 src/snapshots/eww_config__test-6.snap create mode 100644 src/snapshots/eww_config__test-7.snap create mode 100644 src/snapshots/eww_config__test-8.snap create mode 100644 src/snapshots/eww_config__test-9.snap create mode 100644 src/snapshots/eww_config__test.snap diff --git a/Cargo.lock b/Cargo.lock index 3aaeee1..b50e278 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -93,6 +93,19 @@ 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 = "constant_time_eq" version = "0.1.5" @@ -133,6 +146,12 @@ dependencies = [ "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" @@ -148,6 +167,25 @@ dependencies = [ "log", ] +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + +[[package]] +name = "eww_config" +version = "0.1.0" +dependencies = [ + "insta", + "itertools", + "lalrpop", + "lalrpop-util", + "maplit", + "regex", + "thiserror", +] + [[package]] name = "fixedbitset" version = "0.2.0" @@ -190,6 +228,21 @@ dependencies = [ "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.0" @@ -199,6 +252,12 @@ 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.5" @@ -243,6 +302,12 @@ version = "0.2.94" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e" +[[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" @@ -270,18 +335,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" -[[package]] -name = "nomwut" -version = "0.1.0" -dependencies = [ - "itertools", - "lalrpop", - "lalrpop-util", - "maplit", - "regex", - "thiserror", -] - [[package]] name = "petgraph" version = "0.5.1" @@ -377,6 +430,61 @@ dependencies = [ "crossbeam-utils", ] +[[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 = "siphasher" version = "0.3.5" @@ -417,6 +525,16 @@ dependencies = [ "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.25" @@ -452,6 +570,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +[[package]] +name = "uuid" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" + [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" @@ -479,3 +603,12 @@ 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", +] diff --git a/Cargo.toml b/Cargo.toml index dfcdf09..650302b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "nomwut" +name = "eww_config" version = "0.1.0" authors = ["elkowar <5300871+elkowar@users.noreply.github.com>"] edition = "2018" @@ -17,3 +17,6 @@ maplit = "1.0" [build-dependencies] lalrpop = "0.19.5" + +[dev-dependencies] +insta = "1.7" diff --git a/src/config.rs b/src/config.rs index e1053d4..2efca23 100644 --- a/src/config.rs +++ b/src/config.rs @@ -19,6 +19,7 @@ impl FromExpr for Expr { } } +#[derive(Debug, Eq, PartialEq)] pub enum DefType { Widget, } @@ -36,15 +37,16 @@ impl FromExpr for DefType { } } -pub struct Definitional { +#[derive(Debug, Eq, PartialEq)] +pub struct Definitional { def_type: DefType, name: String, - attrs: HashMap, - children: Vec, + attrs: HashMap, + children: Vec, span: Span, } -impl FromExpr for Definitional { +impl FromExpr for Definitional { fn from_expr(e: Expr) -> AstResult { let span = e.span(); spanned!(e.span(), { @@ -52,23 +54,24 @@ impl FromExpr for Definitional { let mut iter = itertools::put_back(list.into_iter()); let def_type = DefType::from_expr(iter.next().or_missing(ExprType::Symbol)?)?; - let name = iter.next().or_missing(ExprType::Str)?.as_str()?; - let attrs = parse_key_values(&mut iter); + let name = iter.next().or_missing(ExprType::Symbol)?.as_symbol()?; + let attrs = parse_key_values(&mut iter)?; + let children = iter.map(|x| C::from_expr(x)).collect::>>()?; - let children = iter.map(|x| T::from_expr(x)).collect::>>()?; Definitional { span, def_type, name, attrs, children } }) } } + #[derive(Debug, Eq, PartialEq)] -pub struct Element { +pub struct Element { name: String, - attrs: HashMap, - children: Vec, + attrs: HashMap, + children: Vec, span: Span, } -impl FromExpr for Element { +impl FromExpr for Element { fn from_expr(e: Expr) -> AstResult { let span = e.span(); spanned!(e.span(), { @@ -76,31 +79,33 @@ impl FromExpr for Element { let mut iter = itertools::put_back(list.into_iter()); let name = iter.next().or_missing(ExprType::Str)?.as_symbol()?; - let attrs = parse_key_values(&mut iter); + let attrs = parse_key_values(&mut iter)?; + let children = iter.map(C::from_expr).collect::>>()?; - Element { span, name, attrs, children: iter.collect_vec() } + Element { span, name, attrs, children } }) } } -fn parse_key_values>(iter: &mut itertools::PutBack) -> HashMap { +/// Parse consecutive `:keyword value` pairs from an expression iterator into a HashMap. Transforms the keys using the FromExpr trait. +fn parse_key_values>(iter: &mut itertools::PutBack) -> AstResult> { let mut data = HashMap::new(); loop { match iter.next() { Some(Expr::Keyword(span, kw)) => match iter.next() { Some(value) => { - data.insert(kw, value); + data.insert(kw, T::from_expr(value)?); } None => { iter.put_back(Expr::Keyword(span, kw)); - return data; + return Ok(data); } }, Some(expr) => { iter.put_back(expr); - return data; + return Ok(data); } - None => return data, + None => return Ok(data), } } } @@ -109,25 +114,18 @@ fn parse_key_values>(iter: &mut itertools::PutBack) mod test { use super::*; + use insta; #[test] fn test() { let parser = parser::ExprParser::new(); - assert_eq!( - Element::::from_expr(parser.parse("(box :bar 12 :baz \"hi\" foo (bar))").unwrap()).unwrap(), - Element { - span: Span(0, 33), - name: "box".to_string(), - attrs: maplit::hashmap! { - ":bar".to_string() => Expr::Number(Span(10, 12), 12), - ":baz".to_string() => Expr::Str(Span(18, 22), "hi".to_string()), - - }, - children: vec![ - Expr::Symbol(Span(23, 26), "foo".to_string()), - Expr::List(Span(27, 32), vec![Expr::Symbol(Span(28, 31), "bar".to_string())]), - ], - } - ); + insta::with_settings!({sort_maps => true}, { + insta::assert_debug_snapshot!( + Element::::from_expr(parser.parse("(box :bar 12 :baz \"hi\" foo (bar))").unwrap()).unwrap() + ); + insta::assert_debug_snapshot!( + Definitional::::from_expr(parser.parse("(defwidget box (child) (child2))").unwrap()).unwrap() + ); + }); } } diff --git a/src/main.rs b/src/main.rs index cf327f3..2d03ab2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,12 +16,18 @@ use lalrpop_util::lalrpop_mod; lalrpop_mod!(pub parser); -#[derive(Debug, Eq, PartialEq, Clone, Copy)] +#[derive(Eq, PartialEq, Clone, Copy)] pub struct Span(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) + 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) } } @@ -118,39 +124,33 @@ fn main() {} #[allow(unused_macros)] macro_rules! test_p { ($e:expr) => { - let e = $e; let p = parser::ExprParser::new(); - match p.parse(e) { - Ok(res) => println!("{}\n=> {}\n", e, res), - Err(e) => eprintln!("{}", e), - } + insta::assert_debug_snapshot!(p.parse($e)) }; } #[test] fn test() { - // test_p!("1"); - // test_p!("(12)"); - // test_p!("(1 2)"); - // test_p!("(1 :foo 1)"); - // test_p!("(:foo 1)"); - // test_p!("(:foo->: 1)"); - // test_p!("(foo 1)"); - // test_p!("(lol😄 1)"); + test_p!("1"); + test_p!("(12)"); + test_p!("(1 2)"); + test_p!("(1 :foo 1)"); + test_p!("(:foo 1)"); + test_p!("(:foo->: 1)"); + test_p!("(foo 1)"); + test_p!("(lol😄 1)"); - // test_p!(r#"(test "hi")"#); - // test_p!(r#"(test "h\"i")"#); - // test_p!(r#"(test " hi ")"#); + test_p!(r#"(test "hi")"#); + test_p!(r#"(test "h\"i")"#); + test_p!(r#"(test " hi ")"#); - // test_p!("(+ (1 2 (* 2 5)))"); + test_p!("(+ (1 2 (* 2 5)))"); - // test_p!(r#"{:key value 12 "hi" (test) (1 2 3)}"#); + test_p!(r#"{:key value 12 "hi" (test) (1 2 3)}"#); - // test_p!(r#"; test"#); - // test_p!( - // r#"(f arg ; test - // arg2)"# - //); - - // println!("\n\n\n\n\n\n"); + test_p!(r#"; test"#); + test_p!( + r#"(f arg ; test + arg2)"# + ); } diff --git a/src/snapshots/eww_config__config__test__test-2.snap b/src/snapshots/eww_config__config__test__test-2.snap new file mode 100644 index 0000000..b983ed4 --- /dev/null +++ b/src/snapshots/eww_config__config__test__test-2.snap @@ -0,0 +1,31 @@ +--- +source: src/config.rs +expression: "Definitional::::from_expr(parser.parse(\"(defwidget box (child) (child2))\").unwrap()).unwrap()" + +--- +Definitional { + def_type: Widget, + name: "box", + attrs: {}, + children: [ + List( + 15..22, + [ + Symbol( + 16..21, + "child", + ), + ], + ), + List( + 23..31, + [ + Symbol( + 24..30, + "child2", + ), + ], + ), + ], + span: 0..32, +} diff --git a/src/snapshots/eww_config__config__test__test.snap b/src/snapshots/eww_config__config__test__test.snap new file mode 100644 index 0000000..cb3999e --- /dev/null +++ b/src/snapshots/eww_config__config__test__test.snap @@ -0,0 +1,34 @@ +--- +source: src/config.rs +expression: "Element::::from_expr(parser.parse(\"(box :bar 12 :baz \\\"hi\\\" foo (bar))\").unwrap()).unwrap()" + +--- +Element { + name: "box", + attrs: { + ":baz": Str( + 18..22, + "hi", + ), + ":bar": Number( + 10..12, + 12, + ), + }, + children: [ + Symbol( + 23..26, + "foo", + ), + List( + 27..32, + [ + Symbol( + 28..31, + "bar", + ), + ], + ), + ], + span: 0..33, +} diff --git a/src/snapshots/eww_config__test-10.snap b/src/snapshots/eww_config__test-10.snap new file mode 100644 index 0000000..2d0f6c5 --- /dev/null +++ b/src/snapshots/eww_config__test-10.snap @@ -0,0 +1,20 @@ +--- +source: src/main.rs +expression: "p.parse(r#\"(test \"h\\\"i\")\"#)" + +--- +Ok( + List( + 0..13, + [ + Symbol( + 1..5, + "test", + ), + Str( + 6..12, + "h\\\"i", + ), + ], + ), +) diff --git a/src/snapshots/eww_config__test-11.snap b/src/snapshots/eww_config__test-11.snap new file mode 100644 index 0000000..ae042cc --- /dev/null +++ b/src/snapshots/eww_config__test-11.snap @@ -0,0 +1,20 @@ +--- +source: src/main.rs +expression: "p.parse(r#\"(test \" hi \")\"#)" + +--- +Ok( + List( + 0..13, + [ + Symbol( + 1..5, + "test", + ), + Str( + 6..12, + " hi ", + ), + ], + ), +) diff --git a/src/snapshots/eww_config__test-12.snap b/src/snapshots/eww_config__test-12.snap new file mode 100644 index 0000000..96fcd40 --- /dev/null +++ b/src/snapshots/eww_config__test-12.snap @@ -0,0 +1,46 @@ +--- +source: src/main.rs +expression: "p.parse(\"(+ (1 2 (* 2 5)))\")" + +--- +Ok( + List( + 0..17, + [ + Symbol( + 1..2, + "+", + ), + List( + 3..16, + [ + Number( + 4..5, + 1, + ), + Number( + 6..7, + 2, + ), + List( + 8..15, + [ + Symbol( + 9..10, + "*", + ), + Number( + 11..12, + 2, + ), + Number( + 13..14, + 5, + ), + ], + ), + ], + ), + ], + ), +) diff --git a/src/snapshots/eww_config__test-13.snap b/src/snapshots/eww_config__test-13.snap new file mode 100644 index 0000000..aeed887 --- /dev/null +++ b/src/snapshots/eww_config__test-13.snap @@ -0,0 +1,60 @@ +--- +source: src/main.rs +expression: "p.parse(r#\"{:key value 12 \"hi\" (test) (1 2 3)}\"#)" + +--- +Ok( + Table( + 0..35, + [ + ( + Keyword( + 1..5, + ":key", + ), + Symbol( + 6..11, + "value", + ), + ), + ( + Number( + 12..14, + 12, + ), + Str( + 15..19, + "hi", + ), + ), + ( + List( + 20..26, + [ + Symbol( + 21..25, + "test", + ), + ], + ), + List( + 27..34, + [ + Number( + 28..29, + 1, + ), + Number( + 30..31, + 2, + ), + Number( + 32..33, + 3, + ), + ], + ), + ), + ], + ), +) diff --git a/src/snapshots/eww_config__test-14.snap b/src/snapshots/eww_config__test-14.snap new file mode 100644 index 0000000..5b8d869 --- /dev/null +++ b/src/snapshots/eww_config__test-14.snap @@ -0,0 +1,10 @@ +--- +source: src/main.rs +expression: "p.parse(r#\"; test\"#)" + +--- +Ok( + Comment( + 0..6, + ), +) diff --git a/src/snapshots/eww_config__test-15.snap b/src/snapshots/eww_config__test-15.snap new file mode 100644 index 0000000..9b4f877 --- /dev/null +++ b/src/snapshots/eww_config__test-15.snap @@ -0,0 +1,27 @@ +--- +source: src/main.rs +expression: "p.parse(r#\"(f arg ; test\n arg2)\"#)" + +--- +Ok( + List( + 0..24, + [ + Symbol( + 1..2, + "f", + ), + Symbol( + 3..6, + "arg", + ), + Comment( + 7..13, + ), + Symbol( + 19..23, + "arg2", + ), + ], + ), +) diff --git a/src/snapshots/eww_config__test-2.snap b/src/snapshots/eww_config__test-2.snap new file mode 100644 index 0000000..f16a309 --- /dev/null +++ b/src/snapshots/eww_config__test-2.snap @@ -0,0 +1,16 @@ +--- +source: src/main.rs +expression: "p.parse(\"(12)\")" + +--- +Ok( + List( + 0..4, + [ + Number( + 1..3, + 12, + ), + ], + ), +) diff --git a/src/snapshots/eww_config__test-3.snap b/src/snapshots/eww_config__test-3.snap new file mode 100644 index 0000000..99b52c6 --- /dev/null +++ b/src/snapshots/eww_config__test-3.snap @@ -0,0 +1,20 @@ +--- +source: src/main.rs +expression: "p.parse(\"(1 2)\")" + +--- +Ok( + List( + 0..5, + [ + Number( + 1..2, + 1, + ), + Number( + 3..4, + 2, + ), + ], + ), +) diff --git a/src/snapshots/eww_config__test-4.snap b/src/snapshots/eww_config__test-4.snap new file mode 100644 index 0000000..6ecf27d --- /dev/null +++ b/src/snapshots/eww_config__test-4.snap @@ -0,0 +1,24 @@ +--- +source: src/main.rs +expression: "p.parse(\"(1 :foo 1)\")" + +--- +Ok( + List( + 0..10, + [ + Number( + 1..2, + 1, + ), + Keyword( + 3..7, + ":foo", + ), + Number( + 8..9, + 1, + ), + ], + ), +) diff --git a/src/snapshots/eww_config__test-5.snap b/src/snapshots/eww_config__test-5.snap new file mode 100644 index 0000000..ff6efe3 --- /dev/null +++ b/src/snapshots/eww_config__test-5.snap @@ -0,0 +1,20 @@ +--- +source: src/main.rs +expression: "p.parse(\"(:foo 1)\")" + +--- +Ok( + List( + 0..8, + [ + Keyword( + 1..5, + ":foo", + ), + Number( + 6..7, + 1, + ), + ], + ), +) diff --git a/src/snapshots/eww_config__test-6.snap b/src/snapshots/eww_config__test-6.snap new file mode 100644 index 0000000..919dd49 --- /dev/null +++ b/src/snapshots/eww_config__test-6.snap @@ -0,0 +1,20 @@ +--- +source: src/main.rs +expression: "p.parse(\"(:foo->: 1)\")" + +--- +Ok( + List( + 0..11, + [ + Keyword( + 1..8, + ":foo->:", + ), + Number( + 9..10, + 1, + ), + ], + ), +) diff --git a/src/snapshots/eww_config__test-7.snap b/src/snapshots/eww_config__test-7.snap new file mode 100644 index 0000000..0c038c5 --- /dev/null +++ b/src/snapshots/eww_config__test-7.snap @@ -0,0 +1,20 @@ +--- +source: src/main.rs +expression: "p.parse(\"(foo 1)\")" + +--- +Ok( + List( + 0..7, + [ + Symbol( + 1..4, + "foo", + ), + Number( + 5..6, + 1, + ), + ], + ), +) diff --git a/src/snapshots/eww_config__test-8.snap b/src/snapshots/eww_config__test-8.snap new file mode 100644 index 0000000..eec1f65 --- /dev/null +++ b/src/snapshots/eww_config__test-8.snap @@ -0,0 +1,20 @@ +--- +source: src/main.rs +expression: "p.parse(\"(lol😄 1)\")" + +--- +Ok( + List( + 0..11, + [ + Symbol( + 1..8, + "lol😄", + ), + Number( + 9..10, + 1, + ), + ], + ), +) diff --git a/src/snapshots/eww_config__test-9.snap b/src/snapshots/eww_config__test-9.snap new file mode 100644 index 0000000..ce6c9db --- /dev/null +++ b/src/snapshots/eww_config__test-9.snap @@ -0,0 +1,20 @@ +--- +source: src/main.rs +expression: "p.parse(r#\"(test \"hi\")\"#)" + +--- +Ok( + List( + 0..11, + [ + Symbol( + 1..5, + "test", + ), + Str( + 6..10, + "hi", + ), + ], + ), +) diff --git a/src/snapshots/eww_config__test.snap b/src/snapshots/eww_config__test.snap new file mode 100644 index 0000000..2547c26 --- /dev/null +++ b/src/snapshots/eww_config__test.snap @@ -0,0 +1,11 @@ +--- +source: src/main.rs +expression: "p.parse(\"1\")" + +--- +Ok( + Number( + 0..1, + 1, + ), +) From 86c02e86bbf5ac34780c39105775075665bc2bb6 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Fri, 2 Jul 2021 22:00:03 +0200 Subject: [PATCH 015/137] cleanup --- src/config.rs | 74 +++++++------------ src/error.rs | 4 +- src/expr.rs | 173 +++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 104 +-------------------------- src/parser.lalrpop | 3 +- 5 files changed, 203 insertions(+), 155 deletions(-) create mode 100644 src/expr.rs diff --git a/src/config.rs b/src/config.rs index 2efca23..3b9b104 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,9 +1,14 @@ -use std::{collections::HashMap, iter::FromIterator}; - -use super::*; -use crate::error::*; +use crate::{ + error::*, + expr::{Expr, ExprIterator, ExprType, Span}, + parser, spanned, +}; use itertools::Itertools; -use std::collections::LinkedList; +use std::{ + collections::{HashMap, LinkedList}, + iter::FromIterator, + str::FromStr, +}; type VarName = String; type AttrValue = String; @@ -24,15 +29,13 @@ pub enum DefType { Widget, } -impl FromExpr for DefType { - fn from_expr(e: Expr) -> AstResult { - if let Expr::Symbol(span, sym) = e { - match sym.as_str() { - "defwidget" => Ok(DefType::Widget), - _ => Err(AstError::InvalidDefinition(Some(span))), - } - } else { - Err(AstError::WrongExprType(Some(e.span()), ExprType::Symbol, e)) +impl FromStr for DefType { + type Err = (); + + fn from_str(s: &str) -> Result { + match s { + "defwidget" => Ok(DefType::Widget), + _ => Err(()), } } } @@ -51,13 +54,13 @@ impl FromExpr for Definitional { let span = e.span(); spanned!(e.span(), { let list = e.as_list()?; - let mut iter = itertools::put_back(list.into_iter()); + let mut iter = ExprIterator::new(list.into_iter()); + let (span, def_type) = iter.next_symbol()?; + let def_type = def_type.parse().map_err(|_| AstError::InvalidDefinition(Some(span)))?; - let def_type = DefType::from_expr(iter.next().or_missing(ExprType::Symbol)?)?; - let name = iter.next().or_missing(ExprType::Symbol)?.as_symbol()?; - let attrs = parse_key_values(&mut iter)?; + let (_, name) = iter.next_symbol()?; + let attrs = iter.key_values()?; let children = iter.map(|x| C::from_expr(x)).collect::>>()?; - Definitional { span, def_type, name, attrs, children } }) } @@ -76,40 +79,15 @@ impl FromExpr for Element { let span = e.span(); spanned!(e.span(), { let list = e.as_list()?; - let mut iter = itertools::put_back(list.into_iter()); - - let name = iter.next().or_missing(ExprType::Str)?.as_symbol()?; - let attrs = parse_key_values(&mut iter)?; - let children = iter.map(C::from_expr).collect::>>()?; - + let mut iter = ExprIterator::new(list.into_iter()); + let (_, name) = iter.next_symbol()?; + let attrs = iter.key_values()?; + let children = iter.map(|x| C::from_expr(x)).collect::>>()?; Element { span, name, attrs, children } }) } } -/// Parse consecutive `:keyword value` pairs from an expression iterator into a HashMap. Transforms the keys using the FromExpr trait. -fn parse_key_values>(iter: &mut itertools::PutBack) -> AstResult> { - let mut data = HashMap::new(); - loop { - match iter.next() { - Some(Expr::Keyword(span, kw)) => match iter.next() { - Some(value) => { - data.insert(kw, T::from_expr(value)?); - } - None => { - iter.put_back(Expr::Keyword(span, kw)); - return Ok(data); - } - }, - Some(expr) => { - iter.put_back(expr); - return Ok(data); - } - None => return Ok(data), - } - } -} - #[cfg(test)] mod test { diff --git a/src/error.rs b/src/error.rs index e472763..82a722c 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,4 +1,4 @@ -use crate::{Expr, ExprType, Span}; +use crate::expr::{Expr, ExprType, Span}; use thiserror::Error; pub type AstResult = Result; @@ -10,7 +10,7 @@ pub enum AstError { #[error("Expected a {1}, but got nothing")] MissingNode(Option, ExprType), #[error("Wrong type of expression: Expected {1} but got {2}")] - WrongExprType(Option, ExprType, Expr), + WrongExprType(Option, ExprType, ExprType), } pub fn spanned(span: Span, err: impl Into) -> AstError { diff --git a/src/expr.rs b/src/expr.rs new file mode 100644 index 0000000..099b5e2 --- /dev/null +++ b/src/expr.rs @@ -0,0 +1,173 @@ +use itertools::Itertools; +use std::collections::HashMap; + +use crate::{config::FromExpr, error::*}; +use std::fmt::Display; + +#[derive(Eq, PartialEq, Clone, Copy)] +pub struct Span(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) + } +} + +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +pub enum ExprType { + List, + Table, + Keyword, + Symbol, + Str, + Number, + Comment, +} + +impl Display for ExprType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum Expr { + List(Span, Vec), + Table(Span, Vec<(Expr, Expr)>), + Keyword(Span, String), + Symbol(Span, String), + Str(Span, String), + Number(Span, i32), + Comment(Span), +} + +macro_rules! as_func { + ($exprtype:expr, $name:ident < $t:ty > = $p:pat => $value:expr) => { + pub fn $name(self) -> Result<$t, AstError> { + match self { + $p => Ok($value), + x => Err(AstError::WrongExprType(Some(x.span()), $exprtype, x.expr_type())), + } + } + }; +} + +impl Expr { + as_func!(ExprType::Str, as_str = Expr::Str(_, x) => x); + + as_func!(ExprType::Symbol, as_symbol = Expr::Symbol(_, x) => x); + + as_func!(ExprType::List, as_list> = Expr::List(_, x) => x); + + pub fn expr_type(&self) -> ExprType { + match self { + Expr::List(..) => ExprType::List, + Expr::Table(..) => ExprType::Table, + Expr::Keyword(..) => ExprType::Keyword, + Expr::Symbol(..) => ExprType::Symbol, + Expr::Str(..) => ExprType::Str, + Expr::Number(..) => ExprType::Number, + Expr::Comment(_) => ExprType::Number, + } + } + + pub fn span(&self) -> Span { + match self { + Expr::List(span, _) => *span, + Expr::Table(span, _) => *span, + Expr::Keyword(span, _) => *span, + Expr::Symbol(span, _) => *span, + Expr::Str(span, _) => *span, + Expr::Number(span, _) => *span, + Expr::Comment(span) => *span, + } + } +} + +impl std::fmt::Display for Expr { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use Expr::*; + match self { + Number(_, x) => write!(f, "{}", x), + List(_, x) => write!(f, "({})", x.iter().map(|e| format!("{}", e)).join(" ")), + Table(_, x) => write!(f, "{{{}}}", x.iter().map(|(k, v)| format!("{} {}", k, v)).join(" ")), + Keyword(_, x) => write!(f, "{}", x), + Symbol(_, x) => write!(f, "{}", x), + Str(_, x) => write!(f, "{}", x), + Comment(_) => write!(f, ""), + } + } +} + +pub struct ExprIterator> { + iter: itertools::PutBack, +} + +macro_rules! return_or_put_back { + ($name:ident, $expr_type:expr, $t:ty = $p:pat => $ret:expr) => { + pub fn $name(&mut self) -> AstResult<$t> { + let expr_type = $expr_type; + match self.next() { + Some($p) => Ok($ret), + Some(other) => { + let span = other.span(); + let actual_type = other.expr_type(); + self.iter.put_back(other); + Err(AstError::WrongExprType(Some(span), expr_type, actual_type)) + } + None => Err(AstError::MissingNode(None, expr_type)), + } + } + }; +} + +impl> ExprIterator { + return_or_put_back!(next_symbol, ExprType::Symbol, (Span, String) = Expr::Symbol(span, x) => (span, x)); + + return_or_put_back!(next_string, ExprType::Str, (Span, String) = Expr::Str(span, x) => (span, x)); + + pub fn new(iter: I) -> Self { + ExprIterator { iter: itertools::put_back(iter) } + } + + pub fn key_values(&mut self) -> AstResult> { + parse_key_values(&mut self.iter) + } +} + +impl> Iterator for ExprIterator { + type Item = Expr; + + fn next(&mut self) -> Option { + self.iter.next() + } +} + +/// Parse consecutive `:keyword value` pairs from an expression iterator into a HashMap. Transforms the keys using the FromExpr trait. +fn parse_key_values>(iter: &mut itertools::PutBack) -> AstResult> { + let mut data = HashMap::new(); + loop { + match iter.next() { + Some(Expr::Keyword(span, kw)) => match iter.next() { + Some(value) => { + data.insert(kw, T::from_expr(value)?); + } + None => { + iter.put_back(Expr::Keyword(span, kw)); + return Ok(data); + } + }, + Some(expr) => { + iter.put_back(expr); + return Ok(data); + } + None => return Ok(data), + } + } +} diff --git a/src/main.rs b/src/main.rs index 2d03ab2..1462fac 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,7 @@ mod config; mod error; +mod expr; use error::AstError; use std::{fmt::Display, ops::Deref}; @@ -16,109 +17,6 @@ use lalrpop_util::lalrpop_mod; lalrpop_mod!(pub parser); -#[derive(Eq, PartialEq, Clone, Copy)] -pub struct Span(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) - } -} - -#[derive(Debug, PartialEq, Eq, Copy, Clone)] -pub enum ExprType { - List, - Table, - Keyword, - Symbol, - Str, - Number, - Comment, -} - -impl Display for ExprType { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self) - } -} - -#[derive(Debug, PartialEq, Eq, Clone)] -pub enum Expr { - List(Span, Vec), - Table(Span, Vec<(Expr, Expr)>), - Keyword(Span, String), - Symbol(Span, String), - Str(Span, String), - Number(Span, i32), - Comment(Span), -} - -impl From for ExprType { - fn from(x: Expr) -> Self { - match x { - Expr::List(..) => ExprType::List, - Expr::Table(..) => ExprType::Table, - Expr::Keyword(..) => ExprType::Keyword, - Expr::Symbol(..) => ExprType::Symbol, - Expr::Str(..) => ExprType::Str, - Expr::Number(..) => ExprType::Number, - Expr::Comment(_) => ExprType::Number, - } - } -} - -macro_rules! as_func { - ($exprtype:expr, $name:ident < $t:ty > = $p:pat => $value:expr) => { - fn $name(self) -> Result<$t, AstError> { - match self { - $p => Ok($value), - x => Err(AstError::WrongExprType(Some(x.span()), $exprtype, x)), - } - } - }; -} - -impl Expr { - as_func!(ExprType::Str, as_str = Expr::Str(_, x) => x); - - as_func!(ExprType::Symbol, as_symbol = Expr::Symbol(_, x) => x); - - as_func!(ExprType::List, as_list> = Expr::List(_, x) => x); - - pub fn span(&self) -> Span { - match self { - Expr::List(span, _) => *span, - Expr::Table(span, _) => *span, - Expr::Keyword(span, _) => *span, - Expr::Symbol(span, _) => *span, - Expr::Str(span, _) => *span, - Expr::Number(span, _) => *span, - Expr::Comment(span) => *span, - } - } -} - -impl std::fmt::Display for Expr { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - use Expr::*; - match self { - Number(_, x) => write!(f, "{}", x), - List(_, x) => write!(f, "({})", x.iter().map(|e| format!("{}", e)).join(" ")), - Table(_, x) => write!(f, "{{{}}}", x.iter().map(|(k, v)| format!("{} {}", k, v)).join(" ")), - Keyword(_, x) => write!(f, "{}", x), - Symbol(_, x) => write!(f, "{}", x), - Str(_, x) => write!(f, "{}", x), - Comment(_) => write!(f, ""), - } - } -} - fn main() {} #[allow(unused_macros)] diff --git a/src/parser.lalrpop b/src/parser.lalrpop index 99e511c..c306776 100644 --- a/src/parser.lalrpop +++ b/src/parser.lalrpop @@ -1,7 +1,6 @@ use std::str::FromStr; //use crate::lexer; -use crate::Expr; -use crate::Span; +use crate::expr::{Expr, Span}; grammar; From 1ec5a1d6bc490c88c5f76e50b29dd47efcec3665 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Sat, 3 Jul 2021 16:36:48 +0200 Subject: [PATCH 016/137] some more cleanup --- src/config.rs | 2 +- src/lib.rs | 46 ++++++++++++++++ src/main.rs | 54 ------------------- .../eww_config__config__test__test-2.snap | 2 +- .../eww_config__config__test__test.snap | 8 +-- src/snapshots/eww_config__test-15.snap | 8 +-- 6 files changed, 56 insertions(+), 64 deletions(-) create mode 100644 src/lib.rs delete mode 100644 src/main.rs diff --git a/src/config.rs b/src/config.rs index 3b9b104..28c5a66 100644 --- a/src/config.rs +++ b/src/config.rs @@ -82,7 +82,7 @@ impl FromExpr for Element { let mut iter = ExprIterator::new(list.into_iter()); let (_, name) = iter.next_symbol()?; let attrs = iter.key_values()?; - let children = iter.map(|x| C::from_expr(x)).collect::>>()?; + let children = iter.map(C::from_expr).collect::>>()?; Element { span, name, attrs, children } }) } diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..d4a6f66 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,46 @@ +#![allow(unused_imports)] +#![allow(unused)] +#![feature(try_blocks)] + +mod config; +mod error; +mod expr; +use error::AstError; + +use std::{fmt::Display, ops::Deref}; + +use itertools::Itertools; + +use lalrpop_util::lalrpop_mod; + +lalrpop_mod!(pub parser); + +macro_rules! test_parser { + ($p:expr, $($text:literal),*) => {{ + $(insta::assert_debug_snapshot!($p.parse($text));)* + }} +} + +#[test] +fn test() { + let p = parser::ExprParser::new(); + test_parser!( + p, + "1", + "(12)", + "(1 2)", + "(1 :foo 1)", + "(:foo 1)", + "(:foo->: 1)", + "(foo 1)", + "(lol😄 1)", + r#"(test "hi")"#, + r#"(test "h\"i")"#, + r#"(test " hi ")"#, + "(+ (1 2 (* 2 5)))", + r#"{:key value 12 "hi" (test) (1 2 3)}"#, + r#"; test"#, + r#"(f arg ; test + arg2)"# + ); +} diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index 1462fac..0000000 --- a/src/main.rs +++ /dev/null @@ -1,54 +0,0 @@ -#![allow(unused_imports)] -#![allow(unused)] -#![feature(try_blocks)] - -mod config; -mod error; -mod expr; -use error::AstError; - -use std::{fmt::Display, ops::Deref}; - -use itertools::Itertools; - -use lalrpop_util::lalrpop_mod; - -// mod lexer; - -lalrpop_mod!(pub parser); - -fn main() {} - -#[allow(unused_macros)] -macro_rules! test_p { - ($e:expr) => { - let p = parser::ExprParser::new(); - insta::assert_debug_snapshot!(p.parse($e)) - }; -} - -#[test] -fn test() { - test_p!("1"); - test_p!("(12)"); - test_p!("(1 2)"); - test_p!("(1 :foo 1)"); - test_p!("(:foo 1)"); - test_p!("(:foo->: 1)"); - test_p!("(foo 1)"); - test_p!("(lol😄 1)"); - - test_p!(r#"(test "hi")"#); - test_p!(r#"(test "h\"i")"#); - test_p!(r#"(test " hi ")"#); - - test_p!("(+ (1 2 (* 2 5)))"); - - test_p!(r#"{:key value 12 "hi" (test) (1 2 3)}"#); - - test_p!(r#"; test"#); - test_p!( - r#"(f arg ; test - arg2)"# - ); -} diff --git a/src/snapshots/eww_config__config__test__test-2.snap b/src/snapshots/eww_config__config__test__test-2.snap index b983ed4..39b709c 100644 --- a/src/snapshots/eww_config__config__test__test-2.snap +++ b/src/snapshots/eww_config__config__test__test-2.snap @@ -27,5 +27,5 @@ Definitional { ], ), ], - span: 0..32, + span: 1..10, } diff --git a/src/snapshots/eww_config__config__test__test.snap b/src/snapshots/eww_config__config__test__test.snap index cb3999e..b58093b 100644 --- a/src/snapshots/eww_config__config__test__test.snap +++ b/src/snapshots/eww_config__config__test__test.snap @@ -6,14 +6,14 @@ expression: "Element::::from_expr(parser.parse(\"(box :ba Element { name: "box", attrs: { - ":baz": Str( - 18..22, - "hi", - ), ":bar": Number( 10..12, 12, ), + ":baz": Str( + 18..22, + "hi", + ), }, children: [ Symbol( diff --git a/src/snapshots/eww_config__test-15.snap b/src/snapshots/eww_config__test-15.snap index 9b4f877..b2943ca 100644 --- a/src/snapshots/eww_config__test-15.snap +++ b/src/snapshots/eww_config__test-15.snap @@ -1,11 +1,11 @@ --- -source: src/main.rs -expression: "p.parse(r#\"(f arg ; test\n arg2)\"#)" +source: src/lib.rs +expression: "p.parse(r#\"(f arg ; test\n arg2)\"#)" --- Ok( List( - 0..24, + 0..27, [ Symbol( 1..2, @@ -19,7 +19,7 @@ Ok( 7..13, ), Symbol( - 19..23, + 22..26, "arg2", ), ], From af19291bf9c3433b199585a3c80b0192fe5d9377 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Sat, 3 Jul 2021 19:05:45 +0200 Subject: [PATCH 017/137] some cleanup --- src/config.rs | 51 ++----------------- src/expr.rs | 50 ++++++++++++++---- src/lib.rs | 3 +- .../eww_config__config__test__test-2.snap | 16 ++---- .../eww_config__config__test__test.snap | 23 ++------- src/snapshots/eww_config__test-10.snap | 15 ++---- src/snapshots/eww_config__test-11.snap | 15 ++---- src/snapshots/eww_config__test-12.snap | 41 ++++----------- src/snapshots/eww_config__test-13.snap | 51 +++++-------------- src/snapshots/eww_config__test-14.snap | 6 +-- src/snapshots/eww_config__test-15.snap | 22 ++------ src/snapshots/eww_config__test-16.snap | 8 +++ src/snapshots/eww_config__test-2.snap | 10 ++-- src/snapshots/eww_config__test-3.snap | 15 ++---- src/snapshots/eww_config__test-4.snap | 20 ++------ src/snapshots/eww_config__test-5.snap | 15 ++---- src/snapshots/eww_config__test-6.snap | 15 ++---- src/snapshots/eww_config__test-7.snap | 15 ++---- src/snapshots/eww_config__test-8.snap | 15 ++---- src/snapshots/eww_config__test-9.snap | 15 ++---- src/snapshots/eww_config__test.snap | 7 +-- 21 files changed, 135 insertions(+), 293 deletions(-) create mode 100644 src/snapshots/eww_config__test-16.snap diff --git a/src/config.rs b/src/config.rs index 28c5a66..b3f687e 100644 --- a/src/config.rs +++ b/src/config.rs @@ -10,6 +10,8 @@ use std::{ str::FromStr, }; +// https://michael-f-bryan.github.io/static-analyser-in-rust/book/codemap.html + type VarName = String; type AttrValue = String; type AttrName = String; @@ -24,48 +26,6 @@ impl FromExpr for Expr { } } -#[derive(Debug, Eq, PartialEq)] -pub enum DefType { - Widget, -} - -impl FromStr for DefType { - type Err = (); - - fn from_str(s: &str) -> Result { - match s { - "defwidget" => Ok(DefType::Widget), - _ => Err(()), - } - } -} - -#[derive(Debug, Eq, PartialEq)] -pub struct Definitional { - def_type: DefType, - name: String, - attrs: HashMap, - children: Vec, - span: Span, -} - -impl FromExpr for Definitional { - fn from_expr(e: Expr) -> AstResult { - let span = e.span(); - spanned!(e.span(), { - let list = e.as_list()?; - let mut iter = ExprIterator::new(list.into_iter()); - let (span, def_type) = iter.next_symbol()?; - let def_type = def_type.parse().map_err(|_| AstError::InvalidDefinition(Some(span)))?; - - let (_, name) = iter.next_symbol()?; - let attrs = iter.key_values()?; - let children = iter.map(|x| C::from_expr(x)).collect::>>()?; - Definitional { span, def_type, name, attrs, children } - }) - } -} - #[derive(Debug, Eq, PartialEq)] pub struct Element { name: String, @@ -80,8 +40,8 @@ impl FromExpr for Element { spanned!(e.span(), { let list = e.as_list()?; let mut iter = ExprIterator::new(list.into_iter()); - let (_, name) = iter.next_symbol()?; - let attrs = iter.key_values()?; + let (_, name) = iter.expect_symbol()?; + let attrs = iter.expect_key_values()?; let children = iter.map(C::from_expr).collect::>>()?; Element { span, name, attrs, children } }) @@ -101,9 +61,6 @@ mod test { insta::assert_debug_snapshot!( Element::::from_expr(parser.parse("(box :bar 12 :baz \"hi\" foo (bar))").unwrap()).unwrap() ); - insta::assert_debug_snapshot!( - Definitional::::from_expr(parser.parse("(defwidget box (child) (child2))").unwrap()).unwrap() - ); }); } } diff --git a/src/expr.rs b/src/expr.rs index 099b5e2..97a0b92 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -15,7 +15,7 @@ impl std::fmt::Display for Span { impl std::fmt::Debug for Span { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self) + write!(f, "{}..{}", self.0, self.1) } } @@ -36,7 +36,7 @@ impl Display for ExprType { } } -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(PartialEq, Eq, Clone)] pub enum Expr { List(Span, Vec), Table(Span, Vec<(Expr, Expr)>), @@ -48,22 +48,29 @@ pub enum Expr { } macro_rules! as_func { - ($exprtype:expr, $name:ident < $t:ty > = $p:pat => $value:expr) => { + ($exprtype:expr, $name:ident $nameref:ident < $t:ty > = $p:pat => $value:expr) => { pub fn $name(self) -> Result<$t, AstError> { match self { $p => Ok($value), x => Err(AstError::WrongExprType(Some(x.span()), $exprtype, x.expr_type())), } } + + pub fn $nameref(&self) -> Result<&$t, AstError> { + match self { + $p => Ok($value), + x => Err(AstError::WrongExprType(Some(x.span()), $exprtype, x.expr_type())), + } + } }; } impl Expr { - as_func!(ExprType::Str, as_str = Expr::Str(_, x) => x); + as_func!(ExprType::Str, as_str as_str_ref = Expr::Str(_, x) => x); - as_func!(ExprType::Symbol, as_symbol = Expr::Symbol(_, x) => x); + as_func!(ExprType::Symbol, as_symbol as_symbol_ref = Expr::Symbol(_, x) => x); - as_func!(ExprType::List, as_list> = Expr::List(_, x) => x); + as_func!(ExprType::List, as_list as_list_ref> = Expr::List(_, x) => x); pub fn expr_type(&self) -> ExprType { match self { @@ -88,6 +95,13 @@ impl Expr { Expr::Comment(span) => *span, } } + + pub fn first_list_elem(&self) -> Option<&Expr> { + match self { + Expr::List(_, list) => list.first(), + _ => None, + } + } } impl std::fmt::Display for Expr { @@ -104,6 +118,20 @@ impl std::fmt::Display for Expr { } } } +impl std::fmt::Debug for Expr { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use Expr::*; + match self { + Number(span, x) => write!(f, "Number<{}>({})", span, x), + List(span, x) => f.debug_tuple(&format!("List<{}>", span)).field(x).finish(), + Table(span, x) => f.debug_tuple(&format!("Table<{}>", span)).field(x).finish(), + Keyword(span, x) => write!(f, "Number<{}>({})", span, x), + Symbol(span, x) => write!(f, "Symbol<{}>({})", span, x), + Str(span, x) => write!(f, "Str<{}>({})", span, x), + Comment(span) => write!(f, "Comment<{}>", span), + } + } +} pub struct ExprIterator> { iter: itertools::PutBack, @@ -128,15 +156,19 @@ macro_rules! return_or_put_back { } impl> ExprIterator { - return_or_put_back!(next_symbol, ExprType::Symbol, (Span, String) = Expr::Symbol(span, x) => (span, x)); + return_or_put_back!(expect_symbol, ExprType::Symbol, (Span, String) = Expr::Symbol(span, x) => (span, x)); - return_or_put_back!(next_string, ExprType::Str, (Span, String) = Expr::Str(span, x) => (span, x)); + return_or_put_back!(expect_string, ExprType::Str, (Span, String) = Expr::Str(span, x) => (span, x)); + + return_or_put_back!(expect_number, ExprType::Number, (Span, i32) = Expr::Number(span, x) => (span, x)); + + return_or_put_back!(expect_list, ExprType::List, (Span, Vec) = Expr::List(span, x) => (span, x)); pub fn new(iter: I) -> Self { ExprIterator { iter: itertools::put_back(iter) } } - pub fn key_values(&mut self) -> AstResult> { + pub fn expect_key_values(&mut self) -> AstResult> { parse_key_values(&mut self.iter) } } diff --git a/src/lib.rs b/src/lib.rs index d4a6f66..5135ce4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -41,6 +41,7 @@ fn test() { r#"{:key value 12 "hi" (test) (1 2 3)}"#, r#"; test"#, r#"(f arg ; test - arg2)"# + arg2)"#, + "\"h\\\"i\"" ); } diff --git a/src/snapshots/eww_config__config__test__test-2.snap b/src/snapshots/eww_config__config__test__test-2.snap index 39b709c..991e320 100644 --- a/src/snapshots/eww_config__config__test__test-2.snap +++ b/src/snapshots/eww_config__config__test__test-2.snap @@ -8,22 +8,14 @@ Definitional { name: "box", attrs: {}, children: [ - List( - 15..22, + List<15..22>( [ - Symbol( - 16..21, - "child", - ), + Symbol<16..21>(child), ], ), - List( - 23..31, + List<23..31>( [ - Symbol( - 24..30, - "child2", - ), + Symbol<24..30>(child2), ], ), ], diff --git a/src/snapshots/eww_config__config__test__test.snap b/src/snapshots/eww_config__config__test__test.snap index b58093b..2a5ccaa 100644 --- a/src/snapshots/eww_config__config__test__test.snap +++ b/src/snapshots/eww_config__config__test__test.snap @@ -6,27 +6,14 @@ expression: "Element::::from_expr(parser.parse(\"(box :ba Element { name: "box", attrs: { - ":bar": Number( - 10..12, - 12, - ), - ":baz": Str( - 18..22, - "hi", - ), + ":baz": Str<18..22>(hi), + ":bar": Number<10..12>(12), }, children: [ - Symbol( - 23..26, - "foo", - ), - List( - 27..32, + Symbol<23..26>(foo), + List<27..32>( [ - Symbol( - 28..31, - "bar", - ), + Symbol<28..31>(bar), ], ), ], diff --git a/src/snapshots/eww_config__test-10.snap b/src/snapshots/eww_config__test-10.snap index 2d0f6c5..50bf3b4 100644 --- a/src/snapshots/eww_config__test-10.snap +++ b/src/snapshots/eww_config__test-10.snap @@ -1,20 +1,13 @@ --- -source: src/main.rs +source: src/lib.rs expression: "p.parse(r#\"(test \"h\\\"i\")\"#)" --- Ok( - List( - 0..13, + List<0..13>( [ - Symbol( - 1..5, - "test", - ), - Str( - 6..12, - "h\\\"i", - ), + Symbol<1..5>(test), + Str<6..12>(h\"i), ], ), ) diff --git a/src/snapshots/eww_config__test-11.snap b/src/snapshots/eww_config__test-11.snap index ae042cc..8437f61 100644 --- a/src/snapshots/eww_config__test-11.snap +++ b/src/snapshots/eww_config__test-11.snap @@ -1,20 +1,13 @@ --- -source: src/main.rs +source: src/lib.rs expression: "p.parse(r#\"(test \" hi \")\"#)" --- Ok( - List( - 0..13, + List<0..13>( [ - Symbol( - 1..5, - "test", - ), - Str( - 6..12, - " hi ", - ), + Symbol<1..5>(test), + Str<6..12>( hi ), ], ), ) diff --git a/src/snapshots/eww_config__test-12.snap b/src/snapshots/eww_config__test-12.snap index 96fcd40..1f7e872 100644 --- a/src/snapshots/eww_config__test-12.snap +++ b/src/snapshots/eww_config__test-12.snap @@ -1,42 +1,21 @@ --- -source: src/main.rs +source: src/lib.rs expression: "p.parse(\"(+ (1 2 (* 2 5)))\")" --- Ok( - List( - 0..17, + List<0..17>( [ - Symbol( - 1..2, - "+", - ), - List( - 3..16, + Symbol<1..2>(+), + List<3..16>( [ - Number( - 4..5, - 1, - ), - Number( - 6..7, - 2, - ), - List( - 8..15, + Number<4..5>(1), + Number<6..7>(2), + List<8..15>( [ - Symbol( - 9..10, - "*", - ), - Number( - 11..12, - 2, - ), - Number( - 13..14, - 5, - ), + Symbol<9..10>(*), + Number<11..12>(2), + Number<13..14>(5), ], ), ], diff --git a/src/snapshots/eww_config__test-13.snap b/src/snapshots/eww_config__test-13.snap index aeed887..9b51e53 100644 --- a/src/snapshots/eww_config__test-13.snap +++ b/src/snapshots/eww_config__test-13.snap @@ -1,57 +1,30 @@ --- -source: src/main.rs +source: src/lib.rs expression: "p.parse(r#\"{:key value 12 \"hi\" (test) (1 2 3)}\"#)" --- Ok( - Table( - 0..35, + Table<0..35>( [ ( - Keyword( - 1..5, - ":key", - ), - Symbol( - 6..11, - "value", - ), + Number<1..5>(:key), + Symbol<6..11>(value), ), ( - Number( - 12..14, - 12, - ), - Str( - 15..19, - "hi", - ), + Number<12..14>(12), + Str<15..19>(hi), ), ( - List( - 20..26, + List<20..26>( [ - Symbol( - 21..25, - "test", - ), + Symbol<21..25>(test), ], ), - List( - 27..34, + List<27..34>( [ - Number( - 28..29, - 1, - ), - Number( - 30..31, - 2, - ), - Number( - 32..33, - 3, - ), + Number<28..29>(1), + Number<30..31>(2), + Number<32..33>(3), ], ), ), diff --git a/src/snapshots/eww_config__test-14.snap b/src/snapshots/eww_config__test-14.snap index 5b8d869..3abe4ce 100644 --- a/src/snapshots/eww_config__test-14.snap +++ b/src/snapshots/eww_config__test-14.snap @@ -1,10 +1,8 @@ --- -source: src/main.rs +source: src/lib.rs expression: "p.parse(r#\"; test\"#)" --- Ok( - Comment( - 0..6, - ), + Comment<0..6>, ) diff --git a/src/snapshots/eww_config__test-15.snap b/src/snapshots/eww_config__test-15.snap index b2943ca..c2f54ba 100644 --- a/src/snapshots/eww_config__test-15.snap +++ b/src/snapshots/eww_config__test-15.snap @@ -4,24 +4,12 @@ expression: "p.parse(r#\"(f arg ; test\n arg2)\"#)" --- Ok( - List( - 0..27, + List<0..27>( [ - Symbol( - 1..2, - "f", - ), - Symbol( - 3..6, - "arg", - ), - Comment( - 7..13, - ), - Symbol( - 22..26, - "arg2", - ), + Symbol<1..2>(f), + Symbol<3..6>(arg), + Comment<7..13>, + Symbol<22..26>(arg2), ], ), ) diff --git a/src/snapshots/eww_config__test-16.snap b/src/snapshots/eww_config__test-16.snap new file mode 100644 index 0000000..021fcd1 --- /dev/null +++ b/src/snapshots/eww_config__test-16.snap @@ -0,0 +1,8 @@ +--- +source: src/lib.rs +expression: "p.parse(\"\\\"h\\\\\\\"i\\\"\")" + +--- +Ok( + Str<0..6>(h\"i), +) diff --git a/src/snapshots/eww_config__test-2.snap b/src/snapshots/eww_config__test-2.snap index f16a309..dd84a41 100644 --- a/src/snapshots/eww_config__test-2.snap +++ b/src/snapshots/eww_config__test-2.snap @@ -1,16 +1,12 @@ --- -source: src/main.rs +source: src/lib.rs expression: "p.parse(\"(12)\")" --- Ok( - List( - 0..4, + List<0..4>( [ - Number( - 1..3, - 12, - ), + Number<1..3>(12), ], ), ) diff --git a/src/snapshots/eww_config__test-3.snap b/src/snapshots/eww_config__test-3.snap index 99b52c6..5f7b32c 100644 --- a/src/snapshots/eww_config__test-3.snap +++ b/src/snapshots/eww_config__test-3.snap @@ -1,20 +1,13 @@ --- -source: src/main.rs +source: src/lib.rs expression: "p.parse(\"(1 2)\")" --- Ok( - List( - 0..5, + List<0..5>( [ - Number( - 1..2, - 1, - ), - Number( - 3..4, - 2, - ), + Number<1..2>(1), + Number<3..4>(2), ], ), ) diff --git a/src/snapshots/eww_config__test-4.snap b/src/snapshots/eww_config__test-4.snap index 6ecf27d..8ce142b 100644 --- a/src/snapshots/eww_config__test-4.snap +++ b/src/snapshots/eww_config__test-4.snap @@ -1,24 +1,14 @@ --- -source: src/main.rs +source: src/lib.rs expression: "p.parse(\"(1 :foo 1)\")" --- Ok( - List( - 0..10, + List<0..10>( [ - Number( - 1..2, - 1, - ), - Keyword( - 3..7, - ":foo", - ), - Number( - 8..9, - 1, - ), + Number<1..2>(1), + Number<3..7>(:foo), + Number<8..9>(1), ], ), ) diff --git a/src/snapshots/eww_config__test-5.snap b/src/snapshots/eww_config__test-5.snap index ff6efe3..9db3361 100644 --- a/src/snapshots/eww_config__test-5.snap +++ b/src/snapshots/eww_config__test-5.snap @@ -1,20 +1,13 @@ --- -source: src/main.rs +source: src/lib.rs expression: "p.parse(\"(:foo 1)\")" --- Ok( - List( - 0..8, + List<0..8>( [ - Keyword( - 1..5, - ":foo", - ), - Number( - 6..7, - 1, - ), + Number<1..5>(:foo), + Number<6..7>(1), ], ), ) diff --git a/src/snapshots/eww_config__test-6.snap b/src/snapshots/eww_config__test-6.snap index 919dd49..f368f33 100644 --- a/src/snapshots/eww_config__test-6.snap +++ b/src/snapshots/eww_config__test-6.snap @@ -1,20 +1,13 @@ --- -source: src/main.rs +source: src/lib.rs expression: "p.parse(\"(:foo->: 1)\")" --- Ok( - List( - 0..11, + List<0..11>( [ - Keyword( - 1..8, - ":foo->:", - ), - Number( - 9..10, - 1, - ), + Number<1..8>(:foo->:), + Number<9..10>(1), ], ), ) diff --git a/src/snapshots/eww_config__test-7.snap b/src/snapshots/eww_config__test-7.snap index 0c038c5..eb63df2 100644 --- a/src/snapshots/eww_config__test-7.snap +++ b/src/snapshots/eww_config__test-7.snap @@ -1,20 +1,13 @@ --- -source: src/main.rs +source: src/lib.rs expression: "p.parse(\"(foo 1)\")" --- Ok( - List( - 0..7, + List<0..7>( [ - Symbol( - 1..4, - "foo", - ), - Number( - 5..6, - 1, - ), + Symbol<1..4>(foo), + Number<5..6>(1), ], ), ) diff --git a/src/snapshots/eww_config__test-8.snap b/src/snapshots/eww_config__test-8.snap index eec1f65..ee21f3e 100644 --- a/src/snapshots/eww_config__test-8.snap +++ b/src/snapshots/eww_config__test-8.snap @@ -1,20 +1,13 @@ --- -source: src/main.rs +source: src/lib.rs expression: "p.parse(\"(lol😄 1)\")" --- Ok( - List( - 0..11, + List<0..11>( [ - Symbol( - 1..8, - "lol😄", - ), - Number( - 9..10, - 1, - ), + Symbol<1..8>(lol😄), + Number<9..10>(1), ], ), ) diff --git a/src/snapshots/eww_config__test-9.snap b/src/snapshots/eww_config__test-9.snap index ce6c9db..edc1cb8 100644 --- a/src/snapshots/eww_config__test-9.snap +++ b/src/snapshots/eww_config__test-9.snap @@ -1,20 +1,13 @@ --- -source: src/main.rs +source: src/lib.rs expression: "p.parse(r#\"(test \"hi\")\"#)" --- Ok( - List( - 0..11, + List<0..11>( [ - Symbol( - 1..5, - "test", - ), - Str( - 6..10, - "hi", - ), + Symbol<1..5>(test), + Str<6..10>(hi), ], ), ) diff --git a/src/snapshots/eww_config__test.snap b/src/snapshots/eww_config__test.snap index 2547c26..8c42a6b 100644 --- a/src/snapshots/eww_config__test.snap +++ b/src/snapshots/eww_config__test.snap @@ -1,11 +1,8 @@ --- -source: src/main.rs +source: src/lib.rs expression: "p.parse(\"1\")" --- Ok( - Number( - 0..1, - 1, - ), + Number<0..1>(1), ) From 55e3d17f73b7ef22fa99a477fb9cd0ddf082708b Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Sat, 3 Jul 2021 20:25:50 +0200 Subject: [PATCH 018/137] add example --- Cargo.lock | 35 +++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + examples/errors.rs | 18 ++++++++++++++++++ src/config.rs | 4 +--- src/error.rs | 12 ++++++++++++ src/expr.rs | 2 +- src/lib.rs | 8 ++++---- src/parser.lalrpop | 16 ++++++++-------- 8 files changed, 80 insertions(+), 16 deletions(-) create mode 100644 examples/errors.rs diff --git a/Cargo.lock b/Cargo.lock index b50e278..943b8e6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -93,6 +93,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + [[package]] name = "console" version = "0.14.1" @@ -177,6 +187,7 @@ checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" name = "eww_config" version = "0.1.0" dependencies = [ + "codespan-reporting", "insta", "itertools", "lalrpop", @@ -525,6 +536,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "termcolor" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +dependencies = [ + "winapi-util", +] + [[package]] name = "terminal_size" version = "0.1.17" @@ -564,6 +584,12 @@ dependencies = [ "crunchy", ] +[[package]] +name = "unicode-width" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" + [[package]] name = "unicode-xid" version = "0.2.2" @@ -598,6 +624,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index 650302b..449e909 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ regex = "1" itertools = "0.10" thiserror = "1.0" maplit = "1.0" +codespan-reporting = "0.11" [build-dependencies] lalrpop = "0.19.5" diff --git a/examples/errors.rs b/examples/errors.rs new file mode 100644 index 0000000..241ac3c --- /dev/null +++ b/examples/errors.rs @@ -0,0 +1,18 @@ +use eww_config::{config::*, error, expr::*, parser}; + +fn main() { + let parser = parser::ExprParser::new(); + let mut files = codespan_reporting::files::SimpleFiles::new(); + + let input = "(12 :bar 22 (foo) (baz))"; + + let file_id = files.add("foo.eww", input); + let ast = parser.parse(file_id, input).unwrap(); + let element: Result, _> = Element::from_expr(ast); + let err = element.unwrap_err(); + + let diag = err.pretty_diagnostic(&files); + use codespan_reporting::term; + let mut writer = term::termcolor::StandardStream::stderr(term::termcolor::ColorChoice::Always); + term::emit(&mut writer, &term::Config::default(), &files, &diag).unwrap(); +} diff --git a/src/config.rs b/src/config.rs index b3f687e..8e0c7c8 100644 --- a/src/config.rs +++ b/src/config.rs @@ -10,8 +10,6 @@ use std::{ str::FromStr, }; -// https://michael-f-bryan.github.io/static-analyser-in-rust/book/codemap.html - type VarName = String; type AttrValue = String; type AttrName = String; @@ -59,7 +57,7 @@ mod test { let parser = parser::ExprParser::new(); insta::with_settings!({sort_maps => true}, { insta::assert_debug_snapshot!( - Element::::from_expr(parser.parse("(box :bar 12 :baz \"hi\" foo (bar))").unwrap()).unwrap() + Element::::from_expr(parser.parse(0, "(box :bar 12 :baz \"hi\" foo (bar))").unwrap()).unwrap() ); }); } diff --git a/src/error.rs b/src/error.rs index 82a722c..14cf169 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,4 +1,5 @@ use crate::expr::{Expr, ExprType, Span}; +use codespan_reporting::{diagnostic, files}; use thiserror::Error; pub type AstResult = Result; @@ -13,6 +14,17 @@ pub enum AstError { WrongExprType(Option, ExprType, ExprType), } +impl AstError { + pub fn pretty_diagnostic(&self, files: &files::SimpleFiles<&str, &str>) -> diagnostic::Diagnostic { + let diag = diagnostic::Diagnostic::error().with_message(format!("{}", self)); + if let AstError::WrongExprType(Some(span), ..) = self { + diag.with_labels(vec![diagnostic::Label::primary(span.2, span.0..span.1)]) + } else { + diag + } + } +} + pub fn spanned(span: Span, err: impl Into) -> AstError { use AstError::*; match err.into() { diff --git a/src/expr.rs b/src/expr.rs index 97a0b92..8535642 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -5,7 +5,7 @@ use crate::{config::FromExpr, error::*}; use std::fmt::Display; #[derive(Eq, PartialEq, Clone, Copy)] -pub struct Span(pub usize, pub usize); +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 { diff --git a/src/lib.rs b/src/lib.rs index 5135ce4..cdda0b5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,9 +2,9 @@ #![allow(unused)] #![feature(try_blocks)] -mod config; -mod error; -mod expr; +pub mod config; +pub mod error; +pub mod expr; use error::AstError; use std::{fmt::Display, ops::Deref}; @@ -17,7 +17,7 @@ lalrpop_mod!(pub parser); macro_rules! test_parser { ($p:expr, $($text:literal),*) => {{ - $(insta::assert_debug_snapshot!($p.parse($text));)* + $(insta::assert_debug_snapshot!($p.parse(0, $text));)* }} } diff --git a/src/parser.lalrpop b/src/parser.lalrpop index c306776..6268611 100644 --- a/src/parser.lalrpop +++ b/src/parser.lalrpop @@ -2,23 +2,23 @@ use std::str::FromStr; //use crate::lexer; use crate::expr::{Expr, Span}; -grammar; +grammar(file_id: usize); pub Expr: Expr = { - "(" )+> ")" => Expr::List(Span(l, r), elems), + "(" )+> ")" => Expr::List(Span(l, r, file_id), elems), - "{" )> <()>)*> "}" => Expr::Table(Span(l, r), elems), + "{" )> <()>)*> "}" => Expr::Table(Span(l, r, file_id), elems), => x, => x, - => Expr::Str(Span(l, r), x), - => Expr::Number(Span(l, r), x), - Comment => Expr::Comment(Span(l, r)), + => Expr::Str(Span(l, r, file_id), x), + => Expr::Number(Span(l, r, file_id), x), + Comment => Expr::Comment(Span(l, r, file_id)), }; -Keyword: Expr = => Expr::Keyword(Span(l, r), x.to_string()); -Symbol: Expr = /.*-+][^\s{}\(\)]*"> => Expr::Symbol(Span(l, r), x.to_string()); +Keyword: Expr = => Expr::Keyword(Span(l, r, file_id), x.to_string()); +Symbol: Expr = /.*-+][^\s{}\(\)]*"> => Expr::Symbol(Span(l, r, file_id), x.to_string()); StrLit: String = { => { From cc07d68c914b46dc58a9a5bd21c7337cb3577019 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Sat, 3 Jul 2021 21:21:23 +0200 Subject: [PATCH 019/137] yeet table and number nodes --- examples/errors.rs | 23 ++++++++++++++--------- src/expr.rs | 32 +++++++++----------------------- src/parser.lalrpop | 31 ++++++++++++++++++++----------- 3 files changed, 43 insertions(+), 43 deletions(-) diff --git a/examples/errors.rs b/examples/errors.rs index 241ac3c..00eee25 100644 --- a/examples/errors.rs +++ b/examples/errors.rs @@ -1,18 +1,23 @@ -use eww_config::{config::*, error, expr::*, parser}; +use eww_config::{config::*, expr::*, parser}; fn main() { let parser = parser::ExprParser::new(); let mut files = codespan_reporting::files::SimpleFiles::new(); - let input = "(12 :bar 22 (foo) (baz))"; + let input = "(12 :bar 22 (foo) (baz)"; let file_id = files.add("foo.eww", input); - let ast = parser.parse(file_id, input).unwrap(); - let element: Result, _> = Element::from_expr(ast); - let err = element.unwrap_err(); + let ast = parser.parse(file_id, input); + match ast { + Ok(ast) => { + let element: Result, _> = Element::from_expr(ast); + let err = element.unwrap_err(); - let diag = err.pretty_diagnostic(&files); - use codespan_reporting::term; - let mut writer = term::termcolor::StandardStream::stderr(term::termcolor::ColorChoice::Always); - term::emit(&mut writer, &term::Config::default(), &files, &diag).unwrap(); + let diag = err.pretty_diagnostic(&files); + use codespan_reporting::term; + let mut writer = term::termcolor::StandardStream::stderr(term::termcolor::ColorChoice::Always); + term::emit(&mut writer, &term::Config::default(), &files, &diag).unwrap(); + } + Err(err) => eprintln!("{}", err), + } } diff --git a/src/expr.rs b/src/expr.rs index 8535642..7426359 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -22,11 +22,9 @@ impl std::fmt::Debug for Span { #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum ExprType { List, - Table, Keyword, Symbol, - Str, - Number, + Value, Comment, } @@ -39,11 +37,9 @@ impl Display for ExprType { #[derive(PartialEq, Eq, Clone)] pub enum Expr { List(Span, Vec), - Table(Span, Vec<(Expr, Expr)>), Keyword(Span, String), Symbol(Span, String), - Str(Span, String), - Number(Span, i32), + Value(Span, String), Comment(Span), } @@ -66,7 +62,7 @@ macro_rules! as_func { } impl Expr { - as_func!(ExprType::Str, as_str as_str_ref = Expr::Str(_, x) => x); + as_func!(ExprType::Value, as_value as_value_ref = Expr::Value(_, x) => x); as_func!(ExprType::Symbol, as_symbol as_symbol_ref = Expr::Symbol(_, x) => x); @@ -75,23 +71,19 @@ impl Expr { pub fn expr_type(&self) -> ExprType { match self { Expr::List(..) => ExprType::List, - Expr::Table(..) => ExprType::Table, Expr::Keyword(..) => ExprType::Keyword, Expr::Symbol(..) => ExprType::Symbol, - Expr::Str(..) => ExprType::Str, - Expr::Number(..) => ExprType::Number, - Expr::Comment(_) => ExprType::Number, + Expr::Value(..) => ExprType::Value, + Expr::Comment(_) => ExprType::Comment, } } pub fn span(&self) -> Span { match self { Expr::List(span, _) => *span, - Expr::Table(span, _) => *span, Expr::Keyword(span, _) => *span, Expr::Symbol(span, _) => *span, - Expr::Str(span, _) => *span, - Expr::Number(span, _) => *span, + Expr::Value(span, _) => *span, Expr::Comment(span) => *span, } } @@ -108,12 +100,10 @@ impl std::fmt::Display for Expr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { use Expr::*; match self { - Number(_, x) => write!(f, "{}", x), List(_, x) => write!(f, "({})", x.iter().map(|e| format!("{}", e)).join(" ")), - Table(_, x) => write!(f, "{{{}}}", x.iter().map(|(k, v)| format!("{} {}", k, v)).join(" ")), Keyword(_, x) => write!(f, "{}", x), Symbol(_, x) => write!(f, "{}", x), - Str(_, x) => write!(f, "{}", x), + Value(_, x) => write!(f, "{}", x), Comment(_) => write!(f, ""), } } @@ -122,12 +112,10 @@ impl std::fmt::Debug for Expr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { use Expr::*; match self { - Number(span, x) => write!(f, "Number<{}>({})", span, x), List(span, x) => f.debug_tuple(&format!("List<{}>", span)).field(x).finish(), - Table(span, x) => f.debug_tuple(&format!("Table<{}>", span)).field(x).finish(), Keyword(span, x) => write!(f, "Number<{}>({})", span, x), Symbol(span, x) => write!(f, "Symbol<{}>({})", span, x), - Str(span, x) => write!(f, "Str<{}>({})", span, x), + Value(span, x) => write!(f, "Value<{}>({})", span, x), Comment(span) => write!(f, "Comment<{}>", span), } } @@ -158,9 +146,7 @@ macro_rules! return_or_put_back { impl> ExprIterator { return_or_put_back!(expect_symbol, ExprType::Symbol, (Span, String) = Expr::Symbol(span, x) => (span, x)); - return_or_put_back!(expect_string, ExprType::Str, (Span, String) = Expr::Str(span, x) => (span, x)); - - return_or_put_back!(expect_number, ExprType::Number, (Span, i32) = Expr::Number(span, x) => (span, x)); + return_or_put_back!(expect_string, ExprType::Value, (Span, String) = Expr::Value(span, x) => (span, x)); return_or_put_back!(expect_list, ExprType::List, (Span, Vec) = Expr::List(span, x) => (span, x)); diff --git a/src/parser.lalrpop b/src/parser.lalrpop index 6268611..4931527 100644 --- a/src/parser.lalrpop +++ b/src/parser.lalrpop @@ -7,30 +7,39 @@ grammar(file_id: usize); pub Expr: Expr = { "(" )+> ")" => Expr::List(Span(l, r, file_id), elems), - - "{" )> <()>)*> "}" => Expr::Table(Span(l, r, file_id), elems), - => x, => x, - => Expr::Str(Span(l, r, file_id), x), - => Expr::Number(Span(l, r, file_id), x), + => Expr::Value(Span(l, r, file_id), x), Comment => Expr::Comment(Span(l, r, file_id)), }; Keyword: Expr = => Expr::Keyword(Span(l, r, file_id), x.to_string()); -Symbol: Expr = /.*-+][^\s{}\(\)]*"> => Expr::Symbol(Span(l, r, file_id), x.to_string()); +Symbol: Expr = => Expr::Symbol(Span(l, r, file_id), x.to_string()); + +Value: String = { + => <>, + => <>, + => <>, +}; StrLit: String = { => { x[1..x.len() - 1].to_owned() }, +}; + + +Num: String = => x.to_string(); +Bool: String = => x.to_string(); + +match { + r"true|false" => BoolRegex, +} else { + r"[a-zA-Z_!\?<>/.*-+][^\s{}\(\)]*" => SymbolRegex, + _ } + Comment: () = r";[^\n\r]*"; - -Num: i32 = => i32::from_str(<>).unwrap(); - - - // vim:shiftwidth=4 From 98ef505a212fe45bd9ee467491f4bd687917bb6f Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Mon, 5 Jul 2021 19:23:12 +0200 Subject: [PATCH 020/137] Add custom lexer --- Cargo.lock | 43 +++++++++++++ Cargo.toml | 1 + examples/errors.rs | 8 ++- src/config.rs | 4 +- src/lexer.rs | 63 +++++++++++++++++++ src/lib.rs | 17 +++-- src/parser.lalrpop | 41 +++++++----- .../eww_config__config__test__test.snap | 6 +- .../eww_config__config__test__test.snap.new | 21 +++++++ src/snapshots/eww_config__test-10.snap | 8 +-- src/snapshots/eww_config__test-11.snap | 6 +- src/snapshots/eww_config__test-12.snap | 20 ++---- src/snapshots/eww_config__test-13.snap | 28 ++------- src/snapshots/eww_config__test-14.snap | 21 ++++++- src/snapshots/eww_config__test-15.snap | 11 +--- src/snapshots/eww_config__test-16.snap | 11 +++- src/snapshots/eww_config__test-17.snap | 8 +++ src/snapshots/eww_config__test-18.snap | 8 +++ src/snapshots/eww_config__test-2.snap | 4 +- src/snapshots/eww_config__test-3.snap | 9 +-- src/snapshots/eww_config__test-4.snap | 10 +-- src/snapshots/eww_config__test-5.snap | 8 +-- src/snapshots/eww_config__test-6.snap | 9 +-- src/snapshots/eww_config__test-7.snap | 8 +-- src/snapshots/eww_config__test-8.snap | 6 +- src/snapshots/eww_config__test-9.snap | 8 +-- src/snapshots/eww_config__test.snap | 4 +- 27 files changed, 266 insertions(+), 125 deletions(-) create mode 100644 src/lexer.rs create mode 100644 src/snapshots/eww_config__config__test__test.snap.new create mode 100644 src/snapshots/eww_config__test-17.snap create mode 100644 src/snapshots/eww_config__test-18.snap diff --git a/Cargo.lock b/Cargo.lock index 943b8e6..bf2c2b7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -55,6 +55,12 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +[[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" @@ -192,6 +198,7 @@ dependencies = [ "itertools", "lalrpop", "lalrpop-util", + "logos", "maplit", "regex", "thiserror", @@ -203,6 +210,12 @@ 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.1.16" @@ -328,6 +341,30 @@ 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" @@ -596,6 +633,12 @@ 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" diff --git a/Cargo.toml b/Cargo.toml index 449e909..4f2e5ae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ itertools = "0.10" thiserror = "1.0" maplit = "1.0" codespan-reporting = "0.11" +logos = "0.12" [build-dependencies] lalrpop = "0.19.5" diff --git a/examples/errors.rs b/examples/errors.rs index 00eee25..061047a 100644 --- a/examples/errors.rs +++ b/examples/errors.rs @@ -1,4 +1,4 @@ -use eww_config::{config::*, expr::*, parser}; +use eww_config::{config::*, expr::*, lexer, parser}; fn main() { let parser = parser::ExprParser::new(); @@ -7,7 +7,9 @@ fn main() { let input = "(12 :bar 22 (foo) (baz)"; let file_id = files.add("foo.eww", input); - let ast = parser.parse(file_id, input); + let lexer = lexer::Lexer::new(input); + + let ast = parser.parse(file_id, lexer); match ast { Ok(ast) => { let element: Result, _> = Element::from_expr(ast); @@ -18,6 +20,6 @@ fn main() { let mut writer = term::termcolor::StandardStream::stderr(term::termcolor::ColorChoice::Always); term::emit(&mut writer, &term::Config::default(), &files, &diag).unwrap(); } - Err(err) => eprintln!("{}", err), + Err(err) => eprintln!("{:?}", err), } } diff --git a/src/config.rs b/src/config.rs index 8e0c7c8..5fd1f11 100644 --- a/src/config.rs +++ b/src/config.rs @@ -50,14 +50,16 @@ impl FromExpr for Element { mod test { use super::*; + use crate::lexer; use insta; #[test] fn test() { let parser = parser::ExprParser::new(); insta::with_settings!({sort_maps => true}, { + let lexer = lexer::Lexer::new("(box :bar 12 :baz \"hi\" foo (bar))"); insta::assert_debug_snapshot!( - Element::::from_expr(parser.parse(0, "(box :bar 12 :baz \"hi\" foo (bar))").unwrap()).unwrap() + Element::::from_expr(parser.parse(0, lexer).unwrap()).unwrap() ); }); } diff --git a/src/lexer.rs b/src/lexer.rs new file mode 100644 index 0000000..b0efdfe --- /dev/null +++ b/src/lexer.rs @@ -0,0 +1,63 @@ +use logos::Logos; + +#[derive(Logos, Debug, PartialEq, Eq, Clone)] +pub enum Token { + #[token("(")] + LPren, + + #[token(")")] + RPren, + + #[token("true")] + True, + + #[token("false")] + False, + + #[regex(r#""(?:[^"\\]|\\.)*""#, |x| x.slice().to_string())] + StrLit(String), + + #[regex(r#"[+-]?(?:[0-9]+[.])?[0-9]+"#, priority = 2, callback = |x| x.slice().to_string())] + NumLit(String), + + #[regex(r#"[a-zA-Z_!\?<>/.*-+][^\s{}\(\)]*"#, |x| x.slice().to_string())] + Symbol(String), + + #[regex(r#":\S+"#, |x| x.slice().to_string())] + Keyword(String), + + #[regex(r#";.*"#)] + Comment, + + #[error] + #[regex(r"[ \t\n\f]+", logos::skip)] + Error, +} + +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +pub struct LexicalError(usize, usize); + +pub type SpannedResult = 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; + + fn next(&mut self) -> Option { + 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))) + } + } +} diff --git a/src/lib.rs b/src/lib.rs index cdda0b5..d9fab6f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,6 +5,7 @@ pub mod config; pub mod error; pub mod expr; +pub mod lexer; use error::AstError; use std::{fmt::Display, ops::Deref}; @@ -16,18 +17,25 @@ use lalrpop_util::lalrpop_mod; lalrpop_mod!(pub parser); macro_rules! test_parser { - ($p:expr, $($text:literal),*) => {{ - $(insta::assert_debug_snapshot!($p.parse(0, $text));)* + ($($text:literal),*) => {{ + let p = crate::parser::ExprParser::new(); + use crate::lexer::Lexer; + + ::insta::with_settings!({sort_maps => true}, { + $( + ::insta::assert_debug_snapshot!(p.parse(0, Lexer::new($text))); + )* + }); }} } #[test] fn test() { - let p = parser::ExprParser::new(); test_parser!( - p, "1", "(12)", + "1.2", + "-1.2", "(1 2)", "(1 :foo 1)", "(:foo 1)", @@ -38,7 +46,6 @@ fn test() { r#"(test "h\"i")"#, r#"(test " hi ")"#, "(+ (1 2 (* 2 5)))", - r#"{:key value 12 "hi" (test) (1 2 3)}"#, r#"; test"#, r#"(f arg ; test arg2)"#, diff --git a/src/parser.lalrpop b/src/parser.lalrpop index 4931527..7734025 100644 --- a/src/parser.lalrpop +++ b/src/parser.lalrpop @@ -1,20 +1,37 @@ use std::str::FromStr; -//use crate::lexer; +use crate::lexer::{Token, LexicalError}; use crate::expr::{Expr, Span}; grammar(file_id: usize); +extern { + type Location = usize; + type Error = LexicalError; + + enum Token { + "(" => Token::LPren, + ")" => Token::RPren, + "true" => Token::True, + "false" => Token::False, + "strLit" => Token::StrLit(), + "numLit" => Token::NumLit(), + "symbol" => Token::Symbol(), + "keyword" => Token::Keyword(), + "comment" => Token::Comment, + } +} + pub Expr: Expr = { "(" )+> ")" => Expr::List(Span(l, r, file_id), elems), => x, => x, => Expr::Value(Span(l, r, file_id), x), - Comment => Expr::Comment(Span(l, r, file_id)), + "comment" => Expr::Comment(Span(l, r, file_id)), }; -Keyword: Expr = => Expr::Keyword(Span(l, r, file_id), x.to_string()); -Symbol: Expr = => Expr::Symbol(Span(l, r, file_id), x.to_string()); +Keyword: Expr = => Expr::Keyword(Span(l, r, file_id), x.to_string()); +Symbol: Expr = => Expr::Symbol(Span(l, r, file_id), x.to_string()); Value: String = { => <>, @@ -23,23 +40,17 @@ Value: String = { }; StrLit: String = { - => { + => { x[1..x.len() - 1].to_owned() }, }; -Num: String = => x.to_string(); -Bool: String = => x.to_string(); - -match { - r"true|false" => BoolRegex, -} else { - r"[a-zA-Z_!\?<>/.*-+][^\s{}\(\)]*" => SymbolRegex, - _ +Num: String = <"numLit"> => <>.to_string(); +Bool: String = { + "true" => "true".to_string(), + "false" => "false".to_string(), } -Comment: () = r";[^\n\r]*"; - // vim:shiftwidth=4 diff --git a/src/snapshots/eww_config__config__test__test.snap b/src/snapshots/eww_config__config__test__test.snap index 2a5ccaa..ac592e4 100644 --- a/src/snapshots/eww_config__config__test__test.snap +++ b/src/snapshots/eww_config__config__test__test.snap @@ -1,13 +1,13 @@ --- source: src/config.rs -expression: "Element::::from_expr(parser.parse(\"(box :bar 12 :baz \\\"hi\\\" foo (bar))\").unwrap()).unwrap()" +expression: "Element::::from_expr(parser.parse(0, lexer).unwrap()).unwrap()" --- Element { name: "box", attrs: { - ":baz": Str<18..22>(hi), - ":bar": Number<10..12>(12), + ":bar": Value<10..12>(12), + ":baz": Value<18..22>(hi), }, children: [ Symbol<23..26>(foo), diff --git a/src/snapshots/eww_config__config__test__test.snap.new b/src/snapshots/eww_config__config__test__test.snap.new new file mode 100644 index 0000000..40a3984 --- /dev/null +++ b/src/snapshots/eww_config__config__test__test.snap.new @@ -0,0 +1,21 @@ +--- +source: src/config.rs +expression: "Element::::from_expr(parser.parse(0, lexer).unwrap()).unwrap()" + +--- +Element { + name: "box", + attrs: { + ":baz": Value<18..22>(hi), + ":bar": Value<10..12>(12), + }, + children: [ + Symbol<23..26>(foo), + List<27..32>( + [ + Symbol<28..31>(bar), + ], + ), + ], + span: 0..33, +} diff --git a/src/snapshots/eww_config__test-10.snap b/src/snapshots/eww_config__test-10.snap index 50bf3b4..00f2730 100644 --- a/src/snapshots/eww_config__test-10.snap +++ b/src/snapshots/eww_config__test-10.snap @@ -1,13 +1,13 @@ --- source: src/lib.rs -expression: "p.parse(r#\"(test \"h\\\"i\")\"#)" +expression: "p.parse(0, lexer::Lexer::new(\"(lol😄 1)\"))" --- Ok( - List<0..13>( + List<0..11>( [ - Symbol<1..5>(test), - Str<6..12>(h\"i), + Symbol<1..8>(lol😄), + Value<9..10>(1), ], ), ) diff --git a/src/snapshots/eww_config__test-11.snap b/src/snapshots/eww_config__test-11.snap index 8437f61..5b02964 100644 --- a/src/snapshots/eww_config__test-11.snap +++ b/src/snapshots/eww_config__test-11.snap @@ -1,13 +1,13 @@ --- source: src/lib.rs -expression: "p.parse(r#\"(test \" hi \")\"#)" +expression: "p.parse(0, lexer::Lexer::new(r#\"(test \"hi\")\"#))" --- Ok( - List<0..13>( + List<0..11>( [ Symbol<1..5>(test), - Str<6..12>( hi ), + Value<6..10>(hi), ], ), ) diff --git a/src/snapshots/eww_config__test-12.snap b/src/snapshots/eww_config__test-12.snap index 1f7e872..b60c2dd 100644 --- a/src/snapshots/eww_config__test-12.snap +++ b/src/snapshots/eww_config__test-12.snap @@ -1,25 +1,13 @@ --- source: src/lib.rs -expression: "p.parse(\"(+ (1 2 (* 2 5)))\")" +expression: "p.parse(0, lexer::Lexer::new(r#\"(test \"h\\\"i\")\"#))" --- Ok( - List<0..17>( + List<0..13>( [ - Symbol<1..2>(+), - List<3..16>( - [ - Number<4..5>(1), - Number<6..7>(2), - List<8..15>( - [ - Symbol<9..10>(*), - Number<11..12>(2), - Number<13..14>(5), - ], - ), - ], - ), + Symbol<1..5>(test), + Value<6..12>(h\"i), ], ), ) diff --git a/src/snapshots/eww_config__test-13.snap b/src/snapshots/eww_config__test-13.snap index 9b51e53..e103f93 100644 --- a/src/snapshots/eww_config__test-13.snap +++ b/src/snapshots/eww_config__test-13.snap @@ -1,33 +1,13 @@ --- source: src/lib.rs -expression: "p.parse(r#\"{:key value 12 \"hi\" (test) (1 2 3)}\"#)" +expression: "p.parse(0, lexer::Lexer::new(r#\"(test \" hi \")\"#))" --- Ok( - Table<0..35>( + List<0..13>( [ - ( - Number<1..5>(:key), - Symbol<6..11>(value), - ), - ( - Number<12..14>(12), - Str<15..19>(hi), - ), - ( - List<20..26>( - [ - Symbol<21..25>(test), - ], - ), - List<27..34>( - [ - Number<28..29>(1), - Number<30..31>(2), - Number<32..33>(3), - ], - ), - ), + Symbol<1..5>(test), + Value<6..12>( hi ), ], ), ) diff --git a/src/snapshots/eww_config__test-14.snap b/src/snapshots/eww_config__test-14.snap index 3abe4ce..9271dab 100644 --- a/src/snapshots/eww_config__test-14.snap +++ b/src/snapshots/eww_config__test-14.snap @@ -1,8 +1,25 @@ --- source: src/lib.rs -expression: "p.parse(r#\"; test\"#)" +expression: "p.parse(0, lexer::Lexer::new(\"(+ (1 2 (* 2 5)))\"))" --- Ok( - Comment<0..6>, + List<0..17>( + [ + Symbol<1..2>(+), + List<3..16>( + [ + Value<4..5>(1), + Value<6..7>(2), + List<8..15>( + [ + Symbol<9..10>(*), + Value<11..12>(2), + Value<13..14>(5), + ], + ), + ], + ), + ], + ), ) diff --git a/src/snapshots/eww_config__test-15.snap b/src/snapshots/eww_config__test-15.snap index c2f54ba..c439fd1 100644 --- a/src/snapshots/eww_config__test-15.snap +++ b/src/snapshots/eww_config__test-15.snap @@ -1,15 +1,8 @@ --- source: src/lib.rs -expression: "p.parse(r#\"(f arg ; test\n arg2)\"#)" +expression: "p.parse(0, lexer::Lexer::new(r#\"; test\"#))" --- Ok( - List<0..27>( - [ - Symbol<1..2>(f), - Symbol<3..6>(arg), - Comment<7..13>, - Symbol<22..26>(arg2), - ], - ), + Comment<0..6>, ) diff --git a/src/snapshots/eww_config__test-16.snap b/src/snapshots/eww_config__test-16.snap index 021fcd1..30c9111 100644 --- a/src/snapshots/eww_config__test-16.snap +++ b/src/snapshots/eww_config__test-16.snap @@ -1,8 +1,15 @@ --- source: src/lib.rs -expression: "p.parse(\"\\\"h\\\\\\\"i\\\"\")" +expression: "p.parse(0, lexer::Lexer::new(r#\"(f arg ; test\n arg2)\"#))" --- Ok( - Str<0..6>(h\"i), + List<0..27>( + [ + Symbol<1..2>(f), + Symbol<3..6>(arg), + Comment<7..13>, + Symbol<22..26>(arg2), + ], + ), ) diff --git a/src/snapshots/eww_config__test-17.snap b/src/snapshots/eww_config__test-17.snap new file mode 100644 index 0000000..17db7ac --- /dev/null +++ b/src/snapshots/eww_config__test-17.snap @@ -0,0 +1,8 @@ +--- +source: src/lib.rs +expression: "p.parse(0, lexer::Lexer::new(\"\\\"h\\\\\\\"i\\\"\"))" + +--- +Ok( + Value<0..6>(h\"i), +) diff --git a/src/snapshots/eww_config__test-18.snap b/src/snapshots/eww_config__test-18.snap new file mode 100644 index 0000000..17db7ac --- /dev/null +++ b/src/snapshots/eww_config__test-18.snap @@ -0,0 +1,8 @@ +--- +source: src/lib.rs +expression: "p.parse(0, lexer::Lexer::new(\"\\\"h\\\\\\\"i\\\"\"))" + +--- +Ok( + Value<0..6>(h\"i), +) diff --git a/src/snapshots/eww_config__test-2.snap b/src/snapshots/eww_config__test-2.snap index dd84a41..c99806f 100644 --- a/src/snapshots/eww_config__test-2.snap +++ b/src/snapshots/eww_config__test-2.snap @@ -1,12 +1,12 @@ --- source: src/lib.rs -expression: "p.parse(\"(12)\")" +expression: "p.parse(0, lexer::Lexer::new(\"(12)\"))" --- Ok( List<0..4>( [ - Number<1..3>(12), + Value<1..3>(12), ], ), ) diff --git a/src/snapshots/eww_config__test-3.snap b/src/snapshots/eww_config__test-3.snap index 5f7b32c..09d221f 100644 --- a/src/snapshots/eww_config__test-3.snap +++ b/src/snapshots/eww_config__test-3.snap @@ -1,13 +1,8 @@ --- source: src/lib.rs -expression: "p.parse(\"(1 2)\")" +expression: "p.parse(0, lexer::Lexer::new(\"1.2\"))" --- Ok( - List<0..5>( - [ - Number<1..2>(1), - Number<3..4>(2), - ], - ), + Value<0..3>(1.2), ) diff --git a/src/snapshots/eww_config__test-4.snap b/src/snapshots/eww_config__test-4.snap index 8ce142b..f29bac3 100644 --- a/src/snapshots/eww_config__test-4.snap +++ b/src/snapshots/eww_config__test-4.snap @@ -1,14 +1,8 @@ --- source: src/lib.rs -expression: "p.parse(\"(1 :foo 1)\")" +expression: "p.parse(0, lexer::Lexer::new(\"-1.2\"))" --- Ok( - List<0..10>( - [ - Number<1..2>(1), - Number<3..7>(:foo), - Number<8..9>(1), - ], - ), + Value<0..4>(-1.2), ) diff --git a/src/snapshots/eww_config__test-5.snap b/src/snapshots/eww_config__test-5.snap index 9db3361..0c3ad08 100644 --- a/src/snapshots/eww_config__test-5.snap +++ b/src/snapshots/eww_config__test-5.snap @@ -1,13 +1,13 @@ --- source: src/lib.rs -expression: "p.parse(\"(:foo 1)\")" +expression: "p.parse(0, lexer::Lexer::new(\"(1 2)\"))" --- Ok( - List<0..8>( + List<0..5>( [ - Number<1..5>(:foo), - Number<6..7>(1), + Value<1..2>(1), + Value<3..4>(2), ], ), ) diff --git a/src/snapshots/eww_config__test-6.snap b/src/snapshots/eww_config__test-6.snap index f368f33..b3b4ab8 100644 --- a/src/snapshots/eww_config__test-6.snap +++ b/src/snapshots/eww_config__test-6.snap @@ -1,13 +1,14 @@ --- source: src/lib.rs -expression: "p.parse(\"(:foo->: 1)\")" +expression: "p.parse(0, lexer::Lexer::new(\"(1 :foo 1)\"))" --- Ok( - List<0..11>( + List<0..10>( [ - Number<1..8>(:foo->:), - Number<9..10>(1), + Value<1..2>(1), + Number<3..7>(:foo), + Value<8..9>(1), ], ), ) diff --git a/src/snapshots/eww_config__test-7.snap b/src/snapshots/eww_config__test-7.snap index eb63df2..de5b5c0 100644 --- a/src/snapshots/eww_config__test-7.snap +++ b/src/snapshots/eww_config__test-7.snap @@ -1,13 +1,13 @@ --- source: src/lib.rs -expression: "p.parse(\"(foo 1)\")" +expression: "p.parse(0, lexer::Lexer::new(\"(:foo 1)\"))" --- Ok( - List<0..7>( + List<0..8>( [ - Symbol<1..4>(foo), - Number<5..6>(1), + Number<1..5>(:foo), + Value<6..7>(1), ], ), ) diff --git a/src/snapshots/eww_config__test-8.snap b/src/snapshots/eww_config__test-8.snap index ee21f3e..c149453 100644 --- a/src/snapshots/eww_config__test-8.snap +++ b/src/snapshots/eww_config__test-8.snap @@ -1,13 +1,13 @@ --- source: src/lib.rs -expression: "p.parse(\"(lol😄 1)\")" +expression: "p.parse(0, lexer::Lexer::new(\"(:foo->: 1)\"))" --- Ok( List<0..11>( [ - Symbol<1..8>(lol😄), - Number<9..10>(1), + Number<1..8>(:foo->:), + Value<9..10>(1), ], ), ) diff --git a/src/snapshots/eww_config__test-9.snap b/src/snapshots/eww_config__test-9.snap index edc1cb8..cd26a6e 100644 --- a/src/snapshots/eww_config__test-9.snap +++ b/src/snapshots/eww_config__test-9.snap @@ -1,13 +1,13 @@ --- source: src/lib.rs -expression: "p.parse(r#\"(test \"hi\")\"#)" +expression: "p.parse(0, lexer::Lexer::new(\"(foo 1)\"))" --- Ok( - List<0..11>( + List<0..7>( [ - Symbol<1..5>(test), - Str<6..10>(hi), + Symbol<1..4>(foo), + Value<5..6>(1), ], ), ) diff --git a/src/snapshots/eww_config__test.snap b/src/snapshots/eww_config__test.snap index 8c42a6b..034b165 100644 --- a/src/snapshots/eww_config__test.snap +++ b/src/snapshots/eww_config__test.snap @@ -1,8 +1,8 @@ --- source: src/lib.rs -expression: "p.parse(\"1\")" +expression: "p.parse(0, lexer::Lexer::new(\"1\"))" --- Ok( - Number<0..1>(1), + Value<0..1>(1), ) From b6a6188b8aa2bd9c8ccb7381b3ab41709415f493 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Mon, 5 Jul 2021 20:09:18 +0200 Subject: [PATCH 021/137] proper parse error messages --- examples/errors.rs | 16 ++++++---------- src/error.rs | 36 ++++++++++++++++++++++++++++++++++-- src/lexer.rs | 23 +++++++++++++++++++++++ src/lib.rs | 11 +++++++++-- src/parser.lalrpop | 8 ++++---- 5 files changed, 76 insertions(+), 18 deletions(-) diff --git a/examples/errors.rs b/examples/errors.rs index 061047a..6476432 100644 --- a/examples/errors.rs +++ b/examples/errors.rs @@ -1,25 +1,21 @@ -use eww_config::{config::*, expr::*, lexer, parser}; +use eww_config::{config::*, expr::*}; fn main() { - let parser = parser::ExprParser::new(); let mut files = codespan_reporting::files::SimpleFiles::new(); let input = "(12 :bar 22 (foo) (baz)"; let file_id = files.add("foo.eww", input); - let lexer = lexer::Lexer::new(input); - - let ast = parser.parse(file_id, lexer); - match ast { + let ast = eww_config::parse_string(file_id, input); + match ast.and_then(Element::::from_expr) { Ok(ast) => { - let element: Result, _> = Element::from_expr(ast); - let err = element.unwrap_err(); - + println!("{:?}", ast); + } + Err(err) => { let diag = err.pretty_diagnostic(&files); use codespan_reporting::term; let mut writer = term::termcolor::StandardStream::stderr(term::termcolor::ColorChoice::Always); term::emit(&mut writer, &term::Config::default(), &files, &diag).unwrap(); } - Err(err) => eprintln!("{:?}", err), } } diff --git a/src/error.rs b/src/error.rs index 14cf169..384df0a 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,4 +1,7 @@ -use crate::expr::{Expr, ExprType, Span}; +use crate::{ + expr::{Expr, ExprType, Span}, + lexer, +}; use codespan_reporting::{diagnostic, files}; use thiserror::Error; @@ -12,17 +15,46 @@ pub enum AstError { MissingNode(Option, ExprType), #[error("Wrong type of expression: Expected {1} but got {2}")] WrongExprType(Option, ExprType, ExprType), + + #[error("Parse error: {source}")] + ParseError { file_id: Option, source: lalrpop_util::ParseError }, } impl AstError { + pub fn get_span(&self) -> Option { + match self { + AstError::InvalidDefinition(span) => *span, + AstError::MissingNode(span, _) => *span, + AstError::WrongExprType(span, ..) => *span, + AstError::ParseError { file_id, source } => file_id.and_then(|id| get_parse_error_span(id, source)), + } + } + pub fn pretty_diagnostic(&self, files: &files::SimpleFiles<&str, &str>) -> diagnostic::Diagnostic { let diag = diagnostic::Diagnostic::error().with_message(format!("{}", self)); - if let AstError::WrongExprType(Some(span), ..) = self { + if let Some(span) = self.get_span() { diag.with_labels(vec![diagnostic::Label::primary(span.2, span.0..span.1)]) } else { diag } } + + pub fn from_parse_error(file_id: usize, err: lalrpop_util::ParseError) -> AstError { + AstError::ParseError { file_id: Some(file_id), source: err } + } +} + +fn get_parse_error_span( + file_id: usize, + err: &lalrpop_util::ParseError, +) -> Option { + 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 } => None, + } } pub fn spanned(span: Span, err: impl Into) -> AstError { diff --git a/src/lexer.rs b/src/lexer.rs index b0efdfe..7a85c67 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -34,9 +34,32 @@ pub enum Token { Error, } +impl std::fmt::Display for Token { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Token::LPren => write!(f, "'('"), + Token::RPren => write!(f, "')'"), + Token::True => write!(f, "true"), + Token::False => write!(f, "false"), + Token::StrLit(x) => write!(f, "\"{}\"", x), + Token::NumLit(x) => write!(f, "{}", x), + Token::Symbol(x) => write!(f, "{}", x), + Token::Keyword(x) => write!(f, "{}", x), + Token::Comment => write!(f, ""), + Token::Error => write!(f, ""), + } + } +} + #[derive(Debug, Eq, PartialEq, Copy, Clone)] pub struct LexicalError(usize, 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 = Result<(Loc, Tok, Loc), Error>; pub struct Lexer<'input> { diff --git a/src/lib.rs b/src/lib.rs index d9fab6f..dbd1316 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,8 +5,9 @@ pub mod config; pub mod error; pub mod expr; -pub mod lexer; -use error::AstError; +mod lexer; +use error::{AstError, AstResult}; +use expr::Expr; use std::{fmt::Display, ops::Deref}; @@ -16,6 +17,12 @@ use lalrpop_util::lalrpop_mod; lalrpop_mod!(pub parser); +pub fn parse_string(file_id: usize, s: &str) -> AstResult { + let lexer = lexer::Lexer::new(s); + let parser = parser::ExprParser::new(); + Ok(parser.parse(file_id, lexer).map_err(|e| AstError::from_parse_error(file_id, e))?) +} + macro_rules! test_parser { ($($text:literal),*) => {{ let p = crate::parser::ExprParser::new(); diff --git a/src/parser.lalrpop b/src/parser.lalrpop index 7734025..4070cef 100644 --- a/src/parser.lalrpop +++ b/src/parser.lalrpop @@ -13,8 +13,8 @@ extern { ")" => Token::RPren, "true" => Token::True, "false" => Token::False, - "strLit" => Token::StrLit(), - "numLit" => Token::NumLit(), + "string" => Token::StrLit(), + "number" => Token::NumLit(), "symbol" => Token::Symbol(), "keyword" => Token::Keyword(), "comment" => Token::Comment, @@ -40,13 +40,13 @@ Value: String = { }; StrLit: String = { - => { + => { x[1..x.len() - 1].to_owned() }, }; -Num: String = <"numLit"> => <>.to_string(); +Num: String = <"number"> => <>.to_string(); Bool: String = { "true" => "true".to_string(), "false" => "false".to_string(), From 641796d38f03f4f4bbcf3e4e6a29c66c8c87606b Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Mon, 5 Jul 2021 20:42:45 +0200 Subject: [PATCH 022/137] add kw --- examples/errors.rs | 2 +- src/expr.rs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/errors.rs b/examples/errors.rs index 6476432..48e394b 100644 --- a/examples/errors.rs +++ b/examples/errors.rs @@ -3,7 +3,7 @@ use eww_config::{config::*, expr::*}; fn main() { let mut files = codespan_reporting::files::SimpleFiles::new(); - let input = "(12 :bar 22 (foo) (baz)"; + let input = "(12 :bar 22 (foo) (baz))"; let file_id = files.add("foo.eww", input); let ast = eww_config::parse_string(file_id, input); diff --git a/src/expr.rs b/src/expr.rs index 7426359..ca67df1 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -66,6 +66,8 @@ impl Expr { as_func!(ExprType::Symbol, as_symbol as_symbol_ref = Expr::Symbol(_, x) => x); + as_func!(ExprType::Keyword, as_keyword as_keyword_ref = Expr::Keyword(_, x) => x); + as_func!(ExprType::List, as_list as_list_ref> = Expr::List(_, x) => x); pub fn expr_type(&self) -> ExprType { From 589948925091100bfca6497662eb0fa424bd007e Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Mon, 12 Jul 2021 11:57:02 +0200 Subject: [PATCH 023/137] rename expr to ast --- examples/errors.rs | 4 +- src/{expr.rs => ast.rs} | 81 +++++++++++++++++++++-------------------- src/config.rs | 22 +++++------ src/error.rs | 10 ++--- src/lib.rs | 10 ++--- src/parser.lalrpop | 14 +++---- 6 files changed, 71 insertions(+), 70 deletions(-) rename src/{expr.rs => ast.rs} (63%) diff --git a/examples/errors.rs b/examples/errors.rs index 48e394b..7f156a3 100644 --- a/examples/errors.rs +++ b/examples/errors.rs @@ -1,4 +1,4 @@ -use eww_config::{config::*, expr::*}; +use eww_config::{ast::*, config::*}; fn main() { let mut files = codespan_reporting::files::SimpleFiles::new(); @@ -7,7 +7,7 @@ fn main() { let file_id = files.add("foo.eww", input); let ast = eww_config::parse_string(file_id, input); - match ast.and_then(Element::::from_expr) { + match ast.and_then(Element::::from_ast) { Ok(ast) => { println!("{:?}", ast); } diff --git a/src/expr.rs b/src/ast.rs similarity index 63% rename from src/expr.rs rename to src/ast.rs index ca67df1..81033fb 100644 --- a/src/expr.rs +++ b/src/ast.rs @@ -1,7 +1,7 @@ use itertools::Itertools; use std::collections::HashMap; -use crate::{config::FromExpr, error::*}; +use crate::{config::FromAst, error::*}; use std::fmt::Display; #[derive(Eq, PartialEq, Clone, Copy)] @@ -20,7 +20,7 @@ impl std::fmt::Debug for Span { } #[derive(Debug, PartialEq, Eq, Copy, Clone)] -pub enum ExprType { +pub enum AstType { List, Keyword, Symbol, @@ -28,15 +28,16 @@ pub enum ExprType { Comment, } -impl Display for ExprType { +impl Display for AstType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{:?}", self) } } #[derive(PartialEq, Eq, Clone)] -pub enum Expr { - List(Span, Vec), +pub enum Ast { + List(Span, Vec), + // ArgList(Span, Vec), Keyword(Span, String), Symbol(Span, String), Value(Span, String), @@ -61,46 +62,46 @@ macro_rules! as_func { }; } -impl Expr { - as_func!(ExprType::Value, as_value as_value_ref = Expr::Value(_, x) => x); +impl Ast { + as_func!(AstType::Value, as_value as_value_ref = Ast::Value(_, x) => x); - as_func!(ExprType::Symbol, as_symbol as_symbol_ref = Expr::Symbol(_, x) => x); + as_func!(AstType::Symbol, as_symbol as_symbol_ref = Ast::Symbol(_, x) => x); - as_func!(ExprType::Keyword, as_keyword as_keyword_ref = Expr::Keyword(_, x) => x); + as_func!(AstType::Keyword, as_keyword as_keyword_ref = Ast::Keyword(_, x) => x); - as_func!(ExprType::List, as_list as_list_ref> = Expr::List(_, x) => x); + as_func!(AstType::List, as_list as_list_ref> = Ast::List(_, x) => x); - pub fn expr_type(&self) -> ExprType { + pub fn expr_type(&self) -> AstType { match self { - Expr::List(..) => ExprType::List, - Expr::Keyword(..) => ExprType::Keyword, - Expr::Symbol(..) => ExprType::Symbol, - Expr::Value(..) => ExprType::Value, - Expr::Comment(_) => ExprType::Comment, + Ast::List(..) => AstType::List, + Ast::Keyword(..) => AstType::Keyword, + Ast::Symbol(..) => AstType::Symbol, + Ast::Value(..) => AstType::Value, + Ast::Comment(_) => AstType::Comment, } } pub fn span(&self) -> Span { match self { - Expr::List(span, _) => *span, - Expr::Keyword(span, _) => *span, - Expr::Symbol(span, _) => *span, - Expr::Value(span, _) => *span, - Expr::Comment(span) => *span, + Ast::List(span, _) => *span, + Ast::Keyword(span, _) => *span, + Ast::Symbol(span, _) => *span, + Ast::Value(span, _) => *span, + Ast::Comment(span) => *span, } } - pub fn first_list_elem(&self) -> Option<&Expr> { + pub fn first_list_elem(&self) -> Option<&Ast> { match self { - Expr::List(_, list) => list.first(), + Ast::List(_, list) => list.first(), _ => None, } } } -impl std::fmt::Display for Expr { +impl std::fmt::Display for Ast { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - use Expr::*; + use Ast::*; match self { List(_, x) => write!(f, "({})", x.iter().map(|e| format!("{}", e)).join(" ")), Keyword(_, x) => write!(f, "{}", x), @@ -110,9 +111,9 @@ impl std::fmt::Display for Expr { } } } -impl std::fmt::Debug for Expr { +impl std::fmt::Debug for Ast { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - use Expr::*; + use Ast::*; match self { List(span, x) => f.debug_tuple(&format!("List<{}>", span)).field(x).finish(), Keyword(span, x) => write!(f, "Number<{}>({})", span, x), @@ -123,7 +124,7 @@ impl std::fmt::Debug for Expr { } } -pub struct ExprIterator> { +pub struct AstIterator> { iter: itertools::PutBack, } @@ -145,24 +146,24 @@ macro_rules! return_or_put_back { }; } -impl> ExprIterator { - return_or_put_back!(expect_symbol, ExprType::Symbol, (Span, String) = Expr::Symbol(span, x) => (span, x)); +impl> AstIterator { + return_or_put_back!(expect_symbol, AstType::Symbol, (Span, String) = Ast::Symbol(span, x) => (span, x)); - return_or_put_back!(expect_string, ExprType::Value, (Span, String) = Expr::Value(span, x) => (span, x)); + return_or_put_back!(expect_string, AstType::Value, (Span, String) = Ast::Value(span, x) => (span, x)); - return_or_put_back!(expect_list, ExprType::List, (Span, Vec) = Expr::List(span, x) => (span, x)); + return_or_put_back!(expect_list, AstType::List, (Span, Vec) = Ast::List(span, x) => (span, x)); pub fn new(iter: I) -> Self { - ExprIterator { iter: itertools::put_back(iter) } + AstIterator { iter: itertools::put_back(iter) } } - pub fn expect_key_values(&mut self) -> AstResult> { + pub fn expect_key_values(&mut self) -> AstResult> { parse_key_values(&mut self.iter) } } -impl> Iterator for ExprIterator { - type Item = Expr; +impl> Iterator for AstIterator { + type Item = Ast; fn next(&mut self) -> Option { self.iter.next() @@ -170,16 +171,16 @@ impl> Iterator for ExprIterator { } /// Parse consecutive `:keyword value` pairs from an expression iterator into a HashMap. Transforms the keys using the FromExpr trait. -fn parse_key_values>(iter: &mut itertools::PutBack) -> AstResult> { +fn parse_key_values>(iter: &mut itertools::PutBack) -> AstResult> { let mut data = HashMap::new(); loop { match iter.next() { - Some(Expr::Keyword(span, kw)) => match iter.next() { + Some(Ast::Keyword(span, kw)) => match iter.next() { Some(value) => { - data.insert(kw, T::from_expr(value)?); + data.insert(kw, T::from_ast(value)?); } None => { - iter.put_back(Expr::Keyword(span, kw)); + iter.put_back(Ast::Keyword(span, kw)); return Ok(data); } }, diff --git a/src/config.rs b/src/config.rs index 5fd1f11..20d1558 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,6 +1,6 @@ use crate::{ + ast::{Ast, AstIterator, AstType, Span}, error::*, - expr::{Expr, ExprIterator, ExprType, Span}, parser, spanned, }; use itertools::Itertools; @@ -14,12 +14,12 @@ type VarName = String; type AttrValue = String; type AttrName = String; -pub trait FromExpr: Sized { - fn from_expr(e: Expr) -> AstResult; +pub trait FromAst: Sized { + fn from_ast(e: Ast) -> AstResult; } -impl FromExpr for Expr { - fn from_expr(e: Expr) -> AstResult { +impl FromAst for Ast { + fn from_ast(e: Ast) -> AstResult { Ok(e) } } @@ -32,15 +32,15 @@ pub struct Element { span: Span, } -impl FromExpr for Element { - fn from_expr(e: Expr) -> AstResult { +impl FromAst for Element { + fn from_ast(e: Ast) -> AstResult { let span = e.span(); spanned!(e.span(), { let list = e.as_list()?; - let mut iter = ExprIterator::new(list.into_iter()); + let mut iter = AstIterator::new(list.into_iter()); let (_, name) = iter.expect_symbol()?; let attrs = iter.expect_key_values()?; - let children = iter.map(C::from_expr).collect::>>()?; + let children = iter.map(C::from_ast).collect::>>()?; Element { span, name, attrs, children } }) } @@ -55,11 +55,11 @@ mod test { #[test] fn test() { - let parser = parser::ExprParser::new(); + let parser = parser::AstParser::new(); insta::with_settings!({sort_maps => true}, { let lexer = lexer::Lexer::new("(box :bar 12 :baz \"hi\" foo (bar))"); insta::assert_debug_snapshot!( - Element::::from_expr(parser.parse(0, lexer).unwrap()).unwrap() + Element::::from_ast(parser.parse(0, lexer).unwrap()).unwrap() ); }); } diff --git a/src/error.rs b/src/error.rs index 384df0a..f89e709 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,5 +1,5 @@ use crate::{ - expr::{Expr, ExprType, Span}, + ast::{Ast, AstType, Span}, lexer, }; use codespan_reporting::{diagnostic, files}; @@ -12,9 +12,9 @@ pub enum AstError { #[error("Definition invalid")] InvalidDefinition(Option), #[error("Expected a {1}, but got nothing")] - MissingNode(Option, ExprType), + MissingNode(Option, AstType), #[error("Wrong type of expression: Expected {1} but got {2}")] - WrongExprType(Option, ExprType, ExprType), + WrongExprType(Option, AstType, AstType), #[error("Parse error: {source}")] ParseError { file_id: Option, source: lalrpop_util::ParseError }, @@ -68,10 +68,10 @@ pub fn spanned(span: Span, err: impl Into) -> AstError { } pub trait OptionAstErrorExt { - fn or_missing(self, t: ExprType) -> Result; + fn or_missing(self, t: AstType) -> Result; } impl OptionAstErrorExt for Option { - fn or_missing(self, t: ExprType) -> Result { + fn or_missing(self, t: AstType) -> Result { self.ok_or(AstError::MissingNode(None, t)) } } diff --git a/src/lib.rs b/src/lib.rs index dbd1316..f21dad6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,12 +2,12 @@ #![allow(unused)] #![feature(try_blocks)] +pub mod ast; pub mod config; pub mod error; -pub mod expr; mod lexer; +use ast::Ast; use error::{AstError, AstResult}; -use expr::Expr; use std::{fmt::Display, ops::Deref}; @@ -17,15 +17,15 @@ use lalrpop_util::lalrpop_mod; lalrpop_mod!(pub parser); -pub fn parse_string(file_id: usize, s: &str) -> AstResult { +pub fn parse_string(file_id: usize, s: &str) -> AstResult { let lexer = lexer::Lexer::new(s); - let parser = parser::ExprParser::new(); + let parser = parser::AstParser::new(); Ok(parser.parse(file_id, lexer).map_err(|e| AstError::from_parse_error(file_id, e))?) } macro_rules! test_parser { ($($text:literal),*) => {{ - let p = crate::parser::ExprParser::new(); + let p = crate::parser::AstParser::new(); use crate::lexer::Lexer; ::insta::with_settings!({sort_maps => true}, { diff --git a/src/parser.lalrpop b/src/parser.lalrpop index 4070cef..c434c48 100644 --- a/src/parser.lalrpop +++ b/src/parser.lalrpop @@ -1,6 +1,6 @@ use std::str::FromStr; use crate::lexer::{Token, LexicalError}; -use crate::expr::{Expr, Span}; +use crate::ast::{Ast, Span}; grammar(file_id: usize); @@ -22,16 +22,16 @@ extern { } -pub Expr: Expr = { - "(" )+> ")" => Expr::List(Span(l, r, file_id), elems), +pub Ast: Ast = { + "(" )+> ")" => Ast::List(Span(l, r, file_id), elems), => x, => x, - => Expr::Value(Span(l, r, file_id), x), - "comment" => Expr::Comment(Span(l, r, file_id)), + => Ast::Value(Span(l, r, file_id), x), + "comment" => Ast::Comment(Span(l, r, file_id)), }; -Keyword: Expr = => Expr::Keyword(Span(l, r, file_id), x.to_string()); -Symbol: Expr = => Expr::Symbol(Span(l, r, file_id), x.to_string()); +Keyword: Ast = => Ast::Keyword(Span(l, r, file_id), x.to_string()); +Symbol: Ast = => Ast::Symbol(Span(l, r, file_id), x.to_string()); Value: String = { => <>, From 923d478b33660a008e52bf1ee6f811c12c7d8e2f Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Mon, 12 Jul 2021 16:45:16 +0200 Subject: [PATCH 024/137] Start implementing parser --- .gitignore | 2 + Cargo.toml | 28 ++++++ build.rs | 4 + rust-toolchain | 1 + rustfmt.toml | 14 +++ src/ast.rs | 94 +++++++++++++++++++++ src/lib.rs | 35 ++++++++ src/parser.lalrpop | 57 +++++++++++++ src/snapshots/simplexpr__tests__test-2.snap | 16 ++++ src/snapshots/simplexpr__tests__test-3.snap | 34 ++++++++ src/snapshots/simplexpr__tests__test-4.snap | 22 +++++ src/snapshots/simplexpr__tests__test-5.snap | 24 ++++++ src/snapshots/simplexpr__tests__test-6.snap | 30 +++++++ src/snapshots/simplexpr__tests__test-7.snap | 30 +++++++ src/snapshots/simplexpr__tests__test-8.snap | 18 ++++ src/snapshots/simplexpr__tests__test.snap | 10 +++ 16 files changed, 419 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 build.rs create mode 100644 rust-toolchain create mode 100644 rustfmt.toml create mode 100644 src/ast.rs create mode 100644 src/lib.rs create mode 100644 src/parser.lalrpop create mode 100644 src/snapshots/simplexpr__tests__test-2.snap create mode 100644 src/snapshots/simplexpr__tests__test-3.snap create mode 100644 src/snapshots/simplexpr__tests__test-4.snap create mode 100644 src/snapshots/simplexpr__tests__test-5.snap create mode 100644 src/snapshots/simplexpr__tests__test-6.snap create mode 100644 src/snapshots/simplexpr__tests__test-7.snap create mode 100644 src/snapshots/simplexpr__tests__test-8.snap create mode 100644 src/snapshots/simplexpr__tests__test.snap diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..87e69fd --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,28 @@ +[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" +codespan-reporting = "0.11" +logos = "0.12" + +serde = {version = "1.0", features = ["derive"]} +serde_json = "1.0" + + + +[build-dependencies] +lalrpop = "0.19.5" + +[dev-dependencies] +insta = "1.7" diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..57684be --- /dev/null +++ b/build.rs @@ -0,0 +1,4 @@ +extern crate lalrpop; +fn main() { + lalrpop::process_root().unwrap(); +} diff --git a/rust-toolchain b/rust-toolchain new file mode 100644 index 0000000..bf867e0 --- /dev/null +++ b/rust-toolchain @@ -0,0 +1 @@ +nightly diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..edce9c8 --- /dev/null +++ b/rustfmt.toml @@ -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" diff --git a/src/ast.rs b/src/ast.rs new file mode 100644 index 0000000..0bb2684 --- /dev/null +++ b/src/ast.rs @@ -0,0 +1,94 @@ +use itertools::Itertools; +use serde::{Deserialize, Serialize}; + +#[derive(Eq, PartialEq, Clone, Copy)] +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) + } +} + +#[derive(Clone, PartialEq, Serialize, Deserialize, Debug)] +pub enum BinOp { + Plus, + Minus, + Times, + Div, + Mod, + Equals, + NotEquals, + And, + Or, + GT, + LT, + Elvis, + RegexMatch, +} + +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, "<"), + BinOp::Elvis => write!(f, "?:"), + BinOp::RegexMatch => 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, Debug)] +pub enum SimplExpr { + Literal(String), + VarRef(String), + BinOp(Box, BinOp, Box), + UnaryOp(UnaryOp, Box), + IfElse(Box, Box, Box), + JsonAccess(Box, Box), + FunctionCall(String, Vec), +} + +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(", ")) + } + } + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..1636430 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,35 @@ +pub mod ast; +use lalrpop_util::lalrpop_mod; + +lalrpop_mod!(pub parser); + +macro_rules! test_parser { + ($($text:literal),*) => {{ + let p = crate::parser::ExprParser::new(); + //use crate::lexer::Lexer; + + ::insta::with_settings!({sort_maps => true}, { + $( + ::insta::assert_debug_snapshot!(p.parse($text)); + )* + }); + }} +} + +#[cfg(test)] +mod tests { + #[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" + ); + } +} diff --git a/src/parser.lalrpop b/src/parser.lalrpop new file mode 100644 index 0000000..2db6bf0 --- /dev/null +++ b/src/parser.lalrpop @@ -0,0 +1,57 @@ + +use crate::ast::{SimplExpr, Span, BinOp, UnaryOp}; + +grammar; + +Comma: Vec = { + ",")*> => match e { + None => v, + Some(e) => { + v.push(e); + v + } + } +}; + +pub Expr: SimplExpr = { + #[precedence(level="0")] + "true" => SimplExpr::Literal("true".to_string()), + "false" => SimplExpr::Literal("false".to_string()), + , + "(" ")", + "(" > ")" => SimplExpr::FunctionCall(ident, args), + + + #[precedence(level="1")] #[assoc(side="left")] + "!" => SimplExpr::UnaryOp(UnaryOp::Not, Box::new(<>)) + + + #[precedence(level="2")] #[assoc(side="left")] + "*" => SimplExpr::BinOp(Box::new(l), BinOp::Times, Box::new(r)), + "/" => SimplExpr::BinOp(Box::new(l), BinOp::Div, Box::new(r)), + "%" => SimplExpr::BinOp(Box::new(l), BinOp::Mod, Box::new(r)), + + #[precedence(level="3")] #[assoc(side="left")] + "+" => SimplExpr::BinOp(Box::new(l), BinOp::Plus, Box::new(r)), + "-" => SimplExpr::BinOp(Box::new(l), BinOp::Minus, Box::new(r)), + + #[precedence(level="4")] #[assoc(side="left")] + "==" => SimplExpr::BinOp(Box::new(l), BinOp::Equals, Box::new(r)), + "!=" => SimplExpr::BinOp(Box::new(l), BinOp::NotEquals, Box::new(r)), + "<" => SimplExpr::BinOp(Box::new(l), BinOp::GT, Box::new(r)), + ">" => SimplExpr::BinOp(Box::new(l), BinOp::LT, Box::new(r)), + "=~" => SimplExpr::BinOp(Box::new(l), BinOp::RegexMatch, Box::new(r)), + + #[precedence(level="5")] #[assoc(side="left")] + "&&" => SimplExpr::BinOp(Box::new(l), BinOp::And, Box::new(r)), + "||" => SimplExpr::BinOp(Box::new(l), BinOp::Or, Box::new(r)), + "?:" => SimplExpr::BinOp(Box::new(l), BinOp::Elvis, Box::new(r)), + + #[precedence(level="6")] #[assoc(side="right")] + "?" ":" => SimplExpr::IfElse(Box::new(cond), Box::new(then), Box::new(els)), +}; + +ExprReset = ; + +Number: SimplExpr = r"[+-]?(?:[0-9]+[.])?[0-9]+" => SimplExpr::Literal(<>.to_string()); +Ident: String = r"[a-zA-Z_][^\s{}\(\)]*" => <>.to_string(); diff --git a/src/snapshots/simplexpr__tests__test-2.snap b/src/snapshots/simplexpr__tests__test-2.snap new file mode 100644 index 0000000..9d2e4fb --- /dev/null +++ b/src/snapshots/simplexpr__tests__test-2.snap @@ -0,0 +1,16 @@ +--- +source: src/lib.rs +expression: "p.parse(\"2 + 5\")" + +--- +Ok( + BinOp( + Literal( + "2", + ), + Plus, + Literal( + "5", + ), + ), +) diff --git a/src/snapshots/simplexpr__tests__test-3.snap b/src/snapshots/simplexpr__tests__test-3.snap new file mode 100644 index 0000000..ed8b2c9 --- /dev/null +++ b/src/snapshots/simplexpr__tests__test-3.snap @@ -0,0 +1,34 @@ +--- +source: src/lib.rs +expression: "p.parse(\"2 * 5 + 1 * 1 + 3\")" + +--- +Ok( + BinOp( + BinOp( + BinOp( + Literal( + "2", + ), + Times, + Literal( + "5", + ), + ), + Plus, + BinOp( + Literal( + "1", + ), + Times, + Literal( + "1", + ), + ), + ), + Plus, + Literal( + "3", + ), + ), +) diff --git a/src/snapshots/simplexpr__tests__test-4.snap b/src/snapshots/simplexpr__tests__test-4.snap new file mode 100644 index 0000000..1c5c5f5 --- /dev/null +++ b/src/snapshots/simplexpr__tests__test-4.snap @@ -0,0 +1,22 @@ +--- +source: src/lib.rs +expression: "p.parse(\"(1 + 2) * 2\")" + +--- +Ok( + BinOp( + BinOp( + Literal( + "1", + ), + Plus, + Literal( + "2", + ), + ), + Times, + Literal( + "2", + ), + ), +) diff --git a/src/snapshots/simplexpr__tests__test-5.snap b/src/snapshots/simplexpr__tests__test-5.snap new file mode 100644 index 0000000..f71ba69 --- /dev/null +++ b/src/snapshots/simplexpr__tests__test-5.snap @@ -0,0 +1,24 @@ +--- +source: src/lib.rs +expression: "p.parse(\"1 + true ? 2 : 5\")" + +--- +Ok( + IfElse( + BinOp( + Literal( + "1", + ), + Plus, + Literal( + "true", + ), + ), + Literal( + "2", + ), + Literal( + "5", + ), + ), +) diff --git a/src/snapshots/simplexpr__tests__test-6.snap b/src/snapshots/simplexpr__tests__test-6.snap new file mode 100644 index 0000000..4efff48 --- /dev/null +++ b/src/snapshots/simplexpr__tests__test-6.snap @@ -0,0 +1,30 @@ +--- +source: src/lib.rs +expression: "p.parse(\"1 + true ? 2 : 5 + 2\")" + +--- +Ok( + IfElse( + BinOp( + Literal( + "1", + ), + Plus, + Literal( + "true", + ), + ), + Literal( + "2", + ), + BinOp( + Literal( + "5", + ), + Plus, + Literal( + "2", + ), + ), + ), +) diff --git a/src/snapshots/simplexpr__tests__test-7.snap b/src/snapshots/simplexpr__tests__test-7.snap new file mode 100644 index 0000000..ff662bd --- /dev/null +++ b/src/snapshots/simplexpr__tests__test-7.snap @@ -0,0 +1,30 @@ +--- +source: src/lib.rs +expression: "p.parse(\"1 + (if true then 2 else 5) + 2\")" + +--- +Ok( + BinOp( + BinOp( + Literal( + "1", + ), + Plus, + IfElse( + Literal( + "true", + ), + Literal( + "2", + ), + Literal( + "5", + ), + ), + ), + Plus, + Literal( + "2", + ), + ), +) diff --git a/src/snapshots/simplexpr__tests__test-8.snap b/src/snapshots/simplexpr__tests__test-8.snap new file mode 100644 index 0000000..976aa45 --- /dev/null +++ b/src/snapshots/simplexpr__tests__test-8.snap @@ -0,0 +1,18 @@ +--- +source: src/lib.rs +expression: "p.parse(\"foo(1, 2)\")" + +--- +Ok( + FunctionCall( + "foo", + [ + Literal( + "1", + ), + Literal( + "2", + ), + ], + ), +) diff --git a/src/snapshots/simplexpr__tests__test.snap b/src/snapshots/simplexpr__tests__test.snap new file mode 100644 index 0000000..929695f --- /dev/null +++ b/src/snapshots/simplexpr__tests__test.snap @@ -0,0 +1,10 @@ +--- +source: src/lib.rs +expression: "p.parse(\"1\")" + +--- +Ok( + Literal( + "1", + ), +) From c5643424ca7367afc510dcaf64e28f021ad9e61f Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Fri, 16 Jul 2021 14:19:54 +0200 Subject: [PATCH 025/137] Finish basic parser --- Cargo.toml | 2 + build.rs | 2 +- examples/errors.rs | 19 ++++ src/ast.rs | 100 +++++++---------- src/error.rs | 46 ++++++++ src/lalrpop_helpers.rs | 3 + src/lexer.rs | 78 ++++++++++++++ src/lib.rs | 38 ++++--- src/parser.lalrpop | 108 ++++++++++++++----- src/snapshots/simplexpr__tests__test-10.snap | 8 ++ src/snapshots/simplexpr__tests__test-11.snap | 8 ++ src/snapshots/simplexpr__tests__test-12.snap | 8 ++ src/snapshots/simplexpr__tests__test-13.snap | 8 ++ src/snapshots/simplexpr__tests__test-14.snap | 30 ++++++ src/snapshots/simplexpr__tests__test-15.snap | 13 +++ src/snapshots/simplexpr__tests__test-16.snap | 18 ++++ src/snapshots/simplexpr__tests__test-17.snap | 12 +++ src/snapshots/simplexpr__tests__test-18.snap | 22 ++++ src/snapshots/simplexpr__tests__test-19.snap | 10 ++ src/snapshots/simplexpr__tests__test-2.snap | 12 +-- src/snapshots/simplexpr__tests__test-20.snap | 16 +++ src/snapshots/simplexpr__tests__test-21.snap | 11 ++ src/snapshots/simplexpr__tests__test-22.snap | 15 +++ src/snapshots/simplexpr__tests__test-23.snap | 12 +++ src/snapshots/simplexpr__tests__test-24.snap | 20 ++++ src/snapshots/simplexpr__tests__test-25.snap | 22 ++++ src/snapshots/simplexpr__tests__test-26.snap | 42 ++++++++ src/snapshots/simplexpr__tests__test-3.snap | 30 +----- src/snapshots/simplexpr__tests__test-4.snap | 18 +--- src/snapshots/simplexpr__tests__test-5.snap | 20 +--- src/snapshots/simplexpr__tests__test-6.snap | 26 +---- src/snapshots/simplexpr__tests__test-7.snap | 26 +---- src/snapshots/simplexpr__tests__test-8.snap | 14 +-- src/snapshots/simplexpr__tests__test-9.snap | 8 ++ src/snapshots/simplexpr__tests__test.snap | 6 +- 35 files changed, 596 insertions(+), 235 deletions(-) create mode 100644 examples/errors.rs create mode 100644 src/error.rs create mode 100644 src/lalrpop_helpers.rs create mode 100644 src/lexer.rs create mode 100644 src/snapshots/simplexpr__tests__test-10.snap create mode 100644 src/snapshots/simplexpr__tests__test-11.snap create mode 100644 src/snapshots/simplexpr__tests__test-12.snap create mode 100644 src/snapshots/simplexpr__tests__test-13.snap create mode 100644 src/snapshots/simplexpr__tests__test-14.snap create mode 100644 src/snapshots/simplexpr__tests__test-15.snap create mode 100644 src/snapshots/simplexpr__tests__test-16.snap create mode 100644 src/snapshots/simplexpr__tests__test-17.snap create mode 100644 src/snapshots/simplexpr__tests__test-18.snap create mode 100644 src/snapshots/simplexpr__tests__test-19.snap create mode 100644 src/snapshots/simplexpr__tests__test-20.snap create mode 100644 src/snapshots/simplexpr__tests__test-21.snap create mode 100644 src/snapshots/simplexpr__tests__test-22.snap create mode 100644 src/snapshots/simplexpr__tests__test-23.snap create mode 100644 src/snapshots/simplexpr__tests__test-24.snap create mode 100644 src/snapshots/simplexpr__tests__test-25.snap create mode 100644 src/snapshots/simplexpr__tests__test-26.snap create mode 100644 src/snapshots/simplexpr__tests__test-9.snap diff --git a/Cargo.toml b/Cargo.toml index 87e69fd..2d6c0c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,8 @@ logos = "0.12" serde = {version = "1.0", features = ["derive"]} serde_json = "1.0" +strum = { version = "0.21", features = ["derive"] } + [build-dependencies] diff --git a/build.rs b/build.rs index 57684be..cbc2c25 100644 --- a/build.rs +++ b/build.rs @@ -1,4 +1,4 @@ extern crate lalrpop; fn main() { - lalrpop::process_root().unwrap(); + lalrpop::Configuration::new().log_verbose().process_current_dir().unwrap(); } diff --git a/examples/errors.rs b/examples/errors.rs new file mode 100644 index 0000000..d089959 --- /dev/null +++ b/examples/errors.rs @@ -0,0 +1,19 @@ +fn main() { + let mut files = codespan_reporting::files::SimpleFiles::new(); + + let input = "12 + \"hi\" * foo ) ? bar == baz : false"; + + let _ = files.add("foo.eww", input); + let ast = simplexpr::parse_string(input); + match ast { + Ok(ast) => { + println!("{:?}", ast); + } + Err(err) => { + let diag = err.pretty_diagnostic(); + use codespan_reporting::term; + let mut writer = term::termcolor::StandardStream::stderr(term::termcolor::ColorChoice::Always); + term::emit(&mut writer, &term::Config::default(), &files, &diag).unwrap(); + } + } +} diff --git a/src/ast.rs b/src/ast.rs index 0bb2684..9ef345a 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -1,8 +1,8 @@ use itertools::Itertools; use serde::{Deserialize, Serialize}; -#[derive(Eq, PartialEq, Clone, Copy)] -pub struct Span(pub usize, pub usize, pub usize); +#[derive(Eq, PartialEq, Clone, Copy, Serialize, Deserialize)] +pub struct Span(pub usize, pub usize); impl std::fmt::Display for Span { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -16,79 +16,59 @@ impl std::fmt::Debug for Span { } } -#[derive(Clone, PartialEq, Serialize, Deserialize, Debug)] +#[rustfmt::skip] +#[derive(Clone, PartialEq, Serialize, Deserialize, Debug, strum::EnumString, strum::Display)] pub enum BinOp { - Plus, - Minus, - Times, - Div, - Mod, - Equals, - NotEquals, - And, - Or, - GT, - LT, - Elvis, - RegexMatch, + #[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, } -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, "<"), - BinOp::Elvis => write!(f, "?:"), - BinOp::RegexMatch => write!(f, "=~"), - } - } -} - -#[derive(Clone, PartialEq, Serialize, Deserialize, Debug)] +#[derive(Clone, PartialEq, Serialize, Deserialize, Debug, strum::EnumString, strum::Display)] pub enum UnaryOp { + #[strum(serialize = "!")] 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, Debug)] +#[derive(Clone, PartialEq, Serialize, Deserialize)] pub enum SimplExpr { - Literal(String), - VarRef(String), - BinOp(Box, BinOp, Box), - UnaryOp(UnaryOp, Box), - IfElse(Box, Box, Box), - JsonAccess(Box, Box), - FunctionCall(String, Vec), + Literal(Span, String), + VarRef(Span, String), + BinOp(Span, Box, BinOp, Box), + UnaryOp(Span, UnaryOp, Box), + IfElse(Span, Box, Box, Box), + JsonAccess(Span, Box, Box), + FunctionCall(Span, String, Vec), } 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) => { + 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 std::fmt::Debug for SimplExpr { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self) + } +} diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..2f04c9b --- /dev/null +++ b/src/error.rs @@ -0,0 +1,46 @@ +use crate::{ast::Span, lexer}; +use codespan_reporting::diagnostic; + +pub type Result = std::result::Result; +pub enum Error { + ParseError { source: lalrpop_util::ParseError }, +} + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::ParseError { source } => write!(f, "Parse error: {}", source), + } + } +} + +impl Error { + pub fn from_parse_error(err: lalrpop_util::ParseError) -> Self { + Error::ParseError { source: err } + } + + pub fn get_span(&self) -> Option { + match self { + Self::ParseError { source } => get_parse_error_span(source), + } + } + + pub fn pretty_diagnostic(&self) -> diagnostic::Diagnostic { + let diag = diagnostic::Diagnostic::error().with_message(format!("{}", self)); + if let Some(span) = self.get_span() { + diag.with_labels(vec![diagnostic::Label::primary(0, span.0..span.1)]) + } else { + diag + } + } +} + +fn get_parse_error_span(err: &lalrpop_util::ParseError) -> Option { + match err { + lalrpop_util::ParseError::InvalidToken { location } => Some(Span(*location, *location)), + lalrpop_util::ParseError::UnrecognizedEOF { location, expected: _ } => Some(Span(*location, *location)), + lalrpop_util::ParseError::UnrecognizedToken { token, expected: _ } => Some(Span(token.0, token.2)), + lalrpop_util::ParseError::ExtraToken { token } => Some(Span(token.0, token.2)), + lalrpop_util::ParseError::User { error: _ } => None, + } +} diff --git a/src/lalrpop_helpers.rs b/src/lalrpop_helpers.rs new file mode 100644 index 0000000..f642a30 --- /dev/null +++ b/src/lalrpop_helpers.rs @@ -0,0 +1,3 @@ +pub fn b(x: T) -> Box { + Box::new(x) +} diff --git a/src/lexer.rs b/src/lexer.rs new file mode 100644 index 0000000..ab73f36 --- /dev/null +++ b/src/lexer.rs @@ -0,0 +1,78 @@ +use logos::Logos; + +#[rustfmt::skip] +#[derive(Logos, Debug, PartialEq, Eq, Clone, strum::Display)] +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(usize, 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 = 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; + + fn next(&mut self) -> Option { + 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))) + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 1636430..96fca9c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,23 +1,33 @@ pub mod ast; +pub mod error; +mod lalrpop_helpers; +mod lexer; +use ast::SimplExpr; +use error::{Error, Result}; use lalrpop_util::lalrpop_mod; lalrpop_mod!(pub parser); -macro_rules! test_parser { - ($($text:literal),*) => {{ - let p = crate::parser::ExprParser::new(); - //use crate::lexer::Lexer; - - ::insta::with_settings!({sort_maps => true}, { - $( - ::insta::assert_debug_snapshot!(p.parse($text)); - )* - }); - }} +pub fn parse_string(s: &str) -> Result { + let lexer = lexer::Lexer::new(s); + let parser = parser::ExprParser::new(); + Ok(parser.parse(lexer).map_err(|e| Error::from_parse_error(e))?) } #[cfg(test)] mod tests { + macro_rules! test_parser { + ($($text:literal),* $(,)?) => {{ + let p = crate::parser::ExprParser::new(); + use crate::lexer::Lexer; + ::insta::with_settings!({sort_maps => true}, { + $( + ::insta::assert_debug_snapshot!(p.parse(Lexer::new($text))); + )* + }); + }} + } + #[test] fn test() { test_parser!( @@ -29,7 +39,11 @@ mod tests { "1 + true ? 2 : 5 + 2", "1 + (true ? 2 : 5) + 2", "foo(1, 2)", - "! false || ! true" + "! false || ! true", + "\"foo\" + 12.4", + "hi[\"ho\"]", + "foo.bar.baz", + "foo.bar[2 + 2] * asdf[foo.bar]", ); } } diff --git a/src/parser.lalrpop b/src/parser.lalrpop index 2db6bf0..d574c7c 100644 --- a/src/parser.lalrpop +++ b/src/parser.lalrpop @@ -1,8 +1,50 @@ -use crate::ast::{SimplExpr, Span, BinOp, UnaryOp}; +use crate::ast::{SimplExpr::{self, *}, Span, BinOp::*, UnaryOp::*}; +use crate::lexer::{Token, LexicalError}; +use crate::lalrpop_helpers::*; + grammar; +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(), + "number" => Token::NumLit(), + "string" => Token::StrLit(), + + } +} + Comma: Vec = { ",")*> => match e { None => v, @@ -15,43 +57,57 @@ Comma: Vec = { pub Expr: SimplExpr = { #[precedence(level="0")] - "true" => SimplExpr::Literal("true".to_string()), - "false" => SimplExpr::Literal("false".to_string()), - , + , + => VarRef(Span(l, r), ident.to_string()), "(" ")", - "(" > ")" => SimplExpr::FunctionCall(ident, args), + #[precedence(level="1")] #[assoc(side="right")] + "(" > ")" => FunctionCall(Span(l, r), ident, args), + "[" "]" => JsonAccess(Span(l, r), b(value), b(index)), - #[precedence(level="1")] #[assoc(side="left")] - "!" => SimplExpr::UnaryOp(UnaryOp::Not, Box::new(<>)) + "." => { + JsonAccess(Span(l, r), b(value), b(Literal(Span(lit_l, r), index))) + }, - - #[precedence(level="2")] #[assoc(side="left")] - "*" => SimplExpr::BinOp(Box::new(l), BinOp::Times, Box::new(r)), - "/" => SimplExpr::BinOp(Box::new(l), BinOp::Div, Box::new(r)), - "%" => SimplExpr::BinOp(Box::new(l), BinOp::Mod, Box::new(r)), + #[precedence(level="2")] #[assoc(side="right")] + "!" => UnaryOp(Span(l, r), Not, b(e)), #[precedence(level="3")] #[assoc(side="left")] - "+" => SimplExpr::BinOp(Box::new(l), BinOp::Plus, Box::new(r)), - "-" => SimplExpr::BinOp(Box::new(l), BinOp::Minus, Box::new(r)), + "*" => BinOp(Span(l, r), b(le), Times, b(re)), + "/" => BinOp(Span(l, r), b(le), Div, b(re)), + "%" => BinOp(Span(l, r), b(le), Mod, b(re)), #[precedence(level="4")] #[assoc(side="left")] - "==" => SimplExpr::BinOp(Box::new(l), BinOp::Equals, Box::new(r)), - "!=" => SimplExpr::BinOp(Box::new(l), BinOp::NotEquals, Box::new(r)), - "<" => SimplExpr::BinOp(Box::new(l), BinOp::GT, Box::new(r)), - ">" => SimplExpr::BinOp(Box::new(l), BinOp::LT, Box::new(r)), - "=~" => SimplExpr::BinOp(Box::new(l), BinOp::RegexMatch, Box::new(r)), + "+" => BinOp(Span(l, r), b(le), Plus, b(re)), + "-" => BinOp(Span(l, r), b(le), Minus, b(re)), #[precedence(level="5")] #[assoc(side="left")] - "&&" => SimplExpr::BinOp(Box::new(l), BinOp::And, Box::new(r)), - "||" => SimplExpr::BinOp(Box::new(l), BinOp::Or, Box::new(r)), - "?:" => SimplExpr::BinOp(Box::new(l), BinOp::Elvis, Box::new(r)), + "==" => BinOp(Span(l, r), b(le), Equals, b(re)), + "!=" => BinOp(Span(l, r), b(le), NotEquals, b(re)), + "<" => BinOp(Span(l, r), b(le), GT, b(re)), + ">" => BinOp(Span(l, r), b(le), LT, b(re)), + "=~" => BinOp(Span(l, r), b(le), RegexMatch, b(re)), - #[precedence(level="6")] #[assoc(side="right")] - "?" ":" => SimplExpr::IfElse(Box::new(cond), Box::new(then), Box::new(els)), + #[precedence(level="6")] #[assoc(side="left")] + "&&" => BinOp(Span(l, r), b(le), And, b(re)), + "||" => BinOp(Span(l, r), b(le), Or, b(re)), + "?:" => BinOp(Span(l, r), b(le), Elvis, b(re)), + + #[precedence(level="7")] #[assoc(side="right")] + "?" ":" => { + IfElse(Span(l, r), b(cond), b(then), b(els)) + }, }; ExprReset = ; -Number: SimplExpr = r"[+-]?(?:[0-9]+[.])?[0-9]+" => SimplExpr::Literal(<>.to_string()); -Ident: String = r"[a-zA-Z_][^\s{}\(\)]*" => <>.to_string(); +Literal: SimplExpr = { + => Literal(Span(l, r), x), + => Literal(Span(l, r), x.to_string()), + "true" => Literal(Span(l, r), "true".to_string()), + "false" => Literal(Span(l, r), "false".to_string()), +} + +StrLit: String = { + => x[1..x.len() - 1].to_owned(), +}; diff --git a/src/snapshots/simplexpr__tests__test-10.snap b/src/snapshots/simplexpr__tests__test-10.snap new file mode 100644 index 0000000..1fa28f0 --- /dev/null +++ b/src/snapshots/simplexpr__tests__test-10.snap @@ -0,0 +1,8 @@ +--- +source: src/lib.rs +expression: "p.parse(Lexer::new(\"\\\"foo\\\" + 12.4\"))" + +--- +Ok( + ("foo" + "12.4"), +) diff --git a/src/snapshots/simplexpr__tests__test-11.snap b/src/snapshots/simplexpr__tests__test-11.snap new file mode 100644 index 0000000..44b65b7 --- /dev/null +++ b/src/snapshots/simplexpr__tests__test-11.snap @@ -0,0 +1,8 @@ +--- +source: src/lib.rs +expression: "p.parse(Lexer::new(\"hi[\\\"ho\\\"]\"))" + +--- +Ok( + hi["ho"], +) diff --git a/src/snapshots/simplexpr__tests__test-12.snap b/src/snapshots/simplexpr__tests__test-12.snap new file mode 100644 index 0000000..e19354b --- /dev/null +++ b/src/snapshots/simplexpr__tests__test-12.snap @@ -0,0 +1,8 @@ +--- +source: src/lib.rs +expression: "p.parse(Lexer::new(\"foo.bar.baz\"))" + +--- +Ok( + foo["bar"]["baz"], +) diff --git a/src/snapshots/simplexpr__tests__test-13.snap b/src/snapshots/simplexpr__tests__test-13.snap new file mode 100644 index 0000000..7494deb --- /dev/null +++ b/src/snapshots/simplexpr__tests__test-13.snap @@ -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"]]), +) diff --git a/src/snapshots/simplexpr__tests__test-14.snap b/src/snapshots/simplexpr__tests__test-14.snap new file mode 100644 index 0000000..692ac35 --- /dev/null +++ b/src/snapshots/simplexpr__tests__test-14.snap @@ -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", + ), + ), +) diff --git a/src/snapshots/simplexpr__tests__test-15.snap b/src/snapshots/simplexpr__tests__test-15.snap new file mode 100644 index 0000000..16b8e8a --- /dev/null +++ b/src/snapshots/simplexpr__tests__test-15.snap @@ -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::>()" + +--- +[ + "foo", + "LPren", + "1", + "Comma", + "2", + "RPren", +] diff --git a/src/snapshots/simplexpr__tests__test-16.snap b/src/snapshots/simplexpr__tests__test-16.snap new file mode 100644 index 0000000..e128f15 --- /dev/null +++ b/src/snapshots/simplexpr__tests__test-16.snap @@ -0,0 +1,18 @@ +--- +source: src/lib.rs +expression: "p.parse(Lexer::new(\"foo(1, 2)\"))" + +--- +Ok( + FunctionCall( + "foo", + [ + Literal( + "1", + ), + Literal( + "2", + ), + ], + ), +) diff --git a/src/snapshots/simplexpr__tests__test-17.snap b/src/snapshots/simplexpr__tests__test-17.snap new file mode 100644 index 0000000..ae5f678 --- /dev/null +++ b/src/snapshots/simplexpr__tests__test-17.snap @@ -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::>()" + +--- +[ + "!", + "False", + "||", + "!", + "True", +] diff --git a/src/snapshots/simplexpr__tests__test-18.snap b/src/snapshots/simplexpr__tests__test-18.snap new file mode 100644 index 0000000..9ffd512 --- /dev/null +++ b/src/snapshots/simplexpr__tests__test-18.snap @@ -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", + ), + ), + ), +) diff --git a/src/snapshots/simplexpr__tests__test-19.snap b/src/snapshots/simplexpr__tests__test-19.snap new file mode 100644 index 0000000..8d6b02b --- /dev/null +++ b/src/snapshots/simplexpr__tests__test-19.snap @@ -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::>()" + +--- +[ + "\"foo\"", + "+", + "12.4", +] diff --git a/src/snapshots/simplexpr__tests__test-2.snap b/src/snapshots/simplexpr__tests__test-2.snap index 9d2e4fb..3b4e5e4 100644 --- a/src/snapshots/simplexpr__tests__test-2.snap +++ b/src/snapshots/simplexpr__tests__test-2.snap @@ -1,16 +1,8 @@ --- source: src/lib.rs -expression: "p.parse(\"2 + 5\")" +expression: "p.parse(Lexer::new(\"2 + 5\"))" --- Ok( - BinOp( - Literal( - "2", - ), - Plus, - Literal( - "5", - ), - ), + ("2" + "5"), ) diff --git a/src/snapshots/simplexpr__tests__test-20.snap b/src/snapshots/simplexpr__tests__test-20.snap new file mode 100644 index 0000000..9d42e5a --- /dev/null +++ b/src/snapshots/simplexpr__tests__test-20.snap @@ -0,0 +1,16 @@ +--- +source: src/lib.rs +expression: "p.parse(Lexer::new(\"\\\"foo\\\" + 12.4\"))" + +--- +Ok( + BinOp( + Literal( + "foo", + ), + Plus, + Literal( + "12.4", + ), + ), +) diff --git a/src/snapshots/simplexpr__tests__test-21.snap b/src/snapshots/simplexpr__tests__test-21.snap new file mode 100644 index 0000000..84a5eb7 --- /dev/null +++ b/src/snapshots/simplexpr__tests__test-21.snap @@ -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::>()" + +--- +[ + "hi", + "LBrack", + "\"ho\"", + "RBrack", +] diff --git a/src/snapshots/simplexpr__tests__test-22.snap b/src/snapshots/simplexpr__tests__test-22.snap new file mode 100644 index 0000000..958cec3 --- /dev/null +++ b/src/snapshots/simplexpr__tests__test-22.snap @@ -0,0 +1,15 @@ +--- +source: src/lib.rs +expression: "p.parse(Lexer::new(\"hi[\\\"ho\\\"]\"))" + +--- +Ok( + JsonAccess( + VarRef( + "hi", + ), + Literal( + "ho", + ), + ), +) diff --git a/src/snapshots/simplexpr__tests__test-23.snap b/src/snapshots/simplexpr__tests__test-23.snap new file mode 100644 index 0000000..6e91f79 --- /dev/null +++ b/src/snapshots/simplexpr__tests__test-23.snap @@ -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::>()" + +--- +[ + "foo", + "Dot", + "bar", + "Dot", + "baz", +] diff --git a/src/snapshots/simplexpr__tests__test-24.snap b/src/snapshots/simplexpr__tests__test-24.snap new file mode 100644 index 0000000..be97009 --- /dev/null +++ b/src/snapshots/simplexpr__tests__test-24.snap @@ -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", + ), + ), +) diff --git a/src/snapshots/simplexpr__tests__test-25.snap b/src/snapshots/simplexpr__tests__test-25.snap new file mode 100644 index 0000000..3a21b7b --- /dev/null +++ b/src/snapshots/simplexpr__tests__test-25.snap @@ -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::>()" + +--- +[ + "foo", + "Dot", + "bar", + "LBrack", + "2", + "+", + "2", + "RBrack", + "*", + "asdf", + "LBrack", + "foo", + "Dot", + "bar", + "RBrack", +] diff --git a/src/snapshots/simplexpr__tests__test-26.snap b/src/snapshots/simplexpr__tests__test-26.snap new file mode 100644 index 0000000..bdabe4e --- /dev/null +++ b/src/snapshots/simplexpr__tests__test-26.snap @@ -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", + ), + ), + ), + ), +) diff --git a/src/snapshots/simplexpr__tests__test-3.snap b/src/snapshots/simplexpr__tests__test-3.snap index ed8b2c9..7a11e2b 100644 --- a/src/snapshots/simplexpr__tests__test-3.snap +++ b/src/snapshots/simplexpr__tests__test-3.snap @@ -1,34 +1,8 @@ --- source: src/lib.rs -expression: "p.parse(\"2 * 5 + 1 * 1 + 3\")" +expression: "p.parse(Lexer::new(\"2 * 5 + 1 * 1 + 3\"))" --- Ok( - BinOp( - BinOp( - BinOp( - Literal( - "2", - ), - Times, - Literal( - "5", - ), - ), - Plus, - BinOp( - Literal( - "1", - ), - Times, - Literal( - "1", - ), - ), - ), - Plus, - Literal( - "3", - ), - ), + ((("2" * "5") + ("1" * "1")) + "3"), ) diff --git a/src/snapshots/simplexpr__tests__test-4.snap b/src/snapshots/simplexpr__tests__test-4.snap index 1c5c5f5..9025ed3 100644 --- a/src/snapshots/simplexpr__tests__test-4.snap +++ b/src/snapshots/simplexpr__tests__test-4.snap @@ -1,22 +1,8 @@ --- source: src/lib.rs -expression: "p.parse(\"(1 + 2) * 2\")" +expression: "p.parse(Lexer::new(\"(1 + 2) * 2\"))" --- Ok( - BinOp( - BinOp( - Literal( - "1", - ), - Plus, - Literal( - "2", - ), - ), - Times, - Literal( - "2", - ), - ), + (("1" + "2") * "2"), ) diff --git a/src/snapshots/simplexpr__tests__test-5.snap b/src/snapshots/simplexpr__tests__test-5.snap index f71ba69..683c97b 100644 --- a/src/snapshots/simplexpr__tests__test-5.snap +++ b/src/snapshots/simplexpr__tests__test-5.snap @@ -1,24 +1,8 @@ --- source: src/lib.rs -expression: "p.parse(\"1 + true ? 2 : 5\")" +expression: "p.parse(Lexer::new(\"1 + true ? 2 : 5\"))" --- Ok( - IfElse( - BinOp( - Literal( - "1", - ), - Plus, - Literal( - "true", - ), - ), - Literal( - "2", - ), - Literal( - "5", - ), - ), + (if ("1" + "true") then "2" else "5"), ) diff --git a/src/snapshots/simplexpr__tests__test-6.snap b/src/snapshots/simplexpr__tests__test-6.snap index 4efff48..ca3c9ba 100644 --- a/src/snapshots/simplexpr__tests__test-6.snap +++ b/src/snapshots/simplexpr__tests__test-6.snap @@ -1,30 +1,8 @@ --- source: src/lib.rs -expression: "p.parse(\"1 + true ? 2 : 5 + 2\")" +expression: "p.parse(Lexer::new(\"1 + true ? 2 : 5 + 2\"))" --- Ok( - IfElse( - BinOp( - Literal( - "1", - ), - Plus, - Literal( - "true", - ), - ), - Literal( - "2", - ), - BinOp( - Literal( - "5", - ), - Plus, - Literal( - "2", - ), - ), - ), + (if ("1" + "true") then "2" else ("5" + "2")), ) diff --git a/src/snapshots/simplexpr__tests__test-7.snap b/src/snapshots/simplexpr__tests__test-7.snap index ff662bd..bd2587c 100644 --- a/src/snapshots/simplexpr__tests__test-7.snap +++ b/src/snapshots/simplexpr__tests__test-7.snap @@ -1,30 +1,8 @@ --- source: src/lib.rs -expression: "p.parse(\"1 + (if true then 2 else 5) + 2\")" +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", - ), - ), + (("1" + (if "true" then "2" else "5")) + "2"), ) diff --git a/src/snapshots/simplexpr__tests__test-8.snap b/src/snapshots/simplexpr__tests__test-8.snap index 976aa45..cbd02d1 100644 --- a/src/snapshots/simplexpr__tests__test-8.snap +++ b/src/snapshots/simplexpr__tests__test-8.snap @@ -1,18 +1,8 @@ --- source: src/lib.rs -expression: "p.parse(\"foo(1, 2)\")" +expression: "p.parse(Lexer::new(\"foo(1, 2)\"))" --- Ok( - FunctionCall( - "foo", - [ - Literal( - "1", - ), - Literal( - "2", - ), - ], - ), + foo("1", "2"), ) diff --git a/src/snapshots/simplexpr__tests__test-9.snap b/src/snapshots/simplexpr__tests__test-9.snap new file mode 100644 index 0000000..eba646a --- /dev/null +++ b/src/snapshots/simplexpr__tests__test-9.snap @@ -0,0 +1,8 @@ +--- +source: src/lib.rs +expression: "p.parse(Lexer::new(\"! false || ! true\"))" + +--- +Ok( + (!"false" || !"true"), +) diff --git a/src/snapshots/simplexpr__tests__test.snap b/src/snapshots/simplexpr__tests__test.snap index 929695f..6c1e712 100644 --- a/src/snapshots/simplexpr__tests__test.snap +++ b/src/snapshots/simplexpr__tests__test.snap @@ -1,10 +1,8 @@ --- source: src/lib.rs -expression: "p.parse(\"1\")" +expression: "p.parse(Lexer::new(\"1\"))" --- Ok( - Literal( - "1", - ), + "1", ) From 3bb2e6516ec2c7b7c194266ba7d0d7c5e4a06b13 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Fri, 16 Jul 2021 14:45:37 +0200 Subject: [PATCH 026/137] add error handling --- src/lib.rs | 1 + src/parser.lalrpop | 49 +++++++++++++++++++++++----------------------- 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 96fca9c..84702b1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ pub mod ast; +pub mod dynval; pub mod error; mod lalrpop_helpers; mod lexer; diff --git a/src/parser.lalrpop b/src/parser.lalrpop index d574c7c..f44a845 100644 --- a/src/parser.lalrpop +++ b/src/parser.lalrpop @@ -11,32 +11,33 @@ extern { 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::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::Not, - "," => Token::Comma, - "?" => Token::Question, - ":" => Token::Colon, - "(" => Token::LPren, - ")" => Token::RPren, - "[" => Token::LBrack, - "]" => Token::RBrack, - "." => Token::Dot, - "true" => Token::True, - "false" => Token::False, + "," => 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(), "number" => Token::NumLit(), From d8ffd3153d951eb4d4c315cda4ba44bd438c95ae Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Fri, 16 Jul 2021 17:39:35 +0200 Subject: [PATCH 027/137] add expression evaluation and dynval --- examples/errors.rs | 2 +- src/EvalError.rs | 0 src/ast.rs | 16 +- src/dynval.rs | 184 +++++++++++++++++ src/error.rs | 52 ++++- src/eval.rs | 195 ++++++++++++++++++ src/lib.rs | 51 +---- src/{ => parser}/lalrpop_helpers.rs | 0 src/{ => parser}/lexer.rs | 0 src/parser/mod.rs | 47 +++++ .../simplexpr_parser.lalrpop} | 14 +- src/simplexpr_parser.lalrpop | 114 ++++++++++ 12 files changed, 613 insertions(+), 62 deletions(-) create mode 100644 src/EvalError.rs create mode 100644 src/dynval.rs create mode 100644 src/eval.rs rename src/{ => parser}/lalrpop_helpers.rs (100%) rename src/{ => parser}/lexer.rs (100%) create mode 100644 src/parser/mod.rs rename src/{parser.lalrpop => parser/simplexpr_parser.lalrpop} (90%) create mode 100644 src/simplexpr_parser.lalrpop diff --git a/examples/errors.rs b/examples/errors.rs index d089959..9bd7053 100644 --- a/examples/errors.rs +++ b/examples/errors.rs @@ -4,7 +4,7 @@ fn main() { let input = "12 + \"hi\" * foo ) ? bar == baz : false"; let _ = files.add("foo.eww", input); - let ast = simplexpr::parse_string(input); + let ast = simplexpr::parser::parse_string(input); match ast { Ok(ast) => { println!("{:?}", ast); diff --git a/src/EvalError.rs b/src/EvalError.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/ast.rs b/src/ast.rs index 9ef345a..96ab4bb 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -1,3 +1,4 @@ +use crate::dynval::DynVal; use itertools::Itertools; use serde::{Deserialize, Serialize}; @@ -42,7 +43,7 @@ pub enum UnaryOp { #[derive(Clone, PartialEq, Serialize, Deserialize)] pub enum SimplExpr { - Literal(Span, String), + Literal(Span, DynVal), VarRef(Span, String), BinOp(Span, Box, BinOp, Box), UnaryOp(Span, UnaryOp, Box), @@ -66,6 +67,19 @@ impl std::fmt::Display for SimplExpr { } } } +impl SimplExpr { + 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 { diff --git a/src/dynval.rs b/src/dynval.rs new file mode 100644 index 0000000..f9352ec --- /dev/null +++ b/src/dynval.rs @@ -0,0 +1,184 @@ +use crate::ast::Span; +use itertools::Itertools; +use serde::{Deserialize, Serialize}; +use std::{convert::TryFrom, fmt, iter::FromIterator}; + +pub type Result = std::result::Result; + +#[derive(Debug, thiserror::Error)] +#[error("Type error: Failed to turn {value} into a {target_type}")] +pub struct ConversionError { + value: DynVal, + target_type: &'static str, + source: Option>, +} + +impl ConversionError { + fn new(value: DynVal, target_type: &'static str, source: Box) -> Self { + ConversionError { value, target_type, source: Some(source) } + } +} + +#[derive(Clone, Deserialize, Serialize, Default)] +pub struct DynVal(pub String, pub Option); + +impl From 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 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 for DynVal { + fn from_iter>(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 { + Ok(DynVal::from_string(s.to_string())) + } +} + +macro_rules! impl_try_from { + (impl From<$typ:ty> { + $(for $for:ty => |$arg:ident| $code:expr);*; + }) => { + $(impl TryFrom<$typ> for $for { + type Error = ConversionError; + fn try_from($arg: $typ) -> std::result::Result { $code } + })* + }; +} +macro_rules! impl_primval_from { + ($($t:ty),*) => { + $(impl From<$t> for DynVal { + fn from(x: $t) -> Self { DynVal(x.to_string(), None) } + })* + }; +} +impl_try_from!(impl From { + for String => |x| x.as_string(); + for f64 => |x| x.as_f64(); + for i32 => |x| x.as_i32(); + for bool => |x| x.as_bool(); + for Vec => |x| x.as_vec(); +}); + +impl_primval_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(|| "".to_string()), + None, + ) + } +} + +impl DynVal { + pub fn from_string(s: String) -> Self { + DynVal(s, None) + } + + pub fn into_inner(self) -> String { + self.0 + } + + /// This will never fail + pub fn as_string(&self) -> Result { + Ok(self.0.to_owned()) + } + + pub fn as_f64(&self) -> Result { + self.0.parse().map_err(|e| ConversionError::new(self.clone(), "f64", Box::new(e))) + } + + pub fn as_i32(&self) -> Result { + self.0.parse().map_err(|e| ConversionError::new(self.clone(), "i32", Box::new(e))) + } + + pub fn as_bool(&self) -> Result { + self.0.parse().map_err(|e| ConversionError::new(self.clone(), "bool", Box::new(e))) + } + + pub fn as_vec(&self) -> Result> { + match self.0.strip_prefix('[').and_then(|x| x.strip_suffix(']')) { + Some(content) => { + let mut items: Vec = 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::from_str::(&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"); + //} +} diff --git a/src/error.rs b/src/error.rs index 2f04c9b..b794344 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,17 +1,24 @@ -use crate::{ast::Span, lexer}; +use crate::{ast::Span, dynval, parser::lexer}; use codespan_reporting::diagnostic; pub type Result = std::result::Result; +#[derive(thiserror::Error, Debug)] pub enum Error { + #[error("Parse error: {source}")] ParseError { source: lalrpop_util::ParseError }, -} + #[error("Conversion error: {source}")] + ConversionError { + #[from] + source: dynval::ConversionError, + }, + #[error("At: {0}: {1}")] + Spanned(Span, Box), -impl std::fmt::Display for Error { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::ParseError { source } => write!(f, "Parse error: {}", source), - } - } + #[error(transparent)] + Eval(crate::eval::EvalError), + + #[error(transparent)] + Other(#[from] Box), } impl Error { @@ -22,6 +29,9 @@ impl Error { pub fn get_span(&self) -> Option { match self { Self::ParseError { source } => get_parse_error_span(source), + Self::Spanned(span, _) => Some(*span), + Self::Eval(err) => err.span(), + _ => None, } } @@ -35,6 +45,23 @@ impl Error { } } +pub trait ErrorExt { + fn at(self, span: Span) -> Error; +} +impl ErrorExt for Box { + fn at(self, span: Span) -> Error { + Error::Spanned(span, self) + } +} +pub trait ResultExt { + fn at(self, span: Span) -> std::result::Result; +} +impl ResultExt for std::result::Result { + fn at(self, span: Span) -> std::result::Result { + self.map_err(|x| Error::Spanned(span, Box::new(x))) + } +} + fn get_parse_error_span(err: &lalrpop_util::ParseError) -> Option { match err { lalrpop_util::ParseError::InvalidToken { location } => Some(Span(*location, *location)), @@ -44,3 +71,12 @@ fn get_parse_error_span(err: &lalrpop_util::ParseError None, } } + +#[macro_export] +macro_rules! spanned { + ($err:ty, $span:expr, $block:expr) => {{ + let span = $span; + let result: Result<_, $err> = try { $block }; + result.at(span) + }}; +} diff --git a/src/eval.rs b/src/eval.rs new file mode 100644 index 0000000..66034c2 --- /dev/null +++ b/src/eval.rs @@ -0,0 +1,195 @@ +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("Invalid regex: {0}")] + InvalidRegex(#[from] regex::Error), + + #[error("got unresolved variable {0}")] + UnresolvedVariable(VarName), + + #[error("Conversion error: {0}")] + ConversionError(#[from] ConversionError), + + #[error("Incorrect number of arguments given to function: {0}")] + WrongArgCount(String), + + #[error("Unknown function {0}")] + UnknownFunction(String), + + #[error("Unable to index into value {0}")] + CannotIndex(String), + + #[error("At {0}: {1}")] + Spanned(Span, Box), +} + +impl EvalError { + pub fn span(&self) -> Option { + match self { + EvalError::Spanned(span, _) => Some(*span), + _ => None, + } + } + + pub fn at(self, span: Span) -> Self { + Self::Spanned(span, Box::new(self)) + } +} + +type VarName = String; + +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)), + 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 SimplExpr::*; + // match self { + //// Literal(x) => Ok(Literal(AttrValue::from_primitive(x.resolve_fully(&variables)?))), + // Literal(x) => Ok(Literal(x)), + // VarRef(ref name) => Ok(Literal(AttrVal::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?), + //)), + // JsonAccess(box a, box b) => { + // Ok(JsonAccess(box a.resolve_refs(variables?), box b.resolve_refs(variables?))) + //} + // FunctionCall(function_name, args) => { + // Ok(FunctionCall(function_name, args.into_iter().map(|a| a.resolve_refs(variables)).collect::>()?)) + //} + + 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(self, values: &HashMap) -> Result { + match self { + SimplExpr::Literal(_, x) => Ok(x), + SimplExpr::VarRef(span, ref name) => { + values.get(name).cloned().ok_or_else(|| EvalError::UnresolvedVariable(name.to_string()).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 => DynVal::from(a.as_f64()? + b.as_f64()?), + 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()?), + 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::>()?; + call_expr_function(&function_name, args).map_err(|e| e.at(span)) + } + } + } +} + +fn call_expr_function(name: &str, args: Vec) -> Result { + 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())), + } +} diff --git a/src/lib.rs b/src/lib.rs index 84702b1..617a06b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,50 +1,11 @@ +#![feature(box_patterns)] +#![feature(box_syntax)] +#![feature(try_blocks)] pub mod ast; pub mod dynval; pub mod error; -mod lalrpop_helpers; -mod lexer; -use ast::SimplExpr; -use error::{Error, Result}; +pub mod eval; +pub mod parser; use lalrpop_util::lalrpop_mod; -lalrpop_mod!(pub parser); - -pub fn parse_string(s: &str) -> Result { - let lexer = lexer::Lexer::new(s); - let parser = parser::ExprParser::new(); - Ok(parser.parse(lexer).map_err(|e| Error::from_parse_error(e))?) -} - -#[cfg(test)] -mod tests { - macro_rules! test_parser { - ($($text:literal),* $(,)?) => {{ - let p = crate::parser::ExprParser::new(); - use crate::lexer::Lexer; - ::insta::with_settings!({sort_maps => true}, { - $( - ::insta::assert_debug_snapshot!(p.parse(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]", - ); - } -} +lalrpop_mod!(pub simplexpr_parser); diff --git a/src/lalrpop_helpers.rs b/src/parser/lalrpop_helpers.rs similarity index 100% rename from src/lalrpop_helpers.rs rename to src/parser/lalrpop_helpers.rs diff --git a/src/lexer.rs b/src/parser/lexer.rs similarity index 100% rename from src/lexer.rs rename to src/parser/lexer.rs diff --git a/src/parser/mod.rs b/src/parser/mod.rs new file mode 100644 index 0000000..ab47e58 --- /dev/null +++ b/src/parser/mod.rs @@ -0,0 +1,47 @@ +pub mod lalrpop_helpers; +pub mod lexer; + +use crate::{ + ast::SimplExpr, + error::{Error, Result}, +}; + +pub fn parse_string(s: &str) -> Result { + let lexer = lexer::Lexer::new(s); + let parser = crate::simplexpr_parser::ExprParser::new(); + Ok(parser.parse(lexer).map_err(|e| Error::from_parse_error(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(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]", + ); + } +} diff --git a/src/parser.lalrpop b/src/parser/simplexpr_parser.lalrpop similarity index 90% rename from src/parser.lalrpop rename to src/parser/simplexpr_parser.lalrpop index f44a845..46dc13a 100644 --- a/src/parser.lalrpop +++ b/src/parser/simplexpr_parser.lalrpop @@ -1,7 +1,7 @@ use crate::ast::{SimplExpr::{self, *}, Span, BinOp::*, UnaryOp::*}; -use crate::lexer::{Token, LexicalError}; -use crate::lalrpop_helpers::*; +use crate::parser::lexer::{Token, LexicalError}; +use crate::parser::lalrpop_helpers::*; grammar; @@ -67,7 +67,7 @@ pub Expr: SimplExpr = { "[" "]" => JsonAccess(Span(l, r), b(value), b(index)), "." => { - JsonAccess(Span(l, r), b(value), b(Literal(Span(lit_l, r), index))) + JsonAccess(Span(l, r), b(value), b(Literal(Span(lit_l, r), index.into()))) }, #[precedence(level="2")] #[assoc(side="right")] @@ -103,10 +103,10 @@ pub Expr: SimplExpr = { ExprReset = ; Literal: SimplExpr = { - => Literal(Span(l, r), x), - => Literal(Span(l, r), x.to_string()), - "true" => Literal(Span(l, r), "true".to_string()), - "false" => Literal(Span(l, r), "false".to_string()), + => Literal(Span(l, r), x.into()), + => Literal(Span(l, r), x.into()), + "true" => Literal(Span(l, r), "true".into()), + "false" => Literal(Span(l, r), "false".into()), } StrLit: String = { diff --git a/src/simplexpr_parser.lalrpop b/src/simplexpr_parser.lalrpop new file mode 100644 index 0000000..46dc13a --- /dev/null +++ b/src/simplexpr_parser.lalrpop @@ -0,0 +1,114 @@ + +use crate::ast::{SimplExpr::{self, *}, Span, BinOp::*, UnaryOp::*}; +use crate::parser::lexer::{Token, LexicalError}; +use crate::parser::lalrpop_helpers::*; + + +grammar; + +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(), + "number" => Token::NumLit(), + "string" => Token::StrLit(), + + } +} + +Comma: Vec = { + ",")*> => match e { + None => v, + Some(e) => { + v.push(e); + v + } + } +}; + +pub Expr: SimplExpr = { + #[precedence(level="0")] + , + => VarRef(Span(l, r), ident.to_string()), + "(" ")", + + #[precedence(level="1")] #[assoc(side="right")] + "(" > ")" => FunctionCall(Span(l, r), ident, args), + "[" "]" => JsonAccess(Span(l, r), b(value), b(index)), + + "." => { + JsonAccess(Span(l, r), b(value), b(Literal(Span(lit_l, r), index.into()))) + }, + + #[precedence(level="2")] #[assoc(side="right")] + "!" => UnaryOp(Span(l, r), Not, b(e)), + + #[precedence(level="3")] #[assoc(side="left")] + "*" => BinOp(Span(l, r), b(le), Times, b(re)), + "/" => BinOp(Span(l, r), b(le), Div, b(re)), + "%" => BinOp(Span(l, r), b(le), Mod, b(re)), + + #[precedence(level="4")] #[assoc(side="left")] + "+" => BinOp(Span(l, r), b(le), Plus, b(re)), + "-" => BinOp(Span(l, r), b(le), Minus, b(re)), + + #[precedence(level="5")] #[assoc(side="left")] + "==" => BinOp(Span(l, r), b(le), Equals, b(re)), + "!=" => BinOp(Span(l, r), b(le), NotEquals, b(re)), + "<" => BinOp(Span(l, r), b(le), GT, b(re)), + ">" => BinOp(Span(l, r), b(le), LT, b(re)), + "=~" => BinOp(Span(l, r), b(le), RegexMatch, b(re)), + + #[precedence(level="6")] #[assoc(side="left")] + "&&" => BinOp(Span(l, r), b(le), And, b(re)), + "||" => BinOp(Span(l, r), b(le), Or, b(re)), + "?:" => BinOp(Span(l, r), b(le), Elvis, b(re)), + + #[precedence(level="7")] #[assoc(side="right")] + "?" ":" => { + IfElse(Span(l, r), b(cond), b(then), b(els)) + }, +}; + +ExprReset = ; + +Literal: SimplExpr = { + => Literal(Span(l, r), x.into()), + => Literal(Span(l, r), x.into()), + "true" => Literal(Span(l, r), "true".into()), + "false" => Literal(Span(l, r), "false".into()), +} + +StrLit: String = { + => x[1..x.len() - 1].to_owned(), +}; From 3b6180ad7d9541a2e15fb25040e9aac85df77774 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Fri, 16 Jul 2021 18:57:22 +0200 Subject: [PATCH 028/137] Better error handling --- examples/errors.rs | 12 ++- src/ast.rs | 23 ++++++ src/dynval.rs | 20 ++++- src/error.rs | 10 +-- src/eval.rs | 13 ++-- src/parser/simplexpr_parser.lalrpop | 114 ---------------------------- src/simplexpr_parser.lalrpop | 9 +-- 7 files changed, 66 insertions(+), 135 deletions(-) delete mode 100644 src/parser/simplexpr_parser.lalrpop diff --git a/examples/errors.rs b/examples/errors.rs index 9bd7053..0ecc8e3 100644 --- a/examples/errors.rs +++ b/examples/errors.rs @@ -1,11 +1,19 @@ +use std::collections::HashMap; + +use simplexpr::dynval::DynVal; + fn main() { let mut files = codespan_reporting::files::SimpleFiles::new(); - let input = "12 + \"hi\" * foo ) ? bar == baz : false"; + let input = "12 + foo * 2 < 2 ? bar == true : false"; let _ = files.add("foo.eww", input); let ast = simplexpr::parser::parse_string(input); - match ast { + + let mut vars = HashMap::new(); + vars.insert("foo".to_string(), "2".into()); + + match ast.and_then(|x| x.eval(&vars).map_err(|e| e.into())) { Ok(ast) => { println!("{:?}", ast); } diff --git a/src/ast.rs b/src/ast.rs index 96ab4bb..ab81e03 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -5,6 +5,20 @@ use serde::{Deserialize, Serialize}; #[derive(Eq, PartialEq, Clone, Copy, Serialize, Deserialize)] pub struct Span(pub usize, pub usize); +pub trait MaybeSpanned { + fn try_span(&self) -> Option; +} + +impl MaybeSpanned for Span { + fn try_span(&self) -> Option { + Some(*self) + } +} + +pub fn span_between(a: impl MaybeSpanned, b: impl MaybeSpanned) -> Option { + Some(Span(a.try_span()?.0, b.try_span()?.1)) +} + impl std::fmt::Display for Span { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}..{}", self.0, self.1) @@ -51,6 +65,11 @@ pub enum SimplExpr { JsonAccess(Span, Box, Box), FunctionCall(Span, String, Vec), } +impl MaybeSpanned for SimplExpr { + fn try_span(&self) -> Option { + Some(self.span()) + } +} impl std::fmt::Display for SimplExpr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -68,6 +87,10 @@ impl std::fmt::Display for SimplExpr { } } 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, diff --git a/src/dynval.rs b/src/dynval.rs index f9352ec..5a38635 100644 --- a/src/dynval.rs +++ b/src/dynval.rs @@ -1,4 +1,4 @@ -use crate::ast::Span; +use crate::ast::{MaybeSpanned, Span}; use itertools::Itertools; use serde::{Deserialize, Serialize}; use std::{convert::TryFrom, fmt, iter::FromIterator}; @@ -6,7 +6,7 @@ use std::{convert::TryFrom, fmt, iter::FromIterator}; pub type Result = std::result::Result; #[derive(Debug, thiserror::Error)] -#[error("Type error: Failed to turn {value} into a {target_type}")] +#[error("Failed to turn {value} into a {target_type}")] pub struct ConversionError { value: DynVal, target_type: &'static str, @@ -17,11 +17,21 @@ impl ConversionError { fn new(value: DynVal, target_type: &'static str, source: Box) -> Self { ConversionError { value, target_type, source: Some(source) } } + + pub fn span(&self) -> Option { + self.value.1 + } } #[derive(Clone, Deserialize, Serialize, Default)] pub struct DynVal(pub String, pub Option); +impl MaybeSpanned for DynVal { + fn try_span(&self) -> Option { + self.1 + } +} + impl From for DynVal { fn from(s: String) -> Self { DynVal(s, None) @@ -30,7 +40,7 @@ impl From for DynVal { impl fmt::Display for DynVal { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.0) + write!(f, "\"{}\"", self.0) } } impl fmt::Debug for DynVal { @@ -106,6 +116,10 @@ impl From<&serde_json::Value> for DynVal { } impl DynVal { + pub fn at(self, span: Span) -> Self { + DynVal(self.0, Some(span)) + } + pub fn from_string(s: String) -> Self { DynVal(s, None) } diff --git a/src/error.rs b/src/error.rs index b794344..b6a50c1 100644 --- a/src/error.rs +++ b/src/error.rs @@ -6,16 +6,13 @@ pub type Result = std::result::Result; pub enum Error { #[error("Parse error: {source}")] ParseError { source: lalrpop_util::ParseError }, - #[error("Conversion error: {source}")] - ConversionError { - #[from] - source: dynval::ConversionError, - }, + #[error("Conversion error: {0}")] + ConversionError(#[from] dynval::ConversionError), #[error("At: {0}: {1}")] Spanned(Span, Box), #[error(transparent)] - Eval(crate::eval::EvalError), + Eval(#[from] crate::eval::EvalError), #[error(transparent)] Other(#[from] Box), @@ -31,6 +28,7 @@ impl Error { Self::ParseError { source } => get_parse_error_span(source), Self::Spanned(span, _) => Some(*span), Self::Eval(err) => err.span(), + Self::ConversionError(err) => err.span(), _ => None, } } diff --git a/src/eval.rs b/src/eval.rs index 66034c2..689818c 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -11,10 +11,10 @@ pub enum EvalError { #[error("Invalid regex: {0}")] InvalidRegex(#[from] regex::Error), - #[error("got unresolved variable {0}")] + #[error("got unresolved variable `{0}`")] UnresolvedVariable(VarName), - #[error("Conversion error: {0}")] + #[error("Type error: {0}")] ConversionError(#[from] ConversionError), #[error("Incorrect number of arguments given to function: {0}")] @@ -34,6 +34,7 @@ impl EvalError { pub fn span(&self) -> Option { match self { EvalError::Spanned(span, _) => Some(*span), + EvalError::ConversionError(err) => err.span(), _ => None, } } @@ -103,10 +104,11 @@ impl SimplExpr { } pub fn eval(self, values: &HashMap) -> Result { - match self { + let span = self.span(); + let value = match self { SimplExpr::Literal(_, x) => Ok(x), SimplExpr::VarRef(span, ref name) => { - values.get(name).cloned().ok_or_else(|| EvalError::UnresolvedVariable(name.to_string()).at(span)) + 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)?; @@ -167,7 +169,8 @@ impl SimplExpr { let args = args.into_iter().map(|a| a.eval(values)).collect::>()?; call_expr_function(&function_name, args).map_err(|e| e.at(span)) } - } + }; + Ok(value?.at(span)) } } diff --git a/src/parser/simplexpr_parser.lalrpop b/src/parser/simplexpr_parser.lalrpop deleted file mode 100644 index 46dc13a..0000000 --- a/src/parser/simplexpr_parser.lalrpop +++ /dev/null @@ -1,114 +0,0 @@ - -use crate::ast::{SimplExpr::{self, *}, Span, BinOp::*, UnaryOp::*}; -use crate::parser::lexer::{Token, LexicalError}; -use crate::parser::lalrpop_helpers::*; - - -grammar; - -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(), - "number" => Token::NumLit(), - "string" => Token::StrLit(), - - } -} - -Comma: Vec = { - ",")*> => match e { - None => v, - Some(e) => { - v.push(e); - v - } - } -}; - -pub Expr: SimplExpr = { - #[precedence(level="0")] - , - => VarRef(Span(l, r), ident.to_string()), - "(" ")", - - #[precedence(level="1")] #[assoc(side="right")] - "(" > ")" => FunctionCall(Span(l, r), ident, args), - "[" "]" => JsonAccess(Span(l, r), b(value), b(index)), - - "." => { - JsonAccess(Span(l, r), b(value), b(Literal(Span(lit_l, r), index.into()))) - }, - - #[precedence(level="2")] #[assoc(side="right")] - "!" => UnaryOp(Span(l, r), Not, b(e)), - - #[precedence(level="3")] #[assoc(side="left")] - "*" => BinOp(Span(l, r), b(le), Times, b(re)), - "/" => BinOp(Span(l, r), b(le), Div, b(re)), - "%" => BinOp(Span(l, r), b(le), Mod, b(re)), - - #[precedence(level="4")] #[assoc(side="left")] - "+" => BinOp(Span(l, r), b(le), Plus, b(re)), - "-" => BinOp(Span(l, r), b(le), Minus, b(re)), - - #[precedence(level="5")] #[assoc(side="left")] - "==" => BinOp(Span(l, r), b(le), Equals, b(re)), - "!=" => BinOp(Span(l, r), b(le), NotEquals, b(re)), - "<" => BinOp(Span(l, r), b(le), GT, b(re)), - ">" => BinOp(Span(l, r), b(le), LT, b(re)), - "=~" => BinOp(Span(l, r), b(le), RegexMatch, b(re)), - - #[precedence(level="6")] #[assoc(side="left")] - "&&" => BinOp(Span(l, r), b(le), And, b(re)), - "||" => BinOp(Span(l, r), b(le), Or, b(re)), - "?:" => BinOp(Span(l, r), b(le), Elvis, b(re)), - - #[precedence(level="7")] #[assoc(side="right")] - "?" ":" => { - IfElse(Span(l, r), b(cond), b(then), b(els)) - }, -}; - -ExprReset = ; - -Literal: SimplExpr = { - => Literal(Span(l, r), x.into()), - => Literal(Span(l, r), x.into()), - "true" => Literal(Span(l, r), "true".into()), - "false" => Literal(Span(l, r), "false".into()), -} - -StrLit: String = { - => x[1..x.len() - 1].to_owned(), -}; diff --git a/src/simplexpr_parser.lalrpop b/src/simplexpr_parser.lalrpop index 46dc13a..ea1ba6f 100644 --- a/src/simplexpr_parser.lalrpop +++ b/src/simplexpr_parser.lalrpop @@ -1,4 +1,3 @@ - use crate::ast::{SimplExpr::{self, *}, Span, BinOp::*, UnaryOp::*}; use crate::parser::lexer::{Token, LexicalError}; use crate::parser::lalrpop_helpers::*; @@ -103,10 +102,10 @@ pub Expr: SimplExpr = { ExprReset = ; Literal: SimplExpr = { - => Literal(Span(l, r), x.into()), - => Literal(Span(l, r), x.into()), - "true" => Literal(Span(l, r), "true".into()), - "false" => Literal(Span(l, r), "false".into()), + => SimplExpr::literal(Span(l, r), x), + => SimplExpr::literal(Span(l, r), x), + "true" => SimplExpr::literal(Span(l, r), "true".into()), + "false" => SimplExpr::literal(Span(l, r), "false".into()), } StrLit: String = { From 752a842cbfad99f82a0c37355c49593ddb771a76 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Fri, 16 Jul 2021 19:19:48 +0200 Subject: [PATCH 029/137] add resolve --- src/eval.rs | 52 +++++++++++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/src/eval.rs b/src/eval.rs index 689818c..4ebd4cf 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -23,6 +23,9 @@ pub enum EvalError { #[error("Unknown function {0}")] UnknownFunction(String), + #[error("Unknown variable {0}")] + UnknownVariable(String), + #[error("Unable to index into value {0}")] CannotIndex(String), @@ -53,34 +56,37 @@ impl SimplExpr { 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) -> Result { - // use SimplExpr::*; - // match self { - //// Literal(x) => Ok(Literal(AttrValue::from_primitive(x.resolve_fully(&variables)?))), - // Literal(x) => Ok(Literal(x)), - // VarRef(ref name) => Ok(Literal(AttrVal::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?), - //)), - // JsonAccess(box a, box b) => { - // Ok(JsonAccess(box a.resolve_refs(variables?), box b.resolve_refs(variables?))) - //} - // FunctionCall(function_name, args) => { - // Ok(FunctionCall(function_name, args.into_iter().map(|a| a.resolve_refs(variables)).collect::>()?)) - //} + pub fn resolve_refs(self, variables: &HashMap) -> Result { + 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::>()?, + )), + 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::*; From 228d10aeb31690e755fc3ed1b7a1eabc9c604b5b Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Fri, 16 Jul 2021 19:46:23 +0200 Subject: [PATCH 030/137] small error handling improvement, allow adding strings --- examples/errors.rs | 2 -- src/error.rs | 6 ++++-- src/eval.rs | 13 ++++++++++--- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/examples/errors.rs b/examples/errors.rs index 0ecc8e3..5b36355 100644 --- a/examples/errors.rs +++ b/examples/errors.rs @@ -1,7 +1,5 @@ use std::collections::HashMap; -use simplexpr::dynval::DynVal; - fn main() { let mut files = codespan_reporting::files::SimpleFiles::new(); diff --git a/src/error.rs b/src/error.rs index b6a50c1..6e91360 100644 --- a/src/error.rs +++ b/src/error.rs @@ -6,9 +6,11 @@ pub type Result = std::result::Result; pub enum Error { #[error("Parse error: {source}")] ParseError { source: lalrpop_util::ParseError }, - #[error("Conversion error: {0}")] + + #[error("Type error: {0}")] ConversionError(#[from] dynval::ConversionError), - #[error("At: {0}: {1}")] + + #[error("{1}")] Spanned(Span, Box), #[error(transparent)] diff --git a/src/eval.rs b/src/eval.rs index 4ebd4cf..2fcb3ce 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -29,7 +29,7 @@ pub enum EvalError { #[error("Unable to index into value {0}")] CannotIndex(String), - #[error("At {0}: {1}")] + #[error("{1}")] Spanned(Span, Box), } @@ -49,6 +49,11 @@ impl EvalError { type VarName = String; +pub trait FunctionSource { + type Err; + fn run_fn(&self, name: &str, args: &Vec) -> Result; +} + impl SimplExpr { pub fn map_terminals_into(self, f: impl Fn(Self) -> Self) -> Self { use SimplExpr::*; @@ -124,8 +129,10 @@ impl SimplExpr { 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 => DynVal::from(a.as_f64()? + b.as_f64()?), + 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()?), From 1118182156fc098b5373a60d3e1af9e84141462b Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Sat, 17 Jul 2021 12:57:12 +0200 Subject: [PATCH 031/137] Small adjustments for integration --- src/ast.rs | 6 +++--- src/dynval.rs | 2 +- src/lib.rs | 6 ++++++ src/parser/lexer.rs | 2 +- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index ab81e03..8b70dd9 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -32,7 +32,7 @@ impl std::fmt::Debug for Span { } #[rustfmt::skip] -#[derive(Clone, PartialEq, Serialize, Deserialize, Debug, strum::EnumString, strum::Display)] +#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug, strum::EnumString, strum::Display)] pub enum BinOp { #[strum(serialize = "+") ] Plus, #[strum(serialize = "-") ] Minus, @@ -49,13 +49,13 @@ pub enum BinOp { #[strum(serialize = "=~")] RegexMatch, } -#[derive(Clone, PartialEq, Serialize, Deserialize, Debug, strum::EnumString, strum::Display)] +#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug, strum::EnumString, strum::Display)] pub enum UnaryOp { #[strum(serialize = "!")] Not, } -#[derive(Clone, PartialEq, Serialize, Deserialize)] +#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum SimplExpr { Literal(Span, DynVal), VarRef(Span, String), diff --git a/src/dynval.rs b/src/dynval.rs index 5a38635..62b2de4 100644 --- a/src/dynval.rs +++ b/src/dynval.rs @@ -23,7 +23,7 @@ impl ConversionError { } } -#[derive(Clone, Deserialize, Serialize, Default)] +#[derive(Clone, Deserialize, Serialize, Default, Eq)] pub struct DynVal(pub String, pub Option); impl MaybeSpanned for DynVal { diff --git a/src/lib.rs b/src/lib.rs index 617a06b..991b775 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,12 @@ pub mod dynval; pub mod error; pub mod eval; pub mod parser; +use ast::SimplExpr; use lalrpop_util::lalrpop_mod; lalrpop_mod!(pub simplexpr_parser); + +pub fn parse_string(s: &str) -> Result { + parser::parse_string(s) +} +pub use ast::Span; diff --git a/src/parser/lexer.rs b/src/parser/lexer.rs index ab73f36..7d49959 100644 --- a/src/parser/lexer.rs +++ b/src/parser/lexer.rs @@ -1,7 +1,7 @@ use logos::Logos; #[rustfmt::skip] -#[derive(Logos, Debug, PartialEq, Eq, Clone, strum::Display)] +#[derive(Logos, Debug, PartialEq, Eq, Clone, strum::Display, strum::EnumString)] pub enum Token { #[strum(serialize = "+") ] #[token("+") ] Plus, #[strum(serialize = "-") ] #[token("-") ] Minus, From 8405d01303919a5d8b47f691642644e9e1150b25 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Sat, 17 Jul 2021 12:57:30 +0200 Subject: [PATCH 032/137] Fully include simplexpr --- Cargo.lock | 172 ++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 10 +++ examples/errors.rs | 2 +- src/ast.rs | 14 +++- src/config.rs | 2 +- src/error.rs | 18 +++-- src/lexer.rs | 31 ++++---- src/lib.rs | 6 +- src/parse_error.rs | 10 +++ src/parser.lalrpop | 21 +++++- src/value/coords.rs | 112 +++++++++++++++++++++++++++++ src/value/mod.rs | 41 +++++++++++ 12 files changed, 412 insertions(+), 27 deletions(-) create mode 100644 src/parse_error.rs create mode 100644 src/value/coords.rs create mode 100644 src/value/mod.rs diff --git a/Cargo.lock b/Cargo.lock index bf2c2b7..f48cb10 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + [[package]] name = "arrayref" version = "0.3.6" @@ -128,6 +137,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + [[package]] name = "crossbeam-utils" version = "0.8.4" @@ -145,6 +160,29 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "ctor" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e98e2ad1a782e33928b96fc3948e7c355e5af34ba4de7670fe8bac2a3b2006d" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "derive_more" +version = "0.99.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40eebddd2156ce1bb37b20bbe5151340a31828b1f2d22ba4141f3531710e38df" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn", +] + [[package]] name = "diff" version = "0.1.12" @@ -194,13 +232,20 @@ name = "eww_config" version = "0.1.0" dependencies = [ "codespan-reporting", + "derive_more", "insta", "itertools", "lalrpop", "lalrpop-util", + "lazy_static", "logos", "maplit", + "pretty_assertions", "regex", + "serde", + "serde_json", + "simplexpr", + "smart-default", "thiserror", ] @@ -233,6 +278,15 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" +[[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.18" @@ -383,6 +437,24 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" +[[package]] +name = "output_vt100" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9" +dependencies = [ + "winapi", +] + +[[package]] +name = "pest" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +dependencies = [ + "ucd-trie", +] + [[package]] name = "petgraph" version = "0.5.1" @@ -414,6 +486,18 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" +[[package]] +name = "pretty_assertions" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cab0e7c02cf376875e9335e0ba1da535775beb5450d21e1dffca068818ed98b" +dependencies = [ + "ansi_term", + "ctor", + "diff", + "output_vt100", +] + [[package]] name = "proc-macro2" version = "1.0.27" @@ -478,12 +562,39 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver", +] + [[package]] name = "ryu" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] + [[package]] name = "serde" version = "1.0.126" @@ -533,12 +644,40 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ad1d488a557b235fc46dae55512ffbfc429d2482b08b4d9435ab07384ca8aec" +[[package]] +name = "simplexpr" +version = "0.1.0" +dependencies = [ + "codespan-reporting", + "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 = "smart-default" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "133659a15339456eeeb07572eb02a91c91e9815e9cbc89566944d2c8d3efdbf6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "string_cache" version = "0.8.1" @@ -551,6 +690,27 @@ dependencies = [ "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" @@ -621,6 +781,18 @@ dependencies = [ "crunchy", ] +[[package]] +name = "ucd-trie" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" + +[[package]] +name = "unicode-segmentation" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" + [[package]] name = "unicode-width" version = "0.1.8" diff --git a/Cargo.toml b/Cargo.toml index 4f2e5ae..79adb01 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,16 @@ maplit = "1.0" codespan-reporting = "0.11" logos = "0.12" +derive_more = "0.99" +smart-default = "0.6" +serde = {version = "1.0", features = ["derive"]} +serde_json = "1.0" +lazy_static = "1.4" +pretty_assertions = "0.7" + + +simplexpr = { path = "../../projects/simplexpr" } + [build-dependencies] lalrpop = "0.19.5" diff --git a/examples/errors.rs b/examples/errors.rs index 7f156a3..3cfd837 100644 --- a/examples/errors.rs +++ b/examples/errors.rs @@ -3,7 +3,7 @@ use eww_config::{ast::*, config::*}; fn main() { let mut files = codespan_reporting::files::SimpleFiles::new(); - let input = "(12 :bar 22 (foo) (baz))"; + let input = r#"(hi :bar 22 :baz {"hi" asdfasdf * 2} (foo) (baz))"#; let file_id = files.add("foo.eww", input); let ast = eww_config::parse_string(file_id, input); diff --git a/src/ast.rs b/src/ast.rs index 81033fb..a3cc70b 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -1,4 +1,5 @@ use itertools::Itertools; +use simplexpr::ast::SimplExpr; use std::collections::HashMap; use crate::{config::FromAst, error::*}; @@ -22,9 +23,11 @@ impl std::fmt::Debug for Span { #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum AstType { List, + Array, Keyword, Symbol, Value, + SimplExpr, Comment, } @@ -37,10 +40,11 @@ impl Display for AstType { #[derive(PartialEq, Eq, Clone)] pub enum Ast { List(Span, Vec), - // ArgList(Span, Vec), + Array(Span, Vec), Keyword(Span, String), Symbol(Span, String), Value(Span, String), + SimplExpr(Span, SimplExpr), Comment(Span), } @@ -74,9 +78,11 @@ impl Ast { pub fn expr_type(&self) -> AstType { match self { Ast::List(..) => AstType::List, + Ast::Array(..) => AstType::Array, Ast::Keyword(..) => AstType::Keyword, Ast::Symbol(..) => AstType::Symbol, Ast::Value(..) => AstType::Value, + Ast::SimplExpr(..) => AstType::SimplExpr, Ast::Comment(_) => AstType::Comment, } } @@ -84,9 +90,11 @@ impl Ast { pub fn span(&self) -> Span { match self { Ast::List(span, _) => *span, + Ast::Array(span, _) => *span, Ast::Keyword(span, _) => *span, Ast::Symbol(span, _) => *span, Ast::Value(span, _) => *span, + Ast::SimplExpr(span, _) => *span, Ast::Comment(span) => *span, } } @@ -104,9 +112,11 @@ impl std::fmt::Display for Ast { use Ast::*; match self { List(_, x) => write!(f, "({})", x.iter().map(|e| format!("{}", e)).join(" ")), + Array(_, x) => write!(f, "({})", x.iter().map(|e| format!("{}", e)).join(" ")), Keyword(_, x) => write!(f, "{}", x), Symbol(_, x) => write!(f, "{}", x), Value(_, x) => write!(f, "{}", x), + SimplExpr(_, x) => write!(f, "{{{}}}", x), Comment(_) => write!(f, ""), } } @@ -116,9 +126,11 @@ impl std::fmt::Debug for Ast { use Ast::*; match self { List(span, x) => f.debug_tuple(&format!("List<{}>", span)).field(x).finish(), + Array(span, x) => f.debug_tuple(&format!("Array<{}>", span)).field(x).finish(), Keyword(span, x) => write!(f, "Number<{}>({})", span, x), Symbol(span, x) => write!(f, "Symbol<{}>({})", span, x), Value(span, x) => write!(f, "Value<{}>({})", span, x), + SimplExpr(span, x) => write!(f, "SimplExpr<{}>({})", span, x), Comment(span) => write!(f, "Comment<{}>", span), } } diff --git a/src/config.rs b/src/config.rs index 20d1558..2753ea3 100644 --- a/src/config.rs +++ b/src/config.rs @@ -57,7 +57,7 @@ mod test { fn test() { let parser = parser::AstParser::new(); insta::with_settings!({sort_maps => true}, { - let lexer = lexer::Lexer::new("(box :bar 12 :baz \"hi\" foo (bar))"); + let lexer = lexer::Lexer::new(0, "(box :bar 12 :baz \"hi\" foo (bar))"); insta::assert_debug_snapshot!( Element::::from_ast(parser.parse(0, lexer).unwrap()).unwrap() ); diff --git a/src/error.rs b/src/error.rs index f89e709..0e6ba14 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,13 +1,13 @@ use crate::{ ast::{Ast, AstType, Span}, - lexer, + lexer, parse_error, }; use codespan_reporting::{diagnostic, files}; use thiserror::Error; pub type AstResult = Result; -#[derive(Debug, PartialEq, Eq, Error)] +#[derive(Debug, Error)] pub enum AstError { #[error("Definition invalid")] InvalidDefinition(Option), @@ -17,7 +17,7 @@ pub enum AstError { WrongExprType(Option, AstType, AstType), #[error("Parse error: {source}")] - ParseError { file_id: Option, source: lalrpop_util::ParseError }, + ParseError { file_id: Option, source: lalrpop_util::ParseError }, } impl AstError { @@ -39,21 +39,27 @@ impl AstError { } } - pub fn from_parse_error(file_id: usize, err: lalrpop_util::ParseError) -> AstError { + pub fn from_parse_error( + file_id: usize, + err: lalrpop_util::ParseError, + ) -> AstError { AstError::ParseError { file_id: Some(file_id), source: err } } } fn get_parse_error_span( file_id: usize, - err: &lalrpop_util::ParseError, + err: &lalrpop_util::ParseError, ) -> Option { 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 } => None, + lalrpop_util::ParseError::User { error } => match error { + parse_error::ParseError::SimplExpr(span, error) => *span, + parse_error::ParseError::LexicalError(span) => Some(*span), + }, } } diff --git a/src/lexer.rs b/src/lexer.rs index 7a85c67..90d1b84 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -1,12 +1,17 @@ use logos::Logos; +use crate::{ast::Span, parse_error}; + #[derive(Logos, Debug, PartialEq, Eq, Clone)] pub enum Token { #[token("(")] LPren, - #[token(")")] RPren, + #[token("[")] + LBrack, + #[token("]")] + RBrack, #[token("true")] True, @@ -26,6 +31,9 @@ pub enum Token { #[regex(r#":\S+"#, |x| x.slice().to_string())] Keyword(String), + #[regex(r#"\{[^}]*\}"#, |x| x.slice().to_string())] + SimplExpr(String), + #[regex(r#";.*"#)] Comment, @@ -39,46 +47,41 @@ impl std::fmt::Display for Token { match self { Token::LPren => write!(f, "'('"), Token::RPren => write!(f, "')'"), + Token::LBrack => write!(f, "'['"), + Token::RBrack => write!(f, "']'"), Token::True => write!(f, "true"), Token::False => write!(f, "false"), Token::StrLit(x) => write!(f, "\"{}\"", x), Token::NumLit(x) => write!(f, "{}", x), Token::Symbol(x) => write!(f, "{}", x), Token::Keyword(x) => write!(f, "{}", x), + Token::SimplExpr(x) => write!(f, "{{{}}}", x), Token::Comment => write!(f, ""), Token::Error => write!(f, ""), } } } -#[derive(Debug, Eq, PartialEq, Copy, Clone)] -pub struct LexicalError(usize, 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 = Result<(Loc, Tok, Loc), Error>; pub struct Lexer<'input> { + file_id: usize, lexer: logos::SpannedIter<'input, Token>, } impl<'input> Lexer<'input> { - pub fn new(text: &'input str) -> Self { - Lexer { lexer: logos::Lexer::new(text).spanned() } + pub fn new(file_id: usize, text: &'input str) -> Self { + Lexer { file_id, lexer: logos::Lexer::new(text).spanned() } } } impl<'input> Iterator for Lexer<'input> { - type Item = SpannedResult; + type Item = SpannedResult; fn next(&mut self) -> Option { let (token, range) = self.lexer.next()?; if token == Token::Error { - Some(Err(LexicalError(range.start, range.end))) + Some(Err(parse_error::ParseError::LexicalError(Span(range.start, range.end, self.file_id)))) } else { Some(Ok((range.start, token, range.end))) } diff --git a/src/lib.rs b/src/lib.rs index f21dad6..cc06510 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,8 @@ pub mod ast; pub mod config; pub mod error; mod lexer; +mod parse_error; +pub mod value; use ast::Ast; use error::{AstError, AstResult}; @@ -18,7 +20,7 @@ use lalrpop_util::lalrpop_mod; lalrpop_mod!(pub parser); pub fn parse_string(file_id: usize, s: &str) -> AstResult { - let lexer = lexer::Lexer::new(s); + let lexer = lexer::Lexer::new(file_id, s); let parser = parser::AstParser::new(); Ok(parser.parse(file_id, lexer).map_err(|e| AstError::from_parse_error(file_id, e))?) } @@ -30,7 +32,7 @@ macro_rules! test_parser { ::insta::with_settings!({sort_maps => true}, { $( - ::insta::assert_debug_snapshot!(p.parse(0, Lexer::new($text))); + ::insta::assert_debug_snapshot!(p.parse(0, Lexer::new(0, $text))); )* }); }} diff --git a/src/parse_error.rs b/src/parse_error.rs new file mode 100644 index 0000000..972fb6c --- /dev/null +++ b/src/parse_error.rs @@ -0,0 +1,10 @@ +use crate::ast::Span; + +#[derive(Debug, thiserror::Error)] +pub enum ParseError { + #[error("{1}")] + SimplExpr(Option, simplexpr::error::Error), + + #[error("Unknown token")] + LexicalError(Span), +} diff --git a/src/parser.lalrpop b/src/parser.lalrpop index c434c48..3ae49a8 100644 --- a/src/parser.lalrpop +++ b/src/parser.lalrpop @@ -1,22 +1,28 @@ use std::str::FromStr; -use crate::lexer::{Token, LexicalError}; +use crate::lexer::{Token}; use crate::ast::{Ast, Span}; +use simplexpr::ast::SimplExpr; +use simplexpr; +use lalrpop_util::ParseError; grammar(file_id: usize); extern { type Location = usize; - type Error = LexicalError; + type Error = crate::parse_error::ParseError; enum Token { "(" => Token::LPren, ")" => Token::RPren, + "[" => Token::LBrack, + "]" => Token::RBrack, "true" => Token::True, "false" => Token::False, "string" => Token::StrLit(), "number" => Token::NumLit(), "symbol" => Token::Symbol(), "keyword" => Token::Keyword(), + "simplexpr" => Token::SimplExpr(), "comment" => Token::Comment, } } @@ -24,6 +30,8 @@ extern { pub Ast: Ast = { "(" )+> ")" => Ast::List(Span(l, r, file_id), elems), + "[" )+> "]" => Ast::Array(Span(l, r, file_id), elems), + => Ast::SimplExpr(Span(l, r, file_id), expr), => x, => x, => Ast::Value(Span(l, r, file_id), x), @@ -45,6 +53,15 @@ StrLit: String = { }, }; +SimplExpr: SimplExpr = { + =>? { + let expr = x[1..x.len() - 1].to_string(); + simplexpr::parse_string(&expr).map_err(|e| { + let span = e.get_span().map(|simplexpr::Span(simpl_l, simpl_r)| Span(1 + l + simpl_l, 1 + l + simpl_r, file_id)); + ParseError::User { error: crate::parse_error::ParseError::SimplExpr(span, e) }}) + } +} + Num: String = <"number"> => <>.to_string(); Bool: String = { diff --git a/src/value/coords.rs b/src/value/coords.rs new file mode 100644 index 0000000..da15e70 --- /dev/null +++ b/src/value/coords.rs @@ -0,0 +1,112 @@ +use derive_more::*; +use serde::{Deserialize, Serialize}; +use smart_default::SmartDefault; +use std::{fmt, str::FromStr}; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("Failed to parse \"{0}\" as a length value")] + NumParseFailed(String), + #[error("Inalid unit \"{0}\", must be either % or px")] + InvalidUnit(String), + #[error("Invalid format. Coordinates must be formated like 200x100")] + MalformedCoords, +} + +#[derive(Clone, Copy, PartialEq, Eq, Deserialize, Serialize, Display, DebugCustom, SmartDefault)] +pub enum NumWithUnit { + #[display(fmt = "{}%", .0)] + #[debug(fmt = "{}%", .0)] + Percent(i32), + #[display(fmt = "{}px", .0)] + #[debug(fmt = "{}px", .0)] + #[default] + Pixels(i32), +} + +impl NumWithUnit { + pub fn relative_to(&self, max: i32) -> i32 { + match *self { + NumWithUnit::Percent(n) => ((max as f64 / 100.0) * n as f64) as i32, + NumWithUnit::Pixels(n) => n, + } + } +} + +impl FromStr for NumWithUnit { + type Err = Error; + + fn from_str(s: &str) -> Result { + lazy_static::lazy_static! { + static ref PATTERN: regex::Regex = regex::Regex::new("^(-?\\d+)(.*)$").unwrap(); + }; + + let captures = PATTERN.captures(s).ok_or_else(|| Error::NumParseFailed(s.to_string()))?; + let value = captures.get(1).unwrap().as_str().parse::().map_err(|_| Error::NumParseFailed(s.to_string()))?; + match captures.get(2).unwrap().as_str() { + "px" | "" => Ok(NumWithUnit::Pixels(value)), + "%" => Ok(NumWithUnit::Percent(value)), + unit => Err(Error::InvalidUnit(unit.to_string())), + } + } +} + +#[derive(Clone, Copy, PartialEq, Eq, Deserialize, Serialize, Display, Default)] +#[display(fmt = "{}*{}", x, y)] +pub struct Coords { + pub x: NumWithUnit, + pub y: NumWithUnit, +} + +impl FromStr for Coords { + type Err = Error; + + fn from_str(s: &str) -> Result { + let (x, y) = s + .split_once(|x: char| x.to_ascii_lowercase() == 'x' || x.to_ascii_lowercase() == '*') + .ok_or_else(|| Error::MalformedCoords)?; + Coords::from_strs(x, y) + } +} + +impl fmt::Debug for Coords { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "CoordsWithUnits({}, {})", self.x, self.y) + } +} + +impl Coords { + pub fn from_pixels(x: i32, y: i32) -> Self { + Coords { x: NumWithUnit::Pixels(x), y: NumWithUnit::Pixels(y) } + } + + /// parse a string for x and a string for y into a [`Coords`] object. + pub fn from_strs(x: &str, y: &str) -> Result { + Ok(Coords { x: x.parse()?, y: y.parse()? }) + } + + /// resolve the possibly relative coordinates relative to a given containers size + pub fn relative_to(&self, width: i32, height: i32) -> (i32, i32) { + (self.x.relative_to(width), self.y.relative_to(height)) + } +} + +#[cfg(test)] +mod test { + use super::*; + use pretty_assertions::assert_eq; + + #[test] + fn test_parse_num_with_unit() { + assert_eq!(NumWithUnit::Pixels(55), NumWithUnit::from_str("55").unwrap()); + assert_eq!(NumWithUnit::Pixels(55), NumWithUnit::from_str("55px").unwrap()); + assert_eq!(NumWithUnit::Percent(55), NumWithUnit::from_str("55%").unwrap()); + assert!(NumWithUnit::from_str("55pp").is_err()); + } + + #[test] + fn test_parse_coords() { + assert_eq!(Coords { x: NumWithUnit::Pixels(50), y: NumWithUnit::Pixels(60) }, Coords::from_str("50x60").unwrap()); + assert!(Coords::from_str("5060").is_err()); + } +} diff --git a/src/value/mod.rs b/src/value/mod.rs new file mode 100644 index 0000000..b3e80a0 --- /dev/null +++ b/src/value/mod.rs @@ -0,0 +1,41 @@ +use derive_more::*; +use serde::{Deserialize, Serialize}; + +pub mod coords; +pub use coords::*; + +/// The name of a variable +#[repr(transparent)] +#[derive(Clone, Hash, PartialEq, Eq, Serialize, Deserialize, AsRef, From, FromStr, Display, DebugCustom)] +#[debug(fmt = "VarName({})", .0)] +pub struct VarName(pub String); + +impl std::borrow::Borrow for VarName { + fn borrow(&self) -> &str { + &self.0 + } +} + +impl From<&str> for VarName { + fn from(s: &str) -> Self { + VarName(s.to_owned()) + } +} + +/// The name of an attribute +#[repr(transparent)] +#[derive(Clone, Hash, PartialEq, Eq, Serialize, Deserialize, AsRef, From, FromStr, Display, DebugCustom)] +#[debug(fmt="AttrName({})", .0)] +pub struct AttrName(pub String); + +impl std::borrow::Borrow for AttrName { + fn borrow(&self) -> &str { + &self.0 + } +} + +impl From<&str> for AttrName { + fn from(s: &str) -> Self { + AttrName(s.to_owned()) + } +} From b12b7e9977e4fba103d23997cc8f5b031507deb1 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Sat, 17 Jul 2021 13:31:23 +0200 Subject: [PATCH 033/137] add readme --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..4a60e3d --- /dev/null +++ b/README.md @@ -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. From 3a79d8650a631045e592dd8818560a719d32f904 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Sat, 17 Jul 2021 17:14:00 +0200 Subject: [PATCH 034/137] Make more fields public --- src/dynval.rs | 6 +++--- src/error.rs | 12 ++++++++++-- src/parser/lexer.rs | 2 +- src/simplexpr_parser.lalrpop | 8 ++++++++ 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/dynval.rs b/src/dynval.rs index 62b2de4..dbdd9d3 100644 --- a/src/dynval.rs +++ b/src/dynval.rs @@ -8,9 +8,9 @@ pub type Result = std::result::Result; #[derive(Debug, thiserror::Error)] #[error("Failed to turn {value} into a {target_type}")] pub struct ConversionError { - value: DynVal, - target_type: &'static str, - source: Option>, + pub value: DynVal, + pub target_type: &'static str, + pub source: Option>, } impl ConversionError { diff --git a/src/error.rs b/src/error.rs index 6e91360..e66b50c 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,4 +1,8 @@ -use crate::{ast::Span, dynval, parser::lexer}; +use crate::{ + ast::Span, + dynval, + parser::lexer::{self, LexicalError}, +}; use codespan_reporting::diagnostic; pub type Result = std::result::Result; @@ -25,6 +29,10 @@ impl Error { Error::ParseError { source: err } } + pub fn at(self, span: Span) -> Self { + Self::Spanned(span, Box::new(self)) + } + pub fn get_span(&self) -> Option { match self { Self::ParseError { source } => get_parse_error_span(source), @@ -68,7 +76,7 @@ fn get_parse_error_span(err: &lalrpop_util::ParseError Some(Span(*location, *location)), lalrpop_util::ParseError::UnrecognizedToken { token, expected: _ } => Some(Span(token.0, token.2)), lalrpop_util::ParseError::ExtraToken { token } => Some(Span(token.0, token.2)), - lalrpop_util::ParseError::User { error: _ } => None, + lalrpop_util::ParseError::User { error: LexicalError(l, r) } => Some(Span(*l, *r)), } } diff --git a/src/parser/lexer.rs b/src/parser/lexer.rs index 7d49959..e3c2447 100644 --- a/src/parser/lexer.rs +++ b/src/parser/lexer.rs @@ -44,7 +44,7 @@ pub enum Token { } #[derive(Debug, Eq, PartialEq, Copy, Clone)] -pub struct LexicalError(usize, usize); +pub struct LexicalError(pub usize, pub usize); impl std::fmt::Display for LexicalError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/src/simplexpr_parser.lalrpop b/src/simplexpr_parser.lalrpop index ea1ba6f..2f1c8dd 100644 --- a/src/simplexpr_parser.lalrpop +++ b/src/simplexpr_parser.lalrpop @@ -1,6 +1,7 @@ use crate::ast::{SimplExpr::{self, *}, Span, BinOp::*, UnaryOp::*}; use crate::parser::lexer::{Token, LexicalError}; use crate::parser::lalrpop_helpers::*; +use lalrpop_util::ParseError; grammar; @@ -42,6 +43,8 @@ extern { "number" => Token::NumLit(), "string" => Token::StrLit(), + "lexer_error" => Token::Error, + } } @@ -56,7 +59,12 @@ Comma: Vec = { }; pub Expr: SimplExpr = { + #[precedence(level="0")] + "lexer_error" =>? { + Err(ParseError::User { error: LexicalError(l, r) }) + }, + , => VarRef(Span(l, r), ident.to_string()), "(" ")", From d12d129eb8e901521d5df4c7e345f33a1046b34c Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Sat, 17 Jul 2021 17:17:46 +0200 Subject: [PATCH 035/137] better error reporting --- examples/errors.rs | 11 ++++-- src/error.rs | 9 ----- src/format_diagnostic.rs | 81 ++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 + 4 files changed, 90 insertions(+), 13 deletions(-) create mode 100644 src/format_diagnostic.rs diff --git a/examples/errors.rs b/examples/errors.rs index 3cfd837..8f6c534 100644 --- a/examples/errors.rs +++ b/examples/errors.rs @@ -1,9 +1,10 @@ -use eww_config::{ast::*, config::*}; +use eww_config::{ast::*, config::*, format_diagnostic::ToDiagnostic}; fn main() { let mut files = codespan_reporting::files::SimpleFiles::new(); - let input = r#"(hi :bar 22 :baz {"hi" asdfasdf * 2} (foo) (baz))"#; + let input = r#" + (hi :bar 22 :baz {(foo == bar ? 12.K : 12)} (foo) (baz))"#; let file_id = files.add("foo.eww", input); let ast = eww_config::parse_string(file_id, input); @@ -12,10 +13,12 @@ fn main() { println!("{:?}", ast); } Err(err) => { - let diag = err.pretty_diagnostic(&files); + dbg!(&err); + let diag = err.to_diagnostic(&files); use codespan_reporting::term; + let config = term::Config::default(); let mut writer = term::termcolor::StandardStream::stderr(term::termcolor::ColorChoice::Always); - term::emit(&mut writer, &term::Config::default(), &files, &diag).unwrap(); + term::emit(&mut writer, &config, &files, &diag).unwrap(); } } } diff --git a/src/error.rs b/src/error.rs index 0e6ba14..095fdef 100644 --- a/src/error.rs +++ b/src/error.rs @@ -30,15 +30,6 @@ impl AstError { } } - pub fn pretty_diagnostic(&self, files: &files::SimpleFiles<&str, &str>) -> diagnostic::Diagnostic { - let diag = diagnostic::Diagnostic::error().with_message(format!("{}", self)); - if let Some(span) = self.get_span() { - diag.with_labels(vec![diagnostic::Label::primary(span.2, span.0..span.1)]) - } else { - diag - } - } - pub fn from_parse_error( file_id: usize, err: lalrpop_util::ParseError, diff --git a/src/format_diagnostic.rs b/src/format_diagnostic.rs new file mode 100644 index 0000000..5fd8a71 --- /dev/null +++ b/src/format_diagnostic.rs @@ -0,0 +1,81 @@ +use codespan_reporting::{diagnostic, files}; +use simplexpr::dynval; + +use crate::{ast::Span, error::AstError, parse_error}; +use diagnostic::*; + +fn span_to_label(span: Span) -> Label { + Label::primary(span.2, span.0..span.1) +} + +pub trait ToDiagnostic { + fn to_diagnostic(&self, files: &files::SimpleFiles<&str, &str>) -> Diagnostic; +} + +impl ToDiagnostic for AstError { + fn to_diagnostic(&self, files: &files::SimpleFiles<&str, &str>) -> Diagnostic { + let diag = Diagnostic::error(); + if let Some(span) = self.get_span() { + use lalrpop_util::ParseError::*; + match self { + AstError::InvalidDefinition(_) => todo!(), + + AstError::MissingNode(_, expected) => diag + .with_message(format!("Missing {}", expected)) + .with_labels(vec![span_to_label(span).with_message(format!("Expected `{}` here", expected))]), + + AstError::WrongExprType(_, expected, actual) => diag + .with_message("Wrong type of expression") + .with_notes(vec![format!("Expected: {}\nGot: {}", expected, actual)]) + .with_labels(vec![span_to_label(span).with_message(format!("Expected a `{}` here", expected))]), + + AstError::ParseError { file_id, source } => { + lalrpop_error_to_diagnostic(source, diag, span, move |diag, error| match error { + parse_error::ParseError::SimplExpr(_, error) => simplexpr_error_to_diagnostic(error, diag, span), + parse_error::ParseError::LexicalError(_) => diag + .with_message("Invalid token") + .with_labels(vec![span_to_label(span).with_message("Invalid token")]), + }) + } + } + } else { + diag.with_message(format!("{}", self)) + } + } +} + +fn lalrpop_error_to_diagnostic( + error: &lalrpop_util::ParseError, + diag: Diagnostic, + span: Span, + handle_user_error: impl FnOnce(Diagnostic, &E) -> Diagnostic, +) -> Diagnostic { + use lalrpop_util::ParseError::*; + match error { + InvalidToken { location } => diag.with_message("Invalid token").with_labels(vec![span_to_label(span)]), + UnrecognizedEOF { location, expected } => diag + .with_message("Input ended unexpectedly. Check if you have any unclosed delimiters") + .with_labels(vec![span_to_label(span)]), + UnrecognizedToken { token, expected } => diag + .with_message(format!("Unexpected token `{}` encoutered", token.1)) + .with_labels(vec![span_to_label(span).with_message("Token unexpected")]), + + ExtraToken { token } => diag.with_message(format!("Extra token encountered: `{}`", token.1)), + User { error } => handle_user_error(diag, error), + } +} + +fn simplexpr_error_to_diagnostic(error: &simplexpr::error::Error, diag: Diagnostic, span: Span) -> Diagnostic { + match error { + simplexpr::error::Error::ParseError { source } => lalrpop_error_to_diagnostic(source, diag, span, move |diag, error| { + diag.with_message("Invalid token").with_labels(vec![span_to_label(span).with_message("Invalid token")]) + }), + simplexpr::error::Error::ConversionError(dynval::ConversionError { value, target_type, source }) => diag + .with_message(format!("{}", error)) + .with_labels(vec![span_to_label(span).with_message(format!("{} is not of type `{}`", value, target_type))]) + .with_notes(source.as_ref().map(|x| vec![format!("{}", x)]).unwrap_or_default()), + simplexpr::error::Error::Spanned(..) => todo!(), + simplexpr::error::Error::Eval(error) => diag.with_message(format!("{}", error)).with_labels(vec![span_to_label(span)]), + simplexpr::error::Error::Other(error) => diag.with_message(format!("{}", error)).with_labels(vec![span_to_label(span)]), + } +} diff --git a/src/lib.rs b/src/lib.rs index cc06510..ffcee55 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,9 +5,11 @@ pub mod ast; pub mod config; pub mod error; +pub mod format_diagnostic; mod lexer; mod parse_error; pub mod value; + use ast::Ast; use error::{AstError, AstResult}; From a6fe813ed115d3b3432a85e1c831644968c9c614 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Sat, 17 Jul 2021 18:07:25 +0200 Subject: [PATCH 036/137] fix clippy lints --- Cargo.toml | 1 - examples/errors.rs | 25 ------------------------ src/ast.rs | 19 ------------------ src/dynval.rs | 48 +++++++++++++++++++--------------------------- src/error.rs | 27 -------------------------- src/eval.rs | 3 ++- src/lib.rs | 5 ++++- src/parser/mod.rs | 2 +- 8 files changed, 27 insertions(+), 103 deletions(-) delete mode 100644 examples/errors.rs diff --git a/Cargo.toml b/Cargo.toml index 2d6c0c2..df354ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,6 @@ regex = "1" itertools = "0.10" thiserror = "1.0" maplit = "1.0" -codespan-reporting = "0.11" logos = "0.12" serde = {version = "1.0", features = ["derive"]} diff --git a/examples/errors.rs b/examples/errors.rs deleted file mode 100644 index 5b36355..0000000 --- a/examples/errors.rs +++ /dev/null @@ -1,25 +0,0 @@ -use std::collections::HashMap; - -fn main() { - let mut files = codespan_reporting::files::SimpleFiles::new(); - - let input = "12 + foo * 2 < 2 ? bar == true : false"; - - let _ = files.add("foo.eww", input); - let ast = simplexpr::parser::parse_string(input); - - let mut vars = HashMap::new(); - vars.insert("foo".to_string(), "2".into()); - - match ast.and_then(|x| x.eval(&vars).map_err(|e| e.into())) { - Ok(ast) => { - println!("{:?}", ast); - } - Err(err) => { - let diag = err.pretty_diagnostic(); - use codespan_reporting::term; - let mut writer = term::termcolor::StandardStream::stderr(term::termcolor::ColorChoice::Always); - term::emit(&mut writer, &term::Config::default(), &files, &diag).unwrap(); - } - } -} diff --git a/src/ast.rs b/src/ast.rs index 8b70dd9..8b7562c 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -5,20 +5,6 @@ use serde::{Deserialize, Serialize}; #[derive(Eq, PartialEq, Clone, Copy, Serialize, Deserialize)] pub struct Span(pub usize, pub usize); -pub trait MaybeSpanned { - fn try_span(&self) -> Option; -} - -impl MaybeSpanned for Span { - fn try_span(&self) -> Option { - Some(*self) - } -} - -pub fn span_between(a: impl MaybeSpanned, b: impl MaybeSpanned) -> Option { - Some(Span(a.try_span()?.0, b.try_span()?.1)) -} - impl std::fmt::Display for Span { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}..{}", self.0, self.1) @@ -65,11 +51,6 @@ pub enum SimplExpr { JsonAccess(Span, Box, Box), FunctionCall(Span, String, Vec), } -impl MaybeSpanned for SimplExpr { - fn try_span(&self) -> Option { - Some(self.span()) - } -} impl std::fmt::Display for SimplExpr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/src/dynval.rs b/src/dynval.rs index dbdd9d3..b3b5b2b 100644 --- a/src/dynval.rs +++ b/src/dynval.rs @@ -1,4 +1,4 @@ -use crate::ast::{MaybeSpanned, Span}; +use crate::ast::Span; use itertools::Itertools; use serde::{Deserialize, Serialize}; use std::{convert::TryFrom, fmt, iter::FromIterator}; @@ -26,12 +26,6 @@ impl ConversionError { #[derive(Clone, Deserialize, Serialize, Default, Eq)] pub struct DynVal(pub String, pub Option); -impl MaybeSpanned for DynVal { - fn try_span(&self) -> Option { - self.1 - } -} - impl From for DynVal { fn from(s: String) -> Self { DynVal(s, None) @@ -98,7 +92,7 @@ impl_try_from!(impl From { for f64 => |x| x.as_f64(); for i32 => |x| x.as_i32(); for bool => |x| x.as_bool(); - for Vec => |x| x.as_vec(); + //for Vec => |x| x.as_vec(); }); impl_primval_from!(bool, i32, u32, f32, u8, f64, &str); @@ -145,26 +139,24 @@ impl DynVal { self.0.parse().map_err(|e| ConversionError::new(self.clone(), "bool", Box::new(e))) } - pub fn as_vec(&self) -> Result> { - match self.0.strip_prefix('[').and_then(|x| x.strip_suffix(']')) { - Some(content) => { - let mut items: Vec = 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_vec(&self) -> Result> { + // match self.0.strip_prefix('[').and_then(|x| x.strip_suffix(']')) { + // Some(content) => { + // let mut items: Vec = 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::from_str::(&self.0) diff --git a/src/error.rs b/src/error.rs index e66b50c..22b6041 100644 --- a/src/error.rs +++ b/src/error.rs @@ -3,7 +3,6 @@ use crate::{ dynval, parser::lexer::{self, LexicalError}, }; -use codespan_reporting::diagnostic; pub type Result = std::result::Result; #[derive(thiserror::Error, Debug)] @@ -42,32 +41,6 @@ impl Error { _ => None, } } - - pub fn pretty_diagnostic(&self) -> diagnostic::Diagnostic { - let diag = diagnostic::Diagnostic::error().with_message(format!("{}", self)); - if let Some(span) = self.get_span() { - diag.with_labels(vec![diagnostic::Label::primary(0, span.0..span.1)]) - } else { - diag - } - } -} - -pub trait ErrorExt { - fn at(self, span: Span) -> Error; -} -impl ErrorExt for Box { - fn at(self, span: Span) -> Error { - Error::Spanned(span, self) - } -} -pub trait ResultExt { - fn at(self, span: Span) -> std::result::Result; -} -impl ResultExt for std::result::Result { - fn at(self, span: Span) -> std::result::Result { - self.map_err(|x| Error::Spanned(span, Box::new(x))) - } } fn get_parse_error_span(err: &lalrpop_util::ParseError) -> Option { diff --git a/src/eval.rs b/src/eval.rs index 2fcb3ce..01d58bd 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -51,7 +51,7 @@ type VarName = String; pub trait FunctionSource { type Err; - fn run_fn(&self, name: &str, args: &Vec) -> Result; + fn run_fn(&self, name: &str, args: &[DynVal]) -> Result; } impl SimplExpr { @@ -139,6 +139,7 @@ impl SimplExpr { 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()?)?; diff --git a/src/lib.rs b/src/lib.rs index 991b775..2398211 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,7 +9,10 @@ pub mod parser; use ast::SimplExpr; use lalrpop_util::lalrpop_mod; -lalrpop_mod!(pub simplexpr_parser); +lalrpop_mod!( + #[allow(clippy::all)] + pub simplexpr_parser +); pub fn parse_string(s: &str) -> Result { parser::parse_string(s) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index ab47e58..a867374 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -9,7 +9,7 @@ use crate::{ pub fn parse_string(s: &str) -> Result { let lexer = lexer::Lexer::new(s); let parser = crate::simplexpr_parser::ExprParser::new(); - Ok(parser.parse(lexer).map_err(|e| Error::from_parse_error(e))?) + parser.parse(lexer).map_err(Error::from_parse_error) } #[cfg(test)] From dacb6e49e28bdff38543069154103b1e1539a48f Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Sat, 17 Jul 2021 18:11:54 +0200 Subject: [PATCH 037/137] fix clippy lints --- Cargo.lock | 1 - src/lib.rs | 7 +++++-- src/value/coords.rs | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f48cb10..fe0b30d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -648,7 +648,6 @@ checksum = "1ad1d488a557b235fc46dae55512ffbfc429d2482b08b4d9435ab07384ca8aec" name = "simplexpr" version = "0.1.0" dependencies = [ - "codespan-reporting", "itertools", "lalrpop", "lalrpop-util", diff --git a/src/lib.rs b/src/lib.rs index ffcee55..30aa4fc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,12 +19,15 @@ use itertools::Itertools; use lalrpop_util::lalrpop_mod; -lalrpop_mod!(pub parser); +lalrpop_mod!( + #[allow(clippy::all)] + pub parser +); pub fn parse_string(file_id: usize, s: &str) -> AstResult { let lexer = lexer::Lexer::new(file_id, s); let parser = parser::AstParser::new(); - Ok(parser.parse(file_id, lexer).map_err(|e| AstError::from_parse_error(file_id, e))?) + parser.parse(file_id, lexer).map_err(|e| AstError::from_parse_error(file_id, e)) } macro_rules! test_parser { diff --git a/src/value/coords.rs b/src/value/coords.rs index da15e70..11728d6 100644 --- a/src/value/coords.rs +++ b/src/value/coords.rs @@ -64,7 +64,7 @@ impl FromStr for Coords { fn from_str(s: &str) -> Result { let (x, y) = s .split_once(|x: char| x.to_ascii_lowercase() == 'x' || x.to_ascii_lowercase() == '*') - .ok_or_else(|| Error::MalformedCoords)?; + .ok_or(Error::MalformedCoords)?; Coords::from_strs(x, y) } } From 378f463b7c02235b1efad5f5f5723d328118bb4c Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Sat, 17 Jul 2021 19:41:26 +0200 Subject: [PATCH 038/137] handwritten lexer --- Cargo.lock | 1 - Cargo.toml | 1 - examples/errors.rs | 5 +- src/lexer.rs | 160 ++++++++++++++---- .../eww_config__config__test__test.snap.new | 21 --- src/snapshots/eww_config__lexer__test-13.snap | 15 ++ src/snapshots/eww_config__lexer__test-2.snap | 15 ++ src/snapshots/eww_config__lexer__test-4.snap | 10 ++ src/snapshots/eww_config__lexer__test-6.snap | 18 ++ src/snapshots/eww_config__lexer__test.snap | 15 ++ 10 files changed, 200 insertions(+), 61 deletions(-) delete mode 100644 src/snapshots/eww_config__config__test__test.snap.new create mode 100644 src/snapshots/eww_config__lexer__test-13.snap create mode 100644 src/snapshots/eww_config__lexer__test-2.snap create mode 100644 src/snapshots/eww_config__lexer__test-4.snap create mode 100644 src/snapshots/eww_config__lexer__test-6.snap create mode 100644 src/snapshots/eww_config__lexer__test.snap diff --git a/Cargo.lock b/Cargo.lock index fe0b30d..ee65321 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -238,7 +238,6 @@ dependencies = [ "lalrpop", "lalrpop-util", "lazy_static", - "logos", "maplit", "pretty_assertions", "regex", diff --git a/Cargo.toml b/Cargo.toml index 79adb01..73a9e5e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,6 @@ itertools = "0.10" thiserror = "1.0" maplit = "1.0" codespan-reporting = "0.11" -logos = "0.12" derive_more = "0.99" smart-default = "0.6" diff --git a/examples/errors.rs b/examples/errors.rs index 8f6c534..63b2655 100644 --- a/examples/errors.rs +++ b/examples/errors.rs @@ -4,7 +4,10 @@ fn main() { let mut files = codespan_reporting::files::SimpleFiles::new(); let input = r#" - (hi :bar 22 :baz {(foo == bar ? 12.K : 12)} (foo) (baz))"#; + (heyho :foo { "foo \" } bar " } + :baz {(foo == bar ? 12.2 : 12)} + (foo) + (baz))"#; let file_id = files.add("foo.eww", input); let ast = eww_config::parse_string(file_id, input); diff --git a/src/lexer.rs b/src/lexer.rs index 90d1b84..4cf46af 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -1,45 +1,22 @@ -use logos::Logos; +use regex::{Regex, RegexSet}; use crate::{ast::Span, parse_error}; -#[derive(Logos, Debug, PartialEq, Eq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone)] pub enum Token { - #[token("(")] LPren, - #[token(")")] RPren, - #[token("[")] LBrack, - #[token("]")] RBrack, - - #[token("true")] True, - - #[token("false")] False, - - #[regex(r#""(?:[^"\\]|\\.)*""#, |x| x.slice().to_string())] StrLit(String), - - #[regex(r#"[+-]?(?:[0-9]+[.])?[0-9]+"#, priority = 2, callback = |x| x.slice().to_string())] NumLit(String), - - #[regex(r#"[a-zA-Z_!\?<>/.*-+][^\s{}\(\)]*"#, |x| x.slice().to_string())] Symbol(String), - - #[regex(r#":\S+"#, |x| x.slice().to_string())] Keyword(String), - - #[regex(r#"\{[^}]*\}"#, |x| x.slice().to_string())] SimplExpr(String), - - #[regex(r#";.*"#)] Comment, - - #[error] - #[regex(r"[ \t\n\f]+", logos::skip)] - Error, + Skip, } impl std::fmt::Display for Token { @@ -57,33 +34,142 @@ impl std::fmt::Display for Token { Token::Keyword(x) => write!(f, "{}", x), Token::SimplExpr(x) => write!(f, "{{{}}}", x), Token::Comment => write!(f, ""), - Token::Error => write!(f, ""), + Token::Skip => write!(f, ""), } } } +pub struct LexIterator { + source: String, + pos: usize, +} + +macro_rules! regex_rules { + ($($regex:literal => $token:expr),*) => { + lazy_static::lazy_static! { + static ref LEXER_REGEX_SET: RegexSet = RegexSet::new(&[ + $(format!("^{}", $regex)),* + ]).unwrap(); + static ref LEXER_REGEXES: Vec = vec![ + $(Regex::new(&format!("^{}", $regex)).unwrap()),* + ]; + static ref LEXER_FNS: Vec Token + Sync>> = vec![ + $(Box::new($token)),* + ]; + } + } +} + +regex_rules! { + r"\(" => |_| Token::LPren, + r"\)" => |_| Token::RPren, + r"\[" => |_| Token::LBrack, + r"\]" => |_| Token::LBrack, + r"true" => |_| Token::True, + r"false" => |_| Token::False, + r#""(?:[^"\\]|\\.)*""# => |x| Token::StrLit(x), + r#"[+-]?(?:[0-9]+[.])?[0-9]+"# => |x| Token::NumLit(x), + r#"[a-zA-Z_!\?<>/.*-+][^\s{}\(\)]*"# => |x| Token::Symbol(x), + r#":\S+"# => |x| Token::Keyword(x), + r#";.*"# => |_| Token::Comment, + r"[ \t\n\f]+" => |_| Token::Skip +} + +impl Iterator for LexIterator { + type Item = (usize, Token, usize); + + fn next(&mut self) -> Option { + loop { + if self.pos >= self.source.len() { + return None; + } + let string = &self.source[self.pos..]; + + if string.starts_with('{') { + self.pos += 1; + let expr_start = self.pos; + let mut in_string = false; + loop { + if self.pos >= self.source.len() { + return None; + } + let string = &self.source[self.pos..]; + + if string.starts_with('}') && !in_string { + let tok_str = &self.source[expr_start..self.pos]; + self.pos += 1; + return Some((expr_start, Token::SimplExpr(tok_str.to_string()), self.pos - 1)); + } else if string.starts_with('"') { + self.pos += 1; + in_string = !in_string; + } else if string.starts_with("\\\"") { + self.pos += 2; + } else { + self.pos += 1; + } + } + } else { + let match_set = LEXER_REGEX_SET.matches(string); + let (len, i) = match_set + .into_iter() + .map(|i: usize| { + let m = LEXER_REGEXES[i].find(string).unwrap(); + (m.end(), i) + }) + .next() + .unwrap(); + + let tok_str = &self.source[self.pos..self.pos + len]; + let old_pos = self.pos; + self.pos += len; + match LEXER_FNS[i](tok_str.to_string()) { + Token::Skip => {} + token => return Some((old_pos, token, self.pos)), + } + } + } + } +} + +macro_rules! test_lexer { + ($($text:literal),*) => {{ + ::insta::with_settings!({sort_maps => true}, { + $( + ::insta::assert_debug_snapshot!( + LexIterator { pos: 0, source: $text.to_string() }.map(|x| x.1).collect::>() + ); + )* + }); + }} +} + +#[test] +fn test() { + test_lexer!(r#"(test "h\"i")"#, r#"(foo { "}" })"#); +} + pub type SpannedResult = Result<(Loc, Tok, Loc), Error>; -pub struct Lexer<'input> { +pub struct Lexer { file_id: usize, - lexer: logos::SpannedIter<'input, Token>, + lexer: LexIterator, } -impl<'input> Lexer<'input> { - pub fn new(file_id: usize, text: &'input str) -> Self { - Lexer { file_id, lexer: logos::Lexer::new(text).spanned() } +impl Lexer { + pub fn new(file_id: usize, text: &str) -> Self { + Lexer { file_id, lexer: LexIterator { source: text.to_string(), pos: 0 } } } } -impl<'input> Iterator for Lexer<'input> { +impl Iterator for Lexer { type Item = SpannedResult; fn next(&mut self) -> Option { - let (token, range) = self.lexer.next()?; - if token == Token::Error { - Some(Err(parse_error::ParseError::LexicalError(Span(range.start, range.end, self.file_id)))) + let (l, token, r) = self.lexer.next()?; + if token == Token::Skip { + Some(Err(parse_error::ParseError::LexicalError(Span(l, r, self.file_id)))) } else { - Some(Ok((range.start, token, range.end))) + Some(Ok((l, token, r))) } } } diff --git a/src/snapshots/eww_config__config__test__test.snap.new b/src/snapshots/eww_config__config__test__test.snap.new deleted file mode 100644 index 40a3984..0000000 --- a/src/snapshots/eww_config__config__test__test.snap.new +++ /dev/null @@ -1,21 +0,0 @@ ---- -source: src/config.rs -expression: "Element::::from_expr(parser.parse(0, lexer).unwrap()).unwrap()" - ---- -Element { - name: "box", - attrs: { - ":baz": Value<18..22>(hi), - ":bar": Value<10..12>(12), - }, - children: [ - Symbol<23..26>(foo), - List<27..32>( - [ - Symbol<28..31>(bar), - ], - ), - ], - span: 0..33, -} diff --git a/src/snapshots/eww_config__lexer__test-13.snap b/src/snapshots/eww_config__lexer__test-13.snap new file mode 100644 index 0000000..70fceff --- /dev/null +++ b/src/snapshots/eww_config__lexer__test-13.snap @@ -0,0 +1,15 @@ +--- +source: src/lexer.rs +expression: "LexIterator{pos: 0,\n source:\n r#\"(test \" hi \")\"#.to_string(),}.map(|x|\n x.1).collect::>()" + +--- +[ + LPren, + Symbol( + "test", + ), + StrLit( + "\" hi \"", + ), + RPren, +] diff --git a/src/snapshots/eww_config__lexer__test-2.snap b/src/snapshots/eww_config__lexer__test-2.snap new file mode 100644 index 0000000..11bb951 --- /dev/null +++ b/src/snapshots/eww_config__lexer__test-2.snap @@ -0,0 +1,15 @@ +--- +source: src/lexer.rs +expression: "LexIterator{pos: 0,\n source:\n r#\"(foo { \"}\" })\"#.to_string(),}.map(|x|\n x.1).collect::>()" + +--- +[ + LPren, + Symbol( + "foo", + ), + SimplExpr( + " \"}\" ", + ), + RPren, +] diff --git a/src/snapshots/eww_config__lexer__test-4.snap b/src/snapshots/eww_config__lexer__test-4.snap new file mode 100644 index 0000000..c8700fa --- /dev/null +++ b/src/snapshots/eww_config__lexer__test-4.snap @@ -0,0 +1,10 @@ +--- +source: src/lexer.rs +expression: "LexIterator{pos: 0,\n source: \"-1.2\".to_string(),}.map(|x| x.1).collect::>()" + +--- +[ + NumLit( + "-1.2", + ), +] diff --git a/src/snapshots/eww_config__lexer__test-6.snap b/src/snapshots/eww_config__lexer__test-6.snap new file mode 100644 index 0000000..88f2846 --- /dev/null +++ b/src/snapshots/eww_config__lexer__test-6.snap @@ -0,0 +1,18 @@ +--- +source: src/lexer.rs +expression: "LexIterator{pos: 0,\n source:\n \"(1 :foo 1)\".to_string(),}.map(|x| x.1).collect::>()" + +--- +[ + LPren, + NumLit( + "1", + ), + Keyword( + ":foo", + ), + NumLit( + "1", + ), + RPren, +] diff --git a/src/snapshots/eww_config__lexer__test.snap b/src/snapshots/eww_config__lexer__test.snap new file mode 100644 index 0000000..0f6e502 --- /dev/null +++ b/src/snapshots/eww_config__lexer__test.snap @@ -0,0 +1,15 @@ +--- +source: src/lexer.rs +expression: "LexIterator{pos: 0,\n source:\n r#\"(test \"h\\\"i\")\"#.to_string(),}.map(|x|\n x.1).collect::>()" + +--- +[ + LPren, + Symbol( + "test", + ), + StrLit( + "\"h\\\"i\"", + ), + RPren, +] From e723335db6b549fd733d8a133b4ec6d8728aebf8 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Sat, 17 Jul 2021 19:56:56 +0200 Subject: [PATCH 039/137] Cleanup parser --- src/config.rs | 2 +- src/lexer.rs | 112 +++++++----------- src/lib.rs | 4 +- .../eww_config__config__test__test-2.snap | 23 ---- src/snapshots/eww_config__lexer__test-13.snap | 15 --- src/snapshots/eww_config__lexer__test-2.snap | 15 --- src/snapshots/eww_config__lexer__test-4.snap | 10 -- src/snapshots/eww_config__lexer__test-6.snap | 18 --- src/snapshots/eww_config__lexer__test.snap | 15 --- src/snapshots/eww_config__test-18.snap | 8 -- 10 files changed, 46 insertions(+), 176 deletions(-) delete mode 100644 src/snapshots/eww_config__config__test__test-2.snap delete mode 100644 src/snapshots/eww_config__lexer__test-13.snap delete mode 100644 src/snapshots/eww_config__lexer__test-2.snap delete mode 100644 src/snapshots/eww_config__lexer__test-4.snap delete mode 100644 src/snapshots/eww_config__lexer__test-6.snap delete mode 100644 src/snapshots/eww_config__lexer__test.snap delete mode 100644 src/snapshots/eww_config__test-18.snap diff --git a/src/config.rs b/src/config.rs index 2753ea3..3e64b7a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -57,7 +57,7 @@ mod test { fn test() { let parser = parser::AstParser::new(); insta::with_settings!({sort_maps => true}, { - let lexer = lexer::Lexer::new(0, "(box :bar 12 :baz \"hi\" foo (bar))"); + let lexer = lexer::Lexer::new(0, "(box :bar 12 :baz \"hi\" foo (bar))".to_string()); insta::assert_debug_snapshot!( Element::::from_ast(parser.parse(0, lexer).unwrap()).unwrap() ); diff --git a/src/lexer.rs b/src/lexer.rs index 4cf46af..ded1181 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -39,13 +39,10 @@ impl std::fmt::Display for Token { } } -pub struct LexIterator { - source: String, - pos: usize, -} - macro_rules! regex_rules { - ($($regex:literal => $token:expr),*) => { + ($( + $regex:literal => $token:expr),* + ) => { lazy_static::lazy_static! { static ref LEXER_REGEX_SET: RegexSet = RegexSet::new(&[ $(format!("^{}", $regex)),* @@ -61,26 +58,39 @@ macro_rules! regex_rules { } regex_rules! { - r"\(" => |_| Token::LPren, - r"\)" => |_| Token::RPren, - r"\[" => |_| Token::LBrack, - r"\]" => |_| Token::LBrack, - r"true" => |_| Token::True, - r"false" => |_| Token::False, - r#""(?:[^"\\]|\\.)*""# => |x| Token::StrLit(x), - r#"[+-]?(?:[0-9]+[.])?[0-9]+"# => |x| Token::NumLit(x), - r#"[a-zA-Z_!\?<>/.*-+][^\s{}\(\)]*"# => |x| Token::Symbol(x), - r#":\S+"# => |x| Token::Keyword(x), - r#";.*"# => |_| Token::Comment, - r"[ \t\n\f]+" => |_| Token::Skip + r"\(" => |_| Token::LPren, + r"\)" => |_| Token::RPren, + r"\[" => |_| Token::LBrack, + r"\]" => |_| Token::LBrack, + r"true" => |_| Token::True, + r"false" => |_| Token::False, + r#""(?:[^"\\]|\\.)*""# => |x| Token::StrLit(x), + r#"[+-]?(?:[0-9]+[.])?[0-9]+"# => |x| Token::NumLit(x), + r#"[a-zA-Z_!\?<>/.*-+][^\s{}\(\)]*"# => |x| Token::Symbol(x), + r#":\S+"# => |x| Token::Keyword(x), + r#";.*"# => |_| Token::Comment, + r"[ \t\n\f]+" => |_| Token::Skip } -impl Iterator for LexIterator { - type Item = (usize, Token, usize); +pub struct Lexer { + source: String, + file_id: usize, + failed: bool, + pos: usize, +} + +impl Lexer { + pub fn new(file_id: usize, source: String) -> Self { + Lexer { source, file_id, failed: false, pos: 0 } + } +} + +impl Iterator for Lexer { + type Item = Result<(usize, Token, usize), parse_error::ParseError>; fn next(&mut self) -> Option { loop { - if self.pos >= self.source.len() { + if self.failed || self.pos >= self.source.len() { return None; } let string = &self.source[self.pos..]; @@ -98,7 +108,7 @@ impl Iterator for LexIterator { if string.starts_with('}') && !in_string { let tok_str = &self.source[expr_start..self.pos]; self.pos += 1; - return Some((expr_start, Token::SimplExpr(tok_str.to_string()), self.pos - 1)); + return Some(Ok((expr_start, Token::SimplExpr(tok_str.to_string()), self.pos - 1))); } else if string.starts_with('"') { self.pos += 1; in_string = !in_string; @@ -110,66 +120,30 @@ impl Iterator for LexIterator { } } else { let match_set = LEXER_REGEX_SET.matches(string); - let (len, i) = match_set + let matched_token = match_set .into_iter() .map(|i: usize| { let m = LEXER_REGEXES[i].find(string).unwrap(); (m.end(), i) }) - .next() - .unwrap(); + .next(); + + let (len, i) = match matched_token { + Some(x) => x, + None => { + self.failed = true; + return Some(Err(parse_error::ParseError::LexicalError(Span(self.pos, self.pos, self.file_id)))); + } + }; let tok_str = &self.source[self.pos..self.pos + len]; let old_pos = self.pos; self.pos += len; match LEXER_FNS[i](tok_str.to_string()) { Token::Skip => {} - token => return Some((old_pos, token, self.pos)), + token => return Some(Ok((old_pos, token, self.pos))), } } } } } - -macro_rules! test_lexer { - ($($text:literal),*) => {{ - ::insta::with_settings!({sort_maps => true}, { - $( - ::insta::assert_debug_snapshot!( - LexIterator { pos: 0, source: $text.to_string() }.map(|x| x.1).collect::>() - ); - )* - }); - }} -} - -#[test] -fn test() { - test_lexer!(r#"(test "h\"i")"#, r#"(foo { "}" })"#); -} - -pub type SpannedResult = Result<(Loc, Tok, Loc), Error>; - -pub struct Lexer { - file_id: usize, - lexer: LexIterator, -} - -impl Lexer { - pub fn new(file_id: usize, text: &str) -> Self { - Lexer { file_id, lexer: LexIterator { source: text.to_string(), pos: 0 } } - } -} - -impl Iterator for Lexer { - type Item = SpannedResult; - - fn next(&mut self) -> Option { - let (l, token, r) = self.lexer.next()?; - if token == Token::Skip { - Some(Err(parse_error::ParseError::LexicalError(Span(l, r, self.file_id)))) - } else { - Some(Ok((l, token, r))) - } - } -} diff --git a/src/lib.rs b/src/lib.rs index 30aa4fc..ddae1d1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,7 +25,7 @@ lalrpop_mod!( ); pub fn parse_string(file_id: usize, s: &str) -> AstResult { - let lexer = lexer::Lexer::new(file_id, s); + let lexer = lexer::Lexer::new(file_id, s.to_string()); let parser = parser::AstParser::new(); parser.parse(file_id, lexer).map_err(|e| AstError::from_parse_error(file_id, e)) } @@ -37,7 +37,7 @@ macro_rules! test_parser { ::insta::with_settings!({sort_maps => true}, { $( - ::insta::assert_debug_snapshot!(p.parse(0, Lexer::new(0, $text))); + ::insta::assert_debug_snapshot!(p.parse(0, Lexer::new(0, $text.to_string()))); )* }); }} diff --git a/src/snapshots/eww_config__config__test__test-2.snap b/src/snapshots/eww_config__config__test__test-2.snap deleted file mode 100644 index 991e320..0000000 --- a/src/snapshots/eww_config__config__test__test-2.snap +++ /dev/null @@ -1,23 +0,0 @@ ---- -source: src/config.rs -expression: "Definitional::::from_expr(parser.parse(\"(defwidget box (child) (child2))\").unwrap()).unwrap()" - ---- -Definitional { - def_type: Widget, - name: "box", - attrs: {}, - children: [ - List<15..22>( - [ - Symbol<16..21>(child), - ], - ), - List<23..31>( - [ - Symbol<24..30>(child2), - ], - ), - ], - span: 1..10, -} diff --git a/src/snapshots/eww_config__lexer__test-13.snap b/src/snapshots/eww_config__lexer__test-13.snap deleted file mode 100644 index 70fceff..0000000 --- a/src/snapshots/eww_config__lexer__test-13.snap +++ /dev/null @@ -1,15 +0,0 @@ ---- -source: src/lexer.rs -expression: "LexIterator{pos: 0,\n source:\n r#\"(test \" hi \")\"#.to_string(),}.map(|x|\n x.1).collect::>()" - ---- -[ - LPren, - Symbol( - "test", - ), - StrLit( - "\" hi \"", - ), - RPren, -] diff --git a/src/snapshots/eww_config__lexer__test-2.snap b/src/snapshots/eww_config__lexer__test-2.snap deleted file mode 100644 index 11bb951..0000000 --- a/src/snapshots/eww_config__lexer__test-2.snap +++ /dev/null @@ -1,15 +0,0 @@ ---- -source: src/lexer.rs -expression: "LexIterator{pos: 0,\n source:\n r#\"(foo { \"}\" })\"#.to_string(),}.map(|x|\n x.1).collect::>()" - ---- -[ - LPren, - Symbol( - "foo", - ), - SimplExpr( - " \"}\" ", - ), - RPren, -] diff --git a/src/snapshots/eww_config__lexer__test-4.snap b/src/snapshots/eww_config__lexer__test-4.snap deleted file mode 100644 index c8700fa..0000000 --- a/src/snapshots/eww_config__lexer__test-4.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: src/lexer.rs -expression: "LexIterator{pos: 0,\n source: \"-1.2\".to_string(),}.map(|x| x.1).collect::>()" - ---- -[ - NumLit( - "-1.2", - ), -] diff --git a/src/snapshots/eww_config__lexer__test-6.snap b/src/snapshots/eww_config__lexer__test-6.snap deleted file mode 100644 index 88f2846..0000000 --- a/src/snapshots/eww_config__lexer__test-6.snap +++ /dev/null @@ -1,18 +0,0 @@ ---- -source: src/lexer.rs -expression: "LexIterator{pos: 0,\n source:\n \"(1 :foo 1)\".to_string(),}.map(|x| x.1).collect::>()" - ---- -[ - LPren, - NumLit( - "1", - ), - Keyword( - ":foo", - ), - NumLit( - "1", - ), - RPren, -] diff --git a/src/snapshots/eww_config__lexer__test.snap b/src/snapshots/eww_config__lexer__test.snap deleted file mode 100644 index 0f6e502..0000000 --- a/src/snapshots/eww_config__lexer__test.snap +++ /dev/null @@ -1,15 +0,0 @@ ---- -source: src/lexer.rs -expression: "LexIterator{pos: 0,\n source:\n r#\"(test \"h\\\"i\")\"#.to_string(),}.map(|x|\n x.1).collect::>()" - ---- -[ - LPren, - Symbol( - "test", - ), - StrLit( - "\"h\\\"i\"", - ), - RPren, -] diff --git a/src/snapshots/eww_config__test-18.snap b/src/snapshots/eww_config__test-18.snap deleted file mode 100644 index 17db7ac..0000000 --- a/src/snapshots/eww_config__test-18.snap +++ /dev/null @@ -1,8 +0,0 @@ ---- -source: src/lib.rs -expression: "p.parse(0, lexer::Lexer::new(\"\\\"h\\\\\\\"i\\\"\"))" - ---- -Ok( - Value<0..6>(h\"i), -) From de9d979ce5aecc54d5a0f043e720712885cf68ef Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Sun, 18 Jul 2021 17:34:58 +0200 Subject: [PATCH 040/137] refactor module structure, clean up diagnostic reporting --- Cargo.lock | 158 ++++++++++----------------- src/config/mod.rs | 0 src/error.rs | 2 +- src/format_diagnostic.rs | 115 +++++++++++-------- src/lib.rs | 61 +---------- src/{ => parser}/ast.rs | 23 ++-- src/{config.rs => parser/element.rs} | 17 +-- src/{ => parser}/lexer.rs | 2 +- src/parser/mod.rs | 62 +++++++++++ src/{ => parser}/parse_error.rs | 2 +- src/{ => parser}/parser.lalrpop | 7 +- 11 files changed, 222 insertions(+), 227 deletions(-) create mode 100644 src/config/mod.rs rename src/{ => parser}/ast.rs (90%) rename src/{config.rs => parser/element.rs} (84%) rename src/{ => parser}/lexer.rs (99%) create mode 100644 src/parser/mod.rs rename src/{ => parser}/parse_error.rs (89%) rename src/{ => parser}/parser.lalrpop (90%) diff --git a/Cargo.lock b/Cargo.lock index ee65321..a5c995c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,23 +20,11 @@ dependencies = [ "winapi", ] -[[package]] -name = "arrayref" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" - -[[package]] -name = "arrayvec" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" - [[package]] name = "ascii-canvas" -version = "2.0.0" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff8eb72df928aafb99fe5d37b383f2fe25bd2a765e3e5f7c365916b6f2463a29" +checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" dependencies = [ "term", ] @@ -58,12 +46,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" -[[package]] -name = "base64" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" - [[package]] name = "beef" version = "0.5.0" @@ -86,21 +68,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" [[package]] -name = "blake2b_simd" -version = "0.5.11" +name = "bitflags" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587" -dependencies = [ - "arrayref", - "arrayvec", - "constant_time_eq", -] - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "cfg-if" @@ -131,29 +102,12 @@ dependencies = [ "winapi", ] -[[package]] -name = "constant_time_eq" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" - [[package]] name = "convert_case" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" -[[package]] -name = "crossbeam-utils" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4feb231f0d4d6af81aed15928e58ecf5816aa62a2393e2c82f46973e92a9a278" -dependencies = [ - "autocfg", - "cfg-if", - "lazy_static", -] - [[package]] name = "crunchy" version = "0.2.2" @@ -190,10 +144,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499" [[package]] -name = "dirs" -version = "1.0.5" +name = "dirs-next" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901" +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", @@ -262,9 +226,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "getrandom" -version = "0.1.16" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" dependencies = [ "cfg-if", "libc", @@ -273,9 +237,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.9.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" [[package]] name = "heck" @@ -288,18 +252,18 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" dependencies = [ "libc", ] [[package]] name = "indexmap" -version = "1.6.2" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3" +checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" dependencies = [ "autocfg", "hashbrown", @@ -322,9 +286,9 @@ dependencies = [ [[package]] name = "itertools" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d572918e350e82412fe766d24b15e6682fb2ed2bbe018280caa810397cb319" +checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf" dependencies = [ "either", ] @@ -337,9 +301,9 @@ checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" [[package]] name = "lalrpop" -version = "0.19.5" +version = "0.19.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46962a8c71b91c3524b117dfdd70844d4265a173c4c9109f98171aebdcf1195f" +checksum = "b15174f1c529af5bf1283c3bc0058266b483a67156f79589fab2a25e23cf8988" dependencies = [ "ascii-canvas", "atty", @@ -360,9 +324,9 @@ dependencies = [ [[package]] name = "lalrpop-util" -version = "0.19.5" +version = "0.19.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a708007b751af124d09e9c5d97515257902bc6b486a56b40bcafd939e8ff467" +checksum = "d3e58cce361efcc90ba8a0a5f982c741ff86b603495bb15a998412e957dcd278" dependencies = [ "regex", ] @@ -375,9 +339,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.94" +version = "0.2.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e" +checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790" [[package]] name = "linked-hash-map" @@ -475,9 +439,9 @@ dependencies = [ [[package]] name = "pico-args" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d7afeb98c5a10e0bffcc7fc16e105b04d06729fac5fd6384aebf7ff5cb5a67d" +checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468" [[package]] name = "precomputed-hash" @@ -517,19 +481,21 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.1.57" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" +checksum = "5ab49abadf3f9e1c4bc499e8845e152ad87d2ad2d30371841171169e9d75feee" +dependencies = [ + "bitflags", +] [[package]] name = "redox_users" -version = "0.3.5" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d" +checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" dependencies = [ "getrandom", "redox_syscall", - "rust-argon2", ] [[package]] @@ -549,18 +515,6 @@ version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" -[[package]] -name = "rust-argon2" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb" -dependencies = [ - "base64", - "blake2b_simd", - "constant_time_eq", - "crossbeam-utils", -] - [[package]] name = "rustc_version" version = "0.3.3" @@ -570,6 +524,12 @@ dependencies = [ "semver", ] +[[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" @@ -722,12 +682,12 @@ dependencies = [ [[package]] name = "term" -version = "0.5.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42" +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" dependencies = [ - "byteorder", - "dirs", + "dirs-next", + "rustversion", "winapi", ] @@ -752,18 +712,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa6f76457f59514c7eeb4e59d891395fab0b2fd1d40723ae737d64153392e9c6" +checksum = "93119e4feac1cbe6c798c34d3a53ea0026b0b1de6a120deef895137c0529bfe2" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a36768c0fbf1bb15eca10defa29526bda730a2376c2ab4393ccfa16fb1a318d" +checksum = "060d69a0afe7796bf42e9e2ff91f5ee691fb15c53d38b4b62a9a53eb23164745" dependencies = [ "proc-macro2", "quote", @@ -817,9 +777,9 @@ checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" [[package]] name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" +version = "0.10.2+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" [[package]] name = "winapi" diff --git a/src/config/mod.rs b/src/config/mod.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/error.rs b/src/error.rs index 095fdef..333dc58 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,4 +1,4 @@ -use crate::{ +use crate::parser::{ ast::{Ast, AstType, Span}, lexer, parse_error, }; diff --git a/src/format_diagnostic.rs b/src/format_diagnostic.rs index 5fd8a71..d72c16a 100644 --- a/src/format_diagnostic.rs +++ b/src/format_diagnostic.rs @@ -1,42 +1,60 @@ use codespan_reporting::{diagnostic, files}; use simplexpr::dynval; -use crate::{ast::Span, error::AstError, parse_error}; use diagnostic::*; -fn span_to_label(span: Span) -> Label { - Label::primary(span.2, span.0..span.1) +use crate::error::AstError; + +use super::parser::{ast::Span, parse_error}; + +macro_rules! gen_diagnostic { + ( + $(msg = $msg:expr)? + $(, label = $span:expr $(=> $label:expr)?)? + $(, note = $note:expr)? $(,)? + ) => { + Diagnostic::error() + $(.with_message($msg))? + $(.with_labels(vec![ + Label::primary($span.2, $span.0..$span.1) + $(.with_message($label))? + ]))? + $(.with_notes(vec![$note]))? + }; + ($msg:expr $(, $span:expr $(,)?)?) => {{ + Diagnostic::error() + .with_message($msg) + $(.with_labels(vec![Label::primary($span.2, $span.0..$span.1)]))? + }}; } pub trait ToDiagnostic { - fn to_diagnostic(&self, files: &files::SimpleFiles<&str, &str>) -> Diagnostic; + fn to_diagnostic(&self) -> Diagnostic; } impl ToDiagnostic for AstError { - fn to_diagnostic(&self, files: &files::SimpleFiles<&str, &str>) -> Diagnostic { + fn to_diagnostic(&self) -> Diagnostic { let diag = Diagnostic::error(); if let Some(span) = self.get_span() { use lalrpop_util::ParseError::*; match self { AstError::InvalidDefinition(_) => todo!(), - AstError::MissingNode(_, expected) => diag - .with_message(format!("Missing {}", expected)) - .with_labels(vec![span_to_label(span).with_message(format!("Expected `{}` here", expected))]), + AstError::MissingNode(_, expected) => gen_diagnostic! { + msg = format!("Missing {}", expected), + label = span => format!("Expected `{}` here", expected), + }, - AstError::WrongExprType(_, expected, actual) => diag - .with_message("Wrong type of expression") - .with_notes(vec![format!("Expected: {}\nGot: {}", expected, actual)]) - .with_labels(vec![span_to_label(span).with_message(format!("Expected a `{}` here", expected))]), + AstError::WrongExprType(_, expected, actual) => gen_diagnostic! { + msg = "Wrong type of expression", + label = span => format!("Expected a `{}` here", expected), + note = format!("Expected: {}\nGot: {}", expected, actual), + }, - AstError::ParseError { file_id, source } => { - lalrpop_error_to_diagnostic(source, diag, span, move |diag, error| match error { - parse_error::ParseError::SimplExpr(_, error) => simplexpr_error_to_diagnostic(error, diag, span), - parse_error::ParseError::LexicalError(_) => diag - .with_message("Invalid token") - .with_labels(vec![span_to_label(span).with_message("Invalid token")]), - }) - } + AstError::ParseError { file_id, source } => lalrpop_error_to_diagnostic(source, span, |error| match error { + parse_error::ParseError::SimplExpr(_, error) => simplexpr_error_to_diagnostic(error, span), + parse_error::ParseError::LexicalError(_) => lexical_error_to_diagnostic(span), + }), } } else { diag.with_message(format!("{}", self)) @@ -46,36 +64,47 @@ impl ToDiagnostic for AstError { fn lalrpop_error_to_diagnostic( error: &lalrpop_util::ParseError, - diag: Diagnostic, span: Span, - handle_user_error: impl FnOnce(Diagnostic, &E) -> Diagnostic, + handle_user_error: impl FnOnce(&E) -> Diagnostic, ) -> Diagnostic { use lalrpop_util::ParseError::*; match error { - InvalidToken { location } => diag.with_message("Invalid token").with_labels(vec![span_to_label(span)]), - UnrecognizedEOF { location, expected } => diag - .with_message("Input ended unexpectedly. Check if you have any unclosed delimiters") - .with_labels(vec![span_to_label(span)]), - UnrecognizedToken { token, expected } => diag - .with_message(format!("Unexpected token `{}` encoutered", token.1)) - .with_labels(vec![span_to_label(span).with_message("Token unexpected")]), - - ExtraToken { token } => diag.with_message(format!("Extra token encountered: `{}`", token.1)), - User { error } => handle_user_error(diag, error), + InvalidToken { location } => gen_diagnostic! { msg = "Invalid token", label = span }, + UnrecognizedEOF { location, expected } => gen_diagnostic! { + msg = "Input ended unexpectedly. Check if you have any unclosed delimiters", + label = span + }, + UnrecognizedToken { token, expected } => gen_diagnostic! { + msg = format!("Unexpected token `{}` encountered", token.1), + label = span => "Token unexpected", + }, + ExtraToken { token } => gen_diagnostic!(format!("Extra token encountered: `{}`", token.1)), + User { error } => handle_user_error(error), } } -fn simplexpr_error_to_diagnostic(error: &simplexpr::error::Error, diag: Diagnostic, span: Span) -> Diagnostic { +fn simplexpr_error_to_diagnostic(error: &simplexpr::error::Error, span: Span) -> Diagnostic { + use simplexpr::error::Error::*; match error { - simplexpr::error::Error::ParseError { source } => lalrpop_error_to_diagnostic(source, diag, span, move |diag, error| { - diag.with_message("Invalid token").with_labels(vec![span_to_label(span).with_message("Invalid token")]) - }), - simplexpr::error::Error::ConversionError(dynval::ConversionError { value, target_type, source }) => diag - .with_message(format!("{}", error)) - .with_labels(vec![span_to_label(span).with_message(format!("{} is not of type `{}`", value, target_type))]) - .with_notes(source.as_ref().map(|x| vec![format!("{}", x)]).unwrap_or_default()), - simplexpr::error::Error::Spanned(..) => todo!(), - simplexpr::error::Error::Eval(error) => diag.with_message(format!("{}", error)).with_labels(vec![span_to_label(span)]), - simplexpr::error::Error::Other(error) => diag.with_message(format!("{}", error)).with_labels(vec![span_to_label(span)]), + ParseError { source } => lalrpop_error_to_diagnostic(source, span, move |error| lexical_error_to_diagnostic(span)), + ConversionError(error) => conversion_error_to_diagnostic(error, span), + Eval(error) => gen_diagnostic!(format!("{}", error), span), + Other(error) => gen_diagnostic!(format!("{}", error), span), + Spanned(_, error) => gen_diagnostic!(format!("{}", error), span), + } +} + +fn conversion_error_to_diagnostic(error: &dynval::ConversionError, span: Span) -> Diagnostic { + let diag = gen_diagnostic! { + msg = format!("{}", error), + label = span => format!("{} is not of type `{}`", error.value, error.target_type), + }; + diag.with_notes(error.source.as_ref().map(|x| vec![format!("{}", x)]).unwrap_or_default()) +} + +fn lexical_error_to_diagnostic(span: Span) -> Diagnostic { + gen_diagnostic! { + msg = "Invalid token", + label = span => "Invalid token" } } diff --git a/src/lib.rs b/src/lib.rs index ddae1d1..687c8f1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,67 +2,8 @@ #![allow(unused)] #![feature(try_blocks)] -pub mod ast; pub mod config; pub mod error; pub mod format_diagnostic; -mod lexer; -mod parse_error; +pub mod parser; pub mod value; - -use ast::Ast; -use error::{AstError, AstResult}; - -use std::{fmt::Display, ops::Deref}; - -use itertools::Itertools; - -use lalrpop_util::lalrpop_mod; - -lalrpop_mod!( - #[allow(clippy::all)] - pub parser -); - -pub fn parse_string(file_id: usize, s: &str) -> AstResult { - let lexer = lexer::Lexer::new(file_id, s.to_string()); - let parser = parser::AstParser::new(); - parser.parse(file_id, lexer).map_err(|e| AstError::from_parse_error(file_id, e)) -} - -macro_rules! test_parser { - ($($text:literal),*) => {{ - let p = crate::parser::AstParser::new(); - use crate::lexer::Lexer; - - ::insta::with_settings!({sort_maps => true}, { - $( - ::insta::assert_debug_snapshot!(p.parse(0, Lexer::new(0, $text.to_string()))); - )* - }); - }} -} - -#[test] -fn test() { - test_parser!( - "1", - "(12)", - "1.2", - "-1.2", - "(1 2)", - "(1 :foo 1)", - "(:foo 1)", - "(:foo->: 1)", - "(foo 1)", - "(lol😄 1)", - r#"(test "hi")"#, - r#"(test "h\"i")"#, - r#"(test " hi ")"#, - "(+ (1 2 (* 2 5)))", - r#"; test"#, - r#"(f arg ; test - arg2)"#, - "\"h\\\"i\"" - ); -} diff --git a/src/ast.rs b/src/parser/ast.rs similarity index 90% rename from src/ast.rs rename to src/parser/ast.rs index a3cc70b..66ed23e 100644 --- a/src/ast.rs +++ b/src/parser/ast.rs @@ -2,9 +2,11 @@ use itertools::Itertools; use simplexpr::ast::SimplExpr; use std::collections::HashMap; -use crate::{config::FromAst, error::*}; use std::fmt::Display; +use super::element::FromAst; +use crate::error::{AstError, AstResult}; + #[derive(Eq, PartialEq, Clone, Copy)] pub struct Span(pub usize, pub usize, pub usize); @@ -124,15 +126,16 @@ impl std::fmt::Display for Ast { impl std::fmt::Debug for Ast { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { use Ast::*; - match self { - List(span, x) => f.debug_tuple(&format!("List<{}>", span)).field(x).finish(), - Array(span, x) => f.debug_tuple(&format!("Array<{}>", span)).field(x).finish(), - Keyword(span, x) => write!(f, "Number<{}>({})", span, x), - Symbol(span, x) => write!(f, "Symbol<{}>({})", span, x), - Value(span, x) => write!(f, "Value<{}>({})", span, x), - SimplExpr(span, x) => write!(f, "SimplExpr<{}>({})", span, x), - Comment(span) => write!(f, "Comment<{}>", span), - } + write!(f, "{}", self) + // match self { + // List(span, x) => f.debug_tuple(&format!("List<{}>", span)).field(x).finish(), + // Array(span, x) => f.debug_tuple(&format!("Array<{}>", span)).field(x).finish(), + // Keyword(span, x) => write!(f, "Number<{}>({})", span, x), + // Symbol(span, x) => write!(f, "Symbol<{}>({})", span, x), + // Value(span, x) => write!(f, "Value<{}>({})", span, x), + // SimplExpr(span, x) => write!(f, "SimplExpr<{}>({})", span, x), + // Comment(span) => write!(f, "Comment<{}>", span), + //} } } diff --git a/src/config.rs b/src/parser/element.rs similarity index 84% rename from src/config.rs rename to src/parser/element.rs index 3e64b7a..728ac0b 100644 --- a/src/config.rs +++ b/src/parser/element.rs @@ -1,8 +1,5 @@ -use crate::{ - ast::{Ast, AstIterator, AstType, Span}, - error::*, - parser, spanned, -}; +use super::ast::{Ast, AstIterator, AstType, Span}; +use crate::{error::*, parser, spanned}; use itertools::Itertools; use std::{ collections::{HashMap, LinkedList}, @@ -49,13 +46,17 @@ impl FromAst for Element { #[cfg(test)] mod test { - use super::*; - use crate::lexer; + use super::super::{ + ast::Ast, + element::{Element, FromAst}, + lexer, + }; + use insta; #[test] fn test() { - let parser = parser::AstParser::new(); + let parser = super::parser::parser::AstParser::new(); insta::with_settings!({sort_maps => true}, { let lexer = lexer::Lexer::new(0, "(box :bar 12 :baz \"hi\" foo (bar))".to_string()); insta::assert_debug_snapshot!( diff --git a/src/lexer.rs b/src/parser/lexer.rs similarity index 99% rename from src/lexer.rs rename to src/parser/lexer.rs index ded1181..de184e1 100644 --- a/src/lexer.rs +++ b/src/parser/lexer.rs @@ -1,6 +1,6 @@ use regex::{Regex, RegexSet}; -use crate::{ast::Span, parse_error}; +use super::{ast::Span, parse_error}; #[derive(Debug, PartialEq, Eq, Clone)] pub enum Token { diff --git a/src/parser/mod.rs b/src/parser/mod.rs new file mode 100644 index 0000000..d00ad8a --- /dev/null +++ b/src/parser/mod.rs @@ -0,0 +1,62 @@ +use lalrpop_util::lalrpop_mod; + +use super::error::{AstError, AstResult}; +use ast::Ast; + +use std::{fmt::Display, ops::Deref}; + +use itertools::Itertools; + +pub mod ast; +pub mod element; +pub(crate) mod lexer; +pub(crate) mod parse_error; + +lalrpop_mod!( + #[allow(clippy::all)] + pub parser, + "/parser/parser.rs" +); + +pub fn parse_string(file_id: usize, s: &str) -> AstResult { + let lexer = lexer::Lexer::new(file_id, s.to_string()); + let parser = parser::AstParser::new(); + parser.parse(file_id, lexer).map_err(|e| AstError::from_parse_error(file_id, e)) +} + +macro_rules! test_parser { + ($($text:literal),*) => {{ + let p = parser::AstParser::new(); + use lexer::Lexer; + + ::insta::with_settings!({sort_maps => true}, { + $( + ::insta::assert_debug_snapshot!(p.parse(0, Lexer::new(0, $text.to_string()))); + )* + }); + }} +} + +#[test] +fn test() { + test_parser!( + "1", + "(12)", + "1.2", + "-1.2", + "(1 2)", + "(1 :foo 1)", + "(:foo 1)", + "(:foo->: 1)", + "(foo 1)", + "(lol😄 1)", + r#"(test "hi")"#, + r#"(test "h\"i")"#, + r#"(test " hi ")"#, + "(+ (1 2 (* 2 5)))", + r#"; test"#, + r#"(f arg ; test + arg2)"#, + "\"h\\\"i\"" + ); +} diff --git a/src/parse_error.rs b/src/parser/parse_error.rs similarity index 89% rename from src/parse_error.rs rename to src/parser/parse_error.rs index 972fb6c..9c2fe44 100644 --- a/src/parse_error.rs +++ b/src/parser/parse_error.rs @@ -1,4 +1,4 @@ -use crate::ast::Span; +use super::ast::Span; #[derive(Debug, thiserror::Error)] pub enum ParseError { diff --git a/src/parser.lalrpop b/src/parser/parser.lalrpop similarity index 90% rename from src/parser.lalrpop rename to src/parser/parser.lalrpop index 3ae49a8..817a57b 100644 --- a/src/parser.lalrpop +++ b/src/parser/parser.lalrpop @@ -1,6 +1,5 @@ use std::str::FromStr; -use crate::lexer::{Token}; -use crate::ast::{Ast, Span}; +use crate::parser::{lexer::{Token}, ast::{Ast, Span}, parse_error}; use simplexpr::ast::SimplExpr; use simplexpr; use lalrpop_util::ParseError; @@ -9,7 +8,7 @@ grammar(file_id: usize); extern { type Location = usize; - type Error = crate::parse_error::ParseError; + type Error = parse_error::ParseError; enum Token { "(" => Token::LPren, @@ -58,7 +57,7 @@ SimplExpr: SimplExpr = { let expr = x[1..x.len() - 1].to_string(); simplexpr::parse_string(&expr).map_err(|e| { let span = e.get_span().map(|simplexpr::Span(simpl_l, simpl_r)| Span(1 + l + simpl_l, 1 + l + simpl_r, file_id)); - ParseError::User { error: crate::parse_error::ParseError::SimplExpr(span, e) }}) + ParseError::User { error: parse_error::ParseError::SimplExpr(span, e) }}) } } From bfb7c5a27b8155ead6a74d3413998973c2a8652f Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Sun, 18 Jul 2021 19:48:16 +0200 Subject: [PATCH 041/137] add basic config structure parsing and add some validation, mostly to demonstrate --- examples/errors.rs | 17 ++++--- examples/validation.rs | 40 +++++++++++++++++ src/config/mod.rs | 3 ++ src/config/validate.rs | 42 ++++++++++++++++++ src/config/widget_definition.rs | 44 +++++++++++++++++++ src/config/widget_use.rs | 43 ++++++++++++++++++ src/error.rs | 34 +++++++++----- src/format_diagnostic.rs | 40 ++++++++++++----- src/parser/ast.rs | 24 +++++++--- src/parser/element.rs | 20 ++++++--- src/parser/lexer.rs | 8 ++-- src/parser/parser.lalrpop | 6 +-- ...nfig__parser__element__test__test.snap.new | 17 +++++++ .../eww_config__parser__test.snap.new | 8 ++++ 14 files changed, 300 insertions(+), 46 deletions(-) create mode 100644 examples/validation.rs create mode 100644 src/config/validate.rs create mode 100644 src/config/widget_definition.rs create mode 100644 src/config/widget_use.rs create mode 100644 src/parser/snapshots/eww_config__parser__element__test__test.snap.new create mode 100644 src/parser/snapshots/eww_config__parser__test.snap.new diff --git a/examples/errors.rs b/examples/errors.rs index 63b2655..9b329da 100644 --- a/examples/errors.rs +++ b/examples/errors.rs @@ -1,23 +1,28 @@ -use eww_config::{ast::*, config::*, format_diagnostic::ToDiagnostic}; +use eww_config::{ + config::*, + format_diagnostic::ToDiagnostic, + parser::{ast::*, element::FromAst}, +}; fn main() { let mut files = codespan_reporting::files::SimpleFiles::new(); let input = r#" - (heyho :foo { "foo \" } bar " } - :baz {(foo == bar ? 12.2 : 12)} + (heyho ; :foo { "foo \" } bar " } + ; :baz {(foo == bar ? 12.2 : 12)} (foo) + (defwidget foo [something bla] "foo") (baz))"#; let file_id = files.add("foo.eww", input); - let ast = eww_config::parse_string(file_id, input); - match ast.and_then(Element::::from_ast) { + let ast = eww_config::parser::parse_string(file_id, input); + match ast.and_then(eww_config::parser::element::Element::::from_ast) { Ok(ast) => { println!("{:?}", ast); } Err(err) => { dbg!(&err); - let diag = err.to_diagnostic(&files); + let diag = err.to_diagnostic(); use codespan_reporting::term; let config = term::Config::default(); let mut writer = term::termcolor::StandardStream::stderr(term::termcolor::ColorChoice::Always); diff --git a/examples/validation.rs b/examples/validation.rs new file mode 100644 index 0000000..f54311e --- /dev/null +++ b/examples/validation.rs @@ -0,0 +1,40 @@ +use eww_config::{ + config::{widget_definition::WidgetDefinition, widget_use::WidgetUse, *}, + error::AstError, + format_diagnostic::ToDiagnostic, + parser::{ast::*, element::FromAst}, +}; + +fn main() { + let mut files = codespan_reporting::files::SimpleFiles::new(); + + let input_use = r#" + (foo :something 12 + :bla "bruh" + "some text") + "#; + let input_def = r#" + (defwidget foo [something bla] "foo") + "#; + + let file_id_use = files.add("use.eww", input_use); + let file_id_def = files.add("def.eww", input_def); + let parsed_use = WidgetUse::from_ast(eww_config::parser::parse_string(file_id_use, input_use).unwrap()).unwrap(); + let parsed_def = WidgetDefinition::from_ast(eww_config::parser::parse_string(file_id_def, input_def).unwrap()).unwrap(); + let defs = maplit::hashmap! { + "foo".to_string() => parsed_def, + }; + match validate::validate(&defs, &parsed_use) { + Ok(ast) => { + println!("{:?}", ast); + } + Err(err) => { + let err = AstError::ValidationError(err); + let diag = err.to_diagnostic(); + use codespan_reporting::term; + let config = term::Config::default(); + let mut writer = term::termcolor::StandardStream::stderr(term::termcolor::ColorChoice::Always); + term::emit(&mut writer, &config, &files, &diag).unwrap(); + } + } +} diff --git a/src/config/mod.rs b/src/config/mod.rs index e69de29..1cd54e6 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -0,0 +1,3 @@ +pub mod validate; +pub mod widget_definition; +pub mod widget_use; diff --git a/src/config/validate.rs b/src/config/validate.rs new file mode 100644 index 0000000..d35c0cc --- /dev/null +++ b/src/config/validate.rs @@ -0,0 +1,42 @@ +use std::collections::HashMap; + +use simplexpr::SimplExpr; + +use crate::{ + error::AstResult, + parser::{ + ast::{Ast, AstIterator, Span}, + element::{Element, FromAst}, + }, + spanned, + value::{AttrName, VarName}, +}; + +use super::{widget_definition::WidgetDefinition, widget_use::WidgetUse}; + +#[derive(Debug, thiserror::Error)] +pub enum ValidationError { + #[error("Unknown widget referenced: {1}")] + UnknownWidget(Span, String), + + #[error("Missing attribute `{arg_name}` in use of widget `{widget_name}`")] + MissingAttr { widget_name: String, arg_name: AttrName, arg_list_span: Span, use_span: Span }, +} + +pub fn validate(defs: &HashMap, content: &WidgetUse) -> Result<(), ValidationError> { + if let Some(def) = defs.get(&content.name) { + for expected in def.expected_args.iter() { + if !content.attrs.contains_key(expected) { + return Err(ValidationError::MissingAttr { + widget_name: def.name.to_string(), + arg_name: expected.clone(), + arg_list_span: def.args_span, + use_span: content.span, + }); + } + } + } else { + return Err(ValidationError::UnknownWidget(content.span, content.name.to_string())); + } + Ok(()) +} diff --git a/src/config/widget_definition.rs b/src/config/widget_definition.rs new file mode 100644 index 0000000..a2ffd9a --- /dev/null +++ b/src/config/widget_definition.rs @@ -0,0 +1,44 @@ +use std::collections::HashMap; + +use simplexpr::SimplExpr; + +use crate::{ + error::AstResult, + parser::{ + ast::{Ast, AstIterator, Span}, + element::{Element, FromAst}, + }, + spanned, + value::{AttrName, VarName}, +}; + +use super::widget_use::WidgetUse; +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct WidgetDefinition { + pub name: String, + pub expected_args: Vec, + pub widget: WidgetUse, + pub span: Span, + pub args_span: Span, +} + +impl FromAst for WidgetDefinition { + fn from_ast(e: Ast) -> AstResult { + let span = e.span(); + spanned!(e.span(), { + let list = e.as_list()?; + let mut iter = AstIterator::new(list.into_iter()); + + let (_, def_type) = iter.expect_symbol()?; + assert!(def_type == "defwidget"); + + let (_, name) = iter.expect_symbol()?; + let (args_span, expected_args) = iter.expect_array()?; + let expected_args = expected_args.into_iter().map(|x| x.as_symbol().map(AttrName)).collect::>()?; + let widget = iter.expect_any().and_then(WidgetUse::from_ast)?; + // TODO verify that this was the last element in the list + // iter.expect_done()?; + Self { name, expected_args, widget, span, args_span } + }) + } +} diff --git a/src/config/widget_use.rs b/src/config/widget_use.rs new file mode 100644 index 0000000..79949e3 --- /dev/null +++ b/src/config/widget_use.rs @@ -0,0 +1,43 @@ +use std::collections::HashMap; + +use simplexpr::SimplExpr; + +use crate::{ + error::AstResult, + parser::{ + ast::{Ast, AstIterator, Span}, + element::{Element, FromAst}, + }, + spanned, + value::AttrName, +}; +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct WidgetUse { + pub name: String, + pub attrs: HashMap, + pub children: Vec, + pub span: Span, +} + +impl FromAst for WidgetUse { + fn from_ast(e: Ast) -> AstResult { + let span = e.span(); + spanned!(e.span(), { + if let Ok(text) = e.as_value_ref() { + Self { + name: "text".to_string(), + attrs: maplit::hashmap! { AttrName("text".to_string()) => SimplExpr::Literal(span.into(), text.clone()) }, + children: Vec::new(), + span, + } + } else { + let list = e.as_list()?; + let mut iter = AstIterator::new(list.into_iter()); + let (_, name) = iter.expect_symbol()?; + let attrs = iter.expect_key_values()?.into_iter().map(|(k, v)| (AttrName(k), v)).collect(); + let children = iter.map(WidgetUse::from_ast).collect::>>()?; + Self { name, attrs, children, span } + } + }) + } +} diff --git a/src/error.rs b/src/error.rs index 333dc58..a603734 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,6 +1,9 @@ -use crate::parser::{ - ast::{Ast, AstType, Span}, - lexer, parse_error, +use crate::{ + config::validate::ValidationError, + parser::{ + ast::{Ast, AstType, Span}, + lexer, parse_error, + }, }; use codespan_reporting::{diagnostic, files}; use thiserror::Error; @@ -11,10 +14,15 @@ pub type AstResult = Result; pub enum AstError { #[error("Definition invalid")] InvalidDefinition(Option), - #[error("Expected a {1}, but got nothing")] - MissingNode(Option, AstType), + #[error("Expected another element, but got nothing")] + MissingNode(Option), #[error("Wrong type of expression: Expected {1} but got {2}")] WrongExprType(Option, AstType, AstType), + #[error("Expected to get a value, but got {1}")] + NotAValue(Option, AstType), + + #[error(transparent)] + ValidationError(#[from] ValidationError), #[error("Parse error: {source}")] ParseError { file_id: Option, source: lalrpop_util::ParseError }, @@ -24,8 +32,10 @@ impl AstError { pub fn get_span(&self) -> Option { match self { AstError::InvalidDefinition(span) => *span, - AstError::MissingNode(span, _) => *span, + AstError::MissingNode(span) => *span, AstError::WrongExprType(span, ..) => *span, + AstError::NotAValue(span, ..) => *span, + AstError::ValidationError(error) => None, // TODO none here is stupid AstError::ParseError { file_id, source } => file_id.and_then(|id| get_parse_error_span(id, source)), } } @@ -58,18 +68,18 @@ pub fn spanned(span: Span, err: impl Into) -> AstError { use AstError::*; match err.into() { AstError::InvalidDefinition(None) => AstError::InvalidDefinition(Some(span)), - AstError::MissingNode(None, x) => AstError::MissingNode(Some(span), x), + AstError::MissingNode(None) => AstError::MissingNode(Some(span)), AstError::WrongExprType(None, x, y) => AstError::WrongExprType(Some(span), x, y), x => x, } } pub trait OptionAstErrorExt { - fn or_missing(self, t: AstType) -> Result; + fn or_missing(self) -> Result; } impl OptionAstErrorExt for Option { - fn or_missing(self, t: AstType) -> Result { - self.ok_or(AstError::MissingNode(None, t)) + fn or_missing(self) -> Result { + self.ok_or(AstError::MissingNode(None)) } } @@ -87,7 +97,7 @@ impl> AstResultExt for Result { macro_rules! spanned { ($span:expr, $block:expr) => {{ let span = $span; - let result: Result<_, AstError> = try { $block }; - result.at(span) + let result: Result<_, crate::error::AstError> = try { $block }; + crate::error::AstResultExt::at(result, span) }}; } diff --git a/src/format_diagnostic.rs b/src/format_diagnostic.rs index d72c16a..263bdf2 100644 --- a/src/format_diagnostic.rs +++ b/src/format_diagnostic.rs @@ -16,8 +16,8 @@ macro_rules! gen_diagnostic { Diagnostic::error() $(.with_message($msg))? $(.with_labels(vec![ - Label::primary($span.2, $span.0..$span.1) - $(.with_message($label))? + Label::primary($span.2, $span.0..$span.1) + $(.with_message($label))? ]))? $(.with_notes(vec![$note]))? }; @@ -34,15 +34,29 @@ pub trait ToDiagnostic { impl ToDiagnostic for AstError { fn to_diagnostic(&self) -> Diagnostic { - let diag = Diagnostic::error(); - if let Some(span) = self.get_span() { - use lalrpop_util::ParseError::*; + if let AstError::ValidationError(error) = self { + match error { + crate::config::validate::ValidationError::UnknownWidget(span, name) => gen_diagnostic! { + msg = format!("No widget named {} exists", name), + label = span => "Used here", + }, + crate::config::validate::ValidationError::MissingAttr { widget_name, arg_name, arg_list_span, use_span } => { + let diag = gen_diagnostic! { + msg = format!("{}", error), + }; + diag.with_labels(vec![ + Label::secondary(use_span.2, use_span.0..use_span.1).with_message("Argument missing here"), + Label::secondary(arg_list_span.2, arg_list_span.0..arg_list_span.1).with_message("but is required here"), + ]) + } + } + } else if let Some(span) = self.get_span() { match self { AstError::InvalidDefinition(_) => todo!(), - AstError::MissingNode(_, expected) => gen_diagnostic! { - msg = format!("Missing {}", expected), - label = span => format!("Expected `{}` here", expected), + AstError::MissingNode(_) => gen_diagnostic! { + msg = "Expected another element", + label = span => "Expected another element here", }, AstError::WrongExprType(_, expected, actual) => gen_diagnostic! { @@ -50,14 +64,20 @@ impl ToDiagnostic for AstError { label = span => format!("Expected a `{}` here", expected), note = format!("Expected: {}\nGot: {}", expected, actual), }, + AstError::NotAValue(_, actual) => gen_diagnostic! { + msg = format!("Expected value, but got {}", actual), + label = span => "Expected some value here", + note = format!("Got: {}", actual), + }, AstError::ParseError { file_id, source } => lalrpop_error_to_diagnostic(source, span, |error| match error { parse_error::ParseError::SimplExpr(_, error) => simplexpr_error_to_diagnostic(error, span), parse_error::ParseError::LexicalError(_) => lexical_error_to_diagnostic(span), }), + _ => panic!(), } } else { - diag.with_message(format!("{}", self)) + Diagnostic::error().with_message(format!("{}", self)) } } } @@ -86,7 +106,7 @@ fn lalrpop_error_to_diagnostic( fn simplexpr_error_to_diagnostic(error: &simplexpr::error::Error, span: Span) -> Diagnostic { use simplexpr::error::Error::*; match error { - ParseError { source } => lalrpop_error_to_diagnostic(source, span, move |error| lexical_error_to_diagnostic(span)), + ParseError { source, .. } => lalrpop_error_to_diagnostic(source, span, move |error| lexical_error_to_diagnostic(span)), ConversionError(error) => conversion_error_to_diagnostic(error, span), Eval(error) => gen_diagnostic!(format!("{}", error), span), Other(error) => gen_diagnostic!(format!("{}", error), span), diff --git a/src/parser/ast.rs b/src/parser/ast.rs index 66ed23e..c68d279 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -1,15 +1,21 @@ use itertools::Itertools; -use simplexpr::ast::SimplExpr; +use simplexpr::{ast::SimplExpr, dynval::DynVal}; use std::collections::HashMap; use std::fmt::Display; use super::element::FromAst; -use crate::error::{AstError, AstResult}; +use crate::error::{AstError, AstResult, OptionAstErrorExt}; #[derive(Eq, PartialEq, Clone, Copy)] pub struct Span(pub usize, pub usize, pub usize); +impl Into for Span { + fn into(self) -> simplexpr::Span { + simplexpr::Span(self.0, self.1, self.2) + } +} + impl std::fmt::Display for Span { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}..{}", self.0, self.1) @@ -45,7 +51,7 @@ pub enum Ast { Array(Span, Vec), Keyword(Span, String), Symbol(Span, String), - Value(Span, String), + Value(Span, DynVal), SimplExpr(Span, SimplExpr), Comment(Span), } @@ -69,7 +75,7 @@ macro_rules! as_func { } impl Ast { - as_func!(AstType::Value, as_value as_value_ref = Ast::Value(_, x) => x); + as_func!(AstType::Value, as_value as_value_ref = Ast::Value(_, x) => x); as_func!(AstType::Symbol, as_symbol as_symbol_ref = Ast::Symbol(_, x) => x); @@ -155,7 +161,7 @@ macro_rules! return_or_put_back { self.iter.put_back(other); Err(AstError::WrongExprType(Some(span), expr_type, actual_type)) } - None => Err(AstError::MissingNode(None, expr_type)), + None => Err(AstError::MissingNode(None)), } } }; @@ -164,14 +170,20 @@ macro_rules! return_or_put_back { impl> AstIterator { return_or_put_back!(expect_symbol, AstType::Symbol, (Span, String) = Ast::Symbol(span, x) => (span, x)); - return_or_put_back!(expect_string, AstType::Value, (Span, String) = Ast::Value(span, x) => (span, x)); + return_or_put_back!(expect_value, AstType::Value, (Span, DynVal) = Ast::Value(span, x) => (span, x)); return_or_put_back!(expect_list, AstType::List, (Span, Vec) = Ast::List(span, x) => (span, x)); + return_or_put_back!(expect_array, AstType::Array, (Span, Vec) = Ast::Array(span, x) => (span, x)); + pub fn new(iter: I) -> Self { AstIterator { iter: itertools::put_back(iter) } } + pub fn expect_any(&mut self) -> AstResult { + self.iter.next().or_missing().and_then(T::from_ast) + } + pub fn expect_key_values(&mut self) -> AstResult> { parse_key_values(&mut self.iter) } diff --git a/src/parser/element.rs b/src/parser/element.rs index 728ac0b..7455607 100644 --- a/src/parser/element.rs +++ b/src/parser/element.rs @@ -1,16 +1,13 @@ use super::ast::{Ast, AstIterator, AstType, Span}; -use crate::{error::*, parser, spanned}; +use crate::{error::*, parser, spanned, value::AttrName}; use itertools::Itertools; +use simplexpr::ast::SimplExpr; use std::{ collections::{HashMap, LinkedList}, iter::FromIterator, str::FromStr, }; -type VarName = String; -type AttrValue = String; -type AttrName = String; - pub trait FromAst: Sized { fn from_ast(e: Ast) -> AstResult; } @@ -21,6 +18,17 @@ impl FromAst for Ast { } } +impl FromAst for SimplExpr { + fn from_ast(e: Ast) -> AstResult { + match e { + Ast::Symbol(span, x) => Ok(SimplExpr::VarRef(span.into(), x)), + Ast::Value(span, x) => Ok(SimplExpr::Literal(span.into(), x)), + Ast::SimplExpr(span, x) => Ok(x), + _ => Err(AstError::NotAValue(Some(e.span()), e.expr_type())), + } + } +} + #[derive(Debug, Eq, PartialEq)] pub struct Element { name: String, @@ -36,7 +44,7 @@ impl FromAst for Element { let list = e.as_list()?; let mut iter = AstIterator::new(list.into_iter()); let (_, name) = iter.expect_symbol()?; - let attrs = iter.expect_key_values()?; + let attrs = iter.expect_key_values()?.into_iter().map(|(k, v)| (AttrName(k), v)).collect(); let children = iter.map(C::from_ast).collect::>>()?; Element { span, name, attrs, children } }) diff --git a/src/parser/lexer.rs b/src/parser/lexer.rs index de184e1..d13cfa3 100644 --- a/src/parser/lexer.rs +++ b/src/parser/lexer.rs @@ -61,12 +61,12 @@ regex_rules! { r"\(" => |_| Token::LPren, r"\)" => |_| Token::RPren, r"\[" => |_| Token::LBrack, - r"\]" => |_| Token::LBrack, + r"\]" => |_| Token::RBrack, r"true" => |_| Token::True, r"false" => |_| Token::False, r#""(?:[^"\\]|\\.)*""# => |x| Token::StrLit(x), r#"[+-]?(?:[0-9]+[.])?[0-9]+"# => |x| Token::NumLit(x), - r#"[a-zA-Z_!\?<>/.*-+][^\s{}\(\)]*"# => |x| Token::Symbol(x), + r#"[a-zA-Z_!\?<>/.*-+][^\s{}\(\)\[\](){}]*"# => |x| Token::Symbol(x), r#":\S+"# => |x| Token::Keyword(x), r#";.*"# => |_| Token::Comment, r"[ \t\n\f]+" => |_| Token::Skip @@ -141,7 +141,9 @@ impl Iterator for Lexer { self.pos += len; match LEXER_FNS[i](tok_str.to_string()) { Token::Skip => {} - token => return Some(Ok((old_pos, token, self.pos))), + token => { + return Some(Ok((old_pos, token, self.pos))); + } } } } diff --git a/src/parser/parser.lalrpop b/src/parser/parser.lalrpop index 817a57b..7fc5163 100644 --- a/src/parser/parser.lalrpop +++ b/src/parser/parser.lalrpop @@ -33,7 +33,7 @@ pub Ast: Ast = { => Ast::SimplExpr(Span(l, r, file_id), expr), => x, => x, - => Ast::Value(Span(l, r, file_id), x), + => Ast::Value(Span(l, r, file_id), x.into()), "comment" => Ast::Comment(Span(l, r, file_id)), }; @@ -55,8 +55,8 @@ StrLit: String = { SimplExpr: SimplExpr = { =>? { let expr = x[1..x.len() - 1].to_string(); - simplexpr::parse_string(&expr).map_err(|e| { - let span = e.get_span().map(|simplexpr::Span(simpl_l, simpl_r)| Span(1 + l + simpl_l, 1 + l + simpl_r, file_id)); + simplexpr::parse_string(file_id, &expr).map_err(|e| { + let span = e.get_span().map(|simplexpr::Span(simpl_l, simpl_r, file_id)| Span(1 + l + simpl_l, 1 + l + simpl_r, file_id)); ParseError::User { error: parse_error::ParseError::SimplExpr(span, e) }}) } } diff --git a/src/parser/snapshots/eww_config__parser__element__test__test.snap.new b/src/parser/snapshots/eww_config__parser__element__test__test.snap.new new file mode 100644 index 0000000..56ba341 --- /dev/null +++ b/src/parser/snapshots/eww_config__parser__element__test__test.snap.new @@ -0,0 +1,17 @@ +--- +source: src/parser/element.rs +expression: "Element::::from_ast(parser.parse(0, lexer).unwrap()).unwrap()" + +--- +Element { + name: "box", + attrs: { + ":bar": "12", + ":baz": "hi", + }, + children: [ + foo, + (bar), + ], + span: 0..33, +} diff --git a/src/parser/snapshots/eww_config__parser__test.snap.new b/src/parser/snapshots/eww_config__parser__test.snap.new new file mode 100644 index 0000000..befaabc --- /dev/null +++ b/src/parser/snapshots/eww_config__parser__test.snap.new @@ -0,0 +1,8 @@ +--- +source: src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, \"1\".to_string()))" + +--- +Ok( + "1", +) From 5f164650e9831b547a49bf1531357b697ea01b7d Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Sun, 18 Jul 2021 20:52:17 +0200 Subject: [PATCH 042/137] start adding more eww-specific stuff --- examples/errors.rs | 1 - examples/validation.rs | 2 +- src/config/config.rs | 57 +++++++++++++++++++++++++++++++++++++++ src/config/mod.rs | 2 ++ src/config/var.rs | 34 +++++++++++++++++++++++ src/error.rs | 8 +++--- src/format_diagnostic.rs | 3 +-- src/parser/parser.lalrpop | 7 +++-- 8 files changed, 104 insertions(+), 10 deletions(-) create mode 100644 src/config/config.rs create mode 100644 src/config/var.rs diff --git a/examples/errors.rs b/examples/errors.rs index 9b329da..782e5c0 100644 --- a/examples/errors.rs +++ b/examples/errors.rs @@ -1,5 +1,4 @@ use eww_config::{ - config::*, format_diagnostic::ToDiagnostic, parser::{ast::*, element::FromAst}, }; diff --git a/examples/validation.rs b/examples/validation.rs index f54311e..9361975 100644 --- a/examples/validation.rs +++ b/examples/validation.rs @@ -2,7 +2,7 @@ use eww_config::{ config::{widget_definition::WidgetDefinition, widget_use::WidgetUse, *}, error::AstError, format_diagnostic::ToDiagnostic, - parser::{ast::*, element::FromAst}, + parser::element::FromAst, }; fn main() { diff --git a/src/config/config.rs b/src/config/config.rs new file mode 100644 index 0000000..6c3f4f4 --- /dev/null +++ b/src/config/config.rs @@ -0,0 +1,57 @@ +use std::collections::HashMap; + +use simplexpr::SimplExpr; + +use super::{var::VarDefinition, widget_definition::WidgetDefinition, widget_use::WidgetUse}; +use crate::{ + error::{AstError, AstResult, OptionAstErrorExt}, + parser::{ + ast::{Ast, AstIterator, Span}, + element::{Element, FromAst}, + }, + spanned, + value::{AttrName, VarName}, +}; + +pub enum TopLevel { + VarDefinition(VarDefinition), + WidgetDefinition(WidgetDefinition), +} + +impl FromAst for TopLevel { + fn from_ast(e: Ast) -> AstResult { + let span = e.span(); + spanned!(e.span(), { + let list = e.as_list_ref()?; + match list.first().or_missing()?.as_symbol_ref()?.as_ref() { + "defwidget" => Self::WidgetDefinition(WidgetDefinition::from_ast(e)?), + "defvar" => Self::VarDefinition(VarDefinition::from_ast(e)?), + x => return Err(AstError::UnknownToplevel(Some(span), x.to_string())), + } + }) + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct Config { + widget_definitions: HashMap, + var_definitions: HashMap, +} + +impl FromAst for Config { + fn from_ast(e: Ast) -> AstResult { + let list = e.as_list()?; + let mut config = Self { widget_definitions: HashMap::new(), var_definitions: HashMap::new() }; + for element in list { + match TopLevel::from_ast(element)? { + TopLevel::VarDefinition(x) => { + config.var_definitions.insert(x.name.clone(), x); + } + TopLevel::WidgetDefinition(x) => { + config.widget_definitions.insert(x.name.clone(), x); + } + } + } + Ok(config) + } +} diff --git a/src/config/mod.rs b/src/config/mod.rs index 1cd54e6..683abb4 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,3 +1,5 @@ +mod config; pub mod validate; +pub mod var; pub mod widget_definition; pub mod widget_use; diff --git a/src/config/var.rs b/src/config/var.rs new file mode 100644 index 0000000..a949c94 --- /dev/null +++ b/src/config/var.rs @@ -0,0 +1,34 @@ +use std::collections::HashMap; + +use simplexpr::{dynval::DynVal, SimplExpr}; + +use crate::{ + error::AstResult, + parser::{ + ast::{Ast, AstIterator, Span}, + element::{Element, FromAst}, + }, + spanned, + value::{AttrName, VarName}, +}; + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct VarDefinition { + pub name: VarName, + pub initial_value: DynVal, + pub span: Span, +} + +impl FromAst for VarDefinition { + fn from_ast(e: Ast) -> AstResult { + let span = e.span(); + spanned!(e.span(), { + let list = e.as_list()?; + let mut iter = AstIterator::new(list.into_iter()); + let _ = iter.expect_symbol()?; + let (_, name) = iter.expect_symbol()?; + let (_, initial_value) = iter.expect_value()?; + Self { name: VarName(name), initial_value, span } + }) + } +} diff --git a/src/error.rs b/src/error.rs index a603734..9b7b874 100644 --- a/src/error.rs +++ b/src/error.rs @@ -12,8 +12,8 @@ pub type AstResult = Result; #[derive(Debug, Error)] pub enum AstError { - #[error("Definition invalid")] - InvalidDefinition(Option), + #[error("Unknown toplevel declaration `{1}`")] + UnknownToplevel(Option, String), #[error("Expected another element, but got nothing")] MissingNode(Option), #[error("Wrong type of expression: Expected {1} but got {2}")] @@ -31,7 +31,7 @@ pub enum AstError { impl AstError { pub fn get_span(&self) -> Option { match self { - AstError::InvalidDefinition(span) => *span, + AstError::UnknownToplevel(span, _) => *span, AstError::MissingNode(span) => *span, AstError::WrongExprType(span, ..) => *span, AstError::NotAValue(span, ..) => *span, @@ -67,7 +67,7 @@ fn get_parse_error_span( pub fn spanned(span: Span, err: impl Into) -> AstError { use AstError::*; match err.into() { - AstError::InvalidDefinition(None) => AstError::InvalidDefinition(Some(span)), + AstError::UnknownToplevel(None, x) => AstError::UnknownToplevel(Some(span), x), AstError::MissingNode(None) => AstError::MissingNode(Some(span)), AstError::WrongExprType(None, x, y) => AstError::WrongExprType(Some(span), x, y), x => x, diff --git a/src/format_diagnostic.rs b/src/format_diagnostic.rs index 263bdf2..6ad8f8f 100644 --- a/src/format_diagnostic.rs +++ b/src/format_diagnostic.rs @@ -52,8 +52,7 @@ impl ToDiagnostic for AstError { } } else if let Some(span) = self.get_span() { match self { - AstError::InvalidDefinition(_) => todo!(), - + AstError::UnknownToplevel(_, name) => gen_diagnostic!(format!("{}", self), span), AstError::MissingNode(_) => gen_diagnostic! { msg = "Expected another element", label = span => "Expected another element here", diff --git a/src/parser/parser.lalrpop b/src/parser/parser.lalrpop index 7fc5163..5b1debc 100644 --- a/src/parser/parser.lalrpop +++ b/src/parser/parser.lalrpop @@ -26,10 +26,13 @@ extern { } } +pub Toplevel: Vec = { + <()*> => <> +} pub Ast: Ast = { - "(" )+> ")" => Ast::List(Span(l, r, file_id), elems), - "[" )+> "]" => Ast::Array(Span(l, r, file_id), elems), + "(" )*> ")" => Ast::List(Span(l, r, file_id), elems), + "[" )*> "]" => Ast::Array(Span(l, r, file_id), elems), => Ast::SimplExpr(Span(l, r, file_id), expr), => x, => x, From 7539dda162bba856b8b00e724f9b1f734e81a0ca Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Sun, 18 Jul 2021 20:52:38 +0200 Subject: [PATCH 043/137] add fileid to span --- src/ast.rs | 3 ++- src/error.rs | 23 +++++++++-------- src/lib.rs | 9 ++++--- src/parser/mod.rs | 6 ++--- src/simplexpr_parser.lalrpop | 48 ++++++++++++++++++------------------ 5 files changed, 47 insertions(+), 42 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index 8b7562c..519b0d3 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -2,8 +2,9 @@ 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 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 { diff --git a/src/error.rs b/src/error.rs index 22b6041..74e193f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -8,7 +8,7 @@ pub type Result = std::result::Result; #[derive(thiserror::Error, Debug)] pub enum Error { #[error("Parse error: {source}")] - ParseError { source: lalrpop_util::ParseError }, + ParseError { file_id: usize, source: lalrpop_util::ParseError }, #[error("Type error: {0}")] ConversionError(#[from] dynval::ConversionError), @@ -24,8 +24,8 @@ pub enum Error { } impl Error { - pub fn from_parse_error(err: lalrpop_util::ParseError) -> Self { - Error::ParseError { source: err } + pub fn from_parse_error(file_id: usize, err: lalrpop_util::ParseError) -> Self { + Error::ParseError { file_id, source: err } } pub fn at(self, span: Span) -> Self { @@ -34,7 +34,7 @@ impl Error { pub fn get_span(&self) -> Option { match self { - Self::ParseError { source } => get_parse_error_span(source), + 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(), @@ -43,13 +43,16 @@ impl Error { } } -fn get_parse_error_span(err: &lalrpop_util::ParseError) -> Option { +fn get_parse_error_span( + file_id: usize, + err: &lalrpop_util::ParseError, +) -> Option { match err { - lalrpop_util::ParseError::InvalidToken { location } => Some(Span(*location, *location)), - lalrpop_util::ParseError::UnrecognizedEOF { location, expected: _ } => Some(Span(*location, *location)), - lalrpop_util::ParseError::UnrecognizedToken { token, expected: _ } => Some(Span(token.0, token.2)), - lalrpop_util::ParseError::ExtraToken { token } => Some(Span(token.0, token.2)), - lalrpop_util::ParseError::User { error: LexicalError(l, r) } => Some(Span(*l, *r)), + 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)), } } diff --git a/src/lib.rs b/src/lib.rs index 2398211..74df615 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,7 +6,9 @@ pub mod dynval; pub mod error; pub mod eval; pub mod parser; -use ast::SimplExpr; + +pub use ast::{SimplExpr, Span}; + use lalrpop_util::lalrpop_mod; lalrpop_mod!( @@ -14,7 +16,6 @@ lalrpop_mod!( pub simplexpr_parser ); -pub fn parse_string(s: &str) -> Result { - parser::parse_string(s) +pub fn parse_string(file_id: usize, s: &str) -> Result { + parser::parse_string(file_id, s) } -pub use ast::Span; diff --git a/src/parser/mod.rs b/src/parser/mod.rs index a867374..f833b61 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -6,10 +6,10 @@ use crate::{ error::{Error, Result}, }; -pub fn parse_string(s: &str) -> Result { +pub fn parse_string(file_id: usize, s: &str) -> Result { let lexer = lexer::Lexer::new(s); let parser = crate::simplexpr_parser::ExprParser::new(); - parser.parse(lexer).map_err(Error::from_parse_error) + parser.parse(file_id, lexer).map_err(|e| Error::from_parse_error(file_id, e)) } #[cfg(test)] @@ -20,7 +20,7 @@ mod tests { use crate::parser::lexer::Lexer; ::insta::with_settings!({sort_maps => true}, { $( - ::insta::assert_debug_snapshot!(p.parse(Lexer::new($text))); + ::insta::assert_debug_snapshot!(p.parse(0, Lexer::new($text))); )* }); }} diff --git a/src/simplexpr_parser.lalrpop b/src/simplexpr_parser.lalrpop index 2f1c8dd..f351087 100644 --- a/src/simplexpr_parser.lalrpop +++ b/src/simplexpr_parser.lalrpop @@ -4,7 +4,7 @@ use crate::parser::lalrpop_helpers::*; use lalrpop_util::ParseError; -grammar; +grammar(fid: usize); extern { type Location = usize; @@ -66,54 +66,54 @@ pub Expr: SimplExpr = { }, , - => VarRef(Span(l, r), ident.to_string()), + => VarRef(Span(l, r, fid), ident.to_string()), "(" ")", #[precedence(level="1")] #[assoc(side="right")] - "(" > ")" => FunctionCall(Span(l, r), ident, args), - "[" "]" => JsonAccess(Span(l, r), b(value), b(index)), + "(" > ")" => FunctionCall(Span(l, r, fid), ident, args), + "[" "]" => JsonAccess(Span(l, r, fid), b(value), b(index)), "." => { - JsonAccess(Span(l, r), b(value), b(Literal(Span(lit_l, r), index.into()))) + JsonAccess(Span(l, r, fid), b(value), b(Literal(Span(lit_l, r, fid), index.into()))) }, #[precedence(level="2")] #[assoc(side="right")] - "!" => UnaryOp(Span(l, r), Not, b(e)), + "!" => UnaryOp(Span(l, r, fid), Not, b(e)), #[precedence(level="3")] #[assoc(side="left")] - "*" => BinOp(Span(l, r), b(le), Times, b(re)), - "/" => BinOp(Span(l, r), b(le), Div, b(re)), - "%" => BinOp(Span(l, r), b(le), Mod, b(re)), + "*" => BinOp(Span(l, r, fid), b(le), Times, b(re)), + "/" => BinOp(Span(l, r, fid), b(le), Div, b(re)), + "%" => BinOp(Span(l, r, fid), b(le), Mod, b(re)), #[precedence(level="4")] #[assoc(side="left")] - "+" => BinOp(Span(l, r), b(le), Plus, b(re)), - "-" => BinOp(Span(l, r), b(le), Minus, b(re)), + "+" => BinOp(Span(l, r, fid), b(le), Plus, b(re)), + "-" => BinOp(Span(l, r, fid), b(le), Minus, b(re)), #[precedence(level="5")] #[assoc(side="left")] - "==" => BinOp(Span(l, r), b(le), Equals, b(re)), - "!=" => BinOp(Span(l, r), b(le), NotEquals, b(re)), - "<" => BinOp(Span(l, r), b(le), GT, b(re)), - ">" => BinOp(Span(l, r), b(le), LT, b(re)), - "=~" => BinOp(Span(l, r), b(le), RegexMatch, b(re)), + "==" => BinOp(Span(l, r, fid), b(le), Equals, b(re)), + "!=" => BinOp(Span(l, r, fid), b(le), NotEquals, b(re)), + "<" => BinOp(Span(l, r, fid), b(le), GT, b(re)), + ">" => BinOp(Span(l, r, fid), b(le), LT, b(re)), + "=~" => BinOp(Span(l, r, fid), b(le), RegexMatch, b(re)), #[precedence(level="6")] #[assoc(side="left")] - "&&" => BinOp(Span(l, r), b(le), And, b(re)), - "||" => BinOp(Span(l, r), b(le), Or, b(re)), - "?:" => BinOp(Span(l, r), b(le), Elvis, b(re)), + "&&" => BinOp(Span(l, r, fid), b(le), And, b(re)), + "||" => BinOp(Span(l, r, fid), b(le), Or, b(re)), + "?:" => BinOp(Span(l, r, fid), b(le), Elvis, b(re)), #[precedence(level="7")] #[assoc(side="right")] "?" ":" => { - IfElse(Span(l, r), b(cond), b(then), b(els)) + IfElse(Span(l, r, fid), b(cond), b(then), b(els)) }, }; ExprReset = ; Literal: SimplExpr = { - => SimplExpr::literal(Span(l, r), x), - => SimplExpr::literal(Span(l, r), x), - "true" => SimplExpr::literal(Span(l, r), "true".into()), - "false" => SimplExpr::literal(Span(l, r), "false".into()), + => SimplExpr::literal(Span(l, r, fid), x), + => SimplExpr::literal(Span(l, r, fid), x), + "true" => SimplExpr::literal(Span(l, r, fid), "true".into()), + "false" => SimplExpr::literal(Span(l, r, fid), "false".into()), } StrLit: String = { From 2451f6fd4947ea5323a960daaf77d45667502960 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Mon, 19 Jul 2021 11:46:52 +0200 Subject: [PATCH 044/137] Add script-vars --- Cargo.lock | 7 +++ Cargo.toml | 2 + src/config/config.rs | 36 ++++++++++---- src/config/mod.rs | 3 +- src/config/script_var_definition.rs | 73 +++++++++++++++++++++++++++++ src/config/var.rs | 34 -------------- src/config/var_definition.rs | 32 +++++++++++++ src/config/widget_definition.rs | 31 +++++------- src/error.rs | 17 +++++-- src/lib.rs | 1 + src/parser/element.rs | 30 +++++++++++- src/util.rs | 14 ++++++ 12 files changed, 215 insertions(+), 65 deletions(-) create mode 100644 src/config/script_var_definition.rs delete mode 100644 src/config/var.rs create mode 100644 src/config/var_definition.rs create mode 100644 src/util.rs diff --git a/Cargo.lock b/Cargo.lock index a5c995c..922facd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,6 +20,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "anyhow" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595d3cfa7a60d4555cb5067b99f07142a08ea778de5cf993f7b75c7d8fabc486" + [[package]] name = "ascii-canvas" version = "3.0.0" @@ -195,6 +201,7 @@ checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" name = "eww_config" version = "0.1.0" dependencies = [ + "anyhow", "codespan-reporting", "derive_more", "insta", diff --git a/Cargo.toml b/Cargo.toml index 73a9e5e..9c02841 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,8 @@ serde_json = "1.0" lazy_static = "1.4" pretty_assertions = "0.7" +anyhow = "1" + simplexpr = { path = "../../projects/simplexpr" } diff --git a/src/config/config.rs b/src/config/config.rs index 6c3f4f4..9c77961 100644 --- a/src/config/config.rs +++ b/src/config/config.rs @@ -2,12 +2,16 @@ use std::collections::HashMap; use simplexpr::SimplExpr; -use super::{var::VarDefinition, widget_definition::WidgetDefinition, widget_use::WidgetUse}; +use super::{ + script_var_definition::ScriptVarDefinition, var_definition::VarDefinition, widget_definition::WidgetDefinition, + widget_use::WidgetUse, +}; use crate::{ + config::script_var_definition::{PollScriptVar, TailScriptVar}, error::{AstError, AstResult, OptionAstErrorExt}, parser::{ ast::{Ast, AstIterator, Span}, - element::{Element, FromAst}, + element::{Element, FromAst, FromAstElementContent}, }, spanned, value::{AttrName, VarName}, @@ -15,6 +19,7 @@ use crate::{ pub enum TopLevel { VarDefinition(VarDefinition), + ScriptVarDefinition(ScriptVarDefinition), WidgetDefinition(WidgetDefinition), } @@ -22,11 +27,21 @@ impl FromAst for TopLevel { fn from_ast(e: Ast) -> AstResult { let span = e.span(); spanned!(e.span(), { - let list = e.as_list_ref()?; - match list.first().or_missing()?.as_symbol_ref()?.as_ref() { - "defwidget" => Self::WidgetDefinition(WidgetDefinition::from_ast(e)?), - "defvar" => Self::VarDefinition(VarDefinition::from_ast(e)?), - x => return Err(AstError::UnknownToplevel(Some(span), x.to_string())), + let list = e.as_list()?; + let mut iter = AstIterator::new(list.into_iter()); + let (sym_span, element_name) = iter.expect_symbol()?; + match element_name.as_str() { + x if x == WidgetDefinition::get_element_name() => { + Self::WidgetDefinition(WidgetDefinition::from_tail(span, iter)?) + } + x if x == VarDefinition::get_element_name() => Self::VarDefinition(VarDefinition::from_tail(span, iter)?), + x if x == PollScriptVar::get_element_name() => { + Self::ScriptVarDefinition(ScriptVarDefinition::Poll(PollScriptVar::from_tail(span, iter)?)) + } + x if x == TailScriptVar::get_element_name() => { + Self::ScriptVarDefinition(ScriptVarDefinition::Tail(TailScriptVar::from_tail(span, iter)?)) + } + x => return Err(AstError::UnknownToplevel(Some(sym_span), x.to_string())), } }) } @@ -36,17 +51,22 @@ impl FromAst for TopLevel { pub struct Config { widget_definitions: HashMap, var_definitions: HashMap, + script_vars: HashMap, } impl FromAst for Config { fn from_ast(e: Ast) -> AstResult { let list = e.as_list()?; - let mut config = Self { widget_definitions: HashMap::new(), var_definitions: HashMap::new() }; + let mut config = + Self { widget_definitions: HashMap::new(), var_definitions: HashMap::new(), script_vars: HashMap::new() }; for element in list { match TopLevel::from_ast(element)? { TopLevel::VarDefinition(x) => { config.var_definitions.insert(x.name.clone(), x); } + TopLevel::ScriptVarDefinition(x) => { + config.script_vars.insert(x.name().clone(), x); + } TopLevel::WidgetDefinition(x) => { config.widget_definitions.insert(x.name.clone(), x); } diff --git a/src/config/mod.rs b/src/config/mod.rs index 683abb4..c1c9b70 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,5 +1,6 @@ mod config; +pub mod script_var_definition; pub mod validate; -pub mod var; +pub mod var_definition; pub mod widget_definition; pub mod widget_use; diff --git a/src/config/script_var_definition.rs b/src/config/script_var_definition.rs new file mode 100644 index 0000000..ca46c80 --- /dev/null +++ b/src/config/script_var_definition.rs @@ -0,0 +1,73 @@ +use std::collections::HashMap; + +use simplexpr::{dynval::DynVal, SimplExpr}; + +use crate::{ + error::{AstError, AstResult}, + parser::{ + ast::{Ast, AstIterator, Span}, + element::{Element, FromAst, FromAstElementContent}, + }, + spanned, + value::{AttrName, VarName}, +}; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum ScriptVarDefinition { + Poll(PollScriptVar), + Tail(TailScriptVar), +} + +impl ScriptVarDefinition { + pub fn name(&self) -> &VarName { + match self { + ScriptVarDefinition::Poll(x) => &x.name, + ScriptVarDefinition::Tail(x) => &x.name, + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum VarSource { + // TODO allow for other executors? (python, etc) + Shell(String), + Function(fn() -> Result>), +} +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct PollScriptVar { + pub name: VarName, + pub command: VarSource, + pub interval: std::time::Duration, +} + +impl FromAstElementContent for PollScriptVar { + fn get_element_name() -> &'static str { + "defpollvar" + } + + fn from_tail>(span: Span, mut iter: AstIterator) -> AstResult { + let (_, name) = iter.expect_symbol()?; + let attrs: HashMap = iter.expect_key_values()?; + let interval = attrs.get("interval").unwrap(); + let interval = crate::util::parse_duration(interval).map_err(|e| AstError::Other(Some(span), e.into()))?; + let (_, script) = iter.expect_value()?; + Ok(Self { name: VarName(name), command: VarSource::Shell(script.to_string()), interval }) + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct TailScriptVar { + pub name: VarName, + pub command: String, +} +impl FromAstElementContent for TailScriptVar { + fn get_element_name() -> &'static str { + "deftailvar" + } + + fn from_tail>(span: Span, mut iter: AstIterator) -> AstResult { + let (_, name) = iter.expect_symbol()?; + let (_, script) = iter.expect_value()?; + Ok(Self { name: VarName(name), command: script.to_string() }) + } +} diff --git a/src/config/var.rs b/src/config/var.rs deleted file mode 100644 index a949c94..0000000 --- a/src/config/var.rs +++ /dev/null @@ -1,34 +0,0 @@ -use std::collections::HashMap; - -use simplexpr::{dynval::DynVal, SimplExpr}; - -use crate::{ - error::AstResult, - parser::{ - ast::{Ast, AstIterator, Span}, - element::{Element, FromAst}, - }, - spanned, - value::{AttrName, VarName}, -}; - -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct VarDefinition { - pub name: VarName, - pub initial_value: DynVal, - pub span: Span, -} - -impl FromAst for VarDefinition { - fn from_ast(e: Ast) -> AstResult { - let span = e.span(); - spanned!(e.span(), { - let list = e.as_list()?; - let mut iter = AstIterator::new(list.into_iter()); - let _ = iter.expect_symbol()?; - let (_, name) = iter.expect_symbol()?; - let (_, initial_value) = iter.expect_value()?; - Self { name: VarName(name), initial_value, span } - }) - } -} diff --git a/src/config/var_definition.rs b/src/config/var_definition.rs new file mode 100644 index 0000000..b60d078 --- /dev/null +++ b/src/config/var_definition.rs @@ -0,0 +1,32 @@ +use std::collections::HashMap; + +use simplexpr::{dynval::DynVal, SimplExpr}; + +use crate::{ + error::AstResult, + parser::{ + ast::{Ast, AstIterator, Span}, + element::{Element, FromAst, FromAstElementContent}, + }, + spanned, + value::{AttrName, VarName}, +}; + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct VarDefinition { + pub name: VarName, + pub initial_value: DynVal, + pub span: Span, +} + +impl FromAstElementContent for VarDefinition { + fn get_element_name() -> &'static str { + "defvar" + } + + fn from_tail>(span: Span, mut iter: AstIterator) -> AstResult { + let (_, name) = iter.expect_symbol()?; + let (_, initial_value) = iter.expect_value()?; + Ok(Self { name: VarName(name), initial_value, span }) + } +} diff --git a/src/config/widget_definition.rs b/src/config/widget_definition.rs index a2ffd9a..172b687 100644 --- a/src/config/widget_definition.rs +++ b/src/config/widget_definition.rs @@ -6,7 +6,7 @@ use crate::{ error::AstResult, parser::{ ast::{Ast, AstIterator, Span}, - element::{Element, FromAst}, + element::{Element, FromAst, FromAstElementContent}, }, spanned, value::{AttrName, VarName}, @@ -22,23 +22,18 @@ pub struct WidgetDefinition { pub args_span: Span, } -impl FromAst for WidgetDefinition { - fn from_ast(e: Ast) -> AstResult { - let span = e.span(); - spanned!(e.span(), { - let list = e.as_list()?; - let mut iter = AstIterator::new(list.into_iter()); +impl FromAstElementContent for WidgetDefinition { + fn get_element_name() -> &'static str { + "defwidget" + } - let (_, def_type) = iter.expect_symbol()?; - assert!(def_type == "defwidget"); - - let (_, name) = iter.expect_symbol()?; - let (args_span, expected_args) = iter.expect_array()?; - let expected_args = expected_args.into_iter().map(|x| x.as_symbol().map(AttrName)).collect::>()?; - let widget = iter.expect_any().and_then(WidgetUse::from_ast)?; - // TODO verify that this was the last element in the list - // iter.expect_done()?; - Self { name, expected_args, widget, span, args_span } - }) + fn from_tail>(span: Span, mut iter: AstIterator) -> AstResult { + let (_, name) = iter.expect_symbol()?; + let (args_span, expected_args) = iter.expect_array()?; + let expected_args = expected_args.into_iter().map(|x| x.as_symbol().map(AttrName)).collect::>()?; + let widget = iter.expect_any().and_then(WidgetUse::from_ast)?; + // TODO verify that this was the last element in the list + // iter.expect_done()?; + Ok(Self { name, expected_args, widget, span, args_span }) } } diff --git a/src/error.rs b/src/error.rs index 9b7b874..8b6fcf2 100644 --- a/src/error.rs +++ b/src/error.rs @@ -20,6 +20,10 @@ pub enum AstError { WrongExprType(Option, AstType, AstType), #[error("Expected to get a value, but got {1}")] NotAValue(Option, AstType), + #[error("Expected element {1}, but read {2}")] + MismatchedElementName(Option, String, String), + #[error("{1}")] + Other(Option, Box), #[error(transparent)] ValidationError(#[from] ValidationError), @@ -35,6 +39,8 @@ impl AstError { AstError::MissingNode(span) => *span, AstError::WrongExprType(span, ..) => *span, AstError::NotAValue(span, ..) => *span, + AstError::MismatchedElementName(span, ..) => *span, + AstError::Other(span, ..) => *span, AstError::ValidationError(error) => None, // TODO none here is stupid AstError::ParseError { file_id, source } => file_id.and_then(|id| get_parse_error_span(id, source)), } @@ -67,9 +73,14 @@ fn get_parse_error_span( pub fn spanned(span: Span, err: impl Into) -> AstError { use AstError::*; match err.into() { - AstError::UnknownToplevel(None, x) => AstError::UnknownToplevel(Some(span), x), - AstError::MissingNode(None) => AstError::MissingNode(Some(span)), - AstError::WrongExprType(None, x, y) => AstError::WrongExprType(Some(span), x, y), + UnknownToplevel(None, x) => UnknownToplevel(Some(span), x), + MissingNode(None) => MissingNode(Some(span)), + WrongExprType(None, x, y) => WrongExprType(Some(span), x, y), + UnknownToplevel(None, x) => UnknownToplevel(Some(span), x), + MissingNode(None) => MissingNode(Some(span)), + NotAValue(None, x) => NotAValue(Some(span), x), + MismatchedElementName(None, x, y) => MismatchedElementName(Some(span), x, y), + Other(None, x) => Other(Some(span), x), x => x, } } diff --git a/src/lib.rs b/src/lib.rs index 687c8f1..91cace7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,4 +6,5 @@ pub mod config; pub mod error; pub mod format_diagnostic; pub mod parser; +mod util; pub mod value; diff --git a/src/parser/element.rs b/src/parser/element.rs index 7455607..9d1201b 100644 --- a/src/parser/element.rs +++ b/src/parser/element.rs @@ -1,7 +1,7 @@ use super::ast::{Ast, AstIterator, AstType, Span}; use crate::{error::*, parser, spanned, value::AttrName}; use itertools::Itertools; -use simplexpr::ast::SimplExpr; +use simplexpr::{ast::SimplExpr, dynval::DynVal}; use std::{ collections::{HashMap, LinkedList}, iter::FromIterator, @@ -18,6 +18,34 @@ impl FromAst for Ast { } } +impl FromAst for String { + fn from_ast(e: Ast) -> AstResult { + Ok(e.as_value()?.to_string()) + } +} + +/// A trait that allows creating a type from the tail of a list-node. +/// I.e. to parse (foo [a b] (c d)), [from_tail] would just get [a b] (c d). +pub trait FromAstElementContent: Sized { + fn get_element_name() -> &'static str; + fn from_tail>(span: Span, iter: AstIterator) -> AstResult; +} + +impl FromAst for T { + fn from_ast(e: Ast) -> AstResult { + let span = e.span(); + spanned!(e.span(), { + let list = e.as_list()?; + let mut iter = AstIterator::new(list.into_iter()); + let (_, element_name) = iter.expect_symbol()?; + if Self::get_element_name() != element_name { + return Err(AstError::MismatchedElementName(Some(span), Self::get_element_name().to_string(), element_name)); + } + Self::from_tail(span, iter)? + }) + } +} + impl FromAst for SimplExpr { fn from_ast(e: Ast) -> AstResult { match e { diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..a15474f --- /dev/null +++ b/src/util.rs @@ -0,0 +1,14 @@ +pub fn parse_duration(s: &str) -> anyhow::Result { + use std::time::Duration; + if s.ends_with("ms") { + Ok(Duration::from_millis(s.trim_end_matches("ms").parse()?)) + } else if s.ends_with('s') { + Ok(Duration::from_secs(s.trim_end_matches('s').parse()?)) + } else if s.ends_with('m') { + Ok(Duration::from_secs(s.trim_end_matches('m').parse::()? * 60)) + } else if s.ends_with('h') { + Ok(Duration::from_secs(s.trim_end_matches('h').parse::()? * 60 * 60)) + } else { + Err(anyhow::anyhow!("Failed to parse duration `{}`", s)) + } +} From 00abe27c139da27ec801ad31d9ab8bdce528a0d6 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Mon, 19 Jul 2021 13:27:30 +0200 Subject: [PATCH 045/137] fix some tests and bugs --- Cargo.lock | 18 ++++++ Cargo.toml | 2 +- src/config/config.rs | 2 +- src/config/mod.rs | 2 + src/config/script_var_definition.rs | 9 +-- .../eww_config__config__test__config.snap | 55 +++++++++++++++++++ src/config/test.rs | 30 ++++++++++ src/config/var_definition.rs | 2 +- src/config/widget_definition.rs | 2 +- src/config/widget_use.rs | 2 +- src/parser/ast.rs | 6 +- src/parser/element.rs | 25 +-------- src/parser/lexer.rs | 8 +-- src/parser/mod.rs | 2 +- src/parser/parser.lalrpop | 6 +- ..._config__parser__element__test__test.snap} | 4 +- .../eww_config__parser__test-10.snap | 8 +++ .../eww_config__parser__test-11.snap | 8 +++ .../eww_config__parser__test-12.snap | 8 +++ .../eww_config__parser__test-13.snap | 8 +++ .../eww_config__parser__test-14.snap | 8 +++ .../eww_config__parser__test-15.snap | 8 +++ .../eww_config__parser__test-16.snap | 8 +++ .../eww_config__parser__test-17.snap | 8 +++ .../snapshots/eww_config__parser__test-2.snap | 8 +++ .../snapshots/eww_config__parser__test-3.snap | 8 +++ .../snapshots/eww_config__parser__test-4.snap | 8 +++ .../snapshots/eww_config__parser__test-5.snap | 8 +++ .../snapshots/eww_config__parser__test-6.snap | 8 +++ .../snapshots/eww_config__parser__test-7.snap | 8 +++ .../snapshots/eww_config__parser__test-8.snap | 8 +++ .../snapshots/eww_config__parser__test-9.snap | 8 +++ ...snap.new => eww_config__parser__test.snap} | 0 .../eww_config__config__test__test.snap | 21 ------- src/snapshots/eww_config__test-10.snap | 13 ----- src/snapshots/eww_config__test-11.snap | 13 ----- src/snapshots/eww_config__test-12.snap | 13 ----- src/snapshots/eww_config__test-13.snap | 13 ----- src/snapshots/eww_config__test-14.snap | 25 --------- src/snapshots/eww_config__test-15.snap | 8 --- src/snapshots/eww_config__test-16.snap | 15 ----- src/snapshots/eww_config__test-17.snap | 8 --- src/snapshots/eww_config__test-2.snap | 12 ---- src/snapshots/eww_config__test-3.snap | 8 --- src/snapshots/eww_config__test-4.snap | 8 --- src/snapshots/eww_config__test-5.snap | 13 ----- src/snapshots/eww_config__test-6.snap | 14 ----- src/snapshots/eww_config__test-7.snap | 13 ----- src/snapshots/eww_config__test-8.snap | 13 ----- src/snapshots/eww_config__test-9.snap | 13 ----- src/snapshots/eww_config__test.snap | 8 --- 51 files changed, 257 insertions(+), 277 deletions(-) create mode 100644 src/config/snapshots/eww_config__config__test__config.snap create mode 100644 src/config/test.rs rename src/parser/snapshots/{eww_config__parser__element__test__test.snap.new => eww_config__parser__element__test__test.snap} (84%) create mode 100644 src/parser/snapshots/eww_config__parser__test-10.snap create mode 100644 src/parser/snapshots/eww_config__parser__test-11.snap create mode 100644 src/parser/snapshots/eww_config__parser__test-12.snap create mode 100644 src/parser/snapshots/eww_config__parser__test-13.snap create mode 100644 src/parser/snapshots/eww_config__parser__test-14.snap create mode 100644 src/parser/snapshots/eww_config__parser__test-15.snap create mode 100644 src/parser/snapshots/eww_config__parser__test-16.snap create mode 100644 src/parser/snapshots/eww_config__parser__test-17.snap create mode 100644 src/parser/snapshots/eww_config__parser__test-2.snap create mode 100644 src/parser/snapshots/eww_config__parser__test-3.snap create mode 100644 src/parser/snapshots/eww_config__parser__test-4.snap create mode 100644 src/parser/snapshots/eww_config__parser__test-5.snap create mode 100644 src/parser/snapshots/eww_config__parser__test-6.snap create mode 100644 src/parser/snapshots/eww_config__parser__test-7.snap create mode 100644 src/parser/snapshots/eww_config__parser__test-8.snap create mode 100644 src/parser/snapshots/eww_config__parser__test-9.snap rename src/parser/snapshots/{eww_config__parser__test.snap.new => eww_config__parser__test.snap} (100%) delete mode 100644 src/snapshots/eww_config__config__test__test.snap delete mode 100644 src/snapshots/eww_config__test-10.snap delete mode 100644 src/snapshots/eww_config__test-11.snap delete mode 100644 src/snapshots/eww_config__test-12.snap delete mode 100644 src/snapshots/eww_config__test-13.snap delete mode 100644 src/snapshots/eww_config__test-14.snap delete mode 100644 src/snapshots/eww_config__test-15.snap delete mode 100644 src/snapshots/eww_config__test-16.snap delete mode 100644 src/snapshots/eww_config__test-17.snap delete mode 100644 src/snapshots/eww_config__test-2.snap delete mode 100644 src/snapshots/eww_config__test-3.snap delete mode 100644 src/snapshots/eww_config__test-4.snap delete mode 100644 src/snapshots/eww_config__test-5.snap delete mode 100644 src/snapshots/eww_config__test-6.snap delete mode 100644 src/snapshots/eww_config__test-7.snap delete mode 100644 src/snapshots/eww_config__test-8.snap delete mode 100644 src/snapshots/eww_config__test-9.snap delete mode 100644 src/snapshots/eww_config__test.snap diff --git a/Cargo.lock b/Cargo.lock index 922facd..8a5778d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -52,6 +52,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + [[package]] name = "beef" version = "0.5.0" @@ -284,6 +290,7 @@ checksum = "c4a1b21a2971cea49ca4613c0e9fe8225ecaf5de64090fddc6002284726e9244" dependencies = [ "console", "lazy_static", + "ron", "serde", "serde_json", "serde_yaml", @@ -522,6 +529,17 @@ version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +[[package]] +name = "ron" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "064ea8613fb712a19faf920022ec8ddf134984f100090764a4e1d768f3827f1f" +dependencies = [ + "base64", + "bitflags", + "serde", +] + [[package]] name = "rustc_version" version = "0.3.3" diff --git a/Cargo.toml b/Cargo.toml index 9c02841..8b35304 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,4 +32,4 @@ simplexpr = { path = "../../projects/simplexpr" } lalrpop = "0.19.5" [dev-dependencies] -insta = "1.7" +insta = { version = "1.7", features = ["ron"]} diff --git a/src/config/config.rs b/src/config/config.rs index 9c77961..e34e766 100644 --- a/src/config/config.rs +++ b/src/config/config.rs @@ -47,7 +47,7 @@ impl FromAst for TopLevel { } } -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone, serde::Serialize)] pub struct Config { widget_definitions: HashMap, var_definitions: HashMap, diff --git a/src/config/mod.rs b/src/config/mod.rs index c1c9b70..42c678c 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,5 +1,7 @@ mod config; pub mod script_var_definition; +#[cfg(test)] +mod test; pub mod validate; pub mod var_definition; pub mod widget_definition; diff --git a/src/config/script_var_definition.rs b/src/config/script_var_definition.rs index ca46c80..5801ca2 100644 --- a/src/config/script_var_definition.rs +++ b/src/config/script_var_definition.rs @@ -12,7 +12,7 @@ use crate::{ value::{AttrName, VarName}, }; -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)] pub enum ScriptVarDefinition { Poll(PollScriptVar), Tail(TailScriptVar), @@ -27,13 +27,14 @@ impl ScriptVarDefinition { } } -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)] pub enum VarSource { // TODO allow for other executors? (python, etc) Shell(String), + #[serde(skip)] Function(fn() -> Result>), } -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)] pub struct PollScriptVar { pub name: VarName, pub command: VarSource, @@ -55,7 +56,7 @@ impl FromAstElementContent for PollScriptVar { } } -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)] pub struct TailScriptVar { pub name: VarName, pub command: String, diff --git a/src/config/snapshots/eww_config__config__test__config.snap b/src/config/snapshots/eww_config__config__test__config.snap new file mode 100644 index 0000000..d7ba933 --- /dev/null +++ b/src/config/snapshots/eww_config__config__test__config.snap @@ -0,0 +1,55 @@ +--- +source: src/config/test.rs +expression: config.unwrap() + +--- +Config( + widget_definitions: { + "bar": WidgetDefinition( + name: "bar", + expected_args: [ + AttrName("arg"), + AttrName("arg2"), + ], + widget: WidgetUse( + name: "text", + attrs: { + AttrName("text"): Literal(Span(99, 104, 0), DynVal("bla", None)), + }, + children: [], + span: Span(99, 104, 0), + ), + span: Span(61, 105, 0), + args_span: Span(76, 86, 0), + ), + "foo": WidgetDefinition( + name: "foo", + expected_args: [ + AttrName("arg"), + ], + widget: WidgetUse( + name: "text", + attrs: { + AttrName("text"): Literal(Span(44, 51, 0), DynVal("heyho", None)), + }, + children: [], + span: Span(44, 51, 0), + ), + span: Span(11, 52, 0), + args_span: Span(26, 31, 0), + ), + }, + var_definitions: { + VarName("some_var"): VarDefinition( + name: VarName("some_var"), + initial_value: DynVal("bla", None), + span: Span(114, 137, 0), + ), + }, + script_vars: { + VarName("stuff"): Tail(TailScriptVar( + name: VarName("stuff"), + command: "tail -f stuff", + )), + }, +) diff --git a/src/config/test.rs b/src/config/test.rs new file mode 100644 index 0000000..9d9c454 --- /dev/null +++ b/src/config/test.rs @@ -0,0 +1,30 @@ +use crate::{ + config::config::Config, + parser::{ + self, + ast::{Ast, Span}, + element::FromAst, + lexer::Lexer, + }, +}; + +#[test] +fn test_config() { + let input = r#" + (defwidget foo [arg] + "heyho") + (defwidget bar [arg arg2] + "bla") + (defvar some_var "bla") + (defpollvar stuff :interval "12s" "date") + (deftailvar stuff "tail -f stuff") + "#; + + let lexer = Lexer::new(0, input.to_string()); + let p = parser::parser::ToplevelParser::new(); + let (span, parse_result) = p.parse(0, lexer).unwrap(); + let config = Config::from_ast(Ast::List(span, parse_result)); + insta::with_settings!({sort_maps => true}, { + insta::assert_ron_snapshot!(config.unwrap()); + }); +} diff --git a/src/config/var_definition.rs b/src/config/var_definition.rs index b60d078..3002980 100644 --- a/src/config/var_definition.rs +++ b/src/config/var_definition.rs @@ -12,7 +12,7 @@ use crate::{ value::{AttrName, VarName}, }; -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone, serde::Serialize)] pub struct VarDefinition { pub name: VarName, pub initial_value: DynVal, diff --git a/src/config/widget_definition.rs b/src/config/widget_definition.rs index 172b687..bb3764f 100644 --- a/src/config/widget_definition.rs +++ b/src/config/widget_definition.rs @@ -13,7 +13,7 @@ use crate::{ }; use super::widget_use::WidgetUse; -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone, serde::Serialize)] pub struct WidgetDefinition { pub name: String, pub expected_args: Vec, diff --git a/src/config/widget_use.rs b/src/config/widget_use.rs index 79949e3..be29039 100644 --- a/src/config/widget_use.rs +++ b/src/config/widget_use.rs @@ -11,7 +11,7 @@ use crate::{ spanned, value::AttrName, }; -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone, serde::Serialize)] pub struct WidgetUse { pub name: String, pub attrs: HashMap, diff --git a/src/parser/ast.rs b/src/parser/ast.rs index c68d279..ed10d76 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -7,7 +7,7 @@ use std::fmt::Display; use super::element::FromAst; use crate::error::{AstError, AstResult, OptionAstErrorExt}; -#[derive(Eq, PartialEq, Clone, Copy)] +#[derive(Eq, PartialEq, Clone, Copy, serde::Serialize)] pub struct Span(pub usize, pub usize, pub usize); impl Into for Span { @@ -121,9 +121,9 @@ impl std::fmt::Display for Ast { match self { List(_, x) => write!(f, "({})", x.iter().map(|e| format!("{}", e)).join(" ")), Array(_, x) => write!(f, "({})", x.iter().map(|e| format!("{}", e)).join(" ")), - Keyword(_, x) => write!(f, "{}", x), + Keyword(_, x) => write!(f, ":{}", x), Symbol(_, x) => write!(f, "{}", x), - Value(_, x) => write!(f, "{}", x), + Value(_, x) => write!(f, "\"{}\"", x), SimplExpr(_, x) => write!(f, "{{{}}}", x), Comment(_) => write!(f, ""), } diff --git a/src/parser/element.rs b/src/parser/element.rs index 9d1201b..9bbff0a 100644 --- a/src/parser/element.rs +++ b/src/parser/element.rs @@ -20,7 +20,7 @@ impl FromAst for Ast { impl FromAst for String { fn from_ast(e: Ast) -> AstResult { - Ok(e.as_value()?.to_string()) + Ok(e.as_value()?.as_string().unwrap()) } } @@ -78,26 +78,3 @@ impl FromAst for Element { }) } } - -#[cfg(test)] -mod test { - - use super::super::{ - ast::Ast, - element::{Element, FromAst}, - lexer, - }; - - use insta; - - #[test] - fn test() { - let parser = super::parser::parser::AstParser::new(); - insta::with_settings!({sort_maps => true}, { - let lexer = lexer::Lexer::new(0, "(box :bar 12 :baz \"hi\" foo (bar))".to_string()); - insta::assert_debug_snapshot!( - Element::::from_ast(parser.parse(0, lexer).unwrap()).unwrap() - ); - }); - } -} diff --git a/src/parser/lexer.rs b/src/parser/lexer.rs index d13cfa3..eb74f14 100644 --- a/src/parser/lexer.rs +++ b/src/parser/lexer.rs @@ -66,8 +66,8 @@ regex_rules! { r"false" => |_| Token::False, r#""(?:[^"\\]|\\.)*""# => |x| Token::StrLit(x), r#"[+-]?(?:[0-9]+[.])?[0-9]+"# => |x| Token::NumLit(x), - r#"[a-zA-Z_!\?<>/.*-+][^\s{}\(\)\[\](){}]*"# => |x| Token::Symbol(x), - r#":\S+"# => |x| Token::Keyword(x), + r#":[^\s\)\]}]+"# => |x| Token::Keyword(x), + r#"[a-zA-Z_!\?<>/\.\*-\+][^\s{}\(\)\[\](){}]*"# => |x| Token::Symbol(x), r#";.*"# => |_| Token::Comment, r"[ \t\n\f]+" => |_| Token::Skip } @@ -126,7 +126,7 @@ impl Iterator for Lexer { let m = LEXER_REGEXES[i].find(string).unwrap(); (m.end(), i) }) - .next(); + .min_by_key(|(_, x)| *x); let (len, i) = match matched_token { Some(x) => x, @@ -140,7 +140,7 @@ impl Iterator for Lexer { let old_pos = self.pos; self.pos += len; match LEXER_FNS[i](tok_str.to_string()) { - Token::Skip => {} + Token::Skip | Token::Comment => {} token => { return Some(Ok((old_pos, token, self.pos))); } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index d00ad8a..e8ed750 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -54,7 +54,7 @@ fn test() { r#"(test "h\"i")"#, r#"(test " hi ")"#, "(+ (1 2 (* 2 5)))", - r#"; test"#, + r#"foo ; test"#, r#"(f arg ; test arg2)"#, "\"h\\\"i\"" diff --git a/src/parser/parser.lalrpop b/src/parser/parser.lalrpop index 5b1debc..7ecc91b 100644 --- a/src/parser/parser.lalrpop +++ b/src/parser/parser.lalrpop @@ -26,8 +26,8 @@ extern { } } -pub Toplevel: Vec = { - <()*> => <> +pub Toplevel: (Span, Vec) = { + )*> => (Span(l, r, file_id), elems) } pub Ast: Ast = { @@ -40,7 +40,7 @@ pub Ast: Ast = { "comment" => Ast::Comment(Span(l, r, file_id)), }; -Keyword: Ast = => Ast::Keyword(Span(l, r, file_id), x.to_string()); +Keyword: Ast = => Ast::Keyword(Span(l, r, file_id), x[1..].to_string()); Symbol: Ast = => Ast::Symbol(Span(l, r, file_id), x.to_string()); Value: String = { diff --git a/src/parser/snapshots/eww_config__parser__element__test__test.snap.new b/src/parser/snapshots/eww_config__parser__element__test__test.snap similarity index 84% rename from src/parser/snapshots/eww_config__parser__element__test__test.snap.new rename to src/parser/snapshots/eww_config__parser__element__test__test.snap index 56ba341..f38b397 100644 --- a/src/parser/snapshots/eww_config__parser__element__test__test.snap.new +++ b/src/parser/snapshots/eww_config__parser__element__test__test.snap @@ -6,8 +6,8 @@ expression: "Element::::from_ast(parser.parse(0, lexer).unwrap()).unwr Element { name: "box", attrs: { - ":bar": "12", - ":baz": "hi", + "baz": "hi", + "bar": "12", }, children: [ foo, diff --git a/src/parser/snapshots/eww_config__parser__test-10.snap b/src/parser/snapshots/eww_config__parser__test-10.snap new file mode 100644 index 0000000..86604ce --- /dev/null +++ b/src/parser/snapshots/eww_config__parser__test-10.snap @@ -0,0 +1,8 @@ +--- +source: src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, \"(lol😄 1)\".to_string()))" + +--- +Ok( + (lol😄 "1"), +) diff --git a/src/parser/snapshots/eww_config__parser__test-11.snap b/src/parser/snapshots/eww_config__parser__test-11.snap new file mode 100644 index 0000000..ac7da83 --- /dev/null +++ b/src/parser/snapshots/eww_config__parser__test-11.snap @@ -0,0 +1,8 @@ +--- +source: src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, r#\"(test \"hi\")\"#.to_string()))" + +--- +Ok( + (test "hi"), +) diff --git a/src/parser/snapshots/eww_config__parser__test-12.snap b/src/parser/snapshots/eww_config__parser__test-12.snap new file mode 100644 index 0000000..1906a01 --- /dev/null +++ b/src/parser/snapshots/eww_config__parser__test-12.snap @@ -0,0 +1,8 @@ +--- +source: src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, r#\"(test \"h\\\"i\")\"#.to_string()))" + +--- +Ok( + (test "h\"i"), +) diff --git a/src/parser/snapshots/eww_config__parser__test-13.snap b/src/parser/snapshots/eww_config__parser__test-13.snap new file mode 100644 index 0000000..53ceea1 --- /dev/null +++ b/src/parser/snapshots/eww_config__parser__test-13.snap @@ -0,0 +1,8 @@ +--- +source: src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, r#\"(test \" hi \")\"#.to_string()))" + +--- +Ok( + (test " hi "), +) diff --git a/src/parser/snapshots/eww_config__parser__test-14.snap b/src/parser/snapshots/eww_config__parser__test-14.snap new file mode 100644 index 0000000..de774e7 --- /dev/null +++ b/src/parser/snapshots/eww_config__parser__test-14.snap @@ -0,0 +1,8 @@ +--- +source: src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, \"(+ (1 2 (* 2 5)))\".to_string()))" + +--- +Ok( + (+ ("1" "2" (* "2" "5"))), +) diff --git a/src/parser/snapshots/eww_config__parser__test-15.snap b/src/parser/snapshots/eww_config__parser__test-15.snap new file mode 100644 index 0000000..929bfab --- /dev/null +++ b/src/parser/snapshots/eww_config__parser__test-15.snap @@ -0,0 +1,8 @@ +--- +source: src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, r#\"foo ; test\"#.to_string()))" + +--- +Ok( + foo, +) diff --git a/src/parser/snapshots/eww_config__parser__test-16.snap b/src/parser/snapshots/eww_config__parser__test-16.snap new file mode 100644 index 0000000..bd07751 --- /dev/null +++ b/src/parser/snapshots/eww_config__parser__test-16.snap @@ -0,0 +1,8 @@ +--- +source: src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, r#\"(f arg ; test\n arg2)\"#.to_string()))" + +--- +Ok( + (f arg arg2), +) diff --git a/src/parser/snapshots/eww_config__parser__test-17.snap b/src/parser/snapshots/eww_config__parser__test-17.snap new file mode 100644 index 0000000..23356d7 --- /dev/null +++ b/src/parser/snapshots/eww_config__parser__test-17.snap @@ -0,0 +1,8 @@ +--- +source: src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, \"\\\"h\\\\\\\"i\\\"\".to_string()))" + +--- +Ok( + "h\"i", +) diff --git a/src/parser/snapshots/eww_config__parser__test-2.snap b/src/parser/snapshots/eww_config__parser__test-2.snap new file mode 100644 index 0000000..43fb4a4 --- /dev/null +++ b/src/parser/snapshots/eww_config__parser__test-2.snap @@ -0,0 +1,8 @@ +--- +source: src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, \"(12)\".to_string()))" + +--- +Ok( + ("12"), +) diff --git a/src/parser/snapshots/eww_config__parser__test-3.snap b/src/parser/snapshots/eww_config__parser__test-3.snap new file mode 100644 index 0000000..fb2160d --- /dev/null +++ b/src/parser/snapshots/eww_config__parser__test-3.snap @@ -0,0 +1,8 @@ +--- +source: src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, \"1.2\".to_string()))" + +--- +Ok( + "1.2", +) diff --git a/src/parser/snapshots/eww_config__parser__test-4.snap b/src/parser/snapshots/eww_config__parser__test-4.snap new file mode 100644 index 0000000..464f39b --- /dev/null +++ b/src/parser/snapshots/eww_config__parser__test-4.snap @@ -0,0 +1,8 @@ +--- +source: src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, \"-1.2\".to_string()))" + +--- +Ok( + "-1.2", +) diff --git a/src/parser/snapshots/eww_config__parser__test-5.snap b/src/parser/snapshots/eww_config__parser__test-5.snap new file mode 100644 index 0000000..d1331e7 --- /dev/null +++ b/src/parser/snapshots/eww_config__parser__test-5.snap @@ -0,0 +1,8 @@ +--- +source: src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, \"(1 2)\".to_string()))" + +--- +Ok( + ("1" "2"), +) diff --git a/src/parser/snapshots/eww_config__parser__test-6.snap b/src/parser/snapshots/eww_config__parser__test-6.snap new file mode 100644 index 0000000..31b00ed --- /dev/null +++ b/src/parser/snapshots/eww_config__parser__test-6.snap @@ -0,0 +1,8 @@ +--- +source: src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, \"(1 :foo 1)\".to_string()))" + +--- +Ok( + ("1" :foo "1"), +) diff --git a/src/parser/snapshots/eww_config__parser__test-7.snap b/src/parser/snapshots/eww_config__parser__test-7.snap new file mode 100644 index 0000000..e0454d1 --- /dev/null +++ b/src/parser/snapshots/eww_config__parser__test-7.snap @@ -0,0 +1,8 @@ +--- +source: src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, \"(:foo 1)\".to_string()))" + +--- +Ok( + (:foo "1"), +) diff --git a/src/parser/snapshots/eww_config__parser__test-8.snap b/src/parser/snapshots/eww_config__parser__test-8.snap new file mode 100644 index 0000000..9025a0f --- /dev/null +++ b/src/parser/snapshots/eww_config__parser__test-8.snap @@ -0,0 +1,8 @@ +--- +source: src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, \"(:foo->: 1)\".to_string()))" + +--- +Ok( + (:foo->: "1"), +) diff --git a/src/parser/snapshots/eww_config__parser__test-9.snap b/src/parser/snapshots/eww_config__parser__test-9.snap new file mode 100644 index 0000000..503bb62 --- /dev/null +++ b/src/parser/snapshots/eww_config__parser__test-9.snap @@ -0,0 +1,8 @@ +--- +source: src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, \"(foo 1)\".to_string()))" + +--- +Ok( + (foo "1"), +) diff --git a/src/parser/snapshots/eww_config__parser__test.snap.new b/src/parser/snapshots/eww_config__parser__test.snap similarity index 100% rename from src/parser/snapshots/eww_config__parser__test.snap.new rename to src/parser/snapshots/eww_config__parser__test.snap diff --git a/src/snapshots/eww_config__config__test__test.snap b/src/snapshots/eww_config__config__test__test.snap deleted file mode 100644 index ac592e4..0000000 --- a/src/snapshots/eww_config__config__test__test.snap +++ /dev/null @@ -1,21 +0,0 @@ ---- -source: src/config.rs -expression: "Element::::from_expr(parser.parse(0, lexer).unwrap()).unwrap()" - ---- -Element { - name: "box", - attrs: { - ":bar": Value<10..12>(12), - ":baz": Value<18..22>(hi), - }, - children: [ - Symbol<23..26>(foo), - List<27..32>( - [ - Symbol<28..31>(bar), - ], - ), - ], - span: 0..33, -} diff --git a/src/snapshots/eww_config__test-10.snap b/src/snapshots/eww_config__test-10.snap deleted file mode 100644 index 00f2730..0000000 --- a/src/snapshots/eww_config__test-10.snap +++ /dev/null @@ -1,13 +0,0 @@ ---- -source: src/lib.rs -expression: "p.parse(0, lexer::Lexer::new(\"(lol😄 1)\"))" - ---- -Ok( - List<0..11>( - [ - Symbol<1..8>(lol😄), - Value<9..10>(1), - ], - ), -) diff --git a/src/snapshots/eww_config__test-11.snap b/src/snapshots/eww_config__test-11.snap deleted file mode 100644 index 5b02964..0000000 --- a/src/snapshots/eww_config__test-11.snap +++ /dev/null @@ -1,13 +0,0 @@ ---- -source: src/lib.rs -expression: "p.parse(0, lexer::Lexer::new(r#\"(test \"hi\")\"#))" - ---- -Ok( - List<0..11>( - [ - Symbol<1..5>(test), - Value<6..10>(hi), - ], - ), -) diff --git a/src/snapshots/eww_config__test-12.snap b/src/snapshots/eww_config__test-12.snap deleted file mode 100644 index b60c2dd..0000000 --- a/src/snapshots/eww_config__test-12.snap +++ /dev/null @@ -1,13 +0,0 @@ ---- -source: src/lib.rs -expression: "p.parse(0, lexer::Lexer::new(r#\"(test \"h\\\"i\")\"#))" - ---- -Ok( - List<0..13>( - [ - Symbol<1..5>(test), - Value<6..12>(h\"i), - ], - ), -) diff --git a/src/snapshots/eww_config__test-13.snap b/src/snapshots/eww_config__test-13.snap deleted file mode 100644 index e103f93..0000000 --- a/src/snapshots/eww_config__test-13.snap +++ /dev/null @@ -1,13 +0,0 @@ ---- -source: src/lib.rs -expression: "p.parse(0, lexer::Lexer::new(r#\"(test \" hi \")\"#))" - ---- -Ok( - List<0..13>( - [ - Symbol<1..5>(test), - Value<6..12>( hi ), - ], - ), -) diff --git a/src/snapshots/eww_config__test-14.snap b/src/snapshots/eww_config__test-14.snap deleted file mode 100644 index 9271dab..0000000 --- a/src/snapshots/eww_config__test-14.snap +++ /dev/null @@ -1,25 +0,0 @@ ---- -source: src/lib.rs -expression: "p.parse(0, lexer::Lexer::new(\"(+ (1 2 (* 2 5)))\"))" - ---- -Ok( - List<0..17>( - [ - Symbol<1..2>(+), - List<3..16>( - [ - Value<4..5>(1), - Value<6..7>(2), - List<8..15>( - [ - Symbol<9..10>(*), - Value<11..12>(2), - Value<13..14>(5), - ], - ), - ], - ), - ], - ), -) diff --git a/src/snapshots/eww_config__test-15.snap b/src/snapshots/eww_config__test-15.snap deleted file mode 100644 index c439fd1..0000000 --- a/src/snapshots/eww_config__test-15.snap +++ /dev/null @@ -1,8 +0,0 @@ ---- -source: src/lib.rs -expression: "p.parse(0, lexer::Lexer::new(r#\"; test\"#))" - ---- -Ok( - Comment<0..6>, -) diff --git a/src/snapshots/eww_config__test-16.snap b/src/snapshots/eww_config__test-16.snap deleted file mode 100644 index 30c9111..0000000 --- a/src/snapshots/eww_config__test-16.snap +++ /dev/null @@ -1,15 +0,0 @@ ---- -source: src/lib.rs -expression: "p.parse(0, lexer::Lexer::new(r#\"(f arg ; test\n arg2)\"#))" - ---- -Ok( - List<0..27>( - [ - Symbol<1..2>(f), - Symbol<3..6>(arg), - Comment<7..13>, - Symbol<22..26>(arg2), - ], - ), -) diff --git a/src/snapshots/eww_config__test-17.snap b/src/snapshots/eww_config__test-17.snap deleted file mode 100644 index 17db7ac..0000000 --- a/src/snapshots/eww_config__test-17.snap +++ /dev/null @@ -1,8 +0,0 @@ ---- -source: src/lib.rs -expression: "p.parse(0, lexer::Lexer::new(\"\\\"h\\\\\\\"i\\\"\"))" - ---- -Ok( - Value<0..6>(h\"i), -) diff --git a/src/snapshots/eww_config__test-2.snap b/src/snapshots/eww_config__test-2.snap deleted file mode 100644 index c99806f..0000000 --- a/src/snapshots/eww_config__test-2.snap +++ /dev/null @@ -1,12 +0,0 @@ ---- -source: src/lib.rs -expression: "p.parse(0, lexer::Lexer::new(\"(12)\"))" - ---- -Ok( - List<0..4>( - [ - Value<1..3>(12), - ], - ), -) diff --git a/src/snapshots/eww_config__test-3.snap b/src/snapshots/eww_config__test-3.snap deleted file mode 100644 index 09d221f..0000000 --- a/src/snapshots/eww_config__test-3.snap +++ /dev/null @@ -1,8 +0,0 @@ ---- -source: src/lib.rs -expression: "p.parse(0, lexer::Lexer::new(\"1.2\"))" - ---- -Ok( - Value<0..3>(1.2), -) diff --git a/src/snapshots/eww_config__test-4.snap b/src/snapshots/eww_config__test-4.snap deleted file mode 100644 index f29bac3..0000000 --- a/src/snapshots/eww_config__test-4.snap +++ /dev/null @@ -1,8 +0,0 @@ ---- -source: src/lib.rs -expression: "p.parse(0, lexer::Lexer::new(\"-1.2\"))" - ---- -Ok( - Value<0..4>(-1.2), -) diff --git a/src/snapshots/eww_config__test-5.snap b/src/snapshots/eww_config__test-5.snap deleted file mode 100644 index 0c3ad08..0000000 --- a/src/snapshots/eww_config__test-5.snap +++ /dev/null @@ -1,13 +0,0 @@ ---- -source: src/lib.rs -expression: "p.parse(0, lexer::Lexer::new(\"(1 2)\"))" - ---- -Ok( - List<0..5>( - [ - Value<1..2>(1), - Value<3..4>(2), - ], - ), -) diff --git a/src/snapshots/eww_config__test-6.snap b/src/snapshots/eww_config__test-6.snap deleted file mode 100644 index b3b4ab8..0000000 --- a/src/snapshots/eww_config__test-6.snap +++ /dev/null @@ -1,14 +0,0 @@ ---- -source: src/lib.rs -expression: "p.parse(0, lexer::Lexer::new(\"(1 :foo 1)\"))" - ---- -Ok( - List<0..10>( - [ - Value<1..2>(1), - Number<3..7>(:foo), - Value<8..9>(1), - ], - ), -) diff --git a/src/snapshots/eww_config__test-7.snap b/src/snapshots/eww_config__test-7.snap deleted file mode 100644 index de5b5c0..0000000 --- a/src/snapshots/eww_config__test-7.snap +++ /dev/null @@ -1,13 +0,0 @@ ---- -source: src/lib.rs -expression: "p.parse(0, lexer::Lexer::new(\"(:foo 1)\"))" - ---- -Ok( - List<0..8>( - [ - Number<1..5>(:foo), - Value<6..7>(1), - ], - ), -) diff --git a/src/snapshots/eww_config__test-8.snap b/src/snapshots/eww_config__test-8.snap deleted file mode 100644 index c149453..0000000 --- a/src/snapshots/eww_config__test-8.snap +++ /dev/null @@ -1,13 +0,0 @@ ---- -source: src/lib.rs -expression: "p.parse(0, lexer::Lexer::new(\"(:foo->: 1)\"))" - ---- -Ok( - List<0..11>( - [ - Number<1..8>(:foo->:), - Value<9..10>(1), - ], - ), -) diff --git a/src/snapshots/eww_config__test-9.snap b/src/snapshots/eww_config__test-9.snap deleted file mode 100644 index cd26a6e..0000000 --- a/src/snapshots/eww_config__test-9.snap +++ /dev/null @@ -1,13 +0,0 @@ ---- -source: src/lib.rs -expression: "p.parse(0, lexer::Lexer::new(\"(foo 1)\"))" - ---- -Ok( - List<0..7>( - [ - Symbol<1..4>(foo), - Value<5..6>(1), - ], - ), -) diff --git a/src/snapshots/eww_config__test.snap b/src/snapshots/eww_config__test.snap deleted file mode 100644 index 034b165..0000000 --- a/src/snapshots/eww_config__test.snap +++ /dev/null @@ -1,8 +0,0 @@ ---- -source: src/lib.rs -expression: "p.parse(0, lexer::Lexer::new(\"1\"))" - ---- -Ok( - Value<0..1>(1), -) From 497f781d0d9cb6e3b2aa8052cb94f58230cc5f30 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Mon, 19 Jul 2021 14:19:46 +0200 Subject: [PATCH 046/137] Refactor --- examples/errors.rs | 27 ++++++++++++------------- examples/validation.rs | 2 +- src/config/config.rs | 2 +- src/config/config_parse_error.rs | 2 ++ src/config/mod.rs | 1 + src/config/script_var_definition.rs | 6 +++--- src/config/test.rs | 2 +- src/config/validate.rs | 2 +- src/config/var_definition.rs | 4 ++-- src/config/widget_definition.rs | 2 +- src/config/widget_use.rs | 4 ++-- src/parser/ast.rs | 28 ++++++++++++++++---------- src/parser/{element.rs => from_ast.rs} | 28 +++----------------------- src/parser/mod.rs | 2 +- src/parser/parser.lalrpop | 4 ++-- 15 files changed, 51 insertions(+), 65 deletions(-) create mode 100644 src/config/config_parse_error.rs rename src/parser/{element.rs => from_ast.rs} (65%) diff --git a/examples/errors.rs b/examples/errors.rs index 782e5c0..f86bf68 100644 --- a/examples/errors.rs +++ b/examples/errors.rs @@ -1,6 +1,6 @@ use eww_config::{ format_diagnostic::ToDiagnostic, - parser::{ast::*, element::FromAst}, + parser::{ast::*, from_ast::FromAst}, }; fn main() { @@ -15,17 +15,16 @@ fn main() { let file_id = files.add("foo.eww", input); let ast = eww_config::parser::parse_string(file_id, input); - match ast.and_then(eww_config::parser::element::Element::::from_ast) { - Ok(ast) => { - println!("{:?}", ast); - } - Err(err) => { - dbg!(&err); - let diag = err.to_diagnostic(); - use codespan_reporting::term; - let config = term::Config::default(); - let mut writer = term::termcolor::StandardStream::stderr(term::termcolor::ColorChoice::Always); - term::emit(&mut writer, &config, &files, &diag).unwrap(); - } - } + // match ast.and_then(eww_config::parser::from_ast::Element::::from_ast) { + // Ok(ast) => { + // println!("{:?}", ast); + //} + // Err(err) => { + // dbg!(&err); + // let diag = err.to_diagnostic(); + // use codespan_reporting::term; + // let config = term::Config::default(); + // let mut writer = term::termcolor::StandardStream::stderr(term::termcolor::ColorChoice::Always); + // term::emit(&mut writer, &config, &files, &diag).unwrap(); + //} } diff --git a/examples/validation.rs b/examples/validation.rs index 9361975..f2cca38 100644 --- a/examples/validation.rs +++ b/examples/validation.rs @@ -2,7 +2,7 @@ use eww_config::{ config::{widget_definition::WidgetDefinition, widget_use::WidgetUse, *}, error::AstError, format_diagnostic::ToDiagnostic, - parser::element::FromAst, + parser::from_ast::FromAst, }; fn main() { diff --git a/src/config/config.rs b/src/config/config.rs index e34e766..17085b5 100644 --- a/src/config/config.rs +++ b/src/config/config.rs @@ -11,7 +11,7 @@ use crate::{ error::{AstError, AstResult, OptionAstErrorExt}, parser::{ ast::{Ast, AstIterator, Span}, - element::{Element, FromAst, FromAstElementContent}, + from_ast::{FromAst, FromAstElementContent}, }, spanned, value::{AttrName, VarName}, diff --git a/src/config/config_parse_error.rs b/src/config/config_parse_error.rs new file mode 100644 index 0000000..9a99b01 --- /dev/null +++ b/src/config/config_parse_error.rs @@ -0,0 +1,2 @@ +#[derive(Debug, thiserror::Error)] +pub enum ConfigParseError {} diff --git a/src/config/mod.rs b/src/config/mod.rs index 42c678c..39e20af 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,4 +1,5 @@ mod config; +pub mod config_parse_error; pub mod script_var_definition; #[cfg(test)] mod test; diff --git a/src/config/script_var_definition.rs b/src/config/script_var_definition.rs index 5801ca2..d245ced 100644 --- a/src/config/script_var_definition.rs +++ b/src/config/script_var_definition.rs @@ -6,7 +6,7 @@ use crate::{ error::{AstError, AstResult}, parser::{ ast::{Ast, AstIterator, Span}, - element::{Element, FromAst, FromAstElementContent}, + from_ast::{FromAst, FromAstElementContent}, }, spanned, value::{AttrName, VarName}, @@ -51,7 +51,7 @@ impl FromAstElementContent for PollScriptVar { let attrs: HashMap = iter.expect_key_values()?; let interval = attrs.get("interval").unwrap(); let interval = crate::util::parse_duration(interval).map_err(|e| AstError::Other(Some(span), e.into()))?; - let (_, script) = iter.expect_value()?; + let (_, script) = iter.expect_literal()?; Ok(Self { name: VarName(name), command: VarSource::Shell(script.to_string()), interval }) } } @@ -68,7 +68,7 @@ impl FromAstElementContent for TailScriptVar { fn from_tail>(span: Span, mut iter: AstIterator) -> AstResult { let (_, name) = iter.expect_symbol()?; - let (_, script) = iter.expect_value()?; + let (_, script) = iter.expect_literal()?; Ok(Self { name: VarName(name), command: script.to_string() }) } } diff --git a/src/config/test.rs b/src/config/test.rs index 9d9c454..593a683 100644 --- a/src/config/test.rs +++ b/src/config/test.rs @@ -3,7 +3,7 @@ use crate::{ parser::{ self, ast::{Ast, Span}, - element::FromAst, + from_ast::FromAst, lexer::Lexer, }, }; diff --git a/src/config/validate.rs b/src/config/validate.rs index d35c0cc..dc9678b 100644 --- a/src/config/validate.rs +++ b/src/config/validate.rs @@ -6,7 +6,7 @@ use crate::{ error::AstResult, parser::{ ast::{Ast, AstIterator, Span}, - element::{Element, FromAst}, + from_ast::FromAst, }, spanned, value::{AttrName, VarName}, diff --git a/src/config/var_definition.rs b/src/config/var_definition.rs index 3002980..37948cd 100644 --- a/src/config/var_definition.rs +++ b/src/config/var_definition.rs @@ -6,7 +6,7 @@ use crate::{ error::AstResult, parser::{ ast::{Ast, AstIterator, Span}, - element::{Element, FromAst, FromAstElementContent}, + from_ast::{FromAst, FromAstElementContent}, }, spanned, value::{AttrName, VarName}, @@ -26,7 +26,7 @@ impl FromAstElementContent for VarDefinition { fn from_tail>(span: Span, mut iter: AstIterator) -> AstResult { let (_, name) = iter.expect_symbol()?; - let (_, initial_value) = iter.expect_value()?; + let (_, initial_value) = iter.expect_literal()?; Ok(Self { name: VarName(name), initial_value, span }) } } diff --git a/src/config/widget_definition.rs b/src/config/widget_definition.rs index bb3764f..518286d 100644 --- a/src/config/widget_definition.rs +++ b/src/config/widget_definition.rs @@ -6,7 +6,7 @@ use crate::{ error::AstResult, parser::{ ast::{Ast, AstIterator, Span}, - element::{Element, FromAst, FromAstElementContent}, + from_ast::{FromAst, FromAstElementContent}, }, spanned, value::{AttrName, VarName}, diff --git a/src/config/widget_use.rs b/src/config/widget_use.rs index be29039..516b862 100644 --- a/src/config/widget_use.rs +++ b/src/config/widget_use.rs @@ -6,7 +6,7 @@ use crate::{ error::AstResult, parser::{ ast::{Ast, AstIterator, Span}, - element::{Element, FromAst}, + from_ast::FromAst, }, spanned, value::AttrName, @@ -23,7 +23,7 @@ impl FromAst for WidgetUse { fn from_ast(e: Ast) -> AstResult { let span = e.span(); spanned!(e.span(), { - if let Ok(text) = e.as_value_ref() { + if let Ok(text) = e.as_literal_ref() { Self { name: "text".to_string(), attrs: maplit::hashmap! { AttrName("text".to_string()) => SimplExpr::Literal(span.into(), text.clone()) }, diff --git a/src/parser/ast.rs b/src/parser/ast.rs index ed10d76..af1b513 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -4,7 +4,7 @@ use std::collections::HashMap; use std::fmt::Display; -use super::element::FromAst; +use super::from_ast::FromAst; use crate::error::{AstError, AstResult, OptionAstErrorExt}; #[derive(Eq, PartialEq, Clone, Copy, serde::Serialize)] @@ -34,9 +34,11 @@ pub enum AstType { Array, Keyword, Symbol, - Value, + Literal, SimplExpr, Comment, + // A value that could be used as a [SimplExpr] + IntoPrimitive, } impl Display for AstType { @@ -51,7 +53,7 @@ pub enum Ast { Array(Span, Vec), Keyword(Span, String), Symbol(Span, String), - Value(Span, DynVal), + Literal(Span, DynVal), SimplExpr(Span, SimplExpr), Comment(Span), } @@ -75,7 +77,7 @@ macro_rules! as_func { } impl Ast { - as_func!(AstType::Value, as_value as_value_ref = Ast::Value(_, x) => x); + as_func!(AstType::Literal, as_literal as_literal_ref = Ast::Literal(_, x) => x); as_func!(AstType::Symbol, as_symbol as_symbol_ref = Ast::Symbol(_, x) => x); @@ -89,7 +91,7 @@ impl Ast { Ast::Array(..) => AstType::Array, Ast::Keyword(..) => AstType::Keyword, Ast::Symbol(..) => AstType::Symbol, - Ast::Value(..) => AstType::Value, + Ast::Literal(..) => AstType::Literal, Ast::SimplExpr(..) => AstType::SimplExpr, Ast::Comment(_) => AstType::Comment, } @@ -101,16 +103,20 @@ impl Ast { Ast::Array(span, _) => *span, Ast::Keyword(span, _) => *span, Ast::Symbol(span, _) => *span, - Ast::Value(span, _) => *span, + Ast::Literal(span, _) => *span, Ast::SimplExpr(span, _) => *span, Ast::Comment(span) => *span, } } - pub fn first_list_elem(&self) -> Option<&Ast> { + pub fn as_simplexpr(self) -> AstResult { match self { - Ast::List(_, list) => list.first(), - _ => None, + // do I do this? + // Ast::Array(_, _) => todo!(), + // Ast::Symbol(_, _) => todo!(), + Ast::Literal(span, x) => Ok(SimplExpr::Literal(span.into(), x)), + Ast::SimplExpr(span, x) => Ok(x), + _ => Err(AstError::WrongExprType(Some(self.span()), AstType::IntoPrimitive, self.expr_type())), } } } @@ -123,7 +129,7 @@ impl std::fmt::Display for Ast { Array(_, x) => write!(f, "({})", x.iter().map(|e| format!("{}", e)).join(" ")), Keyword(_, x) => write!(f, ":{}", x), Symbol(_, x) => write!(f, "{}", x), - Value(_, x) => write!(f, "\"{}\"", x), + Literal(_, x) => write!(f, "\"{}\"", x), SimplExpr(_, x) => write!(f, "{{{}}}", x), Comment(_) => write!(f, ""), } @@ -170,7 +176,7 @@ macro_rules! return_or_put_back { impl> AstIterator { return_or_put_back!(expect_symbol, AstType::Symbol, (Span, String) = Ast::Symbol(span, x) => (span, x)); - return_or_put_back!(expect_value, AstType::Value, (Span, DynVal) = Ast::Value(span, x) => (span, x)); + return_or_put_back!(expect_literal, AstType::Literal, (Span, DynVal) = Ast::Literal(span, x) => (span, x)); return_or_put_back!(expect_list, AstType::List, (Span, Vec) = Ast::List(span, x) => (span, x)); diff --git a/src/parser/element.rs b/src/parser/from_ast.rs similarity index 65% rename from src/parser/element.rs rename to src/parser/from_ast.rs index 9bbff0a..8404439 100644 --- a/src/parser/element.rs +++ b/src/parser/from_ast.rs @@ -1,5 +1,5 @@ use super::ast::{Ast, AstIterator, AstType, Span}; -use crate::{error::*, parser, spanned, value::AttrName}; +use crate::{error::*, parser, spanned, util, value::AttrName}; use itertools::Itertools; use simplexpr::{ast::SimplExpr, dynval::DynVal}; use std::{ @@ -20,7 +20,7 @@ impl FromAst for Ast { impl FromAst for String { fn from_ast(e: Ast) -> AstResult { - Ok(e.as_value()?.as_string().unwrap()) + Ok(e.as_literal()?.as_string().unwrap()) } } @@ -50,31 +50,9 @@ impl FromAst for SimplExpr { fn from_ast(e: Ast) -> AstResult { match e { Ast::Symbol(span, x) => Ok(SimplExpr::VarRef(span.into(), x)), - Ast::Value(span, x) => Ok(SimplExpr::Literal(span.into(), x)), + Ast::Literal(span, x) => Ok(SimplExpr::Literal(span.into(), x)), Ast::SimplExpr(span, x) => Ok(x), _ => Err(AstError::NotAValue(Some(e.span()), e.expr_type())), } } } - -#[derive(Debug, Eq, PartialEq)] -pub struct Element { - name: String, - attrs: HashMap, - children: Vec, - span: Span, -} - -impl FromAst for Element { - fn from_ast(e: Ast) -> AstResult { - let span = e.span(); - spanned!(e.span(), { - let list = e.as_list()?; - let mut iter = AstIterator::new(list.into_iter()); - let (_, name) = iter.expect_symbol()?; - let attrs = iter.expect_key_values()?.into_iter().map(|(k, v)| (AttrName(k), v)).collect(); - let children = iter.map(C::from_ast).collect::>>()?; - Element { span, name, attrs, children } - }) - } -} diff --git a/src/parser/mod.rs b/src/parser/mod.rs index e8ed750..d0a99d2 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -8,7 +8,7 @@ use std::{fmt::Display, ops::Deref}; use itertools::Itertools; pub mod ast; -pub mod element; +pub mod from_ast; pub(crate) mod lexer; pub(crate) mod parse_error; diff --git a/src/parser/parser.lalrpop b/src/parser/parser.lalrpop index 7ecc91b..110c749 100644 --- a/src/parser/parser.lalrpop +++ b/src/parser/parser.lalrpop @@ -36,14 +36,14 @@ pub Ast: Ast = { => Ast::SimplExpr(Span(l, r, file_id), expr), => x, => x, - => Ast::Value(Span(l, r, file_id), x.into()), + => Ast::Literal(Span(l, r, file_id), x.into()), "comment" => Ast::Comment(Span(l, r, file_id)), }; Keyword: Ast = => Ast::Keyword(Span(l, r, file_id), x[1..].to_string()); Symbol: Ast = => Ast::Symbol(Span(l, r, file_id), x.to_string()); -Value: String = { +Literal: String = { => <>, => <>, => <>, From 12ded2f726013b4487474fcf1ab08cd442127c27 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Tue, 20 Jul 2021 15:56:47 +0200 Subject: [PATCH 047/137] Refactor attribute parsing --- src/config/attributes.rs | 109 ++++++++++++++++++++++++++++ src/config/config.rs | 3 +- src/config/mod.rs | 1 + src/config/script_var_definition.rs | 6 +- src/config/validate.rs | 2 +- src/config/widget_use.rs | 21 ++++-- src/error.rs | 25 ++++++- src/parser/ast.rs | 80 +++++++++++++++----- src/parser/from_ast.rs | 3 +- 9 files changed, 219 insertions(+), 31 deletions(-) create mode 100644 src/config/attributes.rs diff --git a/src/config/attributes.rs b/src/config/attributes.rs new file mode 100644 index 0000000..8a4154a --- /dev/null +++ b/src/config/attributes.rs @@ -0,0 +1,109 @@ +use std::{ + collections::HashMap, + convert::{TryFrom, TryInto}, +}; + +use simplexpr::{dynval::DynVal, eval::EvalError, SimplExpr}; + +use crate::{ + parser::{ + ast::{Ast, Span}, + from_ast::FromAst, + }, + value::AttrName, +}; + +#[derive(Debug, thiserror::Error)] +pub enum AttrError { + #[error("Missing required attribute {0}")] + MissingRequiredAttr(Span, AttrName), + + #[error("Failed to parse attribute value {0} in this context")] + AttrTypeError(Span, AttrName), + + #[error("{1}")] + EvaluationError(Span, EvalError), +} + +impl AttrError { + pub fn span(&self) -> Span { + match self { + AttrError::MissingRequiredAttr(span, _) => *span, + AttrError::AttrTypeError(span, _) => *span, + AttrError::EvaluationError(span, _) => *span, + } + } +} + +#[derive(Debug)] +pub struct UnusedAttrs { + definition_span: Span, + attrs: Vec<(Span, AttrName)>, +} + +#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)] +pub struct AttrEntry { + pub key_span: Span, + pub value: SimplExpr, +} + +impl AttrEntry { + pub fn new(key_span: Span, value: SimplExpr) -> AttrEntry { + AttrEntry { key_span, value } + } +} + +#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize)] +pub struct Attributes { + pub span: Span, + pub attrs: HashMap, +} + +impl Attributes { + pub fn new(span: Span, attrs: HashMap) -> Self { + Attributes { span, attrs } + } + + pub fn eval_required>(&mut self, key: &str) -> Result { + let key = AttrName(key.to_string()); + match self.attrs.remove(&key) { + Some(AttrEntry { key_span, value }) => { + let value_span = value.span(); + let dynval = value.eval_no_vars().map_err(|err| AttrError::EvaluationError(value_span.into(), err))?; + T::try_from(dynval).map_err(|_| AttrError::AttrTypeError(value_span.into(), key.clone())) + } + None => Err(AttrError::MissingRequiredAttr(self.span, key.clone())), + } + } + + // pub fn parse_required>(&mut self, key: &str) -> Result { + // let key = AttrName(key.to_string()); + // match self.attrs.remove(&key) { + // Some(value) => match value.value.try_into() { + // Ok(value) => Ok(value), + // Err(_) => Err(AttrError::AttrTypeError(value.value.span().into(), key.clone())), + // }, + // None => Err(AttrError::MissingRequiredAttr(self.span, key.clone())), + // } + // } + // + // pub fn parse_optional>(&mut self, key: &str) -> Result, AttrError> { + // let key = AttrName(key.to_string()); + // match self.attrs.remove(&key) { + // Some(value) => match value.value.try_into() { + // Ok(value) => Ok(Some(value)), + // Err(_) => Err(AttrError::AttrTypeError(value.value.span().into(), key.clone())), + // }, + // None => Ok(None), + // } + // } + + /// Consumes the attributes to return a list of unused attributes which may be used to emit a warning. + /// TODO actually use this and implement warnings,... lol + pub fn get_unused(self, definition_span: Span) -> UnusedAttrs { + UnusedAttrs { + definition_span, + attrs: self.attrs.into_iter().map(|(k, v)| (v.key_span.to(v.value.span().into()), k)).collect(), + } + } +} diff --git a/src/config/config.rs b/src/config/config.rs index 17085b5..b86b28f 100644 --- a/src/config/config.rs +++ b/src/config/config.rs @@ -27,8 +27,7 @@ impl FromAst for TopLevel { fn from_ast(e: Ast) -> AstResult { let span = e.span(); spanned!(e.span(), { - let list = e.as_list()?; - let mut iter = AstIterator::new(list.into_iter()); + let mut iter = e.try_ast_iter()?; let (sym_span, element_name) = iter.expect_symbol()?; match element_name.as_str() { x if x == WidgetDefinition::get_element_name() => { diff --git a/src/config/mod.rs b/src/config/mod.rs index 39e20af..3c51d23 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,3 +1,4 @@ +pub mod attributes; mod config; pub mod config_parse_error; pub mod script_var_definition; diff --git a/src/config/script_var_definition.rs b/src/config/script_var_definition.rs index d245ced..9d6d907 100644 --- a/src/config/script_var_definition.rs +++ b/src/config/script_var_definition.rs @@ -48,9 +48,9 @@ impl FromAstElementContent for PollScriptVar { fn from_tail>(span: Span, mut iter: AstIterator) -> AstResult { let (_, name) = iter.expect_symbol()?; - let attrs: HashMap = iter.expect_key_values()?; - let interval = attrs.get("interval").unwrap(); - let interval = crate::util::parse_duration(interval).map_err(|e| AstError::Other(Some(span), e.into()))?; + let mut attrs = iter.expect_key_values()?; + let interval: String = attrs.eval_required("interval")?; + let interval = crate::util::parse_duration(&interval).map_err(|e| AstError::Other(Some(span), e.into()))?; let (_, script) = iter.expect_literal()?; Ok(Self { name: VarName(name), command: VarSource::Shell(script.to_string()), interval }) } diff --git a/src/config/validate.rs b/src/config/validate.rs index dc9678b..99b049b 100644 --- a/src/config/validate.rs +++ b/src/config/validate.rs @@ -26,7 +26,7 @@ pub enum ValidationError { pub fn validate(defs: &HashMap, content: &WidgetUse) -> Result<(), ValidationError> { if let Some(def) = defs.get(&content.name) { for expected in def.expected_args.iter() { - if !content.attrs.contains_key(expected) { + if !content.attrs.attrs.contains_key(expected) { return Err(ValidationError::MissingAttr { widget_name: def.name.to_string(), arg_name: expected.clone(), diff --git a/src/config/widget_use.rs b/src/config/widget_use.rs index 516b862..2c5e314 100644 --- a/src/config/widget_use.rs +++ b/src/config/widget_use.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; use simplexpr::SimplExpr; use crate::{ + config::attributes::AttrEntry, error::AstResult, parser::{ ast::{Ast, AstIterator, Span}, @@ -11,10 +12,13 @@ use crate::{ spanned, value::AttrName, }; + +use super::attributes::Attributes; + #[derive(Debug, PartialEq, Eq, Clone, serde::Serialize)] pub struct WidgetUse { pub name: String, - pub attrs: HashMap, + pub attrs: Attributes, pub children: Vec, pub span: Span, } @@ -26,15 +30,22 @@ impl FromAst for WidgetUse { if let Ok(text) = e.as_literal_ref() { Self { name: "text".to_string(), - attrs: maplit::hashmap! { AttrName("text".to_string()) => SimplExpr::Literal(span.into(), text.clone()) }, + attrs: Attributes::new( + span.into(), + maplit::hashmap! { + AttrName("text".to_string()) => AttrEntry::new( + span.into(), + SimplExpr::Literal(span.into(), text.clone()) + ) + }, + ), children: Vec::new(), span, } } else { - let list = e.as_list()?; - let mut iter = AstIterator::new(list.into_iter()); + let mut iter = e.try_ast_iter()?; let (_, name) = iter.expect_symbol()?; - let attrs = iter.expect_key_values()?.into_iter().map(|(k, v)| (AttrName(k), v)).collect(); + let attrs = iter.expect_key_values()?; let children = iter.map(WidgetUse::from_ast).collect::>>()?; Self { name, attrs, children, span } } diff --git a/src/error.rs b/src/error.rs index 8b6fcf2..53126f9 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,5 +1,5 @@ use crate::{ - config::validate::ValidationError, + config::{attributes::AttrError, validate::ValidationError}, parser::{ ast::{Ast, AstType, Span}, lexer, parse_error, @@ -22,9 +22,20 @@ pub enum AstError { NotAValue(Option, AstType), #[error("Expected element {1}, but read {2}")] MismatchedElementName(Option, String, String), + #[error("{1}")] Other(Option, Box), + #[error(transparent)] + AttrError(#[from] AttrError), + + //#[error("{msg}: {source}")] + // Context { + // span: Option, + //#[source] + // source: Box, + // msg: String, + //}, #[error(transparent)] ValidationError(#[from] ValidationError), @@ -40,7 +51,9 @@ impl AstError { AstError::WrongExprType(span, ..) => *span, AstError::NotAValue(span, ..) => *span, AstError::MismatchedElementName(span, ..) => *span, + AstError::AttrError(err) => Some(err.span()), AstError::Other(span, ..) => *span, + // AstError::Context { span, .. } => *span, AstError::ValidationError(error) => None, // TODO none here is stupid AstError::ParseError { file_id, source } => file_id.and_then(|id| get_parse_error_span(id, source)), } @@ -80,6 +93,7 @@ pub fn spanned(span: Span, err: impl Into) -> AstError { MissingNode(None) => MissingNode(Some(span)), NotAValue(None, x) => NotAValue(Some(span), x), MismatchedElementName(None, x, y) => MismatchedElementName(Some(span), x, y), + // Context { span: None, source, msg } => Context { span: Some(span), source, msg }, Other(None, x) => Other(Some(span), x), x => x, } @@ -98,12 +112,21 @@ pub trait AstResultExt { fn at(self, span: Span) -> Result; } +pub trait Context { + fn context(self, span: Span, msg: String) -> Result; +} + impl> AstResultExt for Result { fn at(self, span: Span) -> Result { self.map_err(|err| spanned(span, err)) } } +// impl Context for Result { +// fn context(self, span: Span, msg: String) -> Result { +// self.map_err(|x| AstError::Context { msg, span: Some(span), source: Box::new(x) }) +//} + #[macro_export] macro_rules! spanned { ($span:expr, $block:expr) => {{ diff --git a/src/parser/ast.rs b/src/parser/ast.rs index af1b513..0861ab7 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -5,16 +5,45 @@ use std::collections::HashMap; use std::fmt::Display; use super::from_ast::FromAst; -use crate::error::{AstError, AstResult, OptionAstErrorExt}; +use crate::{ + config::attributes::{AttrEntry, Attributes}, + error::{AstError, AstResult, OptionAstErrorExt}, + value::AttrName, +}; #[derive(Eq, PartialEq, Clone, Copy, serde::Serialize)] pub struct Span(pub usize, pub usize, pub usize); +impl Span { + /// Get the span that includes this and the other span completely. + /// Will panic if the spans are from different file_ids. + pub fn to(mut self, other: Span) -> Self { + assert!(other.2 == self.2); + self.1 = other.1; + self + } + + pub fn ending_at(mut self, end: usize) -> Self { + self.1 = end; + self + } + + pub fn with_length(mut self, end: usize) -> Self { + self.1 = self.0; + self + } +} + impl Into for Span { fn into(self) -> simplexpr::Span { simplexpr::Span(self.0, self.1, self.2) } } +impl From for Span { + fn from(x: simplexpr::Span) -> Span { + Span(x.0, x.1, x.2) + } +} impl std::fmt::Display for Span { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -47,7 +76,7 @@ impl Display for AstType { } } -#[derive(PartialEq, Eq, Clone)] +#[derive(PartialEq, Eq, Clone, serde::Serialize)] pub enum Ast { List(Span, Vec), Array(Span, Vec), @@ -119,6 +148,12 @@ impl Ast { _ => Err(AstError::WrongExprType(Some(self.span()), AstType::IntoPrimitive, self.expr_type())), } } + + pub fn try_ast_iter(self) -> AstResult>> { + let span = self.span(); + let list = self.as_list()?; + Ok(AstIterator::new(span, list.into_iter())) + } } impl std::fmt::Display for Ast { @@ -152,6 +187,7 @@ impl std::fmt::Debug for Ast { } pub struct AstIterator> { + remaining_span: Span, iter: itertools::PutBack, } @@ -160,7 +196,11 @@ macro_rules! return_or_put_back { pub fn $name(&mut self) -> AstResult<$t> { let expr_type = $expr_type; match self.next() { - Some($p) => Ok($ret), + Some($p) => { + let (span, value) = $ret; + self.remaining_span.1 = span.1; + Ok((span, value)) + } Some(other) => { let span = other.span(); let actual_type = other.expr_type(); @@ -182,16 +222,16 @@ impl> AstIterator { return_or_put_back!(expect_array, AstType::Array, (Span, Vec) = Ast::Array(span, x) => (span, x)); - pub fn new(iter: I) -> Self { - AstIterator { iter: itertools::put_back(iter) } + pub fn new(span: Span, iter: I) -> Self { + AstIterator { remaining_span: span, iter: itertools::put_back(iter) } } pub fn expect_any(&mut self) -> AstResult { self.iter.next().or_missing().and_then(T::from_ast) } - pub fn expect_key_values(&mut self) -> AstResult> { - parse_key_values(&mut self.iter) + pub fn expect_key_values(&mut self) -> AstResult { + parse_key_values(self) } } @@ -203,25 +243,31 @@ impl> Iterator for AstIterator { } } -/// Parse consecutive `:keyword value` pairs from an expression iterator into a HashMap. Transforms the keys using the FromExpr trait. -fn parse_key_values>(iter: &mut itertools::PutBack) -> AstResult> { +/// Parse consecutive `:keyword value` pairs from an expression iterator into an [Attributes]. +fn parse_key_values(iter: &mut AstIterator>) -> AstResult { let mut data = HashMap::new(); + let mut attrs_span = Span(iter.remaining_span.0, iter.remaining_span.0, iter.remaining_span.1); loop { match iter.next() { - Some(Ast::Keyword(span, kw)) => match iter.next() { + Some(Ast::Keyword(key_span, kw)) => match iter.next() { Some(value) => { - data.insert(kw, T::from_ast(value)?); + attrs_span.1 = iter.remaining_span.0; + let attr_value = AttrEntry { key_span, value: value.as_simplexpr()? }; + data.insert(AttrName(kw), attr_value); } None => { - iter.put_back(Ast::Keyword(span, kw)); - return Ok(data); + iter.iter.put_back(Ast::Keyword(key_span, kw)); + attrs_span.1 = iter.remaining_span.0; + return Ok(Attributes::new(attrs_span, data)); } }, - Some(expr) => { - iter.put_back(expr); - return Ok(data); + next => { + if let Some(expr) = next { + iter.iter.put_back(expr); + } + attrs_span.1 = iter.remaining_span.0; + return Ok(Attributes::new(attrs_span, data)); } - None => return Ok(data), } } } diff --git a/src/parser/from_ast.rs b/src/parser/from_ast.rs index 8404439..21ab4bd 100644 --- a/src/parser/from_ast.rs +++ b/src/parser/from_ast.rs @@ -35,8 +35,7 @@ impl FromAst for T { fn from_ast(e: Ast) -> AstResult { let span = e.span(); spanned!(e.span(), { - let list = e.as_list()?; - let mut iter = AstIterator::new(list.into_iter()); + let mut iter = e.try_ast_iter()?; let (_, element_name) = iter.expect_symbol()?; if Self::get_element_name() != element_name { return Err(AstError::MismatchedElementName(Some(span), Self::get_element_name().to_string(), element_name)); From 49bd2c667895c9b44e86d302ba32fafd8a0d67f1 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Tue, 20 Jul 2021 17:06:05 +0200 Subject: [PATCH 048/137] Start implementing window definition --- Cargo.toml | 1 + src/config/attributes.rs | 12 +++ src/config/mod.rs | 1 + src/config/window_definition.rs | 150 ++++++++++++++++++++++++++++++++ 4 files changed, 164 insertions(+) create mode 100644 src/config/window_definition.rs diff --git a/Cargo.toml b/Cargo.toml index 8b35304..81ee6d8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ serde = {version = "1.0", features = ["derive"]} serde_json = "1.0" lazy_static = "1.4" pretty_assertions = "0.7" +smart-default = "0.6" anyhow = "1" diff --git a/src/config/attributes.rs b/src/config/attributes.rs index 8a4154a..132e292 100644 --- a/src/config/attributes.rs +++ b/src/config/attributes.rs @@ -76,6 +76,18 @@ impl Attributes { } } + pub fn eval_optional>(&mut self, key: &str) -> Result, AttrError> { + let key = AttrName(key.to_string()); + match self.attrs.remove(&key) { + Some(AttrEntry { key_span, value }) => { + let value_span = value.span(); + let dynval = value.eval_no_vars().map_err(|err| AttrError::EvaluationError(value_span.into(), err))?; + T::try_from(dynval).map(Some).map_err(|_| AttrError::AttrTypeError(value_span.into(), key.clone())) + } + None => Ok(None), + } + } + // pub fn parse_required>(&mut self, key: &str) -> Result { // let key = AttrName(key.to_string()); // match self.attrs.remove(&key) { diff --git a/src/config/mod.rs b/src/config/mod.rs index 3c51d23..91325de 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -8,3 +8,4 @@ pub mod validate; pub mod var_definition; pub mod widget_definition; pub mod widget_use; +pub mod window_definition; diff --git a/src/config/window_definition.rs b/src/config/window_definition.rs new file mode 100644 index 0000000..75dbbcb --- /dev/null +++ b/src/config/window_definition.rs @@ -0,0 +1,150 @@ +use std::collections::HashMap; + +use simplexpr::{dynval::DynVal, SimplExpr}; + +use crate::{ + error::{AstError, AstResult}, + parser::{ + ast::{Ast, AstIterator, Span}, + from_ast::{FromAst, FromAstElementContent}, + }, + spanned, + value::{AttrName, VarName}, +}; + +use super::widget_use::WidgetUse; + +#[derive(Debug, Clone, serde::Serialize)] +pub struct EwwWindowDefinition { + pub name: String, + pub geometry: Option, + pub stacking: WindowStacking, + pub monitor_number: Option, + pub widget: WidgetUse, + pub resizable: bool, + // pub backend_options: BackendWindowOptions, +} + +impl FromAstElementContent for EwwWindowDefinition { + fn get_element_name() -> &'static str { + "defwindow" + } + + fn from_tail>(span: Span, mut iter: AstIterator) -> AstResult { + let (_, name) = iter.expect_symbol()?; + let mut attrs = iter.expect_key_values()?; + let monitor_number = attrs.eval_optional("monitor")?; + let resizable = attrs.eval_optional("resizable")?.unwrap_or(true); + let stacking = attrs.eval_optional("stacking")?.unwrap_or(WindowStacking::Foreground); + let widget = iter.expect_any()?; + Ok(Self { name, monitor_number, resizable, widget, stacking }) + } +} + +pub struct EnumParseError { + input: String, + expected: Vec<&'static str>, +} + +/// Parse a string with a concrete set of options into some data-structure, +/// and return an [EnumParseError] +/// ```rs +/// let input = "up"; +/// enum_parse { "direction", input, +/// "up" => Direction::Up, +/// "down" => Direction::Down, +/// } +/// ``` +#[macro_export] +macro_rules! enum_parse { + ($name:literal, $input:expr, $($($s:literal)|* => $val:expr),* $(,)?) => { + let input = $input.to_lowercase(); + match input.as_str() { + $( $( $s )|* => Ok($val) ),*, + _ => Err(EnumParseError { + input: $name, + expected: vec![$($($s),*),*], + }) + } + }; +} + +#[derive(Debug, Clone, PartialEq, Eq, SmartDefault)] +pub enum EwwWindowType { + #[default] + Dock, + Dialog, + Toolbar, + Normal, + Utility, +} +impl FromStr for EwwWindowType { + type Err = EnumParseError; + + fn from_str(s: &str) -> Result { + enum_parse! { "window type", s, + "dock" => Self::Dock, + "toolbar" => Self::Toolbar, + "dialog" => Self::Dialog, + "normal" => Self::Normal, + "utility" => Self::Utility, + } + } +} + +#[derive(Debug, Clone, Copy, Eq, PartialEq, smart_default::SmartDefault)] +pub enum Side { + #[default] + Top, + Left, + Right, + Bottom, +} + +impl std::str::FromStr for Side { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + enum_parse! { "side", s, + "l" | "left" => Side::Left, + "r" | "right" => Side::Right, + "t" | "top" => Side::Top, + "b" | "bottom" => Side::Bottom, + } + } +} + +// Surface definition if the backend for X11 is enable +#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] +pub struct StrutDefinition { + pub side: Side, + pub dist: NumWithUnit, +} + +impl StrutDefinition { + pub fn from_xml_element(xml: XmlElement) -> Result { + Ok(StrutDefinition { side: xml.attr("side")?.parse()?, dist: xml.attr("distance")?.parse()? }) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, derive_more::Display, SmartDefault)] +pub enum WindowStacking { + #[default] + Foreground, + Background, + Bottom, + Overlay, +} + +impl std::str::FromStr for WindowStacking { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + enum_parse! { "WindowStacking", s, + "foreground" | "fg" => WindowStacking::Foreground, + "background" | "bg" => WindowStacking::Background, + "bottom" | "bt" => WindowStacking::Bottom, + "overlay" | "ov" => WindowStacking::Overlay, + } + } +} From dd5078b4be2dbf8627dc0122e0ddbdb47008495a Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Tue, 20 Jul 2021 18:51:13 +0200 Subject: [PATCH 049/137] continue implementing window definition --- Cargo.lock | 1 + Cargo.toml | 2 +- src/config/attributes.rs | 74 +++++++------- src/config/mod.rs | 1 + src/config/script_var_definition.rs | 2 +- src/config/widget_use.rs | 2 +- src/config/window_definition.rs | 52 ++++++---- src/config/window_geometry.rs | 150 ++++++++++++++++++++++++++++ src/parser/ast.rs | 2 +- 9 files changed, 223 insertions(+), 63 deletions(-) create mode 100644 src/config/window_geometry.rs diff --git a/Cargo.lock b/Cargo.lock index 8a5778d..b720e0f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -222,6 +222,7 @@ dependencies = [ "serde_json", "simplexpr", "smart-default", + "strum", "thiserror", ] diff --git a/Cargo.toml b/Cargo.toml index 81ee6d8..81f54f2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,8 +22,8 @@ serde = {version = "1.0", features = ["derive"]} serde_json = "1.0" lazy_static = "1.4" pretty_assertions = "0.7" -smart-default = "0.6" +strum = { version = "0.21", features = ["derive"] } anyhow = "1" diff --git a/src/config/attributes.rs b/src/config/attributes.rs index 132e292..536bbfa 100644 --- a/src/config/attributes.rs +++ b/src/config/attributes.rs @@ -3,9 +3,14 @@ use std::{ convert::{TryFrom, TryInto}, }; -use simplexpr::{dynval::DynVal, eval::EvalError, SimplExpr}; +use simplexpr::{ + dynval::{DynVal, FromDynVal}, + eval::EvalError, + SimplExpr, +}; use crate::{ + error::AstError, parser::{ ast::{Ast, Span}, from_ast::FromAst, @@ -44,11 +49,11 @@ pub struct UnusedAttrs { #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)] pub struct AttrEntry { pub key_span: Span, - pub value: SimplExpr, + pub value: Ast, } impl AttrEntry { - pub fn new(key_span: Span, value: SimplExpr) -> AttrEntry { + pub fn new(key_span: Span, value: Ast) -> AttrEntry { AttrEntry { key_span, value } } } @@ -64,51 +69,42 @@ impl Attributes { Attributes { span, attrs } } - pub fn eval_required>(&mut self, key: &str) -> Result { + pub fn ast_required(&mut self, key: &str) -> Result { let key = AttrName(key.to_string()); match self.attrs.remove(&key) { - Some(AttrEntry { key_span, value }) => { - let value_span = value.span(); - let dynval = value.eval_no_vars().map_err(|err| AttrError::EvaluationError(value_span.into(), err))?; - T::try_from(dynval).map_err(|_| AttrError::AttrTypeError(value_span.into(), key.clone())) - } - None => Err(AttrError::MissingRequiredAttr(self.span, key.clone())), + Some(AttrEntry { key_span, value }) => T::from_ast(value), + None => Err(AttrError::MissingRequiredAttr(self.span, key.clone()).into()), } } - pub fn eval_optional>(&mut self, key: &str) -> Result, AttrError> { - let key = AttrName(key.to_string()); - match self.attrs.remove(&key) { - Some(AttrEntry { key_span, value }) => { - let value_span = value.span(); - let dynval = value.eval_no_vars().map_err(|err| AttrError::EvaluationError(value_span.into(), err))?; - T::try_from(dynval).map(Some).map_err(|_| AttrError::AttrTypeError(value_span.into(), key.clone())) - } + pub fn ast_optional(&mut self, key: &str) -> Result, AstError> { + match self.attrs.remove(&AttrName(key.to_string())) { + Some(AttrEntry { key_span, value }) => T::from_ast(value).map(Some), None => Ok(None), } } - // pub fn parse_required>(&mut self, key: &str) -> Result { - // let key = AttrName(key.to_string()); - // match self.attrs.remove(&key) { - // Some(value) => match value.value.try_into() { - // Ok(value) => Ok(value), - // Err(_) => Err(AttrError::AttrTypeError(value.value.span().into(), key.clone())), - // }, - // None => Err(AttrError::MissingRequiredAttr(self.span, key.clone())), - // } - // } - // - // pub fn parse_optional>(&mut self, key: &str) -> Result, AttrError> { - // let key = AttrName(key.to_string()); - // match self.attrs.remove(&key) { - // Some(value) => match value.value.try_into() { - // Ok(value) => Ok(Some(value)), - // Err(_) => Err(AttrError::AttrTypeError(value.value.span().into(), key.clone())), - // }, - // None => Ok(None), - // } - // } + pub fn primitive_required(&mut self, key: &str) -> Result { + let ast: SimplExpr = self.ast_required(&key)?; + Ok(ast + .eval_no_vars() + .map_err(|err| AttrError::EvaluationError(ast.span().into(), err))? + .read_as() + .map_err(|_| AttrError::AttrTypeError(ast.span().into(), AttrName(key.to_string())))?) + } + + pub fn primitive_optional(&mut self, key: &str) -> Result, AstError> { + let ast: SimplExpr = match self.ast_optional(key)? { + Some(ast) => ast, + None => return Ok(None), + }; + Ok(Some( + ast.eval_no_vars() + .map_err(|err| AttrError::EvaluationError(ast.span().into(), err))? + .read_as() + .map_err(|_| AttrError::AttrTypeError(ast.span().into(), AttrName(key.to_string())))?, + )) + } /// Consumes the attributes to return a list of unused attributes which may be used to emit a warning. /// TODO actually use this and implement warnings,... lol diff --git a/src/config/mod.rs b/src/config/mod.rs index 91325de..03d20a8 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -9,3 +9,4 @@ pub mod var_definition; pub mod widget_definition; pub mod widget_use; pub mod window_definition; +pub mod window_geometry; diff --git a/src/config/script_var_definition.rs b/src/config/script_var_definition.rs index 9d6d907..147c5c0 100644 --- a/src/config/script_var_definition.rs +++ b/src/config/script_var_definition.rs @@ -49,7 +49,7 @@ impl FromAstElementContent for PollScriptVar { fn from_tail>(span: Span, mut iter: AstIterator) -> AstResult { let (_, name) = iter.expect_symbol()?; let mut attrs = iter.expect_key_values()?; - let interval: String = attrs.eval_required("interval")?; + let interval: String = attrs.primitive_required("interval")?; let interval = crate::util::parse_duration(&interval).map_err(|e| AstError::Other(Some(span), e.into()))?; let (_, script) = iter.expect_literal()?; Ok(Self { name: VarName(name), command: VarSource::Shell(script.to_string()), interval }) diff --git a/src/config/widget_use.rs b/src/config/widget_use.rs index 2c5e314..4bdd06a 100644 --- a/src/config/widget_use.rs +++ b/src/config/widget_use.rs @@ -35,7 +35,7 @@ impl FromAst for WidgetUse { maplit::hashmap! { AttrName("text".to_string()) => AttrEntry::new( span.into(), - SimplExpr::Literal(span.into(), text.clone()) + Ast::Literal(span.into(), text.clone()) ) }, ), diff --git a/src/config/window_definition.rs b/src/config/window_definition.rs index 75dbbcb..b19fb9c 100644 --- a/src/config/window_definition.rs +++ b/src/config/window_definition.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::{collections::HashMap, fmt::Display, str::FromStr}; use simplexpr::{dynval::DynVal, SimplExpr}; @@ -9,15 +9,15 @@ use crate::{ from_ast::{FromAst, FromAstElementContent}, }, spanned, - value::{AttrName, VarName}, + value::{AttrName, NumWithUnit, VarName}, }; -use super::widget_use::WidgetUse; +use super::{widget_use::WidgetUse, window_geometry::WindowGeometry}; #[derive(Debug, Clone, serde::Serialize)] pub struct EwwWindowDefinition { pub name: String, - pub geometry: Option, + pub geometry: Option, pub stacking: WindowStacking, pub monitor_number: Option, pub widget: WidgetUse, @@ -33,17 +33,24 @@ impl FromAstElementContent for EwwWindowDefinition { fn from_tail>(span: Span, mut iter: AstIterator) -> AstResult { let (_, name) = iter.expect_symbol()?; let mut attrs = iter.expect_key_values()?; - let monitor_number = attrs.eval_optional("monitor")?; - let resizable = attrs.eval_optional("resizable")?.unwrap_or(true); - let stacking = attrs.eval_optional("stacking")?.unwrap_or(WindowStacking::Foreground); + let monitor_number = attrs.primitive_optional("monitor")?; + let resizable = attrs.primitive_optional("resizable")?.unwrap_or(true); + let stacking = attrs.primitive_optional("stacking")?.unwrap_or(WindowStacking::Foreground); + let geometry = attrs.ast_optional("geometry")?; let widget = iter.expect_any()?; - Ok(Self { name, monitor_number, resizable, widget, stacking }) + Ok(Self { name, monitor_number, resizable, widget, stacking, geometry }) } } +#[derive(Debug, thiserror::Error)] pub struct EnumParseError { - input: String, - expected: Vec<&'static str>, + pub input: String, + pub expected: Vec<&'static str>, +} +impl Display for EnumParseError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Failed to parse `{}`, must be one of {}", self.input, self.expected.join(", ")) + } } /// Parse a string with a concrete set of options into some data-structure, @@ -62,14 +69,14 @@ macro_rules! enum_parse { match input.as_str() { $( $( $s )|* => Ok($val) ),*, _ => Err(EnumParseError { - input: $name, + input: input, expected: vec![$($($s),*),*], }) } }; } -#[derive(Debug, Clone, PartialEq, Eq, SmartDefault)] +#[derive(Debug, Clone, PartialEq, Eq, smart_default::SmartDefault)] pub enum EwwWindowType { #[default] Dock, @@ -102,9 +109,9 @@ pub enum Side { } impl std::str::FromStr for Side { - type Err = anyhow::Error; + type Err = EnumParseError; - fn from_str(s: &str) -> Result { + fn from_str(s: &str) -> Result { enum_parse! { "side", s, "l" | "left" => Side::Left, "r" | "right" => Side::Right, @@ -121,13 +128,18 @@ pub struct StrutDefinition { pub dist: NumWithUnit, } -impl StrutDefinition { - pub fn from_xml_element(xml: XmlElement) -> Result { - Ok(StrutDefinition { side: xml.attr("side")?.parse()?, dist: xml.attr("distance")?.parse()? }) +impl FromAstElementContent for StrutDefinition { + fn get_element_name() -> &'static str { + "struts" + } + + fn from_tail>(span: Span, mut iter: AstIterator) -> AstResult { + let mut attrs = iter.expect_key_values()?; + Ok(StrutDefinition { side: attrs.primitive_required("side")?, dist: attrs.primitive_required("distance")? }) } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, derive_more::Display, SmartDefault)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, derive_more::Display, smart_default::SmartDefault, serde::Serialize)] pub enum WindowStacking { #[default] Foreground, @@ -137,9 +149,9 @@ pub enum WindowStacking { } impl std::str::FromStr for WindowStacking { - type Err = anyhow::Error; + type Err = EnumParseError; - fn from_str(s: &str) -> Result { + fn from_str(s: &str) -> Result { enum_parse! { "WindowStacking", s, "foreground" | "fg" => WindowStacking::Foreground, "background" | "bg" => WindowStacking::Background, diff --git a/src/config/window_geometry.rs b/src/config/window_geometry.rs new file mode 100644 index 0000000..599984e --- /dev/null +++ b/src/config/window_geometry.rs @@ -0,0 +1,150 @@ +use std::collections::HashMap; + +use simplexpr::{dynval::DynVal, SimplExpr}; + +use crate::{enum_parse, error::{AstError, AstResult}, parser::{ + ast::{Ast, AstIterator, Span}, + from_ast::{FromAst, FromAstElementContent}, + }, spanned, value::{AttrName, Coords, VarName}}; + +use super::{widget_use::WidgetUse, window_definition::EnumParseError}; +use serde::{Serialize, Deserialize}; + +#[derive(Debug, Clone, Copy, Eq, PartialEq, smart_default::SmartDefault, Serialize, Deserialize, strum::Display)] +pub enum AnchorAlignment { + #[strum(serialize = "start")] + #[default] + START, + #[strum(serialize = "center")] + CENTER, + #[strum(serialize = "end")] + END, +} + +impl AnchorAlignment { + pub fn from_x_alignment(s: &str) -> Result { + enum_parse! { "x-alignment", s, + "l" | "left" => AnchorAlignment::START, + "c" | "center" => AnchorAlignment::CENTER, + "r" | "right" => AnchorAlignment::END, + } + } + + pub fn from_y_alignment(s: &str) -> Result { + enum_parse! { "y-alignment", s, + "t" | "top" => AnchorAlignment::START, + "c" | "center" => AnchorAlignment::CENTER, + "b" | "bottom" => AnchorAlignment::END, + } + } + + pub fn alignment_to_coordinate(&self, size_inner: i32, size_container: i32) -> i32 { + match self { + AnchorAlignment::START => 0, + AnchorAlignment::CENTER => (size_container / 2) - (size_inner / 2), + AnchorAlignment::END => size_container - size_inner, + } + } +} + +#[derive(Debug, Clone, Copy, Eq, PartialEq, Default, Serialize, Deserialize)] +pub struct AnchorPoint { + pub x: AnchorAlignment, + pub y: AnchorAlignment, +} + +impl std::fmt::Display for AnchorPoint { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use AnchorAlignment::*; + match (self.x, self.y) { + (CENTER, CENTER) => write!(f, "center"), + (x, y) => write!( + f, + "{} {}", + match x { + START => "left", + CENTER => "center", + END => "right", + }, + match y { + START => "top", + CENTER => "center", + END => "bottom", + } + ), + } + } +} + +#[derive(Debug, thiserror::Error)] +pub enum AnchorPointParseError { + #[error("Could not parse anchor: Must either be \"center\" or be formatted like \"top left\"")] + WrongFormat(String), + #[error(transparent)] + EnumParseError(#[from] EnumParseError), +} + + +impl std::str::FromStr for AnchorPoint { + type Err = AnchorPointParseError; + + fn from_str(s: &str) -> Result { + if s == "center" { + Ok(AnchorPoint { x: AnchorAlignment::CENTER, y: AnchorAlignment::CENTER }) + } else { + let (first, second) = s + .split_once(' ') + .ok_or_else(|| AnchorPointParseError::WrongFormat(s.to_string()))?; + let x_y_result: Result<_, EnumParseError> = try { + AnchorPoint { x: AnchorAlignment::from_x_alignment(first)?, y: AnchorAlignment::from_y_alignment(second)? } + }; + x_y_result.or_else(|_| { + Ok(AnchorPoint { x: AnchorAlignment::from_x_alignment(second)?, y: AnchorAlignment::from_y_alignment(first)? }) + }) + } + } +} + +#[derive(Default, Debug, Clone, Copy, Eq, PartialEq, Serialize)] +pub struct WindowGeometry { + pub anchor_point: AnchorPoint, + pub offset: Coords, + pub size: Coords, +} + +impl FromAstElementContent for WindowGeometry { + fn get_element_name() -> &'static str { + "geometry" + } + + fn from_tail>(span: Span, mut iter: AstIterator) -> AstResult { + let mut attrs = iter.expect_key_values()?; + Ok(WindowGeometry { + anchor_point: attrs.primitive_optional("anchor")?.unwrap_or_default(), + size: Coords { + x: attrs.primitive_optional("width")?.unwrap_or_default(), + y: attrs.primitive_optional("height")?.unwrap_or_default(), + }, + offset: Coords { + x: attrs.primitive_optional("x")?.unwrap_or_default(), + y: attrs.primitive_optional("y")?.unwrap_or_default(), + }, + }) + } +} + +impl WindowGeometry { + pub fn override_if_given(&self, anchor_point: Option, offset: Option, size: Option) -> Self { + WindowGeometry { + anchor_point: anchor_point.unwrap_or(self.anchor_point), + offset: offset.unwrap_or(self.offset), + size: size.unwrap_or(self.size), + } + } +} + +impl std::fmt::Display for WindowGeometry { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}-{} ({})", self.offset, self.size, self.anchor_point) + } +} diff --git a/src/parser/ast.rs b/src/parser/ast.rs index 0861ab7..a9f0903 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -252,7 +252,7 @@ fn parse_key_values(iter: &mut AstIterator>) -> AstRes Some(Ast::Keyword(key_span, kw)) => match iter.next() { Some(value) => { attrs_span.1 = iter.remaining_span.0; - let attr_value = AttrEntry { key_span, value: value.as_simplexpr()? }; + let attr_value = AttrEntry { key_span, value }; data.insert(AttrName(kw), attr_value); } None => { From 5748185fb7a9b13266a69e8f15420cf839cd70ca Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Tue, 20 Jul 2021 18:51:29 +0200 Subject: [PATCH 050/137] FromDynVal-trait --- src/dynval.rs | 54 ++++++++++++++++++++++++++++++++++----------------- src/eval.rs | 21 +++++++++++++++----- 2 files changed, 52 insertions(+), 23 deletions(-) diff --git a/src/dynval.rs b/src/dynval.rs index b3b5b2b..463eae7 100644 --- a/src/dynval.rs +++ b/src/dynval.rs @@ -1,7 +1,7 @@ use crate::ast::Span; use itertools::Itertools; use serde::{Deserialize, Serialize}; -use std::{convert::TryFrom, fmt, iter::FromIterator}; +use std::{fmt, iter::FromIterator, str::FromStr}; pub type Result = std::result::Result; @@ -34,7 +34,7 @@ impl From for DynVal { impl fmt::Display for DynVal { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "\"{}\"", self.0) + write!(f, "{}", self.0) } } impl fmt::Debug for DynVal { @@ -70,32 +70,46 @@ impl std::str::FromStr for DynVal { } } -macro_rules! impl_try_from { - (impl From<$typ:ty> { - $(for $for:ty => |$arg:ident| $code:expr);*; - }) => { - $(impl TryFrom<$typ> for $for { - type Error = ConversionError; - fn try_from($arg: $typ) -> std::result::Result { $code } +pub trait FromDynVal: Sized { + type Err; + fn from_dynval(x: &DynVal) -> std::result::Result; +} + +impl> FromDynVal for T { + type Err = E; + + fn from_dynval(x: &DynVal) -> std::result::Result { + x.0.parse() + } +} + +macro_rules! impl_from_dynval { + ( + $(for $for:ty => |$name:ident| $code:expr);*; + ) => { + $(impl FromDynVal for $for { + type Err = ConversionError; + fn from_dynval($name: DynVal) -> std::result::Result { $code } })* }; } -macro_rules! impl_primval_from { +macro_rules! impl_dynval_from { ($($t:ty),*) => { $(impl From<$t> for DynVal { fn from(x: $t) -> Self { DynVal(x.to_string(), None) } })* }; } -impl_try_from!(impl From { - for String => |x| x.as_string(); - for f64 => |x| x.as_f64(); - for i32 => |x| x.as_i32(); - for bool => |x| x.as_bool(); - //for Vec => |x| x.as_vec(); -}); -impl_primval_from!(bool, i32, u32, f32, u8, f64, &str); +// impl_from_dynval! { +// for String => |x| x.as_string(); +// for f64 => |x| x.as_f64(); +// for i32 => |x| x.as_i32(); +// for bool => |x| x.as_bool(); +////for Vec => |x| x.as_vec(); +//} + +impl_dynval_from!(bool, i32, u32, f32, u8, f64, &str); impl From<&serde_json::Value> for DynVal { fn from(v: &serde_json::Value) -> Self { @@ -118,6 +132,10 @@ impl DynVal { DynVal(s, None) } + pub fn read_as>(&self) -> std::result::Result { + T::from_dynval(self) + } + pub fn into_inner(self) -> String { self.0 } diff --git a/src/eval.rs b/src/eval.rs index 01d58bd..3c70583 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -8,6 +8,9 @@ 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), @@ -114,12 +117,20 @@ impl SimplExpr { } } - pub fn eval(self, values: &HashMap) -> Result { + pub fn eval_no_vars(&self) -> Result { + 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) -> Result { let span = self.span(); let value = match self { - SimplExpr::Literal(_, x) => Ok(x), + 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)) + 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)?; @@ -176,12 +187,12 @@ impl SimplExpr { .unwrap_or(&serde_json::Value::Null); Ok(DynVal::from(indexed_value)) } - _ => Err(EvalError::CannotIndex(format!("{}", val)).at(span)), + _ => Err(EvalError::CannotIndex(format!("{}", val)).at(*span)), } } SimplExpr::FunctionCall(span, function_name, args) => { let args = args.into_iter().map(|a| a.eval(values)).collect::>()?; - call_expr_function(&function_name, args).map_err(|e| e.at(span)) + call_expr_function(&function_name, args).map_err(|e| e.at(*span)) } }; Ok(value?.at(span)) From 98cbbff7c93de55e8f3b4978687794aff3930171 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Tue, 20 Jul 2021 19:19:43 +0200 Subject: [PATCH 051/137] Window definitions are parsed fully --- src/config/backend_window_options.rs | 123 ++++++++++++++++++ src/config/config.rs | 18 ++- src/config/mod.rs | 1 + .../eww_config__config__test__config.snap | 69 +++++++++- src/config/test.rs | 7 + src/config/window_definition.rs | 76 +---------- 6 files changed, 216 insertions(+), 78 deletions(-) create mode 100644 src/config/backend_window_options.rs diff --git a/src/config/backend_window_options.rs b/src/config/backend_window_options.rs new file mode 100644 index 0000000..99918cd --- /dev/null +++ b/src/config/backend_window_options.rs @@ -0,0 +1,123 @@ +use std::str::FromStr; + +use anyhow::*; + +use crate::{ + enum_parse, + error::AstResult, + parser::{ + ast::{Ast, AstIterator, Span}, + from_ast::FromAstElementContent, + }, + value::NumWithUnit, +}; + +use super::{attributes::Attributes, window_definition::EnumParseError}; + +pub type BackendWindowOptions = X11WindowOptions; + +#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)] +pub struct X11WindowOptions { + pub wm_ignore: bool, + pub sticky: bool, + pub window_type: EwwWindowType, + pub struts: StrutDefinition, +} + +impl X11WindowOptions { + pub fn from_attrs(attrs: &mut Attributes) -> AstResult { + let struts = attrs.ast_optional("reserve")?; + let window_type = attrs.primitive_optional("windowtype")?; + Ok(X11WindowOptions { + wm_ignore: attrs.primitive_optional("wm-ignore")?.unwrap_or(window_type.is_none() && struts.is_none()), + window_type: window_type.unwrap_or_default(), + sticky: attrs.primitive_optional("sticky")?.unwrap_or(true), + struts: struts.unwrap_or_default(), + }) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, smart_default::SmartDefault, serde::Serialize)] +pub enum EwwWindowType { + #[default] + Dock, + Dialog, + Toolbar, + Normal, + Utility, +} +impl FromStr for EwwWindowType { + type Err = EnumParseError; + + fn from_str(s: &str) -> Result { + enum_parse! { "window type", s, + "dock" => Self::Dock, + "toolbar" => Self::Toolbar, + "dialog" => Self::Dialog, + "normal" => Self::Normal, + "utility" => Self::Utility, + } + } +} + +#[derive(Debug, Clone, Copy, Eq, PartialEq, smart_default::SmartDefault, serde::Serialize)] +pub enum Side { + #[default] + Top, + Left, + Right, + Bottom, +} + +impl std::str::FromStr for Side { + type Err = EnumParseError; + + fn from_str(s: &str) -> Result { + enum_parse! { "side", s, + "l" | "left" => Side::Left, + "r" | "right" => Side::Right, + "t" | "top" => Side::Top, + "b" | "bottom" => Side::Bottom, + } + } +} + +// Surface definition if the backend for X11 is enable +#[derive(Debug, Clone, Copy, Eq, PartialEq, Default, serde::Serialize)] +pub struct StrutDefinition { + pub side: Side, + pub dist: NumWithUnit, +} + +impl FromAstElementContent for StrutDefinition { + fn get_element_name() -> &'static str { + "struts" + } + + fn from_tail>(span: Span, mut iter: AstIterator) -> AstResult { + let mut attrs = iter.expect_key_values()?; + Ok(StrutDefinition { side: attrs.primitive_required("side")?, dist: attrs.primitive_required("distance")? }) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)] +pub struct WaylandWindowOptions { + pub exclusive: bool, + pub focusable: bool, +} +impl WaylandWindowOptions { + pub fn from_attrs(attrs: &mut Attributes) -> AstResult { + Ok(WaylandWindowOptions { + exclusive: attrs.primitive_optional("exclusive")?.unwrap_or(false), + focusable: attrs.primitive_optional("focusable")?.unwrap_or(false), + }) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)] +pub struct NoBackendWindowOptions; +impl NoBackendWindowOptions { + pub fn from_attrs(attrs: &mut Attributes) -> Result { + Ok(NoBackendWindowOptions) + } +} diff --git a/src/config/config.rs b/src/config/config.rs index b86b28f..8cd5f7c 100644 --- a/src/config/config.rs +++ b/src/config/config.rs @@ -4,7 +4,7 @@ use simplexpr::SimplExpr; use super::{ script_var_definition::ScriptVarDefinition, var_definition::VarDefinition, widget_definition::WidgetDefinition, - widget_use::WidgetUse, + widget_use::WidgetUse, window_definition::WindowDefinition, }; use crate::{ config::script_var_definition::{PollScriptVar, TailScriptVar}, @@ -21,6 +21,7 @@ pub enum TopLevel { VarDefinition(VarDefinition), ScriptVarDefinition(ScriptVarDefinition), WidgetDefinition(WidgetDefinition), + WindowDefinition(WindowDefinition), } impl FromAst for TopLevel { @@ -40,6 +41,9 @@ impl FromAst for TopLevel { x if x == TailScriptVar::get_element_name() => { Self::ScriptVarDefinition(ScriptVarDefinition::Tail(TailScriptVar::from_tail(span, iter)?)) } + x if x == WindowDefinition::get_element_name() => { + Self::WindowDefinition(WindowDefinition::from_tail(span, iter)?) + } x => return Err(AstError::UnknownToplevel(Some(sym_span), x.to_string())), } }) @@ -49,6 +53,7 @@ impl FromAst for TopLevel { #[derive(Debug, PartialEq, Eq, Clone, serde::Serialize)] pub struct Config { widget_definitions: HashMap, + window_definitions: HashMap, var_definitions: HashMap, script_vars: HashMap, } @@ -56,8 +61,12 @@ pub struct Config { impl FromAst for Config { fn from_ast(e: Ast) -> AstResult { let list = e.as_list()?; - let mut config = - Self { widget_definitions: HashMap::new(), var_definitions: HashMap::new(), script_vars: HashMap::new() }; + let mut config = Self { + widget_definitions: HashMap::new(), + window_definitions: HashMap::new(), + var_definitions: HashMap::new(), + script_vars: HashMap::new(), + }; for element in list { match TopLevel::from_ast(element)? { TopLevel::VarDefinition(x) => { @@ -69,6 +78,9 @@ impl FromAst for Config { TopLevel::WidgetDefinition(x) => { config.widget_definitions.insert(x.name.clone(), x); } + TopLevel::WindowDefinition(x) => { + config.window_definitions.insert(x.name.clone(), x); + } } } Ok(config) diff --git a/src/config/mod.rs b/src/config/mod.rs index 03d20a8..2b3a558 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,4 +1,5 @@ pub mod attributes; +pub mod backend_window_options; mod config; pub mod config_parse_error; pub mod script_var_definition; diff --git a/src/config/snapshots/eww_config__config__test__config.snap b/src/config/snapshots/eww_config__config__test__config.snap index d7ba933..4316d36 100644 --- a/src/config/snapshots/eww_config__config__test__config.snap +++ b/src/config/snapshots/eww_config__config__test__config.snap @@ -13,9 +13,15 @@ Config( ], widget: WidgetUse( name: "text", - attrs: { - AttrName("text"): Literal(Span(99, 104, 0), DynVal("bla", None)), - }, + attrs: Attributes( + span: Span(99, 104, 0), + attrs: { + AttrName("text"): AttrEntry( + key_span: Span(99, 104, 0), + value: Literal(Span(99, 104, 0), DynVal("bla", None)), + ), + }, + ), children: [], span: Span(99, 104, 0), ), @@ -29,9 +35,15 @@ Config( ], widget: WidgetUse( name: "text", - attrs: { - AttrName("text"): Literal(Span(44, 51, 0), DynVal("heyho", None)), - }, + attrs: Attributes( + span: Span(44, 51, 0), + attrs: { + AttrName("text"): AttrEntry( + key_span: Span(44, 51, 0), + value: Literal(Span(44, 51, 0), DynVal("heyho", None)), + ), + }, + ), children: [], span: Span(44, 51, 0), ), @@ -39,6 +51,51 @@ Config( args_span: Span(26, 31, 0), ), }, + window_definitions: { + "some-window": WindowDefinition( + name: "some-window", + geometry: Some(WindowGeometry( + anchor_point: AnchorPoint( + x: START, + y: START, + ), + offset: Coords( + x: Pixels(0), + y: Pixels(0), + ), + size: Coords( + x: Percent(12), + y: Pixels(20), + ), + )), + stacking: Foreground, + monitor_number: Some(12), + widget: WidgetUse( + name: "foo", + attrs: Attributes( + span: Span(509, 509, 513), + attrs: { + AttrName("arg"): AttrEntry( + key_span: Span(514, 518, 0), + value: Literal(Span(519, 524, 0), DynVal("bla", None)), + ), + }, + ), + children: [], + span: Span(509, 525, 0), + ), + resizable: true, + backend_options: X11WindowOptions( + wm_ignore: false, + sticky: true, + window_type: Dock, + struts: StrutDefinition( + side: Left, + dist: Pixels(30), + ), + ), + ), + }, var_definitions: { VarName("some_var"): VarDefinition( name: VarName("some_var"), diff --git a/src/config/test.rs b/src/config/test.rs index 593a683..bd34c77 100644 --- a/src/config/test.rs +++ b/src/config/test.rs @@ -18,6 +18,13 @@ fn test_config() { (defvar some_var "bla") (defpollvar stuff :interval "12s" "date") (deftailvar stuff "tail -f stuff") + (defwindow some-window + :stacking "fg" + :monitor 12 + :resizable true + :geometry (geometry :width "12%" :height "20px") + :reserve (struts :side "left" :distance "30px") + (foo :arg "bla")) "#; let lexer = Lexer::new(0, input.to_string()); diff --git a/src/config/window_definition.rs b/src/config/window_definition.rs index b19fb9c..bc792b8 100644 --- a/src/config/window_definition.rs +++ b/src/config/window_definition.rs @@ -12,20 +12,20 @@ use crate::{ value::{AttrName, NumWithUnit, VarName}, }; -use super::{widget_use::WidgetUse, window_geometry::WindowGeometry}; +use super::{backend_window_options::BackendWindowOptions, widget_use::WidgetUse, window_geometry::WindowGeometry}; -#[derive(Debug, Clone, serde::Serialize)] -pub struct EwwWindowDefinition { +#[derive(Debug, Clone, serde::Serialize, PartialEq, Eq)] +pub struct WindowDefinition { pub name: String, pub geometry: Option, pub stacking: WindowStacking, pub monitor_number: Option, pub widget: WidgetUse, pub resizable: bool, - // pub backend_options: BackendWindowOptions, + pub backend_options: BackendWindowOptions, } -impl FromAstElementContent for EwwWindowDefinition { +impl FromAstElementContent for WindowDefinition { fn get_element_name() -> &'static str { "defwindow" } @@ -37,8 +37,9 @@ impl FromAstElementContent for EwwWindowDefinition { let resizable = attrs.primitive_optional("resizable")?.unwrap_or(true); let stacking = attrs.primitive_optional("stacking")?.unwrap_or(WindowStacking::Foreground); let geometry = attrs.ast_optional("geometry")?; + let backend_options = BackendWindowOptions::from_attrs(&mut attrs)?; let widget = iter.expect_any()?; - Ok(Self { name, monitor_number, resizable, widget, stacking, geometry }) + Ok(Self { name, monitor_number, resizable, widget, stacking, geometry, backend_options }) } } @@ -76,69 +77,6 @@ macro_rules! enum_parse { }; } -#[derive(Debug, Clone, PartialEq, Eq, smart_default::SmartDefault)] -pub enum EwwWindowType { - #[default] - Dock, - Dialog, - Toolbar, - Normal, - Utility, -} -impl FromStr for EwwWindowType { - type Err = EnumParseError; - - fn from_str(s: &str) -> Result { - enum_parse! { "window type", s, - "dock" => Self::Dock, - "toolbar" => Self::Toolbar, - "dialog" => Self::Dialog, - "normal" => Self::Normal, - "utility" => Self::Utility, - } - } -} - -#[derive(Debug, Clone, Copy, Eq, PartialEq, smart_default::SmartDefault)] -pub enum Side { - #[default] - Top, - Left, - Right, - Bottom, -} - -impl std::str::FromStr for Side { - type Err = EnumParseError; - - fn from_str(s: &str) -> Result { - enum_parse! { "side", s, - "l" | "left" => Side::Left, - "r" | "right" => Side::Right, - "t" | "top" => Side::Top, - "b" | "bottom" => Side::Bottom, - } - } -} - -// Surface definition if the backend for X11 is enable -#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] -pub struct StrutDefinition { - pub side: Side, - pub dist: NumWithUnit, -} - -impl FromAstElementContent for StrutDefinition { - fn get_element_name() -> &'static str { - "struts" - } - - fn from_tail>(span: Span, mut iter: AstIterator) -> AstResult { - let mut attrs = iter.expect_key_values()?; - Ok(StrutDefinition { side: attrs.primitive_required("side")?, dist: attrs.primitive_required("distance")? }) - } -} - #[derive(Debug, Clone, Copy, PartialEq, Eq, derive_more::Display, smart_default::SmartDefault, serde::Serialize)] pub enum WindowStacking { #[default] From 3892562be909cab56d4337e5b2ce121a4d7e534b Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Tue, 20 Jul 2021 19:26:19 +0200 Subject: [PATCH 052/137] Improve errors for attribute parsing --- src/config/attributes.rs | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/config/attributes.rs b/src/config/attributes.rs index 536bbfa..b2e668d 100644 --- a/src/config/attributes.rs +++ b/src/config/attributes.rs @@ -23,19 +23,19 @@ pub enum AttrError { #[error("Missing required attribute {0}")] MissingRequiredAttr(Span, AttrName), - #[error("Failed to parse attribute value {0} in this context")] - AttrTypeError(Span, AttrName), - #[error("{1}")] EvaluationError(Span, EvalError), + + #[error("{1}")] + Other(Span, Box), } impl AttrError { pub fn span(&self) -> Span { match self { AttrError::MissingRequiredAttr(span, _) => *span, - AttrError::AttrTypeError(span, _) => *span, AttrError::EvaluationError(span, _) => *span, + AttrError::Other(span, _) => *span, } } } @@ -84,16 +84,24 @@ impl Attributes { } } - pub fn primitive_required(&mut self, key: &str) -> Result { + pub fn primitive_required(&mut self, key: &str) -> Result + where + E: std::error::Error + 'static, + T: FromDynVal, + { let ast: SimplExpr = self.ast_required(&key)?; Ok(ast .eval_no_vars() .map_err(|err| AttrError::EvaluationError(ast.span().into(), err))? .read_as() - .map_err(|_| AttrError::AttrTypeError(ast.span().into(), AttrName(key.to_string())))?) + .map_err(|e| AttrError::Other(ast.span().into(), Box::new(e)))?) } - pub fn primitive_optional(&mut self, key: &str) -> Result, AstError> { + pub fn primitive_optional(&mut self, key: &str) -> Result, AstError> + where + E: std::error::Error + 'static, + T: FromDynVal, + { let ast: SimplExpr = match self.ast_optional(key)? { Some(ast) => ast, None => return Ok(None), @@ -102,7 +110,7 @@ impl Attributes { ast.eval_no_vars() .map_err(|err| AttrError::EvaluationError(ast.span().into(), err))? .read_as() - .map_err(|_| AttrError::AttrTypeError(ast.span().into(), AttrName(key.to_string())))?, + .map_err(|e| AttrError::Other(ast.span().into(), Box::new(e)))?, )) } From efa00a408b5840b5b6cf83b68ae75ab0b82e4307 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Wed, 21 Jul 2021 15:23:21 +0200 Subject: [PATCH 053/137] factor out astIterator --- src/config/backend_window_options.rs | 3 +- src/config/config.rs | 3 +- src/config/script_var_definition.rs | 3 +- src/config/validate.rs | 3 +- src/config/var_definition.rs | 3 +- src/config/widget_definition.rs | 3 +- src/config/widget_use.rs | 3 +- src/config/window_definition.rs | 3 +- src/config/window_geometry.rs | 2 +- src/parser/ast.rs | 90 +----------------------- src/parser/ast_iterator.rs | 101 +++++++++++++++++++++++++++ src/parser/from_ast.rs | 5 +- src/parser/mod.rs | 1 + 13 files changed, 125 insertions(+), 98 deletions(-) create mode 100644 src/parser/ast_iterator.rs diff --git a/src/config/backend_window_options.rs b/src/config/backend_window_options.rs index 99918cd..0cc0a9e 100644 --- a/src/config/backend_window_options.rs +++ b/src/config/backend_window_options.rs @@ -6,7 +6,8 @@ use crate::{ enum_parse, error::AstResult, parser::{ - ast::{Ast, AstIterator, Span}, + ast::{Ast, Span}, + ast_iterator::AstIterator, from_ast::FromAstElementContent, }, value::NumWithUnit, diff --git a/src/config/config.rs b/src/config/config.rs index 8cd5f7c..556c850 100644 --- a/src/config/config.rs +++ b/src/config/config.rs @@ -10,7 +10,8 @@ use crate::{ config::script_var_definition::{PollScriptVar, TailScriptVar}, error::{AstError, AstResult, OptionAstErrorExt}, parser::{ - ast::{Ast, AstIterator, Span}, + ast::{Ast, Span}, + ast_iterator::AstIterator, from_ast::{FromAst, FromAstElementContent}, }, spanned, diff --git a/src/config/script_var_definition.rs b/src/config/script_var_definition.rs index 147c5c0..4089cda 100644 --- a/src/config/script_var_definition.rs +++ b/src/config/script_var_definition.rs @@ -5,7 +5,8 @@ use simplexpr::{dynval::DynVal, SimplExpr}; use crate::{ error::{AstError, AstResult}, parser::{ - ast::{Ast, AstIterator, Span}, + ast::{Ast, Span}, + ast_iterator::AstIterator, from_ast::{FromAst, FromAstElementContent}, }, spanned, diff --git a/src/config/validate.rs b/src/config/validate.rs index 99b049b..e4ca804 100644 --- a/src/config/validate.rs +++ b/src/config/validate.rs @@ -5,7 +5,8 @@ use simplexpr::SimplExpr; use crate::{ error::AstResult, parser::{ - ast::{Ast, AstIterator, Span}, + ast::{Ast, Span}, + ast_iterator::AstIterator, from_ast::FromAst, }, spanned, diff --git a/src/config/var_definition.rs b/src/config/var_definition.rs index 37948cd..29d5364 100644 --- a/src/config/var_definition.rs +++ b/src/config/var_definition.rs @@ -5,7 +5,8 @@ use simplexpr::{dynval::DynVal, SimplExpr}; use crate::{ error::AstResult, parser::{ - ast::{Ast, AstIterator, Span}, + ast::{Ast, Span}, + ast_iterator::AstIterator, from_ast::{FromAst, FromAstElementContent}, }, spanned, diff --git a/src/config/widget_definition.rs b/src/config/widget_definition.rs index 518286d..7418a71 100644 --- a/src/config/widget_definition.rs +++ b/src/config/widget_definition.rs @@ -5,7 +5,8 @@ use simplexpr::SimplExpr; use crate::{ error::AstResult, parser::{ - ast::{Ast, AstIterator, Span}, + ast::{Ast, Span}, + ast_iterator::AstIterator, from_ast::{FromAst, FromAstElementContent}, }, spanned, diff --git a/src/config/widget_use.rs b/src/config/widget_use.rs index 4bdd06a..6ce0332 100644 --- a/src/config/widget_use.rs +++ b/src/config/widget_use.rs @@ -6,7 +6,8 @@ use crate::{ config::attributes::AttrEntry, error::AstResult, parser::{ - ast::{Ast, AstIterator, Span}, + ast::{Ast, Span}, + ast_iterator::AstIterator, from_ast::FromAst, }, spanned, diff --git a/src/config/window_definition.rs b/src/config/window_definition.rs index bc792b8..1ae9c99 100644 --- a/src/config/window_definition.rs +++ b/src/config/window_definition.rs @@ -5,7 +5,8 @@ use simplexpr::{dynval::DynVal, SimplExpr}; use crate::{ error::{AstError, AstResult}, parser::{ - ast::{Ast, AstIterator, Span}, + ast::{Ast, Span}, + ast_iterator::AstIterator, from_ast::{FromAst, FromAstElementContent}, }, spanned, diff --git a/src/config/window_geometry.rs b/src/config/window_geometry.rs index 599984e..db9f93d 100644 --- a/src/config/window_geometry.rs +++ b/src/config/window_geometry.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use simplexpr::{dynval::DynVal, SimplExpr}; use crate::{enum_parse, error::{AstError, AstResult}, parser::{ - ast::{Ast, AstIterator, Span}, + ast::{Ast, Span}, ast_iterator::AstIterator, from_ast::{FromAst, FromAstElementContent}, }, spanned, value::{AttrName, Coords, VarName}}; diff --git a/src/parser/ast.rs b/src/parser/ast.rs index a9f0903..eb0b45a 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -4,7 +4,7 @@ use std::collections::HashMap; use std::fmt::Display; -use super::from_ast::FromAst; +use super::{ast_iterator::AstIterator, from_ast::FromAst}; use crate::{ config::attributes::{AttrEntry, Attributes}, error::{AstError, AstResult, OptionAstErrorExt}, @@ -66,7 +66,7 @@ pub enum AstType { Literal, SimplExpr, Comment, - // A value that could be used as a [SimplExpr] + /// A value that could be used as a [SimplExpr] IntoPrimitive, } @@ -185,89 +185,3 @@ impl std::fmt::Debug for Ast { //} } } - -pub struct AstIterator> { - remaining_span: Span, - iter: itertools::PutBack, -} - -macro_rules! return_or_put_back { - ($name:ident, $expr_type:expr, $t:ty = $p:pat => $ret:expr) => { - pub fn $name(&mut self) -> AstResult<$t> { - let expr_type = $expr_type; - match self.next() { - Some($p) => { - let (span, value) = $ret; - self.remaining_span.1 = span.1; - Ok((span, value)) - } - Some(other) => { - let span = other.span(); - let actual_type = other.expr_type(); - self.iter.put_back(other); - Err(AstError::WrongExprType(Some(span), expr_type, actual_type)) - } - None => Err(AstError::MissingNode(None)), - } - } - }; -} - -impl> AstIterator { - return_or_put_back!(expect_symbol, AstType::Symbol, (Span, String) = Ast::Symbol(span, x) => (span, x)); - - return_or_put_back!(expect_literal, AstType::Literal, (Span, DynVal) = Ast::Literal(span, x) => (span, x)); - - return_or_put_back!(expect_list, AstType::List, (Span, Vec) = Ast::List(span, x) => (span, x)); - - return_or_put_back!(expect_array, AstType::Array, (Span, Vec) = Ast::Array(span, x) => (span, x)); - - pub fn new(span: Span, iter: I) -> Self { - AstIterator { remaining_span: span, iter: itertools::put_back(iter) } - } - - pub fn expect_any(&mut self) -> AstResult { - self.iter.next().or_missing().and_then(T::from_ast) - } - - pub fn expect_key_values(&mut self) -> AstResult { - parse_key_values(self) - } -} - -impl> Iterator for AstIterator { - type Item = Ast; - - fn next(&mut self) -> Option { - self.iter.next() - } -} - -/// Parse consecutive `:keyword value` pairs from an expression iterator into an [Attributes]. -fn parse_key_values(iter: &mut AstIterator>) -> AstResult { - let mut data = HashMap::new(); - let mut attrs_span = Span(iter.remaining_span.0, iter.remaining_span.0, iter.remaining_span.1); - loop { - match iter.next() { - Some(Ast::Keyword(key_span, kw)) => match iter.next() { - Some(value) => { - attrs_span.1 = iter.remaining_span.0; - let attr_value = AttrEntry { key_span, value }; - data.insert(AttrName(kw), attr_value); - } - None => { - iter.iter.put_back(Ast::Keyword(key_span, kw)); - attrs_span.1 = iter.remaining_span.0; - return Ok(Attributes::new(attrs_span, data)); - } - }, - next => { - if let Some(expr) = next { - iter.iter.put_back(expr); - } - attrs_span.1 = iter.remaining_span.0; - return Ok(Attributes::new(attrs_span, data)); - } - } - } -} diff --git a/src/parser/ast_iterator.rs b/src/parser/ast_iterator.rs new file mode 100644 index 0000000..097e330 --- /dev/null +++ b/src/parser/ast_iterator.rs @@ -0,0 +1,101 @@ +use itertools::Itertools; +use simplexpr::{ast::SimplExpr, dynval::DynVal}; +use std::collections::HashMap; + +use std::fmt::Display; + +use super::{ + ast::{Ast, AstType, Span}, + from_ast::FromAst, +}; +use crate::{ + config::attributes::{AttrEntry, Attributes}, + error::{AstError, AstResult, OptionAstErrorExt}, + value::AttrName, +}; + +pub struct AstIterator> { + remaining_span: Span, + iter: itertools::PutBack, +} + +macro_rules! return_or_put_back { + ($name:ident, $expr_type:expr, $t:ty = $p:pat => $ret:expr) => { + pub fn $name(&mut self) -> AstResult<$t> { + let expr_type = $expr_type; + match self.next() { + Some($p) => { + let (span, value) = $ret; + self.remaining_span.1 = span.1; + Ok((span, value)) + } + Some(other) => { + let span = other.span(); + let actual_type = other.expr_type(); + self.iter.put_back(other); + Err(AstError::WrongExprType(Some(span), expr_type, actual_type)) + } + None => Err(AstError::MissingNode(None)), + } + } + }; +} + +impl> AstIterator { + return_or_put_back!(expect_symbol, AstType::Symbol, (Span, String) = Ast::Symbol(span, x) => (span, x)); + + return_or_put_back!(expect_literal, AstType::Literal, (Span, DynVal) = Ast::Literal(span, x) => (span, x)); + + return_or_put_back!(expect_list, AstType::List, (Span, Vec) = Ast::List(span, x) => (span, x)); + + return_or_put_back!(expect_array, AstType::Array, (Span, Vec) = Ast::Array(span, x) => (span, x)); + + pub fn new(span: Span, iter: I) -> Self { + AstIterator { remaining_span: span, iter: itertools::put_back(iter) } + } + + pub fn expect_any(&mut self) -> AstResult { + self.iter.next().or_missing().and_then(T::from_ast) + } + + pub fn expect_key_values(&mut self) -> AstResult { + parse_key_values(self) + } +} + +impl> Iterator for AstIterator { + type Item = Ast; + + fn next(&mut self) -> Option { + self.iter.next() + } +} + +/// Parse consecutive `:keyword value` pairs from an expression iterator into an [Attributes]. +fn parse_key_values(iter: &mut AstIterator>) -> AstResult { + let mut data = HashMap::new(); + let mut attrs_span = Span(iter.remaining_span.0, iter.remaining_span.0, iter.remaining_span.1); + loop { + match iter.next() { + Some(Ast::Keyword(key_span, kw)) => match iter.next() { + Some(value) => { + attrs_span.1 = iter.remaining_span.0; + let attr_value = AttrEntry { key_span, value }; + data.insert(AttrName(kw), attr_value); + } + None => { + iter.iter.put_back(Ast::Keyword(key_span, kw)); + attrs_span.1 = iter.remaining_span.0; + return Ok(Attributes::new(attrs_span, data)); + } + }, + next => { + if let Some(expr) = next { + iter.iter.put_back(expr); + } + attrs_span.1 = iter.remaining_span.0; + return Ok(Attributes::new(attrs_span, data)); + } + } + } +} diff --git a/src/parser/from_ast.rs b/src/parser/from_ast.rs index 21ab4bd..ed495d8 100644 --- a/src/parser/from_ast.rs +++ b/src/parser/from_ast.rs @@ -1,4 +1,7 @@ -use super::ast::{Ast, AstIterator, AstType, Span}; +use super::{ + ast::{Ast, AstType, Span}, + ast_iterator::AstIterator, +}; use crate::{error::*, parser, spanned, util, value::AttrName}; use itertools::Itertools; use simplexpr::{ast::SimplExpr, dynval::DynVal}; diff --git a/src/parser/mod.rs b/src/parser/mod.rs index d0a99d2..c4654b2 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -8,6 +8,7 @@ use std::{fmt::Display, ops::Deref}; use itertools::Itertools; pub mod ast; +pub mod ast_iterator; pub mod from_ast; pub(crate) mod lexer; pub(crate) mod parse_error; From d8575073f1c432e2ac67ec5a5d480deab60604ce Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Wed, 21 Jul 2021 19:02:38 +0200 Subject: [PATCH 054/137] remove unused code --- examples/errors.rs | 26 +++---- src/config/config.rs | 31 ++++----- src/config/script_var_definition.rs | 5 +- src/config/validate.rs | 1 - src/config/var_definition.rs | 1 - src/config/widget_definition.rs | 1 - src/config/widget_use.rs | 47 ++++++------- src/config/window_definition.rs | 1 - src/config/window_geometry.rs | 2 +- src/error.rs | 101 ++++++++++------------------ src/format_diagnostic.rs | 22 ++++-- src/parser/ast.rs | 6 +- src/parser/ast_iterator.rs | 11 ++- src/parser/from_ast.rs | 18 +++-- src/util.rs | 14 ---- 15 files changed, 115 insertions(+), 172 deletions(-) diff --git a/examples/errors.rs b/examples/errors.rs index f86bf68..a5b26ba 100644 --- a/examples/errors.rs +++ b/examples/errors.rs @@ -1,20 +1,20 @@ -use eww_config::{ - format_diagnostic::ToDiagnostic, - parser::{ast::*, from_ast::FromAst}, -}; +// use eww_config::{ +// format_diagnostic::ToDiagnostic, +// parser::{ast::*, from_ast::FromAst}, +//}; fn main() { - let mut files = codespan_reporting::files::SimpleFiles::new(); + // let mut files = codespan_reporting::files::SimpleFiles::new(); - let input = r#" - (heyho ; :foo { "foo \" } bar " } - ; :baz {(foo == bar ? 12.2 : 12)} - (foo) - (defwidget foo [something bla] "foo") - (baz))"#; + // let input = r#" + //(heyho ; :foo { "foo \" } bar " } + //; :baz {(foo == bar ? 12.2 : 12)} + //(foo) + //(defwidget foo [something bla] "foo") + //(baz))"#; - let file_id = files.add("foo.eww", input); - let ast = eww_config::parser::parse_string(file_id, input); + // let file_id = files.add("foo.eww", input); + // let ast = eww_config::parser::parse_string(file_id, input); // match ast.and_then(eww_config::parser::from_ast::Element::::from_ast) { // Ok(ast) => { // println!("{:?}", ast); diff --git a/src/config/config.rs b/src/config/config.rs index 556c850..b969b4f 100644 --- a/src/config/config.rs +++ b/src/config/config.rs @@ -14,7 +14,6 @@ use crate::{ ast_iterator::AstIterator, from_ast::{FromAst, FromAstElementContent}, }, - spanned, value::{AttrName, VarName}, }; @@ -28,25 +27,19 @@ pub enum TopLevel { impl FromAst for TopLevel { fn from_ast(e: Ast) -> AstResult { let span = e.span(); - spanned!(e.span(), { - let mut iter = e.try_ast_iter()?; - let (sym_span, element_name) = iter.expect_symbol()?; - match element_name.as_str() { - x if x == WidgetDefinition::get_element_name() => { - Self::WidgetDefinition(WidgetDefinition::from_tail(span, iter)?) - } - x if x == VarDefinition::get_element_name() => Self::VarDefinition(VarDefinition::from_tail(span, iter)?), - x if x == PollScriptVar::get_element_name() => { - Self::ScriptVarDefinition(ScriptVarDefinition::Poll(PollScriptVar::from_tail(span, iter)?)) - } - x if x == TailScriptVar::get_element_name() => { - Self::ScriptVarDefinition(ScriptVarDefinition::Tail(TailScriptVar::from_tail(span, iter)?)) - } - x if x == WindowDefinition::get_element_name() => { - Self::WindowDefinition(WindowDefinition::from_tail(span, iter)?) - } - x => return Err(AstError::UnknownToplevel(Some(sym_span), x.to_string())), + let mut iter = e.try_ast_iter()?; + let (sym_span, element_name) = iter.expect_symbol()?; + Ok(match element_name.as_str() { + x if x == WidgetDefinition::get_element_name() => Self::WidgetDefinition(WidgetDefinition::from_tail(span, iter)?), + x if x == VarDefinition::get_element_name() => Self::VarDefinition(VarDefinition::from_tail(span, iter)?), + x if x == PollScriptVar::get_element_name() => { + Self::ScriptVarDefinition(ScriptVarDefinition::Poll(PollScriptVar::from_tail(span, iter)?)) } + x if x == TailScriptVar::get_element_name() => { + Self::ScriptVarDefinition(ScriptVarDefinition::Tail(TailScriptVar::from_tail(span, iter)?)) + } + x if x == WindowDefinition::get_element_name() => Self::WindowDefinition(WindowDefinition::from_tail(span, iter)?), + x => return Err(AstError::UnknownToplevel(sym_span, x.to_string())), }) } } diff --git a/src/config/script_var_definition.rs b/src/config/script_var_definition.rs index 4089cda..2d12424 100644 --- a/src/config/script_var_definition.rs +++ b/src/config/script_var_definition.rs @@ -9,7 +9,6 @@ use crate::{ ast_iterator::AstIterator, from_ast::{FromAst, FromAstElementContent}, }, - spanned, value::{AttrName, VarName}, }; @@ -50,8 +49,8 @@ impl FromAstElementContent for PollScriptVar { fn from_tail>(span: Span, mut iter: AstIterator) -> AstResult { let (_, name) = iter.expect_symbol()?; let mut attrs = iter.expect_key_values()?; - let interval: String = attrs.primitive_required("interval")?; - let interval = crate::util::parse_duration(&interval).map_err(|e| AstError::Other(Some(span), e.into()))?; + let interval = attrs.primitive_required::("interval")?.as_duration()?; + // let interval = interval.as_duration()?; let (_, script) = iter.expect_literal()?; Ok(Self { name: VarName(name), command: VarSource::Shell(script.to_string()), interval }) } diff --git a/src/config/validate.rs b/src/config/validate.rs index e4ca804..153656a 100644 --- a/src/config/validate.rs +++ b/src/config/validate.rs @@ -9,7 +9,6 @@ use crate::{ ast_iterator::AstIterator, from_ast::FromAst, }, - spanned, value::{AttrName, VarName}, }; diff --git a/src/config/var_definition.rs b/src/config/var_definition.rs index 29d5364..da458d5 100644 --- a/src/config/var_definition.rs +++ b/src/config/var_definition.rs @@ -9,7 +9,6 @@ use crate::{ ast_iterator::AstIterator, from_ast::{FromAst, FromAstElementContent}, }, - spanned, value::{AttrName, VarName}, }; diff --git a/src/config/widget_definition.rs b/src/config/widget_definition.rs index 7418a71..b7bf2d9 100644 --- a/src/config/widget_definition.rs +++ b/src/config/widget_definition.rs @@ -9,7 +9,6 @@ use crate::{ ast_iterator::AstIterator, from_ast::{FromAst, FromAstElementContent}, }, - spanned, value::{AttrName, VarName}, }; diff --git a/src/config/widget_use.rs b/src/config/widget_use.rs index 6ce0332..78a6cfc 100644 --- a/src/config/widget_use.rs +++ b/src/config/widget_use.rs @@ -10,7 +10,6 @@ use crate::{ ast_iterator::AstIterator, from_ast::FromAst, }, - spanned, value::AttrName, }; @@ -27,29 +26,27 @@ pub struct WidgetUse { impl FromAst for WidgetUse { fn from_ast(e: Ast) -> AstResult { let span = e.span(); - spanned!(e.span(), { - if let Ok(text) = e.as_literal_ref() { - Self { - name: "text".to_string(), - attrs: Attributes::new( - span.into(), - maplit::hashmap! { - AttrName("text".to_string()) => AttrEntry::new( - span.into(), - Ast::Literal(span.into(), text.clone()) - ) - }, - ), - children: Vec::new(), - span, - } - } else { - let mut iter = e.try_ast_iter()?; - let (_, name) = iter.expect_symbol()?; - let attrs = iter.expect_key_values()?; - let children = iter.map(WidgetUse::from_ast).collect::>>()?; - Self { name, attrs, children, span } - } - }) + if let Ok(text) = e.as_literal_ref() { + Ok(Self { + name: "text".to_string(), + attrs: Attributes::new( + span.into(), + maplit::hashmap! { + AttrName("text".to_string()) => AttrEntry::new( + span.into(), + Ast::Literal(span.into(), text.clone()) + ) + }, + ), + children: Vec::new(), + span, + }) + } else { + let mut iter = e.try_ast_iter()?; + let (_, name) = iter.expect_symbol()?; + let attrs = iter.expect_key_values()?; + let children = iter.map(WidgetUse::from_ast).collect::>>()?; + Ok(Self { name, attrs, children, span }) + } } } diff --git a/src/config/window_definition.rs b/src/config/window_definition.rs index 1ae9c99..f49f940 100644 --- a/src/config/window_definition.rs +++ b/src/config/window_definition.rs @@ -9,7 +9,6 @@ use crate::{ ast_iterator::AstIterator, from_ast::{FromAst, FromAstElementContent}, }, - spanned, value::{AttrName, NumWithUnit, VarName}, }; diff --git a/src/config/window_geometry.rs b/src/config/window_geometry.rs index db9f93d..16aac88 100644 --- a/src/config/window_geometry.rs +++ b/src/config/window_geometry.rs @@ -5,7 +5,7 @@ use simplexpr::{dynval::DynVal, SimplExpr}; use crate::{enum_parse, error::{AstError, AstResult}, parser::{ ast::{Ast, Span}, ast_iterator::AstIterator, from_ast::{FromAst, FromAstElementContent}, - }, spanned, value::{AttrName, Coords, VarName}}; + }, value::{AttrName, Coords, VarName}}; use super::{widget_use::WidgetUse, window_definition::EnumParseError}; use serde::{Serialize, Deserialize}; diff --git a/src/error.rs b/src/error.rs index 53126f9..ac99e07 100644 --- a/src/error.rs +++ b/src/error.rs @@ -6,6 +6,7 @@ use crate::{ }, }; use codespan_reporting::{diagnostic, files}; +use simplexpr::dynval; use thiserror::Error; pub type AstResult = Result; @@ -13,15 +14,18 @@ pub type AstResult = Result; #[derive(Debug, Error)] pub enum AstError { #[error("Unknown toplevel declaration `{1}`")] - UnknownToplevel(Option, String), + UnknownToplevel(Span, String), #[error("Expected another element, but got nothing")] - MissingNode(Option), + MissingNode(Span), #[error("Wrong type of expression: Expected {1} but got {2}")] - WrongExprType(Option, AstType, AstType), + WrongExprType(Span, AstType, AstType), #[error("Expected to get a value, but got {1}")] - NotAValue(Option, AstType), + NotAValue(Span, AstType), #[error("Expected element {1}, but read {2}")] - MismatchedElementName(Option, String, String), + MismatchedElementName(Span, String, String), + + #[error(transparent)] + ConversionError(#[from] dynval::ConversionError), #[error("{1}")] Other(Option, Box), @@ -29,13 +33,6 @@ pub enum AstError { #[error(transparent)] AttrError(#[from] AttrError), - //#[error("{msg}: {source}")] - // Context { - // span: Option, - //#[source] - // source: Box, - // msg: String, - //}, #[error(transparent)] ValidationError(#[from] ValidationError), @@ -46,14 +43,14 @@ pub enum AstError { impl AstError { pub fn get_span(&self) -> Option { match self { - AstError::UnknownToplevel(span, _) => *span, - AstError::MissingNode(span) => *span, - AstError::WrongExprType(span, ..) => *span, - AstError::NotAValue(span, ..) => *span, - AstError::MismatchedElementName(span, ..) => *span, + AstError::UnknownToplevel(span, _) => Some(*span), + AstError::MissingNode(span) => Some(*span), + AstError::WrongExprType(span, ..) => Some(*span), + AstError::NotAValue(span, ..) => Some(*span), + AstError::MismatchedElementName(span, ..) => Some(*span), AstError::AttrError(err) => Some(err.span()), AstError::Other(span, ..) => *span, - // AstError::Context { span, .. } => *span, + AstError::ConversionError(err) => err.value.span().map(|x| x.into()), AstError::ValidationError(error) => None, // TODO none here is stupid AstError::ParseError { file_id, source } => file_id.and_then(|id| get_parse_error_span(id, source)), } @@ -83,55 +80,25 @@ fn get_parse_error_span( } } -pub fn spanned(span: Span, err: impl Into) -> AstError { - use AstError::*; - match err.into() { - UnknownToplevel(None, x) => UnknownToplevel(Some(span), x), - MissingNode(None) => MissingNode(Some(span)), - WrongExprType(None, x, y) => WrongExprType(Some(span), x, y), - UnknownToplevel(None, x) => UnknownToplevel(Some(span), x), - MissingNode(None) => MissingNode(Some(span)), - NotAValue(None, x) => NotAValue(Some(span), x), - MismatchedElementName(None, x, y) => MismatchedElementName(Some(span), x, y), - // Context { span: None, source, msg } => Context { span: Some(span), source, msg }, - Other(None, x) => Other(Some(span), x), - x => x, - } -} - -pub trait OptionAstErrorExt { - fn or_missing(self) -> Result; -} -impl OptionAstErrorExt for Option { - fn or_missing(self) -> Result { - self.ok_or(AstError::MissingNode(None)) - } -} - -pub trait AstResultExt { - fn at(self, span: Span) -> Result; -} - -pub trait Context { - fn context(self, span: Span, msg: String) -> Result; -} - -impl> AstResultExt for Result { - fn at(self, span: Span) -> Result { - self.map_err(|err| spanned(span, err)) - } -} - -// impl Context for Result { -// fn context(self, span: Span, msg: String) -> Result { -// self.map_err(|x| AstError::Context { msg, span: Some(span), source: Box::new(x) }) +// pub fn spanned(span: Span, err: impl Into) -> AstError { +// use AstError::*; +// match err.into() { +// UnknownToplevel(s, x) => UnknownToplevel(Some(s.unwrap_or(span)), x), +// MissingNode(s) => MissingNode(Some(s.unwrap_or(span))), +// WrongExprType(s, x, y) => WrongExprType(Some(s.unwrap_or(span)), x, y), +// UnknownToplevel(s, x) => UnknownToplevel(Some(s.unwrap_or(span)), x), +// MissingNode(s) => MissingNode(Some(s.unwrap_or(span))), +// NotAValue(s, x) => NotAValue(Some(s.unwrap_or(span)), x), +// MismatchedElementName(s, expected, got) => MismatchedElementName(Some(s.unwrap_or(span)), expected, got), +// Other(s, x) => Other(Some(s.unwrap_or(span)), x), +// x @ ConversionError(_) | x @ AttrError(_) | x @ ValidationError(_) | x @ ParseError { .. } => x, //} -#[macro_export] -macro_rules! spanned { - ($span:expr, $block:expr) => {{ - let span = $span; - let result: Result<_, crate::error::AstError> = try { $block }; - crate::error::AstResultExt::at(result, span) - }}; +pub trait OptionAstErrorExt { + fn or_missing(self, span: Span) -> Result; +} +impl OptionAstErrorExt for Option { + fn or_missing(self, span: Span) -> Result { + self.ok_or(AstError::MissingNode(span)) + } } diff --git a/src/format_diagnostic.rs b/src/format_diagnostic.rs index 6ad8f8f..a1238c3 100644 --- a/src/format_diagnostic.rs +++ b/src/format_diagnostic.rs @@ -14,7 +14,7 @@ macro_rules! gen_diagnostic { $(, note = $note:expr)? $(,)? ) => { Diagnostic::error() - $(.with_message($msg))? + $(.with_message($msg.to_string()))? $(.with_labels(vec![ Label::primary($span.2, $span.0..$span.1) $(.with_message($label))? @@ -23,7 +23,7 @@ macro_rules! gen_diagnostic { }; ($msg:expr $(, $span:expr $(,)?)?) => {{ Diagnostic::error() - .with_message($msg) + .with_message($msg.to_string()) $(.with_labels(vec![Label::primary($span.2, $span.0..$span.1)]))? }}; } @@ -64,7 +64,7 @@ impl ToDiagnostic for AstError { note = format!("Expected: {}\nGot: {}", expected, actual), }, AstError::NotAValue(_, actual) => gen_diagnostic! { - msg = format!("Expected value, but got {}", actual), + msg = format!("Expected value, but got `{}`", actual), label = span => "Expected some value here", note = format!("Got: {}", actual), }, @@ -73,7 +73,15 @@ impl ToDiagnostic for AstError { parse_error::ParseError::SimplExpr(_, error) => simplexpr_error_to_diagnostic(error, span), parse_error::ParseError::LexicalError(_) => lexical_error_to_diagnostic(span), }), - _ => panic!(), + AstError::MismatchedElementName(_, expected, got) => gen_diagnostic! { + msg = format!("Expected element `{}`, but found `{}`", expected, got), + label = span => format!("Expected `{}` here", expected), + note = format!("Expected: {}\nGot: {}", expected, got), + }, + AstError::ConversionError(err) => conversion_error_to_diagnostic(err, span), + AstError::Other(_, source) => gen_diagnostic!(source, span), + AstError::AttrError(source) => gen_diagnostic!(source, span), + AstError::ValidationError(_) => todo!(), } } else { Diagnostic::error().with_message(format!("{}", self)) @@ -107,9 +115,9 @@ fn simplexpr_error_to_diagnostic(error: &simplexpr::error::Error, span: Span) -> match error { ParseError { source, .. } => lalrpop_error_to_diagnostic(source, span, move |error| lexical_error_to_diagnostic(span)), ConversionError(error) => conversion_error_to_diagnostic(error, span), - Eval(error) => gen_diagnostic!(format!("{}", error), span), - Other(error) => gen_diagnostic!(format!("{}", error), span), - Spanned(_, error) => gen_diagnostic!(format!("{}", error), span), + Eval(error) => gen_diagnostic!(error, span), + Other(error) => gen_diagnostic!(error, span), + Spanned(_, error) => gen_diagnostic!(error, span), } } diff --git a/src/parser/ast.rs b/src/parser/ast.rs index eb0b45a..24a6abc 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -92,14 +92,14 @@ macro_rules! as_func { pub fn $name(self) -> Result<$t, AstError> { match self { $p => Ok($value), - x => Err(AstError::WrongExprType(Some(x.span()), $exprtype, x.expr_type())), + x => Err(AstError::WrongExprType(x.span(), $exprtype, x.expr_type())), } } pub fn $nameref(&self) -> Result<&$t, AstError> { match self { $p => Ok($value), - x => Err(AstError::WrongExprType(Some(x.span()), $exprtype, x.expr_type())), + x => Err(AstError::WrongExprType(x.span(), $exprtype, x.expr_type())), } } }; @@ -145,7 +145,7 @@ impl Ast { // Ast::Symbol(_, _) => todo!(), Ast::Literal(span, x) => Ok(SimplExpr::Literal(span.into(), x)), Ast::SimplExpr(span, x) => Ok(x), - _ => Err(AstError::WrongExprType(Some(self.span()), AstType::IntoPrimitive, self.expr_type())), + _ => Err(AstError::WrongExprType(self.span(), AstType::IntoPrimitive, self.expr_type())), } } diff --git a/src/parser/ast_iterator.rs b/src/parser/ast_iterator.rs index 097e330..6f841d0 100644 --- a/src/parser/ast_iterator.rs +++ b/src/parser/ast_iterator.rs @@ -23,19 +23,18 @@ macro_rules! return_or_put_back { ($name:ident, $expr_type:expr, $t:ty = $p:pat => $ret:expr) => { pub fn $name(&mut self) -> AstResult<$t> { let expr_type = $expr_type; - match self.next() { - Some($p) => { + match self.expect_any()? { + $p => { let (span, value) = $ret; self.remaining_span.1 = span.1; Ok((span, value)) } - Some(other) => { + other => { let span = other.span(); let actual_type = other.expr_type(); self.iter.put_back(other); - Err(AstError::WrongExprType(Some(span), expr_type, actual_type)) + Err(AstError::WrongExprType(span, expr_type, actual_type)) } - None => Err(AstError::MissingNode(None)), } } }; @@ -55,7 +54,7 @@ impl> AstIterator { } pub fn expect_any(&mut self) -> AstResult { - self.iter.next().or_missing().and_then(T::from_ast) + self.iter.next().or_missing(self.remaining_span.with_length(0)).and_then(T::from_ast) } pub fn expect_key_values(&mut self) -> AstResult { diff --git a/src/parser/from_ast.rs b/src/parser/from_ast.rs index ed495d8..bca20b4 100644 --- a/src/parser/from_ast.rs +++ b/src/parser/from_ast.rs @@ -2,7 +2,7 @@ use super::{ ast::{Ast, AstType, Span}, ast_iterator::AstIterator, }; -use crate::{error::*, parser, spanned, util, value::AttrName}; +use crate::{error::*, parser, util, value::AttrName}; use itertools::Itertools; use simplexpr::{ast::SimplExpr, dynval::DynVal}; use std::{ @@ -37,14 +37,12 @@ pub trait FromAstElementContent: Sized { impl FromAst for T { fn from_ast(e: Ast) -> AstResult { let span = e.span(); - spanned!(e.span(), { - let mut iter = e.try_ast_iter()?; - let (_, element_name) = iter.expect_symbol()?; - if Self::get_element_name() != element_name { - return Err(AstError::MismatchedElementName(Some(span), Self::get_element_name().to_string(), element_name)); - } - Self::from_tail(span, iter)? - }) + let mut iter = e.try_ast_iter()?; + let (_, element_name) = iter.expect_symbol()?; + if Self::get_element_name() != element_name { + return Err(AstError::MismatchedElementName(span, Self::get_element_name().to_string(), element_name)); + } + Self::from_tail(span, iter) } } @@ -54,7 +52,7 @@ impl FromAst for SimplExpr { Ast::Symbol(span, x) => Ok(SimplExpr::VarRef(span.into(), x)), Ast::Literal(span, x) => Ok(SimplExpr::Literal(span.into(), x)), Ast::SimplExpr(span, x) => Ok(x), - _ => Err(AstError::NotAValue(Some(e.span()), e.expr_type())), + _ => Err(AstError::NotAValue(e.span(), e.expr_type())), } } } diff --git a/src/util.rs b/src/util.rs index a15474f..e69de29 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,14 +0,0 @@ -pub fn parse_duration(s: &str) -> anyhow::Result { - use std::time::Duration; - if s.ends_with("ms") { - Ok(Duration::from_millis(s.trim_end_matches("ms").parse()?)) - } else if s.ends_with('s') { - Ok(Duration::from_secs(s.trim_end_matches('s').parse()?)) - } else if s.ends_with('m') { - Ok(Duration::from_secs(s.trim_end_matches('m').parse::()? * 60)) - } else if s.ends_with('h') { - Ok(Duration::from_secs(s.trim_end_matches('h').parse::()? * 60 * 60)) - } else { - Err(anyhow::anyhow!("Failed to parse duration `{}`", s)) - } -} From 6f51f263cd684cbf76f2e6cf45d89aa47e6cd565 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Wed, 21 Jul 2021 19:15:23 +0200 Subject: [PATCH 055/137] cleanup dynval --- src/dynval.rs | 58 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 24 deletions(-) diff --git a/src/dynval.rs b/src/dynval.rs index 463eae7..2ee7c5a 100644 --- a/src/dynval.rs +++ b/src/dynval.rs @@ -6,7 +6,7 @@ use std::{fmt, iter::FromIterator, str::FromStr}; pub type Result = std::result::Result; #[derive(Debug, thiserror::Error)] -#[error("Failed to turn {value} into a {target_type}")] +#[error("Failed to turn {value} into a value of type {target_type}")] pub struct ConversionError { pub value: DynVal, pub target_type: &'static str, @@ -14,8 +14,8 @@ pub struct ConversionError { } impl ConversionError { - fn new(value: DynVal, target_type: &'static str, source: Box) -> Self { - ConversionError { value, target_type, source: Some(source) } + 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 { @@ -83,16 +83,6 @@ impl> FromDynVal for T { } } -macro_rules! impl_from_dynval { - ( - $(for $for:ty => |$name:ident| $code:expr);*; - ) => { - $(impl FromDynVal for $for { - type Err = ConversionError; - fn from_dynval($name: DynVal) -> std::result::Result { $code } - })* - }; -} macro_rules! impl_dynval_from { ($($t:ty),*) => { $(impl From<$t> for DynVal { @@ -101,14 +91,6 @@ macro_rules! impl_dynval_from { }; } -// impl_from_dynval! { -// for String => |x| x.as_string(); -// for f64 => |x| x.as_f64(); -// for i32 => |x| x.as_i32(); -// for bool => |x| x.as_bool(); -////for Vec => |x| x.as_vec(); -//} - impl_dynval_from!(bool, i32, u32, f32, u8, f64, &str); impl From<&serde_json::Value> for DynVal { @@ -128,6 +110,10 @@ impl DynVal { DynVal(self.0, Some(span)) } + pub fn span(&self) -> Option { + self.1 + } + pub fn from_string(s: String) -> Self { DynVal(s, None) } @@ -146,15 +132,39 @@ impl DynVal { } pub fn as_f64(&self) -> Result { - self.0.parse().map_err(|e| ConversionError::new(self.clone(), "f64", Box::new(e))) + self.0.parse().map_err(|e| ConversionError::new(self.clone(), "f64", e)) } pub fn as_i32(&self) -> Result { - self.0.parse().map_err(|e| ConversionError::new(self.clone(), "i32", Box::new(e))) + self.0.parse().map_err(|e| ConversionError::new(self.clone(), "i32", e)) } pub fn as_bool(&self) -> Result { - self.0.parse().map_err(|e| ConversionError::new(self.clone(), "bool", Box::new(e))) + self.0.parse().map_err(|e| ConversionError::new(self.clone(), "bool", e)) + } + + pub fn as_duration(&self) -> Result { + 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::().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::().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> { From 0c137c8b209180fd4ef4aaf87e119db2b9a101c4 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Wed, 21 Jul 2021 19:18:43 +0200 Subject: [PATCH 056/137] Move to crates directory --- .gitignore | 2 +- crates/simplexpr/Cargo.lock | 656 ++++++++++++++++++ Cargo.toml => crates/simplexpr/Cargo.toml | 0 README.md => crates/simplexpr/README.md | 0 build.rs => crates/simplexpr/build.rs | 0 .../simplexpr/rust-toolchain | 0 rustfmt.toml => crates/simplexpr/rustfmt.toml | 0 {src => crates/simplexpr/src}/EvalError.rs | 0 {src => crates/simplexpr/src}/ast.rs | 0 {src => crates/simplexpr/src}/dynval.rs | 0 {src => crates/simplexpr/src}/error.rs | 0 {src => crates/simplexpr/src}/eval.rs | 0 {src => crates/simplexpr/src}/lib.rs | 0 .../simplexpr/src}/parser/lalrpop_helpers.rs | 0 {src => crates/simplexpr/src}/parser/lexer.rs | 0 {src => crates/simplexpr/src}/parser/mod.rs | 0 .../simplexpr/src}/simplexpr_parser.lalrpop | 0 .../snapshots/simplexpr__tests__test-10.snap | 0 .../snapshots/simplexpr__tests__test-11.snap | 0 .../snapshots/simplexpr__tests__test-12.snap | 0 .../snapshots/simplexpr__tests__test-13.snap | 0 .../snapshots/simplexpr__tests__test-14.snap | 0 .../snapshots/simplexpr__tests__test-15.snap | 0 .../snapshots/simplexpr__tests__test-16.snap | 0 .../snapshots/simplexpr__tests__test-17.snap | 0 .../snapshots/simplexpr__tests__test-18.snap | 0 .../snapshots/simplexpr__tests__test-19.snap | 0 .../snapshots/simplexpr__tests__test-2.snap | 0 .../snapshots/simplexpr__tests__test-20.snap | 0 .../snapshots/simplexpr__tests__test-21.snap | 0 .../snapshots/simplexpr__tests__test-22.snap | 0 .../snapshots/simplexpr__tests__test-23.snap | 0 .../snapshots/simplexpr__tests__test-24.snap | 0 .../snapshots/simplexpr__tests__test-25.snap | 0 .../snapshots/simplexpr__tests__test-26.snap | 0 .../snapshots/simplexpr__tests__test-3.snap | 0 .../snapshots/simplexpr__tests__test-4.snap | 0 .../snapshots/simplexpr__tests__test-5.snap | 0 .../snapshots/simplexpr__tests__test-6.snap | 0 .../snapshots/simplexpr__tests__test-7.snap | 0 .../snapshots/simplexpr__tests__test-8.snap | 0 .../snapshots/simplexpr__tests__test-9.snap | 0 .../snapshots/simplexpr__tests__test.snap | 0 43 files changed, 657 insertions(+), 1 deletion(-) create mode 100644 crates/simplexpr/Cargo.lock rename Cargo.toml => crates/simplexpr/Cargo.toml (100%) rename README.md => crates/simplexpr/README.md (100%) rename build.rs => crates/simplexpr/build.rs (100%) rename rust-toolchain => crates/simplexpr/rust-toolchain (100%) rename rustfmt.toml => crates/simplexpr/rustfmt.toml (100%) rename {src => crates/simplexpr/src}/EvalError.rs (100%) rename {src => crates/simplexpr/src}/ast.rs (100%) rename {src => crates/simplexpr/src}/dynval.rs (100%) rename {src => crates/simplexpr/src}/error.rs (100%) rename {src => crates/simplexpr/src}/eval.rs (100%) rename {src => crates/simplexpr/src}/lib.rs (100%) rename {src => crates/simplexpr/src}/parser/lalrpop_helpers.rs (100%) rename {src => crates/simplexpr/src}/parser/lexer.rs (100%) rename {src => crates/simplexpr/src}/parser/mod.rs (100%) rename {src => crates/simplexpr/src}/simplexpr_parser.lalrpop (100%) rename {src => crates/simplexpr/src}/snapshots/simplexpr__tests__test-10.snap (100%) rename {src => crates/simplexpr/src}/snapshots/simplexpr__tests__test-11.snap (100%) rename {src => crates/simplexpr/src}/snapshots/simplexpr__tests__test-12.snap (100%) rename {src => crates/simplexpr/src}/snapshots/simplexpr__tests__test-13.snap (100%) rename {src => crates/simplexpr/src}/snapshots/simplexpr__tests__test-14.snap (100%) rename {src => crates/simplexpr/src}/snapshots/simplexpr__tests__test-15.snap (100%) rename {src => crates/simplexpr/src}/snapshots/simplexpr__tests__test-16.snap (100%) rename {src => crates/simplexpr/src}/snapshots/simplexpr__tests__test-17.snap (100%) rename {src => crates/simplexpr/src}/snapshots/simplexpr__tests__test-18.snap (100%) rename {src => crates/simplexpr/src}/snapshots/simplexpr__tests__test-19.snap (100%) rename {src => crates/simplexpr/src}/snapshots/simplexpr__tests__test-2.snap (100%) rename {src => crates/simplexpr/src}/snapshots/simplexpr__tests__test-20.snap (100%) rename {src => crates/simplexpr/src}/snapshots/simplexpr__tests__test-21.snap (100%) rename {src => crates/simplexpr/src}/snapshots/simplexpr__tests__test-22.snap (100%) rename {src => crates/simplexpr/src}/snapshots/simplexpr__tests__test-23.snap (100%) rename {src => crates/simplexpr/src}/snapshots/simplexpr__tests__test-24.snap (100%) rename {src => crates/simplexpr/src}/snapshots/simplexpr__tests__test-25.snap (100%) rename {src => crates/simplexpr/src}/snapshots/simplexpr__tests__test-26.snap (100%) rename {src => crates/simplexpr/src}/snapshots/simplexpr__tests__test-3.snap (100%) rename {src => crates/simplexpr/src}/snapshots/simplexpr__tests__test-4.snap (100%) rename {src => crates/simplexpr/src}/snapshots/simplexpr__tests__test-5.snap (100%) rename {src => crates/simplexpr/src}/snapshots/simplexpr__tests__test-6.snap (100%) rename {src => crates/simplexpr/src}/snapshots/simplexpr__tests__test-7.snap (100%) rename {src => crates/simplexpr/src}/snapshots/simplexpr__tests__test-8.snap (100%) rename {src => crates/simplexpr/src}/snapshots/simplexpr__tests__test-9.snap (100%) rename {src => crates/simplexpr/src}/snapshots/simplexpr__tests__test.snap (100%) diff --git a/.gitignore b/.gitignore index 96ef6c0..d03f68d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ /target -Cargo.lock +/**/target diff --git a/crates/simplexpr/Cargo.lock b/crates/simplexpr/Cargo.lock new file mode 100644 index 0000000..774d4b5 --- /dev/null +++ b/crates/simplexpr/Cargo.lock @@ -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", +] diff --git a/Cargo.toml b/crates/simplexpr/Cargo.toml similarity index 100% rename from Cargo.toml rename to crates/simplexpr/Cargo.toml diff --git a/README.md b/crates/simplexpr/README.md similarity index 100% rename from README.md rename to crates/simplexpr/README.md diff --git a/build.rs b/crates/simplexpr/build.rs similarity index 100% rename from build.rs rename to crates/simplexpr/build.rs diff --git a/rust-toolchain b/crates/simplexpr/rust-toolchain similarity index 100% rename from rust-toolchain rename to crates/simplexpr/rust-toolchain diff --git a/rustfmt.toml b/crates/simplexpr/rustfmt.toml similarity index 100% rename from rustfmt.toml rename to crates/simplexpr/rustfmt.toml diff --git a/src/EvalError.rs b/crates/simplexpr/src/EvalError.rs similarity index 100% rename from src/EvalError.rs rename to crates/simplexpr/src/EvalError.rs diff --git a/src/ast.rs b/crates/simplexpr/src/ast.rs similarity index 100% rename from src/ast.rs rename to crates/simplexpr/src/ast.rs diff --git a/src/dynval.rs b/crates/simplexpr/src/dynval.rs similarity index 100% rename from src/dynval.rs rename to crates/simplexpr/src/dynval.rs diff --git a/src/error.rs b/crates/simplexpr/src/error.rs similarity index 100% rename from src/error.rs rename to crates/simplexpr/src/error.rs diff --git a/src/eval.rs b/crates/simplexpr/src/eval.rs similarity index 100% rename from src/eval.rs rename to crates/simplexpr/src/eval.rs diff --git a/src/lib.rs b/crates/simplexpr/src/lib.rs similarity index 100% rename from src/lib.rs rename to crates/simplexpr/src/lib.rs diff --git a/src/parser/lalrpop_helpers.rs b/crates/simplexpr/src/parser/lalrpop_helpers.rs similarity index 100% rename from src/parser/lalrpop_helpers.rs rename to crates/simplexpr/src/parser/lalrpop_helpers.rs diff --git a/src/parser/lexer.rs b/crates/simplexpr/src/parser/lexer.rs similarity index 100% rename from src/parser/lexer.rs rename to crates/simplexpr/src/parser/lexer.rs diff --git a/src/parser/mod.rs b/crates/simplexpr/src/parser/mod.rs similarity index 100% rename from src/parser/mod.rs rename to crates/simplexpr/src/parser/mod.rs diff --git a/src/simplexpr_parser.lalrpop b/crates/simplexpr/src/simplexpr_parser.lalrpop similarity index 100% rename from src/simplexpr_parser.lalrpop rename to crates/simplexpr/src/simplexpr_parser.lalrpop diff --git a/src/snapshots/simplexpr__tests__test-10.snap b/crates/simplexpr/src/snapshots/simplexpr__tests__test-10.snap similarity index 100% rename from src/snapshots/simplexpr__tests__test-10.snap rename to crates/simplexpr/src/snapshots/simplexpr__tests__test-10.snap diff --git a/src/snapshots/simplexpr__tests__test-11.snap b/crates/simplexpr/src/snapshots/simplexpr__tests__test-11.snap similarity index 100% rename from src/snapshots/simplexpr__tests__test-11.snap rename to crates/simplexpr/src/snapshots/simplexpr__tests__test-11.snap diff --git a/src/snapshots/simplexpr__tests__test-12.snap b/crates/simplexpr/src/snapshots/simplexpr__tests__test-12.snap similarity index 100% rename from src/snapshots/simplexpr__tests__test-12.snap rename to crates/simplexpr/src/snapshots/simplexpr__tests__test-12.snap diff --git a/src/snapshots/simplexpr__tests__test-13.snap b/crates/simplexpr/src/snapshots/simplexpr__tests__test-13.snap similarity index 100% rename from src/snapshots/simplexpr__tests__test-13.snap rename to crates/simplexpr/src/snapshots/simplexpr__tests__test-13.snap diff --git a/src/snapshots/simplexpr__tests__test-14.snap b/crates/simplexpr/src/snapshots/simplexpr__tests__test-14.snap similarity index 100% rename from src/snapshots/simplexpr__tests__test-14.snap rename to crates/simplexpr/src/snapshots/simplexpr__tests__test-14.snap diff --git a/src/snapshots/simplexpr__tests__test-15.snap b/crates/simplexpr/src/snapshots/simplexpr__tests__test-15.snap similarity index 100% rename from src/snapshots/simplexpr__tests__test-15.snap rename to crates/simplexpr/src/snapshots/simplexpr__tests__test-15.snap diff --git a/src/snapshots/simplexpr__tests__test-16.snap b/crates/simplexpr/src/snapshots/simplexpr__tests__test-16.snap similarity index 100% rename from src/snapshots/simplexpr__tests__test-16.snap rename to crates/simplexpr/src/snapshots/simplexpr__tests__test-16.snap diff --git a/src/snapshots/simplexpr__tests__test-17.snap b/crates/simplexpr/src/snapshots/simplexpr__tests__test-17.snap similarity index 100% rename from src/snapshots/simplexpr__tests__test-17.snap rename to crates/simplexpr/src/snapshots/simplexpr__tests__test-17.snap diff --git a/src/snapshots/simplexpr__tests__test-18.snap b/crates/simplexpr/src/snapshots/simplexpr__tests__test-18.snap similarity index 100% rename from src/snapshots/simplexpr__tests__test-18.snap rename to crates/simplexpr/src/snapshots/simplexpr__tests__test-18.snap diff --git a/src/snapshots/simplexpr__tests__test-19.snap b/crates/simplexpr/src/snapshots/simplexpr__tests__test-19.snap similarity index 100% rename from src/snapshots/simplexpr__tests__test-19.snap rename to crates/simplexpr/src/snapshots/simplexpr__tests__test-19.snap diff --git a/src/snapshots/simplexpr__tests__test-2.snap b/crates/simplexpr/src/snapshots/simplexpr__tests__test-2.snap similarity index 100% rename from src/snapshots/simplexpr__tests__test-2.snap rename to crates/simplexpr/src/snapshots/simplexpr__tests__test-2.snap diff --git a/src/snapshots/simplexpr__tests__test-20.snap b/crates/simplexpr/src/snapshots/simplexpr__tests__test-20.snap similarity index 100% rename from src/snapshots/simplexpr__tests__test-20.snap rename to crates/simplexpr/src/snapshots/simplexpr__tests__test-20.snap diff --git a/src/snapshots/simplexpr__tests__test-21.snap b/crates/simplexpr/src/snapshots/simplexpr__tests__test-21.snap similarity index 100% rename from src/snapshots/simplexpr__tests__test-21.snap rename to crates/simplexpr/src/snapshots/simplexpr__tests__test-21.snap diff --git a/src/snapshots/simplexpr__tests__test-22.snap b/crates/simplexpr/src/snapshots/simplexpr__tests__test-22.snap similarity index 100% rename from src/snapshots/simplexpr__tests__test-22.snap rename to crates/simplexpr/src/snapshots/simplexpr__tests__test-22.snap diff --git a/src/snapshots/simplexpr__tests__test-23.snap b/crates/simplexpr/src/snapshots/simplexpr__tests__test-23.snap similarity index 100% rename from src/snapshots/simplexpr__tests__test-23.snap rename to crates/simplexpr/src/snapshots/simplexpr__tests__test-23.snap diff --git a/src/snapshots/simplexpr__tests__test-24.snap b/crates/simplexpr/src/snapshots/simplexpr__tests__test-24.snap similarity index 100% rename from src/snapshots/simplexpr__tests__test-24.snap rename to crates/simplexpr/src/snapshots/simplexpr__tests__test-24.snap diff --git a/src/snapshots/simplexpr__tests__test-25.snap b/crates/simplexpr/src/snapshots/simplexpr__tests__test-25.snap similarity index 100% rename from src/snapshots/simplexpr__tests__test-25.snap rename to crates/simplexpr/src/snapshots/simplexpr__tests__test-25.snap diff --git a/src/snapshots/simplexpr__tests__test-26.snap b/crates/simplexpr/src/snapshots/simplexpr__tests__test-26.snap similarity index 100% rename from src/snapshots/simplexpr__tests__test-26.snap rename to crates/simplexpr/src/snapshots/simplexpr__tests__test-26.snap diff --git a/src/snapshots/simplexpr__tests__test-3.snap b/crates/simplexpr/src/snapshots/simplexpr__tests__test-3.snap similarity index 100% rename from src/snapshots/simplexpr__tests__test-3.snap rename to crates/simplexpr/src/snapshots/simplexpr__tests__test-3.snap diff --git a/src/snapshots/simplexpr__tests__test-4.snap b/crates/simplexpr/src/snapshots/simplexpr__tests__test-4.snap similarity index 100% rename from src/snapshots/simplexpr__tests__test-4.snap rename to crates/simplexpr/src/snapshots/simplexpr__tests__test-4.snap diff --git a/src/snapshots/simplexpr__tests__test-5.snap b/crates/simplexpr/src/snapshots/simplexpr__tests__test-5.snap similarity index 100% rename from src/snapshots/simplexpr__tests__test-5.snap rename to crates/simplexpr/src/snapshots/simplexpr__tests__test-5.snap diff --git a/src/snapshots/simplexpr__tests__test-6.snap b/crates/simplexpr/src/snapshots/simplexpr__tests__test-6.snap similarity index 100% rename from src/snapshots/simplexpr__tests__test-6.snap rename to crates/simplexpr/src/snapshots/simplexpr__tests__test-6.snap diff --git a/src/snapshots/simplexpr__tests__test-7.snap b/crates/simplexpr/src/snapshots/simplexpr__tests__test-7.snap similarity index 100% rename from src/snapshots/simplexpr__tests__test-7.snap rename to crates/simplexpr/src/snapshots/simplexpr__tests__test-7.snap diff --git a/src/snapshots/simplexpr__tests__test-8.snap b/crates/simplexpr/src/snapshots/simplexpr__tests__test-8.snap similarity index 100% rename from src/snapshots/simplexpr__tests__test-8.snap rename to crates/simplexpr/src/snapshots/simplexpr__tests__test-8.snap diff --git a/src/snapshots/simplexpr__tests__test-9.snap b/crates/simplexpr/src/snapshots/simplexpr__tests__test-9.snap similarity index 100% rename from src/snapshots/simplexpr__tests__test-9.snap rename to crates/simplexpr/src/snapshots/simplexpr__tests__test-9.snap diff --git a/src/snapshots/simplexpr__tests__test.snap b/crates/simplexpr/src/snapshots/simplexpr__tests__test.snap similarity index 100% rename from src/snapshots/simplexpr__tests__test.snap rename to crates/simplexpr/src/snapshots/simplexpr__tests__test.snap From f2b476aaf67e8f4dc2eecfc7b7ccccc0d4db83fb Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Wed, 21 Jul 2021 19:15:01 +0200 Subject: [PATCH 057/137] Setup cargo workspace --- .gitignore | 1 + Cargo.lock | 24 +- Cargo.toml | 77 +- crates/eww/Cargo.lock | 2069 +++++++++++++++++ crates/eww/Cargo.toml | 73 + rustfmt.toml => crates/eww/rustfmt.toml | 0 {src => crates/eww/src}/app.rs | 0 .../eww/src}/application_lifecycle.rs | 0 {src => crates/eww/src}/client.rs | 0 {src => crates/eww/src}/config/element.rs | 0 {src => crates/eww/src}/config/eww_config.rs | 0 {src => crates/eww/src}/config/inbuilt.rs | 0 {src => crates/eww/src}/config/mod.rs | 0 {src => crates/eww/src}/config/script_var.rs | 0 .../eww/src}/config/system_stats.rs | 0 .../eww/src}/config/window_definition.rs | 0 .../eww/src}/config/window_geometry.rs | 0 {src => crates/eww/src}/config/xml_ext.rs | 0 {src => crates/eww/src}/display_backend.rs | 0 {src => crates/eww/src}/eww_state.rs | 0 {src => crates/eww/src}/geometry.rs | 0 {src => crates/eww/src}/ipc_server.rs | 0 {src => crates/eww/src}/main.rs | 0 {src => crates/eww/src}/opts.rs | 0 {src => crates/eww/src}/script_var_handler.rs | 0 {src => crates/eww/src}/server.rs | 0 {src => crates/eww/src}/util.rs | 0 .../eww/src}/value/attr_value/attr_value.rs | 0 .../src}/value/attr_value/attr_value_expr.rs | 0 .../eww/src}/value/attr_value/mod.rs | 0 .../eww/src}/value/attr_value/parser.rs | 0 {src => crates/eww/src}/value/coords.rs | 0 {src => crates/eww/src}/value/mod.rs | 0 {src => crates/eww/src}/value/primitive.rs | 0 {src => crates/eww/src}/widgets/mod.rs | 0 .../eww/src}/widgets/widget_definitions.rs | 0 .../eww/src}/widgets/widget_node.rs | 0 37 files changed, 2149 insertions(+), 95 deletions(-) create mode 100644 crates/eww/Cargo.lock create mode 100644 crates/eww/Cargo.toml rename rustfmt.toml => crates/eww/rustfmt.toml (100%) rename {src => crates/eww/src}/app.rs (100%) rename {src => crates/eww/src}/application_lifecycle.rs (100%) rename {src => crates/eww/src}/client.rs (100%) rename {src => crates/eww/src}/config/element.rs (100%) rename {src => crates/eww/src}/config/eww_config.rs (100%) rename {src => crates/eww/src}/config/inbuilt.rs (100%) rename {src => crates/eww/src}/config/mod.rs (100%) rename {src => crates/eww/src}/config/script_var.rs (100%) rename {src => crates/eww/src}/config/system_stats.rs (100%) rename {src => crates/eww/src}/config/window_definition.rs (100%) rename {src => crates/eww/src}/config/window_geometry.rs (100%) rename {src => crates/eww/src}/config/xml_ext.rs (100%) rename {src => crates/eww/src}/display_backend.rs (100%) rename {src => crates/eww/src}/eww_state.rs (100%) rename {src => crates/eww/src}/geometry.rs (100%) rename {src => crates/eww/src}/ipc_server.rs (100%) rename {src => crates/eww/src}/main.rs (100%) rename {src => crates/eww/src}/opts.rs (100%) rename {src => crates/eww/src}/script_var_handler.rs (100%) rename {src => crates/eww/src}/server.rs (100%) rename {src => crates/eww/src}/util.rs (100%) rename {src => crates/eww/src}/value/attr_value/attr_value.rs (100%) rename {src => crates/eww/src}/value/attr_value/attr_value_expr.rs (100%) rename {src => crates/eww/src}/value/attr_value/mod.rs (100%) rename {src => crates/eww/src}/value/attr_value/parser.rs (100%) rename {src => crates/eww/src}/value/coords.rs (100%) rename {src => crates/eww/src}/value/mod.rs (100%) rename {src => crates/eww/src}/value/primitive.rs (100%) rename {src => crates/eww/src}/widgets/mod.rs (100%) rename {src => crates/eww/src}/widgets/widget_definitions.rs (100%) rename {src => crates/eww/src}/widgets/widget_node.rs (100%) diff --git a/.gitignore b/.gitignore index ea8c4bf..d03f68d 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +/**/target diff --git a/Cargo.lock b/Cargo.lock index 8eb7f9b..2b142b2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -389,8 +389,6 @@ dependencies = [ "simple-signal", "smart-default", "structopt", - "strum 0.20.0", - "strum_macros 0.20.1", "sysinfo", "tokio", "tokio-stream", @@ -1713,12 +1711,6 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57bd81eb48f4c437cadc685403cad539345bf703d78e63707418431cecd4522b" -[[package]] -name = "strum" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7318c509b5ba57f18533982607f24070a55d353e90d4cae30c467cdb2ad5ac5c" - [[package]] name = "strum_macros" version = "0.18.0" @@ -1731,18 +1723,6 @@ dependencies = [ "syn 1.0.72", ] -[[package]] -name = "strum_macros" -version = "0.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee8bc6b87a5112aeeab1f4a9f7ab634fe6cbefc4850006df31267f4cfb9e3149" -dependencies = [ - "heck", - "proc-macro2", - "quote 1.0.9", - "syn 1.0.72", -] - [[package]] name = "syn" version = "0.11.11" @@ -1798,8 +1778,8 @@ checksum = "0f3ecc17269a19353b3558b313bba738b25d82993e30d62a18406a24aba4649b" dependencies = [ "heck", "pkg-config", - "strum 0.18.0", - "strum_macros 0.18.0", + "strum", + "strum_macros", "thiserror", "toml", "version-compare", diff --git a/Cargo.toml b/Cargo.toml index cbb85b4..b2e98c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,73 +1,4 @@ -[package] -name = "eww" -version = "0.1.0" -authors = ["elkowar <5300871+elkowar@users.noreply.github.com>"] -edition = "2018" -description= "Widget system for everyone!" -license = "MIT" -repository = "https://github.com/elkowar/eww" -homepage = "https://github.com/elkowar/eww" - - -[features] -default = ["x11"] -x11 = ["gdkx11", "x11rb"] -wayland = ["gtk-layer-shell", "gtk-layer-shell-sys"] -no-x11-wayland = [] -[dependencies.cairo-sys-rs] -version = "0.10.0" - -[dependencies] -gtk = { version = "0.9", features = [ "v3_22" ] } -gdk = { version = "*", features = ["v3_22"] } -gio = { version = "*", features = ["v2_44"] } -glib = { version = "*", features = ["v2_44"] } - -gdk-pixbuf = "0.9" - -gtk-layer-shell = { version="0.2.0", optional=true } -gtk-layer-shell-sys = { version="0.2.0", optional=true } -gdkx11 = { version = "0.9", optional = true } -x11rb = { version = "0.8", features = ["randr"], optional = true } - -regex = "1" -bincode = "1.3" -anyhow = "1.0" -derive_more = "0.99" -maplit = "1" -structopt = "0.3" -serde = {version = "1.0", features = ["derive"]} -serde_json = "1.0" -extend = "1" -grass = "0.10" -num = "0.4" -roxmltree = "0.14" -itertools = "0.10" -debug_stub_derive = "0.3" -log = "0.4" -pretty_env_logger = "0.4" -lazy_static = "1.4.0" -libc = "0.2" -nix = "0.20" -smart-default = "0.6" -simple-signal = "1.1" -unescape = "0.1" - -tokio = { version = "1.0", features = ["full"] } -tokio-stream = "0.1" -async-stream = "0.3" -futures-core = "0.3" -futures-util = "0.3" -tokio-util = "0.6" - -sysinfo = "0.16.1" - -nom = "6.1" -dyn-clone = "1.0" -base64 = "0.13" -wait-timeout = "0.2" - -notify = "5.0.0-pre.7" - -[dev-dependencies] -pretty_assertions = "0.7.1" +[workspace] +members = [ + "crates/eww" +] diff --git a/crates/eww/Cargo.lock b/crates/eww/Cargo.lock new file mode 100644 index 0000000..cf1c575 --- /dev/null +++ b/crates/eww/Cargo.lock @@ -0,0 +1,2069 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ahash" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217" + +[[package]] +name = "aho-corasick" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" +dependencies = [ + "memchr", +] + +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +dependencies = [ + "winapi", +] + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "anyhow" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595d3cfa7a60d4555cb5067b99f07142a08ea778de5cf993f7b75c7d8fabc486" + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "async-stream" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "171374e7e3b2504e0e5236e3b59260560f9fe94bfe9ac39ba5e4e929c5590625" +dependencies = [ + "async-stream-impl", + "futures-core", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "648ed8c8d2ce5409ccd57453d9d1b214b342a0d69376a6feda1fd6cae3299308" +dependencies = [ + "proc-macro2", + "quote 1.0.9", + "syn 1.0.73", +] + +[[package]] +name = "atk" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812b4911e210bd51b24596244523c856ca749e6223c50a7fbbba3f89ee37c426" +dependencies = [ + "atk-sys", + "bitflags", + "glib", + "glib-sys", + "gobject-sys", + "libc", +] + +[[package]] +name = "atk-sys" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f530e4af131d94cc4fa15c5c9d0348f0ef28bac64ba660b6b2a1cf2605dedfce" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[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 = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "beef" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "474a626a67200bd107d44179bb3d4fc61891172d11696609264589be6a0e6a43" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "bitvec" +version = "0.19.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8942c8d352ae1838c9dda0b0ca2ab657696ef2232a20147cf1b30ae1a9cb4321" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "bytes" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" + +[[package]] +name = "cairo-rs" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5c0f2e047e8ca53d0ff249c54ae047931d7a6ebe05d00af73e0ffeb6e34bdb8" +dependencies = [ + "bitflags", + "cairo-sys-rs", + "glib", + "glib-sys", + "gobject-sys", + "libc", + "thiserror", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ed2639b9ad5f1d6efa76de95558e11339e7318426d84ac4890b86c03e828ca7" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "cc" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e70cc2f62c6ce1868963827bd677764c62d07c3d9a3e1fb1177ee1a9ab199eb2" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "2.33.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +dependencies = [ + "ansi_term 0.11.0", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "codemap" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e769b5c8c8283982a987c6e948e540254f1058d5a74b8794914d4ef5fc2a24" + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "core-foundation-sys" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" + +[[package]] +name = "crossbeam-channel" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" +dependencies = [ + "cfg-if", + "lazy_static", +] + +[[package]] +name = "ctor" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e98e2ad1a782e33928b96fc3948e7c355e5af34ba4de7670fe8bac2a3b2006d" +dependencies = [ + "quote 1.0.9", + "syn 1.0.73", +] + +[[package]] +name = "debug_stub_derive" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "496b7f8a2f853313c3ca370641d7ff3e42c32974fdccda8f0684599ed0a3ff6b" +dependencies = [ + "quote 0.3.15", + "syn 0.11.11", +] + +[[package]] +name = "derive_more" +version = "0.99.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40eebddd2156ce1bb37b20bbe5151340a31828b1f2d22ba4141f3531710e38df" +dependencies = [ + "convert_case", + "proc-macro2", + "quote 1.0.9", + "rustc_version", + "syn 1.0.73", +] + +[[package]] +name = "diff" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499" + +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + +[[package]] +name = "dyn-clone" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee2626afccd7561a06cf1367e2950c4718ea04565e20fb5029b6c7d8ad09abcf" + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "env_logger" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "eww" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-stream", + "base64", + "bincode", + "cairo-sys-rs", + "debug_stub_derive", + "derive_more", + "dyn-clone", + "extend", + "futures-core", + "futures-util", + "gdk", + "gdk-pixbuf", + "gdkx11", + "gio", + "glib", + "grass", + "gtk", + "gtk-layer-shell", + "gtk-layer-shell-sys", + "itertools 0.10.1", + "lazy_static", + "libc", + "log", + "maplit", + "nix", + "nom", + "notify", + "num", + "pretty_assertions", + "pretty_env_logger", + "regex", + "roxmltree", + "serde", + "serde_json", + "simple-signal", + "smart-default", + "structopt", + "sysinfo", + "tokio", + "tokio-stream", + "tokio-util", + "unescape", + "wait-timeout", + "x11rb", +] + +[[package]] +name = "extend" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5c89e2933a4ec753dc007a4d6a7f9b6dc8e89b8fe89cabc252ccddf39c08bb1" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote 1.0.9", + "syn 1.0.73", +] + +[[package]] +name = "filetime" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d34cfa13a63ae058bfa601fe9e313bbdb3746427c1459185464ce0fcf62e1e8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "winapi", +] + +[[package]] +name = "fsevent-sys" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c0e564d24da983c053beff1bb7178e237501206840a3e6bf4e267b9e8ae734a" +dependencies = [ + "libc", +] + +[[package]] +name = "funty" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" + +[[package]] +name = "futures" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7e43a803dae2fa37c1f6a8fe121e1f7bf9548b4dfc0522a42f34145dadfc27" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e682a68b29a882df0545c143dc3646daefe80ba479bcdede94d5a703de2871e2" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0402f765d8a89a26043b889b26ce3c4679d268fa6bb22cd7c6aad98340e179d1" + +[[package]] +name = "futures-executor" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "badaa6a909fac9e7236d0620a2f57f7664640c56575b71a7552fbd68deafab79" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acc499defb3b348f8d8f3f66415835a9131856ff7714bf10dadfc4ec4bdb29a1" + +[[package]] +name = "futures-macro" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c40298486cdf52cc00cd6d6987892ba502c7656a16a4192a9992b1ccedd121" +dependencies = [ + "autocfg", + "proc-macro-hack", + "proc-macro2", + "quote 1.0.9", + "syn 1.0.73", +] + +[[package]] +name = "futures-sink" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a57bead0ceff0d6dde8f465ecd96c9338121bb7717d3e7b108059531870c4282" + +[[package]] +name = "futures-task" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a16bef9fc1a4dddb5bee51c989e3fbba26569cbb0e31f5b303c184e3dd33dae" + +[[package]] +name = "futures-util" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "feb5c238d27e2bf94ffdfd27b2c29e3df4a68c4193bb6427384259e2bf191967" +dependencies = [ + "autocfg", + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "proc-macro-hack", + "proc-macro-nested", + "slab", +] + +[[package]] +name = "gdk" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db00839b2a68a7a10af3fa28dfb3febaba3a20c3a9ac2425a33b7df1f84a6b7d" +dependencies = [ + "bitflags", + "cairo-rs", + "cairo-sys-rs", + "gdk-pixbuf", + "gdk-sys", + "gio", + "gio-sys", + "glib", + "glib-sys", + "gobject-sys", + "libc", + "pango", +] + +[[package]] +name = "gdk-pixbuf" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f6dae3cb99dd49b758b88f0132f8d401108e63ae8edd45f432d42cdff99998a" +dependencies = [ + "gdk-pixbuf-sys", + "gio", + "gio-sys", + "glib", + "glib-sys", + "gobject-sys", + "libc", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bfe468a7f43e97b8d193a762b6c5cf67a7d36cacbc0b9291dbcae24bfea1e8f" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gdk-sys" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a9653cfc500fd268015b1ac055ddbc3df7a5c9ea3f4ccef147b3957bd140d69" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkx11" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b89606baa221f9b8d8aa81924fd448c6b107d20de949f0fbf9a4ec203bb54b63" +dependencies = [ + "bitflags", + "gdk", + "gdk-pixbuf", + "gdk-pixbuf-sys", + "gdk-sys", + "gdkx11-sys", + "gio", + "gio-sys", + "glib", + "glib-sys", + "gobject-sys", + "libc", + "pango", + "x11", +] + +[[package]] +name = "gdkx11-sys" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6710388d530f3178ccbeb65cbafdf497a5772c4409eaf574ee9fa461af0a3d09" +dependencies = [ + "gdk-pixbuf-sys", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "system-deps", + "x11", +] + +[[package]] +name = "gethostname" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e692e296bfac1d2533ef168d0b60ff5897b8b70a4009276834014dd8924cc028" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gio" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fb60242bfff700772dae5d9e3a1f7aa2e4ebccf18b89662a16acb2822568561" +dependencies = [ + "bitflags", + "futures", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys", + "glib", + "glib-sys", + "gobject-sys", + "libc", + "once_cell", + "thiserror", +] + +[[package]] +name = "gio-sys" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e24fb752f8f5d2cf6bbc2c606fd2bc989c81c5e2fe321ab974d54f8b6344eac" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", + "winapi", +] + +[[package]] +name = "glib" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c685013b7515e668f1b57a165b009d4d28cb139a8a989bbd699c10dad29d0c5" +dependencies = [ + "bitflags", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "once_cell", +] + +[[package]] +name = "glib-macros" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41486a26d1366a8032b160b59065a59fb528530a46a49f627e7048fb8c064039" +dependencies = [ + "anyhow", + "heck", + "itertools 0.9.0", + "proc-macro-crate", + "proc-macro-error", + "proc-macro2", + "quote 1.0.9", + "syn 1.0.73", +] + +[[package]] +name = "glib-sys" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7e9b997a66e9a23d073f2b1abb4dbfc3925e0b8952f67efd8d9b6e168e4cdc1" +dependencies = [ + "libc", + "system-deps", +] + +[[package]] +name = "gobject-sys" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "952133b60c318a62bf82ee75b93acc7e84028a093e06b9e27981c2b6fe68218c" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "grass" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "352df9cd46a5538323ba016fdbff8baee4a55011a7349120b0d7280992276fa7" +dependencies = [ + "beef", + "clap", + "codemap", + "indexmap", + "lasso", + "num-bigint 0.3.2", + "num-rational 0.3.2", + "num-traits", + "once_cell", + "peekmore", + "phf", + "rand", +] + +[[package]] +name = "gtk" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f022f2054072b3af07666341984562c8e626a79daa8be27b955d12d06a5ad6a" +dependencies = [ + "atk", + "bitflags", + "cairo-rs", + "cairo-sys-rs", + "cc", + "gdk", + "gdk-pixbuf", + "gdk-pixbuf-sys", + "gdk-sys", + "gio", + "gio-sys", + "glib", + "glib-sys", + "gobject-sys", + "gtk-sys", + "libc", + "once_cell", + "pango", + "pango-sys", + "pkg-config", +] + +[[package]] +name = "gtk-layer-shell" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfb41868e4305f14a5b5bc0d5a3fcc0553d3a24f1f176f93c7e136290eaa3b77" +dependencies = [ + "bitflags", + "gdk", + "glib", + "glib-sys", + "gtk", + "gtk-layer-shell-sys", + "libc", +] + +[[package]] +name = "gtk-layer-shell-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b3c2ad4c131bf8e6870686615eb9897ad8c02ca0d4354c575f0d74acb429a0b" +dependencies = [ + "gdk-sys", + "glib-sys", + "gtk-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gtk-sys" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89acda6f084863307d948ba64a4b1ef674e8527dddab147ee4cdcc194c880457" +dependencies = [ + "atk-sys", + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "system-deps", +] + +[[package]] +name = "hashbrown" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b62f79061a0bc2e046024cb7ba44b08419ed238ecbd9adbd787434b9e8c25" +dependencies = [ + "ahash", + "autocfg", +] + +[[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 = "humantime" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" +dependencies = [ + "quick-error", +] + +[[package]] +name = "indexmap" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" +dependencies = [ + "autocfg", + "hashbrown 0.11.2", +] + +[[package]] +name = "inotify" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b031475cb1b103ee221afb806a23d35e0570bf7271d7588762ceba8127ed43b3" +dependencies = [ + "bitflags", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + +[[package]] +name = "instant" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee0328b1209d157ef001c94dd85b4f8f64139adb0eac2659f4b08382b2f474d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "itertools" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" +dependencies = [ + "either", +] + +[[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 = "lasso" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf1a626ea51398f5acf36666c8046ff4bfd048aab88e92db676d2a6eac8805d0" +dependencies = [ + "hashbrown 0.8.2", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lexical-core" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" +dependencies = [ + "arrayvec", + "bitflags", + "cfg-if", + "ryu", + "static_assertions", +] + +[[package]] +name = "libc" +version = "0.2.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790" + +[[package]] +name = "lock_api" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + +[[package]] +name = "memchr" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" + +[[package]] +name = "memoffset" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mio" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c2bdb6314ec10835cd3293dd268473a835c02b7b352e788be788b3c6ca6bb16" +dependencies = [ + "libc", + "log", + "miow", + "ntapi", + "winapi", +] + +[[package]] +name = "miow" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" +dependencies = [ + "winapi", +] + +[[package]] +name = "nix" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa9b4819da1bc61c0ea48b63b7bc8604064dd43013e7cc325df098d49cd7c18a" +dependencies = [ + "bitflags", + "cc", + "cfg-if", + "libc", +] + +[[package]] +name = "nom" +version = "6.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c5c51b9083a3c620fa67a2a635d1ce7d95b897e957d6b28ff9a5da960a103a6" +dependencies = [ + "bitvec", + "funty", + "lexical-core", + "memchr", + "version_check", +] + +[[package]] +name = "notify" +version = "5.0.0-pre.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51f18203a26893ca1d3526cf58084025d5639f91c44f8b70ab3b724f60e819a0" +dependencies = [ + "bitflags", + "crossbeam-channel", + "filetime", + "fsevent-sys", + "inotify", + "libc", + "mio", + "walkdir", + "winapi", +] + +[[package]] +name = "ntapi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +dependencies = [ + "winapi", +] + +[[package]] +name = "num" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606" +dependencies = [ + "num-bigint 0.4.0", + "num-complex", + "num-integer", + "num-iter", + "num-rational 0.4.0", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d0a3d5e207573f948a9e5376662aa743a2ea13f7c50a554d7af443a73fbfeba" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e0d047c1062aa51e256408c560894e5251f08925980e53cf1aa5bd00eec6512" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26873667bbbb7c5182d4a37c1add32cdf09f841af72da53318fdb81543c15085" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" +dependencies = [ + "autocfg", + "num-bigint 0.3.2", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a" +dependencies = [ + "autocfg", + "num-bigint 0.4.0", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" + +[[package]] +name = "output_vt100" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9" +dependencies = [ + "winapi", +] + +[[package]] +name = "pango" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9937068580bebd8ced19975938573803273ccbcbd598c58d4906efd4ac87c438" +dependencies = [ + "bitflags", + "glib", + "glib-sys", + "gobject-sys", + "libc", + "once_cell", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d2650c8b62d116c020abd0cea26a4ed96526afda89b1c4ea567131fdefc890" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "parking_lot" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + +[[package]] +name = "peekmore" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4089f831c514cbf080bd19bfce702f7a2de250492730d419204af6710ba19316" +dependencies = [ + "smallvec", +] + +[[package]] +name = "pest" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +dependencies = [ + "ucd-trie", +] + +[[package]] +name = "phf" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" +dependencies = [ + "phf_macros", + "phf_shared", + "proc-macro-hack", +] + +[[package]] +name = "phf_generator" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_macros" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro-hack", + "proc-macro2", + "quote 1.0.9", + "syn 1.0.73", +] + +[[package]] +name = "phf_shared" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" + +[[package]] +name = "ppv-lite86" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" + +[[package]] +name = "pretty_assertions" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cab0e7c02cf376875e9335e0ba1da535775beb5450d21e1dffca068818ed98b" +dependencies = [ + "ansi_term 0.12.1", + "ctor", + "diff", + "output_vt100", +] + +[[package]] +name = "pretty_env_logger" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d" +dependencies = [ + "env_logger", + "log", +] + +[[package]] +name = "proc-macro-crate" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +dependencies = [ + "toml", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote 1.0.9", + "syn 1.0.73", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote 1.0.9", + "version_check", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + +[[package]] +name = "proc-macro-nested" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" + +[[package]] +name = "proc-macro2" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038" +dependencies = [ + "unicode-xid 0.2.2", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quote" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom", + "libc", + "rand_chacha", + "rand_core", + "rand_hc", + "rand_pcg", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_pcg" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rayon" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" +dependencies = [ + "autocfg", + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "lazy_static", + "num_cpus", +] + +[[package]] +name = "redox_syscall" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ab49abadf3f9e1c4bc499e8845e152ad87d2ad2d30371841171169e9d75feee" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a26af418b574bd56588335b3a3659a65725d4e636eb1016c2f9e3b38c7cc759" +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 = "roxmltree" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "921904a62e410e37e215c40381b7117f830d9d89ba60ab5236170541dd25646b" +dependencies = [ + "xmlparser", +] + +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver", +] + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] + +[[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 1.0.9", + "syn 1.0.73", +] + +[[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 = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + +[[package]] +name = "simple-signal" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53f7da44adcc42667d57483bd93f81295f27d66897804b757573b61b6f13288b" +dependencies = [ + "lazy_static", + "libc", +] + +[[package]] +name = "siphasher" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbce6d4507c7e4a3962091436e56e95290cb71fa302d0d270e32130b75fbff27" + +[[package]] +name = "slab" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527" + +[[package]] +name = "smallvec" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" + +[[package]] +name = "smart-default" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "133659a15339456eeeb07572eb02a91c91e9815e9cbc89566944d2c8d3efdbf6" +dependencies = [ + "proc-macro2", + "quote 1.0.9", + "syn 1.0.73", +] + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "structopt" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69b041cdcb67226aca307e6e7be44c8806423d83e018bd662360a93dabce4d71" +dependencies = [ + "clap", + "lazy_static", + "structopt-derive", +] + +[[package]] +name = "structopt-derive" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7813934aecf5f51a54775e00068c237de98489463968231a51746bbbc03f9c10" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote 1.0.9", + "syn 1.0.73", +] + +[[package]] +name = "strum" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57bd81eb48f4c437cadc685403cad539345bf703d78e63707418431cecd4522b" + +[[package]] +name = "strum_macros" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87c85aa3f8ea653bfd3ddf25f7ee357ee4d204731f6aa9ad04002306f6e2774c" +dependencies = [ + "heck", + "proc-macro2", + "quote 1.0.9", + "syn 1.0.73", +] + +[[package]] +name = "syn" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" +dependencies = [ + "quote 0.3.15", + "synom", + "unicode-xid 0.0.4", +] + +[[package]] +name = "syn" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7" +dependencies = [ + "proc-macro2", + "quote 1.0.9", + "unicode-xid 0.2.2", +] + +[[package]] +name = "synom" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" +dependencies = [ + "unicode-xid 0.0.4", +] + +[[package]] +name = "sysinfo" +version = "0.16.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567e910ef0207be81a4e1bb0491e9a8d9866cf45b20fe1a52c03d347da9ea51b" +dependencies = [ + "cfg-if", + "core-foundation-sys", + "doc-comment", + "libc", + "ntapi", + "once_cell", + "rayon", + "winapi", +] + +[[package]] +name = "system-deps" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f3ecc17269a19353b3558b313bba738b25d82993e30d62a18406a24aba4649b" +dependencies = [ + "heck", + "pkg-config", + "strum", + "strum_macros", + "thiserror", + "toml", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[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 1.0.9", + "syn 1.0.73", +] + +[[package]] +name = "tokio" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2602b8af3767c285202012822834005f596c811042315fa7e9f5b12b2a43207" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "once_cell", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "tokio-macros", + "winapi", +] + +[[package]] +name = "tokio-macros" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54473be61f4ebe4efd09cec9bd5d16fa51d70ea0192213d754d2d500457db110" +dependencies = [ + "proc-macro2", + "quote 1.0.9", + "syn 1.0.73", +] + +[[package]] +name = "tokio-stream" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b2f3f698253f03119ac0102beaa64f67a67e08074d03a22d18784104543727f" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1caa0b0c8d94a049db56b5acf8cba99dc0623aab1b26d5b5f5e2d945846b3592" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + +[[package]] +name = "ucd-trie" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" + +[[package]] +name = "unescape" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccb97dac3243214f8d8507998906ca3e2e0b900bf9bf4870477f125b82e68f6e" + +[[package]] +name = "unicode-segmentation" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" + +[[package]] +name = "unicode-width" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" + +[[package]] +name = "unicode-xid" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version-compare" +version = "0.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d63556a25bae6ea31b52e640d7c41d1ab27faba4ccb600013837a3d0b3994ca1" + +[[package]] +name = "version_check" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" + +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[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-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-wsapoll" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c17110f57155602a80dca10be03852116403c9ff3cd25b079d666f2aa3df6e" +dependencies = [ + "winapi", +] + +[[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 = "wyz" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" + +[[package]] +name = "x11" +version = "2.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ecd092546cb16f25783a5451538e73afc8d32e242648d54f4ae5459ba1e773" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "x11rb" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ffb080b3f2f616242a4eb8e7d325035312127901025b0052bc3154a282d0f19" +dependencies = [ + "gethostname", + "nix", + "winapi", + "winapi-wsapoll", +] + +[[package]] +name = "xmlparser" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "114ba2b24d2167ef6d67d7d04c8cc86522b87f490025f39f0303b7db5bf5e3d8" diff --git a/crates/eww/Cargo.toml b/crates/eww/Cargo.toml new file mode 100644 index 0000000..cbb85b4 --- /dev/null +++ b/crates/eww/Cargo.toml @@ -0,0 +1,73 @@ +[package] +name = "eww" +version = "0.1.0" +authors = ["elkowar <5300871+elkowar@users.noreply.github.com>"] +edition = "2018" +description= "Widget system for everyone!" +license = "MIT" +repository = "https://github.com/elkowar/eww" +homepage = "https://github.com/elkowar/eww" + + +[features] +default = ["x11"] +x11 = ["gdkx11", "x11rb"] +wayland = ["gtk-layer-shell", "gtk-layer-shell-sys"] +no-x11-wayland = [] +[dependencies.cairo-sys-rs] +version = "0.10.0" + +[dependencies] +gtk = { version = "0.9", features = [ "v3_22" ] } +gdk = { version = "*", features = ["v3_22"] } +gio = { version = "*", features = ["v2_44"] } +glib = { version = "*", features = ["v2_44"] } + +gdk-pixbuf = "0.9" + +gtk-layer-shell = { version="0.2.0", optional=true } +gtk-layer-shell-sys = { version="0.2.0", optional=true } +gdkx11 = { version = "0.9", optional = true } +x11rb = { version = "0.8", features = ["randr"], optional = true } + +regex = "1" +bincode = "1.3" +anyhow = "1.0" +derive_more = "0.99" +maplit = "1" +structopt = "0.3" +serde = {version = "1.0", features = ["derive"]} +serde_json = "1.0" +extend = "1" +grass = "0.10" +num = "0.4" +roxmltree = "0.14" +itertools = "0.10" +debug_stub_derive = "0.3" +log = "0.4" +pretty_env_logger = "0.4" +lazy_static = "1.4.0" +libc = "0.2" +nix = "0.20" +smart-default = "0.6" +simple-signal = "1.1" +unescape = "0.1" + +tokio = { version = "1.0", features = ["full"] } +tokio-stream = "0.1" +async-stream = "0.3" +futures-core = "0.3" +futures-util = "0.3" +tokio-util = "0.6" + +sysinfo = "0.16.1" + +nom = "6.1" +dyn-clone = "1.0" +base64 = "0.13" +wait-timeout = "0.2" + +notify = "5.0.0-pre.7" + +[dev-dependencies] +pretty_assertions = "0.7.1" diff --git a/rustfmt.toml b/crates/eww/rustfmt.toml similarity index 100% rename from rustfmt.toml rename to crates/eww/rustfmt.toml diff --git a/src/app.rs b/crates/eww/src/app.rs similarity index 100% rename from src/app.rs rename to crates/eww/src/app.rs diff --git a/src/application_lifecycle.rs b/crates/eww/src/application_lifecycle.rs similarity index 100% rename from src/application_lifecycle.rs rename to crates/eww/src/application_lifecycle.rs diff --git a/src/client.rs b/crates/eww/src/client.rs similarity index 100% rename from src/client.rs rename to crates/eww/src/client.rs diff --git a/src/config/element.rs b/crates/eww/src/config/element.rs similarity index 100% rename from src/config/element.rs rename to crates/eww/src/config/element.rs diff --git a/src/config/eww_config.rs b/crates/eww/src/config/eww_config.rs similarity index 100% rename from src/config/eww_config.rs rename to crates/eww/src/config/eww_config.rs diff --git a/src/config/inbuilt.rs b/crates/eww/src/config/inbuilt.rs similarity index 100% rename from src/config/inbuilt.rs rename to crates/eww/src/config/inbuilt.rs diff --git a/src/config/mod.rs b/crates/eww/src/config/mod.rs similarity index 100% rename from src/config/mod.rs rename to crates/eww/src/config/mod.rs diff --git a/src/config/script_var.rs b/crates/eww/src/config/script_var.rs similarity index 100% rename from src/config/script_var.rs rename to crates/eww/src/config/script_var.rs diff --git a/src/config/system_stats.rs b/crates/eww/src/config/system_stats.rs similarity index 100% rename from src/config/system_stats.rs rename to crates/eww/src/config/system_stats.rs diff --git a/src/config/window_definition.rs b/crates/eww/src/config/window_definition.rs similarity index 100% rename from src/config/window_definition.rs rename to crates/eww/src/config/window_definition.rs diff --git a/src/config/window_geometry.rs b/crates/eww/src/config/window_geometry.rs similarity index 100% rename from src/config/window_geometry.rs rename to crates/eww/src/config/window_geometry.rs diff --git a/src/config/xml_ext.rs b/crates/eww/src/config/xml_ext.rs similarity index 100% rename from src/config/xml_ext.rs rename to crates/eww/src/config/xml_ext.rs diff --git a/src/display_backend.rs b/crates/eww/src/display_backend.rs similarity index 100% rename from src/display_backend.rs rename to crates/eww/src/display_backend.rs diff --git a/src/eww_state.rs b/crates/eww/src/eww_state.rs similarity index 100% rename from src/eww_state.rs rename to crates/eww/src/eww_state.rs diff --git a/src/geometry.rs b/crates/eww/src/geometry.rs similarity index 100% rename from src/geometry.rs rename to crates/eww/src/geometry.rs diff --git a/src/ipc_server.rs b/crates/eww/src/ipc_server.rs similarity index 100% rename from src/ipc_server.rs rename to crates/eww/src/ipc_server.rs diff --git a/src/main.rs b/crates/eww/src/main.rs similarity index 100% rename from src/main.rs rename to crates/eww/src/main.rs diff --git a/src/opts.rs b/crates/eww/src/opts.rs similarity index 100% rename from src/opts.rs rename to crates/eww/src/opts.rs diff --git a/src/script_var_handler.rs b/crates/eww/src/script_var_handler.rs similarity index 100% rename from src/script_var_handler.rs rename to crates/eww/src/script_var_handler.rs diff --git a/src/server.rs b/crates/eww/src/server.rs similarity index 100% rename from src/server.rs rename to crates/eww/src/server.rs diff --git a/src/util.rs b/crates/eww/src/util.rs similarity index 100% rename from src/util.rs rename to crates/eww/src/util.rs diff --git a/src/value/attr_value/attr_value.rs b/crates/eww/src/value/attr_value/attr_value.rs similarity index 100% rename from src/value/attr_value/attr_value.rs rename to crates/eww/src/value/attr_value/attr_value.rs diff --git a/src/value/attr_value/attr_value_expr.rs b/crates/eww/src/value/attr_value/attr_value_expr.rs similarity index 100% rename from src/value/attr_value/attr_value_expr.rs rename to crates/eww/src/value/attr_value/attr_value_expr.rs diff --git a/src/value/attr_value/mod.rs b/crates/eww/src/value/attr_value/mod.rs similarity index 100% rename from src/value/attr_value/mod.rs rename to crates/eww/src/value/attr_value/mod.rs diff --git a/src/value/attr_value/parser.rs b/crates/eww/src/value/attr_value/parser.rs similarity index 100% rename from src/value/attr_value/parser.rs rename to crates/eww/src/value/attr_value/parser.rs diff --git a/src/value/coords.rs b/crates/eww/src/value/coords.rs similarity index 100% rename from src/value/coords.rs rename to crates/eww/src/value/coords.rs diff --git a/src/value/mod.rs b/crates/eww/src/value/mod.rs similarity index 100% rename from src/value/mod.rs rename to crates/eww/src/value/mod.rs diff --git a/src/value/primitive.rs b/crates/eww/src/value/primitive.rs similarity index 100% rename from src/value/primitive.rs rename to crates/eww/src/value/primitive.rs diff --git a/src/widgets/mod.rs b/crates/eww/src/widgets/mod.rs similarity index 100% rename from src/widgets/mod.rs rename to crates/eww/src/widgets/mod.rs diff --git a/src/widgets/widget_definitions.rs b/crates/eww/src/widgets/widget_definitions.rs similarity index 100% rename from src/widgets/widget_definitions.rs rename to crates/eww/src/widgets/widget_definitions.rs diff --git a/src/widgets/widget_node.rs b/crates/eww/src/widgets/widget_node.rs similarity index 100% rename from src/widgets/widget_node.rs rename to crates/eww/src/widgets/widget_node.rs From 4391e541b818058f219dee059ac2cb4feadf3737 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Wed, 21 Jul 2021 19:24:44 +0200 Subject: [PATCH 058/137] move to crates directory --- .gitignore | 1 + Cargo.lock => crates/yuck/Cargo.lock | 0 Cargo.toml => crates/yuck/Cargo.toml | 0 build.rs => crates/yuck/build.rs | 0 {examples => crates/yuck/examples}/errors.rs | 0 {examples => crates/yuck/examples}/validation.rs | 0 rust-toolchain => crates/yuck/rust-toolchain | 0 rustfmt.toml => crates/yuck/rustfmt.toml | 0 {src => crates/yuck/src}/config/attributes.rs | 0 {src => crates/yuck/src}/config/backend_window_options.rs | 0 {src => crates/yuck/src}/config/config.rs | 0 {src => crates/yuck/src}/config/config_parse_error.rs | 0 {src => crates/yuck/src}/config/mod.rs | 0 {src => crates/yuck/src}/config/script_var_definition.rs | 0 .../src}/config/snapshots/eww_config__config__test__config.snap | 0 {src => crates/yuck/src}/config/test.rs | 0 {src => crates/yuck/src}/config/validate.rs | 0 {src => crates/yuck/src}/config/var_definition.rs | 0 {src => crates/yuck/src}/config/widget_definition.rs | 0 {src => crates/yuck/src}/config/widget_use.rs | 0 {src => crates/yuck/src}/config/window_definition.rs | 0 {src => crates/yuck/src}/config/window_geometry.rs | 0 {src => crates/yuck/src}/error.rs | 0 {src => crates/yuck/src}/format_diagnostic.rs | 0 {src => crates/yuck/src}/lib.rs | 0 {src => crates/yuck/src}/parser/ast.rs | 0 {src => crates/yuck/src}/parser/ast_iterator.rs | 0 {src => crates/yuck/src}/parser/from_ast.rs | 0 {src => crates/yuck/src}/parser/lexer.rs | 0 {src => crates/yuck/src}/parser/mod.rs | 0 {src => crates/yuck/src}/parser/parse_error.rs | 0 {src => crates/yuck/src}/parser/parser.lalrpop | 0 .../snapshots/eww_config__parser__element__test__test.snap | 0 .../yuck/src}/parser/snapshots/eww_config__parser__test-10.snap | 0 .../yuck/src}/parser/snapshots/eww_config__parser__test-11.snap | 0 .../yuck/src}/parser/snapshots/eww_config__parser__test-12.snap | 0 .../yuck/src}/parser/snapshots/eww_config__parser__test-13.snap | 0 .../yuck/src}/parser/snapshots/eww_config__parser__test-14.snap | 0 .../yuck/src}/parser/snapshots/eww_config__parser__test-15.snap | 0 .../yuck/src}/parser/snapshots/eww_config__parser__test-16.snap | 0 .../yuck/src}/parser/snapshots/eww_config__parser__test-17.snap | 0 .../yuck/src}/parser/snapshots/eww_config__parser__test-2.snap | 0 .../yuck/src}/parser/snapshots/eww_config__parser__test-3.snap | 0 .../yuck/src}/parser/snapshots/eww_config__parser__test-4.snap | 0 .../yuck/src}/parser/snapshots/eww_config__parser__test-5.snap | 0 .../yuck/src}/parser/snapshots/eww_config__parser__test-6.snap | 0 .../yuck/src}/parser/snapshots/eww_config__parser__test-7.snap | 0 .../yuck/src}/parser/snapshots/eww_config__parser__test-8.snap | 0 .../yuck/src}/parser/snapshots/eww_config__parser__test-9.snap | 0 .../yuck/src}/parser/snapshots/eww_config__parser__test.snap | 0 {src => crates/yuck/src}/util.rs | 0 {src => crates/yuck/src}/value/coords.rs | 0 {src => crates/yuck/src}/value/mod.rs | 0 53 files changed, 1 insertion(+) rename Cargo.lock => crates/yuck/Cargo.lock (100%) rename Cargo.toml => crates/yuck/Cargo.toml (100%) rename build.rs => crates/yuck/build.rs (100%) rename {examples => crates/yuck/examples}/errors.rs (100%) rename {examples => crates/yuck/examples}/validation.rs (100%) rename rust-toolchain => crates/yuck/rust-toolchain (100%) rename rustfmt.toml => crates/yuck/rustfmt.toml (100%) rename {src => crates/yuck/src}/config/attributes.rs (100%) rename {src => crates/yuck/src}/config/backend_window_options.rs (100%) rename {src => crates/yuck/src}/config/config.rs (100%) rename {src => crates/yuck/src}/config/config_parse_error.rs (100%) rename {src => crates/yuck/src}/config/mod.rs (100%) rename {src => crates/yuck/src}/config/script_var_definition.rs (100%) rename {src => crates/yuck/src}/config/snapshots/eww_config__config__test__config.snap (100%) rename {src => crates/yuck/src}/config/test.rs (100%) rename {src => crates/yuck/src}/config/validate.rs (100%) rename {src => crates/yuck/src}/config/var_definition.rs (100%) rename {src => crates/yuck/src}/config/widget_definition.rs (100%) rename {src => crates/yuck/src}/config/widget_use.rs (100%) rename {src => crates/yuck/src}/config/window_definition.rs (100%) rename {src => crates/yuck/src}/config/window_geometry.rs (100%) rename {src => crates/yuck/src}/error.rs (100%) rename {src => crates/yuck/src}/format_diagnostic.rs (100%) rename {src => crates/yuck/src}/lib.rs (100%) rename {src => crates/yuck/src}/parser/ast.rs (100%) rename {src => crates/yuck/src}/parser/ast_iterator.rs (100%) rename {src => crates/yuck/src}/parser/from_ast.rs (100%) rename {src => crates/yuck/src}/parser/lexer.rs (100%) rename {src => crates/yuck/src}/parser/mod.rs (100%) rename {src => crates/yuck/src}/parser/parse_error.rs (100%) rename {src => crates/yuck/src}/parser/parser.lalrpop (100%) rename {src => crates/yuck/src}/parser/snapshots/eww_config__parser__element__test__test.snap (100%) rename {src => crates/yuck/src}/parser/snapshots/eww_config__parser__test-10.snap (100%) rename {src => crates/yuck/src}/parser/snapshots/eww_config__parser__test-11.snap (100%) rename {src => crates/yuck/src}/parser/snapshots/eww_config__parser__test-12.snap (100%) rename {src => crates/yuck/src}/parser/snapshots/eww_config__parser__test-13.snap (100%) rename {src => crates/yuck/src}/parser/snapshots/eww_config__parser__test-14.snap (100%) rename {src => crates/yuck/src}/parser/snapshots/eww_config__parser__test-15.snap (100%) rename {src => crates/yuck/src}/parser/snapshots/eww_config__parser__test-16.snap (100%) rename {src => crates/yuck/src}/parser/snapshots/eww_config__parser__test-17.snap (100%) rename {src => crates/yuck/src}/parser/snapshots/eww_config__parser__test-2.snap (100%) rename {src => crates/yuck/src}/parser/snapshots/eww_config__parser__test-3.snap (100%) rename {src => crates/yuck/src}/parser/snapshots/eww_config__parser__test-4.snap (100%) rename {src => crates/yuck/src}/parser/snapshots/eww_config__parser__test-5.snap (100%) rename {src => crates/yuck/src}/parser/snapshots/eww_config__parser__test-6.snap (100%) rename {src => crates/yuck/src}/parser/snapshots/eww_config__parser__test-7.snap (100%) rename {src => crates/yuck/src}/parser/snapshots/eww_config__parser__test-8.snap (100%) rename {src => crates/yuck/src}/parser/snapshots/eww_config__parser__test-9.snap (100%) rename {src => crates/yuck/src}/parser/snapshots/eww_config__parser__test.snap (100%) rename {src => crates/yuck/src}/util.rs (100%) rename {src => crates/yuck/src}/value/coords.rs (100%) rename {src => crates/yuck/src}/value/mod.rs (100%) diff --git a/.gitignore b/.gitignore index ea8c4bf..d03f68d 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +/**/target diff --git a/Cargo.lock b/crates/yuck/Cargo.lock similarity index 100% rename from Cargo.lock rename to crates/yuck/Cargo.lock diff --git a/Cargo.toml b/crates/yuck/Cargo.toml similarity index 100% rename from Cargo.toml rename to crates/yuck/Cargo.toml diff --git a/build.rs b/crates/yuck/build.rs similarity index 100% rename from build.rs rename to crates/yuck/build.rs diff --git a/examples/errors.rs b/crates/yuck/examples/errors.rs similarity index 100% rename from examples/errors.rs rename to crates/yuck/examples/errors.rs diff --git a/examples/validation.rs b/crates/yuck/examples/validation.rs similarity index 100% rename from examples/validation.rs rename to crates/yuck/examples/validation.rs diff --git a/rust-toolchain b/crates/yuck/rust-toolchain similarity index 100% rename from rust-toolchain rename to crates/yuck/rust-toolchain diff --git a/rustfmt.toml b/crates/yuck/rustfmt.toml similarity index 100% rename from rustfmt.toml rename to crates/yuck/rustfmt.toml diff --git a/src/config/attributes.rs b/crates/yuck/src/config/attributes.rs similarity index 100% rename from src/config/attributes.rs rename to crates/yuck/src/config/attributes.rs diff --git a/src/config/backend_window_options.rs b/crates/yuck/src/config/backend_window_options.rs similarity index 100% rename from src/config/backend_window_options.rs rename to crates/yuck/src/config/backend_window_options.rs diff --git a/src/config/config.rs b/crates/yuck/src/config/config.rs similarity index 100% rename from src/config/config.rs rename to crates/yuck/src/config/config.rs diff --git a/src/config/config_parse_error.rs b/crates/yuck/src/config/config_parse_error.rs similarity index 100% rename from src/config/config_parse_error.rs rename to crates/yuck/src/config/config_parse_error.rs diff --git a/src/config/mod.rs b/crates/yuck/src/config/mod.rs similarity index 100% rename from src/config/mod.rs rename to crates/yuck/src/config/mod.rs diff --git a/src/config/script_var_definition.rs b/crates/yuck/src/config/script_var_definition.rs similarity index 100% rename from src/config/script_var_definition.rs rename to crates/yuck/src/config/script_var_definition.rs diff --git a/src/config/snapshots/eww_config__config__test__config.snap b/crates/yuck/src/config/snapshots/eww_config__config__test__config.snap similarity index 100% rename from src/config/snapshots/eww_config__config__test__config.snap rename to crates/yuck/src/config/snapshots/eww_config__config__test__config.snap diff --git a/src/config/test.rs b/crates/yuck/src/config/test.rs similarity index 100% rename from src/config/test.rs rename to crates/yuck/src/config/test.rs diff --git a/src/config/validate.rs b/crates/yuck/src/config/validate.rs similarity index 100% rename from src/config/validate.rs rename to crates/yuck/src/config/validate.rs diff --git a/src/config/var_definition.rs b/crates/yuck/src/config/var_definition.rs similarity index 100% rename from src/config/var_definition.rs rename to crates/yuck/src/config/var_definition.rs diff --git a/src/config/widget_definition.rs b/crates/yuck/src/config/widget_definition.rs similarity index 100% rename from src/config/widget_definition.rs rename to crates/yuck/src/config/widget_definition.rs diff --git a/src/config/widget_use.rs b/crates/yuck/src/config/widget_use.rs similarity index 100% rename from src/config/widget_use.rs rename to crates/yuck/src/config/widget_use.rs diff --git a/src/config/window_definition.rs b/crates/yuck/src/config/window_definition.rs similarity index 100% rename from src/config/window_definition.rs rename to crates/yuck/src/config/window_definition.rs diff --git a/src/config/window_geometry.rs b/crates/yuck/src/config/window_geometry.rs similarity index 100% rename from src/config/window_geometry.rs rename to crates/yuck/src/config/window_geometry.rs diff --git a/src/error.rs b/crates/yuck/src/error.rs similarity index 100% rename from src/error.rs rename to crates/yuck/src/error.rs diff --git a/src/format_diagnostic.rs b/crates/yuck/src/format_diagnostic.rs similarity index 100% rename from src/format_diagnostic.rs rename to crates/yuck/src/format_diagnostic.rs diff --git a/src/lib.rs b/crates/yuck/src/lib.rs similarity index 100% rename from src/lib.rs rename to crates/yuck/src/lib.rs diff --git a/src/parser/ast.rs b/crates/yuck/src/parser/ast.rs similarity index 100% rename from src/parser/ast.rs rename to crates/yuck/src/parser/ast.rs diff --git a/src/parser/ast_iterator.rs b/crates/yuck/src/parser/ast_iterator.rs similarity index 100% rename from src/parser/ast_iterator.rs rename to crates/yuck/src/parser/ast_iterator.rs diff --git a/src/parser/from_ast.rs b/crates/yuck/src/parser/from_ast.rs similarity index 100% rename from src/parser/from_ast.rs rename to crates/yuck/src/parser/from_ast.rs diff --git a/src/parser/lexer.rs b/crates/yuck/src/parser/lexer.rs similarity index 100% rename from src/parser/lexer.rs rename to crates/yuck/src/parser/lexer.rs diff --git a/src/parser/mod.rs b/crates/yuck/src/parser/mod.rs similarity index 100% rename from src/parser/mod.rs rename to crates/yuck/src/parser/mod.rs diff --git a/src/parser/parse_error.rs b/crates/yuck/src/parser/parse_error.rs similarity index 100% rename from src/parser/parse_error.rs rename to crates/yuck/src/parser/parse_error.rs diff --git a/src/parser/parser.lalrpop b/crates/yuck/src/parser/parser.lalrpop similarity index 100% rename from src/parser/parser.lalrpop rename to crates/yuck/src/parser/parser.lalrpop diff --git a/src/parser/snapshots/eww_config__parser__element__test__test.snap b/crates/yuck/src/parser/snapshots/eww_config__parser__element__test__test.snap similarity index 100% rename from src/parser/snapshots/eww_config__parser__element__test__test.snap rename to crates/yuck/src/parser/snapshots/eww_config__parser__element__test__test.snap diff --git a/src/parser/snapshots/eww_config__parser__test-10.snap b/crates/yuck/src/parser/snapshots/eww_config__parser__test-10.snap similarity index 100% rename from src/parser/snapshots/eww_config__parser__test-10.snap rename to crates/yuck/src/parser/snapshots/eww_config__parser__test-10.snap diff --git a/src/parser/snapshots/eww_config__parser__test-11.snap b/crates/yuck/src/parser/snapshots/eww_config__parser__test-11.snap similarity index 100% rename from src/parser/snapshots/eww_config__parser__test-11.snap rename to crates/yuck/src/parser/snapshots/eww_config__parser__test-11.snap diff --git a/src/parser/snapshots/eww_config__parser__test-12.snap b/crates/yuck/src/parser/snapshots/eww_config__parser__test-12.snap similarity index 100% rename from src/parser/snapshots/eww_config__parser__test-12.snap rename to crates/yuck/src/parser/snapshots/eww_config__parser__test-12.snap diff --git a/src/parser/snapshots/eww_config__parser__test-13.snap b/crates/yuck/src/parser/snapshots/eww_config__parser__test-13.snap similarity index 100% rename from src/parser/snapshots/eww_config__parser__test-13.snap rename to crates/yuck/src/parser/snapshots/eww_config__parser__test-13.snap diff --git a/src/parser/snapshots/eww_config__parser__test-14.snap b/crates/yuck/src/parser/snapshots/eww_config__parser__test-14.snap similarity index 100% rename from src/parser/snapshots/eww_config__parser__test-14.snap rename to crates/yuck/src/parser/snapshots/eww_config__parser__test-14.snap diff --git a/src/parser/snapshots/eww_config__parser__test-15.snap b/crates/yuck/src/parser/snapshots/eww_config__parser__test-15.snap similarity index 100% rename from src/parser/snapshots/eww_config__parser__test-15.snap rename to crates/yuck/src/parser/snapshots/eww_config__parser__test-15.snap diff --git a/src/parser/snapshots/eww_config__parser__test-16.snap b/crates/yuck/src/parser/snapshots/eww_config__parser__test-16.snap similarity index 100% rename from src/parser/snapshots/eww_config__parser__test-16.snap rename to crates/yuck/src/parser/snapshots/eww_config__parser__test-16.snap diff --git a/src/parser/snapshots/eww_config__parser__test-17.snap b/crates/yuck/src/parser/snapshots/eww_config__parser__test-17.snap similarity index 100% rename from src/parser/snapshots/eww_config__parser__test-17.snap rename to crates/yuck/src/parser/snapshots/eww_config__parser__test-17.snap diff --git a/src/parser/snapshots/eww_config__parser__test-2.snap b/crates/yuck/src/parser/snapshots/eww_config__parser__test-2.snap similarity index 100% rename from src/parser/snapshots/eww_config__parser__test-2.snap rename to crates/yuck/src/parser/snapshots/eww_config__parser__test-2.snap diff --git a/src/parser/snapshots/eww_config__parser__test-3.snap b/crates/yuck/src/parser/snapshots/eww_config__parser__test-3.snap similarity index 100% rename from src/parser/snapshots/eww_config__parser__test-3.snap rename to crates/yuck/src/parser/snapshots/eww_config__parser__test-3.snap diff --git a/src/parser/snapshots/eww_config__parser__test-4.snap b/crates/yuck/src/parser/snapshots/eww_config__parser__test-4.snap similarity index 100% rename from src/parser/snapshots/eww_config__parser__test-4.snap rename to crates/yuck/src/parser/snapshots/eww_config__parser__test-4.snap diff --git a/src/parser/snapshots/eww_config__parser__test-5.snap b/crates/yuck/src/parser/snapshots/eww_config__parser__test-5.snap similarity index 100% rename from src/parser/snapshots/eww_config__parser__test-5.snap rename to crates/yuck/src/parser/snapshots/eww_config__parser__test-5.snap diff --git a/src/parser/snapshots/eww_config__parser__test-6.snap b/crates/yuck/src/parser/snapshots/eww_config__parser__test-6.snap similarity index 100% rename from src/parser/snapshots/eww_config__parser__test-6.snap rename to crates/yuck/src/parser/snapshots/eww_config__parser__test-6.snap diff --git a/src/parser/snapshots/eww_config__parser__test-7.snap b/crates/yuck/src/parser/snapshots/eww_config__parser__test-7.snap similarity index 100% rename from src/parser/snapshots/eww_config__parser__test-7.snap rename to crates/yuck/src/parser/snapshots/eww_config__parser__test-7.snap diff --git a/src/parser/snapshots/eww_config__parser__test-8.snap b/crates/yuck/src/parser/snapshots/eww_config__parser__test-8.snap similarity index 100% rename from src/parser/snapshots/eww_config__parser__test-8.snap rename to crates/yuck/src/parser/snapshots/eww_config__parser__test-8.snap diff --git a/src/parser/snapshots/eww_config__parser__test-9.snap b/crates/yuck/src/parser/snapshots/eww_config__parser__test-9.snap similarity index 100% rename from src/parser/snapshots/eww_config__parser__test-9.snap rename to crates/yuck/src/parser/snapshots/eww_config__parser__test-9.snap diff --git a/src/parser/snapshots/eww_config__parser__test.snap b/crates/yuck/src/parser/snapshots/eww_config__parser__test.snap similarity index 100% rename from src/parser/snapshots/eww_config__parser__test.snap rename to crates/yuck/src/parser/snapshots/eww_config__parser__test.snap diff --git a/src/util.rs b/crates/yuck/src/util.rs similarity index 100% rename from src/util.rs rename to crates/yuck/src/util.rs diff --git a/src/value/coords.rs b/crates/yuck/src/value/coords.rs similarity index 100% rename from src/value/coords.rs rename to crates/yuck/src/value/coords.rs diff --git a/src/value/mod.rs b/crates/yuck/src/value/mod.rs similarity index 100% rename from src/value/mod.rs rename to crates/yuck/src/value/mod.rs From 4f2e9cf0634ed5fd0a5addbb23856f76c10bdfea Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Wed, 21 Jul 2021 19:28:55 +0200 Subject: [PATCH 059/137] Fix Cargo.toml files --- Cargo.toml | 4 +++- crates/eww/Cargo.toml | 5 +++-- crates/yuck/Cargo.toml | 6 ++---- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b2e98c2..bed4082 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,4 +1,6 @@ [workspace] members = [ - "crates/eww" + "crates/eww", + "crates/simplexpr", + "crates/yuck" ] diff --git a/crates/eww/Cargo.toml b/crates/eww/Cargo.toml index cbb85b4..910bfbe 100644 --- a/crates/eww/Cargo.toml +++ b/crates/eww/Cargo.toml @@ -41,7 +41,6 @@ serde_json = "1.0" extend = "1" grass = "0.10" num = "0.4" -roxmltree = "0.14" itertools = "0.10" debug_stub_derive = "0.3" log = "0.4" @@ -62,12 +61,14 @@ tokio-util = "0.6" sysinfo = "0.16.1" -nom = "6.1" dyn-clone = "1.0" base64 = "0.13" wait-timeout = "0.2" notify = "5.0.0-pre.7" +simplexpr = { path = "../simplexpr" } +yuck = { path = "../yuck" } + [dev-dependencies] pretty_assertions = "0.7.1" diff --git a/crates/yuck/Cargo.toml b/crates/yuck/Cargo.toml index 81f54f2..b5ab428 100644 --- a/crates/yuck/Cargo.toml +++ b/crates/yuck/Cargo.toml @@ -1,12 +1,10 @@ [package] -name = "eww_config" +name = "yuck" version = "0.1.0" authors = ["elkowar <5300871+elkowar@users.noreply.github.com>"] edition = "2018" - build = "build.rs" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] lalrpop-util = "0.19.5" @@ -27,7 +25,7 @@ strum = { version = "0.21", features = ["derive"] } anyhow = "1" -simplexpr = { path = "../../projects/simplexpr" } +simplexpr = { path = "../simplexpr" } [build-dependencies] lalrpop = "0.19.5" From db9e673b81d0d85dccc1aee2c8dd19cb7ca460e5 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Thu, 15 Jul 2021 15:03:58 +0200 Subject: [PATCH 060/137] Clean up X Window configuration and add more options factor out some x specific logic cleanly factor out more code into backend specific sections Minor refactor to enum parsing fix build on wayland make default of wm-ignore depend on type and reserve, and add documentation --- Cargo.lock | 334 ++++++++++++++------------- docs/src/configuration.md | 37 ++- src/app.rs | 83 ++++--- src/config/backend_window_options.rs | 69 ++++++ src/config/mod.rs | 1 + src/config/window_definition.rs | 170 +++++--------- src/config/window_geometry.rs | 8 +- src/display_backend.rs | 132 +++++------ src/util.rs | 21 +- src/value/coords.rs | 2 +- src/value/primitive.rs | 63 ++--- 11 files changed, 454 insertions(+), 466 deletions(-) create mode 100644 src/config/backend_window_options.rs diff --git a/Cargo.lock b/Cargo.lock index 8eb7f9b..f13487e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10,9 +10,9 @@ checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217" [[package]] name = "aho-corasick" -version = "0.7.18" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" dependencies = [ "memchr", ] @@ -37,15 +37,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.40" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b" - -[[package]] -name = "anymap" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33954243bd79057c2de7338850b85983a44588021f8a5fee574a8888c6de4344" +checksum = "595d3cfa7a60d4555cb5067b99f07142a08ea778de5cf993f7b75c7d8fabc486" [[package]] name = "arrayvec" @@ -55,9 +49,9 @@ checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" [[package]] name = "async-stream" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a26cb53174ddd320edfff199a853f93d571f48eeb4dde75e67a9a3dbb7b7e5e" +checksum = "171374e7e3b2504e0e5236e3b59260560f9fe94bfe9ac39ba5e4e929c5590625" dependencies = [ "async-stream-impl", "futures-core", @@ -65,13 +59,13 @@ dependencies = [ [[package]] name = "async-stream-impl" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db134ba52475c060f3329a8ef0f8786d6b872ed01515d4b79c162e5798da1340" +checksum = "648ed8c8d2ce5409ccd57453d9d1b214b342a0d69376a6feda1fd6cae3299308" dependencies = [ "proc-macro2", "quote 1.0.9", - "syn 1.0.72", + "syn 1.0.73", ] [[package]] @@ -190,9 +184,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.67" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" +checksum = "e70cc2f62c6ce1868963827bd677764c62d07c3d9a3e1fb1177ee1a9ab199eb2" [[package]] name = "cfg-if" @@ -256,9 +250,9 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52fb27eab85b17fbb9f6fd667089e07d6a2eb8743d02639ee7f6a7a7729c9c94" +checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" dependencies = [ "cfg-if", "crossbeam-utils", @@ -269,11 +263,10 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4feb231f0d4d6af81aed15928e58ecf5816aa62a2393e2c82f46973e92a9a278" +checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" dependencies = [ - "autocfg", "cfg-if", "lazy_static", ] @@ -285,7 +278,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e98e2ad1a782e33928b96fc3948e7c355e5af34ba4de7670fe8bac2a3b2006d" dependencies = [ "quote 1.0.9", - "syn 1.0.72", + "syn 1.0.73", ] [[package]] @@ -300,14 +293,15 @@ dependencies = [ [[package]] name = "derive_more" -version = "0.99.13" +version = "0.99.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f82b1b72f1263f214c0f823371768776c4f5841b942c9883aa8e5ec584fd0ba6" +checksum = "40eebddd2156ce1bb37b20bbe5151340a31828b1f2d22ba4141f3531710e38df" dependencies = [ "convert_case", "proc-macro2", "quote 1.0.9", - "syn 1.0.72", + "rustc_version", + "syn 1.0.73", ] [[package]] @@ -371,7 +365,7 @@ dependencies = [ "gtk", "gtk-layer-shell", "gtk-layer-shell-sys", - "itertools 0.10.0", + "itertools 0.10.1", "lazy_static", "libc", "log", @@ -389,8 +383,6 @@ dependencies = [ "simple-signal", "smart-default", "structopt", - "strum 0.20.0", - "strum_macros 0.20.1", "sysinfo", "tokio", "tokio-stream", @@ -402,14 +394,14 @@ dependencies = [ [[package]] name = "extend" -version = "1.0.1" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2feaa8e332f0db2f08788707dc550075aab8e6d20ffc85958e1174e22887d11" +checksum = "f5c89e2933a4ec753dc007a4d6a7f9b6dc8e89b8fe89cabc252ccddf39c08bb1" dependencies = [ "proc-macro-error", "proc-macro2", "quote 1.0.9", - "syn 1.0.72", + "syn 1.0.73", ] [[package]] @@ -424,21 +416,11 @@ dependencies = [ "winapi", ] -[[package]] -name = "fsevent" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97f347202c95c98805c216f9e1df210e8ebaec9fdb2365700a43c10797a35e63" -dependencies = [ - "bitflags", - "fsevent-sys", -] - [[package]] name = "fsevent-sys" -version = "3.0.2" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a29c77f1ca394c3e73a9a5d24cfcabb734682d9634fc398f2204a63c994120" +checksum = "5c0e564d24da983c053beff1bb7178e237501206840a3e6bf4e267b9e8ae734a" dependencies = [ "libc", ] @@ -451,9 +433,9 @@ checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" [[package]] name = "futures" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d5813545e459ad3ca1bff9915e9ad7f1a47dc6a91b627ce321d5863b7dd253" +checksum = "0e7e43a803dae2fa37c1f6a8fe121e1f7bf9548b4dfc0522a42f34145dadfc27" dependencies = [ "futures-channel", "futures-core", @@ -466,9 +448,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce79c6a52a299137a6013061e0cf0e688fce5d7f1bc60125f520912fdb29ec25" +checksum = "e682a68b29a882df0545c143dc3646daefe80ba479bcdede94d5a703de2871e2" dependencies = [ "futures-core", "futures-sink", @@ -476,15 +458,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "098cd1c6dda6ca01650f1a37a794245eb73181d0d4d4e955e2f3c37db7af1815" +checksum = "0402f765d8a89a26043b889b26ce3c4679d268fa6bb22cd7c6aad98340e179d1" [[package]] name = "futures-executor" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f6cb7042eda00f0049b1d2080aa4b93442997ee507eb3828e8bd7577f94c9d" +checksum = "badaa6a909fac9e7236d0620a2f57f7664640c56575b71a7552fbd68deafab79" dependencies = [ "futures-core", "futures-task", @@ -493,40 +475,42 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "365a1a1fb30ea1c03a830fdb2158f5236833ac81fa0ad12fe35b29cddc35cb04" +checksum = "acc499defb3b348f8d8f3f66415835a9131856ff7714bf10dadfc4ec4bdb29a1" [[package]] name = "futures-macro" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "668c6733a182cd7deb4f1de7ba3bf2120823835b3bcfbeacf7d2c4a773c1bb8b" +checksum = "a4c40298486cdf52cc00cd6d6987892ba502c7656a16a4192a9992b1ccedd121" dependencies = [ + "autocfg", "proc-macro-hack", "proc-macro2", "quote 1.0.9", - "syn 1.0.72", + "syn 1.0.73", ] [[package]] name = "futures-sink" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5629433c555de3d82861a7a4e3794a4c40040390907cfbfd7143a92a426c23" +checksum = "a57bead0ceff0d6dde8f465ecd96c9338121bb7717d3e7b108059531870c4282" [[package]] name = "futures-task" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba7aa51095076f3ba6d9a1f702f74bd05ec65f555d70d2033d55ba8d69f581bc" +checksum = "8a16bef9fc1a4dddb5bee51c989e3fbba26569cbb0e31f5b303c184e3dd33dae" [[package]] name = "futures-util" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c144ad54d60f23927f0a6b6d816e4271278b64f005ad65e4e35291d2de9c025" +checksum = "feb5c238d27e2bf94ffdfd27b2c29e3df4a68c4193bb6427384259e2bf191967" dependencies = [ + "autocfg", "futures-channel", "futures-core", "futures-io", @@ -732,7 +716,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote 1.0.9", - "syn 1.0.72", + "syn 1.0.73", ] [[package]] @@ -758,9 +742,9 @@ dependencies = [ [[package]] name = "grass" -version = "0.10.4" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8209e6832a1f93e15adca845b43af12ead0cb8221fb2ef615ca84c75e54af1a" +checksum = "352df9cd46a5538323ba016fdbff8baee4a55011a7349120b0d7280992276fa7" dependencies = [ "beef", "clap", @@ -862,24 +846,24 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.9.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" [[package]] name = "heck" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" dependencies = [ "unicode-segmentation", ] [[package]] name = "hermit-abi" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" dependencies = [ "libc", ] @@ -895,19 +879,19 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.6.2" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3" +checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" dependencies = [ "autocfg", - "hashbrown 0.9.1", + "hashbrown 0.11.2", ] [[package]] name = "inotify" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d19f57db1baad9d09e43a3cd76dcf82ebdafd37d75c9498b87762dba77c93f15" +checksum = "b031475cb1b103ee221afb806a23d35e0570bf7271d7588762ceba8127ed43b3" dependencies = [ "bitflags", "inotify-sys", @@ -925,9 +909,9 @@ dependencies = [ [[package]] name = "instant" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" +checksum = "bee0328b1209d157ef001c94dd85b4f8f64139adb0eac2659f4b08382b2f474d" dependencies = [ "cfg-if", ] @@ -943,9 +927,9 @@ dependencies = [ [[package]] name = "itertools" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d572918e350e82412fe766d24b15e6682fb2ed2bbe018280caa810397cb319" +checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf" dependencies = [ "either", ] @@ -986,9 +970,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.94" +version = "0.2.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e" +checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790" [[package]] name = "lock_api" @@ -1016,24 +1000,24 @@ checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" [[package]] name = "memchr" -version = "2.4.0" +version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" +checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" [[package]] name = "memoffset" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83fb6581e8ed1f85fd45c116db8405483899489e38406156c25eb743554361d" +checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" dependencies = [ "autocfg", ] [[package]] name = "mio" -version = "0.7.11" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf80d3e903b34e0bd7282b218398aec54e082c840d9baf8339e0080a0c542956" +checksum = "8c2bdb6314ec10835cd3293dd268473a835c02b7b352e788be788b3c6ca6bb16" dependencies = [ "libc", "log", @@ -1065,9 +1049,9 @@ dependencies = [ [[package]] name = "nom" -version = "6.1.2" +version = "6.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7413f999671bd4745a7b624bd370a569fb6bc574b23c83a3c5ed2e453f3d5e2" +checksum = "9c5c51b9083a3c620fa67a2a635d1ce7d95b897e957d6b28ff9a5da960a103a6" dependencies = [ "bitvec", "funty", @@ -1078,15 +1062,13 @@ dependencies = [ [[package]] name = "notify" -version = "5.0.0-pre.7" +version = "5.0.0-pre.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ebe7699a0f8c5759450716ee03d231685c22b4fe8f406c42c22e0ad94d40ce7" +checksum = "51f18203a26893ca1d3526cf58084025d5639f91c44f8b70ab3b724f60e819a0" dependencies = [ - "anymap", "bitflags", "crossbeam-channel", "filetime", - "fsevent", "fsevent-sys", "inotify", "libc", @@ -1215,9 +1197,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.7.2" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" +checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" [[package]] name = "output_vt100" @@ -1289,6 +1271,15 @@ dependencies = [ "smallvec", ] +[[package]] +name = "pest" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +dependencies = [ + "ucd-trie", +] + [[package]] name = "phf" version = "0.8.0" @@ -1321,7 +1312,7 @@ dependencies = [ "proc-macro-hack", "proc-macro2", "quote 1.0.9", - "syn 1.0.72", + "syn 1.0.73", ] [[package]] @@ -1335,9 +1326,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc0e1f259c92177c30a4c9d177246edd0a3568b25756a977d0632cf8fa37e905" +checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" [[package]] name = "pin-utils" @@ -1397,7 +1388,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote 1.0.9", - "syn 1.0.72", + "syn 1.0.73", "version_check", ] @@ -1426,9 +1417,9 @@ checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" [[package]] name = "proc-macro2" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" +checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038" dependencies = [ "unicode-xid 0.2.2", ] @@ -1513,9 +1504,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b0d8e0819fadc20c74ea8373106ead0600e3a67ef1fe8da56e39b9ae7275674" +checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" dependencies = [ "autocfg", "crossbeam-deque", @@ -1525,9 +1516,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.9.0" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a" +checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" dependencies = [ "crossbeam-channel", "crossbeam-deque", @@ -1538,18 +1529,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "742739e41cd49414de871ea5e549afb7e2a3ac77b589bcbebe8c82fab37147fc" +checksum = "5ab49abadf3f9e1c4bc499e8845e152ad87d2ad2d30371841171169e9d75feee" dependencies = [ "bitflags", ] [[package]] name = "regex" -version = "1.5.4" +version = "1.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +checksum = "2a26af418b574bd56588335b3a3659a65725d4e636eb1016c2f9e3b38c7cc759" dependencies = [ "aho-corasick", "memchr", @@ -1571,6 +1562,15 @@ dependencies = [ "xmlparser", ] +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver", +] + [[package]] name = "ryu" version = "1.0.5" @@ -1593,23 +1593,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] -name = "serde" -version = "1.0.125" +name = "semver" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] + +[[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.125" +version = "1.0.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d" +checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43" dependencies = [ "proc-macro2", "quote 1.0.9", - "syn 1.0.72", + "syn 1.0.73", ] [[package]] @@ -1625,9 +1643,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" dependencies = [ "libc", ] @@ -1668,7 +1686,7 @@ checksum = "133659a15339456eeeb07572eb02a91c91e9815e9cbc89566944d2c8d3efdbf6" dependencies = [ "proc-macro2", "quote 1.0.9", - "syn 1.0.72", + "syn 1.0.73", ] [[package]] @@ -1685,9 +1703,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "structopt" -version = "0.3.21" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5277acd7ee46e63e5168a80734c9f6ee81b1367a7d8772a2d765df2a3705d28c" +checksum = "69b041cdcb67226aca307e6e7be44c8806423d83e018bd662360a93dabce4d71" dependencies = [ "clap", "lazy_static", @@ -1696,15 +1714,15 @@ dependencies = [ [[package]] name = "structopt-derive" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ba9cdfda491b814720b6b06e0cac513d922fc407582032e8706e9f137976f90" +checksum = "7813934aecf5f51a54775e00068c237de98489463968231a51746bbbc03f9c10" dependencies = [ "heck", "proc-macro-error", "proc-macro2", "quote 1.0.9", - "syn 1.0.72", + "syn 1.0.73", ] [[package]] @@ -1713,12 +1731,6 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57bd81eb48f4c437cadc685403cad539345bf703d78e63707418431cecd4522b" -[[package]] -name = "strum" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7318c509b5ba57f18533982607f24070a55d353e90d4cae30c467cdb2ad5ac5c" - [[package]] name = "strum_macros" version = "0.18.0" @@ -1728,19 +1740,7 @@ dependencies = [ "heck", "proc-macro2", "quote 1.0.9", - "syn 1.0.72", -] - -[[package]] -name = "strum_macros" -version = "0.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee8bc6b87a5112aeeab1f4a9f7ab634fe6cbefc4850006df31267f4cfb9e3149" -dependencies = [ - "heck", - "proc-macro2", - "quote 1.0.9", - "syn 1.0.72", + "syn 1.0.73", ] [[package]] @@ -1756,9 +1756,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.72" +version = "1.0.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1e8cdbefb79a9a5a65e0db8b47b723ee907b7c7f8496c76a1770b5c310bab82" +checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7" dependencies = [ "proc-macro2", "quote 1.0.9", @@ -1798,8 +1798,8 @@ checksum = "0f3ecc17269a19353b3558b313bba738b25d82993e30d62a18406a24aba4649b" dependencies = [ "heck", "pkg-config", - "strum 0.18.0", - "strum_macros 0.18.0", + "strum", + "strum_macros", "thiserror", "toml", "version-compare", @@ -1831,29 +1831,29 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.24" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e" +checksum = "93119e4feac1cbe6c798c34d3a53ea0026b0b1de6a120deef895137c0529bfe2" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.24" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0" +checksum = "060d69a0afe7796bf42e9e2ff91f5ee691fb15c53d38b4b62a9a53eb23164745" dependencies = [ "proc-macro2", "quote 1.0.9", - "syn 1.0.72", + "syn 1.0.73", ] [[package]] name = "tokio" -version = "1.5.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83f0c8e7c0addab50b663055baf787d0af7f413a46e6e7fb9559a4e4db7137a5" +checksum = "98c8b05dc14c75ea83d63dd391100353789f5f24b8b3866542a5e85c8be8e985" dependencies = [ "autocfg", "bytes", @@ -1871,20 +1871,20 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caf7b11a536f46a809a8a9f0bb4237020f70ecbf115b842360afb127ea2fda57" +checksum = "54473be61f4ebe4efd09cec9bd5d16fa51d70ea0192213d754d2d500457db110" dependencies = [ "proc-macro2", "quote 1.0.9", - "syn 1.0.72", + "syn 1.0.73", ] [[package]] name = "tokio-stream" -version = "0.1.5" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e177a5d8c3bf36de9ebe6d58537d8879e964332f93fb3339e43f618c81361af0" +checksum = "7b2f3f698253f03119ac0102beaa64f67a67e08074d03a22d18784104543727f" dependencies = [ "futures-core", "pin-project-lite", @@ -1893,9 +1893,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.6.6" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "940a12c99365c31ea8dd9ba04ec1be183ffe4920102bb7122c2f515437601e8e" +checksum = "1caa0b0c8d94a049db56b5acf8cba99dc0623aab1b26d5b5f5e2d945846b3592" dependencies = [ "bytes", "futures-core", @@ -1914,6 +1914,12 @@ dependencies = [ "serde", ] +[[package]] +name = "ucd-trie" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" + [[package]] name = "unescape" version = "0.1.0" @@ -1922,9 +1928,9 @@ checksum = "ccb97dac3243214f8d8507998906ca3e2e0b900bf9bf4870477f125b82e68f6e" [[package]] name = "unicode-segmentation" -version = "1.7.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" +checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" [[package]] name = "unicode-width" diff --git a/docs/src/configuration.md b/docs/src/configuration.md index 8f2261c..dc55bcb 100644 --- a/docs/src/configuration.md +++ b/docs/src/configuration.md @@ -206,7 +206,7 @@ The `` config should look something like this: ```xml - + @@ -220,7 +220,7 @@ For Wayland users the `` block is replaced by the exclusive field in ` The previous `` block would look like this. ```xml - +
@@ -237,9 +237,6 @@ There are a couple things you can optionally configure on the window itself: - `stacking`: stacking describes on what "layer" of the screen the window is shown. Possible values on the X11 backend: `foreground "fg"`, `background "bg"`. Default: `"fg"` Possible values on the Wayland backend: `foreground "fg"`, `bottom "bt"`, `background "bg"`, `overlay "ov"`. Default: `"fg"` -- `focusable`: whether the window should be focusable by the windowmanager. - This is necessary for things like text-input-fields to work properly. - Possible values: `"true"`, `"false"`. Default: `"false"` - `screen`: Specifies on which display to show the window in a multi-monitor setup. This can be any number, representing the index of your monitor. - `exclusive`: Specifies whether or not a surface can be occupied by another. @@ -247,10 +244,34 @@ There are a couple things you can optionally configure on the window itself: The details on how it is actually implemented are left to the compositor. This option is only valid on Wayland. Possible values: `"true"`, `"false"`. Default: `"false"` +- `focusable`: (Wayland only) whether the window should be able to capture keyboard input. + Possible values: `"true"`, `"false"`. Default: `"false"` +- `wm-ignore`: (X11 only) wether the window should be managed by the window manager. + For a centered widget setup this is recommended to be set to true. For a bar, set the windowtype to `dock` instead. + Note that setting `wm-ignore` will make some other options not work, as those rely on the window manager. + Possible values: `"true"`, `"false"`. Default: `"true"` except if `` is set. - `windowtype`: (X11 only) Can be used in determining the decoration, stacking position and other behavior of the window. - Possible values: + Window managers tend to interpret these differently, so play around with which one works for your usecase! + Possible values: - `"normal"`: indicates that this is a normal, top-level window - - `"dock"`: indicates a dock or panel feature + - `"dock"`: indicates a bar, dock, or panel window + - `"utility"`: indicates a pinned utility window - `"toolbar"`: toolbars "torn off" from the main application - `"dialog"`: indicates that this is a dialog window - - Default: `"dock"` if reserve is set, else `"normal"` + - Default: `"dock"` +- `sticky`: (X11 only) If the window should show up on all workspaces. Note that this may not have any effect, depending on your window manager and the window type. + Possible values: `"true"`, `"false"`. Default: `"true"` +- `resizable`: (X11 only) If the window should be resizable. Note that this may not have any effect, depending on your window manager and the window type. + Possible values: `"true"`, `"false"`. Default: `"true"` + + +### Recommendations for different use-cases on X + +Window positioning is... weird on X11. Different window-managers handle things differently, and some things are just not compatible. +Thus, the following setups are recommendations that will _probably_ work. If they don't try to play around with different settings for any of the X11 only properties. + +- For a bar: + - Set `windowtype` to `dock`, and provide a `reserve` configuration to match your window geometry to make the WM reserve space. + - Set `wm-ignore` to `false`. +- For a centered, full-screen widget setup: + - Set `wm-ignore` to `true`. diff --git a/src/app.rs b/src/app.rs index 367d654..62d27cd 100644 --- a/src/app.rs +++ b/src/app.rs @@ -3,7 +3,7 @@ use crate::{ config::{window_definition::WindowName, AnchorPoint}, display_backend, eww_state, script_var_handler::*, - value::{Coords, NumWithUnit, PrimVal, VarName}, + value::{Coords, PrimVal, VarName}, EwwPaths, }; use anyhow::*; @@ -231,7 +231,7 @@ impl App { log::info!("Opening window {}", window_name); let mut window_def = self.eww_config.get_window(window_name)?.clone(); - window_def.geometry = window_def.geometry.override_if_given(anchor, pos, size); + window_def.geometry = window_def.geometry.map(|x| x.override_if_given(anchor, pos, size)); let root_widget = window_def.widget.render(&mut self.eww_state, window_name, &self.eww_config.get_widget_definitions())?; @@ -245,8 +245,7 @@ impl App { // initialize script var handlers for variables that where not used before opening this window. // TODO somehow make this less shit - for newly_used_var in - self.variables_only_used_in(window_name).filter_map(|var| self.eww_config.get_script_var(var).ok()) + for newly_used_var in self.variables_only_used_in(window_name).filter_map(|var| self.eww_config.get_script_var(var).ok()) { self.script_var_handler.add(newly_used_var.clone()); } @@ -306,55 +305,53 @@ fn initialize_window( root_widget: gtk::Widget, window_def: config::EwwWindowDefinition, ) -> Result { - let actual_window_rect = window_def.geometry.get_window_rectangle(monitor_geometry); - if let Some(window) = display_backend::initialize_window(&window_def, monitor_geometry) { - window.set_title(&format!("Eww - {}", window_def.name)); - let wm_class_name = format!("eww-{}", window_def.name); - window.set_wmclass(&wm_class_name, &wm_class_name); - window.set_position(gtk::WindowPosition::Center); + let window = display_backend::initialize_window(&window_def, monitor_geometry) + .with_context(|| format!("monitor {} is unavailable", window_def.screen_number.unwrap()))?; + + window.set_title(&format!("Eww - {}", window_def.name)); + window.set_position(gtk::WindowPosition::None); + window.set_gravity(gdk::Gravity::Center); + + if let Some(geometry) = window_def.geometry { + let actual_window_rect = geometry.get_window_rectangle(monitor_geometry); window.set_size_request(actual_window_rect.width, actual_window_rect.height); window.set_default_size(actual_window_rect.width, actual_window_rect.height); - window.set_decorated(false); - // run on_screen_changed to set the visual correctly initially. - on_screen_changed(&window, None); - window.connect_screen_changed(on_screen_changed); - - window.add(&root_widget); - - window.show_all(); - - apply_window_position(window_def.clone(), monitor_geometry, &window)?; - let gdk_window = window.get_window().context("couldn't get gdk window from gtk window")?; - gdk_window.set_override_redirect(!window_def.focusable); - - #[cfg(feature = "x11")] - display_backend::set_xprops(&window, monitor_geometry, &window_def)?; - - // this should only be required on x11, as waylands layershell should manage the margins properly anways. - #[cfg(feature = "x11")] - window.connect_configure_event({ - let window_def = window_def.clone(); - move |window, _evt| { - let _ = apply_window_position(window_def.clone(), monitor_geometry, &window); - false - } - }); - Ok(EwwWindow { name: window_def.name.clone(), definition: window_def, gtk_window: window }) - } else { - Err(anyhow!("monitor {} is unavailable", window_def.screen_number.unwrap())) } + window.set_decorated(false); + window.set_skip_taskbar_hint(true); + window.set_skip_pager_hint(true); + + // run on_screen_changed to set the visual correctly initially. + on_screen_changed(&window, None); + window.connect_screen_changed(on_screen_changed); + + window.add(&root_widget); + + window.show_all(); + + #[cfg(feature = "x11")] + { + if let Some(geometry) = window_def.geometry { + let _ = apply_window_position(geometry, monitor_geometry, &window); + window.connect_configure_event(move |window, _| { + let _ = apply_window_position(geometry, monitor_geometry, &window); + false + }); + } + display_backend::set_xprops(&window, monitor_geometry, &window_def)?; + } + Ok(EwwWindow { name: window_def.name.clone(), definition: window_def, gtk_window: window }) } /// Apply the provided window-positioning rules to the window. fn apply_window_position( - mut window_def: config::EwwWindowDefinition, + mut window_geometry: config::EwwWindowGeometry, monitor_geometry: gdk::Rectangle, window: >k::Window, ) -> Result<()> { - let (gtk_window_width, gtk_window_height) = window.get_size(); - window_def.geometry.size = Coords { x: NumWithUnit::Pixels(gtk_window_width), y: NumWithUnit::Pixels(gtk_window_height) }; let gdk_window = window.get_window().context("Failed to get gdk window from gtk window")?; - let actual_window_rect = window_def.geometry.get_window_rectangle(monitor_geometry); + window_geometry.size = Coords::from_pixels(window.get_size()); + let actual_window_rect = window_geometry.get_window_rectangle(monitor_geometry); gdk_window.move_(actual_window_rect.x, actual_window_rect.y); Ok(()) } @@ -367,11 +364,13 @@ fn on_screen_changed(window: >k::Window, _old_screen: Option<&gdk::Screen>) { } fn get_default_monitor_index() -> i32 { + #[allow(deprecated)] gdk::Display::get_default().expect("could not get default display").get_default_screen().get_primary_monitor() } /// Get the monitor geometry of a given monitor number fn get_monitor_geometry(n: i32) -> gdk::Rectangle { + #[allow(deprecated)] gdk::Display::get_default().expect("could not get default display").get_default_screen().get_monitor_geometry(n) } diff --git a/src/config/backend_window_options.rs b/src/config/backend_window_options.rs new file mode 100644 index 0000000..f194b3c --- /dev/null +++ b/src/config/backend_window_options.rs @@ -0,0 +1,69 @@ +use crate::config::xml_ext::XmlElement; +use anyhow::*; + +pub use backend::*; + +#[cfg(feature = "x11")] +mod backend { + + use super::*; + use crate::config::{EwwWindowType, StrutDefinition}; + + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct BackendWindowOptions { + pub wm_ignore: bool, + pub sticky: bool, + pub window_type: EwwWindowType, + pub struts: StrutDefinition, + } + + impl BackendWindowOptions { + pub fn from_xml_element(xml: &XmlElement) -> Result { + let struts: Option = xml + .child("reserve") + .ok() + .map(StrutDefinition::from_xml_element) + .transpose() + .context("Failed to parse ")?; + + let window_type = xml.parse_optional_attr("windowtype")?; + + Ok(BackendWindowOptions { + wm_ignore: xml.parse_optional_attr("wm-ignore")?.unwrap_or(window_type.is_none() && struts.is_none()), + window_type: window_type.unwrap_or_default(), + sticky: xml.parse_optional_attr("sticky")?.unwrap_or(true), + struts: struts.unwrap_or_default(), + }) + } + } +} + +#[cfg(feature = "wayland")] +mod backend { + use super::*; + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct BackendWindowOptions { + pub exclusive: bool, + pub focusable: bool, + } + impl BackendWindowOptions { + pub fn from_xml_element(xml: &XmlElement) -> Result { + Ok(BackendWindowOptions { + exclusive: xml.parse_optional_attr("exclusive")?.unwrap_or(false), + focusable: xml.parse_optional_attr("focusable")?.unwrap_or(false), + }) + } + } +} + +#[cfg(feature = "no-x11-wayland")] +mod backend { + use super::*; + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct BackendWindowOptions; + impl BackendWindowOptions { + pub fn from_xml_element(xml: &XmlElement) -> Result { + Ok(BackendWindowOptions) + } + } +} diff --git a/src/config/mod.rs b/src/config/mod.rs index 361c337..d057d5a 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -8,6 +8,7 @@ use anyhow::*; use element::*; use xml_ext::*; +pub mod backend_window_options; pub mod element; pub mod eww_config; pub mod inbuilt; diff --git a/src/config/window_definition.rs b/src/config/window_definition.rs index 1e28336..e2c85ec 100644 --- a/src/config/window_definition.rs +++ b/src/config/window_definition.rs @@ -1,57 +1,22 @@ -use super::*; -use crate::{ensure_xml_tag_is, value::NumWithUnit, widgets::widget_node}; +use super::{backend_window_options::*, *}; +use crate::{ensure_xml_tag_is, enum_parse, value::NumWithUnit, widgets::widget_node}; use derive_more::*; use serde::{Deserialize, Serialize}; use smart_default::SmartDefault; use std::{collections::HashMap, str::FromStr}; -#[derive(Debug, Clone, PartialEq)] -pub enum EwwWindowType { - Dock, - Dialog, - Toolbar, - Normal, -} -impl FromStr for EwwWindowType { - type Err = anyhow::Error; - - fn from_str(s: &str) -> Result { - match s { - "dock" => Ok(Self::Dock), - "toolbar" => Ok(Self::Toolbar), - "dialog" => Ok(Self::Dialog), - "normal" => Ok(Self::Normal), - x => Err(anyhow!("Unknown windowtype provided '{}'. Possible values are: dock, toolbar, dialog, normal", x)), - } - } -} - -impl Default for EwwWindowType { - fn default() -> Self { - Self::Normal - } -} - /// Full window-definition containing the fully expanded widget tree. -/// **Use this** rather than `[RawEwwWindowDefinition]`. +/// **Use this** rather than [RawEwwWindowDefinition]. #[derive(Debug, Clone)] pub struct EwwWindowDefinition { pub name: WindowName, - - pub geometry: EwwWindowGeometry, + + pub geometry: Option, pub stacking: WindowStacking, pub screen_number: Option, pub widget: Box, - pub focusable: bool, - - #[cfg(feature = "x11")] - pub window_type: EwwWindowType, - - #[cfg(feature = "x11")] - pub struts: StrutDefinition, - - #[cfg(feature = "wayland")] - pub exclusive: bool, + pub resizable: bool, + pub backend_options: BackendWindowOptions, } impl EwwWindowDefinition { @@ -61,14 +26,9 @@ impl EwwWindowDefinition { geometry: window.geometry, stacking: window.stacking, screen_number: window.screen_number, + resizable: window.resizable, widget: widget_node::generate_generic_widget_node(defs, &HashMap::new(), window.widget)?, - focusable: window.focusable, - #[cfg(feature = "x11")] - window_type: window.window_type, - #[cfg(feature = "x11")] - struts: window.struts, - #[cfg(feature = "wayland")] - exclusive: window.exclusive, + backend_options: window.backend_options, }) } } @@ -77,61 +37,58 @@ impl EwwWindowDefinition { #[derive(Debug, Clone, PartialEq)] pub struct RawEwwWindowDefinition { pub name: WindowName, - pub geometry: EwwWindowGeometry, + pub geometry: Option, pub stacking: WindowStacking, - pub screen_number: Option, pub widget: WidgetUse, - pub focusable: bool, - - #[cfg(feature = "x11")] - pub window_type: EwwWindowType, - - #[cfg(feature = "x11")] - pub struts: StrutDefinition, - - #[cfg(feature = "wayland")] - pub exclusive: bool, + pub resizable: bool, + pub backend_options: BackendWindowOptions, + pub screen_number: Option, } impl RawEwwWindowDefinition { pub fn from_xml_element(xml: &XmlElement) -> Result { ensure_xml_tag_is!(xml, "window"); - let stacking: WindowStacking = xml.parse_optional_attr("stacking")?.unwrap_or_default(); - - // TODO maybe rename this to monitor? - let focusable = xml.parse_optional_attr("focusable")?; - let screen_number = xml.parse_optional_attr("screen")?; - - #[cfg(feature = "x11")] - let struts: Option = - xml.child("reserve").ok().map(StrutDefinition::from_xml_element).transpose().context("Failed to parse ")?; + let geometry = match xml.child("geometry") { + Ok(node) => Some(EwwWindowGeometry::from_xml_element(node)?), + Err(_) => None, + }; Ok(RawEwwWindowDefinition { name: WindowName(xml.attr("name")?), - geometry: match xml.child("geometry") { - Ok(node) => EwwWindowGeometry::from_xml_element(node)?, - Err(_) => EwwWindowGeometry::default(), - }, - #[cfg(feature = "x11")] - window_type: match xml.attr("windowtype") { - Ok(v) => EwwWindowType::from_str(&v)?, - Err(_) => match struts { - Some(_) => EwwWindowType::Dock, - None => Default::default(), - }, - }, + geometry, widget: WidgetUse::from_xml_node(xml.child("widget")?.only_child()?)?, - stacking, - screen_number, - focusable: focusable.unwrap_or(false), - #[cfg(feature = "x11")] - struts: struts.unwrap_or_default(), - #[cfg(feature = "wayland")] - exclusive: xml.parse_optional_attr("exclusive")?.unwrap_or_default(), + stacking: xml.parse_optional_attr("stacking")?.unwrap_or_default(), + // TODO maybe rename this to monitor? + screen_number: xml.parse_optional_attr("screen")?, + resizable: xml.parse_optional_attr("resizable")?.unwrap_or(true), + backend_options: BackendWindowOptions::from_xml_element(xml)?, }) } } +#[derive(Debug, Clone, PartialEq, Eq, SmartDefault)] +pub enum EwwWindowType { + #[default] + Dock, + Dialog, + Toolbar, + Normal, + Utility, +} +impl FromStr for EwwWindowType { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + enum_parse! { "window type", s, + "dock" => Self::Dock, + "toolbar" => Self::Toolbar, + "dialog" => Self::Dialog, + "normal" => Self::Normal, + "utility" => Self::Utility, + } + } +} + #[derive(Debug, Clone, Copy, Eq, PartialEq, smart_default::SmartDefault)] pub enum Side { #[default] @@ -145,12 +102,11 @@ impl std::str::FromStr for Side { type Err = anyhow::Error; fn from_str(s: &str) -> Result { - match s { - "l" | "left" => Ok(Side::Left), - "r" | "right" => Ok(Side::Right), - "t" | "top" => Ok(Side::Top), - "b" | "bottom" => Ok(Side::Bottom), - _ => Err(anyhow!("Failed to parse {} as valid side. Must be one of \"left\", \"right\", \"top\", \"bottom\"", s)), + enum_parse! { "side", s, + "l" | "left" => Side::Left, + "r" | "right" => Side::Right, + "t" | "top" => Side::Top, + "b" | "bottom" => Side::Bottom, } } } @@ -182,27 +138,19 @@ impl std::str::FromStr for WindowStacking { #[cfg(not(feature = "wayland"))] fn from_str(s: &str) -> Result { - let s = s.to_lowercase(); - match s.as_str() { - "foreground" | "fg" | "f" => Ok(WindowStacking::Foreground), - "background" | "bg" | "b" => Ok(WindowStacking::Background), - _ => Err(anyhow!("Couldn't parse '{}' as window stacking, must be either foreground, fg, background or bg", s)), + enum_parse! { "WindowStacking", s, + "foreground" | "fg" | "f" => WindowStacking::Foreground, + "background" | "bg" | "b" => WindowStacking::Background, } } #[cfg(feature = "wayland")] fn from_str(s: &str) -> Result { - let s = s.to_lowercase(); - match s.as_str() { - "foreground" | "fg" => Ok(WindowStacking::Foreground), - "background" | "bg" => Ok(WindowStacking::Background), - "bottom" | "bt" => Ok(WindowStacking::Bottom), - "overlay" | "ov" => Ok(WindowStacking::Overlay), - _ => Err(anyhow!( - "Couldn't parse '{}' as window stacking, must be either foreground, fg, background, bg, bottom, bt, overlay or \ - ov", - s - )), + enum_parse! { "WindowStacking", s, + "foreground" | "fg" => WindowStacking::Foreground, + "background" | "bg" => WindowStacking::Background, + "bottom" | "bt" => WindowStacking::Bottom, + "overlay" | "ov" => WindowStacking::Overlay, } } } diff --git a/src/config/window_geometry.rs b/src/config/window_geometry.rs index 4b68c35..8f88f5c 100644 --- a/src/config/window_geometry.rs +++ b/src/config/window_geometry.rs @@ -9,12 +9,12 @@ use super::xml_ext::XmlElement; #[derive(Debug, derive_more::Display, Clone, Copy, Eq, PartialEq, SmartDefault, Serialize, Deserialize)] pub enum AnchorAlignment { - #[display("start")] + #[display(fmt = "start")] #[default] START, - #[display("center")] + #[display(fmt = "center")] CENTER, - #[display("end")] + #[display(fmt = "end")] END, } @@ -117,7 +117,7 @@ impl EwwWindowGeometry { }) } - pub fn override_if_given(&mut self, anchor_point: Option, offset: Option, size: Option) -> Self { + pub fn override_if_given(&self, anchor_point: Option, offset: Option, size: Option) -> Self { EwwWindowGeometry { anchor_point: anchor_point.unwrap_or(self.anchor_point), offset: offset.unwrap_or(self.offset), diff --git a/src/display_backend.rs b/src/display_backend.rs index cac9484..8734842 100644 --- a/src/display_backend.rs +++ b/src/display_backend.rs @@ -7,32 +7,13 @@ mod platform { use gtk::{self, prelude::*}; pub fn initialize_window(window_def: &EwwWindowDefinition, _monitor: gdk::Rectangle) -> Option { - let window = if window_def.focusable { - gtk::Window::new(gtk::WindowType::Toplevel) - } else { - gtk::Window::new(gtk::WindowType::Popup) - }; - window.set_resizable(true); - if !window_def.focusable { - window.set_type_hint(gdk::WindowTypeHint::Dock); - } - if window_def.stacking == WindowStacking::Foreground { - window.set_keep_above(true); - } else { - window.set_keep_below(true); - } - Some(window) - } - - pub fn reserve_space_for(_window: >k::Window, _monitor: gdk::Rectangle, _strut_def: StrutDefinition) -> Result<()> { - Err(anyhow!("Cannot reserve space on non X11 or and wayland backends")) + Some(gtk::Window::new(gtk::WindowType::Toplevel)) } } #[cfg(feature = "wayland")] mod platform { - use crate::config::{AnchorAlignment, EwwWindowDefinition, Side, WindowStacking}; - use anyhow::*; + use crate::config::{AnchorAlignment, EwwWindowDefinition, WindowStacking}; use gdk; use gtk::prelude::*; @@ -46,12 +27,12 @@ mod platform { if let Some(monitor) = gdk::Display::get_default().expect("could not get default display").get_monitor(index) { gtk_layer_shell::set_monitor(&window, &monitor); } else { - return None + return None; } } - None => {}, + None => {} }; - window.set_resizable(true); + window.set_resizable(window_def.resizable); // Sets the layer where the layer shell surface will spawn match window_def.stacking { @@ -62,44 +43,46 @@ mod platform { } // Sets the keyboard interactivity - gtk_layer_shell::set_keyboard_interactivity(&window, window_def.focusable); - // Positioning surface - let mut top = false; - let mut left = false; - let mut right = false; - let mut bottom = false; + gtk_layer_shell::set_keyboard_interactivity(&window, window_def.backend_options.focusable); - match window_def.geometry.anchor_point.x { - AnchorAlignment::START => left = true, - AnchorAlignment::CENTER => {} - AnchorAlignment::END => right = true, + if let Some(geometry) = window_def.geometry { + // Positioning surface + let mut top = false; + let mut left = false; + let mut right = false; + let mut bottom = false; + + match geometry.anchor_point.x { + AnchorAlignment::START => left = true, + AnchorAlignment::CENTER => {} + AnchorAlignment::END => right = true, + } + match geometry.anchor_point.y { + AnchorAlignment::START => top = true, + AnchorAlignment::CENTER => {} + AnchorAlignment::END => bottom = true, + } + + gtk_layer_shell::set_anchor(&window, gtk_layer_shell::Edge::Left, left); + gtk_layer_shell::set_anchor(&window, gtk_layer_shell::Edge::Right, right); + gtk_layer_shell::set_anchor(&window, gtk_layer_shell::Edge::Top, top); + gtk_layer_shell::set_anchor(&window, gtk_layer_shell::Edge::Bottom, bottom); + + let xoffset = geometry.offset.x.relative_to(monitor.width); + let yoffset = geometry.offset.y.relative_to(monitor.height); + + if left { + gtk_layer_shell::set_margin(&window, gtk_layer_shell::Edge::Left, xoffset); + } else { + gtk_layer_shell::set_margin(&window, gtk_layer_shell::Edge::Right, xoffset); + } + if bottom { + gtk_layer_shell::set_margin(&window, gtk_layer_shell::Edge::Bottom, yoffset); + } else { + gtk_layer_shell::set_margin(&window, gtk_layer_shell::Edge::Top, yoffset); + } } - match window_def.geometry.anchor_point.y { - AnchorAlignment::START => top = true, - AnchorAlignment::CENTER => {} - AnchorAlignment::END => bottom = true, - } - - gtk_layer_shell::set_anchor(&window, gtk_layer_shell::Edge::Left, left); - gtk_layer_shell::set_anchor(&window, gtk_layer_shell::Edge::Right, right); - gtk_layer_shell::set_anchor(&window, gtk_layer_shell::Edge::Top, top); - gtk_layer_shell::set_anchor(&window, gtk_layer_shell::Edge::Bottom, bottom); - - let xoffset = window_def.geometry.offset.x.relative_to(monitor.width); - let yoffset = window_def.geometry.offset.y.relative_to(monitor.height); - - if left { - gtk_layer_shell::set_margin(&window, gtk_layer_shell::Edge::Left, xoffset); - } else { - gtk_layer_shell::set_margin(&window, gtk_layer_shell::Edge::Right, xoffset); - } - if bottom { - gtk_layer_shell::set_margin(&window, gtk_layer_shell::Edge::Bottom, yoffset); - } else { - gtk_layer_shell::set_margin(&window, gtk_layer_shell::Edge::Top, yoffset); - } - - if window_def.exclusive { + if window_def.backend_options.exclusive { gtk_layer_shell::auto_exclusive_zone_enable(&window); } Some(window) @@ -122,19 +105,18 @@ mod platform { }; pub fn initialize_window(window_def: &EwwWindowDefinition, _monitor: gdk::Rectangle) -> Option { - let window = if window_def.focusable { - gtk::Window::new(gtk::WindowType::Toplevel) + let window_type = if window_def.backend_options.wm_ignore { gtk::WindowType::Popup } else { gtk::WindowType::Toplevel }; + let window = gtk::Window::new(window_type); + let wm_class_name = format!("eww-{}", window_def.name); + #[allow(deprecated)] + window.set_wmclass(&wm_class_name, &wm_class_name); + window.set_resizable(window_def.resizable); + window.set_keep_above(window_def.stacking == WindowStacking::Foreground); + window.set_keep_below(window_def.stacking == WindowStacking::Background); + if window_def.backend_options.sticky { + window.stick(); } else { - gtk::Window::new(gtk::WindowType::Popup) - }; - window.set_resizable(true); - if !window_def.focusable { - window.set_type_hint(gdk::WindowTypeHint::Dock); - } - if window_def.stacking == WindowStacking::Foreground { - window.set_keep_above(true); - } else { - window.set_keep_below(true); + window.unstick(); } Some(window) } @@ -172,7 +154,7 @@ mod platform { .ok() .context("Failed to get x11 window for gtk window")? .get_xid() as u32; - let strut_def = window_def.struts; + let strut_def = window_def.backend_options.struts; let root_window_geometry = self.conn.get_geometry(self.root_window)?.reply()?; let mon_end_x = (monitor_rect.x + monitor_rect.width) as u32 - 1u32; @@ -225,11 +207,12 @@ mod platform { win_id, self.atoms._NET_WM_WINDOW_TYPE, self.atoms.ATOM, - &[match window_def.window_type { + &[match window_def.backend_options.window_type { EwwWindowType::Dock => self.atoms._NET_WM_WINDOW_TYPE_DOCK, EwwWindowType::Normal => self.atoms._NET_WM_WINDOW_TYPE_NORMAL, EwwWindowType::Dialog => self.atoms._NET_WM_WINDOW_TYPE_DIALOG, EwwWindowType::Toolbar => self.atoms._NET_WM_WINDOW_TYPE_TOOLBAR, + EwwWindowType::Utility => self.atoms._NET_WM_WINDOW_TYPE_UTILITY, }], )? .check()?; @@ -245,6 +228,7 @@ mod platform { _NET_WM_WINDOW_TYPE_DOCK, _NET_WM_WINDOW_TYPE_DIALOG, _NET_WM_WINDOW_TYPE_TOOLBAR, + _NET_WM_WINDOW_TYPE_UTILITY, _NET_WM_STATE, _NET_WM_STATE_STICKY, _NET_WM_STATE_ABOVE, diff --git a/src/util.rs b/src/util.rs index 2048961..4f684f7 100644 --- a/src/util.rs +++ b/src/util.rs @@ -3,23 +3,6 @@ use extend::ext; use itertools::Itertools; use std::path::Path; -#[macro_export] -macro_rules! impl_try_from { - ($typ:ty { - $( - for $for:ty => |$arg:ident| $code:expr - );*; - }) => { - $(impl TryFrom<$typ> for $for { - type Error = anyhow::Error; - - fn try_from($arg: $typ) -> Result { - $code - } - })* - }; -} - #[macro_export] macro_rules! try_logging_errors { ($context:expr => $code:block) => {{ @@ -62,8 +45,8 @@ macro_rules! loop_select { #[macro_export] macro_rules! enum_parse { ($name:literal, $input:expr, $($($s:literal)|* => $val:expr),* $(,)?) => { - let input = $input; - match input { + let input = $input.to_lowercase(); + match input.as_str() { $( $( $s )|* => Ok($val) ),*, _ => Err(anyhow!(concat!("Couldn't parse ", $name, ": '{}'. Possible values are ", $($($s),*),*), input)) } diff --git a/src/value/coords.rs b/src/value/coords.rs index cb50699..b4a12a2 100644 --- a/src/value/coords.rs +++ b/src/value/coords.rs @@ -68,7 +68,7 @@ impl fmt::Debug for Coords { } impl Coords { - pub fn from_pixels(x: i32, y: i32) -> Self { + pub fn from_pixels((x, y): (i32, i32)) -> Self { Coords { x: NumWithUnit::Pixels(x), y: NumWithUnit::Pixels(y) } } diff --git a/src/value/primitive.rs b/src/value/primitive.rs index 98a771c..df49978 100644 --- a/src/value/primitive.rs +++ b/src/value/primitive.rs @@ -3,8 +3,6 @@ use itertools::Itertools; use serde::{Deserialize, Serialize}; use std::{convert::TryFrom, fmt, iter::FromIterator}; -use crate::impl_try_from; - #[derive(Clone, Deserialize, Serialize, derive_more::From, Default)] pub struct PrimVal(pub String); @@ -46,53 +44,32 @@ impl std::str::FromStr for PrimVal { } } -impl_try_from!(PrimVal { +macro_rules! impl_try_from { + (impl From<$typ:ty> { + $(for $for:ty => |$arg:ident| $code:expr);*; + }) => { + $(impl TryFrom<$typ> for $for { + type Error = anyhow::Error; + fn try_from($arg: $typ) -> Result { $code } + })* + }; +} +macro_rules! impl_primval_from { + ($($t:ty),*) => { + $(impl From<$t> for PrimVal { + fn from(x: $t) -> Self { PrimVal(x.to_string()) } + })* + }; +} + +impl_try_from!(impl From { for String => |x| x.as_string(); for f64 => |x| x.as_f64(); for bool => |x| x.as_bool(); for Vec => |x| x.as_vec(); }); -impl From for PrimVal { - fn from(x: bool) -> Self { - PrimVal(x.to_string()) - } -} - -impl From for PrimVal { - fn from(s: i32) -> Self { - PrimVal(s.to_string()) - } -} - -impl From for PrimVal { - fn from(s: u32) -> Self { - PrimVal(s.to_string()) - } -} - -impl From for PrimVal { - fn from(s: f32) -> Self { - PrimVal(s.to_string()) - } -} - -impl From for PrimVal { - fn from(s: u8) -> Self { - PrimVal(s.to_string()) - } -} -impl From for PrimVal { - fn from(s: f64) -> Self { - PrimVal(s.to_string()) - } -} - -impl From<&str> for PrimVal { - fn from(s: &str) -> Self { - PrimVal(s.to_string()) - } -} +impl_primval_from!(bool, i32, u32, f32, u8, f64, &str); impl From<&serde_json::Value> for PrimVal { fn from(v: &serde_json::Value) -> Self { From 38f53074178fc3ed9877935f81e6b393c4405571 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Wed, 21 Jul 2021 20:25:26 +0200 Subject: [PATCH 061/137] Start migration --- Cargo.lock | 2043 ----------------- crates/eww/src/app.rs | 6 +- crates/eww/src/config/element.rs | 2 +- crates/eww/src/config/eww_config.rs | 304 +-- crates/eww/src/config/inbuilt.rs | 14 +- crates/eww/src/config/mod.rs | 2 +- crates/eww/src/config/script_var.rs | 12 +- crates/eww/src/config/window_definition.rs | 170 +- crates/eww/src/config/window_geometry.rs | 2 +- crates/eww/src/eww_state.rs | 22 +- crates/eww/src/main.rs | 1 - crates/eww/src/opts.rs | 8 +- crates/eww/src/script_var_handler.rs | 4 +- crates/eww/src/value/attr_value/attr_value.rs | 209 -- .../src/value/attr_value/attr_value_expr.rs | 244 -- crates/eww/src/value/attr_value/mod.rs | 5 - crates/eww/src/value/attr_value/parser.rs | 224 -- crates/eww/src/value/coords.rs | 107 - crates/eww/src/value/mod.rs | 46 - crates/eww/src/value/primitive.rs | 188 -- crates/eww/src/widgets/mod.rs | 6 +- crates/eww/src/widgets/widget_definitions.rs | 1 - crates/eww/src/widgets/widget_node.rs | 2 +- crates/simplexpr/src/ast.rs | 6 + crates/simplexpr/src/dynval.rs | 4 +- crates/simplexpr/src/error.rs | 4 +- crates/yuck/Cargo.toml | 1 + crates/yuck/examples/validation.rs | 6 +- crates/yuck/src/config/attributes.rs | 6 +- crates/yuck/src/config/config.rs | 8 +- crates/yuck/src/config/mod.rs | 4 +- .../yuck/src/config/script_var_definition.rs | 2 +- crates/yuck/src/error.rs | 6 +- 33 files changed, 149 insertions(+), 3520 deletions(-) delete mode 100644 Cargo.lock delete mode 100644 crates/eww/src/value/attr_value/attr_value.rs delete mode 100644 crates/eww/src/value/attr_value/attr_value_expr.rs delete mode 100644 crates/eww/src/value/attr_value/mod.rs delete mode 100644 crates/eww/src/value/attr_value/parser.rs delete mode 100644 crates/eww/src/value/coords.rs delete mode 100644 crates/eww/src/value/mod.rs delete mode 100644 crates/eww/src/value/primitive.rs diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index 2b142b2..0000000 --- a/Cargo.lock +++ /dev/null @@ -1,2043 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "ahash" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217" - -[[package]] -name = "aho-corasick" -version = "0.7.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" -dependencies = [ - "memchr", -] - -[[package]] -name = "ansi_term" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" -dependencies = [ - "winapi", -] - -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - -[[package]] -name = "anyhow" -version = "1.0.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b" - -[[package]] -name = "anymap" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33954243bd79057c2de7338850b85983a44588021f8a5fee574a8888c6de4344" - -[[package]] -name = "arrayvec" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" - -[[package]] -name = "async-stream" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a26cb53174ddd320edfff199a853f93d571f48eeb4dde75e67a9a3dbb7b7e5e" -dependencies = [ - "async-stream-impl", - "futures-core", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db134ba52475c060f3329a8ef0f8786d6b872ed01515d4b79c162e5798da1340" -dependencies = [ - "proc-macro2", - "quote 1.0.9", - "syn 1.0.72", -] - -[[package]] -name = "atk" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812b4911e210bd51b24596244523c856ca749e6223c50a7fbbba3f89ee37c426" -dependencies = [ - "atk-sys", - "bitflags", - "glib", - "glib-sys", - "gobject-sys", - "libc", -] - -[[package]] -name = "atk-sys" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f530e4af131d94cc4fa15c5c9d0348f0ef28bac64ba660b6b2a1cf2605dedfce" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[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 = "base64" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" - -[[package]] -name = "beef" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "474a626a67200bd107d44179bb3d4fc61891172d11696609264589be6a0e6a43" - -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - -[[package]] -name = "bitflags" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" - -[[package]] -name = "bitvec" -version = "0.19.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8942c8d352ae1838c9dda0b0ca2ab657696ef2232a20147cf1b30ae1a9cb4321" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - -[[package]] -name = "bytes" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" - -[[package]] -name = "cairo-rs" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5c0f2e047e8ca53d0ff249c54ae047931d7a6ebe05d00af73e0ffeb6e34bdb8" -dependencies = [ - "bitflags", - "cairo-sys-rs", - "glib", - "glib-sys", - "gobject-sys", - "libc", - "thiserror", -] - -[[package]] -name = "cairo-sys-rs" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ed2639b9ad5f1d6efa76de95558e11339e7318426d84ac4890b86c03e828ca7" -dependencies = [ - "glib-sys", - "libc", - "system-deps", -] - -[[package]] -name = "cc" -version = "1.0.67" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "clap" -version = "2.33.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" -dependencies = [ - "ansi_term 0.11.0", - "atty", - "bitflags", - "strsim", - "textwrap", - "unicode-width", - "vec_map", -] - -[[package]] -name = "codemap" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e769b5c8c8283982a987c6e948e540254f1058d5a74b8794914d4ef5fc2a24" - -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - -[[package]] -name = "core-foundation-sys" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" - -[[package]] -name = "crossbeam-channel" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9" -dependencies = [ - "cfg-if", - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52fb27eab85b17fbb9f6fd667089e07d6a2eb8743d02639ee7f6a7a7729c9c94" -dependencies = [ - "cfg-if", - "crossbeam-utils", - "lazy_static", - "memoffset", - "scopeguard", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4feb231f0d4d6af81aed15928e58ecf5816aa62a2393e2c82f46973e92a9a278" -dependencies = [ - "autocfg", - "cfg-if", - "lazy_static", -] - -[[package]] -name = "ctor" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e98e2ad1a782e33928b96fc3948e7c355e5af34ba4de7670fe8bac2a3b2006d" -dependencies = [ - "quote 1.0.9", - "syn 1.0.72", -] - -[[package]] -name = "debug_stub_derive" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "496b7f8a2f853313c3ca370641d7ff3e42c32974fdccda8f0684599ed0a3ff6b" -dependencies = [ - "quote 0.3.15", - "syn 0.11.11", -] - -[[package]] -name = "derive_more" -version = "0.99.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f82b1b72f1263f214c0f823371768776c4f5841b942c9883aa8e5ec584fd0ba6" -dependencies = [ - "convert_case", - "proc-macro2", - "quote 1.0.9", - "syn 1.0.72", -] - -[[package]] -name = "diff" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499" - -[[package]] -name = "doc-comment" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" - -[[package]] -name = "dyn-clone" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee2626afccd7561a06cf1367e2950c4718ea04565e20fb5029b6c7d8ad09abcf" - -[[package]] -name = "either" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" - -[[package]] -name = "env_logger" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" -dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", -] - -[[package]] -name = "eww" -version = "0.1.0" -dependencies = [ - "anyhow", - "async-stream", - "base64", - "bincode", - "cairo-sys-rs", - "debug_stub_derive", - "derive_more", - "dyn-clone", - "extend", - "futures-core", - "futures-util", - "gdk", - "gdk-pixbuf", - "gdkx11", - "gio", - "glib", - "grass", - "gtk", - "gtk-layer-shell", - "gtk-layer-shell-sys", - "itertools 0.10.0", - "lazy_static", - "libc", - "log", - "maplit", - "nix", - "nom", - "notify", - "num", - "pretty_assertions", - "pretty_env_logger", - "regex", - "roxmltree", - "serde", - "serde_json", - "simple-signal", - "smart-default", - "structopt", - "sysinfo", - "tokio", - "tokio-stream", - "tokio-util", - "unescape", - "wait-timeout", - "x11rb", -] - -[[package]] -name = "extend" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2feaa8e332f0db2f08788707dc550075aab8e6d20ffc85958e1174e22887d11" -dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote 1.0.9", - "syn 1.0.72", -] - -[[package]] -name = "filetime" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d34cfa13a63ae058bfa601fe9e313bbdb3746427c1459185464ce0fcf62e1e8" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "winapi", -] - -[[package]] -name = "fsevent" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97f347202c95c98805c216f9e1df210e8ebaec9fdb2365700a43c10797a35e63" -dependencies = [ - "bitflags", - "fsevent-sys", -] - -[[package]] -name = "fsevent-sys" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a29c77f1ca394c3e73a9a5d24cfcabb734682d9634fc398f2204a63c994120" -dependencies = [ - "libc", -] - -[[package]] -name = "funty" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" - -[[package]] -name = "futures" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d5813545e459ad3ca1bff9915e9ad7f1a47dc6a91b627ce321d5863b7dd253" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce79c6a52a299137a6013061e0cf0e688fce5d7f1bc60125f520912fdb29ec25" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "098cd1c6dda6ca01650f1a37a794245eb73181d0d4d4e955e2f3c37db7af1815" - -[[package]] -name = "futures-executor" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f6cb7042eda00f0049b1d2080aa4b93442997ee507eb3828e8bd7577f94c9d" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "365a1a1fb30ea1c03a830fdb2158f5236833ac81fa0ad12fe35b29cddc35cb04" - -[[package]] -name = "futures-macro" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "668c6733a182cd7deb4f1de7ba3bf2120823835b3bcfbeacf7d2c4a773c1bb8b" -dependencies = [ - "proc-macro-hack", - "proc-macro2", - "quote 1.0.9", - "syn 1.0.72", -] - -[[package]] -name = "futures-sink" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5629433c555de3d82861a7a4e3794a4c40040390907cfbfd7143a92a426c23" - -[[package]] -name = "futures-task" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba7aa51095076f3ba6d9a1f702f74bd05ec65f555d70d2033d55ba8d69f581bc" - -[[package]] -name = "futures-util" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c144ad54d60f23927f0a6b6d816e4271278b64f005ad65e4e35291d2de9c025" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "proc-macro-hack", - "proc-macro-nested", - "slab", -] - -[[package]] -name = "gdk" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db00839b2a68a7a10af3fa28dfb3febaba3a20c3a9ac2425a33b7df1f84a6b7d" -dependencies = [ - "bitflags", - "cairo-rs", - "cairo-sys-rs", - "gdk-pixbuf", - "gdk-sys", - "gio", - "gio-sys", - "glib", - "glib-sys", - "gobject-sys", - "libc", - "pango", -] - -[[package]] -name = "gdk-pixbuf" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f6dae3cb99dd49b758b88f0132f8d401108e63ae8edd45f432d42cdff99998a" -dependencies = [ - "gdk-pixbuf-sys", - "gio", - "gio-sys", - "glib", - "glib-sys", - "gobject-sys", - "libc", -] - -[[package]] -name = "gdk-pixbuf-sys" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bfe468a7f43e97b8d193a762b6c5cf67a7d36cacbc0b9291dbcae24bfea1e8f" -dependencies = [ - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "gdk-sys" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a9653cfc500fd268015b1ac055ddbc3df7a5c9ea3f4ccef147b3957bd140d69" -dependencies = [ - "cairo-sys-rs", - "gdk-pixbuf-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "pango-sys", - "pkg-config", - "system-deps", -] - -[[package]] -name = "gdkx11" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b89606baa221f9b8d8aa81924fd448c6b107d20de949f0fbf9a4ec203bb54b63" -dependencies = [ - "bitflags", - "gdk", - "gdk-pixbuf", - "gdk-pixbuf-sys", - "gdk-sys", - "gdkx11-sys", - "gio", - "gio-sys", - "glib", - "glib-sys", - "gobject-sys", - "libc", - "pango", - "x11", -] - -[[package]] -name = "gdkx11-sys" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6710388d530f3178ccbeb65cbafdf497a5772c4409eaf574ee9fa461af0a3d09" -dependencies = [ - "gdk-pixbuf-sys", - "gdk-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "pango-sys", - "system-deps", - "x11", -] - -[[package]] -name = "gethostname" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e692e296bfac1d2533ef168d0b60ff5897b8b70a4009276834014dd8924cc028" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "gio" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb60242bfff700772dae5d9e3a1f7aa2e4ebccf18b89662a16acb2822568561" -dependencies = [ - "bitflags", - "futures", - "futures-channel", - "futures-core", - "futures-io", - "futures-util", - "gio-sys", - "glib", - "glib-sys", - "gobject-sys", - "libc", - "once_cell", - "thiserror", -] - -[[package]] -name = "gio-sys" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e24fb752f8f5d2cf6bbc2c606fd2bc989c81c5e2fe321ab974d54f8b6344eac" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", - "winapi", -] - -[[package]] -name = "glib" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c685013b7515e668f1b57a165b009d4d28cb139a8a989bbd699c10dad29d0c5" -dependencies = [ - "bitflags", - "futures-channel", - "futures-core", - "futures-executor", - "futures-task", - "futures-util", - "glib-macros", - "glib-sys", - "gobject-sys", - "libc", - "once_cell", -] - -[[package]] -name = "glib-macros" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41486a26d1366a8032b160b59065a59fb528530a46a49f627e7048fb8c064039" -dependencies = [ - "anyhow", - "heck", - "itertools 0.9.0", - "proc-macro-crate", - "proc-macro-error", - "proc-macro2", - "quote 1.0.9", - "syn 1.0.72", -] - -[[package]] -name = "glib-sys" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7e9b997a66e9a23d073f2b1abb4dbfc3925e0b8952f67efd8d9b6e168e4cdc1" -dependencies = [ - "libc", - "system-deps", -] - -[[package]] -name = "gobject-sys" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "952133b60c318a62bf82ee75b93acc7e84028a093e06b9e27981c2b6fe68218c" -dependencies = [ - "glib-sys", - "libc", - "system-deps", -] - -[[package]] -name = "grass" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8209e6832a1f93e15adca845b43af12ead0cb8221fb2ef615ca84c75e54af1a" -dependencies = [ - "beef", - "clap", - "codemap", - "indexmap", - "lasso", - "num-bigint 0.3.2", - "num-rational 0.3.2", - "num-traits", - "once_cell", - "peekmore", - "phf", - "rand", -] - -[[package]] -name = "gtk" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f022f2054072b3af07666341984562c8e626a79daa8be27b955d12d06a5ad6a" -dependencies = [ - "atk", - "bitflags", - "cairo-rs", - "cairo-sys-rs", - "cc", - "gdk", - "gdk-pixbuf", - "gdk-pixbuf-sys", - "gdk-sys", - "gio", - "gio-sys", - "glib", - "glib-sys", - "gobject-sys", - "gtk-sys", - "libc", - "once_cell", - "pango", - "pango-sys", - "pkg-config", -] - -[[package]] -name = "gtk-layer-shell" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfb41868e4305f14a5b5bc0d5a3fcc0553d3a24f1f176f93c7e136290eaa3b77" -dependencies = [ - "bitflags", - "gdk", - "glib", - "glib-sys", - "gtk", - "gtk-layer-shell-sys", - "libc", -] - -[[package]] -name = "gtk-layer-shell-sys" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b3c2ad4c131bf8e6870686615eb9897ad8c02ca0d4354c575f0d74acb429a0b" -dependencies = [ - "gdk-sys", - "glib-sys", - "gtk-sys", - "libc", - "system-deps", -] - -[[package]] -name = "gtk-sys" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89acda6f084863307d948ba64a4b1ef674e8527dddab147ee4cdcc194c880457" -dependencies = [ - "atk-sys", - "cairo-sys-rs", - "gdk-pixbuf-sys", - "gdk-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "pango-sys", - "system-deps", -] - -[[package]] -name = "hashbrown" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b62f79061a0bc2e046024cb7ba44b08419ed238ecbd9adbd787434b9e8c25" -dependencies = [ - "ahash", - "autocfg", -] - -[[package]] -name = "hashbrown" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" - -[[package]] -name = "heck" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "hermit-abi" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" -dependencies = [ - "libc", -] - -[[package]] -name = "humantime" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" -dependencies = [ - "quick-error", -] - -[[package]] -name = "indexmap" -version = "1.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3" -dependencies = [ - "autocfg", - "hashbrown 0.9.1", -] - -[[package]] -name = "inotify" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d19f57db1baad9d09e43a3cd76dcf82ebdafd37d75c9498b87762dba77c93f15" -dependencies = [ - "bitflags", - "inotify-sys", - "libc", -] - -[[package]] -name = "inotify-sys" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" -dependencies = [ - "libc", -] - -[[package]] -name = "instant" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "itertools" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" -dependencies = [ - "either", -] - -[[package]] -name = "itertools" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d572918e350e82412fe766d24b15e6682fb2ed2bbe018280caa810397cb319" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" - -[[package]] -name = "lasso" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf1a626ea51398f5acf36666c8046ff4bfd048aab88e92db676d2a6eac8805d0" -dependencies = [ - "hashbrown 0.8.2", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "lexical-core" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" -dependencies = [ - "arrayvec", - "bitflags", - "cfg-if", - "ryu", - "static_assertions", -] - -[[package]] -name = "libc" -version = "0.2.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e" - -[[package]] -name = "lock_api" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb" -dependencies = [ - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" -dependencies = [ - "cfg-if", -] - -[[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 = "memoffset" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83fb6581e8ed1f85fd45c116db8405483899489e38406156c25eb743554361d" -dependencies = [ - "autocfg", -] - -[[package]] -name = "mio" -version = "0.7.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf80d3e903b34e0bd7282b218398aec54e082c840d9baf8339e0080a0c542956" -dependencies = [ - "libc", - "log", - "miow", - "ntapi", - "winapi", -] - -[[package]] -name = "miow" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" -dependencies = [ - "winapi", -] - -[[package]] -name = "nix" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa9b4819da1bc61c0ea48b63b7bc8604064dd43013e7cc325df098d49cd7c18a" -dependencies = [ - "bitflags", - "cc", - "cfg-if", - "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 = "notify" -version = "5.0.0-pre.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ebe7699a0f8c5759450716ee03d231685c22b4fe8f406c42c22e0ad94d40ce7" -dependencies = [ - "anymap", - "bitflags", - "crossbeam-channel", - "filetime", - "fsevent", - "fsevent-sys", - "inotify", - "libc", - "mio", - "walkdir", - "winapi", -] - -[[package]] -name = "ntapi" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" -dependencies = [ - "winapi", -] - -[[package]] -name = "num" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606" -dependencies = [ - "num-bigint 0.4.0", - "num-complex", - "num-integer", - "num-iter", - "num-rational 0.4.0", - "num-traits", -] - -[[package]] -name = "num-bigint" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d0a3d5e207573f948a9e5376662aa743a2ea13f7c50a554d7af443a73fbfeba" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-bigint" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e0d047c1062aa51e256408c560894e5251f08925980e53cf1aa5bd00eec6512" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-complex" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26873667bbbb7c5182d4a37c1add32cdf09f841af72da53318fdb81543c15085" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-integer" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-iter" -version = "0.1.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" -dependencies = [ - "autocfg", - "num-bigint 0.3.2", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a" -dependencies = [ - "autocfg", - "num-bigint 0.4.0", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" -dependencies = [ - "autocfg", -] - -[[package]] -name = "num_cpus" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "once_cell" -version = "1.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" - -[[package]] -name = "output_vt100" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9" -dependencies = [ - "winapi", -] - -[[package]] -name = "pango" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9937068580bebd8ced19975938573803273ccbcbd598c58d4906efd4ac87c438" -dependencies = [ - "bitflags", - "glib", - "glib-sys", - "gobject-sys", - "libc", - "once_cell", - "pango-sys", -] - -[[package]] -name = "pango-sys" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d2650c8b62d116c020abd0cea26a4ed96526afda89b1c4ea567131fdefc890" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "parking_lot" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" -dependencies = [ - "cfg-if", - "instant", - "libc", - "redox_syscall", - "smallvec", - "winapi", -] - -[[package]] -name = "peekmore" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4089f831c514cbf080bd19bfce702f7a2de250492730d419204af6710ba19316" -dependencies = [ - "smallvec", -] - -[[package]] -name = "phf" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" -dependencies = [ - "phf_macros", - "phf_shared", - "proc-macro-hack", -] - -[[package]] -name = "phf_generator" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" -dependencies = [ - "phf_shared", - "rand", -] - -[[package]] -name = "phf_macros" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" -dependencies = [ - "phf_generator", - "phf_shared", - "proc-macro-hack", - "proc-macro2", - "quote 1.0.9", - "syn 1.0.72", -] - -[[package]] -name = "phf_shared" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" -dependencies = [ - "siphasher", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc0e1f259c92177c30a4c9d177246edd0a3568b25756a977d0632cf8fa37e905" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkg-config" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" - -[[package]] -name = "ppv-lite86" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" - -[[package]] -name = "pretty_assertions" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cab0e7c02cf376875e9335e0ba1da535775beb5450d21e1dffca068818ed98b" -dependencies = [ - "ansi_term 0.12.1", - "ctor", - "diff", - "output_vt100", -] - -[[package]] -name = "pretty_env_logger" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d" -dependencies = [ - "env_logger", - "log", -] - -[[package]] -name = "proc-macro-crate" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" -dependencies = [ - "toml", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote 1.0.9", - "syn 1.0.72", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote 1.0.9", - "version_check", -] - -[[package]] -name = "proc-macro-hack" -version = "0.5.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" - -[[package]] -name = "proc-macro-nested" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" - -[[package]] -name = "proc-macro2" -version = "1.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" -dependencies = [ - "unicode-xid 0.2.2", -] - -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - -[[package]] -name = "quote" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" - -[[package]] -name = "quote" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom", - "libc", - "rand_chacha", - "rand_core", - "rand_hc", - "rand_pcg", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core", -] - -[[package]] -name = "rand_pcg" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" -dependencies = [ - "rand_core", -] - -[[package]] -name = "rayon" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b0d8e0819fadc20c74ea8373106ead0600e3a67ef1fe8da56e39b9ae7275674" -dependencies = [ - "autocfg", - "crossbeam-deque", - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-utils", - "lazy_static", - "num_cpus", -] - -[[package]] -name = "redox_syscall" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "742739e41cd49414de871ea5e549afb7e2a3ac77b589bcbebe8c82fab37147fc" -dependencies = [ - "bitflags", -] - -[[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 = "roxmltree" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "921904a62e410e37e215c40381b7117f830d9d89ba60ab5236170541dd25646b" -dependencies = [ - "xmlparser", -] - -[[package]] -name = "ryu" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "serde" -version = "1.0.125" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.125" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d" -dependencies = [ - "proc-macro2", - "quote 1.0.9", - "syn 1.0.72", -] - -[[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 = "signal-hook-registry" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6" -dependencies = [ - "libc", -] - -[[package]] -name = "simple-signal" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53f7da44adcc42667d57483bd93f81295f27d66897804b757573b61b6f13288b" -dependencies = [ - "lazy_static", - "libc", -] - -[[package]] -name = "siphasher" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbce6d4507c7e4a3962091436e56e95290cb71fa302d0d270e32130b75fbff27" - -[[package]] -name = "slab" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527" - -[[package]] -name = "smallvec" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" - -[[package]] -name = "smart-default" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "133659a15339456eeeb07572eb02a91c91e9815e9cbc89566944d2c8d3efdbf6" -dependencies = [ - "proc-macro2", - "quote 1.0.9", - "syn 1.0.72", -] - -[[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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" - -[[package]] -name = "structopt" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5277acd7ee46e63e5168a80734c9f6ee81b1367a7d8772a2d765df2a3705d28c" -dependencies = [ - "clap", - "lazy_static", - "structopt-derive", -] - -[[package]] -name = "structopt-derive" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ba9cdfda491b814720b6b06e0cac513d922fc407582032e8706e9f137976f90" -dependencies = [ - "heck", - "proc-macro-error", - "proc-macro2", - "quote 1.0.9", - "syn 1.0.72", -] - -[[package]] -name = "strum" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57bd81eb48f4c437cadc685403cad539345bf703d78e63707418431cecd4522b" - -[[package]] -name = "strum_macros" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87c85aa3f8ea653bfd3ddf25f7ee357ee4d204731f6aa9ad04002306f6e2774c" -dependencies = [ - "heck", - "proc-macro2", - "quote 1.0.9", - "syn 1.0.72", -] - -[[package]] -name = "syn" -version = "0.11.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" -dependencies = [ - "quote 0.3.15", - "synom", - "unicode-xid 0.0.4", -] - -[[package]] -name = "syn" -version = "1.0.72" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1e8cdbefb79a9a5a65e0db8b47b723ee907b7c7f8496c76a1770b5c310bab82" -dependencies = [ - "proc-macro2", - "quote 1.0.9", - "unicode-xid 0.2.2", -] - -[[package]] -name = "synom" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" -dependencies = [ - "unicode-xid 0.0.4", -] - -[[package]] -name = "sysinfo" -version = "0.16.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567e910ef0207be81a4e1bb0491e9a8d9866cf45b20fe1a52c03d347da9ea51b" -dependencies = [ - "cfg-if", - "core-foundation-sys", - "doc-comment", - "libc", - "ntapi", - "once_cell", - "rayon", - "winapi", -] - -[[package]] -name = "system-deps" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3ecc17269a19353b3558b313bba738b25d82993e30d62a18406a24aba4649b" -dependencies = [ - "heck", - "pkg-config", - "strum", - "strum_macros", - "thiserror", - "toml", - "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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] - -[[package]] -name = "thiserror" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0" -dependencies = [ - "proc-macro2", - "quote 1.0.9", - "syn 1.0.72", -] - -[[package]] -name = "tokio" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83f0c8e7c0addab50b663055baf787d0af7f413a46e6e7fb9559a4e4db7137a5" -dependencies = [ - "autocfg", - "bytes", - "libc", - "memchr", - "mio", - "num_cpus", - "once_cell", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "tokio-macros", - "winapi", -] - -[[package]] -name = "tokio-macros" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caf7b11a536f46a809a8a9f0bb4237020f70ecbf115b842360afb127ea2fda57" -dependencies = [ - "proc-macro2", - "quote 1.0.9", - "syn 1.0.72", -] - -[[package]] -name = "tokio-stream" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e177a5d8c3bf36de9ebe6d58537d8879e964332f93fb3339e43f618c81361af0" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "940a12c99365c31ea8dd9ba04ec1be183ffe4920102bb7122c2f515437601e8e" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "log", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "toml" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" -dependencies = [ - "serde", -] - -[[package]] -name = "unescape" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccb97dac3243214f8d8507998906ca3e2e0b900bf9bf4870477f125b82e68f6e" - -[[package]] -name = "unicode-segmentation" -version = "1.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" - -[[package]] -name = "unicode-width" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" - -[[package]] -name = "unicode-xid" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" - -[[package]] -name = "unicode-xid" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" - -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - -[[package]] -name = "version-compare" -version = "0.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d63556a25bae6ea31b52e640d7c41d1ab27faba4ccb600013837a3d0b3994ca1" - -[[package]] -name = "version_check" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" - -[[package]] -name = "wait-timeout" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" -dependencies = [ - "libc", -] - -[[package]] -name = "walkdir" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" -dependencies = [ - "same-file", - "winapi", - "winapi-util", -] - -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - -[[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-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-wsapoll" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c17110f57155602a80dca10be03852116403c9ff3cd25b079d666f2aa3df6e" -dependencies = [ - "winapi", -] - -[[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 = "wyz" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" - -[[package]] -name = "x11" -version = "2.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ecd092546cb16f25783a5451538e73afc8d32e242648d54f4ae5459ba1e773" -dependencies = [ - "libc", - "pkg-config", -] - -[[package]] -name = "x11rb" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ffb080b3f2f616242a4eb8e7d325035312127901025b0052bc3154a282d0f19" -dependencies = [ - "gethostname", - "nix", - "winapi", - "winapi-wsapoll", -] - -[[package]] -name = "xmlparser" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "114ba2b24d2167ef6d67d7d04c8cc86522b87f490025f39f0303b7db5bf5e3d8" diff --git a/crates/eww/src/app.rs b/crates/eww/src/app.rs index 367d654..c89444f 100644 --- a/crates/eww/src/app.rs +++ b/crates/eww/src/app.rs @@ -3,7 +3,7 @@ use crate::{ config::{window_definition::WindowName, AnchorPoint}, display_backend, eww_state, script_var_handler::*, - value::{Coords, NumWithUnit, PrimVal, VarName}, + dynval::{Coords, NumWithUnit, DynVal, VarName}, EwwPaths, }; use anyhow::*; @@ -38,7 +38,7 @@ pub type DaemonResponseReceiver = tokio::sync::mpsc::UnboundedReceiver), + UpdateVars(Vec<(VarName, DynVal)>), ReloadConfigAndCss(DaemonResponseSender), UpdateConfig(config::EwwConfig), UpdateCss(String), @@ -199,7 +199,7 @@ impl App { gtk::main_quit(); } - fn update_state(&mut self, fieldname: VarName, value: PrimVal) { + fn update_state(&mut self, fieldname: VarName, value: DynVal) { self.eww_state.update_variable(fieldname, value) } diff --git a/crates/eww/src/config/element.rs b/crates/eww/src/config/element.rs index 51052fe..98d9445 100644 --- a/crates/eww/src/config/element.rs +++ b/crates/eww/src/config/element.rs @@ -4,7 +4,7 @@ use regex::Regex; use std::ops::Range; use crate::{ - value::{AttrName, AttrVal}, + dynval::{AttrName, AttrVal}, with_text_pos_context, }; use maplit::hashmap; diff --git a/crates/eww/src/config/eww_config.rs b/crates/eww/src/config/eww_config.rs index 88a2494..a6644b8 100644 --- a/crates/eww/src/config/eww_config.rs +++ b/crates/eww/src/config/eww_config.rs @@ -1,50 +1,56 @@ use anyhow::*; use std::collections::HashMap; - -use crate::{ - util, - value::{PrimVal, VarName}, +use yuck::{ + config::{ + script_var_definition::ScriptVarDefinition, widget_definition::WidgetDefinition, window_definition::WindowDefinition, + }, + parser::from_ast::FromAst, + value::VarName, }; -use super::{ - element::WidgetDefinition, - xml_ext::{XmlElement, XmlNode}, - EwwWindowDefinition, RawEwwWindowDefinition, ScriptVar, WindowName, -}; +use simplexpr::dynval::DynVal; + use std::path::PathBuf; /// Eww configuration structure. #[derive(Debug, Clone)] pub struct EwwConfig { widgets: HashMap, - windows: HashMap, - initial_variables: HashMap, - script_vars: HashMap, + windows: HashMap, + initial_variables: HashMap, + script_vars: HashMap, pub filepath: PathBuf, } impl EwwConfig { pub fn read_from_file>(path: P) -> Result { - Self::generate(RawEwwConfig::read_from_file(path)?) + let content = std::fs::read_to_string(path)?; + let ast = yuck::parser::parse_string(0, &content)?; + let config = yuck::config::Config::from_ast(ast)?; + Self::generate(config) } - pub fn generate(conf: RawEwwConfig) -> Result { - let RawEwwConfig { windows, initial_variables, script_vars, filepath, widgets } = conf; + pub fn generate(config: yuck::config::Config) -> Result { Ok(EwwConfig { - windows: windows + windows: config + .window_definitions .into_iter() .map(|(name, window)| { - Ok((name, EwwWindowDefinition::generate(&widgets, window).context("Failed expand window definition")?)) + Ok(( + name, + WindowDefinition::generate(&config.widget_definitions, window) + .context("Failed expand window definition")?, + )) }) .collect::>>()?, - widgets, - initial_variables, - script_vars, - filepath, + widgets: config.widget_definitions, + initial_variables: config.var_definitions.into_iter().map(|(k, v)| (k, v.initial_value)).collect(), + script_vars: config.script_vars, + filepath: todo!(), }) } // TODO this is kinda ugly - pub fn generate_initial_state(&self) -> Result> { + pub fn generate_initial_state(&self) -> Result> { let mut vars = self.script_vars.iter().map(|var| Ok((var.0.clone(), var.1.initial_value()?))).collect::>>()?; vars.extend(self.initial_variables.clone()); @@ -68,222 +74,40 @@ impl EwwConfig { } } -/// Raw Eww configuration, before expanding widget usages. -#[derive(Debug, Clone)] -pub struct RawEwwConfig { - widgets: HashMap, - windows: HashMap, - initial_variables: HashMap, - script_vars: HashMap, - pub filepath: PathBuf, -} +// Raw Eww configuration, before expanding widget usages. +//#[derive(Debug, Clone)] +// pub struct RawEwwConfig { +// widgets: HashMap, +// windows: HashMap, +// initial_variables: HashMap, +// script_vars: HashMap, +// pub filepath: PathBuf, +//} -impl RawEwwConfig { - pub fn merge_includes(mut eww_config: RawEwwConfig, includes: Vec) -> Result { - let config_path = eww_config.filepath.clone(); - let log_conflict = |what: &str, conflict: &str, included_path: &std::path::PathBuf| { - log::error!( - "{} '{}' defined twice (defined in {} and in {})", - what, - conflict, - config_path.display(), - included_path.display() - ); - }; - for included_config in includes { - for conflict in util::extend_safe(&mut eww_config.widgets, included_config.widgets) { - log_conflict("widget", &conflict, &included_config.filepath) - } - for conflict in util::extend_safe(&mut eww_config.windows, included_config.windows) { - log_conflict("window", &conflict.to_string(), &included_config.filepath) - } - for conflict in util::extend_safe(&mut eww_config.script_vars, included_config.script_vars) { - log_conflict("script-var", &conflict.to_string(), &included_config.filepath) - } - for conflict in util::extend_safe(&mut eww_config.initial_variables, included_config.initial_variables) { - log_conflict("var", &conflict.to_string(), &included_config.filepath) - } - } - Ok(eww_config) - } - - 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()?; - - let (config, included_paths) = Self::from_xml_element(root_element.clone(), path.as_ref()) - .with_context(|| format!("Error parsing eww config file {}", path.as_ref().display()))?; - - let parsed_includes = included_paths - .into_iter() - .map(Self::read_from_file) - .collect::>>() - .with_context(|| format!("Included in {}", path.as_ref().display()))?; - - Self::merge_includes(config, parsed_includes) - .context("Failed to merge included files into parent configuration file")? - }; - result.with_context(|| format!("Failed to load eww config file {}", path.as_ref().display())) - } - - pub fn from_xml_element>(xml: XmlElement, path: P) -> Result<(Self, Vec)> { - let path = path.as_ref(); - - let included_paths = match xml.child("includes").ok() { - Some(tag) => tag - .child_elements() - .map(|child| { - crate::ensure_xml_tag_is!(child, "file"); - Ok(util::join_path_pretty(path, PathBuf::from(child.attr("path")?))) - }) - .collect::>>()?, - None => Default::default(), - }; - - let definitions = match xml.child("definitions").ok() { - Some(tag) => tag - .child_elements() - .map(|child| { - let def = WidgetDefinition::from_xml_element(&child).with_context(|| { - format!("Error parsing widget definition at {}:{}", path.display(), &child.text_pos()) - })?; - Ok((def.name.clone(), def)) - }) - .collect::>>()?, - None => Default::default(), - }; - - let windows = match xml.child("windows").ok() { - Some(tag) => tag - .child_elements() - .map(|child| { - let def = RawEwwWindowDefinition::from_xml_element(&child).with_context(|| { - format!("Error parsing window definition at {}:{}", path.display(), &child.text_pos()) - })?; - Ok((def.name.to_owned(), def)) - }) - .collect::>>()?, - None => Default::default(), - }; - - let (initial_variables, script_vars) = match xml.child("variables").ok() { - Some(tag) => parse_variables_block(tag)?, - None => Default::default(), - }; - - let config = RawEwwConfig { widgets: definitions, windows, initial_variables, script_vars, filepath: path.to_path_buf() }; - Ok((config, included_paths)) - } -} - -fn parse_variables_block(xml: XmlElement) -> Result<(HashMap, HashMap)> { - let mut normal_vars = HashMap::new(); - let mut script_vars = HashMap::new(); - for node in xml.child_elements() { - match node.tag_name() { - "var" => { - let value = node.only_child().map(|c| c.as_text_or_sourcecode()).unwrap_or_else(|_| String::new()); - normal_vars.insert(VarName(node.attr("name")?.to_owned()), PrimVal::from_string(value)); - } - "script-var" => { - let script_var = ScriptVar::from_xml_element(node)?; - script_vars.insert(script_var.name().clone(), script_var); - } - _ => bail!("Illegal element in variables block: {}", node.as_tag_string()), - } - } - - // Extends the variables with the predefined variables - let inbuilt = crate::config::inbuilt::get_inbuilt_vars(); - for i in util::extend_safe(&mut script_vars, inbuilt) { - log::error!( - "script-var '{}' defined twice (defined in your config and in the eww included variables)\nHint: don't define any \ - varible like any of these: https://elkowar.github.io/eww/main/magic-variables-documenation/", - i, - ); - } - - Ok((normal_vars, script_vars)) -} - -#[cfg(test)] -mod test { - use crate::config::{RawEwwConfig, XmlNode}; - use std::collections::HashMap; - - #[test] - fn test_merge_includes() { - let input1 = r#" - - - - - {{var1}} - - - - - - var1 - - - - - - - - - - - - "#; - let input2 = r#" - - - - - {{var2}} - - - - - var2 - - - - - - - - - - - - "#; - - let document1 = roxmltree::Document::parse(&input1).unwrap(); - let document2 = roxmltree::Document::parse(input2).unwrap(); - let config1 = - RawEwwConfig::from_xml_element(XmlNode::from(document1.root_element()).as_element().unwrap().clone(), "").unwrap().0; - let config2 = - RawEwwConfig::from_xml_element(XmlNode::from(document2.root_element()).as_element().unwrap().clone(), "").unwrap().0; - let base_config = RawEwwConfig { - widgets: HashMap::new(), - windows: HashMap::new(), - initial_variables: HashMap::new(), - script_vars: HashMap::new(), - filepath: "test_path".into(), - }; - - let merged_config = RawEwwConfig::merge_includes(base_config, vec![config1, config2]).unwrap(); - - assert_eq!(merged_config.widgets.len(), 2); - assert_eq!(merged_config.windows.len(), 2); - assert_eq!(merged_config.initial_variables.len(), 2); - assert_eq!(merged_config.script_vars.len(), 6); - } -} +// impl RawEwwConfig { +// pub fn merge_includes(mut eww_config: RawEwwConfig, includes: Vec) -> Result { +// let config_path = eww_config.filepath.clone(); +// let log_conflict = |what: &str, conflict: &str, included_path: &std::path::PathBuf| { +// log::error!( +//"{} '{}' defined twice (defined in {} and in {})", +// what, +// conflict, +// config_path.display(), +// included_path.display() +//); +//}; +// for included_config in includes { +// for conflict in util::extend_safe(&mut eww_config.widgets, included_config.widgets) { +// log_conflict("widget", &conflict, &included_config.filepath) +//} +// for conflict in util::extend_safe(&mut eww_config.windows, included_config.windows) { +// log_conflict("window", &conflict.to_string(), &included_config.filepath) +//} +// for conflict in util::extend_safe(&mut eww_config.script_vars, included_config.script_vars) { +// log_conflict("script-var", &conflict.to_string(), &included_config.filepath) +//} +// for conflict in util::extend_safe(&mut eww_config.initial_variables, included_config.initial_variables) { +// log_conflict("var", &conflict.to_string(), &included_config.filepath) +//} +// Ok(eww_config) +//} diff --git a/crates/eww/src/config/inbuilt.rs b/crates/eww/src/config/inbuilt.rs index cd9e117..dd9efba 100644 --- a/crates/eww/src/config/inbuilt.rs +++ b/crates/eww/src/config/inbuilt.rs @@ -1,6 +1,6 @@ use crate::{ config::{system_stats::*, PollScriptVar, ScriptVar, VarSource}, - value::{PrimVal as PrimitiveValue, VarName}, + dynval::{DynVal as PrimitiveValue, VarName}, }; use std::{collections::HashMap, time::Duration}; @@ -20,16 +20,16 @@ macro_rules! builtin_vars { pub fn get_inbuilt_vars() -> HashMap { builtin_vars! {Duration::new(2, 0), // @desc EWW_TEMPS - Heat of the components in Celcius\nExample: `{{(CPU_TEMPS.core_1 + CPU_TEMPS.core_2) / 2}}` - "EWW_TEMPS" => || Ok(PrimitiveValue::from(cores())), + "EWW_TEMPS" => || Ok(Primitivedynval::from(cores())), // @desc EWW_RAM - The current RAM + Swap usage - "EWW_RAM" => || Ok(PrimitiveValue::from(format!("{:.2}", ram()))), + "EWW_RAM" => || Ok(Primitivedynval::from(format!("{:.2}", ram()))), // @desc EWW_DISK - Information on on all mounted partitions (Might report inaccurately on some filesystems, like btrfs)\nExample: `{{EWW_DISK["/"]}}` - "EWW_DISK" => || Ok(PrimitiveValue::from(disk())), + "EWW_DISK" => || Ok(Primitivedynval::from(disk())), // @desc EWW_BATTERY - Battery capacity in procent of the main battery - "EWW_BATTERY" => || Ok(PrimitiveValue::from( + "EWW_BATTERY" => || Ok(Primitivedynval::from( match get_battery_capacity() { Err(e) => { log::error!("Couldn't get the battery capacity: {:?}", e); @@ -40,9 +40,9 @@ pub fn get_inbuilt_vars() -> HashMap { )), // @desc EWW_CPU_USAGE - Average CPU usage (all cores) since the last update (No MacOS support) - "EWW_CPU_USAGE" => || Ok(PrimitiveValue::from(get_avg_cpu_usage())), + "EWW_CPU_USAGE" => || Ok(Primitivedynval::from(get_avg_cpu_usage())), // @desc EWW_NET - Bytes up/down on all interfaces - "EWW_NET" => || Ok(PrimitiveValue::from(net())), + "EWW_NET" => || Ok(Primitivedynval::from(net())), } } diff --git a/crates/eww/src/config/mod.rs b/crates/eww/src/config/mod.rs index 361c337..bacbf46 100644 --- a/crates/eww/src/config/mod.rs +++ b/crates/eww/src/config/mod.rs @@ -1,6 +1,6 @@ use crate::{ util, - value::{PrimVal, VarName}, + dynval::{DynVal, VarName}, }; use anyhow::*; diff --git a/crates/eww/src/config/script_var.rs b/crates/eww/src/config/script_var.rs index b3aeda8..0f965e6 100644 --- a/crates/eww/src/config/script_var.rs +++ b/crates/eww/src/config/script_var.rs @@ -9,7 +9,7 @@ use super::*; #[derive(Clone, Debug, PartialEq, Eq)] pub enum VarSource { Shell(String), - Function(fn() -> Result), + Function(fn() -> Result), } #[derive(Clone, Debug, PartialEq, Eq)] pub struct PollScriptVar { @@ -19,7 +19,7 @@ pub struct PollScriptVar { } impl PollScriptVar { - pub fn run_once(&self) -> Result { + pub fn run_once(&self) -> Result { match &self.command { VarSource::Shell(x) => run_command(x), VarSource::Function(x) => x(), @@ -47,7 +47,7 @@ impl ScriptVar { } } - pub fn initial_value(&self) -> Result { + pub fn initial_value(&self) -> Result { match self { ScriptVar::Poll(x) => match &x.command { VarSource::Function(f) => f().with_context(|| format!("Failed to compute initial value for {}", &self.name())), @@ -55,7 +55,7 @@ impl ScriptVar { run_command(f).with_context(|| format!("Failed to compute initial value for {}", &self.name())) } }, - ScriptVar::Tail(_) => Ok(PrimVal::from_string(String::new())), + ScriptVar::Tail(_) => Ok(DynVal::from_string(String::new())), } } @@ -74,9 +74,9 @@ impl ScriptVar { } /// Run a command and get the output -fn run_command(cmd: &str) -> Result { +fn run_command(cmd: &str) -> Result { log::debug!("Running command: {}", cmd); let output = String::from_utf8(Command::new("/bin/sh").arg("-c").arg(cmd).output()?.stdout)?; let output = output.trim_matches('\n'); - Ok(PrimVal::from(output)) + Ok(DynVal::from(output)) } diff --git a/crates/eww/src/config/window_definition.rs b/crates/eww/src/config/window_definition.rs index 1e28336..a8ac3b2 100644 --- a/crates/eww/src/config/window_definition.rs +++ b/crates/eww/src/config/window_definition.rs @@ -1,9 +1,14 @@ use super::*; -use crate::{ensure_xml_tag_is, value::NumWithUnit, widgets::widget_node}; +use crate::{dynval::NumWithUnit, ensure_xml_tag_is, widgets::widget_node}; use derive_more::*; use serde::{Deserialize, Serialize}; use smart_default::SmartDefault; use std::{collections::HashMap, str::FromStr}; +use yuck::config::{ + backend_window_options::StrutDefinition, + window_definition::{WindowDefinition, WindowStacking}, + window_geometry::WindowGeometry, +}; #[derive(Debug, Clone, PartialEq)] pub enum EwwWindowType { @@ -36,31 +41,31 @@ impl Default for EwwWindowType { /// **Use this** rather than `[RawEwwWindowDefinition]`. #[derive(Debug, Clone)] pub struct EwwWindowDefinition { - pub name: WindowName, - - pub geometry: EwwWindowGeometry, + pub name: String, + + pub geometry: WindowGeometry, pub stacking: WindowStacking, - pub screen_number: Option, + pub monitor_number: Option, pub widget: Box, pub focusable: bool, - + #[cfg(feature = "x11")] pub window_type: EwwWindowType, - + #[cfg(feature = "x11")] pub struts: StrutDefinition, - + #[cfg(feature = "wayland")] pub exclusive: bool, } impl EwwWindowDefinition { - pub fn generate(defs: &HashMap, window: RawEwwWindowDefinition) -> Result { + pub fn generate(defs: &HashMap, window: WindowDefinition) -> Result { Ok(EwwWindowDefinition { name: window.name, geometry: window.geometry, stacking: window.stacking, - screen_number: window.screen_number, + monitor_number: window.screen_number, widget: widget_node::generate_generic_widget_node(defs, &HashMap::new(), window.widget)?, focusable: window.focusable, #[cfg(feature = "x11")] @@ -72,148 +77,3 @@ impl EwwWindowDefinition { }) } } - -/// Window-definition storing the raw WidgetUse, as received directly from parsing. -#[derive(Debug, Clone, PartialEq)] -pub struct RawEwwWindowDefinition { - pub name: WindowName, - pub geometry: EwwWindowGeometry, - pub stacking: WindowStacking, - pub screen_number: Option, - pub widget: WidgetUse, - pub focusable: bool, - - #[cfg(feature = "x11")] - pub window_type: EwwWindowType, - - #[cfg(feature = "x11")] - pub struts: StrutDefinition, - - #[cfg(feature = "wayland")] - pub exclusive: bool, -} - -impl RawEwwWindowDefinition { - pub fn from_xml_element(xml: &XmlElement) -> Result { - ensure_xml_tag_is!(xml, "window"); - let stacking: WindowStacking = xml.parse_optional_attr("stacking")?.unwrap_or_default(); - - // TODO maybe rename this to monitor? - let focusable = xml.parse_optional_attr("focusable")?; - let screen_number = xml.parse_optional_attr("screen")?; - - #[cfg(feature = "x11")] - let struts: Option = - xml.child("reserve").ok().map(StrutDefinition::from_xml_element).transpose().context("Failed to parse ")?; - - Ok(RawEwwWindowDefinition { - name: WindowName(xml.attr("name")?), - geometry: match xml.child("geometry") { - Ok(node) => EwwWindowGeometry::from_xml_element(node)?, - Err(_) => EwwWindowGeometry::default(), - }, - #[cfg(feature = "x11")] - window_type: match xml.attr("windowtype") { - Ok(v) => EwwWindowType::from_str(&v)?, - Err(_) => match struts { - Some(_) => EwwWindowType::Dock, - None => Default::default(), - }, - }, - widget: WidgetUse::from_xml_node(xml.child("widget")?.only_child()?)?, - stacking, - screen_number, - focusable: focusable.unwrap_or(false), - #[cfg(feature = "x11")] - struts: struts.unwrap_or_default(), - #[cfg(feature = "wayland")] - exclusive: xml.parse_optional_attr("exclusive")?.unwrap_or_default(), - }) - } -} - -#[derive(Debug, Clone, Copy, Eq, PartialEq, smart_default::SmartDefault)] -pub enum Side { - #[default] - Top, - Left, - Right, - Bottom, -} - -impl std::str::FromStr for Side { - type Err = anyhow::Error; - - fn from_str(s: &str) -> Result { - match s { - "l" | "left" => Ok(Side::Left), - "r" | "right" => Ok(Side::Right), - "t" | "top" => Ok(Side::Top), - "b" | "bottom" => Ok(Side::Bottom), - _ => Err(anyhow!("Failed to parse {} as valid side. Must be one of \"left\", \"right\", \"top\", \"bottom\"", s)), - } - } -} - -// Surface definition if the backend for X11 is enable -#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] -pub struct StrutDefinition { - pub side: Side, - pub dist: NumWithUnit, -} - -impl StrutDefinition { - pub fn from_xml_element(xml: XmlElement) -> Result { - Ok(StrutDefinition { side: xml.attr("side")?.parse()?, dist: xml.attr("distance")?.parse()? }) - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, derive_more::Display, SmartDefault)] -pub enum WindowStacking { - #[default] - Foreground, - Background, - Bottom, - Overlay, -} - -impl std::str::FromStr for WindowStacking { - type Err = anyhow::Error; - - #[cfg(not(feature = "wayland"))] - fn from_str(s: &str) -> Result { - let s = s.to_lowercase(); - match s.as_str() { - "foreground" | "fg" | "f" => Ok(WindowStacking::Foreground), - "background" | "bg" | "b" => Ok(WindowStacking::Background), - _ => Err(anyhow!("Couldn't parse '{}' as window stacking, must be either foreground, fg, background or bg", s)), - } - } - - #[cfg(feature = "wayland")] - fn from_str(s: &str) -> Result { - let s = s.to_lowercase(); - match s.as_str() { - "foreground" | "fg" => Ok(WindowStacking::Foreground), - "background" | "bg" => Ok(WindowStacking::Background), - "bottom" | "bt" => Ok(WindowStacking::Bottom), - "overlay" | "ov" => Ok(WindowStacking::Overlay), - _ => Err(anyhow!( - "Couldn't parse '{}' as window stacking, must be either foreground, fg, background, bg, bottom, bt, overlay or \ - ov", - s - )), - } - } -} - -#[repr(transparent)] -#[derive(Clone, Hash, PartialEq, Eq, AsRef, FromStr, Display, Serialize, Deserialize, Default, From, DebugCustom)] -#[debug(fmt = "WindowName(\".0\")")] -pub struct WindowName(String); - -impl std::borrow::Borrow for WindowName { - fn borrow(&self) -> &str { - &self.0 - } -} diff --git a/crates/eww/src/config/window_geometry.rs b/crates/eww/src/config/window_geometry.rs index 4b68c35..75308d4 100644 --- a/crates/eww/src/config/window_geometry.rs +++ b/crates/eww/src/config/window_geometry.rs @@ -1,4 +1,4 @@ -use crate::value::Coords; +use crate::dynval::Coords; use anyhow::*; use serde::{Deserialize, Serialize}; use smart_default::SmartDefault; diff --git a/crates/eww/src/eww_state.rs b/crates/eww/src/eww_state.rs index 7f929e5..4160c1e 100644 --- a/crates/eww/src/eww_state.rs +++ b/crates/eww/src/eww_state.rs @@ -1,16 +1,16 @@ use crate::{ config::window_definition::WindowName, - value::{AttrName, AttrValElement, VarName}, + dynval::{AttrName, AttrValElement, VarName}, }; use anyhow::*; use std::{collections::HashMap, sync::Arc}; -use crate::value::{AttrVal, PrimVal}; +use crate::dynval::{AttrVal, DynVal}; /// Handler that gets executed to apply the necessary parts of the eww state to /// a gtk widget. These are created and initialized in EwwState::resolve. pub struct StateChangeHandler { - func: Box) -> Result<()> + 'static>, + func: Box) -> Result<()> + 'static>, unresolved_values: HashMap, } @@ -21,7 +21,7 @@ impl StateChangeHandler { /// Run the StateChangeHandler. /// [`state`] should be the global [EwwState::state]. - fn run_with_state(&self, state: &HashMap) { + fn run_with_state(&self, state: &HashMap) { let resolved_attrs = self .unresolved_values .clone() @@ -61,7 +61,7 @@ impl EwwWindowState { #[derive(Default)] pub struct EwwState { windows: HashMap, - variables_state: HashMap, + variables_state: HashMap, } impl std::fmt::Debug for EwwState { @@ -71,11 +71,11 @@ impl std::fmt::Debug for EwwState { } impl EwwState { - pub fn from_default_vars(defaults: HashMap) -> Self { + pub fn from_default_vars(defaults: HashMap) -> Self { EwwState { variables_state: defaults, ..EwwState::default() } } - pub fn get_variables(&self) -> &HashMap { + pub fn get_variables(&self) -> &HashMap { &self.variables_state } @@ -91,7 +91,7 @@ impl EwwState { /// Update the value of a variable, running all registered /// [StateChangeHandler]s. - pub fn update_variable(&mut self, key: VarName, value: PrimVal) { + pub fn update_variable(&mut self, key: VarName, value: DynVal) { self.variables_state.insert(key.clone(), value); // run all of the handlers @@ -103,12 +103,12 @@ impl EwwState { } /// Look up a single variable in the eww state, returning an `Err` when the value is not found. - pub fn lookup(&self, var_name: &VarName) -> Result<&PrimVal> { + pub fn lookup(&self, var_name: &VarName) -> Result<&DynVal> { self.variables_state.get(var_name).with_context(|| format!("Unknown variable '{}' referenced", var_name)) } /// resolves a value if possible, using the current eww_state. - pub fn resolve_once<'a>(&'a self, value: &'a AttrVal) -> Result { + pub fn resolve_once<'a>(&'a self, value: &'a AttrVal) -> Result { value .iter() .map(|element| match element { @@ -120,7 +120,7 @@ impl EwwState { /// Resolve takes a function that applies a set of fully resolved attribute /// values to it's gtk widget. - pub fn resolve) -> Result<()> + 'static + Clone>( + pub fn resolve) -> Result<()> + 'static + Clone>( &mut self, window_name: &WindowName, required_attributes: HashMap, diff --git a/crates/eww/src/main.rs b/crates/eww/src/main.rs index 3fd10e7..a39850c 100644 --- a/crates/eww/src/main.rs +++ b/crates/eww/src/main.rs @@ -29,7 +29,6 @@ pub mod opts; pub mod script_var_handler; pub mod server; pub mod util; -pub mod value; pub mod widgets; fn main() { diff --git a/crates/eww/src/opts.rs b/crates/eww/src/opts.rs index 5e84c99..d6ef99c 100644 --- a/crates/eww/src/opts.rs +++ b/crates/eww/src/opts.rs @@ -5,7 +5,7 @@ use structopt::StructOpt; use crate::{ app, config::{AnchorPoint, WindowName}, - value::{Coords, PrimVal, VarName}, + dynval::{Coords, DynVal, VarName}, }; /// Struct that gets generated from `RawOpt`. @@ -61,7 +61,7 @@ pub enum ActionWithServer { Update { /// variable_name="new_value"-pairs that will be updated #[structopt(parse(try_from_str = parse_var_update_arg))] - mappings: Vec<(VarName, PrimVal)>, + mappings: Vec<(VarName, DynVal)>, }, /// open a window @@ -142,11 +142,11 @@ impl From for Opt { } } -fn parse_var_update_arg(s: &str) -> Result<(VarName, PrimVal)> { +fn parse_var_update_arg(s: &str) -> Result<(VarName, DynVal)> { let (name, value) = s .split_once('=') .with_context(|| format!("arguments must be in the shape `variable_name=\"new_value\"`, but got: {}", s))?; - Ok((name.into(), PrimVal::from_string(value.to_owned()))) + Ok((name.into(), DynVal::from_string(value.to_owned()))) } impl ActionWithServer { diff --git a/crates/eww/src/script_var_handler.rs b/crates/eww/src/script_var_handler.rs index d867a2b..372a77b 100644 --- a/crates/eww/src/script_var_handler.rs +++ b/crates/eww/src/script_var_handler.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use crate::{ app, config, - value::{PrimVal, VarName}, + dynval::{DynVal, VarName}, }; use anyhow::*; use app::DaemonCommand; @@ -197,7 +197,7 @@ impl TailVarHandler { _ = handle.wait() => break, _ = cancellation_token.cancelled() => break, Ok(Some(line)) = stdout_lines.next_line() => { - let new_value = PrimVal::from_string(line.to_owned()); + let new_value = DynVal::from_string(line.to_owned()); evt_send.send(DaemonCommand::UpdateVars(vec![(var.name.to_owned(), new_value)]))?; } else => break, diff --git a/crates/eww/src/value/attr_value/attr_value.rs b/crates/eww/src/value/attr_value/attr_value.rs deleted file mode 100644 index c7359ab..0000000 --- a/crates/eww/src/value/attr_value/attr_value.rs +++ /dev/null @@ -1,209 +0,0 @@ -use anyhow::*; -use itertools::Itertools; -use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, fmt, iter::FromIterator}; - -use super::super::*; - -/// A value assigned to an attribute in a widget. -/// This can be a primitive String that contains any amount of variable -/// references, as would be generated by the string "foo {{var}} bar". -#[derive(Serialize, Deserialize, Clone, PartialEq, derive_more::Into, derive_more::From, Default)] -pub struct AttrVal(Vec); - -impl fmt::Display for AttrVal { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.iter().map(|x| format!("{}", x)).join("")) - } -} - -impl fmt::Debug for AttrVal { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "AttrValue({:?})", self.0) - } -} - -impl IntoIterator for AttrVal { - type IntoIter = std::vec::IntoIter; - type Item = AttrValElement; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} - -impl FromIterator for AttrVal { - fn from_iter>(iter: T) -> Self { - AttrVal(iter.into_iter().collect()) - } -} - -impl AttrVal { - pub fn from_primitive>(v: T) -> Self { - AttrVal(vec![AttrValElement::Primitive(v.into())]) - } - - pub fn from_var_ref>(v: T) -> Self { - AttrVal(vec![AttrValElement::Expr(AttrValExpr::VarRef(v.into()))]) - } - - pub fn iter(&self) -> std::slice::Iter { - self.0.iter() - } - - pub fn var_refs(&self) -> impl Iterator { - self.0.iter().filter_map(|x| Some(x.as_expr()?.var_refs())).flatten() - } - - /// resolve partially. - /// If a var-ref links to another var-ref, that other var-ref is used. - /// If a referenced variable is not found in the given hashmap, returns the var-ref unchanged. - pub fn resolve_one_level(self, variables: &HashMap) -> AttrVal { - self.into_iter() - .map(|entry| match entry { - AttrValElement::Expr(expr) => AttrValElement::Expr(expr.map_terminals_into(|child_expr| match child_expr { - AttrValExpr::VarRef(var_name) => match variables.get(&var_name) { - Some(value) => AttrValExpr::Literal(value.clone()), - None => AttrValExpr::VarRef(var_name), - }, - other => other, - })), - - _ => entry, - }) - .collect() - } - - /// resolve fully. - /// As the variables here have to be primitive values, - /// this enforces that var-refs are not linking to other variables. - pub fn resolve_fully(self, variables: &HashMap) -> Result { - self.into_iter() - .map(|element| match element { - AttrValElement::Primitive(x) => Ok(x), - AttrValElement::Expr(expr) => expr.eval(variables), - }) - .collect() - } - - // TODO this could be a fancy Iterator implementation, ig - pub fn parse_string(s: &str) -> AttrVal { - let mut elements = Vec::new(); - - let mut cur_word = "".to_owned(); - let mut cur_varref: Option = None; - let mut curly_count = 0; - for c in s.chars() { - if let Some(ref mut varref) = cur_varref { - if c == '}' { - curly_count -= 1; - if curly_count == 0 { - elements.push(AttrValElement::Expr(AttrValExpr::parse(varref).unwrap())); - cur_varref = None - } - } else { - curly_count = 2; - varref.push(c); - } - } else if c == '{' { - curly_count += 1; - if curly_count == 2 { - if !cur_word.is_empty() { - elements.push(AttrValElement::primitive(std::mem::take(&mut cur_word))); - } - cur_varref = Some(String::new()) - } - } else { - if curly_count == 1 { - cur_word.push('{'); - } - curly_count = 0; - cur_word.push(c); - } - } - if let Some(unfinished_varref) = cur_varref.take() { - elements.push(AttrValElement::primitive(unfinished_varref)); - } else if !cur_word.is_empty() { - elements.push(AttrValElement::primitive(cur_word)); - } - AttrVal(elements) - } -} - -#[derive(Clone, PartialEq, Serialize, Deserialize)] -pub enum AttrValElement { - Primitive(PrimVal), - Expr(AttrValExpr), -} -impl fmt::Display for AttrValElement { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - AttrValElement::Primitive(x) => write!(f, "{}", x), - AttrValElement::Expr(x) => write!(f, "{{{{{}}}}}", x), - } - } -} - -impl fmt::Debug for AttrValElement { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - AttrValElement::Primitive(x) => write!(f, "Primitive({:?})", x), - AttrValElement::Expr(x) => write!(f, "Expr({:?})", x), - } - } -} - -impl AttrValElement { - pub fn primitive(s: String) -> Self { - AttrValElement::Primitive(PrimVal::from_string(s)) - } - - pub fn as_expr(&self) -> Option<&AttrValExpr> { - match self { - AttrValElement::Expr(x) => Some(x), - _ => None, - } - } - - pub fn as_primitive(&self) -> Option<&PrimVal> { - match self { - AttrValElement::Primitive(x) => Some(x), - _ => None, - } - } -} - -#[cfg(test)] -mod test { - use super::*; - use pretty_assertions::assert_eq; - #[test] - fn test_parse_string_or_var_ref_list() { - let input = "{{foo}}{{bar}}b{}azb{a}z{{bat}}{}quok{{test}}"; - let output = AttrVal::parse_string(input); - assert_eq!( - output, - AttrVal(vec![ - AttrValElement::Expr(AttrValExpr::VarRef(VarName("foo".to_owned()))), - AttrValElement::Expr(AttrValExpr::VarRef(VarName("bar".to_owned()))), - AttrValElement::primitive("b{}azb{a}z".to_owned()), - AttrValElement::Expr(AttrValExpr::VarRef(VarName("bat".to_owned()))), - AttrValElement::primitive("{}quok".to_owned()), - AttrValElement::Expr(AttrValExpr::VarRef(VarName("test".to_owned()))), - ]), - ) - } - #[test] - fn test_parse_string_with_var_refs_attr_value() { - assert_eq!( - AttrVal( - vec![ - AttrValElement::Expr(AttrValExpr::VarRef(VarName("var".to_owned()))), - AttrValElement::primitive("something".to_owned()) - ] - .into() - ), - AttrVal::parse_string("{{var}}something") - ); - } -} diff --git a/crates/eww/src/value/attr_value/attr_value_expr.rs b/crates/eww/src/value/attr_value/attr_value_expr.rs deleted file mode 100644 index f565634..0000000 --- a/crates/eww/src/value/attr_value/attr_value_expr.rs +++ /dev/null @@ -1,244 +0,0 @@ -use super::super::*; -use anyhow::*; -use itertools::Itertools; -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, - Elvis, - RegexMatch, -} - -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, "<"), - BinOp::Elvis => write!(f, "?:"), - BinOp::RegexMatch => 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, Debug)] -pub enum AttrValExpr { - Literal(AttrVal), - VarRef(VarName), - BinOp(Box, BinOp, Box), - UnaryOp(UnaryOp, Box), - IfElse(Box, Box, Box), - JsonAccess(Box, Box), - FunctionCall(String, Vec), -} - -impl std::fmt::Display for AttrValExpr { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - AttrValExpr::VarRef(x) => write!(f, "{}", x), - AttrValExpr::Literal(x) => write!(f, "\"{}\"", x), - AttrValExpr::BinOp(l, op, r) => write!(f, "({} {} {})", l, op, r), - AttrValExpr::UnaryOp(op, x) => write!(f, "{}{}", op, x), - AttrValExpr::IfElse(a, b, c) => write!(f, "(if {} then {} else {})", a, b, c), - AttrValExpr::JsonAccess(value, index) => write!(f, "{}[{}]", value, index), - AttrValExpr::FunctionCall(function_name, args) => write!(f, "{}({})", function_name, args.iter().join(", ")), - } - } -} - -// impl std::fmt::Debug for AttrValueExpr { -// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { -// write!(f, "{:?}", self) -//} - -impl AttrValExpr { - pub fn map_terminals_into(self, f: impl Fn(Self) -> Self) -> Self { - use AttrValExpr::*; - 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 AttrValExpr::*; - match self { - // Literal(x) => Ok(Literal(AttrValue::from_primitive(x.resolve_fully(&variables)?))), - Literal(x) => Ok(Literal(x)), - VarRef(ref name) => Ok(Literal(AttrVal::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)?)) - } - JsonAccess(box a, box b) => Ok(JsonAccess(box a.resolve_refs(variables)?, box b.resolve_refs(variables)?)), - FunctionCall(function_name, args) => { - Ok(FunctionCall(function_name, args.into_iter().map(|a| a.resolve_refs(variables)).collect::>()?)) - } - } - } - - pub fn var_refs(&self) -> Vec<&VarName> { - use AttrValExpr::*; - match self { - Literal(s) => s.var_refs().collect(), - 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 - } - JsonAccess(box a, box b) => { - let mut refs = a.var_refs(); - refs.append(&mut b.var_refs()); - refs - } - FunctionCall(_, args) => args.iter().flat_map(|a| a.var_refs()).collect_vec(), - } - } - - pub fn eval(self, values: &HashMap) -> Result { - match self { - AttrValExpr::Literal(x) => x.resolve_fully(values), - AttrValExpr::VarRef(ref name) => values - .get(name) - .cloned() - .context(format!("Got unresolved variable {} while trying to evaluate expression {:?}", &name, &self)), - AttrValExpr::BinOp(a, op, b) => { - let a = a.eval(values)?; - let b = b.eval(values)?; - Ok(match op { - BinOp::Equals => PrimVal::from(a == b), - BinOp::NotEquals => PrimVal::from(a != b), - BinOp::And => PrimVal::from(a.as_bool()? && b.as_bool()?), - BinOp::Or => PrimVal::from(a.as_bool()? || b.as_bool()?), - - BinOp::Plus => PrimVal::from(a.as_f64()? + b.as_f64()?), - BinOp::Minus => PrimVal::from(a.as_f64()? - b.as_f64()?), - BinOp::Times => PrimVal::from(a.as_f64()? * b.as_f64()?), - BinOp::Div => PrimVal::from(a.as_f64()? / b.as_f64()?), - BinOp::Mod => PrimVal::from(a.as_f64()? % b.as_f64()?), - BinOp::GT => PrimVal::from(a.as_f64()? > b.as_f64()?), - BinOp::LT => PrimVal::from(a.as_f64()? < b.as_f64()?), - BinOp::Elvis => PrimVal::from(if a.0.is_empty() { b } else { a }), - BinOp::RegexMatch => { - let regex = regex::Regex::new(&b.as_string()?)?; - PrimVal::from(regex.is_match(&a.as_string()?)) - } - }) - } - AttrValExpr::UnaryOp(op, a) => { - let a = a.eval(values)?; - Ok(match op { - UnaryOp::Not => PrimVal::from(!a.as_bool()?), - }) - } - AttrValExpr::IfElse(cond, yes, no) => { - if cond.eval(values)?.as_bool()? { - yes.eval(values) - } else { - no.eval(values) - } - } - AttrValExpr::JsonAccess(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(PrimVal::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(PrimVal::from(indexed_value)) - } - _ => bail!("Unable to index into value {}", val), - } - } - AttrValExpr::FunctionCall(function_name, args) => { - let args = args.into_iter().map(|a| a.eval(values)).collect::>()?; - call_expr_function(&function_name, args) - } - } - } - - pub fn parse(s: &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") - } -} - -fn call_expr_function(name: &str, args: Vec) -> Result { - match name { - "round" => match args.as_slice() { - [num, digits] => { - let num = num.as_f64()?; - let digits = digits.as_i32()?; - Ok(PrimVal::from(format!("{:.1$}", num, digits as usize))) - } - _ => Err(anyhow!("Incorrect number of arguments given to {}", name)), - }, - "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(PrimVal::from(pattern.replace_all(&string, replacement.replace("$", "$$").replace("\\", "$")).into_owned())) - } - _ => Err(anyhow!("Incorrect number of arguments given to {}", name)), - }, - _ => Err(anyhow!("Unknown function {}", name)), - } -} diff --git a/crates/eww/src/value/attr_value/mod.rs b/crates/eww/src/value/attr_value/mod.rs deleted file mode 100644 index 077a689..0000000 --- a/crates/eww/src/value/attr_value/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod attr_value; -pub mod attr_value_expr; -pub mod parser; -pub use attr_value::*; -pub use attr_value_expr::*; diff --git a/crates/eww/src/value/attr_value/parser.rs b/crates/eww/src/value/attr_value/parser.rs deleted file mode 100644 index 5f73d1d..0000000 --- a/crates/eww/src/value/attr_value/parser.rs +++ /dev/null @@ -1,224 +0,0 @@ -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, separated_list0}, - 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, f64, VerboseError<&str>> { - let (i, neg) = opt(tag("-"))(i)?; - let (i, num): (_, f64) = map_res(take_while(|c: char| c.is_numeric() || c == '.'), |n: &str| n.parse::())(i)?; - Ok((i, if neg.is_some() { -num } else { num })) -} - -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_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_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) -} - -fn parse_function_call(i: &str) -> IResult<&str, AttrValExpr, VerboseError<&str>> { - let (i, name) = take_while(|c: char| c.is_ascii_alphanumeric() || c == '_')(i)?; - let (i, args) = delimited(tag("("), separated_list0(tag(","), ws(parse_expr)), tag(")"))(i)?; - Ok((i, AttrValExpr::FunctionCall(name.to_string(), args))) -} - -///////////////// -// actual tree // -///////////////// - -fn parse_factor(i: &str) -> IResult<&str, AttrValExpr, 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("function-call", ws(parse_function_call)), - context("literal", map(ws(parse_literal), |x| AttrValExpr::Literal(AttrVal::parse_string(x)))), - context("identifier", map(ws(parse_identifier), |x| AttrValExpr::VarRef(VarName(x.to_string())))), - ))(i)?; - Ok(( - i, - match unary_op { - Some(op) => AttrValExpr::UnaryOp(op, box factor), - None => factor, - }, - )) -} - -fn parse_object_index(i: &str) -> IResult<&str, AttrValExpr, VerboseError<&str>> { - let (i, initial) = parse_factor(i)?; - let (i, remainder) = many0(alt(( - delimited(tag("["), ws(parse_expr), tag("]")), - map(preceded(tag("."), parse_identifier), |x| AttrValExpr::Literal(AttrVal::from_primitive(x))), - )))(i)?; - let indexes = remainder.into_iter().fold(initial, |acc, index| AttrValExpr::JsonAccess(box acc, box index)); - Ok((i, indexes)) -} - -fn parse_term3(i: &str) -> IResult<&str, AttrValExpr, VerboseError<&str>> { - let (i, initial) = parse_object_index(i)?; - let (i, remainder) = many0(alt(( - map(preceded(tag("*"), parse_object_index), |x| (BinOp::Times, x)), - map(preceded(tag("/"), parse_object_index), |x| (BinOp::Div, x)), - map(preceded(tag("%"), parse_object_index), |x| (BinOp::Mod, x)), - )))(i)?; - - let exprs = remainder.into_iter().fold(initial, |acc, (op, expr)| AttrValExpr::BinOp(box acc, op, box expr)); - - Ok((i, exprs)) -} -fn parse_term2(i: &str) -> IResult<&str, AttrValExpr, 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)| AttrValExpr::BinOp(box acc, op, box expr)); - - Ok((i, exprs)) -} - -fn parse_term1(i: &str) -> IResult<&str, AttrValExpr, 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)), - map(preceded(tag("=~"), parse_term2), |x| (BinOp::RegexMatch, x)), - )))(i)?; - - let exprs = remainder.into_iter().fold(initial, |acc, (op, expr)| AttrValExpr::BinOp(box acc, op, box expr)); - - Ok((i, exprs)) -} -pub fn parse_expr(i: &str) -> IResult<&str, AttrValExpr, 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)), - map(preceded(tag("?:"), parse_term1), |x| (BinOp::Elvis, x)), - )))(i)?; - - let exprs = remainder.into_iter().fold(initial, |acc, (op, expr)| AttrValExpr::BinOp(box acc, op, box expr)); - - Ok((i, exprs)) -} - -fn parse_ifelse(i: &str) -> IResult<&str, AttrValExpr, 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, AttrValExpr::IfElse(box a, box b, box c))) -} - -pub fn parse(i: &str) -> IResult<&str, AttrValExpr, VerboseError<&str>> { - complete(parse_expr)(i) -} - -#[cfg(test)] -mod test { - use super::*; - use pretty_assertions::assert_eq; - #[test] - fn test_parser() { - use self::{BinOp::*, UnaryOp::*}; - use AttrValExpr::*; - - assert_eq!(("", 12.22f64), parse_num("12.22").unwrap()); - assert_eq!(Literal(AttrVal::from_primitive("12")), AttrValExpr::parse("12").unwrap()); - assert_eq!(UnaryOp(Not, box Literal(AttrVal::from_primitive("false"))), AttrValExpr::parse("!false").unwrap()); - assert_eq!( - BinOp(box Literal(AttrVal::from_primitive("12")), Plus, box Literal(AttrVal::from_primitive("2"))), - AttrValExpr::parse("12 + 2").unwrap() - ); - assert_eq!( - BinOp( - box FunctionCall( - "test".to_string(), - vec![ - JsonAccess(box VarRef(VarName("foo".to_string())), box Literal(AttrVal::from_primitive("hi"))), - Literal(AttrVal::from_primitive("ho")), - ] - ), - Times, - box Literal(AttrVal::from_primitive(2)) - ), - AttrValExpr::parse(r#"(test(foo["hi"], ("ho")) * 2)"#).unwrap() - ); - assert_eq!( - UnaryOp(Not, box BinOp(box Literal(AttrVal::from_primitive("1")), Equals, box Literal(AttrVal::from_primitive("2")))), - AttrValExpr::parse("!(1 == 2)").unwrap() - ); - assert_eq!( - IfElse( - box VarRef(VarName("a".to_string())), - box VarRef(VarName("b".to_string())), - box VarRef(VarName("c".to_string())), - ), - AttrValExpr::parse("if a then b else c").unwrap() - ); - assert_eq!( - JsonAccess( - box VarRef(VarName("array".to_string())), - box BinOp(box Literal(AttrVal::from_primitive("1")), Plus, box Literal(AttrVal::from_primitive("2"))) - ), - AttrValExpr::parse(r#"(array)[1+2]"#).unwrap() - ); - assert_eq!( - JsonAccess( - box JsonAccess( - box VarRef(VarName("object".to_string())), - box Literal(AttrVal::from_primitive("field".to_string())), - ), - box Literal(AttrVal::from_primitive("field2".to_string())), - ), - AttrValExpr::parse(r#"object.field.field2"#).unwrap() - ); - } - #[test] - fn test_complex() { - let parsed = - AttrValExpr::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/crates/eww/src/value/coords.rs b/crates/eww/src/value/coords.rs deleted file mode 100644 index cb50699..0000000 --- a/crates/eww/src/value/coords.rs +++ /dev/null @@ -1,107 +0,0 @@ -use anyhow::*; -use derive_more::*; -use serde::{Deserialize, Serialize}; -use smart_default::SmartDefault; -use std::{fmt, str::FromStr}; - -#[derive(Clone, Copy, PartialEq, Eq, Deserialize, Serialize, Display, DebugCustom, SmartDefault)] -pub enum NumWithUnit { - #[display(fmt = "{}%", .0)] - #[debug(fmt = "{}%", .0)] - Percent(i32), - #[display(fmt = "{}px", .0)] - #[debug(fmt = "{}px", .0)] - #[default] - Pixels(i32), -} - -impl NumWithUnit { - pub fn relative_to(&self, max: i32) -> i32 { - match *self { - NumWithUnit::Percent(n) => ((max as f64 / 100.0) * n as f64) as i32, - NumWithUnit::Pixels(n) => n, - } - } -} - -impl FromStr for NumWithUnit { - type Err = anyhow::Error; - - fn from_str(s: &str) -> Result { - lazy_static::lazy_static! { - static ref PATTERN: regex::Regex = regex::Regex::new("^(-?\\d+)(.*)$").unwrap(); - }; - - let captures = PATTERN.captures(s).with_context(|| format!("could not parse '{}'", s))?; - let value = captures.get(1).unwrap().as_str().parse::()?; - let value = match captures.get(2).unwrap().as_str() { - "px" | "" => NumWithUnit::Pixels(value), - "%" => NumWithUnit::Percent(value), - _ => bail!("couldn't parse {}, unit must be either px or %", s), - }; - Ok(value) - } -} - -#[derive(Clone, Copy, PartialEq, Eq, Deserialize, Serialize, Display, Default)] -#[display(fmt = "{}*{}", x, y)] -pub struct Coords { - pub x: NumWithUnit, - pub y: NumWithUnit, -} - -impl FromStr for Coords { - type Err = anyhow::Error; - - fn from_str(s: &str) -> Result { - let (x, y) = s - .split_once(|x: char| x.to_ascii_lowercase() == 'x' || x.to_ascii_lowercase() == '*') - .ok_or_else(|| anyhow!("must be formatted like 200x500"))?; - Coords::from_strs(x, y) - } -} - -impl fmt::Debug for Coords { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "CoordsWithUnits({}, {})", self.x, self.y) - } -} - -impl Coords { - pub fn from_pixels(x: i32, y: i32) -> Self { - Coords { x: NumWithUnit::Pixels(x), y: NumWithUnit::Pixels(y) } - } - - /// parse a string for x and a string for y into a [`Coords`] object. - pub fn from_strs(x: &str, y: &str) -> Result { - Ok(Coords { - x: x.parse().with_context(|| format!("Failed to parse '{}'", x))?, - y: y.parse().with_context(|| format!("Failed to parse '{}'", y))?, - }) - } - - /// resolve the possibly relative coordinates relative to a given containers size - pub fn relative_to(&self, width: i32, height: i32) -> (i32, i32) { - (self.x.relative_to(width), self.y.relative_to(height)) - } -} - -#[cfg(test)] -mod test { - use super::*; - use pretty_assertions::assert_eq; - - #[test] - fn test_parse_num_with_unit() { - assert_eq!(NumWithUnit::Pixels(55), NumWithUnit::from_str("55").unwrap()); - assert_eq!(NumWithUnit::Pixels(55), NumWithUnit::from_str("55px").unwrap()); - assert_eq!(NumWithUnit::Percent(55), NumWithUnit::from_str("55%").unwrap()); - assert!(NumWithUnit::from_str("55pp").is_err()); - } - - #[test] - fn test_parse_coords() { - assert_eq!(Coords { x: NumWithUnit::Pixels(50), y: NumWithUnit::Pixels(60) }, Coords::from_str("50x60").unwrap()); - assert!(Coords::from_str("5060").is_err()); - } -} diff --git a/crates/eww/src/value/mod.rs b/crates/eww/src/value/mod.rs deleted file mode 100644 index cd002af..0000000 --- a/crates/eww/src/value/mod.rs +++ /dev/null @@ -1,46 +0,0 @@ -use derive_more::*; -use serde::{Deserialize, Serialize}; - -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::*; - -/// The name of a variable -#[repr(transparent)] -#[derive(Clone, Hash, PartialEq, Eq, Serialize, Deserialize, AsRef, From, FromStr, Display, DebugCustom)] -#[debug(fmt = "VarName({})", .0)] -pub struct VarName(pub String); - -impl std::borrow::Borrow for VarName { - fn borrow(&self) -> &str { - &self.0 - } -} - -impl From<&str> for VarName { - fn from(s: &str) -> Self { - VarName(s.to_owned()) - } -} - -/// The name of an attribute -#[repr(transparent)] -#[derive(Clone, Hash, PartialEq, Eq, Serialize, Deserialize, AsRef, From, FromStr, Display, DebugCustom)] -#[debug(fmt="AttrName({})", .0)] -pub struct AttrName(pub String); - -impl std::borrow::Borrow for AttrName { - fn borrow(&self) -> &str { - &self.0 - } -} - -impl From<&str> for AttrName { - fn from(s: &str) -> Self { - AttrName(s.to_owned()) - } -} diff --git a/crates/eww/src/value/primitive.rs b/crates/eww/src/value/primitive.rs deleted file mode 100644 index 98a771c..0000000 --- a/crates/eww/src/value/primitive.rs +++ /dev/null @@ -1,188 +0,0 @@ -use anyhow::*; -use itertools::Itertools; -use serde::{Deserialize, Serialize}; -use std::{convert::TryFrom, fmt, iter::FromIterator}; - -use crate::impl_try_from; - -#[derive(Clone, Deserialize, Serialize, derive_more::From, Default)] -pub struct PrimVal(pub String); - -impl fmt::Display for PrimVal { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.0) - } -} -impl fmt::Debug for PrimVal { - 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 for PrimVal { - 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 PrimVal { - fn from_iter>(iter: T) -> Self { - PrimVal(iter.into_iter().join("")) - } -} - -impl std::str::FromStr for PrimVal { - type Err = anyhow::Error; - - /// 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 { - Ok(PrimVal::from_string(s.to_string())) - } -} - -impl_try_from!(PrimVal { - for String => |x| x.as_string(); - for f64 => |x| x.as_f64(); - for bool => |x| x.as_bool(); - for Vec => |x| x.as_vec(); -}); - -impl From for PrimVal { - fn from(x: bool) -> Self { - PrimVal(x.to_string()) - } -} - -impl From for PrimVal { - fn from(s: i32) -> Self { - PrimVal(s.to_string()) - } -} - -impl From for PrimVal { - fn from(s: u32) -> Self { - PrimVal(s.to_string()) - } -} - -impl From for PrimVal { - fn from(s: f32) -> Self { - PrimVal(s.to_string()) - } -} - -impl From for PrimVal { - fn from(s: u8) -> Self { - PrimVal(s.to_string()) - } -} -impl From for PrimVal { - fn from(s: f64) -> Self { - PrimVal(s.to_string()) - } -} - -impl From<&str> for PrimVal { - fn from(s: &str) -> Self { - PrimVal(s.to_string()) - } -} - -impl From<&serde_json::Value> for PrimVal { - fn from(v: &serde_json::Value) -> Self { - PrimVal( - v.as_str() - .map(|x| x.to_string()) - .or_else(|| serde_json::to_string(v).ok()) - .unwrap_or_else(|| "".to_string()), - ) - } -} - -impl PrimVal { - pub fn from_string(s: String) -> Self { - PrimVal(s) - } - - pub fn into_inner(self) -> String { - self.0 - } - - /// This will never fail - pub fn as_string(&self) -> Result { - Ok(self.0.to_owned()) - } - - pub fn as_f64(&self) -> Result { - self.0.parse().map_err(|e| anyhow!("couldn't convert {:?} to f64: {}", &self, e)) - } - - pub fn as_i32(&self) -> Result { - self.0.parse().map_err(|e| anyhow!("couldn't convert {:?} to i32: {}", &self, e)) - } - - pub fn as_bool(&self) -> Result { - self.0.parse().map_err(|e| anyhow!("couldn't convert {:?} to bool: {}", &self, e)) - } - - pub fn as_vec(&self) -> Result> { - parse_vec(self.0.to_owned()).map_err(|e| anyhow!("Couldn't convert {:#?} to a vec: {}", &self, e)) - } - - pub fn as_json_value(&self) -> Result { - serde_json::from_str::(&self.0) - .with_context(|| format!("Couldn't convert {:#?} to a json object", &self)) - } -} - -fn parse_vec(a: String) -> Result> { - match a.strip_prefix('[').and_then(|x| x.strip_suffix(']')) { - Some(content) => { - let mut items: Vec = 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(anyhow!("Is your array built like this: '[these,are,items]'?")), - } -} - -#[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"); - } -} diff --git a/crates/eww/src/widgets/mod.rs b/crates/eww/src/widgets/mod.rs index c6214ae..d772fdc 100644 --- a/crates/eww/src/widgets/mod.rs +++ b/crates/eww/src/widgets/mod.rs @@ -1,12 +1,12 @@ use crate::{ config::{element::WidgetDefinition, window_definition::WindowName}, eww_state::*, - value::AttrName, }; use anyhow::*; use gtk::prelude::*; use itertools::Itertools; use std::collections::HashMap; +use yuck::value::AttrName; use std::process::Command; use widget_definitions::*; @@ -132,7 +132,7 @@ macro_rules! resolve_block { let attr_map: Result<_> = try { ::maplit::hashmap! { $( - crate::value::AttrName(::std::stringify!($attr_name).to_owned()) => + yuck::value::AttrName(::std::stringify!($attr_name).to_owned()) => resolve_block!(@get_value $args, &::std::stringify!($attr_name).replace('_', "-"), $(= $default)?) ),* } @@ -154,7 +154,7 @@ macro_rules! resolve_block { }; (@get_value $args:ident, $name:expr, = $default:expr) => { - $args.widget.get_attr($name).cloned().unwrap_or(AttrVal::from_primitive($default)) + $args.widget.get_attr($name).cloned().unwrap_or(simplexpr::SimplExpr::synth_literal($default)) }; (@get_value $args:ident, $name:expr,) => { diff --git a/crates/eww/src/widgets/widget_definitions.rs b/crates/eww/src/widgets/widget_definitions.rs index d806092..2f69689 100644 --- a/crates/eww/src/widgets/widget_definitions.rs +++ b/crates/eww/src/widgets/widget_definitions.rs @@ -3,7 +3,6 @@ use super::{run_command, BuilderArgs}; use crate::{ config, enum_parse, eww_state, resolve_block, util::{list_difference, parse_duration}, - value::AttrVal, widgets::widget_node, }; use anyhow::*; diff --git a/crates/eww/src/widgets/widget_node.rs b/crates/eww/src/widgets/widget_node.rs index bb7171b..c05a80a 100644 --- a/crates/eww/src/widgets/widget_node.rs +++ b/crates/eww/src/widgets/widget_node.rs @@ -5,7 +5,7 @@ use crate::{ WindowName, }, eww_state::EwwState, - value::{AttrName, AttrVal, VarName}, + dynval::{AttrName, AttrVal, VarName}, }; use anyhow::*; use dyn_clone; diff --git a/crates/simplexpr/src/ast.rs b/crates/simplexpr/src/ast.rs index 519b0d3..db1aaf9 100644 --- a/crates/simplexpr/src/ast.rs +++ b/crates/simplexpr/src/ast.rs @@ -5,6 +5,7 @@ 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); +pub static DUMMY_SPAN: Span = Span(usize::MAX, usize::MAX, usize::MAX); impl std::fmt::Display for Span { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -73,6 +74,11 @@ impl SimplExpr { Self::Literal(span, DynVal(s, Some(span))) } + /// Construct a synthetic simplexpr from a literal value, without adding any relevant span information (uses [DUMMY_SPAN]) + pub fn synth_literal(s: String) -> Self { + Self::Literal(DUMMY_SPAN, DynVal(s, Some(DUMMY_SPAN))) + } + pub fn span(&self) -> Span { match self { SimplExpr::Literal(span, _) => *span, diff --git a/crates/simplexpr/src/dynval.rs b/crates/simplexpr/src/dynval.rs index 2ee7c5a..e9685f0 100644 --- a/crates/simplexpr/src/dynval.rs +++ b/crates/simplexpr/src/dynval.rs @@ -10,11 +10,11 @@ pub type Result = std::result::Result; pub struct ConversionError { pub value: DynVal, pub target_type: &'static str, - pub source: Option>, + pub source: Option>, } impl ConversionError { - fn new(value: DynVal, target_type: &'static str, source: impl std::error::Error + 'static) -> Self { + fn new(value: DynVal, target_type: &'static str, source: impl std::error::Error + 'static + Sync + Send) -> Self { ConversionError { value, target_type, source: Some(Box::new(source)) } } diff --git a/crates/simplexpr/src/error.rs b/crates/simplexpr/src/error.rs index 74e193f..187f18d 100644 --- a/crates/simplexpr/src/error.rs +++ b/crates/simplexpr/src/error.rs @@ -14,13 +14,13 @@ pub enum Error { ConversionError(#[from] dynval::ConversionError), #[error("{1}")] - Spanned(Span, Box), + Spanned(Span, Box), #[error(transparent)] Eval(#[from] crate::eval::EvalError), #[error(transparent)] - Other(#[from] Box), + Other(#[from] Box), } impl Error { diff --git a/crates/yuck/Cargo.toml b/crates/yuck/Cargo.toml index b5ab428..c0031dd 100644 --- a/crates/yuck/Cargo.toml +++ b/crates/yuck/Cargo.toml @@ -23,6 +23,7 @@ pretty_assertions = "0.7" strum = { version = "0.21", features = ["derive"] } anyhow = "1" +static_assertions = "1.1" simplexpr = { path = "../simplexpr" } diff --git a/crates/yuck/examples/validation.rs b/crates/yuck/examples/validation.rs index f2cca38..d2c739d 100644 --- a/crates/yuck/examples/validation.rs +++ b/crates/yuck/examples/validation.rs @@ -1,4 +1,4 @@ -use eww_config::{ +use yuck::{ config::{widget_definition::WidgetDefinition, widget_use::WidgetUse, *}, error::AstError, format_diagnostic::ToDiagnostic, @@ -19,8 +19,8 @@ fn main() { let file_id_use = files.add("use.eww", input_use); let file_id_def = files.add("def.eww", input_def); - let parsed_use = WidgetUse::from_ast(eww_config::parser::parse_string(file_id_use, input_use).unwrap()).unwrap(); - let parsed_def = WidgetDefinition::from_ast(eww_config::parser::parse_string(file_id_def, input_def).unwrap()).unwrap(); + let parsed_use = WidgetUse::from_ast(yuck::parser::parse_string(file_id_use, input_use).unwrap()).unwrap(); + let parsed_def = WidgetDefinition::from_ast(yuck::parser::parse_string(file_id_def, input_def).unwrap()).unwrap(); let defs = maplit::hashmap! { "foo".to_string() => parsed_def, }; diff --git a/crates/yuck/src/config/attributes.rs b/crates/yuck/src/config/attributes.rs index b2e668d..34afc6b 100644 --- a/crates/yuck/src/config/attributes.rs +++ b/crates/yuck/src/config/attributes.rs @@ -27,7 +27,7 @@ pub enum AttrError { EvaluationError(Span, EvalError), #[error("{1}")] - Other(Span, Box), + Other(Span, Box), } impl AttrError { @@ -86,7 +86,7 @@ impl Attributes { pub fn primitive_required(&mut self, key: &str) -> Result where - E: std::error::Error + 'static, + E: std::error::Error + 'static + Sync + Send, T: FromDynVal, { let ast: SimplExpr = self.ast_required(&key)?; @@ -99,7 +99,7 @@ impl Attributes { pub fn primitive_optional(&mut self, key: &str) -> Result, AstError> where - E: std::error::Error + 'static, + E: std::error::Error + 'static + Sync + Send, T: FromDynVal, { let ast: SimplExpr = match self.ast_optional(key)? { diff --git a/crates/yuck/src/config/config.rs b/crates/yuck/src/config/config.rs index b969b4f..710e717 100644 --- a/crates/yuck/src/config/config.rs +++ b/crates/yuck/src/config/config.rs @@ -46,10 +46,10 @@ impl FromAst for TopLevel { #[derive(Debug, PartialEq, Eq, Clone, serde::Serialize)] pub struct Config { - widget_definitions: HashMap, - window_definitions: HashMap, - var_definitions: HashMap, - script_vars: HashMap, + pub widget_definitions: HashMap, + pub window_definitions: HashMap, + pub var_definitions: HashMap, + pub script_vars: HashMap, } impl FromAst for Config { diff --git a/crates/yuck/src/config/mod.rs b/crates/yuck/src/config/mod.rs index 2b3a558..3338d5d 100644 --- a/crates/yuck/src/config/mod.rs +++ b/crates/yuck/src/config/mod.rs @@ -1,6 +1,6 @@ pub mod attributes; pub mod backend_window_options; -mod config; +pub mod config; pub mod config_parse_error; pub mod script_var_definition; #[cfg(test)] @@ -11,3 +11,5 @@ pub mod widget_definition; pub mod widget_use; pub mod window_definition; pub mod window_geometry; + +pub use config::*; diff --git a/crates/yuck/src/config/script_var_definition.rs b/crates/yuck/src/config/script_var_definition.rs index 2d12424..5940a93 100644 --- a/crates/yuck/src/config/script_var_definition.rs +++ b/crates/yuck/src/config/script_var_definition.rs @@ -32,7 +32,7 @@ pub enum VarSource { // TODO allow for other executors? (python, etc) Shell(String), #[serde(skip)] - Function(fn() -> Result>), + Function(fn() -> Result>), } #[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)] pub struct PollScriptVar { diff --git a/crates/yuck/src/error.rs b/crates/yuck/src/error.rs index ac99e07..846a5d0 100644 --- a/crates/yuck/src/error.rs +++ b/crates/yuck/src/error.rs @@ -28,7 +28,7 @@ pub enum AstError { ConversionError(#[from] dynval::ConversionError), #[error("{1}")] - Other(Option, Box), + Other(Option, Box), #[error(transparent)] AttrError(#[from] AttrError), @@ -40,6 +40,10 @@ pub enum AstError { ParseError { file_id: Option, source: lalrpop_util::ParseError }, } +// static_assertions::assert_impl_all!(AstError: Send, Sync); +// static_assertions::assert_impl_all!(dynval::ConversionError: Send, Sync); +// static_assertions::assert_impl_all!(lalrpop_util::ParseError < usize, lexer::Token, parse_error::ParseError>: Send, Sync); + impl AstError { pub fn get_span(&self) -> Option { match self { From c752cc928e8930bd20e6b95e0f2558c1b52711be Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Wed, 21 Jul 2021 21:47:02 +0200 Subject: [PATCH 062/137] good progress - state stuff is still broken, and I'm confused --- crates/eww/src/app.rs | 86 +++-- .../eww/src/config/backend_window_options.rs | 69 ---- crates/eww/src/config/element.rs | 176 ---------- crates/eww/src/config/eww_config.rs | 23 +- crates/eww/src/config/inbuilt.rs | 28 +- crates/eww/src/config/mod.rs | 25 -- crates/eww/src/config/script_var.rs | 82 +---- crates/eww/src/config/window_definition.rs | 64 +--- crates/eww/src/config/window_geometry.rs | 144 -------- crates/eww/src/config/xml_ext.rs | 307 ------------------ crates/eww/src/display_backend.rs | 17 +- crates/eww/src/eww_state.rs | 37 +-- crates/eww/src/opts.rs | 17 +- crates/eww/src/script_var_handler.rs | 34 +- crates/eww/src/widgets/mod.rs | 17 +- crates/eww/src/widgets/widget_definitions.rs | 4 +- crates/eww/src/widgets/widget_node.rs | 148 +++++---- crates/simplexpr/src/ast.rs | 9 +- crates/yuck/src/config/attributes.rs | 1 + .../yuck/src/config/backend_window_options.rs | 6 +- .../yuck/src/config/script_var_definition.rs | 2 +- src/config/window_definition.rs | 167 ---------- src/value/primitive.rs | 165 ---------- 23 files changed, 262 insertions(+), 1366 deletions(-) delete mode 100644 crates/eww/src/config/backend_window_options.rs delete mode 100644 crates/eww/src/config/element.rs delete mode 100644 crates/eww/src/config/window_geometry.rs delete mode 100644 crates/eww/src/config/xml_ext.rs delete mode 100644 src/config/window_definition.rs delete mode 100644 src/value/primitive.rs diff --git a/crates/eww/src/app.rs b/crates/eww/src/app.rs index cb57aa2..77456d6 100644 --- a/crates/eww/src/app.rs +++ b/crates/eww/src/app.rs @@ -1,9 +1,7 @@ use crate::{ - config, - config::{window_definition::WindowName, AnchorPoint}, + config::{self, EwwConfig}, display_backend, eww_state, script_var_handler::*, - dynval::{Coords, NumWithUnit, DynVal, VarName}, EwwPaths, }; use anyhow::*; @@ -11,8 +9,13 @@ use debug_stub_derive::*; use gdk::WindowExt; use gtk::{ContainerExt, CssProviderExt, GtkWindowExt, StyleContextExt, WidgetExt}; use itertools::Itertools; +use simplexpr::dynval::DynVal; use std::collections::HashMap; use tokio::sync::mpsc::UnboundedSender; +use yuck::{ + config::window_geometry::{AnchorPoint, WindowGeometry}, + value::{Coords, VarName}, +}; /// Response that the app may send as a response to a event. /// This is used in `DaemonCommand`s that contain a response sender. @@ -43,11 +46,11 @@ pub enum DaemonCommand { UpdateConfig(config::EwwConfig), UpdateCss(String), OpenMany { - windows: Vec, + windows: Vec, sender: DaemonResponseSender, }, OpenWindow { - window_name: WindowName, + window_name: String, pos: Option, size: Option, anchor: Option, @@ -55,7 +58,7 @@ pub enum DaemonCommand { sender: DaemonResponseSender, }, CloseWindow { - window_name: WindowName, + window_name: String, sender: DaemonResponseSender, }, KillServer, @@ -70,7 +73,7 @@ pub enum DaemonCommand { #[derive(Debug, Clone)] pub struct EwwWindow { - pub name: WindowName, + pub name: String, pub definition: config::EwwWindowDefinition, pub gtk_window: gtk::Window, } @@ -85,7 +88,7 @@ impl EwwWindow { pub struct App { pub eww_state: eww_state::EwwState, pub eww_config: config::EwwConfig, - pub open_windows: HashMap, + pub open_windows: HashMap, pub css_provider: gtk::CssProvider, #[debug_stub = "ScriptVarHandler(...)"] @@ -109,27 +112,30 @@ impl App { } } DaemonCommand::ReloadConfigAndCss(sender) => { - let mut errors = Vec::new(); - let config_result = config::RawEwwConfig::read_from_file(&self.paths.get_eww_xml_path()) - .and_then(config::EwwConfig::generate); - match config_result { - Ok(new_config) => self.handle_command(DaemonCommand::UpdateConfig(new_config)), - Err(e) => errors.push(e), - } + // TODO implement this + //let mut errors = Vec::new(); + todo!() - let css_result = crate::util::parse_scss_from_file(&self.paths.get_eww_scss_path()); - match css_result { - Ok(new_css) => self.handle_command(DaemonCommand::UpdateCss(new_css)), - Err(e) => errors.push(e), - } + //let config_result = + //EwwConfig::read_from_file(&self.paths.get_eww_xml_path()).and_then(config::EwwConfig::generate); + //match config_result { + //Ok(new_config) => self.handle_command(DaemonCommand::UpdateConfig(new_config)), + //Err(e) => errors.push(e), + //} - let errors = errors.into_iter().map(|e| format!("{:?}", e)).join("\n"); - if errors.is_empty() { - sender.send(DaemonResponse::Success(String::new()))?; - } else { - sender.send(DaemonResponse::Failure(errors))?; - } + //let css_result = crate::util::parse_scss_from_file(&self.paths.get_eww_scss_path()); + //match css_result { + //Ok(new_css) => self.handle_command(DaemonCommand::UpdateCss(new_css)), + //Err(e) => errors.push(e), + //} + + //let errors = errors.into_iter().map(|e| format!("{:?}", e)).join("\n"); + //if errors.is_empty() { + //sender.send(DaemonResponse::Success(String::new()))?; + //} else { + //sender.send(DaemonResponse::Failure(errors))?; + //} } DaemonCommand::UpdateConfig(config) => { self.load_config(config)?; @@ -203,7 +209,7 @@ impl App { self.eww_state.update_variable(fieldname, value) } - fn close_window(&mut self, window_name: &WindowName) -> Result<()> { + fn close_window(&mut self, window_name: &String) -> Result<()> { for unused_var in self.variables_only_used_in(window_name) { log::info!("stopping for {}", &unused_var); self.script_var_handler.stop_for_variable(unused_var.clone()); @@ -220,11 +226,11 @@ impl App { fn open_window( &mut self, - window_name: &WindowName, + window_name: &String, pos: Option, size: Option, monitor: Option, - anchor: Option, + anchor: Option, ) -> Result<()> { // remove and close existing window with the same name let _ = self.close_window(window_name); @@ -238,7 +244,7 @@ impl App { root_widget.get_style_context().add_class(&window_name.to_string()); let monitor_geometry = - get_monitor_geometry(monitor.or(window_def.screen_number).unwrap_or_else(get_default_monitor_index)); + get_monitor_geometry(monitor.or(window_def.monitor_number).unwrap_or_else(get_default_monitor_index)); let eww_window = initialize_window(monitor_geometry, root_widget, window_def)?; self.open_windows.insert(window_name.clone(), eww_window); @@ -281,7 +287,7 @@ impl App { } /// Get all variables mapped to a list of windows they are being used in. - pub fn currently_used_variables<'a>(&'a self) -> HashMap<&'a VarName, Vec<&'a WindowName>> { + pub fn currently_used_variables<'a>(&'a self) -> HashMap<&'a VarName, Vec<&'a String>> { let mut vars: HashMap<&'a VarName, Vec<_>> = HashMap::new(); for window_name in self.open_windows.keys() { for var in self.eww_state.vars_referenced_in(window_name) { @@ -292,7 +298,7 @@ impl App { } /// Get all variables that are only used in the given window. - pub fn variables_only_used_in<'a>(&'a self, window: &'a WindowName) -> impl Iterator { + pub fn variables_only_used_in<'a>(&'a self, window: &'a String) -> impl Iterator { self.currently_used_variables() .into_iter() .filter(move |(_, wins)| wins.len() == 1 && wins.contains(&window)) @@ -306,14 +312,14 @@ fn initialize_window( window_def: config::EwwWindowDefinition, ) -> Result { let window = display_backend::initialize_window(&window_def, monitor_geometry) - .with_context(|| format!("monitor {} is unavailable", window_def.screen_number.unwrap()))?; + .with_context(|| format!("monitor {} is unavailable", window_def.monitor_number.unwrap()))?; window.set_title(&format!("Eww - {}", window_def.name)); window.set_position(gtk::WindowPosition::None); window.set_gravity(gdk::Gravity::Center); if let Some(geometry) = window_def.geometry { - let actual_window_rect = geometry.get_window_rectangle(monitor_geometry); + let actual_window_rect = get_window_rectangle(geometry, monitor_geometry); window.set_size_request(actual_window_rect.width, actual_window_rect.height); window.set_default_size(actual_window_rect.width, actual_window_rect.height); } @@ -345,13 +351,13 @@ fn initialize_window( /// Apply the provided window-positioning rules to the window. fn apply_window_position( - mut window_geometry: config::EwwWindowGeometry, + mut window_geometry: WindowGeometry, monitor_geometry: gdk::Rectangle, window: >k::Window, ) -> Result<()> { let gdk_window = window.get_window().context("Failed to get gdk window from gtk window")?; window_geometry.size = Coords::from_pixels(window.get_size()); - let actual_window_rect = window_geometry.get_window_rectangle(monitor_geometry); + let actual_window_rect = get_window_rectangle(window_geometry, monitor_geometry); gdk_window.move_(actual_window_rect.x, actual_window_rect.y); Ok(()) } @@ -382,3 +388,11 @@ fn respond_with_error(sender: DaemonResponseSender, result: Result) -> Res } .context("sending response from main thread") } + +pub fn get_window_rectangle(geometry: WindowGeometry, screen_rect: gdk::Rectangle) -> gdk::Rectangle { + let (offset_x, offset_y) = geometry.offset.relative_to(screen_rect.width, screen_rect.height); + let (width, height) = geometry.size.relative_to(screen_rect.width, screen_rect.height); + let x = screen_rect.x + offset_x + geometry.anchor_point.x.alignment_to_coordinate(width, screen_rect.width); + let y = screen_rect.y + offset_y + geometry.anchor_point.y.alignment_to_coordinate(height, screen_rect.height); + gdk::Rectangle { x, y, width, height } +} diff --git a/crates/eww/src/config/backend_window_options.rs b/crates/eww/src/config/backend_window_options.rs deleted file mode 100644 index f194b3c..0000000 --- a/crates/eww/src/config/backend_window_options.rs +++ /dev/null @@ -1,69 +0,0 @@ -use crate::config::xml_ext::XmlElement; -use anyhow::*; - -pub use backend::*; - -#[cfg(feature = "x11")] -mod backend { - - use super::*; - use crate::config::{EwwWindowType, StrutDefinition}; - - #[derive(Debug, Clone, PartialEq, Eq)] - pub struct BackendWindowOptions { - pub wm_ignore: bool, - pub sticky: bool, - pub window_type: EwwWindowType, - pub struts: StrutDefinition, - } - - impl BackendWindowOptions { - pub fn from_xml_element(xml: &XmlElement) -> Result { - let struts: Option = xml - .child("reserve") - .ok() - .map(StrutDefinition::from_xml_element) - .transpose() - .context("Failed to parse ")?; - - let window_type = xml.parse_optional_attr("windowtype")?; - - Ok(BackendWindowOptions { - wm_ignore: xml.parse_optional_attr("wm-ignore")?.unwrap_or(window_type.is_none() && struts.is_none()), - window_type: window_type.unwrap_or_default(), - sticky: xml.parse_optional_attr("sticky")?.unwrap_or(true), - struts: struts.unwrap_or_default(), - }) - } - } -} - -#[cfg(feature = "wayland")] -mod backend { - use super::*; - #[derive(Debug, Clone, PartialEq, Eq)] - pub struct BackendWindowOptions { - pub exclusive: bool, - pub focusable: bool, - } - impl BackendWindowOptions { - pub fn from_xml_element(xml: &XmlElement) -> Result { - Ok(BackendWindowOptions { - exclusive: xml.parse_optional_attr("exclusive")?.unwrap_or(false), - focusable: xml.parse_optional_attr("focusable")?.unwrap_or(false), - }) - } - } -} - -#[cfg(feature = "no-x11-wayland")] -mod backend { - use super::*; - #[derive(Debug, Clone, PartialEq, Eq)] - pub struct BackendWindowOptions; - impl BackendWindowOptions { - pub fn from_xml_element(xml: &XmlElement) -> Result { - Ok(BackendWindowOptions) - } - } -} diff --git a/crates/eww/src/config/element.rs b/crates/eww/src/config/element.rs deleted file mode 100644 index 98d9445..0000000 --- a/crates/eww/src/config/element.rs +++ /dev/null @@ -1,176 +0,0 @@ -use super::*; -use lazy_static::lazy_static; -use regex::Regex; -use std::ops::Range; - -use crate::{ - dynval::{AttrName, AttrVal}, - with_text_pos_context, -}; -use maplit::hashmap; -use std::collections::HashMap; - -#[derive(Debug, Clone, PartialEq)] -pub struct WidgetDefinition { - pub name: String, - pub structure: WidgetUse, - pub size: Option<(i32, i32)>, -} - -impl WidgetDefinition { - pub fn from_xml_element(xml: &XmlElement) -> Result { - with_text_pos_context! { xml => - if xml.tag_name() != "def" { - bail!( - "{} | Illegal element: only may be used in definition block, but found '{}'", - xml.text_pos(), - xml.as_tag_string() - ); - } - - WidgetDefinition { - name: xml.attr("name")?, - size: Option::zip(xml.parse_optional_attr("width")?, xml.parse_optional_attr("height")?), - structure: WidgetUse::from_xml_node(xml.only_child()?)?, - } - } - } -} - -#[derive(Debug, Clone, Default)] -pub struct WidgetUse { - pub name: String, - pub children: Vec, - pub attrs: HashMap, - pub text_pos: Option, -} - -#[derive(Debug, Clone)] -pub struct PositionData { - pub range: Range, -} - -impl PartialEq for WidgetUse { - fn eq(&self, other: &WidgetUse) -> bool { - self.name == other.name && self.children == other.children && self.attrs == other.attrs - } -} - -impl WidgetUse { - pub fn new(name: String, children: Vec) -> Self { - WidgetUse { name, children, attrs: HashMap::new(), ..WidgetUse::default() } - } - - pub fn from_xml_node(xml: XmlNode) -> Result { - lazy_static! { - static ref PATTERN: Regex = Regex::new("\\{\\{(.*)\\}\\}").unwrap(); - }; - let text_pos = xml.text_pos(); - let widget_use = match xml { - XmlNode::Text(text) => WidgetUse::simple_text(AttrVal::parse_string(&text.text())), - XmlNode::Element(elem) => WidgetUse { - name: elem.tag_name().to_owned(), - children: with_text_pos_context! { elem => elem.children().map(WidgetUse::from_xml_node).collect::>()?}?, - attrs: elem - .attributes() - .iter() - .map(|attr| { - ( - AttrName(attr.name().to_owned()), - AttrVal::parse_string(&xml_ext::resolve_escaped_symbols(attr.value())), - ) - }) - .collect::>(), - ..WidgetUse::default() - }, - XmlNode::Ignored(_) => bail!("{} | Failed to parse node {:?} as widget use", xml.text_pos(), xml), - }; - Ok(widget_use.at_pos(text_pos)) - } - - pub fn simple_text(text: AttrVal) -> Self { - WidgetUse { - name: "label".to_owned(), - children: vec![], - attrs: hashmap! { AttrName("text".to_owned()) => text }, // TODO this hardcoded "text" is dumdum - ..WidgetUse::default() - } - } - - pub fn at_pos(mut self, text_pos: TextPos) -> Self { - self.text_pos = Some(text_pos); - self - } -} - -#[cfg(test)] -mod test { - use super::*; - use maplit::hashmap; - use pretty_assertions::assert_eq; - - #[test] - fn test_simple_text() { - let expected_attr_value = AttrVal::from_primitive("my text"); - let widget = WidgetUse::simple_text(expected_attr_value.clone()); - assert_eq!( - widget, - WidgetUse { - name: "label".to_owned(), - children: Vec::new(), - attrs: hashmap! { AttrName("text".to_owned()) => expected_attr_value}, - ..WidgetUse::default() - }, - ); - } - - #[test] - fn test_parse_widget_use() { - let input = r#" - - - foo - - "#; - let document = roxmltree::Document::parse(input).unwrap(); - let xml = XmlNode::from(document.root_element().clone()); - - let expected = WidgetUse { - name: "widget_name".to_owned(), - attrs: hashmap! { - AttrName("attr1".to_owned()) => AttrVal::from_primitive("hi"), - AttrName("attr2".to_owned()) => AttrVal::from_primitive("12"), - }, - children: vec![ - WidgetUse::new("child_widget".to_owned(), Vec::new()), - WidgetUse::simple_text(AttrVal::from_primitive("foo".to_owned())), - ], - ..WidgetUse::default() - }; - assert_eq!(expected, WidgetUse::from_xml_node(xml).unwrap()); - } - - #[test] - fn test_parse_widget_definition() { - let input = r#" - - test - - "#; - let document = roxmltree::Document::parse(input).unwrap(); - let xml = XmlNode::from(document.root_element().clone()); - - let expected = WidgetDefinition { - name: "foo".to_owned(), - size: Some((12, 20)), - structure: WidgetUse { - name: "layout".to_owned(), - children: vec![WidgetUse::simple_text(AttrVal::from_primitive("test"))], - attrs: HashMap::new(), - ..WidgetUse::default() - }, - }; - - assert_eq!(expected, WidgetDefinition::from_xml_element(xml.as_element().unwrap()).unwrap()); - } -} diff --git a/crates/eww/src/config/eww_config.rs b/crates/eww/src/config/eww_config.rs index a6644b8..ed1af4b 100644 --- a/crates/eww/src/config/eww_config.rs +++ b/crates/eww/src/config/eww_config.rs @@ -1,9 +1,7 @@ use anyhow::*; use std::collections::HashMap; use yuck::{ - config::{ - script_var_definition::ScriptVarDefinition, widget_definition::WidgetDefinition, window_definition::WindowDefinition, - }, + config::{script_var_definition::ScriptVarDefinition, widget_definition::WidgetDefinition}, parser::from_ast::FromAst, value::VarName, }; @@ -12,11 +10,13 @@ use simplexpr::dynval::DynVal; use std::path::PathBuf; +use super::{script_var, EwwWindowDefinition}; + /// Eww configuration structure. #[derive(Debug, Clone)] pub struct EwwConfig { widgets: HashMap, - windows: HashMap, + windows: HashMap, initial_variables: HashMap, script_vars: HashMap, pub filepath: PathBuf, @@ -37,7 +37,7 @@ impl EwwConfig { .map(|(name, window)| { Ok(( name, - WindowDefinition::generate(&config.widget_definitions, window) + EwwWindowDefinition::generate(&config.widget_definitions, window) .context("Failed expand window definition")?, )) }) @@ -51,21 +51,24 @@ impl EwwConfig { // TODO this is kinda ugly pub fn generate_initial_state(&self) -> Result> { - let mut vars = - self.script_vars.iter().map(|var| Ok((var.0.clone(), var.1.initial_value()?))).collect::>>()?; + let mut vars = self + .script_vars + .iter() + .map(|(name, var)| Ok((name.clone(), script_var::initial_value(var)?))) + .collect::>>()?; vars.extend(self.initial_variables.clone()); Ok(vars) } - pub fn get_windows(&self) -> &HashMap { + pub fn get_windows(&self) -> &HashMap { &self.windows } - pub fn get_window(&self, name: &WindowName) -> Result<&EwwWindowDefinition> { + pub fn get_window(&self, name: &String) -> Result<&EwwWindowDefinition> { self.windows.get(name).with_context(|| format!("No window named '{}' exists", name)) } - pub fn get_script_var(&self, name: &VarName) -> Result<&ScriptVar> { + pub fn get_script_var(&self, name: &VarName) -> Result<&ScriptVarDefinition> { self.script_vars.get(name).with_context(|| format!("No script var named '{}' exists", name)) } diff --git a/crates/eww/src/config/inbuilt.rs b/crates/eww/src/config/inbuilt.rs index dd9efba..5f98212 100644 --- a/crates/eww/src/config/inbuilt.rs +++ b/crates/eww/src/config/inbuilt.rs @@ -1,14 +1,18 @@ -use crate::{ - config::{system_stats::*, PollScriptVar, ScriptVar, VarSource}, - dynval::{DynVal as PrimitiveValue, VarName}, -}; use std::{collections::HashMap, time::Duration}; +use simplexpr::dynval::DynVal; +use yuck::{ + config::script_var_definition::{PollScriptVar, ScriptVarDefinition, VarSource}, + value::VarName, +}; + +use crate::config::system_stats::*; + macro_rules! builtin_vars { ($interval:expr, $($name:literal => $fun:expr),*$(,)?) => {{ maplit::hashmap! { $( - VarName::from($name) => ScriptVar::Poll(PollScriptVar { + VarName::from($name) => ScriptVarDefinition::Poll(PollScriptVar { name: VarName::from($name), command: VarSource::Function($fun), interval: $interval, @@ -17,19 +21,19 @@ macro_rules! builtin_vars { } }}} -pub fn get_inbuilt_vars() -> HashMap { +pub fn get_inbuilt_vars() -> HashMap { builtin_vars! {Duration::new(2, 0), // @desc EWW_TEMPS - Heat of the components in Celcius\nExample: `{{(CPU_TEMPS.core_1 + CPU_TEMPS.core_2) / 2}}` - "EWW_TEMPS" => || Ok(Primitivedynval::from(cores())), + "EWW_TEMPS" => || Ok(DynVal::from(cores())), // @desc EWW_RAM - The current RAM + Swap usage - "EWW_RAM" => || Ok(Primitivedynval::from(format!("{:.2}", ram()))), + "EWW_RAM" => || Ok(DynVal::from(format!("{:.2}", ram()))), // @desc EWW_DISK - Information on on all mounted partitions (Might report inaccurately on some filesystems, like btrfs)\nExample: `{{EWW_DISK["/"]}}` - "EWW_DISK" => || Ok(Primitivedynval::from(disk())), + "EWW_DISK" => || Ok(DynVal::from(disk())), // @desc EWW_BATTERY - Battery capacity in procent of the main battery - "EWW_BATTERY" => || Ok(Primitivedynval::from( + "EWW_BATTERY" => || Ok(DynVal::from( match get_battery_capacity() { Err(e) => { log::error!("Couldn't get the battery capacity: {:?}", e); @@ -40,9 +44,9 @@ pub fn get_inbuilt_vars() -> HashMap { )), // @desc EWW_CPU_USAGE - Average CPU usage (all cores) since the last update (No MacOS support) - "EWW_CPU_USAGE" => || Ok(Primitivedynval::from(get_avg_cpu_usage())), + "EWW_CPU_USAGE" => || Ok(DynVal::from(get_avg_cpu_usage())), // @desc EWW_NET - Bytes up/down on all interfaces - "EWW_NET" => || Ok(Primitivedynval::from(net())), + "EWW_NET" => || Ok(DynVal::from(net())), } } diff --git a/crates/eww/src/config/mod.rs b/crates/eww/src/config/mod.rs index 66fe9bd..19ed762 100644 --- a/crates/eww/src/config/mod.rs +++ b/crates/eww/src/config/mod.rs @@ -1,33 +1,8 @@ -use crate::{ - util, - dynval::{DynVal, VarName}, -}; - -use anyhow::*; - -use element::*; -use xml_ext::*; - -pub mod backend_window_options; -pub mod element; pub mod eww_config; pub mod inbuilt; pub mod script_var; pub mod system_stats; pub mod window_definition; -pub mod window_geometry; -pub mod xml_ext; pub use eww_config::*; pub use script_var::*; pub use window_definition::*; -pub use window_geometry::*; - -#[macro_export] -macro_rules! ensure_xml_tag_is { - ($element:ident, $name:literal) => { - ensure!( - $element.tag_name() == $name, - anyhow!("{} | Tag needed to be of type '{}', but was: {}", $element.text_pos(), $name, $element.as_tag_string()) - ) - }; -} diff --git a/crates/eww/src/config/script_var.rs b/crates/eww/src/config/script_var.rs index 0f965e6..39bddfe 100644 --- a/crates/eww/src/config/script_var.rs +++ b/crates/eww/src/config/script_var.rs @@ -1,80 +1,22 @@ use std::process::Command; use anyhow::*; +use simplexpr::dynval::DynVal; +use yuck::config::script_var_definition::{ScriptVarDefinition, VarSource}; -use crate::ensure_xml_tag_is; - -use super::*; - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum VarSource { - Shell(String), - Function(fn() -> Result), -} -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct PollScriptVar { - pub name: VarName, - pub command: VarSource, - pub interval: std::time::Duration, -} - -impl PollScriptVar { - pub fn run_once(&self) -> Result { - match &self.command { - VarSource::Shell(x) => run_command(x), - VarSource::Function(x) => x(), - } +pub fn initial_value(var: &ScriptVarDefinition) -> Result { + match var { + ScriptVarDefinition::Poll(x) => match &x.command { + VarSource::Function(f) => { + f().map_err(|err| anyhow!(err)).with_context(|| format!("Failed to compute initial value for {}", &var.name())) + } + VarSource::Shell(f) => run_command(f).with_context(|| format!("Failed to compute initial value for {}", &var.name())), + }, + ScriptVarDefinition::Tail(_) => Ok(DynVal::from_string(String::new())), } } - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct TailScriptVar { - pub name: VarName, - pub command: String, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum ScriptVar { - Poll(PollScriptVar), - Tail(TailScriptVar), -} - -impl ScriptVar { - pub fn name(&self) -> &VarName { - match self { - ScriptVar::Poll(x) => &x.name, - ScriptVar::Tail(x) => &x.name, - } - } - - pub fn initial_value(&self) -> Result { - match self { - ScriptVar::Poll(x) => match &x.command { - VarSource::Function(f) => f().with_context(|| format!("Failed to compute initial value for {}", &self.name())), - VarSource::Shell(f) => { - run_command(f).with_context(|| format!("Failed to compute initial value for {}", &self.name())) - } - }, - ScriptVar::Tail(_) => Ok(DynVal::from_string(String::new())), - } - } - - pub fn from_xml_element(xml: XmlElement) -> Result { - ensure_xml_tag_is!(xml, "script-var"); - - let name = VarName(xml.attr("name")?); - let command = xml.only_child()?.as_text()?.text(); - if let Ok(interval) = xml.attr("interval") { - let interval = util::parse_duration(&interval)?; - Ok(ScriptVar::Poll(PollScriptVar { name, command: crate::config::VarSource::Shell(command), interval })) - } else { - Ok(ScriptVar::Tail(TailScriptVar { name, command })) - } - } -} - /// Run a command and get the output -fn run_command(cmd: &str) -> Result { +pub fn run_command(cmd: &str) -> Result { log::debug!("Running command: {}", cmd); let output = String::from_utf8(Command::new("/bin/sh").arg("-c").arg(cmd).output()?.stdout)?; let output = output.trim_matches('\n'); diff --git a/crates/eww/src/config/window_definition.rs b/crates/eww/src/config/window_definition.rs index a8ac3b2..750e8fb 100644 --- a/crates/eww/src/config/window_definition.rs +++ b/crates/eww/src/config/window_definition.rs @@ -1,41 +1,14 @@ -use super::*; -use crate::{dynval::NumWithUnit, ensure_xml_tag_is, widgets::widget_node}; -use derive_more::*; -use serde::{Deserialize, Serialize}; -use smart_default::SmartDefault; -use std::{collections::HashMap, str::FromStr}; +use std::collections::HashMap; + +use anyhow::*; use yuck::config::{ - backend_window_options::StrutDefinition, + backend_window_options::BackendWindowOptions, + widget_definition::WidgetDefinition, window_definition::{WindowDefinition, WindowStacking}, window_geometry::WindowGeometry, }; -#[derive(Debug, Clone, PartialEq)] -pub enum EwwWindowType { - Dock, - Dialog, - Toolbar, - Normal, -} -impl FromStr for EwwWindowType { - type Err = anyhow::Error; - - fn from_str(s: &str) -> Result { - match s { - "dock" => Ok(Self::Dock), - "toolbar" => Ok(Self::Toolbar), - "dialog" => Ok(Self::Dialog), - "normal" => Ok(Self::Normal), - x => Err(anyhow!("Unknown windowtype provided '{}'. Possible values are: dock, toolbar, dialog, normal", x)), - } - } -} - -impl Default for EwwWindowType { - fn default() -> Self { - Self::Normal - } -} +use crate::widgets::widget_node; /// Full window-definition containing the fully expanded widget tree. /// **Use this** rather than `[RawEwwWindowDefinition]`. @@ -43,20 +16,12 @@ impl Default for EwwWindowType { pub struct EwwWindowDefinition { pub name: String, - pub geometry: WindowGeometry, + pub geometry: Option, pub stacking: WindowStacking, pub monitor_number: Option, pub widget: Box, - pub focusable: bool, - - #[cfg(feature = "x11")] - pub window_type: EwwWindowType, - - #[cfg(feature = "x11")] - pub struts: StrutDefinition, - - #[cfg(feature = "wayland")] - pub exclusive: bool, + pub resizable: bool, + pub backend_options: BackendWindowOptions, } impl EwwWindowDefinition { @@ -65,15 +30,10 @@ impl EwwWindowDefinition { name: window.name, geometry: window.geometry, stacking: window.stacking, - monitor_number: window.screen_number, + monitor_number: window.monitor_number, + resizable: window.resizable, widget: widget_node::generate_generic_widget_node(defs, &HashMap::new(), window.widget)?, - focusable: window.focusable, - #[cfg(feature = "x11")] - window_type: window.window_type, - #[cfg(feature = "x11")] - struts: window.struts, - #[cfg(feature = "wayland")] - exclusive: window.exclusive, + backend_options: window.backend_options, }) } } diff --git a/crates/eww/src/config/window_geometry.rs b/crates/eww/src/config/window_geometry.rs deleted file mode 100644 index 6e11bfe..0000000 --- a/crates/eww/src/config/window_geometry.rs +++ /dev/null @@ -1,144 +0,0 @@ -use crate::dynval::Coords; -use anyhow::*; -use serde::{Deserialize, Serialize}; -use smart_default::SmartDefault; - -use std::fmt; - -use super::xml_ext::XmlElement; - -#[derive(Debug, derive_more::Display, Clone, Copy, Eq, PartialEq, SmartDefault, Serialize, Deserialize)] -pub enum AnchorAlignment { - #[display(fmt = "start")] - #[default] - START, - #[display(fmt = "center")] - CENTER, - #[display(fmt = "end")] - END, -} - -impl AnchorAlignment { - pub fn from_x_alignment(s: &str) -> Result { - match s { - "l" | "left" => Ok(AnchorAlignment::START), - "c" | "center" => Ok(AnchorAlignment::CENTER), - "r" | "right" => Ok(AnchorAlignment::END), - _ => bail!(r#"couldn't parse '{}' as x-alignment. Must be one of "left", "center", "right""#, s), - } - } - - pub fn from_y_alignment(s: &str) -> Result { - match s { - "t" | "top" => Ok(AnchorAlignment::START), - "c" | "center" => Ok(AnchorAlignment::CENTER), - "b" | "bottom" => Ok(AnchorAlignment::END), - _ => bail!(r#"couldn't parse '{}' as y-alignment. Must be one of "top", "center", "bottom""#, s), - } - } - - pub fn alignment_to_coordinate(&self, size_inner: i32, size_container: i32) -> i32 { - match self { - AnchorAlignment::START => 0, - AnchorAlignment::CENTER => (size_container / 2) - (size_inner / 2), - AnchorAlignment::END => size_container - size_inner, - } - } -} - -#[derive(Debug, Clone, Copy, Eq, PartialEq, Default, Serialize, Deserialize)] -pub struct AnchorPoint { - pub x: AnchorAlignment, - pub y: AnchorAlignment, -} - -impl std::fmt::Display for AnchorPoint { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use AnchorAlignment::*; - match (self.x, self.y) { - (CENTER, CENTER) => write!(f, "center"), - (x, y) => write!( - f, - "{} {}", - match x { - START => "left", - CENTER => "center", - END => "right", - }, - match y { - START => "top", - CENTER => "center", - END => "bottom", - } - ), - } - } -} - -impl std::str::FromStr for AnchorPoint { - type Err = anyhow::Error; - - fn from_str(s: &str) -> Result { - if s == "center" { - Ok(AnchorPoint { x: AnchorAlignment::CENTER, y: AnchorAlignment::CENTER }) - } else { - let (first, second) = s - .split_once(' ') - .context("Failed to parse anchor: Must either be \"center\" or be formatted like \"top left\"")?; - let x_y_result: Result<_> = try { - AnchorPoint { x: AnchorAlignment::from_x_alignment(first)?, y: AnchorAlignment::from_y_alignment(second)? } - }; - x_y_result.or_else(|_| { - Ok(AnchorPoint { x: AnchorAlignment::from_x_alignment(second)?, y: AnchorAlignment::from_y_alignment(first)? }) - }) - } - } -} - -#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)] -pub struct EwwWindowGeometry { - pub anchor_point: AnchorPoint, - pub offset: Coords, - pub size: Coords, -} - -impl EwwWindowGeometry { - pub fn from_xml_element(xml: XmlElement) -> Result { - Ok(EwwWindowGeometry { - anchor_point: xml.parse_optional_attr("anchor")?.unwrap_or_default(), - size: Coords { - x: xml.parse_optional_attr("width")?.unwrap_or_default(), - y: xml.parse_optional_attr("height")?.unwrap_or_default(), - }, - offset: Coords { - x: xml.parse_optional_attr("x")?.unwrap_or_default(), - y: xml.parse_optional_attr("y")?.unwrap_or_default(), - }, - }) - } - - pub fn override_if_given(&self, anchor_point: Option, offset: Option, size: Option) -> Self { - EwwWindowGeometry { - anchor_point: anchor_point.unwrap_or(self.anchor_point), - offset: offset.unwrap_or(self.offset), - size: size.unwrap_or(self.size), - } - } -} - -impl std::fmt::Display for EwwWindowGeometry { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}-{} ({})", self.offset, self.size, self.anchor_point) - } -} - -impl EwwWindowGeometry { - /// Calculate the window rectangle given the configured window geometry - pub fn get_window_rectangle(&self, screen_rect: gdk::Rectangle) -> gdk::Rectangle { - let (offset_x, offset_y) = self.offset.relative_to(screen_rect.width, screen_rect.height); - let (width, height) = self.size.relative_to(screen_rect.width, screen_rect.height); - let x = screen_rect.x + offset_x + self.anchor_point.x.alignment_to_coordinate(width, screen_rect.width); - let y = screen_rect.y + offset_y + self.anchor_point.y.alignment_to_coordinate(height, screen_rect.height); - gdk::Rectangle { x, y, width, height } - } -} diff --git a/crates/eww/src/config/xml_ext.rs b/crates/eww/src/config/xml_ext.rs deleted file mode 100644 index b8cdf9c..0000000 --- a/crates/eww/src/config/xml_ext.rs +++ /dev/null @@ -1,307 +0,0 @@ -use crate::util::StringExt; -use anyhow::*; -use itertools::Itertools; -use std::fmt; - -#[macro_export] -macro_rules! with_text_pos_context { - ($node:expr => $($code:tt)*) => {{ - let result: Result<_> = try { $($code)* }; - result.with_context(|| anyhow!("at: {}", $node.text_pos())) - }}; -} - -/// resolve symbols such as " to replace them with the actual " symbol -pub fn resolve_escaped_symbols(s: &str) -> String { - s.replace(""", "\"").replace("<", "<").replace(">", ">") -} - -#[derive(Debug, Clone)] -pub enum XmlNode<'a, 'b> { - Element(XmlElement<'a, 'b>), - Text(XmlText<'a, 'b>), - Ignored(roxmltree::Node<'a, 'b>), -} - -impl<'a, 'b> fmt::Display for XmlNode<'a, 'b> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - XmlNode::Text(text) => write!(f, "{}", text), - XmlNode::Element(elem) => write!(f, "{}", elem), - XmlNode::Ignored(node) => write!(f, "{:?}", node), - } - } -} - -#[derive(PartialEq, Eq, Clone, Copy, derive_more::Display)] -#[display(fmt = "{}:{}", row, col)] -pub struct TextPos { - pub row: u32, - pub col: u32, -} - -impl std::fmt::Debug for TextPos { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self) - } -} - -impl From for TextPos { - fn from(x: roxmltree::TextPos) -> Self { - TextPos { row: x.row, col: x.col } - } -} - -/// Get the part of a string that is selected by the start and end TextPos. -/// Will panic if the range is out of bounds in any way. -fn get_text_from_text_range(s: &str, (start_pos, end_pos): (TextPos, TextPos)) -> String { - let mut code_text = - s.lines().dropping(start_pos.row as usize - 1).take(end_pos.row as usize - (start_pos.row as usize - 1)).collect_vec(); - if let Some(first_line) = code_text.first_mut() { - *first_line = first_line.split_at(start_pos.col as usize - 1).1; - } - if let Some(last_line) = code_text.last_mut() { - *last_line = last_line.split_at(end_pos.col as usize - 1).0; - } - resolve_escaped_symbols(&code_text.join("\n")) -} - -impl<'a, 'b> XmlNode<'a, 'b> { - pub fn get_sourcecode(&self) -> String { - let input_text = self.node().document().input_text(); - let range = self.node().range(); - let start_pos = self.node().document().text_pos_at(range.start).into(); - let end_pos = self.node().document().text_pos_at(range.end).into(); - get_text_from_text_range(input_text, (start_pos, end_pos)) - } - - pub fn as_text_or_sourcecode(&self) -> String { - self.as_text().map(|c| resolve_escaped_symbols(&c.text())).unwrap_or_else(|_| self.get_sourcecode()) - } - - pub fn as_text(&self) -> Result<&XmlText<'a, 'b>> { - match self { - XmlNode::Text(text) => Ok(text), - _ => Err(anyhow!("'{}' is not a text node", self)), - } - } - - pub fn as_element(&self) -> Result<&XmlElement<'a, 'b>> { - match self { - XmlNode::Element(element) => Ok(element), - _ => Err(anyhow!("'{}' is not an element node", self)), - } - } - - pub fn text_range(&self) -> std::ops::Range { - self.node().range() - } - - pub fn text_pos(&self) -> TextPos { - let document = self.node().document(); - let range = self.node().range(); - document.text_pos_at(range.start).into() - } - - fn node(&self) -> roxmltree::Node<'a, 'b> { - match self { - XmlNode::Text(x) => x.0, - XmlNode::Element(x) => x.0, - XmlNode::Ignored(x) => *x, - } - } -} - -#[derive(Debug, Clone)] -pub struct XmlText<'a, 'b>(roxmltree::Node<'a, 'b>); - -impl<'a, 'b> fmt::Display for XmlText<'a, 'b> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Text(\"{}\")", self.text()) - } -} - -impl<'a, 'b> XmlText<'a, 'b> { - pub fn text(&self) -> String { - self.0.text().map(resolve_escaped_symbols).unwrap_or_default().trim_lines().trim_matches('\n').to_owned() - } - - pub fn text_pos(&self) -> TextPos { - let document = self.0.document(); - let range = self.0.range(); - document.text_pos_at(range.start).into() - } -} - -#[derive(Debug, Clone)] -pub struct XmlElement<'a, 'b>(roxmltree::Node<'a, 'b>); - -impl<'a, 'b> fmt::Display for XmlElement<'a, 'b> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let children = self - .children() - .map(|child| format!("{}", child)) - .map(|x| x.lines().map(|line| format!(" {}", line)).join("\n")) - .join("\n"); - - if children.is_empty() { - write!(f, "{}", self.as_tag_string(), self.tag_name()) - } else { - write!(f, "{}\n{}\n", self.as_tag_string(), children, self.tag_name()) - } - } -} - -impl<'a, 'b> XmlElement<'a, 'b> { - pub fn as_tag_string(&self) -> String { - let attrs = self.attributes().iter().map(|attr| format!("{}=\"{}\"", attr.name(), attr.value())).join(" "); - - format!("<{} {}>", self.tag_name(), attrs) - } - - pub fn tag_name(&self) -> &str { - self.0.tag_name().name() - } - - pub fn child(&self, tagname: &str) -> Result { - with_text_pos_context! { self => - self.child_elements() - .find(|child| child.tag_name() == tagname) - .with_context(|| anyhow!("child element '{}' missing from {}", tagname, self.as_tag_string()))? - } - } - - pub fn children(&self) -> impl Iterator { - self.0 - .children() - .filter(|child| child.is_element() || (child.is_text() && !child.text().unwrap_or_default().is_blank())) - .map(XmlNode::from) - } - - pub fn child_elements(&self) -> impl Iterator { - self.0.children().filter(|child| child.is_element()).map(XmlElement) - } - - pub fn attributes(&self) -> &[roxmltree::Attribute] { - self.0.attributes() - } - - pub fn attr(&self, key: &str) -> Result { - with_text_pos_context! { self => - self.0 - .attribute(key) - .map(resolve_escaped_symbols) - .with_context(|| anyhow!("'{}' missing attribute '{}'", self.as_tag_string(), key))? - } - } - - pub fn optional_attr Result>(&self, key: &str, parse: F) -> Result> { - match self.0.attribute(key) { - Some(value) => parse(value) - .with_context(|| format!("Parsing the value of {}=\"{}\" in <{}>", key, value, self.tag_name())) - .map(Some), - None => Ok(None), - } - } - - pub fn parse_optional_attr, O: std::str::FromStr>(&self, key: &str) -> Result> { - match self.0.attribute(key) { - Some(value) => value - .parse::() - .map_err(|e| anyhow!(e)) - .with_context(|| format!("Parsing the value of {}=\"{}\" in <{}>", key, value, self.tag_name())) - .map(Some), - None => Ok(None), - } - } - - pub fn only_child(&self) -> Result { - with_text_pos_context! { self => - let mut children_iter = self.children(); - let only_child = children_iter - .next() - .with_context(|| anyhow!("'{}' had no children", self.as_tag_string()))?; - if children_iter.next().is_some() { - bail!("'{}' had more than one child", &self); - } - only_child - } - } - - pub fn only_child_element(&self) -> Result { - with_text_pos_context! { self => - self.only_child()?.as_element()?.clone() - } - } - - pub fn text_pos(&self) -> TextPos { - let document = self.0.document(); - let range = self.0.range(); - document.text_pos_at(range.start).into() - } -} - -impl<'a, 'b> From> for XmlNode<'a, 'b> { - fn from(elem: XmlElement<'a, 'b>) -> Self { - XmlNode::Element(elem) - } -} - -impl<'a, 'b> From> for XmlNode<'a, 'b> { - fn from(elem: XmlText<'a, 'b>) -> Self { - XmlNode::Text(elem) - } -} - -impl<'a, 'b> From> for XmlNode<'a, 'b> { - fn from(node: roxmltree::Node<'a, 'b>) -> Self { - if node.is_text() { - XmlNode::Text(XmlText(node)) - } else if node.is_element() | node.is_root() { - XmlNode::Element(XmlElement(node)) - } else { - XmlNode::Ignored(node) - } - } -} - -#[cfg(test)] -mod test { - use super::*; - #[test] - pub fn test_parse_sourcecode_singleline() { - let input = "whatever"; - let document = roxmltree::Document::parse(&input).unwrap(); - let root_node = XmlNode::from(document.root_element()); - assert_eq!(root_node.as_element().unwrap().only_child().unwrap().as_text_or_sourcecode(), "whatever".to_string()); - } - - #[test] - pub fn test_parse_sourcecode_multiline() { - let input = r#" -this is -multiline - "#; - let document = roxmltree::Document::parse(&input).unwrap(); - let root_node = XmlNode::from(document.root_element()); - assert_eq!( - root_node.as_element().unwrap().only_child().unwrap().as_text_or_sourcecode(), - "this is\nmultiline".to_string() - ); - } - - #[test] - pub fn test_parse_sourcecode_code() { - let input = r#" -if [ "this" == '$that' ]; then - echo `hi` -fi - "#; - let document = roxmltree::Document::parse(&input).unwrap(); - let root_node = XmlNode::from(document.root_element()); - assert_eq!( - root_node.as_element().unwrap().only_child().unwrap().as_text_or_sourcecode(), - "if [ \"this\" == '$that' ]; then\necho `hi`\nfi".to_string() - ); - } -} diff --git a/crates/eww/src/display_backend.rs b/crates/eww/src/display_backend.rs index 8734842..f2c176d 100644 --- a/crates/eww/src/display_backend.rs +++ b/crates/eww/src/display_backend.rs @@ -91,7 +91,6 @@ mod platform { #[cfg(feature = "x11")] mod platform { - use crate::config::{EwwWindowDefinition, EwwWindowType, Side, WindowStacking}; use anyhow::*; use gdkx11; use gtk::{self, prelude::*}; @@ -103,6 +102,12 @@ mod platform { protocol::xproto::*, rust_connection::{DefaultStream, RustConnection}, }; + use yuck::config::{ + backend_window_options::{Side, WindowType}, + window_definition::WindowStacking, + }; + + use crate::config::EwwWindowDefinition; pub fn initialize_window(window_def: &EwwWindowDefinition, _monitor: gdk::Rectangle) -> Option { let window_type = if window_def.backend_options.wm_ignore { gtk::WindowType::Popup } else { gtk::WindowType::Toplevel }; @@ -208,11 +213,11 @@ mod platform { self.atoms._NET_WM_WINDOW_TYPE, self.atoms.ATOM, &[match window_def.backend_options.window_type { - EwwWindowType::Dock => self.atoms._NET_WM_WINDOW_TYPE_DOCK, - EwwWindowType::Normal => self.atoms._NET_WM_WINDOW_TYPE_NORMAL, - EwwWindowType::Dialog => self.atoms._NET_WM_WINDOW_TYPE_DIALOG, - EwwWindowType::Toolbar => self.atoms._NET_WM_WINDOW_TYPE_TOOLBAR, - EwwWindowType::Utility => self.atoms._NET_WM_WINDOW_TYPE_UTILITY, + WindowType::Dock => self.atoms._NET_WM_WINDOW_TYPE_DOCK, + WindowType::Normal => self.atoms._NET_WM_WINDOW_TYPE_NORMAL, + WindowType::Dialog => self.atoms._NET_WM_WINDOW_TYPE_DIALOG, + WindowType::Toolbar => self.atoms._NET_WM_WINDOW_TYPE_TOOLBAR, + WindowType::Utility => self.atoms._NET_WM_WINDOW_TYPE_UTILITY, }], )? .check()?; diff --git a/crates/eww/src/eww_state.rs b/crates/eww/src/eww_state.rs index 4160c1e..20bb448 100644 --- a/crates/eww/src/eww_state.rs +++ b/crates/eww/src/eww_state.rs @@ -1,22 +1,20 @@ -use crate::{ - config::window_definition::WindowName, - dynval::{AttrName, AttrValElement, VarName}, -}; use anyhow::*; use std::{collections::HashMap, sync::Arc}; +use yuck::value::{AttrName, VarName}; -use crate::dynval::{AttrVal, DynVal}; +use simplexpr::{dynval::DynVal, SimplExpr}; /// Handler that gets executed to apply the necessary parts of the eww state to /// a gtk widget. These are created and initialized in EwwState::resolve. pub struct StateChangeHandler { func: Box) -> Result<()> + 'static>, - unresolved_values: HashMap, + unresolved_values: HashMap, } impl StateChangeHandler { - fn used_variables(&self) -> impl Iterator { - self.unresolved_values.iter().flat_map(|(_, value)| value.var_refs()) + fn used_variables(&self) -> impl Iterator + '_ { + // TODO fix this clone + self.unresolved_values.iter().flat_map(|(_, value)| value.var_refs()).map(|x| VarName(x.to_string())) } /// Run the StateChangeHandler. @@ -60,7 +58,7 @@ impl EwwWindowState { /// window-specific state-change handlers. #[derive(Default)] pub struct EwwState { - windows: HashMap, + windows: HashMap, variables_state: HashMap, } @@ -80,7 +78,7 @@ impl EwwState { } /// remove all state stored specific to one window - pub fn clear_window_state(&mut self, window_name: &WindowName) { + pub fn clear_window_state(&mut self, window_name: &str) { self.windows.remove(window_name); } @@ -108,22 +106,17 @@ impl EwwState { } /// resolves a value if possible, using the current eww_state. - pub fn resolve_once<'a>(&'a self, value: &'a AttrVal) -> Result { - value - .iter() - .map(|element| match element { - AttrValElement::Primitive(primitive) => Ok(primitive.clone()), - AttrValElement::Expr(expr) => expr.clone().eval(&self.variables_state), - }) - .collect() + pub fn resolve_once<'a>(&'a self, value: &'a SimplExpr) -> Result { + // TODO fix this clone + Ok(value.clone().eval(&self.variables_state.into_iter().map(|(k, v)| (k.0, v)).collect())?) } /// Resolve takes a function that applies a set of fully resolved attribute /// values to it's gtk widget. pub fn resolve) -> Result<()> + 'static + Clone>( &mut self, - window_name: &WindowName, - required_attributes: HashMap, + window_name: &str, + required_attributes: HashMap, set_value: F, ) { let handler = StateChangeHandler { func: Box::new(set_value), unresolved_values: required_attributes }; @@ -132,7 +125,7 @@ impl EwwState { // only store the handler if at least one variable is being used if handler.used_variables().next().is_some() { - self.windows.entry(window_name.clone()).or_insert_with(EwwWindowState::default).put_handler(handler); + self.windows.entry(window_name.to_string()).or_insert_with(EwwWindowState::default).put_handler(handler); } } @@ -140,7 +133,7 @@ impl EwwState { self.windows.values().flat_map(|w| w.state_change_handlers.keys()) } - pub fn vars_referenced_in(&self, window_name: &WindowName) -> std::collections::HashSet<&VarName> { + pub fn vars_referenced_in(&self, window_name: &str) -> std::collections::HashSet<&VarName> { self.windows.get(window_name).map(|window| window.state_change_handlers.keys().collect()).unwrap_or_default() } } diff --git a/crates/eww/src/opts.rs b/crates/eww/src/opts.rs index d6ef99c..95872d5 100644 --- a/crates/eww/src/opts.rs +++ b/crates/eww/src/opts.rs @@ -1,13 +1,14 @@ use anyhow::*; use serde::{Deserialize, Serialize}; +use simplexpr::dynval::DynVal; use structopt::StructOpt; - -use crate::{ - app, - config::{AnchorPoint, WindowName}, - dynval::{Coords, DynVal, VarName}, +use yuck::{ + config::window_geometry::AnchorPoint, + value::{Coords, VarName}, }; +use crate::app; + /// Struct that gets generated from `RawOpt`. #[derive(Debug, Serialize, Deserialize, PartialEq)] pub struct Opt { @@ -68,7 +69,7 @@ pub enum ActionWithServer { #[structopt(name = "open", alias = "o")] OpenWindow { /// Name of the window you want to open. - window_name: WindowName, + window_name: String, /// Monitor-index the window should open on #[structopt(short, long)] @@ -90,11 +91,11 @@ pub enum ActionWithServer { /// Open multiple windows at once. /// NOTE: This will in the future be part of eww open, and will then be removed. #[structopt(name = "open-many")] - OpenMany { windows: Vec }, + OpenMany { windows: Vec }, /// Close the window with the given name #[structopt(name = "close", alias = "c")] - CloseWindow { window_name: WindowName }, + CloseWindow { window_name: String }, /// Reload the configuration #[structopt(name = "reload", alias = "r")] diff --git a/crates/eww/src/script_var_handler.rs b/crates/eww/src/script_var_handler.rs index 372a77b..ced0b17 100644 --- a/crates/eww/src/script_var_handler.rs +++ b/crates/eww/src/script_var_handler.rs @@ -1,17 +1,16 @@ use std::collections::HashMap; -use crate::{ - app, config, - dynval::{DynVal, VarName}, -}; +use crate::app; use anyhow::*; use app::DaemonCommand; +use simplexpr::dynval::DynVal; use tokio::{ io::{AsyncBufReadExt, BufReader}, sync::mpsc::UnboundedSender, }; use tokio_util::sync::CancellationToken; +use yuck::{config::script_var_definition::{PollScriptVar, ScriptVarDefinition, TailScriptVar}, value::VarName}; /// Initialize the script var handler, and return a handle to that handler, which can be used to control /// the script var execution. @@ -53,7 +52,7 @@ pub struct ScriptVarHandlerHandle { impl ScriptVarHandlerHandle { /// Add a new script-var that should be executed. - pub fn add(&self, script_var: config::ScriptVar) { + pub fn add(&self, script_var: ScriptVarDefinition) { crate::print_result_err!( "while forwarding instruction to script-var handler", self.msg_send.send(ScriptVarHandlerMsg::AddVar(script_var)) @@ -80,22 +79,22 @@ impl ScriptVarHandlerHandle { /// Message enum used by the ScriptVarHandlerHandle to communicate to the ScriptVarHandler #[derive(Debug, Eq, PartialEq)] enum ScriptVarHandlerMsg { - AddVar(config::ScriptVar), + AddVar(ScriptVarDefinition), Stop(VarName), StopAll, } -/// Handler that manages running and updating [ScriptVar]s +/// Handler that manages running and updating [ScriptVarDefinition]s struct ScriptVarHandler { tail_handler: TailVarHandler, poll_handler: PollVarHandler, } impl ScriptVarHandler { - async fn add(&mut self, script_var: config::ScriptVar) { + async fn add(&mut self, script_var: ScriptVarDefinition) { match script_var { - config::ScriptVar::Poll(var) => self.poll_handler.start(var).await, - config::ScriptVar::Tail(var) => self.tail_handler.start(var).await, + ScriptVarDefinition::Poll(var) => self.poll_handler.start(var).await, + ScriptVarDefinition::Tail(var) => self.tail_handler.start(var).await, }; } @@ -126,14 +125,14 @@ impl PollVarHandler { Ok(handler) } - async fn start(&mut self, var: config::PollScriptVar) { + async fn start(&mut self, var: PollScriptVar) { log::debug!("starting poll var {}", &var.name); let cancellation_token = CancellationToken::new(); self.poll_handles.insert(var.name.clone(), cancellation_token.clone()); let evt_send = self.evt_send.clone(); tokio::spawn(async move { let result: Result<_> = try { - evt_send.send(app::DaemonCommand::UpdateVars(vec![(var.name.clone(), var.run_once()?)]))?; + evt_send.send(app::DaemonCommand::UpdateVars(vec![(var.name.clone(), run_poll_once(&var)?)]))?; }; crate::print_result_err!("while running script-var command", &result); @@ -141,7 +140,7 @@ impl PollVarHandler { _ = cancellation_token.cancelled() => break, _ = tokio::time::sleep(var.interval) => { let result: Result<_> = try { - evt_send.send(app::DaemonCommand::UpdateVars(vec![(var.name.clone(), var.run_once()?)]))?; + evt_send.send(app::DaemonCommand::UpdateVars(vec![(var.name.clone(), run_poll_once(&var)?)]))?; }; crate::print_result_err!("while running script-var command", &result); } @@ -161,6 +160,13 @@ impl PollVarHandler { } } +fn run_poll_once(var: &PollScriptVar) -> Result { + match &var.command { + yuck::config::script_var_definition::VarSource::Shell(x) => crate::config::script_var::run_command(x), + yuck::config::script_var_definition::VarSource::Function(x) => x().map_err(|e| anyhow!(e)), + } +} + impl Drop for PollVarHandler { fn drop(&mut self) { self.stop_all(); @@ -178,7 +184,7 @@ impl TailVarHandler { Ok(handler) } - async fn start(&mut self, var: config::TailScriptVar) { + async fn start(&mut self, var: TailScriptVar) { log::debug!("starting poll var {}", &var.name); let cancellation_token = CancellationToken::new(); self.tail_process_handles.insert(var.name.clone(), cancellation_token.clone()); diff --git a/crates/eww/src/widgets/mod.rs b/crates/eww/src/widgets/mod.rs index d772fdc..f36d48b 100644 --- a/crates/eww/src/widgets/mod.rs +++ b/crates/eww/src/widgets/mod.rs @@ -1,12 +1,9 @@ -use crate::{ - config::{element::WidgetDefinition, window_definition::WindowName}, - eww_state::*, -}; +use crate::eww_state::*; use anyhow::*; use gtk::prelude::*; use itertools::Itertools; use std::collections::HashMap; -use yuck::value::AttrName; +use yuck::{config::widget_definition::WidgetDefinition, value::AttrName}; use std::process::Command; use widget_definitions::*; @@ -45,7 +42,7 @@ struct BuilderArgs<'a, 'b, 'c, 'd, 'e> { eww_state: &'a mut EwwState, widget: &'b widget_node::Generic, unhandled_attrs: Vec<&'c AttrName>, - window_name: &'d WindowName, + window_name: &'d str, widget_definitions: &'e HashMap, } @@ -60,7 +57,7 @@ struct BuilderArgs<'a, 'b, 'c, 'd, 'e> { /// widget name. fn build_builtin_gtk_widget( eww_state: &mut EwwState, - window_name: &WindowName, + window_name: &str, widget_definitions: &HashMap, widget: &widget_node::Generic, ) -> Result> { @@ -72,7 +69,7 @@ fn build_builtin_gtk_widget( return result.with_context(|| { format!( "{}Error building widget {}", - bargs.widget.text_pos.map(|x| format!("{} |", x)).unwrap_or_default(), + bargs.widget.span.map(|x| format!("{} |", x)).unwrap_or_default(), bargs.widget.name, ) }) @@ -87,7 +84,7 @@ fn build_builtin_gtk_widget( let child_widget = child.render(bargs.eww_state, window_name, widget_definitions).with_context(|| { format!( "{}error while building child '{:#?}' of '{}'", - widget.text_pos.map(|x| format!("{} |", x)).unwrap_or_default(), + widget.span.map(|x| format!("{} |", x)).unwrap_or_default(), &child, >k_widget.get_widget_name() ) @@ -108,7 +105,7 @@ fn build_builtin_gtk_widget( if !bargs.unhandled_attrs.is_empty() { log::error!( "{}: Unknown attribute used in {}: {}", - widget.text_pos.map(|x| format!("{} | ", x)).unwrap_or_default(), + widget.span.map(|x| format!("{} | ", x)).unwrap_or_default(), widget.name, bargs.unhandled_attrs.iter().map(|x| x.to_string()).join(", ") ) diff --git a/crates/eww/src/widgets/widget_definitions.rs b/crates/eww/src/widgets/widget_definitions.rs index 2f69689..2494042 100644 --- a/crates/eww/src/widgets/widget_definitions.rs +++ b/crates/eww/src/widgets/widget_definitions.rs @@ -45,7 +45,9 @@ pub(super) fn widget_to_gtk_widget(bargs: &mut BuilderArgs) -> Result &str; - fn get_text_pos(&self) -> Option; + fn get_span(&self) -> Option; /// Generate a [gtk::Widget] from a [element::WidgetUse]. /// @@ -24,7 +23,7 @@ pub trait WidgetNode: std::fmt::Debug + dyn_clone::DynClone + Send + Sync { fn render( &self, eww_state: &mut EwwState, - window_name: &WindowName, + window_name: &str, widget_definitions: &HashMap, ) -> Result; } @@ -34,7 +33,7 @@ dyn_clone::clone_trait_object!(WidgetNode); #[derive(Debug, Clone)] pub struct UserDefined { name: String, - text_pos: Option, + span: Option, content: Box, } @@ -43,14 +42,14 @@ impl WidgetNode for UserDefined { &self.name } - fn get_text_pos(&self) -> Option { - self.text_pos + fn get_span(&self) -> Option { + self.span } fn render( &self, eww_state: &mut EwwState, - window_name: &WindowName, + window_name: &str, widget_definitions: &HashMap, ) -> Result { self.content.render(eww_state, window_name, widget_definitions) @@ -60,19 +59,20 @@ impl WidgetNode for UserDefined { #[derive(Debug, Clone)] pub struct Generic { pub name: String, - pub text_pos: Option, + pub span: Option, pub children: Vec>, - pub attrs: HashMap, + pub attrs: HashMap, } impl Generic { - pub fn get_attr(&self, key: &str) -> Result<&AttrVal> { + pub fn get_attr(&self, key: &str) -> Result<&SimplExpr> { self.attrs.get(key).context(format!("attribute '{}' missing from use of '{}'", key, &self.name)) } /// returns all the variables that are referenced in this widget - pub fn referenced_vars(&self) -> impl Iterator { - self.attrs.iter().flat_map(|(_, value)| value.var_refs()) + pub fn referenced_vars(&self) -> impl Iterator + '_ { + // TODO fix this clone + self.attrs.iter().flat_map(|(_, value)| value.var_refs()).map(|x| VarName(x.to_string())) } } @@ -81,14 +81,14 @@ impl WidgetNode for Generic { &self.name } - fn get_text_pos(&self) -> Option { - self.text_pos + fn get_span(&self) -> Option { + self.span } fn render( &self, eww_state: &mut EwwState, - window_name: &WindowName, + window_name: &str, widget_definitions: &HashMap, ) -> Result { crate::widgets::build_builtin_gtk_widget(eww_state, window_name, widget_definitions, self)? @@ -98,25 +98,42 @@ impl WidgetNode for Generic { pub fn generate_generic_widget_node( defs: &HashMap, - local_env: &HashMap, + local_env: &HashMap, w: WidgetUse, ) -> Result> { if let Some(def) = defs.get(&w.name) { ensure!(w.children.is_empty(), "User-defined widgets cannot be given children."); let new_local_env = w + .attrs .attrs .into_iter() - .map(|(name, value)| (VarName(name.0), value.resolve_one_level(local_env))) - .collect::>(); + .map(|(name, value)| { + Ok(( + VarName(name.0), + SimplExpr::Literal(value.value.span().into(), value.value.as_simplexpr()?.resolve_one_level(local_env)?), + )) + }) + .collect::>>()?; - let content = generate_generic_widget_node(defs, &new_local_env, def.structure.clone())?; - Ok(Box::new(UserDefined { name: w.name, text_pos: w.text_pos, content })) + let content = generate_generic_widget_node(defs, &new_local_env, def.widget.clone())?; + Ok(Box::new(UserDefined { name: w.name, span: Some(w.span), content })) } else { Ok(Box::new(Generic { name: w.name, - text_pos: w.text_pos, - attrs: w.attrs.into_iter().map(|(name, value)| (name, value.resolve_one_level(local_env))).collect(), + span: Some(w.span), + attrs: w + .attrs + .attrs + .into_iter() + .map(|(name, value)| { + Ok(( + VarName(name.0), + SimplExpr::Literal(value.value.span().into(), value.value.as_simplexpr()?.resolve_one_level(local_env)?), + )) + }) + .collect()?, + children: w .children .into_iter() @@ -126,42 +143,41 @@ pub fn generate_generic_widget_node( } } -#[cfg(test)] -mod test { - use super::*; - use crate::config::xml_ext::*; - use maplit::hashmap; - #[test] - fn test_generic_generate() { - let w_def1 = { - let input = r#"{{nested1}}{{raw1}}"#; - let document = roxmltree::Document::parse(input).unwrap(); - let xml = XmlNode::from(document.root_element().clone()); - WidgetDefinition::from_xml_element(&xml.as_element().unwrap()).unwrap() - }; - let w_def2 = { - let input = r#""#; - let document = roxmltree::Document::parse(input).unwrap(); - let xml = XmlNode::from(document.root_element().clone()); - WidgetDefinition::from_xml_element(&xml.as_element().unwrap()).unwrap() - }; - let w_use = { - let input = r#""#; - let document = roxmltree::Document::parse(input).unwrap(); - let xml = XmlNode::from(document.root_element().clone()); - WidgetUse::from_xml_node(xml).unwrap() - }; +//#[cfg(test)] +// mod test { +// use super::*; +// use crate::config::xml_ext::*; +// use maplit::hashmap; +//#[test] +// fn test_generic_generate() { +// let w_def1 = { +// let input = r#"{{nested1}}{{raw1}}"#; +// let document = roxmltree::Document::parse(input).unwrap(); +// let xml = XmlNode::from(document.root_element().clone()); +// WidgetDefinition::from_xml_element(&xml.as_element().unwrap()).unwrap() +//}; +// let w_def2 = { +// let input = r#""#; +// let document = roxmltree::Document::parse(input).unwrap(); +// let xml = XmlNode::from(document.root_element().clone()); +// WidgetDefinition::from_xml_element(&xml.as_element().unwrap()).unwrap() +//}; +// let w_use = { +// let input = r#""#; +// let document = roxmltree::Document::parse(input).unwrap(); +// let xml = XmlNode::from(document.root_element().clone()); +// WidgetUse::from_xml_node(xml).unwrap() +//}; - let generic = generate_generic_widget_node( - &hashmap! { "foo".to_string() => w_def1, "bar".to_string() => w_def2 }, - &HashMap::new(), - w_use, - ) - .unwrap(); +// let generic = generate_generic_widget_node( +//&hashmap! { "foo".to_string() => w_def1, "bar".to_string() => w_def2 }, +//&HashMap::new(), +// w_use, +//) +//.unwrap(); - // TODO actually implement this test ._. +//// TODO actually implement this test ._. - dbg!(&generic); - // panic!("REEEEEEEEEE") - } -} +// dbg!(&generic); +//// panic!("REEEEEEEEEE") +//} diff --git a/crates/simplexpr/src/ast.rs b/crates/simplexpr/src/ast.rs index db1aaf9..22bc15d 100644 --- a/crates/simplexpr/src/ast.rs +++ b/crates/simplexpr/src/ast.rs @@ -74,11 +74,16 @@ impl SimplExpr { Self::Literal(span, DynVal(s, Some(span))) } - /// Construct a synthetic simplexpr from a literal value, without adding any relevant span information (uses [DUMMY_SPAN]) - pub fn synth_literal(s: String) -> Self { + /// Construct a synthetic simplexpr from a literal string, without adding any relevant span information (uses [DUMMY_SPAN]) + pub fn synth_string(s: String) -> Self { Self::Literal(DUMMY_SPAN, DynVal(s, Some(DUMMY_SPAN))) } + /// Construct a synthetic simplexpr from a literal dynval, without adding any relevant span information (uses [DUMMY_SPAN]) + pub fn synth_literal>(s: T) -> Self { + Self::Literal(DUMMY_SPAN, s.into()) + } + pub fn span(&self) -> Span { match self { SimplExpr::Literal(span, _) => *span, diff --git a/crates/yuck/src/config/attributes.rs b/crates/yuck/src/config/attributes.rs index 34afc6b..e749c71 100644 --- a/crates/yuck/src/config/attributes.rs +++ b/crates/yuck/src/config/attributes.rs @@ -58,6 +58,7 @@ impl AttrEntry { } } +// TODO maybe make this generic over the contained content #[derive(Debug, Clone, Eq, PartialEq, serde::Serialize)] pub struct Attributes { pub span: Span, diff --git a/crates/yuck/src/config/backend_window_options.rs b/crates/yuck/src/config/backend_window_options.rs index 0cc0a9e..1e61f16 100644 --- a/crates/yuck/src/config/backend_window_options.rs +++ b/crates/yuck/src/config/backend_window_options.rs @@ -21,7 +21,7 @@ pub type BackendWindowOptions = X11WindowOptions; pub struct X11WindowOptions { pub wm_ignore: bool, pub sticky: bool, - pub window_type: EwwWindowType, + pub window_type: WindowType, pub struts: StrutDefinition, } @@ -39,7 +39,7 @@ impl X11WindowOptions { } #[derive(Debug, Clone, PartialEq, Eq, smart_default::SmartDefault, serde::Serialize)] -pub enum EwwWindowType { +pub enum WindowType { #[default] Dock, Dialog, @@ -47,7 +47,7 @@ pub enum EwwWindowType { Normal, Utility, } -impl FromStr for EwwWindowType { +impl FromStr for WindowType { type Err = EnumParseError; fn from_str(s: &str) -> Result { diff --git a/crates/yuck/src/config/script_var_definition.rs b/crates/yuck/src/config/script_var_definition.rs index 5940a93..5659563 100644 --- a/crates/yuck/src/config/script_var_definition.rs +++ b/crates/yuck/src/config/script_var_definition.rs @@ -32,7 +32,7 @@ pub enum VarSource { // TODO allow for other executors? (python, etc) Shell(String), #[serde(skip)] - Function(fn() -> Result>), + Function(fn() -> Result>), } #[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)] pub struct PollScriptVar { diff --git a/src/config/window_definition.rs b/src/config/window_definition.rs deleted file mode 100644 index e2c85ec..0000000 --- a/src/config/window_definition.rs +++ /dev/null @@ -1,167 +0,0 @@ -use super::{backend_window_options::*, *}; -use crate::{ensure_xml_tag_is, enum_parse, value::NumWithUnit, widgets::widget_node}; -use derive_more::*; -use serde::{Deserialize, Serialize}; -use smart_default::SmartDefault; -use std::{collections::HashMap, str::FromStr}; - -/// Full window-definition containing the fully expanded widget tree. -/// **Use this** rather than [RawEwwWindowDefinition]. -#[derive(Debug, Clone)] -pub struct EwwWindowDefinition { - pub name: WindowName, - - pub geometry: Option, - pub stacking: WindowStacking, - pub screen_number: Option, - pub widget: Box, - pub resizable: bool, - pub backend_options: BackendWindowOptions, -} - -impl EwwWindowDefinition { - pub fn generate(defs: &HashMap, window: RawEwwWindowDefinition) -> Result { - Ok(EwwWindowDefinition { - name: window.name, - geometry: window.geometry, - stacking: window.stacking, - screen_number: window.screen_number, - resizable: window.resizable, - widget: widget_node::generate_generic_widget_node(defs, &HashMap::new(), window.widget)?, - backend_options: window.backend_options, - }) - } -} - -/// Window-definition storing the raw WidgetUse, as received directly from parsing. -#[derive(Debug, Clone, PartialEq)] -pub struct RawEwwWindowDefinition { - pub name: WindowName, - pub geometry: Option, - pub stacking: WindowStacking, - pub widget: WidgetUse, - pub resizable: bool, - pub backend_options: BackendWindowOptions, - pub screen_number: Option, -} - -impl RawEwwWindowDefinition { - pub fn from_xml_element(xml: &XmlElement) -> Result { - ensure_xml_tag_is!(xml, "window"); - let geometry = match xml.child("geometry") { - Ok(node) => Some(EwwWindowGeometry::from_xml_element(node)?), - Err(_) => None, - }; - - Ok(RawEwwWindowDefinition { - name: WindowName(xml.attr("name")?), - geometry, - widget: WidgetUse::from_xml_node(xml.child("widget")?.only_child()?)?, - stacking: xml.parse_optional_attr("stacking")?.unwrap_or_default(), - // TODO maybe rename this to monitor? - screen_number: xml.parse_optional_attr("screen")?, - resizable: xml.parse_optional_attr("resizable")?.unwrap_or(true), - backend_options: BackendWindowOptions::from_xml_element(xml)?, - }) - } -} - -#[derive(Debug, Clone, PartialEq, Eq, SmartDefault)] -pub enum EwwWindowType { - #[default] - Dock, - Dialog, - Toolbar, - Normal, - Utility, -} -impl FromStr for EwwWindowType { - type Err = anyhow::Error; - - fn from_str(s: &str) -> Result { - enum_parse! { "window type", s, - "dock" => Self::Dock, - "toolbar" => Self::Toolbar, - "dialog" => Self::Dialog, - "normal" => Self::Normal, - "utility" => Self::Utility, - } - } -} - -#[derive(Debug, Clone, Copy, Eq, PartialEq, smart_default::SmartDefault)] -pub enum Side { - #[default] - Top, - Left, - Right, - Bottom, -} - -impl std::str::FromStr for Side { - type Err = anyhow::Error; - - fn from_str(s: &str) -> Result { - enum_parse! { "side", s, - "l" | "left" => Side::Left, - "r" | "right" => Side::Right, - "t" | "top" => Side::Top, - "b" | "bottom" => Side::Bottom, - } - } -} - -// Surface definition if the backend for X11 is enable -#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] -pub struct StrutDefinition { - pub side: Side, - pub dist: NumWithUnit, -} - -impl StrutDefinition { - pub fn from_xml_element(xml: XmlElement) -> Result { - Ok(StrutDefinition { side: xml.attr("side")?.parse()?, dist: xml.attr("distance")?.parse()? }) - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, derive_more::Display, SmartDefault)] -pub enum WindowStacking { - #[default] - Foreground, - Background, - Bottom, - Overlay, -} - -impl std::str::FromStr for WindowStacking { - type Err = anyhow::Error; - - #[cfg(not(feature = "wayland"))] - fn from_str(s: &str) -> Result { - enum_parse! { "WindowStacking", s, - "foreground" | "fg" | "f" => WindowStacking::Foreground, - "background" | "bg" | "b" => WindowStacking::Background, - } - } - - #[cfg(feature = "wayland")] - fn from_str(s: &str) -> Result { - enum_parse! { "WindowStacking", s, - "foreground" | "fg" => WindowStacking::Foreground, - "background" | "bg" => WindowStacking::Background, - "bottom" | "bt" => WindowStacking::Bottom, - "overlay" | "ov" => WindowStacking::Overlay, - } - } -} - -#[repr(transparent)] -#[derive(Clone, Hash, PartialEq, Eq, AsRef, FromStr, Display, Serialize, Deserialize, Default, From, DebugCustom)] -#[debug(fmt = "WindowName(\".0\")")] -pub struct WindowName(String); - -impl std::borrow::Borrow for WindowName { - fn borrow(&self) -> &str { - &self.0 - } -} diff --git a/src/value/primitive.rs b/src/value/primitive.rs deleted file mode 100644 index df49978..0000000 --- a/src/value/primitive.rs +++ /dev/null @@ -1,165 +0,0 @@ -use anyhow::*; -use itertools::Itertools; -use serde::{Deserialize, Serialize}; -use std::{convert::TryFrom, fmt, iter::FromIterator}; - -#[derive(Clone, Deserialize, Serialize, derive_more::From, Default)] -pub struct PrimVal(pub String); - -impl fmt::Display for PrimVal { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.0) - } -} -impl fmt::Debug for PrimVal { - 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 for PrimVal { - 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 PrimVal { - fn from_iter>(iter: T) -> Self { - PrimVal(iter.into_iter().join("")) - } -} - -impl std::str::FromStr for PrimVal { - type Err = anyhow::Error; - - /// 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 { - Ok(PrimVal::from_string(s.to_string())) - } -} - -macro_rules! impl_try_from { - (impl From<$typ:ty> { - $(for $for:ty => |$arg:ident| $code:expr);*; - }) => { - $(impl TryFrom<$typ> for $for { - type Error = anyhow::Error; - fn try_from($arg: $typ) -> Result { $code } - })* - }; -} -macro_rules! impl_primval_from { - ($($t:ty),*) => { - $(impl From<$t> for PrimVal { - fn from(x: $t) -> Self { PrimVal(x.to_string()) } - })* - }; -} - -impl_try_from!(impl From { - for String => |x| x.as_string(); - for f64 => |x| x.as_f64(); - for bool => |x| x.as_bool(); - for Vec => |x| x.as_vec(); -}); - -impl_primval_from!(bool, i32, u32, f32, u8, f64, &str); - -impl From<&serde_json::Value> for PrimVal { - fn from(v: &serde_json::Value) -> Self { - PrimVal( - v.as_str() - .map(|x| x.to_string()) - .or_else(|| serde_json::to_string(v).ok()) - .unwrap_or_else(|| "".to_string()), - ) - } -} - -impl PrimVal { - pub fn from_string(s: String) -> Self { - PrimVal(s) - } - - pub fn into_inner(self) -> String { - self.0 - } - - /// This will never fail - pub fn as_string(&self) -> Result { - Ok(self.0.to_owned()) - } - - pub fn as_f64(&self) -> Result { - self.0.parse().map_err(|e| anyhow!("couldn't convert {:?} to f64: {}", &self, e)) - } - - pub fn as_i32(&self) -> Result { - self.0.parse().map_err(|e| anyhow!("couldn't convert {:?} to i32: {}", &self, e)) - } - - pub fn as_bool(&self) -> Result { - self.0.parse().map_err(|e| anyhow!("couldn't convert {:?} to bool: {}", &self, e)) - } - - pub fn as_vec(&self) -> Result> { - parse_vec(self.0.to_owned()).map_err(|e| anyhow!("Couldn't convert {:#?} to a vec: {}", &self, e)) - } - - pub fn as_json_value(&self) -> Result { - serde_json::from_str::(&self.0) - .with_context(|| format!("Couldn't convert {:#?} to a json object", &self)) - } -} - -fn parse_vec(a: String) -> Result> { - match a.strip_prefix('[').and_then(|x| x.strip_suffix(']')) { - Some(content) => { - let mut items: Vec = 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(anyhow!("Is your array built like this: '[these,are,items]'?")), - } -} - -#[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"); - } -} From 4bf3c6fd638f8537cd978fde7b720ac503d5284a Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Thu, 22 Jul 2021 17:44:15 +0200 Subject: [PATCH 063/137] It compiles :o --- Cargo.lock | 11 +++++ Cargo.toml | 3 +- crates/eww/Cargo.toml | 2 + crates/eww/src/app.rs | 41 ++++++++-------- crates/eww/src/config/eww_config.rs | 21 ++++---- crates/eww/src/config/inbuilt.rs | 6 +-- crates/eww/src/eww_state.rs | 12 ++--- crates/eww/src/main.rs | 4 +- crates/eww/src/opts.rs | 10 ++-- crates/eww/src/script_var_handler.rs | 3 +- crates/eww/src/server.rs | 2 +- crates/eww/src/widgets/mod.rs | 5 +- crates/eww/src/widgets/widget_definitions.rs | 23 +++++---- crates/eww/src/widgets/widget_node.rs | 28 +++-------- crates/eww_shared_util/Cargo.toml | 8 ++++ crates/eww_shared_util/src/lib.rs | 5 ++ crates/eww_shared_util/src/span.rs | 36 ++++++++++++++ crates/eww_shared_util/src/wrappers.rs | 42 ++++++++++++++++ crates/simplexpr/Cargo.toml | 1 + crates/simplexpr/src/ast.rs | 20 ++------ crates/simplexpr/src/dynval.rs | 2 +- crates/simplexpr/src/error.rs | 2 +- crates/simplexpr/src/eval.rs | 44 +++++++++++++---- crates/simplexpr/src/lib.rs | 5 +- crates/simplexpr/src/simplexpr_parser.lalrpop | 5 +- crates/yuck/Cargo.toml | 1 + crates/yuck/src/config/attributes.rs | 7 +-- .../yuck/src/config/backend_window_options.rs | 7 +-- crates/yuck/src/config/config.rs | 4 +- .../yuck/src/config/script_var_definition.rs | 4 +- crates/yuck/src/config/test.rs | 9 +--- crates/yuck/src/config/validate.rs | 8 +--- crates/yuck/src/config/var_definition.rs | 4 +- crates/yuck/src/config/widget_definition.rs | 4 +- crates/yuck/src/config/widget_use.rs | 10 ++-- crates/yuck/src/config/window_definition.rs | 5 +- crates/yuck/src/config/window_geometry.rs | 20 ++++---- crates/yuck/src/error.rs | 3 +- crates/yuck/src/format_diagnostic.rs | 3 +- crates/yuck/src/parser/ast.rs | 48 +------------------ crates/yuck/src/parser/ast_iterator.rs | 6 +-- crates/yuck/src/parser/from_ast.rs | 7 +-- crates/yuck/src/parser/lexer.rs | 3 +- crates/yuck/src/parser/mod.rs | 7 +++ crates/yuck/src/parser/parse_error.rs | 2 +- crates/yuck/src/parser/parser.lalrpop | 5 +- crates/yuck/src/value/mod.rs | 36 -------------- 47 files changed, 279 insertions(+), 265 deletions(-) create mode 100644 crates/eww_shared_util/Cargo.toml create mode 100644 crates/eww_shared_util/src/lib.rs create mode 100644 crates/eww_shared_util/src/span.rs create mode 100644 crates/eww_shared_util/src/wrappers.rs diff --git a/Cargo.lock b/Cargo.lock index 1349726..6a62da4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -436,6 +436,7 @@ dependencies = [ "debug_stub_derive", "derive_more", "dyn-clone", + "eww_shared_util", "extend", "futures-core", "futures-util", @@ -475,6 +476,14 @@ dependencies = [ "yuck", ] +[[package]] +name = "eww_shared_util" +version = "0.1.0" +dependencies = [ + "derive_more", + "serde", +] + [[package]] name = "extend" version = "1.1.1" @@ -1874,6 +1883,7 @@ dependencies = [ name = "simplexpr" version = "0.1.0" dependencies = [ + "eww_shared_util", "insta", "itertools 0.10.1", "lalrpop", @@ -2374,6 +2384,7 @@ dependencies = [ "anyhow", "codespan-reporting", "derive_more", + "eww_shared_util", "insta", "itertools 0.10.1", "lalrpop", diff --git a/Cargo.toml b/Cargo.toml index bed4082..ce16374 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,5 +2,6 @@ members = [ "crates/eww", "crates/simplexpr", - "crates/yuck" + "crates/yuck", + "crates/eww_shared_util" ] diff --git a/crates/eww/Cargo.toml b/crates/eww/Cargo.toml index 910bfbe..2d77226 100644 --- a/crates/eww/Cargo.toml +++ b/crates/eww/Cargo.toml @@ -69,6 +69,8 @@ notify = "5.0.0-pre.7" simplexpr = { path = "../simplexpr" } yuck = { path = "../yuck" } +eww_shared_util = { path = "../eww_shared_util" } + [dev-dependencies] pretty_assertions = "0.7.1" diff --git a/crates/eww/src/app.rs b/crates/eww/src/app.rs index 77456d6..18b2cd9 100644 --- a/crates/eww/src/app.rs +++ b/crates/eww/src/app.rs @@ -6,6 +6,7 @@ use crate::{ }; use anyhow::*; use debug_stub_derive::*; +use eww_shared_util::VarName; use gdk::WindowExt; use gtk::{ContainerExt, CssProviderExt, GtkWindowExt, StyleContextExt, WidgetExt}; use itertools::Itertools; @@ -14,7 +15,7 @@ use std::collections::HashMap; use tokio::sync::mpsc::UnboundedSender; use yuck::{ config::window_geometry::{AnchorPoint, WindowGeometry}, - value::{Coords, VarName}, + value::Coords, }; /// Response that the app may send as a response to a event. @@ -112,30 +113,26 @@ impl App { } } DaemonCommand::ReloadConfigAndCss(sender) => { + let mut errors = Vec::new(); - // TODO implement this - //let mut errors = Vec::new(); - todo!() + let config_result = EwwConfig::read_from_file(&self.paths.get_yuck_path()); + match config_result { + Ok(new_config) => self.handle_command(DaemonCommand::UpdateConfig(new_config)), + Err(e) => errors.push(e), + } - //let config_result = - //EwwConfig::read_from_file(&self.paths.get_eww_xml_path()).and_then(config::EwwConfig::generate); - //match config_result { - //Ok(new_config) => self.handle_command(DaemonCommand::UpdateConfig(new_config)), - //Err(e) => errors.push(e), - //} + let css_result = crate::util::parse_scss_from_file(&self.paths.get_eww_scss_path()); + match css_result { + Ok(new_css) => self.handle_command(DaemonCommand::UpdateCss(new_css)), + Err(e) => errors.push(e), + } - //let css_result = crate::util::parse_scss_from_file(&self.paths.get_eww_scss_path()); - //match css_result { - //Ok(new_css) => self.handle_command(DaemonCommand::UpdateCss(new_css)), - //Err(e) => errors.push(e), - //} - - //let errors = errors.into_iter().map(|e| format!("{:?}", e)).join("\n"); - //if errors.is_empty() { - //sender.send(DaemonResponse::Success(String::new()))?; - //} else { - //sender.send(DaemonResponse::Failure(errors))?; - //} + let errors = errors.into_iter().map(|e| format!("{:?}", e)).join("\n"); + if errors.is_empty() { + sender.send(DaemonResponse::Success(String::new()))?; + } else { + sender.send(DaemonResponse::Failure(errors))?; + } } DaemonCommand::UpdateConfig(config) => { self.load_config(config)?; diff --git a/crates/eww/src/config/eww_config.rs b/crates/eww/src/config/eww_config.rs index ed1af4b..1fafdcd 100644 --- a/crates/eww/src/config/eww_config.rs +++ b/crates/eww/src/config/eww_config.rs @@ -1,15 +1,13 @@ use anyhow::*; +use eww_shared_util::VarName; use std::collections::HashMap; use yuck::{ config::{script_var_definition::ScriptVarDefinition, widget_definition::WidgetDefinition}, parser::from_ast::FromAst, - value::VarName, }; use simplexpr::dynval::DynVal; -use std::path::PathBuf; - use super::{script_var, EwwWindowDefinition}; /// Eww configuration structure. @@ -19,33 +17,30 @@ pub struct EwwConfig { windows: HashMap, initial_variables: HashMap, script_vars: HashMap, - pub filepath: PathBuf, } impl EwwConfig { pub fn read_from_file>(path: P) -> Result { let content = std::fs::read_to_string(path)?; - let ast = yuck::parser::parse_string(0, &content)?; + let ast = yuck::parser::parse_toplevel(0, &content)?; let config = yuck::config::Config::from_ast(ast)?; Self::generate(config) } pub fn generate(config: yuck::config::Config) -> Result { + let yuck::config::Config { widget_definitions, window_definitions, var_definitions, script_vars } = config; Ok(EwwConfig { - windows: config - .window_definitions + windows: window_definitions .into_iter() .map(|(name, window)| { Ok(( name, - EwwWindowDefinition::generate(&config.widget_definitions, window) - .context("Failed expand window definition")?, + EwwWindowDefinition::generate(&widget_definitions, window).context("Failed expand window definition")?, )) }) .collect::>>()?, - widgets: config.widget_definitions, - initial_variables: config.var_definitions.into_iter().map(|(k, v)| (k, v.initial_value)).collect(), - script_vars: config.script_vars, - filepath: todo!(), + widgets: widget_definitions, + initial_variables: var_definitions.into_iter().map(|(k, v)| (k, v.initial_value)).collect(), + script_vars, }) } diff --git a/crates/eww/src/config/inbuilt.rs b/crates/eww/src/config/inbuilt.rs index 5f98212..9e36276 100644 --- a/crates/eww/src/config/inbuilt.rs +++ b/crates/eww/src/config/inbuilt.rs @@ -1,12 +1,10 @@ use std::{collections::HashMap, time::Duration}; use simplexpr::dynval::DynVal; -use yuck::{ - config::script_var_definition::{PollScriptVar, ScriptVarDefinition, VarSource}, - value::VarName, -}; +use yuck::config::script_var_definition::{PollScriptVar, ScriptVarDefinition, VarSource}; use crate::config::system_stats::*; +use eww_shared_util::VarName; macro_rules! builtin_vars { ($interval:expr, $($name:literal => $fun:expr),*$(,)?) => {{ diff --git a/crates/eww/src/eww_state.rs b/crates/eww/src/eww_state.rs index 20bb448..92aeeee 100644 --- a/crates/eww/src/eww_state.rs +++ b/crates/eww/src/eww_state.rs @@ -1,6 +1,6 @@ use anyhow::*; +use eww_shared_util::{AttrName, VarName}; use std::{collections::HashMap, sync::Arc}; -use yuck::value::{AttrName, VarName}; use simplexpr::{dynval::DynVal, SimplExpr}; @@ -12,9 +12,8 @@ pub struct StateChangeHandler { } impl StateChangeHandler { - fn used_variables(&self) -> impl Iterator + '_ { - // TODO fix this clone - self.unresolved_values.iter().flat_map(|(_, value)| value.var_refs()).map(|x| VarName(x.to_string())) + fn used_variables(&self) -> impl Iterator { + self.unresolved_values.iter().flat_map(|(_, value)| value.var_refs()) } /// Run the StateChangeHandler. @@ -24,7 +23,7 @@ impl StateChangeHandler { .unresolved_values .clone() .into_iter() - .map(|(attr_name, value)| Ok((attr_name, value.resolve_fully(state)?))) + .map(|(attr_name, value)| Ok((attr_name, value.eval(state)?))) .collect::>(); match resolved_attrs { @@ -107,8 +106,7 @@ impl EwwState { /// resolves a value if possible, using the current eww_state. pub fn resolve_once<'a>(&'a self, value: &'a SimplExpr) -> Result { - // TODO fix this clone - Ok(value.clone().eval(&self.variables_state.into_iter().map(|(k, v)| (k.0, v)).collect())?) + Ok(value.clone().eval(&self.variables_state)?) } /// Resolve takes a function that applies a set of fully resolved attribute diff --git a/crates/eww/src/main.rs b/crates/eww/src/main.rs index a39850c..527e222 100644 --- a/crates/eww/src/main.rs +++ b/crates/eww/src/main.rs @@ -162,8 +162,8 @@ impl EwwPaths { self.config_dir.as_path() } - pub fn get_eww_xml_path(&self) -> PathBuf { - self.config_dir.join("eww.xml") + pub fn get_yuck_path(&self) -> PathBuf { + self.config_dir.join("eww.yuck") } pub fn get_eww_scss_path(&self) -> PathBuf { diff --git a/crates/eww/src/opts.rs b/crates/eww/src/opts.rs index 95872d5..9e82045 100644 --- a/crates/eww/src/opts.rs +++ b/crates/eww/src/opts.rs @@ -1,11 +1,9 @@ use anyhow::*; +use eww_shared_util::VarName; use serde::{Deserialize, Serialize}; use simplexpr::dynval::DynVal; use structopt::StructOpt; -use yuck::{ - config::window_geometry::AnchorPoint, - value::{Coords, VarName}, -}; +use yuck::{config::window_geometry::AnchorPoint, value::Coords}; use crate::app; @@ -23,7 +21,7 @@ struct RawOpt { #[structopt(long = "debug", global = true)] log_debug: bool, - /// override path to configuration directory (directory that contains eww.xml and eww.scss) + /// override path to configuration directory (directory that contains eww.yuck and eww.scss) #[structopt(short, long, global = true)] config: Option, @@ -153,7 +151,7 @@ fn parse_var_update_arg(s: &str) -> Result<(VarName, DynVal)> { impl ActionWithServer { pub fn into_daemon_command(self) -> (app::DaemonCommand, Option) { let command = match self { - ActionWithServer::Update { mappings } => app::DaemonCommand::UpdateVars(mappings.into_iter().collect()), + ActionWithServer::Update { mappings } => app::DaemonCommand::UpdateVars(mappings), ActionWithServer::KillServer => app::DaemonCommand::KillServer, ActionWithServer::CloseAll => app::DaemonCommand::CloseAll, diff --git a/crates/eww/src/script_var_handler.rs b/crates/eww/src/script_var_handler.rs index ced0b17..0d94395 100644 --- a/crates/eww/src/script_var_handler.rs +++ b/crates/eww/src/script_var_handler.rs @@ -4,13 +4,14 @@ use crate::app; use anyhow::*; use app::DaemonCommand; +use eww_shared_util::VarName; use simplexpr::dynval::DynVal; use tokio::{ io::{AsyncBufReadExt, BufReader}, sync::mpsc::UnboundedSender, }; use tokio_util::sync::CancellationToken; -use yuck::{config::script_var_definition::{PollScriptVar, ScriptVarDefinition, TailScriptVar}, value::VarName}; +use yuck::config::script_var_definition::{PollScriptVar, ScriptVarDefinition, TailScriptVar}; /// Initialize the script var handler, and return a handle to that handler, which can be used to control /// the script var execution. diff --git a/crates/eww/src/server.rs b/crates/eww/src/server.rs index 3df39a6..f2c8127 100644 --- a/crates/eww/src/server.rs +++ b/crates/eww/src/server.rs @@ -27,7 +27,7 @@ pub fn initialize_server(paths: EwwPaths) -> Result<()> { .with_context(|| format!("Failed to change working directory to {}", paths.get_config_dir().display()))?; log::info!("Loading paths: {}", &paths); - let eww_config = config::EwwConfig::read_from_file(&paths.get_eww_xml_path())?; + let eww_config = config::EwwConfig::read_from_file(&paths.get_yuck_path())?; gtk::init()?; diff --git a/crates/eww/src/widgets/mod.rs b/crates/eww/src/widgets/mod.rs index f36d48b..7598ff7 100644 --- a/crates/eww/src/widgets/mod.rs +++ b/crates/eww/src/widgets/mod.rs @@ -1,9 +1,10 @@ use crate::eww_state::*; use anyhow::*; +use eww_shared_util::AttrName; use gtk::prelude::*; use itertools::Itertools; use std::collections::HashMap; -use yuck::{config::widget_definition::WidgetDefinition, value::AttrName}; +use yuck::config::widget_definition::WidgetDefinition; use std::process::Command; use widget_definitions::*; @@ -129,7 +130,7 @@ macro_rules! resolve_block { let attr_map: Result<_> = try { ::maplit::hashmap! { $( - yuck::value::AttrName(::std::stringify!($attr_name).to_owned()) => + eww_shared_util::AttrName(::std::stringify!($attr_name).to_owned()) => resolve_block!(@get_value $args, &::std::stringify!($attr_name).replace('_', "-"), $(= $default)?) ),* } diff --git a/crates/eww/src/widgets/widget_definitions.rs b/crates/eww/src/widgets/widget_definitions.rs index 2494042..f4cd54c 100644 --- a/crates/eww/src/widgets/widget_definitions.rs +++ b/crates/eww/src/widgets/widget_definitions.rs @@ -1,7 +1,7 @@ #![allow(clippy::option_map_unit_fn)] use super::{run_command, BuilderArgs}; use crate::{ - config, enum_parse, eww_state, resolve_block, + enum_parse, eww_state, resolve_block, util::{list_difference, parse_duration}, widgets::widget_node, }; @@ -10,6 +10,7 @@ use gdk::WindowExt; use glib; use gtk::{self, prelude::*, ImageExt}; use std::{cell::RefCell, collections::HashMap, rc::Rc}; +use yuck::parser::from_ast::FromAst; // TODO figure out how to // TODO https://developer.gnome.org/gtk3/stable/GtkFixed.html @@ -244,12 +245,13 @@ fn build_gtk_combo_box_text(bargs: &mut BuilderArgs) -> Result>> = Rc::new(RefCell::new(None)); resolve_block!(bargs, gtk_widget, { // @prop items - Items that should be displayed in the combo box - prop(items: as_vec) { - gtk_widget.remove_all(); - for i in items { - gtk_widget.append_text(&i); - } - }, + // TODO reimplement, obviously + //prop(items: as_vec) { + //gtk_widget.remove_all(); + //for i in items { + //gtk_widget.append_text(&i); + //} + //}, // @prop onchange - runs the code when a item was selected, replacing {} with the item as a string prop(onchange: as_string) { let old_id = on_change_handler_id.replace(Some( @@ -518,15 +520,16 @@ fn build_gtk_literal(bargs: &mut BuilderArgs) -> Result { gtk_widget.set_widget_name("literal"); // TODO these clones here are dumdum - let window_name = bargs.window_name.clone(); + let window_name = bargs.window_name.to_string(); let widget_definitions = bargs.widget_definitions.clone(); + resolve_block!(bargs, gtk_widget, { // @prop content - inline Eww XML that will be rendered as a widget. prop(content: as_string) { gtk_widget.get_children().iter().for_each(|w| gtk_widget.remove(w)); if !content.is_empty() { - let document = roxmltree::Document::parse(&content).map_err(|e| anyhow!("Failed to parse eww xml literal: {:?}", e))?; - let content_widget_use = config::element::WidgetUse::from_xml_node(document.root_element().into())?; + let ast = yuck::parser::parse_string(usize::MAX, &content)?; + let content_widget_use = yuck::config::widget_use::WidgetUse::from_ast(ast)?; let widget_node = &*widget_node::generate_generic_widget_node(&widget_definitions, &HashMap::new(), content_widget_use)?; let child_widget = widget_node.render(&mut eww_state::EwwState::default(), &window_name, &widget_definitions)?; diff --git a/crates/eww/src/widgets/widget_node.rs b/crates/eww/src/widgets/widget_node.rs index ad0ef0d..82a367f 100644 --- a/crates/eww/src/widgets/widget_node.rs +++ b/crates/eww/src/widgets/widget_node.rs @@ -1,13 +1,10 @@ use crate::eww_state::EwwState; use anyhow::*; use dyn_clone; +use eww_shared_util::{AttrName, Span, VarName}; use simplexpr::SimplExpr; use std::collections::HashMap; -use yuck::{ - config::{widget_definition::WidgetDefinition, widget_use::WidgetUse}, - parser::ast::Span, - value::{AttrName, VarName}, -}; +use yuck::config::{widget_definition::WidgetDefinition, widget_use::WidgetUse}; pub trait WidgetNode: std::fmt::Debug + dyn_clone::DynClone + Send + Sync { fn get_name(&self) -> &str; @@ -70,9 +67,8 @@ impl Generic { } /// returns all the variables that are referenced in this widget - pub fn referenced_vars(&self) -> impl Iterator + '_ { - // TODO fix this clone - self.attrs.iter().flat_map(|(_, value)| value.var_refs()).map(|x| VarName(x.to_string())) + pub fn referenced_vars(&self) -> impl Iterator { + self.attrs.iter().flat_map(|(_, value)| value.var_refs()) } } @@ -108,12 +104,7 @@ pub fn generate_generic_widget_node( .attrs .attrs .into_iter() - .map(|(name, value)| { - Ok(( - VarName(name.0), - SimplExpr::Literal(value.value.span().into(), value.value.as_simplexpr()?.resolve_one_level(local_env)?), - )) - }) + .map(|(name, value)| Ok((VarName(name.0), value.value.as_simplexpr()?.resolve_one_level(local_env)))) .collect::>>()?; let content = generate_generic_widget_node(defs, &new_local_env, def.widget.clone())?; @@ -126,13 +117,8 @@ pub fn generate_generic_widget_node( .attrs .attrs .into_iter() - .map(|(name, value)| { - Ok(( - VarName(name.0), - SimplExpr::Literal(value.value.span().into(), value.value.as_simplexpr()?.resolve_one_level(local_env)?), - )) - }) - .collect()?, + .map(|(name, value)| Ok((name, value.value.as_simplexpr()?.resolve_one_level(local_env)))) + .collect::>>()?, children: w .children diff --git a/crates/eww_shared_util/Cargo.toml b/crates/eww_shared_util/Cargo.toml new file mode 100644 index 0000000..3293833 --- /dev/null +++ b/crates/eww_shared_util/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "eww_shared_util" +version = "0.1.0" +edition = "2018" + +[dependencies] +serde = {version = "1.0", features = ["derive"]} +derive_more = "0.99" diff --git a/crates/eww_shared_util/src/lib.rs b/crates/eww_shared_util/src/lib.rs new file mode 100644 index 0000000..de590ff --- /dev/null +++ b/crates/eww_shared_util/src/lib.rs @@ -0,0 +1,5 @@ +pub mod span; +pub mod wrappers; + +pub use span::*; +pub use wrappers::*; diff --git a/crates/eww_shared_util/src/span.rs b/crates/eww_shared_util/src/span.rs new file mode 100644 index 0000000..589f295 --- /dev/null +++ b/crates/eww_shared_util/src/span.rs @@ -0,0 +1,36 @@ +#[derive(Eq, PartialEq, Clone, Copy, serde::Serialize, serde::Deserialize)] +pub struct Span(pub usize, pub usize, pub usize); +pub static DUMMY_SPAN: Span = Span(usize::MAX, usize::MAX, usize::MAX); + +impl Span { + /// Get the span that includes this and the other span completely. + /// Will panic if the spans are from different file_ids. + pub fn to(mut self, other: Span) -> Self { + assert!(other.2 == self.2); + self.1 = other.1; + self + } + + pub fn ending_at(mut self, end: usize) -> Self { + self.1 = end; + self + } + + /// Turn this span into a span only highlighting the point it starts at, setting the length to 0. + pub fn point_span(mut self) -> Self { + self.1 = self.0; + self + } +} + +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) + } +} diff --git a/crates/eww_shared_util/src/wrappers.rs b/crates/eww_shared_util/src/wrappers.rs new file mode 100644 index 0000000..dabdfe8 --- /dev/null +++ b/crates/eww_shared_util/src/wrappers.rs @@ -0,0 +1,42 @@ +use derive_more::*; +use serde::{Deserialize, Serialize}; + +/// The name of a variable +#[repr(transparent)] +#[derive( + Clone, Hash, PartialEq, Eq, Serialize, Deserialize, AsRef, From, FromStr, Display, DebugCustom, +)] +#[debug(fmt = "VarName({})", .0)] +pub struct VarName(pub String); + +impl std::borrow::Borrow for VarName { + fn borrow(&self) -> &str { + &self.0 + } +} + +impl From<&str> for VarName { + fn from(s: &str) -> Self { + VarName(s.to_owned()) + } +} + +/// The name of an attribute +#[repr(transparent)] +#[derive( + Clone, Hash, PartialEq, Eq, Serialize, Deserialize, AsRef, From, FromStr, Display, DebugCustom, +)] +#[debug(fmt="AttrName({})", .0)] +pub struct AttrName(pub String); + +impl std::borrow::Borrow for AttrName { + fn borrow(&self) -> &str { + &self.0 + } +} + +impl From<&str> for AttrName { + fn from(s: &str) -> Self { + AttrName(s.to_owned()) + } +} diff --git a/crates/simplexpr/Cargo.toml b/crates/simplexpr/Cargo.toml index df354ef..ead2853 100644 --- a/crates/simplexpr/Cargo.toml +++ b/crates/simplexpr/Cargo.toml @@ -20,6 +20,7 @@ serde_json = "1.0" strum = { version = "0.21", features = ["derive"] } +eww_shared_util = { path = "../eww_shared_util" } [build-dependencies] diff --git a/crates/simplexpr/src/ast.rs b/crates/simplexpr/src/ast.rs index 22bc15d..e63d2d3 100644 --- a/crates/simplexpr/src/ast.rs +++ b/crates/simplexpr/src/ast.rs @@ -1,23 +1,9 @@ use crate::dynval::DynVal; +use eww_shared_util::{Span, DUMMY_SPAN}; 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); -pub static DUMMY_SPAN: Span = Span(usize::MAX, usize::MAX, usize::MAX); - -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) - } -} +use eww_shared_util::VarName; #[rustfmt::skip] #[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug, strum::EnumString, strum::Display)] @@ -46,7 +32,7 @@ pub enum UnaryOp { #[derive(Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum SimplExpr { Literal(Span, DynVal), - VarRef(Span, String), + VarRef(Span, VarName), BinOp(Span, Box, BinOp, Box), UnaryOp(Span, UnaryOp, Box), IfElse(Span, Box, Box, Box), diff --git a/crates/simplexpr/src/dynval.rs b/crates/simplexpr/src/dynval.rs index e9685f0..f72248d 100644 --- a/crates/simplexpr/src/dynval.rs +++ b/crates/simplexpr/src/dynval.rs @@ -1,4 +1,4 @@ -use crate::ast::Span; +use eww_shared_util::Span; use itertools::Itertools; use serde::{Deserialize, Serialize}; use std::{fmt, iter::FromIterator, str::FromStr}; diff --git a/crates/simplexpr/src/error.rs b/crates/simplexpr/src/error.rs index 187f18d..76bbbbc 100644 --- a/crates/simplexpr/src/error.rs +++ b/crates/simplexpr/src/error.rs @@ -1,8 +1,8 @@ use crate::{ - ast::Span, dynval, parser::lexer::{self, LexicalError}, }; +use eww_shared_util::Span; pub type Result = std::result::Result; #[derive(thiserror::Error, Debug)] diff --git a/crates/simplexpr/src/eval.rs b/crates/simplexpr/src/eval.rs index 3c70583..ea2bb2a 100644 --- a/crates/simplexpr/src/eval.rs +++ b/crates/simplexpr/src/eval.rs @@ -1,9 +1,10 @@ use itertools::Itertools; use crate::{ - ast::{BinOp, SimplExpr, Span, UnaryOp}, + ast::{BinOp, SimplExpr, UnaryOp}, dynval::{ConversionError, DynVal}, }; +use eww_shared_util::{Span, VarName}; use std::collections::HashMap; #[derive(Debug, thiserror::Error)] @@ -50,13 +51,6 @@ impl EvalError { } } -type VarName = String; - -pub trait FunctionSource { - type Err; - fn run_fn(&self, name: &str, args: &[DynVal]) -> Result; -} - impl SimplExpr { pub fn map_terminals_into(self, f: impl Fn(Self) -> Self) -> Self { use SimplExpr::*; @@ -70,6 +64,36 @@ impl SimplExpr { } } + /// map over all of the variable references, replacing them with whatever expression the provided function returns. + /// Returns [Err] when the provided function fails with an [Err] + pub fn try_map_var_refs Result + Copy>(self, f: F) -> Result { + use SimplExpr::*; + Ok(match self { + BinOp(span, box a, op, box b) => BinOp(span, box a.try_map_var_refs(f)?, op, box b.try_map_var_refs(f)?), + UnaryOp(span, op, box a) => UnaryOp(span, op, box a.try_map_var_refs(f)?), + IfElse(span, box a, box b, box c) => { + IfElse(span, box a.try_map_var_refs(f)?, box b.try_map_var_refs(f)?, box c.try_map_var_refs(f)?) + } + JsonAccess(span, box a, box b) => JsonAccess(span, box a.try_map_var_refs(f)?, box b.try_map_var_refs(f)?), + FunctionCall(span, name, args) => { + FunctionCall(span, name, args.into_iter().map(|x| x.try_map_var_refs(f)).collect::>()?) + } + VarRef(span, name) => f(span, name)?, + x @ Literal(..) => x, + }) + } + + pub fn map_var_refs(self, f: impl Fn(Span, VarName) -> SimplExpr) -> Self { + self.try_map_var_refs(|span, var| Ok::<_, !>(f(span, var))).into_ok() + } + + /// resolve partially. + /// If a var-ref links to another var-ref, that other var-ref is used. + /// If a referenced variable is not found in the given hashmap, returns the var-ref unchanged. + pub fn resolve_one_level(self, variables: &HashMap) -> Self { + self.map_var_refs(|span, name| variables.get(&name).cloned().unwrap_or_else(|| Self::VarRef(span, name))) + } + /// resolve variable references in the expression. Fails if a variable cannot be resolved. pub fn resolve_refs(self, variables: &HashMap) -> Result { use SimplExpr::*; @@ -96,7 +120,7 @@ impl SimplExpr { } } - pub fn var_refs(&self) -> Vec<&String> { + pub fn var_refs(&self) -> Vec<&VarName> { use SimplExpr::*; match self { Literal(..) => Vec::new(), @@ -130,7 +154,7 @@ impl SimplExpr { 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)) + Ok(values.get(name).cloned().ok_or_else(|| EvalError::UnresolvedVariable(name.clone()).at(*span))?.at(*span)) } SimplExpr::BinOp(_, a, op, b) => { let a = a.eval(values)?; diff --git a/crates/simplexpr/src/lib.rs b/crates/simplexpr/src/lib.rs index 74df615..48156f4 100644 --- a/crates/simplexpr/src/lib.rs +++ b/crates/simplexpr/src/lib.rs @@ -1,13 +1,16 @@ #![feature(box_patterns)] #![feature(box_syntax)] #![feature(try_blocks)] +#![feature(unwrap_infallible)] +#![feature(never_type)] + pub mod ast; pub mod dynval; pub mod error; pub mod eval; pub mod parser; -pub use ast::{SimplExpr, Span}; +pub use ast::SimplExpr; use lalrpop_util::lalrpop_mod; diff --git a/crates/simplexpr/src/simplexpr_parser.lalrpop b/crates/simplexpr/src/simplexpr_parser.lalrpop index f351087..9620aac 100644 --- a/crates/simplexpr/src/simplexpr_parser.lalrpop +++ b/crates/simplexpr/src/simplexpr_parser.lalrpop @@ -1,4 +1,5 @@ -use crate::ast::{SimplExpr::{self, *}, Span, BinOp::*, UnaryOp::*}; +use crate::ast::{SimplExpr::{self, *}, BinOp::*, UnaryOp::*}; +use eww_shared_util::{Span, VarName}; use crate::parser::lexer::{Token, LexicalError}; use crate::parser::lalrpop_helpers::*; use lalrpop_util::ParseError; @@ -66,7 +67,7 @@ pub Expr: SimplExpr = { }, , - => VarRef(Span(l, r, fid), ident.to_string()), + => VarRef(Span(l, r, fid), VarName(ident.to_string())), "(" ")", #[precedence(level="1")] #[assoc(side="right")] diff --git a/crates/yuck/Cargo.toml b/crates/yuck/Cargo.toml index c0031dd..68f4557 100644 --- a/crates/yuck/Cargo.toml +++ b/crates/yuck/Cargo.toml @@ -27,6 +27,7 @@ static_assertions = "1.1" simplexpr = { path = "../simplexpr" } +eww_shared_util = { path = "../eww_shared_util" } [build-dependencies] lalrpop = "0.19.5" diff --git a/crates/yuck/src/config/attributes.rs b/crates/yuck/src/config/attributes.rs index e749c71..0efd2a5 100644 --- a/crates/yuck/src/config/attributes.rs +++ b/crates/yuck/src/config/attributes.rs @@ -11,12 +11,9 @@ use simplexpr::{ use crate::{ error::AstError, - parser::{ - ast::{Ast, Span}, - from_ast::FromAst, - }, - value::AttrName, + parser::{ast::Ast, from_ast::FromAst}, }; +use eww_shared_util::{AttrName, Span, VarName}; #[derive(Debug, thiserror::Error)] pub enum AttrError { diff --git a/crates/yuck/src/config/backend_window_options.rs b/crates/yuck/src/config/backend_window_options.rs index 1e61f16..4267388 100644 --- a/crates/yuck/src/config/backend_window_options.rs +++ b/crates/yuck/src/config/backend_window_options.rs @@ -5,13 +5,10 @@ use anyhow::*; use crate::{ enum_parse, error::AstResult, - parser::{ - ast::{Ast, Span}, - ast_iterator::AstIterator, - from_ast::FromAstElementContent, - }, + parser::{ast::Ast, ast_iterator::AstIterator, from_ast::FromAstElementContent}, value::NumWithUnit, }; +use eww_shared_util::Span; use super::{attributes::Attributes, window_definition::EnumParseError}; diff --git a/crates/yuck/src/config/config.rs b/crates/yuck/src/config/config.rs index 710e717..e893876 100644 --- a/crates/yuck/src/config/config.rs +++ b/crates/yuck/src/config/config.rs @@ -10,12 +10,12 @@ use crate::{ config::script_var_definition::{PollScriptVar, TailScriptVar}, error::{AstError, AstResult, OptionAstErrorExt}, parser::{ - ast::{Ast, Span}, + ast::Ast, ast_iterator::AstIterator, from_ast::{FromAst, FromAstElementContent}, }, - value::{AttrName, VarName}, }; +use eww_shared_util::{AttrName, Span, VarName}; pub enum TopLevel { VarDefinition(VarDefinition), diff --git a/crates/yuck/src/config/script_var_definition.rs b/crates/yuck/src/config/script_var_definition.rs index 5659563..8091110 100644 --- a/crates/yuck/src/config/script_var_definition.rs +++ b/crates/yuck/src/config/script_var_definition.rs @@ -5,12 +5,12 @@ use simplexpr::{dynval::DynVal, SimplExpr}; use crate::{ error::{AstError, AstResult}, parser::{ - ast::{Ast, Span}, + ast::Ast, ast_iterator::AstIterator, from_ast::{FromAst, FromAstElementContent}, }, - value::{AttrName, VarName}, }; +use eww_shared_util::{AttrName, Span, VarName}; #[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)] pub enum ScriptVarDefinition { diff --git a/crates/yuck/src/config/test.rs b/crates/yuck/src/config/test.rs index bd34c77..677f4b3 100644 --- a/crates/yuck/src/config/test.rs +++ b/crates/yuck/src/config/test.rs @@ -1,16 +1,11 @@ use crate::{ config::config::Config, - parser::{ - self, - ast::{Ast, Span}, - from_ast::FromAst, - lexer::Lexer, - }, + parser::{self, ast::Ast, from_ast::FromAst, lexer::Lexer}, }; #[test] fn test_config() { - let input = r#" + let input = r#" (defwidget foo [arg] "heyho") (defwidget bar [arg arg2] diff --git a/crates/yuck/src/config/validate.rs b/crates/yuck/src/config/validate.rs index 153656a..093e2c5 100644 --- a/crates/yuck/src/config/validate.rs +++ b/crates/yuck/src/config/validate.rs @@ -4,15 +4,11 @@ use simplexpr::SimplExpr; use crate::{ error::AstResult, - parser::{ - ast::{Ast, Span}, - ast_iterator::AstIterator, - from_ast::FromAst, - }, - value::{AttrName, VarName}, + parser::{ast::Ast, ast_iterator::AstIterator, from_ast::FromAst}, }; use super::{widget_definition::WidgetDefinition, widget_use::WidgetUse}; +use eww_shared_util::{AttrName, Span, VarName}; #[derive(Debug, thiserror::Error)] pub enum ValidationError { diff --git a/crates/yuck/src/config/var_definition.rs b/crates/yuck/src/config/var_definition.rs index da458d5..065820d 100644 --- a/crates/yuck/src/config/var_definition.rs +++ b/crates/yuck/src/config/var_definition.rs @@ -5,12 +5,12 @@ use simplexpr::{dynval::DynVal, SimplExpr}; use crate::{ error::AstResult, parser::{ - ast::{Ast, Span}, + ast::Ast, ast_iterator::AstIterator, from_ast::{FromAst, FromAstElementContent}, }, - value::{AttrName, VarName}, }; +use eww_shared_util::{AttrName, Span, VarName}; #[derive(Debug, PartialEq, Eq, Clone, serde::Serialize)] pub struct VarDefinition { diff --git a/crates/yuck/src/config/widget_definition.rs b/crates/yuck/src/config/widget_definition.rs index b7bf2d9..641f587 100644 --- a/crates/yuck/src/config/widget_definition.rs +++ b/crates/yuck/src/config/widget_definition.rs @@ -5,12 +5,12 @@ use simplexpr::SimplExpr; use crate::{ error::AstResult, parser::{ - ast::{Ast, Span}, + ast::Ast, ast_iterator::AstIterator, from_ast::{FromAst, FromAstElementContent}, }, - value::{AttrName, VarName}, }; +use eww_shared_util::{AttrName, Span, VarName}; use super::widget_use::WidgetUse; #[derive(Debug, PartialEq, Eq, Clone, serde::Serialize)] diff --git a/crates/yuck/src/config/widget_use.rs b/crates/yuck/src/config/widget_use.rs index 78a6cfc..18efcc0 100644 --- a/crates/yuck/src/config/widget_use.rs +++ b/crates/yuck/src/config/widget_use.rs @@ -5,13 +5,9 @@ use simplexpr::SimplExpr; use crate::{ config::attributes::AttrEntry, error::AstResult, - parser::{ - ast::{Ast, Span}, - ast_iterator::AstIterator, - from_ast::FromAst, - }, - value::AttrName, + parser::{ast::Ast, ast_iterator::AstIterator, from_ast::FromAst}, }; +use eww_shared_util::{AttrName, Span, VarName}; use super::attributes::Attributes; @@ -28,7 +24,7 @@ impl FromAst for WidgetUse { let span = e.span(); if let Ok(text) = e.as_literal_ref() { Ok(Self { - name: "text".to_string(), + name: "label".to_string(), attrs: Attributes::new( span.into(), maplit::hashmap! { diff --git a/crates/yuck/src/config/window_definition.rs b/crates/yuck/src/config/window_definition.rs index f49f940..8687455 100644 --- a/crates/yuck/src/config/window_definition.rs +++ b/crates/yuck/src/config/window_definition.rs @@ -5,12 +5,13 @@ use simplexpr::{dynval::DynVal, SimplExpr}; use crate::{ error::{AstError, AstResult}, parser::{ - ast::{Ast, Span}, + ast::Ast, ast_iterator::AstIterator, from_ast::{FromAst, FromAstElementContent}, }, - value::{AttrName, NumWithUnit, VarName}, + value::NumWithUnit, }; +use eww_shared_util::{AttrName, Span, VarName}; use super::{backend_window_options::BackendWindowOptions, widget_use::WidgetUse, window_geometry::WindowGeometry}; diff --git a/crates/yuck/src/config/window_geometry.rs b/crates/yuck/src/config/window_geometry.rs index 16aac88..4245ab0 100644 --- a/crates/yuck/src/config/window_geometry.rs +++ b/crates/yuck/src/config/window_geometry.rs @@ -2,13 +2,20 @@ use std::collections::HashMap; use simplexpr::{dynval::DynVal, SimplExpr}; -use crate::{enum_parse, error::{AstError, AstResult}, parser::{ - ast::{Ast, Span}, ast_iterator::AstIterator, +use crate::{ + enum_parse, + error::{AstError, AstResult}, + parser::{ + ast::Ast, + ast_iterator::AstIterator, from_ast::{FromAst, FromAstElementContent}, - }, value::{AttrName, Coords, VarName}}; + }, + value::Coords, +}; use super::{widget_use::WidgetUse, window_definition::EnumParseError}; -use serde::{Serialize, Deserialize}; +use eww_shared_util::{AttrName, Span, VarName}; +use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Copy, Eq, PartialEq, smart_default::SmartDefault, Serialize, Deserialize, strum::Display)] pub enum AnchorAlignment { @@ -84,7 +91,6 @@ pub enum AnchorPointParseError { EnumParseError(#[from] EnumParseError), } - impl std::str::FromStr for AnchorPoint { type Err = AnchorPointParseError; @@ -92,9 +98,7 @@ impl std::str::FromStr for AnchorPoint { if s == "center" { Ok(AnchorPoint { x: AnchorAlignment::CENTER, y: AnchorAlignment::CENTER }) } else { - let (first, second) = s - .split_once(' ') - .ok_or_else(|| AnchorPointParseError::WrongFormat(s.to_string()))?; + let (first, second) = s.split_once(' ').ok_or_else(|| AnchorPointParseError::WrongFormat(s.to_string()))?; let x_y_result: Result<_, EnumParseError> = try { AnchorPoint { x: AnchorAlignment::from_x_alignment(first)?, y: AnchorAlignment::from_y_alignment(second)? } }; diff --git a/crates/yuck/src/error.rs b/crates/yuck/src/error.rs index 846a5d0..f973ebd 100644 --- a/crates/yuck/src/error.rs +++ b/crates/yuck/src/error.rs @@ -1,11 +1,12 @@ use crate::{ config::{attributes::AttrError, validate::ValidationError}, parser::{ - ast::{Ast, AstType, Span}, + ast::{Ast, AstType}, lexer, parse_error, }, }; use codespan_reporting::{diagnostic, files}; +use eww_shared_util::{AttrName, Span, VarName}; use simplexpr::dynval; use thiserror::Error; diff --git a/crates/yuck/src/format_diagnostic.rs b/crates/yuck/src/format_diagnostic.rs index a1238c3..c724f74 100644 --- a/crates/yuck/src/format_diagnostic.rs +++ b/crates/yuck/src/format_diagnostic.rs @@ -5,7 +5,8 @@ use diagnostic::*; use crate::error::AstError; -use super::parser::{ast::Span, parse_error}; +use super::parser::parse_error; +use eww_shared_util::{AttrName, Span, VarName}; macro_rules! gen_diagnostic { ( diff --git a/crates/yuck/src/parser/ast.rs b/crates/yuck/src/parser/ast.rs index 24a6abc..bcaa402 100644 --- a/crates/yuck/src/parser/ast.rs +++ b/crates/yuck/src/parser/ast.rs @@ -2,61 +2,15 @@ use itertools::Itertools; use simplexpr::{ast::SimplExpr, dynval::DynVal}; use std::collections::HashMap; +use eww_shared_util::Span; use std::fmt::Display; use super::{ast_iterator::AstIterator, from_ast::FromAst}; use crate::{ config::attributes::{AttrEntry, Attributes}, error::{AstError, AstResult, OptionAstErrorExt}, - value::AttrName, }; -#[derive(Eq, PartialEq, Clone, Copy, serde::Serialize)] -pub struct Span(pub usize, pub usize, pub usize); - -impl Span { - /// Get the span that includes this and the other span completely. - /// Will panic if the spans are from different file_ids. - pub fn to(mut self, other: Span) -> Self { - assert!(other.2 == self.2); - self.1 = other.1; - self - } - - pub fn ending_at(mut self, end: usize) -> Self { - self.1 = end; - self - } - - pub fn with_length(mut self, end: usize) -> Self { - self.1 = self.0; - self - } -} - -impl Into for Span { - fn into(self) -> simplexpr::Span { - simplexpr::Span(self.0, self.1, self.2) - } -} -impl From for Span { - fn from(x: simplexpr::Span) -> Span { - Span(x.0, x.1, x.2) - } -} - -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) - } -} - #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum AstType { List, diff --git a/crates/yuck/src/parser/ast_iterator.rs b/crates/yuck/src/parser/ast_iterator.rs index 6f841d0..e8983c6 100644 --- a/crates/yuck/src/parser/ast_iterator.rs +++ b/crates/yuck/src/parser/ast_iterator.rs @@ -5,14 +5,14 @@ use std::collections::HashMap; use std::fmt::Display; use super::{ - ast::{Ast, AstType, Span}, + ast::{Ast, AstType}, from_ast::FromAst, }; use crate::{ config::attributes::{AttrEntry, Attributes}, error::{AstError, AstResult, OptionAstErrorExt}, - value::AttrName, }; +use eww_shared_util::{AttrName, Span, VarName}; pub struct AstIterator> { remaining_span: Span, @@ -54,7 +54,7 @@ impl> AstIterator { } pub fn expect_any(&mut self) -> AstResult { - self.iter.next().or_missing(self.remaining_span.with_length(0)).and_then(T::from_ast) + self.iter.next().or_missing(self.remaining_span.point_span()).and_then(T::from_ast) } pub fn expect_key_values(&mut self) -> AstResult { diff --git a/crates/yuck/src/parser/from_ast.rs b/crates/yuck/src/parser/from_ast.rs index bca20b4..06fe2df 100644 --- a/crates/yuck/src/parser/from_ast.rs +++ b/crates/yuck/src/parser/from_ast.rs @@ -1,8 +1,9 @@ use super::{ - ast::{Ast, AstType, Span}, + ast::{Ast, AstType}, ast_iterator::AstIterator, }; -use crate::{error::*, parser, util, value::AttrName}; +use crate::{error::*, parser, util}; +use eww_shared_util::{AttrName, Span, VarName}; use itertools::Itertools; use simplexpr::{ast::SimplExpr, dynval::DynVal}; use std::{ @@ -49,7 +50,7 @@ impl FromAst for T { impl FromAst for SimplExpr { fn from_ast(e: Ast) -> AstResult { match e { - Ast::Symbol(span, x) => Ok(SimplExpr::VarRef(span.into(), x)), + Ast::Symbol(span, x) => Ok(SimplExpr::VarRef(span.into(), VarName(x))), Ast::Literal(span, x) => Ok(SimplExpr::Literal(span.into(), x)), Ast::SimplExpr(span, x) => Ok(x), _ => Err(AstError::NotAValue(e.span(), e.expr_type())), diff --git a/crates/yuck/src/parser/lexer.rs b/crates/yuck/src/parser/lexer.rs index eb74f14..322abdf 100644 --- a/crates/yuck/src/parser/lexer.rs +++ b/crates/yuck/src/parser/lexer.rs @@ -1,6 +1,7 @@ use regex::{Regex, RegexSet}; -use super::{ast::Span, parse_error}; +use super::parse_error; +use eww_shared_util::{AttrName, Span, VarName}; #[derive(Debug, PartialEq, Eq, Clone)] pub enum Token { diff --git a/crates/yuck/src/parser/mod.rs b/crates/yuck/src/parser/mod.rs index c4654b2..497d428 100644 --- a/crates/yuck/src/parser/mod.rs +++ b/crates/yuck/src/parser/mod.rs @@ -25,6 +25,13 @@ pub fn parse_string(file_id: usize, s: &str) -> AstResult { parser.parse(file_id, lexer).map_err(|e| AstError::from_parse_error(file_id, e)) } +/// Parse multiple toplevel nodes into an [Ast::List] +pub fn parse_toplevel(file_id: usize, s: &str) -> AstResult { + let lexer = lexer::Lexer::new(file_id, s.to_string()); + let parser = parser::ToplevelParser::new(); + parser.parse(file_id, lexer).map(|(span, nodes)| Ast::List(span, nodes)).map_err(|e| AstError::from_parse_error(file_id, e)) +} + macro_rules! test_parser { ($($text:literal),*) => {{ let p = parser::AstParser::new(); diff --git a/crates/yuck/src/parser/parse_error.rs b/crates/yuck/src/parser/parse_error.rs index 9c2fe44..b5f0816 100644 --- a/crates/yuck/src/parser/parse_error.rs +++ b/crates/yuck/src/parser/parse_error.rs @@ -1,4 +1,4 @@ -use super::ast::Span; +use eww_shared_util::{AttrName, Span, VarName}; #[derive(Debug, thiserror::Error)] pub enum ParseError { diff --git a/crates/yuck/src/parser/parser.lalrpop b/crates/yuck/src/parser/parser.lalrpop index 110c749..a7d05d4 100644 --- a/crates/yuck/src/parser/parser.lalrpop +++ b/crates/yuck/src/parser/parser.lalrpop @@ -1,5 +1,6 @@ use std::str::FromStr; -use crate::parser::{lexer::{Token}, ast::{Ast, Span}, parse_error}; +use crate::parser::{lexer::Token, ast::Ast, parse_error}; +use eww_shared_util::Span; use simplexpr::ast::SimplExpr; use simplexpr; use lalrpop_util::ParseError; @@ -59,7 +60,7 @@ SimplExpr: SimplExpr = { =>? { let expr = x[1..x.len() - 1].to_string(); simplexpr::parse_string(file_id, &expr).map_err(|e| { - let span = e.get_span().map(|simplexpr::Span(simpl_l, simpl_r, file_id)| Span(1 + l + simpl_l, 1 + l + simpl_r, file_id)); + let span = e.get_span().map(|Span(simpl_l, simpl_r, file_id)| Span(1 + l + simpl_l, 1 + l + simpl_r, file_id)); ParseError::User { error: parse_error::ParseError::SimplExpr(span, e) }}) } } diff --git a/crates/yuck/src/value/mod.rs b/crates/yuck/src/value/mod.rs index b3e80a0..cc26f2d 100644 --- a/crates/yuck/src/value/mod.rs +++ b/crates/yuck/src/value/mod.rs @@ -3,39 +3,3 @@ use serde::{Deserialize, Serialize}; pub mod coords; pub use coords::*; - -/// The name of a variable -#[repr(transparent)] -#[derive(Clone, Hash, PartialEq, Eq, Serialize, Deserialize, AsRef, From, FromStr, Display, DebugCustom)] -#[debug(fmt = "VarName({})", .0)] -pub struct VarName(pub String); - -impl std::borrow::Borrow for VarName { - fn borrow(&self) -> &str { - &self.0 - } -} - -impl From<&str> for VarName { - fn from(s: &str) -> Self { - VarName(s.to_owned()) - } -} - -/// The name of an attribute -#[repr(transparent)] -#[derive(Clone, Hash, PartialEq, Eq, Serialize, Deserialize, AsRef, From, FromStr, Display, DebugCustom)] -#[debug(fmt="AttrName({})", .0)] -pub struct AttrName(pub String); - -impl std::borrow::Borrow for AttrName { - fn borrow(&self) -> &str { - &self.0 - } -} - -impl From<&str> for AttrName { - fn from(s: &str) -> Self { - AttrName(s.to_owned()) - } -} From 618e652389ef80cbe1379c6bbaa604942e777834 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Thu, 22 Jul 2021 21:06:22 +0200 Subject: [PATCH 064/137] Implement YuckFiles for include handling --- Cargo.lock | 1 + crates/eww/Cargo.toml | 2 + crates/eww/src/app.rs | 19 +++++- crates/eww/src/config/eww_config.rs | 64 +++---------------- crates/eww/src/main.rs | 11 ++-- crates/eww/src/server.rs | 6 +- crates/yuck/src/config/config.rs | 82 +++++++++++++++++++------ crates/yuck/src/config/file_provider.rs | 69 +++++++++++++++++++++ crates/yuck/src/config/mod.rs | 1 + crates/yuck/src/config/test.rs | 9 +-- crates/yuck/src/error.rs | 6 +- crates/yuck/src/format_diagnostic.rs | 9 +++ crates/yuck/src/parser/mod.rs | 9 +-- 13 files changed, 193 insertions(+), 95 deletions(-) create mode 100644 crates/yuck/src/config/file_provider.rs diff --git a/Cargo.lock b/Cargo.lock index 6a62da4..33f838a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -433,6 +433,7 @@ dependencies = [ "base64", "bincode", "cairo-sys-rs", + "codespan-reporting", "debug_stub_derive", "derive_more", "dyn-clone", diff --git a/crates/eww/Cargo.toml b/crates/eww/Cargo.toml index 2d77226..42dee41 100644 --- a/crates/eww/Cargo.toml +++ b/crates/eww/Cargo.toml @@ -67,6 +67,8 @@ wait-timeout = "0.2" notify = "5.0.0-pre.7" +codespan-reporting = "0.11" + simplexpr = { path = "../simplexpr" } yuck = { path = "../yuck" } eww_shared_util = { path = "../eww_shared_util" } diff --git a/crates/eww/src/app.rs b/crates/eww/src/app.rs index 18b2cd9..73dfbbc 100644 --- a/crates/eww/src/app.rs +++ b/crates/eww/src/app.rs @@ -14,7 +14,10 @@ use simplexpr::dynval::DynVal; use std::collections::HashMap; use tokio::sync::mpsc::UnboundedSender; use yuck::{ - config::window_geometry::{AnchorPoint, WindowGeometry}, + config::{ + file_provider::FsYuckFiles, + window_geometry::{AnchorPoint, WindowGeometry}, + }, value::Coords, }; @@ -115,10 +118,13 @@ impl App { DaemonCommand::ReloadConfigAndCss(sender) => { let mut errors = Vec::new(); - let config_result = EwwConfig::read_from_file(&self.paths.get_yuck_path()); + let mut yuck_files = FsYuckFiles::new(); + let config_result = EwwConfig::read_from_file(&mut yuck_files, &self.paths.get_yuck_path()); match config_result { Ok(new_config) => self.handle_command(DaemonCommand::UpdateConfig(new_config)), - Err(e) => errors.push(e), + Err(e) => { + errors.push(e); + } } let css_result = crate::util::parse_scss_from_file(&self.paths.get_eww_scss_path()); @@ -193,6 +199,13 @@ impl App { } }; + // if let Err(err) = result { + // if let Some(ast_error) = err.root_cause().downcast_ref::() { + // println!("ast error: {:?}", ast_error); + //} else { + // dbg!(err.root_cause()); + //} + crate::print_result_err!("while handling event", &result); } diff --git a/crates/eww/src/config/eww_config.rs b/crates/eww/src/config/eww_config.rs index 1fafdcd..1e18eb3 100644 --- a/crates/eww/src/config/eww_config.rs +++ b/crates/eww/src/config/eww_config.rs @@ -1,9 +1,8 @@ use anyhow::*; use eww_shared_util::VarName; use std::collections::HashMap; -use yuck::{ - config::{script_var_definition::ScriptVarDefinition, widget_definition::WidgetDefinition}, - parser::from_ast::FromAst, +use yuck::config::{ + file_provider::FsYuckFiles, script_var_definition::ScriptVarDefinition, widget_definition::WidgetDefinition, Config, }; use simplexpr::dynval::DynVal; @@ -17,30 +16,21 @@ pub struct EwwConfig { windows: HashMap, initial_variables: HashMap, script_vars: HashMap, + // files: FsYuckFiles, } impl EwwConfig { - pub fn read_from_file>(path: P) -> Result { - let content = std::fs::read_to_string(path)?; - let ast = yuck::parser::parse_toplevel(0, &content)?; - let config = yuck::config::Config::from_ast(ast)?; - Self::generate(config) - } - - pub fn generate(config: yuck::config::Config) -> Result { - let yuck::config::Config { widget_definitions, window_definitions, var_definitions, script_vars } = config; + pub fn read_from_file>(files: &mut FsYuckFiles, path: P) -> Result { + let config = Config::generate_from_main_file(files, path.as_ref().to_str().context("Failed to decode path")?)?; + let Config { widget_definitions, window_definitions, var_definitions, script_vars } = config; Ok(EwwConfig { windows: window_definitions .into_iter() - .map(|(name, window)| { - Ok(( - name, - EwwWindowDefinition::generate(&widget_definitions, window).context("Failed expand window definition")?, - )) - }) + .map(|(name, window)| Ok((name, EwwWindowDefinition::generate(&widget_definitions, window)?))) .collect::>>()?, widgets: widget_definitions, initial_variables: var_definitions.into_iter().map(|(k, v)| (k, v.initial_value)).collect(), script_vars, + // files, }) } @@ -71,41 +61,3 @@ impl EwwConfig { &self.widgets } } - -// Raw Eww configuration, before expanding widget usages. -//#[derive(Debug, Clone)] -// pub struct RawEwwConfig { -// widgets: HashMap, -// windows: HashMap, -// initial_variables: HashMap, -// script_vars: HashMap, -// pub filepath: PathBuf, -//} - -// impl RawEwwConfig { -// pub fn merge_includes(mut eww_config: RawEwwConfig, includes: Vec) -> Result { -// let config_path = eww_config.filepath.clone(); -// let log_conflict = |what: &str, conflict: &str, included_path: &std::path::PathBuf| { -// log::error!( -//"{} '{}' defined twice (defined in {} and in {})", -// what, -// conflict, -// config_path.display(), -// included_path.display() -//); -//}; -// for included_config in includes { -// for conflict in util::extend_safe(&mut eww_config.widgets, included_config.widgets) { -// log_conflict("widget", &conflict, &included_config.filepath) -//} -// for conflict in util::extend_safe(&mut eww_config.windows, included_config.windows) { -// log_conflict("window", &conflict.to_string(), &included_config.filepath) -//} -// for conflict in util::extend_safe(&mut eww_config.script_vars, included_config.script_vars) { -// log_conflict("script-var", &conflict.to_string(), &included_config.filepath) -//} -// for conflict in util::extend_safe(&mut eww_config.initial_variables, included_config.initial_variables) { -// log_conflict("var", &conflict.to_string(), &included_config.filepath) -//} -// Ok(eww_config) -//} diff --git a/crates/eww/src/main.rs b/crates/eww/src/main.rs index 527e222..d75f694 100644 --- a/crates/eww/src/main.rs +++ b/crates/eww/src/main.rs @@ -105,7 +105,7 @@ fn check_server_running(socket_path: impl AsRef) -> bool { response.is_some() } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone)] pub struct EwwPaths { log_file: PathBuf, ipc_socket_file: PathBuf, @@ -115,11 +115,9 @@ pub struct EwwPaths { impl EwwPaths { pub fn from_config_dir>(config_dir: P) -> Result { let config_dir = config_dir.as_ref(); - let config_dir = if config_dir.is_file() { - config_dir.parent().context("Given config file did not have a parent directory")? - } else { - config_dir - }; + if config_dir.is_file() { + bail!("Please provide the path to the config directory, not a file within it") + } if !config_dir.exists() { bail!("Configuration directory {} does not exist", config_dir.display()); @@ -166,6 +164,7 @@ impl EwwPaths { self.config_dir.join("eww.yuck") } + pub fn get_eww_scss_path(&self) -> PathBuf { self.config_dir.join("eww.scss") } diff --git a/crates/eww/src/server.rs b/crates/eww/src/server.rs index f2c8127..77a6dc5 100644 --- a/crates/eww/src/server.rs +++ b/crates/eww/src/server.rs @@ -1,5 +1,6 @@ use crate::{app, config, eww_state::*, ipc_server, script_var_handler, util, EwwPaths}; use anyhow::*; +use yuck::config::file_provider::FsYuckFiles; use std::{collections::HashMap, os::unix::io::AsRawFd, path::Path}; use tokio::sync::mpsc::*; @@ -27,7 +28,10 @@ pub fn initialize_server(paths: EwwPaths) -> Result<()> { .with_context(|| format!("Failed to change working directory to {}", paths.get_config_dir().display()))?; log::info!("Loading paths: {}", &paths); - let eww_config = config::EwwConfig::read_from_file(&paths.get_yuck_path())?; + + let mut yuck_files = FsYuckFiles::new(); + + let eww_config = config::EwwConfig::read_from_file(&mut yuck_files, &paths.get_yuck_path())?; gtk::init()?; diff --git a/crates/yuck/src/config/config.rs b/crates/yuck/src/config/config.rs index e893876..bb5bce9 100644 --- a/crates/yuck/src/config/config.rs +++ b/crates/yuck/src/config/config.rs @@ -1,10 +1,15 @@ use std::collections::HashMap; +use codespan_reporting::files::SimpleFiles; use simplexpr::SimplExpr; use super::{ - script_var_definition::ScriptVarDefinition, var_definition::VarDefinition, widget_definition::WidgetDefinition, - widget_use::WidgetUse, window_definition::WindowDefinition, + file_provider::{FilesError, YuckFiles}, + script_var_definition::ScriptVarDefinition, + var_definition::VarDefinition, + widget_definition::WidgetDefinition, + widget_use::WidgetUse, + window_definition::WindowDefinition, }; use crate::{ config::script_var_definition::{PollScriptVar, TailScriptVar}, @@ -17,7 +22,25 @@ use crate::{ }; use eww_shared_util::{AttrName, Span, VarName}; +#[derive(Debug, PartialEq, Eq, Clone, serde::Serialize)] +pub struct Include { + pub path: String, + pub path_span: Span, +} + +impl FromAstElementContent for Include { + fn get_element_name() -> &'static str { + "include" + } + + fn from_tail>(span: Span, mut iter: AstIterator) -> AstResult { + let (path_span, path) = iter.expect_literal()?; + Ok(Include { path: path.to_string(), path_span }) + } +} + pub enum TopLevel { + Include(Include), VarDefinition(VarDefinition), ScriptVarDefinition(ScriptVarDefinition), WidgetDefinition(WidgetDefinition), @@ -30,6 +53,7 @@ impl FromAst for TopLevel { let mut iter = e.try_ast_iter()?; let (sym_span, element_name) = iter.expect_symbol()?; Ok(match element_name.as_str() { + x if x == Include::get_element_name() => Self::Include(Include::from_tail(span, iter)?), x if x == WidgetDefinition::get_element_name() => Self::WidgetDefinition(WidgetDefinition::from_tail(span, iter)?), x if x == VarDefinition::get_element_name() => Self::VarDefinition(VarDefinition::from_tail(span, iter)?), x if x == PollScriptVar::get_element_name() => { @@ -52,31 +76,49 @@ pub struct Config { pub script_vars: HashMap, } -impl FromAst for Config { - fn from_ast(e: Ast) -> AstResult { - let list = e.as_list()?; +impl Config { + fn append_toplevel(&mut self, files: &mut impl YuckFiles, toplevel: TopLevel) -> AstResult<()> { + match toplevel { + TopLevel::VarDefinition(x) => { + self.var_definitions.insert(x.name.clone(), x); + } + TopLevel::ScriptVarDefinition(x) => { + self.script_vars.insert(x.name().clone(), x); + } + TopLevel::WidgetDefinition(x) => { + self.widget_definitions.insert(x.name.clone(), x); + } + TopLevel::WindowDefinition(x) => { + self.window_definitions.insert(x.name.clone(), x); + } + TopLevel::Include(include) => { + let (file_id, toplevels) = files.load(&include.path).map_err(|err| match err { + FilesError::IoError(_) => AstError::IncludedFileNotFound(include), + FilesError::AstError(x) => x, + })?; + for element in toplevels { + self.append_toplevel(files, TopLevel::from_ast(element)?)?; + } + } + } + Ok(()) + } + + pub fn generate(files: &mut impl YuckFiles, elements: Vec) -> AstResult { let mut config = Self { widget_definitions: HashMap::new(), window_definitions: HashMap::new(), var_definitions: HashMap::new(), script_vars: HashMap::new(), }; - for element in list { - match TopLevel::from_ast(element)? { - TopLevel::VarDefinition(x) => { - config.var_definitions.insert(x.name.clone(), x); - } - TopLevel::ScriptVarDefinition(x) => { - config.script_vars.insert(x.name().clone(), x); - } - TopLevel::WidgetDefinition(x) => { - config.widget_definitions.insert(x.name.clone(), x); - } - TopLevel::WindowDefinition(x) => { - config.window_definitions.insert(x.name.clone(), x); - } - } + for element in elements { + config.append_toplevel(files, TopLevel::from_ast(element)?)?; } Ok(config) } + + pub fn generate_from_main_file(files: &mut impl YuckFiles, path: &str) -> AstResult { + let (span, top_levels) = files.load(path).map_err(|err| AstError::Other(None, Box::new(err)))?; + Self::generate(files, top_levels) + } } diff --git a/crates/yuck/src/config/file_provider.rs b/crates/yuck/src/config/file_provider.rs new file mode 100644 index 0000000..cfc5588 --- /dev/null +++ b/crates/yuck/src/config/file_provider.rs @@ -0,0 +1,69 @@ +use std::{collections::HashMap, path::PathBuf}; + +use codespan_reporting::files::{Files, SimpleFile, SimpleFiles}; +use eww_shared_util::Span; + +use crate::{ + error::{AstError, AstResult}, + parser::ast::Ast, +}; + +#[derive(thiserror::Error, Debug)] +pub enum FilesError { + #[error(transparent)] + IoError(#[from] std::io::Error), + + #[error(transparent)] + AstError(#[from] AstError), +} + +pub trait YuckFiles { + fn load(&mut self, path: &str) -> Result<(Span, Vec), FilesError>; +} + +#[derive(Debug, Clone)] +pub struct FsYuckFiles { + files: SimpleFiles, +} + +impl FsYuckFiles { + pub fn new() -> Self { + Self { files: SimpleFiles::new() } + } +} + +impl YuckFiles for FsYuckFiles { + fn load(&mut self, path: &str) -> Result<(Span, Vec), FilesError> { + let path = PathBuf::from(path); + + let file_content = std::fs::read_to_string(&path)?; + let file_id = self.files.add(path.display().to_string(), file_content.to_string()); + Ok(crate::parser::parse_toplevel(file_id, file_content)?) + } +} + +impl<'a> Files<'a> for FsYuckFiles { + type FileId = usize; + type Name = String; + type Source = &'a str; + + fn name(&self, id: Self::FileId) -> Result { + self.files.name(id) + } + + fn source(&'a self, id: Self::FileId) -> Result { + self.files.source(id) + } + + fn line_index(&self, id: Self::FileId, byte_index: usize) -> Result { + self.files.line_index(id, byte_index) + } + + fn line_range( + &self, + id: Self::FileId, + line_index: usize, + ) -> Result, codespan_reporting::files::Error> { + self.files.line_range(id, line_index) + } +} diff --git a/crates/yuck/src/config/mod.rs b/crates/yuck/src/config/mod.rs index 3338d5d..8275305 100644 --- a/crates/yuck/src/config/mod.rs +++ b/crates/yuck/src/config/mod.rs @@ -2,6 +2,7 @@ pub mod attributes; pub mod backend_window_options; pub mod config; pub mod config_parse_error; +pub mod file_provider; pub mod script_var_definition; #[cfg(test)] mod test; diff --git a/crates/yuck/src/config/test.rs b/crates/yuck/src/config/test.rs index 677f4b3..fcf0963 100644 --- a/crates/yuck/src/config/test.rs +++ b/crates/yuck/src/config/test.rs @@ -25,8 +25,9 @@ fn test_config() { let lexer = Lexer::new(0, input.to_string()); let p = parser::parser::ToplevelParser::new(); let (span, parse_result) = p.parse(0, lexer).unwrap(); - let config = Config::from_ast(Ast::List(span, parse_result)); - insta::with_settings!({sort_maps => true}, { - insta::assert_ron_snapshot!(config.unwrap()); - }); + // TODO implement another YuckFiles thing to test here again + // let config = Config::from_ast(Ast::List(span, parse_result)); + // insta::with_settings!({sort_maps => true}, { + // insta::assert_ron_snapshot!(config.unwrap()); + //}); } diff --git a/crates/yuck/src/error.rs b/crates/yuck/src/error.rs index f973ebd..2c4662b 100644 --- a/crates/yuck/src/error.rs +++ b/crates/yuck/src/error.rs @@ -1,5 +1,5 @@ use crate::{ - config::{attributes::AttrError, validate::ValidationError}, + config::{attributes::AttrError, config::Include, validate::ValidationError}, parser::{ ast::{Ast, AstType}, lexer, parse_error, @@ -25,6 +25,9 @@ pub enum AstError { #[error("Expected element {1}, but read {2}")] MismatchedElementName(Span, String, String), + #[error("Included file not found {}", .0.path)] + IncludedFileNotFound(Include), + #[error(transparent)] ConversionError(#[from] dynval::ConversionError), @@ -56,6 +59,7 @@ impl AstError { AstError::AttrError(err) => Some(err.span()), AstError::Other(span, ..) => *span, AstError::ConversionError(err) => err.value.span().map(|x| x.into()), + AstError::IncludedFileNotFound(include) => Some(include.path_span), AstError::ValidationError(error) => None, // TODO none here is stupid AstError::ParseError { file_id, source } => file_id.and_then(|id| get_parse_error_span(id, source)), } diff --git a/crates/yuck/src/format_diagnostic.rs b/crates/yuck/src/format_diagnostic.rs index c724f74..f244344 100644 --- a/crates/yuck/src/format_diagnostic.rs +++ b/crates/yuck/src/format_diagnostic.rs @@ -8,6 +8,10 @@ use crate::error::AstError; use super::parser::parse_error; use eww_shared_util::{AttrName, Span, VarName}; +fn span_to_secondary_label(span: Span) -> Label { + Label::secondary(span.2, span.0..span.1) +} + macro_rules! gen_diagnostic { ( $(msg = $msg:expr)? @@ -82,6 +86,11 @@ impl ToDiagnostic for AstError { AstError::ConversionError(err) => conversion_error_to_diagnostic(err, span), AstError::Other(_, source) => gen_diagnostic!(source, span), AstError::AttrError(source) => gen_diagnostic!(source, span), + AstError::IncludedFileNotFound(include) => gen_diagnostic!( + msg = format!("Included file `{}` not found", include.path), + label = include.path_span => "Included here", + ), + AstError::ValidationError(_) => todo!(), } } else { diff --git a/crates/yuck/src/parser/mod.rs b/crates/yuck/src/parser/mod.rs index 497d428..8e5499f 100644 --- a/crates/yuck/src/parser/mod.rs +++ b/crates/yuck/src/parser/mod.rs @@ -1,3 +1,4 @@ +use eww_shared_util::Span; use lalrpop_util::lalrpop_mod; use super::error::{AstError, AstResult}; @@ -25,11 +26,11 @@ pub fn parse_string(file_id: usize, s: &str) -> AstResult { parser.parse(file_id, lexer).map_err(|e| AstError::from_parse_error(file_id, e)) } -/// Parse multiple toplevel nodes into an [Ast::List] -pub fn parse_toplevel(file_id: usize, s: &str) -> AstResult { - let lexer = lexer::Lexer::new(file_id, s.to_string()); +/// Parse multiple toplevel nodes into a list of [Ast] +pub fn parse_toplevel(file_id: usize, s: String) -> AstResult<(Span, Vec)> { + let lexer = lexer::Lexer::new(file_id, s); let parser = parser::ToplevelParser::new(); - parser.parse(file_id, lexer).map(|(span, nodes)| Ast::List(span, nodes)).map_err(|e| AstError::from_parse_error(file_id, e)) + parser.parse(file_id, lexer).map_err(|e| AstError::from_parse_error(file_id, e)) } macro_rules! test_parser { From a8b256987ce3199605031e06b62366b1cf912ea2 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Thu, 22 Jul 2021 22:13:43 +0200 Subject: [PATCH 065/137] Simple implementation of error handling using globally stored files --- crates/eww/src/app.rs | 13 +++++--- crates/eww/src/error_handling_ctx.rs | 50 ++++++++++++++++++++++++++++ crates/eww/src/main.rs | 5 +-- crates/eww/src/server.rs | 7 ++-- 4 files changed, 65 insertions(+), 10 deletions(-) create mode 100644 crates/eww/src/error_handling_ctx.rs diff --git a/crates/eww/src/app.rs b/crates/eww/src/app.rs index 73dfbbc..e8e0fe7 100644 --- a/crates/eww/src/app.rs +++ b/crates/eww/src/app.rs @@ -1,6 +1,6 @@ use crate::{ config::{self, EwwConfig}, - display_backend, eww_state, + display_backend, error_handling_ctx, eww_state, script_var_handler::*, EwwPaths, }; @@ -118,8 +118,11 @@ impl App { DaemonCommand::ReloadConfigAndCss(sender) => { let mut errors = Vec::new(); - let mut yuck_files = FsYuckFiles::new(); - let config_result = EwwConfig::read_from_file(&mut yuck_files, &self.paths.get_yuck_path()); + error_handling_ctx::clear_files(); + let config_result = config::EwwConfig::read_from_file( + &mut error_handling_ctx::ERROR_HANDLING_CTX.lock().unwrap(), + &self.paths.get_yuck_path(), + ); match config_result { Ok(new_config) => self.handle_command(DaemonCommand::UpdateConfig(new_config)), Err(e) => { @@ -133,7 +136,7 @@ impl App { Err(e) => errors.push(e), } - let errors = errors.into_iter().map(|e| format!("{:?}", e)).join("\n"); + let errors = errors.into_iter().map(|e| error_handling_ctx::format_error(e)).join("\n"); if errors.is_empty() { sender.send(DaemonResponse::Success(String::new()))?; } else { @@ -394,7 +397,7 @@ fn get_monitor_geometry(n: i32) -> gdk::Rectangle { fn respond_with_error(sender: DaemonResponseSender, result: Result) -> Result<()> { match result { Ok(_) => sender.send(DaemonResponse::Success(String::new())), - Err(e) => sender.send(DaemonResponse::Failure(format!("{:?}", e))), + Err(e) => sender.send(DaemonResponse::Failure(error_handling_ctx::format_error(e))), } .context("sending response from main thread") } diff --git a/crates/eww/src/error_handling_ctx.rs b/crates/eww/src/error_handling_ctx.rs new file mode 100644 index 0000000..4a8999a --- /dev/null +++ b/crates/eww/src/error_handling_ctx.rs @@ -0,0 +1,50 @@ +use std::sync::{Arc, Mutex}; + +use yuck::{config::file_provider::FsYuckFiles, error::AstError, format_diagnostic::ToDiagnostic}; + +lazy_static::lazy_static! { + pub static ref ERROR_HANDLING_CTX: Arc> = Arc::new(Mutex::new(FsYuckFiles::new())); +} + +pub fn clear_files() { + *ERROR_HANDLING_CTX.lock().unwrap() = FsYuckFiles::new(); +} + +pub fn print_error(err: anyhow::Error) { + match err.downcast_ref::() { + Some(err) => { + print_ast_error(err); + } + None => { + log::error!("{:?}", err); + } + } +} + +pub fn print_ast_error(err: &AstError) { + let diag = err.to_diagnostic(); + use codespan_reporting::term; + let config = term::Config::default(); + let mut writer = term::termcolor::StandardStream::stderr(term::termcolor::ColorChoice::Always); + let files = ERROR_HANDLING_CTX.lock().unwrap(); + term::emit(&mut writer, &config, &*files, &diag).unwrap(); +} + +pub fn format_error(err: anyhow::Error) -> String { + match err.downcast_ref::() { + Some(err) => format_ast_error(err), + None => format!("{:?}", err), + } +} + +pub fn format_ast_error(err: &AstError) -> String { + let diag = err.to_diagnostic(); + use codespan_reporting::term; + let config = term::Config::default(); + // let mut writer = term::termcolor::StandardStream::stderr(term::termcolor::ColorChoice::Always); + let mut buf = Vec::new(); + let mut writer = term::termcolor::Ansi::new(&mut buf); + let files = ERROR_HANDLING_CTX.lock().unwrap(); + term::emit(&mut writer, &config, &*files, &diag).unwrap(); + String::from_utf8(buf).unwrap() +} diff --git a/crates/eww/src/main.rs b/crates/eww/src/main.rs index d75f694..fde5ad8 100644 --- a/crates/eww/src/main.rs +++ b/crates/eww/src/main.rs @@ -17,11 +17,13 @@ use std::{ path::{Path, PathBuf}, }; + pub mod app; pub mod application_lifecycle; pub mod client; pub mod config; pub mod display_backend; +mod error_handling_ctx; pub mod eww_state; pub mod geometry; pub mod ipc_server; @@ -92,7 +94,7 @@ fn main() { }; if let Err(e) = result { - log::error!("{:?}", e); + error_handling_ctx::print_error(e); std::process::exit(1); } } @@ -164,7 +166,6 @@ impl EwwPaths { self.config_dir.join("eww.yuck") } - pub fn get_eww_scss_path(&self) -> PathBuf { self.config_dir.join("eww.scss") } diff --git a/crates/eww/src/server.rs b/crates/eww/src/server.rs index 77a6dc5..fe4802c 100644 --- a/crates/eww/src/server.rs +++ b/crates/eww/src/server.rs @@ -1,4 +1,4 @@ -use crate::{app, config, eww_state::*, ipc_server, script_var_handler, util, EwwPaths}; +use crate::{EwwPaths, app, config, error_handling_ctx, eww_state::*, ipc_server, script_var_handler, util}; use anyhow::*; use yuck::config::file_provider::FsYuckFiles; use std::{collections::HashMap, os::unix::io::AsRawFd, path::Path}; @@ -29,9 +29,10 @@ pub fn initialize_server(paths: EwwPaths) -> Result<()> { log::info!("Loading paths: {}", &paths); - let mut yuck_files = FsYuckFiles::new(); - let eww_config = config::EwwConfig::read_from_file(&mut yuck_files, &paths.get_yuck_path())?; + error_handling_ctx::clear_files(); + + let eww_config = config::EwwConfig::read_from_file(&mut error_handling_ctx::ERROR_HANDLING_CTX.lock().unwrap(), &paths.get_yuck_path())?; gtk::init()?; From 9f70a22cf0a9d6fe79a7ebc8b30252a13d710446 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Fri, 23 Jul 2021 13:45:30 +0200 Subject: [PATCH 066/137] significantly better error message handling everywhere --- crates/eww/src/app.rs | 45 ++++++---------- crates/eww/src/error_handling_ctx.rs | 43 ++++++++-------- crates/eww/src/eww_state.rs | 10 +++- crates/eww/src/main.rs | 3 +- crates/eww/src/server.rs | 54 ++++++++++++-------- crates/eww/src/util.rs | 15 ------ crates/eww/src/widgets/mod.rs | 17 ++---- crates/eww/src/widgets/widget_definitions.rs | 20 ++++---- crates/eww/src/widgets/widget_node.rs | 33 ++++++++---- crates/simplexpr/src/dynval.rs | 8 ++- crates/simplexpr/src/lib.rs | 4 +- crates/simplexpr/src/parser/lexer.rs | 10 ++-- crates/simplexpr/src/parser/mod.rs | 6 +-- crates/yuck/src/config/config.rs | 5 +- crates/yuck/src/config/validate.rs | 4 +- crates/yuck/src/config/widget_use.rs | 14 ++--- crates/yuck/src/error.rs | 14 ----- crates/yuck/src/format_diagnostic.rs | 34 +++++++++--- crates/yuck/src/parser/ast.rs | 20 ++------ crates/yuck/src/parser/lexer.rs | 4 +- crates/yuck/src/parser/parser.lalrpop | 5 +- 21 files changed, 181 insertions(+), 187 deletions(-) diff --git a/crates/eww/src/app.rs b/crates/eww/src/app.rs index e8e0fe7..7db2642 100644 --- a/crates/eww/src/app.rs +++ b/crates/eww/src/app.rs @@ -1,9 +1,4 @@ -use crate::{ - config::{self, EwwConfig}, - display_backend, error_handling_ctx, eww_state, - script_var_handler::*, - EwwPaths, -}; +use crate::{config, display_backend, error_handling_ctx, eww_state, script_var_handler::*, EwwPaths}; use anyhow::*; use debug_stub_derive::*; use eww_shared_util::VarName; @@ -14,10 +9,7 @@ use simplexpr::dynval::DynVal; use std::collections::HashMap; use tokio::sync::mpsc::UnboundedSender; use yuck::{ - config::{ - file_provider::FsYuckFiles, - window_geometry::{AnchorPoint, WindowGeometry}, - }, + config::window_geometry::{AnchorPoint, WindowGeometry}, value::Coords, }; @@ -123,20 +115,18 @@ impl App { &mut error_handling_ctx::ERROR_HANDLING_CTX.lock().unwrap(), &self.paths.get_yuck_path(), ); - match config_result { - Ok(new_config) => self.handle_command(DaemonCommand::UpdateConfig(new_config)), - Err(e) => { - errors.push(e); - } - } - - let css_result = crate::util::parse_scss_from_file(&self.paths.get_eww_scss_path()); - match css_result { - Ok(new_css) => self.handle_command(DaemonCommand::UpdateCss(new_css)), + match config_result.and_then(|new_config| self.load_config(new_config)) { + Ok(()) => {} Err(e) => errors.push(e), } - let errors = errors.into_iter().map(|e| error_handling_ctx::format_error(e)).join("\n"); + let css_result = crate::util::parse_scss_from_file(&self.paths.get_eww_scss_path()); + match css_result.and_then(|css| self.load_css(&css)) { + Ok(()) => {} + Err(e) => errors.push(e), + } + + let errors = errors.into_iter().map(|e| error_handling_ctx::format_error(&e)).join("\n"); if errors.is_empty() { sender.send(DaemonResponse::Success(String::new()))?; } else { @@ -202,14 +192,9 @@ impl App { } }; - // if let Err(err) = result { - // if let Some(ast_error) = err.root_cause().downcast_ref::() { - // println!("ast error: {:?}", ast_error); - //} else { - // dbg!(err.root_cause()); - //} - - crate::print_result_err!("while handling event", &result); + if let Err(err) = result { + error_handling_ctx::print_error(&err); + } } fn stop_application(&mut self) { @@ -397,7 +382,7 @@ fn get_monitor_geometry(n: i32) -> gdk::Rectangle { fn respond_with_error(sender: DaemonResponseSender, result: Result) -> Result<()> { match result { Ok(_) => sender.send(DaemonResponse::Success(String::new())), - Err(e) => sender.send(DaemonResponse::Failure(error_handling_ctx::format_error(e))), + Err(e) => sender.send(DaemonResponse::Failure(error_handling_ctx::format_error(&e))), } .context("sending response from main thread") } diff --git a/crates/eww/src/error_handling_ctx.rs b/crates/eww/src/error_handling_ctx.rs index 4a8999a..04ce248 100644 --- a/crates/eww/src/error_handling_ctx.rs +++ b/crates/eww/src/error_handling_ctx.rs @@ -1,6 +1,13 @@ use std::sync::{Arc, Mutex}; -use yuck::{config::file_provider::FsYuckFiles, error::AstError, format_diagnostic::ToDiagnostic}; +use codespan_reporting::diagnostic::Diagnostic; +use eww_shared_util::DUMMY_SPAN; +use simplexpr::eval::EvalError; +use yuck::{ + config::file_provider::FsYuckFiles, + error::AstError, + format_diagnostic::{eval_error_to_diagnostic, ToDiagnostic}, +}; lazy_static::lazy_static! { pub static ref ERROR_HANDLING_CTX: Arc> = Arc::new(Mutex::new(FsYuckFiles::new())); @@ -10,41 +17,35 @@ pub fn clear_files() { *ERROR_HANDLING_CTX.lock().unwrap() = FsYuckFiles::new(); } -pub fn print_error(err: anyhow::Error) { +pub fn print_error(err: &anyhow::Error) { match err.downcast_ref::() { Some(err) => { - print_ast_error(err); - } - None => { - log::error!("{:?}", err); + eprintln!("{:?}\n{}", err, stringify_diagnostic(err.to_diagnostic())); } + None => match err.downcast_ref::() { + Some(err) => { + eprintln!("{:?}\n{}", err, stringify_diagnostic(eval_error_to_diagnostic(err, err.span().unwrap_or(DUMMY_SPAN)))); + } + None => { + log::error!("{:?}", err); + } + }, } } -pub fn print_ast_error(err: &AstError) { - let diag = err.to_diagnostic(); - use codespan_reporting::term; - let config = term::Config::default(); - let mut writer = term::termcolor::StandardStream::stderr(term::termcolor::ColorChoice::Always); - let files = ERROR_HANDLING_CTX.lock().unwrap(); - term::emit(&mut writer, &config, &*files, &diag).unwrap(); -} - -pub fn format_error(err: anyhow::Error) -> String { +pub fn format_error(err: &anyhow::Error) -> String { match err.downcast_ref::() { - Some(err) => format_ast_error(err), + Some(err) => stringify_diagnostic(err.to_diagnostic()), None => format!("{:?}", err), } } -pub fn format_ast_error(err: &AstError) -> String { - let diag = err.to_diagnostic(); +pub fn stringify_diagnostic(diagnostic: Diagnostic) -> String { use codespan_reporting::term; let config = term::Config::default(); - // let mut writer = term::termcolor::StandardStream::stderr(term::termcolor::ColorChoice::Always); let mut buf = Vec::new(); let mut writer = term::termcolor::Ansi::new(&mut buf); let files = ERROR_HANDLING_CTX.lock().unwrap(); - term::emit(&mut writer, &config, &*files, &diag).unwrap(); + term::emit(&mut writer, &config, &*files, &diagnostic).unwrap(); String::from_utf8(buf).unwrap() } diff --git a/crates/eww/src/eww_state.rs b/crates/eww/src/eww_state.rs index 92aeeee..25c6640 100644 --- a/crates/eww/src/eww_state.rs +++ b/crates/eww/src/eww_state.rs @@ -4,6 +4,8 @@ use std::{collections::HashMap, sync::Arc}; use simplexpr::{dynval::DynVal, SimplExpr}; +use crate::error_handling_ctx; + /// Handler that gets executed to apply the necessary parts of the eww state to /// a gtk widget. These are created and initialized in EwwState::resolve. pub struct StateChangeHandler { @@ -28,9 +30,13 @@ impl StateChangeHandler { match resolved_attrs { Ok(resolved_attrs) => { - crate::print_result_err!("while updating UI based after state change", &(self.func)(resolved_attrs)) + if let Err(err) = &(self.func)(resolved_attrs).context("Error while updating UI after state change") { + error_handling_ctx::print_error(&err); + } + } + Err(err) => { + error_handling_ctx::print_error(&err); } - Err(err) => log::error!("Error while resolving attributes: {:?}", err), } } } diff --git a/crates/eww/src/main.rs b/crates/eww/src/main.rs index fde5ad8..9cccbaf 100644 --- a/crates/eww/src/main.rs +++ b/crates/eww/src/main.rs @@ -38,7 +38,6 @@ fn main() { let log_level_filter = if opts.log_debug { log::LevelFilter::Debug } else { log::LevelFilter::Info }; if std::env::var("RUST_LOG").is_ok() { - println!("hey"); pretty_env_logger::init_timed(); } else { pretty_env_logger::formatted_timed_builder().filter(Some("eww"), log_level_filter).init(); @@ -94,7 +93,7 @@ fn main() { }; if let Err(e) = result { - error_handling_ctx::print_error(e); + error_handling_ctx::print_error(&e); std::process::exit(1); } } diff --git a/crates/eww/src/server.rs b/crates/eww/src/server.rs index fe4802c..20f9b77 100644 --- a/crates/eww/src/server.rs +++ b/crates/eww/src/server.rs @@ -1,7 +1,12 @@ -use crate::{EwwPaths, app, config, error_handling_ctx, eww_state::*, ipc_server, script_var_handler, util}; +use crate::{app, config, error_handling_ctx, eww_state::*, ipc_server, script_var_handler, util, EwwPaths}; use anyhow::*; -use yuck::config::file_provider::FsYuckFiles; -use std::{collections::HashMap, os::unix::io::AsRawFd, path::Path}; + +use std::{ + collections::HashMap, + os::unix::io::AsRawFd, + path::Path, + sync::{atomic::Ordering, Arc}, +}; use tokio::sync::mpsc::*; pub fn initialize_server(paths: EwwPaths) -> Result<()> { @@ -29,10 +34,10 @@ pub fn initialize_server(paths: EwwPaths) -> Result<()> { log::info!("Loading paths: {}", &paths); - + // disgusting global state, I hate this, but https://github.com/buffet told me that this is what I should do for peak maintainability error_handling_ctx::clear_files(); - - let eww_config = config::EwwConfig::read_from_file(&mut error_handling_ctx::ERROR_HANDLING_CTX.lock().unwrap(), &paths.get_yuck_path())?; + let eww_config = + config::EwwConfig::read_from_file(&mut error_handling_ctx::ERROR_HANDLING_CTX.lock().unwrap(), &paths.get_yuck_path())?; gtk::init()?; @@ -109,27 +114,36 @@ fn init_async_part(paths: EwwPaths, ui_send: UnboundedSender /// Watch configuration files for changes, sending reload events to the eww app when the files change. async fn run_filewatch>(config_dir: P, evt_send: UnboundedSender) -> Result<()> { - use notify::Watcher; + use notify::{RecommendedWatcher, RecursiveMode, Watcher}; let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel(); - let mut watcher: notify::RecommendedWatcher = - notify::Watcher::new_immediate(move |res: notify::Result| match res { - Ok(event) => { - if let Err(err) = tx.send(event.paths) { + let mut watcher: RecommendedWatcher = Watcher::new_immediate(move |res: notify::Result| match res { + Ok(event) => { + let relevant_files_changed = event.paths.iter().any(|path| { + let ext = path.extension().unwrap_or_default(); + ext == "yuck" || ext == "scss" + }); + if !relevant_files_changed { + if let Err(err) = tx.send(()) { log::warn!("Error forwarding file update event: {:?}", err); } } - Err(e) => log::error!("Encountered Error While Watching Files: {}", e), - })?; - watcher.watch(&config_dir, notify::RecursiveMode::Recursive)?; + } + Err(e) => log::error!("Encountered Error While Watching Files: {}", e), + })?; + watcher.watch(&config_dir, RecursiveMode::Recursive)?; + + // make sure to not trigger reloads too much by only accepting one reload every 500ms. + let debounce_done = Arc::new(std::sync::atomic::AtomicBool::new(true)); crate::loop_select_exiting! { - Some(paths) = rx.recv() => { - for path in paths { - let extension = path.extension().unwrap_or_default(); - if extension != "xml" && extension != "scss" { - continue; - } + Some(()) = rx.recv() => { + let debounce_done = debounce_done.clone(); + if debounce_done.swap(false, Ordering::SeqCst) { + tokio::spawn(async move { + tokio::time::sleep(std::time::Duration::from_millis(500)).await; + debounce_done.store(true, Ordering::SeqCst); + }); let (daemon_resp_sender, mut daemon_resp_response) = tokio::sync::mpsc::unbounded_channel(); evt_send.send(app::DaemonCommand::ReloadConfigAndCss(daemon_resp_sender))?; diff --git a/crates/eww/src/util.rs b/crates/eww/src/util.rs index 4f684f7..5d853ed 100644 --- a/crates/eww/src/util.rs +++ b/crates/eww/src/util.rs @@ -122,21 +122,6 @@ impl> T { } } -pub fn parse_duration(s: &str) -> Result { - use std::time::Duration; - if s.ends_with("ms") { - Ok(Duration::from_millis(s.trim_end_matches("ms").parse()?)) - } else if s.ends_with('s') { - Ok(Duration::from_secs(s.trim_end_matches('s').parse()?)) - } else if s.ends_with('m') { - Ok(Duration::from_secs(s.trim_end_matches('m').parse::()? * 60)) - } else if s.ends_with('h') { - Ok(Duration::from_secs(s.trim_end_matches('h').parse::()? * 60 * 60)) - } else { - Err(anyhow!("unrecognized time format: {}", s)) - } -} - pub trait IterAverage { fn avg(self) -> f32; } diff --git a/crates/eww/src/widgets/mod.rs b/crates/eww/src/widgets/mod.rs index 7598ff7..a918390 100644 --- a/crates/eww/src/widgets/mod.rs +++ b/crates/eww/src/widgets/mod.rs @@ -64,18 +64,7 @@ fn build_builtin_gtk_widget( ) -> Result> { let mut bargs = BuilderArgs { eww_state, widget, window_name, unhandled_attrs: widget.attrs.keys().collect(), widget_definitions }; - let gtk_widget = match widget_to_gtk_widget(&mut bargs) { - Ok(Some(gtk_widget)) => gtk_widget, - result => { - return result.with_context(|| { - format!( - "{}Error building widget {}", - bargs.widget.span.map(|x| format!("{} |", x)).unwrap_or_default(), - bargs.widget.name, - ) - }) - } - }; + let gtk_widget = widget_to_gtk_widget(&mut bargs)?; // run resolve functions for superclasses such as range, orientable, and widget @@ -85,7 +74,7 @@ fn build_builtin_gtk_widget( let child_widget = child.render(bargs.eww_state, window_name, widget_definitions).with_context(|| { format!( "{}error while building child '{:#?}' of '{}'", - widget.span.map(|x| format!("{} |", x)).unwrap_or_default(), + format!("{} | ", widget.span), &child, >k_widget.get_widget_name() ) @@ -106,7 +95,7 @@ fn build_builtin_gtk_widget( if !bargs.unhandled_attrs.is_empty() { log::error!( "{}: Unknown attribute used in {}: {}", - widget.span.map(|x| format!("{} | ", x)).unwrap_or_default(), + format!("{} | ", widget.span), widget.name, bargs.unhandled_attrs.iter().map(|x| x.to_string()).join(", ") ) diff --git a/crates/eww/src/widgets/widget_definitions.rs b/crates/eww/src/widgets/widget_definitions.rs index f4cd54c..b14c1c2 100644 --- a/crates/eww/src/widgets/widget_definitions.rs +++ b/crates/eww/src/widgets/widget_definitions.rs @@ -1,23 +1,19 @@ #![allow(clippy::option_map_unit_fn)] use super::{run_command, BuilderArgs}; -use crate::{ - enum_parse, eww_state, resolve_block, - util::{list_difference, parse_duration}, - widgets::widget_node, -}; +use crate::{enum_parse, eww_state, resolve_block, util::list_difference, widgets::widget_node}; use anyhow::*; use gdk::WindowExt; use glib; use gtk::{self, prelude::*, ImageExt}; -use std::{cell::RefCell, collections::HashMap, rc::Rc}; -use yuck::parser::from_ast::FromAst; +use std::{cell::RefCell, collections::HashMap, rc::Rc, time::Duration}; +use yuck::{config::validate::ValidationError, error::AstError, parser::from_ast::FromAst}; // TODO figure out how to // TODO https://developer.gnome.org/gtk3/stable/GtkFixed.html //// widget definitions -pub(super) fn widget_to_gtk_widget(bargs: &mut BuilderArgs) -> Result> { +pub(super) fn widget_to_gtk_widget(bargs: &mut BuilderArgs) -> Result { let gtk_widget = match bargs.widget.name.as_str() { "box" => build_gtk_box(bargs)?.upcast(), "scale" => build_gtk_scale(bargs)?.upcast(), @@ -35,9 +31,11 @@ pub(super) fn widget_to_gtk_widget(bargs: &mut BuilderArgs) -> Result build_gtk_checkbox(bargs)?.upcast(), "revealer" => build_gtk_revealer(bargs)?.upcast(), "if-else" => build_if_else(bargs)?.upcast(), - _ => return Ok(None), + _ => { + Err(AstError::ValidationError(ValidationError::UnknownWidget(bargs.widget.name_span, bargs.widget.name.to_string())))? + } }; - Ok(Some(gtk_widget)) + Ok(gtk_widget) } /// attributes that apply to all widgets @@ -287,7 +285,7 @@ fn build_gtk_revealer(bargs: &mut BuilderArgs) -> Result { // @prop reveal - sets if the child is revealed or not prop(reveal: as_bool) { gtk_widget.set_reveal_child(reveal); }, // @prop duration - the duration of the reveal transition - prop(duration: as_string = "500ms") { gtk_widget.set_transition_duration(parse_duration(&duration)?.as_millis() as u32); }, + prop(duration: as_duration = Duration::from_millis(500)) { gtk_widget.set_transition_duration(duration.as_millis() as u32); }, }); Ok(gtk_widget) } diff --git a/crates/eww/src/widgets/widget_node.rs b/crates/eww/src/widgets/widget_node.rs index 82a367f..215689c 100644 --- a/crates/eww/src/widgets/widget_node.rs +++ b/crates/eww/src/widgets/widget_node.rs @@ -4,7 +4,10 @@ use dyn_clone; use eww_shared_util::{AttrName, Span, VarName}; use simplexpr::SimplExpr; use std::collections::HashMap; -use yuck::config::{widget_definition::WidgetDefinition, widget_use::WidgetUse}; +use yuck::{ + config::{validate::ValidationError, widget_definition::WidgetDefinition, widget_use::WidgetUse}, + error::AstError, +}; pub trait WidgetNode: std::fmt::Debug + dyn_clone::DynClone + Send + Sync { fn get_name(&self) -> &str; @@ -14,9 +17,8 @@ pub trait WidgetNode: std::fmt::Debug + dyn_clone::DynClone + Send + Sync { /// /// Also registers all the necessary state-change handlers in the eww_state. /// - /// This may return `Err` in case there was an actual error while parsing or - /// resolving the widget, Or `Ok(None)` if the widget_use just didn't match any - /// widget name. + /// This may return `Err` in case there was an actual error while parsing + /// or when the widget_use did not match any widget name fn render( &self, eww_state: &mut EwwState, @@ -56,14 +58,23 @@ impl WidgetNode for UserDefined { #[derive(Debug, Clone)] pub struct Generic { pub name: String, - pub span: Option, + pub name_span: Span, + pub span: Span, pub children: Vec>, pub attrs: HashMap, } impl Generic { pub fn get_attr(&self, key: &str) -> Result<&SimplExpr> { - self.attrs.get(key).context(format!("attribute '{}' missing from use of '{}'", key, &self.name)) + Ok(self.attrs.get(key).ok_or_else(|| { + AstError::ValidationError(ValidationError::MissingAttr { + widget_name: self.name.to_string(), + arg_name: AttrName(key.to_string()), + use_span: self.span, + // TODO set this when available + arg_list_span: None, + }) + })?) } /// returns all the variables that are referenced in this widget @@ -78,7 +89,7 @@ impl WidgetNode for Generic { } fn get_span(&self) -> Option { - self.span + Some(self.span) } fn render( @@ -87,8 +98,9 @@ impl WidgetNode for Generic { window_name: &str, widget_definitions: &HashMap, ) -> Result { - crate::widgets::build_builtin_gtk_widget(eww_state, window_name, widget_definitions, self)? - .with_context(|| format!("Unknown widget '{}'", self.get_name())) + Ok(crate::widgets::build_builtin_gtk_widget(eww_state, window_name, widget_definitions, self)?.ok_or_else(|| { + AstError::ValidationError(ValidationError::UnknownWidget(self.name_span, self.get_name().to_string())) + })?) } } @@ -112,7 +124,8 @@ pub fn generate_generic_widget_node( } else { Ok(Box::new(Generic { name: w.name, - span: Some(w.span), + name_span: w.name_span, + span: w.span, attrs: w .attrs .attrs diff --git a/crates/simplexpr/src/dynval.rs b/crates/simplexpr/src/dynval.rs index f72248d..62b71bb 100644 --- a/crates/simplexpr/src/dynval.rs +++ b/crates/simplexpr/src/dynval.rs @@ -6,7 +6,7 @@ use std::{fmt, iter::FromIterator, str::FromStr}; pub type Result = std::result::Result; #[derive(Debug, thiserror::Error)] -#[error("Failed to turn {value} into a value of type {target_type}")] +#[error("Failed to turn `{value}` into a value of type {target_type}")] pub struct ConversionError { pub value: DynVal, pub target_type: &'static str, @@ -93,6 +93,12 @@ macro_rules! impl_dynval_from { impl_dynval_from!(bool, i32, u32, f32, u8, f64, &str); +impl From for DynVal { + fn from(d: std::time::Duration) -> Self { + DynVal(format!("{}ms", d.as_millis()), None) + } +} + impl From<&serde_json::Value> for DynVal { fn from(v: &serde_json::Value) -> Self { DynVal( diff --git a/crates/simplexpr/src/lib.rs b/crates/simplexpr/src/lib.rs index 48156f4..b4eefc1 100644 --- a/crates/simplexpr/src/lib.rs +++ b/crates/simplexpr/src/lib.rs @@ -19,6 +19,4 @@ lalrpop_mod!( pub simplexpr_parser ); -pub fn parse_string(file_id: usize, s: &str) -> Result { - parser::parse_string(file_id, s) -} +pub use parser::parse_string; diff --git a/crates/simplexpr/src/parser/lexer.rs b/crates/simplexpr/src/parser/lexer.rs index e3c2447..2d3394d 100644 --- a/crates/simplexpr/src/parser/lexer.rs +++ b/crates/simplexpr/src/parser/lexer.rs @@ -56,11 +56,12 @@ pub type SpannedResult = Result<(Loc, Tok, Loc), Error>; pub struct Lexer<'input> { lexer: logos::SpannedIter<'input, Token>, + byte_offset: usize, } impl<'input> Lexer<'input> { - pub fn new(text: &'input str) -> Self { - Lexer { lexer: logos::Lexer::new(text).spanned() } + pub fn new(byte_offset: usize, text: &'input str) -> Self { + Lexer { lexer: logos::Lexer::new(text).spanned(), byte_offset } } } @@ -69,10 +70,11 @@ impl<'input> Iterator for Lexer<'input> { fn next(&mut self) -> Option { let (token, range) = self.lexer.next()?; + let range = (range.start + self.byte_offset, range.end + self.byte_offset); if token == Token::Error { - Some(Err(LexicalError(range.start, range.end))) + Some(Err(LexicalError(range.0, range.1))) } else { - Some(Ok((range.start, token, range.end))) + Some(Ok((range.0, token, range.1))) } } } diff --git a/crates/simplexpr/src/parser/mod.rs b/crates/simplexpr/src/parser/mod.rs index f833b61..169490a 100644 --- a/crates/simplexpr/src/parser/mod.rs +++ b/crates/simplexpr/src/parser/mod.rs @@ -6,8 +6,8 @@ use crate::{ error::{Error, Result}, }; -pub fn parse_string(file_id: usize, s: &str) -> Result { - let lexer = lexer::Lexer::new(s); +pub fn parse_string(byte_offset: usize, file_id: usize, s: &str) -> Result { + let lexer = lexer::Lexer::new(byte_offset, s); let parser = crate::simplexpr_parser::ExprParser::new(); parser.parse(file_id, lexer).map_err(|e| Error::from_parse_error(file_id, e)) } @@ -20,7 +20,7 @@ mod tests { use crate::parser::lexer::Lexer; ::insta::with_settings!({sort_maps => true}, { $( - ::insta::assert_debug_snapshot!(p.parse(0, Lexer::new($text))); + ::insta::assert_debug_snapshot!(p.parse(0, Lexer::new(0, $text))); )* }); }} diff --git a/crates/yuck/src/config/config.rs b/crates/yuck/src/config/config.rs index bb5bce9..0063d0a 100644 --- a/crates/yuck/src/config/config.rs +++ b/crates/yuck/src/config/config.rs @@ -118,7 +118,10 @@ impl Config { } pub fn generate_from_main_file(files: &mut impl YuckFiles, path: &str) -> AstResult { - let (span, top_levels) = files.load(path).map_err(|err| AstError::Other(None, Box::new(err)))?; + let (span, top_levels) = files.load(path).map_err(|err| match err { + FilesError::IoError(err) => AstError::Other(None, Box::new(err)), + FilesError::AstError(x) => x, + })?; Self::generate(files, top_levels) } } diff --git a/crates/yuck/src/config/validate.rs b/crates/yuck/src/config/validate.rs index 093e2c5..73a4630 100644 --- a/crates/yuck/src/config/validate.rs +++ b/crates/yuck/src/config/validate.rs @@ -16,7 +16,7 @@ pub enum ValidationError { UnknownWidget(Span, String), #[error("Missing attribute `{arg_name}` in use of widget `{widget_name}`")] - MissingAttr { widget_name: String, arg_name: AttrName, arg_list_span: Span, use_span: Span }, + MissingAttr { widget_name: String, arg_name: AttrName, arg_list_span: Option, use_span: Span }, } pub fn validate(defs: &HashMap, content: &WidgetUse) -> Result<(), ValidationError> { @@ -26,7 +26,7 @@ pub fn validate(defs: &HashMap, content: &WidgetUse) - return Err(ValidationError::MissingAttr { widget_name: def.name.to_string(), arg_name: expected.clone(), - arg_list_span: def.args_span, + arg_list_span: Some(def.args_span), use_span: content.span, }); } diff --git a/crates/yuck/src/config/widget_use.rs b/crates/yuck/src/config/widget_use.rs index 18efcc0..efea534 100644 --- a/crates/yuck/src/config/widget_use.rs +++ b/crates/yuck/src/config/widget_use.rs @@ -17,20 +17,22 @@ pub struct WidgetUse { pub attrs: Attributes, pub children: Vec, pub span: Span, + pub name_span: Span, } impl FromAst for WidgetUse { fn from_ast(e: Ast) -> AstResult { let span = e.span(); - if let Ok(text) = e.as_literal_ref() { + if let Ok(value) = e.clone().as_simplexpr() { Ok(Self { name: "label".to_string(), + name_span: span.point_span(), attrs: Attributes::new( - span.into(), + span, maplit::hashmap! { AttrName("text".to_string()) => AttrEntry::new( - span.into(), - Ast::Literal(span.into(), text.clone()) + span, + Ast::SimplExpr(span.into(), value.clone()) ) }, ), @@ -39,10 +41,10 @@ impl FromAst for WidgetUse { }) } else { let mut iter = e.try_ast_iter()?; - let (_, name) = iter.expect_symbol()?; + let (name_span, name) = iter.expect_symbol()?; let attrs = iter.expect_key_values()?; let children = iter.map(WidgetUse::from_ast).collect::>>()?; - Ok(Self { name, attrs, children, span }) + Ok(Self { name, attrs, children, span, name_span }) } } } diff --git a/crates/yuck/src/error.rs b/crates/yuck/src/error.rs index 2c4662b..79e1098 100644 --- a/crates/yuck/src/error.rs +++ b/crates/yuck/src/error.rs @@ -89,20 +89,6 @@ fn get_parse_error_span( } } -// pub fn spanned(span: Span, err: impl Into) -> AstError { -// use AstError::*; -// match err.into() { -// UnknownToplevel(s, x) => UnknownToplevel(Some(s.unwrap_or(span)), x), -// MissingNode(s) => MissingNode(Some(s.unwrap_or(span))), -// WrongExprType(s, x, y) => WrongExprType(Some(s.unwrap_or(span)), x, y), -// UnknownToplevel(s, x) => UnknownToplevel(Some(s.unwrap_or(span)), x), -// MissingNode(s) => MissingNode(Some(s.unwrap_or(span))), -// NotAValue(s, x) => NotAValue(Some(s.unwrap_or(span)), x), -// MismatchedElementName(s, expected, got) => MismatchedElementName(Some(s.unwrap_or(span)), expected, got), -// Other(s, x) => Other(Some(s.unwrap_or(span)), x), -// x @ ConversionError(_) | x @ AttrError(_) | x @ ValidationError(_) | x @ ParseError { .. } => x, -//} - pub trait OptionAstErrorExt { fn or_missing(self, span: Span) -> Result; } diff --git a/crates/yuck/src/format_diagnostic.rs b/crates/yuck/src/format_diagnostic.rs index f244344..d96586c3 100644 --- a/crates/yuck/src/format_diagnostic.rs +++ b/crates/yuck/src/format_diagnostic.rs @@ -33,6 +33,20 @@ macro_rules! gen_diagnostic { }}; } +pub trait DiagnosticExt: Sized { + fn with_opt_label(self, label: Option>) -> Self; +} + +impl DiagnosticExt for Diagnostic { + fn with_opt_label(self, label: Option>) -> Self { + if let Some(label) = label { + self.with_labels(vec![label]) + } else { + self + } + } +} + pub trait ToDiagnostic { fn to_diagnostic(&self) -> Diagnostic; } @@ -49,10 +63,8 @@ impl ToDiagnostic for AstError { let diag = gen_diagnostic! { msg = format!("{}", error), }; - diag.with_labels(vec![ - Label::secondary(use_span.2, use_span.0..use_span.1).with_message("Argument missing here"), - Label::secondary(arg_list_span.2, arg_list_span.0..arg_list_span.1).with_message("but is required here"), - ]) + diag.with_opt_label(Some(span_to_secondary_label(*use_span).with_message("Argument missing here"))) + .with_opt_label(arg_list_span.map(|s| span_to_secondary_label(s).with_message("but is required here"))) } } } else if let Some(span) = self.get_span() { @@ -76,7 +88,7 @@ impl ToDiagnostic for AstError { AstError::ParseError { file_id, source } => lalrpop_error_to_diagnostic(source, span, |error| match error { parse_error::ParseError::SimplExpr(_, error) => simplexpr_error_to_diagnostic(error, span), - parse_error::ParseError::LexicalError(_) => lexical_error_to_diagnostic(span), + parse_error::ParseError::LexicalError(span) => lexical_error_to_diagnostic(*span), }), AstError::MismatchedElementName(_, expected, got) => gen_diagnostic! { msg = format!("Expected element `{}`, but found `{}`", expected, got), @@ -120,17 +132,23 @@ fn lalrpop_error_to_diagnostic( } } -fn simplexpr_error_to_diagnostic(error: &simplexpr::error::Error, span: Span) -> Diagnostic { +// TODO this needs a lot of improvement +pub fn simplexpr_error_to_diagnostic(error: &simplexpr::error::Error, span: Span) -> Diagnostic { use simplexpr::error::Error::*; match error { ParseError { source, .. } => lalrpop_error_to_diagnostic(source, span, move |error| lexical_error_to_diagnostic(span)), ConversionError(error) => conversion_error_to_diagnostic(error, span), - Eval(error) => gen_diagnostic!(error, span), + Eval(error) => eval_error_to_diagnostic(error, span), Other(error) => gen_diagnostic!(error, span), - Spanned(_, error) => gen_diagnostic!(error, span), + Spanned(span, error) => gen_diagnostic!(error, span), } } +// TODO this needs a lot of improvement +pub fn eval_error_to_diagnostic(error: &simplexpr::eval::EvalError, span: Span) -> Diagnostic { + gen_diagnostic!(error, error.span().unwrap_or(span)) +} + fn conversion_error_to_diagnostic(error: &dynval::ConversionError, span: Span) -> Diagnostic { let diag = gen_diagnostic! { msg = format!("{}", error), diff --git a/crates/yuck/src/parser/ast.rs b/crates/yuck/src/parser/ast.rs index bcaa402..a1efb33 100644 --- a/crates/yuck/src/parser/ast.rs +++ b/crates/yuck/src/parser/ast.rs @@ -2,7 +2,7 @@ use itertools::Itertools; use simplexpr::{ast::SimplExpr, dynval::DynVal}; use std::collections::HashMap; -use eww_shared_util::Span; +use eww_shared_util::{Span, VarName}; use std::fmt::Display; use super::{ast_iterator::AstIterator, from_ast::FromAst}; @@ -94,10 +94,10 @@ impl Ast { pub fn as_simplexpr(self) -> AstResult { match self { - // do I do this? - // Ast::Array(_, _) => todo!(), - // Ast::Symbol(_, _) => todo!(), - Ast::Literal(span, x) => Ok(SimplExpr::Literal(span.into(), x)), + // TODO do I do this? + // Ast::Array(span, elements) => todo!() + Ast::Symbol(span, x) => Ok(SimplExpr::VarRef(span, VarName(x))), + Ast::Literal(span, x) => Ok(SimplExpr::Literal(span, x)), Ast::SimplExpr(span, x) => Ok(x), _ => Err(AstError::WrongExprType(self.span(), AstType::IntoPrimitive, self.expr_type())), } @@ -126,16 +126,6 @@ impl std::fmt::Display for Ast { } impl std::fmt::Debug for Ast { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - use Ast::*; write!(f, "{}", self) - // match self { - // List(span, x) => f.debug_tuple(&format!("List<{}>", span)).field(x).finish(), - // Array(span, x) => f.debug_tuple(&format!("Array<{}>", span)).field(x).finish(), - // Keyword(span, x) => write!(f, "Number<{}>({})", span, x), - // Symbol(span, x) => write!(f, "Symbol<{}>({})", span, x), - // Value(span, x) => write!(f, "Value<{}>({})", span, x), - // SimplExpr(span, x) => write!(f, "SimplExpr<{}>({})", span, x), - // Comment(span) => write!(f, "Comment<{}>", span), - //} } } diff --git a/crates/yuck/src/parser/lexer.rs b/crates/yuck/src/parser/lexer.rs index 322abdf..c8b5012 100644 --- a/crates/yuck/src/parser/lexer.rs +++ b/crates/yuck/src/parser/lexer.rs @@ -97,7 +97,7 @@ impl Iterator for Lexer { let string = &self.source[self.pos..]; if string.starts_with('{') { - self.pos += 1; + // self.pos += 1; let expr_start = self.pos; let mut in_string = false; loop { @@ -107,8 +107,8 @@ impl Iterator for Lexer { let string = &self.source[self.pos..]; if string.starts_with('}') && !in_string { - let tok_str = &self.source[expr_start..self.pos]; self.pos += 1; + let tok_str = &self.source[expr_start..self.pos]; return Some(Ok((expr_start, Token::SimplExpr(tok_str.to_string()), self.pos - 1))); } else if string.starts_with('"') { self.pos += 1; diff --git a/crates/yuck/src/parser/parser.lalrpop b/crates/yuck/src/parser/parser.lalrpop index a7d05d4..848120d 100644 --- a/crates/yuck/src/parser/parser.lalrpop +++ b/crates/yuck/src/parser/parser.lalrpop @@ -59,9 +59,8 @@ StrLit: String = { SimplExpr: SimplExpr = { =>? { let expr = x[1..x.len() - 1].to_string(); - simplexpr::parse_string(file_id, &expr).map_err(|e| { - let span = e.get_span().map(|Span(simpl_l, simpl_r, file_id)| Span(1 + l + simpl_l, 1 + l + simpl_r, file_id)); - ParseError::User { error: parse_error::ParseError::SimplExpr(span, e) }}) + simplexpr::parse_string(l + 1, file_id, &expr).map_err(|e| { + ParseError::User { error: parse_error::ParseError::SimplExpr(e.get_span(), e) }}) } } From cff2f6beb87dc6b518c1ccf650980af05c930848 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Fri, 23 Jul 2021 15:04:03 +0200 Subject: [PATCH 067/137] Add ad-hoc diagnostic errors --- crates/eww/src/config/script_var.rs | 26 ++++++++++-- crates/eww/src/error.rs | 20 ++++++++++ crates/eww/src/error_handling_ctx.rs | 26 ++++++------ crates/eww/src/main.rs | 1 + crates/eww/src/script_var_handler.rs | 40 ++++++++++--------- crates/yuck/src/config/config.rs | 6 +-- .../yuck/src/config/script_var_definition.rs | 35 ++++++++++------ crates/yuck/src/format_diagnostic.rs | 9 +++-- 8 files changed, 107 insertions(+), 56 deletions(-) create mode 100644 crates/eww/src/error.rs diff --git a/crates/eww/src/config/script_var.rs b/crates/eww/src/config/script_var.rs index 39bddfe..95ef72f 100644 --- a/crates/eww/src/config/script_var.rs +++ b/crates/eww/src/config/script_var.rs @@ -1,8 +1,21 @@ use std::process::Command; use anyhow::*; +use eww_shared_util::{Span, VarName}; use simplexpr::dynval::DynVal; -use yuck::config::script_var_definition::{ScriptVarDefinition, VarSource}; +use yuck::{ + config::script_var_definition::{ScriptVarDefinition, VarSource}, + gen_diagnostic, +}; + +use crate::error::DiagError; + +pub fn create_script_var_failed_error(span: Span, var_name: &VarName) -> DiagError { + DiagError::new(gen_diagnostic! { + msg = format!("Failed to compute value for `{}`", var_name), + label = span => "Defined here", + }) +} pub fn initial_value(var: &ScriptVarDefinition) -> Result { match var { @@ -10,15 +23,20 @@ pub fn initial_value(var: &ScriptVarDefinition) -> Result { VarSource::Function(f) => { f().map_err(|err| anyhow!(err)).with_context(|| format!("Failed to compute initial value for {}", &var.name())) } - VarSource::Shell(f) => run_command(f).with_context(|| format!("Failed to compute initial value for {}", &var.name())), + VarSource::Shell(span, f) => run_command(f).map_err(|_| anyhow!(create_script_var_failed_error(*span, var.name()))), }, - ScriptVarDefinition::Tail(_) => Ok(DynVal::from_string(String::new())), + ScriptVarDefinition::Listen(_) => Ok(DynVal::from_string(String::new())), } } + /// Run a command and get the output pub fn run_command(cmd: &str) -> Result { log::debug!("Running command: {}", cmd); - let output = String::from_utf8(Command::new("/bin/sh").arg("-c").arg(cmd).output()?.stdout)?; + let command = Command::new("/bin/sh").arg("-c").arg(cmd).output()?; + if !command.status.success() { + bail!("Execution of `{}` failed", cmd); + } + let output = String::from_utf8(command.stdout)?; let output = output.trim_matches('\n'); Ok(DynVal::from(output)) } diff --git a/crates/eww/src/error.rs b/crates/eww/src/error.rs new file mode 100644 index 0000000..b21348a --- /dev/null +++ b/crates/eww/src/error.rs @@ -0,0 +1,20 @@ +use codespan_reporting::diagnostic::Diagnostic; + +/// An error that contains a [Diagnostic] for ad-hoc creation of diagnostics. +#[derive(Debug)] +pub struct DiagError { + pub diag: Diagnostic, +} + +impl DiagError { + pub fn new(diag: Diagnostic) -> Self { + Self { diag } + } +} + +impl std::error::Error for DiagError {} +impl std::fmt::Display for DiagError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.diag.message) + } +} diff --git a/crates/eww/src/error_handling_ctx.rs b/crates/eww/src/error_handling_ctx.rs index 04ce248..04a43c8 100644 --- a/crates/eww/src/error_handling_ctx.rs +++ b/crates/eww/src/error_handling_ctx.rs @@ -9,6 +9,8 @@ use yuck::{ format_diagnostic::{eval_error_to_diagnostic, ToDiagnostic}, }; +use crate::error::DiagError; + lazy_static::lazy_static! { pub static ref ERROR_HANDLING_CTX: Arc> = Arc::new(Mutex::new(FsYuckFiles::new())); } @@ -18,29 +20,25 @@ pub fn clear_files() { } pub fn print_error(err: &anyhow::Error) { - match err.downcast_ref::() { - Some(err) => { - eprintln!("{:?}\n{}", err, stringify_diagnostic(err.to_diagnostic())); - } - None => match err.downcast_ref::() { - Some(err) => { - eprintln!("{:?}\n{}", err, stringify_diagnostic(eval_error_to_diagnostic(err, err.span().unwrap_or(DUMMY_SPAN)))); - } - None => { - log::error!("{:?}", err); - } - }, + if let Some(err) = err.downcast_ref::() { + eprintln!("{:?}\n{}", err, stringify_diagnostic(&err.diag)); + } else if let Some(err) = err.downcast_ref::() { + eprintln!("{:?}\n{}", err, stringify_diagnostic(&err.to_diagnostic())); + } else if let Some(err) = err.downcast_ref::() { + eprintln!("{:?}\n{}", err, stringify_diagnostic(&eval_error_to_diagnostic(err, err.span().unwrap_or(DUMMY_SPAN)))); + } else { + log::error!("{:?}", err); } } pub fn format_error(err: &anyhow::Error) -> String { match err.downcast_ref::() { - Some(err) => stringify_diagnostic(err.to_diagnostic()), + Some(err) => stringify_diagnostic(&err.to_diagnostic()), None => format!("{:?}", err), } } -pub fn stringify_diagnostic(diagnostic: Diagnostic) -> String { +pub fn stringify_diagnostic(diagnostic: &Diagnostic) -> String { use codespan_reporting::term; let config = term::Config::default(); let mut buf = Vec::new(); diff --git a/crates/eww/src/main.rs b/crates/eww/src/main.rs index 9cccbaf..f90edfb 100644 --- a/crates/eww/src/main.rs +++ b/crates/eww/src/main.rs @@ -32,6 +32,7 @@ pub mod script_var_handler; pub mod server; pub mod util; pub mod widgets; +pub mod error; fn main() { let opts: opts::Opt = opts::Opt::from_env(); diff --git a/crates/eww/src/script_var_handler.rs b/crates/eww/src/script_var_handler.rs index 0d94395..8ec7522 100644 --- a/crates/eww/src/script_var_handler.rs +++ b/crates/eww/src/script_var_handler.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use crate::app; +use crate::{app, config::create_script_var_failed_error}; use anyhow::*; use app::DaemonCommand; @@ -11,7 +11,7 @@ use tokio::{ sync::mpsc::UnboundedSender, }; use tokio_util::sync::CancellationToken; -use yuck::config::script_var_definition::{PollScriptVar, ScriptVarDefinition, TailScriptVar}; +use yuck::config::script_var_definition::{ListenScriptVar, PollScriptVar, ScriptVarDefinition, VarSource}; /// Initialize the script var handler, and return a handle to that handler, which can be used to control /// the script var execution. @@ -23,7 +23,7 @@ pub fn init(evt_send: UnboundedSender) -> ScriptVarHandlerHandle rt.block_on(async { let _: Result<_> = try { let mut handler = ScriptVarHandler { - tail_handler: TailVarHandler::new(evt_send.clone())?, + listen_handler: ListenVarHandler::new(evt_send.clone())?, poll_handler: PollVarHandler::new(evt_send)?, }; crate::loop_select_exiting! { @@ -87,7 +87,7 @@ enum ScriptVarHandlerMsg { /// Handler that manages running and updating [ScriptVarDefinition]s struct ScriptVarHandler { - tail_handler: TailVarHandler, + listen_handler: ListenVarHandler, poll_handler: PollVarHandler, } @@ -95,14 +95,14 @@ impl ScriptVarHandler { async fn add(&mut self, script_var: ScriptVarDefinition) { match script_var { ScriptVarDefinition::Poll(var) => self.poll_handler.start(var).await, - ScriptVarDefinition::Tail(var) => self.tail_handler.start(var).await, + ScriptVarDefinition::Listen(var) => self.listen_handler.start(var).await, }; } /// Stop the handler that is responsible for a given variable. fn stop_for_variable(&mut self, name: &VarName) -> Result<()> { log::debug!("Stopping script var process for variable {}", name); - self.tail_handler.stop_for_variable(name); + self.listen_handler.stop_for_variable(name); self.poll_handler.stop_for_variable(name); Ok(()) } @@ -110,7 +110,7 @@ impl ScriptVarHandler { /// stop all running scripts and schedules fn stop_all(&mut self) { log::debug!("Stopping script-var-handlers"); - self.tail_handler.stop_all(); + self.listen_handler.stop_all(); self.poll_handler.stop_all(); } } @@ -163,8 +163,10 @@ impl PollVarHandler { fn run_poll_once(var: &PollScriptVar) -> Result { match &var.command { - yuck::config::script_var_definition::VarSource::Shell(x) => crate::config::script_var::run_command(x), - yuck::config::script_var_definition::VarSource::Function(x) => x().map_err(|e| anyhow!(e)), + VarSource::Shell(span, x) => crate::config::script_var::run_command(x).map_err(|_| { + anyhow!(create_script_var_failed_error(*span, &var.name)) + }), + VarSource::Function(x) => x().map_err(|e| anyhow!(e)), } } @@ -174,21 +176,21 @@ impl Drop for PollVarHandler { } } -struct TailVarHandler { +struct ListenVarHandler { evt_send: UnboundedSender, - tail_process_handles: HashMap, + listen_process_handles: HashMap, } -impl TailVarHandler { +impl ListenVarHandler { fn new(evt_send: UnboundedSender) -> Result { - let handler = TailVarHandler { evt_send, tail_process_handles: HashMap::new() }; + let handler = ListenVarHandler { evt_send, listen_process_handles: HashMap::new() }; Ok(handler) } - async fn start(&mut self, var: TailScriptVar) { + async fn start(&mut self, var: ListenScriptVar) { log::debug!("starting poll var {}", &var.name); let cancellation_token = CancellationToken::new(); - self.tail_process_handles.insert(var.name.clone(), cancellation_token.clone()); + self.listen_process_handles.insert(var.name.clone(), cancellation_token.clone()); let evt_send = self.evt_send.clone(); tokio::spawn(async move { @@ -215,18 +217,18 @@ impl TailVarHandler { } fn stop_for_variable(&mut self, name: &VarName) { - if let Some(token) = self.tail_process_handles.remove(name) { - log::debug!("stopped tail var {}", name); + if let Some(token) = self.listen_process_handles.remove(name) { + log::debug!("stopped listen-var {}", name); token.cancel(); } } fn stop_all(&mut self) { - self.tail_process_handles.drain().for_each(|(_, token)| token.cancel()); + self.listen_process_handles.drain().for_each(|(_, token)| token.cancel()); } } -impl Drop for TailVarHandler { +impl Drop for ListenVarHandler { fn drop(&mut self) { self.stop_all(); } diff --git a/crates/yuck/src/config/config.rs b/crates/yuck/src/config/config.rs index 0063d0a..b07021b 100644 --- a/crates/yuck/src/config/config.rs +++ b/crates/yuck/src/config/config.rs @@ -12,7 +12,7 @@ use super::{ window_definition::WindowDefinition, }; use crate::{ - config::script_var_definition::{PollScriptVar, TailScriptVar}, + config::script_var_definition::{ListenScriptVar, PollScriptVar}, error::{AstError, AstResult, OptionAstErrorExt}, parser::{ ast::Ast, @@ -59,8 +59,8 @@ impl FromAst for TopLevel { x if x == PollScriptVar::get_element_name() => { Self::ScriptVarDefinition(ScriptVarDefinition::Poll(PollScriptVar::from_tail(span, iter)?)) } - x if x == TailScriptVar::get_element_name() => { - Self::ScriptVarDefinition(ScriptVarDefinition::Tail(TailScriptVar::from_tail(span, iter)?)) + x if x == ListenScriptVar::get_element_name() => { + Self::ScriptVarDefinition(ScriptVarDefinition::Listen(ListenScriptVar::from_tail(span, iter)?)) } x if x == WindowDefinition::get_element_name() => Self::WindowDefinition(WindowDefinition::from_tail(span, iter)?), x => return Err(AstError::UnknownToplevel(sym_span, x.to_string())), diff --git a/crates/yuck/src/config/script_var_definition.rs b/crates/yuck/src/config/script_var_definition.rs index 8091110..862c323 100644 --- a/crates/yuck/src/config/script_var_definition.rs +++ b/crates/yuck/src/config/script_var_definition.rs @@ -15,14 +15,24 @@ use eww_shared_util::{AttrName, Span, VarName}; #[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)] pub enum ScriptVarDefinition { Poll(PollScriptVar), - Tail(TailScriptVar), + Listen(ListenScriptVar), } impl ScriptVarDefinition { pub fn name(&self) -> &VarName { match self { ScriptVarDefinition::Poll(x) => &x.name, - ScriptVarDefinition::Tail(x) => &x.name, + ScriptVarDefinition::Listen(x) => &x.name, + } + } + + pub fn command_span(&self) -> Option { + match self { + ScriptVarDefinition::Poll(x) => match x.command { + VarSource::Shell(span, _) => Some(span), + VarSource::Function(_) => None, + }, + ScriptVarDefinition::Listen(x) => Some(x.command_span), } } } @@ -30,10 +40,11 @@ impl ScriptVarDefinition { #[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)] pub enum VarSource { // TODO allow for other executors? (python, etc) - Shell(String), + Shell(Span, String), #[serde(skip)] Function(fn() -> Result>), } + #[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)] pub struct PollScriptVar { pub name: VarName, @@ -43,32 +54,32 @@ pub struct PollScriptVar { impl FromAstElementContent for PollScriptVar { fn get_element_name() -> &'static str { - "defpollvar" + "defpoll" } fn from_tail>(span: Span, mut iter: AstIterator) -> AstResult { let (_, name) = iter.expect_symbol()?; let mut attrs = iter.expect_key_values()?; let interval = attrs.primitive_required::("interval")?.as_duration()?; - // let interval = interval.as_duration()?; - let (_, script) = iter.expect_literal()?; - Ok(Self { name: VarName(name), command: VarSource::Shell(script.to_string()), interval }) + let (script_span, script) = iter.expect_literal()?; + Ok(Self { name: VarName(name), command: VarSource::Shell(script_span, script.to_string()), interval }) } } #[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)] -pub struct TailScriptVar { +pub struct ListenScriptVar { pub name: VarName, pub command: String, + pub command_span: Span, } -impl FromAstElementContent for TailScriptVar { +impl FromAstElementContent for ListenScriptVar { fn get_element_name() -> &'static str { - "deftailvar" + "deflisten" } fn from_tail>(span: Span, mut iter: AstIterator) -> AstResult { let (_, name) = iter.expect_symbol()?; - let (_, script) = iter.expect_literal()?; - Ok(Self { name: VarName(name), command: script.to_string() }) + let (command_span, script) = iter.expect_literal()?; + Ok(Self { name: VarName(name), command: script.to_string(), command_span }) } } diff --git a/crates/yuck/src/format_diagnostic.rs b/crates/yuck/src/format_diagnostic.rs index d96586c3..e04f917 100644 --- a/crates/yuck/src/format_diagnostic.rs +++ b/crates/yuck/src/format_diagnostic.rs @@ -12,24 +12,25 @@ fn span_to_secondary_label(span: Span) -> Label { Label::secondary(span.2, span.0..span.1) } +#[macro_export] macro_rules! gen_diagnostic { ( $(msg = $msg:expr)? $(, label = $span:expr $(=> $label:expr)?)? $(, note = $note:expr)? $(,)? ) => { - Diagnostic::error() + ::codespan_reporting::diagnostic::Diagnostic::error() $(.with_message($msg.to_string()))? $(.with_labels(vec![ - Label::primary($span.2, $span.0..$span.1) + ::codespan_reporting::diagnostic::Label::primary($span.2, $span.0..$span.1) $(.with_message($label))? ]))? $(.with_notes(vec![$note]))? }; ($msg:expr $(, $span:expr $(,)?)?) => {{ - Diagnostic::error() + ::codespan_reporting::diagnostic::Diagnostic::error() .with_message($msg.to_string()) - $(.with_labels(vec![Label::primary($span.2, $span.0..$span.1)]))? + $(.with_labels(vec![::codespan_reporting::diagnostic::Label::primary($span.2, $span.0..$span.1)]))? }}; } From 186c284a1a8dbc789104bc03b3fdce0141b946db Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Fri, 23 Jul 2021 15:19:16 +0200 Subject: [PATCH 068/137] Fix build on wayland and no display backend --- crates/eww/src/app.rs | 1 + crates/eww/src/display_backend.rs | 12 +- crates/yuck/Cargo.toml | 6 + .../yuck/src/config/backend_window_options.rs | 197 ++++++++++-------- 4 files changed, 118 insertions(+), 98 deletions(-) diff --git a/crates/eww/src/app.rs b/crates/eww/src/app.rs index 7db2642..137d405 100644 --- a/crates/eww/src/app.rs +++ b/crates/eww/src/app.rs @@ -348,6 +348,7 @@ fn initialize_window( } /// Apply the provided window-positioning rules to the window. +#[cfg(feature = "x11")] fn apply_window_position( mut window_geometry: WindowGeometry, monitor_geometry: gdk::Rectangle, diff --git a/crates/eww/src/display_backend.rs b/crates/eww/src/display_backend.rs index f2c176d..af38e82 100644 --- a/crates/eww/src/display_backend.rs +++ b/crates/eww/src/display_backend.rs @@ -2,27 +2,27 @@ pub use platform::*; #[cfg(feature = "no-x11-wayland")] mod platform { - use crate::config::{EwwWindowDefinition, StrutDefinition, WindowStacking}; - use anyhow::*; - use gtk::{self, prelude::*}; + use crate::config::EwwWindowDefinition; - pub fn initialize_window(window_def: &EwwWindowDefinition, _monitor: gdk::Rectangle) -> Option { + pub fn initialize_window(_window_def: &EwwWindowDefinition, _monitor: gdk::Rectangle) -> Option { Some(gtk::Window::new(gtk::WindowType::Toplevel)) } } #[cfg(feature = "wayland")] mod platform { - use crate::config::{AnchorAlignment, EwwWindowDefinition, WindowStacking}; use gdk; use gtk::prelude::*; + use yuck::config::{window_definition::WindowStacking, window_geometry::AnchorAlignment}; + + use crate::config::EwwWindowDefinition; pub fn initialize_window(window_def: &EwwWindowDefinition, monitor: gdk::Rectangle) -> Option { let window = gtk::Window::new(gtk::WindowType::Toplevel); // Initialising a layer shell surface gtk_layer_shell::init_for_window(&window); // Sets the monitor where the surface is shown - match window_def.screen_number { + match window_def.monitor_number { Some(index) => { if let Some(monitor) = gdk::Display::get_default().expect("could not get default display").get_monitor(index) { gtk_layer_shell::set_monitor(&window, &monitor); diff --git a/crates/yuck/Cargo.toml b/crates/yuck/Cargo.toml index 68f4557..d159111 100644 --- a/crates/yuck/Cargo.toml +++ b/crates/yuck/Cargo.toml @@ -6,6 +6,12 @@ edition = "2018" build = "build.rs" +[features] +default = ["x11"] +x11 = [] +wayland = [] +no-x11-wayland = [] + [dependencies] lalrpop-util = "0.19.5" regex = "1" diff --git a/crates/yuck/src/config/backend_window_options.rs b/crates/yuck/src/config/backend_window_options.rs index 4267388..ad0cfe8 100644 --- a/crates/yuck/src/config/backend_window_options.rs +++ b/crates/yuck/src/config/backend_window_options.rs @@ -12,110 +12,123 @@ use eww_shared_util::Span; use super::{attributes::Attributes, window_definition::EnumParseError}; -pub type BackendWindowOptions = X11WindowOptions; +pub use backend::*; -#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)] -pub struct X11WindowOptions { - pub wm_ignore: bool, - pub sticky: bool, - pub window_type: WindowType, - pub struts: StrutDefinition, -} +#[cfg(feature = "x11")] +mod backend { + use super::*; -impl X11WindowOptions { - pub fn from_attrs(attrs: &mut Attributes) -> AstResult { - let struts = attrs.ast_optional("reserve")?; - let window_type = attrs.primitive_optional("windowtype")?; - Ok(X11WindowOptions { - wm_ignore: attrs.primitive_optional("wm-ignore")?.unwrap_or(window_type.is_none() && struts.is_none()), - window_type: window_type.unwrap_or_default(), - sticky: attrs.primitive_optional("sticky")?.unwrap_or(true), - struts: struts.unwrap_or_default(), - }) + #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)] + pub struct BackendWindowOptions { + pub wm_ignore: bool, + pub sticky: bool, + pub window_type: WindowType, + pub struts: StrutDefinition, } -} -#[derive(Debug, Clone, PartialEq, Eq, smart_default::SmartDefault, serde::Serialize)] -pub enum WindowType { - #[default] - Dock, - Dialog, - Toolbar, - Normal, - Utility, -} -impl FromStr for WindowType { - type Err = EnumParseError; + impl BackendWindowOptions { + pub fn from_attrs(attrs: &mut Attributes) -> AstResult { + let struts = attrs.ast_optional("reserve")?; + let window_type = attrs.primitive_optional("windowtype")?; + Ok(Self { + wm_ignore: attrs.primitive_optional("wm-ignore")?.unwrap_or(window_type.is_none() && struts.is_none()), + window_type: window_type.unwrap_or_default(), + sticky: attrs.primitive_optional("sticky")?.unwrap_or(true), + struts: struts.unwrap_or_default(), + }) + } + } - fn from_str(s: &str) -> Result { - enum_parse! { "window type", s, - "dock" => Self::Dock, - "toolbar" => Self::Toolbar, - "dialog" => Self::Dialog, - "normal" => Self::Normal, - "utility" => Self::Utility, + #[derive(Debug, Clone, PartialEq, Eq, smart_default::SmartDefault, serde::Serialize)] + pub enum WindowType { + #[default] + Dock, + Dialog, + Toolbar, + Normal, + Utility, + } + impl FromStr for WindowType { + type Err = EnumParseError; + + fn from_str(s: &str) -> Result { + enum_parse! { "window type", s, + "dock" => Self::Dock, + "toolbar" => Self::Toolbar, + "dialog" => Self::Dialog, + "normal" => Self::Normal, + "utility" => Self::Utility, + } + } + } + + #[derive(Debug, Clone, Copy, Eq, PartialEq, smart_default::SmartDefault, serde::Serialize)] + pub enum Side { + #[default] + Top, + Left, + Right, + Bottom, + } + + impl std::str::FromStr for Side { + type Err = EnumParseError; + + fn from_str(s: &str) -> Result { + enum_parse! { "side", s, + "l" | "left" => Side::Left, + "r" | "right" => Side::Right, + "t" | "top" => Side::Top, + "b" | "bottom" => Side::Bottom, + } + } + } + + // Surface definition if the backend for X11 is enable + #[derive(Debug, Clone, Copy, Eq, PartialEq, Default, serde::Serialize)] + pub struct StrutDefinition { + pub side: Side, + pub dist: NumWithUnit, + } + + impl FromAstElementContent for StrutDefinition { + fn get_element_name() -> &'static str { + "struts" + } + + fn from_tail>(span: Span, mut iter: AstIterator) -> AstResult { + let mut attrs = iter.expect_key_values()?; + Ok(StrutDefinition { side: attrs.primitive_required("side")?, dist: attrs.primitive_required("distance")? }) } } } -#[derive(Debug, Clone, Copy, Eq, PartialEq, smart_default::SmartDefault, serde::Serialize)] -pub enum Side { - #[default] - Top, - Left, - Right, - Bottom, -} - -impl std::str::FromStr for Side { - type Err = EnumParseError; - - fn from_str(s: &str) -> Result { - enum_parse! { "side", s, - "l" | "left" => Side::Left, - "r" | "right" => Side::Right, - "t" | "top" => Side::Top, - "b" | "bottom" => Side::Bottom, +#[cfg(feature = "wayland")] +mod backend { + use super::*; + #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)] + pub struct BackendWindowOptions { + pub exclusive: bool, + pub focusable: bool, + } + impl BackendWindowOptions { + pub fn from_attrs(attrs: &mut Attributes) -> AstResult { + Ok(Self { + exclusive: attrs.primitive_optional("exclusive")?.unwrap_or(false), + focusable: attrs.primitive_optional("focusable")?.unwrap_or(false), + }) } } } -// Surface definition if the backend for X11 is enable -#[derive(Debug, Clone, Copy, Eq, PartialEq, Default, serde::Serialize)] -pub struct StrutDefinition { - pub side: Side, - pub dist: NumWithUnit, -} - -impl FromAstElementContent for StrutDefinition { - fn get_element_name() -> &'static str { - "struts" - } - - fn from_tail>(span: Span, mut iter: AstIterator) -> AstResult { - let mut attrs = iter.expect_key_values()?; - Ok(StrutDefinition { side: attrs.primitive_required("side")?, dist: attrs.primitive_required("distance")? }) - } -} - -#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)] -pub struct WaylandWindowOptions { - pub exclusive: bool, - pub focusable: bool, -} -impl WaylandWindowOptions { - pub fn from_attrs(attrs: &mut Attributes) -> AstResult { - Ok(WaylandWindowOptions { - exclusive: attrs.primitive_optional("exclusive")?.unwrap_or(false), - focusable: attrs.primitive_optional("focusable")?.unwrap_or(false), - }) - } -} - -#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)] -pub struct NoBackendWindowOptions; -impl NoBackendWindowOptions { - pub fn from_attrs(attrs: &mut Attributes) -> Result { - Ok(NoBackendWindowOptions) +#[cfg(feature = "no-x11-wayland")] +mod backend { + use super::*; + #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)] + pub struct BackendWindowOptions; + impl BackendWindowOptions { + pub fn from_attrs(attrs: &mut Attributes) -> AstResult { + Ok(Self) + } } } From 680498df82a7dec3c067ff8caaa47569356ca354 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Fri, 23 Jul 2021 18:24:42 +0200 Subject: [PATCH 069/137] Fix lexer string escaping --- Cargo.lock | 1 + crates/eww/src/error_handling_ctx.rs | 27 ++--- crates/simplexpr/Cargo.toml | 1 + crates/simplexpr/src/ast.rs | 2 +- crates/simplexpr/src/parser/lexer.rs | 14 ++- ...lexpr__parser__lexer__simplexpr_lexer.snap | 99 +++++++++++++++++++ .../simplexpr__parser__tests__test-10.snap | 8 ++ .../simplexpr__parser__tests__test-11.snap | 8 ++ .../simplexpr__parser__tests__test-12.snap | 8 ++ .../simplexpr__parser__tests__test-13.snap | 8 ++ .../simplexpr__parser__tests__test-2.snap | 8 ++ .../simplexpr__parser__tests__test-3.snap | 8 ++ .../simplexpr__parser__tests__test-4.snap | 8 ++ .../simplexpr__parser__tests__test-5.snap | 8 ++ .../simplexpr__parser__tests__test-6.snap | 8 ++ .../simplexpr__parser__tests__test-7.snap | 8 ++ .../simplexpr__parser__tests__test-8.snap | 8 ++ .../simplexpr__parser__tests__test-9.snap | 8 ++ .../simplexpr__parser__tests__test.snap | 8 ++ crates/yuck/src/parser/lexer.rs | 18 +++- .../yuck__parser__lexer__yuck_lexer-2.snap | 16 +++ .../yuck__parser__lexer__yuck_lexer-3.snap | 16 +++ .../yuck__parser__lexer__yuck_lexer.snap | 57 +++++++++++ .../snapshots/yuck__parser__test-10.snap | 8 ++ .../snapshots/yuck__parser__test-11.snap | 8 ++ .../snapshots/yuck__parser__test-12.snap | 8 ++ .../snapshots/yuck__parser__test-13.snap | 8 ++ .../snapshots/yuck__parser__test-14.snap | 8 ++ .../snapshots/yuck__parser__test-15.snap | 8 ++ .../snapshots/yuck__parser__test-16.snap | 8 ++ .../snapshots/yuck__parser__test-17.snap | 8 ++ .../snapshots/yuck__parser__test-2.snap | 8 ++ .../snapshots/yuck__parser__test-3.snap | 8 ++ .../snapshots/yuck__parser__test-4.snap | 8 ++ .../snapshots/yuck__parser__test-5.snap | 8 ++ .../snapshots/yuck__parser__test-6.snap | 8 ++ .../snapshots/yuck__parser__test-7.snap | 8 ++ .../snapshots/yuck__parser__test-8.snap | 8 ++ .../snapshots/yuck__parser__test-9.snap | 8 ++ .../parser/snapshots/yuck__parser__test.snap | 8 ++ 40 files changed, 476 insertions(+), 15 deletions(-) create mode 100644 crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__simplexpr_lexer.snap create mode 100644 crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-10.snap create mode 100644 crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-11.snap create mode 100644 crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-12.snap create mode 100644 crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-13.snap create mode 100644 crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-2.snap create mode 100644 crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-3.snap create mode 100644 crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-4.snap create mode 100644 crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-5.snap create mode 100644 crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-6.snap create mode 100644 crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-7.snap create mode 100644 crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-8.snap create mode 100644 crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-9.snap create mode 100644 crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test.snap create mode 100644 crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-2.snap create mode 100644 crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-3.snap create mode 100644 crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer.snap create mode 100644 crates/yuck/src/parser/snapshots/yuck__parser__test-10.snap create mode 100644 crates/yuck/src/parser/snapshots/yuck__parser__test-11.snap create mode 100644 crates/yuck/src/parser/snapshots/yuck__parser__test-12.snap create mode 100644 crates/yuck/src/parser/snapshots/yuck__parser__test-13.snap create mode 100644 crates/yuck/src/parser/snapshots/yuck__parser__test-14.snap create mode 100644 crates/yuck/src/parser/snapshots/yuck__parser__test-15.snap create mode 100644 crates/yuck/src/parser/snapshots/yuck__parser__test-16.snap create mode 100644 crates/yuck/src/parser/snapshots/yuck__parser__test-17.snap create mode 100644 crates/yuck/src/parser/snapshots/yuck__parser__test-2.snap create mode 100644 crates/yuck/src/parser/snapshots/yuck__parser__test-3.snap create mode 100644 crates/yuck/src/parser/snapshots/yuck__parser__test-4.snap create mode 100644 crates/yuck/src/parser/snapshots/yuck__parser__test-5.snap create mode 100644 crates/yuck/src/parser/snapshots/yuck__parser__test-6.snap create mode 100644 crates/yuck/src/parser/snapshots/yuck__parser__test-7.snap create mode 100644 crates/yuck/src/parser/snapshots/yuck__parser__test-8.snap create mode 100644 crates/yuck/src/parser/snapshots/yuck__parser__test-9.snap create mode 100644 crates/yuck/src/parser/snapshots/yuck__parser__test.snap diff --git a/Cargo.lock b/Cargo.lock index 33f838a..674b57a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1889,6 +1889,7 @@ dependencies = [ "itertools 0.10.1", "lalrpop", "lalrpop-util", + "lazy_static", "logos", "maplit", "regex", diff --git a/crates/eww/src/error_handling_ctx.rs b/crates/eww/src/error_handling_ctx.rs index 04a43c8..2198efb 100644 --- a/crates/eww/src/error_handling_ctx.rs +++ b/crates/eww/src/error_handling_ctx.rs @@ -20,30 +20,35 @@ pub fn clear_files() { } pub fn print_error(err: &anyhow::Error) { - if let Some(err) = err.downcast_ref::() { - eprintln!("{:?}\n{}", err, stringify_diagnostic(&err.diag)); - } else if let Some(err) = err.downcast_ref::() { - eprintln!("{:?}\n{}", err, stringify_diagnostic(&err.to_diagnostic())); - } else if let Some(err) = err.downcast_ref::() { - eprintln!("{:?}\n{}", err, stringify_diagnostic(&eval_error_to_diagnostic(err, err.span().unwrap_or(DUMMY_SPAN)))); - } else { + let result: anyhow::Result<_> = try { + if let Some(err) = err.downcast_ref::() { + eprintln!("{:?}\n{}", err, stringify_diagnostic(&err.diag)?); + } else if let Some(err) = err.downcast_ref::() { + eprintln!("{:?}\n{}", err, stringify_diagnostic(&err.to_diagnostic())?); + } else if let Some(err) = err.downcast_ref::() { + eprintln!("{:?}\n{}", err, stringify_diagnostic(&eval_error_to_diagnostic(err, err.span().unwrap_or(DUMMY_SPAN)))?); + } else { + log::error!("{:?}", err); + } + }; + if result.is_err() { log::error!("{:?}", err); } } pub fn format_error(err: &anyhow::Error) -> String { match err.downcast_ref::() { - Some(err) => stringify_diagnostic(&err.to_diagnostic()), + Some(err) => stringify_diagnostic(&err.to_diagnostic()).unwrap_or_else(|_| format!("{:?}", err)), None => format!("{:?}", err), } } -pub fn stringify_diagnostic(diagnostic: &Diagnostic) -> String { +pub fn stringify_diagnostic(diagnostic: &Diagnostic) -> anyhow::Result { use codespan_reporting::term; let config = term::Config::default(); let mut buf = Vec::new(); let mut writer = term::termcolor::Ansi::new(&mut buf); let files = ERROR_HANDLING_CTX.lock().unwrap(); - term::emit(&mut writer, &config, &*files, &diagnostic).unwrap(); - String::from_utf8(buf).unwrap() + term::emit(&mut writer, &config, &*files, &diagnostic)?; + Ok(String::from_utf8(buf)?) } diff --git a/crates/simplexpr/Cargo.toml b/crates/simplexpr/Cargo.toml index ead2853..43cb0ff 100644 --- a/crates/simplexpr/Cargo.toml +++ b/crates/simplexpr/Cargo.toml @@ -14,6 +14,7 @@ itertools = "0.10" thiserror = "1.0" maplit = "1.0" logos = "0.12" +lazy_static = "1.4" serde = {version = "1.0", features = ["derive"]} serde_json = "1.0" diff --git a/crates/simplexpr/src/ast.rs b/crates/simplexpr/src/ast.rs index e63d2d3..b0c282d 100644 --- a/crates/simplexpr/src/ast.rs +++ b/crates/simplexpr/src/ast.rs @@ -47,7 +47,7 @@ impl std::fmt::Display for SimplExpr { 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::IfElse(_, a, b, c) => write!(f, "({} ? {} : {})", a, b, c), SimplExpr::JsonAccess(_, value, index) => write!(f, "{}[{}]", value, index), SimplExpr::FunctionCall(_, function_name, args) => { write!(f, "{}({})", function_name, args.iter().join(", ")) diff --git a/crates/simplexpr/src/parser/lexer.rs b/crates/simplexpr/src/parser/lexer.rs index 2d3394d..27bbff4 100644 --- a/crates/simplexpr/src/parser/lexer.rs +++ b/crates/simplexpr/src/parser/lexer.rs @@ -1,4 +1,9 @@ use logos::Logos; +use regex::Regex; + +lazy_static::lazy_static! { + static ref ESCAPE_REPLACE_REGEX: Regex = Regex::new(r"\\(.)").unwrap(); +} #[rustfmt::skip] #[derive(Logos, Debug, PartialEq, Eq, Clone, strum::Display, strum::EnumString)] @@ -34,7 +39,7 @@ pub enum Token { Ident(String), #[regex(r"[+-]?(?:[0-9]+[.])?[0-9]+", |x| x.slice().to_string())] NumLit(String), - #[regex(r#""(?:[^"\\]|\\.)*""#, |x| x.slice().to_string())] + #[regex(r#""(?:[^"\\]|\\.)*""#, |x| ESCAPE_REPLACE_REGEX.replace_all(x.slice(), "$1").to_string())] StrLit(String), @@ -78,3 +83,10 @@ impl<'input> Iterator for Lexer<'input> { } } } + +#[cfg(test)] +#[test] +fn test_simplexpr_lexer() { + use itertools::Itertools; + insta::assert_debug_snapshot!(Lexer::new(0, r#"(foo + - "()" "a\"b" true false [] 12.2)"#).collect_vec()); +} diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__simplexpr_lexer.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__simplexpr_lexer.snap new file mode 100644 index 0000000..841b2c3 --- /dev/null +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__simplexpr_lexer.snap @@ -0,0 +1,99 @@ +--- +source: crates/simplexpr/src/parser/lexer.rs +expression: "Lexer::new(0, r#\"(foo + - \"()\" \"a\\\"b\" true false [] 12.2)\"#).collect_vec()" + +--- +[ + Ok( + ( + 0, + LPren, + 1, + ), + ), + Ok( + ( + 1, + Ident( + "foo", + ), + 4, + ), + ), + Ok( + ( + 5, + Plus, + 6, + ), + ), + Ok( + ( + 7, + Minus, + 8, + ), + ), + Ok( + ( + 9, + StrLit( + "\"()\"", + ), + 13, + ), + ), + Ok( + ( + 14, + StrLit( + "\"a\"b\"", + ), + 20, + ), + ), + Ok( + ( + 21, + True, + 25, + ), + ), + Ok( + ( + 26, + False, + 31, + ), + ), + Ok( + ( + 32, + LBrack, + 33, + ), + ), + Ok( + ( + 33, + RBrack, + 34, + ), + ), + Ok( + ( + 35, + NumLit( + "12.2", + ), + 39, + ), + ), + Ok( + ( + 39, + RPren, + 40, + ), + ), +] diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-10.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-10.snap new file mode 100644 index 0000000..c9bfa02 --- /dev/null +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-10.snap @@ -0,0 +1,8 @@ +--- +source: crates/simplexpr/src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, \"\\\"foo\\\" + 12.4\"))" + +--- +Ok( + ("foo" + "12.4"), +) diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-11.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-11.snap new file mode 100644 index 0000000..259a37f --- /dev/null +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-11.snap @@ -0,0 +1,8 @@ +--- +source: crates/simplexpr/src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, \"hi[\\\"ho\\\"]\"))" + +--- +Ok( + hi["ho"], +) diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-12.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-12.snap new file mode 100644 index 0000000..7e3d9da --- /dev/null +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-12.snap @@ -0,0 +1,8 @@ +--- +source: crates/simplexpr/src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, \"foo.bar.baz\"))" + +--- +Ok( + foo["bar"]["baz"], +) diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-13.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-13.snap new file mode 100644 index 0000000..6d69829 --- /dev/null +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-13.snap @@ -0,0 +1,8 @@ +--- +source: crates/simplexpr/src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, \"foo.bar[2 + 2] * asdf[foo.bar]\"))" + +--- +Ok( + (foo["bar"][("2" + "2")] * asdf[foo["bar"]]), +) diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-2.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-2.snap new file mode 100644 index 0000000..a6a77d0 --- /dev/null +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-2.snap @@ -0,0 +1,8 @@ +--- +source: crates/simplexpr/src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, \"2 + 5\"))" + +--- +Ok( + ("2" + "5"), +) diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-3.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-3.snap new file mode 100644 index 0000000..748f054 --- /dev/null +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-3.snap @@ -0,0 +1,8 @@ +--- +source: crates/simplexpr/src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, \"2 * 5 + 1 * 1 + 3\"))" + +--- +Ok( + ((("2" * "5") + ("1" * "1")) + "3"), +) diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-4.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-4.snap new file mode 100644 index 0000000..ae57e55 --- /dev/null +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-4.snap @@ -0,0 +1,8 @@ +--- +source: crates/simplexpr/src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, \"(1 + 2) * 2\"))" + +--- +Ok( + (("1" + "2") * "2"), +) diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-5.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-5.snap new file mode 100644 index 0000000..9ba70aa --- /dev/null +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-5.snap @@ -0,0 +1,8 @@ +--- +source: crates/simplexpr/src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, \"1 + true ? 2 : 5\"))" + +--- +Ok( + (("1" + "true") ? "2" : "5"), +) diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-6.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-6.snap new file mode 100644 index 0000000..92e68d3 --- /dev/null +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-6.snap @@ -0,0 +1,8 @@ +--- +source: crates/simplexpr/src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, \"1 + true ? 2 : 5 + 2\"))" + +--- +Ok( + (("1" + "true") ? "2" : ("5" + "2")), +) diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-7.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-7.snap new file mode 100644 index 0000000..53d78a7 --- /dev/null +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-7.snap @@ -0,0 +1,8 @@ +--- +source: crates/simplexpr/src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, \"1 + (true ? 2 : 5) + 2\"))" + +--- +Ok( + (("1" + ("true" ? "2" : "5")) + "2"), +) diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-8.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-8.snap new file mode 100644 index 0000000..d679378 --- /dev/null +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-8.snap @@ -0,0 +1,8 @@ +--- +source: crates/simplexpr/src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, \"foo(1, 2)\"))" + +--- +Ok( + foo("1", "2"), +) diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-9.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-9.snap new file mode 100644 index 0000000..0513d5d --- /dev/null +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-9.snap @@ -0,0 +1,8 @@ +--- +source: crates/simplexpr/src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, \"! false || ! true\"))" + +--- +Ok( + (!"false" || !"true"), +) diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test.snap new file mode 100644 index 0000000..b5ee229 --- /dev/null +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test.snap @@ -0,0 +1,8 @@ +--- +source: crates/simplexpr/src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, \"1\"))" + +--- +Ok( + "1", +) diff --git a/crates/yuck/src/parser/lexer.rs b/crates/yuck/src/parser/lexer.rs index c8b5012..804dbfa 100644 --- a/crates/yuck/src/parser/lexer.rs +++ b/crates/yuck/src/parser/lexer.rs @@ -58,6 +58,10 @@ macro_rules! regex_rules { } } +lazy_static::lazy_static! { + static ref ESCAPE_REPLACE_REGEX: Regex = Regex::new(r"\\(.)").unwrap(); +} + regex_rules! { r"\(" => |_| Token::LPren, r"\)" => |_| Token::RPren, @@ -65,10 +69,10 @@ regex_rules! { r"\]" => |_| Token::RBrack, r"true" => |_| Token::True, r"false" => |_| Token::False, - r#""(?:[^"\\]|\\.)*""# => |x| Token::StrLit(x), + r#""(?:[^"\\]|\\.)*""# => |x| Token::StrLit(ESCAPE_REPLACE_REGEX.replace_all(&x, "$1").to_string()), r#"[+-]?(?:[0-9]+[.])?[0-9]+"# => |x| Token::NumLit(x), r#":[^\s\)\]}]+"# => |x| Token::Keyword(x), - r#"[a-zA-Z_!\?<>/\.\*-\+][^\s{}\(\)\[\](){}]*"# => |x| Token::Symbol(x), + r#"[a-zA-Z_!\?<>/\.\*-\+\-][^\s{}\(\)\[\](){}]*"# => |x| Token::Symbol(x), r#";.*"# => |_| Token::Comment, r"[ \t\n\f]+" => |_| Token::Skip } @@ -133,6 +137,7 @@ impl Iterator for Lexer { Some(x) => x, None => { self.failed = true; + dbg!(&string); return Some(Err(parse_error::ParseError::LexicalError(Span(self.pos, self.pos, self.file_id)))); } }; @@ -150,3 +155,12 @@ impl Iterator for Lexer { } } } + +#[cfg(test)] +#[test] +fn test_yuck_lexer() { + use itertools::Itertools; + insta::assert_debug_snapshot!(Lexer::new(0, r#"(foo + - "text" )"#.to_string()).collect_vec()); + insta::assert_debug_snapshot!(Lexer::new(0, r#"{ bla "} \" }" " \" "}"#.to_string()).collect_vec()); + insta::assert_debug_snapshot!(Lexer::new(0, r#""< \" >""#.to_string()).collect_vec()); +} diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-2.snap b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-2.snap new file mode 100644 index 0000000..52688dc --- /dev/null +++ b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-2.snap @@ -0,0 +1,16 @@ +--- +source: crates/yuck/src/parser/lexer.rs +expression: "Lexer::new(0, r#\"{ bla \"} \\\" }\" \" \\\" \"}\"#.to_string()).collect_vec()" + +--- +[ + Ok( + ( + 0, + SimplExpr( + "{ bla \"} \\\" }\" \" \\\" \"}", + ), + 21, + ), + ), +] diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-3.snap b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-3.snap new file mode 100644 index 0000000..18607c4 --- /dev/null +++ b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-3.snap @@ -0,0 +1,16 @@ +--- +source: crates/yuck/src/parser/lexer.rs +expression: "Lexer::new(0, r#\"\"< \\\" >\"\"#.to_string()).collect_vec()" + +--- +[ + Ok( + ( + 0, + StrLit( + "\"< \" >\"", + ), + 8, + ), + ), +] diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer.snap b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer.snap new file mode 100644 index 0000000..695431e --- /dev/null +++ b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer.snap @@ -0,0 +1,57 @@ +--- +source: crates/yuck/src/parser/lexer.rs +expression: "Lexer::new(0, r#\"(foo + - \"text\" )\"#.to_string()).collect_vec()" + +--- +[ + Ok( + ( + 0, + LPren, + 1, + ), + ), + Ok( + ( + 1, + Symbol( + "foo", + ), + 4, + ), + ), + Ok( + ( + 5, + Symbol( + "+", + ), + 6, + ), + ), + Ok( + ( + 7, + Symbol( + "-", + ), + 8, + ), + ), + Ok( + ( + 9, + StrLit( + "\"text\"", + ), + 15, + ), + ), + Ok( + ( + 16, + RPren, + 17, + ), + ), +] diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__test-10.snap b/crates/yuck/src/parser/snapshots/yuck__parser__test-10.snap new file mode 100644 index 0000000..d57421a --- /dev/null +++ b/crates/yuck/src/parser/snapshots/yuck__parser__test-10.snap @@ -0,0 +1,8 @@ +--- +source: crates/yuck/src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, \"(lol😄 1)\".to_string()))" + +--- +Ok( + (lol😄 "1"), +) diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__test-11.snap b/crates/yuck/src/parser/snapshots/yuck__parser__test-11.snap new file mode 100644 index 0000000..4f80a03 --- /dev/null +++ b/crates/yuck/src/parser/snapshots/yuck__parser__test-11.snap @@ -0,0 +1,8 @@ +--- +source: crates/yuck/src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, r#\"(test \"hi\")\"#.to_string()))" + +--- +Ok( + (test "hi"), +) diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__test-12.snap b/crates/yuck/src/parser/snapshots/yuck__parser__test-12.snap new file mode 100644 index 0000000..3fcc8ea --- /dev/null +++ b/crates/yuck/src/parser/snapshots/yuck__parser__test-12.snap @@ -0,0 +1,8 @@ +--- +source: crates/yuck/src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, r#\"(test \"h\\\"i\")\"#.to_string()))" + +--- +Ok( + (test "h"i"), +) diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__test-13.snap b/crates/yuck/src/parser/snapshots/yuck__parser__test-13.snap new file mode 100644 index 0000000..8d4d533 --- /dev/null +++ b/crates/yuck/src/parser/snapshots/yuck__parser__test-13.snap @@ -0,0 +1,8 @@ +--- +source: crates/yuck/src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, r#\"(test \" hi \")\"#.to_string()))" + +--- +Ok( + (test " hi "), +) diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__test-14.snap b/crates/yuck/src/parser/snapshots/yuck__parser__test-14.snap new file mode 100644 index 0000000..0975a96 --- /dev/null +++ b/crates/yuck/src/parser/snapshots/yuck__parser__test-14.snap @@ -0,0 +1,8 @@ +--- +source: crates/yuck/src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, \"(+ (1 2 (* 2 5)))\".to_string()))" + +--- +Ok( + (+ ("1" "2" (* "2" "5"))), +) diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__test-15.snap b/crates/yuck/src/parser/snapshots/yuck__parser__test-15.snap new file mode 100644 index 0000000..fbc9f49 --- /dev/null +++ b/crates/yuck/src/parser/snapshots/yuck__parser__test-15.snap @@ -0,0 +1,8 @@ +--- +source: crates/yuck/src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, r#\"foo ; test\"#.to_string()))" + +--- +Ok( + foo, +) diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__test-16.snap b/crates/yuck/src/parser/snapshots/yuck__parser__test-16.snap new file mode 100644 index 0000000..f226f40 --- /dev/null +++ b/crates/yuck/src/parser/snapshots/yuck__parser__test-16.snap @@ -0,0 +1,8 @@ +--- +source: crates/yuck/src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, r#\"(f arg ; test\n arg2)\"#.to_string()))" + +--- +Ok( + (f arg arg2), +) diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__test-17.snap b/crates/yuck/src/parser/snapshots/yuck__parser__test-17.snap new file mode 100644 index 0000000..a2e848a --- /dev/null +++ b/crates/yuck/src/parser/snapshots/yuck__parser__test-17.snap @@ -0,0 +1,8 @@ +--- +source: crates/yuck/src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, \"\\\"h\\\\\\\"i\\\"\".to_string()))" + +--- +Ok( + "h"i", +) diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__test-2.snap b/crates/yuck/src/parser/snapshots/yuck__parser__test-2.snap new file mode 100644 index 0000000..6fec876 --- /dev/null +++ b/crates/yuck/src/parser/snapshots/yuck__parser__test-2.snap @@ -0,0 +1,8 @@ +--- +source: crates/yuck/src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, \"(12)\".to_string()))" + +--- +Ok( + ("12"), +) diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__test-3.snap b/crates/yuck/src/parser/snapshots/yuck__parser__test-3.snap new file mode 100644 index 0000000..6303fc0 --- /dev/null +++ b/crates/yuck/src/parser/snapshots/yuck__parser__test-3.snap @@ -0,0 +1,8 @@ +--- +source: crates/yuck/src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, \"1.2\".to_string()))" + +--- +Ok( + "1.2", +) diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__test-4.snap b/crates/yuck/src/parser/snapshots/yuck__parser__test-4.snap new file mode 100644 index 0000000..d086147 --- /dev/null +++ b/crates/yuck/src/parser/snapshots/yuck__parser__test-4.snap @@ -0,0 +1,8 @@ +--- +source: crates/yuck/src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, \"-1.2\".to_string()))" + +--- +Ok( + "-1.2", +) diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__test-5.snap b/crates/yuck/src/parser/snapshots/yuck__parser__test-5.snap new file mode 100644 index 0000000..6ec6487 --- /dev/null +++ b/crates/yuck/src/parser/snapshots/yuck__parser__test-5.snap @@ -0,0 +1,8 @@ +--- +source: crates/yuck/src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, \"(1 2)\".to_string()))" + +--- +Ok( + ("1" "2"), +) diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__test-6.snap b/crates/yuck/src/parser/snapshots/yuck__parser__test-6.snap new file mode 100644 index 0000000..f6c3cbd --- /dev/null +++ b/crates/yuck/src/parser/snapshots/yuck__parser__test-6.snap @@ -0,0 +1,8 @@ +--- +source: crates/yuck/src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, \"(1 :foo 1)\".to_string()))" + +--- +Ok( + ("1" :foo "1"), +) diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__test-7.snap b/crates/yuck/src/parser/snapshots/yuck__parser__test-7.snap new file mode 100644 index 0000000..e7fc34b --- /dev/null +++ b/crates/yuck/src/parser/snapshots/yuck__parser__test-7.snap @@ -0,0 +1,8 @@ +--- +source: crates/yuck/src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, \"(:foo 1)\".to_string()))" + +--- +Ok( + (:foo "1"), +) diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__test-8.snap b/crates/yuck/src/parser/snapshots/yuck__parser__test-8.snap new file mode 100644 index 0000000..c5edd60 --- /dev/null +++ b/crates/yuck/src/parser/snapshots/yuck__parser__test-8.snap @@ -0,0 +1,8 @@ +--- +source: crates/yuck/src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, \"(:foo->: 1)\".to_string()))" + +--- +Ok( + (:foo->: "1"), +) diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__test-9.snap b/crates/yuck/src/parser/snapshots/yuck__parser__test-9.snap new file mode 100644 index 0000000..d852703 --- /dev/null +++ b/crates/yuck/src/parser/snapshots/yuck__parser__test-9.snap @@ -0,0 +1,8 @@ +--- +source: crates/yuck/src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, \"(foo 1)\".to_string()))" + +--- +Ok( + (foo "1"), +) diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__test.snap b/crates/yuck/src/parser/snapshots/yuck__parser__test.snap new file mode 100644 index 0000000..0d12c19 --- /dev/null +++ b/crates/yuck/src/parser/snapshots/yuck__parser__test.snap @@ -0,0 +1,8 @@ +--- +source: crates/yuck/src/parser/mod.rs +expression: "p.parse(0, Lexer::new(0, \"1\".to_string()))" + +--- +Ok( + "1", +) From ae542b833d9a08c7a5aa52cc06617fce5c9e85b5 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Sat, 24 Jul 2021 14:19:06 +0200 Subject: [PATCH 070/137] Custom codespan_reporting::files implementation, unload files properly for literal tags --- crates/eww/src/config/eww_config.rs | 8 +- crates/eww/src/error_handling_ctx.rs | 7 +- crates/eww/src/widgets/widget_definitions.rs | 33 +++--- crates/yuck/src/config/config.rs | 15 +-- crates/yuck/src/config/file_provider.rs | 103 +++++++++++++++---- crates/yuck/src/error.rs | 4 + crates/yuck/src/format_diagnostic.rs | 4 + crates/yuck/src/parser/mod.rs | 9 ++ 8 files changed, 136 insertions(+), 47 deletions(-) diff --git a/crates/eww/src/config/eww_config.rs b/crates/eww/src/config/eww_config.rs index 1e18eb3..db70e74 100644 --- a/crates/eww/src/config/eww_config.rs +++ b/crates/eww/src/config/eww_config.rs @@ -1,8 +1,8 @@ use anyhow::*; use eww_shared_util::VarName; -use std::collections::HashMap; +use std::{collections::HashMap, path::Path}; use yuck::config::{ - file_provider::FsYuckFiles, script_var_definition::ScriptVarDefinition, widget_definition::WidgetDefinition, Config, + file_provider::YuckFiles, script_var_definition::ScriptVarDefinition, widget_definition::WidgetDefinition, Config, }; use simplexpr::dynval::DynVal; @@ -19,8 +19,8 @@ pub struct EwwConfig { // files: FsYuckFiles, } impl EwwConfig { - pub fn read_from_file>(files: &mut FsYuckFiles, path: P) -> Result { - let config = Config::generate_from_main_file(files, path.as_ref().to_str().context("Failed to decode path")?)?; + pub fn read_from_file(files: &mut YuckFiles, path: impl AsRef) -> Result { + let config = Config::generate_from_main_file(files, path)?; let Config { widget_definitions, window_definitions, var_definitions, script_vars } = config; Ok(EwwConfig { windows: window_definitions diff --git a/crates/eww/src/error_handling_ctx.rs b/crates/eww/src/error_handling_ctx.rs index 2198efb..6e8e305 100644 --- a/crates/eww/src/error_handling_ctx.rs +++ b/crates/eww/src/error_handling_ctx.rs @@ -4,7 +4,7 @@ use codespan_reporting::diagnostic::Diagnostic; use eww_shared_util::DUMMY_SPAN; use simplexpr::eval::EvalError; use yuck::{ - config::file_provider::FsYuckFiles, + config::file_provider::YuckFiles, error::AstError, format_diagnostic::{eval_error_to_diagnostic, ToDiagnostic}, }; @@ -12,13 +12,14 @@ use yuck::{ use crate::error::DiagError; lazy_static::lazy_static! { - pub static ref ERROR_HANDLING_CTX: Arc> = Arc::new(Mutex::new(FsYuckFiles::new())); + pub static ref ERROR_HANDLING_CTX: Arc> = Arc::new(Mutex::new(YuckFiles::new())); } pub fn clear_files() { - *ERROR_HANDLING_CTX.lock().unwrap() = FsYuckFiles::new(); + *ERROR_HANDLING_CTX.lock().unwrap() = YuckFiles::new(); } + pub fn print_error(err: &anyhow::Error) { let result: anyhow::Result<_> = try { if let Some(err) = err.downcast_ref::() { diff --git a/crates/eww/src/widgets/widget_definitions.rs b/crates/eww/src/widgets/widget_definitions.rs index b14c1c2..a769197 100644 --- a/crates/eww/src/widgets/widget_definitions.rs +++ b/crates/eww/src/widgets/widget_definitions.rs @@ -1,6 +1,6 @@ #![allow(clippy::option_map_unit_fn)] use super::{run_command, BuilderArgs}; -use crate::{enum_parse, eww_state, resolve_block, util::list_difference, widgets::widget_node}; +use crate::{enum_parse, error_handling_ctx, eww_state, resolve_block, util::list_difference, widgets::widget_node}; use anyhow::*; use gdk::WindowExt; use glib; @@ -301,11 +301,7 @@ fn build_gtk_checkbox(bargs: &mut BuilderArgs) -> Result { prop(onchecked: as_string = "", onunchecked: as_string = "") { let old_id = on_change_handler_id.replace(Some( gtk_widget.connect_toggled(move |gtk_widget| { - if gtk_widget.get_active() { - run_command(&onchecked, ""); - } else { - run_command(&onunchecked, ""); - } + run_command(if gtk_widget.get_active() { &onchecked } else { &onunchecked }, ""); }) )); old_id.map(|id| gtk_widget.disconnect(id)); @@ -496,17 +492,11 @@ fn build_gtk_label(bargs: &mut BuilderArgs) -> Result { gtk_widget.set_text(&text); }, // @prop markup - Pango markup to display - prop(markup: as_string) { - gtk_widget.set_markup(&markup); - }, + prop(markup: as_string) { gtk_widget.set_markup(&markup); }, // @prop wrap - Wrap the text. This mainly makes sense if you set the width of this widget. - prop(wrap: as_bool) { - gtk_widget.set_line_wrap(wrap) - }, + prop(wrap: as_bool) { gtk_widget.set_line_wrap(wrap) }, // @prop angle - the angle of rotation for the label (between 0 - 360) - prop(angle: as_f64 = 0) { - gtk_widget.set_angle(angle) - } + prop(angle: as_f64 = 0) { gtk_widget.set_angle(angle) } }); Ok(gtk_widget) } @@ -521,12 +511,23 @@ fn build_gtk_literal(bargs: &mut BuilderArgs) -> Result { let window_name = bargs.window_name.to_string(); let widget_definitions = bargs.widget_definitions.clone(); + // the file id the literal-content has been stored under, for error reporting. + let literal_file_id: Rc>> = Rc::new(RefCell::new(None)); + resolve_block!(bargs, gtk_widget, { // @prop content - inline Eww XML that will be rendered as a widget. prop(content: as_string) { gtk_widget.get_children().iter().for_each(|w| gtk_widget.remove(w)); if !content.is_empty() { - let ast = yuck::parser::parse_string(usize::MAX, &content)?; + let ast = { + let mut yuck_files = error_handling_ctx::ERROR_HANDLING_CTX.lock().unwrap(); + let (span, asts) = yuck_files.load_str("".to_string(), content)?; + if let Some(file_id) = literal_file_id.replace(Some(span.2)) { + yuck_files.unload(file_id); + } + yuck::parser::require_single_toplevel(span, asts)? + }; + let content_widget_use = yuck::config::widget_use::WidgetUse::from_ast(ast)?; let widget_node = &*widget_node::generate_generic_widget_node(&widget_definitions, &HashMap::new(), content_widget_use)?; diff --git a/crates/yuck/src/config/config.rs b/crates/yuck/src/config/config.rs index b07021b..81b2113 100644 --- a/crates/yuck/src/config/config.rs +++ b/crates/yuck/src/config/config.rs @@ -1,4 +1,7 @@ -use std::collections::HashMap; +use std::{ + collections::HashMap, + path::{Path, PathBuf}, +}; use codespan_reporting::files::SimpleFiles; use simplexpr::SimplExpr; @@ -77,7 +80,7 @@ pub struct Config { } impl Config { - fn append_toplevel(&mut self, files: &mut impl YuckFiles, toplevel: TopLevel) -> AstResult<()> { + fn append_toplevel(&mut self, files: &mut YuckFiles, toplevel: TopLevel) -> AstResult<()> { match toplevel { TopLevel::VarDefinition(x) => { self.var_definitions.insert(x.name.clone(), x); @@ -92,7 +95,7 @@ impl Config { self.window_definitions.insert(x.name.clone(), x); } TopLevel::Include(include) => { - let (file_id, toplevels) = files.load(&include.path).map_err(|err| match err { + let (file_id, toplevels) = files.load_file(PathBuf::from(&include.path)).map_err(|err| match err { FilesError::IoError(_) => AstError::IncludedFileNotFound(include), FilesError::AstError(x) => x, })?; @@ -104,7 +107,7 @@ impl Config { Ok(()) } - pub fn generate(files: &mut impl YuckFiles, elements: Vec) -> AstResult { + pub fn generate(files: &mut YuckFiles, elements: Vec) -> AstResult { let mut config = Self { widget_definitions: HashMap::new(), window_definitions: HashMap::new(), @@ -117,8 +120,8 @@ impl Config { Ok(config) } - pub fn generate_from_main_file(files: &mut impl YuckFiles, path: &str) -> AstResult { - let (span, top_levels) = files.load(path).map_err(|err| match err { + pub fn generate_from_main_file(files: &mut YuckFiles, path: impl AsRef) -> AstResult { + let (span, top_levels) = files.load_file(path.as_ref().to_path_buf()).map_err(|err| match err { FilesError::IoError(err) => AstError::Other(None, Box::new(err)), FilesError::AstError(x) => x, })?; diff --git a/crates/yuck/src/config/file_provider.rs b/crates/yuck/src/config/file_provider.rs index cfc5588..1b6c333 100644 --- a/crates/yuck/src/config/file_provider.rs +++ b/crates/yuck/src/config/file_provider.rs @@ -17,46 +17,110 @@ pub enum FilesError { AstError(#[from] AstError), } -pub trait YuckFiles { - fn load(&mut self, path: &str) -> Result<(Span, Vec), FilesError>; +#[derive(Clone, Debug)] +pub enum YuckSource { + File(std::path::PathBuf), + Literal(String), +} + +impl YuckSource { + pub fn read_content(&self) -> std::io::Result { + match self { + YuckSource::File(path) => Ok(std::fs::read_to_string(path)?), + YuckSource::Literal(x) => Ok(x.to_string()), + } + } +} + +#[derive(Clone, Debug)] +pub struct YuckFile { + name: String, + line_starts: Vec, + source: YuckSource, + source_len_bytes: usize, +} + +impl YuckFile { + /// Return the starting byte index of the line with the specified line index. + /// Convenience method that already generates errors if necessary. + fn line_start(&self, line_index: usize) -> Result { + use std::cmp::Ordering; + + match line_index.cmp(&self.line_starts.len()) { + Ordering::Less => Ok(self.line_starts.get(line_index).cloned().expect("failed despite previous check")), + Ordering::Equal => Ok(self.source_len_bytes), + Ordering::Greater => { + Err(codespan_reporting::files::Error::LineTooLarge { given: line_index, max: self.line_starts.len() - 1 }) + } + } + } } #[derive(Debug, Clone)] -pub struct FsYuckFiles { - files: SimpleFiles, +pub struct YuckFiles { + files: HashMap, + latest_id: usize, } -impl FsYuckFiles { +impl YuckFiles { pub fn new() -> Self { - Self { files: SimpleFiles::new() } + Self { files: HashMap::new(), latest_id: 0 } } } -impl YuckFiles for FsYuckFiles { - fn load(&mut self, path: &str) -> Result<(Span, Vec), FilesError> { - let path = PathBuf::from(path); +impl YuckFiles { + fn get_file(&self, id: usize) -> Result<&YuckFile, codespan_reporting::files::Error> { + self.files.get(&id).ok_or(codespan_reporting::files::Error::FileMissing) + } + fn insert_file(&mut self, file: YuckFile) -> usize { + let file_id = self.latest_id; + self.files.insert(file_id, file); + self.latest_id += 1; + file_id + } + + pub fn load_file(&mut self, path: std::path::PathBuf) -> Result<(Span, Vec), FilesError> { let file_content = std::fs::read_to_string(&path)?; - let file_id = self.files.add(path.display().to_string(), file_content.to_string()); + let line_starts = codespan_reporting::files::line_starts(&file_content).collect(); + let yuck_file = YuckFile { + name: path.display().to_string(), + line_starts, + source_len_bytes: file_content.len(), + source: YuckSource::File(path), + }; + let file_id = self.insert_file(yuck_file); Ok(crate::parser::parse_toplevel(file_id, file_content)?) } + + pub fn load_str(&mut self, name: String, content: String) -> Result<(Span, Vec), FilesError> { + let line_starts = codespan_reporting::files::line_starts(&content).collect(); + let yuck_file = + YuckFile { name, line_starts, source_len_bytes: content.len(), source: YuckSource::Literal(content.to_string()) }; + let file_id = self.insert_file(yuck_file); + Ok(crate::parser::parse_toplevel(file_id, content)?) + } + + pub fn unload(&mut self, id: usize) { + self.files.remove(&id); + } } -impl<'a> Files<'a> for FsYuckFiles { +impl<'a> Files<'a> for YuckFiles { type FileId = usize; - type Name = String; - type Source = &'a str; + type Name = &'a str; + type Source = String; - fn name(&self, id: Self::FileId) -> Result { - self.files.name(id) + fn name(&'a self, id: Self::FileId) -> Result { + Ok(&self.get_file(id)?.name) } fn source(&'a self, id: Self::FileId) -> Result { - self.files.source(id) + Ok(self.get_file(id)?.source.read_content().map_err(codespan_reporting::files::Error::Io)?) } fn line_index(&self, id: Self::FileId, byte_index: usize) -> Result { - self.files.line_index(id, byte_index) + Ok(self.get_file(id)?.line_starts.binary_search(&byte_index).unwrap_or_else(|next_line| next_line - 1)) } fn line_range( @@ -64,6 +128,9 @@ impl<'a> Files<'a> for FsYuckFiles { id: Self::FileId, line_index: usize, ) -> Result, codespan_reporting::files::Error> { - self.files.line_range(id, line_index) + let file = self.get_file(id)?; + let line_start = file.line_start(line_index)?; + let next_line_start = file.line_start(line_index + 1)?; + Ok(line_start..next_line_start) } } diff --git a/crates/yuck/src/error.rs b/crates/yuck/src/error.rs index 79e1098..7bff8ad 100644 --- a/crates/yuck/src/error.rs +++ b/crates/yuck/src/error.rs @@ -18,6 +18,9 @@ pub enum AstError { UnknownToplevel(Span, String), #[error("Expected another element, but got nothing")] MissingNode(Span), + #[error("Too many elements, must be exactly {1}")] + TooManyNodes(Span, i32), + #[error("Wrong type of expression: Expected {1} but got {2}")] WrongExprType(Span, AstType, AstType), #[error("Expected to get a value, but got {1}")] @@ -60,6 +63,7 @@ impl AstError { AstError::Other(span, ..) => *span, AstError::ConversionError(err) => err.value.span().map(|x| x.into()), AstError::IncludedFileNotFound(include) => Some(include.path_span), + AstError::TooManyNodes(span, ..) => Some(*span), AstError::ValidationError(error) => None, // TODO none here is stupid AstError::ParseError { file_id, source } => file_id.and_then(|id| get_parse_error_span(id, source)), } diff --git a/crates/yuck/src/format_diagnostic.rs b/crates/yuck/src/format_diagnostic.rs index e04f917..82de131 100644 --- a/crates/yuck/src/format_diagnostic.rs +++ b/crates/yuck/src/format_diagnostic.rs @@ -104,6 +104,10 @@ impl ToDiagnostic for AstError { label = include.path_span => "Included here", ), + AstError::TooManyNodes(extra_nodes_span, expected) => gen_diagnostic! { + msg = self, + label = extra_nodes_span => "these elements must not be here. Consider nesting the elements in some container element.", + }, AstError::ValidationError(_) => todo!(), } } else { diff --git a/crates/yuck/src/parser/mod.rs b/crates/yuck/src/parser/mod.rs index 8e5499f..62c7291 100644 --- a/crates/yuck/src/parser/mod.rs +++ b/crates/yuck/src/parser/mod.rs @@ -33,6 +33,15 @@ pub fn parse_toplevel(file_id: usize, s: String) -> AstResult<(Span, Vec)> parser.parse(file_id, lexer).map_err(|e| AstError::from_parse_error(file_id, e)) } +/// get a single ast node from a list of asts, returning an Err if the length is not exactly 1. +pub fn require_single_toplevel(span: Span, mut asts: Vec) -> AstResult { + match asts.len() { + 0 => Err(AstError::MissingNode(span)), + 1 => Ok(asts.remove(0)), + _ => Err(AstError::TooManyNodes(asts.get(1).unwrap().span().to(asts.last().unwrap().span()), 1)), + } +} + macro_rules! test_parser { ($($text:literal),*) => {{ let p = parser::AstParser::new(); From 08cc28194b2cca7c82e07143eaf3c253fbd1f09a Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Sat, 24 Jul 2021 16:48:37 +0200 Subject: [PATCH 071/137] Improve error messages for literal --- crates/eww/src/app.rs | 2 +- crates/eww/src/config/eww_config.rs | 3 +- crates/eww/src/error_handling_ctx.rs | 43 +++++++----- crates/eww/src/eww_state.rs | 6 +- crates/eww/src/main.rs | 2 +- crates/eww/src/script_var_handler.rs | 15 +++-- crates/eww/src/widgets/widget_definitions.rs | 37 +++++++---- crates/eww/src/widgets/widget_node.rs | 14 ++-- crates/eww_shared_util/src/span.rs | 11 +++ crates/yuck/src/config/file_provider.rs | 2 +- crates/yuck/src/config/validate.rs | 2 +- crates/yuck/src/config/widget_use.rs | 10 +++ crates/yuck/src/error.rs | 32 ++++++++- crates/yuck/src/format_diagnostic.rs | 70 ++++++++++++++------ 14 files changed, 179 insertions(+), 70 deletions(-) diff --git a/crates/eww/src/app.rs b/crates/eww/src/app.rs index 137d405..058a2a1 100644 --- a/crates/eww/src/app.rs +++ b/crates/eww/src/app.rs @@ -193,7 +193,7 @@ impl App { }; if let Err(err) = result { - error_handling_ctx::print_error(&err); + error_handling_ctx::print_error(err); } } diff --git a/crates/eww/src/config/eww_config.rs b/crates/eww/src/config/eww_config.rs index db70e74..0e766b8 100644 --- a/crates/eww/src/config/eww_config.rs +++ b/crates/eww/src/config/eww_config.rs @@ -16,8 +16,8 @@ pub struct EwwConfig { windows: HashMap, initial_variables: HashMap, script_vars: HashMap, - // files: FsYuckFiles, } + impl EwwConfig { pub fn read_from_file(files: &mut YuckFiles, path: impl AsRef) -> Result { let config = Config::generate_from_main_file(files, path)?; @@ -30,7 +30,6 @@ impl EwwConfig { widgets: widget_definitions, initial_variables: var_definitions.into_iter().map(|(k, v)| (k, v.initial_value)).collect(), script_vars, - // files, }) } diff --git a/crates/eww/src/error_handling_ctx.rs b/crates/eww/src/error_handling_ctx.rs index 6e8e305..c451b4a 100644 --- a/crates/eww/src/error_handling_ctx.rs +++ b/crates/eww/src/error_handling_ctx.rs @@ -1,12 +1,13 @@ use std::sync::{Arc, Mutex}; -use codespan_reporting::diagnostic::Diagnostic; +use codespan_reporting::{diagnostic::Diagnostic, term, term::Chars}; use eww_shared_util::DUMMY_SPAN; use simplexpr::eval::EvalError; use yuck::{ config::file_provider::YuckFiles, error::AstError, format_diagnostic::{eval_error_to_diagnostic, ToDiagnostic}, + gen_diagnostic, }; use crate::error::DiagError; @@ -19,34 +20,44 @@ pub fn clear_files() { *ERROR_HANDLING_CTX.lock().unwrap() = YuckFiles::new(); } +pub fn anyhow_err_to_diagnostic(err: &anyhow::Error) -> Diagnostic { + if let Some(err) = err.downcast_ref::() { + err.diag.clone() + } else if let Some(err) = err.downcast_ref::() { + err.to_diagnostic() + } else if let Some(err) = err.downcast_ref::() { + eval_error_to_diagnostic(err, err.span().unwrap_or(DUMMY_SPAN)) + } else { + gen_diagnostic!(err) + } +} -pub fn print_error(err: &anyhow::Error) { - let result: anyhow::Result<_> = try { - if let Some(err) = err.downcast_ref::() { - eprintln!("{:?}\n{}", err, stringify_diagnostic(&err.diag)?); - } else if let Some(err) = err.downcast_ref::() { - eprintln!("{:?}\n{}", err, stringify_diagnostic(&err.to_diagnostic())?); - } else if let Some(err) = err.downcast_ref::() { - eprintln!("{:?}\n{}", err, stringify_diagnostic(&eval_error_to_diagnostic(err, err.span().unwrap_or(DUMMY_SPAN)))?); - } else { +pub fn print_error(err: anyhow::Error) { + match stringify_diagnostic(&anyhow_err_to_diagnostic(&err)) { + Ok(diag) => { + eprintln!("{:?}\n{}", err, diag); + } + Err(_) => { log::error!("{:?}", err); } - }; - if result.is_err() { - log::error!("{:?}", err); } } pub fn format_error(err: &anyhow::Error) -> String { + for err in err.chain() { + format!("chain: {}", err); + } match err.downcast_ref::() { Some(err) => stringify_diagnostic(&err.to_diagnostic()).unwrap_or_else(|_| format!("{:?}", err)), None => format!("{:?}", err), } } -pub fn stringify_diagnostic(diagnostic: &Diagnostic) -> anyhow::Result { - use codespan_reporting::term; - let config = term::Config::default(); +pub fn stringify_diagnostic(diagnostic: &codespan_reporting::diagnostic::Diagnostic) -> anyhow::Result { + let mut config = term::Config::default(); + let mut chars = Chars::box_drawing(); + chars.single_primary_caret = '─'; + config.chars = chars; let mut buf = Vec::new(); let mut writer = term::termcolor::Ansi::new(&mut buf); let files = ERROR_HANDLING_CTX.lock().unwrap(); diff --git a/crates/eww/src/eww_state.rs b/crates/eww/src/eww_state.rs index 25c6640..5ea82e5 100644 --- a/crates/eww/src/eww_state.rs +++ b/crates/eww/src/eww_state.rs @@ -30,12 +30,12 @@ impl StateChangeHandler { match resolved_attrs { Ok(resolved_attrs) => { - if let Err(err) = &(self.func)(resolved_attrs).context("Error while updating UI after state change") { - error_handling_ctx::print_error(&err); + if let Err(err) = (self.func)(resolved_attrs).context("Error while updating UI after state change") { + error_handling_ctx::print_error(err); } } Err(err) => { - error_handling_ctx::print_error(&err); + error_handling_ctx::print_error(err); } } } diff --git a/crates/eww/src/main.rs b/crates/eww/src/main.rs index f90edfb..5ccb48a 100644 --- a/crates/eww/src/main.rs +++ b/crates/eww/src/main.rs @@ -94,7 +94,7 @@ fn main() { }; if let Err(e) = result { - error_handling_ctx::print_error(&e); + error_handling_ctx::print_error(e); std::process::exit(1); } } diff --git a/crates/eww/src/script_var_handler.rs b/crates/eww/src/script_var_handler.rs index 8ec7522..c4bfa9c 100644 --- a/crates/eww/src/script_var_handler.rs +++ b/crates/eww/src/script_var_handler.rs @@ -135,7 +135,9 @@ impl PollVarHandler { let result: Result<_> = try { evt_send.send(app::DaemonCommand::UpdateVars(vec![(var.name.clone(), run_poll_once(&var)?)]))?; }; - crate::print_result_err!("while running script-var command", &result); + if let Err(err) = result { + crate::error_handling_ctx::print_error(err); + } crate::loop_select_exiting! { _ = cancellation_token.cancelled() => break, @@ -143,7 +145,10 @@ impl PollVarHandler { let result: Result<_> = try { evt_send.send(app::DaemonCommand::UpdateVars(vec![(var.name.clone(), run_poll_once(&var)?)]))?; }; - crate::print_result_err!("while running script-var command", &result); + + if let Err(err) = result { + crate::error_handling_ctx::print_error(err); + } } } }); @@ -163,9 +168,9 @@ impl PollVarHandler { fn run_poll_once(var: &PollScriptVar) -> Result { match &var.command { - VarSource::Shell(span, x) => crate::config::script_var::run_command(x).map_err(|_| { - anyhow!(create_script_var_failed_error(*span, &var.name)) - }), + VarSource::Shell(span, x) => { + crate::config::script_var::run_command(x).map_err(|_| anyhow!(create_script_var_failed_error(*span, &var.name))) + } VarSource::Function(x) => x().map_err(|e| anyhow!(e)), } } diff --git a/crates/eww/src/widgets/widget_definitions.rs b/crates/eww/src/widgets/widget_definitions.rs index a769197..15b6b75 100644 --- a/crates/eww/src/widgets/widget_definitions.rs +++ b/crates/eww/src/widgets/widget_definitions.rs @@ -6,7 +6,11 @@ use gdk::WindowExt; use glib; use gtk::{self, prelude::*, ImageExt}; use std::{cell::RefCell, collections::HashMap, rc::Rc, time::Duration}; -use yuck::{config::validate::ValidationError, error::AstError, parser::from_ast::FromAst}; +use yuck::{ + config::validate::ValidationError, + error::{AstError, AstResult, ResultExt}, + parser::from_ast::FromAst, +}; // TODO figure out how to // TODO https://developer.gnome.org/gtk3/stable/GtkFixed.html @@ -510,6 +514,7 @@ fn build_gtk_literal(bargs: &mut BuilderArgs) -> Result { // TODO these clones here are dumdum let window_name = bargs.window_name.to_string(); let widget_definitions = bargs.widget_definitions.clone(); + let literal_use_span = bargs.widget.span; // the file id the literal-content has been stored under, for error reporting. let literal_file_id: Rc>> = Rc::new(RefCell::new(None)); @@ -519,19 +524,27 @@ fn build_gtk_literal(bargs: &mut BuilderArgs) -> Result { prop(content: as_string) { gtk_widget.get_children().iter().for_each(|w| gtk_widget.remove(w)); if !content.is_empty() { - let ast = { - let mut yuck_files = error_handling_ctx::ERROR_HANDLING_CTX.lock().unwrap(); - let (span, asts) = yuck_files.load_str("".to_string(), content)?; - if let Some(file_id) = literal_file_id.replace(Some(span.2)) { - yuck_files.unload(file_id); - } - yuck::parser::require_single_toplevel(span, asts)? + let widget_node_result: AstResult<_> = try { + let ast = { + let mut yuck_files = error_handling_ctx::ERROR_HANDLING_CTX.lock().unwrap(); + let (span, asts) = yuck_files.load_str("".to_string(), content)?; + if let Some(file_id) = literal_file_id.replace(Some(span.2)) { + yuck_files.unload(file_id); + } + yuck::parser::require_single_toplevel(span, asts)? + }; + + let content_widget_use = yuck::config::widget_use::WidgetUse::from_ast(ast)?; + widget_node::generate_generic_widget_node(&widget_definitions, &HashMap::new(), content_widget_use)? }; - let content_widget_use = yuck::config::widget_use::WidgetUse::from_ast(ast)?; - - let widget_node = &*widget_node::generate_generic_widget_node(&widget_definitions, &HashMap::new(), content_widget_use)?; - let child_widget = widget_node.render(&mut eww_state::EwwState::default(), &window_name, &widget_definitions)?; + let widget_node = widget_node_result.context_label(literal_use_span, "Error in the literal used here")?; + let child_widget = widget_node.render(&mut eww_state::EwwState::default(), &window_name, &widget_definitions) + .map_err(|e| AstError::ErrorContext { + label_span: literal_use_span, + context: "Error in the literal used here".to_string(), + main_err: Box::new(error_handling_ctx::anyhow_err_to_diagnostic(&e)) + })?; gtk_widget.add(&child_widget); child_widget.show(); } diff --git a/crates/eww/src/widgets/widget_node.rs b/crates/eww/src/widgets/widget_node.rs index 215689c..0f2c3bc 100644 --- a/crates/eww/src/widgets/widget_node.rs +++ b/crates/eww/src/widgets/widget_node.rs @@ -6,7 +6,7 @@ use simplexpr::SimplExpr; use std::collections::HashMap; use yuck::{ config::{validate::ValidationError, widget_definition::WidgetDefinition, widget_use::WidgetUse}, - error::AstError, + error::{AstError, AstResult}, }; pub trait WidgetNode: std::fmt::Debug + dyn_clone::DynClone + Send + Sync { @@ -108,16 +108,18 @@ pub fn generate_generic_widget_node( defs: &HashMap, local_env: &HashMap, w: WidgetUse, -) -> Result> { +) -> AstResult> { if let Some(def) = defs.get(&w.name) { - ensure!(w.children.is_empty(), "User-defined widgets cannot be given children."); + if !w.children.is_empty() { + Err(AstError::TooManyNodes(w.children_span(), 0).note("User-defined widgets cannot be given children."))? + } let new_local_env = w .attrs .attrs .into_iter() .map(|(name, value)| Ok((VarName(name.0), value.value.as_simplexpr()?.resolve_one_level(local_env)))) - .collect::>>()?; + .collect::>>()?; let content = generate_generic_widget_node(defs, &new_local_env, def.widget.clone())?; Ok(Box::new(UserDefined { name: w.name, span: Some(w.span), content })) @@ -131,13 +133,13 @@ pub fn generate_generic_widget_node( .attrs .into_iter() .map(|(name, value)| Ok((name, value.value.as_simplexpr()?.resolve_one_level(local_env)))) - .collect::>>()?, + .collect::>>()?, children: w .children .into_iter() .map(|child| generate_generic_widget_node(defs, local_env, child)) - .collect::>>()?, + .collect::>>()?, })) } } diff --git a/crates/eww_shared_util/src/span.rs b/crates/eww_shared_util/src/span.rs index 589f295..1e0a693 100644 --- a/crates/eww_shared_util/src/span.rs +++ b/crates/eww_shared_util/src/span.rs @@ -21,6 +21,17 @@ impl Span { self.1 = self.0; self } + + /// Turn this span into a span only highlighting the point it ends at, setting the length to 0. + pub fn point_span_at_end(mut self) -> Self { + self.0 = self.1; + self + } + pub fn shifted(mut self, n: isize) -> Self { + self.0 = isize::max(0, self.0 as isize + n) as usize; + self.1 = isize::max(0, self.0 as isize + n) as usize; + self + } } impl std::fmt::Display for Span { diff --git a/crates/yuck/src/config/file_provider.rs b/crates/yuck/src/config/file_provider.rs index 1b6c333..52581ff 100644 --- a/crates/yuck/src/config/file_provider.rs +++ b/crates/yuck/src/config/file_provider.rs @@ -93,7 +93,7 @@ impl YuckFiles { Ok(crate::parser::parse_toplevel(file_id, file_content)?) } - pub fn load_str(&mut self, name: String, content: String) -> Result<(Span, Vec), FilesError> { + pub fn load_str(&mut self, name: String, content: String) -> Result<(Span, Vec), AstError> { let line_starts = codespan_reporting::files::line_starts(&content).collect(); let yuck_file = YuckFile { name, line_starts, source_len_bytes: content.len(), source: YuckSource::Literal(content.to_string()) }; diff --git a/crates/yuck/src/config/validate.rs b/crates/yuck/src/config/validate.rs index 73a4630..9356dea 100644 --- a/crates/yuck/src/config/validate.rs +++ b/crates/yuck/src/config/validate.rs @@ -12,7 +12,7 @@ use eww_shared_util::{AttrName, Span, VarName}; #[derive(Debug, thiserror::Error)] pub enum ValidationError { - #[error("Unknown widget referenced: {1}")] + #[error("Unknown widget `{1}` referenced")] UnknownWidget(Span, String), #[error("Missing attribute `{arg_name}` in use of widget `{widget_name}`")] diff --git a/crates/yuck/src/config/widget_use.rs b/crates/yuck/src/config/widget_use.rs index efea534..96f7769 100644 --- a/crates/yuck/src/config/widget_use.rs +++ b/crates/yuck/src/config/widget_use.rs @@ -20,6 +20,16 @@ pub struct WidgetUse { pub name_span: Span, } +impl WidgetUse { + pub fn children_span(&self) -> Span { + if self.children.is_empty() { + self.span.point_span_at_end().shifted(-1) + } else { + self.children.first().unwrap().span.to(self.children.last().unwrap().span) + } + } +} + impl FromAst for WidgetUse { fn from_ast(e: Ast) -> AstResult { let span = e.span(); diff --git a/crates/yuck/src/error.rs b/crates/yuck/src/error.rs index 7bff8ad..bbeefab 100644 --- a/crates/yuck/src/error.rs +++ b/crates/yuck/src/error.rs @@ -1,5 +1,6 @@ use crate::{ config::{attributes::AttrError, config::Include, validate::ValidationError}, + format_diagnostic::ToDiagnostic, parser::{ ast::{Ast, AstType}, lexer, parse_error, @@ -31,6 +32,11 @@ pub enum AstError { #[error("Included file not found {}", .0.path)] IncludedFileNotFound(Include), + #[error("{}", .main_err.to_message())] + ErrorContext { label_span: Span, context: String, main_err: Box }, + #[error("{1}")] + ErrorNote(String, #[source] Box), + #[error(transparent)] ConversionError(#[from] dynval::ConversionError), @@ -47,11 +53,19 @@ pub enum AstError { ParseError { file_id: Option, source: lalrpop_util::ParseError }, } -// static_assertions::assert_impl_all!(AstError: Send, Sync); -// static_assertions::assert_impl_all!(dynval::ConversionError: Send, Sync); -// static_assertions::assert_impl_all!(lalrpop_util::ParseError < usize, lexer::Token, parse_error::ParseError>: Send, Sync); +static_assertions::assert_impl_all!(AstError: Send, Sync); +static_assertions::assert_impl_all!(dynval::ConversionError: Send, Sync); +static_assertions::assert_impl_all!(lalrpop_util::ParseError < usize, lexer::Token, parse_error::ParseError>: Send, Sync); impl AstError { + pub fn note(self, note: &str) -> Self { + AstError::ErrorNote(note.to_string(), Box::new(self)) + } + + pub fn context_label(self, label_span: Span, context: &str) -> Self { + AstError::ErrorContext { label_span, context: context.to_string(), main_err: Box::new(self) } + } + pub fn get_span(&self) -> Option { match self { AstError::UnknownToplevel(span, _) => Some(*span), @@ -64,8 +78,10 @@ impl AstError { AstError::ConversionError(err) => err.value.span().map(|x| x.into()), AstError::IncludedFileNotFound(include) => Some(include.path_span), AstError::TooManyNodes(span, ..) => Some(*span), + AstError::ErrorContext { label_span, .. } => Some(*label_span), AstError::ValidationError(error) => None, // TODO none here is stupid AstError::ParseError { file_id, source } => file_id.and_then(|id| get_parse_error_span(id, source)), + AstError::ErrorNote(_, err) => err.get_span(), } } @@ -101,3 +117,13 @@ impl OptionAstErrorExt for Option { self.ok_or(AstError::MissingNode(span)) } } + +pub trait ResultExt { + fn context_label(self, label_span: Span, context: &str) -> AstResult; +} + +impl ResultExt for AstResult { + fn context_label(self, label_span: Span, context: &str) -> AstResult { + self.map_err(|e| AstError::ErrorContext { label_span, context: context.to_string(), main_err: Box::new(e) }) + } +} diff --git a/crates/yuck/src/format_diagnostic.rs b/crates/yuck/src/format_diagnostic.rs index 82de131..86de768 100644 --- a/crates/yuck/src/format_diagnostic.rs +++ b/crates/yuck/src/format_diagnostic.rs @@ -3,7 +3,10 @@ use simplexpr::dynval; use diagnostic::*; -use crate::error::AstError; +use crate::{ + config::{attributes::AttrError, validate::ValidationError}, + error::AstError, +}; use super::parser::parse_error; use eww_shared_util::{AttrName, Span, VarName}; @@ -25,7 +28,7 @@ macro_rules! gen_diagnostic { ::codespan_reporting::diagnostic::Label::primary($span.2, $span.0..$span.1) $(.with_message($label))? ]))? - $(.with_notes(vec![$note]))? + $(.with_notes(vec![$note.to_string()]))? }; ($msg:expr $(, $span:expr $(,)?)?) => {{ ::codespan_reporting::diagnostic::Diagnostic::error() @@ -48,26 +51,23 @@ impl DiagnosticExt for Diagnostic { } } -pub trait ToDiagnostic { +pub trait ToDiagnostic: std::fmt::Debug { fn to_diagnostic(&self) -> Diagnostic; + fn to_message(&self) -> String { + self.to_diagnostic().message + } } +impl ToDiagnostic for Diagnostic { + fn to_diagnostic(&self) -> Diagnostic { + self.clone() + } +} impl ToDiagnostic for AstError { fn to_diagnostic(&self) -> Diagnostic { + // TODO this if let should be unnecessary if let AstError::ValidationError(error) = self { - match error { - crate::config::validate::ValidationError::UnknownWidget(span, name) => gen_diagnostic! { - msg = format!("No widget named {} exists", name), - label = span => "Used here", - }, - crate::config::validate::ValidationError::MissingAttr { widget_name, arg_name, arg_list_span, use_span } => { - let diag = gen_diagnostic! { - msg = format!("{}", error), - }; - diag.with_opt_label(Some(span_to_secondary_label(*use_span).with_message("Argument missing here"))) - .with_opt_label(arg_list_span.map(|s| span_to_secondary_label(s).with_message("but is required here"))) - } - } + error.to_diagnostic() } else if let Some(span) = self.get_span() { match self { AstError::UnknownToplevel(_, name) => gen_diagnostic!(format!("{}", self), span), @@ -96,6 +96,10 @@ impl ToDiagnostic for AstError { label = span => format!("Expected `{}` here", expected), note = format!("Expected: {}\nGot: {}", expected, got), }, + AstError::ErrorContext { label_span, context, main_err } => { + main_err.to_diagnostic().with_opt_label(Some(span_to_secondary_label(*label_span).with_message(context))) + } + AstError::ConversionError(err) => conversion_error_to_diagnostic(err, span), AstError::Other(_, source) => gen_diagnostic!(source, span), AstError::AttrError(source) => gen_diagnostic!(source, span), @@ -106,9 +110,11 @@ impl ToDiagnostic for AstError { AstError::TooManyNodes(extra_nodes_span, expected) => gen_diagnostic! { msg = self, - label = extra_nodes_span => "these elements must not be here. Consider nesting the elements in some container element.", + label = extra_nodes_span => "these elements must not be here", + note = "Consider wrapping the elements in some container element", }, - AstError::ValidationError(_) => todo!(), + AstError::ErrorNote(note, source) => source.to_diagnostic().with_notes(vec![note.to_string()]), + AstError::ValidationError(source) => source.to_diagnostic(), } } else { Diagnostic::error().with_message(format!("{}", self)) @@ -116,6 +122,32 @@ impl ToDiagnostic for AstError { } } +impl ToDiagnostic for AttrError { + fn to_diagnostic(&self) -> Diagnostic { + match self { + AttrError::MissingRequiredAttr(span, attr_name) => { + gen_diagnostic!(format!("Missing attribute `{}`", attr_name), span) + } + AttrError::EvaluationError(span, source) => eval_error_to_diagnostic(source, *span), + AttrError::Other(span, source) => gen_diagnostic!(source, span), + } + } +} + +impl ToDiagnostic for ValidationError { + fn to_diagnostic(&self) -> Diagnostic { + match self { + ValidationError::UnknownWidget(span, name) => gen_diagnostic! { + msg = self, + label = span => "Used here", + }, + ValidationError::MissingAttr { widget_name, arg_name, arg_list_span, use_span } => gen_diagnostic!(self) + .with_opt_label(Some(span_to_secondary_label(*use_span).with_message("Argument missing here"))) + .with_opt_label(arg_list_span.map(|s| span_to_secondary_label(s).with_message("but is required here"))), + } + } +} + fn lalrpop_error_to_diagnostic( error: &lalrpop_util::ParseError, span: Span, @@ -156,7 +188,7 @@ pub fn eval_error_to_diagnostic(error: &simplexpr::eval::EvalError, span: Span) fn conversion_error_to_diagnostic(error: &dynval::ConversionError, span: Span) -> Diagnostic { let diag = gen_diagnostic! { - msg = format!("{}", error), + msg = error, label = span => format!("{} is not of type `{}`", error.value, error.target_type), }; diag.with_notes(error.source.as_ref().map(|x| vec![format!("{}", x)]).unwrap_or_default()) From ed73938af121942d113a3b5184fbe5ca8dbfa7d0 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Sat, 24 Jul 2021 17:56:44 +0200 Subject: [PATCH 072/137] implement ToDiagnostic for more things --- crates/eww/src/error_handling_ctx.rs | 19 ++-- crates/eww/src/main.rs | 1 + crates/eww_shared_util/src/span.rs | 8 ++ crates/simplexpr/src/error.rs | 2 +- crates/simplexpr/src/parser/lexer.rs | 1 + crates/yuck/src/config/validate.rs | 9 ++ crates/yuck/src/error.rs | 18 ++-- crates/yuck/src/format_diagnostic.rs | 143 ++++++++++++++++---------- crates/yuck/src/parser/parse_error.rs | 14 ++- crates/yuck/src/parser/parser.lalrpop | 2 +- 10 files changed, 134 insertions(+), 83 deletions(-) diff --git a/crates/eww/src/error_handling_ctx.rs b/crates/eww/src/error_handling_ctx.rs index c451b4a..86f8ad4 100644 --- a/crates/eww/src/error_handling_ctx.rs +++ b/crates/eww/src/error_handling_ctx.rs @@ -1,14 +1,9 @@ use std::sync::{Arc, Mutex}; use codespan_reporting::{diagnostic::Diagnostic, term, term::Chars}; -use eww_shared_util::DUMMY_SPAN; +use eww_shared_util::Span; use simplexpr::eval::EvalError; -use yuck::{ - config::file_provider::YuckFiles, - error::AstError, - format_diagnostic::{eval_error_to_diagnostic, ToDiagnostic}, - gen_diagnostic, -}; +use yuck::{config::file_provider::YuckFiles, error::AstError, format_diagnostic::ToDiagnostic, gen_diagnostic}; use crate::error::DiagError; @@ -26,14 +21,14 @@ pub fn anyhow_err_to_diagnostic(err: &anyhow::Error) -> Diagnostic { } else if let Some(err) = err.downcast_ref::() { err.to_diagnostic() } else if let Some(err) = err.downcast_ref::() { - eval_error_to_diagnostic(err, err.span().unwrap_or(DUMMY_SPAN)) + err.to_diagnostic() } else { gen_diagnostic!(err) } } pub fn print_error(err: anyhow::Error) { - match stringify_diagnostic(&anyhow_err_to_diagnostic(&err)) { + match stringify_diagnostic(anyhow_err_to_diagnostic(&err)) { Ok(diag) => { eprintln!("{:?}\n{}", err, diag); } @@ -48,12 +43,14 @@ pub fn format_error(err: &anyhow::Error) -> String { format!("chain: {}", err); } match err.downcast_ref::() { - Some(err) => stringify_diagnostic(&err.to_diagnostic()).unwrap_or_else(|_| format!("{:?}", err)), + Some(err) => stringify_diagnostic(err.to_diagnostic()).unwrap_or_else(|_| format!("{:?}", err)), None => format!("{:?}", err), } } -pub fn stringify_diagnostic(diagnostic: &codespan_reporting::diagnostic::Diagnostic) -> anyhow::Result { +pub fn stringify_diagnostic(mut diagnostic: codespan_reporting::diagnostic::Diagnostic) -> anyhow::Result { + diagnostic.labels.drain_filter(|label| Span(label.range.start, label.range.end, label.file_id).is_dummy()); + let mut config = term::Config::default(); let mut chars = Chars::box_drawing(); chars.single_primary_caret = '─'; diff --git a/crates/eww/src/main.rs b/crates/eww/src/main.rs index 5ccb48a..2b35cab 100644 --- a/crates/eww/src/main.rs +++ b/crates/eww/src/main.rs @@ -1,4 +1,5 @@ #![feature(trace_macros)] +#![feature(drain_filter)] #![feature(box_syntax)] #![feature(box_patterns)] #![feature(slice_concat_trait)] diff --git a/crates/eww_shared_util/src/span.rs b/crates/eww_shared_util/src/span.rs index 1e0a693..7bde1fb 100644 --- a/crates/eww_shared_util/src/span.rs +++ b/crates/eww_shared_util/src/span.rs @@ -3,6 +3,10 @@ pub struct Span(pub usize, pub usize, pub usize); pub static DUMMY_SPAN: Span = Span(usize::MAX, usize::MAX, usize::MAX); impl Span { + pub fn point(loc: usize, file_id: usize) -> Self { + Span(loc, loc, file_id) + } + /// Get the span that includes this and the other span completely. /// Will panic if the spans are from different file_ids. pub fn to(mut self, other: Span) -> Self { @@ -32,6 +36,10 @@ impl Span { self.1 = isize::max(0, self.0 as isize + n) as usize; self } + + pub fn is_dummy(&self) -> bool { + *self == DUMMY_SPAN + } } impl std::fmt::Display for Span { diff --git a/crates/simplexpr/src/error.rs b/crates/simplexpr/src/error.rs index 76bbbbc..62300c1 100644 --- a/crates/simplexpr/src/error.rs +++ b/crates/simplexpr/src/error.rs @@ -14,7 +14,7 @@ pub enum Error { ConversionError(#[from] dynval::ConversionError), #[error("{1}")] - Spanned(Span, Box), + Spanned(Span, Box), #[error(transparent)] Eval(#[from] crate::eval::EvalError), diff --git a/crates/simplexpr/src/parser/lexer.rs b/crates/simplexpr/src/parser/lexer.rs index 27bbff4..f3e5891 100644 --- a/crates/simplexpr/src/parser/lexer.rs +++ b/crates/simplexpr/src/parser/lexer.rs @@ -48,6 +48,7 @@ pub enum Token { Error, } +// TODO can i include the fileid here already? #[derive(Debug, Eq, PartialEq, Copy, Clone)] pub struct LexicalError(pub usize, pub usize); diff --git a/crates/yuck/src/config/validate.rs b/crates/yuck/src/config/validate.rs index 9356dea..fc7c420 100644 --- a/crates/yuck/src/config/validate.rs +++ b/crates/yuck/src/config/validate.rs @@ -19,6 +19,15 @@ pub enum ValidationError { MissingAttr { widget_name: String, arg_name: AttrName, arg_list_span: Option, use_span: Span }, } +impl ValidationError { + pub fn span(&self) -> Span { + match self { + ValidationError::UnknownWidget(span, _) => *span, + ValidationError::MissingAttr { use_span, .. } => *use_span, + } + } +} + pub fn validate(defs: &HashMap, content: &WidgetUse) -> Result<(), ValidationError> { if let Some(def) = defs.get(&content.name) { for expected in def.expected_args.iter() { diff --git a/crates/yuck/src/error.rs b/crates/yuck/src/error.rs index bbeefab..3b20877 100644 --- a/crates/yuck/src/error.rs +++ b/crates/yuck/src/error.rs @@ -50,7 +50,7 @@ pub enum AstError { ValidationError(#[from] ValidationError), #[error("Parse error: {source}")] - ParseError { file_id: Option, source: lalrpop_util::ParseError }, + ParseError { file_id: usize, source: lalrpop_util::ParseError }, } static_assertions::assert_impl_all!(AstError: Send, Sync); @@ -79,8 +79,8 @@ impl AstError { AstError::IncludedFileNotFound(include) => Some(include.path_span), AstError::TooManyNodes(span, ..) => Some(*span), AstError::ErrorContext { label_span, .. } => Some(*label_span), - AstError::ValidationError(error) => None, // TODO none here is stupid - AstError::ParseError { file_id, source } => file_id.and_then(|id| get_parse_error_span(id, source)), + AstError::ValidationError(error) => Some(error.span()), + AstError::ParseError { file_id, source } => get_parse_error_span(*file_id, source, |err| err.span()), AstError::ErrorNote(_, err) => err.get_span(), } } @@ -89,23 +89,21 @@ impl AstError { file_id: usize, err: lalrpop_util::ParseError, ) -> AstError { - AstError::ParseError { file_id: Some(file_id), source: err } + AstError::ParseError { file_id, source: err } } } -fn get_parse_error_span( +pub fn get_parse_error_span( file_id: usize, - err: &lalrpop_util::ParseError, + err: &lalrpop_util::ParseError, + handle_user: impl FnOnce(&E) -> Option, ) -> Option { 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 } => match error { - parse_error::ParseError::SimplExpr(span, error) => *span, - parse_error::ParseError::LexicalError(span) => Some(*span), - }, + lalrpop_util::ParseError::User { error } => handle_user(error), } } diff --git a/crates/yuck/src/format_diagnostic.rs b/crates/yuck/src/format_diagnostic.rs index 86de768..a2f3cd0 100644 --- a/crates/yuck/src/format_diagnostic.rs +++ b/crates/yuck/src/format_diagnostic.rs @@ -5,7 +5,7 @@ use diagnostic::*; use crate::{ config::{attributes::AttrError, validate::ValidationError}, - error::AstError, + error::{get_parse_error_span, AstError}, }; use super::parser::parse_error; @@ -68,30 +68,27 @@ impl ToDiagnostic for AstError { // TODO this if let should be unnecessary if let AstError::ValidationError(error) = self { error.to_diagnostic() - } else if let Some(span) = self.get_span() { + } else { match self { - AstError::UnknownToplevel(_, name) => gen_diagnostic!(format!("{}", self), span), - AstError::MissingNode(_) => gen_diagnostic! { + AstError::UnknownToplevel(span, name) => gen_diagnostic!(self, span), + AstError::MissingNode(span) => gen_diagnostic! { msg = "Expected another element", label = span => "Expected another element here", }, - AstError::WrongExprType(_, expected, actual) => gen_diagnostic! { + AstError::WrongExprType(span, expected, actual) => gen_diagnostic! { msg = "Wrong type of expression", label = span => format!("Expected a `{}` here", expected), note = format!("Expected: {}\nGot: {}", expected, actual), }, - AstError::NotAValue(_, actual) => gen_diagnostic! { + AstError::NotAValue(span, actual) => gen_diagnostic! { msg = format!("Expected value, but got `{}`", actual), label = span => "Expected some value here", note = format!("Got: {}", actual), }, - AstError::ParseError { file_id, source } => lalrpop_error_to_diagnostic(source, span, |error| match error { - parse_error::ParseError::SimplExpr(_, error) => simplexpr_error_to_diagnostic(error, span), - parse_error::ParseError::LexicalError(span) => lexical_error_to_diagnostic(*span), - }), - AstError::MismatchedElementName(_, expected, got) => gen_diagnostic! { + AstError::ParseError { file_id, source } => lalrpop_error_to_diagnostic(source, *file_id, |error| error.to_diagnostic()), + AstError::MismatchedElementName(span, expected, got) => gen_diagnostic! { msg = format!("Expected element `{}`, but found `{}`", expected, got), label = span => format!("Expected `{}` here", expected), note = format!("Expected: {}\nGot: {}", expected, got), @@ -100,9 +97,10 @@ impl ToDiagnostic for AstError { main_err.to_diagnostic().with_opt_label(Some(span_to_secondary_label(*label_span).with_message(context))) } - AstError::ConversionError(err) => conversion_error_to_diagnostic(err, span), - AstError::Other(_, source) => gen_diagnostic!(source, span), - AstError::AttrError(source) => gen_diagnostic!(source, span), + AstError::ConversionError(source) => source.to_diagnostic(), + AstError::Other(Some(span), source) => gen_diagnostic!(source, span), + AstError::Other(None, source) => gen_diagnostic!(source), + AstError::AttrError(source) => source.to_diagnostic(), AstError::IncludedFileNotFound(include) => gen_diagnostic!( msg = format!("Included file `{}` not found", include.path), label = include.path_span => "Included here", @@ -116,8 +114,15 @@ impl ToDiagnostic for AstError { AstError::ErrorNote(note, source) => source.to_diagnostic().with_notes(vec![note.to_string()]), AstError::ValidationError(source) => source.to_diagnostic(), } - } else { - Diagnostic::error().with_message(format!("{}", self)) + } + } +} + +impl ToDiagnostic for parse_error::ParseError { + fn to_diagnostic(&self) -> Diagnostic { + match self { + parse_error::ParseError::SimplExpr(error) => error.to_diagnostic(), + parse_error::ParseError::LexicalError(span) => lexical_error_diagnostic(*span), } } } @@ -128,7 +133,7 @@ impl ToDiagnostic for AttrError { AttrError::MissingRequiredAttr(span, attr_name) => { gen_diagnostic!(format!("Missing attribute `{}`", attr_name), span) } - AttrError::EvaluationError(span, source) => eval_error_to_diagnostic(source, *span), + AttrError::EvaluationError(span, source) => source.to_diagnostic(), AttrError::Other(span, source) => gen_diagnostic!(source, span), } } @@ -148,53 +153,77 @@ impl ToDiagnostic for ValidationError { } } -fn lalrpop_error_to_diagnostic( +fn lalrpop_error_to_diagnostic( error: &lalrpop_util::ParseError, - span: Span, + file_id: usize, handle_user_error: impl FnOnce(&E) -> Diagnostic, ) -> Diagnostic { use lalrpop_util::ParseError::*; - match error { - InvalidToken { location } => gen_diagnostic! { msg = "Invalid token", label = span }, - UnrecognizedEOF { location, expected } => gen_diagnostic! { - msg = "Input ended unexpectedly. Check if you have any unclosed delimiters", - label = span - }, - UnrecognizedToken { token, expected } => gen_diagnostic! { - msg = format!("Unexpected token `{}` encountered", token.1), - label = span => "Token unexpected", - }, - ExtraToken { token } => gen_diagnostic!(format!("Extra token encountered: `{}`", token.1)), - User { error } => handle_user_error(error), - } -} - -// TODO this needs a lot of improvement -pub fn simplexpr_error_to_diagnostic(error: &simplexpr::error::Error, span: Span) -> Diagnostic { - use simplexpr::error::Error::*; - match error { - ParseError { source, .. } => lalrpop_error_to_diagnostic(source, span, move |error| lexical_error_to_diagnostic(span)), - ConversionError(error) => conversion_error_to_diagnostic(error, span), - Eval(error) => eval_error_to_diagnostic(error, span), - Other(error) => gen_diagnostic!(error, span), - Spanned(span, error) => gen_diagnostic!(error, span), - } -} - -// TODO this needs a lot of improvement -pub fn eval_error_to_diagnostic(error: &simplexpr::eval::EvalError, span: Span) -> Diagnostic { - gen_diagnostic!(error, error.span().unwrap_or(span)) -} - -fn conversion_error_to_diagnostic(error: &dynval::ConversionError, span: Span) -> Diagnostic { - let diag = gen_diagnostic! { - msg = error, - label = span => format!("{} is not of type `{}`", error.value, error.target_type), + // None is okay here, as the case that would be affected by it (User { error }) is manually handled here anyways + let span = get_parse_error_span(file_id, error, |e| None); + let res: Option<_> = try { + match error { + InvalidToken { location } => gen_diagnostic!("Invalid token", span?), + UnrecognizedEOF { location, expected } => gen_diagnostic! { + "Input ended unexpectedly. Check if you have any unclosed delimiters", + span? + }, + UnrecognizedToken { token, expected } => gen_diagnostic! { + msg = format!("Unexpected token `{}` encountered", token.1), + label = span? => "Token unexpected", + }, + ExtraToken { token } => gen_diagnostic!(format!("Extra token encountered: `{}`", token.1)), + User { error } => handle_user_error(error), + } }; - diag.with_notes(error.source.as_ref().map(|x| vec![format!("{}", x)]).unwrap_or_default()) + res.unwrap_or_else(|| gen_diagnostic!(error)) } -fn lexical_error_to_diagnostic(span: Span) -> Diagnostic { +impl ToDiagnostic for simplexpr::error::Error { + // TODO this needs a lot of improvement + fn to_diagnostic(&self) -> Diagnostic { + use simplexpr::error::Error::*; + let res: Option<_> = try { + match self { + ParseError { source, file_id } => { + let span = get_parse_error_span(*file_id, source, |e| Some(Span(e.0, e.1, *file_id)))?; + lalrpop_error_to_diagnostic(source, *file_id, move |error| lexical_error_diagnostic(span)) + } + ConversionError(error) => error.to_diagnostic(), + Eval(error) => error.to_diagnostic(), + Other(error) => gen_diagnostic!(error), + Spanned(span, error) => gen_diagnostic!(error, span), + } + }; + res.unwrap_or_else(|| gen_diagnostic!(self)) + } +} + +impl ToDiagnostic for simplexpr::eval::EvalError { + // TODO this needs a lot of improvement + fn to_diagnostic(&self) -> Diagnostic { + match self.span() { + Some(span) => gen_diagnostic!(self, span), + None => gen_diagnostic!(self), + } + } +} + +impl ToDiagnostic for dynval::ConversionError { + fn to_diagnostic(&self) -> Diagnostic { + let diag = match self.span() { + Some(span) => gen_diagnostic! { + msg = self, + label = span => format!("`{}` is not of type `{}`", self.value, self.target_type), + }, + None => gen_diagnostic!(self), + }; + diag.with_notes(self.source.as_ref().map(|x| vec![format!("{}", x)]).unwrap_or_default()) + } +} + +/// Generate a simple diagnostic indicating a lexical error +fn lexical_error_diagnostic(span: Span) -> Diagnostic { gen_diagnostic! { msg = "Invalid token", label = span => "Invalid token" diff --git a/crates/yuck/src/parser/parse_error.rs b/crates/yuck/src/parser/parse_error.rs index b5f0816..c2a6007 100644 --- a/crates/yuck/src/parser/parse_error.rs +++ b/crates/yuck/src/parser/parse_error.rs @@ -2,9 +2,17 @@ use eww_shared_util::{AttrName, Span, VarName}; #[derive(Debug, thiserror::Error)] pub enum ParseError { - #[error("{1}")] - SimplExpr(Option, simplexpr::error::Error), - + #[error("{0}")] + SimplExpr(simplexpr::error::Error), #[error("Unknown token")] LexicalError(Span), } + +impl ParseError { + pub fn span(&self) -> Option { + match self { + ParseError::SimplExpr(err) => err.get_span(), + ParseError::LexicalError(span) => Some(*span), + } + } +} diff --git a/crates/yuck/src/parser/parser.lalrpop b/crates/yuck/src/parser/parser.lalrpop index 848120d..a1d30d5 100644 --- a/crates/yuck/src/parser/parser.lalrpop +++ b/crates/yuck/src/parser/parser.lalrpop @@ -60,7 +60,7 @@ SimplExpr: SimplExpr = { =>? { let expr = x[1..x.len() - 1].to_string(); simplexpr::parse_string(l + 1, file_id, &expr).map_err(|e| { - ParseError::User { error: parse_error::ParseError::SimplExpr(e.get_span(), e) }}) + ParseError::User { error: parse_error::ParseError::SimplExpr(e) }}) } } From 44d7ea7a097daab38f294d32572e44d06a2bf549 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Mon, 26 Jul 2021 17:53:30 +0200 Subject: [PATCH 073/137] Fix wayland build by setting default-features=false in workspace member --- crates/eww/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/eww/Cargo.toml b/crates/eww/Cargo.toml index 42dee41..f18f26d 100644 --- a/crates/eww/Cargo.toml +++ b/crates/eww/Cargo.toml @@ -70,7 +70,7 @@ notify = "5.0.0-pre.7" codespan-reporting = "0.11" simplexpr = { path = "../simplexpr" } -yuck = { path = "../yuck" } +yuck = { path = "../yuck", default-features = false} eww_shared_util = { path = "../eww_shared_util" } From ee933fccf5e0dd1e9f7f5d8e8d204625bebd5558 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Mon, 26 Jul 2021 18:48:15 +0200 Subject: [PATCH 074/137] Readd some tests and as_vec dynval transformation --- crates/eww/src/widgets/widget_definitions.rs | 13 ++- crates/eww/src/widgets/widget_node.rs | 39 -------- crates/simplexpr/src/dynval.rs | 75 +++++++-------- .../simplexpr__dynval__test__parse_vec-2.snap | 10 ++ .../simplexpr__dynval__test__parse_vec-3.snap | 12 +++ .../simplexpr__dynval__test__parse_vec-4.snap | 10 ++ .../simplexpr__dynval__test__parse_vec-5.snap | 11 +++ .../simplexpr__dynval__test__parse_vec-6.snap | 8 ++ .../simplexpr__dynval__test__parse_vec-7.snap | 12 +++ .../simplexpr__dynval__test__parse_vec-8.snap | 12 +++ .../simplexpr__dynval__test__parse_vec.snap | 10 ++ .../snapshots/yuck__config__test__config.snap | 93 +++++++++++++++++++ crates/yuck/src/config/test.rs | 27 +++--- 13 files changed, 232 insertions(+), 100 deletions(-) create mode 100644 crates/simplexpr/src/snapshots/simplexpr__dynval__test__parse_vec-2.snap create mode 100644 crates/simplexpr/src/snapshots/simplexpr__dynval__test__parse_vec-3.snap create mode 100644 crates/simplexpr/src/snapshots/simplexpr__dynval__test__parse_vec-4.snap create mode 100644 crates/simplexpr/src/snapshots/simplexpr__dynval__test__parse_vec-5.snap create mode 100644 crates/simplexpr/src/snapshots/simplexpr__dynval__test__parse_vec-6.snap create mode 100644 crates/simplexpr/src/snapshots/simplexpr__dynval__test__parse_vec-7.snap create mode 100644 crates/simplexpr/src/snapshots/simplexpr__dynval__test__parse_vec-8.snap create mode 100644 crates/simplexpr/src/snapshots/simplexpr__dynval__test__parse_vec.snap create mode 100644 crates/yuck/src/config/snapshots/yuck__config__test__config.snap diff --git a/crates/eww/src/widgets/widget_definitions.rs b/crates/eww/src/widgets/widget_definitions.rs index 15b6b75..4ea9b03 100644 --- a/crates/eww/src/widgets/widget_definitions.rs +++ b/crates/eww/src/widgets/widget_definitions.rs @@ -247,13 +247,12 @@ fn build_gtk_combo_box_text(bargs: &mut BuilderArgs) -> Result>> = Rc::new(RefCell::new(None)); resolve_block!(bargs, gtk_widget, { // @prop items - Items that should be displayed in the combo box - // TODO reimplement, obviously - //prop(items: as_vec) { - //gtk_widget.remove_all(); - //for i in items { - //gtk_widget.append_text(&i); - //} - //}, + prop(items: as_vec) { + gtk_widget.remove_all(); + for i in items { + gtk_widget.append_text(&i); + } + }, // @prop onchange - runs the code when a item was selected, replacing {} with the item as a string prop(onchange: as_string) { let old_id = on_change_handler_id.replace(Some( diff --git a/crates/eww/src/widgets/widget_node.rs b/crates/eww/src/widgets/widget_node.rs index 0f2c3bc..ff675cd 100644 --- a/crates/eww/src/widgets/widget_node.rs +++ b/crates/eww/src/widgets/widget_node.rs @@ -143,42 +143,3 @@ pub fn generate_generic_widget_node( })) } } - -//#[cfg(test)] -// mod test { -// use super::*; -// use crate::config::xml_ext::*; -// use maplit::hashmap; -//#[test] -// fn test_generic_generate() { -// let w_def1 = { -// let input = r#"{{nested1}}{{raw1}}"#; -// let document = roxmltree::Document::parse(input).unwrap(); -// let xml = XmlNode::from(document.root_element().clone()); -// WidgetDefinition::from_xml_element(&xml.as_element().unwrap()).unwrap() -//}; -// let w_def2 = { -// let input = r#""#; -// let document = roxmltree::Document::parse(input).unwrap(); -// let xml = XmlNode::from(document.root_element().clone()); -// WidgetDefinition::from_xml_element(&xml.as_element().unwrap()).unwrap() -//}; -// let w_use = { -// let input = r#""#; -// let document = roxmltree::Document::parse(input).unwrap(); -// let xml = XmlNode::from(document.root_element().clone()); -// WidgetUse::from_xml_node(xml).unwrap() -//}; - -// let generic = generate_generic_widget_node( -//&hashmap! { "foo".to_string() => w_def1, "bar".to_string() => w_def2 }, -//&HashMap::new(), -// w_use, -//) -//.unwrap(); - -//// TODO actually implement this test ._. - -// dbg!(&generic); -//// panic!("REEEEEEEEEE") -//} diff --git a/crates/simplexpr/src/dynval.rs b/crates/simplexpr/src/dynval.rs index 62b71bb..27edfc7 100644 --- a/crates/simplexpr/src/dynval.rs +++ b/crates/simplexpr/src/dynval.rs @@ -173,24 +173,30 @@ impl DynVal { } } - // pub fn as_vec(&self) -> Result> { - // match self.0.strip_prefix('[').and_then(|x| x.strip_suffix(']')) { - // Some(content) => { - // let mut items: Vec = 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_vec(&self) -> Result> { + if self.0.is_empty() { + Ok(Vec::new()) + } else { + match self.0.strip_prefix('[').and_then(|x| x.strip_suffix(']')) { + Some(content) => { + let mut items: Vec = 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::from_str::(&self.0) @@ -200,25 +206,16 @@ impl DynVal { #[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"); - //} + use super::*; + #[test] + fn test_parse_vec() { + insta::assert_debug_snapshot!(DynVal::from_string("[]".to_string()).as_vec()); + insta::assert_debug_snapshot!(DynVal::from_string("[hi]".to_string()).as_vec()); + insta::assert_debug_snapshot!(DynVal::from_string("[hi,ho,hu]".to_string()).as_vec()); + insta::assert_debug_snapshot!(DynVal::from_string("[hi\\,ho]".to_string()).as_vec()); + insta::assert_debug_snapshot!(DynVal::from_string("[hi\\,ho,hu]".to_string()).as_vec()); + insta::assert_debug_snapshot!(DynVal::from_string("".to_string()).as_vec()); + insta::assert_debug_snapshot!(DynVal::from_string("[a,b".to_string()).as_vec()); + insta::assert_debug_snapshot!(DynVal::from_string("a]".to_string()).as_vec()); + } } diff --git a/crates/simplexpr/src/snapshots/simplexpr__dynval__test__parse_vec-2.snap b/crates/simplexpr/src/snapshots/simplexpr__dynval__test__parse_vec-2.snap new file mode 100644 index 0000000..bc9aa76 --- /dev/null +++ b/crates/simplexpr/src/snapshots/simplexpr__dynval__test__parse_vec-2.snap @@ -0,0 +1,10 @@ +--- +source: crates/simplexpr/src/dynval.rs +expression: "DynVal::from_string(\"[hi]\".to_string()).as_vec()" + +--- +Ok( + [ + "hi", + ], +) diff --git a/crates/simplexpr/src/snapshots/simplexpr__dynval__test__parse_vec-3.snap b/crates/simplexpr/src/snapshots/simplexpr__dynval__test__parse_vec-3.snap new file mode 100644 index 0000000..62819a1 --- /dev/null +++ b/crates/simplexpr/src/snapshots/simplexpr__dynval__test__parse_vec-3.snap @@ -0,0 +1,12 @@ +--- +source: crates/simplexpr/src/dynval.rs +expression: "DynVal::from_string(\"[hi,ho,hu]\".to_string()).as_vec()" + +--- +Ok( + [ + "hi", + "ho", + "hu", + ], +) diff --git a/crates/simplexpr/src/snapshots/simplexpr__dynval__test__parse_vec-4.snap b/crates/simplexpr/src/snapshots/simplexpr__dynval__test__parse_vec-4.snap new file mode 100644 index 0000000..485cc90 --- /dev/null +++ b/crates/simplexpr/src/snapshots/simplexpr__dynval__test__parse_vec-4.snap @@ -0,0 +1,10 @@ +--- +source: crates/simplexpr/src/dynval.rs +expression: "DynVal::from_string(\"[hi\\\\,ho]\".to_string()).as_vec()" + +--- +Ok( + [ + "hi,ho", + ], +) diff --git a/crates/simplexpr/src/snapshots/simplexpr__dynval__test__parse_vec-5.snap b/crates/simplexpr/src/snapshots/simplexpr__dynval__test__parse_vec-5.snap new file mode 100644 index 0000000..8b754d4 --- /dev/null +++ b/crates/simplexpr/src/snapshots/simplexpr__dynval__test__parse_vec-5.snap @@ -0,0 +1,11 @@ +--- +source: crates/simplexpr/src/dynval.rs +expression: "DynVal::from_string(\"[hi\\\\,ho,hu]\".to_string()).as_vec()" + +--- +Ok( + [ + "hi,ho", + "hu", + ], +) diff --git a/crates/simplexpr/src/snapshots/simplexpr__dynval__test__parse_vec-6.snap b/crates/simplexpr/src/snapshots/simplexpr__dynval__test__parse_vec-6.snap new file mode 100644 index 0000000..a458990 --- /dev/null +++ b/crates/simplexpr/src/snapshots/simplexpr__dynval__test__parse_vec-6.snap @@ -0,0 +1,8 @@ +--- +source: crates/simplexpr/src/dynval.rs +expression: "DynVal::from_string(\"\".to_string()).as_vec()" + +--- +Ok( + [], +) diff --git a/crates/simplexpr/src/snapshots/simplexpr__dynval__test__parse_vec-7.snap b/crates/simplexpr/src/snapshots/simplexpr__dynval__test__parse_vec-7.snap new file mode 100644 index 0000000..c02e4e6 --- /dev/null +++ b/crates/simplexpr/src/snapshots/simplexpr__dynval__test__parse_vec-7.snap @@ -0,0 +1,12 @@ +--- +source: crates/simplexpr/src/dynval.rs +expression: "DynVal::from_string(\"[a,b\".to_string()).as_vec()" + +--- +Err( + ConversionError { + value: "[a,b", + target_type: "vec", + source: None, + }, +) diff --git a/crates/simplexpr/src/snapshots/simplexpr__dynval__test__parse_vec-8.snap b/crates/simplexpr/src/snapshots/simplexpr__dynval__test__parse_vec-8.snap new file mode 100644 index 0000000..47e8caf --- /dev/null +++ b/crates/simplexpr/src/snapshots/simplexpr__dynval__test__parse_vec-8.snap @@ -0,0 +1,12 @@ +--- +source: crates/simplexpr/src/dynval.rs +expression: "DynVal::from_string(\"a]\".to_string()).as_vec()" + +--- +Err( + ConversionError { + value: "a]", + target_type: "vec", + source: None, + }, +) diff --git a/crates/simplexpr/src/snapshots/simplexpr__dynval__test__parse_vec.snap b/crates/simplexpr/src/snapshots/simplexpr__dynval__test__parse_vec.snap new file mode 100644 index 0000000..b5d5e2c --- /dev/null +++ b/crates/simplexpr/src/snapshots/simplexpr__dynval__test__parse_vec.snap @@ -0,0 +1,10 @@ +--- +source: crates/simplexpr/src/dynval.rs +expression: "DynVal::from_string(\"[]\".to_string()).as_vec()" + +--- +Ok( + [ + "", + ], +) diff --git a/crates/yuck/src/config/snapshots/yuck__config__test__config.snap b/crates/yuck/src/config/snapshots/yuck__config__test__config.snap new file mode 100644 index 0000000..d633ab4 --- /dev/null +++ b/crates/yuck/src/config/snapshots/yuck__config__test__config.snap @@ -0,0 +1,93 @@ +--- +source: crates/yuck/src/config/test.rs +expression: config.unwrap() + +--- +Config( + widget_definitions: { + "bar": WidgetDefinition( + name: "bar", + expected_args: [ + AttrName("arg"), + AttrName("arg2"), + ], + widget: WidgetUse( + name: "foo", + attrs: Attributes( + span: Span(47, 47, 51), + attrs: { + AttrName("arg"): AttrEntry( + key_span: Span(52, 56, 0), + value: Literal(Span(57, 61, 0), DynVal("hi", None)), + ), + }, + ), + children: [], + span: Span(47, 62, 0), + name_span: Span(48, 51, 0), + ), + span: Span(9, 63, 0), + args_span: Span(24, 34, 0), + ), + }, + window_definitions: { + "some-window": WindowDefinition( + name: "some-window", + geometry: Some(WindowGeometry( + anchor_point: AnchorPoint( + x: START, + y: START, + ), + offset: Coords( + x: Pixels(0), + y: Pixels(0), + ), + size: Coords( + x: Percent(12), + y: Pixels(20), + ), + )), + stacking: Foreground, + monitor_number: Some(12), + widget: WidgetUse( + name: "bar", + attrs: Attributes( + span: Span(463, 463, 467), + attrs: { + AttrName("arg"): AttrEntry( + key_span: Span(468, 472, 0), + value: Literal(Span(473, 478, 0), DynVal("bla", None)), + ), + }, + ), + children: [], + span: Span(463, 479, 0), + name_span: Span(464, 467, 0), + ), + resizable: true, + backend_options: BackendWindowOptions( + wm_ignore: false, + sticky: true, + window_type: Dock, + struts: StrutDefinition( + side: Left, + dist: Pixels(30), + ), + ), + ), + }, + var_definitions: { + VarName("some_var"): VarDefinition( + name: VarName("some_var"), + initial_value: DynVal("bla", None), + span: Span(72, 95, 0), + ), + }, + script_vars: { + VarName("stuff"): Listen(ListenScriptVar( + name: VarName("stuff"), + command: "tail -f stuff", + command_span: Span(168, 183, 0), + )), + }, +) diff --git a/crates/yuck/src/config/test.rs b/crates/yuck/src/config/test.rs index fcf0963..1af2a61 100644 --- a/crates/yuck/src/config/test.rs +++ b/crates/yuck/src/config/test.rs @@ -3,31 +3,28 @@ use crate::{ parser::{self, ast::Ast, from_ast::FromAst, lexer::Lexer}, }; +use super::file_provider::YuckFiles; + #[test] fn test_config() { let input = r#" - (defwidget foo [arg] - "heyho") (defwidget bar [arg arg2] - "bla") + (foo :arg "hi")) (defvar some_var "bla") - (defpollvar stuff :interval "12s" "date") - (deftailvar stuff "tail -f stuff") + (defpoll stuff :interval "12s" "date") + (deflisten stuff "tail -f stuff") (defwindow some-window :stacking "fg" :monitor 12 :resizable true :geometry (geometry :width "12%" :height "20px") :reserve (struts :side "left" :distance "30px") - (foo :arg "bla")) + (bar :arg "bla")) "#; - - let lexer = Lexer::new(0, input.to_string()); - let p = parser::parser::ToplevelParser::new(); - let (span, parse_result) = p.parse(0, lexer).unwrap(); - // TODO implement another YuckFiles thing to test here again - // let config = Config::from_ast(Ast::List(span, parse_result)); - // insta::with_settings!({sort_maps => true}, { - // insta::assert_ron_snapshot!(config.unwrap()); - //}); + let mut files = YuckFiles::new(); + let (span, asts) = files.load_str("config.yuck".to_string(), input.to_string()).unwrap(); + let config = Config::generate(&mut files, asts); + insta::with_settings!({sort_maps => true}, { + insta::assert_ron_snapshot!(config.unwrap()); + }); } From bb741f343db6832fec955a56ceca45c9d4b5ffc0 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Mon, 26 Jul 2021 19:22:05 +0200 Subject: [PATCH 075/137] Include file_id in LexicalError --- crates/simplexpr/src/error.rs | 2 +- crates/simplexpr/src/parser/lexer.rs | 11 ++++++----- crates/simplexpr/src/parser/mod.rs | 4 ++-- crates/simplexpr/src/simplexpr_parser.lalrpop | 2 +- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/crates/simplexpr/src/error.rs b/crates/simplexpr/src/error.rs index 62300c1..920addf 100644 --- a/crates/simplexpr/src/error.rs +++ b/crates/simplexpr/src/error.rs @@ -52,7 +52,7 @@ fn get_parse_error_span( 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)), + lalrpop_util::ParseError::User { error: LexicalError(l, r, file_id) } => Some(Span(*l, *r, *file_id)), } } diff --git a/crates/simplexpr/src/parser/lexer.rs b/crates/simplexpr/src/parser/lexer.rs index f3e5891..022a82a 100644 --- a/crates/simplexpr/src/parser/lexer.rs +++ b/crates/simplexpr/src/parser/lexer.rs @@ -50,7 +50,7 @@ pub enum Token { // TODO can i include the fileid here already? #[derive(Debug, Eq, PartialEq, Copy, Clone)] -pub struct LexicalError(pub usize, pub usize); +pub struct LexicalError(pub usize, pub usize, pub usize); impl std::fmt::Display for LexicalError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -63,11 +63,12 @@ pub type SpannedResult = Result<(Loc, Tok, Loc), Error>; pub struct Lexer<'input> { lexer: logos::SpannedIter<'input, Token>, byte_offset: usize, + file_id: usize, } impl<'input> Lexer<'input> { - pub fn new(byte_offset: usize, text: &'input str) -> Self { - Lexer { lexer: logos::Lexer::new(text).spanned(), byte_offset } + pub fn new(file_id: usize, byte_offset: usize, text: &'input str) -> Self { + Lexer { lexer: logos::Lexer::new(text).spanned(), byte_offset, file_id } } } @@ -78,7 +79,7 @@ impl<'input> Iterator for Lexer<'input> { let (token, range) = self.lexer.next()?; let range = (range.start + self.byte_offset, range.end + self.byte_offset); if token == Token::Error { - Some(Err(LexicalError(range.0, range.1))) + Some(Err(LexicalError(range.0, range.1, self.file_id))) } else { Some(Ok((range.0, token, range.1))) } @@ -89,5 +90,5 @@ impl<'input> Iterator for Lexer<'input> { #[test] fn test_simplexpr_lexer() { use itertools::Itertools; - insta::assert_debug_snapshot!(Lexer::new(0, r#"(foo + - "()" "a\"b" true false [] 12.2)"#).collect_vec()); + insta::assert_debug_snapshot!(Lexer::new(0, 0, r#"(foo + - "()" "a\"b" true false [] 12.2)"#).collect_vec()); } diff --git a/crates/simplexpr/src/parser/mod.rs b/crates/simplexpr/src/parser/mod.rs index 169490a..f6a2d9b 100644 --- a/crates/simplexpr/src/parser/mod.rs +++ b/crates/simplexpr/src/parser/mod.rs @@ -7,7 +7,7 @@ use crate::{ }; pub fn parse_string(byte_offset: usize, file_id: usize, s: &str) -> Result { - let lexer = lexer::Lexer::new(byte_offset, s); + let lexer = lexer::Lexer::new(file_id, byte_offset, s); let parser = crate::simplexpr_parser::ExprParser::new(); parser.parse(file_id, lexer).map_err(|e| Error::from_parse_error(file_id, e)) } @@ -20,7 +20,7 @@ mod tests { use crate::parser::lexer::Lexer; ::insta::with_settings!({sort_maps => true}, { $( - ::insta::assert_debug_snapshot!(p.parse(0, Lexer::new(0, $text))); + ::insta::assert_debug_snapshot!(p.parse(0, Lexer::new(0, 0, $text))); )* }); }} diff --git a/crates/simplexpr/src/simplexpr_parser.lalrpop b/crates/simplexpr/src/simplexpr_parser.lalrpop index 9620aac..8025af6 100644 --- a/crates/simplexpr/src/simplexpr_parser.lalrpop +++ b/crates/simplexpr/src/simplexpr_parser.lalrpop @@ -63,7 +63,7 @@ pub Expr: SimplExpr = { #[precedence(level="0")] "lexer_error" =>? { - Err(ParseError::User { error: LexicalError(l, r) }) + Err(ParseError::User { error: LexicalError(l, r, fid) }) }, , From 16ebf7683a976ae8a6ada4dec805d77167c25db1 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Mon, 26 Jul 2021 19:39:44 +0200 Subject: [PATCH 076/137] Get rid of Option by replacing it with dummy spans --- crates/eww/src/widgets/widget_node.rs | 6 +- crates/eww_shared_util/src/span.rs | 5 +- crates/simplexpr/src/ast.rs | 8 +- crates/simplexpr/src/dynval.rs | 22 ++-- crates/simplexpr/src/error.rs | 21 ++-- crates/simplexpr/src/eval.rs | 6 +- crates/yuck/src/config/config.rs | 2 +- crates/yuck/src/error.rs | 40 +++---- crates/yuck/src/format_diagnostic.rs | 154 ++++++++++++-------------- crates/yuck/src/parser/parse_error.rs | 6 +- 10 files changed, 126 insertions(+), 144 deletions(-) diff --git a/crates/eww/src/widgets/widget_node.rs b/crates/eww/src/widgets/widget_node.rs index ff675cd..d870644 100644 --- a/crates/eww/src/widgets/widget_node.rs +++ b/crates/eww/src/widgets/widget_node.rs @@ -11,7 +11,7 @@ use yuck::{ pub trait WidgetNode: std::fmt::Debug + dyn_clone::DynClone + Send + Sync { fn get_name(&self) -> &str; - fn get_span(&self) -> Option; + fn span(&self) -> Option; /// Generate a [gtk::Widget] from a [element::WidgetUse]. /// @@ -41,7 +41,7 @@ impl WidgetNode for UserDefined { &self.name } - fn get_span(&self) -> Option { + fn span(&self) -> Option { self.span } @@ -88,7 +88,7 @@ impl WidgetNode for Generic { &self.name } - fn get_span(&self) -> Option { + fn span(&self) -> Option { Some(self.span) } diff --git a/crates/eww_shared_util/src/span.rs b/crates/eww_shared_util/src/span.rs index 7bde1fb..a97b263 100644 --- a/crates/eww_shared_util/src/span.rs +++ b/crates/eww_shared_util/src/span.rs @@ -1,8 +1,9 @@ #[derive(Eq, PartialEq, Clone, Copy, serde::Serialize, serde::Deserialize)] pub struct Span(pub usize, pub usize, pub usize); -pub static DUMMY_SPAN: Span = Span(usize::MAX, usize::MAX, usize::MAX); impl Span { + pub const DUMMY: Span = Span(usize::MAX, usize::MAX, usize::MAX); + pub fn point(loc: usize, file_id: usize) -> Self { Span(loc, loc, file_id) } @@ -38,7 +39,7 @@ impl Span { } pub fn is_dummy(&self) -> bool { - *self == DUMMY_SPAN + *self == Self::DUMMY } } diff --git a/crates/simplexpr/src/ast.rs b/crates/simplexpr/src/ast.rs index b0c282d..8d1092b 100644 --- a/crates/simplexpr/src/ast.rs +++ b/crates/simplexpr/src/ast.rs @@ -1,5 +1,5 @@ use crate::dynval::DynVal; -use eww_shared_util::{Span, DUMMY_SPAN}; +use eww_shared_util::Span; use itertools::Itertools; use serde::{Deserialize, Serialize}; @@ -57,17 +57,17 @@ impl std::fmt::Display for SimplExpr { } impl SimplExpr { pub fn literal(span: Span, s: String) -> Self { - Self::Literal(span, DynVal(s, Some(span))) + Self::Literal(span, DynVal(s, span)) } /// Construct a synthetic simplexpr from a literal string, without adding any relevant span information (uses [DUMMY_SPAN]) pub fn synth_string(s: String) -> Self { - Self::Literal(DUMMY_SPAN, DynVal(s, Some(DUMMY_SPAN))) + Self::Literal(Span::DUMMY, DynVal(s, Span::DUMMY)) } /// Construct a synthetic simplexpr from a literal dynval, without adding any relevant span information (uses [DUMMY_SPAN]) pub fn synth_literal>(s: T) -> Self { - Self::Literal(DUMMY_SPAN, s.into()) + Self::Literal(Span::DUMMY, s.into()) } pub fn span(&self) -> Span { diff --git a/crates/simplexpr/src/dynval.rs b/crates/simplexpr/src/dynval.rs index 27edfc7..ebba340 100644 --- a/crates/simplexpr/src/dynval.rs +++ b/crates/simplexpr/src/dynval.rs @@ -18,17 +18,17 @@ impl ConversionError { ConversionError { value, target_type, source: Some(Box::new(source)) } } - pub fn span(&self) -> Option { + pub fn span(&self) -> Span { self.value.1 } } -#[derive(Clone, Deserialize, Serialize, Default, Eq)] -pub struct DynVal(pub String, pub Option); +#[derive(Clone, Deserialize, Serialize, Eq)] +pub struct DynVal(pub String, pub Span); impl From for DynVal { fn from(s: String) -> Self { - DynVal(s, None) + DynVal(s, Span::DUMMY) } } @@ -56,7 +56,7 @@ impl std::cmp::PartialEq for DynVal { impl FromIterator for DynVal { fn from_iter>(iter: T) -> Self { - DynVal(iter.into_iter().join(""), None) + DynVal(iter.into_iter().join(""), Span::DUMMY) } } @@ -86,7 +86,7 @@ impl> FromDynVal for T { macro_rules! impl_dynval_from { ($($t:ty),*) => { $(impl From<$t> for DynVal { - fn from(x: $t) -> Self { DynVal(x.to_string(), None) } + fn from(x: $t) -> Self { DynVal(x.to_string(), Span::DUMMY) } })* }; } @@ -95,7 +95,7 @@ impl_dynval_from!(bool, i32, u32, f32, u8, f64, &str); impl From for DynVal { fn from(d: std::time::Duration) -> Self { - DynVal(format!("{}ms", d.as_millis()), None) + DynVal(format!("{}ms", d.as_millis()), Span::DUMMY) } } @@ -106,22 +106,22 @@ impl From<&serde_json::Value> for DynVal { .map(|x| x.to_string()) .or_else(|| serde_json::to_string(v).ok()) .unwrap_or_else(|| "".to_string()), - None, + Span::DUMMY, ) } } impl DynVal { pub fn at(self, span: Span) -> Self { - DynVal(self.0, Some(span)) + DynVal(self.0, span) } - pub fn span(&self) -> Option { + pub fn span(&self) -> Span { self.1 } pub fn from_string(s: String) -> Self { - DynVal(s, None) + DynVal(s, Span::DUMMY) } pub fn read_as>(&self) -> std::result::Result { diff --git a/crates/simplexpr/src/error.rs b/crates/simplexpr/src/error.rs index 920addf..c583f07 100644 --- a/crates/simplexpr/src/error.rs +++ b/crates/simplexpr/src/error.rs @@ -32,27 +32,24 @@ impl Error { Self::Spanned(span, Box::new(self)) } - pub fn get_span(&self) -> Option { + pub fn span(&self) -> Span { match self { Self::ParseError { file_id, source } => get_parse_error_span(*file_id, source), - Self::Spanned(span, _) => Some(*span), + Self::Spanned(span, _) => *span, Self::Eval(err) => err.span(), Self::ConversionError(err) => err.span(), - _ => None, + _ => Span::DUMMY, } } } -fn get_parse_error_span( - file_id: usize, - err: &lalrpop_util::ParseError, -) -> Option { +fn get_parse_error_span(file_id: usize, err: &lalrpop_util::ParseError) -> 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, file_id) } => Some(Span(*l, *r, *file_id)), + lalrpop_util::ParseError::InvalidToken { location } => Span(*location, *location, file_id), + lalrpop_util::ParseError::UnrecognizedEOF { location, expected: _ } => Span(*location, *location, file_id), + lalrpop_util::ParseError::UnrecognizedToken { token, expected: _ } => Span(token.0, token.2, file_id), + lalrpop_util::ParseError::ExtraToken { token } => Span(token.0, token.2, file_id), + lalrpop_util::ParseError::User { error: LexicalError(l, r, file_id) } => Span(*l, *r, *file_id), } } diff --git a/crates/simplexpr/src/eval.rs b/crates/simplexpr/src/eval.rs index ea2bb2a..59f2f5b 100644 --- a/crates/simplexpr/src/eval.rs +++ b/crates/simplexpr/src/eval.rs @@ -38,11 +38,11 @@ pub enum EvalError { } impl EvalError { - pub fn span(&self) -> Option { + pub fn span(&self) -> Span { match self { - EvalError::Spanned(span, _) => Some(*span), + EvalError::Spanned(span, _) => *span, EvalError::ConversionError(err) => err.span(), - _ => None, + _ => Span::DUMMY, } } diff --git a/crates/yuck/src/config/config.rs b/crates/yuck/src/config/config.rs index 81b2113..25e7ac9 100644 --- a/crates/yuck/src/config/config.rs +++ b/crates/yuck/src/config/config.rs @@ -122,7 +122,7 @@ impl Config { pub fn generate_from_main_file(files: &mut YuckFiles, path: impl AsRef) -> AstResult { let (span, top_levels) = files.load_file(path.as_ref().to_path_buf()).map_err(|err| match err { - FilesError::IoError(err) => AstError::Other(None, Box::new(err)), + FilesError::IoError(err) => AstError::Other(Span::DUMMY, Box::new(err)), FilesError::AstError(x) => x, })?; Self::generate(files, top_levels) diff --git a/crates/yuck/src/error.rs b/crates/yuck/src/error.rs index 3b20877..f7278d4 100644 --- a/crates/yuck/src/error.rs +++ b/crates/yuck/src/error.rs @@ -41,7 +41,7 @@ pub enum AstError { ConversionError(#[from] dynval::ConversionError), #[error("{1}")] - Other(Option, Box), + Other(Span, Box), #[error(transparent)] AttrError(#[from] AttrError), @@ -66,22 +66,22 @@ impl AstError { AstError::ErrorContext { label_span, context: context.to_string(), main_err: Box::new(self) } } - pub fn get_span(&self) -> Option { + pub fn span(&self) -> Span { match self { - AstError::UnknownToplevel(span, _) => Some(*span), - AstError::MissingNode(span) => Some(*span), - AstError::WrongExprType(span, ..) => Some(*span), - AstError::NotAValue(span, ..) => Some(*span), - AstError::MismatchedElementName(span, ..) => Some(*span), - AstError::AttrError(err) => Some(err.span()), + AstError::UnknownToplevel(span, _) => *span, + AstError::MissingNode(span) => *span, + AstError::WrongExprType(span, ..) => *span, + AstError::NotAValue(span, ..) => *span, + AstError::MismatchedElementName(span, ..) => *span, + AstError::AttrError(err) => err.span(), AstError::Other(span, ..) => *span, - AstError::ConversionError(err) => err.value.span().map(|x| x.into()), - AstError::IncludedFileNotFound(include) => Some(include.path_span), - AstError::TooManyNodes(span, ..) => Some(*span), - AstError::ErrorContext { label_span, .. } => Some(*label_span), - AstError::ValidationError(error) => Some(error.span()), + AstError::ConversionError(err) => err.value.span(), + AstError::IncludedFileNotFound(include) => include.path_span, + AstError::TooManyNodes(span, ..) => *span, + AstError::ErrorContext { label_span, .. } => *label_span, + AstError::ValidationError(error) => error.span(), AstError::ParseError { file_id, source } => get_parse_error_span(*file_id, source, |err| err.span()), - AstError::ErrorNote(_, err) => err.get_span(), + AstError::ErrorNote(_, err) => err.span(), } } @@ -96,13 +96,13 @@ impl AstError { pub fn get_parse_error_span( file_id: usize, err: &lalrpop_util::ParseError, - handle_user: impl FnOnce(&E) -> Option, -) -> Option { + handle_user: impl FnOnce(&E) -> Span, +) -> 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::InvalidToken { location } => Span(*location, *location, file_id), + lalrpop_util::ParseError::UnrecognizedEOF { location, expected } => Span(*location, *location, file_id), + lalrpop_util::ParseError::UnrecognizedToken { token, expected } => Span(token.0, token.2, file_id), + lalrpop_util::ParseError::ExtraToken { token } => Span(token.0, token.2, file_id), lalrpop_util::ParseError::User { error } => handle_user(error), } } diff --git a/crates/yuck/src/format_diagnostic.rs b/crates/yuck/src/format_diagnostic.rs index a2f3cd0..f7424c3 100644 --- a/crates/yuck/src/format_diagnostic.rs +++ b/crates/yuck/src/format_diagnostic.rs @@ -65,55 +65,51 @@ impl ToDiagnostic for Diagnostic { } impl ToDiagnostic for AstError { fn to_diagnostic(&self) -> Diagnostic { - // TODO this if let should be unnecessary - if let AstError::ValidationError(error) = self { - error.to_diagnostic() - } else { - match self { - AstError::UnknownToplevel(span, name) => gen_diagnostic!(self, span), - AstError::MissingNode(span) => gen_diagnostic! { - msg = "Expected another element", - label = span => "Expected another element here", - }, + match self { + AstError::UnknownToplevel(span, name) => gen_diagnostic!(self, span), + AstError::MissingNode(span) => gen_diagnostic! { + msg = "Expected another element", + label = span => "Expected another element here", + }, - AstError::WrongExprType(span, expected, actual) => gen_diagnostic! { - msg = "Wrong type of expression", - label = span => format!("Expected a `{}` here", expected), - note = format!("Expected: {}\nGot: {}", expected, actual), - }, - AstError::NotAValue(span, actual) => gen_diagnostic! { - msg = format!("Expected value, but got `{}`", actual), - label = span => "Expected some value here", - note = format!("Got: {}", actual), - }, + AstError::WrongExprType(span, expected, actual) => gen_diagnostic! { + msg = "Wrong type of expression", + label = span => format!("Expected a `{}` here", expected), + note = format!("Expected: {}\nGot: {}", expected, actual), + }, + AstError::NotAValue(span, actual) => gen_diagnostic! { + msg = format!("Expected value, but got `{}`", actual), + label = span => "Expected some value here", + note = format!("Got: {}", actual), + }, - AstError::ParseError { file_id, source } => lalrpop_error_to_diagnostic(source, *file_id, |error| error.to_diagnostic()), - AstError::MismatchedElementName(span, expected, got) => gen_diagnostic! { - msg = format!("Expected element `{}`, but found `{}`", expected, got), - label = span => format!("Expected `{}` here", expected), - note = format!("Expected: {}\nGot: {}", expected, got), - }, - AstError::ErrorContext { label_span, context, main_err } => { - main_err.to_diagnostic().with_opt_label(Some(span_to_secondary_label(*label_span).with_message(context))) - } - - AstError::ConversionError(source) => source.to_diagnostic(), - AstError::Other(Some(span), source) => gen_diagnostic!(source, span), - AstError::Other(None, source) => gen_diagnostic!(source), - AstError::AttrError(source) => source.to_diagnostic(), - AstError::IncludedFileNotFound(include) => gen_diagnostic!( - msg = format!("Included file `{}` not found", include.path), - label = include.path_span => "Included here", - ), - - AstError::TooManyNodes(extra_nodes_span, expected) => gen_diagnostic! { - msg = self, - label = extra_nodes_span => "these elements must not be here", - note = "Consider wrapping the elements in some container element", - }, - AstError::ErrorNote(note, source) => source.to_diagnostic().with_notes(vec![note.to_string()]), - AstError::ValidationError(source) => source.to_diagnostic(), + AstError::ParseError { file_id, source } => { + lalrpop_error_to_diagnostic(source, *file_id, |error| error.to_diagnostic()) } + AstError::MismatchedElementName(span, expected, got) => gen_diagnostic! { + msg = format!("Expected element `{}`, but found `{}`", expected, got), + label = span => format!("Expected `{}` here", expected), + note = format!("Expected: {}\nGot: {}", expected, got), + }, + AstError::ErrorContext { label_span, context, main_err } => { + main_err.to_diagnostic().with_opt_label(Some(span_to_secondary_label(*label_span).with_message(context))) + } + + AstError::ConversionError(source) => source.to_diagnostic(), + AstError::Other(span, source) => gen_diagnostic!(source, span), + AstError::AttrError(source) => source.to_diagnostic(), + AstError::IncludedFileNotFound(include) => gen_diagnostic!( + msg = format!("Included file `{}` not found", include.path), + label = include.path_span => "Included here", + ), + + AstError::TooManyNodes(extra_nodes_span, expected) => gen_diagnostic! { + msg = self, + label = extra_nodes_span => "these elements must not be here", + note = "Consider wrapping the elements in some container element", + }, + AstError::ErrorNote(note, source) => source.to_diagnostic().with_notes(vec![note.to_string()]), + AstError::ValidationError(source) => source.to_diagnostic(), } } } @@ -160,63 +156,51 @@ fn lalrpop_error_to_diagnostic( ) -> Diagnostic { use lalrpop_util::ParseError::*; // None is okay here, as the case that would be affected by it (User { error }) is manually handled here anyways - let span = get_parse_error_span(file_id, error, |e| None); - let res: Option<_> = try { - match error { - InvalidToken { location } => gen_diagnostic!("Invalid token", span?), - UnrecognizedEOF { location, expected } => gen_diagnostic! { - "Input ended unexpectedly. Check if you have any unclosed delimiters", - span? - }, - UnrecognizedToken { token, expected } => gen_diagnostic! { - msg = format!("Unexpected token `{}` encountered", token.1), - label = span? => "Token unexpected", - }, - ExtraToken { token } => gen_diagnostic!(format!("Extra token encountered: `{}`", token.1)), - User { error } => handle_user_error(error), - } - }; - res.unwrap_or_else(|| gen_diagnostic!(error)) + let span = get_parse_error_span(file_id, error, |e| Span::DUMMY); + match error { + InvalidToken { location } => gen_diagnostic!("Invalid token", span), + UnrecognizedEOF { location, expected } => gen_diagnostic! { + msg = "Input ended unexpectedly. Check if you have any unclosed delimiters", + label = span + }, + UnrecognizedToken { token, expected } => gen_diagnostic! { + msg = format!("Unexpected token `{}` encountered", token.1), + label = span => "Token unexpected", + }, + ExtraToken { token } => gen_diagnostic!(format!("Extra token encountered: `{}`", token.1)), + User { error } => handle_user_error(error), + } } impl ToDiagnostic for simplexpr::error::Error { // TODO this needs a lot of improvement fn to_diagnostic(&self) -> Diagnostic { use simplexpr::error::Error::*; - let res: Option<_> = try { - match self { - ParseError { source, file_id } => { - let span = get_parse_error_span(*file_id, source, |e| Some(Span(e.0, e.1, *file_id)))?; - lalrpop_error_to_diagnostic(source, *file_id, move |error| lexical_error_diagnostic(span)) - } - ConversionError(error) => error.to_diagnostic(), - Eval(error) => error.to_diagnostic(), - Other(error) => gen_diagnostic!(error), - Spanned(span, error) => gen_diagnostic!(error, span), + match self { + ParseError { source, file_id } => { + let span = get_parse_error_span(*file_id, source, |e| Span(e.0, e.1, *file_id)); + lalrpop_error_to_diagnostic(source, *file_id, move |error| lexical_error_diagnostic(span)) } - }; - res.unwrap_or_else(|| gen_diagnostic!(self)) + ConversionError(error) => error.to_diagnostic(), + Eval(error) => error.to_diagnostic(), + Other(error) => gen_diagnostic!(error), + Spanned(span, error) => gen_diagnostic!(error, span), + } } } impl ToDiagnostic for simplexpr::eval::EvalError { // TODO this needs a lot of improvement fn to_diagnostic(&self) -> Diagnostic { - match self.span() { - Some(span) => gen_diagnostic!(self, span), - None => gen_diagnostic!(self), - } + gen_diagnostic!(self, self.span()) } } impl ToDiagnostic for dynval::ConversionError { fn to_diagnostic(&self) -> Diagnostic { - let diag = match self.span() { - Some(span) => gen_diagnostic! { - msg = self, - label = span => format!("`{}` is not of type `{}`", self.value, self.target_type), - }, - None => gen_diagnostic!(self), + let diag = gen_diagnostic! { + msg = self, + label = self.value.span() => format!("`{}` is not of type `{}`", self.value, self.target_type), }; diag.with_notes(self.source.as_ref().map(|x| vec![format!("{}", x)]).unwrap_or_default()) } diff --git a/crates/yuck/src/parser/parse_error.rs b/crates/yuck/src/parser/parse_error.rs index c2a6007..1f24285 100644 --- a/crates/yuck/src/parser/parse_error.rs +++ b/crates/yuck/src/parser/parse_error.rs @@ -9,10 +9,10 @@ pub enum ParseError { } impl ParseError { - pub fn span(&self) -> Option { + pub fn span(&self) -> Span { match self { - ParseError::SimplExpr(err) => err.get_span(), - ParseError::LexicalError(span) => Some(*span), + ParseError::SimplExpr(err) => err.span(), + ParseError::LexicalError(span) => *span, } } } From 680ab9d633e4fc8df519726678b41fde9c49c8e7 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Mon, 26 Jul 2021 20:17:36 +0200 Subject: [PATCH 077/137] Add Spanned trait and use it --- crates/eww/Cargo.toml | 4 +- crates/eww/src/display_backend.rs | 2 +- crates/eww/src/widgets/widget_node.rs | 26 ++++---- crates/eww_shared_util/src/span.rs | 4 ++ crates/simplexpr/src/EvalError.rs | 0 crates/simplexpr/src/ast.rs | 7 ++- crates/simplexpr/src/dynval.rs | 17 +++--- crates/simplexpr/src/error.rs | 10 ++-- crates/simplexpr/src/eval.rs | 17 +++--- crates/simplexpr/src/parser/lexer.rs | 8 ++- crates/yuck/Cargo.toml | 1 - crates/yuck/src/config/attributes.rs | 15 ++--- .../yuck/src/config/backend_window_options.rs | 2 +- crates/yuck/src/config/config_parse_error.rs | 2 - crates/yuck/src/config/mod.rs | 1 - crates/yuck/src/config/validate.rs | 6 +- crates/yuck/src/error.rs | 39 ++++++------ crates/yuck/src/format_diagnostic.rs | 60 +++++++++---------- crates/yuck/src/lib.rs | 1 - crates/yuck/src/parser/from_ast.rs | 2 +- crates/yuck/src/parser/parse_error.rs | 6 +- crates/yuck/src/util.rs | 0 22 files changed, 117 insertions(+), 113 deletions(-) delete mode 100644 crates/simplexpr/src/EvalError.rs delete mode 100644 crates/yuck/src/config/config_parse_error.rs delete mode 100644 crates/yuck/src/util.rs diff --git a/crates/eww/Cargo.toml b/crates/eww/Cargo.toml index f18f26d..5f65738 100644 --- a/crates/eww/Cargo.toml +++ b/crates/eww/Cargo.toml @@ -13,7 +13,6 @@ homepage = "https://github.com/elkowar/eww" default = ["x11"] x11 = ["gdkx11", "x11rb"] wayland = ["gtk-layer-shell", "gtk-layer-shell-sys"] -no-x11-wayland = [] [dependencies.cairo-sys-rs] version = "0.10.0" @@ -70,9 +69,8 @@ notify = "5.0.0-pre.7" codespan-reporting = "0.11" simplexpr = { path = "../simplexpr" } -yuck = { path = "../yuck", default-features = false} eww_shared_util = { path = "../eww_shared_util" } - +yuck = { path = "../yuck", default-features = false} [dev-dependencies] pretty_assertions = "0.7.1" diff --git a/crates/eww/src/display_backend.rs b/crates/eww/src/display_backend.rs index af38e82..5427f76 100644 --- a/crates/eww/src/display_backend.rs +++ b/crates/eww/src/display_backend.rs @@ -1,6 +1,6 @@ pub use platform::*; -#[cfg(feature = "no-x11-wayland")] +#[cfg(not(any(feature = "x11", feature = "wayland")))] mod platform { use crate::config::EwwWindowDefinition; diff --git a/crates/eww/src/widgets/widget_node.rs b/crates/eww/src/widgets/widget_node.rs index d870644..0bfc959 100644 --- a/crates/eww/src/widgets/widget_node.rs +++ b/crates/eww/src/widgets/widget_node.rs @@ -1,7 +1,7 @@ use crate::eww_state::EwwState; use anyhow::*; use dyn_clone; -use eww_shared_util::{AttrName, Span, VarName}; +use eww_shared_util::{AttrName, Span, Spanned, VarName}; use simplexpr::SimplExpr; use std::collections::HashMap; use yuck::{ @@ -11,7 +11,6 @@ use yuck::{ pub trait WidgetNode: std::fmt::Debug + dyn_clone::DynClone + Send + Sync { fn get_name(&self) -> &str; - fn span(&self) -> Option; /// Generate a [gtk::Widget] from a [element::WidgetUse]. /// @@ -32,7 +31,7 @@ dyn_clone::clone_trait_object!(WidgetNode); #[derive(Debug, Clone)] pub struct UserDefined { name: String, - span: Option, + span: Span, content: Box, } @@ -41,10 +40,6 @@ impl WidgetNode for UserDefined { &self.name } - fn span(&self) -> Option { - self.span - } - fn render( &self, eww_state: &mut EwwState, @@ -55,6 +50,12 @@ impl WidgetNode for UserDefined { } } +impl Spanned for UserDefined { + fn span(&self) -> Span { + self.span + } +} + #[derive(Debug, Clone)] pub struct Generic { pub name: String, @@ -88,10 +89,6 @@ impl WidgetNode for Generic { &self.name } - fn span(&self) -> Option { - Some(self.span) - } - fn render( &self, eww_state: &mut EwwState, @@ -103,6 +100,11 @@ impl WidgetNode for Generic { })?) } } +impl Spanned for Generic { + fn span(&self) -> Span { + self.span + } +} pub fn generate_generic_widget_node( defs: &HashMap, @@ -122,7 +124,7 @@ pub fn generate_generic_widget_node( .collect::>>()?; let content = generate_generic_widget_node(defs, &new_local_env, def.widget.clone())?; - Ok(Box::new(UserDefined { name: w.name, span: Some(w.span), content })) + Ok(Box::new(UserDefined { name: w.name, span: w.span, content })) } else { Ok(Box::new(Generic { name: w.name, diff --git a/crates/eww_shared_util/src/span.rs b/crates/eww_shared_util/src/span.rs index a97b263..6cbd0f7 100644 --- a/crates/eww_shared_util/src/span.rs +++ b/crates/eww_shared_util/src/span.rs @@ -54,3 +54,7 @@ impl std::fmt::Debug for Span { write!(f, "{}..{}", self.0, self.1) } } + +pub trait Spanned { + fn span(&self) -> Span; +} diff --git a/crates/simplexpr/src/EvalError.rs b/crates/simplexpr/src/EvalError.rs deleted file mode 100644 index e69de29..0000000 diff --git a/crates/simplexpr/src/ast.rs b/crates/simplexpr/src/ast.rs index 8d1092b..a29ba71 100644 --- a/crates/simplexpr/src/ast.rs +++ b/crates/simplexpr/src/ast.rs @@ -1,5 +1,5 @@ use crate::dynval::DynVal; -use eww_shared_util::Span; +use eww_shared_util::{Span, Spanned}; use itertools::Itertools; use serde::{Deserialize, Serialize}; @@ -69,8 +69,9 @@ impl SimplExpr { pub fn synth_literal>(s: T) -> Self { Self::Literal(Span::DUMMY, s.into()) } - - pub fn span(&self) -> Span { +} +impl Spanned for SimplExpr { + fn span(&self) -> Span { match self { SimplExpr::Literal(span, _) => *span, SimplExpr::VarRef(span, _) => *span, diff --git a/crates/simplexpr/src/dynval.rs b/crates/simplexpr/src/dynval.rs index ebba340..9da8e22 100644 --- a/crates/simplexpr/src/dynval.rs +++ b/crates/simplexpr/src/dynval.rs @@ -1,4 +1,4 @@ -use eww_shared_util::Span; +use eww_shared_util::{Span, Spanned}; use itertools::Itertools; use serde::{Deserialize, Serialize}; use std::{fmt, iter::FromIterator, str::FromStr}; @@ -17,8 +17,9 @@ impl ConversionError { fn new(value: DynVal, target_type: &'static str, source: impl std::error::Error + 'static + Sync + Send) -> Self { ConversionError { value, target_type, source: Some(Box::new(source)) } } - - pub fn span(&self) -> Span { +} +impl Spanned for ConversionError { + fn span(&self) -> Span { self.value.1 } } @@ -111,15 +112,17 @@ impl From<&serde_json::Value> for DynVal { } } +impl Spanned for DynVal { + fn span(&self) -> Span { + self.1 + } +} + impl DynVal { pub fn at(self, span: Span) -> Self { DynVal(self.0, span) } - pub fn span(&self) -> Span { - self.1 - } - pub fn from_string(s: String) -> Self { DynVal(s, Span::DUMMY) } diff --git a/crates/simplexpr/src/error.rs b/crates/simplexpr/src/error.rs index c583f07..047ff96 100644 --- a/crates/simplexpr/src/error.rs +++ b/crates/simplexpr/src/error.rs @@ -2,15 +2,15 @@ use crate::{ dynval, parser::lexer::{self, LexicalError}, }; -use eww_shared_util::Span; +use eww_shared_util::{Span, Spanned}; pub type Result = std::result::Result; #[derive(thiserror::Error, Debug)] pub enum Error { - #[error("Parse error: {source}")] + #[error("Error parsing expression: {source}")] ParseError { file_id: usize, source: lalrpop_util::ParseError }, - #[error("Type error: {0}")] + #[error(transparent)] ConversionError(#[from] dynval::ConversionError), #[error("{1}")] @@ -31,8 +31,10 @@ impl Error { pub fn at(self, span: Span) -> Self { Self::Spanned(span, Box::new(self)) } +} - pub fn span(&self) -> Span { +impl Spanned for Error { + fn span(&self) -> Span { match self { Self::ParseError { file_id, source } => get_parse_error_span(*file_id, source), Self::Spanned(span, _) => *span, diff --git a/crates/simplexpr/src/eval.rs b/crates/simplexpr/src/eval.rs index 59f2f5b..4b710f8 100644 --- a/crates/simplexpr/src/eval.rs +++ b/crates/simplexpr/src/eval.rs @@ -4,7 +4,7 @@ use crate::{ ast::{BinOp, SimplExpr, UnaryOp}, dynval::{ConversionError, DynVal}, }; -use eww_shared_util::{Span, VarName}; +use eww_shared_util::{Span, Spanned, VarName}; use std::collections::HashMap; #[derive(Debug, thiserror::Error)] @@ -18,7 +18,7 @@ pub enum EvalError { #[error("got unresolved variable `{0}`")] UnresolvedVariable(VarName), - #[error("Type error: {0}")] + #[error(transparent)] ConversionError(#[from] ConversionError), #[error("Incorrect number of arguments given to function: {0}")] @@ -38,17 +38,19 @@ pub enum EvalError { } impl EvalError { - pub fn span(&self) -> Span { + pub fn at(self, span: Span) -> Self { + Self::Spanned(span, Box::new(self)) + } +} + +impl Spanned for EvalError { + fn span(&self) -> Span { match self { EvalError::Spanned(span, _) => *span, EvalError::ConversionError(err) => err.span(), _ => Span::DUMMY, } } - - pub fn at(self, span: Span) -> Self { - Self::Spanned(span, Box::new(self)) - } } impl SimplExpr { @@ -98,7 +100,6 @@ impl SimplExpr { pub fn resolve_refs(self, variables: &HashMap) -> Result { 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)?)), diff --git a/crates/simplexpr/src/parser/lexer.rs b/crates/simplexpr/src/parser/lexer.rs index 022a82a..66ba6f5 100644 --- a/crates/simplexpr/src/parser/lexer.rs +++ b/crates/simplexpr/src/parser/lexer.rs @@ -1,3 +1,4 @@ +use eww_shared_util::{Span, Spanned}; use logos::Logos; use regex::Regex; @@ -48,10 +49,15 @@ pub enum Token { Error, } -// TODO can i include the fileid here already? #[derive(Debug, Eq, PartialEq, Copy, Clone)] pub struct LexicalError(pub usize, pub usize, pub usize); +impl Spanned for LexicalError { + fn span(&self) -> Span { + Span(self.0, self.1, self.2) + } +} + 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) diff --git a/crates/yuck/Cargo.toml b/crates/yuck/Cargo.toml index d159111..fcf0a42 100644 --- a/crates/yuck/Cargo.toml +++ b/crates/yuck/Cargo.toml @@ -10,7 +10,6 @@ build = "build.rs" default = ["x11"] x11 = [] wayland = [] -no-x11-wayland = [] [dependencies] lalrpop-util = "0.19.5" diff --git a/crates/yuck/src/config/attributes.rs b/crates/yuck/src/config/attributes.rs index 0efd2a5..5d6e370 100644 --- a/crates/yuck/src/config/attributes.rs +++ b/crates/yuck/src/config/attributes.rs @@ -13,7 +13,7 @@ use crate::{ error::AstError, parser::{ast::Ast, from_ast::FromAst}, }; -use eww_shared_util::{AttrName, Span, VarName}; +use eww_shared_util::{AttrName, Span, Spanned, VarName}; #[derive(Debug, thiserror::Error)] pub enum AttrError { @@ -27,8 +27,8 @@ pub enum AttrError { Other(Span, Box), } -impl AttrError { - pub fn span(&self) -> Span { +impl Spanned for AttrError { + fn span(&self) -> Span { match self { AttrError::MissingRequiredAttr(span, _) => *span, AttrError::EvaluationError(span, _) => *span, @@ -90,7 +90,7 @@ impl Attributes { let ast: SimplExpr = self.ast_required(&key)?; Ok(ast .eval_no_vars() - .map_err(|err| AttrError::EvaluationError(ast.span().into(), err))? + .map_err(|err| AttrError::EvaluationError(ast.span(), err))? .read_as() .map_err(|e| AttrError::Other(ast.span().into(), Box::new(e)))?) } @@ -106,7 +106,7 @@ impl Attributes { }; Ok(Some( ast.eval_no_vars() - .map_err(|err| AttrError::EvaluationError(ast.span().into(), err))? + .map_err(|err| AttrError::EvaluationError(ast.span(), err))? .read_as() .map_err(|e| AttrError::Other(ast.span().into(), Box::new(e)))?, )) @@ -115,9 +115,6 @@ impl Attributes { /// Consumes the attributes to return a list of unused attributes which may be used to emit a warning. /// TODO actually use this and implement warnings,... lol pub fn get_unused(self, definition_span: Span) -> UnusedAttrs { - UnusedAttrs { - definition_span, - attrs: self.attrs.into_iter().map(|(k, v)| (v.key_span.to(v.value.span().into()), k)).collect(), - } + UnusedAttrs { definition_span, attrs: self.attrs.into_iter().map(|(k, v)| (v.key_span.to(v.value.span()), k)).collect() } } } diff --git a/crates/yuck/src/config/backend_window_options.rs b/crates/yuck/src/config/backend_window_options.rs index ad0cfe8..6165a46 100644 --- a/crates/yuck/src/config/backend_window_options.rs +++ b/crates/yuck/src/config/backend_window_options.rs @@ -121,7 +121,7 @@ mod backend { } } -#[cfg(feature = "no-x11-wayland")] +#[cfg(not(any(feature = "x11", feature = "wayland")))] mod backend { use super::*; #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)] diff --git a/crates/yuck/src/config/config_parse_error.rs b/crates/yuck/src/config/config_parse_error.rs deleted file mode 100644 index 9a99b01..0000000 --- a/crates/yuck/src/config/config_parse_error.rs +++ /dev/null @@ -1,2 +0,0 @@ -#[derive(Debug, thiserror::Error)] -pub enum ConfigParseError {} diff --git a/crates/yuck/src/config/mod.rs b/crates/yuck/src/config/mod.rs index 8275305..e532f62 100644 --- a/crates/yuck/src/config/mod.rs +++ b/crates/yuck/src/config/mod.rs @@ -1,7 +1,6 @@ pub mod attributes; pub mod backend_window_options; pub mod config; -pub mod config_parse_error; pub mod file_provider; pub mod script_var_definition; #[cfg(test)] diff --git a/crates/yuck/src/config/validate.rs b/crates/yuck/src/config/validate.rs index fc7c420..c248c5e 100644 --- a/crates/yuck/src/config/validate.rs +++ b/crates/yuck/src/config/validate.rs @@ -8,7 +8,7 @@ use crate::{ }; use super::{widget_definition::WidgetDefinition, widget_use::WidgetUse}; -use eww_shared_util::{AttrName, Span, VarName}; +use eww_shared_util::{AttrName, Span, Spanned, VarName}; #[derive(Debug, thiserror::Error)] pub enum ValidationError { @@ -19,8 +19,8 @@ pub enum ValidationError { MissingAttr { widget_name: String, arg_name: AttrName, arg_list_span: Option, use_span: Span }, } -impl ValidationError { - pub fn span(&self) -> Span { +impl Spanned for ValidationError { + fn span(&self) -> Span { match self { ValidationError::UnknownWidget(span, _) => *span, ValidationError::MissingAttr { use_span, .. } => *use_span, diff --git a/crates/yuck/src/error.rs b/crates/yuck/src/error.rs index f7278d4..ad0db1c 100644 --- a/crates/yuck/src/error.rs +++ b/crates/yuck/src/error.rs @@ -7,7 +7,7 @@ use crate::{ }, }; use codespan_reporting::{diagnostic, files}; -use eww_shared_util::{AttrName, Span, VarName}; +use eww_shared_util::{AttrName, Span, Spanned, VarName}; use simplexpr::dynval; use thiserror::Error; @@ -66,7 +66,16 @@ impl AstError { AstError::ErrorContext { label_span, context: context.to_string(), main_err: Box::new(self) } } - pub fn span(&self) -> Span { + pub fn from_parse_error( + file_id: usize, + err: lalrpop_util::ParseError, + ) -> AstError { + AstError::ParseError { file_id, source: err } + } +} + +impl Spanned for AstError { + fn span(&self) -> Span { match self { AstError::UnknownToplevel(span, _) => *span, AstError::MissingNode(span) => *span, @@ -80,30 +89,20 @@ impl AstError { AstError::TooManyNodes(span, ..) => *span, AstError::ErrorContext { label_span, .. } => *label_span, AstError::ValidationError(error) => error.span(), - AstError::ParseError { file_id, source } => get_parse_error_span(*file_id, source, |err| err.span()), + AstError::ParseError { file_id, source } => get_parse_error_span(*file_id, source), AstError::ErrorNote(_, err) => err.span(), } } - - pub fn from_parse_error( - file_id: usize, - err: lalrpop_util::ParseError, - ) -> AstError { - AstError::ParseError { file_id, source: err } - } } -pub fn get_parse_error_span( - file_id: usize, - err: &lalrpop_util::ParseError, - handle_user: impl FnOnce(&E) -> Span, -) -> Span { +pub fn get_parse_error_span(file_id: usize, err: &lalrpop_util::ParseError) -> Span { + use lalrpop_util::ParseError::*; match err { - lalrpop_util::ParseError::InvalidToken { location } => Span(*location, *location, file_id), - lalrpop_util::ParseError::UnrecognizedEOF { location, expected } => Span(*location, *location, file_id), - lalrpop_util::ParseError::UnrecognizedToken { token, expected } => Span(token.0, token.2, file_id), - lalrpop_util::ParseError::ExtraToken { token } => Span(token.0, token.2, file_id), - lalrpop_util::ParseError::User { error } => handle_user(error), + InvalidToken { location } => Span(*location, *location, file_id), + UnrecognizedEOF { location, expected } => Span(*location, *location, file_id), + UnrecognizedToken { token, expected } => Span(token.0, token.2, file_id), + ExtraToken { token } => Span(token.0, token.2, file_id), + User { error } => error.span(), } } diff --git a/crates/yuck/src/format_diagnostic.rs b/crates/yuck/src/format_diagnostic.rs index f7424c3..00e9ff8 100644 --- a/crates/yuck/src/format_diagnostic.rs +++ b/crates/yuck/src/format_diagnostic.rs @@ -9,7 +9,7 @@ use crate::{ }; use super::parser::parse_error; -use eww_shared_util::{AttrName, Span, VarName}; +use eww_shared_util::{AttrName, Span, Spanned, VarName}; fn span_to_secondary_label(span: Span) -> Label { Label::secondary(span.2, span.0..span.1) @@ -38,16 +38,12 @@ macro_rules! gen_diagnostic { } pub trait DiagnosticExt: Sized { - fn with_opt_label(self, label: Option>) -> Self; + fn with_label(self, label: Label) -> Self; } impl DiagnosticExt for Diagnostic { - fn with_opt_label(self, label: Option>) -> Self { - if let Some(label) = label { - self.with_labels(vec![label]) - } else { - self - } + fn with_label(self, label: Label) -> Self { + self.with_labels(vec![label]) } } @@ -83,16 +79,14 @@ impl ToDiagnostic for AstError { note = format!("Got: {}", actual), }, - AstError::ParseError { file_id, source } => { - lalrpop_error_to_diagnostic(source, *file_id, |error| error.to_diagnostic()) - } + AstError::ParseError { file_id, source } => lalrpop_error_to_diagnostic(source, *file_id), AstError::MismatchedElementName(span, expected, got) => gen_diagnostic! { msg = format!("Expected element `{}`, but found `{}`", expected, got), label = span => format!("Expected `{}` here", expected), note = format!("Expected: {}\nGot: {}", expected, got), }, AstError::ErrorContext { label_span, context, main_err } => { - main_err.to_diagnostic().with_opt_label(Some(span_to_secondary_label(*label_span).with_message(context))) + main_err.to_diagnostic().with_label(span_to_secondary_label(*label_span).with_message(context)) } AstError::ConversionError(source) => source.to_diagnostic(), @@ -118,7 +112,7 @@ impl ToDiagnostic for parse_error::ParseError { fn to_diagnostic(&self) -> Diagnostic { match self { parse_error::ParseError::SimplExpr(error) => error.to_diagnostic(), - parse_error::ParseError::LexicalError(span) => lexical_error_diagnostic(*span), + parse_error::ParseError::LexicalError(span) => generate_lexical_error_diagnostic(*span), } } } @@ -142,45 +136,43 @@ impl ToDiagnostic for ValidationError { msg = self, label = span => "Used here", }, - ValidationError::MissingAttr { widget_name, arg_name, arg_list_span, use_span } => gen_diagnostic!(self) - .with_opt_label(Some(span_to_secondary_label(*use_span).with_message("Argument missing here"))) - .with_opt_label(arg_list_span.map(|s| span_to_secondary_label(s).with_message("but is required here"))), + ValidationError::MissingAttr { widget_name, arg_name, arg_list_span, use_span } => { + let mut diag = + gen_diagnostic!(self).with_label(span_to_secondary_label(*use_span).with_message("Argument missing here")); + if let Some(arg_list_span) = arg_list_span { + diag = diag.with_label(span_to_secondary_label(*arg_list_span).with_message("But is required here")); + } + diag + } } } } -fn lalrpop_error_to_diagnostic( +fn lalrpop_error_to_diagnostic( error: &lalrpop_util::ParseError, file_id: usize, - handle_user_error: impl FnOnce(&E) -> Diagnostic, ) -> Diagnostic { use lalrpop_util::ParseError::*; - // None is okay here, as the case that would be affected by it (User { error }) is manually handled here anyways - let span = get_parse_error_span(file_id, error, |e| Span::DUMMY); match error { - InvalidToken { location } => gen_diagnostic!("Invalid token", span), + InvalidToken { location } => gen_diagnostic!("Invalid token", Span::point(*location, file_id)), UnrecognizedEOF { location, expected } => gen_diagnostic! { msg = "Input ended unexpectedly. Check if you have any unclosed delimiters", - label = span + label = Span::point(*location, file_id), }, UnrecognizedToken { token, expected } => gen_diagnostic! { msg = format!("Unexpected token `{}` encountered", token.1), - label = span => "Token unexpected", + label = Span(token.0, token.2, file_id) => "Token unexpected", }, ExtraToken { token } => gen_diagnostic!(format!("Extra token encountered: `{}`", token.1)), - User { error } => handle_user_error(error), + User { error } => error.to_diagnostic(), } } impl ToDiagnostic for simplexpr::error::Error { - // TODO this needs a lot of improvement fn to_diagnostic(&self) -> Diagnostic { use simplexpr::error::Error::*; match self { - ParseError { source, file_id } => { - let span = get_parse_error_span(*file_id, source, |e| Span(e.0, e.1, *file_id)); - lalrpop_error_to_diagnostic(source, *file_id, move |error| lexical_error_diagnostic(span)) - } + ParseError { source, file_id } => lalrpop_error_to_diagnostic(source, *file_id), ConversionError(error) => error.to_diagnostic(), Eval(error) => error.to_diagnostic(), Other(error) => gen_diagnostic!(error), @@ -189,8 +181,13 @@ impl ToDiagnostic for simplexpr::error::Error { } } +impl ToDiagnostic for simplexpr::parser::lexer::LexicalError { + fn to_diagnostic(&self) -> Diagnostic { + generate_lexical_error_diagnostic(self.span()) + } +} + impl ToDiagnostic for simplexpr::eval::EvalError { - // TODO this needs a lot of improvement fn to_diagnostic(&self) -> Diagnostic { gen_diagnostic!(self, self.span()) } @@ -206,8 +203,7 @@ impl ToDiagnostic for dynval::ConversionError { } } -/// Generate a simple diagnostic indicating a lexical error -fn lexical_error_diagnostic(span: Span) -> Diagnostic { +fn generate_lexical_error_diagnostic(span: Span) -> Diagnostic { gen_diagnostic! { msg = "Invalid token", label = span => "Invalid token" diff --git a/crates/yuck/src/lib.rs b/crates/yuck/src/lib.rs index 91cace7..687c8f1 100644 --- a/crates/yuck/src/lib.rs +++ b/crates/yuck/src/lib.rs @@ -6,5 +6,4 @@ pub mod config; pub mod error; pub mod format_diagnostic; pub mod parser; -mod util; pub mod value; diff --git a/crates/yuck/src/parser/from_ast.rs b/crates/yuck/src/parser/from_ast.rs index 06fe2df..b5f44fd 100644 --- a/crates/yuck/src/parser/from_ast.rs +++ b/crates/yuck/src/parser/from_ast.rs @@ -2,7 +2,7 @@ use super::{ ast::{Ast, AstType}, ast_iterator::AstIterator, }; -use crate::{error::*, parser, util}; +use crate::{error::*, parser}; use eww_shared_util::{AttrName, Span, VarName}; use itertools::Itertools; use simplexpr::{ast::SimplExpr, dynval::DynVal}; diff --git a/crates/yuck/src/parser/parse_error.rs b/crates/yuck/src/parser/parse_error.rs index 1f24285..3c57d0a 100644 --- a/crates/yuck/src/parser/parse_error.rs +++ b/crates/yuck/src/parser/parse_error.rs @@ -1,4 +1,4 @@ -use eww_shared_util::{AttrName, Span, VarName}; +use eww_shared_util::{AttrName, Span, Spanned, VarName}; #[derive(Debug, thiserror::Error)] pub enum ParseError { @@ -8,8 +8,8 @@ pub enum ParseError { LexicalError(Span), } -impl ParseError { - pub fn span(&self) -> Span { +impl Spanned for ParseError { + fn span(&self) -> Span { match self { ParseError::SimplExpr(err) => err.span(), ParseError::LexicalError(span) => *span, diff --git a/crates/yuck/src/util.rs b/crates/yuck/src/util.rs deleted file mode 100644 index e69de29..0000000 From 4715283b2402b248a08f1a9c290ddfa0fc508aba Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Mon, 26 Jul 2021 20:37:42 +0200 Subject: [PATCH 078/137] Fix CI test with no backend --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 531c877..a23fb28 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,6 +27,6 @@ jobs: run: cargo check --no-default-features --features=x11 - name: Build wayland run: cargo check --no-default-features --features=wayland - - name: Build no-x11-wayland - run: cargo check --no-default-features --features=no-x11-wayland + - name: Build no-backend + run: cargo check --no-default-features From af38b09d5a9c6273dea5e2a396a97cdce8d0afe1 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Mon, 26 Jul 2021 20:41:36 +0200 Subject: [PATCH 079/137] Fix all tests --- crates/eww_shared_util/src/span.rs | 8 ++++++-- .../src/config/snapshots/yuck__config__test__config.snap | 6 +++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/crates/eww_shared_util/src/span.rs b/crates/eww_shared_util/src/span.rs index 6cbd0f7..94919b1 100644 --- a/crates/eww_shared_util/src/span.rs +++ b/crates/eww_shared_util/src/span.rs @@ -45,13 +45,17 @@ impl Span { impl std::fmt::Display for Span { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}..{}", self.0, self.1) + if self.is_dummy() { + write!(f, "DUMMY") + } else { + 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) + write!(f, "{}", self) } } diff --git a/crates/yuck/src/config/snapshots/yuck__config__test__config.snap b/crates/yuck/src/config/snapshots/yuck__config__test__config.snap index d633ab4..ce4f786 100644 --- a/crates/yuck/src/config/snapshots/yuck__config__test__config.snap +++ b/crates/yuck/src/config/snapshots/yuck__config__test__config.snap @@ -18,7 +18,7 @@ Config( attrs: { AttrName("arg"): AttrEntry( key_span: Span(52, 56, 0), - value: Literal(Span(57, 61, 0), DynVal("hi", None)), + value: Literal(Span(57, 61, 0), DynVal("hi", Span(18446744073709551615, 18446744073709551615, 18446744073709551615))), ), }, ), @@ -56,7 +56,7 @@ Config( attrs: { AttrName("arg"): AttrEntry( key_span: Span(468, 472, 0), - value: Literal(Span(473, 478, 0), DynVal("bla", None)), + value: Literal(Span(473, 478, 0), DynVal("bla", Span(18446744073709551615, 18446744073709551615, 18446744073709551615))), ), }, ), @@ -79,7 +79,7 @@ Config( var_definitions: { VarName("some_var"): VarDefinition( name: VarName("some_var"), - initial_value: DynVal("bla", None), + initial_value: DynVal("bla", Span(18446744073709551615, 18446744073709551615, 18446744073709551615)), span: Span(72, 95, 0), ), }, From de8c79bc7d4b05c451c28254fdcb38b173359fe6 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Mon, 26 Jul 2021 22:41:20 +0200 Subject: [PATCH 080/137] Add magic variables again --- crates/eww/src/config/eww_config.rs | 3 +- crates/simplexpr/src/eval.rs | 6 +-- examples/eww-bar/eww.yuck | 63 +++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 examples/eww-bar/eww.yuck diff --git a/crates/eww/src/config/eww_config.rs b/crates/eww/src/config/eww_config.rs index 0e766b8..b44f102 100644 --- a/crates/eww/src/config/eww_config.rs +++ b/crates/eww/src/config/eww_config.rs @@ -21,7 +21,8 @@ pub struct EwwConfig { impl EwwConfig { pub fn read_from_file(files: &mut YuckFiles, path: impl AsRef) -> Result { let config = Config::generate_from_main_file(files, path)?; - let Config { widget_definitions, window_definitions, var_definitions, script_vars } = config; + let Config { widget_definitions, window_definitions, var_definitions, mut script_vars } = config; + script_vars.extend(crate::config::inbuilt::get_inbuilt_vars()); Ok(EwwConfig { windows: window_definitions .into_iter() diff --git a/crates/simplexpr/src/eval.rs b/crates/simplexpr/src/eval.rs index 4b710f8..5ee736e 100644 --- a/crates/simplexpr/src/eval.rs +++ b/crates/simplexpr/src/eval.rs @@ -165,9 +165,9 @@ impl SimplExpr { 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::Plus => match (a.as_f64(), b.as_f64()) { + (Ok(a), Ok(b)) => DynVal::from(a + b), + _ => 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()?), diff --git a/examples/eww-bar/eww.yuck b/examples/eww-bar/eww.yuck new file mode 100644 index 0000000..8b203ed --- /dev/null +++ b/examples/eww-bar/eww.yuck @@ -0,0 +1,63 @@ +(defwidget bar [] + (box :orientation "h" :hexpand true + (workspaces) + (music) + (sidestuff))) + +(defwidget sidestuff [] + (box :class "sidestuff" :orientation "h" :space-evenly false :halign "end" + (slider-vol) + (slider-ram) + (time))) + +(defwidget workspaces [] + (box :class "workspaces" :orientation "h" :space-evenly true :halign "start" + (button :onclick "wmctrl -s 0" 1) + (button :onclick "wmctrl -s 1" 2) + (button :onclick "wmctrl -s 2" 3) + (button :onclick "wmctrl -s 3" 4) + (button :onclick "wmctrl -s 4" 5) + (button :onclick "wmctrl -s 5" 6) + (button :onclick "wmctrl -s 6" 7) + (button :onclick "wmctrl -s 7" 8) + (button :onclick "wmctrl -s 8" 9))) + +(defwidget music [] + (box :class "music" :orientation "h" :space-evenly false :halign "center" + ;{ " " + music})) + {music})) + +(defwidget slider-vol [] + (box :class "slider-vol" :orientation "h" :space-evenly "false" + (box :class "label-vol" "" + (scale :min 0 :max 101 :value volume :onchange "amixer -D pulse sset Master {}%")))) + +(defwidget slider-ram [] + (box :orientation "h" :class "slider-ram" :space-evenly false + (box :class "label-ram" "" + (scale :min 0 :max 101 :active false :value EWW_RAM)))) + + +(defwidget time [] + (box :class "time" + {hour + ":" + min + " " + month + " " + number_day + ", " + year_full})) + + +(defpoll music :interval "5s" "playerctl metadata --format '{{ artist }} - {{ title }}' || true") +(defpoll volume :interval "16s" "scripts/getvol") + +(defpoll number_day :interval "5h" "date '+%d'") +(defpoll month :interval "10h" "date '+%b'") +(defpoll min :interval "10s" "date '+%M'") +(defpoll hour :interval "1m" "date '+%H'") +(defpoll year_full :interval "15h" "date '+%Y'") + +(deflisten battery-remaining "/sys/class/power_supply/BAT0/capacity") + +(defwindow bar + :screen 0 + :focusable true + :windowtype "dock" + :geometry (geometry :x "0%" :y "0%" :width "100%" :height "4%") + :reserve (struts :side "top" :distance "4%") + (bar)) From e698fd315ebfa5f83107f224fc38c47eb7600dec Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Mon, 26 Jul 2021 23:10:01 +0200 Subject: [PATCH 081/137] Fix multi-point unicode glyphs in yuck lexer --- crates/simplexpr/src/parser/lexer.rs | 1 + ...xpr__parser__lexer__simplexpr_lexer-2.snap | 32 +++++++++++++++++++ crates/yuck/src/parser/lexer.rs | 6 ++-- .../yuck__parser__lexer__yuck_lexer-4.snap | 16 ++++++++++ examples/eww-bar/eww.yuck | 3 +- 5 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__simplexpr_lexer-2.snap create mode 100644 crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-4.snap diff --git a/crates/simplexpr/src/parser/lexer.rs b/crates/simplexpr/src/parser/lexer.rs index 66ba6f5..166befc 100644 --- a/crates/simplexpr/src/parser/lexer.rs +++ b/crates/simplexpr/src/parser/lexer.rs @@ -97,4 +97,5 @@ impl<'input> Iterator for Lexer<'input> { fn test_simplexpr_lexer() { use itertools::Itertools; insta::assert_debug_snapshot!(Lexer::new(0, 0, r#"(foo + - "()" "a\"b" true false [] 12.2)"#).collect_vec()); + insta::assert_debug_snapshot!(Lexer::new(0, 0, r#"" " + music"#).collect_vec()); } diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__simplexpr_lexer-2.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__simplexpr_lexer-2.snap new file mode 100644 index 0000000..0b21e4b --- /dev/null +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__simplexpr_lexer-2.snap @@ -0,0 +1,32 @@ +--- +source: crates/simplexpr/src/parser/lexer.rs +expression: "Lexer::new(0, 0, r#\"\" \" + music\"#).collect_vec()" + +--- +[ + Ok( + ( + 0, + StrLit( + "\"\u{f001} \"", + ), + 8, + ), + ), + Ok( + ( + 9, + Plus, + 10, + ), + ), + Ok( + ( + 11, + Ident( + "music", + ), + 16, + ), + ), +] diff --git a/crates/yuck/src/parser/lexer.rs b/crates/yuck/src/parser/lexer.rs index 804dbfa..1d7dac5 100644 --- a/crates/yuck/src/parser/lexer.rs +++ b/crates/yuck/src/parser/lexer.rs @@ -101,13 +101,15 @@ impl Iterator for Lexer { let string = &self.source[self.pos..]; if string.starts_with('{') { - // self.pos += 1; let expr_start = self.pos; let mut in_string = false; loop { if self.pos >= self.source.len() { return None; } + while !self.source.is_char_boundary(self.pos) { + self.pos += 1; + } let string = &self.source[self.pos..]; if string.starts_with('}') && !in_string { @@ -137,7 +139,6 @@ impl Iterator for Lexer { Some(x) => x, None => { self.failed = true; - dbg!(&string); return Some(Err(parse_error::ParseError::LexicalError(Span(self.pos, self.pos, self.file_id)))); } }; @@ -163,4 +164,5 @@ fn test_yuck_lexer() { insta::assert_debug_snapshot!(Lexer::new(0, r#"(foo + - "text" )"#.to_string()).collect_vec()); insta::assert_debug_snapshot!(Lexer::new(0, r#"{ bla "} \" }" " \" "}"#.to_string()).collect_vec()); insta::assert_debug_snapshot!(Lexer::new(0, r#""< \" >""#.to_string()).collect_vec()); + insta::assert_debug_snapshot!(Lexer::new(0, r#"{ " " + music}"#.to_string()).collect_vec()); } diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-4.snap b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-4.snap new file mode 100644 index 0000000..558fd17 --- /dev/null +++ b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-4.snap @@ -0,0 +1,16 @@ +--- +source: crates/yuck/src/parser/lexer.rs +expression: "Lexer::new(0, r#\"{ \" \" + music}\"#.to_string()).collect_vec()" + +--- +[ + Ok( + ( + 0, + SimplExpr( + "{ \"\u{f001} \" + music}", + ), + 18, + ), + ), +] diff --git a/examples/eww-bar/eww.yuck b/examples/eww-bar/eww.yuck index 8b203ed..1c769f0 100644 --- a/examples/eww-bar/eww.yuck +++ b/examples/eww-bar/eww.yuck @@ -24,8 +24,7 @@ (defwidget music [] (box :class "music" :orientation "h" :space-evenly false :halign "center" - ;{ " " + music})) - {music})) + { " " + music})) (defwidget slider-vol [] (box :class "slider-vol" :orientation "h" :space-evenly "false" From 01c7ba6e2a5393fec655f3ca3b3f63b3d3973772 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Tue, 27 Jul 2021 00:11:16 +0200 Subject: [PATCH 082/137] Add alternative string syntax with single quotes and backticks --- crates/simplexpr/src/parser/lexer.rs | 3 +++ crates/yuck/src/parser/lexer.rs | 17 +++++++++++++---- .../yuck__parser__lexer__yuck_lexer-5.snap | 16 ++++++++++++++++ examples/eww-bar/eww.yuck | 2 +- 4 files changed, 33 insertions(+), 5 deletions(-) create mode 100644 crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-5.snap diff --git a/crates/simplexpr/src/parser/lexer.rs b/crates/simplexpr/src/parser/lexer.rs index 166befc..b9bd352 100644 --- a/crates/simplexpr/src/parser/lexer.rs +++ b/crates/simplexpr/src/parser/lexer.rs @@ -40,7 +40,10 @@ pub enum Token { Ident(String), #[regex(r"[+-]?(?:[0-9]+[.])?[0-9]+", |x| x.slice().to_string())] NumLit(String), + #[regex(r#""(?:[^"\\]|\\.)*""#, |x| ESCAPE_REPLACE_REGEX.replace_all(x.slice(), "$1").to_string())] + #[regex(r#"`(?:[^`\\]|\\.)*`"#, |x| ESCAPE_REPLACE_REGEX.replace_all(x.slice(), "$1").to_string())] + #[regex(r#"'(?:[^'\\]|\\.)*'"#, |x| ESCAPE_REPLACE_REGEX.replace_all(x.slice(), "$1").to_string())] StrLit(String), diff --git a/crates/yuck/src/parser/lexer.rs b/crates/yuck/src/parser/lexer.rs index 1d7dac5..222a8de 100644 --- a/crates/yuck/src/parser/lexer.rs +++ b/crates/yuck/src/parser/lexer.rs @@ -70,6 +70,8 @@ regex_rules! { r"true" => |_| Token::True, r"false" => |_| Token::False, r#""(?:[^"\\]|\\.)*""# => |x| Token::StrLit(ESCAPE_REPLACE_REGEX.replace_all(&x, "$1").to_string()), + r#"`(?:[^`\\]|\\.)*`"# => |x| Token::StrLit(ESCAPE_REPLACE_REGEX.replace_all(&x, "$1").to_string()), + r#"'(?:[^'\\]|\\.)*'"# => |x| Token::StrLit(ESCAPE_REPLACE_REGEX.replace_all(&x, "$1").to_string()), r#"[+-]?(?:[0-9]+[.])?[0-9]+"# => |x| Token::NumLit(x), r#":[^\s\)\]}]+"# => |x| Token::Keyword(x), r#"[a-zA-Z_!\?<>/\.\*-\+\-][^\s{}\(\)\[\](){}]*"# => |x| Token::Symbol(x), @@ -102,7 +104,7 @@ impl Iterator for Lexer { if string.starts_with('{') { let expr_start = self.pos; - let mut in_string = false; + let mut in_string = None; loop { if self.pos >= self.source.len() { return None; @@ -112,13 +114,19 @@ impl Iterator for Lexer { } let string = &self.source[self.pos..]; - if string.starts_with('}') && !in_string { + if string.starts_with('}') && in_string.is_none() { self.pos += 1; let tok_str = &self.source[expr_start..self.pos]; return Some(Ok((expr_start, Token::SimplExpr(tok_str.to_string()), self.pos - 1))); - } else if string.starts_with('"') { + } else if string.starts_with('"') || string.starts_with('\'') || string.starts_with('`') { + if let Some(quote) = in_string { + if string.starts_with(quote) { + in_string = None; + } + } else { + in_string = Some(string.chars().next().unwrap()); + } self.pos += 1; - in_string = !in_string; } else if string.starts_with("\\\"") { self.pos += 2; } else { @@ -165,4 +173,5 @@ fn test_yuck_lexer() { insta::assert_debug_snapshot!(Lexer::new(0, r#"{ bla "} \" }" " \" "}"#.to_string()).collect_vec()); insta::assert_debug_snapshot!(Lexer::new(0, r#""< \" >""#.to_string()).collect_vec()); insta::assert_debug_snapshot!(Lexer::new(0, r#"{ " " + music}"#.to_string()).collect_vec()); + insta::assert_debug_snapshot!(Lexer::new(0, r#"{ " } ' }" }"#.to_string()).collect_vec()); } diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-5.snap b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-5.snap new file mode 100644 index 0000000..0e4ea1b --- /dev/null +++ b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-5.snap @@ -0,0 +1,16 @@ +--- +source: crates/yuck/src/parser/lexer.rs +expression: "Lexer::new(0, r#\"{ \" } ' }\" }\"#.to_string()).collect_vec()" + +--- +[ + Ok( + ( + 0, + SimplExpr( + "{ \" } ' }\" }", + ), + 11, + ), + ), +] diff --git a/examples/eww-bar/eww.yuck b/examples/eww-bar/eww.yuck index 1c769f0..d21e6e0 100644 --- a/examples/eww-bar/eww.yuck +++ b/examples/eww-bar/eww.yuck @@ -24,7 +24,7 @@ (defwidget music [] (box :class "music" :orientation "h" :space-evenly false :halign "center" - { " " + music})) + { ' ' + music})) (defwidget slider-vol [] (box :class "slider-vol" :orientation "h" :space-evenly "false" From 99fd51dc1215709870718b6d19c4aee33d353d72 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Tue, 27 Jul 2021 17:24:40 +0200 Subject: [PATCH 083/137] Improve error messages for unknown variables in evalerror --- crates/simplexpr/src/eval.rs | 6 +++--- crates/yuck/src/format_diagnostic.rs | 13 ++++++++++++- crates/yuck/src/parser/lexer.rs | 2 ++ examples/eww-bar/eww.yuck | 10 +++++----- 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/crates/simplexpr/src/eval.rs b/crates/simplexpr/src/eval.rs index 5ee736e..227fa6f 100644 --- a/crates/simplexpr/src/eval.rs +++ b/crates/simplexpr/src/eval.rs @@ -10,7 +10,7 @@ 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), + NoVariablesAllowed(VarName), #[error("Invalid regex: {0}")] InvalidRegex(#[from] regex::Error), @@ -28,7 +28,7 @@ pub enum EvalError { UnknownFunction(String), #[error("Unknown variable {0}")] - UnknownVariable(String), + UnknownVariable(VarName), #[error("Unable to index into value {0}")] CannotIndex(String), @@ -116,7 +116,7 @@ impl SimplExpr { )), VarRef(span, ref name) => match variables.get(name) { Some(value) => Ok(Literal(span, value.clone())), - None => Err(EvalError::UnknownVariable(name.to_string()).at(span)), + None => Err(EvalError::UnknownVariable(name.clone()).at(span)), }, } } diff --git a/crates/yuck/src/format_diagnostic.rs b/crates/yuck/src/format_diagnostic.rs index 00e9ff8..e094245 100644 --- a/crates/yuck/src/format_diagnostic.rs +++ b/crates/yuck/src/format_diagnostic.rs @@ -11,6 +11,9 @@ use crate::{ use super::parser::parse_error; use eww_shared_util::{AttrName, Span, Spanned, VarName}; +fn span_to_primary_label(span: Span) -> Label { + Label::primary(span.2, span.0..span.1) +} fn span_to_secondary_label(span: Span) -> Label { Label::secondary(span.2, span.0..span.1) } @@ -189,7 +192,15 @@ impl ToDiagnostic for simplexpr::parser::lexer::LexicalError { impl ToDiagnostic for simplexpr::eval::EvalError { fn to_diagnostic(&self) -> Diagnostic { - gen_diagnostic!(self, self.span()) + use simplexpr::eval::EvalError::*; + match self { + UnresolvedVariable(name) | UnknownVariable(name) | NoVariablesAllowed(name) => gen_diagnostic! { + msg = self, + note = format!("If you meant to use the literal value \"{}\", surround the value in quotes", name) + }, + Spanned(span, error) => error.as_ref().to_diagnostic().with_label(span_to_primary_label(*span)), + _ => gen_diagnostic!(self, self.span()), + } } } diff --git a/crates/yuck/src/parser/lexer.rs b/crates/yuck/src/parser/lexer.rs index 222a8de..1b4650d 100644 --- a/crates/yuck/src/parser/lexer.rs +++ b/crates/yuck/src/parser/lexer.rs @@ -92,6 +92,8 @@ impl Lexer { } } +// TODO string literal interpolation stuff by looking for indexes of {{ and }}? + impl Iterator for Lexer { type Item = Result<(usize, Token, usize), parse_error::ParseError>; diff --git a/examples/eww-bar/eww.yuck b/examples/eww-bar/eww.yuck index d21e6e0..8ead5d1 100644 --- a/examples/eww-bar/eww.yuck +++ b/examples/eww-bar/eww.yuck @@ -45,11 +45,11 @@ (defpoll music :interval "5s" "playerctl metadata --format '{{ artist }} - {{ title }}' || true") (defpoll volume :interval "16s" "scripts/getvol") -(defpoll number_day :interval "5h" "date '+%d'") -(defpoll month :interval "10h" "date '+%b'") -(defpoll min :interval "10s" "date '+%M'") -(defpoll hour :interval "1m" "date '+%H'") -(defpoll year_full :interval "15h" "date '+%Y'") +(defpoll number_day :interval "5h" "date '+%d'") +(defpoll month :interval "10h" "date '+%b'") +(defpoll min :interval "10s" "date '+%M'") +(defpoll hour :interval "1m" "date '+%H'") +(defpoll year_full :interval "15h" "date '+%Y'") (deflisten battery-remaining "/sys/class/power_supply/BAT0/capacity") From b83654923256d560cd1ffb163cb807805526feef Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Tue, 27 Jul 2021 17:38:01 +0200 Subject: [PATCH 084/137] Don't exit daemon when initial load fails --- crates/eww/src/config/eww_config.rs | 9 ++++++++ crates/eww/src/server.rs | 32 +++++++++++++++++++---------- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/crates/eww/src/config/eww_config.rs b/crates/eww/src/config/eww_config.rs index b44f102..ffc275b 100644 --- a/crates/eww/src/config/eww_config.rs +++ b/crates/eww/src/config/eww_config.rs @@ -18,8 +18,17 @@ pub struct EwwConfig { script_vars: HashMap, } +impl Default for EwwConfig { + fn default() -> Self { + Self { widgets: HashMap::new(), windows: HashMap::new(), initial_variables: HashMap::new(), script_vars: HashMap::new() } + } +} + impl EwwConfig { pub fn read_from_file(files: &mut YuckFiles, path: impl AsRef) -> Result { + if !path.as_ref().exists() { + bail!("The configuration file `{}` does not exist", path.as_ref().display()); + } let config = Config::generate_from_main_file(files, path)?; let Config { widget_definitions, window_definitions, var_definitions, mut script_vars } = config; script_vars.extend(crate::config::inbuilt::get_inbuilt_vars()); diff --git a/crates/eww/src/server.rs b/crates/eww/src/server.rs index 20f9b77..1457382 100644 --- a/crates/eww/src/server.rs +++ b/crates/eww/src/server.rs @@ -10,6 +10,27 @@ use std::{ use tokio::sync::mpsc::*; pub fn initialize_server(paths: EwwPaths) -> Result<()> { + let (ui_send, mut ui_recv) = tokio::sync::mpsc::unbounded_channel(); + + std::env::set_current_dir(&paths.get_config_dir()) + .with_context(|| format!("Failed to change working directory to {}", paths.get_config_dir().display()))?; + + log::info!("Loading paths: {}", &paths); + + // disgusting global state, I hate this, but https://github.com/buffet told me that this is what I should do for peak maintainability + error_handling_ctx::clear_files(); + + let read_config = + config::EwwConfig::read_from_file(&mut error_handling_ctx::ERROR_HANDLING_CTX.lock().unwrap(), &paths.get_yuck_path()); + + let eww_config = match read_config { + Ok(config) => config, + Err(err) => { + error_handling_ctx::print_error(err); + config::EwwConfig::default() + } + }; + do_detach(&paths.get_log_file())?; println!( @@ -27,17 +48,6 @@ pub fn initialize_server(paths: EwwPaths) -> Result<()> { std::process::exit(1); } }); - let (ui_send, mut ui_recv) = tokio::sync::mpsc::unbounded_channel(); - - std::env::set_current_dir(&paths.get_config_dir()) - .with_context(|| format!("Failed to change working directory to {}", paths.get_config_dir().display()))?; - - log::info!("Loading paths: {}", &paths); - - // disgusting global state, I hate this, but https://github.com/buffet told me that this is what I should do for peak maintainability - error_handling_ctx::clear_files(); - let eww_config = - config::EwwConfig::read_from_file(&mut error_handling_ctx::ERROR_HANDLING_CTX.lock().unwrap(), &paths.get_yuck_path())?; gtk::init()?; From fcd54abf91589b60a2eb772b3265c68d3fca80a4 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Wed, 28 Jul 2021 17:12:58 +0200 Subject: [PATCH 085/137] Allow for different diagnostic severities in gen_diagnostic --- crates/eww/src/config/script_var.rs | 13 +++++++++---- crates/eww/src/script_var_handler.rs | 7 +++++-- crates/yuck/src/format_diagnostic.rs | 20 +++++++++++++++----- 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/crates/eww/src/config/script_var.rs b/crates/eww/src/config/script_var.rs index 95ef72f..a62a864 100644 --- a/crates/eww/src/config/script_var.rs +++ b/crates/eww/src/config/script_var.rs @@ -1,6 +1,7 @@ use std::process::Command; use anyhow::*; +use codespan_reporting::diagnostic::Severity; use eww_shared_util::{Span, VarName}; use simplexpr::dynval::DynVal; use yuck::{ @@ -10,10 +11,12 @@ use yuck::{ use crate::error::DiagError; -pub fn create_script_var_failed_error(span: Span, var_name: &VarName) -> DiagError { +pub fn create_script_var_failed_warn(span: Span, var_name: &VarName, error_output: &str) -> DiagError { DiagError::new(gen_diagnostic! { - msg = format!("Failed to compute value for `{}`", var_name), + kind = Severity::Warning, + msg = format!("The script for the `{}`-variable exited unsuccessfully", var_name), label = span => "Defined here", + note = error_output, }) } @@ -23,7 +26,9 @@ pub fn initial_value(var: &ScriptVarDefinition) -> Result { VarSource::Function(f) => { f().map_err(|err| anyhow!(err)).with_context(|| format!("Failed to compute initial value for {}", &var.name())) } - VarSource::Shell(span, f) => run_command(f).map_err(|_| anyhow!(create_script_var_failed_error(*span, var.name()))), + VarSource::Shell(span, f) => { + run_command(f).map_err(|e| anyhow!(create_script_var_failed_warn(*span, var.name(), &e.to_string()))) + } }, ScriptVarDefinition::Listen(_) => Ok(DynVal::from_string(String::new())), } @@ -34,7 +39,7 @@ pub fn run_command(cmd: &str) -> Result { log::debug!("Running command: {}", cmd); let command = Command::new("/bin/sh").arg("-c").arg(cmd).output()?; if !command.status.success() { - bail!("Execution of `{}` failed", cmd); + bail!("Failed with output:\n{}", String::from_utf8(command.stderr)?); } let output = String::from_utf8(command.stdout)?; let output = output.trim_matches('\n'); diff --git a/crates/eww/src/script_var_handler.rs b/crates/eww/src/script_var_handler.rs index c4bfa9c..f28a534 100644 --- a/crates/eww/src/script_var_handler.rs +++ b/crates/eww/src/script_var_handler.rs @@ -1,6 +1,9 @@ use std::collections::HashMap; -use crate::{app, config::create_script_var_failed_error}; +use crate::{ + app, + config::{create_script_var_failed_warn, script_var}, +}; use anyhow::*; use app::DaemonCommand; @@ -169,7 +172,7 @@ impl PollVarHandler { fn run_poll_once(var: &PollScriptVar) -> Result { match &var.command { VarSource::Shell(span, x) => { - crate::config::script_var::run_command(x).map_err(|_| anyhow!(create_script_var_failed_error(*span, &var.name))) + script_var::run_command(x).map_err(|e| anyhow!(create_script_var_failed_warn(*span, &var.name, &e.to_string()))) } VarSource::Function(x) => x().map_err(|e| anyhow!(e)), } diff --git a/crates/yuck/src/format_diagnostic.rs b/crates/yuck/src/format_diagnostic.rs index e094245..ea35525 100644 --- a/crates/yuck/src/format_diagnostic.rs +++ b/crates/yuck/src/format_diagnostic.rs @@ -20,12 +20,14 @@ fn span_to_secondary_label(span: Span) -> Label { #[macro_export] macro_rules! gen_diagnostic { - ( - $(msg = $msg:expr)? - $(, label = $span:expr $(=> $label:expr)?)? - $(, note = $note:expr)? $(,)? + ( $(kind = $kind:expr,)? + $(msg = $msg:expr)? + $(, label = $span:expr $(=> $label:expr)?)? + $(, note = $note:expr)? $(,)? ) => { - ::codespan_reporting::diagnostic::Diagnostic::error() + ::codespan_reporting::diagnostic::Diagnostic::new(gen_diagnostic! { + @macro_fallback $({$kind})? {::codespan_reporting::diagnostic::Severity::Error} + }) $(.with_message($msg.to_string()))? $(.with_labels(vec![ ::codespan_reporting::diagnostic::Label::primary($span.2, $span.0..$span.1) @@ -38,6 +40,14 @@ macro_rules! gen_diagnostic { .with_message($msg.to_string()) $(.with_labels(vec![::codespan_reporting::diagnostic::Label::primary($span.2, $span.0..$span.1)]))? }}; + + + (@macro_fallback { $value:expr } { $fallback:expr }) => { + $value + }; + (@macro_fallback { $fallback:expr }) => { + $fallback + }; } pub trait DiagnosticExt: Sized { From 8fea52bda97d59e1a2faaa31ead3ff135233311e Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Wed, 28 Jul 2021 17:16:01 +0200 Subject: [PATCH 086/137] Allow for comments in simplexpr --- crates/simplexpr/src/parser/lexer.rs | 2 ++ ...plexpr__parser__lexer__simplexpr_lexer-3.snap | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__simplexpr_lexer-3.snap diff --git a/crates/simplexpr/src/parser/lexer.rs b/crates/simplexpr/src/parser/lexer.rs index b9bd352..8992249 100644 --- a/crates/simplexpr/src/parser/lexer.rs +++ b/crates/simplexpr/src/parser/lexer.rs @@ -49,6 +49,7 @@ pub enum Token { #[error] #[regex(r"[ \t\n\f]+", logos::skip)] + #[regex(r";.*", logos::skip)] Error, } @@ -101,4 +102,5 @@ fn test_simplexpr_lexer() { use itertools::Itertools; insta::assert_debug_snapshot!(Lexer::new(0, 0, r#"(foo + - "()" "a\"b" true false [] 12.2)"#).collect_vec()); insta::assert_debug_snapshot!(Lexer::new(0, 0, r#"" " + music"#).collect_vec()); + insta::assert_debug_snapshot!(Lexer::new(0, 0, r#"foo ; bar"#).collect_vec()); } diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__simplexpr_lexer-3.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__simplexpr_lexer-3.snap new file mode 100644 index 0000000..1fbd0df --- /dev/null +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__simplexpr_lexer-3.snap @@ -0,0 +1,16 @@ +--- +source: crates/simplexpr/src/parser/lexer.rs +expression: "Lexer::new(0, 0, r#\"foo ; bar\"#).collect_vec()" + +--- +[ + Ok( + ( + 0, + Ident( + "foo", + ), + 3, + ), + ), +] From 6f1118bda4fe49b0aa30235a4d447a9b3f11dd66 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Wed, 28 Jul 2021 19:58:51 +0200 Subject: [PATCH 087/137] Improve error messages for unread elements in nodes and fix span handling in ast iterator --- .../yuck/src/config/backend_window_options.rs | 1 + crates/yuck/src/config/config.rs | 1 + .../yuck/src/config/script_var_definition.rs | 2 + crates/yuck/src/config/var_definition.rs | 1 + crates/yuck/src/config/widget_definition.rs | 3 +- crates/yuck/src/config/window_definition.rs | 3 +- crates/yuck/src/config/window_geometry.rs | 1 + crates/yuck/src/error.rs | 3 + crates/yuck/src/format_diagnostic.rs | 1 + crates/yuck/src/parser/ast_iterator.rs | 31 +++++--- crates/yuck/src/parser/from_ast.rs | 4 +- examples/eww-bar/eww.yuck | 73 ++++++++++--------- 12 files changed, 74 insertions(+), 50 deletions(-) diff --git a/crates/yuck/src/config/backend_window_options.rs b/crates/yuck/src/config/backend_window_options.rs index 6165a46..d5ced8f 100644 --- a/crates/yuck/src/config/backend_window_options.rs +++ b/crates/yuck/src/config/backend_window_options.rs @@ -98,6 +98,7 @@ mod backend { fn from_tail>(span: Span, mut iter: AstIterator) -> AstResult { let mut attrs = iter.expect_key_values()?; + iter.expect_done().map_err(|e| e.note("Check if you are missing a colon in front of a key"))?; Ok(StrutDefinition { side: attrs.primitive_required("side")?, dist: attrs.primitive_required("distance")? }) } } diff --git a/crates/yuck/src/config/config.rs b/crates/yuck/src/config/config.rs index 25e7ac9..9e94cb0 100644 --- a/crates/yuck/src/config/config.rs +++ b/crates/yuck/src/config/config.rs @@ -38,6 +38,7 @@ impl FromAstElementContent for Include { fn from_tail>(span: Span, mut iter: AstIterator) -> AstResult { let (path_span, path) = iter.expect_literal()?; + iter.expect_done()?; Ok(Include { path: path.to_string(), path_span }) } } diff --git a/crates/yuck/src/config/script_var_definition.rs b/crates/yuck/src/config/script_var_definition.rs index 862c323..176c50e 100644 --- a/crates/yuck/src/config/script_var_definition.rs +++ b/crates/yuck/src/config/script_var_definition.rs @@ -62,6 +62,7 @@ impl FromAstElementContent for PollScriptVar { let mut attrs = iter.expect_key_values()?; let interval = attrs.primitive_required::("interval")?.as_duration()?; let (script_span, script) = iter.expect_literal()?; + iter.expect_done()?; Ok(Self { name: VarName(name), command: VarSource::Shell(script_span, script.to_string()), interval }) } } @@ -80,6 +81,7 @@ impl FromAstElementContent for ListenScriptVar { fn from_tail>(span: Span, mut iter: AstIterator) -> AstResult { let (_, name) = iter.expect_symbol()?; let (command_span, script) = iter.expect_literal()?; + iter.expect_done()?; Ok(Self { name: VarName(name), command: script.to_string(), command_span }) } } diff --git a/crates/yuck/src/config/var_definition.rs b/crates/yuck/src/config/var_definition.rs index 065820d..cd39a8e 100644 --- a/crates/yuck/src/config/var_definition.rs +++ b/crates/yuck/src/config/var_definition.rs @@ -27,6 +27,7 @@ impl FromAstElementContent for VarDefinition { fn from_tail>(span: Span, mut iter: AstIterator) -> AstResult { let (_, name) = iter.expect_symbol()?; let (_, initial_value) = iter.expect_literal()?; + iter.expect_done()?; Ok(Self { name: VarName(name), initial_value, span }) } } diff --git a/crates/yuck/src/config/widget_definition.rs b/crates/yuck/src/config/widget_definition.rs index 641f587..02d6fdb 100644 --- a/crates/yuck/src/config/widget_definition.rs +++ b/crates/yuck/src/config/widget_definition.rs @@ -32,8 +32,7 @@ impl FromAstElementContent for WidgetDefinition { let (args_span, expected_args) = iter.expect_array()?; let expected_args = expected_args.into_iter().map(|x| x.as_symbol().map(AttrName)).collect::>()?; let widget = iter.expect_any().and_then(WidgetUse::from_ast)?; - // TODO verify that this was the last element in the list - // iter.expect_done()?; + iter.expect_done()?; Ok(Self { name, expected_args, widget, span, args_span }) } } diff --git a/crates/yuck/src/config/window_definition.rs b/crates/yuck/src/config/window_definition.rs index 8687455..58726ea 100644 --- a/crates/yuck/src/config/window_definition.rs +++ b/crates/yuck/src/config/window_definition.rs @@ -39,7 +39,8 @@ impl FromAstElementContent for WindowDefinition { let stacking = attrs.primitive_optional("stacking")?.unwrap_or(WindowStacking::Foreground); let geometry = attrs.ast_optional("geometry")?; let backend_options = BackendWindowOptions::from_attrs(&mut attrs)?; - let widget = iter.expect_any()?; + let widget = iter.expect_any().and_then(WidgetUse::from_ast)?; + iter.expect_done()?; Ok(Self { name, monitor_number, resizable, widget, stacking, geometry, backend_options }) } } diff --git a/crates/yuck/src/config/window_geometry.rs b/crates/yuck/src/config/window_geometry.rs index 4245ab0..49a5059 100644 --- a/crates/yuck/src/config/window_geometry.rs +++ b/crates/yuck/src/config/window_geometry.rs @@ -123,6 +123,7 @@ impl FromAstElementContent for WindowGeometry { fn from_tail>(span: Span, mut iter: AstIterator) -> AstResult { let mut attrs = iter.expect_key_values()?; + iter.expect_done().map_err(|e| e.note("Check if you are missing a colon in front of a key"))?; Ok(WindowGeometry { anchor_point: attrs.primitive_optional("anchor")?.unwrap_or_default(), size: Coords { diff --git a/crates/yuck/src/error.rs b/crates/yuck/src/error.rs index ad0db1c..22ea68e 100644 --- a/crates/yuck/src/error.rs +++ b/crates/yuck/src/error.rs @@ -21,6 +21,8 @@ pub enum AstError { MissingNode(Span), #[error("Too many elements, must be exactly {1}")] TooManyNodes(Span, i32), + #[error("Did not expect any further elements here. Make sure your format is correct")] + NoMoreElementsExpected(Span), #[error("Wrong type of expression: Expected {1} but got {2}")] WrongExprType(Span, AstType, AstType), @@ -91,6 +93,7 @@ impl Spanned for AstError { AstError::ValidationError(error) => error.span(), AstError::ParseError { file_id, source } => get_parse_error_span(*file_id, source), AstError::ErrorNote(_, err) => err.span(), + AstError::NoMoreElementsExpected(span) => *span, } } } diff --git a/crates/yuck/src/format_diagnostic.rs b/crates/yuck/src/format_diagnostic.rs index ea35525..1f7237f 100644 --- a/crates/yuck/src/format_diagnostic.rs +++ b/crates/yuck/src/format_diagnostic.rs @@ -117,6 +117,7 @@ impl ToDiagnostic for AstError { }, AstError::ErrorNote(note, source) => source.to_diagnostic().with_notes(vec![note.to_string()]), AstError::ValidationError(source) => source.to_diagnostic(), + AstError::NoMoreElementsExpected(span) => gen_diagnostic!(self, span), } } } diff --git a/crates/yuck/src/parser/ast_iterator.rs b/crates/yuck/src/parser/ast_iterator.rs index e8983c6..aebe312 100644 --- a/crates/yuck/src/parser/ast_iterator.rs +++ b/crates/yuck/src/parser/ast_iterator.rs @@ -24,15 +24,11 @@ macro_rules! return_or_put_back { pub fn $name(&mut self) -> AstResult<$t> { let expr_type = $expr_type; match self.expect_any()? { - $p => { - let (span, value) = $ret; - self.remaining_span.1 = span.1; - Ok((span, value)) - } + $p => Ok($ret), other => { let span = other.span(); let actual_type = other.expr_type(); - self.iter.put_back(other); + self.put_back(other); Err(AstError::WrongExprType(span, expr_type, actual_type)) } } @@ -53,20 +49,37 @@ impl> AstIterator { AstIterator { remaining_span: span, iter: itertools::put_back(iter) } } - pub fn expect_any(&mut self) -> AstResult { - self.iter.next().or_missing(self.remaining_span.point_span()).and_then(T::from_ast) + pub fn expect_any(&mut self) -> AstResult { + self.next().or_missing(self.remaining_span.point_span()) + } + + pub fn expect_done(&mut self) -> AstResult<()> { + if let Some(next) = self.next() { + self.put_back(next); + Err(AstError::NoMoreElementsExpected(self.remaining_span)) + } else { + Ok(()) + } } pub fn expect_key_values(&mut self) -> AstResult { parse_key_values(self) } + + pub fn put_back(&mut self, ast: Ast) { + self.remaining_span.0 = ast.span().0; + self.iter.put_back(ast) + } } impl> Iterator for AstIterator { type Item = Ast; fn next(&mut self) -> Option { - self.iter.next() + self.iter.next().map(|x| { + self.remaining_span.0 = x.span().1; + x + }) } } diff --git a/crates/yuck/src/parser/from_ast.rs b/crates/yuck/src/parser/from_ast.rs index b5f44fd..acff1ef 100644 --- a/crates/yuck/src/parser/from_ast.rs +++ b/crates/yuck/src/parser/from_ast.rs @@ -39,9 +39,9 @@ impl FromAst for T { fn from_ast(e: Ast) -> AstResult { let span = e.span(); let mut iter = e.try_ast_iter()?; - let (_, element_name) = iter.expect_symbol()?; + let (element_name_span, element_name) = iter.expect_symbol()?; if Self::get_element_name() != element_name { - return Err(AstError::MismatchedElementName(span, Self::get_element_name().to_string(), element_name)); + return Err(AstError::MismatchedElementName(element_name_span, Self::get_element_name().to_string(), element_name)); } Self::from_tail(span, iter) } diff --git a/examples/eww-bar/eww.yuck b/examples/eww-bar/eww.yuck index 8ead5d1..ecfa3c2 100644 --- a/examples/eww-bar/eww.yuck +++ b/examples/eww-bar/eww.yuck @@ -1,45 +1,44 @@ (defwidget bar [] - (box :orientation "h" :hexpand true - (workspaces) - (music) - (sidestuff))) - + (box :orientation "h" :hexpand true + (workspaces) + (music) + (sidestuff))) (defwidget sidestuff [] - (box :class "sidestuff" :orientation "h" :space-evenly false :halign "end" - (slider-vol) - (slider-ram) - (time))) + (box :class "sidestuff" :orientation "h" :space-evenly false :halign "end" + (slider-vol) + (slider-ram) + (time))) (defwidget workspaces [] - (box :class "workspaces" :orientation "h" :space-evenly true :halign "start" - (button :onclick "wmctrl -s 0" 1) - (button :onclick "wmctrl -s 1" 2) - (button :onclick "wmctrl -s 2" 3) - (button :onclick "wmctrl -s 3" 4) - (button :onclick "wmctrl -s 4" 5) - (button :onclick "wmctrl -s 5" 6) - (button :onclick "wmctrl -s 6" 7) - (button :onclick "wmctrl -s 7" 8) - (button :onclick "wmctrl -s 8" 9))) + (box :class "workspaces" :orientation "h" :space-evenly true :halign "start" + (button :onclick "wmctrl -s 0" 1) + (button :onclick "wmctrl -s 1" 2) + (button :onclick "wmctrl -s 2" 3) + (button :onclick "wmctrl -s 3" 4) + (button :onclick "wmctrl -s 4" 5) + (button :onclick "wmctrl -s 5" 6) + (button :onclick "wmctrl -s 6" 7) + (button :onclick "wmctrl -s 7" 8) + (button :onclick "wmctrl -s 8" 9))) (defwidget music [] - (box :class "music" :orientation "h" :space-evenly false :halign "center" - { ' ' + music})) + (box :class "music" :orientation "h" :space-evenly false :halign "center" + { ' ' + music})) (defwidget slider-vol [] - (box :class "slider-vol" :orientation "h" :space-evenly "false" - (box :class "label-vol" "" - (scale :min 0 :max 101 :value volume :onchange "amixer -D pulse sset Master {}%")))) + (box :class "slider-vol" :orientation "h" :space-evenly "false" + (box :class "label-vol" "" + (scale :min 0 :max 101 :value volume :onchange "amixer -D pulse sset Master {}%")))) (defwidget slider-ram [] - (box :orientation "h" :class "slider-ram" :space-evenly false - (box :class "label-ram" "" - (scale :min 0 :max 101 :active false :value EWW_RAM)))) + (box :orientation "h" :class "slider-ram" :space-evenly false + (box :class "label-ram" "" + (scale :min 0 :max 101 :active false :value EWW_RAM)))) (defwidget time [] - (box :class "time" - {hour + ":" + min + " " + month + " " + number_day + ", " + year_full})) + (box :class "time" + {hour + ":" + min + " " + month + " " + number_day + ", " + year_full})) (defpoll music :interval "5s" "playerctl metadata --format '{{ artist }} - {{ title }}' || true") @@ -51,12 +50,14 @@ (defpoll hour :interval "1m" "date '+%H'") (defpoll year_full :interval "15h" "date '+%Y'") -(deflisten battery-remaining "/sys/class/power_supply/BAT0/capacity") +(deflisten battery-remaining "tail -f /sys/class/power_supply/BAT0/capacity") (defwindow bar - :screen 0 - :focusable true - :windowtype "dock" - :geometry (geometry :x "0%" :y "0%" :width "100%" :height "4%") - :reserve (struts :side "top" :distance "4%") - (bar)) + :screen 0 + :focusable true + :windowtype "dock" + :geometry (geometry :x "0%" :y "0%" :width "100%" :height "4%") + :reserve (struts :side "top" :distance "4%") + (bar)) + +; asdf From cf25f9eddb5de5f09989ed78d595453569dfc06e Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Wed, 28 Jul 2021 20:20:41 +0200 Subject: [PATCH 088/137] Fix window definition example code --- examples/eww-bar/eww.yuck | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/eww-bar/eww.yuck b/examples/eww-bar/eww.yuck index ecfa3c2..e12e389 100644 --- a/examples/eww-bar/eww.yuck +++ b/examples/eww-bar/eww.yuck @@ -53,8 +53,7 @@ (deflisten battery-remaining "tail -f /sys/class/power_supply/BAT0/capacity") (defwindow bar - :screen 0 - :focusable true + :monitor 0 :windowtype "dock" :geometry (geometry :x "0%" :y "0%" :width "100%" :height "4%") :reserve (struts :side "top" :distance "4%") From ec4506d9e406bbc930ec2d4f1c93e115217bacd6 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Thu, 29 Jul 2021 13:03:37 +0200 Subject: [PATCH 089/137] Make daemon start on server-commands when it's not running --- crates/eww/src/client.rs | 2 +- crates/eww/src/error_handling_ctx.rs | 5 +- crates/eww/src/main.rs | 112 +++++++++++++++++++-------- crates/eww/src/opts.rs | 9 ++- crates/eww/src/server.rs | 38 ++++++--- 5 files changed, 119 insertions(+), 47 deletions(-) diff --git a/crates/eww/src/client.rs b/crates/eww/src/client.rs index 3001357..1297196 100644 --- a/crates/eww/src/client.rs +++ b/crates/eww/src/client.rs @@ -24,7 +24,7 @@ pub fn handle_client_only_action(paths: &EwwPaths, action: ActionClientOnly) -> Ok(()) } -pub fn do_server_call(mut stream: UnixStream, action: opts::ActionWithServer) -> Result> { +pub fn do_server_call(stream: &mut UnixStream, action: &opts::ActionWithServer) -> Result> { log::info!("Forwarding options to server"); stream.set_nonblocking(false).context("Failed to set stream to non-blocking")?; diff --git a/crates/eww/src/error_handling_ctx.rs b/crates/eww/src/error_handling_ctx.rs index 86f8ad4..e126dc8 100644 --- a/crates/eww/src/error_handling_ctx.rs +++ b/crates/eww/src/error_handling_ctx.rs @@ -1,6 +1,9 @@ use std::sync::{Arc, Mutex}; -use codespan_reporting::{diagnostic::Diagnostic, term, term::Chars}; +use codespan_reporting::{ + diagnostic::Diagnostic, + term::{self, Chars}, +}; use eww_shared_util::Span; use simplexpr::eval::EvalError; use yuck::{config::file_provider::YuckFiles, error::AstError, format_diagnostic::ToDiagnostic, gen_diagnostic}; diff --git a/crates/eww/src/main.rs b/crates/eww/src/main.rs index 2b35cab..956dbdd 100644 --- a/crates/eww/src/main.rs +++ b/crates/eww/src/main.rs @@ -13,17 +13,21 @@ extern crate gtk; extern crate gtk_layer_shell as gtk_layer_shell; use anyhow::*; +use opts::ActionWithServer; use std::{ os::unix::net, path::{Path, PathBuf}, + time::Duration, }; +use crate::server::ForkResult; pub mod app; pub mod application_lifecycle; pub mod client; pub mod config; pub mod display_backend; +pub mod error; mod error_handling_ctx; pub mod eww_state; pub mod geometry; @@ -33,9 +37,9 @@ pub mod script_var_handler; pub mod server; pub mod util; pub mod widgets; -pub mod error; fn main() { + let eww_binary_name = std::env::args().next().unwrap(); let opts: opts::Opt = opts::Opt::from_env(); let log_level_filter = if opts.log_debug { log::LevelFilter::Debug } else { log::LevelFilter::Info }; @@ -52,45 +56,55 @@ fn main() { .unwrap_or_else(EwwPaths::default) .context("Failed to initialize eww paths")?; - match opts.action { + let would_show_logs = match opts.action { opts::Action::ClientOnly(action) => { client::handle_client_only_action(&paths, action)?; + false + } + opts::Action::WithServer(ActionWithServer::KillServer) => { + handle_server_command(&paths, &ActionWithServer::KillServer, 1)?; + false } opts::Action::WithServer(action) => { - log::info!("Trying to find server process at socket {}", paths.get_ipc_socket_file().display()); - match net::UnixStream::connect(&paths.get_ipc_socket_file()) { - Ok(stream) => { - log::info!("Connected to Eww server ({}).", &paths.get_ipc_socket_file().display()); - let response = - client::do_server_call(stream, action).context("Error while forwarding command to server")?; - if let Some(response) = response { - println!("{}", response); - if response.is_failure() { - std::process::exit(1); - } - } - } - Err(_) => { - eprintln!("Failed to connect to the eww daemon."); - eprintln!("Make sure to start the eww daemon process by running `eww daemon` first."); - std::process::exit(1); - } - } - } - - opts::Action::Daemon => { - // make sure that there isn't already a Eww daemon running. - if check_server_running(paths.get_ipc_socket_file()) { - eprintln!("Eww server already running."); - std::process::exit(1); - } else { - log::info!("Initializing Eww server. ({})", paths.get_ipc_socket_file().display()); + if let Err(err) = handle_server_command(&paths, &action, 5) { + // connecting to the daemon failed. Thus, start the daemon here! + log::warn!("Failed to connect to daemon: {}", err); + log::info!("Initializing eww server. ({})", paths.get_ipc_socket_file().display()); let _ = std::fs::remove_file(paths.get_ipc_socket_file()); + if !opts.show_logs { + println!("Run `{} logs` to see any errors while editing your configuration.", eww_binary_name); + } - println!("Run `eww logs` to see any errors, warnings or information while editing your configuration."); - server::initialize_server(paths)?; + let (command, response_recv) = action.into_daemon_command(); + let fork_result = server::initialize_server(paths.clone(), Some(command))?; + let is_parent = fork_result == ForkResult::Parent; + if let (Some(recv), true) = (response_recv, is_parent) { + listen_for_daemon_response(recv); + } + is_parent + } else { + true } } + + // make sure that there isn't already a Eww daemon running. + opts::Action::Daemon if check_server_running(paths.get_ipc_socket_file()) => { + eprintln!("Eww server already running."); + true + } + opts::Action::Daemon => { + log::info!("Initializing Eww server. ({})", paths.get_ipc_socket_file().display()); + let _ = std::fs::remove_file(paths.get_ipc_socket_file()); + + if !opts.show_logs { + println!("Run `{} logs` to see any errors while editing your configuration.", eww_binary_name); + } + let fork_result = server::initialize_server(paths.clone(), None)?; + fork_result == ForkResult::Parent + } + }; + if would_show_logs && opts.show_logs { + client::handle_client_only_action(&paths, opts::ActionClientOnly::Logs)?; } }; @@ -100,11 +114,43 @@ fn main() { } } +fn listen_for_daemon_response(mut recv: app::DaemonResponseReceiver) { + let rt = tokio::runtime::Builder::new_current_thread().enable_time().build().expect("Failed to initialize tokio runtime"); + rt.block_on(async { + if let Ok(Some(response)) = tokio::time::timeout(Duration::from_millis(100), recv.recv()).await { + println!("{}", response); + } + }) +} + +fn handle_server_command(paths: &EwwPaths, action: &ActionWithServer, connect_attempts: usize) -> Result<()> { + log::info!("Trying to find server process at socket {}", paths.get_ipc_socket_file().display()); + let mut stream = attempt_connect(&paths.get_ipc_socket_file(), connect_attempts).context("Failed to connect to daemon")?; + log::info!("Connected to Eww server ({}).", &paths.get_ipc_socket_file().display()); + let response = client::do_server_call(&mut stream, action).context("Error while forwarding command to server")?; + if let Some(response) = response { + println!("{}", response); + } + Ok(()) +} + +fn attempt_connect(socket_path: impl AsRef, attempts: usize) -> Option { + for _ in 0..attempts { + if let Ok(mut con) = net::UnixStream::connect(&socket_path) { + if client::do_server_call(&mut con, &opts::ActionWithServer::Ping).is_ok() { + return net::UnixStream::connect(&socket_path).ok(); + } + } + std::thread::sleep(Duration::from_millis(200)); + } + None +} + /// Check if a eww server is currently running by trying to send a ping message to it. fn check_server_running(socket_path: impl AsRef) -> bool { let response = net::UnixStream::connect(socket_path) .ok() - .and_then(|stream| client::do_server_call(stream, opts::ActionWithServer::Ping).ok()); + .and_then(|mut stream| client::do_server_call(&mut stream, &opts::ActionWithServer::Ping).ok()); response.is_some() } diff --git a/crates/eww/src/opts.rs b/crates/eww/src/opts.rs index 9e82045..21b7517 100644 --- a/crates/eww/src/opts.rs +++ b/crates/eww/src/opts.rs @@ -11,6 +11,7 @@ use crate::app; #[derive(Debug, Serialize, Deserialize, PartialEq)] pub struct Opt { pub log_debug: bool, + pub show_logs: bool, pub config_path: Option, pub action: Action, } @@ -25,6 +26,10 @@ struct RawOpt { #[structopt(short, long, global = true)] config: Option, + /// Watch the log output after executing the command + #[structopt(long = "logs", global = true)] + show_logs: bool, + #[structopt(subcommand)] action: Action, } @@ -136,8 +141,8 @@ impl Opt { impl From for Opt { fn from(other: RawOpt) -> Self { - let RawOpt { action, log_debug, config } = other; - Opt { action, log_debug, config_path: config } + let RawOpt { action, log_debug, show_logs, config } = other; + Opt { action, log_debug, show_logs, config_path: config } } } diff --git a/crates/eww/src/server.rs b/crates/eww/src/server.rs index 1457382..784e0a4 100644 --- a/crates/eww/src/server.rs +++ b/crates/eww/src/server.rs @@ -1,4 +1,9 @@ -use crate::{app, config, error_handling_ctx, eww_state::*, ipc_server, script_var_handler, util, EwwPaths}; +use crate::{ + app::{self, DaemonCommand}, + config, error_handling_ctx, + eww_state::*, + ipc_server, script_var_handler, util, EwwPaths, +}; use anyhow::*; use std::{ @@ -9,7 +14,7 @@ use std::{ }; use tokio::sync::mpsc::*; -pub fn initialize_server(paths: EwwPaths) -> Result<()> { +pub fn initialize_server(paths: EwwPaths, action: Option) -> Result { let (ui_send, mut ui_recv) = tokio::sync::mpsc::unbounded_channel(); std::env::set_current_dir(&paths.get_config_dir()) @@ -31,7 +36,11 @@ pub fn initialize_server(paths: EwwPaths) -> Result<()> { } }; - do_detach(&paths.get_log_file())?; + let fork_result = do_detach(&paths.get_log_file())?; + + if fork_result == ForkResult::Parent { + return Ok(ForkResult::Parent); + } println!( r#" @@ -76,6 +85,9 @@ pub fn initialize_server(paths: EwwPaths) -> Result<()> { init_async_part(app.paths.clone(), ui_send); glib::MainContext::default().spawn_local(async move { + if let Some(action) = action { + app.handle_command(action); + } while let Some(event) = ui_recv.recv().await { app.handle_command(event); } @@ -84,7 +96,7 @@ pub fn initialize_server(paths: EwwPaths) -> Result<()> { gtk::main(); log::info!("main application thread finished"); - Ok(()) + Ok(ForkResult::Child) } fn init_async_part(paths: EwwPaths, ui_send: UnboundedSender) { @@ -160,7 +172,7 @@ async fn run_filewatch>(config_dir: P, evt_send: UnboundedSender< tokio::spawn(async move { match daemon_resp_response.recv().await { Some(app::DaemonResponse::Success(_)) => log::info!("Reloaded config successfully"), - Some(app::DaemonResponse::Failure(e)) => log::error!("Failed to reload config: {}", e), + Some(app::DaemonResponse::Failure(e)) => eprintln!("{}", e), None => log::error!("No response to reload configuration-reload request"), } }); @@ -171,14 +183,20 @@ async fn run_filewatch>(config_dir: P, evt_send: UnboundedSender< return Ok(()); } +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum ForkResult { + Parent, + Child, +} + /// detach the process from the terminal, also redirecting stdout and stderr to LOG_FILE -fn do_detach(log_file_path: impl AsRef) -> Result<()> { +fn do_detach(log_file_path: impl AsRef) -> Result { // detach from terminal match unsafe { nix::unistd::fork()? } { - nix::unistd::ForkResult::Parent { .. } => { - std::process::exit(0); - } nix::unistd::ForkResult::Child => {} + nix::unistd::ForkResult::Parent { .. } => { + return Ok(ForkResult::Parent); + } } let file = std::fs::OpenOptions::new() @@ -195,5 +213,5 @@ fn do_detach(log_file_path: impl AsRef) -> Result<()> { nix::unistd::dup2(fd, std::io::stderr().as_raw_fd())?; } - Ok(()) + Ok(ForkResult::Child) } From 41093cf0afa541ac94a68f781f7329b5f16b8ef2 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Thu, 29 Jul 2021 15:30:00 +0200 Subject: [PATCH 090/137] Replace lazy_static with once_cell --- Cargo.lock | 6 ++--- Cargo.toml | 3 +++ crates/eww/Cargo.toml | 4 ++-- crates/eww/src/application_lifecycle.rs | 5 ++--- crates/eww/src/config/system_stats.rs | 9 +++----- crates/eww/src/error_handling_ctx.rs | 5 ++--- crates/eww/src/util.rs | 13 +++++++---- crates/simplexpr/Cargo.toml | 2 +- crates/simplexpr/src/parser/lexer.rs | 5 ++--- crates/yuck/Cargo.toml | 2 +- crates/yuck/src/parser/lexer.rs | 29 ++++++++++--------------- crates/yuck/src/value/coords.rs | 5 ++--- 12 files changed, 42 insertions(+), 46 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 674b57a..d1597d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -451,13 +451,13 @@ dependencies = [ "gtk-layer-shell", "gtk-layer-shell-sys", "itertools 0.10.1", - "lazy_static", "libc", "log", "maplit", "nix", "notify", "num", + "once_cell", "pretty_assertions", "pretty_env_logger", "regex", @@ -1889,9 +1889,9 @@ dependencies = [ "itertools 0.10.1", "lalrpop", "lalrpop-util", - "lazy_static", "logos", "maplit", + "once_cell", "regex", "serde", "serde_json", @@ -2391,8 +2391,8 @@ dependencies = [ "itertools 0.10.1", "lalrpop", "lalrpop-util", - "lazy_static", "maplit", + "once_cell", "pretty_assertions", "regex", "serde", diff --git a/Cargo.toml b/Cargo.toml index ce16374..66b1ea0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,3 +5,6 @@ members = [ "crates/yuck", "crates/eww_shared_util" ] +[profile.dev] +split-debuginfo = "unpacked" + diff --git a/crates/eww/Cargo.toml b/crates/eww/Cargo.toml index 5f65738..db02853 100644 --- a/crates/eww/Cargo.toml +++ b/crates/eww/Cargo.toml @@ -3,7 +3,7 @@ name = "eww" version = "0.1.0" authors = ["elkowar <5300871+elkowar@users.noreply.github.com>"] edition = "2018" -description= "Widget system for everyone!" +description = "Widget system for everyone!" license = "MIT" repository = "https://github.com/elkowar/eww" homepage = "https://github.com/elkowar/eww" @@ -44,8 +44,8 @@ itertools = "0.10" debug_stub_derive = "0.3" log = "0.4" pretty_env_logger = "0.4" -lazy_static = "1.4.0" libc = "0.2" +once_cell = "1.8" nix = "0.20" smart-default = "0.6" simple-signal = "1.1" diff --git a/crates/eww/src/application_lifecycle.rs b/crates/eww/src/application_lifecycle.rs index a44a050..8a5cd3c 100644 --- a/crates/eww/src/application_lifecycle.rs +++ b/crates/eww/src/application_lifecycle.rs @@ -3,11 +3,10 @@ //! `recv_exit()` function which can be awaited to receive an event in case of application termination. use anyhow::*; +use once_cell::sync::Lazy; use tokio::sync::broadcast; -lazy_static::lazy_static! { - static ref APPLICATION_EXIT_SENDER: broadcast::Sender<()> = broadcast::channel(2).0; -} +pub static APPLICATION_EXIT_SENDER: Lazy> = Lazy::new(|| broadcast::channel(2).0); /// Notify all listening tasks of the termination of the eww application process. pub fn send_exit() -> Result<()> { diff --git a/crates/eww/src/config/system_stats.rs b/crates/eww/src/config/system_stats.rs index 282c3a1..1ddb4e4 100644 --- a/crates/eww/src/config/system_stats.rs +++ b/crates/eww/src/config/system_stats.rs @@ -1,13 +1,11 @@ use crate::util::IterAverage; use anyhow::*; use itertools::Itertools; -use lazy_static::lazy_static; +use once_cell::sync::Lazy; use std::{fs::read_to_string, sync::Mutex}; use sysinfo::{ComponentExt, DiskExt, NetworkExt, NetworksExt, ProcessorExt, System, SystemExt}; -lazy_static! { - static ref SYSTEM: Mutex = Mutex::new(System::new()); -} +static SYSTEM: Lazy> = Lazy::new(|| Mutex::new(System::new())); pub fn disk() -> String { let mut c = SYSTEM.lock().unwrap(); @@ -63,7 +61,6 @@ pub fn get_avg_cpu_usage() -> String { #[cfg(target_os = "macos")] pub fn get_battery_capacity() -> Result { - use regex::Regex; let capacity = String::from_utf8( std::process::Command::new("pmset") .args(&["-g", "batt"]) @@ -75,7 +72,7 @@ pub fn get_battery_capacity() -> Result { // Example output of that command: // Now drawing from 'Battery Power' //-InternalBattery-0 (id=11403363) 100%; discharging; (no estimate) present: true - let regex = Regex::new(r"[0-9]*%")?; + let regex = regex!(r"[0-9]*%"); let mut number = regex.captures(&capacity).unwrap().get(0).unwrap().as_str().to_string(); // Removes the % at the end diff --git a/crates/eww/src/error_handling_ctx.rs b/crates/eww/src/error_handling_ctx.rs index e126dc8..0d6681d 100644 --- a/crates/eww/src/error_handling_ctx.rs +++ b/crates/eww/src/error_handling_ctx.rs @@ -5,14 +5,13 @@ use codespan_reporting::{ term::{self, Chars}, }; use eww_shared_util::Span; +use once_cell::sync::Lazy; use simplexpr::eval::EvalError; use yuck::{config::file_provider::YuckFiles, error::AstError, format_diagnostic::ToDiagnostic, gen_diagnostic}; use crate::error::DiagError; -lazy_static::lazy_static! { - pub static ref ERROR_HANDLING_CTX: Arc> = Arc::new(Mutex::new(YuckFiles::new())); -} +pub static ERROR_HANDLING_CTX: Lazy>> = Lazy::new(|| Arc::new(Mutex::new(YuckFiles::new()))); pub fn clear_files() { *ERROR_HANDLING_CTX.lock().unwrap() = YuckFiles::new(); diff --git a/crates/eww/src/util.rs b/crates/eww/src/util.rs index 5d853ed..afbfcc6 100644 --- a/crates/eww/src/util.rs +++ b/crates/eww/src/util.rs @@ -33,6 +33,14 @@ macro_rules! loop_select { } } +#[macro_export] +macro_rules! regex { + ($re:literal $(,)?) => {{ + static RE: once_cell::sync::OnceCell = once_cell::sync::OnceCell::new(); + RE.get_or_init(|| regex::Regex::new($re).unwrap()) + }}; +} + /// Parse a string with a concrete set of options into some data-structure, /// and return a nicely formatted error message on invalid values. I.e.: /// ```rs @@ -142,10 +150,7 @@ impl> IterAverage for I { /// by the actual env-variables. If the env-var isn't found, will replace the /// reference with an empty string. pub fn replace_env_var_references(input: String) -> String { - lazy_static::lazy_static! { - static ref ENV_VAR_PATTERN: regex::Regex = regex::Regex::new(r"\$\{([^\s]*)\}").unwrap(); - } - ENV_VAR_PATTERN + regex!(r"\$\{([^\s]*)\}") .replace_all(&input, |var_name: ®ex::Captures| std::env::var(var_name.get(1).unwrap().as_str()).unwrap_or_default()) .into_owned() } diff --git a/crates/simplexpr/Cargo.toml b/crates/simplexpr/Cargo.toml index 43cb0ff..7d64af8 100644 --- a/crates/simplexpr/Cargo.toml +++ b/crates/simplexpr/Cargo.toml @@ -14,8 +14,8 @@ itertools = "0.10" thiserror = "1.0" maplit = "1.0" logos = "0.12" -lazy_static = "1.4" +once_cell = "1.8" serde = {version = "1.0", features = ["derive"]} serde_json = "1.0" diff --git a/crates/simplexpr/src/parser/lexer.rs b/crates/simplexpr/src/parser/lexer.rs index 8992249..8738925 100644 --- a/crates/simplexpr/src/parser/lexer.rs +++ b/crates/simplexpr/src/parser/lexer.rs @@ -1,10 +1,9 @@ use eww_shared_util::{Span, Spanned}; use logos::Logos; +use once_cell::sync::Lazy; use regex::Regex; -lazy_static::lazy_static! { - static ref ESCAPE_REPLACE_REGEX: Regex = Regex::new(r"\\(.)").unwrap(); -} +static ESCAPE_REPLACE_REGEX: Lazy = Lazy::new(|| Regex::new(r"\\(.)").unwrap()); #[rustfmt::skip] #[derive(Logos, Debug, PartialEq, Eq, Clone, strum::Display, strum::EnumString)] diff --git a/crates/yuck/Cargo.toml b/crates/yuck/Cargo.toml index fcf0a42..ec53745 100644 --- a/crates/yuck/Cargo.toml +++ b/crates/yuck/Cargo.toml @@ -23,8 +23,8 @@ derive_more = "0.99" smart-default = "0.6" serde = {version = "1.0", features = ["derive"]} serde_json = "1.0" -lazy_static = "1.4" pretty_assertions = "0.7" +once_cell = "1.8" strum = { version = "0.21", features = ["derive"] } anyhow = "1" diff --git a/crates/yuck/src/parser/lexer.rs b/crates/yuck/src/parser/lexer.rs index 1b4650d..3d5bf64 100644 --- a/crates/yuck/src/parser/lexer.rs +++ b/crates/yuck/src/parser/lexer.rs @@ -1,3 +1,4 @@ +use once_cell::sync::Lazy; use regex::{Regex, RegexSet}; use super::parse_error; @@ -41,26 +42,20 @@ impl std::fmt::Display for Token { } macro_rules! regex_rules { - ($( - $regex:literal => $token:expr),* - ) => { - lazy_static::lazy_static! { - static ref LEXER_REGEX_SET: RegexSet = RegexSet::new(&[ - $(format!("^{}", $regex)),* - ]).unwrap(); - static ref LEXER_REGEXES: Vec = vec![ - $(Regex::new(&format!("^{}", $regex)).unwrap()),* - ]; - static ref LEXER_FNS: Vec Token + Sync>> = vec![ - $(Box::new($token)),* - ]; - } + ($( $regex:literal => $token:expr),*) => { + static LEXER_REGEX_SET: Lazy = Lazy::new(|| { RegexSet::new(&[ + $(format!("^{}", $regex)),* + ]).unwrap()}); + static LEXER_REGEXES: Lazy> = Lazy::new(|| { vec![ + $(Regex::new(&format!("^{}", $regex)).unwrap()),* + ]}); + static LEXER_FNS: Lazy Token + Sync + Send>>> = Lazy::new(|| { vec![ + $(Box::new($token)),* + ]}); } } -lazy_static::lazy_static! { - static ref ESCAPE_REPLACE_REGEX: Regex = Regex::new(r"\\(.)").unwrap(); -} +static ESCAPE_REPLACE_REGEX: Lazy = Lazy::new(|| Regex::new(r"\\(.)").unwrap()); regex_rules! { r"\(" => |_| Token::LPren, diff --git a/crates/yuck/src/value/coords.rs b/crates/yuck/src/value/coords.rs index b3b49cb..355c2e4 100644 --- a/crates/yuck/src/value/coords.rs +++ b/crates/yuck/src/value/coords.rs @@ -1,4 +1,5 @@ use derive_more::*; +use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; use smart_default::SmartDefault; use std::{fmt, str::FromStr}; @@ -37,9 +38,7 @@ impl FromStr for NumWithUnit { type Err = Error; fn from_str(s: &str) -> Result { - lazy_static::lazy_static! { - static ref PATTERN: regex::Regex = regex::Regex::new("^(-?\\d+)(.*)$").unwrap(); - }; + static PATTERN: Lazy = Lazy::new(|| regex::Regex::new("^(-?\\d+)(.*)$").unwrap()); let captures = PATTERN.captures(s).ok_or_else(|| Error::NumParseFailed(s.to_string()))?; let value = captures.get(1).unwrap().as_str().parse::().map_err(|_| Error::NumParseFailed(s.to_string()))?; From 5cde0228fb63e61feaa21ddbb022cd19b3c844d7 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Fri, 30 Jul 2021 16:29:04 +0200 Subject: [PATCH 091/137] Allow for string-interpolation in simplexpr --- crates/simplexpr/Cargo.toml | 2 +- crates/simplexpr/src/ast.rs | 4 + crates/simplexpr/src/error.rs | 2 +- .../simplexpr/src/parser/lalrpop_helpers.rs | 33 ++ crates/simplexpr/src/parser/lexer.rs | 318 +++++++++++++----- ...xpr__parser__lexer__simplexpr_lexer-2.snap | 32 -- ...xpr__parser__lexer__simplexpr_lexer-3.snap | 16 - ...lexpr__parser__lexer__simplexpr_lexer.snap | 99 ------ ...r__lexer__test__simplexpr_lexer_basic.snap | 33 ++ ...st__simplexpr_lexer_str_interpolate-2.snap | 122 +++++++ ...st__simplexpr_lexer_str_interpolate-3.snap | 24 ++ ...test__simplexpr_lexer_str_interpolate.snap | 58 ++++ .../simplexpr__parser__tests__test-10.snap | 2 +- .../simplexpr__parser__tests__test-11.snap | 2 +- .../simplexpr__parser__tests__test-12.snap | 2 +- .../simplexpr__parser__tests__test-13.snap | 2 +- .../simplexpr__parser__tests__test-2.snap | 2 +- .../simplexpr__parser__tests__test-3.snap | 2 +- .../simplexpr__parser__tests__test-4.snap | 2 +- .../simplexpr__parser__tests__test-5.snap | 2 +- .../simplexpr__parser__tests__test-6.snap | 2 +- .../simplexpr__parser__tests__test-7.snap | 2 +- .../simplexpr__parser__tests__test-8.snap | 2 +- .../simplexpr__parser__tests__test-9.snap | 2 +- .../simplexpr__parser__tests__test.snap | 2 +- crates/simplexpr/src/simplexpr_parser.lalrpop | 30 +- .../snapshots/yuck__config__test__config.snap | 4 +- examples/eww-bar/eww.yuck | 9 +- 28 files changed, 550 insertions(+), 262 deletions(-) delete mode 100644 crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__simplexpr_lexer-2.snap delete mode 100644 crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__simplexpr_lexer-3.snap delete mode 100644 crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__simplexpr_lexer.snap create mode 100644 crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__simplexpr_lexer_basic.snap create mode 100644 crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__simplexpr_lexer_str_interpolate-2.snap create mode 100644 crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__simplexpr_lexer_str_interpolate-3.snap create mode 100644 crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__simplexpr_lexer_str_interpolate.snap diff --git a/crates/simplexpr/Cargo.toml b/crates/simplexpr/Cargo.toml index 7d64af8..97b032a 100644 --- a/crates/simplexpr/Cargo.toml +++ b/crates/simplexpr/Cargo.toml @@ -15,7 +15,7 @@ thiserror = "1.0" maplit = "1.0" logos = "0.12" -once_cell = "1.8" +once_cell = "1.8.0" serde = {version = "1.0", features = ["derive"]} serde_json = "1.0" diff --git a/crates/simplexpr/src/ast.rs b/crates/simplexpr/src/ast.rs index a29ba71..4139e75 100644 --- a/crates/simplexpr/src/ast.rs +++ b/crates/simplexpr/src/ast.rs @@ -31,6 +31,10 @@ pub enum UnaryOp { #[derive(Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum SimplExpr { + // TODO figure out if that span value here is even necessary,.. + // DynVal has span information. However, keeping the span here and the span of the dynval separate + // would allow to separate the span of where a dynval was defined and the + // span of the use-site when a literal is used to replace a varref in the evaluation process. Literal(Span, DynVal), VarRef(Span, VarName), BinOp(Span, Box, BinOp, Box), diff --git a/crates/simplexpr/src/error.rs b/crates/simplexpr/src/error.rs index 047ff96..0985468 100644 --- a/crates/simplexpr/src/error.rs +++ b/crates/simplexpr/src/error.rs @@ -51,7 +51,7 @@ fn get_parse_error_span(file_id: usize, err: &lalrpop_util::ParseError Span(*location, *location, file_id), lalrpop_util::ParseError::UnrecognizedToken { token, expected: _ } => Span(token.0, token.2, file_id), lalrpop_util::ParseError::ExtraToken { token } => Span(token.0, token.2, file_id), - lalrpop_util::ParseError::User { error: LexicalError(l, r, file_id) } => Span(*l, *r, *file_id), + lalrpop_util::ParseError::User { error: LexicalError(span) } => *span, } } diff --git a/crates/simplexpr/src/parser/lalrpop_helpers.rs b/crates/simplexpr/src/parser/lalrpop_helpers.rs index f642a30..7d22f13 100644 --- a/crates/simplexpr/src/parser/lalrpop_helpers.rs +++ b/crates/simplexpr/src/parser/lalrpop_helpers.rs @@ -1,3 +1,36 @@ +use eww_shared_util::Span; +use itertools::Itertools; + +use crate::{ast::BinOp, dynval::DynVal, SimplExpr}; + +use super::lexer::{LexicalError, Sp, StrLitSegment, Token}; + pub fn b(x: T) -> Box { Box::new(x) } + +pub fn parse_stringlit( + span: Span, + segs: Vec>, +) -> Result> { + let file_id = span.2; + let parser = crate::simplexpr_parser::ExprParser::new(); + + segs.into_iter() + .filter_map(|(lo, segment, hi)| { + let span = Span(lo, hi, file_id); + match segment { + StrLitSegment::Literal(lit) if lit.is_empty() => None, + StrLitSegment::Literal(lit) => Some(Ok(SimplExpr::Literal(span, DynVal(lit, span)))), + StrLitSegment::Interp(toks) => { + let token_stream = toks.into_iter().map(|x| Ok(x)); + Some(parser.parse(file_id, token_stream)) + } + } + }) + .fold_ok(None, |acc, cur| match acc { + Some(ast) => Some(SimplExpr::BinOp(span, Box::new(ast), BinOp::Plus, Box::new(cur))), + None => Some(cur), + }) + .map(|ast| ast.unwrap_or_else(|| SimplExpr::Literal(span, DynVal(String::new(), span)))) +} diff --git a/crates/simplexpr/src/parser/lexer.rs b/crates/simplexpr/src/parser/lexer.rs index 8738925..f382b65 100644 --- a/crates/simplexpr/src/parser/lexer.rs +++ b/crates/simplexpr/src/parser/lexer.rs @@ -1,105 +1,273 @@ use eww_shared_util::{Span, Spanned}; -use logos::Logos; use once_cell::sync::Lazy; -use regex::Regex; +use regex::{escape, Regex, RegexSet}; -static ESCAPE_REPLACE_REGEX: Lazy = Lazy::new(|| Regex::new(r"\\(.)").unwrap()); +pub type Sp = (usize, T, usize); -#[rustfmt::skip] -#[derive(Logos, Debug, PartialEq, Eq, Clone, strum::Display, strum::EnumString)] +#[derive(Debug, PartialEq, Eq, Clone, strum::Display, strum::EnumString)] +pub enum StrLitSegment { + Literal(String), + Interp(Vec>), +} + +#[derive(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, + Plus, + Minus, + Times, + Div, + Mod, + Equals, + NotEquals, + And, + Or, + GT, + LT, + Elvis, + RegexMatch, - #[strum(serialize = "!") ] #[token("!") ] Not, + 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, + Comma, + Question, + Colon, + LPren, + RPren, + LBrack, + RBrack, + Dot, + True, + 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| ESCAPE_REPLACE_REGEX.replace_all(x.slice(), "$1").to_string())] - #[regex(r#"`(?:[^`\\]|\\.)*`"#, |x| ESCAPE_REPLACE_REGEX.replace_all(x.slice(), "$1").to_string())] - #[regex(r#"'(?:[^'\\]|\\.)*'"#, |x| ESCAPE_REPLACE_REGEX.replace_all(x.slice(), "$1").to_string())] - StrLit(String), + StringLit(Vec>), + Comment, + Skip, +} - #[error] - #[regex(r"[ \t\n\f]+", logos::skip)] - #[regex(r";.*", logos::skip)] - Error, +macro_rules! regex_rules { + ($( $regex:expr => $token:expr),*) => { + static LEXER_REGEX_SET: Lazy = Lazy::new(|| { RegexSet::new(&[ + $(format!("^{}", $regex)),* + ]).unwrap()}); + static LEXER_REGEXES: Lazy> = Lazy::new(|| { vec![ + $(Regex::new(&format!("^{}", $regex)).unwrap()),* + ]}); + static LEXER_FNS: Lazy Token + Sync + Send>>> = Lazy::new(|| { vec![ + $(Box::new($token)),* + ]}); + } +} + +static ESCAPE_REPLACE_REGEX: Lazy = Lazy::new(|| Regex::new(r"\\(.)").unwrap()); + +regex_rules! { + + escape(r"+") => |_| Token::Plus, + escape(r"-") => |_| Token::Minus, + escape(r"*") => |_| Token::Times, + escape(r"/") => |_| Token::Div, + escape(r"%") => |_| Token::Mod, + escape(r"==") => |_| Token::Equals, + escape(r"!=") => |_| Token::NotEquals, + escape(r"&&") => |_| Token::And, + escape(r"||") => |_| Token::Or, + escape(r">") => |_| Token::GT, + escape(r"<") => |_| Token::LT, + escape(r"?:") => |_| Token::Elvis, + escape(r"=~") => |_| Token::RegexMatch, + + escape(r"!" ) => |_| Token::Not, + + escape(r",") => |_| Token::Comma, + escape(r"?") => |_| Token::Question, + escape(r":") => |_| Token::Colon, + escape(r"(") => |_| Token::LPren, + escape(r")") => |_| Token::RPren, + escape(r"[") => |_| Token::LBrack, + escape(r"]") => |_| Token::RBrack, + escape(r".") => |_| Token::Dot, + escape(r"true") => |_| Token::True, + escape(r"false") => |_| Token::False, + + r"[ \n\n\f]+" => |_| Token::Skip, + r";.*"=> |_| Token::Comment, + + r"[a-zA-Z_-]+" => |x| Token::Ident(x.to_string()), + r"[+-]?(?:[0-9]+[.])?[0-9]+" => |x| Token::NumLit(x.to_string()) +} + +pub struct Lexer<'s> { + file_id: usize, + source: &'s str, + pos: usize, + failed: bool, + offset: usize, +} + +impl<'s> Lexer<'s> { + pub fn new(file_id: usize, span_offset: usize, source: &'s str) -> Self { + Lexer { source, offset: span_offset, file_id, failed: false, pos: 0 } + } + + fn remaining(&self) -> &'s str { + &self.source[self.pos..] + } + + pub fn next_token(&mut self) -> Option, LexicalError>> { + loop { + if self.failed || self.pos >= self.source.len() { + return None; + } + let remaining = self.remaining(); + + if remaining.starts_with(&['"', '\'', '`'][..]) { + return self + .string_lit() + .map(|x| x.map(|(lo, segs, hi)| (lo + self.offset, Token::StringLit(segs), hi + self.offset))); + } else { + let match_set = LEXER_REGEX_SET.matches(remaining); + let matched_token = match_set + .into_iter() + .map(|i: usize| { + let m = LEXER_REGEXES[i].find(remaining).unwrap(); + (m.end(), i) + }) + .min_by_key(|(_, x)| *x); + + let (len, i) = match matched_token { + Some(x) => x, + None => { + self.failed = true; + return Some(Err(LexicalError(Span(self.pos + self.offset, self.pos + self.offset, self.file_id)))); + } + }; + + let tok_str = &self.source[self.pos..self.pos + len]; + let old_pos = self.pos; + self.pos += len; + match LEXER_FNS[i](tok_str.to_string()) { + Token::Skip | Token::Comment => {} + token => { + return Some(Ok((old_pos + self.offset, token, self.pos + self.offset))); + } + } + } + } + } + + fn advance_until_char_boundary(&mut self) { + while self.pos < self.source.len() && !self.source.is_char_boundary(self.pos) { + self.pos += 1; + } + } + + fn advance_until_one_of(&mut self, pat: &[char]) -> Option { + for (idx, cur) in self.remaining().char_indices() { + if let Some(matched) = pat.iter().find(|p| **p == cur) { + self.pos += idx + 1; + return Some(*matched); + } + } + self.pos = self.source.len(); + return None; + } + + fn advance_until_unescaped_one_of(&mut self, pat: &[char]) -> Option { + let mut pattern = pat.to_vec(); + pattern.push('\\'); + match self.advance_until_one_of(pattern.as_slice()) { + Some('\\') => { + self.pos += 1; + self.advance_until_char_boundary(); + self.advance_until_unescaped_one_of(pat) + } + result => result, + } + } + + fn string_lit(&mut self) -> Option>>, LexicalError>> { + let quote = self.remaining().chars().next().unwrap(); + let str_lit_start = self.pos; + self.pos += 1; + self.advance_until_char_boundary(); + + let mut elements = Vec::new(); + let mut in_string_lit = true; + loop { + if in_string_lit { + let segment_start = self.pos - 1; + + let segment_ender = self.advance_until_unescaped_one_of(&['{', quote][..])?; + let lit_content = &self.source[segment_start + 1..self.pos - 1]; + let lit_content = ESCAPE_REPLACE_REGEX.replace_all(lit_content, "$1").to_string(); + elements.push((segment_start + self.offset, StrLitSegment::Literal(lit_content), self.pos + self.offset)); + + if segment_ender == '{' { + in_string_lit = false; + } else if segment_ender == quote { + return Some(Ok((str_lit_start + self.offset, elements, self.pos + self.offset))); + } + } else { + let segment_start = self.pos; + let mut toks = Vec::new(); + while self.pos < self.source.len() && !self.remaining().starts_with('}') { + match self.next_token()? { + Ok(tok) => toks.push(tok), + Err(err) => return Some(Err(err)), + } + } + elements.push((segment_start + self.offset, StrLitSegment::Interp(toks), self.pos + self.offset)); + self.pos += 1; + in_string_lit = true; + } + } + } +} + +impl<'s> Iterator for Lexer<'s> { + type Item = Result, LexicalError>; + + fn next(&mut self) -> Option { + self.next_token() + } } #[derive(Debug, Eq, PartialEq, Copy, Clone)] -pub struct LexicalError(pub usize, pub usize, pub usize); +pub struct LexicalError(pub Span); impl Spanned for LexicalError { fn span(&self) -> Span { - Span(self.0, self.1, self.2) + self.0 } } 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 = Result<(Loc, Tok, Loc), Error>; - -pub struct Lexer<'input> { - lexer: logos::SpannedIter<'input, Token>, - byte_offset: usize, - file_id: usize, -} - -impl<'input> Lexer<'input> { - pub fn new(file_id: usize, byte_offset: usize, text: &'input str) -> Self { - Lexer { lexer: logos::Lexer::new(text).spanned(), byte_offset, file_id } - } -} - -impl<'input> Iterator for Lexer<'input> { - type Item = SpannedResult; - - fn next(&mut self) -> Option { - let (token, range) = self.lexer.next()?; - let range = (range.start + self.byte_offset, range.end + self.byte_offset); - if token == Token::Error { - Some(Err(LexicalError(range.0, range.1, self.file_id))) - } else { - Some(Ok((range.0, token, range.1))) - } + write!(f, "Lexical error at {}", self.0) } } #[cfg(test)] -#[test] -fn test_simplexpr_lexer() { +mod test { + use super::*; use itertools::Itertools; - insta::assert_debug_snapshot!(Lexer::new(0, 0, r#"(foo + - "()" "a\"b" true false [] 12.2)"#).collect_vec()); - insta::assert_debug_snapshot!(Lexer::new(0, 0, r#"" " + music"#).collect_vec()); - insta::assert_debug_snapshot!(Lexer::new(0, 0, r#"foo ; bar"#).collect_vec()); + + #[test] + fn test_simplexpr_lexer_basic() { + insta::assert_debug_snapshot!(Lexer::new(0, 0, r#"bar "foo""#).collect_vec()); + } + + #[test] + fn test_simplexpr_lexer_str_interpolate() { + insta::assert_debug_snapshot!(Lexer::new(0, 0, r#" "foo {2 * 2} bar" "#).collect_vec()); + insta::assert_debug_snapshot!(Lexer::new(0, 0, r#" "foo {(2 * 2) + "{5 + 5}"} bar" "#).collect_vec()); + insta::assert_debug_snapshot!(Lexer::new(0, 0, r#" "a\"b\{}" "#).collect_vec()); + } + // insta::assert_debug_snapshot!(Lexer::new(0, 0, r#"(foo + - "()" "a\"b" true false [] 12.2)"#).collect_vec()); + // insta::assert_debug_snapshot!(Lexer::new(0, 0, r#"" " + music"#).collect_vec()); + // insta::assert_debug_snapshot!(Lexer::new(0, 0, r#"foo ; bar"#).collect_vec()); } diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__simplexpr_lexer-2.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__simplexpr_lexer-2.snap deleted file mode 100644 index 0b21e4b..0000000 --- a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__simplexpr_lexer-2.snap +++ /dev/null @@ -1,32 +0,0 @@ ---- -source: crates/simplexpr/src/parser/lexer.rs -expression: "Lexer::new(0, 0, r#\"\" \" + music\"#).collect_vec()" - ---- -[ - Ok( - ( - 0, - StrLit( - "\"\u{f001} \"", - ), - 8, - ), - ), - Ok( - ( - 9, - Plus, - 10, - ), - ), - Ok( - ( - 11, - Ident( - "music", - ), - 16, - ), - ), -] diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__simplexpr_lexer-3.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__simplexpr_lexer-3.snap deleted file mode 100644 index 1fbd0df..0000000 --- a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__simplexpr_lexer-3.snap +++ /dev/null @@ -1,16 +0,0 @@ ---- -source: crates/simplexpr/src/parser/lexer.rs -expression: "Lexer::new(0, 0, r#\"foo ; bar\"#).collect_vec()" - ---- -[ - Ok( - ( - 0, - Ident( - "foo", - ), - 3, - ), - ), -] diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__simplexpr_lexer.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__simplexpr_lexer.snap deleted file mode 100644 index 841b2c3..0000000 --- a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__simplexpr_lexer.snap +++ /dev/null @@ -1,99 +0,0 @@ ---- -source: crates/simplexpr/src/parser/lexer.rs -expression: "Lexer::new(0, r#\"(foo + - \"()\" \"a\\\"b\" true false [] 12.2)\"#).collect_vec()" - ---- -[ - Ok( - ( - 0, - LPren, - 1, - ), - ), - Ok( - ( - 1, - Ident( - "foo", - ), - 4, - ), - ), - Ok( - ( - 5, - Plus, - 6, - ), - ), - Ok( - ( - 7, - Minus, - 8, - ), - ), - Ok( - ( - 9, - StrLit( - "\"()\"", - ), - 13, - ), - ), - Ok( - ( - 14, - StrLit( - "\"a\"b\"", - ), - 20, - ), - ), - Ok( - ( - 21, - True, - 25, - ), - ), - Ok( - ( - 26, - False, - 31, - ), - ), - Ok( - ( - 32, - LBrack, - 33, - ), - ), - Ok( - ( - 33, - RBrack, - 34, - ), - ), - Ok( - ( - 35, - NumLit( - "12.2", - ), - 39, - ), - ), - Ok( - ( - 39, - RPren, - 40, - ), - ), -] diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__simplexpr_lexer_basic.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__simplexpr_lexer_basic.snap new file mode 100644 index 0000000..063238f --- /dev/null +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__simplexpr_lexer_basic.snap @@ -0,0 +1,33 @@ +--- +source: crates/simplexpr/src/parser/lexer.rs +expression: "Lexer::new(0, 0, r#\"bar \"foo\"\"#).collect_vec()" + +--- +[ + Ok( + ( + 0, + Ident( + "bar", + ), + 3, + ), + ), + Ok( + ( + 4, + StringLit( + [ + ( + 4, + Literal( + "foo", + ), + 9, + ), + ], + ), + 9, + ), + ), +] diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__simplexpr_lexer_str_interpolate-2.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__simplexpr_lexer_str_interpolate-2.snap new file mode 100644 index 0000000..30ddaf6 --- /dev/null +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__simplexpr_lexer_str_interpolate-2.snap @@ -0,0 +1,122 @@ +--- +source: crates/simplexpr/src/parser/lexer.rs +expression: "Lexer::new(0, 0, r#\" \"foo {(2 * 2) + \"{5 + 5}\"} bar\" \"#).collect_vec()" + +--- +[ + Ok( + ( + 1, + StringLit( + [ + ( + 1, + Literal( + "foo ", + ), + 7, + ), + ( + 7, + Interp( + [ + ( + 7, + LPren, + 8, + ), + ( + 8, + NumLit( + "2", + ), + 9, + ), + ( + 10, + Times, + 11, + ), + ( + 12, + NumLit( + "2", + ), + 13, + ), + ( + 13, + RPren, + 14, + ), + ( + 15, + Plus, + 16, + ), + ( + 17, + StringLit( + [ + ( + 17, + Literal( + "", + ), + 19, + ), + ( + 19, + Interp( + [ + ( + 19, + NumLit( + "5", + ), + 20, + ), + ( + 21, + Plus, + 22, + ), + ( + 23, + NumLit( + "5", + ), + 24, + ), + ], + ), + 24, + ), + ( + 24, + Literal( + "", + ), + 26, + ), + ], + ), + 26, + ), + ], + ), + 26, + ), + ( + 26, + Literal( + " bar", + ), + 32, + ), + ], + ), + 32, + ), + ), +] diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__simplexpr_lexer_str_interpolate-3.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__simplexpr_lexer_str_interpolate-3.snap new file mode 100644 index 0000000..4114172 --- /dev/null +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__simplexpr_lexer_str_interpolate-3.snap @@ -0,0 +1,24 @@ +--- +source: crates/simplexpr/src/parser/lexer.rs +expression: "Lexer::new(0, 0, r#\" \"a\\\"b\\{}\" \"#).collect_vec()" + +--- +[ + Ok( + ( + 1, + StringLit( + [ + ( + 1, + Literal( + "a\"b{}", + ), + 10, + ), + ], + ), + 10, + ), + ), +] diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__simplexpr_lexer_str_interpolate.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__simplexpr_lexer_str_interpolate.snap new file mode 100644 index 0000000..1d3ed82 --- /dev/null +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__simplexpr_lexer_str_interpolate.snap @@ -0,0 +1,58 @@ +--- +source: crates/simplexpr/src/parser/lexer.rs +expression: "Lexer::new(0, 0, r#\" \"foo {2 * 2} bar\" \"#).collect_vec()" + +--- +[ + Ok( + ( + 1, + StringLit( + [ + ( + 1, + Literal( + "foo ", + ), + 7, + ), + ( + 7, + Interp( + [ + ( + 7, + NumLit( + "2", + ), + 8, + ), + ( + 9, + Times, + 10, + ), + ( + 11, + NumLit( + "2", + ), + 12, + ), + ], + ), + 12, + ), + ( + 12, + Literal( + " bar", + ), + 18, + ), + ], + ), + 18, + ), + ), +] diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-10.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-10.snap index c9bfa02..ea50a76 100644 --- a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-10.snap +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-10.snap @@ -1,6 +1,6 @@ --- source: crates/simplexpr/src/parser/mod.rs -expression: "p.parse(0, Lexer::new(0, \"\\\"foo\\\" + 12.4\"))" +expression: "p.parse(0, Lexer::new(0, 0, \"\\\"foo\\\" + 12.4\"))" --- Ok( diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-11.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-11.snap index 259a37f..6c8c2db 100644 --- a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-11.snap +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-11.snap @@ -1,6 +1,6 @@ --- source: crates/simplexpr/src/parser/mod.rs -expression: "p.parse(0, Lexer::new(0, \"hi[\\\"ho\\\"]\"))" +expression: "p.parse(0, Lexer::new(0, 0, \"hi[\\\"ho\\\"]\"))" --- Ok( diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-12.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-12.snap index 7e3d9da..0d6c286 100644 --- a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-12.snap +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-12.snap @@ -1,6 +1,6 @@ --- source: crates/simplexpr/src/parser/mod.rs -expression: "p.parse(0, Lexer::new(0, \"foo.bar.baz\"))" +expression: "p.parse(0, Lexer::new(0, 0, \"foo.bar.baz\"))" --- Ok( diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-13.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-13.snap index 6d69829..0ee1103 100644 --- a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-13.snap +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-13.snap @@ -1,6 +1,6 @@ --- source: crates/simplexpr/src/parser/mod.rs -expression: "p.parse(0, Lexer::new(0, \"foo.bar[2 + 2] * asdf[foo.bar]\"))" +expression: "p.parse(0, Lexer::new(0, 0, \"foo.bar[2 + 2] * asdf[foo.bar]\"))" --- Ok( diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-2.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-2.snap index a6a77d0..17d36f7 100644 --- a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-2.snap +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-2.snap @@ -1,6 +1,6 @@ --- source: crates/simplexpr/src/parser/mod.rs -expression: "p.parse(0, Lexer::new(0, \"2 + 5\"))" +expression: "p.parse(0, Lexer::new(0, 0, \"2 + 5\"))" --- Ok( diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-3.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-3.snap index 748f054..f05dc6e 100644 --- a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-3.snap +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-3.snap @@ -1,6 +1,6 @@ --- source: crates/simplexpr/src/parser/mod.rs -expression: "p.parse(0, Lexer::new(0, \"2 * 5 + 1 * 1 + 3\"))" +expression: "p.parse(0, Lexer::new(0, 0, \"2 * 5 + 1 * 1 + 3\"))" --- Ok( diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-4.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-4.snap index ae57e55..f012005 100644 --- a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-4.snap +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-4.snap @@ -1,6 +1,6 @@ --- source: crates/simplexpr/src/parser/mod.rs -expression: "p.parse(0, Lexer::new(0, \"(1 + 2) * 2\"))" +expression: "p.parse(0, Lexer::new(0, 0, \"(1 + 2) * 2\"))" --- Ok( diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-5.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-5.snap index 9ba70aa..637cea6 100644 --- a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-5.snap +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-5.snap @@ -1,6 +1,6 @@ --- source: crates/simplexpr/src/parser/mod.rs -expression: "p.parse(0, Lexer::new(0, \"1 + true ? 2 : 5\"))" +expression: "p.parse(0, Lexer::new(0, 0, \"1 + true ? 2 : 5\"))" --- Ok( diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-6.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-6.snap index 92e68d3..b1f1eb3 100644 --- a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-6.snap +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-6.snap @@ -1,6 +1,6 @@ --- source: crates/simplexpr/src/parser/mod.rs -expression: "p.parse(0, Lexer::new(0, \"1 + true ? 2 : 5 + 2\"))" +expression: "p.parse(0, Lexer::new(0, 0, \"1 + true ? 2 : 5 + 2\"))" --- Ok( diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-7.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-7.snap index 53d78a7..7824d4e 100644 --- a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-7.snap +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-7.snap @@ -1,6 +1,6 @@ --- source: crates/simplexpr/src/parser/mod.rs -expression: "p.parse(0, Lexer::new(0, \"1 + (true ? 2 : 5) + 2\"))" +expression: "p.parse(0, Lexer::new(0, 0, \"1 + (true ? 2 : 5) + 2\"))" --- Ok( diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-8.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-8.snap index d679378..1bde215 100644 --- a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-8.snap +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-8.snap @@ -1,6 +1,6 @@ --- source: crates/simplexpr/src/parser/mod.rs -expression: "p.parse(0, Lexer::new(0, \"foo(1, 2)\"))" +expression: "p.parse(0, Lexer::new(0, 0, \"foo(1, 2)\"))" --- Ok( diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-9.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-9.snap index 0513d5d..14b1288 100644 --- a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-9.snap +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test-9.snap @@ -1,6 +1,6 @@ --- source: crates/simplexpr/src/parser/mod.rs -expression: "p.parse(0, Lexer::new(0, \"! false || ! true\"))" +expression: "p.parse(0, Lexer::new(0, 0, \"! false || ! true\"))" --- Ok( diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test.snap index b5ee229..7e0726c 100644 --- a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test.snap +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__tests__test.snap @@ -1,6 +1,6 @@ --- source: crates/simplexpr/src/parser/mod.rs -expression: "p.parse(0, Lexer::new(0, \"1\"))" +expression: "p.parse(0, Lexer::new(0, 0, \"1\"))" --- Ok( diff --git a/crates/simplexpr/src/simplexpr_parser.lalrpop b/crates/simplexpr/src/simplexpr_parser.lalrpop index 8025af6..a72afd6 100644 --- a/crates/simplexpr/src/simplexpr_parser.lalrpop +++ b/crates/simplexpr/src/simplexpr_parser.lalrpop @@ -1,8 +1,7 @@ use crate::ast::{SimplExpr::{self, *}, BinOp::*, UnaryOp::*}; use eww_shared_util::{Span, VarName}; -use crate::parser::lexer::{Token, LexicalError}; +use crate::parser::lexer::{Token, LexicalError, StrLitSegment, Sp}; use crate::parser::lalrpop_helpers::*; -use lalrpop_util::ParseError; grammar(fid: usize); @@ -42,9 +41,7 @@ extern { "identifier" => Token::Ident(), "number" => Token::NumLit(), - "string" => Token::StrLit(), - - "lexer_error" => Token::Error, + "string" => Token::StringLit(>>), } } @@ -62,11 +59,15 @@ Comma: Vec = { pub Expr: SimplExpr = { #[precedence(level="0")] - "lexer_error" =>? { - Err(ParseError::User { error: LexicalError(l, r, fid) }) - }, + // "lexer_error" =>? { + // Err(ParseError::User { error: LexicalError(l, r, fid) }) + //}, + + =>? parse_stringlit(Span(l, r, fid), x), + => SimplExpr::literal(Span(l, r, fid), x), + "true" => SimplExpr::literal(Span(l, r, fid), "true".into()), + "false" => SimplExpr::literal(Span(l, r, fid), "false".into()), - , => VarRef(Span(l, r, fid), VarName(ident.to_string())), "(" ")", @@ -109,14 +110,3 @@ pub Expr: SimplExpr = { }; ExprReset = ; - -Literal: SimplExpr = { - => SimplExpr::literal(Span(l, r, fid), x), - => SimplExpr::literal(Span(l, r, fid), x), - "true" => SimplExpr::literal(Span(l, r, fid), "true".into()), - "false" => SimplExpr::literal(Span(l, r, fid), "false".into()), -} - -StrLit: String = { - => x[1..x.len() - 1].to_owned(), -}; diff --git a/crates/yuck/src/config/snapshots/yuck__config__test__config.snap b/crates/yuck/src/config/snapshots/yuck__config__test__config.snap index ce4f786..059337b 100644 --- a/crates/yuck/src/config/snapshots/yuck__config__test__config.snap +++ b/crates/yuck/src/config/snapshots/yuck__config__test__config.snap @@ -14,7 +14,7 @@ Config( widget: WidgetUse( name: "foo", attrs: Attributes( - span: Span(47, 47, 51), + span: Span(51, 61, 62), attrs: { AttrName("arg"): AttrEntry( key_span: Span(52, 56, 0), @@ -52,7 +52,7 @@ Config( widget: WidgetUse( name: "bar", attrs: Attributes( - span: Span(463, 463, 467), + span: Span(467, 478, 479), attrs: { AttrName("arg"): AttrEntry( key_span: Span(468, 472, 0), diff --git a/examples/eww-bar/eww.yuck b/examples/eww-bar/eww.yuck index e12e389..888d8ba 100644 --- a/examples/eww-bar/eww.yuck +++ b/examples/eww-bar/eww.yuck @@ -38,11 +38,14 @@ (defwidget time [] (box :class "time" - {hour + ":" + min + " " + month + " " + number_day + ", " + year_full})) + {"{hour}:{min} {month + "the month" * 2} {number_day}, {year_full}"})) + ;{hour + ":" + min + " " + month + " " + number_day + ", " + year_full})) +(defvar music "bruh") -(defpoll music :interval "5s" "playerctl metadata --format '{{ artist }} - {{ title }}' || true") -(defpoll volume :interval "16s" "scripts/getvol") +;(defpoll music :interval "5s" "playerctl metadata --format '{{ artist }} - {{ title }}' || true") +(defvar volume "20") +;(defpoll volume :interval "16s" "scripts/getvol") (defpoll number_day :interval "5h" "date '+%d'") (defpoll month :interval "10h" "date '+%b'") From 24c9fee204e976cbd217955af8ee9eb2e52c7457 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Sat, 31 Jul 2021 13:44:23 +0200 Subject: [PATCH 092/137] Fully implement string interpolation with ${} syntax --- Cargo.lock | 1 + crates/eww/src/error_handling_ctx.rs | 4 +- crates/eww_shared_util/Cargo.toml | 4 + crates/eww_shared_util/src/lib.rs | 33 ++++ crates/simplexpr/src/eval.rs | 20 ++- crates/simplexpr/src/lib.rs | 1 + crates/simplexpr/src/parser/lexer.rs | 93 ++++++----- ...simplexpr__parser__lexer__test__basic.snap | 7 + ...plexpr__parser__lexer__test__comments.snap | 6 + ...plexpr__parser__lexer__test__escaping.snap | 6 + ..._parser__lexer__test__interpolation_1.snap | 6 + ...er__lexer__test__interpolation_nested.snap | 6 + ...xpr__parser__lexer__test__symbol_spam.snap | 17 ++ ...r__lexer__test__weird_char_boundaries.snap | 8 + .../snapshots/yuck__config__test__config.snap | 6 +- crates/yuck/src/error.rs | 4 + crates/yuck/src/format_diagnostic.rs | 9 +- crates/yuck/src/parser/ast_iterator.rs | 19 ++- crates/yuck/src/parser/lexer.rs | 148 +++++++++++------- crates/yuck/src/parser/parser.lalrpop | 18 +-- .../yuck__parser__lexer__test__basic.snap | 6 + ...k__parser__lexer__test__char_boundary.snap | 6 + ...k__parser__lexer__test__escaped_quote.snap | 6 + ..._parser__lexer__test__escaped_strings.snap | 6 + ...parser__lexer__test__quotes_in_quotes.snap | 6 + .../yuck__parser__lexer__yuck_lexer-2.snap | 42 ++++- .../yuck__parser__lexer__yuck_lexer-3.snap | 20 ++- .../yuck__parser__lexer__yuck_lexer-4.snap | 32 +++- .../yuck__parser__lexer__yuck_lexer-5.snap | 16 -- .../yuck__parser__lexer__yuck_lexer.snap | 20 ++- .../snapshots/yuck__parser__test-11.snap | 2 +- .../snapshots/yuck__parser__test-12.snap | 2 +- .../snapshots/yuck__parser__test-13.snap | 2 +- .../snapshots/yuck__parser__test-17.snap | 2 +- examples/eww-bar/eww.yuck | 17 +- examples/eww-bar/scripts/getvol | 2 +- 36 files changed, 440 insertions(+), 163 deletions(-) create mode 100644 crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__basic.snap create mode 100644 crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__comments.snap create mode 100644 crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__escaping.snap create mode 100644 crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__interpolation_1.snap create mode 100644 crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__interpolation_nested.snap create mode 100644 crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__symbol_spam.snap create mode 100644 crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__weird_char_boundaries.snap create mode 100644 crates/yuck/src/parser/snapshots/yuck__parser__lexer__test__basic.snap create mode 100644 crates/yuck/src/parser/snapshots/yuck__parser__lexer__test__char_boundary.snap create mode 100644 crates/yuck/src/parser/snapshots/yuck__parser__lexer__test__escaped_quote.snap create mode 100644 crates/yuck/src/parser/snapshots/yuck__parser__lexer__test__escaped_strings.snap create mode 100644 crates/yuck/src/parser/snapshots/yuck__parser__lexer__test__quotes_in_quotes.snap delete mode 100644 crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-5.snap diff --git a/Cargo.lock b/Cargo.lock index d1597d0..cca7641 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -482,6 +482,7 @@ name = "eww_shared_util" version = "0.1.0" dependencies = [ "derive_more", + "insta", "serde", ] diff --git a/crates/eww/src/error_handling_ctx.rs b/crates/eww/src/error_handling_ctx.rs index 0d6681d..0611e51 100644 --- a/crates/eww/src/error_handling_ctx.rs +++ b/crates/eww/src/error_handling_ctx.rs @@ -6,7 +6,7 @@ use codespan_reporting::{ }; use eww_shared_util::Span; use once_cell::sync::Lazy; -use simplexpr::eval::EvalError; +use simplexpr::{dynval::ConversionError, eval::EvalError}; use yuck::{config::file_provider::YuckFiles, error::AstError, format_diagnostic::ToDiagnostic, gen_diagnostic}; use crate::error::DiagError; @@ -22,6 +22,8 @@ pub fn anyhow_err_to_diagnostic(err: &anyhow::Error) -> Diagnostic { err.diag.clone() } else if let Some(err) = err.downcast_ref::() { err.to_diagnostic() + } else if let Some(err) = err.downcast_ref::() { + err.to_diagnostic() } else if let Some(err) = err.downcast_ref::() { err.to_diagnostic() } else { diff --git a/crates/eww_shared_util/Cargo.toml b/crates/eww_shared_util/Cargo.toml index 3293833..fdcb937 100644 --- a/crates/eww_shared_util/Cargo.toml +++ b/crates/eww_shared_util/Cargo.toml @@ -6,3 +6,7 @@ edition = "2018" [dependencies] serde = {version = "1.0", features = ["derive"]} derive_more = "0.99" + + +[dev-dependencies] +insta = "1.7" diff --git a/crates/eww_shared_util/src/lib.rs b/crates/eww_shared_util/src/lib.rs index de590ff..299f994 100644 --- a/crates/eww_shared_util/src/lib.rs +++ b/crates/eww_shared_util/src/lib.rs @@ -3,3 +3,36 @@ pub mod wrappers; pub use span::*; pub use wrappers::*; + +#[macro_export] +macro_rules! snapshot_debug { + ( $($name:ident => $test:expr),* $(,)?) => { + $( + #[test] + fn $name() { ::insta::assert_debug_snapshot!($test); } + )* + }; +} +#[macro_export] +macro_rules! snapshot_string { + ( $($name:ident => $test:expr),* $(,)?) => { + $( + #[test] + fn $name() { ::insta::assert_snapshot!($test); } + )* + }; +} + +#[macro_export] +macro_rules! snapshot_ron { + ( $($name:ident => $test:expr),* $(,)?) => { + $( + #[test] + fn $name() { + ::insta::with_settings!({sort_maps => true}, { + ::insta::assert_ron_snapshot!($test); + }); + } + )* + }; +} diff --git a/crates/simplexpr/src/eval.rs b/crates/simplexpr/src/eval.rs index 227fa6f..e703797 100644 --- a/crates/simplexpr/src/eval.rs +++ b/crates/simplexpr/src/eval.rs @@ -15,9 +15,13 @@ pub enum EvalError { #[error("Invalid regex: {0}")] InvalidRegex(#[from] regex::Error), + // TODO unresolved and unknown are the same for the user,.... #[error("got unresolved variable `{0}`")] UnresolvedVariable(VarName), + #[error("Unknown variable {0}")] + UnknownVariable(VarName), + #[error(transparent)] ConversionError(#[from] ConversionError), @@ -27,9 +31,6 @@ pub enum EvalError { #[error("Unknown function {0}")] UnknownFunction(String), - #[error("Unknown variable {0}")] - UnknownVariable(VarName), - #[error("Unable to index into value {0}")] CannotIndex(String), @@ -41,6 +42,13 @@ impl EvalError { pub fn at(self, span: Span) -> Self { Self::Spanned(span, Box::new(self)) } + + pub fn map_in_span(self, f: impl FnOnce(Self) -> Self) -> Self { + match self { + EvalError::Spanned(span, err) => EvalError::Spanned(span, Box::new(err.map_in_span(f))), + other => f(other), + } + } } impl Spanned for EvalError { @@ -145,8 +153,10 @@ impl SimplExpr { pub fn eval_no_vars(&self) -> Result { match self.eval(&HashMap::new()) { Ok(x) => Ok(x), - Err(EvalError::UnknownVariable(name)) => Err(EvalError::NoVariablesAllowed(name)), - Err(x) => Err(x), + Err(x) => Err(x.map_in_span(|err| match err { + EvalError::UnknownVariable(name) | EvalError::UnresolvedVariable(name) => EvalError::NoVariablesAllowed(name), + other => other, + })), } } diff --git a/crates/simplexpr/src/lib.rs b/crates/simplexpr/src/lib.rs index b4eefc1..23de86b 100644 --- a/crates/simplexpr/src/lib.rs +++ b/crates/simplexpr/src/lib.rs @@ -1,4 +1,5 @@ #![feature(box_patterns)] +#![feature(pattern)] #![feature(box_syntax)] #![feature(try_blocks)] #![feature(unwrap_infallible)] diff --git a/crates/simplexpr/src/parser/lexer.rs b/crates/simplexpr/src/parser/lexer.rs index f382b65..01b890f 100644 --- a/crates/simplexpr/src/parser/lexer.rs +++ b/crates/simplexpr/src/parser/lexer.rs @@ -1,3 +1,5 @@ +use std::str::pattern::Pattern; + use eww_shared_util::{Span, Spanned}; use once_cell::sync::Lazy; use regex::{escape, Regex, RegexSet}; @@ -63,9 +65,10 @@ macro_rules! regex_rules { } static ESCAPE_REPLACE_REGEX: Lazy = Lazy::new(|| Regex::new(r"\\(.)").unwrap()); +pub static STR_INTERPOLATION_START: &str = "${"; +pub static STR_INTERPOLATION_END: &str = "}"; regex_rules! { - escape(r"+") => |_| Token::Plus, escape(r"-") => |_| Token::Minus, escape(r"*") => |_| Token::Times, @@ -100,6 +103,7 @@ regex_rules! { r"[+-]?(?:[0-9]+[.])?[0-9]+" => |x| Token::NumLit(x.to_string()) } +#[derive(Debug)] pub struct Lexer<'s> { file_id: usize, source: &'s str, @@ -117,6 +121,10 @@ impl<'s> Lexer<'s> { &self.source[self.pos..] } + pub fn continues_with(&self, pat: impl Pattern<'s>) -> bool { + self.remaining().starts_with(pat) + } + pub fn next_token(&mut self) -> Option, LexicalError>> { loop { if self.failed || self.pos >= self.source.len() { @@ -125,9 +133,7 @@ impl<'s> Lexer<'s> { let remaining = self.remaining(); if remaining.starts_with(&['"', '\'', '`'][..]) { - return self - .string_lit() - .map(|x| x.map(|(lo, segs, hi)| (lo + self.offset, Token::StringLit(segs), hi + self.offset))); + return self.string_lit().map(|x| x.map(|(lo, segs, hi)| (lo, Token::StringLit(segs), hi))); } else { let match_set = LEXER_REGEX_SET.matches(remaining); let matched_token = match_set @@ -148,7 +154,7 @@ impl<'s> Lexer<'s> { let tok_str = &self.source[self.pos..self.pos + len]; let old_pos = self.pos; - self.pos += len; + self.advance_by(len); match LEXER_FNS[i](tok_str.to_string()) { Token::Skip | Token::Comment => {} token => { @@ -159,54 +165,56 @@ impl<'s> Lexer<'s> { } } - fn advance_until_char_boundary(&mut self) { + fn advance_by(&mut self, n: usize) { + self.pos += n; while self.pos < self.source.len() && !self.source.is_char_boundary(self.pos) { self.pos += 1; } } - fn advance_until_one_of(&mut self, pat: &[char]) -> Option { - for (idx, cur) in self.remaining().char_indices() { - if let Some(matched) = pat.iter().find(|p| **p == cur) { - self.pos += idx + 1; - return Some(*matched); + fn advance_until_one_of<'a>(&mut self, pat: &[&'a str]) -> Option<&'a str> { + loop { + let remaining = self.remaining(); + if remaining.is_empty() { + return None; + } else if let Some(matched) = pat.iter().find(|&&p| remaining.starts_with(p)) { + self.advance_by(matched.len()); + return Some(matched); + } else { + self.advance_by(1); } } - self.pos = self.source.len(); - return None; } - fn advance_until_unescaped_one_of(&mut self, pat: &[char]) -> Option { + fn advance_until_unescaped_one_of<'a>(&mut self, pat: &[&'a str]) -> Option<&'a str> { let mut pattern = pat.to_vec(); - pattern.push('\\'); + pattern.push("\\"); match self.advance_until_one_of(pattern.as_slice()) { - Some('\\') => { - self.pos += 1; - self.advance_until_char_boundary(); + Some("\\") => { + self.advance_by(1); self.advance_until_unescaped_one_of(pat) } result => result, } } - fn string_lit(&mut self) -> Option>>, LexicalError>> { - let quote = self.remaining().chars().next().unwrap(); + pub fn string_lit(&mut self) -> Option>>, LexicalError>> { + let quote = self.remaining().chars().next()?.to_string(); let str_lit_start = self.pos; - self.pos += 1; - self.advance_until_char_boundary(); + self.advance_by(quote.len()); let mut elements = Vec::new(); let mut in_string_lit = true; loop { if in_string_lit { - let segment_start = self.pos - 1; + let segment_start = self.pos - quote.len(); - let segment_ender = self.advance_until_unescaped_one_of(&['{', quote][..])?; - let lit_content = &self.source[segment_start + 1..self.pos - 1]; + let segment_ender = self.advance_until_unescaped_one_of(&[STR_INTERPOLATION_START, "e])?; + let lit_content = &self.source[segment_start + quote.len()..self.pos - segment_ender.len()]; let lit_content = ESCAPE_REPLACE_REGEX.replace_all(lit_content, "$1").to_string(); elements.push((segment_start + self.offset, StrLitSegment::Literal(lit_content), self.pos + self.offset)); - if segment_ender == '{' { + if segment_ender == STR_INTERPOLATION_START { in_string_lit = false; } else if segment_ender == quote { return Some(Ok((str_lit_start + self.offset, elements, self.pos + self.offset))); @@ -214,14 +222,14 @@ impl<'s> Lexer<'s> { } else { let segment_start = self.pos; let mut toks = Vec::new(); - while self.pos < self.source.len() && !self.remaining().starts_with('}') { + while self.pos < self.source.len() && !self.remaining().starts_with(STR_INTERPOLATION_END) { match self.next_token()? { Ok(tok) => toks.push(tok), Err(err) => return Some(Err(err)), } } elements.push((segment_start + self.offset, StrLitSegment::Interp(toks), self.pos + self.offset)); - self.pos += 1; + self.advance_by(STR_INTERPOLATION_END.len()); in_string_lit = true; } } @@ -254,20 +262,27 @@ impl std::fmt::Display for LexicalError { #[cfg(test)] mod test { use super::*; + use eww_shared_util::snapshot_string; use itertools::Itertools; - #[test] - fn test_simplexpr_lexer_basic() { - insta::assert_debug_snapshot!(Lexer::new(0, 0, r#"bar "foo""#).collect_vec()); + macro_rules! v { + ($x:literal) => { + Lexer::new(0, 0, $x) + .map(|x| match x { + Ok((l, x, r)) => format!("({}, {:?}, {})", l, x, r), + Err(err) => format!("{}", err), + }) + .join("\n") + }; } - #[test] - fn test_simplexpr_lexer_str_interpolate() { - insta::assert_debug_snapshot!(Lexer::new(0, 0, r#" "foo {2 * 2} bar" "#).collect_vec()); - insta::assert_debug_snapshot!(Lexer::new(0, 0, r#" "foo {(2 * 2) + "{5 + 5}"} bar" "#).collect_vec()); - insta::assert_debug_snapshot!(Lexer::new(0, 0, r#" "a\"b\{}" "#).collect_vec()); + snapshot_string! { + basic => v!(r#"bar "foo""#), + interpolation_1 => v!(r#" "foo ${2 * 2} bar" "#), + interpolation_nested => v!(r#" "foo ${(2 * 2) + "${5 + 5}"} bar" "#), + escaping => v!(r#" "a\"b\{}" "#), + comments => v!("foo ; bar"), + weird_char_boundaries => v!(r#"" " + music"#), + symbol_spam => v!(r#"(foo + - "()" "a\"b" true false [] 12.2)"#), } - // insta::assert_debug_snapshot!(Lexer::new(0, 0, r#"(foo + - "()" "a\"b" true false [] 12.2)"#).collect_vec()); - // insta::assert_debug_snapshot!(Lexer::new(0, 0, r#"" " + music"#).collect_vec()); - // insta::assert_debug_snapshot!(Lexer::new(0, 0, r#"foo ; bar"#).collect_vec()); } diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__basic.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__basic.snap new file mode 100644 index 0000000..a7382f1 --- /dev/null +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__basic.snap @@ -0,0 +1,7 @@ +--- +source: crates/simplexpr/src/parser/lexer.rs +expression: "v!(r#\"bar \"foo\"\"#)" + +--- +(0, Ident("bar"), 3) +(4, StringLit([(4, Literal("foo"), 9)]), 9) diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__comments.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__comments.snap new file mode 100644 index 0000000..281e375 --- /dev/null +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__comments.snap @@ -0,0 +1,6 @@ +--- +source: crates/simplexpr/src/parser/lexer.rs +expression: "v!(\"foo ; bar\")" + +--- +(0, Ident("foo"), 3) diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__escaping.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__escaping.snap new file mode 100644 index 0000000..e0d89c0 --- /dev/null +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__escaping.snap @@ -0,0 +1,6 @@ +--- +source: crates/simplexpr/src/parser/lexer.rs +expression: "v!(r#\" \"a\\\"b\\{}\" \"#)" + +--- +(1, StringLit([(1, Literal("a\"b{}"), 10)]), 10) diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__interpolation_1.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__interpolation_1.snap new file mode 100644 index 0000000..8c5b37b --- /dev/null +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__interpolation_1.snap @@ -0,0 +1,6 @@ +--- +source: crates/simplexpr/src/parser/lexer.rs +expression: "v!(r#\" \"foo ${2 * 2} bar\" \"#)" + +--- +(1, StringLit([(1, Literal("foo "), 8), (8, Interp([(8, NumLit("2"), 9), (10, Times, 11), (12, NumLit("2"), 13)]), 13), (13, Literal(" bar"), 19)]), 19) diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__interpolation_nested.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__interpolation_nested.snap new file mode 100644 index 0000000..dd53372 --- /dev/null +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__interpolation_nested.snap @@ -0,0 +1,6 @@ +--- +source: crates/simplexpr/src/parser/lexer.rs +expression: "v!(r#\" \"foo ${(2 * 2) + \"${5 + 5}\"} bar\" \"#)" + +--- +(1, StringLit([(1, Literal("foo "), 8), (8, Interp([(8, LPren, 9), (9, NumLit("2"), 10), (11, Times, 12), (13, NumLit("2"), 14), (14, RPren, 15), (16, Plus, 17), (18, StringLit([(18, Literal(""), 21), (21, Interp([(21, NumLit("5"), 22), (23, Plus, 24), (25, NumLit("5"), 26)]), 26), (26, Literal(""), 28)]), 28)]), 28), (28, Literal(" bar"), 34)]), 34) diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__symbol_spam.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__symbol_spam.snap new file mode 100644 index 0000000..f44221e --- /dev/null +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__symbol_spam.snap @@ -0,0 +1,17 @@ +--- +source: crates/simplexpr/src/parser/lexer.rs +expression: "v!(r#\"(foo + - \"()\" \"a\\\"b\" true false [] 12.2)\"#)" + +--- +(0, LPren, 1) +(1, Ident("foo"), 4) +(5, Plus, 6) +(7, Minus, 8) +(9, StringLit([(9, Literal("()"), 13)]), 13) +(14, StringLit([(14, Literal("a\"b"), 20)]), 20) +(21, True, 25) +(26, False, 31) +(32, LBrack, 33) +(33, RBrack, 34) +(35, NumLit("12.2"), 39) +(39, RPren, 40) diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__weird_char_boundaries.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__weird_char_boundaries.snap new file mode 100644 index 0000000..3ced076 --- /dev/null +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__weird_char_boundaries.snap @@ -0,0 +1,8 @@ +--- +source: crates/simplexpr/src/parser/lexer.rs +expression: "v!(r#\"\" \" + music\"#)" + +--- +(0, StringLit([(0, Literal("\u{f001} "), 8)]), 8) +(9, Plus, 10) +(11, Ident("music"), 16) diff --git a/crates/yuck/src/config/snapshots/yuck__config__test__config.snap b/crates/yuck/src/config/snapshots/yuck__config__test__config.snap index 059337b..ef3554d 100644 --- a/crates/yuck/src/config/snapshots/yuck__config__test__config.snap +++ b/crates/yuck/src/config/snapshots/yuck__config__test__config.snap @@ -18,7 +18,7 @@ Config( attrs: { AttrName("arg"): AttrEntry( key_span: Span(52, 56, 0), - value: Literal(Span(57, 61, 0), DynVal("hi", Span(18446744073709551615, 18446744073709551615, 18446744073709551615))), + value: SimplExpr(Span(57, 61, 0), Literal(Span(57, 61, 0), DynVal("hi", Span(57, 61, 0)))), ), }, ), @@ -56,7 +56,7 @@ Config( attrs: { AttrName("arg"): AttrEntry( key_span: Span(468, 472, 0), - value: Literal(Span(473, 478, 0), DynVal("bla", Span(18446744073709551615, 18446744073709551615, 18446744073709551615))), + value: SimplExpr(Span(473, 478, 0), Literal(Span(473, 478, 0), DynVal("bla", Span(473, 478, 0)))), ), }, ), @@ -79,7 +79,7 @@ Config( var_definitions: { VarName("some_var"): VarDefinition( name: VarName("some_var"), - initial_value: DynVal("bla", Span(18446744073709551615, 18446744073709551615, 18446744073709551615)), + initial_value: DynVal("bla", Span(89, 94, 0)), span: Span(72, 95, 0), ), }, diff --git a/crates/yuck/src/error.rs b/crates/yuck/src/error.rs index 22ea68e..9c22e2a 100644 --- a/crates/yuck/src/error.rs +++ b/crates/yuck/src/error.rs @@ -39,6 +39,9 @@ pub enum AstError { #[error("{1}")] ErrorNote(String, #[source] Box), + #[error(transparent)] + SimplExpr(#[from] simplexpr::error::Error), + #[error(transparent)] ConversionError(#[from] dynval::ConversionError), @@ -94,6 +97,7 @@ impl Spanned for AstError { AstError::ParseError { file_id, source } => get_parse_error_span(*file_id, source), AstError::ErrorNote(_, err) => err.span(), AstError::NoMoreElementsExpected(span) => *span, + AstError::SimplExpr(err) => err.span(), } } } diff --git a/crates/yuck/src/format_diagnostic.rs b/crates/yuck/src/format_diagnostic.rs index 1f7237f..3653082 100644 --- a/crates/yuck/src/format_diagnostic.rs +++ b/crates/yuck/src/format_diagnostic.rs @@ -118,6 +118,7 @@ impl ToDiagnostic for AstError { AstError::ErrorNote(note, source) => source.to_diagnostic().with_notes(vec![note.to_string()]), AstError::ValidationError(source) => source.to_diagnostic(), AstError::NoMoreElementsExpected(span) => gen_diagnostic!(self, span), + AstError::SimplExpr(source) => source.to_diagnostic(), } } } @@ -185,12 +186,13 @@ fn lalrpop_error_to_diagnostic( impl ToDiagnostic for simplexpr::error::Error { fn to_diagnostic(&self) -> Diagnostic { use simplexpr::error::Error::*; + dbg!(&self); match self { ParseError { source, file_id } => lalrpop_error_to_diagnostic(source, *file_id), ConversionError(error) => error.to_diagnostic(), Eval(error) => error.to_diagnostic(), Other(error) => gen_diagnostic!(error), - Spanned(span, error) => gen_diagnostic!(error, span), + Spanned(span, error) => error.to_diagnostic().with_label(span_to_primary_label(*span)), } } } @@ -205,7 +207,10 @@ impl ToDiagnostic for simplexpr::eval::EvalError { fn to_diagnostic(&self) -> Diagnostic { use simplexpr::eval::EvalError::*; match self { - UnresolvedVariable(name) | UnknownVariable(name) | NoVariablesAllowed(name) => gen_diagnostic! { + NoVariablesAllowed(name) => gen_diagnostic!(self), + // TODO the note here is confusing when it's an unknown variable being used _within_ a string literal / simplexpr + // it only really makes sense on top-level symbols + UnresolvedVariable(name) | UnknownVariable(name) => gen_diagnostic! { msg = self, note = format!("If you meant to use the literal value \"{}\", surround the value in quotes", name) }, diff --git a/crates/yuck/src/parser/ast_iterator.rs b/crates/yuck/src/parser/ast_iterator.rs index aebe312..63c61de 100644 --- a/crates/yuck/src/parser/ast_iterator.rs +++ b/crates/yuck/src/parser/ast_iterator.rs @@ -39,12 +39,29 @@ macro_rules! return_or_put_back { impl> AstIterator { return_or_put_back!(expect_symbol, AstType::Symbol, (Span, String) = Ast::Symbol(span, x) => (span, x)); - return_or_put_back!(expect_literal, AstType::Literal, (Span, DynVal) = Ast::Literal(span, x) => (span, x)); + // return_or_put_back!(expect_literal, AstType::Literal, (Span, DynVal) = Ast::Literal(span, x) => (span, x)); return_or_put_back!(expect_list, AstType::List, (Span, Vec) = Ast::List(span, x) => (span, x)); return_or_put_back!(expect_array, AstType::Array, (Span, Vec) = Ast::Array(span, x) => (span, x)); + pub fn expect_literal(&mut self) -> AstResult<(Span, DynVal)> { + match self.expect_any()? { + // Ast::List(_, _) => todo!(), + // Ast::Array(_, _) => todo!(), + // Ast::Keyword(_, _) => todo!(), + // Ast::Symbol(_, _) => todo!(), + // Ast::Literal(_, _) => todo!(), + Ast::SimplExpr(span, expr) => Ok((span, dbg!(expr.eval_no_vars()).map_err(|e| AstError::SimplExpr(e.into()))?)), + other => { + let span = other.span(); + let actual_type = other.expr_type(); + self.put_back(other); + Err(AstError::WrongExprType(span, AstType::Literal, actual_type)) + } + } + } + pub fn new(span: Span, iter: I) -> Self { AstIterator { remaining_span: span, iter: itertools::put_back(iter) } } diff --git a/crates/yuck/src/parser/lexer.rs b/crates/yuck/src/parser/lexer.rs index 3d5bf64..edfe6e5 100644 --- a/crates/yuck/src/parser/lexer.rs +++ b/crates/yuck/src/parser/lexer.rs @@ -1,8 +1,9 @@ use once_cell::sync::Lazy; -use regex::{Regex, RegexSet}; +use regex::{escape, Regex, RegexSet}; +use simplexpr::parser::lexer::{STR_INTERPOLATION_END, STR_INTERPOLATION_START}; use super::parse_error; -use eww_shared_util::{AttrName, Span, VarName}; +use eww_shared_util::{AttrName, Span, Spanned, VarName}; #[derive(Debug, PartialEq, Eq, Clone)] pub enum Token { @@ -12,11 +13,10 @@ pub enum Token { RBrack, True, False, - StrLit(String), NumLit(String), Symbol(String), Keyword(String), - SimplExpr(String), + SimplExpr(Vec<(usize, simplexpr::parser::lexer::Token, usize)>), Comment, Skip, } @@ -30,11 +30,10 @@ impl std::fmt::Display for Token { Token::RBrack => write!(f, "']'"), Token::True => write!(f, "true"), Token::False => write!(f, "false"), - Token::StrLit(x) => write!(f, "\"{}\"", x), Token::NumLit(x) => write!(f, "{}", x), Token::Symbol(x) => write!(f, "{}", x), Token::Keyword(x) => write!(f, "{}", x), - Token::SimplExpr(x) => write!(f, "{{{}}}", x), + Token::SimplExpr(x) => write!(f, "{{{:?}}}", x.iter().map(|x| &x.1)), Token::Comment => write!(f, ""), Token::Skip => write!(f, ""), } @@ -42,7 +41,7 @@ impl std::fmt::Display for Token { } macro_rules! regex_rules { - ($( $regex:literal => $token:expr),*) => { + ($( $regex:expr => $token:expr),*) => { static LEXER_REGEX_SET: Lazy = Lazy::new(|| { RegexSet::new(&[ $(format!("^{}", $regex)),* ]).unwrap()}); @@ -58,15 +57,12 @@ macro_rules! regex_rules { static ESCAPE_REPLACE_REGEX: Lazy = Lazy::new(|| Regex::new(r"\\(.)").unwrap()); regex_rules! { - r"\(" => |_| Token::LPren, - r"\)" => |_| Token::RPren, - r"\[" => |_| Token::LBrack, - r"\]" => |_| Token::RBrack, - r"true" => |_| Token::True, - r"false" => |_| Token::False, - r#""(?:[^"\\]|\\.)*""# => |x| Token::StrLit(ESCAPE_REPLACE_REGEX.replace_all(&x, "$1").to_string()), - r#"`(?:[^`\\]|\\.)*`"# => |x| Token::StrLit(ESCAPE_REPLACE_REGEX.replace_all(&x, "$1").to_string()), - r#"'(?:[^'\\]|\\.)*'"# => |x| Token::StrLit(ESCAPE_REPLACE_REGEX.replace_all(&x, "$1").to_string()), + escape("(") => |_| Token::LPren, + escape(")") => |_| Token::RPren, + escape("[") => |_| Token::LBrack, + escape("]") => |_| Token::RBrack, + escape("true") => |_| Token::True, + escape("false") => |_| Token::False, r#"[+-]?(?:[0-9]+[.])?[0-9]+"# => |x| Token::NumLit(x), r#":[^\s\)\]}]+"# => |x| Token::Keyword(x), r#"[a-zA-Z_!\?<>/\.\*-\+\-][^\s{}\(\)\[\](){}]*"# => |x| Token::Symbol(x), @@ -85,9 +81,53 @@ impl Lexer { pub fn new(file_id: usize, source: String) -> Self { Lexer { source, file_id, failed: false, pos: 0 } } -} -// TODO string literal interpolation stuff by looking for indexes of {{ and }}? + fn string_lit(&mut self) -> Option> { + let mut simplexpr_lexer = simplexpr::parser::lexer::Lexer::new(self.file_id, self.pos, &self.source[self.pos..]); + match simplexpr_lexer.string_lit() { + Some(Ok((lo, segments, hi))) => { + self.pos = hi; + self.advance_until_char_boundary(); + Some(Ok((lo, Token::SimplExpr(vec![(lo, simplexpr::parser::lexer::Token::StringLit(segments), hi)]), hi))) + } + Some(Err(e)) => Some(Err(parse_error::ParseError::LexicalError(e.0))), + None => None, + } + } + + fn simplexpr(&mut self) -> Option> { + self.pos += 1; + let mut simplexpr_lexer = simplexpr::parser::lexer::Lexer::new(self.file_id, self.pos, &self.source[self.pos..]); + let mut toks = Vec::new(); + let mut end = self.pos; + loop { + match simplexpr_lexer.next_token() { + Some(Ok((lo, tok, hi))) => { + end = hi; + toks.push((lo, tok, hi)); + } + Some(Err(err)) => { + dbg!(&simplexpr_lexer); + if simplexpr_lexer.continues_with('}') { + let start = toks.first().map(|x| x.0).unwrap_or(end); + self.pos = end + 1; + self.advance_until_char_boundary(); + return Some(Ok((start, Token::SimplExpr(toks), end))); + } else { + return Some(Err(parse_error::ParseError::LexicalError(err.span()))); + } + } + None => return None, + } + } + } + + fn advance_until_char_boundary(&mut self) { + while self.pos < self.source.len() && !self.source.is_char_boundary(self.pos) { + self.pos += 1; + } + } +} impl Iterator for Lexer { type Item = Result<(usize, Token, usize), parse_error::ParseError>; @@ -97,45 +137,17 @@ impl Iterator for Lexer { if self.failed || self.pos >= self.source.len() { return None; } - let string = &self.source[self.pos..]; - - if string.starts_with('{') { - let expr_start = self.pos; - let mut in_string = None; - loop { - if self.pos >= self.source.len() { - return None; - } - while !self.source.is_char_boundary(self.pos) { - self.pos += 1; - } - let string = &self.source[self.pos..]; - - if string.starts_with('}') && in_string.is_none() { - self.pos += 1; - let tok_str = &self.source[expr_start..self.pos]; - return Some(Ok((expr_start, Token::SimplExpr(tok_str.to_string()), self.pos - 1))); - } else if string.starts_with('"') || string.starts_with('\'') || string.starts_with('`') { - if let Some(quote) = in_string { - if string.starts_with(quote) { - in_string = None; - } - } else { - in_string = Some(string.chars().next().unwrap()); - } - self.pos += 1; - } else if string.starts_with("\\\"") { - self.pos += 2; - } else { - self.pos += 1; - } - } + let remaining = &self.source[self.pos..]; + if remaining.starts_with(&['"', '\'', '`'][..]) { + return self.string_lit(); + } else if remaining.starts_with('{') { + return self.simplexpr(); } else { - let match_set = LEXER_REGEX_SET.matches(string); + let match_set = LEXER_REGEX_SET.matches(remaining); let matched_token = match_set .into_iter() .map(|i: usize| { - let m = LEXER_REGEXES[i].find(string).unwrap(); + let m = LEXER_REGEXES[i].find(remaining).unwrap(); (m.end(), i) }) .min_by_key(|(_, x)| *x); @@ -163,12 +175,28 @@ impl Iterator for Lexer { } #[cfg(test)] -#[test] -fn test_yuck_lexer() { +mod test { + + use super::*; + use eww_shared_util::snapshot_string; use itertools::Itertools; - insta::assert_debug_snapshot!(Lexer::new(0, r#"(foo + - "text" )"#.to_string()).collect_vec()); - insta::assert_debug_snapshot!(Lexer::new(0, r#"{ bla "} \" }" " \" "}"#.to_string()).collect_vec()); - insta::assert_debug_snapshot!(Lexer::new(0, r#""< \" >""#.to_string()).collect_vec()); - insta::assert_debug_snapshot!(Lexer::new(0, r#"{ " " + music}"#.to_string()).collect_vec()); - insta::assert_debug_snapshot!(Lexer::new(0, r#"{ " } ' }" }"#.to_string()).collect_vec()); + + macro_rules! v { + ($x:literal) => { + Lexer::new(0, 0, $x) + .map(|x| match x { + Ok((l, x, r)) => format!("({}, {:?}, {})", l, x, r), + Err(err) => format!("{}", err), + }) + .join("\n") + }; + } + + snapshot_string! { + basic => r#"(foo + - "text" )"#, + escaped_strings => r#"{ bla "} \" }" " \" "}"#, + escaped_quote => r#""< \" >""#, + char_boundary => r#"{ " " + music}"#, + quotes_in_quotes => r#"{ " } ' }" }"#, + } } diff --git a/crates/yuck/src/parser/parser.lalrpop b/crates/yuck/src/parser/parser.lalrpop index a1d30d5..9f65b8f 100644 --- a/crates/yuck/src/parser/parser.lalrpop +++ b/crates/yuck/src/parser/parser.lalrpop @@ -18,11 +18,10 @@ extern { "]" => Token::RBrack, "true" => Token::True, "false" => Token::False, - "string" => Token::StrLit(), "number" => Token::NumLit(), "symbol" => Token::Symbol(), "keyword" => Token::Keyword(), - "simplexpr" => Token::SimplExpr(), + "simplexpr" => Token::SimplExpr(>), "comment" => Token::Comment, } } @@ -45,22 +44,17 @@ Keyword: Ast = => Ast::Keyword(Span(l, r, file_id), Symbol: Ast = => Ast::Symbol(Span(l, r, file_id), x.to_string()); Literal: String = { - => <>, => <>, => <>, }; -StrLit: String = { - => { - x[1..x.len() - 1].to_owned() - }, -}; - SimplExpr: SimplExpr = { =>? { - let expr = x[1..x.len() - 1].to_string(); - simplexpr::parse_string(l + 1, file_id, &expr).map_err(|e| { - ParseError::User { error: parse_error::ParseError::SimplExpr(e) }}) + let parser = simplexpr::simplexpr_parser::ExprParser::new(); + parser.parse(file_id, x.into_iter().map(Ok)) + .map_err(|e| ParseError::User { + error: parse_error::ParseError::SimplExpr(simplexpr::error::Error::from_parse_error(file_id, e)) + }) } } diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__lexer__test__basic.snap b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__test__basic.snap new file mode 100644 index 0000000..e8d564a --- /dev/null +++ b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__test__basic.snap @@ -0,0 +1,6 @@ +--- +source: crates/yuck/src/parser/lexer.rs +expression: "r#\"(foo + - \"text\" )\"#" + +--- +(foo + - "text" ) diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__lexer__test__char_boundary.snap b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__test__char_boundary.snap new file mode 100644 index 0000000..708318b --- /dev/null +++ b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__test__char_boundary.snap @@ -0,0 +1,6 @@ +--- +source: crates/yuck/src/parser/lexer.rs +expression: "r#\"{ \" \" + music}\"#" + +--- +{ " " + music} diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__lexer__test__escaped_quote.snap b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__test__escaped_quote.snap new file mode 100644 index 0000000..9ab4524 --- /dev/null +++ b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__test__escaped_quote.snap @@ -0,0 +1,6 @@ +--- +source: crates/yuck/src/parser/lexer.rs +expression: "r#\"\"< \\\" >\"\"#" + +--- +"< \" >" diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__lexer__test__escaped_strings.snap b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__test__escaped_strings.snap new file mode 100644 index 0000000..eb6ab16 --- /dev/null +++ b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__test__escaped_strings.snap @@ -0,0 +1,6 @@ +--- +source: crates/yuck/src/parser/lexer.rs +expression: "r#\"{ bla \"} \\\" }\" \" \\\" \"}\"#" + +--- +{ bla "} \" }" " \" "} diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__lexer__test__quotes_in_quotes.snap b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__test__quotes_in_quotes.snap new file mode 100644 index 0000000..26ead15 --- /dev/null +++ b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__test__quotes_in_quotes.snap @@ -0,0 +1,6 @@ +--- +source: crates/yuck/src/parser/lexer.rs +expression: "r#\"{ \" } ' }\" }\"#" + +--- +{ " } ' }" } diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-2.snap b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-2.snap index 52688dc..846e42a 100644 --- a/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-2.snap +++ b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-2.snap @@ -6,9 +6,47 @@ expression: "Lexer::new(0, r#\"{ bla \"} \\\" }\" \" \\\" \"}\"#.to_string()).co [ Ok( ( - 0, + 2, SimplExpr( - "{ bla \"} \\\" }\" \" \\\" \"}", + [ + ( + 2, + Ident( + "bla", + ), + 5, + ), + ( + 6, + StringLit( + [ + ( + 6, + Literal( + "} \" }", + ), + 14, + ), + ], + ), + 14, + ), + ( + 15, + StringLit( + [ + ( + 15, + Literal( + " \" ", + ), + 21, + ), + ], + ), + 21, + ), + ], ), 21, ), diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-3.snap b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-3.snap index 18607c4..cda3a96 100644 --- a/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-3.snap +++ b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-3.snap @@ -7,8 +7,24 @@ expression: "Lexer::new(0, r#\"\"< \\\" >\"\"#.to_string()).collect_vec()" Ok( ( 0, - StrLit( - "\"< \" >\"", + SimplExpr( + [ + ( + 0, + StringLit( + [ + ( + 0, + Literal( + "< \" >", + ), + 8, + ), + ], + ), + 8, + ), + ], ), 8, ), diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-4.snap b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-4.snap index 558fd17..462cc2a 100644 --- a/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-4.snap +++ b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-4.snap @@ -6,9 +6,37 @@ expression: "Lexer::new(0, r#\"{ \" \" + music}\"#.to_string()).collect_vec [ Ok( ( - 0, + 2, SimplExpr( - "{ \"\u{f001} \" + music}", + [ + ( + 2, + StringLit( + [ + ( + 2, + Literal( + "\u{f001} ", + ), + 10, + ), + ], + ), + 10, + ), + ( + 11, + Plus, + 12, + ), + ( + 13, + Ident( + "music", + ), + 18, + ), + ], ), 18, ), diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-5.snap b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-5.snap deleted file mode 100644 index 0e4ea1b..0000000 --- a/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-5.snap +++ /dev/null @@ -1,16 +0,0 @@ ---- -source: crates/yuck/src/parser/lexer.rs -expression: "Lexer::new(0, r#\"{ \" } ' }\" }\"#.to_string()).collect_vec()" - ---- -[ - Ok( - ( - 0, - SimplExpr( - "{ \" } ' }\" }", - ), - 11, - ), - ), -] diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer.snap b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer.snap index 695431e..813a740 100644 --- a/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer.snap +++ b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer.snap @@ -41,8 +41,24 @@ expression: "Lexer::new(0, r#\"(foo + - \"text\" )\"#.to_string()).collect_vec() Ok( ( 9, - StrLit( - "\"text\"", + SimplExpr( + [ + ( + 9, + StringLit( + [ + ( + 9, + Literal( + "text", + ), + 15, + ), + ], + ), + 15, + ), + ], ), 15, ), diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__test-11.snap b/crates/yuck/src/parser/snapshots/yuck__parser__test-11.snap index 4f80a03..f260cbc 100644 --- a/crates/yuck/src/parser/snapshots/yuck__parser__test-11.snap +++ b/crates/yuck/src/parser/snapshots/yuck__parser__test-11.snap @@ -4,5 +4,5 @@ expression: "p.parse(0, Lexer::new(0, r#\"(test \"hi\")\"#.to_string()))" --- Ok( - (test "hi"), + (test {"hi"}), ) diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__test-12.snap b/crates/yuck/src/parser/snapshots/yuck__parser__test-12.snap index 3fcc8ea..b2bddd7 100644 --- a/crates/yuck/src/parser/snapshots/yuck__parser__test-12.snap +++ b/crates/yuck/src/parser/snapshots/yuck__parser__test-12.snap @@ -4,5 +4,5 @@ expression: "p.parse(0, Lexer::new(0, r#\"(test \"h\\\"i\")\"#.to_string()))" --- Ok( - (test "h"i"), + (test {"h"i"}), ) diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__test-13.snap b/crates/yuck/src/parser/snapshots/yuck__parser__test-13.snap index 8d4d533..c776d11 100644 --- a/crates/yuck/src/parser/snapshots/yuck__parser__test-13.snap +++ b/crates/yuck/src/parser/snapshots/yuck__parser__test-13.snap @@ -4,5 +4,5 @@ expression: "p.parse(0, Lexer::new(0, r#\"(test \" hi \")\"#.to_string()))" --- Ok( - (test " hi "), + (test {" hi "}), ) diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__test-17.snap b/crates/yuck/src/parser/snapshots/yuck__parser__test-17.snap index a2e848a..098049f 100644 --- a/crates/yuck/src/parser/snapshots/yuck__parser__test-17.snap +++ b/crates/yuck/src/parser/snapshots/yuck__parser__test-17.snap @@ -4,5 +4,5 @@ expression: "p.parse(0, Lexer::new(0, \"\\\"h\\\\\\\"i\\\"\".to_string()))" --- Ok( - "h"i", + {"h"i"}, ) diff --git a/examples/eww-bar/eww.yuck b/examples/eww-bar/eww.yuck index 888d8ba..11a3831 100644 --- a/examples/eww-bar/eww.yuck +++ b/examples/eww-bar/eww.yuck @@ -3,6 +3,7 @@ (workspaces) (music) (sidestuff))) + (defwidget sidestuff [] (box :class "sidestuff" :orientation "h" :space-evenly false :halign "end" (slider-vol) @@ -23,12 +24,12 @@ (defwidget music [] (box :class "music" :orientation "h" :space-evenly false :halign "center" - { ' ' + music})) + ' ${music}')) (defwidget slider-vol [] (box :class "slider-vol" :orientation "h" :space-evenly "false" (box :class "label-vol" "" - (scale :min 0 :max 101 :value volume :onchange "amixer -D pulse sset Master {}%")))) + (scale :min 0 :max 101 :value volume :onchange "amixer -D pulse sset Master \{}%")))) (defwidget slider-ram [] (box :orientation "h" :class "slider-ram" :space-evenly false @@ -38,14 +39,10 @@ (defwidget time [] (box :class "time" - {"{hour}:{min} {month + "the month" * 2} {number_day}, {year_full}"})) - ;{hour + ":" + min + " " + month + " " + number_day + ", " + year_full})) + "${hour}:${min} ${month} ${number_day}, ${year_full}")) -(defvar music "bruh") - -;(defpoll music :interval "5s" "playerctl metadata --format '{{ artist }} - {{ title }}' || true") -(defvar volume "20") -;(defpoll volume :interval "16s" "scripts/getvol") +(defpoll music :interval "5s" "playerctl metadata --format '{{ artist }} - {{ title }}' || true") +(defpoll volume :interval "1s" "scripts/getvol") (defpoll number_day :interval "5h" "date '+%d'") (defpoll month :interval "10h" "date '+%b'") @@ -61,5 +58,3 @@ :geometry (geometry :x "0%" :y "0%" :width "100%" :height "4%") :reserve (struts :side "top" :distance "4%") (bar)) - -; asdf diff --git a/examples/eww-bar/scripts/getvol b/examples/eww-bar/scripts/getvol index 3c31607..6a95077 100755 --- a/examples/eww-bar/scripts/getvol +++ b/examples/eww-bar/scripts/getvol @@ -1,2 +1,2 @@ #!/bin/sh -amixer -D pulse sget Master | grep 'Left:' | awk -F'[][]' '{ print $2 }' | tr -d '%' +amixer -D pulse sget Master | grep 'Left:' | awk -F'[][]' '{ print $2 }' | tr -d '%' | head -1 From dd482987cd6d871a3c206c523444ff6b9cb3cc88 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Sat, 31 Jul 2021 13:59:02 +0200 Subject: [PATCH 093/137] Clean up ast structure --- crates/yuck/src/parser/ast.rs | 9 ++------- crates/yuck/src/parser/ast_iterator.rs | 4 +--- crates/yuck/src/parser/from_ast.rs | 3 +-- crates/yuck/src/parser/parser.lalrpop | 2 +- .../yuck/src/parser/snapshots/yuck__parser__test-11.snap | 2 +- .../yuck/src/parser/snapshots/yuck__parser__test-12.snap | 2 +- .../yuck/src/parser/snapshots/yuck__parser__test-13.snap | 2 +- .../yuck/src/parser/snapshots/yuck__parser__test-17.snap | 2 +- 8 files changed, 9 insertions(+), 17 deletions(-) diff --git a/crates/yuck/src/parser/ast.rs b/crates/yuck/src/parser/ast.rs index a1efb33..41ca09c 100644 --- a/crates/yuck/src/parser/ast.rs +++ b/crates/yuck/src/parser/ast.rs @@ -17,6 +17,7 @@ pub enum AstType { Array, Keyword, Symbol, + // TODO this does no longer correspond to an actual literal ast type as that's replaced with SimplExpr Literal, SimplExpr, Comment, @@ -36,7 +37,6 @@ pub enum Ast { Array(Span, Vec), Keyword(Span, String), Symbol(Span, String), - Literal(Span, DynVal), SimplExpr(Span, SimplExpr), Comment(Span), } @@ -60,8 +60,6 @@ macro_rules! as_func { } impl Ast { - as_func!(AstType::Literal, as_literal as_literal_ref = Ast::Literal(_, x) => x); - as_func!(AstType::Symbol, as_symbol as_symbol_ref = Ast::Symbol(_, x) => x); as_func!(AstType::Keyword, as_keyword as_keyword_ref = Ast::Keyword(_, x) => x); @@ -74,7 +72,6 @@ impl Ast { Ast::Array(..) => AstType::Array, Ast::Keyword(..) => AstType::Keyword, Ast::Symbol(..) => AstType::Symbol, - Ast::Literal(..) => AstType::Literal, Ast::SimplExpr(..) => AstType::SimplExpr, Ast::Comment(_) => AstType::Comment, } @@ -86,7 +83,6 @@ impl Ast { Ast::Array(span, _) => *span, Ast::Keyword(span, _) => *span, Ast::Symbol(span, _) => *span, - Ast::Literal(span, _) => *span, Ast::SimplExpr(span, _) => *span, Ast::Comment(span) => *span, } @@ -97,7 +93,6 @@ impl Ast { // TODO do I do this? // Ast::Array(span, elements) => todo!() Ast::Symbol(span, x) => Ok(SimplExpr::VarRef(span, VarName(x))), - Ast::Literal(span, x) => Ok(SimplExpr::Literal(span, x)), Ast::SimplExpr(span, x) => Ok(x), _ => Err(AstError::WrongExprType(self.span(), AstType::IntoPrimitive, self.expr_type())), } @@ -118,7 +113,7 @@ impl std::fmt::Display for Ast { Array(_, x) => write!(f, "({})", x.iter().map(|e| format!("{}", e)).join(" ")), Keyword(_, x) => write!(f, ":{}", x), Symbol(_, x) => write!(f, "{}", x), - Literal(_, x) => write!(f, "\"{}\"", x), + SimplExpr(_, simplexpr::SimplExpr::Literal(_, value)) => write!(f, "\"{}\"", value), SimplExpr(_, x) => write!(f, "{{{}}}", x), Comment(_) => write!(f, ""), } diff --git a/crates/yuck/src/parser/ast_iterator.rs b/crates/yuck/src/parser/ast_iterator.rs index 63c61de..26d2a4f 100644 --- a/crates/yuck/src/parser/ast_iterator.rs +++ b/crates/yuck/src/parser/ast_iterator.rs @@ -39,19 +39,17 @@ macro_rules! return_or_put_back { impl> AstIterator { return_or_put_back!(expect_symbol, AstType::Symbol, (Span, String) = Ast::Symbol(span, x) => (span, x)); - // return_or_put_back!(expect_literal, AstType::Literal, (Span, DynVal) = Ast::Literal(span, x) => (span, x)); - return_or_put_back!(expect_list, AstType::List, (Span, Vec) = Ast::List(span, x) => (span, x)); return_or_put_back!(expect_array, AstType::Array, (Span, Vec) = Ast::Array(span, x) => (span, x)); pub fn expect_literal(&mut self) -> AstResult<(Span, DynVal)> { + // TODO add some others match self.expect_any()? { // Ast::List(_, _) => todo!(), // Ast::Array(_, _) => todo!(), // Ast::Keyword(_, _) => todo!(), // Ast::Symbol(_, _) => todo!(), - // Ast::Literal(_, _) => todo!(), Ast::SimplExpr(span, expr) => Ok((span, dbg!(expr.eval_no_vars()).map_err(|e| AstError::SimplExpr(e.into()))?)), other => { let span = other.span(); diff --git a/crates/yuck/src/parser/from_ast.rs b/crates/yuck/src/parser/from_ast.rs index acff1ef..3aca5ea 100644 --- a/crates/yuck/src/parser/from_ast.rs +++ b/crates/yuck/src/parser/from_ast.rs @@ -24,7 +24,7 @@ impl FromAst for Ast { impl FromAst for String { fn from_ast(e: Ast) -> AstResult { - Ok(e.as_literal()?.as_string().unwrap()) + Ok(e.as_simplexpr()?.eval_no_vars().map_err(simplexpr::error::Error::Eval)?.to_string()) } } @@ -51,7 +51,6 @@ impl FromAst for SimplExpr { fn from_ast(e: Ast) -> AstResult { match e { Ast::Symbol(span, x) => Ok(SimplExpr::VarRef(span.into(), VarName(x))), - Ast::Literal(span, x) => Ok(SimplExpr::Literal(span.into(), x)), Ast::SimplExpr(span, x) => Ok(x), _ => Err(AstError::NotAValue(e.span(), e.expr_type())), } diff --git a/crates/yuck/src/parser/parser.lalrpop b/crates/yuck/src/parser/parser.lalrpop index 9f65b8f..34740b4 100644 --- a/crates/yuck/src/parser/parser.lalrpop +++ b/crates/yuck/src/parser/parser.lalrpop @@ -36,7 +36,7 @@ pub Ast: Ast = { => Ast::SimplExpr(Span(l, r, file_id), expr), => x, => x, - => Ast::Literal(Span(l, r, file_id), x.into()), + => Ast::SimplExpr(Span(l, r, file_id), SimplExpr::literal(Span(l, r, file_id), x.into())), "comment" => Ast::Comment(Span(l, r, file_id)), }; diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__test-11.snap b/crates/yuck/src/parser/snapshots/yuck__parser__test-11.snap index f260cbc..4f80a03 100644 --- a/crates/yuck/src/parser/snapshots/yuck__parser__test-11.snap +++ b/crates/yuck/src/parser/snapshots/yuck__parser__test-11.snap @@ -4,5 +4,5 @@ expression: "p.parse(0, Lexer::new(0, r#\"(test \"hi\")\"#.to_string()))" --- Ok( - (test {"hi"}), + (test "hi"), ) diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__test-12.snap b/crates/yuck/src/parser/snapshots/yuck__parser__test-12.snap index b2bddd7..3fcc8ea 100644 --- a/crates/yuck/src/parser/snapshots/yuck__parser__test-12.snap +++ b/crates/yuck/src/parser/snapshots/yuck__parser__test-12.snap @@ -4,5 +4,5 @@ expression: "p.parse(0, Lexer::new(0, r#\"(test \"h\\\"i\")\"#.to_string()))" --- Ok( - (test {"h"i"}), + (test "h"i"), ) diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__test-13.snap b/crates/yuck/src/parser/snapshots/yuck__parser__test-13.snap index c776d11..8d4d533 100644 --- a/crates/yuck/src/parser/snapshots/yuck__parser__test-13.snap +++ b/crates/yuck/src/parser/snapshots/yuck__parser__test-13.snap @@ -4,5 +4,5 @@ expression: "p.parse(0, Lexer::new(0, r#\"(test \" hi \")\"#.to_string()))" --- Ok( - (test {" hi "}), + (test " hi "), ) diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__test-17.snap b/crates/yuck/src/parser/snapshots/yuck__parser__test-17.snap index 098049f..a2e848a 100644 --- a/crates/yuck/src/parser/snapshots/yuck__parser__test-17.snap +++ b/crates/yuck/src/parser/snapshots/yuck__parser__test-17.snap @@ -4,5 +4,5 @@ expression: "p.parse(0, Lexer::new(0, \"\\\"h\\\\\\\"i\\\"\".to_string()))" --- Ok( - {"h"i"}, + "h"i", ) From ebf9c7e7ef26becd250e58f38a3b6592f3aaf09a Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Sat, 31 Jul 2021 16:01:20 +0200 Subject: [PATCH 094/137] Unify error messages and remove unused code --- crates/simplexpr/src/eval.rs | 20 ++------------------ crates/simplexpr/src/lib.rs | 1 + crates/yuck/src/format_diagnostic.rs | 5 ++--- 3 files changed, 5 insertions(+), 21 deletions(-) diff --git a/crates/simplexpr/src/eval.rs b/crates/simplexpr/src/eval.rs index e703797..159d35b 100644 --- a/crates/simplexpr/src/eval.rs +++ b/crates/simplexpr/src/eval.rs @@ -15,10 +15,6 @@ pub enum EvalError { #[error("Invalid regex: {0}")] InvalidRegex(#[from] regex::Error), - // TODO unresolved and unknown are the same for the user,.... - #[error("got unresolved variable `{0}`")] - UnresolvedVariable(VarName), - #[error("Unknown variable {0}")] UnknownVariable(VarName), @@ -62,18 +58,6 @@ impl Spanned for EvalError { } 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), - } - } - /// map over all of the variable references, replacing them with whatever expression the provided function returns. /// Returns [Err] when the provided function fails with an [Err] pub fn try_map_var_refs Result + Copy>(self, f: F) -> Result { @@ -154,7 +138,7 @@ impl SimplExpr { match self.eval(&HashMap::new()) { Ok(x) => Ok(x), Err(x) => Err(x.map_in_span(|err| match err { - EvalError::UnknownVariable(name) | EvalError::UnresolvedVariable(name) => EvalError::NoVariablesAllowed(name), + EvalError::UnknownVariable(name) => EvalError::NoVariablesAllowed(name), other => other, })), } @@ -165,7 +149,7 @@ impl SimplExpr { 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.clone()).at(*span))?.at(*span)) + Ok(values.get(name).cloned().ok_or_else(|| EvalError::UnknownVariable(name.clone()).at(*span))?.at(*span)) } SimplExpr::BinOp(_, a, op, b) => { let a = a.eval(values)?; diff --git a/crates/simplexpr/src/lib.rs b/crates/simplexpr/src/lib.rs index 23de86b..6aee794 100644 --- a/crates/simplexpr/src/lib.rs +++ b/crates/simplexpr/src/lib.rs @@ -1,4 +1,5 @@ #![feature(box_patterns)] +#![feature(format_args_capture)] #![feature(pattern)] #![feature(box_syntax)] #![feature(try_blocks)] diff --git a/crates/yuck/src/format_diagnostic.rs b/crates/yuck/src/format_diagnostic.rs index 3653082..e7dbee0 100644 --- a/crates/yuck/src/format_diagnostic.rs +++ b/crates/yuck/src/format_diagnostic.rs @@ -186,7 +186,6 @@ fn lalrpop_error_to_diagnostic( impl ToDiagnostic for simplexpr::error::Error { fn to_diagnostic(&self) -> Diagnostic { use simplexpr::error::Error::*; - dbg!(&self); match self { ParseError { source, file_id } => lalrpop_error_to_diagnostic(source, *file_id), ConversionError(error) => error.to_diagnostic(), @@ -210,7 +209,7 @@ impl ToDiagnostic for simplexpr::eval::EvalError { NoVariablesAllowed(name) => gen_diagnostic!(self), // TODO the note here is confusing when it's an unknown variable being used _within_ a string literal / simplexpr // it only really makes sense on top-level symbols - UnresolvedVariable(name) | UnknownVariable(name) => gen_diagnostic! { + UnknownVariable(name) => gen_diagnostic! { msg = self, note = format!("If you meant to use the literal value \"{}\", surround the value in quotes", name) }, @@ -226,7 +225,7 @@ impl ToDiagnostic for dynval::ConversionError { msg = self, label = self.value.span() => format!("`{}` is not of type `{}`", self.value, self.target_type), }; - diag.with_notes(self.source.as_ref().map(|x| vec![format!("{}", x)]).unwrap_or_default()) + diag.with_notes(self.source.as_ref().map(|x| vec![x.to_string()]).unwrap_or_default()) } } From a464a849f12e4af93c55cf3206ea0ab633e73c4b Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Sat, 31 Jul 2021 17:45:59 +0200 Subject: [PATCH 095/137] Add --toggle to open window --- crates/eww/src/app.rs | 9 +++++++-- crates/eww/src/opts.rs | 7 ++++++- examples/eww-bar/eww.yuck | 7 ++++++- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/crates/eww/src/app.rs b/crates/eww/src/app.rs index 058a2a1..d06482c 100644 --- a/crates/eww/src/app.rs +++ b/crates/eww/src/app.rs @@ -51,6 +51,7 @@ pub enum DaemonCommand { size: Option, anchor: Option, monitor: Option, + should_toggle: bool, sender: DaemonResponseSender, }, CloseWindow { @@ -154,8 +155,12 @@ impl App { let result = windows.iter().try_for_each(|w| self.open_window(w, None, None, None, None)); respond_with_error(sender, result)?; } - DaemonCommand::OpenWindow { window_name, pos, size, anchor, monitor, sender } => { - let result = self.open_window(&window_name, pos, size, monitor, anchor); + DaemonCommand::OpenWindow { window_name, pos, size, anchor, monitor, should_toggle, sender } => { + let result = if should_toggle && self.open_windows.contains_key(&window_name) { + self.close_window(&window_name) + } else { + self.open_window(&window_name, pos, size, monitor, anchor) + }; respond_with_error(sender, result)?; } DaemonCommand::CloseWindow { window_name, sender } => { diff --git a/crates/eww/src/opts.rs b/crates/eww/src/opts.rs index 21b7517..802c2d3 100644 --- a/crates/eww/src/opts.rs +++ b/crates/eww/src/opts.rs @@ -89,6 +89,10 @@ pub enum ActionWithServer { /// Sidepoint of the window, formatted like "top right" #[structopt(short, long)] anchor: Option, + + /// If the window is already open, close it instead + #[structopt(long = "toggle")] + should_toggle: bool, }, /// Open multiple windows at once. @@ -168,13 +172,14 @@ impl ActionWithServer { ActionWithServer::OpenMany { windows } => { return with_response_channel(|sender| app::DaemonCommand::OpenMany { windows, sender }); } - ActionWithServer::OpenWindow { window_name, pos, size, monitor, anchor } => { + ActionWithServer::OpenWindow { window_name, pos, size, monitor, anchor, should_toggle } => { return with_response_channel(|sender| app::DaemonCommand::OpenWindow { window_name, pos, size, anchor, monitor, + should_toggle, sender, }) } diff --git a/examples/eww-bar/eww.yuck b/examples/eww-bar/eww.yuck index 11a3831..ce66e45 100644 --- a/examples/eww-bar/eww.yuck +++ b/examples/eww-bar/eww.yuck @@ -11,7 +11,10 @@ (time))) (defwidget workspaces [] - (box :class "workspaces" :orientation "h" :space-evenly true :halign "start" + (box :class "workspaces" + :orientation "h" + :space-evenly true + :halign "start" (button :onclick "wmctrl -s 0" 1) (button :onclick "wmctrl -s 1" 2) (button :onclick "wmctrl -s 2" 3) @@ -37,10 +40,12 @@ (scale :min 0 :max 101 :active false :value EWW_RAM)))) + (defwidget time [] (box :class "time" "${hour}:${min} ${month} ${number_day}, ${year_full}")) + (defpoll music :interval "5s" "playerctl metadata --format '{{ artist }} - {{ title }}' || true") (defpoll volume :interval "1s" "scripts/getvol") From afd0a47bf2e999c465a72fe9a42ef1b09f24109d Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Sat, 31 Jul 2021 20:01:42 +0200 Subject: [PATCH 096/137] Remove redundant information --- crates/simplexpr/src/ast.rs | 16 ++++++---------- crates/simplexpr/src/eval.rs | 6 +++--- crates/simplexpr/src/parser/lalrpop_helpers.rs | 4 ++-- crates/simplexpr/src/simplexpr_parser.lalrpop | 2 +- .../snapshots/yuck__config__test__config.snap | 4 ++-- crates/yuck/src/parser/ast.rs | 2 +- crates/yuck/src/parser/ast_iterator.rs | 5 +---- 7 files changed, 16 insertions(+), 23 deletions(-) diff --git a/crates/simplexpr/src/ast.rs b/crates/simplexpr/src/ast.rs index 4139e75..b7aa2da 100644 --- a/crates/simplexpr/src/ast.rs +++ b/crates/simplexpr/src/ast.rs @@ -31,11 +31,7 @@ pub enum UnaryOp { #[derive(Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum SimplExpr { - // TODO figure out if that span value here is even necessary,.. - // DynVal has span information. However, keeping the span here and the span of the dynval separate - // would allow to separate the span of where a dynval was defined and the - // span of the use-site when a literal is used to replace a varref in the evaluation process. - Literal(Span, DynVal), + Literal(DynVal), VarRef(Span, VarName), BinOp(Span, Box, BinOp, Box), UnaryOp(Span, UnaryOp, Box), @@ -47,8 +43,8 @@ pub enum SimplExpr { impl std::fmt::Display for SimplExpr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { + SimplExpr::Literal(x) => write!(f, "\"{}\"", x), 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, "({} ? {} : {})", a, b, c), @@ -61,23 +57,23 @@ impl std::fmt::Display for SimplExpr { } impl SimplExpr { pub fn literal(span: Span, s: String) -> Self { - Self::Literal(span, DynVal(s, span)) + Self::Literal(DynVal(s, span)) } /// Construct a synthetic simplexpr from a literal string, without adding any relevant span information (uses [DUMMY_SPAN]) pub fn synth_string(s: String) -> Self { - Self::Literal(Span::DUMMY, DynVal(s, Span::DUMMY)) + Self::Literal(DynVal(s, Span::DUMMY)) } /// Construct a synthetic simplexpr from a literal dynval, without adding any relevant span information (uses [DUMMY_SPAN]) pub fn synth_literal>(s: T) -> Self { - Self::Literal(Span::DUMMY, s.into()) + Self::Literal(s.into()) } } impl Spanned for SimplExpr { fn span(&self) -> Span { match self { - SimplExpr::Literal(span, _) => *span, + SimplExpr::Literal(x) => x.span(), SimplExpr::VarRef(span, _) => *span, SimplExpr::BinOp(span, ..) => *span, SimplExpr::UnaryOp(span, ..) => *span, diff --git a/crates/simplexpr/src/eval.rs b/crates/simplexpr/src/eval.rs index 159d35b..cf508ca 100644 --- a/crates/simplexpr/src/eval.rs +++ b/crates/simplexpr/src/eval.rs @@ -92,7 +92,7 @@ impl SimplExpr { pub fn resolve_refs(self, variables: &HashMap) -> Result { use SimplExpr::*; match self { - Literal(span, x) => Ok(Literal(span, x)), + Literal(x) => Ok(Literal(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) => { @@ -107,7 +107,7 @@ impl SimplExpr { args.into_iter().map(|a| a.resolve_refs(variables)).collect::>()?, )), VarRef(span, ref name) => match variables.get(name) { - Some(value) => Ok(Literal(span, value.clone())), + Some(value) => Ok(Literal(value.clone())), None => Err(EvalError::UnknownVariable(name.clone()).at(span)), }, } @@ -147,7 +147,7 @@ impl SimplExpr { pub fn eval(&self, values: &HashMap) -> Result { let span = self.span(); let value = match self { - SimplExpr::Literal(_, x) => Ok(x.clone()), + SimplExpr::Literal(x) => Ok(x.clone()), SimplExpr::VarRef(span, ref name) => { Ok(values.get(name).cloned().ok_or_else(|| EvalError::UnknownVariable(name.clone()).at(*span))?.at(*span)) } diff --git a/crates/simplexpr/src/parser/lalrpop_helpers.rs b/crates/simplexpr/src/parser/lalrpop_helpers.rs index 7d22f13..19e2fa5 100644 --- a/crates/simplexpr/src/parser/lalrpop_helpers.rs +++ b/crates/simplexpr/src/parser/lalrpop_helpers.rs @@ -21,7 +21,7 @@ pub fn parse_stringlit( let span = Span(lo, hi, file_id); match segment { StrLitSegment::Literal(lit) if lit.is_empty() => None, - StrLitSegment::Literal(lit) => Some(Ok(SimplExpr::Literal(span, DynVal(lit, span)))), + StrLitSegment::Literal(lit) => Some(Ok(SimplExpr::Literal(DynVal(lit, span)))), StrLitSegment::Interp(toks) => { let token_stream = toks.into_iter().map(|x| Ok(x)); Some(parser.parse(file_id, token_stream)) @@ -32,5 +32,5 @@ pub fn parse_stringlit( Some(ast) => Some(SimplExpr::BinOp(span, Box::new(ast), BinOp::Plus, Box::new(cur))), None => Some(cur), }) - .map(|ast| ast.unwrap_or_else(|| SimplExpr::Literal(span, DynVal(String::new(), span)))) + .map(|ast| ast.unwrap_or_else(|| SimplExpr::Literal(DynVal(String::new(), span)))) } diff --git a/crates/simplexpr/src/simplexpr_parser.lalrpop b/crates/simplexpr/src/simplexpr_parser.lalrpop index a72afd6..10028b7 100644 --- a/crates/simplexpr/src/simplexpr_parser.lalrpop +++ b/crates/simplexpr/src/simplexpr_parser.lalrpop @@ -76,7 +76,7 @@ pub Expr: SimplExpr = { "[" "]" => JsonAccess(Span(l, r, fid), b(value), b(index)), "." => { - JsonAccess(Span(l, r, fid), b(value), b(Literal(Span(lit_l, r, fid), index.into()))) + JsonAccess(Span(l, r, fid), b(value), b(Literal(index.into()))) }, #[precedence(level="2")] #[assoc(side="right")] diff --git a/crates/yuck/src/config/snapshots/yuck__config__test__config.snap b/crates/yuck/src/config/snapshots/yuck__config__test__config.snap index ef3554d..d4a0ba3 100644 --- a/crates/yuck/src/config/snapshots/yuck__config__test__config.snap +++ b/crates/yuck/src/config/snapshots/yuck__config__test__config.snap @@ -18,7 +18,7 @@ Config( attrs: { AttrName("arg"): AttrEntry( key_span: Span(52, 56, 0), - value: SimplExpr(Span(57, 61, 0), Literal(Span(57, 61, 0), DynVal("hi", Span(57, 61, 0)))), + value: SimplExpr(Span(57, 61, 0), Literal(DynVal("hi", Span(57, 61, 0)))), ), }, ), @@ -56,7 +56,7 @@ Config( attrs: { AttrName("arg"): AttrEntry( key_span: Span(468, 472, 0), - value: SimplExpr(Span(473, 478, 0), Literal(Span(473, 478, 0), DynVal("bla", Span(473, 478, 0)))), + value: SimplExpr(Span(473, 478, 0), Literal(DynVal("bla", Span(473, 478, 0)))), ), }, ), diff --git a/crates/yuck/src/parser/ast.rs b/crates/yuck/src/parser/ast.rs index 41ca09c..2361ea5 100644 --- a/crates/yuck/src/parser/ast.rs +++ b/crates/yuck/src/parser/ast.rs @@ -113,7 +113,7 @@ impl std::fmt::Display for Ast { Array(_, x) => write!(f, "({})", x.iter().map(|e| format!("{}", e)).join(" ")), Keyword(_, x) => write!(f, ":{}", x), Symbol(_, x) => write!(f, "{}", x), - SimplExpr(_, simplexpr::SimplExpr::Literal(_, value)) => write!(f, "\"{}\"", value), + SimplExpr(_, simplexpr::SimplExpr::Literal(value)) => write!(f, "\"{}\"", value), SimplExpr(_, x) => write!(f, "{{{}}}", x), Comment(_) => write!(f, ""), } diff --git a/crates/yuck/src/parser/ast_iterator.rs b/crates/yuck/src/parser/ast_iterator.rs index 26d2a4f..c2b5663 100644 --- a/crates/yuck/src/parser/ast_iterator.rs +++ b/crates/yuck/src/parser/ast_iterator.rs @@ -46,11 +46,8 @@ impl> AstIterator { pub fn expect_literal(&mut self) -> AstResult<(Span, DynVal)> { // TODO add some others match self.expect_any()? { - // Ast::List(_, _) => todo!(), // Ast::Array(_, _) => todo!(), - // Ast::Keyword(_, _) => todo!(), - // Ast::Symbol(_, _) => todo!(), - Ast::SimplExpr(span, expr) => Ok((span, dbg!(expr.eval_no_vars()).map_err(|e| AstError::SimplExpr(e.into()))?)), + Ast::SimplExpr(span, expr) => Ok((span, expr.eval_no_vars().map_err(|e| AstError::SimplExpr(e.into()))?)), other => { let span = other.span(); let actual_type = other.expr_type(); From e24cc0ac1613fd82e35b2abc8cc8ff2dd54a1071 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Sun, 1 Aug 2021 15:52:44 +0200 Subject: [PATCH 097/137] Fix missing attribute error message and add more span data --- crates/eww/src/config/inbuilt.rs | 1 + crates/yuck/src/config/attributes.rs | 2 +- .../yuck/src/config/script_var_definition.rs | 19 ++++++++++++++----- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/crates/eww/src/config/inbuilt.rs b/crates/eww/src/config/inbuilt.rs index 9e36276..54e5949 100644 --- a/crates/eww/src/config/inbuilt.rs +++ b/crates/eww/src/config/inbuilt.rs @@ -14,6 +14,7 @@ macro_rules! builtin_vars { name: VarName::from($name), command: VarSource::Function($fun), interval: $interval, + name_span: eww_shared_util::span::Span::DUMMY, }) ),* } diff --git a/crates/yuck/src/config/attributes.rs b/crates/yuck/src/config/attributes.rs index 5d6e370..eaa7d6a 100644 --- a/crates/yuck/src/config/attributes.rs +++ b/crates/yuck/src/config/attributes.rs @@ -17,7 +17,7 @@ use eww_shared_util::{AttrName, Span, Spanned, VarName}; #[derive(Debug, thiserror::Error)] pub enum AttrError { - #[error("Missing required attribute {0}")] + #[error("Missing required attribute {1}")] MissingRequiredAttr(Span, AttrName), #[error("{1}")] diff --git a/crates/yuck/src/config/script_var_definition.rs b/crates/yuck/src/config/script_var_definition.rs index 176c50e..a792fc7 100644 --- a/crates/yuck/src/config/script_var_definition.rs +++ b/crates/yuck/src/config/script_var_definition.rs @@ -10,7 +10,7 @@ use crate::{ from_ast::{FromAst, FromAstElementContent}, }, }; -use eww_shared_util::{AttrName, Span, VarName}; +use eww_shared_util::{AttrName, Span, Spanned, VarName}; #[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)] pub enum ScriptVarDefinition { @@ -19,6 +19,13 @@ pub enum ScriptVarDefinition { } impl ScriptVarDefinition { + pub fn name_span(&self) -> Span { + match self { + ScriptVarDefinition::Poll(x) => x.name_span, + ScriptVarDefinition::Listen(x) => x.name_span, + } + } + pub fn name(&self) -> &VarName { match self { ScriptVarDefinition::Poll(x) => &x.name, @@ -50,6 +57,7 @@ pub struct PollScriptVar { pub name: VarName, pub command: VarSource, pub interval: std::time::Duration, + pub name_span: Span, } impl FromAstElementContent for PollScriptVar { @@ -58,12 +66,12 @@ impl FromAstElementContent for PollScriptVar { } fn from_tail>(span: Span, mut iter: AstIterator) -> AstResult { - let (_, name) = iter.expect_symbol()?; + let (name_span, name) = iter.expect_symbol()?; let mut attrs = iter.expect_key_values()?; let interval = attrs.primitive_required::("interval")?.as_duration()?; let (script_span, script) = iter.expect_literal()?; iter.expect_done()?; - Ok(Self { name: VarName(name), command: VarSource::Shell(script_span, script.to_string()), interval }) + Ok(Self { name_span, name: VarName(name), command: VarSource::Shell(script_span, script.to_string()), interval }) } } @@ -72,6 +80,7 @@ pub struct ListenScriptVar { pub name: VarName, pub command: String, pub command_span: Span, + pub name_span: Span, } impl FromAstElementContent for ListenScriptVar { fn get_element_name() -> &'static str { @@ -79,9 +88,9 @@ impl FromAstElementContent for ListenScriptVar { } fn from_tail>(span: Span, mut iter: AstIterator) -> AstResult { - let (_, name) = iter.expect_symbol()?; + let (name_span, name) = iter.expect_symbol()?; let (command_span, script) = iter.expect_literal()?; iter.expect_done()?; - Ok(Self { name: VarName(name), command: script.to_string(), command_span }) + Ok(Self { name_span, name: VarName(name), command: script.to_string(), command_span }) } } From 27ce9e798a87ea8950628760a82dd41ccf17cf1b Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Sun, 1 Aug 2021 17:36:49 +0200 Subject: [PATCH 098/137] Fix error messages for script-var definitions --- crates/eww/src/widgets/widget_definitions.rs | 2 +- .../yuck/src/config/script_var_definition.rs | 22 ++++++++++++------- .../snapshots/yuck__config__test__config.snap | 5 +++-- crates/yuck/src/config/var_definition.rs | 18 +++++++-------- crates/yuck/src/error.rs | 9 ++++++-- crates/yuck/src/parser/ast_iterator.rs | 2 +- 6 files changed, 35 insertions(+), 23 deletions(-) diff --git a/crates/eww/src/widgets/widget_definitions.rs b/crates/eww/src/widgets/widget_definitions.rs index 4ea9b03..61e4744 100644 --- a/crates/eww/src/widgets/widget_definitions.rs +++ b/crates/eww/src/widgets/widget_definitions.rs @@ -8,7 +8,7 @@ use gtk::{self, prelude::*, ImageExt}; use std::{cell::RefCell, collections::HashMap, rc::Rc, time::Duration}; use yuck::{ config::validate::ValidationError, - error::{AstError, AstResult, ResultExt}, + error::{AstError, AstResult, AstResultExt}, parser::from_ast::FromAst, }; diff --git a/crates/yuck/src/config/script_var_definition.rs b/crates/yuck/src/config/script_var_definition.rs index a792fc7..82d0928 100644 --- a/crates/yuck/src/config/script_var_definition.rs +++ b/crates/yuck/src/config/script_var_definition.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use simplexpr::{dynval::DynVal, SimplExpr}; use crate::{ - error::{AstError, AstResult}, + error::{AstError, AstResult, AstResultExt}, parser::{ ast::Ast, ast_iterator::AstIterator, @@ -66,12 +66,15 @@ impl FromAstElementContent for PollScriptVar { } fn from_tail>(span: Span, mut iter: AstIterator) -> AstResult { - let (name_span, name) = iter.expect_symbol()?; - let mut attrs = iter.expect_key_values()?; - let interval = attrs.primitive_required::("interval")?.as_duration()?; - let (script_span, script) = iter.expect_literal()?; - iter.expect_done()?; - Ok(Self { name_span, name: VarName(name), command: VarSource::Shell(script_span, script.to_string()), interval }) + let result: AstResult<_> = try { + let (name_span, name) = iter.expect_symbol()?; + let mut attrs = iter.expect_key_values()?; + let interval = attrs.primitive_required::("interval")?.as_duration()?; + let (script_span, script) = iter.expect_literal()?; + iter.expect_done()?; + Self { name_span, name: VarName(name), command: VarSource::Shell(script_span, script.to_string()), interval } + }; + result.note(r#"Expected format: `(defpoll name :interval "10s" "echo 'a shell script'")`"#) } } @@ -88,9 +91,12 @@ impl FromAstElementContent for ListenScriptVar { } fn from_tail>(span: Span, mut iter: AstIterator) -> AstResult { + let result: AstResult<_> = try { let (name_span, name) = iter.expect_symbol()?; let (command_span, script) = iter.expect_literal()?; iter.expect_done()?; - Ok(Self { name_span, name: VarName(name), command: script.to_string(), command_span }) + Self { name_span, name: VarName(name), command: script.to_string(), command_span } + }; + result.note(r#"Expected format: `(deflisten name "tail -f /tmp/example")`"#) } } diff --git a/crates/yuck/src/config/snapshots/yuck__config__test__config.snap b/crates/yuck/src/config/snapshots/yuck__config__test__config.snap index d4a0ba3..0c4fe00 100644 --- a/crates/yuck/src/config/snapshots/yuck__config__test__config.snap +++ b/crates/yuck/src/config/snapshots/yuck__config__test__config.snap @@ -14,7 +14,7 @@ Config( widget: WidgetUse( name: "foo", attrs: Attributes( - span: Span(51, 61, 62), + span: Span(51, 61, 0), attrs: { AttrName("arg"): AttrEntry( key_span: Span(52, 56, 0), @@ -52,7 +52,7 @@ Config( widget: WidgetUse( name: "bar", attrs: Attributes( - span: Span(467, 478, 479), + span: Span(467, 478, 0), attrs: { AttrName("arg"): AttrEntry( key_span: Span(468, 472, 0), @@ -88,6 +88,7 @@ Config( name: VarName("stuff"), command: "tail -f stuff", command_span: Span(168, 183, 0), + name_span: Span(162, 167, 0), )), }, ) diff --git a/crates/yuck/src/config/var_definition.rs b/crates/yuck/src/config/var_definition.rs index cd39a8e..948d81a 100644 --- a/crates/yuck/src/config/var_definition.rs +++ b/crates/yuck/src/config/var_definition.rs @@ -2,14 +2,11 @@ use std::collections::HashMap; use simplexpr::{dynval::DynVal, SimplExpr}; -use crate::{ - error::AstResult, - parser::{ +use crate::{error::{AstResult, AstResultExt}, parser::{ ast::Ast, ast_iterator::AstIterator, from_ast::{FromAst, FromAstElementContent}, - }, -}; + }}; use eww_shared_util::{AttrName, Span, VarName}; #[derive(Debug, PartialEq, Eq, Clone, serde::Serialize)] @@ -25,9 +22,12 @@ impl FromAstElementContent for VarDefinition { } fn from_tail>(span: Span, mut iter: AstIterator) -> AstResult { - let (_, name) = iter.expect_symbol()?; - let (_, initial_value) = iter.expect_literal()?; - iter.expect_done()?; - Ok(Self { name: VarName(name), initial_value, span }) + let result: AstResult<_> = try { + let (_, name) = iter.expect_symbol()?; + let (_, initial_value) = iter.expect_literal()?; + iter.expect_done()?; + Self { name: VarName(name), initial_value, span } + }; + result.note(r#"Expected format: `(defvar name "initial-value")`"#) } } diff --git a/crates/yuck/src/error.rs b/crates/yuck/src/error.rs index 9c22e2a..c12756c 100644 --- a/crates/yuck/src/error.rs +++ b/crates/yuck/src/error.rs @@ -122,12 +122,17 @@ impl OptionAstErrorExt for Option { } } -pub trait ResultExt { +pub trait AstResultExt { fn context_label(self, label_span: Span, context: &str) -> AstResult; + fn note(self, note: &str) -> AstResult; } -impl ResultExt for AstResult { +impl AstResultExt for AstResult { fn context_label(self, label_span: Span, context: &str) -> AstResult { self.map_err(|e| AstError::ErrorContext { label_span, context: context.to_string(), main_err: Box::new(e) }) } + + fn note(self, note: &str) -> AstResult { + self.map_err(|e| e.note(note)) + } } diff --git a/crates/yuck/src/parser/ast_iterator.rs b/crates/yuck/src/parser/ast_iterator.rs index c2b5663..d2db491 100644 --- a/crates/yuck/src/parser/ast_iterator.rs +++ b/crates/yuck/src/parser/ast_iterator.rs @@ -98,7 +98,7 @@ impl> Iterator for AstIterator { /// Parse consecutive `:keyword value` pairs from an expression iterator into an [Attributes]. fn parse_key_values(iter: &mut AstIterator>) -> AstResult { let mut data = HashMap::new(); - let mut attrs_span = Span(iter.remaining_span.0, iter.remaining_span.0, iter.remaining_span.1); + let mut attrs_span = iter.remaining_span.point_span(); loop { match iter.next() { Some(Ast::Keyword(key_span, kw)) => match iter.next() { From 2a7255c3d5ad850b10a5abe5fe84e19e27c29d59 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Sun, 1 Aug 2021 19:22:45 +0200 Subject: [PATCH 099/137] General refactors --- crates/eww/src/app.rs | 43 ++++---------------- crates/eww/src/client.rs | 4 +- crates/eww/src/config/eww_config.rs | 8 ++++ crates/eww/src/daemon_response.rs | 39 ++++++++++++++++++ crates/eww/src/error_handling_ctx.rs | 39 ++++++++++-------- crates/eww/src/main.rs | 4 +- crates/eww/src/opts.rs | 15 ++++--- crates/eww/src/server.rs | 19 +++------ crates/eww/src/widgets/widget_definitions.rs | 2 +- 9 files changed, 97 insertions(+), 76 deletions(-) create mode 100644 crates/eww/src/daemon_response.rs diff --git a/crates/eww/src/app.rs b/crates/eww/src/app.rs index d06482c..4158f6c 100644 --- a/crates/eww/src/app.rs +++ b/crates/eww/src/app.rs @@ -1,4 +1,4 @@ -use crate::{config, display_backend, error_handling_ctx, eww_state, script_var_handler::*, EwwPaths}; +use crate::{EwwPaths, config, daemon_response::DaemonResponseSender, display_backend, error_handling_ctx, eww_state, script_var_handler::*}; use anyhow::*; use debug_stub_derive::*; use eww_shared_util::VarName; @@ -13,27 +13,6 @@ use yuck::{ value::Coords, }; -/// Response that the app may send as a response to a event. -/// This is used in `DaemonCommand`s that contain a response sender. -#[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize, derive_more::Display)] -pub enum DaemonResponse { - Success(String), - Failure(String), -} - -impl DaemonResponse { - pub fn is_success(&self) -> bool { - matches!(self, DaemonResponse::Success(_)) - } - - pub fn is_failure(&self) -> bool { - !self.is_success() - } -} - -pub type DaemonResponseSender = tokio::sync::mpsc::UnboundedSender; -pub type DaemonResponseReceiver = tokio::sync::mpsc::UnboundedReceiver; - #[derive(Debug)] pub enum DaemonCommand { NoOp, @@ -111,11 +90,7 @@ impl App { DaemonCommand::ReloadConfigAndCss(sender) => { let mut errors = Vec::new(); - error_handling_ctx::clear_files(); - let config_result = config::EwwConfig::read_from_file( - &mut error_handling_ctx::ERROR_HANDLING_CTX.lock().unwrap(), - &self.paths.get_yuck_path(), - ); + let config_result = config::read_from_file(&self.paths.get_yuck_path()); match config_result.and_then(|new_config| self.load_config(new_config)) { Ok(()) => {} Err(e) => errors.push(e), @@ -129,9 +104,9 @@ impl App { let errors = errors.into_iter().map(|e| error_handling_ctx::format_error(&e)).join("\n"); if errors.is_empty() { - sender.send(DaemonResponse::Success(String::new()))?; + sender.send_success(String::new())?; } else { - sender.send(DaemonResponse::Failure(errors))?; + sender.send_failure(errors)?; } } DaemonCommand::UpdateConfig(config) => { @@ -176,7 +151,7 @@ impl App { .map(|(key, value)| format!("{}: {}", key, value)) .join("\n") }; - sender.send(DaemonResponse::Success(output)).context("sending response from main thread")? + sender.send_success(output)? } DaemonCommand::PrintWindows(sender) => { let output = self @@ -188,11 +163,11 @@ impl App { format!("{}{}", if is_open { "*" } else { "" }, window_name) }) .join("\n"); - sender.send(DaemonResponse::Success(output)).context("sending response from main thread")? + sender.send_success(output)? } DaemonCommand::PrintDebug(sender) => { let output = format!("state: {:#?}\n\nconfig: {:#?}", &self.eww_state, &self.eww_config); - sender.send(DaemonResponse::Success(output)).context("sending response from main thread")? + sender.send_success(output)? } } }; @@ -387,8 +362,8 @@ fn get_monitor_geometry(n: i32) -> gdk::Rectangle { /// In case of an Err, send the error message to a sender. fn respond_with_error(sender: DaemonResponseSender, result: Result) -> Result<()> { match result { - Ok(_) => sender.send(DaemonResponse::Success(String::new())), - Err(e) => sender.send(DaemonResponse::Failure(error_handling_ctx::format_error(&e))), + Ok(_) => sender.send_success(String::new()), + Err(e) => sender.send_failure(error_handling_ctx::format_error(&e)), } .context("sending response from main thread") } diff --git a/crates/eww/src/client.rs b/crates/eww/src/client.rs index 1297196..60bee3d 100644 --- a/crates/eww/src/client.rs +++ b/crates/eww/src/client.rs @@ -1,7 +1,7 @@ use std::process::Stdio; use crate::{ - app, + daemon_response::DaemonResponse, opts::{self, ActionClientOnly}, EwwPaths, }; @@ -24,7 +24,7 @@ pub fn handle_client_only_action(paths: &EwwPaths, action: ActionClientOnly) -> Ok(()) } -pub fn do_server_call(stream: &mut UnixStream, action: &opts::ActionWithServer) -> Result> { +pub fn do_server_call(stream: &mut UnixStream, action: &opts::ActionWithServer) -> Result> { log::info!("Forwarding options to server"); stream.set_nonblocking(false).context("Failed to set stream to non-blocking")?; diff --git a/crates/eww/src/config/eww_config.rs b/crates/eww/src/config/eww_config.rs index ffc275b..722f019 100644 --- a/crates/eww/src/config/eww_config.rs +++ b/crates/eww/src/config/eww_config.rs @@ -7,8 +7,16 @@ use yuck::config::{ use simplexpr::dynval::DynVal; +use crate::error_handling_ctx; + use super::{script_var, EwwWindowDefinition}; +/// Load an [EwwConfig] from a given file, resetting and applying the global YuckFiles object in [error_handling_ctx]. +pub fn read_from_file(path: impl AsRef) -> Result { + error_handling_ctx::clear_files(); + EwwConfig::read_from_file(&mut error_handling_ctx::YUCK_FILES.write().unwrap(), path) +} + /// Eww configuration structure. #[derive(Debug, Clone)] pub struct EwwConfig { diff --git a/crates/eww/src/daemon_response.rs b/crates/eww/src/daemon_response.rs new file mode 100644 index 0000000..19e96e2 --- /dev/null +++ b/crates/eww/src/daemon_response.rs @@ -0,0 +1,39 @@ +use anyhow::*; + +/// Response that the app may send as a response to a event. +/// This is used in `DaemonCommand`s that contain a response sender. +#[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize, derive_more::Display)] +pub enum DaemonResponse { + Success(String), + Failure(String), +} + +impl DaemonResponse { + pub fn is_success(&self) -> bool { + matches!(self, DaemonResponse::Success(_)) + } + + pub fn is_failure(&self) -> bool { + !self.is_success() + } +} + +#[derive(Debug)] +pub struct DaemonResponseSender(tokio::sync::mpsc::UnboundedSender); + +pub fn create_pair() -> (DaemonResponseSender, tokio::sync::mpsc::UnboundedReceiver) { + let (sender, recv) = tokio::sync::mpsc::unbounded_channel(); + (DaemonResponseSender(sender), recv) +} + +impl DaemonResponseSender { + pub fn send_success(&self, s: String) -> Result<()> { + self.0.send(DaemonResponse::Success(s)).context("Failed to send success response from application thread") + } + + pub fn send_failure(&self, s: String) -> Result<()> { + self.0.send(DaemonResponse::Failure(s)).context("Failed to send failure response from application thread") + } +} + +pub type DaemonResponseReceiver = tokio::sync::mpsc::UnboundedReceiver; diff --git a/crates/eww/src/error_handling_ctx.rs b/crates/eww/src/error_handling_ctx.rs index 0611e51..dd0d2fb 100644 --- a/crates/eww/src/error_handling_ctx.rs +++ b/crates/eww/src/error_handling_ctx.rs @@ -1,4 +1,7 @@ -use std::sync::{Arc, Mutex}; +//! Disgusting global state. +//! I hate this, but [buffet](https://github.com/buffet) told me that this is what I should do for peak maintainability! + +use std::sync::{Arc, RwLock}; use codespan_reporting::{ diagnostic::Diagnostic, @@ -11,24 +14,10 @@ use yuck::{config::file_provider::YuckFiles, error::AstError, format_diagnostic: use crate::error::DiagError; -pub static ERROR_HANDLING_CTX: Lazy>> = Lazy::new(|| Arc::new(Mutex::new(YuckFiles::new()))); +pub static YUCK_FILES: Lazy>> = Lazy::new(|| Arc::new(RwLock::new(YuckFiles::new()))); pub fn clear_files() { - *ERROR_HANDLING_CTX.lock().unwrap() = YuckFiles::new(); -} - -pub fn anyhow_err_to_diagnostic(err: &anyhow::Error) -> Diagnostic { - if let Some(err) = err.downcast_ref::() { - err.diag.clone() - } else if let Some(err) = err.downcast_ref::() { - err.to_diagnostic() - } else if let Some(err) = err.downcast_ref::() { - err.to_diagnostic() - } else if let Some(err) = err.downcast_ref::() { - err.to_diagnostic() - } else { - gen_diagnostic!(err) - } + *YUCK_FILES.write().unwrap() = YuckFiles::new(); } pub fn print_error(err: anyhow::Error) { @@ -52,6 +41,20 @@ pub fn format_error(err: &anyhow::Error) -> String { } } +pub fn anyhow_err_to_diagnostic(err: &anyhow::Error) -> Diagnostic { + if let Some(err) = err.downcast_ref::() { + err.diag.clone() + } else if let Some(err) = err.downcast_ref::() { + err.to_diagnostic() + } else if let Some(err) = err.downcast_ref::() { + err.to_diagnostic() + } else if let Some(err) = err.downcast_ref::() { + err.to_diagnostic() + } else { + gen_diagnostic!(err) + } +} + pub fn stringify_diagnostic(mut diagnostic: codespan_reporting::diagnostic::Diagnostic) -> anyhow::Result { diagnostic.labels.drain_filter(|label| Span(label.range.start, label.range.end, label.file_id).is_dummy()); @@ -61,7 +64,7 @@ pub fn stringify_diagnostic(mut diagnostic: codespan_reporting::diagnostic::Diag config.chars = chars; let mut buf = Vec::new(); let mut writer = term::termcolor::Ansi::new(&mut buf); - let files = ERROR_HANDLING_CTX.lock().unwrap(); + let files = YUCK_FILES.read().unwrap(); term::emit(&mut writer, &config, &*files, &diagnostic)?; Ok(String::from_utf8(buf)?) } diff --git a/crates/eww/src/main.rs b/crates/eww/src/main.rs index 956dbdd..ed7d758 100644 --- a/crates/eww/src/main.rs +++ b/crates/eww/src/main.rs @@ -13,6 +13,7 @@ extern crate gtk; extern crate gtk_layer_shell as gtk_layer_shell; use anyhow::*; +use daemon_response::DaemonResponseReceiver; use opts::ActionWithServer; use std::{ os::unix::net, @@ -37,6 +38,7 @@ pub mod script_var_handler; pub mod server; pub mod util; pub mod widgets; +mod daemon_response; fn main() { let eww_binary_name = std::env::args().next().unwrap(); @@ -114,7 +116,7 @@ fn main() { } } -fn listen_for_daemon_response(mut recv: app::DaemonResponseReceiver) { +fn listen_for_daemon_response(mut recv: DaemonResponseReceiver) { let rt = tokio::runtime::Builder::new_current_thread().enable_time().build().expect("Failed to initialize tokio runtime"); rt.block_on(async { if let Ok(Some(response)) = tokio::time::timeout(Duration::from_millis(100), recv.recv()).await { diff --git a/crates/eww/src/opts.rs b/crates/eww/src/opts.rs index 802c2d3..01550a3 100644 --- a/crates/eww/src/opts.rs +++ b/crates/eww/src/opts.rs @@ -5,7 +5,10 @@ use simplexpr::dynval::DynVal; use structopt::StructOpt; use yuck::{config::window_geometry::AnchorPoint, value::Coords}; -use crate::app; +use crate::{ + app, + daemon_response::{self, DaemonResponse, DaemonResponseSender}, +}; /// Struct that gets generated from `RawOpt`. #[derive(Debug, Serialize, Deserialize, PartialEq)] @@ -158,7 +161,7 @@ fn parse_var_update_arg(s: &str) -> Result<(VarName, DynVal)> { } impl ActionWithServer { - pub fn into_daemon_command(self) -> (app::DaemonCommand, Option) { + pub fn into_daemon_command(self) -> (app::DaemonCommand, Option) { let command = match self { ActionWithServer::Update { mappings } => app::DaemonCommand::UpdateVars(mappings), @@ -166,7 +169,7 @@ impl ActionWithServer { ActionWithServer::CloseAll => app::DaemonCommand::CloseAll, ActionWithServer::Ping => { let (send, recv) = tokio::sync::mpsc::unbounded_channel(); - let _ = send.send(app::DaemonResponse::Success("pong".to_owned())); + let _ = send.send(DaemonResponse::Success("pong".to_owned())); return (app::DaemonCommand::NoOp, Some(recv)); } ActionWithServer::OpenMany { windows } => { @@ -197,10 +200,10 @@ impl ActionWithServer { } } -fn with_response_channel(f: F) -> (O, Option>) +fn with_response_channel(f: F) -> (O, Option>) where - F: FnOnce(tokio::sync::mpsc::UnboundedSender) -> O, + F: FnOnce(DaemonResponseSender) -> O, { - let (sender, recv) = tokio::sync::mpsc::unbounded_channel(); + let (sender, recv) = daemon_response::create_pair(); (f(sender), Some(recv)) } diff --git a/crates/eww/src/server.rs b/crates/eww/src/server.rs index 784e0a4..dbcae9f 100644 --- a/crates/eww/src/server.rs +++ b/crates/eww/src/server.rs @@ -1,9 +1,4 @@ -use crate::{ - app::{self, DaemonCommand}, - config, error_handling_ctx, - eww_state::*, - ipc_server, script_var_handler, util, EwwPaths, -}; +use crate::{EwwPaths, app::{self, DaemonCommand}, config, daemon_response, error_handling_ctx, eww_state::*, ipc_server, script_var_handler, util}; use anyhow::*; use std::{ @@ -22,11 +17,7 @@ pub fn initialize_server(paths: EwwPaths, action: Option) -> Resu log::info!("Loading paths: {}", &paths); - // disgusting global state, I hate this, but https://github.com/buffet told me that this is what I should do for peak maintainability - error_handling_ctx::clear_files(); - - let read_config = - config::EwwConfig::read_from_file(&mut error_handling_ctx::ERROR_HANDLING_CTX.lock().unwrap(), &paths.get_yuck_path()); + let read_config = config::read_from_file(&paths.get_yuck_path()); let eww_config = match read_config { Ok(config) => config, @@ -167,12 +158,12 @@ async fn run_filewatch>(config_dir: P, evt_send: UnboundedSender< debounce_done.store(true, Ordering::SeqCst); }); - let (daemon_resp_sender, mut daemon_resp_response) = tokio::sync::mpsc::unbounded_channel(); + let (daemon_resp_sender, mut daemon_resp_response) = daemon_response::create_pair(); evt_send.send(app::DaemonCommand::ReloadConfigAndCss(daemon_resp_sender))?; tokio::spawn(async move { match daemon_resp_response.recv().await { - Some(app::DaemonResponse::Success(_)) => log::info!("Reloaded config successfully"), - Some(app::DaemonResponse::Failure(e)) => eprintln!("{}", e), + Some(daemon_response::DaemonResponse::Success(_)) => log::info!("Reloaded config successfully"), + Some(daemon_response::DaemonResponse::Failure(e)) => eprintln!("{}", e), None => log::error!("No response to reload configuration-reload request"), } }); diff --git a/crates/eww/src/widgets/widget_definitions.rs b/crates/eww/src/widgets/widget_definitions.rs index 61e4744..755f3f2 100644 --- a/crates/eww/src/widgets/widget_definitions.rs +++ b/crates/eww/src/widgets/widget_definitions.rs @@ -525,7 +525,7 @@ fn build_gtk_literal(bargs: &mut BuilderArgs) -> Result { if !content.is_empty() { let widget_node_result: AstResult<_> = try { let ast = { - let mut yuck_files = error_handling_ctx::ERROR_HANDLING_CTX.lock().unwrap(); + let mut yuck_files = error_handling_ctx::YUCK_FILES.write().unwrap(); let (span, asts) = yuck_files.load_str("".to_string(), content)?; if let Some(file_id) = literal_file_id.replace(Some(span.2)) { yuck_files.unload(file_id); From 9588f31861d1dd081c68acf56ac1215286832dbb Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Mon, 2 Aug 2021 14:20:00 +0200 Subject: [PATCH 100/137] Add timeout to widgets reading on-click commands --- crates/eww/src/config/script_var.rs | 4 +- crates/eww/src/script_var_handler.rs | 4 +- crates/eww/src/widgets/mod.rs | 4 +- crates/eww/src/widgets/widget_definitions.rs | 62 ++++++++++++------- .../yuck/src/config/script_var_definition.rs | 13 ++-- 5 files changed, 54 insertions(+), 33 deletions(-) diff --git a/crates/eww/src/config/script_var.rs b/crates/eww/src/config/script_var.rs index a62a864..46db670 100644 --- a/crates/eww/src/config/script_var.rs +++ b/crates/eww/src/config/script_var.rs @@ -26,8 +26,8 @@ pub fn initial_value(var: &ScriptVarDefinition) -> Result { VarSource::Function(f) => { f().map_err(|err| anyhow!(err)).with_context(|| format!("Failed to compute initial value for {}", &var.name())) } - VarSource::Shell(span, f) => { - run_command(f).map_err(|e| anyhow!(create_script_var_failed_warn(*span, var.name(), &e.to_string()))) + VarSource::Shell(span, command) => { + run_command(command).map_err(|e| anyhow!(create_script_var_failed_warn(*span, var.name(), &e.to_string()))) } }, ScriptVarDefinition::Listen(_) => Ok(DynVal::from_string(String::new())), diff --git a/crates/eww/src/script_var_handler.rs b/crates/eww/src/script_var_handler.rs index f28a534..8fc2202 100644 --- a/crates/eww/src/script_var_handler.rs +++ b/crates/eww/src/script_var_handler.rs @@ -171,8 +171,8 @@ impl PollVarHandler { fn run_poll_once(var: &PollScriptVar) -> Result { match &var.command { - VarSource::Shell(span, x) => { - script_var::run_command(x).map_err(|e| anyhow!(create_script_var_failed_warn(*span, &var.name, &e.to_string()))) + VarSource::Shell(span, command) => { + script_var::run_command(command).map_err(|e| anyhow!(create_script_var_failed_warn(*span, &var.name, &e.to_string()))) } VarSource::Function(x) => x().map_err(|e| anyhow!(e)), } diff --git a/crates/eww/src/widgets/mod.rs b/crates/eww/src/widgets/mod.rs index a918390..6d83bb2 100644 --- a/crates/eww/src/widgets/mod.rs +++ b/crates/eww/src/widgets/mod.rs @@ -16,7 +16,7 @@ const CMD_STRING_PLACEHODLER: &str = "{}"; /// Run a command that was provided as an attribute. This command may use a /// placeholder ('{}') which will be replaced by the value provided as [`arg`] -pub(self) fn run_command(cmd: &str, arg: T) { +pub(self) fn run_command(timeout: std::time::Duration, cmd: &str, arg: T) { use wait_timeout::ChildExt; let cmd = cmd.to_string(); std::thread::spawn(move || { @@ -24,7 +24,7 @@ pub(self) fn run_command(cmd: &str log::debug!("Running command from widget: {}", cmd); let child = Command::new("/bin/sh").arg("-c").arg(&cmd).spawn(); match child { - Ok(mut child) => match child.wait_timeout(std::time::Duration::from_millis(200)) { + Ok(mut child) => match child.wait_timeout(timeout) { // child timed out Ok(None) => { log::error!("WARNING: command {} timed out", &cmd); diff --git a/crates/eww/src/widgets/widget_definitions.rs b/crates/eww/src/widgets/widget_definitions.rs index 755f3f2..b67bd04 100644 --- a/crates/eww/src/widgets/widget_definitions.rs +++ b/crates/eww/src/widgets/widget_definitions.rs @@ -104,24 +104,26 @@ pub(super) fn resolve_widget_attrs(bargs: &mut BuilderArgs, gtk_widget: >k::Wi css_provider.load_from_data(format!("* {{ {} }}", style).as_bytes())?; gtk_widget.get_style_context().add_provider(&css_provider, gtk::STYLE_PROVIDER_PRIORITY_APPLICATION) }, + // @prop timeout - timeout of the command // @prop onscroll - event to execute when the user scrolls with the mouse over the widget. The placeholder `{}` used in the command will be replaced with either `up` or `down`. - prop(onscroll: as_string) { + prop(timeout: as_duration = Duration::from_millis(200), onscroll: as_string) { gtk_widget.add_events(gdk::EventMask::SCROLL_MASK); gtk_widget.add_events(gdk::EventMask::SMOOTH_SCROLL_MASK); let old_id = on_scroll_handler_id.replace(Some( gtk_widget.connect_scroll_event(move |_, evt| { - run_command(&onscroll, if evt.get_delta().1 < 0f64 { "up" } else { "down" }); + run_command(timeout, &onscroll, if evt.get_delta().1 < 0f64 { "up" } else { "down" }); gtk::Inhibit(false) }) )); old_id.map(|id| gtk_widget.disconnect(id)); }, + // @prop timeout - timeout of the command // @prop onhover - event to execute when the user hovers over the widget - prop(onhover: as_string) { + prop(timeout: as_duration = Duration::from_millis(200),onhover: as_string) { gtk_widget.add_events(gdk::EventMask::ENTER_NOTIFY_MASK); let old_id = on_hover_handler_id.replace(Some( gtk_widget.connect_enter_notify_event(move |_, evt| { - run_command(&onhover, format!("{} {}", evt.get_position().0, evt.get_position().1)); + run_command(timeout, &onhover, format!("{} {}", evt.get_position().0, evt.get_position().1)); gtk::Inhibit(false) }) )); @@ -192,13 +194,14 @@ pub(super) fn resolve_range_attrs(bargs: &mut BuilderArgs, gtk_widget: >k::Ran prop(min: as_f64) { gtk_widget.get_adjustment().set_lower(min)}, // @prop max - the maximum value prop(max: as_f64) { gtk_widget.get_adjustment().set_upper(max)}, + // @prop timeout - timeout of the command // @prop onchange - command executed once the value is changes. The placeholder `{}`, used in the command will be replaced by the new value. - prop(onchange: as_string) { + prop(timeout: as_duration = Duration::from_millis(200), onchange: as_string) { gtk_widget.set_sensitive(true); gtk_widget.add_events(gdk::EventMask::ENTER_NOTIFY_MASK); let old_id = on_change_handler_id.replace(Some( gtk_widget.connect_value_changed(move |gtk_widget| { - run_command(&onchange, gtk_widget.get_value()); + run_command(timeout, &onchange, gtk_widget.get_value()); }) )); old_id.map(|id| gtk_widget.disconnect(id)); @@ -253,11 +256,12 @@ fn build_gtk_combo_box_text(bargs: &mut BuilderArgs) -> Result Result { let gtk_widget = gtk::CheckButton::new(); let on_change_handler_id: Rc>> = Rc::new(RefCell::new(None)); resolve_block!(bargs, gtk_widget, { - // @prop onchecked - action (command) to be executed when checked by the user - // @prop onunchecked - similar to onchecked but when the widget is unchecked - prop(onchecked: as_string = "", onunchecked: as_string = "") { + // @prop timeout - timeout of the command + // @prop onchecked - action (command) to be executed when checked by the user + // @prop onunchecked - similar to onchecked but when the widget is unchecked + prop(timeout: as_duration = Duration::from_millis(200), onchecked: as_string = "", onunchecked: as_string = "") { let old_id = on_change_handler_id.replace(Some( gtk_widget.connect_toggled(move |gtk_widget| { - run_command(if gtk_widget.get_active() { &onchecked } else { &onunchecked }, ""); + run_command(timeout, if gtk_widget.get_active() { &onchecked } else { &onunchecked }, ""); }) )); old_id.map(|id| gtk_widget.disconnect(id)); @@ -324,10 +329,11 @@ fn build_gtk_color_button(bargs: &mut BuilderArgs) -> Result { prop(use_alpha: as_bool) {gtk_widget.set_use_alpha(use_alpha);}, // @prop onchange - runs the code when the color was selected - prop(onchange: as_string) { + // @prop timeout - timeout of the command + prop(timeout: as_duration = Duration::from_millis(200), onchange: as_string) { let old_id = on_change_handler_id.replace(Some( gtk_widget.connect_color_set(move |gtk_widget| { - run_command(&onchange, gtk_widget.get_rgba()); + run_command(timeout, &onchange, gtk_widget.get_rgba()); }) )); old_id.map(|id| gtk_widget.disconnect(id)); @@ -347,10 +353,11 @@ fn build_gtk_color_chooser(bargs: &mut BuilderArgs) -> Result Result { }, // @prop onchange - Command to run when the text changes. The placeholder `{}` will be replaced by the value - prop(onchange: as_string) { + // @prop timeout - timeout of the command + prop(timeout: as_duration = Duration::from_millis(200), onchange: as_string) { let old_id = on_change_handler_id.replace(Some( gtk_widget.connect_changed(move |gtk_widget| { - run_command(&onchange, gtk_widget.get_text().to_string()); + run_command(timeout, &onchange, gtk_widget.get_text().to_string()); }) )); old_id.map(|id| gtk_widget.disconnect(id)); @@ -425,14 +433,20 @@ fn build_gtk_button(bargs: &mut BuilderArgs) -> Result { // @prop onclick - a command that get's run when the button is clicked // @prop onmiddleclick - a command that get's run when the button is middleclicked // @prop onrightclick - a command that get's run when the button is rightclicked - prop(onclick: as_string = "", onmiddleclick: as_string = "", onrightclick: as_string = "") { + // @prop timeout - timeout of the command + prop( + timeout: as_duration = Duration::from_millis(200), + onclick: as_string = "", + onmiddleclick: as_string = "", + onrightclick: as_string = "" + ) { gtk_widget.add_events(gdk::EventMask::ENTER_NOTIFY_MASK); let old_id = on_click_handler_id.replace(Some( gtk_widget.connect_button_press_event(move |_, evt| { match evt.get_button() { - 1 => run_command(&onclick, ""), - 2 => run_command(&onmiddleclick, ""), - 3 => run_command(&onrightclick, ""), + 1 => run_command(timeout, &onclick, ""), + 2 => run_command(timeout, &onmiddleclick, ""), + 3 => run_command(timeout, &onrightclick, ""), _ => {}, } gtk::Inhibit(false) @@ -573,10 +587,12 @@ fn build_gtk_calendar(bargs: &mut BuilderArgs) -> Result { // @prop show-week-numbers - show week numbers prop(show_week_numbers: as_bool) { gtk_widget.set_property_show_week_numbers(show_week_numbers) }, // @prop onclick - command to run when the user selects a date. The `{}` placeholder will be replaced by the selected date. - prop(onclick: as_string) { + // @prop timeout - timeout of the command + prop(timeout: as_duration = Duration::from_millis(200), onclick: as_string) { let old_id = on_click_handler_id.replace(Some( gtk_widget.connect_day_selected(move |w| { run_command( + timeout, &onclick, format!("{}.{}.{}", w.get_property_day(), w.get_property_month(), w.get_property_year()) ) diff --git a/crates/yuck/src/config/script_var_definition.rs b/crates/yuck/src/config/script_var_definition.rs index 82d0928..d616dd3 100644 --- a/crates/yuck/src/config/script_var_definition.rs +++ b/crates/yuck/src/config/script_var_definition.rs @@ -70,6 +70,11 @@ impl FromAstElementContent for PollScriptVar { let (name_span, name) = iter.expect_symbol()?; let mut attrs = iter.expect_key_values()?; let interval = attrs.primitive_required::("interval")?.as_duration()?; + let timeout = attrs + .primitive_optional::("timeout")? + .map(|x| x.as_duration()) + .transpose()? + .unwrap_or_else(|| std::time::Duration::from_millis(200)); let (script_span, script) = iter.expect_literal()?; iter.expect_done()?; Self { name_span, name: VarName(name), command: VarSource::Shell(script_span, script.to_string()), interval } @@ -92,10 +97,10 @@ impl FromAstElementContent for ListenScriptVar { fn from_tail>(span: Span, mut iter: AstIterator) -> AstResult { let result: AstResult<_> = try { - let (name_span, name) = iter.expect_symbol()?; - let (command_span, script) = iter.expect_literal()?; - iter.expect_done()?; - Self { name_span, name: VarName(name), command: script.to_string(), command_span } + let (name_span, name) = iter.expect_symbol()?; + let (command_span, script) = iter.expect_literal()?; + iter.expect_done()?; + Self { name_span, name: VarName(name), command: script.to_string(), command_span } }; result.note(r#"Expected format: `(deflisten name "tail -f /tmp/example")`"#) } From eb3c2646a04fdcc2f9dab8d37d49b895233b5e3b Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Mon, 2 Aug 2021 15:54:38 +0200 Subject: [PATCH 101/137] Add did you mean question to wrong variables --- Cargo.lock | 7 +++++ crates/simplexpr/Cargo.toml | 1 + crates/simplexpr/src/eval.rs | 18 +++++++++--- crates/yuck/Cargo.toml | 2 +- .../yuck/src/config/backend_window_options.rs | 4 +-- crates/yuck/src/config/config.rs | 25 ++++++++++------ .../yuck/src/config/script_var_definition.rs | 8 ++--- crates/yuck/src/config/var_definition.rs | 11 +++---- crates/yuck/src/config/widget_definition.rs | 4 +-- crates/yuck/src/config/window_definition.rs | 4 +-- crates/yuck/src/config/window_geometry.rs | 4 +-- crates/yuck/src/format_diagnostic.rs | 29 +++++++++++++------ crates/yuck/src/parser/from_ast.rs | 6 ++-- 13 files changed, 74 insertions(+), 49 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cca7641..6e58f10 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1114,6 +1114,12 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "levenshtein" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760" + [[package]] name = "libc" version = "0.2.98" @@ -1890,6 +1896,7 @@ dependencies = [ "itertools 0.10.1", "lalrpop", "lalrpop-util", + "levenshtein", "logos", "maplit", "once_cell", diff --git a/crates/simplexpr/Cargo.toml b/crates/simplexpr/Cargo.toml index 97b032a..bf1785f 100644 --- a/crates/simplexpr/Cargo.toml +++ b/crates/simplexpr/Cargo.toml @@ -18,6 +18,7 @@ logos = "0.12" once_cell = "1.8.0" serde = {version = "1.0", features = ["derive"]} serde_json = "1.0" +levenshtein = "1.0" strum = { version = "0.21", features = ["derive"] } diff --git a/crates/simplexpr/src/eval.rs b/crates/simplexpr/src/eval.rs index cf508ca..5d02944 100644 --- a/crates/simplexpr/src/eval.rs +++ b/crates/simplexpr/src/eval.rs @@ -16,7 +16,7 @@ pub enum EvalError { InvalidRegex(#[from] regex::Error), #[error("Unknown variable {0}")] - UnknownVariable(VarName), + UnknownVariable(VarName, Vec), #[error(transparent)] ConversionError(#[from] ConversionError), @@ -108,7 +108,11 @@ impl SimplExpr { )), VarRef(span, ref name) => match variables.get(name) { Some(value) => Ok(Literal(value.clone())), - None => Err(EvalError::UnknownVariable(name.clone()).at(span)), + None => { + let similar_ish = + variables.keys().filter(|key| levenshtein::levenshtein(&key.0, &name.0) < 3).cloned().collect_vec(); + Err(EvalError::UnknownVariable(name.clone(), similar_ish).at(span)) + } }, } } @@ -138,7 +142,7 @@ impl SimplExpr { match self.eval(&HashMap::new()) { Ok(x) => Ok(x), Err(x) => Err(x.map_in_span(|err| match err { - EvalError::UnknownVariable(name) => EvalError::NoVariablesAllowed(name), + EvalError::UnknownVariable(name, _) => EvalError::NoVariablesAllowed(name), other => other, })), } @@ -149,7 +153,13 @@ impl SimplExpr { let value = match self { SimplExpr::Literal(x) => Ok(x.clone()), SimplExpr::VarRef(span, ref name) => { - Ok(values.get(name).cloned().ok_or_else(|| EvalError::UnknownVariable(name.clone()).at(*span))?.at(*span)) + let similar_ish = + values.keys().filter(|keys| levenshtein::levenshtein(&keys.0, &name.0) < 3).cloned().collect_vec(); + Ok(values + .get(name) + .cloned() + .ok_or_else(|| EvalError::UnknownVariable(name.clone(), similar_ish).at(*span))? + .at(*span)) } SimplExpr::BinOp(_, a, op, b) => { let a = a.eval(values)?; diff --git a/crates/yuck/Cargo.toml b/crates/yuck/Cargo.toml index ec53745..4e65d56 100644 --- a/crates/yuck/Cargo.toml +++ b/crates/yuck/Cargo.toml @@ -30,10 +30,10 @@ strum = { version = "0.21", features = ["derive"] } anyhow = "1" static_assertions = "1.1" - simplexpr = { path = "../simplexpr" } eww_shared_util = { path = "../eww_shared_util" } + [build-dependencies] lalrpop = "0.19.5" diff --git a/crates/yuck/src/config/backend_window_options.rs b/crates/yuck/src/config/backend_window_options.rs index d5ced8f..46eedfa 100644 --- a/crates/yuck/src/config/backend_window_options.rs +++ b/crates/yuck/src/config/backend_window_options.rs @@ -92,9 +92,7 @@ mod backend { } impl FromAstElementContent for StrutDefinition { - fn get_element_name() -> &'static str { - "struts" - } + const ELEMENT_NAME: &'static str = "struts"; fn from_tail>(span: Span, mut iter: AstIterator) -> AstResult { let mut attrs = iter.expect_key_values()?; diff --git a/crates/yuck/src/config/config.rs b/crates/yuck/src/config/config.rs index 9e94cb0..71d84bf 100644 --- a/crates/yuck/src/config/config.rs +++ b/crates/yuck/src/config/config.rs @@ -25,6 +25,15 @@ use crate::{ }; use eww_shared_util::{AttrName, Span, VarName}; +pub static TOP_LEVEL_DEFINITION_NAMES: &[&str] = &[ + WidgetDefinition::ELEMENT_NAME, + WindowDefinition::ELEMENT_NAME, + VarDefinition::ELEMENT_NAME, + ListenScriptVar::ELEMENT_NAME, + PollScriptVar::ELEMENT_NAME, + Include::ELEMENT_NAME, +]; + #[derive(Debug, PartialEq, Eq, Clone, serde::Serialize)] pub struct Include { pub path: String, @@ -32,9 +41,7 @@ pub struct Include { } impl FromAstElementContent for Include { - fn get_element_name() -> &'static str { - "include" - } + const ELEMENT_NAME: &'static str = "include"; fn from_tail>(span: Span, mut iter: AstIterator) -> AstResult { let (path_span, path) = iter.expect_literal()?; @@ -57,16 +64,16 @@ impl FromAst for TopLevel { let mut iter = e.try_ast_iter()?; let (sym_span, element_name) = iter.expect_symbol()?; Ok(match element_name.as_str() { - x if x == Include::get_element_name() => Self::Include(Include::from_tail(span, iter)?), - x if x == WidgetDefinition::get_element_name() => Self::WidgetDefinition(WidgetDefinition::from_tail(span, iter)?), - x if x == VarDefinition::get_element_name() => Self::VarDefinition(VarDefinition::from_tail(span, iter)?), - x if x == PollScriptVar::get_element_name() => { + x if x == Include::ELEMENT_NAME => Self::Include(Include::from_tail(span, iter)?), + x if x == WidgetDefinition::ELEMENT_NAME => Self::WidgetDefinition(WidgetDefinition::from_tail(span, iter)?), + x if x == VarDefinition::ELEMENT_NAME => Self::VarDefinition(VarDefinition::from_tail(span, iter)?), + x if x == PollScriptVar::ELEMENT_NAME => { Self::ScriptVarDefinition(ScriptVarDefinition::Poll(PollScriptVar::from_tail(span, iter)?)) } - x if x == ListenScriptVar::get_element_name() => { + x if x == ListenScriptVar::ELEMENT_NAME => { Self::ScriptVarDefinition(ScriptVarDefinition::Listen(ListenScriptVar::from_tail(span, iter)?)) } - x if x == WindowDefinition::get_element_name() => Self::WindowDefinition(WindowDefinition::from_tail(span, iter)?), + x if x == WindowDefinition::ELEMENT_NAME => Self::WindowDefinition(WindowDefinition::from_tail(span, iter)?), x => return Err(AstError::UnknownToplevel(sym_span, x.to_string())), }) } diff --git a/crates/yuck/src/config/script_var_definition.rs b/crates/yuck/src/config/script_var_definition.rs index d616dd3..bc48e51 100644 --- a/crates/yuck/src/config/script_var_definition.rs +++ b/crates/yuck/src/config/script_var_definition.rs @@ -61,9 +61,7 @@ pub struct PollScriptVar { } impl FromAstElementContent for PollScriptVar { - fn get_element_name() -> &'static str { - "defpoll" - } + const ELEMENT_NAME: &'static str = "defpoll"; fn from_tail>(span: Span, mut iter: AstIterator) -> AstResult { let result: AstResult<_> = try { @@ -91,9 +89,7 @@ pub struct ListenScriptVar { pub name_span: Span, } impl FromAstElementContent for ListenScriptVar { - fn get_element_name() -> &'static str { - "deflisten" - } + const ELEMENT_NAME: &'static str = "deflisten"; fn from_tail>(span: Span, mut iter: AstIterator) -> AstResult { let result: AstResult<_> = try { diff --git a/crates/yuck/src/config/var_definition.rs b/crates/yuck/src/config/var_definition.rs index 948d81a..77f6927 100644 --- a/crates/yuck/src/config/var_definition.rs +++ b/crates/yuck/src/config/var_definition.rs @@ -2,11 +2,14 @@ use std::collections::HashMap; use simplexpr::{dynval::DynVal, SimplExpr}; -use crate::{error::{AstResult, AstResultExt}, parser::{ +use crate::{ + error::{AstResult, AstResultExt}, + parser::{ ast::Ast, ast_iterator::AstIterator, from_ast::{FromAst, FromAstElementContent}, - }}; + }, +}; use eww_shared_util::{AttrName, Span, VarName}; #[derive(Debug, PartialEq, Eq, Clone, serde::Serialize)] @@ -17,9 +20,7 @@ pub struct VarDefinition { } impl FromAstElementContent for VarDefinition { - fn get_element_name() -> &'static str { - "defvar" - } + const ELEMENT_NAME: &'static str = "defvar"; fn from_tail>(span: Span, mut iter: AstIterator) -> AstResult { let result: AstResult<_> = try { diff --git a/crates/yuck/src/config/widget_definition.rs b/crates/yuck/src/config/widget_definition.rs index 02d6fdb..6dea6ee 100644 --- a/crates/yuck/src/config/widget_definition.rs +++ b/crates/yuck/src/config/widget_definition.rs @@ -23,9 +23,7 @@ pub struct WidgetDefinition { } impl FromAstElementContent for WidgetDefinition { - fn get_element_name() -> &'static str { - "defwidget" - } + const ELEMENT_NAME: &'static str = "defwidget"; fn from_tail>(span: Span, mut iter: AstIterator) -> AstResult { let (_, name) = iter.expect_symbol()?; diff --git a/crates/yuck/src/config/window_definition.rs b/crates/yuck/src/config/window_definition.rs index 58726ea..06b3578 100644 --- a/crates/yuck/src/config/window_definition.rs +++ b/crates/yuck/src/config/window_definition.rs @@ -27,9 +27,7 @@ pub struct WindowDefinition { } impl FromAstElementContent for WindowDefinition { - fn get_element_name() -> &'static str { - "defwindow" - } + const ELEMENT_NAME: &'static str = "defwindow"; fn from_tail>(span: Span, mut iter: AstIterator) -> AstResult { let (_, name) = iter.expect_symbol()?; diff --git a/crates/yuck/src/config/window_geometry.rs b/crates/yuck/src/config/window_geometry.rs index 49a5059..2d06eb4 100644 --- a/crates/yuck/src/config/window_geometry.rs +++ b/crates/yuck/src/config/window_geometry.rs @@ -117,9 +117,7 @@ pub struct WindowGeometry { } impl FromAstElementContent for WindowGeometry { - fn get_element_name() -> &'static str { - "geometry" - } + const ELEMENT_NAME: &'static str = "geometry"; fn from_tail>(span: Span, mut iter: AstIterator) -> AstResult { let mut attrs = iter.expect_key_values()?; diff --git a/crates/yuck/src/format_diagnostic.rs b/crates/yuck/src/format_diagnostic.rs index e7dbee0..c9b82dd 100644 --- a/crates/yuck/src/format_diagnostic.rs +++ b/crates/yuck/src/format_diagnostic.rs @@ -1,10 +1,12 @@ use codespan_reporting::{diagnostic, files}; +use config::TOP_LEVEL_DEFINITION_NAMES; +use itertools::Itertools; use simplexpr::dynval; use diagnostic::*; use crate::{ - config::{attributes::AttrError, validate::ValidationError}, + config::{attributes::AttrError, config, validate::ValidationError}, error::{get_parse_error_span, AstError}, }; @@ -75,12 +77,15 @@ impl ToDiagnostic for Diagnostic { impl ToDiagnostic for AstError { fn to_diagnostic(&self) -> Diagnostic { match self { - AstError::UnknownToplevel(span, name) => gen_diagnostic!(self, span), + AstError::UnknownToplevel(span, name) => gen_diagnostic! { + msg = self, + label = span, + note = format!("Must be one of: {}", TOP_LEVEL_DEFINITION_NAMES.iter().join(", ")) + }, AstError::MissingNode(span) => gen_diagnostic! { msg = "Expected another element", label = span => "Expected another element here", }, - AstError::WrongExprType(span, expected, actual) => gen_diagnostic! { msg = "Wrong type of expression", label = span => format!("Expected a `{}` here", expected), @@ -207,12 +212,18 @@ impl ToDiagnostic for simplexpr::eval::EvalError { use simplexpr::eval::EvalError::*; match self { NoVariablesAllowed(name) => gen_diagnostic!(self), - // TODO the note here is confusing when it's an unknown variable being used _within_ a string literal / simplexpr - // it only really makes sense on top-level symbols - UnknownVariable(name) => gen_diagnostic! { - msg = self, - note = format!("If you meant to use the literal value \"{}\", surround the value in quotes", name) - }, + UnknownVariable(name, similar) => { + let mut notes = Vec::new(); + if similar.len() == 1 { + notes.push(format!("Did you mean `{}`?", similar.first().unwrap())) + } else if similar.len() > 1 { + notes.push(format!("Did you mean one of: {}?", similar.iter().map(|x| format!("`{}`", x)).join(", "))) + } + // TODO the note here is confusing when it's an unknown variable being used _within_ a string literal / simplexpr + // it only really makes sense on top-level symbols + notes.push(format!("If you meant to use the literal value \"{}\", surround the value in quotes", name)); + gen_diagnostic!(self).with_notes(notes) + } Spanned(span, error) => error.as_ref().to_diagnostic().with_label(span_to_primary_label(*span)), _ => gen_diagnostic!(self, self.span()), } diff --git a/crates/yuck/src/parser/from_ast.rs b/crates/yuck/src/parser/from_ast.rs index 3aca5ea..09541ce 100644 --- a/crates/yuck/src/parser/from_ast.rs +++ b/crates/yuck/src/parser/from_ast.rs @@ -31,7 +31,7 @@ impl FromAst for String { /// A trait that allows creating a type from the tail of a list-node. /// I.e. to parse (foo [a b] (c d)), [from_tail] would just get [a b] (c d). pub trait FromAstElementContent: Sized { - fn get_element_name() -> &'static str; + const ELEMENT_NAME: &'static str; fn from_tail>(span: Span, iter: AstIterator) -> AstResult; } @@ -40,8 +40,8 @@ impl FromAst for T { let span = e.span(); let mut iter = e.try_ast_iter()?; let (element_name_span, element_name) = iter.expect_symbol()?; - if Self::get_element_name() != element_name { - return Err(AstError::MismatchedElementName(element_name_span, Self::get_element_name().to_string(), element_name)); + if Self::ELEMENT_NAME != element_name { + return Err(AstError::MismatchedElementName(element_name_span, Self::ELEMENT_NAME.to_string(), element_name)); } Self::from_tail(span, iter) } From 142894c3ce65fca7e823188aaad4a16b2b5c6dee Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Mon, 2 Aug 2021 16:35:43 +0200 Subject: [PATCH 102/137] Add more concrete error messages for missing argument list --- crates/eww/src/error_handling_ctx.rs | 1 + crates/yuck/src/config/widget_definition.rs | 13 +++++--- crates/yuck/src/error.rs | 33 ++++++++++++++++++++ crates/yuck/src/format_diagnostic.rs | 15 ++++++++- crates/yuck/src/parser/ast_iterator.rs | 34 +++++++++++---------- 5 files changed, 75 insertions(+), 21 deletions(-) diff --git a/crates/eww/src/error_handling_ctx.rs b/crates/eww/src/error_handling_ctx.rs index dd0d2fb..af05638 100644 --- a/crates/eww/src/error_handling_ctx.rs +++ b/crates/eww/src/error_handling_ctx.rs @@ -62,6 +62,7 @@ pub fn stringify_diagnostic(mut diagnostic: codespan_reporting::diagnostic::Diag let mut chars = Chars::box_drawing(); chars.single_primary_caret = '─'; config.chars = chars; + config.chars.note_bullet = '→'; let mut buf = Vec::new(); let mut writer = term::termcolor::Ansi::new(&mut buf); let files = YUCK_FILES.read().unwrap(); diff --git a/crates/yuck/src/config/widget_definition.rs b/crates/yuck/src/config/widget_definition.rs index 6dea6ee..4aaf41b 100644 --- a/crates/yuck/src/config/widget_definition.rs +++ b/crates/yuck/src/config/widget_definition.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use simplexpr::SimplExpr; use crate::{ - error::AstResult, + error::{AstError::WrongExprType, AstResult, AstResultExt, FormFormatError}, parser::{ ast::Ast, ast_iterator::AstIterator, @@ -26,11 +26,16 @@ impl FromAstElementContent for WidgetDefinition { const ELEMENT_NAME: &'static str = "defwidget"; fn from_tail>(span: Span, mut iter: AstIterator) -> AstResult { - let (_, name) = iter.expect_symbol()?; - let (args_span, expected_args) = iter.expect_array()?; + let (name_span, name) = iter.expect_symbol().note(EXPECTED_WIDGET_DEF_FORMAT)?; + let (args_span, expected_args) = iter + .expect_array() + .wrong_expr_type_to(|_| FormFormatError::WidgetDefArglistMissing(name_span.point_span_at_end())) + .note(EXPECTED_WIDGET_DEF_FORMAT)?; let expected_args = expected_args.into_iter().map(|x| x.as_symbol().map(AttrName)).collect::>()?; - let widget = iter.expect_any().and_then(WidgetUse::from_ast)?; + let widget = iter.expect_any().note(EXPECTED_WIDGET_DEF_FORMAT).and_then(WidgetUse::from_ast)?; iter.expect_done()?; Ok(Self { name, expected_args, widget, span, args_span }) } } + +static EXPECTED_WIDGET_DEF_FORMAT: &str = r#"Expected format: `(defwidget name [] (contained-widgets))`"#; diff --git a/crates/yuck/src/error.rs b/crates/yuck/src/error.rs index c12756c..76c9972 100644 --- a/crates/yuck/src/error.rs +++ b/crates/yuck/src/error.rs @@ -24,6 +24,9 @@ pub enum AstError { #[error("Did not expect any further elements here. Make sure your format is correct")] NoMoreElementsExpected(Span), + #[error(transparent)] + FormFormatError(#[from] FormFormatError), + #[error("Wrong type of expression: Expected {1} but got {2}")] WrongExprType(Span, AstType, AstType), #[error("Expected to get a value, but got {1}")] @@ -77,6 +80,14 @@ impl AstError { ) -> AstError { AstError::ParseError { file_id, source: err } } + + pub fn wrong_expr_type_to(self, f: impl FnOnce(Span) -> FormFormatError) -> AstError { + match self { + AstError::WrongExprType(span, ..) => AstError::FormFormatError(f(span.point_span())), + AstError::ErrorNote(s, err) => AstError::ErrorNote(s, Box::new(err.wrong_expr_type_to(f))), + other => other, + } + } } impl Spanned for AstError { @@ -98,6 +109,7 @@ impl Spanned for AstError { AstError::ErrorNote(_, err) => err.span(), AstError::NoMoreElementsExpected(span) => *span, AstError::SimplExpr(err) => err.span(), + AstError::FormFormatError(err) => err.span(), } } } @@ -125,6 +137,9 @@ impl OptionAstErrorExt for Option { pub trait AstResultExt { fn context_label(self, label_span: Span, context: &str) -> AstResult; fn note(self, note: &str) -> AstResult; + + /// Map any [AstError::WrongExprType]s error to a [FormFormatError] + fn wrong_expr_type_to(self, f: impl FnOnce(Span) -> FormFormatError) -> AstResult; } impl AstResultExt for AstResult { @@ -135,4 +150,22 @@ impl AstResultExt for AstResult { fn note(self, note: &str) -> AstResult { self.map_err(|e| e.note(note)) } + + fn wrong_expr_type_to(self, f: impl FnOnce(Span) -> FormFormatError) -> AstResult { + self.map_err(|err| err.wrong_expr_type_to(f)) + } +} + +#[derive(Debug, Error)] +pub enum FormFormatError { + #[error("Widget definition missing argument list")] + WidgetDefArglistMissing(Span), +} + +impl Spanned for FormFormatError { + fn span(&self) -> Span { + match self { + FormFormatError::WidgetDefArglistMissing(span) => *span, + } + } } diff --git a/crates/yuck/src/format_diagnostic.rs b/crates/yuck/src/format_diagnostic.rs index c9b82dd..58a192a 100644 --- a/crates/yuck/src/format_diagnostic.rs +++ b/crates/yuck/src/format_diagnostic.rs @@ -7,7 +7,7 @@ use diagnostic::*; use crate::{ config::{attributes::AttrError, config, validate::ValidationError}, - error::{get_parse_error_span, AstError}, + error::{get_parse_error_span, AstError, FormFormatError}, }; use super::parser::parse_error; @@ -124,6 +124,7 @@ impl ToDiagnostic for AstError { AstError::ValidationError(source) => source.to_diagnostic(), AstError::NoMoreElementsExpected(span) => gen_diagnostic!(self, span), AstError::SimplExpr(source) => source.to_diagnostic(), + AstError::FormFormatError(error) => error.to_diagnostic(), } } } @@ -246,3 +247,15 @@ fn generate_lexical_error_diagnostic(span: Span) -> Diagnostic { label = span => "Invalid token" } } + +impl ToDiagnostic for FormFormatError { + fn to_diagnostic(&self) -> diagnostic::Diagnostic { + match self { + FormFormatError::WidgetDefArglistMissing(span) => gen_diagnostic! { + msg = self, + label = span => "Insert the argument list (e.g.: `[]`) here", + note = "This list will in the future need to declare all the non-global variables / attributes used in this widget.\nThis is not yet neccessary, but is still considered good style.", + }, + } + } +} diff --git a/crates/yuck/src/parser/ast_iterator.rs b/crates/yuck/src/parser/ast_iterator.rs index d2db491..f1e9414 100644 --- a/crates/yuck/src/parser/ast_iterator.rs +++ b/crates/yuck/src/parser/ast_iterator.rs @@ -20,28 +20,30 @@ pub struct AstIterator> { } macro_rules! return_or_put_back { - ($name:ident, $expr_type:expr, $t:ty = $p:pat => $ret:expr) => { - pub fn $name(&mut self) -> AstResult<$t> { - let expr_type = $expr_type; - match self.expect_any()? { - $p => Ok($ret), - other => { - let span = other.span(); - let actual_type = other.expr_type(); - self.put_back(other); - Err(AstError::WrongExprType(span, expr_type, actual_type)) + ($(fn $name:ident -> $expr_type:expr, $t:ty = $p:pat => $ret:expr)*) => { + $( + pub fn $name(&mut self) -> AstResult<$t> { + let expr_type = $expr_type; + match self.expect_any()? { + $p => Ok($ret), + other => { + let span = other.span(); + let actual_type = other.expr_type(); + self.put_back(other); + Err(AstError::WrongExprType(span, expr_type, actual_type)) + } } } - } + )* }; } impl> AstIterator { - return_or_put_back!(expect_symbol, AstType::Symbol, (Span, String) = Ast::Symbol(span, x) => (span, x)); - - return_or_put_back!(expect_list, AstType::List, (Span, Vec) = Ast::List(span, x) => (span, x)); - - return_or_put_back!(expect_array, AstType::Array, (Span, Vec) = Ast::Array(span, x) => (span, x)); + return_or_put_back! { + fn expect_symbol -> AstType::Symbol, (Span, String) = Ast::Symbol(span, x) => (span, x) + fn expect_list -> AstType::List, (Span, Vec) = Ast::List(span, x) => (span, x) + fn expect_array -> AstType::Array, (Span, Vec) = Ast::Array(span, x) => (span, x) + } pub fn expect_literal(&mut self) -> AstResult<(Span, DynVal)> { // TODO add some others From ec8b12d20673b71fdedbc435403ef14f8c991c04 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Tue, 10 Aug 2021 14:30:33 +0200 Subject: [PATCH 103/137] Allow digits in identifiers in simplexpr --- crates/simplexpr/src/parser/lexer.rs | 4 +++- .../snapshots/simplexpr__parser__lexer__test__digit.snap | 6 ++++++ .../simplexpr__parser__lexer__test__number_in_ident.snap | 6 ++++++ crates/yuck/examples/errors.rs | 1 + crates/yuck/src/config/file_provider.rs | 2 +- 5 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__digit.snap create mode 100644 crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__number_in_ident.snap diff --git a/crates/simplexpr/src/parser/lexer.rs b/crates/simplexpr/src/parser/lexer.rs index 01b890f..4df1bde 100644 --- a/crates/simplexpr/src/parser/lexer.rs +++ b/crates/simplexpr/src/parser/lexer.rs @@ -99,7 +99,7 @@ regex_rules! { r"[ \n\n\f]+" => |_| Token::Skip, r";.*"=> |_| Token::Comment, - r"[a-zA-Z_-]+" => |x| Token::Ident(x.to_string()), + r"[a-zA-Z_][a-zA-Z0-9_-]*" => |x| Token::Ident(x.to_string()), r"[+-]?(?:[0-9]+[.])?[0-9]+" => |x| Token::NumLit(x.to_string()) } @@ -278,6 +278,8 @@ mod test { snapshot_string! { basic => v!(r#"bar "foo""#), + digit => v!(r#"12"#), + number_in_ident => v!(r#"foo_1_bar"#), interpolation_1 => v!(r#" "foo ${2 * 2} bar" "#), interpolation_nested => v!(r#" "foo ${(2 * 2) + "${5 + 5}"} bar" "#), escaping => v!(r#" "a\"b\{}" "#), diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__digit.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__digit.snap new file mode 100644 index 0000000..d57d778 --- /dev/null +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__digit.snap @@ -0,0 +1,6 @@ +--- +source: crates/simplexpr/src/parser/lexer.rs +expression: "v!(r#\"12\"#)" + +--- +(0, NumLit("12"), 2) diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__number_in_ident.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__number_in_ident.snap new file mode 100644 index 0000000..3b7104f --- /dev/null +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__number_in_ident.snap @@ -0,0 +1,6 @@ +--- +source: crates/simplexpr/src/parser/lexer.rs +expression: "v!(r#\"foo_1_bar\"#)" + +--- +(0, Ident("foo_1_bar"), 9) diff --git a/crates/yuck/examples/errors.rs b/crates/yuck/examples/errors.rs index a5b26ba..cd14f8a 100644 --- a/crates/yuck/examples/errors.rs +++ b/crates/yuck/examples/errors.rs @@ -4,6 +4,7 @@ //}; fn main() { + println!("hi"); // let mut files = codespan_reporting::files::SimpleFiles::new(); // let input = r#" diff --git a/crates/yuck/src/config/file_provider.rs b/crates/yuck/src/config/file_provider.rs index 52581ff..2db1e68 100644 --- a/crates/yuck/src/config/file_provider.rs +++ b/crates/yuck/src/config/file_provider.rs @@ -69,7 +69,7 @@ impl YuckFiles { } impl YuckFiles { - fn get_file(&self, id: usize) -> Result<&YuckFile, codespan_reporting::files::Error> { + pub fn get_file(&self, id: usize) -> Result<&YuckFile, codespan_reporting::files::Error> { self.files.get(&id).ok_or(codespan_reporting::files::Error::FileMissing) } From dcf27a0cbf475ec31a30e9c2edb9b6c171face0c Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Tue, 10 Aug 2021 19:23:52 +0200 Subject: [PATCH 104/137] Some misc fixes and improvements --- crates/eww/src/config/script_var.rs | 2 +- crates/eww/src/script_var_handler.rs | 4 ++-- crates/eww/src/widgets/widget_definitions.rs | 13 ++++++------- crates/yuck/src/config/script_var_definition.rs | 7 +++++-- crates/yuck/src/parser/lexer.rs | 1 - 5 files changed, 14 insertions(+), 13 deletions(-) diff --git a/crates/eww/src/config/script_var.rs b/crates/eww/src/config/script_var.rs index 46db670..da44b43 100644 --- a/crates/eww/src/config/script_var.rs +++ b/crates/eww/src/config/script_var.rs @@ -30,7 +30,7 @@ pub fn initial_value(var: &ScriptVarDefinition) -> Result { run_command(command).map_err(|e| anyhow!(create_script_var_failed_warn(*span, var.name(), &e.to_string()))) } }, - ScriptVarDefinition::Listen(_) => Ok(DynVal::from_string(String::new())), + ScriptVarDefinition::Listen(var) => Ok(var.initial_value.clone()), } } diff --git a/crates/eww/src/script_var_handler.rs b/crates/eww/src/script_var_handler.rs index 8fc2202..2ebfc31 100644 --- a/crates/eww/src/script_var_handler.rs +++ b/crates/eww/src/script_var_handler.rs @@ -196,13 +196,13 @@ impl ListenVarHandler { } async fn start(&mut self, var: ListenScriptVar) { - log::debug!("starting poll var {}", &var.name); + log::debug!("starting listen-var {}", &var.name); let cancellation_token = CancellationToken::new(); self.listen_process_handles.insert(var.name.clone(), cancellation_token.clone()); let evt_send = self.evt_send.clone(); tokio::spawn(async move { - crate::try_logging_errors!(format!("Executing tail var command {}", &var.command) => { + crate::try_logging_errors!(format!("Executing listen var-command {}", &var.command) => { let mut handle = tokio::process::Command::new("sh") .args(&["-c", &var.command]) .stdout(std::process::Stdio::piped()) diff --git a/crates/eww/src/widgets/widget_definitions.rs b/crates/eww/src/widgets/widget_definitions.rs index b67bd04..dd3d9b4 100644 --- a/crates/eww/src/widgets/widget_definitions.rs +++ b/crates/eww/src/widgets/widget_definitions.rs @@ -83,6 +83,10 @@ pub(super) fn resolve_widget_attrs(bargs: &mut BuilderArgs, gtk_widget: >k::Wi prop(valign: as_string) { gtk_widget.set_valign(parse_align(&valign)?) }, // @prop halign - how to align this horizontally. possible values: $alignment prop(halign: as_string) { gtk_widget.set_halign(parse_align(&halign)?) }, + // @prop vexpand - should this container expand vertically. Default: false. + prop(vexpand: as_bool = false) { gtk_widget.set_vexpand(vexpand) }, + // @prop hexpand - should this widget expand horizontally. Default: false. + prop(hexpand: as_bool = false) { gtk_widget.set_hexpand(hexpand) }, // @prop width - width of this element. note that this can not restrict the size if the contents stretch it prop(width: as_f64) { gtk_widget.set_size_request(width as i32, gtk_widget.get_allocated_height()) }, // @prop height - height of this element. note that this can not restrict the size if the contents stretch it @@ -158,13 +162,8 @@ pub(super) fn resolve_widget_attrs(bargs: &mut BuilderArgs, gtk_widget: >k::Wi } /// @widget !container -pub(super) fn resolve_container_attrs(bargs: &mut BuilderArgs, gtk_widget: >k::Container) { - resolve_block!(bargs, gtk_widget, { - // @prop vexpand - should this container expand vertically - prop(vexpand: as_bool = false) { gtk_widget.set_vexpand(vexpand) }, - // @prop hexpand - should this container expand horizontally - prop(hexpand: as_bool = false) { gtk_widget.set_hexpand(hexpand) }, - }); +pub(super) fn resolve_container_attrs(_bargs: &mut BuilderArgs, _gtk_widget: >k::Container) { + // resolve_block!(bargs, gtk_widget, {}); } /// @widget !range diff --git a/crates/yuck/src/config/script_var_definition.rs b/crates/yuck/src/config/script_var_definition.rs index bc48e51..79b4fb8 100644 --- a/crates/yuck/src/config/script_var_definition.rs +++ b/crates/yuck/src/config/script_var_definition.rs @@ -85,6 +85,7 @@ impl FromAstElementContent for PollScriptVar { pub struct ListenScriptVar { pub name: VarName, pub command: String, + pub initial_value: DynVal, pub command_span: Span, pub name_span: Span, } @@ -94,10 +95,12 @@ impl FromAstElementContent for ListenScriptVar { fn from_tail>(span: Span, mut iter: AstIterator) -> AstResult { let result: AstResult<_> = try { let (name_span, name) = iter.expect_symbol()?; + let mut attrs = iter.expect_key_values()?; + let initial_value = attrs.primitive_optional("initial")?.unwrap_or_else(|| DynVal::from_string(String::new())); let (command_span, script) = iter.expect_literal()?; iter.expect_done()?; - Self { name_span, name: VarName(name), command: script.to_string(), command_span } + Self { name_span, name: VarName(name), command: script.to_string(), initial_value, command_span } }; - result.note(r#"Expected format: `(deflisten name "tail -f /tmp/example")`"#) + result.note(r#"Expected format: `(deflisten name :initial "0" "tail -f /tmp/example")`"#) } } diff --git a/crates/yuck/src/parser/lexer.rs b/crates/yuck/src/parser/lexer.rs index edfe6e5..8068c7f 100644 --- a/crates/yuck/src/parser/lexer.rs +++ b/crates/yuck/src/parser/lexer.rs @@ -107,7 +107,6 @@ impl Lexer { toks.push((lo, tok, hi)); } Some(Err(err)) => { - dbg!(&simplexpr_lexer); if simplexpr_lexer.continues_with('}') { let start = toks.first().map(|x| x.0).unwrap_or(end); self.pos = end + 1; From 9ee852c0fd7c6e4510cd3954c07bd09cf325f71a Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Thu, 12 Aug 2021 16:19:00 +0200 Subject: [PATCH 105/137] Add centerbox widget --- crates/eww/src/app.rs | 5 ++- crates/eww/src/error_handling_ctx.rs | 6 +-- crates/eww/src/widgets/mod.rs | 24 ++++++------ crates/eww/src/widgets/widget_definitions.rs | 40 +++++++++++++++++++- crates/eww/src/widgets/widget_node.rs | 2 +- 5 files changed, 59 insertions(+), 18 deletions(-) diff --git a/crates/eww/src/app.rs b/crates/eww/src/app.rs index 4158f6c..18076a9 100644 --- a/crates/eww/src/app.rs +++ b/crates/eww/src/app.rs @@ -1,4 +1,7 @@ -use crate::{EwwPaths, config, daemon_response::DaemonResponseSender, display_backend, error_handling_ctx, eww_state, script_var_handler::*}; +use crate::{ + config, daemon_response::DaemonResponseSender, display_backend, error_handling_ctx, eww_state, script_var_handler::*, + EwwPaths, +}; use anyhow::*; use debug_stub_derive::*; use eww_shared_util::VarName; diff --git a/crates/eww/src/error_handling_ctx.rs b/crates/eww/src/error_handling_ctx.rs index af05638..dc052cf 100644 --- a/crates/eww/src/error_handling_ctx.rs +++ b/crates/eww/src/error_handling_ctx.rs @@ -35,10 +35,8 @@ pub fn format_error(err: &anyhow::Error) -> String { for err in err.chain() { format!("chain: {}", err); } - match err.downcast_ref::() { - Some(err) => stringify_diagnostic(err.to_diagnostic()).unwrap_or_else(|_| format!("{:?}", err)), - None => format!("{:?}", err), - } + let diag = anyhow_err_to_diagnostic(err); + stringify_diagnostic(diag).unwrap_or_else(|_| format!("{}", err)) } pub fn anyhow_err_to_diagnostic(err: &anyhow::Error) -> Diagnostic { diff --git a/crates/eww/src/widgets/mod.rs b/crates/eww/src/widgets/mod.rs index 6d83bb2..61d084c 100644 --- a/crates/eww/src/widgets/mod.rs +++ b/crates/eww/src/widgets/mod.rs @@ -70,17 +70,19 @@ fn build_builtin_gtk_widget( if let Some(gtk_widget) = gtk_widget.dynamic_cast_ref::() { resolve_container_attrs(&mut bargs, gtk_widget); - for child in &widget.children { - let child_widget = child.render(bargs.eww_state, window_name, widget_definitions).with_context(|| { - format!( - "{}error while building child '{:#?}' of '{}'", - format!("{} | ", widget.span), - &child, - >k_widget.get_widget_name() - ) - })?; - gtk_widget.add(&child_widget); - child_widget.show(); + if gtk_widget.get_children().is_empty() { + for child in &widget.children { + let child_widget = child.render(bargs.eww_state, window_name, widget_definitions).with_context(|| { + format!( + "{}error while building child '{:#?}' of '{}'", + format!("{} | ", widget.span), + &child, + >k_widget.get_widget_name() + ) + })?; + gtk_widget.add(&child_widget); + child_widget.show(); + } } } diff --git a/crates/eww/src/widgets/widget_definitions.rs b/crates/eww/src/widgets/widget_definitions.rs index dd3d9b4..2bc4c5f 100644 --- a/crates/eww/src/widgets/widget_definitions.rs +++ b/crates/eww/src/widgets/widget_definitions.rs @@ -1,14 +1,18 @@ #![allow(clippy::option_map_unit_fn)] use super::{run_command, BuilderArgs}; -use crate::{enum_parse, error_handling_ctx, eww_state, resolve_block, util::list_difference, widgets::widget_node}; +use crate::{ + enum_parse, error::DiagError, error_handling_ctx, eww_state, resolve_block, util::list_difference, widgets::widget_node, +}; use anyhow::*; use gdk::WindowExt; use glib; use gtk::{self, prelude::*, ImageExt}; +use itertools::Itertools; use std::{cell::RefCell, collections::HashMap, rc::Rc, time::Duration}; use yuck::{ config::validate::ValidationError, error::{AstError, AstResult, AstResultExt}, + gen_diagnostic, parser::from_ast::FromAst, }; @@ -20,6 +24,7 @@ use yuck::{ pub(super) fn widget_to_gtk_widget(bargs: &mut BuilderArgs) -> Result { let gtk_widget = match bargs.widget.name.as_str() { "box" => build_gtk_box(bargs)?.upcast(), + "centerbox" => build_center_box(bargs)?.upcast(), "scale" => build_gtk_scale(bargs)?.upcast(), "progress" => build_gtk_progress(bargs)?.upcast(), "image" => build_gtk_image(bargs)?.upcast(), @@ -494,6 +499,39 @@ fn build_gtk_box(bargs: &mut BuilderArgs) -> Result { Ok(gtk_widget) } +/// @widget centerbox extends container +/// @desc a box that must contain exactly three children, which will be layed out at the start, center and end of the container. +fn build_center_box(bargs: &mut BuilderArgs) -> Result { + let gtk_widget = gtk::Box::new(gtk::Orientation::Horizontal, 0); + resolve_block!(bargs, gtk_widget, { + // @prop orientation - orientation of the centerbox. possible values: $orientation + prop(orientation: as_string) { gtk_widget.set_orientation(parse_orientation(&orientation)?) }, + }); + + if bargs.widget.children.len() < 3 { + Err(DiagError::new(gen_diagnostic!("centerbox must contain exactly 3 elements", bargs.widget.span)))? + } else if bargs.widget.children.len() > 3 { + let (_, additional_children) = bargs.widget.children.split_at(3); + // we know that there is more than three children, so unwrapping on first and left here is fine. + let first_span = additional_children.first().unwrap().span(); + let last_span = additional_children.last().unwrap().span(); + Err(DiagError::new(gen_diagnostic!("centerbox must contain exactly 3 elements, but got more", first_span.to(last_span))))? + } + + let mut children = + bargs.widget.children.iter().map(|child| child.render(bargs.eww_state, bargs.window_name, bargs.widget_definitions)); + // we know that we have exactly three children here, so we can unwrap here. + let (first, center, end) = children.next_tuple().unwrap(); + let (first, center, end) = (first?, center?, end?); + gtk_widget.pack_start(&first, true, true, 0); + gtk_widget.set_center_widget(Some(¢er)); + gtk_widget.pack_end(&end, true, true, 0); + first.show(); + center.show(); + end.show(); + Ok(gtk_widget) +} + /// @widget label /// @desc A text widget giving you more control over how the text is displayed fn build_gtk_label(bargs: &mut BuilderArgs) -> Result { diff --git a/crates/eww/src/widgets/widget_node.rs b/crates/eww/src/widgets/widget_node.rs index 0bfc959..08740d5 100644 --- a/crates/eww/src/widgets/widget_node.rs +++ b/crates/eww/src/widgets/widget_node.rs @@ -9,7 +9,7 @@ use yuck::{ error::{AstError, AstResult}, }; -pub trait WidgetNode: std::fmt::Debug + dyn_clone::DynClone + Send + Sync { +pub trait WidgetNode: Spanned + std::fmt::Debug + dyn_clone::DynClone + Send + Sync { fn get_name(&self) -> &str; /// Generate a [gtk::Widget] from a [element::WidgetUse]. From 7bb5fddaa61eaa60961a276480338999c2c65aeb Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Thu, 12 Aug 2021 16:39:10 +0200 Subject: [PATCH 106/137] Fix windows closing eventhough they don't work --- crates/eww/src/app.rs | 14 ++++++++------ crates/eww/src/main.rs | 3 +++ crates/eww/src/server.rs | 1 + 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/crates/eww/src/app.rs b/crates/eww/src/app.rs index 18076a9..af34c15 100644 --- a/crates/eww/src/app.rs +++ b/crates/eww/src/app.rs @@ -192,7 +192,7 @@ impl App { fn close_window(&mut self, window_name: &String) -> Result<()> { for unused_var in self.variables_only_used_in(window_name) { - log::info!("stopping for {}", &unused_var); + log::debug!("stopping for {}", &unused_var); self.script_var_handler.stop_for_variable(unused_var.clone()); } @@ -213,8 +213,6 @@ impl App { monitor: Option, anchor: Option, ) -> Result<()> { - // remove and close existing window with the same name - let _ = self.close_window(window_name); log::info!("Opening window {}", window_name); let mut window_def = self.eww_config.get_window(window_name)?.clone(); @@ -222,6 +220,11 @@ impl App { let root_widget = window_def.widget.render(&mut self.eww_state, window_name, &self.eww_config.get_widget_definitions())?; + + // once generating the root widget has succeeded + // remove and close existing window with the same name + let _ = self.close_window(window_name); + root_widget.get_style_context().add_class(&window_name.to_string()); let monitor_geometry = @@ -240,7 +243,7 @@ impl App { Ok(()) } - /// Load the given configuration, reloading all script-vars and reopening all windows that where opened. + /// Load the given configuration, reloading all script-vars and attempting to reopen all windows that where opened. pub fn load_config(&mut self, config: config::EwwConfig) -> Result<()> { log::info!("Reloading windows"); // refresh script-var poll stuff @@ -250,8 +253,7 @@ impl App { self.eww_state.clear_all_window_states(); let windows = self.open_windows.clone(); - for (window_name, window) in windows { - window.close(); + for (window_name, _) in windows { self.open_window(&window_name, None, None, None, None)?; } Ok(()) diff --git a/crates/eww/src/main.rs b/crates/eww/src/main.rs index ed7d758..edf377c 100644 --- a/crates/eww/src/main.rs +++ b/crates/eww/src/main.rs @@ -67,7 +67,9 @@ fn main() { handle_server_command(&paths, &ActionWithServer::KillServer, 1)?; false } + // a running daemon is necessary for this command opts::Action::WithServer(action) => { + // attempt to just send the command to a running daemon if let Err(err) = handle_server_command(&paths, &action, 5) { // connecting to the daemon failed. Thus, start the daemon here! log::warn!("Failed to connect to daemon: {}", err); @@ -78,6 +80,7 @@ fn main() { } let (command, response_recv) = action.into_daemon_command(); + // start the daemon and give it the command let fork_result = server::initialize_server(paths.clone(), Some(command))?; let is_parent = fork_result == ForkResult::Parent; if let (Some(recv), true) = (response_recv, is_parent) { diff --git a/crates/eww/src/server.rs b/crates/eww/src/server.rs index dbcae9f..2572004 100644 --- a/crates/eww/src/server.rs +++ b/crates/eww/src/server.rs @@ -76,6 +76,7 @@ pub fn initialize_server(paths: EwwPaths, action: Option) -> Resu init_async_part(app.paths.clone(), ui_send); glib::MainContext::default().spawn_local(async move { + // if an action was given to the daemon initially, execute it first. if let Some(action) = action { app.handle_command(action); } From eb11c29e6a478fc5d18d03396d8913de65588e6e Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Thu, 12 Aug 2021 18:17:17 +0200 Subject: [PATCH 107/137] fix some hot-reloading issues --- crates/eww/src/app.rs | 24 +++++++++++++++--------- crates/eww/src/opts.rs | 8 ++++---- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/crates/eww/src/app.rs b/crates/eww/src/app.rs index af34c15..579a0c5 100644 --- a/crates/eww/src/app.rs +++ b/crates/eww/src/app.rs @@ -1,7 +1,4 @@ -use crate::{ - config, daemon_response::DaemonResponseSender, display_backend, error_handling_ctx, eww_state, script_var_handler::*, - EwwPaths, -}; +use crate::{EwwPaths, config, daemon_response::DaemonResponseSender, display_backend, error_handling_ctx, eww_state::{self, EwwState}, script_var_handler::*}; use anyhow::*; use debug_stub_derive::*; use eww_shared_util::VarName; @@ -32,7 +29,7 @@ pub enum DaemonCommand { pos: Option, size: Option, anchor: Option, - monitor: Option, + screen: Option, should_toggle: bool, sender: DaemonResponseSender, }, @@ -133,7 +130,7 @@ impl App { let result = windows.iter().try_for_each(|w| self.open_window(w, None, None, None, None)); respond_with_error(sender, result)?; } - DaemonCommand::OpenWindow { window_name, pos, size, anchor, monitor, should_toggle, sender } => { + DaemonCommand::OpenWindow { window_name, pos, size, anchor, screen: monitor, should_toggle, sender } => { let result = if should_toggle && self.open_windows.contains_key(&window_name) { self.close_window(&window_name) } else { @@ -196,8 +193,10 @@ impl App { self.script_var_handler.stop_for_variable(unused_var.clone()); } - let window = - self.open_windows.remove(window_name).context(format!("No window with name '{}' is running.", window_name))?; + let window = self + .open_windows + .remove(window_name) + .with_context(|| format!("Tried to close window named '{}', but no such window was open", window_name))?; window.close(); self.eww_state.clear_window_state(window_name); @@ -250,7 +249,14 @@ impl App { self.script_var_handler.stop_all(); self.eww_config = config; - self.eww_state.clear_all_window_states(); + + let new_state = EwwState::from_default_vars(self.eww_config.generate_initial_state()?); + let old_state = std::mem::replace(&mut self.eww_state, new_state); + for (key, value) in old_state.get_variables() { + if self.eww_state.get_variables().contains_key(key) { + self.eww_state.update_variable(key.clone(), value.clone()) + } + } let windows = self.open_windows.clone(); for (window_name, _) in windows { diff --git a/crates/eww/src/opts.rs b/crates/eww/src/opts.rs index 01550a3..636d660 100644 --- a/crates/eww/src/opts.rs +++ b/crates/eww/src/opts.rs @@ -78,8 +78,8 @@ pub enum ActionWithServer { window_name: String, /// Monitor-index the window should open on - #[structopt(short, long)] - monitor: Option, + #[structopt(long)] + screen: Option, /// The position of the window, where it should open. #[structopt(short, long)] @@ -175,13 +175,13 @@ impl ActionWithServer { ActionWithServer::OpenMany { windows } => { return with_response_channel(|sender| app::DaemonCommand::OpenMany { windows, sender }); } - ActionWithServer::OpenWindow { window_name, pos, size, monitor, anchor, should_toggle } => { + ActionWithServer::OpenWindow { window_name, pos, size, screen, anchor, should_toggle } => { return with_response_channel(|sender| app::DaemonCommand::OpenWindow { window_name, pos, size, anchor, - monitor, + screen, should_toggle, sender, }) From 7647fcd2c4adedec85a0be938fe9027c4254ce8d Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Fri, 13 Aug 2021 16:48:49 +0200 Subject: [PATCH 108/137] Undo eww-state hotreloading change it caused variables not to be linked to the state after the reload --- crates/eww/src/app.rs | 86 +++++++++++++++++------------ crates/eww/src/config/eww_config.rs | 2 +- crates/eww/src/server.rs | 8 +-- 3 files changed, 53 insertions(+), 43 deletions(-) diff --git a/crates/eww/src/app.rs b/crates/eww/src/app.rs index 579a0c5..d9da63f 100644 --- a/crates/eww/src/app.rs +++ b/crates/eww/src/app.rs @@ -1,4 +1,11 @@ -use crate::{EwwPaths, config, daemon_response::DaemonResponseSender, display_backend, error_handling_ctx, eww_state::{self, EwwState}, script_var_handler::*}; +use crate::{ + config, + daemon_response::DaemonResponseSender, + display_backend, error_handling_ctx, + eww_state::{self}, + script_var_handler::*, + EwwPaths, +}; use anyhow::*; use debug_stub_derive::*; use eww_shared_util::VarName; @@ -6,7 +13,7 @@ use gdk::WindowExt; use gtk::{ContainerExt, CssProviderExt, GtkWindowExt, StyleContextExt, WidgetExt}; use itertools::Itertools; use simplexpr::dynval::DynVal; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use tokio::sync::mpsc::UnboundedSender; use yuck::{ config::window_geometry::{AnchorPoint, WindowGeometry}, @@ -65,6 +72,9 @@ pub struct App { pub eww_state: eww_state::EwwState, pub eww_config: config::EwwConfig, pub open_windows: HashMap, + /// Window names that are supposed to be open, but failed. + /// When reloading the config, these should be opened again. + pub failed_windows: HashSet, pub css_provider: gtk::CssProvider, #[debug_stub = "ScriptVarHandler(...)"] @@ -179,7 +189,9 @@ impl App { fn stop_application(&mut self) { self.script_var_handler.stop_all(); - self.open_windows.drain().for_each(|(_, w)| w.close()); + for (_, window) in self.open_windows.drain() { + window.close(); + } gtk::main_quit(); } @@ -193,12 +205,10 @@ impl App { self.script_var_handler.stop_for_variable(unused_var.clone()); } - let window = self - .open_windows + self.open_windows .remove(window_name) .with_context(|| format!("Tried to close window named '{}', but no such window was open", window_name))?; - window.close(); self.eww_state.clear_window_state(window_name); Ok(()) @@ -212,54 +222,58 @@ impl App { monitor: Option, anchor: Option, ) -> Result<()> { + self.failed_windows.remove(window_name); log::info!("Opening window {}", window_name); - let mut window_def = self.eww_config.get_window(window_name)?.clone(); - window_def.geometry = window_def.geometry.map(|x| x.override_if_given(anchor, pos, size)); - - let root_widget = - window_def.widget.render(&mut self.eww_state, window_name, &self.eww_config.get_widget_definitions())?; - - // once generating the root widget has succeeded - // remove and close existing window with the same name + // if an instance of this is already running, close it let _ = self.close_window(window_name); - root_widget.get_style_context().add_class(&window_name.to_string()); + let open_result: Result<_> = try { + let mut window_def = self.eww_config.get_window(window_name)?.clone(); + window_def.geometry = window_def.geometry.map(|x| x.override_if_given(anchor, pos, size)); - let monitor_geometry = - get_monitor_geometry(monitor.or(window_def.monitor_number).unwrap_or_else(get_default_monitor_index)); - let eww_window = initialize_window(monitor_geometry, root_widget, window_def)?; + let root_widget = + window_def.widget.render(&mut self.eww_state, window_name, &self.eww_config.get_widget_definitions())?; - self.open_windows.insert(window_name.clone(), eww_window); + root_widget.get_style_context().add_class(&window_name.to_string()); - // initialize script var handlers for variables that where not used before opening this window. - // TODO somehow make this less shit - for newly_used_var in self.variables_only_used_in(window_name).filter_map(|var| self.eww_config.get_script_var(var).ok()) - { - self.script_var_handler.add(newly_used_var.clone()); + let monitor_geometry = + get_monitor_geometry(monitor.or(window_def.monitor_number).unwrap_or_else(get_default_monitor_index)); + let eww_window = initialize_window(monitor_geometry, root_widget, window_def)?; + + self.open_windows.insert(window_name.clone(), eww_window); + + // initialize script var handlers for variables that where not used before opening this window. + // TODO somehow make this less shit + for newly_used_var in + self.variables_only_used_in(window_name).filter_map(|var| self.eww_config.get_script_var(var).ok()) + { + self.script_var_handler.add(newly_used_var.clone()); + } + }; + + if let Err(err) = open_result { + self.failed_windows.insert(window_name.to_string()); + Err(err) + } else { + Ok(()) } - - Ok(()) } /// Load the given configuration, reloading all script-vars and attempting to reopen all windows that where opened. pub fn load_config(&mut self, config: config::EwwConfig) -> Result<()> { + // TODO the reload procedure is kinda bad. + // It should probably instead prepare a new eww_state and everything, and then swap the instances once everything has worked. + log::info!("Reloading windows"); // refresh script-var poll stuff self.script_var_handler.stop_all(); self.eww_config = config; + self.eww_state.clear_all_window_states(); - let new_state = EwwState::from_default_vars(self.eww_config.generate_initial_state()?); - let old_state = std::mem::replace(&mut self.eww_state, new_state); - for (key, value) in old_state.get_variables() { - if self.eww_state.get_variables().contains_key(key) { - self.eww_state.update_variable(key.clone(), value.clone()) - } - } - - let windows = self.open_windows.clone(); - for (window_name, _) in windows { + let window_names: Vec = self.open_windows.keys().chain(self.failed_windows.iter()).cloned().collect(); + for window_name in &window_names { self.open_window(&window_name, None, None, None, None)?; } Ok(()) diff --git a/crates/eww/src/config/eww_config.rs b/crates/eww/src/config/eww_config.rs index 722f019..ac8e559 100644 --- a/crates/eww/src/config/eww_config.rs +++ b/crates/eww/src/config/eww_config.rs @@ -67,7 +67,7 @@ impl EwwConfig { } pub fn get_window(&self, name: &String) -> Result<&EwwWindowDefinition> { - self.windows.get(name).with_context(|| format!("No window named '{}' exists", name)) + self.windows.get(name).with_context(|| format!("No window named '{}' exists in config", name)) } pub fn get_script_var(&self, name: &VarName) -> Result<&ScriptVarDefinition> { diff --git a/crates/eww/src/server.rs b/crates/eww/src/server.rs index 2572004..c4c02d1 100644 --- a/crates/eww/src/server.rs +++ b/crates/eww/src/server.rs @@ -1,12 +1,7 @@ use crate::{EwwPaths, app::{self, DaemonCommand}, config, daemon_response, error_handling_ctx, eww_state::*, ipc_server, script_var_handler, util}; use anyhow::*; -use std::{ - collections::HashMap, - os::unix::io::AsRawFd, - path::Path, - sync::{atomic::Ordering, Arc}, -}; +use std::{collections::{HashMap, HashSet}, os::unix::io::AsRawFd, path::Path, sync::{atomic::Ordering, Arc}}; use tokio::sync::mpsc::*; pub fn initialize_server(paths: EwwPaths, action: Option) -> Result { @@ -58,6 +53,7 @@ pub fn initialize_server(paths: EwwPaths, action: Option) -> Resu eww_state: EwwState::from_default_vars(eww_config.generate_initial_state()?), eww_config, open_windows: HashMap::new(), + failed_windows: HashSet::new(), css_provider: gtk::CssProvider::new(), script_var_handler, app_evt_send: ui_send.clone(), From 279785f4200a8697d03b94d6277ba020d3751ea3 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Sat, 14 Aug 2021 11:28:15 +0200 Subject: [PATCH 109/137] Log errors in log even when a response-sender is listening, add error message when a monitor doesn't exist --- crates/eww/src/app.rs | 34 ++++++++++++++-------------- crates/eww/src/error_handling_ctx.rs | 11 ++++++++- 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/crates/eww/src/app.rs b/crates/eww/src/app.rs index d9da63f..ee9b331 100644 --- a/crates/eww/src/app.rs +++ b/crates/eww/src/app.rs @@ -1,9 +1,5 @@ use crate::{ - config, - daemon_response::DaemonResponseSender, - display_backend, error_handling_ctx, - eww_state::{self}, - script_var_handler::*, + config, daemon_response::DaemonResponseSender, display_backend, error_handling_ctx, eww_state, script_var_handler::*, EwwPaths, }; use anyhow::*; @@ -237,8 +233,8 @@ impl App { root_widget.get_style_context().add_class(&window_name.to_string()); - let monitor_geometry = - get_monitor_geometry(monitor.or(window_def.monitor_number).unwrap_or_else(get_default_monitor_index)); + let monitor_geometry = get_monitor_geometry(monitor.or(window_def.monitor_number))?; + let eww_window = initialize_window(monitor_geometry, root_widget, window_def)?; self.open_windows.insert(window_name.clone(), eww_window); @@ -254,7 +250,7 @@ impl App { if let Err(err) = open_result { self.failed_windows.insert(window_name.to_string()); - Err(err) + Err(err).with_context(|| format!("failed to open window `{}`", window_name)) } else { Ok(()) } @@ -373,22 +369,26 @@ fn on_screen_changed(window: >k::Window, _old_screen: Option<&gdk::Screen>) { window.set_visual(visual.as_ref()); } -fn get_default_monitor_index() -> i32 { +/// Get the monitor geometry of a given monitor number, or the default if none is given +fn get_monitor_geometry(n: Option) -> Result { #[allow(deprecated)] - gdk::Display::get_default().expect("could not get default display").get_default_screen().get_primary_monitor() -} - -/// Get the monitor geometry of a given monitor number -fn get_monitor_geometry(n: i32) -> gdk::Rectangle { - #[allow(deprecated)] - gdk::Display::get_default().expect("could not get default display").get_default_screen().get_monitor_geometry(n) + let display = gdk::Display::get_default().expect("could not get default display"); + let monitor = match n { + Some(n) => display.get_monitor(n).with_context(|| format!("Failed to get monitor with index {}", n))?, + None => display.get_primary_monitor().context("Failed to get primary monitor from GTK")?, + }; + Ok(monitor.get_geometry()) } /// In case of an Err, send the error message to a sender. fn respond_with_error(sender: DaemonResponseSender, result: Result) -> Result<()> { match result { Ok(_) => sender.send_success(String::new()), - Err(e) => sender.send_failure(error_handling_ctx::format_error(&e)), + Err(e) => { + let formatted = error_handling_ctx::format_error(&e); + println!("{}", formatted); + sender.send_failure(formatted) + }, } .context("sending response from main thread") } diff --git a/crates/eww/src/error_handling_ctx.rs b/crates/eww/src/error_handling_ctx.rs index dc052cf..130e949 100644 --- a/crates/eww/src/error_handling_ctx.rs +++ b/crates/eww/src/error_handling_ctx.rs @@ -49,10 +49,19 @@ pub fn anyhow_err_to_diagnostic(err: &anyhow::Error) -> Diagnostic { } else if let Some(err) = err.downcast_ref::() { err.to_diagnostic() } else { - gen_diagnostic!(err) + gen_diagnostic!(format!("{:?}", err)) } } +// pub fn print_diagnostic(diagnostic: codespan_reporting::diagnostic::Diagnostic) { +// match stringify_diagnostic(diagnostic.clone()) { +// Ok(diag) => { +// eprintln!("{}", diag); +//} +// Err(_) => { +// log::error!("{:?}", diagnostic); +//} + pub fn stringify_diagnostic(mut diagnostic: codespan_reporting::diagnostic::Diagnostic) -> anyhow::Result { diagnostic.labels.drain_filter(|label| Span(label.range.start, label.range.end, label.file_id).is_dummy()); From c0581e7769fecaad0eb4bdbccc2c5901a21dba19 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Sat, 14 Aug 2021 11:36:39 +0200 Subject: [PATCH 110/137] Improve error messages for multiple chidren in a widget and config loading errors --- crates/eww/src/config/eww_config.rs | 8 +++++++- crates/yuck/src/config/widget_definition.rs | 4 ++-- crates/yuck/src/error.rs | 4 ++++ crates/yuck/src/format_diagnostic.rs | 10 +++++++++- 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/crates/eww/src/config/eww_config.rs b/crates/eww/src/config/eww_config.rs index ac8e559..f945291 100644 --- a/crates/eww/src/config/eww_config.rs +++ b/crates/eww/src/config/eww_config.rs @@ -67,7 +67,13 @@ impl EwwConfig { } pub fn get_window(&self, name: &String) -> Result<&EwwWindowDefinition> { - self.windows.get(name).with_context(|| format!("No window named '{}' exists in config", name)) + self.windows.get(name).with_context(|| { + format!( + "No window named '{}' exists in config.\nThis may also be caused by your config failing to load properly, \ + please check for any other errors in that case.", + name + ) + }) } pub fn get_script_var(&self, name: &VarName) -> Result<&ScriptVarDefinition> { diff --git a/crates/yuck/src/config/widget_definition.rs b/crates/yuck/src/config/widget_definition.rs index 4aaf41b..ffdcfff 100644 --- a/crates/yuck/src/config/widget_definition.rs +++ b/crates/yuck/src/config/widget_definition.rs @@ -10,7 +10,7 @@ use crate::{ from_ast::{FromAst, FromAstElementContent}, }, }; -use eww_shared_util::{AttrName, Span, VarName}; +use eww_shared_util::{AttrName, Span, Spanned, VarName}; use super::widget_use::WidgetUse; #[derive(Debug, PartialEq, Eq, Clone, serde::Serialize)] @@ -33,7 +33,7 @@ impl FromAstElementContent for WidgetDefinition { .note(EXPECTED_WIDGET_DEF_FORMAT)?; let expected_args = expected_args.into_iter().map(|x| x.as_symbol().map(AttrName)).collect::>()?; let widget = iter.expect_any().note(EXPECTED_WIDGET_DEF_FORMAT).and_then(WidgetUse::from_ast)?; - iter.expect_done()?; + iter.expect_done().map_err(|e| FormFormatError::WidgetDefMultipleChildren(e.span()))?; Ok(Self { name, expected_args, widget, span, args_span }) } } diff --git a/crates/yuck/src/error.rs b/crates/yuck/src/error.rs index 76c9972..ca90a50 100644 --- a/crates/yuck/src/error.rs +++ b/crates/yuck/src/error.rs @@ -160,12 +160,16 @@ impl AstResultExt for AstResult { pub enum FormFormatError { #[error("Widget definition missing argument list")] WidgetDefArglistMissing(Span), + + #[error("Widget definition has more than one child widget")] + WidgetDefMultipleChildren(Span), } impl Spanned for FormFormatError { fn span(&self) -> Span { match self { FormFormatError::WidgetDefArglistMissing(span) => *span, + FormFormatError::WidgetDefMultipleChildren(span) => *span, } } } diff --git a/crates/yuck/src/format_diagnostic.rs b/crates/yuck/src/format_diagnostic.rs index 58a192a..ebac4f7 100644 --- a/crates/yuck/src/format_diagnostic.rs +++ b/crates/yuck/src/format_diagnostic.rs @@ -254,7 +254,15 @@ impl ToDiagnostic for FormFormatError { FormFormatError::WidgetDefArglistMissing(span) => gen_diagnostic! { msg = self, label = span => "Insert the argument list (e.g.: `[]`) here", - note = "This list will in the future need to declare all the non-global variables / attributes used in this widget.\nThis is not yet neccessary, but is still considered good style.", + note = "This list will in the future need to declare all the non-global variables / attributes used in this widget.\n\ + This is not yet neccessary, but is still considered good style.", + }, + FormFormatError::WidgetDefMultipleChildren(span) => gen_diagnostic! { + msg = self, + label = span => "Found more than one child element here.", + note = "A widget-definition may only contain one child element.\n\ + To include multiple elements, wrap these elements in a single container widget such as `box`.\n\ + This is necessary as eww can't know how you want these elements to be layed out otherwise." }, } } From f7f718b94a4239d62e48e4a772f579df8d969f5c Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Sat, 14 Aug 2021 11:58:04 +0200 Subject: [PATCH 111/137] Some more error message improvements --- crates/eww/src/server.rs | 4 +-- crates/yuck/src/config/widget_definition.rs | 2 +- crates/yuck/src/config/widget_use.rs | 36 ++++++++++++--------- crates/yuck/src/error.rs | 17 +++++++--- crates/yuck/src/format_diagnostic.rs | 8 +++-- crates/yuck/src/parser/ast.rs | 5 ++- crates/yuck/src/parser/ast_iterator.rs | 14 +++++--- 7 files changed, 54 insertions(+), 32 deletions(-) diff --git a/crates/eww/src/server.rs b/crates/eww/src/server.rs index c4c02d1..be4447d 100644 --- a/crates/eww/src/server.rs +++ b/crates/eww/src/server.rs @@ -46,7 +46,7 @@ pub fn initialize_server(paths: EwwPaths, action: Option) -> Resu gtk::init()?; - log::info!("Initializing script var handler"); + log::debug!("Initializing script var handler"); let script_var_handler = script_var_handler::init(ui_send.clone()); let mut app = app::App { @@ -107,7 +107,7 @@ fn init_async_part(paths: EwwPaths, ui_send: UnboundedSender tokio::spawn(async move { // Wait for application exit event let _ = crate::application_lifecycle::recv_exit().await; - log::info!("Forward task received exit event"); + log::debug!("Forward task received exit event"); // Then forward that to the application let _ = ui_send.send(app::DaemonCommand::KillServer); }) diff --git a/crates/yuck/src/config/widget_definition.rs b/crates/yuck/src/config/widget_definition.rs index ffdcfff..84ccb6e 100644 --- a/crates/yuck/src/config/widget_definition.rs +++ b/crates/yuck/src/config/widget_definition.rs @@ -29,7 +29,7 @@ impl FromAstElementContent for WidgetDefinition { let (name_span, name) = iter.expect_symbol().note(EXPECTED_WIDGET_DEF_FORMAT)?; let (args_span, expected_args) = iter .expect_array() - .wrong_expr_type_to(|_| FormFormatError::WidgetDefArglistMissing(name_span.point_span_at_end())) + .wrong_expr_type_to(|_, _| Some(FormFormatError::WidgetDefArglistMissing(name_span.point_span_at_end()))) .note(EXPECTED_WIDGET_DEF_FORMAT)?; let expected_args = expected_args.into_iter().map(|x| x.as_symbol().map(AttrName)).collect::>()?; let widget = iter.expect_any().note(EXPECTED_WIDGET_DEF_FORMAT).and_then(WidgetUse::from_ast)?; diff --git a/crates/yuck/src/config/widget_use.rs b/crates/yuck/src/config/widget_use.rs index 96f7769..775d53f 100644 --- a/crates/yuck/src/config/widget_use.rs +++ b/crates/yuck/src/config/widget_use.rs @@ -4,7 +4,7 @@ use simplexpr::SimplExpr; use crate::{ config::attributes::AttrEntry, - error::AstResult, + error::{AstError, AstResult}, parser::{ast::Ast, ast_iterator::AstIterator, from_ast::FromAst}, }; use eww_shared_util::{AttrName, Span, VarName}; @@ -34,21 +34,7 @@ impl FromAst for WidgetUse { fn from_ast(e: Ast) -> AstResult { let span = e.span(); if let Ok(value) = e.clone().as_simplexpr() { - Ok(Self { - name: "label".to_string(), - name_span: span.point_span(), - attrs: Attributes::new( - span, - maplit::hashmap! { - AttrName("text".to_string()) => AttrEntry::new( - span, - Ast::SimplExpr(span.into(), value.clone()) - ) - }, - ), - children: Vec::new(), - span, - }) + Ok(label_from_simplexpr(value, span)) } else { let mut iter = e.try_ast_iter()?; let (name_span, name) = iter.expect_symbol()?; @@ -58,3 +44,21 @@ impl FromAst for WidgetUse { } } } + +fn label_from_simplexpr(value: SimplExpr, span: Span) -> WidgetUse { + WidgetUse { + name: "label".to_string(), + name_span: span.point_span(), + attrs: Attributes::new( + span, + maplit::hashmap! { + AttrName("text".to_string()) => AttrEntry::new( + span, + Ast::SimplExpr(span.into(), value.clone()) + ) + }, + ), + children: Vec::new(), + span, + } +} diff --git a/crates/yuck/src/error.rs b/crates/yuck/src/error.rs index ca90a50..c5f1980 100644 --- a/crates/yuck/src/error.rs +++ b/crates/yuck/src/error.rs @@ -34,6 +34,9 @@ pub enum AstError { #[error("Expected element {1}, but read {2}")] MismatchedElementName(Span, String, String), + #[error("Keyword `{1}` is missing a value")] + DanglingKeyword(Span, String), + #[error("Included file not found {}", .0.path)] IncludedFileNotFound(Include), @@ -81,9 +84,11 @@ impl AstError { AstError::ParseError { file_id, source: err } } - pub fn wrong_expr_type_to(self, f: impl FnOnce(Span) -> FormFormatError) -> AstError { + pub fn wrong_expr_type_to>(self, f: impl FnOnce(Span, AstType) -> Option) -> AstError { match self { - AstError::WrongExprType(span, ..) => AstError::FormFormatError(f(span.point_span())), + AstError::WrongExprType(span, expected, got) => { + f(span.point_span(), got).map(|x| x.into()).unwrap_or_else(|| AstError::WrongExprType(span, expected, got)) + } AstError::ErrorNote(s, err) => AstError::ErrorNote(s, Box::new(err.wrong_expr_type_to(f))), other => other, } @@ -98,6 +103,7 @@ impl Spanned for AstError { AstError::WrongExprType(span, ..) => *span, AstError::NotAValue(span, ..) => *span, AstError::MismatchedElementName(span, ..) => *span, + AstError::DanglingKeyword(span, _) => *span, AstError::AttrError(err) => err.span(), AstError::Other(span, ..) => *span, AstError::ConversionError(err) => err.value.span(), @@ -138,8 +144,9 @@ pub trait AstResultExt { fn context_label(self, label_span: Span, context: &str) -> AstResult; fn note(self, note: &str) -> AstResult; - /// Map any [AstError::WrongExprType]s error to a [FormFormatError] - fn wrong_expr_type_to(self, f: impl FnOnce(Span) -> FormFormatError) -> AstResult; + /// Map any [AstError::WrongExprType]s error to any other Into (such as a [FormFormatError]) + /// If the provided closure returns `None`, the error will be kept unmodified + fn wrong_expr_type_to>(self, f: impl FnOnce(Span, AstType) -> Option) -> AstResult; } impl AstResultExt for AstResult { @@ -151,7 +158,7 @@ impl AstResultExt for AstResult { self.map_err(|e| e.note(note)) } - fn wrong_expr_type_to(self, f: impl FnOnce(Span) -> FormFormatError) -> AstResult { + fn wrong_expr_type_to>(self, f: impl FnOnce(Span, AstType) -> Option) -> AstResult { self.map_err(|err| err.wrong_expr_type_to(f)) } } diff --git a/crates/yuck/src/format_diagnostic.rs b/crates/yuck/src/format_diagnostic.rs index ebac4f7..52d5e56 100644 --- a/crates/yuck/src/format_diagnostic.rs +++ b/crates/yuck/src/format_diagnostic.rs @@ -89,7 +89,7 @@ impl ToDiagnostic for AstError { AstError::WrongExprType(span, expected, actual) => gen_diagnostic! { msg = "Wrong type of expression", label = span => format!("Expected a `{}` here", expected), - note = format!("Expected: {}\nGot: {}", expected, actual), + note = format!("Expected: {}\n Got: {}", expected, actual), }, AstError::NotAValue(span, actual) => gen_diagnostic! { msg = format!("Expected value, but got `{}`", actual), @@ -101,7 +101,7 @@ impl ToDiagnostic for AstError { AstError::MismatchedElementName(span, expected, got) => gen_diagnostic! { msg = format!("Expected element `{}`, but found `{}`", expected, got), label = span => format!("Expected `{}` here", expected), - note = format!("Expected: {}\nGot: {}", expected, got), + note = format!("Expected: {}\n Got: {}", expected, got), }, AstError::ErrorContext { label_span, context, main_err } => { main_err.to_diagnostic().with_label(span_to_secondary_label(*label_span).with_message(context)) @@ -120,6 +120,10 @@ impl ToDiagnostic for AstError { label = extra_nodes_span => "these elements must not be here", note = "Consider wrapping the elements in some container element", }, + AstError::DanglingKeyword(span, keyword) => gen_diagnostic! { + msg = self, + label = span => "No value provided for this", + }, AstError::ErrorNote(note, source) => source.to_diagnostic().with_notes(vec![note.to_string()]), AstError::ValidationError(source) => source.to_diagnostic(), AstError::NoMoreElementsExpected(span) => gen_diagnostic!(self, span), diff --git a/crates/yuck/src/parser/ast.rs b/crates/yuck/src/parser/ast.rs index 2361ea5..ac48f38 100644 --- a/crates/yuck/src/parser/ast.rs +++ b/crates/yuck/src/parser/ast.rs @@ -27,7 +27,10 @@ pub enum AstType { impl Display for AstType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self) + match self { + AstType::IntoPrimitive => write!(f, "primitive"), + _ => write!(f, "{:?}", self), + } } } diff --git a/crates/yuck/src/parser/ast_iterator.rs b/crates/yuck/src/parser/ast_iterator.rs index f1e9414..cee69dc 100644 --- a/crates/yuck/src/parser/ast_iterator.rs +++ b/crates/yuck/src/parser/ast_iterator.rs @@ -77,7 +77,7 @@ impl> AstIterator { } pub fn expect_key_values(&mut self) -> AstResult { - parse_key_values(self) + parse_key_values(self, true) } pub fn put_back(&mut self, ast: Ast) { @@ -98,7 +98,7 @@ impl> Iterator for AstIterator { } /// Parse consecutive `:keyword value` pairs from an expression iterator into an [Attributes]. -fn parse_key_values(iter: &mut AstIterator>) -> AstResult { +fn parse_key_values(iter: &mut AstIterator>, fail_on_dangling_kw: bool) -> AstResult { let mut data = HashMap::new(); let mut attrs_span = iter.remaining_span.point_span(); loop { @@ -110,9 +110,13 @@ fn parse_key_values(iter: &mut AstIterator>) -> AstRes data.insert(AttrName(kw), attr_value); } None => { - iter.iter.put_back(Ast::Keyword(key_span, kw)); - attrs_span.1 = iter.remaining_span.0; - return Ok(Attributes::new(attrs_span, data)); + if fail_on_dangling_kw { + return Err(AstError::DanglingKeyword(key_span, kw)); + } else { + iter.iter.put_back(Ast::Keyword(key_span, kw)); + attrs_span.1 = iter.remaining_span.0; + return Ok(Attributes::new(attrs_span, data)); + } } }, next => { From 91f714dc1196a57ee914897b1fdcf6e61f09c6f6 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Sat, 14 Aug 2021 13:14:47 +0200 Subject: [PATCH 112/137] Add config validation step --- crates/eww/src/config/eww_config.rs | 4 ++ crates/eww/src/error_handling_ctx.rs | 9 ++- crates/yuck/examples/validation.rs | 40 ------------- crates/yuck/src/config/validate.rs | 86 ++++++++++++++++++++++------ crates/yuck/src/format_diagnostic.rs | 26 ++++++++- crates/yuck/src/parser/ast.rs | 6 +- 6 files changed, 108 insertions(+), 63 deletions(-) delete mode 100644 crates/yuck/examples/validation.rs diff --git a/crates/eww/src/config/eww_config.rs b/crates/eww/src/config/eww_config.rs index f945291..efdf827 100644 --- a/crates/eww/src/config/eww_config.rs +++ b/crates/eww/src/config/eww_config.rs @@ -38,6 +38,10 @@ impl EwwConfig { bail!("The configuration file `{}` does not exist", path.as_ref().display()); } let config = Config::generate_from_main_file(files, path)?; + + // run some validations on the configuration + yuck::config::validate::validate(&config, super::inbuilt::get_inbuilt_vars().keys().cloned().collect())?; + let Config { widget_definitions, window_definitions, var_definitions, mut script_vars } = config; script_vars.extend(crate::config::inbuilt::get_inbuilt_vars()); Ok(EwwConfig { diff --git a/crates/eww/src/error_handling_ctx.rs b/crates/eww/src/error_handling_ctx.rs index 130e949..bf028b4 100644 --- a/crates/eww/src/error_handling_ctx.rs +++ b/crates/eww/src/error_handling_ctx.rs @@ -10,7 +10,12 @@ use codespan_reporting::{ use eww_shared_util::Span; use once_cell::sync::Lazy; use simplexpr::{dynval::ConversionError, eval::EvalError}; -use yuck::{config::file_provider::YuckFiles, error::AstError, format_diagnostic::ToDiagnostic, gen_diagnostic}; +use yuck::{ + config::{file_provider::YuckFiles, validate::ValidationError}, + error::AstError, + format_diagnostic::ToDiagnostic, + gen_diagnostic, +}; use crate::error::DiagError; @@ -46,6 +51,8 @@ pub fn anyhow_err_to_diagnostic(err: &anyhow::Error) -> Diagnostic { err.to_diagnostic() } else if let Some(err) = err.downcast_ref::() { err.to_diagnostic() + } else if let Some(err) = err.downcast_ref::() { + err.to_diagnostic() } else if let Some(err) = err.downcast_ref::() { err.to_diagnostic() } else { diff --git a/crates/yuck/examples/validation.rs b/crates/yuck/examples/validation.rs deleted file mode 100644 index d2c739d..0000000 --- a/crates/yuck/examples/validation.rs +++ /dev/null @@ -1,40 +0,0 @@ -use yuck::{ - config::{widget_definition::WidgetDefinition, widget_use::WidgetUse, *}, - error::AstError, - format_diagnostic::ToDiagnostic, - parser::from_ast::FromAst, -}; - -fn main() { - let mut files = codespan_reporting::files::SimpleFiles::new(); - - let input_use = r#" - (foo :something 12 - :bla "bruh" - "some text") - "#; - let input_def = r#" - (defwidget foo [something bla] "foo") - "#; - - let file_id_use = files.add("use.eww", input_use); - let file_id_def = files.add("def.eww", input_def); - let parsed_use = WidgetUse::from_ast(yuck::parser::parse_string(file_id_use, input_use).unwrap()).unwrap(); - let parsed_def = WidgetDefinition::from_ast(yuck::parser::parse_string(file_id_def, input_def).unwrap()).unwrap(); - let defs = maplit::hashmap! { - "foo".to_string() => parsed_def, - }; - match validate::validate(&defs, &parsed_use) { - Ok(ast) => { - println!("{:?}", ast); - } - Err(err) => { - let err = AstError::ValidationError(err); - let diag = err.to_diagnostic(); - use codespan_reporting::term; - let config = term::Config::default(); - let mut writer = term::termcolor::StandardStream::stderr(term::termcolor::ColorChoice::Always); - term::emit(&mut writer, &config, &files, &diag).unwrap(); - } - } -} diff --git a/crates/yuck/src/config/validate.rs b/crates/yuck/src/config/validate.rs index c248c5e..8a3818b 100644 --- a/crates/yuck/src/config/validate.rs +++ b/crates/yuck/src/config/validate.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use simplexpr::SimplExpr; @@ -7,7 +7,7 @@ use crate::{ parser::{ast::Ast, ast_iterator::AstIterator, from_ast::FromAst}, }; -use super::{widget_definition::WidgetDefinition, widget_use::WidgetUse}; +use super::{widget_definition::WidgetDefinition, widget_use::WidgetUse, Config}; use eww_shared_util::{AttrName, Span, Spanned, VarName}; #[derive(Debug, thiserror::Error)] @@ -17,6 +17,14 @@ pub enum ValidationError { #[error("Missing attribute `{arg_name}` in use of widget `{widget_name}`")] MissingAttr { widget_name: String, arg_name: AttrName, arg_list_span: Option, use_span: Span }, + + #[error("No variable named `{name}` in scope")] + UnknownVariable { + span: Span, + name: VarName, + /// True if the error occurred inside a widget definition, false if it occurred in a window definition + in_definition: bool, + }, } impl Spanned for ValidationError { @@ -24,24 +32,70 @@ impl Spanned for ValidationError { match self { ValidationError::UnknownWidget(span, _) => *span, ValidationError::MissingAttr { use_span, .. } => *use_span, + ValidationError::UnknownVariable { span, .. } => *span, } } } -pub fn validate(defs: &HashMap, content: &WidgetUse) -> Result<(), ValidationError> { - if let Some(def) = defs.get(&content.name) { - for expected in def.expected_args.iter() { - if !content.attrs.attrs.contains_key(expected) { - return Err(ValidationError::MissingAttr { - widget_name: def.name.to_string(), - arg_name: expected.clone(), - arg_list_span: Some(def.args_span), - use_span: content.span, - }); - } - } - } else { - return Err(ValidationError::UnknownWidget(content.span, content.name.to_string())); +pub fn validate(config: &Config, additional_globals: Vec) -> Result<(), ValidationError> { + let var_names = std::iter::empty() + .chain(additional_globals.iter().cloned()) + .chain(config.script_vars.keys().cloned()) + .chain(config.var_definitions.keys().cloned()) + .collect(); + for window in config.window_definitions.values() { + validate_variables_in_widget_use(&config.widget_definitions, &var_names, &window.widget, false)?; + } + for def in config.widget_definitions.values() { + validate_widget_definition(&config.widget_definitions, &var_names, &def)?; } Ok(()) } + +pub fn validate_widget_definition( + other_defs: &HashMap, + globals: &HashSet, + def: &WidgetDefinition, +) -> Result<(), ValidationError> { + let mut variables_in_scope = globals.clone(); + for arg in def.expected_args.iter() { + variables_in_scope.insert(VarName(arg.0.to_string())); + } + + validate_variables_in_widget_use(other_defs, &variables_in_scope, &def.widget, true) +} + +pub fn validate_variables_in_widget_use( + defs: &HashMap, + variables: &HashSet, + widget: &WidgetUse, + is_in_definition: bool, +) -> Result<(), ValidationError> { + let matching_definition = defs.get(&widget.name); + if let Some(matching_def) = matching_definition { + let missing_arg = matching_def.expected_args.iter().find(|expected| !widget.attrs.attrs.contains_key(*expected)); + if let Some(missing_arg) = missing_arg { + return Err(ValidationError::MissingAttr { + widget_name: widget.name.clone(), + arg_name: missing_arg.clone(), + arg_list_span: Some(matching_def.args_span), + use_span: widget.attrs.span, + }); + } + } + + let values = widget.attrs.attrs.values(); + let unknown_var = values.filter_map(|value| value.value.as_simplexpr().ok()).find_map(|expr: SimplExpr| { + let span = expr.span(); + expr.var_refs().iter().map(move |&x| (span, x.clone())).find(|(span, var_ref)| !variables.contains(var_ref)) + }); + if let Some((span, var)) = unknown_var { + return Err(ValidationError::UnknownVariable { span, name: var.clone(), in_definition: is_in_definition }); + } + + for child in widget.children.iter() { + let _ = validate_variables_in_widget_use(defs, variables, child, is_in_definition)?; + } + + Ok(()) +} diff --git a/crates/yuck/src/format_diagnostic.rs b/crates/yuck/src/format_diagnostic.rs index 52d5e56..d3b0ae4 100644 --- a/crates/yuck/src/format_diagnostic.rs +++ b/crates/yuck/src/format_diagnostic.rs @@ -162,13 +162,33 @@ impl ToDiagnostic for ValidationError { label = span => "Used here", }, ValidationError::MissingAttr { widget_name, arg_name, arg_list_span, use_span } => { - let mut diag = - gen_diagnostic!(self).with_label(span_to_secondary_label(*use_span).with_message("Argument missing here")); + let mut diag = Diagnostic::error() + .with_message(self.to_string()) + .with_label(span_to_secondary_label(*use_span).with_message("Argument missing here")) + .with_notes(vec![format!( + "Hint: pass the attribute like so: `({} :{} your-value ...`", + widget_name, arg_name + )]); if let Some(arg_list_span) = arg_list_span { diag = diag.with_label(span_to_secondary_label(*arg_list_span).with_message("But is required here")); } diag } + ValidationError::UnknownVariable { span, name, in_definition } => { + let diag = gen_diagnostic! { + msg = self, + label = span => "Used here", + note = if *in_definition { + "Hint: Either define it as a global variable, or add it to the argument-list of your `defwidget` and pass it as an argument" + } else { + "Hint: Define it as a global variable" + } + }; + diag.with_notes(vec![format!( + "Hint: If you meant to use the literal value \"{}\", surround the value in quotes", + name + )]) + } } } } @@ -226,7 +246,7 @@ impl ToDiagnostic for simplexpr::eval::EvalError { } // TODO the note here is confusing when it's an unknown variable being used _within_ a string literal / simplexpr // it only really makes sense on top-level symbols - notes.push(format!("If you meant to use the literal value \"{}\", surround the value in quotes", name)); + notes.push(format!("Hint: If you meant to use the literal value \"{}\", surround the value in quotes", name)); gen_diagnostic!(self).with_notes(notes) } Spanned(span, error) => error.as_ref().to_diagnostic().with_label(span_to_primary_label(*span)), diff --git a/crates/yuck/src/parser/ast.rs b/crates/yuck/src/parser/ast.rs index ac48f38..59ad02a 100644 --- a/crates/yuck/src/parser/ast.rs +++ b/crates/yuck/src/parser/ast.rs @@ -91,12 +91,12 @@ impl Ast { } } - pub fn as_simplexpr(self) -> AstResult { + pub fn as_simplexpr(&self) -> AstResult { match self { // TODO do I do this? // Ast::Array(span, elements) => todo!() - Ast::Symbol(span, x) => Ok(SimplExpr::VarRef(span, VarName(x))), - Ast::SimplExpr(span, x) => Ok(x), + Ast::Symbol(span, x) => Ok(SimplExpr::VarRef(*span, VarName(x.clone()))), + Ast::SimplExpr(span, x) => Ok(x.clone()), _ => Err(AstError::WrongExprType(self.span(), AstType::IntoPrimitive, self.expr_type())), } } From 096aedc0d40d9ae3613e69006faad6726a047e31 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Sat, 14 Aug 2021 13:32:09 +0200 Subject: [PATCH 113/137] Clean up some logging output --- crates/eww/src/app.rs | 8 ++++---- crates/eww/src/client.rs | 2 +- crates/eww/src/main.rs | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/eww/src/app.rs b/crates/eww/src/app.rs index ee9b331..1518416 100644 --- a/crates/eww/src/app.rs +++ b/crates/eww/src/app.rs @@ -134,7 +134,7 @@ impl App { } DaemonCommand::OpenMany { windows, sender } => { let result = windows.iter().try_for_each(|w| self.open_window(w, None, None, None, None)); - respond_with_error(sender, result)?; + respond_with_result(sender, result)?; } DaemonCommand::OpenWindow { window_name, pos, size, anchor, screen: monitor, should_toggle, sender } => { let result = if should_toggle && self.open_windows.contains_key(&window_name) { @@ -142,11 +142,11 @@ impl App { } else { self.open_window(&window_name, pos, size, monitor, anchor) }; - respond_with_error(sender, result)?; + respond_with_result(sender, result)?; } DaemonCommand::CloseWindow { window_name, sender } => { let result = self.close_window(&window_name); - respond_with_error(sender, result)?; + respond_with_result(sender, result)?; } DaemonCommand::PrintState { all, sender } => { let vars = self.eww_state.get_variables().iter(); @@ -381,7 +381,7 @@ fn get_monitor_geometry(n: Option) -> Result { } /// In case of an Err, send the error message to a sender. -fn respond_with_error(sender: DaemonResponseSender, result: Result) -> Result<()> { +fn respond_with_result(sender: DaemonResponseSender, result: Result) -> Result<()> { match result { Ok(_) => sender.send_success(String::new()), Err(e) => { diff --git a/crates/eww/src/client.rs b/crates/eww/src/client.rs index 60bee3d..e5d98ec 100644 --- a/crates/eww/src/client.rs +++ b/crates/eww/src/client.rs @@ -25,7 +25,7 @@ pub fn handle_client_only_action(paths: &EwwPaths, action: ActionClientOnly) -> } pub fn do_server_call(stream: &mut UnixStream, action: &opts::ActionWithServer) -> Result> { - log::info!("Forwarding options to server"); + log::debug!("Forwarding options to server"); stream.set_nonblocking(false).context("Failed to set stream to non-blocking")?; let message_bytes = bincode::serialize(&action)?; diff --git a/crates/eww/src/main.rs b/crates/eww/src/main.rs index edf377c..21d8eaf 100644 --- a/crates/eww/src/main.rs +++ b/crates/eww/src/main.rs @@ -129,9 +129,9 @@ fn listen_for_daemon_response(mut recv: DaemonResponseReceiver) { } fn handle_server_command(paths: &EwwPaths, action: &ActionWithServer, connect_attempts: usize) -> Result<()> { - log::info!("Trying to find server process at socket {}", paths.get_ipc_socket_file().display()); + log::debug!("Trying to find server process at socket {}", paths.get_ipc_socket_file().display()); let mut stream = attempt_connect(&paths.get_ipc_socket_file(), connect_attempts).context("Failed to connect to daemon")?; - log::info!("Connected to Eww server ({}).", &paths.get_ipc_socket_file().display()); + log::debug!("Connected to Eww server ({}).", &paths.get_ipc_socket_file().display()); let response = client::do_server_call(&mut stream, action).context("Error while forwarding command to server")?; if let Some(response) = response { println!("{}", response); From e72a16a5bd729dbd0aa207d6a351482e6fec2a3c Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Sat, 14 Aug 2021 14:03:16 +0200 Subject: [PATCH 114/137] Fix daemonization --- crates/eww/src/server.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/crates/eww/src/server.rs b/crates/eww/src/server.rs index be4447d..9a6abd1 100644 --- a/crates/eww/src/server.rs +++ b/crates/eww/src/server.rs @@ -181,7 +181,13 @@ pub enum ForkResult { fn do_detach(log_file_path: impl AsRef) -> Result { // detach from terminal match unsafe { nix::unistd::fork()? } { - nix::unistd::ForkResult::Child => {} + nix::unistd::ForkResult::Child => { + nix::unistd::setsid()?; + match unsafe {nix::unistd::fork()? + }{ + nix::unistd::ForkResult::Parent { .. } => std::process::exit(0), + nix::unistd::ForkResult::Child => {} + }} nix::unistd::ForkResult::Parent { .. } => { return Ok(ForkResult::Parent); } From b8d222384b7639bd65408f40900a028cdf27d87d Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Sat, 14 Aug 2021 14:44:22 +0200 Subject: [PATCH 115/137] Make only some commands able to start the daemon --- crates/eww/src/app.rs | 4 ++-- crates/eww/src/main.rs | 19 +++++++++++++------ crates/eww/src/opts.rs | 7 +++++++ crates/eww/src/server.rs | 20 +++++++++++++++----- crates/yuck/examples/errors.rs | 31 ------------------------------- 5 files changed, 37 insertions(+), 44 deletions(-) delete mode 100644 crates/yuck/examples/errors.rs diff --git a/crates/eww/src/app.rs b/crates/eww/src/app.rs index 1518416..99e7692 100644 --- a/crates/eww/src/app.rs +++ b/crates/eww/src/app.rs @@ -268,7 +268,7 @@ impl App { self.eww_config = config; self.eww_state.clear_all_window_states(); - let window_names: Vec = self.open_windows.keys().chain(self.failed_windows.iter()).cloned().collect(); + let window_names: Vec = self.open_windows.keys().cloned().chain(self.failed_windows.iter().cloned()).dedup().collect(); for window_name in &window_names { self.open_window(&window_name, None, None, None, None)?; } @@ -386,7 +386,7 @@ fn respond_with_result(sender: DaemonResponseSender, result: Result) -> Re Ok(_) => sender.send_success(String::new()), Err(e) => { let formatted = error_handling_ctx::format_error(&e); - println!("{}", formatted); + println!("Action failed with error: {}", formatted); sender.send_failure(formatted) }, } diff --git a/crates/eww/src/main.rs b/crates/eww/src/main.rs index 21d8eaf..f1b7466 100644 --- a/crates/eww/src/main.rs +++ b/crates/eww/src/main.rs @@ -27,6 +27,7 @@ pub mod app; pub mod application_lifecycle; pub mod client; pub mod config; +mod daemon_response; pub mod display_backend; pub mod error; mod error_handling_ctx; @@ -38,7 +39,6 @@ pub mod script_var_handler; pub mod server; pub mod util; pub mod widgets; -mod daemon_response; fn main() { let eww_binary_name = std::env::args().next().unwrap(); @@ -63,12 +63,9 @@ fn main() { client::handle_client_only_action(&paths, action)?; false } - opts::Action::WithServer(ActionWithServer::KillServer) => { - handle_server_command(&paths, &ActionWithServer::KillServer, 1)?; - false - } + // a running daemon is necessary for this command - opts::Action::WithServer(action) => { + opts::Action::WithServer(action) if action.can_start_daemon() => { // attempt to just send the command to a running daemon if let Err(err) = handle_server_command(&paths, &action, 5) { // connecting to the daemon failed. Thus, start the daemon here! @@ -81,6 +78,7 @@ fn main() { let (command, response_recv) = action.into_daemon_command(); // start the daemon and give it the command + let fork_result = server::initialize_server(paths.clone(), Some(command))?; let is_parent = fork_result == ForkResult::Parent; if let (Some(recv), true) = (response_recv, is_parent) { @@ -91,6 +89,15 @@ fn main() { true } } + opts::Action::WithServer(ActionWithServer::KillServer) => { + handle_server_command(&paths, &ActionWithServer::KillServer, 1)?; + false + } + + opts::Action::WithServer(action) => { + handle_server_command(&paths, &action, 5)?; + true + } // make sure that there isn't already a Eww daemon running. opts::Action::Daemon if check_server_running(paths.get_ipc_socket_file()) => { diff --git a/crates/eww/src/opts.rs b/crates/eww/src/opts.rs index 636d660..e93fb7b 100644 --- a/crates/eww/src/opts.rs +++ b/crates/eww/src/opts.rs @@ -161,6 +161,13 @@ fn parse_var_update_arg(s: &str) -> Result<(VarName, DynVal)> { } impl ActionWithServer { + pub fn can_start_daemon(&self) -> bool { + match self { + ActionWithServer::OpenWindow { .. } | ActionWithServer::OpenMany { .. } => true, + _ => false, + } + } + pub fn into_daemon_command(self) -> (app::DaemonCommand, Option) { let command = match self { ActionWithServer::Update { mappings } => app::DaemonCommand::UpdateVars(mappings), diff --git a/crates/eww/src/server.rs b/crates/eww/src/server.rs index 9a6abd1..9be673b 100644 --- a/crates/eww/src/server.rs +++ b/crates/eww/src/server.rs @@ -1,7 +1,17 @@ -use crate::{EwwPaths, app::{self, DaemonCommand}, config, daemon_response, error_handling_ctx, eww_state::*, ipc_server, script_var_handler, util}; +use crate::{ + app::{self, DaemonCommand}, + config, daemon_response, error_handling_ctx, + eww_state::*, + ipc_server, script_var_handler, util, EwwPaths, +}; use anyhow::*; -use std::{collections::{HashMap, HashSet}, os::unix::io::AsRawFd, path::Path, sync::{atomic::Ordering, Arc}}; +use std::{ + collections::{HashMap, HashSet}, + os::unix::io::AsRawFd, + path::Path, + sync::{atomic::Ordering, Arc}, +}; use tokio::sync::mpsc::*; pub fn initialize_server(paths: EwwPaths, action: Option) -> Result { @@ -183,11 +193,11 @@ fn do_detach(log_file_path: impl AsRef) -> Result { match unsafe { nix::unistd::fork()? } { nix::unistd::ForkResult::Child => { nix::unistd::setsid()?; - match unsafe {nix::unistd::fork()? - }{ + match unsafe { nix::unistd::fork()? } { nix::unistd::ForkResult::Parent { .. } => std::process::exit(0), nix::unistd::ForkResult::Child => {} - }} + } + } nix::unistd::ForkResult::Parent { .. } => { return Ok(ForkResult::Parent); } diff --git a/crates/yuck/examples/errors.rs b/crates/yuck/examples/errors.rs deleted file mode 100644 index cd14f8a..0000000 --- a/crates/yuck/examples/errors.rs +++ /dev/null @@ -1,31 +0,0 @@ -// use eww_config::{ -// format_diagnostic::ToDiagnostic, -// parser::{ast::*, from_ast::FromAst}, -//}; - -fn main() { - println!("hi"); - // let mut files = codespan_reporting::files::SimpleFiles::new(); - - // let input = r#" - //(heyho ; :foo { "foo \" } bar " } - //; :baz {(foo == bar ? 12.2 : 12)} - //(foo) - //(defwidget foo [something bla] "foo") - //(baz))"#; - - // let file_id = files.add("foo.eww", input); - // let ast = eww_config::parser::parse_string(file_id, input); - // match ast.and_then(eww_config::parser::from_ast::Element::::from_ast) { - // Ok(ast) => { - // println!("{:?}", ast); - //} - // Err(err) => { - // dbg!(&err); - // let diag = err.to_diagnostic(); - // use codespan_reporting::term; - // let config = term::Config::default(); - // let mut writer = term::termcolor::StandardStream::stderr(term::termcolor::ColorChoice::Always); - // term::emit(&mut writer, &config, &files, &diag).unwrap(); - //} -} From 47e13011805c2b1238205431a602c24fbba8be78 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Sat, 14 Aug 2021 14:56:49 +0200 Subject: [PATCH 116/137] Slightly clean up error reporting --- crates/eww/src/error_handling_ctx.rs | 33 +++++++++++--------- crates/eww/src/script_var_handler.rs | 6 +++- crates/eww/src/widgets/widget_definitions.rs | 2 +- 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/crates/eww/src/error_handling_ctx.rs b/crates/eww/src/error_handling_ctx.rs index bf028b4..b965b4a 100644 --- a/crates/eww/src/error_handling_ctx.rs +++ b/crates/eww/src/error_handling_ctx.rs @@ -14,7 +14,6 @@ use yuck::{ config::{file_provider::YuckFiles, validate::ValidationError}, error::AstError, format_diagnostic::ToDiagnostic, - gen_diagnostic, }; use crate::error::DiagError; @@ -26,11 +25,16 @@ pub fn clear_files() { } pub fn print_error(err: anyhow::Error) { - match stringify_diagnostic(anyhow_err_to_diagnostic(&err)) { - Ok(diag) => { - eprintln!("{:?}\n{}", err, diag); - } - Err(_) => { + match anyhow_err_to_diagnostic(&err) { + Some(diag) => match stringify_diagnostic(diag) { + Ok(diag) => { + eprintln!("{}", diag); + } + Err(_) => { + log::error!("{:?}", err); + } + }, + None => { log::error!("{:?}", err); } } @@ -40,23 +44,22 @@ pub fn format_error(err: &anyhow::Error) -> String { for err in err.chain() { format!("chain: {}", err); } - let diag = anyhow_err_to_diagnostic(err); - stringify_diagnostic(diag).unwrap_or_else(|_| format!("{}", err)) + anyhow_err_to_diagnostic(err).and_then(|diag| stringify_diagnostic(diag).ok()).unwrap_or_else(|| format!("{:?}", err)) } -pub fn anyhow_err_to_diagnostic(err: &anyhow::Error) -> Diagnostic { +pub fn anyhow_err_to_diagnostic(err: &anyhow::Error) -> Option> { if let Some(err) = err.downcast_ref::() { - err.diag.clone() + Some(err.diag.clone()) } else if let Some(err) = err.downcast_ref::() { - err.to_diagnostic() + Some(err.to_diagnostic()) } else if let Some(err) = err.downcast_ref::() { - err.to_diagnostic() + Some(err.to_diagnostic()) } else if let Some(err) = err.downcast_ref::() { - err.to_diagnostic() + Some(err.to_diagnostic()) } else if let Some(err) = err.downcast_ref::() { - err.to_diagnostic() + Some(err.to_diagnostic()) } else { - gen_diagnostic!(format!("{:?}", err)) + None } } diff --git a/crates/eww/src/script_var_handler.rs b/crates/eww/src/script_var_handler.rs index 2ebfc31..0e9cb00 100644 --- a/crates/eww/src/script_var_handler.rs +++ b/crates/eww/src/script_var_handler.rs @@ -206,10 +206,11 @@ impl ListenVarHandler { let mut handle = tokio::process::Command::new("sh") .args(&["-c", &var.command]) .stdout(std::process::Stdio::piped()) - .stderr(std::process::Stdio::inherit()) + .stderr(std::process::Stdio::piped()) .stdin(std::process::Stdio::null()) .spawn()?; let mut stdout_lines = BufReader::new(handle.stdout.take().unwrap()).lines(); + let mut stderr_lines = BufReader::new(handle.stderr.take().unwrap()).lines(); crate::loop_select_exiting! { _ = handle.wait() => break, _ = cancellation_token.cancelled() => break, @@ -217,6 +218,9 @@ impl ListenVarHandler { let new_value = DynVal::from_string(line.to_owned()); evt_send.send(DaemonCommand::UpdateVars(vec![(var.name.to_owned(), new_value)]))?; } + Ok(Some(line)) = stderr_lines.next_line() => { + log::warn!("stderr of `{}`: {}", var.name, line); + } else => break, } let _ = handle.kill().await; diff --git a/crates/eww/src/widgets/widget_definitions.rs b/crates/eww/src/widgets/widget_definitions.rs index 2bc4c5f..3be328f 100644 --- a/crates/eww/src/widgets/widget_definitions.rs +++ b/crates/eww/src/widgets/widget_definitions.rs @@ -593,7 +593,7 @@ fn build_gtk_literal(bargs: &mut BuilderArgs) -> Result { .map_err(|e| AstError::ErrorContext { label_span: literal_use_span, context: "Error in the literal used here".to_string(), - main_err: Box::new(error_handling_ctx::anyhow_err_to_diagnostic(&e)) + main_err: Box::new(error_handling_ctx::anyhow_err_to_diagnostic(&e).unwrap_or_else(|| gen_diagnostic!(e))) })?; gtk_widget.add(&child_widget); child_widget.show(); From 398717b782918da9ac7805d61e5ef45a961dd71b Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Sat, 14 Aug 2021 15:06:19 +0200 Subject: [PATCH 117/137] Add restart flag --- crates/eww/src/ipc_server.rs | 2 +- crates/eww/src/main.rs | 6 +++++- crates/eww/src/opts.rs | 9 +++++++-- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/crates/eww/src/ipc_server.rs b/crates/eww/src/ipc_server.rs index ac1e116..2faadfe 100644 --- a/crates/eww/src/ipc_server.rs +++ b/crates/eww/src/ipc_server.rs @@ -38,7 +38,7 @@ async fn handle_connection(mut stream: tokio::net::UnixStream, evt_send: Unbound evt_send.send(command)?; if let Some(mut response_recv) = maybe_response_recv { - log::info!("Waiting for response for IPC client"); + log::debug!("Waiting for response for IPC client"); if let Ok(Some(response)) = tokio::time::timeout(Duration::from_millis(100), response_recv.recv()).await { let response = bincode::serialize(&response)?; let result = &stream_write.write_all(&response).await; diff --git a/crates/eww/src/main.rs b/crates/eww/src/main.rs index f1b7466..41c53ad 100644 --- a/crates/eww/src/main.rs +++ b/crates/eww/src/main.rs @@ -66,6 +66,11 @@ fn main() { // a running daemon is necessary for this command opts::Action::WithServer(action) if action.can_start_daemon() => { + if opts.restart { + let _ = handle_server_command(&paths, &ActionWithServer::KillServer, 1); + std::thread::sleep(std::time::Duration::from_millis(200)); + } + // attempt to just send the command to a running daemon if let Err(err) = handle_server_command(&paths, &action, 5) { // connecting to the daemon failed. Thus, start the daemon here! @@ -78,7 +83,6 @@ fn main() { let (command, response_recv) = action.into_daemon_command(); // start the daemon and give it the command - let fork_result = server::initialize_server(paths.clone(), Some(command))?; let is_parent = fork_result == ForkResult::Parent; if let (Some(recv), true) = (response_recv, is_parent) { diff --git a/crates/eww/src/opts.rs b/crates/eww/src/opts.rs index e93fb7b..3925ed4 100644 --- a/crates/eww/src/opts.rs +++ b/crates/eww/src/opts.rs @@ -15,6 +15,7 @@ use crate::{ pub struct Opt { pub log_debug: bool, pub show_logs: bool, + pub restart: bool, pub config_path: Option, pub action: Action, } @@ -33,6 +34,10 @@ struct RawOpt { #[structopt(long = "logs", global = true)] show_logs: bool, + /// Restart the daemon completely before running the command + #[structopt(long = "restart", global = true)] + restart: bool, + #[structopt(subcommand)] action: Action, } @@ -148,8 +153,8 @@ impl Opt { impl From for Opt { fn from(other: RawOpt) -> Self { - let RawOpt { action, log_debug, show_logs, config } = other; - Opt { action, log_debug, show_logs, config_path: config } + let RawOpt { action, log_debug, show_logs, config, restart } = other; + Opt { action, log_debug, show_logs, config_path: config, restart } } } From f0b27d2ec112d1c2e559004fa1f54fe49e20c0c7 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Sat, 14 Aug 2021 16:08:43 +0200 Subject: [PATCH 118/137] Allow for aligned text using unindent --- Cargo.lock | 7 +++++++ crates/eww/Cargo.toml | 1 + crates/eww/src/widgets/widget_definitions.rs | 1 + 3 files changed, 9 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 6e58f10..01c554d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -472,6 +472,7 @@ dependencies = [ "tokio-stream", "tokio-util", "unescape", + "unindent", "wait-timeout", "x11rb", "yuck", @@ -2254,6 +2255,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +[[package]] +name = "unindent" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f14ee04d9415b52b3aeab06258a3f07093182b88ba0f9b8d203f211a7a7d41c7" + [[package]] name = "utf8-ranges" version = "1.0.4" diff --git a/crates/eww/Cargo.toml b/crates/eww/Cargo.toml index db02853..e26d681 100644 --- a/crates/eww/Cargo.toml +++ b/crates/eww/Cargo.toml @@ -50,6 +50,7 @@ nix = "0.20" smart-default = "0.6" simple-signal = "1.1" unescape = "0.1" +unindent = "0.1" tokio = { version = "1.0", features = ["full"] } tokio-stream = "0.1" diff --git a/crates/eww/src/widgets/widget_definitions.rs b/crates/eww/src/widgets/widget_definitions.rs index 3be328f..0f255ad 100644 --- a/crates/eww/src/widgets/widget_definitions.rs +++ b/crates/eww/src/widgets/widget_definitions.rs @@ -543,6 +543,7 @@ fn build_gtk_label(bargs: &mut BuilderArgs) -> Result { prop(text: as_string, limit_width: as_i32 = i32::MAX) { let text = text.chars().take(limit_width as usize).collect::(); let text = unescape::unescape(&text).context(format!("Failed to unescape label text {}", &text))?; + let text = unindent::unindent(&text); gtk_widget.set_text(&text); }, // @prop markup - Pango markup to display From c20b172662d8f3e627ac836c254242598f1a51f6 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Tue, 17 Aug 2021 11:10:48 +0200 Subject: [PATCH 119/137] Fix representation of string-interpolations by adding a dedicated concat AST variant --- crates/simplexpr/src/ast.rs | 12 +++ crates/simplexpr/src/dynval.rs | 5 +- crates/simplexpr/src/eval.rs | 73 +++++++++---------- .../simplexpr/src/parser/lalrpop_helpers.rs | 13 ++-- 4 files changed, 54 insertions(+), 49 deletions(-) diff --git a/crates/simplexpr/src/ast.rs b/crates/simplexpr/src/ast.rs index b7aa2da..57769f8 100644 --- a/crates/simplexpr/src/ast.rs +++ b/crates/simplexpr/src/ast.rs @@ -32,6 +32,7 @@ pub enum UnaryOp { #[derive(Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum SimplExpr { Literal(DynVal), + Concat(Span, Vec), VarRef(Span, VarName), BinOp(Span, Box, BinOp, Box), UnaryOp(Span, UnaryOp, Box), @@ -44,6 +45,16 @@ impl std::fmt::Display for SimplExpr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { SimplExpr::Literal(x) => write!(f, "\"{}\"", x), + SimplExpr::Concat(_, elems) => { + let text = elems + .iter() + .map(|x| match x { + SimplExpr::Literal(lit) => lit.to_string(), + other => format!("${{{}}}", other), + }) + .join(""); + write!(f, "\"{}\"", text) + } SimplExpr::VarRef(_, x) => write!(f, "{}", x), SimplExpr::BinOp(_, l, op, r) => write!(f, "({} {} {})", l, op, r), SimplExpr::UnaryOp(_, op, x) => write!(f, "{}{}", op, x), @@ -74,6 +85,7 @@ impl Spanned for SimplExpr { fn span(&self) -> Span { match self { SimplExpr::Literal(x) => x.span(), + SimplExpr::Concat(span, _) => *span, SimplExpr::VarRef(span, _) => *span, SimplExpr::BinOp(span, ..) => *span, SimplExpr::UnaryOp(span, ..) => *span, diff --git a/crates/simplexpr/src/dynval.rs b/crates/simplexpr/src/dynval.rs index 9da8e22..9ade8d8 100644 --- a/crates/simplexpr/src/dynval.rs +++ b/crates/simplexpr/src/dynval.rs @@ -119,8 +119,9 @@ impl Spanned for DynVal { } impl DynVal { - pub fn at(self, span: Span) -> Self { - DynVal(self.0, span) + pub fn at(mut self, span: Span) -> Self { + self.1 = span; + self } pub fn from_string(s: String) -> Self { diff --git a/crates/simplexpr/src/eval.rs b/crates/simplexpr/src/eval.rs index 5d02944..e3a347e 100644 --- a/crates/simplexpr/src/eval.rs +++ b/crates/simplexpr/src/eval.rs @@ -5,7 +5,7 @@ use crate::{ dynval::{ConversionError, DynVal}, }; use eww_shared_util::{Span, Spanned, VarName}; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; #[derive(Debug, thiserror::Error)] pub enum EvalError { @@ -64,6 +64,7 @@ impl SimplExpr { use SimplExpr::*; Ok(match self { BinOp(span, box a, op, box b) => BinOp(span, box a.try_map_var_refs(f)?, op, box b.try_map_var_refs(f)?), + Concat(span, elems) => Concat(span, elems.into_iter().map(|x| x.try_map_var_refs(f)).collect::>()?), UnaryOp(span, op, box a) => UnaryOp(span, op, box a.try_map_var_refs(f)?), IfElse(span, box a, box b, box c) => { IfElse(span, box a.try_map_var_refs(f)?, box b.try_map_var_refs(f)?, box c.try_map_var_refs(f)?) @@ -91,50 +92,35 @@ impl SimplExpr { /// resolve variable references in the expression. Fails if a variable cannot be resolved. pub fn resolve_refs(self, variables: &HashMap) -> Result { use SimplExpr::*; - match self { - Literal(x) => Ok(Literal(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)?)) + self.try_map_var_refs(|span, name| match variables.get(&name) { + Some(value) => Ok(Literal(value.clone())), + None => { + let similar_ish = + variables.keys().filter(|key| levenshtein::levenshtein(&key.0, &name.0) < 3).cloned().collect_vec(); + Err(EvalError::UnknownVariable(name.clone(), similar_ish).at(span)) } - 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::>()?, - )), - VarRef(span, ref name) => match variables.get(name) { - Some(value) => Ok(Literal(value.clone())), - None => { - let similar_ish = - variables.keys().filter(|key| levenshtein::levenshtein(&key.0, &name.0) < 3).cloned().collect_vec(); - Err(EvalError::UnknownVariable(name.clone(), similar_ish).at(span)) - } - }, - } + }) } - pub fn var_refs(&self) -> Vec<&VarName> { + pub fn var_refs(&self) -> HashSet<&VarName> { use SimplExpr::*; match self { - Literal(..) => Vec::new(), - VarRef(_, name) => vec![name], + Literal(..) => HashSet::new(), + Concat(_, elems) => elems.iter().flat_map(|x| x.var_refs().into_iter()).collect(), + VarRef(_, name) => maplit::hashset! { name }, BinOp(_, box a, _, box b) | JsonAccess(_, box a, box b) => { let mut refs = a.var_refs(); - refs.append(&mut b.var_refs()); + refs.extend(b.var_refs().iter()); 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.extend(b.var_refs().iter()); + refs.extend(c.var_refs().iter()); refs } - FunctionCall(_, _, args) => args.iter().flat_map(|a| a.var_refs()).collect_vec(), + FunctionCall(_, _, args) => args.iter().flat_map(|a| a.var_refs()).collect(), } } @@ -152,6 +138,14 @@ impl SimplExpr { let span = self.span(); let value = match self { SimplExpr::Literal(x) => Ok(x.clone()), + SimplExpr::Concat(span, elems) => { + let mut output = String::new(); + for elem in elems { + let result = elem.eval(values)?; + output.push_str(&result.0); + } + Ok(DynVal(output, *span)) + } SimplExpr::VarRef(span, ref name) => { let similar_ish = values.keys().filter(|keys| levenshtein::levenshtein(&keys.0, &name.0) < 3).cloned().collect_vec(); @@ -161,10 +155,10 @@ impl SimplExpr { .ok_or_else(|| EvalError::UnknownVariable(name.clone(), similar_ish).at(*span))? .at(*span)) } - SimplExpr::BinOp(_, a, op, b) => { + SimplExpr::BinOp(span, a, op, b) => { let a = a.eval(values)?; let b = b.eval(values)?; - Ok(match op { + let dynval = match op { BinOp::Equals => DynVal::from(a == b), BinOp::NotEquals => DynVal::from(a != b), BinOp::And => DynVal::from(a.as_bool()? && b.as_bool()?), @@ -185,12 +179,13 @@ impl SimplExpr { let regex = regex::Regex::new(&b.as_string()?)?; DynVal::from(regex.is_match(&a.as_string()?)) } - }) + }; + Ok(dynval.at(*span)) } - SimplExpr::UnaryOp(_, op, a) => { + SimplExpr::UnaryOp(span, op, a) => { let a = a.eval(values)?; Ok(match op { - UnaryOp::Not => DynVal::from(!a.as_bool()?), + UnaryOp::Not => DynVal::from(!a.as_bool()?).at(*span), }) } SimplExpr::IfElse(_, cond, yes, no) => { @@ -207,21 +202,21 @@ impl SimplExpr { 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)) + Ok(DynVal::from(indexed_value).at(*span)) } 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)) + Ok(DynVal::from(indexed_value).at(*span)) } _ => Err(EvalError::CannotIndex(format!("{}", val)).at(*span)), } } SimplExpr::FunctionCall(span, function_name, args) => { let args = args.into_iter().map(|a| a.eval(values)).collect::>()?; - call_expr_function(&function_name, args).map_err(|e| e.at(*span)) + call_expr_function(&function_name, args).map(|x| x.at(*span)).map_err(|e| e.at(*span)) } }; Ok(value?.at(span)) diff --git a/crates/simplexpr/src/parser/lalrpop_helpers.rs b/crates/simplexpr/src/parser/lalrpop_helpers.rs index 19e2fa5..4815872 100644 --- a/crates/simplexpr/src/parser/lalrpop_helpers.rs +++ b/crates/simplexpr/src/parser/lalrpop_helpers.rs @@ -1,7 +1,6 @@ use eww_shared_util::Span; -use itertools::Itertools; -use crate::{ast::BinOp, dynval::DynVal, SimplExpr}; +use crate::{dynval::DynVal, SimplExpr}; use super::lexer::{LexicalError, Sp, StrLitSegment, Token}; @@ -16,7 +15,8 @@ pub fn parse_stringlit( let file_id = span.2; let parser = crate::simplexpr_parser::ExprParser::new(); - segs.into_iter() + let elems = segs + .into_iter() .filter_map(|(lo, segment, hi)| { let span = Span(lo, hi, file_id); match segment { @@ -28,9 +28,6 @@ pub fn parse_stringlit( } } }) - .fold_ok(None, |acc, cur| match acc { - Some(ast) => Some(SimplExpr::BinOp(span, Box::new(ast), BinOp::Plus, Box::new(cur))), - None => Some(cur), - }) - .map(|ast| ast.unwrap_or_else(|| SimplExpr::Literal(DynVal(String::new(), span)))) + .collect::, _>>()?; + Ok(SimplExpr::Concat(span, elems)) } From 7abf38cbfca5a01395af1adf2e2b0b174803d321 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Tue, 17 Aug 2021 11:53:45 +0200 Subject: [PATCH 120/137] Include spans in var_refs of simplexpr --- crates/eww/src/eww_state.rs | 2 +- crates/eww/src/widgets/widget_node.rs | 2 +- crates/simplexpr/src/eval.rs | 10 ++--- .../simplexpr/src/parser/lalrpop_helpers.rs | 42 ++++++++++++------- crates/yuck/src/config/validate.rs | 6 ++- 5 files changed, 39 insertions(+), 23 deletions(-) diff --git a/crates/eww/src/eww_state.rs b/crates/eww/src/eww_state.rs index 5ea82e5..7bba210 100644 --- a/crates/eww/src/eww_state.rs +++ b/crates/eww/src/eww_state.rs @@ -15,7 +15,7 @@ pub struct StateChangeHandler { impl StateChangeHandler { fn used_variables(&self) -> impl Iterator { - self.unresolved_values.iter().flat_map(|(_, value)| value.var_refs()) + self.unresolved_values.iter().flat_map(|(_, value)| value.var_refs()).map(|(_, value)| value) } /// Run the StateChangeHandler. diff --git a/crates/eww/src/widgets/widget_node.rs b/crates/eww/src/widgets/widget_node.rs index 08740d5..70d36a4 100644 --- a/crates/eww/src/widgets/widget_node.rs +++ b/crates/eww/src/widgets/widget_node.rs @@ -80,7 +80,7 @@ impl Generic { /// returns all the variables that are referenced in this widget pub fn referenced_vars(&self) -> impl Iterator { - self.attrs.iter().flat_map(|(_, value)| value.var_refs()) + self.attrs.iter().flat_map(|(_, value)| value.var_refs()).map(|(_, value)| value) } } diff --git a/crates/simplexpr/src/eval.rs b/crates/simplexpr/src/eval.rs index e3a347e..13ba3f4 100644 --- a/crates/simplexpr/src/eval.rs +++ b/crates/simplexpr/src/eval.rs @@ -5,7 +5,7 @@ use crate::{ dynval::{ConversionError, DynVal}, }; use eww_shared_util::{Span, Spanned, VarName}; -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; #[derive(Debug, thiserror::Error)] pub enum EvalError { @@ -102,12 +102,12 @@ impl SimplExpr { }) } - pub fn var_refs(&self) -> HashSet<&VarName> { + pub fn var_refs(&self) -> Vec<(Span, &VarName)> { use SimplExpr::*; match self { - Literal(..) => HashSet::new(), + Literal(..) => Vec::new(), + VarRef(span, name) => vec![(*span, name)], Concat(_, elems) => elems.iter().flat_map(|x| x.var_refs().into_iter()).collect(), - VarRef(_, name) => maplit::hashset! { name }, BinOp(_, box a, _, box b) | JsonAccess(_, box a, box b) => { let mut refs = a.var_refs(); refs.extend(b.var_refs().iter()); @@ -141,7 +141,7 @@ impl SimplExpr { SimplExpr::Concat(span, elems) => { let mut output = String::new(); for elem in elems { - let result = elem.eval(values)?; + let result = dbg!(elem.eval(values))?; output.push_str(&result.0); } Ok(DynVal(output, *span)) diff --git a/crates/simplexpr/src/parser/lalrpop_helpers.rs b/crates/simplexpr/src/parser/lalrpop_helpers.rs index 4815872..fd7a1a2 100644 --- a/crates/simplexpr/src/parser/lalrpop_helpers.rs +++ b/crates/simplexpr/src/parser/lalrpop_helpers.rs @@ -10,24 +10,36 @@ pub fn b(x: T) -> Box { pub fn parse_stringlit( span: Span, - segs: Vec>, + mut segs: Vec>, ) -> Result> { let file_id = span.2; let parser = crate::simplexpr_parser::ExprParser::new(); - let elems = segs - .into_iter() - .filter_map(|(lo, segment, hi)| { - let span = Span(lo, hi, file_id); - match segment { - StrLitSegment::Literal(lit) if lit.is_empty() => None, - StrLitSegment::Literal(lit) => Some(Ok(SimplExpr::Literal(DynVal(lit, span)))), - StrLitSegment::Interp(toks) => { - let token_stream = toks.into_iter().map(|x| Ok(x)); - Some(parser.parse(file_id, token_stream)) - } + if segs.len() == 1 { + let (lo, seg, hi) = segs.remove(0); + let span = Span(lo, hi, file_id); + match seg { + StrLitSegment::Literal(lit) => Ok(SimplExpr::Literal(DynVal(lit, span))), + StrLitSegment::Interp(toks) => { + let token_stream = toks.into_iter().map(|x| Ok(x)); + parser.parse(file_id, token_stream) } - }) - .collect::, _>>()?; - Ok(SimplExpr::Concat(span, elems)) + } + } else { + let elems = segs + .into_iter() + .filter_map(|(lo, segment, hi)| { + let span = Span(lo, hi, file_id); + match segment { + StrLitSegment::Literal(lit) if lit.is_empty() => None, + StrLitSegment::Literal(lit) => Some(Ok(SimplExpr::Literal(DynVal(lit, span)))), + StrLitSegment::Interp(toks) => { + let token_stream = toks.into_iter().map(|x| Ok(x)); + Some(parser.parse(file_id, token_stream)) + } + } + }) + .collect::, _>>()?; + Ok(SimplExpr::Concat(span, elems)) + } } diff --git a/crates/yuck/src/config/validate.rs b/crates/yuck/src/config/validate.rs index 8a3818b..309b806 100644 --- a/crates/yuck/src/config/validate.rs +++ b/crates/yuck/src/config/validate.rs @@ -87,7 +87,11 @@ pub fn validate_variables_in_widget_use( let values = widget.attrs.attrs.values(); let unknown_var = values.filter_map(|value| value.value.as_simplexpr().ok()).find_map(|expr: SimplExpr| { let span = expr.span(); - expr.var_refs().iter().map(move |&x| (span, x.clone())).find(|(span, var_ref)| !variables.contains(var_ref)) + expr.var_refs() + .iter() + .cloned() + .map(|(span, var_ref)| (span, var_ref.clone())) + .find(|(_, var_ref)| !variables.contains(var_ref)) }); if let Some((span, var)) = unknown_var { return Err(ValidationError::UnknownVariable { span, name: var.clone(), in_definition: is_in_definition }); From 1e1c0fcce3242687b994c7779e00167d6f8aa471 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Tue, 17 Aug 2021 12:02:29 +0200 Subject: [PATCH 121/137] cleanup example --- examples/eww-bar/eww.scss | 10 +++------- examples/eww-bar/eww.yuck | 22 +++++++--------------- 2 files changed, 10 insertions(+), 22 deletions(-) diff --git a/examples/eww-bar/eww.scss b/examples/eww-bar/eww.scss index 139c3ee..845e38f 100644 --- a/examples/eww-bar/eww.scss +++ b/examples/eww-bar/eww.scss @@ -3,13 +3,13 @@ } //Global Styles -window { +.bar { background-color: #3a3a3a; color: #b0b4bc; - font-family: CascadiaCode; + padding: 10px; } -// Styles on classes (see eww.xml for more information) +// Styles on classes (see eww.yuck for more information) .sidestuff slider { all: unset; @@ -49,11 +49,7 @@ window { .label-ram { font-size: large; } -.time { - padding-right: 10px; -} .workspaces button:hover { color: #D35D6E; } - diff --git a/examples/eww-bar/eww.yuck b/examples/eww-bar/eww.yuck index ce66e45..844d12e 100644 --- a/examples/eww-bar/eww.yuck +++ b/examples/eww-bar/eww.yuck @@ -1,5 +1,5 @@ (defwidget bar [] - (box :orientation "h" :hexpand true + (centerbox :orientation "h" (workspaces) (music) (sidestuff))) @@ -8,13 +8,14 @@ (box :class "sidestuff" :orientation "h" :space-evenly false :halign "end" (slider-vol) (slider-ram) - (time))) + time)) (defwidget workspaces [] (box :class "workspaces" :orientation "h" :space-evenly true :halign "start" + :spacing 10 (button :onclick "wmctrl -s 0" 1) (button :onclick "wmctrl -s 1" 2) (button :onclick "wmctrl -s 2" 3) @@ -27,12 +28,12 @@ (defwidget music [] (box :class "music" :orientation "h" :space-evenly false :halign "center" - ' ${music}')) + '🎵${music}')) (defwidget slider-vol [] (box :class "slider-vol" :orientation "h" :space-evenly "false" - (box :class "label-vol" "" - (scale :min 0 :max 101 :value volume :onchange "amixer -D pulse sset Master \{}%")))) + (box :class "label-vol" "🔊" + (scale :min 0 :max 101 :value volume :onchange "amixer -D pulse sset Master {}%")))) (defwidget slider-ram [] (box :orientation "h" :class "slider-ram" :space-evenly false @@ -41,21 +42,12 @@ -(defwidget time [] - (box :class "time" - "${hour}:${min} ${month} ${number_day}, ${year_full}")) (defpoll music :interval "5s" "playerctl metadata --format '{{ artist }} - {{ title }}' || true") (defpoll volume :interval "1s" "scripts/getvol") -(defpoll number_day :interval "5h" "date '+%d'") -(defpoll month :interval "10h" "date '+%b'") -(defpoll min :interval "10s" "date '+%M'") -(defpoll hour :interval "1m" "date '+%H'") -(defpoll year_full :interval "15h" "date '+%Y'") - -(deflisten battery-remaining "tail -f /sys/class/power_supply/BAT0/capacity") +(defpoll time :interval "10s" "date '+%H:%M %b %d, %Y'") (defwindow bar :monitor 0 From 8faedb0e895a3b88d1f4be9706fdc5cae7babae6 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Tue, 17 Aug 2021 12:04:42 +0200 Subject: [PATCH 122/137] Fix documentation on literal widget --- crates/eww/src/widgets/widget_definitions.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/eww/src/widgets/widget_definitions.rs b/crates/eww/src/widgets/widget_definitions.rs index 0f255ad..50f9a64 100644 --- a/crates/eww/src/widgets/widget_definitions.rs +++ b/crates/eww/src/widgets/widget_definitions.rs @@ -557,7 +557,7 @@ fn build_gtk_label(bargs: &mut BuilderArgs) -> Result { } /// @widget literal -/// @desc A widget that allows you to render arbitrary XML. +/// @desc A widget that allows you to render arbitrary yuck. fn build_gtk_literal(bargs: &mut BuilderArgs) -> Result { let gtk_widget = gtk::Box::new(gtk::Orientation::Vertical, 0); gtk_widget.set_widget_name("literal"); @@ -571,7 +571,7 @@ fn build_gtk_literal(bargs: &mut BuilderArgs) -> Result { let literal_file_id: Rc>> = Rc::new(RefCell::new(None)); resolve_block!(bargs, gtk_widget, { - // @prop content - inline Eww XML that will be rendered as a widget. + // @prop content - inline yuck that will be rendered as a widget. prop(content: as_string) { gtk_widget.get_children().iter().for_each(|w| gtk_widget.remove(w)); if !content.is_empty() { From e200f5ef2b7e1d58e561bbe76d6fedc10777e04a Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Mon, 16 Aug 2021 16:31:46 +0200 Subject: [PATCH 123/137] Write defwindow documentation --- docs/src/configuration.md | 309 ++++++-------------------------------- 1 file changed, 45 insertions(+), 264 deletions(-) diff --git a/docs/src/configuration.md b/docs/src/configuration.md index dc55bcb..2753ea2 100644 --- a/docs/src/configuration.md +++ b/docs/src/configuration.md @@ -1,277 +1,58 @@ -# Configuration +# Writing your eww configuration -For specific built in widgets `, , , etc` see [Widget Documentation](widgets.md) +(For a list of all built in widgets (i.e. `box`, `text`, `slider`) see [Widget Documentation](widgets.md)) -## Placing the configuration file +Eww is configured using it's own language called `yuck`. using yuck, you declare the structure and content of your widgets, the geometry, position and behavior of any windows, as well as any state and data that will be used in your widgets. Yuck is based around S-expressions, which you may know from lisp-like languages. If you're using vim, you can make use of [yuck.vim](https://github.com/elkowar/yuck.vim) for editor support. It is also recommended to use [parinfer](https://shaunlebron.github.io/parinfer/), which makes working with s-expressions delightfully easy! -Note: Example configuration files can be found in the `examples` directory of the repository and are showcased in [Examples](examples.md). +Additionally, any styles are defined in scss (which is mostly just slightly improved CSS syntax). While eww supports a significant portion of the CSS you know from the web, not everything is supported, as eww relies on GTKs own CSS engine. Notably, some animation features are unsupported, as well as most layout-related CSS properties such as flexbox, `float`, absolute position or `width`/`height`. -The configuration file and the scss file should lay in `$XDG_CONFIG_HOME/eww` (or, if unset, `$HOME/.config/eww`). The XML file should be named `eww.xml` and the scss should be named `eww.scss` -So the directory structure should look like this: -``` -$HOME -└──.config - ──eww - ├──eww.xml - └──eww.scss +To get started, you'll need to create two files: `eww.yuck` and `eww.scss`. These files must be placed under `$XDG_CONFIG_HOME/eww` (this is most likely `~/.config/eww`). + +Now that those files are created, you can start writing your first widget! + +## Creating your first window + +Firstly, you will need to create a top-level window. Here, you configure things such as the name, position, geometry and content of your window. + +Let's look at an example window definition: + +```lisp +(defwindow example + :monitor 0 + :geometry (geometry :x 0 :y 0 :width "90%" :height "30px") + :anchor "top center" + :stacking "fg" + :reserve (struts :distance "40px" :side "top") + :windowtype "dock" + :wm-ignore false + "example content") ``` -## Config structure +Here, we are defining a window named `example`, which we then set a set of properties for. Additionally, we set the content of the window to be the text `"example content"`. -Your config structure should look like this: -```xml - - - - +### `defwindow`-Properties - - - +| Property | Description | +| ---------: | ------------------------------------------------------------ | +| `monitor` | which monitor this window should be displayed on | +| `geometry` | Position and size of the window. Values may be provided in `px` or relative to the screen size. | +| `anchor` | What side of the screen the window should be anchored to. Either `center` or combinations of `top`, `center`, `bottom` and `left`, `center`, `right` | - - - +Depending on if you are using X11 or wayland, some additional properties exist: - - - - -``` -See -[The `` block](#the-includes-block), -[The `` block](#the-definitions-block), -[Variables](#variables) and the -[The `` block](#the-windows-block). +#### x11 -## Variables +| Property | Description | +| -----------: | ------------------------------------------------------------ | +| `stacking` | Where the window should appear in the stack. Possible values: `fg`, `bg`. | +| `wm-ignore` | Whether the windowmanager should ignore this window. This is useful for dashboard-style widgets that don't need to interact with other windows at all. Note that this makes some of the other properties not have any effect. Either `true` or `false` | +| `reserve` | Specify how the window-manager should make space for your window. This is useful for bars, which should not overlap any other windows. | +| `windowtype` | Specify what type of window this is. This will be used by your window manager to determine how it should handle your window. Possible values: `normal`, `dock`, `toolbar`, `dialog`. Default: `dock` if `reserve` is specified, `normal` otherwise. | -If you create a `` or a ``, you can reference them in your `` by doing `{{var}}`. Where `var` is your variable name. +#### wayland - -### The `` tag -Allows you to repeat the same text multiple times through without retyping it multiple times. - -Example: This will define a variable named `banana`, with the default value "I like bananas." -```xml - - I like bananas. - -``` -You can then reference it in your widgets by doing: - -```xml - - {{banana}} - -``` - -To change the value of the variable, and thus change the UI, you can run `eww update banana "I like apples"` - -### The `` tag - -Allows you to create a script that eww runs. -Useful for creating volume sliders or anything similar. - -Example: -```xml - - - date +%H:%M - - -``` - -and then reference it by doing: -```xml - - {{date}} - -``` - -The `interval="5s"` part says how long time it should take before Eww runs the command again. -Here are the available times you can set: - -| Shortened | Full name | -|-----------|-------------| -| ms | Miliseconds | -| s | Seconds | -| m | Minutes | -| h | Hours | - - -### Tail -If you don't want a set interval and instead want it to tail (run the script when it detects a change is present) you can simply remove the `interval="5s"` so it becomes: -```xml - - - date +%H:%M - - -``` -## The `` block -Here you can include other config files so that they are merged together at startup. Currently namespaced variables are not supported so be careful when reusing code. - -```xml - - - - -``` - -If you define a variable/widget/window, in a config file, when it's defined somewhere else, you can see a warning in the eww logs (`eww logs`) - -## The `` block -In here your whole widget will be made, and you can also create your own widgets. Check [Widget Documentation](widgets.md) for pre-defined widgets. - -### Custom widgets - -Let's get a small config and break it down. - -```xml - - - - The time is: {{my_time}} currently. - - - - - - - - - - - - date - - -``` -That's a long config just for a custom widget. But let's break it down and try to understand it. - -This part: -```xml - - - The time is: {{my_time}} currently. - - -``` -Is the custom widget. As we can see by the -```xml - -``` -the widget is called `clock.`Or referenced `` -The `{{my_time}}` is the value we assign to be well, our time. You can actually set to be anything, it doesn't have to be a time. You can compare it to `value=""` - -So if we look at: -```xml - - - - - -``` -we can see that we assign `{{my_time}}` to be `{{date}}` and if we look at -```xml - - date - -``` -we can see that `{{date}}` is simply running the `date` command. - -It doesn't have to be `{{my_time}}` either, it can be anything. -```xml - - - The time is: {{very_long_list_of_animals}} currently. - - -``` -is valid. - -To use that it would look like this: -```xml - - - - - -``` -## The `` block - -All different windows you might want to use are defined in the `` block. -The `` config should look something like this: - -```xml - - - - - -
- - - -``` - -For Wayland users the `` block is replaced by the exclusive field in ``. -The previous `` block would look like this. - -```xml - - - -
- - -``` - -The window block contains multiple elements to configure the window. -- `` is used to specify the position and size of the window. -- `` is used to have eww reserve space at a given side of the screen the widget is on. -- `` will contain the widget that is shown in the window. - -There are a couple things you can optionally configure on the window itself: -- `stacking`: stacking describes on what "layer" of the screen the window is shown. - Possible values on the X11 backend: `foreground "fg"`, `background "bg"`. Default: `"fg"` - Possible values on the Wayland backend: `foreground "fg"`, `bottom "bt"`, `background "bg"`, `overlay "ov"`. Default: `"fg"` -- `screen`: Specifies on which display to show the window in a multi-monitor setup. - This can be any number, representing the index of your monitor. -- `exclusive`: Specifies whether or not a surface can be occupied by another. - A surface can be a window, an Eww widget or any layershell surface. - The details on how it is actually implemented are left to the compositor. - This option is only valid on Wayland. - Possible values: `"true"`, `"false"`. Default: `"false"` -- `focusable`: (Wayland only) whether the window should be able to capture keyboard input. - Possible values: `"true"`, `"false"`. Default: `"false"` -- `wm-ignore`: (X11 only) wether the window should be managed by the window manager. - For a centered widget setup this is recommended to be set to true. For a bar, set the windowtype to `dock` instead. - Note that setting `wm-ignore` will make some other options not work, as those rely on the window manager. - Possible values: `"true"`, `"false"`. Default: `"true"` except if `` is set. -- `windowtype`: (X11 only) Can be used in determining the decoration, stacking position and other behavior of the window. - Window managers tend to interpret these differently, so play around with which one works for your usecase! - Possible values: - - `"normal"`: indicates that this is a normal, top-level window - - `"dock"`: indicates a bar, dock, or panel window - - `"utility"`: indicates a pinned utility window - - `"toolbar"`: toolbars "torn off" from the main application - - `"dialog"`: indicates that this is a dialog window - - Default: `"dock"` -- `sticky`: (X11 only) If the window should show up on all workspaces. Note that this may not have any effect, depending on your window manager and the window type. - Possible values: `"true"`, `"false"`. Default: `"true"` -- `resizable`: (X11 only) If the window should be resizable. Note that this may not have any effect, depending on your window manager and the window type. - Possible values: `"true"`, `"false"`. Default: `"true"` - - -### Recommendations for different use-cases on X - -Window positioning is... weird on X11. Different window-managers handle things differently, and some things are just not compatible. -Thus, the following setups are recommendations that will _probably_ work. If they don't try to play around with different settings for any of the X11 only properties. - -- For a bar: - - Set `windowtype` to `dock`, and provide a `reserve` configuration to match your window geometry to make the WM reserve space. - - Set `wm-ignore` to `false`. -- For a centered, full-screen widget setup: - - Set `wm-ignore` to `true`. +| Property | Description | +| ----------: | ------------------------------------------------------------ | +| `stacking` | Where the window should appear in the stack. Possible values: `fg`, `bg`, `overlay`, `bottom`. | +| `exclusive` | Whether the compositor should reserve space for the window automatically. | +| `focusable` | Whether the window should be able to be focused. This is necessary for any widgets that use the keyboard to work. | From 20f3d3c10d9aa70d1c5c848324e93e17b891b78b Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Mon, 16 Aug 2021 17:33:33 +0200 Subject: [PATCH 124/137] Document widgets and variables --- docs/src/configuration.md | 90 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 89 insertions(+), 1 deletion(-) diff --git a/docs/src/configuration.md b/docs/src/configuration.md index 2753ea2..3759cf0 100644 --- a/docs/src/configuration.md +++ b/docs/src/configuration.md @@ -2,7 +2,7 @@ (For a list of all built in widgets (i.e. `box`, `text`, `slider`) see [Widget Documentation](widgets.md)) -Eww is configured using it's own language called `yuck`. using yuck, you declare the structure and content of your widgets, the geometry, position and behavior of any windows, as well as any state and data that will be used in your widgets. Yuck is based around S-expressions, which you may know from lisp-like languages. If you're using vim, you can make use of [yuck.vim](https://github.com/elkowar/yuck.vim) for editor support. It is also recommended to use [parinfer](https://shaunlebron.github.io/parinfer/), which makes working with s-expressions delightfully easy! +Eww is configured using its own language called `yuck`. using yuck, you declare the structure and content of your widgets, the geometry, position and behavior of any windows, as well as any state and data that will be used in your widgets. Yuck is based around S-expressions, which you may know from lisp-like languages. If you're using vim, you can make use of [yuck.vim](https://github.com/elkowar/yuck.vim) for editor support. It is also recommended to use [parinfer](https://shaunlebron.github.io/parinfer/), which makes working with s-expressions delightfully easy! Additionally, any styles are defined in scss (which is mostly just slightly improved CSS syntax). While eww supports a significant portion of the CSS you know from the web, not everything is supported, as eww relies on GTKs own CSS engine. Notably, some animation features are unsupported, as well as most layout-related CSS properties such as flexbox, `float`, absolute position or `width`/`height`. @@ -30,6 +30,8 @@ Let's look at an example window definition: Here, we are defining a window named `example`, which we then set a set of properties for. Additionally, we set the content of the window to be the text `"example content"`. +You can now open your first window by running `eww open example`! Glorious! + ### `defwindow`-Properties | Property | Description | @@ -56,3 +58,89 @@ Depending on if you are using X11 or wayland, some additional properties exist: | `stacking` | Where the window should appear in the stack. Possible values: `fg`, `bg`, `overlay`, `bottom`. | | `exclusive` | Whether the compositor should reserve space for the window automatically. | | `focusable` | Whether the window should be able to be focused. This is necessary for any widgets that use the keyboard to work. | + + + +## Your first widget + +While our bar is already looking great, it's a bit boring. Thus, let's add some actual content! + +```lisp +(defwidget greeter [text name] + (box :orientation "horizontal" + :halign "center" + text + (button :onclick "notify-send 'Hello' 'Hello, ${name}'" + "Greet"))) +``` + +To show this, let's replace the text in our window definition with a call to this new widget: + +```lisp +(defwindow example + ; ... values omitted + (greeter :text "Say hello!" + :name "Tim")) +``` + +There is a lot going on here, so let's step through this. + +We are creating a widget named `greeter`. This widget takes two attributes, called `text` and `name`, which must be set when the widget is used. + +Now, we declare the body of our widget. We make use of a `box`, which we set a couple attributes of. This box then contains a reference to the provided attribute `text`, as well as a button. In that buttons `onclick` attribute, we make refer to the provided `name` using string-interpolation syntax: `"${name}"`. This allows us to easily refer to any variables within strings. In fact, there is a lot more you can do withing `${...}` - more on that in the chapter about the [expression language](expression_language.md). + +To then use our widget, we call it just like we would use any other built-in widget, and provide the required attributes. + +As you may have noticed, we are using a couple predefined widgets here. These are all listed and explained in the [widgets chapter](widgets.md). + + + +## Adding dynamic content + +Now that you feel sufficiently greeted by your bar, you may realize that showing data like the time and date might be even more useful than having a button that greets you. + +To implement dynamic content in your widgets you make use of _variables_. + +These user-defined variables are globally available from all of your widgets. Whenever the variable changes, the value in the widget will update! + +There are four different types of variables: basic, polling, listening, and a set of builtin "magic" variables. + +**Basic variables (`defvar`)** + +```lisp +(defvar foo "initial value") +``` + +This is the simplest type of variable. Basic variables don't ever change automatically. Instead, you explicitly update them by calling eww like so: `eww update foo="new value"`. + +This is useful if you have values that change very rarely, or may change as a result of some external script you wrote. They may also be useful to have buttons within eww change what is shown within your widget, by setting attributes like `onclick` to run `eww update`. + +**Polling variables (`defpoll`)** + +```lisp +(defpoll time :interval "1s" + :timeout "0.1s" ; setting timeout is optional + `date +%H:%M:%S`) +``` + +A polling variable is a variable which runs a provided shell-script repeatedly, in a given interval. + +This may be the most commonly used type of variable. They are useful to access any quickly retrieved value repeatedly, and thus are the perfect choice for showing your time, date, as well as other bits of information such as your volume. + +Optionally, you can specify a timeout, after which the provided script will be aborted. This helps to avoid accidentally launching thousands of never-ending processes on your system. + +**Listening variables (`deflisten`)** + +```lisp +(deflisten foo :initial "whatever" + `tail -F /tmp/some_file`) +``` + +Listening variables might be the most confusing of the bunch. A listening variable runs a script once, and reads its output continously. Whenever the script outputs a new line, the value will be updated to that new line. In the example given above, the value of `foo` will start out as `"whatever"`, and will change whenever a new line is appended to `/tmp/some_file`. + +These are particularly useful if you have a script that can monitor some value on its own. For example, the command `xprop -spy -root _NET_CURRENT_DESKTOP` writes the currently focused desktop whenever it changes. This can be used to implement a workspace widget for a bar, for example. Another example usecase is monitoring the currently playing song with playerctl: `playerctl --follow metadata --format {{title}}`. + +**Built-in "magic" variables** + +In addition to definition your own variables, eww provides some values for you to use out of the box. These include values such as your CPU and RAM usage. These mostly contain their data as JSON, which you can then use using the [json access syntax](expression_language.md). All available magic variables are listed [here](magic-vars.md). + From e750abf38cd709a4d0155a0322b28c4efffc06e4 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Mon, 16 Aug 2021 18:01:20 +0200 Subject: [PATCH 125/137] Fix eww expression and start page, add includes to configuration page --- docs/src/configuration.md | 17 +++++++++++++++++ docs/src/eww.md | 5 ++--- docs/src/expression_language.md | 28 +++++++++++++--------------- docs/src/troubleshooting.md | 29 +++++++---------------------- 4 files changed, 39 insertions(+), 40 deletions(-) diff --git a/docs/src/configuration.md b/docs/src/configuration.md index 3759cf0..127b95b 100644 --- a/docs/src/configuration.md +++ b/docs/src/configuration.md @@ -144,3 +144,20 @@ These are particularly useful if you have a script that can monitor some value o In addition to definition your own variables, eww provides some values for you to use out of the box. These include values such as your CPU and RAM usage. These mostly contain their data as JSON, which you can then use using the [json access syntax](expression_language.md). All available magic variables are listed [here](magic-vars.md). +## Splitting up your configuration + +As time passes, your configuration might grow larger and larger. Luckily, you can easily split up your configuration into multiple files! + +There are two options to achieve this: + +### Using `include` + +```lisp +(include "./path/to/your/file.yuck") +``` + +A single yuck-file may import the contents of any other yuck file. For this, make use of the `include` directive. + +### Using a separate eww configuration directory + +If you want to separate different widgets even further, you can create a new eww config folder anywhere else. Then, you can tell eww to use that configuration directory by passing _every_ command the `--config /path/to/your/config/dir` flag. Make sure to actually include this in all your `eww` calls, including `eww kill`, `eww logs`, etc. This launches a separate instance of the eww daemon, that has separate logs and state from your main eww configuration. diff --git a/docs/src/eww.md b/docs/src/eww.md index 0974c33..44c3b9c 100644 --- a/docs/src/eww.md +++ b/docs/src/eww.md @@ -5,7 +5,7 @@ is a widget system made in [rust](https://www.rust-lang.org/), which let's you create your own widgets similarly to how you can in AwesomeWM. The key difference: It is independent of your window manager! -Configured in XML and themed using CSS, it is easy to customize and provides all the flexibility you need! +Configured in yuck and themed using CSS, it is easy to customize and provides all the flexibility you need! ## How to install Eww @@ -21,7 +21,7 @@ as this makes it easy to use the nightly toolchain necessary to build eww. ### Building -Once you have the Prerequisites ready, you're ready to install and build eww. +Once you have the prerequisites ready, you're ready to install and build eww. First clone the repo: ```bash @@ -55,4 +55,3 @@ and then to run it do ./eww daemon ./eww open ``` -`` is the name of the window, see [The windows block](configuration.md#windows-block). diff --git a/docs/src/expression_language.md b/docs/src/expression_language.md index bb72b90..d1f2219 100644 --- a/docs/src/expression_language.md +++ b/docs/src/expression_language.md @@ -1,31 +1,29 @@ -# The embedded Eww expression-language +# Simple 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}}`). +Yuck includes a small expression language that can be used to run several operations on your data. This can be used to show different values depending on certain conditions, do mathematic operations, and even to access values withing JSON-structures. + +These expressions can be placed anywhere within your configuration in between `{ ... }`, as well as withing strings, inside string-interpolation blocks (`"foo ${ ... } bar"`). ## Example -```xml - - -Some math: {{12 + 2 * 10}} +```lisp +(box + "Some math: ${12 + foo * 10}" + (button :class {button_active ? "active" : "inactive"} + :onclick "toggle_thing" + {button_active ? "disable" : "enable"})) ``` -## Syntax +## Features -The expression language supports: +Supported currently are the following features: - simple mathematical operations (`+`, `-`, `*`, `/`, `%`) - comparisons (`==`, `!=`, `>`, `<`) - boolean operations (`||`, `&&`, `!`) - elvis operator (`?:`) - if the left side is `""`, then returns the right side, otherwise evaluates to the left side. -- conditionals (`if condition then 'value' else 'other value'`) +- conditionals (`condition ? 'value' : 'other value'`) - numbers, strings, booleans and variable references (`12`, `'hi'`, `true`, `some_variable`) - - strings can contain other expressions again: `'foo {{some_variable}} bar'` - json access (`object.field`, `array[12]`, `object["field"]`) - for this, the object/array value needs to refer to a variable that contains a valid json string. - some function calls: diff --git a/docs/src/troubleshooting.md b/docs/src/troubleshooting.md index fdd1d36..d765869 100644 --- a/docs/src/troubleshooting.md +++ b/docs/src/troubleshooting.md @@ -12,39 +12,24 @@ Here you will find help if something doesn't work, if the issue isn't listed her 1. Make sure you compiled eww with the `--no-default-features --features=wayland` flags. 2. Make sure that you're not trying to use X11-specific features (these are (hopefully) explicitly specified as such in the documentation). -## My scss isn't being loaded! +## My configuration is not loaded correctly -1. You have not created a scss file -2. The scss file isn't called correctly. (it should be called `eww.scss` in the `$HOME/.config/eww` folder) -3. The scss file isn't placed in the correct location (check above) - -If none of these fixed your problem [open an issue on the GitHub repo](https://github.com/elkowar/eww/issues), or check the [GTK-Debugger](working_with_gtk.md#gtk-debugger). - -## Eww can't find my configuration file! - -1. It's incorrectly named or it's in the wrong place (it should be called `eww.xml` in the `$HOME/.config/eww` folder) -2. You haven't started eww correctly or you started it wrong. +1. Make sure the `eww.yuck` and `eww.scss` files are in the correct places +2. Sometimes, eww might fail to load your configuration as a result of a configuration error. Make sure your configuration is valid. ## Something isn't styled correctly! -1. You have mistyped the CSS class. -2. Check the [GTK-Debugger](working_with_gtk.md#gtk-debugger) +Check the [GTK-Debugger](working_with_gtk.md#gtk-debugger) to get more insight into what styles GTK is applying to which elements. ## General issues You should try the following things, before opening a issue or doing more specialized troubleshooting: -- Kill the eww daemon by running `eww kill` and restart it with `eww --debug daemon` to get additional log output. +- Kill the eww daemon by running `eww kill` and re-open your window with the `--debug`-flag to get additional log output. - Now you can take a look at the logs by running `eww logs`. - use `eww state`, to see the state of all variables -- use `eww debug`, to see the xml of your widget and other information +- use `eww debug`, to see the structure of your widget and other information - update to the latest eww version -- sometimes hot reloading doesn't work. restart the widget in that case - -If you're experiencing issues printing variables, try to print them in quotes, so e.g. - -``` -onchange="notify-send '{}'" -``` +- sometimes hot reloading doesn't work. In that case, you can make use of `eww reload` manually. Remember, if your issue isn't listed here, [open an issue on the GitHub repo](https://github.com/elkowar/eww/issues). From a7d9d0a6fe50747a4a0aa737996413b43d625fa3 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Mon, 16 Aug 2021 18:09:50 +0200 Subject: [PATCH 126/137] mention literal explicitly --- docs/src/configuration.md | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/docs/src/configuration.md b/docs/src/configuration.md index 127b95b..6ae579f 100644 --- a/docs/src/configuration.md +++ b/docs/src/configuration.md @@ -87,7 +87,9 @@ There is a lot going on here, so let's step through this. We are creating a widget named `greeter`. This widget takes two attributes, called `text` and `name`, which must be set when the widget is used. -Now, we declare the body of our widget. We make use of a `box`, which we set a couple attributes of. This box then contains a reference to the provided attribute `text`, as well as a button. In that buttons `onclick` attribute, we make refer to the provided `name` using string-interpolation syntax: `"${name}"`. This allows us to easily refer to any variables within strings. In fact, there is a lot more you can do withing `${...}` - more on that in the chapter about the [expression language](expression_language.md). +Now, we declare the body of our widget. We make use of a `box`, which we set a couple attributes of. + +We need this `box`, as a widget definition can only ever contain a single widget - otherwise, eww would not know if it should align them vertically or horizontally, how it should space them, and so on. Thus, we wrap multiple children in a `box.`. This box then contains a reference to the provided attribute `text`, as well as a button. In that buttons `onclick` attribute, we make refer to the provided `name` using string-interpolation syntax: `"${name}"`. This allows us to easily refer to any variables within strings. In fact, there is a lot more you can do withing `${...}` - more on that in the chapter about the [expression language](expression_language.md). To then use our widget, we call it just like we would use any other built-in widget, and provide the required attributes. @@ -144,6 +146,22 @@ These are particularly useful if you have a script that can monitor some value o In addition to definition your own variables, eww provides some values for you to use out of the box. These include values such as your CPU and RAM usage. These mostly contain their data as JSON, which you can then use using the [json access syntax](expression_language.md). All available magic variables are listed [here](magic-vars.md). +## Dynamically generated widgets with `literal` + +In some cases, you want to not only change the text, value or color of a widget dynamically, but instead want to generate an entire widget structure dynamically. This is necessary if you want to display lists of things (for example notifications) where the amount is not necessarily known, or if you want to change the widget structure in some other more complex way. + +For this, you can make use of one of ewws most powerful features: the `literal` widget. + +```lisp +(defvar variable_containing_yuck + "(box (button 'foo') (button 'bar'))") +(literal :content variable_containing_yuck) +``` + +Here, you specify the content of your literal by providing it a string (most likely stored in a variable) which contains a single yuck widget tree. Eww then reads the provided value and renders the resulting widget. Whenever it changes, the widget will be rerendered. + +Note that this is not all that efficient. Make sure to only use `literal` when necessary! + ## Splitting up your configuration As time passes, your configuration might grow larger and larger. Luckily, you can easily split up your configuration into multiple files! @@ -161,3 +179,4 @@ A single yuck-file may import the contents of any other yuck file. For this, mak ### Using a separate eww configuration directory If you want to separate different widgets even further, you can create a new eww config folder anywhere else. Then, you can tell eww to use that configuration directory by passing _every_ command the `--config /path/to/your/config/dir` flag. Make sure to actually include this in all your `eww` calls, including `eww kill`, `eww logs`, etc. This launches a separate instance of the eww daemon, that has separate logs and state from your main eww configuration. + From 3d6e9a9ba17ec808f615f48ac0f7f272c1ae7ff3 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Tue, 17 Aug 2021 11:14:16 +0200 Subject: [PATCH 127/137] Reformat markdown --- docs/src/configuration.md | 68 ++++++++++++++++++++++++++------- docs/src/expression_language.md | 7 +++- docs/src/working_with_gtk.md | 4 +- 3 files changed, 61 insertions(+), 18 deletions(-) diff --git a/docs/src/configuration.md b/docs/src/configuration.md index 6ae579f..e46c4d4 100644 --- a/docs/src/configuration.md +++ b/docs/src/configuration.md @@ -2,11 +2,22 @@ (For a list of all built in widgets (i.e. `box`, `text`, `slider`) see [Widget Documentation](widgets.md)) -Eww is configured using its own language called `yuck`. using yuck, you declare the structure and content of your widgets, the geometry, position and behavior of any windows, as well as any state and data that will be used in your widgets. Yuck is based around S-expressions, which you may know from lisp-like languages. If you're using vim, you can make use of [yuck.vim](https://github.com/elkowar/yuck.vim) for editor support. It is also recommended to use [parinfer](https://shaunlebron.github.io/parinfer/), which makes working with s-expressions delightfully easy! +Eww is configured using its own language called `yuck`. +using yuck, you declare the structure and content of your widgets, the geometry, position and behavior of any windows, +as well as any state and data that will be used in your widgets. +Yuck is based around S-expressions, which you may know from lisp-like languages. +If you're using vim, you can make use of [yuck.vim](https://github.com/elkowar/yuck.vim) for editor support. +It is also recommended to use [parinfer](https://shaunlebron.github.io/parinfer/), +which makes working with s-expressions delightfully easy! -Additionally, any styles are defined in scss (which is mostly just slightly improved CSS syntax). While eww supports a significant portion of the CSS you know from the web, not everything is supported, as eww relies on GTKs own CSS engine. Notably, some animation features are unsupported, as well as most layout-related CSS properties such as flexbox, `float`, absolute position or `width`/`height`. +Additionally, any styles are defined in scss (which is mostly just slightly improved CSS syntax). +While eww supports a significant portion of the CSS you know from the web, +not everything is supported, as eww relies on GTKs own CSS engine. +Notably, some animation features are unsupported, +as well as most layout-related CSS properties such as flexbox, `float`, absolute position or `width`/`height`. -To get started, you'll need to create two files: `eww.yuck` and `eww.scss`. These files must be placed under `$XDG_CONFIG_HOME/eww` (this is most likely `~/.config/eww`). +To get started, you'll need to create two files: `eww.yuck` and `eww.scss`. +These files must be placed under `$XDG_CONFIG_HOME/eww` (this is most likely `~/.config/eww`). Now that those files are created, you can start writing your first widget! @@ -89,7 +100,13 @@ We are creating a widget named `greeter`. This widget takes two attributes, call Now, we declare the body of our widget. We make use of a `box`, which we set a couple attributes of. -We need this `box`, as a widget definition can only ever contain a single widget - otherwise, eww would not know if it should align them vertically or horizontally, how it should space them, and so on. Thus, we wrap multiple children in a `box.`. This box then contains a reference to the provided attribute `text`, as well as a button. In that buttons `onclick` attribute, we make refer to the provided `name` using string-interpolation syntax: `"${name}"`. This allows us to easily refer to any variables within strings. In fact, there is a lot more you can do withing `${...}` - more on that in the chapter about the [expression language](expression_language.md). +We need this `box`, as a widget definition can only ever contain a single widget - otherwise, +eww would not know if it should align them vertically or horizontally, how it should space them, and so on. +Thus, we wrap multiple children in a `box.`. +This box then contains a reference to the provided attribute `text`, as well as a button. +In that buttons `onclick` attribute, we make refer to the provided `name` using string-interpolation syntax: `"${name}"`. +This allows us to easily refer to any variables within strings. +In fact, there is a lot more you can do withing `${...}` - more on that in the chapter about the [expression language](expression_language.md). To then use our widget, we call it just like we would use any other built-in widget, and provide the required attributes. @@ -113,9 +130,12 @@ There are four different types of variables: basic, polling, listening, and a se (defvar foo "initial value") ``` -This is the simplest type of variable. Basic variables don't ever change automatically. Instead, you explicitly update them by calling eww like so: `eww update foo="new value"`. +This is the simplest type of variable. +Basic variables don't ever change automatically. +Instead, you explicitly update them by calling eww like so: `eww update foo="new value"`. -This is useful if you have values that change very rarely, or may change as a result of some external script you wrote. They may also be useful to have buttons within eww change what is shown within your widget, by setting attributes like `onclick` to run `eww update`. +This is useful if you have values that change very rarely, or may change as a result of some external script you wrote. +They may also be useful to have buttons within eww change what is shown within your widget, by setting attributes like `onclick` to run `eww update`. **Polling variables (`defpoll`)** @@ -127,9 +147,12 @@ This is useful if you have values that change very rarely, or may change as a re A polling variable is a variable which runs a provided shell-script repeatedly, in a given interval. -This may be the most commonly used type of variable. They are useful to access any quickly retrieved value repeatedly, and thus are the perfect choice for showing your time, date, as well as other bits of information such as your volume. +This may be the most commonly used type of variable. +They are useful to access any quickly retrieved value repeatedly, +and thus are the perfect choice for showing your time, date, as well as other bits of information such as your volume. -Optionally, you can specify a timeout, after which the provided script will be aborted. This helps to avoid accidentally launching thousands of never-ending processes on your system. +Optionally, you can specify a timeout, after which the provided script will be aborted. +This helps to avoid accidentally launching thousands of never-ending processes on your system. **Listening variables (`deflisten`)** @@ -138,17 +161,30 @@ Optionally, you can specify a timeout, after which the provided script will be a `tail -F /tmp/some_file`) ``` -Listening variables might be the most confusing of the bunch. A listening variable runs a script once, and reads its output continously. Whenever the script outputs a new line, the value will be updated to that new line. In the example given above, the value of `foo` will start out as `"whatever"`, and will change whenever a new line is appended to `/tmp/some_file`. +Listening variables might be the most confusing of the bunch. +A listening variable runs a script once, and reads its output continously. +Whenever the script outputs a new line, the value will be updated to that new line. +In the example given above, the value of `foo` will start out as `"whatever"`, and will change whenever a new line is appended to `/tmp/some_file`. -These are particularly useful if you have a script that can monitor some value on its own. For example, the command `xprop -spy -root _NET_CURRENT_DESKTOP` writes the currently focused desktop whenever it changes. This can be used to implement a workspace widget for a bar, for example. Another example usecase is monitoring the currently playing song with playerctl: `playerctl --follow metadata --format {{title}}`. +These are particularly useful if you have a script that can monitor some value on its own. +For example, the command `xprop -spy -root _NET_CURRENT_DESKTOP` writes the currently focused desktop whenever it changes. +This can be used to implement a workspace widget for a bar, for example. +Another example usecase is monitoring the currently playing song with playerctl: `playerctl --follow metadata --format {{title}}`. **Built-in "magic" variables** -In addition to definition your own variables, eww provides some values for you to use out of the box. These include values such as your CPU and RAM usage. These mostly contain their data as JSON, which you can then use using the [json access syntax](expression_language.md). All available magic variables are listed [here](magic-vars.md). +In addition to definition your own variables, eww provides some values for you to use out of the box. +These include values such as your CPU and RAM usage. +These mostly contain their data as JSON, which you can then use using the [json access syntax](expression_language.md). +All available magic variables are listed [here](magic-vars.md). ## Dynamically generated widgets with `literal` -In some cases, you want to not only change the text, value or color of a widget dynamically, but instead want to generate an entire widget structure dynamically. This is necessary if you want to display lists of things (for example notifications) where the amount is not necessarily known, or if you want to change the widget structure in some other more complex way. +In some cases, you want to not only change the text, +value or color of a widget dynamically, but instead want to generate an entire widget structure dynamically. +This is necessary if you want to display lists of things (for example notifications) +where the amount is not necessarily known, +or if you want to change the widget structure in some other more complex way. For this, you can make use of one of ewws most powerful features: the `literal` widget. @@ -158,7 +194,8 @@ For this, you can make use of one of ewws most powerful features: the `literal` (literal :content variable_containing_yuck) ``` -Here, you specify the content of your literal by providing it a string (most likely stored in a variable) which contains a single yuck widget tree. Eww then reads the provided value and renders the resulting widget. Whenever it changes, the widget will be rerendered. +Here, you specify the content of your literal by providing it a string (most likely stored in a variable) which contains a single yuck widget tree. +Eww then reads the provided value and renders the resulting widget. Whenever it changes, the widget will be rerendered. Note that this is not all that efficient. Make sure to only use `literal` when necessary! @@ -178,5 +215,8 @@ A single yuck-file may import the contents of any other yuck file. For this, mak ### Using a separate eww configuration directory -If you want to separate different widgets even further, you can create a new eww config folder anywhere else. Then, you can tell eww to use that configuration directory by passing _every_ command the `--config /path/to/your/config/dir` flag. Make sure to actually include this in all your `eww` calls, including `eww kill`, `eww logs`, etc. This launches a separate instance of the eww daemon, that has separate logs and state from your main eww configuration. +If you want to separate different widgets even further, you can create a new eww config folder anywhere else. +Then, you can tell eww to use that configuration directory by passing _every_ command the `--config /path/to/your/config/dir` flag. +Make sure to actually include this in all your `eww` calls, including `eww kill`, `eww logs`, etc. +This launches a separate instance of the eww daemon, that has separate logs and state from your main eww configuration. diff --git a/docs/src/expression_language.md b/docs/src/expression_language.md index d1f2219..b8010fe 100644 --- a/docs/src/expression_language.md +++ b/docs/src/expression_language.md @@ -1,8 +1,11 @@ # Simple expression language -Yuck includes a small expression language that can be used to run several operations on your data. This can be used to show different values depending on certain conditions, do mathematic operations, and even to access values withing JSON-structures. +Yuck includes a small expression language that can be used to run several operations on your data. +This can be used to show different values depending on certain conditions, +do mathematic operations, and even to access values withing JSON-structures. -These expressions can be placed anywhere within your configuration in between `{ ... }`, as well as withing strings, inside string-interpolation blocks (`"foo ${ ... } bar"`). +These expressions can be placed anywhere within your configuration in between `{ ... }`, +as well as withing strings, inside string-interpolation blocks (`"foo ${ ... } bar"`). ## Example diff --git a/docs/src/working_with_gtk.md b/docs/src/working_with_gtk.md index 039d1bb..39dbdad 100644 --- a/docs/src/working_with_gtk.md +++ b/docs/src/working_with_gtk.md @@ -17,13 +17,13 @@ SCSS is _very_ close to CSS so if you know CSS you'll have no problem learning S The debugger can be used for **a lot** of things. Especially if something doesn't work or isn't styled right. to enable it launch your eww daemon with ```bash -GTK_DEBUG=interactive ./eww daemon +GTK_DEBUG=interactive eww daemon ``` or in fish ```bash -env GTK_DEBUG=interactive ./eww daemon +env GTK_DEBUG=interactive eww daemon ``` If a style or something similar doesn't work you can click on the icon in the top left icon to select the thing that isn't being styled or isn't being styled correctly. From 0a27685938ecbe8baea86838f6e6bc3517618c2c Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Tue, 17 Aug 2021 11:18:35 +0200 Subject: [PATCH 128/137] reorder pages --- docs/src/SUMMARY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index d444c40..c0ee579 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -2,9 +2,9 @@ - [Eww - Widgets for everyone!](./eww.md) - [Configuration](./configuration.md) +- [Eww expressions](./expression_language.md) - [Theming with GTK](./working_with_gtk.md) - [Magic Variables](./magic-vars.md) - [Widgets](./widgets.md) - [Troubleshooting](./troubleshooting.md) -- [Eww expressions](./expression_language.md) - [Examples](./examples.md) From 30275007cbd18e57f6f7ab9b60afb8c34a6e1adb Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Tue, 17 Aug 2021 11:25:04 +0200 Subject: [PATCH 129/137] Fix explanation of geometry in configuration --- docs/src/configuration.md | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/docs/src/configuration.md b/docs/src/configuration.md index e46c4d4..98bb6f4 100644 --- a/docs/src/configuration.md +++ b/docs/src/configuration.md @@ -30,8 +30,11 @@ Let's look at an example window definition: ```lisp (defwindow example :monitor 0 - :geometry (geometry :x 0 :y 0 :width "90%" :height "30px") - :anchor "top center" + :geometry (geometry :x "0%" + :y "20px" + :width "90%" + :height "30px" + :anchor "top center") :stacking "fg" :reserve (struts :distance "40px" :side "top") :windowtype "dock" @@ -43,13 +46,22 @@ Here, we are defining a window named `example`, which we then set a set of prope You can now open your first window by running `eww open example`! Glorious! -### `defwindow`-Properties +### `defwindow`-properties | Property | Description | | ---------: | ------------------------------------------------------------ | -| `monitor` | which monitor this window should be displayed on | -| `geometry` | Position and size of the window. Values may be provided in `px` or relative to the screen size. | -| `anchor` | What side of the screen the window should be anchored to. Either `center` or combinations of `top`, `center`, `bottom` and `left`, `center`, `right` | +| `monitor` | which monitor this window should be displayed on. | +| `geometry` | Geometry of the window. | + + +**Geometry-properties** + +| Property | Description | +| -----------------:| ------------------------------------------------------------ | +| `x`, `y` | Position of the window. Values may be provided in `px` or `%`. Will be relative to `anchor` | +| `width`, `height` | Width and height of the window. Values may be provided in `px` or `%`. | +| `anchor` | Anchor-point of the window. Either `center` or combinations of `top`, `center`, `bottom` and `left`, `center`, `right` | + Depending on if you are using X11 or wayland, some additional properties exist: From 8679d7cb710221a87766c5755c7c047334a98503 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Tue, 17 Aug 2021 11:27:55 +0200 Subject: [PATCH 130/137] Add comment to explain where to use literal --- docs/src/configuration.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/src/configuration.md b/docs/src/configuration.md index 98bb6f4..b1f6162 100644 --- a/docs/src/configuration.md +++ b/docs/src/configuration.md @@ -203,6 +203,8 @@ For this, you can make use of one of ewws most powerful features: the `literal` ```lisp (defvar variable_containing_yuck "(box (button 'foo') (button 'bar'))") + +; then, inside your widget, use: (literal :content variable_containing_yuck) ``` From bb84e367575b8421a22cdd84070d6dedc9f251aa Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Tue, 17 Aug 2021 12:07:23 +0200 Subject: [PATCH 131/137] Fix example widgets at start of config --- docs/src/configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/configuration.md b/docs/src/configuration.md index b1f6162..f9c9867 100644 --- a/docs/src/configuration.md +++ b/docs/src/configuration.md @@ -1,6 +1,6 @@ # Writing your eww configuration -(For a list of all built in widgets (i.e. `box`, `text`, `slider`) see [Widget Documentation](widgets.md)) +(For a list of all built in widgets (i.e. `box`, `label`, `button`) see [Widget Documentation](widgets.md)) Eww is configured using its own language called `yuck`. using yuck, you declare the structure and content of your widgets, the geometry, position and behavior of any windows, From 9af334389bc119fa0b993b162ff1dd9333be4ebc Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Tue, 17 Aug 2021 12:26:46 +0200 Subject: [PATCH 132/137] Add lisp syntax highlighting to docs --- docs/theme/highlight.js | 672 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 667 insertions(+), 5 deletions(-) diff --git a/docs/theme/highlight.js b/docs/theme/highlight.js index 180385b..dd6fc1a 100644 --- a/docs/theme/highlight.js +++ b/docs/theme/highlight.js @@ -1,6 +1,668 @@ -/* - Highlight.js 10.1.1 (93fd0d73) +/*! + Highlight.js v11.0.1 (git: 1cf31f015d) + (c) 2006-2021 Ivan Sagalaev and other contributors License: BSD-3-Clause - Copyright (c) 2006-2020, Ivan Sagalaev -*/ -var hljs=function(){"use strict";function e(n){Object.freeze(n);var t="function"==typeof n;return Object.getOwnPropertyNames(n).forEach((function(r){!Object.hasOwnProperty.call(n,r)||null===n[r]||"object"!=typeof n[r]&&"function"!=typeof n[r]||t&&("caller"===r||"callee"===r||"arguments"===r)||Object.isFrozen(n[r])||e(n[r])})),n}class n{constructor(e){void 0===e.data&&(e.data={}),this.data=e.data}ignoreMatch(){this.ignore=!0}}function t(e){return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}function r(e,...n){var t={};for(const n in e)t[n]=e[n];return n.forEach((function(e){for(const n in e)t[n]=e[n]})),t}function a(e){return e.nodeName.toLowerCase()}var i=Object.freeze({__proto__:null,escapeHTML:t,inherit:r,nodeStream:function(e){var n=[];return function e(t,r){for(var i=t.firstChild;i;i=i.nextSibling)3===i.nodeType?r+=i.nodeValue.length:1===i.nodeType&&(n.push({event:"start",offset:r,node:i}),r=e(i,r),a(i).match(/br|hr|img|input/)||n.push({event:"stop",offset:r,node:i}));return r}(e,0),n},mergeStreams:function(e,n,r){var i=0,s="",o=[];function l(){return e.length&&n.length?e[0].offset!==n[0].offset?e[0].offset"}function u(e){s+=""}function d(e){("start"===e.event?c:u)(e.node)}for(;e.length||n.length;){var g=l();if(s+=t(r.substring(i,g[0].offset)),i=g[0].offset,g===e){o.reverse().forEach(u);do{d(g.splice(0,1)[0]),g=l()}while(g===e&&g.length&&g[0].offset===i);o.reverse().forEach(c)}else"start"===g[0].event?o.push(g[0].node):o.pop(),d(g.splice(0,1)[0])}return s+t(r.substr(i))}});const s="",o=e=>!!e.kind;class l{constructor(e,n){this.buffer="",this.classPrefix=n.classPrefix,e.walk(this)}addText(e){this.buffer+=t(e)}openNode(e){if(!o(e))return;let n=e.kind;e.sublanguage||(n=`${this.classPrefix}${n}`),this.span(n)}closeNode(e){o(e)&&(this.buffer+=s)}value(){return this.buffer}span(e){this.buffer+=``}}class c{constructor(){this.rootNode={children:[]},this.stack=[this.rootNode]}get top(){return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){this.top.children.push(e)}openNode(e){const n={kind:e,children:[]};this.add(n),this.stack.push(n)}closeNode(){if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)}walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,n){return"string"==typeof n?e.addText(n):n.children&&(e.openNode(n),n.children.forEach(n=>this._walk(e,n)),e.closeNode(n)),e}static _collapse(e){"string"!=typeof e&&e.children&&(e.children.every(e=>"string"==typeof e)?e.children=[e.children.join("")]:e.children.forEach(e=>{c._collapse(e)}))}}class u extends c{constructor(e){super(),this.options=e}addKeyword(e,n){""!==e&&(this.openNode(n),this.addText(e),this.closeNode())}addText(e){""!==e&&this.add(e)}addSublanguage(e,n){const t=e.root;t.kind=n,t.sublanguage=!0,this.add(t)}toHTML(){return new l(this,this.options).value()}finalize(){return!0}}function d(e){return e?"string"==typeof e?e:e.source:null}const g="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",h={begin:"\\\\[\\s\\S]",relevance:0},f={className:"string",begin:"'",end:"'",illegal:"\\n",contains:[h]},p={className:"string",begin:'"',end:'"',illegal:"\\n",contains:[h]},b={begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},m=function(e,n,t={}){var a=r({className:"comment",begin:e,end:n,contains:[]},t);return a.contains.push(b),a.contains.push({className:"doctag",begin:"(?:TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):",relevance:0}),a},v=m("//","$"),x=m("/\\*","\\*/"),E=m("#","$");var _=Object.freeze({__proto__:null,IDENT_RE:"[a-zA-Z]\\w*",UNDERSCORE_IDENT_RE:"[a-zA-Z_]\\w*",NUMBER_RE:"\\b\\d+(\\.\\d+)?",C_NUMBER_RE:g,BINARY_NUMBER_RE:"\\b(0b[01]+)",RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",SHEBANG:(e={})=>{const n=/^#![ ]*\//;return e.binary&&(e.begin=function(...e){return e.map(e=>d(e)).join("")}(n,/.*\b/,e.binary,/\b.*/)),r({className:"meta",begin:n,end:/$/,relevance:0,"on:begin":(e,n)=>{0!==e.index&&n.ignoreMatch()}},e)},BACKSLASH_ESCAPE:h,APOS_STRING_MODE:f,QUOTE_STRING_MODE:p,PHRASAL_WORDS_MODE:b,COMMENT:m,C_LINE_COMMENT_MODE:v,C_BLOCK_COMMENT_MODE:x,HASH_COMMENT_MODE:E,NUMBER_MODE:{className:"number",begin:"\\b\\d+(\\.\\d+)?",relevance:0},C_NUMBER_MODE:{className:"number",begin:g,relevance:0},BINARY_NUMBER_MODE:{className:"number",begin:"\\b(0b[01]+)",relevance:0},CSS_NUMBER_MODE:{className:"number",begin:"\\b\\d+(\\.\\d+)?(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",relevance:0},REGEXP_MODE:{begin:/(?=\/[^/\n]*\/)/,contains:[{className:"regexp",begin:/\//,end:/\/[gimuy]*/,illegal:/\n/,contains:[h,{begin:/\[/,end:/\]/,relevance:0,contains:[h]}]}]},TITLE_MODE:{className:"title",begin:"[a-zA-Z]\\w*",relevance:0},UNDERSCORE_TITLE_MODE:{className:"title",begin:"[a-zA-Z_]\\w*",relevance:0},METHOD_GUARD:{begin:"\\.\\s*[a-zA-Z_]\\w*",relevance:0},END_SAME_AS_BEGIN:function(e){return Object.assign(e,{"on:begin":(e,n)=>{n.data._beginMatch=e[1]},"on:end":(e,n)=>{n.data._beginMatch!==e[1]&&n.ignoreMatch()}})}}),N="of and for in not or if then".split(" ");function w(e,n){return n?+n:function(e){return N.includes(e.toLowerCase())}(e)?0:1}const R=t,y=r,{nodeStream:k,mergeStreams:O}=i,M=Symbol("nomatch");return function(t){var a=[],i={},s={},o=[],l=!0,c=/(^(<[^>]+>|\t|)+|\n)/gm,g="Could not find the language '{}', did you forget to load/include a language module?";const h={disableAutodetect:!0,name:"Plain text",contains:[]};var f={noHighlightRe:/^(no-?highlight)$/i,languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:null,__emitter:u};function p(e){return f.noHighlightRe.test(e)}function b(e,n,t,r){var a={code:n,language:e};S("before:highlight",a);var i=a.result?a.result:m(a.language,a.code,t,r);return i.code=a.code,S("after:highlight",i),i}function m(e,t,a,s){var o=t;function c(e,n){var t=E.case_insensitive?n[0].toLowerCase():n[0];return Object.prototype.hasOwnProperty.call(e.keywords,t)&&e.keywords[t]}function u(){null!=y.subLanguage?function(){if(""!==A){var e=null;if("string"==typeof y.subLanguage){if(!i[y.subLanguage])return void O.addText(A);e=m(y.subLanguage,A,!0,k[y.subLanguage]),k[y.subLanguage]=e.top}else e=v(A,y.subLanguage.length?y.subLanguage:null);y.relevance>0&&(I+=e.relevance),O.addSublanguage(e.emitter,e.language)}}():function(){if(!y.keywords)return void O.addText(A);let e=0;y.keywordPatternRe.lastIndex=0;let n=y.keywordPatternRe.exec(A),t="";for(;n;){t+=A.substring(e,n.index);const r=c(y,n);if(r){const[e,a]=r;O.addText(t),t="",I+=a,O.addKeyword(n[0],e)}else t+=n[0];e=y.keywordPatternRe.lastIndex,n=y.keywordPatternRe.exec(A)}t+=A.substr(e),O.addText(t)}(),A=""}function h(e){return e.className&&O.openNode(e.className),y=Object.create(e,{parent:{value:y}})}function p(e){return 0===y.matcher.regexIndex?(A+=e[0],1):(L=!0,0)}var b={};function x(t,r){var i=r&&r[0];if(A+=t,null==i)return u(),0;if("begin"===b.type&&"end"===r.type&&b.index===r.index&&""===i){if(A+=o.slice(r.index,r.index+1),!l){const n=Error("0 width match regex");throw n.languageName=e,n.badRule=b.rule,n}return 1}if(b=r,"begin"===r.type)return function(e){var t=e[0],r=e.rule;const a=new n(r),i=[r.__beforeBegin,r["on:begin"]];for(const n of i)if(n&&(n(e,a),a.ignore))return p(t);return r&&r.endSameAsBegin&&(r.endRe=RegExp(t.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"),"m")),r.skip?A+=t:(r.excludeBegin&&(A+=t),u(),r.returnBegin||r.excludeBegin||(A=t)),h(r),r.returnBegin?0:t.length}(r);if("illegal"===r.type&&!a){const e=Error('Illegal lexeme "'+i+'" for mode "'+(y.className||"")+'"');throw e.mode=y,e}if("end"===r.type){var s=function(e){var t=e[0],r=o.substr(e.index),a=function e(t,r,a){let i=function(e,n){var t=e&&e.exec(n);return t&&0===t.index}(t.endRe,a);if(i){if(t["on:end"]){const e=new n(t);t["on:end"](r,e),e.ignore&&(i=!1)}if(i){for(;t.endsParent&&t.parent;)t=t.parent;return t}}if(t.endsWithParent)return e(t.parent,r,a)}(y,e,r);if(!a)return M;var i=y;i.skip?A+=t:(i.returnEnd||i.excludeEnd||(A+=t),u(),i.excludeEnd&&(A=t));do{y.className&&O.closeNode(),y.skip||y.subLanguage||(I+=y.relevance),y=y.parent}while(y!==a.parent);return a.starts&&(a.endSameAsBegin&&(a.starts.endRe=a.endRe),h(a.starts)),i.returnEnd?0:t.length}(r);if(s!==M)return s}if("illegal"===r.type&&""===i)return 1;if(B>1e5&&B>3*r.index)throw Error("potential infinite loop, way more iterations than matches");return A+=i,i.length}var E=T(e);if(!E)throw console.error(g.replace("{}",e)),Error('Unknown language: "'+e+'"');var _=function(e){function n(n,t){return RegExp(d(n),"m"+(e.case_insensitive?"i":"")+(t?"g":""))}class t{constructor(){this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0}addRule(e,n){n.position=this.position++,this.matchIndexes[this.matchAt]=n,this.regexes.push([n,e]),this.matchAt+=function(e){return RegExp(e.toString()+"|").exec("").length-1}(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null);const e=this.regexes.map(e=>e[1]);this.matcherRe=n(function(e,n="|"){for(var t=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./,r=0,a="",i=0;i0&&(a+=n),a+="(";o.length>0;){var l=t.exec(o);if(null==l){a+=o;break}a+=o.substring(0,l.index),o=o.substring(l.index+l[0].length),"\\"===l[0][0]&&l[1]?a+="\\"+(+l[1]+s):(a+=l[0],"("===l[0]&&r++)}a+=")"}return a}(e),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex;const n=this.matcherRe.exec(e);if(!n)return null;const t=n.findIndex((e,n)=>n>0&&void 0!==e),r=this.matchIndexes[t];return n.splice(0,t),Object.assign(n,r)}}class a{constructor(){this.rules=[],this.multiRegexes=[],this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){if(this.multiRegexes[e])return this.multiRegexes[e];const n=new t;return this.rules.slice(e).forEach(([e,t])=>n.addRule(e,t)),n.compile(),this.multiRegexes[e]=n,n}considerAll(){this.regexIndex=0}addRule(e,n){this.rules.push([e,n]),"begin"===n.type&&this.count++}exec(e){const n=this.getMatcher(this.regexIndex);n.lastIndex=this.lastIndex;const t=n.exec(e);return t&&(this.regexIndex+=t.position+1,this.regexIndex===this.count&&(this.regexIndex=0)),t}}function i(e,n){const t=e.input[e.index-1],r=e.input[e.index+e[0].length];"."!==t&&"."!==r||n.ignoreMatch()}if(e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.");return function t(s,o){const l=s;if(s.compiled)return l;s.compiled=!0,s.__beforeBegin=null,s.keywords=s.keywords||s.beginKeywords;let c=null;if("object"==typeof s.keywords&&(c=s.keywords.$pattern,delete s.keywords.$pattern),s.keywords&&(s.keywords=function(e,n){var t={};return"string"==typeof e?r("keyword",e):Object.keys(e).forEach((function(n){r(n,e[n])})),t;function r(e,r){n&&(r=r.toLowerCase()),r.split(" ").forEach((function(n){var r=n.split("|");t[r[0]]=[e,w(r[0],r[1])]}))}}(s.keywords,e.case_insensitive)),s.lexemes&&c)throw Error("ERR: Prefer `keywords.$pattern` to `mode.lexemes`, BOTH are not allowed. (see mode reference) ");return l.keywordPatternRe=n(s.lexemes||c||/\w+/,!0),o&&(s.beginKeywords&&(s.begin="\\b("+s.beginKeywords.split(" ").join("|")+")(?=\\b|\\s)",s.__beforeBegin=i),s.begin||(s.begin=/\B|\b/),l.beginRe=n(s.begin),s.endSameAsBegin&&(s.end=s.begin),s.end||s.endsWithParent||(s.end=/\B|\b/),s.end&&(l.endRe=n(s.end)),l.terminator_end=d(s.end)||"",s.endsWithParent&&o.terminator_end&&(l.terminator_end+=(s.end?"|":"")+o.terminator_end)),s.illegal&&(l.illegalRe=n(s.illegal)),void 0===s.relevance&&(s.relevance=1),s.contains||(s.contains=[]),s.contains=[].concat(...s.contains.map((function(e){return function(e){return e.variants&&!e.cached_variants&&(e.cached_variants=e.variants.map((function(n){return r(e,{variants:null},n)}))),e.cached_variants?e.cached_variants:function e(n){return!!n&&(n.endsWithParent||e(n.starts))}(e)?r(e,{starts:e.starts?r(e.starts):null}):Object.isFrozen(e)?r(e):e}("self"===e?s:e)}))),s.contains.forEach((function(e){t(e,l)})),s.starts&&t(s.starts,o),l.matcher=function(e){const n=new a;return e.contains.forEach(e=>n.addRule(e.begin,{rule:e,type:"begin"})),e.terminator_end&&n.addRule(e.terminator_end,{type:"end"}),e.illegal&&n.addRule(e.illegal,{type:"illegal"}),n}(l),l}(e)}(E),N="",y=s||_,k={},O=new f.__emitter(f);!function(){for(var e=[],n=y;n!==E;n=n.parent)n.className&&e.unshift(n.className);e.forEach(e=>O.openNode(e))}();var A="",I=0,S=0,B=0,L=!1;try{for(y.matcher.considerAll();;){B++,L?L=!1:(y.matcher.lastIndex=S,y.matcher.considerAll());const e=y.matcher.exec(o);if(!e)break;const n=x(o.substring(S,e.index),e);S=e.index+n}return x(o.substr(S)),O.closeAllNodes(),O.finalize(),N=O.toHTML(),{relevance:I,value:N,language:e,illegal:!1,emitter:O,top:y}}catch(n){if(n.message&&n.message.includes("Illegal"))return{illegal:!0,illegalBy:{msg:n.message,context:o.slice(S-100,S+100),mode:n.mode},sofar:N,relevance:0,value:R(o),emitter:O};if(l)return{illegal:!1,relevance:0,value:R(o),emitter:O,language:e,top:y,errorRaised:n};throw n}}function v(e,n){n=n||f.languages||Object.keys(i);var t=function(e){const n={relevance:0,emitter:new f.__emitter(f),value:R(e),illegal:!1,top:h};return n.emitter.addText(e),n}(e),r=t;return n.filter(T).filter(I).forEach((function(n){var a=m(n,e,!1);a.language=n,a.relevance>r.relevance&&(r=a),a.relevance>t.relevance&&(r=t,t=a)})),r.language&&(t.second_best=r),t}function x(e){return f.tabReplace||f.useBR?e.replace(c,e=>"\n"===e?f.useBR?"
":e:f.tabReplace?e.replace(/\t/g,f.tabReplace):e):e}function E(e){let n=null;const t=function(e){var n=e.className+" ";n+=e.parentNode?e.parentNode.className:"";const t=f.languageDetectRe.exec(n);if(t){var r=T(t[1]);return r||(console.warn(g.replace("{}",t[1])),console.warn("Falling back to no-highlight mode for this block.",e)),r?t[1]:"no-highlight"}return n.split(/\s+/).find(e=>p(e)||T(e))}(e);if(p(t))return;S("before:highlightBlock",{block:e,language:t}),f.useBR?(n=document.createElement("div")).innerHTML=e.innerHTML.replace(/\n/g,"").replace(//g,"\n"):n=e;const r=n.textContent,a=t?b(t,r,!0):v(r),i=k(n);if(i.length){const e=document.createElement("div");e.innerHTML=a.value,a.value=O(i,k(e),r)}a.value=x(a.value),S("after:highlightBlock",{block:e,result:a}),e.innerHTML=a.value,e.className=function(e,n,t){var r=n?s[n]:t,a=[e.trim()];return e.match(/\bhljs\b/)||a.push("hljs"),e.includes(r)||a.push(r),a.join(" ").trim()}(e.className,t,a.language),e.result={language:a.language,re:a.relevance,relavance:a.relevance},a.second_best&&(e.second_best={language:a.second_best.language,re:a.second_best.relevance,relavance:a.second_best.relevance})}const N=()=>{if(!N.called){N.called=!0;var e=document.querySelectorAll("pre code");a.forEach.call(e,E)}};function T(e){return e=(e||"").toLowerCase(),i[e]||i[s[e]]}function A(e,{languageName:n}){"string"==typeof e&&(e=[e]),e.forEach(e=>{s[e]=n})}function I(e){var n=T(e);return n&&!n.disableAutodetect}function S(e,n){var t=e;o.forEach((function(e){e[t]&&e[t](n)}))}Object.assign(t,{highlight:b,highlightAuto:v,fixMarkup:x,highlightBlock:E,configure:function(e){f=y(f,e)},initHighlighting:N,initHighlightingOnLoad:function(){window.addEventListener("DOMContentLoaded",N,!1)},registerLanguage:function(e,n){var r=null;try{r=n(t)}catch(n){if(console.error("Language definition for '{}' could not be registered.".replace("{}",e)),!l)throw n;console.error(n),r=h}r.name||(r.name=e),i[e]=r,r.rawDefinition=n.bind(null,t),r.aliases&&A(r.aliases,{languageName:e})},listLanguages:function(){return Object.keys(i)},getLanguage:T,registerAliases:A,requireLanguage:function(e){var n=T(e);if(n)return n;throw Error("The '{}' language is required, but not loaded.".replace("{}",e))},autoDetection:I,inherit:y,addPlugin:function(e){o.push(e)}}),t.debugMode=function(){l=!1},t.safeMode=function(){l=!0},t.versionString="10.1.1";for(const n in _)"object"==typeof _[n]&&e(_[n]);return Object.assign(t,_),t}({})}();"object"==typeof exports&&"undefined"!=typeof module&&(module.exports=hljs);hljs.registerLanguage("php",function(){"use strict";return function(e){var r={begin:"\\$+[a-zA-Z_-ÿ][a-zA-Z0-9_-ÿ]*"},t={className:"meta",variants:[{begin:/<\?php/,relevance:10},{begin:/<\?[=]?/},{begin:/\?>/}]},a={className:"string",contains:[e.BACKSLASH_ESCAPE,t],variants:[{begin:'b"',end:'"'},{begin:"b'",end:"'"},e.inherit(e.APOS_STRING_MODE,{illegal:null}),e.inherit(e.QUOTE_STRING_MODE,{illegal:null})]},n={variants:[e.BINARY_NUMBER_MODE,e.C_NUMBER_MODE]},i={keyword:"__CLASS__ __DIR__ __FILE__ __FUNCTION__ __LINE__ __METHOD__ __NAMESPACE__ __TRAIT__ die echo exit include include_once print require require_once array abstract and as binary bool boolean break callable case catch class clone const continue declare default do double else elseif empty enddeclare endfor endforeach endif endswitch endwhile eval extends final finally float for foreach from global goto if implements instanceof insteadof int integer interface isset iterable list new object or private protected public real return string switch throw trait try unset use var void while xor yield",literal:"false null true",built_in:"Error|0 AppendIterator ArgumentCountError ArithmeticError ArrayIterator ArrayObject AssertionError BadFunctionCallException BadMethodCallException CachingIterator CallbackFilterIterator CompileError Countable DirectoryIterator DivisionByZeroError DomainException EmptyIterator ErrorException Exception FilesystemIterator FilterIterator GlobIterator InfiniteIterator InvalidArgumentException IteratorIterator LengthException LimitIterator LogicException MultipleIterator NoRewindIterator OutOfBoundsException OutOfRangeException OuterIterator OverflowException ParentIterator ParseError RangeException RecursiveArrayIterator RecursiveCachingIterator RecursiveCallbackFilterIterator RecursiveDirectoryIterator RecursiveFilterIterator RecursiveIterator RecursiveIteratorIterator RecursiveRegexIterator RecursiveTreeIterator RegexIterator RuntimeException SeekableIterator SplDoublyLinkedList SplFileInfo SplFileObject SplFixedArray SplHeap SplMaxHeap SplMinHeap SplObjectStorage SplObserver SplObserver SplPriorityQueue SplQueue SplStack SplSubject SplSubject SplTempFileObject TypeError UnderflowException UnexpectedValueException ArrayAccess Closure Generator Iterator IteratorAggregate Serializable Throwable Traversable WeakReference Directory __PHP_Incomplete_Class parent php_user_filter self static stdClass"};return{aliases:["php","php3","php4","php5","php6","php7"],case_insensitive:!0,keywords:i,contains:[e.HASH_COMMENT_MODE,e.COMMENT("//","$",{contains:[t]}),e.COMMENT("/\\*","\\*/",{contains:[{className:"doctag",begin:"@[A-Za-z]+"}]}),e.COMMENT("__halt_compiler.+?;",!1,{endsWithParent:!0,keywords:"__halt_compiler"}),{className:"string",begin:/<<<['"]?\w+['"]?$/,end:/^\w+;?$/,contains:[e.BACKSLASH_ESCAPE,{className:"subst",variants:[{begin:/\$\w+/},{begin:/\{\$/,end:/\}/}]}]},t,{className:"keyword",begin:/\$this\b/},r,{begin:/(::|->)+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/},{className:"function",beginKeywords:"fn function",end:/[;{]/,excludeEnd:!0,illegal:"[$%\\[]",contains:[e.UNDERSCORE_TITLE_MODE,{className:"params",begin:"\\(",end:"\\)",excludeBegin:!0,excludeEnd:!0,keywords:i,contains:["self",r,e.C_BLOCK_COMMENT_MODE,a,n]}]},{className:"class",beginKeywords:"class interface",end:"{",excludeEnd:!0,illegal:/[:\(\$"]/,contains:[{beginKeywords:"extends implements"},e.UNDERSCORE_TITLE_MODE]},{beginKeywords:"namespace",end:";",illegal:/[\.']/,contains:[e.UNDERSCORE_TITLE_MODE]},{beginKeywords:"use",end:";",contains:[e.UNDERSCORE_TITLE_MODE]},{begin:"=>"},a,n]}}}());hljs.registerLanguage("nginx",function(){"use strict";return function(e){var n={className:"variable",variants:[{begin:/\$\d+/},{begin:/\$\{/,end:/}/},{begin:"[\\$\\@]"+e.UNDERSCORE_IDENT_RE}]},a={endsWithParent:!0,keywords:{$pattern:"[a-z/_]+",literal:"on off yes no true false none blocked debug info notice warn error crit select break last permanent redirect kqueue rtsig epoll poll /dev/poll"},relevance:0,illegal:"=>",contains:[e.HASH_COMMENT_MODE,{className:"string",contains:[e.BACKSLASH_ESCAPE,n],variants:[{begin:/"/,end:/"/},{begin:/'/,end:/'/}]},{begin:"([a-z]+):/",end:"\\s",endsWithParent:!0,excludeEnd:!0,contains:[n]},{className:"regexp",contains:[e.BACKSLASH_ESCAPE,n],variants:[{begin:"\\s\\^",end:"\\s|{|;",returnEnd:!0},{begin:"~\\*?\\s+",end:"\\s|{|;",returnEnd:!0},{begin:"\\*(\\.[a-z\\-]+)+"},{begin:"([a-z\\-]+\\.)+\\*"}]},{className:"number",begin:"\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}(:\\d{1,5})?\\b"},{className:"number",begin:"\\b\\d+[kKmMgGdshdwy]*\\b",relevance:0},n]};return{name:"Nginx config",aliases:["nginxconf"],contains:[e.HASH_COMMENT_MODE,{begin:e.UNDERSCORE_IDENT_RE+"\\s+{",returnBegin:!0,end:"{",contains:[{className:"section",begin:e.UNDERSCORE_IDENT_RE}],relevance:0},{begin:e.UNDERSCORE_IDENT_RE+"\\s",end:";|{",returnBegin:!0,contains:[{className:"attribute",begin:e.UNDERSCORE_IDENT_RE,starts:a}],relevance:0}],illegal:"[^\\s\\}]"}}}());hljs.registerLanguage("csharp",function(){"use strict";return function(e){var n={keyword:"abstract as base bool break byte case catch char checked const continue decimal default delegate do double enum event explicit extern finally fixed float for foreach goto if implicit in int interface internal is lock long object operator out override params private protected public readonly ref sbyte sealed short sizeof stackalloc static string struct switch this try typeof uint ulong unchecked unsafe ushort using virtual void volatile while add alias ascending async await by descending dynamic equals from get global group into join let nameof on orderby partial remove select set value var when where yield",literal:"null false true"},i=e.inherit(e.TITLE_MODE,{begin:"[a-zA-Z](\\.?\\w)*"}),a={className:"number",variants:[{begin:"\\b(0b[01']+)"},{begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"}],relevance:0},s={className:"string",begin:'@"',end:'"',contains:[{begin:'""'}]},t=e.inherit(s,{illegal:/\n/}),l={className:"subst",begin:"{",end:"}",keywords:n},r=e.inherit(l,{illegal:/\n/}),c={className:"string",begin:/\$"/,end:'"',illegal:/\n/,contains:[{begin:"{{"},{begin:"}}"},e.BACKSLASH_ESCAPE,r]},o={className:"string",begin:/\$@"/,end:'"',contains:[{begin:"{{"},{begin:"}}"},{begin:'""'},l]},g=e.inherit(o,{illegal:/\n/,contains:[{begin:"{{"},{begin:"}}"},{begin:'""'},r]});l.contains=[o,c,s,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,a,e.C_BLOCK_COMMENT_MODE],r.contains=[g,c,t,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,a,e.inherit(e.C_BLOCK_COMMENT_MODE,{illegal:/\n/})];var d={variants:[o,c,s,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},E={begin:"<",end:">",contains:[{beginKeywords:"in out"},i]},_=e.IDENT_RE+"(<"+e.IDENT_RE+"(\\s*,\\s*"+e.IDENT_RE+")*>)?(\\[\\])?",b={begin:"@"+e.IDENT_RE,relevance:0};return{name:"C#",aliases:["cs","c#"],keywords:n,illegal:/::/,contains:[e.COMMENT("///","$",{returnBegin:!0,contains:[{className:"doctag",variants:[{begin:"///",relevance:0},{begin:"\x3c!--|--\x3e"},{begin:""}]}]}),e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:"meta",begin:"#",end:"$",keywords:{"meta-keyword":"if else elif endif define undef warning error line region endregion pragma checksum"}},d,a,{beginKeywords:"class interface",end:/[{;=]/,illegal:/[^\s:,]/,contains:[{beginKeywords:"where class"},i,E,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{beginKeywords:"namespace",end:/[{;=]/,illegal:/[^\s:]/,contains:[i,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"meta",begin:"^\\s*\\[",excludeBegin:!0,end:"\\]",excludeEnd:!0,contains:[{className:"meta-string",begin:/"/,end:/"/}]},{beginKeywords:"new return throw await else",relevance:0},{className:"function",begin:"("+_+"\\s+)+"+e.IDENT_RE+"\\s*(\\<.+\\>)?\\s*\\(",returnBegin:!0,end:/\s*[{;=]/,excludeEnd:!0,keywords:n,contains:[{begin:e.IDENT_RE+"\\s*(\\<.+\\>)?\\s*\\(",returnBegin:!0,contains:[e.TITLE_MODE,E],relevance:0},{className:"params",begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:n,relevance:0,contains:[d,a,e.C_BLOCK_COMMENT_MODE]},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},b]}}}());hljs.registerLanguage("perl",function(){"use strict";return function(e){var n={$pattern:/[\w.]+/,keyword:"getpwent getservent quotemeta msgrcv scalar kill dbmclose undef lc ma syswrite tr send umask sysopen shmwrite vec qx utime local oct semctl localtime readpipe do return format read sprintf dbmopen pop getpgrp not getpwnam rewinddir qq fileno qw endprotoent wait sethostent bless s|0 opendir continue each sleep endgrent shutdown dump chomp connect getsockname die socketpair close flock exists index shmget sub for endpwent redo lstat msgctl setpgrp abs exit select print ref gethostbyaddr unshift fcntl syscall goto getnetbyaddr join gmtime symlink semget splice x|0 getpeername recv log setsockopt cos last reverse gethostbyname getgrnam study formline endhostent times chop length gethostent getnetent pack getprotoent getservbyname rand mkdir pos chmod y|0 substr endnetent printf next open msgsnd readdir use unlink getsockopt getpriority rindex wantarray hex system getservbyport endservent int chr untie rmdir prototype tell listen fork shmread ucfirst setprotoent else sysseek link getgrgid shmctl waitpid unpack getnetbyname reset chdir grep split require caller lcfirst until warn while values shift telldir getpwuid my getprotobynumber delete and sort uc defined srand accept package seekdir getprotobyname semop our rename seek if q|0 chroot sysread setpwent no crypt getc chown sqrt write setnetent setpriority foreach tie sin msgget map stat getlogin unless elsif truncate exec keys glob tied closedir ioctl socket readlink eval xor readline binmode setservent eof ord bind alarm pipe atan2 getgrent exp time push setgrent gt lt or ne m|0 break given say state when"},t={className:"subst",begin:"[$@]\\{",end:"\\}",keywords:n},s={begin:"->{",end:"}"},r={variants:[{begin:/\$\d/},{begin:/[\$%@](\^\w\b|#\w+(::\w+)*|{\w+}|\w+(::\w*)*)/},{begin:/[\$%@][^\s\w{]/,relevance:0}]},i=[e.BACKSLASH_ESCAPE,t,r],a=[r,e.HASH_COMMENT_MODE,e.COMMENT("^\\=\\w","\\=cut",{endsWithParent:!0}),s,{className:"string",contains:i,variants:[{begin:"q[qwxr]?\\s*\\(",end:"\\)",relevance:5},{begin:"q[qwxr]?\\s*\\[",end:"\\]",relevance:5},{begin:"q[qwxr]?\\s*\\{",end:"\\}",relevance:5},{begin:"q[qwxr]?\\s*\\|",end:"\\|",relevance:5},{begin:"q[qwxr]?\\s*\\<",end:"\\>",relevance:5},{begin:"qw\\s+q",end:"q",relevance:5},{begin:"'",end:"'",contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"'},{begin:"`",end:"`",contains:[e.BACKSLASH_ESCAPE]},{begin:"{\\w+}",contains:[],relevance:0},{begin:"-?\\w+\\s*\\=\\>",contains:[],relevance:0}]},{className:"number",begin:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",relevance:0},{begin:"(\\/\\/|"+e.RE_STARTERS_RE+"|\\b(split|return|print|reverse|grep)\\b)\\s*",keywords:"split return print reverse grep",relevance:0,contains:[e.HASH_COMMENT_MODE,{className:"regexp",begin:"(s|tr|y)/(\\\\.|[^/])*/(\\\\.|[^/])*/[a-z]*",relevance:10},{className:"regexp",begin:"(m|qr)?/",end:"/[a-z]*",contains:[e.BACKSLASH_ESCAPE],relevance:0}]},{className:"function",beginKeywords:"sub",end:"(\\s*\\(.*?\\))?[;{]",excludeEnd:!0,relevance:5,contains:[e.TITLE_MODE]},{begin:"-\\w\\b",relevance:0},{begin:"^__DATA__$",end:"^__END__$",subLanguage:"mojolicious",contains:[{begin:"^@@.*",end:"$",className:"comment"}]}];return t.contains=a,s.contains=a,{name:"Perl",aliases:["pl","pm"],keywords:n,contains:a}}}());hljs.registerLanguage("swift",function(){"use strict";return function(e){var i={keyword:"#available #colorLiteral #column #else #elseif #endif #file #fileLiteral #function #if #imageLiteral #line #selector #sourceLocation _ __COLUMN__ __FILE__ __FUNCTION__ __LINE__ Any as as! as? associatedtype associativity break case catch class continue convenience default defer deinit didSet do dynamic dynamicType else enum extension fallthrough false fileprivate final for func get guard if import in indirect infix init inout internal is lazy left let mutating nil none nonmutating open operator optional override postfix precedence prefix private protocol Protocol public repeat required rethrows return right self Self set static struct subscript super switch throw throws true try try! try? Type typealias unowned var weak where while willSet",literal:"true false nil",built_in:"abs advance alignof alignofValue anyGenerator assert assertionFailure bridgeFromObjectiveC bridgeFromObjectiveCUnconditional bridgeToObjectiveC bridgeToObjectiveCUnconditional c compactMap contains count countElements countLeadingZeros debugPrint debugPrintln distance dropFirst dropLast dump encodeBitsAsWords enumerate equal fatalError filter find getBridgedObjectiveCType getVaList indices insertionSort isBridgedToObjectiveC isBridgedVerbatimToObjectiveC isUniquelyReferenced isUniquelyReferencedNonObjC join lazy lexicographicalCompare map max maxElement min minElement numericCast overlaps partition posix precondition preconditionFailure print println quickSort readLine reduce reflect reinterpretCast reverse roundUpToAlignment sizeof sizeofValue sort split startsWith stride strideof strideofValue swap toString transcode underestimateCount unsafeAddressOf unsafeBitCast unsafeDowncast unsafeUnwrap unsafeReflect withExtendedLifetime withObjectAtPlusZero withUnsafePointer withUnsafePointerToObject withUnsafeMutablePointer withUnsafeMutablePointers withUnsafePointer withUnsafePointers withVaList zip"},n=e.COMMENT("/\\*","\\*/",{contains:["self"]}),t={className:"subst",begin:/\\\(/,end:"\\)",keywords:i,contains:[]},a={className:"string",contains:[e.BACKSLASH_ESCAPE,t],variants:[{begin:/"""/,end:/"""/},{begin:/"/,end:/"/}]},r={className:"number",begin:"\\b([\\d_]+(\\.[\\deE_]+)?|0x[a-fA-F0-9_]+(\\.[a-fA-F0-9p_]+)?|0b[01_]+|0o[0-7_]+)\\b",relevance:0};return t.contains=[r],{name:"Swift",keywords:i,contains:[a,e.C_LINE_COMMENT_MODE,n,{className:"type",begin:"\\b[A-Z][\\wÀ-ʸ']*[!?]"},{className:"type",begin:"\\b[A-Z][\\wÀ-ʸ']*",relevance:0},r,{className:"function",beginKeywords:"func",end:"{",excludeEnd:!0,contains:[e.inherit(e.TITLE_MODE,{begin:/[A-Za-z$_][0-9A-Za-z$_]*/}),{begin://},{className:"params",begin:/\(/,end:/\)/,endsParent:!0,keywords:i,contains:["self",r,a,e.C_BLOCK_COMMENT_MODE,{begin:":"}],illegal:/["']/}],illegal:/\[|%/},{className:"class",beginKeywords:"struct protocol class extension enum",keywords:i,end:"\\{",excludeEnd:!0,contains:[e.inherit(e.TITLE_MODE,{begin:/[A-Za-z$_][\u00C0-\u02B80-9A-Za-z$_]*/})]},{className:"meta",begin:"(@discardableResult|@warn_unused_result|@exported|@lazy|@noescape|@NSCopying|@NSManaged|@objc|@objcMembers|@convention|@required|@noreturn|@IBAction|@IBDesignable|@IBInspectable|@IBOutlet|@infix|@prefix|@postfix|@autoclosure|@testable|@available|@nonobjc|@NSApplicationMain|@UIApplicationMain|@dynamicMemberLookup|@propertyWrapper)\\b"},{beginKeywords:"import",end:/$/,contains:[e.C_LINE_COMMENT_MODE,n]}]}}}());hljs.registerLanguage("makefile",function(){"use strict";return function(e){var i={className:"variable",variants:[{begin:"\\$\\("+e.UNDERSCORE_IDENT_RE+"\\)",contains:[e.BACKSLASH_ESCAPE]},{begin:/\$[@%`]+/}]}]}]};return{name:"HTML, XML",aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"],case_insensitive:!0,contains:[{className:"meta",begin:"",relevance:10,contains:[a,i,t,s,{begin:"\\[",end:"\\]",contains:[{className:"meta",begin:"",contains:[a,s,i,t]}]}]},e.COMMENT("\x3c!--","--\x3e",{relevance:10}),{begin:"<\\!\\[CDATA\\[",end:"\\]\\]>",relevance:10},n,{className:"meta",begin:/<\?xml/,end:/\?>/,relevance:10},{className:"tag",begin:")",end:">",keywords:{name:"style"},contains:[c],starts:{end:"",returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag",begin:")",end:">",keywords:{name:"script"},contains:[c],starts:{end:"<\/script>",returnEnd:!0,subLanguage:["javascript","handlebars","xml"]}},{className:"tag",begin:"",contains:[{className:"name",begin:/[^\/><\s]+/,relevance:0},c]}]}}}());hljs.registerLanguage("bash",function(){"use strict";return function(e){const s={};Object.assign(s,{className:"variable",variants:[{begin:/\$[\w\d#@][\w\d_]*/},{begin:/\$\{/,end:/\}/,contains:[{begin:/:-/,contains:[s]}]}]});const t={className:"subst",begin:/\$\(/,end:/\)/,contains:[e.BACKSLASH_ESCAPE]},n={className:"string",begin:/"/,end:/"/,contains:[e.BACKSLASH_ESCAPE,s,t]};t.contains.push(n);const a={begin:/\$\(\(/,end:/\)\)/,contains:[{begin:/\d+#[0-9a-f]+/,className:"number"},e.NUMBER_MODE,s]},i=e.SHEBANG({binary:"(fish|bash|zsh|sh|csh|ksh|tcsh|dash|scsh)",relevance:10}),c={className:"function",begin:/\w[\w\d_]*\s*\(\s*\)\s*\{/,returnBegin:!0,contains:[e.inherit(e.TITLE_MODE,{begin:/\w[\w\d_]*/})],relevance:0};return{name:"Bash",aliases:["sh","zsh"],keywords:{$pattern:/\b-?[a-z\._]+\b/,keyword:"if then else elif fi for while in do done case esac function",literal:"true false",built_in:"break cd continue eval exec exit export getopts hash pwd readonly return shift test times trap umask unset alias bind builtin caller command declare echo enable help let local logout mapfile printf read readarray source type typeset ulimit unalias set shopt autoload bg bindkey bye cap chdir clone comparguments compcall compctl compdescribe compfiles compgroups compquote comptags comptry compvalues dirs disable disown echotc echoti emulate fc fg float functions getcap getln history integer jobs kill limit log noglob popd print pushd pushln rehash sched setcap setopt stat suspend ttyctl unfunction unhash unlimit unsetopt vared wait whence where which zcompile zformat zftp zle zmodload zparseopts zprof zpty zregexparse zsocket zstyle ztcp",_:"-ne -eq -lt -gt -f -d -e -s -l -a"},contains:[i,e.SHEBANG(),c,a,e.HASH_COMMENT_MODE,n,{className:"",begin:/\\"/},{className:"string",begin:/'/,end:/'/},s]}}}());hljs.registerLanguage("c-like",function(){"use strict";return function(e){function t(e){return"(?:"+e+")?"}var n="(decltype\\(auto\\)|"+t("[a-zA-Z_]\\w*::")+"[a-zA-Z_]\\w*"+t("<.*?>")+")",r={className:"keyword",begin:"\\b[a-z\\d_]*_t\\b"},a={className:"string",variants:[{begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)",end:"'",illegal:"."},e.END_SAME_AS_BEGIN({begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},i={className:"number",variants:[{begin:"\\b(0b[01']+)"},{begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"}],relevance:0},s={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{"meta-keyword":"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include"},contains:[{begin:/\\\n/,relevance:0},e.inherit(a,{className:"meta-string"}),{className:"meta-string",begin:/<.*?>/,end:/$/,illegal:"\\n"},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},o={className:"title",begin:t("[a-zA-Z_]\\w*::")+e.IDENT_RE,relevance:0},c=t("[a-zA-Z_]\\w*::")+e.IDENT_RE+"\\s*\\(",l={keyword:"int float while private char char8_t char16_t char32_t catch import module export virtual operator sizeof dynamic_cast|10 typedef const_cast|10 const for static_cast|10 union namespace unsigned long volatile static protected bool template mutable if public friend do goto auto void enum else break extern using asm case typeid wchar_t short reinterpret_cast|10 default double register explicit signed typename try this switch continue inline delete alignas alignof constexpr consteval constinit decltype concept co_await co_return co_yield requires noexcept static_assert thread_local restrict final override atomic_bool atomic_char atomic_schar atomic_uchar atomic_short atomic_ushort atomic_int atomic_uint atomic_long atomic_ulong atomic_llong atomic_ullong new throw return and and_eq bitand bitor compl not not_eq or or_eq xor xor_eq",built_in:"std string wstring cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set pair bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap priority_queue make_pair array shared_ptr abort terminate abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf future isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr _Bool complex _Complex imaginary _Imaginary",literal:"true false nullptr NULL"},d=[r,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,i,a],_={variants:[{begin:/=/,end:/;/},{begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}],keywords:l,contains:d.concat([{begin:/\(/,end:/\)/,keywords:l,contains:d.concat(["self"]),relevance:0}]),relevance:0},u={className:"function",begin:"("+n+"[\\*&\\s]+)+"+c,returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:l,illegal:/[^\w\s\*&:<>]/,contains:[{begin:"decltype\\(auto\\)",keywords:l,relevance:0},{begin:c,returnBegin:!0,contains:[o],relevance:0},{className:"params",begin:/\(/,end:/\)/,keywords:l,relevance:0,contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,a,i,r,{begin:/\(/,end:/\)/,keywords:l,relevance:0,contains:["self",e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,a,i,r]}]},r,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,s]};return{aliases:["c","cc","h","c++","h++","hpp","hh","hxx","cxx"],keywords:l,disableAutodetect:!0,illegal:"",keywords:l,contains:["self",r]},{begin:e.IDENT_RE+"::",keywords:l},{className:"class",beginKeywords:"class struct",end:/[{;:]/,contains:[{begin://,contains:["self"]},e.TITLE_MODE]}]),exports:{preprocessor:s,strings:a,keywords:l}}}}());hljs.registerLanguage("coffeescript",function(){"use strict";const e=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],n=["true","false","null","undefined","NaN","Infinity"],a=[].concat(["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],["arguments","this","super","console","window","document","localStorage","module","global"],["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer"],["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"]);return function(r){var t={keyword:e.concat(["then","unless","until","loop","by","when","and","or","is","isnt","not"]).filter((e=>n=>!e.includes(n))(["var","const","let","function","static"])).join(" "),literal:n.concat(["yes","no","on","off"]).join(" "),built_in:a.concat(["npm","print"]).join(" ")},i="[A-Za-z$_][0-9A-Za-z$_]*",s={className:"subst",begin:/#\{/,end:/}/,keywords:t},o=[r.BINARY_NUMBER_MODE,r.inherit(r.C_NUMBER_MODE,{starts:{end:"(\\s*/)?",relevance:0}}),{className:"string",variants:[{begin:/'''/,end:/'''/,contains:[r.BACKSLASH_ESCAPE]},{begin:/'/,end:/'/,contains:[r.BACKSLASH_ESCAPE]},{begin:/"""/,end:/"""/,contains:[r.BACKSLASH_ESCAPE,s]},{begin:/"/,end:/"/,contains:[r.BACKSLASH_ESCAPE,s]}]},{className:"regexp",variants:[{begin:"///",end:"///",contains:[s,r.HASH_COMMENT_MODE]},{begin:"//[gim]{0,3}(?=\\W)",relevance:0},{begin:/\/(?![ *]).*?(?![\\]).\/[gim]{0,3}(?=\W)/}]},{begin:"@"+i},{subLanguage:"javascript",excludeBegin:!0,excludeEnd:!0,variants:[{begin:"```",end:"```"},{begin:"`",end:"`"}]}];s.contains=o;var c=r.inherit(r.TITLE_MODE,{begin:i}),l={className:"params",begin:"\\([^\\(]",returnBegin:!0,contains:[{begin:/\(/,end:/\)/,keywords:t,contains:["self"].concat(o)}]};return{name:"CoffeeScript",aliases:["coffee","cson","iced"],keywords:t,illegal:/\/\*/,contains:o.concat([r.COMMENT("###","###"),r.HASH_COMMENT_MODE,{className:"function",begin:"^\\s*"+i+"\\s*=\\s*(\\(.*\\))?\\s*\\B[-=]>",end:"[-=]>",returnBegin:!0,contains:[c,l]},{begin:/[:\(,=]\s*/,relevance:0,contains:[{className:"function",begin:"(\\(.*\\))?\\s*\\B[-=]>",end:"[-=]>",returnBegin:!0,contains:[l]}]},{className:"class",beginKeywords:"class",end:"$",illegal:/[:="\[\]]/,contains:[{beginKeywords:"extends",endsWithParent:!0,illegal:/[:="\[\]]/,contains:[c]},c]},{begin:i+":",end:":",returnBegin:!0,returnEnd:!0,relevance:0}])}}}());hljs.registerLanguage("ruby",function(){"use strict";return function(e){var n="[a-zA-Z_]\\w*[!?=]?|[-+~]\\@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?",a={keyword:"and then defined module in return redo if BEGIN retry end for self when next until do begin unless END rescue else break undef not super class case require yield alias while ensure elsif or include attr_reader attr_writer attr_accessor",literal:"true false nil"},s={className:"doctag",begin:"@[A-Za-z]+"},i={begin:"#<",end:">"},r=[e.COMMENT("#","$",{contains:[s]}),e.COMMENT("^\\=begin","^\\=end",{contains:[s],relevance:10}),e.COMMENT("^__END__","\\n$")],c={className:"subst",begin:"#\\{",end:"}",keywords:a},t={className:"string",contains:[e.BACKSLASH_ESCAPE,c],variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/`/,end:/`/},{begin:"%[qQwWx]?\\(",end:"\\)"},{begin:"%[qQwWx]?\\[",end:"\\]"},{begin:"%[qQwWx]?{",end:"}"},{begin:"%[qQwWx]?<",end:">"},{begin:"%[qQwWx]?/",end:"/"},{begin:"%[qQwWx]?%",end:"%"},{begin:"%[qQwWx]?-",end:"-"},{begin:"%[qQwWx]?\\|",end:"\\|"},{begin:/\B\?(\\\d{1,3}|\\x[A-Fa-f0-9]{1,2}|\\u[A-Fa-f0-9]{4}|\\?\S)\b/},{begin:/<<[-~]?'?(\w+)(?:.|\n)*?\n\s*\1\b/,returnBegin:!0,contains:[{begin:/<<[-~]?'?/},e.END_SAME_AS_BEGIN({begin:/(\w+)/,end:/(\w+)/,contains:[e.BACKSLASH_ESCAPE,c]})]}]},b={className:"params",begin:"\\(",end:"\\)",endsParent:!0,keywords:a},d=[t,i,{className:"class",beginKeywords:"class module",end:"$|;",illegal:/=/,contains:[e.inherit(e.TITLE_MODE,{begin:"[A-Za-z_]\\w*(::\\w+)*(\\?|\\!)?"}),{begin:"<\\s*",contains:[{begin:"("+e.IDENT_RE+"::)?"+e.IDENT_RE}]}].concat(r)},{className:"function",beginKeywords:"def",end:"$|;",contains:[e.inherit(e.TITLE_MODE,{begin:n}),b].concat(r)},{begin:e.IDENT_RE+"::"},{className:"symbol",begin:e.UNDERSCORE_IDENT_RE+"(\\!|\\?)?:",relevance:0},{className:"symbol",begin:":(?!\\s)",contains:[t,{begin:n}],relevance:0},{className:"number",begin:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",relevance:0},{begin:"(\\$\\W)|((\\$|\\@\\@?)(\\w+))"},{className:"params",begin:/\|/,end:/\|/,keywords:a},{begin:"("+e.RE_STARTERS_RE+"|unless)\\s*",keywords:"unless",contains:[i,{className:"regexp",contains:[e.BACKSLASH_ESCAPE,c],illegal:/\n/,variants:[{begin:"/",end:"/[a-z]*"},{begin:"%r{",end:"}[a-z]*"},{begin:"%r\\(",end:"\\)[a-z]*"},{begin:"%r!",end:"![a-z]*"},{begin:"%r\\[",end:"\\][a-z]*"}]}].concat(r),relevance:0}].concat(r);c.contains=d,b.contains=d;var g=[{begin:/^\s*=>/,starts:{end:"$",contains:d}},{className:"meta",begin:"^([>?]>|[\\w#]+\\(\\w+\\):\\d+:\\d+>|(\\w+-)?\\d+\\.\\d+\\.\\d(p\\d+)?[^>]+>)",starts:{end:"$",contains:d}}];return{name:"Ruby",aliases:["rb","gemspec","podspec","thor","irb"],keywords:a,illegal:/\/\*/,contains:r.concat(g).concat(d)}}}());hljs.registerLanguage("yaml",function(){"use strict";return function(e){var n="true false yes no null",a="[\\w#;/?:@&=+$,.~*\\'()[\\]]+",s={className:"string",relevance:0,variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/\S+/}],contains:[e.BACKSLASH_ESCAPE,{className:"template-variable",variants:[{begin:"{{",end:"}}"},{begin:"%{",end:"}"}]}]},i=e.inherit(s,{variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/[^\s,{}[\]]+/}]}),l={end:",",endsWithParent:!0,excludeEnd:!0,contains:[],keywords:n,relevance:0},t={begin:"{",end:"}",contains:[l],illegal:"\\n",relevance:0},g={begin:"\\[",end:"\\]",contains:[l],illegal:"\\n",relevance:0},b=[{className:"attr",variants:[{begin:"\\w[\\w :\\/.-]*:(?=[ \t]|$)"},{begin:'"\\w[\\w :\\/.-]*":(?=[ \t]|$)'},{begin:"'\\w[\\w :\\/.-]*':(?=[ \t]|$)"}]},{className:"meta",begin:"^---s*$",relevance:10},{className:"string",begin:"[\\|>]([0-9]?[+-])?[ ]*\\n( *)[\\S ]+\\n(\\2[\\S ]+\\n?)*"},{begin:"<%[%=-]?",end:"[%-]?%>",subLanguage:"ruby",excludeBegin:!0,excludeEnd:!0,relevance:0},{className:"type",begin:"!\\w+!"+a},{className:"type",begin:"!<"+a+">"},{className:"type",begin:"!"+a},{className:"type",begin:"!!"+a},{className:"meta",begin:"&"+e.UNDERSCORE_IDENT_RE+"$"},{className:"meta",begin:"\\*"+e.UNDERSCORE_IDENT_RE+"$"},{className:"bullet",begin:"\\-(?=[ ]|$)",relevance:0},e.HASH_COMMENT_MODE,{beginKeywords:n,keywords:{literal:n}},{className:"number",begin:"\\b[0-9]{4}(-[0-9][0-9]){0,2}([Tt \\t][0-9][0-9]?(:[0-9][0-9]){2})?(\\.[0-9]*)?([ \\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?\\b"},{className:"number",begin:e.C_NUMBER_RE+"\\b"},t,g,s],c=[...b];return c.pop(),c.push(i),l.contains=c,{name:"YAML",case_insensitive:!0,aliases:["yml","YAML"],contains:b}}}());hljs.registerLanguage("d",function(){"use strict";return function(e){var a={$pattern:e.UNDERSCORE_IDENT_RE,keyword:"abstract alias align asm assert auto body break byte case cast catch class const continue debug default delete deprecated do else enum export extern final finally for foreach foreach_reverse|10 goto if immutable import in inout int interface invariant is lazy macro mixin module new nothrow out override package pragma private protected public pure ref return scope shared static struct super switch synchronized template this throw try typedef typeid typeof union unittest version void volatile while with __FILE__ __LINE__ __gshared|10 __thread __traits __DATE__ __EOF__ __TIME__ __TIMESTAMP__ __VENDOR__ __VERSION__",built_in:"bool cdouble cent cfloat char creal dchar delegate double dstring float function idouble ifloat ireal long real short string ubyte ucent uint ulong ushort wchar wstring",literal:"false null true"},d="((0|[1-9][\\d_]*)|0[bB][01_]+|0[xX]([\\da-fA-F][\\da-fA-F_]*|_[\\da-fA-F][\\da-fA-F_]*))",n="\\\\(['\"\\?\\\\abfnrtv]|u[\\dA-Fa-f]{4}|[0-7]{1,3}|x[\\dA-Fa-f]{2}|U[\\dA-Fa-f]{8})|&[a-zA-Z\\d]{2,};",t={className:"number",begin:"\\b"+d+"(L|u|U|Lu|LU|uL|UL)?",relevance:0},_={className:"number",begin:"\\b(((0[xX](([\\da-fA-F][\\da-fA-F_]*|_[\\da-fA-F][\\da-fA-F_]*)\\.([\\da-fA-F][\\da-fA-F_]*|_[\\da-fA-F][\\da-fA-F_]*)|\\.?([\\da-fA-F][\\da-fA-F_]*|_[\\da-fA-F][\\da-fA-F_]*))[pP][+-]?(0|[1-9][\\d_]*|\\d[\\d_]*|[\\d_]+?\\d))|((0|[1-9][\\d_]*|\\d[\\d_]*|[\\d_]+?\\d)(\\.\\d*|([eE][+-]?(0|[1-9][\\d_]*|\\d[\\d_]*|[\\d_]+?\\d)))|\\d+\\.(0|[1-9][\\d_]*|\\d[\\d_]*|[\\d_]+?\\d)(0|[1-9][\\d_]*|\\d[\\d_]*|[\\d_]+?\\d)|\\.(0|[1-9][\\d_]*)([eE][+-]?(0|[1-9][\\d_]*|\\d[\\d_]*|[\\d_]+?\\d))?))([fF]|L|i|[fF]i|Li)?|"+d+"(i|[fF]i|Li))",relevance:0},r={className:"string",begin:"'("+n+"|.)",end:"'",illegal:"."},i={className:"string",begin:'"',contains:[{begin:n,relevance:0}],end:'"[cwd]?'},s=e.COMMENT("\\/\\+","\\+\\/",{contains:["self"],relevance:10});return{name:"D",keywords:a,contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,s,{className:"string",begin:'x"[\\da-fA-F\\s\\n\\r]*"[cwd]?',relevance:10},i,{className:"string",begin:'[rq]"',end:'"[cwd]?',relevance:5},{className:"string",begin:"`",end:"`[cwd]?"},{className:"string",begin:'q"\\{',end:'\\}"'},_,t,r,{className:"meta",begin:"^#!",end:"$",relevance:5},{className:"meta",begin:"#(line)",end:"$",relevance:5},{className:"keyword",begin:"@[a-zA-Z_][a-zA-Z_\\d]*"}]}}}());hljs.registerLanguage("properties",function(){"use strict";return function(e){var n="[ \\t\\f]*",t="("+n+"[:=]"+n+"|[ \\t\\f]+)",a="([^\\\\:= \\t\\f\\n]|\\\\.)+",s={end:t,relevance:0,starts:{className:"string",end:/$/,relevance:0,contains:[{begin:"\\\\\\n"}]}};return{name:".properties",case_insensitive:!0,illegal:/\S/,contains:[e.COMMENT("^\\s*[!#]","$"),{begin:"([^\\\\\\W:= \\t\\f\\n]|\\\\.)+"+t,returnBegin:!0,contains:[{className:"attr",begin:"([^\\\\\\W:= \\t\\f\\n]|\\\\.)+",endsParent:!0,relevance:0}],starts:s},{begin:a+t,returnBegin:!0,relevance:0,contains:[{className:"meta",begin:a,endsParent:!0,relevance:0}],starts:s},{className:"attr",relevance:0,begin:a+n+"$"}]}}}());hljs.registerLanguage("http",function(){"use strict";return function(e){var n="HTTP/[0-9\\.]+";return{name:"HTTP",aliases:["https"],illegal:"\\S",contains:[{begin:"^"+n,end:"$",contains:[{className:"number",begin:"\\b\\d{3}\\b"}]},{begin:"^[A-Z]+ (.*?) "+n+"$",returnBegin:!0,end:"$",contains:[{className:"string",begin:" ",end:" ",excludeBegin:!0,excludeEnd:!0},{begin:n},{className:"keyword",begin:"[A-Z]+"}]},{className:"attribute",begin:"^\\w",end:": ",excludeEnd:!0,illegal:"\\n|\\s|=",starts:{end:"$",relevance:0}},{begin:"\\n\\n",starts:{subLanguage:[],endsWithParent:!0}}]}}}());hljs.registerLanguage("haskell",function(){"use strict";return function(e){var n={variants:[e.COMMENT("--","$"),e.COMMENT("{-","-}",{contains:["self"]})]},i={className:"meta",begin:"{-#",end:"#-}"},a={className:"meta",begin:"^#",end:"$"},s={className:"type",begin:"\\b[A-Z][\\w']*",relevance:0},l={begin:"\\(",end:"\\)",illegal:'"',contains:[i,a,{className:"type",begin:"\\b[A-Z][\\w]*(\\((\\.\\.|,|\\w+)\\))?"},e.inherit(e.TITLE_MODE,{begin:"[_a-z][\\w']*"}),n]};return{name:"Haskell",aliases:["hs"],keywords:"let in if then else case of where do module import hiding qualified type data newtype deriving class instance as default infix infixl infixr foreign export ccall stdcall cplusplus jvm dotnet safe unsafe family forall mdo proc rec",contains:[{beginKeywords:"module",end:"where",keywords:"module where",contains:[l,n],illegal:"\\W\\.|;"},{begin:"\\bimport\\b",end:"$",keywords:"import qualified as hiding",contains:[l,n],illegal:"\\W\\.|;"},{className:"class",begin:"^(\\s*)?(class|instance)\\b",end:"where",keywords:"class family instance where",contains:[s,l,n]},{className:"class",begin:"\\b(data|(new)?type)\\b",end:"$",keywords:"data family type newtype deriving",contains:[i,s,l,{begin:"{",end:"}",contains:l.contains},n]},{beginKeywords:"default",end:"$",contains:[s,l,n]},{beginKeywords:"infix infixl infixr",end:"$",contains:[e.C_NUMBER_MODE,n]},{begin:"\\bforeign\\b",end:"$",keywords:"foreign import export ccall stdcall cplusplus jvm dotnet safe unsafe",contains:[s,e.QUOTE_STRING_MODE,n]},{className:"meta",begin:"#!\\/usr\\/bin\\/env runhaskell",end:"$"},i,a,e.QUOTE_STRING_MODE,e.C_NUMBER_MODE,s,e.inherit(e.TITLE_MODE,{begin:"^[_a-z][\\w']*"}),n,{begin:"->|<-"}]}}}());hljs.registerLanguage("handlebars",function(){"use strict";function e(...e){return e.map(e=>(function(e){return e?"string"==typeof e?e:e.source:null})(e)).join("")}return function(n){const a={"builtin-name":"action bindattr collection component concat debugger each each-in get hash if in input link-to loc log lookup mut outlet partial query-params render template textarea unbound unless view with yield"},t=/\[.*?\]/,s=/[^\s!"#%&'()*+,.\/;<=>@\[\\\]^`{|}~]+/,i=e("(",/'.*?'/,"|",/".*?"/,"|",t,"|",s,"|",/\.|\//,")+"),r=e("(",t,"|",s,")(?==)"),l={begin:i,lexemes:/[\w.\/]+/},c=n.inherit(l,{keywords:{literal:"true false undefined null"}}),o={begin:/\(/,end:/\)/},m={className:"attr",begin:r,relevance:0,starts:{begin:/=/,end:/=/,starts:{contains:[n.NUMBER_MODE,n.QUOTE_STRING_MODE,n.APOS_STRING_MODE,c,o]}}},d={contains:[n.NUMBER_MODE,n.QUOTE_STRING_MODE,n.APOS_STRING_MODE,{begin:/as\s+\|/,keywords:{keyword:"as"},end:/\|/,contains:[{begin:/\w+/}]},m,c,o],returnEnd:!0},g=n.inherit(l,{className:"name",keywords:a,starts:n.inherit(d,{end:/\)/})});o.contains=[g];const u=n.inherit(l,{keywords:a,className:"name",starts:n.inherit(d,{end:/}}/})}),b=n.inherit(l,{keywords:a,className:"name"}),h=n.inherit(l,{className:"name",keywords:a,starts:n.inherit(d,{end:/}}/})});return{name:"Handlebars",aliases:["hbs","html.hbs","html.handlebars","htmlbars"],case_insensitive:!0,subLanguage:"xml",contains:[{begin:/\\\{\{/,skip:!0},{begin:/\\\\(?=\{\{)/,skip:!0},n.COMMENT(/\{\{!--/,/--\}\}/),n.COMMENT(/\{\{!/,/\}\}/),{className:"template-tag",begin:/\{\{\{\{(?!\/)/,end:/\}\}\}\}/,contains:[u],starts:{end:/\{\{\{\{\//,returnEnd:!0,subLanguage:"xml"}},{className:"template-tag",begin:/\{\{\{\{\//,end:/\}\}\}\}/,contains:[b]},{className:"template-tag",begin:/\{\{#/,end:/\}\}/,contains:[u]},{className:"template-tag",begin:/\{\{(?=else\}\})/,end:/\}\}/,keywords:"else"},{className:"template-tag",begin:/\{\{\//,end:/\}\}/,contains:[b]},{className:"template-variable",begin:/\{\{\{/,end:/\}\}\}/,contains:[h]},{className:"template-variable",begin:/\{\{/,end:/\}\}/,contains:[h]}]}}}());hljs.registerLanguage("rust",function(){"use strict";return function(e){var n="([ui](8|16|32|64|128|size)|f(32|64))?",t="drop i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize f32 f64 str char bool Box Option Result String Vec Copy Send Sized Sync Drop Fn FnMut FnOnce ToOwned Clone Debug PartialEq PartialOrd Eq Ord AsRef AsMut Into From Default Iterator Extend IntoIterator DoubleEndedIterator ExactSizeIterator SliceConcatExt ToString assert! assert_eq! bitflags! bytes! cfg! col! concat! concat_idents! debug_assert! debug_assert_eq! env! panic! file! format! format_args! include_bin! include_str! line! local_data_key! module_path! option_env! print! println! select! stringify! try! unimplemented! unreachable! vec! write! writeln! macro_rules! assert_ne! debug_assert_ne!";return{name:"Rust",aliases:["rs"],keywords:{$pattern:e.IDENT_RE+"!?",keyword:"abstract as async await become box break const continue crate do dyn else enum extern false final fn for if impl in let loop macro match mod move mut override priv pub ref return self Self static struct super trait true try type typeof unsafe unsized use virtual where while yield",literal:"true false Some None Ok Err",built_in:t},illegal:""}]}}}());hljs.registerLanguage("cpp",function(){"use strict";return function(e){var t=e.getLanguage("c-like").rawDefinition();return t.disableAutodetect=!1,t.name="C++",t.aliases=["cc","c++","h++","hpp","hh","hxx","cxx"],t}}());hljs.registerLanguage("ini",function(){"use strict";function e(e){return e?"string"==typeof e?e:e.source:null}function n(...n){return n.map(n=>e(n)).join("")}return function(a){var s={className:"number",relevance:0,variants:[{begin:/([\+\-]+)?[\d]+_[\d_]+/},{begin:a.NUMBER_RE}]},i=a.COMMENT();i.variants=[{begin:/;/,end:/$/},{begin:/#/,end:/$/}];var t={className:"variable",variants:[{begin:/\$[\w\d"][\w\d_]*/},{begin:/\$\{(.*?)}/}]},r={className:"literal",begin:/\bon|off|true|false|yes|no\b/},l={className:"string",contains:[a.BACKSLASH_ESCAPE],variants:[{begin:"'''",end:"'''",relevance:10},{begin:'"""',end:'"""',relevance:10},{begin:'"',end:'"'},{begin:"'",end:"'"}]},c={begin:/\[/,end:/\]/,contains:[i,r,t,l,s,"self"],relevance:0},g="("+[/[A-Za-z0-9_-]+/,/"(\\"|[^"])*"/,/'[^']*'/].map(n=>e(n)).join("|")+")";return{name:"TOML, also INI",aliases:["toml"],case_insensitive:!0,illegal:/\S/,contains:[i,{className:"section",begin:/\[+/,end:/\]+/},{begin:n(g,"(\\s*\\.\\s*",g,")*",n("(?=",/\s*=\s*[^#\s]/,")")),className:"attr",starts:{end:/$/,contains:[i,c,r,t,l,s]}}]}}}());hljs.registerLanguage("objectivec",function(){"use strict";return function(e){var n=/[a-zA-Z@][a-zA-Z0-9_]*/,_={$pattern:n,keyword:"@interface @class @protocol @implementation"};return{name:"Objective-C",aliases:["mm","objc","obj-c"],keywords:{$pattern:n,keyword:"int float while char export sizeof typedef const struct for union unsigned long volatile static bool mutable if do return goto void enum else break extern asm case short default double register explicit signed typename this switch continue wchar_t inline readonly assign readwrite self @synchronized id typeof nonatomic super unichar IBOutlet IBAction strong weak copy in out inout bycopy byref oneway __strong __weak __block __autoreleasing @private @protected @public @try @property @end @throw @catch @finally @autoreleasepool @synthesize @dynamic @selector @optional @required @encode @package @import @defs @compatibility_alias __bridge __bridge_transfer __bridge_retained __bridge_retain __covariant __contravariant __kindof _Nonnull _Nullable _Null_unspecified __FUNCTION__ __PRETTY_FUNCTION__ __attribute__ getter setter retain unsafe_unretained nonnull nullable null_unspecified null_resettable class instancetype NS_DESIGNATED_INITIALIZER NS_UNAVAILABLE NS_REQUIRES_SUPER NS_RETURNS_INNER_POINTER NS_INLINE NS_AVAILABLE NS_DEPRECATED NS_ENUM NS_OPTIONS NS_SWIFT_UNAVAILABLE NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_END NS_REFINED_FOR_SWIFT NS_SWIFT_NAME NS_SWIFT_NOTHROW NS_DURING NS_HANDLER NS_ENDHANDLER NS_VALUERETURN NS_VOIDRETURN",literal:"false true FALSE TRUE nil YES NO NULL",built_in:"BOOL dispatch_once_t dispatch_queue_t dispatch_sync dispatch_async dispatch_once"},illegal:"/,end:/$/,illegal:"\\n"},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"class",begin:"("+_.keyword.split(" ").join("|")+")\\b",end:"({|$)",excludeEnd:!0,keywords:_,contains:[e.UNDERSCORE_TITLE_MODE]},{begin:"\\."+e.UNDERSCORE_IDENT_RE,relevance:0}]}}}());hljs.registerLanguage("apache",function(){"use strict";return function(e){var n={className:"number",begin:"\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}(:\\d{1,5})?"};return{name:"Apache config",aliases:["apacheconf"],case_insensitive:!0,contains:[e.HASH_COMMENT_MODE,{className:"section",begin:"",contains:[n,{className:"number",begin:":\\d{1,5}"},e.inherit(e.QUOTE_STRING_MODE,{relevance:0})]},{className:"attribute",begin:/\w+/,relevance:0,keywords:{nomarkup:"order deny allow setenv rewriterule rewriteengine rewritecond documentroot sethandler errordocument loadmodule options header listen serverroot servername"},starts:{end:/$/,relevance:0,keywords:{literal:"on off all deny allow"},contains:[{className:"meta",begin:"\\s\\[",end:"\\]$"},{className:"variable",begin:"[\\$%]\\{",end:"\\}",contains:["self",{className:"number",begin:"[\\$%]\\d+"}]},n,{className:"number",begin:"\\d+"},e.QUOTE_STRING_MODE]}}],illegal:/\S/}}}());hljs.registerLanguage("java",function(){"use strict";function e(e){return e?"string"==typeof e?e:e.source:null}function n(e){return a("(",e,")?")}function a(...n){return n.map(n=>e(n)).join("")}function s(...n){return"("+n.map(n=>e(n)).join("|")+")"}return function(e){var t="false synchronized int abstract float private char boolean var static null if const for true while long strictfp finally protected import native final void enum else break transient catch instanceof byte super volatile case assert short package default double public try this switch continue throws protected public private module requires exports do",i={className:"meta",begin:"@[À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*",contains:[{begin:/\(/,end:/\)/,contains:["self"]}]},r=e=>a("[",e,"]+([",e,"_]*[",e,"]+)?"),c={className:"number",variants:[{begin:`\\b(0[bB]${r("01")})[lL]?`},{begin:`\\b(0${r("0-7")})[dDfFlL]?`},{begin:a(/\b0[xX]/,s(a(r("a-fA-F0-9"),/\./,r("a-fA-F0-9")),a(r("a-fA-F0-9"),/\.?/),a(/\./,r("a-fA-F0-9"))),/([pP][+-]?(\d+))?/,/[fFdDlL]?/)},{begin:a(/\b/,s(a(/\d*\./,r("\\d")),r("\\d")),/[eE][+-]?[\d]+[dDfF]?/)},{begin:a(/\b/,r(/\d/),n(/\.?/),n(r(/\d/)),/[dDfFlL]?/)}],relevance:0};return{name:"Java",aliases:["jsp"],keywords:t,illegal:/<\/|#/,contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{begin:/\w+@/,relevance:0},{className:"doctag",begin:"@[A-Za-z]+"}]}),e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{className:"class",beginKeywords:"class interface",end:/[{;=]/,excludeEnd:!0,keywords:"class interface",illegal:/[:"\[\]]/,contains:[{beginKeywords:"extends implements"},e.UNDERSCORE_TITLE_MODE]},{beginKeywords:"new throw return else",relevance:0},{className:"function",begin:"([À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*(<[À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*(\\s*,\\s*[À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*)*>)?\\s+)+"+e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:t,contains:[{begin:e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,relevance:0,contains:[e.UNDERSCORE_TITLE_MODE]},{className:"params",begin:/\(/,end:/\)/,keywords:t,relevance:0,contains:[i,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,e.C_NUMBER_MODE,e.C_BLOCK_COMMENT_MODE]},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},c,i]}}}());hljs.registerLanguage("x86asm",function(){"use strict";return function(s){return{name:"Intel x86 Assembly",case_insensitive:!0,keywords:{$pattern:"[.%]?"+s.IDENT_RE,keyword:"lock rep repe repz repne repnz xaquire xrelease bnd nobnd aaa aad aam aas adc add and arpl bb0_reset bb1_reset bound bsf bsr bswap bt btc btr bts call cbw cdq cdqe clc cld cli clts cmc cmp cmpsb cmpsd cmpsq cmpsw cmpxchg cmpxchg486 cmpxchg8b cmpxchg16b cpuid cpu_read cpu_write cqo cwd cwde daa das dec div dmint emms enter equ f2xm1 fabs fadd faddp fbld fbstp fchs fclex fcmovb fcmovbe fcmove fcmovnb fcmovnbe fcmovne fcmovnu fcmovu fcom fcomi fcomip fcomp fcompp fcos fdecstp fdisi fdiv fdivp fdivr fdivrp femms feni ffree ffreep fiadd ficom ficomp fidiv fidivr fild fimul fincstp finit fist fistp fisttp fisub fisubr fld fld1 fldcw fldenv fldl2e fldl2t fldlg2 fldln2 fldpi fldz fmul fmulp fnclex fndisi fneni fninit fnop fnsave fnstcw fnstenv fnstsw fpatan fprem fprem1 fptan frndint frstor fsave fscale fsetpm fsin fsincos fsqrt fst fstcw fstenv fstp fstsw fsub fsubp fsubr fsubrp ftst fucom fucomi fucomip fucomp fucompp fxam fxch fxtract fyl2x fyl2xp1 hlt ibts icebp idiv imul in inc incbin insb insd insw int int01 int1 int03 int3 into invd invpcid invlpg invlpga iret iretd iretq iretw jcxz jecxz jrcxz jmp jmpe lahf lar lds lea leave les lfence lfs lgdt lgs lidt lldt lmsw loadall loadall286 lodsb lodsd lodsq lodsw loop loope loopne loopnz loopz lsl lss ltr mfence monitor mov movd movq movsb movsd movsq movsw movsx movsxd movzx mul mwait neg nop not or out outsb outsd outsw packssdw packsswb packuswb paddb paddd paddsb paddsiw paddsw paddusb paddusw paddw pand pandn pause paveb pavgusb pcmpeqb pcmpeqd pcmpeqw pcmpgtb pcmpgtd pcmpgtw pdistib pf2id pfacc pfadd pfcmpeq pfcmpge pfcmpgt pfmax pfmin pfmul pfrcp pfrcpit1 pfrcpit2 pfrsqit1 pfrsqrt pfsub pfsubr pi2fd pmachriw pmaddwd pmagw pmulhriw pmulhrwa pmulhrwc pmulhw pmullw pmvgezb pmvlzb pmvnzb pmvzb pop popa popad popaw popf popfd popfq popfw por prefetch prefetchw pslld psllq psllw psrad psraw psrld psrlq psrlw psubb psubd psubsb psubsiw psubsw psubusb psubusw psubw punpckhbw punpckhdq punpckhwd punpcklbw punpckldq punpcklwd push pusha pushad pushaw pushf pushfd pushfq pushfw pxor rcl rcr rdshr rdmsr rdpmc rdtsc rdtscp ret retf retn rol ror rdm rsdc rsldt rsm rsts sahf sal salc sar sbb scasb scasd scasq scasw sfence sgdt shl shld shr shrd sidt sldt skinit smi smint smintold smsw stc std sti stosb stosd stosq stosw str sub svdc svldt svts swapgs syscall sysenter sysexit sysret test ud0 ud1 ud2b ud2 ud2a umov verr verw fwait wbinvd wrshr wrmsr xadd xbts xchg xlatb xlat xor cmove cmovz cmovne cmovnz cmova cmovnbe cmovae cmovnb cmovb cmovnae cmovbe cmovna cmovg cmovnle cmovge cmovnl cmovl cmovnge cmovle cmovng cmovc cmovnc cmovo cmovno cmovs cmovns cmovp cmovpe cmovnp cmovpo je jz jne jnz ja jnbe jae jnb jb jnae jbe jna jg jnle jge jnl jl jnge jle jng jc jnc jo jno js jns jpo jnp jpe jp sete setz setne setnz seta setnbe setae setnb setnc setb setnae setcset setbe setna setg setnle setge setnl setl setnge setle setng sets setns seto setno setpe setp setpo setnp addps addss andnps andps cmpeqps cmpeqss cmpleps cmpless cmpltps cmpltss cmpneqps cmpneqss cmpnleps cmpnless cmpnltps cmpnltss cmpordps cmpordss cmpunordps cmpunordss cmpps cmpss comiss cvtpi2ps cvtps2pi cvtsi2ss cvtss2si cvttps2pi cvttss2si divps divss ldmxcsr maxps maxss minps minss movaps movhps movlhps movlps movhlps movmskps movntps movss movups mulps mulss orps rcpps rcpss rsqrtps rsqrtss shufps sqrtps sqrtss stmxcsr subps subss ucomiss unpckhps unpcklps xorps fxrstor fxrstor64 fxsave fxsave64 xgetbv xsetbv xsave xsave64 xsaveopt xsaveopt64 xrstor xrstor64 prefetchnta prefetcht0 prefetcht1 prefetcht2 maskmovq movntq pavgb pavgw pextrw pinsrw pmaxsw pmaxub pminsw pminub pmovmskb pmulhuw psadbw pshufw pf2iw pfnacc pfpnacc pi2fw pswapd maskmovdqu clflush movntdq movnti movntpd movdqa movdqu movdq2q movq2dq paddq pmuludq pshufd pshufhw pshuflw pslldq psrldq psubq punpckhqdq punpcklqdq addpd addsd andnpd andpd cmpeqpd cmpeqsd cmplepd cmplesd cmpltpd cmpltsd cmpneqpd cmpneqsd cmpnlepd cmpnlesd cmpnltpd cmpnltsd cmpordpd cmpordsd cmpunordpd cmpunordsd cmppd comisd cvtdq2pd cvtdq2ps cvtpd2dq cvtpd2pi cvtpd2ps cvtpi2pd cvtps2dq cvtps2pd cvtsd2si cvtsd2ss cvtsi2sd cvtss2sd cvttpd2pi cvttpd2dq cvttps2dq cvttsd2si divpd divsd maxpd maxsd minpd minsd movapd movhpd movlpd movmskpd movupd mulpd mulsd orpd shufpd sqrtpd sqrtsd subpd subsd ucomisd unpckhpd unpcklpd xorpd addsubpd addsubps haddpd haddps hsubpd hsubps lddqu movddup movshdup movsldup clgi stgi vmcall vmclear vmfunc vmlaunch vmload vmmcall vmptrld vmptrst vmread vmresume vmrun vmsave vmwrite vmxoff vmxon invept invvpid pabsb pabsw pabsd palignr phaddw phaddd phaddsw phsubw phsubd phsubsw pmaddubsw pmulhrsw pshufb psignb psignw psignd extrq insertq movntsd movntss lzcnt blendpd blendps blendvpd blendvps dppd dpps extractps insertps movntdqa mpsadbw packusdw pblendvb pblendw pcmpeqq pextrb pextrd pextrq phminposuw pinsrb pinsrd pinsrq pmaxsb pmaxsd pmaxud pmaxuw pminsb pminsd pminud pminuw pmovsxbw pmovsxbd pmovsxbq pmovsxwd pmovsxwq pmovsxdq pmovzxbw pmovzxbd pmovzxbq pmovzxwd pmovzxwq pmovzxdq pmuldq pmulld ptest roundpd roundps roundsd roundss crc32 pcmpestri pcmpestrm pcmpistri pcmpistrm pcmpgtq popcnt getsec pfrcpv pfrsqrtv movbe aesenc aesenclast aesdec aesdeclast aesimc aeskeygenassist vaesenc vaesenclast vaesdec vaesdeclast vaesimc vaeskeygenassist vaddpd vaddps vaddsd vaddss vaddsubpd vaddsubps vandpd vandps vandnpd vandnps vblendpd vblendps vblendvpd vblendvps vbroadcastss vbroadcastsd vbroadcastf128 vcmpeq_ospd vcmpeqpd vcmplt_ospd vcmpltpd vcmple_ospd vcmplepd vcmpunord_qpd vcmpunordpd vcmpneq_uqpd vcmpneqpd vcmpnlt_uspd vcmpnltpd vcmpnle_uspd vcmpnlepd vcmpord_qpd vcmpordpd vcmpeq_uqpd vcmpnge_uspd vcmpngepd vcmpngt_uspd vcmpngtpd vcmpfalse_oqpd vcmpfalsepd vcmpneq_oqpd vcmpge_ospd vcmpgepd vcmpgt_ospd vcmpgtpd vcmptrue_uqpd vcmptruepd vcmplt_oqpd vcmple_oqpd vcmpunord_spd vcmpneq_uspd vcmpnlt_uqpd vcmpnle_uqpd vcmpord_spd vcmpeq_uspd vcmpnge_uqpd vcmpngt_uqpd vcmpfalse_ospd vcmpneq_ospd vcmpge_oqpd vcmpgt_oqpd vcmptrue_uspd vcmppd vcmpeq_osps vcmpeqps vcmplt_osps vcmpltps vcmple_osps vcmpleps vcmpunord_qps vcmpunordps vcmpneq_uqps vcmpneqps vcmpnlt_usps vcmpnltps vcmpnle_usps vcmpnleps vcmpord_qps vcmpordps vcmpeq_uqps vcmpnge_usps vcmpngeps vcmpngt_usps vcmpngtps vcmpfalse_oqps vcmpfalseps vcmpneq_oqps vcmpge_osps vcmpgeps vcmpgt_osps vcmpgtps vcmptrue_uqps vcmptrueps vcmplt_oqps vcmple_oqps vcmpunord_sps vcmpneq_usps vcmpnlt_uqps vcmpnle_uqps vcmpord_sps vcmpeq_usps vcmpnge_uqps vcmpngt_uqps vcmpfalse_osps vcmpneq_osps vcmpge_oqps vcmpgt_oqps vcmptrue_usps vcmpps vcmpeq_ossd vcmpeqsd vcmplt_ossd vcmpltsd vcmple_ossd vcmplesd vcmpunord_qsd vcmpunordsd vcmpneq_uqsd vcmpneqsd vcmpnlt_ussd vcmpnltsd vcmpnle_ussd vcmpnlesd vcmpord_qsd vcmpordsd vcmpeq_uqsd vcmpnge_ussd vcmpngesd vcmpngt_ussd vcmpngtsd vcmpfalse_oqsd vcmpfalsesd vcmpneq_oqsd vcmpge_ossd vcmpgesd vcmpgt_ossd vcmpgtsd vcmptrue_uqsd vcmptruesd vcmplt_oqsd vcmple_oqsd vcmpunord_ssd vcmpneq_ussd vcmpnlt_uqsd vcmpnle_uqsd vcmpord_ssd vcmpeq_ussd vcmpnge_uqsd vcmpngt_uqsd vcmpfalse_ossd vcmpneq_ossd vcmpge_oqsd vcmpgt_oqsd vcmptrue_ussd vcmpsd vcmpeq_osss vcmpeqss vcmplt_osss vcmpltss vcmple_osss vcmpless vcmpunord_qss vcmpunordss vcmpneq_uqss vcmpneqss vcmpnlt_usss vcmpnltss vcmpnle_usss vcmpnless vcmpord_qss vcmpordss vcmpeq_uqss vcmpnge_usss vcmpngess vcmpngt_usss vcmpngtss vcmpfalse_oqss vcmpfalsess vcmpneq_oqss vcmpge_osss vcmpgess vcmpgt_osss vcmpgtss vcmptrue_uqss vcmptruess vcmplt_oqss vcmple_oqss vcmpunord_sss vcmpneq_usss vcmpnlt_uqss vcmpnle_uqss vcmpord_sss vcmpeq_usss vcmpnge_uqss vcmpngt_uqss vcmpfalse_osss vcmpneq_osss vcmpge_oqss vcmpgt_oqss vcmptrue_usss vcmpss vcomisd vcomiss vcvtdq2pd vcvtdq2ps vcvtpd2dq vcvtpd2ps vcvtps2dq vcvtps2pd vcvtsd2si vcvtsd2ss vcvtsi2sd vcvtsi2ss vcvtss2sd vcvtss2si vcvttpd2dq vcvttps2dq vcvttsd2si vcvttss2si vdivpd vdivps vdivsd vdivss vdppd vdpps vextractf128 vextractps vhaddpd vhaddps vhsubpd vhsubps vinsertf128 vinsertps vlddqu vldqqu vldmxcsr vmaskmovdqu vmaskmovps vmaskmovpd vmaxpd vmaxps vmaxsd vmaxss vminpd vminps vminsd vminss vmovapd vmovaps vmovd vmovq vmovddup vmovdqa vmovqqa vmovdqu vmovqqu vmovhlps vmovhpd vmovhps vmovlhps vmovlpd vmovlps vmovmskpd vmovmskps vmovntdq vmovntqq vmovntdqa vmovntpd vmovntps vmovsd vmovshdup vmovsldup vmovss vmovupd vmovups vmpsadbw vmulpd vmulps vmulsd vmulss vorpd vorps vpabsb vpabsw vpabsd vpacksswb vpackssdw vpackuswb vpackusdw vpaddb vpaddw vpaddd vpaddq vpaddsb vpaddsw vpaddusb vpaddusw vpalignr vpand vpandn vpavgb vpavgw vpblendvb vpblendw vpcmpestri vpcmpestrm vpcmpistri vpcmpistrm vpcmpeqb vpcmpeqw vpcmpeqd vpcmpeqq vpcmpgtb vpcmpgtw vpcmpgtd vpcmpgtq vpermilpd vpermilps vperm2f128 vpextrb vpextrw vpextrd vpextrq vphaddw vphaddd vphaddsw vphminposuw vphsubw vphsubd vphsubsw vpinsrb vpinsrw vpinsrd vpinsrq vpmaddwd vpmaddubsw vpmaxsb vpmaxsw vpmaxsd vpmaxub vpmaxuw vpmaxud vpminsb vpminsw vpminsd vpminub vpminuw vpminud vpmovmskb vpmovsxbw vpmovsxbd vpmovsxbq vpmovsxwd vpmovsxwq vpmovsxdq vpmovzxbw vpmovzxbd vpmovzxbq vpmovzxwd vpmovzxwq vpmovzxdq vpmulhuw vpmulhrsw vpmulhw vpmullw vpmulld vpmuludq vpmuldq vpor vpsadbw vpshufb vpshufd vpshufhw vpshuflw vpsignb vpsignw vpsignd vpslldq vpsrldq vpsllw vpslld vpsllq vpsraw vpsrad vpsrlw vpsrld vpsrlq vptest vpsubb vpsubw vpsubd vpsubq vpsubsb vpsubsw vpsubusb vpsubusw vpunpckhbw vpunpckhwd vpunpckhdq vpunpckhqdq vpunpcklbw vpunpcklwd vpunpckldq vpunpcklqdq vpxor vrcpps vrcpss vrsqrtps vrsqrtss vroundpd vroundps vroundsd vroundss vshufpd vshufps vsqrtpd vsqrtps vsqrtsd vsqrtss vstmxcsr vsubpd vsubps vsubsd vsubss vtestps vtestpd vucomisd vucomiss vunpckhpd vunpckhps vunpcklpd vunpcklps vxorpd vxorps vzeroall vzeroupper pclmullqlqdq pclmulhqlqdq pclmullqhqdq pclmulhqhqdq pclmulqdq vpclmullqlqdq vpclmulhqlqdq vpclmullqhqdq vpclmulhqhqdq vpclmulqdq vfmadd132ps vfmadd132pd vfmadd312ps vfmadd312pd vfmadd213ps vfmadd213pd vfmadd123ps vfmadd123pd vfmadd231ps vfmadd231pd vfmadd321ps vfmadd321pd vfmaddsub132ps vfmaddsub132pd vfmaddsub312ps vfmaddsub312pd vfmaddsub213ps vfmaddsub213pd vfmaddsub123ps vfmaddsub123pd vfmaddsub231ps vfmaddsub231pd vfmaddsub321ps vfmaddsub321pd vfmsub132ps vfmsub132pd vfmsub312ps vfmsub312pd vfmsub213ps vfmsub213pd vfmsub123ps vfmsub123pd vfmsub231ps vfmsub231pd vfmsub321ps vfmsub321pd vfmsubadd132ps vfmsubadd132pd vfmsubadd312ps vfmsubadd312pd vfmsubadd213ps vfmsubadd213pd vfmsubadd123ps vfmsubadd123pd vfmsubadd231ps vfmsubadd231pd vfmsubadd321ps vfmsubadd321pd vfnmadd132ps vfnmadd132pd vfnmadd312ps vfnmadd312pd vfnmadd213ps vfnmadd213pd vfnmadd123ps vfnmadd123pd vfnmadd231ps vfnmadd231pd vfnmadd321ps vfnmadd321pd vfnmsub132ps vfnmsub132pd vfnmsub312ps vfnmsub312pd vfnmsub213ps vfnmsub213pd vfnmsub123ps vfnmsub123pd vfnmsub231ps vfnmsub231pd vfnmsub321ps vfnmsub321pd vfmadd132ss vfmadd132sd vfmadd312ss vfmadd312sd vfmadd213ss vfmadd213sd vfmadd123ss vfmadd123sd vfmadd231ss vfmadd231sd vfmadd321ss vfmadd321sd vfmsub132ss vfmsub132sd vfmsub312ss vfmsub312sd vfmsub213ss vfmsub213sd vfmsub123ss vfmsub123sd vfmsub231ss vfmsub231sd vfmsub321ss vfmsub321sd vfnmadd132ss vfnmadd132sd vfnmadd312ss vfnmadd312sd vfnmadd213ss vfnmadd213sd vfnmadd123ss vfnmadd123sd vfnmadd231ss vfnmadd231sd vfnmadd321ss vfnmadd321sd vfnmsub132ss vfnmsub132sd vfnmsub312ss vfnmsub312sd vfnmsub213ss vfnmsub213sd vfnmsub123ss vfnmsub123sd vfnmsub231ss vfnmsub231sd vfnmsub321ss vfnmsub321sd rdfsbase rdgsbase rdrand wrfsbase wrgsbase vcvtph2ps vcvtps2ph adcx adox rdseed clac stac xstore xcryptecb xcryptcbc xcryptctr xcryptcfb xcryptofb montmul xsha1 xsha256 llwpcb slwpcb lwpval lwpins vfmaddpd vfmaddps vfmaddsd vfmaddss vfmaddsubpd vfmaddsubps vfmsubaddpd vfmsubaddps vfmsubpd vfmsubps vfmsubsd vfmsubss vfnmaddpd vfnmaddps vfnmaddsd vfnmaddss vfnmsubpd vfnmsubps vfnmsubsd vfnmsubss vfrczpd vfrczps vfrczsd vfrczss vpcmov vpcomb vpcomd vpcomq vpcomub vpcomud vpcomuq vpcomuw vpcomw vphaddbd vphaddbq vphaddbw vphadddq vphaddubd vphaddubq vphaddubw vphaddudq vphadduwd vphadduwq vphaddwd vphaddwq vphsubbw vphsubdq vphsubwd vpmacsdd vpmacsdqh vpmacsdql vpmacssdd vpmacssdqh vpmacssdql vpmacsswd vpmacssww vpmacswd vpmacsww vpmadcsswd vpmadcswd vpperm vprotb vprotd vprotq vprotw vpshab vpshad vpshaq vpshaw vpshlb vpshld vpshlq vpshlw vbroadcasti128 vpblendd vpbroadcastb vpbroadcastw vpbroadcastd vpbroadcastq vpermd vpermpd vpermps vpermq vperm2i128 vextracti128 vinserti128 vpmaskmovd vpmaskmovq vpsllvd vpsllvq vpsravd vpsrlvd vpsrlvq vgatherdpd vgatherqpd vgatherdps vgatherqps vpgatherdd vpgatherqd vpgatherdq vpgatherqq xabort xbegin xend xtest andn bextr blci blcic blsi blsic blcfill blsfill blcmsk blsmsk blsr blcs bzhi mulx pdep pext rorx sarx shlx shrx tzcnt tzmsk t1mskc valignd valignq vblendmpd vblendmps vbroadcastf32x4 vbroadcastf64x4 vbroadcasti32x4 vbroadcasti64x4 vcompresspd vcompressps vcvtpd2udq vcvtps2udq vcvtsd2usi vcvtss2usi vcvttpd2udq vcvttps2udq vcvttsd2usi vcvttss2usi vcvtudq2pd vcvtudq2ps vcvtusi2sd vcvtusi2ss vexpandpd vexpandps vextractf32x4 vextractf64x4 vextracti32x4 vextracti64x4 vfixupimmpd vfixupimmps vfixupimmsd vfixupimmss vgetexppd vgetexpps vgetexpsd vgetexpss vgetmantpd vgetmantps vgetmantsd vgetmantss vinsertf32x4 vinsertf64x4 vinserti32x4 vinserti64x4 vmovdqa32 vmovdqa64 vmovdqu32 vmovdqu64 vpabsq vpandd vpandnd vpandnq vpandq vpblendmd vpblendmq vpcmpltd vpcmpled vpcmpneqd vpcmpnltd vpcmpnled vpcmpd vpcmpltq vpcmpleq vpcmpneqq vpcmpnltq vpcmpnleq vpcmpq vpcmpequd vpcmpltud vpcmpleud vpcmpnequd vpcmpnltud vpcmpnleud vpcmpud vpcmpequq vpcmpltuq vpcmpleuq vpcmpnequq vpcmpnltuq vpcmpnleuq vpcmpuq vpcompressd vpcompressq vpermi2d vpermi2pd vpermi2ps vpermi2q vpermt2d vpermt2pd vpermt2ps vpermt2q vpexpandd vpexpandq vpmaxsq vpmaxuq vpminsq vpminuq vpmovdb vpmovdw vpmovqb vpmovqd vpmovqw vpmovsdb vpmovsdw vpmovsqb vpmovsqd vpmovsqw vpmovusdb vpmovusdw vpmovusqb vpmovusqd vpmovusqw vpord vporq vprold vprolq vprolvd vprolvq vprord vprorq vprorvd vprorvq vpscatterdd vpscatterdq vpscatterqd vpscatterqq vpsraq vpsravq vpternlogd vpternlogq vptestmd vptestmq vptestnmd vptestnmq vpxord vpxorq vrcp14pd vrcp14ps vrcp14sd vrcp14ss vrndscalepd vrndscaleps vrndscalesd vrndscaless vrsqrt14pd vrsqrt14ps vrsqrt14sd vrsqrt14ss vscalefpd vscalefps vscalefsd vscalefss vscatterdpd vscatterdps vscatterqpd vscatterqps vshuff32x4 vshuff64x2 vshufi32x4 vshufi64x2 kandnw kandw kmovw knotw kortestw korw kshiftlw kshiftrw kunpckbw kxnorw kxorw vpbroadcastmb2q vpbroadcastmw2d vpconflictd vpconflictq vplzcntd vplzcntq vexp2pd vexp2ps vrcp28pd vrcp28ps vrcp28sd vrcp28ss vrsqrt28pd vrsqrt28ps vrsqrt28sd vrsqrt28ss vgatherpf0dpd vgatherpf0dps vgatherpf0qpd vgatherpf0qps vgatherpf1dpd vgatherpf1dps vgatherpf1qpd vgatherpf1qps vscatterpf0dpd vscatterpf0dps vscatterpf0qpd vscatterpf0qps vscatterpf1dpd vscatterpf1dps vscatterpf1qpd vscatterpf1qps prefetchwt1 bndmk bndcl bndcu bndcn bndmov bndldx bndstx sha1rnds4 sha1nexte sha1msg1 sha1msg2 sha256rnds2 sha256msg1 sha256msg2 hint_nop0 hint_nop1 hint_nop2 hint_nop3 hint_nop4 hint_nop5 hint_nop6 hint_nop7 hint_nop8 hint_nop9 hint_nop10 hint_nop11 hint_nop12 hint_nop13 hint_nop14 hint_nop15 hint_nop16 hint_nop17 hint_nop18 hint_nop19 hint_nop20 hint_nop21 hint_nop22 hint_nop23 hint_nop24 hint_nop25 hint_nop26 hint_nop27 hint_nop28 hint_nop29 hint_nop30 hint_nop31 hint_nop32 hint_nop33 hint_nop34 hint_nop35 hint_nop36 hint_nop37 hint_nop38 hint_nop39 hint_nop40 hint_nop41 hint_nop42 hint_nop43 hint_nop44 hint_nop45 hint_nop46 hint_nop47 hint_nop48 hint_nop49 hint_nop50 hint_nop51 hint_nop52 hint_nop53 hint_nop54 hint_nop55 hint_nop56 hint_nop57 hint_nop58 hint_nop59 hint_nop60 hint_nop61 hint_nop62 hint_nop63",built_in:"ip eip rip al ah bl bh cl ch dl dh sil dil bpl spl r8b r9b r10b r11b r12b r13b r14b r15b ax bx cx dx si di bp sp r8w r9w r10w r11w r12w r13w r14w r15w eax ebx ecx edx esi edi ebp esp eip r8d r9d r10d r11d r12d r13d r14d r15d rax rbx rcx rdx rsi rdi rbp rsp r8 r9 r10 r11 r12 r13 r14 r15 cs ds es fs gs ss st st0 st1 st2 st3 st4 st5 st6 st7 mm0 mm1 mm2 mm3 mm4 mm5 mm6 mm7 xmm0 xmm1 xmm2 xmm3 xmm4 xmm5 xmm6 xmm7 xmm8 xmm9 xmm10 xmm11 xmm12 xmm13 xmm14 xmm15 xmm16 xmm17 xmm18 xmm19 xmm20 xmm21 xmm22 xmm23 xmm24 xmm25 xmm26 xmm27 xmm28 xmm29 xmm30 xmm31 ymm0 ymm1 ymm2 ymm3 ymm4 ymm5 ymm6 ymm7 ymm8 ymm9 ymm10 ymm11 ymm12 ymm13 ymm14 ymm15 ymm16 ymm17 ymm18 ymm19 ymm20 ymm21 ymm22 ymm23 ymm24 ymm25 ymm26 ymm27 ymm28 ymm29 ymm30 ymm31 zmm0 zmm1 zmm2 zmm3 zmm4 zmm5 zmm6 zmm7 zmm8 zmm9 zmm10 zmm11 zmm12 zmm13 zmm14 zmm15 zmm16 zmm17 zmm18 zmm19 zmm20 zmm21 zmm22 zmm23 zmm24 zmm25 zmm26 zmm27 zmm28 zmm29 zmm30 zmm31 k0 k1 k2 k3 k4 k5 k6 k7 bnd0 bnd1 bnd2 bnd3 cr0 cr1 cr2 cr3 cr4 cr8 dr0 dr1 dr2 dr3 dr8 tr3 tr4 tr5 tr6 tr7 r0 r1 r2 r3 r4 r5 r6 r7 r0b r1b r2b r3b r4b r5b r6b r7b r0w r1w r2w r3w r4w r5w r6w r7w r0d r1d r2d r3d r4d r5d r6d r7d r0h r1h r2h r3h r0l r1l r2l r3l r4l r5l r6l r7l r8l r9l r10l r11l r12l r13l r14l r15l db dw dd dq dt ddq do dy dz resb resw resd resq rest resdq reso resy resz incbin equ times byte word dword qword nosplit rel abs seg wrt strict near far a32 ptr",meta:"%define %xdefine %+ %undef %defstr %deftok %assign %strcat %strlen %substr %rotate %elif %else %endif %if %ifmacro %ifctx %ifidn %ifidni %ifid %ifnum %ifstr %iftoken %ifempty %ifenv %error %warning %fatal %rep %endrep %include %push %pop %repl %pathsearch %depend %use %arg %stacksize %local %line %comment %endcomment .nolist __FILE__ __LINE__ __SECT__ __BITS__ __OUTPUT_FORMAT__ __DATE__ __TIME__ __DATE_NUM__ __TIME_NUM__ __UTC_DATE__ __UTC_TIME__ __UTC_DATE_NUM__ __UTC_TIME_NUM__ __PASS__ struc endstruc istruc at iend align alignb sectalign daz nodaz up down zero default option assume public bits use16 use32 use64 default section segment absolute extern global common cpu float __utf16__ __utf16le__ __utf16be__ __utf32__ __utf32le__ __utf32be__ __float8__ __float16__ __float32__ __float64__ __float80m__ __float80e__ __float128l__ __float128h__ __Infinity__ __QNaN__ __SNaN__ Inf NaN QNaN SNaN float8 float16 float32 float64 float80m float80e float128l float128h __FLOAT_DAZ__ __FLOAT_ROUND__ __FLOAT__"},contains:[s.COMMENT(";","$",{relevance:0}),{className:"number",variants:[{begin:"\\b(?:([0-9][0-9_]*)?\\.[0-9_]*(?:[eE][+-]?[0-9_]+)?|(0[Xx])?[0-9][0-9_]*\\.?[0-9_]*(?:[pP](?:[+-]?[0-9_]+)?)?)\\b",relevance:0},{begin:"\\$[0-9][0-9A-Fa-f]*",relevance:0},{begin:"\\b(?:[0-9A-Fa-f][0-9A-Fa-f_]*[Hh]|[0-9][0-9_]*[DdTt]?|[0-7][0-7_]*[QqOo]|[0-1][0-1_]*[BbYy])\\b"},{begin:"\\b(?:0[Xx][0-9A-Fa-f_]+|0[DdTt][0-9_]+|0[QqOo][0-7_]+|0[BbYy][0-1_]+)\\b"}]},s.QUOTE_STRING_MODE,{className:"string",variants:[{begin:"'",end:"[^\\\\]'"},{begin:"`",end:"[^\\\\]`"}],relevance:0},{className:"symbol",variants:[{begin:"^\\s*[A-Za-z._?][A-Za-z0-9_$#@~.?]*(:|\\s+label)"},{begin:"^\\s*%%[A-Za-z0-9_$#@~.?]*:"}],relevance:0},{className:"subst",begin:"%[0-9]+",relevance:0},{className:"subst",begin:"%!S+",relevance:0},{className:"meta",begin:/^\s*\.[\w_-]+/}]}}}());hljs.registerLanguage("kotlin",function(){"use strict";return function(e){var n={keyword:"abstract as val var vararg get set class object open private protected public noinline crossinline dynamic final enum if else do while for when throw try catch finally import package is in fun override companion reified inline lateinit init interface annotation data sealed internal infix operator out by constructor super tailrec where const inner suspend typealias external expect actual trait volatile transient native default",built_in:"Byte Short Char Int Long Boolean Float Double Void Unit Nothing",literal:"true false null"},a={className:"symbol",begin:e.UNDERSCORE_IDENT_RE+"@"},i={className:"subst",begin:"\\${",end:"}",contains:[e.C_NUMBER_MODE]},s={className:"variable",begin:"\\$"+e.UNDERSCORE_IDENT_RE},t={className:"string",variants:[{begin:'"""',end:'"""(?=[^"])',contains:[s,i]},{begin:"'",end:"'",illegal:/\n/,contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"',illegal:/\n/,contains:[e.BACKSLASH_ESCAPE,s,i]}]};i.contains.push(t);var r={className:"meta",begin:"@(?:file|property|field|get|set|receiver|param|setparam|delegate)\\s*:(?:\\s*"+e.UNDERSCORE_IDENT_RE+")?"},l={className:"meta",begin:"@"+e.UNDERSCORE_IDENT_RE,contains:[{begin:/\(/,end:/\)/,contains:[e.inherit(t,{className:"meta-string"})]}]},c=e.COMMENT("/\\*","\\*/",{contains:[e.C_BLOCK_COMMENT_MODE]}),o={variants:[{className:"type",begin:e.UNDERSCORE_IDENT_RE},{begin:/\(/,end:/\)/,contains:[]}]},d=o;return d.variants[1].contains=[o],o.variants[1].contains=[d],{name:"Kotlin",aliases:["kt"],keywords:n,contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{className:"doctag",begin:"@[A-Za-z]+"}]}),e.C_LINE_COMMENT_MODE,c,{className:"keyword",begin:/\b(break|continue|return|this)\b/,starts:{contains:[{className:"symbol",begin:/@\w+/}]}},a,r,l,{className:"function",beginKeywords:"fun",end:"[(]|$",returnBegin:!0,excludeEnd:!0,keywords:n,illegal:/fun\s+(<.*>)?[^\s\(]+(\s+[^\s\(]+)\s*=/,relevance:5,contains:[{begin:e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,relevance:0,contains:[e.UNDERSCORE_TITLE_MODE]},{className:"type",begin://,keywords:"reified",relevance:0},{className:"params",begin:/\(/,end:/\)/,endsParent:!0,keywords:n,relevance:0,contains:[{begin:/:/,end:/[=,\/]/,endsWithParent:!0,contains:[o,e.C_LINE_COMMENT_MODE,c],relevance:0},e.C_LINE_COMMENT_MODE,c,r,l,t,e.C_NUMBER_MODE]},c]},{className:"class",beginKeywords:"class interface trait",end:/[:\{(]|$/,excludeEnd:!0,illegal:"extends implements",contains:[{beginKeywords:"public protected internal private constructor"},e.UNDERSCORE_TITLE_MODE,{className:"type",begin://,excludeBegin:!0,excludeEnd:!0,relevance:0},{className:"type",begin:/[,:]\s*/,end:/[<\(,]|$/,excludeBegin:!0,returnEnd:!0},r,l]},t,{className:"meta",begin:"^#!/usr/bin/env",end:"$",illegal:"\n"},{className:"number",begin:"\\b(0[bB]([01]+[01_]+[01]+|[01]+)|0[xX]([a-fA-F0-9]+[a-fA-F0-9_]+[a-fA-F0-9]+|[a-fA-F0-9]+)|(([\\d]+[\\d_]+[\\d]+|[\\d]+)(\\.([\\d]+[\\d_]+[\\d]+|[\\d]+))?|\\.([\\d]+[\\d_]+[\\d]+|[\\d]+))([eE][-+]?\\d+)?)[lLfF]?",relevance:0}]}}}());hljs.registerLanguage("armasm",function(){"use strict";return function(s){const e={variants:[s.COMMENT("^[ \\t]*(?=#)","$",{relevance:0,excludeBegin:!0}),s.COMMENT("[;@]","$",{relevance:0}),s.C_LINE_COMMENT_MODE,s.C_BLOCK_COMMENT_MODE]};return{name:"ARM Assembly",case_insensitive:!0,aliases:["arm"],keywords:{$pattern:"\\.?"+s.IDENT_RE,meta:".2byte .4byte .align .ascii .asciz .balign .byte .code .data .else .end .endif .endm .endr .equ .err .exitm .extern .global .hword .if .ifdef .ifndef .include .irp .long .macro .rept .req .section .set .skip .space .text .word .arm .thumb .code16 .code32 .force_thumb .thumb_func .ltorg ALIAS ALIGN ARM AREA ASSERT ATTR CN CODE CODE16 CODE32 COMMON CP DATA DCB DCD DCDU DCDO DCFD DCFDU DCI DCQ DCQU DCW DCWU DN ELIF ELSE END ENDFUNC ENDIF ENDP ENTRY EQU EXPORT EXPORTAS EXTERN FIELD FILL FUNCTION GBLA GBLL GBLS GET GLOBAL IF IMPORT INCBIN INCLUDE INFO KEEP LCLA LCLL LCLS LTORG MACRO MAP MEND MEXIT NOFP OPT PRESERVE8 PROC QN READONLY RELOC REQUIRE REQUIRE8 RLIST FN ROUT SETA SETL SETS SN SPACE SUBT THUMB THUMBX TTL WHILE WEND ",built_in:"r0 r1 r2 r3 r4 r5 r6 r7 r8 r9 r10 r11 r12 r13 r14 r15 pc lr sp ip sl sb fp a1 a2 a3 a4 v1 v2 v3 v4 v5 v6 v7 v8 f0 f1 f2 f3 f4 f5 f6 f7 p0 p1 p2 p3 p4 p5 p6 p7 p8 p9 p10 p11 p12 p13 p14 p15 c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 c10 c11 c12 c13 c14 c15 q0 q1 q2 q3 q4 q5 q6 q7 q8 q9 q10 q11 q12 q13 q14 q15 cpsr_c cpsr_x cpsr_s cpsr_f cpsr_cx cpsr_cxs cpsr_xs cpsr_xsf cpsr_sf cpsr_cxsf spsr_c spsr_x spsr_s spsr_f spsr_cx spsr_cxs spsr_xs spsr_xsf spsr_sf spsr_cxsf s0 s1 s2 s3 s4 s5 s6 s7 s8 s9 s10 s11 s12 s13 s14 s15 s16 s17 s18 s19 s20 s21 s22 s23 s24 s25 s26 s27 s28 s29 s30 s31 d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 d10 d11 d12 d13 d14 d15 d16 d17 d18 d19 d20 d21 d22 d23 d24 d25 d26 d27 d28 d29 d30 d31 {PC} {VAR} {TRUE} {FALSE} {OPT} {CONFIG} {ENDIAN} {CODESIZE} {CPU} {FPU} {ARCHITECTURE} {PCSTOREOFFSET} {ARMASM_VERSION} {INTER} {ROPI} {RWPI} {SWST} {NOSWST} . @"},contains:[{className:"keyword",begin:"\\b(adc|(qd?|sh?|u[qh]?)?add(8|16)?|usada?8|(q|sh?|u[qh]?)?(as|sa)x|and|adrl?|sbc|rs[bc]|asr|b[lx]?|blx|bxj|cbn?z|tb[bh]|bic|bfc|bfi|[su]bfx|bkpt|cdp2?|clz|clrex|cmp|cmn|cpsi[ed]|cps|setend|dbg|dmb|dsb|eor|isb|it[te]{0,3}|lsl|lsr|ror|rrx|ldm(([id][ab])|f[ds])?|ldr((s|ex)?[bhd])?|movt?|mvn|mra|mar|mul|[us]mull|smul[bwt][bt]|smu[as]d|smmul|smmla|mla|umlaal|smlal?([wbt][bt]|d)|mls|smlsl?[ds]|smc|svc|sev|mia([bt]{2}|ph)?|mrr?c2?|mcrr2?|mrs|msr|orr|orn|pkh(tb|bt)|rbit|rev(16|sh)?|sel|[su]sat(16)?|nop|pop|push|rfe([id][ab])?|stm([id][ab])?|str(ex)?[bhd]?|(qd?)?sub|(sh?|q|u[qh]?)?sub(8|16)|[su]xt(a?h|a?b(16)?)|srs([id][ab])?|swpb?|swi|smi|tst|teq|wfe|wfi|yield)(eq|ne|cs|cc|mi|pl|vs|vc|hi|ls|ge|lt|gt|le|al|hs|lo)?[sptrx]?(?=\\s)"},e,s.QUOTE_STRING_MODE,{className:"string",begin:"'",end:"[^\\\\]'",relevance:0},{className:"title",begin:"\\|",end:"\\|",illegal:"\\n",relevance:0},{className:"number",variants:[{begin:"[#$=]?0x[0-9a-f]+"},{begin:"[#$=]?0b[01]+"},{begin:"[#$=]\\d+"},{begin:"\\b\\d+"}],relevance:0},{className:"symbol",variants:[{begin:"^[ \\t]*[a-z_\\.\\$][a-z0-9_\\.\\$]+:"},{begin:"^[a-z_\\.\\$][a-z0-9_\\.\\$]+"},{begin:"[=#]\\w+"}],relevance:0}]}}}());hljs.registerLanguage("go",function(){"use strict";return function(e){var n={keyword:"break default func interface select case map struct chan else goto package switch const fallthrough if range type continue for import return var go defer bool byte complex64 complex128 float32 float64 int8 int16 int32 int64 string uint8 uint16 uint32 uint64 int uint uintptr rune",literal:"true false iota nil",built_in:"append cap close complex copy imag len make new panic print println real recover delete"};return{name:"Go",aliases:["golang"],keywords:n,illegal:">>|\.\.\.) /},i={className:"subst",begin:/\{/,end:/\}/,keywords:n,illegal:/#/},s={begin:/\{\{/,relevance:0},r={className:"string",contains:[e.BACKSLASH_ESCAPE],variants:[{begin:/(u|b)?r?'''/,end:/'''/,contains:[e.BACKSLASH_ESCAPE,a],relevance:10},{begin:/(u|b)?r?"""/,end:/"""/,contains:[e.BACKSLASH_ESCAPE,a],relevance:10},{begin:/(fr|rf|f)'''/,end:/'''/,contains:[e.BACKSLASH_ESCAPE,a,s,i]},{begin:/(fr|rf|f)"""/,end:/"""/,contains:[e.BACKSLASH_ESCAPE,a,s,i]},{begin:/(u|r|ur)'/,end:/'/,relevance:10},{begin:/(u|r|ur)"/,end:/"/,relevance:10},{begin:/(b|br)'/,end:/'/},{begin:/(b|br)"/,end:/"/},{begin:/(fr|rf|f)'/,end:/'/,contains:[e.BACKSLASH_ESCAPE,s,i]},{begin:/(fr|rf|f)"/,end:/"/,contains:[e.BACKSLASH_ESCAPE,s,i]},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},l={className:"number",relevance:0,variants:[{begin:e.BINARY_NUMBER_RE+"[lLjJ]?"},{begin:"\\b(0o[0-7]+)[lLjJ]?"},{begin:e.C_NUMBER_RE+"[lLjJ]?"}]},t={className:"params",variants:[{begin:/\(\s*\)/,skip:!0,className:null},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,contains:["self",a,l,r,e.HASH_COMMENT_MODE]}]};return i.contains=[r,l,a],{name:"Python",aliases:["py","gyp","ipython"],keywords:n,illegal:/(<\/|->|\?)|=>/,contains:[a,l,{beginKeywords:"if",relevance:0},r,e.HASH_COMMENT_MODE,{variants:[{className:"function",beginKeywords:"def"},{className:"class",beginKeywords:"class"}],end:/:/,illegal:/[${=;\n,]/,contains:[e.UNDERSCORE_TITLE_MODE,t,{begin:/->/,endsWithParent:!0,keywords:"None"}]},{className:"meta",begin:/^[\t ]*@/,end:/$/},{begin:/\b(print|exec)\(/}]}}}());hljs.registerLanguage("shell",function(){"use strict";return function(s){return{name:"Shell Session",aliases:["console"],contains:[{className:"meta",begin:"^\\s{0,3}[/\\w\\d\\[\\]()@-]*[>%$#]",starts:{end:"$",subLanguage:"bash"}}]}}}());hljs.registerLanguage("scala",function(){"use strict";return function(e){var n={className:"subst",variants:[{begin:"\\$[A-Za-z0-9_]+"},{begin:"\\${",end:"}"}]},a={className:"string",variants:[{begin:'"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{begin:'"""',end:'"""',relevance:10},{begin:'[a-z]+"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE,n]},{className:"string",begin:'[a-z]+"""',end:'"""',contains:[n],relevance:10}]},s={className:"type",begin:"\\b[A-Z][A-Za-z0-9_]*",relevance:0},t={className:"title",begin:/[^0-9\n\t "'(),.`{}\[\]:;][^\n\t "'(),.`{}\[\]:;]+|[^0-9\n\t "'(),.`{}\[\]:;=]/,relevance:0},i={className:"class",beginKeywords:"class object trait type",end:/[:={\[\n;]/,excludeEnd:!0,contains:[{beginKeywords:"extends with",relevance:10},{begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0,relevance:0,contains:[s]},{className:"params",begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,relevance:0,contains:[s]},t]},l={className:"function",beginKeywords:"def",end:/[:={\[(\n;]/,excludeEnd:!0,contains:[t]};return{name:"Scala",keywords:{literal:"true false null",keyword:"type yield lazy override def with val var sealed abstract private trait object if forSome for while throw finally protected extends import final return else break new catch super class case package default try this match continue throws implicit"},contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,a,{className:"symbol",begin:"'\\w[\\w\\d_]*(?!')"},s,l,i,e.C_NUMBER_MODE,{className:"meta",begin:"@[A-Za-z]+"}]}}}());hljs.registerLanguage("julia",function(){"use strict";return function(e){var r="[A-Za-z_\\u00A1-\\uFFFF][A-Za-z_0-9\\u00A1-\\uFFFF]*",t={$pattern:r,keyword:"in isa where baremodule begin break catch ccall const continue do else elseif end export false finally for function global if import importall let local macro module quote return true try using while type immutable abstract bitstype typealias ",literal:"true false ARGS C_NULL DevNull ENDIAN_BOM ENV I Inf Inf16 Inf32 Inf64 InsertionSort JULIA_HOME LOAD_PATH MergeSort NaN NaN16 NaN32 NaN64 PROGRAM_FILE QuickSort RoundDown RoundFromZero RoundNearest RoundNearestTiesAway RoundNearestTiesUp RoundToZero RoundUp STDERR STDIN STDOUT VERSION catalan e|0 eu|0 eulergamma golden im nothing pi γ π φ ",built_in:"ANY AbstractArray AbstractChannel AbstractFloat AbstractMatrix AbstractRNG AbstractSerializer AbstractSet AbstractSparseArray AbstractSparseMatrix AbstractSparseVector AbstractString AbstractUnitRange AbstractVecOrMat AbstractVector Any ArgumentError Array AssertionError Associative Base64DecodePipe Base64EncodePipe Bidiagonal BigFloat BigInt BitArray BitMatrix BitVector Bool BoundsError BufferStream CachingPool CapturedException CartesianIndex CartesianRange Cchar Cdouble Cfloat Channel Char Cint Cintmax_t Clong Clonglong ClusterManager Cmd CodeInfo Colon Complex Complex128 Complex32 Complex64 CompositeException Condition ConjArray ConjMatrix ConjVector Cptrdiff_t Cshort Csize_t Cssize_t Cstring Cuchar Cuint Cuintmax_t Culong Culonglong Cushort Cwchar_t Cwstring DataType Date DateFormat DateTime DenseArray DenseMatrix DenseVecOrMat DenseVector Diagonal Dict DimensionMismatch Dims DirectIndexString Display DivideError DomainError EOFError EachLine Enum Enumerate ErrorException Exception ExponentialBackOff Expr Factorization FileMonitor Float16 Float32 Float64 Function Future GlobalRef GotoNode HTML Hermitian IO IOBuffer IOContext IOStream IPAddr IPv4 IPv6 IndexCartesian IndexLinear IndexStyle InexactError InitError Int Int128 Int16 Int32 Int64 Int8 IntSet Integer InterruptException InvalidStateException Irrational KeyError LabelNode LinSpace LineNumberNode LoadError LowerTriangular MIME Matrix MersenneTwister Method MethodError MethodTable Module NTuple NewvarNode NullException Nullable Number ObjectIdDict OrdinalRange OutOfMemoryError OverflowError Pair ParseError PartialQuickSort PermutedDimsArray Pipe PollingFileWatcher ProcessExitedException Ptr QuoteNode RandomDevice Range RangeIndex Rational RawFD ReadOnlyMemoryError Real ReentrantLock Ref Regex RegexMatch RemoteChannel RemoteException RevString RoundingMode RowVector SSAValue SegmentationFault SerializationState Set SharedArray SharedMatrix SharedVector Signed SimpleVector Slot SlotNumber SparseMatrixCSC SparseVector StackFrame StackOverflowError StackTrace StepRange StepRangeLen StridedArray StridedMatrix StridedVecOrMat StridedVector String SubArray SubString SymTridiagonal Symbol Symmetric SystemError TCPSocket Task Text TextDisplay Timer Tridiagonal Tuple Type TypeError TypeMapEntry TypeMapLevel TypeName TypeVar TypedSlot UDPSocket UInt UInt128 UInt16 UInt32 UInt64 UInt8 UndefRefError UndefVarError UnicodeError UniformScaling Union UnionAll UnitRange Unsigned UpperTriangular Val Vararg VecElement VecOrMat Vector VersionNumber Void WeakKeyDict WeakRef WorkerConfig WorkerPool "},a={keywords:t,illegal:/<\//},n={className:"subst",begin:/\$\(/,end:/\)/,keywords:t},o={className:"variable",begin:"\\$"+r},i={className:"string",contains:[e.BACKSLASH_ESCAPE,n,o],variants:[{begin:/\w*"""/,end:/"""\w*/,relevance:10},{begin:/\w*"/,end:/"\w*/}]},l={className:"string",contains:[e.BACKSLASH_ESCAPE,n,o],begin:"`",end:"`"},s={className:"meta",begin:"@"+r};return a.name="Julia",a.contains=[{className:"number",begin:/(\b0x[\d_]*(\.[\d_]*)?|0x\.\d[\d_]*)p[-+]?\d+|\b0[box][a-fA-F0-9][a-fA-F0-9_]*|(\b\d[\d_]*(\.[\d_]*)?|\.\d[\d_]*)([eEfF][-+]?\d+)?/,relevance:0},{className:"string",begin:/'(.|\\[xXuU][a-zA-Z0-9]+)'/},i,l,s,{className:"comment",variants:[{begin:"#=",end:"=#",relevance:10},{begin:"#",end:"$"}]},e.HASH_COMMENT_MODE,{className:"keyword",begin:"\\b(((abstract|primitive)\\s+)type|(mutable\\s+)?struct)\\b"},{begin:/<:/}],n.contains=a.contains,a}}());hljs.registerLanguage("php-template",function(){"use strict";return function(n){return{name:"PHP template",subLanguage:"xml",contains:[{begin:/<\?(php|=)?/,end:/\?>/,subLanguage:"php",contains:[{begin:"/\\*",end:"\\*/",skip:!0},{begin:'b"',end:'"',skip:!0},{begin:"b'",end:"'",skip:!0},n.inherit(n.APOS_STRING_MODE,{illegal:null,className:null,contains:null,skip:!0}),n.inherit(n.QUOTE_STRING_MODE,{illegal:null,className:null,contains:null,skip:!0})]}]}}}());hljs.registerLanguage("scss",function(){"use strict";return function(e){var t={className:"variable",begin:"(\\$[a-zA-Z-][a-zA-Z0-9_-]*)\\b"},i={className:"number",begin:"#[0-9A-Fa-f]+"};return e.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,e.C_BLOCK_COMMENT_MODE,{name:"SCSS",case_insensitive:!0,illegal:"[=/|']",contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:"selector-id",begin:"\\#[A-Za-z0-9_-]+",relevance:0},{className:"selector-class",begin:"\\.[A-Za-z0-9_-]+",relevance:0},{className:"selector-attr",begin:"\\[",end:"\\]",illegal:"$"},{className:"selector-tag",begin:"\\b(a|abbr|acronym|address|area|article|aside|audio|b|base|big|blockquote|body|br|button|canvas|caption|cite|code|col|colgroup|command|datalist|dd|del|details|dfn|div|dl|dt|em|embed|fieldset|figcaption|figure|footer|form|frame|frameset|(h[1-6])|head|header|hgroup|hr|html|i|iframe|img|input|ins|kbd|keygen|label|legend|li|link|map|mark|meta|meter|nav|noframes|noscript|object|ol|optgroup|option|output|p|param|pre|progress|q|rp|rt|ruby|samp|script|section|select|small|span|strike|strong|style|sub|sup|table|tbody|td|textarea|tfoot|th|thead|time|title|tr|tt|ul|var|video)\\b",relevance:0},{className:"selector-pseudo",begin:":(visited|valid|root|right|required|read-write|read-only|out-range|optional|only-of-type|only-child|nth-of-type|nth-last-of-type|nth-last-child|nth-child|not|link|left|last-of-type|last-child|lang|invalid|indeterminate|in-range|hover|focus|first-of-type|first-line|first-letter|first-child|first|enabled|empty|disabled|default|checked|before|after|active)"},{className:"selector-pseudo",begin:"::(after|before|choices|first-letter|first-line|repeat-index|repeat-item|selection|value)"},t,{className:"attribute",begin:"\\b(src|z-index|word-wrap|word-spacing|word-break|width|widows|white-space|visibility|vertical-align|unicode-bidi|transition-timing-function|transition-property|transition-duration|transition-delay|transition|transform-style|transform-origin|transform|top|text-underline-position|text-transform|text-shadow|text-rendering|text-overflow|text-indent|text-decoration-style|text-decoration-line|text-decoration-color|text-decoration|text-align-last|text-align|tab-size|table-layout|right|resize|quotes|position|pointer-events|perspective-origin|perspective|page-break-inside|page-break-before|page-break-after|padding-top|padding-right|padding-left|padding-bottom|padding|overflow-y|overflow-x|overflow-wrap|overflow|outline-width|outline-style|outline-offset|outline-color|outline|orphans|order|opacity|object-position|object-fit|normal|none|nav-up|nav-right|nav-left|nav-index|nav-down|min-width|min-height|max-width|max-height|mask|marks|margin-top|margin-right|margin-left|margin-bottom|margin|list-style-type|list-style-position|list-style-image|list-style|line-height|letter-spacing|left|justify-content|initial|inherit|ime-mode|image-orientation|image-resolution|image-rendering|icon|hyphens|height|font-weight|font-variant-ligatures|font-variant|font-style|font-stretch|font-size-adjust|font-size|font-language-override|font-kerning|font-feature-settings|font-family|font|float|flex-wrap|flex-shrink|flex-grow|flex-flow|flex-direction|flex-basis|flex|filter|empty-cells|display|direction|cursor|counter-reset|counter-increment|content|column-width|column-span|column-rule-width|column-rule-style|column-rule-color|column-rule|column-gap|column-fill|column-count|columns|color|clip-path|clip|clear|caption-side|break-inside|break-before|break-after|box-sizing|box-shadow|box-decoration-break|bottom|border-width|border-top-width|border-top-style|border-top-right-radius|border-top-left-radius|border-top-color|border-top|border-style|border-spacing|border-right-width|border-right-style|border-right-color|border-right|border-radius|border-left-width|border-left-style|border-left-color|border-left|border-image-width|border-image-source|border-image-slice|border-image-repeat|border-image-outset|border-image|border-color|border-collapse|border-bottom-width|border-bottom-style|border-bottom-right-radius|border-bottom-left-radius|border-bottom-color|border-bottom|border|background-size|background-repeat|background-position|background-origin|background-image|background-color|background-clip|background-attachment|background-blend-mode|background|backface-visibility|auto|animation-timing-function|animation-play-state|animation-name|animation-iteration-count|animation-fill-mode|animation-duration|animation-direction|animation-delay|animation|align-self|align-items|align-content)\\b",illegal:"[^\\s]"},{begin:"\\b(whitespace|wait|w-resize|visible|vertical-text|vertical-ideographic|uppercase|upper-roman|upper-alpha|underline|transparent|top|thin|thick|text|text-top|text-bottom|tb-rl|table-header-group|table-footer-group|sw-resize|super|strict|static|square|solid|small-caps|separate|se-resize|scroll|s-resize|rtl|row-resize|ridge|right|repeat|repeat-y|repeat-x|relative|progress|pointer|overline|outside|outset|oblique|nowrap|not-allowed|normal|none|nw-resize|no-repeat|no-drop|newspaper|ne-resize|n-resize|move|middle|medium|ltr|lr-tb|lowercase|lower-roman|lower-alpha|loose|list-item|line|line-through|line-edge|lighter|left|keep-all|justify|italic|inter-word|inter-ideograph|inside|inset|inline|inline-block|inherit|inactive|ideograph-space|ideograph-parenthesis|ideograph-numeric|ideograph-alpha|horizontal|hidden|help|hand|groove|fixed|ellipsis|e-resize|double|dotted|distribute|distribute-space|distribute-letter|distribute-all-lines|disc|disabled|default|decimal|dashed|crosshair|collapse|col-resize|circle|char|center|capitalize|break-word|break-all|bottom|both|bolder|bold|block|bidi-override|below|baseline|auto|always|all-scroll|absolute|table|table-cell)\\b"},{begin:":",end:";",contains:[t,i,e.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,{className:"meta",begin:"!important"}]},{begin:"@(page|font-face)",lexemes:"@[a-z-]+",keywords:"@page @font-face"},{begin:"@",end:"[{;]",returnBegin:!0,keywords:"and or not only",contains:[{begin:"@[a-z-]+",className:"keyword"},t,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,i,e.CSS_NUMBER_MODE]}]}}}());hljs.registerLanguage("r",function(){"use strict";return function(e){var n="([a-zA-Z]|\\.[a-zA-Z.])[a-zA-Z0-9._]*";return{name:"R",contains:[e.HASH_COMMENT_MODE,{begin:n,keywords:{$pattern:n,keyword:"function if in break next repeat else for return switch while try tryCatch stop warning require library attach detach source setMethod setGeneric setGroupGeneric setClass ...",literal:"NULL NA TRUE FALSE T F Inf NaN NA_integer_|10 NA_real_|10 NA_character_|10 NA_complex_|10"},relevance:0},{className:"number",begin:"0[xX][0-9a-fA-F]+[Li]?\\b",relevance:0},{className:"number",begin:"\\d+(?:[eE][+\\-]?\\d*)?L\\b",relevance:0},{className:"number",begin:"\\d+\\.(?!\\d)(?:i\\b)?",relevance:0},{className:"number",begin:"\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d*)?i?\\b",relevance:0},{className:"number",begin:"\\.\\d+(?:[eE][+\\-]?\\d*)?i?\\b",relevance:0},{begin:"`",end:"`",relevance:0},{className:"string",contains:[e.BACKSLASH_ESCAPE],variants:[{begin:'"',end:'"'},{begin:"'",end:"'"}]}]}}}());hljs.registerLanguage("sql",function(){"use strict";return function(e){var t=e.COMMENT("--","$");return{name:"SQL",case_insensitive:!0,illegal:/[<>{}*]/,contains:[{beginKeywords:"begin end start commit rollback savepoint lock alter create drop rename call delete do handler insert load replace select truncate update set show pragma grant merge describe use explain help declare prepare execute deallocate release unlock purge reset change stop analyze cache flush optimize repair kill install uninstall checksum restore check backup revoke comment values with",end:/;/,endsWithParent:!0,keywords:{$pattern:/[\w\.]+/,keyword:"as abort abs absolute acc acce accep accept access accessed accessible account acos action activate add addtime admin administer advanced advise aes_decrypt aes_encrypt after agent aggregate ali alia alias all allocate allow alter always analyze ancillary and anti any anydata anydataset anyschema anytype apply archive archived archivelog are as asc ascii asin assembly assertion associate asynchronous at atan atn2 attr attri attrib attribu attribut attribute attributes audit authenticated authentication authid authors auto autoallocate autodblink autoextend automatic availability avg backup badfile basicfile before begin beginning benchmark between bfile bfile_base big bigfile bin binary_double binary_float binlog bit_and bit_count bit_length bit_or bit_xor bitmap blob_base block blocksize body both bound bucket buffer_cache buffer_pool build bulk by byte byteordermark bytes cache caching call calling cancel capacity cascade cascaded case cast catalog category ceil ceiling chain change changed char_base char_length character_length characters characterset charindex charset charsetform charsetid check checksum checksum_agg child choose chr chunk class cleanup clear client clob clob_base clone close cluster_id cluster_probability cluster_set clustering coalesce coercibility col collate collation collect colu colum column column_value columns columns_updated comment commit compact compatibility compiled complete composite_limit compound compress compute concat concat_ws concurrent confirm conn connec connect connect_by_iscycle connect_by_isleaf connect_by_root connect_time connection consider consistent constant constraint constraints constructor container content contents context contributors controlfile conv convert convert_tz corr corr_k corr_s corresponding corruption cos cost count count_big counted covar_pop covar_samp cpu_per_call cpu_per_session crc32 create creation critical cross cube cume_dist curdate current current_date current_time current_timestamp current_user cursor curtime customdatum cycle data database databases datafile datafiles datalength date_add date_cache date_format date_sub dateadd datediff datefromparts datename datepart datetime2fromparts day day_to_second dayname dayofmonth dayofweek dayofyear days db_role_change dbtimezone ddl deallocate declare decode decompose decrement decrypt deduplicate def defa defau defaul default defaults deferred defi defin define degrees delayed delegate delete delete_all delimited demand dense_rank depth dequeue des_decrypt des_encrypt des_key_file desc descr descri describ describe descriptor deterministic diagnostics difference dimension direct_load directory disable disable_all disallow disassociate discardfile disconnect diskgroup distinct distinctrow distribute distributed div do document domain dotnet double downgrade drop dumpfile duplicate duration each edition editionable editions element ellipsis else elsif elt empty enable enable_all enclosed encode encoding encrypt end end-exec endian enforced engine engines enqueue enterprise entityescaping eomonth error errors escaped evalname evaluate event eventdata events except exception exceptions exchange exclude excluding execu execut execute exempt exists exit exp expire explain explode export export_set extended extent external external_1 external_2 externally extract failed failed_login_attempts failover failure far fast feature_set feature_value fetch field fields file file_name_convert filesystem_like_logging final finish first first_value fixed flash_cache flashback floor flush following follows for forall force foreign form forma format found found_rows freelist freelists freepools fresh from from_base64 from_days ftp full function general generated get get_format get_lock getdate getutcdate global global_name globally go goto grant grants greatest group group_concat group_id grouping grouping_id groups gtid_subtract guarantee guard handler hash hashkeys having hea head headi headin heading heap help hex hierarchy high high_priority hosts hour hours http id ident_current ident_incr ident_seed identified identity idle_time if ifnull ignore iif ilike ilm immediate import in include including increment index indexes indexing indextype indicator indices inet6_aton inet6_ntoa inet_aton inet_ntoa infile initial initialized initially initrans inmemory inner innodb input insert install instance instantiable instr interface interleaved intersect into invalidate invisible is is_free_lock is_ipv4 is_ipv4_compat is_not is_not_null is_used_lock isdate isnull isolation iterate java join json json_exists keep keep_duplicates key keys kill language large last last_day last_insert_id last_value lateral lax lcase lead leading least leaves left len lenght length less level levels library like like2 like4 likec limit lines link list listagg little ln load load_file lob lobs local localtime localtimestamp locate locator lock locked log log10 log2 logfile logfiles logging logical logical_reads_per_call logoff logon logs long loop low low_priority lower lpad lrtrim ltrim main make_set makedate maketime managed management manual map mapping mask master master_pos_wait match matched materialized max maxextents maximize maxinstances maxlen maxlogfiles maxloghistory maxlogmembers maxsize maxtrans md5 measures median medium member memcompress memory merge microsecond mid migration min minextents minimum mining minus minute minutes minvalue missing mod mode model modification modify module monitoring month months mount move movement multiset mutex name name_const names nan national native natural nav nchar nclob nested never new newline next nextval no no_write_to_binlog noarchivelog noaudit nobadfile nocheck nocompress nocopy nocycle nodelay nodiscardfile noentityescaping noguarantee nokeep nologfile nomapping nomaxvalue nominimize nominvalue nomonitoring none noneditionable nonschema noorder nopr nopro noprom nopromp noprompt norely noresetlogs noreverse normal norowdependencies noschemacheck noswitch not nothing notice notnull notrim novalidate now nowait nth_value nullif nulls num numb numbe nvarchar nvarchar2 object ocicoll ocidate ocidatetime ociduration ociinterval ociloblocator ocinumber ociref ocirefcursor ocirowid ocistring ocitype oct octet_length of off offline offset oid oidindex old on online only opaque open operations operator optimal optimize option optionally or oracle oracle_date oradata ord ordaudio orddicom orddoc order ordimage ordinality ordvideo organization orlany orlvary out outer outfile outline output over overflow overriding package pad parallel parallel_enable parameters parent parse partial partition partitions pascal passing password password_grace_time password_lock_time password_reuse_max password_reuse_time password_verify_function patch path patindex pctincrease pctthreshold pctused pctversion percent percent_rank percentile_cont percentile_disc performance period period_add period_diff permanent physical pi pipe pipelined pivot pluggable plugin policy position post_transaction pow power pragma prebuilt precedes preceding precision prediction prediction_cost prediction_details prediction_probability prediction_set prepare present preserve prior priority private private_sga privileges procedural procedure procedure_analyze processlist profiles project prompt protection public publishingservername purge quarter query quick quiesce quota quotename radians raise rand range rank raw read reads readsize rebuild record records recover recovery recursive recycle redo reduced ref reference referenced references referencing refresh regexp_like register regr_avgx regr_avgy regr_count regr_intercept regr_r2 regr_slope regr_sxx regr_sxy reject rekey relational relative relaylog release release_lock relies_on relocate rely rem remainder rename repair repeat replace replicate replication required reset resetlogs resize resource respect restore restricted result result_cache resumable resume retention return returning returns reuse reverse revoke right rlike role roles rollback rolling rollup round row row_count rowdependencies rowid rownum rows rtrim rules safe salt sample save savepoint sb1 sb2 sb4 scan schema schemacheck scn scope scroll sdo_georaster sdo_topo_geometry search sec_to_time second seconds section securefile security seed segment select self semi sequence sequential serializable server servererror session session_user sessions_per_user set sets settings sha sha1 sha2 share shared shared_pool short show shrink shutdown si_averagecolor si_colorhistogram si_featurelist si_positionalcolor si_stillimage si_texture siblings sid sign sin size size_t sizes skip slave sleep smalldatetimefromparts smallfile snapshot some soname sort soundex source space sparse spfile split sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_small_result sql_variant_property sqlcode sqldata sqlerror sqlname sqlstate sqrt square standalone standby start starting startup statement static statistics stats_binomial_test stats_crosstab stats_ks_test stats_mode stats_mw_test stats_one_way_anova stats_t_test_ stats_t_test_indep stats_t_test_one stats_t_test_paired stats_wsr_test status std stddev stddev_pop stddev_samp stdev stop storage store stored str str_to_date straight_join strcmp strict string struct stuff style subdate subpartition subpartitions substitutable substr substring subtime subtring_index subtype success sum suspend switch switchoffset switchover sync synchronous synonym sys sys_xmlagg sysasm sysaux sysdate sysdatetimeoffset sysdba sysoper system system_user sysutcdatetime table tables tablespace tablesample tan tdo template temporary terminated tertiary_weights test than then thread through tier ties time time_format time_zone timediff timefromparts timeout timestamp timestampadd timestampdiff timezone_abbr timezone_minute timezone_region to to_base64 to_date to_days to_seconds todatetimeoffset trace tracking transaction transactional translate translation treat trigger trigger_nestlevel triggers trim truncate try_cast try_convert try_parse type ub1 ub2 ub4 ucase unarchived unbounded uncompress under undo unhex unicode uniform uninstall union unique unix_timestamp unknown unlimited unlock unnest unpivot unrecoverable unsafe unsigned until untrusted unusable unused update updated upgrade upped upper upsert url urowid usable usage use use_stored_outlines user user_data user_resources users using utc_date utc_timestamp uuid uuid_short validate validate_password_strength validation valist value values var var_samp varcharc vari varia variab variabl variable variables variance varp varraw varrawc varray verify version versions view virtual visible void wait wallet warning warnings week weekday weekofyear wellformed when whene whenev wheneve whenever where while whitespace window with within without work wrapped xdb xml xmlagg xmlattributes xmlcast xmlcolattval xmlelement xmlexists xmlforest xmlindex xmlnamespaces xmlpi xmlquery xmlroot xmlschema xmlserialize xmltable xmltype xor year year_to_month years yearweek",literal:"true false null unknown",built_in:"array bigint binary bit blob bool boolean char character date dec decimal float int int8 integer interval number numeric real record serial serial8 smallint text time timestamp tinyint varchar varchar2 varying void"},contains:[{className:"string",begin:"'",end:"'",contains:[{begin:"''"}]},{className:"string",begin:'"',end:'"',contains:[{begin:'""'}]},{className:"string",begin:"`",end:"`"},e.C_NUMBER_MODE,e.C_BLOCK_COMMENT_MODE,t,e.HASH_COMMENT_MODE]},e.C_BLOCK_COMMENT_MODE,t,e.HASH_COMMENT_MODE]}}}());hljs.registerLanguage("c",function(){"use strict";return function(e){var n=e.getLanguage("c-like").rawDefinition();return n.name="C",n.aliases=["c","h"],n}}());hljs.registerLanguage("json",function(){"use strict";return function(n){var e={literal:"true false null"},i=[n.C_LINE_COMMENT_MODE,n.C_BLOCK_COMMENT_MODE],t=[n.QUOTE_STRING_MODE,n.C_NUMBER_MODE],a={end:",",endsWithParent:!0,excludeEnd:!0,contains:t,keywords:e},l={begin:"{",end:"}",contains:[{className:"attr",begin:/"/,end:/"/,contains:[n.BACKSLASH_ESCAPE],illegal:"\\n"},n.inherit(a,{begin:/:/})].concat(i),illegal:"\\S"},s={begin:"\\[",end:"\\]",contains:[n.inherit(a)],illegal:"\\S"};return t.push(l,s),i.forEach((function(n){t.push(n)})),{name:"JSON",contains:t,keywords:e,illegal:"\\S"}}}());hljs.registerLanguage("python-repl",function(){"use strict";return function(n){return{aliases:["pycon"],contains:[{className:"meta",starts:{end:/ |$/,starts:{end:"$",subLanguage:"python"}},variants:[{begin:/^>>>(?=[ ]|$)/},{begin:/^\.\.\.(?=[ ]|$)/}]}]}}}());hljs.registerLanguage("markdown",function(){"use strict";return function(n){const e={begin:"<",end:">",subLanguage:"xml",relevance:0},a={begin:"\\[.+?\\][\\(\\[].*?[\\)\\]]",returnBegin:!0,contains:[{className:"string",begin:"\\[",end:"\\]",excludeBegin:!0,returnEnd:!0,relevance:0},{className:"link",begin:"\\]\\(",end:"\\)",excludeBegin:!0,excludeEnd:!0},{className:"symbol",begin:"\\]\\[",end:"\\]",excludeBegin:!0,excludeEnd:!0}],relevance:10},i={className:"strong",contains:[],variants:[{begin:/_{2}/,end:/_{2}/},{begin:/\*{2}/,end:/\*{2}/}]},s={className:"emphasis",contains:[],variants:[{begin:/\*(?!\*)/,end:/\*/},{begin:/_(?!_)/,end:/_/,relevance:0}]};i.contains.push(s),s.contains.push(i);var c=[e,a];return i.contains=i.contains.concat(c),s.contains=s.contains.concat(c),{name:"Markdown",aliases:["md","mkdown","mkd"],contains:[{className:"section",variants:[{begin:"^#{1,6}",end:"$",contains:c=c.concat(i,s)},{begin:"(?=^.+?\\n[=-]{2,}$)",contains:[{begin:"^[=-]*$"},{begin:"^",end:"\\n",contains:c}]}]},e,{className:"bullet",begin:"^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)",end:"\\s+",excludeEnd:!0},i,s,{className:"quote",begin:"^>\\s+",contains:c,end:"$"},{className:"code",variants:[{begin:"(`{3,})(.|\\n)*?\\1`*[ ]*"},{begin:"(~{3,})(.|\\n)*?\\1~*[ ]*"},{begin:"```",end:"```+[ ]*$"},{begin:"~~~",end:"~~~+[ ]*$"},{begin:"`.+?`"},{begin:"(?=^( {4}|\\t))",contains:[{begin:"^( {4}|\\t)",end:"(\\n)$"}],relevance:0}]},{begin:"^[-\\*]{3,}",end:"$"},a,{begin:/^\[[^\n]+\]:/,returnBegin:!0,contains:[{className:"symbol",begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0},{className:"link",begin:/:\s*/,end:/$/,excludeBegin:!0}]}]}}}());hljs.registerLanguage("javascript",function(){"use strict";const e=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],n=["true","false","null","undefined","NaN","Infinity"],a=[].concat(["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],["arguments","this","super","console","window","document","localStorage","module","global"],["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer"],["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"]);function s(e){return r("(?=",e,")")}function r(...e){return e.map(e=>(function(e){return e?"string"==typeof e?e:e.source:null})(e)).join("")}return function(t){var i="[A-Za-z$_][0-9A-Za-z$_]*",c={begin:/<[A-Za-z0-9\\._:-]+/,end:/\/[A-Za-z0-9\\._:-]+>|\/>/},o={$pattern:"[A-Za-z$_][0-9A-Za-z$_]*",keyword:e.join(" "),literal:n.join(" "),built_in:a.join(" ")},l={className:"number",variants:[{begin:"\\b(0[bB][01]+)n?"},{begin:"\\b(0[oO][0-7]+)n?"},{begin:t.C_NUMBER_RE+"n?"}],relevance:0},E={className:"subst",begin:"\\$\\{",end:"\\}",keywords:o,contains:[]},d={begin:"html`",end:"",starts:{end:"`",returnEnd:!1,contains:[t.BACKSLASH_ESCAPE,E],subLanguage:"xml"}},g={begin:"css`",end:"",starts:{end:"`",returnEnd:!1,contains:[t.BACKSLASH_ESCAPE,E],subLanguage:"css"}},u={className:"string",begin:"`",end:"`",contains:[t.BACKSLASH_ESCAPE,E]};E.contains=[t.APOS_STRING_MODE,t.QUOTE_STRING_MODE,d,g,u,l,t.REGEXP_MODE];var b=E.contains.concat([{begin:/\(/,end:/\)/,contains:["self"].concat(E.contains,[t.C_BLOCK_COMMENT_MODE,t.C_LINE_COMMENT_MODE])},t.C_BLOCK_COMMENT_MODE,t.C_LINE_COMMENT_MODE]),_={className:"params",begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,contains:b};return{name:"JavaScript",aliases:["js","jsx","mjs","cjs"],keywords:o,contains:[t.SHEBANG({binary:"node",relevance:5}),{className:"meta",relevance:10,begin:/^\s*['"]use (strict|asm)['"]/},t.APOS_STRING_MODE,t.QUOTE_STRING_MODE,d,g,u,t.C_LINE_COMMENT_MODE,t.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{className:"doctag",begin:"@[A-Za-z]+",contains:[{className:"type",begin:"\\{",end:"\\}",relevance:0},{className:"variable",begin:i+"(?=\\s*(-)|$)",endsParent:!0,relevance:0},{begin:/(?=[^\n])\s/,relevance:0}]}]}),t.C_BLOCK_COMMENT_MODE,l,{begin:r(/[{,\n]\s*/,s(r(/(((\/\/.*)|(\/\*(.|\n)*\*\/))\s*)*/,i+"\\s*:"))),relevance:0,contains:[{className:"attr",begin:i+s("\\s*:"),relevance:0}]},{begin:"("+t.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*",keywords:"return throw case",contains:[t.C_LINE_COMMENT_MODE,t.C_BLOCK_COMMENT_MODE,t.REGEXP_MODE,{className:"function",begin:"(\\([^(]*(\\([^(]*(\\([^(]*\\))?\\))?\\)|"+t.UNDERSCORE_IDENT_RE+")\\s*=>",returnBegin:!0,end:"\\s*=>",contains:[{className:"params",variants:[{begin:t.UNDERSCORE_IDENT_RE},{className:null,begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:o,contains:b}]}]},{begin:/,/,relevance:0},{className:"",begin:/\s/,end:/\s*/,skip:!0},{variants:[{begin:"<>",end:""},{begin:c.begin,end:c.end}],subLanguage:"xml",contains:[{begin:c.begin,end:c.end,skip:!0,contains:["self"]}]}],relevance:0},{className:"function",beginKeywords:"function",end:/\{/,excludeEnd:!0,contains:[t.inherit(t.TITLE_MODE,{begin:i}),_],illegal:/\[|%/},{begin:/\$[(.]/},t.METHOD_GUARD,{className:"class",beginKeywords:"class",end:/[{;=]/,excludeEnd:!0,illegal:/[:"\[\]]/,contains:[{beginKeywords:"extends"},t.UNDERSCORE_TITLE_MODE]},{beginKeywords:"constructor",end:/\{/,excludeEnd:!0},{begin:"(get|set)\\s+(?="+i+"\\()",end:/{/,keywords:"get set",contains:[t.inherit(t.TITLE_MODE,{begin:i}),{begin:/\(\)/},_]}],illegal:/#(?!!)/}}}());hljs.registerLanguage("typescript",function(){"use strict";const e=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],n=["true","false","null","undefined","NaN","Infinity"],a=[].concat(["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],["arguments","this","super","console","window","document","localStorage","module","global"],["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer"],["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"]);return function(r){var t={$pattern:"[A-Za-z$_][0-9A-Za-z$_]*",keyword:e.concat(["type","namespace","typedef","interface","public","private","protected","implements","declare","abstract","readonly"]).join(" "),literal:n.join(" "),built_in:a.concat(["any","void","number","boolean","string","object","never","enum"]).join(" ")},s={className:"meta",begin:"@[A-Za-z$_][0-9A-Za-z$_]*"},i={className:"number",variants:[{begin:"\\b(0[bB][01]+)n?"},{begin:"\\b(0[oO][0-7]+)n?"},{begin:r.C_NUMBER_RE+"n?"}],relevance:0},o={className:"subst",begin:"\\$\\{",end:"\\}",keywords:t,contains:[]},c={begin:"html`",end:"",starts:{end:"`",returnEnd:!1,contains:[r.BACKSLASH_ESCAPE,o],subLanguage:"xml"}},l={begin:"css`",end:"",starts:{end:"`",returnEnd:!1,contains:[r.BACKSLASH_ESCAPE,o],subLanguage:"css"}},E={className:"string",begin:"`",end:"`",contains:[r.BACKSLASH_ESCAPE,o]};o.contains=[r.APOS_STRING_MODE,r.QUOTE_STRING_MODE,c,l,E,i,r.REGEXP_MODE];var d={begin:"\\(",end:/\)/,keywords:t,contains:["self",r.QUOTE_STRING_MODE,r.APOS_STRING_MODE,r.NUMBER_MODE]},u={className:"params",begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:t,contains:[r.C_LINE_COMMENT_MODE,r.C_BLOCK_COMMENT_MODE,s,d]};return{name:"TypeScript",aliases:["ts"],keywords:t,contains:[r.SHEBANG(),{className:"meta",begin:/^\s*['"]use strict['"]/},r.APOS_STRING_MODE,r.QUOTE_STRING_MODE,c,l,E,r.C_LINE_COMMENT_MODE,r.C_BLOCK_COMMENT_MODE,i,{begin:"("+r.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*",keywords:"return throw case",contains:[r.C_LINE_COMMENT_MODE,r.C_BLOCK_COMMENT_MODE,r.REGEXP_MODE,{className:"function",begin:"(\\([^(]*(\\([^(]*(\\([^(]*\\))?\\))?\\)|"+r.UNDERSCORE_IDENT_RE+")\\s*=>",returnBegin:!0,end:"\\s*=>",contains:[{className:"params",variants:[{begin:r.UNDERSCORE_IDENT_RE},{className:null,begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:t,contains:d.contains}]}]}],relevance:0},{className:"function",beginKeywords:"function",end:/[\{;]/,excludeEnd:!0,keywords:t,contains:["self",r.inherit(r.TITLE_MODE,{begin:"[A-Za-z$_][0-9A-Za-z$_]*"}),u],illegal:/%/,relevance:0},{beginKeywords:"constructor",end:/[\{;]/,excludeEnd:!0,contains:["self",u]},{begin:/module\./,keywords:{built_in:"module"},relevance:0},{beginKeywords:"module",end:/\{/,excludeEnd:!0},{beginKeywords:"interface",end:/\{/,excludeEnd:!0,keywords:"interface extends"},{begin:/\$[(.]/},{begin:"\\."+r.IDENT_RE,relevance:0},s,d]}}}());hljs.registerLanguage("plaintext",function(){"use strict";return function(t){return{name:"Plain text",aliases:["text","txt"],disableAutodetect:!0}}}());hljs.registerLanguage("less",function(){"use strict";return function(e){var n="([\\w-]+|@{[\\w-]+})",a=[],s=[],t=function(e){return{className:"string",begin:"~?"+e+".*?"+e}},r=function(e,n,a){return{className:e,begin:n,relevance:a}},i={begin:"\\(",end:"\\)",contains:s,relevance:0};s.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,t("'"),t('"'),e.CSS_NUMBER_MODE,{begin:"(url|data-uri)\\(",starts:{className:"string",end:"[\\)\\n]",excludeEnd:!0}},r("number","#[0-9A-Fa-f]+\\b"),i,r("variable","@@?[\\w-]+",10),r("variable","@{[\\w-]+}"),r("built_in","~?`[^`]*?`"),{className:"attribute",begin:"[\\w-]+\\s*:",end:":",returnBegin:!0,excludeEnd:!0},{className:"meta",begin:"!important"});var c=s.concat({begin:"{",end:"}",contains:a}),l={beginKeywords:"when",endsWithParent:!0,contains:[{beginKeywords:"and not"}].concat(s)},o={begin:n+"\\s*:",returnBegin:!0,end:"[;}]",relevance:0,contains:[{className:"attribute",begin:n,end:":",excludeEnd:!0,starts:{endsWithParent:!0,illegal:"[<=$]",relevance:0,contains:s}}]},g={className:"keyword",begin:"@(import|media|charset|font-face|(-[a-z]+-)?keyframes|supports|document|namespace|page|viewport|host)\\b",starts:{end:"[;{}]",returnEnd:!0,contains:s,relevance:0}},d={className:"variable",variants:[{begin:"@[\\w-]+\\s*:",relevance:15},{begin:"@[\\w-]+"}],starts:{end:"[;}]",returnEnd:!0,contains:c}},b={variants:[{begin:"[\\.#:&\\[>]",end:"[;{}]"},{begin:n,end:"{"}],returnBegin:!0,returnEnd:!0,illegal:"[<='$\"]",relevance:0,contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,l,r("keyword","all\\b"),r("variable","@{[\\w-]+}"),r("selector-tag",n+"%?",0),r("selector-id","#"+n),r("selector-class","\\."+n,0),r("selector-tag","&",0),{className:"selector-attr",begin:"\\[",end:"\\]"},{className:"selector-pseudo",begin:/:(:)?[a-zA-Z0-9\_\-\+\(\)"'.]+/},{begin:"\\(",end:"\\)",contains:c},{begin:"!important"}]};return a.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,g,d,o,b),{name:"Less",case_insensitive:!0,illegal:"[=>'/<($\"]",contains:a}}}());hljs.registerLanguage("lua",function(){"use strict";return function(e){var t={begin:"\\[=*\\[",end:"\\]=*\\]",contains:["self"]},a=[e.COMMENT("--(?!\\[=*\\[)","$"),e.COMMENT("--\\[=*\\[","\\]=*\\]",{contains:[t],relevance:10})];return{name:"Lua",keywords:{$pattern:e.UNDERSCORE_IDENT_RE,literal:"true false nil",keyword:"and break do else elseif end for goto if in local not or repeat return then until while",built_in:"_G _ENV _VERSION __index __newindex __mode __call __metatable __tostring __len __gc __add __sub __mul __div __mod __pow __concat __unm __eq __lt __le assert collectgarbage dofile error getfenv getmetatable ipairs load loadfile loadstring module next pairs pcall print rawequal rawget rawset require select setfenv setmetatable tonumber tostring type unpack xpcall arg self coroutine resume yield status wrap create running debug getupvalue debug sethook getmetatable gethook setmetatable setlocal traceback setfenv getinfo setupvalue getlocal getregistry getfenv io lines write close flush open output type read stderr stdin input stdout popen tmpfile math log max acos huge ldexp pi cos tanh pow deg tan cosh sinh random randomseed frexp ceil floor rad abs sqrt modf asin min mod fmod log10 atan2 exp sin atan os exit setlocale date getenv difftime remove time clock tmpname rename execute package preload loadlib loaded loaders cpath config path seeall string sub upper len gfind rep find match char dump gmatch reverse byte format gsub lower table setn insert getn foreachi maxn foreach concat sort remove"},contains:a.concat([{className:"function",beginKeywords:"function",end:"\\)",contains:[e.inherit(e.TITLE_MODE,{begin:"([_a-zA-Z]\\w*\\.)*([_a-zA-Z]\\w*:)?[_a-zA-Z]\\w*"}),{className:"params",begin:"\\(",endsWithParent:!0,contains:a}].concat(a)},e.C_NUMBER_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{className:"string",begin:"\\[=*\\[",end:"\\]=*\\]",contains:[t],relevance:5}])}}}()); + */ +var hljs=function(){"use strict";var e={exports:{}};function t(e){ +return e instanceof Map?e.clear=e.delete=e.set=()=>{ +throw Error("map is read-only")}:e instanceof Set&&(e.add=e.clear=e.delete=()=>{ +throw Error("set is read-only") +}),Object.freeze(e),Object.getOwnPropertyNames(e).forEach((n=>{var i=e[n] +;"object"!=typeof i||Object.isFrozen(i)||t(i)})),e} +e.exports=t,e.exports.default=t;var n=e.exports;class i{constructor(e){ +void 0===e.data&&(e.data={}),this.data=e.data,this.isMatchIgnored=!1} +ignoreMatch(){this.isMatchIgnored=!0}}function r(e){ +return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'") +}function s(e,...t){const n=Object.create(null);for(const t in e)n[t]=e[t] +;return t.forEach((e=>{for(const t in e)n[t]=e[t]})),n}const o=e=>!!e.kind +;class a{constructor(e,t){ +this.buffer="",this.classPrefix=t.classPrefix,e.walk(this)}addText(e){ +this.buffer+=r(e)}openNode(e){if(!o(e))return;let t=e.kind +;t=e.sublanguage?"language-"+t:((e,{prefix:t})=>{if(e.includes(".")){ +const n=e.split(".") +;return[`${t}${n.shift()}`,...n.map(((e,t)=>`${e}${"_".repeat(t+1)}`))].join(" ") +}return`${t}${e}`})(t,{prefix:this.classPrefix}),this.span(t)}closeNode(e){ +o(e)&&(this.buffer+="
")}value(){return this.buffer}span(e){ +this.buffer+=``}}class l{constructor(){this.rootNode={ +children:[]},this.stack=[this.rootNode]}get top(){ +return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){ +this.top.children.push(e)}openNode(e){const t={kind:e,children:[]} +;this.add(t),this.stack.push(t)}closeNode(){ +if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){ +for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)} +walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,t){ +return"string"==typeof t?e.addText(t):t.children&&(e.openNode(t), +t.children.forEach((t=>this._walk(e,t))),e.closeNode(t)),e}static _collapse(e){ +"string"!=typeof e&&e.children&&(e.children.every((e=>"string"==typeof e))?e.children=[e.children.join("")]:e.children.forEach((e=>{ +l._collapse(e)})))}}class c extends l{constructor(e){super(),this.options=e} +addKeyword(e,t){""!==e&&(this.openNode(t),this.addText(e),this.closeNode())} +addText(e){""!==e&&this.add(e)}addSublanguage(e,t){const n=e.root +;n.kind=t,n.sublanguage=!0,this.add(n)}toHTML(){ +return new a(this,this.options).value()}finalize(){return!0}}function g(e){ +return e?"string"==typeof e?e:e.source:null}function d(...e){ +return e.map((e=>g(e))).join("")}function u(...e){return"("+((e=>{ +const t=e[e.length-1] +;return"object"==typeof t&&t.constructor===Object?(e.splice(e.length-1,1),t):{} +})(e).capture?"":"?:")+e.map((e=>g(e))).join("|")+")"}function h(e){ +return RegExp(e.toString()+"|").exec("").length-1} +const f=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./ +;function p(e,{joinWith:t}){let n=0;return e.map((e=>{n+=1;const t=n +;let i=g(e),r="";for(;i.length>0;){const e=f.exec(i);if(!e){r+=i;break} +r+=i.substring(0,e.index), +i=i.substring(e.index+e[0].length),"\\"===e[0][0]&&e[1]?r+="\\"+(Number(e[1])+t):(r+=e[0], +"("===e[0]&&n++)}return r})).map((e=>`(${e})`)).join(t)} +const b="[a-zA-Z]\\w*",m="[a-zA-Z_]\\w*",E="\\b\\d+(\\.\\d+)?",x="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",y="\\b(0b[01]+)",w={ +begin:"\\\\[\\s\\S]",relevance:0},_={scope:"string",begin:"'",end:"'", +illegal:"\\n",contains:[w]},v={scope:"string",begin:'"',end:'"',illegal:"\\n", +contains:[w]},O=(e,t,n={})=>{const i=s({scope:"comment",begin:e,end:t, +contains:[]},n);i.contains.push({scope:"doctag", +begin:"[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)", +end:/(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):/,excludeBegin:!0,relevance:0}) +;const r=u("I","a","is","so","us","to","at","if","in","it","on",/[A-Za-z]+['](d|ve|re|ll|t|s|n)/,/[A-Za-z]+[-][a-z]+/,/[A-Za-z][a-z]{2,}/) +;return i.contains.push({begin:d(/[ ]+/,"(",r,/[.]?[:]?([.][ ]|[ ])/,"){3}")}),i +},k=O("//","$"),N=O("/\\*","\\*/"),S=O("#","$");var M=Object.freeze({ +__proto__:null,MATCH_NOTHING_RE:/\b\B/,IDENT_RE:b,UNDERSCORE_IDENT_RE:m, +NUMBER_RE:E,C_NUMBER_RE:x,BINARY_NUMBER_RE:y, +RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~", +SHEBANG:(e={})=>{const t=/^#![ ]*\// +;return e.binary&&(e.begin=d(t,/.*\b/,e.binary,/\b.*/)),s({scope:"meta",begin:t, +end:/$/,relevance:0,"on:begin":(e,t)=>{0!==e.index&&t.ignoreMatch()}},e)}, +BACKSLASH_ESCAPE:w,APOS_STRING_MODE:_,QUOTE_STRING_MODE:v,PHRASAL_WORDS_MODE:{ +begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/ +},COMMENT:O,C_LINE_COMMENT_MODE:k,C_BLOCK_COMMENT_MODE:N,HASH_COMMENT_MODE:S, +NUMBER_MODE:{scope:"number",begin:E,relevance:0},C_NUMBER_MODE:{scope:"number", +begin:x,relevance:0},BINARY_NUMBER_MODE:{scope:"number",begin:y,relevance:0}, +REGEXP_MODE:{begin:/(?=\/[^/\n]*\/)/,contains:[{scope:"regexp",begin:/\//, +end:/\/[gimuy]*/,illegal:/\n/,contains:[w,{begin:/\[/,end:/\]/,relevance:0, +contains:[w]}]}]},TITLE_MODE:{scope:"title",begin:b,relevance:0}, +UNDERSCORE_TITLE_MODE:{scope:"title",begin:m,relevance:0},METHOD_GUARD:{ +begin:"\\.\\s*[a-zA-Z_]\\w*",relevance:0},END_SAME_AS_BEGIN:e=>Object.assign(e,{ +"on:begin":(e,t)=>{t.data._beginMatch=e[1]},"on:end":(e,t)=>{ +t.data._beginMatch!==e[1]&&t.ignoreMatch()}})});function R(e,t){ +"."===e.input[e.index-1]&&t.ignoreMatch()}function j(e,t){ +void 0!==e.className&&(e.scope=e.className,delete e.className)}function A(e,t){ +t&&e.beginKeywords&&(e.begin="\\b("+e.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)", +e.__beforeBegin=R,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords, +void 0===e.relevance&&(e.relevance=0))}function I(e,t){ +Array.isArray(e.illegal)&&(e.illegal=u(...e.illegal))}function B(e,t){ +if(e.match){ +if(e.begin||e.end)throw Error("begin & end are not supported with match") +;e.begin=e.match,delete e.match}}function T(e,t){ +void 0===e.relevance&&(e.relevance=1)}const L=(e,t)=>{if(!e.beforeMatch)return +;if(e.starts)throw Error("beforeMatch cannot be used with starts") +;const n=Object.assign({},e);Object.keys(e).forEach((t=>{delete e[t] +})),e.keywords=n.keywords, +e.begin=d(n.beforeMatch,d("(?=",n.begin,")")),e.starts={relevance:0, +contains:[Object.assign(n,{endsParent:!0})]},e.relevance=0,delete n.beforeMatch +},D=["of","and","for","in","not","or","if","then","parent","list","value"] +;function P(e,t,n="keyword"){const i=Object.create(null) +;return"string"==typeof e?r(n,e.split(" ")):Array.isArray(e)?r(n,e):Object.keys(e).forEach((n=>{ +Object.assign(i,P(e[n],t,n))})),i;function r(e,n){ +t&&(n=n.map((e=>e.toLowerCase()))),n.forEach((t=>{const n=t.split("|") +;i[n[0]]=[e,C(n[0],n[1])]}))}}function C(e,t){ +return t?Number(t):(e=>D.includes(e.toLowerCase()))(e)?0:1}const H={},$=e=>{ +console.error(e)},U=(e,...t)=>{console.log("WARN: "+e,...t)},z=(e,t)=>{ +H[`${e}/${t}`]||(console.log(`Deprecated as of ${e}. ${t}`),H[`${e}/${t}`]=!0) +},K=Error();function W(e,t,{key:n}){let i=0;const r=e[n],s={},o={} +;for(let e=1;e<=t.length;e++)o[e+i]=r[e],s[e+i]=!0,i+=h(t[e-1]) +;e[n]=o,e[n]._emit=s,e[n]._multi=!0}function X(e){(e=>{ +e.scope&&"object"==typeof e.scope&&null!==e.scope&&(e.beginScope=e.scope, +delete e.scope)})(e),"string"==typeof e.beginScope&&(e.beginScope={ +_wrap:e.beginScope}),"string"==typeof e.endScope&&(e.endScope={_wrap:e.endScope +}),(e=>{if(Array.isArray(e.begin)){ +if(e.skip||e.excludeBegin||e.returnBegin)throw $("skip, excludeBegin, returnBegin not compatible with beginScope: {}"), +K +;if("object"!=typeof e.beginScope||null===e.beginScope)throw $("beginScope must be object"), +K;W(e,e.begin,{key:"beginScope"}),e.begin=p(e.begin,{joinWith:""})}})(e),(e=>{ +if(Array.isArray(e.end)){ +if(e.skip||e.excludeEnd||e.returnEnd)throw $("skip, excludeEnd, returnEnd not compatible with endScope: {}"), +K +;if("object"!=typeof e.endScope||null===e.endScope)throw $("endScope must be object"), +K;W(e,e.end,{key:"endScope"}),e.end=p(e.end,{joinWith:""})}})(e)}function G(e){ +function t(t,n){return RegExp(g(t),"m"+(e.case_insensitive?"i":"")+(n?"g":""))} +class n{constructor(){ +this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0} +addRule(e,t){ +t.position=this.position++,this.matchIndexes[this.matchAt]=t,this.regexes.push([t,e]), +this.matchAt+=h(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null) +;const e=this.regexes.map((e=>e[1]));this.matcherRe=t(p(e,{joinWith:"|" +}),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex +;const t=this.matcherRe.exec(e);if(!t)return null +;const n=t.findIndex(((e,t)=>t>0&&void 0!==e)),i=this.matchIndexes[n] +;return t.splice(0,n),Object.assign(t,i)}}class i{constructor(){ +this.rules=[],this.multiRegexes=[], +this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){ +if(this.multiRegexes[e])return this.multiRegexes[e];const t=new n +;return this.rules.slice(e).forEach((([e,n])=>t.addRule(e,n))), +t.compile(),this.multiRegexes[e]=t,t}resumingScanAtSamePosition(){ +return 0!==this.regexIndex}considerAll(){this.regexIndex=0}addRule(e,t){ +this.rules.push([e,t]),"begin"===t.type&&this.count++}exec(e){ +const t=this.getMatcher(this.regexIndex);t.lastIndex=this.lastIndex +;let n=t.exec(e) +;if(this.resumingScanAtSamePosition())if(n&&n.index===this.lastIndex);else{ +const t=this.getMatcher(0);t.lastIndex=this.lastIndex+1,n=t.exec(e)} +return n&&(this.regexIndex+=n.position+1, +this.regexIndex===this.count&&this.considerAll()),n}} +if(e.compilerExtensions||(e.compilerExtensions=[]), +e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.") +;return e.classNameAliases=s(e.classNameAliases||{}),function n(r,o){const a=r +;if(r.isCompiled)return a +;[j,B,X,L].forEach((e=>e(r,o))),e.compilerExtensions.forEach((e=>e(r,o))), +r.__beforeBegin=null,[A,I,T].forEach((e=>e(r,o))),r.isCompiled=!0;let l=null +;return"object"==typeof r.keywords&&r.keywords.$pattern&&(r.keywords=Object.assign({},r.keywords), +l=r.keywords.$pattern, +delete r.keywords.$pattern),l=l||/\w+/,r.keywords&&(r.keywords=P(r.keywords,e.case_insensitive)), +a.keywordPatternRe=t(l,!0), +o&&(r.begin||(r.begin=/\B|\b/),a.beginRe=t(r.begin),r.end||r.endsWithParent||(r.end=/\B|\b/), +r.end&&(a.endRe=t(r.end)), +a.terminatorEnd=g(r.end)||"",r.endsWithParent&&o.terminatorEnd&&(a.terminatorEnd+=(r.end?"|":"")+o.terminatorEnd)), +r.illegal&&(a.illegalRe=t(r.illegal)), +r.contains||(r.contains=[]),r.contains=[].concat(...r.contains.map((e=>(e=>(e.variants&&!e.cachedVariants&&(e.cachedVariants=e.variants.map((t=>s(e,{ +variants:null},t)))),e.cachedVariants?e.cachedVariants:Z(e)?s(e,{ +starts:e.starts?s(e.starts):null +}):Object.isFrozen(e)?s(e):e))("self"===e?r:e)))),r.contains.forEach((e=>{n(e,a) +})),r.starts&&n(r.starts,o),a.matcher=(e=>{const t=new i +;return e.contains.forEach((e=>t.addRule(e.begin,{rule:e,type:"begin" +}))),e.terminatorEnd&&t.addRule(e.terminatorEnd,{type:"end" +}),e.illegal&&t.addRule(e.illegal,{type:"illegal"}),t})(a),a}(e)}function Z(e){ +return!!e&&(e.endsWithParent||Z(e.starts))}const F=r,V=s,q=Symbol("nomatch") +;var J=(e=>{const t=Object.create(null),r=Object.create(null),s=[];let o=!0 +;const a="Could not find the language '{}', did you forget to load/include a language module?",l={ +disableAutodetect:!0,name:"Plain text",contains:[]};let g={ +ignoreUnescapedHTML:!1,noHighlightRe:/^(no-?highlight)$/i, +languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-", +cssSelector:"pre code",languages:null,__emitter:c};function d(e){ +return g.noHighlightRe.test(e)}function u(e,t,n,i){let r="",s="" +;"object"==typeof t?(r=e, +n=t.ignoreIllegals,s=t.language,i=void 0):(z("10.7.0","highlight(lang, code, ...args) has been deprecated."), +z("10.7.0","Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277"), +s=e,r=t),void 0===n&&(n=!0);const o={code:r,language:s};w("before:highlight",o) +;const a=o.result?o.result:h(o.language,o.code,n,i) +;return a.code=o.code,w("after:highlight",a),a}function h(e,n,r,s){ +const l=Object.create(null);function c(){if(!k.keywords)return void S.addText(M) +;let e=0;k.keywordPatternRe.lastIndex=0;let t=k.keywordPatternRe.exec(M),n="" +;for(;t;){n+=M.substring(e,t.index) +;const r=_.case_insensitive?t[0].toLowerCase():t[0],s=(i=r,k.keywords[i]);if(s){ +const[e,i]=s +;if(S.addText(n),n="",l[r]=(l[r]||0)+1,l[r]<=7&&(R+=i),e.startsWith("_"))n+=t[0];else{ +const n=_.classNameAliases[e]||e;S.addKeyword(t[0],n)}}else n+=t[0] +;e=k.keywordPatternRe.lastIndex,t=k.keywordPatternRe.exec(M)}var i +;n+=M.substr(e),S.addText(n)}function d(){null!=k.subLanguage?(()=>{ +if(""===M)return;let e=null;if("string"==typeof k.subLanguage){ +if(!t[k.subLanguage])return void S.addText(M) +;e=h(k.subLanguage,M,!0,N[k.subLanguage]),N[k.subLanguage]=e._top +}else e=f(M,k.subLanguage.length?k.subLanguage:null) +;k.relevance>0&&(R+=e.relevance),S.addSublanguage(e._emitter,e.language) +})():c(),M=""}function u(e,t){let n=1;for(;void 0!==t[n];){if(!e._emit[n]){n++ +;continue}const i=_.classNameAliases[e[n]]||e[n],r=t[n] +;i?S.addKeyword(r,i):(M=r,c(),M=""),n++}}function p(e,t){ +return e.scope&&"string"==typeof e.scope&&S.openNode(_.classNameAliases[e.scope]||e.scope), +e.beginScope&&(e.beginScope._wrap?(S.addKeyword(M,_.classNameAliases[e.beginScope._wrap]||e.beginScope._wrap), +M=""):e.beginScope._multi&&(u(e.beginScope,t),M="")),k=Object.create(e,{parent:{ +value:k}}),k}function b(e,t,n){let r=((e,t)=>{const n=e&&e.exec(t) +;return n&&0===n.index})(e.endRe,n);if(r){if(e["on:end"]){const n=new i(e) +;e["on:end"](t,n),n.isMatchIgnored&&(r=!1)}if(r){ +for(;e.endsParent&&e.parent;)e=e.parent;return e}} +if(e.endsWithParent)return b(e.parent,t,n)}function m(e){ +return 0===k.matcher.regexIndex?(M+=e[0],1):(I=!0,0)}function x(e){ +const t=e[0],i=n.substr(e.index),r=b(k,e,i);if(!r)return q;const s=k +;k.endScope&&k.endScope._wrap?(d(), +S.addKeyword(t,k.endScope._wrap)):k.endScope&&k.endScope._multi?(d(), +u(k.endScope,e)):s.skip?M+=t:(s.returnEnd||s.excludeEnd||(M+=t), +d(),s.excludeEnd&&(M=t));do{ +k.scope&&!k.isMultiClass&&S.closeNode(),k.skip||k.subLanguage||(R+=k.relevance), +k=k.parent}while(k!==r.parent) +;return r.starts&&p(r.starts,e),s.returnEnd?0:t.length}let y={};function w(t,s){ +const a=s&&s[0];if(M+=t,null==a)return d(),0 +;if("begin"===y.type&&"end"===s.type&&y.index===s.index&&""===a){ +if(M+=n.slice(s.index,s.index+1),!o){const t=Error(`0 width match regex (${e})`) +;throw t.languageName=e,t.badRule=y.rule,t}return 1} +if(y=s,"begin"===s.type)return(e=>{ +const t=e[0],n=e.rule,r=new i(n),s=[n.__beforeBegin,n["on:begin"]] +;for(const n of s)if(n&&(n(e,r),r.isMatchIgnored))return m(t) +;return n.skip?M+=t:(n.excludeBegin&&(M+=t), +d(),n.returnBegin||n.excludeBegin||(M=t)),p(n,e),n.returnBegin?0:t.length})(s) +;if("illegal"===s.type&&!r){ +const e=Error('Illegal lexeme "'+a+'" for mode "'+(k.scope||"")+'"') +;throw e.mode=k,e}if("end"===s.type){const e=x(s);if(e!==q)return e} +if("illegal"===s.type&&""===a)return 1 +;if(A>1e5&&A>3*s.index)throw Error("potential infinite loop, way more iterations than matches") +;return M+=a,a.length}const _=E(e) +;if(!_)throw $(a.replace("{}",e)),Error('Unknown language: "'+e+'"') +;const v=G(_);let O="",k=s||v;const N={},S=new g.__emitter(g);(()=>{const e=[] +;for(let t=k;t!==_;t=t.parent)t.scope&&e.unshift(t.scope) +;e.forEach((e=>S.openNode(e)))})();let M="",R=0,j=0,A=0,I=!1;try{ +for(k.matcher.considerAll();;){ +A++,I?I=!1:k.matcher.considerAll(),k.matcher.lastIndex=j +;const e=k.matcher.exec(n);if(!e)break;const t=w(n.substring(j,e.index),e) +;j=e.index+t}return w(n.substr(j)),S.closeAllNodes(),S.finalize(),O=S.toHTML(),{ +language:e,value:O,relevance:R,illegal:!1,_emitter:S,_top:k}}catch(t){ +if(t.message&&t.message.includes("Illegal"))return{language:e,value:F(n), +illegal:!0,relevance:0,_illegalBy:{message:t.message,index:j, +context:n.slice(j-100,j+100),mode:t.mode,resultSoFar:O},_emitter:S};if(o)return{ +language:e,value:F(n),illegal:!1,relevance:0,errorRaised:t,_emitter:S,_top:k} +;throw t}}function f(e,n){n=n||g.languages||Object.keys(t);const i=(e=>{ +const t={value:F(e),illegal:!1,relevance:0,_top:l,_emitter:new g.__emitter(g)} +;return t._emitter.addText(e),t})(e),r=n.filter(E).filter(y).map((t=>h(t,e,!1))) +;r.unshift(i);const s=r.sort(((e,t)=>{ +if(e.relevance!==t.relevance)return t.relevance-e.relevance +;if(e.language&&t.language){if(E(e.language).supersetOf===t.language)return 1 +;if(E(t.language).supersetOf===e.language)return-1}return 0})),[o,a]=s,c=o +;return c.secondBest=a,c}function p(e){let t=null;const n=(e=>{ +let t=e.className+" ";t+=e.parentNode?e.parentNode.className:"" +;const n=g.languageDetectRe.exec(t);if(n){const t=E(n[1]) +;return t||(U(a.replace("{}",n[1])), +U("Falling back to no-highlight mode for this block.",e)),t?n[1]:"no-highlight"} +return t.split(/\s+/).find((e=>d(e)||E(e)))})(e);if(d(n))return +;w("before:highlightElement",{el:e,language:n +}),!g.ignoreUnescapedHTML&&e.children.length>0&&(console.warn("One of your code blocks includes unescaped HTML. This is a potentially serious security risk."), +console.warn("https://github.com/highlightjs/highlight.js/issues/2886"), +console.warn(e)),t=e;const i=t.textContent,s=n?u(i,{language:n,ignoreIllegals:!0 +}):f(i);e.innerHTML=s.value,((e,t,n)=>{const i=t&&r[t]||n +;e.classList.add("hljs"),e.classList.add("language-"+i) +})(e,n,s.language),e.result={language:s.language,re:s.relevance, +relevance:s.relevance},s.secondBest&&(e.secondBest={ +language:s.secondBest.language,relevance:s.secondBest.relevance +}),w("after:highlightElement",{el:e,result:s,text:i})}let b=!1;function m(){ +"loading"!==document.readyState?document.querySelectorAll(g.cssSelector).forEach(p):b=!0 +}function E(e){return e=(e||"").toLowerCase(),t[e]||t[r[e]]} +function x(e,{languageName:t}){"string"==typeof e&&(e=[e]),e.forEach((e=>{ +r[e.toLowerCase()]=t}))}function y(e){const t=E(e) +;return t&&!t.disableAutodetect}function w(e,t){const n=e;s.forEach((e=>{ +e[n]&&e[n](t)}))} +"undefined"!=typeof window&&window.addEventListener&&window.addEventListener("DOMContentLoaded",(()=>{ +b&&m()}),!1),Object.assign(e,{highlight:u,highlightAuto:f,highlightAll:m, +highlightElement:p, +highlightBlock:e=>(z("10.7.0","highlightBlock will be removed entirely in v12.0"), +z("10.7.0","Please use highlightElement now."),p(e)),configure:e=>{g=V(g,e)}, +initHighlighting:()=>{ +m(),z("10.6.0","initHighlighting() deprecated. Use highlightAll() now.")}, +initHighlightingOnLoad:()=>{ +m(),z("10.6.0","initHighlightingOnLoad() deprecated. Use highlightAll() now.") +},registerLanguage:(n,i)=>{let r=null;try{r=i(e)}catch(e){ +if($("Language definition for '{}' could not be registered.".replace("{}",n)), +!o)throw e;$(e),r=l} +r.name||(r.name=n),t[n]=r,r.rawDefinition=i.bind(null,e),r.aliases&&x(r.aliases,{ +languageName:n})},unregisterLanguage:e=>{delete t[e] +;for(const t of Object.keys(r))r[t]===e&&delete r[t]}, +listLanguages:()=>Object.keys(t),getLanguage:E,registerAliases:x, +autoDetection:y,inherit:V,addPlugin:e=>{(e=>{ +e["before:highlightBlock"]&&!e["before:highlightElement"]&&(e["before:highlightElement"]=t=>{ +e["before:highlightBlock"](Object.assign({block:t.el},t)) +}),e["after:highlightBlock"]&&!e["after:highlightElement"]&&(e["after:highlightElement"]=t=>{ +e["after:highlightBlock"](Object.assign({block:t.el},t))})})(e),s.push(e)} +}),e.debugMode=()=>{o=!1},e.safeMode=()=>{o=!0},e.versionString="11.0.1" +;for(const e in M)"object"==typeof M[e]&&n(M[e]);return Object.assign(e,M),e +})({}),Y=Object.freeze({__proto__:null});const Q=J +;for(const e of Object.keys(Y)){const t=e.replace("grmr_","") +;Q.registerLanguage(t,Y[e])}return Q}() +;"object"==typeof exports&&"undefined"!=typeof module&&(module.exports=hljs);hljs.registerLanguage("scheme",(()=>{"use strict";return e=>{ +const t="[^\\(\\)\\[\\]\\{\\}\",'`;#|\\\\\\s]+",n={$pattern:t, +built_in:"case-lambda call/cc class define-class exit-handler field import inherit init-field interface let*-values let-values let/ec mixin opt-lambda override protect provide public rename require require-for-syntax syntax syntax-case syntax-error unit/sig unless when with-syntax and begin call-with-current-continuation call-with-input-file call-with-output-file case cond define define-syntax delay do dynamic-wind else for-each if lambda let let* let-syntax letrec letrec-syntax map or syntax-rules ' * + , ,@ - ... / ; < <= = => > >= ` abs acos angle append apply asin assoc assq assv atan boolean? caar cadr call-with-input-file call-with-output-file call-with-values car cdddar cddddr cdr ceiling char->integer char-alphabetic? char-ci<=? char-ci=? char-ci>? char-downcase char-lower-case? char-numeric? char-ready? char-upcase char-upper-case? char-whitespace? char<=? char=? char>? char? close-input-port close-output-port complex? cons cos current-input-port current-output-port denominator display eof-object? eq? equal? eqv? eval even? exact->inexact exact? exp expt floor force gcd imag-part inexact->exact inexact? input-port? integer->char integer? interaction-environment lcm length list list->string list->vector list-ref list-tail list? load log magnitude make-polar make-rectangular make-string make-vector max member memq memv min modulo negative? newline not null-environment null? number->string number? numerator odd? open-input-file open-output-file output-port? pair? peek-char port? positive? procedure? quasiquote quote quotient rational? rationalize read read-char real-part real? remainder reverse round scheme-report-environment set! set-car! set-cdr! sin sqrt string string->list string->number string->symbol string-append string-ci<=? string-ci=? string-ci>? string-copy string-fill! string-length string-ref string-set! string<=? string=? string>? string? substring symbol->string symbol? tan transcript-off transcript-on truncate values vector vector->list vector-fill! vector-length vector-ref vector-set! with-input-from-file with-output-to-file write write-char zero?" +},r={className:"literal",begin:"(#t|#f|#\\\\"+t+"|#\\\\.)"},a={ +className:"number",variants:[{begin:"(-|\\+)?\\d+([./]\\d+)?",relevance:0},{ +begin:"(-|\\+)?\\d+([./]\\d+)?[+\\-](-|\\+)?\\d+([./]\\d+)?i",relevance:0},{ +begin:"#b[0-1]+(/[0-1]+)?"},{begin:"#o[0-7]+(/[0-7]+)?"},{ +begin:"#x[0-9a-f]+(/[0-9a-f]+)?"}]},i=e.QUOTE_STRING_MODE,c=[e.COMMENT(";","$",{ +relevance:0}),e.COMMENT("#\\|","\\|#")],s={begin:t,relevance:0},l={ +className:"symbol",begin:"'"+t},o={endsWithParent:!0,relevance:0},g={variants:[{ +begin:/'/},{begin:"`"}],contains:[{begin:"\\(",end:"\\)", +contains:["self",r,i,a,s,l]}]},u={className:"name",relevance:0,begin:t, +keywords:n},d={variants:[{begin:"\\(",end:"\\)"},{begin:"\\[",end:"\\]"}], +contains:[{begin:/lambda/,endsWithParent:!0,returnBegin:!0,contains:[u,{ +endsParent:!0,variants:[{begin:/\(/,end:/\)/},{begin:/\[/,end:/\]/}], +contains:[s]}]},u,o]};return o.contains=[r,a,i,s,l,g,d].concat(c),{ +name:"Scheme",illegal:/\S/,contains:[e.SHEBANG(),a,i,l,g,d].concat(c)}}})());hljs.registerLanguage("clojure",(()=>{"use strict";return e=>{ +const t="a-zA-Z_\\-!.?+*=<>&#'",n="["+t+"]["+t+"0-9/;:]*",r="def defonce defprotocol defstruct defmulti defmethod defn- defn defmacro deftype defrecord",a={ +$pattern:n, +built_in:r+" cond apply if-not if-let if not not= =|0 <|0 >|0 <=|0 >=|0 ==|0 +|0 /|0 *|0 -|0 rem quot neg? pos? delay? symbol? keyword? true? false? integer? empty? coll? list? set? ifn? fn? associative? sequential? sorted? counted? reversible? number? decimal? class? distinct? isa? float? rational? reduced? ratio? odd? even? char? seq? vector? string? map? nil? contains? zero? instance? not-every? not-any? libspec? -> ->> .. . inc compare do dotimes mapcat take remove take-while drop letfn drop-last take-last drop-while while intern condp case reduced cycle split-at split-with repeat replicate iterate range merge zipmap declare line-seq sort comparator sort-by dorun doall nthnext nthrest partition eval doseq await await-for let agent atom send send-off release-pending-sends add-watch mapv filterv remove-watch agent-error restart-agent set-error-handler error-handler set-error-mode! error-mode shutdown-agents quote var fn loop recur throw try monitor-enter monitor-exit macroexpand macroexpand-1 for dosync and or when when-not when-let comp juxt partial sequence memoize constantly complement identity assert peek pop doto proxy first rest cons cast coll last butlast sigs reify second ffirst fnext nfirst nnext meta with-meta ns in-ns create-ns import refer keys select-keys vals key val rseq name namespace promise into transient persistent! conj! assoc! dissoc! pop! disj! use class type num float double short byte boolean bigint biginteger bigdec print-method print-dup throw-if printf format load compile get-in update-in pr pr-on newline flush read slurp read-line subvec with-open memfn time re-find re-groups rand-int rand mod locking assert-valid-fdecl alias resolve ref deref refset swap! reset! set-validator! compare-and-set! alter-meta! reset-meta! commute get-validator alter ref-set ref-history-count ref-min-history ref-max-history ensure sync io! new next conj set! to-array future future-call into-array aset gen-class reduce map filter find empty hash-map hash-set sorted-map sorted-map-by sorted-set sorted-set-by vec vector seq flatten reverse assoc dissoc list disj get union difference intersection extend extend-type extend-protocol int nth delay count concat chunk chunk-buffer chunk-append chunk-first chunk-rest max min dec unchecked-inc-int unchecked-inc unchecked-dec-inc unchecked-dec unchecked-negate unchecked-add-int unchecked-add unchecked-subtract-int unchecked-subtract chunk-next chunk-cons chunked-seq? prn vary-meta lazy-seq spread list* str find-keyword keyword symbol gensym force rationalize" +},s={begin:n,relevance:0},o={className:"number",begin:"[-+]?\\d+(\\.\\d+)?", +relevance:0},i=e.inherit(e.QUOTE_STRING_MODE,{illegal:null +}),c=e.COMMENT(";","$",{relevance:0}),d={className:"literal", +begin:/\b(true|false|nil)\b/},l={begin:"[\\[\\{]",end:"[\\]\\}]",relevance:0 +},m={className:"comment",begin:"\\^"+n},p=e.COMMENT("\\^\\{","\\}"),u={ +className:"symbol",begin:"[:]{1,2}"+n},f={begin:"\\(",end:"\\)"},h={ +endsWithParent:!0,relevance:0},y={keywords:a,className:"name",begin:n, +relevance:0,starts:h},g=[f,i,m,p,c,u,l,o,d,s],b={beginKeywords:r,keywords:{ +$pattern:n,keyword:r},end:'(\\[|#|\\d|"|:|\\{|\\)|\\(|$)',contains:[{ +className:"title",begin:n,relevance:0,excludeEnd:!0,endsParent:!0}].concat(g)} +;return f.contains=[e.COMMENT("comment",""),b,y,h], +h.contains=g,l.contains=g,p.contains=[l],{name:"Clojure",aliases:["clj"], +illegal:/\S/,contains:[f,i,m,p,c,u,l,o,d]}}})());hljs.registerLanguage("bash",(()=>{"use strict";function e(...e){ +return e.map((e=>{return(s=e)?"string"==typeof s?s:s.source:null;var s +})).join("")}return s=>{const n={},t={begin:/\$\{/,end:/\}/,contains:["self",{ +begin:/:-/,contains:[n]}]};Object.assign(n,{className:"variable",variants:[{ +begin:e(/\$[\w\d#@][\w\d_]*/,"(?![\\w\\d])(?![$])")},t]});const a={ +className:"subst",begin:/\$\(/,end:/\)/,contains:[s.BACKSLASH_ESCAPE]},i={ +begin:/<<-?\s*(?=\w+)/,starts:{contains:[s.END_SAME_AS_BEGIN({begin:/(\w+)/, +end:/(\w+)/,className:"string"})]}},c={className:"string",begin:/"/,end:/"/, +contains:[s.BACKSLASH_ESCAPE,n,a]};a.contains.push(c);const o={begin:/\$\(\(/, +end:/\)\)/,contains:[{begin:/\d+#[0-9a-f]+/,className:"number"},s.NUMBER_MODE,n] +},r=s.SHEBANG({binary:"(fish|bash|zsh|sh|csh|ksh|tcsh|dash|scsh)",relevance:10 +}),l={className:"function",begin:/\w[\w\d_]*\s*\(\s*\)\s*\{/,returnBegin:!0, +contains:[s.inherit(s.TITLE_MODE,{begin:/\w[\w\d_]*/})],relevance:0};return{ +name:"Bash",aliases:["sh"],keywords:{$pattern:/\b[a-z._-]+\b/, +keyword:["if","then","else","elif","fi","for","while","in","do","done","case","esac","function"], +literal:["true","false"], +built_in:"break cd continue eval exec exit export getopts hash pwd readonly return shift test times trap umask unset alias bind builtin caller command declare echo enable help let local logout mapfile printf read readarray source type typeset ulimit unalias set shopt autoload bg bindkey bye cap chdir clone comparguments compcall compctl compdescribe compfiles compgroups compquote comptags comptry compvalues dirs disable disown echotc echoti emulate fc fg float functions getcap getln history integer jobs kill limit log noglob popd print pushd pushln rehash sched setcap setopt stat suspend ttyctl unfunction unhash unlimit unsetopt vared wait whence where which zcompile zformat zftp zle zmodload zparseopts zprof zpty zregexparse zsocket zstyle ztcp" +},contains:[r,s.SHEBANG(),l,o,s.HASH_COMMENT_MODE,i,c,{className:"",begin:/\\"/ +},{className:"string",begin:/'/,end:/'/},n]}}})());hljs.registerLanguage("lisp",(()=>{"use strict";return e=>{ +var n="[a-zA-Z_\\-+\\*\\/<=>&#][a-zA-Z0-9_\\-+*\\/<=>&#!]*",a="\\|[^]*?\\|",i="(-|\\+)?\\d+(\\.\\d+|\\/\\d+)?((d|e|f|l|s|D|E|F|L|S)(\\+|-)?\\d+)?",s={ +className:"literal",begin:"\\b(t{1}|nil)\\b"},l={className:"number",variants:[{ +begin:i,relevance:0},{begin:"#(b|B)[0-1]+(/[0-1]+)?"},{ +begin:"#(o|O)[0-7]+(/[0-7]+)?"},{begin:"#(x|X)[0-9a-fA-F]+(/[0-9a-fA-F]+)?"},{ +begin:"#(c|C)\\("+i+" +"+i,end:"\\)"}]},b=e.inherit(e.QUOTE_STRING_MODE,{ +illegal:null}),g=e.COMMENT(";","$",{relevance:0}),r={begin:"\\*",end:"\\*"},t={ +className:"symbol",begin:"[:&]"+n},c={begin:n,relevance:0},d={begin:a},o={ +contains:[l,b,r,t,{begin:"\\(",end:"\\)",contains:["self",s,b,l,c]},c], +variants:[{begin:"['`]\\(",end:"\\)"},{begin:"\\(quote ",end:"\\)",keywords:{ +name:"quote"}},{begin:"'"+a}]},v={variants:[{begin:"'"+n},{ +begin:"#'"+n+"(::"+n+")*"}]},m={begin:"\\(\\s*",end:"\\)"},u={endsWithParent:!0, +relevance:0};return m.contains=[{className:"name",variants:[{begin:n,relevance:0 +},{begin:a}]},u],u.contains=[o,v,m,s,l,b,g,r,t,d,c],{name:"Lisp",illegal:/\S/, +contains:[l,e.SHEBANG(),s,b,g,o,v,m,c]}}})());hljs.registerLanguage("css",(()=>{"use strict" +;const e=["a","abbr","address","article","aside","audio","b","blockquote","body","button","canvas","caption","cite","code","dd","del","details","dfn","div","dl","dt","em","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","header","hgroup","html","i","iframe","img","input","ins","kbd","label","legend","li","main","mark","menu","nav","object","ol","p","q","quote","samp","section","span","strong","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","tr","ul","var","video"],t=["any-hover","any-pointer","aspect-ratio","color","color-gamut","color-index","device-aspect-ratio","device-height","device-width","display-mode","forced-colors","grid","height","hover","inverted-colors","monochrome","orientation","overflow-block","overflow-inline","pointer","prefers-color-scheme","prefers-contrast","prefers-reduced-motion","prefers-reduced-transparency","resolution","scan","scripting","update","width","min-width","max-width","min-height","max-height"],i=["active","any-link","blank","checked","current","default","defined","dir","disabled","drop","empty","enabled","first","first-child","first-of-type","fullscreen","future","focus","focus-visible","focus-within","has","host","host-context","hover","indeterminate","in-range","invalid","is","lang","last-child","last-of-type","left","link","local-link","not","nth-child","nth-col","nth-last-child","nth-last-col","nth-last-of-type","nth-of-type","only-child","only-of-type","optional","out-of-range","past","placeholder-shown","read-only","read-write","required","right","root","scope","target","target-within","user-invalid","valid","visited","where"],o=["after","backdrop","before","cue","cue-region","first-letter","first-line","grammar-error","marker","part","placeholder","selection","slotted","spelling-error"],r=["align-content","align-items","align-self","animation","animation-delay","animation-direction","animation-duration","animation-fill-mode","animation-iteration-count","animation-name","animation-play-state","animation-timing-function","auto","backface-visibility","background","background-attachment","background-clip","background-color","background-image","background-origin","background-position","background-repeat","background-size","border","border-bottom","border-bottom-color","border-bottom-left-radius","border-bottom-right-radius","border-bottom-style","border-bottom-width","border-collapse","border-color","border-image","border-image-outset","border-image-repeat","border-image-slice","border-image-source","border-image-width","border-left","border-left-color","border-left-style","border-left-width","border-radius","border-right","border-right-color","border-right-style","border-right-width","border-spacing","border-style","border-top","border-top-color","border-top-left-radius","border-top-right-radius","border-top-style","border-top-width","border-width","bottom","box-decoration-break","box-shadow","box-sizing","break-after","break-before","break-inside","caption-side","clear","clip","clip-path","color","column-count","column-fill","column-gap","column-rule","column-rule-color","column-rule-style","column-rule-width","column-span","column-width","columns","content","counter-increment","counter-reset","cursor","direction","display","empty-cells","filter","flex","flex-basis","flex-direction","flex-flow","flex-grow","flex-shrink","flex-wrap","float","font","font-display","font-family","font-feature-settings","font-kerning","font-language-override","font-size","font-size-adjust","font-smoothing","font-stretch","font-style","font-variant","font-variant-ligatures","font-variation-settings","font-weight","height","hyphens","icon","image-orientation","image-rendering","image-resolution","ime-mode","inherit","initial","justify-content","left","letter-spacing","line-height","list-style","list-style-image","list-style-position","list-style-type","margin","margin-bottom","margin-left","margin-right","margin-top","marks","mask","max-height","max-width","min-height","min-width","nav-down","nav-index","nav-left","nav-right","nav-up","none","normal","object-fit","object-position","opacity","order","orphans","outline","outline-color","outline-offset","outline-style","outline-width","overflow","overflow-wrap","overflow-x","overflow-y","padding","padding-bottom","padding-left","padding-right","padding-top","page-break-after","page-break-before","page-break-inside","perspective","perspective-origin","pointer-events","position","quotes","resize","right","src","tab-size","table-layout","text-align","text-align-last","text-decoration","text-decoration-color","text-decoration-line","text-decoration-style","text-indent","text-overflow","text-rendering","text-shadow","text-transform","text-underline-position","top","transform","transform-origin","transform-style","transition","transition-delay","transition-duration","transition-property","transition-timing-function","unicode-bidi","vertical-align","visibility","white-space","widows","width","word-break","word-spacing","word-wrap","z-index"].reverse() +;return n=>{const a=(e=>({IMPORTANT:{scope:"meta",begin:"!important"},HEXCOLOR:{ +scope:"number",begin:"#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})"}, +ATTRIBUTE_SELECTOR_MODE:{scope:"selector-attr",begin:/\[/,end:/\]/,illegal:"$", +contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},CSS_NUMBER_MODE:{ +scope:"number", +begin:e.NUMBER_RE+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?", +relevance:0}}))(n),l=[n.APOS_STRING_MODE,n.QUOTE_STRING_MODE];return{name:"CSS", +case_insensitive:!0,illegal:/[=|'\$]/,keywords:{keyframePosition:"from to"}, +classNameAliases:{keyframePosition:"selector-tag"}, +contains:[n.C_BLOCK_COMMENT_MODE,{begin:/-(webkit|moz|ms|o)-(?=[a-z])/ +},a.CSS_NUMBER_MODE,{className:"selector-id",begin:/#[A-Za-z0-9_-]+/,relevance:0 +},{className:"selector-class",begin:"\\.[a-zA-Z-][a-zA-Z0-9_-]*",relevance:0 +},a.ATTRIBUTE_SELECTOR_MODE,{className:"selector-pseudo",variants:[{ +begin:":("+i.join("|")+")"},{begin:"::("+o.join("|")+")"}]},{ +className:"attribute",begin:"\\b("+r.join("|")+")\\b"},{begin:":",end:"[;}]", +contains:[a.HEXCOLOR,a.IMPORTANT,a.CSS_NUMBER_MODE,...l,{ +begin:/(url|data-uri)\(/,end:/\)/,relevance:0,keywords:{built_in:"url data-uri" +},contains:[{className:"string",begin:/[^)]/,endsWithParent:!0,excludeEnd:!0}] +},{className:"built_in",begin:/[\w-]+(?=\()/}]},{ +begin:(s=/@/,((...e)=>e.map((e=>(e=>e?"string"==typeof e?e:e.source:null)(e))).join(""))("(?=",s,")")), +end:"[{;]",relevance:0,illegal:/:/,contains:[{className:"keyword", +begin:/@-?\w[\w]*(-\w+)*/},{begin:/\s/,endsWithParent:!0,excludeEnd:!0, +relevance:0,keywords:{$pattern:/[a-z-]+/,keyword:"and or not only", +attribute:t.join(" ")},contains:[{begin:/[a-z-]+(?=:)/,className:"attribute" +},...l,a.CSS_NUMBER_MODE]}]},{className:"selector-tag", +begin:"\\b("+e.join("|")+")\\b"}]};var s}})());hljs.registerLanguage("vim",(()=>{"use strict";return e=>({name:"Vim Script", +keywords:{$pattern:/[!#@\w]+/, +keyword:"N|0 P|0 X|0 a|0 ab abc abo al am an|0 ar arga argd arge argdo argg argl argu as au aug aun b|0 bN ba bad bd be bel bf bl bm bn bo bp br brea breaka breakd breakl bro bufdo buffers bun bw c|0 cN cNf ca cabc caddb cad caddf cal cat cb cc ccl cd ce cex cf cfir cgetb cgete cg changes chd che checkt cl cla clo cm cmapc cme cn cnew cnf cno cnorea cnoreme co col colo com comc comp con conf cope cp cpf cq cr cs cst cu cuna cunme cw delm deb debugg delc delf dif diffg diffo diffp diffpu diffs diffthis dig di dl dell dj dli do doautoa dp dr ds dsp e|0 ea ec echoe echoh echom echon el elsei em en endfo endf endt endw ene ex exe exi exu f|0 files filet fin fina fini fir fix fo foldc foldd folddoc foldo for fu go gr grepa gu gv ha helpf helpg helpt hi hid his ia iabc if ij il im imapc ime ino inorea inoreme int is isp iu iuna iunme j|0 ju k|0 keepa kee keepj lN lNf l|0 lad laddb laddf la lan lat lb lc lch lcl lcs le lefta let lex lf lfir lgetb lgete lg lgr lgrepa lh ll lla lli lmak lm lmapc lne lnew lnf ln loadk lo loc lockv lol lope lp lpf lr ls lt lu lua luad luaf lv lvimgrepa lw m|0 ma mak map mapc marks mat me menut mes mk mks mksp mkv mkvie mod mz mzf nbc nb nbs new nm nmapc nme nn nnoreme noa no noh norea noreme norm nu nun nunme ol o|0 om omapc ome on ono onoreme opt ou ounme ow p|0 profd prof pro promptr pc ped pe perld po popu pp pre prev ps pt ptN ptf ptj ptl ptn ptp ptr pts pu pw py3 python3 py3d py3f py pyd pyf quita qa rec red redi redr redraws reg res ret retu rew ri rightb rub rubyd rubyf rund ru rv sN san sa sal sav sb sbN sba sbf sbl sbm sbn sbp sbr scrip scripte scs se setf setg setl sf sfir sh sim sig sil sl sla sm smap smapc sme sn sni sno snor snoreme sor so spelld spe spelli spellr spellu spellw sp spr sre st sta startg startr star stopi stj sts sun sunm sunme sus sv sw sy synti sync tN tabN tabc tabdo tabe tabf tabfir tabl tabm tabnew tabn tabo tabp tabr tabs tab ta tags tc tcld tclf te tf th tj tl tm tn to tp tr try ts tu u|0 undoj undol una unh unl unlo unm unme uns up ve verb vert vim vimgrepa vi viu vie vm vmapc vme vne vn vnoreme vs vu vunme windo w|0 wN wa wh wi winc winp wn wp wq wqa ws wu wv x|0 xa xmapc xm xme xn xnoreme xu xunme y|0 z|0 ~ Next Print append abbreviate abclear aboveleft all amenu anoremenu args argadd argdelete argedit argglobal arglocal argument ascii autocmd augroup aunmenu buffer bNext ball badd bdelete behave belowright bfirst blast bmodified bnext botright bprevious brewind break breakadd breakdel breaklist browse bunload bwipeout change cNext cNfile cabbrev cabclear caddbuffer caddexpr caddfile call catch cbuffer cclose center cexpr cfile cfirst cgetbuffer cgetexpr cgetfile chdir checkpath checktime clist clast close cmap cmapclear cmenu cnext cnewer cnfile cnoremap cnoreabbrev cnoremenu copy colder colorscheme command comclear compiler continue confirm copen cprevious cpfile cquit crewind cscope cstag cunmap cunabbrev cunmenu cwindow delete delmarks debug debuggreedy delcommand delfunction diffupdate diffget diffoff diffpatch diffput diffsplit digraphs display deletel djump dlist doautocmd doautoall deletep drop dsearch dsplit edit earlier echo echoerr echohl echomsg else elseif emenu endif endfor endfunction endtry endwhile enew execute exit exusage file filetype find finally finish first fixdel fold foldclose folddoopen folddoclosed foldopen function global goto grep grepadd gui gvim hardcopy help helpfind helpgrep helptags highlight hide history insert iabbrev iabclear ijump ilist imap imapclear imenu inoremap inoreabbrev inoremenu intro isearch isplit iunmap iunabbrev iunmenu join jumps keepalt keepmarks keepjumps lNext lNfile list laddexpr laddbuffer laddfile last language later lbuffer lcd lchdir lclose lcscope left leftabove lexpr lfile lfirst lgetbuffer lgetexpr lgetfile lgrep lgrepadd lhelpgrep llast llist lmake lmap lmapclear lnext lnewer lnfile lnoremap loadkeymap loadview lockmarks lockvar lolder lopen lprevious lpfile lrewind ltag lunmap luado luafile lvimgrep lvimgrepadd lwindow move mark make mapclear match menu menutranslate messages mkexrc mksession mkspell mkvimrc mkview mode mzscheme mzfile nbclose nbkey nbsart next nmap nmapclear nmenu nnoremap nnoremenu noautocmd noremap nohlsearch noreabbrev noremenu normal number nunmap nunmenu oldfiles open omap omapclear omenu only onoremap onoremenu options ounmap ounmenu ownsyntax print profdel profile promptfind promptrepl pclose pedit perl perldo pop popup ppop preserve previous psearch ptag ptNext ptfirst ptjump ptlast ptnext ptprevious ptrewind ptselect put pwd py3do py3file python pydo pyfile quit quitall qall read recover redo redir redraw redrawstatus registers resize retab return rewind right rightbelow ruby rubydo rubyfile rundo runtime rviminfo substitute sNext sandbox sargument sall saveas sbuffer sbNext sball sbfirst sblast sbmodified sbnext sbprevious sbrewind scriptnames scriptencoding scscope set setfiletype setglobal setlocal sfind sfirst shell simalt sign silent sleep slast smagic smapclear smenu snext sniff snomagic snoremap snoremenu sort source spelldump spellgood spellinfo spellrepall spellundo spellwrong split sprevious srewind stop stag startgreplace startreplace startinsert stopinsert stjump stselect sunhide sunmap sunmenu suspend sview swapname syntax syntime syncbind tNext tabNext tabclose tabedit tabfind tabfirst tablast tabmove tabnext tabonly tabprevious tabrewind tag tcl tcldo tclfile tearoff tfirst throw tjump tlast tmenu tnext topleft tprevious trewind tselect tunmenu undo undojoin undolist unabbreviate unhide unlet unlockvar unmap unmenu unsilent update vglobal version verbose vertical vimgrep vimgrepadd visual viusage view vmap vmapclear vmenu vnew vnoremap vnoremenu vsplit vunmap vunmenu write wNext wall while winsize wincmd winpos wnext wprevious wqall wsverb wundo wviminfo xit xall xmapclear xmap xmenu xnoremap xnoremenu xunmap xunmenu yank", +built_in:"synIDtrans atan2 range matcharg did_filetype asin feedkeys xor argv complete_check add getwinposx getqflist getwinposy screencol clearmatches empty extend getcmdpos mzeval garbagecollect setreg ceil sqrt diff_hlID inputsecret get getfperm getpid filewritable shiftwidth max sinh isdirectory synID system inputrestore winline atan visualmode inputlist tabpagewinnr round getregtype mapcheck hasmapto histdel argidx findfile sha256 exists toupper getcmdline taglist string getmatches bufnr strftime winwidth bufexists strtrans tabpagebuflist setcmdpos remote_read printf setloclist getpos getline bufwinnr float2nr len getcmdtype diff_filler luaeval resolve libcallnr foldclosedend reverse filter has_key bufname str2float strlen setline getcharmod setbufvar index searchpos shellescape undofile foldclosed setqflist buflisted strchars str2nr virtcol floor remove undotree remote_expr winheight gettabwinvar reltime cursor tabpagenr finddir localtime acos getloclist search tanh matchend rename gettabvar strdisplaywidth type abs py3eval setwinvar tolower wildmenumode log10 spellsuggest bufloaded synconcealed nextnonblank server2client complete settabwinvar executable input wincol setmatches getftype hlID inputsave searchpair or screenrow line settabvar histadd deepcopy strpart remote_peek and eval getftime submatch screenchar winsaveview matchadd mkdir screenattr getfontname libcall reltimestr getfsize winnr invert pow getbufline byte2line soundfold repeat fnameescape tagfiles sin strwidth spellbadword trunc maparg log lispindent hostname setpos globpath remote_foreground getchar synIDattr fnamemodify cscope_connection stridx winbufnr indent min complete_add nr2char searchpairpos inputdialog values matchlist items hlexists strridx browsedir expand fmod pathshorten line2byte argc count getwinvar glob foldtextresult getreg foreground cosh matchdelete has char2nr simplify histget searchdecl iconv winrestcmd pumvisible writefile foldlevel haslocaldir keys cos matchstr foldtext histnr tan tempname getcwd byteidx getbufvar islocked escape eventhandler remote_send serverlist winrestview synstack pyeval prevnonblank readfile cindent filereadable changenr exp" +},illegal:/;/,contains:[e.NUMBER_MODE,{className:"string",begin:"'",end:"'", +illegal:"\\n"},{className:"string",begin:/"(\\"|\n\\|[^"\n])*"/ +},e.COMMENT('"',"$"),{className:"variable",begin:/[bwtglsav]:[\w\d_]+/},{ +begin:[/\b(?:function|function!)/,/\s+/,e.IDENT_RE],className:{1:"keyword", +3:"title"},end:"$",relevance:0,contains:[{className:"params",begin:"\\(", +end:"\\)"}]},{className:"symbol",begin:/<[\w-]+>/}]})})());hljs.registerLanguage("xml",(()=>{"use strict";function e(e){ +return e?"string"==typeof e?e:e.source:null}function n(e){return a("(?=",e,")")} +function a(...n){return n.map((n=>e(n))).join("")}function s(...n){ +return"("+((e=>{const n=e[e.length-1] +;return"object"==typeof n&&n.constructor===Object?(e.splice(e.length-1,1),n):{} +})(n).capture?"":"?:")+n.map((n=>e(n))).join("|")+")"}return e=>{ +const t=a(/[A-Z_]/,a("(?:",/[A-Z0-9_.-]*:/,")?"),/[A-Z0-9_.-]*/),i={ +className:"symbol",begin:/&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/},c={begin:/\s/, +contains:[{className:"keyword",begin:/#?[a-z_][a-z1-9_-]+/,illegal:/\n/}] +},r=e.inherit(c,{begin:/\(/,end:/\)/}),l=e.inherit(e.APOS_STRING_MODE,{ +className:"string"}),g=e.inherit(e.QUOTE_STRING_MODE,{className:"string"}),m={ +endsWithParent:!0,illegal:/`]+/}]}]}]};return{ +name:"HTML, XML", +aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"], +case_insensitive:!0,contains:[{className:"meta",begin://, +relevance:10,contains:[c,g,l,r,{begin:/\[/,end:/\]/,contains:[{className:"meta", +begin://,contains:[c,r,g,l]}]}]},e.COMMENT(//,{ +relevance:10}),{begin://,relevance:10},i,{ +className:"meta",begin:/<\?xml/,end:/\?>/,relevance:10},{className:"tag", +begin:/)/,end:/>/,keywords:{name:"style"},contains:[m],starts:{ +end:/<\/style>/,returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag", +begin:/)/,end:/>/,keywords:{name:"script"},contains:[m],starts:{ +end:/<\/script>/,returnEnd:!0,subLanguage:["javascript","handlebars","xml"]}},{ +className:"tag",begin:/<>|<\/>/},{className:"tag", +begin:a(//,/>/,/\s/)))),end:/\/?>/,contains:[{className:"name", +begin:t,relevance:0,starts:m}]},{className:"tag",begin:a(/<\//,n(a(t,/>/))), +contains:[{className:"name",begin:t,relevance:0},{begin:/>/,relevance:0, +endsParent:!0}]}]}}})());hljs.registerLanguage("markdown",(()=>{"use strict";function n(...n){ +return n.map((n=>{return(e=n)?"string"==typeof e?e:e.source:null;var e +})).join("")}return e=>{const a={begin:/<\/?[A-Za-z_]/,end:">", +subLanguage:"xml",relevance:0},i={variants:[{begin:/\[.+?\]\[.*?\]/,relevance:0 +},{begin:/\[.+?\]\(((data|javascript|mailto):|(?:http|ftp)s?:\/\/).*?\)/, +relevance:2},{begin:n(/\[.+?\]\(/,/[A-Za-z][A-Za-z0-9+.-]*/,/:\/\/.*?\)/), +relevance:2},{begin:/\[.+?\]\([./?&#].*?\)/,relevance:1},{ +begin:/\[.+?\]\(.*?\)/,relevance:0}],returnBegin:!0,contains:[{ +className:"string",relevance:0,begin:"\\[",end:"\\]",excludeBegin:!0, +returnEnd:!0},{className:"link",relevance:0,begin:"\\]\\(",end:"\\)", +excludeBegin:!0,excludeEnd:!0},{className:"symbol",relevance:0,begin:"\\]\\[", +end:"\\]",excludeBegin:!0,excludeEnd:!0}]},s={className:"strong",contains:[], +variants:[{begin:/_{2}/,end:/_{2}/},{begin:/\*{2}/,end:/\*{2}/}]},c={ +className:"emphasis",contains:[],variants:[{begin:/\*(?!\*)/,end:/\*/},{ +begin:/_(?!_)/,end:/_/,relevance:0}]};s.contains.push(c),c.contains.push(s) +;let t=[a,i] +;return s.contains=s.contains.concat(t),c.contains=c.contains.concat(t), +t=t.concat(s,c),{name:"Markdown",aliases:["md","mkdown","mkd"],contains:[{ +className:"section",variants:[{begin:"^#{1,6}",end:"$",contains:t},{ +begin:"(?=^.+?\\n[=-]{2,}$)",contains:[{begin:"^[=-]*$"},{begin:"^",end:"\\n", +contains:t}]}]},a,{className:"bullet",begin:"^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)", +end:"\\s+",excludeEnd:!0},s,c,{className:"quote",begin:"^>\\s+",contains:t, +end:"$"},{className:"code",variants:[{begin:"(`{3,})[^`](.|\\n)*?\\1`*[ ]*"},{ +begin:"(~{3,})[^~](.|\\n)*?\\1~*[ ]*"},{begin:"```",end:"```+[ ]*$"},{ +begin:"~~~",end:"~~~+[ ]*$"},{begin:"`.+?`"},{begin:"(?=^( {4}|\\t))", +contains:[{begin:"^( {4}|\\t)",end:"(\\n)$"}],relevance:0}]},{ +begin:"^[-\\*]{3,}",end:"$"},i,{begin:/^\[[^\n]+\]:/,returnBegin:!0,contains:[{ +className:"symbol",begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0},{ +className:"link",begin:/:\s*/,end:/$/,excludeBegin:!0}]}]}}})());hljs.registerLanguage("scss",(()=>{"use strict" +;const e=["a","abbr","address","article","aside","audio","b","blockquote","body","button","canvas","caption","cite","code","dd","del","details","dfn","div","dl","dt","em","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","header","hgroup","html","i","iframe","img","input","ins","kbd","label","legend","li","main","mark","menu","nav","object","ol","p","q","quote","samp","section","span","strong","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","tr","ul","var","video"],t=["any-hover","any-pointer","aspect-ratio","color","color-gamut","color-index","device-aspect-ratio","device-height","device-width","display-mode","forced-colors","grid","height","hover","inverted-colors","monochrome","orientation","overflow-block","overflow-inline","pointer","prefers-color-scheme","prefers-contrast","prefers-reduced-motion","prefers-reduced-transparency","resolution","scan","scripting","update","width","min-width","max-width","min-height","max-height"],i=["active","any-link","blank","checked","current","default","defined","dir","disabled","drop","empty","enabled","first","first-child","first-of-type","fullscreen","future","focus","focus-visible","focus-within","has","host","host-context","hover","indeterminate","in-range","invalid","is","lang","last-child","last-of-type","left","link","local-link","not","nth-child","nth-col","nth-last-child","nth-last-col","nth-last-of-type","nth-of-type","only-child","only-of-type","optional","out-of-range","past","placeholder-shown","read-only","read-write","required","right","root","scope","target","target-within","user-invalid","valid","visited","where"],r=["after","backdrop","before","cue","cue-region","first-letter","first-line","grammar-error","marker","part","placeholder","selection","slotted","spelling-error"],o=["align-content","align-items","align-self","animation","animation-delay","animation-direction","animation-duration","animation-fill-mode","animation-iteration-count","animation-name","animation-play-state","animation-timing-function","auto","backface-visibility","background","background-attachment","background-clip","background-color","background-image","background-origin","background-position","background-repeat","background-size","border","border-bottom","border-bottom-color","border-bottom-left-radius","border-bottom-right-radius","border-bottom-style","border-bottom-width","border-collapse","border-color","border-image","border-image-outset","border-image-repeat","border-image-slice","border-image-source","border-image-width","border-left","border-left-color","border-left-style","border-left-width","border-radius","border-right","border-right-color","border-right-style","border-right-width","border-spacing","border-style","border-top","border-top-color","border-top-left-radius","border-top-right-radius","border-top-style","border-top-width","border-width","bottom","box-decoration-break","box-shadow","box-sizing","break-after","break-before","break-inside","caption-side","clear","clip","clip-path","color","column-count","column-fill","column-gap","column-rule","column-rule-color","column-rule-style","column-rule-width","column-span","column-width","columns","content","counter-increment","counter-reset","cursor","direction","display","empty-cells","filter","flex","flex-basis","flex-direction","flex-flow","flex-grow","flex-shrink","flex-wrap","float","font","font-display","font-family","font-feature-settings","font-kerning","font-language-override","font-size","font-size-adjust","font-smoothing","font-stretch","font-style","font-variant","font-variant-ligatures","font-variation-settings","font-weight","height","hyphens","icon","image-orientation","image-rendering","image-resolution","ime-mode","inherit","initial","justify-content","left","letter-spacing","line-height","list-style","list-style-image","list-style-position","list-style-type","margin","margin-bottom","margin-left","margin-right","margin-top","marks","mask","max-height","max-width","min-height","min-width","nav-down","nav-index","nav-left","nav-right","nav-up","none","normal","object-fit","object-position","opacity","order","orphans","outline","outline-color","outline-offset","outline-style","outline-width","overflow","overflow-wrap","overflow-x","overflow-y","padding","padding-bottom","padding-left","padding-right","padding-top","page-break-after","page-break-before","page-break-inside","perspective","perspective-origin","pointer-events","position","quotes","resize","right","src","tab-size","table-layout","text-align","text-align-last","text-decoration","text-decoration-color","text-decoration-line","text-decoration-style","text-indent","text-overflow","text-rendering","text-shadow","text-transform","text-underline-position","top","transform","transform-origin","transform-style","transition","transition-delay","transition-duration","transition-property","transition-timing-function","unicode-bidi","vertical-align","visibility","white-space","widows","width","word-break","word-spacing","word-wrap","z-index"].reverse() +;return a=>{const n=(e=>({IMPORTANT:{scope:"meta",begin:"!important"},HEXCOLOR:{ +scope:"number",begin:"#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})"}, +ATTRIBUTE_SELECTOR_MODE:{scope:"selector-attr",begin:/\[/,end:/\]/,illegal:"$", +contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},CSS_NUMBER_MODE:{ +scope:"number", +begin:e.NUMBER_RE+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?", +relevance:0}}))(a),l=r,s=i,d="@[a-z-]+",c={className:"variable", +begin:"(\\$[a-zA-Z-][a-zA-Z0-9_-]*)\\b"};return{name:"SCSS",case_insensitive:!0, +illegal:"[=/|']",contains:[a.C_LINE_COMMENT_MODE,a.C_BLOCK_COMMENT_MODE,{ +className:"selector-id",begin:"#[A-Za-z0-9_-]+",relevance:0},{ +className:"selector-class",begin:"\\.[A-Za-z0-9_-]+",relevance:0 +},n.ATTRIBUTE_SELECTOR_MODE,{className:"selector-tag", +begin:"\\b("+e.join("|")+")\\b",relevance:0},{className:"selector-pseudo", +begin:":("+s.join("|")+")"},{className:"selector-pseudo", +begin:"::("+l.join("|")+")"},c,{begin:/\(/,end:/\)/,contains:[n.CSS_NUMBER_MODE] +},{className:"attribute",begin:"\\b("+o.join("|")+")\\b"},{ +begin:"\\b(whitespace|wait|w-resize|visible|vertical-text|vertical-ideographic|uppercase|upper-roman|upper-alpha|underline|transparent|top|thin|thick|text|text-top|text-bottom|tb-rl|table-header-group|table-footer-group|sw-resize|super|strict|static|square|solid|small-caps|separate|se-resize|scroll|s-resize|rtl|row-resize|ridge|right|repeat|repeat-y|repeat-x|relative|progress|pointer|overline|outside|outset|oblique|nowrap|not-allowed|normal|none|nw-resize|no-repeat|no-drop|newspaper|ne-resize|n-resize|move|middle|medium|ltr|lr-tb|lowercase|lower-roman|lower-alpha|loose|list-item|line|line-through|line-edge|lighter|left|keep-all|justify|italic|inter-word|inter-ideograph|inside|inset|inline|inline-block|inherit|inactive|ideograph-space|ideograph-parenthesis|ideograph-numeric|ideograph-alpha|horizontal|hidden|help|hand|groove|fixed|ellipsis|e-resize|double|dotted|distribute|distribute-space|distribute-letter|distribute-all-lines|disc|disabled|default|decimal|dashed|crosshair|collapse|col-resize|circle|char|center|capitalize|break-word|break-all|bottom|both|bolder|bold|block|bidi-override|below|baseline|auto|always|all-scroll|absolute|table|table-cell)\\b" +},{begin:":",end:";", +contains:[c,n.HEXCOLOR,n.CSS_NUMBER_MODE,a.QUOTE_STRING_MODE,a.APOS_STRING_MODE,n.IMPORTANT] +},{begin:"@(page|font-face)",keywords:{$pattern:d,keyword:"@page @font-face"}},{ +begin:"@",end:"[{;]",returnBegin:!0,keywords:{$pattern:/[a-z-]+/, +keyword:"and or not only",attribute:t.join(" ")},contains:[{begin:d, +className:"keyword"},{begin:/[a-z-]+(?=:)/,className:"attribute" +},c,a.QUOTE_STRING_MODE,a.APOS_STRING_MODE,n.HEXCOLOR,n.CSS_NUMBER_MODE]}]}} +})());hljs.registerLanguage("diff",(()=>{"use strict";function e(...e){ +return"("+((e=>{const n=e[e.length-1] +;return"object"==typeof n&&n.constructor===Object?(e.splice(e.length-1,1),n):{} +})(e).capture?"":"?:")+e.map((e=>{return(n=e)?"string"==typeof n?n:n.source:null +;var n})).join("|")+")"}return n=>({name:"Diff",aliases:["patch"],contains:[{ +className:"meta",relevance:10, +match:e(/^@@ +-\d+,\d+ +\+\d+,\d+ +@@/,/^\*\*\* +\d+,\d+ +\*\*\*\*$/,/^--- +\d+,\d+ +----$/) +},{className:"comment",variants:[{ +begin:e(/Index: /,/^index/,/={3,}/,/^-{3}/,/^\*{3} /,/^\+{3}/,/^diff --git/), +end:/$/},{match:/^\*{15}$/}]},{className:"addition",begin:/^\+/,end:/$/},{ +className:"deletion",begin:/^-/,end:/$/},{className:"addition",begin:/^!/, +end:/$/}]})})());hljs.registerLanguage("plaintext",(()=>{"use strict";return t=>({ +name:"Plain text",aliases:["text","txt"],disableAutodetect:!0})})());hljs.registerLanguage("ruby",(()=>{"use strict";function e(e){ +return n("(?=",e,")")}function n(...e){return e.map((e=>{ +return(n=e)?"string"==typeof n?n:n.source:null;var n})).join("")}return a=>{ +const i="([a-zA-Z_]\\w*[!?=]?|[-+~]@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?)",s={ +keyword:"and then defined module in return redo if BEGIN retry end for self when next until do begin unless END rescue else break undef not super class case require yield alias while ensure elsif or include attr_reader attr_writer attr_accessor __FILE__", +built_in:"proc lambda",literal:"true false nil"},r={className:"doctag", +begin:"@[A-Za-z]+"},b={begin:"#<",end:">"},c=[a.COMMENT("#","$",{contains:[r] +}),a.COMMENT("^=begin","^=end",{contains:[r],relevance:10 +}),a.COMMENT("^__END__","\\n$")],t={className:"subst",begin:/#\{/,end:/\}/, +keywords:s},g={className:"string",contains:[a.BACKSLASH_ESCAPE,t],variants:[{ +begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/`/,end:/`/},{begin:/%[qQwWx]?\(/, +end:/\)/},{begin:/%[qQwWx]?\[/,end:/\]/},{begin:/%[qQwWx]?\{/,end:/\}/},{ +begin:/%[qQwWx]?/},{begin:/%[qQwWx]?\//,end:/\//},{begin:/%[qQwWx]?%/, +end:/%/},{begin:/%[qQwWx]?-/,end:/-/},{begin:/%[qQwWx]?\|/,end:/\|/},{ +begin:/\B\?(\\\d{1,3})/},{begin:/\B\?(\\x[A-Fa-f0-9]{1,2})/},{ +begin:/\B\?(\\u\{?[A-Fa-f0-9]{1,6}\}?)/},{ +begin:/\B\?(\\M-\\C-|\\M-\\c|\\c\\M-|\\M-|\\C-\\M-)[\x20-\x7e]/},{ +begin:/\B\?\\(c|C-)[\x20-\x7e]/},{begin:/\B\?\\?\S/},{ +begin:n(/<<[-~]?'?/,e(/(\w+)(?=\W)[^\n]*\n(?:[^\n]*\n)*?\s*\1\b/)), +contains:[a.END_SAME_AS_BEGIN({begin:/(\w+)/,end:/(\w+)/, +contains:[a.BACKSLASH_ESCAPE,t]})]}]},d="[0-9](_?[0-9])*",l={className:"number", +relevance:0,variants:[{ +begin:`\\b([1-9](_?[0-9])*|0)(\\.(${d}))?([eE][+-]?(${d})|r)?i?\\b`},{ +begin:"\\b0[dD][0-9](_?[0-9])*r?i?\\b"},{begin:"\\b0[bB][0-1](_?[0-1])*r?i?\\b" +},{begin:"\\b0[oO][0-7](_?[0-7])*r?i?\\b"},{ +begin:"\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*r?i?\\b"},{ +begin:"\\b0(_?[0-7])+r?i?\\b"}]},o={className:"params",begin:"\\(",end:"\\)", +endsParent:!0,keywords:s},_=[g,{className:"class",beginKeywords:"class module", +end:"$|;",illegal:/=/,contains:[a.inherit(a.TITLE_MODE,{ +begin:"[A-Za-z_]\\w*(::\\w+)*(\\?|!)?"}),{begin:"<\\s*",contains:[{ +begin:"("+a.IDENT_RE+"::)?"+a.IDENT_RE,relevance:0}]}].concat(c)},{ +className:"function",begin:n(/def\s+/,e(i+"\\s*(\\(|;|$)")),relevance:0, +keywords:"def",end:"$|;",contains:[a.inherit(a.TITLE_MODE,{begin:i +}),o].concat(c)},{begin:a.IDENT_RE+"::"},{className:"symbol", +begin:a.UNDERSCORE_IDENT_RE+"(!|\\?)?:",relevance:0},{className:"symbol", +begin:":(?!\\s)",contains:[g,{begin:i}],relevance:0},l,{className:"variable", +begin:"(\\$\\W)|((\\$|@@?)(\\w+))(?=[^@$?])(?![A-Za-z])(?![@$?'])"},{ +className:"params",begin:/\|/,end:/\|/,relevance:0,keywords:s},{ +begin:"("+a.RE_STARTERS_RE+"|unless)\\s*",keywords:"unless",contains:[{ +className:"regexp",contains:[a.BACKSLASH_ESCAPE,t],illegal:/\n/,variants:[{ +begin:"/",end:"/[a-z]*"},{begin:/%r\{/,end:/\}[a-z]*/},{begin:"%r\\(", +end:"\\)[a-z]*"},{begin:"%r!",end:"![a-z]*"},{begin:"%r\\[",end:"\\][a-z]*"}] +}].concat(b,c),relevance:0}].concat(b,c);t.contains=_,o.contains=_;const E=[{ +begin:/^\s*=>/,starts:{end:"$",contains:_}},{className:"meta", +begin:"^([>?]>|[\\w#]+\\(\\w+\\):\\d+:\\d+>|(\\w+-)?\\d+\\.\\d+\\.\\d+(p\\d+)?[^\\d][^>]+>)(?=[ ])", +starts:{end:"$",contains:_}}];return c.unshift(b),{name:"Ruby", +aliases:["rb","gemspec","podspec","thor","irb"],keywords:s,illegal:/\/\*/, +contains:[a.SHEBANG({binary:"ruby"})].concat(E).concat(c).concat(_)}}})());hljs.registerLanguage("yaml",(()=>{"use strict";return e=>{ +const n="true false yes no null",a="[\\w#;/?:@&=+$,.~*'()[\\]]+",s={ +className:"string",relevance:0,variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/ +},{begin:/\S+/}],contains:[e.BACKSLASH_ESCAPE,{className:"template-variable", +variants:[{begin:/\{\{/,end:/\}\}/},{begin:/%\{/,end:/\}/}]}]},i=e.inherit(s,{ +variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/[^\s,{}[\]]+/}]}),l={ +end:",",endsWithParent:!0,excludeEnd:!0,keywords:n,relevance:0},t={begin:/\{/, +end:/\}/,contains:[l],illegal:"\\n",relevance:0},g={begin:"\\[",end:"\\]", +contains:[l],illegal:"\\n",relevance:0},b=[{className:"attr",variants:[{ +begin:"\\w[\\w :\\/.-]*:(?=[ \t]|$)"},{begin:'"\\w[\\w :\\/.-]*":(?=[ \t]|$)'},{ +begin:"'\\w[\\w :\\/.-]*':(?=[ \t]|$)"}]},{className:"meta",begin:"^---\\s*$", +relevance:10},{className:"string", +begin:"[\\|>]([1-9]?[+-])?[ ]*\\n( +)[^ ][^\\n]*\\n(\\2[^\\n]+\\n?)*"},{ +begin:"<%[%=-]?",end:"[%-]?%>",subLanguage:"ruby",excludeBegin:!0,excludeEnd:!0, +relevance:0},{className:"type",begin:"!\\w+!"+a},{className:"type", +begin:"!<"+a+">"},{className:"type",begin:"!"+a},{className:"type",begin:"!!"+a +},{className:"meta",begin:"&"+e.UNDERSCORE_IDENT_RE+"$"},{className:"meta", +begin:"\\*"+e.UNDERSCORE_IDENT_RE+"$"},{className:"bullet",begin:"-(?=[ ]|$)", +relevance:0},e.HASH_COMMENT_MODE,{beginKeywords:n,keywords:{literal:n}},{ +className:"number", +begin:"\\b[0-9]{4}(-[0-9][0-9]){0,2}([Tt \\t][0-9][0-9]?(:[0-9][0-9]){2})?(\\.[0-9]*)?([ \\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?\\b" +},{className:"number",begin:e.C_NUMBER_RE+"\\b",relevance:0},t,g,s],c=[...b] +;return c.pop(),c.push(i),l.contains=c,{name:"YAML",case_insensitive:!0, +aliases:["yml"],contains:b}}})());hljs.registerLanguage("rust",(()=>{"use strict";function e(...e){ +return e.map((e=>{return(t=e)?"string"==typeof t?t:t.source:null;var t +})).join("")}return t=>{const n={className:"title.function.invoke",relevance:0, +begin:e(/\b/,/(?!let\b)/,t.IDENT_RE,(a=/\s*\(/,e("(?=",a,")")))};var a +;const r="([ui](8|16|32|64|128|size)|f(32|64))?",i=["drop ","Copy","Send","Sized","Sync","Drop","Fn","FnMut","FnOnce","ToOwned","Clone","Debug","PartialEq","PartialOrd","Eq","Ord","AsRef","AsMut","Into","From","Default","Iterator","Extend","IntoIterator","DoubleEndedIterator","ExactSizeIterator","SliceConcatExt","ToString","assert!","assert_eq!","bitflags!","bytes!","cfg!","col!","concat!","concat_idents!","debug_assert!","debug_assert_eq!","env!","panic!","file!","format!","format_args!","include_bin!","include_str!","line!","local_data_key!","module_path!","option_env!","print!","println!","select!","stringify!","try!","unimplemented!","unreachable!","vec!","write!","writeln!","macro_rules!","assert_ne!","debug_assert_ne!"] +;return{name:"Rust",aliases:["rs"],keywords:{$pattern:t.IDENT_RE+"!?", +type:["i8","i16","i32","i64","i128","isize","u8","u16","u32","u64","u128","usize","f32","f64","str","char","bool","Box","Option","Result","String","Vec"], +keyword:["abstract","as","async","await","become","box","break","const","continue","crate","do","dyn","else","enum","extern","false","final","fn","for","if","impl","in","let","loop","macro","match","mod","move","mut","override","priv","pub","ref","return","self","Self","static","struct","super","trait","true","try","type","typeof","unsafe","unsized","use","virtual","where","while","yield"], +literal:["true","false","Some","None","Ok","Err"],built_in:i},illegal:""},n]}}})());hljs.registerLanguage("javascript",(()=>{"use strict" +;const e="[A-Za-z$_][0-9A-Za-z$_]*",n=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],a=["true","false","null","undefined","NaN","Infinity"],t=["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer","BigInt64Array","BigUint64Array","BigInt"],s=["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"],r=["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],i=["arguments","this","super","console","window","document","localStorage","module","global"],c=[].concat(r,t,s) +;function o(e){return l("(?=",e,")")}function l(...e){return e.map((e=>{ +return(n=e)?"string"==typeof n?n:n.source:null;var n})).join("")}return b=>{ +const g=e,d={begin:/<[A-Za-z0-9\\._:-]+/,end:/\/[A-Za-z0-9\\._:-]+>|\/>/, +isTrulyOpeningTag:(e,n)=>{const a=e[0].length+e.index,t=e.input[a] +;"<"!==t?">"===t&&(((e,{after:n})=>{const a="",B={ +match:[/const|var|let/,/\s+/,g,/\s*/,/=\s*/,o(C)],className:{1:"keyword", +3:"title.function"},contains:[w]};return{name:"Javascript", +aliases:["js","jsx","mjs","cjs"],keywords:u,exports:{PARAMS_CONTAINS:S}, +illegal:/#(?![$_A-z])/,contains:[b.SHEBANG({label:"shebang",binary:"node", +relevance:5}),{label:"use_strict",className:"meta",relevance:10, +begin:/^\s*['"]use (strict|asm)['"]/ +},b.APOS_STRING_MODE,b.QUOTE_STRING_MODE,N,f,A,v,y,O,{className:"attr", +begin:g+o(":"),relevance:0},B,{ +begin:"("+b.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*", +keywords:"return throw case",relevance:0,contains:[v,b.REGEXP_MODE,{ +className:"function",begin:C,returnBegin:!0,end:"\\s*=>",contains:[{ +className:"params",variants:[{begin:b.UNDERSCORE_IDENT_RE,relevance:0},{ +className:null,begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0, +excludeEnd:!0,keywords:u,contains:S}]}]},{begin:/,/,relevance:0},{match:/\s+/, +relevance:0},{variants:[{begin:"<>",end:""},{begin:d.begin, +"on:begin":d.isTrulyOpeningTag,end:d.end}],subLanguage:"xml",contains:[{ +begin:d.begin,end:d.end,skip:!0,contains:["self"]}]}]},I,{ +beginKeywords:"while if switch catch for"},{ +begin:"\\b(?!function)"+b.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{", +returnBegin:!0,label:"func.def",contains:[w,b.inherit(b.TITLE_MODE,{begin:g, +className:"title.function"})]},{match:/\.\.\./,relevance:0},M,{match:"\\$"+g, +relevance:0},{match:[/\bconstructor(?=\s*\()/],className:{1:"title.function"}, +contains:[w]},T,{relevance:0,match:/\b[A-Z][A-Z_]+\b/, +className:"variable.constant"},R,k,{match:/\$[(.]/}]}}})());hljs.registerLanguage("json",(()=>{"use strict";return e=>({name:"JSON", +contains:[{className:"attr",begin:/"(\\.|[^\\"\r\n])*"(?=\s*:)/,relevance:1.01 +},{match:/[{}[\],:]/,className:"punctuation",relevance:0},e.QUOTE_STRING_MODE,{ +beginKeywords:"true false null" +},e.C_NUMBER_MODE,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE],illegal:"\\S"}) +})()); \ No newline at end of file From 4942925859c34fcb426bad97d3e283d6fb9d4b9a Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Wed, 18 Aug 2021 15:24:00 +0200 Subject: [PATCH 133/137] Cleanup and update dependencies --- Cargo.lock | 429 ++++++++++-------------------- crates/eww/Cargo.toml | 8 +- crates/eww/src/server.rs | 4 +- crates/eww_shared_util/Cargo.toml | 4 - crates/simplexpr/Cargo.toml | 2 - crates/yuck/Cargo.toml | 2 +- 6 files changed, 145 insertions(+), 304 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 01c554d..41c8076 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,14 @@ version = 3 [[package]] name = "ahash" -version = "0.3.8" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217" +checksum = "43bb833f0bf979d8475d38fbf09ed3b8a55e1885fe93ad3f93239fc6a4f17b98" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] [[package]] name = "aho-corasick" @@ -37,9 +42,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.42" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "595d3cfa7a60d4555cb5067b99f07142a08ea778de5cf993f7b75c7d8fabc486" +checksum = "28ae2b3dec75a406790005a200b1bd89785afc02517a00ca99ecfe093ee9e6cf" [[package]] name = "ascii-canvas" @@ -50,27 +55,6 @@ dependencies = [ "term", ] -[[package]] -name = "async-stream" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "171374e7e3b2504e0e5236e3b59260560f9fe94bfe9ac39ba5e4e929c5590625" -dependencies = [ - "async-stream-impl", - "futures-core", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "648ed8c8d2ce5409ccd57453d9d1b214b342a0d69376a6feda1fd6cae3299308" -dependencies = [ - "proc-macro2", - "quote 1.0.9", - "syn 1.0.73", -] - [[package]] name = "atk" version = "0.9.0" @@ -122,15 +106,9 @@ checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" [[package]] name = "beef" -version = "0.4.4" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "474a626a67200bd107d44179bb3d4fc61891172d11696609264589be6a0e6a43" - -[[package]] -name = "beef" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6736e2428df2ca2848d846c43e88745121a6654696e349ce0054a420815a7409" +checksum = "bed554bd50246729a1ec158d08aa3235d1b69d94ad120ebe187e28894787e736" [[package]] name = "bincode" @@ -274,9 +252,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9" +checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" dependencies = [ "cfg-if", "crossbeam-epoch", @@ -319,7 +297,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e98e2ad1a782e33928b96fc3948e7c355e5af34ba4de7670fe8bac2a3b2006d" dependencies = [ "quote 1.0.9", - "syn 1.0.73", + "syn 1.0.74", ] [[package]] @@ -342,7 +320,7 @@ dependencies = [ "proc-macro2", "quote 1.0.9", "rustc_version", - "syn 1.0.73", + "syn 1.0.74", ] [[package]] @@ -426,10 +404,9 @@ dependencies = [ [[package]] name = "eww" -version = "0.1.0" +version = "0.2.0" dependencies = [ "anyhow", - "async-stream", "base64", "bincode", "cairo-sys-rs", @@ -456,9 +433,7 @@ dependencies = [ "maplit", "nix", "notify", - "num", "once_cell", - "pretty_assertions", "pretty_env_logger", "regex", "serde", @@ -469,7 +444,6 @@ dependencies = [ "structopt", "sysinfo", "tokio", - "tokio-stream", "tokio-util", "unescape", "unindent", @@ -483,7 +457,6 @@ name = "eww_shared_util" version = "0.1.0" dependencies = [ "derive_more", - "insta", "serde", ] @@ -496,14 +469,14 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote 1.0.9", - "syn 1.0.73", + "syn 1.0.74", ] [[package]] name = "filetime" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d34cfa13a63ae058bfa601fe9e313bbdb3746427c1459185464ce0fcf62e1e8" +checksum = "975ccf83d8d9d0d84682850a38c8169027be83368805971cc4f238c2b245bc98" dependencies = [ "cfg-if", "libc", @@ -517,12 +490,6 @@ 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 = "fsevent-sys" version = "4.0.0" @@ -534,9 +501,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.15" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7e43a803dae2fa37c1f6a8fe121e1f7bf9548b4dfc0522a42f34145dadfc27" +checksum = "1adc00f486adfc9ce99f77d717836f0c5aa84965eb0b4f051f4e83f7cab53f8b" dependencies = [ "futures-channel", "futures-core", @@ -549,9 +516,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.15" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e682a68b29a882df0545c143dc3646daefe80ba479bcdede94d5a703de2871e2" +checksum = "74ed2411805f6e4e3d9bc904c95d5d423b89b3b25dc0250aa74729de20629ff9" dependencies = [ "futures-core", "futures-sink", @@ -559,15 +526,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.15" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0402f765d8a89a26043b889b26ce3c4679d268fa6bb22cd7c6aad98340e179d1" +checksum = "af51b1b4a7fdff033703db39de8802c673eb91855f2e0d47dcf3bf2c0ef01f99" [[package]] name = "futures-executor" -version = "0.3.15" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "badaa6a909fac9e7236d0620a2f57f7664640c56575b71a7552fbd68deafab79" +checksum = "4d0d535a57b87e1ae31437b892713aee90cd2d7b0ee48727cd11fc72ef54761c" dependencies = [ "futures-core", "futures-task", @@ -576,40 +543,40 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.15" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acc499defb3b348f8d8f3f66415835a9131856ff7714bf10dadfc4ec4bdb29a1" +checksum = "0b0e06c393068f3a6ef246c75cdca793d6a46347e75286933e5e75fd2fd11582" [[package]] name = "futures-macro" -version = "0.3.15" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c40298486cdf52cc00cd6d6987892ba502c7656a16a4192a9992b1ccedd121" +checksum = "c54913bae956fb8df7f4dc6fc90362aa72e69148e3f39041fbe8742d21e0ac57" dependencies = [ "autocfg", "proc-macro-hack", "proc-macro2", "quote 1.0.9", - "syn 1.0.73", + "syn 1.0.74", ] [[package]] name = "futures-sink" -version = "0.3.15" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a57bead0ceff0d6dde8f465ecd96c9338121bb7717d3e7b108059531870c4282" +checksum = "c0f30aaa67363d119812743aa5f33c201a7a66329f97d1a887022971feea4b53" [[package]] name = "futures-task" -version = "0.3.15" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a16bef9fc1a4dddb5bee51c989e3fbba26569cbb0e31f5b303c184e3dd33dae" +checksum = "bbe54a98670017f3be909561f6ad13e810d9a51f3f061b902062ca3da80799f2" [[package]] name = "futures-util" -version = "0.3.15" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feb5c238d27e2bf94ffdfd27b2c29e3df4a68c4193bb6427384259e2bf191967" +checksum = "67eb846bfd58e44a8481a00049e82c43e0ccb5d61f8dc071057cb19249dd4d78" dependencies = [ "autocfg", "futures-channel", @@ -740,17 +707,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - [[package]] name = "getrandom" version = "0.2.3" @@ -759,7 +715,7 @@ checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" dependencies = [ "cfg-if", "libc", - "wasi 0.10.2+wasi-snapshot-preview1", + "wasi", ] [[package]] @@ -828,7 +784,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote 1.0.9", - "syn 1.0.73", + "syn 1.0.74", ] [[package]] @@ -854,20 +810,19 @@ dependencies = [ [[package]] name = "grass" -version = "0.10.5" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "352df9cd46a5538323ba016fdbff8baee4a55011a7349120b0d7280992276fa7" +checksum = "82317908bc4204532d098390f8e041693aaeab95cf7351f774bdacf253b1c8ed" dependencies = [ - "beef 0.4.4", + "beef", "clap", "codemap", "indexmap", "lasso", - "num-bigint 0.3.2", - "num-rational 0.3.2", + "num-bigint", + "num-rational", "num-traits", "once_cell", - "peekmore", "phf", "rand", ] @@ -946,21 +901,14 @@ dependencies = [ "system-deps", ] -[[package]] -name = "hashbrown" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b62f79061a0bc2e046024cb7ba44b08419ed238ecbd9adbd787434b9e8c25" -dependencies = [ - "ahash", - "autocfg", -] - [[package]] name = "hashbrown" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash", +] [[package]] name = "heck" @@ -996,7 +944,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" dependencies = [ "autocfg", - "hashbrown 0.11.2", + "hashbrown", ] [[package]] @@ -1021,9 +969,9 @@ dependencies = [ [[package]] name = "insta" -version = "1.7.1" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1b21a2971cea49ca4613c0e9fe8225ecaf5de64090fddc6002284726e9244" +checksum = "58019516c1403ac45b106c9fc4e8fcbd77a78e98b014c619d1506338902ccfa4" dependencies = [ "console", "lazy_static", @@ -1068,6 +1016,26 @@ version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" +[[package]] +name = "kqueue" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "058a107a784f8be94c7d35c1300f4facced2e93d2fbe5b1452b44e905ddca4a9" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8367585489f01bc55dd27404dcf56b95e6da061a256a666ab23be9ba96a2e587" +dependencies = [ + "bitflags", + "libc", +] + [[package]] name = "lalrpop" version = "0.19.6" @@ -1102,11 +1070,11 @@ dependencies = [ [[package]] name = "lasso" -version = "0.3.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf1a626ea51398f5acf36666c8046ff4bfd048aab88e92db676d2a6eac8805d0" +checksum = "e8647c8a01e5f7878eacb2c323c4c949fdb63773110f0686c7810769874b7e0a" dependencies = [ - "hashbrown 0.8.2", + "hashbrown", ] [[package]] @@ -1123,9 +1091,9 @@ checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760" [[package]] name = "libc" -version = "0.2.98" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790" +checksum = "a7f823d141fe0a24df1e23b4af4e3c7ba9e5966ec514ea068c93024aa7deb765" [[package]] name = "linked-hash-map" @@ -1151,30 +1119,6 @@ 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 0.5.0", - "fnv", - "proc-macro2", - "quote 1.0.9", - "regex-syntax", - "syn 1.0.73", - "utf8-ranges", -] - [[package]] name = "maplit" version = "1.0.2" @@ -1226,27 +1170,29 @@ checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" [[package]] name = "nix" -version = "0.20.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa9b4819da1bc61c0ea48b63b7bc8604064dd43013e7cc325df098d49cd7c18a" +checksum = "df8e5e343312e7fbeb2a52139114e9e702991ef9c2aea6817ff2440b35647d56" dependencies = [ "bitflags", "cc", "cfg-if", "libc", + "memoffset", ] [[package]] name = "notify" -version = "5.0.0-pre.10" +version = "5.0.0-pre.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51f18203a26893ca1d3526cf58084025d5639f91c44f8b70ab3b724f60e819a0" +checksum = "20a629259bb2c87a884bb76f6086c8637919de6d074754341c12e5dd3aed6326" dependencies = [ "bitflags", "crossbeam-channel", "filetime", "fsevent-sys", "inotify", + "kqueue", "libc", "mio", "walkdir", @@ -1262,31 +1208,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "num" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606" -dependencies = [ - "num-bigint 0.4.0", - "num-complex", - "num-integer", - "num-iter", - "num-rational 0.4.0", - "num-traits", -] - -[[package]] -name = "num-bigint" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d0a3d5e207573f948a9e5376662aa743a2ea13f7c50a554d7af443a73fbfeba" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - [[package]] name = "num-bigint" version = "0.4.0" @@ -1298,15 +1219,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-complex" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26873667bbbb7c5182d4a37c1add32cdf09f841af72da53318fdb81543c15085" -dependencies = [ - "num-traits", -] - [[package]] name = "num-integer" version = "0.1.44" @@ -1317,29 +1229,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-iter" -version = "0.1.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" -dependencies = [ - "autocfg", - "num-bigint 0.3.2", - "num-integer", - "num-traits", -] - [[package]] name = "num-rational" version = "0.4.0" @@ -1347,7 +1236,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a" dependencies = [ "autocfg", - "num-bigint 0.4.0", + "num-bigint", "num-integer", "num-traits", ] @@ -1438,15 +1327,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "peekmore" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4089f831c514cbf080bd19bfce702f7a2de250492730d419204af6710ba19316" -dependencies = [ - "smallvec", -] - [[package]] name = "pest" version = "2.1.3" @@ -1468,37 +1348,37 @@ dependencies = [ [[package]] name = "phf" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" +checksum = "b2ac8b67553a7ca9457ce0e526948cad581819238f4a9d1ea74545851fa24f37" dependencies = [ "phf_macros", - "phf_shared", + "phf_shared 0.9.0", "proc-macro-hack", ] [[package]] name = "phf_generator" -version = "0.8.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" +checksum = "d43f3220d96e0080cc9ea234978ccd80d904eafb17be31bb0f76daaea6493082" dependencies = [ - "phf_shared", + "phf_shared 0.9.0", "rand", ] [[package]] name = "phf_macros" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" +checksum = "b706f5936eb50ed880ae3009395b43ed19db5bff2ebd459c95e7bf013a89ab86" dependencies = [ "phf_generator", - "phf_shared", + "phf_shared 0.9.0", "proc-macro-hack", "proc-macro2", "quote 1.0.9", - "syn 1.0.73", + "syn 1.0.74", ] [[package]] @@ -1510,6 +1390,15 @@ dependencies = [ "siphasher", ] +[[package]] +name = "phf_shared" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a68318426de33640f02be62b4ae8eb1261be2efbc337b60c54d845bf4484e0d9" +dependencies = [ + "siphasher", +] + [[package]] name = "pico-args" version = "0.4.2" @@ -1586,7 +1475,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote 1.0.9", - "syn 1.0.73", + "syn 1.0.74", "version_check", ] @@ -1615,9 +1504,9 @@ checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" [[package]] name = "proc-macro2" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038" +checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612" dependencies = [ "unicode-xid 0.2.2", ] @@ -1645,23 +1534,21 @@ dependencies = [ [[package]] name = "rand" -version = "0.7.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" dependencies = [ - "getrandom 0.1.16", "libc", "rand_chacha", "rand_core", "rand_hc", - "rand_pcg", ] [[package]] name = "rand_chacha" -version = "0.2.2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", "rand_core", @@ -1669,27 +1556,18 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.5.1" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" dependencies = [ - "getrandom 0.1.16", + "getrandom", ] [[package]] name = "rand_hc" -version = "0.2.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core", -] - -[[package]] -name = "rand_pcg" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" dependencies = [ "rand_core", ] @@ -1721,9 +1599,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ab49abadf3f9e1c4bc499e8845e152ad87d2ad2d30371841171169e9d75feee" +checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" dependencies = [ "bitflags", ] @@ -1734,7 +1612,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" dependencies = [ - "getrandom 0.2.3", + "getrandom", "redox_syscall", ] @@ -1822,29 +1700,29 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.126" +version = "1.0.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03" +checksum = "f03b9878abf6d14e6779d3f24f07b2cfa90352cfec4acc5aab8f1ac7f146fae8" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.126" +version = "1.0.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43" +checksum = "a024926d3432516606328597e0f224a51355a493b49fdd67e9209187cbe55ecc" dependencies = [ "proc-macro2", "quote 1.0.9", - "syn 1.0.73", + "syn 1.0.74", ] [[package]] name = "serde_json" -version = "1.0.64" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" +checksum = "336b10da19a12ad094b59d870ebde26a45402e5b470add4b5fd03c5048a32127" dependencies = [ "itoa", "ryu", @@ -1853,9 +1731,9 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.8.17" +version = "0.8.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15654ed4ab61726bf918a39cb8d98a2e2995b002387807fa6ba58fdf7f59bb23" +checksum = "039ba818c784248423789eec090aab9fb566c7b94d6ebbfa1814a9fd52c8afb2" dependencies = [ "dtoa", "linked-hash-map", @@ -1898,8 +1776,6 @@ dependencies = [ "lalrpop", "lalrpop-util", "levenshtein", - "logos", - "maplit", "once_cell", "regex", "serde", @@ -1910,15 +1786,15 @@ dependencies = [ [[package]] name = "siphasher" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbce6d4507c7e4a3962091436e56e95290cb71fa302d0d270e32130b75fbff27" +checksum = "729a25c17d72b06c68cb47955d44fda88ad2d3e7d77e025663fdd69b93dd71a1" [[package]] name = "slab" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527" +checksum = "c307a32c1c5c437f38c7fd45d753050587732ba8628319fbdf12a7e289ccc590" [[package]] name = "smallvec" @@ -1934,7 +1810,7 @@ checksum = "133659a15339456eeeb07572eb02a91c91e9815e9cbc89566944d2c8d3efdbf6" dependencies = [ "proc-macro2", "quote 1.0.9", - "syn 1.0.73", + "syn 1.0.74", ] [[package]] @@ -1951,7 +1827,7 @@ checksum = "8ddb1139b5353f96e429e1a5e19fbaf663bddedaa06d1dbd49f82e352601209a" dependencies = [ "lazy_static", "new_debug_unreachable", - "phf_shared", + "phf_shared 0.8.0", "precomputed-hash", ] @@ -1982,7 +1858,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote 1.0.9", - "syn 1.0.73", + "syn 1.0.74", ] [[package]] @@ -2009,7 +1885,7 @@ dependencies = [ "heck", "proc-macro2", "quote 1.0.9", - "syn 1.0.73", + "syn 1.0.74", ] [[package]] @@ -2021,7 +1897,7 @@ dependencies = [ "heck", "proc-macro2", "quote 1.0.9", - "syn 1.0.73", + "syn 1.0.74", ] [[package]] @@ -2037,9 +1913,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.73" +version = "1.0.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7" +checksum = "1873d832550d4588c3dbc20f01361ab00bfe741048f71e3fecf145a7cc18b29c" dependencies = [ "proc-macro2", "quote 1.0.9", @@ -2142,7 +2018,7 @@ checksum = "060d69a0afe7796bf42e9e2ff91f5ee691fb15c53d38b4b62a9a53eb23164745" dependencies = [ "proc-macro2", "quote 1.0.9", - "syn 1.0.73", + "syn 1.0.74", ] [[package]] @@ -2156,9 +2032,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.8.2" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2602b8af3767c285202012822834005f596c811042315fa7e9f5b12b2a43207" +checksum = "01cf844b23c6131f624accf65ce0e4e9956a8bb329400ea5bcc26ae3a5c20b0b" dependencies = [ "autocfg", "bytes", @@ -2182,18 +2058,7 @@ checksum = "54473be61f4ebe4efd09cec9bd5d16fa51d70ea0192213d754d2d500457db110" dependencies = [ "proc-macro2", "quote 1.0.9", - "syn 1.0.73", -] - -[[package]] -name = "tokio-stream" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b2f3f698253f03119ac0102beaa64f67a67e08074d03a22d18784104543727f" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", + "syn 1.0.74", ] [[package]] @@ -2261,12 +2126,6 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f14ee04d9415b52b3aeab06258a3f07093182b88ba0f9b8d203f211a7a7d41c7" -[[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" @@ -2311,12 +2170,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - [[package]] name = "wasi" version = "0.10.2+wasi-snapshot-preview1" diff --git a/crates/eww/Cargo.toml b/crates/eww/Cargo.toml index e26d681..e1329a8 100644 --- a/crates/eww/Cargo.toml +++ b/crates/eww/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "eww" -version = "0.1.0" +version = "0.2.0" authors = ["elkowar <5300871+elkowar@users.noreply.github.com>"] edition = "2018" description = "Widget system for everyone!" @@ -39,7 +39,6 @@ serde = {version = "1.0", features = ["derive"]} serde_json = "1.0" extend = "1" grass = "0.10" -num = "0.4" itertools = "0.10" debug_stub_derive = "0.3" log = "0.4" @@ -53,8 +52,6 @@ unescape = "0.1" unindent = "0.1" tokio = { version = "1.0", features = ["full"] } -tokio-stream = "0.1" -async-stream = "0.3" futures-core = "0.3" futures-util = "0.3" tokio-util = "0.6" @@ -72,6 +69,3 @@ codespan-reporting = "0.11" simplexpr = { path = "../simplexpr" } eww_shared_util = { path = "../eww_shared_util" } yuck = { path = "../yuck", default-features = false} - -[dev-dependencies] -pretty_assertions = "0.7.1" diff --git a/crates/eww/src/server.rs b/crates/eww/src/server.rs index 9be673b..81e3712 100644 --- a/crates/eww/src/server.rs +++ b/crates/eww/src/server.rs @@ -137,7 +137,7 @@ async fn run_filewatch>(config_dir: P, evt_send: UnboundedSender< use notify::{RecommendedWatcher, RecursiveMode, Watcher}; let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel(); - let mut watcher: RecommendedWatcher = Watcher::new_immediate(move |res: notify::Result| match res { + let mut watcher: RecommendedWatcher = Watcher::new(move |res: notify::Result| match res { Ok(event) => { let relevant_files_changed = event.paths.iter().any(|path| { let ext = path.extension().unwrap_or_default(); @@ -151,7 +151,7 @@ async fn run_filewatch>(config_dir: P, evt_send: UnboundedSender< } Err(e) => log::error!("Encountered Error While Watching Files: {}", e), })?; - watcher.watch(&config_dir, RecursiveMode::Recursive)?; + watcher.watch(&config_dir.as_ref(), RecursiveMode::Recursive)?; // make sure to not trigger reloads too much by only accepting one reload every 500ms. let debounce_done = Arc::new(std::sync::atomic::AtomicBool::new(true)); diff --git a/crates/eww_shared_util/Cargo.toml b/crates/eww_shared_util/Cargo.toml index fdcb937..3293833 100644 --- a/crates/eww_shared_util/Cargo.toml +++ b/crates/eww_shared_util/Cargo.toml @@ -6,7 +6,3 @@ edition = "2018" [dependencies] serde = {version = "1.0", features = ["derive"]} derive_more = "0.99" - - -[dev-dependencies] -insta = "1.7" diff --git a/crates/simplexpr/Cargo.toml b/crates/simplexpr/Cargo.toml index bf1785f..37a8338 100644 --- a/crates/simplexpr/Cargo.toml +++ b/crates/simplexpr/Cargo.toml @@ -12,8 +12,6 @@ lalrpop-util = "0.19.5" regex = "1" itertools = "0.10" thiserror = "1.0" -maplit = "1.0" -logos = "0.12" once_cell = "1.8.0" serde = {version = "1.0", features = ["derive"]} diff --git a/crates/yuck/Cargo.toml b/crates/yuck/Cargo.toml index 4e65d56..f918df5 100644 --- a/crates/yuck/Cargo.toml +++ b/crates/yuck/Cargo.toml @@ -23,7 +23,6 @@ derive_more = "0.99" smart-default = "0.6" serde = {version = "1.0", features = ["derive"]} serde_json = "1.0" -pretty_assertions = "0.7" once_cell = "1.8" strum = { version = "0.21", features = ["derive"] } @@ -39,3 +38,4 @@ lalrpop = "0.19.5" [dev-dependencies] insta = { version = "1.7", features = ["ron"]} +pretty_assertions = "0.7" From b8877c5dc049fcb0b99cdb8d4d3aada256d9c37c Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Wed, 18 Aug 2021 16:01:50 +0200 Subject: [PATCH 134/137] Add migration to readme --- README.md | 16 ++++-- YUCK_MIGRATION.md | 29 +++++++++++ examples/eww-bar/eww.xml | 104 --------------------------------------- 3 files changed, 41 insertions(+), 108 deletions(-) create mode 100644 YUCK_MIGRATION.md delete mode 100644 examples/eww-bar/eww.xml diff --git a/README.md b/README.md index 235efe7..d3d700b 100644 --- a/README.md +++ b/README.md @@ -4,19 +4,27 @@ -Elkowar’s Wacky Widgets is a standalone widget system made in Rust that allows you to implement +Elkowars Wacky Widgets is a standalone widget system made in Rust that allows you to implement your own, custom widgets in any window manager. Documentation **and instructions on how to install** can be found [here](https://elkowar.github.io/eww). -## New configuration language needs a name - USER OPINIONS NEEDED +## New configuration language! -The rework of the configuration language has been decided on. Now, we just need a name! -Please give your opinions in the [discussion post](https://github.com/elkowar/eww/discussions/206). +YUCK IS ALIVE! After months of waiting, the new configuration language has now been released! +This also means that XML is no longer supported from this point onwards. +If you want to keep using the latest releases of eww, you'll need to migrate your config over to yuck. + +The steps to migrate can be found in [the migration guide](YUCK_MIGRATION.md) + +Additionally, a couple _amazing_ people have started to work on an +[automatic converter](https://github.com/undefinedDarkness/ewwxml) that can turn your old eww.xml into the new yuck format! ## Examples +(note that some of these still make use of the old configuration syntax) + * A basic bar, see [examples](./examples/eww-bar) ![Example 1](./examples/eww-bar/eww-bar.png) diff --git a/YUCK_MIGRATION.md b/YUCK_MIGRATION.md new file mode 100644 index 0000000..d46d90c --- /dev/null +++ b/YUCK_MIGRATION.md @@ -0,0 +1,29 @@ +# Migrating to yuck + +Yuck is the new configuration syntax used by eww. +While the syntax has changed dramatically, the general structure of the configuration +has stayed mostly the same. + +Most notably, the top-level blocks are now gone. +This means that `defvar`, `defwidget`, etc blocks no longer need to be in separate +sections of the file, but instead can be put wherever you need them. + +Explaining the exact syntax of yuck would be significantly less effective than just +looking at an example, as the general syntax is very simple. + +Thus, to get a feel for yuck, read through the [example configuration](./examples/eww-bar/eww.yuck). + + +Additionally, a couple smaller things have been changed. +The fields and structure of the `defwindow` block as been adjusted to better reflect +the options provided by the displayserver that is being used. +The major changes are: +- The `screen` field is now called `monitor` +- `reserve` and `geometry` are now structured slightly differently +To see how exactly the configuration now looks, check the [respective documentation](./docs/src/configuration.md#creating-your-first-window) + + +## Automatically converting your configuration + +A couple _amazing_ people have started to work on an [automatic converter](https://github.com/undefinedDarkness/ewwxml) that can turn your +old eww.xml into the new yuck format! diff --git a/examples/eww-bar/eww.xml b/examples/eww-bar/eww.xml deleted file mode 100644 index 4c6e0af..0000000 --- a/examples/eww-bar/eww.xml +++ /dev/null @@ -1,104 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  {{music}} - - - - - - - - - - - - - - - - - - - {{hour}}:{{min}} {{month}} {{number_day}}, {{year_full}} - - - - - - - - - playerctl metadata --format '{{ artist }} - {{ title }}' - - - - - - ~/.config/eww/scripts/getvol - - - date "+%d" - date "+%b" - date "+%M" - date "+%H" - date "+%Y" - - - ~/.config/eww/scripts/getram - - - cat /sys/class/power_supply/BAT0/capacity - - - - - - - - - - - - - - From f83ad913be9b3e955879249095d9fe809442eb50 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Wed, 18 Aug 2021 16:21:04 +0200 Subject: [PATCH 135/137] Clean up example bar --- README.md | 2 +- YUCK_MIGRATION.md | 2 +- crates/simplexpr/src/eval.rs | 2 +- examples/eww-bar/eww.scss | 8 +++---- examples/eww-bar/eww.yuck | 44 ++++++++++++++++++++++++------------ 5 files changed, 36 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index d3d700b..899a2e9 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ YUCK IS ALIVE! After months of waiting, the new configuration language has now b This also means that XML is no longer supported from this point onwards. If you want to keep using the latest releases of eww, you'll need to migrate your config over to yuck. -The steps to migrate can be found in [the migration guide](YUCK_MIGRATION.md) +The steps to migrate can be found in [the migration guide](YUCK_MIGRATION.md). Additionally, a couple _amazing_ people have started to work on an [automatic converter](https://github.com/undefinedDarkness/ewwxml) that can turn your old eww.xml into the new yuck format! diff --git a/YUCK_MIGRATION.md b/YUCK_MIGRATION.md index d46d90c..8ba66f0 100644 --- a/YUCK_MIGRATION.md +++ b/YUCK_MIGRATION.md @@ -19,7 +19,7 @@ The fields and structure of the `defwindow` block as been adjusted to better ref the options provided by the displayserver that is being used. The major changes are: - The `screen` field is now called `monitor` -- `reserve` and `geometry` are now structured slightly differently +- `reserve` and `geometry` are now structured slightly differently (see [here](./docs/src/configuration.md#creating-your-first-window)) To see how exactly the configuration now looks, check the [respective documentation](./docs/src/configuration.md#creating-your-first-window) diff --git a/crates/simplexpr/src/eval.rs b/crates/simplexpr/src/eval.rs index 13ba3f4..1b278d2 100644 --- a/crates/simplexpr/src/eval.rs +++ b/crates/simplexpr/src/eval.rs @@ -141,7 +141,7 @@ impl SimplExpr { SimplExpr::Concat(span, elems) => { let mut output = String::new(); for elem in elems { - let result = dbg!(elem.eval(values))?; + let result = elem.eval(values)?; output.push_str(&result.0); } Ok(DynVal(output, *span)) diff --git a/examples/eww-bar/eww.scss b/examples/eww-bar/eww.scss index 845e38f..efa2a50 100644 --- a/examples/eww-bar/eww.scss +++ b/examples/eww-bar/eww.scss @@ -16,13 +16,13 @@ color: #ffd5cd; } -.slider-vol scale trough highlight { +.metric scale trough highlight { all: unset; background-color: #D35D6E; color: #000000; border-radius: 10px; } -.slider-vol scale trough { +.metric scale trough { all: unset; background-color: #4e4e4e; border-radius: 50px; @@ -31,13 +31,13 @@ margin-left: 10px; margin-right: 20px; } -.slider-ram scale trough highlight { +.metric scale trough highlight { all: unset; background-color: #D35D6E; color: #000000; border-radius: 10px; } -.slider-ram scale trough { +.metric scale trough { all: unset; background-color: #4e4e4e; border-radius: 50px; diff --git a/examples/eww-bar/eww.yuck b/examples/eww-bar/eww.yuck index 844d12e..346661c 100644 --- a/examples/eww-bar/eww.yuck +++ b/examples/eww-bar/eww.yuck @@ -6,8 +6,15 @@ (defwidget sidestuff [] (box :class "sidestuff" :orientation "h" :space-evenly false :halign "end" - (slider-vol) - (slider-ram) + (metric :label "🔊" + :value volume + :onchange "amixer -D pulse sset Master {}%") + (metric :label "" + :value EWW_RAM + :onchange "") + (metric :label "💾" + :value {round((1 - (EWW_DISK["/"].free / EWW_DISK["/"].total)) * 100, 0)} + :onchange "") time)) (defwidget workspaces [] @@ -27,27 +34,34 @@ (button :onclick "wmctrl -s 8" 9))) (defwidget music [] - (box :class "music" :orientation "h" :space-evenly false :halign "center" + (box :class "music" + :orientation "h" + :space-evenly false + :halign "center" '🎵${music}')) -(defwidget slider-vol [] - (box :class "slider-vol" :orientation "h" :space-evenly "false" - (box :class "label-vol" "🔊" - (scale :min 0 :max 101 :value volume :onchange "amixer -D pulse sset Master {}%")))) -(defwidget slider-ram [] - (box :orientation "h" :class "slider-ram" :space-evenly false - (box :class "label-ram" "" - (scale :min 0 :max 101 :active false :value EWW_RAM)))) +(defwidget metric [label value onchange] + (box :orientation "h" + :class "metric" + :space-evenly false + (box :class "label" label) + (scale :min 0 + :max 101 + :active {onchange != ""} + :value value + :onchange onchange))) +(defpoll music :interval "5s" + "playerctl metadata --format '{{ artist }} - {{ title }}' || true") +(defpoll volume :interval "1s" + "scripts/getvol") -(defpoll music :interval "5s" "playerctl metadata --format '{{ artist }} - {{ title }}' || true") -(defpoll volume :interval "1s" "scripts/getvol") - -(defpoll time :interval "10s" "date '+%H:%M %b %d, %Y'") +(defpoll time :interval "10s" + "date '+%H:%M %b %d, %Y'") (defwindow bar :monitor 0 From 3830e003fe4521e56db52f11d3c6e81268b9fd51 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Wed, 18 Aug 2021 16:30:11 +0200 Subject: [PATCH 136/137] Clean up example config further --- crates/eww/src/app.rs | 3 ++- examples/eww-bar/eww.yuck | 12 ++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/crates/eww/src/app.rs b/crates/eww/src/app.rs index 99e7692..05b4f6d 100644 --- a/crates/eww/src/app.rs +++ b/crates/eww/src/app.rs @@ -203,7 +203,8 @@ impl App { self.open_windows .remove(window_name) - .with_context(|| format!("Tried to close window named '{}', but no such window was open", window_name))?; + .with_context(|| format!("Tried to close window named '{}', but no such window was open", window_name))? + .close(); self.eww_state.clear_window_state(window_name); diff --git a/examples/eww-bar/eww.yuck b/examples/eww-bar/eww.yuck index 346661c..7593ed6 100644 --- a/examples/eww-bar/eww.yuck +++ b/examples/eww-bar/eww.yuck @@ -38,7 +38,7 @@ :orientation "h" :space-evenly false :halign "center" - '🎵${music}')) + {music != "" ? "🎵${music}" : ""})) (defwidget metric [label value onchange] @@ -54,8 +54,8 @@ -(defpoll music :interval "5s" - "playerctl metadata --format '{{ artist }} - {{ title }}' || true") +(deflisten music :initial "" + "playerctl --follow metadata --format '{{ artist }} - {{ title }}' || true") (defpoll volume :interval "1s" "scripts/getvol") @@ -66,6 +66,10 @@ (defwindow bar :monitor 0 :windowtype "dock" - :geometry (geometry :x "0%" :y "0%" :width "100%" :height "4%") + :geometry (geometry :x "0%" + :y "0%" + :width "90%" + :height "10px" + :anchor "top center") :reserve (struts :side "top" :distance "4%") (bar)) From 9dc02f413b3642bdf080c90852f96bb17d8def04 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Wed, 18 Aug 2021 16:35:36 +0200 Subject: [PATCH 137/137] set fixed rust toolchain --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index bf867e0..8b49edf 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -nightly +nightly-2021-08-10