diff --git a/src/parser/element_parser.rs b/src/parser/element_parser.rs index 9fe87a10..55bc6495 100644 --- a/src/parser/element_parser.rs +++ b/src/parser/element_parser.rs @@ -1,5 +1,10 @@ use nom::branch::alt; use nom::combinator::map; +use nom::combinator::opt; +use nom::combinator::peek; +use nom::sequence::tuple; +#[cfg(feature = "tracing")] +use tracing::span; use super::clock::clock; use super::comment::comment; @@ -75,43 +80,104 @@ fn _element<'b, 'g, 'r, 's>( let paragraph_matcher = parser_with_context!(paragraph)(context); let latex_environment_matcher = parser_with_context!(latex_environment)(context); - let (remaining, mut element) = match alt(( - map(plain_list_matcher, Element::PlainList), - greater_block_matcher, - map(dynamic_block_matcher, Element::DynamicBlock), - map(footnote_definition_matcher, Element::FootnoteDefinition), - map(comment_matcher, Element::Comment), - map(drawer_matcher, Element::Drawer), - map(table_matcher, Element::Table), - map(verse_block_matcher, Element::VerseBlock), - map(comment_block_matcher, Element::CommentBlock), - map(example_block_matcher, Element::ExampleBlock), - map(export_block_matcher, Element::ExportBlock), - map(src_block_matcher, Element::SrcBlock), - map(clock_matcher, Element::Clock), - map(diary_sexp_matcher, Element::DiarySexp), - map(fixed_width_area_matcher, Element::FixedWidthArea), - map(horizontal_rule_matcher, Element::HorizontalRule), - map(latex_environment_matcher, Element::LatexEnvironment), - map(babel_keyword_matcher, Element::BabelCall), - map(keyword_matcher, Element::Keyword), - ))(input) - { - the_ok @ Ok(_) => the_ok, - Err(_) => { - if can_be_paragraph { - match map(paragraph_matcher, Element::Paragraph)(input) { - the_ok @ Ok(_) => the_ok, - Err(_) => { - // TODO: Because this function expects a single element, if there are multiple affiliated keywords before an element that cannot have affiliated keywords, we end up re-parsing the affiliated keywords many times. - map(affiliated_keyword, Element::Keyword)(input) - } - } - } else { - map(affiliated_keyword, Element::Keyword)(input) - } + let (mut remaining, mut maybe_element) = { + #[cfg(feature = "tracing")] + let span = span!(tracing::Level::DEBUG, "Main element block"); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + opt(alt(( + map(plain_list_matcher, Element::PlainList), + greater_block_matcher, + map(dynamic_block_matcher, Element::DynamicBlock), + map(footnote_definition_matcher, Element::FootnoteDefinition), + map(comment_matcher, Element::Comment), + map(drawer_matcher, Element::Drawer), + map(table_matcher, Element::Table), + map(verse_block_matcher, Element::VerseBlock), + map(comment_block_matcher, Element::CommentBlock), + map(example_block_matcher, Element::ExampleBlock), + map(export_block_matcher, Element::ExportBlock), + map(src_block_matcher, Element::SrcBlock), + map(clock_matcher, Element::Clock), + map(diary_sexp_matcher, Element::DiarySexp), + map(fixed_width_area_matcher, Element::FixedWidthArea), + map(horizontal_rule_matcher, Element::HorizontalRule), + map(latex_environment_matcher, Element::LatexEnvironment), + map(babel_keyword_matcher, Element::BabelCall), + map(keyword_matcher, Element::Keyword), + )))(input)? + }; + + if maybe_element.is_none() && can_be_paragraph { + #[cfg(feature = "tracing")] + let span = span!(tracing::Level::DEBUG, "Paragraph with affiliated keyword."); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + let (remain, paragraph_with_affiliated_keyword) = opt(map( + tuple(( + peek(affiliated_keyword), + map(paragraph_matcher, Element::Paragraph), + )), + |(_, paragraph)| paragraph, + ))(remaining)?; + if paragraph_with_affiliated_keyword.is_some() { + remaining = remain; + maybe_element = paragraph_with_affiliated_keyword; } - }?; + } + + // if maybe_element.is_none() { + // #[cfg(feature = "tracing")] + // let span = span!(tracing::Level::DEBUG, "Regular keyword."); + // #[cfg(feature = "tracing")] + // let _enter = span.enter(); + + // let (remain, kw) = opt(map(keyword_matcher, Element::Keyword))(remaining)?; + // if kw.is_some() { + // maybe_element = kw; + // remaining = remain; + // } + // } + + if maybe_element.is_none() { + #[cfg(feature = "tracing")] + let span = span!( + tracing::Level::DEBUG, + "Affiliated keyword as regular keyword." + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + let (remain, kw) = opt(map(affiliated_keyword, Element::Keyword))(remaining)?; + if kw.is_some() { + maybe_element = kw; + remaining = remain; + } + } + + if maybe_element.is_none() && can_be_paragraph { + #[cfg(feature = "tracing")] + let span = span!( + tracing::Level::DEBUG, + "Paragraph without affiliated keyword." + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + let (remain, paragraph_without_affiliated_keyword) = + map(paragraph_matcher, Element::Paragraph)(remaining)?; + remaining = remain; + maybe_element = Some(paragraph_without_affiliated_keyword); + } + + if maybe_element.is_none() { + return Err(nom::Err::Error(CustomError::MyError(MyError( + "No element.", + )))); + } + let mut element = maybe_element.expect("The above if-statement ensures this is Some()."); let (remaining, _trailing_ws) = maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;