Optimized parsing

This commit is contained in:
Nathan Adams 2018-01-16 17:10:54 +01:00
parent 8ed8f02bee
commit 8d4d3cba80
5 changed files with 65 additions and 32 deletions

View file

@ -3,7 +3,7 @@ import groovy.io.FileType
apply plugin: 'java-library'
apply plugin: 'maven'
version = '0.1.17'
version = '0.1.18'
group = 'com.mojang'
task wrapper(type: Wrapper) {

View file

@ -17,10 +17,10 @@ import com.mojang.brigadier.tree.CommandNode;
import com.mojang.brigadier.tree.LiteralCommandNode;
import com.mojang.brigadier.tree.RootCommandNode;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -170,11 +170,11 @@ public class CommandDispatcher<S> {
private ParseResults<S> parseNodes(final CommandNode<S> node, final StringReader originalReader, final CommandContextBuilder<S> contextSoFar) {
final S source = contextSoFar.getSource();
final Map<CommandNode<S>, CommandSyntaxException> errors = Maps.newLinkedHashMap();
Map<CommandNode<S>, CommandSyntaxException> errors = null;
final List<PartialParse<S>> potentials = Lists.newArrayList();
final int cursor = originalReader.getCursor();
for (final CommandNode<S> child : node.getChildren()) {
for (final CommandNode<S> child : node.getRelevantNodes(originalReader)) {
if (!child.canUse(source)) {
continue;
}
@ -192,6 +192,9 @@ public class CommandDispatcher<S> {
}
}
} catch (final CommandSyntaxException ex) {
if (errors == null) {
errors = new LinkedHashMap<>();
}
errors.put(child, ex);
reader.setCursor(cursor);
continue;
@ -216,27 +219,32 @@ public class CommandDispatcher<S> {
}
if (!potentials.isEmpty()) {
final List<PartialParse<S>> sorted = Lists.newArrayList(potentials);
sorted.sort((a, b) -> {
if (!a.parse.getReader().canRead() && b.parse.getReader().canRead()) {
return -1;
}
if (a.parse.getReader().canRead() && !b.parse.getReader().canRead()) {
return 1;
}
if (a.parse.getExceptions().isEmpty() && !b.parse.getExceptions().isEmpty()) {
return -1;
}
if (!a.parse.getExceptions().isEmpty() && b.parse.getExceptions().isEmpty()) {
return 1;
}
return 0;
});
final PartialParse<S> likely = sorted.get(0);
final PartialParse<S> likely;
if (potentials.size() > 1) {
final List<PartialParse<S>> sorted = Lists.newArrayList(potentials);
sorted.sort((a, b) -> {
if (!a.parse.getReader().canRead() && b.parse.getReader().canRead()) {
return -1;
}
if (a.parse.getReader().canRead() && !b.parse.getReader().canRead()) {
return 1;
}
if (a.parse.getExceptions().isEmpty() && !b.parse.getExceptions().isEmpty()) {
return -1;
}
if (!a.parse.getExceptions().isEmpty() && b.parse.getExceptions().isEmpty()) {
return 1;
}
return 0;
});
likely = sorted.get(0);
} else {
likely = potentials.get(0);
}
return likely.parse;
}
return new ParseResults<>(contextSoFar, originalReader, errors);
return new ParseResults<>(contextSoFar, originalReader, errors == null ? Collections.emptyMap() : errors);
}
public String[] getAllUsage(final CommandNode<S> node, final S source, final boolean restricted) {

View file

@ -13,6 +13,7 @@ import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
@ -21,6 +22,8 @@ import java.util.stream.Collectors;
public abstract class CommandNode<S> implements Comparable<CommandNode<S>> {
private Map<String, CommandNode<S>> children = Maps.newLinkedHashMap();
private Map<String, LiteralCommandNode<S>> literals = Maps.newLinkedHashMap();
private Map<String, ArgumentCommandNode<S, ?>> arguments = Maps.newLinkedHashMap();
private final Predicate<S> requirement;
private final CommandNode<S> redirect;
private final RedirectModifier<S> modifier;
@ -73,6 +76,11 @@ public abstract class CommandNode<S> implements Comparable<CommandNode<S>> {
}
} else {
children.put(node.getName(), node);
if (node instanceof LiteralCommandNode) {
literals.put(node.getName(), (LiteralCommandNode<S>) node);
} else if (node instanceof ArgumentCommandNode) {
arguments.put(node.getName(), (ArgumentCommandNode<S, ?>) node);
}
}
children = children.entrySet().stream().sorted(Map.Entry.comparingByValue()).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
@ -112,6 +120,22 @@ public abstract class CommandNode<S> implements Comparable<CommandNode<S>> {
protected abstract String getSortedKey();
public Collection<? extends CommandNode<S>> getRelevantNodes(final StringReader input) {
if (literals.size() > 0) {
final int cursor = input.getCursor();
final String text = input.readUnquotedString();
input.setCursor(cursor);
final LiteralCommandNode<S> literal = literals.get(text);
if (literal != null) {
return Collections.singleton(literal);
} else {
return arguments.values();
}
} else {
return arguments.values();
}
}
@Override
public int compareTo(final CommandNode<S> o) {
return ComparisonChain

View file

@ -37,16 +37,17 @@ public class LiteralCommandNode<S> extends CommandNode<S> {
@Override
public void parse(final StringReader reader, final CommandContextBuilder<S> contextBuilder) throws CommandSyntaxException {
final int start = reader.getCursor();
for (int i = 0; i < literal.length(); i++) {
if (reader.canRead() && reader.peek() == literal.charAt(i)) {
reader.skip();
} else {
reader.setCursor(start);
throw ERROR_INCORRECT_LITERAL.createWithContext(reader, literal);
if (reader.canRead(literal.length())) {
final int end = start + literal.length();
if (reader.getString().substring(start, end).equals(literal)) {
reader.setCursor(end);
contextBuilder.withNode(this, StringRange.between(start, end));
return;
}
}
contextBuilder.withNode(this, StringRange.between(start, reader.getCursor()));
reader.setCursor(start);
throw ERROR_INCORRECT_LITERAL.createWithContext(reader, literal);
}
@Override

View file

@ -130,8 +130,8 @@ public class CommandDispatcherTest {
subject.execute("foo baz", source);
fail();
} catch (final CommandSyntaxException ex) {
assertThat(ex.getType(), is(LiteralCommandNode.ERROR_INCORRECT_LITERAL));
assertThat(ex.getData(), is(Collections.singletonMap("expected", "bar")));
assertThat(ex.getType(), is(CommandDispatcher.ERROR_UNKNOWN_ARGUMENT));
assertThat(ex.getData(), is(Collections.emptyMap()));
assertThat(ex.getCursor(), is(4));
}
}
@ -332,7 +332,7 @@ public class CommandDispatcherTest {
subject.register(literal("foo").then(argument("bar", integer()).executes(command)));
try {
subject.execute("foo5", source);
subject.execute("foo$", source);
fail();
} catch (final CommandSyntaxException ex) {
assertThat(ex.getType(), is(CommandDispatcher.ERROR_EXPECTED_ARGUMENT_SEPARATOR));