Skip to content

Commit e4c78ef

Browse files
committed
implement some functions and if statment:Wq
1 parent 77936c4 commit e4c78ef

7 files changed

Lines changed: 377 additions & 27 deletions

File tree

libraries/math-parser/src/ast.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ pub enum BinaryOp {
5656
Mul,
5757
Div,
5858
Pow,
59+
Leq,
60+
Geq,
61+
Eq,
5962
}
6063

6164
#[derive(Debug, PartialEq, Clone, Copy)]
@@ -72,4 +75,5 @@ pub enum Node {
7275
FnCall { name: String, expr: Vec<Node> },
7376
BinOp { lhs: Box<Node>, op: BinaryOp, rhs: Box<Node> },
7477
UnaryOp { expr: Box<Node>, op: UnaryOp },
78+
Conditional { condition: Box<Node>, if_block: Box<Node>, else_block: Box<Node> },
7579
}

libraries/math-parser/src/constants.rs

Lines changed: 225 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
use std::{collections::HashMap, f64::consts::PI};
1+
use std::{
2+
collections::HashMap,
3+
f64::consts::{LN_2, PI},
4+
};
25

36
use lazy_static::lazy_static;
47
use num_complex::{Complex, ComplexFloat};
@@ -116,6 +119,227 @@ lazy_static! {
116119
_ => None,
117120
}),
118121
);
122+
// Hyperbolic Functions
123+
map.insert(
124+
"sinh",
125+
Box::new(|values| match values {
126+
[Value::Number(Number::Real(real))] => Some(Value::Number(Number::Real(real.sinh()))),
127+
[Value::Number(Number::Complex(complex))] => Some(Value::Number(Number::Complex(complex.sinh()))),
128+
_ => None,
129+
}),
130+
);
131+
132+
map.insert(
133+
"cosh",
134+
Box::new(|values| match values {
135+
[Value::Number(Number::Real(real))] => Some(Value::Number(Number::Real(real.cosh()))),
136+
[Value::Number(Number::Complex(complex))] => Some(Value::Number(Number::Complex(complex.cosh()))),
137+
_ => None,
138+
}),
139+
);
140+
141+
map.insert(
142+
"tanh",
143+
Box::new(|values| match values {
144+
[Value::Number(Number::Real(real))] => Some(Value::Number(Number::Real(real.tanh()))),
145+
[Value::Number(Number::Complex(complex))] => Some(Value::Number(Number::Complex(complex.tanh()))),
146+
_ => None,
147+
}),
148+
);
149+
150+
// Inverse Hyperbolic Functions
151+
map.insert(
152+
"asinh",
153+
Box::new(|values| match values {
154+
[Value::Number(Number::Real(real))] => Some(Value::Number(Number::Real(real.asinh()))),
155+
[Value::Number(Number::Complex(complex))] => Some(Value::Number(Number::Complex(complex.asinh()))),
156+
_ => None,
157+
}),
158+
);
159+
160+
map.insert(
161+
"acosh",
162+
Box::new(|values| match values {
163+
[Value::Number(Number::Real(real))] => Some(Value::Number(Number::Real(real.acosh()))),
164+
[Value::Number(Number::Complex(complex))] => Some(Value::Number(Number::Complex(complex.acosh()))),
165+
_ => None,
166+
}),
167+
);
168+
169+
map.insert(
170+
"atanh",
171+
Box::new(|values| match values {
172+
[Value::Number(Number::Real(real))] => Some(Value::Number(Number::Real(real.atanh()))),
173+
[Value::Number(Number::Complex(complex))] => Some(Value::Number(Number::Complex(complex.atanh()))),
174+
_ => None,
175+
}),
176+
);
177+
178+
// Logarithm Functions
179+
map.insert(
180+
"ln",
181+
Box::new(|values| match values {
182+
[Value::Number(Number::Real(real))] => Some(Value::Number(Number::Real(real.ln()))),
183+
[Value::Number(Number::Complex(complex))] => Some(Value::Number(Number::Complex(complex.ln()))),
184+
_ => None,
185+
}),
186+
);
187+
188+
map.insert(
189+
"log",
190+
Box::new(|values| match values {
191+
[Value::Number(Number::Real(real))] => Some(Value::Number(Number::Real(real.log10()))),
192+
[Value::Number(Number::Complex(complex))] => Some(Value::Number(Number::Complex(complex.log10()))),
193+
[Value::Number(n), Value::Number(base)] => {
194+
// Custom base logarithm using change of base formula
195+
let compute_log = |x: f64, b: f64| -> f64 { x.ln() / b.ln() };
196+
match (n, base) {
197+
(Number::Real(x), Number::Real(b)) => Some(Value::Number(Number::Real(compute_log(*x, *b)))),
198+
_ => None,
199+
}
200+
}
201+
_ => None,
202+
}),
203+
);
204+
205+
map.insert(
206+
"log2",
207+
Box::new(|values| match values {
208+
[Value::Number(Number::Real(real))] => Some(Value::Number(Number::Real(real.log2()))),
209+
[Value::Number(Number::Complex(complex))] => Some(Value::Number(Number::Complex(complex / LN_2))),
210+
_ => None,
211+
}),
212+
);
213+
214+
// Root Functions
215+
map.insert(
216+
"sqrt",
217+
Box::new(|values| match values {
218+
[Value::Number(Number::Real(real))] => Some(Value::Number(Number::Real(real.sqrt()))),
219+
[Value::Number(Number::Complex(complex))] => Some(Value::Number(Number::Complex(complex.sqrt()))),
220+
_ => None,
221+
}),
222+
);
223+
224+
map.insert(
225+
"cbrt",
226+
Box::new(|values| match values {
227+
[Value::Number(Number::Real(real))] => Some(Value::Number(Number::Real(real.cbrt()))),
228+
[Value::Number(Number::Complex(complex))] => Some(Value::Number(Number::Complex(complex.powf(1.0/3.0)))),
229+
_ => None,
230+
}),
231+
);
232+
233+
// Geometry Functions
234+
map.insert(
235+
"hypot",
236+
Box::new(|values| match values {
237+
[Value::Number(Number::Real(a)), Value::Number(Number::Real(b))] => {
238+
Some(Value::Number(Number::Real(a.hypot(*b))))
239+
},
240+
_ => None,
241+
}),
242+
);
243+
244+
// Mapping Functions
245+
map.insert(
246+
"abs",
247+
Box::new(|values| match values {
248+
[Value::Number(Number::Real(real))] => Some(Value::Number(Number::Real(real.abs()))),
249+
[Value::Number(Number::Complex(complex))] => Some(Value::Number(Number::Real(complex.abs()))),
250+
_ => None,
251+
}),
252+
);
253+
254+
map.insert(
255+
"floor",
256+
Box::new(|values| match values {
257+
[Value::Number(Number::Real(real))] => Some(Value::Number(Number::Real(real.floor()))),
258+
_ => None,
259+
}),
260+
);
261+
262+
map.insert(
263+
"ceil",
264+
Box::new(|values| match values {
265+
[Value::Number(Number::Real(real))] => Some(Value::Number(Number::Real(real.ceil()))),
266+
_ => None,
267+
}),
268+
);
269+
270+
map.insert(
271+
"round",
272+
Box::new(|values| match values {
273+
[Value::Number(Number::Real(real))] => Some(Value::Number(Number::Real(real.round()))),
274+
_ => None,
275+
}),
276+
);
277+
278+
map.insert(
279+
"clamp",
280+
Box::new(|values| match values {
281+
[Value::Number(Number::Real(x)), Value::Number(Number::Real(min)), Value::Number(Number::Real(max))] => {
282+
Some(Value::Number(Number::Real(x.clamp(*min, *max))))
283+
},
284+
_ => None,
285+
}),
286+
);
287+
288+
map.insert(
289+
"lerp",
290+
Box::new(|values| match values {
291+
[Value::Number(Number::Real(a)), Value::Number(Number::Real(b)), Value::Number(Number::Real(t))] => {
292+
Some(Value::Number(Number::Real(a + (b - a) * t)))
293+
},
294+
_ => None,
295+
}),
296+
);
297+
298+
// Complex Number Functions
299+
map.insert(
300+
"real",
301+
Box::new(|values| match values {
302+
[Value::Number(Number::Complex(complex))] => Some(Value::Number(Number::Real(complex.re))),
303+
[Value::Number(Number::Real(real))] => Some(Value::Number(Number::Real(*real))),
304+
_ => None,
305+
}),
306+
);
307+
308+
map.insert(
309+
"imag",
310+
Box::new(|values| match values {
311+
[Value::Number(Number::Complex(complex))] => Some(Value::Number(Number::Real(complex.im))),
312+
[Value::Number(Number::Real(_))] => Some(Value::Number(Number::Real(0.0))),
313+
_ => None,
314+
}),
315+
);
316+
317+
// Logical Functions
318+
map.insert(
319+
"isnan",
320+
Box::new(|values| match values {
321+
[Value::Number(Number::Real(real))] => Some(Value::Number(Number::Real(if real.is_nan() { 1.0 } else { 0.0 }))),
322+
_ => None,
323+
}),
324+
);
325+
326+
map.insert(
327+
"eq",
328+
Box::new(|values| match values {
329+
[Value::Number(a), Value::Number(b)] => Some(Value::Number(Number::Real(if a == b { 1.0 } else { 0.0 }))),
330+
_ => None,
331+
}),
332+
);
333+
334+
map.insert(
335+
"greater",
336+
Box::new(|values| match values {
337+
[Value::Number(Number::Real(a)), Value::Number(Number::Real(b))] => {
338+
Some(Value::Number(Number::Real(if a > b { 1.0 } else { 0.0 })))
339+
},
340+
_ => None,
341+
}),
342+
);
119343

