static char rcsid[] = "$Id: F-datasub.c,v 1.7 2000/12/13 21:08:10 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 "F-front.h"


typedef struct {
    char *varName;
    int val;
    int inited;
} variableEntry;

static variableEntry *varTbl = NULL;
static int nVarTbl = 0;


static int
varComp(v1, v2)
     const void *v1;
     const void *v2;
{
    return strcmp(((variableEntry *)v1)->varName,
		  ((variableEntry *)v2)->varName);
}


static void
InitializeVariableTable()
{
    if (nVarTbl > 0 && varTbl != NULL) {
	int i;
	for (i = 0; i < nVarTbl; i++) {
	    free(varTbl[i].varName);
	}
	free(varTbl);
    }
    varTbl = NULL;
    nVarTbl = 0;
}


#if 0
static void
dumpVariable()
{
    fprintf(stderr, "\n");
    if (nVarTbl > 0 && varTbl != NULL) {
	int i;
	fprintf(stderr, "debug: variable dump:\n");
	for (i = 0; i < nVarTbl; i++) {
	    fprintf(stderr, "%5d:\t'%s'\t%10d\t(%s)\n",
		    i,
		    varTbl[i].varName,
		    varTbl[i].val,
		    (varTbl[i].inited == TRUE) ? "init" : "uninit");
	}
    } else {
	fprintf(stderr, "debug: no variables.\n");
    }
    fprintf(stderr, "\n");
}
#endif


static void
AddVariable(var)
     char *var;
{
    if (varTbl == NULL) {
	varTbl = (variableEntry *)malloc(sizeof(variableEntry) * 1);
	varTbl[0].varName = strdup(var);
	varTbl[0].val = 0;
	varTbl[0].inited = FALSE;
	nVarTbl = 1;
    } else {
	variableEntry *t;

	if (nVarTbl < 2) {
	    t = NULL;
	} else {
	    variableEntry key;
	    key.varName = var;
	    t = (variableEntry *)bsearch((void *)&key,
					 (void *)varTbl, 
					 nVarTbl, sizeof(variableEntry),
					 varComp);
	}
	if (t == NULL) {
	    varTbl = (variableEntry *)realloc(varTbl,
					      sizeof(variableEntry) * (nVarTbl + 1));
	    varTbl[nVarTbl].varName = strdup(var);
	    varTbl[nVarTbl].val = 0;
	    varTbl[nVarTbl].inited = FALSE;
	    nVarTbl++;
	    qsort((void *)varTbl, nVarTbl, sizeof(variableEntry), varComp);
	}
    }
}


static void
SetVariableValue(var, val)
     char *var;
     int val;
{
    variableEntry key;
    variableEntry *t;

    key.varName = var;
    t = (variableEntry *)bsearch((void *)&key,
				 (void *)varTbl,
				 nVarTbl, sizeof(variableEntry),
				 varComp);
    
    if (t != NULL) {
	t->val = val;
	t->inited = TRUE;
    } else {
	fatal("'%s' is not in variable table.", var);
    }
}


static int
GetVariableValue(var)
     char *var;
{
    variableEntry key;
    variableEntry *t;

    key.varName = var;
    t = (variableEntry *)bsearch((void *)&key,
				 (void *)varTbl,
				 nVarTbl, sizeof(variableEntry),
				 varComp);
    
    if (t != NULL) {
	if (t->inited == TRUE) {
	    return t->val;
	} else {
	    fatal("'%s' is not initialized.", var);
	}
    } else {
	fatal("'%s' is not in variable table.", var);
    }
    return 0;
}


static expv
findIdent(spec, new)
     expv spec;
     expv new;
{
    list lp;
    expv v;

    if (new == NULL) {
	new = list0(LIST);
    }

    FOR_ITEMS_IN_LIST(lp, spec) {
	v = LIST_ITEM(lp);
	if (v == NULL) {
	    continue;
	}

	switch (EXPR_CODE(v)) {
	    
	    case IDENT: {
		list lq;
		expv vv;
		int found = FALSE;
		FOR_ITEMS_IN_LIST(lq, new) {
		    vv = LIST_ITEM(lq);
		    if (EXPR_SYM(vv) == EXPR_SYM(v)) {
			found = TRUE;
			break;
		    }
		}
		if (found == FALSE) {
		    new = list_put_last(new, v);
		}
		break;
	    }
	    
	    default: {
		if (EXPR_CODE_IS_TERMINAL(EXPR_CODE(v)) != TRUE) {
		    new = findIdent(v, new);
		}
		break;
	    }
	}
    }
    
    return new;
}


