Skip to content

Commit

Permalink
Added functionality for finding ambiguities on-the-fly
Browse files Browse the repository at this point in the history
  • Loading branch information
Dinnerbone committed Jan 30, 2018
1 parent 3498398 commit 69c2a24
Show file tree
Hide file tree
Showing 18 changed files with 194 additions and 40 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.19'
version = '0.1.21'
group = 'com.mojang'

task wrapper(type: Wrapper) {
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/com/mojang/brigadier/AmbiguityConsumer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.mojang.brigadier;

import com.mojang.brigadier.tree.CommandNode;

import java.util.Collection;

@FunctionalInterface
public interface AmbiguityConsumer<S> {
void ambiguous(final CommandNode<S> parent, final CommandNode<S> child, final CommandNode<S> sibling, final Collection<String> inputs);
}
4 changes: 4 additions & 0 deletions src/main/java/com/mojang/brigadier/CommandDispatcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,10 @@ public CommandNode<S> findNode(final Collection<String> path) {
return node;
}

public void findAmbiguities(final AmbiguityConsumer<S> consumer) {
root.findAmbiguities(consumer);
}

private void addPaths(final CommandNode<S> node, final List<List<CommandNode<S>>> result, final List<CommandNode<S>> parents) {
final List<CommandNode<S>> current = new ArrayList<>(parents);
current.add(node);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,18 @@
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;

import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.CompletableFuture;

public interface ArgumentType<T> {
<S> T parse(StringReader reader, CommandContextBuilder<S> contextBuilder) throws CommandSyntaxException;
<S> T parse(StringReader reader) throws CommandSyntaxException;

default <S> CompletableFuture<Suggestions> listSuggestions(final CommandContext<S> context, final SuggestionsBuilder builder) {
return Suggestions.empty();
}

default Collection<String> getExamples() {
return Collections.emptyList();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,13 @@
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;

import java.util.Arrays;
import java.util.Collection;
import java.util.concurrent.CompletableFuture;

public class BoolArgumentType implements ArgumentType<Boolean> {
private static final Collection<String> EXAMPLES = Arrays.asList("true", "false");

private BoolArgumentType() {
}

Expand All @@ -22,7 +26,7 @@ public static boolean getBool(final CommandContext<?> context, final String name
}

@Override
public <S> Boolean parse(final StringReader reader, final CommandContextBuilder<S> contextBuilder) throws CommandSyntaxException {
public <S> Boolean parse(final StringReader reader) throws CommandSyntaxException {
return reader.readBoolean();
}

Expand All @@ -36,4 +40,9 @@ public <S> CompletableFuture<Suggestions> listSuggestions(final CommandContext<S
}
return builder.buildFuture();
}

@Override
public Collection<String> getExamples() {
return EXAMPLES;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,13 @@
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.exceptions.ParameterizedCommandExceptionType;

import java.util.Arrays;
import java.util.Collection;

public class DoubleArgumentType implements ArgumentType<Double> {
public static final ParameterizedCommandExceptionType ERROR_TOO_SMALL = new ParameterizedCommandExceptionType("argument.double.low", "Double must not be less than ${minimum}, found ${found}", "found", "minimum");
public static final ParameterizedCommandExceptionType ERROR_TOO_BIG = new ParameterizedCommandExceptionType("argument.double.big", "Double must not be more than ${maximum}, found ${found}", "found", "maximum");
private static final Collection<String> EXAMPLES = Arrays.asList("0", "1.2", ".5", "-1", "-.5", "-1234.56");

private final double minimum;
private final double maximum;
Expand Down Expand Up @@ -43,7 +47,7 @@ public double getMaximum() {
}

@Override
public <S> Double parse(final StringReader reader, final CommandContextBuilder<S> contextBuilder) throws CommandSyntaxException {
public <S> Double parse(final StringReader reader) throws CommandSyntaxException {
final int start = reader.getCursor();
final double result = reader.readDouble();
if (result < minimum) {
Expand Down Expand Up @@ -81,4 +85,9 @@ public String toString() {
return "double(" + minimum + ", " + maximum + ")";
}
}

@Override
public Collection<String> getExamples() {
return EXAMPLES;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,13 @@
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.exceptions.ParameterizedCommandExceptionType;

import java.util.Arrays;
import java.util.Collection;

public class FloatArgumentType implements ArgumentType<Float> {
public static final ParameterizedCommandExceptionType ERROR_TOO_SMALL = new ParameterizedCommandExceptionType("argument.float.low", "Float must not be less than ${minimum}, found ${found}", "found", "minimum");
public static final ParameterizedCommandExceptionType ERROR_TOO_BIG = new ParameterizedCommandExceptionType("argument.float.big", "Float must not be more than ${maximum}, found ${found}", "found", "maximum");
private static final Collection<String> EXAMPLES = Arrays.asList("0", "1.2", ".5", "-1", "-.5", "-1234.56");

private final float minimum;
private final float maximum;
Expand Down Expand Up @@ -43,7 +47,7 @@ public float getMaximum() {
}

@Override
public <S> Float parse(final StringReader reader, final CommandContextBuilder<S> contextBuilder) throws CommandSyntaxException {
public <S> Float parse(final StringReader reader) throws CommandSyntaxException {
final int start = reader.getCursor();
final float result = reader.readFloat();
if (result < minimum) {
Expand Down Expand Up @@ -81,4 +85,9 @@ public String toString() {
return "float(" + minimum + ", " + maximum + ")";
}
}

@Override
public Collection<String> getExamples() {
return EXAMPLES;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,13 @@
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.exceptions.ParameterizedCommandExceptionType;

import java.util.Arrays;
import java.util.Collection;

public class IntegerArgumentType implements ArgumentType<Integer> {
public static final ParameterizedCommandExceptionType ERROR_TOO_SMALL = new ParameterizedCommandExceptionType("argument.integer.low", "Integer must not be less than ${minimum}, found ${found}", "found", "minimum");
public static final ParameterizedCommandExceptionType ERROR_TOO_BIG = new ParameterizedCommandExceptionType("argument.integer.big", "Integer must not be more than ${maximum}, found ${found}", "found", "maximum");
private static final Collection<String> EXAMPLES = Arrays.asList("0", "123", "-123");

private final int minimum;
private final int maximum;
Expand Down Expand Up @@ -43,7 +47,7 @@ public int getMaximum() {
}

@Override
public <S> Integer parse(final StringReader reader, final CommandContextBuilder<S> contextBuilder) throws CommandSyntaxException {
public <S> Integer parse(final StringReader reader) throws CommandSyntaxException {
final int start = reader.getCursor();
final int result = reader.readInt();
if (result < minimum) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package com.mojang.brigadier.arguments;

import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.context.CommandContextBuilder;
import com.mojang.brigadier.exceptions.CommandSyntaxException;

import java.util.Arrays;
import java.util.Collection;

public class StringArgumentType implements ArgumentType<String> {
private final StringType type;

Expand Down Expand Up @@ -34,7 +36,7 @@ public StringType getType() {
}

@Override
public <S> String parse(final StringReader reader, final CommandContextBuilder<S> contextBuilder) throws CommandSyntaxException {
public <S> String parse(final StringReader reader) throws CommandSyntaxException {
if (type == StringType.GREEDY_PHRASE) {
final String text = reader.getRemaining();
reader.setCursor(reader.getTotalLength());
Expand All @@ -51,6 +53,11 @@ public String toString() {
return "string()";
}

@Override
public Collection<String> getExamples() {
return type.getExamples();
}

public static String escapeIfRequired(final String input) {
for (final char c : input.toCharArray()) {
if (!StringReader.isAllowedInUnquotedString(c)) {
Expand All @@ -76,8 +83,18 @@ private static String escape(final String input) {
}

public enum StringType {
SINGLE_WORD,
QUOTABLE_PHRASE,
GREEDY_PHRASE,
SINGLE_WORD("word", "words_with_underscores"),
QUOTABLE_PHRASE("\"quoted phrase\"", "word", "\"\""),
GREEDY_PHRASE("word", "words with spaces", "\"and symbols\""),;

private final Collection<String> examples;

StringType(final String... examples) {
this.examples = Arrays.asList(examples);
}

public Collection<String> getExamples() {
return examples;
}
}
}
19 changes: 18 additions & 1 deletion src/main/java/com/mojang/brigadier/tree/ArgumentCommandNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;

import java.util.Collection;
import java.util.concurrent.CompletableFuture;
import java.util.function.Predicate;

Expand Down Expand Up @@ -52,7 +53,7 @@ public SuggestionProvider<S> getCustomSuggestions() {
@Override
public void parse(final StringReader reader, final CommandContextBuilder<S> contextBuilder) throws CommandSyntaxException {
final int start = reader.getCursor();
final T result = type.parse(reader, contextBuilder);
final T result = type.parse(reader);
final ParsedArgument<S, T> parsed = new ParsedArgument<>(start, reader.getCursor(), result);

contextBuilder.withArgument(name, parsed);
Expand Down Expand Up @@ -80,6 +81,17 @@ public RequiredArgumentBuilder<S, T> createBuilder() {
return builder;
}

@Override
public boolean isValidInput(final String input) {
try {
final StringReader reader = new StringReader(input);
type.parse(reader);
return !reader.canRead() || reader.peek() == ' ';
} catch (final CommandSyntaxException ignored) {
return false;
}
}

@Override
public boolean equals(final Object o) {
if (this == o) return true;
Expand All @@ -103,4 +115,9 @@ public int hashCode() {
protected String getSortedKey() {
return name;
}

@Override
public Collection<String> getExamples() {
return type.getExamples();
}
}
44 changes: 44 additions & 0 deletions src/main/java/com/mojang/brigadier/tree/CommandNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import com.google.common.collect.ComparisonChain;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.mojang.brigadier.AmbiguityConsumer;
import com.mojang.brigadier.Command;
import com.mojang.brigadier.RedirectModifier;
import com.mojang.brigadier.StringReader;
Expand All @@ -16,6 +18,7 @@
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.function.Predicate;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -77,6 +80,18 @@ public void addChild(final CommandNode<S> node) {
child.addChild(grandchild);
}
} else {
for (final CommandNode<S> sibling : children.values()) {
for (final String example : node.getExamples()) {
if (sibling.isValidInput(example)) {
System.out.println("Ambiguity detected in " + getName() + ", siblings " + sibling.getName() + " and " + node.getName() + " can both parse '" + example + "' successfully");
}
}
for (final String example : sibling.getExamples()) {
if (node.isValidInput(example)) {
System.out.println("Ambiguity detected in " + getName() + ", siblings " + sibling.getName() + " and " + node.getName() + " can both parse '" + example + "' successfully");
}
}
}
children.put(node.getName(), node);
if (node instanceof LiteralCommandNode) {
literals.put(node.getName(), (LiteralCommandNode<S>) node);
Expand All @@ -88,6 +103,33 @@ public void addChild(final CommandNode<S> node) {
children = children.entrySet().stream().sorted(Map.Entry.comparingByValue()).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
}

public void findAmbiguities(final AmbiguityConsumer<S> consumer) {
Set<String> matches = Sets.newHashSet();

for (final CommandNode<S> child : children.values()) {
for (final CommandNode<S> sibling : children.values()) {
if (child == sibling) {
continue;
}

for (final String input : child.getExamples()) {
if (sibling.isValidInput(input)) {
matches.add(input);
}
}

if (matches.size() > 0) {
consumer.ambiguous(this, child, sibling, matches);
matches = Sets.newHashSet();
}
}

child.findAmbiguities(consumer);
}
}

protected abstract boolean isValidInput(final String input);

@Override
public boolean equals(final Object o) {
if (this == o) return true;
Expand Down Expand Up @@ -153,4 +195,6 @@ public int compareTo(final CommandNode<S> o) {
public boolean isFork() {
return forks;
}

public abstract Collection<String> getExamples();
}
Loading

0 comments on commit 69c2a24

Please sign in to comment.