summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2021-12-22 02:57:56 -0500
committerJesse Luehrs <doy@tozt.net>2021-12-22 02:58:10 -0500
commit6e9ba966a1fd097196394f00c43eca15ac571843 (patch)
tree9761348cb4fac4a50a4b2ae90932a2e8d78fd444
parentfea9c70861326fdc1c670e965b1c214741f27be7 (diff)
downloadnbsh-6e9ba966a1fd097196394f00c43eca15ac571843.tar.gz
nbsh-6e9ba966a1fd097196394f00c43eca15ac571843.zip
start writing a parser
-rw-r--r--Cargo.lock138
-rw-r--r--Cargo.toml2
-rw-r--r--src/parse.rs102
-rw-r--r--src/shell.pest17
-rw-r--r--src/state/history.rs17
5 files changed, 251 insertions, 25 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 6464d48..047deb5 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -162,6 +162,27 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -182,6 +203,18 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -229,12 +262,27 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -280,6 +328,15 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -374,6 +431,12 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -404,6 +467,8 @@ dependencies = [
"hostname",
"libc",
"nix",
+ "pest",
+ "pest_derive",
"pty-process",
"signal-hook",
"signal-hook-async-std",
@@ -445,12 +510,61 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -506,6 +620,18 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -598,6 +724,18 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
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<Word>,
+ original: String,
}
impl Exe {
+ fn parse(pair: pest::iterators::Pair<Rule>) -> 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<Item = &str> {
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<Command>),
- Or(Vec<Command>),
- Both(Vec<Command>),
- Pipe(Vec<Command>),
+ And(Exe, Box<Command>),
+ Or(Exe, Box<Command>),
+ Both(Exe, Box<Command>),
+ Pipe(Exe, Box<Command>),
}
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<Rule>) -> 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<Vec<u8>>,
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) {