diff --git a/src/parser.rs b/src/parser.rs index 5883fb8..5adc5cf 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -2,8 +2,8 @@ mod modified_parsers; mod primitive_parsers; use crate::parser::modified_parsers::{ - BindParser, ConvertParser, LitervalCountParser, LitervalParser, LookAheadParser, MapParser, - NextParser, OptionalParser, ReverseParser, TryParser, + AndThenParser, BindParser, ConvertParser, LitervalCountParser, LitervalParser, LookAheadParser, + MapParser, NextParser, OptionalParser, ReverseParser, RunParser, TryParser, }; use crate::parser::primitive_parsers::{ AnyParser, FailParser, FailWithMessageParser, SatifiedMapParser, SatisfyParser, SkipParser, @@ -33,6 +33,13 @@ impl<'a, TToken: Debug> FailedParserResult<'a, TToken> { } } + pub fn new_with_error(input: &'a [TToken], err: anyhow::Error) -> Self { + Self { + input, + message: format!("{}", err), + } + } + pub fn message(&self) -> &str { self.message.as_str() } @@ -207,6 +214,29 @@ where { OptionalParser { parser: self } } + + fn run(self, action: F) -> RunParser + where + Self: Sized, + F: Fn(&T) -> (), + { + RunParser { + parser: self, + action, + } + } + + fn and_then(self, mapper: F) -> AndThenParser + where + Self: Sized, + F: Fn(T) -> anyhow::Result, + { + AndThenParser { + parser: self, + mapper, + phantom_data: PhantomData, + } + } } impl<'a, TToken, T: 'a, TContext, F> Parser<'a, TToken, T, TContext> for F diff --git a/src/parser/modified_parsers.rs b/src/parser/modified_parsers.rs index 545d862..2d36bde 100644 --- a/src/parser/modified_parsers.rs +++ b/src/parser/modified_parsers.rs @@ -261,13 +261,68 @@ where } } +pub struct RunParser { + pub(crate) parser: P, + pub(crate) action: F, +} + +impl<'a, TToken, T: 'a, TContext, P, F> Parser<'a, TToken, T, TContext> for RunParser +where + TToken: Debug + Clone, + P: Parser<'a, TToken, T, TContext>, + F: Fn(&T) -> (), +{ + fn parse( + &self, + context: Rc>>, + input: &'a [TToken], + ) -> ParserResult<'a, TToken, T> { + match self.parser.parse(context, input) { + Ok(r) => { + (self.action)(&r.1); + + Ok(r) + } + r => r, + } + } +} + +pub struct AndThenParser { + pub(crate) parser: P, + pub(crate) mapper: F, + pub(crate) phantom_data: PhantomData, +} + +impl<'a, TToken, T: 'a, TResult: 'a, TContext, P, F> Parser<'a, TToken, TResult, TContext> + for AndThenParser +where + TToken: Debug + Clone, + P: Parser<'a, TToken, T, TContext>, + F: Fn(T) -> anyhow::Result, +{ + fn parse( + &self, + context: Rc>>, + input: &'a [TToken], + ) -> ParserResult<'a, TToken, TResult> { + match self.parser.parse(context, input) { + Ok((i, r)) => match (self.mapper)(r) { + Ok(result) => Ok((i, result)), + Err(err) => Err(FailedParserResult::new_with_error(i, err)), + }, + Err(r) => Err(r), + } + } +} + #[cfg(test)] mod test { use super::*; use crate::parser::{ failed_parser_test_helper, parser_test_helper, satisfy, take, FailedParserResult, }; - use crate::text::char_parser; + use crate::text::{char_parser, char_satisfy}; #[test] fn map_test() { @@ -447,4 +502,23 @@ mod test { char_parser('a').optional().parse(c, i) }); } + + #[test] + fn run_test() { + parser_test_helper(ParserContext::new_with_str("abc", ()), &'a', |c, i| { + char_parser('a').run(|c| assert_eq!(c, &'a')).parse(c, i) + }) + } + + #[test] + fn and_then_test() { + parser_test_helper(ParserContext::new_with_str("123", ()), &1, |c, i| { + char_satisfy(char::is_ascii_digit) + .and_then(|c| { + let word = c.to_string(); + Ok(i32::from_str_radix(word.as_str(), 10)?) + }) + .parse(c, i) + }); + } }