static int
getExprValue(v)
     expv v;
{
    int ret = 0;

    switch (EXPR_CODE(v)) {

	case IDENT: {
	    ret = GetVariableValue(SYM_NAME(EXPR_SYM(v)));
	    break;
	}

	case INT_CONSTANT: {
	    ret = EXPV_INT_VALUE(v);
	    break;
	}

#ifdef HAS_INT64
	case LONGLONG_CONSTANT: {
	    _omInt64_t i64 = EXPV_INT64_VALUE(v);
	    ret = (int)i64;
	    break;
	}
#endif /* HAS_INT64 */

	case F_UNARY_MINUS_EXPR:
	case UNARY_MINUS_EXPR: {
	    ret = -getExprValue(v);
	    break;
	}

	case F_PLUS_EXPR:
	case PLUS_EXPR: {
	    ret = getExprValue(EXPR_ARG1(v)) + getExprValue(EXPR_ARG2(v));
	    break;
	}

	case F_MINUS_EXPR:
	case MINUS_EXPR: {
	    ret = getExprValue(EXPR_ARG1(v)) - getExprValue(EXPR_ARG2(v));
	    break;
	}
	
	case F_MUL_EXPR:
	case MUL_EXPR: {
	    ret = getExprValue(EXPR_ARG1(v)) * getExprValue(EXPR_ARG2(v));
	    break;
	}

	case F_DIV_EXPR:
	case DIV_EXPR: {
	    ret = getExprValue(EXPR_ARG1(v)) / getExprValue(EXPR_ARG2(v));
	    break;
	}

	case F_POWER_EXPR: {
	    ret = power_ii(getExprValue(EXPR_ARG1(v)),
			   getExprValue(EXPR_ARG2(v)));
	    break;
	}

	default: {
	    error("only integer expression is allowed in implied DO in DATA statement.");
	    return 0;
	}
    }

    return ret;
}


static int
InterpretImpliedDo(doSpec, new)
     expv doSpec;
     expv new;
{
    expv loopVar;
    char *varName;
    
    int thisLoop;

    int loopInit;
    expv loopInitV;
    int loopEnd;
    expv loopEndV;
    int loopIncr;
    expv loopIncrV;

    expv v;
    list lp;
    list lq;

    if (EXPR_CODE(doSpec) != F_IMPLIED_DO) {
	return FALSE;
    }

    if (new == NULL) {
	new = list0(LIST);
    }

    v = EXPR_ARG1(doSpec);
    loopVar = EXPR_ARG1(v);
    varName = SYM_NAME(EXPV_NAME(loopVar));

    loopInitV = EXPR_ARG2(v);
    loopInit = getExprValue(loopInitV);
    loopEndV = EXPR_ARG3(v);
    loopEnd = getExprValue(loopEndV);
    loopIncrV = EXPR_ARG4(v);
    if (loopIncrV == NULL) {
	loopIncr = 1;
    } else {
	loopIncr = getExprValue(loopIncrV);
    }

    for (thisLoop = loopInit, SetVariableValue(varName, loopInit);
	 thisLoop <= loopEnd;
	 thisLoop += loopIncr, SetVariableValue(varName, thisLoop)) {
    
	FOR_ITEMS_IN_LIST(lp, EXPR_ARG2(doSpec)) {
	    v = LIST_ITEM(lp);

	    switch (EXPR_CODE(v)) {

		case F_IMPLIED_DO: {
		    if (InterpretImpliedDo(v, new) == FALSE) {
			return FALSE;
		    }
		    break;
		}

		case F_ARRAY_REF: {
		    expv aRefV;
		    expv idxV = list0(LIST);
		    FOR_ITEMS_IN_LIST(lq, EXPR_ARG2(v)) {
			idxV = list_put_last(idxV,
					     expv_int_term(INT_CONSTANT, type_INT,
							   getExprValue(LIST_ITEM(lq))));
		    }
		    aRefV = list2(F_ARRAY_REF,
				  make_enode(IDENT,
					     f_find_symbol(SYM_NAME(EXPR_SYM(EXPR_ARG1(v))))),
				  idxV);
		    new = list_put_last(new, aRefV);
		    break;
		}

		default: {
		    error("invalid expression in implied DO in DATA statement.");
		    return FALSE;
		}
	    }
	}
    }

    return TRUE;
}


expv
ExpandImpliedDoInDATA(spec, new)
     expv spec;
     expv new;
{
    list lp;
    expv v;
    expv idents = findIdent(spec, (expv)NULL);

    if (new == NULL) {
	new = list0(LIST);
    }

    InitializeVariableTable();
    FOR_ITEMS_IN_LIST(lp, idents) {
	v = LIST_ITEM(lp);
	AddVariable(SYM_NAME(EXPR_SYM(v)));
	if (expr_is_param(v) == TRUE) {
	    SetVariableValue(SYM_NAME(EXPR_SYM(v)),
			     EXPV_INT_VALUE(expr_param_value(v)));
	}
    }

    if (InterpretImpliedDo(spec, new) == FALSE) {
	return NULL;
    }

    return new;
}

