From 9fb6ec91cb7a1e76b5ceb5c03966ef116c991c07 Mon Sep 17 00:00:00 2001
From: altrisi
Date: Sat, 8 Jan 2022 23:05:21 +0100
Subject: [PATCH] Minor improvements to scarpet annotation API (#1239)
---
src/main/java/carpet/script/Expression.java | 11 +++++++----
src/main/java/carpet/script/Fluff.java | 2 ++
.../script/annotation/AnnotationParser.java | 19 +++++++++++--------
.../script/annotation/ScarpetFunction.java | 9 +++++++--
.../script/annotation/ValueConverter.java | 6 +++---
5 files changed, 30 insertions(+), 17 deletions(-)
diff --git a/src/main/java/carpet/script/Expression.java b/src/main/java/carpet/script/Expression.java
index e4109da4..4796765c 100644
--- a/src/main/java/carpet/script/Expression.java
+++ b/src/main/java/carpet/script/Expression.java
@@ -481,9 +481,9 @@ public class Expression
}
- public void addLazyFunction(String name, int num_params, TriFunction, LazyValue> fun)
+ public void addLazyFunction(String name, int numParams, TriFunction, LazyValue> fun)
{
- functions.put(name, new AbstractLazyFunction(num_params, name)
+ functions.put(name, new AbstractLazyFunction(numParams, name)
{
@Override
public boolean pure() {
@@ -499,8 +499,11 @@ public class Expression
public LazyValue lazyEval(Context c, Context.Type i, Expression e, Tokenizer.Token t, List lazyParams)
{
ILazyFunction.checkInterrupts();
- if (num_params >= 0 && lazyParams.size() != num_params)
- throw new InternalExpressionException("'"+name+"' requires "+num_params+" parameters, "+lazyParams.size()+" provided.");
+ if (numParams >= 0 && lazyParams.size() != numParams)
+ {
+ String error = "Function '"+name+"' requires "+numParams+" arguments, got "+lazyParams.size()+". ";
+ throw new InternalExpressionException(error + (fun instanceof Fluff.UsageProvider up ? up.getUsage() : ""));
+ }
try
{
diff --git a/src/main/java/carpet/script/Fluff.java b/src/main/java/carpet/script/Fluff.java
index d2e07fdc..ab2e45ba 100644
--- a/src/main/java/carpet/script/Fluff.java
+++ b/src/main/java/carpet/script/Fluff.java
@@ -30,6 +30,8 @@ public abstract class Fluff
@FunctionalInterface
public interface SexFunction { R apply(A a, B b, C c, D d, E e, F f);}
+ public interface UsageProvider { String getUsage();}
+
public interface EvalNode
{
/**
diff --git a/src/main/java/carpet/script/annotation/AnnotationParser.java b/src/main/java/carpet/script/annotation/AnnotationParser.java
index a2c759c6..2e8907dd 100644
--- a/src/main/java/carpet/script/annotation/AnnotationParser.java
+++ b/src/main/java/carpet/script/annotation/AnnotationParser.java
@@ -21,6 +21,7 @@ import carpet.script.Context;
import carpet.script.Expression;
import carpet.script.Fluff.AbstractLazyFunction;
import carpet.script.Fluff.TriFunction;
+import carpet.script.Fluff.UsageProvider;
import carpet.script.exception.InternalExpressionException;
import carpet.script.LazyValue;
import carpet.script.value.Value;
@@ -73,6 +74,7 @@ import it.unimi.dsi.fastutil.objects.ObjectArrayList;
*/
public final class AnnotationParser
{
+ static final int UNDEFINED_PARAMS = -2;
private static final List functionList = new ArrayList<>();
/**
@@ -90,8 +92,8 @@ public final class AnnotationParser
* {@link InternalExpressionException}).
* Basically, it's fine as long as you don't add a {@code throws} declaration to your methods.
* Varargs (or effectively varargs) annotated methods must explicitly declare a maximum number of parameters to ingest in the {@link ScarpetFunction}
- * annotation. They can still declare an unlimited amount by setting that maximum to {@code -1}. "Effectively varargs" means a function that has
- * a parameter using/requiring a {@link ValueConverter} that has declared {@link ValueConverter#consumesVariableArgs()}.
+ * annotation. They can still declare an unlimited amount by setting that maximum to {@link ScarpetFunction#UNLIMITED_PARAMS}. "Effectively varargs"
+ * means a function that has at least a parameter requiring a {@link ValueConverter} that has declared {@link ValueConverter#consumesVariableArgs()}.
* Annotated methods must not have a parameter with generics as the varargs parameter. This is just because it was painful for me (altrisi) and
* didn't want to support it. Those will crash with a {@code ClassCastException}
*
@@ -141,7 +143,7 @@ public final class AnnotationParser
expr.addLazyFunction(function.name, function.scarpetParamCount, function);
}
- private static class ParsedFunction implements TriFunction, LazyValue>
+ private static class ParsedFunction implements TriFunction, LazyValue>, UsageProvider
{
private final String name;
private final boolean isMethodVarArgs;
@@ -186,14 +188,14 @@ public final class AnnotationParser
if (this.isEffectivelyVarArgs)
{
maxParams = method.getAnnotation(ScarpetFunction.class).maxParams();
- if (maxParams == -2)
- throw new IllegalArgumentException("No maximum number of params specified for " + name + ", use -1 for unlimited. "
+ if (maxParams == UNDEFINED_PARAMS)
+ throw new IllegalArgumentException("No maximum number of params specified for " + name + ", use ScarpetFunction.UNLIMITED_PARAMS for unlimited. "
+ "Provided in " + instance.getClass());
+ if (maxParams == ScarpetFunction.UNLIMITED_PARAMS)
+ maxParams = Integer.MAX_VALUE;
if (maxParams < this.minParams)
throw new IllegalArgumentException("Provided maximum number of params for " + name + " is smaller than method's param count."
+ "Provided in " + instance.getClass());
- if (maxParams == -1)
- maxParams = Integer.MAX_VALUE;
}
this.maxParams = maxParams;
@@ -293,7 +295,8 @@ public final class AnnotationParser
return params;
}
- private String getUsage()
+ @Override
+ public String getUsage()
{
// Possibility: More descriptive messages using param.getName()? Would need changing gradle setup to keep those
StringBuilder builder = new StringBuilder("Usage: '");
diff --git a/src/main/java/carpet/script/annotation/ScarpetFunction.java b/src/main/java/carpet/script/annotation/ScarpetFunction.java
index 466bda7e..edb980fd 100644
--- a/src/main/java/carpet/script/annotation/ScarpetFunction.java
+++ b/src/main/java/carpet/script/annotation/ScarpetFunction.java
@@ -51,6 +51,11 @@ import carpet.script.value.Value;
@Retention(RUNTIME)
public @interface ScarpetFunction
{
+ /**
+ * Used to define that this {@link ScarpetFunction} can accept an unlimited number of parameters
+ */
+ public static final int UNLIMITED_PARAMS = -1;
+
/**
* If the function can accept a variable number of parameters, either by declaring its last parameter as a varargs parameter or by having one
* of their parameters use a converter that consumes a variable number of arguments, this must define the maximum number of parameters this
@@ -65,11 +70,11 @@ public @interface ScarpetFunction
* consider that those can take either a single triple of values or 3 independent values, that would be counted in the maximum number of
* parameters.
*
- * Use -1 to specify an unlimited number of parameters.
+ * Use {@link ScarpetFunction#UNLIMITED_PARAMS} to allow an unlimited number of parameters.
*
* @return The maximum number of parameters this function can accept
*/
- int maxParams() default -2;
+ int maxParams() default AnnotationParser.UNDEFINED_PARAMS;
/**
* Defines the Context Type that will be used when evaluating arguments to annotated methods.
diff --git a/src/main/java/carpet/script/annotation/ValueConverter.java b/src/main/java/carpet/script/annotation/ValueConverter.java
index 2a5e50c8..ed1a1812 100644
--- a/src/main/java/carpet/script/annotation/ValueConverter.java
+++ b/src/main/java/carpet/script/annotation/ValueConverter.java
@@ -162,13 +162,13 @@ public interface ValueConverter
* constraints as {@link #convert(Value)}
* @param valueIterator An {@link Iterator} holding the {@link Value} to convert in next position
* @param context The {@link Context} this function has been called with. This was used when this passed lazy values instead in order to
- * evaluate, now it's used to get the context. It is now ignored by the default implementation
- * @param theLazyT The {@code t} that the original function was called with. It is ignored by the default implementation.
+ * evaluate, now it's used to get the context
+ * @param contextType The {@link Context.Type} that the original function was called with
* @return The next {@link Value} (s) converted to the type {@code } of this {@link ValueConverter}
* @implNote This method's default implementation runs the {@link #convert(Value)} function in the next {@link Value} ignoring {@link Context} and
* {@code theLazyT}.
*/
- default public R checkAndConvert(Iterator valueIterator, Context context, Context.Type theLazyT)
+ default public R checkAndConvert(Iterator valueIterator, Context context, Context.Type contextType)
{
if (!valueIterator.hasNext())
return null;