lang: Create modules and dependencies on request

This commit is contained in:
Jookia 2023-11-03 22:25:49 +11:00
parent bd2cac8074
commit 0fd1f67923
3 changed files with 89 additions and 15 deletions

View file

@ -62,6 +62,7 @@ int lang_main(void) {
VmState state = vm_create();
test_rational(state);
test_module(state);
modules_free(state);
vm_destroy(&state);
printf("All done!\n");
return 0;

View file

@ -23,6 +23,30 @@ static ModuleInfo *modules_list[] = {
};
#undef X
// Size of a stack in elemenets
#define STACK_SIZE 64
// A stack of ModuleInfo pointers
struct module_stack {
const ModuleInfo *elems[STACK_SIZE];
int next;
};
// A stack of Objects
struct object_stack {
Object elems[STACK_SIZE];
int next;
};
// A mapping mapping between ModuleInfo and Object
// Each stack element should have a corresponding element
// in the other stack at the same index
struct module_mapping {
struct module_stack infos;
struct object_stack objects;
};
// Finds a ModuleInfo by name
static const ModuleInfo *info_by_name(const char *name) {
for (unsigned int i = 0; i < ARRAY_SIZE(modules_list); ++i) {
ModuleInfo *info = modules_list[i];
@ -32,12 +56,7 @@ static const ModuleInfo *info_by_name(const char *name) {
return NULL;
}
struct module_stack {
ModuleInfo *elems[64];
int next;
};
// Checks if an info is in a stack
// Checks if a ModuleInfo is in a module_stack
static int in_stack(const ModuleInfo *info, struct module_stack *stack) {
for (int i = 0; i < stack->next; ++i) {
ModuleInfo *elem = stack->elems[i];
@ -48,8 +67,8 @@ static int in_stack(const ModuleInfo *info, struct module_stack *stack) {
}
// Traverses a module use dependency tree, checking for circular dependencies
// This uses a depth-first search with a stack for traversing
// The resulting visited list is the reverse load order
// The result is the visited stack containing modules in load order
// This function works using a depth-first search
static void traverse_module_uses(
const ModuleInfo *start, struct module_stack *visited) {
static struct module_stack traversing = {0};
@ -57,37 +76,86 @@ static void traverse_module_uses(
abort_print("Modules too big to traverse");
if (in_stack(start, visited))
return;
// Push the start module to the top of the traversal stack
traversing.next = 0;
traversing.elems[traversing.next++] = start;
do {
// Use the top of traversal stack as our current module
ModuleInfo *cur = traversing.elems[traversing.next - 1];
// Iterate through module uses
const char **use = cur->uses;
while (*use) {
// Find the info associated with the use
const ModuleInfo *info = info_by_name(*use);
if (info == NULL)
abort_print("Couldn't find module");
// If we are already traversing this use then
// we have a cyclic dependency. Bail
if (in_stack(info, &traversing))
abort_print("Cyclic dependency found");
// If this use is unvisited, push it to the stack
// and traverse it
if (!in_stack(info, visited)) {
traversing.elems[traversing.next++] = info;
break;
}
use++;
}
// No unvisited uses? Mark this module as visited and
// retry the module we were traversing before
if (*use == NULL) {
traversing.elems[traversing.next--] = NULL;
visited->elems[visited->next++] = cur;
}
} while (traversing.next != -1);
} while (traversing.next != 0);
// The traversal stack is now empty, all done!
}
// Finds a module object by its info using a module_mapping
Object get_module_by_info(
struct module_mapping *mapping, const ModuleInfo *info) {
struct module_stack *infos = &mapping->infos;
for (int i = 0; i < infos->next; ++i) {
ModuleInfo *elem = infos->elems[i];
if (elem == info)
return mapping->objects.elems[i];
}
abort_print("Couldn't find module by info");
}
// Creates modules and stores them in a mapping
void create_modules(VmState state, struct module_mapping *mapping) {
struct module_stack *infos = &mapping->infos;
struct object_stack *objs = &mapping->objects;
for (int i = objs->next; i < infos->next; ++i) {
ModuleInfo *info = infos->elems[i];
Object module = info->create(state);
objs->elems[objs->next++] = module;
}
}
// Global mapping used for finding and freeing modules
static struct module_mapping mapping = {0};
Object module_find(VmState state, const char *name) {
const ModuleInfo *info = info_by_name(name);
vm_abort_if(state, !info, "Unable to find module!");
static struct module_stack visited = {0};
if (sizeof(modules_list) > sizeof(visited.elems))
if (sizeof(modules_list) > sizeof(mapping.infos.elems))
abort_print("Modules too big to track");
traverse_module_uses(info, &visited); // TODO: Does nothing
traverse_module_uses(info, &visited); // TODO: Does nothing
return info->create(state);
if (sizeof(modules_list) > sizeof(mapping.objects.elems))
abort_print("Modules too big to load");
traverse_module_uses(info, &mapping.infos);
create_modules(state, &mapping);
Object module = get_module_by_info(&mapping, info);
// Create a new reference for the caller
object_hold(state, module);
return module;
}
void modules_free(VmState state) {
struct object_stack *objs = &mapping.objects;
for (int i = 0; i < objs->next; ++i)
object_drop(state, &objs->elems[i]);
memset(&mapping, 0, sizeof(mapping));
}

View file

@ -7,11 +7,16 @@
#include "types.h"
// Finds a module by its name
// This internally loads the module and dependencies
// A new reference is created for the caller
Object module_find(VmState state, const char *name);
// Frees all modules loaded internally
// You must call this at the end of your program
void modules_free(VmState state);
// The following section is an internal API for use only with
// the compiler's C code generator. Do not use for regular C code.
#ifdef MODULE_INTERNAL_API
// Data structure for creating a module