commit f9c18212c8450372c3dfb9e413f318d4b8a85fc2
parent 71b4c6d7b1dafcf229573a6662558c10c64dff4f
Author: Drew DeVault <sir@cmpwn.com>
Date: Sat, 16 Jan 2021 17:41:12 -0500
check: (partially) implement switch expressions
Diffstat:
2 files changed, 82 insertions(+), 1 deletion(-)
diff --git a/include/expr.h b/include/expr.h
@@ -191,6 +191,22 @@ struct expression_slice {
struct expression *start, *end;
};
+struct case_option {
+ struct expression *value;
+ struct case_option *next;
+};
+
+struct switch_case {
+ struct case_option *options; // NULL for *
+ struct expression *value;
+ struct switch_case *next;
+};
+
+struct expression_switch {
+ struct expression *value;
+ struct switch_case *cases;
+};
+
struct expression_struct {
const struct struct_field *field;
struct expression *value;
@@ -230,6 +246,7 @@ struct expression {
struct expression_list list;
struct expression_measure measure;
struct expression_return _return;
+ struct expression_switch _switch;
struct expression_struct _struct;
struct expression_slice slice;
struct expression_unarithm unarithm;
diff --git a/src/check.c b/src/check.c
@@ -922,6 +922,69 @@ check_expr_struct(struct context *ctx,
}
static void
+check_expr_switch(struct context *ctx,
+ const struct ast_expression *aexpr,
+ struct expression *expr)
+{
+ trenter(TR_CHECK, "switch");
+ expr->type = EXPR_SWITCH;
+
+ struct expression *value = xcalloc(1, sizeof(struct expression));
+ check_expression(ctx, aexpr->_switch.value, value);
+ const struct type *type = value->result;
+ expr->_switch.value = value;
+
+ // TODO: Test for dupes, exhaustiveness
+ struct switch_case **next = &expr->_switch.cases, *_case = NULL;
+ for (struct ast_switch_case *acase = aexpr->_switch.cases;
+ acase; acase = acase->next) {
+ _case = *next = xcalloc(1, sizeof(struct switch_case));
+ next = &_case->next;
+
+ struct case_option *opt, **next_opt = &_case->options;
+ for (struct ast_case_option *aopt = acase->options;
+ aopt; aopt = aopt->next) {
+ opt = *next_opt = xcalloc(1, sizeof(struct case_option));
+ struct expression *value =
+ xcalloc(1, sizeof(struct expression));
+ struct expression *evaled =
+ xcalloc(1, sizeof(struct expression));
+
+ check_expression(ctx, aopt->value, value);
+ // XXX: Should this be assignable instead?
+ expect(&aopt->value->loc,
+ type == value->result,
+ "Invalid type for switch case");
+
+ enum eval_result r = eval_expr(ctx, value, evaled);
+ expect(&aopt->value->loc,
+ r == EVAL_OK,
+ "Unable to evaluate case at compile time");
+
+ opt->value = evaled;
+ next_opt = &opt->next;
+ }
+
+ _case->value = xcalloc(1, sizeof(struct expression));
+ check_expression(ctx, acase->value, _case->value);
+ if (expr->terminates) {
+ continue;
+ }
+
+ if (expr->result == NULL) {
+ expr->result = _case->value->result;
+ } else if (expr->result != _case->value->result) {
+ assert(0); // TODO: Form tagged union
+ }
+ }
+
+ if (expr->result == NULL) {
+ expr->result = &builtin_type_void;
+ expr->terminates = true;
+ }
+}
+
+static void
check_expr_unarithm(struct context *ctx,
const struct ast_expression *aexpr,
struct expression *expr)
@@ -1038,7 +1101,8 @@ check_expression(struct context *ctx,
check_expr_struct(ctx, aexpr, expr);
break;
case EXPR_SWITCH:
- assert(0); // TODO
+ check_expr_switch(ctx, aexpr, expr);
+ break;
case EXPR_UNARITHM:
check_expr_unarithm(ctx, aexpr, expr);
break;