Ton
293 строки · 9.4 Кб
1import os
2import os.path
3import random
4import subprocess
5import sys
6import tempfile
7
8def getenv(name, default=None):
9if name in os.environ:
10return os.environ[name]
11if default is not None:
12return default
13print("Environemnt variable", name, "is not set", file=sys.stderr)
14exit(1)
15
16VAR_CNT = 10
17TMP_DIR = tempfile.mkdtemp()
18FUNC_EXECUTABLE = getenv("FUNC_EXECUTABLE", "func")
19FIFT_EXECUTABLE = getenv("FIFT_EXECUTABLE", "fift")
20FIFT_LIBS = getenv("FIFT_LIBS")
21MAGIC = 123456789
22
23var_idx = 0
24def gen_var_name():
25global var_idx
26var_idx += 1
27return "i%d" % var_idx
28
29class State:
30def __init__(self, x):
31self.x = x
32self.vs = [0] * VAR_CNT
33
34def copy(self):
35s = State(self.x)
36s.vs = self.vs.copy()
37return s
38
39def copy_from(self, s):
40self.x = s.x
41self.vs = s.vs.copy()
42
43class Code:
44pass
45
46class CodeEmpty(Code):
47def execute(self, state):
48return None
49
50def write(self, f, indent=0):
51pass
52
53class CodeReturn(Code):
54def __init__(self, value):
55self.value = value
56
57def execute(self, state):
58return [self.value] + state.vs
59
60def write(self, f, indent=0):
61print(" " * indent + "return (%d, %s);" % (self.value, ", ".join("v%d" % i for i in range(VAR_CNT))), file=f)
62
63class CodeAdd(Code):
64def __init__(self, i, value):
65self.i = i
66self.value = value
67
68def execute(self, state):
69state.vs[self.i] += self.value
70return None
71
72def write(self, f, indent=0):
73print(" " * indent + "v%d += %d;" % (self.i, self.value), file=f)
74
75class CodeBlock(Code):
76def __init__(self, code):
77self.code = code
78
79def execute(self, state):
80for c in self.code:
81res = c.execute(state)
82if res is not None:
83return res
84return None
85
86def write(self, f, indent=0):
87for c in self.code:
88c.write(f, indent)
89
90class CodeIfRange(Code):
91def __init__(self, l, r, c1, c2):
92self.l = l
93self.r = r
94self.c1 = c1
95self.c2 = c2
96
97def execute(self, state):
98if self.l <= state.x < self.r:
99return self.c1.execute(state)
100else:
101return self.c2.execute(state)
102
103def write(self, f, indent=0):
104print(" " * indent + "if (in(x, %d, %d)) {" % (self.l, self.r), file=f)
105self.c1.write(f, indent + 1)
106if isinstance(self.c2, CodeEmpty):
107print(" " * indent + "}", file=f)
108else:
109print(" " * indent + "} else {", file=f)
110self.c2.write(f, indent + 1)
111print(" " * indent + "}", file=f)
112
113class CodeRepeat(Code):
114def __init__(self, n, c, loop_type):
115if loop_type == 2:
116n = max(n, 1)
117self.n = n
118self.c = c
119self.loop_type = loop_type
120
121def execute(self, state):
122for _ in range(self.n):
123res = self.c.execute(state)
124if res is not None:
125return res
126return None
127
128def write(self, f, indent=0):
129if self.loop_type == 0:
130print(" " * indent + "repeat (%d) {" % self.n, file=f)
131self.c.write(f, indent + 1)
132print(" " * indent + "}", file=f)
133elif self.loop_type == 1:
134var = gen_var_name()
135print(" " * indent + "int %s = 0;" % var, file=f)
136print(" " * indent + "while (%s < %d) {" % (var, self.n), file=f)
137self.c.write(f, indent + 1)
138print(" " * (indent + 1) + "%s += 1;" % var, file=f)
139print(" " * indent + "}", file=f)
140elif self.loop_type == 2:
141var = gen_var_name()
142print(" " * indent + "int %s = 0;" % var, file=f)
143print(" " * indent + "do {", file=f)
144self.c.write(f, indent + 1)
145print(" " * (indent + 1) + "%s += 1;" % var, file=f)
146print(" " * indent + "} until (%s >= %d);" % (var, self.n), file=f)
147else:
148var = gen_var_name()
149print(" " * indent + "int %s = %d;" % (var, self.n - 1), file=f)
150print(" " * indent + "while (%s >= 0) {" % var, file=f)
151self.c.write(f, indent + 1)
152print(" " * (indent + 1) + "%s -= 1;" % var, file=f)
153print(" " * indent + "}", file=f)
154
155class CodeThrow(Code):
156def __init__(self):
157pass
158
159def execute(self, state):
160return "EXCEPTION"
161
162def write(self, f, indent=0):
163print(" " * indent + "throw(42);", file=f)
164
165class CodeTryCatch(Code):
166def __init__(self, c1, c2):
167self.c1 = c1
168self.c2 = c2
169
170def execute(self, state):
171state0 = state.copy()
172res = self.c1.execute(state)
173if res == "EXCEPTION":
174state.copy_from(state0)
175return self.c2.execute(state)
176else:
177return res
178
179def write(self, f, indent=0):
180print(" " * indent + "try {", file=f)
181self.c1.write(f, indent + 1)
182print(" " * indent + "} catch (_, _) {", file=f)
183self.c2.write(f, indent + 1)
184print(" " * indent + "}", file=f)
185
186def write_function(f, name, body, inline=False, inline_ref=False, method_id=None):
187print("_ %s(int x)" % name, file=f, end="")
188if inline:
189print(" inline", file=f, end="")
190if inline_ref:
191print(" inline_ref", file=f, end="")
192if method_id is not None:
193print(" method_id(%d)" % method_id, file=f, end="")
194print(" {", file=f)
195for i in range(VAR_CNT):
196print(" int v%d = 0;" % i, file=f)
197body.write(f, 1)
198print("}", file=f)
199
200def gen_code(xl, xr, with_return, loop_depth=0, try_catch_depth=0, can_throw=False):
201if try_catch_depth < 3 and random.randint(0, 5) == 0:
202c1 = gen_code(xl, xr, with_return, loop_depth, try_catch_depth + 1, random.randint(0, 1) == 0)
203c2 = gen_code(xl, xr, with_return, loop_depth, try_catch_depth + 1, can_throw)
204return CodeTryCatch(c1, c2)
205code = []
206for _ in range(random.randint(0, 2)):
207if random.randint(0, 3) == 0 and loop_depth < 3:
208c = gen_code(xl, xr, False, loop_depth + 1, try_catch_depth, can_throw)
209code.append(CodeRepeat(random.randint(0, 3), c, random.randint(0, 3)))
210elif xr - xl > 1:
211xmid = random.randrange(xl + 1, xr)
212ret = random.choice((0, 0, 0, 0, 0, 1, 2))
213c1 = gen_code(xl, xmid, ret == 1, loop_depth, try_catch_depth, can_throw)
214if random.randrange(5) == 0:
215c2 = CodeEmpty()
216else:
217c2 = gen_code(xmid, xr, ret == 2, loop_depth, try_catch_depth, can_throw)
218code.append(CodeIfRange(xl, xmid, c1, c2))
219if xr - xl == 1 and can_throw and random.randint(0, 5) == 0:
220code.append(CodeThrow())
221if with_return:
222if xr - xl == 1:
223code.append(CodeReturn(random.randrange(10**9)))
224else:
225xmid = random.randrange(xl + 1, xr)
226c1 = gen_code(xl, xmid, True, loop_depth, try_catch_depth, can_throw)
227c2 = gen_code(xmid, xr, True, loop_depth, try_catch_depth, can_throw)
228code.append(CodeIfRange(xl, xmid, c1, c2))
229for _ in range(random.randint(0, 3)):
230pos = random.randint(0, len(code))
231code.insert(pos, CodeAdd(random.randrange(VAR_CNT), random.randint(0, 10**6)))
232if len(code) == 0:
233return CodeEmpty()
234return CodeBlock(code)
235
236class ExecutionError(Exception):
237pass
238
239def compile_func(fc, fif):
240res = subprocess.run([FUNC_EXECUTABLE, "-o", fif, "-SPA", fc], capture_output=True)
241if res.returncode != 0:
242raise ExecutionError(str(res.stderr, "utf-8"))
243
244def runvm(compiled_fif, xl, xr):
245runner = os.path.join(TMP_DIR, "runner.fif")
246with open(runner, "w") as f:
247print("\"%s\" include <s constant code" % compiled_fif, file=f)
248for x in range(xl, xr):
249print("%d 0 code 1 runvmx abort\"exitcode is not 0\" .s cr { drop } depth 1- times" % x, file=f)
250res = subprocess.run([FIFT_EXECUTABLE, "-I", FIFT_LIBS, runner], capture_output=True)
251if res.returncode != 0:
252raise ExecutionError(str(res.stderr, "utf-8"))
253output = []
254for s in str(res.stdout, "utf-8").split("\n"):
255if s.strip() != "":
256output.append(list(map(int, s.split())))
257return output
258
259
260cnt_ok = 0
261cnt_fail = 0
262for test_id in range(0, 1000000):
263random.seed(test_id)
264inline = random.randint(0, 2)
265xr = random.randint(1, 15)
266var_idx = 0
267code = gen_code(0, xr, True)
268fc = os.path.join(TMP_DIR, "code.fc")
269fif = os.path.join(TMP_DIR, "compiled.fif")
270with open(fc, "w") as f:
271print("int in(int x, int l, int r) impure { return (l <= x) & (x < r); }", file=f)
272write_function(f, "foo", code, inline=(inline == 1), inline_ref=(inline == 2))
273print("_ main(int x) {", file=f)
274print(" (int ret, %s) = foo(x);" % ", ".join("int v%d" % i for i in range(VAR_CNT)), file=f)
275print(" return (ret, %s, %d);" % (", ".join("v%d" % i for i in range(VAR_CNT)), MAGIC), file=f)
276print("}", file=f)
277compile_func(fc, fif)
278ok = True
279try:
280output = runvm(fif, 0, xr)
281for x in range(xr):
282my_out = code.execute(State(x)) + [MAGIC]
283fc_out = output[x]
284if my_out != fc_out:
285ok = False
286break
287except ExecutionError:
288ok = False
289if ok:
290cnt_ok += 1
291else:
292cnt_fail += 1
293print("Test %-6d %-6s ok:%-6d fail:%-6d" % (test_id, "OK" if ok else "FAIL", cnt_ok, cnt_fail), file=sys.stderr)
294