diff options
Diffstat (limited to 'storage.c')
| -rw-r--r-- | storage.c | 217 |
1 files changed, 217 insertions, 0 deletions
diff --git a/storage.c b/storage.c new file mode 100644 index 0000000..8ab1ad4 --- /dev/null +++ b/storage.c @@ -0,0 +1,217 @@ +#include "storage.h" +#include "errloc.h" +#include "fn.h" +#include "lex.h" +#include "prv.h" +#include "type.h" +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + +static int id(const struct lex *l, struct prv *p); + +static const struct seq seqs[] = +{ + {(struct step[]){{ID}, {0}}, .fn = id}, + {0} +}; + +static int compat(const struct fn *fn, const struct prv *p, + const struct tk *name, const struct type *t, const struct type **ot) +{ + const struct stentry *oe; + const struct fn *top = fn_cur(p); + + /* allow global entries if already found on linkage section + * and vice versa, as long as the types are compatible. */ + if (p->st == top->gl) + { + for (size_t i = 0; i < fn->nimps; i++) + { + const struct im *im = &fn->imps[i]; + + if (compat(im->ast.fns, p, name, t, ot)) + return 1; + } + + if (fn->lk && (oe = storage_find(name->s, fn->lk))) + { + if (t == oe->t) + return 1; + + *ot = oe->t; + return -1; + } + } + else if (p->st == top->lk) + { + if (fn->gl && (oe = storage_find(name->s, fn->gl))) + { + if (t == oe->t) + return 1; + + *ot = oe->t; + return -1; + } + } + + return 0; +} + +static int stpush(const struct lex *l, struct prv *p, const struct tk *name, + const struct type *t) +{ + int ret = -1; + struct stentry *entries; + struct storage *st = p->st; + size_t n = st->nentries + 1; + const struct fn *fn = fn_cur(p); + const struct loc *loc = &name->loc; + const struct stentry *e; + char *tname = type_name(t); + + if (!t->sz && p->st != fn->lk) + { + errloc(name + 1, "type \"%s\" has null size", tname); + goto end; + } + else if (st->check && (e = fn_var(fn, name))) + { + const struct loc *loc = &e->tk->loc; + const struct type *ot; + int ret = compat(fn, p, name, t, &ot); + + if (ret < 0) + { + errloc(name, "variable \"%s\" (\"%s\") already defined at " + "%s:%d:%d with incompatible type (\"%s\")", + name->s, type_name(t), loc->f, loc->line, loc->col, + type_name(ot)); + goto end; + } + else if (!ret) + { + errloc(name, "variable \"%s\" already defined at %s:%d:%d", + name->s, loc->f, loc->line, loc->col); + goto end; + } + } + + /* find var in currently known symbols */ + for (size_t i = 0; i < st->nentries; i++) + { + const struct stentry *e = &st->entries[i]; + const struct tk *tk = e->tk; + + if (!strcmp(name->s, tk->s)) + { + const struct loc *loc = &tk->loc; + + errloc(tk, "variable \"%s\" already defined at %s:%d:%d", + tk->s, loc->f, loc->line, loc->col); + goto end; + } + } + + if (pop(l, p)) + goto end; + else if (!(entries = realloc(st->entries, n * sizeof *entries))) + { + perror("realloc(3)"); + goto end; + } + + if (t->sz) + st->offset += st->offset % t->sz; + + entries[st->nentries++] = (struct stentry) + { + .t = t, + .tk = name, + .offset = st->offset + }; + + st->entries = entries; + st->offset += t->sz; + p->stk = p->tk; + + if (p->proto) + fputc('\t', stderr); + + fprintf(stderr, "\tadding %s (%d:%d) to %s, type %s, offset %zu, " + "size %zu\n", name->s, loc->line, loc->col, st->name, tname, + entries[st->nentries - 1].offset, t->sz); + ret = 1; +end: + free(tname); + return ret; +} + +static int v(const struct lex *l, struct prv *p, const struct type *t) +{ + return stpush(l, p, p->id, t); +} + +static int id(const struct lex *l, struct prv *p) +{ + p->id = p->stk; + return type(l, p, v); +} + +void storage_free(struct storage *s) +{ + if (s) + free(s->entries); + + free(s); +} + +const struct stentry *storage_find(const char *name, const struct storage *s) +{ + for (size_t i = 0; i < s->nentries; i++) + { + const struct stentry *e = &s->entries[i]; + + if (!strcmp(name, e->tk->s)) + return e; + } + + return NULL; +} + +int storage(const struct lex *l, struct prv *p, const char *name, int check, + struct storage **out) +{ + struct storage *st = NULL; + struct pos init = + { + .seq = seqs, + .stseq = seqs, + .step = seqs->steps + }; + + if (*out) + { + const struct loc *loc = &(*out)->tk->loc; + + errloc(p->stk, "%s already defined at %s:%d:%d", name, loc->f, + loc->line, loc->col); + goto failure; + } + else if (!(st = malloc(sizeof *st))) + { + perror("malloc(3)"); + goto failure; + } + else if (push(&init, p)) + goto failure; + + *st = (struct storage){.name = name, .tk = p->stk, .check = check}; + *out = p->st = st; + p->stk = p->tk; + return 1; + +failure: + free(st); + return -1; +} |
