/*ident "@(#)cls4:src/lalex.c 1.21" */ /******************************************************************************* C++ source for the C++ Language System, Release 3.0. This product is a new release of the original cfront developed in the computer science research center of AT&T Bell Laboratories. Copyright (c) 1993 UNIX System Laboratories, Inc. Copyright (c) 1991, 1992 AT&T and UNIX System Laboratories, Inc. Copyright (c) 1984, 1989, 1990 AT&T. All Rights Reserved. THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE of AT&T and UNIX System Laboratories, Inc. The copyright notice above does not evidence any actual or intended publication of such source code. lalex.c: lookahead *****************************************************************************/ #include #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 // static data members definition int templ_compilation::parameters_in_progress=0; toknode* toknode::free_toks=0; #ifdef DBG extern "C" char* image( int t ) { if(keys[t]) return keys[t]; else { static char b[20]; sprintf(b,"token(%d)",t); return b; } } extern "C" void printok2( TOK tk, YYSTYPE rv, loc wh ) { switch(tk) { default: fprintf(stderr,"\t%s",image(tk)); break; case ID: case ICON: case CCON: case FCON: case STRING: fprintf(stderr,"\tID '%s'",rv.s); break; case TNAME: fprintf(stderr,"\tTNAME '%s'",rv.pn->string); break; case NAME: fprintf(stderr,"\tID '%s'",rv.pn->string); break; case PTNAME: fprintf(stderr,"\tPTNAME '%s'",rv.pn->string); break; case AGGR: fprintf(stderr,"\tAGGR '%s'",image(rv.t)); break; case TYPE: fprintf(stderr,"\tTYPE '%s'",image(rv.t)); break; case TSCOPE: fprintf(stderr,"\tTSCOPE '%s'::",rv.pn->string); break; case MEMPTR: fprintf(stderr,"\tMEMPTR '%s'::*",rv.pn->string); break; } putc(' ',stderr); wh.put(stderr); putc('\n',stderr); fflush(stderr); } extern "C" void printok( toknode* t, char* id = 0 ) { if ( id ) fprintf(stderr,"%s:",id); if ( t == 0 ) { fprintf(stderr," NULL TOKNODE!\n"); fflush(stderr); } else printok2( t->tok, t->retval, t->place ); } extern "C" 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); fflush(stderr); } #endif int bl_level; static int laexpr( TOK ); static int latype( TOK ); static int la_decl( int ); static TOK lookahead(); /* make this a toknode! */ static int lasttk = 0; // one token history static YYSTYPE lastval; // yylval lasttk value static 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; static toknode* latok; // current lookahead token toknode* front = 0; static toknode* rear = 0; const int TQCHUNK = 16; void* toknode::operator new(size_t) { register toknode* p; if ((p=free_toks) == 0) { register toknode* q; free_toks = q = (toknode*)new char[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; } 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(); } #define USE_TOKEN(T,W) \ LDB(2,error('d',&(T)->place,"use_token('%k','%s')",(T)->tok,W);); \ if ( !(T)->used ) use_token(T); //SYM -- removed Ptype return_nstd_local_type(Pname,TOK&) //SYM -- removed Pname local_nested_kludge( Pname n, Pname tn ) enum { one_back, two_back }; static TOK last_tokens[2]; // TSCOPE not reduced at this point static Pname last_tname; // tname :: id, where id is nested class static void use_token( toknode* T ) /* lookup TNAMEs here instead of in tlex() maintain block level */ { T->used = 1; static bit aggr=0; if (T->tok == AGGR || T->tok == ENUM) aggr=1; else if ((T->tok != MEM) && (T->tok != ID)) aggr=0; 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 REF: case DOT: { toknode* t = T; Pname q = 0, r = 0; for(;;) { if ( t->next == 0 ) add_tokens(); t = t->next; if ( t->tok == ID && t->next->tok == MEM ) { Pname n = new name( t->retval.s ); n->base = MEMQ; if ( q == 0 ) q = r = n; else { r->n_list = n; r = n; } t = t->next; } else if ( t->tok == MEM ) { Pname n = new name(); n->base = MEMQ; if ( q == 0 ) q = r = n; else { r->n_list = n; r = n; } } else break; } if ( q ) { toknode *x = T->next, *xx = x->next; x->tok = MEMQ; x->retval.pn = q; x->used = 1; x->next = t; t->last->next = 0; t->last = x; for ( ; xx; xx = x ) { x = xx->next; delete xx; } } break; } case ID: if ( last_tokens[one_back] == MEMQ ) break; { Pname n = 0; TOK sc = T->next&&T->next->tok==MEM || aggr ? HIDDEN : 0; // error('d', &T->place, "use_token: %s", T->retval.s ); // look up in correct table if ( last_tokens[one_back]==MEM || (last_tokens[one_back]==TSCOPE && !templp->in_progress) ) { if ( last_tokens[two_back]==TNAME || (last_tokens[two_back]==GT && last_tokens[one_back]!=MEM) ) { if ( last_tokens[two_back] == GT && last_tokens[one_back] == TSCOPE ) { extern YYSTYPE yyval; last_tname = yyval.pn; } // TNAME :: ID Pname tn = last_tname; if ( tn == 0 ) error('i',&T->place,"last_tname not set for tname::%s",T->retval.s); while (tn->tp && tn->tp->base==TYPE) tn=Pbase(tn->tp)->b_name; if ( strcmp(T->retval.s,tn->string)==0 ) { // X::X or X::~X -- leave as TNAME here n = tn; } else if ( tn->tp && tn->tp->base == COBJ ) { Pclass cl = Pclass( Pbase(tn->tp)->b_name->tp ); // X::X or X::~X -- leave as TNAME here if ( cl->is_templ_instance() && strcmp(T->retval.s,Ptclass(cl)->unparametrized_tname()->string)==0 ) { n = tn; } else if ( (cl->defined & (DEF_SEEN|DEFINED)) == 0 ) error(&T->place,"%n:: %s -- %tU",last_tname,T->retval.s,cl); else { n = k_find_member(T->retval.s, cl, sc); if (n && n->n_ktable == Gtbl) n = 0; } } else { if (tn->tp->base != ANY) // don't flag Template formal error(&T->place,"%n:: %s --%n not aCN",tn,T->retval.s,tn); n = k_find_name(T->retval.s,Ctbl,sc); } } else if ( last_tokens[two_back] != ID ) { // :: ID n = k_find_name(T->retval.s,Gtbl,sc); } } else // look in current scope n = k_find_name(T->retval.s,Ctbl,sc); T->idname = n; if ( n && n->base == TNAME ) { T->tok = TNAME; T->retval.pn = n; DB(if(Ldebug>=1)error('d',&T->place,"use_token: tname%n",T->retval.pn);); } //SYM -- remove nn = local_nested_kludge(nn,ntd==NESTED?n:0); #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; } } static void la_reset( toknode* T, Pname qual ) { // just failed a lookahead for fptr declarator after qualified TNAME // TNAME::TNAME ( ... ) // probably a member ftn declaration // "unuse" tokens after T so names will be searched in correct scope // T should == '(' last_tokens[one_back] = MEM; last_tokens[two_back] = TNAME; last_tname = qual; while ( T && T->used ) { T->used = 0; switch ( T->tok ) { case TNAME: T->tok = ID; T->retval.s = T->retval.pn->string; break; case LC: --bl_level; break; // backtrack case RC: ++bl_level; break; // backtrack } T = T->next; } } 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"); } static Pname idname; 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; idname = T->idname; delete T; return tk; } 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 void del_1( toknode* t ) // delete t from the queue { if ( t->last ) t->last->next = t->next; else front = t->next; if ( latok == t ) latok = t->last ? t->last : front; if ( t->next ) t->next->last = t->last; else rear = t->last; delete t; } extern TOK la_look() /* peek at head of token queue */ { LDB(1,fprintf(stderr,"\n*** la_look()\n")); 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 DB(if(Ldebug>=1)error('d',"check_decl() tscope%n...",yylval.pn);); 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(in_arg_list) ) { t->tok = DECL_MARKER; //TNAME } } DB(if(Ldebug>=1)error('d',"%k",DECL_MARKER);); break; case DECL_MARKER: if ( in_arg_list == 0 ) break; // gag! -- re-scan for declaration in case previous call // to la_decl() came before reduction of arg_lp // (occurs if la_decl() were called from lalex()) yychar = DECL_TYPE; DECL_TYPE = 0; if ( yychar != TYPE && yychar != TNAME ) error('i',"check_decl() failed rescanning arg decl; yychar==%d",yychar); // no break; case TYPE: case TNAME: DB(if(Ldebug>=1)error('d',"check_decl() %s",yychar==TYPE?"TYPE":"TNAME");); if ( la_look() == LP && la_decl(in_arg_list) ) { must_be_id = 0; DECL_TYPE=yychar; yychar = DECL_MARKER; DB(if(Ldebug>=1)error('d',"%k",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(in_arg_list) ) { 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 || t->tok==TSCOPE) { // determine whether it is a memptr if (t->next == 0) add_tokens(); if (t->next->tok == MUL) { t->tok = MEMPTR; del_1(t->next); } else { t->tok = TSCOPE ; // handle X::Y ... if (t->next->tok == ID && templp->in_progress) { Pname cn = tn->tp->is_cl_obj(); if ( cn ) { int hh = t->next->next->tok==MEM; Pname tn2 = k_find_member(t->next->retval.s, Pclass(cn->tp), hh ); if ( tn2 && tn2->base == TNAME ) { t->next->tok = TNAME; t->next->retval.pn = tn2; } } } } t->retval.pn = tn ; --templp->parameters_in_progress; return 1; } else { --templp->parameters_in_progress; 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; } } //??? check_for_nested() return tk; } extern int in_sizeof; extern int in_friend; extern int in_new; static int type_is_redefined( Pname n ) { Pktab tb = Ctbl; while ( tb->k_id == ARG ) tb = tb->k_next; return n->n_ktable == tb; } static Pname do_nl_type( Pname n, int lex_level, TOK tecsu ) /* * replaces do_local_class() and do_nested_type() * define a new type * context is either "AGGR ID" or "ENUM ID" at local or nested scope * NOTE: typedefs now processed in name::tdef() */ { Pname nn = n; if ( ccl && in_mem_fct==0 && strcmp(ccl->string, n->string)==0) { // class x { typedef T x; error( "nested%k%n redefines immediately enclosing class",tecsu==TYPE?TPDEF:tecsu,n); error( 'i', "cannot recover from previous errors" ); } switch ( tecsu ) { case CLASS: case STRUCT: case UNION: case ENUM: // check for redef at local scope... if ( n->base == TNAME // previous def exists && n->lex_level == lex_level // same block level && type_is_redefined(n) ) { if ( n->tp==0 || ( n->tp->base!=COBJ && n->tp->base!=EOBJ )) { //error("two definitions of%n",n); //error('i',"cannot recover from earlier errors"); // typedef T ... class T{}, etc. // error caught later return n; } // catch some redefs here to avoid internal errors later if ( n->tp->base == EOBJ && tecsu != ENUM || n->tp->base == COBJ && tecsu == ENUM ) { error("%n defined asC and enum",n); error('i',"cannot recover"); } // class C{}; ... class C{}; // enum E{}; ... enum E{}; // etc. -- also an error,but requires name to be placed on // local_class so error can be detected later during dcl phase if ( n->tp->base == COBJ && (n->tp->classtype()->defined & (DEFINED|DEF_SEEN)) == 0 ) // class X; class X{}; ... return n; } nn = new name(n->string); nn->lex_level = lex_level; nn = nn->tname(tecsu); if ( nn->n_ktable == 0 ) error('i',"TN insert failed for%n",nn); // if local, put on list for use in del.c if ( tecsu!=ENUM && nn->n_ktable->k_id==BLOCK ) local_class = new name_list( nn, local_class ); break; default: error('i',&n->where,"bad tecsu%k in do_nl_type()",tecsu); } return nn; } //SYM -- removed int is_empty( Pclass cl, bit const_chk ) /* for nested class check, empty means *no* members * for const object check, means no *data* members */ //SYM -- Since size isn't calculated until dcl() and since //SYM local/nested classes aren't type checked until the //SYM end of a function/class, is_empty should not be used by the parser. //SYM -- removed int is_empty( Penum en ) //SYM -- Pname check_nested_type(Pname) removed //SYM -- removed int in_local_class( Pclass cl ) //SYM ??? static Pname dtor_seen; //SYM ??? static int in_expr; 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(); Pname n = idname; // error('d',&curloc,"lalex: just got %k '%s' in_typedef: %d ccl: %t",tk,tk==ID||tk==TNAME?n->string:"", in_typedef,ccl); 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 && lasttk != TSCOPE && lasttk != MEM && (lasttk==TYPE || bl_level == 0 || parsing_class_members) && ( laexpr(lasttk) == 0 ) && must_be_expr == 0 && DECL_TYPE == 0 ) { if (la_decl(in_arg_list)) { must_be_id = 0; DECL_TYPE = tk; tk = DECL_MARKER; goto ret; } } //SYM -- change if ( lasttk == AGGR || lasttk == ENUM ) { if ( tk2 == LC || tk2 == COLON || (tk2==SM && !in_new && !in_friend) ) { // tag def //XXXXX currently enter all unqualified //XXXXX friends in Gtbl //XXXXX Should eventually enter in enclosing //XXXXX scope, however back end currently //XXXXX doesn't support this, due to lack //XXXXX of context info. //XXXXX Commented code below will enter name //XXXXX correctly when back end is brought //XXXXX up to date. //Pktab otbl = Ctbl; //Pclass occl = ccl; if ( in_friend ) { if ( Ctbl->k_id != CLASS ) error("friend %s not inC",yylval.pn->string); // else { // --in_class_decl; // ccl = ccl->in_class; // } // if ( Ctbl->k_id == CLASS || Ctbl->k_id == TEMPLATE ) { // Ctbl = Ctbl->k_next; // if (Ctbl->k_id == TEMPLATE) Ctbl = Ctbl->k_next; // } } else // remove this line when friend name entry is fixed if ( tk2 != SM || type_is_redefined(yylval.pn)==0 ) { if ( lex_level && (in_class_decl==0 || in_mem_fct) ) yylval.pn = do_nl_type( yylval.pn, lex_level, lastval.t ); else if ( in_class_decl && ccl ) yylval.pn = do_nl_type( yylval.pn, lex_level, lastval.t ); } //Ctbl = otbl; //if ( ccl != occl ) { // ccl = occl; // ++in_class_decl; //} } } if (tk2 == LT && template_tscope(yylval.pn,latok)) { tk = PTNAME; // a parameterized type name } else if ( tk2==MEM || (tk2==DOT && lasttk!=REF && lasttk!=DOT && lasttk !=REFMUL )) { if (tk2==DOT) error("``.'' used for qualification; please use ``::''"); if (yylval.pn->tp->base == COBJ) { Pclass cl = yylval.pn->tp->classtype(); Pclass cl2 = ccl; while (cl2 && cl2->in_class) cl2=cl2->in_class; if (cl && cl->class_base && (cl2==0 || cl2->templ_base==CL_TEMPLATE || same_class(cl,cl2)==0)) error("YC%n must be qualifiedWZL of instantiations",yylval.pn); } tk = TSCOPE; {//XXX -- should be modified to do lookup and del at each :: int n = 0; while ( (tk2 = lookahead()) == TSCOPE ) n += 2; if ( tk2 == TNAME ) { Pname cn = latok->retval.pn; toknode* t = latok; tk2 = lookahead(); if ( tk2 == LP && (bl_level == 0 || parsing_class_members) && ( laexpr(lasttk) == 0 ) && must_be_expr == 0 && DECL_TYPE == 0 ) { if (la_decl(in_arg_list)) { must_be_id = 0; // t->tok = DECL_MARKER; DECL_TYPE = TNAME; // tk2 = deltok(1); // :: n++; // :: n++; // TNAME tk = DECL_MARKER; while (n-- > 0) deltok(); goto ret; } la_reset(t->next,cn); } } } tk2 = deltok(1); tk2 = la_look(); if ( tk2 == MUL ) { tk = MEMPTR; tk2 = deltok(1); } } // if tk2==MEM // Have a TNAME. Check to be sure. else if ( must_be_id ){ DB(if(Ldebug>=2)error('d',"lalex: must_be_id: <%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 ) //SYM -- remove || 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 )) { DB(if(Ldebug>=2)error('d',"lalex: -- tname -> id; lasttk%k",lasttk)); tk = ID; if ( in_typedef && (lasttk == MUL || lasttk == REF)) { defer_check = 1; in_tag = yylval.pn; } if ( lasttk == MEM && yylval.pn->lex_level ) { //SYM -- change //SYM ??? this code looks suspicious Pname nn = k_find_name(yylval.pn->string,Gtbl,0); if ( nn==0 || nn->base == NAME ) error( "%k%s undeclared", lasttk, yylval.pn->string); else yylval.pn = nn; } else { if (lasttk!=DOT && lasttk!=REF && lasttk!=TSCOPE && lasttk != GOTO ) { // handle typedefs in basetype::check // when type is available if (!in_typedef || in_arg_list) { DB(if(Ldebug>=2)error('d',"\"%s\" line %d: hiding%n",__FILE__,__LINE__,yylval.pn)); yylval.pn->hide(); yylval.pn = new name(yylval.pn->string); } else if ( yylval.pn->base == TNAME ) { yylval.pn = new name(yylval.pn->string); } // else name already copied in do_nl_type() // NOTE: copying name should preserve tpdef yylval.pn->n_oper = TNAME; } else { //error('d',"tname%n -> id lasttk%k tk2%k",yylval.pn,lasttk,tk2); yylval.pn = new name(yylval.pn->string); } DB(if(Ldebug>=2)error('d'," -- %n%k",yylval.pn,yylval.pn->n_oper)); } if ( defer_check ) defer_check = 0; } } // must_be_id DB(if(Ldebug>=2)error('d',"lalex -- end of if tname --%k%n",tk,yylval.pn);); // if we still have a TNAME, make sure have the right TNAME // possibility of ananchronistic reference to nested type //SYM -- transitional stuff removed //Ptype nbt = yylval.pn->tp; //if (tk == TNAME && curr_scope == 0 && nbt && // Y y; not X::Y y; // (nbt->base == EOBJ || nbt->base == COBJ)) //{ ... } } else { // tk == ID char *s = yylval.s; if ( n ) n = n->n_hidden; //SYM Pname nstd = ktbl->look( s, NESTED ); //SYM -- removed nstd weirdness if (tk2 == MEM) { // ID :: -- see use_token() error( "%s:: -- %sis not aTN", s, s ); tk2 = deltok(1); goto gettok; } //SYM transitional kludge deleted // Have an ID. Check last token to be sure. else if (lasttk==ENUM || lasttk==AGGR && (in_arg_list != 2 // template || (tk2 != GT && tk2 != CM))) { // "class X {}" "class X :" enters tag in current scope // "class X;" (but not "new class X;") // enters tag in current scope if it isn't already there // (note: could be defined in enclosing scope) // "friend class X;" enters in scope enclosing ccl int tagdef = tk2==LC || tk2==COLON || (tk2==SM && !in_new && !in_friend); tk = TNAME; // new tag, define it //error("n%n ll %d bl %d tk2%k",n,n?n->lex_level:0,bl_level,tk2); //error('d',"n%n %s ctbl %s",n,n?n->n_ktable->whereami():"???",Ctbl->whereami()); //error('d',"n%n tagdef %d tk2%k rdf %d",n,tagdef,tk2,n?type_is_redefined(n):0); if ( n==0 // no hidden type name ... || (n->n_template_arg == template_type_formal) || tagdef ) { //error('d', "ccl%t ll %d bl %d in_class_decl %d", ccl, ccl?ccl->lex_level:0, bl_level, in_class_decl); //XXXXX currently enter all unqualified //XXXXX friends in Gtbl //XXXXX Should eventually enter in enclosing //XXXXX scope, however back end currently //XXXXX doesn't support this, due to lack //XXXXX of context info. //XXXXX Commented code below will enter name //XXXXX correctly when back end is brought //XXXXX up to date. //Pktab otbl = Ctbl; //Pclass occl = ccl; if ( in_friend && tagdef ) { if ( Ctbl->k_id != CLASS ) error("friend %s not inC",s); //else { // --in_class_decl; // ccl = ccl->in_class; //} //if ( Ctbl->k_id == CLASS || Ctbl->k_id == TEMPLATE ) { // Ctbl = Ctbl->k_next; // if (Ctbl->k_id == TEMPLATE) Ctbl = Ctbl->k_next; //} } else // remove this line when friend name entry is fixed if ( n==0 || tk2!=SM || !type_is_redefined(n) ) { Pname nn = new name( s ); if ( !tagdef ) // struct X*, etc. nn->lex_level=0; else nn->lex_level=lex_level; if ( ccl && tagdef && in_class_decl && (bl_level == ccl->lex_level + in_class_decl + (tk2==LC))) { if ( n ) { DEL(nn); nn=n; } n = do_nl_type(nn,lex_level,lastval.t);//SYM } else if ( nn->lex_level ) { int ll = nn->lex_level; if ( n ) { DEL(nn); nn=n; } n = do_nl_type( nn, ll, lastval.t ); } else { // either global def "class n { ... }" // or global or non-global ref "class n" // (class or enum) //SYM XXXXX currently simulates 2.1 behavior //SYM XXXXX by entering undefined tags in //SYM XXXXX Gtbl //NOTE: By accident, this code also // correctly enters template class // defs into global scope instead // of the current template table. // This should be tested for explicitly // if the code changes. if ( n == 0 ) { Pktab otbl = Ctbl;//SYM Pclass occl = ccl; Ctbl = Gtbl;//SYM ccl = 0; n = nn->tname( lastval.t ); Ctbl = otbl;//SYM ccl = occl; } else DEL(nn); //SYM removed statStat stuff } } //if ( Ctbl != otbl ) { //Ctbl = otbl; //if ( ccl != occl ) { //ccl = occl; //++in_class_decl; //} //} } else { if (n->tp->base!=COBJ && n->tp->base!=EOBJ) { // call error twice to avoid omitting // the message if there were // previous errors error( "hidden%n:%t",n,n->tp ); error('i',"cannot recover"); } //SYM removed statStat stuff } yylval.pn = n; //SYM 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; //SYM removed nstd stuff... n = k_find_name( s, Ctbl, HIDDEN ); if ( n ) { char* x = (tk2==ID||tk2==TNAME) ? front->retval.s : keys[tk2]; // if n->n_key != HIDDEN, then lasttk // was probably TSCOPE (C::) // Otherwise n would have been found. switch ( n->tp->base ) { default: error("%s%s:Tdef %sis %sin this scope", s, x, s, n->n_key==HIDDEN?"hidden":"undefined" ); break; case COBJ: error("%sis %sin this scope: useC %s%s",s,n->n_key==HIDDEN?"hidden":"undefined",s,x); break; case EOBJ: error("%sis %sin this scope: use enum %s%s",s,n->n_key==HIDDEN?"hidden":"undefined",s,x); break; } tk = TNAME; yylval.pn = n; } // if n //SYM -- else nested lookup removed else { // probably a typo if ( tk2 == ID ) { // x::y z, where y is a nested class if (lasttk == TSCOPE && lastval.pn->base == TNAME) { if (lastval.pn->tp->base == ANY && lastval.pn->n_template_arg == template_type_formal) error('s',"use of %n::%sW formalYTZ",lastval.pn,s); else if (lastval.pn->tp->base == COBJ && lastval.pn->tp->classtype()->class_base != VANILLA) error('s',"Zized nestedC access: %n<...>::%s",lastval.pn,s); else error("%s%s: %sis not aTN", s,front->retval.s,s); error( 'i', "cannot recover from previous errors" ); } else 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: 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 //SYM deleted nstd weirdness 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 ); //SYM???if (lex_level && tk==ID && tk2==LP && //SYM??? (lasttk==LC || lasttk==RC || lasttk==RP || //SYM??? lasttk == ASSIGN || lasttk == SM)) //SYM??? in_expr = 1; //SYM???else in_expr = 0; } if ( tk == TNAME || ( tk == TYPE && latype(yylval.t) ) || 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: //SYM???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; break; case NEW: ++in_new; break; } ret: //SYM???if ( tk == COMPL && lasttk == TSCOPE ) //SYM??? dtor_seen = lastval.pn; //SYM???else dtor_seen = 0; lasttk = tk; lastval = yylval; LDB(1,showQ("after"); fprintf(stderr,"lalex returning "); printok2(tk==ID?NAME:tk,yylval,curloc); ); // 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))); switch ( t ) { case ID: { Pname n = r.pn; r.s = n->string; DEL(n); break; } case LC: --bl_level; break; case RC: ++bl_level; break; } 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; } static 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 t%k, latok->tok%k", t, latok->tok ); 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; } } static TOK ptname_tscope(toknode *lt) { int nest = 0; // the LT has been fetched if (lt->tok != LT) error ('i', "ptname_tscope: a `<' token was expected"); TOK tk; for (toknode *t = lt;; t = get_next_token(t)) { // error('d',"ptname_tscope: t: %k t->next: %k",t->tok, t->next?t->next->tok:(add_tokens(),t->next->tok)); switch (t->tok) { case LT: ++nest; continue; case GT: if (--nest == 0) { t = get_next_token(t); if (t->tok == MEM) { if (t->next == 0) add_tokens(); if (t->next->tok == MUL) { t = t->next; tk = MEMPTR; } else tk = TSCOPE; latok = t; return tk; } else if (t->tok == MEMPTR || t->tok == TSCOPE) { latok = t; return t->tok; } else { latok = t; return PTNAME; } } continue; case SM: case LC: case RC: case EOFTOK: latok = t; return PTNAME; default: continue; }} } 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; if ( tk == PTNAME ) { if (latok->tok==TNAME) latok->tok=PTNAME; if (latok->next == 0) add_tokens(); tk2=ptname_tscope(latok->next); } else tk2 = lookahead(); switch( tk ) { case LP: if( tk2 == MUL || tk2 == AND || tk2 == MEMPTR || tk2 == PTNAME ) continue; else // T ( expr ) return 0; case MUL: case AND: case MEMPTR: while ( tk2==TYPE && la_sctype( tk2 ) ) // T ( * const ... // T ( * volatile ... tk2 = lookahead(); continue; case PTNAME: // T ( C<...> ... if ( tk2 == MEMPTR ) continue; return 0; case RP: case LB: // T (*) ... // T (*[ ... return 1; default: return 0; } // switch tk } // for(;;) } // undo SET_SCOPE() on qualified declarator immediately before returning // RESET_SCOPE() could be called several times, but only the first would // have an effect #define RESET_SCOPE(s) if(s>0) do UNSET_SCOPE(); while(--s); static int la_decl( int arg_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( %d)\n",arg_decl);); int tk, tk2 = latok->tok; int paren = 0; int ptr = 0; static int scopesets = 0; if ( tk2 != LP ) error('d',&latok->place,"la_decl(): latok ==%k -- '(' expected",tk2); for ( ; ; ) { tk = tk2; if ( tk == PTNAME ) { if (latok->tok==TNAME) latok->tok=PTNAME; if (latok->next == 0) add_tokens(); tk2=ptname_tscope(latok->next); } else tk2 = lookahead(); // fprintf(stderr,"\nla_decl:tk:%d %s tk2: %d %s", tk, keys[tk], tk2, keys[tk2]); fflush(stderr); switch( tk ) { case LP: if ( tk2 == RP ) { RESET_SCOPE(scopesets); return 0; } if ( paren && ptr==0 && arg_decl ) { // redundant parens in arg decl RESET_SCOPE(scopesets); return 0; } ++paren; ptr = 0; continue; case MUL: case AND: ptr = 1; if ( tk2==TYPE && la_sctype( tk2 )) { // T ( * const ... // T ( * volatile ... RESET_SCOPE(scopesets); return 1; } else { continue; } case MEMPTR: // T ( C :: * ... RESET_SCOPE(scopesets); return 1; case TSCOPE: if ( (tk2 == ID || tk2 == OPERATOR) && !arg_decl ) { // T ( * C :: id ... if ( latok->last->last->tok == TNAME && SET_SCOPE(latok->last->last->retval.pn) ) ++scopesets; continue; } else { // error RESET_SCOPE(scopesets); return 0; } case PTNAME: if ( tk2 == TSCOPE && !arg_decl ) { toknode* t = latok; tk = tk2; tk2 = lookahead(); if ( tk2 == ID || tk2 == OPERATOR ) { // T ( * C :: id ... if ( SET_SCOPE(t->retval.pn) ) ++scopesets; continue; } else { // error RESET_SCOPE(scopesets); return 0; } } else { RESET_SCOPE(scopesets); return tk2==MEMPTR; } } break; } if ( tk == RP || tk == LB ) { // T (*)() // T (*[])() RESET_SCOPE(scopesets); return 1; } if ( tk != ID && tk != OPERATOR ) { // T ( exp ) RESET_SCOPE(scopesets); return 0; } if (tk == ID && tk2 == RP && arg_decl && !ptr) { TOK nt = lookahead(); latok = latok->last; if (nt == LP) { RESET_SCOPE(scopesets); return 1; } } if ( ptr == 0 && arg_decl ) { // possible redundant parens in arg decl: T ( id ... RESET_SCOPE(scopesets); 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 { RESET_SCOPE(scopesets); return 0; } case LB: // OPERATOR [] tk2 = lookahead(); if ( tk2 == RB ) { tk2 = lookahead(); break; } else { RESET_SCOPE(scopesets); return 0; } default: // illegal operator RESET_SCOPE(scopesets); return 0; } } // if OPERATOR int allow_lp = 1; int allow_rp = 1; int pd = paren; for ( ; ; ) { tk = tk2; if (tk == LP || tk == LB || tk == RP) tk2 = lookahead(); //error('d',&latok->place,"ad: tk%k tk2%k alp %d arp %d",tk,tk2,allow_lp,allow_rp); // 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 ] ( ... RESET_SCOPE(scopesets); return 0; } // Current lookahead will be a decl if // the next lookahead is an arg decl if ( tk2 == RP && paren ) { tk2 = lookahead(); allow_lp = 0; allow_rp = 1; continue; } if ( tk2 == RP || tk2 == ENUM || tk2==AGGR || tk2==ELLIPSIS || tk2==TYPE && la_sctype( tk2 )) { // T ( * id () // T ( * id ) () RESET_SCOPE(scopesets); return 1; } if ( tk2 == TSCOPE ) { ts: do { //latok = latok->next; // :: tk2 = lookahead(); } while ( tk2 == TSCOPE ); if ( tk2 == TNAME ) { toknode* T = latok; if ( lookahead() == LP && !la_decl(1) ) { RESET_SCOPE(scopesets); return 0; } la_reset(T->next,T->retval.pn); RESET_SCOPE(scopesets); return 1; } RESET_SCOPE(scopesets); return 0; } if ( tk2 == PTNAME ) { if (latok->tok==TNAME) latok->tok=PTNAME; if (latok->next == 0) add_tokens(); tk2=ptname_tscope(latok->next); switch ( tk2 ) { case TSCOPE: goto ts; case TNAME: break; default: RESET_SCOPE(scopesets); return 0; } } if ( tk2 == TYPE || tk2 == TNAME ) { // T ( * id ) ( T2 ... if ( lookahead() == LP && !la_decl(1) ) { RESET_SCOPE(scopesets); return 0; } RESET_SCOPE(scopesets); return 1; } RESET_SCOPE(scopesets); return 0; case LB: if ( paren == 0 ) { RESET_SCOPE(scopesets); return 1; } if ( tk2 == RB ) { // T ( * id [] ... RESET_SCOPE(scopesets); return 1; } else { // T ( * id [ exp ] ... allow_lp = 0; allow_rp = 1; //XXXXX should balance []! while ( lookahead() != RB && latok->tok!=EOFTOK ); tk2 = lookahead(); continue; } case RP: // error ('d', "rp: allow_rp: %d paren: %d", allow_rp, paren ); if ( !allow_rp || !paren ) { // T ( * id ) ) RESET_SCOPE(scopesets); return 0; } // permit redundant parentheses else if ( tk2 == SM || tk2 == CM || tk2 == ASSIGN ) { RESET_SCOPE(scopesets); // if at local scope, interpret // T ( id ); as ctor call and // T ( * id ); as declaration if (!arg_decl && (pd==1 && !ptr) && !strict_opt) error('w', "T(id) (anachronism; will be declaration in future)"); return !arg_decl && (pd>(strict_opt?0:1) || ptr); } else if ( tk2 == RP && (bl_level-in_class_decl == 0)) { RESET_SCOPE(scopesets); return !arg_decl; } else { // T ( * id ) ... --paren; allow_lp = 1; allow_rp = (paren>0); continue; } default: RESET_SCOPE(scopesets); 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 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 level; 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: level = 0; break; case LC: level = 1; 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: ++level; if ( parens <= 0 ) goto eatf; continue; case RC: if ( --level < 0 ) { error(&latok->place,"unexpected '}'"); goto bad; } 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: 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; }