From e68480f2fc508ce73cad331ce4e8439e07d25702 Mon Sep 17 00:00:00 2001 From: Kimplul Date: Mon, 1 Sep 2025 22:00:02 +0300 Subject: use <> as expansion characters + I kind of like the 'symmetry' of <> being 'self' and being some parameter, but having to type lots of extra <*> is a bit annoying, and might also be an issue for C++. Still, enough functionality for a hashmap example --- src/main.c | 221 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 204 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/main.c b/src/main.c index 38102fb..f3811c7 100644 --- a/src/main.c +++ b/src/main.c @@ -13,6 +13,8 @@ struct def { char *args; char *inst; char *body; + + struct def *tmpl; }; #define MAP_NAME defs @@ -24,6 +26,7 @@ struct def { struct defs templates; struct defs traits; +struct defs impls; static void usage(FILE *f) { @@ -368,10 +371,12 @@ static char *process_trait(char *id, struct tokens *tokens, char *grp, FILE *f) } struct def def = { - .id = strdup(id), + .id = strdup(id), .types = NULL, - .args = NULL, - .body = strdup(grp) + .args = NULL, + .inst = NULL, + .body = strdup(grp), + .tmpl = NULL }; defs_insert(&traits, def.id, def); @@ -412,7 +417,8 @@ static char *process_supertemplate(char *id, char *types, char *args, struct tok .types = strdup(types), .args = strdup(args), .body = strdup(body), - .inst = strdup(inst) + .inst = strdup(inst), + .tmpl = NULL }; defs_insert(&templates, def.id, def); @@ -485,6 +491,52 @@ static char *maybe_process_template(char *id, struct tokens *tokens, char *grp, return grp; } +static char *replace_trait(char *body, char *replace, char *id) +{ + /** @todo check that trait exists */ + assert(body); + assert(id); + size_t len = strlen(replace); + size_t idlen = strlen(id); + + /* check that replacement is regular alphanumeric */ + for (size_t i = 0; i < len; ++i) { + assert(isalnum(replace[i]) || replace[i] == '_'); + } + + char *new = malloc(1 * sizeof(char)); + new[0] = '\0'; + + size_t idx = 0; + while (body[idx]) { + if (body[idx] != '<') { + new = append(new, body[idx++]); + continue; + } + + if (strncmp(&body[idx + 1], replace, len) != 0) { + new = append(new, body[idx++]); + continue; + } + + if (body[idx + len + 1] != '>') { + new = append(new, body[idx++]); + continue; + } + + /* match */ + /** @todo check that func exists (whatever is following this + * template instanciation */ + for (size_t i = 0; i < idlen; ++i) + new = append(new, id[i]); + + idx += len + 2; + } + + free(body); + return new; +} + static char *replace_id(char *body, char *replace, char *id) { assert(body); @@ -545,15 +597,52 @@ static char *replace(char *body, char *replace, char *id) continue; } - if (strncmp(&body[idx], replace, len) == 0) { - for (size_t i = 0; i < idlen; ++i) - new = append(new, id[i]); + if (strncmp(&body[idx], replace, len) != 0) { + new = append(new, body[idx++]); + continue; + } + + for (size_t i = 0; i < idlen; ++i) + new = append(new, id[i]); + + idx += len; + } + + free(body); + return new; +} + +static char *remove_traits(char *body) +{ + char *new = malloc(1 * sizeof(char)); + new[0] = '\0'; + + size_t idx = 0; + while (body[idx]) { + if (body[idx] != '<') { + new = append(new, body[idx++]); + continue; + } + + if (body[idx + 1] != '[') { + new = append(new, body[idx++]); + continue; + } + + size_t start = idx; + while (body[idx] && body[idx] != ']') + idx++; - idx += len; + /* found what we expected, remove from body by not appending to + * new body */ + if (body[idx] && body[idx + 1] == '>') { + idx += 2; continue; } - new = append(new, body[idx++]); + /* not what we expected, include everything */ + for (size_t i = start; i < idx; ++i) + new = append(new, body[i]); } free(body); @@ -680,6 +769,52 @@ static void collect_const_args(struct tokens *types, struct tokens *params, stru free(grp); } +static bool check_implements(struct def *tmpl, char *trait) +{ + char *body = tmpl->body; + assert(body); + + /** @todo more robust checking */ + if (strcmp(trait, "any") == 0) + return true; + + size_t len = strlen(trait); + + size_t idx = 0; + while (body[idx]) { + if (body[idx] != '<') { + idx++; + continue; + } + + if (body[idx + 1] != '[') { + idx++; + continue; + } + + if (strncmp(&body[idx + 2], trait, len) != 0) { + idx++; + continue; + } + + if (body[idx + len + 2] != ']') { + idx++; + continue; + } + + if (body[idx + len + 3] != '>') { + idx++; + continue; + } + + /* found! */ + return true; + } + + /** @todo check super */ + return false; +} + static void expand_instance(char *id, char *instance) { char *grp = NULL; @@ -705,7 +840,7 @@ static void expand_instance(char *id, char *instance) if (!def) { /** @todo location info and so on */ fprintf(stderr, "no such template: %s\n", name); - goto cleanup; + abort(); } /* +1 to skip leading '{' */ @@ -713,7 +848,8 @@ static void expand_instance(char *id, char *instance) /* overwrite trailing '}' */ body[strlen(body) - 1] = '\0'; - body = replace(body, "<>", id); + body = remove_traits(body); + body = replace(body, "<>", id); if (def->inst) super = replace(strdup(def->inst), "<>", id); @@ -734,16 +870,31 @@ static void expand_instance(char *id, char *instance) } for (size_t i = 0; i < tokens_len(&type_params); ++i) { - char *name = *tokens_at(&type_params, i); - char *type = *tokens_at(&type_args, i); + char *trait = *tokens_at(&type_traits, i); + char *name = *tokens_at(&type_params, i); + char *type = *tokens_at(&type_args, i); + + struct def *def = defs_find(&traits, trait); + if (!def) { + fprintf(stderr, "no trait '%s'\n", trait); + abort(); + } + + struct def *impl = defs_find(&impls, type); + /* no definition found presumably means raw struct or something, + * try optimistically continuing */ + if (impl && !check_implements(impl->tmpl, trait)) { + fprintf(stderr, "'%s' does not implement '%s'\n", type, trait); + abort(); + } - body = replace_id(body, name, type); + body = replace_trait(body, name, type); if (super) - super = replace_id(super, name, type); + super = replace_trait(super, name, type); if (args) - args = replace_id(args, name, type); + args = replace_trait(args, name, type); } drop_tokens(&type_traits); @@ -779,6 +930,20 @@ static void expand_instance(char *id, char *instance) super = replace_id(super, name, arg); } + if (tokens_len(&const_types) != tokens_len(&const_args)) { + fprintf(stderr, "mismatch const types vs args\n"); + abort(); + } + + for (size_t i = 0; i < tokens_len(&const_types); ++i) { + char *type = *tokens_at(&const_types, i); + char *arg = *tokens_at(&const_args, i); + + printf("_Static_assert(_Generic((%s), %s: 1, default: 0)," + "\"expected %s\");\n", + arg, type, type); + } + if (super) expand_instance(id, super); @@ -788,7 +953,6 @@ static void expand_instance(char *id, char *instance) printf(body); -cleanup: free(body); free(super); @@ -811,6 +975,27 @@ static char *process_instance(char *id, struct tokens *tokens, char *grp, FILE * expand_instance(id, instance); + /* I hate this, this is effectively the same as in expand_instance */ + size_t instance_idx = 0; + optional_str_space(instance, &instance_idx); + grp = next_str_grp(grp, instance, &instance_idx); + assert(is_identifier(grp)); + char *tmpl_name = strdup(grp); + + struct def *tmpl = defs_find(&templates, tmpl_name); + if (tmpl) { + struct def inst = { + .id = strdup(id), + .types = NULL, + .args = NULL, + .inst = NULL, + .body = NULL, + .tmpl = tmpl + }; + + defs_insert(&impls, inst.id, inst); + } + /* ; can probably be ignored...?*/ drop_tokens(tokens); return grp; @@ -893,6 +1078,7 @@ int main(int argc, char *argv[]) templates = defs_create(0); traits = defs_create(0); + impls = defs_create(0); char *fname = argv[1]; @@ -908,6 +1094,7 @@ int main(int argc, char *argv[]) free_defs(&templates); free_defs(&traits); + free_defs(&impls); fclose(f); return ret; } -- cgit v1.2.3