harec

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

commit d73557f1362ed831964af0e3ee3a6d92f2f5ad29
parent 222d6e74557a996834397b82c3e453a883c6c230
Author: Drew DeVault <sir@cmpwn.com>
Date:   Sat, 23 Jan 2021 14:43:00 -0500

gen: implement match expressions

Diffstat:
Minclude/expr.h | 4++--
Msrc/check.c | 18++++++++++++++++--
Msrc/gen.c | 94++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/parse.c | 4++--
4 files changed, 113 insertions(+), 7 deletions(-)

diff --git a/include/expr.h b/include/expr.h @@ -196,8 +196,8 @@ struct expression_list { }; struct match_case { - const struct scope_object *object; // May be NULL - const struct type *type; + const struct scope_object *object; // NULL if not bound + const struct type *type; // NULL if default struct expression *value; struct match_case *next; }; diff --git a/src/check.c b/src/check.c @@ -889,8 +889,6 @@ check_expr_match(struct context *ctx, const struct type *ctype = type_store_lookup_atype( ctx->store, acase->type); - struct expression *value = xcalloc(1, sizeof(struct expression)); - check_expression(ctx, acase->value, value, NULL); // TODO: Figure out alias semantics properly if (is_ptr) { @@ -930,8 +928,24 @@ check_expr_match(struct context *ctx, } } + if (acase->name) { + struct identifier ident = { + .name = acase->name, + }; + struct scope *scope = scope_push(&ctx->scope, TR_CHECK); + scope->type = EXPR_MATCH; + _case->object = scope_insert(scope, O_BIND, + &ident, &ident, ctype, NULL); + } + _case->value = xcalloc(1, sizeof(struct expression)); + _case->type = ctype; check_expression(ctx, acase->value, _case->value, type); + + if (acase->name) { + scope_pop(&ctx->scope, TR_CHECK); + } + if (_case->value->terminates) { continue; } diff --git a/src/gen.c b/src/gen.c @@ -1388,6 +1388,97 @@ gen_expr_list(struct gen_context *ctx, } static void +gen_expr_match(struct gen_context *ctx, + const struct expression *expr, + const struct qbe_value *out) +{ + // TODO: Pointers + assert(expr->match.value->result->storage == TYPE_STORAGE_TAGGED_UNION); + + const struct type *mtype = expr->match.value->result; + struct qbe_value mval = {0}, tag = {0}, match = {0}, temp = {0}; + gen_temp(ctx, &mval, qtype_for_type(ctx, mtype, false), "match.%d"); + gen_temp(ctx, &temp, &qbe_word, "temp.%d"); + qval_address(&mval); + gen_expression(ctx, expr->match.value, &mval); + + gen_temp(ctx, &tag, &qbe_word, "tag.%d"); + pushi(ctx->current, &tag, Q_LOADUW, &mval, NULL); + + struct qbe_statement olabel = {0}; + struct qbe_value obranch = {0}; + obranch.kind = QV_LABEL; + obranch.name = strdup(genl(&olabel, &ctx->id, "out.%d")); + + struct match_case *_default = NULL; + for (struct match_case *_case = expr->match.cases; + _case; _case = _case->next) { + if (!_case->type) { + _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")); + + const struct type_tagged_union *tu; + struct type_tagged_union synthetic = {0}; + if (_case->type->storage != TYPE_STORAGE_TAGGED_UNION) { + synthetic.type = _case->type; + tu = &synthetic; + } else { + tu = &_case->type->tagged; + } + + for (; tu; tu = tu->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")); + + constw(&match, tu->type->id); + pushi(ctx->current, &temp, Q_CEQW, &match, &tag, 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); + + if (_case->object) { + struct qbe_value val = {0}, temp = {0}; + gen_temp(ctx, &val, + qtype_for_type(ctx, _case->type, false), + "bound.%d"); + struct gen_binding *binding = + xcalloc(1, sizeof(struct gen_binding)); + binding->name = strdup(val.name); + binding->object = _case->object; + binding->next = ctx->bindings; + ctx->bindings = binding; + constl(&temp, mtype->align); + val.type = &qbe_long; + pushi(ctx->current, &val, Q_ADD, &mval, &temp, NULL); + } + + 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_measure(struct gen_context *ctx, const struct expression *expr, const struct qbe_value *out) @@ -1722,7 +1813,8 @@ gen_expression(struct gen_context *ctx, gen_expr_list(ctx, expr, out); break; case EXPR_MATCH: - assert(0); // TODO + gen_expr_match(ctx, expr, out); + break; case EXPR_MEASURE: gen_expr_measure(ctx, expr, out); break; diff --git a/src/parse.c b/src/parse.c @@ -1741,15 +1741,15 @@ parse_match_expression(struct lexer *lexer) } break; case T_TIMES: - want(lexer, T_CASE, &tok); + // (default case) break; default: unlex(lexer, &tok); _case->type = parse_type(lexer); - want(lexer, T_CASE, &tok); break; } + want(lexer, T_CASE, &tok); _case->value = parse_compound_expression(lexer); switch (lex(lexer, &tok)) {