aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorlemon <lsof@mailbox.org>2023-06-04 13:46:02 +0200
committerlemon <lsof@mailbox.org>2023-06-04 13:46:02 +0200
commitae8f6c54bc8dc2a439bff83b590481427c9ed58d (patch)
treeb01930e457cbada7cd37783d385afa32c0f10d92
parentd8f63a7c8f9ec6c1213e219d4b6d5d30ce595cdd (diff)
enums
-rw-r--r--parse.c54
-rw-r--r--test.c8
2 files changed, 57 insertions, 5 deletions
diff --git a/parse.c b/parse.c
index dc1b3e5..1081fca 100644
--- a/parse.c
+++ b/parse.c
@@ -63,11 +63,14 @@ enum declkind {
struct decl {
union type ty;
uchar scls;
- uchar qual;
- ushort align;
+ uchar qual : 2;
+ uchar isenum : 1;
struct span span;
const char *name;
- int id;
+ union {
+ struct { ushort align; int id; };
+ vlong value;
+ };
};
struct declstate {
@@ -713,6 +716,8 @@ Unary:
} else if (decl->scls == SCTYPEDEF) {
error(&tk.span, "unexpected typename %'tk (expected expression)", &tk);
ex = mkexpr(ESYM, tk.span, decl->ty, .sym = NULL);
+ } else if (decl->isenum) {
+ ex = mkexpr(ENUMLIT, tk.span, decl->ty, .i = decl->value);
} else {
ex = mkexpr(ESYM, tk.span, decl->ty, .qual = decl->qual, .sym = decl);
}
@@ -1902,11 +1907,45 @@ static union type
buildenum(struct parser *pr, const char *name)
{
union type t;
- struct typedata td = {TYENUM};
+ struct token tk;
enum typetag backing = TYINT;
-
+ struct typedata td = {TYENUM, .backing = backing};
+ vlong iota = 0,
+ min = -(1ll << (8*targ_primsizes[TYINT] - 1)),
+ max = (1ll << (8*targ_primsizes[TYINT] - 1)) - 1;
t = mktagtype(name, &td);
t.backing = backing;
+
+ while (!match(pr, &tk, '}')) {
+ struct decl decl = {0};
+ peek(pr, &tk);
+ expect(pr, TKIDENT, NULL);
+ if (match(pr, NULL, '=') || (peek(pr, NULL) == TKNUMLIT && !expect(pr, '=', NULL))) {
+ struct expr ex = expr(pr);
+ if (eval(&ex, EVINTCONST)) {
+ iota = ex.i;
+ } else {
+ error(&ex.span, "enum value is not an integer constant");
+ }
+ } else if (tk.t != TKIDENT) {
+ lex(pr, NULL);
+ continue;
+ }
+ if (iota < min || iota > max)
+ warn(&tk.span, "enumerator value %ld doesn't fit in `int', will be silently truncated", iota);
+
+ decl.name = tk.s;
+ decl.ty = t;
+ decl.isenum = 1;
+ decl.value = iota++;
+ putdecl(pr, &decl);
+ if (!match(pr, &tk, ',')) {
+ if (expect(pr, '}', "or `,'"))
+ break;
+ else lex(pr, NULL);
+ }
+ }
+
return t;
}
@@ -1929,6 +1968,11 @@ tagtype(struct parser *pr, enum toktag kind)
return mktype(0);
}
t = gettagged(pr, &span, tt, tag, /* def? */ peek(pr, NULL) == ';');
+ if (!t.t) {
+ assert(tt == TYENUM);
+ error(&span, "cannot forward-declare enum");
+ return mktype(TYINT);
+ }
} else {
if (tt != TYENUM) {
if (tag) {
diff --git a/test.c b/test.c
index 388f9f7..a86f2d2 100644
--- a/test.c
+++ b/test.c
@@ -71,6 +71,14 @@ void fill(char *p, int c, unsigned long n)
void zero(void *p, unsigned long n) { fill(p,0,n); }
+enum ball {
+ Zero,
+ Two = 2,
+ Three,
+ One = Zero + 1,
+ W = 1l<<44
+};
+
main(t) {
putc(t + 1, t + 2);
}