#include #include #include #include #include #include #define TODO() errx(EXIT_FAILURE, "%s:%d: todo", __FILE__, __LINE__) struct fwdval; struct fwdvals; struct fwdcnt { size_t pc; struct fwdvalues *ctx; }; enum fwdtype { FWDSCRIPT_NUM, FWDSCRIPT_STR, FWDSCRIPT_CNT, FWDSCRIPT_TBL, FWDSCRIPT_OPQ }; struct fwdval { enum fwdtype kind; union { double num; char *str; struct fwdcnt cnt; struct fwdtbl *tbl; void *opq; }; }; #define FWDSTR(s) (struct fwdval){.kind = FWDSCRIPT_STR, .str = (s)} static int fwdcmp(const struct fwdval *a, const struct fwdval *b) { if (a->kind != b->kind) return a->kind - b->kind; switch (a->kind) { case FWDSCRIPT_NUM: return a->num - b->num; case FWDSCRIPT_STR: return strcmp(a->str, b->str); case FWDSCRIPT_CNT: return a->cnt.pc - b->cnt.pc; case FWDSCRIPT_TBL: return (intptr_t)a->tbl - (intptr_t)b->tbl; case FWDSCRIPT_OPQ: return (intptr_t)a->opq - (intptr_t)b->opq; default: abort(); } return 0; } static int fwdhash(const struct fwdval *a) { switch (a->kind) { /* trash hashes, lol */ case FWDSCRIPT_NUM: return a->num * 15647839; case FWDSCRIPT_STR: return a->str[0]; case FWDSCRIPT_CNT: return a->cnt.pc * 15647839; case FWDSCRIPT_TBL: return 10; case FWDSCRIPT_OPQ: return (intptr_t)a->opq; default: abort(); } return 0; } #define MAP_KEY struct fwdval #define MAP_TYPE struct fwdval #define MAP_CMP(A, B) fwdcmp(&(A), &(B)) #define MAP_HASH(A) fwdhash(&(A)) #define MAP_NAME fwdtbl #include #define MAP_KEY const char * #define MAP_TYPE size_t #define MAP_CMP(A, B) strcmp((A), (B)) #define MAP_HASH(A) (A)[0] #define MAP_NAME fwdfuncs #include #define VEC_TYPE struct fwdval #define VEC_NAME fwdvals #include #define VEC_TYPE enum fwdtype #define VEC_NAME fwdtypes #include struct fwdinsn { /* use args[0] as destination, if /cnt then call the * continuation, if /opq call builtin, otherwise abort */ struct fwdvals args; }; #define VEC_TYPE struct fwdinsn #define VEC_NAME fwdinsns #include struct fwdscript; typedef int (*fwd_typecheck_t)(struct fwdscript *s, struct fwdtypes *types); typedef int (*fwd_callback_t)(struct fwdscript *s, struct fwdvals *args); struct fwdbuiltin { fwd_callback_t cb; fwd_typecheck_t tp; }; #define MAP_KEY const char * #define MAP_TYPE struct fwdbuiltin #define MAP_CMP(A, B) strcmp((A), (B)) #define MAP_HASH(A) (A)[0] #define MAP_NAME fwdbuiltins #include struct fwdscript { struct fwdfuncs funcs; struct fwdinsns insns; struct fwdbuiltins builtins; }; static struct fwdscript fwdscript_create() { struct fwdscript inst = { .funcs = fwdfuncs_create(1), .insns = fwdinsns_create(1), .builtins = fwdbuiltins_create(1) }; return inst; } static int fwdscript_register(struct fwdscript *s, const char *name, fwd_callback_t cb, fwd_typecheck_t tp) { struct fwdbuiltin builtin = { .cb = cb, .tp = tp }; struct fwdbuiltin *b = fwdbuiltins_insert(&s->builtins, name, builtin); if (b == NULL) { fprintf(stderr, "failed registering %s\n", name); return -1; } return 0; } static int fwdscript_get(struct fwdscript *s, struct fwdvals *args) { TODO(); } static int fwdscript_get_typecheck(struct fwdscript *s, struct fwdtypes *types) { TODO(); } static int fwdscript_abort(struct fwdscript *s, struct fwdvals *args) { TODO(); } static int fwdscript_abort_typecheck(struct fwdscript *s, struct fwdtypes *types) { TODO(); } static int fwdscript_print(struct fwdscript *s, struct fwdvals *args) { TODO(); } static int fwdscript_print_typecheck(struct fwdscript *s, struct fwdtypes *args) { TODO(); } static int fwdscript_register_builtins(struct fwdscript *s) { if (fwdscript_register(s, "get", fwdscript_get, fwdscript_get_typecheck)) return -1; if (fwdscript_register(s, "abort", fwdscript_abort, fwdscript_abort_typecheck)) return -1; if (fwdscript_register(s, "print", fwdscript_print, fwdscript_print_typecheck)) return -1; return 0; } static const char *skip_ignored(const char *buf) { while (*buf && isspace(*buf)) buf++; if (*buf == '#') while (*buf && *buf != '\n') buf++; return buf; } static int separator(int c) { return c == ';' || c == '{' || c == '}' || c == ';' || c == '=' || c == '"' || c == '\n'; } static const char *next_separator(const char *buf) { while (*buf && !isspace(*buf) && !separator(*buf)) buf++; return buf; } struct fwdtoken { enum { FWDTOKEN_ID, FWDTOKEN_STR, FWDTOKEN_NUM, FWDTOKEN_OPEN, FWDTOKEN_CLOSE, FWDTOKEN_TO, FWDTOKEN_END } kind; size_t line; union { char *s; double d; }; }; #define VEC_TYPE struct fwdtoken #define VEC_NAME fwdtokens #include static int fwdscript_tokenize(struct fwdtokens *tokens, const char *buf) { size_t line = 0; const char *start = buf; while (*start) { const char *tok = skip_ignored(start); if (!*tok) break; if (*tok == '\n') { line += 1; start = tok + 1; continue; } if (*tok == ';') { struct fwdtoken end = { .kind = FWDTOKEN_END, .line = line, .s = NULL }; if (!fwdtokens_append(tokens, end)) { fprintf(stderr, "failed appending end token\n"); return -1; } start = tok + 1; continue; } if (*tok == '{') { struct fwdtoken open = { .kind = FWDTOKEN_OPEN, .line = line, .s = NULL }; if (!fwdtokens_append(tokens, open)) { fprintf(stderr, "failed appending open token\n"); return -1; } start = tok + 1; continue; } if (*tok == '}') { struct fwdtoken close = { .kind = FWDTOKEN_CLOSE, .line = line, .s = NULL }; if (!fwdtokens_append(tokens, close)) { fprintf(stderr, "failed appending close token\n"); return -1; } start = tok + 1; continue; } if (*tok == '=' && *(tok + 1) == '>') { struct fwdtoken to = { .kind = FWDTOKEN_TO, .line = line, .s = NULL }; if (!fwdtokens_append(tokens, to)) { fprintf(stderr, "failed appending to token\n"); return -1; } start = tok + 2; continue; } if (*tok == '"') { size_t start_line = line; const char *end = tok + 1; while (*end && *end != '"') { if (*end == '\n') { line++; while (isblank(*end)) end++; } if (*end == '\'') end++; end++; } if (!*end) { fprintf(stderr, "unterminated string at %zu\n", start_line); return -1; } char *s = strndup(tok + 1, end - tok - 2); if (!s) { fprintf(stderr, "failed duping str token\n"); return -1; } struct fwdtoken str = { .kind = FWDTOKEN_STR, .line = start_line, .s = s }; if (!fwdtokens_append(tokens, str)) { fprintf(stderr, "failed appending str token\n"); free(s); return -1; } start = end + 1; continue; } /* otherwise, read until next separation and check if we're * maybe dealing with a number or a regular identifier */ const char *end = next_separator(tok); char *dparse = NULL; double d = strtod(tok, &dparse); if (end == dparse) { /* all of the non-separators were consumed, so we have a * number */ struct fwdtoken num = { .kind = FWDTOKEN_NUM, .line = line, .d = d }; if (!fwdtokens_append(tokens, num)) { fprintf(stderr, "failed appending num token\n"); return -1; } start = end + 1; continue; } char *s = strndup(tok, end - tok); if (!s) { fprintf(stderr, "failed duping id token\n"); return -1; } struct fwdtoken id = { .kind = FWDTOKEN_ID, .line = line, .s = s }; if (!fwdtokens_append(tokens, id)) { fprintf(stderr, "failed appending id token\n"); free(s); return -1; } start = end; } return 0; } static void free_tokens(struct fwdtokens *tokens) { foreach(fwdtokens, token, tokens) { switch (token->kind) { case FWDTOKEN_STR: case FWDTOKEN_ID: free(token->s); default: break; } } fwdtokens_destroy(tokens); } static int fwdscript_parse(struct fwdscript *s, const char *buf) { struct fwdtokens tokens = fwdtokens_create(0); if (fwdscript_tokenize(&tokens, buf)) { free_tokens(&tokens); return -1; } /* convert tokens into some kind of structure */ TODO(); free_tokens(&tokens); return 0; } static int fwdscript_run(struct fwdscript *s, const char *name, struct fwdvals *args) { TODO(); } static void fwdscript_destroy(struct fwdscript *s) { fwdfuncs_destroy(&s->funcs); fwdinsns_destroy(&s->insns); fwdbuiltins_destroy(&s->builtins); } int main() { int ret = 0; struct fwdscript inst = fwdscript_create(); ret = fwdscript_register_builtins(&inst); assert(ret == 0); ret = fwdscript_parse(&inst, "main /tbl args {\n" " get /str args 0 => {abort \"impossible\"}\n" " => /str line;\n" " print line\n" "}\n"); assert(ret == 0); struct fwdvals args = fwdvals_create(1); fwdvals_append(&args, FWDSTR(strdup("Hello world!\n"))); fwdscript_run(&inst, "main", &args); fwdvals_destroy(&args); fwdscript_destroy(&inst); }