static char rcsid[] = "$Id: C-pragma.c,v 1.2 2002/02/20 06:32:45 m-hirano 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.
 *  
 *  
 *  $
 */
#include "C-front.h"

char *pg_cp;
char pg_tok;	/* current token */
char pg_tok_buf[MAX_TOKEN_LEN];
expr pg_args;
expr pg_tok_val;

extern enum OMP_pragma pg_OMP_pragma;

static expr	pg_parse_number _ANSI_ARGS_((void));
static expr	pg_parse_string_constant _ANSI_ARGS_((void));
static expr	pg_parse_char_constant _ANSI_ARGS_((void));

/* topdown parser for simple expression */
static expr pg_term_expr _ANSI_ARGS_((int pre));
static expr pg_factor_expr _ANSI_ARGS_((void));
static expr pg_unary_expr _ANSI_ARGS_((void));
static expr pg_primary_expr _ANSI_ARGS_((void));

expr parse_pragma(char *line,enum pragma_syntax *ret)
{
    pg_cp = line;
    pg_get_token();
    if(pg_tok != PG_IDENT){
#ifdef not
	error("bad #pragma line");
#endif
	goto ret_none;
    }

    if(PG_IS_IDENT("omp") || PG_IS_IDENT("OMP")){
	if(no_omp_flag){
	    *ret = SYN_PRAGMA_NONE;
	    return NULL;
	}
	if((*ret = parse_OMP_pragma()) == SYN_PRAGMA_NONE) return NULL;
	return elist2(current_line,PRAGMA_LINE,
		      make_enode(INT_CONSTANT, (void *)pg_OMP_pragma), 
		      pg_args);
#ifdef USE_TEA_PRAGMA
    } else if(PG_IS_IDENT("omni") || PG_IS_IDENT("OMNI") ||
	      PG_IS_IDENT("tea") || PG_IS_IDENT("TEA")){
	if(no_omp_flag){
	    *ret = SYN_PRAGMA_NONE;
	    return NULL;
	}
	return parse_TEA_pragma(ret);
#endif /* USE_TEA_PRAGMA */
#ifdef USE_OPENGR_PRAGMA
    } else if (PG_IS_IDENT("opengr") || 
	       PG_IS_IDENT("ogr")) {
	if (no_ogr_flag) {
	    *ret = SYN_PRAGMA_NONE;
	    return NULL;
	}
	return parse_OGR_pragma(ret);
#endif /* USE_OPENGR_PRAGMA */
    } 
#ifdef not
    else  warning("unknown #pragma '%s'",pg_tok_buf);
#endif
 ret_none:
    /* external pramga */
    *ret = SYN_PRAGMA_DECL;
    return elist1(current_line,PRAGMA_LINE,
		  make_enode(STRING_CONSTANT, (void *)strdup(line)));
}

/*
 * compile pragma, called from compile_statement 
 */
expv compile_pragma_line(expr x)
{
    int pragma;
    if(EXPR_CODE(x) != PRAGMA_LINE) fatal("compile_pragma_line:");
    if(EXPR_CODE(EXPR_ARG1(x)) == STRING_CONSTANT){
      /* external pragma */
      return (expv) x;
    }
    pragma = EXPR_INT(EXPR_ARG1(x));
#ifdef USE_TEA_PRAGMA
    if (IS_TEA_PRAGMA(pragma)) {
	return compile_TEA_pragma((enum TEA_pragma)pragma,x);
    }
#endif /* USE_TEA_PRAGMA */
#ifdef USE_OPENGR_PRAGMA
    else if (IS_OGR_PRAGMA(pragma)) {
	return compile_OGR_pragma((enum OGR_pragma)pragma, x);
    }
#endif /* USE_OPENGR_PRAGMA */
    return compile_OMP_pragma((enum OMP_pragma)pragma,x);
}

