adding cd

main
Jordan Orelli 7 months ago
parent 678b5f7932
commit be5bebf25b

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

@ -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::{
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<bool> {
let mut stdout = output::Writer::stdout()?;
impl Call for Tail {
fn call(&self, ctx: &mut Context, args: &[Value]) -> Result<Value, ExecError> {
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)
}
}

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

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

@ -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") ]
}
}

@ -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.

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

Loading…
Cancel
Save