Skip to content

Commit 16cd471

Browse files
committed
Added another heuristic to "is_function_name".
If the "function name" is also in side of parens, then it is times, not maybe. For example, "n(n+1)" now will read as "n times open n plus 1 close". Before, times would be omitted.
1 parent a6cb0c1 commit 16cd471

File tree

1 file changed

+55
-2
lines changed

1 file changed

+55
-2
lines changed

src/canonicalize.rs

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)