test_loop_interpreter.py 10.2 KB
Newer Older
Chris's avatar
Chris committed
1
from loopinterpreter import interpret
Chris's avatar
Chris committed
2
import unittest
3
from unittest import mock
Chris's avatar
Chris committed
4

Chris's avatar
Chris committed
5

6
7
8
def input_exit(prompt):
    return "EXIT"

Chris's avatar
Chris committed
9

10
11
12
def input_continue(prompt):
    return ""

Chris's avatar
Chris committed
13

14
15
16
17
def init_without_tokens(self, regex_to_token, program):
    self.regex_to_token = {}
    self.program = program
    self.current_position = 0
Chris's avatar
Chris committed
18

Chris's avatar
Chris committed
19
20

class LOOPInterpreterTest(unittest.TestCase):
Chris's avatar
Chris committed
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
    def test_assignment_default_zero(self):
        self.assertEqual(interpret('x0:=x0 + 0'), 0)
        self.assertEqual(interpret('x0:=x1 + 0'), 0)
        self.assertEqual(interpret('x0:=x2 + 0'), 0)

    def test_assignment_non_negative(self):
        self.assertEqual(interpret('x0:=x0-1'), 0)
        self.assertEqual(interpret('x0:=x1-1'), 0)
        self.assertEqual(interpret('x0:=x2-6'), 0)

    def test_assignment_number(self):
        self.assertEqual(interpret("x0:=5"), 5)
        self.assertEqual(interpret("x0:=2"), 2)
        self.assertEqual(interpret('x0:=3'), 3)

    def test_assignment_variable(self):
        self.assertEqual(interpret('x0:=x0+1'), 1)
        self.assertEqual(interpret('x0:= 5; x0:=x0-1'), 4)
        self.assertEqual(interpret('x0:=x1-1; x0:=x0+1'), 1)

    def test_assignment_wrong_syntax(self):
        with self.assertRaises(SyntaxError):
            interpret('x1:=x2')
        with self.assertRaises(SyntaxError):
            interpret('x1:=0+x2')
        with self.assertRaises(SyntaxError):
            interpret('x5:=-1+x4')
        with self.assertRaises(SyntaxError):
            interpret('x5:=-x3+x1')
        with self.assertRaises(SyntaxError):
            interpret('x5:=x1-x3')
        with self.assertRaises(SyntaxError):
            interpret('x2:=x1+x4')
        with self.assertRaises(SyntaxError):
            interpret('x2:=x1+2;')
        with self.assertRaises(SyntaxError):
            interpret('x1:=c')
        with self.assertRaises(SyntaxError):
            interpret('xi:=2')
        with self.assertRaises(SyntaxError):
            interpret('x0:=xj+1')

    def test_loop_assignment(self):
        self.assertEqual(interpret('x1:=1; LOOP x1 DO x0:=1 END'), 1)
        self.assertEqual(interpret('x1:=2; LOOP x1 DO x0:=x0 + 2 END'), 4)

    def test_loop_empty_assignment(self):
        self.assertEqual(interpret('LOOP x1 DO x0:=1 END'), 0)
        self.assertEqual(interpret('x2:=2;LOOP x1 DO x0:=x2+1 END'), 0)

    def test_loop_nested_assignment(self):
        self.assertEqual(interpret('x1:=3; LOOP x1 DO x2:=x2+1; LOOP x2 DO x0:=x0+1 END END'), 6)
        self.assertEqual(interpret('x1:=3; x2:=3; LOOP x1 DO x2:=x2-1; LOOP x2 DO x0:=x0+1 END END'), 3)

    def test_loop_forbidden_identifier(self):
        with self.assertRaises(SyntaxError):
            interpret('x1:=1; LOOP x1 DO x1:=x1+1 END')
        with self.assertRaises(SyntaxError):
            interpret('x1:=1; LOOP x1 DO x2:=x1 + 2 END')

    def test_loop_empty_forbidden_identifier(self):
        with self.assertRaises(SyntaxError):
            interpret('LOOP x2 DO x2:=2 END')
        with self.assertRaises(SyntaxError):
            interpret('LOOP x1 DO x2:=x1 - 2 END')

    def test_loop_nested_forbidden_identifier(self):
        with self.assertRaises(SyntaxError):
            interpret('x1:=2; LOOP x1 DO LOOP x1 DO x0:=x0+1 END END')
        with self.assertRaises(SyntaxError):
            interpret('x1:=1; x2:=2 LOOP x1 DO LOOP x2 DO x1:=2 END END')
        with self.assertRaises(SyntaxError):
            interpret('x1:=1; x2:=2 LOOP x1 DO LOOP x2 DO x2:=2 END END')
        with self.assertRaises(SyntaxError):
            interpret('x1:=1; x2:=2 LOOP x1 DO LOOP x2 DO x0:=x2+2 END END')
        with self.assertRaises(SyntaxError):
            interpret('x1:=1; x2:=2 LOOP x1 DO LOOP x2 DO x0:=x1-2 END END')

    def test_loop_nested_empty_forbidden_identifier(self):
        with self.assertRaises(SyntaxError):
            interpret('LOOP x1 DO LOOP x2 DO x2:=2 END END')
        with self.assertRaises(SyntaxError):
            interpret('LOOP x1 DO LOOP x2 DO x0:=x2+2 END END')
        with self.assertRaises(SyntaxError):
            interpret('LOOP x1 DO LOOP x2 DO x0:=x1 + 0 END END')
        with self.assertRaises(SyntaxError):
            interpret('LOOP x1 DO LOOP x2 DO x1:=2 END END')
        with self.assertRaises(SyntaxError):
            interpret('LOOP x1 DO LOOP x1 DO x2:=2 END END')

    def test_loop_wrong_syntax(self):
        with self.assertRaises(SyntaxError):
            interpret('LOOP 2 DO x2:=5 END')
        with self.assertRaises(SyntaxError):
            interpret('x1:=1; LOOP x1 DO x2:=5; END')
        with self.assertRaises(SyntaxError):
            interpret('x1:=1; LOOP x1 DO; x2:=5 END')
        with self.assertRaises(SyntaxError):
            interpret('x1:=1; LOOP x1 DO x2:=5 END;')

    def test_assignment_with_loop(self):
        self.assertEqual(interpret('x0:=2; LOOP x0 DO x1:=x1+1 END; x0:=x1+0'), 2)
        self.assertEqual(interpret('x1:=x1+1; LOOP x0 DO x1:=x1+1 END; x0:=x1+0'), 1)

    def test_syntax_unnecessary_semicolon(self):
        with self.assertRaises(SyntaxError):
            interpret('LOOP x0 DO x1:=x1+1 END;')
        with self.assertRaises(SyntaxError):
            interpret('LOOP x0 DO x1:=x1+1;; x1:=x1+1 END')
        with self.assertRaises(SyntaxError):
            interpret('x1:=x1+1;; x1:=x1+1')
        with self.assertRaises(SyntaxError):
            interpret(';x1:=x1+1')

