init: repo.
This commit is contained in:
commit
ffe288ab61
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(|_| ()),
|
||||
)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user