mirror of
https://github.com/plexusorg/Plex-FAWE.git
synced 2025-07-01 02:46:41 +00:00
Upstream Merge
This commit is contained in:
@ -26,4 +26,4 @@ public interface CompiledExpression {
|
||||
|
||||
Double execute(ExecutionData executionData);
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -48,4 +48,4 @@ public class ExecutionData {
|
||||
public SetMultimap<String, MethodHandle> getFunctions() {
|
||||
return requireNonNull(functions, "Cannot use functions in a constant");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -35,14 +35,6 @@ import static com.sk89q.worldedit.antlr.ExpressionLexer.ID;
|
||||
|
||||
public class ExpressionHelper {
|
||||
|
||||
/**
|
||||
* The argument should be wrapped in a {@link LocalSlot.Constant} before being passed.
|
||||
*/
|
||||
public static final String WRAPPED_CONSTANT = "<wrapped constant>";
|
||||
|
||||
private ExpressionHelper() {
|
||||
}
|
||||
|
||||
public static void check(boolean condition, ParserRuleContext ctx, String message) {
|
||||
if (!condition) {
|
||||
throw evalException(ctx, message);
|
||||
@ -59,8 +51,8 @@ public class ExpressionHelper {
|
||||
|
||||
public static EvaluationException evalException(Token token, String message) {
|
||||
return new EvaluationException(
|
||||
getErrorPosition(token),
|
||||
message
|
||||
getErrorPosition(token),
|
||||
message
|
||||
);
|
||||
}
|
||||
|
||||
@ -68,8 +60,6 @@ public class ExpressionHelper {
|
||||
check(iterations <= 256, ctx, "Loop exceeded 256 iterations");
|
||||
}
|
||||
|
||||
// Special argument handle names
|
||||
|
||||
public static void checkTimeout() {
|
||||
if (Thread.interrupted()) {
|
||||
throw new ExpressionTimeoutException("Calculations exceeded time limit.");
|
||||
@ -97,15 +87,21 @@ public class ExpressionHelper {
|
||||
}
|
||||
// We matched no function, fail with appropriate message.
|
||||
String possibleCounts = matchingFns.stream()
|
||||
.map(mh -> mh.isVarargsCollector()
|
||||
? (mh.type().parameterCount() - 1) + "+"
|
||||
: String.valueOf(mh.type().parameterCount()))
|
||||
.collect(Collectors.joining("/"));
|
||||
.map(mh -> mh.isVarargsCollector()
|
||||
? (mh.type().parameterCount() - 1) + "+"
|
||||
: String.valueOf(mh.type().parameterCount()))
|
||||
.collect(Collectors.joining("/"));
|
||||
throw evalException(ctx, "Incorrect number of arguments for function '" + fnName + "', " +
|
||||
"expected " + possibleCounts + ", " +
|
||||
"got " + ctx.args.size());
|
||||
"expected " + possibleCounts + ", " +
|
||||
"got " + ctx.args.size());
|
||||
}
|
||||
|
||||
// Special argument handle names
|
||||
/**
|
||||
* The argument should be wrapped in a {@link LocalSlot.Constant} before being passed.
|
||||
*/
|
||||
public static final String WRAPPED_CONSTANT = "<wrapped constant>";
|
||||
|
||||
/**
|
||||
* If this argument needs a handle, returns the name of the handle needed. Otherwise, returns
|
||||
* {@code null}. If {@code arg} isn't a valid handle reference, throws.
|
||||
@ -118,7 +114,7 @@ public class ExpressionHelper {
|
||||
if (pType == LocalSlot.Variable.class) {
|
||||
// MUST be an id
|
||||
check(id.isPresent(), arg,
|
||||
"Function '" + fnName + "' requires a variable in parameter " + i);
|
||||
"Function '" + fnName + "' requires a variable in parameter " + i);
|
||||
return id.get();
|
||||
} else if (pType == LocalSlot.class) {
|
||||
return id.orElse(WRAPPED_CONSTANT);
|
||||
@ -128,7 +124,7 @@ public class ExpressionHelper {
|
||||
|
||||
private static Optional<String> tryResolveId(ParserRuleContext arg) {
|
||||
Optional<ExpressionParser.WrappedExprContext> wrappedExprContext =
|
||||
tryAs(arg, ExpressionParser.WrappedExprContext.class);
|
||||
tryAs(arg, ExpressionParser.WrappedExprContext.class);
|
||||
if (wrappedExprContext.isPresent()) {
|
||||
return tryResolveId(wrappedExprContext.get().expression());
|
||||
}
|
||||
@ -139,8 +135,8 @@ public class ExpressionHelper {
|
||||
}
|
||||
|
||||
private static <T extends ParserRuleContext> Optional<T> tryAs(
|
||||
ParserRuleContext ctx,
|
||||
Class<T> rule
|
||||
ParserRuleContext ctx,
|
||||
Class<T> rule
|
||||
) {
|
||||
if (rule.isInstance(ctx)) {
|
||||
return Optional.of(rule.cast(ctx));
|
||||
@ -155,4 +151,7 @@ public class ExpressionHelper {
|
||||
return tryAs(ctxs.get(0), rule);
|
||||
}
|
||||
|
||||
}
|
||||
private ExpressionHelper() {
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -22,8 +22,8 @@ package com.sk89q.worldedit.internal.expression;
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSetMultimap;
|
||||
import com.google.common.collect.SetMultimap;
|
||||
import com.google.common.collect.Multimaps;
|
||||
import com.google.common.collect.SetMultimap;
|
||||
import com.google.common.primitives.Doubles;
|
||||
import com.sk89q.worldedit.internal.expression.LocalSlot.Variable;
|
||||
import com.sk89q.worldedit.math.Vector3;
|
||||
@ -61,7 +61,7 @@ final class Functions {
|
||||
|
||||
// clean up all the functions
|
||||
return ImmutableSetMultimap.copyOf(
|
||||
Multimaps.transformValues(map, Functions::clean)
|
||||
Multimaps.transformValues(map, Functions::clean)
|
||||
);
|
||||
}
|
||||
|
||||
@ -71,7 +71,7 @@ final class Functions {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
try {
|
||||
DOUBLE_VALUE = lookup.findVirtual(Number.class, "doubleValue",
|
||||
methodType(double.class));
|
||||
methodType(double.class));
|
||||
} catch (NoSuchMethodException | IllegalAccessException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
@ -83,7 +83,7 @@ final class Functions {
|
||||
if (handle.type().returnType() != Double.class) {
|
||||
// Ensure that the handle returns a Double, even if originally a Number
|
||||
checkState(Number.class.isAssignableFrom(handle.type().returnType()),
|
||||
"Function does not return a number");
|
||||
"Function does not return a number");
|
||||
handle = handle.asType(handle.type().changeReturnType(Number.class));
|
||||
handle = filterReturnValue(handle, DOUBLE_VALUE);
|
||||
}
|
||||
|
@ -22,7 +22,11 @@ package com.sk89q.worldedit.internal.expression.invoke;
|
||||
import com.google.common.collect.SetMultimap;
|
||||
import com.sk89q.worldedit.antlr.ExpressionBaseVisitor;
|
||||
import com.sk89q.worldedit.antlr.ExpressionParser;
|
||||
import com.sk89q.worldedit.internal.expression.*;
|
||||
import com.sk89q.worldedit.internal.expression.BreakException;
|
||||
import com.sk89q.worldedit.internal.expression.EvaluationException;
|
||||
import com.sk89q.worldedit.internal.expression.ExecutionData;
|
||||
import com.sk89q.worldedit.internal.expression.ExpressionHelper;
|
||||
import com.sk89q.worldedit.internal.expression.LocalSlot;
|
||||
import it.unimi.dsi.fastutil.doubles.Double2ObjectLinkedOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.doubles.Double2ObjectMap;
|
||||
import org.antlr.v4.runtime.CommonToken;
|
||||
@ -40,9 +44,35 @@ import java.util.function.DoubleBinaryOperator;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.*;
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.ASSIGN;
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.DIVIDE;
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.DIVIDE_ASSIGN;
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.EQUAL;
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.EXCLAMATION_MARK;
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.GREATER_THAN;
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.GREATER_THAN_OR_EQUAL;
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.INCREMENT;
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.LEFT_SHIFT;
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.LESS_THAN;
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.LESS_THAN_OR_EQUAL;
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.MINUS;
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.MINUS_ASSIGN;
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.MODULO;
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.MODULO_ASSIGN;
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.NEAR;
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.NOT_EQUAL;
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.PLUS;
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.PLUS_ASSIGN;
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.POWER_ASSIGN;
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.RIGHT_SHIFT;
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.TIMES;
|
||||
import static com.sk89q.worldedit.antlr.ExpressionLexer.TIMES_ASSIGN;
|
||||
import static com.sk89q.worldedit.internal.expression.ExpressionHelper.WRAPPED_CONSTANT;
|
||||
import static com.sk89q.worldedit.internal.expression.invoke.ExpressionHandles.*;
|
||||
import static com.sk89q.worldedit.internal.expression.invoke.ExpressionHandles.CALL_BINARY_OP;
|
||||
import static com.sk89q.worldedit.internal.expression.invoke.ExpressionHandles.DOUBLE_TO_BOOL;
|
||||
import static com.sk89q.worldedit.internal.expression.invoke.ExpressionHandles.IS_NULL;
|
||||
import static com.sk89q.worldedit.internal.expression.invoke.ExpressionHandles.NEW_LS_CONSTANT;
|
||||
import static com.sk89q.worldedit.internal.expression.invoke.ExpressionHandles.NULL_DOUBLE;
|
||||
import static java.lang.invoke.MethodType.methodType;
|
||||
|
||||
/**
|
||||
@ -50,26 +80,6 @@ import static java.lang.invoke.MethodType.methodType;
|
||||
*/
|
||||
class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
|
||||
|
||||
private static final MethodHandle BREAK_STATEMENT =
|
||||
ExpressionHandles.dropData(MethodHandles.throwException(Double.class, BreakException.class)
|
||||
.bindTo(BreakException.BREAK));
|
||||
private static final MethodHandle CONTINUE_STATEMENT =
|
||||
ExpressionHandles.dropData(MethodHandles.throwException(Double.class, BreakException.class)
|
||||
.bindTo(BreakException.CONTINUE));
|
||||
private static final double[] factorials = new double[171];
|
||||
/**
|
||||
* Method handle (ExecutionData)Double, returns null.
|
||||
*/
|
||||
private static final MethodHandle DEFAULT_RESULT =
|
||||
ExpressionHandles.dropData(MethodHandles.constant(Double.class, null));
|
||||
|
||||
static {
|
||||
factorials[0] = 1;
|
||||
for (int i = 1; i < factorials.length; ++i) {
|
||||
factorials[i] = factorials[i - 1] * i;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* General idea is that we don't need to pass around variables, they're all in ExecutionData.
|
||||
* We do need to pass that around, so most MethodHandles will be of the type
|
||||
@ -82,43 +92,11 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
|
||||
this.functions = functions;
|
||||
}
|
||||
|
||||
// Usable AlmostEqual function, based on http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm
|
||||
private static boolean almostEqual2sComplement(double a, double b, long maxUlps) {
|
||||
// Make sure maxUlps is non-negative and small enough that the
|
||||
// default NAN won't compare as equal to anything.
|
||||
//assert(maxUlps > 0 && maxUlps < 4 * 1024 * 1024); // this is for floats, not doubles
|
||||
|
||||
long aLong = Double.doubleToRawLongBits(a);
|
||||
// Make aLong lexicographically ordered as a twos-complement long
|
||||
if (aLong < 0) aLong = 0x8000000000000000L - aLong;
|
||||
|
||||
long bLong = Double.doubleToRawLongBits(b);
|
||||
// Make bLong lexicographically ordered as a twos-complement long
|
||||
if (bLong < 0) bLong = 0x8000000000000000L - bLong;
|
||||
|
||||
final long longDiff = Math.abs(aLong - bLong);
|
||||
return longDiff <= maxUlps;
|
||||
}
|
||||
|
||||
private static double factorial(double x) throws EvaluationException {
|
||||
final int n = (int) x;
|
||||
|
||||
if (n < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (n >= factorials.length) {
|
||||
return Double.POSITIVE_INFINITY;
|
||||
}
|
||||
|
||||
return factorials[n];
|
||||
}
|
||||
|
||||
private Token extractToken(ParserRuleContext ctx) {
|
||||
List<TerminalNode> children = ctx.children.stream()
|
||||
.filter(TerminalNode.class::isInstance)
|
||||
.map(TerminalNode.class::cast)
|
||||
.collect(Collectors.toList());
|
||||
.filter(TerminalNode.class::isInstance)
|
||||
.map(TerminalNode.class::cast)
|
||||
.collect(Collectors.toList());
|
||||
ExpressionHelper.check(children.size() == 1, ctx, "Expected exactly one token, got " + children.size());
|
||||
return children.get(0).getSymbol();
|
||||
}
|
||||
@ -133,19 +111,19 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
|
||||
|
||||
private void checkHandle(MethodHandle mh, ParserRuleContext ctx) {
|
||||
ExpressionHelper.check(mh.type().equals(ExpressionHandles.COMPILED_EXPRESSION_SIG), ctx,
|
||||
"Incorrect type returned from handler for " + ctx.getClass());
|
||||
"Incorrect type returned from handler for " + ctx.getClass());
|
||||
}
|
||||
|
||||
private MethodHandle evaluateForNamedValue(ParserRuleContext ctx, String name) {
|
||||
MethodHandle guard = MethodHandles.guardWithTest(
|
||||
// if result is null
|
||||
IS_NULL.asType(methodType(boolean.class, Double.class)),
|
||||
// throw appropriate exception, dropping `result` argument
|
||||
MethodHandles.dropArguments(
|
||||
ExpressionHandles.throwEvalException(ctx, "Invalid expression for " + name), 0, Double.class
|
||||
),
|
||||
// else return the argument we were passed
|
||||
MethodHandles.identity(Double.class)
|
||||
// if result is null
|
||||
IS_NULL.asType(methodType(boolean.class, Double.class)),
|
||||
// throw appropriate exception, dropping `result` argument
|
||||
MethodHandles.dropArguments(
|
||||
ExpressionHandles.throwEvalException(ctx, "Invalid expression for " + name), 0, Double.class
|
||||
),
|
||||
// else return the argument we were passed
|
||||
MethodHandles.identity(Double.class)
|
||||
);
|
||||
// now pass `result` into `guard`
|
||||
MethodHandle result = evaluate(ctx).handle;
|
||||
@ -161,7 +139,7 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
|
||||
value = value.asType(value.type().unwrap());
|
||||
// Pass `value` into converter, returns (ExecutionData)boolean;
|
||||
return MethodHandles.collectArguments(
|
||||
DOUBLE_TO_BOOL, 0, value
|
||||
DOUBLE_TO_BOOL, 0, value
|
||||
);
|
||||
}
|
||||
|
||||
@ -170,9 +148,9 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
|
||||
ParserRuleContext falseBranch) {
|
||||
// easiest one of the bunch
|
||||
return MethodHandles.guardWithTest(
|
||||
evaluateBoolean(condition),
|
||||
trueBranch == null ? NULL_DOUBLE : evaluate(trueBranch).handle,
|
||||
falseBranch == null ? NULL_DOUBLE : evaluate(falseBranch).handle
|
||||
evaluateBoolean(condition),
|
||||
trueBranch == null ? NULL_DOUBLE : evaluate(trueBranch).handle,
|
||||
falseBranch == null ? NULL_DOUBLE : evaluate(falseBranch).handle
|
||||
);
|
||||
}
|
||||
|
||||
@ -189,39 +167,46 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
|
||||
@Override
|
||||
public MethodHandle visitWhileStatement(ExpressionParser.WhileStatementContext ctx) {
|
||||
return ExpressionHandles.whileLoop(
|
||||
evaluateBoolean(ctx.condition),
|
||||
evaluate(ctx.body)
|
||||
evaluateBoolean(ctx.condition),
|
||||
evaluate(ctx.body)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodHandle visitDoStatement(ExpressionParser.DoStatementContext ctx) {
|
||||
return ExpressionHandles.doWhileLoop(
|
||||
evaluateBoolean(ctx.condition),
|
||||
evaluate(ctx.body)
|
||||
evaluateBoolean(ctx.condition),
|
||||
evaluate(ctx.body)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodHandle visitForStatement(ExpressionParser.ForStatementContext ctx) {
|
||||
return ExpressionHandles.forLoop(
|
||||
evaluate(ctx.init).handle,
|
||||
evaluateBoolean(ctx.condition),
|
||||
evaluate(ctx.body),
|
||||
evaluate(ctx.update).handle
|
||||
evaluate(ctx.init).handle,
|
||||
evaluateBoolean(ctx.condition),
|
||||
evaluate(ctx.body),
|
||||
evaluate(ctx.update).handle
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodHandle visitSimpleForStatement(ExpressionParser.SimpleForStatementContext ctx) {
|
||||
return ExpressionHandles.simpleForLoop(
|
||||
evaluateForValue(ctx.first),
|
||||
evaluateForValue(ctx.last),
|
||||
ctx.counter,
|
||||
evaluate(ctx.body)
|
||||
evaluateForValue(ctx.first),
|
||||
evaluateForValue(ctx.last),
|
||||
ctx.counter,
|
||||
evaluate(ctx.body)
|
||||
);
|
||||
}
|
||||
|
||||
private static final MethodHandle BREAK_STATEMENT =
|
||||
ExpressionHandles.dropData(MethodHandles.throwException(Double.class, BreakException.class)
|
||||
.bindTo(BreakException.BREAK));
|
||||
private static final MethodHandle CONTINUE_STATEMENT =
|
||||
ExpressionHandles.dropData(MethodHandles.throwException(Double.class, BreakException.class)
|
||||
.bindTo(BreakException.CONTINUE));
|
||||
|
||||
@Override
|
||||
public MethodHandle visitBreakStatement(ExpressionParser.BreakStatementContext ctx) {
|
||||
return BREAK_STATEMENT;
|
||||
@ -309,7 +294,7 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
|
||||
return value;
|
||||
case MINUS:
|
||||
return ExpressionHandles.call(data ->
|
||||
-(double) ExpressionHandles.standardInvoke(value, data)
|
||||
-(double) ExpressionHandles.standardInvoke(value, data)
|
||||
);
|
||||
}
|
||||
throw ExpressionHelper.evalException(ctx, "Invalid text for plus/minus expr: " + ctx.op.getText());
|
||||
@ -319,7 +304,7 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
|
||||
public MethodHandle visitNotExpr(ExpressionParser.NotExprContext ctx) {
|
||||
MethodHandle expr = evaluateBoolean(ctx.expr);
|
||||
return ExpressionHandles.call(data ->
|
||||
ExpressionHandles.boolToDouble(!(boolean) ExpressionHandles.standardInvoke(expr, data))
|
||||
ExpressionHandles.boolToDouble(!(boolean) ExpressionHandles.standardInvoke(expr, data))
|
||||
);
|
||||
}
|
||||
|
||||
@ -331,7 +316,7 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
|
||||
// - Convert to long from double value
|
||||
// - Convert from Object to Double to double.
|
||||
return ExpressionHandles.call(data ->
|
||||
(double) ~(long) (double) ExpressionHandles.standardInvoke(expr, data)
|
||||
(double) ~(long) (double) ExpressionHandles.standardInvoke(expr, data)
|
||||
);
|
||||
}
|
||||
|
||||
@ -340,11 +325,11 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
|
||||
MethodHandle left = evaluateBoolean(ctx.left);
|
||||
MethodHandle right = evaluateForValue(ctx.right);
|
||||
return MethodHandles.guardWithTest(
|
||||
left,
|
||||
right,
|
||||
ExpressionHandles.dropData(
|
||||
MethodHandles.constant(Double.class, ExpressionHandles.boolToDouble(false))
|
||||
)
|
||||
left,
|
||||
right,
|
||||
ExpressionHandles.dropData(
|
||||
MethodHandles.constant(Double.class, ExpressionHandles.boolToDouble(false))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@ -355,20 +340,20 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
|
||||
// Inject left as primary condition, on failure take right with data parameter
|
||||
// logic = (Double,ExecutionData)Double
|
||||
MethodHandle logic = MethodHandles.guardWithTest(
|
||||
// data arg dropped implicitly
|
||||
DOUBLE_TO_BOOL,
|
||||
// drop data arg
|
||||
MethodHandles.dropArguments(
|
||||
MethodHandles.identity(Double.class), 1, ExecutionData.class
|
||||
),
|
||||
// drop left arg, call right
|
||||
MethodHandles.dropArguments(
|
||||
right, 0, Double.class
|
||||
)
|
||||
// data arg dropped implicitly
|
||||
DOUBLE_TO_BOOL,
|
||||
// drop data arg
|
||||
MethodHandles.dropArguments(
|
||||
MethodHandles.identity(Double.class), 1, ExecutionData.class
|
||||
),
|
||||
// drop left arg, call right
|
||||
MethodHandles.dropArguments(
|
||||
right, 0, Double.class
|
||||
)
|
||||
);
|
||||
// mixed = (ExecutionData,ExecutionData)Double
|
||||
MethodHandle mixed = MethodHandles.collectArguments(
|
||||
logic, 0, left
|
||||
logic, 0, left
|
||||
);
|
||||
// Deduplicate ExecutionData
|
||||
return ExpressionHandles.dedupData(mixed);
|
||||
@ -381,8 +366,8 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
|
||||
MethodHandle mhRight = evaluateForValue(right);
|
||||
// Map two data args to two double args, then evaluate op
|
||||
MethodHandle doubleData = MethodHandles.filterArguments(
|
||||
CALL_BINARY_OP.bindTo(op), 0,
|
||||
mhLeft.asType(mhLeft.type().unwrap()), mhRight.asType(mhRight.type().unwrap())
|
||||
CALL_BINARY_OP.bindTo(op), 0,
|
||||
mhLeft.asType(mhLeft.type().unwrap()), mhRight.asType(mhRight.type().unwrap())
|
||||
);
|
||||
doubleData = doubleData.asType(doubleData.type().wrap());
|
||||
return ExpressionHandles.dedupData(doubleData);
|
||||
@ -474,16 +459,57 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
|
||||
});
|
||||
}
|
||||
|
||||
// Usable AlmostEqual function, based on http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm
|
||||
private static boolean almostEqual2sComplement(double a, double b, long maxUlps) {
|
||||
// Make sure maxUlps is non-negative and small enough that the
|
||||
// default NAN won't compare as equal to anything.
|
||||
//assert(maxUlps > 0 && maxUlps < 4 * 1024 * 1024); // this is for floats, not doubles
|
||||
|
||||
long aLong = Double.doubleToRawLongBits(a);
|
||||
// Make aLong lexicographically ordered as a twos-complement long
|
||||
if (aLong < 0) aLong = 0x8000000000000000L - aLong;
|
||||
|
||||
long bLong = Double.doubleToRawLongBits(b);
|
||||
// Make bLong lexicographically ordered as a twos-complement long
|
||||
if (bLong < 0) bLong = 0x8000000000000000L - bLong;
|
||||
|
||||
final long longDiff = Math.abs(aLong - bLong);
|
||||
return longDiff <= maxUlps;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodHandle visitPostfixExpr(ExpressionParser.PostfixExprContext ctx) {
|
||||
MethodHandle value = evaluateForValue(ctx.expr);
|
||||
if (ctx.op.getType() == EXCLAMATION_MARK) {
|
||||
return ExpressionHandles.call(data ->
|
||||
factorial((double) ExpressionHandles.standardInvoke(value, data))
|
||||
factorial((double) ExpressionHandles.standardInvoke(value, data))
|
||||
);
|
||||
}
|
||||
throw ExpressionHelper.evalException(ctx,
|
||||
"Invalid text for post-unary expr: " + ctx.op.getText());
|
||||
"Invalid text for post-unary expr: " + ctx.op.getText());
|
||||
}
|
||||
|
||||
private static final double[] factorials = new double[171];
|
||||
|
||||
static {
|
||||
factorials[0] = 1;
|
||||
for (int i = 1; i < factorials.length; ++i) {
|
||||
factorials[i] = factorials[i - 1] * i;
|
||||
}
|
||||
}
|
||||
|
||||
private static double factorial(double x) throws EvaluationException {
|
||||
final int n = (int) x;
|
||||
|
||||
if (n < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (n >= factorials.length) {
|
||||
return Double.POSITIVE_INFINITY;
|
||||
}
|
||||
|
||||
return factorials[n];
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -522,7 +548,7 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
|
||||
break;
|
||||
default:
|
||||
throw ExpressionHelper.evalException(ctx, "Invalid text for assign expr: " +
|
||||
ctx.assignmentOperator().getText());
|
||||
ctx.assignmentOperator().getText());
|
||||
}
|
||||
}
|
||||
variable.setValue(value);
|
||||
@ -551,7 +577,7 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
|
||||
// Collapse every data into one argument
|
||||
int[] permutation = new int[arguments.length];
|
||||
return MethodHandles.permuteArguments(
|
||||
manyData, ExpressionHandles.COMPILED_EXPRESSION_SIG, permutation
|
||||
manyData, ExpressionHandles.COMPILED_EXPRESSION_SIG, permutation
|
||||
);
|
||||
}
|
||||
|
||||
@ -567,7 +593,7 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
|
||||
MethodHandle filter = evaluateForValue(arg);
|
||||
filter = filter.asType(filter.type().unwrap());
|
||||
return MethodHandles.collectArguments(
|
||||
NEW_LS_CONSTANT, 0, filter
|
||||
NEW_LS_CONSTANT, 0, filter
|
||||
);
|
||||
}
|
||||
// small hack
|
||||
@ -580,7 +606,7 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
|
||||
public MethodHandle visitConstantExpression(ExpressionParser.ConstantExpressionContext ctx) {
|
||||
try {
|
||||
return ExpressionHandles.dropData(
|
||||
MethodHandles.constant(Double.class, Double.parseDouble(ctx.getText()))
|
||||
MethodHandles.constant(Double.class, Double.parseDouble(ctx.getText()))
|
||||
);
|
||||
} catch (NumberFormatException e) {
|
||||
// Rare, but might happen, e.g. if too many digits
|
||||
@ -594,6 +620,12 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
|
||||
return ExpressionHandles.call(data -> ExpressionHandles.getSlotValue(data, source));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method handle (ExecutionData)Double, returns null.
|
||||
*/
|
||||
private static final MethodHandle DEFAULT_RESULT =
|
||||
ExpressionHandles.dropData(MethodHandles.constant(Double.class, null));
|
||||
|
||||
@Override
|
||||
protected MethodHandle defaultResult() {
|
||||
return DEFAULT_RESULT;
|
||||
@ -641,13 +673,13 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
|
||||
}
|
||||
// Add a dummy Double parameter to the end
|
||||
MethodHandle dummyDouble = MethodHandles.dropArguments(
|
||||
result, 1, Double.class
|
||||
result, 1, Double.class
|
||||
);
|
||||
// Have oldResult turn it from data->Double
|
||||
MethodHandle doubledData = MethodHandles.collectArguments(
|
||||
dummyDouble, 1, oldResult
|
||||
dummyDouble, 1, oldResult
|
||||
);
|
||||
// Deduplicate the `data` parameter
|
||||
return ExpressionHandles.dedupData(doubledData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -31,4 +31,4 @@ class ExecNode {
|
||||
this.ctx = ctx;
|
||||
this.handle = handle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ public class ExpressionCompiler {
|
||||
|
||||
private static final String CE_EXECUTE = "execute";
|
||||
private static final MethodType HANDLE_TO_CE =
|
||||
methodType(CompiledExpression.class, MethodHandle.class);
|
||||
methodType(CompiledExpression.class, MethodHandle.class);
|
||||
|
||||
private static final MethodHandle HANDLE_TO_CE_CONVERTER;
|
||||
|
||||
@ -46,17 +46,17 @@ public class ExpressionCompiler {
|
||||
MethodHandle handleInvoker = MethodHandles.invoker(ExpressionHandles.COMPILED_EXPRESSION_SIG);
|
||||
try {
|
||||
HANDLE_TO_CE_CONVERTER = LambdaMetafactory.metafactory(
|
||||
MethodHandles.lookup(),
|
||||
// Implementing CompiledExpression.execute
|
||||
CE_EXECUTE,
|
||||
// Take a handle, to be converted to CompiledExpression
|
||||
HANDLE_TO_CE,
|
||||
// Raw signature for SAM type
|
||||
ExpressionHandles.COMPILED_EXPRESSION_SIG,
|
||||
// Handle to call the captured handle.
|
||||
handleInvoker,
|
||||
// Actual signature at invoke time
|
||||
ExpressionHandles.COMPILED_EXPRESSION_SIG
|
||||
MethodHandles.lookup(),
|
||||
// Implementing CompiledExpression.execute
|
||||
CE_EXECUTE,
|
||||
// Take a handle, to be converted to CompiledExpression
|
||||
HANDLE_TO_CE,
|
||||
// Raw signature for SAM type
|
||||
ExpressionHandles.COMPILED_EXPRESSION_SIG,
|
||||
// Handle to call the captured handle.
|
||||
handleInvoker,
|
||||
// Actual signature at invoke time
|
||||
ExpressionHandles.COMPILED_EXPRESSION_SIG
|
||||
).dynamicInvoker().asType(HANDLE_TO_CE);
|
||||
} catch (LambdaConversionException e) {
|
||||
throw new IllegalStateException("Failed to load ExpressionCompiler MetaFactory", e);
|
||||
@ -67,7 +67,7 @@ public class ExpressionCompiler {
|
||||
SetMultimap<String, MethodHandle> functions) {
|
||||
MethodHandle invokable = root.accept(new CompilingVisitor(functions));
|
||||
return (CompiledExpression) ExpressionHandles.safeInvoke(
|
||||
HANDLE_TO_CE_CONVERTER, h -> h.invoke(invokable)
|
||||
HANDLE_TO_CE_CONVERTER, h -> h.invoke(invokable)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,12 @@
|
||||
package com.sk89q.worldedit.internal.expression.invoke;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import com.sk89q.worldedit.internal.expression.*;
|
||||
import com.sk89q.worldedit.internal.expression.BreakException;
|
||||
import com.sk89q.worldedit.internal.expression.CompiledExpression;
|
||||
import com.sk89q.worldedit.internal.expression.EvaluationException;
|
||||
import com.sk89q.worldedit.internal.expression.ExecutionData;
|
||||
import com.sk89q.worldedit.internal.expression.ExpressionHelper;
|
||||
import com.sk89q.worldedit.internal.expression.LocalSlot;
|
||||
import it.unimi.dsi.fastutil.doubles.Double2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.doubles.Double2ObjectMaps;
|
||||
import org.antlr.v4.runtime.ParserRuleContext;
|
||||
@ -34,18 +39,22 @@ import java.util.Objects;
|
||||
import java.util.function.DoubleBinaryOperator;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static com.sk89q.worldedit.internal.expression.ExpressionHelper.*;
|
||||
import static java.lang.invoke.MethodHandles.*;
|
||||
import static com.sk89q.worldedit.internal.expression.ExpressionHelper.check;
|
||||
import static com.sk89q.worldedit.internal.expression.ExpressionHelper.checkIterations;
|
||||
import static com.sk89q.worldedit.internal.expression.ExpressionHelper.checkTimeout;
|
||||
import static com.sk89q.worldedit.internal.expression.ExpressionHelper.getErrorPosition;
|
||||
import static java.lang.invoke.MethodHandles.collectArguments;
|
||||
import static java.lang.invoke.MethodHandles.constant;
|
||||
import static java.lang.invoke.MethodHandles.dropArguments;
|
||||
import static java.lang.invoke.MethodHandles.insertArguments;
|
||||
import static java.lang.invoke.MethodHandles.permuteArguments;
|
||||
import static java.lang.invoke.MethodHandles.throwException;
|
||||
import static java.lang.invoke.MethodType.methodType;
|
||||
|
||||
class ExpressionHandles {
|
||||
|
||||
static final MethodType COMPILED_EXPRESSION_SIG = methodType(Double.class, ExecutionData.class);
|
||||
static final MethodHandle IS_NULL;
|
||||
static final MethodHandle DOUBLE_TO_BOOL;
|
||||
static final MethodHandle CALL_BINARY_OP;
|
||||
static final MethodHandle NEW_LS_CONSTANT;
|
||||
static final MethodHandle NULL_DOUBLE = dropData(constant(Double.class, null));
|
||||
|
||||
private static final MethodHandle EVAL_EXCEPTION_CONSTR;
|
||||
private static final MethodHandle CALL_EXPRESSION;
|
||||
private static final MethodHandle GET_VARIABLE;
|
||||
@ -54,43 +63,52 @@ class ExpressionHandles {
|
||||
private static final MethodHandle SIMPLE_FOR_LOOP_IMPL;
|
||||
private static final MethodHandle SWITCH_IMPL;
|
||||
|
||||
static final MethodHandle IS_NULL;
|
||||
static final MethodHandle DOUBLE_TO_BOOL;
|
||||
static final MethodHandle CALL_BINARY_OP;
|
||||
static final MethodHandle NEW_LS_CONSTANT;
|
||||
|
||||
static final MethodHandle NULL_DOUBLE = dropData(constant(Double.class, null));
|
||||
|
||||
static {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
try {
|
||||
EVAL_EXCEPTION_CONSTR = lookup.findConstructor(
|
||||
EvaluationException.class, methodType(void.class, int.class, String.class));
|
||||
EvaluationException.class, methodType(void.class, int.class, String.class));
|
||||
CALL_EXPRESSION = lookup.findVirtual(
|
||||
CompiledExpression.class, "execute",
|
||||
methodType(Double.class, ExecutionData.class));
|
||||
CompiledExpression.class, "execute",
|
||||
methodType(Double.class, ExecutionData.class));
|
||||
GET_VARIABLE = lookup.findStatic(ExpressionHandles.class, "getVariable",
|
||||
methodType(LocalSlot.Variable.class, ExecutionData.class, Token.class));
|
||||
methodType(LocalSlot.Variable.class, ExecutionData.class, Token.class));
|
||||
WHILE_FOR_LOOP_IMPL = lookup.findStatic(ExpressionHandles.class,
|
||||
"whileForLoopImpl",
|
||||
methodType(Double.class, ExecutionData.class, MethodHandle.class,
|
||||
MethodHandle.class, ExecNode.class, MethodHandle.class));
|
||||
"whileForLoopImpl",
|
||||
methodType(Double.class, ExecutionData.class, MethodHandle.class,
|
||||
MethodHandle.class, ExecNode.class, MethodHandle.class));
|
||||
DO_WHILE_LOOP_IMPL = lookup.findStatic(ExpressionHandles.class, "doWhileLoopImpl",
|
||||
methodType(Double.class, ExecutionData.class, MethodHandle.class, ExecNode.class));
|
||||
methodType(Double.class, ExecutionData.class, MethodHandle.class, ExecNode.class));
|
||||
SIMPLE_FOR_LOOP_IMPL = lookup.findStatic(ExpressionHandles.class, "simpleForLoopImpl",
|
||||
methodType(Double.class, ExecutionData.class, MethodHandle.class,
|
||||
MethodHandle.class, Token.class, ExecNode.class));
|
||||
methodType(Double.class, ExecutionData.class, MethodHandle.class,
|
||||
MethodHandle.class, Token.class, ExecNode.class));
|
||||
SWITCH_IMPL = lookup.findStatic(ExpressionHandles.class, "switchImpl",
|
||||
methodType(Double.class, ExecutionData.class, Double2ObjectMap.class,
|
||||
MethodHandle.class, ExecNode.class));
|
||||
methodType(Double.class, ExecutionData.class, Double2ObjectMap.class,
|
||||
MethodHandle.class, ExecNode.class));
|
||||
|
||||
IS_NULL = lookup.findStatic(Objects.class, "isNull",
|
||||
methodType(boolean.class, Object.class));
|
||||
methodType(boolean.class, Object.class));
|
||||
DOUBLE_TO_BOOL = lookup.findStatic(ExpressionHandles.class, "doubleToBool",
|
||||
methodType(boolean.class, double.class));
|
||||
methodType(boolean.class, double.class));
|
||||
CALL_BINARY_OP = lookup.findVirtual(DoubleBinaryOperator.class, "applyAsDouble",
|
||||
methodType(double.class, double.class, double.class));
|
||||
methodType(double.class, double.class, double.class));
|
||||
NEW_LS_CONSTANT = lookup.findConstructor(LocalSlot.Constant.class,
|
||||
methodType(void.class, double.class));
|
||||
methodType(void.class, double.class));
|
||||
} catch (NoSuchMethodException | IllegalAccessException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private ExpressionHandles() {
|
||||
@FunctionalInterface
|
||||
interface Invokable {
|
||||
Object invoke(MethodHandle handle) throws Throwable;
|
||||
}
|
||||
|
||||
static Object safeInvoke(MethodHandle handle, Invokable invokable) {
|
||||
@ -116,22 +134,22 @@ class ExpressionHandles {
|
||||
|
||||
static MethodHandle dedupData(MethodHandle doubleData) {
|
||||
return permuteArguments(
|
||||
doubleData, COMPILED_EXPRESSION_SIG,
|
||||
0, 0
|
||||
doubleData, COMPILED_EXPRESSION_SIG,
|
||||
0, 0
|
||||
);
|
||||
}
|
||||
|
||||
static LocalSlot.Variable initVariable(ExecutionData data, Token nameToken) {
|
||||
String name = nameToken.getText();
|
||||
return data.getSlots().initVariable(name)
|
||||
.orElseThrow(() -> ExpressionHelper.evalException(
|
||||
nameToken, "Cannot overwrite non-variable '" + name + "'"
|
||||
));
|
||||
.orElseThrow(() -> ExpressionHelper.evalException(
|
||||
nameToken, "Cannot overwrite non-variable '" + name + "'"
|
||||
));
|
||||
}
|
||||
|
||||
private static Supplier<EvaluationException> varNotInitException(Token nameToken) {
|
||||
return () -> ExpressionHelper.evalException(
|
||||
nameToken, "'" + nameToken.getText() + "' is not initialized yet"
|
||||
nameToken, "'" + nameToken.getText() + "' is not initialized yet"
|
||||
);
|
||||
}
|
||||
|
||||
@ -142,10 +160,10 @@ class ExpressionHandles {
|
||||
static LocalSlot.Variable getVariable(ExecutionData data, Token nameToken) {
|
||||
String name = nameToken.getText();
|
||||
LocalSlot slot = data.getSlots().getSlot(name)
|
||||
.orElseThrow(varNotInitException(nameToken));
|
||||
.orElseThrow(varNotInitException(nameToken));
|
||||
if (!(slot instanceof LocalSlot.Variable)) {
|
||||
throw ExpressionHelper.evalException(
|
||||
nameToken, "'" + name + "' is not a variable"
|
||||
nameToken, "'" + name + "' is not a variable"
|
||||
);
|
||||
}
|
||||
return (LocalSlot.Variable) slot;
|
||||
@ -154,7 +172,7 @@ class ExpressionHandles {
|
||||
static double getSlotValue(ExecutionData data, Token nameToken) {
|
||||
String name = nameToken.getText();
|
||||
return data.getSlots().getSlotValue(name)
|
||||
.orElseThrow(varNotInitException(nameToken));
|
||||
.orElseThrow(varNotInitException(nameToken));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -163,7 +181,7 @@ class ExpressionHandles {
|
||||
*/
|
||||
private static MethodHandle evalException(ParserRuleContext ctx, String message) {
|
||||
return insertArguments(EVAL_EXCEPTION_CONSTR, 0,
|
||||
getErrorPosition(ctx.start), message);
|
||||
getErrorPosition(ctx.start), message);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -173,9 +191,9 @@ class ExpressionHandles {
|
||||
static MethodHandle throwEvalException(ParserRuleContext ctx, String message) {
|
||||
// replace arg0 of `throw` with `evalException`
|
||||
return collectArguments(
|
||||
throwException(Double.class, EvaluationException.class),
|
||||
0,
|
||||
evalException(ctx, message)
|
||||
throwException(Double.class, EvaluationException.class),
|
||||
0,
|
||||
evalException(ctx, message)
|
||||
);
|
||||
}
|
||||
|
||||
@ -196,7 +214,7 @@ class ExpressionHandles {
|
||||
|
||||
static MethodHandle whileLoop(MethodHandle condition, ExecNode body) {
|
||||
return insertArguments(WHILE_FOR_LOOP_IMPL, 1,
|
||||
null, condition, body, null);
|
||||
null, condition, body, null);
|
||||
}
|
||||
|
||||
static MethodHandle forLoop(MethodHandle init,
|
||||
@ -204,7 +222,7 @@ class ExpressionHandles {
|
||||
ExecNode body,
|
||||
MethodHandle update) {
|
||||
return insertArguments(WHILE_FOR_LOOP_IMPL, 1,
|
||||
init, condition, body, update);
|
||||
init, condition, body, update);
|
||||
}
|
||||
|
||||
private static Double whileForLoopImpl(ExecutionData data,
|
||||
@ -264,7 +282,7 @@ class ExpressionHandles {
|
||||
Token counter,
|
||||
ExecNode body) {
|
||||
return insertArguments(SIMPLE_FOR_LOOP_IMPL, 1,
|
||||
first, last, counter, body);
|
||||
first, last, counter, body);
|
||||
}
|
||||
|
||||
private static Double simpleForLoopImpl(ExecutionData data,
|
||||
@ -331,9 +349,7 @@ class ExpressionHandles {
|
||||
return evaluated;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
interface Invokable {
|
||||
Object invoke(MethodHandle handle) throws Throwable;
|
||||
private ExpressionHandles() {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -19,10 +19,10 @@
|
||||
|
||||
package com.sk89q.worldedit.internal.util;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
/**
|
||||
* An explicit substring. Provides the range from which it was taken.
|
||||
*/
|
||||
|
Reference in New Issue
Block a user