/*ident	"@(#)cls4:src/lex.c	1.1" */
/*******************************************************************************
 
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) 1991 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.

lex.c:
	lexical analyser based on pcc's and cpre's scanners
	modified to handle classes:
	new keywords:	class
			public
			call
			etc.
	names are not entered in the symbol table by lex()
	names can be of arbitrary length
	error() is used to report errors
	{} and () must match
	numeric constants are not converted into internal representation
	but stored as strings

****************************************************************************/

#include "cfront.h"
#include "yystype.h"
#include "size.h"
#include "tqueue.h"
#include "template.h"
#include "Block.h"

# define  CCTRANS(x) x

Blockdeclare(short)

// static data members definition
bool templ_compilation::in_progress=false;

char* strdup(const char* s1) 
/* string duplication
    returns pointer to a new string which is the duplicate of string
    pointed to by s1
    return 0 if new string can't be created
*/
{  
   char * s2;
 
   s2 = (char*)malloc((unsigned) strlen(s1)+1) ;
   return(s2==0 ? 0 : strcpy(s2,s1) );
}
 
#define copy_if_need_be(s)  ((templp->in_progress || templp->parameters_in_progress) ? strdup(s) : (s))

	/* lexical actions */

#define A_ERR	0		/* illegal character */
#define A_LET	1		/* saw a letter */
#define A_DIG	2		/* saw a digit */
#define A_1C	3		/* return a single character */
#define A_STR 	4		/* string */
#define A_CC	5		/* character constant */
#define A_BCD	6		/* GCOS BCD constant */
#define A_SL	7		/* saw a / */
#define A_DOT 	8		/* saw a . */
#define A_2C	9		/* possible two character symbol */
#define A_WS	10		/* whitespace (not \n) */
#define A_NL	11		/* \n */
#define A_LC	12		/* { */
#define A_RC	13		/* } */
#define A_L	14		/* ( */
#define A_R	15		/* ) */
#define A_EOF	16
#define A_ASS	17
#define A_LT	18
#define A_GT	19		/* > */
#define A_ER	20
#define A_OR	21
#define A_AND	22
#define A_MOD	23
#define A_NOT	24
#define A_MIN	25
#define A_MUL	26
#define A_PL	27
#define A_COL	28		/* : */
#define A_SHARP	29		/* # */
#define A_DOLL  30              /* $ */

	/* character classes */

# define LEXLET 01
# define LEXDIG 02
/* no LEXOCT because 8 and 9 used to be octal digits */
# define LEXHEX 010
# define LEXWS 020
# define LEXDOT 040

const FIRSTCHUNK = 8*1024-8;
const BUFCHUNK = 4*1024-8;

	/* text buffer */
static char inbuf[FIRSTCHUNK/*TBUFSZ*/];
static char* txtmax = &inbuf[FIRSTCHUNK/*TBUFSZ*/-1];
static char* txtstart = 0;
static char* txtfree = 0;

static struct buf* bufhead;
static buf* freebuf;
//static bufs;
static struct loc tloc;

struct buf {
	buf* next;
	char chars[BUFCHUNK];
//	buf() { next=bufhead; bufhead=this; }
};

new_buf(char c)
{
//fprintf(stderr,"new_buf %d\n",bufs++);
	buf* pbuf;
	if (freebuf) {
		pbuf = freebuf;
		freebuf = freebuf->next;
	}
	else
		pbuf = new buf;	// allocate and register new chunk
	pbuf->next = bufhead;
	bufhead = pbuf;

	if (BUFCHUNK < txtmax-txtstart) error('l',&tloc,"lexical token too long");

	// copy current token:
	char* p = txtstart;
	txtstart = txtfree = &pbuf->chars[0];
	while (p<txtmax) *txtfree++ = *p++;
	*txtfree++=c;
	txtmax = &pbuf->chars[BUFCHUNK-1];
	return 0;
}


#define pch(c) ((txtmax<=txtfree)?new_buf(c):(*txtfree++=c))
#define start_txt()	txtstart = txtfree
#define del_txt()	txtfree = txtstart

static int Nfile;// = 1;
static Block(Pchar) file_name;	// source file names
				// file_name[0] == src_file_name
				// file_name[0] == 0 means stdin
static int tcurr_file;		// current index in file_name

Linkage linkage = linkage_default; // linkage is default C++	
const LINKMAX = 10;
static Linkage lvec[LINKMAX] = { linkage_default };
static int lcount = 0;

