init: repo.

This commit is contained in:
2024-11-15 22:54:54 +08:00
commit ffe288ab61
8 changed files with 677 additions and 0 deletions

View File

@@ -0,0 +1,131 @@
use crate::parser::{Parser, ParserContext, ParserResult};
use std::cell::RefCell;
use std::fmt::Debug;
use std::marker::PhantomData;
use std::rc::Rc;
pub struct MapParser<P, F, T1> {
pub(crate) parser: P,
pub(crate) mapper: F,
pub(crate) phantom: PhantomData<T1>,
}
impl<TToken, T1, T2, TContext, P, F> Parser<TToken, T2, TContext> for MapParser<P, F, T1>
where
TToken: Debug + Clone,
P: Parser<TToken, T1, TContext>,
F: Fn(&T1) -> T2,
{
fn parse<'a>(
&self,
context: Rc<RefCell<ParserContext<TToken, TContext>>>,
input: &'a [TToken],
) -> ParserResult<'a, TToken, T2> {
self.parser
.parse(context, input)
.map(|(remainder, result)| (remainder, (self.mapper)(&result)))
}
}
pub struct BindParser<P, F, T1> {
pub(crate) parser: P,
pub(crate) binder: F,
pub(crate) phantom_data: PhantomData<T1>,
}
impl<TToken, T1, T2, TContext, P, P2, F> Parser<TToken, T2, TContext> for BindParser<P, F, T1>
where
TToken: Debug + Clone,
P: Parser<TToken, T1, TContext>,
F: Fn(&T1) -> P2,
P2: Parser<TToken, T2, TContext>,
{
fn parse<'a>(
&self,
context: Rc<RefCell<ParserContext<TToken, TContext>>>,
input: &'a [TToken],
) -> ParserResult<'a, TToken, T2> {
let (input, middle) = self.parser.parse(context.clone(), input)?;
(self.binder)(&middle).parse(context, input)
}
}
pub struct ConvertParser<P, T1, T2> {
pub(crate) parser: P,
pub(crate) phantom_data1: PhantomData<T1>,
pub(crate) phantom_data2: PhantomData<T2>,
}
impl<TToken, T1, T2, TContext, P> Parser<TToken, T2, TContext> for ConvertParser<P, T1, T2>
where
TToken: Debug + Clone,
T2: From<T1>,
P: Parser<TToken, T1, TContext>,
{
fn parse<'a>(
&self,
context: Rc<RefCell<ParserContext<TToken, TContext>>>,
input: &'a [TToken],
) -> ParserResult<'a, TToken, T2> {
self.parser
.parse(context, input)
.map(|(input, result)| (input, result.into()))
}
}
pub struct NextParser<P, H, T> {
pub(crate) parser: P,
pub(crate) handler: H,
pub(crate) phantom_data: PhantomData<T>,
}
impl<TToken, T, TResult, TContext, P, H> Parser<TToken, TResult, TContext> for NextParser<P, H, T>
where
TToken: Debug + Clone,
P: Parser<TToken, T, TContext>,
H: for<'f> Fn(ParserResult<'f, TToken, T>) -> ParserResult<'f, TToken, TResult>,
{
fn parse<'a>(
&self,
context: Rc<RefCell<ParserContext<TToken, TContext>>>,
input: &'a [TToken],
) -> ParserResult<'a, TToken, TResult> {
let result = self.parser.parse(context, input);
(self.handler)(result)
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::parser::{parser_test_helper, take};
use crate::text::char_parser;
#[test]
fn map_test() {
parser_test_helper(
ParserContext::new_with_str("hello, world!", ()),
&(),
take(5).map(|_| ()),
)
}
#[test]
fn bind_test() {
parser_test_helper(
ParserContext::new_with_str("abc", ()),
&'b',
char_parser('a').bind(|_| char_parser('b')),
);
parser_test_helper(
ParserContext::new_with_str("abc", ()),
&'c',
char_parser('a')
.bind(|_| char_parser('b'))
.bind(|_| char_parser('c')),
);
}
}

View File

