1 | 'use strict'
|
2 |
|
3 | const map = require('../../utils/array').map
|
4 | const escape = require('../../utils/string').escape
|
5 |
|
6 | function factory (type, config, load, typed) {
|
7 | const Node = load(require('./Node'))
|
8 | const Range = load(require('../../type/matrix/Range'))
|
9 |
|
10 | const isArray = Array.isArray
|
11 |
|
12 | |
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 | function IndexNode (dimensions, dotNotation) {
|
28 | if (!(this instanceof IndexNode)) {
|
29 | throw new SyntaxError('Constructor must be called with the new operator')
|
30 | }
|
31 |
|
32 | this.dimensions = dimensions
|
33 | this.dotNotation = dotNotation || false
|
34 |
|
35 |
|
36 | if (!isArray(dimensions) || !dimensions.every(type.isNode)) {
|
37 | throw new TypeError('Array containing Nodes expected for parameter "dimensions"')
|
38 | }
|
39 | if (this.dotNotation && !this.isObjectProperty()) {
|
40 | throw new Error('dotNotation only applicable for object properties')
|
41 | }
|
42 |
|
43 |
|
44 | const deprecated = function () {
|
45 | throw new Error('Property `IndexNode.object` is deprecated, use `IndexNode.fn` instead')
|
46 | }
|
47 | Object.defineProperty(this, 'object', { get: deprecated, set: deprecated })
|
48 | }
|
49 |
|
50 | IndexNode.prototype = new Node()
|
51 |
|
52 | IndexNode.prototype.type = 'IndexNode'
|
53 |
|
54 | IndexNode.prototype.isIndexNode = true
|
55 |
|
56 | |
57 |
|
58 |
|
59 |
|
60 |
|
61 |
|
62 |
|
63 |
|
64 |
|
65 |
|
66 |
|
67 |
|
68 |
|
69 | IndexNode.prototype._compile = function (math, argNames) {
|
70 |
|
71 |
|
72 |
|
73 |
|
74 |
|
75 |
|
76 |
|
77 | const evalDimensions = map(this.dimensions, function (range, i) {
|
78 | if (type.isRangeNode(range)) {
|
79 | if (range.needsEnd()) {
|
80 |
|
81 | const childArgNames = Object.create(argNames)
|
82 | childArgNames['end'] = true
|
83 |
|
84 | const evalStart = range.start._compile(math, childArgNames)
|
85 | const evalEnd = range.end._compile(math, childArgNames)
|
86 | const evalStep = range.step
|
87 | ? range.step._compile(math, childArgNames)
|
88 | : function () { return 1 }
|
89 |
|
90 | return function evalDimension (scope, args, context) {
|
91 | const size = math.size(context).valueOf()
|
92 | const childArgs = Object.create(args)
|
93 | childArgs['end'] = size[i]
|
94 |
|
95 | return createRange(
|
96 | evalStart(scope, childArgs, context),
|
97 | evalEnd(scope, childArgs, context),
|
98 | evalStep(scope, childArgs, context)
|
99 | )
|
100 | }
|
101 | } else {
|
102 |
|
103 | const evalStart = range.start._compile(math, argNames)
|
104 | const evalEnd = range.end._compile(math, argNames)
|
105 | const evalStep = range.step
|
106 | ? range.step._compile(math, argNames)
|
107 | : function () { return 1 }
|
108 |
|
109 | return function evalDimension (scope, args, context) {
|
110 | return createRange(
|
111 | evalStart(scope, args, context),
|
112 | evalEnd(scope, args, context),
|
113 | evalStep(scope, args, context)
|
114 | )
|
115 | }
|
116 | }
|
117 | } else if (type.isSymbolNode(range) && range.name === 'end') {
|
118 |
|
119 | const childArgNames = Object.create(argNames)
|
120 | childArgNames['end'] = true
|
121 |
|
122 | const evalRange = range._compile(math, childArgNames)
|
123 |
|
124 | return function evalDimension (scope, args, context) {
|
125 | const size = math.size(context).valueOf()
|
126 | const childArgs = Object.create(args)
|
127 | childArgs['end'] = size[i]
|
128 |
|
129 | return evalRange(scope, childArgs, context)
|
130 | }
|
131 | } else {
|
132 |
|
133 | const evalRange = range._compile(math, argNames)
|
134 | return function evalDimension (scope, args, context) {
|
135 | return evalRange(scope, args, context)
|
136 | }
|
137 | }
|
138 | })
|
139 |
|
140 | return function evalIndexNode (scope, args, context) {
|
141 | const dimensions = map(evalDimensions, function (evalDimension) {
|
142 | return evalDimension(scope, args, context)
|
143 | })
|
144 | return math.index.apply(math, dimensions)
|
145 | }
|
146 | }
|
147 |
|
148 | |
149 |
|
150 |
|
151 |
|
152 | IndexNode.prototype.forEach = function (callback) {
|
153 | for (let i = 0; i < this.dimensions.length; i++) {
|
154 | callback(this.dimensions[i], 'dimensions[' + i + ']', this)
|
155 | }
|
156 | }
|
157 |
|
158 | |
159 |
|
160 |
|
161 |
|
162 |
|
163 |
|
164 | IndexNode.prototype.map = function (callback) {
|
165 | const dimensions = []
|
166 | for (let i = 0; i < this.dimensions.length; i++) {
|
167 | dimensions[i] = this._ifNode(callback(this.dimensions[i], 'dimensions[' + i + ']', this))
|
168 | }
|
169 |
|
170 | return new IndexNode(dimensions)
|
171 | }
|
172 |
|
173 | |
174 |
|
175 |
|
176 |
|
177 | IndexNode.prototype.clone = function () {
|
178 | return new IndexNode(this.dimensions.slice(0))
|
179 | }
|
180 |
|
181 | |
182 |
|
183 |
|
184 |
|
185 | IndexNode.prototype.isObjectProperty = function () {
|
186 | return this.dimensions.length === 1 &&
|
187 | type.isConstantNode(this.dimensions[0]) &&
|
188 | typeof this.dimensions[0].value === 'string'
|
189 | }
|
190 |
|
191 | |
192 |
|
193 |
|
194 |
|
195 |
|
196 | IndexNode.prototype.getObjectProperty = function () {
|
197 | return this.isObjectProperty() ? this.dimensions[0].value : null
|
198 | }
|
199 |
|
200 | |
201 |
|
202 |
|
203 |
|
204 |
|
205 | IndexNode.prototype._toString = function (options) {
|
206 |
|
207 | return this.dotNotation
|
208 | ? ('.' + this.getObjectProperty())
|
209 | : ('[' + this.dimensions.join(', ') + ']')
|
210 | }
|
211 |
|
212 | |
213 |
|
214 |
|
215 |
|
216 | IndexNode.prototype.toJSON = function () {
|
217 | return {
|
218 | mathjs: 'IndexNode',
|
219 | dimensions: this.dimensions,
|
220 | dotNotation: this.dotNotation
|
221 | }
|
222 | }
|
223 |
|
224 | |
225 |
|
226 |
|
227 |
|
228 |
|
229 |
|
230 |
|
231 | IndexNode.fromJSON = function (json) {
|
232 | return new IndexNode(json.dimensions, json.dotNotation)
|
233 | }
|
234 |
|
235 | |
236 |
|
237 |
|
238 |
|
239 |
|
240 | IndexNode.prototype.toHTML = function (options) {
|
241 |
|
242 | const dimensions = []
|
243 | for (let i = 0; i < this.dimensions.length; i++) {
|
244 | dimensions[i] = this.dimensions[i].toHTML()
|
245 | }
|
246 | if (this.dotNotation) {
|
247 | return '<span class="math-operator math-accessor-operator">.</span>' + '<span class="math-symbol math-property">' + escape(this.getObjectProperty()) + '</span>'
|
248 | } else {
|
249 | return '<span class="math-parenthesis math-square-parenthesis">[</span>' + dimensions.join('<span class="math-separator">,</span>') + '<span class="math-parenthesis math-square-parenthesis">]</span>'
|
250 | }
|
251 | }
|
252 |
|
253 | |
254 |
|
255 |
|
256 |
|
257 |
|
258 | IndexNode.prototype._toTex = function (options) {
|
259 | const dimensions = this.dimensions.map(function (range) {
|
260 | return range.toTex(options)
|
261 | })
|
262 |
|
263 | return this.dotNotation
|
264 | ? ('.' + this.getObjectProperty() + '')
|
265 | : ('_{' + dimensions.join(',') + '}')
|
266 | }
|
267 |
|
268 |
|
269 | function createRange (start, end, step) {
|
270 | return new Range(
|
271 | type.isBigNumber(start) ? start.toNumber() : start,
|
272 | type.isBigNumber(end) ? end.toNumber() : end,
|
273 | type.isBigNumber(step) ? step.toNumber() : step
|
274 | )
|
275 | }
|
276 |
|
277 | return IndexNode
|
278 | }
|
279 |
|
280 | exports.name = 'IndexNode'
|
281 | exports.path = 'expression.node'
|
282 | exports.factory = factory
|