calculator/
calculator.rs

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}