aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJonas Maier <>2026-03-06 16:36:43 +0100
committerJonas Maier <>2026-03-06 16:36:43 +0100
commit64323dc8f4b97790dfd4617d965cd304d3f5158c (patch)
tree54ac4bc1aba944eaeac661472eb4dc58e6905af8 /src
parent2564bed64f695fc767d668f47c583944e5b5f3b9 (diff)
downloadpish-64323dc8f4b97790dfd4617d965cd304d3f5158c.tar.gz
more parsing stuff
Diffstat (limited to 'src')
-rw-r--r--src/parse.rs106
1 files changed, 73 insertions, 33 deletions
diff --git a/src/parse.rs b/src/parse.rs
index 05ca020..d56b3e6 100644
--- a/src/parse.rs
+++ b/src/parse.rs
@@ -1,9 +1,26 @@
use crate::BString;
+pub trait Stage {
+ type Str: std::fmt::Debug;
+}
+
+#[derive(Debug)]
+pub struct PreExpansion;
+#[derive(Debug)]
+pub struct PostExpansion;
+
+impl Stage for PreExpansion {
+ type Str = ExpString;
+}
+
+impl Stage for PostExpansion {
+ type Str = BString;
+}
+
#[derive(Debug)]
-pub enum Ast {
+pub enum Ast<T: Stage> {
AssignVar(AssignVar),
- Pipes(Pipes),
+ Pipes(Pipes<T>),
}
#[derive(Debug)]
@@ -13,18 +30,32 @@ pub struct AssignVar {
}
#[derive(Debug)]
-pub struct Pipes {
- pub cmds: Vec<Command>,
+pub struct Pipes<T: Stage> {
+ pub cmds: Vec<Command<T>>,
}
+#[derive(Debug)]
pub enum StringPart {
Boring(BString),
Var(VarName),
- Cmd(Ast),
+ Cmd(Ast<PreExpansion>),
+}
+
+impl StringPart {
+ pub fn is_boring(&self) -> bool {
+ matches!(self, StringPart::Boring(..))
+ }
+ pub fn unwrap_boring(&self) -> BString {
+ match self {
+ StringPart::Boring(items) => items,
+ _ => panic!("unwrap on non-boring value"),
+ }
+ }
}
+#[derive(Debug)]
/// `"hi ${var} $(cmd) "` gets mapped to `[Boring("hi "), Var("var"), String(" "), Cmd(...), Boring(" ")]`
-pub struct ShellString {
+pub struct ExpString {
parts: Vec<StringPart>,
}
@@ -42,6 +73,7 @@ fn is_var_name(x: u8) -> bool {
x.is_ascii_alphanumeric() || x == b'_'
}
+#[derive(Debug)]
struct VarName {
name: BString,
}
@@ -71,7 +103,7 @@ impl Parse for VarName {
}
}
-impl Parse for ShellString {
+impl Parse for ExpString {
fn parse(b: &mut Cursor<'_>) -> Result<Self> {
b.spaces();
if b.is_empty() {
@@ -180,9 +212,9 @@ impl Parse for ShellString {
}
#[derive(Debug)]
-pub struct Command {
- pub cmd: BString,
- pub args: Vec<BString>,
+pub struct Command<T: Stage> {
+ pub cmd: T::Str,
+ pub args: Vec<T::Str>,
}
#[derive(Debug)]
@@ -202,7 +234,7 @@ pub enum ParseError {
type Result<T> = std::result::Result<T, ParseError>;
-pub fn do_parse(x: &[u8]) -> Result<Ast> {
+pub fn do_parse(x: &[u8]) -> Result<Ast<PreExpansion>> {
Ast::parse(&mut Cursor::new(x, ParseMode::Command))
}
@@ -217,6 +249,26 @@ pub struct CompletionContext {
pub partial: BString,
}
+impl CompletionContext {
+ pub fn none() -> Self {
+ Self {
+ kind: CompletionKind::None,
+ partial: BString::new(),
+ }
+ }
+}
+
+fn expstr_cc(s: &ExpString, kind: CompletionKind) -> CompletionContext {
+ if s.parts.len() > 1 || !s.parts[0].is_boring() {
+ CompletionContext::none()
+ } else {
+ CompletionContext {
+ kind,
+ partial: s.parts[0].unwrap_boring().clone(),
+ }
+ }
+}
+
pub fn completion_context<'a>(x: &'a [u8]) -> CompletionContext {
let mut cursor = Cursor::new(x, ParseMode::Completion);
let ast = Ast::parse(&mut cursor);
@@ -224,27 +276,15 @@ pub fn completion_context<'a>(x: &'a [u8]) -> CompletionContext {
Ok(Ast::Pipes(pipes)) if cursor.spaced == false => {
if let Some(cmd) = pipes.cmds.last() {
if cmd.args.is_empty() {
- CompletionContext {
- kind: CompletionKind::Command,
- partial: cmd.cmd.clone(),
- }
+ expstr_cc(&cmd.cmd, CompletionKind::Command)
} else {
- CompletionContext {
- kind: CompletionKind::Argument,
- partial: cmd.args[cmd.args.len() - 1].clone(),
- }
+ expstr_cc(&cmd.args[cmd.args.len() - 1], CompletionKind::Argument)
}
} else {
- CompletionContext {
- kind: CompletionKind::None,
- partial: Vec::new(),
- }
+ CompletionContext::none()
}
}
- _ => CompletionContext {
- kind: CompletionKind::None,
- partial: Vec::new(),
- },
+ _ => CompletionContext::none(),
}
}
@@ -364,18 +404,18 @@ impl Parse for Vec<u8> {
}
}
-impl Parse for Ast {
+impl Parse for Ast<PreExpansion> {
fn parse(b: &mut Cursor<'_>) -> Result<Self> {
Ok(Self::Pipes(b.parse()?))
}
}
-impl Parse for Command {
+impl Parse for Command<PreExpansion> {
fn parse(b: &mut Cursor<'_>) -> Result<Self> {
- let path: Vec<u8> = b.parse()?;
+ let path: ExpString = b.parse()?;
let mut args = Vec::new();
loop {
- let arg: Result<Vec<u8>> = b.parse();
+ let arg: Result<ExpString> = b.parse();
match arg {
Ok(arg) => args.push(arg),
Err(ParseError::Eof | ParseError::UnexpectedPipe) => break,
@@ -386,9 +426,9 @@ impl Parse for Command {
}
}
-impl Parse for Pipes {
+impl Parse for Pipes<PreExpansion> {
fn parse(b: &mut Cursor<'_>) -> Result<Self> {
- let mut cmds: Vec<Command> = vec![b.parse()?];
+ let mut cmds: Vec<Command<PreExpansion>> = vec![b.parse()?];
loop {
b.spaces();