Package org.openquark.cal.compiler

Source Code of org.openquark.cal.compiler.TypeClass

/*
* Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
*     * Redistributions of source code must retain the above copyright notice,
*       this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in the
*       documentation and/or other materials provided with the distribution.
*     * Neither the name of Business Objects nor the names of its contributors
*       may be used to endorse or promote products derived from this software
*       without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/


/*
* TypeClass.java
* Created: March 22, 2001
* By: Bo Ilic
*/
package org.openquark.cal.compiler;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import org.openquark.cal.internal.serialization.ModuleSerializationTags;
import org.openquark.cal.internal.serialization.RecordInputStream;
import org.openquark.cal.internal.serialization.RecordOutputStream;
import org.openquark.cal.internal.serialization.RecordInputStream.RecordHeaderInfo;



/**
* Models a type class in CAL.
* <p>
* Some examples of type classes in CAL are:
* Prelude.Eq, Prelude.Ord, Prelude.Num, Prelude.Enum, Prelude.Inputable,
* Prelude.Outputable, Prelude.Appendable and Debug.Show.
*
* @author Bo Ilic
*/
public final class TypeClass extends ScopedEntity {

    private static final int serializationSchema = 0;
   
    /**
     * The immediate superclasses of this type class. The ordering is significant in determining
     * the definitions of the dictionary switching functions and of the dictionary functions.
     */
    private final List<TypeClass> parentTypeClassList;

    /**
     * The list of class methods that are supported by this class. Note that methods of superclasses
     * are not listed here, even though they are implicitly available. The ordering is significant
     * in determining the definitions of the dictionary functions.
     */
    private final List<ClassMethod> classMethodsList;
   
    /**
     * The kind of the type class. One way to think of this is as the kind of the type class variable.
     * For example, the kind of the Functor class is * -> * and the kind of the Eq class is *.
     * There will be no kind variables in kindExpr.
     */
    private KindExpr kindExpr;
   
    /**
     * The name of the type class type variable. Can be used to give better error messages. For example, for
     * class Functor f where ..., this will be "f".
     */
    private String typeClassTypeVarName;
   
    /**
     * This comparator is used when working with sorted sets of type classes, where the sort
     * order is determined by the qualified name of the type class.
     * For example, in the case of the type class constraints on a TypeVar, we need a well-defined
     * order for adding dictionary arguments to the definition of a function whose type depends on
     * constrained type variables.       
     */
    static private final Comparator<TypeClass> COMPARATOR = new Comparator<TypeClass>() {
        /** {@inheritDoc}*/
        public int compare(TypeClass object1, TypeClass object2) {
            return object1.getName().getQualifiedName().compareTo(object2.getName().getQualifiedName());
        }
    };
   
    /** an unmodifiable empty set of type classes, sorted by qualified name. */
    static final SortedSet<TypeClass> NO_CLASS_CONSTRAINTS = Collections.unmodifiableSortedSet(TypeClass.makeNewClassConstraintSet());   
   
       
    /**
     * TypeClass constructor.
     *
     * @param name the name of the type class
     * @param scope the scope of the type class
     * @param typeClassTypeVarName name of the type class type variable parameter. For example, for "class Functor f where..." this is "f".
     */
    TypeClass(QualifiedName name, Scope scope, KindExpr kindExpr, String typeClassTypeVarName) {

        super (name, scope, null);
       
        if (kindExpr == null || typeClassTypeVarName == null) {
            throw new NullPointerException();
        }
       
        this.kindExpr = kindExpr;
        this.typeClassTypeVarName = typeClassTypeVarName;
      
        parentTypeClassList = new ArrayList<TypeClass>();
        classMethodsList = new ArrayList<ClassMethod>();       
    }   
   
    /** Zero argument constructor for serialization. */
    TypeClass() {
        parentTypeClassList = new ArrayList<TypeClass>();
        classMethodsList = new ArrayList<ClassMethod>();       
    }
   
    /**   
     * @return The kind of the type class. One way to think of this is as the kind of the type class variable.
     * For example, the kind of the Functor class is * -> * and the kind of the Eq class is *.
     * There will be no kind variables in kindExpr.
     */
    KindExpr getKindExpr() {
        return kindExpr;
    }
   
    /**
     * Call this method when kind checking for this TypeConstructor is complete and
     * prior to kind checking later dependency groups.
     * The effect is to ground the remaining kind variables by * after kind inference
     * has determined the most general kind of this TypeConstructor. This is because CAL does
     * not support polymorphic kinds.
     */
    void finishedKindChecking() {
        kindExpr = kindExpr.bindKindVariablesToConstant();
    }   
   
