#include "c.h"

static char rcsid[] = "$Id: init.nw,v 2.4 1997/05/10 00:53:49 drh Exp $";

static int curseg;              /* current segment */

/* defpointer - initialize a pointer to p or to 0 if p==0 */
void defpointer(Symbol p) {
        if (p) {
                (*IR->defaddress)(p);
                p->ref++;
        } else {
                static Value v;
                (*IR->defconst)(P, voidptype->size, v);
        }
}

/* genconst - generate/check constant expression e; return size */
static int genconst(Tree e, int def) {
        for (;;)
                switch (generic(e->op)) {
                case ADDRG:
                        if (def)
                                (*IR->defaddress)(e->u.sym);
                        return e->type->size;
                case CNST:
                        if (e->op == CNST+P && isarray(e->type)) {
                                e = cvtconst(e);
                                continue;
                        }
                        if (def)
                                (*IR->defconst)(e->type->op, e->type->size, e->u.v);
                        return e->type->size;
                case RIGHT:
                        assert(e->kids[0] || e->kids[1]);
                        if (e->kids[1] && e->kids[0])
                                error("initializer must be constant\n");
                        e = e->kids[1] ? e->kids[1] : e->kids[0];
                        continue;
                case CVP:
                        if (isarith(e->type))
                                error("cast from `%t' to `%t' is illegal in constant expressions\n",
                                        e->kids[0]->type, e->type);
                        /* fall thru */
                case CVI: case CVU: case CVF:
                        e = e->kids[0];
                        continue;
                default:
                        error("initializer must be constant\n");
                        if (def)
                                genconst(consttree(0, inttype), def);
                        return inttype->size;
                }
}

/* initvalue - evaluate a constant expression for a value of integer type ty */
static Tree initvalue(Type ty) {
        Type aty;
        Tree e;

        needconst++;
        e = expr1(0);
        if ((aty = assign(ty, e)) != NULL)
                e = cast(e, aty);
        else {
                error("invalid initialization type; found `%t' expected `%t'\n",
                        e->type,  ty);
                e = retype(consttree(0, inttype), ty);
        }
        needconst--;
        if (generic(e->op) != CNST) {
                error("initializer must be constant\n");
                e = retype(consttree(0, inttype), ty);
        }
        return e;
}

/* initarray - initialize array of ty of <= len bytes; if len == 0, go to } */
static int initarray(int len, Type ty, int lev) {
        int n = 0;

        do {
                initializer(ty, lev);
                n += ty->size;
                if (len > 0 && n >= len || t != ',')
                        break;
                t = gettok();
        } while (t != '}');
        return n;
}

/* initchar - initialize array of <= len ty characters; if len == 0, go to } */
static int initchar(int len, Type ty) {
        int n = 0;
        char buf[16], *s = buf;

        do {
                *s++ = initvalue(ty)->u.v.i;
                if (++n%inttype->size == 0) {
                        (*IR->defstring)(inttype->size, buf);
                        s = buf;
                }
                if (len > 0 && n >= len || t != ',')
                        break;
                t = gettok();
        } while (t != '}');
        if (s > buf)
                (*IR->defstring)(s - buf, buf);
        return n;
}

/* initend - finish off an initialization at level lev; accepts trailing comma */
static void initend(int lev, char follow[]) {
        if (lev == 0 && t == ',')
                t = gettok();
        test('}', follow);
}

/* initfields - initialize <= an unsigned's worth of bit fields in fields p to q */
static int initfields(Field p, Field q) {
        unsigned int bits = 0;
        int i, n = 0;

        do {
                i = initvalue(inttype)->u.v.i;
                if (fieldsize(p) < 8*p->type->size) {
                        if (p->type == inttype &&
                           (i < -(int)(fieldmask(p)>>1)-1 || i > (int)(fieldmask(p)>>1))
                        ||  p->type == unsignedtype && (i&~fieldmask(p)) !=  0)
                                warning("initializer exceeds bit-field width\n");
                        i &= fieldmask(p);
                }
                bits |= i<<fieldright(p);
                if (IR->little_endian) {
                        if (fieldsize(p) + fieldright(p) > n)
                                n = fieldsize(p) + fieldright(p);
                } else {
                        if (fieldsize(p) + fieldleft(p) > n)
                                n = fieldsize(p) + fieldleft(p);
                }
                if (p->link == q)
                        break;
                p = p->link;
        } while (t == ',' && (t = gettok()) != 0);
        n = (n + 7)/8;
        for (i = 0; i < n; i++) {
                Value v;
                if (IR->little_endian) {
                        v.u = (unsigned char)bits;
                        bits >>= 8;
                } else {        /* a big endian */
                        v.u = (unsigned char)(bits>>(8*(unsignedtype->size - 1)));
                        bits <<= 8;
                }
                (*IR->defconst)(U, unsignedchar->size, v);
        }
        return n;
}

