From 8afa976e073c7bc29c878230eead6dddfdcc08a1 Mon Sep 17 00:00:00 2001 From: Nakidai Date: Sun, 5 Apr 2026 19:51:14 +0300 Subject: Add code --- compile.c | 480 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 480 insertions(+) create mode 100644 compile.c (limited to 'compile.c') diff --git a/compile.c b/compile.c new file mode 100644 index 0000000..d488414 --- /dev/null +++ b/compile.c @@ -0,0 +1,480 @@ +/* + * Copyright (c) 2026 Nakidai Perumenei + * + * Permission to use, copy, modify, and 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 "thac.h" + +#include +#include +#include + + +struct val nil = {VVAR, {.name = "nil"}}; +static struct var valnil = {"nil", {VNIL, {0}}}; +struct scope basescope = {NULL, &valnil, 1}; + +static struct val zero = {VNUM, {0}}; + +static struct val +rnum(struct node *node, struct scope *scope) +{ + struct val val; + + val.type = VNUM; + val.num = node->nnum; + + return val; +} + +static struct val +rvar(struct node *node, struct scope *scope) { + struct val val; + + val.type = VVAR; + val.name = node->nvar; + + return val; +} + +static struct val +roper(struct node *node, struct scope *scope) +{ + struct val l, m, r, ref, val; + struct scope *lscope; + vlong i, j; + + val.type = VNUM; + switch (node->noper.type) + { +#define BIN(NAME, OPER) break; case NAME: \ +{ \ + l = getval(VNUM, eval(node->noper.l, scope), scope, 1); \ + r = getval(VNUM, eval(node->noper.r, scope), scope, 1); \ + val.num = l.num OPER r.num; \ +} + BIN(OSUM, +) + BIN(OSUB, -) + BIN(OMUL, *) + BIN(ODIV, /) + BIN(OMOD, %) + BIN(OLSHIFT, <<) + BIN(ORSHIFT, >>) + BIN(OLESS, <) + BIN(OLESSEQ, <=) + BIN(OGREAT, >) + BIN(OGREATEQ, >=) + BIN(OEQ, ==) + BIN(ONEQ, !=) + BIN(OAND, &&) + BIN(OOR, ||) + BIN(OBOR, |) + BIN(OBAND, &) + BIN(OBXOR, ^) +#undef BIN + break; case OPOW: + l = getval(VNUM, eval(node->noper.l, scope), scope, 1); + r = getval(VNUM, eval(node->noper.r, scope), scope, 1); + + val.num = 1; + for (i = 0; i < r.num; ++i) + val.num *= l.num; +#define UN(NAME, OPER) break; case NAME: \ +{ \ + m = getval(VNUM, eval(node->noper.m, scope), scope, 1); \ + val.num = OPER m.num; \ +} + UN(ONOT, !) + UN(OINV, ~) +#undef UN + break; case OASSIGN: + { + struct val shouldfree = nil; + struct var *var; + + val = getval(VVAR, eval(node->noper.l, scope), scope, 0); + lscope = scope; + i = findvar(val.name, &lscope); + if (i > 0) + shouldfree = deref(val, scope); + + r = deref(eval(node->noper.r, scope), scope); + + /* it's fine to use scope here as it won't be used after */ + assignvar(val.name, copyval(r), &scope); + freeval(shouldfree); + freeval(r); + } +#define ASS(NAME, OPER) break; case NAME: \ +{ \ + ref = getval(VVAR, eval(node->noper.l, scope), scope, 0); \ + l = getval(VNUM, ref, scope, 1); \ + r = getval(VNUM, eval(node->noper.r, scope), scope, 1); \ + val.num = l.num OPER r.num; \ + assignvar(ref.name, val, &scope); \ + val = ref; \ +} + ASS(OASSSUM, +) + ASS(OASSSUB, -) + ASS(OASSMUL, *) + ASS(OASSDIV, /) + ASS(OASSMOD, %) + ASS(OASSBOR, |) + ASS(OASSBAND, &) + ASS(OASSBXOR, ^) + ASS(OASSLSHIFT, <<) + ASS(OASSRSHIFT, >>) +#undef ASS +#define POST(NAME, OPER) break; case NAME: \ +{ \ + ref = getval(VVAR, eval(node->noper.m, scope), scope, 0); \ + val = getval(VNUM, ref, scope, 1); \ + m = val; \ + OPER m.num; \ + assignvar(ref.name, m, &scope); \ +} + POST(OPOSTDECR, --) + POST(OPOSTINCR, ++) +#undef POST +#define PRE(NAME, OPER) break; case NAME: \ +{ \ + ref = getval(VVAR, eval(node->noper.m, scope), scope, 0); \ + val = getval(VNUM, ref, scope, 1); \ + OPER val.num; \ + assignvar(ref.name, val, &scope); \ +} + PRE(OPREDECR, --) + PRE(OPREINCR, ++) +#undef PRE + break; case OCOND: + l = getval(VNUM, eval(node->noper.l, scope), scope, 1); + val = eval(l.num ? node->noper.m : node->noper.r, scope); + break; case OARRAY: + { + char varname[GENVARLEN]; + int c; + + if ((c = varnextchar(scope)) == -1) + complain(1, "too nested array is wanted (27+)"); + snprintf(varname, sizeof(varname), "@%c", c); + + i = assignvar(varname, zero, &scope); + l = getval(VNUM, eval(node->noper.l, scope), scope, 1); + + val.type = VARR; + val.arr = NULL; + val.len = 0; + + for (j = 0; j < l.num; ++j, scope->vars[i].val.num = j) + { + r = deref(eval(node->noper.r, scope), scope); + + val.arr = realloc(val.arr, sizeof(*val.arr) * ++val.len); + if (!val.arr) + dieno(1, "realloc()"); + + val.arr[j] = copyval(r); + } + delvarscope(scope, i); + } + break; case OINDEX: + ref = eval(node->noper.l, scope); + l = getval(VARR, ref, scope, 1); + r = getval(VNUM, eval(node->noper.r, scope), scope, 1); + if (r.num < 0 || r.num >= l.len) + complain(1, "duh that's oob (0<=%lld<%lld)", r.num, l.len); + val = copyval(l.arr[r.num]); + if (ref.type == VARR) + freeval(ref); + break; case OSLICE: + ref = eval(node->noper.l, scope); + l = getval(VARR, ref, scope, 1); + m = getval(VNUM, eval(node->noper.m, scope), scope, 1); + r = getval(VNUM, eval(node->noper.r, scope), scope, 1); + m.num = min(l.len, max(0, m.num)); + r.num = min(l.len, max(0, r.num)); + + val.type = VARR; + val.len = r.num - m.num; + val.arr = malloc(sizeof(*val.arr) * val.len); + if (!val.arr) + dieno(1, "malloc()"); + + for (i = m.num; i < r.num; ++i) + val.arr[i - m.num] = copyval(l.arr[i]); + + if (ref.type == VARR) + freeval(ref); + break; case OCONCAT: + { + struct val refl, refr; + refl = eval(node->noper.l, scope); + l = getval(VARR, refl, scope, 1); + refr = eval(node->noper.r, scope); + r = getval(VARR, refr, scope, 1); + + val.type = VARR; + val.len = l.len + r.len; + val.arr = malloc(sizeof(*val.arr) * val.len); + if (!val.arr) + dieno(1, "malloc()"); + + for (i = 0; i < l.len; ++i) + val.arr[i] = copyval(l.arr[i]); + for (i = 0; i < r.len; ++i) + val.arr[l.len + i] = copyval(r.arr[i]); + + if (refl.type == VARR) + freeval(refl); + if (refr.type == VARR) + freeval(refr); + } + break; case OLEN: + m = getval(VARR, eval(node->noper.m, scope), scope, 1); + val.num = m.len; + break; case OASSERT: + m = getval(VNUM, eval(node->noper.m, scope), scope, 1); + if (!(val.num = m.num)) + complain(1, "assertion has failed!"); + break; case OCONNECT: + val = connectval( + eval(node->noper.l, scope), + eval(node->noper.r, scope), + scope + ); + break; default: + die(1, "undone %s: %s", nodename(node->type), nopername(node->noper.type)); + } + + return val; +} + +static struct val +rnode(struct node *node, struct scope *scope) +{ + struct val prop, val; + ulong i; + + val.type = VNUM; + val.num = mkvert(); + + for (i = 0; i < node->nnode.len; ++i) + { + prop = getval(VNUM, eval(node->nnode.params[i], scope), scope, 1); + addprop(val.num, prop.num); + } + + return val; +} + +static struct val +rcomp(struct node *node, struct scope *scope) +{ + struct scope lscope = {scope, NULL, 0}; + struct val val = nil; + ulong i; + + for (i = 0; i < node->ncomp.len; ++i) + { + val = eval(node->ncomp.stmts[i], &lscope); + if (val.type == VBREAK || val.type == VCONT) + break; + } + + for (i = 0; i < lscope.len; ++i) + freeval(lscope.vars[i].val); + free(lscope.vars); + return val; +} + +static struct val +rcond(struct node *node, struct scope *scope) +{ + struct val val; + val = getval(VNUM, eval(node->ncond.cond, scope), scope, 1); + return eval(val.num ? node->ncond.t : node->ncond.f, scope); +} + +static struct val +rfor(struct node *node, struct scope *scope) +{ + struct val val; + + eval(node->nfor.before, scope); +loop: + val = getval(VNUM, eval(node->nfor.cond, scope), scope, 1); + if (!val.num) + return nil; + + val = eval(node->nfor.code, scope); + if (val.type == VBREAK) + return nil; + + eval(node->nfor.between, scope); + goto loop; +} + +static struct val +rforeachval(struct val arr, struct scope *scope, struct node *code) +{ + char varname[GENVARLEN]; + struct scope *tscope; + ulong i, j, target; + struct val val; + + snprintf(varname, sizeof(varname), "@%lu", varnextnum(scope)); + i = assignvar(varname, zero, &scope); + + for (j = 0; j < arr.len; ++j, scope->vars[i].val.num = j) + { + if (arr.arr[j].type == VARR) + { + val = rforeachval(arr.arr[j], scope, code); + } + else + { + tscope = scope; + target = assignvar("@", copyval(arr.arr[j]), &tscope); + val = eval(code, scope); + delvarscope(tscope, target); + } + if (val.type == VBREAK) + return val; + } + + delvarscope(scope, i); + return nil; +} + +static struct val +rforeach(struct node *node, struct scope *scope) +{ + struct val arr; + + arr = getval(VARR, eval(node->nforeach.obj, scope), scope, 1); + rforeachval(arr, scope, node->nforeach.code); + + return nil; +} + +static struct val +rmod(struct node *node, struct scope *scope) +{ + struct val val; + + val.type = VNUM; + val.num = (vlong)node; + + return val; +} + +/* + * TODO: module doesn't know caller's environment, so it should not + * depend on its scope, but rather make an own one at rmod() + */ +static struct val +rwith(struct node *node, struct scope *scope) +{ + struct scope argscope = {scope, NULL, 0}; + struct scope modscope = {&argscope, NULL, 0}; + struct node *mod; + struct val val; + ulong i; + + val = getval(VNUM, eval(node->nwith.mod, scope), scope, 1); + mod = (struct node *)val.num; + + if (mod->nmod.len != node->nwith.len) + complain( + 1, + "have %lu parameter%s, got %lu argument%s", + mod->nmod.len, + mod->nmod.len != 1 ? "s" : "", + node->nwith.len, + mod->nwith.len != 1 ? "s" : "" + ); + + argscope.len = mod->nmod.len; + argscope.vars = malloc(sizeof(*argscope.vars) * argscope.len); + if (!argscope.vars) + dieno(1, "malloc()"); + + for (i = 0; i < argscope.len; ++i) + { + argscope.vars[i].name = mod->nmod.params[i]; + argscope.vars[i].val = deref(eval(node->nwith.args[i], scope), scope); + } + + for (i = 0; i < mod->nmod.code->ncomp.len; ++i) + { + val = eval(mod->nmod.code->ncomp.stmts[i], &modscope); + switch (val.type) + { + case VBREAK: case VCONT: + complain(1, "cannot %s out of module", valname(val.type)); + break; default: break; + } + } + + eval(node->nwith.code, &modscope); + + for (i = 0; i < modscope.len; ++i) + freeval(modscope.vars[i].val); + free(modscope.vars); + return nil; +} + +static struct val +rbreak(struct node *node, struct scope *scope) +{ + struct val val; + + val.type = node->type == NBREAK ? VBREAK : VCONT; + + return val; +} + +static struct val +rerr(struct node *node, struct scope *scope) +{ + struct val val; + + complain(1, "unknown node %s found", nodename(node->type)); + + /* not reached */ + return nil; +} + +struct val +eval(struct node *node, struct scope *scope) +{ + if (!node) + return nil; + +#define is(t) (node->type == t) + return (is(NCOMP) ? rcomp : + is(NCOND) ? rcond : + is(NFOR) ? rfor : + is(NFOREACH) ? rforeach : + is(NMOD) ? rmod : + is(NNUM) ? rnum : + is(NNODE) ? rnode : + is(NOPER) ? roper : + is(NWITH) ? rwith : + is(NVAR) ? rvar : + is(NBREAK) ? rbreak : + is(NCONT) ? rbreak : + rerr)(node, scope); +#undef is +} -- cgit 1.4.1