//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
// PLEASE DO NOT EDIT WITHOUT THE EXPLICIT CONSENT OF THE AUTHOR! \\
//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\

package ilog.language.design.kernel;

/**
 * @version     Last modified on Fri Oct 18 19:00:53 2002 by hak
 * @version          modified on Wed Jul 24 12:18:40 2002 by pviry
 * @author      <a href="mailto:hak@ilog.fr">Hassan A&iuml;t-Kaci</a>
 * @copyright   &copy; 2000-2002 <a href="http://www.ilog.fr/">ILOG, S.A.</a>
 */

import ilog.language.design.types.*;
import ilog.language.design.base.*;

import ilog.language.util.Locatable;
import ilog.language.util.Location;
import ilog.language.util.Span;
import ilog.language.tools.Misc;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;

/**
 * This class is the mother of all expressions in the kernel language.
 * It specifies the prototypes of the methods that must be implemented
 * by all expression subclasses. The subclasses of <tt>Expression</tt>
 * are:
 * <p>
 *
 * <ul>
 *
 * <li><a href="Constant.html"><tt>Constant</tt></a>:
 *     constant (void, boolean, <a href="Int.html"><tt>integer</tt></a>,
 *     <a href="Real.html"><tt>real number</tt></a>,
 *     <a href="ObjectConstant.html"><tt>object</tt></a>);
 * <p>
 * <li><a href="Abstraction.html"><tt>Abstraction</tt></a>:
 *     functional abstraction (<i>&agrave; la</i> lambda-calculus);
 * <p>
 * <li><a href="Application.html"><tt>Application</tt></a>:
 *     functional application;
 * <p>
 * <li><a href="Local.html"><tt>Local</tt></a>:
 *     local name;
 * <p>
 * <li><a href="Global.html"><tt>Global</tt></a>:
 *     global name;
 * <p>
 * <li><a href="IfThenElse.html"><tt>IfThenElse</tt></a>:
 *     conditional;
 * <p>
 * <li><a href="AndOr.html"><tt>AndOr</tt></a>:
 *     non-strict boolean conjunction and disjunction;
 * <p>
 * <li><a href="Sequence.html"><tt>Sequence</tt></a>:
 *     sequence of expressions (presumably with side-effects);
 * <p>
 * <li><a href="Let.html"><tt>Let</tt></a>:
 *     lexical scoping construct;
 * <p>
 * <li><a href="Loop.html"><tt>Loop</tt></a>:
 *     conditional iteration construct;
 * <p>
 * <li><a href="Definition.html"><tt>Definition</tt></a>:
 *     definition of a global name with an expression defining it
 *     in a global store;
 * <p>
 * <li><a href="Parameter.html"><tt>Parameter</tt></a>:
 *     a function's formal parameter (really a pseudo-expression
 *     as it is not fully processed as a real expression and is used
 *     as a shared type information repository for all occurrences
 *     in a function's body of the variable it stands for);
 * <p>
 * <li><a href="Assignment.html"><tt>Assignment</tt></a>:
 *     construct to set the value of a <a href="LocalAssignment.html"><tt>local</tt></a>
 *     or a <a href="GlobalAssignment.html"><tt>global</tt></a> variable;
 * <p>
 * <li><a href="NewObject.html"><tt>NewObject</tt></a>:
 *     construct to create a new object;
 * <p>
 * <li><a href="FieldUpdate.html"><tt>FieldUpdate</tt></a>:
 *     construct to update the value of an object's field;
 * <p>
 * <li><a href="NewArray.html"><tt>NewArray</tt></a>:
 *     construct to create a new (multidimensional) array;
 * <p>
 * <li><a href="ArraySlot.html"><tt>ArraySlot</tt></a>:
 *     construct to access the element of an array;
 * <p>
 * <li><a href="ArraySlotUpdate.html"><tt>ArraySlotUpdate</tt></a>:
 *     construct to update the element of an array;
 * <p>
 * <li><a href="Tuple.html"><tt>Tuple</tt></a>:
 *     construct to create a new position-indexed tuple;
 * <p>
 * <li><a href="NamedTuple.html"><tt>NamedTuple</tt></a>:
 *     construct to create a new name-indexed tuple;
 * <p>
 * <li><a href="TupleProjection.html"><tt>TupleProjection</tt></a>:
 *     construct to access the component of a tuple;
 * <p>
 * <li><a href="TupleUpdate.html"><tt>TupleUpdate</tt></a>:
 *     construct to update the component of a tuple;
 * <p>
 * <li><a href="DottedNotation.html"><tt>DottedNotation</tt></a>:
 *     a generic "dotted notation" to be interpreted, according to the
 *     type of its let expression, as an object member dereference
 *     a tuple projection, or any other expression that this may denote.
 * <p>
 * <li><a href="Dummy.html"><tt>Dummy</tt></a>:
 *     temporary place holder in lieu of a name prior to being discriminated
 *     into a local or global one.
 * <p>
 * <li><a href="CompiledExpression.html"><tt>CompiledExpression</tt></a>:
 *     pre-compiled code encapsulated as an expression;
 *
 * </ul>
 * Typically, upon being read, an <tt>Expression</tt> will be:
 * <p>
 *
 * <ol>
 *
 * <p>
 * <li><i>"name-sanitized"</i> - in the context of a <a
 * href="Sanitizer.html"><tt>Sanitizer</tt></a> to discriminate
 * between local names and global names, and establish pointers from the
 * local variable occurrences to the abstraction that introduces them,
 * and from global names to entries in the global symbol table;
 *
 * <p>
 * <li><i>type-checked</i> - in the context of a <a
 * href="../types/TypeChecker.html"><tt>TypeChecker</tt></a> to discover
 * whether it has a type at all, or several possible ones (only
 * expressions that have a unique unambiguous type are further
 * processed);
 *
 * <p>
 * <li> <i>"sort-sanitized</i>" - in the context of a <a
 * href="Sanitizer.html"><tt>Sanitizer</tt></a> to discriminate
 * between those local variables that are of primitive Java types
 * (<tt>int</tt> or <tt>double</tt>) or of <tt>Object</tt> type (this is
 * necessary because the set-up means to use unboxed values of primitive
 * types for efficiency reasons); this second "sanitization" phase is
 * also used to compute offsets for local names (<i>i.e.</i>, so-called
 * <i>de Bruijn indices</i>) for each type sort;
 *
 * <p>
 * <li><i>compiled</i> - in the context of a <a
 * href="Compiler.html"><tt>Compiler</tt></a> to generate the sequence
 * of instructions whose execution in an appropriate runtime environment
 * will evaluate the expression;
 *
 * <p>
 * <li><i>executed</i> - in the context of a <a
 * href="../backend/Runtime.html"><tt>Runtime</tt></a> to execute its sequence of
 * instructions.
 *
 * </ol>
 * */
