mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-31 11:50:07 +00:00
when statement; Better entity collection system (for both local and global); Better parsing for record declarations
This commit is contained in:
+205
-89
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user