Implement first try of a module system

Very much inspired by lua, as so often: The module system maintains a list
of searchers that will be called with the requested module name. They can
then return a loader function, which will then be called again with the
module name. That loader then returns the actual module, which is then
cached. We also add a goofy "foo" module in main.c to test this.
This commit is contained in:
Laria 2023-04-04 22:46:22 +02:00
parent c07caa9aa2
commit 4d01f20d6e
4 changed files with 148 additions and 0 deletions

View file

@ -835,6 +835,8 @@ void apfl_registry_set(apfl_ctx, void *key_major, size_t key_minor, apfl_stackid
void apfl_registry_delete(apfl_ctx, void *key_major, size_t key_minor);
bool apfl_registry_try_get(apfl_ctx, void *key_major, size_t key_minor);
void apfl_modules_register(apfl_ctx ctx, const char *name, apfl_stackidx modloader);
enum apfl_call_stack_entry_type {
APFL_CSE_FUNCTION,
APFL_CSE_CFUNCTION,

View file

@ -700,6 +700,41 @@ add_builtin(apfl_ctx ctx, const char *name, apfl_cfunc func)
apfl_dict_set(ctx, -3, -2, -1);
}
static int cmod_searcher_registry_key;
static void
cmod_searcher(apfl_ctx ctx)
{
ONE_ARG(ctx, "cmod-searcher");
if (!apfl_registry_try_get(ctx, &cmod_searcher_registry_key, 0)) {
apfl_drop(ctx, -1);
apfl_push_nil(ctx);
return;
}
if (apfl_get_member_if_exists(ctx, -1, -2)) {
apfl_sym_some(ctx);
apfl_push_pair(ctx, -1, -2);
} else {
apfl_push_nil(ctx);
}
}
void
apfl_modules_register(apfl_ctx ctx, const char *name, apfl_stackidx modloader)
{
apfl_move_to_top_of_stack(ctx, modloader);
if (!apfl_registry_try_get(ctx, &cmod_searcher_registry_key, 0)) {
apfl_dict_create(ctx);
}
apfl_push_const_string(ctx, name);
apfl_dict_set(ctx, -2, -1, -3);
apfl_registry_set(ctx, &cmod_searcher_registry_key, 0, -1);
}
void
apfl_builtins(apfl_ctx ctx)
{
@ -744,4 +779,5 @@ apfl_builtins(apfl_ctx ctx)
add_builtin(ctx, "raise", impl_raise);
add_builtin(ctx, "getsym-Some", apfl_sym_some);
add_builtin(ctx, "get-argv", get_argv);
add_builtin(ctx, "cmod-searcher", cmod_searcher);
}

View file

@ -188,6 +188,45 @@
}
}
andalso := {
f ->
(f)
f ~fs ->
result := (f)
if result { andalso ~fs } { result }
}
orelse := {
f ->
(f)
f ~fs ->
result := (f)
if result { result } { orelse ~fs }
}
find-first := { l f ->
out := nil
i := 0
n := len l
while {andalso {== out nil} {< i n}} {
{
Some:x ->
out = Some::x
nil ->
} (f l@i)
i = ++ i
}
out
}
unwrap-some := {
Some:x then _ -> then x
x _ else -> else x
x then -> unwrap-some x then identity
}
has-key := { k container ->
{
Some: _ -> true
@ -199,6 +238,60 @@
((load-file f))
}
modules := ({
loaded-modules := [->]
searchers := []
ImportError := symbol 'ImportError
add-searcher := { s ->
searchers = [~searchers s]
}
add-searcher { m ->
unwrap-some (get-optional m loaded-modules) { mod ->
{ Some::mod }
}
}
add-searcher { m ->
unwrap-some (builtins.cmod-searcher m) { loader ->
Some::(loader)
}
}
import := { name ->
maybe-mod := find-first searchers { s ->
unwrap-some (s name) { loader ->
loader name
}
}
{
Some:mod ->
loaded-modules@name = mod
mod
nil ->
raise ImportError::(& "Module " name " not found")
} maybe-mod
}
add-preloaded-module := { name mod ->
loaded-modules@name = mod
}
modules := [
'import -> import
'add-searcher -> add-searcher
'add-preloaded-module -> add-preloaded-module
'ImportError -> ImportError
]
add-preloaded-module 'modules modules
modules
})
# Dictionary of exported functions
[
'if -> if
@ -257,5 +350,10 @@
'symbol -> symbol
'Some -> Some
'get-argv -> builtins.get-argv
'andalso -> andalso
'orelse -> orelse
'find-first -> find-first
'unwrap-some -> unwrap-some
'import -> modules.import
]
}

View file

@ -166,6 +166,15 @@ eval_whole_file(apfl_ctx ctx, void *opaque)
#define FMT_TRY(x) if (!(x)) { rv = 1; goto err; }
static void
foomod(apfl_ctx ctx)
{
apfl_dict_create(ctx);
apfl_push_const_string(ctx, "bar");
apfl_push_const_string(ctx, "baz");
apfl_dict_set(ctx, -3, -2, -1);
}
static int
eval(
struct apfl_source_reader source_reader,
@ -193,6 +202,9 @@ eval(
}
apfl_argv_set(ctx, -1);
apfl_push_cfunc(ctx, foomod, 0);
apfl_modules_register(ctx, "foo", -1);
int rv = 0;
if (run_as_repl) {
apfl_iterative_runner runner = apfl_iterative_runner_new(ctx, source_reader);