diff --git a/src/combinators.rs b/src/combinators.rs new file mode 100644 index 0000000..e2b5a9a --- /dev/null +++ b/src/combinators.rs @@ -0,0 +1,281 @@ +mod alternate_parser; +mod left_right_parsers; +mod many_parsers; +mod separated_parsers; +mod tuple_parser; + +use crate::combinators::alternate_parser::AlternateParser; +use crate::combinators::left_right_parsers::{LeftParser, RightParser}; +use crate::combinators::many_parsers::{ + Many1Parser, Many1TillParser, ManyParser, ManyTillParser, Skip1Parser, SkipParser, +}; +use crate::combinators::separated_parsers::{SeparateBy1Parser, SeparateOrEndBy1Parser}; +use crate::combinators::tuple_parser::ParserTuple; +use crate::parser::{any, Parser, ParserContext, ParserResult}; +use std::cell::RefCell; +use std::fmt::Debug; +use std::marker::PhantomData; +use std::rc::Rc; + +pub trait ParserExt: Parser +where + TToken: Debug + Clone, +{ + fn left(self, right_parser: PRight) -> LeftParser + where + Self: Sized, + PRight: Parser, + { + LeftParser { + left_parser: self, + right_parser, + phantom_data: PhantomData, + } + } + + fn right(self, right_parser: PRight) -> RightParser + where + Self: Sized, + PRight: Parser, + { + RightParser { + left_parser: self, + right_parser, + phantom_data: PhantomData, + } + } + + fn alternate

(self, second_parser: P) -> AlternateParser + where + Self: Sized, + P: Parser, + { + AlternateParser { + first: self, + second: second_parser, + } + } + + fn many(self) -> ManyParser + where + Self: Sized, + { + ManyParser { parser: self } + } + + fn many1(self) -> Many1Parser + where + Self: Sized, + { + Many1Parser { parser: self } + } + + fn skip(self) -> SkipParser + where + Self: Sized, + { + SkipParser { + parser: self, + phantom_data: PhantomData, + } + } + + fn skip1(self) -> Skip1Parser + where + Self: Sized, + { + Skip1Parser { + parser: self, + phantom_data: PhantomData, + } + } + + fn many_till( + self, + terminator: PTerminator, + ) -> ManyTillParser + where + Self: Sized, + PTerminator: Parser, + { + ManyTillParser { + parser: self, + terminator, + phantom_data: PhantomData, + } + } + + fn many1_till( + self, + terminator: PTerminator, + ) -> Many1TillParser + where + Self: Sized, + PTerminator: Parser, + { + Many1TillParser { + parser: self, + terminator, + phantom_data: PhantomData, + } + } +} + +impl ParserExt for P +where + TToken: Debug + Clone, + P: Parser, +{ +} + +fn alternate_helper(first: P1, second: P2) -> AlternateParser +where + TToken: Debug + Clone, + P1: Parser, + P2: Parser, +{ + AlternateParser { first, second } +} + +#[macro_export] +macro_rules! alternate { + ($first:expr, $( $second:expr ),*) => { + { + let parser = $first; + $( + let parser = crate::combinators::alternate_helper(parser, $second); + )* + parser + } + }; +} + +pub fn tuple( + list: TList, +) -> impl Fn(Rc>>, &[TToken]) -> ParserResult +where + TToken: Debug + Clone, + TList: ParserTuple, +{ + move |context, input| list.parse(context, input) +} + +pub fn take_till( + termiantor: PTerminator, +) -> impl Parser, TContext> +where + TToken: Debug + Clone, + PTerminator: Parser, +{ + any().many_till(termiantor) +} + +pub fn take1_till( + terminator: PTerminator, +) -> impl Parser, TContext> +where + TToken: Debug + Clone, + PTerminator: Parser, +{ + any().many1_till(terminator) +} + +pub fn quote( + left: PLeft, + parser: P, + right: PRight, +) -> impl Parser, TContext> +where + TToken: Debug + Clone, + P: Parser, + PLeft: Parser, + PRight: Parser, +{ + left.right(parser.many_till(right)) +} + +pub fn separate_by1( + parser: P, + separator: PSeparator, +) -> impl Parser, TContext> +where + TToken: Debug + Clone, + P: Parser, + PSeparator: Parser, +{ + SeparateBy1Parser { + parser, + separator, + phantom_data: PhantomData, + } +} + +pub fn separate_by( + parser: P, + separator: PSeparator, +) -> impl Parser, TContext> +where + TToken: Debug + Clone, + P: Parser, + PSeparator: Parser, +{ + separate_by1(parser, separator).next(|r| match r { + Err((input, _)) => Ok((input, vec![])), + r => r, + }) +} + +pub fn end_by1( + parser: P, + separator: PSeparator, +) -> impl Parser, TContext> +where + TToken: Debug + Clone, + P: Parser, + PSeparator: Parser, +{ + parser.many1_till(separator) +} + +pub fn end_by( + parser: P, + separator: PSeparator, +) -> impl Parser, TContext> +where + TToken: Debug + Clone, + P: Parser, + PSeparator: Parser, +{ + parser.many_till(separator) +} + +pub fn separate_or_end_by1( + parser: P, + separator: PSeparator, +) -> impl Parser, TContext> +where + TToken: Debug + Clone, + P: Parser, + PSeparator: Parser, +{ + SeparateOrEndBy1Parser { + parser, + separator, + phantom_data: PhantomData, + } +} + +pub fn separate_or_end_by( + parser: P, + separator: PSeparator, +) -> impl Parser, TContext> +where + TToken: Debug + Clone, + P: Parser, + PSeparator: Parser, +{ + separate_or_end_by1(parser, separator).next(|r| match r { + Err((i, _)) => Ok((i, vec![])), + r => r, + }) +} diff --git a/src/combinators/alternate_parser.rs b/src/combinators/alternate_parser.rs new file mode 100644 index 0000000..eb8ccf7 --- /dev/null +++ b/src/combinators/alternate_parser.rs @@ -0,0 +1,67 @@ +use crate::parser::{Parser, ParserContext, ParserResult}; +use std::cell::RefCell; +use std::fmt::Debug; +use std::rc::Rc; + +pub struct AlternateParser { + pub(crate) first: P1, + pub(crate) second: P2, +} + +impl Parser for AlternateParser +where + TToken: Debug + Clone, + P1: Parser, + P2: Parser, +{ + fn parse<'a>( + &self, + context: Rc>>, + input: &'a [TToken], + ) -> ParserResult<'a, TToken, T> { + match self.first.parse(context.clone(), input) { + Err((input, _)) => self.second.parse(context, input), + r => r, + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::alternate; + use crate::combinators::ParserExt; + use crate::parser::parser_test_helper; + use crate::text::char_parser; + + #[test] + fn alternate_test() { + fn parser( + context: Rc>>, + input: &[char], + ) -> ParserResult { + char_parser('a') + .alternate(char_parser('b')) + .alternate(char_parser('c')) + .parse(context, input) + } + + parser_test_helper(ParserContext::new_with_str("a", ()), &'a', parser); + parser_test_helper(ParserContext::new_with_str("b", ()), &'b', parser); + parser_test_helper(ParserContext::new_with_str("c", ()), &'c', parser); + } + + #[test] + fn alternate_macro_test() { + fn parser( + context: Rc>>, + input: &[char], + ) -> ParserResult { + alternate!(char_parser('a'), char_parser('b'), char_parser('c')).parse(context, input) + } + + parser_test_helper(ParserContext::new_with_str("a", ()), &'a', parser); + parser_test_helper(ParserContext::new_with_str("b", ()), &'b', parser); + parser_test_helper(ParserContext::new_with_str("c", ()), &'c', parser); + } +} diff --git a/src/combinators/left_right_parsers.rs b/src/combinators/left_right_parsers.rs new file mode 100644 index 0000000..a553cad --- /dev/null +++ b/src/combinators/left_right_parsers.rs @@ -0,0 +1,82 @@ +use crate::combinators::ParserExt; +use crate::parser::{Parser, ParserContext, ParserResult}; +use std::cell::RefCell; +use std::fmt::Debug; +use std::marker::PhantomData; +use std::rc::Rc; + +pub struct LeftParser { + pub(crate) left_parser: PLeft, + pub(crate) right_parser: PRight, + pub(crate) phantom_data: PhantomData, +} + +impl Parser + for LeftParser +where + TToken: Debug + Clone, + PLeft: Parser, + PRight: Parser, +{ + fn parse<'a>( + &self, + context: Rc>>, + input: &'a [TToken], + ) -> ParserResult<'a, TToken, T> { + let (input, r) = self.left_parser.parse(context.clone(), input)?; + + match self.right_parser.parse(context, input) { + Ok((input, _)) => Ok((input, r)), + Err(r) => Err(r), + } + } +} + +pub struct RightParser { + pub(crate) left_parser: PLeft, + pub(crate) right_parser: PRight, + pub(crate) phantom_data: PhantomData, +} + +impl Parser + for RightParser +where + TToken: Debug + Clone, + PLeft: Parser, + PRight: Parser, +{ + fn parse<'a>( + &self, + context: Rc>>, + input: &'a [TToken], + ) -> ParserResult<'a, TToken, TRight> { + let (input, _) = self.left_parser.parse(context.clone(), input)?; + + self.right_parser.parse(context, input) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::parser::{parser_test_helper, ParserContext}; + use crate::text::char_parser; + + #[test] + fn left_test() { + parser_test_helper( + ParserContext::new_with_str("abc", ()), + &'a', + char_parser('a').left(char_parser('b')), + ); + } + + #[test] + fn right_test() { + parser_test_helper( + ParserContext::new_with_str("abc", ()), + &'b', + char_parser('a').right(char_parser('b')), + ) + } +} diff --git a/src/combinators/many_parsers.rs b/src/combinators/many_parsers.rs new file mode 100644 index 0000000..6483af1 --- /dev/null +++ b/src/combinators/many_parsers.rs @@ -0,0 +1,338 @@ +use crate::parser::{FailedParserResult, Parser, ParserContext, ParserResult}; +use std::cell::RefCell; +use std::fmt::Debug; +use std::marker::PhantomData; +use std::rc::Rc; + +pub struct ManyParser

