Skip to content

Commit a84eec5

Browse files
committed
Split out example parsers into separate modules in the examples directory.
1 parent 343815f commit a84eec5

11 files changed

+224
-203
lines changed

CHANGES

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ plusminus Change Log
3232
using (), (], [) or [] notation ("in" with sets is still
3333
supported).
3434

35+
- Deleted the example_parsers.py module in the examples directory, and split
36+
the parsers out into separate modules in that directory.
37+
3538
- Added __version_info__ structure, following pattern of sys.version_info
3639
field names.
3740

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#
2+
# business_arithmetic_parser.py
3+
#
4+
# Copyright 2021, Paul McGuire
5+
#
6+
from plusminus import BaseArithmeticParser, safe_pow
7+
8+
9+
class BusinessArithmeticParser(BaseArithmeticParser):
10+
"""
11+
A parser for evaluating common financial and retail calculations:
12+
13+
50% of 20
14+
20 * (1-20%)
15+
(100-20)% of 20
16+
5 / 20%
17+
FV(20000, 3%, 30)
18+
FV(20000, 3%/12, 30*12)
19+
20+
Functions:
21+
FV(present_value, rate_per_period, number_of_periods)
22+
future value of an amount, n periods in the future, at an interest rate per period
23+
PV(future_value, rate_per_period, number_of_periods)
24+
present value of a future amount, n periods in the future, at an interest rate per period
25+
PP(present_value, rate_per_period, number_of_periods)
26+
periodic value of n amounts, one per period, for n periods, at an interest rate per period
27+
"""
28+
29+
def customize(self):
30+
def pv(future_value, rate, n_periods):
31+
return future_value / safe_pow(1 + rate, n_periods)
32+
33+
def fv(present_value, rate, n_periods):
34+
return present_value * safe_pow(1 + rate, n_periods)
35+
36+
def pp(present_value, rate, n_periods):
37+
return rate * present_value / (1 - safe_pow(1 + rate, -n_periods))
38+
39+
super().customize()
40+
self.add_operator("of", 2, BaseArithmeticParser.LEFT, lambda a, b: a * b)
41+
self.add_operator("%", 1, BaseArithmeticParser.LEFT, lambda a: a / 100)
42+
43+
self.add_function("PV", 3, pv)
44+
self.add_function("FV", 3, fv)
45+
self.add_function("PP", 3, pp)
46+
47+
48+
if __name__ == '__main__':
49+
50+
parser = BusinessArithmeticParser()
51+
parser.runTests(
52+
"""\
53+
25%
54+
20 * 50%
55+
50% of 20
56+
20 * (1-20%)
57+
(100-20)% of 20
58+
5 / 20%
59+
FV(20000, 3%, 30)
60+
FV(20000, 3%/12, 30*12)
61+
""",
62+
postParse=lambda _, result: result[0].evaluate(),
63+
)
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#
2+
# combinatorics_arithmetic_parser.py
3+
#
4+
# Copyright 2021, Paul McGuire
5+
#
6+
from plusminus import BaseArithmeticParser, ArithmeticParser, constrained_factorial
7+
8+
9+
class CombinatoricsArithmeticParser(BaseArithmeticParser):
10+
"""
11+
Parser for evaluating expressions of combinatorics problems, for numbers of
12+
permutations (nPm) and combinations (nCm):
13+
14+
nPm = n! / (n-m)!
15+
8P4 = number of (ordered) permutations of selecting 4 items from a collection of 8
16+
17+
nCm = n! / m!(n-m)!
18+
8C4 = number of (unordered) combinations of selecting 4 items from a collection of 8
19+
"""
20+
21+
def customize(self):
22+
super().customize()
23+
# fmt: off
24+
self.add_operator("P", 2, BaseArithmeticParser.LEFT,
25+
lambda a, b: int(constrained_factorial(a) / constrained_factorial(a - b)))
26+
self.add_operator("C", 2, BaseArithmeticParser.LEFT,
27+
lambda a, b: int(constrained_factorial(a)
28+
/ constrained_factorial(b)
29+
/ constrained_factorial(a - b)))
30+
self.add_operator(*ArithmeticParser.Operators.FACTORIAL)
31+
# fmt: on
32+
33+
34+
if __name__ == '__main__':
35+
36+
parser = CombinatoricsArithmeticParser()
37+
parser.runTests(
38+
"""\
39+
3!
40+
-3!
41+
3!!
42+
6! / (6-2)!
43+
6 P 2
44+
6! / (2!*(6-2)!)
45+
6 C 2
46+
6P6
47+
6C6
48+
""",
49+
postParse=lambda _, result: result[0].evaluate(),
50+
)
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#
2+
# date_time_arithmetic_parser.py
3+
#
4+
# Copyright 2021, Paul McGuire
5+
#
6+
from plusminus import BaseArithmeticParser
7+
8+
9+
class DateTimeArithmeticParser(BaseArithmeticParser):
10+
"""
11+
Parser for evaluating expressions in dates and times, using operators d, h, m, and s
12+
to define terms for amounts of days, hours, minutes, and seconds:
13+
14+
now()
15+
today()
16+
now() + 10s
17+
now() + 24h
18+
19+
All numeric expressions will be treated as UTC integer timestamps. To display
20+
timestamps as ISO strings, use str():
21+
22+
str(now())
23+
str(today() + 3d)
24+
"""
25+
26+
SECONDS_PER_MINUTE = 60
27+
SECONDS_PER_HOUR = SECONDS_PER_MINUTE * 60
28+
SECONDS_PER_DAY = SECONDS_PER_HOUR * 24
29+
30+
def customize(self):
31+
from datetime import datetime
32+
33+
# fmt: off
34+
self.add_operator("d", 1, BaseArithmeticParser.LEFT, lambda t: t * DateTimeArithmeticParser.SECONDS_PER_DAY)
35+
self.add_operator("h", 1, BaseArithmeticParser.LEFT, lambda t: t * DateTimeArithmeticParser.SECONDS_PER_HOUR)
36+
self.add_operator("m", 1, BaseArithmeticParser.LEFT, lambda t: t * DateTimeArithmeticParser.SECONDS_PER_MINUTE)
37+
self.add_operator("s", 1, BaseArithmeticParser.LEFT, lambda t: t)
38+
39+
self.add_function("now", 0, lambda: datetime.utcnow().timestamp())
40+
self.add_function("today", 0,
41+
lambda: datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0).timestamp())
42+
self.add_function("str", 1, lambda dt: str(datetime.fromtimestamp(dt)))
43+
# fmt: on
44+
45+
46+
if __name__ == "__main__":
47+
48+
parser = DateTimeArithmeticParser()
49+
parser.runTests(
50+
"""\
51+
now()
52+
str(now())
53+
str(today())
54+
"A day from now: " + str(now() + 1d)
55+
"A day and an hour from now: " + str(now() + 1d + 1h)
56+
str(now() + 3*(1d + 1h))
57+
""",
58+
postParse=lambda _, result: result[0].evaluate(),
59+
)
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#
2+
# dice_roll_parser.py
3+
#
4+
# Copyright 2021, Paul McGuire
5+
#
6+
from plusminus import BaseArithmeticParser
7+
8+
9+
class DiceRollParser(BaseArithmeticParser):
10+
"""
11+
Parser for evaluating expressions representing rolls of dice, as used in many board and
12+
role-playing games, such as:
13+
14+
d20
15+
3d20
16+
5d6 + d20
17+
"""
18+
19+
def customize(self):
20+
import random
21+
22+
# fmt: off
23+
self.add_operator("d", 1, BaseArithmeticParser.RIGHT, lambda a: random.randint(1, a))
24+
self.add_operator("d", 2, BaseArithmeticParser.LEFT,
25+
lambda a, b: sum(random.randint(1, b) for _ in range(a)))
26+
# fmt: on
27+
28+
29+
if __name__ == '__main__':
30+
31+
parser = DiceRollParser()
32+
parser.runTests(
33+
"""\
34+
d20
35+
3d6
36+
d20+3d4
37+
2d100
38+
""",
39+
postParse=lambda _, result: result[0].evaluate(),
40+
)

0 commit comments

Comments
 (0)