/*ident "@(#)ctrans:src/lalex.c 1.15" */ /************************************************************************** C++ source for cfront, the C++ compiler front-end written in the computer science research center of Bell Labs Copyright (c) 1984 AT&T, Inc. All Rights Reserved THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T, INC. lalex.c: lookahead *****************************************************************************/ #include <stdio.h> #ifdef c_plusplus overload is_empty; #endif #include "cfront.h" #include "yystype.h" #include "tqueue.h" #include "template.h" #ifdef DBG #define LDB(val,a) { if(Ldebug>=val) {a;} } #else #define LDB(val,a) /**/ #endif #ifdef DBG static char* image( int t ) { if(keys[t]) return keys[t]; else { static char b[20]; sprintf(b,"token(%d)",t); return b; } } static void printok( toknode* t ) { switch(t->tok) { default: fprintf(stderr,"\t%s",image(t->tok)); break; case ID: case ICON: case CCON: case FCON: case STRING: fprintf(stderr,"ID '%s'",t->retval.s); break; case TNAME: fprintf(stderr,"TNAME '%s'",t->retval.pn->string); break; case PTNAME: fprintf(stderr,"PTNAME '%s'",t->retval.pn->string); break; case TSCOPE: fprintf(stderr,"TSCOPE '%s'::",t->retval.pn->string); break; case MEMPTR: fprintf(stderr,"MEMPTR '%s'::*",t->retval.pn->string); break; } putc(' ',stderr); t->place.put(stderr); putc('\n',stderr); } static void showQ( char* where ) /* display token Q */ { fprintf(stderr,"TOKEN Q (%s):\n",where); for (register toknode* t = front; t; t = t->next) printok(t); putc('\n',stderr); } #endif int bl_level; static int laexpr( TOK ); static int latype( TOK ); static int la_decl(); static TOK lookahead(); /* make this a toknode! */ static int lasttk = 0; // one token history static YYSTYPE lastval; // yylval lasttk value int must_be_expr = 0; // handle redundant parentheses int must_be_id = 0; // !0, TNAME => ID, i.e., int X loc curloc; int curr_file; toknode* latok; // current lookahead token toknode* front = 0; toknode* rear = 0; const TQCHUNK = 16; void* toknode::operator new(size_t) { register toknode* p; if ((p=free_toks) == 0) { register toknode* q; free_toks = q = (toknode*)malloc( TQCHUNK * sizeof(toknode) ); p = free_toks; for (; q != &p[TQCHUNK-1]; q->next = q+1, ++q); q->next = 0; } free_toks = p->next; return p; } toknode::toknode(TOK t, YYSTYPE r, loc tloc) { tok = t; used = 0; retval = r; place = tloc; next = last = 0; } void toknode::operator delete(void* vp,size_t) { register toknode* p = (toknode*)vp; p->next = free_toks; free_toks = p; vp = 0; } #define USE_TOKEN(T,W) \ LDB(2,error('d',&(T)->place,"use_token('%k','%s')",(T)->tok,W);); \ if ( !(T)->used ) use_token(T); Ptype return_nstd_local_type( Pname n, TOK &sw ) { Ptype tt; switch ( n->tp->base ) { case EOBJ: case COBJ: tt = Pbase(n->tp)->b_name->tp; sw = n->tp->base; break; default: tt = n->tpdef; sw = NESTED; // in repr.c, prints ``typedef'' break; } return tt; } static Pname local_nested_kludge( Pname n, Pname tn ) /* * for backward compatibility with 2.0 * in transitional model of nested class types * * struct T { ... }; * foobar() { * class X { * typedef int T; * // ... * }; * T t; * } * * pure nested classes, choose global struct T{}; * no nested classes, choose typedef int T * transitional model: choose typedef, and generate warning * BUG: local typedefs and enums do not have lex_level set */ { // error( 'd', "local_nested_kludge: n: %n", n ); for ( Pname nn = n; nn; nn = nn->n_tbl_list ) { Pname local_class; TOK ntd; if ( nn->n_key != NESTED ) continue; Ptype tt = return_nstd_local_type(nn,ntd); Pclass cl = tt->in_class; while ( cl->in_class ) cl = cl->in_class; if (cl->lex_level && (local_class = ktbl->look(cl->string,LOCAL))) { // same typedef at nested and non-nested scope if (ntd == NESTED && tn && tn->tp == nn->tp ) ; else error('w',"%s occurs at outer and nested localC scope; using %k %t::%s", n->string,ntd,cl,n->string); /* if ( ntd == NESTED && nn->n_dcl_printed != 2 ) { nn->dcl_print(0); nn->n_dcl_printed = 2; } */ return nn; } } return 0; } enum { one_back, two_back }; static void use_token( toknode* T ) /* lookup TNAMEs here instead of in tlex() maintain block level */ { static TOK last_tokens[2]; // TSCOPE not reduced at this point static Pname last_tname; // tname :: id, where id is nested class static Pname tdef_name; // typedef tname tdef_name T->used = 1; DB(if(Ldebug>=1) { error('d',&T->place,"\n*** use_token(%k )",T->tok); printok(T); error('D',&T->place," lasttk%k last_tname%n last tokens%k%k",lasttk,last_tname,last_tokens[one_back],last_tokens[two_back]); }); switch ( T->tok ) { case ID: Pname n; // error('d', &T->place, "use_token: %s", T->retval.s ); if ( last_tokens[one_back] == MEM && last_tokens[two_back] == TNAME && (n=ktbl->look(T->retval.s,NESTED))) { // TYPEDEF :: ID, nested class ctor if (tdef_name && tdef_name->n_key==NESTED && strcmp(tdef_name->string,n->string)==0) { T->tok = TNAME; T->retval.pn = n; break; } else { // TNAME :: ID, where ID may be nested class for ( Pname nn = n; nn; nn = nn->n_tbl_list ) { TOK sw; if ( nn->n_key != NESTED ) continue; Ptype tt = return_nstd_local_type(nn,sw); Pclass cl = tt->in_class; if (strcmp(last_tname->string,cl->string)==0) { T->tok = TNAME; T->retval.pn = nn; break; } } } } else if ( bl_level && // TNAME:: and :: cannot refer to ``local'' TNAME last_tokens[one_back] != MEM && (n=ktbl->look(T->retval.s,LOCAL)) ) { DB( if(Ldebug>=1)error( 'd', &T->place, "use_token: local class instance: %n", n ) ); T->tok = TNAME; T->retval.pn = n; } else if ( n=ktbl->look(T->retval.s,0) ) { DB( if(Ldebug>=1)error( 'd', &T->place, "use_token:GC instance: %n", n ) ); // error( 'd', &T->place, "use_token:GC instance: %n %t", n, n->tp ); // X:: ?, then n cannot be a global TNAME // except in the case of a constructor if (last_tokens[one_back] == MEM && last_tokens[two_back] == TNAME && strcmp(T->retval.s,last_tname->string)) ; // do nothing; i.e., return ID else if ( bl_level && n->tp->base != COBJ && last_tokens[one_back] == MEM && last_tokens[two_back] != TNAME && gtbl->look(T->retval.s,0)) ; // do nothing: local typedefs & enums not implemented else { Pname nn = 0; TOK ntd; if (bl_level && (nn=ktbl->look(T->retval.s,NESTED))) { (void) return_nstd_local_type(n,ntd); nn = local_nested_kludge(nn,ntd==NESTED?n:0); } T->tok = TNAME; T->retval.pn = nn?nn:n; } } #ifdef DBG else if(Ldebug>=1) error('d',&T->place,"use_token: id %s",T->retval.s); #endif break; case LC: ++bl_level; break; case RC: --bl_level; break; } if (T->tok != COMPL || last_tokens[one_back] != MEM) { last_tokens[two_back] = last_tokens[one_back]; last_tokens[one_back] = T->tok; if (T->tok == TNAME) last_tname = T->retval.pn; if ( last_tname && last_tname->tp->base == TYPE ) { tdef_name = last_tname; do tdef_name = Pbase(tdef_name->tp)->b_name; while ( tdef_name->tp->base == TYPE ); } } } void addtok(TOK t, YYSTYPE r, loc tloc) { toknode* T = new toknode(t,r,tloc); if (front == 0) front = rear = T; else { rear->next = T; T->last = rear; rear = T; } //error('d',&tloc,"addtok: %k '%s'",t,t==ID?r.s:""); //showQ("addtok"); } extern TOK deltok( int noset = 0 ) { register toknode* T = front; USE_TOKEN(T,"deltok"); register TOK tk = T->tok; if ( !noset ) { yylval = T->retval; curloc = T->place; } curr_file = curloc.file; if (front = front->next) front->last = 0; else latok = rear = 0; delete T; return tk; } static void add_tokens() /* extend lookahead token queue when depleted */ { TOK tk = tlex(); if ( tk != ID ) return; while (tk == ID || tk == MEM || tk == DOT ) tk = tlex(); } extern TOK la_look() /* peek at head of token queue */ { LDB(1,fprintf(stderr,"\n*** la_look()")); if ( front == 0 ) add_tokens(); latok = front; USE_TOKEN(latok,"la_look"); LDB(1,fprintf(stderr," -- %s\n",image(latok->tok))); return latok->tok; } #define NEXTTOK() ( (yychar==-1) ? (yychar=lalex(),yychar) : yychar ) void check_decl() /* Lookahead to direct parsing of local/arg type declarations la_decl() returns 1 if lookahead sees a declaration. */ { TOK tk2; switch( NEXTTOK() ) { default: break; case TSCOPE: //XXX tk2 = la_look(); while ( tk2 == TSCOPE ) tk2 = lookahead(); if ( tk2 == TNAME ) { toknode* t = latok; if(t->tok!=TNAME) error('i',&t->place,"check_decl() token scan"); tk2 = lookahead(); if ( tk2 == LP && la_decl() ) { t->tok = DECL_MARKER; //TNAME } } break; case TYPE: case TNAME: if ( la_look() == LP && la_decl() ) { must_be_id = 0; DECL_TYPE=yychar; yychar = DECL_MARKER; } } } void check_cast() /* Lookahead to direct parsing of cast la_cast() returns 1 if lookahead sees an ambiguous old-style C cast. */ { TOK tk2; switch( NEXTTOK() ) { case TSCOPE: //XXX tk2 = la_look(); while ( tk2 == TSCOPE ) tk2 = lookahead(); if ( tk2 == TNAME ) { toknode* t = latok; if(t->tok!=TNAME) error('i',&t->place,"check_cast() token scan"); tk2 = lookahead(); if ( tk2 == LP && la_decl() ) { t->tok = DECL_MARKER;//TNAME } } break; case TYPE: case TNAME: if ( la_look() == LP && la_cast() ) { must_be_id = 0; DECL_TYPE = yychar; yychar = DECL_MARKER; } } } static int latype( TOK t ) { switch ( t ) { default: // includes friend, typedef, storage classes, etc. return 0; case CHAR: case SHORT: case INT: case LONG: case FLOAT: case DOUBLE: case UNSIGNED: return 1; } } static int laexpr( TOK t ) { switch ( t ) { default: return 0; case RETURN: case NEW: case AND: case ANDAND: case OR: case OROR: case SIZEOF: case NOT: case COMPL: case MUL: case PLUS: case MINUS: case ER: case ASSIGN: case ASOP: case RELOP: case EQUOP: case DIVOP: case SHIFTOP: case ICOP: return 1; } } static toknode *get_next_token(toknode *t) { if (! t->next) add_tokens() ; return t->next ; } static int template_tscope(Pname tn, toknode *lt) /* provide the looakhead for determining TSCOPE tokens when the name is a * parametrized type name; the lookahead here is non-trivial, because it * involves stepping over the template arguments. */ { int nest = 0 ; // the LT has been fetched if (lt->tok != LT) error ('i', "a `<' token was expected") ; // assume the worst, ensure that name strings are consed in the heap templp->parameters_in_progress++ ; for (toknode *t = lt;; t = get_next_token(t)) switch (t->tok) { case LT: ++nest; continue; case GT: // *************** // need to fold in awareness of x::y::z if (--nest == 0) { t = get_next_token(t); if (t->tok == MEM) { // determine whether it is a memptr if (t->next == 0) add_tokens(); if (t->next->tok == MUL) { t->tok = MEMPTR; t->next = t->next->next ; } else t->tok = TSCOPE ; t->retval.pn = tn ; --templp->parameters_in_progress; return 1; } else return 0 ; } continue; case SM: case LC: case RC: // a quick exit in case of error case EOFTOK: --templp->parameters_in_progress; return 0 ; default: continue; } } static TOK lookahead() /* advance lookahead pointer, lexing at end of Q handle occurrences of TNAME and TSCOPE (should be kept up to date with lalex()) */ { TOK tk; TOK tk2; TOK prev_tk = 0; YYSTYPE lastval; if ( latok == rear ) { add_tokens(); if ( latok ) latok = latok->next; else latok = front; } else latok = latok->next; if ( latok->last ) { prev_tk = latok->last->tok; lastval = latok->last->retval; } nexttok: USE_TOKEN(latok,"lookahead1"); tk = latok->tok; if ( tk == ID || tk == TNAME ) { if (latok->next == 0) add_tokens(); USE_TOKEN(latok->next,"lookahead2"); /* TOK */ tk2 = latok->next->tok; if ( tk == TNAME ) { if (tk2 == LT) { // a parametrized type name -- differentiate from TNAME // so that it can be dealt with in the grammar. if (template_tscope(latok->retval.pn, latok->next)) tk = PTNAME; } else if ( tk2 == MEM || tk2 == DOT ) { tscope: tk = TSCOPE; // error('d',"lookahead: tk: %k tk2: %k", tk, tk2 ); // XXX -- should be modified to loop and do lookup latok = latok->next; if (latok->next == 0) add_tokens(); USE_TOKEN(latok->next,"lookahead3"); tk2 = latok->next->tok; if ( tk2 == MUL ) { tk = MEMPTR; latok = latok->next; } } else if (( prev_tk == MUL && tk2 != RP ) || prev_tk == AND ) { tk = ID; latok->retval.pn->hide(); latok->tok = ID; latok->retval.s = latok->retval.pn->string; } } else if ( tk2 == MEM ) { // ID :: //XXX latok = latok->next->next; //XXX goto nexttok; goto tscope; // treat as tscope } if ( tk == ID && ( tk2 == ID || ( prev_tk == ID && ( tk2 == COLON || tk2 == LC )))) { // ID ID latok = latok->next; goto nexttok; } } if ( tk == ID ) { Pname nstd = ktbl->look(latok->retval.s,NESTED); if (nstd && (must_be_id == 0 || must_be_id && prev_tk == LP)) { extern Pname check_for_nested(Pname,TOK,YYSTYPE,TOK); // use this in lalex, too Pname n = check_for_nested(nstd,prev_tk,lastval,tk2); if ( n ) { tk = latok->tok = TNAME; latok->retval.pn = n; } } } return tk; } static Pname mem_sel = 0; static Pname do_local_class( Pname n, int lex_level ) { /* * modify to ``do_local_type: * do local types: enums and typedefs */ Pname nn = n; if ( n->tp ) { // already a TNAME Pclass cl = n->tp->base==COBJ ? Pclass(Pbase(n->tp)->b_name->tp) : 0; if ( n->lex_level != lex_level || (cl && cl->lcl) ) { local_hide( n ); nn = new name( n->string ); nn->lex_level = lex_level>=0?lex_level:0; } else if ( lex_level == n->lex_level && cl->defined ) { error( "localC %n redefined", n ); return n; } } nn = nn->tname( lastval.t ); modified_tn = modified_tn->l; nn->n_key = LOCAL; local_class = new name_list( nn, local_class ); local_blk = new name_list( nn, local_blk ); // error('d', "do_local_class: nn %n tp %t", nn, Pclass(Pbase(nn->tp)->b_name->tp)); return nn; } static char* make_nested_name( char *s, Pclass cl ) { // Q<cnt>_<class_names><space><null> const nested_depth = 9; char *str_arr[nested_depth]; int size_arr[nested_depth]; int cnt = 2; int size = 4; // Q,<cnt>,<_>,<null> str_arr[0] = s; str_arr[1] = cl->string; size += size_arr[0] = strlen(s); size += size_arr[1] = cl->strlen?cl->strlen:strlen(cl->string); for (Pclass nc = cl->in_class; nc; nc = nc->in_class ) { if (cnt > nested_depth-1) error('s',"nested depth class beyond %d unsupported",nested_depth); size += size_arr[cnt] = nc->strlen?nc->strlen:strlen(nc->string); str_arr[cnt++] = nc->string; } for ( int i=0; i<cnt; i++ ) // <nnn><string> size += size_arr[i]>99?3:size_arr[i]<10?1:2; //error('d', "make_nested_name( %s, %t ) cnt: %d size: %d", s, cl, cnt, size ); char *result = new char[size]; sprintf(result, "Q%d_", cnt ); size = 3; for ( i=cnt; i; i-- ) { sprintf(result+size,"%d%s", size_arr[i-1], str_arr[i-1]); size += size_arr[i-1] + (size_arr[i-1]>99?3:size_arr[i-1]<10?1:2); } //error('d', "size: %d ", size ); result[size] = '\0'; //error('d', "make_nested_name result: %s", result ); return result; } int is_empty( Pclass cl, bit const_chk ) { /* for nested class check, empty means *no* members * for const object check, means no *data* members */ // error('d',"%t->is_empty: max: %d real_size: %d", cl, cl->memtbl->max(),cl->real_size ); int mbr_cnt = cl->memtbl->max(); if ( mbr_cnt == 0 ) return 1; if ( cl->baselist == 0 && cl->real_size!=1 ) return 0; // empty class to turn on transitional nested class scope if ( const_chk == 0 && ( cl->baselist != 0 || mbr_cnt > 1 )) return 0; int i = 1; for (Pname nn=cl->memtbl->get_mem(i); nn; nn=cl->memtbl->get_mem(++i)) { if (nn->base==NAME && nn->n_union==0 && nn->tp->base!=FCT && nn->tp->base!=OVERLOAD && nn->tp->base!=CLASS && nn->tp->base!=ENUM && nn->tp->base!=EOBJ && nn->n_stclass != STATIC) { if ( nn->string[0]=='_' && nn->string[1]=='_' && nn->string[2]=='W' ) return 1; else return 0; } } return 1; // if here, no data members encountered } static int is_empty( Penum en ) { // is this an empty enum ?? // error('d', "%t no_of_enumerators: %d", en, en->no_of_enumerators); if ( en->no_of_enumerators != 0 ) return 0; return 1; } static Pname check_nested_type( Pname nm ) { // error('d', "check nested type: %n ccl: %t", nm, ccl ); Pname nx, n = ktbl->look(nm->string, NESTED); if ( n == 0 || n == nm ) return nm; int cnt = 1; for (nx = n; n; n=n->n_tbl_list ) if (n->n_key == NESTED) ++cnt; if ( cnt > 1 ) { error( "ambiguous nested type %s (%d instances), use x::y syntax", nm->string, cnt ); error( 'i', "cannot recover from previous errors" ); } else { TOK ntk; Ptype tt = return_nstd_local_type(nx,ntk); error('w', "use %t:: to access nested %k %s (anachronism)", tt->in_class, ntk, nx->string); } return nx; } static int in_local_class( Pclass cl ) { if ( cl->lex_level ) return 1; if ( cl->in_class ) return in_local_class( cl->in_class ); return 0; } Pname do_nested_type( Pname n ) { Pname nn = n; char *str = 0; // error('d', "do_nested_type: %s in_typedef: %d ccl: %t", n->string, in_typedef, ccl ); if ( in_typedef && ccl->string[0]=='_' && ccl->string[1]=='_' && ccl->string[2]=='C') return n; if ( n->tp ) { // already a TNAME : // hide existing instance, encode new instance /* * need handle the anomaly: * class x; * x *p; * class y { * class x{ ... }; // oops * }; */ if (n->tp->base==COBJ) { Pclass cl = Pclass(Pbase(n->tp)->b_name->tp); if (cl->defined == 0 && lasttk == AGGR) { error('w',"forwardD ofC%n resolved to nested%t::%s",n,ccl,n->string); cl->lcl = new char[9]; strcpy(cl->lcl,"FUDGE007"); // license to hack n->lex_level=Pbase(n->tp)->b_name->lex_level=0; return n; } } else if (n->tp->base==EOBJ) { // watch out for enum x; Penum en = Penum(Pbase(n->tp)->b_name->tp); if (en->defined == 0 && lasttk == ENUM) return n; } nested_hide( n ); nn = new name( n->string ); str = make_nested_name( n->string, ccl ); } else // make sure we haven't already seen a nested instance // if so, for transition, this needs to be an error if (ktbl->look( n->string, NESTED )) error("multiple type %s nestings (to do this define an empty class/enum %s {};)",n->string,n->string); if ( in_typedef ) { if (strcmp(ccl->string, nn->string)==0) { // class x { typedef T x; error( "nested Tdef %s redefines immediately enclosing class", nn->string ); error( 'i', "cannot recover from previous errors" ); } // make sure there isn't an identifier at global scope being defined // by a nested typedef -- previously an error; keep it so for transition Pname tn; if ( n->tp == 0 && in_local_class(ccl)==0 && (tn=gtbl->look(n->string,0))) { error( "nested Tdef %s redefinesG %n", n->string, tn ); error( 'i', "cannot recover from previous errors" ); } nn->tpdef = new type; nn->tpdef->nested_sig = str; nn->tpdef->in_class = ccl; nn->tpdef->lex_level = NESTED; PERM(nn->tpdef); } else { nn = nn->tname( lastval.t ); Pname tn = Pbase(nn->tp)->b_name; Ptype tt = tn->tp; if ( tt->defined && tt->in_class == ccl) { error( "nested %t redefines immediately enclosing class", nn->string ); error( 'i', "cannot recover from previous errors" ); } tt->nested_sig = str; modified_tn = modified_tn->l; nn->lex_level = tn->lex_level = 0; nested_type = new name_list( nn, nested_type ); } nn->n_key = NESTED; return nn; } static Pname dtor_seen; static int in_expr; extern int in_sizeof; extern TOK lalex() /* return next token to grammar */ { register TOK tk; if ( front == 0 ) add_tokens(); // extend lookahead queue LDB(1,fprintf(stderr,"\n*** lalex()\n");showQ("before")); gettok: tk = deltok(); // error('d',&curloc,"lalex: just got %k '%s' in_typedef: %d",tk,tk==ID?yylval.s:tk==TNAME?yylval.pn->string:"", in_typedef); if ( tk == ID || tk == TNAME ) { TOK tk2 = la_look(); int lex_level = bl_level - in_class_decl - (tk2 == LC ); if ( tk == TNAME ) { //error('d', "lalex tname %n; lasttk: %k tk2: %k", yylval.pn, lasttk, tk2); //error('d', " must_be_id: %d must_be_expr %d decl_type %d",must_be_id,must_be_expr,DECL_TYPE); //error('d', " bl_level: %d parsing_members %d",bl_level,parsing_class_members); if ( tk2 == LP && (bl_level == 0 || parsing_class_members) && ( laexpr(lasttk) == 0 ) && must_be_expr == 0 && DECL_TYPE == 0 ) { if (la_decl()) { must_be_id = 0; DECL_TYPE = tk; tk = DECL_MARKER; goto ret; } } // note: *** can handle local typedefs here, too! if ( in_typedef && in_typedef->base != 0 && ccl && in_mem_fct == 0 && (tk2 == SM || tk2 == RP || tk2 == LB)) yylval.pn = do_nested_type(yylval.pn); if ( lasttk == AGGR || lasttk == ENUM ) { if ( tk2 == LC || tk2 == COLON ) { // class definition if ( lex_level && (in_class_decl==0 || in_mem_fct) && lasttk != ENUM ) // temporary yylval.pn = do_local_class( yylval.pn, lex_level ); else if ( in_class_decl && ccl ) yylval.pn = do_nested_type( yylval.pn ); } } if (tk2 == LT) { // a parametrized type name if (template_tscope(yylval.pn,latok)) tk = PTNAME ; } else if ( tk2 == MEM || (tk2 == DOT && mem_sel == 0 )) { if (tk2==DOT) error(strict_opt?0:'w',"``.'' used for qualification; please use ``::'' (anachronism)"); crunch: tk = TSCOPE; {//XXX -- should be modified to do lookup and del at each :: while ( (tk2 = lookahead()) == TSCOPE ) ; if ( tk2 == TNAME ) { tk2 = lookahead(); if ( tk2 == LP && (bl_level == 0 || parsing_class_members) && ( laexpr(lasttk) == 0 ) && must_be_expr == 0 && DECL_TYPE == 0 ) { if (la_decl()) { must_be_id = 0; //DECL_TYPE = tk;//??? DECL_TYPE = TNAME; //front should be :: front->tok = TSCOPE; front->retval.pn = yylval.pn; yylval.pn = 0; tk = DECL_MARKER; goto ret; } } } } tk2 = deltok(1); tk2 = la_look(); if ( tk2 == MUL ) { tk = MEMPTR; tk2 = deltok(1); } } // Have a TNAME. Check to be sure. else if ( must_be_id ){ //error('d',"lalex: must_be_id: <tname %n> <%k>",yylval.pn,tk2); if ( in_class_decl && lasttk == TYPE && tk2 == LP && strcmp(yylval.pn->string,ccl->string) == 0 ) error("%nK with returnT", yylval.pn); else if ( lasttk == TYPE && lastval.t == OVERLOAD && ( tk2 == SM || tk2 == LP ) ) { tk = ID; yylval.pn->hide(); yylval.pn = new name( yylval.pn->string ); yylval.pn->n_oper = TNAME; } else if ( lasttk == OPERATOR || in_typedef && yylval.pn->n_key == NESTED) must_be_id = 0; else if ( lasttk != TSCOPE // watch out for X::X || lastval.pn != yylval.pn || (in_typedef && in_typedef->check( yylval.pn->tp,0) == 0 )) { tk = ID; if ( in_typedef && (lasttk == MUL || lasttk == REF)) { defer_check = 1; in_tag = yylval.pn; } if ( lasttk == MEM && yylval.pn->lex_level ) { Pname nn = gtbl->look( yylval.pn->string, 0 ); if (nn == 0 ) error( "%k%s undeclared", lasttk, yylval.pn->string); else yylval.pn = nn; } else { // error('d',"lalex: else: lasttk: %k", lasttk ); if (lasttk!=DOT && lasttk!=REF && lasttk!=TSCOPE && lasttk != GOTO ) { // handle typedefs in basetype::check // when type is available if (!in_typedef) { // error('d',"\"%s\" line %d: hiding%n",__FILE__,__LINE__,yylval.pn); yylval.pn->hide(); } yylval.pn = new name(yylval.pn->string); yylval.pn->n_oper = TNAME; } } if ( defer_check ) defer_check = 0; } } // must_be_id if ( in_class_decl && ccl->lex_level && yylval.pn->lex_level != 0 && yylval.pn->tp && (yylval.pn->tp->base != COBJ && yylval.pn->tp->base != EOBJ)) { Pname n = gtbl->look( yylval.pn->string,0); if ( in_mem_fct ) { if (n && n->base == TNAME ) { error('w', "local typedef %n(%t) is not in scope of local class %s members; usingG (%t)", yylval.pn, yylval.pn->tp, ccl->string, n->tp ); yylval.pn = n; } else error( "local typedef %sis not in scope of inline member function of local class %s", yylval.pn->string, ccl->string); } } // if we still have a TNAME, make sure have the right TNAME // possibility of ananchronistic reference to nested type Ptype nbt = yylval.pn->tp; if (tk == TNAME && curr_scope == 0 && nbt && // Y y; not X::Y y; (nbt->base == EOBJ || nbt->base == COBJ)) { Ptype t = Pbase(nbt)->b_name->tp; if ( ccl && t->in_class && strcmp(t->in_class->string, ccl->string)) { switch( nbt->base ) { case COBJ: if (is_empty(Pclass(t))) yylval.pn = check_nested_type( yylval.pn ); break; case EOBJ: if (is_empty(Penum(t))) yylval.pn = check_nested_type( yylval.pn ); break; }; } } } else { // tk == ID char *s = yylval.s; Pname n = ktbl->look( s, HIDDEN ); Pname nstd = ktbl->look( s, NESTED ); // inside a class definition, ccl, that is nested // s is a nested class name, and is the name of ccl if (ccl && ccl->in_class && nstd && strcmp(s, ccl->string)==0) { for (Pname nn=nstd; nn; nn=nn->n_tbl_list) { Ptype tt = (nn->tp->base==COBJ || nn->tp->base==EOBJ) ? Pbase(nn->tp)->b_name->tp : nn->tpdef; Pclass cl = tt->in_class; if ( nn->n_key != NESTED ) continue; if (strcmp(ccl->in_class->string,cl->string) == 0) { tk = TNAME; yylval.pn = nn; n = nstd = nn; } } } if (tk2 == MEM) { // ID :: if (n) { yylval.pn = n; /*XXX*/ goto crunch; } else if (nstd && nstd->n_tbl_list==0) { yylval.pn = nstd; tk = TSCOPE; tk2 = deltok(1); tk2 = la_look(); if (tk2 == MUL ) { tk = MEMPTR; tk2 = deltok(1); } } else { error( "%s:: %sis not aTN", s, s ); tk2 = deltok(1); goto gettok; } } else // transitional kludge if ( n && nstd && n == nstd ) ; // null statement // Have an ID. Check last token to be sure. else if (lasttk==ENUM || lasttk==AGGR && // template <class id, class id> (tk2 != GT && tk2 != CM)) { int fd = tk2!=LC && tk2!=COLON; tk = TNAME; if ( nstd ) { // in transitional model, need flag this as error if ( fd == 0 ) { // real definition if ( ccl == 0 ) error("nested andG%k %s(to do this placeG%k %s {}; first)",lasttk==ENUM?lasttk:CLASS, s, lasttk==ENUM?lasttk:CLASS, s); else error("multiple nested%k %s(to do this placeG%k %s {}; first)",lasttk==ENUM?lasttk:CLASS, s, lasttk==ENUM?lasttk:CLASS,s); error( 'i', "cannot recover from previous errors" ); } } else // new tag, define it if (n==0 || (n->n_template_arg == template_type_formal)) { // error('d', "ccl: %t fd: %d, in_mem_fct: %d, in_class_decl: %d", ccl, fd, in_mem_fct, in_class_decl); n = new name( s ); if ( fd ) // struct X*, etc. n->lex_level=0; else n->lex_level=lex_level>=0?lex_level:0; if ( ccl && fd == 0 && in_class_decl && (bl_level == ccl->lex_level + in_class_decl + 1)) n = do_nested_type( n ); else // note: ***** modify to handle local typedef // note: ***** add local enums if ( n->lex_level && lasttk != ENUM ) // temporary n = do_local_class( n, n->lex_level ); else { n = n->tname( lastval.t ); modified_tn = modified_tn->l; if (fd && gtbl->look(n->string,0)) statStat = n; } } else { if (n->tp->base!=COBJ && n->tp->base!=EOBJ) { error( 'i', "hidden%n:%t",n,n->tp ); goto gettok; } if ( tk2 == LC || tk2 == COLON ) { // class declared and hidden but not yet defined // may have ctor defined which invalidates hiding statStat = n; n->n_key = 0; // inside class definition it cannot be hidden } } yylval.pn = nstd?nstd:n; } else { tk = ID; yylval.pn = new name( s ); } if ( tk == ID ) { switch ( tk2 ) { case ID: case TNAME: case AGGR: case ENUM: { Pname n = 0; if ((curr_scope||ccl) && nstd) { // within class scope in which nested class is visible // curr_scope == set by TSCOPE, X::foo() { ... } // ccl == parsing class definition ``ccl'' char *str = curr_scope?curr_scope->string:ccl->string; for (Pname nn=nstd; nn; nn=nn->n_tbl_list) { Ptype tt = (nn->tp->base==COBJ || nn->tp->base==EOBJ) ? Pbase(nn->tp)->b_name->tp : nn->tpdef; Pclass cl = tt->in_class; if ( nn->n_key != NESTED ) continue; if ( strcmp(str,cl->string) == 0){ tk = TNAME; yylval.pn = nn; if (lasttk == TYPE && lastval.t == TYPEDEF ) in_typedef = yylval.pn->tp; break; } } } if (tk == TNAME) break; // found nested class n = ktbl->look( s, HIDDEN ); if ( n ) { Pname nn = n; switch ( n->tp->base ) { default: error("typedef %sis not visible in this scope", s ); break; case COBJ: if (is_empty(Pclass(Pbase(n->tp)->b_name->tp))) n = check_nested_type( nn ); if (nn == n) error("%sis hidden: use struct %s%s", s,s,front->retval.s); break; case EOBJ: if (is_empty(Penum(Pbase(n->tp)->b_name->tp))) n = check_nested_type( nn ); if (nn == n) error("%sis hidden: use enum %s%s", s,s,front->retval.s); break; }; tk = TNAME; yylval.pn = n; } else if (n=ktbl->look(s,NESTED)) { TOK ntk; bit ok = 0; Ptype tt = return_nstd_local_type(n,ntk); Pclass cl = tt->in_class; if (ccl) { // x::y unncessary with in_class, // a derived class of in_class // or classes enclosing in_class if (ccl==cl || ccl->has_base(cl)) ok++; else { for (Pclass eccl=ccl->in_class;eccl; eccl=eccl->in_class) if ( eccl == cl ) { ok++; break; } } } if (!ok) error('w', "use %t:: to access nested %k %s (anachronism)", cl, ntk, n->string ); tk = TNAME; yylval.pn = n; } else { // probably a typo if ( tk2 == ID ) error("%s%s: %sis not aTN", s,front->retval.s,s); else if ( tk2 == TNAME ) error("%s%s: %sis not aTN", s,front->retval.pn->string,s); else error("%s%k: %sis not aTN", s,front->retval.t,s); goto gettok; } break; } case DOT: case REF: mem_sel = yylval.pn; break; default: if ( lasttk == TNAME && tk2 == LC ) { error("T%s %k: %s is unexpected", s, tk2, s ); goto gettok; } // have an ID. lets just make sure it should not be a TNAME if (curr_scope||ccl||nstd) { if (ccl && in_typedef && in_typedef->base != 0 && in_mem_fct == 0 && (tk2 == SM || tk2 == RP || tk2 == LB)) { yylval.pn = do_nested_type( yylval.pn ); tk = TNAME; } else if (nstd && must_be_id == 0 && in_expr == 0) { // error('d',"nstd: %n must_be_id 0 have id tk2: %k lasttk: %k",nstd,tk2,lasttk); // error('d',"nstd: in_expr %d lex_level %d",in_expr,lex_level); Pclass xcl = curr_scope?Pclass(Pbase(curr_scope->tp)->b_name->tp):(ccl?ccl:0); for (Pname nn=nstd; nn; nn=nn->n_tbl_list) { TOK ntk; bit ok = 0; Ptype tt = return_nstd_local_type(nn,ntk); Pclass cl = tt->in_class; // error('d',"xcl: %t ccl: %t", xcl, ccl ); if ( xcl ) { if (xcl==cl || xcl->has_base(cl) || ccl == cl) ok++; else { for (Pclass eccl=xcl->in_class;eccl;eccl=eccl->in_class) if ( eccl == cl ) { ok++; break; } } } if (nn == nstd) { if ( // special case: foo(X, // in_arg_list not set until **after** X is handled ((in_arg_list || lasttk==LP) && // foo(nestedX (tk2==CM || tk2==ASSIGN || (tk2==RP && lasttk!=MUL && lasttk!=REF))) || // class x : public nestedX (tk2==LC && (lasttk==PR || lasttk==VIRTUAL)) || // nestedX [*&] (tk2 == MUL || tk2==AND) || (lasttk==LP && tk2==RP) || (lasttk==TSCOPE && lastval.pn == nn) || (lasttk==COMPL && dtor_seen == nn) || (lasttk==TYPE && lastval.t == TYPEDEF) || lasttk == OPERATOR || lasttk == NEW || in_sizeof ) { // must be type name, and it must be nested: if ( nstd->n_tbl_list == 0 ) { // only one: ok if (lasttk != TSCOPE && !ok ) error('w', "use %t:: to access nested %k %s (anachronism)", cl, ntk, nn->string); break; } else { if (lasttk != TSCOPE && lasttk != TYPE && !ok){ error("ambiguous nested type %s, use %t::%s",nn->string,xcl,nn->string); error( 'i', "cannot recover from previous errors" ); } } } } if ( nn->n_key != NESTED ) continue; if (xcl && strcmp(xcl->string,cl->string) == 0) break; } // end: for nn = nstd if (nn) { tk = TNAME; yylval.pn = nn; if (lasttk == TYPE && lastval.t == TYPEDEF ) in_typedef = yylval.pn->tp; } } // end: if (nstd) } // end: if (curr_scope||ccl) break; } // end: switch tk2 } // end: if (tk == ID) } // error('d',"testing for in_expr: in_expr: %d tk: %k", in_expr, tk ); // error('d',"testing for in_expr: tk2: %k lasttk: %k", tk2, lasttk ); if (lex_level && tk==ID && tk2==LP && (lasttk==LC || lasttk==RC || lasttk==RP || lasttk == ASSIGN || lasttk == SM)) in_expr = 1; else in_expr = 0; } if ( tk == TNAME || ( tk == TYPE && latype(yylval.t) ) // XXX || tk == TSCOPE || tk == MEM || tk == REF || tk == DOT || tk == GOTO || tk == MEMPTR ) // TNAME cannot immediately follow a type name, // scope operator, right curly, selection, or goto must_be_id = 1; else must_be_id = 0; switch ( tk ) { case SM: mem_sel = 0; // no break in_expr = 0; case RP: case RC: must_be_expr = 0; break; case COLON: if (lasttk == RP || (lasttk == TYPE && lastval.t == CONST)) must_be_expr = 1; break; case SIZEOF: in_sizeof = 1; break; }; ret: if ( tk == COMPL && lasttk == TSCOPE ) dtor_seen = lastval.pn; else dtor_seen = 0; lasttk = tk; lastval = yylval; LDB(1,showQ("after"); fprintf(stderr,"returning '%s'",image(tk)); if ( tk==ID || tk==TNAME ) fprintf(stderr," -- '%s'",yylval.pn->string); fprintf(stderr,"\n"); ); // error('d',"returning tk: %k dtor_seen: %n", tk,dtor_seen ); return tk; } extern void la_backup( TOK t, YYSTYPE r ) /* called by parser to push token back on front of queue */ { LDB(1,fprintf(stderr,"\n*** la_backup( '%s', ...)\n",image(t))); if ( t == ID ) { Pname n = r.pn; r.s = n->string; DEL(n); } register toknode* T = new toknode(t,r,curloc); if (front) { front->last = T; T->next = front; T->last = 0; front = T; } else front = rear = T; lasttk = 0; } extern int la_sctype( TOK t ) { //error('d',&latok->place,"la_sctype(%k ) -- latok ==%k",t,latok->tok); if ( t != latok->tok && t != TSCOPE && t != MEMPTR ) error( 'i', &latok->place, "la_sctype, lalex.c" ); switch( latok->retval.t ) { case TYPEDEF: case EXTERN: case STATIC: case AUTO: case REGISTER: case OVERLOAD: case INLINE: case FRIEND: case CONST: case VOLATILE: return 1; default: return 0; } } extern int la_cast() /* called in reduction of term_lp to check for ambiguous prefix-style cast if result is 1, caller inserts DECL_MARKER to force reduction of cast */ { // yychar already designates TYPE or TNAME // LP must start the lookahead queue! LDB(1,fprintf(stderr,"\n*** la_cast()\n");); int tk, tk2 = latok->tok; for ( ; ; ) { tk = tk2; tk2 = lookahead(); switch( tk ) { case LP: if ( tk2 == MUL || tk2 == AND || tk2 == TSCOPE || tk2 == MEMPTR ) // T ( * ... // T ( C ::* ... continue; else // T ( exp ) return 0; case MUL: case AND: //if ( tk2 == SCTYPE ) if ( la_sctype( tk2 ) ) // T ( * const ... // T ( * volatile ... tk2 = lookahead(); continue; case MEMPTR: if ( tk2 == RP ) continue; break; case TSCOPE: if ( tk2 == MUL ) // T ( C :: * ... continue; else // T ( exp ) return 0; case RP: case LB: // T (*)() // T (*[])() return 1; } return 0; } } static int la_decl() /* handles ambiguities type (*x) () type (*x) [] at start of arg list / statement return val == 1 if lookahead finds a declaration (used for error messages only) if declaration is "ambiguous" (i.e., can't be recognized with 1-symbol lookahead), insert DECL_MARKER to force reduction of "type" */ { // LP must start the lookahead queue! LDB(1,fprintf(stderr,"\n*** la_decl()\n");); int tk, tk2 = latok->tok; int paren = 0; int ptr = 0; for ( ; ; ) { tk = tk2; tk2 = lookahead(); // fprintf(stderr,"\nla_decl:tk:%d %s tk2: %d %s", tk, keys[tk], tk2, keys[tk2]); switch( tk ) { case LP: if ( tk2 == MUL || tk2 == AND || tk2 == TSCOPE ) { // T ( * ... ++paren; ptr = 1; continue; } else if ( tk2 == MEMPTR ) { // T ( C ::* ... return 1; } else // possible redundant parens if ( tk2 == ID && lookahead() == RP ) { TOK tp = lookahead(); // error( 'd', "tp %k tk: %k tk2: %k", tp, tk, tk2 ); // error( 'd', "bl_level: %d, in_class_decl: %d", bl_level,in_class_decl ); if ( tp == SM || tp == CM || tp == ASSIGN ) { // member initialization list if ( tp != SM && in_arg_list == 0 ) return 1; } else if ( tp == RP && (bl_level-in_class_decl==0)) return 1; if ( tp != LP ) return 0; latok=latok->last; // restore lookahead ++paren; continue; } else // T ( exp ) return 0; case MUL: case AND: //if ( tk2 == SCTYPE ) if ( la_sctype( tk2 )) // T ( * const ... // T ( * volatile ... return 1; else { ptr = 0; continue; } case MEMPTR: // T ( C :: * ... return 1; case TSCOPE: if ( tk2 == MUL ) //??tk SHOULD HAVE TRANSLATED TO MEMPTR!! // T ( C :: * ... return 1; else if ( ptr ) // T ( exp ) return 0; else if ( tk2 == ID || tk2 == OPERATOR ) // T ( * C :: id ... continue; else // error return 0; } break; } if ( tk == RP || tk == LB ) // T (*)() // T (*[])() return 1; if ( tk != ID && tk != OPERATOR ) // T ( exp ) return 0; if ( tk == OPERATOR ) switch ( tk2 ) { case PLUS: case MINUS: case MUL: case REFMUL: case AND: case OR: case ER: case SHIFTOP: case EQUOP: case DIVOP: case RELOP: case ANDAND: case OROR: case NOT: case COMPL: case ICOP: case ASSIGN: case ASOP: case NEW: case GNEW: case DELETE: // OPERATOR oper tk2 = lookahead(); break; case LP: // OPERATOR () tk2 = lookahead(); if ( tk2 == RP ) { tk2 = lookahead(); break; } else return 0; case LB: // OPERATOR [] tk2 = lookahead(); if ( tk2 == LB ) { tk2 = lookahead(); break; } else return 0; default: // illegal operator return 0; } int allow_lp = 1; int allow_rp = 1; for ( ; ; ) { tk = tk2; tk2 = lookahead(); // fprintf(stderr,"\nla_decl2:tk:%d %s tk2: %d %s", tk, keys[tk], tk2, keys[tk2]); switch( tk ) { case LP: if ( !allow_lp ) // T ( * id [ exp ] ( ... return 0; // Current lookahead will be a decl if // the next lookahead is an arg decl if ( tk2 == RP || tk2 == ENUM || tk2==AGGR || tk2==ELLIPSIS || la_sctype( tk2 )) // T ( * id () // T ( * id ) () return 1; if ( tk2 == TYPE || tk2 == TNAME ) { // T ( * id ) ( T2 ... if ( lookahead() == LP && !la_decl() ) return 0; return 1; } return 0; case LB: if ( tk2 == RB || lookahead() == RB ) // T ( * id [] ... return 1; else { // T ( * id [ exp ] ... allow_lp = 0; allow_rp = 1; while ( lookahead() != RB ); tk2 = lookahead(); continue; } case RP: // error ('d', "rp: allow_rp: %d paren: %d", allow_rp, paren ); if ( !allow_rp || !paren ) // T ( * id ) ) return 0; // permit redundant parentheses else if ( tk2 == SM || tk2 == CM || tk2 == ASSIGN ) return 1; else if ( tk2 == RP && (bl_level-in_class_decl == 0)) return 1; else { // T ( * id ) ... allow_lp = 1; allow_rp = 0; --paren; continue; } default: return 0; } } } /* ** PROCESSING OF INLINE MEMBER FUNCTIONS */ static int la_snarf(); extern toknode* save_text() /* save text of inline def on q of class */ { // Q should contain at least the tokens < FDEF, X ... > // where X is either LC or COLON (start of ftn) LDB(2,fprintf(stderr,"save_text()")); LDB(3,fprintf(stderr,"front: %s",image(front->tok))); LDB(3,fprintf(stderr,"front->next: %s",image(front->next->tok))); latok = front->next; if ( la_snarf() ) { // append this set of tokens to // inline tokenq for class toknode* t = front; // FDEF if ( ccl->c_funqf == 0 ) ccl->c_funqf = front; else { ccl->c_funqr->next = front; front->last = ccl->c_funqr; } ccl->c_funqr = latok; front = latok->next; latok->next = 0; if (front) front->last = 0; return t; } return 0; } extern void restore_text() /* restore tokens for member inlines onto token q */ { LDB(2,fprintf(stderr,"restore_text()")); if (ccl->c_funqf == 0) // no inlines on Q return; LDB(3,fprintf(stderr," Q present: %d,%d",ccl->c_funqf,ccl->c_funqr)); LDB(3,fprintf(stderr," front==%s",image(ccl->c_funqf->tok))); LDB(3,fprintf(stderr," rear ==%s",image(ccl->c_funqr->tok))); ccl->c_funqr->next = front; if (front) front->last = ccl->c_funqr; front = ccl->c_funqf; ccl->c_funqf = ccl->c_funqr = 0; } static void del_tokens( toknode* marker ) /* delete tokens from marker to latok, not inclusive */ { if ( marker == 0 || marker == latok || marker->next == 0 ) error('i', "bad token queue"); LDB(2,fprintf(stderr,"del_tokens: %s..%s\n",image(marker->tok),image(latok->tok))); register toknode* tt = marker->next; if ( tt == latok ) return; marker->next = latok; latok->last->next = 0; latok->last = marker; register toknode* tx = tt; do { LDB(3,fprintf(stderr," deleting %s\n",image(tt->tok))); tx = tx->next; delete tt; tt = tx; } while ( tx ); } static int la_snarf() /* scan function def without processing declarations */ { LDB(2,fprintf(stderr,"la_snarf()")); loc *L = &latok->place; //DBPLACE(1,L.l,L.f); int parens = 0; int paren_error = 0; toknode* marker = latok; switch ( latok->tok ) { default: error('i', L, "bad token Q snarfing function: %d", latok->tok); case COLON: break; case LC: --bl_level; goto eatf; } LDB(2,fprintf(stderr,"\"eat\" member initializers")); for (;;) { if (latok->next == 0) add_tokens(); switch ( (latok=latok->next)->tok ) { case LP: ++parens; default: LDB(3,fprintf(stderr,"...%s",image(latok->tok))); continue; case RP: if ( (--parens < 0) && (paren_error++ == 0) ) error(0,&latok->place,"unbalanced ()"); continue; case LC: case RC: if ( parens <= 0 ) goto eatf; continue; case SM: if ( parens <= 0 ) { error(0, L, "illegal bit field"); del_tokens( front ); delete front; front = latok; front->last = 0; return 0; } continue; case EOFTOK: error('i',&latok->place,"unexpected end of file"); } // switch } // for eatf: int level = 1; for (;;) { if (latok->next == 0) add_tokens(); switch ( (latok=latok->next)->tok ) { case LC: ++level; default: LDB(3,fprintf(stderr,"...%s",image(latok->tok))); continue; case RC: LDB(3,fprintf(stderr,"...RC")); if (--level <= 0) { if (level < 0) { error(0,&latok->place,"unexpected '}'"); goto bad; } return 1; } break; case EOFTOK: error('e', &latok->place, "unbalanced {}"); goto bad; } // switch } // for bad: del_tokens( marker ); marker->tok = SM; return 0; } Pname check_for_nested( Pname nstd, TOK lasttk, YYSTYPE lastval, TOK tk2) { // error('d',"nstd: %n must_be_id 0 tk2: %k lasttk: %d",nstd,tk2,lasttk); TOK tk = ID; Pclass xcl = curr_scope?Pclass(Pbase(curr_scope->tp)->b_name->tp):(ccl?ccl:0); for (Pname nn=nstd; nn; nn=nn->n_tbl_list) { TOK ntk; bit ok = 0; Ptype tt = return_nstd_local_type(nn,ntk); Pclass cl = tt->in_class; if ( xcl ) { if (xcl==cl || xcl->has_base(cl)) ok++; else { for (Pclass eccl=xcl->in_class;eccl;eccl=eccl->in_class) if ( eccl == cl ) { ok++; break; } } } if (nn == nstd) { if ( ((in_arg_list || lasttk==LP) && // foo(nestedX (tk2==CM || tk2==ASSIGN || tk2==RP)) || (tk2==LC && (lasttk==PR || lasttk==VIRTUAL)) || (tk2 == MUL || tk2==AND) || (lasttk==LP && tk2==RP) || (lasttk==TSCOPE && lastval.pn == nn) || (lasttk==COMPL && dtor_seen == nn) || (lasttk==TYPE && lastval.t == TYPEDEF) || lasttk == NEW || in_sizeof ) { if ( nstd->n_tbl_list == 0 ) { // only one: ok break; } else { if (lasttk != TSCOPE && !ok){ error("ambiguous nested type %s, use X::%s",nn->string,nn->string); error( 'i', "cannot recover from previous errors" ); } } } } if ( nn->n_key != NESTED ) continue; if (xcl && strcmp(xcl->string,cl->string) == 0) break; } // end: for nn = nstd return nn; }