commit fe5d4a17319e099c61f0d729f2983fc03feab314
parent f9c18212c8450372c3dfb9e413f318d4b8a85fc2
Author: Drew DeVault <sir@cmpwn.com>
Date: Sun, 17 Jan 2021 10:00:36 -0500
gen: implement switch expressions
Diffstat:
M | src/gen.c | | | 69 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- |
M | src/parse.c | | | 4 | +++- |
2 files changed, 71 insertions(+), 2 deletions(-)
diff --git a/src/gen.c b/src/gen.c
@@ -1384,6 +1384,72 @@ gen_expr_struct(struct gen_context *ctx,
}
static void
+gen_expr_switch(struct gen_context *ctx,
+ const struct expression *expr,
+ const struct qbe_value *out)
+{
+ struct qbe_value sval = {0};
+ gen_temp(ctx, &sval,
+ qtype_for_type(ctx, expr->_switch.value->result, false),
+ "switch.%d");
+ gen_expression(ctx, expr->_switch.value, &sval);
+
+ struct qbe_value match = {0}, temp = {0};
+ gen_temp(ctx, &match,
+ qtype_for_type(ctx, expr->_switch.value->result, false),
+ "value.%d");
+ gen_temp(ctx, &temp, &qbe_word, "temp.%d");
+ struct qbe_statement olabel = {0};
+ struct qbe_value obranch = {0};
+ obranch.kind = QV_LABEL;
+ obranch.name = strdup(genl(&olabel, &ctx->id, "out.%d"));
+
+ struct switch_case *_default = NULL;
+ for (struct switch_case *_case = expr->_switch.cases;
+ _case; _case = _case->next) {
+ if (!_case->options) {
+ _default = _case;
+ continue;
+ }
+
+ struct qbe_statement tlabel = {0}, flabel = {0};
+ struct qbe_value tbranch = {0}, fbranch = {0};
+ tbranch.kind = QV_LABEL;
+ tbranch.name = strdup(genl(&tlabel, &ctx->id, "match.%d"));
+ fbranch.kind = QV_LABEL;
+ fbranch.name = strdup(genl(&flabel, &ctx->id, "next.case.%d"));
+
+ for (struct case_option *opt = _case->options;
+ opt; opt = opt->next) {
+ struct qbe_statement nlabel = {0};
+ struct qbe_value nbranch = {0};
+ nbranch.kind = QV_LABEL;
+ nbranch.name = strdup(genl(&nlabel, &ctx->id, "next.opt.%d"));
+
+ gen_expr_constant(ctx, opt->value, &match);
+ pushi(ctx->current, &temp, binarithm_for_op(
+ BIN_LEQUAL, sval.type, sval.type->is_signed),
+ &match, &sval, NULL);
+ pushi(ctx->current, NULL, Q_JNZ,
+ &temp, &tbranch, &nbranch, NULL);
+ push(&ctx->current->body, &nlabel);
+ }
+
+ pushi(ctx->current, NULL, Q_JMP, &fbranch, NULL);
+ push(&ctx->current->body, &tlabel);
+ gen_expression(ctx, _case->value, out);
+ pushi(ctx->current, NULL, Q_JMP, &obranch, NULL);
+ push(&ctx->current->body, &flabel);
+ }
+
+ if (_default) {
+ gen_expression(ctx, _default->value, out);
+ }
+
+ push(&ctx->current->body, &olabel);
+}
+
+static void
gen_expr_address(struct gen_context *ctx,
const struct expression *expr,
const struct qbe_value *out)
@@ -1503,7 +1569,8 @@ gen_expression(struct gen_context *ctx,
gen_expr_struct(ctx, expr, out);
break;
case EXPR_SWITCH:
- assert(0); // TODO
+ gen_expr_switch(ctx, expr, out);
+ break;
case EXPR_UNARITHM:
gen_expr_unarithm(ctx, expr, out);
break;
diff --git a/src/parse.c b/src/parse.c
@@ -1628,10 +1628,12 @@ parse_case_options(struct lexer *lexer)
break;
default:
unlex(lexer, &tok);
+ break;
+ }
+ if (more) {
opt = xcalloc(1, sizeof(struct ast_case_option));
*next = opt;
next = &opt->next;
- break;
}
}