UNPKG

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