void set_linkage(char* p)
{
	if (p==0 || *p == 0) {	// resume previous linkage
		if (lcount>0) linkage = lvec[--lcount];
	}
	else {
		if (LINKMAX<=++lcount) {
			error('l',"linkage directive nested too deep");
			--lcount;
		} else if (strcmp(p,"C")==0)
			lvec[lcount] = linkage = linkage_C;
		else if (strcmp(p,"C++")==0)
			lvec[lcount] = linkage = linkage_Cplusplus;
		else {
			error("%s linkage",p);
			--lcount;
		}
	}
}

FILE * out_file = stdout;
FILE * in_file = stdin;
FILE * debug_file;
FILE * pt_file;
FILE * dtpt_file;
static bit doneflag=0;
int first_file=0;

Ptable keyword_table;

static int p_level = 0;		/* number of unmatched ``(''s */
static int b_level = 0;		/* number of unmatched ``{''s */

# ifdef ibm

# define CSMASK 0377
# define CSSZ 256

# else

# define CSMASK 0177
# define CSSZ 128

# endif

static short lxmask[CSSZ+1];

int saved = 0;	/* putback character, avoid ungetchar */
static int lxtitle();

// overload rt;
inline YYSTYPE rt(char* x) { YYSTYPE y; y.s  = x;        return y; }
inline YYSTYPE rt(int   x) { YYSTYPE y; y.t  = x;        return y; }
inline YYSTYPE rt(loc   x) { YYSTYPE y; y.l  = x;        return y; }
inline YYSTYPE rt(void* x) { YYSTYPE y; y.pn = Pname(x); return y; }

#ifdef DBG
#define get(c)		(saved?error('i',&tloc,"getting twice: %d %c, lex.c line %d",saved,saved,__LINE__):0,c=getc(in_file))
#else
#define get(c)		(c=getc(in_file))
#endif
#define get1(c)		(saved?(c=saved,saved=0,c):(c=getc(in_file)))
#define unget(c)	{if(saved)error('i',&tloc,"unget: saved==%c",saved); saved = c; }
#define backup()	(ungetc(saved,in_file), saved = 0)

#define reti(a,b)	{ addtok(a, rt(b), tloc);          return a; }
#define retn(a,b)	{ addtok(a, rt((Pnode)b), tloc);   return a; }
#define rets(a,b)	{ addtok(a, rt(b), tloc);          return a; }
#define retl(a)		{ addtok(a, rt(tloc), tloc);       return a; }

// keys[] holds the external form for tokens with fixed representation 
// illegal tokens and those with variable representation have 0 entries
char* keys[MAXTOK+1];

static void 
new_key(char* s, TOK toknum, TOK yyclass)
/*
  make "s" a new keyword with the representation (token) "toknum"
  "yyclass" is the yacc token (for example new_key("int",INT,TYPE); )
  "yyclass==0" means yyclass=toknum;
*/
{
	Pname n = new name(s);

	keys[(toknum==LOC)?yyclass:toknum] = s;
	n = new name(s);
	Pname nn = keyword_table->insert(n,0);
	// if (Nold) error('i',&tloc,"keyword %sD twice",s);
	nn->base = toknum;
	nn->syn_class = (yyclass) ? yyclass : toknum;
	delete n;
}

const int keyword_count = 67;
static void 
ktbl_init()
/*
	enter keywords into keyword table for use by lex()
	and into keyword representation table used for output
*/
{
	keyword_table = new table(keyword_count,0,0);

	new_key("asm",ASM,0);
	new_key("auto",AUTO,TYPE);
	new_key("break",LOC,BREAK);
	new_key("case",LOC,CASE);
	new_key("continue",LOC,CONTINUE);
	new_key("char",CHAR,TYPE);
	new_key("do",LOC,DO);
	new_key("double",DOUBLE,TYPE);
	new_key("default",LOC,DEFAULT);
	new_key("enum",ENUM,0);
	new_key("else",LOC,ELSE);
	new_key("extern",EXTERN,TYPE);
	new_key("float",FLOAT,TYPE);
	new_key("for",LOC,FOR);
	new_key("goto",LOC,GOTO);
	new_key("catch",CATCH,CATCH);
	new_key("throw",THROW,THROW);
	new_key("try",TRY,TRY);
	new_key("if",LOC,IF);
	new_key("int",INT,TYPE);
	new_key("long",LONG,TYPE);
	new_key("return",LOC,RETURN);
	new_key("register",REGISTER,TYPE);
	new_key("static",STATIC,TYPE);
	new_key("struct",STRUCT,AGGR);
	new_key("sizeof",SIZEOF,0);
	new_key("short",SHORT,TYPE);
	new_key("switch",LOC,SWITCH);
	new_key("typedef",TYPEDEF,TYPE);
	new_key("unsigned",UNSIGNED,TYPE);
	new_key("union",UNION,AGGR);
	new_key("void",VOID,TYPE);
	new_key("while",LOC,WHILE);

	new_key("class",CLASS,AGGR);
	new_key("const",CONST,TYPE);
	new_key("delete",LOC,DELETE);
	new_key("friend",FRIEND,TYPE);
	new_key("inline",INLINE,TYPE);
	new_key("new",NEW,0);
	new_key("operator",OPERATOR,0);
	new_key("overload",OVERLOAD,TYPE);
	new_key("private",PRIVATE,PR);
	new_key("protected",PROTECTED,PR);
	new_key("public",PUBLIC,PR);
	new_key("signed",SIGNED,TYPE);
	new_key("template",TEMPLATE,0);
	new_key("this",THIS,0);
	new_key("virtual",VIRTUAL,TYPE);
	new_key("volatile",VOLATILE,TYPE);
}

