295 lines
6.9 KiB
Rust
295 lines
6.9 KiB
Rust
mod modified_parsers;
|
|
mod primitive_parsers;
|
|
|
|
use crate::parser::modified_parsers::{
|
|
BindParser, ConvertParser, LitervalCountParser, LitervalParser, LookAheadParser, MapParser,
|
|
NextParser, ReverseParser,
|
|
};
|
|
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 }
|
|
}
|
|
|
|
pub fn message(&self) -> &str {
|
|
self.message.as_str()
|
|
}
|
|
}
|
|
|
|
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<'a, TToken, T, TContext>
|
|
where
|
|
TToken: Debug + Clone,
|
|
T: 'a,
|
|
{
|
|
fn parse(
|
|
&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: 'a, F, P>(self, f: F) -> BindParser<Self, F, T>
|
|
where
|
|
Self: Sized,
|
|
F: Fn(T) -> P,
|
|
P: Parser<'a, 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,
|
|
}
|
|
}
|
|
|
|
fn literal_count(self) -> LitervalCountParser<Self, T>
|
|
where
|
|
Self: Sized,
|
|
{
|
|
LitervalCountParser {
|
|
parser: self,
|
|
phantom_data: PhantomData,
|
|
}
|
|
}
|
|
|
|
fn literal(self) -> LitervalParser<Self, T>
|
|
where
|
|
Self: Sized,
|
|
{
|
|
LitervalParser {
|
|
parser: self,
|
|
phantom_data: PhantomData,
|
|
}
|
|
}
|
|
|
|
fn look_ahead(self) -> LookAheadParser<Self, T>
|
|
where
|
|
Self: Sized,
|
|
{
|
|
LookAheadParser {
|
|
parser: self,
|
|
phantom_data: PhantomData,
|
|
}
|
|
}
|
|
|
|
fn reverse<TResult>(self, result: TResult) -> ReverseParser<Self, T, TResult>
|
|
where
|
|
Self: Sized,
|
|
TResult: Debug + Clone,
|
|
{
|
|
ReverseParser {
|
|
parser: self,
|
|
result,
|
|
phantom_data: PhantomData,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a, TToken, T: 'a, TContext, F> Parser<'a, TToken, T, TContext> for F
|
|
where
|
|
TToken: Debug + Clone + 'a,
|
|
F: Fn(
|
|
Rc<RefCell<ParserContext<TToken, TContext>>>,
|
|
&'a [TToken],
|
|
) -> ParserResult<'a, TToken, T>,
|
|
{
|
|
fn parse(
|
|
&self,
|
|
context: Rc<RefCell<ParserContext<TToken, TContext>>>,
|
|
input: &'a [TToken],
|
|
) -> ParserResult<'a, TToken, T> {
|
|
(*self)(context, input)
|
|
}
|
|
}
|
|
|
|
impl<'a, TToken, T: 'a, TContext> Parser<'a, TToken, T, TContext>
|
|
for Box<dyn Parser<'a, TToken, T, TContext>>
|
|
where
|
|
TToken: Debug + Clone,
|
|
{
|
|
fn parse(
|
|
&self,
|
|
context: Rc<RefCell<ParserContext<TToken, TContext>>>,
|
|
input: &'a [TToken],
|
|
) -> ParserResult<'a, TToken, T> {
|
|
(**self).parse(context, input)
|
|
}
|
|
}
|
|
|
|
pub fn succeed<'a, TToken, T, TContext>(value: T) -> impl Parser<'a, TToken, T, TContext>
|
|
where
|
|
TToken: Debug + Clone,
|
|
T: Debug + Clone + 'a,
|
|
{
|
|
SucceedParser { value }
|
|
}
|
|
|
|
pub fn fail<'a, TToken, T: 'a, TContext>() -> impl Parser<'a, TToken, T, TContext>
|
|
where
|
|
TToken: Debug + Clone,
|
|
{
|
|
FailParser {
|
|
phantom_data: PhantomData,
|
|
}
|
|
}
|
|
|
|
pub fn fail_with_message<'a, TToken, T: 'a, TContext>(
|
|
message: &str,
|
|
) -> impl Parser<'a, TToken, T, TContext>
|
|
where
|
|
TToken: Debug + Clone,
|
|
{
|
|
FailWithMessageParser {
|
|
message: message.to_owned(),
|
|
phantom_data: PhantomData,
|
|
}
|
|
}
|
|
|
|
pub fn satisfy<'a, TToken, TContext, TP>(predicate: TP) -> impl Parser<'a, TToken, TToken, TContext>
|
|
where
|
|
TToken: Debug + Clone + 'a,
|
|
TP: Fn(&TToken) -> bool,
|
|
{
|
|
SatisfyParser { predicate }
|
|
}
|
|
|
|
pub fn any<'a, TToken, TContext>() -> impl Parser<'a, TToken, TToken, TContext>
|
|
where
|
|
TToken: Debug + Clone + 'a,
|
|
{
|
|
AnyParser {}
|
|
}
|
|
|
|
pub fn take<'a, TToken, TContext>(count: usize) -> impl Parser<'a, TToken, Vec<TToken>, TContext>
|
|
where
|
|
TToken: Debug + Clone + 'a,
|
|
{
|
|
TakeParser { count }
|
|
}
|
|
|
|
pub fn skip<'a, TToken, TContext>(count: usize) -> impl Parser<'a, 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: for<'a> Fn(Rc<RefCell<ParserContext<char, ()>>>, &'a [char]) -> ParserResult<'a, 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: for<'a> Fn(Rc<RefCell<ParserContext<char, ()>>>, &'a [char]) -> ParserResult<'a, 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());
|
|
}
|