Fix handling affiliated keywords before invalid paragraphs.

This commit is contained in:
Tom Alexander 2023-10-04 23:40:38 -04:00
parent 65eda08843
commit 1351577c5a
Signed by: talexander
GPG Key ID: D3A179C9A53C0EDE
5 changed files with 62 additions and 10 deletions

View File

@ -12,6 +12,7 @@ use crate::error::CustomError;
use crate::error::MyError;
use crate::error::Res;
use crate::parser::OrgSource;
use crate::types::Keyword;
#[derive(Debug)]
pub(crate) enum ContextElement<'r, 's> {
@ -27,11 +28,22 @@ pub(crate) enum ContextElement<'r, 's> {
/// Indicates if elements should consume the whitespace after them.
ConsumeTrailingWhitespace(bool),
/// Indicate that we are parsing a paragraph that already has affiliated keywords.
///
/// The value stored is the start of the element after the affiliated keywords. In this way, we can ensure that we do not exit an element immediately after the affiliated keyword had been consumed.
HasAffiliatedKeyword(HasAffiliatedKeywordInner<'r, 's>),
/// This is just here to use the 's lifetime until I'm sure we can eliminate it from ContextElement.
#[allow(dead_code)]
Placeholder(PhantomData<&'s str>),
}
#[derive(Debug, Clone)]
pub(crate) struct HasAffiliatedKeywordInner<'r, 's> {
pub(crate) start_after_affiliated_keywords: OrgSource<'s>,
pub(crate) keywords: &'r Vec<Keyword<'s>>,
}
pub(crate) struct ExitMatcherNode<'r> {
// TODO: Should this be "&'r DynContextMatcher<'c>" ?
pub(crate) exit_matcher: &'r DynContextMatcher<'r>,

View File

@ -21,6 +21,7 @@ type DynMatcher<'c> = dyn Matcher + 'c;
pub(crate) use context::Context;
pub(crate) use context::ContextElement;
pub(crate) use context::ExitMatcherNode;
pub(crate) use context::HasAffiliatedKeywordInner;
pub(crate) use exiting::ExitClass;
pub use file_access_interface::FileAccessInterface;
pub use file_access_interface::LocalFileAccessInterface;

View File

@ -14,7 +14,7 @@ use crate::error::MyError;
pub(crate) type BracketDepth = i16;
#[derive(Copy, Clone)]
#[derive(Copy, Clone, PartialEq)]
pub(crate) struct OrgSource<'s> {
full_source: &'s str,
start: usize,

View File

@ -12,12 +12,16 @@ use super::keyword::affiliated_keyword;
use super::org_source::OrgSource;
use super::util::blank_line;
use super::util::get_consumed;
use super::util::get_has_affiliated_keyword;
use super::util::get_name;
use crate::context::parser_with_context;
use crate::context::ContextElement;
use crate::context::ExitClass;
use crate::context::ExitMatcherNode;
use crate::context::HasAffiliatedKeywordInner;
use crate::context::RefContext;
use crate::error::CustomError;
use crate::error::MyError;
use crate::error::Res;
use crate::parser::object_parser::standard_set_object;
use crate::parser::util::exit_matcher_parser;
@ -30,12 +34,18 @@ pub(crate) fn paragraph<'b, 'g, 'r, 's>(
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Paragraph<'s>> {
let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?;
let parser_context = ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Gamma,
exit_matcher: &paragraph_end,
});
let parser_context = context.with_additional_node(&parser_context);
let contexts = [
ContextElement::HasAffiliatedKeyword(HasAffiliatedKeywordInner {
start_after_affiliated_keywords: input,
keywords: &affiliated_keywords,
}),
ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Gamma,
exit_matcher: &paragraph_end,
}),
];
let parser_context = context.with_additional_node(&contexts[0]);
let parser_context = parser_context.with_additional_node(&contexts[1]);
let standard_set_object_matcher = parser_with_context!(standard_set_object)(&parser_context);
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
@ -63,10 +73,21 @@ fn paragraph_end<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let non_paragraph_element_matcher = parser_with_context!(detect_element(false))(context);
let regular_end = recognize(tuple((start_of_line, many1(blank_line))))(input);
if regular_end.is_ok() {
return regular_end;
}
match get_has_affiliated_keyword(context) {
Some(start_post_affiliated_keywords) if input == start_post_affiliated_keywords => {
return Err(nom::Err::Error(CustomError::MyError(MyError(
"No exit due to affiliated keywords.",
))));
}
_ => {}
}
// Check to see if input is the start of a HasAffiliatedKeyword
alt((
recognize(tuple((start_of_line, many1(blank_line)))),
recognize(non_paragraph_element_matcher),
recognize(parser_with_context!(detect_element(false))(context)),
eof,
))(input)
}

View File

@ -284,3 +284,21 @@ pub(crate) fn get_name<'s>(affiliated_keywords: &Vec<Keyword<'s>>) -> Option<&'s
.last();
name_keyword.map(|kw| kw.value)
}
pub(crate) fn get_has_affiliated_keyword<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>,
) -> Option<OrgSource<'s>> {
for context in context.iter() {
match context {
ContextElement::HasAffiliatedKeyword(inner) => {
if !inner.keywords.is_empty() {
return Some(inner.start_after_affiliated_keywords);
} else {
return None;
}
}
_ => {}
}
}
None
}