Skip to content

Commit

Permalink
WIP: Make ints and floats generic.
Browse files Browse the repository at this point in the history
  • Loading branch information
ISibboI committed Oct 14, 2024
1 parent 79c8ae5 commit 1a8b4ac
Show file tree
Hide file tree
Showing 14 changed files with 351 additions and 227 deletions.
26 changes: 17 additions & 9 deletions src/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ impl<NumericTypes: EvalexprNumericTypes> Context for EmptyContext<NumericTypes>
}
}

impl<NumericTypes> IterateVariablesContext for EmptyContext<NumericTypes> {
impl<NumericTypes: EvalexprNumericTypes> IterateVariablesContext for EmptyContext<NumericTypes> {
type VariableIterator<'a> = iter::Empty<(String, Value<Self::NumericTypes>)> where Self: 'a;
type VariableNameIterator<'a> = iter::Empty<String> where Self: 'a;

Expand All @@ -151,7 +151,9 @@ impl<NumericTypes> IterateVariablesContext for EmptyContext<NumericTypes> {
#[derive(Debug, Default)]
pub struct EmptyContextWithBuiltinFunctions<NumericTypes>(PhantomData<NumericTypes>);

impl<NumericTypes> Context for EmptyContextWithBuiltinFunctions<NumericTypes> {
impl<NumericTypes: EvalexprNumericTypes> Context
for EmptyContextWithBuiltinFunctions<NumericTypes>
{
type NumericTypes = NumericTypes;

fn get_value(&self, _identifier: &str) -> Option<&Value<Self::NumericTypes>> {
Expand Down Expand Up @@ -186,7 +188,9 @@ impl<NumericTypes> Context for EmptyContextWithBuiltinFunctions<NumericTypes> {
}
}

impl<NumericTypes> IterateVariablesContext for EmptyContextWithBuiltinFunctions<NumericTypes> {
impl<NumericTypes: EvalexprNumericTypes> IterateVariablesContext
for EmptyContextWithBuiltinFunctions<NumericTypes>
{
type VariableIterator<'a> = iter::Empty<(String, Value<Self::NumericTypes>)> where Self: 'a;
type VariableNameIterator<'a> = iter::Empty<String> where Self:'a;

Expand All @@ -206,7 +210,7 @@ impl<NumericTypes> 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<NumericTypes> {
pub struct HashMapContext<NumericTypes: EvalexprNumericTypes> {
variables: HashMap<String, Value<NumericTypes>>,
#[cfg_attr(feature = "serde_support", serde(skip))]
functions: HashMap<String, Function<NumericTypes>>,
Expand All @@ -215,7 +219,7 @@ pub struct HashMapContext<NumericTypes> {
without_builtin_functions: bool,
}

impl<NumericTypes> HashMapContext<NumericTypes> {
impl<NumericTypes: EvalexprNumericTypes> HashMapContext<NumericTypes> {
/// Constructs a `HashMapContext` with no mappings.
pub fn new() -> Self {
Default::default()
Expand Down Expand Up @@ -265,7 +269,7 @@ impl<NumericTypes> HashMapContext<NumericTypes> {
}
}

impl<NumericTypes> Context for HashMapContext<NumericTypes> {
impl<NumericTypes: EvalexprNumericTypes> Context for HashMapContext<NumericTypes> {
type NumericTypes = NumericTypes;

fn get_value(&self, identifier: &str) -> Option<&Value<Self::NumericTypes>> {
Expand Down Expand Up @@ -299,7 +303,9 @@ impl<NumericTypes> Context for HashMapContext<NumericTypes> {
}
}

impl<NumericTypes> ContextWithMutableVariables for HashMapContext<NumericTypes> {
impl<NumericTypes: EvalexprNumericTypes> ContextWithMutableVariables
for HashMapContext<NumericTypes>
{
fn set_value(
&mut self,
identifier: String,
Expand All @@ -320,7 +326,9 @@ impl<NumericTypes> ContextWithMutableVariables for HashMapContext<NumericTypes>
}
}

impl<NumericTypes> ContextWithMutableFunctions for HashMapContext<NumericTypes> {
impl<NumericTypes: EvalexprNumericTypes> ContextWithMutableFunctions
for HashMapContext<NumericTypes>
{
fn set_function(
&mut self,
identifier: String,
Expand Down Expand Up @@ -350,7 +358,7 @@ impl<NumericTypes: EvalexprNumericTypes> IterateVariablesContext for HashMapCont
}
}

impl<NumericTypes> Default for HashMapContext<NumericTypes> {
impl<NumericTypes: EvalexprNumericTypes> Default for HashMapContext<NumericTypes> {
fn default() -> Self {
Self {
variables: Default::default(),
Expand Down
9 changes: 7 additions & 2 deletions src/error/display.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use std::fmt;

use crate::EvalexprError;
use crate::{value::numeric_types::EvalexprNumericTypes, EvalexprError};

impl fmt::Display for EvalexprError {
impl<NumericTypes: EvalexprNumericTypes> fmt::Display for EvalexprError<NumericTypes> {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
use crate::EvalexprError::*;
match self {
Expand Down Expand Up @@ -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),
}
}
Expand Down
40 changes: 26 additions & 14 deletions src/error/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,9 @@ pub enum EvalexprError<NumericTypes: EvalexprNumericTypes = DefaultNumericTypes>
/// 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<NumericTypes>,
/// 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<NumericTypes>>,
},

/// An addition operation performed by Rust failed.
Expand Down Expand Up @@ -226,11 +226,14 @@ pub enum EvalexprError<NumericTypes: EvalexprNumericTypes = DefaultNumericTypes>
/// 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<NumericTypes> EvalexprError<NumericTypes> {
impl<NumericTypes: EvalexprNumericTypes> EvalexprError<NumericTypes> {
/// Construct a `WrongOperatorArgumentAmount` error.
pub fn wrong_operator_argument_amount(actual: usize, expected: usize) -> Self {
EvalexprError::WrongOperatorArgumentAmount { actual, expected }
Expand Down Expand Up @@ -337,8 +340,8 @@ impl<NumericTypes> EvalexprError<NumericTypes> {
}

pub(crate) fn unmatched_partial_token(
first: PartialToken,
second: Option<PartialToken>,
first: PartialToken<NumericTypes>,
second: Option<PartialToken<NumericTypes>>,
) -> Self {
EvalexprError::UnmatchedPartialToken { first, second }
}
Expand Down Expand Up @@ -392,7 +395,7 @@ impl<NumericTypes> EvalexprError<NumericTypes> {
}

/// Returns `Ok(())` if the actual and expected parameters are equal, and `Err(Error::WrongOperatorArgumentAmount)` otherwise.
pub(crate) fn expect_operator_argument_amount<NumericTypes>(
pub(crate) fn expect_operator_argument_amount<NumericTypes: EvalexprNumericTypes>(
actual: usize,
expected: usize,
) -> EvalexprResult<(), NumericTypes> {
Expand All @@ -406,7 +409,7 @@ pub(crate) fn expect_operator_argument_amount<NumericTypes>(
}

/// Returns `Ok(())` if the actual and expected parameters are equal, and `Err(Error::WrongFunctionArgumentAmount)` otherwise.
pub fn expect_function_argument_amount<NumericTypes>(
pub fn expect_function_argument_amount<NumericTypes: EvalexprNumericTypes>(
actual: usize,
expected: usize,
) -> EvalexprResult<(), NumericTypes> {
Expand All @@ -429,7 +432,7 @@ pub fn expect_number_or_string<NumericTypes: EvalexprNumericTypes>(
}
}

impl std::error::Error for EvalexprError {}
impl<NumericTypes: EvalexprNumericTypes> std::error::Error for EvalexprError<NumericTypes> {}

/// Standard result type used by this crate.
pub type EvalexprResult<T, NumericTypes = DefaultNumericTypes> =
Expand All @@ -441,36 +444,45 @@ pub type EvalexprResultValue<NumericTypes = DefaultNumericTypes> =

#[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::<i64, f64>::Int(3), vec![ValueType::String]),
EvalexprError::type_error(
Value::<DefaultNumericTypes>::Int(3),
vec![ValueType::String]
),
EvalexprError::TypeError {
actual: Value::Int(3),
expected: vec![ValueType::String]
}
);
assert_eq!(
EvalexprError::expected_type(
&Value::<i64, f64>::String("abc".to_string()),
&Value::<DefaultNumericTypes>::String("abc".to_string()),
Value::Empty
),
EvalexprError::expected_string(Value::Empty)
);
assert_eq!(
EvalexprError::expected_type(&Value::<i64, f64>::Boolean(false), Value::Empty),
EvalexprError::expected_type(
&Value::<DefaultNumericTypes>::Boolean(false),
Value::Empty
),
EvalexprError::expected_boolean(Value::Empty)
);
assert_eq!(
EvalexprError::expected_type(&Value::<i64, f64>::Tuple(vec![]), Value::Empty),
EvalexprError::expected_type(
&Value::<DefaultNumericTypes>::Tuple(vec![]),
Value::Empty
),
EvalexprError::expected_tuple(Value::Empty)
);
assert_eq!(
EvalexprError::expected_type(
&Value::<i64, f64>::Empty,
&Value::<DefaultNumericTypes>::Empty,
Value::String("abc".to_string())
),
EvalexprError::expected_empty(Value::String("abc".to_string()))
Expand Down
24 changes: 12 additions & 12 deletions src/function/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,10 @@ pub fn builtin_function<NumericTypes: EvalexprNumericTypes>(
"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())),
Expand All @@ -113,8 +113,8 @@ pub fn builtin_function<NumericTypes: EvalexprNumericTypes>(
})),
"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 {
Expand All @@ -127,16 +127,16 @@ pub fn builtin_function<NumericTypes: EvalexprNumericTypes>(
}
}

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))
}
})),
"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 {
Expand All @@ -149,7 +149,7 @@ pub fn builtin_function<NumericTypes: EvalexprNumericTypes>(
}
}

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))
Expand Down Expand Up @@ -216,9 +216,9 @@ pub fn builtin_function<NumericTypes: EvalexprNumericTypes>(
})),
"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(),
Expand Down
22 changes: 14 additions & 8 deletions src/function/mod.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
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<NumericTypes>
trait ClonableFn<NumericTypes: EvalexprNumericTypes = DefaultNumericTypes>
where
Self: Fn(&Value<NumericTypes>) -> EvalexprResultValue<NumericTypes>,
Self: Send + Sync + 'static,
{
fn dyn_clone(&self) -> Box<dyn ClonableFn<NumericTypes>>;
}

impl<F, NumericTypes> ClonableFn<NumericTypes> for F
impl<F, NumericTypes: EvalexprNumericTypes> ClonableFn<NumericTypes> for F
where
F: Fn(&Value<NumericTypes>) -> EvalexprResultValue<NumericTypes>,
F: Send + Sync + 'static,
Expand All @@ -38,19 +44,19 @@ where
/// })).unwrap(); // Do proper error handling here
/// assert_eq!(eval_with_context("id(4)", &context), Ok(Value::from(4)));
/// ```
pub struct Function<NumericTypes> {
pub struct Function<NumericTypes: EvalexprNumericTypes> {
function: Box<dyn ClonableFn<NumericTypes>>,
}

impl<NumericTypes> Clone for Function<NumericTypes> {
impl<NumericTypes: EvalexprNumericTypes> Clone for Function<NumericTypes> {
fn clone(&self) -> Self {
Self {
function: self.function.dyn_clone(),
}
}
}

impl<NumericTypes> Function<NumericTypes> {
impl<NumericTypes: EvalexprNumericTypes> Function<NumericTypes> {
/// Creates a user-defined function.
///
/// The `function` is boxed for storage.
Expand All @@ -70,7 +76,7 @@ impl<NumericTypes> Function<NumericTypes> {
}
}

impl<NumericTypes> fmt::Debug for Function<NumericTypes> {
impl<NumericTypes: EvalexprNumericTypes> fmt::Debug for Function<NumericTypes> {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "Function {{ [...] }}")
}
Expand All @@ -82,4 +88,4 @@ impl<NumericTypes> fmt::Debug for Function<NumericTypes> {
#[doc(hidden)]
trait IsSendAndSync: Send + Sync {}

impl<NumericTypes> IsSendAndSync for Function<NumericTypes> {}
impl<NumericTypes: EvalexprNumericTypes> IsSendAndSync for Function<NumericTypes> {}
Loading

0 comments on commit 1a8b4ac

Please sign in to comment.