Package wyil.lang

Source Code of wyil.lang.Type

// This file is part of the Whiley-to-Java Compiler (wyjc).
//
// The Whiley-to-Java Compiler is free software; you can redistribute
// it and/or modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 3 of the License, or (at your option) any later version.
//
// The Whiley-to-Java Compiler is distributed in the hope that it
// will be useful, but WITHOUT ANY WARRANTY; without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE.  See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with the Whiley-to-Java Compiler. If not, see
// <http://www.gnu.org/licenses/>
//
// Copyright 2010, David James Pearce.

package wyil.lang;

import java.io.IOException;
import java.util.*;

import wyautl_old.io.BinaryAutomataReader;
import wyautl_old.io.BinaryAutomataWriter;
import wyautl_old.lang.*;
import wyautl_old.lang.Automaton.State;
import wycc.lang.NameID;
import wyfs.io.BinaryInputStream;
import wyfs.io.BinaryOutputStream;
import wyfs.util.Trie;
import wyil.util.type.*;

/**
* A structural type. See
* http://whiley.org/2011/03/07/implementing-structural-types/ for more on how
* this class works.
*
* @author David J. Pearce
*
*/
public abstract class Type {
  // =============================================================
  // Debug Code
  // =============================================================

//  private static final HashSet<Type> distinctTypes = new HashSet<Type>();
//
  private static boolean canonicalisation = true;
//  private static int equalsCount = 0;
//  private static int normalisedCount = 0;
//  private static int unminimisedCount = 0;
//  private static int minimisedCount = 0;

//  static {
//    Thread _shutdownHook = new Thread(Type.class.getName()
//        + ".shutdownHook") {
//      public void run() {
//        shutdown();
//      }
//    };
//    Runtime.getRuntime().addShutdownHook(_shutdownHook);
//  }

//  public static void shutdown() {
//    System.err.println("#TYPE EQUALITY TESTS: " + equalsCount);
//    System.err.println("#TYPE NORMALISATIONS: " + normalisedCount + " (" + unminimisedCount + " -> " + minimisedCount +")");
//    System.err.println("#DISTINCT TYPES: " + distinctTypes.size());
//  }

  // =============================================================
  // Type Constructors
  // =============================================================

  public static final Any T_ANY = new Any();
  public static final Void T_VOID = new Void();
  public static final Null T_NULL = new Null();
  public static final Bool T_BOOL = new Bool();
  public static final Byte T_BYTE = new Byte();
  public static final Char T_CHAR = new Char();
  public static final Int T_INT = new Int();
  public static final Real T_REAL = new Real();
  public static final Strung T_STRING = new Strung();
  public static final Meta T_META = new Meta();

  // the following are strictly unnecessary, but since they occur very
  // commonly it is helpful to provide them as constants.

  public static final Reference T_REF_ANY = Reference(T_ANY);

  /**
   * The type representing all possible set types.
   */
  public static final Set T_SET_ANY = Set(T_ANY,false);

  /**
   * The type representing all possible list types.
   */
  public static final List T_LIST_ANY = List(T_ANY,false);

  /**
   * The type representing all possible map types.
   */
  public static final Map T_MAP_ANY = Map(T_ANY,T_ANY);

  /**
   * Construct a tuple type using the given element types.
   *
   * @param element
   */
  public static final Type.Tuple Tuple(Type... elements) {
    Type r = construct(K_TUPLE, null, elements);
    if(r instanceof Type.Tuple) {
      return (Type.Tuple) r;
    } else {
      throw new IllegalArgumentException("invalid arguments for Type.Tuple()");
    }
  }

  /**
   * Construct a tuple type using the given element types.
   *
   * @param element
   */
  public static final Type.Tuple Tuple(java.util.List<Type> elements) {
    Type r = construct(K_TUPLE, null, elements);
    if(r instanceof Type.Tuple) {
      return (Type.Tuple) r;
    } else {
      throw new IllegalArgumentException("invalid arguments for Type.Tuple()");
    }
  }

  /**
   * Construct a reference type using the given element type.
   *
   * @param element
   */
  public static final Type.Reference Reference(Type element) {
    Type r = construct(K_REFERENCE, null, element);
    if (r instanceof Type.Reference) {
      return (Type.Reference) r;
    } else {
      throw new IllegalArgumentException(
          "invalid arguments for Type.Reference()");
    }
  }

  public static final Nominal Nominal(NameID name) {
    if (name == null) {
      throw new IllegalArgumentException(
          "nominal name cannot be null");
    }
    return new Nominal(name);
  }

  /**
   * Construct a set type using the given element type.
   *
   * @param element
   */
  public static final Type.Set Set(Type element, boolean nonEmpty) {
    return (Type.Set) construct(K_SET, nonEmpty, element);
  }

  /**
   * Construct a list type using the given element type.
   *
   * @param element
   */
  public static final Type.List List(Type element, boolean nonEmpty) {
    return (Type.List) construct(K_LIST, nonEmpty, element);
  }

  /**
   * Construct a map type using the given key and value types.
   *
   * @param element
   */
  public static final Type.Map Map(Type key, Type value) {
    return (Type.Map) construct(K_MAP, null, key, value);
  }

  /**
   * Construct a union type using the given type bounds
   *
   * @param element
   */
  public static final Type Union(Collection<Type> bounds) {
    return construct(K_UNION,null,bounds);
  }

  /**
   * Construct a union type using the given type bounds
   *
   * @param element
   */
  public static final Type Union(Type... bounds) {
    return construct(K_UNION,null,bounds);
  }

  /**
   * Construct a difference of two types.
   *
   * @param element
   */
  public static final Type Negation(Type element) {
    return construct(K_NEGATION,null,element);
  }

  /**
   * Construct a function type using the given return and parameter types.
   *
   * @param element
   */
  public static final Type.Function Function(Type ret, Type throwsClause,
      Collection<Type> params) {
    Type[] rparams = new Type[params.size()+2];
    int i = 2;
    for (Type t : params) { rparams[i++] = t; }
    rparams[0] = ret;
    rparams[1] = throwsClause;
    Type r = construct(K_FUNCTION, null, rparams);
    if (r instanceof Type.Function) {
      return (Type.Function) r;
    } else {
      throw new IllegalArgumentException(
          "invalid arguments for Type.Function() - " + params);
    }
  }

  /**
   * Construct a function type using the given return and parameter types.
   *
   * @param element
   */
  public static final Type.Function Function(Type ret, Type throwsClause,
      Type... params) {
    Type[] rparams = new Type[params.length+2];
    System.arraycopy(params, 0, rparams, 2, params.length);
    rparams[0] = ret;
    rparams[1] = throwsClause;
    Type r = construct(K_FUNCTION, null, rparams);
    if (r instanceof Type.Function) {
      return (Type.Function) r;
    } else {
      throw new IllegalArgumentException(
          "invalid arguments for Type.Function()");
    }
  }

  /**
   * Construct a method type using the given receiver, return and parameter types.
   *
   * @param element
   */
  public static final Type.Method Method(Type ret, Type throwsClause,
      Collection<Type> params) {
    Type[] rparams = new Type[params.size()+2];
    int i = 2;
    for (Type t : params) { rparams[i++] = t; }
    rparams[0] = ret;
    rparams[1] = throwsClause;
    Type r = construct(K_METHOD, null, rparams);
    if (r instanceof Type.Method) {
      return (Type.Method) r;
    } else {
      throw new IllegalArgumentException(
          "invalid arguments for Type.Method()");
    }
  }

  /**
   * Construct a function type using the given return and parameter types.
   *
   * @param element
   */
  public static final Type.Method Method(Type ret,
      Type throwsClause, Type... params) {
    Type[] rparams = new Type[params.length+2];
    System.arraycopy(params, 0, rparams, 2, params.length);
    rparams[0] = ret;
    rparams[1] = throwsClause;
    Type r = construct(K_METHOD, null, rparams);
    if (r instanceof Type.Method) {
      return (Type.Method) r;
    } else {
      throw new IllegalArgumentException(
          "invalid arguments for Type.Method()");
    }
  }