    /**
     * Add the class methods in the order that they are declared within the type class. Do not add duplicates!
     * @param classMethod
     */
    void addClassMethod(ClassMethod classMethod) {
       
        if (classMethod == null) {
            throw new NullPointerException("The argument 'classMethod' cannot be null.");
        }

        classMethodsList.add(classMethod);
    }
   
    /**
     * Add the parent classes in the order that they are mentioned in the context of this type class declaration.
     * Do not add duplicates. However, redundant constraints are OK- these will be handled later e.g. (Eq a, Ord a) => Num a,
     * the constraint Eq a is redundant.
     * @param parentTypeClass
     */
    void addParentClass(TypeClass parentTypeClass) {
       
        if (parentTypeClass == null) {
            throw new NullPointerException("The argument 'parentTypeClass' cannot be null.");
        }

        parentTypeClassList.add(parentTypeClass);
    }
   
    /**
     * Returns the ordered list of all ancestors of this TypeClass. The list does not include
     * the class itself. It is ordered in the ordering required for writing dictionary functions.
     * Creation date: (4/4/01 3:11:56 PM)
     * @return List
     */
    List<TypeClass> calculateAncestorClassList() {

        List<TypeClass> ancestorClassList = new ArrayList<TypeClass>();
        calculateAncestorClassList(ancestorClassList);
        return ancestorClassList;
    }
   
    /**
     * Helper function for the public calculateAncestorClassList. The reason for this style of
     * implementation is to limit reallocations of List objects.
     *
     * Creation date: (4/6/01 8:59:36 AM)
     * @param ancestorClassList list of TypeClass objects
     */
    private void calculateAncestorClassList(List<TypeClass> ancestorClassList) {

        for (int i = 0, nParents = getNParentClasses(); i < nParents; ++i) {

            TypeClass parentTypeClass = getNthParentClass(i);
            parentTypeClass.calculateAncestorClassList(ancestorClassList);
            ancestorClassList.add(parentTypeClass);
        }
    }
   
    /**
     * Provides a deterministic ordering to the ancestors of a type class.   
     * @param ancestorTypeClass
     * @return int the index of the ancestorTypeClass in the list returned by calculateAncestorClassList, or -1 if this is not an ancestor class.
     */
    int getAncestorClassIndex(TypeClass ancestorTypeClass) {
       
        //todoBI this could use a more efficient implementation. However, type class inheritance hierarchies are typically quite small,
        //so we won't bother for now!
                      
        List<TypeClass> ancestorClassList = calculateAncestorClassList();                       
        for (int i = 0, nAncestors = ancestorClassList.size(); i < nAncestors; ++i) {
            TypeClass ancestor = ancestorClassList.get(i);
            if (ancestorTypeClass == ancestor) {
                return i;
            }
        }
       
        return -1;
    }
            
    /**       
     * @param methodN zero-based index.
     * @return ClassMethod the ClassMethod for the given index.
     */
    public ClassMethod getNthClassMethod(int methodN) {
        return classMethodsList.get(methodN);
    }
   
    /**
     * Method getClassMethod.
     * @param classMethodName the unqualified name of the classMethod, assumed to belong to the same module as this type class.
     * @return ClassMethod the ClassMethod or null if there is not one with the given classMethodName.
     */
    public ClassMethod getClassMethod(String classMethodName) {
               
        for (int i = 0, nClassMethods = getNClassMethods(); i < nClassMethods; ++i) {

            ClassMethod classMethod = getNthClassMethod(i);
            if (classMethod.getName().getUnqualifiedName().equals(classMethodName)) {
                return classMethod;
            }
        }

        return null;
    }
   
    /** 
     * Finds the index of the class method in the list of class methods for this type class.
     * The class method can then be retreived with getNthClassMethod.  
     * @param classMethodName name of the class method, assumed to belong to the same module as the type class itself.
     * @return the index of the class method, or -1 if not a class method for this type class.
     */
    int getClassMethodIndex(String classMethodName) {
             
        for (int i = 0, nClassMethods = getNClassMethods(); i < nClassMethods; ++i) {

            ClassMethod classMethod = getNthClassMethod(i);
            if (classMethod.getName().getUnqualifiedName().equals(classMethodName)) {
                return i;
            }
        }

        return -1;
    }    
   
    /**   
     * @return int the number of class methods defined in this TypeClass (does not include superclass methods)
     */
    public int getNClassMethods() {
        return classMethodsList.size();
    }
   
    /**        
     * @return int the number of direct parent classes of this class.
     */
    public int getNParentClasses() {
        return parentTypeClassList.size();
    }
   
