This repository has been archived by the owner on Jan 2, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 32
/
interpreter.py
163 lines (125 loc) · 4.12 KB
/
interpreter.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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
import inspect
from pyparsing import Word, OneOrMore, Optional, Group, Suppress, alphanums
class DeviceNotAvailable(Exception):
pass
class ActionNotAvailable(Exception):
pass
class IncorrectAction(Exception):
pass
class Device(object):
def __call__(self, *args):
action = args[0]
try:
method = getattr(self, action)
except AttributeError:
raise ActionNotAvailable(
'!!! "{}" not available for {}'.format(action, self.__class__.__name__)
)
signature = inspect.signature(method)
# arity of the device's method (excluding self)
arity = len(signature.parameters.keys())
# or alternatively
# arity = method.__code__.co_argcount -1
# arity = len(inspect.getfullargspec(method)[0]) - 1
num_args = len([a for a in args if a is not None])
if arity != num_args - 1:
parameters = list(signature.parameters.keys())
if parameters:
substring = "these parameters {}".format(parameters)
else:
substring = "no parameters"
err_msg = '!!! "{}" on {} must be defined with {}'.format(
action, self.__class__.__name__, substring
)
raise IncorrectAction(err_msg)
else:
if num_args == 1:
method()
elif num_args == 2:
method(int(args[1]))
else:
raise Exception
class Garage(Device):
def __init__(self):
self.is_open = False
def open(self):
print("opening the garage")
self.is_open = True
def close(self):
print("closing the garage")
self.is_open = False
class Boiler(Device):
def __init__(self):
self.temperature = 83
def heat(self, amount):
print("heat the boiler up by {} degrees".format(amount))
self.temperature += amount
def cool(self, amount):
print("cool the boiler down by {} degrees".format(amount))
self.temperature -= amount
class Television(Device):
def __init__(self):
self.is_on = False
def switch_on(self):
print("switch on the television")
self.is_on = True
def switch_off(self):
print("switch off the television")
self.is_on = False
class Interpreter(object):
DEVICES = {"boiler": Boiler(), "garage": Garage(), "television": Television()}
@staticmethod
def parse(input_string):
word = Word(alphanums)
command = Group(OneOrMore(word))
token = Suppress("->")
device = Group(OneOrMore(word))
argument = Group(OneOrMore(word))
event = command + token + device + Optional(token + argument)
parse_results = event.parseString(input_string)
cmd = parse_results[0]
dev = parse_results[1]
cmd_str = "_".join(cmd)
dev_str = "_".join(dev)
try:
val = parse_results[2]
except IndexError:
val_str = None
else:
val_str = "_".join(val)
return cmd_str, dev_str, val_str
def interpret(self, input_string):
cmd_str, dev_str, val_str = self.parse(input_string)
try:
device = self.DEVICES[dev_str]
except KeyError:
raise DeviceNotAvailable(
"!!! {} is not available an available " "device".format(dev_str)
)
else:
device(cmd_str, val_str)
def main():
interpreter = Interpreter()
valid_inputs = (
"open -> garage",
"heat -> boiler -> 5",
"cool -> boiler -> 3",
"switch on -> television",
"switch off -> television",
)
for valid_input in valid_inputs:
interpreter.interpret(valid_input)
try:
interpreter.interpret("read -> book")
except DeviceNotAvailable as e:
print(e)
try:
interpreter.interpret("heat -> boiler")
except IncorrectAction as e:
print(e)
try:
interpreter.interpret("throw away -> television")
except ActionNotAvailable as e:
print(e)
if __name__ == "__main__":
main()