Skip to content

Commit 0893f9a

Browse files
committed
Improved evaluation
1 parent 763aad6 commit 0893f9a

File tree

7 files changed

+35
-47
lines changed

7 files changed

+35
-47
lines changed

dice/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@
1010

1111
__all__ = ['roll', 'ParseException']
1212
__author__ = "Sam Clements <[email protected]>"
13-
__version__ = '0.3.2'
13+
__version__ = '0.4.2'
1414

1515

1616
def roll(string, single=True, verbose=False):
1717
"""Parses and evaluates a dice expression"""
1818
ast = dice.grammar.expression.parseString(string, parseAll=True)
19-
result = [element.evaluate(verbose=verbose) for element in ast]
19+
result = [element.evaluate_cached(verbose=verbose) for element in ast]
2020
if single:
2121
return dice.utilities.single(result)
2222
return result

dice/command.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,4 @@ def main(argv=None):
2323
result = dice.roll(args['<expression>'], verbose=args['--verbose'])
2424
if args['--verbose']:
2525
print("Result:", end=" ")
26-
return result
26+
return str(result)

dice/elements.py

Lines changed: 29 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -9,26 +9,34 @@
99

1010

1111
class Element(object):
12-
def evaluate(self, verbose=False):
12+
verbose = True
13+
14+
def evaluate(self):
1315
"""Evaluate the current object - a no-op by default"""
1416
return self
1517

1618
def evaluate_object(self, obj, cls=None):
17-
"""Evaluates Elements, and optionally coerces objects to a class"""
19+
"""Evaluates elements, and coerces objects to a class if needed"""
1820
if isinstance(obj, Element):
19-
obj = obj.evaluate()
21+
obj = obj.evaluate_cached()
2022
if cls is not None:
2123
obj = cls(obj)
2224
return obj
2325

24-
def print_evaluation(self, result):
25-
"""Prints an explanation of an evaluation"""
26-
print("Evaluating:", str(self), "->", str(result))
26+
def evaluate_cached(self, verbose=None):
27+
"""Wraps evaluate(), caching results"""
28+
if not hasattr(self, 'result'):
29+
self.result = self.evaluate()
30+
if self.verbose:
31+
print("Evaluating:", str(self), "->", str(self.result))
32+
return self.result
2733

2834

2935
class Integer(int, Element):
3036
"""A wrapper around the int class"""
3137

38+
verbose = False
39+
3240
@classmethod
3341
def parse(cls, string, location, tokens):
3442
return cls(tokens[0])
@@ -50,7 +58,7 @@ def __repr__(self):
5058
classname(self), str(self), self.sides)
5159

5260
def __str__(self):
53-
return ', '.join(map(str, self))
61+
return '[' + ', '.join(map(str, self)) + ']'
5462

5563
def __int__(self):
5664
return sum(self)
@@ -79,28 +87,17 @@ def from_string(cls, string):
7987
def __init__(self, amount, sides):
8088
self.amount = amount
8189
self.sides = sides
82-
self.result = None
8390

8491
def __repr__(self):
8592
return "Dice({0!r}, {1!r})".format(self.amount, self.sides)
8693

8794
def __str__(self):
8895
return "{0!s}d{1!s}".format(self.amount, self.sides)
8996

90-
def evaluate(self, verbose=False):
97+
def evaluate(self):
9198
self.amount = self.evaluate_object(self.amount, Integer)
9299
self.sides = self.evaluate_object(self.sides, Integer)
93-
94-
if self.result is None:
95-
self.result = Roll(self.amount, self.sides)
96-
97-
if verbose:
98-
self.print_evaluation(self.result)
99-
100-
return self.result
101-
102-
def roll(self):
103-
return self.evaluate(verbose=False)
100+
return Roll(self.amount, self.sides)
104101

105102

106103
class Operator(Element):
@@ -114,42 +111,37 @@ def __init__(self, operands):
114111
def __repr__(self):
115112
return "{0}({1})".format(
116113
classname(self),
117-
', '.join(map(repr, self.operands)))
114+
', '.join(map(str, self.operands)))
118115

119116
def evaluate(self):
120-
raise NotImplementedError(
121-
"Operator subclass has no evaluate()")
122-
123-
def evaluate_operands(self):
124117
self.operands = map(self.evaluate_object, self.operands)
125-
return self.operands
126-
118+
return self.function(*self.operands)
127119

128-
class FunctionOperator(Operator):
129120
@property
130121
def function(self):
131-
raise NotImplementedError(
132-
"FunctionOperator subclass has no function")
122+
raise NotImplementedError("Operator subclass has no function")
123+
133124

134-
def evaluate(self, verbose=False):
135-
return self.function(*self.evaluate_operands())
125+
class IntegerOperator(Operator):
126+
def evaluate_object(self, obj):
127+
return super(IntegerOperator, self).evaluate_object(obj, Integer)
136128

137129

138-
class Div(FunctionOperator):
130+
class Div(IntegerOperator):
139131
function = operator.floordiv
140132

141133

142-
class Mul(FunctionOperator):
134+
class Mul(IntegerOperator):
143135
function = operator.mul
144136

145137

146-
class Sub(FunctionOperator):
138+
class Sub(IntegerOperator):
147139
function = operator.sub
148140

149141

150-
class Add(FunctionOperator):
142+
class Add(IntegerOperator):
151143
function = operator.add
152144

153145

154-
class Total(FunctionOperator):
146+
class Total(Operator):
155147
function = sum

dice/tests/test_command.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
from dice import roll
66
from dice.command import main
7-
from dice.elements import Integer, Roll
87

98

109
def test_roll():
@@ -13,7 +12,7 @@ def test_roll():
1312

1413

1514
def test_main():
16-
assert isinstance(main(['2d6']), (Integer, Roll, list))
15+
assert isinstance(main(['2d6']), str)
1716

1817

1918
def test_main_verbose():

dice/tests/test_elements.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,6 @@ def test_dice_from_string():
1717
assert d.amount == 2 and d.sides == 6
1818

1919

20-
def test_dice_roll():
21-
assert isinstance(Dice(2, 6).roll(), Roll)
22-
23-
2420
def test_roll():
2521
amount, sides = 6, 6
2622
assert len(Roll.roll(amount, sides)) == amount

dice/tests/test_grammar.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ def test_sub(self):
3636

3737
def test_mul(self):
3838
assert roll('2 * 2') == 4
39+
assert roll('1d6 * 2') % 2 == 0
3940

4041
def test_div(self):
4142
assert roll('2 / 2') == 1

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
setup(
77
name = 'dice',
8-
version = '0.3.2',
8+
version = '0.4.0',
99

1010
author = "Sam Clements",
1111
author_email = "[email protected]",

0 commit comments

Comments
 (0)