diff options
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | .gitmodules | 3 | ||||
| -rw-r--r-- | Makefile | 4 | ||||
| m--------- | deps/conts | 0 | ||||
| -rw-r--r-- | src/fwdscript.c | 447 |
5 files changed, 455 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1df7c5c --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +fwdscript 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..5e0699e --- /dev/null +++ b/Makefile @@ -0,0 +1,4 @@ +all: fwdscript + +fwdscript: src/fwdscript.c + $(CC) -Ideps/conts/include -Wall -Wextra -O0 -g $< -o $@ diff --git a/deps/conts b/deps/conts new file mode 160000 +Subproject e962c7a4c70b6e2c3f2df89be176c491d62739e diff --git a/src/fwdscript.c b/src/fwdscript.c new file mode 100644 index 0000000..c1eda21 --- /dev/null +++ b/src/fwdscript.c @@ -0,0 +1,447 @@ +#include <err.h> +#include <stdio.h> +#include <ctype.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> + +#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 <conts/map.h> + +#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 <conts/map.h> + +#define VEC_TYPE struct fwdval +#define VEC_NAME fwdvals +#include <conts/vec.h> + +#define VEC_TYPE enum fwdtype +#define VEC_NAME fwdtypes +#include <conts/vec.h> + +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 <conts/vec.h> + +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 <conts/map.h> + +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_OPEN, + FWDTOKEN_CLOSE, + FWDTOKEN_TO, + FWDTOKEN_END + } kind; + + size_t line; + char *s; +}; + +#define VEC_TYPE struct fwdtoken +#define VEC_NAME fwdtokens +#include <conts/vec.h> + +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 */ + const char *end = next_separator(tok); + 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 int fwdscript_parse(struct fwdscript *s, const char *buf) +{ + struct fwdtokens tokens = fwdtokens_create(0); + + if (fwdscript_tokenize(&tokens, buf)) { + fwdtokens_destroy(&tokens); + return -1; + } + + /* convert tokens into some kind of structure */ + todo(); + fwdtokens_destroy(&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); +} |
