Skip to content

Commit

Permalink
expose used operator names
Browse files Browse the repository at this point in the history
  • Loading branch information
bertiqwerty committed Jul 26, 2024
1 parent 01f8526 commit 261edc1
Show file tree
Hide file tree
Showing 6 changed files with 210 additions and 14 deletions.
47 changes: 46 additions & 1 deletion src/expression/deep.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::{
fmt::{self, Debug, Display, Formatter},
iter,
marker::PhantomData,
ops,
str::FromStr,
Expand Down Expand Up @@ -794,7 +795,7 @@ where
}

pub(super) fn var_names_like_other(mut self, other: &Self) -> Self {
self.var_names = other.var_names.clone();
self.var_names.clone_from(&other.var_names);
self
}

Expand Down Expand Up @@ -1019,6 +1020,50 @@ where
{
detail::parse(text, LM::is_literal)
}
fn binary_reprs(&self) -> SmallVec<[String; N_BINOPS_OF_DEEPEX_ON_STACK]> {
let mut reprs = self
.nodes
.iter()
.filter_map(|node| match node {
DeepNode::Expr(e) => Some(e.binary_reprs()),
_ => None,
})
.chain(iter::once(
self.bin_ops.reprs.iter().map(|s| s.to_string()).collect(),
))
.flatten()
.collect::<SmallVec<_>>();
reprs.sort_unstable();
reprs.dedup();
reprs
}
fn unary_reprs(&self) -> SmallVec<[String; N_UNARYOPS_OF_DEEPEX_ON_STACK]> {
let mut reprs = self
.nodes
.iter()
.filter_map(|node| match node {
DeepNode::Expr(e) => Some(e.unary_reprs()),
_ => None,
})
.chain(iter::once(
self.unary_op.reprs.iter().map(|s| s.to_string()).collect(),
))
.flatten()
.collect::<SmallVec<_>>();
reprs.sort_unstable();
reprs.dedup();
reprs
}
fn operator_reprs(
&self,
) -> SmallVec<[String; N_BINOPS_OF_DEEPEX_ON_STACK + N_UNARYOPS_OF_DEEPEX_ON_STACK]> {
let mut reprs = SmallVec::new();
reprs.extend(self.binary_reprs());
reprs.extend(self.unary_reprs());
reprs.sort_unstable();
reprs.dedup();
reprs
}
}

