operators.js (7673B)
1 "use strict"; 2 3 Object.defineProperty(exports, "__esModule", { 4 value: true 5 }); 6 exports.getAssociativity = getAssociativity; 7 exports.getPrecedence = getPrecedence; 8 exports.isAssociativeWith = isAssociativeWith; 9 exports.properties = void 0; 10 11 var _object = require("../utils/object.js"); 12 13 // list of identifiers of nodes in order of their precedence 14 // also contains information about left/right associativity 15 // and which other operator the operator is associative with 16 // Example: 17 // addition is associative with addition and subtraction, because: 18 // (a+b)+c=a+(b+c) 19 // (a+b)-c=a+(b-c) 20 // 21 // postfix operators are left associative, prefix operators 22 // are right associative 23 // 24 // It's also possible to set the following properties: 25 // latexParens: if set to false, this node doesn't need to be enclosed 26 // in parentheses when using LaTeX 27 // latexLeftParens: if set to false, this !OperatorNode's! 28 // left argument doesn't need to be enclosed 29 // in parentheses 30 // latexRightParens: the same for the right argument 31 var properties = [{ 32 // assignment 33 AssignmentNode: {}, 34 FunctionAssignmentNode: {} 35 }, { 36 // conditional expression 37 ConditionalNode: { 38 latexLeftParens: false, 39 latexRightParens: false, 40 latexParens: false // conditionals don't need parentheses in LaTeX because 41 // they are 2 dimensional 42 43 } 44 }, { 45 // logical or 46 'OperatorNode:or': { 47 associativity: 'left', 48 associativeWith: [] 49 } 50 }, { 51 // logical xor 52 'OperatorNode:xor': { 53 associativity: 'left', 54 associativeWith: [] 55 } 56 }, { 57 // logical and 58 'OperatorNode:and': { 59 associativity: 'left', 60 associativeWith: [] 61 } 62 }, { 63 // bitwise or 64 'OperatorNode:bitOr': { 65 associativity: 'left', 66 associativeWith: [] 67 } 68 }, { 69 // bitwise xor 70 'OperatorNode:bitXor': { 71 associativity: 'left', 72 associativeWith: [] 73 } 74 }, { 75 // bitwise and 76 'OperatorNode:bitAnd': { 77 associativity: 'left', 78 associativeWith: [] 79 } 80 }, { 81 // relational operators 82 'OperatorNode:equal': { 83 associativity: 'left', 84 associativeWith: [] 85 }, 86 'OperatorNode:unequal': { 87 associativity: 'left', 88 associativeWith: [] 89 }, 90 'OperatorNode:smaller': { 91 associativity: 'left', 92 associativeWith: [] 93 }, 94 'OperatorNode:larger': { 95 associativity: 'left', 96 associativeWith: [] 97 }, 98 'OperatorNode:smallerEq': { 99 associativity: 'left', 100 associativeWith: [] 101 }, 102 'OperatorNode:largerEq': { 103 associativity: 'left', 104 associativeWith: [] 105 }, 106 RelationalNode: { 107 associativity: 'left', 108 associativeWith: [] 109 } 110 }, { 111 // bitshift operators 112 'OperatorNode:leftShift': { 113 associativity: 'left', 114 associativeWith: [] 115 }, 116 'OperatorNode:rightArithShift': { 117 associativity: 'left', 118 associativeWith: [] 119 }, 120 'OperatorNode:rightLogShift': { 121 associativity: 'left', 122 associativeWith: [] 123 } 124 }, { 125 // unit conversion 126 'OperatorNode:to': { 127 associativity: 'left', 128 associativeWith: [] 129 } 130 }, { 131 // range 132 RangeNode: {} 133 }, { 134 // addition, subtraction 135 'OperatorNode:add': { 136 associativity: 'left', 137 associativeWith: ['OperatorNode:add', 'OperatorNode:subtract'] 138 }, 139 'OperatorNode:subtract': { 140 associativity: 'left', 141 associativeWith: [] 142 } 143 }, { 144 // multiply, divide, modulus 145 'OperatorNode:multiply': { 146 associativity: 'left', 147 associativeWith: ['OperatorNode:multiply', 'OperatorNode:divide', 'Operator:dotMultiply', 'Operator:dotDivide'] 148 }, 149 'OperatorNode:divide': { 150 associativity: 'left', 151 associativeWith: [], 152 latexLeftParens: false, 153 latexRightParens: false, 154 latexParens: false // fractions don't require parentheses because 155 // they're 2 dimensional, so parens aren't needed 156 // in LaTeX 157 158 }, 159 'OperatorNode:dotMultiply': { 160 associativity: 'left', 161 associativeWith: ['OperatorNode:multiply', 'OperatorNode:divide', 'OperatorNode:dotMultiply', 'OperatorNode:doDivide'] 162 }, 163 'OperatorNode:dotDivide': { 164 associativity: 'left', 165 associativeWith: [] 166 }, 167 'OperatorNode:mod': { 168 associativity: 'left', 169 associativeWith: [] 170 } 171 }, { 172 // unary prefix operators 173 'OperatorNode:unaryPlus': { 174 associativity: 'right' 175 }, 176 'OperatorNode:unaryMinus': { 177 associativity: 'right' 178 }, 179 'OperatorNode:bitNot': { 180 associativity: 'right' 181 }, 182 'OperatorNode:not': { 183 associativity: 'right' 184 } 185 }, { 186 // exponentiation 187 'OperatorNode:pow': { 188 associativity: 'right', 189 associativeWith: [], 190 latexRightParens: false // the exponent doesn't need parentheses in 191 // LaTeX because it's 2 dimensional 192 // (it's on top) 193 194 }, 195 'OperatorNode:dotPow': { 196 associativity: 'right', 197 associativeWith: [] 198 } 199 }, { 200 // factorial 201 'OperatorNode:factorial': { 202 associativity: 'left' 203 } 204 }, { 205 // matrix transpose 206 'OperatorNode:transpose': { 207 associativity: 'left' 208 } 209 }]; 210 /** 211 * Get the precedence of a Node. 212 * Higher number for higher precedence, starting with 0. 213 * Returns null if the precedence is undefined. 214 * 215 * @param {Node} _node 216 * @param {string} parenthesis 217 * @return {number | null} 218 */ 219 220 exports.properties = properties; 221 222 function getPrecedence(_node, parenthesis) { 223 var node = _node; 224 225 if (parenthesis !== 'keep') { 226 // ParenthesisNodes are only ignored when not in 'keep' mode 227 node = _node.getContent(); 228 } 229 230 var identifier = node.getIdentifier(); 231 232 for (var i = 0; i < properties.length; i++) { 233 if (identifier in properties[i]) { 234 return i; 235 } 236 } 237 238 return null; 239 } 240 /** 241 * Get the associativity of an operator (left or right). 242 * Returns a string containing 'left' or 'right' or null if 243 * the associativity is not defined. 244 * 245 * @param {Node} _node 246 * @param {string} parenthesis 247 * @return {string|null} 248 * @throws {Error} 249 */ 250 251 252 function getAssociativity(_node, parenthesis) { 253 var node = _node; 254 255 if (parenthesis !== 'keep') { 256 // ParenthesisNodes are only ignored when not in 'keep' mode 257 node = _node.getContent(); 258 } 259 260 var identifier = node.getIdentifier(); 261 var index = getPrecedence(node, parenthesis); 262 263 if (index === null) { 264 // node isn't in the list 265 return null; 266 } 267 268 var property = properties[index][identifier]; 269 270 if ((0, _object.hasOwnProperty)(property, 'associativity')) { 271 if (property.associativity === 'left') { 272 return 'left'; 273 } 274 275 if (property.associativity === 'right') { 276 return 'right'; 277 } // associativity is invalid 278 279 280 throw Error('\'' + identifier + '\' has the invalid associativity \'' + property.associativity + '\'.'); 281 } // associativity is undefined 282 283 284 return null; 285 } 286 /** 287 * Check if an operator is associative with another operator. 288 * Returns either true or false or null if not defined. 289 * 290 * @param {Node} nodeA 291 * @param {Node} nodeB 292 * @param {string} parenthesis 293 * @return {boolean | null} 294 */ 295 296 297 function isAssociativeWith(nodeA, nodeB, parenthesis) { 298 // ParenthesisNodes are only ignored when not in 'keep' mode 299 var a = parenthesis !== 'keep' ? nodeA.getContent() : nodeA; 300 var b = parenthesis !== 'keep' ? nodeA.getContent() : nodeB; 301 var identifierA = a.getIdentifier(); 302 var identifierB = b.getIdentifier(); 303 var index = getPrecedence(a, parenthesis); 304 305 if (index === null) { 306 // node isn't in the list 307 return null; 308 } 309 310 var property = properties[index][identifierA]; 311 312 if ((0, _object.hasOwnProperty)(property, 'associativeWith') && property.associativeWith instanceof Array) { 313 for (var i = 0; i < property.associativeWith.length; i++) { 314 if (property.associativeWith[i] === identifierB) { 315 return true; 316 } 317 } 318 319 return false; 320 } // associativeWith is not defined 321 322 323 return null; 324 }