aboutsummaryrefslogtreecommitdiffstats
path: root/src/parse
diff options
context:
space:
mode:
Diffstat (limited to 'src/parse')
-rw-r--r--src/parse/mod.rs85
1 files changed, 52 insertions, 33 deletions
diff --git a/src/parse/mod.rs b/src/parse/mod.rs
index ffbdfe5..102b334 100644
--- a/src/parse/mod.rs
+++ b/src/parse/mod.rs
@@ -403,13 +403,8 @@ pub struct FunDecl<S: Stage> {
impl Parse for Block {
fn parse(b: &mut Cursor<'_>) -> Result<Self> {
let mut commands = Vec::new();
- b.spaces();
- if b.has() && b.peek() == b'{' {
- b.adv();
- } else {
- return Err(ParseError::NotABlock);
- }
+ b.consume_keyword(Keyword::OpenBrace)?;
loop {
while {
@@ -419,7 +414,7 @@ impl Parse for Block {
b.adv();
}
- if b.has() && b"})".contains(&b.peek()) {
+ if b.has() && b"})".contains(&b.peek()) || b.is_completion() && b.is_empty() {
break;
}
@@ -427,16 +422,10 @@ impl Parse for Block {
commands.push(cmd);
}
- let finished_parsing = if b.has() {
- if b.peek() != b'}' {
- return Err(ParseError::Expected('}'));
- }
- b.adv();
- true
- } else if !b.is_completion() {
- return Err(ParseError::Expected('}'));
- } else {
- false
+ let finished_parsing = match b.consume_keyword(Keyword::CloseBrace) {
+ Ok(_) => true,
+ Err(_) if b.is_completion() => false,
+ Err(e) => Err(e)?,
};
Ok(Self {
@@ -1290,7 +1279,8 @@ pub trait Parse: Sized {
}
}
-enum ParseMode {
+#[derive(Copy, Clone)]
+pub enum ParseMode {
Command,
Completion,
}
@@ -1302,7 +1292,7 @@ pub struct Cursor<'a> {
/// if the last byte that was consumed was whitespace or part of a word
spaced: bool,
- backtrace: bool,
+ pub backtrace: bool,
}
#[derive(Default)]
@@ -1314,7 +1304,7 @@ struct SpaceStats {
}
impl<'a> Cursor<'a> {
- fn new(buf: &'a [u8], mode: ParseMode) -> Self {
+ pub fn new(buf: &'a [u8], mode: ParseMode) -> Self {
Self {
buf,
mode,
@@ -1323,6 +1313,10 @@ impl<'a> Cursor<'a> {
}
}
+ pub fn remaining(&self) -> &[u8] {
+ self.buf
+ }
+
// non empty
fn has(&self) -> bool {
!self.buf.is_empty()
@@ -1336,8 +1330,12 @@ impl<'a> Cursor<'a> {
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) {
+ if self.buf.is_empty() {
+ println!("{word} <EOF>\r");
+ } else {
+ println!("{word} {}\r", self.buf[0] as char);
+ }
+ for l in bt.lines().skip(4).take(8) {
println!("{l}\r");
}
println!("\r");
@@ -1413,20 +1411,30 @@ impl<'a> Cursor<'a> {
}
fn consume_keyword(&mut self, kw: Keyword) -> Result<()> {
- self.spaces();
let bytes = kw.as_bytes();
+
+ if self.backtrace {
+ self.bt(&format!("keyword {kw:?}"));
+ }
+
+ self.spaces();
if self.buf.starts_with(bytes) {
- if kw.requires_space() && self.buf.len() > bytes.len() {
- if self.buf[bytes.len()].is_ascii_whitespace() {
+ if kw.requires_space() {
+ if self.buf.len() > bytes.len() && self.buf[bytes.len()].is_ascii_whitespace() {
self.buf = &self.buf[bytes.len() + 1..];
self.spaces();
Ok(())
} else {
- self.buf = &self.buf[bytes.len()..];
- self.spaces();
- Err(ParseError::ExpectedKeyword(kw))
+ if self.is_completion() {
+ self.buf = &self.buf[bytes.len()..];
+ Ok(())
+ } else {
+ Err(ParseError::ExpectedKeyword(kw))
+ }
}
} else {
+ self.buf = &self.buf[bytes.len()..];
+ self.spaces();
Ok(())
}
} else {
@@ -1441,6 +1449,8 @@ pub enum Keyword {
While,
Else,
Elif,
+ OpenBrace,
+ CloseBrace,
}
impl Keyword {
@@ -1450,6 +1460,8 @@ impl Keyword {
Keyword::While => b"while",
Keyword::Elif => b"elif",
Keyword::Else => b"else",
+ Keyword::OpenBrace => b"{",
+ Keyword::CloseBrace => b"}",
}
}
@@ -1459,13 +1471,15 @@ impl Keyword {
Keyword::While => true,
Keyword::Elif => true,
Keyword::Else => false,
+ Keyword::OpenBrace => false,
+ Keyword::CloseBrace => false,
}
}
}
-impl Parse for If<PreExpansion> {
- fn parse(b: &mut Cursor<'_>) -> Result<Self> {
- b.consume_keyword(Keyword::If)?;
+impl If<PreExpansion> {
+ fn parse_internal(b: &mut Cursor<'_>, first_keyword: Keyword) -> Result<Self> {
+ b.consume_keyword(first_keyword)?;
let mut res = If {
condition: Pipes::parse(b)?,
@@ -1493,8 +1507,7 @@ impl Parse for If<PreExpansion> {
res.false_block = if b.consume_keyword(Keyword::Else).is_ok() {
Block::parse(b)?
- } else if b.consume_keyword(Keyword::Elif).is_ok() {
- let elif = If::parse(b)?;
+ } else if let Ok(elif) = Self::parse_internal(b, Keyword::Elif) {
Block {
finished_parsing: elif.parse_progress.is_done(),
commands: vec![Ast::If(elif)],
@@ -1512,6 +1525,12 @@ impl Parse for If<PreExpansion> {
}
}
+impl Parse for If<PreExpansion> {
+ fn parse(b: &mut Cursor<'_>) -> Result<Self> {
+ Self::parse_internal(b, Keyword::If)
+ }
+}
+
impl Parse for While {
fn parse(b: &mut Cursor<'_>) -> Result<Self> {
b.spaces();