mirror of
https://github.com/josdejong/mathjs.git
synced 2025-12-08 19:46:04 +00:00
317 lines
7.7 KiB
JavaScript
317 lines
7.7 KiB
JavaScript
'use strict'
|
|
|
|
//list of identifiers of nodes in order of their precedence
|
|
//also contains information about left/right associativity
|
|
//and which other operator the operator is associative with
|
|
//Example:
|
|
// addition is associative with addition and subtraction, because:
|
|
// (a+b)+c=a+(b+c)
|
|
// (a+b)-c=a+(b-c)
|
|
//
|
|
// postfix operators are left associative, prefix operators
|
|
// are right associative
|
|
//
|
|
//It's also possible to set the following properties:
|
|
// latexParens: if set to false, this node doesn't need to be enclosed
|
|
// in parentheses when using LaTeX
|
|
// latexLeftParens: if set to false, this !OperatorNode's!
|
|
// left argument doesn't need to be enclosed
|
|
// in parentheses
|
|
// latexRightParens: the same for the right argument
|
|
var properties = [
|
|
{ //assignment
|
|
'AssignmentNode': {},
|
|
'FunctionAssignmentNode': {}
|
|
},
|
|
{ //conditional expression
|
|
'ConditionalNode': {
|
|
latexLeftParens: false,
|
|
latexRightParens: false,
|
|
latexParens: false
|
|
//conditionals don't need parentheses in LaTeX because
|
|
//they are 2 dimensional
|
|
}
|
|
},
|
|
{ //logical or
|
|
'OperatorNode:or': {
|
|
associativity: 'left',
|
|
associativeWith: []
|
|
}
|
|
|
|
},
|
|
{ //logical xor
|
|
'OperatorNode:xor': {
|
|
associativity: 'left',
|
|
associativeWith: []
|
|
}
|
|
},
|
|
{ //logical and
|
|
'OperatorNode:and': {
|
|
associativity: 'left',
|
|
associativeWith: []
|
|
}
|
|
},
|
|
{ //bitwise or
|
|
'OperatorNode:bitOr': {
|
|
associativity: 'left',
|
|
associativeWith: []
|
|
}
|
|
},
|
|
{ //bitwise xor
|
|
'OperatorNode:bitXor': {
|
|
associativity: 'left',
|
|
associativeWith: []
|
|
}
|
|
},
|
|
{ //bitwise and
|
|
'OperatorNode:bitAnd': {
|
|
associativity: 'left',
|
|
associativeWith: []
|
|
}
|
|
},
|
|
{ //relational operators
|
|
'OperatorNode:equal': {
|
|
associativity: 'left',
|
|
associativeWith: []
|
|
},
|
|
'OperatorNode:unequal': {
|
|
associativity: 'left',
|
|
associativeWith: []
|
|
},
|
|
'OperatorNode:smaller': {
|
|
associativity: 'left',
|
|
associativeWith: []
|
|
},
|
|
'OperatorNode:larger': {
|
|
associativity: 'left',
|
|
associativeWith: []
|
|
},
|
|
'OperatorNode:smallerEq': {
|
|
associativity: 'left',
|
|
associativeWith: []
|
|
},
|
|
'OperatorNode:largerEq': {
|
|
associativity: 'left',
|
|
associativeWith: []
|
|
}
|
|
},
|
|
{ //bitshift operators
|
|
'OperatorNode:leftShift': {
|
|
associativity: 'left',
|
|
associativeWith: []
|
|
},
|
|
'OperatorNode:rightArithShift': {
|
|
associativity: 'left',
|
|
associativeWith: []
|
|
},
|
|
'OperatorNode:rightLogShift': {
|
|
associativity: 'left',
|
|
associativeWith: []
|
|
}
|
|
},
|
|
{ //unit conversion
|
|
'OperatorNode:to': {
|
|
associativity: 'left',
|
|
associativeWith: []
|
|
}
|
|
},
|
|
{ //range
|
|
'RangeNode': {}
|
|
},
|
|
{ //addition, subtraction
|
|
'OperatorNode:add': {
|
|
associativity: 'left',
|
|
associativeWith: ['OperatorNode:add', 'OperatorNode:subtract']
|
|
},
|
|
'OperatorNode:subtract': {
|
|
associativity: 'left',
|
|
associativeWith: []
|
|
}
|
|
},
|
|
{ //multiply, divide, modulus
|
|
'OperatorNode:multiply': {
|
|
associativity: 'left',
|
|
associativeWith: [
|
|
'OperatorNode:multiply',
|
|
'OperatorNode:divide',
|
|
'Operator:dotMultiply',
|
|
'Operator:dotDivide'
|
|
]
|
|
},
|
|
'OperatorNode:divide': {
|
|
associativity: 'left',
|
|
associativeWith: [],
|
|
latexLeftParens: false,
|
|
latexRightParens: false,
|
|
latexParens: false
|
|
//fractions don't require parentheses because
|
|
//they're 2 dimensional, so parens aren't needed
|
|
//in LaTeX
|
|
},
|
|
'OperatorNode:dotMultiply': {
|
|
associativity: 'left',
|
|
associativeWith: [
|
|
'OperatorNode:multiply',
|
|
'OperatorNode:divide',
|
|
'OperatorNode:dotMultiply',
|
|
'OperatorNode:doDivide'
|
|
]
|
|
},
|
|
'OperatorNode:dotDivide': {
|
|
associativity: 'left',
|
|
associativeWith: []
|
|
},
|
|
'OperatorNode:mod': {
|
|
associativity: 'left',
|
|
associativeWith: []
|
|
}
|
|
},
|
|
{ //unary prefix operators
|
|
'OperatorNode:unaryPlus': {
|
|
associativity: 'right'
|
|
},
|
|
'OperatorNode:unaryMinus': {
|
|
associativity: 'right'
|
|
},
|
|
'OperatorNode:bitNot': {
|
|
associativity: 'right'
|
|
},
|
|
'OperatorNode:not': {
|
|
associativity: 'right'
|
|
}
|
|
},
|
|
{ //exponentiation
|
|
'OperatorNode:pow': {
|
|
associativity: 'right',
|
|
associativeWith: [],
|
|
latexRightParens: false
|
|
//the exponent doesn't need parentheses in
|
|
//LaTeX because it's 2 dimensional
|
|
//(it's on top)
|
|
},
|
|
'OperatorNode:dotPow': {
|
|
associativity: 'right',
|
|
associativeWith: []
|
|
}
|
|
},
|
|
{ //factorial
|
|
'OperatorNode:factorial': {
|
|
associativity: 'left'
|
|
}
|
|
},
|
|
{ //matrix transpose
|
|
'OperatorNode:transpose': {
|
|
associativity: 'left'
|
|
}
|
|
}
|
|
];
|
|
|
|
/**
|
|
* Get the precedence of a Node.
|
|
* Higher number for higher precedence, starting with 0.
|
|
* Returns null if the precedence is undefined.
|
|
*
|
|
* @param {Node}
|
|
* @param {string} parenthesis
|
|
* @return {number|null}
|
|
*/
|
|
function getPrecedence (_node, parenthesis) {
|
|
var node = _node;
|
|
if (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]) {
|
|
return i;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Get the associativity of an operator (left or right).
|
|
* Returns a string containing 'left' or 'right' or null if
|
|
* the associativity is not defined.
|
|
*
|
|
* @param {Node}
|
|
* @param {string} parenthesis
|
|
* @return {string|null}
|
|
* @throws {Error}
|
|
*/
|
|
function getAssociativity (_node, parenthesis) {
|
|
var node = _node;
|
|
if (parenthesis !== 'keep') {
|
|
//ParenthesisNodes are only ignored when not in 'keep' mode
|
|
node = _node.getContent();
|
|
}
|
|
var identifier = node.getIdentifier();
|
|
var index = getPrecedence(node, parenthesis);
|
|
if (index === null) {
|
|
//node isn't in the list
|
|
return null;
|
|
}
|
|
var property = properties[index][identifier];
|
|
|
|
if (property.hasOwnProperty('associativity')) {
|
|
if (property.associativity === 'left') {
|
|
return 'left';
|
|
}
|
|
if (property.associativity === 'right') {
|
|
return 'right';
|
|
}
|
|
//associativity is invalid
|
|
throw Error('\'' + identifier + '\' has the invalid associativity \''
|
|
+ property.associativity + '\'.');
|
|
}
|
|
|
|
//associativity is undefined
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Check if an operator is associative with another operator.
|
|
* Returns either true or false or null if not defined.
|
|
*
|
|
* @param {Node} nodeA
|
|
* @param {Node} nodeB
|
|
* @param {string} parenthesis
|
|
* @return {bool|null}
|
|
*/
|
|
function isAssociativeWith (nodeA, nodeB, parenthesis) {
|
|
var a = nodeA;
|
|
var b = nodeB;
|
|
if (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, parenthesis);
|
|
if (index === null) {
|
|
//node isn't in the list
|
|
return null;
|
|
}
|
|
var property = properties[index][identifierA];
|
|
|
|
if (property.hasOwnProperty('associativeWith')
|
|
&& (property.associativeWith instanceof Array)) {
|
|
for (var i = 0; i < property.associativeWith.length; i++) {
|
|
if (property.associativeWith[i] === identifierB) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//associativeWith is not defined
|
|
return null;
|
|
}
|
|
|
|
module.exports.properties = properties;
|
|
module.exports.getPrecedence = getPrecedence;
|
|
module.exports.getAssociativity = getAssociativity;
|
|
module.exports.isAssociativeWith = isAssociativeWith;
|