use std::fmt; use std::fmt::{Debug, Display, Formatter}; use std::str::FromStr; use nom8::prelude::*; use nom8::{ branch::alt, character::{digit1 as digit, multispace0 as multispace}, multi::many0, sequence::{delimited, preceded}, IResult, }; pub enum Expr { Value(i64), Add(Box, Box), Sub(Box, Box), Mul(Box, Box), Div(Box, Box), Paren(Box), } #[derive(Debug)] pub enum Oper { Add, Sub, Mul, Div, } impl Display for Expr { fn fmt(&self, format: &mut Formatter<'_>) -> fmt::Result { use self::Expr::*; match *self { Value(val) => write!(format, "{}", val), Add(ref left, ref right) => write!(format, "{} + {}", left, right), Sub(ref left, ref right) => write!(format, "{} - {}", left, right), Mul(ref left, ref right) => write!(format, "{} * {}", left, right), Div(ref left, ref right) => write!(format, "{} / {}", left, right), Paren(ref expr) => write!(format, "({})", expr), } } } impl Debug for Expr { fn fmt(&self, format: &mut Formatter<'_>) -> fmt::Result { use self::Expr::*; match *self { Value(val) => write!(format, "{}", val), Add(ref left, ref right) => write!(format, "({:?} + {:?})", left, right), Sub(ref left, ref right) => write!(format, "({:?} - {:?})", left, right), Mul(ref left, ref right) => write!(format, "({:?} * {:?})", left, right), Div(ref left, ref right) => write!(format, "({:?} / {:?})", left, right), Paren(ref expr) => write!(format, "[{:?}]", expr), } } } fn parens(i: &str) -> IResult<&str, Expr> { delimited( multispace, delimited("(", expr.map(|e| Expr::Paren(Box::new(e))), ")"), multispace, )(i) } fn factor(i: &str) -> IResult<&str, Expr> { alt(( delimited(multispace, digit, multispace) .map_res(FromStr::from_str) .map(Expr::Value), parens, ))(i) } fn fold_exprs(initial: Expr, remainder: Vec<(Oper, Expr)>) -> Expr { remainder.into_iter().fold(initial, |acc, pair| { let (oper, expr) = pair; match oper { Oper::Add => Expr::Add(Box::new(acc), Box::new(expr)), Oper::Sub => Expr::Sub(Box::new(acc), Box::new(expr)), Oper::Mul => Expr::Mul(Box::new(acc), Box::new(expr)), Oper::Div => Expr::Div(Box::new(acc), Box::new(expr)), } }) } fn term(i: &str) -> IResult<&str, Expr> { let (i, initial) = factor(i)?; let (i, remainder) = many0(alt(( |i| { let (i, mul) = preceded("*", factor)(i)?; Ok((i, (Oper::Mul, mul))) }, |i| { let (i, div) = preceded("/", factor)(i)?; Ok((i, (Oper::Div, div))) }, )))(i)?; Ok((i, fold_exprs(initial, remainder))) } fn expr(i: &str) -> IResult<&str, Expr> { let (i, initial) = term(i)?; let (i, remainder) = many0(alt(( |i| { let (i, add) = preceded("+", term)(i)?; Ok((i, (Oper::Add, add))) }, |i| { let (i, sub) = preceded("-", term)(i)?; Ok((i, (Oper::Sub, sub))) }, )))(i)?; Ok((i, fold_exprs(initial, remainder))) } #[test] fn factor_test() { assert_eq!( factor(" 3 ").map(|(i, x)| (i, format!("{:?}", x))), Ok(("", String::from("3"))) ); } #[test] fn term_test() { assert_eq!( term(" 3 * 5 ").map(|(i, x)| (i, format!("{:?}", x))), Ok(("", String::from("(3 * 5)"))) ); } #[test] fn expr_test() { assert_eq!( expr(" 1 + 2 * 3 ").map(|(i, x)| (i, format!("{:?}", x))), Ok(("", String::from("(1 + (2 * 3))"))) ); assert_eq!( expr(" 1 + 2 * 3 / 4 - 5 ").map(|(i, x)| (i, format!("{:?}", x))), Ok(("", String::from("((1 + ((2 * 3) / 4)) - 5)"))) ); assert_eq!( expr(" 72 / 2 / 3 ").map(|(i, x)| (i, format!("{:?}", x))), Ok(("", String::from("((72 / 2) / 3)"))) ); } #[test] fn parens_test() { assert_eq!( expr(" ( 1 + 2 ) * 3 ").map(|(i, x)| (i, format!("{:?}", x))), Ok(("", String::from("([(1 + 2)] * 3)"))) ); }