mathjs
mathjs copied to clipboard
Implicit multiplication inside of sqrt call parsed as FunctionNode
Similar issue as https://github.com/josdejong/mathjs/issues/1035 but we're seeing this on the latest version of mathjs 9.5.0.
Minimal code to reproduce:
const {all, create} = require('mathjs')
const math = create(all)
math.evaluate(
['sqrt(p(1-n/N)/N)'],
{
N: '795',
n: '219',
p: '27.5',
}
)
Results in: TypeError: fn is not a function
The equivalent expression sqrt(p*(1-n/N)/N) works as expected.
In applications that use the expression parsing strategy of a graphing calculator or spreadsheet (e.g. "2(x+1)" and "k(x+1)" both use implicit multiplication) you need to post-process math.parse. I followed the advice given in #2236 and used a transform to do this. Code tests ok so far, advice is welcome.
// test if node is FunctionNode with name of scope or constant symbol
function isConvertableNode(node, scopeSymbols = []) {
const constants = ["e", "E", "pi", "Pi", "phi", "tau", "i"];
if (node.isFunctionNode) {
return (
scopeSymbols.indexOf(node.name) >= 0 || constants.indexOf(node.name) >= 0
);
}
return false;
}
// test if node contains child functions that need conversion
function containsConvertableNode(node, scopeSymbols = []) {
let found = false;
node.traverse(function (node) {
if (isConvertableNode(node, scopeSymbols)) {
found = true;
}
});
return found;
}
function parseSpecial(expr, scopeSymbols = []) {
let node = math.parse(expr);
// transform stops after one change, iterate to transform all child nodes
while (containsConvertableNode(node, scopeSymbols)) {
node = node.transform((node) => {
return isConvertableNode(node, scopeSymbols)
? new math.OperatorNode(
"*",
"multiply",
[
new math.SymbolNode(node.name),
new math.ParenthesisNode(node.args[0]),
],
true // implicit multiply
)
: node;
});
}
return node;
}
// example
const expr = "sqrt(p(1-n/N)/N)";
const scope = { N: "795", n: "219", p: "27.5" };
const scopeSymbols = Object.keys(scope);
console.log(parseSpecial(expr, scopeSymbols).evaluate(scope));
// returns 0.15831076953511722