aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJonas Maier <jonas@x77.dev>2026-05-08 22:15:29 +0200
committerJonas Maier <jonas@x77.dev>2026-05-08 22:15:29 +0200
commitce5810be396461d4b4edb4eba191f4094e0f97a8 (patch)
tree4c9e917471b2e64c1db34cf0744d769e63ed2650 /src
parentd8712f7ccc8cb26458f3244f6066b9161968f9e1 (diff)
downloadpish-ce5810be396461d4b4edb4eba191f4094e0f97a8.tar.gz
#16: add break and continue to loops
Diffstat (limited to 'src')
-rw-r--r--src/run/builtin.rs38
-rw-r--r--src/run/mod.rs35
2 files changed, 70 insertions, 3 deletions
diff --git a/src/run/builtin.rs b/src/run/builtin.rs
index a52c345..a40ef51 100644
--- a/src/run/builtin.rs
+++ b/src/run/builtin.rs
@@ -856,3 +856,41 @@ impl Builtin for source {
Ok(())
}
}
+
+#[derive(Copy, Clone)]
+pub struct Break;
+
+impl Builtin for Break {
+ fn name(&self) -> &str {
+ "break"
+ }
+
+ fn io(
+ &self,
+ _session: Arc<Mutex<Session>>,
+ _args: &[BString],
+ _stdin: &mut dyn Read,
+ _stdout: &mut dyn Write,
+ ) -> Result {
+ Err(Error::Break)
+ }
+}
+
+#[derive(Copy, Clone)]
+pub struct Continue;
+
+impl Builtin for Continue {
+ fn name(&self) -> &str {
+ "continue"
+ }
+
+ fn io(
+ &self,
+ _session: Arc<Mutex<Session>>,
+ _args: &[BString],
+ _stdin: &mut dyn Read,
+ _stdout: &mut dyn Write,
+ ) -> Result {
+ Err(Error::Continue)
+ }
+} \ No newline at end of file
diff --git a/src/run/mod.rs b/src/run/mod.rs
index 8c45548..7770b86 100644
--- a/src/run/mod.rs
+++ b/src/run/mod.rs
@@ -20,6 +20,8 @@ pub enum ExecError {
Panic,
AliasDepthExceeded,
Parse(crate::parse::ParseError),
+ Break,
+ Continue,
}
impl ExecError {
@@ -63,6 +65,8 @@ impl ExecError {
}
ExecError::AliasDepthExceeded => "alias depth exceeded".to_string(),
ExecError::Parse(pe) => format!("parse error: {pe:?}"),
+ ExecError::Break => "break: only useful in loops".to_string(),
+ ExecError::Continue => "continue: only useful in loops".to_string(),
}
}
}
@@ -72,6 +76,8 @@ impl From<BuiltinError> for ExecError {
match value {
BuiltinError::IO(error) => Self::IO(error),
BuiltinError::Exit(code) => Self::ExecError(code),
+ BuiltinError::Break => Self::Break,
+ BuiltinError::Continue => Self::Continue,
}
}
}
@@ -105,6 +111,7 @@ enum SpawnedCmd {
trait IsSuccessful {
fn is_successful(&self) -> bool;
+ fn as_bool(&self) -> Result<bool, ExecError>;
}
impl IsSuccessful for Result<(), ExecError> {
fn is_successful(&self) -> bool {
@@ -113,6 +120,18 @@ impl IsSuccessful for Result<(), ExecError> {
_ => false,
}
}
+
+ /// returns the boolean value interpreted from this exit code.
+ ///
+ /// in case the exit code indicates divergence (break/continue) that "error" will be propagated
+ fn as_bool(&self) -> Result<bool, ExecError> {
+ match self {
+ Ok(_) | Err(ExecError::ExecError(0)) => Ok(true),
+ Err(ExecError::Break) => Err(ExecError::Break),
+ Err(ExecError::Continue) => Err(ExecError::Continue),
+ Err(_) => Ok(false),
+ }
+ }
}
impl SpawnedCmd {
@@ -380,7 +399,7 @@ impl Executor {
let res = this
.execute_pipeline(cond.condition, stdin.try_clone()?, stdout.try_clone()?)
.join();
- let block = if res.is_successful() {
+ let block = if res.as_bool()? {
cond.true_block
} else {
cond.false_block
@@ -403,9 +422,15 @@ impl Executor {
let condition = condition.clone().expand(&mut this)?;
this.execute_pipeline(condition, stdin.try_clone()?, stdout.try_clone()?)
.join()
- .is_successful()
+ .as_bool()?
} {
- this.execute_block_inner(&block, stdin.try_clone()?, stdout.try_clone()?)?;
+ let res = this.execute_block_inner(&block, stdin.try_clone()?, stdout.try_clone()?);
+ match res {
+ Ok(_) => (),
+ Err(ExecError::Break) => break,
+ Err(ExecError::Continue) => continue,
+ Err(e) => Err(e)?,
+ }
}
Ok(())
});
@@ -597,6 +622,8 @@ pub fn source(se: Arc<Mutex<Session>>, file: &bstr) -> Result<(), ExecError> {
pub enum BuiltinError {
IO(std::io::Error),
Exit(i32),
+ Break,
+ Continue,
}
impl From<std::io::Error> for BuiltinError {
@@ -658,6 +685,8 @@ const BUILTINS: &[&'static dyn BuiltinClone] = &[
&builtin::exit,
&builtin::ct,
&builtin::source,
+ &builtin::Break,
+ &builtin::Continue,
];
pub fn builtin_map() -> HashMap<BString, &'static dyn BuiltinClone> {