Use context_many1 for paragraphs in a document.

This commit is contained in:
Tom Alexander 2022-12-16 01:35:49 -05:00
parent 601fc4776a
commit 1da38c8f7d
Signed by: talexander
GPG Key ID: D3A179C9A53C0EDE
4 changed files with 38 additions and 11 deletions

View File

@ -72,7 +72,7 @@ where
} }
} }
} }
let elements: Vec<Token<'s>> = current_context let mut elements: Vec<Token<'s>> = current_context
.into_iter_until(context) .into_iter_until(context)
.filter_map(|context_element| match context_element { .filter_map(|context_element| match context_element {
ContextElement::PreviousElementNode(elem) => Some(elem.element), ContextElement::PreviousElementNode(elem) => Some(elem.element),
@ -86,6 +86,7 @@ where
err?; err?;
} }
} }
elements.reverse();
Ok((i, elements)) Ok((i, elements))
} }
} }

View File

@ -79,6 +79,12 @@ pub struct Link<'a> {
pub contents: &'a str, pub contents: &'a str,
} }
#[derive(Debug)]
pub struct Paragraph<'a> {
pub contents: Vec<TextElement<'a>>,
pub paragraph_end: &'a str,
}
pub fn line_break(input: &str) -> Res<&str, LineBreak> { pub fn line_break(input: &str) -> Res<&str, LineBreak> {
map(line_ending, |s: &str| LineBreak { contents: s })(input) map(line_ending, |s: &str| LineBreak { contents: s })(input)
} }

View File

@ -21,6 +21,7 @@ use super::text::span;
use super::text::symbol; use super::text::symbol;
use super::text::Bold; use super::text::Bold;
use super::text::Link; use super::text::Link;
use super::text::Paragraph;
use super::text::Res; use super::text::Res;
use super::text::TextElement; use super::text::TextElement;
use super::token::Token; use super::token::Token;
@ -45,10 +46,16 @@ use nom::InputLength;
type UnboundMatcher<'r, 's, I, O, E> = dyn Fn(Context<'r, 's>, I) -> IResult<I, O, E>; type UnboundMatcher<'r, 's, I, O, E> = dyn Fn(Context<'r, 's>, I) -> IResult<I, O, E>;
pub fn document(input: &str) -> Res<&str, Vec<(Vec<TextElement>, &str)>> { pub fn document(input: &str) -> Res<&str, Vec<Paragraph>> {
let initial_context: ContextTree<'_, '_> = ContextTree::new(); let initial_context: ContextTree<'_, '_> = ContextTree::new();
let ret = context_many1(&initial_context, paragraph)(input); let (remaining, tokens) = context_many1(&initial_context, paragraph)(input)?;
ret let paragraphs = tokens.into_iter().map(|token| {
match token {
Token::TextElement(_) => unreachable!(),
Token::Paragraph(paragraph) => paragraph,
}
}).collect();
Ok((remaining, paragraphs))
} }
pub fn context_paragraph_end<'s, 'r>( pub fn context_paragraph_end<'s, 'r>(
@ -93,6 +100,7 @@ fn _preceded_by_whitespace<'s, 'r>(context: Context<'r, 's>) -> bool {
TextElement::Link(_) => return false, TextElement::Link(_) => return false,
}; };
} }
Token::Paragraph(_) => unreachable!(),
}; };
} }
ContextElement::StartOfParagraph => { ContextElement::StartOfParagraph => {
@ -134,26 +142,30 @@ pub fn context_bold_end<'s, 'r>(context: Context<'r, 's>, input: &'s str) -> Res
Ok((remaining, actual_match)) Ok((remaining, actual_match))
} }
pub fn paragraph<'s, 'r>( pub fn paragraph<'s, 'r>(context: Context<'r, 's>, i: &'s str) -> Res<&'s str, Paragraph<'s>> {
context: Context<'r, 's>,
i: &'s str,
) -> Res<&'s str, (Vec<TextElement<'s>>, &'s str)> {
// Add a not(eof) check because many_till cannot match a zero-length string // Add a not(eof) check because many_till cannot match a zero-length string
not(eof)(i)?; not(eof)(i)?;
let paragraph_context = context let paragraph_context = context
.with_additional_node(ContextElement::StartOfParagraph)
.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { .with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
exit_matcher: ChainBehavior::AndParent(Some(&context_paragraph_end)), exit_matcher: ChainBehavior::AndParent(Some(&context_paragraph_end)),
})); }))
.with_additional_node(ContextElement::StartOfParagraph);
let (remaining, (many, till)) = let (remaining, (many, till)) =
context_many_till(&paragraph_context, flat_text_element, context_paragraph_end)(i)?; context_many_till(&paragraph_context, flat_text_element, context_paragraph_end)(i)?;
let many = many let many = many
.into_iter() .into_iter()
.filter_map(|token| match token { .filter_map(|token| match token {
Token::TextElement(text_element) => Some(text_element), Token::TextElement(text_element) => Some(text_element),
Token::Paragraph(_) => panic!("There should only be text elements in paragraphs."),
}) })
.collect(); .collect();
Ok((remaining, (many, till))) Ok((
remaining,
Paragraph {
contents: many,
paragraph_end: till,
},
))
} }
fn flat_text_element<'s, 'r>( fn flat_text_element<'s, 'r>(

View File

@ -1,8 +1,10 @@
use super::text::Paragraph;
use super::text::TextElement; use super::text::TextElement;
#[derive(Debug)] #[derive(Debug)]
pub enum Token<'a> { pub enum Token<'a> {
TextElement(TextElement<'a>), TextElement(TextElement<'a>),
Paragraph(Paragraph<'a>),
} }
impl<'a> Into<Token<'a>> for TextElement<'a> { impl<'a> Into<Token<'a>> for TextElement<'a> {
@ -10,3 +12,9 @@ impl<'a> Into<Token<'a>> for TextElement<'a> {
Token::TextElement(self) Token::TextElement(self)
} }
} }
impl<'a> Into<Token<'a>> for Paragraph<'a> {
fn into(self) -> Token<'a> {
Token::Paragraph(self)
}
}