add: parser combinators.

This commit is contained in:
jackfiled 2024-11-17 22:56:47 +08:00
parent 0e9684c32e
commit 8a9c58966b
9 changed files with 1177 additions and 3 deletions

281
src/combinators.rs Normal file
View File

@ -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<TToken, T, TContext>: Parser<TToken, T, TContext>
where
TToken: Debug + Clone,
{
fn left<TRight, PRight>(self, right_parser: PRight) -> LeftParser<Self, PRight, TRight>
where
Self: Sized,
PRight: Parser<TToken, TRight, TContext>,
{
LeftParser {
left_parser: self,
right_parser,
phantom_data: PhantomData,
}
}
fn right<TRight, PRight>(self, right_parser: PRight) -> RightParser<Self, PRight, T>
where
Self: Sized,
PRight: Parser<TToken, TRight, TContext>,
{
RightParser {
left_parser: self,
right_parser,
phantom_data: PhantomData,
}
}
fn alternate<P>(self, second_parser: P) -> AlternateParser<Self, P>
where
Self: Sized,
P: Parser<TToken, T, TContext>,
{
AlternateParser {
first: self,
second: second_parser,
}
}
fn many(self) -> ManyParser<Self>
where
Self: Sized,
{
ManyParser { parser: self }
}
fn many1(self) -> Many1Parser<Self>
where
Self: Sized,
{
Many1Parser { parser: self }
}
fn skip(self) -> SkipParser<Self, T>
where
Self: Sized,
{
SkipParser {
parser: self,
phantom_data: PhantomData,
}
}
fn skip1(self) -> Skip1Parser<Self, T>
where
Self: Sized,
{
Skip1Parser {
parser: self,
phantom_data: PhantomData,
}
}
fn many_till<TTerminator, PTerminator>(
self,
terminator: PTerminator,
) -> ManyTillParser<Self, PTerminator, TTerminator>
where
Self: Sized,
PTerminator: Parser<TToken, TTerminator, TContext>,
{
ManyTillParser {
parser: self,
terminator,
phantom_data: PhantomData,
}
}
fn many1_till<TTerminator, PTerminator>(
self,
terminator: PTerminator,
) -> Many1TillParser<Self, PTerminator, TTerminator>
where
Self: Sized,
PTerminator: Parser<TToken, TTerminator, TContext>,
{
Many1TillParser {
parser: self,
terminator,
phantom_data: PhantomData,
}
}
}
impl<TToken, T, TContext, P> ParserExt<TToken, T, TContext> for P
where
TToken: Debug + Clone,
P: Parser<TToken, T, TContext>,
{
}
fn alternate_helper<TToken, T, TContext, P1, P2>(first: P1, second: P2) -> AlternateParser<P1, P2>
where
TToken: Debug + Clone,
P1: Parser<TToken, T, TContext>,
P2: Parser<TToken, T, TContext>,
{
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<TToken, T, TContext, TList>(
list: TList,
) -> impl Fn(Rc<RefCell<ParserContext<TToken, TContext>>>, &[TToken]) -> ParserResult<TToken, T>
where
TToken: Debug + Clone,
TList: ParserTuple<TToken, T, TContext>,
{
move |context, input| list.parse(context, input)
}
pub fn take_till<TToken, TTerminator, TContext, PTerminator>(
termiantor: PTerminator,
) -> impl Parser<TToken, Vec<TToken>, TContext>
where
TToken: Debug + Clone,
PTerminator: Parser<TToken, TTerminator, TContext>,
{
any().many_till(termiantor)
}
pub fn take1_till<TToken, TTerminator, TContext, PTerminator>(
terminator: PTerminator,
) -> impl Parser<TToken, Vec<TToken>, TContext>
where
TToken: Debug + Clone,
PTerminator: Parser<TToken, TTerminator, TContext>,
{
any().many1_till(terminator)
}
pub fn quote<TToken, T, TLeft, TRight, TContext, PLeft, P, PRight>(
left: PLeft,
parser: P,
right: PRight,
) -> impl Parser<TToken, Vec<T>, TContext>
where
TToken: Debug + Clone,
P: Parser<TToken, T, TContext>,
PLeft: Parser<TToken, TLeft, TContext>,
PRight: Parser<TToken, TRight, TContext>,
{
left.right(parser.many_till(right))
}
pub fn separate_by1<TToken, T, TSeparator, TContext, P, PSeparator>(
parser: P,
separator: PSeparator,
) -> impl Parser<TToken, Vec<T>, TContext>
where
TToken: Debug + Clone,
P: Parser<TToken, T, TContext>,
PSeparator: Parser<TToken, TSeparator, TContext>,
{
SeparateBy1Parser {
parser,
separator,
phantom_data: PhantomData,
}
}
pub fn separate_by<TToken, T, TSeparator, TContext, P, PSeparator>(
parser: P,
separator: PSeparator,
) -> impl Parser<TToken, Vec<T>, TContext>
where
TToken: Debug + Clone,
P: Parser<TToken, T, TContext>,
PSeparator: Parser<TToken, TSeparator, TContext>,
{
separate_by1(parser, separator).next(|r| match r {
Err((input, _)) => Ok((input, vec![])),
r => r,
})
}
pub fn end_by1<TToken, T, TSeparator, TContext, P, PSeparator>(
parser: P,
separator: PSeparator,
) -> impl Parser<TToken, Vec<T>, TContext>
where
TToken: Debug + Clone,
P: Parser<TToken, T, TContext>,
PSeparator: Parser<TToken, TSeparator, TContext>,
{
parser.many1_till(separator)
}
pub fn end_by<TToken, T, TSeparator, TContext, P, PSeparator>(
parser: P,
separator: PSeparator,
) -> impl Parser<TToken, Vec<T>, TContext>
where
TToken: Debug + Clone,
P: Parser<TToken, T, TContext>,
PSeparator: Parser<TToken, TSeparator, TContext>,
{
parser.many_till(separator)
}
pub fn separate_or_end_by1<TToken, T, TSeparator, TContext, P, PSeparator>(
parser: P,
separator: PSeparator,
) -> impl Parser<TToken, Vec<T>, TContext>
where
TToken: Debug + Clone,
P: Parser<TToken, T, TContext>,
PSeparator: Parser<TToken, TSeparator, TContext>,
{
SeparateOrEndBy1Parser {
parser,
separator,
phantom_data: PhantomData,
}
}
pub fn separate_or_end_by<TToken, T, TSeparator, TContext, P, PSeparator>(
parser: P,
separator: PSeparator,
) -> impl Parser<TToken, Vec<T>, TContext>
where
TToken: Debug + Clone,
P: Parser<TToken, T, TContext>,
PSeparator: Parser<TToken, TSeparator, TContext>,
{
separate_or_end_by1(parser, separator).next(|r| match r {
Err((i, _)) => Ok((i, vec![])),
r => r,
})
}

View File

@ -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<P1, P2> {
pub(crate) first: P1,
pub(crate) second: P2,
}
impl<TToken, T, TContext, P1, P2> Parser<TToken, T, TContext> for AlternateParser<P1, P2>
where
TToken: Debug + Clone,
P1: Parser<TToken, T, TContext>,
P2: Parser<TToken, T, TContext>,
{
fn parse<'a>(
&self,
context: Rc<RefCell<ParserContext<TToken, TContext>>>,
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<RefCell<ParserContext<char, ()>>>,
input: &[char],
) -> ParserResult<char, char> {
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<RefCell<ParserContext<char, ()>>>,
input: &[char],
) -> ParserResult<char, char> {
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);
}
}

