IndexNode.js (10056B)
1 "use strict"; 2 3 var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 5 Object.defineProperty(exports, "__esModule", { 6 value: true 7 }); 8 exports.createIndexNode = void 0; 9 10 var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray")); 11 12 var _is = require("../../utils/is.js"); 13 14 var _array = require("../../utils/array.js"); 15 16 var _string = require("../../utils/string.js"); 17 18 var _factory = require("../../utils/factory.js"); 19 20 var _customs = require("../../utils/customs.js"); 21 22 var name = 'IndexNode'; 23 var dependencies = ['Range', 'Node', 'size']; 24 var createIndexNode = /* #__PURE__ */(0, _factory.factory)(name, dependencies, function (_ref) { 25 var Range = _ref.Range, 26 Node = _ref.Node, 27 size = _ref.size; 28 29 /** 30 * @constructor IndexNode 31 * @extends Node 32 * 33 * Describes a subset of a matrix or an object property. 34 * Cannot be used on its own, needs to be used within an AccessorNode or 35 * AssignmentNode. 36 * 37 * @param {Node[]} dimensions 38 * @param {boolean} [dotNotation=false] Optional property describing whether 39 * this index was written using dot 40 * notation like `a.b`, or using bracket 41 * notation like `a["b"]` (default). 42 * Used to stringify an IndexNode. 43 */ 44 function IndexNode(dimensions, dotNotation) { 45 if (!(this instanceof IndexNode)) { 46 throw new SyntaxError('Constructor must be called with the new operator'); 47 } 48 49 this.dimensions = dimensions; 50 this.dotNotation = dotNotation || false; // validate input 51 52 if (!Array.isArray(dimensions) || !dimensions.every(_is.isNode)) { 53 throw new TypeError('Array containing Nodes expected for parameter "dimensions"'); 54 } 55 56 if (this.dotNotation && !this.isObjectProperty()) { 57 throw new Error('dotNotation only applicable for object properties'); 58 } 59 } 60 61 IndexNode.prototype = new Node(); 62 IndexNode.prototype.type = 'IndexNode'; 63 IndexNode.prototype.isIndexNode = true; 64 /** 65 * Compile a node into a JavaScript function. 66 * This basically pre-calculates as much as possible and only leaves open 67 * calculations which depend on a dynamic scope with variables. 68 * @param {Object} math Math.js namespace with functions and constants. 69 * @param {Object} argNames An object with argument names as key and `true` 70 * as value. Used in the SymbolNode to optimize 71 * for arguments from user assigned functions 72 * (see FunctionAssignmentNode) or special symbols 73 * like `end` (see IndexNode). 74 * @return {function} Returns a function which can be called like: 75 * evalNode(scope: Object, args: Object, context: *) 76 */ 77 78 IndexNode.prototype._compile = function (math, argNames) { 79 // TODO: implement support for bignumber (currently bignumbers are silently 80 // reduced to numbers when changing the value to zero-based) 81 // TODO: Optimization: when the range values are ConstantNodes, 82 // we can beforehand resolve the zero-based value 83 // optimization for a simple object property 84 var evalDimensions = (0, _array.map)(this.dimensions, function (range, i) { 85 if ((0, _is.isRangeNode)(range)) { 86 if (range.needsEnd()) { 87 // create a range containing end (like '4:end') 88 var childArgNames = Object.create(argNames); 89 childArgNames.end = true; 90 91 var evalStart = range.start._compile(math, childArgNames); 92 93 var evalEnd = range.end._compile(math, childArgNames); 94 95 var evalStep = range.step ? range.step._compile(math, childArgNames) : function () { 96 return 1; 97 }; 98 return function evalDimension(scope, args, context) { 99 var s = size(context).valueOf(); 100 var childArgs = Object.create(args); 101 childArgs.end = s[i]; 102 return createRange(evalStart(scope, childArgs, context), evalEnd(scope, childArgs, context), evalStep(scope, childArgs, context)); 103 }; 104 } else { 105 // create range 106 var _evalStart = range.start._compile(math, argNames); 107 108 var _evalEnd = range.end._compile(math, argNames); 109 110 var _evalStep = range.step ? range.step._compile(math, argNames) : function () { 111 return 1; 112 }; 113 114 return function evalDimension(scope, args, context) { 115 return createRange(_evalStart(scope, args, context), _evalEnd(scope, args, context), _evalStep(scope, args, context)); 116 }; 117 } 118 } else if ((0, _is.isSymbolNode)(range) && range.name === 'end') { 119 // SymbolNode 'end' 120 var _childArgNames = Object.create(argNames); 121 122 _childArgNames.end = true; 123 124 var evalRange = range._compile(math, _childArgNames); 125 126 return function evalDimension(scope, args, context) { 127 var s = size(context).valueOf(); 128 var childArgs = Object.create(args); 129 childArgs.end = s[i]; 130 return evalRange(scope, childArgs, context); 131 }; 132 } else { 133 // ConstantNode 134 var _evalRange = range._compile(math, argNames); 135 136 return function evalDimension(scope, args, context) { 137 return _evalRange(scope, args, context); 138 }; 139 } 140 }); 141 var index = (0, _customs.getSafeProperty)(math, 'index'); 142 return function evalIndexNode(scope, args, context) { 143 var dimensions = (0, _array.map)(evalDimensions, function (evalDimension) { 144 return evalDimension(scope, args, context); 145 }); 146 return index.apply(void 0, (0, _toConsumableArray2.default)(dimensions)); 147 }; 148 }; 149 /** 150 * Execute a callback for each of the child nodes of this node 151 * @param {function(child: Node, path: string, parent: Node)} callback 152 */ 153 154 155 IndexNode.prototype.forEach = function (callback) { 156 for (var i = 0; i < this.dimensions.length; i++) { 157 callback(this.dimensions[i], 'dimensions[' + i + ']', this); 158 } 159 }; 160 /** 161 * Create a new IndexNode having it's childs be the results of calling 162 * the provided callback function for each of the childs of the original node. 163 * @param {function(child: Node, path: string, parent: Node): Node} callback 164 * @returns {IndexNode} Returns a transformed copy of the node 165 */ 166 167 168 IndexNode.prototype.map = function (callback) { 169 var dimensions = []; 170 171 for (var i = 0; i < this.dimensions.length; i++) { 172 dimensions[i] = this._ifNode(callback(this.dimensions[i], 'dimensions[' + i + ']', this)); 173 } 174 175 return new IndexNode(dimensions, this.dotNotation); 176 }; 177 /** 178 * Create a clone of this node, a shallow copy 179 * @return {IndexNode} 180 */ 181 182 183 IndexNode.prototype.clone = function () { 184 return new IndexNode(this.dimensions.slice(0), this.dotNotation); 185 }; 186 /** 187 * Test whether this IndexNode contains a single property name 188 * @return {boolean} 189 */ 190 191 192 IndexNode.prototype.isObjectProperty = function () { 193 return this.dimensions.length === 1 && (0, _is.isConstantNode)(this.dimensions[0]) && typeof this.dimensions[0].value === 'string'; 194 }; 195 /** 196 * Returns the property name if IndexNode contains a property. 197 * If not, returns null. 198 * @return {string | null} 199 */ 200 201 202 IndexNode.prototype.getObjectProperty = function () { 203 return this.isObjectProperty() ? this.dimensions[0].value : null; 204 }; 205 /** 206 * Get string representation 207 * @param {Object} options 208 * @return {string} str 209 */ 210 211 212 IndexNode.prototype._toString = function (options) { 213 // format the parameters like "[1, 0:5]" 214 return this.dotNotation ? '.' + this.getObjectProperty() : '[' + this.dimensions.join(', ') + ']'; 215 }; 216 /** 217 * Get a JSON representation of the node 218 * @returns {Object} 219 */ 220 221 222 IndexNode.prototype.toJSON = function () { 223 return { 224 mathjs: 'IndexNode', 225 dimensions: this.dimensions, 226 dotNotation: this.dotNotation 227 }; 228 }; 229 /** 230 * Instantiate an IndexNode from its JSON representation 231 * @param {Object} json An object structured like 232 * `{"mathjs": "IndexNode", dimensions: [...], dotNotation: false}`, 233 * where mathjs is optional 234 * @returns {IndexNode} 235 */ 236 237 238 IndexNode.fromJSON = function (json) { 239 return new IndexNode(json.dimensions, json.dotNotation); 240 }; 241 /** 242 * Get HTML representation 243 * @param {Object} options 244 * @return {string} str 245 */ 246 247 248 IndexNode.prototype.toHTML = function (options) { 249 // format the parameters like "[1, 0:5]" 250 var dimensions = []; 251 252 for (var i = 0; i < this.dimensions.length; i++) { 253 dimensions[i] = this.dimensions[i].toHTML(); 254 } 255 256 if (this.dotNotation) { 257 return '<span class="math-operator math-accessor-operator">.</span>' + '<span class="math-symbol math-property">' + (0, _string.escape)(this.getObjectProperty()) + '</span>'; 258 } else { 259 return '<span class="math-parenthesis math-square-parenthesis">[</span>' + dimensions.join('<span class="math-separator">,</span>') + '<span class="math-parenthesis math-square-parenthesis">]</span>'; 260 } 261 }; 262 /** 263 * Get LaTeX representation 264 * @param {Object} options 265 * @return {string} str 266 */ 267 268 269 IndexNode.prototype._toTex = function (options) { 270 var dimensions = this.dimensions.map(function (range) { 271 return range.toTex(options); 272 }); 273 return this.dotNotation ? '.' + this.getObjectProperty() + '' : '_{' + dimensions.join(',') + '}'; 274 }; // helper function to create a Range from start, step and end 275 276 277 function createRange(start, end, step) { 278 return new Range((0, _is.isBigNumber)(start) ? start.toNumber() : start, (0, _is.isBigNumber)(end) ? end.toNumber() : end, (0, _is.isBigNumber)(step) ? step.toNumber() : step); 279 } 280 281 return IndexNode; 282 }, { 283 isClass: true, 284 isNode: true 285 }); 286 exports.createIndexNode = createIndexNode;