From cf3c5a878aaf2e03769cfd7e86e84e7c5981e964 Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Sun, 27 Aug 2017 19:36:43 +0100 Subject: [PATCH] `export` declarations --- core/opengl.odin | 2 +- core/os.odin | 6 +- core/sync.odin | 4 +- src/checker.cpp | 288 ++++++++++++++++++++++++++++++----------------- src/parser.cpp | 79 ++++++++++++- 5 files changed, 263 insertions(+), 116 deletions(-) diff --git a/core/opengl.odin b/core/opengl.odin index 30e3820d3..e4b1e117e 100644 --- a/core/opengl.odin +++ b/core/opengl.odin @@ -3,7 +3,7 @@ foreign_system_library lib "gl" when ODIN_OS == "linux"; import win32 "sys/windows.odin" when ODIN_OS == "windows"; import "sys/wgl.odin" when ODIN_OS == "windows"; -using import . "opengl_constants.odin"; +export "opengl_constants.odin"; _ := compile_assert(ODIN_OS != "osx"); diff --git a/core/os.odin b/core/os.odin index e9b854135..61dcf6ea3 100644 --- a/core/os.odin +++ b/core/os.odin @@ -1,6 +1,6 @@ -using import . "os_windows.odin" when ODIN_OS == "windows"; -using import . "os_x.odin" when ODIN_OS == "osx"; -using import . "os_linux.odin" when ODIN_OS == "linux"; +export "os_windows.odin" when ODIN_OS == "windows"; +export "os_x.odin" when ODIN_OS == "osx"; +export "os_linux.odin" when ODIN_OS == "linux"; 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 46f210e9d..689f54674 100644 --- a/core/sync.odin +++ b/core/sync.odin @@ -1,2 +1,2 @@ -using import . "sync_windows.odin" when ODIN_OS == "windows"; -using import . "sync_linux.odin" when ODIN_OS == "linux"; +export "sync_windows.odin" when ODIN_OS == "windows"; +export "sync_linux.odin" when ODIN_OS == "linux"; diff --git a/src/checker.cpp b/src/checker.cpp index 0885b56c1..332a4ac19 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1953,6 +1953,17 @@ void check_collect_entities(Checker *c, Array nodes, bool is_file_sco array_add(&c->delayed_imports, di); case_end; + case_ast_node(id, ExportDecl, decl); + if (!c->context.scope->is_file) { + error(decl, "export declarations are only allowed in the file scope"); + // NOTE(bill): _Should_ be caught by the parser + // TODO(bill): Better error handling if it isn't + continue; + } + DelayedDecl di = {c->context.scope, decl}; + array_add(&c->delayed_imports, di); + case_end; + case_ast_node(fl, ForeignLibraryDecl, decl); if (!c->context.scope->is_file) { error(decl, "%.*s declarations are only allowed in the file scope", LIT(fl->token.string)); @@ -1991,30 +2002,6 @@ void check_collect_entities(Checker *c, Array nodes, bool is_file_sco c->context = prev_context; case_end; - // case_ast_node(pd, ProcDecl, decl); - // AstNode *name = pd->name; - // if (name->kind != AstNode_Ident) { - // error(name, "A declaration's name must be an identifier, got %.*s", LIT(ast_node_strings[name->kind])); - // break; - // } - - - // DeclInfo *d = make_declaration_info(c->allocator, c->context.scope, c->context.decl); - // Entity *e = nullptr; - - // e = make_entity_procedure(c->allocator, d->scope, name->Ident, nullptr, pd->tags); - // AstNode *fl = c->context.curr_foreign_library; - // if (fl != nullptr) { - // GB_ASSERT(fl->kind == AstNode_Ident); - // e->Procedure.foreign_library_ident = fl; - // pd->tags |= ProcTag_foreign; - // } - // d->proc_decl = decl; - // d->type_expr = pd->type; - // e->identifier = name; - // add_entity_and_decl_info(c, name, e, d); - // case_end; - default: if (c->context.scope->is_file) { error(decl, "Only declarations are allowed at file scope"); @@ -2183,7 +2170,7 @@ void import_graph_node_set_remove(ImportGraphNodeSet *s, ImportGraphNode *n) { struct ImportGraphNode { Scope * scope; - Array decls; // AstNodeImportDecl * + Array decls; // AstNodeImportDecl or AstNodeExportDecl String path; isize file_id; ImportGraphNodeSet pred; @@ -2264,37 +2251,73 @@ Array generate_import_dependency_graph(Checker *c, Mapdelayed_imports[i].decl; GB_ASSERT(parent->is_file); - ast_node(id, ImportDecl, decl); + if (decl->kind == AstNode_ImportDecl) { + 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)); + 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)); } - 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); + Scope *scope = *found; + GB_ASSERT(scope != nullptr); - ImportGraphNode *m = nullptr; - ImportGraphNode *n = nullptr; + ImportGraphNode *m = nullptr; + ImportGraphNode *n = nullptr; - ImportGraphNode **found_node = map_get(&M, hash_pointer(parent)); - GB_ASSERT(found_node != nullptr); - m = *found_node; + 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; + found_node = map_get(&M, hash_pointer(scope)); + GB_ASSERT(found_node != nullptr); + n = *found_node; - array_add(&m->decls, decl); + 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); + } + } else if (decl->kind == AstNode_ExportDecl) { + 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); - 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); @@ -2437,83 +2460,140 @@ void check_import_entities(Checker *c, Map *file_scopes) { Scope *parent_scope = node->scope; for_array(i, node->decls) { AstNode *decl = node->decls[i]; - ast_node(id, ImportDecl, decl); - Token token = id->relpath; - GB_ASSERT(parent_scope->is_file); + if (decl->kind == AstNode_ImportDecl) { + ast_node(id, ImportDecl, decl); + 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_ASSERT(parent_scope->is_file); + + 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)); } - 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; + Scope *scope = *found; - 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"); + if (scope->is_global) { + error(token, "Importing a #shared_global_scope is disallowed and unnecessary"); continue; } - if (operand.value.kind == ExactValue_Bool && - operand.value.value_bool == false) { + + 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_add(&parent_scope->imported, scope)) { + // warning(token, "Multiple import of the same file within this 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 (id->import_name.string == ".") { + add_entity(c, parent_scope, e->identifier, e); + } else { + 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); + } + } + } else if (decl->kind == AstNode_ExportDecl) { + ast_node(ed, ExportDecl, decl); + Token token = ed->relpath; + + GB_ASSERT(parent_scope->is_file); + + 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 (ptr_set_add(&parent_scope->imported, scope)) { - // warning(token, "Multiple import of the same file within this scope"); - } + 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; + } + } - scope->has_been_imported = true; + if (ptr_set_add(&parent_scope->imported, scope)) { + // warning(token, "Multiple import of the same file within this scope"); + } - if (id->is_using) { + scope->has_been_imported = true; if (parent_scope->is_global) { - error(id->import_name, "#shared_global_scope imports cannot use using"); + 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)) { - continue; - } - if (id->import_name.string == ".") { + if (is_entity_kind_exported(e->kind)) { add_entity(c, parent_scope, e->identifier, e); - } else { - 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 if (id->import_name.string != ".") { - 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); - } } } } diff --git a/src/parser.cpp b/src/parser.cpp index f7da43d6a..e335888b8 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -354,6 +354,14 @@ AST_NODE_KIND(_DeclBegin, "", i32) \ CommentGroup docs; \ CommentGroup comment; \ }) \ + AST_NODE_KIND(ExportDecl, "export declaration", struct { \ + Token token; \ + Token relpath; \ + String fullpath; \ + AstNode *cond; \ + CommentGroup docs; \ + CommentGroup comment; \ + }) \ AST_NODE_KIND(ForeignLibraryDecl, "foreign library declaration", struct { \ Token token; \ Token filepath; \ @@ -574,6 +582,7 @@ Token ast_node_token(AstNode *node) { case AstNode_ValueDecl: return ast_node_token(node->ValueDecl.names[0]); case AstNode_ImportDecl: return node->ImportDecl.token; + case AstNode_ExportDecl: return node->ExportDecl.token; case AstNode_ForeignLibraryDecl: return node->ForeignLibraryDecl.token; case AstNode_ForeignBlockDecl: return node->ForeignBlockDecl.token; @@ -1539,6 +1548,17 @@ AstNode *ast_import_decl(AstFile *f, Token token, bool is_using, Token relpath, return result; } +AstNode *ast_export_decl(AstFile *f, Token token, Token relpath, AstNode *cond, + CommentGroup docs, CommentGroup comment) { + AstNode *result = make_ast_node(f, AstNode_ExportDecl); + result->ExportDecl.token = token; + result->ExportDecl.relpath = relpath; + result->ExportDecl.cond = cond; + result->ExportDecl.docs = docs; + result->ExportDecl.comment = comment; + return result; +} + AstNode *ast_foreign_library_decl(AstFile *f, Token token, Token filepath, Token library_name, AstNode *cond, CommentGroup docs, CommentGroup comment) { AstNode *result = make_ast_node(f, AstNode_ForeignLibraryDecl); @@ -4315,12 +4335,6 @@ AstNode *parse_import_decl(AstFile *f, bool is_using) { case Token_Ident: import_name = advance_token(f); break; - case Token_Period: - import_name = advance_token(f); - import_name.kind = Token_Ident; - if (is_using) break; - syntax_error(import_name, "`import .` is not allowed. Did you mean `using import`?"); - /* fallthrough */ default: import_name.pos = f->curr_token.pos; break; @@ -4343,6 +4357,23 @@ AstNode *parse_import_decl(AstFile *f, bool is_using) { return ast_import_decl(f, token, is_using, file_path, import_name, cond, docs, f->line_comment); } +AstNode *parse_export_decl(AstFile *f) { + CommentGroup docs = f->lead_comment; + Token token = expect_token(f, Token_export); + AstNode *cond = nullptr; + + Token file_path = expect_token_after(f, Token_String, "export"); + if (allow_token(f, Token_when)) { + cond = parse_expr(f, false); + } + expect_semicolon(f, nullptr); + if (f->curr_proc != nullptr) { + syntax_error(token, "You cannot use `export` within a procedure. This must be done at the file scope"); + return ast_bad_decl(f, token, file_path); + } + return ast_export_decl(f, token, file_path, cond, docs, f->line_comment); +} + AstNode *parse_foreign_decl(AstFile *f) { CommentGroup docs = f->lead_comment; Token token = {}; @@ -4423,6 +4454,10 @@ AstNode *parse_stmt(AstFile *f) { case Token_import: return parse_import_decl(f, false); + case Token_export: + return parse_export_decl(f); + + case Token_if: return parse_if_stmt(f); case Token_when: return parse_when_stmt(f); case Token_for: return parse_for_stmt(f); @@ -4801,6 +4836,38 @@ void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Arrayfullpath = import_file; try_add_import_path(p, import_file, file_str, ast_node_token(node).pos); + } else if (node->kind == AstNode_ExportDecl) { + ast_node(ed, ExportDecl, node); + String collection_name = {}; + String oirignal_string = ed->relpath.string; + String file_str = ed->relpath.string; + gbAllocator a = heap_allocator(); // TODO(bill): Change this allocator + String export_path = {}; + String rel_path = {}; + + if (!is_import_path_valid(file_str)) { + syntax_error(node, "Invalid export path: `%.*s`", LIT(file_str)); + // NOTE(bill): It's a naughty name + decls[i] = ast_bad_decl(f, ed->relpath, ed->relpath); + continue; + } + + gb_mutex_lock(&p->file_decl_mutex); + defer (gb_mutex_unlock(&p->file_decl_mutex)); + + rel_path = get_fullpath_relative(a, base_dir, file_str); + export_path = 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)) { + export_path = abs_path; + } + } + + export_path = string_trim_whitespace(export_path); + + ed->fullpath = export_path; + try_add_import_path(p, export_path, file_str, ast_node_token(node).pos); } else if (node->kind == AstNode_ForeignLibraryDecl) { ast_node(fl, ForeignLibraryDecl, node); String file_str = fl->filepath.string;