Package org.openquark.cal.compiler

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

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


/*
* KindExpr.java
* Creation date: (January 8, 2001)
* By: Bo Ilic
*/
package org.openquark.cal.compiler;

import java.io.IOException;
import java.util.HashMap;
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;


/**
* The internal representation of kind expressions. Each type constructor, type class and type
* expression in a CAL program has an associated kind.
*
* <p>
* Kind inference is the process of determining the kind of user-supplied type expressions, and is used
* to verify that type expressions are correct. User-supplied type expressions occur
* <ol>
<li> in function type declarations and expression type signatures
<li> in algebraic data declarations
<li> in type class definitions
* </ol>
*
* <p>
* This is analogous to an expression having an associated type,
* with type inference being used to determine that the expression is correct. For example, Double
* and Boolean have kind *, while Maybe has kind * -> * and Prelude.Function has kind * -> * -> *.
*
* <p>
* Thus, for example, type expressions such as "Int Double", "Function Char Int Double" will result
* in kind errors. However, depending on the context, an type constructor can correctly be used in an
* undersaturated form. For example, List, Maybe and "Map k" are instances of the Functor type class.
*
* <p>
* Creation date: (January 8, 2001)
* @author Bo Ilic
*/
abstract class KindExpr {
   
    static final KindConstant STAR = new KindConstant();

    /**
     * The array of possible record tags used in calls to {@link RecordInputStream#findRecord(short[])} by
     * the {@link #load} method.
     */
    private static final short[] KIND_EXPR_RECORD_TAGS = new short[]{
        ModuleSerializationTags.KIND_EXPR_KIND_CONSTANT,
        ModuleSerializationTags.KIND_EXPR_KIND_FUNCTION  
    };
   
    /**
     * A kind variable. It is uninstantiated when the instance field is null and instantiated
     * otherwise. An instantiated kind variable behaves like its instantiation.
     */
    static final class KindVar extends KindExpr {
            
        private KindExpr instance;

        KindVar() {
            instance = null;
        }

        KindExpr getInstance() {
            return instance;
        }
       
        void setInstance(KindExpr instance) {
            if (this.instance != null) {           
                throw new IllegalStateException("KindExpr.setInstance: Programming Error- can't set the instance of an instantiated kind variable.");
            }
            this.instance = instance;
        }

        /**
         * Whether an uninstantiated kind variable occurs in a kind expression.
         * For example, "k" occurs in "* -> k -> *". We do not allow unifications where
         * a kind variable is specialized to a kind expression in which the variable
         * itself occurs since this leads to infinite kinds.
         */
        boolean occursInKindExpr(KindExpr kindExpr) {

            kindExpr = kindExpr.prune();

            if (kindExpr instanceof KindVar) {
                return this == kindExpr;

            } else if (kindExpr instanceof KindFunction) {

                return occursInKindExpr(((KindFunction) kindExpr).getDomain()) || occursInKindExpr(((KindFunction) kindExpr).getCodomain());

            } else if (kindExpr instanceof KindConstant) {
                return false;
            }

            throw new IllegalArgumentException("Programming error");
        }
       
        /** {@inheritDoc} */
        @Override
        int getNArguments() {
           
            if (instance != null) {
                return instance.getNArguments();
            }
           
            return 0;          
        }
       
        /** {@inheritDoc} */
        @Override
        boolean isSimpleKindChain() {
           if (instance != null) {
               return instance.isSimpleKindChain();
           }
           return false;
        }       

        /** {@inheritDoc} */
        @Override
        KindExpr prune() {

            if (instance != null) {
                return instance.prune();              
            }

            return this;
        }
       
        /** {@inheritDoc} */
        @Override
        boolean containsKindVars() {
            return true;
        }           

        @Override
        String toString(Map<KindVar, Integer> kindVarToIndexMap) {

            if (instance != null) {
                return instance.toString(kindVarToIndexMap);
            }

            Object indexObject = kindVarToIndexMap.get(this);
            int index;
            if (indexObject == null) {
                index = kindVarToIndexMap.size() + 1;
                kindVarToIndexMap.put(this, Integer.valueOf(index));
            } else {
                index = ((Integer) indexObject).intValue();
            }

            return indexToVarName(index);
        }

        private static String indexToVarName(int index) {

            if (index > 0 && index <= 26) {
                return String.valueOf((char) ('a' + index - 1));
            }

            return "a" + String.valueOf(index);
        }
       
