Expression changes

Dieser Commit ist enthalten in:
MattBDev 2020-03-02 19:08:04 -05:00
Ursprung 2b29266db2
Commit de1bd22f85
3 geänderte Dateien mit 129 neuen und 108 gelöschten Zeilen

Datei anzeigen

@ -38,7 +38,13 @@ import java.lang.invoke.MethodHandle;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Stack; import java.util.Stack;
import java.util.concurrent.*; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/** /**
* Compiles and evaluates expressions. * Compiles and evaluates expressions.
@ -70,18 +76,22 @@ public class Expression {
private static final ThreadLocal<Stack<Expression>> instance = new ThreadLocal<>(); private static final ThreadLocal<Stack<Expression>> instance = new ThreadLocal<>();
private static final ExecutorService evalThread = Executors.newFixedThreadPool( private static final ExecutorService evalThread = Executors.newFixedThreadPool(
Runtime.getRuntime().availableProcessors(), Runtime.getRuntime().availableProcessors(),
new ThreadFactoryBuilder() new ThreadFactoryBuilder()
.setDaemon(true) .setDaemon(true)
.setNameFormat("worldedit-expression-eval-%d") .setNameFormat("worldedit-expression-eval-%d")
.build()); .build());
private final SlotTable slots = new SlotTable(); private final SlotTable slots = new SlotTable();
private final List<String> providedSlots; private final List<String> providedSlots;
private final ExpressionParser.AllStatementsContext root; private final ExpressionParser.AllStatementsContext root;
private final SetMultimap<String, MethodHandle> functions = Functions.getFunctionMap(); private final SetMultimap<String, MethodHandle> functions = Functions.getFunctionMap();
private final CompiledExpression compiledExpression;
private ExpressionEnvironment environment; private ExpressionEnvironment environment;
private final CompiledExpression compiledExpression;
public static Expression compile(String expression, String... variableNames) throws ExpressionException {
return new Expression(expression, variableNames);
}
private Expression(String expression, String... variableNames) throws ExpressionException { private Expression(String expression, String... variableNames) throws ExpressionException {
slots.putSlot("e", new LocalSlot.Constant(Math.E)); slots.putSlot("e", new LocalSlot.Constant(Math.E));
@ -91,8 +101,8 @@ public class Expression {
for (String variableName : variableNames) { for (String variableName : variableNames) {
slots.initVariable(variableName) slots.initVariable(variableName)
.orElseThrow(() -> new ExpressionException(-1, .orElseThrow(() -> new ExpressionException(-1,
"Tried to overwrite identifier '" + variableName + "'")); "Tried to overwrite identifier '" + variableName + "'"));
} }
this.providedSlots = ImmutableList.copyOf(variableNames); this.providedSlots = ImmutableList.copyOf(variableNames);
@ -114,14 +124,6 @@ public class Expression {
this.compiledExpression = new ExpressionCompiler().compileExpression(root, functions); this.compiledExpression = new ExpressionCompiler().compileExpression(root, functions);
} }
public static Expression compile(String expression, String... variableNames) throws ExpressionException {
return new Expression(expression, variableNames);
}
public static Expression getInstance() {
return instance.get().peek();
}
public double evaluate(double... values) throws EvaluationException { public double evaluate(double... values) throws EvaluationException {
return evaluate(values, WorldEdit.getInstance().getConfiguration().calculationTimeout); return evaluate(values, WorldEdit.getInstance().getConfiguration().calculationTimeout);
} }
@ -130,8 +132,8 @@ public class Expression {
for (int i = 0; i < values.length; ++i) { for (int i = 0; i < values.length; ++i) {
String slotName = providedSlots.get(i); String slotName = providedSlots.get(i);
LocalSlot.Variable slot = slots.getVariable(slotName) LocalSlot.Variable slot = slots.getVariable(slotName)
.orElseThrow(() -> new EvaluationException(-1, .orElseThrow(() -> new EvaluationException(-1,
"Tried to assign to non-variable " + slotName + ".")); "Tried to assign to non-variable " + slotName + "."));
slot.setValue(values[i]); slot.setValue(values[i]);
} }
@ -197,6 +199,10 @@ public class Expression {
return slots; return slots;
} }
public static Expression getInstance() {
return instance.get().peek();
}
private void pushInstance() { private void pushInstance() {
Stack<Expression> threadLocalExprStack = instance.get(); Stack<Expression> threadLocalExprStack = instance.get();
if (threadLocalExprStack == null) { if (threadLocalExprStack == null) {
@ -224,4 +230,4 @@ public class Expression {
this.environment = environment; this.environment = environment;
} }
} }

Datei anzeigen

@ -25,7 +25,6 @@ import com.sk89q.worldedit.antlr.ExpressionParser;
import com.sk89q.worldedit.internal.expression.BreakException; import com.sk89q.worldedit.internal.expression.BreakException;
import com.sk89q.worldedit.internal.expression.EvaluationException; import com.sk89q.worldedit.internal.expression.EvaluationException;
import com.sk89q.worldedit.internal.expression.ExecutionData; import com.sk89q.worldedit.internal.expression.ExecutionData;
import com.sk89q.worldedit.internal.expression.ExpressionHelper;
import com.sk89q.worldedit.internal.expression.LocalSlot; import com.sk89q.worldedit.internal.expression.LocalSlot;
import it.unimi.dsi.fastutil.doubles.Double2ObjectLinkedOpenHashMap; import it.unimi.dsi.fastutil.doubles.Double2ObjectLinkedOpenHashMap;
import it.unimi.dsi.fastutil.doubles.Double2ObjectMap; import it.unimi.dsi.fastutil.doubles.Double2ObjectMap;
@ -68,11 +67,39 @@ 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;
import static com.sk89q.worldedit.antlr.ExpressionLexer.TIMES_ASSIGN; 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.ExpressionHelper.WRAPPED_CONSTANT;
import static com.sk89q.worldedit.internal.expression.ExpressionHelper.check;
import static com.sk89q.worldedit.internal.expression.ExpressionHelper.evalException;
import static com.sk89q.worldedit.internal.expression.ExpressionHelper.getArgumentHandleName;
import static com.sk89q.worldedit.internal.expression.ExpressionHelper.resolveFunction;
import static com.sk89q.worldedit.internal.expression.invoke.ExpressionHandles.CALL_BINARY_OP; 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.DOUBLE_TO_BOOL;
import static com.sk89q.worldedit.internal.expression.invoke.ExpressionHandles.IS_NULL; 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.NEW_LS_CONSTANT;
import static com.sk89q.worldedit.internal.expression.invoke.ExpressionHandles.NULL_DOUBLE; import static com.sk89q.worldedit.internal.expression.invoke.ExpressionHandles.NULL_DOUBLE;
import static com.sk89q.worldedit.internal.expression.invoke.ExpressionHandles.boolToDouble;
import static com.sk89q.worldedit.internal.expression.invoke.ExpressionHandles.call;
import static com.sk89q.worldedit.internal.expression.invoke.ExpressionHandles.constantInvoke;
import static com.sk89q.worldedit.internal.expression.invoke.ExpressionHandles.dedupData;
import static com.sk89q.worldedit.internal.expression.invoke.ExpressionHandles.doWhileLoop;
import static com.sk89q.worldedit.internal.expression.invoke.ExpressionHandles.dropData;
import static com.sk89q.worldedit.internal.expression.invoke.ExpressionHandles.forLoop;
import static com.sk89q.worldedit.internal.expression.invoke.ExpressionHandles.getSlotValue;
import static com.sk89q.worldedit.internal.expression.invoke.ExpressionHandles.getVariable;
import static com.sk89q.worldedit.internal.expression.invoke.ExpressionHandles.initVariable;
import static com.sk89q.worldedit.internal.expression.invoke.ExpressionHandles.mhGetVariable;
import static com.sk89q.worldedit.internal.expression.invoke.ExpressionHandles.simpleForLoop;
import static com.sk89q.worldedit.internal.expression.invoke.ExpressionHandles.standardInvoke;
import static com.sk89q.worldedit.internal.expression.invoke.ExpressionHandles.switchStatement;
import static com.sk89q.worldedit.internal.expression.invoke.ExpressionHandles.throwEvalException;
import static com.sk89q.worldedit.internal.expression.invoke.ExpressionHandles.whileLoop;
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.filterArguments;
import static java.lang.invoke.MethodHandles.guardWithTest;
import static java.lang.invoke.MethodHandles.identity;
import static java.lang.invoke.MethodHandles.permuteArguments;
import static java.lang.invoke.MethodHandles.throwException;
import static java.lang.invoke.MethodType.methodType; import static java.lang.invoke.MethodType.methodType;
/** /**
@ -97,7 +124,7 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
.filter(TerminalNode.class::isInstance) .filter(TerminalNode.class::isInstance)
.map(TerminalNode.class::cast) .map(TerminalNode.class::cast)
.collect(Collectors.toList()); .collect(Collectors.toList());
ExpressionHelper.check(children.size() == 1, ctx, "Expected exactly one token, got " + children.size()); check(children.size() == 1, ctx, "Expected exactly one token, got " + children.size());
return children.get(0).getSymbol(); return children.get(0).getSymbol();
} }
@ -110,7 +137,7 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
} }
private void checkHandle(MethodHandle mh, ParserRuleContext ctx) { private void checkHandle(MethodHandle mh, ParserRuleContext ctx) {
ExpressionHelper.check(mh.type().equals(ExpressionHandles.COMPILED_EXPRESSION_SIG), ctx, 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());
} }
@ -119,15 +146,15 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
// if result is null // if result is null
IS_NULL.asType(methodType(boolean.class, Double.class)), IS_NULL.asType(methodType(boolean.class, Double.class)),
// throw appropriate exception, dropping `result` argument // throw appropriate exception, dropping `result` argument
MethodHandles.dropArguments( dropArguments(
ExpressionHandles.throwEvalException(ctx, "Invalid expression for " + name), 0, Double.class throwEvalException(ctx, "Invalid expression for " + name), 0, Double.class
), ),
// else return the argument we were passed // else return the argument we were passed
MethodHandles.identity(Double.class) identity(Double.class)
); );
// now pass `result` into `guard` // now pass `result` into `guard`
MethodHandle result = evaluate(ctx).handle; MethodHandle result = evaluate(ctx).handle;
return MethodHandles.collectArguments(guard, 0, result); return collectArguments(guard, 0, result);
} }
private MethodHandle evaluateForValue(ParserRuleContext ctx) { private MethodHandle evaluateForValue(ParserRuleContext ctx) {
@ -138,7 +165,7 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
MethodHandle value = evaluateForNamedValue(boolExpression, "a boolean"); MethodHandle value = evaluateForNamedValue(boolExpression, "a boolean");
value = value.asType(value.type().unwrap()); value = value.asType(value.type().unwrap());
// Pass `value` into converter, returns (ExecutionData)boolean; // Pass `value` into converter, returns (ExecutionData)boolean;
return MethodHandles.collectArguments( return collectArguments(
DOUBLE_TO_BOOL, 0, value DOUBLE_TO_BOOL, 0, value
); );
} }
@ -147,7 +174,7 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
ParserRuleContext trueBranch, ParserRuleContext trueBranch,
ParserRuleContext falseBranch) { ParserRuleContext falseBranch) {
// easiest one of the bunch // easiest one of the bunch
return MethodHandles.guardWithTest( return guardWithTest(
evaluateBoolean(condition), evaluateBoolean(condition),
trueBranch == null ? NULL_DOUBLE : evaluate(trueBranch).handle, trueBranch == null ? NULL_DOUBLE : evaluate(trueBranch).handle,
falseBranch == null ? NULL_DOUBLE : evaluate(falseBranch).handle falseBranch == null ? NULL_DOUBLE : evaluate(falseBranch).handle
@ -166,7 +193,7 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
@Override @Override
public MethodHandle visitWhileStatement(ExpressionParser.WhileStatementContext ctx) { public MethodHandle visitWhileStatement(ExpressionParser.WhileStatementContext ctx) {
return ExpressionHandles.whileLoop( return whileLoop(
evaluateBoolean(ctx.condition), evaluateBoolean(ctx.condition),
evaluate(ctx.body) evaluate(ctx.body)
); );
@ -174,7 +201,7 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
@Override @Override
public MethodHandle visitDoStatement(ExpressionParser.DoStatementContext ctx) { public MethodHandle visitDoStatement(ExpressionParser.DoStatementContext ctx) {
return ExpressionHandles.doWhileLoop( return doWhileLoop(
evaluateBoolean(ctx.condition), evaluateBoolean(ctx.condition),
evaluate(ctx.body) evaluate(ctx.body)
); );
@ -182,7 +209,7 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
@Override @Override
public MethodHandle visitForStatement(ExpressionParser.ForStatementContext ctx) { public MethodHandle visitForStatement(ExpressionParser.ForStatementContext ctx) {
return ExpressionHandles.forLoop( return forLoop(
evaluate(ctx.init).handle, evaluate(ctx.init).handle,
evaluateBoolean(ctx.condition), evaluateBoolean(ctx.condition),
evaluate(ctx.body), evaluate(ctx.body),
@ -192,7 +219,7 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
@Override @Override
public MethodHandle visitSimpleForStatement(ExpressionParser.SimpleForStatementContext ctx) { public MethodHandle visitSimpleForStatement(ExpressionParser.SimpleForStatementContext ctx) {
return ExpressionHandles.simpleForLoop( return simpleForLoop(
evaluateForValue(ctx.first), evaluateForValue(ctx.first),
evaluateForValue(ctx.last), evaluateForValue(ctx.last),
ctx.counter, ctx.counter,
@ -201,11 +228,9 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
} }
private static final MethodHandle BREAK_STATEMENT = private static final MethodHandle BREAK_STATEMENT =
ExpressionHandles.dropData(MethodHandles.throwException(Double.class, BreakException.class) dropData(throwException(Double.class, BreakException.class).bindTo(BreakException.BREAK));
.bindTo(BreakException.BREAK));
private static final MethodHandle CONTINUE_STATEMENT = private static final MethodHandle CONTINUE_STATEMENT =
ExpressionHandles.dropData(MethodHandles.throwException(Double.class, BreakException.class) dropData(throwException(Double.class, BreakException.class).bindTo(BreakException.CONTINUE));
.bindTo(BreakException.CONTINUE));
@Override @Override
public MethodHandle visitBreakStatement(ExpressionParser.BreakStatementContext ctx) { public MethodHandle visitBreakStatement(ExpressionParser.BreakStatementContext ctx) {
@ -235,15 +260,15 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
ExecNode node = evaluate(body); ExecNode node = evaluate(body);
if (label instanceof ExpressionParser.CaseContext) { if (label instanceof ExpressionParser.CaseContext) {
ExpressionParser.CaseContext caseContext = (ExpressionParser.CaseContext) label; ExpressionParser.CaseContext caseContext = (ExpressionParser.CaseContext) label;
double key = (double) ExpressionHandles.constantInvoke(evaluateForValue(caseContext.constant)); double key = (double) constantInvoke(evaluateForValue(caseContext.constant));
ExpressionHelper.check(!cases.containsKey(key), body, "Duplicate cases detected."); check(!cases.containsKey(key), body, "Duplicate cases detected.");
cases.put(key, node); cases.put(key, node);
} else { } else {
ExpressionHelper.check(defaultCase == null, body, "Duplicate default cases detected."); check(defaultCase == null, body, "Duplicate default cases detected.");
defaultCase = node; defaultCase = node;
} }
} }
return ExpressionHandles.switchStatement(cases, evaluateForValue(ctx.target), defaultCase); return switchStatement(cases, evaluateForValue(ctx.target), defaultCase);
} }
@Override @Override
@ -255,8 +280,8 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
public MethodHandle visitPostCrementExpr(ExpressionParser.PostCrementExprContext ctx) { public MethodHandle visitPostCrementExpr(ExpressionParser.PostCrementExprContext ctx) {
Token target = ctx.target; Token target = ctx.target;
int opType = ctx.op.getType(); int opType = ctx.op.getType();
return ExpressionHandles.call(data -> { return call(data -> {
LocalSlot.Variable variable = ExpressionHandles.getVariable(data, target); LocalSlot.Variable variable = getVariable(data, target);
double value = variable.getValue(); double value = variable.getValue();
if (opType == INCREMENT) { if (opType == INCREMENT) {
value++; value++;
@ -272,8 +297,8 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
public MethodHandle visitPreCrementExpr(ExpressionParser.PreCrementExprContext ctx) { public MethodHandle visitPreCrementExpr(ExpressionParser.PreCrementExprContext ctx) {
Token target = ctx.target; Token target = ctx.target;
int opType = ctx.op.getType(); int opType = ctx.op.getType();
return ExpressionHandles.call(data -> { return call(data -> {
LocalSlot.Variable variable = ExpressionHandles.getVariable(data, target); LocalSlot.Variable variable = getVariable(data, target);
double value = variable.getValue(); double value = variable.getValue();
double result = value; double result = value;
if (opType == INCREMENT) { if (opType == INCREMENT) {
@ -293,19 +318,15 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
case PLUS: case PLUS:
return value; return value;
case MINUS: case MINUS:
return ExpressionHandles.call(data -> return call(data -> -(double) standardInvoke(value, data));
-(double) ExpressionHandles.standardInvoke(value, data)
);
} }
throw ExpressionHelper.evalException(ctx, "Invalid text for plus/minus expr: " + ctx.op.getText()); throw evalException(ctx, "Invalid text for plus/minus expr: " + ctx.op.getText());
} }
@Override @Override
public MethodHandle visitNotExpr(ExpressionParser.NotExprContext ctx) { public MethodHandle visitNotExpr(ExpressionParser.NotExprContext ctx) {
MethodHandle expr = evaluateBoolean(ctx.expr); MethodHandle expr = evaluateBoolean(ctx.expr);
return ExpressionHandles.call(data -> return call(data -> boolToDouble(!(boolean) standardInvoke(expr, data)));
ExpressionHandles.boolToDouble(!(boolean) ExpressionHandles.standardInvoke(expr, data))
);
} }
@Override @Override
@ -315,21 +336,17 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
// - Convert back to double from following long // - Convert back to double from following long
// - Convert to long from double value // - Convert to long from double value
// - Convert from Object to Double to double. // - Convert from Object to Double to double.
return ExpressionHandles.call(data -> return call(data -> (double) ~(long) (double) standardInvoke(expr, data));
(double) ~(long) (double) ExpressionHandles.standardInvoke(expr, data)
);
} }
@Override @Override
public MethodHandle visitConditionalAndExpr(ExpressionParser.ConditionalAndExprContext ctx) { public MethodHandle visitConditionalAndExpr(ExpressionParser.ConditionalAndExprContext ctx) {
MethodHandle left = evaluateBoolean(ctx.left); MethodHandle left = evaluateBoolean(ctx.left);
MethodHandle right = evaluateForValue(ctx.right); MethodHandle right = evaluateForValue(ctx.right);
return MethodHandles.guardWithTest( return guardWithTest(
left, left,
right, right,
ExpressionHandles.dropData( dropData(constant(Double.class, boolToDouble(false)))
MethodHandles.constant(Double.class, ExpressionHandles.boolToDouble(false))
)
); );
} }
@ -339,24 +356,24 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
MethodHandle right = evaluateForValue(ctx.right); MethodHandle right = evaluateForValue(ctx.right);
// Inject left as primary condition, on failure take right with data parameter // Inject left as primary condition, on failure take right with data parameter
// logic = (Double,ExecutionData)Double // logic = (Double,ExecutionData)Double
MethodHandle logic = MethodHandles.guardWithTest( MethodHandle logic = guardWithTest(
// data arg dropped implicitly // data arg dropped implicitly
DOUBLE_TO_BOOL, DOUBLE_TO_BOOL,
// drop data arg // drop data arg
MethodHandles.dropArguments( dropArguments(
MethodHandles.identity(Double.class), 1, ExecutionData.class identity(Double.class), 1, ExecutionData.class
), ),
// drop left arg, call right // drop left arg, call right
MethodHandles.dropArguments( dropArguments(
right, 0, Double.class right, 0, Double.class
) )
); );
// mixed = (ExecutionData,ExecutionData)Double // mixed = (ExecutionData,ExecutionData)Double
MethodHandle mixed = MethodHandles.collectArguments( MethodHandle mixed = collectArguments(
logic, 0, left logic, 0, left
); );
// Deduplicate ExecutionData // Deduplicate ExecutionData
return ExpressionHandles.dedupData(mixed); return dedupData(mixed);
} }
private MethodHandle evaluateBinary(ParserRuleContext left, private MethodHandle evaluateBinary(ParserRuleContext left,
@ -365,12 +382,12 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
MethodHandle mhLeft = evaluateForValue(left); MethodHandle mhLeft = evaluateForValue(left);
MethodHandle mhRight = evaluateForValue(right); MethodHandle mhRight = evaluateForValue(right);
// Map two data args to two double args, then evaluate op // Map two data args to two double args, then evaluate op
MethodHandle doubleData = MethodHandles.filterArguments( MethodHandle doubleData = filterArguments(
CALL_BINARY_OP.bindTo(op), 0, CALL_BINARY_OP.bindTo(op), 0,
mhLeft.asType(mhLeft.type().unwrap()), mhRight.asType(mhRight.type().unwrap()) mhLeft.asType(mhLeft.type().unwrap()), mhRight.asType(mhRight.type().unwrap())
); );
doubleData = doubleData.asType(doubleData.type().wrap()); doubleData = doubleData.asType(doubleData.type().wrap());
return ExpressionHandles.dedupData(doubleData); return dedupData(doubleData);
} }
private MethodHandle evaluateBinary(ParserRuleContext left, private MethodHandle evaluateBinary(ParserRuleContext left,
@ -395,7 +412,7 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
case MODULO: case MODULO:
return (l, r) -> l % r; return (l, r) -> l % r;
} }
throw ExpressionHelper.evalException(ctx, "Invalid text for multiplicative expr: " + ctx.op.getText()); throw evalException(ctx, "Invalid text for multiplicative expr: " + ctx.op.getText());
}); });
} }
@ -408,7 +425,7 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
case MINUS: case MINUS:
return (l, r) -> l - r; return (l, r) -> l - r;
} }
throw ExpressionHelper.evalException(ctx, "Invalid text for additive expr: " + ctx.op.getText()); throw evalException(ctx, "Invalid text for additive expr: " + ctx.op.getText());
}); });
} }
@ -421,7 +438,7 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
case RIGHT_SHIFT: case RIGHT_SHIFT:
return (l, r) -> (double) ((long) l >> (long) r); return (l, r) -> (double) ((long) l >> (long) r);
} }
throw ExpressionHelper.evalException(ctx, "Invalid text for shift expr: " + ctx.op.getText()); throw evalException(ctx, "Invalid text for shift expr: " + ctx.op.getText());
}); });
} }
@ -430,15 +447,15 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
return evaluateBinary(ctx.left, ctx.right, () -> { return evaluateBinary(ctx.left, ctx.right, () -> {
switch (ctx.op.getType()) { switch (ctx.op.getType()) {
case LESS_THAN: case LESS_THAN:
return (l, r) -> ExpressionHandles.boolToDouble(l < r); return (l, r) -> boolToDouble(l < r);
case LESS_THAN_OR_EQUAL: case LESS_THAN_OR_EQUAL:
return (l, r) -> ExpressionHandles.boolToDouble(l <= r); return (l, r) -> boolToDouble(l <= r);
case GREATER_THAN: case GREATER_THAN:
return (l, r) -> ExpressionHandles.boolToDouble(l > r); return (l, r) -> boolToDouble(l > r);
case GREATER_THAN_OR_EQUAL: case GREATER_THAN_OR_EQUAL:
return (l, r) -> ExpressionHandles.boolToDouble(l >= r); return (l, r) -> boolToDouble(l >= r);
} }
throw ExpressionHelper.evalException(ctx, "Invalid text for relational expr: " + ctx.op.getText()); throw evalException(ctx, "Invalid text for relational expr: " + ctx.op.getText());
}); });
} }
@ -447,15 +464,15 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
return evaluateBinary(ctx.left, ctx.right, () -> { return evaluateBinary(ctx.left, ctx.right, () -> {
switch (ctx.op.getType()) { switch (ctx.op.getType()) {
case EQUAL: case EQUAL:
return (l, r) -> ExpressionHandles.boolToDouble(l == r); return (l, r) -> boolToDouble(l == r);
case NOT_EQUAL: case NOT_EQUAL:
return (l, r) -> ExpressionHandles.boolToDouble(l != r); return (l, r) -> boolToDouble(l != r);
case NEAR: case NEAR:
return (l, r) -> ExpressionHandles.boolToDouble(almostEqual2sComplement(l, r, 450359963L)); return (l, r) -> boolToDouble(almostEqual2sComplement(l, r, 450359963L));
case GREATER_THAN_OR_EQUAL: case GREATER_THAN_OR_EQUAL:
return (l, r) -> ExpressionHandles.boolToDouble(l >= r); return (l, r) -> boolToDouble(l >= r);
} }
throw ExpressionHelper.evalException(ctx, "Invalid text for equality expr: " + ctx.op.getText()); throw evalException(ctx, "Invalid text for equality expr: " + ctx.op.getText());
}); });
} }
@ -481,11 +498,9 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
public MethodHandle visitPostfixExpr(ExpressionParser.PostfixExprContext ctx) { public MethodHandle visitPostfixExpr(ExpressionParser.PostfixExprContext ctx) {
MethodHandle value = evaluateForValue(ctx.expr); MethodHandle value = evaluateForValue(ctx.expr);
if (ctx.op.getType() == EXCLAMATION_MARK) { if (ctx.op.getType() == EXCLAMATION_MARK) {
return ExpressionHandles.call(data -> return call(data -> factorial((double) standardInvoke(value, data)));
factorial((double) ExpressionHandles.standardInvoke(value, data))
);
} }
throw ExpressionHelper.evalException(ctx, throw evalException(ctx,
"Invalid text for post-unary expr: " + ctx.op.getText()); "Invalid text for post-unary expr: " + ctx.op.getText());
} }
@ -517,15 +532,15 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
int type = extractToken(ctx.assignmentOperator()).getType(); int type = extractToken(ctx.assignmentOperator()).getType();
Token target = ctx.target; Token target = ctx.target;
MethodHandle getArg = evaluateForValue(ctx.expression()); MethodHandle getArg = evaluateForValue(ctx.expression());
return ExpressionHandles.call(data -> { return call(data -> {
double value; double value;
double arg = (double) ExpressionHandles.standardInvoke(getArg, data); double arg = (double) standardInvoke(getArg, data);
LocalSlot.Variable variable; LocalSlot.Variable variable;
if (type == ASSIGN) { if (type == ASSIGN) {
variable = ExpressionHandles.initVariable(data, target); variable = initVariable(data, target);
value = arg; value = arg;
} else { } else {
variable = ExpressionHandles.getVariable(data, target); variable = getVariable(data, target);
value = variable.getValue(); value = variable.getValue();
switch (type) { switch (type) {
case POWER_ASSIGN: case POWER_ASSIGN:
@ -547,7 +562,7 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
value -= arg; value -= arg;
break; break;
default: default:
throw ExpressionHelper.evalException(ctx, "Invalid text for assign expr: " + throw evalException(ctx, "Invalid text for assign expr: " +
ctx.assignmentOperator().getText()); ctx.assignmentOperator().getText());
} }
} }
@ -558,7 +573,7 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
@Override @Override
public MethodHandle visitFunctionCall(ExpressionParser.FunctionCallContext ctx) { public MethodHandle visitFunctionCall(ExpressionParser.FunctionCallContext ctx) {
MethodHandle handle = ExpressionHelper.resolveFunction(functions, ctx); MethodHandle handle = resolveFunction(functions, ctx);
String fnName = ctx.name.getText(); String fnName = ctx.name.getText();
MethodHandle[] arguments = new MethodHandle[ctx.args.size()]; MethodHandle[] arguments = new MethodHandle[ctx.args.size()];
for (int i = 0; i < arguments.length; i++) { for (int i = 0; i < arguments.length; i++) {
@ -573,10 +588,10 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
arguments[i] = transformed; arguments[i] = transformed;
} }
// Take each of our data accepting arguments, apply them over the source method // Take each of our data accepting arguments, apply them over the source method
MethodHandle manyData = MethodHandles.filterArguments(handle, 0, arguments); MethodHandle manyData = filterArguments(handle, 0, arguments);
// Collapse every data into one argument // Collapse every data into one argument
int[] permutation = new int[arguments.length]; int[] permutation = new int[arguments.length];
return MethodHandles.permuteArguments( return permuteArguments(
manyData, ExpressionHandles.COMPILED_EXPRESSION_SIG, permutation manyData, ExpressionHandles.COMPILED_EXPRESSION_SIG, permutation
); );
} }
@ -584,7 +599,7 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
// MH: (ExecutionData)T; (depends on target) // MH: (ExecutionData)T; (depends on target)
private MethodHandle getArgument(String fnName, MethodType type, int i, ParserRuleContext arg) { private MethodHandle getArgument(String fnName, MethodType type, int i, ParserRuleContext arg) {
// Pass variable handle in for modification? // Pass variable handle in for modification?
String handleName = ExpressionHelper.getArgumentHandleName(fnName, type, i, arg); String handleName = getArgumentHandleName(fnName, type, i, arg);
if (handleName == null) { if (handleName == null) {
return evaluateForValue(arg); return evaluateForValue(arg);
} }
@ -592,39 +607,37 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
// pass arg into new LocalSlot.Constant // pass arg into new LocalSlot.Constant
MethodHandle filter = evaluateForValue(arg); MethodHandle filter = evaluateForValue(arg);
filter = filter.asType(filter.type().unwrap()); filter = filter.asType(filter.type().unwrap());
return MethodHandles.collectArguments( return collectArguments(
NEW_LS_CONSTANT, 0, filter NEW_LS_CONSTANT, 0, filter
); );
} }
// small hack // small hack
CommonToken fake = new CommonToken(arg.start); CommonToken fake = new CommonToken(arg.start);
fake.setText(handleName); fake.setText(handleName);
return ExpressionHandles.mhGetVariable(fake); return mhGetVariable(fake);
} }
@Override @Override
public MethodHandle visitConstantExpression(ExpressionParser.ConstantExpressionContext ctx) { public MethodHandle visitConstantExpression(ExpressionParser.ConstantExpressionContext ctx) {
try { try {
return ExpressionHandles.dropData( return dropData(constant(Double.class, Double.parseDouble(ctx.getText())));
MethodHandles.constant(Double.class, Double.parseDouble(ctx.getText()))
);
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
// Rare, but might happen, e.g. if too many digits // Rare, but might happen, e.g. if too many digits
throw ExpressionHelper.evalException(ctx, "Invalid constant: " + e.getMessage()); throw evalException(ctx, "Invalid constant: " + e.getMessage());
} }
} }
@Override @Override
public MethodHandle visitIdExpr(ExpressionParser.IdExprContext ctx) { public MethodHandle visitIdExpr(ExpressionParser.IdExprContext ctx) {
Token source = ctx.source; Token source = ctx.source;
return ExpressionHandles.call(data -> ExpressionHandles.getSlotValue(data, source)); return call(data -> getSlotValue(data, source));
} }
/** /**
* Method handle (ExecutionData)Double, returns null. * Method handle (ExecutionData)Double, returns null.
*/ */
private static final MethodHandle DEFAULT_RESULT = private static final MethodHandle DEFAULT_RESULT =
ExpressionHandles.dropData(MethodHandles.constant(Double.class, null)); dropData(constant(Double.class, null));
@Override @Override
protected MethodHandle defaultResult() { protected MethodHandle defaultResult() {
@ -672,14 +685,14 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
return oldResult; return oldResult;
} }
// Add a dummy Double parameter to the end // Add a dummy Double parameter to the end
MethodHandle dummyDouble = MethodHandles.dropArguments( MethodHandle dummyDouble = dropArguments(
result, 1, Double.class result, 1, Double.class
); );
// Have oldResult turn it from data->Double // Have oldResult turn it from data->Double
MethodHandle doubledData = MethodHandles.collectArguments( MethodHandle doubledData = collectArguments(
dummyDouble, 1, oldResult dummyDouble, 1, oldResult
); );
// Deduplicate the `data` parameter // Deduplicate the `data` parameter
return ExpressionHandles.dedupData(doubledData); return dedupData(doubledData);
} }
} }