public abstract class Expression implements Locatable
{
  protected boolean _typeCheckLocked = false;
  public final boolean typeCheckLocked ()
    {
      if (!_typeCheckLocked)
        {
          _typeCheckLocked = true;
          return false;
        }

      //System.out.println("This "+Misc.simpleClassName(this)+" has already been type-checked!");
      return true;
    }

  protected boolean _isSetCheckedType = false;
  public final boolean isSetCheckedType ()
    {
      if (!_isSetCheckedType)
        {
          _isSetCheckedType = true;
          return false;
        }

      //System.out.println("This "+Misc.simpleClassName(this)+" has already been set checked-type!");
      return true;
    }

  /**
   * This contains any other types that may be redudantly be prescribed to this
   * expression prior to being type-checked. For example, a grammar constructing
   * an expression from a concrete syntax may allow several types to be attached
   * a single expression. The type-checker will then ensure that all these types
   * are compatible, and can be reduced to a single consistent one.
   */
  protected HashSet _otherTypes;

  /**
   * Returns <tt>true</tt> iff this expression is a
   * <a href="Constant.html"><tt>Constant</tt></a>.
   */
  public final boolean isConstant ()
    {
      return this instanceof Constant;
    }

  /**
   * Returns <tt>true</tt> iff this expression denotes the constant <tt>void</tt>.
   */
  public boolean isVoid ()
    {
      return isConstant() && ((Constant)this).isVoid();
    }

