diff --git a/lang/main.c b/lang/main.c index 7173714..c28090a 100644 --- a/lang/main.c +++ b/lang/main.c @@ -62,6 +62,7 @@ VmState state = vm_create(); test_rational(state); test_module(state); + modules_free(state); vm_destroy(&state); printf("All done!\n"); return 0; diff --git a/lang/module.c b/lang/module.c index ec5729a..3fa9350 100644 --- a/lang/module.c +++ b/lang/module.c @@ -23,6 +23,30 @@ }; #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 @@ 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 @@ } // 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 @@ 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)); } diff --git a/lang/module.h b/lang/module.h index c8e2a43..330509c 100644 --- a/lang/module.h +++ b/lang/module.h @@ -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