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

12 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:

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:

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:

    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:

    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:

    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:

    var node = math.parse('3+4*2');
    node.toString();  // returns '3 + (4 * 2)'
    
  • toTex(): string

    Get a LaTeX representation of the expression. Example:

    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:

    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:

    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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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);