1use nom::{ 2 branch::alt, 3 bytes::complete::tag, 4 character::complete::char, 5 character::complete::{digit1 as digit, space0 as space}, 6 combinator::map_res, 7 multi::fold_many0, 8 sequence::{delimited, pair}, 9 IResult, 10}; 11 12// Parser definition 13 14use std::str::FromStr; 15 16// We parse any expr surrounded by parens, ignoring all whitespaces around those 17fn parens(i: &str) -> IResult<&str, i64> { 18 delimited(space, delimited(tag("("), expr, tag(")")), space)(i) 19} 20 21// We transform an integer string into a i64, ignoring surrounding whitespaces 22// We look for a digit suite, and try to convert it. 23// If either str::from_utf8 or FromStr::from_str fail, 24// we fallback to the parens parser defined above 25fn factor(i: &str) -> IResult<&str, i64> { 26 alt(( 27 map_res(delimited(space, digit, space), FromStr::from_str), 28 parens, 29 ))(i) 30} 31 32// We read an initial factor and for each time we find 33// a * or / operator followed by another factor, we do 34// the math by folding everything 35fn term(i: &str) -> IResult<&str, i64> { 36 let (i, init) = factor(i)?; 37 38 fold_many0( 39 pair(alt((char('*'), char('/'))), factor), 40 move || init, 41 |acc, (op, val): (char, i64)| { 42 if op == '*' { 43 acc * val 44 } else { 45 acc / val 46 } 47 }, 48 )(i) 49} 50 51fn expr(i: &str) -> IResult<&str, i64> { 52 let (i, init) = term(i)?; 53 54 fold_many0( 55 pair(alt((char('+'), char('-'))), term), 56 move || init, 57 |acc, (op, val): (char, i64)| { 58 if op == '+' { 59 acc + val 60 } else { 61 acc - val 62 } 63 }, 64 )(i) 65} 66 67#[test] 68fn factor_test() { 69 assert_eq!(factor("3"), Ok(("", 3))); 70 assert_eq!(factor(" 12"), Ok(("", 12))); 71 assert_eq!(factor("537 "), Ok(("", 537))); 72 assert_eq!(factor(" 24 "), Ok(("", 24))); 73} 74 75#[test] 76fn term_test() { 77 assert_eq!(term(" 12 *2 / 3"), Ok(("", 8))); 78 assert_eq!(term(" 2* 3 *2 *2 / 3"), Ok(("", 8))); 79 assert_eq!(term(" 48 / 3/2"), Ok(("", 8))); 80} 81 82#[test] 83fn expr_test() { 84 assert_eq!(expr(" 1 + 2 "), Ok(("", 3))); 85 assert_eq!(expr(" 12 + 6 - 4+ 3"), Ok(("", 17))); 86 assert_eq!(expr(" 1 + 2*3 + 4"), Ok(("", 11))); 87} 88 89#[test] 90fn parens_test() { 91 assert_eq!(expr(" ( 2 )"), Ok(("", 2))); 92 assert_eq!(expr(" 2* ( 3 + 4 ) "), Ok(("", 14))); 93 assert_eq!(expr(" 2*2 / ( 5 - 1) + 3"), Ok(("", 4))); 94} 95