static char rcsid[] = "$Id: C-lex.c,v 1.32 2000/10/12 04:12:07 msato Exp $";
/* 
 * $RWC_Release: Omni-1.6 $
 * $RWC_Copyright:
 *  Omni Compiler Software Version 1.5-1.6
 *  Copyright (C) 2002 PC Cluster Consortium
 *  
 *  This software is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License version
 *  2.1 published by the Free Software Foundation.
 *  
 *  Omni Compiler Software Version 1.0-1.4
 *  Copyright (C) 1999, 2000, 2001.
 *   Tsukuba Research Center, Real World Computing Partnership, Japan.
 *  
 *  Please check the Copyright and License information in the files named
 *  COPYRIGHT and LICENSE under the top  directory of the Omni Compiler
 *  Software release kit.
 *  
 *  
 *  $
 */
/* lexical analyzer */

/* buffer size for lexical analyzer */
#define LEX_BUFSIZE 4096

char    yytext[LEX_BUFSIZE];

extern struct keyword_entry {
    char   *kw_name;
    enum symbol_type kw_type;
    int     kw_value;
} keyword_table[];

expr    constant_1;
expr    error_expr;

int     current_nest_level = -1;
list    yy_name_list = NULL;
expr    nest_statement_stack[MAX_NEST_LEVEL];
list    yy_name_list_stack[MAX_NEST_LEVEL];
list    pragma_name_list = NULL;

static int st_elm = 0;	/* 0: normal, 1: struct access */

/* value for S_GCC, gcc extension */
#define GCC_ATTRIBUTE 0
#define GCC_EXTENSION 1
#define GCC_INLINE 2
#define GCC_ASM 3

/* initialize parser */
void initialize_lex()
{
    struct keyword_entry *kp;
    SYMBOL  sp;

	/* internal keywords */
    for (kp = keyword_table ; kp->kw_name != NULL ; kp++) {
	sp = find_symbol(kp->kw_name);
	sp->s_type = kp->kw_type;
	sp->s_value = kp->kw_value;
    }

    /* make constant */
    constant_1 = make_enode(INT_CONSTANT, (void *)1);
    error_expr = make_enode(ERROR_NODE, (void *)0);

    current_nest_level = -1;
    yy_name_list = NULL;
}

static void save_statement_list()
{
    ++current_nest_level;
    if ( current_nest_level >= MAX_NEST_LEVEL )
	fatal("too nested compound statement");
    nest_statement_stack[current_nest_level] = list0(LIST);
    yy_name_list_stack[current_nest_level] = yy_name_list;
}

static void put_statement_list(expr x)
{
    list_put_last(nest_statement_stack[current_nest_level], x);
}

static expr restore_statement_list()
{
    if ( current_nest_level < 0 ){
	yyerror("syntax error");
	current_nest_level = -1;
	return (error_expr);
    }
    else {
	yy_name_list = yy_name_list_stack[current_nest_level];
	return ( nest_statement_stack[current_nest_level--] );
    }
}

static void mark_type_name(expr decl_list, /* (LIST (LIST declarator initializer) ...) */
	       enum expr_code code /* IDENT or TYPENAME_IDENT */)
{
    list    lp;
    expr    decl, ep;

    if ( decl_list == NULL )
	return;			/* error recovery */

	/* find declarator name from list */
    for( lp = EXPR_LIST(decl_list) ; lp != NULL ; lp = LIST_NEXT(lp) ){
	decl = LIST_ITEM(lp);
	if ( EXPR_CODE(decl) == LIST )
	    decl = EXPR_ARG1(decl);
	while ( decl != NULL && EXPR_CODE(decl) != IDENT )
	    decl = EXPR_ARG1(decl);
	if ( decl == NULL )
	    continue;

	/* push it(declarator) */
	ep = make_enode(code, (void *)EXPR_SYM(decl));
	yy_name_list = cons_list(ep, yy_name_list);
    }
}

static int is_TYPENAME_IDENT(SYMBOL  sp)
{
    list    lp;

    for( lp = yy_name_list ; lp != NULL ; lp = LIST_NEXT(lp) ){
	if ( EXPR_SYM(LIST_ITEM(lp)) == sp )
	    return (EXPR_CODE(LIST_ITEM(lp)) == TYPENAME_IDENT);
    }
    return (FALSE);	/* unknown */
}