  /**
   * Construct a record type using the given fields. The given record may be
   * either "open" or "closed". A closed record indicates a type which must
   * have exactly the given mapping of fields to types. An open record
   * indicates a type which may have more fields than that listed. The notion
   * of open records corresponds to "width subtyping" in type theory.
   *
   * @param element
   */
  public static final Type.Record Record(boolean isOpen, java.util.Map<String,Type> fields) {
    java.util.Set<String> keySet = fields.keySet();
    Record.State keys = new Record.State(isOpen,keySet);
    Collections.sort(keys);
    Type[] types = new Type[keys.size()];
    for(int i=0;i!=types.length;++i) {
      types[i] = fields.get(keys.get(i));
    }
    Type r = construct(K_RECORD,keys,types);
    if (r instanceof Type.Record) {
      return (Type.Record) r;
    } else {
      throw new IllegalArgumentException(
          "invalid arguments for Type.Record()");
    }
  }

  /**
   * Close a recursive type using a given label (i.e. nominal type).
   * Essentially, this traverses the given type and routes each occurrence of
   * the label to recursively point to the type's root. For example, we
   * construct the recursive type <code>X<null|{X next}></code> as follows:
   *
   * <pre>
   * HashMap<String,Type> fields = new HashMap<String,Type>();
   * Type.Nominal label = T_NOMINAL("X");
   * fields.put("next",label);
   * Type tmp = T_UNION(T_NULL,T_RECORD(fields));
   * Type type = T_RECURSIVE(label,tmp);
   * </pre>
   *
   * <b>NOTE:</b> it is invalid to close a type which does not contain at
   * least one instance of the given label. Doing this will cause an exception
   * to be raised.
   *
   * @param label
   *            --- label to be used for closing.
   * @param type
   *            --- type to be closed.
   * @return
   */
  public static final Type Recursive(NameID label, Type type) {
    // first stage, identify all matching labels
    if (type instanceof Leaf) {
      throw new IllegalArgumentException("cannot close a leaf type");
    }
    Compound compound = (Compound) type;
    Automaton automaton = compound.automaton;
    State[] nodes = automaton.states;
    int[] rmap = new int[nodes.length];
    for (int i = 0; i != nodes.length; ++i) {
      State c = nodes[i];
      if (c.kind == K_NOMINAL && c.data.equals(label)) {
        rmap[i] = 0;
      } else {
        rmap[i] = i;
      }
    }
    return construct(Automata.remap(automaton, rmap));
  }

  /**
   * The following code converts a "type string" into an actual type. This is
   * useful, amongst other things, for debugging.
   *
   * @param str
   * @return
   */
  public static Type fromString(String str) {
    return new TypeParser(str).parse();
  }

  /**
   * This is a utility helper for constructing types. In particular, it's
   * useful for determine whether or not a type needs to be closed. An open
   * type is one which contains a "dangling" reference to some node which
   * needs to be connected to back to form a cycle.
   *
   * @param label
   * @param t
   * @return
   */
  public static boolean isOpen(NameID label, Type t) {
    if (t instanceof Leaf) {
      return false;
    }
    Compound graph = (Compound) t;
    for (State n : graph.automaton.states) {
      if (n.kind == K_NOMINAL && n.data.equals(label)) {
        return true;
      }
    }
    return false;
  }

  /**
   * This is a utility helper for constructing types. In particular, it's
   * useful for determine whether or not a type needs to be closed. An open
   * type is one which contains a "dangling" reference to some node which
   * needs to be connected to back to form a cycle.
   *
   * @param target
   * @param t
   * @return
   */
  public static boolean isOpen(java.util.Set<NameID> labels, Type t) {
    if (t instanceof Leaf) {
      return false;
    }
    Compound graph = (Compound) t;
    for (State n : graph.automaton.states) {
      if (n.kind == K_NOMINAL && labels.contains(n.data)) {
        return true;
      }
    }
    return false;
  }

  // =============================================================
  // Readers / Writers
  // =============================================================

  /**
   * <p>
   * A <code>BinaryReader</code> will read types from a binary input stream.
   * The types should be written to the stream using <code>BinaryWriter</code>
   * .
   * </p>
   * <p>
   * <b>NOTE:</b> Under-the-hood, this class is essentially a wrapper for
   * <code>BinaryAutomataReader</code>.
   * </p>
   *
   * @author David J. Pearce
   *
   */
  public static class BinaryReader extends BinaryAutomataReader {
    public BinaryReader(BinaryInputStream reader) {
      super(reader);
    }
    public Type readType() throws IOException {
      Type t = construct(read());
      return t;
    }
    public Automaton.State readState() throws IOException {
      Automaton.State state = super.readState();
      if (state.kind == Type.K_NOMINAL) {
        String module = readString();
        String name = readString();
        state.data = new NameID(Trie.fromString(module), name);
      } else if(state.kind == Type.K_RECORD) {
        boolean isOpen = reader.read_bit();
        int nfields = reader.read_uv();
        Record.State fields = new Record.State(isOpen);
        for(int i=0;i!=nfields;++i) {
          fields.add(readString());
        }
        state.data = fields;
      else if(state.kind == Type.K_LIST || state.kind == Type.K_SET) {
        boolean nonEmpty = reader.read_bit();
        state.data = nonEmpty;
      }
      return state;
    }

    private String readString() throws IOException {
      String r = "";
      int nchars = reader.read_uv();
      for(int i=0;i!=nchars;++i) {
        char c = (char) reader.read_u16();
        r = r + c;
      }
      return r;
    }
  }

  /**
   * <p>
   * A <code>BinaryWriter</code> will write types to a binary output stream.
   * The types should be read back from the stream using
   * <code>BinaryReader</code> .
   * </p>
   * <p>
   * <b>NOTE:</b> Under-the-hood, this class is essentially a wrapper for
   * <code>BinaryAutomataWriter</code>.
   * </p>
   *
   * @author David J. Pearce
   *
   */
  public static class BinaryWriter extends BinaryAutomataWriter {
    public BinaryWriter(BinaryOutputStream writer) {
      super(writer);
    }
    public void write(Type t) throws IOException {
      write(destruct(t));
    }

    public void write(Automaton.State state) throws IOException {
      super.write(state);
      if (state.kind == Type.K_NOMINAL) {
        NameID name = (NameID) state.data;
        writeString(name.module().toString());
        writeString(name.name());
      } else if(state.kind == Type.K_RECORD) {
        Record.State fields = (Record.State) state.data;
        writer.write_bit(fields.isOpen);
        writer.write_uv(fields.size());
        for(String field : fields) {
          writeString(field);
        }
      } else if(state.kind == Type.K_LIST || state.kind == Type.K_SET) {
        writer.write_bit((Boolean) state.data);
      }
    }

    private void writeString(String str) throws IOException {
      writer.write_uv(str.length());
      for (int i = 0; i != str.length(); ++i) {
        writer.write_u16(str.charAt(i));
      }
    }
  }

  // =============================================================
  // Type operations
  // =============================================================

  /**
   * Determine whether type <code>t2</code> is an <i>explicit coercive
   * subtype</i> of type <code>t1</code>.
   */
  public static boolean isExplicitCoerciveSubtype(Type t1, Type t2) {
    Automaton a1 = destruct(t1);
    Automaton a2 = destruct(t2);
    ExplicitCoercionOperator relation = new ExplicitCoercionOperator(a1,a2);
    return relation.isSubtype(0, 0);
  }

  /**
   * Determine whether type <code>t2</code> is a <i>subtype</i> of type
   * <code>t1</code> (written t1 :> t2). In other words, whether the set of
   * all possible values described by the type <code>t2</code> is a subset of
   * that described by <code>t1</code>.
   */
  public static boolean isSubtype(Type t1, Type t2) {
    Automaton a1 = destruct(t1);
    Automaton a2 = destruct(t2);
    SubtypeOperator relation = new SubtypeOperator(a1,a2);
    return relation.isSubtype(0, 0);
  }