120344
map
121345
};

libraries/math-parser/src/executer.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use num_complex::Complex;
12
use thiserror::Error;
23

34
use crate::{
@@ -27,7 +28,7 @@ impl Node {
2728
},
2829

2930
Node::BinOp { lhs, op, rhs } => match (lhs.eval(context)?, rhs.eval(context)?) {
30-
(Value::Number(lhs), Value::Number(rhs)) => Ok(Value::Number(lhs.binary_op(*op, rhs))),
31+
(Value::Number(lhs), Value::Number(rhs)) => Ok(Value::Number(lhs.binary_op(*op, rhs).ok_or(EvalError::TypeError)?)),
3132
},
3233
Node::UnaryOp { expr, op } => match expr.eval(context)? {
3334
Value::Number(num) => Ok(Value::Number(num.unary_op(*op))),
@@ -43,6 +44,18 @@ impl Node {
4344
context.get_value(name).ok_or_else(|| EvalError::MissingFunction(name.to_string()))
4445
}
4546
}
47+
Node::Conditional { condition, if_block, else_block } => {
48+
let condition = match condition.eval(context)? {
49+
Value::Number(Number::Real(number)) => number != 0.0,
50+
Value::Number(Number::Complex(number)) => number != Complex::ZERO,
51+
};
52+
53+
if condition {
54+
if_block.eval(context)
55+
} else {
56+
else_block.eval(context)
57+
}
58+
}
4659
}
4760
}
4861
}

