summaryrefslogtreecommitdiffstats
path: root/src/parse.rs
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 /src/parse.rs
parentfea9c70861326fdc1c670e965b1c214741f27be7 (diff)
downloadnbsh-6e9ba966a1fd097196394f00c43eca15ac571843.tar.gz
nbsh-6e9ba966a1fd097196394f00c43eca15ac571843.zip
start writing a parser
Diffstat (limited to 'src/parse.rs')
-rw-r--r--src/parse.rs102
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)
+ }
}
}