diff options
Diffstat (limited to 'src/compiler.c')
-rw-r--r-- | src/compiler.c | 138 |
1 files changed, 138 insertions, 0 deletions
diff --git a/src/compiler.c b/src/compiler.c new file mode 100644 index 0000000..d18e767 --- /dev/null +++ b/src/compiler.c @@ -0,0 +1,138 @@ +/* SPDX-License-Identifier: copyleft-next-0.3.1 */ +/* Copyright 2024 Kim Kuparinen < kimi.h.kuparinen@gmail.com > */ + +/** + * @file compiler.c + * + * Top level compiler implementation. + */ + +#include <stdbool.h> +#include <string.h> +#include <unistd.h> +#include <ctype.h> +#include <stdlib.h> +#include <limits.h> +#include <errno.h> + +#include <fwd/compiler.h> +#include <fwd/analyze.h> +#include <fwd/parser.h> +#include <fwd/debug.h> +#include <fwd/scope.h> +#include <fwd/lower.h> + +/** + * Read whole file into a buffer and return pointer to buffer. + * Possibly kind of silly to have both \p file and \p f. + * Apparently there's no standardized way to get the file name of a + * file pointer. + * + * @param file Name of file to read. + * @param f File pointer. + * @return Pointer to buffer with file contents. + */ +static char *read_file(const char *file, FILE *f) +{ + fseek(f, 0, SEEK_END); + /** @todo check how well standardized this actually is */ + long s = ftell(f); + if (s == LONG_MAX) { + error("%s might be a directory", file); + return NULL; + } + + fseek(f, 0, SEEK_SET); + + char *buf = malloc((size_t)(s + 1)); + if (!buf) + return NULL; + + fread(buf, (size_t)(s + 1), 1, f); + /* remember terminating null */ + buf[s] = 0; + return buf; +} + +/** + * Helper for process_file(), actually processes the file + * after process_file() has done path lookups and working directory + * changes and whatnot. + * + * @param parent Parent file context. \c NULL if root file. + * @param public \c 1 if file is being imported publicly, \c 0 otherwise. + * @param file File name to process. + * @return \c 0 if processing was succesful, non-zero value otherwise. + */ +static int process(struct scope **parent, const char *file) +{ + FILE *f = fopen(file, "rb"); + if (!f) { + error("failed opening %s: %s\n", file, strerror(errno)); + return -1; + } + + const char *buf = read_file(file, f); + fclose(f); + + if (!buf) + return -1; + + struct parser *p = create_parser(); + if (!p) { + free((void *)buf); + return -1; + } + + parse(p, file, buf); + struct ast *tree = p->tree; + bool failed = p->failed; + destroy_parser(p); + + if (failed) { + free((void*)buf); + return -1; + } + + ast_dump_list(0, tree); + + struct scope *scope = create_scope(); + if (!scope) { + free((void *)buf); + return -1; + } + + scope->fctx.fbuf = buf; + scope->fctx.fname = strdup(file); + if (*parent) + scope_add_scope(*parent, scope); + else + *parent = scope; + + if (analyze_root(scope, tree)) + return -1; + + return 0; +} + +int compile(const char *input) { + int ret = -1; + struct scope *root = NULL; + if (process(&root, input)) { + destroy_scope(root); + destroy_allocs(); + error("processing of %s stopped due to errors", input); + return ret; + } + + if ((ret = lower(root))) { + destroy_scope(root); + destroy_allocs(); + error("lowering of %s stopped due to errors", input); + return ret; + } + + destroy_scope(root); + destroy_allocs(); + return 0; +} |