| #[macro_use] |
| extern crate nom; |
| |
| use nom::{ |
| branch::alt, |
| sequence::{delimited, pair, preceded}, |
| character::complete::{digit1 as digit, space0 as space}, |
| bytes::complete::tag |
| }; |
| |
| // Parser definition |
| |
| use std::str; |
| use std::str::FromStr; |
| |
| use self::Operator::*; |
| |
| enum Operator { |
| Slash, |
| Star, |
| } |
| |
| impl Operator { |
| fn to_str(&self) -> &'static str { |
| match *self { |
| Slash => "/", |
| Star => "*", |
| } |
| } |
| } |
| |
| // Parse the specified `Operator`. |
| named_args!(operator(op: Operator) <&[u8], &[u8]>, |
| call!(tag(op.to_str())) |
| ); |
| |
| // We parse any expr surrounded by the tags `open_tag` and `close_tag`, ignoring all whitespaces around those |
| named_args!(brackets<'a>(open_tag: &str, close_tag: &str) <&'a[u8], i64>, |
| call!(delimited( |
| space, |
| delimited(tag(open_tag), preceded(space, expr), preceded(space, tag(close_tag))), |
| space |
| )) |
| ); |
| |
| fn byte_slice_to_str<'a>(s: &'a[u8]) -> Result<&'a str, str::Utf8Error> { |
| str::from_utf8(s) |
| } |
| |
| // We transform an integer string into a i64, ignoring surrounding whitespaces |
| // We look for a digit suite, and try to convert it. |
| // If either str::from_utf8 or FromStr::from_str fail, |
| // we fallback to the brackets parser defined above |
| named!(factor<&[u8], i64>, alt!( |
| map_res!( |
| map_res!( |
| call!(delimited(space, digit, space)), |
| byte_slice_to_str |
| ), |
| FromStr::from_str |
| ) |
| | call!(brackets, "(", ")") |
| ) |
| ); |
| |
| // We read an initial factor and for each time we find |
| // a * or / operator followed by another factor, we do |
| // the math by folding everything |
| named!(term <&[u8], i64>, do_parse!( |
| init: factor >> |
| res: fold_many0!( |
| pair!(alt!(call!(operator, Star) | call!(operator, Slash)), factor), |
| init, |
| |acc, (op, val): (&[u8], i64)| { |
| if (op[0] as char) == '*' { acc * val } else { acc / val } |
| } |
| ) >> |
| (res) |
| ) |
| ); |
| |
| named!(expr <&[u8], i64>, do_parse!( |
| init: term >> |
| res: fold_many0!( |
| call!(pair(alt((tag("+"), tag("-"))), term)), |
| init, |
| |acc, (op, val): (&[u8], i64)| { |
| if (op[0] as char) == '+' { acc + val } else { acc - val } |
| } |
| ) >> |
| (res) |
| ) |
| ); |
| |
| #[test] |
| fn factor_test() { |
| assert_eq!( |
| factor(&b"3"[..]), |
| Ok((&b""[..], 3)) |
| ); |
| assert_eq!( |
| factor(&b" 12"[..]), |
| Ok((&b""[..], 12)) |
| ); |
| assert_eq!( |
| factor(&b"537 "[..]), |
| Ok((&b""[..], 537)) |
| ); |
| assert_eq!( |
| factor(&b" 24 "[..]), |
| Ok((&b""[..], 24)) |
| ); |
| } |
| |
| #[test] |
| fn term_test() { |
| assert_eq!( |
| term(&b" 12 *2 / 3"[..]), |
| Ok((&b""[..], 8)) |
| ); |
| assert_eq!( |
| term(&b" 2* 3 *2 *2 / 3"[..]), |
| Ok((&b""[..], 8)) |
| ); |
| assert_eq!( |
| term(&b" 48 / 3/2"[..]), |
| Ok((&b""[..], 8)) |
| ); |
| } |
| |
| #[test] |
| fn expr_test() { |
| assert_eq!( |
| expr(&b" 1 + 2 "[..]), |
| Ok((&b""[..], 3)) |
| ); |
| assert_eq!( |
| expr(&b" 12 + 6 - 4+ 3"[..]), |
| Ok((&b""[..], 17)) |
| ); |
| assert_eq!( |
| expr(&b" 1 + 2*3 + 4"[..]), |
| Ok((&b""[..], 11)) |
| ); |
| } |
| |
| #[test] |
| fn parens_test() { |
| assert_eq!( |
| expr(&b" ( 2 )"[..]), |
| Ok((&b""[..], 2)) |
| ); |
| assert_eq!( |
| expr(&b" 2* ( 3 + 4 ) "[..]), |
| Ok((&b""[..], 14)) |
| ); |
| assert_eq!( |
| expr(&b" 2*2 / ( 5 - 1) + 3"[..]), |
| Ok((&b""[..], 4)) |
| ); |
| } |