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 });