void pg_get_token()
{
    char *cp;

 again:
    cp = pg_tok_buf;

    /* skip white space */
    while(isspace((int)*pg_cp)) pg_cp++;

    if(*pg_cp == '_' || isalpha((int)*pg_cp)){
	*cp++ = *pg_cp++;
	while(isalnum((int)*pg_cp) || *pg_cp == '_') *cp++= *pg_cp++;
	*cp = '\0'; 
	pg_tok = PG_IDENT;	/* identifier */
	return;
    }

    if(isdigit((int)*pg_cp)){
	pg_tok_val = pg_parse_number();
	pg_tok = PG_CONST;		/* number */
	return;
    }

    /* single charactor */
    switch(*pg_cp){
    case 0: 
	
    case '+':
    case '*':
    case '^':
    case '%':
    case ')':
    case '(':
    case ',':
    case '[':
    case ']':
    case ':':
	pg_tok = *pg_cp++;
	return;
    case '-':
	pg_tok = *pg_cp++;
	if(*pg_cp == '>'){
	    pg_cp++;
	    pg_tok = PG_STREF;
	} 
	return;
    case '/':
	pg_tok = *pg_cp++;
	if(*pg_cp == '*'){   /* comment */
	    while(*pg_cp != 0){
		if(*pg_cp++ == '*'){
		    if(*pg_cp == '/'){
			pg_cp++;
			goto again;
		    }
		}
	    }
	    error("bad comment in pragma");
	    pg_tok = 0;
	}
	return;
    case '|':
	pg_tok = *pg_cp++;
	if(*pg_cp == '|'){
	    pg_cp++;
	    pg_tok = PG_OROR;
	} 
	return;
    case '&':
	pg_tok = *pg_cp++;
	if(*pg_cp == '&'){
	    pg_cp++;
	    pg_tok = PG_ANDAND;
	} 
	return;
    case '!':
	pg_tok = *pg_cp++;
	if(*pg_cp == '='){
	    pg_cp++;
	    pg_tok = PG_NEQ;
	    return;
	}
	return;
    case '=':
	pg_tok = *pg_cp++;
	if(*pg_cp == '='){
	    pg_cp++;
	    pg_tok = PG_EQEQ;
	    return;
	} 
	return;
    case '<':
	pg_tok = *pg_cp++;
	if(*pg_cp == '='){
	    pg_cp++;
	    pg_tok = PG_LTEQ;
	}
	else if(*pg_cp == '<'){
	    pg_cp++;
	    pg_tok = PG_LTLT;
	}
	return;
    case '>':
	pg_tok = *pg_cp++;
	if(*pg_cp == '='){
	    pg_cp++;
	    pg_tok = PG_GTEQ;
	}
	else if(*pg_cp == '>'){
	    pg_cp++;
	    pg_tok = PG_GTGT;
	}
	return;

    case '"':
	pg_cp++;
	pg_tok_val = pg_parse_string_constant();
	pg_tok = PG_CONST;
	return;

    case '\'':
	pg_cp++;
	pg_tok_val = pg_parse_char_constant();
	pg_tok = PG_CONST;
	return;
    }

    pg_tok = PG_ERR;
    error("unknown character '%c' in pragma",*pg_cp);
    return;
}

expr pg_parse_ident()
{
    SYMBOL sp;
    if(pg_tok != PG_IDENT) return NULL;
    sp = find_symbol(pg_tok_buf);
    if(sp->s_type != S_IDENT) return NULL;
    pg_get_token();
    return make_enode(IDENT, (void *)sp);
}

expr pg_parse_expr()
{
    return pg_term_expr(0);
}