        @Override
        final void write (RecordOutputStream s) throws IOException {
            throw new IOException("we should not be serializing kind variables- they are transient during type checking.");
        }
        
    }

    /**
     * A singleton. KindConstant is the kind *.
     */
    static final class KindConstant extends KindExpr {

        private static final int serializationSchema = 0;
       
        /** should only be used to construct the KindExpr.STAR. */
        private KindConstant() {
        }
       
        /** {@inheritDoc} */
        @Override
        int getNArguments() {
            return 0;
        }
       
        /** {@inheritDoc} */
        @Override
        boolean isSimpleKindChain() {
           return true;
        }             

        /** {@inheritDoc} */
        @Override
        KindExpr prune() {
            return this;
        }
       
        /** {@inheritDoc} */
        @Override
        boolean containsKindVars() {
            return false;
        }          

        @Override
        String toString(Map<KindVar, Integer> kindVarToIndexMap) {
            return "*";
        }
       
        @Override
        final void write (RecordOutputStream s) throws IOException {
            s.startRecord(ModuleSerializationTags.KIND_EXPR_KIND_CONSTANT, serializationSchema);
            s.endRecord();
        }
       
        static final KindConstant load (RecordInputStream s, int schema, ModuleName moduleName, CompilerMessageLogger msgLogger) throws IOException {
            DeserializationHelper.checkSerializationSchema(schema, serializationSchema, moduleName, "KindExpr.KindConstant", msgLogger);
            s.skipRestOfRecord();
           
            return KindExpr.STAR;
        }
    }

    /**
     * KindFunction is the kind k1 -> k2 where k1 and k2 are kinds.
     */
    static final class KindFunction extends KindExpr {

        private static final int serializationSchema = 0;
        private KindExpr domain;
        private KindExpr codomain;

        KindFunction(KindExpr domain, KindExpr codomain) {
            if (domain == null || codomain == null) {
                throw new NullPointerException();
            }
            this.domain = domain;
            this.codomain = codomain;
        }

        KindExpr getDomain() {
            return domain;
        }

        KindExpr getCodomain() {
            return codomain;
        }
       
        /** {@inheritDoc} */
        @Override
        int getNArguments() {
            return 1 + codomain.getNArguments();
        }
       
        /** {@inheritDoc} */
        @Override
        boolean isSimpleKindChain() {
            return (domain.prune() instanceof KindConstant) && codomain.isSimpleKindChain();
        }        

        /** {@inheritDoc} */       
        @Override
        KindExpr prune() {
            return this;
        }
       
        /** {@inheritDoc} */
        @Override
        boolean containsKindVars() {
            return domain.containsKindVars() || codomain.containsKindVars();
        }        

        @Override
        String toString(Map<KindVar, Integer> kindVarToIndexMap) {

            StringBuilder buf = new StringBuilder();

            String domainString = domain.toString(kindVarToIndexMap);
            if (domainString.length() == 1) {
                buf.append(domainString);
            } else {
                buf.append("(").append(domainString).append(")");
            }

            buf.append(" -> ");

            //parentheses around the codomain are not necessary since -> is right associative.
            buf.append(codomain.toString(kindVarToIndexMap));

            return buf.toString();
        }
       
        @Override
        final void write (RecordOutputStream s) throws IOException {
            s.startRecord(ModuleSerializationTags.KIND_EXPR_KIND_FUNCTION, serializationSchema);
            domain.write(s);
            codomain.write(s);
            s.endRecord();
        }

        /**
         * Load an instance of KindFunction from the RecordInputStream.
         * Read position will be after the KindFunction record header.
         * @param s
         * @param schema
         * @param moduleName the name of the module being loaded
         * @param msgLogger the logger to which to log deserialization messages.
         * @return a KindFunction instance.
         * @throws IOException
         */
        final static KindFunction load (RecordInputStream s, int schema, ModuleName moduleName, CompilerMessageLogger msgLogger) throws IOException {
            DeserializationHelper.checkSerializationSchema(schema, serializationSchema, moduleName, "KindExpr.KindFunction", msgLogger);
            KindExpr ke1 = KindExpr.load(s, moduleName, msgLogger);
            KindExpr ke2 = KindExpr.load(s, moduleName, msgLogger);
            s.skipRestOfRecord();
            return new KindFunction(ke1, ke2);
        }
    }