loc last_line;
loc noloc = { 0, 0 };

void loc::putline()
{
	if (file==0 && line==0) return;
	if ( 0<=file && file <= Nfile ) {
		char* f = file_name[file];
		if (f==0) f = src_file_name;
		fprintf(out_file,line_format,line,f);
		last_line = *this;
	}
}

void loc::put(FILE* p)
{
	if ( 0<=file && file <= Nfile ) {
		char* f = file_name[file];
		if (f==0) f = src_file_name;
		fprintf(p,"%s:%d: ",f,line);
	}
}

char* curr_filename()
{
	return (file_name[curloc.file]);
}

static
int substr(const char* s1,const char* string2)
/* returns 0 is s1 is not in s2, 1 otherwise */
{

	int match = 0;
	const char* s2=string2;

	while (*s1 != '\0' && *s2 != '\0') {
		if (*s1 != *s2) {	
			s1++;
			if (match == 1)
				s2=string2;
			match = 0;
		}
		else  {
			s1++;
			s2++;
			if (*s2=='\0') return 1;
			match = 1;
		}
	}

	return 0;

}

void lxenter(register char* s, short m)
/* enter a mask into lxmask */
{
	register c;

	while( c= *s++ ) lxmask[c+1] |= m;

}


void lxget(register c, register m)
/*
	put 'c' back then scan for members of character class 'm'
	terminate the string read with \0
	txtfree points to the character position after that \0
*/
{
	pch(c);
	if ( (get1(c), lxmask[c+1]&m) ) {
		pch(c);
		while ( (get(c), lxmask[c+1]&m) ) pch(c);
	}
	unget(c);
	pch('\0');
}

struct LXDOPE {
	short lxch;	/* the character */
	short lxact;	/* the action to be performed */
	TOK   lxtok;	/* the token number to be returned */
} lxdope[] = {
#ifdef apollo
	'@',	A_ERR,	0,	/* illegal characters go here... */
#else
         '$',    A_DOLL, 0,
//       '$',    A_ERR,  0,      /* illegal characters go here... */
#endif
	'_',	A_LET,	0,	/* letters point here */
	'0',	A_DIG,	0,	/* digits point here */
	' ',	A_WS,	0,	/* whitespace goes here */
	'\n',	A_NL,	0,
	'"',	A_STR,	0,	/* character string */
	'\'',	A_CC,	0,	/* ASCII character constant */
	'`',	A_BCD,	0,	/* 'foreign' character constant, e.g. BCD */
	'(',	A_L,	LP,
	')',	A_R,	RP,
	'{',	A_LC,	LC,
	'}',	A_RC,	RC,
	'[',	A_1C,	LB,
	']',	A_1C,	RB,
	'*',	A_MUL,	MUL,
	'?',	A_1C,	QUEST,
	':',	A_COL,	COLON,
	'+',	A_PL,	PLUS,
	'-',	A_MIN,	MINUS,
	'/',	A_SL,	DIV,
	'%',	A_MOD,	MOD,
	'&',	A_AND,	AND,
	'|',	A_OR,	OR,
	'^',	A_ER,	ER,
	'!',	A_NOT,	NOT,
	'~',	A_1C,	COMPL,
	',',	A_1C,	CM,
	';',	A_1C,	SM,
	'.',	A_DOT,	DOT,
	'<',	A_LT,	LT,
	'>',	A_GT,	GT,
	'=',	A_ASS,	ASSIGN,
	'#',	A_SHARP, 0,
	EOF,	A_EOF,	EOFTOK
	};
