1 | ;
|
2 |
|
3 | Object.defineProperty(exports, "__esModule", {
|
4 | value: true
|
5 | });
|
6 | exports.createNode = void 0;
|
7 |
|
8 | var _is = require("../../utils/is");
|
9 |
|
10 | var _keywords = require("../keywords");
|
11 |
|
12 | var _object = require("../../utils/object");
|
13 |
|
14 | var _factory = require("../../utils/factory");
|
15 |
|
16 | var _log = require("../../utils/log");
|
17 |
|
18 | function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
|
19 |
|
20 | var name = 'Node';
|
21 | var dependencies = ['mathWithTransform'];
|
22 | var createNode =
|
23 | /* #__PURE__ */
|
24 | (0, _factory.factory)(name, dependencies, function (_ref) {
|
25 | var mathWithTransform = _ref.mathWithTransform;
|
26 |
|
27 | /**
|
28 | * Node
|
29 | */
|
30 | function Node() {
|
31 | if (!(this instanceof Node)) {
|
32 | throw new SyntaxError('Constructor must be called with the new operator');
|
33 | }
|
34 | }
|
35 | /**
|
36 | * Evaluate the node
|
37 | * @param {Object} [scope] Scope to read/write variables
|
38 | * @return {*} Returns the result
|
39 | */
|
40 |
|
41 |
|
42 | Node.prototype.evaluate = function (scope) {
|
43 | return this.compile().evaluate(scope);
|
44 | };
|
45 | /**
|
46 | * Evaluate the node
|
47 | * @param {Object} [scope] Scope to read/write variables
|
48 | * @return {*} Returns the result
|
49 | */
|
50 | // TODO: Deprecated since v6.0.0. Clean up some day
|
51 |
|
52 |
|
53 | Node.prototype.eval = function (scope) {
|
54 | (0, _log.warnOnce)('Method Node.eval is renamed to Node.evaluate. Please use the new method name.');
|
55 | return this.evaluate(scope);
|
56 | };
|
57 |
|
58 | Node.prototype.type = 'Node';
|
59 | Node.prototype.isNode = true;
|
60 | Node.prototype.comment = '';
|
61 | /**
|
62 | * Compile the node into an optimized, evauatable JavaScript function
|
63 | * @return {{evaluate: function([Object])}} object
|
64 | * Returns an object with a function 'evaluate',
|
65 | * which can be invoked as expr.evaluate([scope: Object]),
|
66 | * where scope is an optional object with
|
67 | * variables.
|
68 | */
|
69 |
|
70 | Node.prototype.compile = function () {
|
71 | var expr = this._compile(mathWithTransform, {});
|
72 |
|
73 | var args = {};
|
74 | var context = null;
|
75 |
|
76 | function evaluate(scope) {
|
77 | var s = scope || {};
|
78 |
|
79 | _validateScope(s);
|
80 |
|
81 | return expr(s, args, context);
|
82 | }
|
83 |
|
84 | return {
|
85 | evaluate: evaluate,
|
86 | // TODO: Deprecated since v6.0.0. Clean up some day
|
87 | eval: function deprecatedEval(scope) {
|
88 | (0, _log.warnOnce)('Method eval is renamed to evaluate. Please use the new method.');
|
89 | return evaluate(scope);
|
90 | }
|
91 | };
|
92 | };
|
93 | /**
|
94 | * Compile a node into a JavaScript function.
|
95 | * This basically pre-calculates as much as possible and only leaves open
|
96 | * calculations which depend on a dynamic scope with variables.
|
97 | * @param {Object} math Math.js namespace with functions and constants.
|
98 | * @param {Object} argNames An object with argument names as key and `true`
|
99 | * as value. Used in the SymbolNode to optimize
|
100 | * for arguments from user assigned functions
|
101 | * (see FunctionAssignmentNode) or special symbols
|
102 | * like `end` (see IndexNode).
|
103 | * @return {function} Returns a function which can be called like:
|
104 | * evalNode(scope: Object, args: Object, context: *)
|
105 | */
|
106 |
|
107 |
|
108 | Node.prototype._compile = function (math, argNames) {
|
109 | throw new Error('Method _compile should be implemented by type ' + this.type);
|
110 | };
|
111 | /**
|
112 | * Execute a callback for each of the child nodes of this node
|
113 | * @param {function(child: Node, path: string, parent: Node)} callback
|
114 | */
|
115 |
|
116 |
|
117 | Node.prototype.forEach = function (callback) {
|
118 | // must be implemented by each of the Node implementations
|
119 | throw new Error('Cannot run forEach on a Node interface');
|
120 | };
|
121 | /**
|
122 | * Create a new Node having it's childs be the results of calling
|
123 | * the provided callback function for each of the childs of the original node.
|
124 | * @param {function(child: Node, path: string, parent: Node): Node} callback
|
125 | * @returns {OperatorNode} Returns a transformed copy of the node
|
126 | */
|
127 |
|
128 |
|
129 | Node.prototype.map = function (callback) {
|
130 | // must be implemented by each of the Node implementations
|
131 | throw new Error('Cannot run map on a Node interface');
|
132 | };
|
133 | /**
|
134 | * Validate whether an object is a Node, for use with map
|
135 | * @param {Node} node
|
136 | * @returns {Node} Returns the input if it's a node, else throws an Error
|
137 | * @protected
|
138 | */
|
139 |
|
140 |
|
141 | Node.prototype._ifNode = function (node) {
|
142 | if (!(0, _is.isNode)(node)) {
|
143 | throw new TypeError('Callback function must return a Node');
|
144 | }
|
145 |
|
146 | return node;
|
147 | };
|
148 | /**
|
149 | * Recursively traverse all nodes in a node tree. Executes given callback for
|
150 | * this node and each of its child nodes.
|
151 | * @param {function(node: Node, path: string, parent: Node)} callback
|
152 | * A callback called for every node in the node tree.
|
153 | */
|
154 |
|
155 |
|
156 | Node.prototype.traverse = function (callback) {
|
157 | // execute callback for itself
|
158 | callback(this, null, null); // eslint-disable-line standard/no-callback-literal
|
159 | // recursively traverse over all childs of a node
|
160 |
|
161 | function _traverse(node, callback) {
|
162 | node.forEach(function (child, path, parent) {
|
163 | callback(child, path, parent);
|
164 |
|
165 | _traverse(child, callback);
|
166 | });
|
167 | }
|
168 |
|
169 | _traverse(this, callback);
|
170 | };
|
171 | /**
|
172 | * Recursively transform a node tree via a transform function.
|
173 | *
|
174 | * For example, to replace all nodes of type SymbolNode having name 'x' with a
|
175 | * ConstantNode with value 2:
|
176 | *
|
177 | * const res = Node.transform(function (node, path, parent) {
|
178 | * if (node && node.isSymbolNode) && (node.name === 'x')) {
|
179 | * return new ConstantNode(2)
|
180 | * }
|
181 | * else {
|
182 | * return node
|
183 | * }
|
184 | * })
|
185 | *
|
186 | * @param {function(node: Node, path: string, parent: Node) : Node} callback
|
187 | * A mapping function accepting a node, and returning
|
188 | * a replacement for the node or the original node.
|
189 | * Signature: callback(node: Node, index: string, parent: Node) : Node
|
190 | * @return {Node} Returns the original node or its replacement
|
191 | */
|
192 |
|
193 |
|
194 | Node.prototype.transform = function (callback) {
|
195 | function _transform(child, path, parent) {
|
196 | var replacement = callback(child, path, parent);
|
197 |
|
198 | if (replacement !== child) {
|
199 | // stop iterating when the node is replaced
|
200 | return replacement;
|
201 | }
|
202 |
|
203 | return child.map(_transform);
|
204 | }
|
205 |
|
206 | return _transform(this, null, null);
|
207 | };
|
208 | /**
|
209 | * Find any node in the node tree matching given filter function. For example, to
|
210 | * find all nodes of type SymbolNode having name 'x':
|
211 | *
|
212 | * const results = Node.filter(function (node) {
|
213 | * return (node && node.isSymbolNode) && (node.name === 'x')
|
214 | * })
|
215 | *
|
216 | * @param {function(node: Node, path: string, parent: Node) : Node} callback
|
217 | * A test function returning true when a node matches, and false
|
218 | * otherwise. Function signature:
|
219 | * callback(node: Node, index: string, parent: Node) : boolean
|
220 | * @return {Node[]} nodes An array with nodes matching given filter criteria
|
221 | */
|
222 |
|
223 |
|
224 | Node.prototype.filter = function (callback) {
|
225 | var nodes = [];
|
226 | this.traverse(function (node, path, parent) {
|
227 | if (callback(node, path, parent)) {
|
228 | nodes.push(node);
|
229 | }
|
230 | });
|
231 | return nodes;
|
232 | }; // TODO: deprecated since version 1.1.0, remove this some day
|
233 |
|
234 |
|
235 | Node.prototype.find = function () {
|
236 | throw new Error('Function Node.find is deprecated. Use Node.filter instead.');
|
237 | }; // TODO: deprecated since version 1.1.0, remove this some day
|
238 |
|
239 |
|
240 | Node.prototype.match = function () {
|
241 | throw new Error('Function Node.match is deprecated. See functions Node.filter, Node.transform, Node.traverse.');
|
242 | };
|
243 | /**
|
244 | * Create a shallow clone of this node
|
245 | * @return {Node}
|
246 | */
|
247 |
|
248 |
|
249 | Node.prototype.clone = function () {
|
250 | // must be implemented by each of the Node implementations
|
251 | throw new Error('Cannot clone a Node interface');
|
252 | };
|
253 | /**
|
254 | * Create a deep clone of this node
|
255 | * @return {Node}
|
256 | */
|
257 |
|
258 |
|
259 | Node.prototype.cloneDeep = function () {
|
260 | return this.map(function (node) {
|
261 | return node.cloneDeep();
|
262 | });
|
263 | };
|
264 | /**
|
265 | * Deep compare this node with another node.
|
266 | * @param {Node} other
|
267 | * @return {boolean} Returns true when both nodes are of the same type and
|
268 | * contain the same values (as do their childs)
|
269 | */
|
270 |
|
271 |
|
272 | Node.prototype.equals = function (other) {
|
273 | return other ? (0, _object.deepStrictEqual)(this, other) : false;
|
274 | };
|
275 | /**
|
276 | * Get string representation. (wrapper function)
|
277 | *
|
278 | * This function can get an object of the following form:
|
279 | * {
|
280 | * handler: //This can be a callback function of the form
|
281 | * // "function callback(node, options)"or
|
282 | * // a map that maps function names (used in FunctionNodes)
|
283 | * // to callbacks
|
284 | * parenthesis: "keep" //the parenthesis option (This is optional)
|
285 | * }
|
286 | *
|
287 | * @param {Object} [options]
|
288 | * @return {string}
|
289 | */
|
290 |
|
291 |
|
292 | Node.prototype.toString = function (options) {
|
293 | var customString;
|
294 |
|
295 | if (options && _typeof(options) === 'object') {
|
296 | switch (_typeof(options.handler)) {
|
297 | case 'object':
|
298 | case 'undefined':
|
299 | break;
|
300 |
|
301 | case 'function':
|
302 | customString = options.handler(this, options);
|
303 | break;
|
304 |
|
305 | default:
|
306 | throw new TypeError('Object or function expected as callback');
|
307 | }
|
308 | }
|
309 |
|
310 | if (typeof customString !== 'undefined') {
|
311 | return customString;
|
312 | }
|
313 |
|
314 | return this._toString(options);
|
315 | };
|
316 | /**
|
317 | * Get a JSON representation of the node
|
318 | * Both .toJSON() and the static .fromJSON(json) should be implemented by all
|
319 | * implementations of Node
|
320 | * @returns {Object}
|
321 | */
|
322 |
|
323 |
|
324 | Node.prototype.toJSON = function () {
|
325 | throw new Error('Cannot serialize object: toJSON not implemented by ' + this.type);
|
326 | };
|
327 | /**
|
328 | * Get HTML representation. (wrapper function)
|
329 | *
|
330 | * This function can get an object of the following form:
|
331 | * {
|
332 | * handler: //This can be a callback function of the form
|
333 | * // "function callback(node, options)" or
|
334 | * // a map that maps function names (used in FunctionNodes)
|
335 | * // to callbacks
|
336 | * parenthesis: "keep" //the parenthesis option (This is optional)
|
337 | * }
|
338 | *
|
339 | * @param {Object} [options]
|
340 | * @return {string}
|
341 | */
|
342 |
|
343 |
|
344 | Node.prototype.toHTML = function (options) {
|
345 | var customString;
|
346 |
|
347 | if (options && _typeof(options) === 'object') {
|
348 | switch (_typeof(options.handler)) {
|
349 | case 'object':
|
350 | case 'undefined':
|
351 | break;
|
352 |
|
353 | case 'function':
|
354 | customString = options.handler(this, options);
|
355 | break;
|
356 |
|
357 | default:
|
358 | throw new TypeError('Object or function expected as callback');
|
359 | }
|
360 | }
|
361 |
|
362 | if (typeof customString !== 'undefined') {
|
363 | return customString;
|
364 | }
|
365 |
|
366 | return this.toHTML(options);
|
367 | };
|
368 | /**
|
369 | * Internal function to generate the string output.
|
370 | * This has to be implemented by every Node
|
371 | *
|
372 | * @throws {Error}
|
373 | */
|
374 |
|
375 |
|
376 | Node.prototype._toString = function () {
|
377 | // must be implemented by each of the Node implementations
|
378 | throw new Error('_toString not implemented for ' + this.type);
|
379 | };
|
380 | /**
|
381 | * Get LaTeX representation. (wrapper function)
|
382 | *
|
383 | * This function can get an object of the following form:
|
384 | * {
|
385 | * handler: //This can be a callback function of the form
|
386 | * // "function callback(node, options)"or
|
387 | * // a map that maps function names (used in FunctionNodes)
|
388 | * // to callbacks
|
389 | * parenthesis: "keep" //the parenthesis option (This is optional)
|
390 | * }
|
391 | *
|
392 | * @param {Object} [options]
|
393 | * @return {string}
|
394 | */
|
395 |
|
396 |
|
397 | Node.prototype.toTex = function (options) {
|
398 | var customTex;
|
399 |
|
400 | if (options && _typeof(options) === 'object') {
|
401 | switch (_typeof(options.handler)) {
|
402 | case 'object':
|
403 | case 'undefined':
|
404 | break;
|
405 |
|
406 | case 'function':
|
407 | customTex = options.handler(this, options);
|
408 | break;
|
409 |
|
410 | default:
|
411 | throw new TypeError('Object or function expected as callback');
|
412 | }
|
413 | }
|
414 |
|
415 | if (typeof customTex !== 'undefined') {
|
416 | return customTex;
|
417 | }
|
418 |
|
419 | return this._toTex(options);
|
420 | };
|
421 | /**
|
422 | * Internal function to generate the LaTeX output.
|
423 | * This has to be implemented by every Node
|
424 | *
|
425 | * @param {Object} [options]
|
426 | * @throws {Error}
|
427 | */
|
428 |
|
429 |
|
430 | Node.prototype._toTex = function (options) {
|
431 | // must be implemented by each of the Node implementations
|
432 | throw new Error('_toTex not implemented for ' + this.type);
|
433 | };
|
434 | /**
|
435 | * Get identifier.
|
436 | * @return {string}
|
437 | */
|
438 |
|
439 |
|
440 | Node.prototype.getIdentifier = function () {
|
441 | return this.type;
|
442 | };
|
443 | /**
|
444 | * Get the content of the current Node.
|
445 | * @return {Node} node
|
446 | **/
|
447 |
|
448 |
|
449 | Node.prototype.getContent = function () {
|
450 | return this;
|
451 | };
|
452 | /**
|
453 | * Validate the symbol names of a scope.
|
454 | * Throws an error when the scope contains an illegal symbol.
|
455 | * @param {Object} scope
|
456 | */
|
457 |
|
458 |
|
459 | function _validateScope(scope) {
|
460 | for (var symbol in scope) {
|
461 | if ((0, _object.hasOwnProperty)(scope, symbol)) {
|
462 | if (symbol in _keywords.keywords) {
|
463 | throw new Error('Scope contains an illegal symbol, "' + symbol + '" is a reserved keyword');
|
464 | }
|
465 | }
|
466 | }
|
467 | }
|
468 |
|
469 | return Node;
|
470 | }, {
|
471 | isClass: true,
|
472 | isNode: true
|
473 | });
|
474 | exports.createNode = createNode; |
\ | No newline at end of file |