diff options
author | Jesse Luehrs <doy@tozt.net> | 2021-12-22 02:57:56 -0500 |
---|---|---|
committer | Jesse Luehrs <doy@tozt.net> | 2021-12-22 02:58:10 -0500 |
commit | 6e9ba966a1fd097196394f00c43eca15ac571843 (patch) | |
tree | 9761348cb4fac4a50a4b2ae90932a2e8d78fd444 /src/parse.rs | |
parent | fea9c70861326fdc1c670e965b1c214741f27be7 (diff) | |
download | nbsh-6e9ba966a1fd097196394f00c43eca15ac571843.tar.gz nbsh-6e9ba966a1fd097196394f00c43eca15ac571843.zip |
start writing a parser
Diffstat (limited to 'src/parse.rs')
-rw-r--r-- | src/parse.rs | 102 |
1 files changed, 85 insertions, 17 deletions
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) + } } } |