summaryrefslogtreecommitdiff
path: root/src/fwdscript.c
diff options
context:
space:
mode:
authorKimplul <kimi.h.kuparinen@gmail.com>2026-07-31 23:50:59 +0300
committerKimplul <kimi.h.kuparinen@gmail.com>2026-07-31 23:50:59 +0300
commitf8bf4aaa3e4c25f6ece6cb9b927378e0adcb41a2 (patch)
treeac95ca1628924121e8bb8533f738d21de137236f /src/fwdscript.c
downloadfwdscript-f8bf4aaa3e4c25f6ece6cb9b927378e0adcb41a2.tar.gz
fwdscript-f8bf4aaa3e4c25f6ece6cb9b927378e0adcb41a2.zip
initial tokenization
Diffstat (limited to 'src/fwdscript.c')
-rw-r--r--src/fwdscript.c447
1 files changed, 447 insertions, 0 deletions
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);
+}