utils/parser/macros.rs
1/// Macro to define a parser for one or more string literals, mapping the results.
2///
3/// This is a replacement for
4/// [`parser::one_of`](crate::parser::one_of())`(("a".map(|_| Enum::A), "b".map(|_| Enum::b)))`
5/// which produces more optimized assembly and is easier to read and write.
6///
7/// The string patterns are matched in the order provided, so strings should be ordered by length.
8///
9/// Using this makes [2017 day 11](../../year2017/struct.Day11.html), which parses a sequence of
10/// literals separated by commas, over 2x faster.
11///
12/// See also [`parser::parsable_enum!`](crate::parser::parsable_enum), which provides a macro to
13/// define an enum and literal parser together.
14///
15/// # Examples
16/// ```
17/// # use utils::parser::{Parser, self};
18/// #[derive(Debug, PartialEq)]
19/// enum Example {
20/// A,
21/// B,
22/// C,
23/// }
24///
25/// let parser = parser::literal_map!(
26/// "A" | "a" => Example::A,
27/// "B" => Example::B,
28/// "C" => Example::C,
29/// );
30/// assert_eq!(parser.parse(b"A"), Ok((Example::A, &b""[..])));
31/// assert_eq!(parser.parse(b"a"), Ok((Example::A, &b""[..])));
32/// assert_eq!(parser.parse(b"B"), Ok((Example::B, &b""[..])));
33/// assert_eq!(parser.parse(b"C"), Ok((Example::C, &b""[..])));
34/// assert!(parser.parse(b"D").is_err());
35/// ```
36#[macro_export]
37macro_rules! parser_literal_map {
38 (
39 $($($l:literal)|+ => $e:expr),+$(,)?
40 ) => {{
41 const fn coerce_to_parser<F: Fn(&[u8]) -> $crate::parser::ParseResult<'_, O>, O>(f: F) -> F { f }
42
43 coerce_to_parser(|input| {
44 $($(
45 if input.len() >= const { $l.len() } && const { $l.as_bytes() } == &input[..const { $l.len() }] {
46 return Ok((($e), &input[const { $l.len() }..]));
47 }
48 )+)*
49
50 Err(($crate::parser_literal_map!(@error $($($l)+)+), input))
51 })
52 }};
53 (@error $first:literal $($l:literal)+) => {
54 $crate::parser::ParseError::Custom(concat!("expected one of '", $first, "'", $(", '", $l, "'",)+))
55 };
56 (@error $first:literal) => {
57 $crate::parser::ParseError::ExpectedLiteral($first)
58 };
59}
60
61/// Macro to define an enum that implements [`Parseable`](crate::parser::Parseable).
62///
63/// The parser is implemented using [`parser::literal_map!`](crate::parser::literal_map).
64///
65/// # Examples
66/// ```
67/// # use utils::parser::{Parser, Parseable, self};
68/// parser::parsable_enum! {
69/// #[derive(Debug, PartialEq, Default)]
70/// enum Direction {
71/// #[default]
72/// "north" | "n" => North,
73/// "south" | "s" => South,
74/// "east" | "e" => East,
75/// "west" | "w" => West,
76/// }
77/// }
78///
79/// assert_eq!(Direction::PARSER.parse(b"north"), Ok((Direction::North, &b""[..])));
80/// assert_eq!(Direction::PARSER.parse(b"s"), Ok((Direction::South, &b""[..])));
81/// assert!(Direction::PARSER.parse(b"a").is_err());
82/// ```
83#[macro_export]
84macro_rules! parser_parsable_enum {
85 (
86 $(#[$enum_meta:meta])*
87 enum $name:ident {$(
88 $(#[$meta:meta])*
89 $($l:literal)|+ => $variant:ident $(= $value:expr)?,
90 )+}
91 ) => {
92 $(#[$enum_meta])*
93 pub enum $name {$(
94 $(#[$meta])*
95 $variant $(= $value)?,
96 )+}
97
98 impl $name {
99 const ALL: &'static [$name] = &[$(
100 Self::$variant,
101 )+];
102 }
103
104 impl $crate::parser::Parseable for $name {
105 type Parser = for<'a> fn(&'a [u8]) -> $crate::parser::ParseResult<'a, Self>;
106 const PARSER: Self::Parser = $crate::parser_literal_map!($(
107 $($l)|+ => Self::$variant,
108 )+);
109 }
110 };
111}
112
113/// Macro to define a custom parser using a `match` inspired parse tree syntax.
114///
115/// Each rule is made up of a list of chained parsers enclosed in brackets on the left-hand side.
116/// Parsers can be prefixed with an identifier followed by `@` to store the result of that parser in
117/// the supplied variable, similar to normal match patterns.
118///
119/// After the list of parsers, there is an arrow determining the functionality of the rule when the
120/// parsers match:
121/// - **Expression (`=>`)**: The expression on the right-hand is evaluated and returned.
122/// - **Fallible (`=?>`)**: Similar to Expression, but the right-hand side evaluates a result. If
123/// the expression evaluates to [`Ok`], the value contained inside is returned. Otherwise, the
124/// string contained inside the [`Err`] is handled as a custom [`ParseError`](super::ParseError),
125/// and parsing will continue with the following rule.
126/// - **Subtree (`=>>`)**: The right-hand side is a nested set of rules enclosed in braces.
127///
128/// If none of the rules match successfully, the error from the rule which parsed furthest into
129/// the input is returned.
130///
131/// # Examples
132/// ```
133/// # use utils::parser::{self, Parser};
134/// #
135/// #[derive(Debug, PartialEq)]
136/// enum Register {
137/// A, B, C
138/// }
139///
140/// #[derive(Debug, PartialEq)]
141/// enum Instruction {
142/// Add(Register, Register),
143/// AddConstant(Register, i32),
144/// Copy(Register, Register),
145/// Noop,
146/// }
147///
148/// let register = parser::literal_map!(
149/// "A" => Register::A, "B" => Register::B, "C" => Register::C,
150/// );
151///
152/// let instruction = parser::parse_tree!(
153/// ("add ", r @ register, ", ") =>> {
154/// (r2 @ register) => Instruction::Add(r, r2),
155/// (v @ parser::i32()) => Instruction::AddConstant(r, v),
156/// },
157/// ("copy ", r @ register, ", ", r2 @ register) =?> {
158/// if r == r2 {
159/// Err("cannot copy register to itself")
160/// } else {
161/// Ok(Instruction::Copy(r, r2))
162/// }
163/// },
164/// ("noop") => Instruction::Noop,
165/// );
166///
167/// assert_eq!(
168/// instruction.parse_complete("add A, B").unwrap(),
169/// Instruction::Add(Register::A, Register::B)
170/// );
171/// assert_eq!(
172/// instruction.parse_complete("add C, 100").unwrap(),
173/// Instruction::AddConstant(Register::C, 100)
174/// );
175/// assert_eq!(
176/// instruction.parse_complete("copy A, B").unwrap(),
177/// Instruction::Copy(Register::A, Register::B)
178/// );
179/// assert!(instruction
180/// .parse_complete("copy A, A")
181/// .is_err_and(|err| err.to_string().contains("cannot copy register to itself")));
182/// ```
183#[macro_export]
184macro_rules! parser_parse_tree {
185 (@rule $input:ident $furthest_err:ident $furthest_remaining:ident [$(,)?] @expr $rhs:expr) => {
186 return Ok(($rhs, $input));
187 };
188 (@rule $input:ident $furthest_err:ident $furthest_remaining:ident [$(,)?] @expr_res $rhs:expr) => {
189 match $rhs {
190 Ok(v) => return Ok((v, $input)),
191 Err(e) => {
192 if $input.len() < $furthest_remaining {
193 $furthest_err = $crate::parser::ParseError::Custom(e);
194 $furthest_remaining = $input.len();
195 }
196 }
197 };
198 };
199 (@rule $input:ident $furthest_err:ident $furthest_remaining:ident [$(,)?] @subtree $($rhs:tt)+) => {
200 $crate::parser_parse_tree!(@toplevel $input $furthest_err $furthest_remaining $($rhs)+);
201 };
202
203 (@rule $input:ident $furthest_err:ident $furthest_remaining:ident
204 [$n:ident @ $lhs:expr $(,$($tail:tt)*)?] $($rhs:tt)+
205 ) => {
206 match $crate::parser::Parser::parse(&($lhs), $input) {
207 Ok(($n, $input)) => {
208 $crate::parser_parse_tree!(@rule $input $furthest_err $furthest_remaining
209 [$($($tail)*)?] $($rhs)+
210 );
211 },
212 Err((err, remaining)) => {
213 if remaining.len() < $furthest_remaining {
214 $furthest_err = err;
215 $furthest_remaining = remaining.len();
216 }
217 }
218 };
219 };
220 (@rule $input:ident $furthest_err:ident $furthest_remaining:ident
221 [$lhs:expr $(,$($tail:tt)*)?] $($rhs:tt)+
222 ) => {
223 match $crate::parser::Parser::parse(&($lhs), $input) {
224 Ok((_, $input)) => {
225 $crate::parser_parse_tree!(@rule $input $furthest_err $furthest_remaining
226 [$($($tail)*)?] $($rhs)+
227 );
228 },
229 Err((err, remaining)) => {
230 if remaining.len() < $furthest_remaining {
231 $furthest_err = err;
232 $furthest_remaining = remaining.len();
233 }
234 }
235 };
236 };
237
238 (@toplevel $input:ident $furthest_err:ident $furthest_remaining:ident
239 ($($lhs:tt)+) => $rhs:expr $(, $($tail:tt)*)?
240 ) => {
241 $crate::parser_parse_tree!(@rule $input $furthest_err $furthest_remaining [$($lhs)+] @expr $rhs);
242 $($crate::parser_parse_tree!(@toplevel $input $furthest_err $furthest_remaining $($tail)*);)?
243 };
244 (@toplevel $input:ident $furthest_err:ident $furthest_remaining:ident
245 ($($lhs:tt)+) =?> $rhs:expr $(, $($tail:tt)*)?
246 ) => {
247 $crate::parser_parse_tree!(@rule $input $furthest_err $furthest_remaining [$($lhs)+] @expr_res $rhs);
248 $($crate::parser_parse_tree!(@toplevel $input $furthest_err $furthest_remaining $($tail)*);)?
249 };
250 (@toplevel $input:ident $furthest_err:ident $furthest_remaining:ident
251 ($($lhs:tt)+) =>> {$($rhs:tt)+} $(, $($tail:tt)*)?
252 ) => {
253 $crate::parser_parse_tree!(@rule $input $furthest_err $furthest_remaining [$($lhs)+] @subtree $($rhs)+);
254 $($crate::parser_parse_tree!(@toplevel $input $furthest_err $furthest_remaining $($tail)*);)?
255 };
256 (@toplevel $input:ident $furthest_err:ident $furthest_remaining:ident $(,)?) => {};
257
258 // Ensures this branch only matches inputs starting with (, giving each rule set a unique prefix
259 (($($first:tt)+) $($tail:tt)+) => {{
260 const fn coerce_to_parser<F: Fn(&[u8]) -> $crate::parser::ParseResult<'_, O>, O>(f: F) -> F { f }
261
262 coerce_to_parser(|input| {
263 let mut furthest_err = $crate::parser::ParseError::Custom("unreachable");
264 let mut furthest_remaining = usize::MAX;
265
266 $crate::parser_parse_tree!(@toplevel input furthest_err furthest_remaining ($($first)+) $($tail)+);
267
268 Err((furthest_err, &input[input.len() - furthest_remaining..]))
269 })
270 }};
271}