mirror of
https://github.com/tiffany352/rink-rs
synced 2024-11-10 05:34:14 +00:00
Represent function using an enum
We only support a limited set of function, so we may as well put them in an enum instead of passing around strings. This allows us to simplify match statements, eliminating the case of unsupported functions.
This commit is contained in:
parent
1ce838983d
commit
f805f5d180
4 changed files with 114 additions and 43 deletions
93
src/ast.rs
93
src/ast.rs
|
@ -44,10 +44,88 @@ pub enum Expr {
|
|||
Equals(Box<Expr>, Box<Expr>),
|
||||
Suffix(Degree, Box<Expr>),
|
||||
Of(String, Box<Expr>),
|
||||
Call(String, Vec<Expr>),
|
||||
Call(Function, Vec<Expr>),
|
||||
Error(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Function {
|
||||
Sqrt,
|
||||
Exp,
|
||||
Ln,
|
||||
Log2,
|
||||
Log10,
|
||||
Sin,
|
||||
Cos,
|
||||
Tan,
|
||||
Asin,
|
||||
Acos,
|
||||
Atan,
|
||||
Sinh,
|
||||
Cosh,
|
||||
Tanh,
|
||||
Asinh,
|
||||
Acosh,
|
||||
Atanh,
|
||||
Log,
|
||||
Hypot,
|
||||
Atan2,
|
||||
}
|
||||
|
||||
impl Function {
|
||||
pub fn name(&self) -> &str {
|
||||
match *self {
|
||||
Function::Sqrt => "sqrt",
|
||||
Function::Exp => "exp",
|
||||
Function::Ln => "ln",
|
||||
Function::Log2 => "log2",
|
||||
Function::Log10 => "log10",
|
||||
Function::Sin => "sin",
|
||||
Function::Cos => "cos",
|
||||
Function::Tan => "tan",
|
||||
Function::Asin => "asin",
|
||||
Function::Acos => "acos",
|
||||
Function::Atan => "atan",
|
||||
Function::Sinh => "sinh",
|
||||
Function::Cosh => "cosh",
|
||||
Function::Tanh => "tanh",
|
||||
Function::Asinh => "asinh",
|
||||
Function::Acosh => "acosh",
|
||||
Function::Atanh => "atanh",
|
||||
Function::Log => "log",
|
||||
Function::Hypot => "hypot",
|
||||
Function::Atan2 => "atan2",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_name(s: &str) -> Option<Self> {
|
||||
let func = match s {
|
||||
"sqrt" => Function::Sqrt,
|
||||
"exp" => Function::Exp,
|
||||
"ln" => Function::Ln,
|
||||
"log2" => Function::Log2,
|
||||
"log10" => Function::Log10,
|
||||
"sin" => Function::Sin,
|
||||
"cos" => Function::Cos,
|
||||
"tan" => Function::Tan,
|
||||
"asin" => Function::Asin,
|
||||
"acos" => Function::Acos,
|
||||
"atan" => Function::Atan,
|
||||
"sinh" => Function::Sinh,
|
||||
"cosh" => Function::Cosh,
|
||||
"tanh" => Function::Tanh,
|
||||
"asinh" => Function::Asinh,
|
||||
"acosh" => Function::Acosh,
|
||||
"atanh" => Function::Atanh,
|
||||
"log" => Function::Log,
|
||||
"hypot" => Function::Hypot,
|
||||
"atan2" => Function::Atan2,
|
||||
_ => return None,
|
||||
};
|
||||
Some(func)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Conversion {
|
||||
None,
|
||||
|
@ -219,8 +297,8 @@ impl fmt::Display for Expr {
|
|||
}
|
||||
Ok(())
|
||||
},
|
||||
Expr::Call(ref name, ref args) => {
|
||||
try!(write!(fmt, "{}(", name));
|
||||
Expr::Call(ref func, ref args) => {
|
||||
try!(write!(fmt, "{}(", func.name()));
|
||||
if let Some(first) = args.first() {
|
||||
try!(recurse(first, fmt, Prec::Equals));
|
||||
}
|
||||
|
@ -320,6 +398,7 @@ impl fmt::Display for DateToken {
|
|||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::Expr::{self, *};
|
||||
use super::Function;
|
||||
|
||||
fn check<T: ::std::fmt::Display>(e: T, expected: &str) {
|
||||
assert_eq!(e.to_string(), expected);
|
||||
|
@ -333,9 +412,9 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn test_display_call() {
|
||||
check(Call("f".into(), vec![]), "f()");
|
||||
check(Call("f".into(), vec![1.into()]), "f(1)");
|
||||
check(Call("f".into(), vec![1.into(), 2.into()]), "f(1, 2)");
|
||||
check(Call("f".into(), vec![1.into(), 2.into(), 3.into()]), "f(1, 2, 3)");
|
||||
check(Call(Function::Sin, vec![]), "sin()");
|
||||
check(Call(Function::Sin, vec![1.into()]), "sin(1)");
|
||||
check(Call(Function::Sin, vec![1.into(), 2.into()]), "sin(1, 2)");
|
||||
check(Call(Function::Sin, vec![1.into(), 2.into(), 3.into()]), "sin(1, 2, 3)");
|
||||
}
|
||||
}
|
||||
|
|
49
src/eval.rs
49
src/eval.rs
|
@ -6,7 +6,7 @@ use std::collections::BTreeMap;
|
|||
use number::{Number, Dim, NumberParts, pow};
|
||||
use num::{Num, Int};
|
||||
use date;
|
||||
use ast::{Expr, Query, Conversion, Digits};
|
||||
use ast::{Expr, Query, Conversion, Digits, Function};
|
||||
use std::rc::Rc;
|
||||
use factorize::{factorize, Factors};
|
||||
use value::{Value, Show};
|
||||
|
@ -137,7 +137,7 @@ impl Context {
|
|||
}
|
||||
})
|
||||
},
|
||||
Expr::Call(ref name, ref args) => {
|
||||
Expr::Call(ref func, ref args) => {
|
||||
let args = try!(
|
||||
args.iter()
|
||||
.map(|x| self.eval(x))
|
||||
|
@ -187,23 +187,23 @@ impl Context {
|
|||
}
|
||||
}}
|
||||
|
||||
match &**name {
|
||||
"sqrt" => func!(fn sqrt(num: Number) {
|
||||
match func {
|
||||
Function::Sqrt => func!(fn sqrt(num: Number) {
|
||||
num.root(2).map(Value::Number)
|
||||
}),
|
||||
"exp" => func!(fn exp(num: Number) {
|
||||
Function::Exp => func!(fn exp(num: Number) {
|
||||
Ok(Value::Number(Number {
|
||||
value: Num::Float(num.value.to_f64().exp()),
|
||||
unit: num.unit.clone(),
|
||||
}))
|
||||
}),
|
||||
"ln" => func!(fn ln(num: Number) {
|
||||
Function::Ln => func!(fn ln(num: Number) {
|
||||
Ok(Value::Number(Number {
|
||||
value: Num::Float(num.value.to_f64().ln()),
|
||||
unit: num.unit.clone(),
|
||||
}))
|
||||
}),
|
||||
"log" => func!(fn log(num: Number, base: Number) {
|
||||
Function::Log => func!(fn log(num: Number, base: Number) {
|
||||
if !base.unit.is_empty() {
|
||||
Err("Base must be dimensionless".to_string())
|
||||
} else {
|
||||
|
@ -214,19 +214,19 @@ impl Context {
|
|||
}))
|
||||
}
|
||||
}),
|
||||
"log2" => func!(fn log2(num: Number) {
|
||||
Function::Log2 => func!(fn log2(num: Number) {
|
||||
Ok(Value::Number(Number {
|
||||
value: Num::Float(num.value.to_f64().log2()),
|
||||
unit: num.unit.clone(),
|
||||
}))
|
||||
}),
|
||||
"log10" => func!(fn ln(num: Number) {
|
||||
Function::Log10 => func!(fn ln(num: Number) {
|
||||
Ok(Value::Number(Number {
|
||||
value: Num::Float(num.value.to_f64().log10()),
|
||||
unit: num.unit.clone(),
|
||||
}))
|
||||
}),
|
||||
"hypot" => func!(fn hypot(x: Number, y: Number) {
|
||||
Function::Hypot => func!(fn hypot(x: Number, y: Number) {
|
||||
if x.unit != y.unit {
|
||||
Err("Arguments to hypot must have matching dimensionality".to_string())
|
||||
} else {
|
||||
|
@ -236,43 +236,43 @@ impl Context {
|
|||
}))
|
||||
}
|
||||
}),
|
||||
"sin" => func!(fn sin(num: Number) {
|
||||
Function::Sin => func!(fn sin(num: Number) {
|
||||
Ok(Value::Number(Number {
|
||||
value: Num::Float(num.value.to_f64().sin()),
|
||||
unit: num.unit.clone(),
|
||||
}))
|
||||
}),
|
||||
"cos" => func!(fn cos(num: Number) {
|
||||
Function::Cos => func!(fn cos(num: Number) {
|
||||
Ok(Value::Number(Number {
|
||||
value: Num::Float(num.value.to_f64().cos()),
|
||||
unit: num.unit.clone(),
|
||||
}))
|
||||
}),
|
||||
"tan" => func!(fn tan(num: Number) {
|
||||
Function::Tan => func!(fn tan(num: Number) {
|
||||
Ok(Value::Number(Number {
|
||||
value: Num::Float(num.value.to_f64().tan()),
|
||||
unit: num.unit.clone(),
|
||||
}))
|
||||
}),
|
||||
"asin" => func!(fn asin(num: Number) {
|
||||
Function::Asin => func!(fn asin(num: Number) {
|
||||
Ok(Value::Number(Number {
|
||||
value: Num::Float(num.value.to_f64().asin()),
|
||||
unit: num.unit.clone(),
|
||||
}))
|
||||
}),
|
||||
"acos" => func!(fn acos(num: Number) {
|
||||
Function::Acos => func!(fn acos(num: Number) {
|
||||
Ok(Value::Number(Number {
|
||||
value: Num::Float(num.value.to_f64().acos()),
|
||||
unit: num.unit.clone(),
|
||||
}))
|
||||
}),
|
||||
"atan" => func!(fn atan(num: Number) {
|
||||
Function::Atan => func!(fn atan(num: Number) {
|
||||
Ok(Value::Number(Number {
|
||||
value: Num::Float(num.value.to_f64().atan()),
|
||||
unit: num.unit.clone(),
|
||||
}))
|
||||
}),
|
||||
"atan2" => func!(fn atan2(x: Number, y: Number) {
|
||||
Function::Atan2 => func!(fn atan2(x: Number, y: Number) {
|
||||
if x.unit != y.unit {
|
||||
Err("Arguments to atan2 must have matching dimensionality".to_string())
|
||||
} else {
|
||||
|
@ -283,45 +283,42 @@ impl Context {
|
|||
}))
|
||||
}
|
||||
}),
|
||||
"sinh" => func!(fn sinh(num: Number) {
|
||||
Function::Sinh => func!(fn sinh(num: Number) {
|
||||
Ok(Value::Number(Number {
|
||||
value: Num::Float(num.value.to_f64().sinh()),
|
||||
unit: num.unit.clone(),
|
||||
}))
|
||||
}),
|
||||
"cosh" => func!(fn cosh(num: Number) {
|
||||
Function::Cosh => func!(fn cosh(num: Number) {
|
||||
Ok(Value::Number(Number {
|
||||
value: Num::Float(num.value.to_f64().cosh()),
|
||||
unit: num.unit.clone(),
|
||||
}))
|
||||
}),
|
||||
"tanh" => func!(fn tanh(num: Number) {
|
||||
Function::Tanh => func!(fn tanh(num: Number) {
|
||||
Ok(Value::Number(Number {
|
||||
value: Num::Float(num.value.to_f64().tanh()),
|
||||
unit: num.unit.clone(),
|
||||
}))
|
||||
}),
|
||||
"asinh" => func!(fn asinh(num: Number) {
|
||||
Function::Asinh => func!(fn asinh(num: Number) {
|
||||
Ok(Value::Number(Number {
|
||||
value: Num::Float(num.value.to_f64().asinh()),
|
||||
unit: num.unit.clone(),
|
||||
}))
|
||||
}),
|
||||
"acosh" => func!(fn acosh(num: Number) {
|
||||
Function::Acosh => func!(fn acosh(num: Number) {
|
||||
Ok(Value::Number(Number {
|
||||
value: Num::Float(num.value.to_f64().acosh()),
|
||||
unit: num.unit.clone(),
|
||||
}))
|
||||
}),
|
||||
"atanh" => func!(fn atanh(num: Number) {
|
||||
Function::Atanh => func!(fn atanh(num: Number) {
|
||||
Ok(Value::Number(Number {
|
||||
value: Num::Float(num.value.to_f64().atanh()),
|
||||
unit: num.unit.clone(),
|
||||
}))
|
||||
}),
|
||||
_ => Err(QueryError::Generic(format!(
|
||||
"Function not found: {}", name
|
||||
)))
|
||||
}
|
||||
},
|
||||
Expr::Error(ref e) => Err(QueryError::Generic(e.clone())),
|
||||
|
|
|
@ -203,8 +203,8 @@ impl ExprReply {
|
|||
literal!(")");
|
||||
}
|
||||
},
|
||||
Expr::Call(ref name, ref args) => {
|
||||
literal!(format!("{}(", name));
|
||||
Expr::Call(ref func, ref args) => {
|
||||
literal!(format!("{}(", func.name()));
|
||||
if let Some(first) = args.first() {
|
||||
recurse(first, parts, Prec::Equals);
|
||||
}
|
||||
|
|
|
@ -429,12 +429,7 @@ impl<'a> Iterator for TokenIterator<'a> {
|
|||
pub type Iter<'a> = Peekable<TokenIterator<'a>>;
|
||||
|
||||
fn is_func(name: &str) -> bool {
|
||||
match name {
|
||||
"sqrt" | "exp" | "ln" | "log" | "log2" | "log10" | "hypot" | "sin" |
|
||||
"cos" | "tan" | "asin" | "acos" | "atan" | "atan2" | "sinh" | "cosh" |
|
||||
"tanh" | "asinh" | "acosh" | "atanh" => true,
|
||||
_ => false
|
||||
}
|
||||
Function::from_name(name).is_some()
|
||||
}
|
||||
|
||||
fn is_attr(name: &str) -> Option<&'static str> {
|
||||
|
@ -478,9 +473,9 @@ fn parse_term(iter: &mut Iter) -> Expr {
|
|||
describe(&x)))
|
||||
}
|
||||
}
|
||||
Expr::Call(name.clone(), args)
|
||||
Expr::Call(Function::from_name(name.as_ref()).unwrap(), args)
|
||||
},
|
||||
_ => Expr::Call(name.clone(), vec![parse_pow(iter)]),
|
||||
_ => Expr::Call(Function::from_name(name.as_ref()).unwrap(), vec![parse_pow(iter)]),
|
||||
}
|
||||
},
|
||||
Token::Ident(ref attr) if is_attr(attr).is_some() => {
|
||||
|
|
Loading…
Reference in a new issue