UNPKG

9.7 kBJavaScriptView Raw
1"use strict";
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6exports.createConditionalNode = void 0;
7
8var _is = require("../../utils/is");
9
10var _factory = require("../../utils/factory");
11
12var _operators = require("../operators");
13
14var name = 'ConditionalNode';
15var dependencies = ['Node'];
16var createConditionalNode =
17/* #__PURE__ */
18(0, _factory.factory)(name, dependencies, function (_ref) {
19 var Node = _ref.Node;
20
21 /**
22 * A lazy evaluating conditional operator: 'condition ? trueExpr : falseExpr'
23 *
24 * @param {Node} condition Condition, must result in a boolean
25 * @param {Node} trueExpr Expression evaluated when condition is true
26 * @param {Node} falseExpr Expression evaluated when condition is true
27 *
28 * @constructor ConditionalNode
29 * @extends {Node}
30 */
31 function ConditionalNode(condition, trueExpr, falseExpr) {
32 if (!(this instanceof ConditionalNode)) {
33 throw new SyntaxError('Constructor must be called with the new operator');
34 }
35
36 if (!(0, _is.isNode)(condition)) throw new TypeError('Parameter condition must be a Node');
37 if (!(0, _is.isNode)(trueExpr)) throw new TypeError('Parameter trueExpr must be a Node');
38 if (!(0, _is.isNode)(falseExpr)) throw new TypeError('Parameter falseExpr must be a Node');
39 this.condition = condition;
40 this.trueExpr = trueExpr;
41 this.falseExpr = falseExpr;
42 }
43
44 ConditionalNode.prototype = new Node();
45 ConditionalNode.prototype.type = 'ConditionalNode';
46 ConditionalNode.prototype.isConditionalNode = true;
47 /**
48 * Compile a node into a JavaScript function.
49 * This basically pre-calculates as much as possible and only leaves open
50 * calculations which depend on a dynamic scope with variables.
51 * @param {Object} math Math.js namespace with functions and constants.
52 * @param {Object} argNames An object with argument names as key and `true`
53 * as value. Used in the SymbolNode to optimize
54 * for arguments from user assigned functions
55 * (see FunctionAssignmentNode) or special symbols
56 * like `end` (see IndexNode).
57 * @return {function} Returns a function which can be called like:
58 * evalNode(scope: Object, args: Object, context: *)
59 */
60
61 ConditionalNode.prototype._compile = function (math, argNames) {
62 var evalCondition = this.condition._compile(math, argNames);
63
64 var evalTrueExpr = this.trueExpr._compile(math, argNames);
65
66 var evalFalseExpr = this.falseExpr._compile(math, argNames);
67
68 return function evalConditionalNode(scope, args, context) {
69 return testCondition(evalCondition(scope, args, context)) ? evalTrueExpr(scope, args, context) : evalFalseExpr(scope, args, context);
70 };
71 };
72 /**
73 * Execute a callback for each of the child nodes of this node
74 * @param {function(child: Node, path: string, parent: Node)} callback
75 */
76
77
78 ConditionalNode.prototype.forEach = function (callback) {
79 callback(this.condition, 'condition', this);
80 callback(this.trueExpr, 'trueExpr', this);
81 callback(this.falseExpr, 'falseExpr', this);
82 };
83 /**
84 * Create a new ConditionalNode having it's childs be the results of calling
85 * the provided callback function for each of the childs of the original node.
86 * @param {function(child: Node, path: string, parent: Node): Node} callback
87 * @returns {ConditionalNode} Returns a transformed copy of the node
88 */
89
90
91 ConditionalNode.prototype.map = function (callback) {
92 return new ConditionalNode(this._ifNode(callback(this.condition, 'condition', this)), this._ifNode(callback(this.trueExpr, 'trueExpr', this)), this._ifNode(callback(this.falseExpr, 'falseExpr', this)));
93 };
94 /**
95 * Create a clone of this node, a shallow copy
96 * @return {ConditionalNode}
97 */
98
99
100 ConditionalNode.prototype.clone = function () {
101 return new ConditionalNode(this.condition, this.trueExpr, this.falseExpr);
102 };
103 /**
104 * Get string representation
105 * @param {Object} options
106 * @return {string} str
107 */
108
109
110 ConditionalNode.prototype._toString = function (options) {
111 var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep';
112 var precedence = (0, _operators.getPrecedence)(this, parenthesis); // Enclose Arguments in parentheses if they are an OperatorNode
113 // or have lower or equal precedence
114 // NOTE: enclosing all OperatorNodes in parentheses is a decision
115 // purely based on aesthetics and readability
116
117 var condition = this.condition.toString(options);
118 var conditionPrecedence = (0, _operators.getPrecedence)(this.condition, parenthesis);
119
120 if (parenthesis === 'all' || this.condition.type === 'OperatorNode' || conditionPrecedence !== null && conditionPrecedence <= precedence) {
121 condition = '(' + condition + ')';
122 }
123
124 var trueExpr = this.trueExpr.toString(options);
125 var truePrecedence = (0, _operators.getPrecedence)(this.trueExpr, parenthesis);
126
127 if (parenthesis === 'all' || this.trueExpr.type === 'OperatorNode' || truePrecedence !== null && truePrecedence <= precedence) {
128 trueExpr = '(' + trueExpr + ')';
129 }
130
131 var falseExpr = this.falseExpr.toString(options);
132 var falsePrecedence = (0, _operators.getPrecedence)(this.falseExpr, parenthesis);
133
134 if (parenthesis === 'all' || this.falseExpr.type === 'OperatorNode' || falsePrecedence !== null && falsePrecedence <= precedence) {
135 falseExpr = '(' + falseExpr + ')';
136 }
137
138 return condition + ' ? ' + trueExpr + ' : ' + falseExpr;
139 };
140 /**
141 * Get a JSON representation of the node
142 * @returns {Object}
143 */
144
145
146 ConditionalNode.prototype.toJSON = function () {
147 return {
148 mathjs: 'ConditionalNode',
149 condition: this.condition,
150 trueExpr: this.trueExpr,
151 falseExpr: this.falseExpr
152 };
153 };
154 /**
155 * Instantiate an ConditionalNode from its JSON representation
156 * @param {Object} json An object structured like
157 * `{"mathjs": "ConditionalNode", "condition": ..., "trueExpr": ..., "falseExpr": ...}`,
158 * where mathjs is optional
159 * @returns {ConditionalNode}
160 */
161
162
163 ConditionalNode.fromJSON = function (json) {
164 return new ConditionalNode(json.condition, json.trueExpr, json.falseExpr);
165 };
166 /**
167 * Get HTML representation
168 * @param {Object} options
169 * @return {string} str
170 */
171
172
173 ConditionalNode.prototype.toHTML = function (options) {
174 var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep';
175 var precedence = (0, _operators.getPrecedence)(this, parenthesis); // Enclose Arguments in parentheses if they are an OperatorNode
176 // or have lower or equal precedence
177 // NOTE: enclosing all OperatorNodes in parentheses is a decision
178 // purely based on aesthetics and readability
179
180 var condition = this.condition.toHTML(options);
181 var conditionPrecedence = (0, _operators.getPrecedence)(this.condition, parenthesis);
182
183 if (parenthesis === 'all' || this.condition.type === 'OperatorNode' || conditionPrecedence !== null && conditionPrecedence <= precedence) {
184 condition = '<span class="math-parenthesis math-round-parenthesis">(</span>' + condition + '<span class="math-parenthesis math-round-parenthesis">)</span>';
185 }
186
187 var trueExpr = this.trueExpr.toHTML(options);
188 var truePrecedence = (0, _operators.getPrecedence)(this.trueExpr, parenthesis);
189
190 if (parenthesis === 'all' || this.trueExpr.type === 'OperatorNode' || truePrecedence !== null && truePrecedence <= precedence) {
191 trueExpr = '<span class="math-parenthesis math-round-parenthesis">(</span>' + trueExpr + '<span class="math-parenthesis math-round-parenthesis">)</span>';
192 }
193
194 var falseExpr = this.falseExpr.toHTML(options);
195 var falsePrecedence = (0, _operators.getPrecedence)(this.falseExpr, parenthesis);
196
197 if (parenthesis === 'all' || this.falseExpr.type === 'OperatorNode' || falsePrecedence !== null && falsePrecedence <= precedence) {
198 falseExpr = '<span class="math-parenthesis math-round-parenthesis">(</span>' + falseExpr + '<span class="math-parenthesis math-round-parenthesis">)</span>';
199 }
200
201 return condition + '<span class="math-operator math-conditional-operator">?</span>' + trueExpr + '<span class="math-operator math-conditional-operator">:</span>' + falseExpr;
202 };
203 /**
204 * Get LaTeX representation
205 * @param {Object} options
206 * @return {string} str
207 */
208
209
210 ConditionalNode.prototype._toTex = function (options) {
211 return '\\begin{cases} {' + this.trueExpr.toTex(options) + '}, &\\quad{\\text{if }\\;' + this.condition.toTex(options) + '}\\\\{' + this.falseExpr.toTex(options) + '}, &\\quad{\\text{otherwise}}\\end{cases}';
212 };
213 /**
214 * Test whether a condition is met
215 * @param {*} condition
216 * @returns {boolean} true if condition is true or non-zero, else false
217 */
218
219
220 function testCondition(condition) {
221 if (typeof condition === 'number' || typeof condition === 'boolean' || typeof condition === 'string') {
222 return !!condition;
223 }
224
225 if (condition) {
226 if ((0, _is.isBigNumber)(condition)) {
227 return !condition.isZero();
228 }
229
230 if ((0, _is.isComplex)(condition)) {
231 return !!(condition.re || condition.im);
232 }
233
234 if ((0, _is.isUnit)(condition)) {
235 return !!condition.value;
236 }
237 }
238
239 if (condition === null || condition === undefined) {
240 return false;
241 }
242
243 throw new TypeError('Unsupported type of condition "' + (0, _is.typeOf)(condition) + '"');
244 }
245
246 return ConditionalNode;
247}, {
248 isClass: true,
249 isNode: true
250});
251exports.createConditionalNode = createConditionalNode;
\No newline at end of file