-
Notifications
You must be signed in to change notification settings - Fork 3
/
calculator.py
127 lines (109 loc) · 3.64 KB
/
calculator.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
"""
Adapted from Chapter 10 of Stroustrop, "The C++ Programming Language 4th ed"
"""
import enum, collections
class Kind(enum.Enum):
name = 1
number = 2
end = 3
plus = 4
minus = 5
mul = 6
div = 7
assign = 8
lp = 9
rp = 10
class Token():
def __init__(self, kind, value):
self._kind = kind
self._value = value
@property
def kind(self):
return self._kind
@property
def value(self):
return self._value
def expr(token_stream, get_next):
left = term(token_stream, get_next)
while True:
if token_stream.current.kind == Kind.plus:
left += term(token_stream, True)
elif token_stream.current.kind == Kind.minus:
left -= term(token_stream, True)
else:
return left
def term(token_stream, get_next):
left = prim(token_stream, get_next)
while True:
if token_stream.current.kind == Kind.mul:
left *= prim(token_stream, True)
elif token_stream.current.kind == Kind.div:
# Catch divide by zero??
left /= prim(token_stream, True)
else:
return left
def prim(token_stream, get_next):
if get_next:
token_stream.get()
if token_stream.current.kind == Kind.number:
v = float(token_stream.current.value)
token_stream.get()
return v
elif token_stream.current.kind == Kind.name:
name = token_stream.current.value
v = lookup[name]
token_stream.get()
if token_stream.current.kind == Kind.assign:
v = expr(token_stream, True)
lookup[name] = v
return v
elif token_stream.current.kind == Kind.minus:
return -prim(token_stream, True)
elif token_stream.current.kind == Kind.lp:
e = expr(token_stream, True)
if token_stream.current.kind != Kind.rp:
raise Exception("Unbalanced bracket")
token_stream.get()
return e
else:
raise Exception("Expected primary expression")
class TokenStream():
def __init__(self, data):
self._data = data
self._index = 0
self._current_token = Token(Kind.end, "")
@property
def current(self):
return self._current_token
_CHAR_MAP = {"*" : Kind.mul, "/" : Kind.div, "+" : Kind.plus, "-" : Kind.minus,
"(" : Kind.lp, ")" : Kind.rp, "=" : Kind.assign}
_DIGITS = "0123456789."
_WHITESPACE = " \t"
def get(self):
while self._index < len(self._data) and self._data[self._index] in self._WHITESPACE:
self._index += 1
if self._index >= len(self._data):
self._current_token = Token(Kind.end, "")
elif self._data[self._index] in self._CHAR_MAP:
self._current_token = Token(self._CHAR_MAP[self._data[self._index]], "")
self._index += 1
elif self._data[self._index] in self._DIGITS:
numstring = ""
while self._index < len(self._data) and self._data[self._index] in self._DIGITS:
numstring += self._data[self._index]
self._index += 1
self._current_token = Token(Kind.number, numstring)
else:
name = ""
while self._index < len(self._data) and self._data[self._index].isalpha():
name += self._data[self._index]
self._index += 1
self._current_token = Token(Kind.name, name)
return self._current_token
if __name__ == "__main__":
lookup = collections.defaultdict(float)
lookup["pi"] = 3.141592654
while True:
line = input("> ")
ts = TokenStream(line)
print(expr(ts, True))