1use crate::Error;
2
3use self::Resolver::*;
4
5#[derive(Clone, Debug)]
6pub enum Resolver {
7 Expr(Ast),
10 Function(fn(u64) -> usize),
12}
13
14fn index_of(src: &str, pat: &str) -> Option<usize> {
16 src.chars()
17 .fold(
18 (None, 0, 0, 0),
19 |(match_index, i, n_matches, paren_level), ch| {
20 if let Some(x) = match_index {
21 (Some(x), i, n_matches, paren_level)
22 } else {
23 let new_par_lvl = match ch {
24 '(' => paren_level + 1,
25 ')' => paren_level - 1,
26 _ => paren_level,
27 };
28
29 if Some(ch) == pat.chars().nth(n_matches) {
30 let length = n_matches + 1;
31 if length == pat.len() && new_par_lvl == 0 {
32 (Some(i - n_matches), i + 1, length, new_par_lvl)
33 } else {
34 (match_index, i + 1, length, new_par_lvl)
35 }
36 } else {
37 (match_index, i + 1, 0, new_par_lvl)
38 }
39 }
40 },
41 )
42 .0
43}
44
45use self::Ast::*;
46#[derive(Clone, Debug, PartialEq)]
47pub enum Ast {
48 Ternary(Box<Ast>, Box<Ast>, Box<Ast>),
53 N,
55 Integer(u64),
57 Op(Operator, Box<Ast>, Box<Ast>),
59 Not(Box<Ast>),
61}
62
63#[derive(Clone, Debug, PartialEq)]
64pub enum Operator {
65 Equal,
66 NotEqual,
67 GreaterOrEqual,
68 SmallerOrEqual,
69 Greater,
70 Smaller,
71 And,
72 Or,
73 Modulo,
74}
75
76impl Ast {
77 fn resolve(&self, n: u64) -> usize {
78 match *self {
79 Ternary(ref cond, ref ok, ref nok) => {
80 if cond.resolve(n) == 0 {
81 nok.resolve(n)
82 } else {
83 ok.resolve(n)
84 }
85 }
86 N => n as usize,
87 Integer(x) => x as usize,
88 Op(ref op, ref lhs, ref rhs) => match *op {
89 Operator::Equal => (lhs.resolve(n) == rhs.resolve(n)) as usize,
90 Operator::NotEqual => (lhs.resolve(n) != rhs.resolve(n)) as usize,
91 Operator::GreaterOrEqual => (lhs.resolve(n) >= rhs.resolve(n)) as usize,
92 Operator::SmallerOrEqual => (lhs.resolve(n) <= rhs.resolve(n)) as usize,
93 Operator::Greater => (lhs.resolve(n) > rhs.resolve(n)) as usize,
94 Operator::Smaller => (lhs.resolve(n) < rhs.resolve(n)) as usize,
95 Operator::And => (lhs.resolve(n) != 0 && rhs.resolve(n) != 0) as usize,
96 Operator::Or => (lhs.resolve(n) != 0 || rhs.resolve(n) != 0) as usize,
97 Operator::Modulo => lhs.resolve(n) % rhs.resolve(n),
98 },
99 Not(ref val) => match val.resolve(n) {
100 0 => 1,
101 _ => 0,
102 },
103 }
104 }
105
106 pub fn parse(src: &str) -> Result<Ast, Error> {
107 Self::parse_parens(src.trim())
108 }
109
110 fn parse_parens(src: &str) -> Result<Ast, Error> {
111 if src.starts_with('(') {
112 let end = src[1..src.len() - 1]
113 .chars()
114 .fold((1, 2), |(level, index), ch| match (level, ch) {
115 (0, '(') => (level + 1, index + 1),
116 (0, _) => (level, index),
117 (_, '(') => (level + 1, index + 1),
118 (_, ')') => (level - 1, index + 1),
119 (_, _) => (level, index + 1),
120 })
121 .1;
122 if end == src.len() {
123 Ast::parse(src[1..src.len() - 1].trim())
124 } else {
125 Ast::parse_and(src.trim())
126 }
127 } else {
128 Ast::parse_and(src.trim())
129 }
130 }
131
132 fn parse_and(src: &str) -> Result<Ast, Error> {
133 if let Some(i) = index_of(src, "&&") {
134 Ok(Ast::Op(
135 Operator::And,
136 Box::new(Ast::parse(&src[0..i])?),
137 Box::new(Ast::parse(&src[i + 2..])?),
138 ))
139 } else {
140 Self::parse_or(src)
141 }
142 }
143
144 fn parse_or(src: &str) -> Result<Ast, Error> {
145 if let Some(i) = index_of(src, "||") {
146 Ok(Ast::Op(
147 Operator::Or,
148 Box::new(Ast::parse(&src[0..i])?),
149 Box::new(Ast::parse(&src[i + 2..])?),
150 ))
151 } else {
152 Self::parse_ternary(src)
153 }
154 }
155
156 fn parse_ternary(src: &str) -> Result<Ast, Error> {
157 if let Some(i) = index_of(src, "?") {
158 if let Some(l) = index_of(src, ":") {
159 Ok(Ast::Ternary(
160 Box::new(Ast::parse(&src[0..i])?),
161 Box::new(Ast::parse(&src[i + 1..l])?),
162 Box::new(Ast::parse(&src[l + 1..])?),
163 ))
164 } else {
165 Err(Error::PluralParsing)
166 }
167 } else {
168 Self::parse_ge(src)
169 }
170 }
171
172 fn parse_ge(src: &str) -> Result<Ast, Error> {
173 if let Some(i) = index_of(src, ">=") {
174 Ok(Ast::Op(
175 Operator::GreaterOrEqual,
176 Box::new(Ast::parse(&src[0..i])?),
177 Box::new(Ast::parse(&src[i + 2..])?),
178 ))
179 } else {
180 Self::parse_gt(src)
181 }
182 }
183
184 fn parse_gt(src: &str) -> Result<Ast, Error> {
185 if let Some(i) = index_of(src, ">") {
186 Ok(Ast::Op(
187 Operator::Greater,
188 Box::new(Ast::parse(&src[0..i])?),
189 Box::new(Ast::parse(&src[i + 1..])?),
190 ))
191 } else {
192 Self::parse_le(src)
193 }
194 }
195
196 fn parse_le(src: &str) -> Result<Ast, Error> {
197 if let Some(i) = index_of(src, "<=") {
198 Ok(Ast::Op(
199 Operator::SmallerOrEqual,
200 Box::new(Ast::parse(&src[0..i])?),
201 Box::new(Ast::parse(&src[i + 2..])?),
202 ))
203 } else {
204 Self::parse_lt(src)
205 }
206 }
207
208 fn parse_lt(src: &str) -> Result<Ast, Error> {
209 if let Some(i) = index_of(src, "<") {
210 Ok(Ast::Op(
211 Operator::Smaller,
212 Box::new(Ast::parse(&src[0..i])?),
213 Box::new(Ast::parse(&src[i + 1..])?),
214 ))
215 } else {
216 Self::parse_eq(src)
217 }
218 }
219
220 fn parse_eq(src: &str) -> Result<Ast, Error> {
221 if let Some(i) = index_of(src, "==") {
222 Ok(Ast::Op(
223 Operator::Equal,
224 Box::new(Ast::parse(&src[0..i])?),
225 Box::new(Ast::parse(&src[i + 2..])?),
226 ))
227 } else {
228 Self::parse_neq(src)
229 }
230 }
231
232 fn parse_neq(src: &str) -> Result<Ast, Error> {
233 if let Some(i) = index_of(src, "!=") {
234 Ok(Ast::Op(
235 Operator::NotEqual,
236 Box::new(Ast::parse(&src[0..i])?),
237 Box::new(Ast::parse(&src[i + 2..])?),
238 ))
239 } else {
240 Self::parse_mod(src)
241 }
242 }
243 fn parse_mod(src: &str) -> Result<Ast, Error> {
244 if let Some(i) = index_of(src, "%") {
245 Ok(Ast::Op(
246 Operator::Modulo,
247 Box::new(Ast::parse(&src[0..i])?),
248 Box::new(Ast::parse(&src[i + 1..])?),
249 ))
250 } else {
251 Self::parse_not(src.trim())
252 }
253 }
254
255 fn parse_not(src: &str) -> Result<Ast, Error> {
256 if index_of(src, "!") == Some(0) {
257 Ok(Ast::Not(Box::new(Ast::parse(&src[1..])?)))
258 } else {
259 Self::parse_int(src.trim())
260 }
261 }
262
263 fn parse_int(src: &str) -> Result<Ast, Error> {
264 if let Ok(x) = u64::from_str_radix(src, 10) {
265 Ok(Ast::Integer(x))
266 } else {
267 Self::parse_n(src.trim())
268 }
269 }
270
271 fn parse_n(src: &str) -> Result<Ast, Error> {
272 if src == "n" {
273 Ok(Ast::N)
274 } else {
275 Err(Error::PluralParsing)
276 }
277 }
278}
279
280impl Resolver {
281 pub fn resolve(&self, n: u64) -> usize {
284 match *self {
285 Expr(ref ast) => ast.resolve(n),
286 Function(ref f) => f(n),
287 }
288 }
289}
290
291#[cfg(test)]
292mod tests {
293 use super::*;
294
295 #[test]
296 fn test_expr_resolver() {
297 assert_eq!(Expr(N).resolve(42), 42);
298 }
299
300 #[test]
301 fn test_parser() {
302 assert_eq!(
303 Ast::parse("n == 42 ? n : 6 && n < 7").expect("Invalid plural"),
304 Ast::Op(
305 Operator::And,
306 Box::new(Ast::Ternary(
307 Box::new(Ast::Op(
308 Operator::Equal,
309 Box::new(Ast::N),
310 Box::new(Ast::Integer(42))
311 )),
312 Box::new(Ast::N),
313 Box::new(Ast::Integer(6))
314 )),
315 Box::new(Ast::Op(
316 Operator::Smaller,
317 Box::new(Ast::N),
318 Box::new(Ast::Integer(7))
319 ))
320 )
321 );
322
323 assert_eq!(Ast::parse("(n)").expect("Invalid plural"), Ast::N);
324
325 assert_eq!(
326 Ast::parse("(n == 1 || n == 2) ? 0 : 1").expect("Invalid plural"),
327 Ast::Ternary(
328 Box::new(Ast::Op(
329 Operator::Or,
330 Box::new(Ast::Op(
331 Operator::Equal,
332 Box::new(Ast::N),
333 Box::new(Ast::Integer(1))
334 )),
335 Box::new(Ast::Op(
336 Operator::Equal,
337 Box::new(Ast::N),
338 Box::new(Ast::Integer(2))
339 ))
340 )),
341 Box::new(Ast::Integer(0)),
342 Box::new(Ast::Integer(1))
343 )
344 );
345
346 let ru_plural = "((n%10==1 && n%100!=11) ? 0 : ((n%10 >= 2 && n%10 <=4 && (n%100 < 12 || n%100 > 14)) ? 1 : ((n%10 == 0 || (n%10 >= 5 && n%10 <=9)) || (n%100 >= 11 && n%100 <= 14)) ? 2 : 3))";
347 assert!(Ast::parse(ru_plural).is_ok());
348 }
349}