#define MAX_LINE_LEN	1024
static char line_buf[MAX_LINE_LEN];
static char *linep = NULL;
static int have_peekc = FALSE;
static int line_peekc;
lineno_info *current_line;
static int line_count=0;

#define GETCH()		lex_getc()
#define UNGETCH(c)	(have_peekc++, line_peekc = (c))

char    title_file_name[FILE_NAME_LEN];	/* processed file name (buffer) */

/* read line into buffer and return single char */
static int     lex_getc()
{
    int   ln, i, fid;
    char   *cp;

    if ( have_peekc ){
	have_peekc = FALSE;
	return (line_peekc);
    }

    if (linep == NULL || *linep == '\0') {
next_line:
	if ( fgets(line_buf, MAX_LINE_LEN, source_file) == NULL )
	    return (EOF);
	line_count++;
	linep = line_buf;

	if (debug_expr_flag)
	    printf("%3d:%s", current_line->ln_no, line_buf);

	for( i = 0 ; isspace((int)line_buf[i]) ; )
	    i++;
	/* check line nubmer, '# nnn "file"' */
	if (line_buf[i+0] == '#') {
	    linep = &line_buf[i+1];
	    while( *linep == ' ' || *linep == '\t' || *linep == '\b' )
		linep++;	/* skip space */
#ifdef USE_PRAGMA
	    if ( strncmp(linep, "pragma ", 7) == 0 ||
			strncmp(linep, "pragma	", 7) == 0 ){
		enum pragma_syntax syn;
		yylval.val = parse_pragma(linep+7,&syn);
		current_line = new_line_info(current_line->file_id,
					   current_line->ln_no+1);
		linep = NULL; /* clear line */
		switch(syn){
		case SYN_PRAGMA_DECL:
		    return(PRAGMA_DECL);
		case SYN_PRAGMA_EXEC:
		    return(PRAGMA_EXEC);
		case SYN_PRAGMA_PREFIX:
		    return(PRAGMA_PREFIX);
		case SYN_PRAGMA_POSTFIX:
		    return(PRAGMA_POSTFIX);
		default:
		    break;
		}
		goto next_line;	/* error ? */
	    }
#endif
	    if (isdigit((int)(*linep)) ||
		strncmp(linep, "line ", 5) == 0  /* #line */){

		/*  a special care for #line  */
		if (strncmp(linep, "line ", 5) == 0) {
		    linep += 5;
		    while( *linep == ' ' || *linep == '\t' || *linep == '\b' )
			linep++;	/* skip space */
		    if (!isdigit((int)(*linep)))
			error("syntax error");
		}

		fid = current_line->file_id;
		ln = 0;	/* line # reset default '0' */
		while( isdigit((int)(*linep)))
		    ln = ln * 10 + *linep++ - '0';

		while( *linep == ' ' )
		    linep++;	/* skip space, again */
		if ( *linep == '"' ){	/* parse file name */
		    linep++;
		    cp = title_file_name;	/* rewrite buffer file name */
		    while(*linep != '"' && *linep != '\n' )
			*cp++ = *linep++;
		    *cp = '\0';
		    fid = get_file_id(title_file_name);

		    /* if first line is #line, then set it
		       as original source file name */
		    if(line_count == 1)
			source_file_name = strdup(title_file_name);
		    /* enter file name end */
		}
		current_line = new_line_info(fid,ln);
	    } else  {
		if(strncmp(linep,"ident",5) == 0) { /* sun pramga */ }
		else warning("unknown directive");
		current_line = new_line_info(current_line->file_id,
					   current_line->ln_no+1);
	    }
	    goto next_line;
	}
    }

    if ( *linep == '\n' ){	/* blank line */
	current_line = new_line_info(current_line->file_id,
				   current_line->ln_no+1);
    }

    return (*linep++);
}

/* VARARGS */
static int yylast;

/* error printing routine in parser */
static void yyerror(s)
    char   *s;
{
    char   *cp;

    
    if (yylast != EOF && yylast < 0x7F)
	error("%s at or near '%c'", s, yylast);
    else
	switch (yylast) {
	default:	/* search keyword table */
	    if ((cp = search_keyword_by_token(yylast)) != 0)
		error("%s at or near word '%s'", s, cp);
	    else
		error(s);
	    break;
	case CLASS:
	    error("%s at keyword '%s'", s, yytext);
	    break;
	case TYPE:
	    error("%s at type keyword '%s'", s, yytext);
	    break;
	case IDENTIFIER:
	    error("%s at symbol '%s'", s, yytext);
	    break;
	case CONSTANT:
	    error("%s at constant '%s'", s, yytext);
	    break;
	case EOF:
	    error("%s at EOF",s);
	    break;
	}
}

