RangeNode.js (8855B)
1 "use strict"; 2 3 Object.defineProperty(exports, "__esModule", { 4 value: true 5 }); 6 exports.createRangeNode = void 0; 7 8 var _is = require("../../utils/is.js"); 9 10 var _factory = require("../../utils/factory.js"); 11 12 var _operators = require("../operators.js"); 13 14 var name = 'RangeNode'; 15 var dependencies = ['Node']; 16 var createRangeNode = /* #__PURE__ */(0, _factory.factory)(name, dependencies, function (_ref) { 17 var Node = _ref.Node; 18 19 /** 20 * @constructor RangeNode 21 * @extends {Node} 22 * create a range 23 * @param {Node} start included lower-bound 24 * @param {Node} end included upper-bound 25 * @param {Node} [step] optional step 26 */ 27 function RangeNode(start, end, step) { 28 if (!(this instanceof RangeNode)) { 29 throw new SyntaxError('Constructor must be called with the new operator'); 30 } // validate inputs 31 32 33 if (!(0, _is.isNode)(start)) throw new TypeError('Node expected'); 34 if (!(0, _is.isNode)(end)) throw new TypeError('Node expected'); 35 if (step && !(0, _is.isNode)(step)) throw new TypeError('Node expected'); 36 if (arguments.length > 3) throw new Error('Too many arguments'); 37 this.start = start; // included lower-bound 38 39 this.end = end; // included upper-bound 40 41 this.step = step || null; // optional step 42 } 43 44 RangeNode.prototype = new Node(); 45 RangeNode.prototype.type = 'RangeNode'; 46 RangeNode.prototype.isRangeNode = true; 47 /** 48 * Check whether the RangeNode needs the `end` symbol to be defined. 49 * This end is the size of the Matrix in current dimension. 50 * @return {boolean} 51 */ 52 53 RangeNode.prototype.needsEnd = function () { 54 // find all `end` symbols in this RangeNode 55 var endSymbols = this.filter(function (node) { 56 return (0, _is.isSymbolNode)(node) && node.name === 'end'; 57 }); 58 return endSymbols.length > 0; 59 }; 60 /** 61 * Compile a node into a JavaScript function. 62 * This basically pre-calculates as much as possible and only leaves open 63 * calculations which depend on a dynamic scope with variables. 64 * @param {Object} math Math.js namespace with functions and constants. 65 * @param {Object} argNames An object with argument names as key and `true` 66 * as value. Used in the SymbolNode to optimize 67 * for arguments from user assigned functions 68 * (see FunctionAssignmentNode) or special symbols 69 * like `end` (see IndexNode). 70 * @return {function} Returns a function which can be called like: 71 * evalNode(scope: Object, args: Object, context: *) 72 */ 73 74 75 RangeNode.prototype._compile = function (math, argNames) { 76 var range = math.range; 77 78 var evalStart = this.start._compile(math, argNames); 79 80 var evalEnd = this.end._compile(math, argNames); 81 82 if (this.step) { 83 var evalStep = this.step._compile(math, argNames); 84 85 return function evalRangeNode(scope, args, context) { 86 return range(evalStart(scope, args, context), evalEnd(scope, args, context), evalStep(scope, args, context)); 87 }; 88 } else { 89 return function evalRangeNode(scope, args, context) { 90 return range(evalStart(scope, args, context), evalEnd(scope, args, context)); 91 }; 92 } 93 }; 94 /** 95 * Execute a callback for each of the child nodes of this node 96 * @param {function(child: Node, path: string, parent: Node)} callback 97 */ 98 99 100 RangeNode.prototype.forEach = function (callback) { 101 callback(this.start, 'start', this); 102 callback(this.end, 'end', this); 103 104 if (this.step) { 105 callback(this.step, 'step', this); 106 } 107 }; 108 /** 109 * Create a new RangeNode having it's childs be the results of calling 110 * the provided callback function for each of the childs of the original node. 111 * @param {function(child: Node, path: string, parent: Node): Node} callback 112 * @returns {RangeNode} Returns a transformed copy of the node 113 */ 114 115 116 RangeNode.prototype.map = function (callback) { 117 return new RangeNode(this._ifNode(callback(this.start, 'start', this)), this._ifNode(callback(this.end, 'end', this)), this.step && this._ifNode(callback(this.step, 'step', this))); 118 }; 119 /** 120 * Create a clone of this node, a shallow copy 121 * @return {RangeNode} 122 */ 123 124 125 RangeNode.prototype.clone = function () { 126 return new RangeNode(this.start, this.end, this.step && this.step); 127 }; 128 /** 129 * Calculate the necessary parentheses 130 * @param {Node} node 131 * @param {string} parenthesis 132 * @return {Object} parentheses 133 * @private 134 */ 135 136 137 function calculateNecessaryParentheses(node, parenthesis) { 138 var precedence = (0, _operators.getPrecedence)(node, parenthesis); 139 var parens = {}; 140 var startPrecedence = (0, _operators.getPrecedence)(node.start, parenthesis); 141 parens.start = startPrecedence !== null && startPrecedence <= precedence || parenthesis === 'all'; 142 143 if (node.step) { 144 var stepPrecedence = (0, _operators.getPrecedence)(node.step, parenthesis); 145 parens.step = stepPrecedence !== null && stepPrecedence <= precedence || parenthesis === 'all'; 146 } 147 148 var endPrecedence = (0, _operators.getPrecedence)(node.end, parenthesis); 149 parens.end = endPrecedence !== null && endPrecedence <= precedence || parenthesis === 'all'; 150 return parens; 151 } 152 /** 153 * Get string representation 154 * @param {Object} options 155 * @return {string} str 156 */ 157 158 159 RangeNode.prototype._toString = function (options) { 160 var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep'; 161 var parens = calculateNecessaryParentheses(this, parenthesis); // format string as start:step:stop 162 163 var str; 164 var start = this.start.toString(options); 165 166 if (parens.start) { 167 start = '(' + start + ')'; 168 } 169 170 str = start; 171 172 if (this.step) { 173 var step = this.step.toString(options); 174 175 if (parens.step) { 176 step = '(' + step + ')'; 177 } 178 179 str += ':' + step; 180 } 181 182 var end = this.end.toString(options); 183 184 if (parens.end) { 185 end = '(' + end + ')'; 186 } 187 188 str += ':' + end; 189 return str; 190 }; 191 /** 192 * Get a JSON representation of the node 193 * @returns {Object} 194 */ 195 196 197 RangeNode.prototype.toJSON = function () { 198 return { 199 mathjs: 'RangeNode', 200 start: this.start, 201 end: this.end, 202 step: this.step 203 }; 204 }; 205 /** 206 * Instantiate an RangeNode from its JSON representation 207 * @param {Object} json An object structured like 208 * `{"mathjs": "RangeNode", "start": ..., "end": ..., "step": ...}`, 209 * where mathjs is optional 210 * @returns {RangeNode} 211 */ 212 213 214 RangeNode.fromJSON = function (json) { 215 return new RangeNode(json.start, json.end, json.step); 216 }; 217 /** 218 * Get HTML representation 219 * @param {Object} options 220 * @return {string} str 221 */ 222 223 224 RangeNode.prototype.toHTML = function (options) { 225 var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep'; 226 var parens = calculateNecessaryParentheses(this, parenthesis); // format string as start:step:stop 227 228 var str; 229 var start = this.start.toHTML(options); 230 231 if (parens.start) { 232 start = '<span class="math-parenthesis math-round-parenthesis">(</span>' + start + '<span class="math-parenthesis math-round-parenthesis">)</span>'; 233 } 234 235 str = start; 236 237 if (this.step) { 238 var step = this.step.toHTML(options); 239 240 if (parens.step) { 241 step = '<span class="math-parenthesis math-round-parenthesis">(</span>' + step + '<span class="math-parenthesis math-round-parenthesis">)</span>'; 242 } 243 244 str += '<span class="math-operator math-range-operator">:</span>' + step; 245 } 246 247 var end = this.end.toHTML(options); 248 249 if (parens.end) { 250 end = '<span class="math-parenthesis math-round-parenthesis">(</span>' + end + '<span class="math-parenthesis math-round-parenthesis">)</span>'; 251 } 252 253 str += '<span class="math-operator math-range-operator">:</span>' + end; 254 return str; 255 }; 256 /** 257 * Get LaTeX representation 258 * @params {Object} options 259 * @return {string} str 260 */ 261 262 263 RangeNode.prototype._toTex = function (options) { 264 var parenthesis = options && options.parenthesis ? options.parenthesis : 'keep'; 265 var parens = calculateNecessaryParentheses(this, parenthesis); 266 var str = this.start.toTex(options); 267 268 if (parens.start) { 269 str = "\\left(".concat(str, "\\right)"); 270 } 271 272 if (this.step) { 273 var step = this.step.toTex(options); 274 275 if (parens.step) { 276 step = "\\left(".concat(step, "\\right)"); 277 } 278 279 str += ':' + step; 280 } 281 282 var end = this.end.toTex(options); 283 284 if (parens.end) { 285 end = "\\left(".concat(end, "\\right)"); 286 } 287 288 str += ':' + end; 289 return str; 290 }; 291 292 return RangeNode; 293 }, { 294 isClass: true, 295 isNode: true 296 }); 297 exports.createRangeNode = createRangeNode;