From e6e0aba8c31f5c9ccec7d0113d63f5104df74ea1 Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Sun, 10 Sep 2017 15:17:37 +0100 Subject: [PATCH] Remove `when` suffixes; Implement file scope `when` statement, evaluated in source order --- core/atomics.odin | 4 +- core/opengl.odin | 11 +- core/os.odin | 6 +- core/sync.odin | 4 +- core/sync_windows.odin | 2 +- core/sys/wgl.odin | 2 +- core/sys/windows.odin | 12 +- examples/demo.odin | 7 +- src/check_expr.cpp | 10 +- src/checker.cpp | 617 +++++++++++++++++++---------------------- src/map.cpp | 11 +- src/parser.cpp | 87 +++--- 12 files changed, 373 insertions(+), 400 deletions(-) diff --git a/core/atomics.odin b/core/atomics.odin index 99655a3dd..6f1f395fa 100644 --- a/core/atomics.odin +++ b/core/atomics.odin @@ -1,7 +1,9 @@ // TODO(bill): Use assembly instead here to implement atomics // Inline vs external file? -import win32 "core:sys/windows.odin" when ODIN_OS == "windows"; +when ODIN_OS == "windows" { + import win32 "core:sys/windows.odin"; +} _ :: compile_assert(ODIN_ARCH == "amd64"); // TODO(bill): x86 version diff --git a/core/opengl.odin b/core/opengl.odin index 7bce13a85..60a8bc1a4 100644 --- a/core/opengl.odin +++ b/core/opengl.odin @@ -1,8 +1,11 @@ -foreign_system_library lib "opengl32.lib" when ODIN_OS == "windows"; -foreign_system_library lib "gl" when ODIN_OS == "linux"; +when ODIN_OS == "windows" do foreign_system_library lib "opengl32.lib"; +when ODIN_OS == "linux" do foreign_system_library lib "gl"; + +when ODIN_OS == "windows" { + import win32 "core:sys/windows.odin"; + import "core:sys/wgl.odin"; +} -import win32 "core:sys/windows.odin" when ODIN_OS == "windows"; -import "core:sys/wgl.odin" when ODIN_OS == "windows"; export "core:opengl_constants.odin"; _ := compile_assert(ODIN_OS != "osx"); diff --git a/core/os.odin b/core/os.odin index ecf0139c9..a2a75f83a 100644 --- a/core/os.odin +++ b/core/os.odin @@ -1,6 +1,6 @@ -export "core:os_windows.odin" when ODIN_OS == "windows"; -export "core:os_x.odin" when ODIN_OS == "osx"; -export "core:os_linux.odin" when ODIN_OS == "linux"; +when ODIN_OS == "windows" do export "core:os_windows.odin"; +when ODIN_OS == "osx" do export "core:os_x.odin"; +when ODIN_OS == "linux" do export "core:os_linux.odin"; write_string :: proc(fd: Handle, str: string) -> (int, Errno) { return write(fd, cast([]u8)str); diff --git a/core/sync.odin b/core/sync.odin index b57023bd1..de45d8f6e 100644 --- a/core/sync.odin +++ b/core/sync.odin @@ -1,2 +1,2 @@ -export "core:sync_windows.odin" when ODIN_OS == "windows"; -export "core:sync_linux.odin" when ODIN_OS == "linux"; +when ODIN_OS == "windows" do export "core:sync_windows.odin"; +when ODIN_OS == "linux" do export "core:sync_linux.odin"; diff --git a/core/sync_windows.odin b/core/sync_windows.odin index 066cc95e8..af4177daa 100644 --- a/core/sync_windows.odin +++ b/core/sync_windows.odin @@ -1,4 +1,4 @@ -import win32 "core:sys/windows.odin" when ODIN_OS == "windows"; +when ODIN_OS == "windows" do import win32 "core:sys/windows.odin"; import "core:atomics.odin"; Semaphore :: struct { diff --git a/core/sys/wgl.odin b/core/sys/wgl.odin index 65241a325..4812a6586 100644 --- a/core/sys/wgl.odin +++ b/core/sys/wgl.odin @@ -1,4 +1,4 @@ -foreign_system_library "opengl32.lib" when ODIN_OS == "windows"; +when ODIN_OS == "windows" do foreign_system_library "opengl32.lib"; using import "core:sys/windows.odin"; diff --git a/core/sys/windows.odin b/core/sys/windows.odin index 29a104c97..7d478d5f1 100644 --- a/core/sys/windows.odin +++ b/core/sys/windows.odin @@ -1,8 +1,10 @@ -foreign_system_library "kernel32.lib" when ODIN_OS == "windows"; -foreign_system_library "user32.lib" when ODIN_OS == "windows"; -foreign_system_library "gdi32.lib" when ODIN_OS == "windows"; -foreign_system_library "winmm.lib" when ODIN_OS == "windows"; -foreign_system_library "shell32.lib" when ODIN_OS == "windows"; +when ODIN_OS == "windows" { + foreign_system_library "kernel32.lib"; + foreign_system_library "user32.lib"; + foreign_system_library "gdi32.lib"; + foreign_system_library "winmm.lib"; + foreign_system_library "shell32.lib"; +} Handle :: rawptr; Hwnd :: Handle; diff --git a/examples/demo.odin b/examples/demo.odin index 8af9bff38..8b772e883 100644 --- a/examples/demo.odin +++ b/examples/demo.odin @@ -1,8 +1,6 @@ import "core:fmt.odin"; import "core:strconv.odin"; import "core:mem.odin"; -import "core:thread.odin" when ODIN_OS == "windows"; -import win32 "core:sys/windows.odin" when ODIN_OS == "windows"; import "core:atomics.odin"; import "core:bits.odin"; import "core:hash.odin"; @@ -17,6 +15,11 @@ import "core:types.odin"; import "core:utf8.odin"; import "core:utf16.odin"; +when ODIN_OS == "windows" { + import "core:thread.odin"; + import win32 "core:sys/windows.odin"; +} + general_stuff :: proc() { { // `do` for inline statmes rather than block foo :: proc() do fmt.println("Foo!"); diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 0a361d9d3..db8aef9f9 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -98,7 +98,7 @@ void check_scope_decls(Checker *c, Array nodes, isize reserve_size) { Scope *s = c->context.scope; GB_ASSERT(s->file == nullptr); - check_collect_entities(c, nodes, false); + check_collect_entities(c, nodes); for_array(i, s->elements.entries) { Entity *e = s->elements.entries[i].value; @@ -995,14 +995,14 @@ Array check_fields(Checker *c, AstNode *node, Array decls, array_init(&fields, heap_allocator(), init_field_capacity); Map entity_map = {}; - map_init_with_reserve(&entity_map, c->tmp_allocator, 2*init_field_capacity); + map_init(&entity_map, c->tmp_allocator, 2*init_field_capacity); if (node != nullptr) { GB_ASSERT(node->kind != AstNode_UnionType); } - check_collect_entities(c, decls, false); + check_collect_entities(c, decls); for_array(i, c->context.scope->elements.entries) { Entity *e = c->context.scope->elements.entries[i].value; DeclInfo *d = nullptr; @@ -1442,7 +1442,7 @@ void check_enum_type(Checker *c, Type *enum_type, Type *named_type, AstNode *nod enum_type->Enum.base_type = base_type; Map entity_map = {}; // Key: String - map_init_with_reserve(&entity_map, c->tmp_allocator, 2*(et->fields.count)); + map_init(&entity_map, c->tmp_allocator, 2*(et->fields.count)); Array fields = {}; array_init(&fields, c->allocator, et->fields.count); @@ -1562,7 +1562,7 @@ void check_bit_field_type(Checker *c, Type *bit_field_type, AstNode *node) { defer (gb_temp_arena_memory_end(tmp)); Map entity_map = {}; // Key: String - map_init_with_reserve(&entity_map, c->tmp_allocator, 2*(bft->fields.count)); + map_init(&entity_map, c->tmp_allocator, 2*(bft->fields.count)); isize field_count = 0; Entity **fields = gb_alloc_array(c->allocator, Entity *, bft->fields.count); diff --git a/src/checker.cpp b/src/checker.cpp index be6d86328..7a4e1f2ce 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -342,6 +342,7 @@ struct CheckerContext { DeclInfo * curr_proc_decl; AstNode * curr_foreign_library; + bool allow_file_when_statement; bool allow_polymorphic_types; bool no_polymorphic_errors; Scope * polymorphic_scope; @@ -380,8 +381,7 @@ struct Checker { Scope * global_scope; // NOTE(bill): Procedures to check Map procs; // Key: DeclInfo * - Array delayed_imports; - Array delayed_foreign_libraries; + Map file_scopes; // Key: String (fullpath) Pool pool; gbAllocator allocator; @@ -870,8 +870,6 @@ void init_checker(Checker *c, Parser *parser) { array_init(&c->proc_stack, a); map_init(&c->procs, a); - array_init(&c->delayed_imports, a); - array_init(&c->delayed_foreign_libraries, 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)); @@ -892,6 +890,8 @@ void init_checker(Checker *c, Parser *parser) { c->global_scope = create_scope(universal_scope, c->allocator); c->context.scope = c->global_scope; + + map_init(&c->file_scopes, heap_allocator()); } void destroy_checker(Checker *c) { @@ -899,11 +899,11 @@ void destroy_checker(Checker *c) { destroy_scope(c->global_scope); array_free(&c->proc_stack); map_destroy(&c->procs); - array_free(&c->delayed_imports); - array_free(&c->delayed_foreign_libraries); pool_destroy(&c->pool); gb_arena_free(&c->tmp_arena); + + map_destroy(&c->file_scopes); // gb_arena_free(&c->arena); } @@ -1615,8 +1615,9 @@ void init_preload(Checker *c) { bool check_arity_match(Checker *c, AstNodeValueDecl *vd, bool is_global = false); -void check_collect_entities(Checker *c, Array nodes, bool is_file_scope); -void check_collect_entities_from_when_stmt(Checker *c, AstNodeWhenStmt *ws, bool is_file_scope); +void check_collect_entities(Checker *c, Array nodes); +void check_collect_entities_from_when_stmt(Checker *c, AstNodeWhenStmt *ws); +void check_delayed_file_import_entities(Checker *c, AstNode *decl); bool check_is_entity_overloaded(Entity *e) { if (e->kind != Entity_Procedure) { @@ -1772,7 +1773,7 @@ bool check_arity_match(Checker *c, AstNodeValueDecl *vd, bool is_global) { return true; } -void check_collect_entities_from_when_stmt(Checker *c, AstNodeWhenStmt *ws, bool is_file_scope) { +void check_collect_entities_from_when_stmt(Checker *c, AstNodeWhenStmt *ws) { Operand operand = {Addressing_Invalid}; check_expr(c, &operand, ws->cond); if (operand.mode != Addressing_Invalid && !is_type_boolean(operand.type)) { @@ -1786,14 +1787,14 @@ void check_collect_entities_from_when_stmt(Checker *c, AstNodeWhenStmt *ws, bool } else { if (operand.value.kind == ExactValue_Bool && operand.value.value_bool) { - check_collect_entities(c, ws->body->BlockStmt.stmts, is_file_scope); + check_collect_entities(c, ws->body->BlockStmt.stmts); } else if (ws->else_stmt) { switch (ws->else_stmt->kind) { case AstNode_BlockStmt: - check_collect_entities(c, ws->else_stmt->BlockStmt.stmts, is_file_scope); + check_collect_entities(c, ws->else_stmt->BlockStmt.stmts); break; case AstNode_WhenStmt: - check_collect_entities_from_when_stmt(c, &ws->else_stmt->WhenStmt, is_file_scope); + check_collect_entities_from_when_stmt(c, &ws->else_stmt->WhenStmt); break; default: error(ws->else_stmt, "Invalid `else` statement in `when` statement"); @@ -1804,14 +1805,7 @@ void check_collect_entities_from_when_stmt(Checker *c, AstNodeWhenStmt *ws, bool } // NOTE(bill): If file_scopes == nullptr, this will act like a local scope -void check_collect_entities(Checker *c, Array nodes, bool is_file_scope) { - // NOTE(bill): File scope and local scope are different kinds of scopes - if (is_file_scope) { - GB_ASSERT(c->context.scope->is_file); - } else { - GB_ASSERT(!c->context.scope->is_file); - } - +void check_collect_entities(Checker *c, Array nodes) { for_array(decl_index, nodes) { AstNode *decl = nodes[decl_index]; if (!is_ast_node_decl(decl) && !is_ast_node_when_stmt(decl)) { @@ -1823,11 +1817,7 @@ void check_collect_entities(Checker *c, Array nodes, bool is_file_sco case_end; case_ast_node(ws, WhenStmt, decl); - if (c->context.scope->is_file) { - error(decl, "`when` statements are not allowed at file scope"); - } else { - // Will be handled later - } + // Will be handled later case_end; case_ast_node(vd, ValueDecl, decl); @@ -1975,8 +1965,9 @@ void check_collect_entities(Checker *c, Array nodes, bool is_file_sco // TODO(bill): Better error handling if it isn't continue; } - DelayedDecl di = {c->context.scope, decl}; - array_add(&c->delayed_imports, di); + if (c->context.allow_file_when_statement) { + check_delayed_file_import_entities(c, decl); + } case_end; case_ast_node(id, ExportDecl, decl); @@ -1986,8 +1977,9 @@ void check_collect_entities(Checker *c, Array nodes, bool is_file_sco // TODO(bill): Better error handling if it isn't continue; } - DelayedDecl di = {c->context.scope, decl}; - array_add(&c->delayed_imports, di); + if (c->context.allow_file_when_statement) { + check_delayed_file_import_entities(c, decl); + } case_end; case_ast_node(fl, ForeignLibraryDecl, decl); @@ -1997,22 +1989,9 @@ void check_collect_entities(Checker *c, Array nodes, bool is_file_sco // TODO(bill): Better error handling if it isn't continue; } - - if (fl->cond != nullptr) { - Operand operand = {Addressing_Invalid}; - check_expr(c, &operand, fl->cond); - if (operand.mode != Addressing_Constant || !is_type_boolean(operand.type)) { - error(fl->cond, "Non-constant boolean `when` condition"); - continue; - } - if (operand.value.kind == ExactValue_Bool && - !operand.value.value_bool) { - continue; - } + if (c->context.allow_file_when_statement) { + check_delayed_file_import_entities(c, decl); } - - DelayedDecl di = {c->context.scope, decl}; - array_add(&c->delayed_foreign_libraries, di); case_end; case_ast_node(fb, ForeignBlockDecl, decl); @@ -2024,7 +2003,7 @@ void check_collect_entities(Checker *c, Array nodes, bool is_file_sco CheckerContext prev_context = c->context; c->context.curr_foreign_library = foreign_library; - check_collect_entities(c, fb->decls, is_file_scope); + check_collect_entities(c, fb->decls); c->context = prev_context; case_end; @@ -2038,12 +2017,12 @@ void check_collect_entities(Checker *c, Array nodes, bool is_file_sco // NOTE(bill): `when` stmts need to be handled after the other as the condition may refer to something // declared after this stmt in source - if (!c->context.scope->is_file) { + if (!c->context.scope->is_file || c->context.allow_file_when_statement) { for_array(i, nodes) { AstNode *node = nodes[i]; switch (node->kind) { case_ast_node(ws, WhenStmt, node); - check_collect_entities_from_when_stmt(c, ws, is_file_scope); + check_collect_entities_from_when_stmt(c, ws); case_end; } } @@ -2196,7 +2175,6 @@ void import_graph_node_set_remove(ImportGraphNodeSet *s, ImportGraphNode *n) { struct ImportGraphNode { Scope * scope; - Array decls; // AstNodeImportDecl or AstNodeExportDecl String path; isize file_id; ImportGraphNodeSet pred; @@ -2210,7 +2188,6 @@ ImportGraphNode *import_graph_node_create(gbAllocator a, Scope *scope) { n->scope = scope; n->path = scope->file->tokenizer.fullpath; n->file_id = scope->file->id; - array_init(&n->decls, heap_allocator()); return n; } @@ -2218,7 +2195,6 @@ ImportGraphNode *import_graph_node_create(gbAllocator a, Scope *scope) { void import_graph_node_destroy(ImportGraphNode *n, gbAllocator a) { import_graph_node_set_destroy(&n->pred); import_graph_node_set_destroy(&n->succ); - array_free(&n->decls); gb_free(a, n); } @@ -2256,7 +2232,103 @@ GB_COMPARE_PROC(ast_node_cmp) { } -Array generate_import_dependency_graph(Checker *c, Map *file_scopes) { +void add_import_dependency_node(Checker *c, AstNode *decl, Map *M) { + Scope *parent_file_scope = decl->file->scope; + + switch (decl->kind) { + case_ast_node(id, ImportDecl, decl); + String path = id->fullpath; + HashKey key = hash_string(path); + Scope **found = map_get(&c->file_scopes, key); + if (found == nullptr) { + for_array(scope_index, c->file_scopes.entries) { + Scope *scope = c->file_scopes.entries[scope_index].value; + gb_printf_err("%.*s\n", LIT(scope->file->tokenizer.fullpath)); + } + Token token = ast_node_token(decl); + gb_printf_err("%.*s(%td:%td)\n", LIT(token.pos.file), token.pos.line, token.pos.column); + GB_PANIC("Unable to find scope for file: %.*s", LIT(path)); + } + Scope *scope = *found; + GB_ASSERT(scope != nullptr); + + ImportGraphNode *m = nullptr; + ImportGraphNode *n = nullptr; + + ImportGraphNode **found_node = map_get(M, hash_pointer(parent_file_scope)); + GB_ASSERT(found_node != nullptr); + m = *found_node; + + found_node = map_get(M, hash_pointer(scope)); + GB_ASSERT(found_node != nullptr); + n = *found_node; + + if (id->is_using) { + import_graph_node_set_add(&n->pred, m); + import_graph_node_set_add(&m->succ, n); + ptr_set_add(&m->scope->imported, n->scope); + } + case_end; + + + case_ast_node(ed, ExportDecl, decl); + String path = ed->fullpath; + HashKey key = hash_string(path); + Scope **found = map_get(&c->file_scopes, key); + if (found == nullptr) { + for_array(scope_index, c->file_scopes.entries) { + Scope *scope = c->file_scopes.entries[scope_index].value; + gb_printf_err("%.*s\n", LIT(scope->file->tokenizer.fullpath)); + } + Token token = ast_node_token(decl); + gb_printf_err("%.*s(%td:%td)\n", LIT(token.pos.file), token.pos.line, token.pos.column); + GB_PANIC("Unable to find scope for file: %.*s", LIT(path)); + } + Scope *scope = *found; + GB_ASSERT(scope != nullptr); + + ImportGraphNode *m = nullptr; + ImportGraphNode *n = nullptr; + + ImportGraphNode **found_node = map_get(M, hash_pointer(parent_file_scope)); + GB_ASSERT(found_node != nullptr); + m = *found_node; + + found_node = map_get(M, hash_pointer(scope)); + GB_ASSERT(found_node != nullptr); + n = *found_node; + + import_graph_node_set_add(&n->pred, m); + import_graph_node_set_add(&m->succ, n); + ptr_set_add(&m->scope->imported, n->scope); + case_end; + + case_ast_node(ws, WhenStmt, decl); + if (ws->body != nullptr) { + auto stmts = ws->body->BlockStmt.stmts; + for_array(i, stmts) { + add_import_dependency_node(c, stmts[i], M); + } + } + + if (ws->else_stmt != nullptr) { + switch (ws->else_stmt->kind) { + case AstNode_BlockStmt: { + auto stmts = ws->else_stmt->BlockStmt.stmts; + for_array(i, stmts) { + add_import_dependency_node(c, stmts[i], M); + } + } break; + case AstNode_WhenStmt: + add_import_dependency_node(c, ws->else_stmt, M); + break; + } + } + case_end; + } +} + +Array generate_import_dependency_graph(Checker *c) { gbAllocator a = heap_allocator(); Map M = {}; // Key: Scope * @@ -2270,84 +2342,12 @@ Array generate_import_dependency_graph(Checker *c, Mapdelayed_imports) { - Scope *parent = c->delayed_imports[i].parent; - AstNode *decl = c->delayed_imports[i].decl; - GB_ASSERT(parent->is_file); - - switch (decl->kind) { - case_ast_node(id, ImportDecl, decl); - String path = id->fullpath; - HashKey key = hash_string(path); - Scope **found = map_get(file_scopes, key); - if (found == nullptr) { - for_array(scope_index, file_scopes->entries) { - Scope *scope = file_scopes->entries[scope_index].value; - gb_printf_err("%.*s\n", LIT(scope->file->tokenizer.fullpath)); - } - Token token = ast_node_token(decl); - gb_printf_err("%.*s(%td:%td)\n", LIT(token.pos.file), token.pos.line, token.pos.column); - GB_PANIC("Unable to find scope for file: %.*s", LIT(path)); - } - Scope *scope = *found; - GB_ASSERT(scope != nullptr); - - ImportGraphNode *m = nullptr; - ImportGraphNode *n = nullptr; - - ImportGraphNode **found_node = map_get(&M, hash_pointer(parent)); - GB_ASSERT(found_node != nullptr); - m = *found_node; - - found_node = map_get(&M, hash_pointer(scope)); - GB_ASSERT(found_node != nullptr); - n = *found_node; - - array_add(&m->decls, decl); - - if (id->is_using) { - import_graph_node_set_add(&n->pred, m); - import_graph_node_set_add(&m->succ, n); - ptr_set_add(&m->scope->imported, n->scope); - } - case_end; - - - case_ast_node(ed, ExportDecl, decl); - String path = ed->fullpath; - HashKey key = hash_string(path); - Scope **found = map_get(file_scopes, key); - if (found == nullptr) { - for_array(scope_index, file_scopes->entries) { - Scope *scope = file_scopes->entries[scope_index].value; - gb_printf_err("%.*s\n", LIT(scope->file->tokenizer.fullpath)); - } - Token token = ast_node_token(decl); - gb_printf_err("%.*s(%td:%td)\n", LIT(token.pos.file), token.pos.line, token.pos.column); - GB_PANIC("Unable to find scope for file: %.*s", LIT(path)); - } - Scope *scope = *found; - GB_ASSERT(scope != nullptr); - - ImportGraphNode *m = nullptr; - ImportGraphNode *n = nullptr; - - ImportGraphNode **found_node = map_get(&M, hash_pointer(parent)); - GB_ASSERT(found_node != nullptr); - m = *found_node; - - found_node = map_get(&M, hash_pointer(scope)); - GB_ASSERT(found_node != nullptr); - n = *found_node; - - array_add(&m->decls, decl); - - import_graph_node_set_add(&n->pred, m); - import_graph_node_set_add(&m->succ, n); - ptr_set_add(&m->scope->imported, n->scope); - case_end; + for_array(i, c->parser->files) { + AstFile *f = c->parser->files[i]; + for_array(j, f->decls) { + AstNode *decl = f->decls[j]; + add_import_dependency_node(c, decl, &M); } } @@ -2355,10 +2355,7 @@ Array generate_import_dependency_graph(Checker *c, Mapvalue; - gb_sort_array(n->decls.data, n->decls.count, ast_node_cmp); - array_add(&G, n); + array_add(&G, M.entries[i].value); } for_array(i, G) { @@ -2413,8 +2410,154 @@ Array find_import_path(Map *file_scopes, Scope *start, Scope * return empty_path; } -void check_import_entities(Checker *c, Map *file_scopes) { - Array dep_graph = generate_import_dependency_graph(c, file_scopes); +void check_delayed_file_import_entities(Checker *c, AstNode *decl) { + GB_ASSERT(c->context.allow_file_when_statement); + + Scope *parent_scope = c->context.scope; + GB_ASSERT(parent_scope->is_file); + + switch (decl->kind) { + case_ast_node(ws, WhenStmt, decl); + check_collect_entities_from_when_stmt(c, ws); + case_end; + + case_ast_node(id, ImportDecl, decl); + Token token = id->relpath; + HashKey key = hash_string(id->fullpath); + Scope **found = map_get(&c->file_scopes, key); + if (found == nullptr) { + for_array(scope_index, c->file_scopes.entries) { + Scope *scope = c->file_scopes.entries[scope_index].value; + gb_printf_err("%.*s\n", LIT(scope->file->tokenizer.fullpath)); + } + gb_printf_err("%.*s(%td:%td)\n", LIT(token.pos.file), token.pos.line, token.pos.column); + GB_PANIC("Unable to find scope for file: %.*s", LIT(id->fullpath)); + } + Scope *scope = *found; + + if (scope->is_global) { + error(token, "Importing a #shared_global_scope is disallowed and unnecessary"); + return; + } + + if (ptr_set_exists(&parent_scope->imported, scope)) { + // error(token, "Multiple import of the same file within this scope"); + } else { + ptr_set_add(&parent_scope->imported, scope); + } + + scope->has_been_imported = true; + + if (id->is_using) { + if (parent_scope->is_global) { + error(id->import_name, "#shared_global_scope imports cannot use using"); + } else { + // NOTE(bill): Add imported entities to this file's scope + for_array(elem_index, scope->elements.entries) { + Entity *e = scope->elements.entries[elem_index].value; + if (e->scope == parent_scope) return; + + if (!is_entity_kind_exported(e->kind)) { + return; + } + if (is_entity_exported(e)) { + // TODO(bill): Should these entities be imported but cause an error when used? + bool ok = add_entity(c, parent_scope, e->identifier, e); + if (ok) map_set(&parent_scope->implicit, hash_entity(e), true); + } + } + } + } else { + String import_name = path_to_entity_name(id->import_name.string, id->fullpath); + if (is_blank_ident(import_name)) { + error(token, "File name, %.*s, cannot be use as an import name as it is not a valid identifier", LIT(id->import_name.string)); + } else { + GB_ASSERT(id->import_name.pos.line != 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, nullptr, e); + } + } + case_end; + + case_ast_node(ed, ExportDecl, decl); + Token token = ed->relpath; + HashKey key = hash_string(ed->fullpath); + Scope **found = map_get(&c->file_scopes, key); + if (found == nullptr) { + for_array(scope_index, c->file_scopes.entries) { + Scope *scope = c->file_scopes.entries[scope_index].value; + gb_printf_err("%.*s\n", LIT(scope->file->tokenizer.fullpath)); + } + gb_printf_err("%.*s(%td:%td)\n", LIT(token.pos.file), token.pos.line, token.pos.column); + GB_PANIC("Unable to find scope for file: %.*s", LIT(ed->fullpath)); + } + Scope *scope = *found; + + if (scope->is_global) { + error(token, "Exporting a #shared_global_scope is disallowed and unnecessary"); + return; + } + + if (ptr_set_exists(&parent_scope->imported, scope)) { + // error(token, "Multiple import of the same file within this scope"); + } else { + ptr_set_add(&parent_scope->imported, scope); + } + + scope->has_been_imported = true; + if (parent_scope->is_global) { + error(decl, "`export` cannot be used on #shared_global_scope"); + } else { + // NOTE(bill): Add imported entities to this file's scope + for_array(elem_index, scope->elements.entries) { + Entity *e = scope->elements.entries[elem_index].value; + if (e->scope == parent_scope) return; + + if (is_entity_kind_exported(e->kind)) { + add_entity(c, parent_scope, e->identifier, e); + } + } + } + case_end; + + case_ast_node(fl, ForeignLibraryDecl, decl); + String file_str = fl->filepath.string; + String base_dir = fl->base_dir; + + if (fl->token.kind == Token_foreign_library) { + gbAllocator a = heap_allocator(); // TODO(bill): Change this allocator + + String rel_path = get_fullpath_relative(a, base_dir, file_str); + String import_file = rel_path; + if (!gb_file_exists(cast(char *)rel_path.text)) { // NOTE(bill): This should be null terminated + String abs_path = get_fullpath_core(a, file_str); + if (gb_file_exists(cast(char *)abs_path.text)) { + import_file = abs_path; + } + } + file_str = import_file; + } + + String library_name = path_to_entity_name(fl->library_name.string, file_str); + if (is_blank_ident(library_name)) { + error(decl, "File name, %.*s, cannot be as a library name as it is not a valid identifier", LIT(fl->library_name.string)); + } else { + GB_ASSERT(fl->library_name.pos.line != 0); + fl->library_name.string = library_name; + Entity *e = make_entity_library_name(c->allocator, parent_scope, fl->library_name, t_invalid, + file_str, library_name); + add_entity(c, parent_scope, nullptr, e); + } + case_end; + } +} + +void check_import_entities(Checker *c) { + Array dep_graph = generate_import_dependency_graph(c); defer ({ for_array(i, dep_graph) { import_graph_node_destroy(dep_graph[i], heap_allocator()); @@ -2439,7 +2582,7 @@ void check_import_entities(Checker *c, Map *file_scopes) { Scope *s = n->scope; if (n->dep_count > 0) { - auto path = find_import_path(file_scopes, s, s); + auto path = find_import_path(&c->file_scopes, s, s); defer (array_free(&path)); if (path.count > 0) { @@ -2484,192 +2627,16 @@ void check_import_entities(Checker *c, Map *file_scopes) { for_array(file_index, file_order) { ImportGraphNode *node = file_order[file_index]; - for_array(i, node->decls) { - AstNode *decl = node->decls[i]; - Scope *parent_scope = decl->file->scope; - GB_ASSERT(parent_scope->is_file); + AstFile *f = node->scope->file; - switch (decl->kind) { - case_ast_node(id, ImportDecl, decl); + CheckerContext prev_context = c->context; + defer (c->context = prev_context); + add_curr_ast_file(c, f); - Token token = id->relpath; - HashKey key = hash_string(id->fullpath); - Scope **found = map_get(file_scopes, key); - if (found == nullptr) { - for_array(scope_index, file_scopes->entries) { - Scope *scope = file_scopes->entries[scope_index].value; - gb_printf_err("%.*s\n", LIT(scope->file->tokenizer.fullpath)); - } - gb_printf_err("%.*s(%td:%td)\n", LIT(token.pos.file), token.pos.line, token.pos.column); - GB_PANIC("Unable to find scope for file: %.*s", LIT(id->fullpath)); - } - Scope *scope = *found; + c->context.allow_file_when_statement = true; - if (scope->is_global) { - error(token, "Importing a #shared_global_scope is disallowed and unnecessary"); - continue; - } - - if (id->cond != nullptr) { - Operand operand = {Addressing_Invalid}; - check_expr(c, &operand, id->cond); - if (operand.mode != Addressing_Constant || !is_type_boolean(operand.type)) { - error(id->cond, "Non-constant boolean `when` condition"); - continue; - } - if (operand.value.kind == ExactValue_Bool && - operand.value.value_bool == false) { - continue; - } - } - - if (ptr_set_exists(&parent_scope->imported, scope)) { - // error(token, "Multiple import of the same file within this scope"); - } else { - ptr_set_add(&parent_scope->imported, scope); - } - - scope->has_been_imported = true; - - if (id->is_using) { - if (parent_scope->is_global) { - error(id->import_name, "#shared_global_scope imports cannot use using"); - } else { - // NOTE(bill): Add imported entities to this file's scope - for_array(elem_index, scope->elements.entries) { - Entity *e = scope->elements.entries[elem_index].value; - if (e->scope == parent_scope) continue; - - if (!is_entity_kind_exported(e->kind)) { - continue; - } - if (is_entity_exported(e)) { - // TODO(bill): Should these entities be imported but cause an error when used? - bool ok = add_entity(c, parent_scope, e->identifier, e); - if (ok) map_set(&parent_scope->implicit, hash_entity(e), true); - } - } - } - } else { - String import_name = path_to_entity_name(id->import_name.string, id->fullpath); - if (is_blank_ident(import_name)) { - error(token, "File name, %.*s, cannot be use as an import name as it is not a valid identifier", LIT(id->import_name.string)); - } else { - GB_ASSERT(id->import_name.pos.line != 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, nullptr, e); - } - } - case_end; - - case_ast_node(ed, ExportDecl, decl); - Token token = ed->relpath; - HashKey key = hash_string(ed->fullpath); - Scope **found = map_get(file_scopes, key); - if (found == nullptr) { - for_array(scope_index, file_scopes->entries) { - Scope *scope = file_scopes->entries[scope_index].value; - gb_printf_err("%.*s\n", LIT(scope->file->tokenizer.fullpath)); - } - gb_printf_err("%.*s(%td:%td)\n", LIT(token.pos.file), token.pos.line, token.pos.column); - GB_PANIC("Unable to find scope for file: %.*s", LIT(ed->fullpath)); - } - Scope *scope = *found; - - if (scope->is_global) { - error(token, "Exporting a #shared_global_scope is disallowed and unnecessary"); - continue; - } - - if (ed->cond != nullptr) { - Operand operand = {Addressing_Invalid}; - check_expr(c, &operand, ed->cond); - if (operand.mode != Addressing_Constant || !is_type_boolean(operand.type)) { - error(ed->cond, "Non-constant boolean `when` condition"); - continue; - } - if (operand.value.kind == ExactValue_Bool && - operand.value.value_bool == false) { - continue; - } - } - - if (ptr_set_exists(&parent_scope->imported, scope)) { - // error(token, "Multiple import of the same file within this scope"); - } else { - ptr_set_add(&parent_scope->imported, scope); - } - - scope->has_been_imported = true; - if (parent_scope->is_global) { - error(decl, "`export` cannot be used on #shared_global_scope"); - } else { - // NOTE(bill): Add imported entities to this file's scope - for_array(elem_index, scope->elements.entries) { - Entity *e = scope->elements.entries[elem_index].value; - if (e->scope == parent_scope) continue; - - if (is_entity_kind_exported(e->kind)) { - add_entity(c, parent_scope, e->identifier, e); - } - } - } - case_end; - } - } - } - - for_array(i, c->delayed_foreign_libraries) { - AstNode *decl = c->delayed_foreign_libraries[i].decl; - ast_node(fl, ForeignLibraryDecl, decl); - - // Scope *parent_scope = c->delayed_foreign_libraries[i].parent; - Scope *parent_scope = fl->parent->scope; - GB_ASSERT(parent_scope->is_file); - - String file_str = fl->filepath.string; - String base_dir = fl->base_dir; - - if (fl->token.kind == Token_foreign_library) { - gbAllocator a = heap_allocator(); // TODO(bill): Change this allocator - - String rel_path = get_fullpath_relative(a, base_dir, file_str); - String import_file = rel_path; - if (!gb_file_exists(cast(char *)rel_path.text)) { // NOTE(bill): This should be null terminated - String abs_path = get_fullpath_core(a, file_str); - if (gb_file_exists(cast(char *)abs_path.text)) { - import_file = abs_path; - } - } - file_str = import_file; - } - - if (fl->cond != nullptr) { - Operand operand = {Addressing_Invalid}; - check_expr(c, &operand, fl->cond); - if (operand.mode != Addressing_Constant || !is_type_boolean(operand.type)) { - error(fl->cond, "Non-constant boolean `when` condition"); - continue; - } - if (operand.value.kind == ExactValue_Bool && - !operand.value.value_bool) { - continue; - } - } - - String library_name = path_to_entity_name(fl->library_name.string, file_str); - if (is_blank_ident(library_name)) { - error(decl, "File name, %.*s, cannot be as a library name as it is not a valid identifier", LIT(fl->library_name.string)); - } else { - GB_ASSERT(fl->library_name.pos.line != 0); - fl->library_name.string = library_name; - Entity *e = make_entity_library_name(c->allocator, parent_scope, fl->library_name, t_invalid, - file_str, library_name); - add_entity(c, parent_scope, nullptr, e); + for_array(i, f->decls) { + check_delayed_file_import_entities(c, f->decls[i]); } } } @@ -2797,10 +2764,6 @@ void calculate_global_init_order(Checker *c) { void check_parsed_files(Checker *c) { - Map file_scopes; // Key: String (fullpath) - map_init(&file_scopes, heap_allocator()); - defer (map_destroy(&file_scopes)); - add_type_info_type(c, t_invalid); // Map full filepaths to Scopes @@ -2810,7 +2773,7 @@ void check_parsed_files(Checker *c) { f->decl_info = make_declaration_info(c->allocator, f->scope, c->context.decl); HashKey key = hash_string(f->tokenizer.fullpath); - map_set(&file_scopes, key, scope); + map_set(&c->file_scopes, key, scope); map_set(&c->info.files, key, f); } @@ -2819,11 +2782,11 @@ void check_parsed_files(Checker *c) { AstFile *f = c->parser->files[i]; CheckerContext prev_context = c->context; add_curr_ast_file(c, f); - check_collect_entities(c, f->decls, true); + check_collect_entities(c, f->decls); c->context = prev_context; } - check_import_entities(c, &file_scopes); + check_import_entities(c); check_all_global_entities(c); init_preload(c); // NOTE(bill): This could be setup previously through the use of `type_info(_of_val)` @@ -2935,8 +2898,8 @@ void check_parsed_files(Checker *c) { // gb_printf_err("Count: %td\n", c->info.type_info_count++); if (!build_context.is_dll) { - for_array(i, file_scopes.entries) { - Scope *s = file_scopes.entries[i].value; + for_array(i, c->file_scopes.entries) { + Scope *s = c->file_scopes.entries[i].value; if (s->is_init) { Entity *e = current_scope_lookup_entity(s, str_lit("main")); if (e == nullptr) { diff --git a/src/map.cpp b/src/map.cpp index 4d75270fb..92840a480 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -101,8 +101,7 @@ struct Map { }; -template void map_init (Map *h, gbAllocator a); -template void map_init_with_reserve(Map *h, gbAllocator a, isize capacity); +template void map_init (Map *h, gbAllocator a, isize capacity = 16); template void map_destroy (Map *h); template T * map_get (Map *h, HashKey key); template void map_set (Map *h, HashKey key, T const &value); @@ -123,13 +122,7 @@ template void multi_map_remove_all(Map *h, HashKey key); template -gb_inline void map_init(Map *h, gbAllocator a) { - array_init(&h->hashes, a); - array_init(&h->entries, a); -} - -template -gb_inline void map_init_with_reserve(Map *h, gbAllocator a, isize capacity) { +gb_inline void map_init(Map *h, gbAllocator a, isize capacity) { array_init(&h->hashes, a, capacity); array_init(&h->entries, a, capacity); } diff --git a/src/parser.cpp b/src/parser.cpp index 27d88ad9a..d2c2d9991 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -36,30 +36,30 @@ struct ImportedFile { struct AstFile { - isize id; - gbArena arena; - Tokenizer tokenizer; - Array tokens; - isize curr_token_index; - Token curr_token; - Token prev_token; // previous non-comment + isize id; + gbArena arena; + Tokenizer tokenizer; + Array tokens; + isize curr_token_index; + Token curr_token; + Token prev_token; // previous non-comment // >= 0: In Expression // < 0: In Control Clause // NOTE(bill): Used to prevent type literals in control clauses - isize expr_level; - bool allow_range; // NOTE(bill): Ranges are only allowed in certain cases - bool in_foreign_block; - bool allow_type; + isize expr_level; + bool allow_range; // NOTE(bill): Ranges are only allowed in certain cases + bool in_foreign_block; + bool allow_type; - Array decls; - ImportedFileKind file_kind; - bool is_global_scope; + Array decls; + ImportedFileKind file_kind; + bool is_global_scope; - AstNode * curr_proc; - isize scope_level; - Scope * scope; // NOTE(bill): Created in checker - DeclInfo * decl_info; // NOTE(bill): Created in checker + AstNode * curr_proc; + isize scope_level; + Scope * scope; // NOTE(bill): Created in checker + DeclInfo * decl_info; // NOTE(bill): Created in checker CommentGroup lead_comment; // Comment (block) before the decl @@ -68,7 +68,6 @@ struct AstFile { Array comments; // All the comments! - // TODO(bill): Error recovery #define PARSER_MAX_FIX_COUNT 6 isize fix_count; TokenPos fix_prev_pos; @@ -350,8 +349,6 @@ AST_NODE_KIND(_DeclBegin, "", i32) \ Token relpath; \ String fullpath; \ Token import_name; \ - AstNode *cond; \ - AstFile *parent; \ CommentGroup docs; \ CommentGroup comment; \ }) \ @@ -359,8 +356,6 @@ AST_NODE_KIND(_DeclBegin, "", i32) \ Token token; \ Token relpath; \ String fullpath; \ - AstNode *cond; \ - AstFile *parent; \ CommentGroup docs; \ CommentGroup comment; \ }) \ @@ -369,8 +364,6 @@ AST_NODE_KIND(_DeclBegin, "", i32) \ Token filepath; \ Token library_name; \ String base_dir; \ - AstNode *cond; \ - AstFile *parent; \ CommentGroup docs; \ CommentGroup comment; \ }) \ @@ -831,9 +824,6 @@ AstNode *clone_ast_node(gbAllocator a, AstNode *node) { n->ValueDecl.type = clone_ast_node(a, n->ValueDecl.type); n->ValueDecl.values = clone_ast_node_array(a, n->ValueDecl.values); break; - case AstNode_ForeignLibraryDecl: - n->ForeignLibraryDecl.cond = clone_ast_node(a, n->ForeignLibraryDecl.cond); - break; case AstNode_Field: n->Field.names = clone_ast_node_array(a, n->Field.names); @@ -1547,8 +1537,6 @@ AstNode *ast_import_decl(AstFile *f, Token token, bool is_using, Token relpath, result->ImportDecl.is_using = is_using; result->ImportDecl.relpath = relpath; result->ImportDecl.import_name = import_name; - result->ImportDecl.cond = cond; - result->ImportDecl.parent = f; result->ImportDecl.docs = docs; result->ImportDecl.comment = comment; return result; @@ -1559,8 +1547,6 @@ AstNode *ast_export_decl(AstFile *f, Token token, Token relpath, AstNode *cond, AstNode *result = make_ast_node(f, AstNode_ExportDecl); result->ExportDecl.token = token; result->ExportDecl.relpath = relpath; - result->ExportDecl.cond = cond; - result->ExportDecl.parent = f; result->ExportDecl.docs = docs; result->ExportDecl.comment = comment; return result; @@ -1572,8 +1558,6 @@ AstNode *ast_foreign_library_decl(AstFile *f, Token token, Token filepath, Token result->ForeignLibraryDecl.token = token; result->ForeignLibraryDecl.filepath = filepath; result->ForeignLibraryDecl.library_name = library_name; - result->ForeignLibraryDecl.cond = cond; - result->ForeignLibraryDecl.parent = f; result->ForeignLibraryDecl.docs = docs; result->ForeignLibraryDecl.comment = comment; return result; @@ -1765,7 +1749,6 @@ bool is_blank_ident(AstNode *node) { // NOTE(bill): Go to next statement to prevent numerous error messages popping up void fix_advance_to_next_stmt(AstFile *f) { - // TODO(bill): fix_advance_to_next_stmt #if 1 for (;;) { Token t = f->curr_token; @@ -1999,9 +1982,9 @@ void check_proc_add_tag(AstFile *f, AstNode *tag_expr, u64 *tags, ProcTag tag, S } bool is_foreign_name_valid(String name) { - // TODO(bill): is_foreign_name_valid - if (name.len == 0) + if (name.len == 0) { return false; + } isize offset = 0; while (offset < name.len) { Rune rune; @@ -2047,7 +2030,6 @@ bool is_foreign_name_valid(String name) { } void parse_proc_tags(AstFile *f, u64 *tags, String *link_name, ProcCallingConvention *calling_convention) { - // TODO(bill): Add this to procedure literals too GB_ASSERT(tags != nullptr); GB_ASSERT(link_name != nullptr); @@ -2067,7 +2049,6 @@ void parse_proc_tags(AstFile *f, u64 *tags, String *link_name, ProcCallingConven check_proc_add_tag(f, tag_expr, tags, ProcTag_link_name, tag_name); if (f->curr_token.kind == Token_String) { *link_name = f->curr_token.string; - // TODO(bill): Check if valid string if (!is_foreign_name_valid(*link_name)) { syntax_error(tag_expr, "Invalid alternative link procedure name `%.*s`", LIT(*link_name)); } @@ -4852,12 +4833,35 @@ bool determine_path_from_string(Parser *p, AstNode *node, String base_dir, Strin return true; } + +void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Array decls); + +void parse_setup_file_when_stmt(Parser *p, AstFile *f, String base_dir, AstNodeWhenStmt *ws) { + if (ws->body != nullptr) { + auto stmts = ws->body->BlockStmt.stmts; + parse_setup_file_decls(p, f, base_dir, stmts); + } + + if (ws->else_stmt != nullptr) { + switch (ws->else_stmt->kind) { + case AstNode_BlockStmt: { + auto stmts = ws->else_stmt->BlockStmt.stmts; + parse_setup_file_decls(p, f, base_dir, stmts); + } break; + case AstNode_WhenStmt: + parse_setup_file_when_stmt(p, f, base_dir, &ws->else_stmt->WhenStmt); + break; + } + } +} + void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Array decls) { for_array(i, decls) { AstNode *node = decls[i]; if (!is_ast_node_decl(node) && node->kind != AstNode_BadStmt && - node->kind != AstNode_EmptyStmt) { + node->kind != AstNode_EmptyStmt && + node->kind != AstNode_WhenStmt) { // NOTE(bill): Sanity check syntax_error(node, "Only declarations are allowed at file scope, got %.*s", LIT(ast_node_strings[node->kind])); } else if (node->kind == AstNode_ImportDecl) { @@ -4899,6 +4903,9 @@ void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Arraybase_dir = base_dir; } + } else if (node->kind == AstNode_WhenStmt) { + ast_node(ws, WhenStmt, node); + parse_setup_file_when_stmt(p, f, base_dir, ws); } } }