  /**
   * <p>
   * Contractive types are types which cannot accept value because they have
   * an <i>unterminated cycle</i>. An unterminated cycle has no leaf nodes
   * terminating it. For example, <code>X<{X field}></code> is contractive,
   * where as <code>X<{null|X field}></code> is not.
   * </p>
   *
   * <p>
   * This method returns true if the type is contractive, or contains a
   * contractive subcomponent. For example, <code>null|X<{X field}></code> is
   * considered contracted.
   * </p>
   *
   * @param type --- type to test for contractivity.
   * @return
   */
  public static boolean isContractive(Type type) {
    if(type instanceof Leaf) {
      return false;
    } else {
      Automaton automaton = ((Compound) type).automaton;
      return TypeAlgorithms.isContractive(automaton);
    }
  }

  /**
   * Compute the intersection of two types. The resulting type will only
   * accept values which are accepted by both types being intersected.. In
   * many cases, the only valid intersection will be <code>void</code>.
   *
   * @param t1
   * @param t2
   * @return
   */
  public static Type intersect(Type t1, Type t2) {
    return TypeAlgorithms.intersect(t1,t2);
  }

  public static Reference effectiveReference(Type t) {
    if(t instanceof Type.Reference) {
      return (Type.Reference) t;
    }
    return null;
  }

  public static FunctionOrMethod effectiveFunctionOrMethod(Type t) {
    if(t instanceof Type.FunctionOrMethod) {
      return (Type.FunctionOrMethod) t;
    }
    return null;
  }

  // =============================================================
  // Primitive Types
  // =============================================================

  /**
   * A leaf type represents a type which has no component types. For example,
   * primitive types like <code>int</code> and <code>real</code> are leaf
   * types.
   *
   * @author David J. Pearce
   *
   */
  public static class Leaf extends Type {}

  /**
   * A void type represents the type whose variables cannot exist! That is,
   * they cannot hold any possible value. Void is used to represent the return
   * type of a function which does not return anything. However, it is also
   * used to represent the element type of an empty list of set. <b>NOTE:</b>
   * the void type is a subtype of everything; that is, it is bottom in the
   * type lattice.
   *
   * @author David J. Pearce
   *
   */
  public static final class Void extends Leaf {
    private Void() {}
    public boolean equals(Object o) {
      return this == o;
    }
    public int hashCode() {
      return 1;
    }
    public String toString() {
      return "void";
    }
  }

  /**
   * The type any represents the type whose variables may hold any possible
   * value. <b>NOTE:</b> the any type is top in the type lattice.
   *
   * @author David J. Pearce
   *
   */
  public static final class Any extends Leaf {
    private Any() {}
    public boolean equals(Object o) {
      return o == T_ANY;
    }
    public int hashCode() {
      return 1;
    }
    public String toString() {
      return "any";
    }
  }

  /**
   * The null type is a special type which should be used to show the absence
   * of something. It is distinct from void, since variables can hold the
   * special <code>null</code> value (where as there is no special "void"
   * value). With all of the problems surrounding <code>null</code> and
   * <code>NullPointerException</code>s in languages like Java and C, it may
   * seem that this type should be avoided. However, it remains a very useful
   * abstraction to have around and, in Whiley, it is treated in a completely
   * safe manner (unlike e.g. Java).
   *
   * @author David J. Pearce
   *
   */
  public static final class Null extends Leaf {
    private Null() {}
    public boolean equals(Object o) {
      return this == o;
    }
    public int hashCode() {
      return 2;
    }
    public String toString() {
      return "null";
    }
  }

  /**
   * The type mets represents the type of types. That is, values of this type
   * are themselves types. (think reflection, where we have
   * <code>class Class {}</code>).
   *
   * @author David J. Pearce
   *
   */
  public static final class Meta extends Leaf {
    private Meta() {}
    public boolean equals(Object o) {
      return o == T_META;
    }
    public int hashCode() {
      return 1;
    }
    public String toString() {
      return "type";
    }
  }

  /**
   * Represents the set of boolean values (i.e. true and false)
   * @author David J. Pearce
   *
   */
  public static final class Bool extends Leaf {
    private Bool() {}
    public boolean equals(Object o) {
      return o == T_BOOL;
    }
    public int hashCode() {
      return 3;
    }
    public String toString() {
      return "bool";
    }
  }

  /**
   * Represents a unicode character.
   *
   * @author David J. Pearce
   *
   */
  public static final class Char extends Leaf {
    private Char() {}
    public boolean equals(Object o) {
      return o == T_CHAR;
    }
    public int hashCode() {
      return 4;
    }
    public String toString() {
      return "char";
    }
  }

  /**
   * Represents a sequence of 8 bits. Note that, unlike many languages, there
   * is no representation associated with a byte. For example, to extract an
   * integer value from a byte, it must be explicitly decoded according to
   * some representation (e.g. two's compliment) using an auxillary function
   * (e.g. <code>Byte.toInt()</code>).
   *
   * @author David J. Pearce
   *
   */
  public static final class Byte extends Leaf {
    private Byte() {}
    public boolean equals(Object o) {
      return o == T_BYTE;
    }
    public int hashCode() {
      return 4;
    }
    public String toString() {
      return "byte";
    }
  }


  /**
   * Represents the set of (unbound) integer values. Since integer types in
   * Whiley are unbounded, there is no equivalent to Java's
   * <code>MIN_VALUE</code> and <code>MAX_VALUE</code> for <code>int</code>
   * types.
   *
   * @author David J. Pearce
   *
   */
  public static final class Int extends Leaf {
    private Int() {}
    public boolean equals(Object o) {
      return o == T_INT;
    }
    public int hashCode() {
      return 4;
    }
    public String toString() {
      return "int";
    }
  }

  /**
   * Represents the set of (unbound) rational numbers.
   *
   * @author David J. Pearce
   *
   */
  public static final class Real extends Leaf {
    private Real() {}
    public boolean equals(Object o) {
      return o == T_REAL;
    }
    public int hashCode() {
      return 5;
    }
    public String toString() {
      return "real";
    }
  }

  /**
   * Represents a string of characters
   *
   * @author David J. Pearce
   *
   */
  public static final class Strung extends Leaf implements EffectiveIndexible {
    private Strung() {}
    public boolean equals(Object o) {
      return o == T_STRING;
    }

    public Type key() {
      return T_INT;
    }

    public Type value() {
      return T_CHAR;
    }

    public Type element() {
      return T_CHAR;
    }

    public int hashCode() {
      return 6;
    }

    public String toString() {
      return "string";
    }

    public EffectiveIndexible update(Type key, Type value) {
      if(key == T_INT && value == T_CHAR) {
        return this;
      } else {
        return Map(Type.Union(key,T_INT),Type.Union(value,T_CHAR));
      }
    }
  }


  /**
   * The existential type represents the an unknown type, defined at a given
   * position.
   *
   * @author David J. Pearce
   *
   */
  public static final class Nominal extends Leaf {
    private NameID nid;
    private Nominal(NameID name) {
      nid = name;
    }
    public boolean equals(Object o) {
      if(o instanceof Nominal) {
        Nominal e = (Nominal) o;
        return nid.equals(e.nid);
      }
      return false;
    }
    public NameID name() {
      return nid;
    }
    public int hashCode() {
      return nid.hashCode();
    }
    public String toString() {
      return nid.toString();
    }
  }

  // =============================================================
  // Compound Faces
  // =============================================================

  /*
   * The compound faces are not technically necessary, as they simply provide
   * interfaces to the underlying nodes of a compound type. However, they
   * certainly make it more pleasant to use this library.
   */

  public static class Compound extends Type {
    //protected Automaton automaton;
    public Automaton automaton;

