diff --git a/src/parser/element.rs b/src/parser/element.rs index 3ae3d1a..4b60800 100644 --- a/src/parser/element.rs +++ b/src/parser/element.rs @@ -1,27 +1,12 @@ -use nom::branch::alt; -use nom::character::complete::line_ending; -use nom::character::complete::space0; -use nom::combinator::eof; +use crate::parser::parser_with_context::parser_with_context; use nom::combinator::map; use nom::combinator::not; -use nom::combinator::recognize; -use nom::multi::many0; -use nom::multi::many1; -use nom::sequence::tuple; - -use crate::parser::object::standard_set_object; -use crate::parser::parser_context::ChainBehavior; -use crate::parser::parser_context::ContextElement; -use crate::parser::parser_context::ExitMatcherNode; -use crate::parser::parser_with_context::parser_with_context; use super::error::Res; use super::greater_element::PlainList; use super::lesser_element::Paragraph; +use super::paragraph::paragraph; use super::source::Source; -use super::util::blank_line; -use super::util::get_consumed; -use super::util::trailing_whitespace; use super::Context; #[derive(Debug)] @@ -46,24 +31,3 @@ pub fn element<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, map(paragraph_matcher, Element::Paragraph)(input) } - -fn paragraph<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Paragraph<'s>> { - let parser_context = - context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { - exit_matcher: ChainBehavior::AndParent(Some(¶graph_end)), - })); - let standard_set_object_matcher = parser_with_context!(standard_set_object)(&parser_context); - - let (remaining, children) = many1(standard_set_object_matcher)(input)?; - - let (remaining, _trailing_whitespace) = trailing_whitespace(remaining)?; - - let source = get_consumed(input, remaining); - - Ok((remaining, Paragraph { source, children })) -} - -fn paragraph_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { - // TODO: Other elements should also end paragraphs - alt((recognize(tuple((line_ending, many1(blank_line)))), eof))(input) -} diff --git a/src/parser/mod.rs b/src/parser/mod.rs index f9d281e..ca10ae3 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -6,8 +6,10 @@ mod greater_element; mod lesser_element; mod list; mod object; +mod paragraph; mod parser_context; mod parser_with_context; +mod plain_text; mod source; mod util; pub use document::document; diff --git a/src/parser/object.rs b/src/parser/object.rs index aaf2024..d94e783 100644 --- a/src/parser/object.rs +++ b/src/parser/object.rs @@ -1,11 +1,9 @@ use nom::combinator::map; use nom::combinator::not; -use crate::parser::error::CustomError; -use crate::parser::error::MyError; - use super::error::Res; use super::parser_with_context::parser_with_context; +use super::plain_text::plain_text; use super::source::Source; use super::Context; @@ -51,58 +49,3 @@ pub fn standard_set_object<'r, 's>( map(plain_text_matcher, Object::PlainText)(input) } - -fn plain_text<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, PlainText<'s>> { - if input.len() == 0 { - return Err(nom::Err::Error(CustomError::MyError(MyError( - "Zero input length to plain_text.", - )))); - } - // not(|i| context.check_exit_matcher(i))(input)?; - let mut current_input = input.char_indices(); - loop { - match current_input.next() { - Some((offset, _char)) => { - let remaining = &input[offset..]; - let exit_matcher_status = not(|i| context.check_exit_matcher(i))(remaining); - if exit_matcher_status.is_err() { - if offset == 0 { - // If we're at the start of the input, then nothing is plain text, so fire an error for zero-length match. - exit_matcher_status?; - } else { - return Ok(( - &input[offset..], - PlainText { - source: &input[..offset], - }, - )); - } - } - } - None => { - // We hit the end of the file, so all input must be plain text - return Ok((&input[input.len()..], PlainText { source: input })); - } - }; - } -} - -#[cfg(test)] -mod tests { - use crate::parser::parser_context::ContextElement; - use crate::parser::parser_context::ContextTree; - - use super::*; - - #[test] - fn plain_text_simple() { - let input = "foobarbaz"; - let initial_context: ContextTree<'_, '_> = ContextTree::new(); - let document_context = - initial_context.with_additional_node(ContextElement::DocumentRoot(input)); - let plain_text_matcher = parser_with_context!(plain_text)(&document_context); - let (remaining, result) = map(plain_text_matcher, Object::PlainText)(input).unwrap(); - assert_eq!(remaining, ""); - assert_eq!(result.get_source(), input); - } -} diff --git a/src/parser/paragraph.rs b/src/parser/paragraph.rs new file mode 100644 index 0000000..d63500e --- /dev/null +++ b/src/parser/paragraph.rs @@ -0,0 +1,40 @@ +use nom::branch::alt; +use nom::character::complete::line_ending; +use nom::combinator::eof; +use nom::combinator::recognize; +use nom::multi::many1; +use nom::sequence::tuple; + +use crate::parser::object::standard_set_object; +use crate::parser::parser_context::ChainBehavior; +use crate::parser::parser_context::ContextElement; +use crate::parser::parser_context::ExitMatcherNode; +use crate::parser::parser_with_context::parser_with_context; + +use super::error::Res; +use super::lesser_element::Paragraph; +use super::util::blank_line; +use super::util::get_consumed; +use super::util::trailing_whitespace; +use super::Context; + +pub fn paragraph<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Paragraph<'s>> { + let parser_context = + context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { + exit_matcher: ChainBehavior::AndParent(Some(¶graph_end)), + })); + let standard_set_object_matcher = parser_with_context!(standard_set_object)(&parser_context); + + let (remaining, children) = many1(standard_set_object_matcher)(input)?; + + let (remaining, _trailing_whitespace) = trailing_whitespace(remaining)?; + + let source = get_consumed(input, remaining); + + Ok((remaining, Paragraph { source, children })) +} + +fn paragraph_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { + // TODO: Other elements should also end paragraphs + alt((recognize(tuple((line_ending, many1(blank_line)))), eof))(input) +} diff --git a/src/parser/plain_text.rs b/src/parser/plain_text.rs new file mode 100644 index 0000000..1330cea --- /dev/null +++ b/src/parser/plain_text.rs @@ -0,0 +1,68 @@ +use nom::combinator::not; + +use crate::parser::error::CustomError; +use crate::parser::error::MyError; + +use super::error::Res; +use super::object::PlainText; +use super::Context; + +pub fn plain_text<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, PlainText<'s>> { + if input.len() == 0 { + return Err(nom::Err::Error(CustomError::MyError(MyError( + "Zero input length to plain_text.", + )))); + } + // not(|i| context.check_exit_matcher(i))(input)?; + let mut current_input = input.char_indices(); + loop { + match current_input.next() { + Some((offset, _char)) => { + let remaining = &input[offset..]; + let exit_matcher_status = not(|i| context.check_exit_matcher(i))(remaining); + if exit_matcher_status.is_err() { + if offset == 0 { + // If we're at the start of the input, then nothing is plain text, so fire an error for zero-length match. + exit_matcher_status?; + } else { + return Ok(( + &input[offset..], + PlainText { + source: &input[..offset], + }, + )); + } + } + } + None => { + // We hit the end of the file, so all input must be plain text + return Ok((&input[input.len()..], PlainText { source: input })); + } + }; + } +} + +#[cfg(test)] +mod tests { + use nom::combinator::map; + + use crate::parser::object::Object; + use crate::parser::parser_context::ContextElement; + use crate::parser::parser_context::ContextTree; + use crate::parser::parser_with_context::parser_with_context; + use crate::parser::source::Source; + + use super::*; + + #[test] + fn plain_text_simple() { + let input = "foobarbaz"; + let initial_context: ContextTree<'_, '_> = ContextTree::new(); + let document_context = + initial_context.with_additional_node(ContextElement::DocumentRoot(input)); + let plain_text_matcher = parser_with_context!(plain_text)(&document_context); + let (remaining, result) = map(plain_text_matcher, Object::PlainText)(input).unwrap(); + assert_eq!(remaining, ""); + assert_eq!(result.get_source(), input); + } +}