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 01/42] 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 02/42] 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 03/42] 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 04/42] 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 05/42] 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 06/42] 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 07/42] 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 08/42] 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 09/42] 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 10/42] 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 11/42] 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 12/42] 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 13/42] 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 14/42] 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 15/42] 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 16/42] 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 17/42] 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 18/42] 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 19/42] 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 20/42] 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 21/42] 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 22/42] 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 23/42] 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 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 24/42] 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 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 25/42] 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 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 26/42] 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 27/42] 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 28/42] 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 29/42] 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 30/42] 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 31/42] 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 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 32/42] 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 33/42] 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 34/42] 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 35/42] 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 36/42] 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 37/42] 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 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 38/42] 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 39/42] 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 40/42] 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 41/42] 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 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 42/42] 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