/* lexical analyer */
static int yylex()
{
    yylast = yylex0();
#ifdef LEX_DEBUG
    printf("%c[%d]", (yylast < ' ' || yylast >= 0xFF) ? ' ' : yylast, yylast);
    fflush(stdout);
#endif
    if ( yylast != '.' && yylast != STREF )
	st_elm = 0;	/* reset */
    return (yylast);
}

static int yylex0()
{
    int    ch;

    for( ; ; ){
	yylval.lineno = current_line;  /* defalut */
	switch( ch = GETCH() ){
	case ' ':	/* white space */
	case '\t':
	case '\b':
	case '\r':
	case '\f':
	    continue;

	case '\n':	/* new-line */
	    continue;

	case EOF:	/* ? */
	case PRAGMA_DECL:
	case PRAGMA_EXEC:
	case PRAGMA_PREFIX:
	case PRAGMA_POSTFIX:

	case '(':
	case ')':
	case '{':
	case '}':
	case '[':
	case ']':
	case '*':
	case '%':
	case '?':
	case ':':
	case ';':
	case '^':
	case '~':
	case ',':
	    /* single charactor */
	    return (ch);

	case '|':		/* | and || */
	    if ((ch = GETCH()) == '|')
		return (OROR);
	    UNGETCH(ch);
	    return ('|');

	case '&':		/* & and && */
	    if ((ch = GETCH()) == '&')
		return (ANDAND);
	    UNGETCH(ch);
	    return ('&');

	case '<':		/* < and << */
	    switch (ch = GETCH()) {
	    case '<':
		return (LSHIFT);
	    case '=':
		return (LE);
	    }
	    UNGETCH(ch);
	    return (LT);

	case '>':		/* > and >> and >= */
	    switch (ch = GETCH()) {
	    case '>':
		return (RSHIFT);
	    case '=':
		return (GE);
	    }
	    UNGETCH(ch);
	    return (GT);

	case '!':		/* ! and != */
	    if ((ch = GETCH()) == '=')
		return (NOTEQ);
	    UNGETCH(ch);
	    return ('!');

	case '=':		/* = and == */
	    if ((ch = GETCH()) == '=')
		return (EQEQ);
	    UNGETCH(ch);
	    return ('=');

	case '+':		/* + and ++ */
	    if ((ch = GETCH()) == '+')
		return (PLUSPLUS);
	    UNGETCH(ch);
	    return ('+');

	case '-':		/* - and -- and -> */
	    switch (ch = GETCH()) {
	    case '-':
		return (MINUSMINUS);
	    case '>':	/* -> */
		st_elm = 1;	/* struct access */
		return (STREF);
	    }
	    UNGETCH(ch);
	    return ('-');

	case '/':
	    if ((ch = GETCH()) == '*') {
		/* scan comment */
		for (;;) {
		    ch = GETCH();
		    if (ch == '*' && (ch = GETCH()) == '/')
			break;
		    if (ch == EOF) {
			error("unexpected EOF");
			return (EOF);
		    }
		}
		continue;
	    }
	    else if (ch == '/') {	/* new style comment */
		/* scan comment */
		for (;;) {
		    ch = GETCH();
		    if (ch == '\n')
			break;
		    if (ch == EOF) {
			error("unexpected EOF");
			return (EOF);
		    }
		}
		continue;
	    }
	    UNGETCH(ch);
	    return ('/');

	case '.':
	    ch = GETCH();
	    if (ch >= '0' && ch <= '9') {
		UNGETCH(ch);	/* peek */
		return (read_number('.'));
	    }
	    else if (ch == '.') {
		ch = GETCH();
		if (ch == '.')
		    return (ELLIPSIS);	/* ... */
		yyerror("syntax error");
		continue;
	    }
	    st_elm = 1;	/* maybe struct access */
	    UNGETCH(ch);
	    return ('.');

	case '0':
	case '1':
	case '2':
	case '3':
	case '4':
	case '5':
	case '6':
	case '7':
	case '8':
	case '9':
	    return (read_number(ch));

	case '"':
	    return (read_string_constant('"'));

	case '\'':
	    return (read_char_constant('\''));

	default:	/* identifier */
	    if ( !isalpha(ch) ){
		error("illegal character: 0x% in hex", ch);
		break;
	    }
	case '_':
	case '$':
	    ch = read_identifier(ch);
	    if(ch == 0) break;	/* ingore */
	    return ch;
	}
    }
}

