UNPKG

5.53 kBJavaScriptView Raw
1'use strict'
2
3const map = require('../../utils/array').map
4
5function factory (type, config, load, typed) {
6 const Node = load(require('./Node'))
7
8 /**
9 * @constructor ArrayNode
10 * @extends {Node}
11 * Holds an 1-dimensional array with items
12 * @param {Node[]} [items] 1 dimensional array with items
13 */
14 function ArrayNode (items) {
15 if (!(this instanceof ArrayNode)) {
16 throw new SyntaxError('Constructor must be called with the new operator')
17 }
18
19 this.items = items || []
20
21 // validate input
22 if (!Array.isArray(this.items) || !this.items.every(type.isNode)) {
23 throw new TypeError('Array containing Nodes expected')
24 }
25
26 // TODO: deprecated since v3, remove some day
27 const deprecated = function () {
28 throw new Error('Property `ArrayNode.nodes` is deprecated, use `ArrayNode.items` instead')
29 }
30 Object.defineProperty(this, 'nodes', { get: deprecated, set: deprecated })
31 }
32
33 ArrayNode.prototype = new Node()
34
35 ArrayNode.prototype.type = 'ArrayNode'
36
37 ArrayNode.prototype.isArrayNode = true
38
39 /**
40 * Compile a node into a JavaScript function.
41 * This basically pre-calculates as much as possible and only leaves open
42 * calculations which depend on a dynamic scope with variables.
43 * @param {Object} math Math.js namespace with functions and constants.
44 * @param {Object} argNames An object with argument names as key and `true`
45 * as value. Used in the SymbolNode to optimize
46 * for arguments from user assigned functions
47 * (see FunctionAssignmentNode) or special symbols
48 * like `end` (see IndexNode).
49 * @return {function} Returns a function which can be called like:
50 * evalNode(scope: Object, args: Object, context: *)
51 */
52 ArrayNode.prototype._compile = function (math, argNames) {
53 const evalItems = map(this.items, function (item) {
54 return item._compile(math, argNames)
55 })
56
57 const asMatrix = (math.config().matrix !== 'Array')
58 if (asMatrix) {
59 const matrix = math.matrix
60 return function evalArrayNode (scope, args, context) {
61 return matrix(map(evalItems, function (evalItem) {
62 return evalItem(scope, args, context)
63 }))
64 }
65 } else {
66 return function evalArrayNode (scope, args, context) {
67 return map(evalItems, function (evalItem) {
68 return evalItem(scope, args, context)
69 })
70 }
71 }
72 }
73
74 /**
75 * Execute a callback for each of the child nodes of this node
76 * @param {function(child: Node, path: string, parent: Node)} callback
77 */
78 ArrayNode.prototype.forEach = function (callback) {
79 for (let i = 0; i < this.items.length; i++) {
80 const node = this.items[i]
81 callback(node, 'items[' + i + ']', this)
82 }
83 }
84
85 /**
86 * Create a new ArrayNode having it's childs be the results of calling
87 * the provided callback function for each of the childs of the original node.
88 * @param {function(child: Node, path: string, parent: Node): Node} callback
89 * @returns {ArrayNode} Returns a transformed copy of the node
90 */
91 ArrayNode.prototype.map = function (callback) {
92 const items = []
93 for (let i = 0; i < this.items.length; i++) {
94 items[i] = this._ifNode(callback(this.items[i], 'items[' + i + ']', this))
95 }
96 return new ArrayNode(items)
97 }
98
99 /**
100 * Create a clone of this node, a shallow copy
101 * @return {ArrayNode}
102 */
103 ArrayNode.prototype.clone = function () {
104 return new ArrayNode(this.items.slice(0))
105 }
106
107 /**
108 * Get string representation
109 * @param {Object} options
110 * @return {string} str
111 * @override
112 */
113 ArrayNode.prototype._toString = function (options) {
114 const items = this.items.map(function (node) {
115 return node.toString(options)
116 })
117 return '[' + items.join(', ') + ']'
118 }
119
120 /**
121 * Get a JSON representation of the node
122 * @returns {Object}
123 */
124 ArrayNode.prototype.toJSON = function () {
125 return {
126 mathjs: 'ArrayNode',
127 items: this.items
128 }
129 }
130
131 /**
132 * Instantiate an ArrayNode from its JSON representation
133 * @param {Object} json An object structured like
134 * `{"mathjs": "ArrayNode", items: [...]}`,
135 * where mathjs is optional
136 * @returns {ArrayNode}
137 */
138 ArrayNode.fromJSON = function (json) {
139 return new ArrayNode(json.items)
140 }
141
142 /**
143 * Get HTML representation
144 * @param {Object} options
145 * @return {string} str
146 * @override
147 */
148 ArrayNode.prototype.toHTML = function (options) {
149 const items = this.items.map(function (node) {
150 return node.toHTML(options)
151 })
152 return '<span class="math-parenthesis math-square-parenthesis">[</span>' + items.join('<span class="math-separator">,</span>') + '<span class="math-parenthesis math-square-parenthesis">]</span>'
153 }
154
155 /**
156 * Get LaTeX representation
157 * @param {Object} options
158 * @return {string} str
159 */
160 ArrayNode.prototype._toTex = function (options) {
161 let s = '\\begin{bmatrix}'
162
163 this.items.forEach(function (node) {
164 if (node.items) {
165 s += node.items.map(function (childNode) {
166 return childNode.toTex(options)
167 }).join('&')
168 } else {
169 s += node.toTex(options)
170 }
171
172 // new line
173 s += '\\\\'
174 })
175 s += '\\end{bmatrix}'
176 return s
177 }
178
179 return ArrayNode
180}
181
182exports.name = 'ArrayNode'
183exports.path = 'expression.node'
184exports.factory = factory