  /**
   * Returns <tt>true</tt> iff this expression denotes the constant <tt>true</tt>.
   */
  public boolean isTrue ()
    {
      return isConstant() && ((Constant)this).isTrue();
    }

  /**
   * Returns <tt>true</tt> iff this expression denotes the constant <tt>false</tt>.
   */
  public boolean isFalse ()
    {
      return isConstant() && ((Constant)this).isFalse();
    }

  /**
   * Returns <tt>true</tt> iff this expression denotes the constant <tt>null</tt>.
   */
  public boolean isNull ()
    {
      return isConstant() && ((Constant)this).isNull();
    }

  /**
   * This method returns the current type of this expression. It will
   * typically, but not necessarily, be the dereferenced value of a
   * <a href="TypeParameter.html"> <tt>TypeParameter</tt></a> field.
   * A notable exception is a <a href="Local.html"><tt>Local</tt></a>
   * since its type method will always return the type of the
   * <a href="Parameter.html"> <tt>Parameter</tt></a> it stands for.
   */
  public abstract Type type ();

  /**
   * This method forcibly sets this expression's type field, if it has one,
   * to the specified type. It will be an empty method otherwise.
   */
  public abstract void setType (Type type);

  /**
   * This method returns this expression's undereferenced type field if
   * it has one. It will return <tt>null</tt> otherwise.
   */
  public abstract Type typeRef ();

  /**
   * This method returns the final unambiguously checked type of this
   * expression.
   */
  public abstract Type checkedType ();

  /**
   * This method sets the final unambiguously checked type of this
   * expression, possibly along with other final information that may
   * be needed by the compiler for generating code.
   */
  public abstract void setCheckedType ();

  /**
   * This method sets the final unambiguously checked type of this
   * expression to the specified type. This is used when expression
   * are synthesized on the fly after the type-checking phase and
   * prior to the compiling phase.
   */
  public abstract void setCheckedType (Type type);

  /**
   * This method returns the actual expression corresponding to this one
   * after eliminating all the <a href="Dummy.html"><tt>Dummy</tt></a>
   * expressions that occur in it where names where read. It takes as an
   * argument the stack of parameters it encounters along the way
   * of visiting all its components. A <a href="ParameterStack.html">
   * <tt>ParameterStack</tt></a> is simply a <tt>Stack</tt> of
   * <a href="Parameter.html"><tt>Parameter</tt></a> objects
   * along with a method to retrieve the latest one with a given
   * name.
   */

  public abstract Expression sanitizeNames (ParameterStack parameters,
                                            ClassTypeHandle handle);

  /**
   * This method walks down the expression gathering information relating
   * each <a href="Local.html"><tt>Local</tt></a> object to the
   * <a href="Abstraction.html"><tt>Abstraction</tt></a> containing
   * the corresponding <a href="Parameter.html"><tt>Parameter</tt></a>
   * in its array of formal parameters. It takes as an argument an
   * <a href="Enclosure.html"><tt>Enclosure</tt></a>, which is a <tt>Stack</tt>
   * containing all <a href="Abstraction.html"><tt>Abstraction</tt></a>s
   * encountered along the way, equipped with a method to compute all
   * the information necessary in computing the local's offset.
   */
  public abstract void sanitizeSorts (Enclosure enclosure);

  /**
   * This methods visits this expression shifting the offset of all the
   * <a href="Local.html"><tt>Local</tt></a>s it encounters by the
   * specified amount (per sort) as long as the offset it sees is
   * referencing a parameter within the specified abstraction depth (per
   * sort); This method is used when the compiler needs to synthesize an
   * abstraction from an already typechecked expression (<i>i.e.</i>,
   * for currying or un/boxing -- <i>e.g.</i>, see the <tt>pad</tt>
   * method below).
   */
  public abstract Expression shiftOffsets (int intShift, int realShift, int objectShift,
                                           int intDepth, int realDepth, int objectDepth);

  public final Expression shiftOffsets (int intShift, int realShift, int objectShift)
    {
      return shiftOffsets(intShift,realShift,objectShift,0,0,0);
    }

