diff options
| author | Jonas Maier <> | 2026-03-06 16:36:43 +0100 |
|---|---|---|
| committer | Jonas Maier <> | 2026-03-06 16:36:43 +0100 |
| commit | 64323dc8f4b97790dfd4617d965cd304d3f5158c (patch) | |
| tree | 54ac4bc1aba944eaeac661472eb4dc58e6905af8 | |
| parent | 2564bed64f695fc767d668f47c583944e5b5f3b9 (diff) | |
| download | pish-64323dc8f4b97790dfd4617d965cd304d3f5158c.tar.gz | |
more parsing stuff
| -rw-r--r-- | src/parse.rs | 106 |
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(); |
