1 | import { isNode } from '../../utils/is.js';
|
2 | import { map } from '../../utils/array.js';
|
3 | import { escape } from '../../utils/string.js';
|
4 | import { getSafeProperty, isSafeMethod } from '../../utils/customs.js';
|
5 | import { getAssociativity, getPrecedence, isAssociativeWith, properties } from '../operators.js';
|
6 | import { latexOperators } from '../../utils/latex.js';
|
7 | import { factory } from '../../utils/factory.js';
|
8 | var name = 'OperatorNode';
|
9 | var dependencies = ['Node'];
|
10 | export var createOperatorNode = factory(name, dependencies, (_ref) => {
|
11 | var {
|
12 | Node
|
13 | } = _ref;
|
14 |
|
15 | |
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 | function OperatorNode(op, fn, args, implicit) {
|
26 | if (!(this instanceof OperatorNode)) {
|
27 | throw new SyntaxError('Constructor must be called with the new operator');
|
28 | }
|
29 |
|
30 |
|
31 | if (typeof op !== 'string') {
|
32 | throw new TypeError('string expected for parameter "op"');
|
33 | }
|
34 |
|
35 | if (typeof fn !== 'string') {
|
36 | throw new TypeError('string expected for parameter "fn"');
|
37 | }
|
38 |
|
39 | if (!Array.isArray(args) || !args.every(isNode)) {
|
40 | throw new TypeError('Array containing Nodes expected for parameter "args"');
|
41 | }
|
42 |
|
43 | this.implicit = implicit === true;
|
44 | this.op = op;
|
45 | this.fn = fn;
|
46 | this.args = args || [];
|
47 | }
|
48 |
|
49 | OperatorNode.prototype = new Node();
|
50 | OperatorNode.prototype.type = 'OperatorNode';
|
51 | OperatorNode.prototype.isOperatorNode = true;
|
52 | |
53 |
|
54 |
|
55 |
|
56 |
|
57 |
|
58 |
|
59 |
|
60 |
|
61 |
|
62 |
|
63 |
|
64 |
|
65 |
|
66 | OperatorNode.prototype._compile = function (math, argNames) {
|
67 |
|
68 | if (typeof this.fn !== 'string' || !isSafeMethod(math, this.fn)) {
|
69 | if (!math[this.fn]) {
|
70 | throw new Error('Function ' + this.fn + ' missing in provided namespace "math"');
|
71 | } else {
|
72 | throw new Error('No access to function "' + this.fn + '"');
|
73 | }
|
74 | }
|
75 |
|
76 | var fn = getSafeProperty(math, this.fn);
|
77 | var evalArgs = map(this.args, function (arg) {
|
78 | return arg._compile(math, argNames);
|
79 | });
|
80 |
|
81 | if (evalArgs.length === 1) {
|
82 | var evalArg0 = evalArgs[0];
|
83 | return function evalOperatorNode(scope, args, context) {
|
84 | return fn(evalArg0(scope, args, context));
|
85 | };
|
86 | } else if (evalArgs.length === 2) {
|
87 | var _evalArg = evalArgs[0];
|
88 | var evalArg1 = evalArgs[1];
|
89 | return function evalOperatorNode(scope, args, context) {
|
90 | return fn(_evalArg(scope, args, context), evalArg1(scope, args, context));
|
91 | };
|
92 | } else {
|
93 | return function evalOperatorNode(scope, args, context) {
|
94 | return fn.apply(null, map(evalArgs, function (evalArg) {
|
95 | return evalArg(scope, args, context);
|
96 | }));
|
97 | };
|
98 | }
|
99 | };
|
100 | |
101 |
|
102 |
|
103 |
|
104 |
|
105 |
|
106 | OperatorNode.prototype.forEach = function (callback) {
|
107 | for (var i = 0; i < this.args.length; i++) {
|
108 | callback(this.args[i], 'args[' + i + ']', this);
|
109 | }
|
110 | };
|
111 | |
112 |
|
113 |
|
114 |
|
115 |
|
116 |
|
117 |
|
118 |
|
119 | OperatorNode.prototype.map = function (callback) {
|
120 | var args = [];
|
121 |
|
122 | for (var i = 0; i < this.args.length; i++) {
|
123 | args[i] = this._ifNode(callback(this.args[i], 'args[' + i + ']', this));
|
124 | }
|
125 |
|
126 | return new OperatorNode(this.op, this.fn, args, this.implicit);
|
127 | };
|
128 | |
129 |
|
130 |
|
131 |
|
132 |
|
133 |
|
134 | OperatorNode.prototype.clone = function () {
|
135 | return new OperatorNode(this.op, this.fn, this.args.slice(0), this.implicit);
|
136 | };
|
137 | |
138 |
|
139 |
|
140 |
|
141 |
|
142 |
|
143 |
|
144 | OperatorNode.prototype.isUnary = function () {
|
145 | return this.args.length === 1;
|
146 | };
|
147 | |
148 |
|
149 |
|
150 |
|
151 |
|
152 |
|
153 |
|
154 | OperatorNode.prototype.isBinary = function () {
|
155 | return this.args.length === 2;
|
156 | };
|
157 | |
158 |
|
159 |
|
160 |
|
161 |
|
162 |
|
163 |
|
164 |
|
165 |
|
166 |
|
167 |
|
168 |
|
169 |
|
170 |
|
171 |
|
172 | function calculateNecessaryParentheses(root, parenthesis, implicit, args, latex) {
|
173 |
|
174 | var precedence = getPrecedence(root, parenthesis);
|
175 | var associativity = getAssociativity(root, parenthesis);
|
176 |
|
177 | if (parenthesis === 'all' || args.length > 2 && root.getIdentifier() !== 'OperatorNode:add' && root.getIdentifier() !== 'OperatorNode:multiply') {
|
178 | return args.map(function (arg) {
|
179 | switch (arg.getContent().type) {
|
180 |
|
181 | case 'ArrayNode':
|
182 | case 'ConstantNode':
|
183 | case 'SymbolNode':
|
184 | case 'ParenthesisNode':
|
185 | return false;
|
186 |
|
187 | default:
|
188 | return true;
|
189 | }
|
190 | });
|
191 | }
|
192 |
|
193 | var result;
|
194 |
|
195 | switch (args.length) {
|
196 | case 0:
|
197 | result = [];
|
198 | break;
|
199 |
|
200 | case 1:
|
201 |
|
202 | {
|
203 |
|
204 | var operandPrecedence = getPrecedence(args[0], parenthesis);
|
205 |
|
206 | if (latex && operandPrecedence !== null) {
|
207 | var operandIdentifier;
|
208 | var rootIdentifier;
|
209 |
|
210 | if (parenthesis === 'keep') {
|
211 | operandIdentifier = args[0].getIdentifier();
|
212 | rootIdentifier = root.getIdentifier();
|
213 | } else {
|
214 |
|
215 | operandIdentifier = args[0].getContent().getIdentifier();
|
216 | rootIdentifier = root.getContent().getIdentifier();
|
217 | }
|
218 |
|
219 | if (properties[precedence][rootIdentifier].latexLeftParens === false) {
|
220 | result = [false];
|
221 | break;
|
222 | }
|
223 |
|
224 | if (properties[operandPrecedence][operandIdentifier].latexParens === false) {
|
225 | result = [false];
|
226 | break;
|
227 | }
|
228 | }
|
229 |
|
230 | if (operandPrecedence === null) {
|
231 |
|
232 | result = [false];
|
233 | break;
|
234 | }
|
235 |
|
236 | if (operandPrecedence <= precedence) {
|
237 |
|
238 | result = [true];
|
239 | break;
|
240 | }
|
241 |
|
242 |
|
243 | result = [false];
|
244 | }
|
245 | break;
|
246 |
|
247 | case 2:
|
248 |
|
249 | {
|
250 | var lhsParens;
|
251 |
|
252 |
|
253 | var lhsPrecedence = getPrecedence(args[0], parenthesis);
|
254 |
|
255 | var assocWithLhs = isAssociativeWith(root, args[0], parenthesis);
|
256 |
|
257 | if (lhsPrecedence === null) {
|
258 |
|
259 |
|
260 | lhsParens = false;
|
261 | } else if (lhsPrecedence === precedence && associativity === 'right' && !assocWithLhs) {
|
262 |
|
263 |
|
264 |
|
265 |
|
266 | lhsParens = true;
|
267 | } else if (lhsPrecedence < precedence) {
|
268 | lhsParens = true;
|
269 | } else {
|
270 | lhsParens = false;
|
271 | }
|
272 |
|
273 | var rhsParens;
|
274 |
|
275 |
|
276 | var rhsPrecedence = getPrecedence(args[1], parenthesis);
|
277 |
|
278 | var assocWithRhs = isAssociativeWith(root, args[1], parenthesis);
|
279 |
|
280 | if (rhsPrecedence === null) {
|
281 |
|
282 |
|
283 | rhsParens = false;
|
284 | } else if (rhsPrecedence === precedence && associativity === 'left' && !assocWithRhs) {
|
285 |
|
286 |
|
287 |
|
288 |
|
289 | rhsParens = true;
|
290 | } else if (rhsPrecedence < precedence) {
|
291 | rhsParens = true;
|
292 | } else {
|
293 | rhsParens = false;
|
294 | }
|
295 |
|
296 |
|
297 | if (latex) {
|
298 | var _rootIdentifier;
|
299 |
|
300 | var lhsIdentifier;
|
301 | var rhsIdentifier;
|
302 |
|
303 | if (parenthesis === 'keep') {
|
304 | _rootIdentifier = root.getIdentifier();
|
305 | lhsIdentifier = root.args[0].getIdentifier();
|
306 | rhsIdentifier = root.args[1].getIdentifier();
|
307 | } else {
|
308 |
|
309 | _rootIdentifier = root.getContent().getIdentifier();
|
310 | lhsIdentifier = root.args[0].getContent().getIdentifier();
|
311 | rhsIdentifier = root.args[1].getContent().getIdentifier();
|
312 | }
|
313 |
|
314 | if (lhsPrecedence !== null) {
|
315 | if (properties[precedence][_rootIdentifier].latexLeftParens === false) {
|
316 | lhsParens = false;
|
317 | }
|
318 |
|
319 | if (properties[lhsPrecedence][lhsIdentifier].latexParens === false) {
|
320 | lhsParens = false;
|
321 | }
|
322 | }
|
323 |
|
324 | if (rhsPrecedence !== null) {
|
325 | if (properties[precedence][_rootIdentifier].latexRightParens === false) {
|
326 | rhsParens = false;
|
327 | }
|
328 |
|
329 | if (properties[rhsPrecedence][rhsIdentifier].latexParens === false) {
|
330 | rhsParens = false;
|
331 | }
|
332 | }
|
333 | }
|
334 |
|
335 | result = [lhsParens, rhsParens];
|
336 | }
|
337 | break;
|
338 |
|
339 | default:
|
340 | if (root.getIdentifier() === 'OperatorNode:add' || root.getIdentifier() === 'OperatorNode:multiply') {
|
341 | result = args.map(function (arg) {
|
342 | var argPrecedence = getPrecedence(arg, parenthesis);
|
343 | var assocWithArg = isAssociativeWith(root, arg, parenthesis);
|
344 | var argAssociativity = getAssociativity(arg, parenthesis);
|
345 |
|
346 | if (argPrecedence === null) {
|
347 |
|
348 | return false;
|
349 | } else if (precedence === argPrecedence && associativity === argAssociativity && !assocWithArg) {
|
350 | return true;
|
351 | } else if (argPrecedence < precedence) {
|
352 | return true;
|
353 | }
|
354 |
|
355 | return false;
|
356 | });
|
357 | }
|
358 |
|
359 | break;
|
360 | }
|
361 |
|
362 |
|
363 |
|
364 |
|
365 | if (args.length >= 2 && root.getIdentifier() === 'OperatorNode:multiply' && root.implicit && parenthesis === 'auto' && implicit === 'hide') {
|
366 | result = args.map(function (arg, index) {
|
367 | var isParenthesisNode = arg.getIdentifier() === 'ParenthesisNode';
|
368 |
|
369 | if (result[index] || isParenthesisNode) {
|
370 |
|
371 | return true;
|
372 | }
|
373 |
|
374 | return false;
|
375 | });
|
376 | }
|
377 |
|
378 | return result;
|
379 | }
|
380 | |
381 |
|
382 |
|
383 |
|
384 |
|
385 |
|
386 |
|
387 | OperatorNode.prototype._toString = function (options) {
|
388 | var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep';
|
389 | var implicit = options && options.implicit ? options.implicit : 'hide';
|
390 | var args = this.args;
|
391 | var parens = calculateNecessaryParentheses(this, parenthesis, implicit, args, false);
|
392 |
|
393 | if (args.length === 1) {
|
394 |
|
395 | var assoc = getAssociativity(this, parenthesis);
|
396 | var operand = args[0].toString(options);
|
397 |
|
398 | if (parens[0]) {
|
399 | operand = '(' + operand + ')';
|
400 | }
|
401 |
|
402 |
|
403 | var opIsNamed = /[a-zA-Z]+/.test(this.op);
|
404 |
|
405 | if (assoc === 'right') {
|
406 |
|
407 | return this.op + (opIsNamed ? ' ' : '') + operand;
|
408 | } else if (assoc === 'left') {
|
409 |
|
410 | return operand + (opIsNamed ? ' ' : '') + this.op;
|
411 | }
|
412 |
|
413 |
|
414 | return operand + this.op;
|
415 | } else if (args.length === 2) {
|
416 | var lhs = args[0].toString(options);
|
417 |
|
418 | var rhs = args[1].toString(options);
|
419 |
|
420 | if (parens[0]) {
|
421 |
|
422 | lhs = '(' + lhs + ')';
|
423 | }
|
424 |
|
425 | if (parens[1]) {
|
426 |
|
427 | rhs = '(' + rhs + ')';
|
428 | }
|
429 |
|
430 | if (this.implicit && this.getIdentifier() === 'OperatorNode:multiply' && implicit === 'hide') {
|
431 | return lhs + ' ' + rhs;
|
432 | }
|
433 |
|
434 | return lhs + ' ' + this.op + ' ' + rhs;
|
435 | } else if (args.length > 2 && (this.getIdentifier() === 'OperatorNode:add' || this.getIdentifier() === 'OperatorNode:multiply')) {
|
436 | var stringifiedArgs = args.map(function (arg, index) {
|
437 | arg = arg.toString(options);
|
438 |
|
439 | if (parens[index]) {
|
440 |
|
441 | arg = '(' + arg + ')';
|
442 | }
|
443 |
|
444 | return arg;
|
445 | });
|
446 |
|
447 | if (this.implicit && this.getIdentifier() === 'OperatorNode:multiply' && implicit === 'hide') {
|
448 | return stringifiedArgs.join(' ');
|
449 | }
|
450 |
|
451 | return stringifiedArgs.join(' ' + this.op + ' ');
|
452 | } else {
|
453 |
|
454 | return this.fn + '(' + this.args.join(', ') + ')';
|
455 | }
|
456 | };
|
457 | |
458 |
|
459 |
|
460 |
|
461 |
|
462 |
|
463 | OperatorNode.prototype.toJSON = function () {
|
464 | return {
|
465 | mathjs: 'OperatorNode',
|
466 | op: this.op,
|
467 | fn: this.fn,
|
468 | args: this.args,
|
469 | implicit: this.implicit
|
470 | };
|
471 | };
|
472 | |
473 |
|
474 |
|
475 |
|
476 |
|
477 |
|
478 |
|
479 |
|
480 |
|
481 | OperatorNode.fromJSON = function (json) {
|
482 | return new OperatorNode(json.op, json.fn, json.args, json.implicit);
|
483 | };
|
484 | |
485 |
|
486 |
|
487 |
|
488 |
|
489 |
|
490 |
|
491 | OperatorNode.prototype.toHTML = function (options) {
|
492 | var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep';
|
493 | var implicit = options && options.implicit ? options.implicit : 'hide';
|
494 | var args = this.args;
|
495 | var parens = calculateNecessaryParentheses(this, parenthesis, implicit, args, false);
|
496 |
|
497 | if (args.length === 1) {
|
498 |
|
499 | var assoc = getAssociativity(this, parenthesis);
|
500 | var operand = args[0].toHTML(options);
|
501 |
|
502 | if (parens[0]) {
|
503 | operand = '<span class="math-parenthesis math-round-parenthesis">(</span>' + operand + '<span class="math-parenthesis math-round-parenthesis">)</span>';
|
504 | }
|
505 |
|
506 | if (assoc === 'right') {
|
507 |
|
508 | return '<span class="math-operator math-unary-operator math-lefthand-unary-operator">' + escape(this.op) + '</span>' + operand;
|
509 | } else {
|
510 |
|
511 | return operand + '<span class="math-operator math-unary-operator math-righthand-unary-operator">' + escape(this.op) + '</span>';
|
512 | }
|
513 | } else if (args.length === 2) {
|
514 |
|
515 | var lhs = args[0].toHTML(options);
|
516 |
|
517 | var rhs = args[1].toHTML(options);
|
518 |
|
519 | if (parens[0]) {
|
520 |
|
521 | lhs = '<span class="math-parenthesis math-round-parenthesis">(</span>' + lhs + '<span class="math-parenthesis math-round-parenthesis">)</span>';
|
522 | }
|
523 |
|
524 | if (parens[1]) {
|
525 |
|
526 | rhs = '<span class="math-parenthesis math-round-parenthesis">(</span>' + rhs + '<span class="math-parenthesis math-round-parenthesis">)</span>';
|
527 | }
|
528 |
|
529 | if (this.implicit && this.getIdentifier() === 'OperatorNode:multiply' && implicit === 'hide') {
|
530 | return lhs + '<span class="math-operator math-binary-operator math-implicit-binary-operator"></span>' + rhs;
|
531 | }
|
532 |
|
533 | return lhs + '<span class="math-operator math-binary-operator math-explicit-binary-operator">' + escape(this.op) + '</span>' + rhs;
|
534 | } else {
|
535 | var stringifiedArgs = args.map(function (arg, index) {
|
536 | arg = arg.toHTML(options);
|
537 |
|
538 | if (parens[index]) {
|
539 |
|
540 | arg = '<span class="math-parenthesis math-round-parenthesis">(</span>' + arg + '<span class="math-parenthesis math-round-parenthesis">)</span>';
|
541 | }
|
542 |
|
543 | return arg;
|
544 | });
|
545 |
|
546 | if (args.length > 2 && (this.getIdentifier() === 'OperatorNode:add' || this.getIdentifier() === 'OperatorNode:multiply')) {
|
547 | if (this.implicit && this.getIdentifier() === 'OperatorNode:multiply' && implicit === 'hide') {
|
548 | return stringifiedArgs.join('<span class="math-operator math-binary-operator math-implicit-binary-operator"></span>');
|
549 | }
|
550 |
|
551 | return stringifiedArgs.join('<span class="math-operator math-binary-operator math-explicit-binary-operator">' + escape(this.op) + '</span>');
|
552 | } else {
|
553 |
|
554 | return '<span class="math-function">' + escape(this.fn) + '</span><span class="math-paranthesis math-round-parenthesis">(</span>' + stringifiedArgs.join('<span class="math-separator">,</span>') + '<span class="math-paranthesis math-round-parenthesis">)</span>';
|
555 | }
|
556 | }
|
557 | };
|
558 | |
559 |
|
560 |
|
561 |
|
562 |
|
563 |
|
564 |
|
565 | OperatorNode.prototype._toTex = function (options) {
|
566 | var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep';
|
567 | var implicit = options && options.implicit ? options.implicit : 'hide';
|
568 | var args = this.args;
|
569 | var parens = calculateNecessaryParentheses(this, parenthesis, implicit, args, true);
|
570 | var op = latexOperators[this.fn];
|
571 | op = typeof op === 'undefined' ? this.op : op;
|
572 |
|
573 | if (args.length === 1) {
|
574 |
|
575 | var assoc = getAssociativity(this, parenthesis);
|
576 | var operand = args[0].toTex(options);
|
577 |
|
578 | if (parens[0]) {
|
579 | operand = "\\left(".concat(operand, "\\right)");
|
580 | }
|
581 |
|
582 | if (assoc === 'right') {
|
583 |
|
584 | return op + operand;
|
585 | } else if (assoc === 'left') {
|
586 |
|
587 | return operand + op;
|
588 | }
|
589 |
|
590 |
|
591 | return operand + op;
|
592 | } else if (args.length === 2) {
|
593 |
|
594 | var lhs = args[0];
|
595 |
|
596 | var lhsTex = lhs.toTex(options);
|
597 |
|
598 | if (parens[0]) {
|
599 | lhsTex = "\\left(".concat(lhsTex, "\\right)");
|
600 | }
|
601 |
|
602 | var rhs = args[1];
|
603 |
|
604 | var rhsTex = rhs.toTex(options);
|
605 |
|
606 | if (parens[1]) {
|
607 | rhsTex = "\\left(".concat(rhsTex, "\\right)");
|
608 | }
|
609 |
|
610 |
|
611 | var lhsIdentifier;
|
612 |
|
613 | if (parenthesis === 'keep') {
|
614 | lhsIdentifier = lhs.getIdentifier();
|
615 | } else {
|
616 |
|
617 | lhsIdentifier = lhs.getContent().getIdentifier();
|
618 | }
|
619 |
|
620 | switch (this.getIdentifier()) {
|
621 | case 'OperatorNode:divide':
|
622 |
|
623 | return op + '{' + lhsTex + '}' + '{' + rhsTex + '}';
|
624 |
|
625 | case 'OperatorNode:pow':
|
626 | lhsTex = '{' + lhsTex + '}';
|
627 | rhsTex = '{' + rhsTex + '}';
|
628 |
|
629 | switch (lhsIdentifier) {
|
630 | case 'ConditionalNode':
|
631 |
|
632 | case 'OperatorNode:divide':
|
633 | lhsTex = "\\left(".concat(lhsTex, "\\right)");
|
634 | }
|
635 |
|
636 | break;
|
637 |
|
638 | case 'OperatorNode:multiply':
|
639 | if (this.implicit && implicit === 'hide') {
|
640 | return lhsTex + '~' + rhsTex;
|
641 | }
|
642 |
|
643 | }
|
644 |
|
645 | return lhsTex + op + rhsTex;
|
646 | } else if (args.length > 2 && (this.getIdentifier() === 'OperatorNode:add' || this.getIdentifier() === 'OperatorNode:multiply')) {
|
647 | var texifiedArgs = args.map(function (arg, index) {
|
648 | arg = arg.toTex(options);
|
649 |
|
650 | if (parens[index]) {
|
651 | arg = "\\left(".concat(arg, "\\right)");
|
652 | }
|
653 |
|
654 | return arg;
|
655 | });
|
656 |
|
657 | if (this.getIdentifier() === 'OperatorNode:multiply' && this.implicit) {
|
658 | return texifiedArgs.join('~');
|
659 | }
|
660 |
|
661 | return texifiedArgs.join(op);
|
662 | } else {
|
663 |
|
664 |
|
665 |
|
666 | return '\\mathrm{' + this.fn + '}\\left(' + args.map(function (arg) {
|
667 | return arg.toTex(options);
|
668 | }).join(',') + '\\right)';
|
669 | }
|
670 | };
|
671 | |
672 |
|
673 |
|
674 |
|
675 |
|
676 |
|
677 | OperatorNode.prototype.getIdentifier = function () {
|
678 | return this.type + ':' + this.fn;
|
679 | };
|
680 |
|
681 | return OperatorNode;
|
682 | }, {
|
683 | isClass: true,
|
684 | isNode: true
|
685 | }); |
\ | No newline at end of file |