From 8d4d3cba800244b0ac8215ac3c57a2edf3ed7828 Mon Sep 17 00:00:00 2001 From: Nathan Adams Date: Tue, 16 Jan 2018 17:10:54 +0100 Subject: [PATCH] Optimized parsing --- build.gradle | 2 +- .../mojang/brigadier/CommandDispatcher.java | 50 +++++++++++-------- .../mojang/brigadier/tree/CommandNode.java | 24 +++++++++ .../brigadier/tree/LiteralCommandNode.java | 15 +++--- .../brigadier/CommandDispatcherTest.java | 6 +-- 5 files changed, 65 insertions(+), 32 deletions(-) diff --git a/build.gradle b/build.gradle index 033576b..e1b4a33 100644 --- a/build.gradle +++ b/build.gradle @@ -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) { diff --git a/src/main/java/com/mojang/brigadier/CommandDispatcher.java b/src/main/java/com/mojang/brigadier/CommandDispatcher.java index 54bb9b6..74c7a80 100644 --- a/src/main/java/com/mojang/brigadier/CommandDispatcher.java +++ b/src/main/java/com/mojang/brigadier/CommandDispatcher.java @@ -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 { private ParseResults parseNodes(final CommandNode node, final StringReader originalReader, final CommandContextBuilder contextSoFar) { final S source = contextSoFar.getSource(); - final Map, CommandSyntaxException> errors = Maps.newLinkedHashMap(); + Map, CommandSyntaxException> errors = null; final List> potentials = Lists.newArrayList(); final int cursor = originalReader.getCursor(); - for (final CommandNode child : node.getChildren()) { + for (final CommandNode child : node.getRelevantNodes(originalReader)) { if (!child.canUse(source)) { continue; } @@ -192,6 +192,9 @@ public class CommandDispatcher { } } } 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 { } if (!potentials.isEmpty()) { - final List> 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 likely = sorted.get(0); + final PartialParse likely; + if (potentials.size() > 1) { + final List> 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 node, final S source, final boolean restricted) { diff --git a/src/main/java/com/mojang/brigadier/tree/CommandNode.java b/src/main/java/com/mojang/brigadier/tree/CommandNode.java index c4eef81..2d5e949 100644 --- a/src/main/java/com/mojang/brigadier/tree/CommandNode.java +++ b/src/main/java/com/mojang/brigadier/tree/CommandNode.java @@ -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 implements Comparable> { private Map> children = Maps.newLinkedHashMap(); + private Map> literals = Maps.newLinkedHashMap(); + private Map> arguments = Maps.newLinkedHashMap(); private final Predicate requirement; private final CommandNode redirect; private final RedirectModifier modifier; @@ -73,6 +76,11 @@ public abstract class CommandNode implements Comparable> { } } else { children.put(node.getName(), node); + if (node instanceof LiteralCommandNode) { + literals.put(node.getName(), (LiteralCommandNode) node); + } else if (node instanceof ArgumentCommandNode) { + arguments.put(node.getName(), (ArgumentCommandNode) 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 implements Comparable> { protected abstract String getSortedKey(); + public Collection> getRelevantNodes(final StringReader input) { + if (literals.size() > 0) { + final int cursor = input.getCursor(); + final String text = input.readUnquotedString(); + input.setCursor(cursor); + final LiteralCommandNode 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 o) { return ComparisonChain diff --git a/src/main/java/com/mojang/brigadier/tree/LiteralCommandNode.java b/src/main/java/com/mojang/brigadier/tree/LiteralCommandNode.java index 393ae12..b94440f 100644 --- a/src/main/java/com/mojang/brigadier/tree/LiteralCommandNode.java +++ b/src/main/java/com/mojang/brigadier/tree/LiteralCommandNode.java @@ -37,16 +37,17 @@ public class LiteralCommandNode extends CommandNode { @Override public void parse(final StringReader reader, final CommandContextBuilder 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 diff --git a/src/test/java/com/mojang/brigadier/CommandDispatcherTest.java b/src/test/java/com/mojang/brigadier/CommandDispatcherTest.java index e20bc95..1d4870e 100644 --- a/src/test/java/com/mojang/brigadier/CommandDispatcherTest.java +++ b/src/test/java/com/mojang/brigadier/CommandDispatcherTest.java @@ -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));