/* note: EOF is used as sentinel, so must be <=0 and last entry in table */

static struct LXDOPE *lxcp[CSSZ+1];

void 
lex_init()
{
	register struct LXDOPE *p;
	register i;
	register char *cp;
	/* set up character classes */

	/* first clear lexmask */
	for(i=0; i<=CSSZ; i++) lxmask[i] = 0;

#ifdef apollo
	lxenter( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$", LEXLET );
#else
	lxenter( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_", LEXLET );
#endif
	lxenter( "0123456789", LEXDIG );
	lxenter( "0123456789abcdefABCDEF", LEXHEX );
		/* \013 should become \v someday; \013 is OK for ASCII and EBCDIC */
	lxenter( " \t\r\b\f\013", LEXWS );
	lxmask['.'+1] |= LEXDOT;

	/* make lxcp point to appropriate lxdope entry for each character */

	/* initialize error entries */

	for( i= 0; i<=CSSZ; ++i ) lxcp[i] = lxdope;

	/* make unique entries */

	for( p=lxdope; ; ++p ) {
		lxcp[p->lxch+1] = p;
		if( p->lxch < 0 ) break;
	}

	/* handle letters, digits, and whitespace */
	/* by convention, first, second, and third places */

	cp = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
	while( *cp ) lxcp[*cp++ + 1] = &lxdope[1];
	cp = "123456789";
	while( *cp ) lxcp[*cp++ + 1] = &lxdope[2];
	cp = "\t\b\r\f\013";
	while( *cp ) lxcp[*cp++ + 1] = &lxdope[3];

	file_name.reserve(0);
	file_name[0] = src_file_name;
	// set both curloc and tloc so curloc is valid at program startup
//	curloc.file = tloc.file = 0;
	curloc.line = tloc.line = 1;

	ktbl_init();
	lex_clear();
	saved = lxtitle();
}

void lex_clear()
{
	// delete extra buffers:
	buf* p = bufhead;	
	bufhead = 0;
//if (p) {
//fprintf(stderr,"lex_clear\n");
//bufs=0;
//}
	while (p) {
		buf* pp = p;
		p = p->next;
		pp->next = freebuf;
		freebuf = pp;
	}

	// re-set to static buffer:
	txtstart = txtfree = inbuf;
	txtmax = &inbuf[FIRSTCHUNK/*TBUFSZ*/-1];
}

int int_val(char hex)
{
	switch (hex) {
	case '0': case '1': case '2': case '3': case '4':
	case '5': case '6': case '7': case '8': case '9':
		return hex-'0';
	case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
		return hex-'a'+10;
	case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
		return hex-'A'+10;
	}
}

void hex_to_oct()
/*
	\x has been seen on input (in char const or string) and \ printed
	read the following hexadecimal integer and replace it with an octal
*/
{
	int i = 0;
	int c;
	get1(c);
	if (lxmask[c+1] & LEXHEX) {
		i = int_val(c);
		get(c);					// try for two
		if (lxmask[c+1] & LEXHEX) {
			i = (i<<4) + int_val(c);
			get(c);				// try for three
			if (lxmask[c+1] & LEXHEX)
				i = (i<<4) + int_val(c);
			else
				unget(c);
		}
		else
			unget(c);
	}
	else {
		error(&tloc,"hexadecimal digitE after \\x");
		unget(c);
	}

//	if (0377 < i) error('l',&tloc,"hexadecimal constant too large");
	i &= 0377;

	pch(('0'+(i>>6)));
	pch(('0'+((i&070)>>3)));
	pch(('0'+(i&7)));
}


char * chconst()
/*
	read a character constant into inbuf
*/
{
	register c;
	int nch = 0;

	pch('\'');

	for(;;) {
		char* p;
		char cc = 0;
		get1(c);
		switch (c) {
		case '\'':
			goto ex;
		case EOF:
			error(&tloc,"eof in char constant");
			goto ex;
		case '\n':
			error(&tloc,"newline in char constant");
			goto ex;
		case '\\':
			if (SZ_INT == nch++) error('l',&tloc,"char constant too long");
			pch(c);
			switch (get(c)){
			case '\n':
				++tloc.line;
			default:
				pch(c);
				break;
			case '4': case '5': case '6': case '7': // octal
				p = txtfree;
				cc = c-4;
			case '0': case '1': case '2': case '3':
				pch(c);
				get(c);  /* try for 2 */
				if( lxmask[c+1] & LEXDIG && c<'8'){
					pch(c);
					get(c);  /* try for 3 */
					if (lxmask[c+1] & LEXDIG && c<'8') {
						if (cc) *p = cc; // zap high bit
						pch(c);
					}
					else
						unget(c);
				}
				else
					unget(c);
				break;
			case 'x':	// hexadecimal
				hex_to_oct();
				break;
			};
			break;
		default:
			if (SZ_INT == nch++) error('l',&tloc,"char constant too long");
			pch(c);
		}
	}
ex:
	if(nch==0)
		error(&tloc,"empty char constant");
	pch('\'');
	pch('\0');
	return txtstart;
}

void lxcom()
/* process a "block comment" */
{
	register c;
	get1(c);
	for(;;get(c)) {
	xx: switch (c) {
	    case EOF:
		error('w',&tloc,"eof in comment");
		return;
	    case '\n':
		tloc.line++;
//		Nline++;
		break;
	    case '*':
		if (get(c) == '/') return;
		goto xx;
	    case '/':
		if (get(c) == '*') {
			error('w',&tloc,"``/*'' in comment");
			if (get(c) == '/') return;
		}
		goto xx;
	    }
	}
}


void linecom()
// process a "line comment"
{
	register c;

	get1(c);
#ifdef DBG
	if ( c=='@' && get(c)=='!' ) {
		while ( get(c) != '\n' && c != EOF ) pch(c);
		pch('\0');
		process_debug_flags(txtstart);
		del_txt();
	}
#endif
	for(;;get(c))
	switch (c) {
	case EOF:
		error('w',&tloc,"eof in comment");
		return;
	case '\n':
		tloc.line++;
//		Nline++;
		saved = lxtitle();
		return;
	}
}

char eat_whitespace()
{
	register c;
	get1(c);
	for(;;) {
	lx:
		switch (c) {
		case EOF:
			error('w',&tloc,"unexpected comment");
			return EOF;
		case '/':
			switch (get(c)) {
			case '*':
				lxcom();
				break;
			case '/':
				linecom();
				break;
			default:
				unget(c);
				return '/';
			}
			get1(c);
			goto lx;
		case '\n':
			++tloc.line;
			c = lxtitle();
			goto lx;
		case ' ':
		case '\t':
			break;
		default:
			return c;		
		}
		get(c);
	}	
}

void get_string()
{
	int lxchar;

	for(;;)
	switch (get1(lxchar)) {
 	case '\\':
  		pch('\\');
 		switch (get(lxchar)){
 		case '\n':
 			++tloc.line;
 		default:
  			pch(lxchar);
 			break;
 		case 'x':	// hexadecimal
 			hex_to_oct();
 			break;
 		};
  		break;
	case '"':
        {       char* p = txtstart;     // eat_whitespace() moves txtstart
                if ((lxchar = eat_whitespace()) == '"') {
                        // string catenation, break with
                        // newline to avoid merging characters
                        // (e.g. "\xAB" "C")
                        pch('\\');
                        pch('\n');

                        continue;       // eat '\"' and carry on
                };

                txtstart = p;
		unget(lxchar);
		pch(0);
		return;
        }
	case '\n':
		error(&tloc,"newline in string");
		pch(0);
		return;
	case EOF:
		error(&tloc,"eof in string");
		pch(0);
		return;
	default:
		pch(lxchar);
	}
}

TOK tlex()
{
	TOK ret;
	Pname n;

//	Ntoken++;

	for(;;) {
		register lxchar;
		register struct LXDOPE *p;

		start_txt();

		get1(lxchar);

 		if (lxchar+1 >= CSSZ )
		 error( "illegal input character encountered: %d", lxchar );

		switch( (p=lxcp[lxchar+1])->lxact ){

		case A_1C:	// eat up a single character, and return an opcode 
			reti(p->lxtok,p->lxtok);

		case A_EOF:
			if (p_level || b_level+lcount)
				error(&tloc,"'%s' missing at end of input",(b_level+lcount) ? "}" : ")");
		
			reti(EOFTOK,0);

		case A_SHARP:
			// cope with header file not ended with '\n'
			unget('#');
			saved = lxtitle();
			continue;

		case A_ERR:
		{	if (' '<=lxchar && lxchar<='~')	// ASCII printable
				error(&tloc,"illegal character '%c' (ignored)",lxchar);
			else
				error(&tloc,"illegal character%o (ignored)",lxchar);
			continue;
		}

		case A_DOLL:
                { // lex a name of the for $id for template tree formals
/* SBL:  can remove all this: restore to previous */
                	// Pname fn ;
                        // lxget( lxchar, LEXLET|LEXDIG ) ;

                        // if (!templp->in_progress ||  !txtstart[1]) {
                        // no name string immediately follows, treat it                                  
                          // like an illegal character
                          error(&tloc,"illegal character%o (ignored)",lxchar);                               
                          continue;
                        // }  
/* SBL: remove
                        txtstart++ ;
                        if(fn=templ_compilation::tree_parameter(txtstart)) {                              
                        	switch (fn->n_template_arg) {
                          		case template_expr_tree_formal:
                            		// retain the $ in the name
                            			retn(ID, strdup(--txtstart)) ;

                          		case template_stmt_tree_formal:
                            			retn(SM_PARAM, fn) ;
                          	}  
                        }  
                        error(&tloc,"%s wasn't a statement or expression formal", txtstart);
                        rets(ID, copy_if_need_be(txtstart));
*/
                }

		case A_LET:	// collect an identifier and check for keyword
		{
			char ll;
			switch (ll = lxchar) {
		//	case 'l':
			case 'L':
				switch (get(lxchar)) {
				case '\'':
					error('s',&tloc,"wide character constant");
					unget(lxchar);
					continue;
				case '"':
					error('s',&tloc,"wide character string");
					unget(lxchar);
					continue;
				}
				unget(lxchar);
				lxchar = ll;
			}
		}

			lxget( lxchar, LEXLET|LEXDIG );
                        /* look for a keyword or a global type */
			if ( n = keyword_table->look(txtstart,0) ) /* keyword */
			{
				TOK x;
				del_txt();
				switch (x=n->base) {
				case TNAME:
					error('i',&tloc,"TN%n in keyword_table",n);
					break;
					//rets(ID,n->string);
				case LOC:
					retl(n->syn_class);
				case EXTERN:
					if ((lxchar = eat_whitespace()) == '\"') {
						// linkage directive
						get_string();
						rets(LINKAGE,txtstart);
					}
					if ( saved ) backup();
					unget(lxchar);
					reti(TYPE,EXTERN);
				case CATCH:
					{ static int warned = 0;
					  if ( warned == 0 ) {
						error('s',&tloc,"%k",x);
						warned = 1;
					  }
					}
					reti(n->syn_class,x);
				case THROW:
					{ static int warned = 0;
					  if ( warned == 0 ) {
						error('s',&tloc,"%k",x);
						warned = 1;
					  }
					}
					reti(n->syn_class,x);
				case TRY:
					{ static int warned = 0;
					  if ( warned == 0 ) {
						error('s',&tloc,"%k",x);
						warned = 1;
					  }
					}
					reti(n->syn_class,x);
				default:
					reti(n->syn_class,x);
				}
			}
			if (warning_opt && substr(txtstart,"__") && (!substr(txtstart,"___")))
				error('w',&tloc,"identifier %s contains a \"__\"",txtstart);
			// rets(ID,txtstart);
                        rets(ID, copy_if_need_be(txtstart)) ;

		case A_DIG:

			ret = ICON;

			if (lxchar=='0') {
				int pkchar;
				get(pkchar);
				if(pkchar=='x' || pkchar=='X') {	// hex
					pch(lxchar);
					lxget(pkchar,LEXHEX);
					txtfree--;
					if (txtfree-txtstart<3) // minimum "0Xd\0"
						error(&tloc,"hex digitX after \"0x\"");
					get1(lxchar);
					goto getsuffix;
				}
				unget(pkchar);
			}

			lxget(lxchar,LEXDIG);
			txtfree--;

			if (get1(lxchar) == '.') {
		getfp:
				lxget('.', LEXDIG );
				txtfree--;
				ret = FCON;
				get1(lxchar);
			};
		
			if (lxchar=='e' || lxchar=='E') {
				pch(lxchar);
				get(lxchar);
				if(lxchar=='-' || lxchar=='+') {
					pch(lxchar);
					get(lxchar);
				}
				if (lxmask[lxchar+1] & LEXDIG) {
					lxget( lxchar, LEXDIG );
					txtfree--;
					get1(lxchar);
				}
				else
					error(&tloc,"missing exponent digits?");
				ret = FCON;
			};

			if(*txtstart=='0' && ret==ICON) {
				char *bch = txtstart;
				while (++bch <= txtfree) {
					if(*bch=='8' || *bch=='9')
						error(&tloc,"%c used as octal digit",*bch);
				}
			}

		getsuffix:
			switch (lxchar) {
			case 'f':
			case 'F':
				if (ret==ICON)
					error(&tloc,"%c suffix for integer constant",lxchar);
				else
					pch(lxchar);
				break;
			case 'u':
			case 'U':
				if (ret==FCON) {
					error(&tloc,"%c suffix for floating constant",lxchar);
					break;
				}
				pch(lxchar);
				switch(get(lxchar)) {
				case 'l':
				case 'L':
					pch(lxchar);
					break;
				default:
					saved=lxchar;
					break;
				}
				break;
			case 'l':
			case 'L':
				pch(lxchar);
				if (ret==FCON) {
					break;
				}
				switch(get(lxchar)) {
				case 'u':
				case 'U':
					pch(lxchar);
					break;
				default:
					saved=lxchar;
					break;
				}
				break;
			default:
				saved = lxchar;
				break;
			};

			if(*txtstart=='0' && txtfree-txtstart==1)
				reti(ZERO,0);	// plain zero

			pch(0);
			rets(ret,txtstart);


		case A_DOT:
			switch (get(lxchar)) {
			case '.':	// look for ellipsis
				if (get(lxchar) != '.') {
					error(&tloc,"token .. ?");
					saved = lxchar;
				}
				reti(ELLIPSIS,0);
			case '*':
				reti (REFMUL,DOT);
			}

			if (lxmask[lxchar+1] & LEXDIG){// look for floating constant
				unget(lxchar);
				goto getfp;
			}
			saved = lxchar;
			reti(DOT,0);

		case A_STR:
			/* save string constant in buffer */
			get_string();
			rets(STRING,txtstart);

		case A_CC:
			/* character constant */
			rets(CCON,chconst());

		case A_BCD:
			{
				register i;
				int j;
	
				pch('`');
	
				for (i=0; i<7; ++i) {
					pch(get(j));
					if (j == '`' ) break;
				}
				pch(0);
				if (6<i)
					error('l',&tloc,"bcd constant exceeds 6 characters" );
				rets(CCON,txtstart);
			}

		case A_SL:	/* / */
			switch (get(lxchar))  {
			case '*':
				lxcom();
				break;
			case '/':
				linecom();
				break;
			case '=':
				reti(ASOP,ASDIV);
			default:
				saved = lxchar;
				reti(DIVOP,DIV);
			}

		case A_WS:
			continue;

		case A_NL:
			++tloc.line;
//			Nline++;
			saved = lxtitle();
			continue;

		case A_LC:
			if (BLMAX <= b_level++) {
				error('l',&tloc,"blocks too deeply nested");
				ext(3);
			}
			retl(LC);

		case A_RC:
			if (lcount+b_level-- <= 0) {
				error(&tloc,"unexpected '}'");
				b_level = 0;
			}
			retl(RC);

		case A_L:
			p_level++;
			reti(LP,0);

		case A_R:
			if (p_level-- <= 0) {
				error(&tloc,"unexpected ')'");
				p_level = 0;
			}
			reti(RP,0);

		case A_ASS:
			switch (get(lxchar)) {
			case '=':
				reti(EQUOP,EQ);
			default:
				saved = lxchar;
				reti(ASSIGN,ASSIGN);
			}

		case A_COL:
			switch (get(lxchar)) {
			case ':':
				reti(MEM,0);
			case '=':
				error(&tloc,"':=' is not a c++ operator");
				reti(ASSIGN,ASSIGN);
			default:
				saved = lxchar;
				reti(COLON,COLON);
			}
		case A_NOT:
			switch (get(lxchar)) {
			case '=':
				reti(EQUOP,NE);
			default:
				saved = lxchar;
				reti(NOT,NOT);
			}
		case A_GT:
			switch(get(lxchar)) {
			case '>':
				switch (get(lxchar)) {
				case '=':
					reti(ASOP,ASRS);
					break;
				default:
					saved = lxchar;
					reti(SHIFTOP,RS);
				}
			case '=':
				reti(RELOP,GE);
			default:
				saved = lxchar;
				reti(GT,GT);
			}
		case A_LT:
			switch (get(lxchar)) {
			case '<':
				switch (get(lxchar)) {
				case '=':
					reti(ASOP,ASLS);
				default:
					saved = lxchar;
					reti(SHIFTOP,LS);
				}
			case '=':
				reti(RELOP,LE);
			default:
				saved = lxchar;
				reti(LT,LT);
			}
		case A_AND:
			switch (get(lxchar)) {
			case '&':
				reti(ANDAND,ANDAND);
			case '=':
				reti(ASOP,ASAND);
			default:
				saved = lxchar;
				reti(AND,AND);
			}
		case A_OR:
			switch (get(lxchar)) {
			case '|':
				reti(OROR,OROR);
			case '=':
				reti(ASOP,ASOR);
			default:
				saved = lxchar;
				reti(OR,OR);
			}
		case A_ER:
			switch (get(lxchar)) {
			case '=':
				reti(ASOP,ASER);
			default:
				saved = lxchar;
				reti(ER,ER);
			}
		case A_PL:
			switch (get(lxchar)) {
			case '=':
				reti(ASOP,ASPLUS);
			case '+':
				reti(ICOP,INCR);
			default:
				saved = lxchar;
				reti(PLUS,PLUS);
			}
		case A_MIN:
			switch (get(lxchar)) {
			case '=':
				reti(ASOP,ASMINUS);
			case '-':
				reti(ICOP,DECR);
			case '>':
				if (get(lxchar) == '*')
					{reti(REFMUL,REF);}
				else
					saved = lxchar;
				reti(REF,REF);
			default:
				saved = lxchar;
				reti(MINUS,MINUS);
			}
		case A_MUL:
			switch (get(lxchar)) {
			case '=':
				reti(ASOP,ASMUL);
			case '/':
				error('w',&tloc,"*/ not as end of comment");
			default:
				saved = lxchar;
				reti(MUL,MUL);
			}
		case A_MOD:
			switch (get(lxchar)) {
			case '=':
				reti(ASOP,ASMOD);
			default:
				saved = lxchar;
				reti(DIVOP,MOD);
			}
		default:
			{error('i',&tloc,"lex act==%d getc()->%d",p,lxchar);}

		}

		error('i',&tloc,"lex, main switch");
	}

}

int lxtitle()
/*
	called after a newline; set linenumber and file name
*/
{
    register c;

    for(;;) {
	get1(c);
	switch (c) {
	default:		// e.g. not '\n', not '#'
		return c;
	case '\n':
		tloc.line++;
//		Nline++;
	ll:
		break;
	case '#': 	/* # lineno "filename" */
	{	int cl = tloc.line;
		tloc.line = 0;
		for(;;) 
		switch (get(c)) {
		case '"':
			start_txt();
			for(;;)
			switch (get(c)) {
			case '"':
				pch('\0');

				while (get(c) != '\n') ;  // skip to eol.. ignore anything more

				if (*txtstart) {	// stack file name
					char* fn;
					if ( (fn=file_name[tcurr_file])
					&& (strcmp(txtstart,fn)==0) ) {
						//new line, same file: ignore
					}
 					else {
 						// first look back in the stack
 						int found = 0;
 						int findex = Nfile;
 						for (; findex >= 0; findex--) {
 							if ( findex == tcurr_file ) continue;
 							fn = file_name[findex];
 							if (fn != 0 && strcmp(txtstart,fn)==0) {
 								found = 1;
 								tcurr_file = findex;
 								break;
 							}
 						}
 						if (found == 0) {
 							// not an old file; push
 							char* p = new char[txtfree-txtstart+1];
 							(void) strcpy(p,txtstart);
 							tcurr_file = ++Nfile;
							file_name.reserve(Nfile);
 							file_name[Nfile] = p;
 						}

					}
				}
				else {	// no name .. back to the original .c file: ""
					tcurr_file = 0;
				}
				del_txt();
				tloc.file = tcurr_file;
				if (dtpt_opt && doneflag==0) {
					first_file=tcurr_file;
					doneflag=1;
				}

				goto ll;
			case '\n':
				error(&tloc,"unexpected end of line on '# line'");
			default:
				pch(c);
			}
		case ' ':
			break;

		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
			tloc.line = tloc.line*10+c-'0'; 
			break;

		case 'l':	// look for "#line ..." and then ignore "line"
			if (get(c)=='i' && get(c)=='n' && get(c)=='e') break;
               case '\n':
                        tloc.putline();
                        goto ll;

		default: // pass #rubbish through
			tloc.line = cl;
			pch('#');
			pch(c);
			while (get(c) != '\n') pch(c);
			pch('\0');
			fprintf(out_file,"\n%s\n",txtstart);
			start_txt();
			tloc.line++;
//			Nline++;
			goto ll;
		}
	}
	}
    }
}
