Document all remaining public members

Implements #6
This commit is contained in:
Sebastian Schmidt 2019-03-20 11:32:49 +02:00
parent cdcd24e7b6
commit 38c4c35a0b
5 changed files with 104 additions and 13 deletions

View File

@ -2,12 +2,19 @@ use crate::value::Value;
use function::Function;
use std::collections::HashMap;
/// A configuration for an expression tree.
///
/// A configuration defines methods to retrieve values and functions for literals in an expression tree.
/// This crate implements two basic variants, the `EmptyConfiguration`, that returns `None` for each identifier, and the `HashMapConfiguration`, that stores its mappings in hash maps.
pub trait Configuration {
/// Returns the value that is linked to the given identifier.
fn get_value(&self, identifier: &str) -> Option<&Value>;
/// Returns the function that is linked to the given identifier.
fn get_function(&self, identifier: &str) -> Option<&Function>;
}
/// A configuration that returns `None` for each identifier.
pub struct EmptyConfiguration;
impl Configuration for EmptyConfiguration {
@ -20,12 +27,16 @@ impl Configuration for EmptyConfiguration {
}
}
/// A configuration that stores its mappings in hash maps.
///
/// *Value and function mappings are stored independently, meaning that there can be a function and a value with the same identifier.*
pub struct HashMapConfiguration {
variables: HashMap<String, Value>,
functions: HashMap<String, Function>,
}
impl HashMapConfiguration {
/// Constructs a `HashMapConfiguration` with no mappings.
pub fn new() -> Self {
Self {
variables: Default::default(),
@ -33,10 +44,16 @@ impl HashMapConfiguration {
}
}
/// Adds a variable mapping to the configuration.
///
/// *Value and function mappings are stored independently, meaning that there can be a function and a variable with the same identifier.*
pub fn insert_variable<S: Into<String>, V: Into<Value>>(&mut self, identifier: S, value: V) {
self.variables.insert(identifier.into(), value.into());
}
/// Adds a function mappign to the configuration.
///
/// *Value and function mappings are stored independently, meaning that there can be a function and a variable with the same identifier.*
pub fn insert_function<S: Into<String>>(&mut self, identifier: S, function: Function) {
self.functions.insert(identifier.into(), function);
}

View File

@ -1,20 +1,42 @@
//! The `error` module contains the `Error` enum that contains all error types used by this crate.
//!
//! The `Error` enum implements constructors for its struct variants, because those are ugly to construct.
//!
//! The module also contains some helper functions starting with `expect_` that check for a condition and return `Err(_)` if the condition is not fulfilled.
//! They are meant as shortcuts to not write the same error checking code everywhere.
use crate::value::Value;
use token::PartialToken;
/// Errors used in this crate.
#[derive(Debug, PartialEq)]
pub enum Error {
/// An operator was called with a wrong amount of arguments.
WrongOperatorArgumentAmount {
/// Expected amount of arguments.
expected: usize,
/// Actual amount of arguments.
actual: usize,
},
/// A function was called with a wrong amount of arguments.
WrongFunctionArgumentAmount {
/// Expected amount of arguments.
expected: usize,
/// Actual amount of arguments.
actual: usize,
},
/// A numeric value was expected.
/// Numeric values are the variants `Value::Int` and `Value::Float`.
ExpectedNumber {
/// Actual value.
actual: Value,
},
/// A boolean value was expected.
ExpectedBoolean {
/// Actual value.
actual: Value,
},
@ -47,35 +69,46 @@ pub enum Error {
/// A closing brace without a matching opening brace was found.
UnmatchedRBrace,
/// A `PartialToken` is unmatched, such that it cannot be combined into a full `Token`.
/// This happens if for example a single `=` is found, surrounded by whitespace.
/// It is not a token, but it is part of the string representation of some tokens.
UnmatchedPartialToken {
/// The unmatched partial token.
first: PartialToken,
/// The token that follows the unmatched partial token and that cannot be matched to the partial token, or `None`, if `first` is the last partial token in the stream.
second: Option<PartialToken>,
},
}
impl Error {
pub fn wrong_operator_argument_amount(actual: usize, expected: usize) -> Self {
pub(crate) fn wrong_operator_argument_amount(actual: usize, expected: usize) -> Self {
Error::WrongOperatorArgumentAmount { actual, expected }
}
pub fn wrong_function_argument_amount(actual: usize, expected: usize) -> Self {
pub(crate) fn wrong_function_argument_amount(actual: usize, expected: usize) -> Self {
Error::WrongFunctionArgumentAmount { actual, expected }
}
/// Constructs `Error::ExpectedNumber(actual)`.
pub fn expected_number(actual: Value) -> Self {
Error::ExpectedNumber { actual }
}
/// Constructs `Error::ExpectedBoolean(actual)`.
pub fn expected_boolean(actual: Value) -> Self {
Error::ExpectedBoolean { actual }
}
pub fn unmatched_partial_token(first: PartialToken, second: Option<PartialToken>) -> Self {
pub(crate) fn unmatched_partial_token(
first: PartialToken,
second: Option<PartialToken>,
) -> Self {
Error::UnmatchedPartialToken { first, second }
}
}
pub fn expect_operator_argument_amount(actual: usize, expected: usize) -> Result<(), Error> {
/// Returns `Ok(())` if the actual and expected parameters are equal, and `Err(Error::WrongOperatorArgumentAmount)` otherwise.
pub(crate) fn expect_operator_argument_amount(actual: usize, expected: usize) -> Result<(), Error> {
if actual == expected {
Ok(())
} else {
@ -83,7 +116,8 @@ pub fn expect_operator_argument_amount(actual: usize, expected: usize) -> Result
}
}
pub fn expect_function_argument_amount(actual: usize, expected: usize) -> Result<(), Error> {
/// Returns `Ok(())` if the actual and expected parameters are equal, and `Err(Error::WrongFunctionArgumentAmount)` otherwise.
pub(crate) fn expect_function_argument_amount(actual: usize, expected: usize) -> Result<(), Error> {
if actual == expected {
Ok(())
} else {
@ -91,6 +125,9 @@ pub fn expect_function_argument_amount(actual: usize, expected: usize) -> Result
}
}
/// Returns `Ok(())` if the given value is numeric.
/// Numeric types are `Value::Int` and `Value::Float`.
/// Otherwise, `Err(Error::ExpectedNumber)` is returned.
pub fn expect_number(actual: &Value) -> Result<(), Error> {
match actual {
Value::Float(_) | Value::Int(_) => Ok(()),
@ -98,6 +135,7 @@ pub fn expect_number(actual: &Value) -> Result<(), Error> {
}
}
/// Returns `Ok(())` if the given value is a `Value::Boolean`, or `Err(Error::ExpectedBoolean)` otherwise.
pub fn expect_boolean(actual: &Value) -> Result<bool, Error> {
match actual {
Value::Boolean(boolean) => Ok(*boolean),

View File

@ -1,14 +1,34 @@
use error::{self, Error};
use value::Value;
pub mod builtin;
pub(crate) mod builtin;
/// A user-defined function.
/// Functions can be used in expressions by storing them in a `Configuration`.
///
/// # Examples
///
/// ```rust
/// use evalexpr::*;
///
/// let mut configuration = HashMapConfiguration::new();
/// configuration.insert_function("id", Function::new(Some(1), Box::new(|arguments| {
/// Ok(arguments[0].clone())
/// })));
/// assert_eq!(eval_with_configuration("id(4)", &configuration), Ok(Value::from(4)));
/// ```
pub struct Function {
argument_amount: Option<usize>,
function: Box<Fn(&[Value]) -> Result<Value, Error>>,
}
impl Function {
/// Creates a user-defined function.
///
/// The `argument_amount` is the amount of arguments this function takes.
/// It is verified before the actual function is executed, assuming it is not `None`.
///
/// The `function` is a boxed function that takes a slice of values and returns a `Result<Value, Error>`.
pub fn new(
argument_amount: Option<usize>,
function: Box<Fn(&[Value]) -> Result<Value, Error>>,
@ -19,7 +39,7 @@ impl Function {
}
}
pub fn call(&self, arguments: &[Value]) -> Result<Value, Error> {
pub(crate) fn call(&self, arguments: &[Value]) -> Result<Value, Error> {
if let Some(argument_amount) = self.argument_amount {
error::expect_function_argument_amount(arguments.len(), argument_amount)?;
}

View File

@ -83,7 +83,7 @@ fn char_to_partial_token(c: char) -> PartialToken {
impl Token {
// Make this a const fn as soon as match gets stable (issue #57563)
pub fn is_leftsided_value(&self) -> bool {
pub(crate) fn is_leftsided_value(&self) -> bool {
match self {
Token::Plus => false,
Token::Minus => false,
@ -115,7 +115,7 @@ impl Token {
}
// Make this a const fn as soon as match gets stable (issue #57563)
pub fn is_rightsided_value(&self) -> bool {
pub(crate) fn is_rightsided_value(&self) -> bool {
match self {
Token::Plus => false,
Token::Minus => false,
@ -242,6 +242,6 @@ fn resolve_literals(mut tokens: &[PartialToken]) -> Result<Vec<Token>, Error> {
Ok(result)
}
pub fn tokenize(string: &str) -> Result<Vec<Token>, Error> {
pub(crate) fn tokenize(string: &str) -> Result<Vec<Token>, Error> {
resolve_literals(&str_to_tokens(string))
}

View File

@ -3,6 +3,19 @@ use token::Token;
mod display;
/// A node in the operator tree.
/// The operator tree is created by the crate-level `build_operator_tree` method.
/// It can be evaluated for a given configuration with the `Node::eval` method.
///
/// # Examples
///
/// ```rust
/// use evalexpr::*;
///
/// let node = build_operator_tree("1 + 2").unwrap();
/// assert_eq!(node.eval(&EmptyConfiguration), Ok(Value::from(3)));
/// ```
///
#[derive(Debug)]
pub struct Node {
children: Vec<Node>,
@ -21,6 +34,9 @@ impl Node {
Self::new(RootNode)
}
/// Evaluates the operator tree rooted at this node.
///
/// Fails, if and operator is used with a wrong number of arguments or a wrong type.
pub fn eval(&self, configuration: &Configuration) -> Result<Value, Error> {
let mut arguments = Vec::new();
for child in self.children() {
@ -29,11 +45,11 @@ impl Node {
self.operator().eval(&arguments, configuration)
}
pub fn children(&self) -> &[Node] {
fn children(&self) -> &[Node] {
&self.children
}
pub fn operator(&self) -> &Box<dyn Operator> {
fn operator(&self) -> &Box<dyn Operator> {
&self.operator
}
@ -81,7 +97,7 @@ impl Node {
}
}
pub fn tokens_to_operator_tree(tokens: Vec<Token>) -> Result<Node, Error> {
pub(crate) fn tokens_to_operator_tree(tokens: Vec<Token>) -> Result<Node, Error> {
let mut root = vec![Node::root_node()];
let mut last_token_is_rightsided_value = false;
let mut token_iter = tokens.iter().peekable();