when statement; Better entity collection system (for both local and global); Better parsing for record declarations

This commit is contained in:
Ginger Bill
2016-11-29 23:57:06 +00:00
parent b232b9d5ea
commit d9c686b53d
9 changed files with 309 additions and 265 deletions
+205 -89
View File
@@ -20,6 +20,206 @@ gb_inline Type *check_type(Checker *c, AstNode *expression) {
typedef struct DelayedEntity {
Entity *entity;
DeclInfo *decl;
} DelayedEntity;
typedef struct DelayedOtherFields {
Entity **other_fields;
isize other_field_count;
isize other_field_index;
MapEntity *entity_map;
} DelayedOtherFields;
typedef Array(DelayedEntity) DelayedEntities;
void check_local_collect_entities(Checker *c, AstNodeArray nodes, DelayedEntities *delayed_entities, DelayedOtherFields *dof);
void check_local_collect_entities_from_when_stmt(Checker *c, AstNodeWhenStmt *ws, DelayedEntities *delayed_entities, DelayedOtherFields *dof) {
Operand operand = {Addressing_Invalid};
check_expr(c, &operand, ws->cond);
if (operand.mode != Addressing_Invalid && !is_type_boolean(operand.type)) {
error(ast_node_token(ws->cond), "Non-boolean condition in `when` statement");
}
if (operand.mode != Addressing_Constant) {
error(ast_node_token(ws->cond), "Non-constant condition in `when` statement");
}
if (ws->body == NULL || ws->body->kind != AstNode_BlockStmt) {
error(ast_node_token(ws->cond), "Invalid body for `when` statement");
} else {
if (operand.value.kind == ExactValue_Bool &&
operand.value.value_bool) {
check_local_collect_entities(c, ws->body->BlockStmt.stmts, delayed_entities, dof);
} else if (ws->else_stmt) {
switch (ws->else_stmt->kind) {
case AstNode_BlockStmt:
check_local_collect_entities(c, ws->else_stmt->BlockStmt.stmts, delayed_entities, dof);
break;
case AstNode_WhenStmt:
check_local_collect_entities_from_when_stmt(c, &ws->else_stmt->WhenStmt, delayed_entities, dof);
break;
default:
error(ast_node_token(ws->else_stmt), "Invalid `else` statement in `when` statement");
break;
}
}
}
}
// NOTE(bill): The `dof` is for use within records
void check_local_collect_entities(Checker *c, AstNodeArray nodes, DelayedEntities *delayed_entities, DelayedOtherFields *dof) {
for_array(i, nodes) {
AstNode *node = nodes.e[i];
switch (node->kind) {
case_ast_node(ws, WhenStmt, node);
// Will be handled later
case_end;
case_ast_node(cd, ConstDecl, node);
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
isize entity_count = cd->names.count;
isize entity_index = 0;
Entity **entities = gb_alloc_array(c->tmp_allocator, Entity *, entity_count);
for_array(i, cd->values) {
AstNode *name = cd->names.e[i];
AstNode *value = cd->values.e[i];
ExactValue v = {ExactValue_Invalid};
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 = cd->type;
d->init_expr = value;
add_entity_and_decl_info(c, name, e, d);
DelayedEntity delay = {e, d};
array_add(delayed_entities, delay);
}
isize lhs_count = cd->names.count;
isize rhs_count = cd->values.count;
// TODO(bill): Better error messages or is this good enough?
if (rhs_count == 0 && cd->type == NULL) {
error(ast_node_token(node), "Missing type or initial expression");
} else if (lhs_count < rhs_count) {
error(ast_node_token(node), "Extra initial expression");
}
if (dof != NULL) {
// NOTE(bill): Within a record
for_array(i, cd->names) {
AstNode *name = cd->names.e[i];
Entity *e = entities[i];
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);
}
}
}
gb_temp_arena_memory_end(tmp);
case_end;
case_ast_node(pd, ProcDecl, node);
// NOTE(bill): This must be handled here so it has access to the parent scope stuff
// e.g. using
Entity *e = make_entity_procedure(c->allocator, c->context.scope, pd->name->Ident, NULL);
e->identifier = pd->name;
DeclInfo *d = make_declaration_info(c->allocator, e->scope);
d->proc_decl = node;
add_entity_and_decl_info(c, pd->name, e, d);
check_entity_decl(c, e, d, NULL, NULL);
case_end;
case_ast_node(td, TypeDecl, node);
Token name_token = td->name->Ident;
Entity *e = make_entity_type_name(c->allocator, c->context.scope, name_token, NULL);
e->identifier = td->name;
DeclInfo *d = make_declaration_info(c->allocator, e->scope);
d->type_expr = td->type;
add_entity_and_decl_info(c, td->name, e, d);
DelayedEntity delay = {e, d};
array_add(delayed_entities, delay);
if (dof != NULL) {
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, td->name, e);
add_entity_use(c, td->name, e);
}
}
case_end;
}
}
// NOTE(bill): `when` stmts need to be handled after the other as the condition may refer to something
// declared after this stmt in source
for_array(i, nodes) {
AstNode *node = nodes.e[i];
switch (node->kind) {
case_ast_node(ws, WhenStmt, node);
check_local_collect_entities_from_when_stmt(c, ws, delayed_entities, dof);
case_end;
}
}
}
void check_scope_decls(Checker *c, AstNodeArray nodes, isize reserve_size, DelayedOtherFields *dof) {
DelayedEntities delayed_entities;
array_init_reserve(&delayed_entities, heap_allocator(), reserve_size);
check_local_collect_entities(c, nodes, &delayed_entities, dof);
for_array(i, delayed_entities) {
DelayedEntity delayed = delayed_entities.e[i];
if (delayed.entity->kind == Entity_TypeName) {
check_entity_decl(c, delayed.entity, delayed.decl, NULL, NULL);
}
}
for_array(i, delayed_entities) {
DelayedEntity delayed = delayed_entities.e[i];
if (delayed.entity->kind == Entity_Constant) {
check_entity_decl(c, delayed.entity, delayed.decl, NULL, NULL);
}
}
array_free(&delayed_entities);
}
bool check_is_assignable_to_using_subtype(Type *dst, Type *src) {
Type *prev_src = src;
// Type *prev_dst = dst;
@@ -247,96 +447,12 @@ void check_fields(Checker *c, AstNode *node, AstNodeArray decls,
isize other_field_index = 0;
Entity *using_index_expr = NULL;
DelayedOtherFields dof = {0};
dof.other_fields = other_fields;
dof.other_field_count = other_field_count;
dof.entity_map = &entity_map;
typedef struct {
Entity *e;
AstNode *t;
} Delay;
Array(Delay) delayed_const; array_init_reserve(&delayed_const, c->tmp_allocator, other_field_count);
Array(Delay) delayed_type; array_init_reserve(&delayed_type, c->tmp_allocator, other_field_count);
for_array(decl_index, decls) {
AstNode *decl = decls.e[decl_index];
if (decl->kind == AstNode_ConstDecl) {
ast_node(cd, ConstDecl, decl);
isize entity_count = cd->names.count;
isize entity_index = 0;
Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count);
for_array(i, cd->values) {
AstNode *name = cd->names.e[i];
AstNode *value = cd->values.e[i];
GB_ASSERT(name->kind == AstNode_Ident);
ExactValue v = {ExactValue_Invalid};
Token name_token = name->Ident;
Entity *e = make_entity_constant(c->allocator, c->context.scope, name_token, NULL, v);
entities[entity_index++] = e;
Delay delay = {e, cd->type};
array_add(&delayed_const, delay);
}
isize lhs_count = cd->names.count;
isize rhs_count = cd->values.count;
// TODO(bill): Better error messages or is this good enough?
if (rhs_count == 0 && cd->type == NULL) {
error(ast_node_token(node), "Missing type or initial expression");
} else if (lhs_count < rhs_count) {
error(ast_node_token(node), "Extra initial expression");
}
for_array(i, cd->names) {
AstNode *name = cd->names.e[i];
Entity *e = entities[i];
Token name_token = name->Ident;
if (str_eq(name_token.string, str_lit("_"))) {
other_fields[other_field_index++] = e;
} else {
HashKey key = hash_string(name_token.string);
if (map_entity_get(&entity_map, key) != NULL) {
// TODO(bill): Scope checking already checks the declaration
error(name_token, "`%.*s` is already declared in this structure", LIT(name_token.string));
} else {
map_entity_set(&entity_map, key, e);
other_fields[other_field_index++] = e;
}
add_entity(c, c->context.scope, name, e);
}
}
} else if (decl->kind == AstNode_TypeDecl) {
ast_node(td, TypeDecl, decl);
Token name_token = td->name->Ident;
Entity *e = make_entity_type_name(c->allocator, c->context.scope, name_token, NULL);
Delay delay = {e, td->type};
array_add(&delayed_type, delay);
if (str_eq(name_token.string, str_lit("_"))) {
other_fields[other_field_index++] = e;
} else {
HashKey key = hash_string(name_token.string);
if (map_entity_get(&entity_map, key) != NULL) {
// TODO(bill): Scope checking already checks the declaration
error(name_token, "`%.*s` is already declared in this structure", LIT(name_token.string));
} else {
map_entity_set(&entity_map, key, e);
other_fields[other_field_index++] = e;
}
add_entity(c, c->context.scope, td->name, e);
add_entity_use(c, td->name, e);
}
}
}
for_array(i, delayed_type) {
check_const_decl(c, delayed_type.e[i].e, delayed_type.e[i].t, NULL);
}
for_array(i, delayed_const) {
check_type_decl(c, delayed_const.e[i].e, delayed_const.e[i].t, NULL, NULL);
}
check_scope_decls(c, decls, 1.2*other_field_count, &dof);
if (node->kind == AstNode_UnionType) {
isize field_index = 0;