aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/ek/ast.h1
-rw-r--r--include/ek/scope.h5
-rw-r--r--src/actualize.c332
-rw-r--r--src/ast.c11
-rw-r--r--src/compiler.c5
-rw-r--r--src/scope.c62
-rw-r--r--tests/exports/defines.ek10
-rw-r--r--tests/exports/exports.ek10
-rw-r--r--tests/exports/reexports.ek11
-rw-r--r--tests/exports/source.mk1
10 files changed, 427 insertions, 21 deletions
diff --git a/include/ek/ast.h b/include/ek/ast.h
index 81b1a85..5c1c6e6 100644
--- a/include/ek/ast.h
+++ b/include/ek/ast.h
@@ -751,6 +751,7 @@ struct type *reverse_type_list(struct type *root);
struct ast *chain_base(struct ast *node);
struct ast *clone_chain(struct ast *chain);
+bool is_subchain(struct ast *sub, struct ast *top);
int type_lists_match(struct type *a, struct type *b);
diff --git a/include/ek/scope.h b/include/ek/scope.h
index d8a5974..aedeacf 100644
--- a/include/ek/scope.h
+++ b/include/ek/scope.h
@@ -340,4 +340,9 @@ bool is_exported_type(struct scope *scope, struct ast *def);
bool is_exported_symbol(struct scope *scope, struct ast *def);
bool is_exported_macro(struct scope *scope, struct ast *def);
+int scope_add_exported_symbol(struct scope *scope, struct ast *def);
+int scope_add_exported_type(struct scope *scope, struct ast *def);
+int scope_add_exported_chain(struct scope *scope, struct ast *def);
+int scope_add_exported_macro(struct scope *scope, struct ast *def);
+
#endif /* SCOPE_H */
diff --git a/src/actualize.c b/src/actualize.c
index 941f9e3..0d2c624 100644
--- a/src/actualize.c
+++ b/src/actualize.c
@@ -327,22 +327,79 @@ static int eval_const_if(struct scope *scope, struct ast *node)
static int actualize_proc_sign(struct scope *scope, struct ast *proc);
-static int copy_scope(struct scope *to, struct scope *from)
+static int copy_proc(bool public, struct scope *to, struct ast *def)
{
- /** @todo handle duplicates */
- foreach(visible, n, &from->symbols) {
- struct ast *def = n->data;
- if (!ast_flags(def, AST_FLAG_PUBLIC))
- continue;
+ struct ast *exists = file_scope_find_symbol(to, proc_id(def));
+ if (!exists) {
+ if (scope_add_proc(to, def))
+ return -1;
+
+ if (public && scope_add_exported_symbol(to, def))
+ return -1;
+
+ return 0;
+ }
+
+ if (exists != def) {
+ semantic_error(exists->scope->fctx, exists,
+ "symbol redefined");
+ semantic_info(def->scope->fctx, def,
+ "prev here");
+ return -1;
+ }
+
+ /* symbol already exists, so we can 'unify' here */
+ if (is_exported_symbol(to, def))
+ return 0;
+
+ if (scope_add_exported_symbol(to, def))
+ return -1;
+
+ return 0;
+}
+
+static int copy_var(bool public, struct scope *to, struct ast *def)
+{
+ struct ast *exists = file_scope_find_symbol(to, var_id(def));
+ if (!exists) {
+ if (scope_add_var(to, def))
+ return -1;
+
+ if (public && scope_add_exported_symbol(to, def))
+ return -1;
+
+ return 0;
+ }
+ if (exists != def) {
+ semantic_error(exists->scope->fctx, exists,
+ "symbol redefined");
+ semantic_info(def->scope->fctx, def,
+ "prev here");
+ return -1;
+ }
+
+ if (is_exported_symbol(to, def))
+ return 0;
+
+ if (scope_add_exported_symbol(to, def))
+ return -1;
+
+ return 0;
+}
+
+static int copy_symbols(bool public, struct scope *to, struct scope *from)
+{
+ foreach(exported, n, &from->exported_symbols) {
+ struct ast *def = *n;
switch (def->k) {
case AST_PROC_DEF:
- if (scope_add_proc(to, def))
+ if (copy_proc(public, to, def))
return -1;
break;
case AST_VAR_DEF:
- if (scope_add_var(to, def))
+ if (copy_var(public, to, def))
return -1;
break;
@@ -350,6 +407,180 @@ static int copy_scope(struct scope *to, struct scope *from)
}
}
+ if (!public)
+ return 0;
+
+ return 0;
+}
+
+static int copy_type(bool public, struct scope *to, struct ast *def, char *id)
+{
+ struct ast *exists = file_scope_find_type(to, id);
+ if (!exists) {
+ if (scope_add_type(to, id, def))
+ return -1;
+
+ if (public && scope_add_exported_type(to, def))
+ return -1;
+
+ return 0;
+ }
+
+ if (exists != def) {
+ semantic_error(exists->scope->fctx, exists,
+ "type redefined");
+ semantic_info(def->scope->fctx, def,
+ "prev here");
+ return -1;
+ }
+
+ if (is_exported_type(to, def))
+ return 0;
+
+ if (public && scope_add_exported_type(to, def))
+ return -1;
+
+ return 0;
+}
+
+static int copy_chain(bool public, struct scope *to, struct ast *def)
+{
+ struct ast *exists = file_scope_find_type(to, struct_cont_id(def));
+ if (!exists) {
+ if (scope_add_type(to, struct_cont_id(def), def))
+ return -1;
+
+ if (public && scope_add_exported_type(to, def))
+ return -1;
+
+ return 0;
+ }
+
+ if (exists == def) {
+ if (is_exported_type(to, exists))
+ return 0;
+
+ if (public && scope_add_exported_type(to, def))
+ return -1;
+
+ return 0;
+ }
+
+ if (is_subchain(exists, def)) {
+ /* def takes priority */
+ if (scope_add_chain(to, struct_cont_id(def), def))
+ return -1;
+
+ if (public && scope_add_exported_type(to, def))
+ return -1;
+
+ return 0;
+ }
+
+ if (is_subchain(def, exists)) {
+ /* exists takes priority, but we can still export the subchain
+ * if need be */
+ if (is_exported_type(to, exists))
+ return 0;
+
+ if (public && scope_add_exported_type(to, def))
+ return -1;
+
+ return 0;
+ }
+
+
+ semantic_error(exists->scope->fctx, exists,
+ "unrelated struct cont chains");
+ semantic_info(def->scope->fctx, def,
+ "prev here");
+ return -1;
+}
+
+static int copy_types(bool public, struct scope *to, struct scope *from)
+{
+ foreach(exported, n, &from->exported_types) {
+ struct ast *def = *n;
+ switch (def->k) {
+ case AST_STRUCT_DEF:
+ if (copy_type(public, to, def, struct_id(def)))
+ return -1;
+ break;
+
+ case AST_TRAIT_DEF:
+ if (copy_type(public, to, def, trait_id(def)))
+ return -1;
+ break;
+
+ case AST_ALIAS_DEF:
+ if (copy_type(public, to, def, alias_id(def)))
+ return -1;
+ break;
+
+ case AST_ENUM_DEF:
+ if (copy_type(public, to, def, enum_id(def)))
+ return -1;
+ break;
+
+ case AST_STRUCT_CONT_DEF:
+ if (copy_chain(public, to, def))
+ return -1;
+ break;
+
+ default:
+ internal_error("unhandled type in copy");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int copy_macros(bool public, struct scope *to, struct scope *from)
+{
+ foreach(exported, n, &from->exported_macros) {
+ struct ast *def = *n;
+
+ struct ast *exists = file_scope_find_macro(to, macro_def_id(def));
+ if (!exists) {
+ if (scope_add_macro(to, def))
+ return -1;
+
+ if (public && scope_add_exported_macro(to, def))
+ return -1;
+
+ break;
+ }
+
+ if (exists != def) {
+ semantic_error(exists->scope->fctx, exists,
+ "macro redefined");
+ semantic_info(def->scope->fctx, def,
+ "prev here");
+ return -1;
+ }
+
+ if (is_exported_macro(to, def))
+ break;
+
+ if (scope_add_exported_macro(to, def))
+ return -1;
+ }
+
+ return 0;
+}
+
+static int copy_scope(bool public, struct scope *to, struct scope *from)
+{
+ if (copy_symbols(public, to, from))
+ return -1;
+
+ if (copy_types(public, to, from))
+ return -1;
+
+ if (copy_macros(public, to, from))
+ return -1;
+
return 0;
}
@@ -359,13 +590,37 @@ static int analyze_visibility(struct scope *scope, struct ast *node)
return 0;
switch (node->k) {
- case AST_VAR_DEF: return scope_add_var(scope, node);
+ case AST_VAR_DEF: {
+ /* no global variables at the moment */
+ if (scope_add_var(scope, node))
+ return -1;
+
+ break;
+ }
+
case AST_PROC_DEF: {
node->scope = create_scope();
scope_add_scope(scope, node->scope);
- return scope_add_proc(scope, node);
+ if (scope_add_proc(scope, node))
+ return -1;
+
+ if (ast_flags(node, AST_FLAG_PUBLIC))
+ if (scope_add_exported_symbol(scope, node))
+ return -1;
+
+ break;
+ }
+
+ case AST_MACRO_DEF: {
+ if (scope_add_macro(scope, node))
+ return -1;
+
+ if (ast_flags(node, AST_FLAG_PUBLIC))
+ if (scope_add_exported_macro(scope, node))
+ return -1;
+
+ break;
}
- case AST_MACRO_DEF: return scope_add_macro(scope, node);
case AST_IMPORT: {
const char *file = import_file(node);
@@ -376,12 +631,12 @@ static int analyze_visibility(struct scope *scope, struct ast *node)
return -1;
}
- if (copy_scope(scope, child)) {
+ if (copy_scope(ast_flags(node, AST_FLAG_PUBLIC), scope, child)) {
semantic_info(scope->fctx, node, "imported here");
return -1;
}
- return 0;
+ break;
}
case AST_IF: {
@@ -391,36 +646,73 @@ static int analyze_visibility(struct scope *scope, struct ast *node)
/* since a const if likely replaced the current node with
* something else, we have to analyze the replacement */
- return analyze(scope, node);
+ if (analyze(scope, node))
+ return -1;
+
+ break;
}
case AST_STRUCT_DEF: {
node->scope = create_scope();
scope_add_scope(scope, node->scope);
- return scope_add_type(scope, struct_id(node), node);
+ if (scope_add_type(scope, struct_id(node), node))
+ return -1;
+
+ if (ast_flags(node, AST_FLAG_PUBLIC))
+ if (scope_add_exported_type(scope, node))
+ return -1;
+
+ break;
}
case AST_STRUCT_CONT_DEF: {
node->scope = create_scope();
scope_add_scope(scope, node->scope);
- return scope_add_chain(scope, struct_cont_id(node), node);
+ if (scope_add_chain(scope, struct_cont_id(node), node))
+ return -1;
+
+ if (ast_flags(node, AST_FLAG_PUBLIC))
+ if (scope_add_exported_chain(scope, node))
+ return -1;
+
+ break;
}
case AST_ENUM_DEF: {
node->scope = create_scope();
scope_add_scope(scope, node->scope);
- return scope_add_type(scope, enum_id(node), node);
+ if (scope_add_type(scope, enum_id(node), node))
+ return -1;
+
+ if (ast_flags(node, AST_FLAG_PUBLIC))
+ if (scope_add_exported_type(scope, node))
+ return -1;
+
+ break;
}
case AST_ALIAS_DEF: {
- char *id = alias_id(node);
- return scope_add_type(scope, id, node);
+ if (scope_add_type(scope, alias_id(node), node))
+ return -1;
+
+ if (ast_flags(node, AST_FLAG_PUBLIC))
+ if (scope_add_exported_type(scope, node))
+ return -1;
+
+ break;
}
case AST_TRAIT_DEF: {
node->scope = create_scope();
scope_add_scope(scope, node->scope);
- return scope_add_type(scope, trait_id(node), node);
+ if (scope_add_type(scope, trait_id(node), node))
+ return -1;
+
+ if (ast_flags(node, AST_FLAG_PUBLIC))
+ if (scope_add_exported_type(scope, node))
+ return -1;
+
+ break;
}
case AST_EMPTY: {
diff --git a/src/ast.c b/src/ast.c
index e4254da..c7f32a0 100644
--- a/src/ast.c
+++ b/src/ast.c
@@ -820,3 +820,14 @@ struct ast *clone_chain(struct ast *chain)
return new;
}
+
+bool is_subchain(struct ast *sub, struct ast *top)
+{
+ if (sub == top)
+ return true;
+
+ if (top->chain)
+ return is_subchain(sub, top->chain);
+
+ return false;
+}
diff --git a/src/compiler.c b/src/compiler.c
index b4a6e9b..881326c 100644
--- a/src/compiler.c
+++ b/src/compiler.c
@@ -159,7 +159,10 @@ struct scope *process_file(const char *file)
}
char *real = realpath(base, NULL);
- assert(real);
+ if (!real) {
+ error("no such file: %s", file);
+ goto out;
+ }
struct scope **exists = scopes_find(&scopes, real);
if (exists) {
diff --git a/src/scope.c b/src/scope.c
index 282018c..1eae065 100644
--- a/src/scope.c
+++ b/src/scope.c
@@ -492,3 +492,65 @@ bool is_exported_macro(struct scope *scope, struct ast *def)
return false;
}
+
+int scope_add_exported_symbol(struct scope *scope, struct ast *def)
+{
+ struct ast **inserted = exported_insert(&scope->exported_symbols, def);
+ if (!inserted) {
+ internal_error("failed inserting exported symbol");
+ return -1;
+ }
+
+ assert(*inserted == def);
+ return 0;
+}
+
+int scope_add_exported_type(struct scope *scope, struct ast *def)
+{
+ struct ast **inserted = exported_insert(&scope->exported_types, def);
+ if (!inserted) {
+ internal_error("failed inserting exported type");
+ return -1;
+ }
+
+ assert(*inserted == def);
+ return 0;
+}
+
+static void remove_exported_chain(struct scope *scope, struct ast *chain)
+{
+ struct ast **found = exported_find(&scope->exported_types, chain);
+ if (!found)
+ return;
+
+ exported_remove(&scope->exported_types, chain);
+ if (chain->chain)
+ remove_exported_chain(scope, chain->chain);
+}
+
+int scope_add_exported_chain(struct scope *scope, struct ast *def)
+{
+ assert(exported_find(&scope->exported_types, def) == NULL);
+ remove_exported_chain(scope, def->chain);
+
+ struct ast **inserted = exported_insert(&scope->exported_types, def);
+ if (!inserted) {
+ internal_error("failed inserting exported chain");
+ return -1;
+ }
+
+ assert(*inserted == def);
+ return 0;
+}
+
+int scope_add_exported_macro(struct scope *scope, struct ast *def)
+{
+ struct ast **inserted = exported_insert(&scope->exported_macros, def);
+ if (!inserted) {
+ internal_error("failed inserting exported macro");
+ return -1;
+ }
+
+ assert(*inserted == def);
+ return 0;
+}
diff --git a/tests/exports/defines.ek b/tests/exports/defines.ek
new file mode 100644
index 0000000..d816af7
--- /dev/null
+++ b/tests/exports/defines.ek
@@ -0,0 +1,10 @@
+pub typedef i27 {} /* struct def */
+pub typedef int i27; /* alias def */
+pub define reexported_trait {} /* trait def */
+pub define reexported_macro() {} /* macro def */
+pub reexported_func(i27 v) {} /* func def */
+pub enum reexported_enum : int {
+ SOME_ENUM = 10
+}
+// pub int reexported_var = 10;
+// global vars not currently supported due to missing constexpr checking I guess
diff --git a/tests/exports/exports.ek b/tests/exports/exports.ek
new file mode 100644
index 0000000..4487009
--- /dev/null
+++ b/tests/exports/exports.ek
@@ -0,0 +1,10 @@
+import "reexports.ek"
+
+main()
+{
+ reexported_func(20);
+
+ mut p = exported_struct![i27]{};
+
+ reexported_macro!(p);
+}
diff --git a/tests/exports/reexports.ek b/tests/exports/reexports.ek
new file mode 100644
index 0000000..a9b3167
--- /dev/null
+++ b/tests/exports/reexports.ek
@@ -0,0 +1,11 @@
+pub import "defines.ek"
+
+pub typedef exported_struct[reexported_trait T] {}
+
+priv_func() {
+ reexported_func(10);
+
+ mut p = exported_struct![i27] {};
+
+ reexported_macro!(p);
+}
diff --git a/tests/exports/source.mk b/tests/exports/source.mk
new file mode 100644
index 0000000..92a377e
--- /dev/null
+++ b/tests/exports/source.mk
@@ -0,0 +1 @@
+SIMPLE += exports