Switch citations to using bracket depth from OrgSource instead of from the context.
This is for the same reasons as footnote references.
This commit is contained in:
		
							parent
							
								
									ec813e3b3f
								
							
						
					
					
						commit
						a46b358549
					
				
							
								
								
									
										22
									
								
								org_mode_samples/object/citation/balanced_brackets.org
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								org_mode_samples/object/citation/balanced_brackets.org
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | ||||
| # Extra open | ||||
| [cite/a/b-_/foo:unbalancedglobal[prefix;keyprefix @foo keysuffix;globalsuffix] | ||||
| 
 | ||||
| [cite/a/b-_/foo:globalprefix;unbalancedkey[prefix @foo keysuffix;globalsuffix] | ||||
| 
 | ||||
| [cite/a/b-_/foo:globalprefix;keyprefix @foo unbalancedkey[suffix;globalsuffix] | ||||
| 
 | ||||
| [cite/a/b-_/foo:globalprefix;keyprefix @foo keysuffix;unbalancedglobal[suffix] | ||||
| 
 | ||||
| 
 | ||||
| # Extra close | ||||
| [cite/a/b-_/foo:unbalancedglobal]prefix;keyprefix @foo keysuffix;globalsuffix] | ||||
| 
 | ||||
| [cite/a/b-_/foo:globalprefix;unbalancedkey]prefix @foo keysuffix;globalsuffix] | ||||
| 
 | ||||
| [cite/a/b-_/foo:globalprefix;keyprefix @foo unbalancedkey]suffix;globalsuffix] | ||||
| 
 | ||||
| [cite/a/b-_/foo:globalprefix;keyprefix @foo keysuffix;unbalancedglobal]suffix] | ||||
| 
 | ||||
| 
 | ||||
| # balanced: | ||||
| [cite/a/b-_/foo:gl[obalpref]ix;ke[ypref]ix @foo ke[ysuff]ix;gl[obalsuff]ix] | ||||
| @ -11,17 +11,16 @@ use nom::multi::many_till; | ||||
| use nom::multi::separated_list1; | ||||
| use nom::sequence::tuple; | ||||
| 
 | ||||