libraries/math-parser/src/grammer.pest

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,26 @@ WHITESPACE = _{ " " | "\t" }
44
program = _{ SOI ~ expr ~ EOI }
55

66
expr = { atom ~ (infix ~ atom)* }
7-
atom = _{ prefix? ~ primary ~ postfix? }
8-
infix = _{ add | sub | mul | div | pow | paren }
7+
atom = _{ prefix? ~ primary }
8+
infix = _{ add | sub | mul | div | pow | leq | geq | eq | paren }
99
add = { "+" } // Addition
1010
sub = { "-" } // Subtraction
1111
mul = { "*" } // Multiplication
1212
div = { "/" } // Division
1313
mod = { "%" } // Modulo
1414
pow = { "^" } // Exponentiation
15+
leq = { "<" }
16+
geq = { ">" }
17+
eq = { "=="}
1518
paren = { "" } // Implicit multiplication operator
1619

1720
prefix = _{ neg | sqrt }
1821
neg = { "-" } // Negation
1922
sqrt = { "sqrt" }
2023

21-
postfix = _{ fac }
22-
fac = { "!" } // Factorial
2324

24-
primary = _{ ("(" ~ expr ~ ")") | lit | constant | fn_call | ident }
25+
primary = _{ ("(" ~ expr ~ ")") | conditional | lit | constant | fn_call | ident }
26+
conditional = { "if" ~ "(" ~ expr ~ ")" ~ "{" ~ expr ~ "}" ~ "else" ~ "{" ~ expr ~ "}"}
2527
fn_call = { ident ~ "(" ~ expr ~ ("," ~ expr)* ~ ")" }
2628
ident = @{ (ASCII_ALPHA | "_") ~ (ASCII_ALPHANUMERIC | "_")* }
2729
lit = { unit | ((float | int) ~ unit?) }

