1use nom::IResult;
53use nom::Parser as _;
54use nom::branch::alt;
55use nom::bytes::complete::tag;
56use nom::character::complete::char;
57use nom::character::complete::digit1;
58use nom::combinator::map;
59use nom::combinator::map_res;
60use nom::combinator::opt;
61use nom::multi::separated_list1;
62use nom::sequence::delimited;
63use nom::sequence::preceded;
64
65use crate::selection::Selection;
66use crate::selection::dsl;
67use crate::shape;
68
69fn number(input: &str) -> IResult<&str, usize> {
70 map_res(digit1, str::parse).parse(input)
71}
72
73fn index(input: &str) -> IResult<&str, Selection> {
74 map(number, |n| dsl::range(n, dsl::true_())).parse(input)
75}
76
77fn range(input: &str) -> IResult<&str, Selection> {
78 let (input, (start, end, step)) = (
79 opt(number),
80 preceded(char(':'), opt(number)),
81 opt(preceded(char(':'), number)),
82 )
83 .parse(input)?;
84
85 Ok((
86 input,
87 dsl::range(
88 shape::Range(start.unwrap_or(0), end, step.unwrap_or(1)),
89 dsl::true_(),
90 ),
91 ))
92}
93
94fn wildcard(input: &str) -> IResult<&str, Selection> {
95 map(tag("*"), |_| dsl::all(dsl::true_())).parse(input)
96}
97
98fn any(input: &str) -> IResult<&str, Selection> {
99 map(tag("?"), |_| dsl::any(dsl::true_())).parse(input)
100}
101
102fn group(input: &str) -> IResult<&str, Selection> {
103 alt((
104 delimited(char('('), expression, char(')')),
105 range,
106 index,
107 wildcard,
108 any,
109 ))
110 .parse(input)
111}
112
113fn chain(input: &str) -> IResult<&str, Selection> {
114 map(separated_list1(char(','), group), |dims| {
115 dims.into_iter()
116 .rev()
117 .fold(dsl::true_(), |acc, dim| nest(dim, acc))
118 })
119 .parse(input)
120}
121
122fn nest(dim: Selection, tail: Selection) -> Selection {
133 match dim {
134 Selection::All(inner) => dsl::all(nest(*inner, tail)),
135 Selection::Any(inner) => dsl::any(nest(*inner, tail)),
136 Selection::Range(r, inner) => dsl::range(r, nest(*inner, tail)),
137 Selection::Union(a, b) => dsl::union(nest(*a, tail.clone()), nest(*b, tail)),
138 Selection::Intersection(a, b) => dsl::intersection(nest(*a, tail.clone()), nest(*b, tail)),
139 Selection::True => tail,
140 Selection::False => dsl::false_(),
141 other => panic!("unexpected selection variant in chain: {:?}", other),
142 }
143}
144
145fn intersection(input: &str) -> IResult<&str, Selection> {
146 map(separated_list1(char('&'), chain), |items| {
147 items.into_iter().reduce(dsl::intersection).unwrap()
148 })
149 .parse(input)
150}
151
152pub fn expression(input: &str) -> IResult<&str, Selection> {
153 map(separated_list1(char('|'), intersection), |items| {
154 items.into_iter().reduce(dsl::union).unwrap()
155 })
156 .parse(input)
157}
158
159pub fn parse(input: &str) -> anyhow::Result<Selection> {
173 use nom::combinator::all_consuming;
174
175 let input: String = input.chars().filter(|c| !c.is_whitespace()).collect();
176 let (_, selection) = all_consuming(expression)
177 .parse(&input)
178 .map_err(|err| anyhow::anyhow!("Failed to parse selection: {err:?} (input: {input:?})"))?;
179 Ok(selection)
180}
181
182#[cfg(test)]
183mod tests {
184 use crate::selection::Selection;
185 use crate::shape;
186
187 fn parse(input: &str) -> Selection {
189 super::parse(input).unwrap()
190 }
191
192 #[macro_export]
193 macro_rules! assert_parses_to {
194 ($input:expr_2021, $expected:expr_2021) => {{
195 let actual = $crate::selection::parse::tests::parse($input);
196 $crate::assert_structurally_eq!($expected, actual);
197 }};
198 }
199
200 #[test]
201 fn test_selection_11() {
202 use crate::selection::dsl::*;
203
204 assert_parses_to!("*", all(true_()));
205 assert_parses_to!("*,*", all(all(true_())));
206 assert_parses_to!("*,*,*", all(all(all(true_()))));
207
208 assert_parses_to!("4:8", range(shape::Range(4, Some(8), 1), true_()));
209 assert_parses_to!("4:", range(shape::Range(4, None, 1), true_()));
210 assert_parses_to!("4", range(shape::Range(4, Some(5), 1), true_()));
211 assert_parses_to!(":", range(shape::Range(0, None, 1), true_()));
212
213 assert_parses_to!("0,0,0", range(0, range(0, range(0, true_()))));
214 assert_parses_to!("1,1,1", range(1, range(1, range(1, true_()))));
215 assert_parses_to!("*,0", all(range(0, true_())));
216 assert_parses_to!("*,0,*", all(range(0, all(true_()))));
217 assert_parses_to!("*,0,*", all(range(0, all(true_()))));
218 assert_parses_to!("*,*,4:", all(all(range(4.., true_()))));
219 assert_parses_to!(
220 "*,*,1::2",
221 all(all(range(shape::Range(1, None, 2), true_())))
222 );
223
224 assert_parses_to!(
225 "*,*,:4|*,*,4:",
226 union(
227 all(all(range(0..4, true_()))),
228 all(all(range(shape::Range(4, None, 1), true_())))
229 )
230 );
231 assert_parses_to!(
232 "*,0,:4|*,1,4:8",
233 union(
234 all(range(0, range(0..4, true_()))),
235 all(range(1, range(4..8, true_()))),
236 )
237 );
238 assert_parses_to!(
239 "*,*,(:2|6:)",
240 all(all(union(
241 range(0..2, true_()),
242 range(shape::Range(6, None, 1), true_()),
243 )))
244 );
245 assert_parses_to!(
246 "*,*,(1:4:2|5::2)",
247 all(all(union(
248 range(shape::Range(1, Some(4), 2), true_()),
249 range(shape::Range(5, None, 2), true_()),
250 )))
251 );
252 assert_parses_to!("*&*", intersection(all(true_()), all(true_())));
253 assert_parses_to!(
254 "*&*,*,4:8",
255 intersection(all(true_()), all(all(range(4..8, true_()))))
256 );
257 assert_parses_to!(
258 "*,*,0:5&*,*,4:8",
259 intersection(
260 all(all(range(0..5, true_()))),
261 all(all(range(4..8, true_()))),
262 )
263 );
264
265 assert_parses_to!("?,?,?", any(any(any(true_()))));
266 assert_parses_to!("0,?,:4", range(0, any(range(0..4, true_()))));
267 assert_parses_to!("0,?", range(0, any(true_())));
268 assert_parses_to!("?", any(true_()));
269 assert_parses_to!(
270 "0,0,?|0,0,?",
271 union(
272 range(0, range(0, any(true_()))),
273 range(0, range(0, any(true_()))),
274 )
275 );
276 assert_parses_to!(
277 "*,*,1:4|5:6",
278 union(all(all(range(1..4, true_()))), range(5..6, true_()))
279 );
280 assert_parses_to!(
281 "*,*,(1:4|5:6)",
282 all(all(union(range(1..4, true_()), range(5..6, true_()))))
283 );
284 assert_parses_to!(
285 "*,(1:4|5:6),*",
286 all(union(
287 range(shape::Range(1, Some(4), 1), all(true_())),
288 range(shape::Range(5, Some(6), 1), all(true_())),
289 ))
290 );
291 assert_parses_to!(
292 "(*,*,*)&(*,*,*)",
293 intersection(all(all(all(true_()))), all(all(all(true_()))))
294 );
295 assert_parses_to!(
296 "(0,*,*)&(0,(1|3),*)",
297 intersection(
298 range(0, all(all(true_()))),
299 range(0, union(range(1, all(true_())), range(3, all(true_())))),
300 )
301 );
302 assert_parses_to!(
303 "(*,*,(:2|6:))&(*,*,4:)",
304 intersection(
305 all(all(union(
306 range(0..2, true_()),
307 range(shape::Range(6, None, 1), true_()),
308 ))),
309 all(all(range(shape::Range(4, None, 1), true_()))),
310 )
311 );
312 assert_parses_to!("((1:4),2)", range(1..4, range(2, true_())));
313 }
314
315 #[test]
316 fn test_12() {
317 use crate::dsl::all;
318 use crate::dsl::true_;
319
320 assert_parses_to!("*,*,*,*,*,*", all(all(all(all(all(all(true_())))))));
321 }
322}