  /**
   * This method type-checks this expression in the context of the specified
    *<a href="../types/TypeChecker.html"> <tt>TypeChecker</tt></a>.
   */
  public abstract void typeCheck (TypeChecker typeChecker) throws TypingErrorException;

  /**
   * This method ascertains that this expression has the specified type in
   * the context of the specified <a href="../types/TypeChecker.html">
   * <tt>TypeChecker</tt></a>. It amounts to first type-checking this
   * expression in the type-checker, then making sure the type found for it
   * by the type-checker agrees (<i>i.e.</i>unifies) with the specified type.
   */
  public void typeCheck (Type type, TypeChecker typeChecker) throws TypingErrorException
    {
      typeCheck(typeChecker);
      typeChecker.unify(typeRef(),type,this);
    }
    
  /**
   * This method type-checks this expression in the context of the specified typechecker,
   * using the type of the specified <a href="Global.html"><tt>Global</tt></a> as a filter.
   */
  public final void typeCheck (Global filter, TypeChecker typeChecker) throws TypingErrorException
    {
      typeCheck(typeChecker);
      typeChecker.prune(filter,typeRef(),this);
      filter.typeCheck(typeRef(),typeChecker);
    }
    
  /**
   * This method compiles this expression in the context of the specified
   * <a href="Compiler.html"><tt>Compiler</tt></a>.
   */
  public abstract void compile (Compiler compiler);

  public final Instruction[] compile ()
    {
      Compiler compiler = new Compiler();
      compiler.compile(this);
      return(compiler.code());
    }
    
  /**
   * Returns the number of subexpressions
   */
  public int numberOfSubexpressions ()
    {
      return 0;
    }

  /**
   * Returns the n-th subexpression of this expression; if there is no subexpression
   * at the given position, this throws a <tt>NoSuchSubexpressionException</tt>
   */
  public Expression subexpression (int n) throws NoSuchSubexpressionException
    {
      throw new NoSuchSubexpressionException(this,n);
    }

  /**
   * This is only to enable uniform visitors on expressions. It will return non-null
   * only for abstractions.
   */
  public Parameter[] parameters ()
    {
      return null;
    }

  /**
   * Returns the sort of this expression's type after it has been
   * checked.  Sorts are of three kinds and defined as global constants
   * in <a href="../types/Type.html"><tt>Type</tt></a>: <tt>Type.INT_SORT</tt>,
   * <tt>Type.REAL_SORT</tt>, and <tt>Type.OBJECT_SORT</tt>.
   */
   public final byte sort ()
    {
      return checkedType().sort();
    }

  /**
   * This returns the runtime sort for this expression unless its type
   * has been marked as boxed - in which case it returns <tt>OBJECT_SORT</tt>.
   */
  public /* final */ byte boxSort ()
    {
      return checkedType().boxSort();
    }

  /**
   * This adds the specified type as another type for this expression. <b>NB:</b>
   * This method must <i>never</i> be used on an expression or with a type that
   * has been (even partially) type-checked. This is because, rather than making
   * different type parameters separate types for this expression, it hard-binds
   * together, or to the original type if there is one. It is meant to be used
   * exclusively at syntax-building time prior to type-checking anything.
   */
  public final Expression addType (Type newType)
    {
      Type oldType = type();

      if (newType == null)
        return this;

      if (oldType == null)
        setType(newType);
      else
        {
          oldType = oldType.value();
          newType = newType.value();

          if (oldType == newType)
            return this;

          if (oldType.kind() == Type.PARAMETER)
            ((TypeParameter)oldType).bind(newType);
          else
            if (newType.kind() == Type.PARAMETER)
              ((TypeParameter)newType).bind(oldType);
            else
              {
                if (_otherTypes == null)
                  _otherTypes = new HashSet();
                _otherTypes.add(newType);
              }
        }

      return this;
    }

  public final HashSet otherTypes ()
    {
      return _otherTypes;
    }

  public final Expression setOtherTypes (HashSet otherTypes)
    {
      _otherTypes = otherTypes;
      return this;
    }

