1use 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
10use super::{Parser, Result, Input, Error, ErrorDetail};
11
12/// Transforms an error into a failure, while adding a message in the error detail.
13pub fn with_failure_message<'a, P, V>(mut parser: P, message: &'a str) -> impl Parser<'a, V>
14where
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.
29pub 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>
34where
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.
46pub fn spaced<'a, P, V>(parser: P) -> impl Parser<'a, V>
47where
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.
58pub 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.
63pub fn is_present<'a, P, V>(parser: P) -> impl Parser<'a, bool>
64where
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.
71pub fn function<'a, PV, N, P>(word_parser: N, parser: P) -> impl Parser<'a, PV>
72where
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.
88pub fn word<'a, P>(mut word_parser: P) -> impl Parser<'a, &'a str>
89where
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.
108pub 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.
118pub 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)]
128mod 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