%{
#include "cc.h"
%}
%union  {
       Node*   node;
       Sym*    sym;
       Ref*    ref;
       Type*   type;
       struct {
               Type*   t;
               char    c;
       } tycl;
       struct {
               Type*   t1;
               Type*   t2;
       } tyty;
       struct {
               Sym*    sym;
               Ref*    ref;
       } syrf;
       long    lval;
       double  dval;
       char*   sval;
       ushort* rval;
}
%type   <syrf>  ltag
%type   <lval>  tname tnlist
%type   <type>  tlist sbody complex
%type   <tycl>  types
%type   <node>  zarglist arglist zcexpr
%type   <node>  name block stmnt cexpr expr xuexpr pexpr
%type   <node>  zelist elist adecl slist uexpr
%type   <node>  xdecor xdecor2 labels label ulstmnt
%type   <node>  adlist edecor tag qual
%type   <node>  abdecor abdecor1 abdecor2 abdecor3
%type   <node>  zexpr lexpr init ilist lcexpr

%left   ';'
%left   ','
%right  '=' LPE LME LMLE LDVE LMDE LRSHE LLSHE LANDE LXORE LORE
%right  '?' ':'
%left   LOROR
%left   LANDAND
%left   '|'
%left   '^'
%left   '&'
%left   LEQ LNE
%left   '<' '>' LLE LGE
%left   LLSH LRSH
%left   '+' '-'
%left   '*' '/' '%'
%right  LMM LPP LMG '.' '[' '('

%token  <sym>   LNAME LTYPE
%token  <dval>  LFCONST LVLCONST LDCONST
%token  <lval>  LCONST LLCONST LUCONST LULCONST
%token  <sval>  LSTRING
%token  <rval>  LLSTRING
%token          LAUTO LBREAK LCASE LCHAR LCONTINUE LDEFAULT LDO
%token          LDOUBLE LELSE LEXTERN LFLOAT LFOR LGOTO
%token  LIF LINT LLONG LREGISTER LRETURN LSHORT LSIZEOF LUSED
%token  LSTATIC LSTRUCT LSWITCH LTYPEDEF LUNION LUNSIGNED LWHILE
%token  LVOID LENUM LSIGNED LCONSTNT LVOLATILE LSET
%%
prog:
|       prog xdecl

/*
* external declarator
*/
xdecl:
       zctlist ';'
       {
               dodecl(xdecl, lastclass, lasttype, Z);
       }
|       zctlist xdlist ';'
|       zctlist xdecor
       {
               lastdcl = T;
               firstarg = S;
               dodecl(xdecl, lastclass, lasttype, $2);
               if(lastdcl == T || lastdcl->etype != TFUNC) {
                       diag($2, "not a function");
                       lastdcl = types[TFUNC];
               }
               thisfn = lastdcl;
               markdcl();
               firstdcl = dclstack;
               argmark($2, 0);
       }
       pdecl
       {
               argmark($2, 1);
       }
       block
       {
               revertdcl();
               if(!debug['H'])
                       codgen($6, $2);
       }

xdlist:
       xdecor
       {
               dodecl(xdecl, lastclass, lasttype, $1);
       }
|       xdecor
       {
               $1 = dodecl(xdecl, lastclass, lasttype, $1);    /* dirty use of $1 */
       }
       '=' init
       {
               doinit($1->sym, $1->type, 0L, $4);
       }
|       xdlist ',' xdlist

xdecor:
       xdecor2
|       '*' garbage xdecor
       {
               $$ = new(OIND, $3, Z);
       }

xdecor2:
       tag
|       '(' xdecor ')'
       {
               $$ = $2;
       }
|       xdecor2 '(' zarglist ')'
       {
               $$ = new(OFUNC, $1, $3);
       }
|       xdecor2 '[' zexpr ']'
       {
               $$ = new(OARRAY, $1, $3);
       }

/*
* automatic declarator
*/
adecl:
       {
               $$ = Z;
       }
|       adecl ctlist ';'
       {
               $$ = dodecl(adecl, lastclass, lasttype, Z);
               if($1 != Z)
                       if($$ != Z)
                               $$ = new(OLIST, $1, $$);
                       else
                               $$ = $1;
       }
|       adecl ctlist adlist ';'
       {
               $$ = $1;
               if($3 != Z) {
                       $$ = $3;
                       if($1 != Z)
                               $$ = new(OLIST, $1, $3);
               }
       }

