summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2022-01-17 16:50:27 -0500
committerJesse Luehrs <doy@tozt.net>2022-01-17 16:50:27 -0500
commitcbcb6eb53ee65710657b2e058d2bfe8c5837a96d (patch)
tree28ce19373754bb44d9fb43c8489d91da2c9ccd6e
parente3250575690f85f7b35c4eae585fbbea106e1cb0 (diff)
downloadnbsh-cbcb6eb53ee65710657b2e058d2bfe8c5837a96d.tar.gz
nbsh-cbcb6eb53ee65710657b2e058d2bfe8c5837a96d.zip
implement else
-rw-r--r--src/parse/ast.rs4
-rw-r--r--src/runner/mod.rs41
-rw-r--r--src/shell.pest5
3 files changed, 43 insertions, 7 deletions
diff --git a/src/parse/ast.rs b/src/parse/ast.rs
index 9d74331..534c87e 100644
--- a/src/parse/ast.rs
+++ b/src/parse/ast.rs
@@ -42,6 +42,7 @@ pub enum Command {
If(Pipeline),
While(Pipeline),
For(String, Vec<Word>),
+ Else(Option<Pipeline>),
End,
}
@@ -70,6 +71,9 @@ impl Command {
list.into_inner().map(Word::build_ast).collect();
Self::For(var.as_str().to_string(), vals)
}
+ Rule::control_else => Self::Else(
+ ty.into_inner().next().map(Pipeline::build_ast),
+ ),
Rule::control_end => Self::End,
_ => unreachable!(),
}
diff --git a/src/runner/mod.rs b/src/runner/mod.rs
index 84be9a1..6d3710e 100644
--- a/src/runner/mod.rs
+++ b/src/runner/mod.rs
@@ -41,7 +41,7 @@ impl Stack {
fn current_pc(&self, pc: usize) -> bool {
match self.top() {
- Some(Frame::If(_)) | None => false,
+ Some(Frame::If(..)) | None => false,
Some(Frame::While(_, start) | Frame::For(_, start, _)) => {
*start == pc
}
@@ -52,7 +52,7 @@ impl Stack {
for frame in &self.frames {
if matches!(
frame,
- Frame::If(false)
+ Frame::If(false, ..)
| Frame::While(false, ..)
| Frame::For(false, ..)
) {
@@ -64,7 +64,7 @@ impl Stack {
}
enum Frame {
- If(bool),
+ If(bool, bool),
While(bool, usize),
For(bool, usize, Vec<String>),
}
@@ -104,13 +104,16 @@ async fn run_commands(
crate::parse::ast::Command::If(pipeline) => {
let should = stack.should_execute();
if !stack.current_pc(pc) {
- stack.push(Frame::If(false));
+ stack.push(Frame::If(false, false));
}
if should {
let status = env.latest_status();
run_pipeline(pipeline.clone(), env, shell_write).await?;
- if let Some(Frame::If(should)) = stack.top_mut() {
+ if let Some(Frame::If(should, found)) = stack.top_mut() {
*should = env.latest_status().success();
+ if *should {
+ *found = true;
+ }
} else {
unreachable!();
}
@@ -174,8 +177,34 @@ async fn run_commands(
}
pc += 1;
}
+ crate::parse::ast::Command::Else(pipeline) => {
+ let mut top = stack.pop();
+ if stack.should_execute() {
+ if let Frame::If(ref mut should, ref mut found) = top {
+ if *found {
+ *should = false;
+ } else if let Some(pipeline) = pipeline {
+ let status = env.latest_status();
+ run_pipeline(pipeline.clone(), env, shell_write)
+ .await?;
+ *should = env.latest_status().success();
+ if *should {
+ *found = true;
+ }
+ env.set_status(status);
+ } else {
+ *should = true;
+ *found = true;
+ }
+ } else {
+ todo!();
+ }
+ }
+ stack.push(top);
+ pc += 1;
+ }
crate::parse::ast::Command::End => match stack.top() {
- Some(Frame::If(_)) => {
+ Some(Frame::If(..)) => {
stack.pop();
pc += 1;
}
diff --git a/src/shell.pest b/src/shell.pest
index fdfb1b1..bb06caa 100644
--- a/src/shell.pest
+++ b/src/shell.pest
@@ -52,8 +52,11 @@ pipeline = ${ (subshell | exe) ~ (w? ~ "|" ~ w? ~ (subshell | exe))* }
control_if = ${ "if" ~ w ~ pipeline }
control_while = ${ "while" ~ w ~ pipeline }
control_for = ${ "for" ~ w ~ bareword ~ w ~ "in" ~ w ~ list }
+control_else = ${ "else" ~ (w ~ "if" ~ w ~ pipeline)? }
control_end = ${ "end" }
-control = ${ control_if | control_while | control_for | control_end }
+control = ${
+ control_if | control_while | control_for | control_else | control_end
+}
command = ${ control | pipeline }
commands = ${ command ~ (w? ~ ";" ~ w? ~ command)* }