diff options
| author | Ginger Bill <bill@gingerbill.org> | 2017-01-01 16:18:50 +0000 |
|---|---|---|
| committer | Ginger Bill <bill@gingerbill.org> | 2017-01-01 16:18:50 +0000 |
| commit | 6fef74317cdd0e403fb913ebe965dc08b3dfb22d (patch) | |
| tree | 9f25deab9276dc60847c0548d1024cfdbc3679d4 /src | |
| parent | 0c6775ca14ced37ac58a03ccad4028e225bda7d6 (diff) | |
Bring back `enum` but using iota
Diffstat (limited to 'src')
| -rw-r--r-- | src/checker/checker.c | 3 | ||||
| -rw-r--r-- | src/checker/expr.c | 140 | ||||
| -rw-r--r-- | src/checker/stmt.c | 15 | ||||
| -rw-r--r-- | src/checker/types.c | 49 | ||||
| -rw-r--r-- | src/parser.c | 14 | ||||
| -rw-r--r-- | src/ssa.c | 43 | ||||
| -rw-r--r-- | src/ssa_print.c | 5 | ||||
| -rw-r--r-- | src/tokenizer.c | 1 |
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); @@ -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"), \ |