aboutsummaryrefslogtreecommitdiff
path: root/src/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.c')
-rw-r--r--src/main.c615
1 files changed, 615 insertions, 0 deletions
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..0c7f8cd
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,615 @@
+#include <stdio.h>
+#include <ctype.h>
+#include <assert.h>
+#include <string.h>
+
+#define VEC_NAME tokens
+#define VEC_TYPE char *
+#include <conts/vec.h>
+
+struct def {
+ char *id;
+ char *types;
+ char *args;
+ char *body;
+};
+
+#define MAP_NAME defs
+#define MAP_KEY char *
+#define MAP_TYPE struct def
+#define MAP_CMP(a, b) strcmp((a), (b))
+#define MAP_HASH(a) CONTS_MAP_STR_HASH(a)
+#include <conts/map.h>
+
+struct defs templates;
+struct defs traits;
+
+static void usage(FILE *f)
+{
+ fprintf(f, "usage: ngc <file>\n");
+}
+
+static char *append(char *grp, char c)
+{
+ /* pretty slow but good enough for now I guess */
+ size_t len = strlen(grp);
+ grp = realloc(grp, len + 2);
+ grp[len] = c;
+ grp[len + 1] = '\0';
+ return grp;
+}
+
+static bool char_in(char c, const char *group)
+{
+ for (size_t i = 0; i < strlen(group); ++i)
+ if (c == group[i])
+ return true;
+
+ return false;
+}
+
+static void optional_str_space(char *str, size_t *idx)
+{
+ if (!isspace(str[*idx]))
+ return;
+
+ while (str[*idx] && isspace(str[*idx])) {
+ (*idx)++;
+ }
+}
+
+static char *next_str_grp(char *str, size_t *idx)
+{
+ char *next = malloc(1 * sizeof(char));
+ next[0] = '\0';
+
+ while (1) {
+ char c = str[*idx];
+ if (!c)
+ return next;
+
+ if (isspace(c))
+ return next;
+
+ /* special separators */
+ if (char_in(c, "{[()]},.;"))
+ return next;
+
+ next = append(next, c);
+ (*idx)++;
+ }
+
+ return NULL;
+}
+
+static char *next_str_until(char *str, size_t *idx, char *until)
+{
+ char *next = malloc(1 * sizeof(char));
+ next[0] = '\0';
+
+ while (1) {
+ char c = str[*idx];
+ if (!c)
+ return next;
+
+ if (char_in(c, until))
+ return next;
+
+ next = append(next, c);
+ (*idx)++;
+ }
+
+ return NULL;
+}
+
+static char *optional_space(FILE *f)
+{
+ int c = fgetc(f);
+ if (c == EOF)
+ return NULL;
+
+ if (!isspace(c)) {
+ ungetc(c, f);
+ return NULL;
+ }
+
+ char *space = malloc(2 * sizeof(char));
+ space[0] = c;
+ space[1] = '\0';
+
+ while (1) {
+ c = fgetc(f);
+ if (c == EOF)
+ return space;
+
+ if (!isspace(c)) {
+ ungetc(c, f);
+ return space;
+ }
+
+ space = append(space, c);
+ }
+
+ return NULL;
+}
+
+static char *next_grp(char *grp, FILE *f)
+{
+ /* the worst C parser you'll ever see, lol */
+ int c = fgetc(f);
+ if (c == EOF) {
+ free(grp);
+ return NULL;
+ }
+
+ grp = realloc(grp, 2 * sizeof(char *));
+ grp[0] = c;
+ grp[1] = '\0';
+
+ if (isspace(c)) {
+ while (1) {
+ c = fgetc(f);
+ if (c == EOF)
+ return grp;
+
+ if (!isspace(c)) {
+ ungetc(c, f);
+ return grp;
+ }
+
+ grp = append(grp, c);
+ }
+
+ return NULL;
+ }
+
+ while (1) {
+ c = fgetc(f);
+ if (c == EOF)
+ return grp;
+
+ if (isspace(c)) {
+ ungetc(c, f);
+ return grp;
+ }
+
+ /* special separators */
+ if (char_in(c, "{[(,.*")) {
+ ungetc(c, f);
+ return grp;
+ }
+
+ grp = append(grp, c);
+ if (char_in(c, "}]);"))
+ return grp;
+ }
+
+ return NULL;
+}
+
+static void dump_tokens(struct tokens *tokens)
+{
+ foreach(tokens, t, tokens) {
+ printf("%s", *t);
+ free(*t);
+ }
+ tokens_destroy(tokens);
+}
+
+static void drop_tokens(struct tokens *tokens)
+{
+ foreach(tokens, t, tokens) {
+ free(*t);
+ }
+ tokens_destroy(tokens);
+}
+
+static char *collect_block(char *grp, FILE *f, int start, int end)
+{
+ int c = fgetc(f);
+ assert(c == start);
+
+ grp = realloc(grp, 2 * sizeof(char));
+ grp[0] = c;
+ grp[1] = '\0';
+
+ size_t depth = 1;
+ while (1) {
+ int c = fgetc(f);
+ assert(c != EOF);
+ grp = append(grp, c);
+
+ if (c == start)
+ depth++;
+
+ if (c == end)
+ depth--;
+
+ if (depth == 0)
+ return grp;
+ }
+
+ return NULL;
+}
+
+static int is_identifier(char *id)
+{
+ while (*id) {
+ if (!isalnum(*id) && *id != '_')
+ return false;
+
+ id++;
+ }
+
+ return true;
+}
+
+static char *collect_square_block(char *grp, FILE *f)
+{
+ return collect_block(grp, f, '[', ']');
+}
+
+static char *collect_paren_block(char *grp, FILE *f)
+{
+ return collect_block(grp, f, '(', ')');
+}
+
+static char *collect_curly_block(char *grp, FILE *f)
+{
+ return collect_block(grp, f, '{', '}');
+}
+
+static char *process_trait(char *id, struct tokens *tokens, char *grp, FILE *f)
+{
+ printf("/* found trait %s*/", id);
+ grp = collect_curly_block(grp, f);
+ assert(grp[0] == '{');
+
+ struct def *exists = defs_find(&traits, id);
+ if (exists) {
+ fprintf(stderr, "trait %s redefined\n", id);
+ drop_tokens(tokens);
+ return grp;
+ }
+
+ struct def def = {
+ .id = strdup(id),
+ .types = NULL,
+ .args = NULL,
+ .body = strdup(grp)
+ };
+
+ defs_insert(&traits, def.id, def);
+ drop_tokens(tokens);
+ return grp;
+}
+
+static char *process_supertemplate(char *id, char *types, char *args, struct tokens *tokens, char *grp, FILE *f)
+{
+ printf("/* found supertemplate %s */", id);
+ dump_tokens(tokens);
+ return grp;
+}
+
+static char *maybe_process_template(char *id, struct tokens *tokens, char *grp, FILE *f)
+{
+ printf("/* maybe found template */");
+ /* [type args] */
+ grp = collect_square_block(grp, f);
+ char *types = strdup(grp);
+ tokens_append(tokens, types);
+
+ char *space = optional_space(f);
+ if (space)
+ tokens_append(tokens, space);
+
+ /* should be () if we're a template */
+ int c = fgetc(f);
+ ungetc(c, f);
+ if (c != '(') {
+ printf("/* nope, no parens */");
+ dump_tokens(tokens);
+ return grp;
+ }
+
+ /* (value args) */
+ grp = collect_paren_block(grp, f);
+ char *args = strdup(grp);
+ tokens_append(tokens, args);
+
+ space = optional_space(f);
+ if (space)
+ tokens_append(tokens, space);
+
+ c = fgetc(f);
+ ungetc(c, f);
+ if (c == '=')
+ return process_supertemplate(id, types, args, tokens, grp, f);
+
+ if (c != '{') {
+ printf("/* nope, no block */");
+ dump_tokens(tokens);
+ return grp;
+ }
+
+ printf("/* it is! %s*/", id);
+ char *body = grp = collect_curly_block(grp, f);
+
+ struct def *exists = defs_find(&templates, id);
+ if (exists) {
+ fprintf(stderr, "template %s redefined\n", id);
+ drop_tokens(tokens);
+ return grp;
+ }
+
+ struct def def = {
+ /* these could be gotten rid of but works for now */
+ .id = strdup(id),
+ .types = strdup(types),
+ .args = strdup(args),
+ .body = strdup(body)
+ };
+
+ defs_insert(&templates, def.id, def);
+ drop_tokens(tokens);
+ return grp;
+}
+
+static bool word(char *hay, char *needle, size_t len)
+{
+ for (size_t i = 0; i < len; ++i) {
+ if (hay[i] != needle[i])
+ return false;
+ }
+
+ /* word ends in something other than alphanumeric */
+ return !isalnum(hay[len]);
+}
+
+static char *replace(char *body, char *replace, char *id, bool wordboundary)
+{
+ assert(body);
+ assert(id);
+
+ char *orig = body;
+ char *new = malloc(1 * sizeof(char));
+ new[0] = '\0';
+
+ size_t len = strlen(replace);
+
+ while (body[0]) {
+ /* lol terrible */
+ bool found = wordboundary ? word(body, replace, len)
+ : strncmp(body, replace, len) == 0;
+
+ if (found) {
+ char *copy = id;
+ while (*copy) {
+ new = append(new, *copy);
+ copy++;
+ }
+
+ body += len;
+ continue;
+ }
+
+ new = append(new, body[0]);
+ body += 1;
+ }
+
+ free(orig);
+ return new;
+}
+
+static char *process_instance(char *id, struct tokens *tokens, char *grp, FILE *f)
+{
+ printf("/* found instance %s */", id);
+ /* discard equals sign */
+ int c = fgetc(f);
+ assert(c == '=');
+
+ char *space = optional_space(f);
+ if (space)
+ tokens_append(tokens, space);
+
+ /* template */
+ grp = next_grp(grp, f);
+ char *template = strdup(grp);
+ assert(is_identifier(template));
+ tokens_append(tokens, template);
+
+ /* types */
+ space = optional_space(f);
+ if (space)
+ tokens_append(tokens, space);
+
+ grp = collect_square_block(grp, f);
+ char *types = strdup(grp);
+ tokens_append(tokens, types);
+
+ /* args */
+ space = optional_space(f);
+ if (space)
+ tokens_append(tokens, space);
+
+ grp = collect_paren_block(grp, f);
+ char *args = strdup(grp);
+ tokens_append(tokens, args);
+
+ struct def *def = defs_find(&templates, template);
+ if (!def) {
+ /** @todo location info and so on */
+ fprintf(stderr, "no such template: %s\n", template);
+ return grp;
+ }
+
+ /** @todo check traits */
+
+ /* +1 to skip leading '{' */
+ char *body = strdup(def->body + 1);
+ /* overwrite trailing '}' */
+ body[strlen(body) - 1] = '\0';
+
+ body = replace(body, "<>", id, false);
+
+ /* handle type args */
+ char *params = def->types;
+ assert(params[0] == '[');
+
+ /* skip '[' */
+ size_t params_idx = 1;
+
+ /* skip '(' in args */
+ size_t types_idx = 1;
+
+ while (1) {
+ /* get type param */
+ optional_str_space(params, &params_idx);
+
+ char *trait = next_str_grp(params, &params_idx);
+ assert(is_identifier(trait));
+
+ optional_str_space(params, &params_idx);
+ char *name = next_str_grp(params, &params_idx);
+ assert(is_identifier(name));
+
+ optional_str_space(params, &params_idx);
+ char delim = params[params_idx++];
+ assert(delim == ',' || delim == ']');
+
+ /* get arg type */
+ optional_str_space(types, &types_idx);
+ char *type = next_str_until(types, &types_idx, ",]");
+ char last = types[types_idx++];
+ assert(last == ',' || last == ']');
+
+ /** @todo assert that we're dealing with a type? */
+ body = replace(body, name, type, true);
+
+ free(trait);
+ free(name);
+ free(type);
+
+ if (delim == ']' && last == ']')
+ break;
+
+ if (delim == ']') {
+ fprintf(stderr, "more type args than params\n");
+ free(body);
+ drop_tokens(tokens);
+ return grp;
+ }
+
+ if (last == ']') {
+ fprintf(stderr, "more type params than args\n");
+ free(body);
+ drop_tokens(tokens);
+ return grp;
+ }
+ }
+
+ /** @todo regular params, probably much like above but parsing the type
+ * might be a bit annoying... */
+
+ printf(body);
+ free(body);
+
+ /* ; can probably be ignored...?*/
+ drop_tokens(tokens);
+ return grp;
+}
+
+static char *process_typedef(char *grp, FILE *f)
+{
+ struct tokens tokens = tokens_create(3);
+ tokens_append(&tokens, strdup(grp));
+
+ /* space */
+ grp = next_grp(grp, f);
+ assert(grp);
+ tokens_append(&tokens, strdup(grp));
+
+ /* id */
+ grp = next_grp(grp, f);
+ assert(grp);
+
+ char *id = strdup(grp);
+ assert(is_identifier(id));
+ tokens_append(&tokens, id);
+
+ /* space/check */
+ char *space = optional_space(f);
+ if (space)
+ tokens_append(&tokens, space);
+
+ char c = fgetc(f);
+ ungetc(c, f);
+ if (c == '{'
+ && strcmp(id, "struct") != 0
+ && strcmp(id, "union") != 0
+ && strcmp(id, "enum") != 0)
+ return process_trait(id, &tokens, grp, f);
+
+ if (c == '[')
+ return maybe_process_template(id, &tokens, grp, f);
+
+ if (c == '=')
+ return process_instance(id, &tokens, grp, f);
+
+ dump_tokens(&tokens);
+ return grp;
+}
+
+static int process(FILE *f)
+{
+ char *grp = NULL;
+ while ((grp = next_grp(grp, f))) {
+ if (strcmp(grp, "typedef") == 0)
+ grp = process_typedef(grp, f);
+ else
+ printf("%s", grp);
+ }
+
+ return 0;
+}
+
+static void free_defs(struct defs *defs)
+{
+ foreach(defs, def, defs) {
+ struct def d = def->data;
+ free(d.id);
+ free(d.types);
+ free(d.args);
+ free(d.body);
+ }
+
+ defs_destroy(defs);
+}
+
+int main(int argc, char *argv[])
+{
+ if (argc != 2) {
+ usage(stderr);
+ return -1;
+ }
+
+ templates = defs_create(0);
+ traits = defs_create(0);
+
+ char *fname = argv[1];
+
+ FILE *f = NULL;
+ if (strcmp(fname, "-") == 0)
+ f = stdin;
+ else
+ f = fopen(fname, "rb");
+
+ assert(f);
+
+ int ret = process(f);
+
+ free_defs(&templates);
+ free_defs(&traits);
+ fclose(f);
+ return ret;
+}