aboutsummaryrefslogtreecommitdiff
path: root/src/compiler.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler.c')
-rw-r--r--src/compiler.c138
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;
+}