Chris's avatar
Chris committed
135
136
137
138
139
140
141
142
143
144
    def test_syntax_unnecessary_end(self):
        with self.assertRaises(SyntaxError):
            interpret('LOOP x0 DO x1:=x1+1 END END')
        with self.assertRaises(SyntaxError):
            interpret('LOOP x0 DO x1:=x1+1 END; x1:=x1+1 END')
        with self.assertRaises(SyntaxError):
            interpret('x1:=x1+1; END x1:=x1+1')
        with self.assertRaises(SyntaxError):
            interpret('END x1:=x1+1')

Chris's avatar
Chris committed
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
    def test_syntax_missing_semicolon(self):
        with self.assertRaises(SyntaxError):
            interpret('x0:=2 LOOP x0 DO x1:=x1+1 END')
        with self.assertRaises(SyntaxError):
            interpret('LOOP x0 DO x1:=x1+1 x1:=x1+1 END')
        with self.assertRaises(SyntaxError):
            interpret('x0:=2; LOOP x0 DO x1:=x1+1 x1:=x1+1 END')
        with self.assertRaises(SyntaxError):
            interpret('LOOP x0 DO x1:=x1+1 END x0:=x1+0')
        with self.assertRaises(SyntaxError):
            interpret('x0:=2; LOOP x0 DO x1:=x1+1 END x0:=x1+0')

    def test_syntax_missing_do(self):
        with self.assertRaises(SyntaxError):
            interpret('LOOP x1 x2:=2 END')
        with self.assertRaises(SyntaxError):
            interpret('x1:=2; LOOP x1 x2:=2 END')
        with self.assertRaises(SyntaxError):
            interpret('LOOP x0 DO LOOP x1 x2:=2 END END')
        with self.assertRaises(SyntaxError):
            interpret('x0:=1; LOOP x0 DO LOOP x1 x2:=2 END END')
        with self.assertRaises(SyntaxError):
            interpret('x0:=1; x1:=2; LOOP x0 DO LOOP x1 x2:=2 END END')

    def test_syntax_missing_end(self):
        with self.assertRaises(SyntaxError):
            interpret('LOOP x0 DO LOOP x1 DO x2:=2')
        with self.assertRaises(SyntaxError):
            interpret('x0:=5; LOOP x0 DO LOOP x1 DO x2:=2')
        with self.assertRaises(SyntaxError):
            interpret('x0:=4; x1:=7; LOOP x0 DO LOOP x1 DO x2:=2')
        with self.assertRaises(SyntaxError):
            interpret('LOOP x0 DO LOOP x1 DO x2:=2 END')
        with self.assertRaises(SyntaxError):
            interpret('x0:=2; LOOP x0 DO LOOP x1 DO x2:=2 END')
        with self.assertRaises(SyntaxError):
            interpret('x0:=2; x1:=3; LOOP x0 DO LOOP x1 DO x2:=2 END')
        with self.assertRaises(SyntaxError):
            interpret('x0 := 2; LOOP x0 DO x1 := 1; x2 := x2 + 1')
        with self.assertRaises(SyntaxError):
            interpret('LOOP x0 DO x1:=2; x2:=0')

    def test_syntax_missing_program(self):
        with self.assertRaises(SyntaxError):
            interpret('LOOP x0 DO END')
        with self.assertRaises(SyntaxError):
            interpret('x0:=2; LOOP x0 DO END')
        with self.assertRaises(SyntaxError):
            interpret('LOOP x0 DO LOOP x1 DO END END')
        with self.assertRaises(SyntaxError):
            interpret('LOOP x0 DO LOOP x1 DO x2:=2; END')

    def test_syntax_missing_operator(self):
        with self.assertRaises(SyntaxError):
            interpret('x0:=x1 2')
        with self.assertRaises(SyntaxError):
            interpret('LOOP x2 DO x0:=x1 2 END')
        with self.assertRaises(SyntaxError):
            interpret('x2:=3; LOOP x2 DO x1:=x1 2 END')
        with self.assertRaises(SyntaxError):
            interpret('LOOP x2 DO LOOP x3 DO x1:=x1 2 END END')

    def test_syntax_missing_equals(self):
        with self.assertRaises(SyntaxError):
            interpret('x1 2')
        with self.assertRaises(SyntaxError):
            interpret('x1 x2+2')
        with self.assertRaises(SyntaxError):
            interpret('LOOP x0 DO x1 2 END')
        with self.assertRaises(SyntaxError):
            interpret('x0:=2; LOOP x0 DO x1 2 END')
        with self.assertRaises(SyntaxError):
            interpret('LOOP x0 DO x1 x2+3 END')
        with self.assertRaises(SyntaxError):
            interpret('x0:=2; LOOP x0 DO x1 x2-1 END')

    def test_syntax_missing_identifier(self):
        with self.assertRaises(SyntaxError):
            interpret("x1:=; LOOP x1 DO x2:=2 END")
        with self.assertRaises(SyntaxError):
            interpret("LOOP x1 DO x2:= END")
        with self.assertRaises(SyntaxError):
            interpret("LOOP x1 DO x2:=x0+ END")
        with self.assertRaises(SyntaxError):
            interpret('LOOP x0 DO LOOP DO x1:=x2+0 END END')

    def test_newlines(self):
        self.assertEqual(interpret('''x2:=3;
        x0:=x2+2'''), 5)
        self.assertEqual(interpret('x1:=x1-2;\n x0:=x1+2'), 2)
235

Chris's avatar
Chris committed
236
    @mock.patch('loopinterpreter.input', side_effect=input_exit)
Chris's avatar
Chris committed
237
    def test_break_exit(self, custom_input):
238
239
240
        self.assertEqual(interpret('x1:=2; BREAK x0:=2'), -1)
        self.assertEqual(interpret('LOOP x1 DO BREAK x2:= 2 END'), -1)

Chris's avatar
Chris committed
241
    @mock.patch('loopinterpreter.input', side_effect=input_continue)
Chris's avatar
Chris committed
242
    def test_break_continue(self, custom_input):
243
244
245
246
247
        self.assertEqual(interpret('x1:=2; LOOP x1 DO x0:=x0+2 BREAK END'), 4)

    @mock.patch('lexer.Lexer.__init__', init_without_tokens)
    def test_unknown_tokens(self):
        with self.assertRaises(SyntaxError):
Chris's avatar
Chris committed
248
            interpret('BLIBLABLUB')