aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/parse/mod.rs122
-rw-r--r--src/run/mod.rs2
2 files changed, 101 insertions, 23 deletions
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<Ast<PreExpansion>>,
+ pub finished_parsing: bool,
}
#[derive(Debug, Clone, PartialEq)]
@@ -84,7 +85,6 @@ pub enum Ast<T: Stage> {
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<T: Stage> {
pub condition: Pipes<T>,
@@ -206,6 +212,7 @@ pub fn cmd<const N: usize>(x: [ExpString; N]) -> Command<PreExpansion> {
pub fn block<const N: usize>(x: [Ast<PreExpansion>; 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<T> = std::result::Result<T, ParseError>;
@@ -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<T: Parse>(&mut self) -> Result<T> {
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<PreExpansion> {
fn parse(b: &mut Cursor<'_>) -> Result<Self> {
+ 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)
}
}
diff --git a/src/run/mod.rs b/src/run/mod.rs
index 52d3925..f34b678 100644
--- a/src/run/mod.rs
+++ b/src/run/mod.rs
@@ -451,7 +451,7 @@ impl Executor {
stdin: InputReader,
stdout: OutputWriter,
) -> SpawnedCmd {
- self.execute_block(parse::Block { commands: s.stmts }, stdin, stdout)
+ self.execute_block(parse::Block { commands: s.stmts, finished_parsing: true }, stdin, stdout)
}
}