mathjs/docs/expressions/expression_trees.md
2014-12-26 15:01:59 +01:00

526 lines
12 KiB
Markdown

# Expression trees
When parsing an expression via `math.parse(expr)`, math.js generates an
expression tree and returns the root node of the tree. An expression tree can
be used to analyze, manipulate, and evaluate expressions.
Example:
```js
var node = math.parse('sqrt(2 + x)');
```
In this case, the expression `sqrt(2 + x)` is parsed as:
```
FunctionNode sqrt
|
OperatorNode +
/ \
ConstantNode 2 x SymbolNode
```
Alternatively, this expression tree can be build by manually creating nodes:
```js
var node1 = new math.expression.node.ConstantNode(2);
var node2 = new math.expression.node.SymbolNode('x');
var node3 = new math.expression.node.OperatorNode('+', 'add', [node1, node2]);
var node4 = new math.expression.node.FunctionNode('sqrt', [node3]);
```
The resulting expression tree with root node `node4` is equal to the expression
tree generated by `math.parse('sqrt(2 + x)')`.
## API
### Methods
All nodes have the following methods:
- `clone() : Node`
Recursively clone an expression tree.
- `compile(namespace: Object) : Object`
Compile an expression into optimized JavaScript code.
The expression is compiled against a namespace, typically `math`, needed to
bind internally used functions. `compile` returns an object with a function
`eval([scope])` to evaluate. Example:
```js
var node = math.parse('2 + x'); // returns the root Node of an expression tree
var code = node.compile(math); // returns {eval: function (scope) {...}}
var eval = code.eval({x: 3}; // returns 5
```
- `filter(callback: function) : Array.<Node>`
Filter nodes in an expression tree. The `callback` function is called as
`callback(node: Node, path: string, parent: Node) : boolean` for every node
in the tree, and must return a boolean. The function `filter` returns an
array with nodes for which the test returned true. Parameter `path` is a
string containing a relative JSON Path.
Example:
```js
var node = math.parse('x^2 + x/4 + 3*y');
var filtered = node.filter(function (node) {
return node.type == 'SymbolNode' && node.name == 'x';
});
// returns an array with two entries: two SymbolNodes 'x'
```
- `forEach(callback: function) : Array.<Node>`
Execute a callback for each of the child nodes of this node. The `callback`
function is called as `callback(child: Node, path: string, parent: Node)`.
Parameter `path` is a string containing a relative JSON Path.
See also `traverse`, which is a recursive version of `forEach`.
Example:
```js
var node = math.parse('3 * x + 2');
node.forEach(function (node, path, parent) {
switch (node.type) {
case 'OperatorNode': console.log(node.type, node.op); break;
case 'ConstantNode': console.log(node.type, node.value); break;
case 'SymbolNode': console.log(node.type, node.name); break;
default: console.log(node.type);
}
});
// outputs:
// OperatorNode *
// ConstantNode 2
```
- `map(callback: function) : Array.<Node>`
Transform a node. Creates a new Node having it's childs be the results of
calling the provided callback function for each of the childs of the original
node. The `callback` function is called as `callback(child: Node, path: string,
parent: Node)` and must return a Node. Parameter `path` is a string containing
a relative JSON Path.
See also `transform`, which is a recursive version of `map`.
- `toString() : string`
Get a string representation of the parsed expression. This is not exactly
the same as the original input. Example:
```js
var node = math.parse('3+4*2');
node.toString(); // returns '3 + (4 * 2)'
```
- `toTex(): string`
Get a [LaTeX](http://en.wikipedia.org/wiki/LaTeX) representation of the
expression. Example:
```js
var node = math.parse('sqrt(2/3)');
node.toTex(); // returns '\sqrt{\frac{2}{3}}'
```
- `transform(callback: function)`
Recursively transform an expression tree via a transform function. Similar
to `Array.map`, but recursively executed on all nodes in the expression tree.
The callback function is a mapping function accepting a node, and returning
a replacement for the node or the original node. Function `callback` is
called as `callback(node: Node, path: string, parent: Node)` for every node
in the tree, and must return a `Node`. Parameter `path` is a string containing
a relative JSON Path.
For example, to replace all nodes of type `SymbolNode` having name 'x' with a
ConstantNode with value 2:
```js
var node = math.parse('x^2 + 5*x');
var transformed = node.transform(function (node, path, parent) {
if (node.type == 'SymbolNode' && node.name == 'x') {
return new math.expression.node.ConstantNode(3);
}
else {
return node;
}
});
transformed.toString(); // returns '(3 ^ 2) + (5 * 3)'
```
- `traverse(callback)`
Recursively traverse all nodes in a node tree. Executes given callback for
this node and each of its child nodes. Similar to `Array.forEach`, except
recursive.
The callback function is a mapping function accepting a node, and returning
a replacement for the node or the original node. Function `callback` is
called as `callback(node: Node, path: string, parent: Node)` for every node
in the tree. Parameter `path` is a string containing a relative JSON Path.
Example:
```js
var node = math.parse('3 * x + 2');
node.traverse(function (node, path, parent) {
switch (node.type) {
case 'OperatorNode': console.log(node.type, node.op); break;
case 'ConstantNode': console.log(node.type, node.value); break;
case 'SymbolNode': console.log(node.type, node.name); break;
default: console.log(node.type);
}
});
// outputs:
// OperatorNode +
// OperatorNode *
// ConstantNode 3
// SymbolNode x
// ConstantNode 2
```
### Static methods
- `Node.isNode(object) : boolean`
Test whether an object is a `Node`. Returns `true` when `object` is an
instance of `Node`, else returns `false`.
### Properties
Each `Node` has the following properties:
- `type: string`
The type of the node, for example `'SymbolNode'`.
## Nodes
math.js has the following types of nodes. All nodes are available at the
namespace `math.expression.node`.
### ArrayNode
Construction:
```
new ArrayNode(nodes: Node[])
```
Properties:
- `nodes: Node[]`
Examples:
```js
var node1 = math.parse('[1, 2, 3]');
var one = new math.expression.node.ConstantNode(1);
var two = new math.expression.node.ConstantNode(2);
var three = new math.expression.node.ConstantNode(3);
var node2 = new math.expression.node.ArrayNode([one, two, three]);
```
### AssignmentNode
Construction:
```
new AssignmentNode(name: string, expr: Node)
```
Properties:
- `name: string`
- `expr: Node`
Examples:
```js
var node1 = math.parse('a = 3');
var expr = new math.expression.node.ConstantNode(3);
var node2 = new math.expression.node.AssignmentNode('a', expr);
```
### BlockNode
A `BlockNode` is created when parsing a multi line expression like `a=2;b=3` or
`a=2\nb=3`. Evaluating a `BlockNode` returns a `ResultSet`. The results can be
retrieved via `ResultSet.entries` or `ResultSet.valueOf()`, which contains
an `Array` with the results of the visible lines (i.e. lines not ending with
a semicolon).
Construction:
```
block = new BlockNode(Array.<{node: Node} | {node: Node, visible: boolean}>)
```
Properties:
- `blocks: Array.<{node: Node, visible: boolean}>`
Examples:
```js
var block1 = math.parse('a=1; b=2; c=3');
var one = new math.expression.node.ConstantNode(1);
var a = new math.expression.node.AssignmentNode('a', one);
var two = new math.expression.node.ConstantNode(2);
var b = new math.expression.node.AssignmentNode('b', two);
var three = new math.expression.node.ConstantNode(3);
var c = new math.expression.node.AssignmentNode('c', three);
var block2 = new BlockNode([
{node: a, visible: false},
{node: b, visible: false},
{node: c, visible: true}
]);
```
### ConditionalNode
Construction:
```
new ConditionalNode(condition: Node, trueExpr: Node, falseExpr: Node)
```
Properties:
- `condition: Node`
- `trueExpr: Node`
- `falseExpr: Node`
Examples:
```js
var node1 = math.parse('a > 0 ? a : -a');
var a = new math.expression.node.SymbolNode('a');
var zero = new math.expression.node.ConstantNode(0);
var condition = new math.expression.node.OperatorNode('>', 'larger', [a, zero]);
var trueExpr = a;
var falseExpr = new math.expression.node.OperatorNode('-', 'unaryMinus', [a]);
var node2 = new math.expression.node.ConditionalNode(condition, trueExpr, falseExpr);
```
### ConstantNode
Construction:
```
new ConstantNode(value: * [, valueType: string])
```
Properties:
- `value: *`
- `valueType: string`
Examples:
```js
var node1 = math.parse('2.4');
var node2 = new math.expression.node.ConstantNode(2.4);
var node3 = new math.expression.node.ConstantNode('2.4', 'number');
```
### FunctionAssignmentNode
Construction:
```
new FunctionAssignmentNode(name: string, params: string[], expr: Node)
```
Properties:
- `name: string`
- `params: string[]`
- `expr: Node`
Examples:
```js
var node1 = math.parse('f(x) = x^2');
var x = new math.expression.node.SymbolNode('x');
var two = new math.expression.node.ConstantNode(2);
var expr = new math.expression.node.OperatorNode('^', 'pow', [x, 2]);
var node2 = new math.expression.node.FunctionAssignmentNode('f', ['x'], expr);
```
### FunctionNode
Construction:
```
new FunctionNode(name: string, args: Node[])
```
Properties:
- `symbol: Node`
- `args: Node[]`
Examples:
```js
var node1 = math.parse('sqrt(4)');
var four = new math.expression.node.ConstantNode(4);
var node2 = new math.expression.node.FunctionNode('sqrt', [four]);
```
### IndexNode
Construction:
```
new IndexNode(object: Node, ranges: Node[])
```
Note that ranges are one-based, including range end.
Properties:
- `object: Node`
- `ranges: Node[]`
Examples:
```js
var node1 = math.parse('A[1:3, 2]');
var A = new math.expression.node.SymbolNode('A');
var one = new math.expression.node.ConstantNode(1);
var two = new math.expression.node.ConstantNode(2);
var three = new math.expression.node.ConstantNode(3);
var range = new math.expression.node.RangeNode(one, three);
var node2 = new math.expression.node.IndexNode(A, [range, two]);
```
### OperatorNode
Construction:
```
new OperatorNode(op: string, fn: string, args: Node[])
```
Properties:
- `op: string`
- `fn: string`
- `args: Node[]`
Examples:
```js
var node1 = math.parse('2.3 + 5');
var a = new math.expression.node.ConstantNode(2.3);
var b = new math.expression.node.ConstantNode(5);
var node2 = new math.expression.node.OperatorNode('+', 'add', [a, b]);
```
### RangeNode
Construction:
```
new RangeNode(start: Node, end: Node [, step: Node])
```
Properties:
- `start: Node`
- `end: Node`
- `step: Node | null`
Examples:
```js
var node1 = math.parse('1:10');
var node2 = math.parse('0:2:10');
var zero = new math.expression.node.ConstantNode(0);
var one = new math.expression.node.ConstantNode(1);
var two = new math.expression.node.ConstantNode(2);
var ten = new math.expression.node.ConstantNode(10);
var node3 = new math.expression.node.RangeNode(one, ten);
var node4 = new math.expression.node.RangeNode(zero, ten, two);
```
### SymbolNode
Construction:
```
new SymbolNode(name: string)
```
Properties:
- `name: string`
Examples:
```js
var node = math.parse('x');
var x = new math.expression.node.SymbolNode('x');
```
### UpdateNode
Construction:
```
new UpdateNode(index: IndexNode, expr: Node)
```
Properties:
- `index: IndexNode`
- `expr: Node`
Examples:
```js
var node1 = math.parse('A[3, 1] = 4');
var A = new math.expression.node.SymbolNode('A');
var one = new math.expression.node.ConstantNode(1);
var three = new math.expression.node.ConstantNode(3);
var four = new math.expression.node.ConstantNode(4);
var index = new math.expression.node.IndexNode(A, [three, one]);
var node2 = new math.expression.node.UpdateNode(index, four);
```