• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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