Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix the legendary compare issue with two same literals #5503

Merged
merged 34 commits into from
Jun 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
541ab17
Implement a fix for the famous literal comparing issue
TheLimeGlass Mar 9, 2023
1d10b81
Clean up and get ready for pull request
TheLimeGlass Mar 10, 2023
d95b00a
Clean up and get ready for pull request
TheLimeGlass Mar 10, 2023
009f5c2
Add more regression tests for more issues
TheLimeGlass Mar 10, 2023
efdcb0f
Add more regression tests for more issues
TheLimeGlass Mar 10, 2023
ebdc88d
Add more regression tests for more issues
TheLimeGlass Mar 10, 2023
ce35143
Merge branch 'master' into fix/convert-compare
TheLimeGlass Mar 10, 2023
a2f535f
override wrong .sk test file
TheLimeGlass Mar 10, 2023
a4c6fad
Merge branch 'master' into fix/convert-compare
Moderocky Apr 14, 2023
f679728
Update src/main/java/ch/njol/skript/conditions/CondCompare.java
TheLimeGlass Apr 14, 2023
67d701d
Merge branch 'master' into fix/convert-compare
TheLimeGlass Apr 17, 2023
748d325
Update src/test/skript/tests/regressions/2711-item-compares.sk
TheLimeGlass Apr 25, 2023
76340ce
Update src/test/skript/tests/regressions/4769-fire-visualeffect.sk
TheLimeGlass Apr 25, 2023
71f3da0
Added tests wern't properly setting back block
TheLimeGlass Apr 26, 2023
5f520c6
Added tests wern't properly setting back block
TheLimeGlass Apr 26, 2023
9c72c72
Apply changes
TheLimeGlass Apr 26, 2023
b9c9c93
Update src/test/skript/tests/regressions/pull-5566-block iterator bei…
TheLimeGlass May 20, 2023
ad841ce
Update src/test/skript/tests/regressions/3303-item durability.sk
TheLimeGlass May 20, 2023
f9537d7
Update src/main/java/ch/njol/skript/test/runner/EvtTestCase.java
TheLimeGlass May 20, 2023
cf77032
Update src/main/java/ch/njol/skript/test/runner/EvtTestCase.java
TheLimeGlass May 20, 2023
9bab3aa
Apply changes
TheLimeGlass May 22, 2023
a390a75
Merge branch 'master' into fix/convert-compare
TheLimeGlass May 22, 2023
e6e0dbc
Update src/main/java/ch/njol/skript/conditions/CondCompare.java
TheLimeGlass May 26, 2023
976ed97
Apply changes
TheLimeGlass May 26, 2023
ea6ce6c
Apply changes
TheLimeGlass May 26, 2023
e8a7674
Merge branch 'master' into fix/convert-compare
TheLimeGlass May 30, 2023
7482db7
Change compare gathering
TheLimeGlass May 30, 2023
2bf7325
Merge branch 'fix/convert-compare' of https://github.com/SkriptLang/S…
TheLimeGlass May 30, 2023
e4fd0fa
Grammar in test
TheLimeGlass May 30, 2023
bc8e7ea
Revert block data version changes
TheLimeGlass May 30, 2023
084da81
Lower block data test to 1.14.4
TheLimeGlass May 30, 2023
81241ce
Merge branch 'master' into fix/convert-compare
TheLimeGlass Jun 2, 2023
c0d4068
Change separator
TheLimeGlass Jun 2, 2023
0464f8b
Remove test line from test
TheLimeGlass Jun 2, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
169 changes: 104 additions & 65 deletions src/main/java/ch/njol/skript/conditions/CondCompare.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,22 @@
import org.eclipse.jdt.annotation.Nullable;

import ch.njol.skript.Skript;
import ch.njol.skript.classes.ClassInfo;

import org.skriptlang.skript.lang.comparator.Comparator;
import org.skriptlang.skript.lang.comparator.ComparatorInfo;
import org.skriptlang.skript.lang.comparator.Relation;
import org.skriptlang.skript.lang.converter.ConverterInfo;
import org.skriptlang.skript.lang.converter.Converters;

import ch.njol.skript.doc.Description;
import ch.njol.skript.doc.Examples;
import ch.njol.skript.doc.Name;
import ch.njol.skript.doc.Since;
import ch.njol.skript.lang.Condition;
import ch.njol.skript.lang.Expression;
import ch.njol.skript.lang.ExpressionList;
import ch.njol.skript.lang.Literal;
import ch.njol.skript.lang.ParseContext;
import ch.njol.skript.lang.SkriptParser.ParseResult;
import ch.njol.skript.lang.UnparsedLiteral;
Expand All @@ -39,15 +46,13 @@
import ch.njol.skript.log.RetainingLogHandler;
import ch.njol.skript.log.SkriptLogger;
import ch.njol.skript.registrations.Classes;