    public Compound(Automaton automaton) {
      this.automaton = automaton;
    }

    public int hashCode() {
      return automaton.hashCode();
    }

    public boolean equals(Object o) {
      if (o instanceof Compound) {
        Compound c = (Compound) o;
        //equalsCount++;
        if(canonicalisation) {
          return automaton.equals(c.automaton);
        } else {
          return isSubtype(this, c) && isSubtype(c, this);
        }
      }
      return false;
    }

    public String toString() {
      // First, we need to find the headers of the computation. This is
      // necessary in order to mark the start of a recursive type.
      BitSet headers = new BitSet(automaton.size());
      BitSet visited = new BitSet(automaton.size());
      BitSet onStack = new BitSet(automaton.size());
      findHeaders(0,visited,onStack,headers,automaton);
      visited.clear();
      String[] titles = new String[automaton.size()];
      int count = 0;
      for(int i=0;i!=automaton.size();++i) {
        if(headers.get(i)) {
          titles[i] = headerTitle(count++);
        }
      }
      return Type.toString(0,visited,titles,automaton);
    }
  }

  /**
   * A type which is either a tuple, or a union of tuples. An effective
   * tuple gives access to a subset of the accessible elements
   * guaranteed to be in the type. For example, consider this type:
   *
   * <pre>
   * (int,int,int) | (int,[int])
   * </pre>
   *
   * Here, we're guaranteed to have at least two elements. Therefore, the effective
   * tuple type is <code>(int,int|[int])</code>.
   *
   * @return
   */
  public interface EffectiveTuple {
    public Type element(int index);
    public java.util.List<Type> elements();
  }

  /**
   * A tuple type describes a compound type made up of two or more
   * subcomponents. It is similar to a record, except that fields are
   * effectively anonymous.
   *
   * @author David J. Pearce
   *
   */
  public static final class Tuple extends Compound implements EffectiveTuple {
    private Tuple(Automaton automaton) {
      super(automaton);
    }
    public int size() {
      int[] values = (int[]) automaton.states[0].children;
      return values.length;
    }
    public Type element(int index) {
      int[] values = (int[]) automaton.states[0].children;
      return construct(Automata.extract(automaton,values[index]));
    }
    public java.util.List<Type> elements() {
      int[] values = (int[]) automaton.states[0].children;
      ArrayList<Type> elems = new ArrayList<Type>();
      for(Integer i : values) {
        elems.add(construct(Automata.extract(automaton,i)));
      }
      return elems;
    }
  }

  /**
   * A type which is either a set, list, map, string or a union of such
   * types. An effective collection gives access to an effective element type,
   * which is the union of possible element types.
   *
   * <pre>
   * {int} | [real]
   * </pre>
   *
   * Here, the effective element type is int|real.
   *
   * @return
   */
  public interface EffectiveCollection {
    public Type element();
  }

  /**
   * A type which is either a list, map, string or a union of such
   * types. An effective collection gives access to an effective element type,
   * which is the union of possible key and value types, and allows them to be
   * updated.
   *
   * <pre>
   * {int=>string} | [real]
   * </pre>
   *
   * Here, the effective key type is int, whilst the effective value type is
   * string|real.
   *
   * @return
   */
  public interface EffectiveIndexible extends EffectiveCollection {
    public Type key();

    public Type value();

    public EffectiveIndexible update(Type key, Type Value);
  }

  /**
   * A type which is either a set, or a union of sets. An effective set gives
   * access to an effective element type, which is the union of possible
   * element types.
   *
   * <pre>
   * {int} | {real}
   * </pre>
   *
   * Here, the effective element type is int|real.
   *
   * @return
   */
  public interface EffectiveSet extends EffectiveCollection {
    public Type element();
  }

  /**
   * A set type describes set values whose elements are subtypes of the
   * element type. For example, <code>{1,2,3}</code> is an instance of set
   * type <code>{int}</code>; however, <code>{1.345}</code> is not.
   *
   * @author David J. Pearce
   *
   */
  public static final class Set extends Compound implements EffectiveSet {
    private Set(Automaton automaton) {
      super(automaton);
    }
    public Type element() {
      int elemIdx = automaton.states[0].children[0];
      return construct(Automata.extract(automaton,elemIdx));
    }
    public boolean nonEmpty() {
      return (Boolean) automaton.states[0].data;
    }
  }

  /**
   * A type which is either a list, or a union of lists. An effective list
   * gives access to an effective element type, which is the union of possible
   * element types.
   *
   * <pre>
   * [int] | [real]
   * </pre>
   *
   * Here, the effective element type is int|real.
   *
   * @return
   */
  public interface EffectiveList extends EffectiveIndexible {

    public Type element();

  }

  /**
   * A list type describes list values whose elements are subtypes of the
   * element type. For example, <code>[1,2,3]</code> is an instance of list
   * type <code>[int]</code>; however, <code>[1.345]</code> is not.
   *
   * @author David J. Pearce
   *
   */
  public static final class List extends Compound implements EffectiveList {
    private List(Automaton automaton) {
      super(automaton);
    }

    public Type key() {
      return Type.T_INT;
    }

    public Type value() {
      return element();
    }

    public Type element() {
      int elemIdx = automaton.states[0].children[0];
      return construct(Automata.extract(automaton,elemIdx));
    }

    public boolean nonEmpty() {
      return (Boolean) automaton.states[0].data;
    }

    public EffectiveIndexible update(Type key, Type value) {
      if(key == T_INT) {
        return Type.List(Type.Union(value, element()), nonEmpty());
      } else {
        return Type.Map(Type.Union(T_INT,key),Type.Union(value, element()));
      }
    }
  }

  /**
   * Represents a reference to an object in Whiley.
   *
   * @author David J. Pearce
   *
   */
  public static final class Reference extends Compound  {
    private Reference(Automaton automaton) {
      super(automaton);
    }
    public Type element() {
      int elemIdx = automaton.states[0].children[0];
      return construct(Automata.extract(automaton,elemIdx));
    }
  }

  /**
   * A type which is either a map, or a union of dictionaries. An
   * effective dictionary gives access to effective key and value types, which are
   * the union of possible key and value types (respectively).
   *
   * <pre>
   * [int] | [real]
   * </pre>
   *
   * Here, the effective element type is int|real.
   *
   * @return
   */
  public interface EffectiveMap extends EffectiveIndexible {

    public Type key();

    public Type value();

    public EffectiveMap update(Type key, Type Value);
  }

  /**
   * A map represents a one-many mapping from variables of one type to
   * variables of another type. For example, the map type
   * <code>int->real</code> represents a map from integers to real values. A
   * valid instance of this type might be <code>{1->1.2,2->3}</code>.
   *
   * @author David J. Pearce
   *
   */
  public static final class Map extends Compound implements EffectiveMap {
    private Map(Automaton automaton) {
      super(automaton);
    }
    public Type key() {
      int keyIdx = automaton.states[0].children[0];
      return construct(Automata.extract(automaton,keyIdx));
    }
    public Type value() {
      int valueIdx = automaton.states[0].children[1];
      return construct(Automata.extract(automaton,valueIdx));
    }
    public Type element() {
      return Type.Tuple(key(),value());
    }
    public Map update(Type key, Type value) {
      key = Type.Union(key,key());
      value = Type.Union(value,value());
      return Type.Map(key,value);
    }
  }

  /**
   * A type which is either a record, or a union of records. An effective
   * record gives access to a subset of the visible fields which are
   * guaranteed to be in the type. For example, consider this type:
   *
   * <pre>
   * {int op, int x} | {int op, [int] y}
   * </pre>
   *
   * Here, the field op is guaranteed to be present. Therefore, the effective
   * record type is just <code>{int op}</code>.
   *
   * @return
   */
  public interface EffectiveRecord {

    public Type field(String field);

    public HashMap<String,Type> fields();

    public EffectiveRecord update(String field, Type type);
  }