  /**
   * Adds all the types prescribed for the specified expression to this one.
   */
  public final Expression addTypes (Expression expression)
    {
      addType(expression.typeRef());

      HashSet types = expression.otherTypes();
      if (types != null)
        for (Iterator i=types.iterator();i.hasNext();)
          addType((Type)i.next());
      
      return this;
    }

  /**
   * This field may be used to contain this expression's concrete syntax origin.
   */
  private Locatable _extent;

  public final Locatable extent ()
    {
      return _extent;
    }

  public final Expression setExtent (Locatable extent)
    {
      _extent = extent;
      return this;
    }

  /**
   * Returns the start of this expressions's extent as a <a href="../../util/Location.html">
   * <tt>Location</tt></a>.
   */
  public final Location getStart ()
    {
      return _extent == null ? null : _extent.getStart();
    }

  /**
   * Returns the end of this expressions's extent as a <a href="../../util/Location.html">
   * <tt>Location</tt></a>.
   */
  public final Location getEnd ()
    {
      return _extent == null ? null : _extent.getEnd();
    }

  /**
   * Sets the start of this expressions's extent to the specified <a href="../../util/Location.html">
   * <tt>Location</tt></a> and returns this.
   */
  public final Locatable setStart (Location location)
    {
      if (_extent == null) _extent = new Span();
      _extent.setStart(location);
      return this;
    }
    
  /**
   * Sets the end of this expressions's extent to the specified <a href="../../util/Location.html">
   * <tt>Location</tt></a> and returns this.
   */
  public final Locatable setEnd (Location location)
    {
      if (_extent == null) _extent = new Span();
      _extent.setEnd(location);
      return this;
    }

  /**
   * Sets the extent of the specified <a href="../types/StaticSemanticsErrorException.html">
   * <tt>StaticSemanticsErrorException</tt></a> to this expression.
   */
  protected final StaticSemanticsErrorException locate (StaticSemanticsErrorException e)
    {
      return e.setExtent(this);
    }

  /**
   * Returns an explicit string for this expression's extent's location.
   */
  public final String locationString ()
    {
      return Misc.locationString(_extent);
    }

