aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKimplul <kimi.h.kuparinen@gmail.com>2025-07-18 21:33:50 +0300
committerKimplul <kimi.h.kuparinen@gmail.com>2025-07-18 21:33:50 +0300
commit94b767679fc68c51aa0c80bdada35b9978989199 (patch)
tree711af40c89140a9add7db30cf6a81861a3df1013
parent1564f79cfc7d790e15298d28f52b4c5c216ce9e5 (diff)
downloadngc-94b767679fc68c51aa0c80bdada35b9978989199.tar.gz
ngc-94b767679fc68c51aa0c80bdada35b9978989199.zip
add supertemplate handling
-rw-r--r--.gitignore1
-rw-r--r--Makefile5
-rw-r--r--example_vec/new/main.c15
-rw-r--r--src/main.c334
-rwxr-xr-xtest/compile_main.sh8
-rw-r--r--test/main.c19
-rw-r--r--test/vec.h49
-rw-r--r--tests/Makefile5
-rw-r--r--tests/xok/Makefile15
-rw-r--r--tests/xok/supertmpl.c24
-rw-r--r--tests/xok/tmpl.c17
11 files changed, 329 insertions, 163 deletions
diff --git a/.gitignore b/.gitignore
index eb92346..6a3d0a2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,2 @@
-ngc
ngc1
build
diff --git a/Makefile b/Makefile
index 7a49e49..758f2c9 100644
--- a/Makefile
+++ b/Makefile
@@ -3,6 +3,11 @@ all: ngc1
ngc1: src/main.c
$(CC) -Ideps/conts/include -Wall -Wextra -g src/main.c -o ngc1
+.PHONY: check
+check: all
+ $(MAKE) -C tests
+
.PHONY: clean
clean:
$(RM) ngc1
+ $(MAKE) -C tests clean
diff --git a/example_vec/new/main.c b/example_vec/new/main.c
index a00c35c..009911e 100644
--- a/example_vec/new/main.c
+++ b/example_vec/new/main.c
@@ -1,10 +1,21 @@
#include "vec.h"
-typedef ints = vec[int]();
-
#define foreach(name, i, s) \
for (name##_iter i = name##_begin(s); !name##_end(s, i); i = name##_next(i))
+typedef summable_vec[summable type]() = vec[type](){
+ type <>_sum(struct <> *v) {
+ type sum = 0;
+ for (size_t i = 0; i < v->n; ++i) {
+ sum += v->buf[i];
+ }
+
+ return sum;
+ }
+};
+
+typedef ints = summable_vec[int]();
+
int main()
{
struct ints ints = ints_create(0);
diff --git a/src/main.c b/src/main.c
index 0c7f8cd..bf4d06f 100644
--- a/src/main.c
+++ b/src/main.c
@@ -11,6 +11,7 @@ struct def {
char *id;
char *types;
char *args;
+ char *inst;
char *body;
};
@@ -58,47 +59,49 @@ static void optional_str_space(char *str, size_t *idx)
}
}
-static char *next_str_grp(char *str, size_t *idx)
+static char *next_str_grp(char *grp, char *str, size_t *idx)
{
- char *next = malloc(1 * sizeof(char));
- next[0] = '\0';
+ grp = realloc(grp, sizeof(char));
+ grp[0] = '\0';
while (1) {
char c = str[*idx];
if (!c)
- return next;
+ return grp;
if (isspace(c))
- return next;
+ return grp;
/* special separators */
if (char_in(c, "{[()]},.;"))
- return next;
+ return grp;
- next = append(next, c);
+ grp = append(grp, c);
(*idx)++;
}
+ free(grp);
return NULL;
}
-static char *next_str_until(char *str, size_t *idx, char *until)
+static char *next_str_until(char *grp, char *str, size_t *idx, char *until)
{
- char *next = malloc(1 * sizeof(char));
- next[0] = '\0';
+ grp = realloc(grp, sizeof(char));
+ grp[0] = '\0';
while (1) {
char c = str[*idx];
if (!c)
- return next;
+ return grp;
if (char_in(c, until))
- return next;
+ return grp;
- next = append(next, c);
+ grp = append(grp, c);
(*idx)++;
}
+ free(grp);
return NULL;
}
@@ -204,6 +207,26 @@ static void drop_tokens(struct tokens *tokens)
tokens_destroy(tokens);
}
+static char *collect_until(char *grp, FILE *f, int until)
+{
+ grp = realloc(grp, sizeof(char));
+ grp[0] = '\0';
+
+ while (1) {
+ int c = fgetc(f);
+ assert(c != EOF);
+
+ if (c == until) {
+ ungetc(c, f);
+ return grp;
+ }
+
+ grp = append(grp, c);
+ }
+
+ return NULL;
+}
+
static char *collect_block(char *grp, FILE *f, int start, int end)
{
int c = fgetc(f);
@@ -232,6 +255,34 @@ static char *collect_block(char *grp, FILE *f, int start, int end)
return NULL;
}
+static char *collect_str_block(char *grp, char *str, size_t *idx, int start, int end)
+{
+ char c = str[(*idx)++];
+ assert(c == start);
+
+ grp = realloc(grp, 2 * sizeof(char));
+ grp[0] = start;
+ grp[1] = '\0';
+
+ size_t depth = 1;
+ while (1) {
+ c = str[(*idx)++];
+ 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) {
@@ -259,6 +310,16 @@ static char *collect_curly_block(char *grp, FILE *f)
return collect_block(grp, f, '{', '}');
}
+static char *collect_str_square_block(char *grp, char *str, size_t *idx)
+{
+ return collect_str_block(grp, str, idx, '[', ']');
+}
+
+static char *collect_str_paren_block(char *grp, char *str, size_t *idx)
+{
+ return collect_str_block(grp, str, idx, '(', ')');
+}
+
static char *process_trait(char *id, struct tokens *tokens, char *grp, FILE *f)
{
printf("/* found trait %s*/", id);
@@ -287,7 +348,41 @@ static char *process_trait(char *id, struct tokens *tokens, char *grp, FILE *f)
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);
+
+ /* discard equals sign */
+ int c = fgetc(f);
+ assert(c == '=');
+
+ grp = collect_until(grp, f, '{');
+ assert(grp);
+ char *inst = strdup(grp);
+ tokens_append(tokens, inst);
+
+ grp = collect_curly_block(grp, f);
+ char *body = strdup(grp);
+ tokens_append(tokens, body);
+
+ char *space = NULL;
+ if ((space = optional_space(f)))
+ tokens_append(tokens, space);
+
+ struct def *exists = defs_find(&templates, id);
+ if (exists) {
+ fprintf(stderr, "template %s redefined\n", id);
+ drop_tokens(tokens);
+ return grp;
+ }
+
+ struct def def = {
+ .id = strdup(id),
+ .types = strdup(types),
+ .args = strdup(args),
+ .body = strdup(body),
+ .inst = strdup(inst)
+ };
+
+ defs_insert(&templates, def.id, def);
+ drop_tokens(tokens);
return grp;
}
@@ -347,7 +442,8 @@ static char *maybe_process_template(char *id, struct tokens *tokens, char *grp,
.id = strdup(id),
.types = strdup(types),
.args = strdup(args),
- .body = strdup(body)
+ .body = strdup(body),
+ .inst = NULL
};
defs_insert(&templates, def.id, def);
@@ -355,94 +451,111 @@ static char *maybe_process_template(char *id, struct tokens *tokens, char *grp,
return grp;
}
-static bool word(char *hay, char *needle, size_t len)
+static char *replace_id(char *body, char *replace, char *id)
{
+ 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) {
- if (hay[i] != needle[i])
- return false;
+ assert(isalnum(replace[i]) || replace[i] == '_');
+ }
+
+ char *new = malloc(1 * sizeof(char));
+ new[0] = '\0';
+
+ size_t idx = 0;
+ while (body[idx]) {
+ if (!isalnum(body[idx]) && body[idx] != '_') {
+ new = append(new, body[idx++]);
+ continue;
+ }
+
+ size_t start = idx;
+ while (body[idx] && (isalnum(body[idx]) || body[idx] == '_'))
+ idx++;
+
+ size_t diff = idx - start;
+ if (strncmp(&body[start], replace, diff) == 0) {
+ for (size_t i = 0; i < idlen; ++i)
+ new = append(new, id[i]);
+
+ continue;
+ }
+
+ for (size_t i = 0; i < diff; ++i)
+ new = append(new, body[start + i]);
}
- /* word ends in something other than alphanumeric */
- return !isalnum(hay[len]);
+ free(body);
+ return new;
}
-static char *replace(char *body, char *replace, char *id, bool wordboundary)
+static char *replace(char *body, char *replace, char *id)
{
assert(body);
assert(id);
- char *orig = body;
char *new = malloc(1 * sizeof(char));
new[0] = '\0';
- size_t len = strlen(replace);
+ size_t len = strlen(replace);
+ size_t idlen = strlen(id);
- while (body[0]) {
- /* lol terrible */
- bool found = wordboundary ? word(body, replace, len)
- : strncmp(body, replace, len) == 0;
+ size_t idx = 0;
+ while (body[idx]) {
+ if (body[idx] != replace[0]) {
+ new = append(new, body[idx++]);
+ continue;
+ }
- if (found) {
- char *copy = id;
- while (*copy) {
- new = append(new, *copy);
- copy++;
- }
+ if (strncmp(&body[idx], replace, len) == 0) {
+ for (size_t i = 0; i < idlen; ++i)
+ new = append(new, id[i]);
- body += len;
+ idx += len;
continue;
}
-
- new = append(new, body[0]);
- body += 1;
}
- free(orig);
+ free(body);
return new;
}
-static char *process_instance(char *id, struct tokens *tokens, char *grp, FILE *f)
+static void expand_instance(char *id, char *instance)
{
- 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);
+ char *grp = NULL;
- /* types */
- space = optional_space(f);
- if (space)
- tokens_append(tokens, space);
+ size_t instance_idx = 0;
+ optional_str_space(instance, &instance_idx);
+ grp = next_str_grp(grp, instance, &instance_idx);
+ assert(is_identifier(grp));
+ char *name = strdup(grp);
- grp = collect_square_block(grp, f);
+ optional_str_space(instance, &instance_idx);
+ grp = collect_str_square_block(grp, instance, &instance_idx);
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);
+ optional_str_space(instance, &instance_idx);
+ grp = collect_str_paren_block(grp, instance, &instance_idx);
char *args = strdup(grp);
- tokens_append(tokens, args);
- struct def *def = defs_find(&templates, template);
+ optional_str_space(instance, &instance_idx);
+ struct def *def = defs_find(&templates, name);
if (!def) {
/** @todo location info and so on */
- fprintf(stderr, "no such template: %s\n", template);
- return grp;
+ fprintf(stderr, "no such template: %s\n", name);
+ free(name);
+ free(args);
+ free(types);
+ return;
}
+ free(name);
+
+
/** @todo check traits */
/* +1 to skip leading '{' */
@@ -450,8 +563,6 @@ static char *process_instance(char *id, struct tokens *tokens, char *grp, FILE *
/* overwrite trailing '}' */
body[strlen(body) - 1] = '\0';
- body = replace(body, "<>", id, false);
-
/* handle type args */
char *params = def->types;
assert(params[0] == '[');
@@ -462,16 +573,21 @@ static char *process_instance(char *id, struct tokens *tokens, char *grp, FILE *
/* skip '(' in args */
size_t types_idx = 1;
+ struct tokens type_params = tokens_create(0);
+ struct tokens type_args = tokens_create(0);
+
while (1) {
/* get type param */
optional_str_space(params, &params_idx);
- char *trait = next_str_grp(params, &params_idx);
- assert(is_identifier(trait));
+ grp = next_str_grp(grp, params, &params_idx);
+ assert(is_identifier(grp));
+ char *trait = strdup(grp);
optional_str_space(params, &params_idx);
- char *name = next_str_grp(params, &params_idx);
- assert(is_identifier(name));
+ grp = next_str_grp(grp, params, &params_idx);
+ assert(is_identifier(grp));
+ char *name = strdup(grp);
optional_str_space(params, &params_idx);
char delim = params[params_idx++];
@@ -479,16 +595,16 @@ static char *process_instance(char *id, struct tokens *tokens, char *grp, FILE *
/* get arg type */
optional_str_space(types, &types_idx);
- char *type = next_str_until(types, &types_idx, ",]");
+ grp = next_str_until(grp, types, &types_idx, ",]");
+ char *type = strdup(grp);
+
char last = types[types_idx++];
assert(last == ',' || last == ']');
- /** @todo assert that we're dealing with a type? */
- body = replace(body, name, type, true);
+ tokens_append(&type_params, name);
+ tokens_append(&type_args, type);
free(trait);
- free(name);
- free(type);
if (delim == ']' && last == ']')
break;
@@ -496,23 +612,72 @@ static char *process_instance(char *id, struct tokens *tokens, char *grp, FILE *
if (delim == ']') {
fprintf(stderr, "more type args than params\n");
free(body);
- drop_tokens(tokens);
- return grp;
+ drop_tokens(&type_params);
+ drop_tokens(&type_args);
+ free(args);
+ free(types);
+ free(grp);
+ return;
}
if (last == ']') {
fprintf(stderr, "more type params than args\n");
free(body);
- drop_tokens(tokens);
- return grp;
+ drop_tokens(&type_params);
+ drop_tokens(&type_args);
+ free(args);
+ free(types);
+ free(grp);
+ return;
}
}
+ char *super = NULL;
+ body = replace(body, "<>", id);
+
+ if (def->inst)
+ super = replace(strdup(def->inst), "<>", id);
+
+ 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);
+
+ body = replace_id(body, name, type);
+
+ if (super)
+ super = replace_id(super, name, type);
+ }
+
+ if (super)
+ expand_instance(id, super);
+
/** @todo regular params, probably much like above but parsing the type
* might be a bit annoying... */
+ drop_tokens(&type_params);
+ drop_tokens(&type_args);
+
printf(body);
free(body);
+ free(super);
+
+ free(types);
+ free(args);
+ free(grp);
+}
+
+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 == '=');
+
+ grp = collect_until(grp, f, ';');
+ char *instance = strdup(grp);
+ tokens_append(tokens, instance);
+
+ expand_instance(id, instance);
/* ; can probably be ignored...?*/
drop_tokens(tokens);
@@ -581,6 +746,7 @@ static void free_defs(struct defs *defs)
free(d.types);
free(d.args);
free(d.body);
+ free(d.inst);
}
defs_destroy(defs);
diff --git a/test/compile_main.sh b/test/compile_main.sh
deleted file mode 100755
index 5467005..0000000
--- a/test/compile_main.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/bin/sh
-
-set -eu
-
-mkdir -p build/test
-gcc -E test/main.c -o build/test/main.c
-./ngc build/test/main.c > build/test/main_ngc.c
-gcc build/test/main_ngc.c -o build/test/main
diff --git a/test/main.c b/test/main.c
deleted file mode 100644
index 489618b..0000000
--- a/test/main.c
+++ /dev/null
@@ -1,19 +0,0 @@
-#include <stdio.h>
-#include "vec.h"
-
-typedef ints = vec[int]();
-
-int main()
-{
- struct ints a = intscreate();
- for (size_t i = 0; i < 10; ++i)
- intsappend(&a, i);
-
- int sum = 0;
- for (size_t i = 0; i < 10; ++i) {
- sum += *intsat(&a, i);
- }
-
- printf("%i\n", sum);
- intsdestroy(&a);
-}
diff --git a/test/vec.h b/test/vec.h
deleted file mode 100644
index 810e663..0000000
--- a/test/vec.h
+++ /dev/null
@@ -1,49 +0,0 @@
-#ifndef VEC_H
-#define VEC_H
-
-#include <stddef.h>
-#include <assert.h>
-#include <stdlib.h>
-
-typedef any {};
-
-typedef vec[any type]() {
- struct <> {
- size_t n;
- size_t s;
- type *buf;
- };
-
- static inline struct <> <>create()
- {
- return (struct <>){
- .n = 0,
- .s = 0,
- .buf = NULL
- };
- }
-
- static inline void <>append(struct <> *v, type n)
- {
- v->n++;
- if (v->n > v->s) {
- v->s = v->s == 0 ? 1 : 2 * v->s;
- v->buf = realloc(v->buf, v->s * sizeof(type));
- }
-
- v->buf[v->n - 1] = n;
- }
-
- static inline type *<>at(struct <> *v, size_t i)
- {
- assert(i < v->n);
- return &v->buf[i];
- }
-
- static inline void <>destroy(struct <> *v)
- {
- free(v->buf);
- }
-}
-
-#endif /* VEC_H */
diff --git a/tests/Makefile b/tests/Makefile
new file mode 100644
index 0000000..bcc91d3
--- /dev/null
+++ b/tests/Makefile
@@ -0,0 +1,5 @@
+all:
+ $(MAKE) -C xok
+
+clean:
+ $(MAKE) -C xok clean
diff --git a/tests/xok/Makefile b/tests/xok/Makefile
new file mode 100644
index 0000000..210982c
--- /dev/null
+++ b/tests/xok/Makefile
@@ -0,0 +1,15 @@
+FILES != echo *.c
+TARGETS := $(FILES:%.c=build/%)
+
+all: $(TARGETS)
+
+build/%: %.c ../../ngc1
+ @mkdir -p build
+ @gcc -E $< -o $@.i
+ @../../ngc1 $@.i > $@.c
+ @gcc $@.c -o $@
+ @./$@ | (grep -c 'OK' >/dev/null && echo "OK $@" || echo "FAIL $@")
+
+.PHONY: clean
+clean:
+ $(RM) -rf build
diff --git a/tests/xok/supertmpl.c b/tests/xok/supertmpl.c
new file mode 100644
index 0000000..bc946fb
--- /dev/null
+++ b/tests/xok/supertmpl.c
@@ -0,0 +1,24 @@
+#include <stdio.h>
+
+typedef tmpl[](){
+ int <>_ok()
+ {
+ return 20;
+ }
+};
+
+typedef supertmpl[]() = tmpl[]() {
+ int <>_superok()
+ {
+ return <>_ok();
+ }
+};
+
+typedef inst = supertmpl[]();
+
+int main()
+{
+ int ok = inst_superok();
+ if (ok == 20)
+ puts("OK");
+}
diff --git a/tests/xok/tmpl.c b/tests/xok/tmpl.c
new file mode 100644
index 0000000..7b514dd
--- /dev/null
+++ b/tests/xok/tmpl.c
@@ -0,0 +1,17 @@
+#include <stdio.h>
+
+typedef tmpl[](){
+ int <>_ok()
+ {
+ return 20;
+ }
+};
+
+typedef inst = tmpl[]();
+
+int main()
+{
+ int ok = inst_ok();
+ if (ok == 20)
+ puts("OK");
+}