harec

[hare] Hare compiler, written in C11 for POSIX OSs
Log | Files | Refs | README | LICENSE

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 }