/* collect an identifier, check for reserved word, and return */
static int read_identifier(char ch) 	/* first char */
{
    char   *cp;
    SYMBOL  sp;

    cp = yytext;
    do {
	*cp++ = ch;
	ch = GETCH();
	if ( cp >= &yytext[LEX_BUFSIZE - 1] ){
	    fatal("too long identifier");
	    break;
	}
    } while (isalnum((int)ch) || ch == '_' || ch == '$');

    UNGETCH(ch);		/* push back */
    *cp = '\0';
    sp = find_symbol(yytext);

    switch (sp->s_type) {
    case S_KEYWORD:
	return (sp->s_value);

    case S_TYPE:
	yylval.val = make_enode(BASIC_TYPE_NODE, (void *)((_omAddrInt_t)sp->s_value));
	return (TYPE);

    case S_CLASS:
	yylval.val = make_enode(STORAGE_CLASS_NODE, (void *)((_omAddrInt_t)sp->s_value));
	return (CLASS);

    case S_QUAL:
	yylval.val = make_enode(TYPE_QUAL_NODE, (void *)((_omAddrInt_t)sp->s_value));
	return (TYPE_QUALIFIER);

    case S_IDENT:
	/* must be checked if it is TYPENAME by TYPEDEF. */
	if ( st_elm == 0 && is_TYPENAME_IDENT(sp) ){
	    yylval.val = make_enode(TYPENAME_IDENT, (void *)sp);
	    return (TYPENAME);
	}
	yylval.val = make_enode(IDENT, (void *)sp);
	return (IDENTIFIER);

    case S_PARAM:  /* for tea pramga */
	yylval.val = pragma_name_value(sp);
	return (CONSTANT);

    case S_GCC:	/* gcc extension */
	switch(sp->s_value){
	case GCC_INLINE:
	    yylval.val = make_enode(TYPE_QUAL_NODE, 
				    (void *)((_omAddrInt_t)QUAL_INLINE));
	    return (TYPE_QUALIFIER);
	case GCC_ATTRIBUTE:
	case GCC_ASM:
	{ 
	    int lev;
	    while((ch = GETCH()) != '('){
		if(ch == EOF){
		    error("unexpected EOF");
		    return (EOF);
		}
	    }
	    lev = 1;
	    while(lev != 0){
		switch(ch = GETCH()){
		case '(': lev++; break;
		case ')': lev--; break;
		case EOF:
		    error("unexpected EOF");
		    return (EOF);
		}
	    }
	}
	case GCC_EXTENSION: 
	    break;
	}
	return 0;	/* ignore */
    default:
	fatal("read_identifier");
	return 0;
    }
}

