package org.simantics.scl.compiler.types; import java.util.ArrayList; import org.simantics.scl.compiler.environment.Environment; import org.simantics.scl.compiler.internal.types.HashCodeUtils; import org.simantics.scl.compiler.internal.types.TypeHashCodeContext; import org.simantics.scl.compiler.internal.types.ast.TEffectAst; import org.simantics.scl.compiler.internal.types.ast.TFunctionAst; import org.simantics.scl.compiler.internal.types.ast.TPredAst; import org.simantics.scl.compiler.internal.types.ast.TypeAst; import org.simantics.scl.compiler.types.exceptions.KindUnificationException; import org.simantics.scl.compiler.types.kinds.Kind; import org.simantics.scl.compiler.types.kinds.Kinds; import org.simantics.scl.compiler.types.util.Polarity; import org.simantics.scl.compiler.types.util.TypeUnparsingContext; import gnu.trove.map.hash.THashMap; import gnu.trove.set.hash.THashSet; public class TFun extends Type { public Type domain; public Type effect; public Type range; TFun(Type domain, Type effect, Type range) { if(domain == null) throw new NullPointerException(); if(effect == null) throw new NullPointerException(); if(range == null) throw new NullPointerException(); /*domain = Types.weakCanonical(domain); range = Types.weakCanonical(range); if(domain instanceof TUnion) throw new RuntimeException(); if(range instanceof TUnion) throw new RuntimeException();*/ this.domain = domain; this.effect = effect; this.range = range; } @Override public Type replace(TVar var, Type replacement) { Type newDomain = domain.replace(var, replacement); Type newEffect = effect.replace(var, replacement); Type newRange = range.replace(var, replacement); if(newDomain == domain && newEffect == effect && newRange == range) return this; else return new TFun(newDomain, newEffect, newRange); } @Override public TypeAst toTypeAst(TypeUnparsingContext context) { TypeAst domainAst = domain.toTypeAst(context); TypeAst rangeAst = range.toTypeAst(context); if(Types.canonical(effect) != Types.NO_EFFECTS) rangeAst = new TEffectAst(effect.toTypeAst(context), rangeAst); Type dom = Types.canonical(domain); if(dom instanceof TPred) return new TPredAst(domainAst, rangeAst); else if(dom == Types.PUNIT) { //if(rangeAst instanceof TEffectAst) return rangeAst; /*else return new TEffectAst(Types.NO_EFFECTS, rangeAst);*/ } else return new TFunctionAst(domainAst, rangeAst); } @Override public void toName(TypeUnparsingContext context, StringBuilder b) { b.append("FUNC_"); domain.toName(context, b); b.append('_'); effect.toName(context, b); b.append('_'); range.toName(context, b); } @Override public void updateHashCode(TypeHashCodeContext context) { context.append(TypeHashCodeContext.FUN); domain.updateHashCode(context); effect.updateHashCode(context); range.updateHashCode(context); } @Override public void collectFreeVars(ArrayList vars) { domain.collectFreeVars(vars); effect.collectFreeVars(vars); range.collectFreeVars(vars); } @Override public void collectMetaVars(ArrayList vars) { domain.collectMetaVars(vars); effect.collectMetaVars(vars); range.collectMetaVars(vars); } @Override public void collectMetaVars(THashSet vars) { domain.collectMetaVars(vars); effect.collectMetaVars(vars); range.collectMetaVars(vars); } @Override public void collectEffectMetaVars(ArrayList vars) { domain.collectEffectMetaVars(vars); effect.collectMetaVars(vars); range.collectEffectMetaVars(vars); } @Override public boolean isGround() { return domain.isGround() && effect.isGround() && range.isGround(); } @Override public boolean containsMetaVars() { return domain.containsMetaVars() || effect.containsMetaVars() || range.containsMetaVars(); } @Override public boolean contains(TMetaVar other) { return domain.contains(other) || effect.contains(other) || range.contains(other); } @Override public int getClassId() { return FUN_ID; } @Override public Kind inferKind(Environment context) throws KindUnificationException { domain.checkKind(context, Kinds.STAR); range.checkKind(context, Kinds.STAR); return Kinds.STAR; } @Override public Type convertMetaVarsToVars() { Type newDomain = domain.convertMetaVarsToVars(); Type newEffect = effect.convertMetaVarsToVars(); Type newRange = range.convertMetaVarsToVars(); if(newDomain == domain && newEffect == effect && newRange == range) return this; else return new TFun(newDomain, newEffect, newRange); } @Override public boolean isMinimal() { return Types.canonical(effect) == Types.NO_EFFECTS && range.isMinimal() && domain.isMaximal(); } public boolean isMaximal() { return false; } @Override public void addPolarity(Polarity polarity) { domain.addPolarity(polarity.flip()); effect.addPolarity(polarity); range.addPolarity(polarity); } @Override public Type head() { return Types.ARROW; } @Override public Type copySkeleton(THashMap metaVarMap) { Type newDomain = domain.copySkeleton(metaVarMap); Type newEffect = Types.NO_EFFECTS; Type newRange = range.copySkeleton(metaVarMap); return new TFun(newDomain, newEffect, newRange); } @Override public int hashCode(int hash) { hash = HashCodeUtils.updateWithPreprocessedValue(hash, FUN_HASH); hash = domain.hashCode(hash); hash = effect.hashCode(hash); hash = range.hashCode(hash); return hash; } @Override public int hashCode(int hash, TVar[] boundVars) { hash = HashCodeUtils.updateWithPreprocessedValue(hash, FUN_HASH); hash = domain.hashCode(hash, boundVars); hash = effect.hashCode(hash, boundVars); hash = range.hashCode(hash, boundVars); return hash; } @Override public int skeletonHashCode(int hash) { hash = HashCodeUtils.updateWithPreprocessedValue(hash, FUN_HASH); hash = domain.skeletonHashCode(hash); hash = range.skeletonHashCode(hash); return hash; } @Override public int skeletonHashCode(int hash, TVar[] boundVars) { hash = HashCodeUtils.updateWithPreprocessedValue(hash, FUN_HASH); hash = domain.skeletonHashCode(hash, boundVars); hash = range.skeletonHashCode(hash, boundVars); return hash; } public Type getCanonicalDomain() { if(domain instanceof TMetaVar) domain = domain.canonical(); return domain; } public Type getCanonicalEffect() { if(effect instanceof TMetaVar) effect = effect.canonical(); return effect; } public Type getCanonicalRange() { if(range instanceof TMetaVar) range = range.canonical(); return range; } @Override public boolean equalsCanonical(Type other) { if(this == other) return true; if(!other.getClass().equals(TFun.class)) return false; TFun fun = (TFun)other; return getCanonicalDomain().equalsCanonical(fun.getCanonicalDomain()) && getCanonicalEffect().equalsCanonical(fun.getCanonicalEffect()) && getCanonicalRange().equalsCanonical(fun.getCanonicalRange()); } @Override public Kind getKind(Environment context) { return Kinds.STAR; } @Override public Type[] skeletonCanonicalChildren() { return new Type[] {Skeletons.canonicalSkeleton(domain), Skeletons.canonicalSkeleton(range)}; } }