diff --git a/src/text.rs b/src/text.rs index fab818c..8d1ed7b 100644 --- a/src/text.rs +++ b/src/text.rs @@ -1,23 +1,16 @@ -use crate::parser::{satisfy, take, FailedParserResult, Parser}; +mod string_parsers; + +use crate::parser::{satisfy, Parser}; +use crate::text::string_parsers::StringParser; pub fn char_parser<'a, TContext>(c: char) -> impl Parser<'a, char, char, TContext> { satisfy(move |x| *x == c) } -pub fn string_parser<'a, TContext>(str: &'static str) -> impl Parser<'a, char, String, TContext> { - take::(str.chars().count()).next(move |result| { - result.and_then(|(input, chars)| { - let chars: String = chars.iter().collect(); - if chars == str { - Ok((input, chars)) - } else { - Err(( - input, - FailedParserResult::new(format!("Failed to parse '{}'.", str)), - )) - } - }) - }) +pub fn string_parser<'a, TContext>( + str: &'static str, +) -> impl Parser<'a, char, &'a [char], TContext> { + StringParser { str } } pub fn one_of<'a, TContext>(str: &'static str) -> impl Parser<'a, char, char, TContext> { @@ -25,6 +18,13 @@ pub fn one_of<'a, TContext>(str: &'static str) -> impl Parser<'a, char, char, TC satisfy(move |c: &char| str.contains(c)) } +pub fn char_satisfy<'a, TContext, F>(condition: F) -> impl Parser<'a, char, char, TContext> +where + F: Fn(&char) -> bool, +{ + satisfy(move |x: &char| condition(x)) +} + #[cfg(test)] mod test { use super::*; @@ -52,7 +52,11 @@ mod test { parser_test_helper( ParserContext::new_with_str("Hello, world!", ()), &"Hello".to_owned(), - |context, input| string_parser("Hello").parse(context, input), + |context, input| { + string_parser("Hello") + .map(|x| x.iter().collect()) + .parse(context, input) + }, ); fn test_parser( @@ -62,7 +66,10 @@ mod test { let (input, first) = string_parser("hello, ").parse(context.clone(), input)?; let (input, second) = string_parser("world!").parse(context.clone(), input)?; - Ok((input, first + second.as_str())) + Ok(( + input, + first.iter().collect::() + second.iter().collect::().as_str(), + )) } parser_test_helper( @@ -76,4 +83,11 @@ mod test { |context, input| test_parser.map(|_| ()).parse(context, input), ) } + + #[test] + fn char_satsify_test() { + parser_test_helper(ParserContext::new_with_str("abc", ()), &'a', |c, i| { + char_satisfy(char::is_ascii).parse(c, i) + }); + } } diff --git a/src/text/string_parsers.rs b/src/text/string_parsers.rs new file mode 100644 index 0000000..9384ce0 --- /dev/null +++ b/src/text/string_parsers.rs @@ -0,0 +1,37 @@ +use crate::parser::{FailedParserResult, Parser, ParserContext, ParserResult}; +use std::cell::RefCell; +use std::rc::Rc; + +pub struct StringParser { + pub(crate) str: &'static str, +} + +impl<'a, TContext> Parser<'a, char, &'a [char], TContext> for StringParser { + fn parse( + &self, + _: Rc>>, + input: &'a [char], + ) -> ParserResult<'a, char, &'a [char]> { + let length = self.str.chars().count(); + if input.len() < length { + return Err(FailedParserResult::new( + input, + format!("Failed to get enough elements for string '{}'.", self.str), + )); + } + + if (&input[..length]) + .iter() + .zip(self.str.chars()) + .map(|(x, y)| *x == y) + .all(|x| x) + { + Ok((&input[length..], &input[..length])) + } else { + Err(FailedParserResult::new( + input, + format!("Input is not string '{}'", self.str), + )) + } + } +}