1use std::fmt; 2use std::fmt::{Debug, Display, Formatter}; 3 4use std::str::FromStr; 5 6use nom::{ 7 branch::alt, 8 bytes::complete::tag, 9 character::complete::{digit1 as digit, multispace0 as multispace}, 10 combinator::{map, map_res}, 11 multi::many0, 12 sequence::{delimited, preceded}, 13 IResult, 14}; 15 16pub enum Expr { 17 Value(i64), 18 Add(Box<Expr>, Box<Expr>), 19 Sub(Box<Expr>, Box<Expr>), 20 Mul(Box<Expr>, Box<Expr>), 21 Div(Box<Expr>, Box<Expr>), 22 Paren(Box<Expr>), 23} 24 25#[derive(Debug)] 26pub enum Oper { 27 Add, 28 Sub, 29 Mul, 30 Div, 31} 32 33impl Display for Expr { 34 fn fmt(&self, format: &mut Formatter<'_>) -> fmt::Result { 35 use self::Expr::*; 36 match *self { 37 Value(val) => write!(format, "{}", val), 38 Add(ref left, ref right) => write!(format, "{} + {}", left, right), 39 Sub(ref left, ref right) => write!(format, "{} - {}", left, right), 40 Mul(ref left, ref right) => write!(format, "{} * {}", left, right), 41 Div(ref left, ref right) => write!(format, "{} / {}", left, right), 42 Paren(ref expr) => write!(format, "({})", expr), 43 } 44 } 45} 46 47impl Debug for Expr { 48 fn fmt(&self, format: &mut Formatter<'_>) -> fmt::Result { 49 use self::Expr::*; 50 match *self { 51 Value(val) => write!(format, "{}", val), 52 Add(ref left, ref right) => write!(format, "({:?} + {:?})", left, right), 53 Sub(ref left, ref right) => write!(format, "({:?} - {:?})", left, right), 54 Mul(ref left, ref right) => write!(format, "({:?} * {:?})", left, right), 55 Div(ref left, ref right) => write!(format, "({:?} / {:?})", left, right), 56 Paren(ref expr) => write!(format, "[{:?}]", expr), 57 } 58 } 59} 60 61fn parens(i: &str) -> IResult<&str, Expr> { 62 delimited( 63 multispace, 64 delimited(tag("("), map(expr, |e| Expr::Paren(Box::new(e))), tag(")")), 65 multispace, 66 )(i) 67} 68 69fn factor(i: &str) -> IResult<&str, Expr> { 70 alt(( 71 map( 72 map_res(delimited(multispace, digit, multispace), FromStr::from_str), 73 Expr::Value, 74 ), 75 parens, 76 ))(i) 77} 78 79fn fold_exprs(initial: Expr, remainder: Vec<(Oper, Expr)>) -> Expr { 80 remainder.into_iter().fold(initial, |acc, pair| { 81 let (oper, expr) = pair; 82 match oper { 83 Oper::Add => Expr::Add(Box::new(acc), Box::new(expr)), 84 Oper::Sub => Expr::Sub(Box::new(acc), Box::new(expr)), 85 Oper::Mul => Expr::Mul(Box::new(acc), Box::new(expr)), 86 Oper::Div => Expr::Div(Box::new(acc), Box::new(expr)), 87 } 88 }) 89} 90 91fn term(i: &str) -> IResult<&str, Expr> { 92 let (i, initial) = factor(i)?; 93 let (i, remainder) = many0(alt(( 94 |i| { 95 let (i, mul) = preceded(tag("*"), factor)(i)?; 96 Ok((i, (Oper::Mul, mul))) 97 }, 98 |i| { 99 let (i, div) = preceded(tag("/"), factor)(i)?; 100 Ok((i, (Oper::Div, div))) 101 }, 102 )))(i)?; 103 104 Ok((i, fold_exprs(initial, remainder))) 105} 106 107fn expr(i: &str) -> IResult<&str, Expr> { 108 let (i, initial) = term(i)?; 109 let (i, remainder) = many0(alt(( 110 |i| { 111 let (i, add) = preceded(tag("+"), term)(i)?; 112 Ok((i, (Oper::Add, add))) 113 }, 114 |i| { 115 let (i, sub) = preceded(tag("-"), term)(i)?; 116 Ok((i, (Oper::Sub, sub))) 117 }, 118 )))(i)?; 119 120 Ok((i, fold_exprs(initial, remainder))) 121} 122 123#[test] 124fn factor_test() { 125 assert_eq!( 126 factor(" 3 ").map(|(i, x)| (i, format!("{:?}", x))), 127 Ok(("", String::from("3"))) 128 ); 129} 130 131#[test] 132fn term_test() { 133 assert_eq!( 134 term(" 3 * 5 ").map(|(i, x)| (i, format!("{:?}", x))), 135 Ok(("", String::from("(3 * 5)"))) 136 ); 137} 138 139#[test] 140fn expr_test() { 141 assert_eq!( 142 expr(" 1 + 2 * 3 ").map(|(i, x)| (i, format!("{:?}", x))), 143 Ok(("", String::from("(1 + (2 * 3))"))) 144 ); 145 assert_eq!( 146 expr(" 1 + 2 * 3 / 4 - 5 ").map(|(i, x)| (i, format!("{:?}", x))), 147 Ok(("", String::from("((1 + ((2 * 3) / 4)) - 5)"))) 148 ); 149 assert_eq!( 150 expr(" 72 / 2 / 3 ").map(|(i, x)| (i, format!("{:?}", x))), 151 Ok(("", String::from("((72 / 2) / 3)"))) 152 ); 153} 154 155#[test] 156fn parens_test() { 157 assert_eq!( 158 expr(" ( 1 + 2 ) * 3 ").map(|(i, x)| (i, format!("{:?}", x))), 159 Ok(("", String::from("([(1 + 2)] * 3)"))) 160 ); 161} 162