adlist:
       xdecor
       {
               dodecl(adecl, lastclass, lasttype, $1);
               $$ = Z;
       }
|       xdecor
       {
               $1 = dodecl(adecl, lastclass, lasttype, $1);
       }
       '=' init
       {
               long w;

               w = $1->sym->type->width;
               $$ = doinit($1->sym, $1->type, 0L, $4);
               $$ = contig($1->sym, $$, w);
       }
|       adlist ',' adlist
       {
               $$ = $1;
               if($3 != Z) {
                       $$ = $3;
                       if($1 != Z)
                               $$ = new(OLIST, $1, $3);
               }
       }

/*
* parameter declarator
*/
pdecl:
|       pdecl ctlist pdlist ';'

pdlist:
       xdecor
       {
               dodecl(pdecl, lastclass, lasttype, $1);
       }
|       pdlist ',' pdlist

/*
* structure element declarator
*/
edecl:
       tlist
       {
               lasttype = $1;
       }
       zedlist ';'
|       edecl tlist
       {
               lasttype = $2;
       }
       zedlist ';'

zedlist:                                        /* extension */
       {
               edecl(CXXX, lasttype, S);
       }
|       edlist

edlist:
       edecor
       {
               dodecl(edecl, CXXX, lasttype, $1);
       }
|       edlist ',' edlist

edecor:
       xdecor
       {
               lastbit = 0;
               firstbit = 1;
       }
|       tag ':' lexpr
       {
               $$ = new(OBIT, $1, $3);
       }
|       ':' lexpr
       {
               $$ = new(OBIT, Z, $2);
       }

/*
* abstract declarator
*/
abdecor:
       {
               $$ = (Z);
       }
|       abdecor1

abdecor1:
       '*' garbage
       {
               $$ = new(OIND, (Z), Z);
       }
|       '*' garbage abdecor1
       {
               $$ = new(OIND, $3, Z);
       }
|       abdecor2

abdecor2:
       abdecor3
|       abdecor2 '(' zarglist ')'
       {
               $$ = new(OFUNC, $1, $3);
       }
|       abdecor2 '[' zexpr ']'
       {
               $$ = new(OARRAY, $1, $3);
       }

abdecor3:
       '(' ')'
       {
               $$ = new(OFUNC, (Z), Z);
       }
|       '[' zexpr ']'
       {
               $$ = new(OARRAY, (Z), $2);
       }
|       '(' abdecor1 ')'
       {
               $$ = $2;
       }

init:
       expr
|       '{' ilist '}'
       {
               $$ = new(OINIT, invert($2), Z);
       }

qual:
       '[' lexpr ']'
       {
               $$ = new(OARRAY, $2, Z);
       }
|       '.' tag
       {
               $$ = new(ODOT, $2, Z);
       }
|       '[' lexpr ']' qual
       {
               $$ = new(OARRAY, $2, Z);
               $$ = new(OLIST, $$, $4);
       }
|       '.' tag qual
       {
               $$ = new(ODOT, $2, Z);
               $$ = new(OLIST, $$, $3);
       }

ilist:
       init
|       qual init
       {
               $$ = new(OLIST, $1, $2);
       }
|       qual '=' init
       {
               $$ = new(OLIST, $1, $3);
       }
|       ilist ',' init
       {
               $$ = new(OLIST, $1, $3);
       }
|       ilist ',' qual init
       {
               $$ = new(OLIST, $1, $3);
               $$ = new(OLIST, $$, $4);
       }
|       ilist ',' qual '=' init
       {
               $$ = new(OLIST, $1, $3);
               $$ = new(OLIST, $$, $5);
       }
|       ilist ','

zarglist:
       {
               $$ = Z;
       }
|       arglist
       {
               $$ = invert($1);
       }


arglist:
       name
|       tlist abdecor
       {
               $$ = new(OPROTO, $2, Z);
               $$->type = $1;
       }
|       tlist xdecor
       {
               $$ = new(OPROTO, $2, Z);
               $$->type = $1;
       }
|       '.' '.' '.'
       {
               $$ = new(ODOTDOT, Z, Z);
       }
|       arglist ',' arglist
       {
               $$ = new(OLIST, $1, $3);
       }

block:
       '{' adecl slist '}'
       {
               $$ = invert($3);
               if($2 != Z)
                       $$ = new(OLIST, $2, $$);
       }

slist:
       {
               $$ = Z;
       }
|       slist stmnt
       {
               $$ = new(OLIST, $1, $2);
       }

