init: repo.
This commit is contained in:
		
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
				
			|||||||
 | 
					/target
 | 
				
			||||||
 | 
					.idea/
 | 
				
			||||||
							
								
								
									
										7
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							@@ -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"
 | 
				
			||||||
							
								
								
									
										6
									
								
								Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								Cargo.toml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					[package]
 | 
				
			||||||
 | 
					name = "zero-parser"
 | 
				
			||||||
 | 
					version = "0.1.0"
 | 
				
			||||||
 | 
					edition = "2021"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[dependencies]
 | 
				
			||||||
							
								
								
									
										4
									
								
								src/lib.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								src/lib.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					extern crate core;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mod parser;
 | 
				
			||||||
 | 
					mod text;
 | 
				
			||||||
							
								
								
									
										229
									
								
								src/parser.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										229
									
								
								src/parser.rs
									
									
									
									
									
										Normal file
									
								
							@@ -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());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										131
									
								
								src/parser/modified_parsers.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								src/parser/modified_parsers.rs
									
									
									
									
									
										Normal 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')),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										236
									
								
								src/parser/primitive_parsers.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										236
									
								
								src/parser/primitive_parsers.rs
									
									
									
									
									
										Normal 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));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										62
									
								
								src/text.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								src/text.rs
									
									
									
									
									
										Normal file
									
								
							@@ -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(|_| ()),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user