summaryrefslogtreecommitdiff
path: root/td.c
diff options
context:
space:
mode:
Diffstat (limited to 'td.c')
-rw-r--r--td.c506
1 files changed, 506 insertions, 0 deletions
diff --git a/td.c b/td.c
new file mode 100644
index 0000000..2e38fbd
--- /dev/null
+++ b/td.c
@@ -0,0 +1,506 @@
+#include "td.h"
+#include "errloc.h"
+#include "fn.h"
+#include "lex.h"
+#include "parse.h"
+#include "prv.h"
+#include "storage.h"
+#include "type.h"
+#include <errno.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static int cid(const struct lex *, struct prv *);
+static int cnum(const struct lex *, struct prv *);
+static int end(const struct lex *, struct prv *);
+static int u(const struct lex *, struct prv *);
+static int s(const struct lex *, struct prv *);
+static int c(const struct lex *, struct prv *);
+static int t(const struct lex *, struct prv *);
+static int p(const struct lex *, struct prv *);
+
+static const struct seq seqs[] =
+{
+ {(const struct step[]){{ID, "end"}, {0}}, .fn = end},
+ {(const struct step[]){
+ {ID}, {ID, "constant"}, {ID, "to"}, {ID}, {0}}, .fn = cid},
+ {(const struct step[]){
+ {ID}, {ID, "constant"}, {ID, "to"}, {NUM}, {0}}, .fn = cnum},
+ {(const struct step[]){{ID}, {ID, "constant"}, {0}}, .fn = c},
+ {(const struct step[]){{ID}, {ID, "type"}, {ID}, {0}}, .fn = t},
+ {(const struct step[]){{ID}, {ID, "prototype"}, {0}}, .fn = p},
+ {(const struct step[]){{ID}, {ID, "union"}, {0}}, .fn = u},
+ {(const struct step[]){{ID}, {ID, "struct"}, {0}}, .fn = s},
+ {0}
+};
+
+static void reset(struct prv *p)
+{
+ struct pos *pos = &p->pos[p->i];
+
+ pos->seq = seqs;
+ pos->step = seqs->steps;
+ p->stk = p->tk;
+}
+
+static int bump(const struct tk *tk, const struct type *t, struct prv *p)
+{
+ struct tdlevel *l = &p->levels[p->block];
+ int neg = 0;
+
+ if (!t->sign)
+ {
+ unsigned long long uv = t->u.c.uv;
+
+ if (uv + 1 < uv)
+ {
+ errloc(tk, "constant value exceeds maximum (%llu)", ULLONG_MAX);
+ return -1;
+ }
+
+ l->u.uv = ++uv;
+ }
+ else
+ {
+ long long v = t->u.c.v;
+
+ if (++v < 0)
+ neg = 1;
+
+ l->u.v = v;
+ }
+
+ l->neg = neg;
+
+ for (size_t i = 0; i < p->block; i++)
+ fputc('\t', stderr);
+
+ fprintf(stderr, "\t\tnext %s constant value set to ",
+ neg ? "negative" : "positive");
+
+ if (neg)
+ fprintf(stderr, "%lld", l->u.v);
+ else
+ fprintf(stderr, "%llu", l->u.uv);
+
+ fputc('\n', stderr);
+ return 0;
+}
+
+static int cid(const struct lex *l, struct prv *p)
+{
+ const struct fn *fn = fn_cur(p);
+ const struct tk *id = p->stk, *v = p->stk + 3;
+ const struct stentry *e;
+ const struct type *t = type_find(fn, id->s);
+ struct tdlevel *lev = &p->levels[p->block];
+ struct td *td = fn->td;
+ size_t n = td->ntypes + 1;
+ struct type *types, *nt;
+
+ if (t)
+ {
+ const struct loc *loc = &t->tk->loc;
+
+ errloc(id, "type \"%s\" already defined at %s:%d:%d", id->s, loc->f,
+ loc->line, loc->col);
+ return -1;
+ }
+ else if ((e = fn_var(fn, id)))
+ {
+ const struct loc *loc = &e->tk->loc;
+
+ errloc(id, "\"%s\" already defined as a variable at %s:%d:%d", id->s,
+ loc->f, loc->line, loc->col);
+ return -1;
+ }
+ else if ((e = fn_var(fn, v)))
+ {
+ const struct loc *loc = &e->tk->loc;
+
+ errloc(id, "value \"%s\" (defined at %s:%d:%d) is not a constant",
+ v->s, loc->f, loc->line, loc->col);
+ return -1;
+ }
+ else if (!(t = type_find(fn, v->s)))
+ {
+ errloc(id, "undefined reference to value \"%s\"", v->s);
+ return -1;
+ }
+ else if (t->type != C)
+ {
+ const struct loc *loc = &t->tk->loc;
+
+ errloc(id, "value \"%s\" (defined at %s:%d:%d) is not a constant",
+ v->s, loc->f, loc->line, loc->col);
+ return -1;
+ }
+ else if (!(types = realloc(td->types, n * sizeof *types)))
+ {
+ perror("realloc(3)");
+ return -1;
+ }
+
+ td->types = types;
+ nt = &td->types[td->ntypes++];
+ *nt = (struct type)
+ {
+ .type = C,
+ .tk = id,
+ .sign = t->sign,
+ .u.c = t->u.c
+ };
+
+ lev->neg = t->sign;
+
+ if (bump(id, t, p))
+ return -1;
+
+ for (size_t i = 0; i < p->block; i++)
+ fputc('\t', stderr);
+
+ fprintf(stderr, "\tadding constant %s to %s (%lld)\n", id->s, v->s,
+ t->u.c.v);
+ reset(p);
+ return 1;
+}
+
+static int cnum(const struct lex *l, struct prv *p)
+{
+ const struct fn *fn = fn_cur(p);
+ const struct tk *id = p->stk, *num = p->stk + 3;
+ const char *name = id->s;
+ const struct stentry *e;
+ struct td *td = fn->td;
+ size_t n = td->ntypes + 1;
+ struct type *types, *nt;
+ struct tdlevel *lev = &p->levels[p->block];
+ const struct type *t = type_find(fn, name);
+ int sign = *num->s == '-';
+ unsigned long long uv = 0;
+ long long v;
+ char *end;
+
+ errno = 0;
+ v = strtoll(num->s, &end, 0);
+
+ if (sign || errno || *end)
+ {
+ errno = 0;
+ uv = strtoull(num->s, NULL, 0);
+ }
+
+ if (t)
+ {
+ const struct loc *loc = &t->tk->loc;
+
+ errloc(id, "type \"%s\" already defined at %s:%d:%d", name, loc->f,
+ loc->line, loc->col);
+ return -1;
+ }
+ else if ((e = fn_var(fn, id)))
+ {
+ const struct loc *loc = &e->tk->loc;
+
+ errloc(id, "\"%s\" already defined as a variable at %s:%d:%d", id->s,
+ loc->f, loc->line, loc->col);
+ return -1;
+ }
+ else if (!(types = realloc(td->types, n * sizeof *types)))
+ {
+ perror("realloc(3)");
+ return -1;
+ }
+
+ td->types = types;
+ nt = &td->types[td->ntypes++];
+ *nt = (struct type)
+ {
+ .type = C,
+ .tk = id,
+ .sign = sign
+ };
+
+ if (sign)
+ lev->u.v = nt->u.c.v = v;
+ else
+ lev->u.uv = nt->u.c.uv = uv;
+
+ lev->neg = sign;
+
+ for (size_t i = 0; i < p->block; i++)
+ fputc('\t', stderr);
+
+ fprintf(stderr, "\tadding constant %s to %s\n", id->s, num->s);
+
+ if (bump(id, nt, p))
+ return -1;
+
+ reset(p);
+ return 1;
+}
+
+static int c(const struct lex *l, struct prv *p)
+{
+ const struct fn *fn = fn_cur(p);
+ const struct tk *id = p->stk;
+ const struct stentry *e;
+ const struct type *t = type_find(fn, id->s);
+ struct td *td = fn->td;
+ size_t n = td->ntypes + 1;
+ struct type *types, *nt;
+ const struct tdlevel *lev = &p->levels[p->block];
+
+ if (t)
+ {
+ const struct loc *loc = &t->tk->loc;
+
+ errloc(id, "type \"%s\" already defined at %s:%d:%d", id->s, loc->f,
+ loc->line, loc->col);
+ return -1;
+ }
+ else if ((e = fn_var(fn, id)))
+ {
+ const struct loc *loc = &e->tk->loc;
+
+ errloc(id, "\"%s\" already defined as a variable at %s:%d:%d", id->s,
+ loc->f, loc->line, loc->col);
+ return -1;
+ }
+ else if (!(types = realloc(td->types, n * sizeof *types)))
+ {
+ perror("realloc(3)");
+ return -1;
+ }
+
+ td->types = types;
+ nt = &td->types[td->ntypes];
+ *nt = (struct type)
+ {
+ .type = C,
+ .tk = id,
+ .sign = lev->neg,
+ };
+
+ if (nt->sign)
+ nt->u.c.v = lev->u.v;
+ else
+ nt->u.c.uv = lev->u.uv;
+
+ for (size_t i = 0; i < p->block; i++)
+ fputc('\t', stderr);
+
+ fprintf(stderr, "\tadding constant %s to ", id->s);
+
+ if (lev->neg)
+ fprintf(stderr, "%lld", lev->u.v);
+ else
+ fprintf(stderr, "%llu", lev->u.uv);
+
+ fputc('\n', stderr);
+
+ if (bump(id, &td->types[td->ntypes++], p))
+ return -1;
+
+ reset(p);
+ return 1;
+}
+
+static int end(const struct lex *l, struct prv *p)
+{
+ const struct tk *tk = p->tk - 1;
+ struct type *t;
+
+ if (!p->block)
+ {
+ errloc(tk, "unexpected \"end\" statement");
+ return -1;
+ }
+ else if (!(t = p->nest))
+ {
+ errloc(tk, "unreachable");
+ return -1;
+ }
+
+ switch (t->type)
+ {
+ case U:
+ {
+ const struct u *u = &t->u.u;
+
+ if (!u->storage->nentries)
+ {
+ errloc(t->tk, "struct \"%s\" has no children", t->tk->s);
+ return -1;
+ }
+ }
+ break;
+
+ case S:
+ {
+ const struct s *s = &t->u.s;
+
+ if (!s->storage->nentries)
+ {
+ errloc(t->tk, "struct \"%s\" has no children", t->tk->s);
+ return -1;
+ }
+ }
+ break;
+
+ default:
+ errloc(tk, "unreachable");
+ return -1;
+ }
+
+ p->nest = NULL;
+ p->block--;
+ reset(p);
+ return 1;
+}
+
+static int s(const struct lex *l, struct prv *p)
+{
+ const struct tk *id = p->stk;
+ struct fn *fn = fn_cur(p);
+ struct td *td = fn->td;
+ size_t nb = p->block + 2, nt = td->ntypes + 1;
+ struct tdlevel *levels;
+ struct type *types, *newt;
+ const struct type *t = type_find(fn, id->s);
+
+ if (t)
+ {
+ const struct loc *loc = &t->tk->loc;
+
+ errloc(id, "type \"%s\" already defined at %s:%d:%d", id->s, loc->f,
+ loc->line, loc->col);
+ return -1;
+ }
+ else if (!(types = realloc(td->types, nt * sizeof *types)))
+ {
+ perror("realloc(3)");
+ return -1;
+ }
+
+ td->types = types;
+ newt = &td->types[td->ntypes++];
+ *newt = (struct type)
+ {
+ .type = S,
+ .tk = id
+ };
+
+ if (!(levels = realloc(p->levels, nb * sizeof *levels)))
+ {
+ perror("realloc(3)");
+ return -1;
+ }
+
+ p->levels = levels;
+ p->levels[++p->block] = (struct tdlevel){0};
+ p->nest = newt;
+ return storage(l, p, id->s, 0, &newt->u.s.storage);
+}
+
+static int u(const struct lex *l, struct prv *p)
+{
+ fprintf(stderr, "%s: TODO\n", __func__);
+ return -1;
+}
+
+static int t(const struct lex *l, struct prv *p)
+{
+ fprintf(stderr, "%s: TODO\n", __func__);
+ return -1;
+}
+
+static int p(const struct lex *l, struct prv *p)
+{
+ fprintf(stderr, "%s: TODO\n", __func__);
+ return -1;
+}
+
+static void type_free(struct type *t)
+{
+ switch (t->type)
+ {
+ case U:
+ {
+ struct u *u = &t->u.u;
+
+ storage_free(u->storage);
+ }
+ break;
+ case S:
+ {
+ struct s *s = &t->u.s;
+
+ storage_free(s->storage);
+ }
+ break;
+ case C:
+ case P:
+ case T:
+ case BUILTIN:
+ break;
+ }
+}
+
+void td_free(struct td *td)
+{
+ if (td)
+ {
+ for (size_t i = 0; i < td->ntypes; i++)
+ type_free(&td->types[i]);
+
+ free(td->types);
+ }
+
+ free(td);
+}
+
+int td(const struct lex *l, struct prv *p)
+{
+ const struct tk *tk = p->stk;
+ struct fn *fn = fn_cur(p);
+ struct td *td = NULL, *prev = fn->td;
+ struct tdlevel *levels = NULL;
+ struct pos init =
+ {
+ .seq = seqs,
+ .stseq = seqs,
+ .step = seqs->steps
+ };
+
+ if (prev)
+ {
+ const struct loc *loc = &prev->tk->loc;
+
+ errloc(tk, "types section already defined at %s:%d:%d", loc->f,
+ loc->line, loc->col);
+ goto failure;
+ }
+ else if (!(td = malloc(sizeof *td))
+ || !(levels = malloc(sizeof *levels)))
+ {
+ perror("malloc(3)");
+ goto failure;
+ }
+ else if (push(&init, p))
+ goto failure;
+
+ *levels = (struct tdlevel){0};
+ *td = (struct td){.tk = tk};
+ fn->td = td;
+ p->levels = levels;
+ p->stk = p->tk;
+ return 1;
+
+failure:
+ free(levels);
+ free(td);
+ return -1;
+}