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

package ilog.language.design.kernel;

/**
 * @version     Last modified on Fri Oct 18 17:51:57 2002 by hak
 * @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 java.util.ArrayList;

/**
 * This class represents a scope for local variables. It may be seen as a lambda
 * abstraction that never needs to be lexically closed. It is used for creating
 * a <a href="Let.html"><tt>Let</tt></a> block as the application of such a
 * <tt>Scope</tt> to its local variable's initial values. As a result, it can
 * never be exported outside its enclosing scope, and therefore it can dispense
 * with the whole machinery of creating and maintaining lexical frames.
 *
 * @see         Abstraction
 */
public class Scope extends ProtoExpression
{
  protected Parameter[] _parameters;
  protected Expression _body;

  protected boolean _isExitable = true;

  protected int _intArity;
  protected int _realArity;
  protected int _objectArity;

  public Scope (Parameter[] parameters, Expression body)
    {
      _parameters = parameters;         // NB: all strings in this array must be interned!
      _body = body;
      _flatten();
    }

  public Scope (String parameter, Expression body)
    {
      _parameters = new Parameter[1];
      _parameters[0] = new Parameter(parameter.intern());
      _body = body;
      _flatten();
    }

  public Scope (ArrayList parameters, Expression body)
    {
      if (!parameters.isEmpty())
        {
          _parameters = new Parameter[parameters.size()];

          for (int i=0; i<_parameters.length; i++)
            _parameters[i] = new Parameter(((String)parameters.get(i)).intern());
        }
      else
        {
          _parameters = new Parameter[1];
          _parameters[0] = Parameter.VOID;
        }
      
      _body = body;
      _flatten();
    }

  protected final void _flatten ()
    {
      if (_body instanceof Scope)
        {
          Parameter[] nestedParameters = ((Scope)_body).parameters();
          Parameter[] newParameters = new Parameter[_parameters.length+nestedParameters.length];

          for (int i=_parameters.length; i-->0;)
            newParameters[i] = _parameters[i];
          for (int i=_parameters.length; i<newParameters.length;i++)
            newParameters[i] = nestedParameters[i-_parameters.length];

          _parameters = newParameters;
          _body = ((Scope)_body).body();
        }
    }

  public final boolean isExitable ()
    {
      return _isExitable;
    }

  public final Scope setIsExitable (boolean flag)
    {
      _isExitable = flag;
      return this;
    }

  public final Scope setNonExitable ()
    {
      _isExitable = false;
      return this;
    }

  public final int arity ()
    {
      return _parameters.length;
    }

  public final int intArity ()
    {
      return _intArity;
    }

  public final int realArity ()
    {
      return _realArity;
    }

  public final int objectArity ()
    {
      return _objectArity;
    }

  public final void setSortedArities ()
    {
      for (int i=0; i<_parameters.length; i++)
        switch (_parameters[i].boxSort())
          {
          case Type.INT_SORT:
            _intArity++;
            break;
          case Type.REAL_SORT:
            _realArity++;
            break;
          default:
            _objectArity++;
          }
    }

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

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

      if (n == _parameters.length)
        return _body;

      throw new NoSuchSubexpressionException(this,n);
    }

  public final Parameter[] parameters ()
    {
      return _parameters;
    }

  public final Parameter parameter (int i)
    {
      return _parameters[i];
    }

  public final Expression body ()
    {
      return _body;
    }

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

  public final void setCheckedType ()
    {
      if (isSetCheckedType()) return;
      _checkedType = type().copy();

      for (int i=0; i<_parameters.length; i++)
        _parameters[i].setCheckedType();

      _body.setCheckedType();
      setSortedArities();
    }

  public final void typeCheck (TypeChecker typeChecker) throws TypingErrorException
    {
      if (typeCheckLocked()) return;

      if (_isExitable)
        typeChecker.prove(new PushExitableGoal(this));

      Type[] parameterTypes = new Type[arity()];

      for (int i=0; i<arity(); i++)
        {
          _parameters[i].typeCheck(typeChecker);
          parameterTypes[i] = _parameters[i].typeRef();
        }
      
      typeChecker.unify(_type,new FunctionType(parameterTypes,_body.typeRef()),this);
      _body.typeCheck(typeChecker);

      if (_isExitable)
        typeChecker.prove(new PopExitableGoal(this));
    }

  public final Expression sanitizeNames (ParameterStack parameters, ClassTypeHandle handle)
    {
      for (int i=0; i<_parameters.length; i++)
        parameters.push(_parameters[i]);

      _body = _body.sanitizeNames(parameters,handle);

      for (int i=0; i<_parameters.length; i++)
        parameters.pop();

      return this;
    }

  protected boolean _isSortSanitized = false;

  public void sanitizeSorts (Enclosure enclosure)
    {
      if (_isSortSanitized) return;

      enclosure.push(this);
      _body.sanitizeSorts(enclosure);
      enclosure.pop();

      _isSortSanitized = true;
    }

  public Expression shiftOffsets (int intShift, int realShift, int objectShift,
                                  int intDepth, int realDepth, int objectDepth)
    {
      return this;
    }

  protected PushScope _pushInstruction ()
    {
      return new PushScope(_intArity,_realArity,_objectArity);
    }

  public final void compile (Compiler compiler)
    {
      compiler.generate(_pushInstruction().setIsExitable(_isExitable),_body);
    }

  public String toString ()
    {
      String s = "(scope";

      s += "(";

      for (int i=0; i<arity(); i++)
        {
          s += _parameters[i].name();
          if (i < arity()-1) s += ",";
        }

      return s + ") " + _body + ")";
    }

  // Added by PV
  public String toTypedString ()
    {
      String s = "(scope";

      s += "(";
      
      for (int i=0; i<arity(); i++)
        {
          s += _parameters[i].toTypedString();
          if (i < arity()-1) s += ",";
        }

      return typed(s + ") " + _body.toTypedString()) + ")";
    }
}