static int read_number(int   ch)	/* first char */
{
    char   		*cp;
    long int		value, h_value;
    int     		radix, us;
    expr		val;

    cp = yytext;
    radix = 10;

    if ( ch == '0' ){
	ch = GETCH();
	if ( ch == 'x' || ch == 'X' ){    /* HEX */
	    radix = 16;
	    for( ; ; ){
		ch = GETCH();
		if ( !isxdigit(ch) )
		    goto ret_INT;
		*cp++ = ch;
	    }
	}
	if ( ch == '.' )
	    goto read_floating;
	if ( !(ch >= '0' && ch <= '7') )
	    goto ret_INT;
	/* octal */
	radix = 8;
	for( ; ; ){
	    *cp++ = ch;
	    ch = GETCH();
	    if ( !(ch >= '0' && ch <= '7') )
		goto ret_INT;
	}
	/* NOT REACHED */
    }

    /* else decimal or floating */
read_floating:
    while( isdigit(ch) ){
	*cp++ = ch;
	ch = GETCH();
    }
    if ( ch != '.' && ch != 'e' && ch != 'E' && ch != 'f' && ch != 'F' )
	goto ret_INT;
    /* floating */
    if ( ch == '.' ){
	*cp++ = ch;
	/* reading floating */
	ch = GETCH();
	while( isdigit(ch) ){
	    *cp++ = ch;
	    ch = GETCH();
	}
    }

    if ( ch == 'e' || ch == 'E' ){
	*cp++ = 'e';
	ch = GETCH();
	if ( ch == '+' || ch == '-' ){
	    *cp++ = ch;
	    ch = GETCH();
	}
	while( isdigit(ch) ){
	    *cp++ = ch;
	    ch = GETCH();
	}
    }

    if ( ch == 'f' || ch == 'F' ) {
	ch = GETCH ();
    }

    UNGETCH(ch);
    *cp = '\0';
    yylval.val = make_float_enode(atof(yytext));
    return (CONSTANT);

ret_INT:
    *cp = '\0';
    string_to_integer(&value, &h_value, yytext, radix);

    us = FALSE;
    if (ch == 'U'  ||  ch == 'u') {   /* unsigned */
	ch = GETCH ();
	us = TRUE;
    }

    if (ch == 'L'  ||  ch == 'l') {
	ch = GETCH ();
	if (ch == 'L'  ||  ch == 'l') {	/* long long */
	    ch = GETCH ();
	    val = make_longlong_enode(value, h_value);
	} else {		      /* long  */
#ifdef SCANNER_READ_L_AS_LL
	    if (h_value) {
		val = make_longlong_enode(value, h_value);
	    } else {
		val = make_enode(LONG_CONSTANT, (void *)value);
	    }		
#else
	    if (h_value)
		warning("integer constant out of range");
	    val = make_enode(LONG_CONSTANT, (void *)value);
#endif /* SCANNER_READ_L_AS_LL */
	}
	if (us == FALSE) {
	    if (ch == 'U'  ||  ch == 'u') { /* unsinged */
		ch = GETCH ();
		us = TRUE;
	    }
	}
    } else {			      /* int */
#ifdef SCANNER_READ_L_AS_LL
	if (h_value) {
	    val = make_longlong_enode(value, h_value);
	} else {
	    val = make_enode(INT_CONSTANT, (void *)value);
	}
#else
	if (h_value) {
	    warning("integer constant out of range");
	}
	val = make_enode(INT_CONSTANT, (void *)value);
#endif /* SCANNER_READ_L_AS_LL */
    }

    if (us == TRUE) {
	BASIC_DATA_TYPE	ustype = UNSIGNED;
	switch (EXPR_CODE(val)) {
	case INT_CONSTANT:	ustype = UNSIGNED;		break;
	case LONG_CONSTANT:	ustype = UNSIGNED_LONG;		break;
	case LONGLONG_CONSTANT:	ustype = UNSIGNED_LONGLONG;	break;
	default:		
	    fatal("unknown constant type");
	}
        /* make cast expr tree */
	yylval.val = list2(CAST_EXPR,
			   list2(LIST, make_enode(BASIC_TYPE_NODE,(void *)ustype), NULL),
			   val);
    } else {
	yylval.val = val;
    }

    UNGETCH (ch);

    return (CONSTANT);
}

void string_to_integer(long int *p,long int *hp,char *cp, int radix)
{
    char    ch;
#ifdef NO_LONGLONG
    int     value;
#endif
    int	    x;
    unsigned int v0, v1, v2, v3;

#ifdef NO_LONGLONG
    value = 0;
    for( ; (ch = *cp) != 0 ; cp++ ){
	if ( isdigit(ch) )
	    x = ch - '0';
	else if ( isupper(ch) )
	    x = ch - 'A' + 10;
	else
	    x = ch - 'a' + 10;
	value = value * radix + x;
    }
    *p = value;
#endif
    v0 = v1 = v2 = v3 = 0;	/* clear */
    for( ; (ch = *cp) != 0 ; cp++ ){
	if (isdigit((int)ch))
	    x = ch - '0';
	else if ( isupper((int)ch) )
	    x = ch - 'A' + 10;
	else
	    x = ch - 'a' + 10;
	v0 = v0 * radix + x;
	v1 = v1 * radix + ((v0 >> 16) & 0xFFFF);
	v2 = v2 * radix + ((v1 >> 16) & 0xFFFF);
	v3 = v3 * radix + ((v2 >> 16) & 0xFFFF);
	v0 &= 0xFFFF;
	v1 &= 0xFFFF;
	v2 &= 0xFFFF;
	if ( v3 & 0xFFFF0000 ){
	    error("too large integer constant");
	    break;
	}
    }
    *p = (v1 << 16) | v0;
    *hp = (v3 << 16) | v2;
}

