Skip to content

Commit

Permalink
linearArg tooling
Browse files Browse the repository at this point in the history
  • Loading branch information
mcmonkey4eva committed Aug 23, 2022
1 parent 70c1f4d commit 005143b
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 64 deletions.
Expand Up @@ -150,11 +150,15 @@ public Argument next() {
@Override
public ArgumentIterator iterator() {
// NOTE: This relies strongly on the assumption of non-async execution for performance benefit.
internal.argumentIterator.index = 0;
internal.argumentIterator.index = internal.actualCommand != null ? internal.actualCommand.linearHandledCount : 0;
internal.argumentIterator.entry = this;
return internal.argumentIterator;
}

public final Argument argAtIndex(boolean isLinear, int index) {
return argAtIndex(isLinear ? internal.arguments_to_use : internal.all_arguments, index);
}

public final Argument argAtIndex(ScriptEntry.InternalArgument[] argSet, int index) {
InternalArgument internalArg = argSet[index];
Argument arg = internalArg.aHArg;
Expand Down
Expand Up @@ -152,6 +152,8 @@ public void addCustomTabCompletions(TabCompletionsBuilder tab) {

public boolean anyPrefixSymbolAllowed = false;

public int linearHandledCount = 0;

public void addRemappedPrefixes(String realName, String... alts) {
Integer oldIndex = prefixesHandled.get(realName);
int index = oldIndex != null ? oldIndex : prefixesThusFar++;
Expand Down
Expand Up @@ -7,6 +7,5 @@

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface LinearArg {
boolean required();
public @interface ArgLinear {
}
@@ -0,0 +1,12 @@
package com.denizenscript.denizencore.scripts.commands.generator;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/** Specifies that the argument should use a raw element, ignoring prefix-parsing. */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface ArgRaw {
}
Expand Up @@ -37,6 +37,7 @@ public interface CommandExecutor {
public static final Method HELPER_BOOLEAN_ARG_METHOD = ReflectionHelper.getMethod(CommandExecutionGenerator.class, "helperBooleanArg", ScriptEntry.class, ArgData.class);
public static final Method HELPER_ENUM_ARG_METHOD = ReflectionHelper.getMethod(CommandExecutionGenerator.class, "helperEnumArg", ScriptEntry.class, ArgData.class);
public static final Method HELPER_PREFIX_STRING_METHOD = ReflectionHelper.getMethod(CommandExecutionGenerator.class, "helperPrefixString", ScriptEntry.class, ArgData.class);
public static final Method HELPER_PREFIX_ELEMENT_METHOD = ReflectionHelper.getMethod(CommandExecutionGenerator.class, "helperPrefixElement", ScriptEntry.class, ArgData.class);
public static final Method HELPER_PREFIX_BOOLEAN_METHOD = ReflectionHelper.getMethod(CommandExecutionGenerator.class, "helperPrefixBoolean", ScriptEntry.class, ArgData.class);
public static final Method HELPER_PREFIX_INTEGER_METHOD = ReflectionHelper.getMethod(CommandExecutionGenerator.class, "helperPrefixInteger", ScriptEntry.class, ArgData.class);
public static final Method HELPER_PREFIX_LONG_METHOD = ReflectionHelper.getMethod(CommandExecutionGenerator.class, "helperPrefixLong", ScriptEntry.class, ArgData.class);
Expand All @@ -50,24 +51,45 @@ public interface CommandExecutor {
public static class ArgData {
public Class type;
public boolean required;
public String prefix;
public String name;
public String defaultValue;
public ObjectTag defaultObject;
public int index;
public boolean isLinear;
public boolean getRaw;
}

public static Argument getArgumentFor(ScriptEntry entry, ArgData arg) {
if (arg.isLinear) {
if (entry.internal.arguments_to_use.length <= arg.index) {
if (arg.required) {
throw new InvalidArgumentsRuntimeException("Must specify input to linear argument '" + arg.name + "'. Did you forget an argument? Check meta docs!");
}
return null;
}
return entry.argAtIndex(true, arg.index);
}
else {
Integer index = entry.internal.prefixedArgMapper[arg.index];
if (index == null) {
if (arg.required) {
throw new InvalidArgumentsRuntimeException("Must specify input to '" + arg.name + "' argument. Did you forget an argument? Check meta docs!");
}
return null;
}
return entry.argAtIndex(false, index);
}
}

/** Used for generated calls. */
public static ObjectTag helperPrefixEntryArg(ScriptEntry entry, ArgData arg) {
Integer index = entry.internal.prefixedArgMapper[arg.index];
if (index == null) {
if (arg.required) {
throw new InvalidArgumentsRuntimeException("Must specify input to '" + arg.prefix + "' argument. Did you forget an argument? Check meta docs!");
}
return null;
Argument givenArg = getArgumentFor(entry, arg);
if (givenArg == null) {
return arg.defaultObject;
}
Argument givenArg = entry.argAtIndex(entry.internal.all_arguments, index);
ObjectTag output = givenArg.asType(arg.type);
if (output == null) {
throw new InvalidArgumentsRuntimeException("Invalid input to '" + arg.prefix + "': '" + givenArg.getValue() + "': not a valid " + arg.type.getSimpleName());
throw new InvalidArgumentsRuntimeException("Invalid input to '" + arg.name + "': '" + givenArg.getValue() + "': not a valid " + arg.type.getSimpleName());
}
return output;
}
Expand All @@ -81,50 +103,55 @@ public static boolean helperBooleanArg(ScriptEntry entry, ArgData arg) {
if (givenArg.rawValue != null) {
return givenArg.rawValue;
}
Argument dynamicArg = entry.argAtIndex(entry.internal.all_arguments, givenArg.argIndex);
Argument dynamicArg = entry.argAtIndex(arg.isLinear, givenArg.argIndex);
ElementTag value = dynamicArg.asElement();
if (CoreUtilities.equalsIgnoreCase(value.asString(), "false")) {
return false;
}
if (CoreUtilities.equalsIgnoreCase(value.asString(), "true")) {
return true;
}
throw new InvalidArgumentsRuntimeException("Input to boolean argument '" + arg.prefix + "' of '" + value + "' is invalid: must specify either 'true' or 'false'!");
throw new InvalidArgumentsRuntimeException("Input to boolean argument '" + arg.name + "' of '" + value + "' is invalid: must specify either 'true' or 'false'!");
}

/** Used for generated calls. */
public static Enum helperEnumArg(ScriptEntry entry, ArgData arg) {
ScriptEntry.EnumArg givenArg = entry.internal.enumVals[arg.index];
if (givenArg == null) {
throw new InvalidArgumentsRuntimeException("Must specify input to '" + arg.prefix + "' argument. Did you forget an argument? Check meta docs!");
}
if (givenArg.rawValue != null) {
return givenArg.rawValue;
}
Argument dynamicArg = entry.argAtIndex(entry.internal.all_arguments, givenArg.argIndex);
ElementTag value = dynamicArg.asElement();
if (value == null && arg.required) {
throw new InvalidArgumentsRuntimeException("Must specify input to '" + arg.prefix + "' argument. Did you forget an argument? Check meta docs!");
}
if (value != null) {
Enum enumVal = value.asEnum(arg.type);
if (enumVal == null) {
throw new InvalidArgumentsRuntimeException("Invalid input to '" + arg.prefix + "' argument. Does not match enum of value options. Must be one of: " + String.join(", ", EnumHelper.get(arg.type).valuesMapLower.keySet()));
if (givenArg != null) {
if (givenArg.rawValue != null) {
return givenArg.rawValue;
}
Argument dynamicArg = entry.argAtIndex(false, givenArg.argIndex);
ElementTag value = dynamicArg.asElement();
if (value != null) {
Enum enumVal = value.asEnum(arg.type);
if (enumVal == null) {
throw new InvalidArgumentsRuntimeException("Invalid input to '" + arg.name + "' argument. Does not match enum of value options. Must be one of: " + String.join(", ", EnumHelper.get(arg.type).valuesMapLower.keySet()));
}
return enumVal;
}
return enumVal;
}
if (arg.required) {
throw new InvalidArgumentsRuntimeException("Must specify input to '" + arg.name + "' argument. Did you forget an argument? Check meta docs!");
}
return arg.defaultValue == null ? null : ElementTag.asEnum(arg.type, arg.defaultValue);
}

public static ElementTag getElementForPrefix(ScriptEntry entry, ArgData arg) {
Integer index = entry.internal.prefixedArgMapper[arg.index];
if (index == null) {
if (arg.required) {
throw new InvalidArgumentsRuntimeException("Must specify input to '" + arg.prefix + "' argument. Did you forget an argument? Check meta docs!");
}
Argument givenArg = getArgumentFor(entry, arg);
if (givenArg == null) {
return null;
}
return entry.argAtIndex(entry.internal.all_arguments, index).asElement();
return arg.getRaw ? givenArg.getRawElement() : givenArg.asElement();
}

/** Used for generated calls. */
public static ElementTag helperPrefixElement(ScriptEntry entry, ArgData arg) {
ElementTag value = getElementForPrefix(entry, arg);
if (value == null) {
return (ElementTag) arg.defaultObject;
}
return value;
}

/** Used for generated calls. */
Expand Down Expand Up @@ -157,7 +184,7 @@ public static boolean helperPrefixBoolean(ScriptEntry entry, ArgData arg) {
if (CoreUtilities.equalsIgnoreCase(value.asString(), "true")) {
return true;
}
throw new InvalidArgumentsRuntimeException("Input to boolean argument '" + arg.prefix + "' of '" + value + "' is invalid: must specify either 'true' or 'false'!");
throw new InvalidArgumentsRuntimeException("Input to boolean argument '" + arg.name + "' of '" + value + "' is invalid: must specify either 'true' or 'false'!");
}

/** Used for generated calls. */
Expand All @@ -175,7 +202,7 @@ public static long helperPrefixLong(ScriptEntry entry, ArgData arg) {
return Long.parseLong(value.cleanedForLong());
}
catch (NumberFormatException ex) {
throw new InvalidArgumentsRuntimeException("Input to integer argument '" + arg.prefix + "' of '" + value + "' is invalid: must specify an integer number!");
throw new InvalidArgumentsRuntimeException("Input to integer argument '" + arg.name + "' of '" + value + "' is invalid: must specify an integer number!");
}
}

Expand All @@ -194,7 +221,7 @@ public static double helperPrefixDouble(ScriptEntry entry, ArgData arg) {
return Double.parseDouble(ElementTag.percentageMatcher.trimToNonMatches(value.asString()));
}
catch (NumberFormatException ex) {
throw new InvalidArgumentsRuntimeException("Input to decimal argument '" + arg.prefix + "' of '" + value + "' is invalid: must specify a decimal number!");
throw new InvalidArgumentsRuntimeException("Input to decimal argument '" + arg.name + "' of '" + value + "' is invalid: must specify a decimal number!");
}
}

Expand All @@ -203,7 +230,7 @@ public static String helperDebugFormat(Object value, ArgData arg) {
if (value == null) {
return "";
}
return ArgumentHelper.debugObj(arg.prefix, value);
return ArgumentHelper.debugObj(arg.name, value);
}

public static CommandExecutor generateExecutorFor(Class<? extends AbstractCommand> cmdClass, AbstractCommand cmd) {
Expand Down Expand Up @@ -238,18 +265,46 @@ public static CommandExecutor generateExecutorFor(Class<? extends AbstractComman
ArgName argName = param.getAnnotation(ArgName.class);
ArgPrefixed argPrefixed = param.getAnnotation(ArgPrefixed.class);
ArgDefaultText argDefaultText = param.getAnnotation(ArgDefaultText.class);
LinearArg linearArg = param.getAnnotation(LinearArg.class);
ArgLinear argLinear = param.getAnnotation(ArgLinear.class);
if (argName == null) {
Debug.echoError("Cannot generate executor for command '" + cmdClass.getName() + "': autoExecute method has param '" + param.getName() + "' which lacks a proper naming parameter.");
return null;
}
MethodGenerator.Local argLocal = gen.addLocal("arg_" + args.size() + "_" + CodeGenUtil.cleanName(argName.value()), paramType);
ArgData argData = new ArgData();
argData.getRaw = param.isAnnotationPresent(ArgRaw.class);
argData.type = paramType;
argData.name = argName.value();
if (!param.isAnnotationPresent(ArgDefaultNull.class)) {
if (argDefaultText == null) {
argData.required = true;
}
else {
argData.defaultValue = argDefaultText.value();
if (ObjectTag.class.isAssignableFrom(argData.type)) {
argData.defaultObject = new ElementTag(argData.defaultValue).asType(argData.type, CoreUtilities.noDebugContext);
if (argData.defaultObject == null) {
Debug.echoError("Cannot generate executor for command '" + cmdClass.getName() + "': autoExecute method has param '" + argData.name
+ "' which specifies default value '" + argData.defaultValue + "' which is a not a valid '" + argData.type.getSimpleName() + "'");
return null;
}
}
}
}
MethodGenerator.Local argLocal = gen.addLocal("arg_" + args.size() + "_" + CodeGenUtil.cleanName(argData.name), paramType);
Method argMethod = null;
boolean doCast = false;
ArgData argData = new ArgData();
if (argPrefixed != null) {
argData.index = cmd.setPrefixHandled(argName.value());
if (ObjectTag.class.isAssignableFrom(paramType)) {
if (argPrefixed != null || argLinear != null) {
if (argPrefixed != null) {
argData.index = cmd.setPrefixHandled(argData.name);
}
else {
argData.index = cmd.linearHandledCount++;
argData.isLinear = true;
}
if (paramType == ElementTag.class) {
argMethod = HELPER_PREFIX_ELEMENT_METHOD;
}
else if (ObjectTag.class.isAssignableFrom(paramType)) {
argMethod = HELPER_PREFIX_ENTRY_ARG_METHOD;
doCast = true;
}
Expand Down Expand Up @@ -278,20 +333,15 @@ else if (paramType == double.class) {
}
else if (paramType == boolean.class) {
argMethod = HELPER_BOOLEAN_ARG_METHOD;
argData = new ArgData();
argData.index = cmd.setBooleanHandled(argName.value());
argData.index = cmd.setBooleanHandled(argData.name);
}
else if (Enum.class.isAssignableFrom(paramType)) {
argMethod = HELPER_ENUM_ARG_METHOD;
argData = new ArgData();
argData.index = cmd.setEnumHandled(argName.value(), (Class<? extends Enum>) paramType);
argData.index = cmd.setEnumHandled(argData.name, (Class<? extends Enum>) paramType);
doCast = true;
}
if (linearArg != null) {
// TODO
}
if (argMethod == null) {
Debug.echoError("Cannot generate executor for command '" + cmdClass.getName() + "': autoExecute method has param '" + argName.value() + "' of type '" + paramType.getName() + "' which is not supported.");
Debug.echoError("Cannot generate executor for command '" + cmdClass.getName() + "': autoExecute method has param '" + argData.name + "' of type '" + paramType.getName() + "' which is not supported.");
return null;
}
gen.loadLocal(scriptEntryLocal);
Expand All @@ -301,16 +351,6 @@ else if (Enum.class.isAssignableFrom(paramType)) {
gen.cast(paramType);
}
gen.storeLocal(argLocal);
argData.type = paramType;
argData.prefix = argName.value();
if (!param.isAnnotationPresent(ArgDefaultNull.class)) {
if (argDefaultText == null) {
argData.required = true;
}
else {
argData.defaultValue = argDefaultText.value();
}
}
argLocals.add(argLocal);
args.add(argData);
}
Expand Down

0 comments on commit 005143b

Please sign in to comment.