math_parse/
lib.rs

1/// Module containing the function to parse math expressions.
2mod 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
16/* --------------------------------- Parsing -------------------------------- */
17
18/// Object generated when parsing a string of math. Can be later used for
19/// solving or formatting to other representations.
20pub struct MathParse {
21    // Internal representation of parsed math is the RPN one. Might or might
22    // not change in the future.
23    internal: Vec<RPN>
24}
25
26impl MathParse {
27    /// Parse a math expression in infix notation.
28    ///
29    /// ```
30    /// math_parse::MathParse::parse("3 + 4").unwrap();
31    /// ```
32    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    /// Parse a math expression in postfix notation (RPN).
39    ///
40    /// ```
41    /// math_parse::MathParse::parse_rpn("3 4 +").unwrap();
42    /// ```
43    pub fn parse_rpn(expression: &str) -> Result<Self, MathParseErrors> {
44        let internal = parse_rpn::parse_rpn(expression)?;
45        Ok(MathParse{internal})
46    }
47}
48
49/* --------------------------------- Solving -------------------------------- */
50
51impl MathParse {
52    /// Does all the computation from a string with a line of math to the final
53    /// resulting number. If the result can be an int, return it as
54    /// `Ok(Ok(int))`. If it can only be a float, return it as `Ok(Err(floar))`.
55    /// If it can't be solved, return `Err(error)`.
56    ///
57    /// ```
58    /// use math_parse::MathParse;
59    /// use math_parse::MathParseErrors::*;
60    ///
61    /// assert_eq!(
62    ///     MathParse::parse("3 + 8.0").unwrap().solve_auto(None),
63    ///     Ok(Ok(11)));
64    /// assert_eq!(
65    ///     MathParse::parse("3 - 8.5").unwrap().solve_auto(None),
66    ///     Ok(Err(-5.5)));
67    /// assert_eq!(
68    ///     MathParse::parse("34 + bcd").unwrap().solve_auto(None),
69    ///     Err(InvalidNumber("bcd".to_string())));
70    /// ```
71    ///
72    /// A optional map of variable name can be taken as argument.
73    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    /// Does all the computation from a string with a line of math to the final
98    /// resulting number. If the result can be an int, return it as
99    /// `Ok(int)`. If it can't be solved as an int, return `Err(error)`.
100    ///
101    /// ```
102    /// use math_parse::MathParse;
103    /// use math_parse::MathParseErrors::*;
104    ///
105    /// assert_eq!(
106    ///     MathParse::parse("3 + 8.0").unwrap().solve_int(None),
107    ///     Ok(11));
108    /// assert_eq!(
109    ///     MathParse::parse("3 - 8.5").unwrap().solve_int(None),
110    ///     Err(ReturnFloatExpectedInt(-5.5)));
111    /// ```
112    ///
113    /// A optional map of variable name can be taken as argument:
114    /// ```
115    /// use math_parse::MathParse;
116    ///
117    /// let variables = std::collections::HashMap::from([
118    ///     ("a".to_string(), "1".to_string()),
119    ///     ("b".to_string(), "3*3".to_string()),
120    /// ]);
121    /// let result = MathParse::parse("a+b").unwrap().solve_int(Some(&variables)).unwrap();
122    /// assert_eq!(result, 10);
123    /// ```
124    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    /// Does all the computation from a string with a line of math to the final
132    /// resulting number. The result is returned as a `Ok(f64)`.
133    /// If it can't be solved, return `Err(error)`.
134    ///
135    /// ```
136    /// use math_parse::MathParse;
137    /// use math_parse::MathParseErrors::*;
138    ///
139    /// assert_eq!(
140    ///     MathParse::parse("3 + 8").unwrap().solve_float(None),
141    ///     Ok(11.0));
142    /// assert_eq!(
143    ///     MathParse::parse("3 - 8.5").unwrap().solve_float(None),
144    ///     Ok(-5.5));
145    /// ```
146    ///
147    /// A optional map of variable name can be taken as argument.
148    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    /// Solve the result as a number, for internal use.
156    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
164/* ---------------------------------- Misc. --------------------------------- */
165
166/// Return true if the given string contains any character that are used as
167/// operators inside of math-parse
168///
169/// Example:
170/// ```
171/// use math_parse::contains_math_char;
172/// assert_eq!(contains_math_char("abcd"), false);
173/// assert_eq!(contains_math_char("ab+cd"), true);
174/// ```
175pub fn contains_math_char(s: &str) -> bool {
176    tokenize::contains_math_char(s)
177}
178
179/* --------------------------------- Errors --------------------------------- */
180
181/// Type used to represent any errors that can happen in the parsing of a math
182/// expression.
183#[derive(Debug, PartialEq)]
184pub enum MathParseErrors {
185    /// A parenthesis was opened but never closed.
186    UnclosedParenthesis,
187
188    /// A closing parenthesis was used with no matching open parenthesis.
189    UnopenedParenthesis,
190
191    /// The math expression is empty. Or the right hand side of an operator is
192    /// empty.
193    EmptyLine,
194
195    /// An expression that should have been a number but can't be read.
196    InvalidNumber(String),
197
198    /// An operator is not where it should be. Like a "*" after a "+", or the
199    /// left hand side of an operator being empty.
200    MisplacedOperator(char),
201
202    /// An operator is the last element of a line of math.
203    TrailingOperator,
204
205    /// A float could not be converted to an int.
206    IntConversion(f64),
207
208    /// A binary operation have been tried on a float.
209    BinaryOpOnFloat(f64, char),
210
211    /// We wanted to return an int but we got a float instead.
212    ReturnFloatExpectedInt(f64),
213
214    /// A given operator was invalid, but we can suggest an other instead.
215    BadOperatorHint(char, &'static str),
216
217    /// There was an unwanted zero.
218    UnexpectedZero,
219
220    /// There was an unwanted negative number.
221    UnexpectedNegative,
222
223    /// An operator is not valid in the context of RPN parsing.
224    InvalidRPNOperator(char),
225
226    /// The number of elements on the RPN stack is not valid.
227    UnbalancedStack,
228
229    /// This error should never be raised and should be reported to the
230    /// library's maintainer.
231    MathParseInternalBug(String),
232}
233
234use MathParseErrors::*;
235use std::fmt;
236
237impl fmt::Display for MathParseErrors {
238    /// From a `MathParseError`, makes an error message that could even be
239    /// shown to the final user.
240    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/* ------------------------------- Operations ------------------------------- */
262
263/// Available unary operations.
264#[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/// Available binary operations.
294#[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, "⊕"), // Not ^ in order not to mistake it for exponentiation.
345        }
346    }
347}
348
349/* ----------------------------------- RPN ---------------------------------- */
350
351/// Elements that make a list of RPN instruction extracted from a math
352/// expression.
353#[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    /// Parse a math expression into list of instructions in Reverse Polish
372    /// notation (postfix notation).
373    ///
374    /// Example:
375    /// ```
376    /// use math_parse::RPN::*;
377    /// use math_parse::UnaryOp::*;
378    /// use math_parse::BinaryOp::*;
379    /// use math_parse::MathParse;
380    ///
381    /// assert_eq!(
382    ///     MathParse::parse("3-4+(-5)").unwrap().to_rpn(),
383    ///     Ok(vec![Name("3".to_string()), Name("4".to_string()), Binary(Subtraction), Name("5".to_string()), Unary(Minus), Binary(Addition)]));
384    /// ```
385    pub fn to_rpn(&self) -> Result<Vec<RPN>, MathParseErrors> {
386        Ok(self.internal.clone())
387    }
388}
389
390/// Shows a representation of an expression formatted into RPN.
391///
392/// Example:
393/// ```
394/// use math_parse::*;
395/// let rpn = MathParse::parse("3+1*2").unwrap().to_rpn().unwrap();
396/// assert_eq!(
397///     rpn_slice_to_string(&rpn),
398///     "3 1 2 * +".to_string());
399/// ```
400pub 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/* ------------------------------ Tree notation ----------------------------- */
413
414/// Parsed element showed in a tree in infix notation. 
415#[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    /// Parse a math expression into list of instructions as a Tree (infix
425    /// notation).
426    ///
427    /// Example:
428    /// ```
429    /// use math_parse::Tree::*;
430    /// use math_parse::UnaryOp::*;
431    /// use math_parse::BinaryOp::*;
432    /// use math_parse::MathParse;
433    ///
434    /// assert_eq!(
435    ///     MathParse::parse("3*4+(-5)").unwrap().to_tree(),
436    ///     Ok(Binary(Addition,
437    ///         Box::new(Binary(Multiplication,
438    ///             Box::new(Name("3".to_string())),
439    ///             Box::new(Name("4".to_string())))),
440    ///         Box::new(Unary(Minus,
441    ///             Box::new(Name("5".to_string())))))));
442    /// ```
443    pub fn to_tree(&self) -> Result<Tree, MathParseErrors> {
444        tree::parse_to_tree(&self.internal)
445    }
446}
447
448impl fmt::Display for Tree {
449    /// Show a tree as an infix expression.
450    ///
451    /// Example:
452    /// ```
453    /// use math_parse::*;
454    /// assert_eq!(
455    ///     format!("{}", MathParse::parse("(2+3)*2/5").unwrap().to_tree().unwrap()),
456    ///     "(((2 + 3) * 2) / 5)".to_string());
457    /// ```
458    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/* --------------------------------- Testing -------------------------------- */
492
493#[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            // Expected result
592        },
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) => {/* Expected */},
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); // Prints 9
669
670    let num2: f64 = MathParse::parse("5/8+6").unwrap().solve_float(None).unwrap();
671    assert_eq!(num2, 6.625); // Prints 6.625
672
673    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}