diff --git a/parse.c b/parse.c index ce97ba160189e4e6222073b83fef81c9abff6fb3..3bebce4fed9b7206ba4ae68e392ff0cb2e114dbe 100644 --- a/parse.c +++ b/parse.c @@ -117,6 +117,7 @@ static Type *type_suffix(Token **rest, Token *tok, Type *ty); static Type *declarator(Token **rest, Token *tok, Type *ty); static Node *declaration(Token **rest, Token *tok, Type *basety, VarAttr *attr); static void array_initializer2(Token **rest, Token *tok, Initializer *init, int i); +static void struct_initializer2(Token **rest, Token *tok, Initializer *init, Member *mem); static void initializer2(Token **rest, Token *tok, Initializer *init); static Initializer *initializer(Token **rest, Token *tok, Type *ty, Type **new_ty); static Node *lvar_initializer(Token **rest, Token *tok, Obj *var); @@ -866,6 +867,12 @@ static void string_initializer(Token **rest, Token *tok, Initializer *init) { // int x[5][10] = { [5][8]=1, 2, 3 }; // // It sets x[5][8], x[5][9] and x[6][0] to 1, 2 and 3, respectively. +// +// Use `.fieldname` to move the cursor for a struct initializer. E.g. +// +// struct { int a, b, c; } x = { .c=5 }; +// +// The above initializer sets x.c to 5. static int array_designator(Token **rest, Token *tok, Type *ty) { Token *start = tok; int i = const_expr(&tok, tok->next); @@ -875,7 +882,23 @@ static int array_designator(Token **rest, Token *tok, Type *ty) { return i; } -// designation = ("[" const-expr "]")* "="? initializer +// struct-designator = "." ident +static Member *struct_designator(Token **rest, Token *tok, Type *ty) { + tok = skip(tok, "."); + if (tok->kind != TK_IDENT) + error_tok(tok, "expected a field designator"); + + for (Member *mem = ty->members; mem; mem = mem->next) { + if (mem->name->len == tok->len && !strncmp(mem->name->loc, tok->loc, tok->len)) { + *rest = tok->next; + return mem; + } + } + + error_tok(tok, "struct has no such member"); +} + +// designation = ("[" const-expr "]" | "." ident)* "="? initializer static void designation(Token **rest, Token *tok, Initializer *init) { if (equal(tok, "[")) { if (init->ty->kind != TY_ARRAY) @@ -886,6 +909,17 @@ static void designation(Token **rest, Token *tok, Initializer *init) { return; } + if (equal(tok, ".") && init->ty->kind == TY_STRUCT) { + Member *mem = struct_designator(&tok, tok, init->ty); + designation(&tok, tok, init->children[mem->idx]); + init->expr = NULL; + struct_initializer2(rest, tok, init, mem->next); + return; + } + + if (equal(tok, ".")) + error_tok(tok, "field name not in struct or union initializer"); + if (equal(tok, "=")) tok = tok->next; initializer2(rest, tok, init); @@ -967,7 +1001,7 @@ static void array_initializer2(Token **rest, Token *tok, Initializer *init, int if (i > 0) tok = skip(tok, ","); - if (equal(tok, "[")) { + if (equal(tok, "[") || equal(tok, ".")) { *rest = start; return; } @@ -982,10 +1016,19 @@ static void struct_initializer1(Token **rest, Token *tok, Initializer *init) { tok = skip(tok, "{"); Member *mem = init->ty->members; + bool first = true; while (!consume_end(rest, tok)) { - if (mem != init->ty->members) + if (!first) tok = skip(tok, ","); + first = false; + + if (equal(tok, ".")) { + mem = struct_designator(&tok, tok, init->ty); + designation(&tok, tok, init->children[mem->idx]); + mem = mem->next; + continue; + } if (mem) { initializer2(&tok, tok, init->children[mem->idx]); @@ -997,13 +1040,21 @@ static void struct_initializer1(Token **rest, Token *tok, Initializer *init) { } // struct-initializer2 = initializer ("," initializer)* -static void struct_initializer2(Token **rest, Token *tok, Initializer *init) { +static void struct_initializer2(Token **rest, Token *tok, Initializer *init, Member *mem) { bool first = true; - for (Member *mem = init->ty->members; mem && !is_end(tok); mem = mem->next) { + for (; mem && !is_end(tok); mem = mem->next) { + Token *start = tok; + if (!first) tok = skip(tok, ","); first = false; + + if (equal(tok, "[") || equal(tok, ".")) { + *rest = start; + return; + } + initializer2(&tok, tok, init->children[mem->idx]); } *rest = tok; @@ -1054,7 +1105,7 @@ static void initializer2(Token **rest, Token *tok, Initializer *init) { return; } - struct_initializer2(rest, tok, init); + struct_initializer2(rest, tok, init, init->ty->members); return; } diff --git a/test/initializer.c b/test/initializer.c index c7d3ed456da91d23cd630b62dbcfaf13d0d17d9d..d6529661644bae5b57ccbb1a52fa26257c3000bb 100644 --- a/test/initializer.c +++ b/test/initializer.c @@ -216,6 +216,36 @@ int main() { ASSERT(7, ((int[10]){ [3] 7 })[3]); ASSERT(0, ((int[10]){ [3] 7 })[4]); + ASSERT(4, ({ struct { int a,b; } x={1,2,.b=3,.a=4}; x.a; })); + ASSERT(3, ({ struct { int a,b; } x={1,2,.b=3,.a=4}; x.b; })); + + ASSERT(1, ({ struct { struct { int a,b; } c; } x={.c=1,2}; x.c.a; })); + ASSERT(2, ({ struct { struct { int a,b; } c; } x={.c=1,2}; x.c.b; })); + + ASSERT(0, ({ struct { struct { int a,b; } c; } x={.c.b=1}; x.c.a; })); + ASSERT(1, ({ struct { struct { int a,b; } c; } x={.c.b=1}; x.c.b; })); + + ASSERT(1, ({ struct { int a[2]; } x={.a=1,2}; x.a[0]; })); + ASSERT(2, ({ struct { int a[2]; } x={.a=1,2}; x.a[1]; })); + + ASSERT(0, ({ struct { int a[2]; } x={.a[1]=1}; x.a[0]; })); + ASSERT(1, ({ struct { int a[2]; } x={.a[1]=1}; x.a[1]; })); + + ASSERT(3, ({ struct { int a,b; } x[]={[1].b=1,2,[0]=3,4,}; x[0].a; })); + ASSERT(4, ({ struct { int a,b; } x[]={[1].b=1,2,[0]=3,4,}; x[0].b; })); + ASSERT(0, ({ struct { int a,b; } x[]={[1].b=1,2,[0]=3,4,}; x[1].a; })); + ASSERT(1, ({ struct { int a,b; } x[]={[1].b=1,2,[0]=3,4,}; x[1].b; })); + ASSERT(2, ({ struct { int a,b; } x[]={[1].b=1,2,[0]=3,4,}; x[2].a; })); + ASSERT(0, ({ struct { int a,b; } x[]={[1].b=1,2,[0]=3,4,}; x[2].b; })); + + ASSERT(1, ({ typedef struct { int a,b; } T; T x={1,2}; T y[]={x}; y[0].a; })); + ASSERT(2, ({ typedef struct { int a,b; } T; T x={1,2}; T y[]={x}; y[0].b; })); + ASSERT(0, ({ typedef struct { int a,b; } T; T x={1,2}; T y[]={x, [0].b=3}; y[0].a; })); + ASSERT(3, ({ typedef struct { int a,b; } T; T x={1,2}; T y[]={x, [0].b=3}; y[0].b; })); + + ASSERT(5, ((struct { int a,b,c; }){ .c=5 }).c); + ASSERT(0, ((struct { int a,b,c; }){ .c=5 }).a); + printf("OK\n"); return 0; }