View File

@ -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<PLeft, PRight, TRight> {
pub(crate) left_parser: PLeft,
pub(crate) right_parser: PRight,
pub(crate) phantom_data: PhantomData<TRight>,
}
impl<TToken, T, TContext, TRight, PLeft, PRight> Parser<TToken, T, TContext>
for LeftParser<PLeft, PRight, TRight>
where
TToken: Debug + Clone,
PLeft: Parser<TToken, T, TContext>,
PRight: Parser<TToken, TRight, TContext>,
{
fn parse<'a>(
&self,
context: Rc<RefCell<ParserContext<TToken, TContext>>>,
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<PLeft, PRight, T> {
pub(crate) left_parser: PLeft,
pub(crate) right_parser: PRight,
pub(crate) phantom_data: PhantomData<T>,
}
impl<TToken, T, TContext, TRight, PLeft, PRight> Parser<TToken, TRight, TContext>
for RightParser<PLeft, PRight, T>
where
TToken: Debug + Clone,
PLeft: Parser<TToken, T, TContext>,
PRight: Parser<TToken, TRight, TContext>,
{
fn parse<'a>(
&self,
context: Rc<RefCell<ParserContext<TToken, TContext>>>,
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')),
)
}
}

View File

@ -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<P> {
pub(crate) parser: P,
}
impl<TToken, T, TContext, P> Parser<TToken, Vec<T>, TContext> for ManyParser<P>
where
TToken: Debug + Clone,
P: Parser<TToken, T, TContext>,
{
fn parse<'a>(
&self,
context: Rc<RefCell<ParserContext<TToken, TContext>>>,
input: &'a [TToken],
) -> ParserResult<'a, TToken, Vec<T>> {
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<P> {
pub(crate) parser: P,
}
impl<TToken, T, TContext, P> Parser<TToken, Vec<T>, TContext> for Many1Parser<P>
where
TToken: Debug + Clone,
P: Parser<TToken, T, TContext>,
{
fn parse<'a>(
&self,
context: Rc<RefCell<ParserContext<TToken, TContext>>>,
input: &'a [TToken],
) -> ParserResult<'a, TToken, Vec<T>> {
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<P, T> {
pub(crate) parser: P,
pub(crate) phantom_data: PhantomData<T>,
}
impl<TToken, T, TContext, P> Parser<TToken, (), TContext> for SkipParser<P, T>
where
TToken: Debug + Clone,
P: Parser<TToken, T, TContext>,
{
fn parse<'a>(
&self,
context: Rc<RefCell<ParserContext<TToken, TContext>>>,
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<P, T> {
pub(crate) parser: P,
pub(crate) phantom_data: PhantomData<T>,
}
impl<TToken, T, TContext, P> Parser<TToken, (), TContext> for Skip1Parser<P, T>
where
TToken: Debug + Clone,
P: Parser<TToken, T, TContext>,
{
fn parse<'a>(
&self,
context: Rc<RefCell<ParserContext<TToken, TContext>>>,
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<P, PTerminator, TTerminator> {
pub(crate) parser: P,
pub(crate) terminator: PTerminator,
pub(crate) phantom_data: PhantomData<TTerminator>,
}
impl<TToken, T, TTerminator, TContext, P, PTerminator> Parser<TToken, Vec<T>, TContext>
for ManyTillParser<P, PTerminator, TTerminator>
where
TToken: Debug + Clone,
P: Parser<TToken, T, TContext>,
PTerminator: Parser<TToken, TTerminator, TContext>,
{
fn parse<'a>(
&self,
context: Rc<RefCell<ParserContext<TToken, TContext>>>,
input: &'a [TToken],
) -> ParserResult<'a, TToken, Vec<T>> {
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<P, PTerminator, TTerminator> {
pub(crate) parser: P,
pub(crate) terminator: PTerminator,
pub(crate) phantom_data: PhantomData<TTerminator>,
}
impl<TToken, T, TTerminator, TContext, P, PTerminator> Parser<TToken, Vec<T>, TContext>
for Many1TillParser<P, PTerminator, TTerminator>
where
TToken: Debug + Clone,
P: Parser<TToken, T, TContext>,
PTerminator: Parser<TToken, TTerminator, TContext>,
{
fn parse<'a>(
&self,
context: Rc<RefCell<ParserContext<TToken, TContext>>>,
input: &'a [TToken],
) -> ParserResult<'a, TToken, Vec<T>> {
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'))),
);
}
}

