diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/compiler.c | 179 | ||||
| -rw-r--r-- | src/ctpp.c | 7 | ||||
| -rw-r--r-- | src/imports.c | 7 | ||||
| -rw-r--r-- | src/main.c | 57 | ||||
| -rw-r--r-- | src/path.c | 54 | ||||
| -rw-r--r-- | src/res.c | 60 | ||||
| -rw-r--r-- | src/source.mk | 2 | ||||
| -rw-r--r-- | src/string.c | 86 |
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; +} |
