mirror of
https://github.com/Xiao-MoMi/craft-engine.git
synced 2025-12-19 15:09:15 +00:00
改进语言输出
This commit is contained in:
@@ -9,7 +9,7 @@ import net.momirealms.craftengine.core.util.GsonHelper;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.Pair;
|
||||
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
|
||||
import net.momirealms.craftengine.core.util.snbt.TagParser;
|
||||
import net.momirealms.craftengine.core.util.TagParser;
|
||||
import net.momirealms.sparrow.nbt.CompoundTag;
|
||||
import net.momirealms.sparrow.nbt.Tag;
|
||||
|
||||
@@ -47,7 +47,7 @@ public class ComponentsModifier<I> implements ItemDataModifier<I> {
|
||||
String snbt = string.substring("(snbt) ".length());
|
||||
try {
|
||||
return TagParser.parseTagFully(snbt);
|
||||
} catch (CommandSyntaxException e) {
|
||||
} catch (Exception e) {
|
||||
throw new LocalizedResourceConfigException("warning.config.type.snbt.invalid_syntax", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package net.momirealms.craftengine.core.plugin.config.template;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import net.momirealms.craftengine.core.plugin.config.template.argument.TemplateArgument;
|
||||
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
|
||||
import net.momirealms.craftengine.core.util.snbt.TagParser;
|
||||
import net.momirealms.craftengine.core.util.TagParser;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -71,7 +71,7 @@ public interface ArgumentString {
|
||||
Object parsed;
|
||||
try {
|
||||
parsed = TagParser.parseObjectFully(defaultValueString);
|
||||
} catch (CommandSyntaxException e) {
|
||||
} catch (Exception e) {
|
||||
throw new LocalizedResourceConfigException("warning.config.type.snbt.invalid_syntax", e.getMessage());
|
||||
}
|
||||
try {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,91 +0,0 @@
|
||||
package net.momirealms.craftengine.core.util.snbt;
|
||||
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.serialization.DynamicOps;
|
||||
import net.momirealms.craftengine.core.util.snbt.parse.*;
|
||||
import net.momirealms.sparrow.nbt.util.UUIDUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class SnbtOperations {
|
||||
static final DelayedException<CommandSyntaxException> ERROR_EXPECTED_STRING_UUID = DelayedException.create(
|
||||
new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.expected_string_uuid"))
|
||||
);
|
||||
static final DelayedException<CommandSyntaxException> ERROR_EXPECTED_NUMBER_OR_BOOLEAN = DelayedException.create(
|
||||
new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.expected_number_or_boolean"))
|
||||
);
|
||||
public static final String BUILTIN_TRUE = "true";
|
||||
public static final String BUILTIN_FALSE = "false";
|
||||
public static final String BUILTIN_NULL = "null";
|
||||
public static final Map<BuiltinKey, BuiltinOperation> BUILTIN_OPERATIONS = Map.of(
|
||||
new BuiltinKey("bool", 1), new BuiltinOperation() {
|
||||
@Override
|
||||
public <T> T run(DynamicOps<T> ops, List<T> arguments, ParseState<StringReader> state) {
|
||||
Boolean result = convert(ops, arguments.getFirst());
|
||||
if (result == null) {
|
||||
state.errorCollector().store(state.mark(), SnbtOperations.ERROR_EXPECTED_NUMBER_OR_BOOLEAN);
|
||||
return null;
|
||||
}
|
||||
return ops.createBoolean(result);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static <T> Boolean convert(DynamicOps<T> ops, T arg) {
|
||||
Optional<Boolean> asBoolean = ops.getBooleanValue(arg).result();
|
||||
if (asBoolean.isPresent()) {
|
||||
return asBoolean.get();
|
||||
} else {
|
||||
Optional<Number> asNumber = ops.getNumberValue(arg).result();
|
||||
return asNumber.isPresent() ? asNumber.get().doubleValue() != 0.0 : null;
|
||||
}
|
||||
}
|
||||
}, new BuiltinKey("uuid", 1), new BuiltinOperation() {
|
||||
@Override
|
||||
public <T> T run(DynamicOps<T> ops, List<T> arguments, ParseState<StringReader> state) {
|
||||
Optional<String> arg = ops.getStringValue(arguments.getFirst()).result();
|
||||
if (arg.isEmpty()) {
|
||||
state.errorCollector().store(state.mark(), SnbtOperations.ERROR_EXPECTED_STRING_UUID);
|
||||
return null;
|
||||
}
|
||||
UUID uuid;
|
||||
try {
|
||||
uuid = UUID.fromString(arg.get());
|
||||
} catch (IllegalArgumentException var7) {
|
||||
state.errorCollector().store(state.mark(), SnbtOperations.ERROR_EXPECTED_STRING_UUID);
|
||||
return null;
|
||||
}
|
||||
|
||||
return ops.createIntList(IntStream.of(UUIDUtil.uuidToIntArray(uuid)));
|
||||
}
|
||||
}
|
||||
);
|
||||
public static final SuggestionSupplier<StringReader> BUILTIN_IDS = new SuggestionSupplier<>() {
|
||||
private final Set<String> keys = Stream.concat(
|
||||
Stream.of(BUILTIN_FALSE, BUILTIN_TRUE, BUILTIN_NULL), SnbtOperations.BUILTIN_OPERATIONS.keySet().stream().map(BuiltinKey::id)
|
||||
)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
@Override
|
||||
public Stream<String> possibleValues(ParseState<StringReader> state) {
|
||||
return this.keys.stream();
|
||||
}
|
||||
};
|
||||
|
||||
public record BuiltinKey(String id, int argCount) {
|
||||
@Override
|
||||
public @NotNull String toString() {
|
||||
return this.id + "/" + this.argCount;
|
||||
}
|
||||
}
|
||||
|
||||
public interface BuiltinOperation {
|
||||
@Nullable
|
||||
<T> T run(DynamicOps<T> ops, List<T> arguments, ParseState<StringReader> state);
|
||||
}
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
package net.momirealms.craftengine.core.util.snbt;
|
||||
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
|
||||
import com.mojang.serialization.DynamicOps;
|
||||
import com.mojang.serialization.JavaOps;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import net.momirealms.craftengine.core.util.snbt.parse.LocalizedMessage;
|
||||
import net.momirealms.craftengine.core.util.snbt.parse.LocalizedSimpleCommandExceptionType;
|
||||
import net.momirealms.craftengine.core.util.snbt.parse.grammar.Grammar;
|
||||
import net.momirealms.sparrow.nbt.CompoundTag;
|
||||
import net.momirealms.sparrow.nbt.Tag;
|
||||
import net.momirealms.sparrow.nbt.codec.LegacyJavaOps;
|
||||
import net.momirealms.sparrow.nbt.codec.LegacyNBTOps;
|
||||
import net.momirealms.sparrow.nbt.codec.NBTOps;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class TagParser<T> {
|
||||
public static final SimpleCommandExceptionType ERROR_TRAILING_DATA = new LocalizedSimpleCommandExceptionType(
|
||||
new LocalizedMessage("warning.config.type.snbt.parser.trailing")
|
||||
);
|
||||
public static final SimpleCommandExceptionType ERROR_EXPECTED_COMPOUND = new LocalizedSimpleCommandExceptionType(
|
||||
new LocalizedMessage("warning.config.type.snbt.parser.expected.compound")
|
||||
);
|
||||
public static final char ELEMENT_SEPARATOR = ',';
|
||||
public static final char NAME_VALUE_SEPARATOR = ':';
|
||||
public static final TagParser<Tag> NBT_OPS_PARSER = create(VersionHelper.isOrAbove1_20_5() ? NBTOps.INSTANCE : LegacyNBTOps.INSTANCE);
|
||||
public static final TagParser<Object> JAVA_OPS_PARSER = create(VersionHelper.isOrAbove1_20_5() ? JavaOps.INSTANCE : LegacyJavaOps.INSTANCE);
|
||||
private final DynamicOps<T> ops;
|
||||
private final Grammar<T> grammar;
|
||||
|
||||
private TagParser(DynamicOps<T> ops, Grammar<T> grammar) {
|
||||
this.ops = ops;
|
||||
this.grammar = grammar;
|
||||
}
|
||||
|
||||
public DynamicOps<T> ops() {
|
||||
return this.ops;
|
||||
}
|
||||
|
||||
public static <T> TagParser<T> create(DynamicOps<T> ops) {
|
||||
return new TagParser<>(ops, SnbtGrammar.createParser(ops));
|
||||
}
|
||||
|
||||
private static CompoundTag castToCompoundOrThrow(StringReader reader, Tag result) throws CommandSyntaxException {
|
||||
if (result instanceof CompoundTag compoundTag) {
|
||||
return compoundTag;
|
||||
}
|
||||
throw ERROR_EXPECTED_COMPOUND.createWithContext(reader);
|
||||
}
|
||||
public static CompoundTag parseCompoundFully(String input) throws CommandSyntaxException {
|
||||
StringReader reader = new StringReader(input);
|
||||
Tag result = NBT_OPS_PARSER.parseFully(reader);
|
||||
return castToCompoundOrThrow(reader, result);
|
||||
}
|
||||
|
||||
public static Tag parseTagFully(String input) throws CommandSyntaxException {
|
||||
StringReader reader = new StringReader(input);
|
||||
return NBT_OPS_PARSER.parseFully(reader);
|
||||
}
|
||||
|
||||
public static Object parseObjectFully(String input) throws CommandSyntaxException {
|
||||
StringReader reader = new StringReader(input);
|
||||
return JAVA_OPS_PARSER.parseFully(reader);
|
||||
}
|
||||
|
||||
public T parseFully(String input) throws CommandSyntaxException {
|
||||
return this.parseFully(new StringReader(input));
|
||||
}
|
||||
|
||||
public T parseFully(StringReader reader) throws CommandSyntaxException {
|
||||
T result = this.grammar.parse(reader);
|
||||
reader.skipWhitespace();
|
||||
if (reader.canRead()) {
|
||||
throw ERROR_TRAILING_DATA.createWithContext(reader);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public T parseAsArgument(StringReader reader) throws CommandSyntaxException {
|
||||
return this.grammar.parse(reader);
|
||||
}
|
||||
|
||||
public static CompoundTag parseCompoundAsArgument(StringReader reader) throws CommandSyntaxException {
|
||||
Tag result = parseTagAsArgument(reader);
|
||||
return castToCompoundOrThrow(reader, result);
|
||||
}
|
||||
|
||||
public static Tag parseTagAsArgument(StringReader reader) throws CommandSyntaxException {
|
||||
return NBT_OPS_PARSER.parseAsArgument(reader);
|
||||
}
|
||||
|
||||
public static Object parseObjectAsArgument(StringReader reader) throws CommandSyntaxException {
|
||||
return JAVA_OPS_PARSER.parseAsArgument(reader);
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package net.momirealms.craftengine.core.util.snbt.parse;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public record Atom<T>(String name) {
|
||||
@Override
|
||||
public @NotNull String toString() {
|
||||
return "<" + this.name + ">";
|
||||
}
|
||||
|
||||
public static <T> Atom<T> of(String name) {
|
||||
return new Atom<>(name);
|
||||
}
|
||||
}
|
||||
@@ -1,235 +0,0 @@
|
||||
package net.momirealms.craftengine.core.util.snbt.parse;
|
||||
|
||||
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public abstract class CachedParseState<S> implements ParseState<S> {
|
||||
private PositionCache[] positionCache = new PositionCache[256];
|
||||
private final ErrorCollector<S> errorCollector;
|
||||
private final Scope scope = new Scope();
|
||||
private SimpleControl[] controlCache = new SimpleControl[16];
|
||||
private int nextControlToReturn;
|
||||
private final Silent silent = new Silent();
|
||||
public static final Object JAVA_NULL_VALUE_MARKER = new Object() {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "null";
|
||||
}
|
||||
};
|
||||
|
||||
protected CachedParseState(ErrorCollector<S> errorCollector) {
|
||||
this.errorCollector = errorCollector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Scope scope() {
|
||||
return this.scope;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ErrorCollector<S> errorCollector() {
|
||||
return this.errorCollector;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public <T> T parse(NamedRule<S, T> rule) {
|
||||
int markBeforeParse = this.mark();
|
||||
PositionCache positionCache = this.getCacheForPosition(markBeforeParse);
|
||||
int entryIndex = positionCache.findKeyIndex(rule.name());
|
||||
if (entryIndex != -1) {
|
||||
CacheEntry<T> value = positionCache.getValue(entryIndex);
|
||||
if (value != null) {
|
||||
if (value == CachedParseState.CacheEntry.NEGATIVE) {
|
||||
return null;
|
||||
}
|
||||
this.restore(value.markAfterParse);
|
||||
return value.value;
|
||||
}
|
||||
} else {
|
||||
entryIndex = positionCache.allocateNewEntry(rule.name());
|
||||
}
|
||||
|
||||
T result = rule.value().parse(this);
|
||||
CacheEntry<T> entry;
|
||||
if (result == null) {
|
||||
entry = CacheEntry.negativeEntry();
|
||||
} else {
|
||||
int markAfterParse = this.mark();
|
||||
entry = new CacheEntry<>(result, markAfterParse);
|
||||
}
|
||||
|
||||
positionCache.setValue(entryIndex, entry);
|
||||
return result;
|
||||
}
|
||||
|
||||
private PositionCache getCacheForPosition(int index) {
|
||||
int currentSize = this.positionCache.length;
|
||||
if (index >= currentSize) {
|
||||
int newSize = MiscUtils.growByHalf(currentSize, index + 1);
|
||||
PositionCache[] newCache = new PositionCache[newSize];
|
||||
System.arraycopy(this.positionCache, 0, newCache, 0, currentSize);
|
||||
this.positionCache = newCache;
|
||||
}
|
||||
|
||||
PositionCache result = this.positionCache[index];
|
||||
if (result == null) {
|
||||
result = new PositionCache();
|
||||
this.positionCache[index] = result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Control acquireControl() {
|
||||
int currentSize = this.controlCache.length;
|
||||
if (this.nextControlToReturn >= currentSize) {
|
||||
int newSize = MiscUtils.growByHalf(currentSize, this.nextControlToReturn + 1);
|
||||
SimpleControl[] newControlCache = new SimpleControl[newSize];
|
||||
System.arraycopy(this.controlCache, 0, newControlCache, 0, currentSize);
|
||||
this.controlCache = newControlCache;
|
||||
}
|
||||
|
||||
int controlIndex = this.nextControlToReturn++;
|
||||
SimpleControl entry = this.controlCache[controlIndex];
|
||||
if (entry == null) {
|
||||
entry = new SimpleControl();
|
||||
this.controlCache[controlIndex] = entry;
|
||||
} else {
|
||||
entry.reset();
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void releaseControl() {
|
||||
this.nextControlToReturn--;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParseState<S> silent() {
|
||||
return this.silent;
|
||||
}
|
||||
|
||||
record CacheEntry<T>(@Nullable T value, int markAfterParse) {
|
||||
public static final CacheEntry<?> NEGATIVE = new CacheEntry<>(null, -1);
|
||||
|
||||
public static <T> CacheEntry<T> negativeEntry() {
|
||||
return (CacheEntry<T>) NEGATIVE;
|
||||
}
|
||||
}
|
||||
|
||||
static class PositionCache {
|
||||
public static final int ENTRY_STRIDE = 2;
|
||||
private static final int NOT_FOUND = -1;
|
||||
private Object[] atomCache = new Object[16];
|
||||
private int nextKey;
|
||||
|
||||
public int findKeyIndex(Atom<?> key) {
|
||||
for (int i = 0; i < this.nextKey; i += ENTRY_STRIDE) {
|
||||
if (this.atomCache[i] == key) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return NOT_FOUND;
|
||||
}
|
||||
|
||||
public int allocateNewEntry(Atom<?> key) {
|
||||
int newKeyIndex = this.nextKey;
|
||||
this.nextKey += ENTRY_STRIDE;
|
||||
int newValueIndex = newKeyIndex + 1;
|
||||
int currentSize = this.atomCache.length;
|
||||
if (newValueIndex >= currentSize) {
|
||||
int newSize = MiscUtils.growByHalf(currentSize, newValueIndex + 1);
|
||||
Object[] newCache = new Object[newSize];
|
||||
System.arraycopy(this.atomCache, 0, newCache, 0, currentSize);
|
||||
this.atomCache = newCache;
|
||||
}
|
||||
|
||||
this.atomCache[newKeyIndex] = key;
|
||||
return newKeyIndex;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public <T> CacheEntry<T> getValue(int keyIndex) {
|
||||
return (CacheEntry<T>) this.atomCache[keyIndex + 1];
|
||||
}
|
||||
|
||||
public void setValue(int keyIndex, CacheEntry<?> entry) {
|
||||
this.atomCache[keyIndex + 1] = entry;
|
||||
}
|
||||
}
|
||||
|
||||
class Silent implements ParseState<S> {
|
||||
private final ErrorCollector<S> silentCollector = new ErrorCollector.Nop<>();
|
||||
|
||||
@Override
|
||||
public ErrorCollector<S> errorCollector() {
|
||||
return this.silentCollector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Scope scope() {
|
||||
return CachedParseState.this.scope();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public <T> T parse(NamedRule<S, T> rule) {
|
||||
return CachedParseState.this.parse(rule);
|
||||
}
|
||||
|
||||
@Override
|
||||
public S input() {
|
||||
return CachedParseState.this.input();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int mark() {
|
||||
return CachedParseState.this.mark();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restore(int mark) {
|
||||
CachedParseState.this.restore(mark);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Control acquireControl() {
|
||||
return CachedParseState.this.acquireControl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void releaseControl() {
|
||||
CachedParseState.this.releaseControl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParseState<S> silent() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
static class SimpleControl implements Control {
|
||||
private boolean hasCut;
|
||||
|
||||
@Override
|
||||
public void cut() {
|
||||
this.hasCut = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasCut() {
|
||||
return this.hasCut;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
this.hasCut = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package net.momirealms.craftengine.core.util.snbt.parse;
|
||||
|
||||
public interface Control {
|
||||
Control UNBOUND = new Control() {
|
||||
@Override
|
||||
public void cut() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasCut() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
void cut();
|
||||
|
||||
boolean hasCut();
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
package net.momirealms.craftengine.core.util.snbt.parse;
|
||||
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
|
||||
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
|
||||
import net.momirealms.craftengine.core.util.snbt.parse.grammar.StringReaderTerms;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface DelayedException<T extends Exception> {
|
||||
T create(String contents, int position);
|
||||
|
||||
static DelayedException<CommandSyntaxException> create(SimpleCommandExceptionType type) {
|
||||
return (contents, position) -> type.createWithContext(StringReaderTerms.createReader(contents, position));
|
||||
}
|
||||
|
||||
static DelayedException<CommandSyntaxException> create(DynamicCommandExceptionType type, String argument) {
|
||||
return (contents, position) -> type.createWithContext(StringReaderTerms.createReader(contents, position), argument);
|
||||
}
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
package net.momirealms.craftengine.core.util.snbt.parse;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class Dictionary<S> {
|
||||
private final Map<Atom<?>, Entry<S, ?>> terms = new IdentityHashMap<>();
|
||||
|
||||
public <T> NamedRule<S, T> put(Atom<T> name, Rule<S, T> entry) {
|
||||
Entry<S, T> holder = (Entry<S, T>)this.terms.computeIfAbsent(name, Entry::new);
|
||||
if (holder.value != null) {
|
||||
throw new IllegalArgumentException("Trying to override rule: " + name);
|
||||
}
|
||||
holder.value = entry;
|
||||
return holder;
|
||||
}
|
||||
|
||||
public <T> NamedRule<S, T> putComplex(Atom<T> name, Term<S> term, Rule.RuleAction<S, T> action) {
|
||||
return this.put(name, Rule.fromTerm(term, action));
|
||||
}
|
||||
|
||||
public <T> NamedRule<S, T> put(Atom<T> name, Term<S> term, Rule.SimpleRuleAction<S, T> action) {
|
||||
return this.put(name, Rule.fromTerm(term, action));
|
||||
}
|
||||
|
||||
public void checkAllBound() {
|
||||
List<? extends Atom<?>> unboundNames = this.terms.entrySet().stream()
|
||||
.filter(entry -> entry.getValue() == null)
|
||||
.map(Map.Entry::getKey)
|
||||
.toList();
|
||||
if (!unboundNames.isEmpty()) {
|
||||
throw new IllegalStateException("Unbound names: " + unboundNames);
|
||||
}
|
||||
}
|
||||
|
||||
public <T> NamedRule<S, T> forward(Atom<T> name) {
|
||||
return this.getOrCreateEntry(name);
|
||||
}
|
||||
|
||||
private <T> Entry<S, T> getOrCreateEntry(Atom<T> name) {
|
||||
return (Entry<S, T>)this.terms.computeIfAbsent(name, Entry::new);
|
||||
}
|
||||
|
||||
public <T> Term<S> named(Atom<T> name) {
|
||||
return new Reference<>(this.getOrCreateEntry(name), name);
|
||||
}
|
||||
|
||||
public <T> Term<S> namedWithAlias(Atom<T> nameToParse, Atom<T> nameToStore) {
|
||||
return new Reference<>(this.getOrCreateEntry(nameToParse), nameToStore);
|
||||
}
|
||||
|
||||
static class Entry<S, T> implements NamedRule<S, T>, Supplier<String> {
|
||||
private final Atom<T> name;
|
||||
@Nullable
|
||||
Rule<S, T> value;
|
||||
|
||||
private Entry(Atom<T> name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Atom<T> name() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rule<S, T> value() {
|
||||
return Objects.requireNonNull(this.value, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get() {
|
||||
return "Unbound rule " + this.name;
|
||||
}
|
||||
}
|
||||
|
||||
record Reference<S, T>(Entry<S, T> ruleToParse, Atom<T> nameToStore) implements Term<S> {
|
||||
@Override
|
||||
public boolean parse(ParseState<S> state, Scope scope, Control control) {
|
||||
T result = state.parse(this.ruleToParse);
|
||||
if (result == null) {
|
||||
return false;
|
||||
}
|
||||
scope.put(this.nameToStore, result);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
package net.momirealms.craftengine.core.util.snbt.parse;
|
||||
|
||||
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public interface ErrorCollector<S> {
|
||||
void store(int cursor, SuggestionSupplier<S> suggestions, Object reason);
|
||||
|
||||
default void store(int cursor, Object reason) {
|
||||
this.store(cursor, SuggestionSupplier.empty(), reason);
|
||||
}
|
||||
|
||||
void finish(int finalCursor);
|
||||
|
||||
class LongestOnly<S> implements ErrorCollector<S> {
|
||||
private MutableErrorEntry<S>[] entries = new MutableErrorEntry[16];
|
||||
private int nextErrorEntry;
|
||||
private int lastCursor = -1;
|
||||
|
||||
private void discardErrorsFromShorterParse(int cursor) {
|
||||
if (cursor > this.lastCursor) {
|
||||
this.lastCursor = cursor;
|
||||
this.nextErrorEntry = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finish(int finalCursor) {
|
||||
this.discardErrorsFromShorterParse(finalCursor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void store(int cursor, SuggestionSupplier<S> suggestions, Object reason) {
|
||||
this.discardErrorsFromShorterParse(cursor);
|
||||
if (cursor == this.lastCursor) {
|
||||
this.addErrorEntry(suggestions, reason);
|
||||
}
|
||||
}
|
||||
|
||||
private void addErrorEntry(SuggestionSupplier<S> suggestions, Object reason) {
|
||||
int currentSize = this.entries.length;
|
||||
if (this.nextErrorEntry >= currentSize) {
|
||||
int newSize = MiscUtils.growByHalf(currentSize, this.nextErrorEntry + 1);
|
||||
MutableErrorEntry<S>[] newEntries = new MutableErrorEntry[newSize];
|
||||
System.arraycopy(this.entries, 0, newEntries, 0, currentSize);
|
||||
this.entries = newEntries;
|
||||
}
|
||||
|
||||
int entryIndex = this.nextErrorEntry++;
|
||||
MutableErrorEntry<S> entry = this.entries[entryIndex];
|
||||
if (entry == null) {
|
||||
entry = new MutableErrorEntry<>();
|
||||
this.entries[entryIndex] = entry;
|
||||
}
|
||||
|
||||
entry.suggestions = suggestions;
|
||||
entry.reason = reason;
|
||||
}
|
||||
|
||||
public List<ErrorEntry<S>> entries() {
|
||||
int errorCount = this.nextErrorEntry;
|
||||
if (errorCount == 0) {
|
||||
return List.of();
|
||||
}
|
||||
List<ErrorEntry<S>> result = new ArrayList<>(errorCount);
|
||||
|
||||
for (int i = 0; i < errorCount; i++) {
|
||||
MutableErrorEntry<S> mutableErrorEntry = this.entries[i];
|
||||
result.add(new ErrorEntry<>(this.lastCursor, mutableErrorEntry.suggestions, mutableErrorEntry.reason));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public int cursor() {
|
||||
return this.lastCursor;
|
||||
}
|
||||
|
||||
static class MutableErrorEntry<S> {
|
||||
SuggestionSupplier<S> suggestions = SuggestionSupplier.empty();
|
||||
Object reason = "empty";
|
||||
}
|
||||
}
|
||||
|
||||
class Nop<S> implements ErrorCollector<S> {
|
||||
@Override
|
||||
public void store(int cursor, SuggestionSupplier<S> suggestions, Object reason) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finish(int finalCursor) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
package net.momirealms.craftengine.core.util.snbt.parse;
|
||||
|
||||
public record ErrorEntry<S>(int cursor, SuggestionSupplier<S> suggestions, Object reason) {
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
package net.momirealms.craftengine.core.util.snbt.parse;
|
||||
|
||||
import com.mojang.brigadier.Message;
|
||||
import com.mojang.brigadier.exceptions.CommandExceptionType;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import net.momirealms.craftengine.core.plugin.locale.TranslationManager;
|
||||
import net.momirealms.craftengine.core.util.AdventureHelper;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class LocalizedCommandSyntaxException extends CommandSyntaxException {
|
||||
public static final int CONTEXT_AMOUNT = 50;
|
||||
public static final String PARSE_ERROR_NODE = "warning.config.type.snbt.invalid_syntax.parse_error";
|
||||
public static final String HERE_NODE = "warning.config.type.snbt.invalid_syntax.here";
|
||||
private final Message message;
|
||||
private final String input;
|
||||
private final int cursor;
|
||||
|
||||
public LocalizedCommandSyntaxException(CommandExceptionType type, Message message) {
|
||||
super(type, message);
|
||||
this.message = message;
|
||||
this.input = null;
|
||||
this.cursor = -1;
|
||||
}
|
||||
|
||||
public LocalizedCommandSyntaxException(CommandExceptionType type, Message message, String input, int cursor) {
|
||||
super(type, message, input, cursor);
|
||||
this.message = message;
|
||||
this.input = input;
|
||||
this.cursor = cursor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
String message = this.message.getString();
|
||||
final String context = getContext();
|
||||
if (context == null) {
|
||||
return message;
|
||||
}
|
||||
return generateLocalizedMessage(
|
||||
PARSE_ERROR_NODE,
|
||||
() -> message + " at position " + this.cursor + ": " + context,
|
||||
message, String.valueOf(this.cursor), context
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContext() {
|
||||
if (this.input == null || this.cursor < 0) {
|
||||
return null;
|
||||
}
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
final int cursor = Math.min(this.input.length(), this.cursor);
|
||||
|
||||
if (cursor > CONTEXT_AMOUNT) {
|
||||
builder.append("...");
|
||||
}
|
||||
|
||||
builder.append(this.input, Math.max(0, cursor - CONTEXT_AMOUNT), cursor);
|
||||
builder.append(generateLocalizedMessage(HERE_NODE, () -> "<--[HERE]"));
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
|
||||
private String generateLocalizedMessage(String node, Supplier<String> fallback, String... arguments) {
|
||||
try {
|
||||
String rawMessage = Optional.ofNullable(TranslationManager.instance()
|
||||
.miniMessageTranslation(node)).orElse(fallback.get());
|
||||
String cleanMessage = AdventureHelper.miniMessage()
|
||||
.stripTags(rawMessage);
|
||||
for (int i = 0; i < arguments.length; i++) {
|
||||
cleanMessage = cleanMessage.replace(
|
||||
"<arg:" + i + ">",
|
||||
arguments[i] != null ? arguments[i] : "null"
|
||||
);
|
||||
}
|
||||
return cleanMessage;
|
||||
} catch (Exception e) {
|
||||
return fallback.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package net.momirealms.craftengine.core.util.snbt.parse;
|
||||
|
||||
import com.mojang.brigadier.ImmutableStringReader;
|
||||
import com.mojang.brigadier.Message;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
public class LocalizedDynamicCommandExceptionType extends DynamicCommandExceptionType {
|
||||
private final Function<Object, Message> function;
|
||||
|
||||
public LocalizedDynamicCommandExceptionType(Function<Object, Message> function) {
|
||||
super(function);
|
||||
this.function = function;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandSyntaxException create(final Object arg) {
|
||||
return new LocalizedCommandSyntaxException(this, function.apply(arg));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandSyntaxException createWithContext(final ImmutableStringReader reader, final Object arg) {
|
||||
return new LocalizedCommandSyntaxException(this, function.apply(arg), reader.getString(), reader.getCursor());
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
package net.momirealms.craftengine.core.util.snbt.parse;
|
||||
|
||||
import com.mojang.brigadier.Message;
|
||||
import net.momirealms.craftengine.core.plugin.locale.TranslationManager;
|
||||
import net.momirealms.craftengine.core.util.AdventureHelper;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
|
||||
public class LocalizedMessage implements Message {
|
||||
private final String node;
|
||||
private final String[] arguments;
|
||||
|
||||
public LocalizedMessage(
|
||||
@NotNull String node,
|
||||
@Nullable String... arguments
|
||||
) {
|
||||
this.node = node;
|
||||
this.arguments = arguments != null
|
||||
? Arrays.copyOf(arguments, arguments.length)
|
||||
: new String[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString() {
|
||||
return generateLocalizedMessage();
|
||||
}
|
||||
|
||||
private String generateLocalizedMessage() {
|
||||
try {
|
||||
String rawMessage = Optional.ofNullable(TranslationManager.instance()
|
||||
.miniMessageTranslation(this.node)).orElse(this.node);
|
||||
String cleanMessage = AdventureHelper.miniMessage()
|
||||
.stripTags(rawMessage);
|
||||
for (int i = 0; i < arguments.length; i++) {
|
||||
cleanMessage = cleanMessage.replace(
|
||||
"<arg:" + i + ">",
|
||||
arguments[i] != null ? arguments[i] : "null"
|
||||
);
|
||||
}
|
||||
return cleanMessage;
|
||||
} catch (Exception e) {
|
||||
return String.format(
|
||||
"Failed to translate. Node: %s, Arguments: %s. Cause: %s",
|
||||
node,
|
||||
Arrays.toString(arguments),
|
||||
e.getMessage()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package net.momirealms.craftengine.core.util.snbt.parse;
|
||||
|
||||
import com.mojang.brigadier.ImmutableStringReader;
|
||||
import com.mojang.brigadier.Message;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
|
||||
|
||||
public class LocalizedSimpleCommandExceptionType extends SimpleCommandExceptionType {
|
||||
private final Message message;
|
||||
|
||||
public LocalizedSimpleCommandExceptionType(Message message) {
|
||||
super(message);
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandSyntaxException create() {
|
||||
return new LocalizedCommandSyntaxException(this, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandSyntaxException createWithContext(final ImmutableStringReader reader) {
|
||||
return new LocalizedCommandSyntaxException(this, message, reader.getString(), reader.getCursor());
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package net.momirealms.craftengine.core.util.snbt.parse;
|
||||
|
||||
public interface NamedRule<S, T> {
|
||||
Atom<T> name();
|
||||
|
||||
Rule<S, T> value();
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package net.momirealms.craftengine.core.util.snbt.parse;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface ParseState<S> {
|
||||
Scope scope();
|
||||
|
||||
ErrorCollector<S> errorCollector();
|
||||
|
||||
default <T> Optional<T> parseTopRule(NamedRule<S, T> rule) {
|
||||
T result = this.parse(rule);
|
||||
if (result != null) {
|
||||
this.errorCollector().finish(this.mark());
|
||||
}
|
||||
|
||||
if (!this.scope().hasOnlySingleFrame()) {
|
||||
throw new IllegalStateException("Malformed scope: " + this.scope());
|
||||
}
|
||||
return Optional.ofNullable(result);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
<T> T parse(NamedRule<S, T> rule);
|
||||
|
||||
S input();
|
||||
|
||||
int mark();
|
||||
|
||||
void restore(int mark);
|
||||
|
||||
Control acquireControl();
|
||||
|
||||
void releaseControl();
|
||||
|
||||
ParseState<S> silent();
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
package net.momirealms.craftengine.core.util.snbt.parse;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public interface Rule<S, T> {
|
||||
@Nullable
|
||||
T parse(ParseState<S> state);
|
||||
|
||||
static <S, T> Rule<S, T> fromTerm(Term<S> child, RuleAction<S, T> action) {
|
||||
return new WrappedTerm<>(action, child);
|
||||
}
|
||||
|
||||
static <S, T> Rule<S, T> fromTerm(Term<S> child, SimpleRuleAction<S, T> action) {
|
||||
return new WrappedTerm<>(action, child);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
interface RuleAction<S, T> {
|
||||
@Nullable
|
||||
T run(ParseState<S> state);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
interface SimpleRuleAction<S, T> extends RuleAction<S, T> {
|
||||
T run(Scope ruleScope);
|
||||
|
||||
@Override
|
||||
default T run(ParseState<S> state) {
|
||||
return this.run(state.scope());
|
||||
}
|
||||
}
|
||||
|
||||
record WrappedTerm<S, T>(RuleAction<S, T> action, Term<S> child) implements Rule<S, T> {
|
||||
@Nullable
|
||||
@Override
|
||||
public T parse(ParseState<S> state) {
|
||||
Scope scope = state.scope();
|
||||
scope.pushFrame();
|
||||
|
||||
try {
|
||||
if (!this.child.parse(state, scope, Control.UNBOUND)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.action.run(state);
|
||||
} finally {
|
||||
scope.popFrame();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,315 +0,0 @@
|
||||
package net.momirealms.craftengine.core.util.snbt.parse;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public final class Scope {
|
||||
private static final int NOT_FOUND = -1;
|
||||
private static final Object FRAME_START_MARKER = new Object() {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "frame";
|
||||
}
|
||||
};
|
||||
private static final int ENTRY_STRIDE = 2;
|
||||
private Object[] stack = new Object[128];
|
||||
private int topEntryKeyIndex = 0;
|
||||
private int topMarkerKeyIndex = 0;
|
||||
private int depth;
|
||||
|
||||
public Scope() {
|
||||
this.stack[0] = FRAME_START_MARKER;
|
||||
this.stack[1] = null;
|
||||
}
|
||||
|
||||
private int valueIndex(Atom<?> atom) {
|
||||
for (int i = this.topEntryKeyIndex; i > this.topMarkerKeyIndex; i -= ENTRY_STRIDE) {
|
||||
Object key = this.stack[i];
|
||||
|
||||
assert key instanceof Atom;
|
||||
|
||||
if (key == atom) {
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return NOT_FOUND;
|
||||
}
|
||||
|
||||
public int valueIndexForAny(Atom<?>... atoms) {
|
||||
for (int i = this.topEntryKeyIndex; i > this.topMarkerKeyIndex; i -= ENTRY_STRIDE) {
|
||||
Object key = this.stack[i];
|
||||
|
||||
assert key instanceof Atom;
|
||||
|
||||
for (Atom<?> atom : atoms) {
|
||||
if (atom == key) {
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NOT_FOUND;
|
||||
}
|
||||
|
||||
private void ensureCapacity(int additionalEntryCount) {
|
||||
int currentSize = this.stack.length;
|
||||
int currentLastValueIndex = this.topEntryKeyIndex + 1;
|
||||
int newLastValueIndex = currentLastValueIndex + additionalEntryCount * 2;
|
||||
if (newLastValueIndex >= currentSize) {
|
||||
int newSize = MiscUtils.growByHalf(currentSize, newLastValueIndex + 1);
|
||||
Object[] newStack = new Object[newSize];
|
||||
System.arraycopy(this.stack, 0, newStack, 0, currentSize);
|
||||
this.stack = newStack;
|
||||
}
|
||||
|
||||
assert this.validateStructure();
|
||||
}
|
||||
|
||||
private void setupNewFrame() {
|
||||
this.topEntryKeyIndex += ENTRY_STRIDE;
|
||||
this.stack[this.topEntryKeyIndex] = FRAME_START_MARKER;
|
||||
this.stack[this.topEntryKeyIndex + 1] = this.topMarkerKeyIndex;
|
||||
this.topMarkerKeyIndex = this.topEntryKeyIndex;
|
||||
}
|
||||
|
||||
public void pushFrame() {
|
||||
this.ensureCapacity(1);
|
||||
this.setupNewFrame();
|
||||
|
||||
assert this.validateStructure();
|
||||
}
|
||||
|
||||
private int getPreviousMarkerIndex(int markerKeyIndex) {
|
||||
return (Integer) this.stack[markerKeyIndex + 1];
|
||||
}
|
||||
|
||||
public void popFrame() {
|
||||
assert this.topMarkerKeyIndex != 0;
|
||||
|
||||
this.topEntryKeyIndex = this.topMarkerKeyIndex - ENTRY_STRIDE;
|
||||
this.topMarkerKeyIndex = this.getPreviousMarkerIndex(this.topMarkerKeyIndex);
|
||||
|
||||
assert this.validateStructure();
|
||||
}
|
||||
|
||||
public void splitFrame() {
|
||||
int currentFrameMarkerIndex = this.topMarkerKeyIndex;
|
||||
int nonMarkerEntriesInFrame = (this.topEntryKeyIndex - this.topMarkerKeyIndex) / ENTRY_STRIDE;
|
||||
this.ensureCapacity(nonMarkerEntriesInFrame + 1);
|
||||
this.setupNewFrame();
|
||||
int sourceCursor = currentFrameMarkerIndex + ENTRY_STRIDE;
|
||||
int targetCursor = this.topEntryKeyIndex;
|
||||
|
||||
for (int i = 0; i < nonMarkerEntriesInFrame; i++) {
|
||||
targetCursor += ENTRY_STRIDE;
|
||||
Object key = this.stack[sourceCursor];
|
||||
|
||||
assert key != null;
|
||||
|
||||
this.stack[targetCursor] = key;
|
||||
this.stack[targetCursor + 1] = null;
|
||||
sourceCursor += ENTRY_STRIDE;
|
||||
}
|
||||
|
||||
this.topEntryKeyIndex = targetCursor;
|
||||
|
||||
assert this.validateStructure();
|
||||
}
|
||||
|
||||
public void clearFrameValues() {
|
||||
for (int i = this.topEntryKeyIndex; i > this.topMarkerKeyIndex; i -= ENTRY_STRIDE) {
|
||||
assert this.stack[i] instanceof Atom;
|
||||
|
||||
this.stack[i + 1] = null;
|
||||
}
|
||||
|
||||
assert this.validateStructure();
|
||||
}
|
||||
|
||||
public void mergeFrame() {
|
||||
int previousMarkerIndex = this.getPreviousMarkerIndex(this.topMarkerKeyIndex);
|
||||
int previousFrameCursor = previousMarkerIndex;
|
||||
int currentFrameCursor = this.topMarkerKeyIndex;
|
||||
|
||||
while (currentFrameCursor < this.topEntryKeyIndex) {
|
||||
previousFrameCursor += ENTRY_STRIDE;
|
||||
currentFrameCursor += ENTRY_STRIDE;
|
||||
Object newKey = this.stack[currentFrameCursor];
|
||||
|
||||
assert newKey instanceof Atom;
|
||||
|
||||
Object newValue = this.stack[currentFrameCursor + 1];
|
||||
Object oldKey = this.stack[previousFrameCursor];
|
||||
if (oldKey != newKey) {
|
||||
this.stack[previousFrameCursor] = newKey;
|
||||
this.stack[previousFrameCursor + 1] = newValue;
|
||||
} else if (newValue != null) {
|
||||
this.stack[previousFrameCursor + 1] = newValue;
|
||||
}
|
||||
}
|
||||
|
||||
this.topEntryKeyIndex = previousFrameCursor;
|
||||
this.topMarkerKeyIndex = previousMarkerIndex;
|
||||
|
||||
assert this.validateStructure();
|
||||
}
|
||||
|
||||
public <T> void put(Atom<T> name, @Nullable T value) {
|
||||
int valueIndex = this.valueIndex(name);
|
||||
if (valueIndex != NOT_FOUND) {
|
||||
this.stack[valueIndex] = value;
|
||||
} else {
|
||||
this.ensureCapacity(1);
|
||||
this.topEntryKeyIndex += ENTRY_STRIDE;
|
||||
this.stack[this.topEntryKeyIndex] = name;
|
||||
this.stack[this.topEntryKeyIndex + 1] = value;
|
||||
}
|
||||
|
||||
assert this.validateStructure();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public <T> T get(Atom<T> name) {
|
||||
int valueIndex = this.valueIndex(name);
|
||||
return (T) (valueIndex != NOT_FOUND ? this.stack[valueIndex] : null);
|
||||
}
|
||||
|
||||
public <T> T getOrThrow(Atom<T> name) {
|
||||
int valueIndex = this.valueIndex(name);
|
||||
if (valueIndex == NOT_FOUND) {
|
||||
throw new IllegalArgumentException("No value for atom " + name);
|
||||
}
|
||||
return (T) this.stack[valueIndex];
|
||||
}
|
||||
|
||||
public <T> T getOrDefault(Atom<T> name, T fallback) {
|
||||
int valueIndex = this.valueIndex(name);
|
||||
return (T) (valueIndex != NOT_FOUND ? this.stack[valueIndex] : fallback);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@SafeVarargs
|
||||
public final <T> T getAny(Atom<? extends T>... names) {
|
||||
int valueIndex = this.valueIndexForAny(names);
|
||||
return (T) (valueIndex != NOT_FOUND ? this.stack[valueIndex] : null);
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public final <T> T getAnyOrThrow(Atom<? extends T>... names) {
|
||||
int valueIndex = this.valueIndexForAny(names);
|
||||
if (valueIndex == NOT_FOUND) {
|
||||
throw new IllegalArgumentException("No value for atoms " + Arrays.toString(names));
|
||||
}
|
||||
return (T) this.stack[valueIndex];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder result = new StringBuilder();
|
||||
boolean afterFrame = true;
|
||||
|
||||
for (int i = 0; i <= this.topEntryKeyIndex; i += ENTRY_STRIDE) {
|
||||
Object key = this.stack[i];
|
||||
Object value = this.stack[i + 1];
|
||||
if (key == FRAME_START_MARKER) {
|
||||
result.append('|');
|
||||
afterFrame = true;
|
||||
} else {
|
||||
if (!afterFrame) {
|
||||
result.append(',');
|
||||
}
|
||||
|
||||
afterFrame = false;
|
||||
result.append(key).append(':').append(value);
|
||||
}
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public Map<Atom<?>, ?> lastFrame() {
|
||||
HashMap<Atom<?>, Object> result = new HashMap<>();
|
||||
|
||||
for (int i = this.topEntryKeyIndex; i > this.topMarkerKeyIndex; i -= ENTRY_STRIDE) {
|
||||
Object key = this.stack[i];
|
||||
Object value = this.stack[i + 1];
|
||||
result.put((Atom<?>) key, value);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean hasOnlySingleFrame() {
|
||||
for (int i = this.topEntryKeyIndex; i > 0; i--) {
|
||||
if (this.stack[i] == FRAME_START_MARKER) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.stack[0] != FRAME_START_MARKER) {
|
||||
throw new IllegalStateException("Corrupted stack");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean validateStructure() {
|
||||
assert this.topMarkerKeyIndex >= 0;
|
||||
|
||||
assert this.topEntryKeyIndex >= this.topMarkerKeyIndex;
|
||||
|
||||
for (int i = 0; i <= this.topEntryKeyIndex; i += ENTRY_STRIDE) {
|
||||
Object object = this.stack[i];
|
||||
if (object != FRAME_START_MARKER && !(object instanceof Atom)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (int ix = this.topMarkerKeyIndex; ix != 0; ix = this.getPreviousMarkerIndex(ix)) {
|
||||
Object object = this.stack[ix];
|
||||
if (object != FRAME_START_MARKER) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public static <S> Term<S> increaseDepth() {
|
||||
class IncreasingDepthTerm<W> implements Term<W> {
|
||||
public static final IncreasingDepthTerm INSTANCE = new IncreasingDepthTerm();
|
||||
|
||||
@Override
|
||||
public boolean parse(final ParseState<W> state, final Scope scope, final Control control) {
|
||||
if (++scope.depth > 512) {
|
||||
state.errorCollector().store(state.mark(), new IllegalStateException("Too deep"));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return (Term<S>) IncreasingDepthTerm.INSTANCE;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public static <S> Term<S> decreaseDepth() {
|
||||
class DecreasingDepthTerm<W> implements Term<W> {
|
||||
public static final DecreasingDepthTerm INSTANCE = new DecreasingDepthTerm();
|
||||
|
||||
@Override
|
||||
public boolean parse(final ParseState<W> state, final Scope scope, final Control control) {
|
||||
scope.depth--;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return (Term<S>) DecreasingDepthTerm.INSTANCE;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package net.momirealms.craftengine.core.util.snbt.parse;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public interface SuggestionSupplier<S> {
|
||||
Stream<String> possibleValues(ParseState<S> state);
|
||||
|
||||
static <S> SuggestionSupplier<S> empty() {
|
||||
return state -> Stream.empty();
|
||||
}
|
||||
}
|
||||
@@ -1,235 +0,0 @@
|
||||
package net.momirealms.craftengine.core.util.snbt.parse;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public interface Term<S> {
|
||||
boolean parse(ParseState<S> state, Scope scope, Control control);
|
||||
|
||||
static <S, T> Term<S> marker(Atom<T> name, T value) {
|
||||
return new Marker<>(name, value);
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
static <S> Term<S> sequence(Term<S>... terms) {
|
||||
return new Sequence<>(terms);
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
static <S> Term<S> alternative(Term<S>... terms) {
|
||||
return new Alternative<>(terms);
|
||||
}
|
||||
|
||||
static <S> Term<S> optional(Term<S> term) {
|
||||
return new Maybe<>(term);
|
||||
}
|
||||
|
||||
static <S, T> Term<S> repeated(NamedRule<S, T> element, Atom<List<T>> listName) {
|
||||
return repeated(element, listName, 0);
|
||||
}
|
||||
|
||||
static <S, T> Term<S> repeated(NamedRule<S, T> element, Atom<List<T>> listName, int minRepetitions) {
|
||||
return new Repeated<>(element, listName, minRepetitions);
|
||||
}
|
||||
|
||||
static <S, T> Term<S> repeatedWithTrailingSeparator(NamedRule<S, T> element, Atom<List<T>> listName, Term<S> separator) {
|
||||
return repeatedWithTrailingSeparator(element, listName, separator, 0);
|
||||
}
|
||||
|
||||
static <S, T> Term<S> repeatedWithTrailingSeparator(NamedRule<S, T> element, Atom<List<T>> listName, Term<S> seperator, int minRepetitions) {
|
||||
return new RepeatedWithSeparator<>(element, listName, seperator, minRepetitions, true);
|
||||
}
|
||||
|
||||
static <S> Term<S> positiveLookahead(Term<S> term) {
|
||||
return new LookAhead<>(term, true);
|
||||
}
|
||||
|
||||
static <S> Term<S> cut() {
|
||||
return new Term<>() {
|
||||
@Override
|
||||
public boolean parse(ParseState<S> state, Scope scope, Control control) {
|
||||
control.cut();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "↑";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static <S> Term<S> empty() {
|
||||
return new Term<>() {
|
||||
@Override
|
||||
public boolean parse(ParseState<S> state, Scope scope, Control control) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ε";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static <S> Term<S> fail(final Object message) {
|
||||
return new Term<>() {
|
||||
@Override
|
||||
public boolean parse(ParseState<S> state, Scope scope, Control control) {
|
||||
state.errorCollector().store(state.mark(), message);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "fail";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
record Alternative<S>(Term<S>[] elements) implements Term<S> {
|
||||
@Override
|
||||
public boolean parse(ParseState<S> state, Scope scope, Control control) {
|
||||
Control controlForThis = state.acquireControl();
|
||||
|
||||
try {
|
||||
int mark = state.mark();
|
||||
scope.splitFrame();
|
||||
|
||||
for (Term<S> element : this.elements) {
|
||||
if (element.parse(state, scope, controlForThis)) {
|
||||
scope.mergeFrame();
|
||||
return true;
|
||||
}
|
||||
|
||||
scope.clearFrameValues();
|
||||
state.restore(mark);
|
||||
if (controlForThis.hasCut()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
scope.popFrame();
|
||||
return false;
|
||||
} finally {
|
||||
state.releaseControl();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
record LookAhead<S>(Term<S> term, boolean positive) implements Term<S> {
|
||||
@Override
|
||||
public boolean parse(ParseState<S> state, Scope scope, Control control) {
|
||||
int mark = state.mark();
|
||||
boolean result = this.term.parse(state.silent(), scope, control);
|
||||
state.restore(mark);
|
||||
return this.positive == result;
|
||||
}
|
||||
}
|
||||
|
||||
record Marker<S, T>(Atom<T> name, T value) implements Term<S> {
|
||||
@Override
|
||||
public boolean parse(ParseState<S> state, Scope scope, Control control) {
|
||||
scope.put(this.name, this.value);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
record Maybe<S>(Term<S> term) implements Term<S> {
|
||||
@Override
|
||||
public boolean parse(ParseState<S> state, Scope scope, Control control) {
|
||||
int mark = state.mark();
|
||||
if (!this.term.parse(state, scope, control)) {
|
||||
state.restore(mark);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
record Repeated<S, T>(NamedRule<S, T> element, Atom<List<T>> listName, int minRepetitions) implements Term<S> {
|
||||
@Override
|
||||
public boolean parse(ParseState<S> state, Scope scope, Control control) {
|
||||
int mark = state.mark();
|
||||
List<T> elements = new ArrayList<>(this.minRepetitions);
|
||||
|
||||
while (true) {
|
||||
int entryMark = state.mark();
|
||||
T parsedElement = state.parse(this.element);
|
||||
if (parsedElement == null) {
|
||||
state.restore(entryMark);
|
||||
if (elements.size() < this.minRepetitions) {
|
||||
state.restore(mark);
|
||||
return false;
|
||||
}
|
||||
scope.put(this.listName, elements);
|
||||
return true;
|
||||
}
|
||||
|
||||
elements.add(parsedElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
record RepeatedWithSeparator<S, T>(
|
||||
NamedRule<S, T> element, Atom<List<T>> listName, Term<S> separator, int minRepetitions, boolean allowTrailingSeparator
|
||||
) implements Term<S> {
|
||||
@Override
|
||||
public boolean parse(ParseState<S> state, Scope scope, Control control) {
|
||||
int listMark = state.mark();
|
||||
List<T> elements = new ArrayList<>(this.minRepetitions);
|
||||
boolean first = true;
|
||||
|
||||
while (true) {
|
||||
int markBeforeSeparator = state.mark();
|
||||
if (!first && !this.separator.parse(state, scope, control)) {
|
||||
state.restore(markBeforeSeparator);
|
||||
break;
|
||||
}
|
||||
|
||||
int markAfterSeparator = state.mark();
|
||||
T parsedElement = state.parse(this.element);
|
||||
if (parsedElement == null) {
|
||||
if (first) {
|
||||
state.restore(markAfterSeparator);
|
||||
} else {
|
||||
if (!this.allowTrailingSeparator) {
|
||||
state.restore(listMark);
|
||||
return false;
|
||||
}
|
||||
|
||||
state.restore(markAfterSeparator);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
elements.add(parsedElement);
|
||||
first = false;
|
||||
}
|
||||
|
||||
if (elements.size() < this.minRepetitions) {
|
||||
state.restore(listMark);
|
||||
return false;
|
||||
}
|
||||
scope.put(this.listName, elements);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
record Sequence<S>(Term<S>[] elements) implements Term<S> {
|
||||
@Override
|
||||
public boolean parse(ParseState<S> state, Scope scope, Control control) {
|
||||
int mark = state.mark();
|
||||
|
||||
for (Term<S> element : this.elements) {
|
||||
if (!element.parse(state, scope, control)) {
|
||||
state.restore(mark);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
package net.momirealms.craftengine.core.util.snbt.parse.grammar;
|
||||
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import net.momirealms.craftengine.core.util.snbt.parse.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public record Grammar<T>(Dictionary<StringReader> rules, NamedRule<StringReader, T> top) {
|
||||
public Grammar {
|
||||
rules.checkAllBound();
|
||||
}
|
||||
|
||||
public Optional<T> parse(ParseState<StringReader> state) {
|
||||
return state.parseTopRule(this.top);
|
||||
}
|
||||
|
||||
public T parse(StringReader reader) throws CommandSyntaxException {
|
||||
ErrorCollector.LongestOnly<StringReader> errorCollector = new ErrorCollector.LongestOnly<>();
|
||||
StringReaderParserState stringReaderParserState = new StringReaderParserState(errorCollector, reader);
|
||||
Optional<T> optionalResult = this.parse(stringReaderParserState);
|
||||
if (optionalResult.isPresent()) {
|
||||
T result = optionalResult.get();
|
||||
if (CachedParseState.JAVA_NULL_VALUE_MARKER.equals(result)) {
|
||||
result = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
List<ErrorEntry<StringReader>> errorEntries = errorCollector.entries();
|
||||
List<Exception> exceptions = errorEntries.stream().<Exception>mapMulti((entry, output) -> {
|
||||
if (entry.reason() instanceof DelayedException<?> delayedException) {
|
||||
output.accept(delayedException.create(reader.getString(), entry.cursor()));
|
||||
} else if (entry.reason() instanceof Exception exception1) {
|
||||
output.accept(exception1);
|
||||
}
|
||||
}).toList();
|
||||
|
||||
for (Exception exception : exceptions) {
|
||||
if (exception instanceof CommandSyntaxException cse) {
|
||||
throw cse;
|
||||
}
|
||||
}
|
||||
|
||||
if (exceptions.size() == 1 && exceptions.getFirst() instanceof RuntimeException re) {
|
||||
throw re;
|
||||
}
|
||||
throw new IllegalStateException("Failed to parse: " + errorEntries.stream().map(ErrorEntry::toString).collect(Collectors.joining(", ")));
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package net.momirealms.craftengine.core.util.snbt.parse.grammar;
|
||||
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import net.momirealms.craftengine.core.util.snbt.parse.DelayedException;
|
||||
import net.momirealms.craftengine.core.util.snbt.parse.ParseState;
|
||||
import net.momirealms.craftengine.core.util.snbt.parse.Rule;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public final class GreedyPatternParseRule implements Rule<StringReader, String> {
|
||||
private final Pattern pattern;
|
||||
private final DelayedException<CommandSyntaxException> error;
|
||||
|
||||
public GreedyPatternParseRule(Pattern pattern, DelayedException<CommandSyntaxException> error) {
|
||||
this.pattern = pattern;
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parse(ParseState<StringReader> state) {
|
||||
StringReader input = state.input();
|
||||
String fullString = input.getString();
|
||||
Matcher matcher = this.pattern.matcher(fullString).region(input.getCursor(), fullString.length());
|
||||
if (!matcher.lookingAt()) {
|
||||
state.errorCollector().store(state.mark(), this.error);
|
||||
return null;
|
||||
}
|
||||
input.setCursor(matcher.end());
|
||||
return matcher.group(0);
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package net.momirealms.craftengine.core.util.snbt.parse.grammar;
|
||||
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import net.momirealms.craftengine.core.util.snbt.parse.DelayedException;
|
||||
import net.momirealms.craftengine.core.util.snbt.parse.ParseState;
|
||||
import net.momirealms.craftengine.core.util.snbt.parse.Rule;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public abstract class GreedyPredicateParseRule implements Rule<StringReader, String> {
|
||||
private final int minSize;
|
||||
private final int maxSize;
|
||||
private final DelayedException<CommandSyntaxException> error;
|
||||
|
||||
public GreedyPredicateParseRule(int minSize, DelayedException<CommandSyntaxException> error) {
|
||||
this(minSize, Integer.MAX_VALUE, error);
|
||||
}
|
||||
|
||||
public GreedyPredicateParseRule(int minSize, int maxSize, DelayedException<CommandSyntaxException> error) {
|
||||
this.minSize = minSize;
|
||||
this.maxSize = maxSize;
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String parse(ParseState<StringReader> state) {
|
||||
StringReader input = state.input();
|
||||
String fullString = input.getString();
|
||||
int start = input.getCursor();
|
||||
int pos = start;
|
||||
|
||||
while (pos < fullString.length() && this.isAccepted(fullString.charAt(pos)) && pos - start < this.maxSize) {
|
||||
pos++;
|
||||
}
|
||||
|
||||
int length = pos - start;
|
||||
if (length < this.minSize) {
|
||||
state.errorCollector().store(state.mark(), this.error);
|
||||
return null;
|
||||
}
|
||||
input.setCursor(pos);
|
||||
return fullString.substring(start, pos);
|
||||
}
|
||||
|
||||
protected abstract boolean isAccepted(char c);
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
package net.momirealms.craftengine.core.util.snbt.parse.grammar;
|
||||
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import net.momirealms.craftengine.core.util.snbt.parse.DelayedException;
|
||||
import net.momirealms.craftengine.core.util.snbt.parse.ParseState;
|
||||
import net.momirealms.craftengine.core.util.snbt.parse.Rule;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public abstract class NumberRunParseRule implements Rule<StringReader, String> {
|
||||
private final DelayedException<CommandSyntaxException> noValueError;
|
||||
private final DelayedException<CommandSyntaxException> underscoreNotAllowedError;
|
||||
|
||||
public NumberRunParseRule(DelayedException<CommandSyntaxException> noValueError, DelayedException<CommandSyntaxException> underscoreNotAllowedError) {
|
||||
this.noValueError = noValueError;
|
||||
this.underscoreNotAllowedError = underscoreNotAllowedError;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String parse(ParseState<StringReader> state) {
|
||||
StringReader input = state.input();
|
||||
input.skipWhitespace();
|
||||
String fullString = input.getString();
|
||||
int start = input.getCursor();
|
||||
int pos = start;
|
||||
|
||||
while (pos < fullString.length() && this.isAccepted(fullString.charAt(pos))) {
|
||||
pos++;
|
||||
}
|
||||
|
||||
int length = pos - start;
|
||||
if (length == 0) {
|
||||
state.errorCollector().store(state.mark(), this.noValueError);
|
||||
return null;
|
||||
} else if (fullString.charAt(start) != '_' && fullString.charAt(pos - 1) != '_') {
|
||||
input.setCursor(pos);
|
||||
return fullString.substring(start, pos);
|
||||
}
|
||||
state.errorCollector().store(state.mark(), this.underscoreNotAllowedError);
|
||||
return null;
|
||||
}
|
||||
|
||||
protected abstract boolean isAccepted(char c);
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package net.momirealms.craftengine.core.util.snbt.parse.grammar;
|
||||
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import net.momirealms.craftengine.core.util.snbt.parse.CachedParseState;
|
||||
import net.momirealms.craftengine.core.util.snbt.parse.ErrorCollector;
|
||||
|
||||
public class StringReaderParserState extends CachedParseState<StringReader> {
|
||||
private final StringReader input;
|
||||
|
||||
public StringReaderParserState(ErrorCollector<StringReader> errorCollector, StringReader input) {
|
||||
super(errorCollector);
|
||||
this.input = input;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringReader input() {
|
||||
return this.input;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int mark() {
|
||||
return this.input.getCursor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restore(int mark) {
|
||||
this.input.setCursor(mark);
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
package net.momirealms.craftengine.core.util.snbt.parse.grammar;
|
||||
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
|
||||
import it.unimi.dsi.fastutil.chars.CharList;
|
||||
import net.momirealms.craftengine.core.util.snbt.parse.*;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public interface StringReaderTerms {
|
||||
DynamicCommandExceptionType LITERAL_INCORRECT = new LocalizedDynamicCommandExceptionType(
|
||||
expected -> new LocalizedMessage("warning.config.type.snbt.parser.incorrect", String.valueOf(expected))
|
||||
);
|
||||
|
||||
static Term<StringReader> character(final char value) {
|
||||
return new TerminalCharacters(CharList.of(value)) {
|
||||
@Override
|
||||
protected boolean isAccepted(char v) {
|
||||
return value == v;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static Term<StringReader> characters(final char v1, final char v2) {
|
||||
return new TerminalCharacters(CharList.of(v1, v2)) {
|
||||
@Override
|
||||
protected boolean isAccepted(char v) {
|
||||
return v == v1 || v == v2;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static StringReader createReader(String contents, int cursor) {
|
||||
StringReader reader = new StringReader(contents);
|
||||
reader.setCursor(cursor);
|
||||
return reader;
|
||||
}
|
||||
|
||||
abstract class TerminalCharacters implements Term<StringReader> {
|
||||
private final DelayedException<CommandSyntaxException> error;
|
||||
private final SuggestionSupplier<StringReader> suggestions;
|
||||
|
||||
public TerminalCharacters(CharList values) {
|
||||
String joinedValues = values.intStream().mapToObj(Character::toString).collect(Collectors.joining("|"));
|
||||
this.error = DelayedException.create(LITERAL_INCORRECT, joinedValues);
|
||||
this.suggestions = s -> values.intStream().mapToObj(Character::toString);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean parse(ParseState<StringReader> state, Scope scope, Control control) {
|
||||
state.input().skipWhitespace();
|
||||
int cursor = state.mark();
|
||||
if (state.input().canRead() && this.isAccepted(state.input().read())) {
|
||||
return true;
|
||||
}
|
||||
state.errorCollector().store(cursor, this.suggestions, this.error);
|
||||
return false;
|
||||
}
|
||||
|
||||
protected abstract boolean isAccepted(char value);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package net.momirealms.craftengine.core.util.snbt.parse.grammar;
|
||||
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import net.momirealms.craftengine.core.util.snbt.parse.DelayedException;
|
||||
import net.momirealms.craftengine.core.util.snbt.parse.ParseState;
|
||||
import net.momirealms.craftengine.core.util.snbt.parse.Rule;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class UnquotedStringParseRule implements Rule<StringReader, String> {
|
||||
private final int minSize;
|
||||
private final DelayedException<CommandSyntaxException> error;
|
||||
|
||||
public UnquotedStringParseRule(int minSize, DelayedException<CommandSyntaxException> error) {
|
||||
this.minSize = minSize;
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String parse(ParseState<StringReader> state) {
|
||||
state.input().skipWhitespace();
|
||||
int cursor = state.mark();
|
||||
String value = state.input().readUnquotedString();
|
||||
if (value.length() < this.minSize) {
|
||||
state.errorCollector().store(cursor, this.error);
|
||||
return null;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,7 @@ zstd_version=1.5.7-6
|
||||
commons_io_version=2.21.0
|
||||
commons_lang3_version=3.20.0
|
||||
sparrow_nbt_version=0.10.6
|
||||
sparrow_util_version=0.66
|
||||
sparrow_util_version=0.67
|
||||
fastutil_version=8.5.18
|
||||
netty_version=4.1.128.Final
|
||||
joml_version=1.10.8
|
||||
|
||||
Reference in New Issue
Block a user