aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGinger Bill <bill@gingerbill.org>2016-12-19 13:18:20 +0000
committerGinger Bill <bill@gingerbill.org>2016-12-19 13:18:20 +0000
commitac1566762bdfea1e9cf27dfdad393352398bbab0 (patch)
tree9d1b7af076cae8d1323488069c32cb0fb6cf187b /src
parentf5eeecaca5842e4594ad2ed482c529a683bfb012 (diff)
Golang style enumerations with `iota`
Diffstat (limited to 'src')
-rw-r--r--src/checker/checker.c80
-rw-r--r--src/checker/decl.c14
-rw-r--r--src/checker/entity.c4
-rw-r--r--src/checker/expr.c88
-rw-r--r--src/parser.c35
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