1 | # Expression trees
|
2 |
|
3 | When parsing an expression via `math.parse(expr)`, math.js generates an
|
4 | expression tree and returns the root node of the tree. An expression tree can
|
5 | be used to analyze, manipulate, and evaluate expressions.
|
6 |
|
7 | Example:
|
8 |
|
9 | ```js
|
10 | const node = math.parse('sqrt(2 + x)')
|
11 | ```
|
12 |
|
13 | In this case, the expression `sqrt(2 + x)` is parsed as:
|
14 |
|
15 | ```
|
16 | FunctionNode sqrt
|
17 | |
|
18 | OperatorNode +
|
19 | / \
|
20 | ConstantNode 2 x SymbolNode
|
21 | ```
|
22 |
|
23 | Alternatively, this expression tree can be build by manually creating nodes:
|
24 |
|
25 | ```js
|
26 | const node1 = new math.ConstantNode(2)
|
27 | const node2 = new math.SymbolNode('x')
|
28 | const node3 = new math.OperatorNode('+', 'add', [node1, node2])
|
29 | const node4 = new math.FunctionNode('sqrt', [node3])
|
30 | ```
|
31 |
|
32 | The resulting expression tree with root node `node4` is equal to the expression
|
33 | tree generated by `math.parse('sqrt(2 + x)')`.
|
34 |
|
35 |
|
36 | ## API
|
37 |
|
38 | ### Methods
|
39 |
|
40 | All nodes have the following methods:
|
41 |
|
42 | - `clone() : Node`
|
43 |
|
44 | Create a shallow clone of the node.
|
45 | The node itself is cloned, its childs are not cloned.
|
46 |
|
47 | - `cloneDeep() : Node`
|
48 |
|
49 | Create a deep clone of the node.
|
50 | Both the node as well as all its childs are cloned recursively.
|
51 |
|
52 | - `compile() : Object`
|
53 |
|
54 | Compile an expression into optimized JavaScript code. `compile` returns an
|
55 | object with a function `evaluate([scope])` to evaluate. Example:
|
56 |
|
57 | ```js
|
58 | const node = math.parse('2 + x') // returns the root Node of an expression tree
|
59 | const code = node.compile() // returns {evaluate: function (scope) {...}}
|
60 | const evaluate = code.evaluate({x: 3}) // returns 5
|
61 | ```
|
62 |
|
63 | - `evaluate([scope]) : Object`
|
64 |
|
65 | Compile and evaluate an expression, this is the equivalent of doing
|
66 | `node.compile().evaluate(scope)`. Example:
|
67 |
|
68 | ```js
|
69 | const node = math.parse('2 + x') // returns the root Node of an expression tree
|
70 | const evaluate = node.evaluate({x: 3}) // returns 5
|
71 | ```
|
72 |
|
73 | - `equals(other: Node) : boolean`
|
74 |
|
75 | Test whether this node equals an other node. Does a deep comparison of the
|
76 | values of both nodes.
|
77 |
|
78 | - `filter(callback: function) : Node[]`
|
79 |
|
80 | Recursively filter nodes in an expression tree. The `callback` function is
|
81 | called as `callback(node: Node, path: string, parent: Node) : boolean` for
|
82 | every node in the tree, and must return a boolean. The function `filter`
|
83 | returns an array with nodes for which the test returned true.
|
84 | Parameter `path` is a string containing a relative JSON Path.
|
85 |
|
86 | Example:
|
87 |
|
88 | ```js
|
89 | const node = math.parse('x^2 + x/4 + 3*y')
|
90 | const filtered = node.filter(function (node) {
|
91 | return node.isSymbolNode && node.name === 'x'
|
92 | })
|
93 | // returns an array with two entries: two SymbolNodes 'x'
|
94 | ```
|
95 |
|
96 | - `forEach(callback: function) : Node[]`
|
97 |
|
98 | Execute a callback for each of the child nodes of this node. The `callback`
|
99 | function is called as `callback(child: Node, path: string, parent: Node)`.
|
100 | Parameter `path` is a string containing a relative JSON Path.
|
101 |
|
102 | See also `traverse`, which is a recursive version of `forEach`.
|
103 |
|
104 | Example:
|
105 |
|
106 | ```js
|
107 | const node = math.parse('3 * x + 2')
|
108 | node.forEach(function (node, path, parent) {
|
109 | switch (node.type) {
|
110 | case 'OperatorNode':
|
111 | console.log(node.type, node.op)
|
112 | break
|
113 | case 'ConstantNode':
|
114 | console.log(node.type, node.value)
|
115 | break
|
116 | case 'SymbolNode':
|
117 | console.log(node.type, node.name)
|
118 | break
|
119 | default:
|
120 | console.log(node.type)
|
121 | }
|
122 | })
|
123 | // outputs:
|
124 | // OperatorNode *
|
125 | // ConstantNode 2
|
126 | ```
|
127 |
|
128 | - `map(callback: function) : Node[]`
|
129 |
|
130 | Transform a node. Creates a new Node having it's childs be the results of
|
131 | calling the provided callback function for each of the childs of the original
|
132 | node. The `callback` function is called as `callback(child: Node, path: string,
|
133 | parent: Node)` and must return a Node. Parameter `path` is a string containing
|
134 | a relative JSON Path.
|
135 |
|
136 | See also `transform`, which is a recursive version of `map`.
|
137 |
|
138 | - `toHTML(options: object): string`
|
139 |
|
140 | Get a HTML representation of the parsed expression. Example:
|
141 |
|
142 | ```js
|
143 | const node = math.parse('sqrt(2/3)')
|
144 | node.toHTML()
|
145 | // returns
|
146 | // <span class="math-function">sqrt</span>
|
147 | // <span class="math-paranthesis math-round-parenthesis">(</span>
|
148 | // <span class="math-number">2</span>
|
149 | // <span class="math-operator math-binary-operator math-explicit-binary-operator">/</span>
|
150 | // <span class="math-number">3</span>
|
151 | // <span class="math-paranthesis math-round-parenthesis">)</span>
|
152 | ```
|
153 |
|
154 | Information about the available HTML classes in [HTML Classes](html_classes.md).
|
155 | Information about the options in [Customization](customization.md#custom-html-latex-and-string-output).
|
156 |
|
157 | - `toString(options: object) : string`
|
158 |
|
159 | Get a string representation of the parsed expression. This is not exactly
|
160 | the same as the original input. Example:
|
161 |
|
162 | ```js
|
163 | const node = math.parse('3+4*2')
|
164 | node.toString() // returns '3 + (4 * 2)'
|
165 | ```
|
166 |
|
167 | Information about the options in [Customization](customization.md#custom-html-latex-and-string-output).
|
168 |
|
169 | - `toTex(options: object): string`
|
170 |
|
171 | Get a [LaTeX](https://en.wikipedia.org/wiki/LaTeX) representation of the
|
172 | expression. Example:
|
173 |
|
174 | ```js
|
175 | const node = math.parse('sqrt(2/3)')
|
176 | node.toTex() // returns '\sqrt{\frac{2}{3}}'
|
177 | ```
|
178 |
|
179 | Information about the options in [Customization](customization.md#custom-html-latex-and-string-output).
|
180 |
|
181 | - `transform(callback: function)`
|
182 |
|
183 | Recursively transform an expression tree via a transform function. Similar
|
184 | to `Array.map`, but recursively executed on all nodes in the expression tree.
|
185 | The callback function is a mapping function accepting a node, and returning
|
186 | a replacement for the node or the original node. Function `callback` is
|
187 | called as `callback(node: Node, path: string, parent: Node)` for every node
|
188 | in the tree, and must return a `Node`. Parameter `path` is a string containing
|
189 | a relative JSON Path.
|
190 |
|
191 | The transform function will stop iterating when a node is replaced by the
|
192 | callback function, it will not iterate over replaced nodes.
|
193 |
|
194 | For example, to replace all nodes of type `SymbolNode` having name 'x' with a
|
195 | ConstantNode with value `3`:
|
196 |
|
197 | ```js
|
198 | const node = math.parse('x^2 + 5*x')
|
199 | const transformed = node.transform(function (node, path, parent) {
|
200 | if (node.isSymbolNode && node.name === 'x') {
|
201 | return new math.ConstantNode(3)
|
202 | }
|
203 | else {
|
204 | return node
|
205 | }
|
206 | })
|
207 | transformed.toString() // returns '3 ^ 2 + 5 * 3'
|
208 | ```
|
209 |
|
210 | - `traverse(callback)`
|
211 |
|
212 | Recursively traverse all nodes in a node tree. Executes given callback for
|
213 | this node and each of its child nodes. Similar to `Array.forEach`, except
|
214 | recursive.
|
215 | The callback function is a mapping function accepting a node, and returning
|
216 | a replacement for the node or the original node. Function `callback` is
|
217 | called as `callback(node: Node, path: string, parent: Node)` for every node
|
218 | in the tree. Parameter `path` is a string containing a relative JSON Path.
|
219 | Example:
|
220 |
|
221 | ```js
|
222 | const node = math.parse('3 * x + 2')
|
223 | node.traverse(function (node, path, parent) {
|
224 | switch (node.type) {
|
225 | case 'OperatorNode':
|
226 | console.log(node.type, node.op)
|
227 | break
|
228 | case 'ConstantNode':
|
229 | console.log(node.type, node.value)
|
230 | break
|
231 | case 'SymbolNode':
|
232 | console.log(node.type, node.name)
|
233 | break
|
234 | default:
|
235 | console.log(node.type)
|
236 | }
|
237 | })
|
238 | // outputs:
|
239 | // OperatorNode +
|
240 | // OperatorNode *
|
241 | // ConstantNode 3
|
242 | // SymbolNode x
|
243 | // ConstantNode 2
|
244 | ```
|
245 |
|
246 |
|
247 | ### Properties
|
248 |
|
249 | Each `Node` has the following properties:
|
250 |
|
251 | - `comment: string`
|
252 |
|
253 | A string holding a comment if there was any in the expression, or else the
|
254 | string will be empty string. A comment can be attached to the root node of
|
255 | an expression or to each of the childs nodes of a `BlockNode`.
|
256 |
|
257 | - `isNode: true`
|
258 |
|
259 | Is defined with value `true` on Nodes. Additionally, each type of node
|
260 | adds it's own flag, for example a `SymbolNode` as has a property
|
261 | `isSymbolNode: true`.
|
262 |
|
263 | - `type: string`
|
264 |
|
265 | The type of the node, for example `'SymbolNode'` in case of a `SymbolNode`.
|
266 |
|
267 |
|
268 | ## Nodes
|
269 |
|
270 | math.js has the following types of nodes. All nodes are available at the
|
271 | namespace `math`.
|
272 |
|
273 |
|
274 | ### AccessorNode
|
275 |
|
276 | Construction:
|
277 |
|
278 | ```
|
279 | new AccessorNode(object: Node, index: IndexNode)
|
280 | ```
|
281 |
|
282 | Properties:
|
283 |
|
284 | - `object: Node`
|
285 | - `index: IndexNode`
|
286 | - `name: string` (read-only) The function or method name. Returns an empty string when undefined.
|
287 |
|
288 | Examples:
|
289 |
|
290 | ```js
|
291 | const node1 = math.parse('a[3]')
|
292 |
|
293 | const object = new math.SymbolNode('a')
|
294 | const index = new math.IndexNode([3])
|
295 | const node2 = new math.AccessorNode(object, index)
|
296 | ```
|
297 |
|
298 |
|
299 | ### ArrayNode
|
300 |
|
301 | Construction:
|
302 |
|
303 | ```
|
304 | new ArrayNode(items: Node[])
|
305 | ```
|
306 |
|
307 | Properties:
|
308 |
|
309 | - `items: Node[]`
|
310 |
|
311 | Examples:
|
312 |
|
313 | ```js
|
314 | const node1 = math.parse('[1, 2, 3]')
|
315 |
|
316 | const one = new math.ConstantNode(1)
|
317 | const two = new math.ConstantNode(2)
|
318 | const three = new math.ConstantNode(3)
|
319 | const node2 = new math.ArrayNode([one, two, three])
|
320 | ```
|
321 |
|
322 |
|
323 | ### AssignmentNode
|
324 |
|
325 | Construction:
|
326 |
|
327 | ```
|
328 | new AssignmentNode(object: SymbolNode, value: Node)
|
329 | new AssignmentNode(object: SymbolNode | AccessorNode, index: IndexNode, value: Node)
|
330 | ```
|
331 |
|
332 | Properties:
|
333 |
|
334 | - `object: SymbolNode | AccessorNode`
|
335 | - `index: IndexNode | null`
|
336 | - `value: Node`
|
337 | - `name: string` (read-only) The function or method name. Returns an empty string when undefined.
|
338 |
|
339 | Examples:
|
340 |
|
341 | ```js
|
342 | const node1 = math.parse('a = 3')
|
343 |
|
344 | const object = new math.SymbolNode('a')
|
345 | const value = new math.ConstantNode(3)
|
346 | const node2 = new math.AssignmentNode(object, value)
|
347 | ```
|
348 |
|
349 |
|
350 | ### BlockNode
|
351 |
|
352 | A `BlockNode` is created when parsing a multi line expression like `a=2;b=3` or
|
353 | `a=2\nb=3`. Evaluating a `BlockNode` returns a `ResultSet`. The results can be
|
354 | retrieved via `ResultSet.entries` or `ResultSet.valueOf()`, which contains
|
355 | an `Array` with the results of the visible lines (i.e. lines not ending with
|
356 | a semicolon).
|
357 |
|
358 | Construction:
|
359 |
|
360 | ```
|
361 | block = new BlockNode(Array.<{node: Node} | {node: Node, visible: boolean}>)
|
362 | ```
|
363 |
|
364 | Properties:
|
365 |
|
366 | - `blocks: Array.<{node: Node, visible: boolean}>`
|
367 |
|
368 | Examples:
|
369 |
|
370 | ```js
|
371 | const block1 = math.parse('a=1; b=2; c=3')
|
372 |
|
373 | const a = new math.SymbolNode('a')
|
374 | const one = new math.ConstantNode(1)
|
375 | const ass1 = new math.AssignmentNode(a, one)
|
376 |
|
377 | const b = new math.SymbolNode('b')
|
378 | const two = new math.ConstantNode(2)
|
379 | const ass2 = new math.AssignmentNode(b, two)
|
380 |
|
381 | const c = new math.SymbolNode('c')
|
382 | const three = new math.ConstantNode(3)
|
383 | const ass3 = new math.AssignmentNode(c, three)
|
384 |
|
385 | const block2 = new BlockNode([
|
386 | {node: ass1, visible: false},
|
387 | {node: ass2, visible: false},
|
388 | {node: ass3, visible: true}
|
389 | ])
|
390 | ```
|
391 |
|
392 |
|
393 | ### ConditionalNode
|
394 |
|
395 | Construction:
|
396 |
|
397 | ```
|
398 | new ConditionalNode(condition: Node, trueExpr: Node, falseExpr: Node)
|
399 | ```
|
400 |
|
401 | Properties:
|
402 |
|
403 | - `condition: Node`
|
404 | - `trueExpr: Node`
|
405 | - `falseExpr: Node`
|
406 |
|
407 | Examples:
|
408 |
|
409 | ```js
|
410 | const node1 = math.parse('a > 0 ? a : -a')
|
411 |
|
412 | const a = new math.SymbolNode('a')
|
413 | const zero = new math.ConstantNode(0)
|
414 | const condition = new math.OperatorNode('>', 'larger', [a, zero])
|
415 | const trueExpr = a
|
416 | const falseExpr = new math.OperatorNode('-', 'unaryMinus', [a])
|
417 | const node2 = new math.ConditionalNode(condition, trueExpr, falseExpr)
|
418 | ```
|
419 |
|
420 | ### ConstantNode
|
421 |
|
422 | Construction:
|
423 |
|
424 | ```
|
425 | new ConstantNode(value: *)
|
426 | ```
|
427 |
|
428 | Properties:
|
429 |
|
430 | - `value: *`
|
431 |
|
432 | Examples:
|
433 |
|
434 | ```js
|
435 | const node1 = math.parse('2.4')
|
436 |
|
437 | const node2 = new math.ConstantNode(2.4)
|
438 | const node3 = new math.ConstantNode('foo')
|
439 | ```
|
440 |
|
441 |
|
442 | ### FunctionAssignmentNode
|
443 |
|
444 | Construction:
|
445 |
|
446 | ```
|
447 | new FunctionAssignmentNode(name: string, params: string[], expr: Node)
|
448 | ```
|
449 |
|
450 | Properties:
|
451 |
|
452 | - `name: string`
|
453 | - `params: string[]`
|
454 | - `expr: Node`
|
455 |
|
456 | Examples:
|
457 |
|
458 | ```js
|
459 | const node1 = math.parse('f(x) = x^2')
|
460 |
|
461 | const x = new math.SymbolNode('x')
|
462 | const two = new math.ConstantNode(2)
|
463 | const expr = new math.OperatorNode('^', 'pow', [x, 2])
|
464 | const node2 = new math.FunctionAssignmentNode('f', ['x'], expr)
|
465 | ```
|
466 |
|
467 |
|
468 | ### FunctionNode
|
469 |
|
470 | Construction:
|
471 |
|
472 | ```
|
473 | new FunctionNode(fn: Node | string, args: Node[])
|
474 | ```
|
475 |
|
476 | Properties:
|
477 |
|
478 | - `fn: Node | string` (read-only) The object or function name which to invoke.
|
479 | - `args: Node[]`
|
480 |
|
481 | Examples:
|
482 |
|
483 | ```js
|
484 | const node1 = math.parse('sqrt(4)')
|
485 |
|
486 | const four = new math.ConstantNode(4)
|
487 | const node3 = new math.FunctionNode(new SymbolNode('sqrt'), [four])
|
488 | ```
|
489 |
|
490 |
|
491 | ### IndexNode
|
492 |
|
493 | Construction:
|
494 |
|
495 | ```
|
496 | new IndexNode(dimensions: Node[])
|
497 | new IndexNode(dimensions: Node[], dotNotation: boolean)
|
498 | ```
|
499 |
|
500 | Each dimension can be a single value, a range, or a property. The values of
|
501 | indices are one-based, including range end.
|
502 |
|
503 | An optional property `dotNotation` can be provided describing whether this index
|
504 | was written using dot notation like `a.b`, or using bracket notation
|
505 | like `a["b"]`. Default value is `false`. This information is used when
|
506 | stringifying the IndexNode.
|
507 |
|
508 | Properties:
|
509 |
|
510 | - `dimensions: Node[]`
|
511 | - `dotNotation: boolean`
|
512 |
|
513 | Examples:
|
514 |
|
515 | ```js
|
516 | const node1 = math.parse('A[1:3, 2]')
|
517 |
|
518 | const A = new math.SymbolNode('A')
|
519 | const one = new math.ConstantNode(1)
|
520 | const two = new math.ConstantNode(2)
|
521 | const three = new math.ConstantNode(3)
|
522 |
|
523 | const range = new math.RangeNode(one, three)
|
524 | const index = new math.IndexNode([range, two])
|
525 | const node2 = new math.AccessNode(A, index)
|
526 | ```
|
527 |
|
528 | ### ObjectNode
|
529 |
|
530 | Construction:
|
531 |
|
532 | ```
|
533 | new ObjectNode(properties: Object.<string, Node>)
|
534 | ```
|
535 |
|
536 | Properties:
|
537 |
|
538 | - `properties: Object.<string, Node>`
|
539 |
|
540 | Examples:
|
541 |
|
542 | ```js
|
543 | const node1 = math.parse('{a: 1, b: 2, c: 3}')
|
544 |
|
545 | const a = new math.ConstantNode(1)
|
546 | const b = new math.ConstantNode(2)
|
547 | const c = new math.ConstantNode(3)
|
548 | const node2 = new math.ObjectNode({a: a, b: b, c: c})
|
549 | ```
|
550 |
|
551 |
|
552 | ### OperatorNode
|
553 |
|
554 | Construction:
|
555 |
|
556 | ```
|
557 | new OperatorNode(op: string, fn: string, args: Node[])
|
558 | ```
|
559 |
|
560 | Additional methods:
|
561 |
|
562 | - `isUnary() : boolean`
|
563 |
|
564 | Returns true when the `OperatorNode` contains exactly one argument,
|
565 | like with a unary minus:
|
566 |
|
567 | ```js
|
568 | const a = new math.ConstantNode(2)
|
569 | const b = new math.OperatorNode('-', 'unaryMinus', [a])
|
570 | b.isUnary() // true
|
571 | ```
|
572 |
|
573 | - `isBinary() : boolean`
|
574 |
|
575 | Returns true when the `OperatorNode` contains exactly two arguments,
|
576 | like with most regular operators:
|
577 |
|
578 | ```js
|
579 | const a = new math.ConstantNode(2)
|
580 | const b = new math.ConstantNode(3)
|
581 | const c = new math.OperatorNode('+', 'add', [a, b])
|
582 | c.isBinary() // true
|
583 | ```
|
584 |
|
585 | Properties:
|
586 |
|
587 | - `op: string`
|
588 | - `fn: string`
|
589 | - `args: Node[]`
|
590 |
|
591 | Examples:
|
592 |
|
593 | ```js
|
594 | const node1 = math.parse('2.3 + 5')
|
595 |
|
596 | const a = new math.ConstantNode(2.3)
|
597 | const b = new math.ConstantNode(5)
|
598 | const node2 = new math.OperatorNode('+', 'add', [a, b])
|
599 | ```
|
600 |
|
601 | ### ParenthesisNode
|
602 |
|
603 | Construction:
|
604 |
|
605 | ```
|
606 | new ParenthesisNode(content: Node)
|
607 | ```
|
608 |
|
609 | Properties:
|
610 |
|
611 | - `content: Node`
|
612 |
|
613 | Examples:
|
614 |
|
615 | ```js
|
616 | const node1 = math.parse('(1)')
|
617 |
|
618 | const a = new math.ConstantNode(1)
|
619 | const node2 = new math.ParenthesisNode(a)
|
620 | ```
|
621 |
|
622 | ### RangeNode
|
623 |
|
624 | Construction:
|
625 |
|
626 | ```
|
627 | new RangeNode(start: Node, end: Node [, step: Node])
|
628 | ```
|
629 |
|
630 | Properties:
|
631 |
|
632 | - `start: Node`
|
633 | - `end: Node`
|
634 | - `step: Node | null`
|
635 |
|
636 | Examples:
|
637 |
|
638 | ```js
|
639 | const node1 = math.parse('1:10')
|
640 | const node2 = math.parse('0:2:10')
|
641 |
|
642 | const zero = new math.ConstantNode(0)
|
643 | const one = new math.ConstantNode(1)
|
644 | const two = new math.ConstantNode(2)
|
645 | const ten = new math.ConstantNode(10)
|
646 |
|
647 | const node3 = new math.RangeNode(one, ten)
|
648 | const node4 = new math.RangeNode(zero, ten, two)
|
649 | ```
|
650 |
|
651 | ### RelationalNode
|
652 |
|
653 | Construction:
|
654 |
|
655 | ```
|
656 | new RelationalNode(conditionals: string[], params: Node[])
|
657 | ```
|
658 |
|
659 | `conditionals` is an array of strings, each of which may be 'smaller', 'larger', 'smallerEq', 'largerEq', 'equal', or 'unequal'. The `conditionals` array must contain exactly one fewer item than `params`.
|
660 |
|
661 | Properties:
|
662 |
|
663 | - `conditionals: string[]`
|
664 | - `params: Node[]`
|
665 |
|
666 | A `RelationalNode` efficiently represents a chained conditional expression with two or more comparison operators, such as `10 < x <= 50`. The expression is equivalent to `10 < x and x <= 50`, except that `x` is evaluated only once, and evaluation stops (is "short-circuited") once any condition tests false. Operators that are subject to chaining are `<`, `>`, `<=`, `>=`, `==`, and `!=`. For backward compatibility, `math.parse` will return an `OperatorNode` if only a single conditional is present (such as `x > 2`).
|
667 |
|
668 | Examples:
|
669 |
|
670 | ```js
|
671 |
|
672 | const ten = new math.ConstantNode(10)
|
673 | const x = new math.SymbolNode('x')
|
674 | const fifty = new math.ConstantNode(50)
|
675 |
|
676 | const node1 = new math.RelationalNode(['smaller', 'smallerEq'], [ten, x, fifty])
|
677 | const node2 = math.parse('10 < x <= 50')
|
678 | ```
|
679 |
|
680 |
|
681 | ### SymbolNode
|
682 |
|
683 | Construction:
|
684 |
|
685 | ```
|
686 | new SymbolNode(name: string)
|
687 | ```
|
688 |
|
689 | Properties:
|
690 |
|
691 | - `name: string`
|
692 |
|
693 | Examples:
|
694 |
|
695 | ```js
|
696 | const node = math.parse('x')
|
697 |
|
698 | const x = new math.SymbolNode('x')
|
699 | ```
|