labels:
       label
|       labels label
       {
               $$ = new(OLIST, $1, $2);
       }

label:
       LCASE expr ':'
       {
               $$ = new(OCASE, $2, Z);
       }
|       LDEFAULT ':'
       {
               $$ = new(OCASE, Z, Z);
       }
|       LNAME ':'
       {
               $$ = new(OLABEL, dcllabel($1, 1), Z);
               if($1->ref)
                       $1->ref->class = CLABEL+CLAST;
       }

stmnt:
       error ';'
       {
               $$ = Z;
       }
|       ulstmnt
|       labels ulstmnt
       {
               $$ = new(OLIST, $1, $2);
       }

ulstmnt:
       zcexpr ';'
|       {
               markdcl();
       }
       block
       {
               revertdcl();
               $$ = $2;
       }
|       LIF '(' cexpr ')' stmnt
       {
               $$ = new(OIF, $3, new(OLIST, $5, Z));
       }
|       LIF '(' cexpr ')' stmnt LELSE stmnt
       {
               $$ = new(OIF, $3, new(OLIST, $5, $7));
       }
|       LFOR '(' zcexpr ';' zcexpr ';' zcexpr ')' stmnt
       {
               $$ = new(OFOR, new(OLIST, $5, new(OLIST, $3, $7)), $9);
       }
|       LWHILE '(' cexpr ')' stmnt
       {
               $$ = new(OWHILE, $3, $5);
       }
|       LDO stmnt LWHILE '(' cexpr ')' ';'
       {
               $$ = new(ODWHILE, $5, $2);
       }
|       LRETURN zcexpr ';'
       {
               $$ = new(ORETURN, $2, Z);
               $$->type = thisfn->link;
       }
|       LSWITCH '(' lcexpr ')' stmnt
       {
               $$ = new(OSWITCH, $3, $5);
       }
|       LBREAK ';'
       {
               $$ = new(OBREAK, Z, Z);
       }
|       LCONTINUE ';'
       {
               $$ = new(OCONTINUE, Z, Z);
       }
|       LGOTO LNAME ';'
       {
               $$ = new(OGOTO, dcllabel($2, 0), Z);
               if($2->ref)
                       $2->ref->class = CLABEL;
       }
|       LUSED '(' zelist ')' ';'
       {
               $$ = new(OUSED, $3, Z);
       }
|       LSET '(' zelist ')' ';'
       {
               $$ = new(OSET, $3, Z);
       }

zcexpr:
       {
               $$ = Z;
       }
|       cexpr

zexpr:
       {
               $$ = Z;
       }
|       lexpr

lexpr:
       expr
       {
               $$ = new(OCAST, $1, Z);
               $$->type = types[TLONG];
       }

lcexpr:
       cexpr
       {
               $$ = new(OCAST, $1, Z);
               $$->type = types[TLONG];
       }

cexpr:
       expr
|       cexpr ',' cexpr
       {
               $$ = new(OCOMMA, $1, $3);
       }

expr:
       xuexpr
|       expr '*' expr
       {
               $$ = new(OMUL, $1, $3);
       }
|       expr '/' expr
       {
               $$ = new(ODIV, $1, $3);
       }
|       expr '%' expr
       {
               $$ = new(OMOD, $1, $3);
       }
|       expr '+' expr
       {
               $$ = new(OADD, $1, $3);
       }
|       expr '-' expr
       {
               $$ = new(OSUB, $1, $3);
       }
|       expr LRSH expr
       {
               $$ = new(OASHR, $1, $3);
       }
|       expr LLSH expr
       {
               $$ = new(OASHL, $1, $3);
       }
|       expr '<' expr
       {
               $$ = new(OLT, $1, $3);
       }
|       expr '>' expr
       {
               $$ = new(OGT, $1, $3);
       }
|       expr LLE expr
       {
               $$ = new(OLE, $1, $3);
       }
|       expr LGE expr
       {
               $$ = new(OGE, $1, $3);
       }
|       expr LEQ expr
       {
               $$ = new(OEQ, $1, $3);
       }
|       expr LNE expr
       {
               $$ = new(ONE, $1, $3);
       }
|       expr '&' expr
       {
               $$ = new(OAND, $1, $3);
       }
|       expr '^' expr
       {
               $$ = new(OXOR, $1, $3);
       }
|       expr '|' expr
       {
               $$ = new(OOR, $1, $3);
       }
|       expr LANDAND expr
       {
               $$ = new(OANDAND, $1, $3);
       }
