axfive_matrix_dicebot/dice/
parser.rs1use nom::{
2 alt, bytes::complete::tag, character::complete::digit1, complete, many0, named,
3 sequence::tuple, tag, IResult,
4};
5
6use crate::dice::{Dice, Element, ElementExpression, SignedElement};
7use crate::parser::eat_whitespace;
8
9#[derive(Debug, PartialEq, Eq, Clone, Copy)]
10enum Sign {
11 Plus,
12 Minus,
13}
14
15fn parse_dice(input: &str) -> IResult<&str, Dice> {
17 let (input, (count, _, sides)) = tuple((digit1, tag("d"), digit1))(input)?;
18 Ok((
19 input,
20 Dice::new(count.parse().unwrap(), sides.parse().unwrap()),
21 ))
22}
23
24fn parse_bonus(input: &str) -> IResult<&str, u32> {
26 let (input, bonus) = digit1(input)?;
27 Ok((input, bonus.parse().unwrap()))
28}
29
30fn parse_sign(input: &str) -> IResult<&str, Sign> {
32 let (input, _) = eat_whitespace(input)?;
33 named!(sign(&str) -> Sign, alt!(
34 complete!(tag!("+")) => { |_| Sign::Plus } |
35 complete!(tag!("-")) => { |_| Sign::Minus }
36 ));
37
38 let (input, sign) = sign(input)?;
39 Ok((input, sign))
40}
41
42fn parse_element(input: &str) -> IResult<&str, Element> {
44 let (input, _) = eat_whitespace(input)?;
45 named!(element(&str) -> Element, alt!(
46 parse_dice => { |d| Element::Dice(d) } |
47 parse_bonus => { |b| Element::Bonus(b) }
48 ));
49
50 let (input, element) = element(input)?;
51 Ok((input, element))
52}
53
54fn parse_signed_element(input: &str) -> IResult<&str, SignedElement> {
56 let (input, _) = eat_whitespace(input)?;
57 let (input, sign) = parse_sign(input)?;
58 let (input, _) = eat_whitespace(input)?;
59
60 let (input, element) = parse_element(input)?;
61 let element = match sign {
62 Sign::Plus => SignedElement::Positive(element),
63 Sign::Minus => SignedElement::Negative(element),
64 };
65 Ok((input, element))
66}
67
68pub fn parse_element_expression(input: &str) -> IResult<&str, ElementExpression> {
70 named!(first_element(&str) -> SignedElement, alt!(
71 parse_signed_element => { |e| e } |
72 parse_element => { |e| SignedElement::Positive(e) }
73 ));
74 let (input, first) = first_element(input)?;
75 let (input, rest) = if input.trim().is_empty() {
76 (input, vec![first])
77 } else {
78 named!(rest_elements(&str) -> Vec<SignedElement>, many0!(parse_signed_element));
79 let (input, mut rest) = rest_elements(input)?;
80 rest.insert(0, first);
81 (input, rest)
82 };
83
84 Ok((input, ElementExpression(rest)))
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90 #[test]
91 fn dice_test() {
92 assert_eq!(parse_dice("2d4"), Ok(("", Dice::new(2, 4))));
93 assert_eq!(parse_dice("20d40"), Ok(("", Dice::new(20, 40))));
94 assert_eq!(parse_dice("8d7"), Ok(("", Dice::new(8, 7))));
95 }
96
97 #[test]
98 fn element_test() {
99 assert_eq!(
100 parse_element(" \t\n\r\n 8d7 \n"),
101 Ok((" \n", Element::Dice(Dice::new(8, 7))))
102 );
103 assert_eq!(
104 parse_element(" \t\n\r\n 8 \n"),
105 Ok((" \n", Element::Bonus(8)))
106 );
107 }
108
109 #[test]
110 fn signed_element_test() {
111 assert_eq!(
112 parse_signed_element("+ 7"),
113 Ok(("", SignedElement::Positive(Element::Bonus(7))))
114 );
115 assert_eq!(
116 parse_signed_element(" \t\n\r\n- 8 \n"),
117 Ok((" \n", SignedElement::Negative(Element::Bonus(8))))
118 );
119 assert_eq!(
120 parse_signed_element(" \t\n\r\n- 8d4 \n"),
121 Ok((
122 " \n",
123 SignedElement::Negative(Element::Dice(Dice::new(8, 4)))
124 ))
125 );
126 assert_eq!(
127 parse_signed_element(" \t\n\r\n+ 8d4 \n"),
128 Ok((
129 " \n",
130 SignedElement::Positive(Element::Dice(Dice::new(8, 4)))
131 ))
132 );
133 }
134
135 #[test]
136 fn element_expression_test() {
137 assert_eq!(
138 parse_element_expression("8d4"),
139 Ok((
140 "",
141 ElementExpression(vec![SignedElement::Positive(Element::Dice(Dice::new(
142 8, 4
143 )))])
144 ))
145 );
146 assert_eq!(
147 parse_element_expression(" - 8d4 \n "),
148 Ok((
149 " \n ",
150 ElementExpression(vec![SignedElement::Negative(Element::Dice(Dice::new(
151 8, 4
152 )))])
153 ))
154 );
155 assert_eq!(
156 parse_element_expression("\t3d4 + 7 - 5 - 6d12 + 1d1 + 53 1d5 "),
157 Ok((
158 " 1d5 ",
159 ElementExpression(vec![
160 SignedElement::Positive(Element::Dice(Dice::new(3, 4))),
161 SignedElement::Positive(Element::Bonus(7)),
162 SignedElement::Negative(Element::Bonus(5)),
163 SignedElement::Negative(Element::Dice(Dice::new(6, 12))),
164 SignedElement::Positive(Element::Dice(Dice::new(1, 1))),
165 SignedElement::Positive(Element::Bonus(53)),
166 ])
167 ))
168 );
169 }
170}