Optimized parsing
This commit is contained in:
+1
-1
@@ -3,7 +3,7 @@ import groovy.io.FileType
|
|||||||
apply plugin: 'java-library'
|
apply plugin: 'java-library'
|
||||||
apply plugin: 'maven'
|
apply plugin: 'maven'
|
||||||
|
|
||||||
version = '0.1.17'
|
version = '0.1.18'
|
||||||
group = 'com.mojang'
|
group = 'com.mojang'
|
||||||
|
|
||||||
task wrapper(type: Wrapper) {
|
task wrapper(type: Wrapper) {
|
||||||
|
|||||||
@@ -17,10 +17,10 @@ import com.mojang.brigadier.tree.CommandNode;
|
|||||||
import com.mojang.brigadier.tree.LiteralCommandNode;
|
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||||
import com.mojang.brigadier.tree.RootCommandNode;
|
import com.mojang.brigadier.tree.RootCommandNode;
|
||||||
|
|
||||||
import java.lang.reflect.Array;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
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) {
|
private ParseResults<S> parseNodes(final CommandNode<S> node, final StringReader originalReader, final CommandContextBuilder<S> contextSoFar) {
|
||||||
final S source = contextSoFar.getSource();
|
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 List<PartialParse<S>> potentials = Lists.newArrayList();
|
||||||
final int cursor = originalReader.getCursor();
|
final int cursor = originalReader.getCursor();
|
||||||
|
|
||||||
for (final CommandNode<S> child : node.getChildren()) {
|
for (final CommandNode<S> child : node.getRelevantNodes(originalReader)) {
|
||||||
if (!child.canUse(source)) {
|
if (!child.canUse(source)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -192,6 +192,9 @@ public class CommandDispatcher<S> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (final CommandSyntaxException ex) {
|
} catch (final CommandSyntaxException ex) {
|
||||||
|
if (errors == null) {
|
||||||
|
errors = new LinkedHashMap<>();
|
||||||
|
}
|
||||||
errors.put(child, ex);
|
errors.put(child, ex);
|
||||||
reader.setCursor(cursor);
|
reader.setCursor(cursor);
|
||||||
continue;
|
continue;
|
||||||
@@ -216,6 +219,8 @@ public class CommandDispatcher<S> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!potentials.isEmpty()) {
|
if (!potentials.isEmpty()) {
|
||||||
|
final PartialParse<S> likely;
|
||||||
|
if (potentials.size() > 1) {
|
||||||
final List<PartialParse<S>> sorted = Lists.newArrayList(potentials);
|
final List<PartialParse<S>> sorted = Lists.newArrayList(potentials);
|
||||||
sorted.sort((a, b) -> {
|
sorted.sort((a, b) -> {
|
||||||
if (!a.parse.getReader().canRead() && b.parse.getReader().canRead()) {
|
if (!a.parse.getReader().canRead() && b.parse.getReader().canRead()) {
|
||||||
@@ -232,11 +237,14 @@ public class CommandDispatcher<S> {
|
|||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
final PartialParse<S> likely = sorted.get(0);
|
likely = sorted.get(0);
|
||||||
|
} else {
|
||||||
|
likely = potentials.get(0);
|
||||||
|
}
|
||||||
return likely.parse;
|
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) {
|
public String[] getAllUsage(final CommandNode<S> node, final S source, final boolean restricted) {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import com.mojang.brigadier.suggestion.Suggestions;
|
|||||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
@@ -21,6 +22,8 @@ import java.util.stream.Collectors;
|
|||||||
|
|
||||||
public abstract class CommandNode<S> implements Comparable<CommandNode<S>> {
|
public abstract class CommandNode<S> implements Comparable<CommandNode<S>> {
|
||||||
private Map<String, CommandNode<S>> children = Maps.newLinkedHashMap();
|
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 Predicate<S> requirement;
|
||||||
private final CommandNode<S> redirect;
|
private final CommandNode<S> redirect;
|
||||||
private final RedirectModifier<S> modifier;
|
private final RedirectModifier<S> modifier;
|
||||||
@@ -73,6 +76,11 @@ public abstract class CommandNode<S> implements Comparable<CommandNode<S>> {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
children.put(node.getName(), node);
|
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));
|
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();
|
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
|
@Override
|
||||||
public int compareTo(final CommandNode<S> o) {
|
public int compareTo(final CommandNode<S> o) {
|
||||||
return ComparisonChain
|
return ComparisonChain
|
||||||
|
|||||||
@@ -37,16 +37,17 @@ public class LiteralCommandNode<S> extends CommandNode<S> {
|
|||||||
@Override
|
@Override
|
||||||
public void parse(final StringReader reader, final CommandContextBuilder<S> contextBuilder) throws CommandSyntaxException {
|
public void parse(final StringReader reader, final CommandContextBuilder<S> contextBuilder) throws CommandSyntaxException {
|
||||||
final int start = reader.getCursor();
|
final int start = reader.getCursor();
|
||||||
for (int i = 0; i < literal.length(); i++) {
|
if (reader.canRead(literal.length())) {
|
||||||
if (reader.canRead() && reader.peek() == literal.charAt(i)) {
|
final int end = start + literal.length();
|
||||||
reader.skip();
|
if (reader.getString().substring(start, end).equals(literal)) {
|
||||||
} else {
|
reader.setCursor(end);
|
||||||
reader.setCursor(start);
|
contextBuilder.withNode(this, StringRange.between(start, end));
|
||||||
throw ERROR_INCORRECT_LITERAL.createWithContext(reader, literal);
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
contextBuilder.withNode(this, StringRange.between(start, reader.getCursor()));
|
reader.setCursor(start);
|
||||||
|
throw ERROR_INCORRECT_LITERAL.createWithContext(reader, literal);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -130,8 +130,8 @@ public class CommandDispatcherTest {
|
|||||||
subject.execute("foo baz", source);
|
subject.execute("foo baz", source);
|
||||||
fail();
|
fail();
|
||||||
} catch (final CommandSyntaxException ex) {
|
} catch (final CommandSyntaxException ex) {
|
||||||
assertThat(ex.getType(), is(LiteralCommandNode.ERROR_INCORRECT_LITERAL));
|
assertThat(ex.getType(), is(CommandDispatcher.ERROR_UNKNOWN_ARGUMENT));
|
||||||
assertThat(ex.getData(), is(Collections.singletonMap("expected", "bar")));
|
assertThat(ex.getData(), is(Collections.emptyMap()));
|
||||||
assertThat(ex.getCursor(), is(4));
|
assertThat(ex.getCursor(), is(4));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -332,7 +332,7 @@ public class CommandDispatcherTest {
|
|||||||
subject.register(literal("foo").then(argument("bar", integer()).executes(command)));
|
subject.register(literal("foo").then(argument("bar", integer()).executes(command)));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
subject.execute("foo5", source);
|
subject.execute("foo$", source);
|
||||||
fail();
|
fail();
|
||||||
} catch (final CommandSyntaxException ex) {
|
} catch (final CommandSyntaxException ex) {
|
||||||
assertThat(ex.getType(), is(CommandDispatcher.ERROR_EXPECTED_ARGUMENT_SEPARATOR));
|
assertThat(ex.getType(), is(CommandDispatcher.ERROR_EXPECTED_ARGUMENT_SEPARATOR));
|
||||||
|
|||||||
Reference in New Issue
Block a user