|
| 1 | +/// Macro to define a parser which consumes a single byte and maps it using a lookup table. |
| 2 | +/// |
| 3 | +/// This macro is a wrapper around [`parser::byte_lut`](crate::parser::byte_lut) to allow defining |
| 4 | +/// the lookup table using a match-like syntax. Each expression must be const and evaluate to a |
| 5 | +/// value of the same copy type. |
| 6 | +/// |
| 7 | +/// # Examples |
| 8 | +/// ``` |
| 9 | +/// # use utils::parser::{Parser, self}; |
| 10 | +/// let parser = parser::byte_map!( |
| 11 | +/// b'#' => true, |
| 12 | +/// b'.' | b'S' => false, |
| 13 | +/// ); |
| 14 | +/// assert_eq!(parser.parse(b"#.S##"), Ok((true, &b".S##"[..]))); |
| 15 | +/// assert_eq!(parser.parse(b".S##"), Ok((false, &b"S##"[..]))); |
| 16 | +/// assert_eq!(parser.parse(b"S##"), Ok((false, &b"##"[..]))); |
| 17 | +/// |
| 18 | +/// let (err, remaining) = parser.parse(b"abc").unwrap_err(); |
| 19 | +/// assert_eq!(err.to_string(), "expected one of '#', '.', 'S'"); |
| 20 | +/// assert_eq!(remaining, &b"abc"[..]); |
| 21 | +/// ``` |
| 22 | +#[macro_export] |
| 23 | +macro_rules! parser_byte_map { |
| 24 | + ( |
| 25 | + $($($l:literal)|+ => $e:expr),+$(,)? |
| 26 | + ) => {{ |
| 27 | + // `let _: u8 = $l` ensures $l is used in the repetition and also ensures all the literals |
| 28 | + // are byte literals |
| 29 | + const COUNT: usize = 0usize $($(+ {let _: u8 = $l; 1usize})+)+; |
| 30 | + const LEN: usize = 14 + 5 * COUNT; |
| 31 | + const { |
| 32 | + assert!(COUNT >= 2, "at least two literals must be provided"); |
| 33 | + } |
| 34 | + |
| 35 | + // Once concat_bytes! is stabilized this error message can be created in the macro similar |
| 36 | + // to parser_literal_map! |
| 37 | + const ERROR: [u8; LEN] = { |
| 38 | + let mut result = [0u8; LEN]; |
| 39 | + let (prefix, vals) = result.split_at_mut(16); |
| 40 | + prefix.copy_from_slice(b"expected one of "); |
| 41 | + |
| 42 | + let mut i = 0; |
| 43 | + let literals = [$($($l),+),+]; |
| 44 | + while i < COUNT { |
| 45 | + vals[i * 5] = b'\''; |
| 46 | + vals[i * 5 + 1] = literals[i]; |
| 47 | + vals[i * 5 + 2] = b'\''; |
| 48 | + if i + 1 < COUNT { |
| 49 | + vals[i * 5 + 3] = b','; |
| 50 | + vals[i * 5 + 4] = b' '; |
| 51 | + } |
| 52 | + i += 1; |
| 53 | + } |
| 54 | + |
| 55 | + result |
| 56 | + }; |
| 57 | + |
| 58 | + $crate::parser::byte_lut(&const { |
| 59 | + // Don't use a const item for the lut to avoid naming the value type |
| 60 | + let mut lut = [None; 256]; |
| 61 | + $($( |
| 62 | + assert!(lut[$l as usize].is_none(), "duplicate literal"); |
| 63 | + lut[$l as usize] = Some($e); |
| 64 | + )+)+ |
| 65 | + lut |
| 66 | + }, const { |
| 67 | + match str::from_utf8(&ERROR) { |
| 68 | + Ok(v) => v, |
| 69 | + Err(_) => panic!("one or more of the provided literals is invalid unicode"), |
| 70 | + } |
| 71 | + }) |
| 72 | + }}; |
| 73 | +} |
| 74 | + |
1 | 75 | /// Macro to define a parser for one or more string literals, mapping the results. |
2 | 76 | /// |
3 | 77 | /// This is a replacement for |
|
0 commit comments