1extern crate honeycomb;
2use honeycomb::{
3 atoms::{eof, rec, seq, space, sym},
4 language::number,
5 transform::to_number,
6 Parser,
7};
8
9use std::io::{stdin, stdout, Write};
10use std::sync::Arc;
11
12#[derive(Clone, Debug)]
13enum Math {
14 Add(Arc<Math>, Arc<Math>),
15 Multiply(Arc<Math>, Arc<Math>),
16 Divide(Arc<Math>, Arc<Math>),
17 Subtract(Arc<Math>, Arc<Math>),
18 Number(f64),
19 Exit,
20 Clear,
21 EOF,
22}
23
24fn token(symbol: &'static str) -> Parser<String> {
25 space() >> seq(symbol) << space()
26}
27
28fn operation(symbol: char, map_fn: fn((Math, Math)) -> Math) -> Parser<Math> {
29 (number() - to_number - Math::Number | rec(math))
30 .suffix(space() & sym(symbol) & space())
31 .and(rec(math))
32 - map_fn
33}
34
35fn add() -> Parser<Math> {
36 operation('+', |m| Math::Add(Arc::new(m.0), Arc::new(m.1)))
37}
38
39fn multiply() -> Parser<Math> {
40 operation('*', |m| Math::Multiply(Arc::new(m.0), Arc::new(m.1)))
41}
42
43fn divide() -> Parser<Math> {
44 operation('/', |m| Math::Divide(Arc::new(m.0), Arc::new(m.1)))
45}
46
47fn subtract() -> Parser<Math> {
48 operation('-', |m| Math::Subtract(Arc::new(m.0), Arc::new(m.1)))
49}
50
51fn exit() -> Parser<Math> {
52 (seq("exit") | seq("quit")) - |_| Math::Exit
53}
54
55fn clear() -> Parser<Math> {
56 seq("clear") - |_| Math::Clear
57}
58
59fn math() -> Parser<Math> {
60 exit()
61 | eof() - (|_| Math::EOF)
62 | clear()
63 | token("(") >> rec(math) << token(")")
64 | (number().is()
65 >> (multiply() | divide() | add() | subtract() | (number() - to_number - Math::Number)))
66}
67
68fn eval(math: Math) -> f64 {
69 match math {
70 Math::Number(n) => n,
71 Math::Add(a, b) => eval((*a).clone()) + eval((*b).clone()),
72 Math::Subtract(a, b) => eval((*a).clone()) - eval((*b).clone()),
73 Math::Divide(a, b) => eval((*a).clone()) / eval((*b).clone()),
74 Math::Multiply(a, b) => eval((*a).clone()) * eval((*b).clone()),
75 Math::Exit => std::process::exit(0),
76 Math::Clear => {
77 println!("{}", "\n".repeat(1000));
78 0.0
79 }
80 _ => 0.0,
81 }
82}
83
84fn input(prompt: &str) -> String {
85 let mut s = String::new();
86 print!("{}", prompt);
87
88 let _ = stdout().flush();
89 stdin()
90 .read_line(&mut s)
91 .expect("Did not enter a correct string");
92
93 if let Some('\n') = s.chars().next_back() {
94 s.pop();
95 }
96 if let Some('\r') = s.chars().next_back() {
97 s.pop();
98 }
99
100 s
101}
102
103fn main() {
104 loop {
105 let output = input(">>> ");
106
107 match math().parse(&output) {
108 Ok(m) => println!("{:#?}\n\nResult: {}", m.clone(), eval(m)),
109 Err(_) => println!("Invalid math expression!"),
110 }
111 }
112}