    /**
     * Converts all the kind variables in a kind expression to the default binding *. e.g the
     * kind expression (k1 -> *) -> k2 is converted to  (* -> *) -> *. The returned KindExpr
     * will have no kind variables in it (instantiated or not). this kindExpr may be pruned,
     * but will otherwise not be altered.
     */
    KindExpr bindKindVariablesToConstant() {

        KindExpr kindExpr = this.prune();

        if (kindExpr instanceof KindVar) {

            //will be uninstantiated because of the pruning
            return STAR;

        } else if (kindExpr instanceof KindConstant) {

            return kindExpr;

        } else if (kindExpr instanceof KindFunction) {
           
            if (kindExpr.containsKindVars()) {
                KindExpr domainKindExpr = ((KindFunction) kindExpr).getDomain().bindKindVariablesToConstant();
                KindExpr codomainKindExpr = ((KindFunction) kindExpr).getCodomain().bindKindVariablesToConstant();
                return new KindExpr.KindFunction(domainKindExpr, codomainKindExpr);
            } else {
                //no kind variables, so don't need to make a copy.
                return kindExpr;
            }
        }

        throw new IllegalStateException("Programming error. Unrecognized KindExpr subtype.");
    }
   
    /**
     * A helper function to create kind expressions of the form *->...->*->*.
     * Creation date: (3/7/01 1:36:36 PM)
     * @return KindExpr
     * @param chainLength int the number of *'s in the chain
     */
    static KindExpr makeKindChain(int chainLength) {

        if (chainLength < 0) {
            throw new IllegalArgumentException("KindExpr.makeKindChain: negative chainLength.");
        }
      
        KindExpr kindExpr = KindExpr.STAR;

        while (--chainLength > 0) {
            kindExpr = new KindExpr.KindFunction(KindExpr.STAR, kindExpr);
        }

        return kindExpr;
    }
   
    /**    
     * @return int the number of type arguments taken by the kind. For example, for the kind
     * * -> * -> * this is 2, for the kind (* -> *) -> (* -> *) -> *, this is also 2.
     */
    abstract int getNArguments();
   
   
    /**    
     * @return boolean true if this KindExpr is of the form * -> * -> * ... -> *. In other words,
     * if this is the kind of a TypeConsApp of arity n, then all type variable arguments have kind *.
     * For example, will return false for (* -> *) -> * -> *.
     */
    abstract boolean isSimpleKindChain();    
   
    /**
     * Eliminate redundant instantiated kind variables at the top of this KindExpr.
     * The result of prune is always a non-instantiated kind variable, a kind
     * constant or a kind function.
     * <p>
     * This function has no side-effects on the invocation object.  
     * <p>
     * Creation date: (1/24/01 3:27:07 PM)
     * @return KindExpr
     */
    abstract KindExpr prune();  
   
    /**   
     * @return true if this KindExpr contains a KindVar, whether instantiated or not.
     */
    abstract boolean containsKindVars();
   
    /**
     * Returns a string representation of this KindExpr, where kind variable addresses are
     * replaced by lower case letters: a, b, c, ..., z, a1, a2, a3, ...
     * <p>
     * Since kinds are an internal implementation technique, this is mainly intended for
     * debugging purposes, in contrast to the analogous method in the TypeExpr class.
     * <p>
     * Creation date: (1/24/01 4:26:11 PM)
     * @return String
     */
    @Override
    public String toString() {
        return toString(new HashMap<KindVar, Integer>());
    }
   
    /**
     * Insert the method's description here.
     * Creation date: (1/24/01 4:24:29 PM)
     * @return String
     * @param typeVarToIndexMap
     */
    abstract String toString(Map<KindVar, Integer> typeVarToIndexMap);
   
    /**
     * Write the object to a record output stream.
     * @param s
     * @throws IOException
     */
    abstract void write (RecordOutputStream s) throws IOException;
   
