Plan 9 from Bell Labs’s /usr/web/sources/contrib/steve/root/sys/src/c++/cfront/lalex.C

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


/*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 <stdio.h>

#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<T>::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: <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 )
		//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 <class id, class id>
                           	|| (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<parameters>::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<int> :: 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;
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.