From e24b413cd0690faf1a996d14abc03c2b21046e22 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Fri, 21 Jul 2023 16:38:49 -0400 Subject: [PATCH] Finish first implementation of citation reference. --- src/parser/citation_reference.rs | 132 +++++++++++++++++++++++++++---- 1 file changed, 118 insertions(+), 14 deletions(-) diff --git a/src/parser/citation_reference.rs b/src/parser/citation_reference.rs index cc47254..d704088 100644 --- a/src/parser/citation_reference.rs +++ b/src/parser/citation_reference.rs @@ -1,3 +1,5 @@ +use nom::branch::alt; +use nom::bytes::complete::tag; use nom::character::complete::anychar; use nom::combinator::not; use nom::combinator::recognize; @@ -5,16 +7,20 @@ use nom::combinator::verify; use nom::multi::many1; use nom::multi::many_till; use nom::sequence::preceded; +use nom::sequence::tuple; use super::Context; +use crate::error::CustomError; use crate::error::Res; +use crate::parser::exiting::ExitClass; use crate::parser::object::CitationReference; use crate::parser::object_parser::minimal_set_object; use crate::parser::parser_context::CitationBracket; use crate::parser::parser_context::ContextElement; +use crate::parser::parser_context::ExitMatcherNode; use crate::parser::parser_with_context::parser_with_context; use crate::parser::util::exit_matcher_parser; -use crate::parser::util::not_yet_implemented; +use crate::parser::util::get_consumed; use crate::parser::util::WORD_CONSTITUENT_CHARACTERS; use crate::parser::Object; @@ -23,33 +29,73 @@ pub fn citation_reference<'r, 's>( context: Context<'r, 's>, input: &'s str, ) -> Res<&'s str, CitationReference<'s>> { - not_yet_implemented()?; - todo!() + let (remaining, _prefix) = parser_with_context!(key_prefix)(context)(input)?; + let (remaining, _key) = parser_with_context!(key)(context)(remaining)?; + let (remaining, _suffix) = parser_with_context!(key_suffix)(context)(remaining)?; + let source = get_consumed(input, remaining); + + Ok((remaining, CitationReference { source })) } #[tracing::instrument(ret, level = "debug")] fn key<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { - let (remaining, source) = recognize(many1(verify( - preceded( - not(parser_with_context!(exit_matcher_parser)(context)), - anychar, - ), - |c| WORD_CONSTITUENT_CHARACTERS.contains(*c) || "-.:?~`'/*@+|(){}<>&_^$#%~".contains(*c), + let (remaining, source) = recognize(tuple(( + tag("@"), + many1(verify( + preceded( + not(parser_with_context!(exit_matcher_parser)(context)), + anychar, + ), + |c| { + WORD_CONSTITUENT_CHARACTERS.contains(*c) || "-.:?~`'/*@+|(){}<>&_^$#%~".contains(*c) + }, + )), )))(input)?; Ok((remaining, source)) } #[tracing::instrument(ret, level = "debug")] -fn key_suffix<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Vec>> { - // TODO: Add to context with the depth object and an exit matcher +fn key_prefix<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Vec>> { + // TODO: I could insert CitationBracket entries in the context after each matched object to reduce the scanning done for counting brackets which should be more efficient. + let parser_context = context + .with_additional_node(ContextElement::CitationBracket(CitationBracket { + position: input, + depth: 0, + })) + .with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { + class: ExitClass::Beta, + exit_matcher: &key_prefix_end, + })); let (remaining, (children, _exit_contents)) = verify( many_till( - parser_with_context!(minimal_set_object)(context), - parser_with_context!(exit_matcher_parser)(context), + parser_with_context!(minimal_set_object)(&parser_context), + parser_with_context!(exit_matcher_parser)(&parser_context), ), |(children, _exit_contents)| !children.is_empty(), )(input)?; - todo!() + Ok((remaining, children)) +} + +#[tracing::instrument(ret, level = "debug")] +fn key_suffix<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Vec>> { + // TODO: I could insert CitationBracket entries in the context after each matched object to reduce the scanning done for counting brackets which should be more efficient. + let parser_context = context + .with_additional_node(ContextElement::CitationBracket(CitationBracket { + position: input, + depth: 0, + })) + .with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { + class: ExitClass::Beta, + exit_matcher: &key_suffix_end, + })); + let (remaining, (children, _exit_contents)) = verify( + many_till( + parser_with_context!(minimal_set_object)(&parser_context), + parser_with_context!(exit_matcher_parser)(&parser_context), + ), + |(children, _exit_contents)| !children.is_empty(), + )(input)?; + Ok((remaining, children)) } #[tracing::instrument(ret, level = "debug")] @@ -62,3 +108,61 @@ fn get_bracket_depth<'r, 's>(context: Context<'r, 's>) -> Option<&'r CitationBra } None } + +#[tracing::instrument(ret, level = "debug")] +fn key_prefix_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { + let context_depth = get_bracket_depth(context) + .expect("This function should only be called from inside a citation reference."); + let text_since_context_entry = get_consumed(context_depth.position, input); + let mut current_depth = context_depth.depth; + for c in text_since_context_entry.chars() { + match c { + '[' => { + current_depth += 1; + } + ']' if current_depth == 0 => { + panic!("Exceeded citation reference key prefix bracket depth.") + } + ']' if current_depth > 0 => { + current_depth -= 1; + } + _ => {} + } + } + if current_depth == 0 { + let close_bracket = tag::<&str, &str, CustomError<&str>>("]")(input); + if close_bracket.is_ok() { + return close_bracket; + } + } + alt((tag(";"), recognize(parser_with_context!(key)(context))))(input) +} + +#[tracing::instrument(ret, level = "debug")] +fn key_suffix_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { + let context_depth = get_bracket_depth(context) + .expect("This function should only be called from inside a citation reference."); + let text_since_context_entry = get_consumed(context_depth.position, input); + let mut current_depth = context_depth.depth; + for c in text_since_context_entry.chars() { + match c { + '[' => { + current_depth += 1; + } + ']' if current_depth == 0 => { + panic!("Exceeded citation reference key prefix bracket depth.") + } + ']' if current_depth > 0 => { + current_depth -= 1; + } + _ => {} + } + } + if current_depth == 0 { + let close_bracket = tag::<&str, &str, CustomError<&str>>("]")(input); + if close_bracket.is_ok() { + return close_bracket; + } + } + tag(";")(input) +}