aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--.gitmodules3
-rw-r--r--Makefile8
m---------deps/conts0
-rw-r--r--example_vec/new/main.c33
-rw-r--r--example_vec/new/vec.h116
-rw-r--r--example_vec/old/conts.h22
-rw-r--r--example_vec/old/main.c32
-rw-r--r--example_vec/old/vec.h139
-rw-r--r--src/main.c615
-rwxr-xr-xtest/compile_main.sh8
-rw-r--r--test/main.c19
-rw-r--r--test/vec.h49
13 files changed, 1047 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..eb92346
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+ngc
+ngc1
+build
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..70e8d9f
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "deps/conts"]
+ path = deps/conts
+ url = https://metanimi.dy.fi/cgit/conts
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..7a49e49
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,8 @@
+all: ngc1
+
+ngc1: src/main.c
+ $(CC) -Ideps/conts/include -Wall -Wextra -g src/main.c -o ngc1
+
+.PHONY: clean
+clean:
+ $(RM) ngc1
diff --git a/deps/conts b/deps/conts
new file mode 160000
+Subproject 7774ae2f8c2dca9ab2d93082856f031e78a1b5f
diff --git a/example_vec/new/main.c b/example_vec/new/main.c
new file mode 100644
index 0000000..a00c35c
--- /dev/null
+++ b/example_vec/new/main.c
@@ -0,0 +1,33 @@
+#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))
+
+int main()
+{
+ struct ints ints = ints_create(0);
+ for (int i = 0; i < 1000000; ++i) {
+ ints_append(&ints, i);
+ }
+ assert(ints_len(&ints) == 1000000);
+
+ for (int i = 0; i < 1000000; ++i) {
+ int *v = ints_at(&ints, i);
+ assert(v && *v == i);
+ }
+
+ int i = 0;
+ foreach(ints, iter, &ints) {
+ assert(iter && *iter == i);
+ i++;
+ }
+
+ for (int i = 1000000 - 1; i >= 0; --i) {
+ ints_remove(&ints, i);
+ }
+ assert(ints_len(&ints) == 0);
+
+ ints_destroy(&ints);
+}
diff --git a/example_vec/new/vec.h b/example_vec/new/vec.h
new file mode 100644
index 0000000..613af51
--- /dev/null
+++ b/example_vec/new/vec.h
@@ -0,0 +1,116 @@
+#ifndef NGC_VEC_H
+#define NGC_VEC_H
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <stdbool.h>
+
+typedef vec[any type]() {
+ struct <> {
+ size_t n;
+ size_t s;
+ type *buf;
+ };
+
+ typedef type *<>_iter;
+
+ struct <> <>_create(size_t reserve)
+ {
+ if (reserve == 0)
+ return (struct <>){.n = 0, .s = 0, .buf = NULL};
+
+ return (struct <>){
+ .n = 0,
+ .s = reserve,
+ .buf = malloc(reserve * sizeof(type))
+ };
+ }
+
+ bool <>_uninit(struct <> *v)
+ {
+ return v->buf == NULL;
+ }
+
+ size_t <>_len(struct <> *v)
+ {
+ return v->n;
+ }
+
+
+ type *<>_at(struct <> *v, size_t i)
+ {
+ assert(i < v->n && "out of vector bounds");
+ return &v->buf[i];
+ }
+
+ type *<>_pop(struct <> *v)
+ {
+ assert(v->n && "attempting to pop empty vector");
+ v->n--;
+ return &v->buf[v->n];
+ }
+
+ type* <>_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;
+ }
+
+ void <>_reset(struct <> *v)
+ {
+ v->n = 0;
+ }
+
+ void <>_destroy(struct <> *v)
+ {
+ free(v->buf);
+ }
+
+ void <>_reserve(struct <> *v, size_t n)
+ {
+ if (v->n >= n)
+ return;
+
+ while (v->s < v->n)
+ v->s = v->s == 0 ? 1 : 2 * v->s;
+
+ v->buf = realloc(v->buf, v->s * sizeof(type));
+ }
+
+ void <>_shrink(struct <> *v, size_t n)
+ {
+ assert(v->n >= n);
+ v->n = n;
+ }
+
+ void <>_remove(struct <> *v, size_t i)
+ {
+ assert(v->n > i);
+ size_t c = sizeof(type) * (v->n - i);
+ memcpy(&v->buf[i], &v->buf[i + 1], c);
+ v->n--;
+ }
+
+ <>_iter <>_begin(struct <> *v)
+ {
+ return &v->buf[0];
+ }
+
+ bool <>_end(struct <> *v, <>_iter i)
+ {
+ return &v->buf[v->n] == i;
+ }
+
+ <>_iter <>_next(<>_iter i)
+ {
+ return i + 1;
+ }
+}
+
+#endif /* NGC_VEC_H */
diff --git a/example_vec/old/conts.h b/example_vec/old/conts.h
new file mode 100644
index 0000000..d175d30
--- /dev/null
+++ b/example_vec/old/conts.h
@@ -0,0 +1,22 @@
+#ifndef CONTS_H
+#define CONTS_H
+
+#define CONTS_JOIN2(a, b) a##_##b
+#define CONTS_JOIN(a, b) CONTS_JOIN2(a, b)
+
+#define CONTAINER_OF(ptr, type, member) \
+ (type *)((char *)(ptr) - offsetof(type, member))
+
+#if __STDC_VERSION__ >= 202311UL
+# define CONTS_AUTO auto
+#elif defined(__GNUC__)
+# define CONTS_AUTO __auto_type
+#else
+# warning "iteration won't work with this compiler"
+#endif
+
+#define foreach(name, i, s)\
+ for (CONTS_AUTO i = CONTS_JOIN(name, begin)(s);\
+ !CONTS_JOIN(name, end)(s, i);\
+ i = CONTS_JOIN(name, next)(i))
+#endif /* CONTS_H */
diff --git a/example_vec/old/main.c b/example_vec/old/main.c
new file mode 100644
index 0000000..a84096c
--- /dev/null
+++ b/example_vec/old/main.c
@@ -0,0 +1,32 @@
+#include <assert.h>
+
+#define VEC_TYPE int
+#define VEC_NAME ints
+#include <conts/vec.h>
+
+int main()
+{
+ struct ints ints = ints_create(0);
+ for (int i = 0; i < 1000000; ++i) {
+ ints_append(&ints, i);
+ }
+ assert(ints_len(&ints) == 1000000);
+
+ for (int i = 0; i < 1000000; ++i) {
+ int *v = ints_at(&ints, i);
+ assert(v && *v == i);
+ }
+
+ int i = 0;
+ foreach(ints, iter, &ints) {
+ assert(iter && *iter == i);
+ i++;
+ }
+
+ for (int i = 1000000 - 1; i >= 0; --i) {
+ ints_remove(&ints, i);
+ }
+ assert(ints_len(&ints) == 0);
+
+ ints_destroy(&ints);
+}
diff --git a/example_vec/old/vec.h b/example_vec/old/vec.h
new file mode 100644
index 0000000..19fd18f
--- /dev/null
+++ b/example_vec/old/vec.h
@@ -0,0 +1,139 @@
+#ifndef VEC_TYPE
+#error "Need vector type"
+#endif
+
+#ifndef VEC_NAME
+#error "Need vector name"
+#endif
+
+#include <stdbool.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "conts.h"
+
+#define VEC(a) CONTS_JOIN(VEC_NAME, a)
+
+#define VEC_STRUCT VEC_NAME
+struct VEC_STRUCT {
+ size_t n;
+ size_t s;
+ VEC_TYPE *buf;
+};
+
+static inline struct VEC_STRUCT VEC(create)(size_t reserve)
+{
+ if (reserve == 0)
+ return (struct VEC_STRUCT) {.n = 0, .s = 0, .buf = NULL};
+
+ return (struct VEC_STRUCT) {
+ .n = 0,
+ .s = reserve,
+ .buf = malloc(reserve * sizeof(VEC_TYPE)),
+ };
+}
+
+static inline bool VEC(uninit)(struct VEC_STRUCT *v)
+{
+ return v->buf == NULL;
+}
+
+static inline size_t VEC(len)(struct VEC_STRUCT *v)
+{
+ return v->n;
+}
+
+static inline VEC_TYPE *VEC(at)(struct VEC_STRUCT *v, size_t i)
+{
+ assert(i < v->n && "out of vector bounds");
+ return &v->buf[i];
+}
+
+static inline VEC_TYPE *VEC(back)(struct VEC_STRUCT *v)
+{
+ assert(v->n);
+ return &v->buf[v->n - 1];
+}
+
+static inline VEC_TYPE *VEC(pop)(struct VEC_STRUCT *v)
+{
+ assert(v->n && "attempting to pop empty vector");
+ v->n--;
+ return &v->buf[v->n];
+}
+
+static inline void VEC(append)(struct VEC_STRUCT *v, VEC_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(VEC_TYPE));
+ }
+
+ v->buf[v->n - 1] = n;
+}
+
+static inline void VEC(reset)(struct VEC_STRUCT *v)
+{
+ v->n = 0;
+}
+
+static inline void VEC(destroy)(struct VEC_STRUCT *v) {
+ free(v->buf);
+}
+
+typedef int (*VEC(comp_t))(VEC_TYPE *a, VEC_TYPE *b);
+static inline void VEC(sort)(struct VEC_STRUCT *v, VEC(comp_t) comp)
+{
+ qsort(v->buf, v->n, sizeof(VEC_TYPE), (__compar_fn_t)comp);
+}
+
+static inline void VEC(reserve)(struct VEC_STRUCT *v, size_t n)
+{
+ if (v->n >= n)
+ return;
+
+ v->n = n;
+ if (v->s >= v->n)
+ return;
+
+ while (v->s < v->n)
+ v->s = v->s == 0 ? 1 : 2 * v->s;
+
+ v->buf = realloc(v->buf, v->s * sizeof(VEC_TYPE));
+}
+
+static inline void VEC(shrink)(struct VEC_STRUCT *v, size_t n)
+{
+ assert(v->n >= n);
+ v->n = n;
+}
+
+static inline void VEC(remove)(struct VEC_STRUCT *v, size_t i)
+{
+ assert(v->n > i);
+ size_t c = sizeof(VEC_TYPE) * (v->n - i);
+ memcpy(&v->buf[i], &v->buf[i + 1], c);
+ v->n--;
+}
+
+static inline VEC_TYPE *VEC(begin)(struct VEC_STRUCT *v)
+{
+ return &v->buf[0];
+}
+
+static inline bool VEC(end)(struct VEC_STRUCT *v, VEC_TYPE *i)
+{
+ return &v->buf[v->n] == i;
+}
+
+static inline VEC_TYPE *VEC(next)(VEC_TYPE *i)
+{
+ return i + 1;
+}
+
+#undef VEC
+#undef VEC_TYPE
+#undef VEC_NAME
+#undef VEC_STRUCT
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;
+}
diff --git a/test/compile_main.sh b/test/compile_main.sh
new file mode 100755
index 0000000..5467005
--- /dev/null
+++ b/test/compile_main.sh
@@ -0,0 +1,8 @@
+#!/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
new file mode 100644
index 0000000..489618b
--- /dev/null
+++ b/test/main.c
@@ -0,0 +1,19 @@
+#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
new file mode 100644
index 0000000..810e663
--- /dev/null
+++ b/test/vec.h
@@ -0,0 +1,49 @@
+#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 */