/* * Copyright 2003-2012 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jetbrains.mps.newTypesystem; import gnu.trove.THashSet; import org.jetbrains.mps.openapi.model.SNode; import jetbrains.mps.typesystem.inference.TypeChecker; import jetbrains.mps.typesystem.inference.TypeCheckingContext; import jetbrains.mps.typesystem.inference.util.StructuralNodeSet; import jetbrains.mps.typesystemEngine.util.LatticeUtil; import java.util.*; /** * User: fyodor * Date: 9/4/12 */ public class SubtypingUtil { public static Set<SNode> mostSpecificTypes(Set<SNode> nodes) { Set<SNode> residualNodes = new THashSet<SNode>(nodes); while (residualNodes.size() > 1) { List<SNode> nodesToIterate = new ArrayList<SNode>(residualNodes); Collections.sort(nodesToIterate, new Comparator<SNode>() { @Override public int compare(SNode o1, SNode o2) { return TypesUtil.depth(o2) - TypesUtil.depth(o1); } }); boolean wasChange = false; int size = nodesToIterate.size(); for (int i = 0; i < size; i++) { for (int j = i + 1; j < size; j++) { SNode node1 = nodesToIterate.get(i); SNode node2 = nodesToIterate.get(j); if (node1 == null || node2 == null) { residualNodes.remove(null); } if (TypeChecker.getInstance().getSubtypingManager().isSubtype(node1, node2)) { residualNodes.remove(node2); wasChange = true; } else if (TypeChecker.getInstance().getSubtypingManager().isSubtype(node2, node1)) { residualNodes.remove(node1); wasChange = true; } } } if (!wasChange) { break; } } return residualNodes; } public static List<SNode> eliminateSuperTypes(Collection<SNode> types) { return new RelationEliminator() { @Override boolean inRelation(SNode left, SNode right) { return TypeChecker.getInstance().getSubtypingManager().isSubtype(right, left, true); } }.doEliminate(types); } public static List<SNode> eliminateSubTypes(Collection<SNode> types) { return new RelationEliminator() { @Override boolean inRelation(SNode left, SNode right) { return TypeChecker.getInstance().getSubtypingManager().isSubtype(left, right, true); } }.doEliminate(types); } public static SNode createLeastCommonSupertype(List<SNode> types, TypeCheckingContext context) { if (types.isEmpty()) return null; if (types.size() == 1) return types.iterator().next(); if (types.size() > 1) { Collections.sort(types, new Comparator<SNode>() { @Override public int compare(SNode node1, SNode node2) { return node1.getPresentation().compareTo(node2.getPresentation()); } }); types = SubtypingUtil.eliminateSubTypes(types); } return LatticeUtil.meetNodes(new THashSet<SNode>(SubtypingUtil.leastCommonSuperTypes(types, context))); } public static List<SNode> leastCommonSuperTypes(List<SNode> types, TypeCheckingContext context) { if (types.size() == 0) { return null; } if (types.size () == 1) { return types; } types = SubtypingUtil.eliminateSubTypes(types); int newNodesSize = 1; while (types.size() > newNodesSize) { if (types.size() == 1) { return types; } if (types.size() == 0) { return null; } SNode left = types.remove(0); SNode right = types.remove(0); List<SNode> newNodes = SubtypingUtil.eliminateSuperTypes(SubtypingUtil.leastCommonSuperTypes(left, right, context)); newNodesSize = newNodes.size(); types.addAll(newNodes); } return SubtypingUtil.eliminateSuperTypes(types); } private static Set<SNode> leastCommonSuperTypes(SNode left, SNode right, TypeCheckingContext context) { StructuralNodeSet<?> frontier = new StructuralNodeSet(); StructuralNodeSet<?> newFrontier = new StructuralNodeSet(); StructuralNodeSet<?> yetPassed = new StructuralNodeSet(); Set<SNode> result = new THashSet<SNode>(); frontier.add(left); while (!frontier.isEmpty()) { Set<SNode> yetPassedRaw = new THashSet<SNode>(); //collecting a set of frontier's ancestors StructuralNodeSet<?> ancestors = new StructuralNodeSet(); for (SNode node : frontier) { TypeChecker.getInstance().getSubtypingManager().collectImmediateSuperTypes(node, true, ancestors, context); if (!yetPassed.contains(node)) { ancestors.add(node); } yetPassedRaw.add(node); } for (SNode ancestor : ancestors) { if (TypeChecker.getInstance().getSubtypingManager().isSubtype(right, ancestor, true)) { if (!TypeChecker.getInstance().getSubtypingManager().isSuperType(ancestor, result)) { result.add(ancestor); } } } for (SNode passedNodeRaw : yetPassedRaw) { yetPassed.add(passedNodeRaw); } for (SNode passedNode : yetPassed) { ancestors.removeStructurally(passedNode); } for (SNode resultNode : result) { ancestors.removeStructurally(resultNode); } newFrontier.addAllStructurally(ancestors); yetPassed.addAllStructurally(ancestors); frontier = newFrontier; newFrontier = new StructuralNodeSet(); } return result; } abstract private static class RelationEliminator { abstract boolean inRelation(SNode left, SNode right); public List<SNode> doEliminate (Collection<SNode> types) { List<SNode> result = new LinkedList<SNode>(); Set<SNode> toRemove = new THashSet<SNode>(); for (SNode current : types) { boolean toAdd = true; for (SNode resultType : result) { if (inRelation(current, resultType)) { toAdd = false; break; } if (inRelation(resultType, current)) { toRemove.add(resultType); } } if (toAdd) { result.add(current); } for (SNode removeType : toRemove) { result.remove(removeType); } } return result; } } }