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 --- src/parse.rs | 102 +++++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 85 insertions(+), 17 deletions(-) (limited to 'src/parse.rs') 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) + } } } -- cgit v1.2.3-54-g00ecf