Annotated Scarpet (#772)

* Progress by now.

* Output conversions plus a few extra random things

* More progress, mostly list converting and a way to get converters

* Apply ValueConverters, refactor valueconverter getter to

accept types with annotations and generics, made listconverter pass
those params and annotations recursively. And probably more things.

* Some extra docs, the 100% most important thing

* Added MapConverter, strict Strings and Texts, some docs additions

and commented out heuristics

* Rework StrictParam to use SimpleTypeConverters and be saved in a map

* Minor changes and docs & added non-fully-functional context "converter"

* Added support for variable value count converters, multi-value eaters &

0 value eaters (Context).

* Docs update, enable Context converter, fix minor TODOs

* Move (most) parameter annotations into a single class,

remove deprecated ValueCaster#cast, update some docs.

* Small refactor of Param.Strict registering, added boolean to it

* Added PairConverter, moved Context and LV ValueConverters to Params

* Document public methods of AnnotationParser

* Add a lot more docs, make it ready for the first preview PR

* Move ValueConverter population outside of makeFunction,

check lv size for > max arguments, minor doc updates and workaround for
Mercury#28 until released

* Added optional parameters (declared using Java's Optional),

made evalAndConvert check for the presence of the next iteration,
updated test class

* Generalize Value OutputConverter

* ListConverter and MapConverter now follow ValueConverter contract of

incorrect value handling, returning a complete null for an incorrect
value anywhere. Also small fix to OptionalConverter and docs.
Getting ready for checking null in AnnotationParser!

* Change reflection's #invoke to a MethodHandle to boost speed by a lot

Commented

* Dedupe code, update and remove outdated todos, add 2 new ValueCasters

* Merge lambdas in display_title2 test function to be more accurate

* Check nulls somehow, I guess

* Move base function logic into a class to stop

passing hundreds of parameters into static functions and therefore simplify it

* Reduce visibility and fix a bug with PairConverter

* Hide Context provider in usage report

* Add support for primitive varargs

* Added TheLazyT converter

* Drop #getPrefixedTypeName and use 'something' as name of values

* Use value again because of varargs, document that

* Remove a completed TODO

* Remove old disabled heuristic value type name getter

* Fix TheLazyT

* Preparing locators before touching current Carpet infrastructure

* Add notes about non-implementedness of remaining locators

* Added APIs to register custom ValueConverters

* Mention new APIs in javadocs

* Adapt to new Carpet changes and deprecated non-implemented annotations

* Update example function for display_title()

* Better implementation for player_list example

* Fix example error message

* FunctionArgument no longer has generics

* Modernize Text converters

* OutputConverter is final, plus other small changes

* Few non-behavioural changes

* Rename LazyFunction to ScarpetFunction

* Drop LazyValue in parameters, not in return

Updating docs was a nightmare

* CARPET CHANGE - Make Fluff.AbstractLazyFunction.unpackArgs call unpackLazy (was duped)

* Make errors more like current Scarpet errors

* Allow customizing ContextType when evaluating arguments

* Add a few missing docs, comment out outdated ValueCaster doc

It's been a while, wow

* Fix for new Carpet signature :irritatered:

Please don't break API methods! Extensions may be using them, and they
will just crash the game or behave in unexpected ways else!

* Format all code to Carpet's. Powered by Eclipse

Reviewed to be sure it's ok. No functional changes, except from a small correction

* Always use full name for Context.Type

* Add parsing for all current Scarpet api and language classes

And remove test functions

* Allow void return type

* Fix no param methods

Whoops!

* Remove outdated comment and fix small javadoc linting problem

* Drop Param.TheLazyT in favour of just Context.Type type

And small performance improvement to varargs varargs since fastutil's toArray is crap

* Quote functions in error messages

* Make SimpleTypeConverter independent of ValueCaster

'Real world' usage demonstrated it can be annoying else, mostly for wrapper only values where you just want the wrapped object
This commit is contained in:
altrisi authored and GitHub committed 2021-05-21 01:33:00 -03:00
1 parent 7fd49e0f04
commit cc7a9c01be
16 files changed
+1976 -9

No files matched your search

@@ -0,0 +1,270 @@
package carpet.script.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.reflect.AnnotatedType;
import java.util.Iterator;
import org.apache.commons.lang3.NotImplementedException;
import carpet.script.Context;
import carpet.script.argument.Argument;
import carpet.script.argument.BlockArgument;
import carpet.script.argument.FunctionArgument;
import carpet.script.argument.Vector3Argument;
import carpet.script.bundled.Module;
import carpet.script.value.BlockValue;
import carpet.script.value.FunctionValue;
import carpet.script.value.Value;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* <p>Class that holds annotation for {@link Argument} locators to be used in Scarpet functions.</p>
*
* <p>Note: Nothing in this class has been implemented at this point in time, since it requires Carpet changes. There is an implementation for
* Locator.Block working, but not implemented.</p>
*/
public interface Locator
{
/**
* <p>Represents that the annotated argument must be gotten by passing the arguments in there into a {@link BlockArgument} locator.</p>
*
* <p>Must be used in either {@link BlockValue} or {@link BlockArgument} parameters</p>
*/
@Documented
@Retention(RUNTIME)
@Target({ PARAMETER, TYPE_USE })
@Deprecated // Not implemented, although implementation available
public @interface Block
{
/**
* <p>Whether or not should the locator accept a single {@link String} as the parameter and let parsing to {@link BlockValue}.</p>
*/
boolean acceptString() default false;
/**
* <p>Whether or not should the {@link BlockValue} argument be optional.</p> <p>Requires the annotation to be present in a
* {@link BlockArgument} type, since it may return that the {@link BlockValue} is {@code null}, which would be considered as an incorrect
* type.</p>
*/
boolean optional() default false;
/**
* <p>Whether or not should the {@link BlockArgument} locator accept any string as the argument.</p> <p>Requires the annotation to be present
* in a {@link BlockArgument} type, since it may just return a {@link String}</p>
*/
boolean anyString() default false;
}
/**
* <p>Represents that the annotated argument must be gotten by passing the arguments in there into a {@link Vector3Argument} locator.</p>
*
* <p>Must be used in either a {@link Vector3Argument} or a {@link Vec3d} parameter, though the latest may not get all the data.</p>
*
* <p>Note: This locator has not been implemented yet (TODO)</p>
*/
@Documented
@Retention(RUNTIME)
@Target({ PARAMETER, TYPE_USE })
@Deprecated // Not implemented
public @interface Vec3d
{
/**
* <p>Whether or not should the {@link Vector3Argument} locator accept an optional direction aside from the
* {@link net.minecraft.util.math.Vec3d}</p> <p>This parameter can only be used in a {@link Vector3Argument} type, since else there is no way
* to get the direction too.</p>
*/
boolean optionalDirection() default false;
/**
* <p>Whether or not should the {@link Vector3Argument} locator accept an entity aside to get the {@link Vec3d} from and return that entity
* too</p> <p>Note that you will only be able to get that entity if the annotation is present in a {@link Vector3Argument}</p>
*/
boolean optionalEntity() default false;
}
/**
* <p>Represents that the annotated argument must be gotten by passing the arguments in this annotation into a {@link FunctionArgument}
* locator</p>
*
* <p>Can be used in both {@link FunctionArgument} and {@link FunctionValue} types, but the last won't have access to arguments provided to the
* function, even though they will still be consumed from the arguments the function was called with.</p>
*
* <p><b>This will consume any remaining parameters passed to the function, therefore any other parameters after this will throw.</b></p>
*
* <p>Note: This locator has not been implemented yet (TODO)</p>
*/
@Documented
@Retention(RUNTIME)
@Target({ PARAMETER, TYPE_USE })
@Deprecated // Not implemented
public @interface Function
{
/**
* <p>Whether this Locator should allow no function to be passed.</p> <p>This is not compatible with {@link FunctionValue} type, since a
* converter returning {@code null} will throw as if the passed argument was incorrect. You can still use it when targeting
* {@link FunctionArgument}</p>
*/
boolean allowNone() default false;
/**
* <p>Whether the locator should check that the number of arguments passed along with the function matches the number of arguments that the
* located function requires. Note that FunctionLocators consume all remaining arguments even if this is set to {@code false}.</p>
*/
boolean checkArgs();
}
/**
* <p>Class that holds locators and methods to get them</p>
*
* <p>Not part of the public API, just that interfaces must have all members public</p>
*/
static final class Locators
{
private Locators() {}
static <R> ValueConverter<R> fromAnnotatedType(AnnotatedType annoType, Class<R> type)
{
if (annoType.isAnnotationPresent(Block.class))
return new BlockLocator<R>(annoType.getAnnotation(Block.class), type);
if (annoType.isAnnotationPresent(Function.class))
return new FunctionLocator<R>(annoType.getAnnotation(Function.class), type);
if (annoType.isAnnotationPresent(Vec3d.class))
return new Vec3dLocator<R>(annoType.getAnnotation(Vec3d.class), type);
throw new IllegalStateException("Locator#fromAnnotatedType got called with an incompatible AnnotatedType");
}
private static class BlockLocator<R> extends AbstractLocator<R>
{
private final boolean returnBlockValue;
private final boolean acceptString;
private final boolean anyString;
private final boolean optional;
public BlockLocator(Block annotation, Class<R> type)
{
this.acceptString = annotation.acceptString();
this.anyString = annotation.anyString();
this.optional = annotation.optional();
this.returnBlockValue = type == BlockValue.class;
if (returnBlockValue && (anyString || optional))
throw new IllegalArgumentException("Can only use anyString or optional parameters of Locator.Block if targeting a BlockArgument");
if (!returnBlockValue && type != BlockArgument.class)
throw new IllegalArgumentException("Locator.Block can only be used against BlockArgument or BlockValue types!");
}
@Override
public String getTypeName()
{
return "block";
}
@Override
public R checkAndConvert(Iterator<Value> valueIterator, Context context, Context.Type theLazyT)
{
BlockArgument locator = null; // BlockArgument.findIn((CarpetContext)context, valueIterator (requires changing to Value), 0,
// acceptString, optional, anyString);
// return (R) (returnBlockValue ? locator.block : locator);
throw new NotImplementedException("Locator.Block still requires adapting BlockArgument to accept iterators (which is actually simple)");
}
}
private static class Vec3dLocator<R> extends AbstractLocator<R>
{
private final boolean optionalDirection;
private final boolean optionalEntity;
private final boolean returnVec3d;
public Vec3dLocator(Vec3d annotation, Class<R> type)
{
this.optionalDirection = annotation.optionalDirection();
this.optionalEntity = annotation.optionalEntity();
this.returnVec3d = type == net.minecraft.util.math.Vec3d.class; // Because of the locator
if (returnVec3d && optionalDirection)
throw new IllegalArgumentException("optionalDirection Locator.Vec3d cannot be used for Vec3d type, use Vector3Argument instead");
if (!returnVec3d && type != Vector3Argument.class)
throw new IllegalArgumentException("Locator.Vec3d can only be used in Vector3Argument or Vec3d types");
}
@Override
public String getTypeName()
{
return "position";
}
@Override
public R checkAndConvert(Iterator<Value> valueIterator, Context context, Context.Type theLazyT)
{
Vector3Argument locator = null;
// TODO Make the locator
// return (R) (returnVec3d ? locator.vec : locator);
throw new NotImplementedException("Locator.Vec3d still require adapting Vector3Argument to accept iterators!");
}
}
private static class FunctionLocator<R> extends AbstractLocator<R>
{
private final boolean returnFunctionValue;
private final boolean allowNone;
private final boolean checkArgs;
FunctionLocator(Function annotation, Class<R> type)
{
this.returnFunctionValue = type == FunctionValue.class;
if (!returnFunctionValue && type != FunctionArgument.class)
throw new IllegalArgumentException(
"Params annotated with Locator.Function must be of either FunctionArgument or FunctionValue type");
this.allowNone = annotation.allowNone();
this.checkArgs = annotation.checkArgs();
if (returnFunctionValue && allowNone)
throw new IllegalArgumentException("Cannot use allowNone of Locator.Function in FunctionValue types, use FunctionArgument");
}
@Override
public R checkAndConvert(Iterator<Value> valueIterator, Context context, Context.Type theLazyT)
{
Module module = context.host.main;
FunctionArgument locator = null;
// TODO Make the locator
// return (R) (returnFunctionValue ? locator.function : locator);
throw new NotImplementedException("Locator.FunctionValue still requires adapting the annotation system to somehow get lv size"
+ "or FunctionArgument to not depend on it!");
}
@Override
public String getTypeName()
{
return "function";
}
}
private static abstract class AbstractLocator<R> implements ValueConverter<R>, Locator
{
@Override
public R convert(Value value)
{
throw new UnsupportedOperationException("Cannot call a locator in a parameter that doesn't contain a context!");
}
@Override
public boolean consumesVariableArgs()
{
return true;
}
@Override
public int valueConsumption()
{
return 1;
}
@Override
public abstract R checkAndConvert(Iterator<Value> valueIterator, Context context, Context.Type theLazyT);
}
}
}