Browse Source

`export` declarations

Ginger Bill 8 years ago
parent
commit
cf3c5a878a
5 changed files with 263 additions and 116 deletions
  1. 1 1
      core/opengl.odin
  2. 3 3
      core/os.odin
  3. 2 2
      core/sync.odin
  4. 184 104
      src/checker.cpp
  5. 73 6
      src/parser.cpp

+ 1 - 1
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");
 

+ 3 - 3
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);

+ 2 - 2
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";

+ 184 - 104
src/checker.cpp

@@ -1953,6 +1953,17 @@ void check_collect_entities(Checker *c, Array<AstNode *> 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<AstNode *> 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<AstNode *>   decls; // AstNodeImportDecl *
+	Array<AstNode *>   decls; // AstNodeImportDecl or AstNodeExportDecl
 	String             path;
 	isize              file_id;
 	ImportGraphNodeSet pred;
@@ -2264,37 +2251,73 @@ Array<ImportGraphNode *> generate_import_dependency_graph(Checker *c, Map<Scope
 		AstNode *decl = c->delayed_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 **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;
 
-		ImportGraphNode *m = nullptr;
-		ImportGraphNode *n  = nullptr;
+			array_add(&m->decls, decl);
 
-		ImportGraphNode **found_node = map_get(&M, hash_pointer(parent));
-		GB_ASSERT(found_node != nullptr);
-		m = *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);
+			}
+		} 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);
 
-		found_node = map_get(&M, hash_pointer(scope));
-		GB_ASSERT(found_node != nullptr);
-		n = *found_node;
+			ImportGraphNode *m = nullptr;
+			ImportGraphNode *n  = nullptr;
 
-		array_add(&m->decls, decl);
+			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<Scope *> *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_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;
+				GB_ASSERT(parent_scope->is_file);
 
-			if (scope->is_global) {
-				error(token, "Importing a #shared_global_scope is disallowed and unnecessary");
-				continue;
-			}
+				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;
 
-			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);
-				}
 			}
 		}
 	}

+ 73 - 6
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, Array<AstNod
 
 			id->fullpath = 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;