Skip to content

Commit

Permalink
Optimized parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
Dinnerbone committed Jan 16, 2018
1 parent 8ed8f02 commit 8d4d3cb
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 32 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
50 changes: 29 additions & 21 deletions src/main/java/com/mojang/brigadier/CommandDispatcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@
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;
Expand Down Expand Up @@ -170,11 +170,11 @@ private PartialParse(final CommandContextBuilder<S> context, final ParseResults<

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;
}
Expand All @@ -192,6 +192,9 @@ private ParseResults<S> parseNodes(final CommandNode<S> node, final StringReader
}
}
} catch (final CommandSyntaxException ex) {
if (errors == null) {
errors = new LinkedHashMap<>();
}
errors.put(child, ex);
reader.setCursor(cursor);
continue;
Expand All @@ -216,27 +219,32 @@ private ParseResults<S> parseNodes(final CommandNode<S> node, final StringReader
}

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) {
Expand Down
24 changes: 24 additions & 0 deletions src/main/java/com/mojang/brigadier/tree/CommandNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
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;
Expand All @@ -21,6 +22,8 @@

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;
Expand Down Expand Up @@ -73,6 +76,11 @@ public void addChild(final CommandNode<S> node) {
}
} 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));
Expand Down Expand Up @@ -112,6 +120,22 @@ public Predicate<S> getRequirement() {

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
Expand Down
15 changes: 8 additions & 7 deletions src/main/java/com/mojang/brigadier/tree/LiteralCommandNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,17 @@ public String getName() {
@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
Expand Down
6 changes: 3 additions & 3 deletions src/test/java/com/mojang/brigadier/CommandDispatcherTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,8 @@ public void testExecuteIncorrectLiteral() throws Exception {
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));
}
}
Expand Down Expand Up @@ -332,7 +332,7 @@ public void parse_noSpaceSeparator() throws Exception {
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));
Expand Down

0 comments on commit 8d4d3cb

Please sign in to comment.