#include #include #include #include #define VEC_NAME tokens #define VEC_TYPE char * #include 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 struct defs templates; struct defs traits; static void usage(FILE *f) { fprintf(f, "usage: ngc \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; }