diff --git a/src/entry/unix/unix.rs b/src/entry/unix/unix.rs index cfe2c89..d101a59 100644 --- a/src/entry/unix/unix.rs +++ b/src/entry/unix/unix.rs @@ -24,7 +24,7 @@ use crate::context::Context; fn do_expression( stdout: &mut RawTerminal, s: &String, - context: &Context + context: &mut Context ) -> Result { #[cfg(debug_assertions)] RawTerminal::suspend_raw_mode(&stdout).unwrap(); @@ -118,6 +118,16 @@ fn do_expression( style::Reset, color::Fg(color::Reset), ).unwrap(); + }, + + Err(EvalError::BadDefineName) => { + write!( + stdout, "\n {}{}Evaluation Error: {}Invalid variable name{}\r\n\n", + style::Bold, + color::Fg(color::Red), + style::Reset, + color::Fg(color::Reset), + ).unwrap(); } } } @@ -167,7 +177,7 @@ pub fn main() -> Result<(), std::io::Error> { } else if command::is_command(&in_str) { command::do_command(&mut stdout, &in_str)?; } else { - let r = do_expression(&mut stdout, &in_str, &context); + let r = do_expression(&mut stdout, &in_str, &mut context); if let Ok(t) = r { context.push_hist(t); } } diff --git a/src/evaluate/evaluate.rs b/src/evaluate/evaluate.rs index 85cacc6..dfa027f 100644 --- a/src/evaluate/evaluate.rs +++ b/src/evaluate/evaluate.rs @@ -26,7 +26,9 @@ pub fn evaluate(t: &Token, context: &mut Context) -> Result { // Exits when we finish parsing the root node. loop { // Current position in the tree - let g = root.get_at_coords_mut(&coords[0 .. coords.len() - 1]); + let g = root.get_at_coords_mut( + &coords[0 .. coords.len() - 1] + ).unwrap(); // "Move up" step. // We move up if we're at a leaf or if we're out of children to move down to. @@ -36,16 +38,18 @@ pub fn evaluate(t: &Token, context: &mut Context) -> Result { (coords.len() != 0 && (*coords.last().unwrap() >= g.get_args().unwrap().len())) } { - if !g.is_quantity() { - *g = match g { - Token::Quantity(_) => panic!(), - Token::Constant(c) => { evaluate(&c.value(), context).unwrap() }, - Token::Variable(s) => { context.get_variable(&s).unwrap() }, - Token::Operator(Operator::Function(f), v) => { eval_function(&f, &v)? }, - Token::Operator(o, v) => { eval_operator(&o, &v, context)? }, - } - } + let new = match g { + Token::Quantity(_) => None, + + Token::Constant(c) => { Some(evaluate(&c.value(), context).unwrap()) }, + Token::Variable(s) => { context.get_variable(&s) }, + Token::Operator(Operator::Function(f), v) => { Some(eval_function(&f, &v)?) }, + Token::Operator(o, v) => { eval_operator(&o, &v, context)? }, + }; + + if new.is_some() { *g = new.unwrap()} + // Move up the tree coords.pop(); @@ -56,6 +60,13 @@ pub fn evaluate(t: &Token, context: &mut Context) -> Result { } else { // Move down the tree coords.push(0); + + let n = root.get_at_coords(&coords[..]); + if let Some(n) = n { + if let Token::Operator(Operator::Define, _) = n { + *coords.last_mut().unwrap() += 1; + } + } } } diff --git a/src/evaluate/mod.rs b/src/evaluate/mod.rs index 4e5cca9..fe5c65b 100644 --- a/src/evaluate/mod.rs +++ b/src/evaluate/mod.rs @@ -9,5 +9,6 @@ pub enum EvalError { BadMath, TooBig, ZeroDivision, - IncompatibleUnit + IncompatibleUnit, + BadDefineName } \ No newline at end of file diff --git a/src/evaluate/operator.rs b/src/evaluate/operator.rs index c4e7959..3907c99 100644 --- a/src/evaluate/operator.rs +++ b/src/evaluate/operator.rs @@ -4,8 +4,9 @@ use crate::quantity::Quantity; use crate::parser::Operator; use crate::parser::Token; use super::EvalError; +use crate::context::Context; -pub fn eval_operator(op: &Operator, args: &VecDeque) -> Result { +pub fn eval_operator(op: &Operator, args: &VecDeque, context: &mut Context) -> Result, EvalError> { match op { // Handled seperately in evaluate.rs @@ -19,32 +20,42 @@ pub fn eval_operator(op: &Operator, args: &VecDeque) -> Result { panic!() } + Operator::Define => { + if args.len() != 2 { panic!() }; + let b = &args[1]; + + if let Token::Variable(s) = &args[0] { + context.push_var(s.clone(), b.clone()); + return Ok(Some(b.clone())); + } else { return Err(EvalError::BadDefineName); } + }, + Operator::Negative => { - if args.len() != 1 {panic!()}; + if args.len() != 1 { panic!() }; let args = &args[0]; if let Token::Quantity(v) = args { - return Ok(Token::Quantity(-v.clone())); - } else { panic!(); } + return Ok(Some(Token::Quantity(-v.clone()))); + } else { return Ok(None); } }, Operator::Flip => { - if args.len() != 1 {panic!()}; + if args.len() != 1 { panic!() }; let args = &args[0]; if let Token::Quantity(v) = args { if v.is_zero() { return Err(EvalError::ZeroDivision); } - return Ok(Token::Quantity( + return Ok(Some(Token::Quantity( Quantity::new_rational(1f64).unwrap()/v.clone() - )); - } else { panic!(); } + ))); + } else { return Ok(None); } }, Operator::Add => { let mut sum: Quantity; if let Token::Quantity(s) = &args[0] { sum = s.clone(); - } else {panic!()}; + } else { return Ok(None); }; let mut i: usize = 1; while i < args.len() { @@ -56,12 +67,10 @@ pub fn eval_operator(op: &Operator, args: &VecDeque) -> Result { @@ -70,16 +79,14 @@ pub fn eval_operator(op: &Operator, args: &VecDeque) -> Result { - if args.len() != 2 {panic!()}; + if args.len() != 2 { panic!() }; let a = &args[0]; let b = &args[1]; @@ -94,14 +101,14 @@ pub fn eval_operator(op: &Operator, args: &VecDeque) -> Result { - if args.len() != 2 {panic!()}; + if args.len() != 2 { panic!() }; let a = &args[0]; let b = &args[1]; @@ -111,9 +118,9 @@ pub fn eval_operator(op: &Operator, args: &VecDeque) -> Result { @@ -134,9 +141,9 @@ pub fn eval_operator(op: &Operator, args: &VecDeque) -> Result { @@ -159,8 +166,8 @@ pub fn eval_operator(op: &Operator, args: &VecDeque) -> Result { String::from("Syntax") }, - ParserError::Undefined(s) => { - format!("\"{s}\" isn't defined") - }, ParserError::BadNumber => { String::from("Invalid number") } diff --git a/src/parser/pretoken.rs b/src/parser/pretoken.rs index f151467..15bd070 100644 --- a/src/parser/pretoken.rs +++ b/src/parser/pretoken.rs @@ -68,13 +68,15 @@ impl PreToken { } let r = Quantity::new_rational_from_string(&s); + if r.is_none() { return Err((l, ParserError::BadNumber)) } + return Ok(Token::Quantity(r.unwrap())); }, - PreToken::PreWord(l, s) => { + PreToken::PreWord(_l, s) => { let c = Constant::from_string(&s); if c.is_some() { return Ok(Token::Constant(c.unwrap())); } @@ -84,8 +86,7 @@ impl PreToken { let c = context.get_variable(&s); if c.is_some() { return Ok(Token::Variable(s)); } - - return Err((l, ParserError::Undefined(s))); + return Ok(Token::Variable(s)); } PreToken::Container(v) => { return Ok(v); } diff --git a/src/parser/stage/tokenize.rs b/src/parser/stage/tokenize.rs index 5d705d7..231421b 100644 --- a/src/parser/stage/tokenize.rs +++ b/src/parser/stage/tokenize.rs @@ -136,7 +136,7 @@ pub fn tokenize(input: &String) -> VecDeque { // Operator '*'|'×'|'/'|'÷'| - '^'|'!'|'%' + '^'|'!'|'%'|'=' => { match &mut t { Some(PreToken::PreOperator(_, val)) => { val.push(c); }, diff --git a/src/parser/token/operator.rs b/src/parser/token/operator.rs index 7cd0bd2..08bac28 100644 --- a/src/parser/token/operator.rs +++ b/src/parser/token/operator.rs @@ -11,7 +11,8 @@ use super::Function; #[derive(Clone)] #[repr(usize)] pub enum Operator { - ModuloLong = 0, // Mod invoked with "mod" + Define = 0, // Variable and function definition + ModuloLong, // Mod invoked with "mod" DivideLong, UnitConvert, Subtract, @@ -68,6 +69,7 @@ impl Operator { } return match s { + "=" => {Some( Operator::Define )}, "+" => {Some( Operator::Add )}, "-" => {Some( Operator::Subtract )}, "neg" => {Some( Operator::Negative )}, @@ -166,6 +168,7 @@ impl Operator { | Operator::Power | Operator::ModuloLong | Operator::UnitConvert + | Operator::Define => { Token::Operator(self, args) }, } } @@ -201,6 +204,14 @@ impl Operator { Operator::Divide | Operator::Subtract => { panic!() } + Operator::Define => { + return format!( + "{} = {}", + self.add_parens_to_arg(&args[0]), + self.add_parens_to_arg(&args[1]) + ); + }, + Operator::Flip => { return format!("{}⁻¹", Operator::Divide.add_parens_to_arg(&args[0])); }, diff --git a/src/parser/token/token.rs b/src/parser/token/token.rs index eb3f0ef..386156d 100644 --- a/src/parser/token/token.rs +++ b/src/parser/token/token.rs @@ -37,6 +37,13 @@ impl Token { } } + pub fn is_quantity(&self) -> bool { + match self { + Token::Quantity(_) => true, + _ => false + } + } + #[inline(always)] pub fn get_args_mut(&mut self) -> Option<&mut VecDeque> { match self { @@ -46,14 +53,34 @@ impl Token { } #[inline(always)] - pub fn get_at_coords<'a>(g: &'a mut Token, coords: &Vec) -> &'a mut Token { - let mut h = &mut *g; - - for t in coords.iter() { - let inner = h.get_args_mut().unwrap(); - h = &mut inner[*t]; + pub fn get_args(&self) -> Option<&VecDeque> { + match self { + Token::Operator(_, ref a) => Some(a), + _ => None } + } - return h; + #[inline(always)] + pub fn get_at_coords<'a, 'b, I>(&'a self, coords: I) -> Option<&'a Token> + where I: IntoIterator + Sized { + let mut g = self; + for t in coords.into_iter() { + let args = g.get_args(); + let Some(args) = args else { return None; }; + g = &args[*t]; + } + return Some(g); + } + + #[inline(always)] + pub fn get_at_coords_mut<'a, 'b, I>(&'a mut self, coords: I) -> Option<&'a mut Token> + where I: IntoIterator + Sized { + let mut g = self; + for t in coords.into_iter() { + let args = g.get_args_mut(); + let Some(args) = args else { return None; }; + g = &mut args[*t]; + } + return Some(g); } } \ No newline at end of file diff --git a/src/tests.rs b/src/tests.rs index b677739..b245173 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -10,7 +10,7 @@ fn eval_to_str(s: &str) -> Result { }; //let out_str = g.print(); - return match evaluate(&g, &Context::new()) { + return match evaluate(&g, &mut Context::new()) { Ok(x) => Ok(x.to_string_outer()), Err(_) => Err(()) };