mirror of
https://github.com/josdejong/mathjs.git
synced 2026-01-25 15:07:57 +00:00
operators: Use config when calculating precedence etc.
Use the parenthesis configuration to decide wether ParenthesisNodes should be skipped or not.
This commit is contained in:
parent
60e2b5700a
commit
2959858b99
@ -80,8 +80,8 @@ function factory (type, config, load, typed) {
|
||||
* @return {String}
|
||||
*/
|
||||
AssignmentNode.prototype._toString = function() {
|
||||
var precedence = operators.getPrecedence(this);
|
||||
var exprPrecedence = operators.getPrecedence(this.expr);
|
||||
var precedence = operators.getPrecedence(this, config);
|
||||
var exprPrecedence = operators.getPrecedence(this.expr, config);
|
||||
var expr = this.expr.toString();
|
||||
if ((exprPrecedence !== null) && (exprPrecedence <= precedence)) {
|
||||
expr = '(' + expr + ')';
|
||||
@ -95,8 +95,8 @@ function factory (type, config, load, typed) {
|
||||
* @return {String}
|
||||
*/
|
||||
AssignmentNode.prototype._toTex = function(callbacks) {
|
||||
var precedence = operators.getPrecedence(this);
|
||||
var exprPrecedence = operators.getPrecedence(this.expr);
|
||||
var precedence = operators.getPrecedence(this, config);
|
||||
var exprPrecedence = operators.getPrecedence(this.expr, config);
|
||||
|
||||
var expr = this.expr.toTex(callbacks);
|
||||
if ((exprPrecedence !== null) && (exprPrecedence <= precedence)) {
|
||||
|
||||
@ -125,28 +125,28 @@ function factory (type, config, load, typed) {
|
||||
* @return {String} str
|
||||
*/
|
||||
ConditionalNode.prototype._toString = function () {
|
||||
var precedence = operators.getPrecedence(this);
|
||||
var precedence = operators.getPrecedence(this, config);
|
||||
|
||||
//Enclose Arguments in parentheses if they are an OperatorNode
|
||||
//or have lower or equal precedence
|
||||
//NOTE: enclosing all OperatorNodes in parentheses is a decision
|
||||
//purely based on aesthetics and readability
|
||||
var condition = this.condition.toString();
|
||||
var conditionPrecedence = operators.getPrecedence(this.condition);
|
||||
var conditionPrecedence = operators.getPrecedence(this.condition, config);
|
||||
if ((this.condition.type === 'OperatorNode')
|
||||
|| ((conditionPrecedence !== null) && (conditionPrecedence <= precedence))) {
|
||||
condition = '(' + condition + ')';
|
||||
}
|
||||
|
||||
var trueExpr = this.trueExpr.toString();
|
||||
var truePrecedence = operators.getPrecedence(this.trueExpr);
|
||||
var truePrecedence = operators.getPrecedence(this.trueExpr, config);
|
||||
if ((this.trueExpr.type === 'OperatorNode')
|
||||
|| ((truePrecedence !== null) && (truePrecedence <= precedence))) {
|
||||
trueExpr = '(' + trueExpr + ')';
|
||||
}
|
||||
|
||||
var falseExpr = this.falseExpr.toString();
|
||||
var falsePrecedence = operators.getPrecedence(this.falseExpr);
|
||||
var falsePrecedence = operators.getPrecedence(this.falseExpr, config);
|
||||
if ((this.falseExpr.type === 'OperatorNode')
|
||||
|| ((falsePrecedence !== null) && (falsePrecedence <= precedence))) {
|
||||
falseExpr = '(' + falseExpr + ')';
|
||||
|
||||
@ -104,8 +104,8 @@ function factory (type, config, load, typed) {
|
||||
* @return {String} str
|
||||
*/
|
||||
FunctionAssignmentNode.prototype._toString = function () {
|
||||
var precedence = operators.getPrecedence(this);
|
||||
var exprPrecedence = operators.getPrecedence(this.expr);
|
||||
var precedence = operators.getPrecedence(this, config);
|
||||
var exprPrecedence = operators.getPrecedence(this.expr, config);
|
||||
|
||||
var expr = this.expr.toString();
|
||||
if ((exprPrecedence !== null) && (exprPrecedence <= precedence)) {
|
||||
@ -121,8 +121,8 @@ function factory (type, config, load, typed) {
|
||||
* @return {String} str
|
||||
*/
|
||||
FunctionAssignmentNode.prototype._toTex = function (callbacks) {
|
||||
var precedence = operators.getPrecedence(this);
|
||||
var exprPrecedence = operators.getPrecedence(this.expr);
|
||||
var precedence = operators.getPrecedence(this, config);
|
||||
var exprPrecedence = operators.getPrecedence(this.expr, config);
|
||||
|
||||
var expr = this.expr.toTex(callbacks);
|
||||
if ((exprPrecedence !== null) && (exprPrecedence <= precedence)) {
|
||||
|
||||
@ -111,13 +111,13 @@ function factory (type, config, load, typed, math) {
|
||||
*/
|
||||
function calculateNecessaryParentheses(root, args, latex) {
|
||||
//precedence of the root OperatorNode
|
||||
var precedence = operators.getPrecedence(root);
|
||||
var associativity = operators.getAssociativity(root);
|
||||
var precedence = operators.getPrecedence(root, config);
|
||||
var associativity = operators.getAssociativity(root, config);
|
||||
|
||||
switch (args.length) {
|
||||
case 1: //unary operators
|
||||
//precedence of the operand
|
||||
var operandPrecedence = operators.getPrecedence(args[0]);
|
||||
var operandPrecedence = operators.getPrecedence(args[0], config);
|
||||
|
||||
//handle special cases for LaTeX, where some of the parentheses aren't needed
|
||||
if (latex && (operandPrecedence !== null)) {
|
||||
@ -148,9 +148,9 @@ function factory (type, config, load, typed, math) {
|
||||
case 2: //binary operators
|
||||
var lhsParens; //left hand side needs parenthesis?
|
||||
//precedence of the left hand side
|
||||
var lhsPrecedence = operators.getPrecedence(args[0]);
|
||||
var lhsPrecedence = operators.getPrecedence(args[0], config);
|
||||
//is the root node associative with the left hand side
|
||||
var assocWithLhs = operators.isAssociativeWith(root, args[0]);
|
||||
var assocWithLhs = operators.isAssociativeWith(root, args[0], config);
|
||||
|
||||
if (lhsPrecedence === null) {
|
||||
//if the left hand side has no defined precedence, no parens are needed
|
||||
@ -173,9 +173,9 @@ function factory (type, config, load, typed, math) {
|
||||
|
||||
var rhsParens; //right hand side needs parenthesis?
|
||||
//precedence of the right hand side
|
||||
var rhsPrecedence = operators.getPrecedence(args[1]);
|
||||
var rhsPrecedence = operators.getPrecedence(args[1], config);
|
||||
//is the root node associative with the right hand side?
|
||||
var assocWithRhs = operators.isAssociativeWith(root, args[1]);
|
||||
var assocWithRhs = operators.isAssociativeWith(root, args[1], config);
|
||||
|
||||
if (rhsPrecedence === null) {
|
||||
//if the right hand side has no defined precedence, no parens are needed
|
||||
@ -244,7 +244,7 @@ function factory (type, config, load, typed, math) {
|
||||
|
||||
switch (args.length) {
|
||||
case 1: //unary operators
|
||||
var assoc = operators.getAssociativity(this);
|
||||
var assoc = operators.getAssociativity(this, config);
|
||||
|
||||
var operand = args[0].toString();
|
||||
if (parens[0]) {
|
||||
@ -292,7 +292,7 @@ function factory (type, config, load, typed, math) {
|
||||
|
||||
switch (args.length) {
|
||||
case 1: //unary operators
|
||||
var assoc = operators.getAssociativity(this);
|
||||
var assoc = operators.getAssociativity(this, config);
|
||||
|
||||
var operand = args[0].toTex(callbacks);
|
||||
if (parens[0]) {
|
||||
|
||||
@ -90,13 +90,13 @@ function factory (type, config, load, typed) {
|
||||
* @return {String} str
|
||||
*/
|
||||
RangeNode.prototype._toString = function () {
|
||||
var precedence = operators.getPrecedence(this);
|
||||
var precedence = operators.getPrecedence(this, config);
|
||||
|
||||
//format string as start:step:stop
|
||||
var str;
|
||||
|
||||
var start = this.start.toString();
|
||||
var startPrecedence = operators.getPrecedence(this.start);
|
||||
var startPrecedence = operators.getPrecedence(this.start, config);
|
||||
if ((startPrecedence !== null) && (startPrecedence <= precedence)) {
|
||||
start = '(' + start + ')';
|
||||
}
|
||||
@ -104,7 +104,7 @@ function factory (type, config, load, typed) {
|
||||
|
||||
if (this.step) {
|
||||
var step = this.step.toString();
|
||||
var stepPrecedence = operators.getPrecedence(this.step);
|
||||
var stepPrecedence = operators.getPrecedence(this.step, config);
|
||||
if ((stepPrecedence !== null) && (stepPrecedence <= precedence)) {
|
||||
step = '(' + step + ')';
|
||||
}
|
||||
@ -112,7 +112,7 @@ function factory (type, config, load, typed) {
|
||||
}
|
||||
|
||||
var end = this.end.toString();
|
||||
var endPrecedence = operators.getPrecedence(this.end);
|
||||
var endPrecedence = operators.getPrecedence(this.end, config);
|
||||
if ((endPrecedence !== null) && (endPrecedence <= precedence)) {
|
||||
end = '(' + end + ')';
|
||||
}
|
||||
|
||||
@ -214,7 +214,12 @@ var properties = [
|
||||
* @param {Node}
|
||||
* @return {Number|null}
|
||||
*/
|
||||
function getPrecedence (node) {
|
||||
function getPrecedence (_node, config) {
|
||||
var node = _node;
|
||||
if (config.parenthesis !== 'keep') {
|
||||
//ParenthesisNodes are only ignored when not in 'keep' mode
|
||||
node = _node.getContent();
|
||||
}
|
||||
var identifier = node.getIdentifier();
|
||||
for (var i = 0; i < properties.length; i++) {
|
||||
if (identifier in properties[i]) {
|
||||
@ -233,9 +238,14 @@ function getPrecedence (node) {
|
||||
* @return {String|null}
|
||||
* @throws {Error}
|
||||
*/
|
||||
function getAssociativity (node) {
|
||||
function getAssociativity (_node, config) {
|
||||
var node = _node;
|
||||
if (config.parenthesis !== 'keep') {
|
||||
//ParenthesisNodes are only ignored when not in 'keep' mode
|
||||
node = _node.getContent();
|
||||
}
|
||||
var identifier = node.getIdentifier();
|
||||
var index = getPrecedence(node);
|
||||
var index = getPrecedence(node, config);
|
||||
if (index === null) {
|
||||
//node isn't in the list
|
||||
return null;
|
||||
@ -264,12 +274,20 @@ function getAssociativity (node) {
|
||||
*
|
||||
* @param {Node} nodeA
|
||||
* @param {Node} nodeB
|
||||
* @param {bool} doGetContent
|
||||
* @return {bool|null}
|
||||
*/
|
||||
function isAssociativeWith (nodeA, nodeB) {
|
||||
var identifierA = nodeA.getIdentifier();
|
||||
var identifierB = nodeB.getIdentifier();
|
||||
var index = getPrecedence(nodeA);
|
||||
function isAssociativeWith (nodeA, nodeB, config) {
|
||||
var a = nodeA;
|
||||
var b = nodeB;
|
||||
if (config.parenthesis !== 'keep') {
|
||||
//ParenthesisNodes are only ignored when not in 'keep' mode
|
||||
var a = nodeA.getContent();
|
||||
var b = nodeB.getContent();
|
||||
}
|
||||
var identifierA = a.getIdentifier();
|
||||
var identifierB = b.getIdentifier();
|
||||
var index = getPrecedence(a, config);
|
||||
if (index === null) {
|
||||
//node isn't in the list
|
||||
return null;
|
||||
|
||||
@ -1,12 +1,14 @@
|
||||
var assert = require('assert');
|
||||
|
||||
var math = require('../../core').create();
|
||||
var math = require('../../index');
|
||||
var operators = require('../../lib/expression/operators');
|
||||
var OperatorNode = math.import(require('../../lib/expression/node/OperatorNode'));
|
||||
var AssignmentNode = math.import(require('../../lib/expression/node/AssignmentNode'));
|
||||
var ConstantNode = math.import(require('../../lib/expression/node/ConstantNode'));
|
||||
var Node = math.import(require('../../lib/expression/node/Node'));
|
||||
var OperatorNode = math.expression.node.OperatorNode;
|
||||
var AssignmentNode = math.expression.node.AssignmentNode;
|
||||
var ConstantNode = math.expression.node.ConstantNode;
|
||||
var Node = math.expression.node.Node;
|
||||
var ParenthesisNode = math.expression.node.ParenthesisNode;
|
||||
|
||||
var config = {parenthesis: 'keep'};
|
||||
|
||||
describe('operators', function () {
|
||||
it('should return the precedence of a node', function () {
|
||||
@ -16,14 +18,26 @@ describe('operators', function () {
|
||||
var n1 = new AssignmentNode('a', a);
|
||||
var n2 = new OperatorNode('or', 'or', [a, b]);
|
||||
|
||||
assert.equal(operators.getPrecedence(n1), 0);
|
||||
assert.equal(operators.getPrecedence(n2), 2);
|
||||
assert.equal(operators.getPrecedence(n1, config), 0);
|
||||
assert.equal(operators.getPrecedence(n2, config), 2);
|
||||
});
|
||||
|
||||
it('should return null if precedence is not defined for a node', function () {
|
||||
var n = new Node();
|
||||
|
||||
assert.equal(operators.getPrecedence(n), null);
|
||||
assert.equal(operators.getPrecedence(n, config), null);
|
||||
});
|
||||
|
||||
it ('should return the precedence of a ParenthesisNode', function () {
|
||||
var c = new ConstantNode(1);
|
||||
|
||||
var op = new OperatorNode('or', 'or', [c, c]);
|
||||
|
||||
var p = new ParenthesisNode(op);
|
||||
|
||||
assert.equal(operators.getPrecedence(p, {parenthesis: 'all'}), operators.getPrecedence(op, config));
|
||||
assert.equal(operators.getPrecedence(p, {parenthesis: 'auto'}), operators.getPrecedence(op, config));
|
||||
assert.equal(operators.getPrecedence(p, config), null);
|
||||
});
|
||||
|
||||
it('should return the associativity of a node', function () {
|
||||
@ -34,10 +48,22 @@ describe('operators', function () {
|
||||
var n3 = new OperatorNode('-', 'unaryMinus', [a]);
|
||||
var n4 = new OperatorNode('!', 'factorial', [a]);
|
||||
|
||||
assert.equal(operators.getAssociativity(n1), 'left');
|
||||
assert.equal(operators.getAssociativity(n2), 'right');
|
||||
assert.equal(operators.getAssociativity(n3), 'right');
|
||||
assert.equal(operators.getAssociativity(n4), 'left');
|
||||
assert.equal(operators.getAssociativity(n1, config), 'left');
|
||||
assert.equal(operators.getAssociativity(n2, config), 'right');
|
||||
assert.equal(operators.getAssociativity(n3, config), 'right');
|
||||
assert.equal(operators.getAssociativity(n4, config), 'left');
|
||||
});
|
||||
|
||||
it ('should return the associativity of a ParenthesisNode', function () {
|
||||
var c = new ConstantNode(1);
|
||||
|
||||
var op = new OperatorNode('or', 'or', [c, c]);
|
||||
|
||||
var p = new ParenthesisNode(op);
|
||||
|
||||
assert.equal(operators.getAssociativity(p, {parenthesis: 'all'}), operators.getAssociativity(op, config));
|
||||
assert.equal(operators.getAssociativity(p, {parenthesis: 'auto'}), operators.getAssociativity(op, config));
|
||||
assert.equal(operators.getAssociativity(p, config), null);
|
||||
});
|
||||
|
||||
it('should return null if associativity is not defined for a node', function () {
|
||||
@ -46,8 +72,8 @@ describe('operators', function () {
|
||||
var n1 = new Node();
|
||||
var n2 = new AssignmentNode('a', a);
|
||||
|
||||
assert.equal(operators.getAssociativity(n1), null);
|
||||
assert.equal(operators.getAssociativity(n2), null);
|
||||
assert.equal(operators.getAssociativity(n1, config), null);
|
||||
assert.equal(operators.getAssociativity(n2, config), null);
|
||||
});
|
||||
|
||||
it('should return if a Node is associative with another Node', function () {
|
||||
@ -56,10 +82,10 @@ describe('operators', function () {
|
||||
var n1 = new OperatorNode('+', 'add', [a, a]);
|
||||
var n2 = new OperatorNode('-', 'subtract', [a, a]);
|
||||
|
||||
assert.equal(operators.isAssociativeWith(n1, n1), true);
|
||||
assert.equal(operators.isAssociativeWith(n1, n2), true);
|
||||
assert.equal(operators.isAssociativeWith(n2, n2), false);
|
||||
assert.equal(operators.isAssociativeWith(n2, n1), false);
|
||||
assert.equal(operators.isAssociativeWith(n1, n1, config), true);
|
||||
assert.equal(operators.isAssociativeWith(n1, n2, config), true);
|
||||
assert.equal(operators.isAssociativeWith(n2, n2, config), false);
|
||||
assert.equal(operators.isAssociativeWith(n2, n1, config), false);
|
||||
});
|
||||
|
||||
it('should return null if the associativity between two Nodes is not defined', function () {
|
||||
@ -68,9 +94,22 @@ describe('operators', function () {
|
||||
var n1 = new Node();
|
||||
var n2 = new AssignmentNode('a', a);
|
||||
|
||||
assert.equal(operators.isAssociativeWith(n1, n1), null);
|
||||
assert.equal(operators.isAssociativeWith(n1, n2), null);
|
||||
assert.equal(operators.isAssociativeWith(n2, n2), null);
|
||||
assert.equal(operators.isAssociativeWith(n2, n1), null);
|
||||
assert.equal(operators.isAssociativeWith(n1, n1, config), null);
|
||||
assert.equal(operators.isAssociativeWith(n1, n2, config), null);
|
||||
assert.equal(operators.isAssociativeWith(n2, n2, config), null);
|
||||
assert.equal(operators.isAssociativeWith(n2, n1, config), null);
|
||||
});
|
||||
|
||||
it ('should return if a ParenthesisNode is associative with another Node', function () {
|
||||
var a = new ConstantNode(1);
|
||||
|
||||
var add = new OperatorNode('+', 'add', [a, a]);
|
||||
var sub = new OperatorNode('-', 'subtract', [a, a]);
|
||||
|
||||
var p = new ParenthesisNode(add);
|
||||
|
||||
assert.equal(operators.isAssociativeWith(p, sub, {parenthesis: 'all'}), true);
|
||||
assert.equal(operators.isAssociativeWith(p, sub, {parenthesis: 'auto'}), true);
|
||||
assert.equal(operators.isAssociativeWith(p, sub, config), null);
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user