| author | |
| committer | |
| log | 58e375d0a1423c04f4d3faabe4d84bfc11028d56 |
| tree | 733056bbfef07b984aabaea612d1dae67f698f68 |
| parent | 29f24e3c5066e7cb28876d40a811a4a64f9d4b33 |
8 files changed, 168 insertions(+), 53 deletions(-)
README.md-1| ... | ... | @@ -43,7 +43,6 @@ make |
| 43 | 43 | ## Roadmap |
| 44 | 44 | |
| 45 | 45 | * variable declarations and assignment expressions |
| 46 | * Multiple files | |
| 47 | 46 | * Type checking |
| 48 | 47 | * inline assembly and syscalls |
| 49 | 48 | * running code at compile time |
example/multiple_files/foo.zig+1-1| ... | ... | @@ -6,6 +6,6 @@ fn private_function() { |
| 6 | 6 | puts("it works!"); |
| 7 | 7 | } |
| 8 | 8 | |
| 9 | fn print_text() { | |
| 9 | pub fn print_text() { | |
| 10 | 10 | private_function(); |
| 11 | 11 | } |
example/multiple_files/libc.zig+2-2| ... | ... | @@ -1,5 +1,5 @@ |
| 1 | 1 | #link("c") |
| 2 | 2 | extern { |
| 3 | fn puts(s: *mut u8) -> i32; | |
| 4 | fn exit(code: i32) -> unreachable; | |
| 3 | pub fn puts(s: *mut u8) -> i32; | |
| 4 | pub fn exit(code: i32) -> unreachable; | |
| 5 | 5 | } |
example/multiple_files/main.zig+1-1| ... | ... | @@ -3,7 +3,7 @@ export executable "test"; |
| 3 | 3 | use "libc.zig"; |
| 4 | 4 | use "foo.zig"; |
| 5 | 5 | |
| 6 | fn _start() -> unreachable { | |
| 6 | export fn _start() -> unreachable { | |
| 7 | 7 | private_function(); |
| 8 | 8 | } |
| 9 | 9 |
src/analyze.cpp+73-30| ... | ... | @@ -137,6 +137,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, |
| 137 | 137 | AstNode *fn_decl = node->data.extern_block.fn_decls.at(fn_decl_i); |
| 138 | 138 | assert(fn_decl->type == NodeTypeFnDecl); |
| 139 | 139 | AstNode *fn_proto = fn_decl->data.fn_decl.fn_proto; |
| 140 | bool is_pub = (fn_proto->data.fn_proto.visib_mod == FnProtoVisibModPub); | |
| 140 | 141 | resolve_function_proto(g, fn_proto); |
| 141 | 142 | Buf *name = &fn_proto->data.fn_proto.name; |
| 142 | 143 | |
| ... | ... | @@ -145,7 +146,12 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, |
| 145 | 146 | fn_table_entry->is_extern = true; |
| 146 | 147 | fn_table_entry->calling_convention = LLVMCCallConv; |
| 147 | 148 | fn_table_entry->import_entry = import; |
| 148 | g->fn_table.put(name, fn_table_entry); | |
| 149 | ||
| 150 | g->fn_protos.append(fn_table_entry); | |
| 151 | import->fn_table.put(name, fn_table_entry); | |
| 152 | if (is_pub) { | |
| 153 | g->fn_table.put(name, fn_table_entry); | |
| 154 | } | |
| 149 | 155 | } |
| 150 | 156 | break; |
| 151 | 157 | case NodeTypeFnDef: |
| ... | ... | @@ -153,27 +159,44 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, |
| 153 | 159 | AstNode *proto_node = node->data.fn_def.fn_proto; |
| 154 | 160 | assert(proto_node->type == NodeTypeFnProto); |
| 155 | 161 | Buf *proto_name = &proto_node->data.fn_proto.name; |
| 156 | auto entry = g->fn_table.maybe_get(proto_name); | |
| 162 | auto entry = import->fn_table.maybe_get(proto_name); | |
| 163 | bool skip = false; | |
| 164 | bool is_internal = (proto_node->data.fn_proto.visib_mod != FnProtoVisibModExport); | |
| 165 | bool is_pub = (proto_node->data.fn_proto.visib_mod == FnProtoVisibModPub); | |
| 157 | 166 | if (entry) { |
| 158 | 167 | add_node_error(g, node, |
| 159 | 168 | buf_sprintf("redefinition of '%s'", buf_ptr(proto_name))); |
| 160 | 169 | assert(!node->codegen_node); |
| 161 | 170 | node->codegen_node = allocate<CodeGenNode>(1); |
| 162 | 171 | node->codegen_node->data.fn_def_node.skip = true; |
| 163 | } else { | |
| 172 | skip = true; | |
| 173 | } else if (is_pub) { | |
| 174 | auto entry = g->fn_table.maybe_get(proto_name); | |
| 175 | if (entry) { | |
| 176 | add_node_error(g, node, | |
| 177 | buf_sprintf("redefinition of '%s'", buf_ptr(proto_name))); | |
| 178 | assert(!node->codegen_node); | |
| 179 | node->codegen_node = allocate<CodeGenNode>(1); | |
| 180 | node->codegen_node->data.fn_def_node.skip = true; | |
| 181 | skip = true; | |
| 182 | } | |
| 183 | } | |
| 184 | if (!skip) { | |
| 164 | 185 | FnTableEntry *fn_table_entry = allocate<FnTableEntry>(1); |
| 165 | 186 | fn_table_entry->import_entry = import; |
| 166 | 187 | fn_table_entry->proto_node = proto_node; |
| 167 | 188 | fn_table_entry->fn_def_node = node; |
| 168 | fn_table_entry->internal_linkage = proto_node->data.fn_proto.visib_mod != FnProtoVisibModExport; | |
| 169 | if (fn_table_entry->internal_linkage) { | |
| 170 | fn_table_entry->calling_convention = LLVMFastCallConv; | |
| 171 | } else { | |
| 172 | fn_table_entry->calling_convention = LLVMCCallConv; | |
| 173 | } | |
| 174 | g->fn_table.put(proto_name, fn_table_entry); | |
| 189 | fn_table_entry->internal_linkage = is_internal; | |
| 190 | fn_table_entry->calling_convention = is_internal ? LLVMFastCallConv : LLVMCCallConv; | |
| 191 | ||
| 192 | g->fn_protos.append(fn_table_entry); | |
| 175 | 193 | g->fn_defs.append(fn_table_entry); |
| 176 | 194 | |
| 195 | import->fn_table.put(proto_name, fn_table_entry); | |
| 196 | if (is_pub) { | |
| 197 | g->fn_table.put(proto_name, fn_table_entry); | |
| 198 | } | |
| 199 | ||
| 177 | 200 | resolve_function_proto(g, proto_node); |
| 178 | 201 | } |
| 179 | 202 | } |
| ... | ... | @@ -297,28 +320,31 @@ static void check_fn_def_control_flow(CodeGen *g, AstNode *node) { |
| 297 | 320 | } |
| 298 | 321 | } |
| 299 | 322 | |
| 300 | static void analyze_expression(CodeGen *g, AstNode *node) { | |
| 323 | static void analyze_expression(CodeGen *g, ImportTableEntry *import, AstNode *node) { | |
| 301 | 324 | switch (node->type) { |
| 302 | 325 | case NodeTypeBlock: |
| 303 | 326 | for (int i = 0; i < node->data.block.statements.length; i += 1) { |
| 304 | 327 | AstNode *child = node->data.block.statements.at(i); |
| 305 | analyze_expression(g, child); | |
| 328 | analyze_expression(g, import, child); | |
| 306 | 329 | } |
| 307 | 330 | break; |
| 308 | 331 | case NodeTypeReturnExpr: |
| 309 | 332 | if (node->data.return_expr.expr) { |
| 310 | analyze_expression(g, node->data.return_expr.expr); | |
| 333 | analyze_expression(g, import, node->data.return_expr.expr); | |
| 311 | 334 | } |
| 312 | 335 | break; |
| 313 | 336 | case NodeTypeBinOpExpr: |
| 314 | analyze_expression(g, node->data.bin_op_expr.op1); | |
| 315 | analyze_expression(g, node->data.bin_op_expr.op2); | |
| 337 | analyze_expression(g, import, node->data.bin_op_expr.op1); | |
| 338 | analyze_expression(g, import, node->data.bin_op_expr.op2); | |
| 316 | 339 | break; |
| 317 | 340 | case NodeTypeFnCallExpr: |
| 318 | 341 | { |
| 319 | 342 | Buf *name = hack_get_fn_call_name(g, node->data.fn_call_expr.fn_ref_expr); |
| 320 | 343 | |
| 321 | auto entry = g->fn_table.maybe_get(name); | |
| 344 | auto entry = import->fn_table.maybe_get(name); | |
| 345 | if (!entry) | |
| 346 | entry = g->fn_table.maybe_get(name); | |
| 347 | ||
| 322 | 348 | if (!entry) { |
| 323 | 349 | add_node_error(g, node, |
| 324 | 350 | buf_sprintf("undefined function: '%s'", buf_ptr(name))); |
| ... | ... | @@ -336,7 +362,7 @@ static void analyze_expression(CodeGen *g, AstNode *node) { |
| 336 | 362 | |
| 337 | 363 | for (int i = 0; i < node->data.fn_call_expr.params.length; i += 1) { |
| 338 | 364 | AstNode *child = node->data.fn_call_expr.params.at(i); |
| 339 | analyze_expression(g, child); | |
| 365 | analyze_expression(g, import, child); | |
| 340 | 366 | } |
| 341 | 367 | break; |
| 342 | 368 | } |
| ... | ... | @@ -366,7 +392,7 @@ static void analyze_expression(CodeGen *g, AstNode *node) { |
| 366 | 392 | } |
| 367 | 393 | } |
| 368 | 394 | |
| 369 | static void analyze_top_level_declaration(CodeGen *g, AstNode *node) { | |
| 395 | static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import, AstNode *node) { | |
| 370 | 396 | switch (node->type) { |
| 371 | 397 | case NodeTypeFnDef: |
| 372 | 398 | { |
| ... | ... | @@ -387,7 +413,7 @@ static void analyze_top_level_declaration(CodeGen *g, AstNode *node) { |
| 387 | 413 | } |
| 388 | 414 | |
| 389 | 415 | check_fn_def_control_flow(g, node); |
| 390 | analyze_expression(g, node->data.fn_def.body); | |
| 416 | analyze_expression(g, import, node->data.fn_def.body); | |
| 391 | 417 | } |
| 392 | 418 | break; |
| 393 | 419 | |
| ... | ... | @@ -423,33 +449,50 @@ static void analyze_top_level_declaration(CodeGen *g, AstNode *node) { |
| 423 | 449 | } |
| 424 | 450 | } |
| 425 | 451 | |
| 426 | static void analyze_root(CodeGen *g, ImportTableEntry *import, AstNode *node) { | |
| 452 | static void find_function_declarations_root(CodeGen *g, ImportTableEntry *import, AstNode *node) { | |
| 427 | 453 | assert(node->type == NodeTypeRoot); |
| 428 | 454 | |
| 429 | // find function declarations | |
| 430 | 455 | for (int i = 0; i < node->data.root.top_level_decls.length; i += 1) { |
| 431 | 456 | AstNode *child = node->data.root.top_level_decls.at(i); |
| 432 | 457 | preview_function_declarations(g, import, child); |
| 433 | 458 | } |
| 434 | 459 | |
| 460 | } | |
| 461 | ||
| 462 | static void analyze_top_level_decls_root(CodeGen *g, ImportTableEntry *import, AstNode *node) { | |
| 463 | assert(node->type == NodeTypeRoot); | |
| 464 | ||
| 435 | 465 | for (int i = 0; i < node->data.root.top_level_decls.length; i += 1) { |
| 436 | 466 | AstNode *child = node->data.root.top_level_decls.at(i); |
| 437 | analyze_top_level_declaration(g, child); | |
| 467 | analyze_top_level_declaration(g, import, child); | |
| 438 | 468 | } |
| 439 | ||
| 440 | 469 | } |
| 441 | 470 | |
| 442 | 471 | void semantic_analyze(CodeGen *g) { |
| 443 | auto it = g->import_table.entry_iterator(); | |
| 444 | for (;;) { | |
| 445 | auto *entry = it.next(); | |
| 446 | if (!entry) | |
| 447 | break; | |
| 472 | { | |
| 473 | auto it = g->import_table.entry_iterator(); | |
| 474 | for (;;) { | |
| 475 | auto *entry = it.next(); | |
| 476 | if (!entry) | |
| 477 | break; | |
| 448 | 478 | |
| 449 | ImportTableEntry *import = entry->value; | |
| 450 | analyze_root(g, import, import->root); | |
| 479 | ImportTableEntry *import = entry->value; | |
| 480 | find_function_declarations_root(g, import, import->root); | |
| 481 | } | |
| 482 | } | |
| 483 | { | |
| 484 | auto it = g->import_table.entry_iterator(); | |
| 485 | for (;;) { | |
| 486 | auto *entry = it.next(); | |
| 487 | if (!entry) | |
| 488 | break; | |
| 489 | ||
| 490 | ImportTableEntry *import = entry->value; | |
| 491 | analyze_top_level_decls_root(g, import, import->root); | |
| 492 | } | |
| 451 | 493 | } |
| 452 | 494 | |
| 495 | ||
| 453 | 496 | if (!g->root_out_name) { |
| 454 | 497 | add_node_error(g, g->root_import->root, |
| 455 | 498 | buf_sprintf("missing export declaration and output name not provided")); |
src/codegen.cpp+13-11| ... | ... | @@ -125,7 +125,13 @@ static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) { |
| 125 | 125 | |
| 126 | 126 | Buf *name = hack_get_fn_call_name(g, node->data.fn_call_expr.fn_ref_expr); |
| 127 | 127 | |
| 128 | FnTableEntry *fn_table_entry = g->fn_table.get(name); | |
| 128 | FnTableEntry *fn_table_entry; | |
| 129 | auto entry = g->cur_fn->import_entry->fn_table.maybe_get(name); | |
| 130 | if (entry) | |
| 131 | fn_table_entry = entry->value; | |
| 132 | else | |
| 133 | fn_table_entry = g->fn_table.get(name); | |
| 134 | ||
| 129 | 135 | assert(fn_table_entry->proto_node->type == NodeTypeFnProto); |
| 130 | 136 | int expected_param_count = fn_table_entry->proto_node->data.fn_proto.params.length; |
| 131 | 137 | int actual_param_count = node->data.fn_call_expr.params.length; |
| ... | ... | @@ -478,13 +484,8 @@ static void do_code_gen(CodeGen *g) { |
| 478 | 484 | |
| 479 | 485 | |
| 480 | 486 | // Generate function prototypes |
| 481 | auto it = g->fn_table.entry_iterator(); | |
| 482 | for (;;) { | |
| 483 | auto *entry = it.next(); | |
| 484 | if (!entry) | |
| 485 | break; | |
| 486 | ||
| 487 | FnTableEntry *fn_table_entry = entry->value; | |
| 487 | for (int i = 0; i < g->fn_protos.length; i += 1) { | |
| 488 | FnTableEntry *fn_table_entry = g->fn_protos.at(i); | |
| 488 | 489 | |
| 489 | 490 | AstNode *proto_node = fn_table_entry->proto_node; |
| 490 | 491 | assert(proto_node->type == NodeTypeFnProto); |
| ... | ... | @@ -547,6 +548,7 @@ static void do_code_gen(CodeGen *g) { |
| 547 | 548 | assert(codegen_node); |
| 548 | 549 | |
| 549 | 550 | FnDefNode *codegen_fn_def = &codegen_node->data.fn_def_node; |
| 551 | assert(codegen_fn_def); | |
| 550 | 552 | codegen_fn_def->params = allocate<LLVMValueRef>(LLVMCountParams(fn)); |
| 551 | 553 | LLVMGetParams(fn, codegen_fn_def->params); |
| 552 | 554 | |
| ... | ... | @@ -733,9 +735,9 @@ static ImportTableEntry *codegen_add_code(CodeGen *g, Buf *source_path, Buf *sou |
| 733 | 735 | if (!entry) { |
| 734 | 736 | Buf full_path = BUF_INIT; |
| 735 | 737 | os_path_join(g->root_source_dir, &top_level_decl->data.use.path, &full_path); |
| 736 | Buf import_code = BUF_INIT; | |
| 737 | os_fetch_file_path(&full_path, &import_code); | |
| 738 | codegen_add_code(g, &top_level_decl->data.use.path, &import_code); | |
| 738 | Buf *import_code = buf_alloc(); | |
| 739 | os_fetch_file_path(&full_path, import_code); | |
| 740 | codegen_add_code(g, &top_level_decl->data.use.path, import_code); | |
| 739 | 741 | } |
| 740 | 742 | } |
| 741 | 743 |
src/semantic_info.hpp+7| ... | ... | @@ -80,7 +80,14 @@ struct CodeGen { |
| 80 | 80 | Buf *root_source_dir; |
| 81 | 81 | Buf *root_out_name; |
| 82 | 82 | ZigList<LLVMZigDIScope *> block_scopes; |
| 83 | ||
| 84 | // The function definitions this module includes. There must be a corresponding | |
| 85 | // fn_protos entry. | |
| 83 | 86 | ZigList<FnTableEntry *> fn_defs; |
| 87 | // The function prototypes this module includes. In the case of external declarations, | |
| 88 | // there will not be a corresponding fn_defs entry. | |
| 89 | ZigList<FnTableEntry *> fn_protos; | |
| 90 | ||
| 84 | 91 | OutType out_type; |
| 85 | 92 | FnTableEntry *cur_fn; |
| 86 | 93 | bool c_stdint_used; |
test/run_tests.cpp+71-7| ... | ... | @@ -14,13 +14,13 @@ |
| 14 | 14 | |
| 15 | 15 | struct TestSourceFile { |
| 16 | 16 | const char *relative_path; |
| 17 | const char *text; | |
| 17 | const char *source_code; | |
| 18 | 18 | }; |
| 19 | 19 | |
| 20 | 20 | struct TestCase { |
| 21 | 21 | const char *case_name; |
| 22 | 22 | const char *output; |
| 23 | const char *source; | |
| 23 | ZigList<TestSourceFile> source_files; | |
| 24 | 24 | ZigList<const char *> compile_errors; |
| 25 | 25 | ZigList<const char *> compiler_args; |
| 26 | 26 | ZigList<const char *> program_args; |
| ... | ... | @@ -31,11 +31,20 @@ static const char *tmp_source_path = ".tmp_source.zig"; |
| 31 | 31 | static const char *tmp_exe_path = "./.tmp_exe"; |
| 32 | 32 | static const char *zig_exe = "./zig"; |
| 33 | 33 | |
| 34 | static void add_simple_case(const char *case_name, const char *source, const char *output) { | |
| 34 | static void add_source_file(TestCase *test_case, const char *path, const char *source) { | |
| 35 | test_case->source_files.add_one(); | |
| 36 | test_case->source_files.last().relative_path = path; | |
| 37 | test_case->source_files.last().source_code = source; | |
| 38 | } | |
| 39 | ||
| 40 | static TestCase *add_simple_case(const char *case_name, const char *source, const char *output) { | |
| 35 | 41 | TestCase *test_case = allocate<TestCase>(1); |
| 36 | 42 | test_case->case_name = case_name; |
| 37 | 43 | test_case->output = output; |
| 38 | test_case->source = source; | |
| 44 | ||
| 45 | test_case->source_files.resize(1); | |
| 46 | test_case->source_files.at(0).relative_path = tmp_source_path; | |
| 47 | test_case->source_files.at(0).source_code = source; | |
| 39 | 48 | |
| 40 | 49 | test_case->compiler_args.append("build"); |
| 41 | 50 | test_case->compiler_args.append(tmp_source_path); |
| ... | ... | @@ -52,15 +61,19 @@ static void add_simple_case(const char *case_name, const char *source, const cha |
| 52 | 61 | test_case->compiler_args.append("on"); |
| 53 | 62 | |
| 54 | 63 | test_cases.append(test_case); |
| 64 | ||
| 65 | return test_case; | |
| 55 | 66 | } |
| 56 | 67 | |
| 57 | static void add_compile_fail_case(const char *case_name, const char *source, int count, ...) { | |
| 68 | static TestCase *add_compile_fail_case(const char *case_name, const char *source, int count, ...) { | |
| 58 | 69 | va_list ap; |
| 59 | 70 | va_start(ap, count); |
| 60 | 71 | |
| 61 | 72 | TestCase *test_case = allocate<TestCase>(1); |
| 62 | 73 | test_case->case_name = case_name; |
| 63 | test_case->source = source; | |
| 74 | test_case->source_files.resize(1); | |
| 75 | test_case->source_files.at(0).relative_path = tmp_source_path; | |
| 76 | test_case->source_files.at(0).source_code = source; | |
| 64 | 77 | |
| 65 | 78 | for (int i = 0; i < count; i += 1) { |
| 66 | 79 | const char *arg = va_arg(ap, const char *); |
| ... | ... | @@ -78,6 +91,8 @@ static void add_compile_fail_case(const char *case_name, const char *source, int |
| 78 | 91 | test_cases.append(test_case); |
| 79 | 92 | |
| 80 | 93 | va_end(ap); |
| 94 | ||
| 95 | return test_case; | |
| 81 | 96 | } |
| 82 | 97 | |
| 83 | 98 | static void add_compiling_test_cases(void) { |
| ... | ... | @@ -135,6 +150,45 @@ static void add_compiling_test_cases(void) { |
| 135 | 150 | exit(0); |
| 136 | 151 | } |
| 137 | 152 | )SOURCE", "OK\n"); |
| 153 | ||
| 154 | { | |
| 155 | TestCase *tc = add_simple_case("multiple files with private function", R"SOURCE( | |
| 156 | use "libc.zig"; | |
| 157 | use "foo.zig"; | |
| 158 | ||
| 159 | export fn _start() -> unreachable { | |
| 160 | private_function(); | |
| 161 | } | |
| 162 | ||
| 163 | fn private_function() -> unreachable { | |
| 164 | print_text(); | |
| 165 | exit(0); | |
| 166 | } | |
| 167 | )SOURCE", "OK\n"); | |
| 168 | ||
| 169 | add_source_file(tc, "libc.zig", R"SOURCE( | |
| 170 | #link("c") | |
| 171 | extern { | |
| 172 | pub fn puts(s: *mut u8) -> i32; | |
| 173 | pub fn exit(code: i32) -> unreachable; | |
| 174 | } | |
| 175 | )SOURCE"); | |
| 176 | ||
| 177 | add_source_file(tc, "foo.zig", R"SOURCE( | |
| 178 | use "libc.zig"; | |
| 179 | ||
| 180 | // purposefully conflicting function with main source file | |
| 181 | // but it's private so it should be OK | |
| 182 | fn private_function() { | |
| 183 | puts("OK"); | |
| 184 | } | |
| 185 | ||
| 186 | pub fn print_text() { | |
| 187 | private_function(); | |
| 188 | } | |
| 189 | )SOURCE"); | |
| 190 | } | |
| 191 | ||
| 138 | 192 | } |
| 139 | 193 | |
| 140 | 194 | static void add_compile_failure_test_cases(void) { |
| ... | ... | @@ -207,7 +261,12 @@ static void print_compiler_invokation(TestCase *test_case, Buf *zig_stderr) { |
| 207 | 261 | } |
| 208 | 262 | |
| 209 | 263 | static void run_test(TestCase *test_case) { |
| 210 | os_write_file(buf_create_from_str(tmp_source_path), buf_create_from_str(test_case->source)); | |
| 264 | for (int i = 0; i < test_case->source_files.length; i += 1) { | |
| 265 | TestSourceFile *test_source = &test_case->source_files.at(i); | |
| 266 | os_write_file( | |
| 267 | buf_create_from_str(test_source->relative_path), | |
| 268 | buf_create_from_str(test_source->source_code)); | |
| 269 | } | |
| 211 | 270 | |
| 212 | 271 | Buf zig_stderr = BUF_INIT; |
| 213 | 272 | Buf zig_stdout = BUF_INIT; |
| ... | ... | @@ -265,6 +324,11 @@ static void run_test(TestCase *test_case) { |
| 265 | 324 | printf("=======================================\n"); |
| 266 | 325 | exit(1); |
| 267 | 326 | } |
| 327 | ||
| 328 | for (int i = 0; i < test_case->source_files.length; i += 1) { | |
| 329 | TestSourceFile *test_source = &test_case->source_files.at(i); | |
| 330 | remove(test_source->relative_path); | |
| 331 | } | |
| 268 | 332 | } |
| 269 | 333 | |
| 270 | 334 | static void run_all_tests(void) { |