aboutsummaryrefslogtreecommitdiff
path: root/bootstrap/parse.c
diff options
context:
space:
mode:
authorlemon <lsof@mailbox.org>2022-08-07 22:36:57 +0200
committerlemon <lsof@mailbox.org>2022-08-07 22:36:57 +0200
commit96ef1857bac446f4e065e7c10530213e361d726a (patch)
tree2c5241e4f7cf03d3498ff3114ca315ea076c855c /bootstrap/parse.c
parente305f71c3cbd685eacb77451b3728b2cac492d71 (diff)
add tagged unions
Diffstat (limited to 'bootstrap/parse.c')
-rw-r--r--bootstrap/parse.c108
1 files changed, 87 insertions, 21 deletions
diff --git a/bootstrap/parse.c b/bootstrap/parse.c
index 2b44ca8..963b986 100644
--- a/bootstrap/parse.c
+++ b/bootstrap/parse.c
@@ -1091,17 +1091,34 @@ pexprimary(struct parser *P) {
int i;
enumlit:
vname = (tok = lexexpect(P, TKident)).str;
- if (!(ex.ty = P->targty) || ex.ty->t != TYenum)
+ if (!(ex.ty = P->targty) || (ex.ty->t != TYenum && ex.ty->t != TYeunion))
fatal(P, tok.span, "cannot infer type for enum literal `:%s'", vname);
- for (i = 0; i < ex.ty->enu.vals.n; ++i)
- if (!strcmp(ex.ty->enu.vals.d[i].name, vname))
- goto found;
- fatal(P, tok.span, "enum %t contains no variant `%s'", ex.ty, vname);
- found:
- P->used_targty = 1;
- ex.t = Eenumval;
- ex.enu.vname = vname;
- ex.enu.i = ex.ty->enu.vals.d[i].i;
+ if (ex.ty->t == TYenum) {
+ for (i = 0; i < ex.ty->enu.vals.n; ++i)
+ if (!strcmp(ex.ty->enu.vals.d[i].name, vname))
+ goto found;
+ fatal(P, tok.span, "enum %t contains no variant `%s'", ex.ty, vname);
+ found:
+ P->used_targty = 1;
+ ex.t = Eenumval;
+ ex.span = tok.span;
+ ex.enu.vname = vname;
+ ex.enu.i = ex.ty->enu.vals.d[i].i;
+ } else {
+ const struct aggfield *fld;
+ for (i = 0; i < ex.ty->agg.flds.n; ++i)
+ if (!strcmp((fld = &ex.ty->agg.flds.d[i])->name, vname))
+ goto found1;
+ fatal(P, tok.span, "tagged union %t contains no variant `%s'", ex.ty, vname);
+ found1:
+ P->used_targty = 1;
+ ex.t = Eeuini;
+ ex.span = tok.span;
+ ex.euini.fnam = fld->name;
+ ex.euini.tag = ex.ty->agg.enumty->enu.vals.d[i].i;
+ if (fld->ty)
+ ex.euini.ini = exprdup(parseexpr(P));
+ }
} else if (lexmatch(P, &tok, '{')) {
aggini:
P->used_targty = 1;
@@ -2408,6 +2425,7 @@ parseenum(struct parser *P, const char *name) {
ty.size = ty.enu.intty->size;
ty.align = ty.enu.intty->align;
+
ty.enu.name = name;
ty.enu.id = id++;
vec_slice_cpy(&ty.enu.vals, &vals);
@@ -2439,6 +2457,7 @@ parseagg(struct parser *P, const char *name, int kind, struct decl **retdecl) {
const struct type *pty = NULL;
static int id = 0;
size_t size = 0, align = 1;
+ i64 f0align = -1;
bool decls = 0;
vec_t(struct aggfield) flds = {0};
@@ -2471,23 +2490,31 @@ parseagg(struct parser *P, const char *name, int kind, struct decl **retdecl) {
}
const char *fnam = (tok = lexexpect(P, TKident)).str;
- const struct type *ty = parsetype(P);
- size_t off = kind == TYunion ? 0 : ALIGNUP(size, ty->align);
+ const struct type *ty = NULL;
+ size_t off = size;
+ if (kind != TYeunion || ((tok = lexpeek(P)).t != ',' && tok.t != '}' && !isdecltokt(tok.t))) {
+ ty = parsetype(P);
+ if (f0align < 0)
+ f0align = ty->align;
+ off = kind != TYstruct ? 0 : ALIGNUP(size, ty->align);
+ }
int i; struct aggfield fld;
vec_foreach(&flds, fld, i)
if (!strcmp(fnam, fld.name))
fatal(P, tok.span, "duplicate field %T", tok);
- if (!completetype(ty))
+ if (ty && !completetype(ty))
fatal(P, tok.span, "aggregate field `%s' is of incomplete type (%t)",
fnam, ty);
- align = MAX(align, ty->align);
- if (kind == TYstruct)
- size = off + ty->size;
- else
- size = MAX(size, ty->size);
+ if (ty) {
+ align = MAX(align, ty->align);
+ if (kind == TYstruct)
+ size = off + ty->size;
+ else
+ size = MAX(size, ty->size);
+ }
vec_push(&flds, ((struct aggfield) {
off, fnam, ty
@@ -2498,10 +2525,43 @@ parseagg(struct parser *P, const char *name, int kind, struct decl **retdecl) {
break;
}
}
+
struct type *ppty = (struct type *)pty;
ppty->size = ALIGNUP(size, align);
ppty->align = align;
vec_slice_cpy(&ppty->agg.flds, &flds);
+
+ if (kind == TYeunion) {
+ struct type enumty = {TYenum, .enu.id = id++};
+ int n = flds.length;
+
+ enumty.enu.name = name;
+ enumty.enu.vals.d = xmalloc(n * sizeof(struct enumval));
+ enumty.enu.vals.n = n;
+ if (n < 256)
+ enumty.enu.intty = ty_u8;
+ else if (n < 65536)
+ enumty.enu.intty = ty_u16;
+ else assert(0);
+ enumty.size = enumty.enu.intty->size;
+ enumty.align = enumty.enu.intty->align;
+
+ int i; struct aggfield fld;
+ vec_foreach(&flds, fld, i) {
+ struct enumval *ev = &enumty.enu.vals.d[i];
+ ev->name = fld.name;
+ ev->i = i;
+ }
+ ppty->agg.enumty = interntype(enumty);
+
+ size_t off = f0align < 0 ? 0 : ALIGNUP(enumty.size, f0align);
+ ppty->size += off;
+ align = MAX(align, enumty.align);
+
+ for (int i = 0; i < ppty->agg.flds.n; ++i)
+ ppty->agg.flds.d[i].off += off;
+ }
+
if (!decls)
return pty;
else {
@@ -2701,10 +2761,16 @@ parsedecl(decl_yielder_t yield, void *yarg, struct parser *P, bool toplevel) {
decl.macro = parsemacro(P);
decl.macro.name = name;
} else if (lexmatch(P, &tok, TKkw_enum)) {
- if (externp) fatal(P, tok.span, "enum cannot be `extern'");
decl.t = Dtype;
- decl.name = lexexpects(P, TKident, "enum name").str;
- decl.ty = parseenum(P, decl.name);
+ if (lexmatch(P, &tok, TKkw_union)) {
+ if (externp) fatal(P, tok.span, "enum union cannot be `extern'");
+ kind = TYeunion;
+ goto agg;
+ } else {
+ if (externp) fatal(P, tok.span, "enum cannot be `extern'");
+ decl.name = lexexpects(P, TKident, "enum name").str;
+ decl.ty = parseenum(P, decl.name);
+ }
} else if (lexmatch(P, &tok, TKkw_struct)) {
kind = TYstruct;
if (externp) fatal(P, tok.span, "struct cannot be `extern'");