{ + pub(crate) parser: P, +} + +impl Parser, TContext> for ManyParser

+where + TToken: Debug + Clone, + P: Parser, +{ + fn parse<'a>( + &self, + context: Rc>>, + input: &'a [TToken], + ) -> ParserResult<'a, TToken, Vec> { + let mut result = vec![]; + let mut input = input; + + while let Ok((i, r)) = self.parser.parse(context.clone(), input) { + result.push(r); + input = i; + } + + Ok((input, result)) + } +} + +pub struct Many1Parser

{ + pub(crate) parser: P, +} + +impl Parser, TContext> for Many1Parser

+where + TToken: Debug + Clone, + P: Parser, +{ + fn parse<'a>( + &self, + context: Rc>>, + input: &'a [TToken], + ) -> ParserResult<'a, TToken, Vec> { + let (mut input, first_result) = self.parser.parse(context.clone(), input)?; + let mut result = vec![first_result]; + + while let Ok((i, r)) = self.parser.parse(context.clone(), input) { + result.push(r); + input = i; + } + + Ok((input, result)) + } +} + +pub struct SkipParser { + pub(crate) parser: P, + pub(crate) phantom_data: PhantomData, +} + +impl Parser for SkipParser +where + TToken: Debug + Clone, + P: Parser, +{ + fn parse<'a>( + &self, + context: Rc>>, + input: &'a [TToken], + ) -> ParserResult<'a, TToken, ()> { + let mut input = input; + + while let Ok((i, _)) = self.parser.parse(context.clone(), input) { + input = i; + } + + Ok((input, ())) + } +} + +pub struct Skip1Parser { + pub(crate) parser: P, + pub(crate) phantom_data: PhantomData, +} + +impl Parser for Skip1Parser +where + TToken: Debug + Clone, + P: Parser, +{ + fn parse<'a>( + &self, + context: Rc>>, + input: &'a [TToken], + ) -> ParserResult<'a, TToken, ()> { + let (mut input, _) = self.parser.parse(context.clone(), input)?; + + while let Ok((i, _)) = self.parser.parse(context.clone(), input) { + input = i; + } + + Ok((input, ())) + } +} + +pub struct ManyTillParser { + pub(crate) parser: P, + pub(crate) terminator: PTerminator, + pub(crate) phantom_data: PhantomData, +} + +impl Parser, TContext> + for ManyTillParser +where + TToken: Debug + Clone, + P: Parser, + PTerminator: Parser, +{ + fn parse<'a>( + &self, + context: Rc>>, + input: &'a [TToken], + ) -> ParserResult<'a, TToken, Vec> { + let mut input = input; + let mut result = vec![]; + + loop { + if self.terminator.parse(context.clone(), input).is_ok() { + return Ok((input, result)); + } + + if let Ok((i, r)) = self.parser.parse(context.clone(), input) { + input = i; + result.push(r); + } else { + return Err(( + input, + FailedParserResult::new("End with other elements.".to_owned()), + )); + } + } + } +} +pub struct Many1TillParser { + pub(crate) parser: P, + pub(crate) terminator: PTerminator, + pub(crate) phantom_data: PhantomData, +} + +impl Parser, TContext> + for Many1TillParser +where + TToken: Debug + Clone, + P: Parser, + PTerminator: Parser, +{ + fn parse<'a>( + &self, + context: Rc>>, + input: &'a [TToken], + ) -> ParserResult<'a, TToken, Vec> { + let (mut input, r) = self.parser.parse(context.clone(), input)?; + let mut result = vec![r]; + + loop { + if self.terminator.parse(context.clone(), input).is_ok() { + return Ok((input, result)); + } + + if let Ok((i, r)) = self.parser.parse(context.clone(), input) { + result.push(r); + input = i; + } else { + return Ok((input, result)); + } + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::combinators::{take1_till, take_till, tuple, ParserExt}; + use crate::parser::{failed_parser_test_helper, parser_test_helper}; + use crate::text::char_parser; + + #[test] + fn many_test() { + parser_test_helper( + ParserContext::new_with_str("aaa", ()), + &vec!['a', 'a', 'a'], + char_parser('a').many(), + ); + + parser_test_helper( + ParserContext::new_with_str("aaabbaa", ()), + &vec!['a', 'a', 'a'], + char_parser('a').many(), + ); + + parser_test_helper( + ParserContext::new_with_str("bbaa", ()), + &vec![], + char_parser('a').many(), + ); + + parser_test_helper( + ParserContext::new_with_str("", ()), + &vec![], + char_parser('a').many(), + ); + } + + #[test] + fn many1_test() { + parser_test_helper( + ParserContext::new_with_str("aaa", ()), + &vec!['a', 'a', 'a'], + char_parser('a').many1(), + ); + + parser_test_helper( + ParserContext::new_with_str("aaabbaa", ()), + &vec!['a', 'a', 'a'], + char_parser('a').many1(), + ); + + failed_parser_test_helper( + ParserContext::new_with_str("bbaa", ()), + char_parser('a').many1(), + ); + + failed_parser_test_helper( + ParserContext::new_with_str("", ()), + char_parser('a').many1(), + ); + } + + #[test] + fn skip_test() { + parser_test_helper( + ParserContext::new_with_str("aaab", ()), + &'b', + char_parser('a').skip().right(char_parser('b')), + ); + + parser_test_helper( + ParserContext::new_with_str("b", ()), + &'b', + char_parser('a').skip().right(char_parser('b')), + ); + } + + #[test] + fn skip1_test() { + parser_test_helper( + ParserContext::new_with_str("aaab", ()), + &'b', + char_parser('a').skip1().right(char_parser('b')), + ); + + failed_parser_test_helper( + ParserContext::new_with_str("b", ()), + char_parser('a').skip1().right(char_parser('b')), + ); + } + + #[test] + fn many_till_test() { + parser_test_helper( + ParserContext::new_with_str("aaab", ()), + &vec!['a', 'a', 'a'], + char_parser('a').many_till(char_parser('b')), + ); + + parser_test_helper( + ParserContext::new_with_str("b", ()), + &vec![], + char_parser('a').many_till(char_parser('b')), + ); + + failed_parser_test_helper( + ParserContext::new_with_str("aaacb", ()), + char_parser('a').many_till(char_parser('b')), + ); + } + + #[test] + fn many1_till_test() { + parser_test_helper( + ParserContext::new_with_str("aaab", ()), + &vec!['a', 'a', 'a'], + char_parser('a').many_till(char_parser('b')), + ); + + failed_parser_test_helper( + ParserContext::new_with_str("b", ()), + char_parser('a').many1_till(char_parser('b')), + ); + + failed_parser_test_helper( + ParserContext::new_with_str("aaacb", ()), + char_parser('a').many_till(char_parser('b')), + ); + } + + #[test] + fn take_till_test() { + parser_test_helper( + ParserContext::new_with_str("aaab", ()), + &(vec!['a', 'a', 'a'], 'b'), + tuple((take_till(char_parser('b')), char_parser('b'))), + ); + + parser_test_helper( + ParserContext::new_with_str("b", ()), + &(vec![], 'b'), + tuple((take_till(char_parser('b')), char_parser('b'))), + ); + } + + #[test] + fn take1_till_test() { + parser_test_helper( + ParserContext::new_with_str("aaab", ()), + &(vec!['a', 'a', 'a'], 'b'), + tuple((take1_till(char_parser('b')), char_parser('b'))), + ); + + failed_parser_test_helper( + ParserContext::new_with_str("b", ()), + tuple((take1_till(char_parser('b')), char_parser('b'))), + ); + } +} diff --git a/src/combinators/separated_parsers.rs b/src/combinators/separated_parsers.rs new file mode 100644 index 0000000..fca1fe4 --- /dev/null +++ b/src/combinators/separated_parsers.rs @@ -0,0 +1,256 @@ +use crate::parser::{Parser, ParserContext, ParserResult}; +use std::cell::RefCell; +use std::fmt::Debug; +use std::marker::PhantomData; +use std::rc::Rc; + +pub struct SeparateBy1Parser { + pub(crate) parser: P, + pub(crate) separator: PSeparator, + pub(crate) phantom_data: PhantomData, +} + +impl Parser, TContext> + for SeparateBy1Parser +where + TToken: Debug + Clone, + P: Parser, + PSeparator: Parser, +{ + fn parse<'a>( + &self, + context: Rc>>, + input: &'a [TToken], + ) -> ParserResult<'a, TToken, Vec> { + let (mut input, first) = self.parser.parse(context.clone(), input)?; + let mut result = vec![first]; + + loop { + if let Ok((i, _)) = self.separator.parse(context.clone(), input) { + let (i, r) = self.parser.parse(context.clone(), i)?; + input = i; + result.push(r); + } else { + break; + } + } + + Ok((input, result)) + } +} + +pub struct SeparateOrEndBy1Parser { + pub(crate) parser: P, + pub(crate) separator: PSeparator, + pub(crate) phantom_data: PhantomData, +} + +impl Parser, TContext> + for SeparateOrEndBy1Parser +where + TToken: Debug + Clone, + P: Parser, + PSeparator: Parser, +{ + fn parse<'a>( + &self, + context: Rc>>, + input: &'a [TToken], + ) -> ParserResult<'a, TToken, Vec> { + let (mut input, first) = self.parser.parse(context.clone(), input)?; + let mut result = vec![first]; + + loop { + if let Ok((i, _)) = self.separator.parse(context.clone(), input) { + if let Ok((i, r)) = self.parser.parse(context.clone(), i) { + result.push(r); + input = i; + } else { + break; + } + } else { + break; + } + } + + Ok((input, result)) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::combinators::{ + end_by, end_by1, quote, separate_by, separate_by1, separate_or_end_by, separate_or_end_by1, + }; + use crate::parser::{failed_parser_test_helper, parser_test_helper, satisfy}; + use crate::text::char_parser; + + #[test] + fn quote_test() { + fn parser( + context: Rc>>, + input: &[char], + ) -> ParserResult> { + quote( + char_parser('\''), + satisfy(|c: &char| c.is_ascii()), + char_parser('\''), + ) + .parse(context, input) + } + + parser_test_helper( + ParserContext::new_with_str("'abc'", ()), + &vec!['a', 'b', 'c'], + parser, + ); + parser_test_helper(ParserContext::new_with_str("''", ()), &vec![], parser); + failed_parser_test_helper(ParserContext::new_with_str("asd", ()), parser) + } + + #[test] + fn separate_by1_test() { + parser_test_helper( + ParserContext::new_with_str("a,b,c", ()), + &vec!['a', 'b', 'c'], + separate_by1(satisfy(|c: &char| c.is_ascii()), char_parser(',')), + ); + parser_test_helper( + ParserContext::new_with_str("a", ()), + &vec!['a'], + separate_by1(satisfy(|c: &char| c.is_ascii()), char_parser(',')), + ); + parser_test_helper( + ParserContext::new_with_str("bbc", ()), + &vec!['b'], + separate_by1(satisfy(|c: &char| c.is_ascii()), char_parser(',')), + ); + } + + #[test] + fn separate_by_test() { + parser_test_helper( + ParserContext::new_with_str("1,2,3", ()), + &vec!['1', '2', '3'], + separate_by(satisfy(|c: &char| c.is_ascii()), char_parser(',')), + ); + parser_test_helper( + ParserContext::new_with_str("1", ()), + &vec!['1'], + separate_by(satisfy(|c: &char| c.is_ascii()), char_parser(',')), + ); + parser_test_helper( + ParserContext::new_with_str("", ()), + &vec![], + separate_by(satisfy(|c: &char| c.is_ascii()), char_parser(',')), + ); + parser_test_helper( + ParserContext::new_with_str("abc", ()), + &vec!['a'], + separate_by(satisfy(|c: &char| c.is_ascii()), char_parser(',')), + ); + } + + #[test] + fn end_by1_test() { + parser_test_helper( + ParserContext::new_with_str("aab", ()), + &vec!['a', 'a'], + end_by1(char_parser('a'), char_parser('b')), + ); + parser_test_helper( + ParserContext::new_with_str("ab", ()), + &vec!['a'], + end_by1(char_parser('a'), char_parser('b')), + ); + failed_parser_test_helper( + ParserContext::new_with_str("b", ()), + end_by1(char_parser('a'), char_parser('b')), + ); + failed_parser_test_helper( + ParserContext::new_with_str("cd", ()), + end_by1(char_parser('a'), char_parser('b')), + ); + } + + #[test] + fn end_by_test() { + parser_test_helper( + ParserContext::new_with_str("aab", ()), + &vec!['a', 'a'], + end_by(char_parser('a'), char_parser('b')), + ); + parser_test_helper( + ParserContext::new_with_str("ab", ()), + &vec!['a'], + end_by(char_parser('a'), char_parser('b')), + ); + parser_test_helper( + ParserContext::new_with_str("b", ()), + &vec![], + end_by(char_parser('a'), char_parser('b')), + ); + failed_parser_test_helper( + ParserContext::new_with_str("cd", ()), + end_by(char_parser('a'), char_parser('b')), + ); + } + + #[test] + fn separate_or_end_by1_test() { + parser_test_helper( + ParserContext::new_with_str("1,2,3,", ()), + &vec!['1', '2', '3'], + separate_or_end_by1(satisfy(|c: &char| c.is_numeric()), char_parser(',')), + ); + parser_test_helper( + ParserContext::new_with_str("1,", ()), + &vec!['1'], + separate_or_end_by1(satisfy(|c: &char| c.is_numeric()), char_parser(',')), + ); + parser_test_helper( + ParserContext::new_with_str("1,2,3", ()), + &vec!['1', '2', '3'], + separate_or_end_by1(satisfy(|c: &char| c.is_numeric()), char_parser(',')), + ); + parser_test_helper( + ParserContext::new_with_str("1", ()), + &vec!['1'], + separate_or_end_by1(satisfy(|c: &char| c.is_numeric()), char_parser(',')), + ); + failed_parser_test_helper( + ParserContext::new_with_str("abc", ()), + separate_or_end_by1(satisfy(|c: &char| c.is_numeric()), char_parser(',')), + ); + } + + #[test] + fn separate_or_end_by_test() { + parser_test_helper( + ParserContext::new_with_str("1,2,3,", ()), + &vec!['1', '2', '3'], + separate_or_end_by(satisfy(|c: &char| c.is_numeric()), char_parser(',')), + ); + parser_test_helper( + ParserContext::new_with_str("1,", ()), + &vec!['1'], + separate_or_end_by(satisfy(|c: &char| c.is_numeric()), char_parser(',')), + ); + parser_test_helper( + ParserContext::new_with_str("1,2,3", ()), + &vec!['1', '2', '3'], + separate_or_end_by(satisfy(|c: &char| c.is_numeric()), char_parser(',')), + ); + parser_test_helper( + ParserContext::new_with_str("1", ()), + &vec!['1'], + separate_or_end_by(satisfy(|c: &char| c.is_numeric()), char_parser(',')), + ); + parser_test_helper( + ParserContext::new_with_str("abc", ()), + &vec![], + separate_or_end_by(satisfy(|c: &char| c.is_numeric()), char_parser(',')), + ); + } +} diff --git a/src/combinators/tuple_parser.rs b/src/combinators/tuple_parser.rs new file mode 100644 index 0000000..85ac91b --- /dev/null +++ b/src/combinators/tuple_parser.rs @@ -0,0 +1,136 @@ +use crate::parser::{Parser, ParserContext, ParserResult}; +use std::cell::RefCell; +use std::fmt::Debug; +use std::rc::Rc; + +pub trait ParserTuple +where + TToken: Debug + Clone, +{ + fn parse<'a>( + &self, + context: Rc>>, + input: &'a [TToken], + ) -> ParserResult<'a, TToken, T>; +} + +impl ParserTuple for (P1, P2) +where + TToken: Debug + Clone, + P1: Parser, + P2: Parser, +{ + fn parse<'a>( + &self, + context: Rc>>, + input: &'a [TToken], + ) -> ParserResult<'a, TToken, (T1, T2)> { + let (input, r1) = self.0.parse(context.clone(), input)?; + let (input, r2) = self.1.parse(context, input)?; + + Ok((input, (r1, r2))) + } +} + +impl ParserTuple + for (P1, P2, P3) +where + TToken: Debug + Clone, + P1: Parser, + P2: Parser, + P3: Parser, +{ + fn parse<'a>( + &self, + context: Rc>>, + input: &'a [TToken], + ) -> ParserResult<'a, TToken, (T1, T2, T3)> { + let (input, r1) = self.0.parse(context.clone(), input)?; + let (input, r2) = self.1.parse(context.clone(), input)?; + let (input, r3) = self.2.parse(context.clone(), input)?; + + Ok((input, (r1, r2, r3))) + } +} + +impl + ParserTuple for (P1, P2, P3, P4) +where + TToken: Debug + Clone, + P1: Parser, + P2: Parser, + P3: Parser, + P4: Parser, +{ + fn parse<'a>( + &self, + context: Rc>>, + input: &'a [TToken], + ) -> ParserResult<'a, TToken, (T1, T2, T3, T4)> { + let (input, r1) = self.0.parse(context.clone(), input)?; + let (input, r2) = self.1.parse(context.clone(), input)?; + let (input, r3) = self.2.parse(context.clone(), input)?; + let (input, r4) = self.3.parse(context.clone(), input)?; + + Ok((input, (r1, r2, r3, r4))) + } +} + +impl + ParserTuple for (P1, P2, P3, P4, P5) +where + TToken: Debug + Clone, + P1: Parser, + P2: Parser, + P3: Parser, + P4: Parser, + P5: Parser, +{ + fn parse<'a>( + &self, + context: Rc>>, + input: &'a [TToken], + ) -> ParserResult<'a, TToken, (T1, T2, T3, T4, T5)> { + let (input, r1) = self.0.parse(context.clone(), input)?; + let (input, r2) = self.1.parse(context.clone(), input)?; + let (input, r3) = self.2.parse(context.clone(), input)?; + let (input, r4) = self.3.parse(context.clone(), input)?; + let (input, r5) = self.4.parse(context.clone(), input)?; + + Ok((input, (r1, r2, r3, r4, r5))) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::combinators::tuple; + use crate::parser::parser_test_helper; + use crate::text::char_parser; + + #[test] + fn tuple_test() { + parser_test_helper( + ParserContext::new_with_str("ab", ()), + &('a', 'b'), + tuple((char_parser('a'), char_parser('b'))), + ); + + parser_test_helper( + ParserContext::new_with_str("abc", ()), + &('a', 'b', 'c'), + tuple((char_parser('a'), char_parser('b'), char_parser('c'))), + ); + + parser_test_helper( + ParserContext::new_with_str("abcd", ()), + &('a', 'b', 'c', 'd'), + tuple(( + char_parser('a'), + char_parser('b'), + char_parser('c'), + char_parser('d'), + )), + ); + } +} diff --git a/src/lib.rs b/src/lib.rs index 3b7a478..ef8357e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,2 +1,3 @@ mod parser; mod text; +mod combinators; diff --git a/src/parser.rs b/src/parser.rs index 76b2278..5726da5 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -92,7 +92,7 @@ where fn bind(self, f: F) -> BindParser where Self: Sized, - F: Fn(&T) -> P, + F: Fn(T) -> P, P: Parser, { BindParser { @@ -175,6 +175,19 @@ where } } +impl Parser for Box> +where + TToken: Debug + Clone, +{ + fn parse<'a>( + &self, + context: Rc>>, + input: &'a [TToken], + ) -> ParserResult<'a, TToken, T> { + (**self).parse(context, input) + } +} + pub fn succeed(value: T) -> impl Parser where TToken: Debug + Clone, diff --git a/src/parser/modified_parsers.rs b/src/parser/modified_parsers.rs index eb4a36a..59fdc52 100644 --- a/src/parser/modified_parsers.rs +++ b/src/parser/modified_parsers.rs @@ -37,7 +37,7 @@ impl Parser for BindPa where TToken: Debug + Clone, P: Parser, - F: Fn(&T1) -> P2, + F: Fn(T1) -> P2, P2: Parser, { fn parse<'a>( @@ -47,7 +47,7 @@ where ) -> ParserResult<'a, TToken, T2> { let (input, middle) = self.parser.parse(context.clone(), input)?; - (self.binder)(&middle).parse(context, input) + (self.binder)(middle).parse(context, input) } }