Source code for example_code.expression_tools

from functools import singledispatch
import expressions


[docs] @singledispatch def evaluate(expr, *o, **kwargs): """Evaluate an expression node. Parameters ---------- expr: Expression The expression node to be evaluated. *o: numbers.Number The results of evaluating the operands of expr. **kwargs: Any keyword arguments required to evaluate specific types of expression. symbol_map: dict A dictionary mapping Symbol names to numerical values, for example: {'x': 1} """ raise NotImplementedError( f"Cannot evaluate a {type(expr).__name__}")
@evaluate.register(expressions.Number) def _(expr, *o, **kwargs): return expr.value @evaluate.register(expressions.Symbol) def _(expr, *o, symbol_map, **kwargs): return symbol_map[expr.value] @evaluate.register(expressions.Add) def _(expr, *o, **kwargs): return o[0] + o[1] @evaluate.register(expressions.Sub) def _(expr, *o, **kwargs): return o[0] - o[1] @evaluate.register(expressions.Mul) def _(expr, *o, **kwargs): return o[0] * o[1] @evaluate.register(expressions.Div) def _(expr, *o, **kwargs): return o[0] / o[1] @evaluate.register(expressions.Pow) def _(expr, *o, **kwargs): return o[0] ** o[1]
[docs] def postvisitor(expr, fn, **kwargs): '''Visit an Expression in postorder applying a function to every node. Parameters ---------- expr: Expression The expression to be visited. fn: `function(node, *o, **kwargs)` A function to be applied at each node. The function should take the node to be visited as its first argument, and the results of visiting its operands as any further positional arguments. Any additional information that the visitor requires can be passed in as keyword arguments. **kwargs: Any additional keyword arguments to be passed to fn. ''' return fn(expr, *(postvisitor(c, fn, **kwargs) for c in expr.operands), **kwargs)