2023-07-21 20:38:49 +00:00
use nom ::branch ::alt ;
use nom ::bytes ::complete ::tag ;
2023-07-20 05:13:49 +00:00
use nom ::character ::complete ::anychar ;
use nom ::combinator ::not ;
use nom ::combinator ::recognize ;
use nom ::combinator ::verify ;
use nom ::multi ::many1 ;
use nom ::multi ::many_till ;
use nom ::sequence ::preceded ;
2023-07-21 20:38:49 +00:00
use nom ::sequence ::tuple ;
2023-07-20 05:13:49 +00:00
2023-07-20 04:38:16 +00:00
use super ::Context ;
2023-07-21 20:38:49 +00:00
use crate ::error ::CustomError ;
2023-07-20 04:38:16 +00:00
use crate ::error ::Res ;
2023-07-21 20:38:49 +00:00
use crate ::parser ::exiting ::ExitClass ;
2023-07-20 04:38:16 +00:00
use crate ::parser ::object ::CitationReference ;
2023-07-20 05:13:49 +00:00
use crate ::parser ::object_parser ::minimal_set_object ;
use crate ::parser ::parser_context ::CitationBracket ;
use crate ::parser ::parser_context ::ContextElement ;
2023-07-21 20:38:49 +00:00
use crate ::parser ::parser_context ::ExitMatcherNode ;
2023-07-20 05:13:49 +00:00
use crate ::parser ::parser_with_context ::parser_with_context ;
use crate ::parser ::util ::exit_matcher_parser ;
2023-07-21 20:38:49 +00:00
use crate ::parser ::util ::get_consumed ;
2023-07-20 05:13:49 +00:00
use crate ::parser ::util ::WORD_CONSTITUENT_CHARACTERS ;
use crate ::parser ::Object ;
2023-07-20 04:38:16 +00:00
#[ tracing::instrument(ret, level = " debug " ) ]
pub fn citation_reference < ' r , ' s > (
context : Context < ' r , ' s > ,
input : & ' s str ,
) -> Res < & ' s str , CitationReference < ' s > > {
2023-07-21 20:38:49 +00:00
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 } ) )
2023-07-20 04:38:16 +00:00
}
2023-07-20 05:13:49 +00:00
#[ tracing::instrument(ret, level = " debug " ) ]
fn key < ' r , ' s > ( context : Context < ' r , ' s > , input : & ' s str ) -> Res < & ' s str , & ' s str > {
2023-07-21 20:38:49 +00:00
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 )
} ,
) ) ,
2023-07-20 05:13:49 +00:00
) ) ) ( input ) ? ;
Ok ( ( remaining , source ) )
}
2023-07-21 20:38:49 +00:00
#[ tracing::instrument(ret, level = " debug " ) ]
fn key_prefix < ' r , ' s > ( context : Context < ' r , ' s > , input : & ' s str ) -> Res < & ' s str , 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 {
class : ExitClass ::Beta ,
exit_matcher : & key_prefix_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 ) )
}
2023-07-20 05:13:49 +00:00
#[ tracing::instrument(ret, level = " debug " ) ]
fn key_suffix < ' r , ' s > ( context : Context < ' r , ' s > , input : & ' s str ) -> Res < & ' s str , Vec < Object < ' s > > > {
2023-07-21 20:38:49 +00:00
// 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 ,
} ) ) ;
2023-07-20 05:13:49 +00:00
let ( remaining , ( children , _exit_contents ) ) = verify (
many_till (
2023-07-21 20:38:49 +00:00
parser_with_context! ( minimal_set_object ) ( & parser_context ) ,
parser_with_context! ( exit_matcher_parser ) ( & parser_context ) ,
2023-07-20 05:13:49 +00:00
) ,
| ( children , _exit_contents ) | ! children . is_empty ( ) ,
) ( input ) ? ;
2023-07-21 20:38:49 +00:00
Ok ( ( remaining , children ) )
2023-07-20 05:13:49 +00:00
}
#[ tracing::instrument(ret, level = " debug " ) ]
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 ) ,
_ = > { }
}
}
None
}
2023-07-21 20:38:49 +00:00
#[ 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 )
}