2023-07-24 15:41:14 -04:00
use nom ::branch ::alt ;
use nom ::bytes ::complete ::tag ;
use nom ::character ::complete ::anychar ;
use nom ::character ::complete ::one_of ;
use nom ::character ::complete ::space0 ;
use nom ::combinator ::map ;
use nom ::combinator ::not ;
use nom ::combinator ::opt ;
use nom ::combinator ::peek ;
use nom ::combinator ::recognize ;
use nom ::combinator ::verify ;
use nom ::multi ::many_till ;
2023-08-23 00:30:26 -04:00
use super ::org_source ::OrgSource ;
2023-07-24 14:19:19 -04:00
use super ::Context ;
2023-07-24 15:41:14 -04:00
use super ::Object ;
use crate ::error ::CustomError ;
use crate ::error ::MyError ;
2023-07-24 14:19:19 -04:00
use crate ::error ::Res ;
2023-07-24 16:29:31 -04:00
use crate ::parser ::exiting ::ExitClass ;
use crate ::parser ::object_parser ::standard_set_object ;
use crate ::parser ::parser_context ::ContextElement ;
use crate ::parser ::parser_context ::ExitMatcherNode ;
use crate ::parser ::parser_context ::SubscriptSuperscriptBrace ;
2023-07-24 15:41:14 -04:00
use crate ::parser ::parser_with_context ::parser_with_context ;
2023-07-24 16:29:31 -04:00
use crate ::parser ::util ::exit_matcher_parser ;
2023-07-24 15:41:14 -04:00
use crate ::parser ::util ::get_consumed ;
2023-07-24 14:19:19 -04:00
use crate ::parser ::Subscript ;
use crate ::parser ::Superscript ;
2023-08-10 20:04:59 -04:00
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
2023-08-23 00:30:26 -04:00
pub fn subscript < ' r , ' s > (
context : Context < ' r , ' s > ,
input : OrgSource < ' s > ,
) -> Res < OrgSource < ' s > , Subscript < ' s > > {
2023-07-24 15:41:14 -04:00
// We check for the underscore first before checking the pre-character as a minor optimization to avoid walking up the context tree to find the document root unnecessarily.
let ( remaining , _ ) = tag ( " _ " ) ( input ) ? ;
pre ( context , input ) ? ;
let ( remaining , _body ) = script_body ( context , remaining ) ? ;
let ( remaining , _ ) = space0 ( remaining ) ? ;
let source = get_consumed ( input , remaining ) ;
2023-08-23 00:30:26 -04:00
Ok ( (
remaining ,
Subscript {
source : source . into ( ) ,
} ,
) )
2023-07-24 14:19:19 -04:00
}
2023-08-10 20:04:59 -04:00
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
2023-07-24 14:19:19 -04:00
pub fn superscript < ' r , ' s > (
context : Context < ' r , ' s > ,
2023-08-23 00:30:26 -04:00
input : OrgSource < ' s > ,
) -> Res < OrgSource < ' s > , Superscript < ' s > > {
2023-07-24 15:41:14 -04:00
// We check for the circumflex first before checking the pre-character as a minor optimization to avoid walking up the context tree to find the document root unnecessarily.
let ( remaining , _ ) = tag ( " ^ " ) ( input ) ? ;
pre ( context , input ) ? ;
let ( remaining , _body ) = script_body ( context , remaining ) ? ;
let ( remaining , _ ) = space0 ( remaining ) ? ;
let source = get_consumed ( input , remaining ) ;
2023-08-23 00:30:26 -04:00
Ok ( (
remaining ,
Superscript {
source : source . into ( ) ,
} ,
) )
2023-07-24 15:41:14 -04:00
}
2023-08-10 20:04:59 -04:00
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
2023-08-24 23:43:41 +00:00
fn pre < ' r , ' s > ( _context : Context < ' r , ' s > , input : OrgSource < ' s > ) -> Res < OrgSource < ' s > , ( ) > {
2023-08-24 16:55:56 -04:00
let preceding_character = input . get_preceding_character ( ) ;
2023-07-24 15:41:14 -04:00
match preceding_character {
Some ( c ) if ! c . is_whitespace ( ) = > { }
_ = > {
return Err ( nom ::Err ::Error ( CustomError ::MyError ( MyError (
2023-08-23 00:30:26 -04:00
" Must be preceded by a non-whitespace character. " . into ( ) ,
2023-07-24 15:41:14 -04:00
) ) ) ) ;
}
} ;
Ok ( ( input , ( ) ) )
}
#[ derive(Debug) ]
enum ScriptBody < ' s > {
Braceless ( & ' s str ) ,
WithBraces ( Vec < Object < ' s > > ) ,
}
2023-08-10 20:04:59 -04:00
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
2023-08-23 00:30:26 -04:00
fn script_body < ' r , ' s > (
context : Context < ' r , ' s > ,
input : OrgSource < ' s > ,
) -> Res < OrgSource < ' s > , ScriptBody < ' s > > {
2023-07-24 15:41:14 -04:00
alt ( (
map ( parser_with_context! ( script_asterisk ) ( context ) , | body | {
2023-08-23 00:30:26 -04:00
ScriptBody ::Braceless ( body . into ( ) )
2023-07-24 15:41:14 -04:00
} ) ,
map ( parser_with_context! ( script_alphanum ) ( context ) , | body | {
2023-08-23 00:30:26 -04:00
ScriptBody ::Braceless ( body . into ( ) )
2023-07-24 15:41:14 -04:00
} ) ,
2023-07-24 16:29:31 -04:00
map ( parser_with_context! ( script_with_braces ) ( context ) , | body | {
2023-08-23 00:30:26 -04:00
ScriptBody ::WithBraces ( body . into ( ) )
2023-07-24 16:29:31 -04:00
} ) ,
2023-07-24 15:41:14 -04:00
) ) ( input )
}
2023-08-10 20:04:59 -04:00
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
2023-08-23 00:30:26 -04:00
fn script_asterisk < ' r , ' s > (
2023-08-24 23:43:41 +00:00
_context : Context < ' r , ' s > ,
2023-08-23 00:30:26 -04:00
input : OrgSource < ' s > ,
) -> Res < OrgSource < ' s > , OrgSource < ' s > > {
2023-07-24 15:41:14 -04:00
tag ( " * " ) ( input )
}
2023-08-10 20:04:59 -04:00
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
2023-08-23 00:30:26 -04:00
fn script_alphanum < ' r , ' s > (
context : Context < ' r , ' s > ,
input : OrgSource < ' s > ,
) -> Res < OrgSource < ' s > , OrgSource < ' s > > {
2023-07-24 15:41:14 -04:00
let ( remaining , _sign ) = opt ( recognize ( one_of ( " +- " ) ) ) ( input ) ? ;
let ( remaining , _script ) = many_till (
parser_with_context! ( script_alphanum_character ) ( context ) ,
parser_with_context! ( end_script_alphanum_character ) ( context ) ,
) ( remaining ) ? ;
let source = get_consumed ( input , remaining ) ;
Ok ( ( remaining , source ) )
}
2023-08-10 20:04:59 -04:00
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
2023-07-24 15:41:14 -04:00
fn script_alphanum_character < ' r , ' s > (
2023-08-24 23:43:41 +00:00
_context : Context < ' r , ' s > ,
2023-08-23 00:30:26 -04:00
input : OrgSource < ' s > ,
) -> Res < OrgSource < ' s > , OrgSource < ' s > > {
2023-07-24 15:41:14 -04:00
recognize ( verify ( anychar , | c | {
c . is_alphanumeric ( ) | | r # ",.\"# . contains ( * c )
} ) ) ( input )
}
2023-08-10 20:04:59 -04:00
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
2023-07-24 15:41:14 -04:00
fn end_script_alphanum_character < ' r , ' s > (
context : Context < ' r , ' s > ,
2023-08-23 00:30:26 -04:00
input : OrgSource < ' s > ,
) -> Res < OrgSource < ' s > , OrgSource < ' s > > {
2023-07-24 15:41:14 -04:00
let ( remaining , final_char ) = recognize ( verify ( anychar , | c | c . is_alphanumeric ( ) ) ) ( input ) ? ;
peek ( not ( parser_with_context! ( script_alphanum_character ) (
context ,
) ) ) ( remaining ) ? ;
Ok ( ( remaining , final_char ) )
2023-07-24 14:19:19 -04:00
}
2023-07-24 16:29:31 -04:00
2023-08-10 20:04:59 -04:00
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
2023-07-24 16:29:31 -04:00
fn script_with_braces < ' r , ' s > (
context : Context < ' r , ' s > ,
2023-08-23 00:30:26 -04:00
input : OrgSource < ' s > ,
) -> Res < OrgSource < ' s > , Vec < Object < ' s > > > {
2023-07-24 16:29:31 -04:00
let ( remaining , _ ) = tag ( " { " ) ( input ) ? ;
let parser_context = context
. with_additional_node ( ContextElement ::SubscriptSuperscriptBrace (
SubscriptSuperscriptBrace {
2023-08-23 00:30:26 -04:00
position : remaining . into ( ) ,
2023-07-24 16:29:31 -04:00
depth : 0 ,
} ,
) )
. with_additional_node ( ContextElement ::ExitMatcherNode ( ExitMatcherNode {
2023-08-24 23:34:23 -04:00
class : ExitClass ::Gamma ,
2023-07-24 16:29:31 -04:00
exit_matcher : & script_with_braces_end ,
} ) ) ;
let ( remaining , ( children , _exit_contents ) ) = many_till (
parser_with_context! ( standard_set_object ) ( & parser_context ) ,
parser_with_context! ( exit_matcher_parser ) ( & parser_context ) ,
) ( remaining ) ? ;
let ( remaining , _ ) = tag ( " } " ) ( remaining ) ? ;
Ok ( ( remaining , children ) )
}
2023-08-10 20:04:59 -04:00
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
2023-07-24 16:29:31 -04:00
fn script_with_braces_end < ' r , ' s > (
context : Context < ' r , ' s > ,
2023-08-23 00:30:26 -04:00
input : OrgSource < ' s > ,
) -> Res < OrgSource < ' s > , OrgSource < ' s > > {
2023-07-24 16:29:31 -04:00
let context_depth = get_bracket_depth ( context )
. expect ( " This function should only be called from inside a subscript or superscript. " ) ;
let text_since_context_entry = get_consumed ( context_depth . position , input ) ;
let mut current_depth = context_depth . depth ;
2023-08-23 00:30:26 -04:00
for c in Into ::< & str > ::into ( text_since_context_entry ) . chars ( ) {
2023-07-24 16:29:31 -04:00
match c {
'{' = > {
current_depth + = 1 ;
}
'}' if current_depth = = 0 = > {
panic! ( " Exceeded subscript or superscript brace depth. " )
}
'}' if current_depth > 0 = > {
current_depth - = 1 ;
}
_ = > { }
}
}
if current_depth = = 0 {
2023-08-23 00:30:26 -04:00
let close_bracket = tag ::< & str , OrgSource < '_ > , CustomError < OrgSource < '_ > > > ( " } " ) ( input ) ;
2023-07-24 16:29:31 -04:00
if close_bracket . is_ok ( ) {
return close_bracket ;
}
}
return Err ( nom ::Err ::Error ( CustomError ::MyError ( MyError (
2023-08-23 00:30:26 -04:00
" Not a valid end for subscript or superscript. " . into ( ) ,
2023-07-24 16:29:31 -04:00
) ) ) ) ;
}
2023-08-10 20:04:59 -04:00
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
2023-07-24 16:29:31 -04:00
fn get_bracket_depth < ' r , ' s > (
context : Context < ' r , ' s > ,
) -> Option < & ' r SubscriptSuperscriptBrace < ' s > > {
for node in context . iter ( ) {
match node . get_data ( ) {
ContextElement ::SubscriptSuperscriptBrace ( depth ) = > return Some ( depth ) ,
_ = > { }
}
}
None
}