From be5bebf25bc965375b80516de53d2840fecd3be7 Mon Sep 17 00:00:00 2001 From: Jordan Orelli Date: Mon, 5 Feb 2024 19:12:51 -0600 Subject: [PATCH] adding cd --- src/builtin.rs | 14 ++++++++++- src/builtin/cd.rs | 25 +++++++++++++++++++ src/{ext => builtin}/tail.rs | 35 ++++++++------------------ src/error.rs | 3 +++ src/ext.rs | 2 -- src/lex.rs | 48 ++++++++++++++++++++++-------------- src/run.rs | 2 +- src/topo.rs | 5 +++- 8 files changed, 85 insertions(+), 49 deletions(-) create mode 100644 src/builtin/cd.rs rename src/{ext => builtin}/tail.rs (59%) diff --git a/src/builtin.rs b/src/builtin.rs index 65451fa..9cf6171 100644 --- a/src/builtin.rs +++ b/src/builtin.rs @@ -1,5 +1,7 @@ +mod cd; mod echo; mod printenv; +mod tail; mod which; use crate::{ @@ -10,8 +12,10 @@ use std::collections::HashMap; #[derive(Clone, Copy)] pub enum Builtin { + Changedir, Echo, Printenv, + Tail, Which, } @@ -19,8 +23,10 @@ impl Builtin { fn resolve(&self) -> &dyn Call { use Builtin::*; match self { + Changedir => &cd::Changedir, Echo => &echo::Echo, Printenv => &printenv::Printenv, + Tail => &tail::Tail, Which => &which::Which, } } @@ -34,5 +40,11 @@ impl Call for Builtin { pub fn all() -> HashMap<&'static str, Builtin> { use Builtin::*; - HashMap::from([("echo", Echo), ("printenv", Printenv), ("which", Which)]) + HashMap::from([ + ("cd", Changedir), + ("echo", Echo), + ("printenv", Printenv), + ("tail", Tail), + ("which", Which), + ]) } diff --git a/src/builtin/cd.rs b/src/builtin/cd.rs new file mode 100644 index 0000000..e1f0406 --- /dev/null +++ b/src/builtin/cd.rs @@ -0,0 +1,25 @@ +use crate::{ + error::ExecError, + run::{Call, Context, Value}, +}; +use std::{env, io::Write, os, process}; + +pub struct Changedir; + +impl Call for Changedir { + fn call(&self, ctx: &mut Context, args: &[Value]) -> Result { + match args.len() { + 0 => { + todo!() + } + 1 => { + let name = args[0].try_as_str()?; + env::set_current_dir(name).map_err(|e| ExecError::Misc(e.to_string()))?; + } + _ => { + todo!() + } + } + Ok(Value::None) + } +} diff --git a/src/ext/tail.rs b/src/builtin/tail.rs similarity index 59% rename from src/ext/tail.rs rename to src/builtin/tail.rs index 03b0b44..1975421 100644 --- a/src/ext/tail.rs +++ b/src/builtin/tail.rs @@ -1,30 +1,18 @@ use crate::{ - ext::Command, - output, + error::ExecError, + run::{Call, Context, Value}, }; - use std::{ fs::File, - io::{Seek, SeekFrom, Read, Write}, + io::{Read, Seek, SeekFrom, Write}, }; -use anyhow::Result; - -pub struct Tail { } - -impl Command for Tail { - fn name() -> String { - String::from("tail") - } - - fn create() -> Self { - Self{ } - } +pub struct Tail; - fn exec(&mut self, args: Vec<&str>) -> Result { - let mut stdout = output::Writer::stdout()?; +impl Call for Tail { + fn call(&self, ctx: &mut Context, args: &[Value]) -> Result { if args.len() > 0 { - let fname = args[0]; + let fname = args[0].try_as_str()?; match File::options().read(true).open(fname) { Ok(mut f) => { _ = f.seek(SeekFrom::End(0)); @@ -36,7 +24,7 @@ impl Command for Tail { if n == 1 { buf.push(one_byte[0]); if let Ok(s) = std::str::from_utf8(&buf) { - stdout.write(s.as_bytes())?; + _ = ctx.stdout.write(s.as_bytes()); buf.clear(); } } @@ -46,13 +34,10 @@ impl Command for Tail { } } Err(e) => { - println!("failed to open file: {}", e); - return Err(e.into()); + write!(ctx.stderr, "failed to open file: {}", e); } } - } else { - println!("need a file name"); - return Ok(false); } + Ok(Value::None) } } diff --git a/src/error.rs b/src/error.rs index e256a31..950c473 100644 --- a/src/error.rs +++ b/src/error.rs @@ -82,6 +82,9 @@ pub enum ExecError { #[error("value type error: {0}")] ValueTypeError(String), + + #[error("error")] + Misc(String), } impl ExecError { diff --git a/src/ext.rs b/src/ext.rs index 5fde9ac..d62d87a 100644 --- a/src/ext.rs +++ b/src/ext.rs @@ -1,8 +1,6 @@ mod command; -mod tail; pub use command::Command; -pub use tail::Tail; /* Posix Shell builtins: diff --git a/src/lex.rs b/src/lex.rs index d7b5121..a885461 100644 --- a/src/lex.rs +++ b/src/lex.rs @@ -78,12 +78,26 @@ impl Token { fn same(&self, other: &Self) -> bool { use Token::*; + println!( + "check same self: {self:?} other: {other:?}", + self = self.text(), + other = other.text() + ); + match (self, other) { (Word(a), Word(b)) => a.text() == b.text(), (Glob(a), Glob(b)) => a.text() == b.text(), _ => false, } } + + fn text(&self) -> String { + use Token::*; + + match self { + Word(lexeme) | Glob(lexeme) => lexeme.text(), + } + } } /// Tokenizer splits some input [Glyphs] into [Token] values @@ -107,10 +121,10 @@ 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])), - '\\' => match self.source.pop() { - Ok(escaped) => Some(self.lex_bare_string(vec![escaped])), - Err(e) => Some(Err(e)), - }, + // '\\' => match self.source.pop() { + // Ok(escaped) => Some(self.lex_bare_string(vec![escaped])), + // Err(e) => Some(Err(e)), + // }, '@' => Some(self.lex_var(vec![next])), '\'' => Some(self.lex_raw_string(vec![next])), '"' => Some(self.lex_interp_string(vec![next])), @@ -127,10 +141,10 @@ impl<'text> Tokenizer<'text> { progress.push(self.source.pop()?); return self.lex_glob(progress); } - '\\' => { - self.source.pop()?; - progress.push(self.source.pop()?); - } + // '\\' => { + // self.source.pop()?; + // progress.push(self.source.pop()?); + // } _ => return Err(LexError::UnexpectedCharacter(self.source.pop()?)), } } @@ -147,10 +161,10 @@ impl<'text> Tokenizer<'text> { match next.glyph { _ if next.glyph.is_whitespace() => break, _ if next.is_glob() => progress.push(self.source.pop()?), - '\\' => { - self.source.pop()?; - progress.push(self.source.pop()?); - } + // '\\' => { + // self.source.pop()?; + // progress.push(self.source.pop()?); + // } _ => return Err(LexError::UnexpectedCharacter(self.source.pop()?)), } } @@ -271,6 +285,7 @@ mod tests { assert_eq!(expected.len(), lexed.len()); for pair in zip(expected, lexed) { + println!("pair: {pair:?}"); assert!(pair.0.same(&pair.1)); } @@ -295,13 +310,6 @@ mod tests { } reject! { - // A slash on its own makes no sense - lonely_slash: r"\"; - - // A slash is an escape character, so starting the escape sequence and then ending the - // input makes no sense - trailing_slash: r"one two three \"; - // Vars aren't done yet var: "@name"; @@ -332,5 +340,7 @@ mod tests { glob_4 "*x" [ glob("*x") ] glob_5 "*.py" [ glob("*.py") ] mixed_1 "ls *.py" [ word("ls") glob("*.py") ] + abs "cd c:\\one\\two" [ word("cd") word("c:\\one\\two")] + home "cd ~\\stuff" [ word("cd") word("~\\stuff") ] } } diff --git a/src/run.rs b/src/run.rs index 37e49cd..3c2391e 100644 --- a/src/run.rs +++ b/src/run.rs @@ -1,4 +1,4 @@ -use crate::{builtin, error::ExecError, input, output}; +use crate::{builtin, error::ExecError, output}; use std::{collections::HashMap, process}; /// Eval represents anything that can be evaluated at runtime. diff --git a/src/topo.rs b/src/topo.rs index 3471077..a824998 100644 --- a/src/topo.rs +++ b/src/topo.rs @@ -65,7 +65,10 @@ pub struct Glyph { impl Glyph { /// checks to see whether or not the character for this glyph is a word character pub fn is_word(&self) -> bool { - self.glyph.is_alphanumeric() || self.glyph == '.' + match self.glyph { + '-' | '/' | '\\' | '.' | ':' | '~' => true, + _ => self.glyph.is_alphanumeric(), + } } /// checks to see whether or not the character for this glyph is a glob character