expr pg_term_expr(int pre)
{
    int op;
    expr e,ee;

    if(pre > 10){
       return pg_unary_expr();
   }
   if((e = pg_term_expr(pre+1)) == NULL) return NULL;
 again:
   switch(pre){
   case 0:
       if(pg_tok == PG_OROR){ op = LOG_OR_EXPR; goto next; }
       break;
   case 1:
       if(pg_tok == PG_ANDAND){ op = LOG_AND_EXPR; goto next; }
       break;
   case 2:
       if(pg_tok == '|'){ op = BIT_OR_EXPR; goto next; }
       break;
   case 3:
       if(pg_tok == '^'){ op = BIT_XOR_EXPR;  goto next; }
       break;
   case 4:
       if(pg_tok == '&'){ op = BIT_AND_EXPR; goto next; }
       break;
   case 5:
       if(pg_tok == PG_EQEQ) { op = LOG_EQ_EXPR; goto next; }
       if(pg_tok == PG_NEQ) { op = LOG_NEQ_EXPR; goto next; }
       break;
   case 6:
       if(pg_tok == '>') { op = LOG_GT_EXPR; goto next; }
       if(pg_tok == '<') { op = LOG_LT_EXPR; goto next; }
       if(pg_tok == PG_GTEQ) { op = LOG_GE_EXPR; goto next; }
       if(pg_tok == PG_LTEQ) { op = LOG_LE_EXPR; goto next; }
       break;
   case 7:
       if(pg_tok == PG_LTLT) { op = LSHIFT_EXPR; goto next; }
       if(pg_tok == PG_GTGT) { op = RSHIFT_EXPR; goto next; }
       break;
   case 8:
       if(pg_tok == '+') { op = PLUS_EXPR; goto next; }
       if(pg_tok == '-') { op = MINUS_EXPR; goto next; }
       break;
   case 10:
       if(pg_tok == '*') { op = MUL_EXPR; goto next; }
       if(pg_tok == '/') { op = DIV_EXPR; goto next; }
       if(pg_tok == '%') { op = MOD_EXPR; goto next; }
       break;
   }
   return e;
 next:
   pg_get_token();
   if((ee = pg_term_expr(pre+1)) == NULL) return NULL;
   e = list2((enum expr_code)op,e,ee);
   goto again;
}

expr pragma_name_value(SYMBOL sp);

expr pg_unary_expr()
{
    expr e;

    switch(pg_tok){
    case '*':
	pg_get_token();
	if((e = pg_unary_expr()) == NULL) return NULL;
	return list1(POINTER_REF,e);
    case '-':
	pg_get_token();
	if((e = pg_unary_expr()) == NULL) return NULL;
	return list1(UNARY_MINUS_EXPR,e);
    case '!':
	pg_get_token();
	if((e = pg_unary_expr()) == NULL) return NULL;
	return list1(LOG_NOT_EXPR,e);
    case '~':
	pg_get_token();
	if((e = pg_unary_expr()) == NULL) return NULL;
	return list1(BIT_NOT_EXPR,e);
    default:
	return pg_factor_expr();
    }
}

/* process postfix expression */
expr pg_factor_expr()
{
    expr e,ee,args;

    e = pg_primary_expr();
next:
    switch(pg_tok){
    default:
	return e;
    case '[':
	pg_get_token();
	if((ee = pg_term_expr(0)) == NULL) return NULL;
	if(pg_tok != ']'){
	    error("syntax error near ']' in pragma expression");
	    return NULL;	 /* syntax error */
	}
	e = list2(ARRAY_REF,e,ee);
	pg_get_token();
	break;
    case '(':
	pg_get_token();
	args = EMPTY_LIST;
	if(pg_tok != ')'){
	next_arg:
	    if((ee = pg_term_expr(0)) == NULL) return NULL;
	    list_put_last(args,ee);
	    if(pg_tok == ','){
		pg_get_token();
		goto next_arg;
	    } 
	}
	if(pg_tok == ')'){
	    pg_get_token();
	    e = list2(FUNCTION_CALL,e,args);
	    break;
	} else {
	    error("syntax error in pragma expression");
	    return NULL;
	}
    case '.':
	pg_get_token();
	if(pg_tok != PG_IDENT){
	    error("syntax error in pragma expression");
	    return NULL;
	}
	ee = pg_primary_expr();
	e = list2(STRUCT_REF,e,ee);
	break;
    case PG_STREF:
	pg_get_token();
	if(pg_tok != PG_IDENT){
	    error("syntax error in pragma expression");
	    return NULL;
	}
	ee = pg_primary_expr();
	e = list2(STRUCT_REF,list1(POINTER_REF,e),ee);
	break;
    }
    goto next;
}

expr pg_primary_expr()
{
    expr e;
    SYMBOL sp;

    switch(pg_tok){
    case PG_IDENT:
	pg_get_token();
	sp = find_symbol(pg_tok_buf);
	if(sp->s_type == S_IDENT) 
	    e = make_enode(IDENT, (void *)sp);
	else if(sp->s_type == S_PARAM) 
	    e = pragma_name_value(sp);
	else {
	    error("bad symbol in expression of pragma args");
	    return NULL;
	}
	return e;
    case '(':
	pg_get_token();
	if((e = pg_term_expr(0)) == NULL) return NULL;
	if(pg_tok != ')'){
	    error("mismatch paren in pragma expression");
	    return NULL;
	}
	pg_get_token();
	return e;
	/* 
	 * constant 
	 */

    case PG_CONST:
	e = pg_tok_val;
	pg_get_token();
	return e;

    default:
	error("syntax error in pragma expression");
	return NULL;
    }
}

