diff --git a/src/lib.rs b/src/lib.rs index 10f2d36..3b7a478 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,2 @@ -extern crate core; - mod parser; mod text; diff --git a/src/parser.rs b/src/parser.rs index c946190..76b2278 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,7 +1,10 @@ mod modified_parsers; mod primitive_parsers; -use crate::parser::modified_parsers::{BindParser, ConvertParser, MapParser, NextParser}; +use crate::parser::modified_parsers::{ + BindParser, ConvertParser, LitervalParser, LookAheadParser, MapParser, NextParser, + ReverseParser, +}; use crate::parser::primitive_parsers::{ AnyParser, FailParser, FailWithMessageParser, SatisfyParser, SkipParser, SucceedParser, TakeParser, @@ -121,6 +124,38 @@ where phantom_data: PhantomData, } } + + fn literal(self) -> LitervalParser + where + Self: Sized, + { + LitervalParser { + parser: self, + phantom_data: PhantomData, + } + } + + fn look_ahead(self) -> LookAheadParser + where + Self: Sized, + { + LookAheadParser { + parser: self, + phantom_data: PhantomData, + } + } + + fn reverse(self, result: TResult) -> ReverseParser + where + Self: Sized, + TResult: Debug + Clone, + { + ReverseParser { + parser: self, + result, + phantom_data: PhantomData, + } + } } impl Parser for F @@ -226,4 +261,3 @@ pub fn failed_parser_test_helper( assert!(result.is_err()); } - diff --git a/src/parser/modified_parsers.rs b/src/parser/modified_parsers.rs index b849d06..eb4a36a 100644 --- a/src/parser/modified_parsers.rs +++ b/src/parser/modified_parsers.rs @@ -1,4 +1,4 @@ -use crate::parser::{Parser, ParserContext, ParserResult}; +use crate::parser::{FailedParserResult, Parser, ParserContext, ParserResult}; use std::cell::RefCell; use std::fmt::Debug; use std::marker::PhantomData; @@ -97,10 +97,102 @@ where } } +pub struct LitervalParser { + pub(crate) parser: P, + pub(crate) phantom_data: PhantomData, +} + +impl Parser, TContext> for LitervalParser +where + TToken: Debug + Clone, + P: Parser, +{ + fn parse<'a>( + &self, + context: Rc>>, + input: &'a [TToken], + ) -> ParserResult<'a, TToken, Vec> { + let origin_input = input; + let (input, _) = self.parser.parse(context, input)?; + + if origin_input.is_empty() { + return Err(( + input, + FailedParserResult::new( + "Empty input, the 'literal' parser cann't get length of elememtn.".to_owned(), + ), + )); + } + + let length = (&origin_input[1..]).as_ptr() as usize - origin_input.as_ptr() as usize; + let index = (input.as_ptr() as usize - origin_input.as_ptr() as usize) / length; + + Ok(( + input, + (&origin_input[..index]) + .into_iter() + .map(|x| x.clone()) + .collect(), + )) + } +} + +pub struct LookAheadParser { + pub(crate) parser: P, + pub(crate) phantom_data: PhantomData, +} + +impl Parser for LookAheadParser +where + TToken: Debug + Clone, + P: Parser, +{ + fn parse<'a>( + &self, + context: Rc>>, + input: &'a [TToken], + ) -> ParserResult<'a, TToken, T> { + let original_input = input; + let (_, r) = self.parser.parse(context, input)?; + + Ok((original_input, r)) + } +} + +pub struct ReverseParser { + pub(crate) parser: P, + pub(crate) result: TResult, + pub(crate) phantom_data: PhantomData, +} + +impl Parser + for ReverseParser +where + TToken: Debug + Clone, + TResult: Debug + Clone, + P: Parser, +{ + fn parse<'a>( + &self, + context: Rc>>, + input: &'a [TToken], + ) -> ParserResult<'a, TToken, TResult> { + match self.parser.parse(context, input) { + Ok((input, _)) => Err(( + input, + FailedParserResult::new("Reverse failed succeeded.".to_owned()), + )), + Err((input, _)) => Ok((input, self.result.clone())), + } + } +} + #[cfg(test)] mod test { use super::*; - use crate::parser::{parser_test_helper, take}; + use crate::parser::{ + failed_parser_test_helper, parser_test_helper, satisfy, take, FailedParserResult, + }; use crate::text::char_parser; #[test] @@ -128,4 +220,121 @@ mod test { .bind(|_| char_parser('c')), ); } + + struct Number(i32); + + impl From for String { + fn from(value: Number) -> Self { + format!("{}", value.0) + } + } + + #[test] + fn convert_test() { + fn single_numer_parser( + context: Rc>>, + input: &[char], + ) -> ParserResult { + let (input, result) = satisfy(|c: &char| c.is_numeric()).parse(context, input)?; + + match result.to_digit(10) { + None => Err((input, FailedParserResult::new("What?".to_string()))), + Some(r) => Ok((input, Number(r as i32))), + } + } + + parser_test_helper( + ParserContext::new_with_str("9", ()), + &"9".to_owned(), + single_numer_parser.convert(), + ); + + parser_test_helper( + ParserContext::new_with_str("1", ()), + &"1".to_owned(), + single_numer_parser.convert(), + ); + + failed_parser_test_helper( + ParserContext::new_with_str("abc", ()), + single_numer_parser.convert::(), + ); + } + + #[test] + fn next_test() { + let parser = || { + satisfy(|c: &char| c.is_numeric()).next(|r| match r { + Ok((input, result)) => match result.to_digit(10) { + None => Err((input, FailedParserResult::new("What?".to_string()))), + Some(r) => Ok((input, Number(r as i32))), + }, + Err(r) => Err(r), + }) + }; + + parser_test_helper( + ParserContext::new_with_str("9", ()), + &"9".to_owned(), + parser().convert(), + ); + + parser_test_helper( + ParserContext::new_with_str("1", ()), + &"1".to_owned(), + parser().convert(), + ); + + failed_parser_test_helper( + ParserContext::new_with_str("abc", ()), + parser().convert::(), + ); + } + + #[test] + fn literal_test() { + parser_test_helper( + ParserContext::new_with_str("abc", ()), + &vec!['a'], + char_parser('a').literal(), + ); + + parser_test_helper( + ParserContext::new_with_str("abc", ()), + &vec!['a', 'b'], + char_parser('a').bind(|_| char_parser('b')).literal(), + ); + + parser_test_helper( + ParserContext::new_with_str("abc", ()), + &vec!['a', 'b', 'c'], + char_parser('a') + .bind(|_| char_parser('b')) + .bind(|_| char_parser('c')) + .literal(), + ); + } + + #[test] + fn look_ahead_test() { + parser_test_helper( + ParserContext::new_with_str("abc", ()), + &'a', + char_parser('a').look_ahead().bind(|_| char_parser('a')), + ); + } + + #[test] + fn reverse_test() { + parser_test_helper( + ParserContext::new_with_str("abc", ()), + &(), + char_parser('b').reverse(()), + ); + + failed_parser_test_helper( + ParserContext::new_with_str("abc", ()), + char_parser('a').reverse(()), + ); + } } diff --git a/src/text.rs b/src/text.rs index a6f9b8a..d186316 100644 --- a/src/text.rs +++ b/src/text.rs @@ -24,10 +24,23 @@ pub fn string_parser(str: String) -> impl Parser