libraries/math-parser/src/lib.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,5 +147,49 @@ mod tests {
147147
trig_tan_pi_div_four: "tan(pi/4)" => (1.0, Unit::BASE_UNIT),
148148
trig_sin_tau: "sin(tau)" => (0.0, Unit::BASE_UNIT),
149149
trig_cos_tau_div_two: "cos(tau/2)" => (-1.0, Unit::BASE_UNIT),
150+
151+
// Basic if statements
152+
if_true_condition: "if(1){5} else {3}" => (5., Unit::BASE_UNIT),
153+
if_false_condition: "if(0){5} else {3}" => (3., Unit::BASE_UNIT),
154+
155+
// Arithmetic conditions
156+
if_arithmetic_true: "if(2+2-4){1} else {0}" => (0., Unit::BASE_UNIT),
157+
if_arithmetic_false: "if(3*2-5){1} else {0}" => (1., Unit::BASE_UNIT),
158+
159+
// Nested arithmetic
160+
if_complex_arithmetic: "if((5+3)*(2-1)){10} else {20}" => (10., Unit::BASE_UNIT),
161+
if_with_division: "if(8/4-2){15} else {25}" => (15., Unit::BASE_UNIT),
162+
163+
// Constants in conditions
164+
if_with_pi: "if(pi > 3){1} else {0}" => (1., Unit::BASE_UNIT),
165+
if_with_e: "if(e < 3){1} else {0}" => (0., Unit::BASE_UNIT),
166+
167+
// Functions in conditions
168+
if_with_sqrt: "if(sqrt(16) == 4){1} else {0}" => (1., Unit::BASE_UNIT),
169+
if_with_sin: "if(sin(pi)){1} else {0}" => (0., Unit::BASE_UNIT),
170+
171+
// Nested if statements
172+
nested_if: "if(1){if(0){1} else {2}} else {3}" => (2., Unit::BASE_UNIT),
173+
nested_if_complex: "if(2-2){if(1){5} else {6}} else {if(1){7} else {8}}" => (7., Unit::BASE_UNIT),
174+
175+
// If statements with variables
176+
if_with_var: "if(x > 0){1} else {0}" => (1., Unit::BASE_UNIT),
177+
if_var_arithmetic: "if(x + 3 > 5){1} else {0}" => (0., Unit::BASE_UNIT),
178+
179+
// Mixed operations in conditions and blocks
180+
if_complex_condition: "if(sqrt(16) + sin(pi) < 5){2*pi} else {3*e}" => (2. * std::f64::consts::PI, Unit::BASE_UNIT),
181+
if_complex_blocks: "if(1){2*sqrt(16) + sin(pi/2)} else {3*cos(0) + 4}" => (8., Unit::BASE_UNIT),
182+
183+
// Edge cases
184+
if_zero: "if(0.0){1} else {2}" => (2., Unit::BASE_UNIT),
185+
if_negative: "if(-1){1} else {2}" => (1., Unit::BASE_UNIT),
186+
if_infinity: "if(inf){1} else {2}" => (1., Unit::BASE_UNIT),
187+
188+
// Units in if statements
189+
if_with_units: "if(5m > 3m){10s} else {20s}" => (10., Unit::TIME),
190+
if_mixed_units: "if(2km/h > 1m/s){5kg} else {10kg}" => (10., Unit::MASS),
191+
192+
// Complex nested expressions
193+
if_nested_expr: "if((sqrt(16) + 2) * (sin(pi) + 1)){3 + 4 * 2} else {5 - 2 / 1}" => (11., Unit::BASE_UNIT),
150194
}
151195
}

0 commit comments

Comments
 (0)