diff --git a/src/parser.rs b/src/parser.rs index d9bd2dc..922ffd1 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -3,11 +3,11 @@ mod primitive_parsers; use crate::parser::modified_parsers::{ BindParser, ConvertParser, LitervalCountParser, LitervalParser, LookAheadParser, MapParser, - NextParser, ReverseParser, + NextParser, ReverseParser, TryParser, }; use crate::parser::primitive_parsers::{ - AnyParser, FailParser, FailWithMessageParser, SatisfyParser, SkipParser, SucceedParser, - TakeParser, + AnyParser, FailParser, FailWithMessageParser, SatifiedMapParser, SatisfyParser, SkipParser, + SucceedParser, TakeParser, }; use std::cell::RefCell; use std::error::Error; @@ -178,6 +178,17 @@ where } } + fn try_parse(self, result: T) -> TryParser + where + Self: Sized, + T: Clone, + { + TryParser { + parser: self, + result, + } + } + fn reverse(self, result: TResult) -> ReverseParser where Self: Sized, @@ -259,6 +270,16 @@ where SatisfyParser { predicate } } +pub fn satified_map<'a, TToken, TContext, T: 'a, F>( + mapper: F, +) -> impl Parser<'a, TToken, T, TContext> +where + TToken: Debug + Clone, + F: Fn(&TToken) -> Option, +{ + SatifiedMapParser { mapper } +} + pub fn any<'a, TToken, TContext>() -> impl Parser<'a, TToken, TToken, TContext> where TToken: Debug + Clone + 'a, diff --git a/src/parser/modified_parsers.rs b/src/parser/modified_parsers.rs index 2c4405e..d4893d6 100644 --- a/src/parser/modified_parsers.rs +++ b/src/parser/modified_parsers.rs @@ -189,6 +189,29 @@ where } } +pub struct TryParser { + pub(crate) parser: P, + pub(crate) result: T, +} + +impl<'a, TToken, T, TContext, P> Parser<'a, TToken, T, TContext> for TryParser +where + TToken: Debug + Clone, + T: Clone + 'a, + P: Parser<'a, TToken, T, TContext>, +{ + fn parse( + &self, + context: Rc>>, + input: &'a [TToken], + ) -> ParserResult<'a, TToken, T> { + match self.parser.parse(context, input) { + Err(r) => Ok((r.input, self.result.clone())), + r => r, + } + } +} + pub struct ReverseParser { pub(crate) parser: P, pub(crate) result: TResult, @@ -370,6 +393,17 @@ mod test { }); } + #[test] + fn try_parse_test() { + parser_test_helper(ParserContext::new_with_str("abc", ()), &'a', |c, i| { + char_parser('a').try_parse('a').parse(c, i) + }); + + parser_test_helper(ParserContext::new_with_str("bc", ()), &'a', |c, i| { + char_parser('a').try_parse('a').parse(c, i) + }); + } + #[test] fn reverse_test() { parser_test_helper(ParserContext::new_with_str("abc", ()), &(), |c, i| { diff --git a/src/parser/primitive_parsers.rs b/src/parser/primitive_parsers.rs index e7dcb1e..c071777 100644 --- a/src/parser/primitive_parsers.rs +++ b/src/parser/primitive_parsers.rs @@ -86,6 +86,32 @@ where } } +pub struct SatifiedMapParser { + pub(crate) mapper: F, +} + +impl<'a, TToken, TContext, T: 'a, F> Parser<'a, TToken, T, TContext> for SatifiedMapParser +where + TToken: Debug + Clone, + F: Fn(&TToken) -> Option, +{ + fn parse( + &self, + _: Rc>>, + input: &'a [TToken], + ) -> ParserResult<'a, TToken, T> { + if input.is_empty() { + return Err(FailedParserResult::new_with_str(input, "Input is empty")); + } + + if let Some(result) = (self.mapper)(&input[0]) { + Ok((&input[1..], result)) + } else { + Err(FailedParserResult::new_with_str(input, "Predicate failed.")) + } + } +} + pub struct AnyParser {} impl<'a, TToken, TContext> Parser<'a, TToken, TToken, TContext> for AnyParser @@ -166,8 +192,8 @@ where mod test { use super::*; use crate::parser::{ - any, fail, fail_with_message, failed_parser_test_helper, parser_test_helper, satisfy, skip, - succeed, take, + any, fail, fail_with_message, failed_parser_test_helper, parser_test_helper, satified_map, + satisfy, skip, succeed, take, }; #[test] @@ -202,6 +228,21 @@ mod test { }); } + #[test] + fn satified_map_test() { + parser_test_helper(ParserContext::new_with_str("123", ()), &1, |c, i| { + satified_map(|x: &char| x.to_digit(10)).parse(c, i) + }); + + parser_test_helper(ParserContext::new_with_str("23", ()), &2, |c, i| { + satified_map(|x: &char| x.to_digit(10)).parse(c, i) + }); + + parser_test_helper(ParserContext::new_with_str("3", ()), &3, |c, i| { + satified_map(|x: &char| x.to_digit(10)).parse(c, i) + }); + } + #[test] fn any_test() { parser_test_helper(ParserContext::new_with_str("abc", ()), &'a', |c, i| {