    /**
     * Read an instance of one of the concrete derived classes from
     * the RecordInputStream.
     * @param s
     * @param moduleName the name of the module being loaded
     * @param msgLogger the logger to which to log deserialization messages.
     * @return and instance of KindExpr.
     * @throws IOException
     */
    static KindExpr load (RecordInputStream s, ModuleName moduleName, CompilerMessageLogger msgLogger) throws IOException {
       
        RecordHeaderInfo rhi = s.findRecord(KIND_EXPR_RECORD_TAGS);
       
        switch (rhi.getRecordTag()) {
            case ModuleSerializationTags.KIND_EXPR_KIND_CONSTANT:          
                return KindConstant.load(s, rhi.getSchema(), moduleName, msgLogger);
           
            case ModuleSerializationTags.KIND_EXPR_KIND_FUNCTION:
                return KindFunction.load(s, rhi.getSchema(), moduleName, msgLogger);
                          
            default:
                throw new IOException ("Unexpected record tag while loading KindExpr: " + rhi.getRecordTag());
        }
    }
   
    /**
     * Unify two kind expressions. What this means is to find the most general kind to which both
     * kindExpr1 and kindExpr2 can be specialized to. Unification fails when trying to a kind constant
     * to a kind function (such as * and *->*) or when trying to instantiate a kind variable to a term
     * containing that kind variable (like a and a->b, where a circular structure would be built.)
     * As a general point of interest, this process is essentially the same as unification in Prolog.
     *
     * <p>
     * An important point is that this function has side-effects on its KindExpr arguments, including
     * potentially when the unification fails in a TypeException.
     *
     * <p>
     * Creation date: (1/24/01 5:49:01 PM)
     * @param kindExpr1
     * @param kindExpr2
     */
    static void unifyKind(KindExpr kindExpr1, KindExpr kindExpr2) throws TypeException {

        kindExpr1 = kindExpr1.prune();
        kindExpr2 = kindExpr2.prune();

        if (kindExpr1 instanceof KindVar) {

            KindExpr.KindVar kindVar = (KindVar) kindExpr1;

            if (kindVar.occursInKindExpr(kindExpr2)) {

                if (kindVar != kindExpr2) {
                    throw new TypeException("Kind clash: attempt to create an infinite kind");
                }

            } else {
                kindVar.setInstance(kindExpr2);
            }

        } else if (kindExpr1 instanceof KindConstant) {

            if (kindExpr2 instanceof KindVar) {
                unifyKind(kindExpr2, kindExpr1);
            } else if (kindExpr2 instanceof KindFunction) {
                throw new TypeException("Kinding clash");
            } else if (kindExpr2 instanceof KindConstant) {
                //do nothing if kindExpr2 instanceof KindConstant
            } else {
                throw new IllegalStateException();
            }

        } else if (kindExpr1 instanceof KindFunction) {

            if (kindExpr2 instanceof KindVar || kindExpr2 instanceof KindConstant) {
                unifyKind(kindExpr2, kindExpr1);
            } else {
                //kindExpr2 instanceof KindFunction
                unifyKind(((KindFunction) kindExpr1).getDomain(), ((KindFunction) kindExpr2).getDomain());
                unifyKind(((KindFunction) kindExpr1).getCodomain(), ((KindFunction) kindExpr2).getCodomain());
            }
           
        }  else {
            throw new IllegalStateException();                                        
        }
    }
   
    /**
     * In certain cases there are multiple options as to how to break up a KindExpr into argument kinds and
     * return kind.
     * For example, if KindExpr is (* -> *) -> (* -> * -> *) -> *, then getKindPieces(2) returns
     * [* -> *, * -> * -> *, *]
     * and getKindPieces(1) returns [* -> *, (* -> * -> *) -> *]    
     *        
     * @param arity must be less than or equal to this.getNArguments()
     * @return kind pieces array of length arity + 1. The return kind is the final element of the array.
     */
    final KindExpr[] getKindPieces(int arity) {
              
        if (arity < 0 || arity > getNArguments()) {
            throw new IllegalArgumentException("arity arguments must be non-negative and less than or equal to KindExpr.getNArguments()");
        }
       
        KindExpr[] kindPieces = new KindExpr[arity + 1];

        KindExpr kindExpr = this.prune();

        for (int i = 0; i < arity; ++i) {
                                  
            if (kindExpr instanceof KindFunction) {
               
                KindFunction kindFunction = (KindFunction)kindExpr;
                                               
                kindPieces[i] = kindFunction.getDomain().prune();
                kindExpr = kindFunction.getCodomain().prune();
                          
            } else {
                throw new IllegalStateException();
            }        
        }

        kindPieces[arity] = kindExpr;

        return kindPieces;
    }   
   
}
TOP

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

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.