ls and pwd

main
Jordan Orelli 9 months ago
parent d25382ee10
commit e71c3f89f7

@ -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),

@ -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<Value, ExecError> {
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)
}
}

@ -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<Value, ExecError> {
let cwd = env::current_dir()?;
_ = write!(ctx.stdout, "{}\n", cwd.display());
Ok(Value::None)
}
}

@ -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),

@ -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()?,

@ -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<bool, ParseError> {
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<bool, ParseError> {
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));

@ -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);
}

Loading…
Cancel
Save