/* * Copyright © 2022 Michael Smith * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "../os.h" #include "cmeta.h" #include "skiplist.h" #include "vec.h" #define MAXENT 65536 // arbitrary limit! static struct ent { const char *name; bool unreg; bool isvar; // false for cmd } ents[MAXENT]; static int nents; static void die(const char *s) { fprintf(stderr, "codegen: %s\n", s); exit(100); } #define PUT(name_, isvar_, unreg_) do { \ if (nents == sizeof(ents) / sizeof(*ents)) { \ fprintf(stderr, "codegen: out of space; make ents bigger!\n"); \ exit(1); \ } \ ents[nents].name = name_; \ ents[nents].isvar = isvar_; ents[nents++].unreg = unreg_; \ } while (0) 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, ...) \ if (fprintf(out, f "\n", __VA_ARGS__) < 0) die("couldn't write to file"); #define H() \ _( "/* 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"); if (!out) die("couldn't open cmdinit.gen.h"); H(); for (const struct ent *p = ents; p - ents < nents; ++p) { F( "extern struct con_%s *%s;", p->isvar ? "var" : "cmd", p->name) } _( "") _( "static void regcmds(void) {") for (const struct ent *p = ents; p - ents < nents; ++p) { if (p->isvar) { F( " initval(%s);", p->name) } if (!p->unreg) { F( " con_reg(%s);", p->name) } } _( "}") _( "") _( "static void freevars(void) {") for (const struct ent *p = ents; p - ents < nents; ++p) { if (p->isvar) { F( " extfree(%s->strval);", p->name); } } _( "}") 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; } // vi: sw=4 ts=4 noet tw=80 cc=80