  /**
   * A record is made up of a number of fields, each of which has a unique
   * name. Each field has a corresponding type. One can think of a record as a
   * special kind of "fixed" map (i.e. where we know exactly which
   * entries we have).
   *
   * @author David J. Pearce
   *
   */
  public static final class Record extends Compound implements EffectiveRecord {
    private Record(Automaton automaton) {
      super(automaton);
    }

    public boolean isOpen() {
      State state = (State) automaton.states[0].data;
      return state.isOpen;
    }

    /**
     * Extract just the key set for this field. This is a cheaper operation
     * than extracting the keys and their types (since types must be
     * extracted).
     *
     * @return
     */
    public HashSet<String> keys() {
      State fields = (State) automaton.states[0].data;
      HashSet<String> r = new HashSet<String>();
      for(String f : fields) {
        r.add(f);
      }
      return r;
    }

    public Type field(String field) {
      State fields = (State) automaton.states[0].data;
      int index = Collections.binarySearch(fields, field);
      if (index < 0) {
        return null; // not found
      } else {
        int[] children = automaton.states[0].children;
        return construct(Automata.extract(automaton, children[index]));
      }
    }

    /**
     * Return a mapping from field names to their types.
     *
     * @return
     */
    public HashMap<String, Type> fields() {
      State fields = (State) automaton.states[0].data;
      int[] children = automaton.states[0].children;
      HashMap<String, Type> r = new HashMap<String, Type>();
      for (int i = 0; i != children.length; ++i) {
        r.put(fields.get(i),
            construct(Automata.extract(automaton, children[i])));
      }
      return r;
    }

    public Record update(String field, Type type) {
      HashMap<String,Type> fields = fields();
      fields.put(field, type);
      return Type.Record(isOpen(),fields);
    }

    public static final class State extends ArrayList<String> {
      public final boolean isOpen;

      public State(boolean isOpen) {
        this.isOpen = isOpen;
      }

      public State(boolean isOpen, Collection<String> values) {
        super(values);
        this.isOpen = isOpen;
      }

      public boolean equals(Object o) {
        if (o instanceof State) {
          State s = (State) o;
          return isOpen == s.isOpen && super.equals(s);
        }
        return false;
      }
    }

  }

  /**
   * A union type represents a type whose variables may hold values from any
   * of its "bounds". For example, the union type null|int indicates a
   * variable can either hold an integer value, or null. <b>NOTE:</b>There
   * must be at least two bounds for a union type to make sense.
   *
   * @author David J. Pearce
   *
   */
  public static class Union extends Compound {
    private Union(Automaton automaton) {
      super(automaton);
    }

    /**
     * Return the bounds of this union type.
     *
     * @return
     */
    public HashSet<Type> bounds() {
      HashSet<Type> r = new HashSet<Type>();
      int[] fields = (int[]) automaton.states[0].children;
      for(int i : fields) {
        Type b = construct(Automata.extract(automaton,i));
        r.add(b);
      }
      return r;
    }
  }

  public static final class UnionOfSets extends Union implements
  EffectiveSet {
    private UnionOfSets(Automaton automaton) {
      super(automaton);
    }

    public Type element() {
      Type r = null;
      HashSet<Type.Set> bounds = (HashSet) bounds();
      for(Type.Set bound : bounds) {
        Type t = bound.element();
        if(r == null || t == null) {
          r = t;
        } else {
          r = Type.Union(r,t);
        }
      }
      return r;
    }
  }

  public static final class UnionOfLists extends Union implements
      EffectiveList {
    private UnionOfLists(Automaton automaton) {
      super(automaton);
    }

    public Type key() {
      return T_INT;
    }

    public Type value() {
      return element();
    }

    public Type element() {
      Type r = null;
      HashSet<Type.List> bounds = (HashSet) bounds();
      for(Type.List bound : bounds) {
        Type t = bound.element();
        if(r == null || t == null) {
          r = t;
        } else {
          r = Type.Union(r,t);
        }
      }
      return r;
    }

    public EffectiveIndexible update(Type key, Type type) {
      HashSet<Type> nbounds = new HashSet<Type>();
      HashSet<Type.List> bounds = (HashSet) bounds();
      for(Type.List bound : bounds) {
        nbounds.add((Type) bound.update(key,type));
      }

      // we can only safely return an EffectiveList here since an update
      // can fold multiple lists into one.  For example:
      //
      // [int]|[real]
      //
      // assigning type any into this yields [any]

      return (EffectiveIndexible) Type.Union(nbounds);
    }
  }

  public static final class UnionOfIndexibles extends Union
  implements EffectiveIndexible {
    private UnionOfIndexibles(Automaton automaton) {
      super(automaton);
    }

    public Type key() {
      Type r = null;
      HashSet<EffectiveIndexible> bounds = (HashSet) bounds();
      for (EffectiveIndexible bound : bounds) {
        Type t = bound.key();
        if (r == null || t == null) {
          r = t;
        } else {
          r = Type.Union(r, t);
        }
      }
      return r;
    }

    public Type value() {
      Type r = null;
      HashSet<EffectiveIndexible> bounds = (HashSet) bounds();
      for (EffectiveIndexible bound : bounds) {
        Type t = bound.value();
        if (r == null || t == null) {
          r = t;
        } else {
          r = Type.Union(r, t);
        }
      }
      return r;
    }

    public Type element() {
      Type r = null;
      HashSet<EffectiveIndexible> bounds = (HashSet) bounds();
      for (EffectiveIndexible bound : bounds) {
        Type t = bound.element();
        if (r == null || t == null) {
          r = t;
        } else {
          r = Type.Union(r, t);
        }
      }
      return r;
    }

    public EffectiveIndexible update(Type key, Type type) {
      HashSet<Type> nbounds = new HashSet<Type>();
      HashSet<Type.List> bounds = (HashSet) bounds();
      for(Type.List bound : bounds) {
        nbounds.add((Type) bound.update(key,type));
      }

      // we can only safely return an EffectiveList here since an update
      // can fold multiple lists into one.  For example:
      //
      // [int]|[real]
      //
      // assigning type any into this yields [any]

      return (EffectiveIndexible) Type.Union(nbounds);
    }
  }

  public static final class UnionOfCollections extends Union
      implements
        EffectiveCollection {
    private UnionOfCollections(Automaton automaton) {
      super(automaton);
    }

    public Type element() {
      Type r = null;
      HashSet<EffectiveCollection> bounds = (HashSet) bounds();
      for (EffectiveCollection bound : bounds) {
        Type t = bound.element();
        if (r == null || t == null) {
          r = t;
        } else {
          r = Type.Union(r, t);
        }
      }
      return r;
    }
  }

  public static final class UnionOfMaps extends Union implements
  EffectiveMap {
    private UnionOfMaps(Automaton automaton) {
      super(automaton);
    }

    public Type key() {
      Type r = null;
      HashSet<Type.Map> bounds = (HashSet) bounds();
      for(Type.Map bound : bounds) {
        Type t = bound.key();
        if(r == null || t == null) {
          r = t;
        } else {
          r = Type.Union(r,t);
        }
      }
      return r;
    }

    public Type value() {
      Type r = null;
      HashSet<Type.Map> bounds = (HashSet) bounds();
      for(Type.Map bound : bounds) {
        Type t = bound.value();
        if(r == null || t == null) {
          r = t;
        } else {
          r = Type.Union(r,t);
        }
      }
      return r;
    }

    public Type element() {
      return Type.Tuple(key(),value());
    }

    public EffectiveMap update(Type key, Type value) {
      HashSet<Type> nbounds = new HashSet<Type>();
      HashSet<Type.Map> bounds = (HashSet) bounds();
      for(Type.Map bound : bounds) {
        nbounds.add(bound.update(key,value));
      }

      // we can only safely return an EffectiveMap here since an
      // update can fold multiple dictionaries into one. For example:
      //
      // {int=>string}|{int=>real}
      //
      // assigning int=>any into this yields {int=>any}

      return (EffectiveMap) Type.Union(nbounds);
    }
  }

