adding cd

main
Jordan Orelli 11 months ago
parent 678b5f7932
commit be5bebf25b

@ -1,5 +1,7 @@
mod cd;
mod echo; mod echo;
mod printenv; mod printenv;
mod tail;
mod which; mod which;
use crate::{ use crate::{
@ -10,8 +12,10 @@ use std::collections::HashMap;
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub enum Builtin { pub enum Builtin {
Changedir,
Echo, Echo,
Printenv, Printenv,
Tail,
Which, Which,
} }
@ -19,8 +23,10 @@ impl Builtin {
fn resolve(&self) -> &dyn Call { fn resolve(&self) -> &dyn Call {
use Builtin::*; use Builtin::*;
match self { match self {
Changedir => &cd::Changedir,
Echo => &echo::Echo, Echo => &echo::Echo,
Printenv => &printenv::Printenv, Printenv => &printenv::Printenv,
Tail => &tail::Tail,
Which => &which::Which, Which => &which::Which,
} }
} }
@ -34,5 +40,11 @@ impl Call for Builtin {
pub fn all() -> HashMap<&'static str, Builtin> { pub fn all() -> HashMap<&'static str, Builtin> {
use Builtin::*; use Builtin::*;
HashMap::from([("echo", Echo), ("printenv", Printenv), ("which", Which)]) HashMap::from([
("cd", Changedir),
("echo", Echo),
("printenv", Printenv),
("tail", Tail),
("which", Which),
])
} }

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

@ -1,30 +1,18 @@
use crate::{ use crate::{
ext::Command, error::ExecError,
output, run::{Call, Context, Value},
}; };
use std::{ use std::{
fs::File, fs::File,
io::{Seek, SeekFrom, Read, Write}, io::{Read, Seek, SeekFrom, Write},
}; };
use anyhow::Result; pub struct Tail;
pub struct Tail { }
impl Command for Tail {
fn name() -> String {
String::from("tail")
}
fn create() -> Self {
Self{ }
}
fn exec(&mut self, args: Vec<&str>) -> Result<bool> { impl Call for Tail {
let mut stdout = output::Writer::stdout()?; fn call(&self, ctx: &mut Context, args: &[Value]) -> Result<Value, ExecError> {
if args.len() > 0 { if args.len() > 0 {
let fname = args[0]; let fname = args[0].try_as_str()?;
match File::options().read(true).open(fname) { match File::options().read(true).open(fname) {
Ok(mut f) => { Ok(mut f) => {
_ = f.seek(SeekFrom::End(0)); _ = f.seek(SeekFrom::End(0));
@ -36,7 +24,7 @@ impl Command for Tail {
if n == 1 { if n == 1 {
buf.push(one_byte[0]); buf.push(one_byte[0]);
if let Ok(s) = std::str::from_utf8(&buf) { if let Ok(s) = std::str::from_utf8(&buf) {
stdout.write(s.as_bytes())?; _ = ctx.stdout.write(s.as_bytes());
buf.clear(); buf.clear();
} }
} }
@ -46,13 +34,10 @@ impl Command for Tail {
} }
} }
Err(e) => { Err(e) => {
println!("failed to open file: {}", e); write!(ctx.stderr, "failed to open file: {}", e);
return Err(e.into());
} }
} }
} else {
println!("need a file name");
return Ok(false);
} }
Ok(Value::None)
} }
} }

@ -82,6 +82,9 @@ pub enum ExecError {
#[error("value type error: {0}")] #[error("value type error: {0}")]
ValueTypeError(String), ValueTypeError(String),
#[error("error")]
Misc(String),
} }
impl ExecError { impl ExecError {

@ -1,8 +1,6 @@
mod command; mod command;
mod tail;
pub use command::Command; pub use command::Command;
pub use tail::Tail;
/* /*
Posix Shell builtins: Posix Shell builtins:

@ -78,12 +78,26 @@ impl Token {
fn same(&self, other: &Self) -> bool { fn same(&self, other: &Self) -> bool {
use Token::*; use Token::*;
println!(
"check same self: {self:?} other: {other:?}",
self = self.text(),
other = other.text()
);
match (self, other) { match (self, other) {
(Word(a), Word(b)) => a.text() == b.text(), (Word(a), Word(b)) => a.text() == b.text(),
(Glob(a), Glob(b)) => a.text() == b.text(), (Glob(a), Glob(b)) => a.text() == b.text(),
_ => false, _ => false,
} }
} }
fn text(&self) -> String {
use Token::*;
match self {
Word(lexeme) | Glob(lexeme) => lexeme.text(),
}
}
} }
/// Tokenizer splits some input [Glyphs] into [Token] values /// Tokenizer splits some input [Glyphs] into [Token] values
@ -107,10 +121,10 @@ impl<'text> Tokenizer<'text> {
match next.glyph { match next.glyph {
_ if next.is_word() => Some(self.lex_bare_string(vec![next])), _ if next.is_word() => Some(self.lex_bare_string(vec![next])),
_ if next.is_glob() => Some(self.lex_glob(vec![next])), _ if next.is_glob() => Some(self.lex_glob(vec![next])),
'\\' => match self.source.pop() { // '\\' => match self.source.pop() {
Ok(escaped) => Some(self.lex_bare_string(vec![escaped])), // Ok(escaped) => Some(self.lex_bare_string(vec![escaped])),
Err(e) => Some(Err(e)), // Err(e) => Some(Err(e)),
}, // },
'@' => Some(self.lex_var(vec![next])), '@' => Some(self.lex_var(vec![next])),
'\'' => Some(self.lex_raw_string(vec![next])), '\'' => Some(self.lex_raw_string(vec![next])),
'"' => Some(self.lex_interp_string(vec![next])), '"' => Some(self.lex_interp_string(vec![next])),
@ -127,10 +141,10 @@ impl<'text> Tokenizer<'text> {
progress.push(self.source.pop()?); progress.push(self.source.pop()?);
return self.lex_glob(progress); return self.lex_glob(progress);
} }
'\\' => { // '\\' => {
self.source.pop()?; // self.source.pop()?;
progress.push(self.source.pop()?); // progress.push(self.source.pop()?);
} // }
_ => return Err(LexError::UnexpectedCharacter(self.source.pop()?)), _ => return Err(LexError::UnexpectedCharacter(self.source.pop()?)),
} }
} }
@ -147,10 +161,10 @@ impl<'text> Tokenizer<'text> {
match next.glyph { match next.glyph {
_ if next.glyph.is_whitespace() => break, _ if next.glyph.is_whitespace() => break,
_ if next.is_glob() => progress.push(self.source.pop()?), _ if next.is_glob() => progress.push(self.source.pop()?),
'\\' => { // '\\' => {
self.source.pop()?; // self.source.pop()?;
progress.push(self.source.pop()?); // progress.push(self.source.pop()?);
} // }
_ => return Err(LexError::UnexpectedCharacter(self.source.pop()?)), _ => return Err(LexError::UnexpectedCharacter(self.source.pop()?)),
} }
} }
@ -271,6 +285,7 @@ mod tests {
assert_eq!(expected.len(), lexed.len()); assert_eq!(expected.len(), lexed.len());
for pair in zip(expected, lexed) { for pair in zip(expected, lexed) {
println!("pair: {pair:?}");
assert!(pair.0.same(&pair.1)); assert!(pair.0.same(&pair.1));
} }
@ -295,13 +310,6 @@ mod tests {
} }
reject! { 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 // Vars aren't done yet
var: "@name"; var: "@name";
@ -332,5 +340,7 @@ mod tests {
glob_4 "*x" [ glob("*x") ] glob_4 "*x" [ glob("*x") ]
glob_5 "*.py" [ glob("*.py") ] glob_5 "*.py" [ glob("*.py") ]
mixed_1 "ls *.py" [ word("ls") 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") ]
} }
} }

@ -1,4 +1,4 @@
use crate::{builtin, error::ExecError, input, output}; use crate::{builtin, error::ExecError, output};
use std::{collections::HashMap, process}; use std::{collections::HashMap, process};
/// Eval represents anything that can be evaluated at runtime. /// Eval represents anything that can be evaluated at runtime.

@ -65,7 +65,10 @@ pub struct Glyph {
impl Glyph { impl Glyph {
/// checks to see whether or not the character for this glyph is a word character /// checks to see whether or not the character for this glyph is a word character
pub fn is_word(&self) -> bool { 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 /// checks to see whether or not the character for this glyph is a glob character

Loading…
Cancel
Save