Package org.openquark.cal.compiler

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

/*
* 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.
*/


/*
* TypeConstructor.java
* Created: July 24, 2001
* By: Bo Ilic
*/

package org.openquark.cal.compiler;

import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;

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;
import org.openquark.cal.module.Cal.Core.CAL_Prelude;
import org.openquark.cal.util.ArrayMap;


/**
* Represents type constructors in CAL e.g. "Cal.Core.Prelude.Int", "Cal.Core.Prelude.Maybe".
* <P>
* Creation date: (July 24, 2001)
* @author Bo Ilic
*/
public final class TypeConstructor extends ScopedEntity {
      
    private static final int serializationSchema = 0;
   
    /**
     * This class is used to store information about a deriving clause element
     * It records the name of class instance and the position in the source code
     * of that name
     * @author mbyne
     */
    final static class DerivingClauseInfo {
        /** the name of the type - this cannot be null*/
        final private QualifiedName name;
       
        /** the source range of the deriving type - this may be null*/
        final private SourceRange range;

        DerivingClauseInfo(QualifiedName name, SourceRange range) {
            if (name == null) {
                throw new NullPointerException("The argument name cannot be null");
            }
           
            this.name = name;
            this.range = range;
        }

        /** get the source range of the name, this may be null if it is not available*/
        SourceRange getSourceRange() {
            return range;
        }

        /** get the name of the class*/
        QualifiedName getName() {
            return name;
        }
    }
   
    /**
     * The array of possible record tags used in calls to {@link RecordInputStream#findRecord(short[])} by
     * the {@link #loadInit} and {@link #loadFinal} methods.
     */
    private static final short[] TYPE_CONSTRUCTOR_ENTITY_RECORD_TAGS = new short[] {
        ModuleSerializationTags.TYPE_CONSTRUCTOR_ENTITY,
        ModuleSerializationTags.FOREIGN_TYPE_CONSTRUCTOR_ENTITY
    };
   
    /**
     * the kind of this TypeConstructor. For example, Prelude.List has kind * -> * while Prelude.Ordering has kind *.
     * For type constuctor entities that have successfully passed type checking, this will not have any kind variables in it.
     */
    private KindExpr kindExpr;
   
    /**
     * extra information about the Java implementation of this type if it was defined using a foreign data declaration.
     * null if this is not a foreign type.
     */
    private ForeignTypeInfo foreignTypeInfo;
   
    /**
     * (String -> DataConstructor)
     * the data constructors defined in the data declaration for the type. They are ordered in the same
     * order as in the data declaration.
     */
    private final ArrayMap<String, DataConstructor> dataConstructorMap;
   
    /**
     * Names and positions of the type classes appearing in the "deriving" clause of the type.
     * These define the instances that are automatically derived for the type.
     * They are ordered in the order that they appear textually in the deriving clause.
     * There are no duplicates.
     */
    private DerivingClauseInfo[] derivingClauseTypeClassInfos;
   
    static private final DerivingClauseInfo[] NO_DERIVING_CLAUSE = new DerivingClauseInfo[0];  
     
    /** the TypeConstructor for the Prelude.Function type */
    static final TypeConstructor FUNCTION = new TypeConstructor(CAL_Prelude.TypeConstructors.Function, Scope.PUBLIC, KindExpr.makeKindChain(3), null, NO_DERIVING_CLAUSE, null);
       
    /**
     * map from the QualifiedName of the built-in type constructor to its TypeConstructor. Note: the name is an internal name for the
     * type constructors that do not have CAL source names.
     */
    private static final Map<QualifiedName, TypeConstructor> builtInTypeConstructors = makeBuiltInTypeConstructors();

