blob: d48b9028b9f71d2ae318e1e6fbb46d6a76545344 [file] [log] [blame]
#[macro_use]
extern crate nom;
use std::fmt;
use std::fmt::{Display, Debug, Formatter};
use std::str;
use std::str::FromStr;
use nom::{IResult, digit, multispace};
pub enum Expr {
Value(i64),
Add(Box<Expr>, Box<Expr>),
Sub(Box<Expr>, Box<Expr>),
Mul(Box<Expr>, Box<Expr>),
Div(Box<Expr>, Box<Expr>),
Paren(Box<Expr>),
}
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),
}
}
}
named!(parens< Expr >, delimited!(
delimited!(opt!(multispace), tag!("("), opt!(multispace)),
map!(map!(expr, Box::new), Expr::Paren),
delimited!(opt!(multispace), tag!(")"), opt!(multispace))
)
);
named!(factor< Expr >, alt_complete!(
map!(
map_res!(
map_res!(
delimited!(opt!(multispace), digit, opt!(multispace)),
str::from_utf8
),
FromStr::from_str
),
Expr::Value)
| parens
)
);
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)),
}
})
}
named!(term< Expr >, do_parse!(
initial: factor >>
remainder: many0!(
alt!(
do_parse!(tag!("*") >> mul: factor >> (Oper::Mul, mul)) |
do_parse!(tag!("/") >> div: factor >> (Oper::Div, div))
)
) >>
(fold_exprs(initial, remainder))
));
named!(expr< Expr >, do_parse!(
initial: term >>
remainder: many0!(
alt!(
do_parse!(tag!("+") >> add: term >> (Oper::Add, add)) |
do_parse!(tag!("-") >> sub: term >> (Oper::Sub, sub))
)
) >>
(fold_exprs(initial, remainder))
));
#[test]
fn factor_test() {
assert_eq!(factor(&b" 3 "[..]).map(|x| format!("{:?}", x)),
IResult::Done(&b""[..], String::from("3")));
}
#[test]
fn term_test() {
assert_eq!(term(&b" 3 * 5 "[..]).map(|x| format!("{:?}", x)),
IResult::Done(&b""[..], String::from("(3 * 5)")));
}
#[test]
fn expr_test() {
assert_eq!(expr(&b" 1 + 2 * 3 "[..]).map(|x| format!("{:?}", x)),
IResult::Done(&b""[..], String::from("(1 + (2 * 3))")));
assert_eq!(expr(&b" 1 + 2 * 3 / 4 - 5 "[..]).map(|x| format!("{:?}", x)),
IResult::Done(&b""[..], String::from("((1 + ((2 * 3) / 4)) - 5)")));
assert_eq!(expr(&b" 72 / 2 / 3 "[..]).map(|x| format!("{:?}", x)),
IResult::Done(&b""[..], String::from("((72 / 2) / 3)")));
}
#[test]
fn parens_test() {
assert_eq!(expr(&b" ( 1 + 2 ) * 3 "[..]).map(|x| format!("{:?}", x)),
IResult::Done(&b""[..], String::from("([(1 + 2)] * 3)")));
}