From bd04451d5852ebd8ae8d77f25ab5bdfe5cf76f2b Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Fri, 14 Jul 2023 19:06:58 -0400 Subject: [PATCH] Implement the second parsing pass. --- src/parser/document.rs | 46 ++++++++++++++++++++++++++++-------- src/parser/parser_context.rs | 2 +- 2 files changed, 37 insertions(+), 11 deletions(-) diff --git a/src/parser/document.rs b/src/parser/document.rs index 98e570c..719b53b 100644 --- a/src/parser/document.rs +++ b/src/parser/document.rs @@ -96,8 +96,35 @@ pub fn document(input: &str) -> Res<&str, Document> { let initial_context: ContextTree<'_, '_> = ContextTree::new(); let document_context = initial_context.with_additional_node(ContextElement::DocumentRoot(input)); - let zeroth_section_matcher = parser_with_context!(zeroth_section)(&document_context); - let heading_matcher = parser_with_context!(heading)(&document_context); + let (remaining, document) = _document(&document_context, input)?; + { + // If there are radio targets in this document then we need to parse the entire document again with the knowledge of the radio targets. + let all_radio_targets: Vec<&Vec>> = document + .iter_tokens() + .filter_map(|tkn| match tkn { + Token::Object(obj) => Some(obj), + _ => None, + }) + .filter_map(|obj| match obj { + Object::RadioTarget(rt) => Some(rt), + _ => None, + }) + .map(|rt| &rt.children) + .collect(); + if !all_radio_targets.is_empty() { + let document_context = document_context + .with_additional_node(ContextElement::RadioTarget(all_radio_targets)); + let (remaining, document) = _document(&document_context, input)?; + return Ok((remaining, document)); + } + } + Ok((remaining, document)) +} + +#[tracing::instrument(ret, level = "debug")] +pub fn _document<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Document<'s>> { + let zeroth_section_matcher = parser_with_context!(zeroth_section)(context); + let heading_matcher = parser_with_context!(heading)(context); let (remaining, _blank_lines) = many0(blank_line)(input)?; let (remaining, zeroth_section) = opt(zeroth_section_matcher)(remaining)?; let (remaining, children) = many0(heading_matcher)(remaining)?; @@ -268,14 +295,13 @@ impl<'s> Document<'s> { impl<'s> Heading<'s> { pub fn iter_tokens<'r>(&'r self) -> impl Iterator> { - self.title.iter().map(Token::Object).chain(self.children.iter().map( - |de| { - match de { - DocumentElement::Heading(obj) => Token::Heading(obj), - DocumentElement::Section(obj) => Token::Section(obj), - } - } - )) + self.title + .iter() + .map(Token::Object) + .chain(self.children.iter().map(|de| match de { + DocumentElement::Heading(obj) => Token::Heading(obj), + DocumentElement::Section(obj) => Token::Section(obj), + })) } } diff --git a/src/parser/parser_context.rs b/src/parser/parser_context.rs index 616b983..c49e099 100644 --- a/src/parser/parser_context.rs +++ b/src/parser/parser_context.rs @@ -140,7 +140,7 @@ pub enum ContextElement<'r, 's> { /// If any are found, this will force a 2nd parse through the /// org-mode document since text needs to be re-parsed to look for /// radio links matching the contents of radio targets. - RadioTarget(Vec>>), + RadioTarget(Vec<&'r Vec>>), } pub struct ExitMatcherNode<'r> {