    /**
     * TypeConstructor constructor comment.
     * @param name
     * @param scope
     * @param kindExpr
     * @param foreignTypeInfo
     * @param derivingClauseTypeClassNames
     * @param calDocComment
     */
    private TypeConstructor(QualifiedName name, Scope scope,
            KindExpr kindExpr, ForeignTypeInfo foreignTypeInfo,
            DerivingClauseInfo[] derivingClauseTypeClassNames,
            CALDocComment calDocComment) {

        super(name, scope, calDocComment);
       
        if (kindExpr == null || derivingClauseTypeClassNames == null) {
            throw new NullPointerException()
        }
       
        if (foreignTypeInfo != null && kindExpr != KindExpr.STAR) {
            throw new IllegalArgumentException ("foreign type constructors must have kind *.");
        }
            
        this.kindExpr = kindExpr;
        this.foreignTypeInfo = foreignTypeInfo;
        this.derivingClauseTypeClassInfos = derivingClauseTypeClassNames;
       
        this.dataConstructorMap = new ArrayMap<String, DataConstructor>();
    }
   
    // zero argument constructor used for serialization.
    private TypeConstructor() {
        this.dataConstructorMap = new ArrayMap<String, DataConstructor>();
    }
         
    /**
     * The factory method for TypeConstructor.
     * @param name
     * @param scope
     * @param kindExpr
     * @param foreignTypeInfo use null if this is not a foreign data type.
     * @param derivingClauseTypeClassNames
     * @param calDocComment the CALDoc associated with this entity, or null if there is none.
     * @return TypeConstructor
     */
    static final TypeConstructor makeTypeConstructor(QualifiedName name, Scope scope, KindExpr kindExpr, ForeignTypeInfo foreignTypeInfo, DerivingClauseInfo[] derivingClauseTypeClassNames, CALDocComment calDocComment) {
                              
        TypeConstructor typeCons = builtInTypeConstructors.get(name);
        if (typeCons != null) {
            return typeCons;
        }
           
        return new TypeConstructor (name, scope, kindExpr, foreignTypeInfo, derivingClauseTypeClassNames, calDocComment);
    }  
   
    /**
     * The kind of this TypeConstructor.
     * For example, the kind of Prelude.Function this will be * -> * -> *.
     * The kind of Prelude.Maybe is * -> *.
     * The kind of Prelude.Int it is *.
     * One important point to note is that the kind of a TypeConstructor will not involve kind variables. 
     * @return KindExpr
     */
    KindExpr getKindExpr() {
        return kindExpr;
    }
   
    /**
     * The number of type variable arguments taken by the fully saturated type constructor.
     * For example,
     * the type arities of Int, String, Char are 0
     * the type arity of List is 1.
     * the type arity of Either is 2.
     *    
     * For example, if the kind is *->*->*, then this is 2.
     * @return int the number of type variable arguments taken by the fully saturated type constructor.
     */
    public int getTypeArity() {
        return kindExpr.getNArguments();
    }
         
    /**   
     * @return ForeignTypeInfo information about the Java type corresponding to this foreign type if
     *      this is a foreign type, or null if not a foreign type.
     */
    public final ForeignTypeInfo getForeignTypeInfo() {
        return foreignTypeInfo;
    }
              
    /**
     * Returns a map from the built-in type names to their TypeConstructor objects.
     * All built-in types must belong to the Prelude module.
     *   
     * "Cal.Core.Prelude.Function a b" is equivalent to "a -> b"    
     * Type application uses the special syntax of juxtiposition e.g. Maybe Int.
     * 
     * @return Map (QualifiedName->TypeConstructor)
     */
    private static final Map<QualifiedName, TypeConstructor> makeBuiltInTypeConstructors() {
       
        Map<QualifiedName, TypeConstructor> tcs = new LinkedHashMap<QualifiedName, TypeConstructor> ();
                   
        //reason for being built-in: Function is neither an algebraic type, nor a foreign type. It is truly something special.                   
        addBuiltInTypeConstructor(tcs, TypeConstructor.FUNCTION);                           
       
        return tcs;                               
    }
   
    /**
     * Method addBuiltInTypeConstructor.
     * @param tcs
     * @param typeConstructor
     */
    private static final void addBuiltInTypeConstructor(Map<QualifiedName, TypeConstructor> tcs, TypeConstructor typeConstructor){
       
        tcs.put(typeConstructor.getName(), typeConstructor);
    }
   
