Writing Your Own Translator

Translators or renderers are nothing more than recursive tree iterators. In other words, they perform an action at each tree node by calling itself for each of that node's children. For example, a translator for the simple expression tree ['Plus', ['Literal', 'Int', 5], ['Literal', 'Int', 10]] could be evaluated with the following simple function:

var evalExpression = function (tree) {
    if (tree[0] === 'Literal') {
        // return the value of the node to the previous level
        return tree[2];
    } else if (tree[0] === 'Plus') {
        // return the sum of each child node to the previous level
        return evalExpression(tree[1]) + evalExpression(tree[2]);
    } else {
        // tree node ID is not known
        var message = 'unknown tree node: ' + tree[0];
        console.log(message);
        throw message;
    }
}

Or equivalently,

var evalExpression = function (tree) {
    switch (tree[0]) {
        case 'Literal':
            return tree[2];
        case 'Plus':
            return evalExpression(tree[1]) + evalExpression(tree[2]);
        default:
            var message = 'unknown tree node: ' + tree[0];
            console.log(message);
            throw message;
    }
}

Notice how the function calls itself in the if (tree[0] === 'Plus') case to determine the value of each child node before adding them together. Writing large if or switch statements in JavaScript can be messy due to all the extra syntax, so I recommend using CoffeeScript instead:

evalExpression = (tree) -> switch tree[0]
    when 'Literal' then tree[2]
    when 'Plus' then evalExpression(tree[1]) + evalExpression(tree[2])
    else
        message = "unknown tree node: #{tree[0]}"
        console.log message
        throw message

Your language choice does not matter since CoffeeScript will be compiled into JavaScript anyway. All nodes are represented as arrays where the first element (i.e. tree[0]) is a string ID. Appendix B lists all tree node IDs and structures that MathLex produces. Write cases to handle all node IDs you wish to handle, and then leave a default case to gracefully deal with unknown node IDs.

After you've built a translator function, include the source file in the browser (compile first if it is written in CoffeeScript) and pass it an AST object returned by MathLex.parse(). For example, the following code would work in a web browser:

tree = MathLex.parse('5+10');
// => ['Plus', ['Literal', 'Int', 5], ['Literal', 'Int', 10]]
evalExpression(tree);
// => 15

Suppose we had a more advanced expression, say (33+3)/sqrt(3*3)-3. Assuming our evalExpression function had implemented division, square roots, and subtraction correctly, the code above would execute as follows:

tree = MathLex.parse('(33+3)/sqrt(3*3)-3');
// => ['Minus',
//      ['Fraction',
//        ['Plus', ['Literal', 'Int', 33], ['Literal', 'Int', 3]],
//        ['Function', ['Variable', 'sqrt'],
//          [['Times', ['Literal', 'Int', 3], ['Literal', 'Int', 3]]]
//        ]
//      ],
//      ['Literal', 'Int', 3]
//    ]
evalExpression(tree);
// => 9

Note that functions are stored in "builder notation" in which the first element of the Function node contains the function as an expression (in this case, just ['Variable', 'sqrt']) and the function parameters as a list of subnodes in the second element (in this case, [['Times', ...]]).