View File

@ -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<P, PSeparator, TSeparator> {
pub(crate) parser: P,
pub(crate) separator: PSeparator,
pub(crate) phantom_data: PhantomData<TSeparator>,
}
impl<TToken, T, TSeparator, TContext, P, PSeparator> Parser<TToken, Vec<T>, TContext>
for SeparateBy1Parser<P, PSeparator, TSeparator>
where
TToken: Debug + Clone,
P: Parser<TToken, T, TContext>,
PSeparator: Parser<TToken, TSeparator, TContext>,
{
fn parse<'a>(
&self,
context: Rc<RefCell<ParserContext<TToken, TContext>>>,
input: &'a [TToken],
) -> ParserResult<'a, TToken, Vec<T>> {
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<P, PSeparator, TSeparator> {
pub(crate) parser: P,
pub(crate) separator: PSeparator,
pub(crate) phantom_data: PhantomData<TSeparator>,
}
impl<TToken, T, TSeparator, TContext, P, PSeparator> Parser<TToken, Vec<T>, TContext>
for SeparateOrEndBy1Parser<P, PSeparator, TSeparator>
where
TToken: Debug + Clone,
P: Parser<TToken, T, TContext>,
PSeparator: Parser<TToken, TSeparator, TContext>,
{
fn parse<'a>(
&self,
context: Rc<RefCell<ParserContext<TToken, TContext>>>,
input: &'a [TToken],
) -> ParserResult<'a, TToken, Vec<T>> {
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<RefCell<ParserContext<char, ()>>>,
input: &[char],
) -> ParserResult<char, Vec<char>> {
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(',')),
);
}
}