|       expr LOROR expr
       {
               $$ = new(OOROR, $1, $3);
       }
|       expr '?' cexpr ':' expr
       {
               $$ = new(OCOND, $1, new(OLIST, $3, $5));
       }
|       expr '=' expr
       {
               $$ = new(OAS, $1, $3);
       }
|       expr LPE expr
       {
               $$ = new(OASADD, $1, $3);
       }
|       expr LME expr
       {
               $$ = new(OASSUB, $1, $3);
       }
|       expr LMLE expr
       {
               $$ = new(OASMUL, $1, $3);
       }
|       expr LDVE expr
       {
               $$ = new(OASDIV, $1, $3);
       }
|       expr LMDE expr
       {
               $$ = new(OASMOD, $1, $3);
       }
|       expr LLSHE expr
       {
               $$ = new(OASASHL, $1, $3);
       }
|       expr LRSHE expr
       {
               $$ = new(OASASHR, $1, $3);
       }
|       expr LANDE expr
       {
               $$ = new(OASAND, $1, $3);
       }
|       expr LXORE expr
       {
               $$ = new(OASXOR, $1, $3);
       }
|       expr LORE expr
       {
               $$ = new(OASOR, $1, $3);
       }

xuexpr:
       uexpr
|       '(' tlist abdecor ')' xuexpr
       {
               $$ = new(OCAST, $5, Z);
               dodecl(NODECL, CXXX, $2, $3);
               $$->type = lastdcl;
       }
|       '(' tlist abdecor ')' '{' ilist '}'     /* extension */
       {
               $$ = new(OSTRUCT, $6, Z);
               dodecl(NODECL, CXXX, $2, $3);
               $$->type = lastdcl;
       }

uexpr:
       pexpr
|       '*' xuexpr
       {
               $$ = new(OIND, $2, Z);
       }
|       '&' xuexpr
       {
               $$ = new(OADDR, $2, Z);
       }
|       '+' xuexpr
       {
               $$ = new(OCONST, Z, Z);
               $$->offset = 0;
               $$->type = tint;
               $2 = new(OSUB, $$, $2);

               $$ = new(OCONST, Z, Z);
               $$->offset = 0;
               $$->type = tint;
               $$ = new(OSUB, $$, $2);
       }
|       '-' xuexpr
       {
               $$ = new(OCONST, Z, Z);
               $$->offset = 0;
               $$->type = tint;
               $$ = new(OSUB, $$, $2);
       }
|       '!' xuexpr
       {
               $$ = new(ONOT, $2, Z);
       }
|       '~' xuexpr
       {
               $$ = new(OCONST, Z, Z);
               $$->offset = -1;
               $$->type = tint;
               $$ = new(OXOR, $$, $2);
       }
|       LPP xuexpr
       {
               $$ = new(OPREINC, $2, Z);
       }
|       LMM xuexpr
       {
               $$ = new(OPREDEC, $2, Z);
       }
|       LSIZEOF uexpr
       {
               $$ = new(OSIZE, $2, Z);
       }

pexpr:
       '(' cexpr ')'
       {
               $$ = $2;
       }
|       LSIZEOF '(' tlist abdecor ')'
       {
               $$ = new(OSIZE, Z, Z);
               dodecl(NODECL, CXXX, $3, $4);
               $$->type = lastdcl;
       }
|       pexpr '(' zelist ')'
       {
               $$ = new(OFUNC, $1, Z);
               if($1->op == ONAME)
               if($1->type == T)
                       dodecl(xdecl, CXXX, tint, $$);
               $$->right = invert($3);
       }
|       pexpr '[' cexpr ']'
       {
               $$ = new(OIND, new(OADD, $1, $3), Z);
       }
|       pexpr LMG ltag
       {
               $$ = new(ODOT, new(OIND, $1, Z), Z);
               $$->sym = $3.sym;
               if($3.ref) {
                       $3.ref->class = CSELEM;
                       $$->ref = $3.ref;
               }
       }
|       pexpr '.' ltag
       {
               $$ = new(ODOT, $1, Z);
               $$->sym = $3.sym;
               if($3.ref) {
                       $3.ref->class = CSELEM;
                       $$->ref = $3.ref;
               }
       }
|       pexpr LPP
       {
               $$ = new(OPOSTINC, $1, Z);
       }
|       pexpr LMM
       {
               $$ = new(OPOSTDEC, $1, Z);
       }
