1 | 'use strict'
|
2 |
|
3 | const getSafeProperty = require('../../utils/customs').getSafeProperty
|
4 | const setSafeProperty = require('../../utils/customs').setSafeProperty
|
5 |
|
6 | function factory (type, config, load, typed) {
|
7 | const Node = load(require('./Node'))
|
8 | const assign = load(require('./utils/assign'))
|
9 | const access = load(require('./utils/access'))
|
10 |
|
11 | const operators = require('../operators')
|
12 |
|
13 | |
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 |
|
38 |
|
39 | function AssignmentNode (object, index, value) {
|
40 | if (!(this instanceof AssignmentNode)) {
|
41 | throw new SyntaxError('Constructor must be called with the new operator')
|
42 | }
|
43 |
|
44 | this.object = object
|
45 | this.index = value ? index : null
|
46 | this.value = value || index
|
47 |
|
48 |
|
49 | if (!type.isSymbolNode(object) && !type.isAccessorNode(object)) {
|
50 | throw new TypeError('SymbolNode or AccessorNode expected as "object"')
|
51 | }
|
52 | if (type.isSymbolNode(object) && object.name === 'end') {
|
53 | throw new Error('Cannot assign to symbol "end"')
|
54 | }
|
55 | if (this.index && !type.isIndexNode(this.index)) {
|
56 | throw new TypeError('IndexNode expected as "index"')
|
57 | }
|
58 | if (!type.isNode(this.value)) {
|
59 | throw new TypeError('Node expected as "value"')
|
60 | }
|
61 |
|
62 |
|
63 | Object.defineProperty(this, 'name', {
|
64 | get: function () {
|
65 | if (this.index) {
|
66 | return (this.index.isObjectProperty())
|
67 | ? this.index.getObjectProperty()
|
68 | : ''
|
69 | } else {
|
70 | return this.object.name || ''
|
71 | }
|
72 | }.bind(this),
|
73 | set: function () {
|
74 | throw new Error('Cannot assign a new name, name is read-only')
|
75 | }
|
76 | })
|
77 | }
|
78 |
|
79 | AssignmentNode.prototype = new Node()
|
80 |
|
81 | AssignmentNode.prototype.type = 'AssignmentNode'
|
82 |
|
83 | AssignmentNode.prototype.isAssignmentNode = true
|
84 |
|
85 | |
86 |
|
87 |
|
88 |
|
89 |
|
90 |
|
91 |
|
92 |
|
93 |
|
94 |
|
95 |
|
96 |
|
97 |
|
98 | AssignmentNode.prototype._compile = function (math, argNames) {
|
99 | const evalObject = this.object._compile(math, argNames)
|
100 | const evalIndex = this.index ? this.index._compile(math, argNames) : null
|
101 | const evalValue = this.value._compile(math, argNames)
|
102 | const name = this.object.name
|
103 |
|
104 | if (!this.index) {
|
105 |
|
106 | if (!type.isSymbolNode(this.object)) {
|
107 | throw new TypeError('SymbolNode expected as object')
|
108 | }
|
109 |
|
110 | return function evalAssignmentNode (scope, args, context) {
|
111 | return setSafeProperty(scope, name, evalValue(scope, args, context))
|
112 | }
|
113 | } else if (this.index.isObjectProperty()) {
|
114 |
|
115 | const prop = this.index.getObjectProperty()
|
116 |
|
117 | return function evalAssignmentNode (scope, args, context) {
|
118 | const object = evalObject(scope, args, context)
|
119 | const value = evalValue(scope, args, context)
|
120 | return setSafeProperty(object, prop, value)
|
121 | }
|
122 | } else if (type.isSymbolNode(this.object)) {
|
123 |
|
124 | return function evalAssignmentNode (scope, args, context) {
|
125 | const childObject = evalObject(scope, args, context)
|
126 | const value = evalValue(scope, args, context)
|
127 | const index = evalIndex(scope, args, childObject)
|
128 | setSafeProperty(scope, name, assign(childObject, index, value))
|
129 | return value
|
130 | }
|
131 | } else {
|
132 |
|
133 |
|
134 |
|
135 |
|
136 |
|
137 | const evalParentObject = this.object.object._compile(math, argNames)
|
138 |
|
139 | if (this.object.index.isObjectProperty()) {
|
140 | const parentProp = this.object.index.getObjectProperty()
|
141 |
|
142 | return function evalAssignmentNode (scope, args, context) {
|
143 | const parent = evalParentObject(scope, args, context)
|
144 | const childObject = getSafeProperty(parent, parentProp)
|
145 | const index = evalIndex(scope, args, childObject)
|
146 | const value = evalValue(scope, args, context)
|
147 | setSafeProperty(parent, parentProp, assign(childObject, index, value))
|
148 | return value
|
149 | }
|
150 | } else {
|
151 |
|
152 | const evalParentIndex = this.object.index._compile(math, argNames)
|
153 |
|
154 | return function evalAssignmentNode (scope, args, context) {
|
155 | const parent = evalParentObject(scope, args, context)
|
156 | const parentIndex = evalParentIndex(scope, args, parent)
|
157 | const childObject = access(parent, parentIndex)
|
158 | const index = evalIndex(scope, args, childObject)
|
159 | const value = evalValue(scope, args, context)
|
160 |
|
161 | assign(parent, parentIndex, assign(childObject, index, value))
|
162 |
|
163 | return value
|
164 | }
|
165 | }
|
166 | }
|
167 | }
|
168 |
|
169 | |
170 |
|
171 |
|
172 |
|
173 | AssignmentNode.prototype.forEach = function (callback) {
|
174 | callback(this.object, 'object', this)
|
175 | if (this.index) {
|
176 | callback(this.index, 'index', this)
|
177 | }
|
178 | callback(this.value, 'value', this)
|
179 | }
|
180 |
|
181 | |
182 |
|
183 |
|
184 |
|
185 |
|
186 |
|
187 | AssignmentNode.prototype.map = function (callback) {
|
188 | const object = this._ifNode(callback(this.object, 'object', this))
|
189 | const index = this.index
|
190 | ? this._ifNode(callback(this.index, 'index', this))
|
191 | : null
|
192 | const value = this._ifNode(callback(this.value, 'value', this))
|
193 |
|
194 | return new AssignmentNode(object, index, value)
|
195 | }
|
196 |
|
197 | |
198 |
|
199 |
|
200 |
|
201 | AssignmentNode.prototype.clone = function () {
|
202 | return new AssignmentNode(this.object, this.index, this.value)
|
203 | }
|
204 |
|
205 | |
206 |
|
207 |
|
208 |
|
209 |
|
210 |
|
211 | function needParenthesis (node, parenthesis) {
|
212 | if (!parenthesis) {
|
213 | parenthesis = 'keep'
|
214 | }
|
215 |
|
216 | const precedence = operators.getPrecedence(node, parenthesis)
|
217 | const exprPrecedence = operators.getPrecedence(node.value, parenthesis)
|
218 | return (parenthesis === 'all') ||
|
219 | ((exprPrecedence !== null) && (exprPrecedence <= precedence))
|
220 | }
|
221 |
|
222 | |
223 |
|
224 |
|
225 |
|
226 |
|
227 | AssignmentNode.prototype._toString = function (options) {
|
228 | const object = this.object.toString(options)
|
229 | const index = this.index ? this.index.toString(options) : ''
|
230 | let value = this.value.toString(options)
|
231 | if (needParenthesis(this, options && options.parenthesis)) {
|
232 | value = '(' + value + ')'
|
233 | }
|
234 |
|
235 | return object + index + ' = ' + value
|
236 | }
|
237 |
|
238 | |
239 |
|
240 |
|
241 |
|
242 | AssignmentNode.prototype.toJSON = function () {
|
243 | return {
|
244 | mathjs: 'AssignmentNode',
|
245 | object: this.object,
|
246 | index: this.index,
|
247 | value: this.value
|
248 | }
|
249 | }
|
250 |
|
251 | |
252 |
|
253 |
|
254 |
|
255 |
|
256 |
|
257 |
|
258 | AssignmentNode.fromJSON = function (json) {
|
259 | return new AssignmentNode(json.object, json.index, json.value)
|
260 | }
|
261 |
|
262 | |
263 |
|
264 |
|
265 |
|
266 |
|
267 | AssignmentNode.prototype.toHTML = function (options) {
|
268 | const object = this.object.toHTML(options)
|
269 | const index = this.index ? this.index.toHTML(options) : ''
|
270 | let value = this.value.toHTML(options)
|
271 | if (needParenthesis(this, options && options.parenthesis)) {
|
272 | value = '<span class="math-paranthesis math-round-parenthesis">(</span>' + value + '<span class="math-paranthesis math-round-parenthesis">)</span>'
|
273 | }
|
274 |
|
275 | return object + index + '<span class="math-operator math-assignment-operator math-variable-assignment-operator math-binary-operator">=</span>' + value
|
276 | }
|
277 |
|
278 | |
279 |
|
280 |
|
281 |
|
282 |
|
283 | AssignmentNode.prototype._toTex = function (options) {
|
284 | const object = this.object.toTex(options)
|
285 | const index = this.index ? this.index.toTex(options) : ''
|
286 | let value = this.value.toTex(options)
|
287 | if (needParenthesis(this, options && options.parenthesis)) {
|
288 | value = `\\left(${value}\\right)`
|
289 | }
|
290 |
|
291 | return object + index + ':=' + value
|
292 | }
|
293 |
|
294 | return AssignmentNode
|
295 | }
|
296 |
|
297 | exports.name = 'AssignmentNode'
|
298 | exports.path = 'expression.node'
|
299 | exports.factory = factory
|