/* simple version */
static int read_string_constant(int mark)
{
    int     ch;
    char   *cp;
    int i,val;

    cp = yytext;
cont:
    ch = GETCH();
    while( ch != mark ){
	switch (ch) {
	case EOF:
	    error("unexpected EOF");
	    goto exit;
#ifdef not
	case '\n':
	    error("newline in string");
	    break;
#endif
	case '\\':	/* escape */
	    if ( cp >= &yytext[LEX_BUFSIZE-1] ){
		fatal("too long string");
		break;
	    }
	    switch(ch = GETCH()){ /* escaped char(n,r,...) */
	    case EOF:
		error("unexpected EOF");
		goto exit;
	    case 't': ch = '\t'; break;
	    case 'b': ch = '\b'; break;
	    case 'f': ch = '\f'; break;
	    case 'n': ch = '\n'; break;
	    case 'a': ch = '\a'; break;
	    case 'r': ch = '\r'; break;
	    case 'v': ch = '\v'; break;
	    case '\\': ch = '\\'; break;
	    case '0': 
	    case '1':
	    case '2':
	    case '3':
	    case '4':
	    case '5':
	    case '6':
	    case '7':
		val = 0;
		for(i = 0; i < 3; i++){
		    if(!(ch >= '0' && ch <= '7')){
			break;
		    }
		    val = val*8 + ch - '0';
		    ch = GETCH();
		}
		UNGETCH(ch);
		ch = val;
	    }
	    *cp++ = ch;
	    break;

	default:
	    *cp++ = ch;
	}
	if ( cp >= &yytext[LEX_BUFSIZE - 1] ){
	    fatal("too long string");
	    break;
	}
	ch = GETCH();
    }
exit:
    do {
	ch = GETCH();
    } while(isspace(ch));
    if(ch == mark) goto cont;
    UNGETCH(ch);
    *cp = '\0';

	/* end of string */
    yylval.val = make_enode(STRING_CONSTANT, (void *)strdup(yytext));

    return (CONSTANT);
}

