harec

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

30-reduction.c (4480B)


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