@@ -3442,7 +3442,7 @@ impl CanonicalizeContext {
34423442 return FunctionNameCertainty :: True ;
34433443 }
34443444
3445- // Try to figure out whether an <mi> is a function name or note .
3445+ // Try to figure out whether an <mi> is a function name or not .
34463446 // There are two important cases depending upon whether parens/brackets are used or not.
34473447 // E.g, sin x and f(x)
34483448 // 1. If parens follow the name, then we use a more inclusive set of heuristics as it is more likely a function
@@ -3567,7 +3567,8 @@ impl CanonicalizeContext {
35673567 }
35683568
35693569 // debug!(" ...didn't match options to be a function");
3570- return FunctionNameCertainty :: Maybe ; // didn't fit one of the above categories
3570+ // debug!("Right siblings:\n{} ", right_siblings.iter().map(|&child| mml_to_string(as_element(child))).collect::<Vec<String>>().join("\n "));
3571+ return if is_name_inside_parens ( base_name, right_siblings) { FunctionNameCertainty :: False } else { FunctionNameCertainty :: Maybe } ;
35713572 } ) ;
35723573
35733574 fn is_single_arg ( open : & str , following_nodes : & [ ChildOfElement ] ) -> bool {
@@ -3631,6 +3632,58 @@ impl CanonicalizeContext {
36313632 // debug!(" is_matching_right_paren: open={}, close={}", open, text);
36323633 return ( open == "(" && text == ")" ) || ( open == "[" && text == "]" ) ;
36333634 }
3635+
3636+ /// Returns true if the name of the potential function is inside the parens. In that case, it is very unlikely to be a function call
3637+ /// For example, "n(n+1)"
3638+ fn is_name_inside_parens ( function_name : & str , right_siblings : & [ ChildOfElement ] ) -> bool {
3639+ // the first child of right_siblings is either '(' or '['
3640+ // right_siblings may extend well beyond the closing parens, so we first break this into finding the contents
3641+ // then we search the contents for the name
3642+ match find_contents ( right_siblings) {
3643+ None => return false ,
3644+ Some ( contents) => return is_name_inside_contents ( function_name, contents) ,
3645+ }
3646+
3647+
3648+ fn find_contents < ' a > ( right_siblings : & ' a [ ChildOfElement < ' a > ] ) -> Option < & ' a [ ChildOfElement < ' a > ] > {
3649+ let open_text = as_text ( as_element ( right_siblings[ 0 ] ) ) ;
3650+ let close_text = if open_text == "(" { ")" } else { "]" } ;
3651+ let mut nesting_level = 1 ;
3652+ let mut i = 1 ;
3653+ while i < right_siblings. len ( ) {
3654+ let child = as_element ( right_siblings[ i] ) ;
3655+ if name ( child) == "mo" {
3656+ let op_text = as_text ( child) ;
3657+ if op_text == open_text {
3658+ nesting_level += 1 ;
3659+ } else if op_text == close_text {
3660+ if nesting_level == 1 {
3661+ return Some ( & right_siblings[ 1 ..i] ) ;
3662+ }
3663+ nesting_level -= 1 ;
3664+ }
3665+ }
3666+ i += 1 ;
3667+ }
3668+ return None ; // didn't find matching paren
3669+ }
3670+
3671+ fn is_name_inside_contents ( function_name : & str , contents : & [ ChildOfElement ] ) -> bool {
3672+ for & child in contents {
3673+ let child = as_element ( child) ;
3674+ // debug!("is_name_inside_contents: child={}", mml_to_string(child));
3675+ if is_leaf ( child) {
3676+ let text = as_text ( child) ;
3677+ if ( name ( child) == "mi" || name ( child) == "mtext" ) && text == function_name {
3678+ return true ;
3679+ }
3680+ } else if is_name_inside_contents ( function_name, & child. children ( ) ) {
3681+ return true ;
3682+ }
3683+ }
3684+ return false ;
3685+ }
3686+ }
36343687 }
36353688
36363689 fn is_mixed_fraction < ' a > ( & self , integer_part : Element < ' a > , fraction_children : & [ ChildOfElement < ' a > ] ) -> Result < bool > {
0 commit comments