aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main.rs2
-rw-r--r--src/parse.rs132
-rw-r--r--src/run/mod.rs61
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;
}
};