From 9dd1d992bd2344d8824e69d8f47c9009a6caf021 Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Fri, 7 Jan 2022 23:42:32 -0500 Subject: large refactor --- src/parse.rs | 335 +++++++++++------------------------------------------------ 1 file changed, 63 insertions(+), 272 deletions(-) (limited to 'src/parse.rs') diff --git a/src/parse.rs b/src/parse.rs index 1dfb4f9..f147892 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -1,10 +1,65 @@ -use pest::Parser as _; +pub mod ast; -#[derive(pest_derive::Parser)] -#[grammar = "shell.pest"] -struct Shell; +#[derive(Debug)] +pub struct Commands { + pipelines: Vec, +} + +impl Commands { + pub fn pipelines(&self) -> &[Pipeline] { + &self.pipelines + } +} -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug)] +pub struct Pipeline { + exes: Vec, + input_string: String, +} + +impl Pipeline { + pub fn into_exes(self) -> impl Iterator { + self.exes.into_iter() + } + + pub fn input_string(&self) -> &str { + &self.input_string + } +} + +#[derive(Debug)] +pub struct Exe { + exe: std::path::PathBuf, + args: Vec, + redirects: Vec, +} + +impl Exe { + pub fn exe(&self) -> &std::path::Path { + &self.exe + } + + pub fn args(&self) -> &[String] { + &self.args + } + + pub fn redirects(&self) -> &[Redirect] { + &self.redirects + } + + pub fn shift(&mut self) { + self.exe = std::path::PathBuf::from(self.args.remove(0)); + } +} + +#[derive(Debug, Clone)] +pub struct Redirect { + pub from: std::os::unix::io::RawFd, + pub to: RedirectTarget, + pub dir: Direction, +} + +#[derive(Debug, Clone)] pub enum RedirectTarget { Fd(std::os::unix::io::RawFd), File(std::path::PathBuf), @@ -25,12 +80,12 @@ impl Direction { use nix::fcntl::OFlag; use nix::sys::stat::Mode; Ok(match self { - crate::parse::Direction::In => nix::fcntl::open( + Self::In => nix::fcntl::open( path, OFlag::O_NOCTTY | OFlag::O_RDONLY, Mode::empty(), )?, - crate::parse::Direction::Out => nix::fcntl::open( + Self::Out => nix::fcntl::open( path, OFlag::O_CREAT | OFlag::O_NOCTTY @@ -43,7 +98,7 @@ impl Direction { | Mode::S_IROTH | Mode::S_IWOTH, )?, - crate::parse::Direction::Append => nix::fcntl::open( + Self::Append => nix::fcntl::open( path, OFlag::O_APPEND | OFlag::O_CREAT @@ -60,266 +115,6 @@ impl Direction { } } -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Redirect { - pub from: std::os::unix::io::RawFd, - pub to: RedirectTarget, - pub dir: Direction, -} - -impl Redirect { - fn parse(prefix: &str, to: &str) -> Self { - let (from, dir) = if let Some(from) = prefix.strip_suffix(">>") { - (from, Direction::Append) - } else if let Some(from) = prefix.strip_suffix('>') { - (from, Direction::Out) - } else if let Some(from) = prefix.strip_suffix('<') { - (from, Direction::In) - } else { - unreachable!() - }; - let from = if from.is_empty() { - match dir { - Direction::In => 0, - Direction::Out | Direction::Append => 1, - } - } else { - from.parse().unwrap() - }; - let to = to.strip_prefix('&').map_or_else( - || RedirectTarget::File(to.into()), - |fd| RedirectTarget::Fd(fd.parse().unwrap()), - ); - Self { from, to, dir } - } -} - -#[derive(Debug, PartialEq, Eq)] -pub struct Word { - word: String, - interpolate: bool, - quoted: bool, -} - -impl Word { - fn parse(s: &str, interpolate: bool, quoted: bool) -> Self { - let mut word_str = s.to_string(); - if interpolate { - word_str = strip_escape(&word_str); - } else { - word_str = strip_basic_escape(&word_str); - } - Self { - word: word_str, - interpolate, - quoted, - } - } -} - -enum WordOrRedirect { - Word(Word), - Redirect(Redirect), -} - -impl WordOrRedirect { - fn build_ast(pair: pest::iterators::Pair) -> Self { - assert!(matches!(pair.as_rule(), Rule::word)); - let mut inner = pair.into_inner(); - let mut word = inner.next().unwrap(); - let mut prefix = None; - if matches!(word.as_rule(), Rule::redir_prefix) { - prefix = Some(word.as_str().trim().to_string()); - word = inner.next().unwrap(); - } - assert!(matches!( - word.as_rule(), - Rule::bareword | Rule::single_string | Rule::double_string - )); - let word = Word::parse( - word.as_str(), - matches!(word.as_rule(), Rule::bareword | Rule::double_string), - matches!( - word.as_rule(), - Rule::single_string | Rule::double_string - ), - ); - if let Some(prefix) = prefix { - Self::Redirect(Redirect::parse(&prefix, &word.word)) - } else { - Self::Word(word) - } - } -} - -#[derive(Debug, PartialEq, Eq)] -pub struct Exe { - exe: Word, - args: Vec, - redirects: Vec, -} - -impl Exe { - fn build_ast(pair: pest::iterators::Pair) -> Self { - assert!(matches!(pair.as_rule(), Rule::exe)); - let mut iter = pair.into_inner(); - let exe = match WordOrRedirect::build_ast(iter.next().unwrap()) { - WordOrRedirect::Word(word) => word, - WordOrRedirect::Redirect(_) => todo!(), - }; - let (args, redirects): (_, Vec<_>) = iter - .map(WordOrRedirect::build_ast) - .partition(|word| matches!(word, WordOrRedirect::Word(_))); - let args = args - .into_iter() - .map(|word| match word { - WordOrRedirect::Word(word) => word, - WordOrRedirect::Redirect(_) => unreachable!(), - }) - .collect(); - let redirects = redirects - .into_iter() - .map(|word| match word { - WordOrRedirect::Word(_) => unreachable!(), - WordOrRedirect::Redirect(redirect) => redirect, - }) - .collect(); - Self { - exe, - args, - redirects, - } - } - - pub fn exe(&self) -> &str { - &self.exe.word - } - - pub fn args(&self) -> impl Iterator { - self.args.iter().map(|arg| arg.word.as_ref()) - } - - pub fn redirects(&self) -> &[Redirect] { - &self.redirects - } - - pub fn shift(&mut self) { - self.exe = self.args.remove(0); - } -} - -#[derive(Debug, PartialEq, Eq)] -pub struct Pipeline { - exes: Vec, - input_string: String, -} - -impl Pipeline { - pub fn parse(pipeline: &str) -> Result { - Ok(Self::build_ast( - Shell::parse(Rule::pipeline, pipeline) - .map_err(|e| Error::new(pipeline, anyhow::anyhow!(e)))? - .next() - .unwrap(), - )) - } - - pub fn into_exes(self) -> impl Iterator { - self.exes.into_iter() - } - - pub fn input_string(&self) -> &str { - &self.input_string - } - - fn build_ast(pipeline: pest::iterators::Pair) -> Self { - assert!(matches!(pipeline.as_rule(), Rule::pipeline)); - let input_string = pipeline.as_str().to_string(); - Self { - exes: pipeline.into_inner().map(Exe::build_ast).collect(), - input_string, - } - } -} - -#[derive(Debug, PartialEq, Eq)] -pub struct Commands { - pipelines: Vec, - input_string: String, -} - -impl Commands { - pub fn parse(full_cmd: &str) -> Result { - Ok(Self::build_ast( - Shell::parse(Rule::line, full_cmd) - .map_err(|e| Error::new(full_cmd, anyhow::anyhow!(e)))? - .next() - .unwrap() - .into_inner() - .next() - .unwrap(), - )) - } - - pub fn pipelines(&self) -> &[Pipeline] { - &self.pipelines - } - - pub fn input_string(&self) -> &str { - &self.input_string - } - - fn build_ast(commands: pest::iterators::Pair) -> Self { - assert!(matches!(commands.as_rule(), Rule::commands)); - let input_string = commands.as_str().to_string(); - Self { - pipelines: commands - .into_inner() - .map(Pipeline::build_ast) - .collect(), - input_string, - } - } -} - -fn strip_escape(s: &str) -> String { - let mut new = String::new(); - let mut escape = false; - for c in s.chars() { - if escape { - new.push(c); - escape = false; - } else { - match c { - '\\' => escape = true, - _ => new.push(c), - } - } - } - new -} - -fn strip_basic_escape(s: &str) -> String { - let mut new = String::new(); - let mut escape = false; - for c in s.chars() { - if escape { - match c { - '\\' | '\'' => {} - _ => new.push('\\'), - } - new.push(c); - escape = false; - } else { - match c { - '\\' => escape = true, - _ => new.push(c), - } - } - } - new -} - #[derive(Debug)] pub struct Error { input: String, @@ -350,7 +145,3 @@ impl std::error::Error for Error { Some(&*self.e) } } - -#[cfg(test)] -#[path = "test_parse.rs"] -mod test; -- cgit v1.2.3-54-g00ecf