diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/main.c | 615 |
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, ¶ms_idx); + + char *trait = next_str_grp(params, ¶ms_idx); + assert(is_identifier(trait)); + + optional_str_space(params, ¶ms_idx); + char *name = next_str_grp(params, ¶ms_idx); + assert(is_identifier(name)); + + optional_str_space(params, ¶ms_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; +} |