|       name
|       LCONST
       {
               $$ = new(OCONST, Z, Z);
               $$->type = tint;
               $$->offset = $1;
       }
|       LLCONST
       {
               $$ = new(OCONST, Z, Z);
               $$->type = types[TLONG];
               $$->offset = $1;
       }
|       LUCONST
       {
               $$ = new(OCONST, Z, Z);
               $$->type = tuint;
               $$->offset = $1;
       }
|       LULCONST
       {
               $$ = new(OCONST, Z, Z);
               $$->type = types[TULONG];
               $$->offset = $1;
       }
|       LDCONST
       {
               $$ = new(OCONST, Z, Z);
               $$->type = types[TDOUBLE];
               $$->ud = $1;
       }
|       LFCONST
       {
               $$ = new(OCONST, Z, Z);
               $$->type = types[TFLOAT];
               $$->ud = $1;
       }
|       LVLCONST
       {
               $$ = new(OCONST, Z, Z);
               $$->type = types[TVLONG];
               $$->ud = $1;
       }
|       LSTRING
       {
               $$ = new(OSTRING, Z, Z);
               $$->us = $1;
               $$->sym = symstring;
               $$->type = typ(TARRAY, types[TCHAR]);
               $$->etype = TARRAY;
               $$->type->width = lnstring;
               $$->class = CSTATIC;
       }
|       LLSTRING
       {
               $$ = new(OLSTRING, Z, Z);
               $$->rs = $1;
               $$->sym = symstring;
               $$->type = typ(TARRAY, types[TUSHORT]);
               $$->etype = TARRAY;
               $$->type->width = lnstring;
               $$->class = CSTATIC;
       }

zelist:
       {
               $$ = Z;
       }
|       elist

elist:
       expr
|       elist ',' elist
       {
               $$ = new(OLIST, $1, $3);
       }

sbody:
       '{'
       {
               $<tyty>$.t1 = strf;
               $<tyty>$.t2 = strl;
               strf = T;
               strl = T;
               lastbit = 0;
               firstbit = 1;
       }
       edecl '}'
       {
               $$ = strf;
               strf = $<tyty>2.t1;
               strl = $<tyty>2.t2;
       }

zctlist:
       {
               lastclass = CXXX;
               lasttype = tint;
       }
|       ctlist

types:
       complex
       {
               $$.t = $1;
               $$.c = CXXX;
       }
|       complex tnlist
       {
               $$.t = $1;
               $$.c = simplec($2);
               if($2 & ~BCLASS)
                       diag(Z, "illegal combination of types 1: %Q/%T", $2, $1);
       }
|       tnlist
       {
               $$.t = simplet($1);
               $$.c = simplec($1);
       }
|       tnlist complex
       {
               $$.t = $2;
               $$.c = simplec($1);
               if($1 & ~BCLASS)
                       diag(Z, "illegal combination of types 1: %Q/%T", $1, $2);
       }
|       tnlist complex tnlist
       {
               $$.t = $2;
               $$.c = simplec($1|$3);
               if(($1|$3) & ~BCLASS)
                       diag(Z, "illegal combination of types 1: %Q/%T", $1|$3, $2);
       }

tlist:
       types
       {
               $$ = $1.t;
               if($1.c != CXXX)
                       diag(Z, "illegal combination of class 3: %s", cnames[$1.c]);
       }

ctlist:
       types
       {
               lasttype = $1.t;
               lastclass = $1.c;
       }

complex:
       LSTRUCT ltag
       {
               dotag($2.sym, TSTRUCT, 0);
               $$ = $2.sym->suetag;
               if($2.ref)
                       $2.ref->class = CSUETAG;
       }
|       LSTRUCT ltag
       {
               dotag($2.sym, TSTRUCT, autobn);
               if($2.ref)
                       $2.ref->class = CSUETAG+CLAST;
       }
       sbody
       {
               $$ = $2.sym->suetag;
               if($$->link != T)
                       diag(Z, "redeclare tag: %s", $2.sym->name);
               $$->link = $4;
               suallign($$);
               dbgprint($2.sym, $$);
       }
|       LSTRUCT sbody
       {
               $$ = typ(TSTRUCT, $2);
               suallign($$);
       }
|       LUNION ltag
       {
               dotag($2.sym, TUNION, 0);
               $$ = $2.sym->suetag;
               if($2.ref)
                       $2.ref->class = CSUETAG;
       }
