package ilog.language.design.kernel;

/**
 * @version     Last modified on Thu Apr 11 14:17:19 2002 by hak
 * @author      <a href="mailto:hak@ilog.fr">Hassan A&iuml;t-Kaci</a>
 * @copyright   &copy; 2002 <a href="http://www.ilog.fr/">ILOG, S.A.</a>
 */

import java.util.ArrayList;
import ilog.language.design.types.*;
import ilog.language.design.base.*;

/**
 * This is the class for a special kind of expressions used essentially for initializing
 * an array with a specified dimension vector from an expression that evaluates to an
 * array whose dimension vectors is <i>almost</i> identical to that of the specified one.
 * By "almost", we mean that:
 * <ul>
 * <li> where an integer is expected as the specified dimension's indexable, the array's
 *      corresponding indexable must be the same integer;
 * <li> where a set is expected as the specified dimension's indexable, the array's
 *      corresponding indexable must be either an integer that must be equal to the set's
 *      size, or the same set;
 * <li> where an int range is expected as the specified dimension's indexable, the array's
 *      corresponding indexable must be either an integer that must be equal to the ranges's
 *      size, or the same range, or a set of integers that contains all the elements of
 *      the range.
 * </ul>
 * The type of the elements of the specified array must correspond to the one specified
 * as the element type in this expression.
 */

public class ArrayInitializer extends Expression 
{
  /**
   * This is the type of the array's elements. For example, if the dimension vector is
   * <tt>[d<sub>1</sub>,...,d<sub>n</sub>]</tt>, then the array must be of type
   * <tt>t[t<sub>1</sub>]...[t<sub>m</sub>]</tt>, where <tt>m >= n</tt>, and the element
   * type is then <tt>t</tt> if <tt>m=n</tt>, or <tt>t[t<sub>n+1</sub>]...[t<sub>m</sub>]</tt>
   * if <tt>m>n</tt>.
   */
  private Type _elementType;

  /**
   * This contains the expressions that comprise the dimension.
   */
  private Expression[] _dimension;

  /**
   * This is the array expression.
   */
  private Expression _array;

  private Type _type = new TypeParameter();
  private Type _checkedType;

  public ArrayInitializer (Type elementType, ArrayList dimension, Expression array)
    {
      _elementType = elementType;

      _dimension = new Expression[dimension.size()];

      for (int i=_dimension.length; i-->0;)
        _dimension[i] = (Expression)dimension.get(i);

      _array = array;
    }  

  public final int numberOfSubexpressions ()
    {
      return _dimension.length + 1;
    }

  public final Expression subexpression (int n) throws NoSuchSubexpressionException
    {
      if (0 <= n && n < _dimension.length)
        return _dimension[n];

      if (n == _dimension.length)
        return _array;

      throw new NoSuchSubexpressionException(this,n);
    }

  public final Type elementType ()
    {
      return _elementType.value();
    }

  public final Expression[] dimension ()
    {
      return _dimension;
    }

  public final Expression array ()
    {
      return _array;
    }

  public final Type type ()
    {
      return _type.value();
    }

  public final void setType (Type type)
    {
      _type = type;
    }

  public final Type typeRef ()
    {
      return _type;
    }

  public final Type checkedType ()
    {
      return _checkedType;
    }

  public final void setCheckedType (Type type)
    {
      _checkedType = type;
    }

  public final Expression sanitizeNames (ParameterStack parameters)
    {
      for (int i=_dimension.length; i-->0;)
        _dimension[i] = _dimension[i].sanitizeNames(parameters);

      _array = _array.sanitizeNames(parameters);

      return this;
    }

  public final void sanitizeSorts (Enclosure enclosure)
    {
      for (int i=_dimension.length; i-->0;)
        _dimension[i].sanitizeSorts(enclosure);

      _array.sanitizeSorts(enclosure);
    }

  public final Expression shiftOffsets (int intShift, int realShift, int objectShift,
                                        int intDepth, int realDepth, int objectDepth)
    {
      for (int i=_dimension.length; i-->0;)
        _dimension[i] = _dimension[i].shiftOffsets(intShift,realShift,objectShift,
                                                   intDepth,realDepth,objectDepth);

      _array = _array.shiftOffsets(intShift,realShift,objectShift,
                                   intDepth,realDepth,objectDepth);

      return this;
    }

  public final void setCheckedType ()    
    {
      for (int i=_dimension.length; i-->0;)
        _dimension[i].setCheckedType();

      _array.setCheckedType();

      setCheckedType(type().copy());
    }

  /**
   * Type-checking proceeds as follows:
   * <ol>
   * <li> the array is type-checked and it is verified that its type has sufficiently
   *      many dimensions to accommodate the number of specified dimensions;
   * <li> the element type is unified with the type of the array elements at the depth
   *      corresponding to the number of specified dimensions;
   * <li> the type of this is checked as an array type corresponding to the specified
   *      dimensions and element type.
   * <li> each dimension expression is type-checked to have an index set type, or forced
   *      to be of type int when such is the type of the corresponding dimension in the
   *      specified array.
   * </ol>
   * Note that this is not a fully general type-checking because it disallows overloaded
   * types for the array by taking the first possible type found for it as the final one.
   */
  public final void typeCheck (TypeChecker typeChecker) throws TypingErrorException
    {
      _array.typeCheck(new ArrayType(),typeChecker);

      // NB: The following disallows overloaded types for the array!
      ArrayType arrayType = (ArrayType)_array.type();

      if (arrayType.dimension() < _dimension.length)
        typeChecker.error(new TypingErrorException("array type "+arrayType+" is missing "+
                                                   (_dimension.length-arrayType.dimension())+
                                                   " dimension(s)"),
                          _array);

      typeChecker.unify(_elementType,arrayType.projection(_dimension.length));

      for (int i=_dimension.length; i-->0;)
        {
          Global indexSet = Global.dummyIndexSet();
          indexSet.typeCheck(typeChecker);
          _dimension[i].typeCheck(indexSet.typeRef(),typeChecker);

          if (_dimension[i].type().isInt())
            typeChecker.unify(arrayType.dimension(i),Type.INT());
        }

      typeChecker.typeCheck(this,elementType().array(_dimension));
    }

  public final void compile (Compiler compiler)
    { // compile: (1) the array
      //          (2) the dimensions (ints are systematically boxed)
      //          (3) the number of dimensions
      //          (4) ARRAY_INITIALIZE

      _array.compile(compiler);

      for (int i=_dimension.length; i-->0;)
        {
          _dimension[i].compile(compiler);
          if (_dimension[i].checkedType().boxSort() == Type.INT_SORT)
            compiler.generate(Instruction.I_TO_O);
        }

      compiler.generate(new PushValueInt(_dimension.length));
      compiler.generate(Instruction.ARRAY_INITIALIZE);
    }


  public final String toString ()
    {
      StringBuffer buf = new StringBuffer();

      buf.append(_elementType.value());

      for (int i=0; i<_dimension.length; i++)
        buf.append("[").append(_dimension[i]).append("]");

      return buf.append(" = ").append(_array).toString();
    }
}




