simple-squiggle

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

AccessorNode.js (6642B)


      1 import { isAccessorNode, isArrayNode, isConstantNode, isFunctionNode, isIndexNode, isNode, isObjectNode, isParenthesisNode, isSymbolNode } from '../../utils/is.js';
      2 import { getSafeProperty } from '../../utils/customs.js';
      3 import { factory } from '../../utils/factory.js';
      4 import { accessFactory } from './utils/access.js';
      5 var name = 'AccessorNode';
      6 var dependencies = ['subset', 'Node'];
      7 export var createAccessorNode = /* #__PURE__ */factory(name, dependencies, _ref => {
      8   var {
      9     subset,
     10     Node
     11   } = _ref;
     12   var access = accessFactory({
     13     subset
     14   });
     15   /**
     16    * @constructor AccessorNode
     17    * @extends {Node}
     18    * Access an object property or get a matrix subset
     19    *
     20    * @param {Node} object                 The object from which to retrieve
     21    *                                      a property or subset.
     22    * @param {IndexNode} index             IndexNode containing ranges
     23    */
     24 
     25   function AccessorNode(object, index) {
     26     if (!(this instanceof AccessorNode)) {
     27       throw new SyntaxError('Constructor must be called with the new operator');
     28     }
     29 
     30     if (!isNode(object)) {
     31       throw new TypeError('Node expected for parameter "object"');
     32     }
     33 
     34     if (!isIndexNode(index)) {
     35       throw new TypeError('IndexNode expected for parameter "index"');
     36     }
     37 
     38     this.object = object || null;
     39     this.index = index; // readonly property name
     40 
     41     Object.defineProperty(this, 'name', {
     42       get: function () {
     43         if (this.index) {
     44           return this.index.isObjectProperty() ? this.index.getObjectProperty() : '';
     45         } else {
     46           return this.object.name || '';
     47         }
     48       }.bind(this),
     49       set: function set() {
     50         throw new Error('Cannot assign a new name, name is read-only');
     51       }
     52     });
     53   }
     54 
     55   AccessorNode.prototype = new Node();
     56   AccessorNode.prototype.type = 'AccessorNode';
     57   AccessorNode.prototype.isAccessorNode = true;
     58   /**
     59    * Compile a node into a JavaScript function.
     60    * This basically pre-calculates as much as possible and only leaves open
     61    * calculations which depend on a dynamic scope with variables.
     62    * @param {Object} math     Math.js namespace with functions and constants.
     63    * @param {Object} argNames An object with argument names as key and `true`
     64    *                          as value. Used in the SymbolNode to optimize
     65    *                          for arguments from user assigned functions
     66    *                          (see FunctionAssignmentNode) or special symbols
     67    *                          like `end` (see IndexNode).
     68    * @return {function} Returns a function which can be called like:
     69    *                        evalNode(scope: Object, args: Object, context: *)
     70    */
     71 
     72   AccessorNode.prototype._compile = function (math, argNames) {
     73     var evalObject = this.object._compile(math, argNames);
     74 
     75     var evalIndex = this.index._compile(math, argNames);
     76 
     77     if (this.index.isObjectProperty()) {
     78       var prop = this.index.getObjectProperty();
     79       return function evalAccessorNode(scope, args, context) {
     80         // get a property from an object evaluated using the scope.
     81         return getSafeProperty(evalObject(scope, args, context), prop);
     82       };
     83     } else {
     84       return function evalAccessorNode(scope, args, context) {
     85         var object = evalObject(scope, args, context);
     86         var index = evalIndex(scope, args, object); // we pass object here instead of context
     87 
     88         return access(object, index);
     89       };
     90     }
     91   };
     92   /**
     93    * Execute a callback for each of the child nodes of this node
     94    * @param {function(child: Node, path: string, parent: Node)} callback
     95    */
     96 
     97 
     98   AccessorNode.prototype.forEach = function (callback) {
     99     callback(this.object, 'object', this);
    100     callback(this.index, 'index', this);
    101   };
    102   /**
    103    * Create a new AccessorNode having it's childs be the results of calling
    104    * the provided callback function for each of the childs of the original node.
    105    * @param {function(child: Node, path: string, parent: Node): Node} callback
    106    * @returns {AccessorNode} Returns a transformed copy of the node
    107    */
    108 
    109 
    110   AccessorNode.prototype.map = function (callback) {
    111     return new AccessorNode(this._ifNode(callback(this.object, 'object', this)), this._ifNode(callback(this.index, 'index', this)));
    112   };
    113   /**
    114    * Create a clone of this node, a shallow copy
    115    * @return {AccessorNode}
    116    */
    117 
    118 
    119   AccessorNode.prototype.clone = function () {
    120     return new AccessorNode(this.object, this.index);
    121   };
    122   /**
    123    * Get string representation
    124    * @param {Object} options
    125    * @return {string}
    126    */
    127 
    128 
    129   AccessorNode.prototype._toString = function (options) {
    130     var object = this.object.toString(options);
    131 
    132     if (needParenthesis(this.object)) {
    133       object = '(' + object + ')';
    134     }
    135 
    136     return object + this.index.toString(options);
    137   };
    138   /**
    139    * Get HTML representation
    140    * @param {Object} options
    141    * @return {string}
    142    */
    143 
    144 
    145   AccessorNode.prototype.toHTML = function (options) {
    146     var object = this.object.toHTML(options);
    147 
    148     if (needParenthesis(this.object)) {
    149       object = '<span class="math-parenthesis math-round-parenthesis">(</span>' + object + '<span class="math-parenthesis math-round-parenthesis">)</span>';
    150     }
    151 
    152     return object + this.index.toHTML(options);
    153   };
    154   /**
    155    * Get LaTeX representation
    156    * @param {Object} options
    157    * @return {string}
    158    */
    159 
    160 
    161   AccessorNode.prototype._toTex = function (options) {
    162     var object = this.object.toTex(options);
    163 
    164     if (needParenthesis(this.object)) {
    165       object = '\\left(\' + object + \'\\right)';
    166     }
    167 
    168     return object + this.index.toTex(options);
    169   };
    170   /**
    171    * Get a JSON representation of the node
    172    * @returns {Object}
    173    */
    174 
    175 
    176   AccessorNode.prototype.toJSON = function () {
    177     return {
    178       mathjs: 'AccessorNode',
    179       object: this.object,
    180       index: this.index
    181     };
    182   };
    183   /**
    184    * Instantiate an AccessorNode from its JSON representation
    185    * @param {Object} json  An object structured like
    186    *                       `{"mathjs": "AccessorNode", object: ..., index: ...}`,
    187    *                       where mathjs is optional
    188    * @returns {AccessorNode}
    189    */
    190 
    191 
    192   AccessorNode.fromJSON = function (json) {
    193     return new AccessorNode(json.object, json.index);
    194   };
    195   /**
    196    * Are parenthesis needed?
    197    * @private
    198    */
    199 
    200 
    201   function needParenthesis(node) {
    202     // TODO: maybe make a method on the nodes which tells whether they need parenthesis?
    203     return !(isAccessorNode(node) || isArrayNode(node) || isConstantNode(node) || isFunctionNode(node) || isObjectNode(node) || isParenthesisNode(node) || isSymbolNode(node));
    204   }
    205 
    206   return AccessorNode;
    207 }, {
    208   isClass: true,
    209   isNode: true
    210 });