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