From 9a727206f1c94f7580e79a5355adc4b932ae6345 Mon Sep 17 00:00:00 2001 From: Jonas Maier <> Date: Fri, 22 May 2026 16:51:05 +0200 Subject: if-statement parsing might now allow for completion within if-statements --- src/parse/mod.rs | 122 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 100 insertions(+), 22 deletions(-) (limited to 'src/parse/mod.rs') diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 1827122..ffbdfe5 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -47,6 +47,7 @@ pub trait Expander { #[derive(Debug, Clone, PartialEq)] pub struct Block { pub commands: Vec>, + pub finished_parsing: bool, } #[derive(Debug, Clone, PartialEq)] @@ -84,7 +85,6 @@ pub enum Ast { While(While), } -#[allow(unused)] #[derive(Debug, Clone, PartialEq)] enum IfParseProgress { Condition, @@ -93,6 +93,12 @@ enum IfParseProgress { Done, } +impl IfParseProgress { + pub fn is_done(&self) -> bool { + matches!(self, Self::Done) + } +} + #[derive(Debug, Clone, PartialEq)] pub struct If { pub condition: Pipes, @@ -206,6 +212,7 @@ pub fn cmd(x: [ExpString; N]) -> Command { pub fn block(x: [Ast; N]) -> Block { Block { commands: x.to_vec(), + finished_parsing: true, } } @@ -420,16 +427,22 @@ impl Parse for Block { commands.push(cmd); } - if b.has() { + 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 + }; - Ok(Self { commands }) + Ok(Self { + commands, + finished_parsing, + }) } } @@ -1128,6 +1141,8 @@ pub enum ParseError { NotAnIf, NotAWhile, + + ExpectedKeyword(Keyword), } type Result = std::result::Result; @@ -1177,6 +1192,7 @@ impl Block { fn empty() -> Self { Self { commands: Vec::with_capacity(0), + finished_parsing: true, } } } @@ -1395,42 +1411,104 @@ impl<'a> Cursor<'a> { fn parse(&mut self) -> Result { T::parse(self) } + + fn consume_keyword(&mut self, kw: Keyword) -> Result<()> { + self.spaces(); + let bytes = kw.as_bytes(); + if self.buf.starts_with(bytes) { + if kw.requires_space() && self.buf.len() > bytes.len() { + if 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)) + } + } else { + Ok(()) + } + } else { + Err(ParseError::ExpectedKeyword(kw)) + } + } +} + +#[derive(Debug, PartialEq)] +pub enum Keyword { + If, + While, + Else, + Elif, +} + +impl Keyword { + fn as_bytes(&self) -> &bstr { + match self { + Keyword::If => b"if", + Keyword::While => b"while", + Keyword::Elif => b"elif", + Keyword::Else => b"else", + } + } + + fn requires_space(&self) -> bool { + match self { + Keyword::If => true, + Keyword::While => true, + Keyword::Elif => true, + Keyword::Else => false, + } + } } impl Parse for If { fn parse(b: &mut Cursor<'_>) -> Result { + b.consume_keyword(Keyword::If)?; + + let mut res = If { + condition: Pipes::parse(b)?, + true_block: Block::empty(), + false_block: Block::empty(), + parse_progress: IfParseProgress::Condition, + }; + b.spaces(); - if !b.buf.starts_with(b"if ") || b.buf.starts_with(b"if\t") { - return Err(ParseError::NotAnIf); + if b.is_completion() && b.is_empty() { + return Ok(res); + } + + res.true_block = Block::parse(b)?; + if res.true_block.finished_parsing { + res.parse_progress = IfParseProgress::Done; + } else { + res.parse_progress = IfParseProgress::TrueBlock; } - b.advance(3); - b.spaces(); - let condition = Pipes::parse(b)?; - let true_block = Block::parse(b)?; b.spaces(); + if b.is_completion() && b.is_empty() { + return Ok(res); + } - let false_block = if b.buf.starts_with(b"else") { - b.advance(4); + res.false_block = if b.consume_keyword(Keyword::Else).is_ok() { Block::parse(b)? - } else if b.buf.starts_with(b"elif") { - b.advance(2); + } else if b.consume_keyword(Keyword::Elif).is_ok() { let elif = If::parse(b)?; Block { + finished_parsing: elif.parse_progress.is_done(), commands: vec![Ast::If(elif)], } } else { Block::empty() }; + if res.false_block.finished_parsing { + res.parse_progress = IfParseProgress::Done; + } else { + res.parse_progress = IfParseProgress::FalseBlock; + } - Ok(If { - condition, - true_block, - false_block, - - // TODO: for completion one should allow more lenient parsing and set this appropriately then - parse_progress: IfParseProgress::Done, - }) + Ok(res) } } -- cgit v1.2.3