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:
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();
};