| use super::citation_reference::must_balance_bracket; | ||||
| use super::org_source::OrgSource; | ||||
| use super::Context; | ||||
| use crate::error::CustomError; | ||||
| use crate::error::Res; | ||||
| use crate::parser::citation_reference::citation_reference; | ||||
| use crate::parser::citation_reference::citation_reference_key; | ||||
| use crate::parser::citation_reference::get_bracket_depth; | ||||
| use crate::parser::exiting::ExitClass; | ||||
| use crate::parser::object::Citation; | ||||
| use crate::parser::object_parser::standard_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; | ||||
| @ -38,13 +37,15 @@ pub fn citation<'r, 's>( | ||||
|     let (remaining, _) = tag_no_case("[cite")(input)?; | ||||
|     let (remaining, _) = opt(citestyle)(remaining)?; | ||||
|     let (remaining, _) = tag(":")(remaining)?; | ||||
|     let (remaining, _prefix) = opt(parser_with_context!(global_prefix)(context))(remaining)?; | ||||
|     let (remaining, _prefix) = | ||||
|         must_balance_bracket(opt(parser_with_context!(global_prefix)(context)))(remaining)?; | ||||
| 
 | ||||
|     let (remaining, _references) = | ||||
|         separated_list1(tag(";"), parser_with_context!(citation_reference)(context))(remaining)?; | ||||
|     let (remaining, _suffix) = opt(tuple(( | ||||
|     let (remaining, _suffix) = must_balance_bracket(opt(tuple(( | ||||
|         tag(";"), | ||||
|         parser_with_context!(global_suffix)(context), | ||||
|     )))(remaining)?; | ||||
|     ))))(remaining)?; | ||||
|     let (remaining, _) = tag("]")(remaining)?; | ||||
|     let (remaining, _) = space0(remaining)?; | ||||
|     let source = get_consumed(input, remaining); | ||||
| @ -83,15 +84,11 @@ fn global_prefix<'r, 's>( | ||||
|     context: Context<'r, 's>, | ||||
|     input: OrgSource<'s>, | ||||
| ) -> Res<OrgSource<'s>, Vec<Object<'s>>> { | ||||
|     // 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 { | ||||
|     let exit_with_depth = global_prefix_end(input.get_bracket_depth()); | ||||
|     let parser_context = | ||||
|         context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { | ||||
|             class: ExitClass::Gamma, | ||||
|             exit_matcher: &global_prefix_end, | ||||
|             exit_matcher: &exit_with_depth, | ||||
|         })); | ||||
|     let (remaining, (children, _exit_contents)) = verify( | ||||
|         many_till( | ||||
| @ -104,28 +101,24 @@ fn global_prefix<'r, 's>( | ||||
|     Ok((remaining, children)) | ||||
| } | ||||
| 
 | ||||
| fn global_prefix_end( | ||||
|     starting_bracket_depth: isize, | ||||
| ) -> impl for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> { | ||||
|     move |context: Context, input: OrgSource<'_>| { | ||||
|         _global_prefix_end(context, input, starting_bracket_depth) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] | ||||
| fn global_prefix_end<'r, 's>( | ||||
| fn _global_prefix_end<'r, 's>( | ||||
|     context: Context<'r, 's>, | ||||
|     input: OrgSource<'s>, | ||||
|     starting_bracket_depth: isize, | ||||
| ) -> Res<OrgSource<'s>, OrgSource<'s>> { | ||||
|     let context_depth = get_bracket_depth(context) | ||||
|         .expect("This function should only be called from inside a citation."); | ||||
|     let text_since_context_entry = get_consumed(context_depth.position, input); | ||||
|     let mut current_depth = context_depth.depth; | ||||
|     for c in Into::<&str>::into(text_since_context_entry).chars() { | ||||
|         match c { | ||||
|             '[' => { | ||||
|                 current_depth += 1; | ||||
|             } | ||||
|             ']' if current_depth == 0 => { | ||||
|                 panic!("Exceeded citation global prefix bracket depth.") | ||||
|             } | ||||
|             ']' if current_depth > 0 => { | ||||
|                 current_depth -= 1; | ||||
|             } | ||||
|             _ => {} | ||||
|         } | ||||
|     let current_depth = input.get_bracket_depth() - starting_bracket_depth; | ||||
|     if current_depth < 0 { | ||||
|         // This shouldn't be possible because if depth is 0 then a closing bracket should end the citation.
 | ||||
|         unreachable!("Exceeded citation global prefix bracket depth.") | ||||
|     } | ||||
|     if current_depth == 0 { | ||||
|         let close_bracket = tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>("]")(input); | ||||
| @ -144,15 +137,11 @@ fn global_suffix<'r, 's>( | ||||
|     context: Context<'r, 's>, | ||||
|     input: OrgSource<'s>, | ||||
| ) -> Res<OrgSource<'s>, Vec<Object<'s>>> { | ||||
|     // 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 { | ||||
|     let exit_with_depth = global_suffix_end(input.get_bracket_depth()); | ||||
|     let parser_context = | ||||
|         context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { | ||||
|             class: ExitClass::Gamma, | ||||
|             exit_matcher: &global_suffix_end, | ||||
|             exit_matcher: &exit_with_depth, | ||||
|         })); | ||||
|     let (remaining, (children, _exit_contents)) = verify( | ||||
|         many_till( | ||||
| @ -164,28 +153,24 @@ fn global_suffix<'r, 's>( | ||||
|     Ok((remaining, children)) | ||||
| } | ||||
| 
 | ||||
| fn global_suffix_end( | ||||
|     starting_bracket_depth: isize, | ||||
| ) -> impl for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> { | ||||
|     move |context: Context, input: OrgSource<'_>| { | ||||
|         _global_suffix_end(context, input, starting_bracket_depth) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] | ||||
| fn global_suffix_end<'r, 's>( | ||||
| fn _global_suffix_end<'r, 's>( | ||||
|     context: Context<'r, 's>, | ||||
|     input: OrgSource<'s>, | ||||
|     starting_bracket_depth: isize, | ||||
| ) -> Res<OrgSource<'s>, OrgSource<'s>> { | ||||
|     let context_depth = get_bracket_depth(context) | ||||
|         .expect("This function should only be called from inside a citation."); | ||||
|     let text_since_context_entry = get_consumed(context_depth.position, input); | ||||
|     let mut current_depth = context_depth.depth; | ||||
|     for c in Into::<&str>::into(text_since_context_entry).chars() { | ||||
|         match c { | ||||
|             '[' => { | ||||
|                 current_depth += 1; | ||||
|             } | ||||
|             ']' if current_depth == 0 => { | ||||
|                 panic!("Exceeded citation global suffix bracket depth.") | ||||
|             } | ||||
|             ']' if current_depth > 0 => { | ||||
|                 current_depth -= 1; | ||||
|             } | ||||
|             _ => {} | ||||
|         } | ||||
|     let current_depth = input.get_bracket_depth() - starting_bracket_depth; | ||||
|     if current_depth < 0 { | ||||
|         // This shouldn't be possible because if depth is 0 then a closing bracket should end the citation.
 | ||||
|         unreachable!("Exceeded citation global suffix bracket depth.") | ||||
|     } | ||||
|     if current_depth == 0 { | ||||
|         let close_bracket = tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>("]")(input); | ||||
|  | ||||
| @ -13,11 +13,11 @@ 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::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; | ||||
| @ -31,9 +31,11 @@ pub fn citation_reference<'r, 's>( | ||||
|     context: Context<'r, 's>, | ||||
|     input: OrgSource<'s>, | ||||
| ) -> Res<OrgSource<'s>, CitationReference<'s>> { | ||||
|     let (remaining, _prefix) = opt(parser_with_context!(key_prefix)(context))(input)?; | ||||
|     let (remaining, _prefix) = | ||||
|         must_balance_bracket(opt(parser_with_context!(key_prefix)(context)))(input)?; | ||||
|     let (remaining, _key) = parser_with_context!(citation_reference_key)(context)(remaining)?; | ||||
|     let (remaining, _suffix) = opt(parser_with_context!(key_suffix)(context))(remaining)?; | ||||
|     let (remaining, _suffix) = | ||||
|         must_balance_bracket(opt(parser_with_context!(key_suffix)(context)))(remaining)?; | ||||
|     let source = get_consumed(input, remaining); | ||||
| 
 | ||||
|     Ok(( | ||||
| @ -69,15 +71,11 @@ fn key_prefix<'r, 's>( | ||||
|     context: Context<'r, 's>, | ||||
|     input: OrgSource<'s>, | ||||
| ) -> Res<OrgSource<'s>, Vec<Object<'s>>> { | ||||
|     // 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 { | ||||
|     let exit_with_depth = key_prefix_end(input.get_bracket_depth()); | ||||
|     let parser_context = | ||||
|         context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { | ||||
|             class: ExitClass::Gamma, | ||||
|             exit_matcher: &key_prefix_end, | ||||
|             exit_matcher: &exit_with_depth, | ||||
|         })); | ||||
|     let (remaining, (children, _exit_contents)) = verify( | ||||
|         many_till( | ||||
| @ -94,15 +92,11 @@ fn key_suffix<'r, 's>( | ||||
|     context: Context<'r, 's>, | ||||
|     input: OrgSource<'s>, | ||||
| ) -> Res<OrgSource<'s>, Vec<Object<'s>>> { | ||||
|     // 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 { | ||||
|     let exit_with_depth = key_suffix_end(input.get_bracket_depth()); | ||||
|     let parser_context = | ||||
|         context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { | ||||
|             class: ExitClass::Gamma, | ||||
|             exit_matcher: &key_suffix_end, | ||||
|             exit_matcher: &exit_with_depth, | ||||
|         })); | ||||
|     let (remaining, (children, _exit_contents)) = verify( | ||||
|         many_till( | ||||
| @ -114,39 +108,24 @@ fn key_suffix<'r, 's>( | ||||
|     Ok((remaining, children)) | ||||
| } | ||||
| 
 | ||||
| #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] | ||||
| pub fn get_bracket_depth<'r, 's>(context: Context<'r, 's>) -> Option<&'r CitationBracket<'s>> { | ||||
|     for node in context.iter() { | ||||
|         match node.get_data() { | ||||
|             ContextElement::CitationBracket(depth) => return Some(depth), | ||||
|             _ => {} | ||||
|         } | ||||
| fn key_prefix_end( | ||||
|     starting_bracket_depth: isize, | ||||
| ) -> impl for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> { | ||||
|     move |context: Context, input: OrgSource<'_>| { | ||||
|         _key_prefix_end(context, input, starting_bracket_depth) | ||||
|     } | ||||
|     None | ||||
| } | ||||
| 
 | ||||
| #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] | ||||
| fn key_prefix_end<'r, 's>( | ||||
| fn _key_prefix_end<'r, 's>( | ||||
|     context: Context<'r, 's>, | ||||
|     input: OrgSource<'s>, | ||||
|     starting_bracket_depth: isize, | ||||
| ) -> Res<OrgSource<'s>, OrgSource<'s>> { | ||||
|     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 Into::<&str>::into(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; | ||||
|             } | ||||
|             _ => {} | ||||
|         } | ||||
|     let current_depth = input.get_bracket_depth() - starting_bracket_depth; | ||||
|     if current_depth < 0 { | ||||
|         // This shouldn't be possible because if depth is 0 then a closing bracket should end the citation.
 | ||||
|         unreachable!("Exceeded citation key prefix bracket depth.") | ||||
|     } | ||||
|     if current_depth == 0 { | ||||
|         let close_bracket = tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>("]")(input); | ||||
| @ -160,28 +139,24 @@ fn key_prefix_end<'r, 's>( | ||||
|     ))(input) | ||||
| } | ||||
| 
 | ||||
| fn key_suffix_end( | ||||
|     starting_bracket_depth: isize, | ||||
| ) -> impl for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> { | ||||
|     move |context: Context, input: OrgSource<'_>| { | ||||
|         _key_suffix_end(context, input, starting_bracket_depth) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] | ||||
| fn key_suffix_end<'r, 's>( | ||||
|     context: Context<'r, 's>, | ||||
| fn _key_suffix_end<'r, 's>( | ||||
|     _context: Context<'r, 's>, | ||||
|     input: OrgSource<'s>, | ||||
|     starting_bracket_depth: isize, | ||||
| ) -> Res<OrgSource<'s>, OrgSource<'s>> { | ||||
|     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 Into::<&str>::into(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; | ||||
|             } | ||||
|             _ => {} | ||||
|         } | ||||
|     let current_depth = input.get_bracket_depth() - starting_bracket_depth; | ||||
|     if current_depth < 0 { | ||||
|         // This shouldn't be possible because if depth is 0 then a closing bracket should end the citation.
 | ||||
|         unreachable!("Exceeded citation key suffix bracket depth.") | ||||
|     } | ||||
|     if current_depth == 0 { | ||||
|         let close_bracket = tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>("]")(input); | ||||
| @ -191,3 +166,21 @@ fn key_suffix_end<'r, 's>( | ||||
|     } | ||||
|     tag(";")(input) | ||||
| } | ||||
| 
 | ||||
| pub fn must_balance_bracket<'s, F, O>( | ||||
|     mut inner: F, | ||||
| ) -> impl FnMut(OrgSource<'s>) -> Res<OrgSource<'s>, O> | ||||
| where | ||||
|     F: FnMut(OrgSource<'s>) -> Res<OrgSource<'s>, O>, | ||||
| { | ||||
|     move |input: OrgSource<'_>| { | ||||
|         let pre_bracket_depth = input.get_bracket_depth(); | ||||
|         let (remaining, output) = inner(input)?; | ||||
|         if remaining.get_bracket_depth() - pre_bracket_depth != 0 { | ||||
|             return Err(nom::Err::Error(CustomError::MyError(MyError( | ||||
|                 "UnbalancedBrackets".into(), | ||||
|             )))); | ||||
|         } | ||||
|         Ok((remaining, output)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -124,21 +124,6 @@ pub enum ContextElement<'r, 's> { | ||||
|     /// radio links matching the contents of radio targets.
 | ||||
|     RadioTarget(Vec<&'r Vec<Object<'s>>>), | ||||
| 
 | ||||
|     /// Stores the current bracket depth inside a citation.
 | ||||
|     ///
 | ||||
|     /// The global prefix, global suffix, key prefix, and key suffix
 | ||||
|     /// inside a footnote reference must have balanced brackets []
 | ||||
|     /// inside the definition, so this stores the amount of opening
 | ||||
|     /// brackets subtracted by the amount of closing brackets within
 | ||||
|     /// the definition must equal zero. None of the prefixes or
 | ||||
|     /// suffixes can be nested inside each other so we can use a
 | ||||
|     /// single type for this without conflict.
 | ||||
|     ///
 | ||||
|     /// A reference to the position in the string is also included so
 | ||||
|     /// unbalanced brackets can be detected in the middle of an
 | ||||
|     /// object.
 | ||||
|     CitationBracket(CitationBracket<'s>), | ||||
| 
 | ||||
|     /// Stores the current bracket or parenthesis depth inside an inline babel call.
 | ||||
|     ///
 | ||||
|     /// Inside an inline babel call the headers must have balanced
 | ||||
| @ -183,12 +168,6 @@ pub struct ExitMatcherNode<'r> { | ||||
|     pub class: ExitClass, | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub struct CitationBracket<'s> { | ||||
|     pub position: OrgSource<'s>, | ||||
|     pub depth: usize, | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub struct BabelHeaderBracket<'s> { | ||||
|     pub position: OrgSource<'s>, | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Tom Alexander
						Tom Alexander