30-reduction.c (4718B)
1 #include <assert.h> 2 #include <stdbool.h> 3 #include <stdio.h> 4 #include <stdlib.h> 5 #include <string.h> 6 7 #include "check.h" 8 #include "identifier.h" 9 #include "lex.h" 10 #include "parse.h" 11 #include "scope.h" 12 #include "type_store.h" 13 #include "typedef.h" 14 #include "types.h" 15 #include "util.h" 16 17 void test(struct context *ctx, const char *expected, const char *input) { 18 // The target is irrelevant here, since it's only used to compute the 19 // sizes of types, which isn't tested here or depended on. 20 builtin_types_init("x86_64"); 21 22 ctx->errors = NULL; 23 ctx->next = &ctx->errors; 24 25 sources = (const char *[2]){"<expected>", input}; 26 27 const struct type *etype = NULL; 28 if (strlen(expected) != 0) { 29 FILE *ebuf = fmemopen((char *)expected, strlen(expected), "r"); 30 struct lexer elex; 31 lex_init(&elex, ebuf, 0); 32 struct ast_type *eatype = parse_type(&elex); 33 etype = type_store_lookup_atype(ctx->store, eatype); 34 } 35 36 FILE *ibuf = fmemopen((char *)input, strlen(input), "r"); 37 struct lexer ilex; 38 lex_init(&ilex, ibuf, 1); 39 struct ast_expression *iaexpr = parse_expression(&ilex); 40 struct expression iexpr = {0}; 41 check_expression(ctx, iaexpr, &iexpr, NULL); 42 43 if (etype == NULL) { 44 assert(ctx->errors != NULL); 45 return; 46 } 47 48 struct errors *error = ctx->errors; 49 while (error) { 50 fprintf(stderr, "Error %s:%d:%d: %s\n", sources[error->loc.file], 51 error->loc.lineno, error->loc.colno, error->msg); 52 struct errors *next = error->next; 53 free(error); 54 error = next; 55 } 56 if (ctx->errors) { 57 exit(EXIT_FAILURE); 58 } 59 60 if (etype->id != iexpr.result->id) { 61 fprintf(stderr, "Expected expression %s to have type ", input); 62 emit_type(etype, stderr); 63 fprintf(stderr, ", got "); 64 emit_type(iexpr.result, stderr); 65 fprintf(stderr, "\n"); 66 exit(EXIT_FAILURE); 67 } 68 } 69 70 int main(void) { 71 struct context ctx = {0}; 72 static struct type_store ts = {0}; 73 struct modcache *modcache[MODCACHE_BUCKETS]; 74 memset(modcache, 0, sizeof(modcache)); 75 ctx.is_test = false; 76 ctx.store = &ts; 77 ctx.store->check_context = &ctx; 78 ctx.modcache = modcache; 79 ctx.unit = scope_push(&ctx.scope, SCOPE_UNIT); 80 81 test(&ctx, "(int | void)", "if (true) 0: int else void: void"); 82 test(&ctx, "(nullable *int | void)", 83 "if (true) null: *int " 84 "else if (true) null: nullable *int " 85 "else if (true) null"); 86 test(&ctx, "(nullable *int | void)", 87 "match (0u8: (u8 | u16 | u32 | u64)) { " 88 "case u8 => " 89 " yield null: *int; " 90 "case u16 => " 91 " yield null: nullable *int; " 92 "case u32 => " 93 " yield null; " 94 "case u64 => " 95 " yield;" 96 "}"); 97 test(&ctx, "(nullable *int | void)", 98 "switch (0) { " 99 "case 42 => " 100 " yield null: *int;" 101 "case 69 => " 102 " yield null: nullable *int;" 103 "case 1337 => " 104 " yield null;" 105 "case => " 106 " yield;" 107 "};"); 108 109 // if, match, and switch all use the same code for reduction, so we 110 // don't need to rigorously test all three 111 112 test(&ctx, "nullable *int", 113 "if (true) null: *int " 114 "else null"); 115 test(&ctx, "nullable *int", 116 "if (true) null: *int " 117 "else null: nullable *int"); 118 test(&ctx, "(*int | const nullable *int)", 119 "if (true) null: *int " 120 "else null: const nullable *int"); 121 test(&ctx, "const rune", 122 "if (true) 'a' " 123 "else 'a': const rune"); 124 test(&ctx, "const rune", 125 "if (true) 'a': const rune " 126 "else 'a'"); 127 test(&ctx, "(*int | const nullable *int)", 128 "if (true) null: *int " 129 "else if (true) null: const nullable *int " 130 "else null: nullable *int"); 131 test(&ctx, "(int | void)", 132 "{ " 133 "if (true) yield; " 134 "yield 0; " 135 "}"); 136 137 test(&ctx, "", 138 "if (true) null " 139 "else if (true) null: *int " 140 "else null: *void"); 141 test(&ctx, "", 142 "if (true) null " 143 "else void"); 144 145 // However, constants behave differently in if vs switch/match 146 147 test(&ctx, "int", "if (true) 0 else if (true) 1 else 2"); 148 test(&ctx, "(int | i64)", "if (true) 0 else 9223372036854775807"); 149 test(&ctx, "(int | size)", "if (true) 0 else 0z"); 150 test(&ctx, "(int | void)", "if (true) 0 else void"); 151 152 test(&ctx, "int", 153 "switch (0) { " 154 "case 0 => " 155 " yield 0; " 156 "case 1 => " 157 " yield 1; " 158 "case => " 159 " yield 2; " 160 "};"); 161 test(&ctx, "(int | i64)", 162 "switch (0) { " 163 "case 0 => " 164 " yield 0; " 165 "case => " 166 " yield 9223372036854775807; " 167 "};"); 168 test(&ctx, "(int | size)", 169 "switch (0) { " 170 "case 0 => " 171 " yield 0; " 172 "case => " 173 " yield 0z; " 174 "};"); 175 test(&ctx, "(int | void)", 176 "switch (0) { " 177 "case 0 => " 178 " yield 0; " 179 "case => " 180 " yield; " 181 "};"); 182 test(&ctx, "(int | size | u32)", 183 "switch (0) { " 184 "case 0 => " 185 " yield 0; " 186 "case 1 => " 187 " yield 1z; " 188 "case => " 189 " yield 2u32; " 190 "};"); 191 test(&ctx, "(int | i64)", 192 "switch (0) { " 193 "case 0 => " 194 " yield 0; " 195 "case 1 => " 196 " yield 1i; " 197 "case => " 198 " yield 9223372036854775807; " 199 "};"); 200 }