From 6e9ba966a1fd097196394f00c43eca15ac571843 Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Wed, 22 Dec 2021 02:57:56 -0500 Subject: start writing a parser --- Cargo.lock | 138 +++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 2 + src/parse.rs | 102 ++++++++++++++++++++++++++++++------- src/shell.pest | 17 +++++++ src/state/history.rs | 17 ++++--- 5 files changed, 251 insertions(+), 25 deletions(-) create mode 100644 src/shell.pest diff --git a/Cargo.lock b/Cargo.lock index 6464d48..047deb5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -161,6 +161,27 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +dependencies = [ + "block-padding", + "byte-tools", + "byteorder", + "generic-array", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +dependencies = [ + "byte-tools", +] + [[package]] name = "blocking" version = "1.1.0" @@ -181,6 +202,18 @@ version = "3.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c" +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + [[package]] name = "cache-padded" version = "1.1.1" @@ -228,12 +261,27 @@ dependencies = [ "syn", ] +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array", +] + [[package]] name = "event-listener" version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7531096570974c3a9dcf9e4b8e1cede1ec26cf5046219fb3b9d897503b9be59" +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" + [[package]] name = "fastrand" version = "1.5.0" @@ -279,6 +327,15 @@ dependencies = [ "waker-fn", ] +[[package]] +name = "generic-array" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" +dependencies = [ + "typenum", +] + [[package]] name = "gloo-timers" version = "0.2.2" @@ -373,6 +430,12 @@ dependencies = [ "value-bag", ] +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + [[package]] name = "match_cfg" version = "0.1.0" @@ -404,6 +467,8 @@ dependencies = [ "hostname", "libc", "nix", + "pest", + "pest_derive", "pty-process", "signal-hook", "signal-hook-async-std", @@ -444,12 +509,61 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + [[package]] name = "parking" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" +[[package]] +name = "pest" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +dependencies = [ + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pest_meta" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d" +dependencies = [ + "maplit", + "pest", + "sha-1", +] + [[package]] name = "pin-project-lite" version = "0.2.7" @@ -505,6 +619,18 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "sha-1" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" +dependencies = [ + "block-buffer", + "digest", + "fake-simd", + "opaque-debug", +] + [[package]] name = "signal-hook" version = "0.3.12" @@ -597,6 +723,18 @@ dependencies = [ "libc", ] +[[package]] +name = "typenum" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec" + +[[package]] +name = "ucd-trie" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" + [[package]] name = "unicode-width" version = "0.1.9" diff --git a/Cargo.toml b/Cargo.toml index c0904f2..b805342 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,8 @@ futures-lite = "1.12.0" hostname = "0.3.1" libc = "0.2.112" nix = "0.23.0" +pest = "2.1.3" +pest_derive = "2.1.0" pty-process = { version = "0.2.0", features = ["backend-async-std"] } signal-hook = "0.3.12" signal-hook-async-std = "0.2.1" diff --git a/src/parse.rs b/src/parse.rs index 42ead6e..abeefb0 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -1,30 +1,52 @@ +use pest::Parser as _; + +#[derive(pest_derive::Parser)] +#[grammar = "shell.pest"] +struct Shell; + +#[derive(Debug, Clone)] pub struct Word { word: String, interpolate: bool, } impl Word { - fn new(word: String) -> Self { + fn new(word: &str) -> Self { Self { - word, + word: word.to_string(), interpolate: true, } } - fn literal(word: String) -> Self { + fn literal(word: &str) -> Self { Self { - word, + word: word.to_string(), interpolate: false, } } } +#[derive(Debug, Clone)] pub struct Exe { exe: Word, args: Vec, + original: String, } impl Exe { + fn parse(pair: pest::iterators::Pair) -> Self { + assert!(matches!(pair.as_rule(), Rule::exe)); + let original = pair.as_str().to_string(); + let mut iter = pair.into_inner(); + let exe = Word::new(iter.next().unwrap().as_str()); + let args = iter.map(|word| Word::new(word.as_str())).collect(); + Self { + exe, + args, + original, + } + } + pub fn exe(&self) -> &str { &self.exe.word } @@ -32,26 +54,72 @@ impl Exe { pub fn args(&self) -> impl Iterator { self.args.iter().map(|arg| arg.word.as_ref()) } + + pub fn input_string(&self) -> String { + self.original.clone() + } } +#[derive(Debug, Clone)] pub enum Command { Exe(Exe), - And(Vec), - Or(Vec), - Both(Vec), - Pipe(Vec), + And(Exe, Box), + Or(Exe, Box), + Both(Exe, Box), + Pipe(Exe, Box), } impl Command { pub fn parse(full_cmd: &str) -> Self { - let mut parts = full_cmd.split(' '); - let cmd = parts.next().unwrap(); - Self::Exe(Exe { - exe: Word::new(cmd.to_string()), - args: parts - .map(std::string::ToString::to_string) - .map(Word::new) - .collect(), - }) + Self::build_ast( + Shell::parse(Rule::line, full_cmd) + .unwrap() + .next() + .unwrap() + .into_inner(), + ) + } + + pub fn input_string(&self) -> String { + match self { + Self::Exe(exe) => exe.input_string(), + Self::And(exe, command) => format!( + "{} && {}", + exe.input_string(), + command.input_string() + ), + Self::Or(exe, command) => format!( + "{} || {}", + exe.input_string(), + command.input_string() + ), + Self::Both(exe, command) => { + format!("{}; {}", exe.input_string(), command.input_string()) + } + Self::Pipe(exe, command) => { + format!("{} | {}", exe.input_string(), command.input_string()) + } + } + } + + fn build_ast(mut pairs: pest::iterators::Pairs) -> Self { + let command = pairs.next().unwrap(); + assert!(matches!(command.as_rule(), Rule::command)); + let mut inner = command.into_inner(); + let exe = inner.next().unwrap(); + let exe = Exe::parse(exe); + if let Some(rest) = inner.next() { + let rule = rest.as_rule(); + let ast = Self::build_ast(rest.into_inner()); + match rule { + Rule::and => Self::And(exe, Box::new(ast)), + Rule::or => Self::Or(exe, Box::new(ast)), + Rule::both => Self::Both(exe, Box::new(ast)), + Rule::pipe => Self::Pipe(exe, Box::new(ast)), + _ => unreachable!(), + } + } else { + Self::Exe(exe) + } } } diff --git a/src/shell.pest b/src/shell.pest new file mode 100644 index 0000000..f1f39ab --- /dev/null +++ b/src/shell.pest @@ -0,0 +1,17 @@ +char = { ASCII_ALPHANUMERIC } + +word = @{ char+ } + +exe = { word+ } + +and = { "&&" ~ command } +or = { "||" ~ command } +both = { ";" ~ command } +pipe = { "|" ~ command } + +command = { exe ~ (and | or | both | pipe)? } + +line = { SOI ~ command ~ EOI } + +WHITESPACE = _{ " " } +COMMENT = _{ "#" ~ ANY* } diff --git a/src/state/history.rs b/src/state/history.rs index 856153d..685650a 100644 --- a/src/state/history.rs +++ b/src/state/history.rs @@ -91,15 +91,16 @@ impl History { let (input_w, input_r) = async_std::channel::unbounded(); let (resize_w, resize_r) = async_std::channel::unbounded(); + let entry = async_std::sync::Arc::new(async_std::sync::Mutex::new( + Entry::new(cmd.clone(), self.size, input_w, resize_w), + )); + // for now let cmd = match cmd { crate::parse::Command::Exe(exe) => exe, _ => todo!(), }; - let entry = async_std::sync::Arc::new(async_std::sync::Mutex::new( - Entry::new(cmd.exe(), self.size, input_w, resize_w), - )); if crate::builtins::is(cmd.exe()) { let code: i32 = crate::builtins::run(cmd.exe(), cmd.args()).into(); @@ -247,7 +248,7 @@ impl std::iter::DoubleEndedIterator for VisibleEntries { } pub struct Entry { - cmd: String, + cmd: crate::parse::Command, vt: vt100::Parser, audible_bell_state: usize, visual_bell_state: usize, @@ -261,13 +262,13 @@ pub struct Entry { impl Entry { fn new( - cmd: &str, + cmd: crate::parse::Command, size: (u16, u16), input: async_std::channel::Sender>, resize: async_std::channel::Sender<(u16, u16)>, ) -> Self { Self { - cmd: cmd.into(), + cmd, vt: vt100::Parser::new(size.0, size.1, 0), audible_bell_state: 0, visual_bell_state: 0, @@ -319,7 +320,7 @@ impl Entry { if self.running() { out.set_bgcolor(textmode::Color::Rgb(16, 64, 16)); } - out.write_str(&self.cmd); + out.write_str(&self.cmd.input_string()); out.reset_attributes(); set_bgcolor(out, focused); @@ -429,7 +430,7 @@ impl Entry { } pub fn cmd(&self) -> String { - self.cmd.clone() + self.cmd.input_string() } pub fn toggle_fullscreen(&mut self) { -- cgit v1.2.3-54-g00ecf