From 38c4c35a0b48fd4d7560e12d2ce995e592485911 Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Wed, 20 Mar 2019 11:32:49 +0200 Subject: [PATCH] Document all remaining public members Implements #6 --- src/configuration/mod.rs | 17 ++++++++++++++ src/error/mod.rs | 48 +++++++++++++++++++++++++++++++++++----- src/function/mod.rs | 24 ++++++++++++++++++-- src/token/mod.rs | 6 ++--- src/tree/mod.rs | 22 +++++++++++++++--- 5 files changed, 104 insertions(+), 13 deletions(-) diff --git a/src/configuration/mod.rs b/src/configuration/mod.rs index b272de6..b4ed6b3 100644 --- a/src/configuration/mod.rs +++ b/src/configuration/mod.rs @@ -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, functions: HashMap, } 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, V: Into>(&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>(&mut self, identifier: S, function: Function) { self.functions.insert(identifier.into(), function); } diff --git a/src/error/mod.rs b/src/error/mod.rs index 39792c8..c942235 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -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, }, } 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) -> Self { + pub(crate) fn unmatched_partial_token( + first: PartialToken, + second: Option, + ) -> 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 { match actual { Value::Boolean(boolean) => Ok(*boolean), diff --git a/src/function/mod.rs b/src/function/mod.rs index baa0877..26aa75e 100644 --- a/src/function/mod.rs +++ b/src/function/mod.rs @@ -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, function: Box Result>, } 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`. pub fn new( argument_amount: Option, function: Box Result>, @@ -19,7 +39,7 @@ impl Function { } } - pub fn call(&self, arguments: &[Value]) -> Result { + pub(crate) fn call(&self, arguments: &[Value]) -> Result { if let Some(argument_amount) = self.argument_amount { error::expect_function_argument_amount(arguments.len(), argument_amount)?; } diff --git a/src/token/mod.rs b/src/token/mod.rs index bc5ca51..2dd0543 100644 --- a/src/token/mod.rs +++ b/src/token/mod.rs @@ -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, Error> { Ok(result) } -pub fn tokenize(string: &str) -> Result, Error> { +pub(crate) fn tokenize(string: &str) -> Result, Error> { resolve_literals(&str_to_tokens(string)) } diff --git a/src/tree/mod.rs b/src/tree/mod.rs index 2a9f6be..d77a1d8 100644 --- a/src/tree/mod.rs +++ b/src/tree/mod.rs @@ -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, @@ -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 { 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 { + fn operator(&self) -> &Box { &self.operator } @@ -81,7 +97,7 @@ impl Node { } } -pub fn tokens_to_operator_tree(tokens: Vec) -> Result { +pub(crate) fn tokens_to_operator_tree(tokens: Vec) -> Result { let mut root = vec![Node::root_node()]; let mut last_token_is_rightsided_value = false; let mut token_iter = tokens.iter().peekable();