View File

@ -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<TToken, T, TContext>
where
TToken: Debug + Clone,
{
fn parse<'a>(
&self,
context: Rc<RefCell<ParserContext<TToken, TContext>>>,
input: &'a [TToken],
) -> ParserResult<'a, TToken, T>;
}
impl<TToken, T1, T2, TContext, P1, P2> ParserTuple<TToken, (T1, T2), TContext> for (P1, P2)
where
TToken: Debug + Clone,
P1: Parser<TToken, T1, TContext>,
P2: Parser<TToken, T2, TContext>,
{
fn parse<'a>(
&self,
context: Rc<RefCell<ParserContext<TToken, TContext>>>,
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<TToken, T1, T2, T3, TContext, P1, P2, P3> ParserTuple<TToken, (T1, T2, T3), TContext>
for (P1, P2, P3)
where
TToken: Debug + Clone,
P1: Parser<TToken, T1, TContext>,
P2: Parser<TToken, T2, TContext>,
P3: Parser<TToken, T3, TContext>,
{
fn parse<'a>(
&self,
context: Rc<RefCell<ParserContext<TToken, TContext>>>,
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<TToken, T1, T2, T3, T4, TContext, P1, P2, P3, P4>
ParserTuple<TToken, (T1, T2, T3, T4), TContext> for (P1, P2, P3, P4)
where
TToken: Debug + Clone,
P1: Parser<TToken, T1, TContext>,
P2: Parser<TToken, T2, TContext>,
P3: Parser<TToken, T3, TContext>,
P4: Parser<TToken, T4, TContext>,
{
fn parse<'a>(
&self,
context: Rc<RefCell<ParserContext<TToken, TContext>>>,
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<TToken, T1, T2, T3, T4, T5, TContext, P1, P2, P3, P4, P5>
ParserTuple<TToken, (T1, T2, T3, T4, T5), TContext> for (P1, P2, P3, P4, P5)
where
TToken: Debug + Clone,
P1: Parser<TToken, T1, TContext>,
P2: Parser<TToken, T2, TContext>,
P3: Parser<TToken, T3, TContext>,
P4: Parser<TToken, T4, TContext>,
P5: Parser<TToken, T5, TContext>,
{
fn parse<'a>(
&self,
context: Rc<RefCell<ParserContext<TToken, TContext>>>,
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'),
)),
);
}
}

View File

@ -1,2 +1,3 @@
mod parser;
mod text;
mod combinators;

View File

@ -92,7 +92,7 @@ where
fn bind<TResult, F, P>(self, f: F) -> BindParser<Self, F, T>
where
Self: Sized,
F: Fn(&T) -> P,
F: Fn(T) -> P,
P: Parser<TToken, TResult, TContext>,
{
BindParser {
@ -175,6 +175,19 @@ where
}
}
impl<TToken, T, TContext> Parser<TToken, T, TContext> for Box<dyn Parser<TToken, T, TContext>>
where
TToken: Debug + Clone,
{
fn parse<'a>(
&self,
context: Rc<RefCell<ParserContext<TToken, TContext>>>,
input: &'a [TToken],
) -> ParserResult<'a, TToken, T> {
(**self).parse(context, input)
}
}
pub fn succeed<TToken, T, TContext>(value: T) -> impl Parser<TToken, T, TContext>
where
TToken: Debug + Clone,

View File

@ -37,7 +37,7 @@ impl<TToken, T1, T2, TContext, P, P2, F> Parser<TToken, T2, TContext> for BindPa
where
TToken: Debug + Clone,
P: Parser<TToken, T1, TContext>,
F: Fn(&T1) -> P2,
F: Fn(T1) -> P2,
P2: Parser<TToken, T2, TContext>,
{
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)
}
}