/* * Copyright 2003-2011 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 jetbrains.mps.lang.pattern.util.IMatchModifier; import jetbrains.mps.lang.pattern.util.MatchingUtil; import jetbrains.mps.lang.smodel.generator.smodelAdapter.SNodeOperations; import jetbrains.mps.lang.typesystem.runtime.HUtil; import jetbrains.mps.newTypesystem.state.Equations; import jetbrains.mps.newTypesystem.state.State; import jetbrains.mps.smodel.SModelUtil_new; import jetbrains.mps.smodel.SNodeUtil; import jetbrains.mps.typesystem.inference.EquationInfo; import jetbrains.mps.typesystemEngine.util.LatticeUtil; import jetbrains.mps.util.Pair; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.mps.openapi.model.SNode; import org.jetbrains.mps.openapi.model.SReference; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Set; public class TypesUtil { public static boolean isVariable(SNode node) { return HUtil.isRuntimeTypeVariable(node); } public static boolean canBeVariable(SNode node) { return isVariable(node) || LatticeUtil.isMeet(node) && hasVariablesInside(node); } public static boolean hasVariablesInside(SNode node) { if (node == null) { return false; } if (TypesUtil.isVariable(node)) { return true; } for (SNode child : node.getChildren()) { if (TypesUtil.hasVariablesInside(child)) { return true; } } for (SNode referent : getNodeReferents(node)) { if (referent != null && TypesUtil.isVariable(referent)) { return true; } } return false; } public static int depth(SNode sNode) { int childDepth = 0; for (SNode child : sNode.getChildren()) { int depth = depth(child); if (childDepth < depth) { childDepth = depth; } } if (!SNodeOperations.isInstanceOf(sNode, SNodeUtil.concept_SNodeType)) return childDepth + 1; if (sNode.getReference(SNodeUtil.ref_SNodeType_concept) == null) return childDepth + 1; return childDepth + 2; } public static List<SNode> getVariables(SNode node, State state) { List<SNode> result = new LinkedList<SNode>(); getVariablesInside(node, result, state); return result; } private static void getVariablesInside(SNode node, List<SNode> result, State state) { if (state != null) { node = state.getRepresentative(node); } if (node == null) { return; } if (isVariable(node)) { result.add(node); return; } for (SNode child : node.getChildren()) { getVariablesInside(child, result, state); } for (SNode referent : getNodeReferents(node)) { if (state != null) { referent = state.getRepresentative(referent); } if (referent != null && isVariable(referent)) { result.add(referent); } } } @NotNull public static List<SNode> getNodeReferents(@NotNull SNode node) { if (node == null) return Collections.emptyList(); final List<SNode> result = new ArrayList<SNode>(); for (SReference ref : node.getReferences()) { result.add(ref.getTargetNode()); } return result; } public static boolean match(SNode left, SNode right) { return match(left, right, null); } public static boolean match(SNode left, SNode right, /*out*/ Collection<Pair<SNode, SNode>> matchingPairs) { if (left == right) return true; if (left == null || right == null) return false; if (TypesUtil.isVariable(left) || TypesUtil.isVariable(right)) { if (matchingPairs != null) { matchingPairs.add(new Pair<SNode, SNode>(left, right)); } return true; } MatchingNodesCollector matchingNodesCollector = new MatchingNodesCollector(); boolean match = MatchingUtil.matchNodes(left, right, matchingNodesCollector, false); if (match && matchingPairs != null) { matchingPairs.addAll(matchingNodesCollector.myMatchingPairs); } return match; } public static boolean matchExpandingJoinAndMeet(SNode left, SNode right, /*out*/ Collection<Pair<SNode, SNode>> matchingPairs) { if (match(left, right, matchingPairs)) return true; if (LatticeUtil.isJoin(left)) { for (SNode arg : LatticeUtil.getJoinArguments(left)) { if (match(arg, right, matchingPairs)) return true; } } if (LatticeUtil.isMeet(left)) { for (SNode arg : LatticeUtil.getMeetArguments(left)) { if (!match(arg, right, matchingPairs)) return false; } return true; } if (LatticeUtil.isJoin(right)) { for (SNode arg : LatticeUtil.getJoinArguments(right)) { if (match(left, arg, matchingPairs)) return true; } } if (LatticeUtil.isMeet(right)) { for (SNode arg : LatticeUtil.getMeetArguments(right)) { if (!match(left, arg, matchingPairs)) return false; } return true; } return false; } @Deprecated public static boolean match(SNode left, SNode right, Equations equations, @Nullable EquationInfo info) { THashSet<Pair<SNode, SNode>> matchingPairs = new THashSet<Pair<SNode, SNode>>(); boolean match = match(left, right, matchingPairs); if (match && equations != null) { equations.addEquations(matchingPairs, info); } return match; } public static SNode cleanupMeet(SNode type) { // Dirty hack to avoid meet type to appear inside fully reified type Set<SNode> newArgs = new THashSet<SNode>(); final List<SNode> arguments = LatticeUtil.getMeetArguments(type); boolean addTheRest = false; for (SNode arg : arguments) { if (arg != null && (addTheRest || !SNodeOperations.isInstanceOf(arg, SNodeUtil.concept_VoidType))) { newArgs.add(arg); } else { addTheRest = true; } } if (newArgs.size() != arguments.size()) { type = LatticeUtil.meetNodes(newArgs); } return type; } private static class MatchingNodesCollector implements IMatchModifier { private final Set<Pair<SNode, SNode>> myMatchingPairs = new THashSet<Pair<SNode, SNode>>(); @Override public boolean accept(SNode node1, SNode node2) { return TypesUtil.isVariable(node1) || TypesUtil.isVariable(node2); } @Override public boolean acceptList(List<SNode> nodes1, List<SNode> nodes2) { return false; } @Override public void performAction(SNode node1, SNode node2) { if (myMatchingPairs != null) { myMatchingPairs.add(new Pair<SNode, SNode>(node1, node2)); } } @Override public void performGroupAction(List<SNode> nodes1, List<SNode> nodes2) { } } public static SNode createRuntimeErrorType() { return SModelUtil_new.instantiateConceptDeclaration(SNodeUtil.concept_RuntimeErrorType, null, null, false); } }