Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion 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, DefaultNumericTypes};
use evalexpr::{build_operator_tree, combine_trees, DefaultNumericTypes, Operator};
use rand::{distributions::Uniform, seq::SliceRandom, Rng, SeedableRng};
use rand_pcg::Pcg32;
use std::{fmt::Write, hint::black_box};
Expand Down Expand Up @@ -138,3 +138,26 @@ fn bench_evaluate_large_tuple_expression(bencher: &mut Bencher) {

bencher.iter(|| large_tuple_expression.eval().unwrap());
}

#[bench]
fn bench_tree_combining(bencher: &mut Bencher) {
let mut gen = Pcg32::seed_from_u64(33);
let small_expressions: Vec<_> = generate_small_expressions(BENCHMARK_LEN, &mut gen)
.iter()
.zip(&generate_small_expressions(BENCHMARK_LEN, &mut gen))
.map(|(expression_a, expression_b)| {
(
build_operator_tree::<DefaultNumericTypes>(expression_a).unwrap(),
build_operator_tree::<DefaultNumericTypes>(expression_b).unwrap(),
)
})
.collect();

bencher.iter(|| {
for expression in &small_expressions {
black_box(
combine_trees(expression.0.clone(), expression.1.clone(), Operator::Add).unwrap(),
);
}
});
}
1 change: 1 addition & 0 deletions src/error/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ impl<NumericTypes: EvalexprNumericTypes> fmt::Display for EvalexprError<NumericT
int
),
RandNotEnabled => write!(f, "The feature 'rand' must be enabled to use randomness"),
UnsuitableOperator(operator) => write!(f, "Unsuitable operator used: {operator}"),
CustomMessage(message) => write!(f, "Error: {}", message),
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/error/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,9 @@ pub enum EvalexprError<NumericTypes: EvalexprNumericTypes = DefaultNumericTypes>
/// The feature `rand` is not enabled, but required for the used function.
RandNotEnabled,

/// An unsuitable Operator was used
UnsuitableOperator(Operator<NumericTypes>),

/// A custom error explained by its message.
CustomMessage(String),
}
Expand Down
19 changes: 18 additions & 1 deletion src/interface/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::{
TupleType,
},
Context, ContextWithMutableVariables, EmptyType, EvalexprError, EvalexprResult, HashMapContext,
Node, Value, EMPTY_VALUE,
Node, Operator, Value, EMPTY_VALUE,
};

/// Evaluate the given expression string.
Expand Down Expand Up @@ -357,3 +357,20 @@ pub fn eval_empty_with_context_mut<C: ContextWithMutableVariables>(
Err(error) => Err(error),
}
}

/// Combined two parsed expression trees around an operator
/// ```
///use evalexpr::{build_operator_tree, combine_trees, eval, Operator};
///let expr_1 = build_operator_tree("42/2").unwrap();
///let expr_2 = build_operator_tree("34*5").unwrap();
///
///let combined = combine_trees(expr_1, expr_2, Operator::Add).unwrap();
///assert_eq!(combined.eval(), eval("(42/2)+(34*5)"))
/// ```
pub fn combine_trees<NumericTypes: EvalexprNumericTypes>(
a: Node<NumericTypes>,
b: Node<NumericTypes>,
operator: Operator<NumericTypes>,
) -> EvalexprResult<Node<NumericTypes>, NumericTypes> {
tree::combine_trees(a, b, operator)
}
36 changes: 36 additions & 0 deletions src/tree/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -973,3 +973,39 @@ pub(crate) fn tokens_to_operator_tree<NumericTypes: EvalexprNumericTypes>(
Err(EvalexprError::UnmatchedRBrace)
}
}

/// combine two trees with a specific binary+ operator.
pub(crate) fn combine_trees<NumericTypes: EvalexprNumericTypes>(
a: Node<NumericTypes>,
b: Node<NumericTypes>,
operator: Operator<NumericTypes>,
) -> EvalexprResult<Node<NumericTypes>, NumericTypes> {
if operator.is_unary() {
return Err(EvalexprError::UnsuitableOperator(operator));
}

let mut node: Node<NumericTypes> = Node::new(operator);

node.children.push(extract_root(a)?);
node.children.push(extract_root(b)?);

let mut root = Node::root_node();

root.children.push(node);

Ok(root)
}

/// extract root
fn extract_root<NumericTypes: EvalexprNumericTypes>(
a: Node<NumericTypes>,
) -> EvalexprResult<Node<NumericTypes>, NumericTypes> {
if a.operator == Operator::RootNode {
Ok(a)
} else {
a.children
.into_iter()
.next()
.map_or_else(|| Err(EvalexprError::OutOfBoundsAccess), Ok)
}
}
15 changes: 15 additions & 0 deletions tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2610,3 +2610,18 @@ fn test_node_mutable_access() {
assert_eq!(node.children_mut().len(), 1);
assert_eq!(*node.operator_mut(), Operator::RootNode);
}

#[test]
fn test_tree_combining() {
let a = build_operator_tree::<DefaultNumericTypes>("1").unwrap();
let b = build_operator_tree::<DefaultNumericTypes>("4").unwrap();

let combined = combine_trees(a.clone(), b, Operator::Add).unwrap();
assert_eq!(combined.eval(), Ok(Value::Int(5)));

let c = build_operator_tree::<DefaultNumericTypes>("4+2").unwrap();
assert_eq!(
combine_trees(a, c, Operator::Add).unwrap().eval(),
Ok(Value::Int(7))
);
}