aboutsummaryrefslogtreecommitdiffstats
path: root/src/parse.rs
diff options
context:
space:
mode:
authorJonas Maier <>2026-03-06 16:17:48 +0100
committerJonas Maier <>2026-03-06 16:17:48 +0100
commit2564bed64f695fc767d668f47c583944e5b5f3b9 (patch)
treec9f02d303bc74301f5f1844337ab7df927c2bbf4 /src/parse.rs
parent40387917c8ba4703aee111e1cf37dcd793e39ba2 (diff)
downloadpish-2564bed64f695fc767d668f47c583944e5b5f3b9.tar.gz
paaaarser
Diffstat (limited to 'src/parse.rs')
-rw-r--r--src/parse.rs88
1 files changed, 84 insertions, 4 deletions
diff --git a/src/parse.rs b/src/parse.rs
index a685f5d..05ca020 100644
--- a/src/parse.rs
+++ b/src/parse.rs
@@ -19,7 +19,7 @@ pub struct Pipes {
pub enum StringPart {
Boring(BString),
- Var(BString),
+ Var(VarName),
Cmd(Ast),
}
@@ -30,11 +30,47 @@ pub struct ShellString {
fn is_symbol(x: u8) -> bool {
match x {
- b'|' => true,
+ b'|' | b'{' | b'}' | b'$' => true,
_ => false,
}
}
+fn is_var_begin(x: u8) -> bool {
+ x.is_ascii_alphabetic()
+}
+fn is_var_name(x: u8) -> bool {
+ x.is_ascii_alphanumeric() || x == b'_'
+}
+
+struct VarName {
+ name: BString,
+}
+
+impl Parse for VarName {
+ fn parse(b: &mut Cursor<'_>) -> Result<Self> {
+ if b.is_empty() {
+ return Err(ParseError::Eof);
+ }
+
+ if !is_var_begin(b.peek()) {
+ return Err(ParseError::ExpectedAlphabetic);
+ }
+
+ let mut name = BString::new();
+ while b.has() {
+ let x = b.peek();
+ if is_var_name(x) {
+ b.adv();
+ name.push(x)
+ } else {
+ break;
+ }
+ }
+
+ Ok(Self { name })
+ }
+}
+
impl Parse for ShellString {
fn parse(b: &mut Cursor<'_>) -> Result<Self> {
b.spaces();
@@ -77,13 +113,55 @@ impl Parse for ShellString {
b.adv();
+ if delim == b'\'' {
+ // no fancy stuff here
+ add_char(p, x);
+ continue;
+ }
+
if x == b'\\' {
escaping = true;
continue;
}
if x == b'$' {
- todo!()
+ if !b.has() {
+ add_char(p, x);
+ continue;
+ }
+
+ let x = b.peek();
+
+ if is_var_begin(x) {
+ let v = VarName::parse(b)?;
+ p.push(StringPart::Var(v));
+ } else if x == b'{' {
+ b.adv();
+ let v = VarName::parse(b)?;
+
+ if !b.has() {
+ return Err(ParseError::Eof);
+ } else if b.peek() == b':' {
+ todo!(": in var expansion")
+ }
+
+ if !b.has() {
+ return Err(ParseError::Eof);
+ } else if b.peek() != b'}' {
+ return Err(ParseError::Incomplete);
+ }
+
+ b.adv();
+ p.push(StringPart::Var(v));
+ } else if x == b'(' {
+ todo!()
+ } else {
+ // doesn't seem to be a variable or expansion, just add $ back into the string
+ add_char(p, b'$');
+ continue;
+ }
+
+ continue;
}
if delim == b' ' && is_symbol(x) {
@@ -93,7 +171,7 @@ impl Parse for ShellString {
add_char(p, x);
}
- if b.is_completion() {
+ if b.is_completion() || delim == b' ' {
Ok(Self { parts })
} else {
Err(ParseError::Eof)
@@ -117,6 +195,8 @@ pub enum ParseError {
UnexpectedPipe,
+ ExpectedAlphabetic,
+
Unknown(u8),
}