diff --git a/src/builtins.rs b/src/builtins.rs index 0c4090c..d2c77c5 100644 --- a/src/builtins.rs +++ b/src/builtins.rs @@ -1,3 +1,33 @@ -pub trait BuiltinFn { - fn call(&self, args: Vec<&str>); +use crate::{ + error::ExecError, + syntax::{self, State}, +}; + +pub enum Builtin { + Echo, +} + +impl Builtin { + pub fn lookup(name: &str) -> Option { + match name { + "echo" => Some(Self::Echo), + _ => None, + } + } + + pub fn call( + &self, + _: &mut State, + args: &Vec, + ) -> Result { + match self { + Builtin::Echo => { + let args: Result, ExecError> = + args.into_iter().map(|arg| arg.try_as_string()).collect(); + let args = args?; + println!("{}", args.join(" ")); + Ok(syntax::Value::None) + } + } + } } diff --git a/src/error.rs b/src/error.rs index 8347112..adecc3f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -20,7 +20,8 @@ pub enum LexError { #[error("a word character was expected but none was encountered")] ExpectedWordCharacter, - #[error("unexpected character: {0:?}")] + // #[error("unexpected character {0.glyph} at {0.position}")] + #[error("unexpected character {g} at {pos:?}", g = .0.glyph, pos = .0.position)] UnexpectedCharacter(Glyph), #[error("unexpected eof")] diff --git a/src/line.rs b/src/line.rs index b93b367..e427e62 100644 --- a/src/line.rs +++ b/src/line.rs @@ -5,7 +5,7 @@ pub struct Line { /// the current contents of the line chars: Vec, - /// the cursor position of our dit head within the vector of characters that we store as chars + /// the cursor position of our head within the vector of characters that we store as chars cursor: usize, } diff --git a/src/output.rs b/src/output.rs index 7e7276a..21d98b1 100644 --- a/src/output.rs +++ b/src/output.rs @@ -150,10 +150,16 @@ impl Writer { impl Write for Writer { fn write(&mut self, buf: &[u8]) -> io::Result { + let mut written = 0; unsafe { - Error::check(Console::WriteConsoleA(self.output, buf, None, None))?; + Error::check(Console::WriteConsoleA( + self.output, + buf, + Some(&mut written), + None, + ))?; } - Ok(0) + Ok(written as usize) } fn flush(&mut self) -> io::Result<()> { diff --git a/src/shell.rs b/src/shell.rs index 6ae420a..5ff4863 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -166,9 +166,9 @@ impl Shell { fn render_error_helper(&mut self, e: E, depth: u8) -> io::Result<()> { if depth > 0 { - println!(" {e}"); + writeln!(self.output, " {e}")?; } else { - println!("{e}:"); + writeln!(self.output, "{e}:")?; } if let Some(cause) = e.source() { self.render_error_helper(cause, depth + 1)?; diff --git a/src/syntax.rs b/src/syntax.rs index 705e844..be84476 100644 --- a/src/syntax.rs +++ b/src/syntax.rs @@ -1,5 +1,5 @@ use crate::{ - builtins::BuiltinFn, + builtins::Builtin, error::{ExecError, ParseError}, lex::{Lexer, Token}, log::debug, @@ -11,14 +11,12 @@ use std::{ }; pub struct State { - builtins: HashMap<&'static str, Box>, variables: HashMap<&'static str, syntax::Value>, } impl State { pub fn new() -> Self { Self { - builtins: HashMap::new(), variables: HashMap::new(), } } @@ -54,7 +52,7 @@ pub enum Value { } impl Eval for Value { - fn eval(&self, ctx: &mut State) -> Result { + fn eval(&self, _: &mut State) -> Result { Ok(self.clone()) } } @@ -69,6 +67,16 @@ impl Value { )), } } + + pub fn try_as_string(&self) -> Result { + match self { + Value::None => Err(ExecError::type_error("expected text value, saw None value")), + Value::Text(v) => Ok(v.clone()), + Value::ExitStatus(_) => Err(ExecError::type_error( + "expected text value, saw ExitStatus value", + )), + } + } } #[derive(Debug)] @@ -100,9 +108,12 @@ pub struct Command { args: Vec, } -impl Eval for Command { - fn eval(&self, ctx: &mut State) -> Result { - let name = self.name.eval(ctx)?.try_to_string()?; +impl Command { + fn eval_args(&self, ctx: &mut State) -> Result, ExecError> { + self.args.iter().map(|elem| elem.eval(ctx)).collect() + } + + fn exec_command(&self, ctx: &mut State, name: &str) -> Result { let mut proc = process::Command::new(&name); if self.args.len() > 0 { @@ -114,16 +125,32 @@ impl Eval for Command { proc.args(args); } let mut child = proc.spawn().map_err(|e| ExecError::ProcessSpawnError { - name: name.clone(), + name: name.to_string(), source: e, })?; let pid = child.id(); let status = child.wait().map_err(|e| ExecError::ProcessWaitError { - name, + name: name.to_string(), pid, source: e, })?; - return Ok(Value::ExitStatus(status)); + Ok(Value::ExitStatus(status)) + } +} + +impl Eval for Command { + fn eval(&self, ctx: &mut State) -> Result { + let name = self.name.eval(ctx)?.try_to_string()?; + + match Builtin::lookup(&name) { + Some(builtin) => { + let args = self.eval_args(ctx)?; + // let args: Vec<&str> = args.into_iter().map(|arg| arg).collect(); + + builtin.call(ctx, &args) + } + None => self.exec_command(ctx, &name), + } } } @@ -199,11 +226,3 @@ mod test { //Ok(()) } } - -/* - -> ls - command - name: ls - -*/