harec

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

commit 6c40c6cb64fb78f7f9c1a19bc1ecb441d3500fb6
parent 1dbd863cdf45ce1568ed935ff5289a1d03fa0edf
Author: Drew DeVault <sir@cmpwn.com>
Date:   Wed, 13 Jan 2021 12:03:21 -0500

gen: partially implement slicing expressions

Works to slice array types, pending implementation for slice types

Diffstat:
Msrc/check.c | 6+++---
Msrc/gen.c | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++--
Msrc/parse.c | 8++++++++
Mtests/08-slices.ha | 38++++++++++++++++++++++++++++++++++++++
4 files changed, 102 insertions(+), 5 deletions(-)

diff --git a/src/check.c b/src/check.c @@ -403,9 +403,7 @@ check_expr_call(struct context *ctx, type_is_assignable(&ctx->store, param->type, arg->value->result), "Argument is not assignable to parameter type"); - if (param->type != arg->value->result) { - arg->value = lower_implicit_cast(param->type, arg->value); - } + arg->value = lower_implicit_cast(param->type, arg->value); aarg = aarg->next; param = param->next; @@ -800,6 +798,8 @@ check_expr_slice(struct context *ctx, type_storage_unparse(itype->storage)); expr->slice.end = lower_implicit_cast( &builtin_type_size, expr->slice.end); + } else { + // TODO: Assert that array type has a well-defined length } expr->result = type_store_lookup_slice(&ctx->store, diff --git a/src/gen.c b/src/gen.c @@ -141,7 +141,6 @@ gen_copy(struct gen_context *ctx, const struct qbe_value *src) { assert(!dest->indirect && !src->indirect); - assert(dest->type == src->type); const struct qbe_field *field = &dest->type->fields; struct qbe_value temp = {0}, destp = {0}, srcp = {0}, size = {0}; @@ -1079,6 +1078,57 @@ gen_expr_return(struct gen_context *ctx, } static void +gen_expr_slice(struct gen_context *ctx, + const struct expression *expr, + const struct qbe_value *out) +{ + // XXX: ARCH + struct qbe_value object = {0}, start = {0}, end = {0}; + const struct type *otype = expr->slice.object->result; + address_object(ctx, expr->slice.object, &object); + gen_temp(ctx, &start, &qbe_long, "start.%d"); + gen_temp(ctx, &end, &qbe_long, "end.%d"); + + if (expr->slice.start) { + gen_expression(ctx, expr->slice.start, &start); + } else { + constl(&start, 0); + } + + if (expr->slice.end) { + gen_expression(ctx, expr->slice.end, &end); + } else if (otype->storage == TYPE_STORAGE_ARRAY) { + constl(&end, otype->array.length); + } else { + assert(0); // TODO: Read slice length into &end + } + + // TODO: Bounds check + struct qbe_value src = {0}, dest = {0}, temp = {0}, offset = {0}; + gen_temp(ctx, &dest, &qbe_long, "dest.%d"); + gen_temp(ctx, &offset, &qbe_long, "offset.%d"); + pushi(ctx->current, &dest, Q_COPY, out, NULL); + if (otype->storage == TYPE_STORAGE_SLICE) { + assert(0); // TODO + } else { + gen_temp(ctx, &src, &qbe_long, "length.%d"); + + constl(&temp, otype->array.members->size); + pushi(ctx->current, &offset, Q_MUL, &start, &temp, NULL); + pushi(ctx->current, &offset, Q_ADD, &object, &offset, NULL); + pushi(ctx->current, NULL, Q_STOREL, &offset, &dest, NULL); + + pushi(ctx->current, &offset, Q_SUB, &end, &start, NULL); + + constl(&temp, 8); // XXX: ARCH + pushi(ctx->current, &dest, Q_ADD, &dest, &temp, NULL); + pushi(ctx->current, NULL, Q_STOREL, &offset, &dest, NULL); + pushi(ctx->current, &dest, Q_ADD, &dest, &temp, NULL); + pushi(ctx->current, NULL, Q_STOREL, &offset, &dest, NULL); + } +} + +static void gen_expr_struct(struct gen_context *ctx, const struct expression *expr, const struct qbe_value *out) @@ -1213,7 +1263,8 @@ gen_expression(struct gen_context *ctx, gen_expr_return(ctx, expr, out); break; case EXPR_SLICE: - assert(0); // TODO + gen_expr_slice(ctx, expr, out); + break; case EXPR_STRUCT: gen_expr_struct(ctx, expr, out); break; diff --git a/src/parse.c b/src/parse.c @@ -1100,6 +1100,12 @@ parse_index_slice_expression(struct lexer *lexer, struct ast_expression *lvalue) break; case T_RBRACKET: break; + case T_LITERAL: + if (is_slice) { + unlex(lexer, &tok); + break; + } + // Fallthrough default: synassert(false, &tok, T_SLICE, T_RBRACKET, T_EOF); break; @@ -1118,7 +1124,9 @@ parse_index_slice_expression(struct lexer *lexer, struct ast_expression *lvalue) case T_RBRACKET: break; default: + unlex(lexer, &tok); end = parse_simple_expression(lexer); + want(lexer, T_RBRACKET, &tok); break; } diff --git a/tests/08-slices.ha b/tests/08-slices.ha @@ -1,3 +1,5 @@ +fn rt::compile(src: str) int; + fn from_array() void = { let src = [1, 2, 3]; let x: []int = src; @@ -34,6 +36,17 @@ fn measurements() void = { assert(&x: uintptr: size % size(int) == 0z); }; +fn indexing() void = { + let x = [1, 3, 3, 7]; + assert(x[0] == 1 && x[1] == 3 && x[2] == 3 && x[3] == 7); + assert(rt::compile( + "fn test() void = { let x: []int = [1, 2, 3]; x[\"hello\"]; };" + ) != 0, "non-numeric index"); + assert(rt::compile( + "fn test() void = { let x = 10; x[10]; };" + ) != 0, "indexing non-array, non-slice object"); +}; + fn assignment() void = { let source = [1, 2, 3]; let x: []int = source; @@ -51,9 +64,34 @@ fn assignment() void = { assert(source[0] == 4 && source[1] == 5 && source[2] == 6); }; +fn assert_slice_eq(actual: []int, expected: []int) void = { + assert(len(expected) == len(actual)); + for (let i = 0z; i < len(expected); i += 1z) { + assert(expected[i] == actual[i]); + }; +}; + +fn slicing() void = { + let a = [1, 2, 3, 4, 5]; + assert_slice_eq(a[..3], [1, 2, 3]); + assert_slice_eq(a[1..3], [2, 3]); + assert_slice_eq(a[1..], [2, 3, 4, 5]); + + // TODO: Test slicing other slices + + assert(rt::compile( + "fn test() void = { let x = \"test\"; x[1..3]; };" + ) != 0, "slicing non-array, non-slice object"); + assert(rt::compile( + "fn test() void = { let x = [1, 2, 3]; x[\"hi\"..]; };" + ) != 0, "slicing object with non-array, non-slice range"); +}; + export fn main() void = { from_array(); storage(); measurements(); + indexing(); assignment(); + slicing(); };