impl<'a, T, OF, LM> Calculate<'a, T> for DeepEx<'a, T, OF, LM>
Expand Down
110 changes: 100 additions & 10 deletions src/expression/flat.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use self::detail::{var_indices_ordered, FlatNode, FlatNodeKind, FlatNodeVec, FlatOpVec};
use crate::data_type::DataType;
use crate::definitions::{N_NODES_ON_STACK, N_VARS_ON_STACK};
use crate::definitions::{
N_BINOPS_OF_DEEPEX_ON_STACK, N_NODES_ON_STACK, N_UNARYOPS_OF_DEEPEX_ON_STACK, N_VARS_ON_STACK,
};
use crate::expression::{
deep::{DeepEx, DeepNode},
Express,
Expand All @@ -27,7 +29,10 @@ mod detail {

use crate::{
data_type::DataType,
definitions::{N_NODES_ON_STACK, N_UNARYOPS_OF_DEEPEX_ON_STACK, N_VARS_ON_STACK},
definitions::{
N_BINOPS_OF_DEEPEX_ON_STACK, N_NODES_ON_STACK, N_UNARYOPS_OF_DEEPEX_ON_STACK,
N_VARS_ON_STACK,
},
exerr,
expression::{eval_binary, number_tracker::NumberTracker},
operators::{OperateBinary, UnaryOp},
Expand All @@ -40,6 +45,8 @@ mod detail {
pub type FlatNodeVec<T> = SmallVec<[FlatNode<T>; N_NODES_ON_STACK]>;
pub type FlatOpVec<T> = SmallVec<[FlatOp<T>; N_NODES_ON_STACK]>;
type UnaryOpIdxDepthStack = SmallVec<[(usize, i64); N_UNARYOPS_OF_DEEPEX_ON_STACK]>;
const CANNOT_FIND_OP_MSG: &str =
"Bug! It should not be possible that I cannot find my own operator";

/// A `FlatOp` contains besides a binary operation an optional unary operation that
/// will be executed after the binary operation in case of its existence.
Expand Down Expand Up @@ -81,7 +88,7 @@ mod detail {

use crate::expression::deep::{BinOpsWithReprs, DeepEx, DeepNode, UnaryOpWithReprs};

fn collect_reprs<'a, F, T, I>(
pub fn collect_reprs<'a, F, T, I>(
funcs: I,
ops: &[Operator<'a, T>],
predicate: fn(&Operator<T>, F) -> bool,
Expand All @@ -101,34 +108,72 @@ mod detail {
.collect::<ExResult<SmallVec<[Operator<'a, T>; N_UNARYOPS_OF_DEEPEX_ON_STACK]>>>()
}

fn unary_predicate<T: Clone>(op: &Operator<T>, func: &fn(T) -> T) -> bool {
pub fn binary_reprs<'a, T>(
operators: &[Operator<'a, T>],
flat_ops: &'a FlatOpVec<T>,
) -> SmallVec<[String; N_BINOPS_OF_DEEPEX_ON_STACK]>
where
T: Clone,
{
collect_reprs::<&fn(T, T) -> T, _, _>(
flat_ops.iter().map(|op| &op.bin_op.apply),
operators,
binary_predicate,
)
.expect(CANNOT_FIND_OP_MSG)
.iter()
.map(|op| op.repr().to_string())
.collect()
}

pub fn unary_reprs<'a, T>(
operators: &[Operator<'a, T>],
unary_ops: impl Iterator<Item = &'a UnaryOp<T>>,
) -> SmallVec<[String; N_UNARYOPS_OF_DEEPEX_ON_STACK]>
where
T: Clone + 'a,
{
let mut reprs = SmallVec::new();
let result_unary_reprs = unary_ops.map(|op| unary_reprs_of_composition(operators, op));
for repr in result_unary_reprs {
reprs.extend(
repr.expect(CANNOT_FIND_OP_MSG)
.iter()
.map(|s| s.to_string()),
);
}
reprs
}

pub fn unary_predicate<T: Clone>(op: &Operator<T>, func: &fn(T) -> T) -> bool {
if op.has_unary() {
op.unary().unwrap() == *func
} else {
false
}
}

fn binary_predicate<T: Clone>(op: &Operator<T>, func: &fn(T, T) -> T) -> bool {
pub fn binary_predicate<T: Clone>(op: &Operator<T>, func: &fn(T, T) -> T) -> bool {
if op.has_bin() {
op.bin().unwrap().apply == *func
} else {
false
}
}

fn collect_unary_reprs<'a, T: Clone>(
pub fn unary_reprs_of_composition<'a, T: Clone>(
ops: &[Operator<'a, T>],
unary_op: &UnaryOp<T>,
) -> ExResult<SmallVec<[&'a str; N_UNARYOPS_OF_DEEPEX_ON_STACK]>> {
Ok(collect_reprs::<&fn(T) -> T, _, _>(
let reprs = collect_reprs::<&fn(T) -> T, _, _>(
unary_op.funcs_to_be_composed().iter(),
ops,
unary_predicate,
)?
.iter()
.map(|op| op.repr())
.collect::<SmallVec<[&'a str; N_UNARYOPS_OF_DEEPEX_ON_STACK]>>())
.collect::<SmallVec<[&'a str; N_UNARYOPS_OF_DEEPEX_ON_STACK]>>();
Ok(reprs)
}

fn convert_node<'a, T, OF, LM>(
Expand All @@ -148,7 +193,7 @@ mod detail {
};

// cannot fail unless there is a bug
let reprs = collect_unary_reprs(ops, &node.unary_op).unwrap();
let reprs = unary_reprs_of_composition(ops, &node.unary_op).unwrap();

let n_reprs = reprs.len();
let unary_op = UnaryOpWithReprs {
Expand Down Expand Up @@ -224,7 +269,7 @@ mod detail {
ops: smallvec![bin_op],
};
let unary_op = mem::take(&mut flat_ops[idx].unary_op);
let unary_reprs = collect_unary_reprs(&operators, &unary_op)?;
let unary_reprs = unary_reprs_of_composition(&operators, &unary_op)?;
let unary_op = UnaryOpWithReprs {
reprs: unary_reprs,
op: unary_op,
Expand Down Expand Up @@ -861,6 +906,51 @@ where
let ops = OF::make();
detail::parse(text, &ops)
}

fn binary_reprs(&self) -> SmallVec<[String; N_BINOPS_OF_DEEPEX_ON_STACK]> {
let operators = OF::make();
let mut reprs = detail::binary_reprs(&operators, &self.flat_ops);
reprs.sort_unstable();
reprs.dedup();
reprs
}
fn unary_reprs(&self) -> SmallVec<[String; N_UNARYOPS_OF_DEEPEX_ON_STACK]> {
let operators = OF::make();
let unary_ops = self
.flat_ops
.iter()
.map(|op| &op.unary_op)
.chain(self.nodes.iter().map(|n| &n.unary_op));
let mut reprs = detail::unary_reprs(&operators, unary_ops);
reprs.sort_unstable();
reprs.dedup();
reprs
}
fn operator_reprs(
&self,
) -> SmallVec<[String; N_BINOPS_OF_DEEPEX_ON_STACK + N_UNARYOPS_OF_DEEPEX_ON_STACK]> {
let operators = OF::make();
let mut reprs = SmallVec::new();

reprs.extend(
detail::binary_reprs(&operators, &self.flat_ops)
.iter()
.map(|s| s.to_string()),
);
let unary_ops = self
.flat_ops
.iter()
.map(|op| &op.unary_op)
.chain(self.nodes.iter().map(|n| &n.unary_op));
reprs.extend(
detail::unary_reprs(&operators, unary_ops)
.iter()
.map(|s| s.to_string()),
);
reprs.sort_unstable();
reprs.dedup();
reprs
}
}

/// The expression is displayed as a string created by [`unparse`](FlatEx::unparse).
Expand Down
18 changes: 17 additions & 1 deletion src/expression/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
use std::{fmt::Debug, mem, str::FromStr};

use crate::{data_type::DataType, operators::OperateBinary, parser, ExResult, MakeOperators};
use smallvec::SmallVec;

use crate::{
data_type::DataType,
definitions::{N_BINOPS_OF_DEEPEX_ON_STACK, N_UNARYOPS_OF_DEEPEX_ON_STACK},
operators::OperateBinary,
parser, ExResult, MakeOperators,
};

use self::{deep::DeepEx, number_tracker::NumberTracker};
pub mod calculate;
Expand Down Expand Up @@ -96,6 +103,15 @@ where
fn parse(text: &'a str) -> ExResult<Self>
where
Self: Sized;

/// Returns an alphabetically sorted list of all binary operators without duplicates in the expression.
fn binary_reprs(&self) -> SmallVec<[String; N_BINOPS_OF_DEEPEX_ON_STACK]>;
/// Returns an alphabetically sorted list of all unary operators without duplicates in the expression.
fn unary_reprs(&self) -> SmallVec<[String; N_UNARYOPS_OF_DEEPEX_ON_STACK]>;
/// Returns an alphabetically sorted list of all operators without duplicates in the expression.
fn operator_reprs(
&self,
) -> SmallVec<[String; N_BINOPS_OF_DEEPEX_ON_STACK + N_UNARYOPS_OF_DEEPEX_ON_STACK]>;
}

pub fn eval_binary<T, O, N>(
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@ pub mod prelude {
/// In case the parsing went wrong, e.g., due to an invalid input string, an
/// [`ExError`] is returned.
///
pub fn eval_str<T: DataType>(text: &str) -> ExResult<T>
pub fn eval_str<T>(text: &str) -> ExResult<T>
where
T: DataType + Float,
<T as FromStr>::Err: Debug,
Expand Down
1 change: 1 addition & 0 deletions src/statements.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ mod detail {

use crate::{exerr, DataType, ExResult, Express, FlatEx, MakeOperators, MatchLiteral};

#[allow(dead_code)]
#[derive(Default, Debug)]
pub enum ParsedLhs<'a> {
Var(&'a str),
Expand Down
46 changes: 45 additions & 1 deletion tests/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use std::io::{self, BufRead};
use std::iter::repeat;
use utils::assert_float_eq_f64;

use std::fmt::Debug;
#[cfg(test)]
use std::{
iter::once,
Expand Down Expand Up @@ -976,7 +977,6 @@ fn test_string_ops() {

#[test]
fn test_binary_function_style() {
use std::fmt::Debug;
fn test(s: &str, vars: &[f64], reference: f64) {
println!("testing {s}");
fn test_<'a, EX: Express<'a, f64> + Debug>(s: &'a str, vars: &[f64], reference: f64) {
Expand Down Expand Up @@ -1013,3 +1013,47 @@ fn test_binary_function_style() {
);
assert!(FlatEx::<f64>::parse("atan3(z, y, x").is_err());
}

#[test]
fn test_op_reprs() {
fn test(s: &str, uo_reference: &[&str], bo_reference: &[&str]) {
println!("testing {s}");
fn test_<'a, EX: Express<'a, f64> + Debug>(
s: &'a str,
uo_reference: &[&str],
bo_reference: &[&str],
) {
let expr = EX::parse(s).unwrap();
for r in uo_reference.iter().chain(bo_reference.iter()) {
assert!(expr.operator_reprs().contains(&r.to_string()));
}
let uops = expr.unary_reprs().to_vec();
let bops = expr.binary_reprs().to_vec();
let mut uo_reference = uo_reference
.iter()
.map(|s| s.to_string())
.collect::<Vec<_>>();
let mut bo_reference = bo_reference
.iter()
.map(|s| s.to_string())
.collect::<Vec<_>>();
uo_reference.sort();
bo_reference.sort();
assert_eq!(uops, uo_reference);
assert_eq!(bops, bo_reference);
}

println!("flatex...");
test_::<FlatEx<f64>>(s, uo_reference, bo_reference);
println!("deepex...");
test_::<DeepEx<f64>>(s, uo_reference, bo_reference);
}
test("atan2(0.2/y, x)", &[], &["atan2", "/"]);
test("-x", &["-"], &[]);
test("sin(-x)", &["-", "sin"], &[]);
test("sin(tan(cos(x)))", &["cos", "sin", "tan"], &[]);
test("sin(1+tan(cos(x)))", &["cos", "sin", "tan"], &["+"]);
test("sin(-tan(cos(x)))", &["-", "cos", "sin", "tan"], &[]);
test("sin(-tan(y+cos(x-z)))", &["-", "cos", "sin", "tan"], &["+", "-"]);
test("sin(-tan(y+sin(cos(x-z))))", &["-", "cos", "sin", "tan"], &["+", "-"]);
}

0 comments on commit 261edc1

Please sign in to comment.