--- /dev/null
+"""BulletML expression evaluator.
+
+http://www.asahi-net.or.jp/~cs8k-cyu/bulletml/index_e.html
+"""
+
+# BulletML assumes 1/2 = 0.5.
+from __future__ import division
+
+import random
+import re
+
+from bulletml.errors import Error
+
+class ExprError(Error):
+ """Raised when an invalid expression is evaluated/compiled."""
+ pass
+
+class NumberDef(object):
+ """BulletML numeric expression.
+
+ This translates BulletML numeric expressions into Python expressions.
+ The
+
+ Examples:
+ 35
+ 360/16
+ 0.7 + 0.9*$rand
+ 180-$rank*20
+ (2+$1)*0.3
+
+ """
+ def __init__(self, expr):
+ try:
+ expr = expr.__original
+ except AttributeError:
+ pass
+ self.__original = expr
+ repl = lambda match: "params[%d]" % (int(match.group()[1:]) - 1)
+ expr = re.sub(r"\$\d+", repl, expr.lower())
+ self.__expr = expr.replace("$rand", "rand").replace("$rank", "rank")
+ try:
+ try:
+ self.__value = eval(self.__expr, dict(__builtins__={}))
+ except NameError:
+ fake = [0] * 99
+ variables = dict(rand=1, rank=1, params=fake, __builtins__={})
+ value = eval(self.__expr, variables)
+ if not isinstance(value, (int, float)):
+ raise TypeError(expr)
+ self.__value = None
+ except Exception:
+ raise ExprError(expr)
+ self.__expr = compile(self.__expr, __file__, "eval")
+
+ def __call__(self, params, rank):
+ """Evaluate the expression and return its value."""
+ if self.__value is not None:
+ return self.__value
+ rand = random.random()
+ variables = dict(rand=rand, rank=rank, params=params)
+ return eval(self.__expr, variables)
+
+ def __repr__(self):
+ return "%s(%r)" % (type(self).__name__, self.__original)