Docstrings.
[python-bulletml.git] / bulletml / expr.py
1 """BulletML expression evaluator.
2
3 http://www.asahi-net.or.jp/~cs8k-cyu/bulletml/index_e.html
4 """
5
6 # BulletML assumes 1/2 = 0.5.
7 from __future__ import division
8
9 import random
10 import re
11
12 from bulletml.errors import Error
13
14 class ExprError(Error):
15 """Raised when an invalid expression is evaluated/compiled."""
16 pass
17
18 class NumberDef(object):
19 """BulletML numeric expression.
20
21 This translates BulletML numeric expressions into Python expressions.
22 The
23
24 Examples:
25 35
26 360/16
27 0.7 + 0.9*$rand
28 180-$rank*20
29 (2+$1)*0.3
30
31 """
32 def __init__(self, expr):
33 try:
34 expr = expr.__original
35 except AttributeError:
36 pass
37 self.__original = expr
38 repl = lambda match: "params[%d]" % (int(match.group()[1:]) - 1)
39 expr = re.sub(r"\$\d+", repl, expr.lower())
40 self.__expr = expr.replace("$rand", "rand").replace("$rank", "rank")
41 try:
42 try:
43 self.__value = eval(self.__expr, dict(__builtins__={}))
44 except NameError:
45 fake = [0] * 99
46 variables = dict(rand=1, rank=1, params=fake, __builtins__={})
47 value = eval(self.__expr, variables)
48 if not isinstance(value, (int, float)):
49 raise TypeError(expr)
50 self.__value = None
51 except Exception:
52 raise ExprError(expr)
53 self.__expr = compile(self.__expr, __file__, "eval")
54
55 def __call__(self, params, rank):
56 """Evaluate the expression and return its value."""
57 if self.__value is not None:
58 return self.__value
59 rand = random.random()
60 variables = dict(rand=rand, rank=rank, params=params)
61 return eval(self.__expr, variables)
62
63 def __repr__(self):
64 return "%s(%r)" % (type(self).__name__, self.__original)