/*  */
static int read_char_constant(int     mark)
{
    int     ch, value;
    char   *cp;

    value = 0;
    cp = yytext;
    ch = GETCH();

    switch (ch) {
    case EOF:
	error("unexpected EOF");
	break;

    case '\n':
	error("newline in char constant");
	break;

    case '\\':	/* escape sequence */
		/* '\': \nnn and \xNN are default except top 2 chars */
	ch = GETCH();
	switch (ch) {
	case 'x':	/* hex '\xhh', at most 2 */
	    ch = GETCH();
	    if ( !((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') ||
		   (ch >= 'A' && ch <= 'F')) ){
		warning("\\x must follow hex digit");
		break;
	    }
	    *cp++ = ch;
	    value = 0xf & ch;
	    ch = GETCH();
	    if ( !((ch >= '0' && ch <= '9') ||(ch >= 'a' && ch <= 'f') ||
		   (ch >= 'A' && ch <= 'F'))){
		break;
	    }
	    *cp++ = ch;
	    value = (value << 4) | (0xf & ch);
	    break;

	case '0':      /* octal '\ooo', at most 3 character */
	case '1':
	case '2':
	case '3':
	case '4':
	case '5':
	case '6':
	case '7':
	    value = ch - '0';

	    ch = GETCH();
	    if (ch < '0'  ||  '7'< ch) {
	      UNGETCH(ch);
	      break;
	    }
	    *cp++ = ch;
	    value = value * 8 + (ch - '0');

	    ch = GETCH();
	    if (ch < '0'  ||  '7'< ch) {
	      UNGETCH(ch);
	      break;
	    }
	    *cp++ = ch;
	    value = value * 8 + (ch - '0');
	    break;

	case 'a':
	    value = '\a';
	    break;
	case 'b':
	    value = '\b';
	    break;
	case 'f':
	    value = '\f';
	    break;
	case 'n':
	    value = '\n';
	    break;
	case 'r':
	    value = '\r';
	    break;
	case 't':
	    value = '\t';
	    break;
	case 'v':
	    value = '\v';
	    break;
	case '\\':
	    value = '\\';
	    break;
	case '?':
	    value = '\?';
	    break;
	case '\'':
	    value = '\'';
	    break;
	case '"':
	    value = '\"';
	    break;
	default:
	    warning("unknown escape sequence");
	    break;
	}
	*cp++ = ch;
	break;

    default:
	*cp++ = ch;
	value = ch;
	break;
    }
    *cp = '\0';

    ch = GETCH();
    if ( ch != mark )
      {
	error ("too many characters");
	while (ch != mark) {
	  ch = GETCH ();
	}
      }

    if ( cp == yytext )
	error("empty character constant");

    yylval.val = make_char_enode(INT_CONSTANT, value);

    return (CONSTANT);
}

static char   * search_keyword_by_token(int t)
{
    struct keyword_entry *kp;

    /* intern all keywords */
    for( kp = keyword_table ; kp->kw_name != NULL ; kp++ )
	if ( kp->kw_type == S_KEYWORD && kp->kw_value == t )
	    return (kp->kw_name);
    return (NULL);
}

struct keyword_entry keyword_table[] = {
    {"auto", S_CLASS, (int) AUTO},
    {"register", S_CLASS, (int) REGISTER},
    {"static", S_CLASS, (int) STATIC},
    {"extern", S_CLASS, (int) EXTERN},

    {"char", S_TYPE, (int) CHAR},
    {"short", S_TYPE, (int) SHORT},
    {"int", S_TYPE, (int) INT},
    {"long", S_TYPE, (int) LONG},
    {"float", S_TYPE, (int) FLOAT},
    {"double", S_TYPE, (int) DOUBLE},
    {"signed", S_TYPE, (int) SIGNED},
    {"__signed__", S_TYPE, (int) SIGNED},
    {"unsigned", S_TYPE, (int) UNSIGNED},
    {"void", S_TYPE, (int) VOID},

    {"break", S_KEYWORD, BREAK},
    {"case", S_KEYWORD, CASE},
    {"continue", S_KEYWORD, CONTINUE},
    {"default", S_KEYWORD, DEFAULT},
    {"do", S_KEYWORD, DO},
    {"else", S_KEYWORD, ELSE},
    {"enum", S_KEYWORD, ENUM},
    {"for", S_KEYWORD, FOR},
    {"goto", S_KEYWORD, GOTO},
    {"if", S_KEYWORD, IF},
    {"return", S_KEYWORD, RETURN},
    {"switch", S_KEYWORD, SWITCH},
    {"struct", S_KEYWORD, STRUCT},
    {"sizeof", S_KEYWORD, SIZEOF},
    {"typedef", S_KEYWORD, TYPEDEF},
    {"union", S_KEYWORD, UNION},
    {"while", S_KEYWORD, WHILE},

    {"const", S_QUAL, (int) QUAL_CONST},
    {"__const", S_QUAL, (int) QUAL_CONST},	/* for gnu soruce ? */
    {"volatile", S_QUAL, (int) QUAL_VOLATILE},

    {"__attribute__",S_GCC,GCC_ATTRIBUTE},
    {"__extension__",S_GCC,GCC_EXTENSION},
    {"__inline",S_GCC,GCC_INLINE},
    {"__inline__",S_GCC,GCC_INLINE},
    {"__asm__",S_GCC,GCC_ASM},
    {NULL/*, 0, 0*/}
};

static int is_func_decl_expr(expr x)
{
    expr    xx;

    xx = NULL;
    while( EXPR_CODE(x) != IDENT ){
	xx = x;
	x = EXPR_ARG1(x);
    }

    if ( xx != NULL && ( EXPR_CODE(xx) == FUNCTION_DECL ||
			 EXPR_CODE(xx) == FUNCTION_PROTO_DECL ) )
	return (TRUE);
    else {
	yyerror("syntax error");
	return (FALSE);
    }
}

void define_pragma_name(SYMBOL sp, expr x)
{
    if(sp->s_type != S_IDENT) fatal("define_pragma_name: not IDENT");
    sp->s_type = S_PARAM;
    pragma_name_list = 
	cons_list(list2(LIST,
			make_enode(IDENT, (void *)sp) ,x),
		  pragma_name_list);
}

expr pragma_name_value(SYMBOL sp)
{
    list lp;
    expr x;
    for(lp = pragma_name_list; lp != NULL; lp = LIST_NEXT(lp)){
	x = LIST_ITEM(lp);
	if(EXPR_SYM(EXPR_ARG1(x)) == sp) return EXPR_ARG2(x);
    }
    fatal("pramga_name_value: IDENT is not found");
    return NULL;
}


