From c8d7588251fd4fe63ac6afe2a90ca7066c786609 Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Sat, 23 Jul 2022 23:47:26 +0100 Subject: Add event system --- src/build/cmeta.c | 32 ++++++++++++++ src/build/cmeta.h | 12 ++++++ src/build/codegen.c | 120 +++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 163 insertions(+), 1 deletion(-) (limited to 'src/build') diff --git a/src/build/cmeta.c b/src/build/cmeta.c index bf18903..3d49927 100644 --- a/src/build/cmeta.c +++ b/src/build/cmeta.c @@ -266,4 +266,36 @@ void cmeta_conmacros(const struct cmeta *cm, } } +void cmeta_evdefmacros(const struct cmeta *cm, void (*cb_def)(const char *name)) { + Token *tp = (Token *)cm; + if (!tp || !tp->next || !tp->next->next) return; // DEF_EVENT, (, name + while (tp) { + 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); + } + tp = tp->next; + } +} + +void cmeta_evhandlermacros(const struct cmeta *cm, const char *modname, + void (*cb_handler)(const char *evname, const char *modname)) { + Token *tp = (Token *)cm; + while (tp) { + if (equal(tp, "HANDLE_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_handler(name, modname); + } + tp = tp->next; + } +} + // vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/build/cmeta.h b/src/build/cmeta.h index 4757654..125ce2c 100644 --- a/src/build/cmeta.h +++ b/src/build/cmeta.h @@ -39,6 +39,18 @@ void cmeta_includes(const struct cmeta *cm, void cmeta_conmacros(const struct cmeta *cm, void (*cb)(const char *name, bool isvar, bool unreg)); +/* + * 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)); + +/* + * Iterates through all event-related macros and gives a callback for each event + * that is handled by the given module. + */ +void cmeta_evhandlermacros(const struct cmeta *cm, const char *modname, + void (*cb)(const char *evname, const char *modname)); #endif // vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/build/codegen.c b/src/build/codegen.c index d96059d..1dee6a1 100644 --- a/src/build/codegen.c +++ b/src/build/codegen.c @@ -20,6 +20,8 @@ #include "../os.h" #include "cmeta.h" +#include "skiplist.h" +#include "vec.h" #define MAXENT 65536 // arbitrary limit! static struct ent { @@ -47,6 +49,51 @@ static void oncondef(const char *name, bool isvar, bool unreg) { PUT(name, isvar, unreg); } +struct vec_str VEC(const char *); +DECL_SKIPLIST(static, event, struct event, const char *, 4) +struct event { + const char *name; + struct vec_str handlers; + struct skiplist_hdr_event hdr; +}; +static inline int cmp_event(struct event *e, const char *s) { + return strcmp(e->name, s); +} +static inline struct skiplist_hdr_event *hdr_event(struct event *e) { + return &e->hdr; +} +DEF_SKIPLIST(static, event, cmp_event, hdr_event) +struct skiplist_hdr_event events = {0}; + +static void onevdef(const char *name) { + 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; + e->handlers = (struct vec_str){0}; + e->hdr = (struct skiplist_hdr_event){0}; + skiplist_insert_event(&events, name, e); + } + else { + fprintf(stderr, "codegen: error: duplicate event definition `%s`\n", + name); + exit(2); + } +} + +static void onevhandler(const char *evname, const char *modname) { + struct event *e = skiplist_get_event(&events, evname); + if (!e) { + fprintf(stderr, "codegen: error: module `%s` trying to handle " + "non-existent event `%s`\n", modname, evname); + exit(2); + } + // NOTE: not bothering to check for more than one handler in a file. + // compiler will get that anyway. + if (!vec_push(&e->handlers, modname)) die("couldn't allocate memory"); +} + #define _(x) \ if (fprintf(out, "%s\n", x) < 0) die("couldn't write to file"); #define F(f, ...) \ @@ -55,10 +102,67 @@ static void oncondef(const char *name, bool isvar, bool unreg) { _( "/* This file is autogenerated by src/build/codegen.c. DO NOT EDIT! */") \ _( "") +struct passinfo { + const struct cmeta *cm; + const os_char *path; +}; +struct vec_passinfo VEC(struct passinfo) pass2 = {0}; + int OS_MAIN(int argc, os_char *argv[]) { for (++argv; *argv; ++argv) { const struct cmeta *cm = cmeta_loadfile(*argv); cmeta_conmacros(cm, &oncondef); + cmeta_evdefmacros(cm, &onevdef); + if (!vec_push(&pass2, ((struct passinfo){cm, *argv}))) { + die("couldn't allocate memory"); + } + } + + // we have to a second pass for event handlers. also, there's a bunch of + // terrible garbage here. don't stare for too long... + for (struct passinfo *pi = pass2.data; pi - pass2.data < pass2.sz; ++pi) { + // XXX: I guess we should cache these by name or something! + const struct cmeta *cm = pi->cm; +#ifdef _WIN32 + int arglen = wcslen(pi->path); + char *p = malloc(arglen + 1); + if (!p) die("couldn't allocate string"); + // XXX: Unicode isn't real, it can't hurt you. + for (const ushort *q = pi->path; q - pi->path < arglen; ++q) { + p[q - pi->path] = *q; // ugh this is stupid + } + p[arglen] = '\0'; +#else + const char *p = p->path; +#endif + const char *lastslash = p - 1; + for (; *p; ++p) { +#ifdef _WIN32 + if (*p == '/' || *p == '\\') { +#else + if (*p == '/') { +#endif + lastslash = p; + } + } + int len = strlen(lastslash + 1); + if (len <= 3 || lastslash[len - 1] != '.' || lastslash[len] != 'c') { + fprintf(stderr, "filenames should end in .c probably\n"); + exit(2); + } + char *modname = malloc(len - 1); + if (!modname) die("couldn't allocate string"); + memcpy(modname, lastslash + 1, len - 2); + modname[len - 2] = '\0'; + // ugh. same dumb hacks from compile scripts + if (!strcmp(modname, "con_")) { + free(modname); // might as well + modname = "con"; + } + else if (!strcmp(modname, "sst")) { + continue; // I guess??? + } + cmeta_evhandlermacros(cm, modname, &onevhandler); } FILE *out = fopen(".build/include/cmdinit.gen.h", "wb"); @@ -86,7 +190,21 @@ F( " extfree(%s->strval);", p->name); } } _( "}") - if (fflush(out) == EOF) die("couldn't fully write cmdinit.gen.h"); + if (fclose(out) == EOF) die("couldn't fully write cmdinit.gen.h"); + + out = fopen(".build/include/evglue.gen.h", "wb"); + if (!out) die("couldn't open evglue.gen.h"); + H() + for (const struct event *e = events.x[0]; e; e = e->hdr.x[0]) { +F( "void _evemit_%s(void) {", e->name) + for (const char **pp = e->handlers.data; + pp - e->handlers.data < e->handlers.sz; ++pp) { +F( " void _evhandler_%s_%s(void);", *pp, e->name); // blegh. +F( " _evhandler_%s_%s();", *pp, e->name); + } +_( "}") + } + if (fclose(out) == EOF) die("couldn't fully write evglue.gen.h"); return 0; } -- cgit v1.2.3