From 07daff9331dbdc607584edbf1a8fb3e415c338ea Mon Sep 17 00:00:00 2001 From: Jonas Maier <> Date: Fri, 22 May 2026 17:44:24 +0200 Subject: lenient block parsing for completion --- src/parse/mod.rs | 85 ++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 52 insertions(+), 33 deletions(-) (limited to 'src/parse/mod.rs') 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 { impl Parse for Block { fn parse(b: &mut Cursor<'_>) -> Result { 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} \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 { - fn parse(b: &mut Cursor<'_>) -> Result { - b.consume_keyword(Keyword::If)?; +impl If { + fn parse_internal(b: &mut Cursor<'_>, first_keyword: Keyword) -> Result { + b.consume_keyword(first_keyword)?; let mut res = If { condition: Pipes::parse(b)?, @@ -1493,8 +1507,7 @@ impl Parse for If { 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 { } } +impl Parse for If { + fn parse(b: &mut Cursor<'_>) -> Result { + Self::parse_internal(b, Keyword::If) + } +} + impl Parse for While { fn parse(b: &mut Cursor<'_>) -> Result { b.spaces(); -- cgit v1.2.3