mirror of
https://github.com/rm-dr/daisy
synced 2025-10-11 21:02:58 -07:00
Split tokens.rs
This commit is contained in:
152
src/tokens/function.rs
Normal file
152
src/tokens/function.rs
Normal file
@ -0,0 +1,152 @@
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use crate::tokens::Token;
|
||||
use crate::tokens::Operator;
|
||||
//use crate::quantity::Quantity;
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum Function {
|
||||
Abs,
|
||||
Floor,
|
||||
Ceil,
|
||||
Round,
|
||||
|
||||
// TODO: Add arbitrary log
|
||||
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, ()> {
|
||||
if args.len() != 1 {panic!()};
|
||||
let a = args[0].as_number();
|
||||
let Token::Number(q) = a else {panic!()};
|
||||
|
||||
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()?
|
||||
);
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
}
|
74
src/tokens/mod.rs
Normal file
74
src/tokens/mod.rs
Normal file
@ -0,0 +1,74 @@
|
||||
use std::collections::VecDeque;
|
||||
|
||||
mod function;
|
||||
mod operator;
|
||||
|
||||
pub use crate::tokens::function::Function;
|
||||
pub use crate::tokens::operator::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 Token {
|
||||
|
||||
pub fn print(&self) -> String {
|
||||
match self {
|
||||
Token::Number(v) => v.to_string(),
|
||||
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, ()> {
|
||||
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!()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
421
src/tokens/operator.rs
Normal file
421
src/tokens/operator.rs
Normal file
@ -0,0 +1,421 @@
|
||||
use std::collections::VecDeque;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use crate::tokens::Token;
|
||||
use crate::tokens::Function;
|
||||
use crate::quantity::Quantity;
|
||||
|
||||
/// Operator types, in order of increasing priority.
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone)]
|
||||
#[repr(usize)]
|
||||
pub enum Operator {
|
||||
ModuloLong = 0, // Mod invoked with "mod"
|
||||
Subtract,
|
||||
Add,
|
||||
Divide,
|
||||
Multiply,
|
||||
Modulo, // Mod invoked with %
|
||||
Negative,
|
||||
|
||||
Sqrt,
|
||||
ImplicitMultiply,
|
||||
|
||||
Power,
|
||||
Factorial,
|
||||
|
||||
Function(Function),
|
||||
|
||||
// Not accessible from prompt
|
||||
Flip,
|
||||
}
|
||||
|
||||
impl PartialEq for Operator {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.as_int() == other.as_int()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Operator {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
match (self, other) {
|
||||
(Operator::Add, Operator::Subtract)
|
||||
| (Operator::Subtract, Operator::Add)
|
||||
| (Operator::Multiply, Operator::Divide)
|
||||
| (Operator::Divide, Operator::Multiply)
|
||||
=> {Some(Ordering::Equal)}
|
||||
|
||||
_ => { self.as_int().partial_cmp(&other.as_int()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Operator {
|
||||
|
||||
#[inline(always)]
|
||||
fn add_parens_to_arg(&self, arg: &Token) -> String {
|
||||
let mut astr: String = arg.print();
|
||||
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::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(&args[0]),
|
||||
self.add_parens_to_arg(&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);
|
||||
|
||||
|
||||
let mut astr: String = a.print();
|
||||
if let Token::Operator(o,_) = a {
|
||||
if o < self {
|
||||
astr = format!("({})", astr);
|
||||
}
|
||||
}
|
||||
|
||||
let mut bstr: String = b.print();
|
||||
if let Token::Operator(o,_) = b {
|
||||
if o < self {
|
||||
bstr = format!("({})", astr);
|
||||
}
|
||||
}
|
||||
|
||||
if div {
|
||||
return format!("{} ÷ {}", astr, bstr);
|
||||
} else {
|
||||
return format!("{} × {}", astr, bstr);
|
||||
}
|
||||
},
|
||||
|
||||
Operator::Function(s) => {
|
||||
return format!("{}({})", s.to_string(), args[0].print());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn from_string(s: &str) -> Option<Operator> {
|
||||
match s {
|
||||
"+" => {Some( Operator::Add )},
|
||||
"-" => {Some( Operator::Subtract )},
|
||||
"neg" => {Some( Operator::Negative )},
|
||||
"*"|"×" => {Some( Operator::Multiply )},
|
||||
"/"|"÷" => {Some( Operator::Divide )},
|
||||
"i*" => {Some( Operator::ImplicitMultiply )},
|
||||
"%" => {Some( Operator::Modulo )},
|
||||
"mod" => {Some( Operator::ModuloLong )},
|
||||
"^"|"**" => {Some( Operator::Power )},
|
||||
"!" => {Some( Operator::Factorial )},
|
||||
"sqrt"|"rt"|"√" => {Some( Operator::Sqrt )},
|
||||
|
||||
"abs" => {Some( Operator::Function(Function::Abs) )},
|
||||
"floor" => {Some( Operator::Function(Function::Floor) )},
|
||||
"ceil" => {Some( Operator::Function(Function::Ceil) )},
|
||||
"round" => {Some( Operator::Function(Function::Round) )},
|
||||
"ln" => {Some( Operator::Function(Function::NaturalLog) )},
|
||||
"log" => {Some( Operator::Function(Function::TenLog) )},
|
||||
"sin" => {Some( Operator::Function(Function::Sin) )},
|
||||
"cos" => {Some( Operator::Function(Function::Cos) )},
|
||||
"tan" => {Some( Operator::Function(Function::Tan) )},
|
||||
"asin" => {Some( Operator::Function(Function::Asin) )},
|
||||
"acos" => {Some( Operator::Function(Function::Acos) )},
|
||||
"atan" => {Some( Operator::Function(Function::Atan) )},
|
||||
"csc" => {Some( Operator::Function(Function::Csc) )},
|
||||
"sec" => {Some( Operator::Function(Function::Sec) )},
|
||||
"cot" => {Some( Operator::Function(Function::Cot) )},
|
||||
"sinh" => {Some( Operator::Function(Function::Sinh) )},
|
||||
"cosh" => {Some( Operator::Function(Function::Cosh) )},
|
||||
"tanh" => {Some( Operator::Function(Function::Tanh) )},
|
||||
"asinh" => {Some( Operator::Function(Function::Asinh) )},
|
||||
"acosh" => {Some( Operator::Function(Function::Acosh) )},
|
||||
"atanh" => {Some( Operator::Function(Function::Atanh) )},
|
||||
"csch" => {Some( Operator::Function(Function::Csch) )},
|
||||
"sech" => {Some( Operator::Function(Function::Sech) )},
|
||||
"coth" => {Some( Operator::Function(Function::Coth) )},
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn is_binary(&self) -> bool {
|
||||
match self {
|
||||
Operator::Negative
|
||||
| Operator::Factorial
|
||||
| Operator::Sqrt
|
||||
| Operator::Function(_)
|
||||
=> false,
|
||||
_ => true
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn is_left_associative(&self) -> bool {
|
||||
match self {
|
||||
Operator::Negative
|
||||
| Operator::Sqrt
|
||||
| Operator::Function(_)
|
||||
=> false,
|
||||
_ => true
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn as_int(&self) -> usize {
|
||||
unsafe { *<*const _>::from(self).cast::<usize>() }
|
||||
}
|
||||
|
||||
|
||||
#[inline(always)]
|
||||
pub fn into_token(self, mut args: VecDeque<Token>) -> Token {
|
||||
match self {
|
||||
Operator::Subtract => {
|
||||
if args.len() != 2 { panic!() }
|
||||
let a = args.pop_front().unwrap();
|
||||
let b = args.pop_front().unwrap();
|
||||
|
||||
let b_new;
|
||||
if let Token::Number(q) = b {
|
||||
b_new = Token::Number(-q);
|
||||
} else {
|
||||
b_new = Token::Operator(Operator::Negative, VecDeque::from(vec!(b)));
|
||||
}
|
||||
|
||||
Token::Operator(
|
||||
Operator::Add,
|
||||
VecDeque::from(vec!(a,b_new))
|
||||
)
|
||||
},
|
||||
|
||||
Operator::Divide => {
|
||||
if args.len() != 2 { panic!() }
|
||||
let a = args.pop_front().unwrap();
|
||||
let b = args.pop_front().unwrap();
|
||||
let b = Token::Operator(Operator::Flip, VecDeque::from(vec!(b)));
|
||||
|
||||
Token::Operator(
|
||||
Operator::Multiply,
|
||||
VecDeque::from(vec!(a,b))
|
||||
)
|
||||
},
|
||||
|
||||
Operator::Sqrt => {
|
||||
if args.len() != 1 { panic!() }
|
||||
let a = args.pop_front().unwrap();
|
||||
|
||||
Token::Operator(
|
||||
Operator::Power,
|
||||
VecDeque::from(vec!(a, Token::Number(Quantity::new_rational(1,2))))
|
||||
)
|
||||
},
|
||||
|
||||
Operator::ImplicitMultiply
|
||||
=> { Token::Operator(Operator::Multiply, args) },
|
||||
|
||||
Operator::Function(_)
|
||||
| Operator::Factorial
|
||||
| Operator::Negative
|
||||
| Operator::Flip
|
||||
| Operator::Add
|
||||
| Operator::Multiply
|
||||
| Operator::Modulo
|
||||
| Operator::Power
|
||||
| Operator::ModuloLong
|
||||
=> { Token::Operator(self, args) },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Operator{
|
||||
pub fn apply(&self, args: &VecDeque<Token>) -> Result<Token, ()> {
|
||||
match self {
|
||||
Operator::ImplicitMultiply |
|
||||
Operator::Sqrt |
|
||||
Operator::Divide |
|
||||
Operator::Subtract => { panic!() }
|
||||
|
||||
Operator::Negative => {
|
||||
if args.len() != 1 {panic!()};
|
||||
let args = args[0].as_number();
|
||||
|
||||
if let Token::Number(v) = args {
|
||||
return Ok(Token::Number(-v));
|
||||
} else { panic!(); }
|
||||
},
|
||||
|
||||
Operator::Flip => {
|
||||
if args.len() != 1 {panic!()};
|
||||
let args = args[0].as_number();
|
||||
|
||||
if let Token::Number(v) = args {
|
||||
if v.is_zero() { return Err(()); }
|
||||
return Ok(Token::Number(Quantity::new_rational(1,1)/v));
|
||||
} else { panic!(); }
|
||||
},
|
||||
|
||||
Operator::Add => {
|
||||
let mut sum = Quantity::new_rational(0,1);
|
||||
for i in args.iter() {
|
||||
let j = i.as_number();
|
||||
if let Token::Number(v) = j {
|
||||
sum += v;
|
||||
} else {
|
||||
panic!();
|
||||
}
|
||||
}
|
||||
return Ok(Token::Number(sum));
|
||||
},
|
||||
|
||||
Operator::Multiply => {
|
||||
let mut prod = Quantity::new_rational(1,1);
|
||||
for i in args.iter() {
|
||||
let j = i.as_number();
|
||||
if let Token::Number(v) = j {
|
||||
prod *= v;
|
||||
} else {
|
||||
panic!();
|
||||
}
|
||||
}
|
||||
return Ok(Token::Number(prod));
|
||||
},
|
||||
|
||||
Operator::ModuloLong
|
||||
| Operator::Modulo => {
|
||||
if args.len() != 2 {panic!()};
|
||||
let a = args[0].as_number();
|
||||
let b = args[1].as_number();
|
||||
|
||||
if let Token::Number(va) = a {
|
||||
if let Token::Number(vb) = b {
|
||||
if vb <= Quantity::new_rational(1,1) { return Err(()); }
|
||||
if va.fract() != Quantity::new_rational(0,1) { return Err(()); }
|
||||
if vb.fract() != Quantity::new_rational(0,1) { return Err(()); }
|
||||
|
||||
return Ok(Token::Number(va%vb));
|
||||
} else { panic!(); }
|
||||
} else { panic!(); }
|
||||
},
|
||||
|
||||
Operator::Power => {
|
||||
if args.len() != 2 {panic!()};
|
||||
let a = args[0].as_number();
|
||||
let b = args[1].as_number();
|
||||
|
||||
if let Token::Number(va) = a {
|
||||
if let Token::Number(vb) = b {
|
||||
let p = va.pow(vb);
|
||||
if p.is_nan() {return Err(());}
|
||||
return Ok(Token::Number(p));
|
||||
} else { panic!(); }
|
||||
} else { panic!(); }
|
||||
},
|
||||
|
||||
Operator::Factorial => {
|
||||
if args.len() != 1 {panic!()};
|
||||
let args = args[0].as_number();
|
||||
|
||||
if let Token::Number(v) = args {
|
||||
if !v.fract().is_zero() { return Err(()); }
|
||||
if v > Quantity::new_rational(50_000, 1) { return Err(()); }
|
||||
|
||||
let mut prod = Quantity::new_rational(1, 1);
|
||||
let mut u = v.clone();
|
||||
while u > Quantity::new_rational(0, 1) {
|
||||
prod *= u.clone();
|
||||
u = u - Quantity::new_rational(1, 1);
|
||||
}
|
||||
|
||||
return Ok(Token::Number(prod));
|
||||
} else { panic!(); }
|
||||
},
|
||||
|
||||
Operator::Function(f) => {
|
||||
return f.apply(args);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user