harec

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit db0bfa1523aea9f3727d5bd3e8eb11b9395befc3
parent 29cd66e401eba7732a017c79bdc966f5a17a630f
Author: Drew DeVault <sir@cmpwn.com>
Date:   Mon,  9 Aug 2021 10:57:39 +0200

gen: implement switch

Signed-off-by: Drew DeVault <sir@cmpwn.com>

Diffstat:
Msrc/gen.c | 92+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Atests/914-switch.ha | 48++++++++++++++++++++++++++++++++++++++++++++++++
Mtests/configure | 3++-
3 files changed, 135 insertions(+), 8 deletions(-)

diff --git a/src/gen.c b/src/gen.c @@ -1115,7 +1115,7 @@ enum match_compat { static void gen_nested_match_tests(struct gen_context *ctx, struct gen_value object, struct qbe_value bmatch, struct qbe_value bnext, - struct qbe_value tag, struct match_case *_case, + struct qbe_value tag, const struct match_case *_case, struct qbe_value *offset) { // This function handles the case where we're matching against a type @@ -1171,7 +1171,7 @@ gen_nested_match_tests(struct gen_context *ctx, struct gen_value object, static void gen_subset_match_tests(struct gen_context *ctx, struct qbe_value bmatch, struct qbe_value bnext, - struct qbe_value tag, struct match_case *_case) + struct qbe_value tag, const struct match_case *_case) { // In this case, we're testing a case which is itself a tagged union, // and is a subset of the match object. @@ -1221,8 +1221,8 @@ gen_match_with_tagged(struct gen_context *ctx, struct qbe_value bout = mklabel(ctx, &lout, ".%d"); struct gen_value bval; - struct match_case *_default = NULL; - for (struct match_case *_case = expr->match.cases; + const struct match_case *_default = NULL; + for (const struct match_case *_case = expr->match.cases; _case; _case = _case->next) { if (!_case->type) { _default = _case; @@ -1332,8 +1332,8 @@ gen_match_with_nullable(struct gen_context *ctx, struct qbe_value qobject = mkqval(ctx, &object); struct gen_value bval; - struct match_case *_default = NULL; - for (struct match_case *_case = expr->match.cases; + const struct match_case *_default = NULL; + for (const struct match_case *_case = expr->match.cases; _case; _case = _case->next) { if (!_case->type) { _default = _case; @@ -1497,6 +1497,81 @@ gen_expr_struct_at(struct gen_context *ctx, } } +static struct gen_value +gen_expr_switch_with(struct gen_context *ctx, + const struct expression *expr, + struct gen_value *out) +{ + struct gen_value gvout = gv_void; + if (!out) { + gvout = mktemp(ctx, expr->result, ".%d"); + } + + struct qbe_statement lout; + struct qbe_value bout = mklabel(ctx, &lout, ".%d"); + struct gen_value value = gen_expr(ctx, expr->_switch.value); + + struct gen_value bval; + const struct switch_case *_default = NULL; + for (const struct switch_case *_case = expr->_switch.cases; + _case; _case = _case->next) { + if (!_case->options) { + _default = _case; + continue; + } + + struct qbe_statement lmatch, lnextcase; + struct qbe_value bmatch = mklabel(ctx, &lmatch, "matches.%d"); + struct qbe_value bnextcase = mklabel(ctx, &lnextcase, "next.%d"); + + for (struct case_option *opt = _case->options; + opt; opt = opt->next) { + struct qbe_statement lnextopt; + struct qbe_value bnextopt = mklabel(ctx, &lnextopt, ".%d"); + struct gen_value test = gen_expr_const(ctx, opt->value); + struct expression lvalue = { + .type = EXPR_GEN_VALUE, + .result = value.type, + .user = &value, + }, rvalue = { + .type = EXPR_GEN_VALUE, + .result = test.type, + .user = &test, + }, compare = { + .type = EXPR_BINARITHM, + .result = &builtin_type_bool, + .binarithm = { + .op = BIN_LEQUAL, + .lvalue = &lvalue, + .rvalue = &rvalue, + }, + }; + struct gen_value match = gen_expr(ctx, &compare); + struct qbe_value cond = mkqval(ctx, &match); + pushi(ctx->current, NULL, Q_JNZ, + &cond, &bmatch, &bnextopt, NULL); + push(&ctx->current->body, &lnextopt); + } + + pushi(ctx->current, NULL, Q_JMP, &bnextcase, NULL); + push(&ctx->current->body, &lmatch); + bval = gen_expr_with(ctx, _case->value, out); + branch_copyresult(ctx, bval, gvout, out); + if (!_case->value->terminates) { + pushi(ctx->current, NULL, Q_JMP, &bout, NULL); + } + push(&ctx->current->body, &lnextcase); + } + + if (_default) { + bval = gen_expr_with(ctx, _default->value, out); + branch_copyresult(ctx, bval, gvout, out); + } + + push(&ctx->current->body, &lout); + return gvout; +} + static void gen_expr_slice_at(struct gen_context *ctx, const struct expression *expr, @@ -1710,7 +1785,7 @@ gen_expr(struct gen_context *ctx, const struct expression *expr) case EXPR_RETURN: return gen_expr_return(ctx, expr); case EXPR_SWITCH: - assert(0); // TODO + return gen_expr_switch_with(ctx, expr, NULL); case EXPR_UNARITHM: return gen_expr_unarithm(ctx, expr); case EXPR_SLICE: @@ -1760,6 +1835,9 @@ gen_expr_at(struct gen_context *ctx, case EXPR_STRUCT: gen_expr_struct_at(ctx, expr, out); return; + case EXPR_SWITCH: + gen_expr_switch_with(ctx, expr, &out); + return; case EXPR_TUPLE: gen_expr_tuple_at(ctx, expr, out); return; diff --git a/tests/914-switch.ha b/tests/914-switch.ha @@ -0,0 +1,48 @@ +fn basics() void = { + let cases = [[0, 1], [1, 3], [10, 20], [11, 21], [12, 22], [13, 13]]; + for (let i = 0z; i < len(cases); i += 1) { + let x = cases[i][0]; + let y: int = switch (x) { + 0 => x + 1, + 1 => x + 2, + 10, 11, 12 => x + 10, + * => { + x; + }, + }; + assert(y == cases[i][1]); + }; +}; + +fn termination() void = { + let x = 42; + let y: int = switch (x) { + 42 => 1337, + 24 => abort(), + * => abort(), + }; + assert(y == 1337); +}; + +fn tagged_result() void = { + let x = 42; + let y: (int | uint) = switch (x) { + 42 => 1337i, + * => 1337u, + }; + assert(y is int); + + x = 24; + y = switch (x) { + 42 => 1337i, + * => 1337u, + }; + assert(y is uint); +}; + +export fn main() int = { + basics(); + termination(); + tagged_result(); + return 0; +}; diff --git a/tests/configure b/tests/configure @@ -17,7 +17,8 @@ tests() { 910-tagged \ 911-slices \ 912-enums \ - 913-match + 913-match \ + 914-switch do cat <<EOF tests/$t: harec tests/$t.ha tests/rt.o