  public static final class UnionOfTuples extends Union implements
  EffectiveTuple {
    private UnionOfTuples(Automaton automaton) {
      super(automaton);
    }

    public Type element(int index) {
      Type r = null;
      HashSet<Type.Tuple> bounds = (HashSet) bounds();
      for(Type.Tuple bound : bounds) {
        Type t = bound.element(index);
        if(r == null || t == null) {
          r = t;
        } else {
          r = Type.Union(r,t);
        }
      }
      return r;
    }

    public int size() {
      HashSet<Type.Tuple> bounds = (HashSet) bounds();
      int max = Integer.MAX_VALUE;
      // first, determine maximum number of elements in effective tuple.
      for(Type.Tuple bound : bounds) {
        max = Math.min(max,bound.size());
      }
      return max;
    }

    public ArrayList<Type> elements() {
      HashSet<Type.Tuple> bounds = (HashSet) bounds();
      int max = Integer.MAX_VALUE;
      // first, determine maximum number of elements in effective tuple.
      for(Type.Tuple bound : bounds) {
        max = Math.min(max,bound.size());
      }

      // now, create list of elements
      ArrayList<Type> elements = new ArrayList<Type>();
      for(int i=0;i!=max;++i) {
        Type element = Type.T_VOID;
        for(Type.Tuple bound : bounds) {
          element = Type.Union(bound.element(i),element);
        }
        elements.add(element);
      }
      return elements;
    }
  }

  public static final class UnionOfRecords extends Union implements
      EffectiveRecord {
    private UnionOfRecords(Automaton automaton) {
      super(automaton);
    }

    public Type field(String field) {
      Type r = null;
      HashSet<Type.Record> bounds = (HashSet) bounds();
      for(Type.Record bound : bounds) {
        Type t = bound.field(field);
        if(r == null || t == null) {
          r = t;
        } else {
          r = Type.Union(r,t);
        }
      }
      return r;
    }

    public HashMap<String,Type> fields() {
      // TODO: this method could be optimised to avoid creating so many
      // HashMaps.
      HashMap<String,Type> fields = null;
      HashSet<Type.Record> bounds = (HashSet) bounds();
      for(Type.Record bound : bounds) {
        fields = join(fields,bound.fields());
      }
      return fields;
    }

    private static HashMap<String, Type> join(HashMap<String, Type> m1,
        HashMap<String, Type> m2) {
      if (m1 == null) {
        return m2;
      }
      HashMap<String, Type> m3 = new HashMap<String, Type>();
      for (java.util.Map.Entry<String, Type> e : m1.entrySet()) {
        String field = e.getKey();
        Type t1 = e.getValue();
        Type t2 = m2.get(field);
        if (t2 != null) {
          m3.put(field, Type.Union(t1, t2));
        }
      }
      return m3;
    }

    public EffectiveRecord update(String field, Type type) {
      HashSet<Type> nbounds = new HashSet<Type>();
      HashSet<Type.Record> bounds = (HashSet) bounds();
      for(Type.Record bound : bounds) {
        nbounds.add(bound.update(field, type));
      }

      // we can only safely return an EffectiveRecord here since an update
      // can fold multiple records into one.  For example:
      //
      // {int x,string y}|{int x,real y}
      //
      // updating y to type int gives {int x,int y}

      return (EffectiveRecord) Type.Union(nbounds);
    }
  }

  /**
   * A difference type represents a type which accepts values in the
   * difference between its bounds.
   *
   * @author David J. Pearce
   *
   */
  public static final class Negation extends Compound {
    private Negation(Automaton automaton) {
      super(automaton);
    }

    public Type element() {
      int[] fields = automaton.states[0].children;
      return construct(Automata.extract(automaton,fields[0]));
    }
  }

  public abstract static class FunctionOrMethod extends Compound {
    FunctionOrMethod(Automaton automaton) {
      super(automaton);
    }

    /**
     * Get the return type of this function or method type.
     *
     * @return
     */
    public Type ret() {
      int[] fields = automaton.states[0].children;
      return construct(Automata.extract(automaton, fields[0]));
    }

    /**
     * Get the throws clause of this function or method type.
     *
     * @return
     */
    public Type throwsClause() {
      int[] fields = automaton.states[0].children;
      return construct(Automata.extract(automaton, fields[1]));
    }

    /**
     * Get the parameter types of this function or method type.
     *
     * @return
     */
    public ArrayList<Type> params() {
      int[] fields = automaton.states[0].children;
      ArrayList<Type> r = new ArrayList<Type>();
      for(int i=2;i<fields.length;++i) {
        r.add(construct(Automata.extract(automaton, fields[i])));
      }
      return r;
    }
  }

  /**
   * A function type, consisting of a list of zero or more parameters and a
   * return type.
   *
   * @author David J. Pearce
   *
   */
  public static class Function extends FunctionOrMethod  {
    Function(Automaton automaton) {
      super(automaton);
    }
  }

  public static final class Method extends FunctionOrMethod {
    Method(Automaton automaton) {
      super(automaton);
    }
  }

  /**
   * The following method constructs a string representation of the underlying
   * automaton. This representation may be an expanded version of the underling
   * graph, since one cannot easily represent aliasing in the type graph in a
   * textual manner.
   *
   * @param automaton
   *            --- the automaton being turned into a string.
   * @return --- string representation of automaton.
   */
  public final static String toString(Automaton automaton) {
    // First, we need to find the headers of the computation. This is
    // necessary in order to mark the start of a recursive type.
    BitSet headers = new BitSet(automaton.size());
    BitSet visited = new BitSet(automaton.size());
    BitSet onStack = new BitSet(automaton.size());
    findHeaders(0, visited, onStack, headers, automaton);
    visited.clear();
    String[] titles = new String[automaton.size()];
    int count = 0;
    for (int i = 0; i != automaton.size(); ++i) {
      if (headers.get(i)) {
        titles[i] = headerTitle(count++);
      }
    }
    return Type.toString(0, visited, titles, automaton);
  }