    /**        
     * @param n
     * @return TypeClass the nth direct parent class of this class.
     */
    public TypeClass getNthParentClass(int n) {
        return parentTypeClassList.get(n);
    }   
   
    /**
     * Returns true if this TypeClass is the same or a subclass of anotherTypeClass.
     *
     * Creation date: (3/28/01 1:43:23 PM)
     * @return boolean
     * @param anotherTypeClass
     */
    boolean isSpecializationOf(TypeClass anotherTypeClass) {

        if (this == anotherTypeClass) {
            return true;
        }

        for (int i = 0, nParents = getNParentClasses(); i < nParents; ++i) {

            if (getNthParentClass(i).isSpecializationOf(anotherTypeClass)) {
                return true;
            }
        }

        return false;
    }
   
    /**
     * For internal compiler use only.
     *  
     * @return true if the class has no superclasses, and only 1 class method. For example,
     *    Prelude.Outputable has only the single class method output.
     */
    public boolean internal_isSingleMethodRootClass() {
        return getNClassMethods() == 1 && getNParentClasses() == 0;
    }
   
    /**    
     * @return The name of the type class type variable. Can be used to give better error messages. For example, for
     * class Functor f where ..., this will be "f".
     */
    String getTypeClassTypeVarName() {
        return typeClassTypeVarName;
    }
   
    /**
     * Returns an approximation to actual CAL source for a class declaration. Currently, it is
     * only needed for debugging purposes.
     *
     * @return String
     */
    @Override
    public String toString() {

        StringBuilder result = new StringBuilder(getScope().toString() + " class (");

        int nParents = parentTypeClassList.size();
        if (nParents > 0) {
           
            for (int i = 0; i < nParents; ++i) {
               
                if (i > 0) {                   
                    result.append(", ");
                }               
                result.append(parentTypeClassList.get(i).getName()).append(' ').append(typeClassTypeVarName);
            }
        }

        result.append(") => ").append(getName());
        result.append(' ').append(typeClassTypeVarName).append(" :: ").append(kindExpr).append(" where\n");

        for (int i = 0, nMethods = getNClassMethods(); i < nMethods; ++i) {
            result.append("    ").append(getNthClassMethod(i)).append('\n');
        }
        result.append('\n');

        return result.toString();
    }
   
    /**    
     * @return creates a new empty SortedSet for holding type classes sorted by qualified name.
     *   This set can then be modified by the caller.
     */
    static SortedSet<TypeClass> makeNewClassConstraintSet() {
        return new TreeSet<TypeClass>(TypeClass.COMPARATOR);
    }
   
    /**
     * Removes all those type classes that have child classes in the set from the
     * set. For example, if typeClassConstraintSet = {Eq, Ord, Num, Outputable} then
     * after the call, it would be {Num, Outputable}.    
     * @param typeClassConstraintSet (TypeClass SortedSet)     
     */
    static void removeSuperclassConstraints(SortedSet<TypeClass> typeClassConstraintSet) {
       
        //can't have redundant constraints if there are only 0 or 1 classes in the
        //constraint set!
        if (typeClassConstraintSet.size() <= 1) {
            return;
        }
       
        Set<TypeClass> superclassSet = new HashSet<TypeClass>();
       
        for (final TypeClass typeClass : typeClassConstraintSet) {
                       
            superclassSet.addAll(typeClass.calculateAncestorClassList());
        }
       
        typeClassConstraintSet.removeAll(superclassSet);
    }   
   
    /**
     * Write this TypeClass instance to the RecordOutputStream.
     * @param s
     * @throws IOException
     */
    @Override
    final void write (RecordOutputStream s) throws IOException {
        s.startRecord(ModuleSerializationTags.TYPE_CLASS, serializationSchema);
        super.writeContent(s);
       
        // parent type classes
        s.writeShortCompressed(parentTypeClassList.size());      
        for (final TypeClass parent : parentTypeClassList) {         
            s.writeQualifiedName(parent.getName());
        }
       
        // class methods
        s.writeShortCompressed(classMethodsList.size());       
        for (final ClassMethod cm : classMethodsList) {           
            cm.write(s);
        }
              
        // kindExpr field
        kindExpr.write(s);
       
        // typeClassTypeVarName field
        s.writeUTF(typeClassTypeVarName);       
       
        s.endRecord();
    }
   
