Restructured packages

pull/2/head
Mark 2023-06-11 13:53:45 -07:00
parent 813c1e7292
commit af2eb39fd5
Signed by: Mark
GPG Key ID: AD62BB059C2AAEE4
25 changed files with 850 additions and 833 deletions

2
Cargo.lock generated
View File

@ -22,7 +22,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "daisy" name = "daisy"
version = "0.1.5" version = "0.2.0"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"rug", "rug",

View File

@ -1,6 +1,6 @@
[package] [package]
name = "daisy" name = "daisy"
version = "0.1.5" version = "0.2.0"
edition = "2021" edition = "2021"
[profile.release] [profile.release]

View File

@ -13,9 +13,11 @@ use termion::{
}; };
use super::promptbuffer::PromptBuffer; use super::promptbuffer::PromptBuffer;
use crate::tokens::EvalError; //use crate::tokens::EvalError;
use crate::parser; use crate::parser;
use crate::command; use crate::command;
use crate::evaluate::evaluate;
use crate::evaluate::EvalError;
@ -37,7 +39,7 @@ fn do_expression(
color::Fg(color::Red), color::Fg(color::Red),
" ".repeat(l.pos + 4), " ".repeat(l.pos + 4),
"^".repeat(l.len), "^".repeat(l.len),
e.to_message(), e.to_string(),
color::Fg(color::Reset), color::Fg(color::Reset),
)?; )?;
return Ok(()); return Ok(());
@ -54,10 +56,11 @@ fn do_expression(
g.to_string() g.to_string()
)?; )?;
// Evaluate expression // Evaluate expression
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
RawTerminal::suspend_raw_mode(&stdout)?; RawTerminal::suspend_raw_mode(&stdout)?;
let g = g.evaluate(); let g = evaluate(&g);
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
RawTerminal::activate_raw_mode(&stdout)?; RawTerminal::activate_raw_mode(&stdout)?;
@ -118,6 +121,7 @@ fn do_expression(
} }
return Ok(()); return Ok(());
} }

81
src/evaluate/evaluate.rs Normal file
View File

@ -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<Token, EvalError> {
let mut g = t.clone();
let mut coords: Vec<usize> = 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);
}

102
src/evaluate/function.rs Normal file
View File

@ -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<Token, EvalError> {
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<Token>) -> Result<Token, EvalError> {
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())))
))?
);
},
}
}

11
src/evaluate/mod.rs Normal file
View File

@ -0,0 +1,11 @@
mod operator;
mod function;
mod evaluate;
pub use self::evaluate::evaluate;
pub enum EvalError {
BadMath,
TooBig,
ZeroDivision,
IncompatibleUnit
}

165
src/evaluate/operator.rs Normal file
View File

@ -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<Token>) -> Result<Token, EvalError> {
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);
}
};
}

View File

@ -1,11 +1,8 @@
pub mod tokens;
pub mod parser; pub mod parser;
pub mod command; pub mod command;
pub mod quantity; pub mod quantity;
pub mod evaluate;
//use crate::tokens::Token;
//use crate::parser::ParserError;
//use crate::parser::LineLocation;
mod entry; mod entry;
use crate::entry::main_e; use crate::entry::main_e;

View File

