UNPKG

7.67 kBJavaScriptView Raw
1"use strict";
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6exports.getPrecedence = getPrecedence;
7exports.getAssociativity = getAssociativity;
8exports.isAssociativeWith = isAssociativeWith;
9exports.properties = void 0;
10
11var _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
31var 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
220exports.properties = properties;
221
222function 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
252function 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
297function 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}
\No newline at end of file