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