diff options
| author | Jonas Maier <jonas@x77.dev> | 2026-05-08 19:00:39 +0200 |
|---|---|---|
| committer | Jonas Maier <jonas@x77.dev> | 2026-05-08 19:00:39 +0200 |
| commit | f4b69eb87ca61f408323ab9d24938859f7c4ceb3 (patch) | |
| tree | d74bdb4869eb7ce0ee2899ec92244a484ff5b4f4 | |
| parent | 5697ecb492579413a7ce9589df6847a3e205c647 (diff) | |
| download | pish-f4b69eb87ca61f408323ab9d24938859f7c4ceb3.tar.gz | |
while loops
| -rw-r--r-- | src/parse/mod.rs | 50 | ||||
| -rw-r--r-- | src/parse/test.rs | 2 | ||||
| -rw-r--r-- | src/run/mod.rs | 66 |
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(()) }); |
