package ilog.language.design.kernel;

/**
 * @version     Last modified on Wed Jul 24 12:18:21 2002 by pviry
 * @version          modified on Mon Jul 08 17:20:39 2002 by hak
 * @author      <a href="mailto:hak@ilog.fr">Hassan A&iuml;t-Kaci</a>
 * @copyright   &copy; 2001 <a href="http://www.ilog.fr/">ILOG, S.A.</a>
 */

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

import java.util.ArrayList;

/**
 * This class represents a syntactic construct that is often used,
 * albeit with different, though related, interpretations. A dotted
 * notation is an expression (most often an application, but it could be
 * any composition - of <i>what</i> to/with <i>what</i> to be determined
 * according to partial type analysis).

 * Thus, this class can be used to represent a particular kind of
 * functional applications <i>&agrave; la</i> object-oriented
 * programming; or (equivalently), arrow composition in Category
 * Theory. More precisely, it represents the application (resp.,
 * composition) of an <tt>Expression</tt> to (resp., with) another
 * <tt>Expression</tt>, those <tt>Expression</tt>s being determined
 * according to the type of the expression on the <i>left</i> of the
 * "dot".

 * <p> Thus, a dotted notation is interpreted as follows.<p>

 * A <tt>DottedNotation</tt> object is a wrapper of an expression - most
 * often, of an application, but more generally, of any composition. It
 * is a binary expression of the form
 * <i><tt>e<sub>1</sub><b>.</b>e<sub>2</sub></tt></i>, where the
 * <i><tt>e<sub><i>i</i></sub></tt></i>'s,
 * <i>i=<tt>1</tt>,<tt>2</tt></i>, are expressions. This is
 * interpreted depending on the type of
 * <i><tt>e<sub>1</sub></tt></i> as follows:

 * <ul>
 * <p><li>if <i><tt>e<sub>1</sub></tt></i>'s type is a class <i><tt>C</tt></i>,
 * then this is interpreted as:
 * <pre>
 * <i><tt><b>mangle</b>(e<sub>1</sub>:C,e<sub>2</sub>)<b>args(</b>e<sub>2</sub></tt><b>)</b></i>
 * </pre>
 * where <i><tt><b>mangle</b>(o:C,e)</tt></i> is some "mangling"
 * of the name of the member of the object expression <i><tt>o</tt></i> of class type
 * <i><tt>C</tt></i> being so-referred, and <i><tt><b>args</b>(e)</tt></i> is either
 * the empty string or the args of the member expression <i><tt>e</tt></i>.

 * <p>
 *
 * For example, consider:
 * <pre>
 * counter.set(1);
 * </pre>
 * when <tt>counter : Counter</tt> is an object of class <tt>Counter</tt>,
 * with method (say) <tt>Counter_set : (Counter, int) -> int</tt>. Then,
 * for example, a default "mangling"
 * <i><tt><b>mangle</b>(C,member)</tt></i>
 * may simply concatenate the names of the class and the member separated with an underscore
 * character ('<tt>_</tt>'), followed by '<tt>(</tt>',  the object, and '<tt>)</tt>'; in other words:
 * <i><tt><b>mangle</b>(counter:Counter,set(1))</tt></i> = "<i><tt>Counter_set(counter)</tt></i>" and
 * <i><tt><b>args(</b>set(1)<b>)</b></tt> = "<tt>(1)</tt>"</i>. Therefore,
 * <pre>
 * counter.set(1)  ====> Counter_set(counter)(1);
 * </pre>
 * that is:
 * <pre>
 * counter.set(1)  ====> Counter_set(counter,1);
 * </pre>


 * <p><li>if <i><tt>e<sub>1</sub></tt></i>'s type is a tuple type
 * <tt>&lt;T<sub>1</sub>,...T<sub>n</sub>&gt;</tt> or
 * <tt>&lt;l<sub>1</sub>:T<sub>1</sub>,...l<sub>n</sub>:T<sub>n</sub>&gt;</tt>,
 * then this is interpreted as:

 * <pre>
 * <i><tt><b>project</b><sub>e<sub>2</sub></sub>(e<sub>1</sub>)</tt></i>
 * </pre>

 * where <i><tt><b>project</b><sub>e</sub></tt></i> is a tuple
 * <i>projection</i> of type <tt>&lt;T<sub>1</sub>,...T<sub>n</sub>&gt;
 * -> T<sub>k</sub></tt> and <tt>e</tt>=<tt>k</tt>,
 * <tt>k=1,...,n</tt>, or of type
 * <tt>&lt;l<sub>1</sub>:T<sub>1</sub>,...l<sub>n</sub>:T<sub>n</sub>&gt;
 * -> T<sub>k</sub></tt> and <tt>e</tt>=<tt>l<sub>k</sub></tt>,
 * <tt>k=1,...,n</tt>.

 * <p> This is interpreted as:

 * <pre>
 * TupleProjection(e<sub>1</sub>,e<sub>2</sub>) : T<sub>k</sub>.
 * </pre>

 * For example:
 * <pre>
 * &lt;name:="a",number:=1&gt;.name ====> TupleProjection(&lt;name:="a",number:=1&gt;,name)
 * </pre>
 * In other words,
 * <pre>
 * <i><tt><b>project</b><sub>name</sub> : <b>&lt;name:string,number:int&gt; -> string</b> (&lt;name:="a",number:=1&gt;) : <b>string</b> </tt></i>
 * </pre>

 * <p><li> Otherwise, the default is simply to interpret this as the application:
 * <pre>
 * <i><tt><b>member_name(</b>e<sub>2</sub><b>)</b>(e<sub>1</sub>)(<b>args(</b>e<sub>2</sub><b>)</b>)</tt></i>.
 * </pre>
 * This default behavior can be overridden and customized through the methods:
 * <tt>setNoDefault()</tt> and
 * <tt>setDefault(Expression)</tt>.
 * </ul>
 */