Datei anzeigen

@ -29,6 +29,8 @@ import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType; import java.lang.invoke.MethodType;
import static com.sk89q.worldedit.internal.expression.invoke.ExpressionHandles.COMPILED_EXPRESSION_SIG;
import static com.sk89q.worldedit.internal.expression.invoke.ExpressionHandles.safeInvoke;
import static java.lang.invoke.MethodType.methodType; import static java.lang.invoke.MethodType.methodType;
/** /**
@ -43,7 +45,7 @@ public class ExpressionCompiler {
private static final MethodHandle HANDLE_TO_CE_CONVERTER; private static final MethodHandle HANDLE_TO_CE_CONVERTER;
static { static {
MethodHandle handleInvoker = MethodHandles.invoker(ExpressionHandles.COMPILED_EXPRESSION_SIG); MethodHandle handleInvoker = MethodHandles.invoker(COMPILED_EXPRESSION_SIG);
try { try {
HANDLE_TO_CE_CONVERTER = LambdaMetafactory.metafactory( HANDLE_TO_CE_CONVERTER = LambdaMetafactory.metafactory(
MethodHandles.lookup(), MethodHandles.lookup(),
@ -52,11 +54,11 @@ public class ExpressionCompiler {
// Take a handle, to be converted to CompiledExpression // Take a handle, to be converted to CompiledExpression
HANDLE_TO_CE, HANDLE_TO_CE,
// Raw signature for SAM type // Raw signature for SAM type
ExpressionHandles.COMPILED_EXPRESSION_SIG, COMPILED_EXPRESSION_SIG,
// Handle to call the captured handle. // Handle to call the captured handle.
handleInvoker, handleInvoker,
// Actual signature at invoke time // Actual signature at invoke time
ExpressionHandles.COMPILED_EXPRESSION_SIG COMPILED_EXPRESSION_SIG
).dynamicInvoker().asType(HANDLE_TO_CE); ).dynamicInvoker().asType(HANDLE_TO_CE);
} catch (LambdaConversionException e) { } catch (LambdaConversionException e) {
throw new IllegalStateException("Failed to load ExpressionCompiler MetaFactory", e); throw new IllegalStateException("Failed to load ExpressionCompiler MetaFactory", e);
@ -66,7 +68,7 @@ public class ExpressionCompiler {
public CompiledExpression compileExpression(ExpressionParser.AllStatementsContext root, public CompiledExpression compileExpression(ExpressionParser.AllStatementsContext root,
SetMultimap<String, MethodHandle> functions) { SetMultimap<String, MethodHandle> functions) {
MethodHandle invokable = root.accept(new CompilingVisitor(functions)); MethodHandle invokable = root.accept(new CompilingVisitor(functions));
return (CompiledExpression) ExpressionHandles.safeInvoke( return (CompiledExpression) safeInvoke(
HANDLE_TO_CE_CONVERTER, h -> h.invoke(invokable) HANDLE_TO_CE_CONVERTER, h -> h.invoke(invokable)
); );
} }