diff options
| author | Jonas Maier <> | 2026-05-22 17:44:24 +0200 |
|---|---|---|
| committer | Jonas Maier <> | 2026-05-22 17:44:24 +0200 |
| commit | 07daff9331dbdc607584edbf1a8fb3e415c338ea (patch) | |
| tree | 4742c87aba7748652bd8ea9da7be01607d4d1555 /src | |
| parent | 9a727206f1c94f7580e79a5355adc4b932ae6345 (diff) | |
| download | pish-07daff9331dbdc607584edbf1a8fb3e415c338ea.tar.gz | |
lenient block parsing for completion
Diffstat (limited to 'src')
| -rw-r--r-- | src/parse/mod.rs | 85 | ||||
| -rw-r--r-- | src/run/builtin.rs | 34 |
2 files changed, 80 insertions, 39 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(); diff --git a/src/run/builtin.rs b/src/run/builtin.rs index 488ee04..638cfcd 100644 --- a/src/run/builtin.rs +++ b/src/run/builtin.rs @@ -6,7 +6,6 @@ use std::{env::*, fs::OpenOptions, path::PathBuf}; use pish_derive::FromArgs; use super::{Builtin, BuiltinError as Error, BuiltinResult as Result}; -use crate::parse::CmdDisplay; use crate::run::{AliasAge, AliasBody}; use crate::*; @@ -354,16 +353,39 @@ impl Builtin for parse { _stdin: &mut dyn Read, stdout: &mut dyn Write, ) -> Result { + use crate::parse::*; + + let mut parse_mode = ParseMode::Command; + let mut backtrace = false; + for arg in args { - match crate::parse::do_parse(arg) { + match &arg[..] { + b"-c" | b"--completion" => { + parse_mode = ParseMode::Completion; + continue; + } + b"-e" | b"--execution" => { + parse_mode = ParseMode::Command; + continue; + } + b"--bt" | b"--backtrace" => { + backtrace = !backtrace; + } + _ => {} + } + + let mut c = crate::parse::Cursor::new(arg, parse_mode); + c.backtrace = backtrace; + + match Ast::parse(&mut c) { Ok(parsed) => { write!(stdout, "ok ")?; parsed.cdisplay(stdout)?; - writeln!(stdout)?; + writeln!(stdout, "\n{parsed:?}")?; } - Err(err) => { - writeln!(stdout, "err {:?} ", err.0)?; - stdout.write_all(&err.1)?; + Err(e) => { + writeln!(stdout, "err {e:?} ")?; + stdout.write_all(&c.remaining())?; } } } |
