Handle struct initializers for local variables

This commit is contained in:
Rui Ueyama 2019-08-18 17:56:36 +09:00
parent 5b95533603
commit e9d2c46ab3
3 changed files with 85 additions and 4 deletions

View File

@ -243,6 +243,7 @@ struct Member {
Type *ty;
Token *tok; // for error message
Token *name;
int idx;
int offset;
};

68
parse.c
View File

@ -79,6 +79,7 @@ typedef struct InitDesg InitDesg;
struct InitDesg {
InitDesg *next;
int idx;
Member *member;
Obj *var;
};
@ -240,6 +241,20 @@ static Initializer *new_initializer(Type *ty, bool is_flexible) {
init->children = calloc(ty->array_len, sizeof(Initializer *));
for (int i = 0; i < ty->array_len; i++)
init->children[i] = new_initializer(ty->base, false);
return init;
}
if (ty->kind == TY_STRUCT) {
// Count the number of struct members.
int len = 0;
for (Member *mem = ty->members; mem; mem = mem->next)
len++;
init->children = calloc(len, sizeof(Initializer *));
for (Member *mem = ty->members; mem; mem = mem->next)
init->children[mem->idx] = new_initializer(mem->ty, false);
return init;
}
return init;
@ -667,7 +682,27 @@ static void array_initializer(Token **rest, Token *tok, Initializer *init) {
}
}
// initializer = string-initializer | array-initializer | assign
// struct-initializer = "{" initializer ("," initializer)* "}"
static void struct_initializer(Token **rest, Token *tok, Initializer *init) {
tok = skip(tok, "{");
Member *mem = init->ty->members;
while (!consume(rest, tok, "}")) {
if (mem != init->ty->members)
tok = skip(tok, ",");
if (mem) {
initializer2(&tok, tok, init->children[mem->idx]);
mem = mem->next;
} else {
tok = skip_excess_element(tok);
}
}
}
// initializer = string-initializer | array-initializer
// | struct-initializer | assign
static void initializer2(Token **rest, Token *tok, Initializer *init) {
if (init->ty->kind == TY_ARRAY && tok->kind == TK_STR) {
string_initializer(rest, tok, init);
@ -679,6 +714,11 @@ static void initializer2(Token **rest, Token *tok, Initializer *init) {
return;
}
if (init->ty->kind == TY_STRUCT) {
struct_initializer(rest, tok, init);
return;
}
init->expr = assign(rest, tok);
}
@ -693,6 +733,12 @@ static Node *init_desg_expr(InitDesg *desg, Token *tok) {
if (desg->var)
return new_var_node(desg->var, tok);
if (desg->member) {
Node *node = new_unary(ND_MEMBER, init_desg_expr(desg->next, tok), tok);
node->member = desg->member;
return node;
}
Node *lhs = init_desg_expr(desg->next, tok);
Node *rhs = new_num(desg->idx, tok);
return new_unary(ND_DEREF, new_add(lhs, rhs, tok), tok);
@ -709,6 +755,17 @@ static Node *create_lvar_init(Initializer *init, Type *ty, InitDesg *desg, Token
return node;
}
if (ty->kind == TY_STRUCT) {
Node *node = new_node(ND_NULL_EXPR, tok);
for (Member *mem = ty->members; mem; mem = mem->next) {
InitDesg desg2 = {desg, 0, mem};
Node *rhs = create_lvar_init(init->children[mem->idx], mem->ty, &desg2, tok);
node = new_binary(ND_COMMA, node, rhs, tok);
}
return node;
}
if (!init->expr)
return new_node(ND_NULL_EXPR, tok);
@ -728,7 +785,7 @@ static Node *create_lvar_init(Initializer *init, Type *ty, InitDesg *desg, Token
// x[1][1] = 9;
static Node *lvar_initializer(Token **rest, Token *tok, Obj *var) {
Initializer *init = initializer(rest, tok, var->ty, &var->ty);
InitDesg desg = {NULL, 0, var};
InitDesg desg = {NULL, 0, NULL, var};
// If a partial initializer list is given, the standard requires
// that unspecified elements are set to 0. Here, we simply
@ -1426,18 +1483,21 @@ static Node *unary(Token **rest, Token *tok) {
static void struct_members(Token **rest, Token *tok, Type *ty) {
Member head = {};
Member *cur = &head;
int idx = 0;
while (!equal(tok, "}")) {
Type *basety = declspec(&tok, tok, NULL);
int i = 0;
bool first = true;
while (!consume(&tok, tok, ";")) {
if (i++)
if (!first)
tok = skip(tok, ",");
first = false;
Member *mem = calloc(1, sizeof(Member));
mem->ty = declarator(&tok, tok, basety);
mem->name = mem->ty->name;
mem->idx = idx++;
cur = cur->next = mem;
}
}

View File

@ -35,6 +35,26 @@ int main() {
ASSERT(2, ({ typedef char T[]; T x="x"; T y="foo"; sizeof(x); }));
ASSERT(4, ({ typedef char T[]; T x="x"; T y="foo"; sizeof(y); }));
ASSERT(1, ({ struct {int a; int b; int c;} x={1,2,3}; x.a; }));
ASSERT(2, ({ struct {int a; int b; int c;} x={1,2,3}; x.b; }));
ASSERT(3, ({ struct {int a; int b; int c;} x={1,2,3}; x.c; }));
ASSERT(1, ({ struct {int a; int b; int c;} x={1}; x.a; }));
ASSERT(0, ({ struct {int a; int b; int c;} x={1}; x.b; }));
ASSERT(0, ({ struct {int a; int b; int c;} x={1}; x.c; }));
ASSERT(1, ({ struct {int a; int b;} x[2]={{1,2},{3,4}}; x[0].a; }));
ASSERT(2, ({ struct {int a; int b;} x[2]={{1,2},{3,4}}; x[0].b; }));
ASSERT(3, ({ struct {int a; int b;} x[2]={{1,2},{3,4}}; x[1].a; }));
ASSERT(4, ({ struct {int a; int b;} x[2]={{1,2},{3,4}}; x[1].b; }));
ASSERT(0, ({ struct {int a; int b;} x[2]={{1,2}}; x[1].b; }));
ASSERT(0, ({ struct {int a; int b;} x={}; x.a; }));
ASSERT(0, ({ struct {int a; int b;} x={}; x.b; }));
ASSERT(5, ({ typedef struct {int a,b,c,d,e,f;} T; T x={1,2,3,4,5,6}; T y; y=x; y.e; }));
ASSERT(2, ({ typedef struct {int a,b;} T; T x={1,2}; T y, z; z=y=x; z.b; }));
printf("OK\n");
return 0;
}