From 669da4073ea94746582d2bd254cb24faf50190b0 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Fri, 8 Sep 2023 17:45:49 -0400 Subject: [PATCH] Accept the end condition as a parameter to the plain text parser so it can adapt to the context. --- src/parser/object_parser.rs | 179 +++++++++++++++++++++++------------- src/parser/plain_text.rs | 49 ++++++---- src/parser/regular_link.rs | 4 +- 3 files changed, 152 insertions(+), 80 deletions(-) diff --git a/src/parser/object_parser.rs b/src/parser/object_parser.rs index b65f525d..5542618b 100644 --- a/src/parser/object_parser.rs +++ b/src/parser/object_parser.rs @@ -37,54 +37,11 @@ pub fn standard_set_object<'b, 'g, 'r, 's>( input: OrgSource<'s>, ) -> Res, Object<'s>> { let (remaining, object) = alt(( - map(parser_with_context!(timestamp)(context), Object::Timestamp), - map(parser_with_context!(subscript)(context), Object::Subscript), + parser_with_context!(standard_set_object_sans_plain_text)(context), map( - parser_with_context!(superscript)(context), - Object::Superscript, + parser_with_context!(plain_text(detect_standard_set_object_sans_plain_text))(context), + Object::PlainText, ), - map( - parser_with_context!(statistics_cookie)(context), - Object::StatisticsCookie, - ), - map(parser_with_context!(target)(context), Object::Target), - map(parser_with_context!(line_break)(context), Object::LineBreak), - map( - parser_with_context!(inline_source_block)(context), - Object::InlineSourceBlock, - ), - map( - parser_with_context!(inline_babel_call)(context), - Object::InlineBabelCall, - ), - map(parser_with_context!(citation)(context), Object::Citation), - map( - parser_with_context!(footnote_reference)(context), - Object::FootnoteReference, - ), - map( - parser_with_context!(export_snippet)(context), - Object::ExportSnippet, - ), - map(parser_with_context!(entity)(context), Object::Entity), - map( - parser_with_context!(latex_fragment)(context), - Object::LatexFragment, - ), - map(parser_with_context!(radio_link)(context), Object::RadioLink), - map( - parser_with_context!(radio_target)(context), - Object::RadioTarget, - ), - parser_with_context!(text_markup)(context), - map( - parser_with_context!(regular_link)(context), - Object::RegularLink, - ), - map(parser_with_context!(plain_link)(context), Object::PlainLink), - map(parser_with_context!(angle_link)(context), Object::AngleLink), - map(parser_with_context!(org_macro)(context), Object::OrgMacro), - map(parser_with_context!(plain_text)(context), Object::PlainText), ))(input)?; Ok((remaining, object)) } @@ -95,24 +52,17 @@ pub fn minimal_set_object<'b, 'g, 'r, 's>( input: OrgSource<'s>, ) -> Res, Object<'s>> { let (remaining, object) = alt(( - map(parser_with_context!(subscript)(context), Object::Subscript), + parser_with_context!(minimal_set_object_sans_plain_text)(context), map( - parser_with_context!(superscript)(context), - Object::Superscript, + parser_with_context!(plain_text(detect_minimal_set_object_sans_plain_text))(context), + Object::PlainText, ), - map(parser_with_context!(entity)(context), Object::Entity), - map( - parser_with_context!(latex_fragment)(context), - Object::LatexFragment, - ), - parser_with_context!(text_markup)(context), - map(parser_with_context!(plain_text)(context), Object::PlainText), ))(input)?; Ok((remaining, object)) } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] -pub fn any_object_except_plain_text<'b, 'g, 'r, 's>( +fn standard_set_object_sans_plain_text<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, Object<'s>> { @@ -169,14 +119,35 @@ pub fn any_object_except_plain_text<'b, 'g, 'r, 's>( } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] -pub fn detect_any_object_except_plain_text<'b, 'g, 'r, 's>( +fn minimal_set_object_sans_plain_text<'b, 'g, 'r, 's>( + context: RefContext<'b, 'g, 'r, 's>, + input: OrgSource<'s>, +) -> Res, Object<'s>> { + let (remaining, object) = alt(( + map(parser_with_context!(subscript)(context), Object::Subscript), + map( + parser_with_context!(superscript)(context), + Object::Superscript, + ), + map(parser_with_context!(entity)(context), Object::Entity), + map( + parser_with_context!(latex_fragment)(context), + Object::LatexFragment, + ), + parser_with_context!(text_markup)(context), + ))(input)?; + Ok((remaining, object)) +} + +#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] +pub fn detect_standard_set_object_sans_plain_text<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, ()> { if detect_subscript_or_superscript(input).is_ok() { return Ok((input, ())); } - if any_object_except_plain_text(context, input).is_ok() { + if standard_set_object_sans_plain_text(context, input).is_ok() { return Ok((input, ())); } @@ -186,7 +157,42 @@ pub fn detect_any_object_except_plain_text<'b, 'g, 'r, 's>( } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] -pub fn regular_link_description_object_set<'b, 'g, 'r, 's>( +fn detect_minimal_set_object_sans_plain_text<'b, 'g, 'r, 's>( + context: RefContext<'b, 'g, 'r, 's>, + input: OrgSource<'s>, +) -> Res, ()> { + if detect_subscript_or_superscript(input).is_ok() { + return Ok((input, ())); + } + if minimal_set_object_sans_plain_text(context, input).is_ok() { + return Ok((input, ())); + } + + return Err(nom::Err::Error(CustomError::MyError(MyError( + "No object detected.".into(), + )))); +} + +#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] +pub fn regular_link_description_set_object<'b, 'g, 'r, 's>( + context: RefContext<'b, 'g, 'r, 's>, + input: OrgSource<'s>, +) -> Res, Object<'s>> { + // TODO: It can also contain another link, but only when it is a plain or angle link. It can contain square brackets, but not ]] + let (remaining, object) = alt(( + parser_with_context!(regular_link_description_set_object_sans_plain_text)(context), + map( + parser_with_context!(plain_text( + detect_regular_link_description_set_object_sans_plain_text + ))(context), + Object::PlainText, + ), + ))(input)?; + Ok((remaining, object)) +} + +#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] +fn regular_link_description_set_object_sans_plain_text<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, Object<'s>> { @@ -209,15 +215,47 @@ pub fn regular_link_description_object_set<'b, 'g, 'r, 's>( Object::InlineBabelCall, ), map(parser_with_context!(org_macro)(context), Object::OrgMacro), - parser_with_context!(minimal_set_object)(context), + parser_with_context!(minimal_set_object_sans_plain_text)(context), ))(input)?; Ok((remaining, object)) } +#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] +pub fn detect_regular_link_description_set_object_sans_plain_text<'b, 'g, 'r, 's>( + context: RefContext<'b, 'g, 'r, 's>, + input: OrgSource<'s>, +) -> Res, ()> { + if detect_subscript_or_superscript(input).is_ok() { + return Ok((input, ())); + } + if regular_link_description_set_object_sans_plain_text(context, input).is_ok() { + return Ok((input, ())); + } + + return Err(nom::Err::Error(CustomError::MyError(MyError( + "No object detected.".into(), + )))); +} + #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] pub fn table_cell_set_object<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, +) -> Res, Object<'s>> { + let (remaining, object) = alt(( + parser_with_context!(table_cell_set_object_sans_plain_text)(context), + map( + parser_with_context!(plain_text(detect_table_cell_set_object_sans_plain_text))(context), + Object::PlainText, + ), + ))(input)?; + Ok((remaining, object)) +} + +#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] +pub fn table_cell_set_object_sans_plain_text<'b, 'g, 'r, 's>( + context: RefContext<'b, 'g, 'r, 's>, + input: OrgSource<'s>, ) -> Res, Object<'s>> { let (remaining, object) = alt(( map(parser_with_context!(citation)(context), Object::Citation), @@ -243,7 +281,24 @@ pub fn table_cell_set_object<'b, 'g, 'r, 's>( ), map(parser_with_context!(target)(context), Object::Target), map(parser_with_context!(timestamp)(context), Object::Timestamp), - parser_with_context!(minimal_set_object)(context), + parser_with_context!(minimal_set_object_sans_plain_text)(context), ))(input)?; Ok((remaining, object)) } + +#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] +pub fn detect_table_cell_set_object_sans_plain_text<'b, 'g, 'r, 's>( + context: RefContext<'b, 'g, 'r, 's>, + input: OrgSource<'s>, +) -> Res, ()> { + if detect_subscript_or_superscript(input).is_ok() { + return Ok((input, ())); + } + if table_cell_set_object_sans_plain_text(context, input).is_ok() { + return Ok((input, ())); + } + + return Err(nom::Err::Error(CustomError::MyError(MyError( + "No object detected.".into(), + )))); +} diff --git a/src/parser/plain_text.rs b/src/parser/plain_text.rs index e57f4d1a..b50bd139 100644 --- a/src/parser/plain_text.rs +++ b/src/parser/plain_text.rs @@ -7,7 +7,6 @@ use nom::combinator::recognize; use nom::combinator::verify; use nom::multi::many_till; -use super::object_parser::detect_any_object_except_plain_text; use super::org_source::OrgSource; use super::radio_link::RematchObject; use super::util::exit_matcher_parser; @@ -17,17 +16,42 @@ use crate::error::Res; use crate::types::Object; use crate::types::PlainText; -#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] -pub fn plain_text<'b, 'g, 'r, 's>( +pub fn plain_text( + end_condition: F, +) -> impl for<'b, 'g, 'r, 's> Fn( + RefContext<'b, 'g, 'r, 's>, + OrgSource<'s>, +) -> Res, PlainText<'s>> +where + F: for<'bb, 'gg, 'rr, 'ss> Fn( + RefContext<'bb, 'gg, 'rr, 'ss>, + OrgSource<'ss>, + ) -> Res, ()>, +{ + move |context, input| _plain_text(&end_condition, context, input) +} + +#[cfg_attr( + feature = "tracing", + tracing::instrument(ret, level = "debug", skip(end_condition)) +)] +fn _plain_text<'b, 'g, 'r, 's, F>( + end_condition: F, context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, -) -> Res, PlainText<'s>> { +) -> Res, PlainText<'s>> +where + F: for<'bb, 'gg, 'rr, 'ss> Fn( + RefContext<'bb, 'gg, 'rr, 'ss>, + OrgSource<'ss>, + ) -> Res, ()>, +{ let (remaining, source) = recognize(verify( many_till( anychar, peek(alt(( parser_with_context!(exit_matcher_parser)(context), - parser_with_context!(plain_text_end)(context), + recognize(parser_with_context!(end_condition)(context)), ))), ), |(children, _exit_contents)| !children.is_empty(), @@ -41,16 +65,6 @@ pub fn plain_text<'b, 'g, 'r, 's>( )) } -#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] -fn plain_text_end<'b, 'g, 'r, 's>( - context: RefContext<'b, 'g, 'r, 's>, - input: OrgSource<'s>, -) -> Res, OrgSource<'s>> { - recognize(parser_with_context!(detect_any_object_except_plain_text)( - context, - ))(input) -} - impl<'x> RematchObject<'x> for PlainText<'x> { #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn rematch_object<'b, 'g, 'r, 's>( @@ -75,6 +89,7 @@ mod tests { use crate::context::ContextElement; use crate::context::GlobalSettings; use crate::context::List; + use crate::parser::object_parser::detect_standard_set_object_sans_plain_text; use crate::types::Source; #[test] @@ -83,7 +98,9 @@ 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_text_matcher = parser_with_context!(plain_text)(&initial_context); + let plain_text_matcher = parser_with_context!(plain_text( + detect_standard_set_object_sans_plain_text + ))(&initial_context); let (remaining, result) = map(plain_text_matcher, Object::PlainText)(input).unwrap(); assert_eq!(Into::<&str>::into(remaining), ""); assert_eq!(result.get_source(), Into::<&str>::into(input)); diff --git a/src/parser/regular_link.rs b/src/parser/regular_link.rs index 615e77f5..e959a28f 100644 --- a/src/parser/regular_link.rs +++ b/src/parser/regular_link.rs @@ -6,7 +6,7 @@ use nom::character::complete::one_of; use nom::combinator::verify; use nom::multi::many_till; -use super::object_parser::regular_link_description_object_set; +use super::object_parser::regular_link_description_set_object; use super::org_source::OrgSource; use super::util::exit_matcher_parser; use super::util::get_consumed; @@ -99,7 +99,7 @@ pub fn description<'b, 'g, 'r, 's>( let parser_context = context.with_additional_node(&parser_context); let (remaining, (children, _exit_contents)) = verify( many_till( - parser_with_context!(regular_link_description_object_set)(&parser_context), + parser_with_context!(regular_link_description_set_object)(&parser_context), parser_with_context!(exit_matcher_parser)(&parser_context), ), |(children, _exit_contents)| !children.is_empty(),