aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGinger Bill <bill@gingerbill.org>2017-01-01 16:18:50 +0000
committerGinger Bill <bill@gingerbill.org>2017-01-01 16:18:50 +0000
commit6fef74317cdd0e403fb913ebe965dc08b3dfb22d (patch)
tree9f25deab9276dc60847c0548d1024cfdbc3679d4 /src
parent0c6775ca14ced37ac58a03ccad4028e225bda7d6 (diff)
Bring back `enum` but using iota
Diffstat (limited to 'src')
-rw-r--r--src/checker/checker.c3
-rw-r--r--src/checker/expr.c140
-rw-r--r--src/checker/stmt.c15
-rw-r--r--src/checker/types.c49
-rw-r--r--src/parser.c14
-rw-r--r--src/ssa.c43
-rw-r--r--src/ssa_print.c5
-rw-r--r--src/tokenizer.c1
8 files changed, 253 insertions, 17 deletions
diff --git a/src/checker/checker.c b/src/checker/checker.c
index c125049ac..79ca9eef6 100644
--- a/src/checker/checker.c
+++ b/src/checker/checker.c
@@ -1027,7 +1027,7 @@ void init_preload(Checker *c) {
t_type_info_member = type_info_member_entity->type;
t_type_info_member_ptr = make_type_pointer(c->allocator, t_type_info_member);
- if (record->field_count != 17) {
+ if (record->field_count != 18) {
compiler_error("Invalid `Type_Info` layout");
}
t_type_info_named = record->fields[ 1]->type;
@@ -1046,6 +1046,7 @@ void init_preload(Checker *c) {
t_type_info_struct = record->fields[14]->type;
t_type_info_union = record->fields[15]->type;
t_type_info_raw_union = record->fields[16]->type;
+ t_type_info_enum = record->fields[17]->type;
}
if (t_allocator == NULL) {
diff --git a/src/checker/expr.c b/src/checker/expr.c
index 77e9bd58e..7b9b0db52 100644
--- a/src/checker/expr.c
+++ b/src/checker/expr.c
@@ -18,6 +18,7 @@ bool check_is_terminating (AstNode *node);
bool check_has_break (AstNode *stmt, bool implicit);
void check_stmt (Checker *c, AstNode *node, u32 flags);
void check_stmt_list (Checker *c, AstNodeArray stmts, u32 flags);
+void check_init_constant (Checker *c, Entity *e, Operand *operand);
gb_inline Type *check_type(Checker *c, AstNode *expression) {
@@ -724,6 +725,106 @@ void check_raw_union_type(Checker *c, Type *union_type, AstNode *node) {
// return i < j ? -1 : i > j;
// }
+void check_enum_type(Checker *c, Type *enum_type, Type *named_type, AstNode *node) {
+ ast_node(et, EnumType, node);
+ GB_ASSERT(is_type_enum(enum_type));
+
+ gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
+
+ Type *base_type = t_int;
+ if (et->base_type != NULL) {
+ base_type = check_type(c, et->base_type);
+ }
+
+ if (base_type == NULL || !(is_type_integer(base_type) || is_type_float(base_type))) {
+ error_node(node, "Base type for enumeration must be numeric");
+ return;
+ }
+
+ // NOTE(bill): Must be up here for the `check_init_constant` system
+ enum_type->Record.enum_base_type = base_type;
+
+ MapEntity entity_map = {0}; // Key: String
+ map_entity_init_with_reserve(&entity_map, c->tmp_allocator, 2*(et->fields.count));
+
+ Entity **fields = gb_alloc_array(c->allocator, Entity *, et->fields.count);
+ isize field_index = 0;
+
+ Type *constant_type = enum_type;
+ if (named_type != NULL) {
+ constant_type = named_type;
+ }
+
+ AstNode *prev_expr = NULL;
+
+ i64 iota = 0;
+
+ for_array(i, et->fields) {
+ AstNode *field = et->fields.e[i];
+ AstNode *ident = NULL;
+ if (field->kind == AstNode_FieldValue) {
+ ast_node(fv, FieldValue, field);
+ if (fv->field == NULL || fv->field->kind != AstNode_Ident) {
+ error_node(field, "An enum field's name must be an identifier");
+ continue;
+ }
+ ident = fv->field;
+ prev_expr = fv->value;
+ } else if (field->kind == AstNode_Ident) {
+ ident = field;
+ } else {
+ error_node(field, "An enum field's name must be an identifier");
+ continue;
+ }
+ String name = ident->Ident.string;
+
+ if (str_ne(name, str_lit("_"))) {
+ ExactValue v = make_exact_value_integer(iota);
+ Entity *e = make_entity_constant(c->allocator, c->context.scope, ident->Ident, constant_type, v);
+ e->identifier = ident;
+ e->flags |= EntityFlag_Visited;
+
+
+ AstNode *init = prev_expr;
+ if (init == NULL) {
+ error_node(field, "Missing initial expression for enumeration, e.g. iota");
+ continue;
+ }
+
+ GB_ASSERT(c->context.iota.kind == ExactValue_Invalid);
+ c->context.iota = e->Constant.value;
+ e->Constant.value = (ExactValue){0};
+
+ Operand operand = {0};
+ check_expr(c, &operand, init);
+
+ check_init_constant(c, e, &operand);
+ c->context.iota = (ExactValue){0};
+
+ if (operand.mode == Addressing_Constant) {
+ HashKey key = hash_string(name);
+ if (map_entity_get(&entity_map, key) != NULL) {
+ error_node(ident, "`%.*s` is already declared in this enumeration", LIT(name));
+ } else {
+ map_entity_set(&entity_map, key, e);
+ add_entity(c, c->context.scope, NULL, e);
+ fields[field_index++] = e;
+ add_entity_use(c, field, e);
+ }
+ }
+ }
+ iota++;
+ }
+
+ GB_ASSERT(field_index <= et->fields.count);
+
+ enum_type->Record.fields = fields;
+ enum_type->Record.field_count = field_index;
+
+ gb_temp_arena_memory_end(tmp);
+}
+
+
Type *check_get_params(Checker *c, Scope *scope, AstNodeArray params, bool *is_variadic_) {
if (params.count == 0) {
return NULL;
@@ -1105,6 +1206,16 @@ Type *check_type_extra(Checker *c, AstNode *e, Type *named_type) {
goto end;
case_end;
+ case_ast_node(et, EnumType, e);
+ type = make_type_enum(c->allocator);
+ set_base_type(named_type, type);
+ check_open_scope(c, e);
+ check_enum_type(c, type, named_type, e);
+ check_close_scope(c);
+ type->Record.node = e;
+ goto end;
+ case_end;
+
case_ast_node(pt, ProcType, e);
type = alloc_type(c->allocator, Type_Proc);
set_base_type(named_type, type);
@@ -1114,6 +1225,7 @@ Type *check_type_extra(Checker *c, AstNode *e, Type *named_type) {
goto end;
case_end;
+
case_ast_node(ce, CallExpr, e);
Operand o = {0};
check_expr_or_type(c, &o, e);
@@ -1161,7 +1273,7 @@ end:
bool check_unary_op(Checker *c, Operand *o, Token op) {
// TODO(bill): Handle errors correctly
- Type *type = base_type(base_vector_type(o->type));
+ Type *type = base_type(base_enum_type(base_vector_type(o->type)));
gbString str = NULL;
switch (op.kind) {
case Token_Add:
@@ -1197,7 +1309,7 @@ bool check_unary_op(Checker *c, Operand *o, Token op) {
bool check_binary_op(Checker *c, Operand *o, Token op) {
// TODO(bill): Handle errors correctly
- Type *type = base_type(base_vector_type(o->type));
+ Type *type = base_type(base_enum_type(base_vector_type(o->type)));
switch (op.kind) {
case Token_Sub:
case Token_SubEq:
@@ -1274,6 +1386,8 @@ bool check_value_is_expressible(Checker *c, ExactValue in_value, Type *type, Exa
return true;
}
+ type = base_type(base_enum_type(type));
+
if (is_type_boolean(type)) {
return in_value.kind == ExactValue_Bool;
} else if (is_type_string(type)) {
@@ -1363,7 +1477,7 @@ void check_is_expressible(Checker *c, Operand *o, Type *type) {
error_node(o->expr, "`%s = %lld` overflows `%s`", a, o->value.value_integer, b);
}
} else {
- error_node(o->expr, "Cannot convert `%s` to `%s`", a, b);
+ error_node(o->expr, "Cannot convert `%s` to `%s`", a, b);
}
gb_string_free(b);
@@ -2036,10 +2150,10 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) {
}
if (op.kind == Token_Add || op.kind == Token_Sub) {
- if (is_type_pointer(x->type) && is_type_integer(y->type)) {
+ if (is_type_pointer(x->type) && is_type_integer(base_enum_type(y->type))) {
*x = check_ptr_addition(c, op.kind, x, y, node);
return;
- } else if (is_type_integer(x->type) && is_type_pointer(y->type)) {
+ } else if (is_type_integer(base_enum_type(x->type)) && is_type_pointer(y->type)) {
if (op.kind == Token_Sub) {
gbString lhs = expr_to_string(x->expr);
gbString rhs = expr_to_string(y->expr);
@@ -2247,20 +2361,22 @@ void convert_to_typed(Checker *c, Operand *operand, Type *target_type, i32 level
}
if (is_type_untyped(target_type)) {
- Type *x = operand->type;
- Type *y = target_type;
- if (is_type_numeric(x) && is_type_numeric(y)) {
- if (x < y) {
+ GB_ASSERT(operand->type->kind == Type_Basic);
+ GB_ASSERT(target_type->kind == Type_Basic);
+ BasicKind x_kind = operand->type->Basic.kind;
+ BasicKind y_kind = target_type->Basic.kind;
+ if (is_type_numeric(operand->type) && is_type_numeric(target_type)) {
+ if (x_kind < y_kind) {
operand->type = target_type;
update_expr_type(c, operand->expr, target_type, false);
}
- } else if (x != y) {
+ } else if (x_kind != y_kind) {
convert_untyped_error(c, operand, target_type);
}
return;
}
- Type *t = base_type(target_type);
+ Type *t = base_type(base_enum_type(target_type));
switch (t->kind) {
case Type_Basic:
if (operand->mode == Addressing_Constant) {
@@ -4366,7 +4482,9 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
case AstNode_ArrayType:
case AstNode_VectorType:
case AstNode_StructType:
+ case AstNode_UnionType:
case AstNode_RawUnionType:
+ case AstNode_EnumType:
o->mode = Addressing_Type;
o->type = check_type(c, node);
break;
diff --git a/src/checker/stmt.c b/src/checker/stmt.c
index 45906b94e..5b7d7887f 100644
--- a/src/checker/stmt.c
+++ b/src/checker/stmt.c
@@ -934,8 +934,21 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
}
f->using_parent = e;
}
+ } else if (is_type_enum(t)) {
+ for (isize i = 0; i < t->Record.field_count; i++) {
+ Entity *f = t->Record.fields[i];
+ Entity *found = scope_insert_entity(c->context.scope, f);
+ if (found != NULL) {
+ gbString expr_str = expr_to_string(expr);
+ error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string));
+ gb_string_free(expr_str);
+ return;
+ }
+ f->using_parent = e;
+ }
+
} else {
- error(us->token, "`using` can be only applied to `union` type entities");
+ error(us->token, "`using` can be only applied to `union` or `enum` type entities");
}
} break;
diff --git a/src/checker/types.c b/src/checker/types.c
index 4e81c5a38..df11e1097 100644
--- a/src/checker/types.c
+++ b/src/checker/types.c
@@ -64,6 +64,7 @@ typedef enum TypeRecordKind {
TypeRecord_Struct,
TypeRecord_RawUnion,
TypeRecord_Union, // Tagged
+ TypeRecord_Enum,
TypeRecord_Count,
} TypeRecordKind;
@@ -82,6 +83,8 @@ typedef struct TypeRecord {
bool struct_is_packed;
bool struct_is_ordered;
Entity **fields_in_src_order; // Entity_Variable
+
+ Type * enum_base_type;
} TypeRecord;
#define TYPE_KINDS \
@@ -270,6 +273,7 @@ gb_global Type *t_type_info_tuple = NULL;
gb_global Type *t_type_info_struct = NULL;
gb_global Type *t_type_info_union = NULL;
gb_global Type *t_type_info_raw_union = NULL;
+gb_global Type *t_type_info_enum = NULL;
gb_global Type *t_allocator = NULL;
gb_global Type *t_allocator_ptr = NULL;
@@ -285,7 +289,10 @@ gbString type_to_string(Type *type);
Type *base_type(Type *t) {
for (;;) {
- if (t == NULL || t->kind != Type_Named) {
+ if (t == NULL) {
+ break;
+ }
+ if (t->kind != Type_Named) {
break;
}
if (t == t->Named.base) {
@@ -296,6 +303,16 @@ Type *base_type(Type *t) {
return t;
}
+Type *base_enum_type(Type *t) {
+ Type *bt = base_type(t);
+ if (bt != NULL &&
+ bt->kind == Type_Record &&
+ bt->Record.kind == TypeRecord_Enum) {
+ return bt->Record.enum_base_type;
+ }
+ return t;
+}
+
void set_base_type(Type *t, Type *base) {
if (t && t->kind == Type_Named) {
t->Named.base = base;
@@ -367,6 +384,13 @@ Type *make_type_raw_union(gbAllocator a) {
return t;
}
+Type *make_type_enum(gbAllocator a) {
+ Type *t = alloc_type(a, Type_Record);
+ t->Record.kind = TypeRecord_Enum;
+ return t;
+}
+
+
@@ -492,7 +516,7 @@ bool is_type_ordered(Type *t) {
return false;
}
bool is_type_constant_type(Type *t) {
- t = base_type(t);
+ t = base_type(base_enum_type(t));
if (t->kind == Type_Basic) {
return (t->Basic.flags & BasicFlag_ConstantType) != 0;
}
@@ -598,6 +622,11 @@ bool is_type_raw_union(Type *t) {
t = base_type(t);
return (t->kind == Type_Record && t->Record.kind == TypeRecord_RawUnion);
}
+bool is_type_enum(Type *t) {
+ t = base_type(t);
+ return (t->kind == Type_Record && t->Record.kind == TypeRecord_Enum);
+}
+
bool is_type_any(Type *t) {
t = base_type(t);
@@ -642,6 +671,8 @@ bool is_type_comparable(Type *t) {
if (!is_type_comparable(t->Record.fields[i]->type))
return false;
}
+ } else if (is_type_enum(t)) {
+ return is_type_comparable(base_enum_type(t));
}
return false;
} break;
@@ -712,6 +743,8 @@ bool are_types_identical(Type *x, Type *y) {
return true;
}
break;
+ case TypeRecord_Enum:
+ return x == y; // NOTE(bill): All enums are unique
}
}
}
@@ -998,6 +1031,18 @@ Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_n
return sel;
}
}
+ } else if (is_type_enum(type)) {
+ for (isize i = 0; i < type->Record.field_count; i++) {
+ Entity *f = type->Record.fields[i];
+ GB_ASSERT(f->kind == Entity_Constant);
+ String str = f->token.string;
+
+ if (str_eq(field_name, str)) {
+ sel.entity = f;
+ selection_add_index(&sel, i);
+ return sel;
+ }
+ }
}
} else if (!is_type_union(type)) {
for (isize i = 0; i < type->Record.field_count; i++) {
diff --git a/src/parser.c b/src/parser.c
index bfed77b0b..a7d9b4282 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -347,7 +347,7 @@ AST_NODE_KIND(_TypeBegin, "", i32) \
AST_NODE_KIND(EnumType, "enum type", struct { \
Token token; \
AstNode *base_type; \
- AstNodeArray fields; \
+ AstNodeArray fields; /* FieldValue */ \
}) \
AST_NODE_KIND(_TypeEnd, "", i32)
@@ -2548,6 +2548,18 @@ AstNode *parse_identifier_or_type(AstFile *f) {
return make_raw_union_type(f, token, decls, decl_count);
}
+ case Token_enum: {
+ Token token = expect_token(f, Token_enum);
+ AstNode *base_type = NULL;
+ if (f->curr_token.kind != Token_OpenBrace) {
+ base_type = parse_type(f);
+ }
+ Token open = expect_token(f, Token_OpenBrace);
+ AstNodeArray fields = parse_element_list(f);
+ Token close = expect_token(f, Token_CloseBrace);
+ return make_enum_type(f, token, base_type, fields);
+ }
+
case Token_proc: {
Token token = f->curr_token;
AstNode *pt = parse_proc_type(f, NULL, NULL);
diff --git a/src/ssa.c b/src/ssa.c
index 1d0ef093c..d53b5fcf6 100644
--- a/src/ssa.c
+++ b/src/ssa.c
@@ -5484,6 +5484,49 @@ void ssa_gen_tree(ssaGen *s) {
ssa_emit_store(proc, len, field_count);
ssa_emit_store(proc, cap, field_count);
} break;
+ case TypeRecord_Enum:
+ tag = ssa_add_local_generated(proc, t_type_info_enum);
+ {
+ GB_ASSERT(t->Record.enum_base_type != NULL);
+ ssaValue *base = ssa_type_info(proc, t->Record.enum_base_type);
+ ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), base);
+
+ if (t->Record.field_count > 0) {
+ Entity **fields = t->Record.fields;
+ isize count = t->Record.field_count;
+ ssaValue *name_array = NULL;
+
+ {
+ Token token = {Token_Ident};
+ i32 id = cast(i32)entry_index;
+ char name_base[] = "__$enum_names";
+ isize name_len = gb_size_of(name_base) + 10;
+ token.string.text = gb_alloc_array(a, u8, name_len);
+ token.string.len = gb_snprintf(cast(char *)token.string.text, name_len,
+ "%s-%d", name_base, id)-1;
+ Entity *e = make_entity_variable(a, NULL, token, make_type_array(a, t_string, count), false);
+ name_array = ssa_make_value_global(a, e, NULL);
+ name_array->Global.is_private = true;
+ ssa_module_add_value(m, e, name_array);
+ map_ssa_value_set(&m->members, hash_string(token.string), name_array);
+ }
+
+ for (isize i = 0; i < count; i++) {
+ ssaValue *name_ep = ssa_emit_array_epi(proc, name_array, i);
+ ssa_emit_store(proc, name_ep, ssa_make_const_string(a, fields[i]->token.string));
+ }
+
+ ssaValue *v_count = ssa_make_const_int(a, count);
+
+ ssaValue *names = ssa_emit_struct_ep(proc, tag, 1);
+ ssaValue *name_array_elem = ssa_array_elem(proc, name_array);
+
+ ssa_emit_store(proc, ssa_emit_struct_ep(proc, names, 0), name_array_elem);
+ ssa_emit_store(proc, ssa_emit_struct_ep(proc, names, 1), v_count);
+ ssa_emit_store(proc, ssa_emit_struct_ep(proc, names, 2), v_count);
+ }
+ }
+ break;
}
} break;
diff --git a/src/ssa_print.c b/src/ssa_print.c
index bbca6b9db..81842e72a 100644
--- a/src/ssa_print.c
+++ b/src/ssa_print.c
@@ -225,6 +225,9 @@ void ssa_print_type(ssaFileBuffer *f, ssaModule *m, Type *t) {
i64 align_of_union = type_align_of(s, heap_allocator(), t);
ssa_fprintf(f, "{[0 x <%lld x i8>], [%lld x i8]}", align_of_union, size_of_union);
} break;
+ case TypeRecord_Enum:
+ ssa_print_type(f, m, base_enum_type(t));
+ break;
}
} break;
@@ -299,7 +302,7 @@ void ssa_print_compound_element(ssaFileBuffer *f, ssaModule *m, ExactValue v, Ty
}
void ssa_print_exact_value(ssaFileBuffer *f, ssaModule *m, ExactValue value, Type *type) {
- type = base_type(type);
+ type = base_type(base_enum_type(type));
if (is_type_float(type)) {
value = exact_value_to_float(value);
} else if (is_type_integer(type)) {
diff --git a/src/tokenizer.c b/src/tokenizer.c
index 2c42df1c5..a35ed070e 100644
--- a/src/tokenizer.c
+++ b/src/tokenizer.c
@@ -108,6 +108,7 @@ TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \
TOKEN_KIND(Token_struct, "struct"), \
TOKEN_KIND(Token_union, "union"), \
TOKEN_KIND(Token_raw_union, "raw_union"), \
+ TOKEN_KIND(Token_enum, "enum"), \
TOKEN_KIND(Token_vector, "vector"), \
TOKEN_KIND(Token_using, "using"), \
TOKEN_KIND(Token_asm, "asm"), \