/*
* Copyright 2013-2016 Sergey Ignatov, Alexander Zolotov, Florin Patan
*
* 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 com.goide.psi.impl;
import com.goide.psi.*;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Trinity;
import com.intellij.psi.PsiElement;
import com.intellij.psi.impl.source.tree.LeafElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class GoExpressionUtil {
public static boolean identical(@Nullable GoExpression left, @Nullable GoExpression right) {
if (left == right) return true;
if (left == null || right == null) return false;
GoExpression l = unwrap(left);
GoExpression r = unwrap(right);
if (l == null || r == null) return false;
if (!l.getClass().equals(r.getClass())) return false;
if (l instanceof GoBinaryExpr) {
GoBinaryExpr lBin = (GoBinaryExpr)l;
GoBinaryExpr rBin = (GoBinaryExpr)r;
return isOperatorEquals(lBin.getOperator(), rBin.getOperator()) && isChildrenExprEquals(lBin, rBin);
}
if (l instanceof GoUnaryExpr) {
GoUnaryExpr lUnary = (GoUnaryExpr)l;
GoUnaryExpr rUnary = (GoUnaryExpr)r;
return isOperatorEquals(lUnary.getOperator(), rUnary.getOperator()) && identical(lUnary.getExpression(), rUnary.getExpression());
}
if (l instanceof GoReferenceExpression) {
PsiElement resolve = ((GoReferenceExpression)l).resolve();
return resolve != null && resolve.isEquivalentTo(((GoReferenceExpression)r).resolve());
}
if (l instanceof GoIndexOrSliceExpr) {
GoIndexOrSliceExpr lSlice = (GoIndexOrSliceExpr)l;
GoIndexOrSliceExpr rSlice = (GoIndexOrSliceExpr)r;
return identical(lSlice.getExpression(), rSlice.getExpression()) && isIndicesIdentical(lSlice.getIndices(), rSlice.getIndices());
}
if (l instanceof GoStringLiteral) {
return Comparing.equal(((GoStringLiteral)l).getDecodedText(), ((GoStringLiteral)r).getDecodedText());
}
if (l instanceof GoLiteralTypeExpr) {
GoLiteralTypeExpr lLit = (GoLiteralTypeExpr)l;
GoLiteralTypeExpr rLit = (GoLiteralTypeExpr)r;
GoTypeReferenceExpression lExpr = lLit.getTypeReferenceExpression();
GoTypeReferenceExpression rExpr = rLit.getTypeReferenceExpression();
PsiElement lResolve = lExpr != null ? lExpr.resolve() : null;
return lResolve != null && rExpr != null && lResolve.equals(rExpr.resolve());
//todo: add || GoTypeUtil.identical(lLit.getType(), rLit.getType)
}
if (l instanceof GoLiteral) {
return l.textMatches(r);
}
if (l instanceof GoBuiltinCallExpr || l instanceof GoCallExpr) {
return false;
}
if (l instanceof GoCompositeLit) {
GoCompositeLit lLit = (GoCompositeLit)l;
GoCompositeLit rLit = (GoCompositeLit)r;
return identical(lLit.getLiteralValue(), rLit.getLiteralValue());
// todo: add && GoTypeUtil.identical
}
if (l instanceof GoTypeAssertionExpr) {
GoTypeAssertionExpr lAssertion = (GoTypeAssertionExpr)l;
GoTypeAssertionExpr rAssertion = (GoTypeAssertionExpr)r;
return identical(lAssertion.getExpression(), rAssertion.getExpression());
//todo: add && GoTypeUtil.identical(lAssertion.getType(), rAssertion.getType())
}
String lText = l.getText();
return lText != null && lText.equals(r.getText());
}
private static boolean isIndicesIdentical(@NotNull Trinity<GoExpression, GoExpression, GoExpression> l,
@NotNull Trinity<GoExpression, GoExpression, GoExpression> r) {
return identical(l.first, r.first) && identical(l.second, r.second) && identical(l.third, r.third);
}
private static boolean identical(@Nullable GoLiteralValue l, @Nullable GoLiteralValue r) {
if (l == null || r == null) return false;
return l.textMatches(r); //todo: fill
}
private static boolean isOperatorEquals(@Nullable PsiElement l, @Nullable PsiElement r) {
if (l == null || r == null) return false;
ASTNode lNode = l.getNode();
ASTNode rNode = r.getNode();
return lNode instanceof LeafElement && lNode.getElementType().equals(rNode.getElementType());
}
private static boolean isOrderImportant(@NotNull GoBinaryExpr o) {
if (o instanceof GoConversionExpr || o instanceof GoSelectorExpr) return true;
if (o instanceof GoMulExpr) {
GoMulExpr m = (GoMulExpr)o;
return m.getRemainder() != null || m.getQuotient() != null || m.getShiftLeft() != null || m.getShiftRight() != null;
}
if (o instanceof GoConditionalExpr) {
GoConditionalExpr c = (GoConditionalExpr)o;
return c.getEq() == null && c.getNotEq() == null;
}
return false;
}
private static boolean isChildrenExprEquals(@NotNull GoBinaryExpr left, @NotNull GoBinaryExpr right) {
GoExpression l1 = left.getLeft();
GoExpression l2 = left.getRight();
GoExpression r1 = right.getLeft();
GoExpression r2 = right.getRight();
boolean order = isOrderImportant(left);
return identical(l1, r1) && identical(l2, r2) || !order && identical(l1, r2) && identical(l2, r1);
}
@Nullable
private static GoExpression unwrap(@Nullable GoExpression o) {
if (o instanceof GoParenthesesExpr) return unwrap(((GoParenthesesExpr)o).getExpression());
if (o instanceof GoUnaryExpr && ((GoUnaryExpr)o).getPlus() != null) return unwrap(((GoUnaryExpr)o).getExpression());
return o;
}
}