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]

Creates an empty Evaluator with no defined symbols.

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

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.

Returns the "default value" for a type. Read more

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