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 function::Function;
use std::collections::HashMap; 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 { pub trait Configuration {
/// Returns the value that is linked to the given identifier.
fn get_value(&self, identifier: &str) -> Option<&Value>; 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>; fn get_function(&self, identifier: &str) -> Option<&Function>;
} }
/// A configuration that returns `None` for each identifier.
pub struct EmptyConfiguration; pub struct EmptyConfiguration;
impl Configuration for 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 { pub struct HashMapConfiguration {
variables: HashMap<String, Value>, variables: HashMap<String, Value>,
functions: HashMap<String, Function>, functions: HashMap<String, Function>,
} }
impl HashMapConfiguration { impl HashMapConfiguration {
/// Constructs a `HashMapConfiguration` with no mappings.
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
variables: Default::default(), 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) { pub fn insert_variable<S: Into<String>, V: Into<Value>>(&mut self, identifier: S, value: V) {
self.variables.insert(identifier.into(), value.into()); 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) { pub fn insert_function<S: Into<String>>(&mut self, identifier: S, function: Function) {
self.functions.insert(identifier.into(), 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 crate::value::Value;
use token::PartialToken; use token::PartialToken;
/// Errors used in this crate.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum Error { pub enum Error {
/// An operator was called with a wrong amount of arguments.
WrongOperatorArgumentAmount { WrongOperatorArgumentAmount {
/// Expected amount of arguments.
expected: usize, expected: usize,
/// Actual amount of arguments.
actual: usize, actual: usize,
}, },
/// A function was called with a wrong amount of arguments.
WrongFunctionArgumentAmount { WrongFunctionArgumentAmount {
/// Expected amount of arguments.
expected: usize, expected: usize,
/// Actual amount of arguments.
actual: usize, actual: usize,
}, },
/// A numeric value was expected.
/// Numeric values are the variants `Value::Int` and `Value::Float`.
ExpectedNumber { ExpectedNumber {
/// Actual value.
actual: Value, actual: Value,
}, },
/// A boolean value was expected.
ExpectedBoolean { ExpectedBoolean {
/// Actual value.
actual: Value, actual: Value,
}, },
@ -47,35 +69,46 @@ pub enum Error {
/// A closing brace without a matching opening brace was found. /// A closing brace without a matching opening brace was found.
UnmatchedRBrace, 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 { UnmatchedPartialToken {
/// The unmatched partial token.
first: PartialToken, 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>, second: Option<PartialToken>,
}, },
} }
impl Error { 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 } 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 } Error::WrongFunctionArgumentAmount { actual, expected }
} }
/// Constructs `Error::ExpectedNumber(actual)`.
pub fn expected_number(actual: Value) -> Self { pub fn expected_number(actual: Value) -> Self {
Error::ExpectedNumber { actual } Error::ExpectedNumber { actual }
} }
/// Constructs `Error::ExpectedBoolean(actual)`.
pub fn expected_boolean(actual: Value) -> Self { pub fn expected_boolean(actual: Value) -> Self {
Error::ExpectedBoolean { actual } 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 } 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 { if actual == expected {
Ok(()) Ok(())
} else { } 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 { if actual == expected {
Ok(()) Ok(())
} else { } 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> { pub fn expect_number(actual: &Value) -> Result<(), Error> {
match actual { match actual {
Value::Float(_) | Value::Int(_) => Ok(()), 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> { pub fn expect_boolean(actual: &Value) -> Result<bool, Error> {
match actual { match actual {
Value::Boolean(boolean) => Ok(*boolean), Value::Boolean(boolean) => Ok(*boolean),

View File

@ -1,14 +1,34 @@
use error::{self, Error}; use error::{self, Error};
use value::Value; 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 { pub struct Function {
argument_amount: Option<usize>, argument_amount: Option<usize>,
function: Box<Fn(&[Value]) -> Result<Value, Error>>, function: Box<Fn(&[Value]) -> Result<Value, Error>>,
} }
impl Function { 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( pub fn new(
argument_amount: Option<usize>, argument_amount: Option<usize>,
function: Box<Fn(&[Value]) -> Result<Value, Error>>, 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 { if let Some(argument_amount) = self.argument_amount {
error::expect_function_argument_amount(arguments.len(), 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 { impl Token {
// Make this a const fn as soon as match gets stable (issue #57563) // 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 { match self {
Token::Plus => false, Token::Plus => false,
Token::Minus => false, Token::Minus => false,
@ -115,7 +115,7 @@ impl Token {
} }
// Make this a const fn as soon as match gets stable (issue #57563) // 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 { match self {
Token::Plus => false, Token::Plus => false,
Token::Minus => false, Token::Minus => false,
@ -242,6 +242,6 @@ fn resolve_literals(mut tokens: &[PartialToken]) -> Result<Vec<Token>, Error> {
Ok(result) 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)) resolve_literals(&str_to_tokens(string))
} }

View File

@ -3,6 +3,19 @@ use token::Token;
mod display; 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)] #[derive(Debug)]
pub struct Node { pub struct Node {
children: Vec<Node>, children: Vec<Node>,
@ -21,6 +34,9 @@ impl Node {
Self::new(RootNode) 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> { pub fn eval(&self, configuration: &Configuration) -> Result<Value, Error> {
let mut arguments = Vec::new(); let mut arguments = Vec::new();
for child in self.children() { for child in self.children() {
@ -29,11 +45,11 @@ impl Node {
self.operator().eval(&arguments, configuration) self.operator().eval(&arguments, configuration)
} }
pub fn children(&self) -> &[Node] { fn children(&self) -> &[Node] {
&self.children &self.children
} }
pub fn operator(&self) -> &Box<dyn Operator> { fn operator(&self) -> &Box<dyn Operator> {
&self.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 root = vec![Node::root_node()];
let mut last_token_is_rightsided_value = false; let mut last_token_is_rightsided_value = false;
let mut token_iter = tokens.iter().peekable(); let mut token_iter = tokens.iter().peekable();