aboutsummaryrefslogtreecommitdiffstats
path: root/src/parse/mod.rs
diff options
context:
space:
mode:
authorJonas Maier <jonas@x77.dev>2026-03-09 20:49:53 +0100
committerJonas Maier <jonas@x77.dev>2026-03-09 20:49:53 +0100
commit5d8d9f07669cdc8ac17c866edb6a8c07bbe4221c (patch)
treef37429eadff223b4381361b7994b6a9840f00f4e /src/parse/mod.rs
parent93e5286946b5ed3e6c27a4ef6f1b457a1f6bebc7 (diff)
downloadpish-5d8d9f07669cdc8ac17c866edb6a8c07bbe4221c.tar.gz
variable default value
Diffstat (limited to 'src/parse/mod.rs')
-rw-r--r--src/parse/mod.rs77
1 files changed, 63 insertions, 14 deletions
diff --git a/src/parse/mod.rs b/src/parse/mod.rs
index d25767d..4806421 100644
--- a/src/parse/mod.rs
+++ b/src/parse/mod.rs
@@ -28,7 +28,7 @@ type Res<T, E> = std::result::Result<T, E>;
pub trait Expander {
type Error;
- fn expand_var(&mut self, v: BString) -> Res<BString, Self::Error>;
+ fn expand_var(&mut self, v: BString, default: Option<BString>) -> Res<BString, Self::Error>;
fn expand_cmd(&mut self, c: Ast<PostExpansion>) -> Res<BString, Self::Error>;
}
@@ -75,7 +75,17 @@ pub fn plain(x: &[u8]) -> StringPart {
}
pub fn var(x: &[u8]) -> StringPart {
- StringPart::Var(VarName { name: x.to_vec() })
+ StringPart::Var(Var {
+ name: VarName { name: x.to_vec() },
+ default: None,
+ })
+}
+
+pub fn var_default(x: &[u8], default: ExpString) -> StringPart {
+ StringPart::Var(Var {
+ name: VarName { name: x.to_vec() },
+ default: Some(default),
+ })
}
pub fn cmdp(x: Ast<PreExpansion>) -> StringPart {
@@ -150,10 +160,18 @@ impl CmdDisplay for StringPart {
items.as_slice().cdisplay(w)?;
write!(w, ")")
}
- StringPart::Var(var_name) => {
- write!(w, "var(")?;
- var_name.name.as_slice().cdisplay(w)?;
- write!(w, ")")
+ StringPart::Var(var) => {
+ if let Some(default) = &var.default {
+ write!(w, "var_default(")?;
+ var.name.name.as_slice().cdisplay(w)?;
+ write!(w, ",")?;
+ default.cdisplay(w)?;
+ write!(w, ")")
+ } else {
+ write!(w, "var(")?;
+ var.name.name.as_slice().cdisplay(w)?;
+ write!(w, ")")
+ }
}
StringPart::Cmd(ast) => {
write!(w, "cmdp(")?;
@@ -312,10 +330,16 @@ impl Pipes<PreExpansion> {
#[derive(Debug, Clone, PartialEq)]
pub enum StringPart {
Boring(BString),
- Var(VarName),
+ Var(Var),
Cmd(Ast<PreExpansion>),
}
+#[derive(Debug, Clone, PartialEq)]
+pub struct Var {
+ name: VarName,
+ default: Option<ExpString>,
+}
+
impl StringPart {
pub fn is_boring(&self) -> bool {
matches!(self, StringPart::Boring(..))
@@ -340,7 +364,13 @@ impl ExpString {
for part in self.parts.into_iter() {
let mut x = match part {
StringPart::Boring(items) => items,
- StringPart::Var(v) => e.expand_var(v.name)?,
+ StringPart::Var(v) => {
+ let default = match v.default {
+ Some(default) => Some(default.expand(e)?),
+ None => None,
+ };
+ e.expand_var(v.name.name, default)?
+ }
StringPart::Cmd(ast) => {
let exp = ast.expand(e)?;
e.expand_cmd(exp)?
@@ -483,18 +513,34 @@ impl Parse for ExpString {
if x == b'?' || x == b'!' {
b.adv();
- p.push(StringPart::Var(VarName { name: vec![x] }))
+ p.push(StringPart::Var(Var {
+ name: VarName { name: vec![x] },
+ default: None,
+ }))
} else if is_var_begin(x) {
let v = VarName::parse(b)?;
- p.push(StringPart::Var(v));
+ p.push(StringPart::Var(Var {
+ name: v,
+ default: None,
+ }));
} else if x == b'{' {
b.adv();
let v = VarName::parse(b)?;
+ let mut default = None;
if !b.has() {
return Err(ParseError::Eof);
} else if b.peek() == b':' {
- todo!(": in var expansion")
+ b.adv();
+ if !b.has() {
+ return Err(ParseError::Eof);
+ }
+ if b.peek() == b'-' {
+ b.adv();
+ default = Some(ExpString::parse(b)?);
+ } else {
+ todo!(": in var expansion")
+ }
}
if !b.has() {
@@ -504,7 +550,7 @@ impl Parse for ExpString {
}
b.adv();
- p.push(StringPart::Var(v));
+ p.push(StringPart::Var(Var { name: v, default }));
} else if x == b'(' {
b.adv();
let cmd = Ast::parse(b)?;
@@ -530,8 +576,11 @@ impl Parse for ExpString {
}
if delim == b' ' && x == b'~' {
- p.push(StringPart::Var(VarName {
- name: b"HOME".to_vec(),
+ p.push(StringPart::Var(Var {
+ name: VarName {
+ name: b"HOME".to_vec(),
+ },
+ default: None,
}));
} else {
add_char(p, x);