diff options
| -rw-r--r-- | src/main.rs | 2 | ||||
| -rw-r--r-- | src/parse.rs | 132 | ||||
| -rw-r--r-- | src/run/mod.rs | 61 |
3 files changed, 108 insertions, 87 deletions
diff --git a/src/main.rs b/src/main.rs index 8a6e84e..c66ec67 100644 --- a/src/main.rs +++ b/src/main.rs @@ -100,7 +100,7 @@ impl Session { } fn prompt(&self) -> String { - format!("{} $ ", self.pretty_cwd()) + format!("[{}]# ", self.pretty_cwd()) } fn clear_prompt(&mut self) { diff --git a/src/parse.rs b/src/parse.rs index 2ed0dbf..b2433a8 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -106,7 +106,7 @@ impl ExpString { fn is_symbol(x: u8) -> bool { match x { - b'|' | b'{' | b'}' | b'$' => true, + b'|' | b'{' | b'}' | b'$' | b'(' | b')' | b'\'' | b'"' => true, _ => false, } } @@ -152,14 +152,16 @@ impl Parse for ExpString { fn parse(b: &mut Cursor<'_>) -> Result<Self> { b.spaces(); if b.is_empty() { - return Err(ParseError::Eof); + return Err(ParseError::NotAString); } let mut delim = b.peek(); - if delim != b'\'' && delim != b'"' { - delim = b' '; - } else { + if delim == b'\'' || delim == b'"' { b.adv(); + } else if is_symbol(delim) && delim != b'$' { + return Err(ParseError::NotAString); + } else { + delim = b' '; } let mut parts = Vec::new(); @@ -181,13 +183,17 @@ impl Parse for ExpString { continue; } - if x == delim { + if x == delim || (b.peek_space() && delim == b' ') { if delim != b' ' { b.adv(); } return Ok(Self { parts }); } + if delim == b' ' && is_symbol(x) && x != b'$' { + return Ok(Self { parts }); + } + b.adv(); if delim == b'\'' { @@ -231,7 +237,16 @@ impl Parse for ExpString { b.adv(); p.push(StringPart::Var(v)); } else if x == b'(' { - todo!() + b.adv(); + let cmd = Ast::parse(b)?; + b.spaces(); + if b.is_empty() { + return Err(ParseError::Eof); + } else if b.peek() == b')' { + p.push(StringPart::Cmd(cmd)); + } else { + return Err(ParseError::Expected(')')); + } } else { // doesn't seem to be a variable or expansion, just add $ back into the string add_char(p, b'$'); @@ -241,10 +256,6 @@ impl Parse for ExpString { continue; } - if delim == b' ' && is_symbol(x) { - return Ok(Self { parts }); - } - add_char(p, x); } @@ -286,12 +297,20 @@ pub enum ParseError { ExpectedAlphabetic, Unknown(u8), + + Expected(char), + + NotAString, } type Result<T> = std::result::Result<T, ParseError>; -pub fn do_parse(x: &[u8]) -> Result<Ast<PreExpansion>> { - Ast::parse(&mut Cursor::new(x, ParseMode::Command)) +pub fn do_parse(x: &[u8]) -> Res<Ast<PreExpansion>, (ParseError, &[u8])> { + let mut c = Cursor::new(x, ParseMode::Command); + match Ast::parse(&mut c) { + Ok(ast) => Ok(ast), + Err(e) => Err((e, c.buf)), + } } pub enum CompletionKind { @@ -359,6 +378,8 @@ struct Cursor<'a> { /// if the last byte that was consumed was whitespace or part of a word spaced: bool, + + backtrace: bool, } impl<'a> Cursor<'a> { @@ -367,6 +388,7 @@ impl<'a> Cursor<'a> { buf, mode, spaced: false, + backtrace: false, } } @@ -379,19 +401,40 @@ impl<'a> Cursor<'a> { self.buf.is_empty() } + fn bt(&self, word: &str) { + 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) { + println!("{l}\r"); + } + println!("\r"); + } + } + fn peek(&self) -> u8 { + self.bt("peek"); self.buf[0] } fn adv(&mut self) -> u8 { + self.bt("adv"); let out = self.buf[0]; self.buf = &self.buf[1..]; self.spaced = false; out } + fn peek_space(&self) -> bool { + if self.buf.is_empty() { + return false; + } + matches!(self.buf[0], b' ' | b'\t' | b'\n' | b'\r') + } + fn spaces(&mut self) { - while let Some(b' ' | b'\t') = self.buf.first() { + while self.peek_space() { self.adv(); self.spaced = true; } @@ -409,57 +452,6 @@ impl<'a> Cursor<'a> { } } -fn parse_quoted_string(b: &mut Cursor<'_>, delim: u8) -> Result<Vec<u8>> { - // TODO: escape sequence stuff - - let mut s = Vec::new(); - while b.has() { - if delim == b' ' && b.peek() == b'|' { - return if s.len() == 0 { - Err(ParseError::UnexpectedPipe) - } else { - Ok(s) - }; - } - - if b.peek() == delim { - b.adv(); - if delim == b' ' { - b.spaced = true; - } - return Ok(s); - } - - s.push(b.adv()); - } - - if delim == b' ' || b.is_completion() { - Ok(s) - } else { - Err(ParseError::Incomplete) - } -} - -impl Parse for Vec<u8> { - fn parse(b: &mut Cursor<'_>) -> Result<Self> { - b.spaces(); - if b.is_empty() { - return Err(ParseError::Eof); - } - let c = b.peek(); - if c == b'|' { - Err(ParseError::UnexpectedPipe) - } else if c == b'\'' || c == b'"' { - b.adv(); - parse_quoted_string(b, c) - } else if c.is_ascii_graphic() { - parse_quoted_string(b, b' ') - } else { - Err(ParseError::Unknown(c)) - } - } -} - impl Parse for Ast<PreExpansion> { fn parse(b: &mut Cursor<'_>) -> Result<Self> { Ok(Self::Pipes(b.parse()?)) @@ -471,14 +463,14 @@ impl Parse for Command<PreExpansion> { let path: ExpString = b.parse()?; let mut args = Vec::new(); loop { - let arg: Result<ExpString> = b.parse(); - match arg { + match ExpString::parse(b) { Ok(arg) => args.push(arg), - Err(ParseError::Eof | ParseError::UnexpectedPipe) => break, + Err(ParseError::NotAString) => break, Err(e) => Err(e)?, } } - Ok(Self { cmd: path, args }) + let x = Ok(Self { cmd: path, args }); + x } } @@ -496,6 +488,8 @@ impl Parse for Pipes<PreExpansion> { if c == b'|' { b.adv(); cmds.push(b.parse()?); + } else if is_symbol(c) { + return Ok(Pipes { cmds }); } else { Err(ParseError::Unknown(c))?; } diff --git a/src/run/mod.rs b/src/run/mod.rs index 40f4927..b45fac8 100644 --- a/src/run/mod.rs +++ b/src/run/mod.rs @@ -47,26 +47,25 @@ impl<'a> Executor<'a> { fn execute( &mut self, ast: Ast<parse::PostExpansion>, - stdin: Box<dyn io::Read + Send>, - stdout: Box<dyn io::Write + Send>, + capture: Option<&mut Vec<u8>>, ) -> Result<(), ExecError> { let Ast::Pipes(pipes) = ast else { todo!("can only handle pipes"); }; - let mut stdin = Some(stdin); - let mut stdout = Some(stdout); + //print!("exec {}", format!("{pipes:?}\n").replace("\n", "\r\n")); let mut children = Vec::new(); let mut threads = Vec::new(); let mut prev_reader = None; let mut spawn_error = false; + let mut last_output = ArcWriter::new(); + let mut last_is_command = false; + let pipelen = pipes.cmds.len(); for (i, cmd) in pipes.cmds.into_iter().enumerate() { - let last = i == pipelen - 1; - - let (reader, writer) = if !last { + let (reader, writer) = if i < pipelen - 1 { let (r, w) = io::pipe().unwrap(); (Some(r), Some(w)) } else { @@ -77,6 +76,8 @@ impl<'a> Executor<'a> { match dc { CommandKind::Path(path) => { + last_is_command = true; + let mut command = Command::new(&path); for arg in cmd.args.iter() { command.arg(OsStr::from_bytes(arg)); @@ -84,10 +85,14 @@ impl<'a> Executor<'a> { if let Some(r) = prev_reader.take() { command.stdin(Stdio::from(r)); + } else if capture.is_some() { + command.stdin(Stdio::null()); } if let Some(w) = writer { command.stdout(Stdio::from(w)); + } else if capture.is_some() { + command.stdout(Stdio::piped()); } let child = match command.spawn() { @@ -124,16 +129,20 @@ impl<'a> Executor<'a> { } CommandKind::Builtin(builtin) => { + last_is_command = false; + builtin.mod_session(&mut self.se, &cmd.args); let mut input: Box<dyn io::Read + Send> = match prev_reader.take() { Some(r) => Box::new(r), - None => stdin.take().unwrap(), + None if capture.is_some() => Box::new(io::empty()), + None => Box::new(io::stdin()), }; let mut output: Box<dyn io::Write + Send> = match writer { Some(w) => Box::new(w), - None => stdout.take().unwrap(), + None if capture.is_some() => Box::new(last_output.clone()), + None => Box::new(io::stdout()), }; let handle = @@ -159,7 +168,7 @@ impl<'a> Executor<'a> { // TODO do not ignore panics let _ = jh.join(); } - for mut child in children { + for child in children.iter_mut() { match child.wait() { Ok(ec) => { if let Some(c) = ec.code() { @@ -171,6 +180,22 @@ impl<'a> Executor<'a> { } } } + + if let Some(cap) = capture { + if last_is_command { + let child = children + .into_iter() + .last() + .unwrap(); + let out = child + .wait_with_output() + .unwrap(); + *cap = out.stdout; + } else { + *cap = last_output.into_inner(); + } + } + if code == 0 { Ok(()) } else { @@ -191,17 +216,16 @@ impl<'a> parse::Expander for Executor<'a> { } fn expand_cmd(&mut self, ast: Ast<parse::PostExpansion>) -> Result<BString, Self::Error> { - let stdout = Box::new(ArcWriter::new()); - let stdin = Box::new(std::io::empty()); - self.execute(ast, stdin, stdout.clone())?; - Ok(stdout.into_inner()) + let mut out = Vec::new(); + self.execute(ast, Some(&mut out))?; + Ok(out) } } fn exec(se: &mut Session, ast: Ast<PreExpansion>) -> Result<(), ExecError> { let mut exec = Executor { se }; let ast = ast.expand(&mut exec)?; - exec.execute(ast, Box::new(io::stdin()), Box::new(io::stdout())) + exec.execute(ast, None) } pub fn run(se: &mut Session, cmd: Vec<u8>) { @@ -209,8 +233,11 @@ pub fn run(se: &mut Session, cmd: Vec<u8>) { let parsed = match parsed { Ok(p) => p, Err(err) => { - println!("{cmd:?}"); - print!("{err:?}\r\n{}", se.prompt()); + se.raw.disable(); + println!("{:?}: {}", err.0, String::from_utf8_lossy(&err.1)); + print!("{}", se.prompt()); + std::io::stdout().lock().flush().unwrap(); + se.raw.enable(); return; } }; |