    /**
     * Because type classes can have mutually recursive references we need to
     * do an initial load of the TypeClass objects loading just the members in
     * the base class.  This is enough to build up the map of TypeClass instances
     * for the module and allow resolution of references to other type classes.
     * The TypeClass specific members are loaded by loadFinal().
     * The read position will be before the TypeClass record header.
     * @param s
     * @param mti
     * @param msgLogger the logger to which to log deserialization messages.
     * @return an instance of TypeClass.
     * @throws IOException
     */
    static final TypeClass loadInit(RecordInputStream s, ModuleTypeInfo mti, CompilerMessageLogger msgLogger) throws IOException {
        TypeClass tc = new TypeClass();
        try {
            tc.readInit(s, mti, msgLogger);
        } catch (IOException e) {
            // Add context to error message.
            QualifiedName qn = tc.getName();
            throw new IOException ("Error loading TypeClass " + (qn == null ? "" : qn.getQualifiedName()) + ": " + e.getLocalizedMessage());
        }
        return tc;
    }
   
    /**
     * Load the TypeClass specific members of this TypeClass instance.
     * The read position will be before the TypeClass record header.
     * @param s
     * @param mti
     * @param msgLogger the logger to which to log deserialization messages.
     * @throws IOException
     */
    final void loadFinal(RecordInputStream s, ModuleTypeInfo mti, CompilerMessageLogger msgLogger) throws IOException {
        try {
            readFinal(s, mti, msgLogger);
        } catch (IOException e) {
            // Add context to error message.
            QualifiedName qn = getName();
            throw new IOException ("Error loading TypeClass " + (qn == null ? "" : qn.getQualifiedName()) + ": " + e.getLocalizedMessage());
        }
    }
   
   
    /**
     * Because type classes can have mutually recursive references we need to
     * do an initial load of the TypeClass objects loading just the members in
     * the base class.  This is enough to build up the map of TypeClass instances
     * for the module and allow resolution of references to other type classes.
     * The TypeClass specific members are loaded by readFinal().
     * The read position will be before the TypeClass record header.
     * @param s
     * @param mti   
     * @param msgLogger the logger to which to log deserialization messages.
     * @throws IOException
     */
    private void readInit (RecordInputStream s, ModuleTypeInfo mti, CompilerMessageLogger msgLogger) throws IOException {
        // Look for Record header.
        RecordHeaderInfo rhi = s.findRecord(ModuleSerializationTags.TYPE_CLASS);
        if (rhi == null) {
            throw new IOException("Unable to find TypeClass record header.");
        }
        DeserializationHelper.checkSerializationSchema(rhi.getSchema(), serializationSchema, mti.getModuleName(), "TypeClass", msgLogger);
       
        super.readContent(s, mti, msgLogger);
       
        // Now that we've read/set the name of this TypeClass we can add it to the ModuleTypeInfo.
        mti.addTypeClass(this);
       
        s.skipRestOfRecord();
    }
   
    /**
     * Load the TypeClass specific members of this TypeClass instance.
     * The read position will be before the TypeClass record header.
     * @param s
     * @param mti
     * @param msgLogger the logger to which to log deserialization messages.
     * @throws IOException
     */
    private void readFinal (RecordInputStream s, ModuleTypeInfo mti, CompilerMessageLogger msgLogger) throws IOException {
        // Look for Record header.
        RecordHeaderInfo rhi = s.findRecord(ModuleSerializationTags.TYPE_CLASS);
        if(rhi == null) {
            throw new IOException("Unable to find TypeClass record header.");
        }
        DeserializationHelper.checkSerializationSchema(rhi.getSchema(), serializationSchema, mti.getModuleName(), "TypeClass", msgLogger);
       
        // Now get the record header for the parent members and skip the entire record.
        RecordHeaderInfo parentHeader = s.findRecord(ModuleSerializationTags.SCOPED_ENTITY);
        if(parentHeader == null) {
            throw new IOException("Unable to find ScopedEntityImpl record header while loading TypeClass " + getName() + ".");
        }
        s.skipRestOfRecord();
       
        int nParentTypeClasses = s.readShortCompressed();
        for (int i = 0; i < nParentTypeClasses; ++i) {
            QualifiedName qn = s.readQualifiedName();
            TypeClass parent = mti.getReachableTypeClass(qn);
            // This could be a type from a non-direct dependee module.
            if (parent == null) {
                throw new IOException("Unable to resolve TypeClass " + qn + " while loading TypeClass " + getName());
            }
           
            addParentClass(parent);
        }
       
        int nClassMethods = s.readShortCompressed();
        for (int i = 0; i < nClassMethods; ++i) {
            ClassMethod cm = ClassMethod.load(s, mti, msgLogger);
            addClassMethod(cm);
        }
       
        kindExpr = KindExpr.load(s, mti.getModuleName(), msgLogger);
       
        typeClassTypeVarName = s.readUTF();
       
        s.skipRestOfRecord();
    }
   
}
TOP

Related Classes of org.openquark.cal.compiler.TypeClass

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.