324 lines
7.5 KiB
JavaScript
324 lines
7.5 KiB
JavaScript
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
exports.getAssociativity = getAssociativity;
|
|
exports.getPrecedence = getPrecedence;
|
|
exports.isAssociativeWith = isAssociativeWith;
|
|
exports.properties = void 0;
|
|
|
|
var _object = require("../utils/object.js");
|
|
|
|
// 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: []
|
|
},
|
|
RelationalNode: {
|
|
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} _node
|
|
* @param {string} parenthesis
|
|
* @return {number | null}
|
|
*/
|
|
|
|
exports.properties = properties;
|
|
|
|
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} _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 ((0, _object.hasOwnProperty)(property, '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 {boolean | null}
|
|
*/
|
|
|
|
|
|
function isAssociativeWith(nodeA, nodeB, parenthesis) {
|
|
// ParenthesisNodes are only ignored when not in 'keep' mode
|
|
var a = parenthesis !== 'keep' ? nodeA.getContent() : nodeA;
|
|
var b = parenthesis !== 'keep' ? nodeA.getContent() : nodeB;
|
|
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 ((0, _object.hasOwnProperty)(property, '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;
|
|
} |