aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/compiler.c179
-rw-r--r--src/ctpp.c7
-rw-r--r--src/imports.c7
-rw-r--r--src/main.c57
-rw-r--r--src/path.c54
-rw-r--r--src/res.c60
-rw-r--r--src/source.mk2
-rw-r--r--src/string.c86
8 files changed, 452 insertions, 0 deletions
diff --git a/src/compiler.c b/src/compiler.c
new file mode 100644
index 0000000..b8f5823
--- /dev/null
+++ b/src/compiler.c
@@ -0,0 +1,179 @@
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include <ct/compiler.h>
+#include <ct/string.h>
+#include <ct/debug.h>
+#include <ct/path.h>
+#include <ct/res.h>
+
+static int read_element(FILE *f, struct string *element)
+{
+ if (feof(f))
+ return -1;
+
+ str_clear(element);
+
+ char c = fgetc(f);
+ str_append(element, c);
+ if (!isalnum(c))
+ return 0;
+
+ while ((c = fgetc(f))) {
+ if (isalnum(c)) {
+ str_append(element, c);
+ } else {
+ ungetc(c, f);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static struct string *read_block(FILE *f, size_t *lines)
+{
+ size_t depth = 0;
+ bool maybe_statement = true;
+ struct string *block = new_string(NULL);
+ if (!block)
+ return NULL;
+
+ struct string *elem = new_string(NULL);
+ if (!elem) {
+ destroy_string(block);
+ return NULL;
+ }
+
+ size_t newlines = 0;
+ while (1) {
+ if (read_element(f, elem)) {
+ destroy_string(elem);
+ destroy_string(block);
+ return NULL;
+ }
+
+ if (maybe_statement && str_compare(elem, ";")) {
+ str_add(block, elem);
+ break;
+ }
+
+ if (str_compare(elem, "{")) {
+ maybe_statement = false;
+ depth += 1;
+ }
+
+ if (str_compare(elem, "}"))
+ depth -= 1;
+
+ str_add(block, elem);
+
+ if (str_compare(elem, "\n"))
+ newlines++;
+
+ if (!maybe_statement && depth == 0)
+ break;
+ }
+
+ if (lines)
+ *lines += newlines;
+
+ destroy_string(elem);
+ return block;
+}
+
+static int process(struct res *r, const char *file)
+{
+ FILE *f = fopen(file, "r");
+ if (!f) {
+ error("failed opening %s: %s\n", file, strerror(errno));
+ return -1;
+ }
+
+ size_t lines = 1, prev_lines = 1;
+ struct string *block;
+ while ((block = read_block(f, &lines))) {
+ debug("block: (%zu - %zu, %zu)\n%s\n", prev_lines, lines,
+ lines - prev_lines, block->buf);
+ prev_lines = lines + 1;
+
+ /* with each block,
+ * A) Detect if it is an import block and what kind,
+ * in which case process the file that it refers to.
+ *
+ * B) Detect if it is a macro block and what kind, in which case
+ * parse it and add it to the macro subsystem.
+ *
+ * C) Run the preprocessor on the block.
+ *
+ * D) Actually lex/parse/generate AST for the block.
+ *
+ * E) Run semantic checks on the block.
+ *
+ * With all of these parts checked, I'm pretty sure we can
+ * directly generate code for each block. Possibly something
+ * like one C file per CT file, will have to look into what
+ * might be easiest. Also, some internal duplicate checking, so
+ * that we don't accidentally generate the same generic function
+ * in two different files or something like that.
+ *
+ * Same duplicate checking could be useful for the interpreted
+ * approach.
+ *
+ * Step D) could potentially be done on request, maybe fitting
+ * for the interpreted approach, I guess it depends on how fast the
+ * lexing/parsing/AST generation for each block is. Semantic
+ * checking is what I suspect will be the slowest part.
+ *
+ * Oh yeah, need some way to push result of some lower file
+ * processing upwards. And probably detecting a main block as
+ * well?
+ */
+ destroy_string(block);
+ }
+
+ fclose(f);
+ return 0;
+}
+
+void compile(const char *file) {
+ struct res *r = res_create();
+ res_add(r, (void *)(file = strdup(file)));
+
+ const char *base = ct_basename(file);
+ res_add(r, (void *)base);
+ if (!base)
+ goto out;
+
+ const char *dir = ct_dirname(file);
+ res_add(r, (void *)dir);
+ if (!dir)
+ goto out;
+
+ const char *cwd = ct_cwdname();
+ res_add(r, (void *)cwd);
+ if (!cwd)
+ goto out;
+
+ debug("(cwd:%s)(dir:%s)(base:%s)\n", cwd, dir, base);
+
+ if (chdir(dir)) {
+ error("couldn't change to directory %s: %s\n", dir, strerror(errno));
+ goto out;
+ }
+
+ if (process(r, base))
+ goto out;
+
+ if (chdir(cwd)) {
+ error("couldn't change back to directory %s: %s\n", cwd, strerror(errno));
+ goto out;
+ }
+
+
+out:
+ res_destroy(r);
+}
diff --git a/src/ctpp.c b/src/ctpp.c
new file mode 100644
index 0000000..f0759d0
--- /dev/null
+++ b/src/ctpp.c
@@ -0,0 +1,7 @@
+#include <ct/ctpp.h>
+#include <ct/debug.h>
+
+void add_predefined_variable(const char *var)
+{
+ debug("adding predefined variable '%s'...\n", var);
+}
diff --git a/src/imports.c b/src/imports.c
new file mode 100644
index 0000000..e4d6aa7
--- /dev/null
+++ b/src/imports.c
@@ -0,0 +1,7 @@
+#include <ct/imports.h>
+#include <ct/debug.h>
+
+void add_import_path(const char *dir)
+{
+ debug("adding import path %s...\n", dir);
+}
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..93b9dfa
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,57 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <ct/ctpp.h>
+#include <ct/debug.h>
+#include <ct/imports.h>
+#include <ct/compiler.h>
+
+static const char *cmdline_usage =
+"C with traits compiler usage:\n"
+" ct [-I <dir>...] [-D <var>...] infile...\n"
+" -h Show usage (this)\n"
+" -I <dir> Add directory to import path\n"
+" -D <var> Add predefined variable\n"
+" infile Top file to compile\n"
+;
+
+static void usage()
+{
+ fprintf(stderr, cmdline_usage);
+}
+
+int main(int argc, char *argv[])
+{
+ int opt;
+ while ((opt = getopt(argc, argv, "hI:D:")) != -1) {
+ switch (opt) {
+ case 'I':
+ add_import_path(optarg);
+ break;
+
+ case 'D':
+ add_predefined_variable(optarg);
+ break;
+
+ case 'h':
+ usage();
+ exit(EXIT_SUCCESS);
+ default:
+ usage();
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (optind >= argc) {
+ error("no input files\n");
+ exit(EXIT_FAILURE);
+ }
+
+ for (int i = optind; i < argc; ++i) {
+ debug("starting compilation of '%s'\n", argv[i]);
+ compile(argv[i]);
+ }
+
+ return 0;
+}
diff --git a/src/path.c b/src/path.c
new file mode 100644
index 0000000..c9160bb
--- /dev/null
+++ b/src/path.c
@@ -0,0 +1,54 @@
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <ct/path.h>
+#include <ct/debug.h>
+
+char *ct_basename(const char *file)
+{
+ size_t l = strlen(file);
+ size_t n = l - 1;
+ while (--n) {
+ if (file[n] == '/')
+ break;
+ }
+
+ return strndup(file + n + 1, l - n);
+}
+
+char *ct_dirname(const char *file)
+{
+ size_t l = strlen(file);
+ size_t n = l - 1;
+ while (--n) {
+ if (file[n] == '/')
+ break;
+ }
+
+ return strndup(file, n);
+}
+
+char *ct_cwdname()
+{
+ size_t size;
+ long path_max = pathconf(".", _PC_PATH_MAX);
+ if (path_max == -1)
+ size = 1024;
+ else
+ size = (size_t)path_max;
+
+ char *buf = malloc(size);
+ if (!buf)
+ return NULL;
+
+ if (!getcwd(buf, size)) {
+ error("%s\n", strerror(errno));
+ free(buf);
+ return NULL;
+ }
+
+ return buf;
+}
diff --git a/src/res.c b/src/res.c
new file mode 100644
index 0000000..787f50a
--- /dev/null
+++ b/src/res.c
@@ -0,0 +1,60 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+
+/**
+ * @file res.c
+ * Simple resource manager implementation.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <ct/res.h>
+
+/**
+ * Grow resource manager buffer.
+ * Doubles every call.
+ *
+ * @param r Resource manager to expand.
+ */
+static void res_expand(struct res *r)
+{
+ r->max *= 2;
+ r->p = realloc(r->p, r->max * sizeof(void *));
+}
+
+struct res *res_create()
+{
+ struct res *r = malloc(sizeof(struct res));
+ r->n = 0;
+ /* arbitrary number */
+ r->max = 1024;
+ r->p = calloc(1, r->max * sizeof(void *));
+ return r;
+}
+
+void res_add(struct res *r, void *p)
+{
+ if (r->n >= r->max)
+ res_expand(r);
+
+ r->p[r->n++] = p;
+}
+
+void *res_alloc(struct res *r, size_t size)
+{
+ void *p = malloc(size);
+ if (!p)
+ return NULL;
+
+ res_add(r, p);
+ return p;
+}
+
+void res_destroy(struct res *r)
+{
+ for (size_t i = 0; i < r->n; ++i)
+ free(r->p[i]);
+
+ free(r->p);
+ free(r);
+}
diff --git a/src/source.mk b/src/source.mk
new file mode 100644
index 0000000..fd4a8e6
--- /dev/null
+++ b/src/source.mk
@@ -0,0 +1,2 @@
+SRC_LOCAL != echo src/*.c
+SOURCES += $(SRC_LOCAL)
diff --git a/src/string.c b/src/string.c
new file mode 100644
index 0000000..5cce400
--- /dev/null
+++ b/src/string.c
@@ -0,0 +1,86 @@
+#include <errno.h>
+#include <stdlib.h>
+
+#include <ct/debug.h>
+#include <ct/string.h>
+
+
+struct string *new_string(const char *s)
+{
+ struct string *n = malloc(sizeof(struct string));
+ if (!n) {
+ error("allocating new string: %s\n", strerror(errno));
+ return NULL;
+ }
+
+ if (!s) {
+ n->len = 1;
+ n->buf = calloc(1, 1);
+ return n;
+ }
+
+ n->len = strlen(s) + 1;
+ n->buf = strdup(s);
+ return n;
+}
+
+void destroy_string(struct string *s)
+{
+ free(s->buf);
+ free(s);
+}
+
+int str_append(struct string *s, char c)
+{
+ s->len += 1;
+ s->buf = realloc(s->buf, s->len);
+ if (!s->buf) {
+ error("appending to string: %s\n", strerror(errno));
+ return -1;
+ }
+
+ s->buf[s->len - 2] = c;
+ s->buf[s->len - 1] = 0;
+ return 0;
+}
+
+int str_concat(struct string *s, const char *c)
+{
+ size_t c_len = strlen(c);
+ size_t len = s->len + c_len;
+ s->buf = realloc(s->buf, len);
+ if (!s->buf) {
+ error("concatenating strings: %s\n", strerror(errno));
+ return -1;
+ }
+
+ strncat(s->buf + s->len - 1, c, c_len);
+ s->len = len;
+ return 0;
+}
+
+int str_add(struct string *s, struct string *c)
+{
+ size_t len = s->len + c->len - 1;
+ s->buf = realloc(s->buf, len);
+ if (!s->buf) {
+ error("adding to string: %s\n", strerror(errno));
+ return -1;
+ }
+
+ strncat(s->buf + s->len - 1, c->buf, c->len);
+ s->len = len;
+ return 0;
+}
+
+bool str_compare(struct string *s, const char *c)
+{
+ return strncmp(s->buf, c, s->len - 1) == 0;
+}
+
+void str_clear(struct string *s)
+{
+ s->len = 1;
+ if (s->buf)
+ s->buf[0] = 0;
+}