use std::rc::Rc; use nom::combinator::eof; use nom::IResult; use super::list::List; use super::list::Node; use super::Context; use super::Object; use crate::error::CustomError; use crate::error::MyError; use crate::error::Res; use crate::parser::exiting::ExitClass; type Matcher = dyn for<'r, 's> Fn(Context<'r, 's>, &'s str) -> Res<&'s str, &'s str>; #[derive(Debug, Clone)] pub struct ContextTree<'r, 's> { tree: List>, } impl<'r, 's> ContextTree<'r, 's> { pub fn new() -> Self { ContextTree { tree: List::new() } } pub fn branch_from(trunk: &Rc>>) -> Self { ContextTree { tree: List::branch_from(trunk), } } #[allow(dead_code)] pub fn ptr_eq<'x, 'y>(&self, other: &ContextTree<'x, 'y>) -> bool { self.tree.ptr_eq(&other.tree) } pub fn with_additional_node(&self, data: ContextElement<'r, 's>) -> ContextTree<'r, 's> { let new_list = self.tree.push_front(data); ContextTree { tree: new_list } } pub fn iter(&self) -> impl Iterator>>> { self.tree.iter() } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] pub fn check_exit_matcher( &'r self, i: &'s str, ) -> IResult<&'s str, &'s str, CustomError<&'s str>> { // Special check for EOF. We don't just make this a document-level exit matcher since the IgnoreParent ChainBehavior could cause early exit matchers to not run. let at_end_of_file = eof(i); if at_end_of_file.is_ok() { return at_end_of_file; } // let blocked_context = // self.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { // exit_matcher: ChainBehavior::IgnoreParent(Some(&always_fail)), // })); let mut current_class_filter = ExitClass::Beta; for current_node in self.iter() { let context_element = current_node.get_data(); match context_element { ContextElement::ExitMatcherNode(exit_matcher) => { if exit_matcher.class as u32 <= current_class_filter as u32 { current_class_filter = exit_matcher.class; let local_context = ContextTree::branch_from(current_node); let local_result = (exit_matcher.exit_matcher)(&local_context, i); if local_result.is_ok() { return local_result; } } } _ => {} }; } // TODO: Make this a specific error instead of just a generic MyError return Err(nom::Err::Error(CustomError::MyError(MyError("NoExit")))); } pub fn get_document_root(&self) -> Option<&'s str> { for current_node in self.iter() { let context_element = current_node.get_data(); match context_element { ContextElement::DocumentRoot(body) => { return Some(body); } _ => {} } } None } /// Indicates if elements should consume the whitespace after them. /// /// Defaults to true. pub fn should_consume_trailing_whitespace(&self) -> bool { self._should_consume_trailing_whitespace().unwrap_or(true) } fn _should_consume_trailing_whitespace(&self) -> Option { for current_node in self.iter() { let context_element = current_node.get_data(); match context_element { ContextElement::ConsumeTrailingWhitespace(should) => { return Some(*should); } _ => {} } } None } } #[derive(Debug)] pub enum ContextElement<'r, 's> { /// Stores a reference to the entire org-mode document being parsed. /// /// This is used for look-behind. DocumentRoot(&'s str), /// Stores a parser that indicates that children should exit upon matching an exit matcher. ExitMatcherNode(ExitMatcherNode<'r>), Context(&'r str), /// Stores the indentation level of the current list item. ListItem(usize), /// Stores the name of the greater block. GreaterBlock(&'s str), /// Indicates if elements should consume the whitespace after them. ConsumeTrailingWhitespace(bool), /// The contents of a radio target. /// /// 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<&'r Vec>>), /// Stores the current bracket depth inside a footnote reference's definition. /// /// The definition inside a footnote reference must have balanced /// brackets [] inside the definition, so this stores the amount /// of opening brackets subtracted by the amount of closing /// brackets within the definition must equal zero. /// /// A reference to the position in the string is also included so /// unbalanced brackets can be detected in the middle of an /// object. FootnoteReferenceDefinition(FootnoteReferenceDefinition<'s>), /// Stores the current bracket depth inside a citation. /// /// The global prefix, global suffix, key prefix, and key suffix /// inside a footnote reference must have balanced brackets [] /// inside the definition, so this stores the amount of opening /// brackets subtracted by the amount of closing brackets within /// the definition must equal zero. None of the prefixes or /// suffixes can be nested inside each other so we can use a /// single type for this without conflict. /// /// A reference to the position in the string is also included so /// unbalanced brackets can be detected in the middle of an /// object. CitationBracket(CitationBracket<'s>), /// Stores the current bracket or parenthesis depth inside an inline babel call. /// /// Inside an inline babel call the headers must have balanced /// parentheses () and the arguments must have balanced brackets /// [], so this stores the amount of opening brackets subtracted /// by the amount of closing brackets within the definition must /// equal zero. /// /// A reference to the position in the string is also included so /// unbalanced brackets can be detected in the middle of an /// object. BabelHeaderBracket(BabelHeaderBracket<'s>), /// Stores the current bracket or parenthesis depth inside an inline babel call. /// /// Inside an inline babel call the headers must have balanced /// parentheses () and the arguments must have balanced brackets /// [], so this stores the amount of opening brackets subtracted /// by the amount of closing brackets within the definition must /// equal zero. /// /// A reference to the position in the string is also included so /// unbalanced brackets can be detected in the middle of an /// object. InlineSourceBlockBracket(InlineSourceBlockBracket<'s>), /// Stores the current bracket or parenthesis depth inside a /// superscript or superscript. /// /// Inside the braces of a subscript or superscript there must be /// balanced braces {}, so this stores the amount of opening /// braces subtracted by the amount of closing braces within the /// definition must equal zero. /// /// A reference to the position in the string is also included so /// unbalanced braces can be detected in the middle of an object. SubscriptSuperscriptBrace(SubscriptSuperscriptBrace<'s>), } pub struct ExitMatcherNode<'r> { pub exit_matcher: &'r Matcher, pub class: ExitClass, } #[derive(Debug)] pub struct FootnoteReferenceDefinition<'s> { pub position: &'s str, pub depth: usize, } #[derive(Debug)] pub struct CitationBracket<'s> { pub position: &'s str, pub depth: usize, } #[derive(Debug)] pub struct BabelHeaderBracket<'s> { pub position: &'s str, pub depth: usize, } #[derive(Debug)] pub struct InlineSourceBlockBracket<'s> { pub position: &'s str, pub depth: usize, } #[derive(Debug)] pub struct SubscriptSuperscriptBrace<'s> { pub position: &'s str, pub depth: usize, } impl<'r> std::fmt::Debug for ExitMatcherNode<'r> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let mut formatter = f.debug_struct("ExitMatcherNode"); formatter.field("class", &self.class.to_string()); formatter.finish() } }