Struct xxcalc::evaluator::Evaluator
[−]
[src]
pub struct Evaluator { /* fields omitted */ }
Evaluator takes Tokens
in Reverse Polish Notation and evaluates
them using defined functions and constants into a sngle Polynomial
value.
Evaluator stores registered functions (with their arity) between multiple executions. There is no difference between an operator and a function call. Additionaly constants can be registered. Both identifiers are kept in binary tree, so their retrieval is relatively quick. Symbols used for functions or constants must be unique, a function with no arguments can replace a constant, however its value may change.
Examples
let mut tokenizer = Tokenizer::default(); let mut parser = Parser::default(); let mut evaluator = Evaluator::default(); parser.register_operator('+', Operator::new(1, OperatorAssociativity::Left)); evaluator.register_function("+", Function::new(2, Box::new(|args| { // not a production code, just a sample Ok(args[0].clone() + args[1].clone()) }))); let parsed = parser.process(tokenizer.process("2+2")).unwrap(); assert_eq!(evaluator.process(parsed), Ok(Polynomial::constant(4.0)));Run
Extending
One can directly register functions or constants with the evaluator,
or embed the evalutor in another TokensReducer
which will add these
handlers by default.
Methods
impl Evaluator
[src]
fn new() -> Evaluator
Creates an empty Evaluator with no defined symbols.
fn register_function(&mut self, name: &str, function: Function) -> Result<Option<Function>, EvaluationError>
Registers a function with its name.
Each function (or an operator) must have a registered function handle which takes arguments (or operands) and evaluate them into a single value Polynomial.
If a function with the same name has been registered, previously registered function is returned, however the name of the function cannot collide with an already registered constant.
Examples
let mut tokenizer = Tokenizer::default(); let mut parser = Parser::default(); let mut evaluator = Evaluator::default(); parser.register_operator('+', Operator::new(1, OperatorAssociativity::Left)); { let parsed = parser.process(tokenizer.process("2+2")).unwrap(); assert_eq!(evaluator.process(parsed), Err(EvaluationError::UnknownSymbol(String::from("+"), 1))); } evaluator.register_function("+", Function::new(2, Box::new(|args| { // not a production code, just a sample Ok(args[0].clone() + args[1].clone()) }))); { let parsed = parser.process(tokenizer.process("2+2")).unwrap(); assert_eq!(evaluator.process(parsed), Ok(Polynomial::constant(4.0))); }Run
Errors
A ConflictingName error is returned when name of the function collides with previously registered constant.
let mut evaluator = Evaluator::default(); evaluator.register_constant("foo", Polynomial::constant(42.0)); let result = evaluator.register_function("foo", Function::new(1, Box::new(|args| { Ok(args[0].clone()) }))); assert_eq!(result.unwrap_err(), EvaluationError::ConflictingName(String::from("foo")));Run
fn register_constant(&mut self, name: &str, constant: Polynomial) -> Result<Option<Polynomial>, EvaluationError>
Registers a Polynomial constant with its name.
An identifier with given name is replaced with the constant value when it is encountered during evaluation process. If a constant with the same name has been registered, previously registered constant is returned, however the name of the constant cannot collide with an already registered function.
Examples
let mut tokenizer = Tokenizer::default(); let mut parser = Parser::default(); let mut evaluator = Evaluator::default(); { let parsed = parser.process(tokenizer.process("foo")).unwrap(); assert_eq!(evaluator.process(parsed), Err(EvaluationError::UnknownSymbol(String::from("foo"), 0))); } evaluator.register_constant("foo", Polynomial::constant(42.0)); { let parsed = parser.process(tokenizer.process("foo")).unwrap(); assert_eq!(evaluator.process(parsed), Ok(Polynomial::constant(42.0))); }Run
Errors
A ConflictingName error is returned when name of the constant collides with previously registered function.
let mut evaluator = Evaluator::default(); evaluator.register_function("foo", Function::new(1, Box::new(|args| { Ok(args[0].clone()) }))); let result = evaluator.register_constant("foo", Polynomial::constant(42.0)); assert_eq!(result.unwrap_err(), EvaluationError::ConflictingName(String::from("foo")));Run
Trait Implementations
impl Default for Evaluator
[src]
Creates a new default Evaluator.
Such evaluator is not aware of any functions or constants. One must define functions before being able to evaluate operators or other calls.
impl TokensReducer for Evaluator
[src]
This is a main processing unit in the evaluator. It takes
tokens in Reverse Polish Notation and evaluates them into
a single Polynomial
value.
Before evaluating functions, operators or constants they must be registered, as evaluator has no knowledge what to do with the arguments and how to reduce them into a single value. Operators and functions are actualy the same thing, except that operators always require two arguments.
A traditional stack based postfix evaluation algorithm is used (it computes the result in a linear time). Numbers and constants are put on a stack, until a operator or a function call is required. Such call takes off appropriate number of arguments from the stack and calls the function handler with these arguments. Result of such evaluation is put back on the stack. In the end last value on the stack is returned as the result of the evaluation.
Errors
A PolynomialError
is returned when underlying function handler returns
an error (it may happen as a result of converting non constant to float,
division by zero of polynomials or division of polynomials with wrong
degree).
A MultipleExpressions
error is returned when there are multiple tokens
left on the stack. It is causes by providing to many arguments to a
function or giving too many expressions.
let mut tokenizer = Tokenizer::default(); let mut parser = Parser::default(); let mut evaluator = Evaluator::default(); { let parsed = parser.process(tokenizer.process("2, 2")).unwrap(); assert_eq!(evaluator.process(parsed).unwrap_err(), EvaluationError::MultipleExpressions); } evaluator.register_function("foo", Function::new(1, Box::new(|args| { Ok(Polynomial::constant(42.0)) }))); { let parsed = parser.process(tokenizer.process("foo(2, 2)")).unwrap(); assert_eq!(evaluator.process(parsed).unwrap_err(), EvaluationError::MultipleExpressions); }Run
An ArgumentMissing
error is returned when number of tokens on a stack
is less than required arity of given functions. The error contains
required arity and position of error.
let mut tokenizer = Tokenizer::default(); let mut parser = Parser::default(); let mut evaluator = Evaluator::default(); parser.register_operator('+', Operator::new(1, OperatorAssociativity::Left)); evaluator.register_function("+", Function::new(2, Box::new(|args| { // not a production code, just a sample Ok(args[0].clone() + args[1].clone()) }))); { let parsed = parser.process(tokenizer.process("2+2+")).unwrap(); assert_eq!(evaluator.process(parsed).unwrap_err(), EvaluationError::ArgumentMissing(String::from("+"), 2, 3)); } evaluator.register_function("foo", Function::new(1, Box::new(|args| { Ok(Polynomial::constant(42.0)) }))); { let parsed = parser.process(tokenizer.process("foo()")).unwrap(); assert_eq!(evaluator.process(parsed).unwrap_err(), EvaluationError::ArgumentMissing(String::from("foo"), 1, 0)); }Run
An UnknownSymbol
error is returned when an operator or identifier token is
encountered with a name of unregistered function or constant. Each operator,
function and constant need to be registered before it can be evaluated.
let mut tokenizer = Tokenizer::default(); let mut parser = Parser::default(); let mut evaluator = Evaluator::default(); parser.register_operator('+', Operator::new(1, OperatorAssociativity::Left)); { let parsed = parser.process(tokenizer.process("2+2")).unwrap(); assert_eq!(evaluator.process(parsed).unwrap_err(), EvaluationError::UnknownSymbol(String::from("+"), 1)); } { let parsed = parser.process(tokenizer.process("foo(1)")).unwrap(); assert_eq!(evaluator.process(parsed).unwrap_err(), EvaluationError::UnknownSymbol(String::from("foo"), 0)); } { let parsed = parser.process(tokenizer.process("pi")).unwrap(); assert_eq!(evaluator.process(parsed).unwrap_err(), EvaluationError::UnknownSymbol(String::from("pi"), 0)); }Run