/*
* 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.state.blocks;
import gnu.trove.THashSet;
import jetbrains.mps.lang.typesystem.runtime.AbstractInequationReplacementRule_Runtime;
import jetbrains.mps.lang.typesystem.runtime.InequationReplacementRule_Runtime;
import jetbrains.mps.lang.typesystem.runtime.IsApplicable2Status;
import jetbrains.mps.newTypesystem.SubTypingManagerNew;
import jetbrains.mps.newTypesystem.SubtypingResolver;
import jetbrains.mps.newTypesystem.TypesUtil;
import jetbrains.mps.newTypesystem.operation.AddRemarkOperation;
import jetbrains.mps.newTypesystem.operation.CheckSubTypeOperation;
import jetbrains.mps.newTypesystem.operation.ProcessReplacementRuleOperation;
import jetbrains.mps.languageScope.LanguageScopeExecutor;
import jetbrains.mps.newTypesystem.state.Equations;
import jetbrains.mps.newTypesystem.state.State;
import org.jetbrains.mps.openapi.model.SNode;
import jetbrains.mps.typesystem.inference.EquationInfo;
import jetbrains.mps.typesystem.inference.TypeChecker;
import jetbrains.mps.typesystemEngine.util.LatticeUtil;
import jetbrains.mps.util.CollectionUtil;
import jetbrains.mps.util.Computable;
import jetbrains.mps.util.Pair;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
public class InequalityBlock extends RelationBlock {
private boolean lessThan;
public SNode getOutput() {
if (lessThan) {
return myLeftNode;
} else {
return myRightNode;
}
}
public SNode getInput() {
if (isCheckOnly()) {
return null;
}
if (lessThan) {
return myRightNode;
} else {
return myLeftNode;
}
}
@Override
public List<Pair<SNode, SNode>> getInputsAndOutputs() {
List<Pair<SNode, SNode>> result = new LinkedList<Pair<SNode, SNode>>();
result.add(new Pair<SNode, SNode>(getInput(), getOutput()));
return result;
}
public InequalityBlock(State state, SNode left, SNode right, boolean lessThen, RelationKind kind, EquationInfo equationInfo) {
super(state, left, right, kind, equationInfo);
this.lessThan = lessThen;
}
@Override
public void performAction() {
SNode left = getResolvedInput(myLeftNode);
SNode right = getResolvedInput(myRightNode);
processInequality(left, right);
}
private boolean processReplacementRules(final SNode subType, final SNode superType) {
final TypeChecker typeChecker = TypeChecker.getInstance();
List<Pair<InequationReplacementRule_Runtime, IsApplicable2Status>> replacementRules =
LanguageScopeExecutor.execWithMultiLanguageScope(
SubTypingManagerNew.collectLanguagesRecursively(subType, superType),
new Computable<List<Pair<InequationReplacementRule_Runtime, IsApplicable2Status>>>() {
@Override
public List<Pair<InequationReplacementRule_Runtime, IsApplicable2Status>> compute() {
return typeChecker.getRulesManager().getReplacementRules(subType, superType);
}
});
for (jetbrains.mps.util.Pair<InequationReplacementRule_Runtime, IsApplicable2Status> inequalityReplacementRule : replacementRules) {
final InequationReplacementRule_Runtime rule = inequalityReplacementRule.o1;
final IsApplicable2Status status = inequalityReplacementRule.o2;
getState().executeOperation(new ProcessReplacementRuleOperation(subType, superType, new Runnable() {
@Override
public void run() {
((AbstractInequationReplacementRule_Runtime) rule).processInequation(subType, superType, myEquationInfo, getState().getTypeCheckingContext(), status, myRelationKind.isWeak(), lessThan);
}
}));
return true;
}
return false;
}
private void processInequality(final SNode subType, final SNode superType) {
if (subType == null || superType == null || subType == superType) {
return;
}
if (!TypesUtil.hasVariablesInside(subType) && !TypesUtil.hasVariablesInside(superType)){
//no need to check if we don't need error reporting
if (getState().getTypeCheckingContext().isSingleTypeComputation()) return;
if (TypesUtil.match(subType, superType)) {
getState().executeOperation(new AddRemarkOperation("Matched: " + subType + " and " + superType));
return;
}
}
if (processReplacementRules(subType, superType)) {
return;
}
final SubTypingManagerNew subTyping = (SubTypingManagerNew) TypeChecker.getInstance().getSubtypingManager();
getState().executeOperation(new CheckSubTypeOperation(subType, superType, new Runnable() {
@Override
public void run() {
if (!calcIsSubtype(subTyping, subType, superType)) {
getState().getNodeMaps().reportSubTypeError(subType, superType, myEquationInfo, myRelationKind.isWeak());
}
}
}));
}
private boolean calcIsSubtype(SubTypingManagerNew subTyping, SNode subType, SNode superType) {
THashSet<Pair<SNode, SNode>> matchingPairs = new THashSet<Pair<SNode, SNode>>();
SubtypingResolver subtypingResolver = new SubtypingResolver(myRelationKind.isWeak(), getState().getTypeCheckingContext(), matchingPairs);
boolean result = subtypingResolver.calcIsSubType(subType, superType);
if (result) {
Equations equations = getState().getEquations();
if (equations != null) {
equations.addEquations(matchingPairs, myEquationInfo);
}
}
return result;
}
@Override
public Set<Pair<SNode, ConditionKind>> getInitialInputs() {
if (isCheckOnly()) {
return CollectionUtil.set(new Pair<SNode, ConditionKind>(myLeftNode, ConditionKind.CONCRETE),
new Pair<SNode, ConditionKind>(myRightNode, ConditionKind.CONCRETE));
} else {
//hack
ConditionKind left = ConditionKind.SHALLOW;
if (LatticeUtil.isPolymorphic(myLeftNode)) {
left = ConditionKind.CONCRETE;
}
ConditionKind right = ConditionKind.SHALLOW;
if (LatticeUtil.isPolymorphic(myRightNode)) {
right = ConditionKind.CONCRETE;
}
return CollectionUtil.set(new Pair<SNode, ConditionKind>(myLeftNode, left),
new Pair<SNode, ConditionKind>(myRightNode, right));
}
}
@Override
public BlockKind getBlockKind() {
return BlockKind.INEQUALITY;
}
private String getPresentationInternal(SNode left, SNode right) {
if (lessThan) {
return left + myRelationKind.getRelationSign() + right;
} else {
return right + myRelationKind.getReversedRelationSign() + left;
}
}
@Override
public String getShortPresentation() {
return getPresentationInternal(myLeftNode, myRightNode);
}
@Override
public String getExpandedPresentation(State state) {
return getPresentationInternal(state.expand(myLeftNode), state.expand(myRightNode));
}
}