2023-07-24 19:41:14 +00:00
use nom ::branch ::alt ;
use nom ::bytes ::complete ::tag ;
2023-09-07 07:40:14 +00:00
use nom ::bytes ::complete ::take_while ;
2023-07-24 19:41:14 +00:00
use nom ::character ::complete ::anychar ;
use nom ::character ::complete ::one_of ;
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-09-07 07:40:14 +00:00
use nom ::sequence ::tuple ;
2023-07-24 19:41:14 +00:00
2023-09-03 04:27:50 +00:00
use super ::object_parser ::standard_set_object ;
2023-08-29 15:18:15 +00:00
use super ::org_source ::BracketDepth ;
2023-08-23 04:30:26 +00:00
use super ::org_source ::OrgSource ;
2023-09-03 04:27:50 +00:00
use super ::util ::exit_matcher_parser ;
2023-08-31 19:44:44 +00:00
use super ::util ::maybe_consume_object_trailing_whitespace_if_not_exiting ;
2023-09-07 06:27:55 +00:00
use super ::util ::preceded_by_whitespace ;
2023-09-03 04:27:50 +00:00
use crate ::context ::parser_with_context ;
use crate ::context ::ContextElement ;
2023-09-03 19:44:18 +00:00
use crate ::context ::ContextMatcher ;
2023-09-03 04:27:50 +00:00
use crate ::context ::ExitClass ;
use crate ::context ::ExitMatcherNode ;
use crate ::context ::RefContext ;
2023-07-24 19:41:14 +00:00
use crate ::error ::CustomError ;
use crate ::error ::MyError ;
2023-07-24 18:19:19 +00:00
use crate ::error ::Res ;
2023-07-24 19:41:14 +00:00
use crate ::parser ::util ::get_consumed ;
2023-09-03 04:27:50 +00:00
use crate ::types ::Object ;
use crate ::types ::Subscript ;
use crate ::types ::Superscript ;
2023-07-24 18:19:19 +00:00
2023-09-07 06:59:08 +00:00
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
2023-09-11 17:13:28 +00:00
pub ( crate ) fn detect_subscript_or_superscript < ' s > ( input : OrgSource < ' s > ) -> Res < OrgSource < ' s > , ( ) > {
2023-09-07 06:59:08 +00:00
// This does not have to detect all valid subscript/superscript but all that it detects must be valid.
let ( remaining , _ ) = one_of ( " _^ " ) ( input ) ? ;
pre ( input ) ? ;
if tag ::< _ , _ , CustomError < _ > > ( " * " ) ( remaining ) . is_ok ( ) {
return Ok ( ( input , ( ) ) ) ;
}
let ( remaining , _ ) = opt ( one_of ( " +- " ) ) ( remaining ) ? ;
let ( _remaining , _ ) = verify ( anychar , | c | c . is_alphanumeric ( ) ) ( remaining ) ? ;
Ok ( ( input , ( ) ) )
}
2023-08-11 00:04:59 +00:00
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
2023-09-11 17:13:28 +00:00
pub ( crate ) fn subscript < ' b , ' g , ' r , ' s > (
2023-09-03 19:44:18 +00:00
context : RefContext < ' b , ' g , ' r , ' s > ,
2023-08-23 04:30:26 +00:00
input : OrgSource < ' s > ,
) -> Res < OrgSource < ' s > , Subscript < ' s > > {
2023-07-24 19:41:14 +00: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 ) ? ;
2023-09-07 06:27:55 +00:00
pre ( input ) ? ;
2023-07-24 19:41:14 +00:00
let ( remaining , _body ) = script_body ( context , remaining ) ? ;
2023-08-31 19:44:44 +00:00
let ( remaining , _trailing_whitespace ) =
maybe_consume_object_trailing_whitespace_if_not_exiting ( context , remaining ) ? ;
2023-07-24 19:41:14 +00:00
let source = get_consumed ( input , remaining ) ;
2023-08-23 04:30:26 +00:00
Ok ( (
remaining ,
Subscript {
source : source . into ( ) ,
} ,
) )
2023-07-24 18:19:19 +00:00
}
2023-08-11 00:04:59 +00:00
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
2023-09-11 17:13:28 +00:00
pub ( crate ) fn superscript < ' b , ' g , ' r , ' s > (
2023-09-03 19:44:18 +00:00
context : RefContext < ' b , ' g , ' r , ' s > ,
2023-08-23 04:30:26 +00:00
input : OrgSource < ' s > ,
) -> Res < OrgSource < ' s > , Superscript < ' s > > {
2023-07-24 19:41:14 +00: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 ) ? ;
2023-09-07 06:27:55 +00:00
pre ( input ) ? ;
2023-07-24 19:41:14 +00:00
let ( remaining , _body ) = script_body ( context , remaining ) ? ;
2023-08-31 19:44:44 +00:00
let ( remaining , _trailing_whitespace ) =
maybe_consume_object_trailing_whitespace_if_not_exiting ( context , remaining ) ? ;
2023-07-24 19:41:14 +00:00
let source = get_consumed ( input , remaining ) ;
2023-08-23 04:30:26 +00:00
Ok ( (
remaining ,
Superscript {
source : source . into ( ) ,
} ,
) )
2023-07-24 19:41:14 +00:00
}
2023-08-11 00:04:59 +00:00
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
2023-09-07 06:27:55 +00:00
fn pre < ' s > ( input : OrgSource < ' s > ) -> Res < OrgSource < ' s > , ( ) > {
not ( preceded_by_whitespace ( true ) ) ( input ) ? ;
2023-07-24 19:41:14 +00:00
Ok ( ( input , ( ) ) )
}
#[ derive(Debug) ]
enum ScriptBody < ' s > {
Braceless ( & ' s str ) ,
WithBraces ( Vec < Object < ' s > > ) ,
}
2023-08-11 00:04:59 +00:00
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
2023-09-03 19:44:18 +00:00
fn script_body < ' b , ' g , ' r , ' s > (
context : RefContext < ' b , ' g , ' r , ' s > ,
2023-08-23 04:30:26 +00:00
input : OrgSource < ' s > ,
) -> Res < OrgSource < ' s > , ScriptBody < ' s > > {
2023-07-24 19:41:14 +00:00
alt ( (
map ( parser_with_context! ( script_asterisk ) ( context ) , | body | {
2023-08-23 04:30:26 +00:00
ScriptBody ::Braceless ( body . into ( ) )
2023-07-24 19:41:14 +00:00
} ) ,
map ( parser_with_context! ( script_alphanum ) ( context ) , | body | {
2023-08-23 04:30:26 +00:00
ScriptBody ::Braceless ( body . into ( ) )
2023-07-24 19:41:14 +00:00
} ) ,
2023-07-24 20:29:31 +00:00
map ( parser_with_context! ( script_with_braces ) ( context ) , | body | {
2023-08-23 04:30:26 +00:00
ScriptBody ::WithBraces ( body . into ( ) )
2023-07-24 20:29:31 +00:00
} ) ,
2023-07-24 19:41:14 +00:00
) ) ( input )
}
2023-08-11 00:04:59 +00:00
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
2023-09-03 19:44:18 +00:00
fn script_asterisk < ' b , ' g , ' r , ' s > (
_context : RefContext < ' b , ' g , ' r , ' s > ,
2023-08-23 04:30:26 +00:00
input : OrgSource < ' s > ,
) -> Res < OrgSource < ' s > , OrgSource < ' s > > {
2023-07-24 19:41:14 +00:00
tag ( " * " ) ( input )
}
2023-08-11 00:04:59 +00:00
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
2023-09-03 19:44:18 +00:00
fn script_alphanum < ' b , ' g , ' r , ' s > (
2023-09-07 06:27:55 +00:00
_context : RefContext < ' b , ' g , ' r , ' s > ,
2023-08-23 04:30:26 +00:00
input : OrgSource < ' s > ,
) -> Res < OrgSource < ' s > , OrgSource < ' s > > {
2023-07-24 19:41:14 +00:00
let ( remaining , _sign ) = opt ( recognize ( one_of ( " +- " ) ) ) ( input ) ? ;
2023-09-07 06:27:55 +00:00
let ( remaining , _script ) =
many_till ( script_alphanum_character , end_script_alphanum_character ) ( remaining ) ? ;
2023-07-24 19:41:14 +00:00
let source = get_consumed ( input , remaining ) ;
Ok ( ( remaining , source ) )
}
2023-08-11 00:04:59 +00:00
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
2023-09-07 06:27:55 +00:00
fn script_alphanum_character < ' s > ( input : OrgSource < ' s > ) -> Res < OrgSource < ' s > , OrgSource < ' s > > {
2023-07-24 19:41:14 +00:00
recognize ( verify ( anychar , | c | {
c . is_alphanumeric ( ) | | r # ",.\"# . contains ( * c )
} ) ) ( input )
}
2023-08-11 00:04:59 +00:00
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
2023-09-07 06:27:55 +00:00
fn end_script_alphanum_character < ' s > ( input : OrgSource < ' s > ) -> Res < OrgSource < ' s > , OrgSource < ' s > > {
2023-07-24 19:41:14 +00:00
let ( remaining , final_char ) = recognize ( verify ( anychar , | c | c . is_alphanumeric ( ) ) ) ( input ) ? ;
2023-09-07 07:40:14 +00:00
peek ( tuple ( (
take_while ( | c | r # ",.\"# . contains ( c ) ) ,
not ( script_alphanum_character ) ,
) ) ) ( remaining ) ? ;
2023-07-24 19:41:14 +00:00
Ok ( ( remaining , final_char ) )
2023-07-24 18:19:19 +00:00
}
2023-07-24 20:29:31 +00:00
2023-08-11 00:04:59 +00:00
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
2023-09-03 19:44:18 +00:00
fn script_with_braces < ' b , ' g , ' r , ' s > (
context : RefContext < ' b , ' g , ' r , ' s > ,
2023-08-23 04:30:26 +00:00
input : OrgSource < ' s > ,
) -> Res < OrgSource < ' s > , Vec < Object < ' s > > > {
2023-07-24 20:29:31 +00:00
let ( remaining , _ ) = tag ( " { " ) ( input ) ? ;
2023-08-28 06:37:01 +00:00
let exit_with_depth = script_with_braces_end ( remaining . get_brace_depth ( ) ) ;
2023-09-03 04:27:50 +00:00
let parser_context = ContextElement ::ExitMatcherNode ( ExitMatcherNode {
class : ExitClass ::Gamma ,
exit_matcher : & exit_with_depth ,
} ) ;
let parser_context = context . with_additional_node ( & parser_context ) ;
2023-07-24 20:29:31 +00:00
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-09-03 19:44:18 +00:00
fn script_with_braces_end ( starting_brace_depth : BracketDepth ) -> impl ContextMatcher {
2023-09-03 04:27:50 +00:00
move | context , input : OrgSource < '_ > | {
2023-08-28 06:37:01 +00:00
_script_with_braces_end ( context , input , starting_brace_depth )
2023-07-24 20:29:31 +00:00
}
}
2023-08-11 00:04:59 +00:00
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
2023-09-03 19:44:18 +00:00
fn _script_with_braces_end < ' b , ' g , ' r , ' s > (
_context : RefContext < ' b , ' g , ' r , ' s > ,
2023-08-28 06:37:01 +00:00
input : OrgSource < ' s > ,
2023-08-29 15:18:15 +00:00
starting_brace_depth : BracketDepth ,
2023-08-28 06:37:01 +00:00
) -> Res < OrgSource < ' s > , OrgSource < ' s > > {
let current_depth = input . get_brace_depth ( ) - starting_brace_depth ;
if current_depth > 0 {
// Its impossible for the next character to end the subscript or superscript if we're any amount of braces deep
return Err ( nom ::Err ::Error ( CustomError ::MyError ( MyError (
" Not a valid end for subscript or superscript. " . into ( ) ,
) ) ) ) ;
}
if current_depth < 0 {
// This shouldn't be possible because if depth is 0 then a closing brace should end the subscript or superscript.
unreachable! ( " Exceeded subscript or superscript brace depth. " )
2023-07-24 20:29:31 +00:00
}
2023-08-28 06:37:01 +00:00
tag ( " } " ) ( input )
2023-07-24 20:29:31 +00:00
}