|       LUNION ltag
       {
               dotag($2.sym, TUNION, autobn);
               if($2.ref)
                       $2.ref->class = CSUETAG+CLAST;
       }
       sbody
       {
               $$ = $2.sym->suetag;
               if($$->link != T)
                       diag(Z, "redeclare tag: %s", $2.sym->name);
               $$->link = $4;
               suallign($$);
               dbgprint($2.sym, $$);
       }
|       LUNION sbody
       {
               $$ = typ(TUNION, $2);
               suallign($$);
       }
|       LENUM ltag
       {
               dotag($2.sym, TENUM, 0);
               $$ = $2.sym->suetag;
               if($$->link == T)
                       $$->link = tint;
               $$ = $$->link;
               if($2.ref)
                       $2.ref->class = CSUETAG;
       }
|       LENUM ltag
       {
               dotag($2.sym, TENUM, autobn);
               if($2.ref)
                       $2.ref->class = CSUETAG+CLAST;
       }
       '{'
       {
               lastenum = 0;
               maxenum = 0;
       }
       enum '}'
       {
               $$ = $2.sym->suetag;
               if($$->link != T)
                       diag(Z, "redeclare tag: %s", $2.sym->name);
               $$->link = maxtype(maxenum);
               $$ = $$->link;
       }
|       LENUM '{'
       {
               lastenum = 0;
               maxenum = 0;
       }
       enum '}'
       {
               $$ = maxtype(maxenum);
       }
|       LTYPE
       {
               $$ = tcopy($1->type);
               if($1->ref)
                       $1->ref->class = CTYPEDEF;
       }

tnlist:
       tname
|       tnlist tname
       {
               $$ = $1 | $2;
               if($1 & $2)
                       if(($1 & $2) == BLONG)
                               $$ |= BVLONG;           /* long long => vlong */
                       else
                               diag(Z, "once is enough: %Q", $1 & $2);
       }


enum:
       LNAME
       {
               doenum($1, Z);
               if($1->ref) {
                       $1->ref->class = CENUM+CLAST;
                       $1->varlineno = $1->ref->lineno;
               }
       }
|       LNAME
       {
               if($1->ref) {
                       $1->ref->class = CENUM+CLAST;
                       $1->varlineno = $1->ref->lineno;
               }
       }
       '=' expr
       {
               doenum($1, $4);
       }
|       enum ','
|       enum ',' enum

tname:
       LCHAR { $$ = BCHAR; }   /* type words */
|       LSHORT { $$ = BSHORT; }
|       LINT { $$ = BINT; }
|       LLONG { $$ = BLONG; }
|       LSIGNED { $$ = BSIGNED; }
|       LUNSIGNED { $$ = BUNSIGNED; }
|       LFLOAT { $$ = BFLOAT; }
|       LDOUBLE { $$ = BDOUBLE; }
|       LVOID { $$ = BVOID; }

|       LAUTO { $$ = BAUTO; }   /* class words */
|       LSTATIC { $$ = BSTATIC; }
|       LEXTERN { $$ = BEXTERN; }
|       LTYPEDEF { $$ = BTYPEDEF; }
|       LREGISTER { $$ = BREGISTER; }

|       LCONSTNT { $$ = 0; }    /* noise words */
|       LVOLATILE { $$ = 0; }

garbage:
|       garbage LCONSTNT
|       garbage LVOLATILE

name:
       LNAME
       {
               $$ = new(ONAME, Z, Z);
               if($1->class == CLOCAL)
                       $1 = mkstatic($1);
               $$->sym = $1;
               $$->type = $1->type;
               $$->etype = TVOID;
               if($$->type != T)
                       $$->etype = $$->type->etype;
               $$->offset = $1->offset;
               $$->class = $1->class;
               $$->ref = $1->ref;
               $1->aused = 1;
               if($$->ref) {
                       $$->ref->class = $$->class;
                       $$->ref->dlineno = $1->varlineno;
               }
       }
tag:
       ltag
       {
               $$ = new(ONAME, Z, Z);
               $$->sym = $1.sym;
               $$->type = $1.sym->type;
               $$->etype = TVOID;
               if($$->type != T)
                       $$->etype = $$->type->etype;
               $$->offset = $1.sym->offset;
               $$->class = $1.sym->class;
               $$->ref = $1.ref;
       }
ltag:
       LNAME
       {
               $$.sym = $1;
               $$.ref = $1->ref;
       }
|       LTYPE
       {
               $$.sym = $1;
               $$.ref = $1->ref;
       }
%%