From 792463cb133f65645feb48743bbc03ef8eb96bdd Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Tue, 13 Sep 2022 21:46:21 +0100 Subject: Move towards C23, improve events and vcall macros Another big one. Here's a list of things: - Since the upcoming C23 standardises typeof(), use it as an extension for the time being in order to allow passing arbitrary types as macro/codegen parameters. It wouldn't have been a big leap to do this even without standardisation since it's apparently an easy extension to implement - and also, to be honest, this project is essentially glued to Clang anyway so who cares. - Likewise, bool, true and false are becoming pre-defined, so pre-pre-define them now in order to get the benefit of not having to remember one header everywhere. - Really ungodly/amazing vcall macro stuff now allows us to call C++ virtual functions like regular C functions. It's pretty cool! - Events can now take arbitrary parameters and come in two types: regular events and predicates. All this makes the base code even uglier but makes the feature implementation nicer. In other words, it places more of the cognitive burden on myself and less on other people who might want to contribute. This is a good tradeoff, because I'm a genius. --- src/build/cmeta.c | 87 ++++++++++++++++++++++++++++++++++++++------------ src/build/cmeta.h | 6 ++-- src/build/codegen.c | 84 ++++++++++++++++++++++++++++++++++++++++-------- src/build/mkentprops.c | 1 - src/build/mkgamedata.c | 1 - src/build/vec.h | 1 - 6 files changed, 139 insertions(+), 41 deletions(-) (limited to 'src/build') diff --git a/src/build/cmeta.c b/src/build/cmeta.c index 260d33f..7f314c7 100644 --- a/src/build/cmeta.c +++ b/src/build/cmeta.c @@ -14,13 +14,13 @@ * PERFORMANCE OF THIS SOFTWARE. */ -#include #include #include #include "../intdefs.h" #include "../os.h" #include "cmeta.h" +#include "vec.h" /* * This file does C metadata parsing/scraping for the build system. This @@ -73,16 +73,16 @@ Type *array_of(Type *base, int len) { #include "../3p/chibicc/tokenize.c" // one more copypaste from preprocess.c for #include and then I'm // done I promise -static char *join_tokens(Token *tok, Token *end) { +static char *join_tokens(const Token *tok, const Token *end) { int len = 1; - for (Token *t = tok; t != end && t->kind != TK_EOF; t = t->next) { + for (const Token *t = tok; t != end && t->kind != TK_EOF; t = t->next) { if (t != tok && t->has_space) len++; len += t->len; } char *buf = calloc(1, len); int pos = 0; - for (Token *t = tok; t != end && t->kind != TK_EOF; t = t->next) { + for (const Token *t = tok; t != end && t->kind != TK_EOF; t = t->next) { if (t != tok && t->has_space) buf[pos++] = ' '; strncpy(buf + pos, t->loc, t->len); @@ -166,7 +166,7 @@ const struct cmeta *cmeta_loadfile(const os_char *f) { // picks it anyway, and gives far better diagnostics. void cmeta_includes(const struct cmeta *cm, void (*cb)(const char *f, bool issys, void *ctxt), void *ctxt) { - Token *tp = (Token *)cm; + const Token *tp = (const Token *)cm; if (!tp || !tp->next || !tp->next->next) return; // #, include, "string" while (tp) { if (!tp->at_bol || !equal(tp, "#")) { tp = tp->next; continue; } @@ -187,7 +187,7 @@ void cmeta_includes(const struct cmeta *cm, else if (equal(tp, "<")) { tp = tp->next; if (!tp) break; - Token *end = tp; + const Token *end = tp; while (!equal(end, ">")) { end = end->next; if (!end) return; // shouldn't happen in valid source obviously @@ -210,7 +210,7 @@ void cmeta_includes(const struct cmeta *cm, // we're not writing something weird. Don't write something weird! void cmeta_conmacros(const struct cmeta *cm, void (*cb)(const char *, bool, bool)) { - Token *tp = (Token *)cm; + const Token *tp = (const Token *)cm; if (!tp || !tp->next || !tp->next->next) return; // DEF_xyz, (, name while (tp) { bool isplusminus = false, isvar = false; @@ -271,7 +271,7 @@ void cmeta_conmacros(const struct cmeta *cm, } const char *cmeta_findfeatmacro(const struct cmeta *cm) { - Token *tp = (Token *)cm; + const Token *tp = (const Token *)cm; if (!tp || !tp->next) return 0; // FEATURE, ( while (tp) { if (equal(tp, "FEATURE") && equal(tp->next, "(")) { @@ -288,7 +288,7 @@ const char *cmeta_findfeatmacro(const struct cmeta *cm) { void cmeta_featinfomacros(const struct cmeta *cm, void (*cb)( enum cmeta_featmacro type, const char *param, void *ctxt), void *ctxt) { - Token *tp = (Token *)cm; + const Token *tp = (const Token *)cm; if (!tp || !tp->next) return; while (tp) { int type = -1; @@ -336,26 +336,73 @@ void cmeta_featinfomacros(const struct cmeta *cm, void (*cb)( } } -void cmeta_evdefmacros(const struct cmeta *cm, - void (*cb_def)(const char *name)) { - Token *tp = (Token *)cm; +struct vec_str VEC(const char *); + +static void pushmacroarg(const Token *last, const char *start, + struct vec_str *list) { + int len = last->loc - start + last->len; + char *dup = malloc(len + 1); + if (!dup) die1("couldn't allocate memory"); + memcpy(dup, start, len); + dup[len] = '\0'; + if (!vec_push(list, dup)) die1("couldn't append to array"); +} + +// XXX: maybe this should be used for the other functions too. it'd be less ugly +// and handle closing parentheses better, but alloc for tokens we don't care +// about. probably a worthy tradeoff? +static const Token *macroargs(const Token *t, struct vec_str *list) { + int paren = 1; + const Token *last; // avoids copying extra ws/comments in + for (const char *start = t->loc; t; last = t, t = t->next) { + if (equal(t, "(")) { + ++paren; + } + else if (equal(t, ")")) { + if (!--paren) { + pushmacroarg(last, start, list); + return t->next; + } + } + else if (paren == 1 && equal(t, ",")) { + pushmacroarg(last, start, list); + t = t->next; + if (t) start = t->loc; // slightly annoying... + } + } + // I guess we handle this here. + fprintf(stderr, "cmeta: fatal: unexpected EOF in %s\n", t->filename); + exit(2); +} + +void cmeta_evdefmacros(const struct cmeta *cm, void (*cb)(const char *name, + const char *const *params, int nparams, bool predicate)) { + const Token *tp = (const Token *)cm; if (!tp || !tp->next || !tp->next->next) return; // DEF_EVENT, (, name while (tp) { + bool predicate = true; if (equal(tp, "DEF_EVENT") && equal(tp->next, "(")) { - tp = tp->next->next; - char *name = malloc(tp->len + 1); - if (!name) die1("couldn't allocate memory"); - memcpy(name, tp->loc, tp->len); - name[tp->len] = '\0'; - cb_def(name); + predicate = false; } - tp = tp->next; + else if (!equal(tp, "DEF_PREDICATE") || !equal(tp->next, "(")) { + tp = tp->next; + continue; + } + tp = tp->next->next; + struct vec_str args = {0}; + tp = macroargs(tp, &args); + if (args.sz == 0) { + fprintf(stderr, "cmeta: fatal: missing event parameters in %s\n", + tp->filename); + exit(2); + } + cb(args.data[0], args.data + 1, args.sz - 1, predicate); } } void cmeta_evhandlermacros(const struct cmeta *cm, const char *modname, void (*cb_handler)(const char *evname, const char *modname)) { - Token *tp = (Token *)cm; + const Token *tp = (const Token *)cm; while (tp) { if (equal(tp, "HANDLE_EVENT") && equal(tp->next, "(")) { tp = tp->next->next; diff --git a/src/build/cmeta.h b/src/build/cmeta.h index 40c4ac5..48a2d6d 100644 --- a/src/build/cmeta.h +++ b/src/build/cmeta.h @@ -17,8 +17,6 @@ #ifndef INC_CMETA_H #define INC_CMETA_H -#include - #include "../os.h" struct cmeta; @@ -76,8 +74,8 @@ void cmeta_featinfomacros(const struct cmeta *cm, void (*cb)( * Iterates through all event-related macros and takes note of which events are * defined, giving a callback for each. */ -void cmeta_evdefmacros(const struct cmeta *cm, void (*cb)(const char *name)); - +void cmeta_evdefmacros(const struct cmeta *cm, void (*cb)(const char *name, + const char *const *params, int nparams, bool predicate)); /* * Iterates through all event-related macros and gives a callback for each event * that is handled by the given module. diff --git a/src/build/codegen.c b/src/build/codegen.c index 04a9058..f5231c0 100644 --- a/src/build/codegen.c +++ b/src/build/codegen.c @@ -14,7 +14,6 @@ * PERFORMANCE OF THIS SOFTWARE. */ -#include #include #include #include @@ -139,12 +138,15 @@ push: if (!vec_push(vecp, param)) die("couldn't allocate memory"); DECL_SKIPLIST(static, event, struct event, const char *, 4) struct event { - const char *name; + usize name; // string, but tagged pointer - see below + const char *const *params; + int nparams; + //char pad[4]; struct vec_usize handlers; // strings, but with tagged pointers - see below struct skiplist_hdr_event hdr; }; static inline int cmp_event(struct event *e, const char *s) { - return strcmp(e->name, s); + return strcmp((const char *)(e->name & ~1ull), s); } static inline struct skiplist_hdr_event *hdr_event(struct event *e) { return &e->hdr; @@ -152,12 +154,15 @@ static inline struct skiplist_hdr_event *hdr_event(struct event *e) { DEF_SKIPLIST(static, event, cmp_event, hdr_event) static struct skiplist_hdr_event events = {0}; -static void onevdef(const char *name) { +static void onevdef(const char *name, const char *const *params, int nparams, + bool predicate) { struct event *e = skiplist_get_event(&events, name); if (!e) { struct event *e = malloc(sizeof(*e)); if (!e) die("couldn't allocate memory"); - e->name = name; + // hack: using unused pointer bit to distinguish the two types of event + e->name = (usize)name | predicate; + e->params = params; e->nparams = nparams; e->handlers = (struct vec_usize){0}; e->hdr = (struct skiplist_hdr_event){0}; skiplist_insert_event(&events, name, e); @@ -445,19 +450,70 @@ _( "") H_() for (const struct event *e = events.x[0]; e; e = e->hdr.x[0]) { _( "") -F( "void _evemit_%s(void) {", e->name) - for (usize *pp = e->handlers.data; - pp - e->handlers.data < e->handlers.sz; ++pp) { - const char *modname = (const char *)(*pp & ~1ull); -F( " void _evhandler_%s_%s(void);", modname, e->name) // blegh. - if (*pp & 1ull) { - // note: has_* variables are already included by this point (above) -F( " if (has_%s) _evhandler_%s_%s();", modname, modname, e->name) + // gotta break from the string emit macros for a sec in order to do the + // somewhat more complicated task sometimes referred to as a "for loop" + fprintf(out, "%s_%s(", e->name & 1 ? "bool CHECK" : "void EMIT", + (const char *)(e->name & ~1ull)); + for (int n = 0; n < (int)e->nparams - 1; ++n) { + fprintf(out, "typeof(%s) a%d, ", e->params[n], n + 1); + } + if (e->nparams && strcmp(e->params[0], "void")) { + fprintf(out, "typeof(%s) a%d", e->params[e->nparams - 1], + e->nparams); } else { -F( " _evhandler_%s_%s();", modname, e->name) + // just unilaterally doing void for now. when we're fully on C23 + // eventually we can unilaterally do nothing instead + fputs("void", out); } +_( ") {") + for (usize *pp = e->handlers.data; + pp - e->handlers.data < e->handlers.sz; ++pp) { + const char *modname = (const char *)(*pp & ~1ull); + fprintf(out, "\t%s _evhandler_%s_%s(", e->name & 1 ? "bool" : "void", + modname, (const char *)(e->name & ~1ull)); + for (int n = 0; n < (int)e->nparams - 1; ++n) { + fprintf(out, "typeof(%s) a%d, ", e->params[n], n + 1); + } + if (e->nparams && strcmp(e->params[0], "void")) { + fprintf(out, "typeof(%s) a%d", e->params[e->nparams - 1], + e->nparams); + } + else { + fputs("void", out); + } + fputs(");\n\t", out); + // conditional and non-conditional cases - in theory could be + // unified a bit but this is easier to make output relatively pretty + // note: has_* variables are already included by this point (above) + if (e->name & 1) { + if (*pp & 1) fprintf(out, "if (has_%s && !", modname); + else fprintf(out, "if (!"); + fprintf(out, "_evhandler_%s_%s(", modname, + (const char *)(e->name & ~1ull)); + // XXX: much repetitive drivel here + for (int n = 0; n < (int)e->nparams - 1; ++n) { + fprintf(out, "a%d,", n + 1); + } + if (e->nparams && strcmp(e->params[0], "void")) { + fprintf(out, "a%d", e->nparams); + } + fputs(")) return false;\n", out); + } + else { + if (*pp & 1) fprintf(out, "if (has_%s) ", modname); + fprintf(out, "_evhandler_%s_%s(", modname, + (const char *)(e->name & ~1ull)); + for (int n = 0; n < (int)e->nparams - 1; ++n) { + fprintf(out, "a%d,", n + 1); + } + if (e->nparams && strcmp(e->params[0], "void")) { + fprintf(out, "a%d", e->nparams); + } + fputs(");\n", out); + } } + if (e->name & 1) fputs("\treturn true;\n", out); _( "}") } if (fclose(out) == EOF) die("couldn't fully write evglue.gen.h"); diff --git a/src/build/mkentprops.c b/src/build/mkentprops.c index 300dafa..fdb6982 100644 --- a/src/build/mkentprops.c +++ b/src/build/mkentprops.c @@ -14,7 +14,6 @@ * PERFORMANCE OF THIS SOFTWARE. */ -#include #include #include #include diff --git a/src/build/mkgamedata.c b/src/build/mkgamedata.c index 7d7a425..fdb2aef 100644 --- a/src/build/mkgamedata.c +++ b/src/build/mkgamedata.c @@ -14,7 +14,6 @@ * PERFORMANCE OF THIS SOFTWARE. */ -#include #include #include #include diff --git a/src/build/vec.h b/src/build/vec.h index 50b0a3b..6b4dffb 100644 --- a/src/build/vec.h +++ b/src/build/vec.h @@ -4,7 +4,6 @@ #define INC_VEC_H #include -#include #include #include "../intdefs.h" -- cgit v1.2.3