static expr pg_parse_number()
{
    char   ch,*cp;
    long int value, h_value;
    int     radix;

    cp = pg_tok_buf;  /* used as buffer */
    radix = 10;
    ch = *pg_cp++;
    if ( ch == '0' ){
	ch = *pg_cp++;
	if ( ch == 'x' || ch == 'X' ){    /* HEX */
	    radix = 16;
	    for( ; ; ){
		ch = *pg_cp++;
		if ( !isxdigit((int)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 = *pg_cp++;
	    if ( !(ch >= '0' && ch <= '7') )
		goto ret_INT;
	}
	/* NOT REACHED */
    }

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

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

    --pg_cp;
    *cp = '\0';
    return(make_float_enode(atof(pg_tok_buf)));

ret_INT:
    *cp = '\0';
    string_to_integer(&value, &h_value, pg_tok_buf, radix);
    if ( ch == 'L' ){
	ch = *pg_cp++;
	if ( ch == 'L' ){
	    return make_longlong_enode(value, h_value);
	}
	else {
	    --pg_cp;
	    if (h_value)
		warning("integer constant out of range");
	    return make_enode(LONG_CONSTANT, (void *)value);
	}
    }
    else {
	--pg_cp;
	if ( h_value )
	    warning("integer constant out of range");
	return make_enode(INT_CONSTANT, (void *)value);
    }
}

static expr pg_parse_string_constant()
{
    int     ch;
    char   *cp,*end;
    int i,val;

    cp = pg_tok_buf;
    end = &pg_tok_buf[MAX_TOKEN_LEN];
cont:
    ch = *pg_cp++;
    while( ch != '"'){
	switch (ch) {
	case '\\':	/* escape */
	    if (cp >= end){
		fatal("too long string in pragma");
		break;
	    }
	    switch(ch = *pg_cp++){ /* escaped char(n,r,...) */
	    case '\0':
		error("unexpected end of line in pragma");
		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')){
			--pg_cp;
			break;
		    }
		    val = val*8 + ch - '0';
		    ch = *pg_cp++;
		}
		ch = val;
	    }
	    *cp++ = ch;
	    break;

	default:
	    *cp++ = ch;
	}
	if ( cp >= end){
	    fatal("too long string");
	    break;
	}
	ch = *pg_cp++;
    }
exit:
    do {
	ch = *pg_cp++;
    } while(isspace(ch));
    if(ch == '"') goto cont;
    --pg_cp;
    *cp = '\0';

    /* end of string */
    return make_enode(STRING_CONSTANT, (void *)strdup(pg_tok_buf));
}

static expr pg_parse_char_constant()
{
    int     ch, value;
#if 0
    char   *cp,*end;
#else
    char   *cp;
#endif

    value = 0;
    cp = pg_tok_buf;
#if 0
    end = &pg_tok_buf[MAX_TOKEN_LEN];
#endif
    ch = *pg_cp++;

    switch (ch) {
    case '\0':
	error("unexpected end of line in pragma");
	break;

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

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

	case '0':	/* oct '\ooo', at most 3 */
	    ch = *pg_cp++;
	    if ( ch == '"'){	/* '\0' */
		--pg_cp;
		break;
	    }
	case '1':
	case '2':
	case '3':
	case '4':
	case '5':
	case '6':
	case '7':
	    value = 0x7 & ch; 
	    ch = *pg_cp++;
	    if ( !(ch >= '0' && ch <= '7') )
		break;
	    *cp++ = ch;
	    value = (value << 3) | (0x7 & ch);
	    ch = *pg_cp++;
	    if ( !(ch >= '0' && ch <= '7') )
		break;
	    *cp++ = ch;
	    value = (value << 3) | (0x7 & ch);
	    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 = *pg_cp++;
    if ( ch != '\'')
	fatal("too many characters");

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

    return make_char_enode(INT_CONSTANT, value);
}


