* 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
286 lines
8.7 KiB
Java
286 lines
8.7 KiB
Java
package carpet.script;
|
|
|
|
import carpet.CarpetSettings;
|
|
import carpet.script.exception.ExpressionException;
|
|
import carpet.script.exception.InternalExpressionException;
|
|
import carpet.script.value.FunctionUnpackedArgumentsValue;
|
|
import carpet.script.value.ListValue;
|
|
import carpet.script.value.Value;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
|
|
public abstract class Fluff
|
|
{
|
|
@FunctionalInterface
|
|
public interface TriFunction<A, B, C, R> { R apply(A a, B b, C c); }
|
|
|
|
@FunctionalInterface
|
|
public interface TriConsumer<A, B, C> { void accept(A a, B b, C c); }
|
|
|
|
@FunctionalInterface
|
|
public interface QuadConsumer<A, B, C, D> { void accept(A a, B b, C c, D d); }
|
|
|
|
@FunctionalInterface
|
|
public interface QuadFunction<A, B, C, D, R> { R apply(A a, B b, C c, D d);}
|
|
|
|
@FunctionalInterface
|
|
public interface QuinnFunction<A, B, C, D, E, R> { R apply(A a, B b, C c, D d, E e);}
|
|
|
|
@FunctionalInterface
|
|
public interface SexFunction<A, B, C, D, E, F, R> { R apply(A a, B b, C c, D d, E e, F f);}
|
|
|
|
public interface EvalNode
|
|
{
|
|
/**
|
|
* @return true if function has constant output if arguments are constant and can be evaluated
|
|
* statically (without context)
|
|
*/
|
|
boolean pure();
|
|
|
|
/**
|
|
* @return true if function has constant output if arguments are constant and can be evaluated
|
|
* statically (without context)
|
|
*/
|
|
boolean transitive();
|
|
|
|
/**
|
|
* @return required argument eval type in case its evaluated statically without context but with a given context type
|
|
*/
|
|
default Context.Type staticType(Context.Type outerType) {return transitive()?outerType:Context.NONE;};
|
|
}
|
|
|
|
public interface ILazyFunction extends EvalNode
|
|
{
|
|
int getNumParams();
|
|
|
|
boolean numParamsVaries();
|
|
|
|
LazyValue lazyEval(Context c, Context.Type type, Expression expr, Tokenizer.Token token, List<LazyValue> lazyParams);
|
|
|
|
static void checkInterrupts()
|
|
{
|
|
if (ScriptHost.mainThread != Thread.currentThread() && Thread.currentThread().isInterrupted())
|
|
throw new InternalExpressionException("Thread interrupted");
|
|
}
|
|
// lazy function has a chance to change execution based on contxt
|
|
}
|
|
|
|
public interface IFunction extends ILazyFunction
|
|
{
|
|
Value eval(List<Value> parameters);
|
|
}
|
|
|
|
public interface ILazyOperator extends EvalNode
|
|
{
|
|
int getPrecedence();
|
|
|
|
boolean isLeftAssoc();
|
|
|
|
LazyValue lazyEval(Context c, Context.Type type, Expression e, Tokenizer.Token t, LazyValue v1, LazyValue v2);
|
|
}
|
|
|
|
public interface IOperator extends ILazyOperator
|
|
{
|
|
Value eval(Value v1, Value v2);
|
|
}
|
|
|
|
public abstract static class AbstractLazyFunction implements ILazyFunction
|
|
{
|
|
protected String name;
|
|
int numParams;
|
|
|
|
public AbstractLazyFunction(int numParams, String name)
|
|
{
|
|
this.numParams = numParams;
|
|
this.name = name;
|
|
}
|
|
|
|
|
|
public String getName() {
|
|
return name;
|
|
}
|
|
|
|
public int getNumParams() {
|
|
return numParams;
|
|
}
|
|
|
|
public boolean numParamsVaries() {
|
|
return numParams < 0;
|
|
}
|
|
|
|
public static List<Value> unpackLazy(List<LazyValue> lzargs, Context c, Context.Type contextType)
|
|
{
|
|
List<Value> args = new ArrayList<>();
|
|
for (LazyValue lv : lzargs)
|
|
{
|
|
Value arg = lv.evalValue(c, contextType);
|
|
if (arg instanceof FunctionUnpackedArgumentsValue)
|
|
args.addAll(((ListValue) arg).getItems());
|
|
else
|
|
args.add(arg);
|
|
}
|
|
return args;
|
|
}
|
|
|
|
public List<Value> unpackArgs(List<LazyValue> lzargs, Context c, Context.Type contextType)
|
|
{
|
|
List<Value> args = unpackLazy(lzargs, c, contextType);
|
|
if (!numParamsVaries() && getNumParams() != args.size())
|
|
throw new InternalExpressionException("Function " + getName() + " expected " + getNumParams() + " parameters, got " + args.size());
|
|
return args;
|
|
}
|
|
|
|
public static List<LazyValue> lazify(List<Value> args)
|
|
{
|
|
List<LazyValue> lzargs = new ArrayList<>(args.size());
|
|
args.forEach( v -> lzargs.add( (c, t) -> v));
|
|
return lzargs;
|
|
}
|
|
}
|
|
|
|
public abstract static class AbstractFunction extends AbstractLazyFunction implements IFunction
|
|
{
|
|
AbstractFunction(int numParams, String name) {
|
|
super(numParams, name);
|
|
}
|
|
|
|
@Override
|
|
public boolean pure() {
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public boolean transitive() {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public LazyValue lazyEval(Context cc, Context.Type type, Expression e, Tokenizer.Token t, final List<LazyValue> lazyParams)
|
|
{
|
|
|
|
return new LazyValue()
|
|
{ // eager evaluation always ignores the required type and evals params by none default
|
|
private List<Value> params;
|
|
public Value evalValue(Context c, Context.Type type)
|
|
{
|
|
ILazyFunction.checkInterrupts();
|
|
try
|
|
{
|
|
return AbstractFunction.this.eval(getParams(c));
|
|
}
|
|
catch (RuntimeException exc)
|
|
{
|
|
throw Expression.handleCodeException(cc, exc, e, t);
|
|
}
|
|
}
|
|
private List<Value> getParams(Context c)
|
|
{
|
|
if (params == null)
|
|
{
|
|
// very likely needs to be dynamic, so not static like here, or remember if it was.
|
|
params = unpackArgs(lazyParams, c, Context.Type.NONE);
|
|
}
|
|
else
|
|
{
|
|
CarpetSettings.LOG.error("How did we get here 1");
|
|
}
|
|
return params;
|
|
}
|
|
};
|
|
}
|
|
}
|
|
|
|
public abstract static class AbstractLazyOperator implements ILazyOperator
|
|
{
|
|
int precedence;
|
|
|
|
boolean leftAssoc;
|
|
|
|
AbstractLazyOperator(int precedence, boolean leftAssoc) {
|
|
this.precedence = precedence;
|
|
this.leftAssoc = leftAssoc;
|
|
}
|
|
|
|
public int getPrecedence() {
|
|
return precedence;
|
|
}
|
|
|
|
public boolean isLeftAssoc() {
|
|
return leftAssoc;
|
|
}
|
|
|
|
}
|
|
|
|
public abstract static class AbstractOperator extends AbstractLazyOperator implements IOperator
|
|
{
|
|
|
|
AbstractOperator(int precedence, boolean leftAssoc) {
|
|
super(precedence, leftAssoc);
|
|
}
|
|
@Override
|
|
public boolean pure() {
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public boolean transitive() {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public LazyValue lazyEval(Context cc, Context.Type type, Expression e, Tokenizer.Token t, final LazyValue v1, final LazyValue v2)
|
|
{
|
|
try
|
|
{
|
|
return (c, type_ignored) -> AbstractOperator.this.eval(v1.evalValue(c, Context.Type.NONE), v2.evalValue(c, Context.Type.NONE));
|
|
}
|
|
catch (RuntimeException exc)
|
|
{
|
|
throw Expression.handleCodeException(cc, exc, e, t);
|
|
}
|
|
}
|
|
}
|
|
|
|
public abstract static class AbstractUnaryOperator extends AbstractOperator
|
|
{
|
|
AbstractUnaryOperator(int precedence, boolean leftAssoc) {
|
|
super(precedence, leftAssoc);
|
|
}
|
|
@Override
|
|
public boolean pure() {
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public boolean transitive() {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public LazyValue lazyEval(Context cc, Context.Type type, Expression e, Tokenizer.Token t, final LazyValue v1, final LazyValue v2)
|
|
{
|
|
try
|
|
{
|
|
if (v2 != null)
|
|
{
|
|
throw new ExpressionException(cc, e, t, "Did not expect a second parameter for unary operator");
|
|
}
|
|
return (c, ignored_type) -> AbstractUnaryOperator.this.evalUnary(v1.evalValue(c, Context.Type.NONE));
|
|
}
|
|
catch (RuntimeException exc)
|
|
{
|
|
throw Expression.handleCodeException(cc, exc, e, t);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public Value eval(Value v1, Value v2)
|
|
{
|
|
throw new RuntimeException("Shouldn't end up here");
|
|
}
|
|
|
|
public abstract Value evalUnary(Value v1);
|
|
}
|
|
}
|