use nom::branch::alt; use nom::bytes::complete::tag; use nom::character::complete::alpha1; use nom::character::complete::anychar; use nom::character::complete::line_ending; use nom::character::complete::none_of; use nom::character::complete::one_of; use nom::character::complete::space0; 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 super::org_source::OrgSource; use super::Context; use crate::error::CustomError; use crate::error::MyError; use crate::error::Res; use crate::parser::parser_with_context::parser_with_context; use crate::parser::util::exit_matcher_parser; use crate::parser::util::get_consumed; use crate::parser::LatexFragment; #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] pub fn latex_fragment<'r, 's>( context: Context<'r, 's>, input: OrgSource<'s>, ) -> Res, LatexFragment<'s>> { let (remaining, _) = alt(( parser_with_context!(raw_latex_fragment)(context), parser_with_context!(escaped_parenthesis_fragment)(context), parser_with_context!(escaped_bracket_fragment)(context), parser_with_context!(double_dollar_fragment)(context), parser_with_context!(dollar_char_fragment)(context), parser_with_context!(bordered_dollar_fragment)(context), ))(input)?; let (remaining, _) = space0(remaining)?; let source = get_consumed(input, remaining); Ok(( remaining, LatexFragment { source: source.into(), }, )) } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn raw_latex_fragment<'r, 's>( context: Context<'r, 's>, input: OrgSource<'s>, ) -> Res, OrgSource<'s>> { let (remaining, _) = tag("\\")(input)?; let (remaining, _) = name(context, remaining)?; let (remaining, _) = many0(parser_with_context!(brackets)(context))(remaining)?; let source = get_consumed(input, remaining); Ok((remaining, source)) } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn name<'r, 's>( _context: Context<'r, 's>, input: OrgSource<'s>, ) -> Res, OrgSource<'s>> { alpha1(input) } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn brackets<'r, 's>( context: Context<'r, 's>, input: OrgSource<'s>, ) -> Res, OrgSource<'s>> { let (remaining, body) = alt(( recognize(tuple(( tag("["), many_till( anychar, peek(alt(( parser_with_context!(exit_matcher_parser)(context), alt((recognize(one_of("{}[]")), line_ending)), ))), ), tag("]"), ))), recognize(tuple(( tag("{"), many_till( anychar, peek(alt(( parser_with_context!(exit_matcher_parser)(context), alt((recognize(one_of("{}")), line_ending)), ))), ), tag("}"), ))), ))(input)?; Ok((remaining, body)) } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn escaped_parenthesis_fragment<'r, 's>( context: Context<'r, 's>, input: OrgSource<'s>, ) -> Res, OrgSource<'s>> { let (remaining, _) = tag("\\(")(input)?; let (remaining, _) = recognize(many_till( anychar, peek(alt(( parser_with_context!(exit_matcher_parser)(context), tag("\\)"), ))), ))(remaining)?; let (remaining, _) = tag("\\)")(remaining)?; let source = get_consumed(input, remaining); Ok((remaining, source)) } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn escaped_bracket_fragment<'r, 's>( context: Context<'r, 's>, input: OrgSource<'s>, ) -> Res, OrgSource<'s>> { let (remaining, _) = tag("\\[")(input)?; let (remaining, _) = recognize(many_till( anychar, peek(alt(( parser_with_context!(exit_matcher_parser)(context), tag("\\]"), ))), ))(remaining)?; let (remaining, _) = tag("\\]")(remaining)?; let source = get_consumed(input, remaining); Ok((remaining, source)) } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn double_dollar_fragment<'r, 's>( context: Context<'r, 's>, input: OrgSource<'s>, ) -> Res, OrgSource<'s>> { // TODO: The documentation on the dollar sign versions is incomplete. Test to figure out what the real requirements are. For example, can this span more than 3 lines and can this contain a single $ since its terminated by $$? let (remaining, _) = tag("$$")(input)?; let (remaining, _) = recognize(many_till( anychar, peek(alt(( parser_with_context!(exit_matcher_parser)(context), tag("$"), ))), ))(remaining)?; let (remaining, _) = tag("$$")(remaining)?; let source = get_consumed(input, remaining); Ok((remaining, source)) } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn dollar_char_fragment<'r, 's>( context: Context<'r, 's>, input: OrgSource<'s>, ) -> Res, OrgSource<'s>> { let (_, _) = pre(context, input)?; let (remaining, _) = tag("$")(input)?; let (remaining, _) = verify(none_of(".,?;\""), |c| !c.is_whitespace())(remaining)?; let (remaining, _) = tag("$")(remaining)?; let (_, _) = peek(parser_with_context!(post)(context))(remaining)?; let source = get_consumed(input, remaining); Ok((remaining, source)) } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] pub fn pre<'r, 's>(_context: Context<'r, 's>, input: OrgSource<'s>) -> Res, ()> { let preceding_character = input.get_preceding_character(); if let Some('$') = preceding_character { return Err(nom::Err::Error(CustomError::MyError(MyError( "Not a valid pre character for dollar char fragment.".into(), )))); } Ok((input, ())) } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] pub fn post<'r, 's>(_context: Context<'r, 's>, input: OrgSource<'s>) -> Res, ()> { // TODO: What about eof? Test to find out. // TODO: Figure out which punctuation characters should be included. let (remaining, _) = alt((recognize(one_of(" \t-.,;:!?'\"")), line_ending))(input)?; Ok((remaining, ())) } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn bordered_dollar_fragment<'r, 's>( context: Context<'r, 's>, input: OrgSource<'s>, ) -> Res, OrgSource<'s>> { let (_, _) = pre(context, input)?; let (remaining, _) = tag("$")(input)?; // TODO: I'm assuming I should be peeking at the borders but the documentation is not clear. Test to figure out. let (_, _) = peek(parser_with_context!(open_border)(context))(remaining)?; let (remaining, _) = recognize(many_till( anychar, peek(alt(( parser_with_context!(exit_matcher_parser)(context), tag("$"), ))), ))(remaining)?; let (_, _) = peek(parser_with_context!(close_border)(context))(remaining)?; let (remaining, _) = tag("$")(remaining)?; let (_, _) = peek(parser_with_context!(post)(context))(remaining)?; let source = get_consumed(input, remaining); Ok((remaining, source)) } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] pub fn open_border<'r, 's>( _context: Context<'r, 's>, input: OrgSource<'s>, ) -> Res, OrgSource<'s>> { recognize(verify(none_of(".,;$"), |c| !c.is_whitespace()))(input) } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] pub fn close_border<'r, 's>( _context: Context<'r, 's>, input: OrgSource<'s>, ) -> Res, ()> { let preceding_character = input.get_preceding_character(); match preceding_character { Some(c) if !c.is_whitespace() && !".,;$".contains(c) => Ok((input, ())), _ => { return Err(nom::Err::Error(CustomError::MyError(MyError( "Not a valid pre character for dollar char fragment.".into(), )))); } } }