diff options
| author | Kimplul <kimi.h.kuparinen@gmail.com> | 2026-08-01 19:46:40 +0300 |
|---|---|---|
| committer | Kimplul <kimi.h.kuparinen@gmail.com> | 2026-08-01 19:46:40 +0300 |
| commit | c9c04c869051def472b3d3e381ef0682b46bc67a (patch) | |
| tree | e1449dfb0e4a4592e71033c12f40b7ca3fe508b7 | |
| parent | 2e688cefa1620a6dd79f917003e1317b277d805e (diff) | |
| download | fwdscript-c9c04c869051def472b3d3e381ef0682b46bc67a.tar.gz fwdscript-c9c04c869051def472b3d3e381ef0682b46bc67a.zip | |
analysis more-or-less works
+ No tests so probably missing a lot of bugs, hah
+ Next up, implement the builtins and then it's just (just) running
| -rw-r--r-- | src/fwdscript.c | 392 |
1 files changed, 384 insertions, 8 deletions
diff --git a/src/fwdscript.c b/src/fwdscript.c index a44fa93..215f782 100644 --- a/src/fwdscript.c +++ b/src/fwdscript.c @@ -132,8 +132,45 @@ struct fwdbuiltin { #define MAP_NAME fwdbuiltins #include <conts/map.h> +#define VEC_TYPE struct fwdast * +#define VEC_NAME fwdrefs +#include <conts/vec.h> + +struct fwdctx { + struct fwdfuncs defs; + struct fwdrefs refs; +}; + +static struct fwdctx fwdctx_create() +{ + return (struct fwdctx){ + .defs = fwdfuncs_create(0), + .refs = fwdrefs_create(0) + }; +} + +static void fwdctx_destroy(struct fwdctx *ctx) +{ + fwdfuncs_destroy(&ctx->defs); + fwdrefs_destroy(&ctx->refs); +} + +static struct fwdast *fwdctx_lookup(struct fwdctx *global, struct fwdctx *local, char *name) +{ + size_t *ref = NULL; + if (local) + if ((ref = fwdfuncs_find(&local->defs, name)) != NULL) + return *fwdrefs_at(&local->refs, *ref); + + if (global) + if ((ref = fwdfuncs_find(&global->defs, name)) != NULL) + return *fwdrefs_at(&global->refs, *ref); + + return NULL; +} + struct fwdscript { - struct fwdfuncs funcs; + struct fwdctx ctx; struct fwdinsns insns; struct fwdbuiltins builtins; }; @@ -141,9 +178,9 @@ struct fwdscript { static struct fwdscript fwdscript_create() { struct fwdscript inst = { - .funcs = fwdfuncs_create(1), .insns = fwdinsns_create(1), - .builtins = fwdbuiltins_create(1) + .builtins = fwdbuiltins_create(1), + .ctx = fwdctx_create() }; return inst; @@ -491,12 +528,30 @@ struct fwdast { char *s; union { + /* reference to local var > 0, global < 0 (= 0 is invalid) + * To get the actual index, calculate abs(var) - 1. + * + * Populated during analysis */ + ssize_t ref; + + /* number value */ double d; - enum fwdtype t; + }; - size_t argc; - struct fwdast *args; + union { + /* node type and status */ + struct { + bool moved; + enum fwdtype t; + }; + + /* cmd args and cnt params */ + struct { + size_t argc; + struct fwdast *args; + }; + }; size_t bodyc; struct fwdast *body; @@ -1044,6 +1099,322 @@ static void fwdscript_debug_ast(struct fwdast *ast, int depth) } } +static int fwdctx_add(struct fwdctx *ctx, struct fwdast *symb) +{ + assert(symb->s); + if (fwdrefs_append(&ctx->refs, symb) == NULL) { + fprintf(stderr, "couldn't append new ref to fwdctx\n"); + return -1; + } + + size_t handle = fwdrefs_len(&ctx->refs) - 1; + /* not really a func, should maybe rename structure to be more generic? */ + size_t *eh = fwdfuncs_insert(&ctx->defs, symb->s, handle); + if (!eh) { + fprintf(stderr, "couldn't insert ref to fwdfuncs\n"); + return -1; + } + + /* overwrite possible existing data (shadowing) */ + if (*eh != handle) + *eh = handle; + + return 0; +} + +static int fwdscript_analyze_body(struct fwdscript *s, struct fwdasts *body, struct fwdctx *ctx, bool last); + +static int fwdscript_analyze_id(struct fwdscript *s, struct fwdast *id, struct fwdctx *ctx) +{ + assert(id->kind == FWDAST_ID); + struct fwdast *def = NULL; + + /* only look for variable in the local scope since we don't have global + * variables and we don't support callbacks to global defs */ + if ((def = fwdctx_lookup(NULL, ctx, id->s)) == NULL) { + fprintf(stderr, "no such variable: %s\n", id->s); + return -1; + } + + assert(def->kind == FWDAST_VAR); + + if (def->t != FWDSCRIPT_NUM && def->moved) { + fprintf(stderr, "trying to reuse moved var %s\n", def->s); + return -1; + } + + def->moved = true; + id->t = def->t; + return 0; +} + +static int fwdscript_analyze_var(struct fwdscript *s, struct fwdast *var, struct fwdctx *ctx) +{ + if (fwdctx_add(ctx, var)) { + fprintf(stderr, "failed adding %s to local context\n", var->s); + return -1; + } + + return 0; +} + +static int fwdscript_analyze_cnt(struct fwdscript *s, struct fwdast *cnt, struct fwdctx *ctx, bool last) +{ + assert(cnt->kind == FWDAST_CNT); + struct fwdasts params = fwdast_args(cnt); + struct fwdasts body = fwdast_body(cnt); + + foreach(fwdasts, p, ¶ms) { + if (fwdscript_analyze_var(s, p, ctx)) + return -1; + } + + cnt->t = FWDSCRIPT_CNT; + return fwdscript_analyze_body(s, &body, ctx, last); +} + +static int fwdscript_analyze_arg(struct fwdscript *s, struct fwdast *arg, struct fwdctx *ctx, bool last) +{ + switch (arg->kind) { + case FWDAST_NUM: + arg->t = FWDSCRIPT_NUM; + return 0; + + case FWDAST_STR: + arg->t = FWDSCRIPT_STR; + return 0; + + case FWDAST_CNT: + /* for now, the body is handled in a separate pass later on so + * we don't overwrite local variable names */ + arg->t = FWDSCRIPT_CNT; + return 0; + + case FWDAST_ID: + return fwdscript_analyze_id(s, arg, ctx); + + default: + break; + } + + /* should never get here */ + abort(); + return -1; +} + +static int fwdscript_analyze_builtin(struct fwdscript *s, struct fwdast *builtin, struct fwdctx *ctx, bool last) +{ + assert(builtin->kind == FWDAST_CMD && builtin->s); + struct fwdbuiltin *def = NULL; + if ((def = fwdbuiltins_find(&s->builtins, builtin->s)) == NULL) { + fprintf(stderr, "no such cmd: %s\n", builtin->s); + return -1; + } + + + bool must_be_last = false; + struct fwdasts args = fwdast_args(builtin); + struct fwdtypes types = fwdtypes_create(fwdasts_len(&args)); + foreach(fwdasts, arg, &args) { + if (fwdscript_analyze_arg(s, arg, ctx, last)) + return -1; + + if (arg->kind == FWDAST_CNT) + must_be_last = true; + + if (fwdtypes_append(&types, arg->t) == NULL) { + fprintf(stderr, "failed appending type to builtin call\n"); + return -1; + } + } + + if (must_be_last && !last) { + fprintf(stderr, "%s ref call should be last, but isn't\n", builtin->s); + return -1; + } + + if (def->tp(s, &types)) { + fwdtypes_destroy(&types); + return -1; + } + + fwdtypes_destroy(&types); + + foreach(fwdasts, arg, &args) { + if (arg->kind != FWDAST_CNT) + continue; + + if (fwdscript_analyze_cnt(s, arg, ctx, last)) + return -1; + } + + return 0; +} + +static int fwdscript_analyze_ref_call(struct fwdscript *s, struct fwdast *cmd, struct fwdast *def, struct fwdctx *ctx, bool last) +{ + assert(def->kind == FWDAST_VAR); + if (def->t != FWDSCRIPT_CNT) { + fprintf(stderr, "trying to call %s %s\n", + fwdtype_str(def->t), + def->s); + return -1; + } + + /* needs to be checked at runtime, but we can at least do the move + * checks here */ + bool must_be_last = false; + struct fwdasts args = fwdast_args(cmd); + foreach(fwdasts, arg, &args) { + if (fwdscript_analyze_arg(s, arg, ctx, last)) + return -1; + + if (arg->kind == FWDAST_CNT) + must_be_last = true; + } + + if (must_be_last && !last) { + fprintf(stderr, "%s ref call should be last, but isn't\n", cmd->s); + return -1; + } + + foreach(fwdasts, arg, &args) { + if (arg->kind != FWDAST_CNT) + continue; + + if (fwdscript_analyze_cnt(s, arg, ctx, last)) + return -1; + } + + return 0; +} + +static int fwdscript_analyze_call(struct fwdscript *s, struct fwdast *cmd, struct fwdast *def, struct fwdctx *ctx, bool last) +{ + bool must_be_last = false; + struct fwdasts params = fwdast_args(def); + struct fwdasts args = fwdast_args(cmd); + + size_t pl = fwdasts_len(¶ms); + size_t al = fwdasts_len(&args); + + if (pl != al) { + fprintf(stderr, "%s expects %zu args, found %zu\n", + cmd->s, pl, al); + return -1; + } + + for (size_t i = 0; i < pl; ++i) { + struct fwdast *param = fwdasts_at(¶ms, i); + struct fwdast *arg = fwdasts_at(&args, i); + assert(param); + assert(arg); + + if (fwdscript_analyze_arg(s, arg, ctx, last)) + return -1; + + if (arg->kind == FWDAST_CNT) + must_be_last = true; + + if (param->t != arg->t) { + fprintf(stderr, "%s %s expects %s, found %s\n", + cmd->s, param->s, + fwdtype_str(param->t), + fwdtype_str(arg->t)); + return -1; + } + } + + if (must_be_last && !last) { + fprintf(stderr, "%s call should be last, but isn't\n", cmd->s); + return -1; + } + + foreach(fwdasts, arg, &args) { + if (arg->kind != FWDAST_CNT) + continue; + + if (fwdscript_analyze_cnt(s, arg, ctx, last)) + return -1; + } + + return 0; +} + +static int fwdscript_analyze_cmd(struct fwdscript *s, struct fwdast *cmd, struct fwdctx *ctx, bool last) +{ + assert(cmd->kind == FWDAST_CMD && cmd->s); + struct fwdast *def = NULL; + if ((def = fwdctx_lookup(&s->ctx, ctx, cmd->s)) == NULL) + return fwdscript_analyze_builtin(s, cmd, ctx, last); + + if (def->kind == FWDAST_VAR) + return fwdscript_analyze_ref_call(s, cmd, def, ctx, last); + + return fwdscript_analyze_call(s, cmd, def, ctx, last); +} + +static int fwdscript_analyze_body(struct fwdscript *s, struct fwdasts *body, struct fwdctx *ctx, bool last) +{ + size_t n = fwdasts_len(body); + for (size_t i = 0; i < n; ++i) { + bool last_cmd = false; + if (i == n - 1) + last_cmd = last; + + struct fwdast *cmd = fwdasts_at(body, i); + if (fwdscript_analyze_cmd(s, cmd, ctx, last_cmd)) + return -1; + } + + return 0; +} + +static int fwdscript_analyze_def(struct fwdscript *s, struct fwdast *def) +{ + assert(def->kind == FWDAST_CNT && def->s); + + /* add us to the global symbol table */ + if (fwdctx_add(&s->ctx, def)) + return -1; + + /* create a local symbol table */ + struct fwdctx ctx = fwdctx_create(); + + /* add local variables to local context */ + struct fwdasts params = fwdast_args(def); + foreach(fwdasts, p, ¶ms) { + p->moved = false; + + if (fwdctx_add(&ctx, p)) { + fwdctx_destroy(&ctx); + return -1; + } + } + + struct fwdasts body = fwdast_body(def); + if (fwdscript_analyze_body(s, &body, &ctx, true)) { + fwdctx_destroy(&ctx); + return -1; + } + + fwdctx_destroy(&ctx); + return 0; +} + +static int fwdscript_analyze(struct fwdscript *s, struct fwdast *top) +{ + assert(top->kind == FWDAST_TOP); + + struct fwdasts defs = fwdast_body(top); + foreach(fwdasts, def, &defs) { + if (fwdscript_analyze_def(s, def)) + return -1; + } + + return 0; +} + static int fwdscript_parse(struct fwdscript *s, const char *buf) { struct fwdtokens tokens = fwdtokens_create(0); @@ -1065,6 +1436,12 @@ static int fwdscript_parse(struct fwdscript *s, const char *buf) fwdscript_debug_ast(&top, 0); free_tokens(&tokens); + + if (fwdscript_analyze(s, &top)) { + free_ast(&top); + return -1; + } + return 0; } @@ -1078,7 +1455,6 @@ static void fwdscript_destroy(struct fwdscript *s) foreach(fwdbuiltins, builtin, &s->builtins) { free(builtin.t->key); } - fwdfuncs_destroy(&s->funcs); fwdinsns_destroy(&s->insns); fwdbuiltins_destroy(&s->builtins); } @@ -1093,7 +1469,7 @@ int main() ret = fwdscript_parse(&inst, "main /tbl args {\n" - " get /str args 0 => {abort \"impossible\"}\n" + " get args 0 => {abort \"impossible\"}\n" " => /str line;\n" " print line\n" "}\n"); |
