commit ffe288ab613fdca129146aca7c58fde0dc3e6f14
Author: jackfiled <xcrenchangjun@outlook.com>
Date:   Fri Nov 15 22:54:54 2024 +0800

    init: repo.

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c403c34
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+/target
+.idea/
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..28954a6
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,7 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "zero-parser"
+version = "0.1.0"
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..2c6120c
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,6 @@
+[package]
+name = "zero-parser"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..10f2d36
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,4 @@
+extern crate core;
+
+mod parser;
+mod text;
diff --git a/src/parser.rs b/src/parser.rs
new file mode 100644
index 0000000..c946190
--- /dev/null
+++ b/src/parser.rs
@@ -0,0 +1,229 @@
+mod modified_parsers;
+mod primitive_parsers;
+
+use crate::parser::modified_parsers::{BindParser, ConvertParser, MapParser, NextParser};
+use crate::parser::primitive_parsers::{
+    AnyParser, FailParser, FailWithMessageParser, SatisfyParser, SkipParser, SucceedParser,
+    TakeParser,
+};
+use std::cell::RefCell;
+use std::fmt::{Debug, Display, Formatter};
+use std::marker::PhantomData;
+use std::rc::Rc;
+
+#[derive(Debug)]
+pub struct FailedParserResult {
+    message: String,
+}
+
+impl FailedParserResult {
+    pub fn new(message: String) -> Self {
+        Self { message }
+    }
+}
+
+impl Display for FailedParserResult {
+    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+        write!(f, "Parse failed: {}.", self.message)
+    }
+}
+
+pub type ParserResult<'a, TToken, T> =
+    Result<(&'a [TToken], T), (&'a [TToken], FailedParserResult)>;
+
+pub struct ParserContext<TToken, TContext>
+where
+    TToken: Debug + Clone,
+{
+    pub context: TContext,
+    input_array: Vec<TToken>,
+}
+
+impl<TToken, TContext> ParserContext<TToken, TContext>
+where
+    TToken: Debug + Clone,
+{
+    pub fn new(input: &[TToken], context: TContext) -> Rc<RefCell<Self>> {
+        Rc::new(RefCell::new(Self {
+            input_array: input.iter().map(|x| x.clone()).collect(),
+            context,
+        }))
+    }
+
+    pub fn input_slice(&self) -> &[TToken] {
+        self.input_array.as_slice()
+    }
+}
+
+impl<TContext> ParserContext<char, TContext> {
+    pub fn new_with_str(input: &str, context: TContext) -> Rc<RefCell<Self>> {
+        Rc::new(RefCell::new(Self {
+            input_array: input.chars().collect(),
+            context,
+        }))
+    }
+}
+
+pub trait 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>;
+
+    fn map<TResult, F>(self, f: F) -> MapParser<Self, F, T>
+    where
+        Self: Sized,
+        F: Fn(&T) -> TResult,
+    {
+        MapParser {
+            parser: self,
+            mapper: f,
+            phantom: PhantomData,
+        }
+    }
+
+    fn bind<TResult, F, P>(self, f: F) -> BindParser<Self, F, T>
+    where
+        Self: Sized,
+        F: Fn(&T) -> P,
+        P: Parser<TToken, TResult, TContext>,
+    {
+        BindParser {
+            parser: self,
+            binder: f,
+            phantom_data: PhantomData,
+        }
+    }
+
+    fn convert<TResult>(self) -> ConvertParser<Self, T, TResult>
+    where
+        Self: Sized,
+    {
+        ConvertParser {
+            parser: self,
+            phantom_data1: PhantomData,
+            phantom_data2: PhantomData,
+        }
+    }
+
+    fn next<TResult, H>(self, handler: H) -> NextParser<Self, H, T>
+    where
+        Self: Sized,
+        H: for<'f> Fn(ParserResult<'f, TToken, T>) -> ParserResult<'f, TToken, TResult>,
+    {
+        NextParser {
+            parser: self,
+            handler,
+            phantom_data: PhantomData,
+        }
+    }
+}
+
+impl<TToken, T, TContext, F> Parser<TToken, T, TContext> for F
+where
+    TToken: Debug + Clone,
+    F: for<'f> Fn(
+        Rc<RefCell<ParserContext<TToken, TContext>>>,
+        &'f [TToken],
+    ) -> ParserResult<'f, TToken, T>,
+{
+    fn parse<'a>(
+        &self,
+        context: Rc<RefCell<ParserContext<TToken, TContext>>>,
+        input: &'a [TToken],
+    ) -> ParserResult<'a, TToken, T> {
+        (*self)(context, input)
+    }
+}
+
+pub fn succeed<TToken, T, TContext>(value: T) -> impl Parser<TToken, T, TContext>
+where
+    TToken: Debug + Clone,
+    T: Debug + Clone,
+{
+    SucceedParser { value }
+}
+
+pub fn fail<TToken, T, TContext>() -> impl Parser<TToken, T, TContext>
+where
+    TToken: Debug + Clone,
+{
+    FailParser {
+        phantom_data: PhantomData,
+    }
+}
+
+pub fn fail_with_message<TToken, T, TContext>(message: &str) -> impl Parser<TToken, T, TContext>
+where
+    TToken: Debug + Clone,
+{
+    FailWithMessageParser {
+        message: message.to_owned(),
+        phantom_data: PhantomData,
+    }
+}
+
+pub fn satisfy<TToken, TContext, TP>(predicate: TP) -> impl Parser<TToken, TToken, TContext>
+where
+    TToken: Debug + Clone,
+    TP: Fn(&TToken) -> bool,
+{
+    SatisfyParser { predicate }
+}
+
+pub fn any<TToken, TContext>() -> impl Parser<TToken, TToken, TContext>
+where
+    TToken: Debug + Clone,
+{
+    AnyParser {}
+}
+
+pub fn take<TToken, TContext>(count: usize) -> impl Parser<TToken, Vec<TToken>, TContext>
+where
+    TToken: Debug + Clone,
+{
+    TakeParser { count }
+}
+
+pub fn skip<TToken, TContext>(count: usize) -> impl Parser<TToken, (), TContext>
+where
+    TToken: Debug + Clone,
+{
+    SkipParser { count }
+}
+
+#[cfg(test)]
+pub fn parser_test_helper<T, P>(
+    context: Rc<RefCell<ParserContext<char, ()>>>,
+    excepted_node: &T,
+    test_parser: P,
+) where
+    T: PartialEq + Debug,
+    P: Parser<char, T, ()>,
+{
+    let borrowed_context = context.borrow();
+    let input = borrowed_context.input_slice();
+    let (_, actual_result) = test_parser.parse(context.clone(), input).unwrap();
+
+    assert_eq!(excepted_node, &actual_result);
+}
+
+#[cfg(test)]
+pub fn failed_parser_test_helper<T, P>(
+    context: Rc<RefCell<ParserContext<char, ()>>>,
+    test_parser: P,
+) where
+    T: Debug,
+    P: Parser<char, T, ()>,
+{
+    let borrowed_context = context.borrow();
+    let input = borrowed_context.input_slice();
+    let result = test_parser.parse(context.clone(), input);
+
+    assert!(result.is_err());
+}
+
diff --git a/src/parser/modified_parsers.rs b/src/parser/modified_parsers.rs
new file mode 100644
index 0000000..b849d06
--- /dev/null
+++ b/src/parser/modified_parsers.rs
@@ -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')),
+        );
+    }
+}
diff --git a/src/parser/primitive_parsers.rs b/src/parser/primitive_parsers.rs
new file mode 100644
index 0000000..82e1442
--- /dev/null
+++ b/src/parser/primitive_parsers.rs
@@ -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));
+    }
+}
diff --git a/src/text.rs b/src/text.rs
new file mode 100644
index 0000000..a6f9b8a
--- /dev/null
+++ b/src/text.rs
@@ -0,0 +1,62 @@
+use crate::parser::{satisfy, take, FailedParserResult, Parser};
+
+pub fn char_parser<TContext>(c: char) -> impl Parser<char, char, TContext> {
+    satisfy(move |x| *x == c)
+}
+
+pub fn string_parser<TContext>(str: String) -> impl Parser<char, String, TContext> {
+    take::<char, TContext>(str.len()).next(move |result| {
+        result.and_then(|(input, chars)| {
+            let chars: String = chars.into_iter().collect();
+
+            if chars == str {
+                Ok((input, chars))
+            } else {
+                Err((
+                    input,
+                    FailedParserResult::new(format!("Failed to parse '{}'.", str)),
+                ))
+            }
+        })
+    })
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use crate::parser::{parser_test_helper, ParserContext, ParserResult};
+    use std::cell::RefCell;
+    use std::rc::Rc;
+
+    #[test]
+    fn string_parser_test() {
+        parser_test_helper(
+            ParserContext::new_with_str("Hello, world!", ()),
+            &"Hello".to_owned(),
+            string_parser("Hello".to_owned()),
+        );
+
+        fn test_parser(
+            context: Rc<RefCell<ParserContext<char, ()>>>,
+            input: &[char],
+        ) -> ParserResult<char, String> {
+            let (input, first) =
+                string_parser("hello, ".to_string()).parse(context.clone(), input)?;
+            let (input, second) =
+                string_parser("world!".to_owned()).parse(context.clone(), input)?;
+
+            Ok((input, first + second.as_str()))
+        }
+
+        parser_test_helper(
+            ParserContext::new_with_str("hello, world!", ()),
+            &"hello, world!".to_owned(),
+            test_parser,
+        );
+        parser_test_helper(
+            ParserContext::new_with_str("hello, world!", ()),
+            &(),
+            test_parser.map(|_| ()),
+        )
+    }
+}