aboutsummaryrefslogtreecommitdiff
path: root/src/checker
diff options
context:
space:
mode:
authorgingerBill <ginger.bill.22@gmail.com>2016-07-08 01:04:57 +0100
committergingerBill <ginger.bill.22@gmail.com>2016-07-08 01:04:57 +0100
commit9ba2a6d02cab3feff9d70f7bb9c2e8eb72bc5533 (patch)
tree4c4536db506feafcec7157e4064268ffce5a931f /src/checker
parent7430008fd7088339821923bdff533369b7967904 (diff)
Slices and slice expressions
Diffstat (limited to 'src/checker')
-rw-r--r--src/checker/checker.cpp6
-rw-r--r--src/checker/expression.cpp249
-rw-r--r--src/checker/statements.cpp12
-rw-r--r--src/checker/type.cpp33
4 files changed, 259 insertions, 41 deletions
diff --git a/src/checker/checker.cpp b/src/checker/checker.cpp
index 85a491804..4136be912 100644
--- a/src/checker/checker.cpp
+++ b/src/checker/checker.cpp
@@ -71,6 +71,9 @@ enum BuiltinProcedureId {
BuiltinProcedure_offset_of,
BuiltinProcedure_offset_of_val,
BuiltinProcedure_static_assert,
+ BuiltinProcedure_len,
+ BuiltinProcedure_cap,
+ BuiltinProcedure_copy,
BuiltinProcedure_print,
BuiltinProcedure_println,
@@ -111,6 +114,9 @@ gb_global BuiltinProcedure builtin_procedures[BuiltinProcedure_Count] = {
{STR_LIT("offset_of"), 2, false, Expression_Expression},
{STR_LIT("offset_of_val"), 1, false, Expression_Expression},
{STR_LIT("static_assert"), 1, false, Expression_Statement},
+ {STR_LIT("len"), 1, false, Expression_Expression},
+ {STR_LIT("cap"), 1, false, Expression_Expression},
+ {STR_LIT("copy"), 2, false, Expression_Expression},
{STR_LIT("print"), 1, true, Expression_Statement},
{STR_LIT("println"), 1, true, Expression_Statement},
};
diff --git a/src/checker/expression.cpp b/src/checker/expression.cpp
index aa4e86f95..fbb213dfa 100644
--- a/src/checker/expression.cpp
+++ b/src/checker/expression.cpp
@@ -252,8 +252,9 @@ Type *check_type_expression_extra(Checker *c, AstNode *expression, Type *named_t
set_base_type(named_type, t);
return t;
} else {
- print_checker_error(c, ast_node_token(expression), "Empty array size");
- return NULL;
+ Type *t = make_type_slice(check_type(c, expression->array_type.element));
+ set_base_type(named_type, t);
+ return t;
}
break;
@@ -335,12 +336,17 @@ Type *check_type(Checker *c, AstNode *expression, Type *named_type) {
case AstNode_ParenExpression:
return check_type(c, expression->paren_expression.expression, named_type);
- case AstNode_ArrayType:
- type = make_type_array(check_type(c, expression->array_type.element),
- check_array_count(c, expression->array_type.count));
- set_base_type(named_type, type);
+ case AstNode_ArrayType: {
+ if (expression->array_type.count != NULL) {
+ type = make_type_array(check_type(c, expression->array_type.element),
+ check_array_count(c, expression->array_type.count));
+ set_base_type(named_type, type);
+ } else {
+ type = make_type_slice(check_type(c, expression->array_type.element));
+ set_base_type(named_type, type);
+ }
goto end;
- break;
+ } break;
case AstNode_StructType: {
type = make_type_structure();
@@ -860,35 +866,43 @@ void convert_to_typed(Checker *c, Operand *operand, Type *target_type) {
operand->type = target_type;
}
-b32 check_index_value(Checker *c, AstNode *index_value, i64 max_count, b32 bound_checks) {
+b32 check_index_value(Checker *c, AstNode *index_value, i64 max_count, i64 *value) {
Operand operand = {Addressing_Invalid};
check_expression(c, &operand, index_value);
- if (operand.mode == Addressing_Invalid)
+ if (operand.mode == Addressing_Invalid) {
+ if (value) *value = 0;
return false;
+ }
convert_to_typed(c, &operand, &basic_types[Basic_int]);
- if (operand.mode == Addressing_Invalid)
+ if (operand.mode == Addressing_Invalid) {
+ if (value) *value = 0;
return false;
+ }
if (!is_type_integer(operand.type)) {
gbString expr_str = expression_to_string(operand.expression);
print_checker_error(c, ast_node_token(operand.expression),
"Index `%s` must be an integer", expr_str);
gb_string_free(expr_str);
+ if (value) *value = 0;
return false;
}
if (operand.mode == Addressing_Constant) {
- if (bound_checks && max_count > 0) { // NOTE(bill): Do array bound checking
+ if (max_count >= 0) { // NOTE(bill): Do array bound checking
i64 i = value_to_integer(operand.value).value_integer;
if (i < 0) {
gbString expr_str = expression_to_string(operand.expression);
print_checker_error(c, ast_node_token(operand.expression),
"Index `%s` cannot be a negative value", expr_str);
gb_string_free(expr_str);
+ if (value) *value = 0;
return false;
}
+ if (value) *value = i;
+
if (i >= max_count) {
gbString expr_str = expression_to_string(operand.expression);
print_checker_error(c, ast_node_token(operand.expression),
@@ -896,10 +910,13 @@ b32 check_index_value(Checker *c, AstNode *index_value, i64 max_count, b32 bound
gb_string_free(expr_str);
return false;
}
+
+ return true;
}
}
// NOTE(bill): It's alright :D
+ if (value) *value = -1;
return true;
}
@@ -920,10 +937,6 @@ Entity *lookup_field(Type *type, AstNode *field_node, isize *index = NULL) {
return f;
}
}
- } else {
- // TODO(bill): Array.count
- // TODO(bill): Array.elements
- // TODO(bill): Or should these be functions?
}
return NULL;
@@ -960,7 +973,6 @@ void check_selector(Checker *c, Operand *operand, AstNode *node) {
}
-
b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) {
GB_ASSERT(call->kind == AstNode_CallExpression);
auto *ce = &call->call_expression;
@@ -972,10 +984,9 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
if (ce->arg_list_count > bp->arg_count && !bp->variadic)
err = "Too many";
if (err) {
- gbString call_str = expression_to_string(call);
- defer (gb_string_free(call_str));
- print_checker_error(c, ce->close, "`%s` arguments for `%s`, expected %td, got %td",
- err, call_str, bp->arg_count, ce->arg_list_count);
+ print_checker_error(c, ce->close, "`%s` arguments for `%.*s`, expected %td, got %td",
+ err, LIT(call->call_expression.proc->identifier.token.string),
+ bp->arg_count, ce->arg_list_count);
return false;
}
}
@@ -1044,7 +1055,8 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
print_checker_error(c, ast_node_token(ce->arg_list), "Expected a structure type for `offset_of`");
return false;
}
- if (field_arg->kind != AstNode_Identifier) {
+ if (field_arg == NULL ||
+ field_arg->kind != AstNode_Identifier) {
print_checker_error(c, ast_node_token(field_arg), "Expected an identifier for field argument");
return false;
}
@@ -1055,7 +1067,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
if (entity == NULL) {
gbString type_str = type_to_string(type);
print_checker_error(c, ast_node_token(ce->arg_list),
- "`%s` has no field named `%s`", type_str, LIT(field_arg->identifier.token.string));
+ "`%s` has no field named `%.*s`", type_str, LIT(field_arg->identifier.token.string));
return false;
}
@@ -1089,7 +1101,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
if (entity == NULL) {
gbString type_str = type_to_string(type);
print_checker_error(c, ast_node_token(arg),
- "`%s` has no field named `%s`", type_str, LIT(s->selector->identifier.token.string));
+ "`%s` has no field named `%.*s`", type_str, LIT(s->selector->identifier.token.string));
return false;
}
@@ -1099,6 +1111,8 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
} break;
case BuiltinProcedure_static_assert:
+ // static_assert :: proc(cond: bool)
+
if (operand->mode != Addressing_Constant ||
!is_type_boolean(operand->type)) {
gbString str = expression_to_string(ce->arg_list);
@@ -1116,6 +1130,92 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
}
break;
+ case BuiltinProcedure_len:
+ case BuiltinProcedure_cap: {
+ Type *t = get_base_type(operand->type);
+
+ AddressingMode mode = Addressing_Invalid;
+ Value value = {};
+
+ switch (t->kind) {
+ case Type_Basic:
+ if (id == BuiltinProcedure_len) {
+ if (is_type_string(t)) {
+ if (operand->mode == Addressing_Constant) {
+ mode = Addressing_Constant;
+ value = make_value_integer(operand->value.value_string.len);
+ } else {
+ mode = Addressing_Value;
+ }
+ }
+ }
+ break;
+
+ case Type_Array:
+ mode = Addressing_Constant;
+ value = make_value_integer(t->array.count);
+ break;
+
+ case Type_Slice:
+ mode = Addressing_Value;
+ break;
+ }
+
+ if (mode == Addressing_Invalid) {
+ gbString str = expression_to_string(operand->expression);
+ print_checker_error(c, ast_node_token(operand->expression),
+ "Invalid expression `%s` for `%.*s`",
+ str, LIT(bp->name));
+ gb_string_free(str);
+ return false;
+ }
+
+ operand->mode = mode;
+ operand->type = &basic_types[Basic_int];
+ operand->value = value;
+
+ } break;
+
+ case BuiltinProcedure_copy: {
+ // copy :: proc(x, y: []Type) -> int
+ Type *dest_type = NULL, *src_type = NULL;
+ Type *d = get_base_type(operand->type);
+ if (d->kind == Type_Slice)
+ dest_type = d->slice.element;
+
+ Operand op = {};
+ check_expression(c, &op, ce->arg_list->next);
+ if (op.mode == Addressing_Invalid)
+ return false;
+ Type *s = get_base_type(op.type);
+ if (s->kind == Type_Slice)
+ src_type = s->slice.element;
+
+ if (dest_type == NULL || src_type == NULL) {
+ print_checker_error(c, ast_node_token(call), "`copy` only expects slices as arguments");
+ return false;
+ }
+
+ if (!are_types_identical(dest_type, src_type)) {
+ gbString d_arg = expression_to_string(ce->arg_list);
+ gbString s_arg = expression_to_string(ce->arg_list->next);
+ gbString d_str = type_to_string(dest_type);
+ gbString s_str = type_to_string(src_type);
+ defer (gb_string_free(d_arg));
+ defer (gb_string_free(s_arg));
+ defer (gb_string_free(d_str));
+ defer (gb_string_free(s_str));
+ print_checker_error(c, ast_node_token(call),
+ "Arguments to `copy`, %s, %s, have different element types: %s vs %s",
+ d_arg, s_arg, d_str, s_str);
+ return false;
+ }
+
+ operand->type = &basic_types[Basic_int]; // Returns number of elements copied
+ operand->mode = Addressing_Value;
+ } break;
+
+
case BuiltinProcedure_print:
case BuiltinProcedure_println: {
for (AstNode *arg = ce->arg_list; arg != NULL; arg = arg->next) {
@@ -1170,7 +1270,6 @@ void check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNode
operand->mode = Addressing_Value;
check_not_tuple(c, operand);
check_assignment(c, operand, sig_params[param_index]->type, make_string("argument"));
-
}
if (i < tuple->variable_count && param_index == param_count) {
@@ -1179,7 +1278,7 @@ void check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNode
}
}
- if (param_index < param_count)
+ if (param_index >= param_count)
break;
}
@@ -1392,8 +1491,7 @@ ExpressionKind check_expression_base(Checker *c, Operand *operand, AstNode *expr
goto error;
b32 valid = false;
- b32 bound_checks = false;
- i64 max_count = 0;
+ i64 max_count = -1;
Type *t = get_base_type(operand->type);
switch (t->kind) {
case Type_Basic:
@@ -1401,7 +1499,6 @@ ExpressionKind check_expression_base(Checker *c, Operand *operand, AstNode *expr
valid = true;
if (operand->mode == Addressing_Constant) {
max_count = operand->value.value_string.len;
- bound_checks = true;
}
operand->mode = Addressing_Value;
operand->type = &basic_types[Basic_u8];
@@ -1411,16 +1508,19 @@ ExpressionKind check_expression_base(Checker *c, Operand *operand, AstNode *expr
case Type_Array:
valid = true;
max_count = t->array.count;
- bound_checks = max_count > 0;
if (operand->mode != Addressing_Variable)
operand->mode = Addressing_Value;
operand->type = t->array.element;
break;
+ case Type_Slice:
+ valid = true;
+ operand->type = t->slice.element;
+ operand->mode = Addressing_Variable;
+ break;
+
case Type_Pointer:
valid = true;
- bound_checks = false;
- max_count = 0;
operand->mode = Addressing_Variable;
operand->type = get_base_type(t->pointer.element);
break;
@@ -1442,7 +1542,92 @@ ExpressionKind check_expression_base(Checker *c, Operand *operand, AstNode *expr
goto error;
}
- check_index_value(c, expression->index_expression.value, max_count, bound_checks);
+ check_index_value(c, expression->index_expression.value, max_count, NULL);
+ } break;
+
+
+ case AstNode_SliceExpression: {
+ auto *se = &expression->slice_expression;
+ check_expression(c, operand, se->expression);
+ if (operand->mode == Addressing_Invalid)
+ goto error;
+
+ b32 valid = false;
+ i64 max_count = -1;
+ Type *t = get_base_type(operand->type);
+ switch (t->kind) {
+ case Type_Basic:
+ if (is_type_string(t)) {
+ valid = true;
+ if (operand->mode == Addressing_Constant) {
+ max_count = operand->value.value_string.len;
+ }
+ operand->mode = Addressing_Value;
+ }
+ break;
+
+ case Type_Array:
+ valid = true;
+ max_count = t->array.count;
+ if (operand->mode != Addressing_Variable) {
+ gbString str = expression_to_string(expression);
+ print_checker_error(c, ast_node_token(expression), "Cannot slice array `%s`, value is not addressable", str);
+ gb_string_free(str);
+ goto error;
+ }
+ operand->type = make_type_slice(t->array.element);
+ operand->mode = Addressing_Value;
+ break;
+
+ case Type_Slice:
+ valid = true;
+ operand->mode = Addressing_Value;
+ break;
+
+ case Type_Pointer:
+ valid = true;
+ operand->type = make_type_slice(get_base_type(t->pointer.element));
+ operand->mode = Addressing_Value;
+ break;
+ }
+
+ if (!valid) {
+ gbString str = expression_to_string(operand->expression);
+ print_checker_error(c, ast_node_token(operand->expression),
+ "Cannot slice `%s`", str);
+ gb_string_free(str);
+ goto error;
+ }
+
+ i64 indices[3] = {};
+ AstNode *nodes[3] = {se->low, se->high, se->max};
+ for (isize i = 0; i < gb_count_of(nodes); i++) {
+ AstNode *node = nodes[i];
+ i64 index = max_count;
+ if (node != NULL) {
+ i64 capacity = -1;
+ if (max_count >= 0)
+ capacity = max_count;
+ i64 j = 0;
+ if (check_index_value(c, node, capacity, &j)) {
+ index = j;
+ }
+ } else if (i == 0) {
+ index = 0;
+ }
+ indices[i] = index;
+ }
+
+ for (isize i = 0; i < gb_count_of(indices); i++) {
+ i64 a = indices[i];
+ for (isize j = i+1; j < gb_count_of(indices); j++) {
+ i64 b = indices[j];
+ if (a > b && b >= 0) {
+ print_checker_error(c, se->close, "Invalid slice indices: [%td > %td]", a, b);
+ }
+ }
+ }
+
} break;
case AstNode_CastExpression: {
diff --git a/src/checker/statements.cpp b/src/checker/statements.cpp
index 036b096c9..15a0904a6 100644
--- a/src/checker/statements.cpp
+++ b/src/checker/statements.cpp
@@ -38,8 +38,14 @@ b32 check_assignable_to(Checker *c, Operand *operand, Type *type) {
if (sb->kind == Type_Array && tb->kind == Type_Array) {
if (are_types_identical(sb->array.element, tb->array.element)) {
- if (tb->array.count == 0) // NOTE(bill): Not static size
- return true;
+ return sb->array.count == tb->array.count;
+ }
+ }
+
+ if ((sb->kind == Type_Array || sb->kind == Type_Slice) &&
+ tb->kind == Type_Slice) {
+ if (are_types_identical(sb->array.element, tb->slice.element)) {
+ return true;
}
}
@@ -233,7 +239,7 @@ void check_init_variables(Checker *c, Entity **lhs, isize lhs_count, AstNode *in
if (i < lhs_count) {
if (lhs[i]->type == NULL)
print_checker_error(c, lhs[i]->token, "Too few values on the right hand side of the declaration");
- } else if (rhs != NULL) {
+ } else if (rhs != NULL) {
print_checker_error(c, ast_node_token(rhs), "Too many values on the right hand side of the declaration");
}
}
diff --git a/src/checker/type.cpp b/src/checker/type.cpp
index 684e0651c..46957601e 100644
--- a/src/checker/type.cpp
+++ b/src/checker/type.cpp
@@ -55,6 +55,7 @@ enum TypeKind {
Type_Basic,
Type_Array,
+ Type_Slice,
Type_Structure,
Type_Pointer,
Type_Named,
@@ -72,6 +73,9 @@ struct Type {
i64 count;
} array;
struct {
+ Type *element;
+ } slice;
+ struct {
// Theses are arrays
Entity **fields; // Entity_Variable
isize field_count; // == offset_count
@@ -111,6 +115,7 @@ void set_base_type(Type *t, Type *base) {
}
+// TODO(bill): Remove heap allocation
Type *alloc_type(TypeKind kind) {
Type *t = gb_alloc_item(gb_heap_allocator(), Type);
t->kind = kind;
@@ -123,6 +128,7 @@ Type *make_type_basic(BasicType basic) {
t->basic = basic;
return t;
}
+
Type *make_type_array(Type *element, i64 count) {
Type *t = alloc_type(Type_Array);
t->array.element = element;
@@ -130,6 +136,12 @@ Type *make_type_array(Type *element, i64 count) {
return t;
}
+Type *make_type_slice(Type *element) {
+ Type *t = alloc_type(Type_Slice);
+ t->array.element = element;
+ return t;
+}
+
Type *make_type_structure(void) {
Type *t = alloc_type(Type_Structure);
return t;
@@ -166,7 +178,7 @@ Type *make_type_procedure(Scope *scope, Type *params, isize params_count, Type *
-#define STR_LIT(x) {cast(u8 *)x, gb_size_of(x)-1}
+#define STR_LIT(x) {cast(u8 *)(x), gb_size_of(x)-1}
gb_global Type basic_types[] = {
{Type_Basic, {Basic_Invalid, 0, STR_LIT("invalid type")}},
{Type_Basic, {Basic_bool, BasicFlag_Boolean, STR_LIT("bool")}},
@@ -483,10 +495,11 @@ i64 type_size_of(BaseTypeSizes s, gbAllocator allocator, Type *t) {
}
i64 type_offset_of(BaseTypeSizes s, gbAllocator allocator, Type *t, isize index) {
- GB_ASSERT(t->kind == Type_Structure);
- type_set_offsets(s, allocator, t);
- if (gb_is_between(index, 0, t->structure.field_count-1)) {
- return t->structure.offsets[index];
+ if (t->kind == Type_Structure) {
+ type_set_offsets(s, allocator, t);
+ if (gb_is_between(index, 0, t->structure.field_count-1)) {
+ return t->structure.offsets[index];
+ }
}
return 0;
}
@@ -501,6 +514,7 @@ gbString write_type_to_string(gbString str, Type *type) {
case Type_Basic:
str = gb_string_append_length(str, type->basic.name.text, type->basic.name.len);
break;
+
case Type_Array:
if (type->array.count >= 0) {
str = gb_string_appendc(str, gb_bprintf("[%td]", type->array.count));
@@ -509,12 +523,18 @@ gbString write_type_to_string(gbString str, Type *type) {
}
str = write_type_to_string(str, type->array.element);
break;
+
+ case Type_Slice:
+ str = gb_string_appendc(str, "[]");
+ str = write_type_to_string(str, type->array.element);
+ break;
+
case Type_Structure: {
str = gb_string_appendc(str, "struct{");
for (isize i = 0; i < type->structure.field_count; i++) {
Entity *f = type->structure.fields[i];
GB_ASSERT(f->kind == Entity_Variable);
- if (i < type->structure.field_count-1)
+ if (i > 0)
str = gb_string_appendc(str, "; ");
str = gb_string_append_length(str, f->token.string.text, f->token.string.len);
str = gb_string_appendc(str, ": ");
@@ -564,6 +584,7 @@ gbString write_type_to_string(gbString str, Type *type) {
}
break;
}
+
return str;
}