@ -1,174 +1,21 @@
use std::collections::VecDeque; mod stage;
mod pretoken;
mod parsererror;
mod token;
mod tokenize; use self::{
mod treeify; pretoken::PreToken,
mod groupify; parsererror::ParserError,
mod find_subs; parsererror::LineLocation
};
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<PreToken>),
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<Token, (LineLocation, ParserError)>{
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")
}
}
}
}
pub use self::{
token::Token,
token::Constant,
token::Operator,
token::Function,
};
pub fn parse( pub fn parse(
@ -178,10 +25,10 @@ pub fn parse(
(LineLocation, ParserError) (LineLocation, ParserError)
> { > {
let tokens = tokenize(s); let tokens = stage::tokenize(s);
let (_, tokens) = find_subs(tokens); let (_, tokens) = stage::find_subs(tokens);
let g = groupify(tokens)?; let g = stage::groupify(tokens)?;
let g = treeify(g)?; let g = stage::treeify(g)?;
return Ok(g); return Ok(g);
} }
@ -198,8 +45,8 @@ pub fn substitute(
let mut new_s = s.clone(); let mut new_s = s.clone();
let l = s.chars().count(); let l = s.chars().count();
let tokens = tokenize(s); let tokens = stage::tokenize(s);
let (subs, _) = find_subs(tokens); let (subs, _) = stage::find_subs(tokens);
let mut new_c = l - c; let mut new_c = l - c;
for r in subs.iter() { for r in subs.iter() {

45
src/parser/parsererror.rs Normal file
View File

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

104
src/parser/pretoken.rs Normal file
View File

@ -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<PreToken>),
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<Token, (LineLocation, ParserError)>{
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!()
};
}
}

View File

@ -1,10 +1,12 @@
use std::collections::VecDeque; use std::collections::VecDeque;
use crate::parser::PreToken; use super::super::{
use crate::parser::LineLocation; PreToken,
LineLocation
};
pub(in crate::parser) fn find_subs( pub fn find_subs(
mut g: VecDeque<PreToken>, mut g: VecDeque<PreToken>,
) -> ( ) -> (
Vec<(LineLocation, String)>, Vec<(LineLocation, String)>,

View File

@ -1,10 +1,11 @@
use std::collections::VecDeque; use std::collections::VecDeque;
use crate::parser::PreToken; use super::super::{
use crate::parser::LineLocation; PreToken,
use crate::parser::ParserError; LineLocation,
ParserError,
use crate::tokens::Operator; Operator
};
fn lookback_signs( fn lookback_signs(
@ -99,7 +100,6 @@ fn lookback(
lookback_signs(g)?; lookback_signs(g)?;
let mut i: usize = 0; let mut i: usize = 0;
while i < g.len() { while i < g.len() {
if i >= 1 { if i >= 1 {
@ -109,12 +109,12 @@ fn lookback(
match (&a, &b) { match (&a, &b) {
// Insert ImplicitMultiply // Insert ImplicitMultiply
(PreToken::PreGroup(_,_), PreToken::PreGroup(l ,_)) (PreToken::PreGroup(_,_), PreToken::PreGroup(l ,_))
| (PreToken::PreGroup(_,_), PreToken::PreNumber(l,_)) | (PreToken::PreGroup(_,_), PreToken::PreQuantity(l,_))
| (PreToken::PreNumber(_,_), PreToken::PreGroup(l,_)) | (PreToken::PreQuantity(_,_), PreToken::PreGroup(l,_))
| (PreToken::PreGroup(_,_), PreToken::PreWord(l,_)) | (PreToken::PreGroup(_,_), PreToken::PreWord(l,_))
| (PreToken::PreWord(_,_), PreToken::PreGroup(l,_)) | (PreToken::PreWord(_,_), PreToken::PreGroup(l,_))
| (PreToken::PreNumber(_,_), PreToken::PreWord(l,_)) | (PreToken::PreQuantity(_,_), PreToken::PreWord(l,_))
| (PreToken::PreWord(_,_), PreToken::PreNumber(l,_)) | (PreToken::PreWord(_,_), PreToken::PreQuantity(l,_))
| (PreToken::PreWord(_,_), PreToken::PreWord(l,_)) | (PreToken::PreWord(_,_), PreToken::PreWord(l,_))
=> { => {
let loc = LineLocation{pos: l.pos-1, len: 0}; let loc = LineLocation{pos: l.pos-1, len: 0};
@ -128,7 +128,7 @@ fn lookback(
}, },
// Insert implicit multiplications for right-unary operators // 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::PreGroup(_,_), PreToken::PreOperator(l,s))
| (PreToken::PreWord(_,_), PreToken::PreOperator(l,s)) | (PreToken::PreWord(_,_), PreToken::PreOperator(l,s))
=> { => {
@ -149,7 +149,7 @@ fn lookback(
}, },
// Insert implicit multiplications for left-unary operators. // 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::PreGroup(l,_))
| (PreToken::PreOperator(_,s), PreToken::PreWord(l,_)) | (PreToken::PreOperator(_,s), PreToken::PreWord(l,_))
=> { => {
@ -170,7 +170,7 @@ fn lookback(
}, },
// The following are syntax errors // The following are syntax errors
(PreToken::PreNumber(la,_), PreToken::PreNumber(lb,_)) (PreToken::PreQuantity(la,_), PreToken::PreQuantity(lb,_))
=> { => {
return Err(( return Err((
LineLocation{pos: la.pos, len: lb.pos - la.pos + lb.len}, 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<PreToken> mut g: VecDeque<PreToken>
) -> Result< ) -> Result<
PreToken, PreToken,

11
src/parser/stage/mod.rs Normal file
View File

@ -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,
};

View File

@ -1,9 +1,10 @@
use std::collections::VecDeque; use std::collections::VecDeque;
use crate::parser::PreToken; use super::super::{
use crate::parser::LineLocation; PreToken,
LineLocation,
use crate::tokens::Operator; Operator
};
// Called whenever a token is finished. // Called whenever a token is finished.
#[inline(always)] #[inline(always)]
@ -16,7 +17,7 @@ fn push_token(g: &mut VecDeque<PreToken>, t: Option<PreToken>, stop_i: usize) {
PreToken::PreGroupStart(ref mut l) PreToken::PreGroupStart(ref mut l)
| PreToken::PreGroupEnd(ref mut l) | PreToken::PreGroupEnd(ref mut l)
| PreToken::PreOperator(ref mut l, _) | PreToken::PreOperator(ref mut l, _)
| PreToken::PreNumber(ref mut l, _) | PreToken::PreQuantity(ref mut l, _)
| PreToken::PreWord(ref mut l, _) | PreToken::PreWord(ref mut l, _)
=> { => {
*l = LineLocation{ *l = LineLocation{
@ -33,10 +34,10 @@ fn push_token(g: &mut VecDeque<PreToken>, t: Option<PreToken>, stop_i: usize) {
// `2e` isn't exponential notation, it's 2*e. // `2e` isn't exponential notation, it's 2*e.
// If a number ends in `e`, disconnect the `e` and make it a word. // 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..]; let last = &s[s.len()-1..];
if last == "e" { if last == "e" {
g.push_back(PreToken::PreNumber( g.push_back(PreToken::PreQuantity(
LineLocation { pos: l.pos, len: l.len-1 }, LineLocation { pos: l.pos, len: l.len-1 },
String::from(&s[0..s.len()-1]) String::from(&s[0..s.len()-1])
)); ));
@ -60,7 +61,7 @@ fn push_token(g: &mut VecDeque<PreToken>, t: Option<PreToken>, stop_i: usize) {
} }
/// Turns a string into Tokens. First stage of parsing. /// Turns a string into Tokens. First stage of parsing.
pub(in crate::parser) fn tokenize(input: &String) -> VecDeque<PreToken> { pub fn tokenize(input: &String) -> VecDeque<PreToken> {
let mut t: Option<PreToken> = None; // The current token we're reading let mut t: Option<PreToken> = None; // The current token we're reading
let mut g: VecDeque<PreToken> = VecDeque::with_capacity(32); let mut g: VecDeque<PreToken> = VecDeque::with_capacity(32);
@ -73,7 +74,7 @@ pub(in crate::parser) fn tokenize(input: &String) -> VecDeque<PreToken> {
match &mut t { match &mut t {
// If we're already building a number, // If we're already building a number,
// append. // append.
Some(PreToken::PreNumber(_, val)) => { Some(PreToken::PreQuantity(_, val)) => {
val.push(if c == ',' {'.'} else {c}); val.push(if c == ',' {'.'} else {c});
}, },
@ -81,7 +82,7 @@ pub(in crate::parser) fn tokenize(input: &String) -> VecDeque<PreToken> {
// previous token and start one. // previous token and start one.
_ => { _ => {
push_token(&mut g, t, i); 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<PreToken> {
'e' => { 'e' => {
match &mut t { match &mut t {
Some(PreToken::PreWord(_, val)) => { val.push(c); }, 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); push_token(&mut g, t, i);
@ -105,7 +106,7 @@ pub(in crate::parser) fn tokenize(input: &String) -> VecDeque<PreToken> {
// or it can specify a negative exponent. // or it can specify a negative exponent.
'-' | '+' => { '-' | '+' => {
match &mut t { match &mut t {
Some(PreToken::PreNumber(_, val)) => { Some(PreToken::PreQuantity(_, val)) => {
if &val[val.len()-1..] == "e" { if &val[val.len()-1..] == "e" {
// If the current number ends in an `e`, // If the current number ends in an `e`,
// this negative specifies a negative exponent // this negative specifies a negative exponent

View File

@ -1,11 +1,12 @@
use std::collections::VecDeque; use std::collections::VecDeque;
use crate::parser::PreToken; use super::super::{
use crate::parser::ParserError; PreToken,
use crate::parser::LineLocation; ParserError,
LineLocation,
use crate::tokens::Token; Token,
use crate::tokens::Operator; Operator
};
fn treeify_binary( fn treeify_binary(
i: usize, i: usize,
@ -269,7 +270,7 @@ fn treeify_unary(
} }
pub(in crate::parser) fn treeify( pub fn treeify(
mut g: PreToken, mut g: PreToken,
) -> Result<Token, (LineLocation, ParserError)> { ) -> Result<Token, (LineLocation, ParserError)> {

View File

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

View File

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

9
src/parser/token/mod.rs Normal file
View File

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

View File

@ -1,11 +1,10 @@
use std::collections::VecDeque;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::collections::VecDeque;
use crate::tokens::Token;
use crate::tokens::Function;
use crate::tokens::EvalError;
use crate::quantity::Quantity; use crate::quantity::Quantity;
use super::Token;
use super::Function;
/// Operator types, in order of increasing priority. /// Operator types, in order of increasing priority.
#[derive(Debug)] #[derive(Debug)]
@ -54,159 +53,9 @@ impl PartialOrd for Operator {
} }
impl Operator { impl Operator {
#[inline(always)] #[inline(always)]
fn add_parens_to_arg(&self, arg: &Token) -> String { pub fn as_int(&self) -> usize {
let mut astr: String = arg.to_string(); unsafe { *<*const _>::from(self).cast::<usize>() }
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<Token>) -> 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());
}
};
} }
#[inline(always)] #[inline(always)]
@ -276,12 +125,6 @@ impl Operator {
} }
} }
#[inline(always)]
pub fn as_int(&self) -> usize {
unsafe { *<*const _>::from(self).cast::<usize>() }
}
#[inline(always)] #[inline(always)]
pub fn into_token(self, mut args: VecDeque<Token>) -> Token { pub fn into_token(self, mut args: VecDeque<Token>) -> Token {
match self { match self {
@ -291,8 +134,8 @@ impl Operator {
let b = args.pop_front().unwrap(); let b = args.pop_front().unwrap();
let b_new; let b_new;
if let Token::Number(q) = b { if let Token::Quantity(q) = b {
b_new = Token::Number(-q); b_new = Token::Quantity(-q);
} else { } else {
b_new = Token::Operator(Operator::Negative, VecDeque::from(vec!(b))); b_new = Token::Operator(Operator::Negative, VecDeque::from(vec!(b)));
} }
@ -321,7 +164,7 @@ impl Operator {
Token::Operator( Token::Operator(
Operator::Power, 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) }, => { Token::Operator(self, args) },
} }
} }
}
impl Operator{
pub fn apply(&self, args: &VecDeque<Token>) -> Result<Token, EvalError> { #[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<Token>) -> String {
match self { match self {
Operator::ImplicitMultiply | Operator::ImplicitMultiply |
Operator::Sqrt | Operator::Sqrt |
Operator::Divide | Operator::Divide |
Operator::Subtract => { panic!() } 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 => { Operator::Flip => {
if args.len() != 1 {panic!()}; return format!("{}⁻¹", Operator::Divide.add_parens_to_arg(&args[0]));
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!(); }
}, },
Operator::Add => { Operator::Negative => {
let mut sum; return format!("-{}", self.add_parens_to_arg(&args[0]));
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::Multiply => { Operator::ModuloLong => {
let mut prod = Quantity::new_rational(1f64).unwrap(); return format!(
for i in args.iter() { "{} mod {}",
let j = i.as_number(); self.add_parens_to_arg(&args[0]),
if let Token::Number(v) = j { self.add_parens_to_arg(&args[1])
prod *= v; );
} else {
panic!();
}
}
return Ok(Token::Number(prod));
}, },
Operator::ModuloLong Operator::UnitConvert => {
| Operator::Modulo => { return format!(
if args.len() != 2 {panic!()}; "{} to {}",
let a = args[0].as_number(); self.add_parens_to_arg(&args[0]),
let b = args[1].as_number(); self.add_parens_to_arg(&args[1])
);
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 Operator::Modulo => {
=> { return format!(
if args.len() != 2 {panic!()}; "{} % {}",
let a = args[0].as_number(); self.add_parens_to_arg(&args[0]),
let b = args[1].as_number(); self.add_parens_to_arg(&args[1])
);
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::Power => { Operator::Power => {
if args.len() != 2 {panic!()}; return format!(
let a = args[0].as_number(); "{}^{}",
let b = args[1].as_number(); self.add_parens_to_arg_strict(&args[0]),
self.add_parens_to_arg_strict(&args[1])
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!(); }
}, },
Operator::Factorial => { Operator::Factorial => {
if args.len() != 1 {panic!()}; return format!("{}!", self.add_parens_to_arg(&args[0]));
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!(); }
}, },
Operator::Function(f) => { Operator::Add => {
return f.apply(args); 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());
} }
}; };
} }
} }

56
src/parser/token/token.rs Normal file
View File

@ -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<Token>),
}
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<Token>> {
match self {
Token::Operator(_, ref mut a) => Some(a),
_ => None
}
}
#[inline(always)]
pub fn get_at_coords<'a>(g: &'a mut Token, coords: &Vec<usize>) -> &'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;
}
}

View File

@ -1,5 +1,6 @@
// Many of these have been borrowed from insect. // Many of these have been borrowed from insect.
use crate::parser; use crate::parser;
use crate::evaluate::evaluate;
fn eval_to_str(s: &str) -> Result<String, ()> { fn eval_to_str(s: &str) -> Result<String, ()> {
let g = match parser::parse(&String::from(s)) { let g = match parser::parse(&String::from(s)) {
@ -8,7 +9,7 @@ fn eval_to_str(s: &str) -> Result<String, ()> {
}; };
//let out_str = g.print(); //let out_str = g.print();
return match g.evaluate() { return match evaluate(&g) {
Ok(x) => Ok(x.to_string_outer()), Ok(x) => Ok(x.to_string_outer()),
Err(_) => Err(()) Err(_) => Err(())
}; };

View File

@ -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<Token>) -> Result<Token, EvalError> {
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()?
);
},
}
}
}

View File

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

View File

@ -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<Token>
),
}
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<Token>> {
match self {
Token::Operator(_, ref a) => Some(a),
_ => None
}
}
#[inline(always)]
pub fn get_args_mut(&mut self) -> Option<&mut VecDeque<Token>> {
match self {
Token::Operator(_, ref mut a) => Some(a),
_ => None
}
}
#[inline(always)]
pub fn eval(&self) -> Result<Token, EvalError> {
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<usize>) -> &'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<Token, EvalError> {
let mut g = self.clone();
let mut coords: Vec<usize> = 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);
}
}