-
Notifications
You must be signed in to change notification settings - Fork 10
/
compiler_tests.py
152 lines (113 loc) · 5.44 KB
/
compiler_tests.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
import os
import unittest
import subprocess
from scrapscript import env_get_split, discover_cflags, parse, tokenize
from compiler import compile_to_string
def compile_to_binary(source: str, memory: int, debug: bool) -> str:
import shlex
import subprocess
import sysconfig
import tempfile
cc = env_get_split("CC", shlex.split(sysconfig.get_config_var("CC")))
cflags = discover_cflags(cc, debug)
cflags += [f"-DMEMORY_SIZE={memory}"]
program = parse(tokenize(source))
c_code = compile_to_string(program, debug)
with tempfile.NamedTemporaryFile(mode="w", suffix=".c", delete=False) as c_file:
c_file.write(c_code)
# The platform is in the same directory as this file
dirname = os.path.dirname(__file__)
with open(os.path.join(dirname, "cli.c"), "r") as f:
c_file.write(f.read())
with tempfile.NamedTemporaryFile(mode="w", suffix=".out", delete=False) as out_file:
subprocess.run([*cc, *cflags, "-o", out_file.name, c_file.name], check=True)
return out_file.name
class CompilerEndToEndTests(unittest.TestCase):
def _run(self, code: str) -> str:
use_valgrind = bool(os.environ.get("USE_VALGRIND", False))
binary = compile_to_binary(code, memory=4096, debug=True)
if use_valgrind:
cmd = ["valgrind", binary]
else:
cmd = [binary]
result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, check=True)
return result.stdout
def test_int(self) -> None:
self.assertEqual(self._run("1"), "1\n")
def test_small_string(self) -> None:
self.assertEqual(self._run('"hello"'), '"hello"\n')
def test_small_string_concat(self) -> None:
self.assertEqual(self._run('"abc" ++ "def"'), '"abcdef"\n')
def test_heap_string(self) -> None:
self.assertEqual(self._run('"hello world"'), '"hello world"\n')
def test_const_list(self) -> None:
self.assertEqual(
self._run("""[1, "2", [3, 4], {a=1}, #foo ()]"""),
"""[1, "2", [3, 4], {a = 1}, #foo ()]\n""",
)
def test_add(self) -> None:
self.assertEqual(self._run("1 + 2"), "3\n")
def test_sub(self) -> None:
self.assertEqual(self._run("1 - 2"), "-1\n")
def test_mul(self) -> None:
self.assertEqual(self._run("2 * 3"), "6\n")
def test_list(self) -> None:
self.assertEqual(self._run("[1, 2, 3]"), "[1, 2, 3]\n")
def test_var(self) -> None:
self.assertEqual(self._run("a . a = 1"), "1\n")
def test_record(self) -> None:
self.assertEqual(self._run("{a = 1, b = 2}"), "{a = 1, b = 2}\n")
def test_record_builder(self) -> None:
self.assertEqual(self._run("f 1 2 . f = x -> y -> {a = x, b = y}"), "{a = 1, b = 2}\n")
def test_record_access(self) -> None:
self.assertEqual(self._run("rec@a . rec = {a = 1, b = 2}"), "1\n")
def test_record_builder_access(self) -> None:
self.assertEqual(self._run("(f 1 2)@a . f = x -> y -> {a = x, b = y}"), "1\n")
def test_hole(self) -> None:
self.assertEqual(self._run("()"), "()\n")
def test_variant(self) -> None:
self.assertEqual(self._run("# foo 123"), "#foo 123\n")
def test_variant_builder(self) -> None:
self.assertEqual(self._run("f 123 . f = x -> # foo x"), "#foo 123\n")
def test_function(self) -> None:
self.assertEqual(self._run("f 1 . f = x -> x + 1"), "2\n")
def test_anonymous_function_as_value(self) -> None:
self.assertEqual(self._run("x -> x"), "<closure>\n")
def test_anonymous_function(self) -> None:
self.assertEqual(self._run("((x -> x + 1) 1)"), "2\n")
def test_match_int(self) -> None:
self.assertEqual(self._run("f 3 . f = | 1 -> 2 | 3 -> 4"), "4\n")
def test_match_list(self) -> None:
self.assertEqual(self._run("f [4, 5] . f = | [1, 2] -> 3 | [4, 5] -> 6"), "6\n")
def test_match_list_spread(self) -> None:
self.assertEqual(self._run("f [4, 5] . f = | [_, ...xs] -> xs"), "[5]\n")
def test_match_record(self) -> None:
self.assertEqual(self._run("f {a = 4, b = 5} . f = | {a = 1, b = 2} -> 3 | {a = 4, b = 5} -> 6"), "6\n")
def test_match_record_too_few_keys(self) -> None:
self.assertEqual(self._run("f {a = 4, b = 5} . f = | {a = _} -> 3 | {a = _, b = _} -> 6"), "6\n")
def test_match_record_spread(self) -> None:
self.assertEqual(self._run("f {a=1, b=2, c=3} . f = | {a=a, ...} -> a"), "1\n")
@unittest.skip("TODO")
def test_match_record_spread_named(self) -> None:
self.assertEqual(self._run("f {a=1, b=2, c=3} . f = | {a=1, ...rest} -> rest"), "[5]\n")
def test_match_hole(self) -> None:
self.assertEqual(self._run("f () . f = | 1 -> 3 | () -> 4"), "4\n")
def test_match_immediate_variant(self) -> None:
self.assertEqual(self._run("f #foo () . f = | # bar 1 -> 3 | # foo () -> 4"), "4\n")
def test_match_heap_variant(self) -> None:
self.assertEqual(self._run("f #bar 1 . f = | # bar 1 -> 3 | # foo () -> 4"), "3\n")
@unittest.skipIf("STATIC_HEAP" in os.environ.get("CFLAGS", ""), "Can't grow heap in static heap mode")
def test_heap_growth(self) -> None:
self.assertEqual(
self._run(
"""
countdown 1000
. countdown =
| 0 -> []
| n -> n >+ countdown (n - 1)
"""
),
"[" + ", ".join(str(i) for i in range(1000, 0, -1)) + "]\n",
)
if __name__ == "__main__":
unittest.main()