c7d40983dd620d5fa961f7fb70b5b1e7111f456c
1 """BulletML expression evaluator.
3 http://www.asahi-net.or.jp/~cs8k-cyu/bulletml/index_e.html
6 # BulletML assumes 1/2 = 0.5.
7 from __future__
import division
12 from bulletml
.errors
import Error
14 __all__
= ["ExprError", "NumberDef", "INumberDef"]
16 class ExprError(Error
):
17 """Raised when an invalid expression is evaluated/compiled."""
20 class NumberDef(object):
21 """BulletML numeric expression.
23 This translates BulletML numeric expressions into Python expressions.
34 GLOBALS
= dict(random
=random
.random
, __builtins__
={})
36 def __init__(self
, expr
):
39 except AttributeError:
43 # nedbatchelder.com/blog/201206/eval_really_is_dangerous.html
47 self
.string
= expr
= str(expr
)
48 repl
= lambda match
: "params[%d]" % (int(match
.group()[1:]) - 1)
49 expr
= re
.sub(r
"\$\d+", repl
, expr
.lower())
50 self
.__expr
= expr
.replace("$rand", "random()").replace("$rank", "rank")
53 self
._value
= eval(self
.__expr
, dict(__builtins__
={}))
55 variables
= dict(rank
=1, params
=[0] * 99)
56 value
= eval(self
.__expr
, self
.GLOBALS
, variables
)
57 if not isinstance(value
, (int, float)):
60 self
.expr
= self
.string
62 self
.expr
= self
._value
65 self
.__expr
= compile(self
.__expr
, __file__
, "eval")
67 def __call__(self
, params
, rank
):
68 """Evaluate the expression and return its value."""
69 if self
._value
is not None:
71 variables
= { 'rank': rank
, 'params': params
}
72 return eval(self
.__expr
, self
.GLOBALS
, variables
)
75 return "%s(%r)" % (type(self
).__name
__, self
.expr
)
77 class INumberDef(NumberDef
):
78 """A NumberDef, but returns rounded integer results."""
79 def __init__(self
, expr
):
80 super(INumberDef
, self
).__init
__(expr
)
81 if self
._value
is not None:
82 self
._value
= int(round(self
._value
))
84 def __call__(self
, params
, rank
):
85 # Avoid int(round(__call__())) overhead for constants.
86 if self
._value
is not None:
88 return int(round(super(INumberDef
, self
).__call
__(params
, rank
)))