1use crate::{
18 dice_types::{
19 Dice, DiceType, Expression, Filter, FilteredDice, Operation, SelectedDice, Selector, Term,
20 },
21 LabeledExpression,
22};
23
24use nom::{
25 branch::alt,
26 bytes::complete::{tag, tag_no_case},
27 character::complete::{digit1, multispace0, satisfy},
28 combinator::{map, map_res, opt, recognize, success, verify},
29 error::context,
30 multi::{many0, many1},
31 sequence::{delimited, pair, preceded, terminated, tuple},
32 IResult,
33};
34
35pub fn parse_dice_digit(input: &str) -> IResult<&str, &str> {
36 alt((tag_no_case("d"), tag_no_case("w")))(input)
37}
38
39pub fn parse_dice_type(input: &str) -> IResult<&str, DiceType> {
40 alt((
41 map(
42 terminated(parse_u32, terminated(multispace0, tag_no_case("x"))),
43 DiceType::Multiply,
44 ),
45 map(parse_u32, DiceType::Number),
46 map(tag_no_case("f"), |_| DiceType::Fudge),
47 map(tag("%"), |_| DiceType::Number(100)),
48 ))(input)
49}
50
51pub fn parse_u32(input: &str) -> IResult<&str, u32> {
52 context(
53 "Failed to parse integer between 1 and 4294967295 inclusive",
54 verify(
55 map_res(digit1, |s: &str| s.parse::<u32>()),
56 |value: &u32| value > &0,
57 ),
58 )(input)
59}
60
61pub fn parse_i64(input: &str) -> IResult<&str, i64> {
62 map_res(
63 recognize(pair(alt((tag("+"), tag("-"), success(""))), digit1)),
64 |s: &str| s.parse::<i64>(),
65 )(input)
66}
67
68pub fn parse_dice(input: &str) -> IResult<&str, Dice> {
69 map(
70 tuple((
71 terminated(alt((parse_u32, success(1))), multispace0),
72 preceded(parse_dice_digit, preceded(multispace0, parse_dice_type)),
73 )),
74 |dice_params| Dice {
75 throws: dice_params.0,
76 dice: dice_params.1,
77 },
78 )(input)
79}
80
81pub fn parse_filter(input: &str) -> IResult<&str, Filter> {
82 alt((
83 map(tag(">="), |_| Filter::BiggerEq),
84 map(tag(">"), |_| Filter::Bigger),
85 map(tag("<="), |_| Filter::SmallerEq),
86 map(tag("<"), |_| Filter::Smaller),
87 map(tag("!="), |_| Filter::NotEq),
88 ))(input)
89}
90
91pub fn parse_filtered_dice(input: &str) -> IResult<&str, FilteredDice> {
92 alt((
93 map(
94 tuple((
95 parse_dice,
96 delimited(multispace0, parse_filter, multispace0),
97 parse_u32,
98 )),
99 |res| FilteredDice::Filtered(res.0, res.1, res.2),
100 ),
101 map(parse_dice, FilteredDice::Simple),
102 ))(input)
103}
104
105pub fn parse_selector(input: &str) -> IResult<&str, Selector> {
106 alt((
107 map(alt((tag_no_case("h"), tag_no_case("k"))), |_| {
108 Selector::Higher
109 }),
110 map(tag_no_case("l"), |_| Selector::Lower),
111 ))(input)
112}
113
114pub fn parse_selected_dice(input: &str) -> IResult<&str, SelectedDice> {
115 alt((
116 map(
117 tuple((
118 parse_filtered_dice,
119 delimited(multispace0, parse_selector, multispace0),
120 parse_u32,
121 )),
122 |select| SelectedDice::Selected(select.0, select.1, select.2),
123 ),
124 map(parse_filtered_dice, SelectedDice::Unchanged),
125 ))(input)
126}
127
128pub fn parse_term(input: &str) -> IResult<&str, Term> {
129 alt((
130 parse_term_calculation,
131 parse_term_roll,
132 parse_term_constant,
133 parse_term_subterm,
134 ))(input)
135}
136
137pub fn parse_term_constant(input: &str) -> IResult<&str, Term> {
138 map(parse_i64, Term::Constant)(input)
139}
140
141pub fn parse_term_subterm(input: &str) -> IResult<&str, Term> {
142 map(
143 delimited(
144 tag("("),
145 delimited(multispace0, parse_term, multispace0),
146 tag(")"),
147 ),
148 |subterm| Term::SubTerm(Box::new(subterm)),
149 )(input)
150}
151
152pub fn parse_term_roll(input: &str) -> IResult<&str, Term> {
153 map(parse_selected_dice, Term::DiceThrow)(input)
154}
155
156pub fn parse_operator(input: &str) -> IResult<&str, Operation> {
157 alt((
158 map(tag("+"), |_| Operation::Add),
159 map(tag("-"), |_| Operation::Sub),
160 map(tag("*"), |_| Operation::Mul),
161 map(tag("/"), |_| Operation::Div),
162 ))(input)
163}
164
165pub fn parse_term_calculation(input: &str) -> IResult<&str, Term> {
166 map(
167 tuple((
168 alt((parse_term_roll, parse_term_constant, parse_term_subterm)),
169 delimited(multispace0, parse_operator, multispace0),
170 parse_term,
171 )),
172 |calc| Term::Calculation(Box::new(calc.0), calc.1, Box::new(calc.2)),
173 )(input)
174}
175
176fn rearange_term(root: Term) -> Term {
177 if let Term::Calculation(left_top, op_top, right_top) = root {
178 if op_top == Operation::Mul || op_top == Operation::Div {
179 if let Term::Calculation(left_child, op_child, right_child) = *right_top {
180 Term::Calculation(
181 Box::new(Term::Calculation(left_top, op_top, left_child)),
182 op_child,
183 Box::new(rearange_term(*right_child)),
184 )
185 } else {
186 Term::Calculation(left_top, op_top, Box::new(rearange_term(*right_top)))
187 }
188 } else {
189 Term::Calculation(left_top, op_top, Box::new(rearange_term(*right_top)))
190 }
191 } else if let Term::SubTerm(term) = root {
192 Term::SubTerm(Box::new(rearange_term(*term)))
193 } else {
194 root
195 }
196}
197
198pub fn parse_rearanged_term(input: &str) -> IResult<&str, Term> {
199 map(parse_term, rearange_term)(input)
200}
201
202pub fn parse_expression(input: &str) -> IResult<&str, Expression> {
203 alt((
204 map(
205 pair(
206 parse_u32,
207 preceded(
208 multispace0,
209 delimited(
210 tag("{"),
211 delimited(multispace0, parse_rearanged_term, multispace0),
212 tag("}"),
213 ),
214 ),
215 ),
216 |list| Expression::List(list.0, list.1),
217 ),
218 map(parse_rearanged_term, Expression::Simple),
219 ))(input)
220}
221
222pub fn parse_labeled(input: &str) -> IResult<&str, LabeledExpression> {
223 map(
224 pair(
225 parse_expression,
226 opt(preceded(
227 pair(tag("#"), multispace0),
228 map(
229 many0(terminated(
230 recognize(many1(satisfy(|c| !(c.is_whitespace() || c == '\n')))),
231 multispace0,
232 )),
233 |labels: Vec<&str>| {
234 labels
235 .iter()
236 .map(|s| s.to_string())
237 .reduce(|l1, l2| format!("{} {}", l1, l2))
238 .unwrap_or_else(|| "".to_string())
239 },
240 ),
241 )),
242 ),
243 |r| match r {
244 (e, Some(l)) => LabeledExpression::Labeled(e, l),
245 (e, None) => LabeledExpression::Unlabeled(e),
246 },
247 )(input)
248}
249
250#[cfg(test)]
251mod tests {
252
253 use super::*;
254
255 #[test]
256 fn test_parse_dice_digit() {
257 assert_eq!(parse_dice_digit("d"), Ok(("", "d")));
258 assert_eq!(parse_dice_digit("D"), Ok(("", "D")));
259 assert_eq!(parse_dice_digit("w"), Ok(("", "w")));
260 assert_eq!(parse_dice_digit("W"), Ok(("", "W")));
261 assert_eq!(parse_dice_digit("dd"), Ok(("d", "d")));
262 assert_eq!(parse_dice_digit("d%"), Ok(("%", "d")));
263 assert!(parse_dice_digit("l").is_err());
264 assert!(parse_dice_digit("%").is_err());
265 assert!(parse_dice_digit("").is_err());
266 }
267
268 #[test]
269 fn test_parse_u32() {
270 assert_eq!(parse_u32("1"), Ok(("", 1)));
271 assert_eq!(parse_u32("6969"), Ok(("", 6969)));
272 assert_eq!(parse_u32("4294967295"), Ok(("", 4294967295)));
273 assert!(parse_u32("4294967296").is_err());
274 assert!(parse_u32("-1").is_err());
275 assert!(parse_u32("").is_err());
276 assert!(parse_u32("0").is_err());
277 }
278
279 #[test]
280 fn test_parse_i64() {
281 assert_eq!(parse_i64("0"), Ok(("", 0)));
282 assert_eq!(parse_i64("1"), Ok(("", 1)));
283 assert_eq!(parse_i64("+1"), Ok(("", 1)));
284 assert_eq!(parse_i64("-1"), Ok(("", -1)));
285 assert_eq!(parse_i64("6969"), Ok(("", 6969)));
286 assert_eq!(parse_i64("+6969"), Ok(("", 6969)));
287 assert_eq!(parse_i64("-1337"), Ok(("", -1337)));
288 assert_eq!(
289 parse_i64("-9223372036854775808"),
290 Ok(("", -9223372036854775808))
291 );
292 assert_eq!(
293 parse_i64("9223372036854775807"),
294 Ok(("", 9223372036854775807))
295 );
296 assert_eq!(
297 parse_i64("+9223372036854775807"),
298 Ok(("", 9223372036854775807))
299 );
300 assert_eq!(parse_i64("0k"), Ok(("k", 0)));
301 assert!(parse_i64("k").is_err());
302 assert!(parse_i64("").is_err());
303 }
304
305 #[test]
306 fn test_parse_dice_type() {
307 assert_eq!(parse_dice_type("1"), Ok(("", DiceType::Number(1))));
308 assert_eq!(parse_dice_type("1337"), Ok(("", DiceType::Number(1337))));
309 assert_eq!(parse_dice_type("%"), Ok(("", DiceType::Number(100))));
310 assert_eq!(parse_dice_type("f"), Ok(("", DiceType::Fudge)));
311 assert_eq!(parse_dice_type("F"), Ok(("", DiceType::Fudge)));
312 assert_eq!(parse_dice_type("1x"), Ok(("", DiceType::Multiply(1))));
313 assert_eq!(parse_dice_type("6969X"), Ok(("", DiceType::Multiply(6969))));
314 assert_eq!(
315 parse_dice_type("1337 x"),
316 Ok(("", DiceType::Multiply(1337)))
317 );
318 assert!(parse_dice_type("x").is_err());
319 assert!(parse_dice_type("").is_err());
320 }
321
322 #[test]
323 fn test_parse_dice() {
324 assert_eq!(
325 parse_dice("d1"),
326 Ok((
327 "",
328 Dice {
329 throws: 1,
330 dice: DiceType::Number(1)
331 }
332 ))
333 );
334 assert_eq!(
335 parse_dice("1D %"),
336 Ok((
337 "",
338 Dice {
339 throws: 1,
340 dice: DiceType::Number(100)
341 }
342 ))
343 );
344 assert_eq!(
345 parse_dice("20w \t3\tX"),
346 Ok((
347 "",
348 Dice {
349 throws: 20,
350 dice: DiceType::Multiply(3)
351 }
352 ))
353 );
354 }
355
356 #[test]
357 fn test_parse_filter() {
358 assert_eq!(parse_filter("<"), Ok(("", Filter::Smaller)));
359 assert_eq!(parse_filter("<="), Ok(("", Filter::SmallerEq)));
360 assert_eq!(parse_filter(">"), Ok(("", Filter::Bigger)));
361 assert_eq!(parse_filter(">="), Ok(("", Filter::BiggerEq)));
362 assert_eq!(parse_filter("!="), Ok(("", Filter::NotEq)));
363 assert_eq!(parse_filter("!=3"), Ok(("3", Filter::NotEq)));
364 assert!(parse_filter("==").is_err());
365 assert!(parse_filter("").is_err());
366 }
367
368 #[test]
369 fn test_parse_filtered_dice() {
370 assert_eq!(
371 parse_filtered_dice("d4"),
372 Ok((
373 "",
374 FilteredDice::Simple(Dice {
375 throws: 1,
376 dice: DiceType::Number(4)
377 })
378 ))
379 );
380 assert_eq!(
381 parse_filtered_dice("2d2!=2"),
382 Ok((
383 "",
384 FilteredDice::Filtered(
385 Dice {
386 throws: 2,
387 dice: DiceType::Number(2)
388 },
389 Filter::NotEq,
390 2
391 )
392 ))
393 );
394 assert_eq!(
395 parse_filtered_dice("10 w 10 \t x \t < \t 75"),
396 Ok((
397 "",
398 FilteredDice::Filtered(
399 Dice {
400 throws: 10,
401 dice: DiceType::Multiply(10)
402 },
403 Filter::Smaller,
404 75
405 )
406 ))
407 );
408 assert_eq!(
409 parse_filtered_dice("69d69>"),
410 Ok((
411 ">",
412 FilteredDice::Simple(Dice {
413 throws: 69,
414 dice: DiceType::Number(69)
415 })
416 ))
417 );
418 assert!(parse_filtered_dice("").is_err());
419 }
420
421 #[test]
422 fn test_parse_selector() {
423 assert_eq!(parse_selector("h"), Ok(("", Selector::Higher)));
424 assert_eq!(parse_selector("H"), Ok(("", Selector::Higher)));
425 assert_eq!(parse_selector("k"), Ok(("", Selector::Higher)));
426 assert_eq!(parse_selector("K"), Ok(("", Selector::Higher)));
427 assert_eq!(parse_selector("l"), Ok(("", Selector::Lower)));
428 assert_eq!(parse_selector("L"), Ok(("", Selector::Lower)));
429 assert_eq!(parse_selector("hl"), Ok(("l", Selector::Higher)));
430 assert!(parse_selector("").is_err());
431 }
432
433 #[test]
434 fn test_parse_selected_dice() {
435 assert_eq!(
436 parse_selected_dice("d3"),
437 Ok((
438 "",
439 SelectedDice::Unchanged(FilteredDice::Simple(Dice {
440 throws: 1,
441 dice: DiceType::Number(3)
442 }))
443 ))
444 );
445 assert_eq!(
446 parse_selected_dice("4W10X>50k2"),
447 Ok((
448 "",
449 SelectedDice::Selected(
450 FilteredDice::Filtered(
451 Dice {
452 throws: 4,
453 dice: DiceType::Multiply(10)
454 },
455 Filter::Bigger,
456 50
457 ),
458 Selector::Higher,
459 2
460 )
461 ))
462 );
463 assert_eq!(
464 parse_selected_dice("4\t W \t 10 \tX\t >\t 50\t k \t 2"),
465 Ok((
466 "",
467 SelectedDice::Selected(
468 FilteredDice::Filtered(
469 Dice {
470 throws: 4,
471 dice: DiceType::Multiply(10)
472 },
473 Filter::Bigger,
474 50
475 ),
476 Selector::Higher,
477 2
478 )
479 ))
480 );
481 assert!(parse_selected_dice("").is_err());
482 }
483
484 #[test]
485 fn test_parse_term() {
486 assert!(parse_term("d 3 + d f + d % + 1337 d 69 x * 4 d 100 / ( 3 w 10 - 2 )").is_ok());
487 assert_eq!(
488 parse_term("d 3 + 66DF * 4d3x - 1"),
489 Ok((
490 "",
491 Term::Calculation(
492 Box::new(Term::DiceThrow(SelectedDice::Unchanged(
493 FilteredDice::Simple(Dice {
494 throws: 1,
495 dice: DiceType::Number(3)
496 })
497 ))),
498 Operation::Add,
499 Box::new(Term::Calculation(
500 Box::new(Term::DiceThrow(SelectedDice::Unchanged(
501 FilteredDice::Simple(Dice {
502 throws: 66,
503 dice: DiceType::Fudge
504 })
505 ))),
506 Operation::Mul,
507 Box::new(Term::Calculation(
508 Box::new(Term::DiceThrow(SelectedDice::Unchanged(
509 FilteredDice::Simple(Dice {
510 throws: 4,
511 dice: DiceType::Multiply(3)
512 })
513 ))),
514 Operation::Sub,
515 Box::new(Term::Constant(1))
516 ))
517 ))
518 )
519 ))
520 );
521 assert!(parse_term("").is_err())
522 }
523
524 fn test_parse_expr() {}
525}