import org.skriptlang.skript.lang.comparator.Comparators;
import ch.njol.skript.util.Patterns;
import ch.njol.skript.util.Utils;
import ch.njol.util.Checker;
import ch.njol.util.Kleenean;

/**
* @author Peter Güttinger
*/
@Name("Comparison")
@Description({"A very general condition, it simply compares two values. Usually you can only compare for equality (e.g. block is/isn't of <type>), " +
"but some values can also be compared using greater than/less than. In that case you can also test for whether an object is between two others.",
Expand Down Expand Up @@ -93,19 +98,18 @@ public class CondCompare extends Condition {
Skript.registerCondition(CondCompare.class, patterns.getPatterns());
}

@SuppressWarnings("null")
private Expression<?> first;
@SuppressWarnings("null")
private Expression<?> second;

@Nullable
private Expression<?> third;
@SuppressWarnings("null")

private Relation relation;
@SuppressWarnings("rawtypes")

@Nullable
private Comparator comp;
@SuppressWarnings("rawtypes")
private Comparator comparator;

@SuppressWarnings("null")
@Override
public boolean init(final Expression<?>[] vars, final int matchedPattern, final Kleenean isDelayed, final ParseResult parser) {
first = vars[0];
Expand Down Expand Up @@ -134,7 +138,7 @@ public boolean init(final Expression<?>[] vars, final int matchedPattern, final
}
}
@SuppressWarnings("rawtypes")
final Comparator comp = this.comp;
final Comparator comp = this.comparator;
if (comp != null) {
if (third == null) {
if (!relation.isImpliedBy(Relation.EQUAL, Relation.NOT_EQUAL) && !comp.supportsOrdering()) {
Expand All @@ -158,60 +162,59 @@ public static String f(final Expression<?> e) {
return Classes.getSuperClassInfo(e.getReturnType()).getName().withIndefiniteArticle();
}

@SuppressWarnings({"unchecked"})
@SuppressWarnings("unchecked")
private boolean init(String expr) {
final RetainingLogHandler log = SkriptLogger.startRetainingLog();
RetainingLogHandler log = SkriptLogger.startRetainingLog();
Expression<?> third = this.third;
try {
if (first.getReturnType() == Object.class) {
final Expression<?> e = first.getConvertedExpression(Object.class);
if (e == null) {
Expression<?> expression = null;
if (first instanceof UnparsedLiteral)
expression = attemptReconstruction((UnparsedLiteral) first, second);
if (expression == null)
expression = first.getConvertedExpression(Object.class);
if (expression == null) {
log.printErrors();
return false;
}
first = e;
first = expression;
}
if (second.getReturnType() == Object.class) {
final Expression<?> e = second.getConvertedExpression(Object.class);
if (e == null) {
Expression<?> expression = null;
if (second instanceof UnparsedLiteral)
expression = attemptReconstruction((UnparsedLiteral) second, first);
if (expression == null)
expression = second.getConvertedExpression(Object.class);
if (expression == null) {
log.printErrors();
return false;
}
second = e;
second = expression;
}
if (third != null && third.getReturnType() == Object.class) {
final Expression<?> e = third.getConvertedExpression(Object.class);
if (e == null) {
Expression<?> expression = null;
if (third instanceof UnparsedLiteral)
expression = attemptReconstruction((UnparsedLiteral) third, first);
if (expression == null)
expression = third.getConvertedExpression(Object.class);
if (expression == null) {
log.printErrors();
return false;
}
this.third = third = e;
this.third = third = expression;
}
log.printLog();
} finally {
log.stop();
}
final Class<?> f = first.getReturnType(), s = third == null ? second.getReturnType() : Utils.getSuperType(second.getReturnType(), third.getReturnType());
if (f == Object.class || s == Object.class)
Class<?> firstReturnType = first.getReturnType();
Class<?> secondReturnType = third == null ? second.getReturnType() : Utils.getSuperType(second.getReturnType(), third.getReturnType());
if (firstReturnType == Object.class || secondReturnType == Object.class)
return true;
/*
* https://github.com/Mirreski/Skript/issues/10
*
if(Entity.class.isAssignableFrom(s)){
String[] split = expr.split(" ");
System.out.println(expr);
if(!split[split.length - 1].equalsIgnoreCase("player") && EntityData.parseWithoutIndefiniteArticle(split[split.length - 1]) != null){
comp = Comparators.getComparator(f, EntityData.class);
second = SkriptParser.parseLiteral(split[split.length - 1], EntityData.class, ParseContext.DEFAULT);
}else
comp = Comparators.getComparator(f, s);

}
*///else

comp = Comparators.getComparator(f, s);

if (comp == null) { // Try to re-parse with more context

comparator = Comparators.getComparator(firstReturnType, secondReturnType);

if (comparator == null) { // Try to re-parse with more context
/*
* SkriptParser sees that CondCompare takes two objects. Most of the time,
* this works fine. However, when there are multiple conflicting literals,
Expand All @@ -224,23 +227,23 @@ private boolean init(String expr) {
* Some damage types not working (issue #2184) would be a good example
* of issues that SkriptParser's lack of context can cause.
*/
SimpleLiteral<?> reparsedSecond = reparseLiteral(first.getReturnType(), second);
SimpleLiteral<?> reparsedSecond = reparseLiteral(firstReturnType, second);
if (reparsedSecond != null) {
second = reparsedSecond;
comp = Comparators.getComparator(f, second.getReturnType());
comparator = Comparators.getComparator(firstReturnType, second.getReturnType());
} else {
SimpleLiteral<?> reparsedFirst = reparseLiteral(second.getReturnType(), first);
if (reparsedFirst != null) {
first = reparsedFirst;
comp = Comparators.getComparator(first.getReturnType(), s);
comparator = Comparators.getComparator(first.getReturnType(), secondReturnType);
}
}

}
return comp != null;

return comparator != null;
}

/**
* Attempts to parse given expression again as a literal of given type.
* This will only work if the expression is a literal and its unparsed
Expand All @@ -251,22 +254,58 @@ private boolean init(String expr) {
* @return A literal value, or null if parsing failed.
*/
@Nullable
private <T> SimpleLiteral<T> reparseLiteral(Class<T> type, Expression<?> expr) {
if (expr instanceof SimpleLiteral) { // Only works for simple literals
Expression<?> source = expr.getSource();

// Try to get access to unparsed content of it
if (source instanceof UnparsedLiteral) {
String unparsed = ((UnparsedLiteral) source).getData();
T data = Classes.parse(unparsed, type, ParseContext.DEFAULT);
if (data != null) { // Success, let's make a literal of it
return new SimpleLiteral<>(data, false, new UnparsedLiteral(unparsed));
}
private <T> SimpleLiteral<T> reparseLiteral(Class<T> type, Expression<?> expression) {
Expression<?> source = expression;
if (expression instanceof SimpleLiteral) // Only works for simple literals
source = expression.getSource();

// Try to get access to unparsed content of it
if (source instanceof UnparsedLiteral) {
String unparsed = ((UnparsedLiteral) source).getData();
T data = Classes.parse(unparsed, type, ParseContext.DEFAULT);
if (data != null) { // Success, let's make a literal of it
return new SimpleLiteral<>(data, false, new UnparsedLiteral(unparsed));
}
}
return null; // Context-sensitive parsing failed; can't really help it
}


/**
* Attempts to transform an UnparsedLiteral into a type that is comparable to another.
* For example 'fire' will be VisualEffect without this, but if the user attempts to compare 'fire'
* with a block. This method will see if 'fire' can be compared to the block, and it will find ItemType.
* Essentially solving something a human sees as comparable, but Skript doesn't understand.
*
* @param one The UnparsedLiteral expression to attempt to reconstruct.
* @param two any expression to grab the return type from.
* @return The newly formed Literal, will be SimpleLiteral in most cases.
*/
@SuppressWarnings("unchecked")
private Literal<?> attemptReconstruction(UnparsedLiteral one, Expression<?> two) {
Expression<?> expression = null;
// Must handle numbers first.
expression = one.getConvertedExpression(Number.class);
TheLimeGlass marked this conversation as resolved.
Show resolved Hide resolved
if (expression == null) {
for (ClassInfo<?> classinfo : Classes.getClassInfos()) {
if (classinfo.getParser() == null)
continue;
ComparatorInfo<?, ?> comparator = Comparators.getComparatorInfo(two.getReturnType(), classinfo.getC());
if (comparator == null)
continue;
// We don't care about comparators that take in an object. This just causes more issues accepting and increases iterations by half.
// Let getConvertedExpression deal with it in the end if no other possible reparses against the remaining classinfos exist.
if (comparator.getFirstType() == Object.class)
continue;
expression = reparseLiteral(classinfo.getC(), one);
if (expression != null)
break;
}
}
if (expression == null)
expression = one.getConvertedExpression(two.getReturnType());
return (Literal<?>) expression;
}

/*
* # := condition (e.g. is, is less than, contains, is enchanted with, has permission, etc.)
* !# := not #
Expand Down Expand Up @@ -302,20 +341,20 @@ private <T> SimpleLiteral<T> reparseLiteral(Class<T> type, Expression<?> expr) {
* neither a nor b # x or y === a !# x or y && b !# x or y // nor = and
*/
@Override
@SuppressWarnings({"null", "unchecked"})
@SuppressWarnings("unchecked")
public boolean check(final Event e) {
final Expression<?> third = this.third;
return first.check(e, (Checker<Object>) o1 ->
second.check(e, (Checker<Object>) o2 -> {
if (third == null)
return relation.isImpliedBy(comp != null ? comp.compare(o1, o2) : Comparators.compare(o1, o2));
return relation.isImpliedBy(comparator != null ? comparator.compare(o1, o2) : Comparators.compare(o1, o2));
return third.check(e, (Checker<Object>) o3 -> {
boolean isBetween;
if (comp != null) {
if (comparator != null) {
isBetween =
(Relation.GREATER_OR_EQUAL.isImpliedBy(comp.compare(o1, o2)) && Relation.SMALLER_OR_EQUAL.isImpliedBy(comp.compare(o1, o3)))
(Relation.GREATER_OR_EQUAL.isImpliedBy(comparator.compare(o1, o2)) && Relation.SMALLER_OR_EQUAL.isImpliedBy(comparator.compare(o1, o3)))
// Check OPPOSITE (switching o2 / o3)
|| (Relation.GREATER_OR_EQUAL.isImpliedBy(comp.compare(o1, o3)) && Relation.SMALLER_OR_EQUAL.isImpliedBy(comp.compare(o1, o2)));
|| (Relation.GREATER_OR_EQUAL.isImpliedBy(comparator.compare(o1, o3)) && Relation.SMALLER_OR_EQUAL.isImpliedBy(comparator.compare(o1, o2)));
} else {
isBetween =
(Relation.GREATER_OR_EQUAL.isImpliedBy(Comparators.compare(o1, o2)) && Relation.SMALLER_OR_EQUAL.isImpliedBy(Comparators.compare(o1, o3)))
Expand All @@ -337,7 +376,7 @@ public String toString(final @Nullable Event e, final boolean debug) {
else
s = first.toString(e, debug) + " is " + (isNegated() ? "not " : "") + "between " + second.toString(e, debug) + " and " + third.toString(e, debug);
if (debug)
s += " (comparator: " + comp + ")";
s += " (comparator: " + comparator + ")";
return s;
}

Expand Down
6 changes: 3 additions & 3 deletions src/main/java/ch/njol/skript/registrations/Classes.java
Original file line number Diff line number Diff line change
Expand Up @@ -504,9 +504,9 @@ public static <T> T parse(final String s, final Class<T> c, final ParseContext c
continue;
if (c.isAssignableFrom(conv.getTo())) {
log.clear();
final Object o = parseSimple(s, conv.getFrom(), context);
if (o != null) {
t = (T) ((Converter) conv.getConverter()).convert(o);
Object object = parseSimple(s, conv.getFrom(), context);
if (object != null) {
t = (T) ((Converter) conv.getConverter()).convert(object);
if (t != null) {
log.printLog();
return t;
Expand Down
5 changes: 4 additions & 1 deletion src/main/java/ch/njol/skript/test/runner/EffAssert.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import org.bukkit.event.Event;
import org.eclipse.jdt.annotation.Nullable;
import org.skriptlang.skript.lang.script.Script;

import ch.njol.skript.Skript;
import ch.njol.skript.doc.Description;
Expand All @@ -46,6 +47,7 @@ public class EffAssert extends Effect {

@Nullable
private Condition condition;
private Script script;

private Expression<String> errorMsg;
private boolean shouldFail;
Expand All @@ -56,6 +58,7 @@ public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelaye
String conditionString = parseResult.regexes.get(0).group();
errorMsg = (Expression<String>) exprs[0];
shouldFail = parseResult.mark != 0;
script = getParser().getCurrentScript();

ParseLogHandler logHandler = SkriptLogger.startParseLogHandler();
try {
Expand Down Expand Up @@ -91,7 +94,7 @@ public TriggerItem walk(Event event) {
if (SkriptJUnitTest.getCurrentJUnitTest() != null) {
TestTracker.junitTestFailed(SkriptJUnitTest.getCurrentJUnitTest(), message);
} else {
TestTracker.testFailed(message);
TestTracker.testFailed(message, script);
}
return null;
}
Expand Down
Loading