harec

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

commit 65e0df8bc58d73997054d18634e591eb98323070
parent 9e93137bc3cfb79fe09918c771ca16e59299a03a
Author: Drew DeVault <sir@cmpwn.com>
Date:   Sat,  7 Aug 2021 12:18:00 +0200

gen: implement slicing and slice indexing

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

Diffstat:
Msrc/gen.c | 90++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Mtests/08-slices.ha | 2+-
Mtests/911-slices.ha | 30+++++++++++++++++++++++++++++-
3 files changed, 119 insertions(+), 3 deletions(-)

diff --git a/src/gen.c b/src/gen.c @@ -232,6 +232,12 @@ gen_access_index(struct gen_context *ctx, const struct expression *expr) glval = gen_autoderef(ctx, glval); struct qbe_value qlval = mkqval(ctx, &glval); struct qbe_value qival = mkqtmp(ctx, ctx->arch.ptr, ".%d"); + if (type_dealias(glval.type)->storage == STORAGE_SLICE) { + enum qbe_instr load = load_for_type(ctx, &builtin_type_size); + struct qbe_value temp = mkqtmp(ctx, ctx->arch.ptr, ".%d"); + pushi(ctx->current, &temp, load, &qlval, NULL); + qlval = temp; + } struct gen_value index = gen_expr(ctx, expr->access.index); struct qbe_value qindex = mkqval(ctx, &index); @@ -1126,6 +1132,85 @@ gen_expr_struct_at(struct gen_context *ctx, } static void +gen_expr_slice_at(struct gen_context *ctx, + const struct expression *expr, + struct gen_value out) +{ + struct gen_value object = gen_expr(ctx, expr->slice.object); + object = gen_autoderef(ctx, object); + const struct type *srctype = type_dealias(object.type); + + struct gen_value length; + struct qbe_value qlength; + struct qbe_value qbase; + struct qbe_value qobject = mkqval(ctx, &object); + struct qbe_value offset = constl(builtin_type_size.size); + struct qbe_value qptr = mkqtmp(ctx, ctx->arch.ptr, ".%d"); + switch (srctype->storage) { + case STORAGE_ARRAY: + assert(srctype->array.length != SIZE_UNDEFINED); + length = (struct gen_value){ + .kind = GV_CONST, + .type = &builtin_type_size, + .lval = srctype->array.length, + }; + qlength = mkqval(ctx, &length); + qbase = mkqval(ctx, &object); + break; + case STORAGE_SLICE: + qbase = mkqtmp(ctx, ctx->arch.sz, "base.%d"); + enum qbe_instr load = load_for_type(ctx, &builtin_type_size); + pushi(ctx->current, &qbase, load, &qobject, NULL); + length = mktemp(ctx, &builtin_type_size, "len.%d"); + qlength = mkqval(ctx, &length); + pushi(ctx->current, &qptr, Q_ADD, &qobject, &offset, NULL); + pushi(ctx->current, &qlength, load, &qptr, NULL); + break; + default: abort(); // Invariant + } + + struct gen_value start; + if (expr->slice.start) { + start = gen_expr(ctx, expr->slice.start); + } else { + start = (struct gen_value){ + .kind = GV_CONST, + .type = &builtin_type_size, + .lval = 0, + }; + } + + struct gen_value end; + if (expr->slice.end) { + end = gen_expr(ctx, expr->slice.end); + } else { + end = length; + } + + struct qbe_value qstart = mkqval(ctx, &start); + struct qbe_value qend = mkqval(ctx, &end); + struct qbe_value isz = constl(srctype->array.members->size); + + struct qbe_value qout = mkqval(ctx, &out); + struct qbe_value data = mkqtmp(ctx, ctx->arch.ptr, "data.%d"); + pushi(ctx->current, &data, Q_MUL, &qstart, &isz, NULL); + pushi(ctx->current, &data, Q_ADD, &qbase, &data, NULL); + + struct qbe_value newlen = mkqtmp(ctx, ctx->arch.sz, "newlen.%d"); + pushi(ctx->current, &newlen, Q_SUB, &qend, &qstart, NULL); + struct qbe_value newcap = mkqtmp(ctx, ctx->arch.sz, "newcap.%d"); + pushi(ctx->current, &newcap, Q_SUB, &qlength, &qstart, NULL); + + enum qbe_instr store = store_for_type(ctx, &builtin_type_size); + pushi(ctx->current, NULL, store, &data, &qout, NULL); + pushi(ctx->current, &qptr, Q_ADD, &qout, &offset, NULL); + pushi(ctx->current, NULL, store, &newlen, &qptr, NULL); + offset = constl(builtin_type_size.size * 2); + pushi(ctx->current, &qptr, Q_ADD, &qout, &offset, NULL); + pushi(ctx->current, NULL, store, &newcap, &qptr, NULL); +} + +static void gen_expr_tuple_at(struct gen_context *ctx, const struct expression *expr, struct gen_value out) @@ -1241,11 +1326,11 @@ gen_expr(struct gen_context *ctx, const struct expression *expr) assert(0); // Lowered in check (for now?) case EXPR_RETURN: return gen_expr_return(ctx, expr); - case EXPR_SLICE: case EXPR_SWITCH: assert(0); // TODO case EXPR_UNARITHM: return gen_expr_unarithm(ctx, expr); + case EXPR_SLICE: case EXPR_STRUCT: case EXPR_TUPLE: break; // Prefers -at style @@ -1280,6 +1365,9 @@ gen_expr_at(struct gen_context *ctx, case EXPR_LIST: gen_expr_list_with(ctx, expr, &out); return; + case EXPR_SLICE: + gen_expr_slice_at(ctx, expr, out); + return; case EXPR_STRUCT: gen_expr_struct_at(ctx, expr, out); return; diff --git a/tests/08-slices.ha b/tests/08-slices.ha @@ -120,7 +120,7 @@ fn slicing() void = { assert_slice_eq(p[..3], [1, 2, 3]); assert_slice_eq(p[1..3], [2, 3]); assert_slice_eq(p[1..], [2, 3, 4, 5]); - + assert(rt::compile( "fn test() void = { let x = \"test\"; x[1..3]; };" ) != 0, "slicing non-array, non-slice object"); diff --git a/tests/911-slices.ha b/tests/911-slices.ha @@ -11,8 +11,36 @@ fn castarray() void = { assert(p.capacity == len(a)); }; +fn assert_slice_eq(actual: []int, expected: []int) void = { + assert(len(expected) == len(actual)); + for (let i = 0z; i < len(expected); i += 1) { + assert(expected[i] == actual[i]); + }; +}; + +fn slicing() void = { + let a = [1, 2, 3, 4, 5]; + assert_slice_eq(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]); + + let b: []int = [1, 2, 3, 4, 5]; + assert_slice_eq(b[..], [1, 2, 3, 4, 5]); + assert_slice_eq(b[..3], [1, 2, 3]); + assert_slice_eq(b[1..3], [2, 3]); + assert_slice_eq(b[1..], [2, 3, 4, 5]); + + let p = &a; + assert_slice_eq(p[..], [1, 2, 3, 4, 5]); + assert_slice_eq(p[..3], [1, 2, 3]); + assert_slice_eq(p[1..3], [2, 3]); + assert_slice_eq(p[1..], [2, 3, 4, 5]); +}; + export fn main() int = { castarray(); - // TODO: slicing, indexing, slice assignment + slicing(); + // TODO: slice assignment return 0; };