  /**
   * The following method constructs a string representation of the underlying
   * automaton. This representation may be an expanded version of the underling
   * graph, since one cannot easily represent aliasing in the type graph in a
   * textual manner.
   *
   * @param index
   *            --- the index to start from
   * @param visited
   *            --- the set of vertices already visited.
   * @param headers
   *            --- an array of strings which identify the name to be given to
   *            each header.
   * @param automaton
   *            --- the automaton being turned into a string.
   * @return --- string representation of automaton.
   */
  private final static String toString(int index, BitSet visited,
      String[] headers, Automaton automaton) {
    if (visited.get(index)) {
      // node already visited
      return headers[index];
    } else if(headers[index] != null) {
      visited.set(index);
    }
    State state = automaton.states[index];
    String middle;
    switch (state.kind) {
    case K_VOID:
      return "void";
    case K_ANY:
      return "any";
    case K_NULL:
      return "null";
    case K_BOOL:
      return "bool";
    case K_BYTE:
      return "byte";
    case K_CHAR:
      return "char";
    case K_INT:
      return "int";
    case K_RATIONAL:
      return "real";
    case K_STRING:
      return "string";
    case K_SET: {
      boolean nonEmpty = (Boolean) state.data;
      if (nonEmpty) {
        middle = "{"
            + toString(state.children[0], visited, headers,
                automaton) + "+}";
      } else {
        middle = "{"
            + toString(state.children[0], visited, headers,
                automaton) + "}";
      }
      break;
    }
    case K_LIST: {
      boolean nonEmpty = (Boolean) state.data;
      if(nonEmpty) {
        middle = "[" + toString(state.children[0], visited, headers, automaton)
            + "+]";
      } else {
        middle = "[" + toString(state.children[0], visited, headers, automaton)
            + "]";
      }
      break;
    }
    case K_NOMINAL:
      middle = state.data.toString();
      break;
    case K_REFERENCE:
      middle = "&" + toString(state.children[0], visited, headers, automaton);
      break;
    case K_NEGATION: {
      middle = "!" + toBracesString(state.children[0], visited, headers, automaton);
      break;
    }
    case K_MAP: {
      // binary node
      String k = toString(state.children[0], visited, headers, automaton);
      String v = toString(state.children[1], visited, headers, automaton);
      middle = "{" + k + "->" + v + "}";
      break;
    }
    case K_UNION: {
      int[] children = state.children;
      middle = "";
      for (int i = 0; i != children.length; ++i) {
        if(i != 0 || children.length == 1) {
          middle += "|";
        }
        middle += toBracesString(children[i], visited, headers, automaton);
      }
      break;
    }
    case K_TUPLE: {
      middle = "";
      int[] children = state.children;
      for (int i = 0; i != children.length; ++i) {
        if (i != 0) {
          middle += ",";
        }
        middle += toString(children[i], visited, headers, automaton);
      }
      middle = "(" + middle + ")";
      break;
    }
    case K_RECORD: {
      // labeled nary node
      middle = "{";
      int[] children = state.children;
      Record.State fields = (Record.State) state.data;
      for (int i = 0; i != fields.size(); ++i) {
        if (i != 0) {
          middle += ",";
        }
        middle += toString(children[i], visited, headers, automaton) + " " + fields.get(i);
      }
      if(fields.isOpen) {
        if(children.length > 0) {
          middle = middle + ",...}";
        } else {
          middle = middle + "...}";
        }
      } else {
        middle = middle + "}";
      }
      break;
    }
    case K_METHOD:
    case K_FUNCTION: {
      middle = "";
      int[] children = state.children;
      int start = 0;
      String ret = toString(children[start], visited, headers, automaton);
      String thros = toString(children[start+1], visited, headers, automaton);
      boolean firstTime=true;
      for (int i = start+2; i != children.length; ++i) {
        if (!firstTime) {
          middle += ",";
        }
        firstTime=false;
        middle += toString(children[i], visited, headers, automaton);
      }
      if(state.kind == K_FUNCTION) {
        middle = "function(" + middle + ") => " + ret;
      } else {
        middle = "method(" + middle + ") => " + ret;
      }
      if(!thros.equals("void")) {
        middle = middle + " throws " + thros;
      }
      break;
    }
    default:
      throw new IllegalArgumentException("Invalid type encountered (kind: " + state.kind +")");
    }

    // Finally, check whether this is a header node, or not. If it is a
    // header then we need to insert the recursive type.
    String header = headers[index];
    if(header != null) {
      // The following case is interesting. Basically, we'll never revisit
      // a header. Therefore, if we have multiple edges landing on a
      // header we must update the header string to represent the full
      // type reachable from the header.
      String r = header + "<" + middle + ">";
      headers[index] = r;
      return r;
    } else {
      return middle;
    }
  }

  private final static String toBracesString(int index, BitSet visited,
      String[] headers, Automaton automaton) {
    if (visited.get(index)) {
      // node already visited
      return headers[index];
    }
    String middle = toString(index,visited,headers,automaton);
    State state = automaton.states[index];
    switch(state.kind) {
      case K_UNION:
      case K_FUNCTION:
      case K_METHOD:
        return "(" + middle + ")";
      default:
        return middle;
    }
  }

  /**
   * The following method traverses the graph using a depth-first
   * search to identify nodes which are "loop headers". That is, they are the
   * target of one or more recursive edges in the graph.
   *
   * @param index
   *            --- the index to search from.
   * @param visited
   *            --- the set of vertices already visited.
   * @param onStack
   *            --- the set of nodes currently on the DFS path from the root.
   * @param headers
   *            --- header nodes discovered during this search are set to true
   *            in this bitset.
   * @param automaton
   *            --- the automaton we're traversing.
   */
  private final static void findHeaders(int index, BitSet visited,
      BitSet onStack, BitSet headers, Automaton automaton) {
    if(visited.get(index)) {
      // node already visited
      if(onStack.get(index)) {
        headers.set(index);
      }
      return;
    }
    onStack.set(index);
    visited.set(index);
    State state = automaton.states[index];
    for(int child : state.children) {
      findHeaders(child,visited,onStack,headers,automaton);
    }
    onStack.set(index,false);
  }

  private static final char[] headers = { 'X','Y','Z','U','V','W','L','M','N','O','P','Q','R','S','T'};
  private static String headerTitle(int count) {
    String r = Character.toString(headers[count%headers.length]);
    int n = count / headers.length;
    if(n > 0) {
      return r + n;
    } else {
      return r;
    }
  }

  /**
   * Determine the node kind of a Type.Leafs
   * @param leaf
   * @return
   */
  public static final byte leafKind(Type.Leaf leaf) {
    if(leaf instanceof Type.Void) {
      return K_VOID;
    } else if(leaf instanceof Type.Any) {
      return K_ANY;
    } else if(leaf instanceof Type.Null) {
      return K_NULL;
    } else if(leaf instanceof Type.Bool) {
      return K_BOOL;
    } else if(leaf instanceof Type.Byte) {
      return K_BYTE;
    } else if(leaf instanceof Type.Char) {
      return K_CHAR;
    } else if(leaf instanceof Type.Int) {
      return K_INT;
    } else if(leaf instanceof Type.Real) {
      return K_RATIONAL;
    } else if(leaf instanceof Type.Strung) {
      return K_STRING;
    } else if(leaf instanceof Type.Meta) {
      return K_META;
    } else if(leaf instanceof Type.Nominal) {
      return K_NOMINAL;
    } else {
      // should be dead code
      throw new IllegalArgumentException("Invalid leaf node: " + leaf);
    }
  }

  /**
   * Determine the node data of a Type.Leaf.
   * @param leaf
   * @return
   */
  public static final Object leafData(Type.Leaf leaf) {
    if(leaf instanceof Type.Nominal) {
      return ((Type.Nominal)leaf).nid;
    } else {
      return null;
    }
  }

  /**
   * The construct methods constructs a Type from an automaton.
   *
   * @param nodes
   * @return
   */
  public final static Type construct(Automaton automaton) {
    automaton = normalise(automaton);
    // second, construc the appropriate face
    State root = automaton.states[0];
    Type type;

    switch(root.kind) {
    case K_VOID:
      type = T_VOID;
      break;
    case K_ANY:
      type = T_ANY;
      break;
    case K_META:
      type = T_META;
      break;
    case K_NULL:
      type = T_NULL;
      break;
    case K_BOOL:
      type = T_BOOL;
      break;
    case K_BYTE:
      type = T_BYTE;
      break;
    case K_CHAR:
      type = T_CHAR;
      break;
    case K_INT:
      type = T_INT;
      break;
    case K_RATIONAL:
      type = T_REAL;
      break;
    case K_STRING:
      type = T_STRING;
      break;
    case K_NOMINAL:
      type = new Nominal((NameID) root.data);
      break;
    case K_TUPLE:
      type = new Tuple(automaton);
      break;
    case K_SET:
      type = new Set(automaton);
      break;
    case K_LIST:
      type = new List(automaton);
      break;
    case K_REFERENCE:
      type = new Reference(automaton);
      break;
    case K_MAP:
      type = new Map(automaton);
      break;
    case K_RECORD:
      type = new Record(automaton);
      break;
    case K_UNION: {
      boolean allRecords = true;
      boolean allLists = true;
      boolean allDictionaries = true;
      boolean allSets = true;
      boolean allMaps = true;
      boolean allCollections = true;
      boolean allTuples = true;
      Type.Union union = new Union(automaton);
      for(Type bound : union.bounds()) {
        boolean isSet = bound instanceof Set;
        boolean isList = bound instanceof List;
        boolean isString = bound instanceof Strung;
        boolean isMap = bound instanceof Map;
        allRecords &= bound instanceof Record;
        allSets &= isSet;
        allLists &= isList;
        allDictionaries &= isMap;
        allMaps &= isList || isMap || isString;
        allCollections &= isSet || isList || isMap || isString;
        allTuples &= bound instanceof Tuple;
      }
      if(allSets) {
        type = new UnionOfSets(automaton);
      } else if(allDictionaries) {
        type = new UnionOfMaps(automaton);
      } else if(allLists) {
        type = new UnionOfLists(automaton);
      } else if(allMaps) {
        type = new UnionOfIndexibles(automaton);
      } else if(allCollections) {
        type = new UnionOfCollections(automaton);
      } else if(allTuples) {
        type = new UnionOfTuples(automaton);
      } else if(allRecords) {
        type = new UnionOfRecords(automaton);
      } else {
        type = union;
      }
      break;
    }
    case K_NEGATION:
      type = new Negation(automaton);
      break;
    case K_METHOD:
      type = new Method(automaton);
      break;
    case K_FUNCTION:
      type = new Function(automaton);
      break;
    default:
      throw new IllegalArgumentException("invalid node kind: " + root.kind);
    }

    //distinctTypes.add(type);

    return type;
  }