  /**
   * This method analyzes the type boxing information for this expression when it
   * occurs as a <i>functional</i> argument of an application. This returns an
   * expression equivalent to this one <i>"padded"</i> with un/wrappers as needed to
   * reconcile its type boxing information with that of the formal type expected by the
   * argument of the function of which it is ac actual argument, and return the
   * possibly so-modified expression.  It is used by the <tt>compile</tt> method of an
   * <a href="Application.html"> <tt>application</tt></a> that takes this expression as
   * an argument with a functional type. Then, its type information and the type of the
   * function's argument position where it appears (passed as argument to this method)
   * must be used to determine whether to box or unbox its arguments and result. It is
   * defined here as opposed to a concrete subclass of <tt>Expression</tt> because
   * instances of several such classes may have a function type (namely, <a
   * href="Abstraction.html"> <tt>Abstraction</tt></a>s, <a href="Local.html">
   * <tt>Local</tt></a>s, and <a href="Global.html"> <tt>Global</tt></a>s).
   */
  final Expression pad (FunctionType formalType)
    {
      // This expression has necessarily a function type

      FunctionType actualType = (FunctionType)checkedType();
      
      int arity = actualType.arity();
      boolean argumentSortsAgree = true;
      
      for (int i=0; i<arity; i++)
        if (formalType.argumentSortsDisagree(actualType,i))
          {
            argumentSortsAgree = false;
            break;
          }

//        /* start of code for debugging only */
//        ilog.language.tools.Debug.step("Expression = "+this+
//                                       "\nActual type = "+actualType+"\t"+actualType.maskRef()+
//                                       "\nFormal type = "+formalType+"\t"+formalType.maskRef());
//        /* end of code for debugging only */

      if (argumentSortsAgree && !formalType.resultSortsDisagree(actualType))
        return this;

      Application innerPadding;
      Application outerPadding;
      Abstraction abstraction;

      Parameter [] parameters   = new Parameter [arity];
      Local     [] locals       = new Local     [arity];
      Expression[] paddings     = new Expression[arity];

      for (int i=0; i<arity; i++)
        {
          parameters[i] = new Parameter();

          locals[i] = new Local(parameters[i]);
          locals[i].setOffset(arity-1-i);
          
          if (formalType.mustWrapArgument(actualType,i))
            {
              paddings[i] = new Application(formalType.domain(i).wrapper(),locals[i]);

              if (formalType.domain(i).sort() == Type.INT_SORT)
                {
                  parameters[i].setCheckedType(Type.INT());
                  paddings[i].setCheckedType(Type.BOXED_INT());
                }
              else // must have: (formalType.domain(i).sort() == Type.REAL_SORT)
                {
                  parameters[i].setCheckedType(Type.REAL());
                  paddings[i].setCheckedType(Type.BOXED_REAL());
                }                
            }

          else

          if (formalType.mustUnwrapArgument(actualType,i))
            {
              paddings[i] = new Application(formalType.domain(i).unwrapper(),locals[i]);
              if (formalType.domain(i).sort() == Type.INT_SORT)
                {
                  parameters[i].setCheckedType(Type.BOXED_INT());
                  paddings[i].setCheckedType(Type.INT());
                }
              else // must have: (formalType.domain(i).sort() == Type.REAL_SORT)
                {
                  parameters[i].setCheckedType(Type.BOXED_REAL());
                  paddings[i].setCheckedType(Type.REAL());
                }
            }

          else

            {
              parameters[i].setCheckedType(actualType.domain(i));
              paddings[i] = locals[i];
            }
        }

      innerPadding = new Application(this,paddings);

      outerPadding = innerPadding;

      if (formalType.mustWrapResult(actualType))
        {
          outerPadding = new Application(formalType.range().wrapper(),innerPadding);
          if (formalType.range().sort() == Type.INT_SORT)
            {
              innerPadding.setCheckedType(Type.INT());
              outerPadding.setCheckedType(Type.BOXED_INT());
            }
          else // must have: (formalType.range().sort() == Type.REAL_SORT)
            {
              innerPadding.setCheckedType(Type.REAL());
              outerPadding.setCheckedType(Type.BOXED_REAL());
            }
        }

      else

      if (formalType.mustUnwrapResult(actualType))
        {
          outerPadding = new Application(formalType.range().unwrapper(),innerPadding);
          if (formalType.range().sort() == Type.INT_SORT)
            {
              innerPadding.setCheckedType(Type.BOXED_INT());
              outerPadding.setCheckedType(Type.INT());
            }
          else // must have: (formalType.range().sort() == Type.REAL_SORT)
            {
              innerPadding.setCheckedType(Type.BOXED_REAL());
              outerPadding.setCheckedType(Type.REAL());
            }
        }

      else

        innerPadding.setCheckedType(actualType.range());

      abstraction = new Abstraction(parameters,outerPadding);
      abstraction.setNonExitable();
      abstraction.setSortedArities();
      shiftOffsets(abstraction.intArity(),
                   abstraction.realArity(),
                   abstraction.objectArity());

//        /* start of code for debugging only */
//        String s = parameters.length > 1 ? "(" : "";
//        for (int i=0; i<parameters.length; i++)
//          s += parameters[i].checkedType()+(i<parameters.length-1?",":"");
//        s += (parameters.length > 1 ? ")" : "") + " -> " + outerPadding.checkedType();
//        ilog.language.tools.Debug.step(this+" has been padded into: "+abstraction + " : "+s);
//        /* end of code for debugging only */

      return abstraction;
    }


  // HAK 2 HAK -> this below was added by PV - look into this later.

  public void sanitizeTypeReferences ()
    {
      setType(type().sanitizeTypeReferences());
      
      for (int i=0; i<numberOfSubexpressions(); i++)
          if(subexpression(i) != null)
              subexpression(i).sanitizeTypeReferences();
    }

  public abstract String toString ();

  // Added by PV : for debugging I need printing with more
  // information than what is provided by toString().
  //
  // toTypedString() displays the checked type of every expression, when it exists.
  public abstract String toTypedString ();

  // HAK -> PV : what the hell is this???
  protected String typed (String s)
    {
        if(checkedType() == null) {
            return s;
        } else {
            return "(" + s + ") : " + checkedType();
        }
    }

}
