aboutsummaryrefslogtreecommitdiff
path: root/from.c
diff options
context:
space:
mode:
Diffstat (limited to 'from.c')
-rw-r--r--from.c364
1 files changed, 364 insertions, 0 deletions
diff --git a/from.c b/from.c
new file mode 100644
index 0000000..b9cb99e
--- /dev/null
+++ b/from.c
@@ -0,0 +1,364 @@
+#include "from.h"
+#include "cgen.h"
+#include "fn.h"
+#include "errloc.h"
+#include "stmt.h"
+#include "storage.h"
+#include "tmp.h"
+#include "type.h"
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+void from_free(struct from *fr)
+{
+}
+
+static int loadvar(const struct from_it *st, struct cgen *c)
+{
+ const struct stentry *e = st->u.e;
+ size_t sz = e->t->sz;
+ const char *tmp = cgen_tmp(sz);
+
+ printf("%%%s =%s load%s ", tmp, cgen_sz(sz), cgen_load(e->t));
+ cgen_printvar(c->fn, e);
+ putchar('\n');
+ return 0;
+}
+
+/* TODO: de-duplicate function */
+static unsigned long long tonum(const char *s)
+{
+ unsigned long long v;
+ char *end;
+
+ errno = 0;
+ v = strtoull(s, &end, 0);
+
+ if (*s == '-' || errno || *end)
+ return strtoll(s, NULL, 0);
+
+ return v;
+}
+
+static int loadval(const struct from *fr, struct cgen *c)
+{
+ const struct stentry *dst = fr->var;
+ const struct from_it *st = &fr->start;
+ unsigned long long v = tonum(st->u.tk->s);
+
+ printf("store%s %llu, ", cgen_sz(dst->t->sz), v);
+ cgen_printvar(c->fn, dst);
+ putchar('\n');
+ return 0;
+}
+
+static int endvar(const struct from *fr, struct cgen *c)
+{
+ fprintf(stderr, "%s: TODO\n", __func__);
+ return -1;
+}
+
+static int endval(const struct from *fr, struct cgen *c)
+{
+ const struct stentry *src = fr->var;
+ size_t sz = src->t->sz;
+ const char *tmp = cgen_tmp(sz);
+ const struct from_it *end = &fr->end;
+ const struct tk *tk = end->u.tk;
+ int neg = *tk->s == '-';
+ size_t bl = fr->block_i;
+
+ printf("%%%s =%s load%s ", tmp, cgen_sz(sz), cgen_load(src->t));
+ cgen_printvar(c->fn, src);
+ putchar('\n');
+ printf("%%%s =%s c", tmp, cgen_sz(sz));
+ fputs(neg ? "sge" : "uge", stdout);
+ printf("%s ", cgen_sz(sz));
+ printf("%%%s, %s\n", tmp, tk->s);
+ printf("jnz %%%s, @__end%zu, @__blockbody%zu\n", tmp, bl, bl);
+ printf("@__blockbody%zu\n", bl);
+ return 0;
+}
+
+static int from_var(const struct from *fr, struct cgen *c)
+{
+ const struct from_it *st = &fr->start;
+
+ switch (st->type)
+ {
+ case FR_VAR:
+ if (loadvar(st, c))
+ return -1;
+ break;
+
+ case FR_VAL:
+ if (loadval(fr, c))
+ return -1;
+ break;
+ }
+
+ printf("@__block%zu\n", fr->block_i);
+
+ switch (fr->end.type)
+ {
+ case FR_VAR:
+ if (endvar(fr, c))
+ return -1;
+ break;
+
+ case FR_VAL:
+ if (endval(fr, c))
+ return -1;
+ break;
+ }
+
+ return 0;
+}
+
+int from_cgen(const struct from *fr, struct cgen *c)
+{
+ return from_var(fr, c);
+}
+
+static int from_end(const struct stmt *s, struct cgen *c)
+{
+ const struct from *fr = &s->u.from;
+ const struct stentry *src = fr->var;
+ const struct type *t = src->t;
+ size_t sz = t->sz;
+ const char *tmp = cgen_tmp(sz);
+ const struct fn *fn = c->fn;
+
+ printf("%%%s =%s load%s ", tmp, cgen_sz(sz), cgen_load(t));
+ cgen_printvar(fn, src);
+ putchar('\n');
+ printf("%%%s =%s add %%%s, 1\n", tmp, cgen_sz(sz), tmp);
+ printf("store%s %%%s, ", cgen_sz(sz), tmp);
+ cgen_printvar(fn, src);
+ putchar('\n');
+ printf("jmp @__block%zu\n", fr->block_i);
+ return 0;
+}
+
+static int checksym(const struct fn *fn, const struct tk *tk,
+ struct from_it *fr)
+{
+ const struct stentry *e = fn_var(fn, tk);
+
+ if (!e)
+ {
+ errloc(tk, "undefined reference to \"%s\"", tk->s);
+ return -1;
+ }
+
+ switch (e->t->type)
+ {
+ case C:
+ case BUILTIN:
+ break;
+ case U:
+ case S:
+ case P:
+ case T:
+ case PTR:
+ case ARY:
+ {
+ char *s = type_name(e->t);
+
+ if (s)
+ errloc(tk, "\"%s\" (\"%s\") cannot be used in \"from\" loop",
+ tk->s, s);
+
+ free(s);
+ return -1;
+ }
+ }
+
+ fr->type = FR_VAR;
+ fr->u.e = e;
+ return 0;
+}
+
+static int check(const struct fn *fn, const struct tk *tk, struct from_it *fr)
+{
+ switch (tk->type)
+ {
+ case NUM:
+ fr->type = FR_VAL;
+ fr->u.tk = tk;
+ break;
+ case ID:
+ return checksym(fn, tk, fr);
+ case LIT:
+ errloc(tk, "unexpected literal");
+ return -1;
+ case UNDEF:
+ case ANY:
+ fprintf(stderr, "%s: unreachable\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int finalize(struct prv *p, struct fn *fn, struct from *fr)
+{
+ struct stmt *s;
+ struct pos *pos = &p->pos[p->i];
+ size_t nst = fn->nstmts + 1, nbl = fn->nblocks + 1,
+ *bl = realloc(fn->blocks, nbl * sizeof *bl);
+
+ if (!bl)
+ {
+ perror("realloc(3)");
+ return -1;
+ }
+
+ fr->block_i = bl[fn->nblocks++] = fn->nstmts;
+ fn->blocks = bl;
+
+ if (!(s = realloc(fn->stmts, nst * sizeof *s)))
+ {
+ perror("realloc(3)");
+ return -1;
+ }
+
+ s[fn->nstmts++] = (struct stmt)
+ {
+ .type = FROM,
+ .u.from = *fr,
+ .end = from_end
+ };
+
+ fn->stmts = s;
+ pos->seq = pos->stseq = stmts;
+ pos->step = stmts->steps;
+ fprintf(stderr, ", iterator %s, block index %zu\n", fr->var->tk->s,
+ bl[fn->nblocks - 1]);
+ return 0;
+}
+
+static const struct type *valtype(const struct fn *fn, const struct tk *tk)
+{
+ char *end;
+
+ errno = 0;
+ strtol(tk->s, &end, 0);
+
+ if (errno || *end)
+ {
+ errno = 0;
+ strtoll(tk->s, &end, 0);
+
+ if (errno || *end)
+ return type_find(fn, "ulong");
+
+ return type_find(fn, "long");
+ }
+
+ return type_find(fn, "word");
+}
+
+static const struct type *ittype(const struct fn *fn, const struct from_it *it)
+{
+ switch (it->type)
+ {
+ case FR_VAL:
+ return valtype(fn, it->u.tk);
+ case FR_VAR:
+ return it->u.e->t;
+ }
+
+ fprintf(stderr, "%s: unreachable\n", __func__);
+ return NULL;
+}
+
+static const struct type *gettype(const struct fn *fn,
+ const struct from_it *a, const struct from_it *b)
+{
+ const struct type *ta = ittype(fn, a), *tb = ittype(fn, b);
+
+ return ta->sz > tb->sz ? ta : tb;
+}
+
+int from(const struct lex *l, struct prv *p)
+{
+ const struct tk *start = p->stk + 1, *end = p->stk + 3;
+ struct fn *fn = fn_cur(p);
+ struct from fr = {0};
+ const struct type *t;
+
+ if (check(fn, start, &fr.start)
+ || check(fn, end, &fr.end)
+ || !(t = gettype(fn, &fr.start, &fr.end))
+ || !(fr.var = tmp_create(fn, t)))
+ return -1;
+
+ fprintf(stderr, "\t\tadding from statement [%s, %s]", start->s, end->s);
+
+ if (finalize(p, fn, &fr))
+ return -1;
+
+ p->stk = ++end;
+ return 1;
+}
+
+static int checkvar(const struct fn *fn, const struct tk *tk, struct from *fr)
+{
+ const struct stentry *e;
+
+ if (tk->type != ID)
+ {
+ errloc(tk, "\"%s\" not a variable", tk->s);
+ return -1;
+ }
+ else if (!(e = fn_var(fn, tk)))
+ {
+ errloc(tk, "undefined reference to \"%s\"", tk->s);
+ return -1;
+ }
+ else if (e->t->type != BUILTIN)
+ {
+ errloc(tk, "variable \"%s\" not an integer", tk->s);
+ return -1;
+ }
+
+ fr->var = e;
+ return 0;
+}
+
+int from_id(const struct lex *l, struct prv *p)
+{
+ const struct tk *start = p->stk + 1, *end = p->stk + 3, *var = p->stk + 5;
+ struct fn *fn = fn_cur(p);
+ struct from fr = {0};
+
+ if (check(fn, start, &fr.start)
+ || check(fn, end, &fr.end)
+ || checkvar(fn, var, &fr))
+ return -1;
+
+ fprintf(stderr, "\t\tadding from statement [%s, %s] using %s",
+ start->s, end->s, var->s);
+
+ if (finalize(p, fn, &fr))
+ return -1;
+
+ p->stk = ++var;
+ return 1;
+}
+
+int from_by(const struct lex *l, struct prv *p)
+{
+ fprintf(stderr, "%s: TODO\n", __func__);
+ return -1;
+}
+
+int from_idby(const struct lex *l, struct prv *p)
+{
+ fprintf(stderr, "%s: TODO\n", __func__);
+ return -1;
+}