Separate the dispatcher from the parsing algorithm

This commit is contained in:
themode 2021-02-11 04:18:19 +01:00
parent 86908fbd0e
commit e9d91fd21e
5 changed files with 167 additions and 129 deletions

View File

@ -4,6 +4,9 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectRBTreeMap;
import net.minestom.server.command.CommandSender;
import net.minestom.server.command.builder.arguments.Argument;
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
import net.minestom.server.command.builder.parser.CommandParser;
import net.minestom.server.command.builder.parser.CommandSuggestionHolder;
import net.minestom.server.command.builder.parser.ValidSyntaxHolder;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -152,111 +155,7 @@ public class CommandDispatcher {
Int2ObjectRBTreeMap<CommandSuggestionHolder> syntaxesSuggestions = new Int2ObjectRBTreeMap<>(Collections.reverseOrder());
for (CommandSyntax syntax : syntaxes) {
// List of all arguments in the current syntax
final Argument<?>[] arguments = syntax.getArguments();
// Empty object containing the maximum amount of argument results of the syntax.
final List<Object> argsValues = new ArrayList<>(arguments.length);
boolean syntaxCorrect = true;
// The current index in the raw command string arguments
int splitIndex = 0;
boolean useRemaining = false;
// Check the validity of the arguments...
for (int argCount = 0; argCount < arguments.length; argCount++) {
final boolean lastArgumentIteration = argCount + 1 == arguments.length;
final Argument<?> argument = arguments[argCount];
useRemaining = argument.useRemaining();
// the parsed argument value, null if incorrect
Object parsedValue;
// the argument exception, null if the input is correct
ArgumentSyntaxException argumentSyntaxException = null;
// true if the arg is valid, false otherwise
boolean correct = false;
// the raw string representing the correct argument syntax
StringBuilder argValue = new StringBuilder();
if (useRemaining) {
final boolean hasArgs = args.length > splitIndex;
// Verify if there is any string part available
if (hasArgs) {
// Argument is supposed to take the rest of the command input
for (int i = splitIndex; i < args.length; i++) {
final String arg = args[i];
if (argValue.length() > 0)
argValue.append(StringUtils.SPACE);
argValue.append(arg);
}
final String argValueString = argValue.toString();
try {
parsedValue = argument.parse(argValueString);
correct = true;
argsValues.add(parsedValue);
} catch (ArgumentSyntaxException exception) {
argumentSyntaxException = exception;
}
}
} else {
// Argument is either single-word or can accept optional delimited space(s)
for (int i = splitIndex; i < args.length; i++) {
final String rawArg = args[i];
argValue.append(rawArg);
final String argValueString = argValue.toString();
try {
parsedValue = argument.parse(argValueString);
// Prevent quitting the parsing too soon if the argument
// does not allow space
if (lastArgumentIteration && i + 1 < args.length) {
if (!argument.allowSpace())
break;
argValue.append(StringUtils.SPACE);
continue;
}
correct = true;
argsValues.add(parsedValue);
splitIndex = i + 1;
break;
} catch (ArgumentSyntaxException exception) {
argumentSyntaxException = exception;
if (!argument.allowSpace())
break;
argValue.append(StringUtils.SPACE);
}
}
}
if (!correct) {
// Argument is not correct, add it to the syntax suggestion with the number
// of correct argument(s) and do not check the next syntax argument
syntaxCorrect = false;
CommandSuggestionHolder suggestionHolder = new CommandSuggestionHolder();
suggestionHolder.syntax = syntax;
suggestionHolder.argumentSyntaxException = argumentSyntaxException;
suggestionHolder.argIndex = argCount;
syntaxesSuggestions.put(argCount, suggestionHolder);
break;
}
}
// Add the syntax to the list of valid syntaxes if correct
if (syntaxCorrect) {
if (arguments.length == argsValues.size() || useRemaining) {
ValidSyntaxHolder validSyntaxHolder = new ValidSyntaxHolder();
validSyntaxHolder.syntax = syntax;
validSyntaxHolder.argumentsValue = argsValues;
validSyntaxes.add(validSyntaxHolder);
}
}
CommandParser.parse(syntax, syntax.getArguments(), args, validSyntaxes, syntaxesSuggestions);
}
// Check if there is at least one correct syntax
@ -319,7 +218,7 @@ public class CommandDispatcher {
final Argument<?>[] arguments = syntax.getArguments();
final int argumentsCount = arguments.length;
final List<Object> argsValues = validSyntaxHolder.argumentsValue;
final Map<Argument<?>, Object> argsValues = validSyntaxHolder.argumentsValue;
final int argsSize = argsValues.size();
@ -329,9 +228,9 @@ public class CommandDispatcher {
// Fill arguments map
Arguments syntaxValues = new Arguments();
for (int i = 0; i < argumentsCount; i++) {
final Argument<?> argument = arguments[i];
final Object argumentValue = argsValues.get(i);
for (Map.Entry<Argument<?>, Object> entry : argsValues.entrySet()) {
final Argument<?> argument = entry.getKey();
final Object argumentValue = entry.getValue();
syntaxValues.setArg(argument.getId(), argumentValue);
}
@ -346,24 +245,4 @@ public class CommandDispatcher {
return finalSyntax;
}
/**
* Holds the data of a validated syntax.
*/
private static class ValidSyntaxHolder {
private CommandSyntax syntax;
/**
* (Argument index/Argument parsed object)
*/
private List<Object> argumentsValue;
}
/**
* Holds the data of an invalidated syntax.
*/
private static class CommandSuggestionHolder {
private CommandSyntax syntax;
private ArgumentSyntaxException argumentSyntaxException;
private int argIndex;
}
}

View File

@ -22,6 +22,10 @@ public class ArgumentType {
return new ArgumentLiteral(id);
}
public static ArgumentGroup Group(@NotNull String id, @NotNull Argument<?>... arguments) {
return new ArgumentGroup(id, arguments);
}
public static ArgumentBoolean Boolean(@NotNull String id) {
return new ArgumentBoolean(id);
}

View File

@ -0,0 +1,128 @@
package net.minestom.server.command.builder.parser;
import it.unimi.dsi.fastutil.ints.Int2ObjectRBTreeMap;
import net.minestom.server.command.builder.CommandSyntax;
import net.minestom.server.command.builder.arguments.Argument;
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class CommandParser {
public static void parse(@NotNull CommandSyntax syntax, @NotNull Argument<?>[] commandArguments, @NotNull String[] inputArguments,
@Nullable List<ValidSyntaxHolder> validSyntaxes,
@Nullable Int2ObjectRBTreeMap<CommandSuggestionHolder> syntaxesSuggestions) {
final Map<Argument<?>, Object> argsValues = new HashMap<>();
boolean syntaxCorrect = true;
// The current index in the raw command string arguments
int splitIndex = 0;
boolean useRemaining = false;
// Check the validity of the arguments...
for (int argCount = 0; argCount < commandArguments.length; argCount++) {
final boolean lastArgumentIteration = argCount + 1 == commandArguments.length;
final Argument<?> argument = commandArguments[argCount];
useRemaining = argument.useRemaining();
// the parsed argument value, null if incorrect
Object parsedValue;
// the argument exception, null if the input is correct
ArgumentSyntaxException argumentSyntaxException = null;
// true if the arg is valid, false otherwise
boolean correct = false;
// the raw string representing the correct argument syntax
StringBuilder argValue = new StringBuilder();
if (useRemaining) {
final boolean hasArgs = inputArguments.length > splitIndex;
// Verify if there is any string part available
if (hasArgs) {
// Argument is supposed to take the rest of the command input
for (int i = splitIndex; i < inputArguments.length; i++) {
final String arg = inputArguments[i];
if (argValue.length() > 0)
argValue.append(StringUtils.SPACE);
argValue.append(arg);
}
final String argValueString = argValue.toString();
try {
parsedValue = argument.parse(argValueString);
correct = true;
argsValues.put(argument, parsedValue);
} catch (ArgumentSyntaxException exception) {
argumentSyntaxException = exception;
}
}
} else {
// Argument is either single-word or can accept optional delimited space(s)
for (int i = splitIndex; i < inputArguments.length; i++) {
final String rawArg = inputArguments[i];
argValue.append(rawArg);
final String argValueString = argValue.toString();
try {
parsedValue = argument.parse(argValueString);
// Prevent quitting the parsing too soon if the argument
// does not allow space
if (lastArgumentIteration && i + 1 < inputArguments.length) {
if (!argument.allowSpace())
break;
argValue.append(StringUtils.SPACE);
continue;
}
correct = true;
argsValues.put(argument, parsedValue);
splitIndex = i + 1;
break;
} catch (ArgumentSyntaxException exception) {
argumentSyntaxException = exception;
if (!argument.allowSpace())
break;
argValue.append(StringUtils.SPACE);
}
}
}
if (!correct) {
// Argument is not correct, add it to the syntax suggestion with the number
// of correct argument(s) and do not check the next syntax argument
syntaxCorrect = false;
if (syntaxesSuggestions != null) {
CommandSuggestionHolder suggestionHolder = new CommandSuggestionHolder();
suggestionHolder.syntax = syntax;
suggestionHolder.argumentSyntaxException = argumentSyntaxException;
suggestionHolder.argIndex = argCount;
syntaxesSuggestions.put(argCount, suggestionHolder);
}
break;
}
}
// Add the syntax to the list of valid syntaxes if correct
if (syntaxCorrect) {
if (commandArguments.length == argsValues.size() || useRemaining) {
if (validSyntaxes != null) {
ValidSyntaxHolder validSyntaxHolder = new ValidSyntaxHolder();
validSyntaxHolder.syntax = syntax;
validSyntaxHolder.argumentsValue = argsValues;
validSyntaxes.add(validSyntaxHolder);
}
}
}
}
}

View File

@ -0,0 +1,13 @@
package net.minestom.server.command.builder.parser;
import net.minestom.server.command.builder.CommandSyntax;
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
/**
* Holds the data of an invalidated syntax.
*/
public class CommandSuggestionHolder {
public CommandSyntax syntax;
public ArgumentSyntaxException argumentSyntaxException;
public int argIndex;
}

View File

@ -0,0 +1,14 @@
package net.minestom.server.command.builder.parser;
import net.minestom.server.command.builder.CommandSyntax;
import net.minestom.server.command.builder.arguments.Argument;
import java.util.Map;
/**
* Holds the data of a validated syntax.
*/
public class ValidSyntaxHolder {
public CommandSyntax syntax;
public Map<Argument<?>, Object> argumentsValue;
}