anpa/macros.rs
1/// Shorthand for creating a parser.
2/// ### Example:
3/// ```ignore
4/// let p = create_parser!(s, { /* Define parser behavior using state `s` */ })
5/// ```
6#[macro_export]
7macro_rules! create_parser {
8 ($state:ident, $f:expr) => {
9 move |$state: &mut $crate::core::AnpaState<_, _>| $f
10 }
11}
12
13/// Shorthand for creating a deferred parser. This is mandatory when creating recursive parsers.
14///
15/// ### Example:
16/// ```
17/// use anpa::defer_parser;
18/// use anpa::core::*;
19/// use anpa::combinators::{middle, not_empty, or};
20/// use anpa::parsers::{item_while, take};
21///
22/// /// Can parse e.g. "(((something)))"
23/// fn in_parens<'a>() -> impl StrParser<'a> {
24/// defer_parser!(or(
25/// not_empty(item_while(|c: char| c.is_alphanumeric())),
26/// middle(take('('), in_parens(), take(')')))
27/// )
28/// }
29///
30/// assert_eq!(parse(in_parens(), "((((inside))))" ).result, Some("inside"));
31///
32/// ```
33#[macro_export]
34macro_rules! defer_parser {
35 ($p:expr) => {
36 $crate::create_parser!(s, $p(s))
37 }
38}
39
40/// Variadic version of `map`, where all provided parsers must succeed.
41/// ### Arguments
42/// * `f` - the transformation function. Its arguments must match the result types of `p...` in
43/// both type and number.
44/// * `p...` - any number of parsers.
45#[macro_export]
46macro_rules! map {
47 ($f:expr, $($p:expr),* $(,)?) => {
48 $crate::create_parser!(s, Some($f($($p(s)?),*)))
49 };
50}
51
52/// Variadic version of `map_if`, where all provided parsers must succeed.
53/// ### Arguments
54/// * `f` - the transformation function. Its arguments must match the result types of `p...` in
55/// both type and number.
56/// * `p...` - any number of parsers.
57#[macro_export]
58macro_rules! map_if {
59 ($f:expr, $($p:expr),* $(,)?) => {
60 $crate::create_parser!(s, Some($f($($p(s)?),*)?))
61 };
62}
63
64/// Convert a number of parsers to a single parser producing a tuple with all the results.
65/// ### Arguments
66/// * `p...` - any number of parsers.
67#[macro_export]
68macro_rules! tuplify {
69 ($($p:expr),* $(,)?) => {
70 $crate::create_parser!(s, Some(($($p(s)?),*)))
71 };
72}
73
74/// Create a parser that successfully returns `x`.
75///
76/// ### Arguments
77/// * `x` - the result to be returned from the parser.
78#[macro_export]
79macro_rules! pure {
80 ($x:expr) => {
81 $crate::create_parser!(_s, Some($x))
82 };
83}
84
85/// A helper macro to generate variadic macros using repeated application of the rightmost
86/// argument of a binary function.
87///
88/// E.g. for 4 arguments, the resulting function will be constructed as:
89/// `f(e1, f(e2, f(e3, e4)))`
90///
91/// ### Arguments
92/// * `f` - a binary function.
93/// * `e...` - any number of arguments.
94#[macro_export]
95macro_rules! variadic {
96 ($f:expr, $e:expr) => {
97 $e
98 };
99 ($f:expr, $e:expr, $($e2:expr),*) => {
100 $f($e, $crate::variadic!($f, $($e2),*))
101 };
102}
103
104/// Variadic version of `or`.
105///
106/// ### Arguments
107/// * `p...` - any number of parsers.
108#[macro_export]
109macro_rules! or {
110 ($($p:expr),* $(,)?) => {
111 $crate::variadic!($crate::combinators::or, $($p),*)
112 };
113}
114
115/// Variadic version of `or_no_partial`.
116///
117/// ### Arguments
118/// * `p...` - any number of parsers.
119#[macro_export]
120macro_rules! or_no_partial {
121 ($($p:expr),* $(,)?) => {
122 $crate::variadic!($crate::combinators::or_no_partial, $($p),*)
123 };
124}
125
126/// Variadic version of `or_diff`.
127///
128/// ### Arguments
129/// * `p...` - any number of parsers.
130#[macro_export]
131macro_rules! or_diff {
132 ($($p:expr),* $(,)?) => {
133 $crate::variadic!($crate::combinators::or_diff, $($p),*)
134 };
135}
136
137/// Variadic version of `or_diff_no_partial`.
138///
139/// ### Arguments
140/// * `p...` - any number of parsers.
141#[macro_export]
142macro_rules! or_diff_no_partial {
143 ($($p:expr),* $(,)?) => {
144 $crate::variadic!($crate::combinators::or_diff_no_partial, $($p),*)
145 };
146}
147
148/// Variadic version of `left`, where only the leftmost parser's result will be returned.
149///
150/// ### Arguments
151/// * `p...` - any number of parsers.
152#[macro_export]
153macro_rules! left {
154 ($($p:expr),* $(,)?) => {
155 $crate::variadic!($crate::combinators::left, $($p),*)
156 };
157}
158
159/// Variadic version of `right`, where only the rightmost parser's result will be returned.
160///
161/// ### Arguments
162/// * `p...` - any number of parsers.
163#[macro_export]
164macro_rules! right {
165 ($($p:expr),* $(,)?) => {
166 $crate::variadic!($crate::combinators::right, $($p),*)
167 };
168}
169
170/// Variadic parser that succeeds if the next item matches the provided
171/// patterns. Note that unlike `matches!`, this macro accepts multiple
172/// patterns.
173///
174/// ### Arguments
175/// * `patterns` - match patterns (as one would pass to `matches!`)
176///
177/// ### Example:
178/// ```
179/// use anpa::core::*;
180/// use anpa::item_matches;
181///
182/// let p = item_matches!('0' | '1');
183///
184/// assert_eq!(parse(p, "012").result, Some('0'));
185/// assert_eq!(parse(p, "123").result, Some('1'));
186/// assert_eq!(parse(p, "234").result, None);
187/// ```
188#[macro_export]
189macro_rules! item_matches {
190 ($($($patterns:pat_param)|+ $(if $guard:expr)?),+ $(,)?) => {
191 $crate::parsers::item_if(|c| {
192 match c {
193 $($($patterns)|+ $(if $guard)? => true,)+
194 _ => false
195 }
196 })
197 };
198}
199
200/// Alternative to the `take` parser that inlines the argument into the parser.
201///
202/// This can give better performance and/or smaller binary size, or the opposite.
203/// Try it and don't forget to measure!
204///
205/// This macro is likely only useful when passing a literal as argument.
206///
207/// ### Arguments
208/// * `prefix` - the prefix to parse.
209#[macro_export]
210macro_rules! take {
211 ($prefix:expr) => {
212 $crate::create_parser!(s, {
213 $crate::prefix::Prefix::take_prefix(&$prefix, s.input).map(|(res, rest)| {
214 s.input = rest;
215 res
216 })
217 })
218 }
219}
220
221/// Alternative to the `skip` parser that inlines the argument into the parser.
222///
223/// This can give better performance and/or smaller binary size, or the opposite.
224/// Try it and don't forget to measure!
225///
226/// This macro is likely only useful when passing a literal as argument.
227///
228/// ### Arguments
229/// * `prefix` - the prefix to parse.
230#[macro_export]
231macro_rules! skip {
232 ($prefix:expr) => {
233 $crate::create_parser!(s, {
234 s.input = $crate::prefix::Prefix::skip_prefix(&$prefix, s.input)?;
235 Some(())
236 })
237 }
238}
239/// Alternative to the `until` parser that inlines the argument into the parser.
240///
241/// This can give better performance and/or smaller binary size, or the opposite.
242/// Try it and don't forget to measure!
243///
244/// This macro is likely only useful when passing a literal as argument.
245///
246/// ### Arguments
247/// * `needle` - the element to search for.
248#[macro_export]
249macro_rules! until {
250 ($needle:expr) => {
251 $crate::create_parser!(s, {
252 let (size, index) = $crate::needle::Needle::find_in(&$needle, s.input)?;
253 let res = $crate::slicelike::SliceLike::slice_to(s.input, index);
254 s.input = $crate::slicelike::SliceLike::slice_from(s.input, index + size);
255 Some(res)
256 })
257 }
258}
259
260/// Variadic version of `greedy_or`, where the result of the parser with the most consumed
261/// input will be returned.
262///
263/// ### Arguments
264/// * `p...` - any number of parsers.
265#[macro_export]
266macro_rules! greedy_or {
267 ($($p:expr),* $(,)?) => {
268 $crate::variadic!($crate::combinators::greedy_or, $($p),*)
269 };
270}
271
272/// Create a parser that takes the result of a parser, and returns different
273/// parsers depending on the provided conditions.
274///
275/// If none of the provided conditions match, the parser will fail.
276///
277/// The macro comes in two flavors:
278/// - if-style:
279/// ```
280/// # use anpa::{choose, core::parse, number::integer, parsers::take};
281/// let p = choose!(integer() => n; // Explicit binding to variable `v`
282/// (0_u8..=4).contains(&n) => take("range"),
283/// n == 5 => take("five"));
284/// # parse(p, "dummy");
285/// ```
286/// - match-style:
287/// ```
288/// # use anpa::{choose, core::parse, number::integer, parsers::take};
289/// let p = choose!(integer(); // No binding of result.
290/// 0_u8..=4 => take("range"),
291/// 5 => take("five"));
292/// # parse(p, "dummy");
293/// ```
294///
295///
296/// ### Example:
297/// ```
298/// use anpa::core::*;
299/// use anpa::choose;
300/// use anpa::parsers::take;
301/// use anpa::number::integer;
302///
303/// let p_if = choose!(integer() => x: u8; // Note the semicolon
304/// x == 0 => take("zero"),
305/// x == 1 => take("one"),
306/// (2..=100).contains(&x) => take("big")
307/// );
308///
309/// let p_match = choose!(integer(); // Note the semicolon
310/// 0_u8 => take("zero"),
311/// 1 => take("one"),
312/// 2..=100 => take("big")
313/// );
314///
315/// let input1 = "0zero";
316/// let input2 = "1one";
317/// let input3 = "2big";
318/// let input4 = "100big";
319/// let input5 = "101big";
320/// let input6 = "0one";
321/// let input7 = "1zero";
322///
323/// assert_eq!(parse(p_if, input1).result, Some("zero"));
324/// assert_eq!(parse(p_if, input2).result, Some("one"));
325/// assert_eq!(parse(p_if, input3).result, Some("big"));
326/// assert_eq!(parse(p_if, input4).result, Some("big"));
327/// assert_eq!(parse(p_if, input5).result, None);
328/// assert_eq!(parse(p_if, input6).result, None);
329/// assert_eq!(parse(p_if, input7).result, None);
330///
331/// assert_eq!(parse(p_match, input1).result, Some("zero"));
332/// assert_eq!(parse(p_match, input2).result, Some("one"));
333/// assert_eq!(parse(p_match, input3).result, Some("big"));
334/// assert_eq!(parse(p_match, input4).result, Some("big"));
335/// assert_eq!(parse(p_match, input5).result, None);
336/// assert_eq!(parse(p_match, input6).result, None);
337/// assert_eq!(parse(p_match, input7).result, None);
338/// ```
339#[macro_export]
340macro_rules! choose {
341 ($p:expr => $res:ident $(: $t:ty)?; $($cond:expr => $new_p:expr),* $(,)?) => {
342 $crate::create_parser!(s, {
343 let $res $(:$t)? = $p(s)?;
344
345 $(if $cond {
346 return $new_p(s)
347 })*
348
349 None
350 })
351 };
352
353 ($p:expr; $($arm:pat_param $(| $alt:pat_param)* $(if $guard:expr)? => $new_p:expr),* $(,)?) => {
354 $crate::create_parser!(s, {
355 match $p(s)? {
356 $(
357 $arm $(| $alt)* $(if $guard)? => $new_p(s),
358 )*
359 #[allow(unreachable_patterns)]
360 _ => None
361 }
362 })
363 };
364}
365
366/// Create a new parser trait with a concrete input type for cleaner APIs.
367/// ### Arguments
368/// * `id` - The identifier of the new trait
369/// * `input` - The type of the input. Do not include lifetime or reference.
370/// * `comment` - The doc comment to be generated for the trait
371///
372/// ### Example
373/// ```
374/// use anpa::create_parser_trait;
375/// create_parser_trait!(I8Parser, [i8], "Convenience alias for a parser that parses a `&'a [i8]`");
376/// ```
377#[macro_export]
378macro_rules! create_parser_trait {
379 ($id:ident, $input:ty, $comment:expr) => {
380 #[doc=$comment]
381 pub trait $id<'a, O = &'a $input, S = ()>: $crate::core::Parser<&'a $input, O, S> {}
382 impl<'a, O, S, P: $crate::core::Parser<&'a $input, O, S>> $id<'a, O, S> for P {}
383 };
384}