/* initstruct - initialize a struct ty of <= len bytes; if len == 0, go to } */
static int initstruct(int len, Type ty, int lev) {
        int a, n = 0;
        Field p = ty->u.sym->u.s.flist;

        do {
                if (p->offset > n) {
                        (*IR->space)(p->offset - n);
                        n += p->offset - n;
                }
                if (p->lsb) {
                        Field q = p;
                        while (q->link && q->link->offset == p->offset)
                                q = q->link;
                        n += initfields(p, q->link);
                        p = q;
                } else {
                        initializer(p->type, lev);
                        n += p->type->size;
                }
                if (p->link) {
                        p = p->link;
                        a = p->type->align;
                } else
                        a = ty->align;
                if (a && n%a) {
                        (*IR->space)(a - n%a);
                        n = roundup(n, a);
                }
                if (len > 0 && n >= len || t != ',')
                        break;
                t = gettok();
        } while (t != '}');
        return n;
}

/* initializer - constexpr | { constexpr ( , constexpr )* [ , ] } */
Type initializer(Type ty, int lev) {
        int n = 0;
        Tree e;
        Type aty = NULL;
        static char follow[] = { IF, CHAR, STATIC, 0 };

        ty = unqual(ty);
        if (isscalar(ty)) {
                needconst++;
                if (t == '{') {
                        t = gettok();
                        e = expr1(0);
                        initend(lev, follow);
                } else
                        e = expr1(0);
                e = pointer(e);
                if ((aty = assign(ty, e)) != NULL)
                        e = cast(e, aty);
                else
                        error("invalid initialization type; found `%t' expected `%t'\n",
                                e->type, ty);
                n = genconst(e, 1);
                deallocate(STMT);
                needconst--;
        }
        if ((isunion(ty) || isstruct(ty)) && ty->size == 0) {
                static char follow[] = { CHAR, STATIC, 0 };
                error("cannot initialize undefined `%t'\n", ty);
                skipto(';', follow);
                return ty;
        } else if (isunion(ty)) {
                if (t == '{') {
                        t = gettok();
                        n = initstruct(ty->u.sym->u.s.flist->type->size, ty, lev + 1);
                        initend(lev, follow);
                } else {
                        if (lev == 0)
                                error("missing { in initialization of `%t'\n", ty);
                        n = initstruct(ty->u.sym->u.s.flist->type->size, ty, lev + 1);
                }
        } else if (isstruct(ty)) {
                if (t == '{') {
                        t = gettok();
                        n = initstruct(0, ty, lev + 1);
                        test('}', follow);
                } else if (lev > 0)
                        n = initstruct(ty->size, ty, lev + 1);
                else {
                        error("missing { in initialization of `%t'\n", ty);
                        n = initstruct(ty->u.sym->u.s.flist->type->size, ty, lev + 1);
                }
        }
        if (isarray(ty))
                aty = unqual(ty->type);
        if (isarray(ty) && ischar(aty)) {
                if (t == SCON) {
                        if (ty->size > 0 && ty->size == tsym->type->size - 1)
                                tsym->type = array(chartype, ty->size, 0);
                        n = tsym->type->size;
                        (*IR->defstring)(tsym->type->size, tsym->u.c.v.p);
                        t = gettok();
                } else if (t == '{') {
                        t = gettok();
                        if (t == SCON) {
                                ty = initializer(ty, lev + 1);
                                initend(lev, follow);
                                return ty;
                        }
                        n = initchar(0, aty);
                        test('}', follow);
                } else if (lev > 0 && ty->size > 0)
                        n = initchar(ty->size, aty);
                else {  /* eg, char c[] = 0; */
                        error("missing { in initialization of `%t'\n", ty);
                        n = initchar(1, aty);
                }
        } else if (isarray(ty)) {
                if (t == '{') {
                        t = gettok();
                        n = initarray(0, aty, lev + 1);
                        test('}', follow);
                } else if (lev > 0 && ty->size > 0)
                        n = initarray(ty->size, aty, lev + 1);
                else {
                        error("missing { in initialization of `%t'\n", ty);
                        n = initarray(aty->size, aty, lev + 1);
                }
        }       
        if (ty->size) {
                if (n > ty->size)
                        error("too many initializers\n");
                else if (n < ty->size)
                        (*IR->space)(ty->size - n);
        } else if (isarray(ty) && ty->type->size > 0)
                ty = array(ty->type, n/ty->type->size, 0);
        else
                ty->size = n;
        return ty;
}

/* swtoseg - switch to segment seg, if necessary */
void swtoseg(int seg) {
        if (curseg != seg)
                (*IR->segment)(seg);
        curseg = seg;
}
