/* 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 #include #include #include #include #include #include #include #include #include #include #include #include #include /** * 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; if (mvcheck_root(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; }