Delay importing entities till all other entities are collected

This commit is contained in:
Ginger Bill
2016-11-30 20:46:00 +00:00
parent ab2ca7cf59
commit be8b9bda2f
4 changed files with 141 additions and 293 deletions
+132 -109
View File
@@ -223,21 +223,26 @@ typedef struct CheckerContext {
#define MAP_NAME MapExprInfo
#include "../map.c"
typedef struct DelayedImport {
Scope * parent;
AstNodeImportDecl *decl;
} DelayedImport;
// NOTE(bill): Symbol tables
typedef struct CheckerInfo {
MapTypeAndValue types; // Key: AstNode * | Expression -> Type (and value)
MapEntity definitions; // Key: AstNode * | Identifier -> Entity
MapEntity uses; // Key: AstNode * | Identifier -> Entity
MapScope scopes; // Key: AstNode * | Node -> Scope
MapExprInfo untyped; // Key: AstNode * | Expression -> ExprInfo
MapDeclInfo entities; // Key: Entity *
MapEntity foreign_procs; // Key: String
MapAstFile files; // Key: String (full path)
MapIsize type_info_map; // Key: Type *
isize type_info_count;
Entity * implicit_values[ImplicitValue_Count];
Array(String) foreign_libraries; // For the linker
MapTypeAndValue types; // Key: AstNode * | Expression -> Type (and value)
MapEntity definitions; // Key: AstNode * | Identifier -> Entity
MapEntity uses; // Key: AstNode * | Identifier -> Entity
MapScope scopes; // Key: AstNode * | Node -> Scope
MapExprInfo untyped; // Key: AstNode * | Expression -> ExprInfo
MapDeclInfo entities; // Key: Entity *
MapEntity foreign_procs; // Key: String
MapAstFile files; // Key: String (full path)
MapIsize type_info_map; // Key: Type *
isize type_info_count;
Entity * implicit_values[ImplicitValue_Count];
Array(String) foreign_libraries; // For the linker
} CheckerInfo;
typedef struct Checker {
@@ -248,6 +253,8 @@ typedef struct Checker {
BaseTypeSizes sizes;
Scope * global_scope;
Array(ProcedureInfo) procs; // NOTE(bill): Procedures to check
Array(DelayedImport) delayed_imports;
gbArena arena;
gbArena tmp_arena;
@@ -608,6 +615,7 @@ void init_checker(Checker *c, Parser *parser, BaseTypeSizes sizes) {
array_init(&c->proc_stack, a);
array_init(&c->procs, a);
array_init(&c->delayed_imports, a);
// NOTE(bill): Is this big enough or too small?
isize item_size = gb_max3(gb_size_of(Entity), gb_size_of(Type), gb_size_of(Scope));
@@ -633,6 +641,7 @@ void destroy_checker(Checker *c) {
destroy_scope(c->global_scope);
array_free(&c->proc_stack);
array_free(&c->procs);
array_free(&c->delayed_imports);
gb_arena_free(&c->arena);
}
@@ -1117,106 +1126,17 @@ void check_global_collect_entities(Checker *c, Scope *parent_scope, AstNodeArray
switch (decl->kind) {
case_ast_node(bd, BadDecl, decl);
case_end;
case_ast_node(ws, WhenStmt, decl);
// Will be handled later
case_end;
case_ast_node(id, ImportDecl, decl);
if (!parent_scope->is_file) {
// NOTE(bill): _Should_ be caught by the parser
// TODO(bill): Better error handling if it isn't
continue;
}
HashKey key = hash_string(id->fullpath);
Scope **found = map_scope_get(file_scopes, key);
if (found == NULL) {
for_array(scope_index, file_scopes->entries) {
Scope *scope = file_scopes->entries.e[scope_index].value;
gb_printf_err("%.*s\n", LIT(scope->file->tokenizer.fullpath));
}
gb_printf_err("%.*s(%td:%td)\n", LIT(id->token.pos.file), id->token.pos.line, id->token.pos.column);
GB_PANIC("Unable to find scope for file: %.*s", LIT(id->fullpath));
}
Scope *scope = *found;
if (scope->is_global) {
error(id->token, "Importing a #shared_global_scope is disallowed and unnecessary");
continue;
}
bool previously_added = false;
for_array(import_index, parent_scope->imported) {
Scope *prev = parent_scope->imported.e[import_index];
if (prev == scope) {
previously_added = true;
break;
}
}
if (!previously_added) {
array_add(&parent_scope->imported, scope);
} else {
warning(id->token, "Multiple #import of the same file within this scope");
}
if (str_eq(id->import_name.string, str_lit("."))) {
// NOTE(bill): Add imported entities to this file's scope
for_array(elem_index, scope->elements.entries) {
Entity *e = scope->elements.entries.e[elem_index].value;
if (e->scope == parent_scope) {
continue;
}
// NOTE(bill): Do not add other imported entities
add_entity(c, parent_scope, NULL, e);
if (!id->is_load) { // `#import`ed entities don't get exported
HashKey key = hash_string(e->token.string);
map_entity_set(&parent_scope->implicit, key, e);
}
}
} else {
String import_name = id->import_name.string;
if (import_name.len == 0) {
// NOTE(bill): use file name (without extension) as the identifier
// If it is a valid identifier
String filename = id->fullpath;
isize slash = 0;
isize dot = 0;
for (isize i = filename.len-1; i >= 0; i--) {
u8 c = filename.text[i];
if (c == '/' || c == '\\') {
break;
}
slash = i;
}
filename.text += slash;
filename.len -= slash;
dot = filename.len;
while (dot --> 0) {
u8 c = filename.text[dot];
if (c == '.') {
break;
}
}
filename.len = dot;
if (is_string_an_identifier(filename)) {
import_name = filename;
} else {
error_node(decl,
"File name, %.*s, cannot be as an import name as it is not a valid identifier",
LIT(filename));
}
}
if (import_name.len > 0) {
id->import_name.string = import_name;
Entity *e = make_entity_import_name(c->allocator, parent_scope, id->import_name, t_invalid,
id->fullpath, id->import_name.string,
scope);
add_entity(c, parent_scope, NULL, e);
}
}
DelayedImport di = {parent_scope, id};
array_add(&c->delayed_imports, di);
case_end;
case_ast_node(fl, ForeignLibrary, decl);
if (!parent_scope->is_file) {
@@ -1244,9 +1164,6 @@ void check_global_collect_entities(Checker *c, Scope *parent_scope, AstNodeArray
try_add_foreign_library_path(c, file_str);
case_end;
case_ast_node(ws, WhenStmt, decl);
// Will be handled later
case_end;
case_ast_node(cd, ConstDecl, decl);
for_array(i, cd->values) {
AstNode *name = cd->names.e[i];
@@ -1347,6 +1264,110 @@ void check_global_collect_entities(Checker *c, Scope *parent_scope, AstNodeArray
}
}
void check_import_entities(Checker *c, MapScope *file_scopes) {
for_array(i, c->delayed_imports) {
AstNodeImportDecl *id = c->delayed_imports.e[i].decl;
Scope *parent_scope = c->delayed_imports.e[i].parent;
HashKey key = hash_string(id->fullpath);
Scope **found = map_scope_get(file_scopes, key);
if (found == NULL) {
for_array(scope_index, file_scopes->entries) {
Scope *scope = file_scopes->entries.e[scope_index].value;
gb_printf_err("%.*s\n", LIT(scope->file->tokenizer.fullpath));
}
gb_printf_err("%.*s(%td:%td)\n", LIT(id->token.pos.file), id->token.pos.line, id->token.pos.column);
GB_PANIC("Unable to find scope for file: %.*s", LIT(id->fullpath));
}
Scope *scope = *found;
if (scope->is_global) {
error(id->token, "Importing a #shared_global_scope is disallowed and unnecessary");
continue;
}
bool previously_added = false;
for_array(import_index, parent_scope->imported) {
Scope *prev = parent_scope->imported.e[import_index];
if (prev == scope) {
previously_added = true;
break;
}
}
if (!previously_added) {
array_add(&parent_scope->imported, scope);
} else {
warning(id->token, "Multiple #import of the same file within this scope");
}
if (str_eq(id->import_name.string, str_lit("."))) {
// NOTE(bill): Add imported entities to this file's scope
for_array(elem_index, scope->elements.entries) {
Entity *e = scope->elements.entries.e[elem_index].value;
if (e->scope == parent_scope) {
continue;
}
// NOTE(bill): Do not add other imported entities
add_entity(c, parent_scope, NULL, e);
if (!id->is_load) { // `#import`ed entities don't get exported
HashKey key = hash_string(e->token.string);
map_entity_set(&parent_scope->implicit, key, e);
}
}
} else {
String import_name = id->import_name.string;
if (import_name.len == 0) {
// NOTE(bill): use file name (without extension) as the identifier
// If it is a valid identifier
String filename = id->fullpath;
isize slash = 0;
isize dot = 0;
for (isize i = filename.len-1; i >= 0; i--) {
u8 c = filename.text[i];
if (c == '/' || c == '\\') {
break;
}
slash = i;
}
filename.text += slash;
filename.len -= slash;
dot = filename.len;
while (dot --> 0) {
u8 c = filename.text[dot];
if (c == '.') {
break;
}
}
filename.len = dot;
if (is_string_an_identifier(filename)) {
import_name = filename;
} else {
error(id->token,
"File name, %.*s, cannot be as an import name as it is not a valid identifier",
LIT(filename));
}
}
if (import_name.len > 0) {
id->import_name.string = import_name;
Entity *e = make_entity_import_name(c->allocator, parent_scope, id->import_name, t_invalid,
id->fullpath, id->import_name.string,
scope);
add_entity(c, parent_scope, NULL, e);
}
}
}
}
void check_parsed_files(Checker *c) {
MapScope file_scopes; // Key: String (fullpath)
@@ -1384,6 +1405,8 @@ void check_parsed_files(Checker *c) {
check_global_collect_entities(c, f->scope, f->decls, &file_scopes);
}
check_import_entities(c, &file_scopes);
check_global_entities_by_kind(c, Entity_TypeName);
init_preload_types(c);
add_implicit_value(c, ImplicitValue_context, str_lit("context"), str_lit("__context"), t_context);
+6 -5
View File
@@ -2367,8 +2367,7 @@ AstNode *parse_decl(AstFile *f, AstNodeArray names) {
}
return make_type_decl(f, token, names.e[0], parse_type(f));
} else if (f->curr_token.kind == Token_proc &&
is_mutable == false) {
} else if (f->curr_token.kind == Token_proc && is_mutable == false) {
// NOTE(bill): Procedure declarations
Token proc_token = f->curr_token;
AstNode *name = names.e[0];
@@ -2904,7 +2903,7 @@ AstNode *parse_stmt(AstFile *f) {
}
return make_import_decl(f, s->TagStmt.token, file_path, import_name, os, arch, false);
} else if (str_eq(tag, str_lit("include"))) {
} else if (str_eq(tag, str_lit("load"))) {
String os = {0};
String arch = {0};
// TODO(bill): better error messages
@@ -2912,10 +2911,12 @@ AstNode *parse_stmt(AstFile *f) {
Token import_name = file_path;
import_name.string = str_lit(".");
if (f->curr_proc == NULL) {
return make_import_decl(f, s->TagStmt.token, file_path, import_name, os, arch, true);
}
syntax_error(token, "You cannot use #include within a procedure. This must be done at the file scope");
syntax_error(token, "You cannot use #load within a procedure. This must be done at the file scope");
return make_bad_decl(f, token, file_path);
} else {
@@ -3195,7 +3196,7 @@ void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, AstNodeArray
node->kind != AstNode_BadStmt &&
node->kind != AstNode_EmptyStmt) {
// NOTE(bill): Sanity check
syntax_error_node(node, "Only declarations are allowed at file scope");
syntax_error_node(node, "Only declarations are allowed at file scope %.*s", LIT(ast_node_strings[node->kind]));
} else if (node->kind == AstNode_WhenStmt) {
parse_setup_file_when_stmt(p, f, base_dir, &node->WhenStmt);
} else if (node->kind == AstNode_ImportDecl) {