diff --git a/org_mode_samples/greater_element/plain_list/item/name.org b/org_mode_samples/greater_element/plain_list/item/name.org new file mode 100644 index 00000000..0ae0f101 --- /dev/null +++ b/org_mode_samples/greater_element/plain_list/item/name.org @@ -0,0 +1,3 @@ +1. foo +#+NAME: bar +2. baz diff --git a/org_mode_samples/greater_element/property_drawer/name.org b/org_mode_samples/greater_element/property_drawer/name.org new file mode 100644 index 00000000..381edd3a --- /dev/null +++ b/org_mode_samples/greater_element/property_drawer/name.org @@ -0,0 +1,5 @@ +* Overwrite + #+NAME: foo + :PROPERTIES: + :header-args: :var foo="lorem" + :END: diff --git a/org_mode_samples/greater_element/table/row/name.org b/org_mode_samples/greater_element/table/row/name.org new file mode 100644 index 00000000..2719eced --- /dev/null +++ b/org_mode_samples/greater_element/table/row/name.org @@ -0,0 +1,3 @@ +|foo| +#+NAME: bar +|baz | diff --git a/org_mode_samples/sections_and_headings/name.org b/org_mode_samples/sections_and_headings/name.org new file mode 100644 index 00000000..b5f03662 --- /dev/null +++ b/org_mode_samples/sections_and_headings/name.org @@ -0,0 +1,3 @@ +#+NAME: foo +* bar +#+NAME: baz diff --git a/src/parser/affiliated_keyword.rs b/src/parser/affiliated_keyword.rs index 498033f8..92b59fce 100644 --- a/src/parser/affiliated_keyword.rs +++ b/src/parser/affiliated_keyword.rs @@ -25,12 +25,15 @@ use crate::types::AffiliatedKeywordValue; use crate::types::AffiliatedKeywords; use crate::types::Keyword; -pub(crate) fn parse_affiliated_keywords<'g, 's>( +pub(crate) fn parse_affiliated_keywords<'g, 's, AK>( global_settings: &'g GlobalSettings<'g, 's>, - input: Vec>, -) -> AffiliatedKeywords<'s> { + input: AK, +) -> AffiliatedKeywords<'s> +where + AK: IntoIterator>, +{ let mut ret = BTreeMap::new(); - for kw in input.into_iter() { + for kw in input { let translated_name = translate_name(global_settings, kw.key); if is_single_string_keyword(global_settings, translated_name.as_str()) { ret.insert( diff --git a/src/parser/babel_call.rs b/src/parser/babel_call.rs index eaeae758..a40f0764 100644 --- a/src/parser/babel_call.rs +++ b/src/parser/babel_call.rs @@ -9,13 +9,11 @@ use nom::combinator::opt; use nom::combinator::peek; use nom::combinator::recognize; use nom::combinator::verify; -use nom::multi::many0; use nom::multi::many_till; use nom::sequence::tuple; use nom::InputTake; use super::affiliated_keyword::parse_affiliated_keywords; -use super::keyword::affiliated_keyword; use super::org_source::BracketDepth; use super::util::maybe_consume_trailing_whitespace_if_not_exiting; use super::util::start_of_line; @@ -28,17 +26,21 @@ use crate::error::Res; use crate::parser::util::get_consumed; use crate::parser::util::org_line_ending; use crate::types::BabelCall; +use crate::types::Keyword; #[cfg_attr( feature = "tracing", tracing::instrument(ret, level = "debug", skip(context)) )] -pub(crate) fn babel_call<'b, 'g, 'r, 's>( +pub(crate) fn babel_call<'b, 'g, 'r, 's, AK>( + affiliated_keywords: AK, + remaining: OrgSource<'s>, context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, -) -> Res, BabelCall<'s>> { - let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?; - +) -> Res, BabelCall<'s>> +where + AK: IntoIterator>, +{ start_of_line(remaining)?; let (remaining, _) = tuple((space0, tag("#+"), tag_no_case("call"), tag(":")))(remaining)?; diff --git a/src/parser/diary_sexp.rs b/src/parser/diary_sexp.rs index 698b30f0..5b2cbfe5 100644 --- a/src/parser/diary_sexp.rs +++ b/src/parser/diary_sexp.rs @@ -14,16 +14,21 @@ use crate::error::Res; use crate::parser::util::get_consumed; use crate::parser::util::start_of_line; use crate::types::DiarySexp; +use crate::types::Keyword; #[cfg_attr( feature = "tracing", tracing::instrument(ret, level = "debug", skip(context)) )] -pub(crate) fn diary_sexp<'b, 'g, 'r, 's>( +pub(crate) fn diary_sexp<'b, 'g, 'r, 's, AK>( + affiliated_keywords: AK, + remaining: OrgSource<'s>, context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, -) -> Res, DiarySexp<'s>> { - let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?; +) -> Res, DiarySexp<'s>> +where + AK: IntoIterator>, +{ start_of_line(remaining)?; let (remaining, value) = recognize(tuple((tag("%%("), is_not("\r\n"))))(remaining)?; let (remaining, _eol) = org_line_ending(remaining)?; diff --git a/src/parser/drawer.rs b/src/parser/drawer.rs index 438a0074..8c6f27cb 100644 --- a/src/parser/drawer.rs +++ b/src/parser/drawer.rs @@ -7,12 +7,10 @@ use nom::character::complete::space0; use nom::combinator::eof; use nom::combinator::not; use nom::combinator::recognize; -use nom::multi::many0; use nom::multi::many_till; use nom::sequence::tuple; use super::affiliated_keyword::parse_affiliated_keywords; -use super::keyword::affiliated_keyword; use super::org_source::OrgSource; use super::util::maybe_consume_trailing_whitespace_if_not_exiting; use crate::context::parser_with_context; @@ -32,6 +30,7 @@ use crate::parser::util::start_of_line; use crate::parser::util::WORD_CONSTITUENT_CHARACTERS; use crate::types::Drawer; use crate::types::Element; +use crate::types::Keyword; use crate::types::Paragraph; use crate::types::SetSource; @@ -39,16 +38,20 @@ use crate::types::SetSource; feature = "tracing", tracing::instrument(ret, level = "debug", skip(context)) )] -pub(crate) fn drawer<'b, 'g, 'r, 's>( +pub(crate) fn drawer<'b, 'g, 'r, 's, AK>( + affiliated_keywords: AK, + remaining: OrgSource<'s>, context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, -) -> Res, Drawer<'s>> { +) -> Res, Drawer<'s>> +where + AK: IntoIterator>, +{ if immediate_in_section(context, "drawer") { return Err(nom::Err::Error(CustomError::MyError(MyError( "Cannot nest objects of the same element".into(), )))); } - let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?; start_of_line(remaining)?; let (remaining, _leading_whitespace) = space0(remaining)?; let (remaining, (_open_colon, drawer_name, _close_colon, _new_line)) = tuple(( diff --git a/src/parser/dynamic_block.rs b/src/parser/dynamic_block.rs index 6086176e..3a7984cd 100644 --- a/src/parser/dynamic_block.rs +++ b/src/parser/dynamic_block.rs @@ -18,7 +18,6 @@ use nom::sequence::preceded; use nom::sequence::tuple; use super::affiliated_keyword::parse_affiliated_keywords; -use super::keyword::affiliated_keyword; use super::org_source::OrgSource; use super::util::maybe_consume_trailing_whitespace_if_not_exiting; use crate::context::parser_with_context; @@ -37,6 +36,7 @@ use crate::parser::util::immediate_in_section; use crate::parser::util::start_of_line; use crate::types::DynamicBlock; use crate::types::Element; +use crate::types::Keyword; use crate::types::Paragraph; use crate::types::SetSource; @@ -44,16 +44,20 @@ use crate::types::SetSource; feature = "tracing", tracing::instrument(ret, level = "debug", skip(context)) )] -pub(crate) fn dynamic_block<'b, 'g, 'r, 's>( +pub(crate) fn dynamic_block<'b, 'g, 'r, 's, AK>( + affiliated_keywords: AK, + remaining: OrgSource<'s>, context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, -) -> Res, DynamicBlock<'s>> { +) -> Res, DynamicBlock<'s>> +where + AK: IntoIterator>, +{ if immediate_in_section(context, "dynamic block") { return Err(nom::Err::Error(CustomError::MyError(MyError( "Cannot nest objects of the same element".into(), )))); } - let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?; start_of_line(remaining)?; let (remaining, _leading_whitespace) = space0(remaining)?; diff --git a/src/parser/element_parser.rs b/src/parser/element_parser.rs index 12629242..bf043882 100644 --- a/src/parser/element_parser.rs +++ b/src/parser/element_parser.rs @@ -1,8 +1,5 @@ use nom::branch::alt; -use nom::combinator::map; -use nom::combinator::opt; -use nom::combinator::peek; -use nom::sequence::tuple; +use nom::multi::many0; #[cfg(feature = "tracing")] use tracing::span; @@ -21,7 +18,6 @@ use super::footnote_definition::footnote_definition; use super::greater_block::greater_block; use super::horizontal_rule::horizontal_rule; use super::keyword::affiliated_keyword; -use super::keyword::affiliated_keyword_as_regular_keyword; use super::keyword::keyword; use super::latex_environment::latex_environment; use super::lesser_block::comment_block; @@ -39,6 +35,8 @@ use crate::context::RefContext; use crate::error::CustomError; use crate::error::MyError; use crate::error::Res; +use crate::parser::macros::ak_element; +use crate::parser::macros::element; use crate::parser::table::org_mode_table; use crate::types::Element; @@ -60,117 +58,204 @@ fn _element<'b, 'g, 'r, 's>( input: OrgSource<'s>, can_be_paragraph: bool, ) -> Res, Element<'s>> { - let plain_list_matcher = parser_with_context!(plain_list)(context); - let greater_block_matcher = parser_with_context!(greater_block)(context); - let dynamic_block_matcher = parser_with_context!(dynamic_block)(context); - let footnote_definition_matcher = parser_with_context!(footnote_definition)(context); - let comment_matcher = parser_with_context!(comment)(context); - let drawer_matcher = parser_with_context!(drawer)(context); - let table_matcher = parser_with_context!(org_mode_table)(context); - let verse_block_matcher = parser_with_context!(verse_block)(context); - let comment_block_matcher = parser_with_context!(comment_block)(context); - let example_block_matcher = parser_with_context!(example_block)(context); - let export_block_matcher = parser_with_context!(export_block)(context); - let src_block_matcher = parser_with_context!(src_block)(context); - let clock_matcher = parser_with_context!(clock)(context); - let diary_sexp_matcher = parser_with_context!(diary_sexp)(context); - let fixed_width_area_matcher = parser_with_context!(fixed_width_area)(context); - let horizontal_rule_matcher = parser_with_context!(horizontal_rule)(context); - let keyword_matcher = parser_with_context!(keyword)(context); - let babel_keyword_matcher = parser_with_context!(babel_call)(context); - let paragraph_matcher = parser_with_context!(paragraph)(context); - let latex_environment_matcher = parser_with_context!(latex_environment)(context); + let (post_affiliated_keywords_input, affiliated_keywords) = many0(affiliated_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(); + let mut affiliated_keywords = affiliated_keywords.into_iter(); - 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)? - }; + ak_element!( + plain_list, + &mut affiliated_keywords, + post_affiliated_keywords_input, + context, + input, + Element::PlainList + ); - 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(); + ak_element!( + greater_block, + &mut affiliated_keywords, + post_affiliated_keywords_input, + context, + input + ); - 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; - } - } + ak_element!( + dynamic_block, + &mut affiliated_keywords, + post_affiliated_keywords_input, + context, + input, + Element::DynamicBlock + ); - if maybe_element.is_none() { - #[cfg(feature = "tracing")] - let span = span!( - tracing::Level::DEBUG, - "Affiliated keyword as regular keyword." + ak_element!( + footnote_definition, + &mut affiliated_keywords, + post_affiliated_keywords_input, + context, + input, + Element::FootnoteDefinition + ); + + element!(comment, context, input, Element::Comment); + + ak_element!( + drawer, + &mut affiliated_keywords, + post_affiliated_keywords_input, + context, + input, + Element::Drawer + ); + + ak_element!( + org_mode_table, + &mut affiliated_keywords, + post_affiliated_keywords_input, + context, + input, + Element::Table + ); + + ak_element!( + verse_block, + &mut affiliated_keywords, + post_affiliated_keywords_input, + context, + input, + Element::VerseBlock + ); + + ak_element!( + comment_block, + &mut affiliated_keywords, + post_affiliated_keywords_input, + context, + input, + Element::CommentBlock + ); + + ak_element!( + example_block, + &mut affiliated_keywords, + post_affiliated_keywords_input, + context, + input, + Element::ExampleBlock + ); + + ak_element!( + export_block, + &mut affiliated_keywords, + post_affiliated_keywords_input, + context, + input, + Element::ExportBlock + ); + + ak_element!( + src_block, + &mut affiliated_keywords, + post_affiliated_keywords_input, + context, + input, + Element::SrcBlock + ); + + element!(clock, context, input, Element::Clock); + + ak_element!( + diary_sexp, + &mut affiliated_keywords, + post_affiliated_keywords_input, + context, + input, + Element::DiarySexp + ); + + ak_element!( + fixed_width_area, + &mut affiliated_keywords, + post_affiliated_keywords_input, + context, + input, + Element::FixedWidthArea + ); + + ak_element!( + horizontal_rule, + &mut affiliated_keywords, + post_affiliated_keywords_input, + context, + input, + Element::HorizontalRule + ); + + ak_element!( + latex_environment, + &mut affiliated_keywords, + post_affiliated_keywords_input, + context, + input, + Element::LatexEnvironment + ); + + ak_element!( + babel_call, + &mut affiliated_keywords, + post_affiliated_keywords_input, + context, + input, + Element::BabelCall + ); + + // Keyword with affiliated keywords + ak_element!( + keyword, + &mut affiliated_keywords, + post_affiliated_keywords_input, + context, + input, + Element::Keyword + ); + + if can_be_paragraph { + // Paragraph with affiliated keyword + ak_element!( + paragraph, + &mut affiliated_keywords, + post_affiliated_keywords_input, + context, + input, + Element::Paragraph ); - #[cfg(feature = "tracing")] - let _enter = span.enter(); - - let (remain, kw) = opt(map( - parser_with_context!(affiliated_keyword_as_regular_keyword)(context), - 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." + // Keyword without affiliated keywords + ak_element!( + keyword, + std::iter::empty(), + input, + context, + input, + Element::Keyword + ); + + if can_be_paragraph { + // Paragraph without affiliated keyword + ak_element!( + paragraph, + std::iter::empty(), + input, + context, + input, + Element::Paragraph ); - #[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 element = maybe_element.expect("The above if-statement ensures this is Some()."); - - Ok((remaining, element)) + Err(nom::Err::Error(CustomError::MyError(MyError( + "No element.", + )))) } pub(crate) const fn detect_element( diff --git a/src/parser/fixed_width_area.rs b/src/parser/fixed_width_area.rs index 0260784d..642e824d 100644 --- a/src/parser/fixed_width_area.rs +++ b/src/parser/fixed_width_area.rs @@ -21,16 +21,21 @@ use crate::parser::util::exit_matcher_parser; use crate::parser::util::get_consumed; use crate::parser::util::start_of_line; use crate::types::FixedWidthArea; +use crate::types::Keyword; #[cfg_attr( feature = "tracing", tracing::instrument(ret, level = "debug", skip(context)) )] -pub(crate) fn fixed_width_area<'b, 'g, 'r, 's>( +pub(crate) fn fixed_width_area<'b, 'g, 'r, 's, AK>( + affiliated_keywords: AK, + remaining: OrgSource<'s>, context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, -) -> Res, FixedWidthArea<'s>> { - let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?; +) -> Res, FixedWidthArea<'s>> +where + AK: IntoIterator>, +{ let fixed_width_area_line_matcher = parser_with_context!(fixed_width_area_line)(context); let exit_matcher = parser_with_context!(exit_matcher_parser)(context); let (remaining, first_line) = fixed_width_area_line_matcher(remaining)?; diff --git a/src/parser/footnote_definition.rs b/src/parser/footnote_definition.rs index 1ce9aef9..8b669cd1 100644 --- a/src/parser/footnote_definition.rs +++ b/src/parser/footnote_definition.rs @@ -33,21 +33,26 @@ use crate::parser::util::immediate_in_section; use crate::parser::util::maybe_consume_trailing_whitespace; use crate::parser::util::start_of_line; use crate::types::FootnoteDefinition; +use crate::types::Keyword; #[cfg_attr( feature = "tracing", tracing::instrument(ret, level = "debug", skip(context)) )] -pub(crate) fn footnote_definition<'b, 'g, 'r, 's>( +pub(crate) fn footnote_definition<'b, 'g, 'r, 's, AK>( + affiliated_keywords: AK, + remaining: OrgSource<'s>, context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, -) -> Res, FootnoteDefinition<'s>> { +) -> Res, FootnoteDefinition<'s>> +where + AK: IntoIterator>, +{ if immediate_in_section(context, "footnote definition") { return Err(nom::Err::Error(CustomError::MyError(MyError( "Cannot nest objects of the same element".into(), )))); } - let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?; start_of_line(remaining)?; // Cannot be indented. let (remaining, (_, lbl, _, _, _)) = tuple(( diff --git a/src/parser/greater_block.rs b/src/parser/greater_block.rs index 17ba0723..daf59dfc 100644 --- a/src/parser/greater_block.rs +++ b/src/parser/greater_block.rs @@ -18,7 +18,6 @@ use nom::sequence::preceded; use nom::sequence::tuple; use super::affiliated_keyword::parse_affiliated_keywords; -use super::keyword::affiliated_keyword; use super::org_source::OrgSource; use super::util::in_section; use super::util::maybe_consume_trailing_whitespace_if_not_exiting; @@ -48,12 +47,15 @@ use crate::types::SpecialBlock; feature = "tracing", tracing::instrument(ret, level = "debug", skip(context)) )] -pub(crate) fn greater_block<'b, 'g, 'r, 's>( - context: RefContext<'b, 'g, 'r, 's>, +pub(crate) fn greater_block<'b, 'g, 'r, 's, AK>( + affiliated_keywords: AK, input: OrgSource<'s>, -) -> Res, Element<'s>> { - let pre_affiliated_keywords_input = input; - let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; + context: RefContext<'b, 'g, 'r, 's>, + pre_affiliated_keywords_input: OrgSource<'s>, +) -> Res, Element<'s>> +where + AK: IntoIterator>, +{ start_of_line(input)?; let (remaining, _leading_whitespace) = space0(input)?; let (remaining, (_begin, name)) = tuple(( @@ -93,12 +95,15 @@ pub(crate) fn greater_block<'b, 'g, 'r, 's>( feature = "tracing", tracing::instrument(ret, level = "debug", skip(context)) )] -fn center_block<'b, 'g, 'r, 's>( +fn center_block<'b, 'g, 'r, 's, AK>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, pre_affiliated_keywords_input: OrgSource<'s>, - affiliated_keywords: Vec>, -) -> Res, Element<'s>> { + affiliated_keywords: AK, +) -> Res, Element<'s>> +where + AK: IntoIterator>, +{ let (remaining, (source, children)) = greater_block_body( context, input, @@ -123,12 +128,15 @@ fn center_block<'b, 'g, 'r, 's>( feature = "tracing", tracing::instrument(ret, level = "debug", skip(context)) )] -fn quote_block<'b, 'g, 'r, 's>( +fn quote_block<'b, 'g, 'r, 's, AK>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, pre_affiliated_keywords_input: OrgSource<'s>, - affiliated_keywords: Vec>, -) -> Res, Element<'s>> { + affiliated_keywords: AK, +) -> Res, Element<'s>> +where + AK: IntoIterator>, +{ let (remaining, (source, children)) = greater_block_body( context, input, @@ -149,15 +157,18 @@ fn quote_block<'b, 'g, 'r, 's>( )) } -fn special_block<'s>( +fn special_block<'s, AK>( name: &'s str, ) -> impl for<'b, 'g, 'r> Fn( RefContext<'b, 'g, 'r, 's>, OrgSource<'s>, OrgSource<'s>, - Vec>, + AK, ) -> Res, Element<'s>> - + 's { + + 's +where + AK: IntoIterator>, +{ let context_name = format!("special block {}", name); move |context, input, pre_affiliated_keywords_input, affiliated_keywords| { _special_block( @@ -175,14 +186,17 @@ fn special_block<'s>( feature = "tracing", tracing::instrument(ret, level = "debug", skip(context)) )] -fn _special_block<'c, 'b, 'g, 'r, 's>( +fn _special_block<'c, 'b, 'g, 'r, 's, AK>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, pre_affiliated_keywords_input: OrgSource<'s>, name: &'s str, context_name: &'c str, - affiliated_keywords: Vec>, -) -> Res, Element<'s>> { + affiliated_keywords: AK, +) -> Res, Element<'s>> +where + AK: IntoIterator>, +{ let (remaining, parameters) = opt(tuple((space1, parameters)))(input)?; let (remaining, (source, children)) = greater_block_body( context, diff --git a/src/parser/horizontal_rule.rs b/src/parser/horizontal_rule.rs index d331f91b..ec45d95c 100644 --- a/src/parser/horizontal_rule.rs +++ b/src/parser/horizontal_rule.rs @@ -5,12 +5,10 @@ use nom::character::complete::space0; use nom::combinator::eof; use nom::combinator::recognize; use nom::combinator::verify; -use nom::multi::many0; use nom::multi::many1_count; use nom::sequence::tuple; use super::affiliated_keyword::parse_affiliated_keywords; -use super::keyword::affiliated_keyword; use super::org_source::OrgSource; use super::util::get_consumed; use super::util::maybe_consume_trailing_whitespace_if_not_exiting; @@ -18,16 +16,21 @@ use crate::context::RefContext; use crate::error::Res; use crate::parser::util::start_of_line; use crate::types::HorizontalRule; +use crate::types::Keyword; #[cfg_attr( feature = "tracing", tracing::instrument(ret, level = "debug", skip(context)) )] -pub(crate) fn horizontal_rule<'b, 'g, 'r, 's>( +pub(crate) fn horizontal_rule<'b, 'g, 'r, 's, AK>( + affiliated_keywords: AK, + remaining: OrgSource<'s>, context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, -) -> Res, HorizontalRule<'s>> { - let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?; +) -> Res, HorizontalRule<'s>> +where + AK: IntoIterator>, +{ start_of_line(remaining)?; let (remaining, _rule) = recognize(tuple(( space0, diff --git a/src/parser/keyword.rs b/src/parser/keyword.rs index cb6ae152..24a5f891 100644 --- a/src/parser/keyword.rs +++ b/src/parser/keyword.rs @@ -13,7 +13,6 @@ use nom::combinator::not; use nom::combinator::peek; use nom::combinator::recognize; use nom::combinator::verify; -use nom::multi::many0; use nom::multi::many_till; use nom::sequence::tuple; @@ -94,11 +93,15 @@ fn _filtered_keyword<'s, F: Matcher>( feature = "tracing", tracing::instrument(ret, level = "debug", skip(context)) )] -pub(crate) fn keyword<'b, 'g, 'r, 's>( +pub(crate) fn keyword<'b, 'g, 'r, 's, AK>( + affiliated_keywords: AK, + remaining: OrgSource<'s>, context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, -) -> Res, Keyword<'s>> { - let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?; +) -> Res, Keyword<'s>> +where + AK: IntoIterator>, +{ let (remaining, mut kw) = filtered_keyword(regular_keyword_key)(remaining)?; let (remaining, _trailing_ws) = maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; @@ -109,22 +112,6 @@ pub(crate) fn keyword<'b, 'g, 'r, 's>( Ok((remaining, kw)) } -#[cfg_attr( - feature = "tracing", - tracing::instrument(ret, level = "debug", skip(context)) -)] -pub(crate) fn affiliated_keyword_as_regular_keyword<'b, 'g, 'r, 's>( - context: RefContext<'b, 'g, 'r, 's>, - input: OrgSource<'s>, -) -> Res, Keyword<'s>> { - let (remaining, mut kw) = affiliated_keyword(input)?; - let (remaining, _trailing_ws) = - maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; - let source = get_consumed(input, remaining); - kw.source = Into::<&str>::into(source); - Ok((remaining, kw)) -} - #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] pub(crate) fn affiliated_keyword<'s>(input: OrgSource<'s>) -> Res, Keyword<'s>> { filtered_keyword(affiliated_key)(input) diff --git a/src/parser/latex_environment.rs b/src/parser/latex_environment.rs index 44002f51..af41d91d 100644 --- a/src/parser/latex_environment.rs +++ b/src/parser/latex_environment.rs @@ -8,12 +8,10 @@ use nom::character::complete::space0; use nom::combinator::eof; use nom::combinator::peek; use nom::combinator::recognize; -use nom::multi::many0; use nom::multi::many_till; use nom::sequence::tuple; use super::affiliated_keyword::parse_affiliated_keywords; -use super::keyword::affiliated_keyword; use super::org_source::OrgSource; use super::util::get_consumed; use super::util::maybe_consume_trailing_whitespace_if_not_exiting; @@ -26,17 +24,22 @@ use crate::context::RefContext; use crate::error::Res; use crate::parser::util::exit_matcher_parser; use crate::parser::util::start_of_line; +use crate::types::Keyword; use crate::types::LatexEnvironment; #[cfg_attr( feature = "tracing", tracing::instrument(ret, level = "debug", skip(context)) )] -pub(crate) fn latex_environment<'b, 'g, 'r, 's>( +pub(crate) fn latex_environment<'b, 'g, 'r, 's, AK>( + affiliated_keywords: AK, + remaining: OrgSource<'s>, context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, -) -> Res, LatexEnvironment<'s>> { - let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?; +) -> Res, LatexEnvironment<'s>> +where + AK: IntoIterator>, +{ let value_start = remaining; start_of_line(remaining)?; let (remaining, _leading_whitespace) = space0(remaining)?; diff --git a/src/parser/lesser_block.rs b/src/parser/lesser_block.rs index 2870c82c..37f30918 100644 --- a/src/parser/lesser_block.rs +++ b/src/parser/lesser_block.rs @@ -13,13 +13,11 @@ use nom::combinator::opt; use nom::combinator::peek; use nom::combinator::recognize; use nom::combinator::verify; -use nom::multi::many0; use nom::multi::many_till; use nom::sequence::tuple; use nom::InputTake; use super::affiliated_keyword::parse_affiliated_keywords; -use super::keyword::affiliated_keyword; use super::org_source::OrgSource; use super::util::maybe_consume_trailing_whitespace_if_not_exiting; use crate::context::parser_with_context; @@ -41,6 +39,7 @@ use crate::types::CharOffsetInLine; use crate::types::CommentBlock; use crate::types::ExampleBlock; use crate::types::ExportBlock; +use crate::types::Keyword; use crate::types::LineNumber; use crate::types::Object; use crate::types::PlainText; @@ -53,11 +52,15 @@ use crate::types::VerseBlock; feature = "tracing", tracing::instrument(ret, level = "debug", skip(context)) )] -pub(crate) fn verse_block<'b, 'g, 'r, 's>( +pub(crate) fn verse_block<'b, 'g, 'r, 's, AK>( + affiliated_keywords: AK, + remaining: OrgSource<'s>, context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, -) -> Res, VerseBlock<'s>> { - let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?; +) -> Res, VerseBlock<'s>> +where + AK: IntoIterator>, +{ let (remaining, _) = lesser_block_begin("verse")(context, remaining)?; let (remaining, parameters) = opt(tuple((space1, data)))(remaining)?; let (remaining, _nl) = recognize(tuple((space0, line_ending)))(remaining)?; @@ -117,11 +120,15 @@ pub(crate) fn verse_block<'b, 'g, 'r, 's>( feature = "tracing", tracing::instrument(ret, level = "debug", skip(context)) )] -pub(crate) fn comment_block<'b, 'g, 'r, 's>( +pub(crate) fn comment_block<'b, 'g, 'r, 's, AK>( + affiliated_keywords: AK, + remaining: OrgSource<'s>, context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, -) -> Res, CommentBlock<'s>> { - let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?; +) -> Res, CommentBlock<'s>> +where + AK: IntoIterator>, +{ let (remaining, _) = lesser_block_begin("comment")(context, remaining)?; let (remaining, _parameters) = opt(tuple((space1, data)))(remaining)?; let (remaining, _nl) = recognize(tuple((space0, line_ending)))(remaining)?; @@ -161,11 +168,15 @@ pub(crate) fn comment_block<'b, 'g, 'r, 's>( feature = "tracing", tracing::instrument(ret, level = "debug", skip(context)) )] -pub(crate) fn example_block<'b, 'g, 'r, 's>( +pub(crate) fn example_block<'b, 'g, 'r, 's, AK>( + affiliated_keywords: AK, + remaining: OrgSource<'s>, context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, -) -> Res, ExampleBlock<'s>> { - let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?; +) -> Res, ExampleBlock<'s>> +where + AK: IntoIterator>, +{ let (remaining, _) = lesser_block_begin("example")(context, remaining)?; let (remaining, parameters) = opt(alt(( map(tuple((space1, example_switches)), |(_, switches)| switches), @@ -238,11 +249,15 @@ pub(crate) fn example_block<'b, 'g, 'r, 's>( feature = "tracing", tracing::instrument(ret, level = "debug", skip(context)) )] -pub(crate) fn export_block<'b, 'g, 'r, 's>( +pub(crate) fn export_block<'b, 'g, 'r, 's, AK>( + affiliated_keywords: AK, + remaining: OrgSource<'s>, context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, -) -> Res, ExportBlock<'s>> { - let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?; +) -> Res, ExportBlock<'s>> +where + AK: IntoIterator>, +{ let (remaining, _) = lesser_block_begin("export")(context, remaining)?; // https://orgmode.org/worg/org-syntax.html#Blocks claims that export blocks must have a single word for data but testing shows no data and multi-word data still parses as an export block. let (remaining, export_type) = opt(map( @@ -290,11 +305,15 @@ pub(crate) fn export_block<'b, 'g, 'r, 's>( feature = "tracing", tracing::instrument(ret, level = "debug", skip(context)) )] -pub(crate) fn src_block<'b, 'g, 'r, 's>( +pub(crate) fn src_block<'b, 'g, 'r, 's, AK>( + affiliated_keywords: AK, + remaining: OrgSource<'s>, context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, -) -> Res, SrcBlock<'s>> { - let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?; +) -> Res, SrcBlock<'s>> +where + AK: IntoIterator>, +{ let (remaining, _) = lesser_block_begin("src")(context, remaining)?; // https://orgmode.org/worg/org-syntax.html#Blocks claims that data is mandatory and must follow the LANGUAGE SWITCHES ARGUMENTS pattern but testing has shown that no data and incorrect data here will still parse to a src block. let (remaining, language) = diff --git a/src/parser/macros.rs b/src/parser/macros.rs new file mode 100644 index 00000000..f2e07bab --- /dev/null +++ b/src/parser/macros.rs @@ -0,0 +1,40 @@ +/// Parse an element that has affiliated keywords. +macro_rules! ak_element { + ($parser:ident, $affiliated_keywords:expr, $post_affiliated_keywords_input: expr, $context: expr, $input: expr, $wrapper: expr) => { + if let Ok((remaining, ele)) = $parser( + $affiliated_keywords, + $post_affiliated_keywords_input, + $context, + $input, + ) { + return Ok((remaining, $wrapper(ele))); + } + }; + ($parser:ident, $affiliated_keywords:expr, $post_affiliated_keywords_input: expr, $context: expr, $input: expr) => { + if let Ok((remaining, ele)) = $parser( + $affiliated_keywords, + $post_affiliated_keywords_input, + $context, + $input, + ) { + return Ok((remaining, ele)); + } + }; +} + +pub(crate) use ak_element; + +macro_rules! element { + ($parser:ident, $context: expr, $input: expr, $wrapper: expr) => { + if let Ok((remaining, ele)) = $parser($context, $input) { + return Ok((remaining, $wrapper(ele))); + } + }; + ($parser:ident, $context: expr, $input: expr) => { + if let Ok((remaining, ele)) = $parser($context, $input) { + return Ok((remaining, ele)); + } + }; +} + +pub(crate) use element; diff --git a/src/parser/mod.rs b/src/parser/mod.rs index c9c2e6e3..dff7f192 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -27,6 +27,7 @@ mod latex_environment; mod latex_fragment; mod lesser_block; mod line_break; +mod macros; mod object_parser; mod org_macro; mod org_source; diff --git a/src/parser/paragraph.rs b/src/parser/paragraph.rs index 90331eb3..cd8ce7e2 100644 --- a/src/parser/paragraph.rs +++ b/src/parser/paragraph.rs @@ -2,14 +2,12 @@ use nom::branch::alt; use nom::combinator::eof; use nom::combinator::recognize; use nom::combinator::verify; -use nom::multi::many0; use nom::multi::many1; use nom::multi::many_till; use nom::sequence::tuple; use super::affiliated_keyword::parse_affiliated_keywords; use super::element_parser::detect_element; -use super::keyword::affiliated_keyword; use super::org_source::OrgSource; use super::util::blank_line; use super::util::get_consumed; @@ -23,17 +21,22 @@ use crate::error::Res; use crate::parser::object_parser::standard_set_object; use crate::parser::util::exit_matcher_parser; use crate::parser::util::start_of_line; +use crate::types::Keyword; use crate::types::Paragraph; #[cfg_attr( feature = "tracing", tracing::instrument(ret, level = "debug", skip(context)) )] -pub(crate) fn paragraph<'b, 'g, 'r, 's>( +pub(crate) fn paragraph<'b, 'g, 'r, 's, AK>( + affiliated_keywords: AK, + remaining: OrgSource<'s>, context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, -) -> Res, Paragraph<'s>> { - let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?; +) -> Res, Paragraph<'s>> +where + AK: IntoIterator>, +{ let contexts = [ContextElement::ExitMatcherNode(ExitMatcherNode { class: ExitClass::Gamma, exit_matcher: ¶graph_end, diff --git a/src/parser/plain_list.rs b/src/parser/plain_list.rs index 3035f6bc..f3148f24 100644 --- a/src/parser/plain_list.rs +++ b/src/parser/plain_list.rs @@ -43,6 +43,7 @@ use crate::parser::util::org_space; use crate::parser::util::start_of_line; use crate::types::CheckboxType; use crate::types::IndentationLevel; +use crate::types::Keyword; use crate::types::Object; use crate::types::PlainList; use crate::types::PlainListItem; @@ -83,12 +84,15 @@ pub(crate) fn detect_plain_list<'b, 'g, 'r, 's>( feature = "tracing", tracing::instrument(ret, level = "debug", skip(context)) )] -pub(crate) fn plain_list<'b, 'g, 'r, 's>( +pub(crate) fn plain_list<'b, 'g, 'r, 's, AK>( + affiliated_keywords: AK, + remaining: OrgSource<'s>, context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, -) -> Res, PlainList<'s>> { - let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?; - +) -> Res, PlainList<'s>> +where + AK: IntoIterator>, +{ let contexts = [ ContextElement::Context("plain list"), ContextElement::ConsumeTrailingWhitespace(true), @@ -580,8 +584,8 @@ mod tests { let global_settings = GlobalSettings::default(); let initial_context = ContextElement::document_context(); let initial_context = Context::new(&global_settings, List::new(&initial_context)); - let plain_list_matcher = parser_with_context!(plain_list)(&initial_context); - let (remaining, result) = plain_list_matcher(input).unwrap(); + let (remaining, result) = + plain_list(std::iter::empty(), input, &initial_context, input).unwrap(); assert_eq!(Into::<&str>::into(remaining), ""); assert_eq!(result.get_standard_properties().get_source(), "1."); } @@ -592,8 +596,8 @@ mod tests { let global_settings = GlobalSettings::default(); let initial_context = ContextElement::document_context(); let initial_context = Context::new(&global_settings, List::new(&initial_context)); - let plain_list_matcher = parser_with_context!(plain_list)(&initial_context); - let (remaining, result) = plain_list_matcher(input).unwrap(); + let (remaining, result) = + plain_list(std::iter::empty(), input, &initial_context, input).unwrap(); assert_eq!(Into::<&str>::into(remaining), ""); assert_eq!(result.get_standard_properties().get_source(), "1. foo"); } @@ -605,8 +609,7 @@ mod tests { let global_settings = GlobalSettings::default(); let initial_context = ContextElement::document_context(); let initial_context = Context::new(&global_settings, List::new(&initial_context)); - let plain_list_matcher = parser_with_context!(plain_list)(&initial_context); - let result = plain_list_matcher(input); + let result = plain_list(std::iter::empty(), input, &initial_context, input); assert!(result.is_err()); } @@ -617,8 +620,7 @@ mod tests { let global_settings = GlobalSettings::default(); let initial_context = ContextElement::document_context(); let initial_context = Context::new(&global_settings, List::new(&initial_context)); - let plain_list_matcher = parser_with_context!(plain_list)(&initial_context); - let result = plain_list_matcher(input); + let result = plain_list(std::iter::empty(), input, &initial_context, input); assert!(result.is_ok()); } diff --git a/src/parser/table.rs b/src/parser/table.rs index 857a55de..e8f615da 100644 --- a/src/parser/table.rs +++ b/src/parser/table.rs @@ -29,6 +29,7 @@ use crate::context::RefContext; use crate::error::Res; use crate::parser::util::get_consumed; use crate::parser::util::start_of_line; +use crate::types::Keyword; use crate::types::Table; use crate::types::TableCell; use crate::types::TableRow; @@ -40,11 +41,15 @@ use crate::types::TableRow; feature = "tracing", tracing::instrument(ret, level = "debug", skip(context)) )] -pub(crate) fn org_mode_table<'b, 'g, 'r, 's>( +pub(crate) fn org_mode_table<'b, 'g, 'r, 's, AK>( + affiliated_keywords: AK, + remaining: OrgSource<'s>, context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, -) -> Res, Table<'s>> { - let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?; +) -> Res, Table<'s>> +where + AK: IntoIterator>, +{ start_of_line(remaining)?; peek(tuple((space0, tag("|"))))(remaining)?;