public class DottedNotation extends ProtoExpression
{
  /**
   * This is the symbol tables.
   */
  private Tables _tables;

  /**
   * This is the "left" expression of this dotted notation.
   */
  private Expression _left;
  /**
   * This is the "rite" expression of this dotted notation.
   */
  private Expression _rite;

  private boolean _default = true;

  public final DottedNotation setNoDefault ()
    {
      _default = false;
      return this;
    }

  public final DottedNotation setDefault (Expression expression)
    {
      _actualExpression = expression;
      return this;
    }

  /**
   * This is the "actual"  expression this dotted notation is eventually
   * translated into.
   */
  private Expression _actualExpression;

//    /**
//     * Constructs a dotted notation with the specified tables, object expression, and
//     * member/args.
//     */
//    public DottedNotation (Tables tables, Expression object, String member, ArrayList args)
//      {
//        _tables = tables;
//        _left   = left;
//        _rite   = rite;
//      }

  /**
   * Constructs a dotted notation with the specified tables, and left and rite expressions.
   */
  public DottedNotation (Tables tables, Expression left, Expression rite)
    {
      _tables = tables;
      _left   = left;
      _rite   = rite;
    }

  public final int numberOfSubexpressions ()
    {
      return 2;
    }

  public final Expression subexpression (int n) throws NoSuchSubexpressionException
    {
      if (n == 0)
        return _left;
      if (n == 1)
        return _rite;

      throw new NoSuchSubexpressionException(this,n);
    }

  public final Expression left ()
    {
      return _left;
    }

  public final Expression rite ()
    {
      return _rite;
    }

  public final DottedNotation setTables (Tables tables)
    {
      _tables = tables;
      return this;
    }

  public final DottedNotation setLeft (Expression left)
    {
      _left = left;
      return this;
    }

  public final DottedNotation setRite (Expression rite)
    {
      _rite = rite;
      return this;
    }

  public final Expression actualExpression ()
    {
      return _actualExpression;
    }

  public final void setCheckedType ()
    {
      setCheckedType(type().copy());
      _actualExpression.setCheckedType();
    }

  public final Expression sanitizeNames (ParameterStack parameters, ClassTypeHandle handle)
    {
      _left = _left.sanitizeNames(parameters,handle);
      _rite = _rite.sanitizeNames(parameters,handle);      
      return this;
    }

  public final void sanitizeSorts (Enclosure enclosure)
    {
      _left.sanitizeSorts(enclosure);
      _rite.sanitizeSorts(enclosure);
    }

  public final Expression shiftOffsets (int intShift, int realShift, int objectShift,
                                        int intDepth, int realDepth, int objectDepth)
    {
      _left = _left.shiftOffsets(intShift,realShift,objectShift,
                                 intDepth,realDepth,objectDepth);
      _rite = _rite.shiftOffsets(intShift,realShift,objectShift,
                                 intDepth,realDepth,objectDepth);
      return this;
    }

  /**
   * This is a <i>"fragile"</i> typing rule in the sense that it relies
   * on partial type-checking of one (the left) of its
   * subexpressions. This means that <i>ambiguous types for the left
   * expression are not allowed!</i> When a single typing has been
   * obtained, however, type-checking a dotted notation simply delegates
   * the "real" type-checking to the underlying actual expression.
   */
  public void typeCheck (TypeChecker typeChecker) throws TypingErrorException
    {
      TypeChecker tmp = new TypeChecker();
      ArrayList possibleLeftTypes = tmp.allTypes(_left);

      if (possibleLeftTypes.isEmpty())
        typeChecker.error
            (new TypingErrorException
                ("this dotted notation's left expression has no type!"),
             _left);
      
      if (possibleLeftTypes.size() > 1)
        typeChecker.error
            (new TypingErrorException
                ("this dotted notation's left expression has ambiguous types: "
                 +possibleLeftTypes),
             _left);

      Type leftType = ((Type)possibleLeftTypes.get(0)).value();

      switch (leftType.kind())
          {
          case Type.CLASS:
            _actualExpression
                = new Application(new Application(new Global(_tables,
                                                             mangledMemberName((ClassType)leftType)),
                                                  _left),
                                  _rite.args());
            break;

          case Type.TUPLE: case Type.NAMED_TUPLE:
            _actualExpression
                = new TupleProjection(_left,(Constant)_rite);
            break;

          default:
            if (!_default)
              typeChecker.error
                  (new TypingErrorException
                      ("dotted notation has bad left type (must be Class or Tuple; found: "
                       +leftType+")"),
                   this);
            _actualExpression
                = new Application(new Application(new Global(_tables,
                                                             _rite.memberName()),
                                                  _left),
                                  _rite.args());
            break;
          }

      _actualExpression.typeCheck(typeRef(),typeChecker);
    }

  public final String mangledMemberName (ClassType classType)
    {
      return classType.mangledMemberName(this);
    }
  
  public final String mangle (ClassType classType)
    {
      return classType.mangle(this);
    }
  
  /**
   * Compiling a dotted notation simply delegates the "real" compiling
   * to the underlying actual expression.
   */
  public final void compile (Compiler compiler)
    {
      _actualExpression.compile(compiler);
    }


  public String toString ()
    {
      return _left + " . " + _rite;
    }

  // Added by PV
  public String toTypedString ()
    {
      return typed(_left.toTypedString() + " . " + _rite.toTypedString());
    }
}
