Skip to content

Commit

Permalink
Tests, benches and features: Make int and float types generic.
Browse files Browse the repository at this point in the history
  • Loading branch information
ISibboI committed Oct 16, 2024
1 parent 0c6d83a commit 3b19f2f
Show file tree
Hide file tree
Showing 11 changed files with 394 additions and 268 deletions.
17 changes: 6 additions & 11 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 3 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,16 @@ path = "src/lib.rs"

[dependencies]
regex = { version = "1.11.0", optional = true }
serde = { version = "1.0.210", optional = true }
serde_derive = { version = "1.0.210", optional = true }
serde = { version = "1.0.210", features = ["derive"], optional = true }
rand = { version = "0.8.5", optional = true }

[features]
serde = ["dep:serde", "dep:serde_derive"]
serde = ["dep:serde"]
regex = ["dep:regex"]
rand = ["dep:rand"]

[dev-dependencies]
ron = "0.8.1"
ron = "0.7.1"
rand = "0.8.5"
rand_pcg = "0.3.1"

Expand Down
26 changes: 14 additions & 12 deletions benches/benchs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ extern crate rand;
extern crate rand_pcg;
extern crate test;

use evalexpr::build_operator_tree;
use evalexpr::{build_operator_tree, DefaultNumericTypes};
use rand::{distributions::Uniform, seq::SliceRandom, Rng, SeedableRng};
use rand_pcg::Pcg32;
use std::{fmt::Write, hint::black_box};
Expand Down Expand Up @@ -68,15 +68,15 @@ fn bench_parse_long_expression_chains(bencher: &mut Bencher) {
let mut gen = Pcg32::seed_from_u64(0);
let long_expression_chain = generate_expression_chain(BENCHMARK_LEN, &mut gen);

bencher.iter(|| build_operator_tree(&long_expression_chain).unwrap());
bencher.iter(|| build_operator_tree::<DefaultNumericTypes>(&long_expression_chain).unwrap());
}

#[bench]
fn bench_parse_deep_expression_trees(bencher: &mut Bencher) {
let mut gen = Pcg32::seed_from_u64(15);
let deep_expression_tree = generate_expression(BENCHMARK_LEN, &mut gen);

bencher.iter(|| build_operator_tree(&deep_expression_tree).unwrap());
bencher.iter(|| build_operator_tree::<DefaultNumericTypes>(&deep_expression_tree).unwrap());
}

#[bench]
Expand All @@ -86,16 +86,18 @@ fn bench_parse_many_small_expressions(bencher: &mut Bencher) {

bencher.iter(|| {
for expression in &small_expressions {
black_box(build_operator_tree(expression).unwrap());
black_box(build_operator_tree::<DefaultNumericTypes>(expression).unwrap());
}
});
}

