mirror of https://github.com/rm-dr/daisy
Restructured packages
parent
813c1e7292
commit
af2eb39fd5
|
@ -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",
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -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())))
|
||||||
|
))?
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
mod operator;
|
||||||
|
mod function;
|
||||||
|
mod evaluate;
|
||||||
|
pub use self::evaluate::evaluate;
|
||||||
|
|
||||||
|
pub enum EvalError {
|
||||||
|
BadMath,
|
||||||
|
TooBig,
|
||||||
|
ZeroDivision,
|
||||||
|
IncompatibleUnit
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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!()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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)>,
|
|
@ -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,
|
|
@ -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,
|
||||||
|
};
|
|
@ -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
|
|
@ -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)> {
|
||||||
|
|
|
@ -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") },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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") },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
|
@ -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());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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(())
|
||||||
};
|
};
|
||||||
|
|
|
@ -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()?
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue