17 KiB
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:
const 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:
const node1 = new math.ConstantNode(2)
const node2 = new math.SymbolNode('x')
const node3 = new math.OperatorNode('+', 'add', [node1, node2])
const node4 = new math.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
Create a shallow clone of the node. The node itself is cloned, its childs are not cloned.
-
cloneDeep() : Node
Create a deep clone of the node. Both the node as well as all its childs are cloned recursively.
-
compile() : Object
Compile an expression into optimized JavaScript code.
compile
returns an object with a functionevaluate([scope])
to evaluate. Example:const node = math.parse('2 + x') // returns the root Node of an expression tree const code = node.compile() // returns {evaluate: function (scope) {...}} const evaluate = code.evaluate({x: 3}) // returns 5
-
evaluate([scope]) : Object
Compile and evaluate an expression, this is the equivalent of doing
node.compile().evaluate(scope)
. Example:const node = math.parse('2 + x') // returns the root Node of an expression tree const evaluate = node.evaluate({x: 3}) // returns 5
-
equals(other: Node) : boolean
Test whether this node equals an other node. Does a deep comparison of the values of both nodes.
-
filter(callback: function) : Node[]
Recursively filter nodes in an expression tree. The
callback
function is called ascallback(node: Node, path: string, parent: Node) : boolean
for every node in the tree, and must return a boolean. The functionfilter
returns an array with nodes for which the test returned true. Parameterpath
is a string containing a relative JSON Path.Example:
const node = math.parse('x^2 + x/4 + 3*y') const filtered = node.filter(function (node) { return node.isSymbolNode && node.name === 'x' }) // returns an array with two entries: two SymbolNodes 'x'
-
forEach(callback: function) : Node[]
Execute a callback for each of the child nodes of this node. The
callback
function is called ascallback(child: Node, path: string, parent: Node)
. Parameterpath
is a string containing a relative JSON Path.See also
traverse
, which is a recursive version offorEach
.Example:
const 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) : 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 ascallback(child: Node, path: string, parent: Node)
and must return a Node. Parameterpath
is a string containing a relative JSON Path.See also
transform
, which is a recursive version ofmap
. -
toHTML(options: object): string
Get a HTML representation of the parsed expression. Example:
const node = math.parse('sqrt(2/3)') node.toHTML() // returns // <span class="math-function">sqrt</span> // <span class="math-paranthesis math-round-parenthesis">(</span> // <span class="math-number">2</span> // <span class="math-operator math-binary-operator math-explicit-binary-operator">/</span> // <span class="math-number">3</span> // <span class="math-paranthesis math-round-parenthesis">)</span>
Information about the available HTML classes in HTML Classes. Information about the options in Customization.
-
toString(options: object) : string
Get a string representation of the parsed expression. This is not exactly the same as the original input. Example:
const node = math.parse('3+4*2') node.toString() // returns '3 + (4 * 2)'
Information about the options in Customization.
-
toTex(options: object): string
Get a LaTeX representation of the expression. Example:
const node = math.parse('sqrt(2/3)') node.toTex() // returns '\sqrt{\frac{2}{3}}'
Information about the options in Customization.
-
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. Functioncallback
is called ascallback(node: Node, path: string, parent: Node)
for every node in the tree, and must return aNode
. Parameterpath
is a string containing a relative JSON Path.The transform function will stop iterating when a node is replaced by the callback function, it will not iterate over replaced nodes.
For example, to replace all nodes of type
SymbolNode
having name 'x' with a ConstantNode with value3
:const node = math.parse('x^2 + 5*x') const transformed = node.transform(function (node, path, parent) { if (node.isSymbolNode && node.name === 'x') { return new math.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. Functioncallback
is called ascallback(node: Node, path: string, parent: Node)
for every node in the tree. Parameterpath
is a string containing a relative JSON Path. Example:const 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
Properties
Each Node
has the following properties:
-
comment: string
A string holding a comment if there was any in the expression, or else the string will be empty string. A comment can be attached to the root node of an expression or to each of the childs nodes of a
BlockNode
. -
isNode: true
Is defined with value
true
on Nodes. Additionally, each type of node adds it's own flag, for example aSymbolNode
as has a propertyisSymbolNode: true
. -
type: string
The type of the node, for example
'SymbolNode'
in case of aSymbolNode
.
Nodes
math.js has the following types of nodes. All nodes are available at the
namespace math
.
AccessorNode
Construction:
new AccessorNode(object: Node, index: IndexNode)
Properties:
object: Node
index: IndexNode
name: string
(read-only) The function or method name. Returns an empty string when undefined.
Examples:
const node1 = math.parse('a[3]')
const object = new math.SymbolNode('a')
const index = new math.IndexNode([3])
const node2 = new math.AccessorNode(object, index)
ArrayNode
Construction:
new ArrayNode(items: Node[])
Properties:
items: Node[]
Examples:
const node1 = math.parse('[1, 2, 3]')
const one = new math.ConstantNode(1)
const two = new math.ConstantNode(2)
const three = new math.ConstantNode(3)
const node2 = new math.ArrayNode([one, two, three])
AssignmentNode
Construction:
new AssignmentNode(object: SymbolNode, value: Node)
new AssignmentNode(object: SymbolNode | AccessorNode, index: IndexNode, value: Node)
Properties:
object: SymbolNode | AccessorNode
index: IndexNode | null
value: Node
name: string
(read-only) The function or method name. Returns an empty string when undefined.
Examples:
const node1 = math.parse('a = 3')
const object = new math.SymbolNode('a')
const value = new math.ConstantNode(3)
const node2 = new math.AssignmentNode(object, value)
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:
const block1 = math.parse('a=1; b=2; c=3')
const a = new math.SymbolNode('a')
const one = new math.ConstantNode(1)
const ass1 = new math.AssignmentNode(a, one)
const b = new math.SymbolNode('b')
const two = new math.ConstantNode(2)
const ass2 = new math.AssignmentNode(b, two)
const c = new math.SymbolNode('c')
const three = new math.ConstantNode(3)
const ass3 = new math.AssignmentNode(c, three)
const block2 = new BlockNode([
{node: ass1, visible: false},
{node: ass2, visible: false},
{node: ass3, visible: true}
])
ConditionalNode
Construction:
new ConditionalNode(condition: Node, trueExpr: Node, falseExpr: Node)
Properties:
condition: Node
trueExpr: Node
falseExpr: Node
Examples:
const node1 = math.parse('a > 0 ? a : -a')
const a = new math.SymbolNode('a')
const zero = new math.ConstantNode(0)
const condition = new math.OperatorNode('>', 'larger', [a, zero])
const trueExpr = a
const falseExpr = new math.OperatorNode('-', 'unaryMinus', [a])
const node2 = new math.ConditionalNode(condition, trueExpr, falseExpr)
ConstantNode
Construction:
new ConstantNode(value: *)
Properties:
value: *
Examples:
const node1 = math.parse('2.4')
const node2 = new math.ConstantNode(2.4)
const node3 = new math.ConstantNode('foo')
FunctionAssignmentNode
Construction:
new FunctionAssignmentNode(name: string, params: string[], expr: Node)
Properties:
name: string
params: string[]
expr: Node
Examples:
const node1 = math.parse('f(x) = x^2')
const x = new math.SymbolNode('x')
const two = new math.ConstantNode(2)
const expr = new math.OperatorNode('^', 'pow', [x, 2])
const node2 = new math.FunctionAssignmentNode('f', ['x'], expr)
FunctionNode
Construction:
new FunctionNode(fn: Node | string, args: Node[])
Properties:
fn: Node | string
(read-only) The object or function name which to invoke.args: Node[]
Examples:
const node1 = math.parse('sqrt(4)')
const four = new math.ConstantNode(4)
const node3 = new math.FunctionNode(new SymbolNode('sqrt'), [four])
IndexNode
Construction:
new IndexNode(dimensions: Node[])
new IndexNode(dimensions: Node[], dotNotation: boolean)
Each dimension can be a single value, a range, or a property. The values of indices are one-based, including range end.
An optional property dotNotation
can be provided describing whether this index
was written using dot notation like a.b
, or using bracket notation
like a["b"]
. Default value is false
. This information is used when
stringifying the IndexNode.
Properties:
dimensions: Node[]
dotNotation: boolean
Examples:
const node1 = math.parse('A[1:3, 2]')
const A = new math.SymbolNode('A')
const one = new math.ConstantNode(1)
const two = new math.ConstantNode(2)
const three = new math.ConstantNode(3)
const range = new math.RangeNode(one, three)
const index = new math.IndexNode([range, two])
const node2 = new math.AccessNode(A, index)
ObjectNode
Construction:
new ObjectNode(properties: Object.<string, Node>)
Properties:
properties: Object.<string, Node>
Examples:
const node1 = math.parse('{a: 1, b: 2, c: 3}')
const a = new math.ConstantNode(1)
const b = new math.ConstantNode(2)
const c = new math.ConstantNode(3)
const node2 = new math.ObjectNode({a: a, b: b, c: c})
OperatorNode
Construction:
new OperatorNode(op: string, fn: string, args: Node[], implicit: boolean = false)
Additional methods:
-
isUnary() : boolean
Returns true when the
OperatorNode
contains exactly one argument, like with a unary minus:const a = new math.ConstantNode(2) const b = new math.OperatorNode('-', 'unaryMinus', [a]) b.isUnary() // true
-
isBinary() : boolean
Returns true when the
OperatorNode
contains exactly two arguments, like with most regular operators:const a = new math.ConstantNode(2) const b = new math.ConstantNode(3) const c = new math.OperatorNode('+', 'add', [a, b]) c.isBinary() // true
Properties:
op: string
fn: string
args: Node[]
implicit: boolean
True in case of an implicit multiplication, false otherwise
Examples:
const node1 = math.parse('2.3 + 5')
const a = new math.ConstantNode(2.3)
const b = new math.ConstantNode(5)
const node2 = new math.OperatorNode('+', 'add', [a, b])
ParenthesisNode
Construction:
new ParenthesisNode(content: Node)
Properties:
content: Node
Examples:
const node1 = math.parse('(1)')
const a = new math.ConstantNode(1)
const node2 = new math.ParenthesisNode(a)
RangeNode
Construction:
new RangeNode(start: Node, end: Node [, step: Node])
Properties:
start: Node
end: Node
step: Node | null
Examples:
const node1 = math.parse('1:10')
const node2 = math.parse('0:2:10')
const zero = new math.ConstantNode(0)
const one = new math.ConstantNode(1)
const two = new math.ConstantNode(2)
const ten = new math.ConstantNode(10)
const node3 = new math.RangeNode(one, ten)
const node4 = new math.RangeNode(zero, ten, two)
RelationalNode
Construction:
new RelationalNode(conditionals: string[], params: Node[])
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
.
Properties:
conditionals: string[]
params: Node[]
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
).
Examples:
const ten = new math.ConstantNode(10)
const x = new math.SymbolNode('x')
const fifty = new math.ConstantNode(50)
const node1 = new math.RelationalNode(['smaller', 'smallerEq'], [ten, x, fifty])
const node2 = math.parse('10 < x <= 50')
SymbolNode
Construction:
new SymbolNode(name: string)
Properties:
name: string
Examples:
const node = math.parse('x')
const x = new math.SymbolNode('x')