UNPKG

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