Fix variable argument functions in expressions

204ef7a708466d8739e5dcbfbc4794b57cabab0f

Co-Authored-By: Octavia Togami <2093023+octylFractal@users.noreply.github.com>
This commit is contained in:
N0tMyFaultOG 2020-11-13 22:35:53 +01:00
parent a32b4a1345
commit 5d283e1a17
5 changed files with 30 additions and 2 deletions

View File

@ -678,7 +678,11 @@ public class UtilityCommands {
try { try {
expression = Expression.compile(String.join(" ", input)); expression = Expression.compile(String.join(" ", input));
} catch (ExpressionException e) { } catch (ExpressionException e) {
actor.printError(TranslatableComponent.of("worldedit.calc.invalid", TextComponent.of(String.join(" ", input)))); actor.printError(TranslatableComponent.of(
"worldedit.calc.invalid.with-error",
TextComponent.of(String.join(" ", input)),
TextComponent.of(e.getMessage())
));
return; return;
} }
WorldEditAsyncCommandBuilder.createAndSendMessage(actor, () -> { WorldEditAsyncCommandBuilder.createAndSendMessage(actor, () -> {

View File

@ -65,9 +65,22 @@ public class ExpressionHelper {
Set<MethodHandle> matchingFns = functions.getMap().get(fnName); Set<MethodHandle> matchingFns = functions.getMap().get(fnName);
check(!matchingFns.isEmpty(), ctx, "Unknown function '" + fnName + "'"); check(!matchingFns.isEmpty(), ctx, "Unknown function '" + fnName + "'");
for (MethodHandle function : matchingFns) { for (MethodHandle function : matchingFns) {
if (function.isVarargsCollector()) {
int nParams = function.type().parameterCount();
// last param is the array, turn that varargs
int keptParams = nParams - 1;
function = function.asCollector(
// collect into the last array
function.type().parameterType(nParams - 1),
// collect the variable args (args over kept)
ctx.args.size() - keptParams
);
// re-wrap it for the inner arguments
function = function.asType(function.type().wrap());
}
MethodType type = function.type(); MethodType type = function.type();
// Validate argc if not varargs // Validate argc if not varargs
if (!function.isVarargsCollector() && type.parameterCount() != ctx.args.size()) { if (type.parameterCount() != ctx.args.size()) {
// skip non-matching function // skip non-matching function
continue; continue;
} }

View File

@ -63,6 +63,7 @@ public final class Functions {
} }
private static MethodHandle clean(MethodHandle handle) { private static MethodHandle clean(MethodHandle handle) {
boolean wasVarargs = handle.isVarargsCollector();
// box it all first // box it all first
handle = handle.asType(handle.type().wrap()); handle = handle.asType(handle.type().wrap());
if (handle.type().returnType() != Double.class) { if (handle.type().returnType() != Double.class) {
@ -72,6 +73,12 @@ public final class Functions {
handle = handle.asType(handle.type().changeReturnType(Number.class)); handle = handle.asType(handle.type().changeReturnType(Number.class));
handle = filterReturnValue(handle, DOUBLE_VALUE); handle = filterReturnValue(handle, DOUBLE_VALUE);
} }
// return vararg-ity
if (wasVarargs) {
handle = handle.asVarargsCollector(
handle.type().parameterType(handle.type().parameterCount() - 1)
);
}
return handle; return handle;
} }

View File

@ -342,6 +342,7 @@
"worldedit.remove.removed": "{0} entities have been marked for removal.", "worldedit.remove.removed": "{0} entities have been marked for removal.",
"worldedit.remove.explain-all": "Use -1 to remove all entities in loaded chunks", "worldedit.remove.explain-all": "Use -1 to remove all entities in loaded chunks",
"worldedit.calc.invalid": "'{0}' could not be parsed as a valid expression", "worldedit.calc.invalid": "'{0}' could not be parsed as a valid expression",
"worldedit.calc.invalid.with-error": "'{0}' could not be parsed as a valid expression: '{1}'",
"worldedit.paste.pasted": "The clipboard has been pasted at {0}", "worldedit.paste.pasted": "The clipboard has been pasted at {0}",
"worldedit.paste.selected": "Selected clipboard paste region.", "worldedit.paste.selected": "Selected clipboard paste region.",

View File

@ -51,6 +51,9 @@ class ExpressionTest extends BaseExpressionTest {
// check functions // check functions
testCase("sin(5)", sin(5)), testCase("sin(5)", sin(5)),
testCase("atan2(3, 4)", atan2(3, 4)), testCase("atan2(3, 4)", atan2(3, 4)),
testCase("min(1, 2)", 1),
testCase("max(1, 2)", 2),
testCase("max(1, 2, 3, 4, 5)", 5),
// check conditionals // check conditionals
testCase("0 || 5", 5), testCase("0 || 5", 5),
testCase("2 || 5", 2), testCase("2 || 5", 2),