From 1a8b4ac458f0c2dd120f595d2a8df68e97a0d5f8 Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Mon, 14 Oct 2024 11:53:54 +0300 Subject: [PATCH] WIP: Make ints and floats generic. --- src/context/mod.rs | 26 +++++--- src/error/display.rs | 9 ++- src/error/mod.rs | 40 +++++++----- src/function/builtin.rs | 24 +++---- src/function/mod.rs | 22 ++++--- src/interface/mod.rs | 38 +++++++---- src/operator/mod.rs | 28 +++------ src/token/display.rs | 9 ++- src/token/mod.rs | 75 ++++++++++++---------- src/tree/mod.rs | 99 ++++++++++++++++------------- src/value/display.rs | 4 +- src/value/mod.rs | 126 ++++++++++++++++++++----------------- src/value/numeric_types.rs | 60 ++++++++++++++++-- src/value/value_type.rs | 18 +++--- 14 files changed, 351 insertions(+), 227 deletions(-) diff --git a/src/context/mod.rs b/src/context/mod.rs index 8a77af8..afd75b6 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -133,7 +133,7 @@ impl Context for EmptyContext } } -impl IterateVariablesContext for EmptyContext { +impl IterateVariablesContext for EmptyContext { type VariableIterator<'a> = iter::Empty<(String, Value)> where Self: 'a; type VariableNameIterator<'a> = iter::Empty where Self: 'a; @@ -151,7 +151,9 @@ impl IterateVariablesContext for EmptyContext { #[derive(Debug, Default)] pub struct EmptyContextWithBuiltinFunctions(PhantomData); -impl Context for EmptyContextWithBuiltinFunctions { +impl Context + for EmptyContextWithBuiltinFunctions +{ type NumericTypes = NumericTypes; fn get_value(&self, _identifier: &str) -> Option<&Value> { @@ -186,7 +188,9 @@ impl Context for EmptyContextWithBuiltinFunctions { } } -impl IterateVariablesContext for EmptyContextWithBuiltinFunctions { +impl IterateVariablesContext + for EmptyContextWithBuiltinFunctions +{ type VariableIterator<'a> = iter::Empty<(String, Value)> where Self: 'a; type VariableNameIterator<'a> = iter::Empty where Self:'a; @@ -206,7 +210,7 @@ impl IterateVariablesContext for EmptyContextWithBuiltinFunctions< /// This context is type-safe, meaning that an identifier that is assigned a value of some type once cannot be assigned a value of another type. #[derive(Clone, Debug)] #[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))] -pub struct HashMapContext { +pub struct HashMapContext { variables: HashMap>, #[cfg_attr(feature = "serde_support", serde(skip))] functions: HashMap>, @@ -215,7 +219,7 @@ pub struct HashMapContext { without_builtin_functions: bool, } -impl HashMapContext { +impl HashMapContext { /// Constructs a `HashMapContext` with no mappings. pub fn new() -> Self { Default::default() @@ -265,7 +269,7 @@ impl HashMapContext { } } -impl Context for HashMapContext { +impl Context for HashMapContext { type NumericTypes = NumericTypes; fn get_value(&self, identifier: &str) -> Option<&Value> { @@ -299,7 +303,9 @@ impl Context for HashMapContext { } } -impl ContextWithMutableVariables for HashMapContext { +impl ContextWithMutableVariables + for HashMapContext +{ fn set_value( &mut self, identifier: String, @@ -320,7 +326,9 @@ impl ContextWithMutableVariables for HashMapContext } } -impl ContextWithMutableFunctions for HashMapContext { +impl ContextWithMutableFunctions + for HashMapContext +{ fn set_function( &mut self, identifier: String, @@ -350,7 +358,7 @@ impl IterateVariablesContext for HashMapCont } } -impl Default for HashMapContext { +impl Default for HashMapContext { fn default() -> Self { Self { variables: Default::default(), diff --git a/src/error/display.rs b/src/error/display.rs index faa218a..648a75a 100644 --- a/src/error/display.rs +++ b/src/error/display.rs @@ -1,8 +1,8 @@ use std::fmt; -use crate::EvalexprError; +use crate::{value::numeric_types::EvalexprNumericTypes, EvalexprError}; -impl fmt::Display for EvalexprError { +impl fmt::Display for EvalexprError { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { use crate::EvalexprError::*; match self { @@ -139,6 +139,11 @@ impl fmt::Display for EvalexprError { }, IllegalEscapeSequence(string) => write!(f, "Illegal escape sequence: {}", string), OutOfBoundsAccess => write!(f, "Tried to access a tuple or string at an invalid index"), + IntFromUsize { usize_int } => write!( + f, + "The usize {} does not fit into the chosen integer type", + usize_int + ), CustomMessage(message) => write!(f, "Error: {}", message), } } diff --git a/src/error/mod.rs b/src/error/mod.rs index 974086d..01ce8a0 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -152,9 +152,9 @@ pub enum EvalexprError /// It is not a token, but it is part of the string representation of some tokens. 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, + second: Option>, }, /// An addition operation performed by Rust failed. @@ -226,11 +226,14 @@ pub enum EvalexprError /// Out of bounds sequence access. OutOfBoundsAccess, + /// A `usize` was attempted to be converted to an `int`, but it was out of range. + IntFromUsize { usize_int: usize }, + /// A custom error explained by its message. CustomMessage(String), } -impl EvalexprError { +impl EvalexprError { /// Construct a `WrongOperatorArgumentAmount` error. pub fn wrong_operator_argument_amount(actual: usize, expected: usize) -> Self { EvalexprError::WrongOperatorArgumentAmount { actual, expected } @@ -337,8 +340,8 @@ impl EvalexprError { } pub(crate) fn unmatched_partial_token( - first: PartialToken, - second: Option, + first: PartialToken, + second: Option>, ) -> Self { EvalexprError::UnmatchedPartialToken { first, second } } @@ -392,7 +395,7 @@ impl EvalexprError { } /// Returns `Ok(())` if the actual and expected parameters are equal, and `Err(Error::WrongOperatorArgumentAmount)` otherwise. -pub(crate) fn expect_operator_argument_amount( +pub(crate) fn expect_operator_argument_amount( actual: usize, expected: usize, ) -> EvalexprResult<(), NumericTypes> { @@ -406,7 +409,7 @@ pub(crate) fn expect_operator_argument_amount( } /// Returns `Ok(())` if the actual and expected parameters are equal, and `Err(Error::WrongFunctionArgumentAmount)` otherwise. -pub fn expect_function_argument_amount( +pub fn expect_function_argument_amount( actual: usize, expected: usize, ) -> EvalexprResult<(), NumericTypes> { @@ -429,7 +432,7 @@ pub fn expect_number_or_string( } } -impl std::error::Error for EvalexprError {} +impl std::error::Error for EvalexprError {} /// Standard result type used by this crate. pub type EvalexprResult = @@ -441,13 +444,16 @@ pub type EvalexprResultValue = #[cfg(test)] mod tests { - use crate::{EvalexprError, Value, ValueType}; + use crate::{value::numeric_types::DefaultNumericTypes, EvalexprError, Value, ValueType}; /// Tests whose only use is to bring test coverage of trivial lines up, like trivial constructors. #[test] fn trivial_coverage_tests() { assert_eq!( - EvalexprError::type_error(Value::::Int(3), vec![ValueType::String]), + EvalexprError::type_error( + Value::::Int(3), + vec![ValueType::String] + ), EvalexprError::TypeError { actual: Value::Int(3), expected: vec![ValueType::String] @@ -455,22 +461,28 @@ mod tests { ); assert_eq!( EvalexprError::expected_type( - &Value::::String("abc".to_string()), + &Value::::String("abc".to_string()), Value::Empty ), EvalexprError::expected_string(Value::Empty) ); assert_eq!( - EvalexprError::expected_type(&Value::::Boolean(false), Value::Empty), + EvalexprError::expected_type( + &Value::::Boolean(false), + Value::Empty + ), EvalexprError::expected_boolean(Value::Empty) ); assert_eq!( - EvalexprError::expected_type(&Value::::Tuple(vec![]), Value::Empty), + EvalexprError::expected_type( + &Value::::Tuple(vec![]), + Value::Empty + ), EvalexprError::expected_tuple(Value::Empty) ); assert_eq!( EvalexprError::expected_type( - &Value::::Empty, + &Value::::Empty, Value::String("abc".to_string()) ), EvalexprError::expected_empty(Value::String("abc".to_string())) diff --git a/src/function/builtin.rs b/src/function/builtin.rs index 4f94fcf..f3150b0 100644 --- a/src/function/builtin.rs +++ b/src/function/builtin.rs @@ -89,10 +89,10 @@ pub fn builtin_function( "round" => simple_math!(round), "ceil" => simple_math!(ceil), // Float special values - "math::is_nan" => float_is(FloatType::is_nan), - "math::is_finite" => float_is(FloatType::is_finite), - "math::is_infinite" => float_is(FloatType::is_infinite), - "math::is_normal" => float_is(FloatType::is_normal), + "math::is_nan" => float_is(NumericTypes::float_is_nan), + "math::is_finite" => float_is(NumericTypes::float_is_finite), + "math::is_infinite" => float_is(NumericTypes::float_is_infinite), + "math::is_normal" => float_is(NumericTypes::float_is_normal), // Absolute "math::abs" => Some(Function::new(|argument| match argument { Value::Float(num) => Ok(Value::Float(num.abs())), @@ -113,8 +113,8 @@ pub fn builtin_function( })), "min" => Some(Function::new(|argument| { let arguments = argument.as_tuple()?; - let mut min_int = IntType::MAX; - let mut min_float: FloatType = 1.0 / 0.0; + let mut min_int = NumericTypes::MAX_INT; + let mut min_float = NumericTypes::MAX_FLOAT; debug_assert!(min_float.is_infinite()); for argument in arguments { @@ -127,7 +127,7 @@ pub fn builtin_function( } } - if (min_int as FloatType) < min_float { + if (NumericTypes::int_as_float(&min_int)) < min_float { Ok(Value::Int(min_int)) } else { Ok(Value::Float(min_float)) @@ -135,8 +135,8 @@ pub fn builtin_function( })), "max" => Some(Function::new(|argument| { let arguments = argument.as_tuple()?; - let mut max_int = IntType::MIN; - let mut max_float: FloatType = -1.0 / 0.0; + let mut max_int = NumericTypes::MIN_INT; + let mut max_float: NumericTypes::MIN_FLOAT; debug_assert!(max_float.is_infinite()); for argument in arguments { @@ -149,7 +149,7 @@ pub fn builtin_function( } } - if (max_int as FloatType) > max_float { + if (NumericTypes::int_as_float(&max_int)) > max_float { Ok(Value::Int(max_int)) } else { Ok(Value::Float(max_float)) @@ -216,9 +216,9 @@ pub fn builtin_function( })), "len" => Some(Function::new(|argument| { if let Ok(subject) = argument.as_string() { - Ok(Value::from(subject.len() as IntType)) + Ok(Value::from(NumericTypes::int_from_usize(subject.len()))) } else if let Ok(subject) = argument.as_tuple() { - Ok(Value::from(subject.len() as IntType)) + Ok(Value::from(NumericTypes::int_from_usize(subject.len()))) } else { Err(EvalexprError::type_error( argument.clone(), diff --git a/src/function/mod.rs b/src/function/mod.rs index c2ff983..054a810 100644 --- a/src/function/mod.rs +++ b/src/function/mod.rs @@ -1,11 +1,17 @@ use std::fmt; -use crate::{error::EvalexprResultValue, value::Value}; +use crate::{ + error::EvalexprResultValue, + value::{ + numeric_types::{DefaultNumericTypes, EvalexprNumericTypes}, + Value, + }, +}; pub(crate) mod builtin; /// A helper trait to enable cloning through `Fn` trait objects. -trait ClonableFn +trait ClonableFn where Self: Fn(&Value) -> EvalexprResultValue, Self: Send + Sync + 'static, @@ -13,7 +19,7 @@ where fn dyn_clone(&self) -> Box>; } -impl ClonableFn for F +impl ClonableFn for F where F: Fn(&Value) -> EvalexprResultValue, F: Send + Sync + 'static, @@ -38,11 +44,11 @@ where /// })).unwrap(); // Do proper error handling here /// assert_eq!(eval_with_context("id(4)", &context), Ok(Value::from(4))); /// ``` -pub struct Function { +pub struct Function { function: Box>, } -impl Clone for Function { +impl Clone for Function { fn clone(&self) -> Self { Self { function: self.function.dyn_clone(), @@ -50,7 +56,7 @@ impl Clone for Function { } } -impl Function { +impl Function { /// Creates a user-defined function. /// /// The `function` is boxed for storage. @@ -70,7 +76,7 @@ impl Function { } } -impl fmt::Debug for Function { +impl fmt::Debug for Function { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { write!(f, "Function {{ [...] }}") } @@ -82,4 +88,4 @@ impl fmt::Debug for Function { #[doc(hidden)] trait IsSendAndSync: Send + Sync {} -impl IsSendAndSync for Function {} +impl IsSendAndSync for Function {} diff --git a/src/interface/mod.rs b/src/interface/mod.rs index a3a69f6..7d32657 100644 --- a/src/interface/mod.rs +++ b/src/interface/mod.rs @@ -1,7 +1,10 @@ use crate::{ error::EvalexprResultValue, token, tree, - value::{DefaultFloatType, DefaultIntType, TupleType}, + value::{ + numeric_types::{DefaultNumericTypes, EvalexprNumericTypes}, + TupleType, + }, Context, ContextWithMutableVariables, EmptyType, EvalexprError, EvalexprResult, HashMapContext, Node, Value, EMPTY_VALUE, }; @@ -100,14 +103,18 @@ pub fn eval_string(string: &str) -> EvalexprResult { /// Evaluate the given expression string into an integer. /// /// *See the [crate doc](index.html) for more examples and explanations of the expression format.* -pub fn eval_int(string: &str) -> EvalexprResult { +pub fn eval_int( + string: &str, +) -> EvalexprResult<::Int> { eval_int_with_context_mut(string, &mut HashMapContext::new()) } /// Evaluate the given expression string into a float. /// /// *See the [crate doc](index.html) for more examples and explanations of the expression format.* -pub fn eval_float(string: &str) -> EvalexprResult { +pub fn eval_float( + string: &str, +) -> EvalexprResult<::Float> { eval_float_with_context_mut(string, &mut HashMapContext::new()) } @@ -115,7 +122,9 @@ pub fn eval_float(string: &str) -> EvalexprResult { /// If the result of the expression is an integer, it is silently converted into a float. /// /// *See the [crate doc](index.html) for more examples and explanations of the expression format.* -pub fn eval_number(string: &str) -> EvalexprResult { +pub fn eval_number( + string: &str, +) -> EvalexprResult<::Float> { eval_number_with_context_mut(string, &mut HashMapContext::new()) } @@ -154,7 +163,10 @@ pub fn eval_string_with_context(string: &str, context: &C) -> Evalex /// Evaluate the given expression string into an integer with the given context. /// /// *See the [crate doc](index.html) for more examples and explanations of the expression format.* -pub fn eval_int_with_context(string: &str, context: &C) -> EvalexprResult { +pub fn eval_int_with_context( + string: &str, + context: &C, +) -> EvalexprResult<::Int> { match eval_with_context(string, context) { Ok(Value::Int(int)) => Ok(int), Ok(value) => Err(EvalexprError::expected_int(value)), @@ -168,7 +180,7 @@ pub fn eval_int_with_context(string: &str, context: &C) -> EvalexprR pub fn eval_float_with_context( string: &str, context: &C, -) -> EvalexprResult { +) -> EvalexprResult<::Float> { match eval_with_context(string, context) { Ok(Value::Float(float)) => Ok(float), Ok(value) => Err(EvalexprError::expected_float(value)), @@ -183,10 +195,10 @@ pub fn eval_float_with_context( pub fn eval_number_with_context( string: &str, context: &C, -) -> EvalexprResult { +) -> EvalexprResult<::Float> { match eval_with_context(string, context) { Ok(Value::Float(float)) => Ok(float), - Ok(Value::Int(int)) => Ok(int as FloatType), + Ok(Value::Int(int)) => Ok(::int_as_float(int)), Ok(value) => Err(EvalexprError::expected_number(value)), Err(error) => Err(error), } @@ -245,7 +257,7 @@ pub fn eval_string_with_context_mut( pub fn eval_int_with_context_mut( string: &str, context: &mut C, -) -> EvalexprResult { +) -> EvalexprResult<::Int> { match eval_with_context_mut(string, context) { Ok(Value::Int(int)) => Ok(int), Ok(value) => Err(EvalexprError::expected_int(value)), @@ -259,7 +271,7 @@ pub fn eval_int_with_context_mut( pub fn eval_float_with_context_mut( string: &str, context: &mut C, -) -> EvalexprResult { +) -> EvalexprResult<::Float> { match eval_with_context_mut(string, context) { Ok(Value::Float(float)) => Ok(float), Ok(value) => Err(EvalexprError::expected_float(value)), @@ -274,10 +286,12 @@ pub fn eval_float_with_context_mut( pub fn eval_number_with_context_mut( string: &str, context: &mut C, -) -> EvalexprResult { +) -> EvalexprResult<::Float> { match eval_with_context_mut(string, context) { Ok(Value::Float(float)) => Ok(float), - Ok(Value::Int(int)) => Ok(int as FloatType), + Ok(Value::Int(int)) => Ok(::int_as_float( + &int, + )), Ok(value) => Err(EvalexprError::expected_number(value)), Err(error) => Err(error), } diff --git a/src/operator/mod.rs b/src/operator/mod.rs index 5bda1cc..ca42789 100644 --- a/src/operator/mod.rs +++ b/src/operator/mod.rs @@ -1,13 +1,13 @@ use crate::function::builtin::builtin_function; -use crate::value::{DefaultFloatType, DefaultIntType}; +use crate::value::numeric_types::{DefaultNumericTypes, EvalexprNumericTypes}; use crate::{context::Context, error::*, value::Value, ContextWithMutableVariables}; mod display; /// An enum that represents operators in the operator tree. #[derive(Debug, PartialEq, Clone)] -pub enum Operator { +pub enum Operator { /// A root node in the operator tree. /// The whole expression is stored under a root node, as well as each subexpression surrounded by parentheses. RootNode, @@ -73,7 +73,7 @@ pub enum Operator { /// A constant value. Const { /** The value of the constant. */ - value: Value, + value: Value, }, /// A write to a variable identifier. VariableIdentifierWrite { @@ -92,8 +92,8 @@ pub enum Operator { }, } -impl Operator { - pub(crate) fn value(value: Value) -> Self { +impl Operator { + pub(crate) fn value(value: Value) -> Self { Operator::Const { value } } @@ -179,11 +179,11 @@ impl Operator { } /// Evaluates the operator with the given arguments and context. - pub(crate) fn eval( + pub(crate) fn eval>( &self, - arguments: &[Value], + arguments: &[Value], context: &C, - ) -> EvalexprResultValue { + ) -> EvalexprResultValue { use crate::operator::Operator::*; match self { RootNode => { @@ -268,15 +268,7 @@ impl Operator { arguments[1].as_number()?; if let (Ok(a), Ok(b)) = (arguments[0].as_int(), arguments[1].as_int()) { - let result = a.checked_mul(b); - if let Some(result) = result { - Ok(Value::Int(result)) - } else { - Err(EvalexprError::multiplication_error( - arguments[0].clone(), - arguments[1].clone(), - )) - } + NumericTypes::int_checked_mul(&a, &b).map(Value::Int) } else { Ok(Value::Float( arguments[0].as_number()? * arguments[1].as_number()?, @@ -482,7 +474,7 @@ impl Operator { &self, arguments: &[Value], context: &mut C, - ) -> EvalexprResultValue { + ) -> EvalexprResultValue { use crate::operator::Operator::*; match self { Assign => { diff --git a/src/token/display.rs b/src/token/display.rs index 35f2087..8b8b65f 100644 --- a/src/token/display.rs +++ b/src/token/display.rs @@ -1,8 +1,11 @@ use std::fmt; -use crate::token::{PartialToken, Token}; +use crate::{ + token::{PartialToken, Token}, + value::numeric_types::EvalexprNumericTypes, +}; -impl fmt::Display for Token { +impl fmt::Display for Token { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { use self::Token::*; match self { @@ -53,7 +56,7 @@ impl fmt::Display for Token { } } -impl fmt::Display for PartialToken { +impl fmt::Display for PartialToken { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { use self::PartialToken::*; match self { diff --git a/src/token/mod.rs b/src/token/mod.rs index 0074d25..b7ab24c 100644 --- a/src/token/mod.rs +++ b/src/token/mod.rs @@ -1,12 +1,14 @@ +use std::str::FromStr; + use crate::{ error::{EvalexprError, EvalexprResult}, - value::{DefaultFloatType, DefaultIntType}, + value::numeric_types::{DefaultNumericTypes, EvalexprNumericTypes}, }; mod display; #[derive(Clone, PartialEq, Debug)] -pub enum Token { +pub enum Token { // Arithmetic Plus, Minus, @@ -47,17 +49,17 @@ pub enum Token { // Values, Variables and Functions Identifier(String), - Float(FloatType), - Int(IntType), + Float(NumericTypes::Float), + Int(NumericTypes::Int), Boolean(bool), String(String), } /// A partial token is an input character whose meaning depends on the characters around it. #[derive(Clone, Debug, PartialEq)] -pub enum PartialToken { +pub enum PartialToken { /// A partial token that unambiguously maps to a single token. - Token(Token), + Token(Token), /// A partial token that is a literal. Literal(String), /// A plus character '+'. @@ -89,7 +91,9 @@ pub enum PartialToken { } // Make this a const fn as soon as is_whitespace and to_string get stable (issue #57563) -fn char_to_partial_token(c: char) -> PartialToken { +fn char_to_partial_token( + c: char, +) -> PartialToken { match c { '+' => PartialToken::Plus, '-' => PartialToken::Minus, @@ -121,7 +125,7 @@ fn char_to_partial_token(c: char) -> PartialToken Token { +impl Token { #[cfg(not(tarpaulin_include))] pub(crate) const fn is_leftsided_value(&self) -> bool { match self { @@ -229,9 +233,9 @@ impl Token { } /// Parses an escape sequence within a string literal. -fn parse_escape_sequence, IntType, FloatType>( +fn parse_escape_sequence, NumericTypes: EvalexprNumericTypes>( iter: &mut Iter, -) -> EvalexprResult { +) -> EvalexprResult { match iter.next() { Some('"') => Ok('"'), Some('\\') => Ok('\\'), @@ -246,9 +250,9 @@ fn parse_escape_sequence, IntType, FloatType>( /// The string is terminated by a double quote `"`. /// Occurrences of `"` within the string can be escaped with `\`. /// The backslash needs to be escaped with another backslash `\`. -fn parse_string_literal, IntType, FloatType>( +fn parse_string_literal, NumericTypes: EvalexprNumericTypes>( mut iter: &mut Iter, -) -> EvalexprResult, IntType, FloatType> { +) -> EvalexprResult, NumericTypes> { let mut result = String::new(); while let Some(c) = iter.next() { @@ -262,9 +266,9 @@ fn parse_string_literal, IntType, FloatType>( Err(EvalexprError::UnmatchedDoubleQuote) } -fn try_skip_comment( +fn try_skip_comment( iter: &mut std::iter::Peekable>, -) -> EvalexprResult { +) -> EvalexprResult { let mut matched = false; if let Some(lookahead) = iter.peek() { if *lookahead == '/' { @@ -300,9 +304,9 @@ fn try_skip_comment( } /// Converts a string to a vector of partial tokens. -fn str_to_partial_tokens( +fn str_to_partial_tokens( string: &str, -) -> EvalexprResult, IntType, FloatType> { +) -> EvalexprResult>, NumericTypes> { let mut result = Vec::new(); let mut iter = string.chars().peekable(); @@ -337,9 +341,9 @@ fn str_to_partial_tokens( } /// Resolves all partial tokens by converting them to complex tokens. -fn partial_tokens_to_tokens( - mut tokens: &[PartialToken], -) -> EvalexprResult>, IntType, FloatType> { +fn partial_tokens_to_tokens( + mut tokens: &[PartialToken], +) -> EvalexprResult>, NumericTypes> { let mut result = Vec::new(); while !tokens.is_empty() { let first = tokens[0].clone(); @@ -396,9 +400,9 @@ fn partial_tokens_to_tokens( }, PartialToken::Literal(literal) => { cutoff = 1; - if let Ok(number) = parse_dec_or_hex(&literal) { + if let Ok(number) = parse_dec_or_hex::(&literal) { Some(Token::Int(number)) - } else if let Ok(number) = literal.parse::() { + } else if let Ok(number) = literal.parse::() { Some(Token::Float(number)) } else if let Ok(boolean) = literal.parse::() { Some(Token::Boolean(boolean)) @@ -411,8 +415,8 @@ fn partial_tokens_to_tokens( (Some(second), Some(third)) if second == PartialToken::Minus || second == PartialToken::Plus => { - if let Ok(number) = - format!("{}{}{}", literal, second, third).parse::() + if let Ok(number) = format!("{}{}{}", literal, second, third) + .parse::() { cutoff = 3; Some(Token::Float(number)) @@ -483,23 +487,28 @@ fn partial_tokens_to_tokens( Ok(result) } -pub(crate) fn tokenize( +pub(crate) fn tokenize( string: &str, -) -> EvalexprResult>, IntType, FloatType> { +) -> EvalexprResult>, NumericTypes> { partial_tokens_to_tokens(&str_to_partial_tokens(string)?) } -fn parse_dec_or_hex(literal: &str) -> Result { +fn parse_dec_or_hex( + literal: &str, +) -> Result { if let Some(literal) = literal.strip_prefix("0x") { - IntType::from_str_radix(literal, 16) + NumericTypes::int_from_hex_str(literal) } else { - IntType::from_str_radix(literal, 10) + NumericTypes::Int::from_str(literal).map_err(|_| ()) } } #[cfg(test)] mod tests { - use crate::token::{char_to_partial_token, tokenize, Token}; + use crate::{ + token::{char_to_partial_token, tokenize, Token}, + value::numeric_types::DefaultNumericTypes, + }; use std::fmt::Write; #[test] @@ -511,7 +520,7 @@ mod tests { for char in chars { assert_eq!( format!("{}", char), - format!("{}", char_to_partial_token(char)) + format!("{}", char_to_partial_token::(char)) ); } } @@ -520,7 +529,7 @@ mod tests { fn test_token_display() { let token_string = "+ - * / % ^ == != > < >= <= && || ! ( ) = += -= *= /= %= ^= &&= ||= , ; "; - let tokens = tokenize(token_string).unwrap(); + let tokens = tokenize::(token_string).unwrap(); let mut result_string = String::new(); for token in tokens { @@ -542,7 +551,7 @@ mod tests { &&= ||= , ; "; - let tokens = tokenize(token_string_with_comments).unwrap(); + let tokens = tokenize::(token_string_with_comments).unwrap(); let mut result_string = String::new(); for token in tokens { @@ -554,7 +563,7 @@ mod tests { #[test] fn assignment_lhs_is_identifier() { - let tokens = tokenize("a = 1").unwrap(); + let tokens = tokenize::("a = 1").unwrap(); assert_eq!( tokens.as_slice(), [ diff --git a/src/tree/mod.rs b/src/tree/mod.rs index 610a9a3..06450d1 100644 --- a/src/tree/mod.rs +++ b/src/tree/mod.rs @@ -1,7 +1,10 @@ use crate::{ error::EvalexprResultValue, token::Token, - value::{DefaultFloatType, DefaultIntType, TupleType, EMPTY_VALUE}, + value::{ + numeric_types::{DefaultNumericTypes, EvalexprNumericTypes}, + TupleType, EMPTY_VALUE, + }, Context, ContextWithMutableVariables, EmptyType, HashMapContext, }; @@ -35,13 +38,13 @@ mod iter; /// ``` /// #[derive(Debug, PartialEq, Clone)] -pub struct Node { - operator: Operator, - children: Vec>, +pub struct Node { + operator: Operator, + children: Vec>, } -impl Node { - fn new(operator: Operator) -> Self { +impl Node { + fn new(operator: Operator) -> Self { Self { children: Vec::new(), operator, @@ -324,7 +327,7 @@ impl Node { pub fn eval_with_context( &self, context: &C, - ) -> EvalexprResultValue { + ) -> EvalexprResultValue { let mut arguments = Vec::new(); for child in self.children() { arguments.push(child.eval_with_context(context)?); @@ -338,7 +341,7 @@ impl Node { pub fn eval_with_context_mut( &self, context: &mut C, - ) -> EvalexprResultValue { + ) -> EvalexprResultValue { let mut arguments = Vec::new(); for child in self.children() { arguments.push(child.eval_with_context_mut(context)?); @@ -349,7 +352,7 @@ impl Node { /// Evaluates the operator tree rooted at this node. /// /// Fails, if one of the operators in the expression tree fails. - pub fn eval(&self) -> EvalexprResultValue { + pub fn eval(&self) -> EvalexprResultValue { self.eval_with_context_mut(&mut HashMapContext::new()) } @@ -359,7 +362,7 @@ impl Node { pub fn eval_string_with_context( &self, context: &C, - ) -> EvalexprResult { + ) -> EvalexprResult { match self.eval_with_context(context) { Ok(Value::String(string)) => Ok(string), Ok(value) => Err(EvalexprError::expected_string(value)), @@ -373,7 +376,7 @@ impl Node { pub fn eval_float_with_context( &self, context: &C, - ) -> EvalexprResult { + ) -> EvalexprResult<::Float, C::NumericTypes> { match self.eval_with_context(context) { Ok(Value::Float(float)) => Ok(float), Ok(value) => Err(EvalexprError::expected_float(value)), @@ -387,7 +390,7 @@ impl Node { pub fn eval_int_with_context( &self, context: &C, - ) -> EvalexprResult { + ) -> EvalexprResult<::Int, C::NumericTypes> { match self.eval_with_context(context) { Ok(Value::Int(int)) => Ok(int), Ok(value) => Err(EvalexprError::expected_int(value)), @@ -402,9 +405,9 @@ impl Node { pub fn eval_number_with_context( &self, context: &C, - ) -> EvalexprResult { + ) -> EvalexprResult<::Float, C::NumericTypes> { match self.eval_with_context(context) { - Ok(Value::Int(int)) => Ok(int as FloatType), + Ok(Value::Int(int)) => Ok(C::NumericTypes::int_as_float(&int)), Ok(Value::Float(float)) => Ok(float), Ok(value) => Err(EvalexprError::expected_number(value)), Err(error) => Err(error), @@ -417,7 +420,7 @@ impl Node { pub fn eval_boolean_with_context( &self, context: &C, - ) -> EvalexprResult { + ) -> EvalexprResult { match self.eval_with_context(context) { Ok(Value::Boolean(boolean)) => Ok(boolean), Ok(value) => Err(EvalexprError::expected_boolean(value)), @@ -431,7 +434,7 @@ impl Node { pub fn eval_tuple_with_context( &self, context: &C, - ) -> EvalexprResult { + ) -> EvalexprResult { match self.eval_with_context(context) { Ok(Value::Tuple(tuple)) => Ok(tuple), Ok(value) => Err(EvalexprError::expected_tuple(value)), @@ -445,7 +448,7 @@ impl Node { pub fn eval_empty_with_context( &self, context: &C, - ) -> EvalexprResult { + ) -> EvalexprResult { match self.eval_with_context(context) { Ok(Value::Empty) => Ok(EMPTY_VALUE), Ok(value) => Err(EvalexprError::expected_empty(value)), @@ -459,7 +462,7 @@ impl Node { pub fn eval_string_with_context_mut( &self, context: &mut C, - ) -> EvalexprResult { + ) -> EvalexprResult { match self.eval_with_context_mut(context) { Ok(Value::String(string)) => Ok(string), Ok(value) => Err(EvalexprError::expected_string(value)), @@ -473,7 +476,7 @@ impl Node { pub fn eval_float_with_context_mut( &self, context: &mut C, - ) -> EvalexprResult { + ) -> EvalexprResult<::Float, C::NumericTypes> { match self.eval_with_context_mut(context) { Ok(Value::Float(float)) => Ok(float), Ok(value) => Err(EvalexprError::expected_float(value)), @@ -487,7 +490,7 @@ impl Node { pub fn eval_int_with_context_mut( &self, context: &mut C, - ) -> EvalexprResult { + ) -> EvalexprResult<::Int, C::NumericTypes> { match self.eval_with_context_mut(context) { Ok(Value::Int(int)) => Ok(int), Ok(value) => Err(EvalexprError::expected_int(value)), @@ -502,9 +505,11 @@ impl Node { pub fn eval_number_with_context_mut( &self, context: &mut C, - ) -> EvalexprResult { + ) -> EvalexprResult<::Float, C::NumericTypes> { match self.eval_with_context_mut(context) { - Ok(Value::Int(int)) => Ok(int as FloatType), + Ok(Value::Int(int)) => Ok(::int_as_float( + &int, + )), Ok(Value::Float(float)) => Ok(float), Ok(value) => Err(EvalexprError::expected_number(value)), Err(error) => Err(error), @@ -517,7 +522,7 @@ impl Node { pub fn eval_boolean_with_context_mut( &self, context: &mut C, - ) -> EvalexprResult { + ) -> EvalexprResult { match self.eval_with_context_mut(context) { Ok(Value::Boolean(boolean)) => Ok(boolean), Ok(value) => Err(EvalexprError::expected_boolean(value)), @@ -531,7 +536,7 @@ impl Node { pub fn eval_tuple_with_context_mut( &self, context: &mut C, - ) -> EvalexprResult { + ) -> EvalexprResult { match self.eval_with_context_mut(context) { Ok(Value::Tuple(tuple)) => Ok(tuple), Ok(value) => Err(EvalexprError::expected_tuple(value)), @@ -545,7 +550,7 @@ impl Node { pub fn eval_empty_with_context_mut( &self, context: &mut C, - ) -> EvalexprResult { + ) -> EvalexprResult { match self.eval_with_context_mut(context) { Ok(Value::Empty) => Ok(EMPTY_VALUE), Ok(value) => Err(EvalexprError::expected_empty(value)), @@ -556,21 +561,25 @@ impl Node { /// Evaluates the operator tree rooted at this node into a string. /// /// Fails, if one of the operators in the expression tree fails. - pub fn eval_string(&self) -> EvalexprResult { + pub fn eval_string(&self) -> EvalexprResult { self.eval_string_with_context_mut(&mut HashMapContext::new()) } /// Evaluates the operator tree rooted at this node into a float. /// /// Fails, if one of the operators in the expression tree fails. - pub fn eval_float(&self) -> EvalexprResult { + pub fn eval_float( + &self, + ) -> EvalexprResult<::Float, NumericTypes> { self.eval_float_with_context_mut(&mut HashMapContext::new()) } /// Evaluates the operator tree rooted at this node into an integer. /// /// Fails, if one of the operators in the expression tree fails. - pub fn eval_int(&self) -> EvalexprResult { + pub fn eval_int( + &self, + ) -> EvalexprResult<::Int, NumericTypes> { self.eval_int_with_context_mut(&mut HashMapContext::new()) } @@ -578,28 +587,30 @@ impl Node { /// If the result of the expression is an integer, it is silently converted into a float. /// /// Fails, if one of the operators in the expression tree fails. - pub fn eval_number(&self) -> EvalexprResult { + pub fn eval_number( + &self, + ) -> EvalexprResult<::Float, NumericTypes> { self.eval_number_with_context_mut(&mut HashMapContext::new()) } /// Evaluates the operator tree rooted at this node into a boolean. /// /// Fails, if one of the operators in the expression tree fails. - pub fn eval_boolean(&self) -> EvalexprResult { + pub fn eval_boolean(&self) -> EvalexprResult { self.eval_boolean_with_context_mut(&mut HashMapContext::new()) } /// Evaluates the operator tree rooted at this node into a tuple. /// /// Fails, if one of the operators in the expression tree fails. - pub fn eval_tuple(&self) -> EvalexprResult { + pub fn eval_tuple(&self) -> EvalexprResult { self.eval_tuple_with_context_mut(&mut HashMapContext::new()) } /// Evaluates the operator tree rooted at this node into an empty value. /// /// Fails, if one of the operators in the expression tree fails. - pub fn eval_empty(&self) -> EvalexprResult { + pub fn eval_empty(&self) -> EvalexprResult { self.eval_empty_with_context_mut(&mut HashMapContext::new()) } @@ -643,7 +654,7 @@ impl Node { &mut self, node: Node, is_root_node: bool, - ) -> EvalexprResult<(), IntType, FloatType> { + ) -> EvalexprResult<(), NumericTypes> { // println!( // "Inserting {:?} into {:?}, is_root_node = {is_root_node}", // node.operator(), @@ -724,11 +735,11 @@ impl Node { } } -fn collapse_root_stack_to( - root_stack: &mut Vec>, - mut root: Node, - collapse_goal: &Node, -) -> EvalexprResult, IntType, FloatType> { +fn collapse_root_stack_to( + root_stack: &mut Vec>, + mut root: Node, + collapse_goal: &Node, +) -> EvalexprResult, NumericTypes> { loop { if let Some(mut potential_higher_root) = root_stack.pop() { // TODO I'm not sure about this >, as I have no example for different sequence operators with the same precedence @@ -749,9 +760,9 @@ fn collapse_root_stack_to( Ok(root) } -fn collapse_all_sequences( - root_stack: &mut Vec>, -) -> EvalexprResult<(), IntType, FloatType> { +fn collapse_all_sequences( + root_stack: &mut Vec>, +) -> EvalexprResult<(), NumericTypes> { // println!("Collapsing all sequences"); // println!("Initial root stack is: {:?}", root_stack); let mut root = if let Some(root) = root_stack.pop() { @@ -796,9 +807,9 @@ fn collapse_all_sequences( Ok(()) } -pub(crate) fn tokens_to_operator_tree( - tokens: Vec>, -) -> EvalexprResult { +pub(crate) fn tokens_to_operator_tree( + tokens: Vec>, +) -> EvalexprResult { let mut root_stack = vec![Node::root_node()]; let mut last_token_is_rightsided_value = false; let mut token_iter = tokens.iter().peekable(); diff --git a/src/value/display.rs b/src/value/display.rs index 599baa4..2b311a6 100644 --- a/src/value/display.rs +++ b/src/value/display.rs @@ -2,7 +2,9 @@ use std::fmt::{Display, Error, Formatter}; use crate::Value; -impl Display for Value { +use super::numeric_types::EvalexprNumericTypes; + +impl Display for Value { fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { match self { Value::String(string) => write!(f, "\"{}\"", string), diff --git a/src/value/mod.rs b/src/value/mod.rs index a2f1cc5..aee6c3e 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -71,7 +71,7 @@ impl Value { } /// Clones the value stored in `self` as `String`, or returns `Err` if `self` is not a `Value::String`. - pub fn as_string(&self) -> EvalexprResult { + pub fn as_string(&self) -> EvalexprResult { match self { Value::String(string) => Ok(string.clone()), value => Err(EvalexprError::expected_string(value.clone())), @@ -79,7 +79,7 @@ impl Value { } /// Clones the value stored in `self` as `IntType`, or returns `Err` if `self` is not a `Value::Int`. - pub fn as_int(&self) -> EvalexprResult { + pub fn as_int(&self) -> EvalexprResult { match self { Value::Int(i) => Ok(*i), value => Err(EvalexprError::expected_int(value.clone())), @@ -87,7 +87,7 @@ impl Value { } /// Clones the value stored in `self` as `FloatType`, or returns `Err` if `self` is not a `Value::Float`. - pub fn as_float(&self) -> EvalexprResult { + pub fn as_float(&self) -> EvalexprResult { match self { Value::Float(f) => Ok(*f), value => Err(EvalexprError::expected_float(value.clone())), @@ -96,16 +96,16 @@ impl Value { /// Clones the value stored in `self` as `FloatType`, or returns `Err` if `self` is not a `Value::Float` or `Value::Int`. /// Note that this method silently converts `IntType` to `FloatType`, if `self` is a `Value::Int`. - pub fn as_number(&self) -> EvalexprResult { + pub fn as_number(&self) -> EvalexprResult { match self { Value::Float(f) => Ok(f.clone()), - Value::Int(i) => Ok(i.as_float()), + Value::Int(i) => Ok(NumericTypes::int_as_float(i)), value => Err(EvalexprError::expected_number(value.clone())), } } /// Clones the value stored in `self` as `bool`, or returns `Err` if `self` is not a `Value::Boolean`. - pub fn as_boolean(&self) -> EvalexprResult { + pub fn as_boolean(&self) -> EvalexprResult { match self { Value::Boolean(boolean) => Ok(*boolean), value => Err(EvalexprError::expected_boolean(value.clone())), @@ -113,7 +113,7 @@ impl Value { } /// Clones the value stored in `self` as `TupleType`, or returns `Err` if `self` is not a `Value::Tuple`. - pub fn as_tuple(&self) -> EvalexprResult { + pub fn as_tuple(&self) -> EvalexprResult { match self { Value::Tuple(tuple) => Ok(tuple.clone()), value => Err(EvalexprError::expected_tuple(value.clone())), @@ -121,7 +121,7 @@ impl Value { } /// Clones the value stored in `self` as `TupleType` or returns `Err` if `self` is not a `Value::Tuple` of the required length. - pub fn as_fixed_len_tuple(&self, len: usize) -> EvalexprResult { + pub fn as_fixed_len_tuple(&self, len: usize) -> EvalexprResult { match self { Value::Tuple(tuple) => { if tuple.len() == len { @@ -138,7 +138,7 @@ impl Value { pub fn as_ranged_len_tuple( &self, range: RangeInclusive, - ) -> EvalexprResult { + ) -> EvalexprResult { match self { Value::Tuple(tuple) => { if range.contains(&tuple.len()) { @@ -155,7 +155,7 @@ impl Value { } /// Returns `()`, or returns`Err` if `self` is not a `Value::Tuple`. - pub fn as_empty(&self) -> EvalexprResult<(), IntType, FloatType> { + pub fn as_empty(&self) -> EvalexprResult<(), NumericTypes> { match self { Value::Empty => Ok(()), value => Err(EvalexprError::expected_empty(value.clone())), @@ -174,57 +174,57 @@ impl Value { } } - pub fn from_float(float: FloatType) -> Self { + pub fn from_float(float: NumericTypes::Float) -> Self { Self::Float(float) } - pub fn from_int(int: IntType) -> Self { + pub fn from_int(int: NumericTypes::Int) -> Self { Self::Int(int) } } -impl From for Value { +impl From for Value { fn from(string: String) -> Self { Value::String(string) } } -impl From<&str> for Value { +impl From<&str> for Value { fn from(string: &str) -> Self { Value::String(string.to_string()) } } -impl From for Value { +impl From for Value { fn from(boolean: bool) -> Self { Value::Boolean(boolean) } } -impl From for Value { +impl From for Value { fn from(tuple: TupleType) -> Self { Value::Tuple(tuple) } } -impl From> - for EvalexprResultValue +impl From> + for EvalexprResultValue { - fn from(value: Value) -> Self { + fn from(value: Value) -> Self { Ok(value) } } -impl From<()> for Value { +impl From<()> for Value { fn from(_: ()) -> Self { Value::Empty } } -impl TryFrom> for String { - type Error = EvalexprError; +impl TryFrom> for String { + type Error = EvalexprError; - fn try_from(value: Value) -> Result { + fn try_from(value: Value) -> Result { if let Value::String(value) = value { Ok(value) } else { @@ -233,10 +233,10 @@ impl TryFrom> for String { } } -impl TryFrom> for bool { - type Error = EvalexprError; +impl TryFrom> for bool { + type Error = EvalexprError; - fn try_from(value: Value) -> Result { + fn try_from(value: Value) -> Result { if let Value::Boolean(value) = value { Ok(value) } else { @@ -245,10 +245,10 @@ impl TryFrom> for bool { } } -impl TryFrom> for TupleType { - type Error = EvalexprError; +impl TryFrom> for TupleType { + type Error = EvalexprError; - fn try_from(value: Value) -> Result { + fn try_from(value: Value) -> Result { if let Value::Tuple(value) = value { Ok(value) } else { @@ -257,10 +257,10 @@ impl TryFrom> for TupleType { } } -impl TryFrom> for () { - type Error = EvalexprError; +impl TryFrom> for () { + type Error = EvalexprError; - fn try_from(value: Value) -> Result { + fn try_from(value: Value) -> Result { if let Value::Empty = value { Ok(()) } else { @@ -271,50 +271,62 @@ impl TryFrom> for () { #[cfg(test)] mod tests { - use crate::value::{TupleType, Value}; + use crate::value::{numeric_types::DefaultNumericTypes, TupleType, Value}; #[test] fn test_value_conversions() { assert_eq!( - Value::::from("string").as_string(), + Value::::from("string").as_string(), Ok(String::from("string")) ); - assert_eq!(Value::::from_int(3).as_int(), Ok(3)); - assert_eq!(Value::::from_float(3.3).as_float(), Ok(3.3)); - assert_eq!(Value::::from(true).as_boolean(), Ok(true)); + assert_eq!(Value::::from_int(3).as_int(), Ok(3)); assert_eq!( - Value::::from(TupleType::new()).as_tuple(), + Value::::from_float(3.3).as_float(), + Ok(3.3) + ); + assert_eq!( + Value::::from(true).as_boolean(), + Ok(true) + ); + assert_eq!( + Value::::from(TupleType::new()).as_tuple(), Ok(TupleType::new()) ); } #[test] fn test_value_checks() { - assert!(Value::::from("string").is_string()); - assert!(Value::::from_int(3).is_int()); - assert!(Value::::from_float(3.3).is_float()); - assert!(Value::::from(true).is_boolean()); - assert!(Value::::from(TupleType::new()).is_tuple()); + assert!(Value::::from("string").is_string()); + assert!(Value::::from_int(3).is_int()); + assert!(Value::::from_float(3.3).is_float()); + assert!(Value::::from(true).is_boolean()); + assert!(Value::::from(TupleType::new()).is_tuple()); } #[test] fn test_value_str_from() { - assert_eq!(Value::::from("string").str_from(), "string"); - assert_eq!(Value::::from_float(3.3).str_from(), "3.3"); - assert_eq!(Value::::from_int(3).str_from(), "3"); - assert_eq!(Value::::from(true).str_from(), "true"); - assert_eq!(Value::::from(()).str_from(), "()"); assert_eq!( - Value::::from(TupleType::from([ - Value::::from("string"), - Value::::from_float(3.3), - Value::::from_int(3), - Value::::from(TupleType::from([ - Value::::from_int(42), - Value::::from_float(4.2), + Value::::from("string").str_from(), + "string" + ); + assert_eq!( + Value::::from_float(3.3).str_from(), + "3.3" + ); + assert_eq!(Value::::from_int(3).str_from(), "3"); + assert_eq!(Value::::from(true).str_from(), "true"); + assert_eq!(Value::::from(()).str_from(), "()"); + assert_eq!( + Value::::from(TupleType::from([ + Value::::from("string"), + Value::::from_float(3.3), + Value::::from_int(3), + Value::::from(TupleType::from([ + Value::::from_int(42), + Value::::from_float(4.2), ])), - Value::::from(()), - Value::::from(true), + Value::::from(()), + Value::::from(true), ])) .str_from(), r#"("string", 3.3, 3, (42, 4.2), (), true)"# diff --git a/src/value/numeric_types.rs b/src/value/numeric_types.rs index 414948e..dafb78e 100644 --- a/src/value/numeric_types.rs +++ b/src/value/numeric_types.rs @@ -1,26 +1,74 @@ -pub trait EvalexprNumericTypes { - type Int: Clone; - type Float: Clone; +use std::{ + convert::TryInto, + fmt::{Debug, Display}, + ops::Mul, + str::FromStr, +}; + +use crate::{EvalexprError, EvalexprResult, Value}; + +pub trait EvalexprNumericTypes: Sized + Debug + Clone + PartialEq { + type Int: Clone + Debug + Display + FromStr + Eq; + type Float: Clone + Debug + Display + FromStr + PartialEq + Mul; + + const MIN_INT: Self::Int; + const MIN_FLOAT: Self::Float; + const MAX_INT: Self::Int; + const MAX_FLOAT: Self::Float; /// Convert an integer to a float using the `as` operator or a similar mechanic. fn int_as_float(int: &Self::Int) -> Self::Float; /// Convert a float to an integer using the `as` operator or a similar mechanic. fn float_as_int(float: &Self::Float) -> Self::Int; + + /// Convert a value of type [`usize`] into a [`Self::Int`]. + fn int_from_usize(int: usize) -> EvalexprResult; + + /// Parse an integer from a hex string. + fn int_from_hex_str(literal: &str) -> Result; + + fn int_checked_mul(a: &Self::Int, b: &Self::Int) -> EvalexprResult; } +#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] pub struct DefaultNumericTypes; impl EvalexprNumericTypes for DefaultNumericTypes { type Int = i64; - type Float = f64; + const MIN_INT: Self::Int = Self::Int::MIN; + const MIN_FLOAT: Self::Float = Self::Float::NEG_INFINITY; + const MAX_INT: Self::Int = Self::Int::MAX; + const MAX_FLOAT: Self::Float = Self::Float::INFINITY; + fn int_as_float(int: &Self::Int) -> Self::Float { - int as Self::Float + *int as Self::Float } fn float_as_int(float: &Self::Float) -> Self::Int { - float as Self::Int + *float as Self::Int + } + + fn int_from_usize(int: usize) -> EvalexprResult { + int.try_into() + .map_err(|_| EvalexprError::IntFromUsize { usize_int: int }) + } + + fn int_from_hex_str(literal: &str) -> Result { + Self::Int::from_str_radix(literal, 16).map_err(|_| ()) + } + + fn int_checked_mul(a: &Self::Int, b: &Self::Int) -> EvalexprResult { + let result = a.checked_mul(*b); + if let Some(result) = result { + Ok(result) + } else { + Err(EvalexprError::multiplication_error( + Value::::from_int(*a), + Value::::from_int(*b), + )) + } } } diff --git a/src/value/value_type.rs b/src/value/value_type.rs index a3186e8..a2d86bf 100644 --- a/src/value/value_type.rs +++ b/src/value/value_type.rs @@ -1,5 +1,7 @@ use crate::Value; +use super::numeric_types::EvalexprNumericTypes; + /// The type of a `Value`. #[derive(Clone, Copy, Eq, PartialEq, Debug)] pub enum ValueType { @@ -17,8 +19,8 @@ pub enum ValueType { Empty, } -impl From<&Value> for ValueType { - fn from(value: &Value) -> Self { +impl From<&Value> for ValueType { + fn from(value: &Value) -> Self { match value { Value::String(_) => ValueType::String, Value::Float(_) => ValueType::Float, @@ -30,14 +32,14 @@ impl From<&Value> for ValueType { } } -impl From<&mut Value> for ValueType { - fn from(value: &mut Value) -> Self { - From::<&Value>::from(value) +impl From<&mut Value> for ValueType { + fn from(value: &mut Value) -> Self { + From::<&Value>::from(value) } } -impl From<&&mut Value> for ValueType { - fn from(value: &&mut Value) -> Self { - From::<&Value>::from(*value) +impl From<&&mut Value> for ValueType { + fn from(value: &&mut Value) -> Self { + From::<&Value>::from(*value) } }