1 #[macro_use]
2 extern crate nom;
3
4 use nom::{
5 branch::alt,
6 sequence::{delimited, pair, preceded},
7 character::complete::{digit1 as digit, space0 as space},
8 bytes::complete::tag
9 };
10
11 // Parser definition
12
13 use std::str;
14 use std::str::FromStr;
15
16 use self::Operator::*;
17
18 enum Operator {
19 Slash,
20 Star,
21 }
22
23 impl Operator {
to_str(&self) -> &'static str24 fn to_str(&self) -> &'static str {
25 match *self {
26 Slash => "/",
27 Star => "*",
28 }
29 }
30 }
31
32 // Parse the specified `Operator`.
33 named_args!(operator(op: Operator) <&[u8], &[u8]>,
34 call!(tag(op.to_str()))
35 );
36
37 // We parse any expr surrounded by the tags `open_tag` and `close_tag`, ignoring all whitespaces around those
38 named_args!(brackets<'a>(open_tag: &str, close_tag: &str) <&'a[u8], i64>,
39 call!(delimited(
40 space,
41 delimited(tag(open_tag), preceded(space, expr), preceded(space, tag(close_tag))),
42 space
43 ))
44 );
45
byte_slice_to_str<'a>(s: &'a[u8]) -> Result<&'a str, str::Utf8Error>46 fn byte_slice_to_str<'a>(s: &'a[u8]) -> Result<&'a str, str::Utf8Error> {
47 str::from_utf8(s)
48 }
49
50 // We transform an integer string into a i64, ignoring surrounding whitespaces
51 // We look for a digit suite, and try to convert it.
52 // If either str::from_utf8 or FromStr::from_str fail,
53 // we fallback to the brackets parser defined above
54 named!(factor<&[u8], i64>, alt!(
55 map_res!(
56 map_res!(
57 call!(delimited(space, digit, space)),
58 byte_slice_to_str
59 ),
60 FromStr::from_str
61 )
62 | call!(brackets, "(", ")")
63 )
64 );
65
66 // We read an initial factor and for each time we find
67 // a * or / operator followed by another factor, we do
68 // the math by folding everything
69 named!(term <&[u8], i64>, do_parse!(
70 init: factor >>
71 res: fold_many0!(
72 pair!(alt!(call!(operator, Star) | call!(operator, Slash)), factor),
73 init,
74 |acc, (op, val): (&[u8], i64)| {
75 if (op[0] as char) == '*' { acc * val } else { acc / val }
76 }
77 ) >>
78 (res)
79 )
80 );
81
82 named!(expr <&[u8], i64>, do_parse!(
83 init: term >>
84 res: fold_many0!(
85 call!(pair(alt((tag("+"), tag("-"))), term)),
86 init,
87 |acc, (op, val): (&[u8], i64)| {
88 if (op[0] as char) == '+' { acc + val } else { acc - val }
89 }
90 ) >>
91 (res)
92 )
93 );
94
95 #[test]
factor_test()96 fn factor_test() {
97 assert_eq!(
98 factor(&b"3"[..]),
99 Ok((&b""[..], 3))
100 );
101 assert_eq!(
102 factor(&b" 12"[..]),
103 Ok((&b""[..], 12))
104 );
105 assert_eq!(
106 factor(&b"537 "[..]),
107 Ok((&b""[..], 537))
108 );
109 assert_eq!(
110 factor(&b" 24 "[..]),
111 Ok((&b""[..], 24))
112 );
113 }
114
115 #[test]
term_test()116 fn term_test() {
117 assert_eq!(
118 term(&b" 12 *2 / 3"[..]),
119 Ok((&b""[..], 8))
120 );
121 assert_eq!(
122 term(&b" 2* 3 *2 *2 / 3"[..]),
123 Ok((&b""[..], 8))
124 );
125 assert_eq!(
126 term(&b" 48 / 3/2"[..]),
127 Ok((&b""[..], 8))
128 );
129 }
130
131 #[test]
expr_test()132 fn expr_test() {
133 assert_eq!(
134 expr(&b" 1 + 2 "[..]),
135 Ok((&b""[..], 3))
136 );
137 assert_eq!(
138 expr(&b" 12 + 6 - 4+ 3"[..]),
139 Ok((&b""[..], 17))
140 );
141 assert_eq!(
142 expr(&b" 1 + 2*3 + 4"[..]),
143 Ok((&b""[..], 11))
144 );
145 }
146
147 #[test]
parens_test()148 fn parens_test() {
149 assert_eq!(
150 expr(&b" ( 2 )"[..]),
151 Ok((&b""[..], 2))
152 );
153 assert_eq!(
154 expr(&b" 2* ( 3 + 4 ) "[..]),
155 Ok((&b""[..], 14))
156 );
157 assert_eq!(
158 expr(&b" 2*2 / ( 5 - 1) + 3"[..]),
159 Ok((&b""[..], 4))
160 );
161 }
162