1mod rpn_stack_manipulation;
3mod number_conversion;
4mod parse_rpn;
5mod tokenize;
6mod solve;
7mod parse;
8mod tree;
9mod rpn;
10
11use solve::*;
12use parse::math_parse;
13use std::collections::HashMap;
14use number_conversion::*;
15
16pub struct MathParse {
21 internal: Vec<RPN>
24}
25
26impl MathParse {
27 pub fn parse(expression: &str) -> Result<Self, MathParseErrors> {
33 let parsed_tree = math_parse(expression)?;
34 let internal = rpn::parse_rpn(&parsed_tree)?;
35 Ok(MathParse{internal})
36 }
37
38 pub fn parse_rpn(expression: &str) -> Result<Self, MathParseErrors> {
44 let internal = parse_rpn::parse_rpn(expression)?;
45 Ok(MathParse{internal})
46 }
47}
48
49impl MathParse {
52 pub fn solve_auto(&self, map: Option<&HashMap<String, String>>) -> Result<Result<i64, f64>, MathParseErrors> {
74 let map_function = |s: &str| -> Option<String> {
75 match map {
76 None => None,
77 Some(x) => match x.get(s) {
78 Some(x) => Some(x.clone()),
79 None => None,
80 },
81 }
82 };
83
84 match math_solve(&self.internal, &map_function) {
85 Err(err) => Err(err),
86 Ok(Number::Int(i)) => Ok(Ok(i)),
87 Ok(Number::Float(f)) => Ok(
88 if let Ok(i) = f_to_i_strict(f) {
89 Ok(i)
90 } else {
91 Err(f)
92 }
93 ),
94 }
95 }
96
97 pub fn solve_int(&self, variable_map: Option<&HashMap<String, String>>) -> Result<i64, MathParseErrors> {
125 match self.solve_auto(variable_map)? {
126 Ok(i) => Ok(i),
127 Err(f) => Ok(f_to_i_strict(f)?),
128 }
129 }
130
131 pub fn solve_float(&self, variable_map: Option<&HashMap<String, String>>) -> Result<f64, MathParseErrors> {
149 match self.solve_auto(variable_map)? {
150 Ok(i) => Ok(i as f64),
151 Err(f) => Ok(f),
152 }
153 }
154
155 fn solve_number(&self, variable_map: Option<&HashMap<String, String>>) -> Result<solve::Number, MathParseErrors> {
157 Ok(match self.solve_auto(variable_map)? {
158 Ok(i) => solve::Number::Int(i),
159 Err(f) => solve::Number::Float(f),
160 })
161 }
162}
163
164pub fn contains_math_char(s: &str) -> bool {
176 tokenize::contains_math_char(s)
177}
178
179#[derive(Debug, PartialEq)]
184pub enum MathParseErrors {
185 UnclosedParenthesis,
187
188 UnopenedParenthesis,
190
191 EmptyLine,
194
195 InvalidNumber(String),
197
198 MisplacedOperator(char),
201
202 TrailingOperator,
204
205 IntConversion(f64),
207
208 BinaryOpOnFloat(f64, char),
210
211 ReturnFloatExpectedInt(f64),
213
214 BadOperatorHint(char, &'static str),
216
217 UnexpectedZero,
219
220 UnexpectedNegative,
222
223 InvalidRPNOperator(char),
225
226 UnbalancedStack,
228
229 MathParseInternalBug(String),
232}
233
234use MathParseErrors::*;
235use std::fmt;
236
237impl fmt::Display for MathParseErrors {
238 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
241 match self {
242 UnclosedParenthesis => write!(f, "A parenthesis was opened but never closed."),
243 UnopenedParenthesis => write!(f, "A closing parenthesis was used with no matching open parenthesis."),
244 EmptyLine => write!(f, "The math expression is empty. Or the right hand side of an operator is empty."),
245 InvalidNumber(s) => write!(f, "The expression `{s}` that should have been a number but can't be read."),
246 MisplacedOperator(c) => write!(f, "The operator `{c}`is not where it should be. Or the left hand side of an operator being empty."),
247 TrailingOperator => write!(f, "An operator is the last element of a line of math."),
248 IntConversion(fp) => write!(f, "The floating point number {fp} could not be converted to an int which is needed."),
249 BinaryOpOnFloat(fp, c) => write!(f, "The bitwise operation `{c}` is being performed on the floating point number `{fp}`."),
250 ReturnFloatExpectedInt(fp) => write!(f, "An integer was wanted but the floating point number `{fp}` was returned instead."),
251 BadOperatorHint(c, s) => write!(f, "The operator '{c}' is invalid. Did you meant '{s}'?"),
252 UnexpectedZero => write!(f, "There is a 0 in an operation where it is invalid such as a division or a remainder."),
253 UnexpectedNegative => write!(f, "There is a negative number in an operation where it is invalid such as a logical shift."),
254 InvalidRPNOperator(c) => write!(f, "The operators {c} is not valid when parsing RPN expressions."),
255 UnbalancedStack => write!(f, "The RPN stack does not contains a valid number of elements. There is too much or not enough operators."),
256 MathParseInternalBug(s) => write!(f, "There is a bug in the math-parse library. The error message is the following:\n{s}\nPlease, report it with the input given to the library to the developer of math-parse over here: https://github.com/Arkaeriit/math-parse"),
257 }
258 }
259}
260
261#[derive(Debug, PartialEq, Copy, Clone)]
265pub enum UnaryOp {
266 Not,
267 Minus,
268 Plus
269}
270use crate::UnaryOp::*;
271
272impl UnaryOp {
273 fn from_char(c: char) -> Result<Self, MathParseErrors> {
274 match c {
275 '!' => Ok(Not),
276 '-' => Ok(Minus),
277 '+' => Ok(Plus),
278 x => Err(MathParseInternalBug(format!("{x} is not a valid unary operator."))),
279 }
280 }
281}
282
283impl fmt::Display for UnaryOp {
284 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
285 match self {
286 Not => write!(f, "!"),
287 Minus => write!(f, "-"),
288 Plus => write!(f, "+"),
289 }
290 }
291}
292
293#[derive(Debug, PartialEq, Copy, Clone)]
295pub enum BinaryOp {
296 Multiplication,
297 Division,
298 IntegerDivision,
299 Reminder,
300 Addition,
301 Subtraction,
302 ShiftLeft,
303 ShiftRight,
304 BitwiseAnd,
305 BitwiseOr,
306 BitwiseXor,
307}
308use crate::BinaryOp::*;
309
310impl BinaryOp {
311 fn from_char(c: char) -> Result<Self, MathParseErrors> {
312 match c {
313 '*' | '×' | '·' => Ok(Multiplication),
314 '/' | '∕' | '⁄' | '÷' => Ok(Division),
315 '+' => Ok(Addition),
316 '-' | '−' => Ok(Subtraction),
317 '%' => Ok(Reminder),
318 '⟌' => Ok(IntegerDivision),
319 '|' => Ok(BitwiseOr),
320 '&' => Ok(BitwiseAnd),
321 '^' => Ok(BitwiseXor),
322 '≪' => Ok(ShiftLeft),
323 '≫' => Ok(ShiftRight),
324 '<' => Err(BadOperatorHint('<', "<<")),
325 '>' => Err(BadOperatorHint('>', ">>")),
326 x => Err(MathParseInternalBug(format!("{x} is not a valid operator."))),
327 }
328 }
329}
330
331impl fmt::Display for BinaryOp {
332 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
333 match self {
334 Multiplication => write!(f, "*"),
335 Division => write!(f, "/"),
336 IntegerDivision => write!(f, "//"),
337 Reminder => write!(f, "%"),
338 Addition => write!(f, "+"),
339 Subtraction => write!(f, "-"),
340 ShiftLeft => write!(f, "<<"),
341 ShiftRight => write!(f, ">>"),
342 BitwiseAnd => write!(f, "&"),
343 BitwiseOr => write!(f, "|"),
344 BitwiseXor => write!(f, "⊕"), }
346 }
347}
348
349#[derive(Debug, PartialEq, Clone)]
354pub enum RPN {
355 Name(String),
356 Unary(UnaryOp),
357 Binary(BinaryOp),
358}
359
360impl fmt::Display for RPN {
361 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
362 match self {
363 RPN::Name(x) => write!(f, "{x}"),
364 RPN::Unary(x) => write!(f, "{x}"),
365 RPN::Binary(x) => write!(f, "{x}"),
366 }
367 }
368}
369
370impl MathParse {
371 pub fn to_rpn(&self) -> Result<Vec<RPN>, MathParseErrors> {
386 Ok(self.internal.clone())
387 }
388}
389
390pub fn rpn_slice_to_string(rpn: &[RPN]) -> String {
401 let mut ret = String::new();
402 if rpn.len() == 0 {
403 return ret;
404 }
405 ret.push_str(&format!("{}", rpn[0]));
406 for i in 1..rpn.len() {
407 ret.push_str(&format!(" {}", rpn[i]));
408 }
409 ret
410}
411
412#[derive(Debug, PartialEq, Clone)]
416pub enum Tree {
417 Name(String),
418 Unary(UnaryOp, Box<Tree>),
419 Binary(BinaryOp, Box<Tree>, Box<Tree>),
420}
421
422
423impl MathParse {
424 pub fn to_tree(&self) -> Result<Tree, MathParseErrors> {
444 tree::parse_to_tree(&self.internal)
445 }
446}
447
448impl fmt::Display for Tree {
449 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
459 enum TreeFmt {
460 S(String),
461 T(Tree),
462 } use TreeFmt::*;
463 use Tree::*;
464
465 let mut to_format = vec![T(self.clone())];
466 while to_format.len() != 0 {
467 match to_format.pop().unwrap() {
468 T(Name(s)) => {
469 write!(f, "{s}")?;
470 },
471 T(Unary(op, next)) => {
472 to_format.push(T(*next));
473 write!(f, "{op}")?;
474 },
475 T(Binary(op, next_1, next_2)) => {
476 write!(f, "(")?;
477 to_format.push(S(")".to_string()));
478 to_format.push(T(*next_2));
479 to_format.push(S(format!(" {op} ")));
480 to_format.push(T(*next_1));
481 },
482 S(s) => {
483 write!(f, "{s}")?;
484 }
485 }
486 }
487 Ok(())
488 }
489}
490
491#[cfg(test)]
494fn name_r(s: &str) -> RPN {
495 RPN::Name(s.to_string())
496}
497#[cfg(test)]
498fn name_p(s: &str) -> tokenize::MathValue {
499 tokenize::MathValue::Name(s)
500}
501#[cfg(test)]
502fn name_t(s: &str) -> Tree {
503 Tree::Name(s.to_string())
504}
505
506#[cfg(test)]
507fn math_solve_int(expression: &str) -> Result<i64, MathParseErrors> {
508 MathParse::parse(expression)?.solve_int(None)
509}
510#[cfg(test)]
511fn math_solve_float(expression: &str) -> Result<f64, MathParseErrors> {
512 MathParse::parse(expression)?.solve_float(None)
513}
514#[cfg(test)]
515fn compute(expression: &str, variable_map: Option<&HashMap<String, String>>) -> Result<solve::Number, MathParseErrors> {
516 MathParse::parse(expression)?.solve_number(variable_map)
517}
518#[cfg(test)]
519fn parse_rpn(expression: &str) -> Result<Vec<RPN>, MathParseErrors> {
520 MathParse::parse(expression)?.to_rpn()
521}
522
523#[test]
524fn test_math_compute() {
525 let a = 3;
526 let b = 9;
527 let c = 3*5;
528 let variables = HashMap::from([
529 ("a".to_string(), "3".to_string()),
530 ("b".to_string(), "9".to_string()),
531 ("c".to_string(), "(((3)*(5)))".to_string()),
532 ]);
533
534 let compute_int = |input: &str, output: i64| {
535 let res = compute(input, Some(&variables)).unwrap();
536 if let Number::Int(res) = res {
537 assert_eq!(res, output);
538 } else {
539 panic!("Expected integer instead of float.");
540 }
541 };
542
543 fn compute_float (input: &str, output: f64) {
544 let res = compute(input, None).unwrap();
545 match res {
546 Number::Float(res) => {
547 assert_eq!(res, output);
548 },
549 Number::Int(res) => {
550 assert_eq!(res as f64, output);
551 },
552 }
553 }
554
555 compute_int("((3+3)·b+8)*(a-1)", ((3+3)*b+8)*(a-1));
556 compute_int("0", 0);
557 compute_int("-a + b − c", -a + b - c);
558 compute_int("-−-+++-a", ----a);
559 compute_int("3%8+99", 3%8+99);
560 compute_int("10.0//3.0", 10/3);
561 compute_int("!-4", !-4);
562 compute_int("((3+4)*(8+(4-1)))-(43+8//2+1)", ((3+4) * (8+(4-1))) - (43+8/2+1));
563 compute_int("((0xFF&0xF)|0x10)^0x3", ((0xFF & 0xF) | 0x10) ^ 0x3);
564 compute_int("(10<<5)>>(2<<1)", (10 << 5) >> (2 << 1));
565
566 compute_float("4×9/4", 4.0*9.0/4.0);
567 compute_float("4×9/4.0", 4.0*9.0/4.0);
568 compute_float("4.0*9/4", 4.0*9.0/4.0);
569 compute_float("4.0·9.0/4", 4.0*9.0/4.0);
570 compute_float("4*9.0/4", 4.0*9.0/4.0);
571 compute_float("4*9.0/4.0", 4.0*9.0/4.0);
572 compute_float("4.0+9-4", 4.0+9.0-4.0);
573 compute_float("4+9-4.0", 4.0+9.0-4.0);
574 compute_float("4.0+9-4", 4.0+9.0-4.0);
575 compute_float("4.0+9.0-4", 4.0+9.0-4.0);
576 compute_float("4+9.0-4", 4.0+9.0-4.0);
577 compute_float("4+9.0-4.0", 4.0+9.0-4.0);
578
579 compute_float("4.5%2.1", 4.5 % 2.1);
580 compute_float("8%2.2", 8.0 % 2.2);
581 compute_float("33.4%2", 33.4 % 2.0);
582}
583
584#[test]
585fn test_butchered_rpn() {
586 match parse_rpn("3++") {
587 Ok(x) => {
588 panic!("{x:?} should not have been solved.");
589 },
590 Err(TrailingOperator) => {
591 },
593 Err(x) => {
594 panic!("{x:?} is not the expected error.");
595 }
596 }
597}
598
599#[test]
600fn test_api() {
601 assert_eq!(math_solve_int("3+3"), Ok(6));
602 assert_eq!(math_solve_int("3.0+3.0"), Ok(6));
603 assert_eq!(math_solve_int("3.2+3.0"), Err(ReturnFloatExpectedInt(6.2)));
604
605 assert_eq!(math_solve_float("3+3" ), Ok(6.0));
606 assert_eq!(math_solve_float("3.0+3.0"), Ok(6.0));
607
608 assert_eq!(contains_math_char("ab+cd"), true);
609 assert_eq!(contains_math_char("abcd"), false);
610}
611
612#[test]
613fn test_bitwise_on_float() {
614 fn test_operator(op: char) {
615 let exp = format!("3.1{op}4.2");
616 assert_eq!(math_solve_int(&exp), Err(BinaryOpOnFloat(3.1, op)));
617 }
618
619 let operators = ['^', '|', '&', '≪', '≫'];
620 for op in &operators {
621 test_operator(*op);
622 }
623}
624
625#[test]
626fn test_operator_hints() {
627 assert_eq!(math_solve_int("3876<4"), Err(BadOperatorHint('<', "<<")));
628 assert_eq!(math_solve_int("3876>4"), Err(BadOperatorHint('>', ">>")));
629}
630
631#[test]
632fn test_to_rpn() {
633 use RPN::*;
634 use UnaryOp::*;
635 use BinaryOp::*;
636 assert_eq!(parse_rpn("8/2").unwrap(), vec![name_r("8"), name_r("2"), Binary(Division)]);
637 assert_eq!(parse_rpn("-3+4").unwrap(), vec![name_r("3"), Unary(Minus), name_r("4"), Binary(Addition)]);
638}
639
640#[test]
641fn test_parse_rpn() {
642 fn solve_rpn(expression: &str) -> Result<i64, MathParseErrors> {
643 MathParse::parse_rpn(expression)?.solve_int(None)
644 }
645
646 assert_eq!(solve_rpn("3 4 + 2 *"), Ok(14));
647 assert_eq!(solve_rpn("3 4 2 + *"), Ok(18));
648 assert_eq!(solve_rpn("3 (4 + 3) 2 + *"), Err(InvalidRPNOperator('(')));
649 assert_eq!(solve_rpn("3 2 + *"), Err(UnbalancedStack));
650}
651
652#[test]
653fn test_misc_errors() {
654 match MathParse::parse("3 3 +") {
655 Err(EmptyLine) => {},
656 Ok(_) => {panic!("Should not have been solved.");},
657 Err(x) => {panic!("Should not have been {x:?}");},
658 }
659
660 assert_eq!(compute("1 - (1*3)", None), Ok(Number::Int(-2)));
661 assert_eq!(compute("1+-1", None), Ok(Number::Int(0)));
662 assert_eq!(compute("1 + - 1", None), Ok(Number::Int(0)));
663}
664
665#[test]
666fn test_readme_example() {
667 let num1: i64 = MathParse::parse("(1+2)*3").unwrap().solve_int(None).unwrap();
668 assert_eq!(num1, 9); let num2: f64 = MathParse::parse("5/8+6").unwrap().solve_float(None).unwrap();
671 assert_eq!(num2, 6.625); let parsed = MathParse::parse("(2+3)*2/5").unwrap().to_tree().unwrap();
674 assert_eq!(
675 format!("{parsed}").as_str(),
676 "(((2 + 3) * 2) / 5)".to_string());
677}