mirror of
https://github.com/josdejong/mathjs.git
synced 2026-01-18 14:59:29 +00:00
Implement 'all' parenthesis option
This commit is contained in:
parent
c6f22e3663
commit
aa7ca9178f
@ -75,15 +75,24 @@ function factory (type, config, load, typed) {
|
||||
return new AssignmentNode(this.name, this.expr);
|
||||
};
|
||||
|
||||
/*
|
||||
* Is parenthesis needed?
|
||||
* @private
|
||||
*/
|
||||
function needParenthesis(node) {
|
||||
var precedence = operators.getPrecedence(node, config);
|
||||
var exprPrecedence = operators.getPrecedence(node.expr, config);
|
||||
return (config.parenthesis === 'all')
|
||||
|| ((exprPrecedence !== null) && (exprPrecedence <= precedence));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get string representation
|
||||
* @return {String}
|
||||
*/
|
||||
AssignmentNode.prototype._toString = function() {
|
||||
var precedence = operators.getPrecedence(this, config);
|
||||
var exprPrecedence = operators.getPrecedence(this.expr, config);
|
||||
var expr = this.expr.toString();
|
||||
if ((exprPrecedence !== null) && (exprPrecedence <= precedence)) {
|
||||
if (needParenthesis(this)) {
|
||||
expr = '(' + expr + ')';
|
||||
}
|
||||
return this.name + ' = ' + expr;
|
||||
@ -95,11 +104,8 @@ function factory (type, config, load, typed) {
|
||||
* @return {String}
|
||||
*/
|
||||
AssignmentNode.prototype._toTex = function(callbacks) {
|
||||
var precedence = operators.getPrecedence(this, config);
|
||||
var exprPrecedence = operators.getPrecedence(this.expr, config);
|
||||
|
||||
var expr = this.expr.toTex(callbacks);
|
||||
if ((exprPrecedence !== null) && (exprPrecedence <= precedence)) {
|
||||
if (needParenthesis(this)) {
|
||||
expr = '\\left(' + expr + '\\right)';
|
||||
}
|
||||
|
||||
|
||||
@ -133,21 +133,24 @@ function factory (type, config, load, typed) {
|
||||
//purely based on aesthetics and readability
|
||||
var condition = this.condition.toString();
|
||||
var conditionPrecedence = operators.getPrecedence(this.condition, config);
|
||||
if ((this.condition.type === 'OperatorNode')
|
||||
if ((config.parenthesis === 'all')
|
||||
|| (this.condition.type === 'OperatorNode')
|
||||
|| ((conditionPrecedence !== null) && (conditionPrecedence <= precedence))) {
|
||||
condition = '(' + condition + ')';
|
||||
}
|
||||
|
||||
var trueExpr = this.trueExpr.toString();
|
||||
var truePrecedence = operators.getPrecedence(this.trueExpr, config);
|
||||
if ((this.trueExpr.type === 'OperatorNode')
|
||||
if ((config.parenthesis === 'all')
|
||||
|| (this.trueExpr.type === 'OperatorNode')
|
||||
|| ((truePrecedence !== null) && (truePrecedence <= precedence))) {
|
||||
trueExpr = '(' + trueExpr + ')';
|
||||
}
|
||||
|
||||
var falseExpr = this.falseExpr.toString();
|
||||
var falsePrecedence = operators.getPrecedence(this.falseExpr, config);
|
||||
if ((this.falseExpr.type === 'OperatorNode')
|
||||
if ((config.parenthesis === 'all')
|
||||
|| (this.falseExpr.type === 'OperatorNode')
|
||||
|| ((falsePrecedence !== null) && (falsePrecedence <= precedence))) {
|
||||
falseExpr = '(' + falseExpr + ')';
|
||||
}
|
||||
|
||||
@ -99,16 +99,25 @@ function factory (type, config, load, typed) {
|
||||
return new FunctionAssignmentNode(this.name, this.params.slice(0), this.expr);
|
||||
};
|
||||
|
||||
/**
|
||||
* Is parenthesis needed?
|
||||
* @private
|
||||
*/
|
||||
function needParenthesis(node) {
|
||||
var precedence = operators.getPrecedence(node, config);
|
||||
var exprPrecedence = operators.getPrecedence(node.expr, config);
|
||||
|
||||
return (config.parenthesis === 'all')
|
||||
|| ((exprPrecedence !== null) && (exprPrecedence <= precedence));
|
||||
}
|
||||
|
||||
/**
|
||||
* get string representation
|
||||
* @return {String} str
|
||||
*/
|
||||
FunctionAssignmentNode.prototype._toString = function () {
|
||||
var precedence = operators.getPrecedence(this, config);
|
||||
var exprPrecedence = operators.getPrecedence(this.expr, config);
|
||||
|
||||
var expr = this.expr.toString();
|
||||
if ((exprPrecedence !== null) && (exprPrecedence <= precedence)) {
|
||||
if (needParenthesis(this)) {
|
||||
expr = '(' + expr + ')';
|
||||
}
|
||||
return 'function ' + this.name +
|
||||
@ -121,11 +130,8 @@ function factory (type, config, load, typed) {
|
||||
* @return {String} str
|
||||
*/
|
||||
FunctionAssignmentNode.prototype._toTex = function (callbacks) {
|
||||
var precedence = operators.getPrecedence(this, config);
|
||||
var exprPrecedence = operators.getPrecedence(this.expr, config);
|
||||
|
||||
var expr = this.expr.toTex(callbacks);
|
||||
if ((exprPrecedence !== null) && (exprPrecedence <= precedence)) {
|
||||
if (needParenthesis(this)) {
|
||||
expr = '\\left(' + expr + '\\right)';
|
||||
}
|
||||
|
||||
|
||||
@ -203,13 +203,34 @@ function factory (type, config, load, typed) {
|
||||
return new IndexNode(this.object, this.ranges.slice(0));
|
||||
};
|
||||
|
||||
/**
|
||||
* Is parenthesis needed?
|
||||
* @private
|
||||
*/
|
||||
function needParenthesis(node) {
|
||||
switch (node.object.type) {
|
||||
case 'ArrayNode':
|
||||
case 'ConstantNode': //TODO don't know if this one makes sense
|
||||
case 'SymbolNode':
|
||||
case 'ParenthesisNode':
|
||||
//those nodes don't need parentheses within an index node
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get string representation
|
||||
* @return {String} str
|
||||
*/
|
||||
IndexNode.prototype._toString = function () {
|
||||
var object = this.object.toString();
|
||||
if (needParenthesis(this)) {
|
||||
object = '(' + object + '(';
|
||||
}
|
||||
// format the parameters like "[1, 0:5]"
|
||||
return this.object.toString() + '[' + this.ranges.join(', ') + ']';
|
||||
return object + '[' + this.ranges.join(', ') + ']';
|
||||
};
|
||||
|
||||
/**
|
||||
@ -218,10 +239,16 @@ function factory (type, config, load, typed) {
|
||||
* @return {String} str
|
||||
*/
|
||||
IndexNode.prototype._toTex = function (callbacks) {
|
||||
var object = this.object.toTex(callbacks);
|
||||
if (needParenthesis(this)) {
|
||||
object = '\\left(' + object + '\\right)';
|
||||
}
|
||||
|
||||
var ranges = this.ranges.map(function (range) {
|
||||
return range.toTex(callbacks);
|
||||
});
|
||||
return this.object.toTex(callbacks) + '_{\\left[' + ranges.join(',') + '\\right]}';
|
||||
|
||||
return object + '_{' + ranges.join(',') + '}';
|
||||
};
|
||||
|
||||
return IndexNode;
|
||||
|
||||
@ -114,7 +114,26 @@ function factory (type, config, load, typed, math) {
|
||||
var precedence = operators.getPrecedence(root, config);
|
||||
var associativity = operators.getAssociativity(root, config);
|
||||
|
||||
if ((config.parenthesis === 'all') || (args.length > 2)) {
|
||||
var parens = [];
|
||||
args.forEach(function (arg) {
|
||||
switch (arg.getContent().type) { //Nodes that don't need extra parentheses
|
||||
case 'ArrayNode':
|
||||
case 'ConstantNode':
|
||||
case 'SymbolNode':
|
||||
case 'ParenthesisNode':
|
||||
parens.push(false);
|
||||
break;
|
||||
default:
|
||||
parens.push(true);
|
||||
}
|
||||
});
|
||||
return parens;
|
||||
}
|
||||
|
||||
switch (args.length) {
|
||||
case 0:
|
||||
return [];
|
||||
case 1: //unary operators
|
||||
//precedence of the operand
|
||||
var operandPrecedence = operators.getPrecedence(args[0], config);
|
||||
@ -244,13 +263,6 @@ function factory (type, config, load, typed, math) {
|
||||
}
|
||||
|
||||
return [lhsParens, rhsParens];
|
||||
default:
|
||||
//behavior is undefined, fall back to putting everything in parens
|
||||
var parens = [];
|
||||
args.forEach(function () {
|
||||
parens.push(true);
|
||||
});
|
||||
return parens;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -85,35 +85,59 @@ function factory (type, config, load, typed) {
|
||||
return new RangeNode(this.start, this.end, this.step && this.step);
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculate the necessary parentheses
|
||||
* @param {Node} node
|
||||
* @return {Object} parentheses
|
||||
* @private
|
||||
*/
|
||||
function calculateNecessaryParentheses(node) {
|
||||
var precedence = operators.getPrecedence(node, config);
|
||||
var parens = {};
|
||||
|
||||
var startPrecedence = operators.getPrecedence(node.start, config);
|
||||
parens.start = ((startPrecedence !== null) && (startPrecedence <= precedence))
|
||||
|| (config.parenthesis === 'all');
|
||||
|
||||
if (node.step) {
|
||||
var stepPrecedence = operators.getPrecedence(node.step, config);
|
||||
parens.step = ((stepPrecedence !== null) && (stepPrecedence <= precedence))
|
||||
|| (config.parenthesis === 'all');
|
||||
}
|
||||
|
||||
var endPrecedence = operators.getPrecedence(node.end, config);
|
||||
parens.end = ((endPrecedence !== null) && (endPrecedence <= precedence))
|
||||
|| (config.parenthesis === 'all');
|
||||
|
||||
return parens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get string representation
|
||||
* @return {String} str
|
||||
*/
|
||||
RangeNode.prototype._toString = function () {
|
||||
var precedence = operators.getPrecedence(this, config);
|
||||
var parens = calculateNecessaryParentheses(this);
|
||||
|
||||
//format string as start:step:stop
|
||||
var str;
|
||||
|
||||
var start = this.start.toString();
|
||||
var startPrecedence = operators.getPrecedence(this.start, config);
|
||||
if ((startPrecedence !== null) && (startPrecedence <= precedence)) {
|
||||
if (parens.start) {
|
||||
start = '(' + start + ')';
|
||||
}
|
||||
str = start;
|
||||
|
||||
if (this.step) {
|
||||
var step = this.step.toString();
|
||||
var stepPrecedence = operators.getPrecedence(this.step, config);
|
||||
if ((stepPrecedence !== null) && (stepPrecedence <= precedence)) {
|
||||
if (parens.step) {
|
||||
step = '(' + step + ')';
|
||||
}
|
||||
str += ':' + step;
|
||||
}
|
||||
|
||||
var end = this.end.toString();
|
||||
var endPrecedence = operators.getPrecedence(this.end, config);
|
||||
if ((endPrecedence !== null) && (endPrecedence <= precedence)) {
|
||||
if (parens.end) {
|
||||
end = '(' + end + ')';
|
||||
}
|
||||
str += ':' + end;
|
||||
@ -127,11 +151,26 @@ function factory (type, config, load, typed) {
|
||||
* @return {String} str
|
||||
*/
|
||||
RangeNode.prototype._toTex = function (callbacks) {
|
||||
var parens = calculateNecessaryParentheses(this);
|
||||
|
||||
var str = this.start.toTex(callbacks);
|
||||
if (this.step) {
|
||||
str += ':' + this.step.toTex(callbacks);
|
||||
if (parens.start) {
|
||||
str = '\\left(' + str + '\\right)';
|
||||
}
|
||||
str += ':' + this.end.toTex(callbacks);
|
||||
|
||||
if (this.step) {
|
||||
var step = this.step.toTex(callbacks);
|
||||
if (parens.step) {
|
||||
step = '\\left(' + step + '\\right)';
|
||||
}
|
||||
str += ':' + step;
|
||||
}
|
||||
|
||||
var end = this.end.toTex(callbacks);
|
||||
if (parens.end) {
|
||||
end = '\\left(' + end + '\\right)';
|
||||
}
|
||||
str += ':' + end;
|
||||
|
||||
return str;
|
||||
};
|
||||
|
||||
@ -87,7 +87,11 @@ function factory (type, config, load, typed) {
|
||||
* @return {String}
|
||||
*/
|
||||
UpdateNode.prototype._toString = function () {
|
||||
return this.index.toString() + ' = ' + this.expr._toString();
|
||||
var expr = this.expr.toString();
|
||||
if (config.parenthesis === 'all') {
|
||||
expr = '(' + expr + ')';
|
||||
}
|
||||
return this.index.toString() + ' = ' + expr;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -96,7 +100,11 @@ function factory (type, config, load, typed) {
|
||||
* @return {String}
|
||||
*/
|
||||
UpdateNode.prototype._toTex = function (callbacks) {
|
||||
return this.index.toTex(callbacks) + ':=' + this.expr.toTex(callbacks);
|
||||
var expr = this.expr.toTex();
|
||||
if (config.parenthesis === 'all') {
|
||||
expr = '\\left(' + expr + '\\right)';
|
||||
}
|
||||
return this.index.toTex(callbacks) + ':=' + expr;
|
||||
};
|
||||
|
||||
return UpdateNode;
|
||||
|
||||
@ -336,6 +336,7 @@ exports.toSymbol = function (name, isUnit) {
|
||||
};
|
||||
|
||||
//returns the latex output for a given function
|
||||
//TODO: this doesn't yet use the different parenthesis options
|
||||
exports.toFunction = function (node, callbacks, name) {
|
||||
var latexConverter = functions[name];
|
||||
var args = node.args.map(function (arg) { //get LaTeX of the arguments
|
||||
|
||||
@ -198,6 +198,14 @@ describe('AssignmentNode', function() {
|
||||
assert.strictEqual(e.expr, d.expr);
|
||||
});
|
||||
|
||||
it ('should respect the \'all\' parenthesis option', function () {
|
||||
var allMath = math.create({parenthesis: 'all'});
|
||||
|
||||
var expr = allMath.parse('a=1');
|
||||
assert.equal(expr.toString(), 'a = (1)');
|
||||
assert.equal(expr.toTex(), 'a:=\\left(1\\right)');
|
||||
});
|
||||
|
||||
it ('should stringify a AssignmentNode', function () {
|
||||
var b = new ConstantNode(3);
|
||||
var n = new AssignmentNode('b', b);
|
||||
|
||||
@ -253,6 +253,12 @@ describe('ConditionalNode', function() {
|
||||
assert.strictEqual(d.falseExpr, c.falseExpr);
|
||||
});
|
||||
|
||||
it ('should respect the \'all\' parenthesis option', function () {
|
||||
var allMath = math.create({parenthesis: 'all'});
|
||||
|
||||
assert.equal(allMath.parse('a?b:c').toString(), '(a) ? (b) : (c)');
|
||||
});
|
||||
|
||||
it ('should stringify a ConditionalNode', function () {
|
||||
var n = new ConditionalNode(condition, a, b);
|
||||
|
||||
|
||||
@ -245,6 +245,14 @@ describe('FunctionAssignmentNode', function() {
|
||||
assert.strictEqual(e.expr, d.expr);
|
||||
});
|
||||
|
||||
it ('should respect the \'all\' parenthesis option', function () {
|
||||
var allMath = math.create({parenthesis: 'all'});
|
||||
|
||||
var expr = allMath.parse('f(x)=x+1');
|
||||
assert.equal(expr.toString(), 'function f(x) = (x + 1)');
|
||||
assert.equal(expr.toTex(), '\\mathrm{f}\\left(x\\right):=\\left( x+1\\right)');
|
||||
});
|
||||
|
||||
it ('should stringify a FunctionAssignmentNode', function () {
|
||||
var a = new ConstantNode(2);
|
||||
var x = new SymbolNode('x');
|
||||
|
||||
@ -285,10 +285,10 @@ describe('IndexNode', function() {
|
||||
];
|
||||
|
||||
var n = new IndexNode(a, ranges);
|
||||
assert.equal(n.toTex(), ' a_{\\left[2,1\\right]}');
|
||||
assert.equal(n.toTex(), ' a_{2,1}');
|
||||
|
||||
var n2 = new IndexNode(a, []);
|
||||
assert.equal(n2.toTex(), ' a_{\\left[\\right]}')
|
||||
assert.equal(n2.toTex(), ' a_{}')
|
||||
});
|
||||
|
||||
it ('should LaTeX an IndexNode with custom toTex', function () {
|
||||
|
||||
@ -284,6 +284,19 @@ describe('OperatorNode', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it ('should respect the \'all\' parenthesis option', function () {
|
||||
var allMath = math.create({parenthesis: 'all'});
|
||||
|
||||
assert.equal(allMath.parse('1+1+1').toString(), '(1 + 1) + 1' );
|
||||
assert.equal(allMath.parse('1+1+1').toTex(), '\\left(1+1\\right)+1' );
|
||||
});
|
||||
|
||||
it ('should correctly LaTeX fractions in \'all\' parenthesis mode', function () {
|
||||
var allMath = math.create({parenthesis: 'all'});
|
||||
|
||||
assert.equal(allMath.parse('1/2/3').toTex(), '\\frac{\\left(\\frac{1}{2}\\right)}{3}');
|
||||
});
|
||||
|
||||
it ('should LaTeX an OperatorNode', function () {
|
||||
var a = new ConstantNode(2);
|
||||
var b = new ConstantNode(3);
|
||||
|
||||
@ -280,6 +280,13 @@ describe('RangeNode', function() {
|
||||
assert.equal(n.toString(), '(0:10):2:100');
|
||||
});
|
||||
|
||||
it ('should respect the \'all\' parenthesis option', function () {
|
||||
var allMath = math.create({parenthesis: 'all'});
|
||||
|
||||
assert.equal(allMath.parse('1:2:3').toString(), '(1):(2):(3)');
|
||||
assert.equal(allMath.parse('1:2:3').toTex(), '\\left(1\\right):\\left(2\\right):\\left(3\\right)');
|
||||
});
|
||||
|
||||
it ('should LaTeX a RangeNode without step', function () {
|
||||
var start = new ConstantNode(0);
|
||||
var end = new ConstantNode(10);
|
||||
|
||||
@ -321,6 +321,13 @@ describe('UpdateNode', function() {
|
||||
assert.equal(n.toString(), 'a[2, 1] = 5');
|
||||
});
|
||||
|
||||
it ('should respect the \'all\' parenthesis option', function () {
|
||||
var allMath = math.create({parenthesis: 'all'});
|
||||
|
||||
assert.equal(allMath.parse('a[1]=2').toString(), 'a[1] = (2)' );
|
||||
assert.equal(allMath.parse('a[1]=2').toTex(), ' a_{1}:=\\left(2\\right)' );
|
||||
});
|
||||
|
||||
it ('should LaTeX an UpdateNode', function () {
|
||||
var a = new SymbolNode('a');
|
||||
var ranges = [
|
||||
@ -330,7 +337,7 @@ describe('UpdateNode', function() {
|
||||
var v = new ConstantNode(5);
|
||||
|
||||
var n = new UpdateNode(new IndexNode(a, ranges), v);
|
||||
assert.equal(n.toTex(), ' a_{\\left[2,1\\right]}:=5');
|
||||
assert.equal(n.toTex(), ' a_{2,1}:=5');
|
||||
});
|
||||
|
||||
it ('should LaTeX an UpdateNode with custom toTex', function () {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user