@@ -0,0 +1,236 @@
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 SucceedParser<T> {
pub(crate) value: T,
}
impl<TToken, T, TContext> Parser<TToken, T, TContext> for SucceedParser<T>
where
TToken: Debug + Clone,
T: Debug + Clone,
{
fn parse<'a>(
&self,
_: Rc<RefCell<ParserContext<TToken, TContext>>>,
input: &'a [TToken],
) -> ParserResult<'a, TToken, T> {
Ok((input, self.value.clone()))
}
}
pub struct FailParser<T> {
pub(crate) phantom_data: PhantomData<T>,
}
impl<TToken, T, TContext> Parser<TToken, T, TContext> for FailParser<T>
where
TToken: Debug + Clone,
{
fn parse<'a>(
&self,
_: Rc<RefCell<ParserContext<TToken, TContext>>>,
input: &'a [TToken],
) -> ParserResult<'a, TToken, T> {
Err((
input,
FailedParserResult::new("Default failed parser.".to_owned()),
))
}
}
pub struct FailWithMessageParser<T> {
pub(crate) message: String,
pub(crate) phantom_data: PhantomData<T>,
}
impl<TToken, T, TContext> Parser<TToken, T, TContext> for FailWithMessageParser<T>
where
TToken: Debug + Clone,
{
fn parse<'a>(
&self,
_: Rc<RefCell<ParserContext<TToken, TContext>>>,
input: &'a [TToken],
) -> ParserResult<'a, TToken, T> {
Err((input, FailedParserResult::new(self.message.clone())))
}
}
pub struct SatisfyParser<F> {
pub(crate) predicate: F,
}
impl<TToken, TContext, F> Parser<TToken, TToken, TContext> for SatisfyParser<F>
where
TToken: Debug + Clone,
F: Fn(&TToken) -> bool,
{
fn parse<'a>(
&self,
_: Rc<RefCell<ParserContext<TToken, TContext>>>,
input: &'a [TToken],
) -> ParserResult<'a, TToken, TToken> {
if input.is_empty() {
return Err((input, FailedParserResult::new("Input is empty.".to_owned())));
}
if (self.predicate)(&input[0]) {
Ok((&input[1..], input[0].clone()))
} else {
Err((
input,
FailedParserResult::new("Predicate failed.".to_owned()),
))
}
}
}
pub struct AnyParser {}
impl<TToken, TContext> Parser<TToken, TToken, TContext> for AnyParser
where
TToken: Debug + Clone,
{
fn parse<'a>(
&self,
_: Rc<RefCell<ParserContext<TToken, TContext>>>,
input: &'a [TToken],
) -> ParserResult<'a, TToken, TToken> {
if input.is_empty() {
Err((input, FailedParserResult::new("Input is empty.".to_owned())))
} else {
Ok((&input[1..], input[0].clone()))
}
}
}
pub struct TakeParser {
pub(crate) count: usize,
}
impl<TToken, TContext> Parser<TToken, Vec<TToken>, TContext> for TakeParser
where
TToken: Debug + Clone,
{
fn parse<'a>(
&self,
_: Rc<RefCell<ParserContext<TToken, TContext>>>,
input: &'a [TToken],
) -> ParserResult<'a, TToken, Vec<TToken>> {
if input.len() < self.count {
Err((
input,
FailedParserResult::new(format!(
"The input doesn't contain enough {} elemements.",
self.count
)),
))
} else {
Ok((
&input[self.count..],
(&input[..self.count]).iter().map(|x| x.clone()).collect(),
))
}
}
}
pub struct SkipParser {
pub(crate) count: usize,
}
impl<TToken, TContext> Parser<TToken, (), TContext> for SkipParser
where
TToken: Debug + Clone,
{
fn parse<'a>(
&self,
_: Rc<RefCell<ParserContext<TToken, TContext>>>,
input: &'a [TToken],
) -> ParserResult<'a, TToken, ()> {
if input.len() < self.count {
Err((
input,
FailedParserResult::new(format!(
"The input doesn't contain enough {} elemements.",
self.count
)),
))
} else {
Ok((&input[self.count..], ()))
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::parser::{
any, fail, fail_with_message, failed_parser_test_helper, parser_test_helper, satisfy, skip,
succeed, take,
};
#[test]
fn succeed_test() {
parser_test_helper(ParserContext::new_with_str("abc", ()), &(), succeed(()));
}
#[test]
fn fail_test() {
failed_parser_test_helper::<(), _>(ParserContext::new_with_str("abc", ()), fail());
}
#[test]
fn fail_with_message_test() {
failed_parser_test_helper::<(), _>(
ParserContext::new_with_str("abc", ()),
fail_with_message("Failed!"),
);
}
#[test]
fn satisfy_test() {
parser_test_helper(
ParserContext::new_with_str("abc", ()),
&'a',
satisfy(|x| x == &'a'),
);
failed_parser_test_helper(
ParserContext::new_with_str("abc", ()),
satisfy(|x| x == &'b'),
);
}
#[test]
fn any_test() {
parser_test_helper(ParserContext::new_with_str("abc", ()), &'a', any());
parser_test_helper(ParserContext::new_with_str("cde", ()), &'c', any());
}
#[test]
fn take_test() {
parser_test_helper(
ParserContext::new_with_str("hello, world!", ()),
&("hello".chars().collect()),
take(5),
);
failed_parser_test_helper(ParserContext::new_with_str("abcd", ()), take(5));
}
#[test]
fn skip_test() {
parser_test_helper(
ParserContext::new_with_str("hello, world!", ()),
&(),
skip(5),
);
failed_parser_test_helper(ParserContext::new_with_str("abcd", ()), skip(5));
}
}