As it is now, right now the actual math happens in simulate.py, which is a little weird for testing. It's there because the BinOp may have a left or right side that is more complex than a simple int or float or list, and needs to turn VarIds and ListIds to their actual values.
What would probably be more correct would be to move UserDefinedStrategy._reduct_binop's logic to BinOp in stratlang.py and pass the function all the strategy state it would ever need.