]> git.lizzy.rs Git - uwu-lang.git/blob - src/load.c
Use uwu-common as submodule
[uwu-lang.git] / src / load.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <stdarg.h>
5 #include <dlfcn.h>
6 #include "common/err.h"
7 #include "common/str.h"
8 #include "common/file.h"
9 #include "common/dl.h"
10 #include "common/dir.h"
11 #include "load.h"
12 #include "parse.h"
13
14 #define DEBUG 0
15
16 typedef struct
17 {
18         char *name;
19         UwUVMFunction *ref;
20 } FunctionLink;
21
22 typedef struct
23 {
24         char *path;                 // path without file extension
25         char *filename;             // path with file extension
26         char *environment;          // directory path
27
28         UwUVMModuleType type;       // native (.so) or plain (.uwu)
29
30         FunctionLink *functions;    // required functions
31         size_t    num_functions;    // number of required functions
32         size_t loaded_functions;    // number of loaded functions (<= num_functions)
33
34         union
35         {
36                 AbstractSyntaxTree ast; // abstract syntax tree generated by parser (for plain modules)
37                 void *lib;              // dlopen() shared object handle (for native modules)
38         } handle;
39 } Module;
40
41 typedef struct
42 {
43         Module   **modules;         // loaded modules
44         size_t num_modules;         // count for modules
45
46         char     **module_paths;    // module search paths
47         size_t num_module_paths;    // count for module_paths
48         char  *module_paths_str;    // module search paths, stringified
49
50         char *std_path;             // path to standard library
51
52         Program program;            // the result program
53 } LoadState;
54
55 // functions
56
57 // returns mallocated string
58 static inline char *get_filename(const char *module_path)
59 {
60         const char *try_names[3] = {
61                 "%s",
62                 "%s.uwu",
63                 "%s.so",
64         };
65
66         char *filename;
67
68         for (int i = 0; i < 3; i++) {
69                 filename = asprintf_wrapper(try_names[i], module_path);
70
71                 if (file_exists(filename))
72                         return filename;
73                 else
74                         free(filename);
75         }
76
77         return NULL;
78 }
79
80 // module_path is a mallocated string
81 static Module *load_module(LoadState *state, char *module_path)
82 {
83         for (size_t i = 0; i < state->num_modules; i++) {
84                 Module *module = state->modules[i];
85
86                 if (strcmp(module_path, module->path) == 0) {
87                         free(module_path);
88                         return module;
89                 }
90         }
91
92         char *filename = get_filename(module_path);
93
94         if (! filename) {
95                 free(module_path);
96                 return NULL;
97         }
98
99         size_t filename_len = strlen(filename);
100         UwUVMModuleType type = (filename_len >= 3 && strcmp(filename + filename_len - 3, ".so") == 0) ? MODULE_NATIVE : MODULE_PLAIN;
101
102         state->modules = realloc(state->modules, sizeof *state->modules * ++state->num_modules);
103         Module *module = state->modules[state->num_modules - 1] = malloc(sizeof *module);
104
105         *module = (Module) {
106                 .path = module_path,
107                 .filename = filename,
108                 .environment = dirname_wrapper(module_path),
109
110                 .type = type,
111
112                 .functions = NULL,
113                 .num_functions = 0,
114                 .loaded_functions = 0,
115         };
116
117         if (type == MODULE_PLAIN) {
118                 module->handle.ast = parse_file(filename);
119         } else {
120                 state->program.libraries = realloc(state->program.libraries, sizeof(void *) * ++state->program.num_libraries);
121                 state->program.libraries[state->program.num_libraries - 1] = module->handle.lib = dlopen(filename, RTLD_LAZY);
122
123                 check_dlerror();
124         }
125
126         return module;
127 }
128
129 static UwUVMFunction *require_function(LoadState *state, Module *module, const char *name)
130 {
131         for (size_t i = 0; i < module->num_functions; i++) {
132                 FunctionLink *link = &module->functions[i];
133
134                 if (strcmp(link->name, name) == 0)
135                         return link->ref;
136         }
137
138         UwUVMFunction *ref = malloc(sizeof *ref);
139         ref->type = module->type;
140
141         state->program.functions = realloc(state->program.functions, sizeof *state->program.functions * ++state->program.num_functions);
142         state->program.functions[state->program.num_functions - 1] = ref;
143
144         module->functions = realloc(module->functions, sizeof *module->functions * ++module->num_functions);
145         module->functions[module->num_functions - 1] = (FunctionLink) {
146                 .name = strdup(name),
147                 .ref = ref,
148         };
149
150         return ref;
151 }
152
153 static UwUVMFunction *resolve_function(LoadState *state, Module *caller_module, const char *full_name)
154 {
155         size_t len = strlen(full_name);
156
157         const char *fnname;
158         for (fnname = &full_name[len - 1]; *fnname != '.' && fnname > full_name; fnname--)
159                 ;
160
161         if (*fnname == '.')
162                 fnname++;
163
164         if (*fnname == '\0')
165                 error("module error: empty function name referenced/called by module %s\n", caller_module->filename);
166
167         Module *callee_module;
168
169         if (fnname == full_name) {
170                 callee_module = caller_module;
171         } else {
172                 char     **environments     = state->module_paths;
173                 size_t num_environments     = state->num_module_paths;
174                 char      *environments_str = state->module_paths_str;
175
176                 const char *callee_name = full_name;
177
178                 if (*callee_name == '.') {
179                         callee_name++;
180
181                         environments = &caller_module->environment;
182                         num_environments = 1;
183                         environments_str = caller_module->environment;
184                 }
185
186                 size_t path_len = fnname - callee_name;
187                 char callee_path[path_len];
188
189                 for (size_t i = 0; i < path_len; i++)
190                         callee_path[i] = (i == path_len - 1) ? '\0'
191                                 : (callee_name[i] == '.') ? '/'
192                                 : callee_name[i];
193
194                 for (size_t i = 0; i < num_environments; i++)
195                         if ((callee_module = load_module(state, asprintf_wrapper("%s/%s", environments[i], callee_path))))
196                                 break;
197
198                 if (! callee_module)
199                         error("module error: no module %s in path %s\n", callee_path, environments_str);
200         }
201
202         return require_function(state, callee_module, fnname);
203 }
204
205 static void translate_expression(LoadState *state, Module *module, UwUVMExpression *vm_expr, ParseExpression *parse_expr)
206 {
207         UwUVMFunction *vm_function;
208
209         if (parse_expr->type == EX_FNNAME || parse_expr->type == EX_FNCALL) {
210                 vm_function = resolve_function(state, module, parse_expr->value.str_value);
211                 free(parse_expr->value.str_value);
212         }
213
214         switch (vm_expr->type = parse_expr->type) {
215                 case EX_INTLIT:
216                 case EX_ARGNUM:
217                         vm_expr->value.int_value = parse_expr->value.int_value;
218                         break;
219
220                 case EX_STRLIT:
221                         vm_expr->value.str_value = parse_expr->value.str_value;
222                         break;
223
224                 case EX_FNNAME:
225                         vm_expr->value.ref_value = vm_function;
226                         break;
227
228                 case EX_FNCALL:
229                         vm_expr->value.cll_value.function = vm_function;
230                         vm_expr->value.cll_value.args = malloc(sizeof(UwUVMExpression) * parse_expr->num_children);
231                         vm_expr->value.cll_value.num_args = parse_expr->num_children;
232
233                         for (size_t i = 0; i < parse_expr->num_children; i++)
234                                 translate_expression(state, module, &vm_expr->value.cll_value.args[i], parse_expr->children[i]);
235
236                         if (parse_expr->children)
237                                 free(parse_expr->children);
238                         break;
239
240                 default:
241                         break;
242         }
243
244         free(parse_expr);
245 }
246
247 static void load_functions(LoadState *state, Module *module)
248 {
249         for (; module->loaded_functions < module->num_functions; module->loaded_functions++) {
250                 FunctionLink *link = &module->functions[module->loaded_functions];
251
252                 if (module->type == MODULE_PLAIN) {
253                         ParseFunction **function = NULL;
254
255                         for (size_t i = 0; i < module->handle.ast.num_functions; i++) {
256                                 ParseFunction **fn = &module->handle.ast.functions[i];
257
258                                 if (*fn && strcmp((*fn)->name, link->name) == 0) {
259                                         function = fn;
260                                         break;
261                                 }
262                         }
263
264                         if (function) {
265                                 translate_expression(state, module, link->ref->value.plain = malloc(sizeof(UwUVMExpression)), (*function)->expression);
266                                 free((*function)->name);
267                                 free(*function);
268
269                                 *function = NULL;
270                         } else {
271                                 error("module error: no function %s in module %s\n", link->name, module->filename);
272                         }
273                 } else {
274                         char *symbol = asprintf_wrapper("uwu_%s", link->name);
275                         link->ref->value.native = dlsym(module->handle.lib, symbol);
276
277                         check_dlerror();
278                         free(symbol);
279                 }
280         }
281 }
282
283 static void free_expression(ParseExpression *expr)
284 {
285         if (expr->type == EX_FNCALL) {
286                 for (size_t i = 0; i < expr->num_children; i++)
287                         free_expression(expr->children[i]);
288
289                 if (expr->children)
290                         free(expr->children);
291         }
292
293         if (expr->type != EX_INTLIT && expr->type != EX_ARGNUM)
294                 free(expr->value.str_value);
295
296         free(expr);
297 }
298
299 Program load_program(const char *progname, const char *modname)
300 {
301         char *prog_dirname = dirname_wrapper(progname);
302         char *api_path = asprintf_wrapper("%s/api/api.so", prog_dirname);
303
304         LoadState state = {
305                 .modules = NULL,
306                 .num_modules = 0,
307                 .program = {
308                         .api_library = dlopen(api_path, RTLD_NOW | RTLD_GLOBAL),
309                         .main_function = NULL,
310                         .functions = NULL,
311                         .num_functions = 0,
312                         .libraries = NULL,
313                         .num_libraries = 0,
314                 },
315         };
316
317         char *uwu_module_path = getenv("UWU_MODULE_PATH");
318
319         if (uwu_module_path) {
320                 char  *uwu_module_path_ptr = state.module_paths_str = uwu_module_path;
321                 char  *uwu_module_path_base_ptr = uwu_module_path_ptr;
322                 size_t uwu_module_path_len = 1;
323
324                 state.num_module_paths = 0;
325                 state.module_paths = NULL;
326
327                 for (;; uwu_module_path_ptr++, uwu_module_path_len++) {
328                         if (*uwu_module_path_ptr == '\0' || *uwu_module_path_ptr == ':') {
329                                 state.module_paths = realloc(state.module_paths, sizeof(char **) * ++state.num_module_paths);
330                                 strncpy(state.module_paths[state.num_module_paths - 1] = malloc(uwu_module_path_len), uwu_module_path_base_ptr, uwu_module_path_len)[uwu_module_path_len - 1] = '\0';
331
332                                 uwu_module_path_len = 0;
333                                 uwu_module_path_base_ptr = uwu_module_path_ptr + 1;
334                         }
335
336                         if (*uwu_module_path_ptr == '\0')
337                                 break;
338                 }
339         } else {
340                 state.module_paths_str = asprintf_wrapper("%s/std", prog_dirname);
341                 state.num_module_paths = 1;
342                 state.module_paths = malloc(sizeof(char **));
343                 state.module_paths[0] = state.module_paths_str;
344         }
345
346         free(prog_dirname);
347         free(api_path);
348
349         Module *main_module = load_module(&state, strdup(modname));
350
351         if (! main_module)
352                 error("module error: requested module %s not found\n", modname);
353
354         state.program.main_function = require_function(&state, main_module, "main");
355
356         while (true) {
357                 bool fully_loaded = true;
358
359                 for (size_t i = 0; i < state.num_modules; i++) {
360                         Module *module = state.modules[i];
361
362 #if DEBUG
363                         printf("%s %lu/%lu\n", module->filename, module->loaded_functions, module->num_functions);
364 #endif
365
366                         if (module->loaded_functions < module->num_functions) {
367                                 fully_loaded = false;
368                                 load_functions(&state, module);
369                         }
370                 }
371
372                 if (fully_loaded)
373                         break;
374         }
375
376         for (size_t i = 0; i < state.num_module_paths; i++)
377                 free(state.module_paths[i]);
378
379         free(state.module_paths);
380
381         for (size_t i = 0; i < state.num_modules; i++) {
382                 Module *module = state.modules[i];
383
384                 free(module->path);
385                 free(module->filename);
386                 free(module->environment);
387
388                 for (size_t f = 0; f < module->num_functions; f++)
389                         free(module->functions[f].name);
390
391                 free(module->functions);
392
393                 if (module->type == MODULE_PLAIN) {
394                         for (size_t f = 0; f < module->handle.ast.num_functions; f++) {
395                                 ParseFunction *function = module->handle.ast.functions[f];
396
397                                 if (function) {
398                                         free_expression(function->expression);
399                                         free(function->name);
400                                         free(function);
401                                 }
402                         }
403
404                         if (module->handle.ast.functions)
405                                 free(module->handle.ast.functions);
406                 }
407
408                 free(module);
409         }
410
411         free(state.modules);
412
413         return state.program;
414 }