aboutsummaryrefslogtreecommitdiffstats
path: root/src/parse.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/parse.rs')
-rw-r--r--src/parse.rs132
1 files changed, 63 insertions, 69 deletions
diff --git a/src/parse.rs b/src/parse.rs
index 2ed0dbf..b2433a8 100644
--- a/src/parse.rs
+++ b/src/parse.rs
@@ -106,7 +106,7 @@ impl ExpString {
fn is_symbol(x: u8) -> bool {
match x {
- b'|' | b'{' | b'}' | b'$' => true,
+ b'|' | b'{' | b'}' | b'$' | b'(' | b')' | b'\'' | b'"' => true,
_ => false,
}
}
@@ -152,14 +152,16 @@ impl Parse for ExpString {
fn parse(b: &mut Cursor<'_>) -> Result<Self> {
b.spaces();
if b.is_empty() {
- return Err(ParseError::Eof);
+ return Err(ParseError::NotAString);
}
let mut delim = b.peek();
- if delim != b'\'' && delim != b'"' {
- delim = b' ';
- } else {
+ if delim == b'\'' || delim == b'"' {
b.adv();
+ } else if is_symbol(delim) && delim != b'$' {
+ return Err(ParseError::NotAString);
+ } else {
+ delim = b' ';
}
let mut parts = Vec::new();
@@ -181,13 +183,17 @@ impl Parse for ExpString {
continue;
}
- if x == delim {
+ if x == delim || (b.peek_space() && delim == b' ') {
if delim != b' ' {
b.adv();
}
return Ok(Self { parts });
}
+ if delim == b' ' && is_symbol(x) && x != b'$' {
+ return Ok(Self { parts });
+ }
+
b.adv();
if delim == b'\'' {
@@ -231,7 +237,16 @@ impl Parse for ExpString {
b.adv();
p.push(StringPart::Var(v));
} else if x == b'(' {
- todo!()
+ b.adv();
+ let cmd = Ast::parse(b)?;
+ b.spaces();
+ if b.is_empty() {
+ return Err(ParseError::Eof);
+ } else if b.peek() == b')' {
+ p.push(StringPart::Cmd(cmd));
+ } else {
+ return Err(ParseError::Expected(')'));
+ }
} else {
// doesn't seem to be a variable or expansion, just add $ back into the string
add_char(p, b'$');
@@ -241,10 +256,6 @@ impl Parse for ExpString {
continue;
}
- if delim == b' ' && is_symbol(x) {
- return Ok(Self { parts });
- }
-
add_char(p, x);
}
@@ -286,12 +297,20 @@ pub enum ParseError {
ExpectedAlphabetic,
Unknown(u8),
+
+ Expected(char),
+
+ NotAString,
}
type Result<T> = std::result::Result<T, ParseError>;
-pub fn do_parse(x: &[u8]) -> Result<Ast<PreExpansion>> {
- Ast::parse(&mut Cursor::new(x, ParseMode::Command))
+pub fn do_parse(x: &[u8]) -> Res<Ast<PreExpansion>, (ParseError, &[u8])> {
+ let mut c = Cursor::new(x, ParseMode::Command);
+ match Ast::parse(&mut c) {
+ Ok(ast) => Ok(ast),
+ Err(e) => Err((e, c.buf)),
+ }
}
pub enum CompletionKind {
@@ -359,6 +378,8 @@ struct Cursor<'a> {
/// if the last byte that was consumed was whitespace or part of a word
spaced: bool,
+
+ backtrace: bool,
}
impl<'a> Cursor<'a> {
@@ -367,6 +388,7 @@ impl<'a> Cursor<'a> {
buf,
mode,
spaced: false,
+ backtrace: false,
}
}
@@ -379,19 +401,40 @@ impl<'a> Cursor<'a> {
self.buf.is_empty()
}
+ fn bt(&self, word: &str) {
+ if self.backtrace {
+ let bt = std::backtrace::Backtrace::capture();
+ let bt = format!("{bt}");
+ println!("{word} {}\r", self.buf[0] as char);
+ for l in bt.lines().skip(4).take(2) {
+ println!("{l}\r");
+ }
+ println!("\r");
+ }
+ }
+
fn peek(&self) -> u8 {
+ self.bt("peek");
self.buf[0]
}
fn adv(&mut self) -> u8 {
+ self.bt("adv");
let out = self.buf[0];
self.buf = &self.buf[1..];
self.spaced = false;
out
}
+ fn peek_space(&self) -> bool {
+ if self.buf.is_empty() {
+ return false;
+ }
+ matches!(self.buf[0], b' ' | b'\t' | b'\n' | b'\r')
+ }
+
fn spaces(&mut self) {
- while let Some(b' ' | b'\t') = self.buf.first() {
+ while self.peek_space() {
self.adv();
self.spaced = true;
}
@@ -409,57 +452,6 @@ impl<'a> Cursor<'a> {
}
}
-fn parse_quoted_string(b: &mut Cursor<'_>, delim: u8) -> Result<Vec<u8>> {
- // TODO: escape sequence stuff
-
- let mut s = Vec::new();
- while b.has() {
- if delim == b' ' && b.peek() == b'|' {
- return if s.len() == 0 {
- Err(ParseError::UnexpectedPipe)
- } else {
- Ok(s)
- };
- }
-
- if b.peek() == delim {
- b.adv();
- if delim == b' ' {
- b.spaced = true;
- }
- return Ok(s);
- }
-
- s.push(b.adv());
- }
-
- if delim == b' ' || b.is_completion() {
- Ok(s)
- } else {
- Err(ParseError::Incomplete)
- }
-}
-
-impl Parse for Vec<u8> {
- fn parse(b: &mut Cursor<'_>) -> Result<Self> {
- b.spaces();
- if b.is_empty() {
- return Err(ParseError::Eof);
- }
- let c = b.peek();
- if c == b'|' {
- Err(ParseError::UnexpectedPipe)
- } else if c == b'\'' || c == b'"' {
- b.adv();
- parse_quoted_string(b, c)
- } else if c.is_ascii_graphic() {
- parse_quoted_string(b, b' ')
- } else {
- Err(ParseError::Unknown(c))
- }
- }
-}
-
impl Parse for Ast<PreExpansion> {
fn parse(b: &mut Cursor<'_>) -> Result<Self> {
Ok(Self::Pipes(b.parse()?))
@@ -471,14 +463,14 @@ impl Parse for Command<PreExpansion> {
let path: ExpString = b.parse()?;
let mut args = Vec::new();
loop {
- let arg: Result<ExpString> = b.parse();
- match arg {
+ match ExpString::parse(b) {
Ok(arg) => args.push(arg),
- Err(ParseError::Eof | ParseError::UnexpectedPipe) => break,
+ Err(ParseError::NotAString) => break,
Err(e) => Err(e)?,
}
}
- Ok(Self { cmd: path, args })
+ let x = Ok(Self { cmd: path, args });
+ x
}
}
@@ -496,6 +488,8 @@ impl Parse for Pipes<PreExpansion> {
if c == b'|' {
b.adv();
cmds.push(b.parse()?);
+ } else if is_symbol(c) {
+ return Ok(Pipes { cmds });
} else {
Err(ParseError::Unknown(c))?;
}