use mipl::prelude::*;
fn consume_fact(parser: &mut Parser) -> Option<f32> {
let lparen_par = ExactMatch { matches: "(".to_string() };
let rparen_par = ExactMatch { matches: ")".to_string() };
if let Some(_lparen_str) = lparen_par.try_next(parser) {
let res = eval(parser);
let _rparen_str = rparen_par.try_next(parser)?;
res
} else {
let any_str_matcher = AnyStrMatch;
any_str_matcher.try_next(parser)?
.parse::<f32>()
.ok()
}
}
fn consume_term(parser: &mut Parser) -> Option<f32> {
let mut num: f32 = consume_fact(parser)?;
let ops: Vec<String> = vec!["*", "/"]
.into_iter()
.map(String::from)
.collect();
let or_matcher = OrExactMatch::new(ops);
while let Some(op) = or_matcher.try_next(parser) {
match op.as_ref() {
"*" => num *= consume_fact(parser)?,
"/" => num /= consume_fact(parser)?,
_ => panic!("Logically unexpected code path traced")
}
}
Some(num)
}
fn consume_expr(parser: &mut Parser) -> Option<f32> {
let mut term: f32 = consume_term(parser)?;
let ops: Vec<String> = vec!["+", "-"]
.into_iter()
.map(String::from)
.collect();
let or_matcher = OrExactMatch::new(ops);
while let Some(op) = or_matcher.try_next(parser) {
match op.as_ref() {
"+" => term += consume_term(parser)?,
"-" => term -= consume_term(parser)?,
_ => panic!("Logically unexpected code path traced")
}
}
Some(term)
}
fn eval(parser: &mut Parser) -> Option<f32> {
consume_expr(parser)
}
fn setup_parser(source: String) -> Parser {
let d_vec: Vec<char> = vec![' ', '\n', '\t'];
let d_del = DiscardDelimiters::new(d_vec);
let k_vec: Vec<char> = vec!['+', '-', '*', '/', '(', ')'];
let k_del = KeepDelimiters::new(k_vec);
let del_param = DelimitersParam {
keep: k_del, discard: d_del
};
Parser::from(
source,
del_param
)
}
fn eval_expr(expr: String) {
let mut parser = setup_parser(expr);
match eval(&mut parser) {
Some(f) => println!("{}", f),
_ => println!("Error.")
}
}
fn main() {
use std::io::Write;
loop {
print!("[calc] ");
std::io::stdout().flush().unwrap();
let expr = {
let mut buf = String::new();
std::io::stdin().read_line(&mut buf).unwrap();
buf
};
if expr.trim() == "exit" {
break;
}
eval_expr(expr);
}
}
#[cfg(test)]
mod tests {
use mipl::parser;
use super::*;
#[test]
fn test_multiplication() {
let mut parser = setup_parser("6 * 11".to_string());
let res = eval(&mut parser);
assert_eq!(Some(66.0), res);
}
#[test]
fn test_addition() {
let mut parser = setup_parser("32 + 68".to_string());
let res = eval(&mut parser);
assert_eq!(Some(100.0), res);
}
#[test]
fn test_division() {
let mut parser = setup_parser("10 / 2".to_string());
let res = eval(&mut parser);
assert_eq!(Some(5.0), res);
}
#[test]
fn test_subtraction() {
let mut parser = setup_parser("43.3 - 100".to_string());
let res = eval(&mut parser);
assert_eq!(Some(-56.7), res);
}
#[test]
fn test_parentheses() {
let mut parser = setup_parser("2 * (3 + 2) * 2".to_string());
let res = eval(&mut parser);
assert_eq!(Some(20.0), res);
}
#[test]
fn test_newline() {
let mut parser = setup_parser("2 * \n (3 + 2)".to_string());
println!("{:#?}", parser);
let res = eval(&mut parser);
assert_eq!(Some(10.0), res);
}
#[test]
fn test_tab() {
let mut parser = setup_parser("2 * \t (3 + 2)".to_string());
println!("{:#?}", parser);
let res= eval(&mut parser);
assert_eq!(Some(10.0), res)
}
}