simple-squiggle

A restricted subset of Squiggle
Log | Files | Refs | README

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;