Expression evaluator.
authorJoe Wreschnig <joe.wreschnig@gmail.com>
Mon, 15 Mar 2010 05:46:54 +0000 (22:46 -0700)
committerJoe Wreschnig <joe.wreschnig@gmail.com>
Mon, 15 Mar 2010 05:46:54 +0000 (22:46 -0700)
bulletml/__init__.py [new file with mode: 0644]
bulletml/errors.py [new file with mode: 0644]
bulletml/expr.py [new file with mode: 0644]

diff --git a/bulletml/__init__.py b/bulletml/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/bulletml/errors.py b/bulletml/errors.py
new file mode 100644 (file)
index 0000000..16b7106
--- /dev/null
@@ -0,0 +1,3 @@
+class Error(Exception):
+    pass
+
diff --git a/bulletml/expr.py b/bulletml/expr.py
new file mode 100644 (file)
index 0000000..8022476
--- /dev/null
@@ -0,0 +1,64 @@
+"""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)