#[bench]
fn bench_evaluate_long_expression_chains(bencher: &mut Bencher) {
let mut gen = Pcg32::seed_from_u64(0);
let long_expression_chain =
build_operator_tree(&generate_expression_chain(BENCHMARK_LEN, &mut gen)).unwrap();
let long_expression_chain = build_operator_tree::<DefaultNumericTypes>(
&generate_expression_chain(BENCHMARK_LEN, &mut gen),
)
.unwrap();

bencher.iter(|| long_expression_chain.eval().unwrap());
}
Expand All @@ -104,7 +106,8 @@ fn bench_evaluate_long_expression_chains(bencher: &mut Bencher) {
fn bench_evaluate_deep_expression_trees(bencher: &mut Bencher) {
let mut gen = Pcg32::seed_from_u64(15);
let deep_expression_tree =
build_operator_tree(&generate_expression(BENCHMARK_LEN, &mut gen)).unwrap();
build_operator_tree::<DefaultNumericTypes>(&generate_expression(BENCHMARK_LEN, &mut gen))
.unwrap();

bencher.iter(|| deep_expression_tree.eval().unwrap());
}
Expand All @@ -114,7 +117,7 @@ fn bench_evaluate_many_small_expressions(bencher: &mut Bencher) {
let mut gen = Pcg32::seed_from_u64(33);
let small_expressions: Vec<_> = generate_small_expressions(BENCHMARK_LEN, &mut gen)
.iter()
.map(|expression| build_operator_tree(expression).unwrap())
.map(|expression| build_operator_tree::<DefaultNumericTypes>(expression).unwrap())
.collect();

bencher.iter(|| {
Expand All @@ -127,10 +130,9 @@ fn bench_evaluate_many_small_expressions(bencher: &mut Bencher) {
#[bench]
fn bench_evaluate_large_tuple_expression(bencher: &mut Bencher) {
let mut gen = Pcg32::seed_from_u64(44);
let large_tuple_expression = build_operator_tree(&generate_large_tuple_expression(
EXPONENTIAL_TUPLE_ITERATIONS,
&mut gen,
))
let large_tuple_expression = build_operator_tree::<DefaultNumericTypes>(
&generate_large_tuple_expression(EXPONENTIAL_TUPLE_ITERATIONS, &mut gen),
)
.unwrap();
dbg!(&large_tuple_expression);

Expand Down
34 changes: 30 additions & 4 deletions src/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ pub trait GetFunctionContext: Context {

/// A context that returns `None` for each identifier.
/// Builtin functions are disabled and cannot be enabled.
#[derive(Debug, Default)]
#[derive(Debug)]
pub struct EmptyContext<NumericTypes>(PhantomData<NumericTypes>);

impl<NumericTypes: EvalexprNumericTypes> Context for EmptyContext<NumericTypes> {
Expand Down Expand Up @@ -150,9 +150,15 @@ impl<NumericTypes: EvalexprNumericTypes> IterateVariablesContext for EmptyContex
}
}

impl<NumericTypes> Default for EmptyContext<NumericTypes> {
fn default() -> Self {
Self(PhantomData)
}
}

/// A context that returns `None` for each identifier.
/// Builtin functions are enabled and cannot be disabled.
#[derive(Debug, Default)]
#[derive(Debug)]
pub struct EmptyContextWithBuiltinFunctions<NumericTypes>(PhantomData<NumericTypes>);

impl<NumericTypes: EvalexprNumericTypes> Context
Expand Down Expand Up @@ -207,16 +213,22 @@ impl<NumericTypes: EvalexprNumericTypes> IterateVariablesContext
}
}

impl<NumericTypes> Default for EmptyContextWithBuiltinFunctions<NumericTypes> {
fn default() -> Self {
Self(PhantomData)
}
}

/// A context 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.*
///
/// 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))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct HashMapContext<NumericTypes: EvalexprNumericTypes = DefaultNumericTypes> {
variables: HashMap<String, Value<NumericTypes>>,
#[cfg_attr(feature = "serde_support", serde(skip))]
#[cfg_attr(feature = "serde", serde(skip))]
functions: HashMap<String, Function<NumericTypes>>,

/// True if builtin functions are disabled.
Expand Down Expand Up @@ -391,6 +403,10 @@ macro_rules! context_map {
// Termination (allow missing comma at the end of the argument list)
( ($ctx:expr) $k:expr => Function::new($($v:tt)*) ) =>
{ $crate::context_map!(($ctx) $k => Function::new($($v)*),) };
( ($ctx:expr) $k:expr => int $v:expr ) =>
{ $crate::context_map!(($ctx) $k => int $v,) };
( ($ctx:expr) $k:expr => float $v:expr ) =>
{ $crate::context_map!(($ctx) $k => float $v,) };
( ($ctx:expr) $k:expr => $v:expr ) =>
{ $crate::context_map!(($ctx) $k => $v,) };
// Termination
Expand All @@ -401,6 +417,16 @@ macro_rules! context_map {
$crate::ContextWithMutableFunctions::set_function($ctx, $k.into(), $crate::Function::new($($v)*))
.and($crate::context_map!(($ctx) $($tt)*))
}};
// add an integer value, and chain the eventual error with the ones in the next values
( ($ctx:expr) $k:expr => int $v:expr , $($tt:tt)*) => {{
$crate::ContextWithMutableVariables::set_value($ctx, $k.into(), Value::from_int($v.into()))
.and($crate::context_map!(($ctx) $($tt)*))
}};
// add a float value, and chain the eventual error with the ones in the next values
( ($ctx:expr) $k:expr => float $v:expr , $($tt:tt)*) => {{
$crate::ContextWithMutableVariables::set_value($ctx, $k.into(), Value::from_float($v.into()))
.and($crate::context_map!(($ctx) $($tt)*))
}};
// add a value, and chain the eventual error with the ones in the next values
( ($ctx:expr) $k:expr => $v:expr , $($tt:tt)*) => {{
$crate::ContextWithMutableVariables::set_value($ctx, $k.into(), $v.into())
Expand Down
14 changes: 7 additions & 7 deletions src/feature_serde/mod.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
use crate::{interface::build_operator_tree, Node};
use crate::{interface::build_operator_tree, EvalexprNumericTypes, Node};
use serde::{de, Deserialize, Deserializer};
use std::fmt;
use std::{fmt, marker::PhantomData};

impl<'de> Deserialize<'de> for Node {
impl<'de, NumericTypes: EvalexprNumericTypes> Deserialize<'de> for Node<NumericTypes> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_str(NodeVisitor)
deserializer.deserialize_str(NodeVisitor(PhantomData))
}
}

struct NodeVisitor;
struct NodeVisitor<NumericTypes: EvalexprNumericTypes>(PhantomData<NumericTypes>);

impl de::Visitor<'_> for NodeVisitor {
type Value = Node;
impl<NumericTypes: EvalexprNumericTypes> de::Visitor<'_> for NodeVisitor<NumericTypes> {
type Value = Node<NumericTypes>;

fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
Expand Down
6 changes: 3 additions & 3 deletions src/function/builtin.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#[cfg(feature = "regex_support")]
#[cfg(feature = "regex")]
use regex::Regex;

use crate::{
Expand Down Expand Up @@ -229,7 +229,7 @@ pub fn builtin_function<NumericTypes: EvalexprNumericTypes>(
}
})),
// String functions
#[cfg(feature = "regex_support")]
#[cfg(feature = "regex")]
"str::regex_matches" => Some(Function::new(|argument| {
let arguments = argument.as_fixed_len_tuple(2)?;

Expand All @@ -243,7 +243,7 @@ pub fn builtin_function<NumericTypes: EvalexprNumericTypes>(
)),
}
})),
#[cfg(feature = "regex_support")]
#[cfg(feature = "regex")]
"str::regex_replace" => Some(Function::new(|argument| {
let arguments = argument.as_fixed_len_tuple(3)?;

Expand Down
12 changes: 1 addition & 11 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -573,16 +573,6 @@
#![forbid(unsafe_code)]
#![allow(clippy::get_first)]

#[cfg(feature = "regex_support")]
extern crate regex;
#[cfg(test)]
extern crate ron;
#[cfg(feature = "serde_support")]
extern crate serde;
#[cfg(feature = "serde_support")]
#[macro_use]
extern crate serde_derive;

pub use crate::{
context::{
Context, ContextWithMutableFunctions, ContextWithMutableVariables, EmptyContext,
Expand All @@ -603,7 +593,7 @@ pub use crate::{

mod context;
pub mod error;
#[cfg(feature = "serde_support")]
#[cfg(feature = "serde")]
mod feature_serde;
mod function;
mod interface;
Expand Down
2 changes: 1 addition & 1 deletion src/value/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ pub const EMPTY_VALUE: () = ();
/// The value type used by the parser.
/// Values can be of different subtypes that are the variants of this enum.
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Value<NumericTypes: EvalexprNumericTypes = DefaultNumericTypes> {
/// A string value.
String(String),
Expand Down
16 changes: 14 additions & 2 deletions src/value/numeric_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,19 @@ use crate::{EvalexprError, EvalexprResult, Value};
/// See [`EvalexprInt`] and [`EvalexprFloat`] for the requirements on the types.
pub trait EvalexprNumericTypes: 'static + Sized + Debug + Clone + PartialEq {
/// The integer type.
#[cfg(feature = "serde")]
type Int: EvalexprInt<Self> + serde::Serialize + for<'de> serde::Deserialize<'de>;

/// The integer type.
#[cfg(not(feature = "serde"))]
type Int: EvalexprInt<Self>;

/// The float type.
#[cfg(feature = "serde")]
type Float: EvalexprFloat<Self> + serde::Serialize + for<'de> serde::Deserialize<'de>;

/// The float type.
#[cfg(not(feature = "serde"))]
type Float: EvalexprFloat<Self>;

/// Convert an integer to a float using the `as` operator or a similar mechanic.
Expand All @@ -38,9 +48,11 @@ pub trait EvalexprInt<NumericTypes: EvalexprNumericTypes<Int = Self>>:
fn from_usize(int: usize) -> EvalexprResult<Self, NumericTypes>;

/// Convert `self` into [`usize`].
#[expect(clippy::wrong_self_convention)]
fn into_usize(&self) -> EvalexprResult<usize, NumericTypes>;

/// Parse `Self` from a hex string.
#[expect(clippy::result_unit_err)]
fn from_hex_str(literal: &str) -> Result<Self, ()>;

/// Perform an addition operation, returning an error on overflow.
Expand Down Expand Up @@ -245,9 +257,9 @@ impl<NumericTypes: EvalexprNumericTypes<Int = Self>> EvalexprInt<NumericTypes> f
if *self >= 0 {
(*self as u64)
.try_into()
.map_err(|_| EvalexprError::IntIntoUsize { int: self.clone() })
.map_err(|_| EvalexprError::IntIntoUsize { int: *self })
} else {
Err(EvalexprError::IntIntoUsize { int: self.clone() })
Err(EvalexprError::IntIntoUsize { int: *self })
}
}

Expand Down
Loading

0 comments on commit 3b19f2f

Please sign in to comment.