    /**
     * Adds all the built-in types to the ModuleTypeInfo object.
     * @param moduleTypeInfo the module in which to add the built-in types to. Should be the Prelude module.
     */
    static final void addBuiltInTypes(ModuleTypeInfo moduleTypeInfo) {
       
        if (!moduleTypeInfo.getModuleName().equals(CAL_Prelude.MODULE_NAME)) {
            throw new IllegalArgumentException ("the built-in type constructors belong to the Prelude module.");
        }
                               
        for (final QualifiedName qualifiedName : builtInTypeConstructors.keySet()) {    
                   
            moduleTypeInfo.addTypeConstructor(builtInTypeConstructors.get(qualifiedName));
        }       
    }
   
    /**
     * Note: all built-in types belong to the Prelude module.
     * @param typeName name of the built-in type.
     * @return TypeConstructor entity for the built-in type of null if it does not exist
     */
    static final TypeConstructor getBuiltInType(QualifiedName typeName) {
        return builtInTypeConstructors.get(typeName);
    }
   
    /**
     * 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();
    }
                  
    /**   
     * @return textual description of this type constructor entity.
     */
    @Override
    public String toString() {
        return super.toString() + " hasKind " + kindExpr + " foreignInfo " + foreignTypeInfo;
    }
      
    /**
     * Method getDataConstructor.
     * @param dataConstructorName the unqualified data constructor name constructing values of this type
     * @return DataConstructor null if this type does not define a data constructor having the given name 
     */
    public DataConstructor getDataConstructor(String dataConstructorName) {

        return dataConstructorMap.get(dataConstructorName);
    }       
   
    /**
     * Method getNDataConstructors.
     * @return int the number of data constructors defined for this type in its data declaration. Will be 0 for foreign types.
     */
    public int getNDataConstructors() {
        return dataConstructorMap.size();
    }
   
    /**
     * Method getNthDataConstructor.
     * @param n zero based index
     * @return DataConstructor the nth data constructor defined in the data declaration for the type.
     */
    public DataConstructor getNthDataConstructor(int n) {
       
        return dataConstructorMap.getNthValue(n);                  
    }
   
    /**
     * Method addDataConstructor.
     * @param dataConstructor as defined within a data declaration for this type. Add in order of their declaration. 
     */
    void addDataConstructor(DataConstructor dataConstructor) {

        QualifiedName dataConstructorName = dataConstructor.getName();
      
        if (!getName().getModuleName().equals(dataConstructorName.getModuleName())) {

            throw new IllegalArgumentException("the data constructor added must belong to the same module as the type.");
        }
               
        if(dataConstructorMap.put(dataConstructorName.getUnqualifiedName(), dataConstructor) != null) {
             throw new IllegalArgumentException("The type " + this + " already defines the data constructor " + dataConstructorName.getUnqualifiedName() + ".");
        }       
    }
   
    /**    
     * Note: do not expose as public. Clients do not need to know if an instance declaration is defined via a deriving clause
     * or via a "regular" instance declaration.
     *
     * @return int the number of type class names in the deriving clause. May be 0.
     */
    int getNDerivedInstances() {       
        return derivingClauseTypeClassInfos.length;
    }
   
    /**    
     * Note: do not expose as public. Clients do not need to know if an instance declaration is defined via a deriving clause
     * or via a "regular" instance declaration.
     *
     * @param n index where 0 <= n < getNDerivedInstances.
     * @return QualifiedName the type class name.
     */   
    QualifiedName getDerivingClauseTypeClassName(int n) {
        return derivingClauseTypeClassInfos[n].getName();
    }

    /**    
     * Note: do not expose as public. Clients do not need to know if an instance declaration is defined via a deriving clause
     * or via a "regular" instance declaration.
     *
     * returns the source range for the code that requests the derived instance.
     *
     * @param n index where 0 <= n < getNDerivedInstances.
     * @return the source range of the declaration
     */   
    SourceRange getDerivingClauseTypeClassPosition(int n) {
        if (derivingClauseTypeClassInfos[n].getSourceRange() == null)
            return null;
       
        return derivingClauseTypeClassInfos[n].getSourceRange();
    }
   
