diff --git a/src/builtin.rs b/src/builtin.rs index 16e3039..557925c 100644 --- a/src/builtin.rs +++ b/src/builtin.rs @@ -1,12 +1,16 @@ /// changes directory mod cd; +mod ls; + /// prints stuff mod echo; /// prints environment variables mod printenv; +mod pwd; + /// removes files mod rm; @@ -27,7 +31,9 @@ use std::collections::HashMap; pub enum Builtin { Changedir, Echo, + Ls, Printenv, + Pwd, Rm, Tail, Which, @@ -39,7 +45,9 @@ impl Builtin { match self { Changedir => &cd::Changedir, Echo => &echo::Echo, + Ls => &ls::Ls, Printenv => &printenv::Printenv, + Pwd => &pwd::Pwd, Rm => &rm::Rm, Tail => &tail::Tail, Which => &which::Which, @@ -59,7 +67,9 @@ pub fn all() -> HashMap<&'static str, Builtin> { HashMap::from([ ("cd", Changedir), ("echo", Echo), + ("ls", Ls), ("printenv", Printenv), + ("pwd", Pwd), ("rm", Rm), ("tail", Tail), ("which", Which), diff --git a/src/builtin/ls.rs b/src/builtin/ls.rs new file mode 100644 index 0000000..cd3725d --- /dev/null +++ b/src/builtin/ls.rs @@ -0,0 +1,19 @@ +use crate::{ + error::ExecError, + run::{Call, Context, Value}, +}; +use std::{env, fs, io::Write}; + +pub struct Ls; + +impl Call for Ls { + fn call(&self, ctx: &mut Context, args: &[Value]) -> Result { + let cwd = env::current_dir()?; + let dir = fs::read_dir(&cwd)?; + for child in dir { + let child = child?; + _ = write!(ctx.stdout, "{}\n", child.path().display()); + } + Ok(Value::None) + } +} diff --git a/src/builtin/pwd.rs b/src/builtin/pwd.rs new file mode 100644 index 0000000..bd198fb --- /dev/null +++ b/src/builtin/pwd.rs @@ -0,0 +1,15 @@ +use crate::{ + error::ExecError, + run::{Call, Context, Value}, +}; +use std::{env, io::Write}; + +pub struct Pwd; + +impl Call for Pwd { + fn call(&self, ctx: &mut Context, args: &[Value]) -> Result { + let cwd = env::current_dir()?; + _ = write!(ctx.stdout, "{}\n", cwd.display()); + Ok(Value::None) + } +} diff --git a/src/error.rs b/src/error.rs index 35716a5..e1ce4dc 100644 --- a/src/error.rs +++ b/src/error.rs @@ -112,6 +112,9 @@ pub enum ExecError { source: io::Error, }, + #[error("i/o error")] + IOError(#[from] std::io::Error), + #[error("value type error: {0}")] ValueTypeError(String), diff --git a/src/lex.rs b/src/lex.rs index 654fe97..4da9c32 100644 --- a/src/lex.rs +++ b/src/lex.rs @@ -72,6 +72,8 @@ pub enum Token { Glob(Lexeme), Semi(Glyph), + + Pipe(Glyph), } impl Token { @@ -100,7 +102,7 @@ impl Token { match self { Word(lexeme) | Glob(lexeme) => lexeme.text(), - Semi(glyph) => String::from(glyph.glyph), + Pipe(glyph) | Semi(glyph) => String::from(glyph.glyph), } } } @@ -127,6 +129,7 @@ impl<'text> Tokenizer<'text> { match next.glyph { _ if next.is_word() => Some(self.lex_bare_string(vec![next])), _ if next.is_glob() => Some(self.lex_glob(vec![next])), + '|' => Some(Ok(Token::Pipe(next))), '@' => Some(self.lex_var(vec![next])), '\'' => Some(self.lex_raw_string(vec![next])), '"' => Some(self.lex_string_literal(vec![])), @@ -144,7 +147,7 @@ impl<'text> Tokenizer<'text> { progress.push(self.source.pop()?); return self.lex_glob(progress); } - ';' => break, + ';' | '|' => break, _ => { return Err(LexError::UnexpectedCharacterInBareString( self.source.pop()?, diff --git a/src/parse.rs b/src/parse.rs index 52104e7..0e40fd9 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -16,7 +16,7 @@ pub enum Value { /// The children of a statement symbol make up the components of what will become a statement /// in our ast - Statement, + Command, /// Each of the tokens from the lex stage becomes a terminal node on our tree Terminal(Token), @@ -220,7 +220,7 @@ impl<'text> Parser<'text> { fn step(&mut self) -> Result { match self.output.value() { Value::Start => self.step_start(), - Value::Statement => self.step_statement(), + Value::Command => self.step_statement(), Value::Terminal(_) => panic!(), } } @@ -229,7 +229,7 @@ impl<'text> Parser<'text> { assert!(matches!(self.output.value(), Value::Start)); match self.input.peek()? { Some(Token::Word(_)) => { - self.output.push(Value::Statement)?; + self.output.push(Value::Command)?; let token = self.input.next().unwrap()?; self.output.push(Value::Terminal(token))?; self.output.up()?; @@ -240,19 +240,23 @@ impl<'text> Parser<'text> { Err(ParseError::UnexpectedToken(token)) } Some(Token::Semi(_)) => { - self.output.push(Value::Statement)?; + self.output.push(Value::Command)?; let token = self.input.next().unwrap()?; self.output.push(Value::Terminal(token))?; self.output.up()?; self.output.up()?; Ok(true) } + Some(Token::Pipe(_)) => { + let token = self.input.next().unwrap()?; + Err(ParseError::UnexpectedToken(token)) + } None => Ok(false), } } fn step_statement(&mut self) -> Result { - assert!(matches!(self.output.value(), Value::Statement)); + assert!(matches!(self.output.value(), Value::Command)); match self.input.peek()? { Some(Token::Word(_) | Token::Glob(_)) => { let token = self.input.next().unwrap()?; @@ -260,6 +264,9 @@ impl<'text> Parser<'text> { self.output.up()?; Ok(true) } + Some(Token::Pipe(_)) => { + todo!() + } Some(Token::Semi(_)) => { let token = self.input.next().unwrap()?; self.output.push(Value::Terminal(token))?; @@ -272,6 +279,29 @@ impl<'text> Parser<'text> { } } +/* + +a b + command + terminal + a + terminal + b + +a | b + pipeline + command + terminal + a + terminal + | + command + terminal + b + + +*/ + #[cfg(test)] mod test { use super::*; @@ -297,7 +327,7 @@ mod test { let mut tokens = lex(" ls ").unwrap(); let ls = tokens.pop().unwrap(); - assert!(cursor.push(Value::Statement).is_ok()); + assert!(cursor.push(Value::Command).is_ok()); assert!(cursor.push(Value::Terminal(ls.clone())).is_ok()); assert!(cursor.push(Value::Terminal(ls.clone())).is_err()); assert!(cursor.target.value == Value::Terminal(ls)); diff --git a/src/syntax.rs b/src/syntax.rs index 791ea33..800ff3b 100644 --- a/src/syntax.rs +++ b/src/syntax.rs @@ -136,7 +136,7 @@ impl TreeBuilder { } Element::Block(root) } - parse::Value::Statement => { + parse::Value::Command => { let mut children = source.iter_children(); let mut first = match children.next() { Some(child) => child, @@ -175,6 +175,9 @@ impl TreeBuilder { parse::Value::Terminal(Token::Glob(_)) => { todo!() } + parse::Value::Terminal(Token::Pipe(_)) => { + todo!() + } parse::Value::Terminal(Token::Semi(_)) => { return Err(ParseError::WhyParseSemicolon); }