diff options
| author | Ginger Bill <bill@gingerbill.org> | 2016-12-19 13:18:20 +0000 |
|---|---|---|
| committer | Ginger Bill <bill@gingerbill.org> | 2016-12-19 13:18:20 +0000 |
| commit | ac1566762bdfea1e9cf27dfdad393352398bbab0 (patch) | |
| tree | 9d1b7af076cae8d1323488069c32cb0fb6cf187b /src | |
| parent | f5eeecaca5842e4594ad2ed482c529a683bfb012 (diff) | |
Golang style enumerations with `iota`
Diffstat (limited to 'src')
| -rw-r--r-- | src/checker/checker.c | 80 | ||||
| -rw-r--r-- | src/checker/decl.c | 14 | ||||
| -rw-r--r-- | src/checker/entity.c | 4 | ||||
| -rw-r--r-- | src/checker/expr.c | 88 | ||||
| -rw-r--r-- | src/parser.c | 35 |
5 files changed, 126 insertions, 95 deletions
diff --git a/src/checker/checker.c b/src/checker/checker.c index 3af0c2f8b..14cb62bdb 100644 --- a/src/checker/checker.c +++ b/src/checker/checker.c @@ -196,6 +196,7 @@ typedef struct CheckerContext { Scope * scope; DeclInfo * decl; u32 stmt_state_flags; + ExactValue iota; // Value of `iota` in a constant declaration; Invalid otherwise } CheckerContext; #define MAP_TYPE TypeAndValue @@ -534,8 +535,9 @@ void init_universal_scope(BuildContext *bc) { } // Constants - add_global_constant(a, str_lit("true"), t_untyped_bool, make_exact_value_bool(true)); - add_global_constant(a, str_lit("false"), t_untyped_bool, make_exact_value_bool(false)); + add_global_constant(a, str_lit("true"), t_untyped_bool, make_exact_value_bool(true)); + add_global_constant(a, str_lit("false"), t_untyped_bool, make_exact_value_bool(false)); + add_global_constant(a, str_lit("iota"), t_untyped_integer, make_exact_value_integer(0)); add_global_entity(make_entity_nil(a, str_lit("nil"), t_untyped_nil)); @@ -558,6 +560,7 @@ void init_universal_scope(BuildContext *bc) { t_u8_ptr = make_type_pointer(a, t_u8); t_int_ptr = make_type_pointer(a, t_int); + e_iota = scope_lookup_entity(universal_scope, str_lit("iota")); } @@ -1054,14 +1057,42 @@ void init_preload(Checker *c) { c->done_preload = true; } - - - +void check_arity_match(Checker *c, AstNodeValueSpec *s, AstNodeValueSpec *init); #include "expr.c" #include "decl.c" #include "stmt.c" +void check_arity_match(Checker *c, AstNodeValueSpec *s, AstNodeValueSpec *init) { + isize lhs = s->names.count; + isize rhs = s->values.count; + if (init != NULL) { + rhs = init->values.count; + } + + if (init == NULL && rhs == 0) { + if (s->type == NULL) { + error_node(s->names.e[0], "Missing type or initial expression"); + } + } else if (lhs < rhs) { + if (lhs < s->values.count) { + AstNode *n = s->values.e[lhs]; + gbString str = expr_to_string(n); + error_node(n, "Extra initial expression `%s`", str); + gb_string_free(str); + } else { + error_node(s->names.e[0], "Extra initial expression"); + } + } else if (lhs > rhs && (init != NULL || rhs != 1)) { + AstNode *n = s->names.e[rhs]; + gbString str = expr_to_string(n); + error_node(n, "Missing expression for `%s`", str); + gb_string_free(str); + } +} + + + void check_all_global_entities(Checker *c) { Scope *prev_file = {0}; @@ -1112,8 +1143,11 @@ void check_global_collect_entities_from_file(Checker *c, Scope *parent_scope, As continue; } - for_array(spec_index, gd->specs) { - AstNode *spec = gd->specs.e[spec_index]; + AstNodeValueSpec empty_spec_ = {0}, *empty_spec = &empty_spec_; + AstNodeValueSpec *last = NULL; + + for_array(iota, gd->specs) { + AstNode *spec = gd->specs.e[iota]; switch (spec->kind) { case_ast_node(vs, ValueSpec, spec); switch (vs->keyword) { @@ -1156,34 +1190,40 @@ void check_global_collect_entities_from_file(Checker *c, Scope *parent_scope, As add_entity_and_decl_info(c, name, e, d); } + + check_arity_match(c, vs, NULL); } break; case Token_const: { - for_array(i, vs->values) { + if (vs->type != NULL || vs->values.count > 0) { + last = vs; + } else if (last == NULL) { + last = empty_spec; + } + + for_array(i, vs->names) { AstNode *name = vs->names.e[i]; - AstNode *value = unparen_expr(vs->values.e[i]); if (name->kind != AstNode_Ident) { error_node(name, "A declaration's name must be an identifier, got %.*s", LIT(ast_node_strings[name->kind])); continue; } - ExactValue v = {ExactValue_Invalid}; + ExactValue v = make_exact_value_integer(iota); Entity *e = make_entity_constant(c->allocator, parent_scope, name->Ident, NULL, v); e->identifier = name; + + AstNode *init = NULL; + if (i < last->values.count) { + init = last->values.e[i]; + } + DeclInfo *di = make_declaration_info(c->allocator, e->scope); - di->type_expr = vs->type; - di->init_expr = value; + di->type_expr = last->type; + di->init_expr = init; add_entity_and_decl_info(c, name, e, di); } - isize lhs_count = vs->names.count; - isize rhs_count = vs->values.count; - - if (rhs_count == 0 && vs->type == NULL) { - error_node(decl, "Missing type or initial expression"); - } else if (lhs_count < rhs_count) { - error_node(decl, "Extra initial expression"); - } + check_arity_match(c, vs, last); } break; } case_end; diff --git a/src/checker/decl.c b/src/checker/decl.c index abdeddc8a..603d04c5d 100644 --- a/src/checker/decl.c +++ b/src/checker/decl.c @@ -229,8 +229,9 @@ void check_type_decl(Checker *c, Entity *e, AstNode *type_expr, Type *def) { } } -void check_const_decl(Checker *c, Entity *e, AstNode *type_expr, AstNode *init_expr, Type *named_type) { +void check_const_decl(Checker *c, Entity *e, AstNode *type_expr, AstNode *init, Type *named_type) { GB_ASSERT(e->type == NULL); + GB_ASSERT(e->kind == Entity_Constant); if (e->flags & EntityFlag_Visited) { e->type = t_invalid; @@ -238,6 +239,10 @@ void check_const_decl(Checker *c, Entity *e, AstNode *type_expr, AstNode *init_e } e->flags |= EntityFlag_Visited; + GB_ASSERT(c->context.iota.kind == ExactValue_Invalid); + c->context.iota = e->Constant.value; + e->Constant.value = (ExactValue){0}; + if (type_expr) { Type *t = check_type(c, type_expr); if (!is_type_constant_type(t)) { @@ -245,18 +250,19 @@ void check_const_decl(Checker *c, Entity *e, AstNode *type_expr, AstNode *init_e error_node(type_expr, "Invalid constant type `%s`", str); gb_string_free(str); e->type = t_invalid; + c->context.iota = (ExactValue){0}; return; } e->type = t; } Operand operand = {0}; - if (init_expr) { - // check_expr_or_type(c, &operand, init_expr); - check_expr(c, &operand, init_expr); + if (init != NULL) { + check_expr(c, &operand, init); } check_init_constant(c, e, &operand); + c->context.iota = (ExactValue){0}; } diff --git a/src/checker/entity.c b/src/checker/entity.c index 9da334ea8..d02268837 100644 --- a/src/checker/entity.c +++ b/src/checker/entity.c @@ -83,6 +83,10 @@ struct Entity { }; }; + +Entity *e_iota = NULL; + + Entity *alloc_entity(gbAllocator a, EntityKind kind, Scope *scope, Token token, Type *type) { Entity *entity = gb_alloc_item(a, Entity); entity->kind = kind; diff --git a/src/checker/expr.c b/src/checker/expr.c index 3f8215f55..9562473ca 100644 --- a/src/checker/expr.c +++ b/src/checker/expr.c @@ -81,8 +81,11 @@ void check_local_collect_entities(Checker *c, AstNodeArray nodes, DelayedEntitie // Will be handled later case_end; case_ast_node(gd, GenericDecl, node); - for_array(spec_index, gd->specs) { - AstNode *spec = gd->specs.e[spec_index]; + AstNodeValueSpec empty_spec_ = {0}, *empty_spec = &empty_spec_; + AstNodeValueSpec *last = NULL; + + for_array(iota, gd->specs) { + AstNode *spec = gd->specs.e[iota]; switch (spec->kind) { case_ast_node(vs, ValueSpec, spec); switch (vs->keyword) { @@ -90,73 +93,38 @@ void check_local_collect_entities(Checker *c, AstNodeArray nodes, DelayedEntitie break; case Token_const: { - gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena); - - isize entity_count = vs->names.count; - isize entity_index = 0; - Entity **entities = gb_alloc_array(c->tmp_allocator, Entity *, entity_count); + if (vs->type != NULL || vs->values.count > 0) { + last = vs; + } else if (last == NULL) { + last = empty_spec; + } - for_array(i, vs->values) { + for_array(i, vs->names) { AstNode *name = vs->names.e[i]; - AstNode *value = unparen_expr(vs->values.e[i]); if (name->kind != AstNode_Ident) { error_node(name, "A declaration's name must be an identifier, got %.*s", LIT(ast_node_strings[name->kind])); - entities[entity_index++] = NULL; continue; } - ExactValue v = {ExactValue_Invalid}; + ExactValue v = make_exact_value_integer(iota); Entity *e = make_entity_constant(c->allocator, c->context.scope, name->Ident, NULL, v); e->identifier = name; - entities[entity_index++] = e; - DeclInfo *d = make_declaration_info(c->allocator, e->scope); - d->type_expr = vs->type; - d->init_expr = value; - add_entity_and_decl_info(c, name, e, d); - DelayedEntity delay = {name, e, d}; - array_add(delayed_entities, delay); - } - - isize lhs_count = vs->names.count; - isize rhs_count = vs->values.count; + AstNode *init = NULL; + if (i < last->values.count) { + init = last->values.e[i]; + } - // TODO(bill): Better error messages or is this good enough? - if (rhs_count == 0 && vs->type == NULL) { - error_node(node, "Missing type or initial expression"); - } else if (lhs_count < rhs_count) { - error_node(node, "Extra initial expression"); - } + DeclInfo *di = make_declaration_info(c->allocator, e->scope); + di->type_expr = last->type; + di->init_expr = init; + add_entity_and_decl_info(c, name, e, di); - if (dof != NULL) { - // NOTE(bill): Within a record - for_array(i, vs->names) { - Entity *e = entities[i]; - if (e == NULL) { - continue; - } - AstNode *name = vs->names.e[i]; - if (name->kind != AstNode_Ident) { - continue; - } - Token name_token = name->Ident; - if (str_eq(name_token.string, str_lit("_"))) { - dof->other_fields[dof->other_field_index++] = e; - } else { - HashKey key = hash_string(name_token.string); - if (map_entity_get(dof->entity_map, key) != NULL) { - // TODO(bill): Scope checking already checks the declaration - error(name_token, "`%.*s` is already declared in this record", LIT(name_token.string)); - } else { - map_entity_set(dof->entity_map, key, e); - dof->other_fields[dof->other_field_index++] = e; - } - add_entity(c, c->context.scope, name, e); - } - } + DelayedEntity delay = {name, e, di}; + array_add(delayed_entities, delay); } - gb_temp_arena_memory_end(tmp); + check_arity_match(c, vs, last); } break; } case_end; @@ -1097,7 +1065,15 @@ void check_identifier(Checker *c, Operand *o, AstNode *n, Type *named_type) { o->type = t_invalid; return; } - o->value = e->Constant.value; + if (e == e_iota) { + if (c->context.iota.kind == ExactValue_Invalid) { + error(e->token, "Use of `iota` outside a constant declaration is not allowed"); + return; + } + o->value = c->context.iota; + } else { + o->value = e->Constant.value; + } if (o->value.kind == ExactValue_Invalid) { return; } diff --git a/src/parser.c b/src/parser.c index f8460e824..dd4f2a8c5 100644 --- a/src/parser.c +++ b/src/parser.c @@ -1921,7 +1921,7 @@ AstNode *parse_type(AstFile *f) { } -#define PARSE_SPEC_PROC(name) AstNode *(name)(AstFile *f, TokenKind keyword) +#define PARSE_SPEC_PROC(name) AstNode *(name)(AstFile *f, TokenKind keyword, isize index) typedef PARSE_SPEC_PROC(*ParserSpecProc); @@ -1933,9 +1933,12 @@ AstNode *parse_generic_decl(AstFile *f, TokenKind keyword, ParserSpecProc spec_p open = expect_token(f, Token_OpenParen); array_init(&specs, heap_allocator()); - while (f->curr_token.kind != Token_CloseParen && - f->curr_token.kind != Token_EOF) { - AstNode *spec = spec_proc(f, keyword); + + for (isize index = 0; + f->curr_token.kind != Token_CloseParen && + f->curr_token.kind != Token_EOF; + index++) { + AstNode *spec = spec_proc(f, keyword, index); array_add(&specs, spec); expect_semicolon(f, spec); } @@ -1943,7 +1946,7 @@ AstNode *parse_generic_decl(AstFile *f, TokenKind keyword, ParserSpecProc spec_p close = expect_token(f, Token_CloseParen); } else { array_init_reserve(&specs, heap_allocator(), 1); - array_add(&specs, spec_proc(f, keyword)); + array_add(&specs, spec_proc(f, keyword, 0)); } return make_generic_decl(f, token, open, close, specs, 0, false); @@ -1963,17 +1966,19 @@ PARSE_SPEC_PROC(parse_value_spec) { syntax_error(f->curr_token, "Too many values on the right hand side of the declaration"); } - if (keyword == Token_const) { - if (values.count < names.count) { - syntax_error(f->curr_token, "All constant declarations must be defined"); - } else if (values.count == 0) { - syntax_error(f->curr_token, "Expected an expression for this declaration"); + switch (keyword) { + case Token_var: + if (type == NULL && values.count == 0 && names.count > 0) { + syntax_error(f->curr_token, "Missing type or initialization"); + return make_bad_decl(f, f->curr_token, f->curr_token); } - } - - if (type == NULL && values.count == 0 && names.count > 0) { - syntax_error(f->curr_token, "Missing type or initialization"); - return make_bad_decl(f, f->curr_token, f->curr_token); + break; + case Token_const: + if (values.count == 0 && (index == 0 || type != NULL)) { + syntax_error(f->curr_token, "Missing constant value"); + return make_bad_decl(f, f->curr_token, f->curr_token); + } + break; } // TODO(bill): Fix this so it does not require it |