aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/parse/mod.rs50
-rw-r--r--src/parse/test.rs2
-rw-r--r--src/run/mod.rs66
3 files changed, 103 insertions, 15 deletions
diff --git a/src/parse/mod.rs b/src/parse/mod.rs
index 47aff20..dcb5790 100644
--- a/src/parse/mod.rs
+++ b/src/parse/mod.rs
@@ -80,6 +80,7 @@ pub enum Ast<T: Stage> {
VarAssign(VarAssign<T>),
Pipes(Pipes<T>),
If(If<T>),
+ While(While),
}
#[allow(unused)]
@@ -99,6 +100,12 @@ pub struct If<T: Stage> {
parse_progress: IfParseProgress,
}
+#[derive(Debug, Clone, PartialEq)]
+pub struct While {
+ pub condition: Pipes<PreExpansion>,
+ pub block: Block,
+}
+
impl If<PreExpansion> {
fn expand<E: Expander>(self, e: &mut E) -> Res<If<PostExpansion>, E::Error> {
Ok(If {
@@ -140,6 +147,13 @@ pub fn cond(
})
}
+pub fn whil(condition: Ast<PreExpansion>, block: Block) -> Ast<PreExpansion> {
+ let Ast::Pipes(condition) = condition else {
+ panic!()
+ };
+ Ast::While(While { condition, block })
+}
+
pub fn estr(x: &[u8]) -> ExpString {
ExpString {
parts: vec![StringPart::Boring(x.to_vec())],
@@ -243,6 +257,13 @@ impl CmdDisplay for Ast<PreExpansion> {
i.false_block.cdisplay(w)?;
write!(w, ")")?;
}
+ Ast::While(l) => {
+ write!(w, "whil(")?;
+ Ast::Pipes(l.condition.clone()).cdisplay(w)?;
+ write!(w, ", ")?;
+ l.block.cdisplay(w)?;
+ write!(w, ")")?;
+ }
}
Ok(())
}
@@ -328,6 +349,7 @@ impl Ast<PreExpansion> {
Ast::Pipes(pipes) => Ok(Ast::Pipes(pipes.expand(e)?)),
Ast::FunDecl(fd) => Ok(Ast::FunDecl(fd.expand(e)?)),
Ast::If(i) => Ok(Ast::If(i.expand(e)?)),
+ Ast::While(w) => Ok(Ast::While(w)),
}
}
}
@@ -478,7 +500,7 @@ pub struct Pipes<T: Stage> {
}
impl Pipes<PreExpansion> {
- fn expand<E: Expander>(self, e: &mut E) -> Res<Pipes<PostExpansion>, E::Error> {
+ pub fn expand<E: Expander>(self, e: &mut E) -> Res<Pipes<PostExpansion>, E::Error> {
let mut cmds = Vec::with_capacity(self.cmds.len());
for cmd in self.cmds.into_iter() {
cmds.push(cmd.expand(e)?);
@@ -1117,6 +1139,8 @@ pub enum ParseError {
NotABlock,
NotAnIf,
+
+ NotAWhile,
}
type Result<T> = std::result::Result<T, ParseError>;
@@ -1177,6 +1201,7 @@ impl Ast<PreExpansion> {
Ast::VarAssign(va) => va.val.completion(e, CompletionKind::Argument),
Ast::Pipes(p) => p.completion(e),
Ast::If(i) => i.completion(e),
+ Ast::While(_) => todo!(),
}
}
}
@@ -1422,6 +1447,21 @@ impl Parse for If<PreExpansion> {
}
}
+impl Parse for While {
+ fn parse(b: &mut Cursor<'_>) -> Result<Self> {
+ b.spaces();
+ if !b.buf.starts_with(b"while ") || b.buf.starts_with(b"while\t") {
+ return Err(ParseError::NotAWhile);
+ }
+ b.advance(6);
+ b.spaces();
+ let condition = Pipes::parse(b)?;
+ let block = Block::parse(b)?;
+
+ Ok(Self { condition, block })
+ }
+}
+
impl Parse for Ast<PreExpansion> {
fn parse(b: &mut Cursor<'_>) -> Result<Self> {
b.spaces();
@@ -1435,6 +1475,14 @@ impl Parse for Ast<PreExpansion> {
}
let orig_len = b.buf.len();
+ let x = While::parse(b);
+ if let Ok(x) = x {
+ return Ok(Self::While(x));
+ } else if b.buf.len() != orig_len {
+ x?;
+ }
+
+ let orig_len = b.buf.len();
let x = VarAssign::parse(b);
if let Ok(va) = x {
return Ok(Self::VarAssign(va));
diff --git a/src/parse/test.rs b/src/parse/test.rs
index f4ac92e..768a5a2 100644
--- a/src/parse/test.rs
+++ b/src/parse/test.rs
@@ -259,7 +259,7 @@ fn if_else() {
#[test]
fn simple_while() {
- parse_test!(parse(b"while cond { x }"), todo!());
+ parse_test!(parse(b"while cond { x }"), whil(pipes([cmd([estr(b"cond")]),]), block([pipes([cmd([estr(b"x")]),])])));
}
#[test]
diff --git a/src/run/mod.rs b/src/run/mod.rs
index 106e22c..8c45548 100644
--- a/src/run/mod.rs
+++ b/src/run/mod.rs
@@ -103,6 +103,18 @@ enum SpawnedCmd {
Pipeline(SpawnedPipeline),
}
+trait IsSuccessful {
+ fn is_successful(&self) -> bool;
+}
+impl IsSuccessful for Result<(), ExecError> {
+ fn is_successful(&self) -> bool {
+ match self {
+ Ok(_) | Err(ExecError::ExecError(0)) => true,
+ _ => false,
+ }
+ }
+}
+
impl SpawnedCmd {
fn join(self) -> Result<(), ExecError> {
match self {
@@ -244,12 +256,7 @@ impl Executor {
) -> SpawnedCmd {
let mut this = self.clone();
let handle = wait::spawn(move || -> Result<(), ExecError> {
- for cmd in block.commands {
- let cmd = cmd.expand(&mut this)?;
- this.execute(cmd, stdin.try_clone()?, stdout.try_clone()?)
- .join()?;
- }
- Ok(())
+ this.execute_block_inner(&block, stdin, stdout)
});
SpawnedCmd::Fun(handle)
}
@@ -320,6 +327,7 @@ impl Executor {
Ast::VarAssign(va) => self.execute_var_assign(va),
Ast::Pipes(pipes) => self.execute_pipeline(pipes, stdin, stdout),
Ast::If(cond) => self.execute_if(cond, stdin, stdout),
+ Ast::While(w) => self.execute_while(w, stdin, stdout),
}
}
@@ -347,6 +355,20 @@ impl Executor {
this.exec_loop(cmd, &mut [c1, c2])
}
+ fn execute_block_inner(
+ &mut self,
+ block: &Block,
+ stdin: InputReader,
+ stdout: OutputWriter,
+ ) -> Result<(), ExecError> {
+ for cmd in block.commands.iter() {
+ let cmd = cmd.clone().expand(self)?;
+ self.execute(cmd, stdin.try_clone()?, stdout.try_clone()?)
+ .join()?;
+ }
+ Ok(())
+ }
+
fn execute_if(
&mut self,
cond: parse::If<PostExpansion>,
@@ -358,14 +380,32 @@ impl Executor {
let res = this
.execute_pipeline(cond.condition, stdin.try_clone()?, stdout.try_clone()?)
.join();
- let block = match res {
- Ok(_) | Err(ExecError::ExecError(0)) => cond.true_block,
- Err(_) => cond.false_block,
+ let block = if res.is_successful() {
+ cond.true_block
+ } else {
+ cond.false_block
};
- for cmd in block.commands {
- let cmd = cmd.expand(&mut this)?;
- this.execute(cmd, stdin.try_clone()?, stdout.try_clone()?)
- .join()?;
+ this.execute_block_inner(&block, stdin.try_clone()?, stdout.try_clone()?)
+ });
+ SpawnedCmd::Fun(handle)
+ }
+
+ fn execute_while(
+ &mut self,
+ w: parse::While,
+ stdin: InputReader,
+ stdout: OutputWriter,
+ ) -> SpawnedCmd {
+ let mut this = self.clone();
+ let parse::While { condition, block } = w;
+ let handle = wait::spawn(move || -> Result<(), ExecError> {
+ while {
+ let condition = condition.clone().expand(&mut this)?;
+ this.execute_pipeline(condition, stdin.try_clone()?, stdout.try_clone()?)
+ .join()
+ .is_successful()
+ } {
+ this.execute_block_inner(&block, stdin.try_clone()?, stdout.try_clone()?)?;
}
Ok(())
});