diff --git a/Cargo.lock b/Cargo.lock index a83ebb9..a8b3e18 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -22,7 +22,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "daisy" -version = "0.1.5" +version = "0.2.0" dependencies = [ "cfg-if", "rug", diff --git a/Cargo.toml b/Cargo.toml index 6652ee1..7db077f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "daisy" -version = "0.1.5" +version = "0.2.0" edition = "2021" [profile.release] diff --git a/src/entry/unix/unix.rs b/src/entry/unix/unix.rs index f431ad3..1cce689 100644 --- a/src/entry/unix/unix.rs +++ b/src/entry/unix/unix.rs @@ -13,9 +13,11 @@ use termion::{ }; use super::promptbuffer::PromptBuffer; -use crate::tokens::EvalError; +//use crate::tokens::EvalError; use crate::parser; use crate::command; +use crate::evaluate::evaluate; +use crate::evaluate::EvalError; @@ -37,7 +39,7 @@ fn do_expression( color::Fg(color::Red), " ".repeat(l.pos + 4), "^".repeat(l.len), - e.to_message(), + e.to_string(), color::Fg(color::Reset), )?; return Ok(()); @@ -54,10 +56,11 @@ fn do_expression( g.to_string() )?; + // Evaluate expression #[cfg(debug_assertions)] RawTerminal::suspend_raw_mode(&stdout)?; - let g = g.evaluate(); + let g = evaluate(&g); #[cfg(debug_assertions)] RawTerminal::activate_raw_mode(&stdout)?; @@ -118,6 +121,7 @@ fn do_expression( } + return Ok(()); } diff --git a/src/evaluate/evaluate.rs b/src/evaluate/evaluate.rs new file mode 100644 index 0000000..fe66ef7 --- /dev/null +++ b/src/evaluate/evaluate.rs @@ -0,0 +1,81 @@ +use crate::parser::Token; +use crate::parser::Constant; +use crate::quantity::Quantity; + +use super::operator::op_apply; +use super::EvalError; + +pub fn evaluate(t: &Token) -> Result { + let mut g = t.clone(); + let mut coords: Vec = Vec::with_capacity(16); + coords.push(0); + + 'outer: loop { + + let mut h = &mut g; + for t in coords.iter() { + let inner = h.get_args_mut(); + + if inner.is_none() || *t >= inner.as_ref().unwrap().len() { + coords.pop(); + + + let p = Token::get_at_coords(&mut g, &coords); + + let e: Token = match p { + Token::Quantity(_) => { p.clone() }, + Token::Constant(c) => { + match c { + // Mathematical constants + // 100 digits of each. + Constant::Pi => { Token::Quantity(Quantity::new_float_from_string( + "3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067" + ).unwrap())}, + + Constant::E => { Token::Quantity(Quantity::new_float_from_string( + "2.718281828459045235360287471352662497757247093699959574966967627724076630353547594571382178525166427" + ).unwrap()) }, + + Constant::Phi => { Token::Quantity(Quantity::new_float_from_string( + "1.618033988749894848204586834365638117720309179805762862135448622705260462818902449707207204189391137" + ).unwrap()) }, + } + }, + Token::Operator(o,v) => { op_apply(o, &v)? } + }; + + //let e = p.eval()?; + *p = e; + + if coords.len() == 0 { break 'outer; } + let l = coords.pop().unwrap(); + coords.push(l + 1); + continue 'outer; + } + + h = &mut inner.unwrap()[*t]; + } + + + + match h { + Token::Operator(_,_) => { + coords.push(0); + continue 'outer; + }, + + Token::Constant(_) => { + coords.push(0); + continue 'outer; + }, + + Token::Quantity(_) => { + let l = coords.pop().unwrap(); + coords.push(l + 1); + continue 'outer; + } + }; + } + + return Ok(g); +} \ No newline at end of file diff --git a/src/evaluate/function.rs b/src/evaluate/function.rs new file mode 100644 index 0000000..3212145 --- /dev/null +++ b/src/evaluate/function.rs @@ -0,0 +1,102 @@ +use std::collections::VecDeque; + +use crate::parser::Token; +use crate::parser::Function; +use crate::parser::Operator; +use super::EvalError; +use super::operator::op_apply; +use crate::quantity::Quantity; + +fn eval(t: Token) -> Result { + Ok(match t { + Token::Quantity(_) => { t }, + Token::Constant(_) => { Token::Quantity( Quantity::new_rational_from_string("1").unwrap() ) }, + Token::Operator(mut o, v) => { op_apply(&mut o, &v)? } + }) +} + +pub fn func_apply(f: &Function, args: &VecDeque) -> Result { + if args.len() != 1 {panic!()}; + let a = &args[0]; + let Token::Quantity(q) = a else {panic!()}; + + if !q.unitless() { + return Err(EvalError::IncompatibleUnit); + } + + match f { + Function::Abs => { return Ok(Token::Quantity(q.abs())); }, + Function::Floor => { return Ok(Token::Quantity(q.floor())); }, + Function::Ceil => { return Ok(Token::Quantity(q.ceil())); }, + Function::Round => { return Ok(Token::Quantity(q.round())); }, + + Function::NaturalLog => { return Ok(Token::Quantity(q.ln())); }, + Function::TenLog => { return Ok(Token::Quantity(q.log10())); }, + + Function::Sin => { return Ok(Token::Quantity(q.sin())); }, + Function::Cos => { return Ok(Token::Quantity(q.cos())); }, + Function::Tan => { return Ok(Token::Quantity(q.tan())); }, + Function::Asin => { return Ok(Token::Quantity(q.asin())); }, + Function::Acos => { return Ok(Token::Quantity(q.acos())); }, + Function::Atan => { return Ok(Token::Quantity(q.atan())); }, + + Function::Csc => { + return Ok( + eval(Token::Operator( + Operator::Flip, + VecDeque::from(vec!(Token::Quantity(q.sin()))) + ))? + ); + }, + Function::Sec => { + return Ok( + eval(Token::Operator( + Operator::Flip, + VecDeque::from(vec!(Token::Quantity(q.cos()))) + ))? + ); + }, + Function::Cot => { + return Ok( + eval(Token::Operator( + Operator::Flip, + VecDeque::from(vec!(Token::Quantity(q.tan()))) + ))? + ); + }, + + + Function::Sinh => { return Ok(Token::Quantity(q.sinh())); }, + Function::Cosh => { return Ok(Token::Quantity(q.cosh())); }, + Function::Tanh => { return Ok(Token::Quantity(q.tanh())); }, + Function::Asinh => { return Ok(Token::Quantity(q.asinh())); }, + Function::Acosh => { return Ok(Token::Quantity(q.acosh())); }, + Function::Atanh => { return Ok(Token::Quantity(q.atanh())); }, + + Function::Csch => { + return Ok( + eval(Token::Operator( + Operator::Flip, + VecDeque::from(vec!(Token::Quantity(q.sinh()))) + ))? + ); + }, + Function::Sech => { + return Ok( + eval(Token::Operator( + Operator::Flip, + VecDeque::from(vec!(Token::Quantity(q.cosh()))) + ))? + ); + }, + Function::Coth => { + return Ok( + eval(Token::Operator( + Operator::Flip, + VecDeque::from(vec!(Token::Quantity(q.tanh()))) + ))? + ); + }, + + } +} \ No newline at end of file diff --git a/src/evaluate/mod.rs b/src/evaluate/mod.rs new file mode 100644 index 0000000..5408ff2 --- /dev/null +++ b/src/evaluate/mod.rs @@ -0,0 +1,11 @@ +mod operator; +mod function; +mod evaluate; +pub use self::evaluate::evaluate; + +pub enum EvalError { + BadMath, + TooBig, + ZeroDivision, + IncompatibleUnit +} \ No newline at end of file diff --git a/src/evaluate/operator.rs b/src/evaluate/operator.rs new file mode 100644 index 0000000..dd6c7bf --- /dev/null +++ b/src/evaluate/operator.rs @@ -0,0 +1,165 @@ +use std::collections::VecDeque; + +use crate::quantity::Quantity; +use crate::parser::Operator; +use crate::parser::Token; +use super::EvalError; +use super::function::func_apply; + + +pub fn op_apply(op: &mut Operator, args: &VecDeque) -> Result { + match op { + Operator::ImplicitMultiply | + Operator::Sqrt | + Operator::Divide | + Operator::Subtract => { panic!() } + + Operator::Negative => { + if args.len() != 1 {panic!()}; + let args = &args[0]; + + if let Token::Quantity(v) = args { + return Ok(Token::Quantity(-v.clone())); + } else { panic!(); } + }, + + Operator::Flip => { + 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( + Quantity::new_rational(1f64).unwrap()/v.clone() + )); + } else { panic!(); } + }, + + Operator::Add => { + let mut sum: Quantity; + if let Token::Quantity(s) = &args[0] { + sum = s.clone(); + } else {panic!()}; + + let mut i: usize = 1; + while i < args.len() { + let j = &args[i]; + if let Token::Quantity(v) = j { + + if !sum.unit.compatible_with(&v.unit) { + return Err(EvalError::IncompatibleUnit); + } + + sum += v.clone(); + } else { + panic!(); + } + i += 1; + } + return Ok(Token::Quantity(sum)); + }, + + Operator::Multiply => { + let mut prod = Quantity::new_rational(1f64).unwrap(); + for i in args.iter() { + let j = i; + if let Token::Quantity(v) = j { + prod *= v.clone(); + } else { + panic!(); + } + } + return Ok(Token::Quantity(prod)); + }, + + Operator::ModuloLong + | Operator::Modulo => { + if args.len() != 2 {panic!()}; + let a = &args[0]; + let b = &args[1]; + + if let Token::Quantity(va) = a { + if let Token::Quantity(vb) = b { + + if !(va.unitless() && vb.unitless()) { + return Err(EvalError::IncompatibleUnit); + } + + if vb <= &Quantity::new_rational(1f64).unwrap() { return Err(EvalError::BadMath); } + if va.fract() != Quantity::new_rational(0f64).unwrap() { return Err(EvalError::BadMath); } + if vb.fract() != Quantity::new_rational(0f64).unwrap() { return Err(EvalError::BadMath); } + + return Ok(Token::Quantity(va.clone() % vb.clone())); + } else { panic!(); } + } else { panic!(); } + }, + + Operator::UnitConvert + => { + if args.len() != 2 {panic!()}; + let a = &args[0]; + let b = &args[1]; + + if let Token::Quantity(va) = a { + if let Token::Quantity(vb) = b { + let n = va.clone().convert_to(vb.clone()); + if n.is_none() { + return Err(EvalError::IncompatibleUnit); + } + return Ok(Token::Quantity(n.unwrap())); + } else { panic!(); } + } else { panic!(); } + }, + + Operator::Power => { + if args.len() != 2 {panic!()}; + let a = &args[0]; + let b = &args[1]; + + if let Token::Quantity(va) = a { + if let Token::Quantity(vb) = b { + + if !vb.unitless() { + return Err(EvalError::IncompatibleUnit); + } + + if va.is_zero() && vb.is_negative() { + return Err(EvalError::ZeroDivision); + } + + let p = va.pow(vb.clone()); + if p.is_nan() {return Err(EvalError::BadMath);} + return Ok(Token::Quantity(p)); + } else { panic!(); } + } else { panic!(); } + }, + + Operator::Factorial => { + if args.len() != 1 {panic!()}; + let args = &args[0]; + + if let Token::Quantity(v) = args { + + if !v.unitless() { + return Err(EvalError::IncompatibleUnit); + } + + if !v.fract().is_zero() { return Err(EvalError::BadMath); } + if v > &Quantity::new_rational(50_000f64).unwrap() { return Err(EvalError::TooBig); } + + let mut prod = Quantity::new_rational(1f64).unwrap(); + let mut u = v.clone(); + while u > Quantity::new_rational(0f64).unwrap() { + prod *= u.clone(); + u = u - Quantity::new_rational(1f64).unwrap(); + } + + return Ok(Token::Quantity(prod)); + } else { panic!(); } + }, + + Operator::Function(f) => { + return func_apply(f, args); + } + }; +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 56b8cde..bac372b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,8 @@ -pub mod tokens; pub mod parser; pub mod command; pub mod quantity; +pub mod evaluate; -//use crate::tokens::Token; -//use crate::parser::ParserError; -//use crate::parser::LineLocation; mod entry; use crate::entry::main_e; diff --git a/src/parser/mod.rs b/src/parser/mod.rs index c6ebe59..cb7ea5e 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1,174 +1,21 @@ -use std::collections::VecDeque; +mod stage; +mod pretoken; +mod parsererror; +mod token; -mod tokenize; -mod treeify; -mod groupify; -mod find_subs; - - -use crate::parser::tokenize::tokenize; -use crate::parser::groupify::groupify; -use crate::parser::treeify::treeify; -use crate::parser::find_subs::find_subs; - -use crate::quantity::Quantity; -use crate::quantity::Unit; - -use crate::tokens::Token; - -/// Specifies the location of a token in an input string. -/// Used to locate ParserErrors. -#[derive(Debug)] -#[derive(Copy, Clone)] -pub struct LineLocation { - pub pos: usize, - pub len: usize -} - -#[derive(Debug)] -enum PreToken { - PreNumber(LineLocation, String), - PreWord(LineLocation, String), - PreOperator(LineLocation, String), - - PreGroupStart(LineLocation), - PreGroupEnd(LineLocation), - PreGroup(LineLocation, VecDeque), - - Container(Token) -} - -impl PreToken { - #[inline(always)] - pub fn get_line_location(&self) -> &LineLocation { - match self { - PreToken::PreNumber(l, _) - | PreToken::PreWord(l, _) - | PreToken::PreOperator(l, _) - | PreToken::PreGroupStart(l) - | PreToken::PreGroupEnd(l) - | PreToken::PreGroup(l, _) - => l, - - _ => panic!() - } - } - - #[inline(always)] - pub fn get_mut_line_location(&mut self) -> &mut LineLocation { - match self { - PreToken::PreNumber(l, _) - | PreToken::PreWord(l, _) - | PreToken::PreOperator(l, _) - | PreToken::PreGroupStart(l) - | PreToken::PreGroupEnd(l) - | PreToken::PreGroup(l, _) - => l, - - _ => panic!() - } - } - - #[inline(always)] - pub fn to_token(self) -> Result{ - match self { - PreToken::PreNumber(l, mut s) => { - - // The length check here ensures that - // `.` is not parsed as `0.` - // That should be a syntax error. - if s.len() != 1 && &s[0..1] == "." { - s.insert(0, '0'); - } - - let r = Quantity::new_rational_from_string(&s); - if r.is_none() { - return Err((l, ParserError::BadNumber)) - } - return Ok(Token::Number(r.unwrap())); - }, - - PreToken::PreWord(l, s) => { - let c = match &s[..] { - // Mathematical constants - // 100 digits of each. - "π"|"pi" => { Some((Quantity::new_float_from_string( - "3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067" - ).unwrap(), String::from("π")))}, - - "e" => { Some((Quantity::new_float_from_string( - "2.718281828459045235360287471352662497757247093699959574966967627724076630353547594571382178525166427" - ).unwrap(), String::from("e"))) }, - - "phi"|"φ" => { Some((Quantity::new_float_from_string( - "1.618033988749894848204586834365638117720309179805762862135448622705260462818902449707207204189391137" - ).unwrap(), String::from("φ"))) }, - - _ => { None } - }; - - if c.is_some() { - let (a, b) = c.unwrap(); - return Ok(Token::Constant(a, b)); - } - - let c = Unit::from_string(&s); - if c.is_some() { return Ok(Token::Number(c.unwrap())); } - - return Err((l, ParserError::Undefined(s))); - } - - PreToken::Container(v) => { return Ok(v); } - - PreToken::PreOperator(_,_) - | PreToken::PreGroupStart(_) - | PreToken::PreGroupEnd(_) - | PreToken::PreGroup(_, _) - => panic!() - }; - } - -} - - -/// Types of parser errors. -/// If we cannot parse a string, one of these is returned. -#[derive(Debug)] -pub enum ParserError { - //MissingCloseParen, - ExtraCloseParen, - EmptyGroup, - Syntax, - Undefined(String), - BadNumber -} - -impl ParserError { - pub fn to_message(&self) -> String { - match self { - //ParserError::MissingCloseParen => { - // String::from("This group is never closed") - //}, - ParserError::ExtraCloseParen => { - String::from("Extra close parenthesis") - }, - ParserError::EmptyGroup => { - String::from("Groups can't be empty") - }, - ParserError::Syntax => { - String::from("Syntax") - }, - ParserError::Undefined(s) => { - format!("\"{s}\" isn't defined") - }, - ParserError::BadNumber => { - String::from("Invalid number") - } - } - } -} +use self::{ + pretoken::PreToken, + parsererror::ParserError, + parsererror::LineLocation +}; +pub use self::{ + token::Token, + token::Constant, + token::Operator, + token::Function, +}; pub fn parse( @@ -178,10 +25,10 @@ pub fn parse( (LineLocation, ParserError) > { - let tokens = tokenize(s); - let (_, tokens) = find_subs(tokens); - let g = groupify(tokens)?; - let g = treeify(g)?; + let tokens = stage::tokenize(s); + let (_, tokens) = stage::find_subs(tokens); + let g = stage::groupify(tokens)?; + let g = stage::treeify(g)?; return Ok(g); } @@ -198,8 +45,8 @@ pub fn substitute( let mut new_s = s.clone(); let l = s.chars().count(); - let tokens = tokenize(s); - let (subs, _) = find_subs(tokens); + let tokens = stage::tokenize(s); + let (subs, _) = stage::find_subs(tokens); let mut new_c = l - c; for r in subs.iter() { diff --git a/src/parser/parsererror.rs b/src/parser/parsererror.rs new file mode 100644 index 0000000..b6c6701 --- /dev/null +++ b/src/parser/parsererror.rs @@ -0,0 +1,45 @@ +/// Specifies the location of a token in an input string. +/// Used to locate ParserErrors. +#[derive(Debug)] +#[derive(Copy, Clone)] +pub struct LineLocation { + pub pos: usize, + pub len: usize +} + +/// Types of parser errors. +/// If we cannot parse a string, one of these is returned. +#[derive(Debug)] +pub enum ParserError { + //MissingCloseParen, + ExtraCloseParen, + EmptyGroup, + Syntax, + Undefined(String), + BadNumber +} + +impl ToString for ParserError { + fn to_string(&self) -> String { + match self { + //ParserError::MissingCloseParen => { + // String::from("This group is never closed") + //}, + ParserError::ExtraCloseParen => { + String::from("Extra close parenthesis") + }, + ParserError::EmptyGroup => { + String::from("Groups can't be empty") + }, + ParserError::Syntax => { + 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 new file mode 100644 index 0000000..4d3e337 --- /dev/null +++ b/src/parser/pretoken.rs @@ -0,0 +1,104 @@ +use std::collections::VecDeque; +use crate::quantity::Unit; +use crate::quantity::Quantity; + +use super::{ + LineLocation, + ParserError, + Token, + Constant +}; + + +#[derive(Debug)] +pub enum PreToken { + PreQuantity(LineLocation, String), + PreWord(LineLocation, String), + PreOperator(LineLocation, String), + + PreGroupStart(LineLocation), + PreGroupEnd(LineLocation), + PreGroup(LineLocation, VecDeque), + + Container(Token) +} + +impl PreToken { + #[inline(always)] + pub fn get_line_location(&self) -> &LineLocation { + match self { + PreToken::PreQuantity(l, _) + | PreToken::PreWord(l, _) + | PreToken::PreOperator(l, _) + | PreToken::PreGroupStart(l) + | PreToken::PreGroupEnd(l) + | PreToken::PreGroup(l, _) + => l, + + _ => panic!() + } + } + + #[inline(always)] + pub fn get_mut_line_location(&mut self) -> &mut LineLocation { + match self { + PreToken::PreQuantity(l, _) + | PreToken::PreWord(l, _) + | PreToken::PreOperator(l, _) + | PreToken::PreGroupStart(l) + | PreToken::PreGroupEnd(l) + | PreToken::PreGroup(l, _) + => l, + + _ => panic!() + } + } + + #[inline(always)] + pub fn to_token(self) -> Result{ + match self { + PreToken::PreQuantity(l, mut s) => { + + // The length check here ensures that + // `.` is not parsed as `0.` + // That should be a syntax error. + if s.len() != 1 && &s[0..1] == "." { + s.insert(0, '0'); + } + + 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) => { + let c = match &s[..] { + "π"|"pi" => { Some(Constant::Pi)}, + "e" => { Some(Constant::E) }, + "phi"|"φ" => { Some(Constant::Phi) }, + _ => { None } + }; + + if c.is_some() { + return Ok(Token::Constant(c.unwrap())); + } + + let c = Unit::from_string(&s); + if c.is_some() { return Ok(Token::Quantity(c.unwrap())); } + + return Err((l, ParserError::Undefined(s))); + } + + PreToken::Container(v) => { return Ok(v); } + + PreToken::PreOperator(_,_) + | PreToken::PreGroupStart(_) + | PreToken::PreGroupEnd(_) + | PreToken::PreGroup(_, _) + => panic!() + }; + } + +} \ No newline at end of file diff --git a/src/parser/find_subs.rs b/src/parser/stage/find_subs.rs similarity index 96% rename from src/parser/find_subs.rs rename to src/parser/stage/find_subs.rs index 88842ee..e9966f7 100644 --- a/src/parser/find_subs.rs +++ b/src/parser/stage/find_subs.rs @@ -1,10 +1,12 @@ use std::collections::VecDeque; -use crate::parser::PreToken; -use crate::parser::LineLocation; +use super::super::{ + PreToken, + LineLocation +}; -pub(in crate::parser) fn find_subs( +pub fn find_subs( mut g: VecDeque, ) -> ( Vec<(LineLocation, String)>, diff --git a/src/parser/groupify.rs b/src/parser/stage/groupify.rs similarity index 90% rename from src/parser/groupify.rs rename to src/parser/stage/groupify.rs index 75bb337..a2811f2 100644 --- a/src/parser/groupify.rs +++ b/src/parser/stage/groupify.rs @@ -1,10 +1,11 @@ use std::collections::VecDeque; -use crate::parser::PreToken; -use crate::parser::LineLocation; -use crate::parser::ParserError; - -use crate::tokens::Operator; +use super::super::{ + PreToken, + LineLocation, + ParserError, + Operator +}; fn lookback_signs( @@ -99,7 +100,6 @@ fn lookback( lookback_signs(g)?; - let mut i: usize = 0; while i < g.len() { if i >= 1 { @@ -109,12 +109,12 @@ fn lookback( match (&a, &b) { // Insert ImplicitMultiply (PreToken::PreGroup(_,_), PreToken::PreGroup(l ,_)) - | (PreToken::PreGroup(_,_), PreToken::PreNumber(l,_)) - | (PreToken::PreNumber(_,_), PreToken::PreGroup(l,_)) + | (PreToken::PreGroup(_,_), PreToken::PreQuantity(l,_)) + | (PreToken::PreQuantity(_,_), PreToken::PreGroup(l,_)) | (PreToken::PreGroup(_,_), PreToken::PreWord(l,_)) | (PreToken::PreWord(_,_), PreToken::PreGroup(l,_)) - | (PreToken::PreNumber(_,_), PreToken::PreWord(l,_)) - | (PreToken::PreWord(_,_), PreToken::PreNumber(l,_)) + | (PreToken::PreQuantity(_,_), PreToken::PreWord(l,_)) + | (PreToken::PreWord(_,_), PreToken::PreQuantity(l,_)) | (PreToken::PreWord(_,_), PreToken::PreWord(l,_)) => { let loc = LineLocation{pos: l.pos-1, len: 0}; @@ -128,7 +128,7 @@ fn lookback( }, // Insert implicit multiplications for right-unary operators - (PreToken::PreNumber(_,_), PreToken::PreOperator(l,s)) + (PreToken::PreQuantity(_,_), PreToken::PreOperator(l,s)) | (PreToken::PreGroup(_,_), PreToken::PreOperator(l,s)) | (PreToken::PreWord(_,_), PreToken::PreOperator(l,s)) => { @@ -149,7 +149,7 @@ fn lookback( }, // Insert implicit multiplications for left-unary operators. - (PreToken::PreOperator(_,s), PreToken::PreNumber(l,_)) + (PreToken::PreOperator(_,s), PreToken::PreQuantity(l,_)) | (PreToken::PreOperator(_,s), PreToken::PreGroup(l,_)) | (PreToken::PreOperator(_,s), PreToken::PreWord(l,_)) => { @@ -170,7 +170,7 @@ fn lookback( }, // The following are syntax errors - (PreToken::PreNumber(la,_), PreToken::PreNumber(lb,_)) + (PreToken::PreQuantity(la,_), PreToken::PreQuantity(lb,_)) => { return Err(( LineLocation{pos: la.pos, len: lb.pos - la.pos + lb.len}, @@ -188,7 +188,7 @@ fn lookback( } -pub(in crate::parser) fn groupify( +pub fn groupify( mut g: VecDeque ) -> Result< PreToken, diff --git a/src/parser/stage/mod.rs b/src/parser/stage/mod.rs new file mode 100644 index 0000000..f7a9df3 --- /dev/null +++ b/src/parser/stage/mod.rs @@ -0,0 +1,11 @@ +mod tokenize; +mod find_subs; +mod groupify; +mod treeify; + +pub (in super) use self::{ + tokenize::tokenize, + find_subs::find_subs, + groupify::groupify, + treeify::treeify, +}; \ No newline at end of file diff --git a/src/parser/tokenize.rs b/src/parser/stage/tokenize.rs similarity index 88% rename from src/parser/tokenize.rs rename to src/parser/stage/tokenize.rs index 7792ad3..98390d0 100644 --- a/src/parser/tokenize.rs +++ b/src/parser/stage/tokenize.rs @@ -1,9 +1,10 @@ use std::collections::VecDeque; -use crate::parser::PreToken; -use crate::parser::LineLocation; - -use crate::tokens::Operator; +use super::super::{ + PreToken, + LineLocation, + Operator +}; // Called whenever a token is finished. #[inline(always)] @@ -16,7 +17,7 @@ fn push_token(g: &mut VecDeque, t: Option, stop_i: usize) { PreToken::PreGroupStart(ref mut l) | PreToken::PreGroupEnd(ref mut l) | PreToken::PreOperator(ref mut l, _) - | PreToken::PreNumber(ref mut l, _) + | PreToken::PreQuantity(ref mut l, _) | PreToken::PreWord(ref mut l, _) => { *l = LineLocation{ @@ -33,10 +34,10 @@ fn push_token(g: &mut VecDeque, t: Option, stop_i: usize) { // `2e` isn't exponential notation, it's 2*e. // If a number ends in `e`, disconnect the `e` and make it a word. - if let PreToken::PreNumber(l, s) = &t { + if let PreToken::PreQuantity(l, s) = &t { let last = &s[s.len()-1..]; if last == "e" { - g.push_back(PreToken::PreNumber( + g.push_back(PreToken::PreQuantity( LineLocation { pos: l.pos, len: l.len-1 }, String::from(&s[0..s.len()-1]) )); @@ -60,7 +61,7 @@ fn push_token(g: &mut VecDeque, t: Option, stop_i: usize) { } /// Turns a string into Tokens. First stage of parsing. -pub(in crate::parser) fn tokenize(input: &String) -> VecDeque { +pub fn tokenize(input: &String) -> VecDeque { let mut t: Option = None; // The current token we're reading let mut g: VecDeque = VecDeque::with_capacity(32); @@ -73,7 +74,7 @@ pub(in crate::parser) fn tokenize(input: &String) -> VecDeque { match &mut t { // If we're already building a number, // append. - Some(PreToken::PreNumber(_, val)) => { + Some(PreToken::PreQuantity(_, val)) => { val.push(if c == ',' {'.'} else {c}); }, @@ -81,7 +82,7 @@ pub(in crate::parser) fn tokenize(input: &String) -> VecDeque { // previous token and start one. _ => { push_token(&mut g, t, i); - t = Some(PreToken::PreNumber(LineLocation{pos: i, len: 0}, String::from(c))); + t = Some(PreToken::PreQuantity(LineLocation{pos: i, len: 0}, String::from(c))); } }; }, @@ -91,7 +92,7 @@ pub(in crate::parser) fn tokenize(input: &String) -> VecDeque { 'e' => { match &mut t { Some(PreToken::PreWord(_, val)) => { val.push(c); }, - Some(PreToken::PreNumber(_, val)) => { val.push(c); }, + Some(PreToken::PreQuantity(_, val)) => { val.push(c); }, _ => { push_token(&mut g, t, i); @@ -105,7 +106,7 @@ pub(in crate::parser) fn tokenize(input: &String) -> VecDeque { // or it can specify a negative exponent. '-' | '+' => { match &mut t { - Some(PreToken::PreNumber(_, val)) => { + Some(PreToken::PreQuantity(_, val)) => { if &val[val.len()-1..] == "e" { // If the current number ends in an `e`, // this negative specifies a negative exponent diff --git a/src/parser/treeify.rs b/src/parser/stage/treeify.rs similarity index 97% rename from src/parser/treeify.rs rename to src/parser/stage/treeify.rs index cae4c90..ca9559b 100644 --- a/src/parser/treeify.rs +++ b/src/parser/stage/treeify.rs @@ -1,11 +1,12 @@ use std::collections::VecDeque; -use crate::parser::PreToken; -use crate::parser::ParserError; -use crate::parser::LineLocation; - -use crate::tokens::Token; -use crate::tokens::Operator; +use super::super::{ + PreToken, + ParserError, + LineLocation, + Token, + Operator +}; fn treeify_binary( i: usize, @@ -269,7 +270,7 @@ fn treeify_unary( } -pub(in crate::parser) fn treeify( +pub fn treeify( mut g: PreToken, ) -> Result { diff --git a/src/parser/token/constant.rs b/src/parser/token/constant.rs new file mode 100644 index 0000000..15a1ae7 --- /dev/null +++ b/src/parser/token/constant.rs @@ -0,0 +1,17 @@ +#[derive(Debug)] +#[derive(Clone)] +pub enum Constant { + Pi, + Phi, + E +} + +impl Constant { + pub fn to_string(&self) -> String { + match self { + Constant::Pi => { String::from("π") }, + Constant::Phi => { String::from("φ") }, + Constant::E => { String::from("e") }, + } + } +} \ No newline at end of file diff --git a/src/parser/token/function.rs b/src/parser/token/function.rs new file mode 100644 index 0000000..bf3f7d1 --- /dev/null +++ b/src/parser/token/function.rs @@ -0,0 +1,62 @@ +#[derive(Debug)] +#[derive(Copy, Clone)] +pub enum Function { + Abs, + Floor, + Ceil, + Round, + + NaturalLog, + TenLog, + + Sin, + Cos, + Tan, + Asin, + Acos, + Atan, + Csc, + Sec, + Cot, + + Sinh, + Cosh, + Tanh, + Asinh, + Acosh, + Atanh, + Csch, + Sech, + Coth, +} + +impl Function { + pub fn to_string(&self) -> String { + match self { + Function::Abs => { String::from("abs") }, + Function::Floor => { String::from("floor") }, + Function::Ceil => { String::from("ceil") }, + Function::Round => { String::from("round") }, + Function::NaturalLog => { String::from("ln") }, + Function::TenLog => { String::from("log") }, + Function::Sin => { String::from("sin") }, + Function::Cos => { String::from("cos") }, + Function::Tan => { String::from("tan") }, + Function::Asin => { String::from("asin") }, + Function::Acos => { String::from("acos") }, + Function::Atan => { String::from("atan") }, + Function::Csc => { String::from("csc") }, + Function::Sec => { String::from("sec") }, + Function::Cot => { String::from("cot") }, + Function::Sinh => { String::from("sinh") }, + Function::Cosh => { String::from("cosh") }, + Function::Tanh => { String::from("tanh") }, + Function::Asinh => { String::from("asinh") }, + Function::Acosh => { String::from("acosh") }, + Function::Atanh => { String::from("atanh") }, + Function::Csch => { String::from("csch") }, + Function::Sech => { String::from("sech") }, + Function::Coth => { String::from("coth") }, + } + } +} \ No newline at end of file diff --git a/src/parser/token/mod.rs b/src/parser/token/mod.rs new file mode 100644 index 0000000..564b412 --- /dev/null +++ b/src/parser/token/mod.rs @@ -0,0 +1,9 @@ +mod operator; +mod function; +mod token; +mod constant; + +pub use self::operator::Operator; +pub use self::function::Function; +pub use self::token::Token; +pub use self::constant::Constant; \ No newline at end of file diff --git a/src/tokens/operator.rs b/src/parser/token/operator.rs similarity index 55% rename from src/tokens/operator.rs rename to src/parser/token/operator.rs index 1473161..c327e00 100644 --- a/src/tokens/operator.rs +++ b/src/parser/token/operator.rs @@ -1,11 +1,10 @@ -use std::collections::VecDeque; use std::cmp::Ordering; - -use crate::tokens::Token; -use crate::tokens::Function; -use crate::tokens::EvalError; +use std::collections::VecDeque; use crate::quantity::Quantity; +use super::Token; +use super::Function; + /// Operator types, in order of increasing priority. #[derive(Debug)] @@ -54,159 +53,9 @@ impl PartialOrd for Operator { } impl Operator { - #[inline(always)] - fn add_parens_to_arg(&self, arg: &Token) -> String { - let mut astr: String = arg.to_string(); - if let Token::Operator(o,_) = arg { - if o < self { - astr = format!("({})", astr); - } - } - return astr; - } - - #[inline(always)] - fn add_parens_to_arg_strict(&self, arg: &Token) -> String { - let mut astr: String = arg.to_string(); - if let Token::Operator(o,_) = arg { - if o <= self { - astr = format!("({})", astr); - } - } - return astr; - } - - pub fn print(&self, args: &VecDeque) -> String { - match self { - Operator::ImplicitMultiply | - Operator::Sqrt | - Operator::Divide | - Operator::Subtract => { panic!() } - - Operator::Flip => { - return format!("1/{}", Operator::Divide.add_parens_to_arg(&args[0])); - }, - - Operator::Negative => { - return format!("-{}", self.add_parens_to_arg(&args[0])); - }, - - Operator::ModuloLong => { - return format!( - "{} mod {}", - self.add_parens_to_arg(&args[0]), - self.add_parens_to_arg(&args[1]) - ); - }, - - Operator::UnitConvert => { - return format!( - "{} to {}", - self.add_parens_to_arg(&args[0]), - self.add_parens_to_arg(&args[1]) - ); - }, - - Operator::Modulo => { - return format!( - "{} % {}", - self.add_parens_to_arg(&args[0]), - self.add_parens_to_arg(&args[1]) - ); - }, - - Operator::Power => { - return format!( - "{}^{}", - self.add_parens_to_arg_strict(&args[0]), - self.add_parens_to_arg_strict(&args[1]) - ); - }, - - Operator::Factorial => { - return format!("{}!", self.add_parens_to_arg(&args[0])); - }, - - - - Operator::Add => { - let a = &args[0]; - let mut b = &args[1]; - let mut sub = false; - - let tmp; - - if let Token::Operator(o,ar) = b { - if let Operator::Negative = o { - sub = true; - b = &ar[0]; - } - } else if let Token::Number(q) = b { - if q.is_negative() { - sub = true; - tmp = Token::Number(-q.clone()); - b = &tmp; - } - } - let (b, sub) = (b, sub); - - if sub { - return format!("{} - {}", self.add_parens_to_arg(a), self.add_parens_to_arg(b)); - } else { - return format!("{} + {}", self.add_parens_to_arg(a), self.add_parens_to_arg(b)); - } - }, - - Operator::Multiply => { - let a = &args[0]; - let mut b = &args[1]; - let mut div = false; - - if let Token::Operator(o,ar) = b { - if let Operator::Flip = o { - div = true; - b = &ar[0]; - } - } - let (b, div) = (b, div); - - if div { - return format!("{} ÷ {}", - self.add_parens_to_arg_strict(a), - self.add_parens_to_arg_strict(b) - ); - } else { - - // Ommit times sign when we have a number - // multiplied by a unit (like 10 m) - // Times sign should stay in all other cases. - let no_times = if let Token::Number(p) = b { - if let Token::Number(q) = a { - (!p.unitless() && p.is_one()) && - !(!q.unitless() && p.is_one()) - } else {false} - } else {false}; - - if no_times { - return format!("{} {}", - self.add_parens_to_arg_strict(a), - self.add_parens_to_arg_strict(b) - ); - } else { - return format!("{} × {}", - self.add_parens_to_arg_strict(a), - self.add_parens_to_arg_strict(b) - ); - } - - } - }, - - Operator::Function(s) => { - return format!("{}({})", s.to_string(), args[0].to_string()); - } - }; + pub fn as_int(&self) -> usize { + unsafe { *<*const _>::from(self).cast::() } } #[inline(always)] @@ -276,12 +125,6 @@ impl Operator { } } - #[inline(always)] - pub fn as_int(&self) -> usize { - unsafe { *<*const _>::from(self).cast::() } - } - - #[inline(always)] pub fn into_token(self, mut args: VecDeque) -> Token { match self { @@ -291,8 +134,8 @@ impl Operator { let b = args.pop_front().unwrap(); let b_new; - if let Token::Number(q) = b { - b_new = Token::Number(-q); + if let Token::Quantity(q) = b { + b_new = Token::Quantity(-q); } else { b_new = Token::Operator(Operator::Negative, VecDeque::from(vec!(b))); } @@ -321,7 +164,7 @@ impl Operator { Token::Operator( Operator::Power, - VecDeque::from(vec!(a, Token::Number(Quantity::new_rational(0.5).unwrap()))) + VecDeque::from(vec!(a, Token::Quantity(Quantity::new_rational_from_string("0.5").unwrap()))) ) }, @@ -341,164 +184,137 @@ impl Operator { => { Token::Operator(self, args) }, } } -} -impl Operator{ - pub fn apply(&self, args: &VecDeque) -> Result { + + #[inline(always)] + fn add_parens_to_arg(&self, arg: &Token) -> String { + let mut astr: String = arg.to_string(); + if let Token::Operator(o,_) = arg { + if o < self { + astr = format!("({})", astr); + } + } + return astr; + } + + #[inline(always)] + fn add_parens_to_arg_strict(&self, arg: &Token) -> String { + let mut astr: String = arg.to_string(); + if let Token::Operator(o,_) = arg { + if o <= self { + astr = format!("({})", astr); + } + } + return astr; + } + + + pub fn print(&self, args: &VecDeque) -> String { match self { Operator::ImplicitMultiply | Operator::Sqrt | Operator::Divide | Operator::Subtract => { panic!() } - Operator::Negative => { - if args.len() != 1 {panic!()}; - let args = args[0].as_number(); - - if let Token::Number(v) = args { - return Ok(Token::Number(-v)); - } else { panic!(); } - }, - Operator::Flip => { - if args.len() != 1 {panic!()}; - let args = args[0].as_number(); - - if let Token::Number(v) = args { - if v.is_zero() { return Err(EvalError::ZeroDivision); } - return Ok(Token::Number( - Quantity::new_rational(1f64).unwrap()/v - )); - } else { panic!(); } + return format!("{}⁻¹", Operator::Divide.add_parens_to_arg(&args[0])); }, - Operator::Add => { - let mut sum; - if let Token::Number(s) = args[0].as_number() { - sum = s; - } else {panic!()}; - - let mut i: usize = 1; - while i < args.len() { - let j = args[i].as_number(); - if let Token::Number(v) = j { - - if !sum.unit.compatible_with(&v.unit) { - return Err(EvalError::IncompatibleUnit); - } - - sum += v; - } else { - panic!(); - } - i += 1; - } - return Ok(Token::Number(sum)); + Operator::Negative => { + return format!("-{}", self.add_parens_to_arg(&args[0])); }, - Operator::Multiply => { - let mut prod = Quantity::new_rational(1f64).unwrap(); - for i in args.iter() { - let j = i.as_number(); - if let Token::Number(v) = j { - prod *= v; - } else { - panic!(); - } - } - return Ok(Token::Number(prod)); + Operator::ModuloLong => { + return format!( + "{} mod {}", + self.add_parens_to_arg(&args[0]), + self.add_parens_to_arg(&args[1]) + ); }, - Operator::ModuloLong - | Operator::Modulo => { - if args.len() != 2 {panic!()}; - let a = args[0].as_number(); - let b = args[1].as_number(); - - if let Token::Number(va) = a { - if let Token::Number(vb) = b { - - if !(va.unitless() && vb.unitless()) { - return Err(EvalError::IncompatibleUnit); - } - - if vb <= Quantity::new_rational(1f64).unwrap() { return Err(EvalError::BadMath); } - if va.fract() != Quantity::new_rational(0f64).unwrap() { return Err(EvalError::BadMath); } - if vb.fract() != Quantity::new_rational(0f64).unwrap() { return Err(EvalError::BadMath); } - - return Ok(Token::Number(va%vb)); - } else { panic!(); } - } else { panic!(); } + Operator::UnitConvert => { + return format!( + "{} to {}", + self.add_parens_to_arg(&args[0]), + self.add_parens_to_arg(&args[1]) + ); }, - Operator::UnitConvert - => { - if args.len() != 2 {panic!()}; - let a = args[0].as_number(); - let b = args[1].as_number(); - - if let Token::Number(va) = a { - if let Token::Number(vb) = b { - let n = va.convert_to(vb); - if n.is_none() { - return Err(EvalError::IncompatibleUnit); - } - return Ok(Token::Number(n.unwrap())); - } else { panic!(); } - } else { panic!(); } + Operator::Modulo => { + return format!( + "{} % {}", + self.add_parens_to_arg(&args[0]), + self.add_parens_to_arg(&args[1]) + ); }, Operator::Power => { - if args.len() != 2 {panic!()}; - let a = args[0].as_number(); - let b = args[1].as_number(); - - if let Token::Number(va) = a { - if let Token::Number(vb) = b { - - if !vb.unitless() { - return Err(EvalError::IncompatibleUnit); - } - - if va.is_zero() && vb.is_negative() { - return Err(EvalError::ZeroDivision); - } - - let p = va.pow(vb); - if p.is_nan() {return Err(EvalError::BadMath);} - return Ok(Token::Number(p)); - } else { panic!(); } - } else { panic!(); } + return format!( + "{}^{}", + self.add_parens_to_arg_strict(&args[0]), + self.add_parens_to_arg_strict(&args[1]) + ); }, Operator::Factorial => { - if args.len() != 1 {panic!()}; - let args = args[0].as_number(); - - if let Token::Number(v) = args { - - if !v.unitless() { - return Err(EvalError::IncompatibleUnit); - } - - if !v.fract().is_zero() { return Err(EvalError::BadMath); } - if v > Quantity::new_rational(50_000f64).unwrap() { return Err(EvalError::TooBig); } - - let mut prod = Quantity::new_rational(1f64).unwrap(); - let mut u = v.clone(); - while u > Quantity::new_rational(0f64).unwrap() { - prod *= u.clone(); - u = u - Quantity::new_rational(1f64).unwrap(); - } - - return Ok(Token::Number(prod)); - } else { panic!(); } + return format!("{}!", self.add_parens_to_arg(&args[0])); }, - Operator::Function(f) => { - return f.apply(args); + Operator::Add => { + let a = &args[0]; + + let b; let sub; + if let Token::Operator(o,ar) = &args[1] { + if let Operator::Negative = o { + sub = true; + b = &ar[0]; + } else { sub = false; b = &args[1]; } + } else { sub = false; b = &args[1]; } + + if sub { + return format!( + "{} - {}", + self.add_parens_to_arg(a), + self.add_parens_to_arg(b) + ); + } else { + return format!( + "{} + {}", + self.add_parens_to_arg(a), + self.add_parens_to_arg(b) + ); + } + }, + + Operator::Multiply => { + let a = &args[0]; + + let b; let div; + if let Token::Operator(o,ar) = &args[1] { + if let Operator::Flip = o { + div = true; + b = &ar[0]; + } else { div = false; b = &args[1]; } + } else { div = false; b = &args[1]; } + + if div { + return format!("{} ÷ {}", + self.add_parens_to_arg_strict(a), + self.add_parens_to_arg_strict(b) + ); + } else { + return format!("{} × {}", + self.add_parens_to_arg_strict(a), + self.add_parens_to_arg_strict(b) + ); + } + }, + + Operator::Function(s) => { + return format!("{}({})", s.to_string(), args[0].to_string()); } }; } + } \ No newline at end of file diff --git a/src/parser/token/token.rs b/src/parser/token/token.rs new file mode 100644 index 0000000..23a6b33 --- /dev/null +++ b/src/parser/token/token.rs @@ -0,0 +1,56 @@ +use std::collections::VecDeque; +use crate::quantity::Quantity; + +use super::Operator; +use super::Constant; + +/// Tokens represent logical objects in an expession. +#[derive(Debug)] +#[derive(Clone)] +pub enum Token { + Quantity(Quantity), + Constant(Constant), + Operator(Operator, VecDeque), +} + +impl ToString for Token { + fn to_string(&self) -> String { + match self { + Token::Quantity(v) => v.to_string(), + Token::Constant(c) => c.to_string(), + Token::Operator(o,a) => o.print(a) + } + } +} + +impl Token { + // This is called only when this is the outermost token. + // This sometimes leads to different--usually more verbose--behavior. + pub fn to_string_outer(&self) -> String { + match self { + Token::Quantity(v) => v.to_string_outer(), + Token::Constant(c) => c.to_string(), + Token::Operator(o,a) => o.print(a) + } + } + + #[inline(always)] + pub fn get_args_mut(&mut self) -> Option<&mut VecDeque> { + match self { + Token::Operator(_, ref mut a) => Some(a), + _ => None + } + } + + #[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]; + } + + return h; + } +} \ No newline at end of file diff --git a/src/tests.rs b/src/tests.rs index 92d5b10..358403b 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -1,5 +1,6 @@ // Many of these have been borrowed from insect. use crate::parser; +use crate::evaluate::evaluate; fn eval_to_str(s: &str) -> Result { let g = match parser::parse(&String::from(s)) { @@ -8,7 +9,7 @@ fn eval_to_str(s: &str) -> Result { }; //let out_str = g.print(); - return match g.evaluate() { + return match evaluate(&g) { Ok(x) => Ok(x.to_string_outer()), Err(_) => Err(()) }; diff --git a/src/tokens/function.rs b/src/tokens/function.rs deleted file mode 100644 index d0d2000..0000000 --- a/src/tokens/function.rs +++ /dev/null @@ -1,154 +0,0 @@ -use std::collections::VecDeque; - -use crate::tokens::Token; -use crate::tokens::EvalError; -use crate::tokens::Operator; - -#[derive(Debug)] -#[derive(Copy, Clone)] -pub enum Function { - Abs, - Floor, - Ceil, - Round, - - NaturalLog, - TenLog, - - Sin, - Cos, - Tan, - Asin, - Acos, - Atan, - Csc, - Sec, - Cot, - - Sinh, - Cosh, - Tanh, - Asinh, - Acosh, - Atanh, - Csch, - Sech, - Coth, -} - -impl Function { - pub fn to_string(&self) -> String { - match self { - Function::Abs => { String::from("abs") }, - Function::Floor => { String::from("floor") }, - Function::Ceil => { String::from("ceil") }, - Function::Round => { String::from("round") }, - Function::NaturalLog => { String::from("ln") }, - Function::TenLog => { String::from("log") }, - Function::Sin => { String::from("sin") }, - Function::Cos => { String::from("cos") }, - Function::Tan => { String::from("tan") }, - Function::Asin => { String::from("asin") }, - Function::Acos => { String::from("acos") }, - Function::Atan => { String::from("atan") }, - Function::Csc => { String::from("csc") }, - Function::Sec => { String::from("sec") }, - Function::Cot => { String::from("cot") }, - Function::Sinh => { String::from("sinh") }, - Function::Cosh => { String::from("cosh") }, - Function::Tanh => { String::from("tanh") }, - Function::Asinh => { String::from("asinh") }, - Function::Acosh => { String::from("acosh") }, - Function::Atanh => { String::from("atanh") }, - Function::Csch => { String::from("csch") }, - Function::Sech => { String::from("sech") }, - Function::Coth => { String::from("coth") }, - } - } - - pub fn apply(&self, args: &VecDeque) -> Result { - if args.len() != 1 {panic!()}; - let a = args[0].as_number(); - let Token::Number(q) = a else {panic!()}; - - if !q.unitless() { - return Err(EvalError::IncompatibleUnit); - } - - match self { - Function::Abs => { return Ok(Token::Number(q.abs())); }, - Function::Floor => { return Ok(Token::Number(q.floor())); }, - Function::Ceil => { return Ok(Token::Number(q.ceil())); }, - Function::Round => { return Ok(Token::Number(q.round())); }, - - Function::NaturalLog => { return Ok(Token::Number(q.ln())); }, - Function::TenLog => { return Ok(Token::Number(q.log10())); }, - - Function::Sin => { return Ok(Token::Number(q.sin())); }, - Function::Cos => { return Ok(Token::Number(q.cos())); }, - Function::Tan => { return Ok(Token::Number(q.tan())); }, - Function::Asin => { return Ok(Token::Number(q.asin())); }, - Function::Acos => { return Ok(Token::Number(q.acos())); }, - Function::Atan => { return Ok(Token::Number(q.atan())); }, - - Function::Csc => { - return Ok( - Token::Operator( - Operator::Flip, - VecDeque::from(vec!(Token::Number(q.sin()))) - ).eval()? - ); - }, - Function::Sec => { - return Ok( - Token::Operator( - Operator::Flip, - VecDeque::from(vec!(Token::Number(q.cos()))) - ).eval()? - ); - }, - Function::Cot => { - return Ok( - Token::Operator( - Operator::Flip, - VecDeque::from(vec!(Token::Number(q.tan()))) - ).eval()? - ); - }, - - - Function::Sinh => { return Ok(Token::Number(q.sinh())); }, - Function::Cosh => { return Ok(Token::Number(q.cosh())); }, - Function::Tanh => { return Ok(Token::Number(q.tanh())); }, - Function::Asinh => { return Ok(Token::Number(q.asinh())); }, - Function::Acosh => { return Ok(Token::Number(q.acosh())); }, - Function::Atanh => { return Ok(Token::Number(q.atanh())); }, - - Function::Csch => { - return Ok( - Token::Operator( - Operator::Flip, - VecDeque::from(vec!(Token::Number(q.sinh()))) - ).eval()? - ); - }, - Function::Sech => { - return Ok( - Token::Operator( - Operator::Flip, - VecDeque::from(vec!(Token::Number(q.cosh()))) - ).eval()? - ); - }, - Function::Coth => { - return Ok( - Token::Operator( - Operator::Flip, - VecDeque::from(vec!(Token::Number(q.tanh()))) - ).eval()? - ); - }, - - } - } -} diff --git a/src/tokens/mod.rs b/src/tokens/mod.rs deleted file mode 100644 index d9dc9f6..0000000 --- a/src/tokens/mod.rs +++ /dev/null @@ -1,14 +0,0 @@ -mod function; -mod operator; -mod token; - -pub use crate::tokens::token::Token; -pub use crate::tokens::function::Function; -pub use crate::tokens::operator::Operator; - -pub enum EvalError { - BadMath, - TooBig, - ZeroDivision, - IncompatibleUnit -} \ No newline at end of file diff --git a/src/tokens/token.rs b/src/tokens/token.rs deleted file mode 100644 index a2e86cf..0000000 --- a/src/tokens/token.rs +++ /dev/null @@ -1,147 +0,0 @@ -use std::collections::VecDeque; - -use crate::tokens::EvalError; -use crate::tokens::Operator; -use crate::quantity::Quantity; - - -/// Tokens represent logical objects in an expession. -/// -/// Tokens starting with `Pre*` are intermediate tokens, and -/// will never show up in a fully-parsed expression tree. -#[derive(Debug)] -#[derive(Clone)] -pub enum Token { - Number(Quantity), - - Constant(Quantity, String), - - Operator( - Operator, - VecDeque - ), -} - -impl ToString for Token { - fn to_string(&self) -> String { - match self { - Token::Number(v) => v.to_string(), - Token::Constant(_, s) => s.clone(), - Token::Operator(o,a) => o.print(a) - } - } -} - -impl Token { - - // This is called only when this is the outermost token. - // This sometimes leads to different--usually more verbose--behavior. - pub fn to_string_outer(&self) -> String { - match self { - Token::Number(v) => v.to_string_outer(), - Token::Constant(_, s) => s.clone(), - Token::Operator(o,a) => o.print(a) - } - } - - - #[inline(always)] - pub fn get_args(&self) -> Option<&VecDeque> { - match self { - Token::Operator(_, ref a) => Some(a), - _ => None - } - } - - #[inline(always)] - pub fn get_args_mut(&mut self) -> Option<&mut VecDeque> { - match self { - Token::Operator(_, ref mut a) => Some(a), - _ => None - } - } - - #[inline(always)] - pub fn eval(&self) -> Result { - Ok(match self { - Token::Number(_) => { self.clone() }, - Token::Constant(v,_) => { Token::Number(v.clone()) }, - Token::Operator(o,v) => { o.apply(&v)? } - }) - } - - // Temporary solution - #[inline(always)] - pub fn as_number(&self) -> Token { - match self { - Token::Number(v) => { Token::Number(v.clone()) }, - Token::Constant(v,_) => { Token::Number(v.clone()) }, - _ => panic!() - } - } - -} - - -// Evaluate -impl Token { - #[inline(always)] - 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]; - } - - return h; - } - - - pub fn evaluate(&self) -> Result { - let mut g = self.clone(); - let mut coords: Vec = Vec::with_capacity(16); - coords.push(0); - - 'outer: loop { - - let mut h = &mut g; - for t in coords.iter() { - let inner = h.get_args_mut(); - - if inner.is_none() || *t >= inner.as_ref().unwrap().len() { - coords.pop(); - - - let p = Token::get_at_coords(&mut g, &coords); - let e = p.eval()?; - *p = e; - - if coords.len() == 0 { break 'outer; } - let l = coords.pop().unwrap(); - coords.push(l + 1); - continue 'outer; - } - - h = &mut inner.unwrap()[*t]; - } - - match h { - Token::Operator(_,_) - => { - coords.push(0); - continue 'outer; - }, - - Token::Constant(_,_) | - Token::Number(_) => { - let l = coords.pop().unwrap(); - coords.push(l + 1); - continue 'outer; - } - }; - } - - return Ok(g); - } -} \ No newline at end of file