diff --git a/src/parser/combinator.rs b/src/parser/combinator.rs new file mode 100644 index 00000000..2daf5991 --- /dev/null +++ b/src/parser/combinator.rs @@ -0,0 +1,111 @@ +use crate::parser::parser_with_context::parser_with_context; +use crate::parser::text::paragraph_end; + +use super::error::CustomError; +use super::error::MyError; +use super::nom_context::ChainBehavior; +use super::nom_context::ContextElement; +use super::nom_context::ContextTree; +use super::nom_context::ExitMatcherNode; +use super::nom_context::PreviousElementNode; +use super::text::bold_end; +use super::text::bold_start; +use super::text::line_break; +use super::text::link_end; +use super::text::link_start; +use super::text::space; +use super::text::span; +use super::text::symbol; +use super::text::Bold; +use super::text::Link; +use super::text::Res; +use super::text::TextElement; +use super::token::Token; +use super::Context; +use nom::branch::alt; +use nom::bytes::complete::tag; +use nom::bytes::complete::take; +use nom::combinator::cond; +use nom::combinator::eof; +use nom::combinator::map; +use nom::combinator::not; +use nom::combinator::peek; +use nom::combinator::recognize; +use nom::error::ErrorKind; +use nom::error::ParseError; +use nom::error::VerboseError; +use nom::multi::many1; +use nom::multi::many_till; +use nom::sequence::tuple; +use nom::IResult; +use nom::InputLength; + +pub fn context_many_till<'r, 's, I, O, E, F, M, T>( + context: Context<'r, 's>, + mut many_matcher: M, + mut till_matcher: T, +) -> impl FnMut(I) -> IResult>, F), E> + 'r +where + I: Clone + InputLength, + E: ParseError, + M: for<'x> Fn(Context<'x, 's>, I) -> IResult + 'r, + T: for<'x> Fn(Context<'x, 's>, I) -> IResult + 'r, + O: Into>, +{ + move |mut i: I| { + // TODO: Can I eliminate the clone? + let mut current_context = context.clone(); + // Despite the clone, the Rc should still point to the same value, otherwise we'll get stuck in an endless loop. + assert!(current_context.ptr_eq(context)); + loop { + let len = i.input_len(); + match till_matcher(¤t_context, i.clone()) { + Ok((remaining, finish)) => { + let mut ret = Vec::new(); + while !current_context.ptr_eq(context) { + let (context_element, next_context) = current_context.pop_front(); + let context_element = context_element.expect("We only pop off context elements created in this function, so they are all Some()"); + current_context = next_context; + match context_element { + ContextElement::ExitMatcherNode(_) => {} + ContextElement::StartOfParagraph => {} + ContextElement::Context(_) => {} + ContextElement::PreviousElementNode(PreviousElementNode { + element: token, + }) => { + ret.push(token); + } + }; + } + ret.reverse(); + return Ok((remaining, (ret, finish))); + } + Err(nom::Err::Error(_)) => { + match many_matcher(¤t_context, i.clone()) { + Err(nom::Err::Error(err)) => { + return Err(nom::Err::Error(E::append(i, ErrorKind::ManyTill, err))) + } + Err(e) => return Err(e), + Ok((remaining, many_elem)) => { + // infinite loop check: the parser must always consume + if remaining.input_len() == len { + return Err(nom::Err::Error(E::from_error_kind( + remaining, + ErrorKind::ManyTill, + ))); + } + + current_context = current_context.with_additional_node( + ContextElement::PreviousElementNode(PreviousElementNode { + element: many_elem.into(), + }), + ); + i = remaining; + } + } + } + Err(e) => return Err(e), + }; + } + } +} diff --git a/src/parser/mod.rs b/src/parser/mod.rs index baf0499d..472aa0b9 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1,3 +1,4 @@ +mod combinator; mod error; mod list; mod nom_context; diff --git a/src/parser/text_element_parser.rs b/src/parser/text_element_parser.rs index 24f86ed3..ac10512b 100644 --- a/src/parser/text_element_parser.rs +++ b/src/parser/text_element_parser.rs @@ -2,6 +2,7 @@ use crate::parser::parser_with_context::parser_with_context; use crate::parser::text::paragraph_end; +use super::combinator::context_many_till; use super::error::CustomError; use super::error::MyError; use super::nom_context::ChainBehavior; @@ -43,76 +44,6 @@ use nom::InputLength; type UnboundMatcher<'r, 's, I, O, E> = dyn Fn(Context<'r, 's>, I) -> IResult; -fn context_many_till<'r, 's, I, O, E, F, M, T>( - context: Context<'r, 's>, - mut many_matcher: M, - mut till_matcher: T, -) -> impl FnMut(I) -> IResult>, F), E> + 'r -where - I: Clone + InputLength, - E: ParseError, - M: for<'x> Fn(Context<'x, 's>, I) -> IResult + 'r, - T: for<'x> Fn(Context<'x, 's>, I) -> IResult + 'r, - O: Into>, -{ - move |mut i: I| { - // TODO: Can I eliminate the clone? - let mut current_context = context.clone(); - // Despite the clone, the Rc should still point to the same value, otherwise we'll get stuck in an endless loop. - assert!(current_context.ptr_eq(context)); - loop { - let len = i.input_len(); - match till_matcher(¤t_context, i.clone()) { - Ok((remaining, finish)) => { - let mut ret = Vec::new(); - while !current_context.ptr_eq(context) { - let (context_element, next_context) = current_context.pop_front(); - let context_element = context_element.expect("We only pop off context elements created in this function, so they are all Some()"); - current_context = next_context; - match context_element { - ContextElement::ExitMatcherNode(_) => {} - ContextElement::StartOfParagraph => {} - ContextElement::Context(_) => {} - ContextElement::PreviousElementNode(PreviousElementNode { - element: token, - }) => { - ret.push(token); - } - }; - } - ret.reverse(); - return Ok((remaining, (ret, finish))); - } - Err(nom::Err::Error(_)) => { - match many_matcher(¤t_context, i.clone()) { - Err(nom::Err::Error(err)) => { - return Err(nom::Err::Error(E::append(i, ErrorKind::ManyTill, err))) - } - Err(e) => return Err(e), - Ok((remaining, many_elem)) => { - // infinite loop check: the parser must always consume - if remaining.input_len() == len { - return Err(nom::Err::Error(E::from_error_kind( - remaining, - ErrorKind::ManyTill, - ))); - } - - current_context = current_context.with_additional_node( - ContextElement::PreviousElementNode(PreviousElementNode { - element: many_elem.into(), - }), - ); - i = remaining; - } - } - } - Err(e) => return Err(e), - }; - } - } -} - pub fn document(input: &str) -> Res<&str, Vec<(Vec, &str)>> { let initial_context: ContextTree<'_, '_> = ContextTree::new(); let paragraph_parser = parser_with_context!(paragraph); @@ -184,7 +115,9 @@ pub fn context_bold_start<'s, 'r>( recognize(bold_start)(input) } else { // TODO: Make this a specific error instead of just a generic MyError - return Err(nom::Err::Error(CustomError::MyError(MyError("Cannot start bold")))); + return Err(nom::Err::Error(CustomError::MyError(MyError( + "Cannot start bold", + )))); } }