this repo has no description
at trunk 158 lines 5.8 kB view raw
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()