From 284be00c81407735cc8ddec1f1353b867b2deffd Mon Sep 17 00:00:00 2001 From: Kimplul Date: Sun, 19 Jan 2025 16:33:32 +0200 Subject: improve export model --- include/ek/ast.h | 1 + include/ek/scope.h | 5 + src/actualize.c | 332 ++++++++++++++++++++++++++++++++++++++++++--- src/ast.c | 11 ++ src/compiler.c | 5 +- src/scope.c | 62 +++++++++ tests/exports/defines.ek | 10 ++ tests/exports/exports.ek | 10 ++ tests/exports/reexports.ek | 11 ++ tests/exports/source.mk | 1 + 10 files changed, 427 insertions(+), 21 deletions(-) create mode 100644 tests/exports/defines.ek create mode 100644 tests/exports/exports.ek create mode 100644 tests/exports/reexports.ek create mode 100644 tests/exports/source.mk 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 -- cgit v1.3