  /**
   * This method constructs a Node array from an array of types which will
   * form children.
   *
   * @param kind
   * @param elements
   * @return
   */
  private static Type construct(byte kind, Object data, Type... children) {
    int[] nchildren = new int[children.length];
    boolean deterministic = kind != K_UNION;
    Automaton automaton = new Automaton(new State(kind, data, deterministic, nchildren));
    int start = 1;
    int i=0;
    for(Type element : children) {
      nchildren[i] = start;
      Automaton child = destruct(element);
      automaton = Automata.append(automaton,child);
      start += child.size();
      i = i + 1;
    }
    return construct(automaton);
  }

  /**
   * This method constructs a State array from a collection of types which will
   * form children.
   *
   * @param kind
   * @param children
   * @return
   */
  private static Type construct(byte kind, Object data, Collection<Type> children) {
    int[] nchildren = new int[children.size()];
    boolean deterministic = kind != K_UNION;
    Automaton automaton = new Automaton(new State(kind, data, deterministic, nchildren));
    int start = 1;
    int i=0;
    for(Type element : children) {
      nchildren[i] = start;
      Automaton child = destruct(element);
      automaton = Automata.append(automaton,child);
      start += child.size();
      i = i + 1;
    }

    return construct(automaton);
  }

  /**
   * Destruct is the opposite of construct. It converts a type into an
   * automaton.
   *
   * @param t --- type to be converted.
   * @return
   */
  public static final Automaton destruct(Type t) {
    if (t instanceof Leaf) {
      int kind = leafKind((Leaf) t);
      Object data = null;
      if (t instanceof Nominal) {
        Nominal x = (Nominal) t;
        data = x.nid;
      }
      State state = new State(kind, data, true, Automaton.NOCHILDREN);
      return new Automaton(new State[] { state });
    } else {
      // compound type
      return ((Compound) t).automaton;
    }
  }

  /**
   * <p>
   * The following algorithm simplifies a type. For example:
   * </p>
   *
   * <pre>
   * define InnerList as null|{int data, OuterList next}
   * define OuterList as null|{int data, InnerList next}
   * </pre>
   * <p>
   * This type is simplified into the following (equivalent) form:
   * </p>
   *
   * <pre>
   * define LinkedList as null|{int data, LinkedList next}
   * </pre>
   * <p>
   * The simplification algorithm is made up of several different procedures
   * which operate on the underlying <i>automaton</i> representing the type:
   * </p>
   * <ol>
   * <li><b>Extraction.</b> Here, sub-components unreachable from the root are
   * eliminated.</li>
   * <li><b>Simplification.</b> Here, basic simplifications are applied. For
   * example, eliminating unions of unions.</li>
   * <li><b>Minimisation.</b>Here, equivalent states are merged together.</li>
   * <li><b>Canonicalisation.</b> A canonical form of the type is computed</li>
   * </ol>
   *
   * is based on the well-known algorithm for minimising a DFA (see e.g. <a
   * href="http://en.wikipedia.org/wiki/DFA_minimization">[1]</a>). </p>
   * <p>
   * The algorithm operates by performing a subtype test of each node against
   * all others. From this, we can identify nodes which are equivalent under
   * the subtype operator. Using this information, the type is reconstructed
   * such that for each equivalence class only a single node is created.
   * </p>
   * <p>
   * <b>NOTE:</b> this algorithm does not put the type into a canonical form.
   * Additional work is necessary to do this.
   * </p>
   *
   * @param afterType
   * @return
   */
  private static Automaton normalise(Automaton automaton) {
    //normalisedCount++;
    //unminimisedCount += automaton.size();
    TypeAlgorithms.simplify(automaton);
    // TODO: extract in place to avoid allocating data unless necessary
    automaton = Automata.extract(automaton, 0);
    // TODO: minimise in place to avoid allocating data unless necessary
    automaton = Automata.minimise(automaton);
    if(canonicalisation) {
      Automata.canonicalise(automaton, TypeAlgorithms.DATA_COMPARATOR);
    }
    //minimisedCount += automaton.size();
    return automaton;
  }

  public static final byte K_VOID = 0;
  public static final byte K_ANY = 1;
  public static final byte K_META = 2;
  public static final byte K_NULL = 3;
  public static final byte K_BOOL = 4;
  public static final byte K_BYTE = 5;
  public static final byte K_CHAR = 6;
  public static final byte K_INT = 7;
  public static final byte K_RATIONAL = 8;
  public static final byte K_STRING = 9;
  public static final byte K_TUPLE = 10;
  public static final byte K_SET = 11;
  public static final byte K_LIST = 12;
  public static final byte K_MAP = 13;
  public static final byte K_REFERENCE = 14;
  public static final byte K_RECORD = 15;
  public static final byte K_UNION = 16;
  public static final byte K_NEGATION = 17;
  public static final byte K_FUNCTION = 18;
  public static final byte K_METHOD = 20;
  public static final byte K_NOMINAL = 21;

  private static final ArrayList<Automaton> values = new ArrayList<Automaton>();
  private static final HashMap<Automaton,Integer> cache = new HashMap<Automaton,Integer>();

  /**
   * The following method is for implementing the fly-weight pattern.
   */
  private static <T extends Automaton> T get(T type) {
    Integer idx = cache.get(type);
    if(idx != null) {
      return (T) values.get(idx);
    } else {
      cache.put(type, values.size());
      values.add(type);
      return type;
    }
  }

  public static void main(String[] args) {
    //Type from = fromString("(null,null)");
    //Type to = fromString("X<[X]>");
    Type from = fromString("!(!{int x,int z} | !{int x,int y})");
    Type to = fromString("{string name,...}");
    System.out.println(from + " :> " + to + " = " + isSubtype(from, to));
    System.out.println(from + " & " + to + " = " + intersect(from,to));
    //System.out.println(from + " - " + to + " = " + intersect(from,Type.Negation(to)));
    //System.out.println(to + " - " + from + " = " + intersect(to,Type.Negation(from)));
    //System.out.println("!" + from + " & !" + to + " = "
    //    + intersect(Type.Negation(from), Type.Negation(to)));
  }

  public static Type linkedList(int n) {
    NameID label = new NameID(Trie.fromString(""),"X");
    return Recursive(label,innerLinkedList(n));
  }

  public static Type innerLinkedList(int n) {
    if(n == 0) {
      return Nominal(new NameID(Trie.fromString(""),"X"));
    } else {
      Type leaf = Reference(innerLinkedList(n-1));
      HashMap<String,Type> fields = new HashMap<String,Type>();
      fields.put("next", Union(T_NULL,leaf));
      fields.put("data", T_BOOL);
      return Record(false,fields);
    }
  }
}
TOP

Related Classes of wyil.lang.Type

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.