    boolean hasDerivingClause(QualifiedName typeClassName) {
             
        for (int i = 0, nDerivedInstances = getNDerivedInstances(); i < nDerivedInstances; ++i) {
            if (getDerivingClauseTypeClassName(i).equals(typeClassName)) {
                return true;
            }
        }
        return false;
    }
   
    /**
     * Write this instance of TypeConstructor to the RecordOutputStream
     * @param s
     * @throws IOException
     */
    @Override
    void write (RecordOutputStream s) throws IOException {
        if (foreignTypeInfo == null) {
            s.startRecord(ModuleSerializationTags.TYPE_CONSTRUCTOR_ENTITY, serializationSchema);
        } else {
            s.startRecord(ModuleSerializationTags.FOREIGN_TYPE_CONSTRUCTOR_ENTITY, serializationSchema);
        }
       
        super.writeContent(s);
       
        kindExpr.write(s);
       
        if (foreignTypeInfo != null) {
            try {
                foreignTypeInfo.write(s);
            } catch (UnableToResolveForeignEntityException e) {
                // If we are serializing the info, then it must not have been originally deserialized lazily
                final IllegalStateException illegalStateException = new IllegalStateException("Java entities in the ForeignTypeInfo should have been eagerly resolved during the compilation process");
                illegalStateException.initCause(e);
                throw illegalStateException;
            }
        }

        final int nDataConstructors = dataConstructorMap.size();
        s.writeShortCompressed(nDataConstructors);
        for (int i = 0; i < nDataConstructors; ++i) {
            DataConstructor dc = dataConstructorMap.getNthValue(i);
            dc.write(s);
        }
       
        //serialize the field derivingClauseTypeClassNames
        final int nDerivingClauseTypeClassNames = derivingClauseTypeClassInfos.length;       
        s.writeShortCompressed(nDerivingClauseTypeClassNames);
        for (int i = 0; i < nDerivingClauseTypeClassNames; ++i) {
            QualifiedName typeClassName = derivingClauseTypeClassInfos[i].getName();
            s.writeQualifiedName(typeClassName);
        }
       
        s.endRecord();
    }

    /**
     * Load an instance of TypeConstructor from the RecordInputStream
     * This simply creates an instance of the TypeConstructor and
     * loads the members of the ScopedEntityImpl base class.  The rest of
     * the member have to be resolved through loadFinal().
     * This is done so that the set of TypeConstructor instances can be
     * build up before loading the members that can have mutually recursive and out-of-order
     * references.
     * @param s   
     * @param mti
     * @param msgLogger the logger to which to log deserialization messages.
     * @return the instance of TypeConstructor with members of the ScopedEntityImpl base class loaded
     * @throws IOException
     */
    static TypeConstructor loadInit (RecordInputStream s, ModuleTypeInfo mti, CompilerMessageLogger msgLogger) throws IOException {
        RecordHeaderInfo rhi = s.findRecord(TYPE_CONSTRUCTOR_ENTITY_RECORD_TAGS);
        if (rhi == null) {
            throw new IOException ("Unable to find TypeConstructor record.");
        }
        DeserializationHelper.checkSerializationSchema(rhi.getSchema(), serializationSchema, mti.getModuleName(), "TypeConstructor", msgLogger);

        TypeConstructor typeCons = new TypeConstructor();
        try {
            // Note we must do a reassignment of the local variable tce since readInitContent
            // may return a different TypeConstructor if this is a built-in type.
            typeCons = typeCons.readInitContent(s, rhi.getSchema(), mti, msgLogger);
        } catch (IOException e) {
            QualifiedName qn = typeCons.getName();
            throw new IOException ("Error loading TypeConstructor " + (qn == null ? "" : qn.getQualifiedName()) + ": " + e.getLocalizedMessage());
        }
       
        return typeCons;
    }

