| 1 | use nom::{ |
| 2 | Err, |
| 3 | sequence::{delimited, preceded}, |
| 4 | character::complete::{multispace0, alpha1}, |
| 5 | bytes::complete::tag, |
| 6 | combinator::{map, opt}, |
| 7 | error::ErrorKind, |
| 8 | }; |
| 9 | |
| 10 | use super::{Parser, Result, Input, Error, ErrorDetail}; |
| 11 | |
| 12 | /// Transforms an error into a failure, while adding a message in the error detail. |
| 13 | pub fn with_failure_message<'a, P, V>(mut parser: P, message: &'a str) -> impl Parser<'a, V> |
| 14 | where |
| 15 | P: Parser<'a, V>, |
| 16 | { |
| 17 | move |input: Input<'a>| parser(input).map_err( |
| 18 | |nom_err: Err<Error>| match nom_err { |
| 19 | Err::Error(e: Error<'_>) => { |
| 20 | Err::Failure(e.with_detail(ErrorDetail::new(input, message))) |
| 21 | } |
| 22 | e: Err> => e, |
| 23 | } |
| 24 | ) |
| 25 | } |
| 26 | |
| 27 | /// Checks if the first parser succeeds, then parses the input with the second parser. If an error |
| 28 | /// is encountered with the second parser, then a failure message is thrown. |
| 29 | pub fn check_parser_before_failure<'a, C, CV, P, PV>( |
| 30 | mut check_parser: C, |
| 31 | mut parser: P, |
| 32 | failure_msg: &'a str |
| 33 | ) -> impl Parser<'a, PV> |
| 34 | where |
| 35 | C: Parser<'a, CV>, |
| 36 | P: Parser<'a, PV>, |
| 37 | { |
| 38 | move |input: &'a str| { |
| 39 | check_parser(input)?; |
| 40 | with_failure_message(|input| { parser(input) }, message:failure_msg) |
| 41 | (input) |
| 42 | } |
| 43 | } |
| 44 | |
| 45 | /// Creates a parser which accpets spaces around the original parsed input. |
| 46 | pub fn spaced<'a, P, V>(parser: P) -> impl Parser<'a, V> |
| 47 | where |
| 48 | P: Parser<'a, V>, |
| 49 | { |
| 50 | delimited( |
| 51 | first:multispace0, |
| 52 | second:parser, |
| 53 | third:multispace0, |
| 54 | ) |
| 55 | } |
| 56 | |
| 57 | /// Parsed a spaced tag. |
| 58 | pub fn stag(s: &str) -> impl Parser<'_, &str> { |
| 59 | spaced(parser:tag(s)) |
| 60 | } |
| 61 | |
| 62 | /// Creates a parser which makes the parser optional and returns true if the parse was successful. |
| 63 | pub fn is_present<'a, P, V>(parser: P) -> impl Parser<'a, bool> |
| 64 | where |
| 65 | P: Parser<'a, V>, |
| 66 | { |
| 67 | map(parser:opt(parser), |v: Option| v.is_some()) |
| 68 | } |
| 69 | |
| 70 | /// Creates a parser which parses a function call. |
| 71 | pub fn function<'a, PV, N, P>(word_parser: N, parser: P) -> impl Parser<'a, PV> |
| 72 | where |
| 73 | N: Parser<'a, &'a str>, |
| 74 | P: Parser<'a, PV>, |
| 75 | { |
| 76 | preceded( |
| 77 | first:word(word_parser), |
| 78 | second:delimited( |
| 79 | first:with_failure_message(stag("(" ), "Missing opening brace" ), |
| 80 | second:parser, |
| 81 | third:with_failure_message(parser:stag(")" ), message:"Missing closing brace" ) |
| 82 | ) |
| 83 | ) |
| 84 | } |
| 85 | |
| 86 | /// Parses a word made only by alpha characters ('a' => 'z' and 'A' => 'Z'), and checks if this |
| 87 | /// word matches exactly the given parser. |
| 88 | pub fn word<'a, P>(mut word_parser: P) -> impl Parser<'a, &'a str> |
| 89 | where |
| 90 | P: Parser<'a, &'a str>, |
| 91 | { |
| 92 | move |input: &'a str| { |
| 93 | let (input: &'a str, word: &'a str) = alpha1(input)?; |
| 94 | match word_parser(word) { |
| 95 | Ok((_, parsed_word: &'a str)) => { |
| 96 | if word == parsed_word { |
| 97 | Ok((input, word)) |
| 98 | } else { |
| 99 | Err(Err::Error(Error::new(input, code:ErrorKind::Alpha, detail:None))) |
| 100 | } |
| 101 | } |
| 102 | Err(e: Err>) => Err(e), |
| 103 | } |
| 104 | } |
| 105 | } |
| 106 | |
| 107 | /// Parses an uppercase word. |
| 108 | pub fn uppercase_word(input: Input<'_>) -> Result<'_, &str> { |
| 109 | let (input: &str, word: &str) = alpha1(input)?; |
| 110 | if word.chars().all(|c: char| c.is_ascii_uppercase()) { |
| 111 | Ok((input, word)) |
| 112 | } else { |
| 113 | Err(Err::Error(Error::new(input, code:ErrorKind::Alpha, detail:None))) |
| 114 | } |
| 115 | } |
| 116 | |
| 117 | /// Parses a lowercase word. |
| 118 | pub fn lowercase_word(input: Input<'_>) -> Result<'_, &str> { |
| 119 | let (input: &str, word: &str) = alpha1(input)?; |
| 120 | if word.chars().all(|c: char| c.is_ascii_lowercase()) { |
| 121 | Ok((input, word)) |
| 122 | } else { |
| 123 | Err(Err::Error(Error::new(input, code:ErrorKind::Alpha, detail:None))) |
| 124 | } |
| 125 | } |
| 126 | |
| 127 | #[cfg (test)] |
| 128 | mod tests { |
| 129 | use super::*; |
| 130 | |
| 131 | #[test ] |
| 132 | fn test_uppercase_word() { |
| 133 | let input = "foo" ; |
| 134 | assert!(uppercase_word(input).is_err()); |
| 135 | let input = "FOOfoo" ; |
| 136 | assert!(uppercase_word(input).is_err()); |
| 137 | let input = "FOO" ; |
| 138 | assert!(uppercase_word(input).is_ok()); |
| 139 | let input = "FOO;;" ; |
| 140 | assert!(uppercase_word(input).is_ok()); |
| 141 | } |
| 142 | } |
| 143 | |