aboutsummaryrefslogtreecommitdiff
path: root/type.c
diff options
context:
space:
mode:
Diffstat (limited to 'type.c')
-rw-r--r--type.c459
1 files changed, 459 insertions, 0 deletions
diff --git a/type.c b/type.c
new file mode 100644
index 0000000..c7e43d0
--- /dev/null
+++ b/type.c
@@ -0,0 +1,459 @@
+#include "type.h"
+#include "errloc.h"
+#include "fn.h"
+#include "lex.h"
+#include "parse.h"
+#include "prv.h"
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int aetc(const struct lex *l, struct prv *p);
+static int aid(const struct lex *l, struct prv *p);
+static int anum(const struct lex *l, struct prv *p);
+static int p(const struct lex *l, struct prv *p);
+static int t(const struct lex *l, struct prv *p);
+
+static const struct seq seqs[] =
+{
+ {(struct step[])
+ {{ID, "array"}, {ID, "of"}, {ID, "etc"}, {0}}, .fn = aetc},
+ {(struct step[]){{ID, "array"}, {ID, "of"}, {NUM}, {0}}, .fn = anum},
+ {(struct step[]){{ID, "array"}, {ID, "of"}, {ID}, {0}}, .fn = aid},
+ {(struct step[]){{ID, "pointer"}, {ID, "to"}, {0}}, .fn = p},
+ {(struct step[]){{ID, "void"}, {0}}, .fn = t},
+ {(struct step[]){{ID, "byte"}, {0}}, .fn = t},
+ {(struct step[]){{ID, "halfword"}, {0}}, .fn = t},
+ {(struct step[]){{ID, "word"}, {0}}, .fn = t},
+ {(struct step[]){{ID, "long"}, {0}}, .fn = t},
+ {(struct step[]){{ID, "ubyte"}, {0}}, .fn = t},
+ {(struct step[]){{ID, "uhalfword"}, {0}}, .fn = t},
+ {(struct step[]){{ID, "uword"}, {0}}, .fn = t},
+ {(struct step[]){{ID, "ulong"}, {0}}, .fn = t},
+ {(struct step[]){{ID}, {0}}, .fn = t},
+ {0}
+};
+
+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:
+ break;
+
+ case P:
+ {
+ struct p *p = &t->u.p;
+
+ fn_free(p->fn);
+ free(p->fn);
+ }
+ break;
+
+ case T:
+ case ARY:
+ case PTR:
+ case BUILTIN:
+ break;
+ }
+}
+
+void type_lfree(struct type *t)
+{
+ if (t->child)
+ type_lfree(t->child);
+
+ free(t);
+}
+
+static int append(const char *s, char **out)
+{
+ size_t n = *out ? strlen(*out) : 0;
+ char *ns = realloc(*out, strlen(s) + n + 1);
+
+ if (!ns)
+ {
+ perror("realloc(3)");
+ return -1;
+ }
+
+ strcpy(ns + n, s);
+ *out = ns;
+ return 0;
+}
+
+char *type_name(const struct type *t)
+{
+ char *ret = NULL;
+
+ while (t)
+ {
+ int n = -1;
+
+ switch (t->type)
+ {
+ case BUILTIN:
+ n = append(t->u.b.name, &ret);
+ break;
+
+ case ARY:
+ {
+ const struct ary *ary = &t->u.ary;
+ char s[sizeof "18446744073709551615 "];
+
+ if (ary->etc)
+ snprintf(s, sizeof s, "etc ");
+ else
+ snprintf(s, sizeof s, "%llu ", t->u.ary.n);
+
+ n = append("array of ", &ret) || append(s, &ret);
+ break;
+ }
+
+ case PTR:
+ n = append("pointer to ", &ret);
+ break;
+
+ case U:
+ case S:
+ case C:
+ case P:
+ case T:
+ n = append(t->tk->s, &ret);
+ break;
+ }
+
+ if (n)
+ {
+ free(ret);
+ return NULL;
+ }
+
+ t = t->child;
+ }
+
+ return ret;
+}
+
+const struct type *type_ufind(const struct fn *fn, const char *s)
+{
+ static const struct type builtins[] =
+ {
+ {.type = BUILTIN, .u.b.name = "void"},
+ {.type = BUILTIN, .u.b.name = "byte", .sz = 1, .sign = 1, .align = 1},
+ {.type = BUILTIN, .u.b.name = "halfword", .sz = 2, .sign = 1, .align = 2},
+ {.type = BUILTIN, .u.b.name = "word", .sz = 4, .sign = 1, .align = 4},
+ {.type = BUILTIN, .u.b.name = "long", .sz = 8, .sign = 1, .align = 8},
+ {.type = BUILTIN, .u.b.name = "ubyte", .sz = 1, .align = 1},
+ {.type = BUILTIN, .u.b.name = "uhalfword", .sz = 2, .align = 2},
+ {.type = BUILTIN, .u.b.name = "uword", .sz = 4, .align = 4},
+ {.type = BUILTIN, .u.b.name = "ulong", .sz = 8, .align = 8}
+ };
+
+ const struct td *td = fn->td;
+
+ if (fn->parent)
+ {
+ const struct type *t = type_find(fn->parent, s);
+
+ if (t)
+ return t;
+ }
+
+ for (size_t i = 0; i < sizeof builtins / sizeof *builtins; i++)
+ {
+ const struct type *t = &builtins[i];
+
+ if (!strcmp(t->u.b.name, s))
+ return t;
+ }
+
+ if (td)
+ for (size_t i = 0; i < td->ntypes; i++)
+ {
+ const struct type *t = &td->types[i];
+
+ if (!strcmp(t->tk->s, s))
+ return t;
+ }
+
+ for (size_t i = 0; i < fn->nimps; i++)
+ {
+ const struct type *t = type_ufind(fn->imps[i].ast.fns, s);
+
+ if (t)
+ return t;
+ }
+
+ return NULL;
+}
+
+const struct type *type_find(const struct fn *fn, const char *s)
+{
+ const struct type *t = type_ufind(fn, s);
+
+ if (t && t->type == T)
+ return t->u.t.alias;
+
+ return t;
+}
+
+static int queue(struct prv *p, struct type *t)
+{
+ struct type *tt;
+ struct pos *pos = &p->pos[p->i];
+
+ if (!(tt = p->t))
+ p->t = t;
+ else
+ {
+ while (tt->child)
+ tt = tt->child;
+
+ tt->child = t;
+ }
+
+ pos->seq = pos->stseq;
+ pos->step = pos->seq->steps;
+ p->stk = p->tk;
+ return 1;
+}
+
+static int aetc(const struct lex *l, struct prv *p)
+{
+ struct type *t;
+ const struct type *tt = p->t;
+
+ while (tt)
+ {
+ if (tt->type == ARY && tt->u.ary.etc)
+ {
+ errloc(p->stk, "multi-dimensional arrays "
+ "with unknown size are not allowed");
+ return -1;
+ }
+
+ tt = tt->next;
+ }
+
+ if (!(t = malloc(sizeof *t)))
+ {
+ perror("malloc");
+ return -1;
+ }
+
+ *t = (struct type){.type = ARY, .u.ary.etc = 1};
+ return queue(p, t);
+}
+
+static int aid(const struct lex *l, struct prv *p)
+{
+ const struct tk *qty = p->stk + 2;
+ const struct type *ct = type_find(fn_cur(p), qty->s);
+ struct type *t;
+ unsigned long long v;
+
+ if (!ct)
+ {
+ errloc(qty, "undefined reference to type \"%s\"", qty->s);
+ return -1;
+ }
+ else if (ct->type != C)
+ {
+ errloc(qty, "type \"%s\" not a constant", qty->s);
+ return -1;
+ }
+ else if (ct->sign)
+ {
+ errloc(qty, "type \"%s\" defines a negative constant (%lld)",
+ qty->s, ct->u.c.v);
+ return -1;
+ }
+ else if (!(v = ct->u.c.uv))
+ {
+ errloc(qty, "constant \"%s\" equals zero and cannot be used "
+ "for array sizes", qty->s);
+ return -1;
+ }
+ else if (!(t = malloc(sizeof *t)))
+ {
+ perror("malloc");
+ return -1;
+ }
+
+ *t = (struct type){.type = ARY, .u.ary.n = v};
+ return queue(p, t);
+}
+
+static int anum(const struct lex *l, struct prv *p)
+{
+ const struct tk *qty = p->stk + 2;
+ unsigned long long n = strtoull(qty->s, NULL, 0);
+ struct type *t;
+
+ if (*qty->s == '-' || errno)
+ {
+ errloc(qty, "invalid negative size for array");
+ return -1;
+ }
+ else if (!n)
+ {
+ errloc(qty, "array size cannot be zero");
+ return -1;
+ }
+ else if (!(t = malloc(sizeof *t)))
+ {
+ perror("malloc");
+ return -1;
+ }
+
+ *t = (struct type){.type = ARY, .u.ary.n = n};
+ return queue(p, t);
+}
+
+static int p(const struct lex *l, struct prv *p)
+{
+ struct type *t = malloc(sizeof *t), *tt;
+
+ if (!t)
+ {
+ perror("malloc");
+ return -1;
+ }
+
+ *t = (struct type)
+ {
+ .type = PTR,
+ .sz = sizeof (void *),
+ .align = sizeof (void *)
+ };
+
+ if ((tt = p->t) && tt->type == ARY)
+ {
+ tt->sz = tt->u.ary.n * sizeof (void *);
+ tt->align = sizeof (void *);
+ }
+
+ return queue(p, t);
+}
+
+static int aryeq(const struct type *a, const struct type *b)
+{
+ const struct ary *aa = &a->u.ary, *ab = &b->u.ary;
+
+ return !memcmp(aa, ab, sizeof *aa);
+}
+
+static int eq(const struct type *a, const struct type *b)
+{
+ for (; a && b; a = a->child, b = b->child)
+ if (a->type != b->type)
+ return 0;
+ else if (a->type == ARY && !aryeq(a, b))
+ return 0;
+
+ return !a && !b;
+}
+
+static const struct type *insert(struct prv *p)
+{
+ struct ast *ast = p->ast;
+ struct type *a = p->t, *tt = ast->types;
+
+ for (const struct type *b = ast->types; b; b = b->next)
+ if (eq(a, b))
+ {
+ type_lfree(a);
+ return b;
+ }
+
+ if (!ast->types)
+ ast->types = a;
+ else
+ {
+ while (tt->next)
+ tt = tt->next;
+
+ tt->next = a;
+ }
+
+ return a;
+}
+
+static int t(const struct lex *l, struct prv *p)
+{
+ const struct fn *fn = fn_cur(p);
+ const struct tk *tk = p->stk;
+ const struct type *t = type_find(fn, tk->s);
+ struct type *tt = p->t;
+
+ if (!t)
+ {
+ errloc(tk, "undefined reference to type \"%s\"", tk->s);
+ return -1;
+ }
+ else if (tt)
+ {
+ struct type *nt = malloc(sizeof *nt);
+
+ if (!nt)
+ {
+ perror("malloc(3)");
+ return -1;
+ }
+
+ *nt = *t;
+
+ while (tt->child)
+ tt = tt->child;
+
+ tt->child = nt;
+ tt = p->t;
+
+ /* array size still undecided */
+ if (tt->type == ARY && !tt->sz)
+ {
+ tt->sz = tt->u.ary.n * nt->sz;
+ tt->align = t->align;
+ }
+
+ if (!(t = insert(p)))
+ return -1;
+
+ p->t = NULL;
+ }
+
+ return p->type(l, p, t);
+}
+
+int type(const struct lex *l, struct prv *p,
+ int (*fn)(const struct lex *, struct prv *, const struct type *))
+{
+ struct pos init =
+ {
+ .seq = seqs,
+ .stseq = seqs,
+ .step = seqs->steps
+ };
+
+ p->stk = p->tk;
+ p->type = fn;
+ return push(&init, p) ? -1 : 1;
+}