    /**
     * Load an instance of TypeConstructor from the RecordInputStream
     * This simply creates an instance of the TypeConstructor and
     * loads the members of the ScopedEntityImpl base class.  The rest of
     * the member have to be resolved through finalLoad().
     * @param s    
     * @param mti    
     * @param msgLogger the logger to which to log deserialization messages.
     * @throws IOException
     */
    void loadFinal (RecordInputStream s, ModuleTypeInfo mti, CompilerMessageLogger msgLogger) throws IOException {
        RecordHeaderInfo rhi = s.findRecord(TYPE_CONSTRUCTOR_ENTITY_RECORD_TAGS);
        if (rhi == null) {
            throw new IOException ("Unable to find TypeConstructor record.");
        }
        DeserializationHelper.checkSerializationSchema(rhi.getSchema(), serializationSchema, mti.getModuleName(), "TypeConstructor", msgLogger);

        try {
            super.readContent (s, mti, msgLogger);
           
            // QualifiedName
            QualifiedName name = this.getName();
            TypeConstructor builtIn = getBuiltInType(name);
            if (builtIn != null) {
                s.skipRestOfRecord();
                return;
            }
           
            // kindExpr
            kindExpr = KindExpr.load(s, mti.getModuleName(), msgLogger);
           
            // ForeignTypeInfo  
            if (rhi.getRecordTag() == ModuleSerializationTags.FOREIGN_TYPE_CONSTRUCTOR_ENTITY) {
                foreignTypeInfo = ForeignTypeInfo.load(s, mti.getModuleName(), mti.getModule().getForeignClassLoader(), msgLogger);
            }
           
            int nDCs = s.readShortCompressed();
            for (int i = 0; i < nDCs; ++i) {
                DataConstructor dc = DataConstructor.load(s, mti, msgLogger);
                addDataConstructor(dc);
            }
           
            //load the field derivingClauseTypeClassNames
            final int nDerivingClauseTypeClassNames = s.readShortCompressed();  
            if (nDerivingClauseTypeClassNames == 0) {
                derivingClauseTypeClassInfos = NO_DERIVING_CLAUSE;
            } else {
                derivingClauseTypeClassInfos = new DerivingClauseInfo[nDerivingClauseTypeClassNames];
                for (int i = 0; i < nDerivingClauseTypeClassNames; ++i) {               
                    QualifiedName typeClassName = s.readQualifiedName();
                    derivingClauseTypeClassInfos[i] = new DerivingClauseInfo(typeClassName, null);
                }
            }
           
            s.skipRestOfRecord();
        } catch (IOException e) {
            QualifiedName qn = getName();
            throw new IOException ("Error loading TypeConstructor " + (qn == null ? "" : qn.getQualifiedName()) + ": " + e.getLocalizedMessage());
        }
    }
  
    /**
     * Do an initial read to establish the existence of this TypeConstructor.  The reading of the
     * content has to be split into two stages since you can have a closely connected set of TypeConstructor.
     * @param s - the RecordInputStream
     * @param schema - schema of the record in the stream
     * @param mti - current ModuleTypeInfo
     * @param msgLogger the logger to which to log deserialization messages.
     * @return - 'this' if the type is not a built-in.  Otherwise returns the built-in TypeConstructor.
     * @throws IOException
     */
    private TypeConstructor readInitContent (RecordInputStream s, int schema, ModuleTypeInfo mti, CompilerMessageLogger msgLogger) throws IOException {

        super.readContent (s, mti, msgLogger);
       
        // QualifiedName
        QualifiedName name = this.getName();
        TypeConstructor builtIn = getBuiltInType(name);
        if (builtIn != null) {
            s.skipRestOfRecord();
            return builtIn;
        }
       
        // Now that we've loaded the name of the TypeConstructor we can add it to the ModuleTypeInfo.
        mti.addTypeConstructor(this);
        s.skipRestOfRecord();
       
        return this;
    }
}
TOP

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

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.