this repo has no description
1import os
2import unittest
3import subprocess
4
5from scrapscript import env_get_split, discover_cflags, parse, tokenize
6from compiler import compile_to_string
7
8
9def compile_to_binary(source: str, memory: int, debug: bool) -> str:
10 import shlex
11 import subprocess
12 import sysconfig
13 import tempfile
14
15 cc = env_get_split("CC", shlex.split(sysconfig.get_config_var("CC")))
16 cflags = discover_cflags(cc, debug)
17 cflags += [f"-DMEMORY_SIZE={memory}"]
18 program = parse(tokenize(source))
19 c_code = compile_to_string(program, debug)
20 with tempfile.NamedTemporaryFile(mode="w", suffix=".c", delete=False) as c_file:
21 c_file.write(c_code)
22 # The platform is in the same directory as this file
23 dirname = os.path.dirname(__file__)
24 with open(os.path.join(dirname, "cli.c"), "r") as f:
25 c_file.write(f.read())
26 with tempfile.NamedTemporaryFile(mode="w", suffix=".out", delete=False) as out_file:
27 subprocess.run([*cc, *cflags, "-o", out_file.name, c_file.name], check=True)
28 return out_file.name
29
30
31class CompilerEndToEndTests(unittest.TestCase):
32 def _run(self, code: str) -> str:
33 use_valgrind = bool(os.environ.get("USE_VALGRIND", False))
34 binary = compile_to_binary(code, memory=4096, debug=True)
35 if use_valgrind:
36 cmd = ["valgrind", binary]
37 else:
38 cmd = [binary]
39 result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, check=True)
40 return result.stdout
41
42 def test_int(self) -> None:
43 self.assertEqual(self._run("1"), "1\n")
44
45 def test_small_string(self) -> None:
46 self.assertEqual(self._run('"hello"'), '"hello"\n')
47
48 def test_small_string_concat(self) -> None:
49 self.assertEqual(self._run('"abc" ++ "def"'), '"abcdef"\n')
50
51 def test_const_large_string(self) -> None:
52 self.assertEqual(self._run('"hello world"'), '"hello world"\n')
53
54 def test_heap_string_concat(self) -> None:
55 self.assertEqual(self._run('"hello world" ++ " and goodbye"'), '"hello world and goodbye"\n')
56
57 def test_const_list(self) -> None:
58 self.assertEqual(
59 self._run("""[1, "2", [3, 4], {a=1}, #foo ()]"""),
60 """[1, "2", [3, 4], {a = 1}, #foo ()]\n""",
61 )
62
63 def test_add(self) -> None:
64 self.assertEqual(self._run("1 + 2"), "3\n")
65
66 def test_sub(self) -> None:
67 self.assertEqual(self._run("1 - 2"), "-1\n")
68
69 def test_mul(self) -> None:
70 self.assertEqual(self._run("2 * 3"), "6\n")
71
72 def test_list(self) -> None:
73 self.assertEqual(self._run("[1, 2, 3]"), "[1, 2, 3]\n")
74
75 def test_list_concat(self) -> None:
76 self.assertEqual(self._run("0 >+ [1, 2, 3]"), "[0, 1, 2, 3]\n")
77
78 def test_var(self) -> None:
79 self.assertEqual(self._run("a . a = 1"), "1\n")
80
81 def test_record(self) -> None:
82 self.assertEqual(self._run("{a = 1, b = 2}"), "{a = 1, b = 2}\n")
83
84 def test_record_builder(self) -> None:
85 self.assertEqual(self._run("f 1 2 . f = x -> y -> {a = x, b = y}"), "{a = 1, b = 2}\n")
86
87 def test_record_access(self) -> None:
88 self.assertEqual(self._run("rec@a . rec = {a = 1, b = 2}"), "1\n")
89
90 def test_record_builder_access(self) -> None:
91 self.assertEqual(self._run("(f 1 2)@a . f = x -> y -> {a = x, b = y}"), "1\n")
92
93 def test_hole(self) -> None:
94 self.assertEqual(self._run("()"), "()\n")
95
96 def test_variant(self) -> None:
97 self.assertEqual(self._run("# foo 123"), "#foo 123\n")
98
99 def test_variant_builder(self) -> None:
100 self.assertEqual(self._run("f 123 . f = x -> # foo x"), "#foo 123\n")
101
102 def test_function(self) -> None:
103 self.assertEqual(self._run("f 1 . f = x -> x + 1"), "2\n")
104
105 def test_anonymous_function_as_value(self) -> None:
106 self.assertEqual(self._run("x -> x"), "<closure>\n")
107
108 def test_anonymous_function(self) -> None:
109 self.assertEqual(self._run("((x -> x + 1) 1)"), "2\n")
110
111 def test_match_int(self) -> None:
112 self.assertEqual(self._run("f 3 . f = | 1 -> 2 | 3 -> 4"), "4\n")
113
114 def test_match_list(self) -> None:
115 self.assertEqual(self._run("f [4, 5] . f = | [1, 2] -> 3 | [4, 5] -> 6"), "6\n")
116
117 def test_match_list_spread(self) -> None:
118 self.assertEqual(self._run("f [4, 5] . f = | [_, ...xs] -> xs"), "[5]\n")
119
120 def test_match_record(self) -> None:
121 self.assertEqual(self._run("f {a = 4, b = 5} . f = | {a = 1, b = 2} -> 3 | {a = 4, b = 5} -> 6"), "6\n")
122
123 def test_match_record_too_few_keys(self) -> None:
124 self.assertEqual(self._run("f {a = 4, b = 5} . f = | {a = _} -> 3 | {a = _, b = _} -> 6"), "6\n")
125
126 def test_match_record_spread(self) -> None:
127 self.assertEqual(self._run("f {a=1, b=2, c=3} . f = | {a=a, ...} -> a"), "1\n")
128
129 @unittest.skip("TODO")
130 def test_match_record_spread_named(self) -> None:
131 self.assertEqual(self._run("f {a=1, b=2, c=3} . f = | {a=1, ...rest} -> rest"), "[5]\n")
132
133 def test_match_hole(self) -> None:
134 self.assertEqual(self._run("f () . f = | 1 -> 3 | () -> 4"), "4\n")
135
136 def test_match_immediate_variant(self) -> None:
137 self.assertEqual(self._run("f #foo () . f = | # bar 1 -> 3 | # foo () -> 4"), "4\n")
138
139 def test_match_heap_variant(self) -> None:
140 self.assertEqual(self._run("f #bar 1 . f = | # bar 1 -> 3 | # foo () -> 4"), "3\n")
141
142 @unittest.skipIf("STATIC_HEAP" in os.environ.get("CFLAGS", ""), "Can't grow heap in static heap mode")
143 def test_heap_growth(self) -> None:
144 self.assertEqual(
145 self._run(
146 """
147countdown 1000
148. countdown =
149| 0 -> []
150| n -> n >+ countdown (n - 1)
151"""
152 ),
153 "[" + ", ".join(str(i) for i in range(1000, 0, -1)) + "]\n",
154 )
155
156
157if __name__ == "__main__":
158 unittest.main()