From edfdf083e062c2d9f62a44a64dfbb3056ffe6f06 Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Mon, 1 Dec 2025 07:57:46 +0800 Subject: [PATCH] =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/util/snbt/SnbtGrammar.java | 556 +++++++++++------- .../LocalizedCommandSyntaxException.java | 2 +- 2 files changed, 334 insertions(+), 224 deletions(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java index 74512176c..3dfa6cbde 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java @@ -24,7 +24,7 @@ import java.util.stream.LongStream; public class SnbtGrammar { private static final DynamicCommandExceptionType ERROR_NUMBER_PARSE_FAILURE = new LocalizedDynamicCommandExceptionType( - number -> new LocalizedMessage("warning.config.type.snbt.parser.number_parse_failure", String.valueOf(number)) + message -> new LocalizedMessage("warning.config.type.snbt.parser.number_parse_failure", String.valueOf(message)) ); static final DynamicCommandExceptionType ERROR_EXPECTED_HEX_ESCAPE = new LocalizedDynamicCommandExceptionType( length -> new LocalizedMessage("warning.config.type.snbt.parser.expected_hex_escape", String.valueOf(length)) @@ -248,152 +248,151 @@ public class SnbtGrammar { T emptyList = ops.emptyList(); T nullString = ops.createString("null"); boolean isJavaType = "null".equals(nullString); // 确定是 Java 类型的 + Dictionary rules = new Dictionary<>(); + Atom sign = Atom.of("sign"); rules.put( sign, - Term.alternative( - Term.sequence(StringReaderTerms.character('+'), Term.marker(sign, Sign.PLUS)), - Term.sequence(StringReaderTerms.character('-'), Term.marker(sign, Sign.MINUS)) - ), + Term.alternative( + Term.sequence(StringReaderTerms.character('+'), Term.marker(sign, Sign.PLUS)), + Term.sequence(StringReaderTerms.character('-'), Term.marker(sign, Sign.MINUS)) + ), scope -> scope.getOrThrow(sign) ); + Atom integerSuffix = Atom.of("integer_suffix"); rules.put( integerSuffix, - Term.alternative( - Term.sequence( - StringReaderTerms.characters('u', 'U'), - Term.alternative( + Term.alternative( Term.sequence( - StringReaderTerms.characters('b', 'B'), - Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.BYTE)) + StringReaderTerms.characters('u', 'U'), + Term.alternative( + Term.sequence(StringReaderTerms.characters('b', 'B'), Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.BYTE))), + Term.sequence(StringReaderTerms.characters('s', 'S'), Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.SHORT))), + Term.sequence(StringReaderTerms.characters('i', 'I'), Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.INT))), + Term.sequence(StringReaderTerms.characters('l', 'L'), Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.LONG))) + ) ), Term.sequence( - StringReaderTerms.characters('s', 'S'), - Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.SHORT)) + StringReaderTerms.characters('s', 'S'), + Term.alternative( + Term.sequence(StringReaderTerms.characters('b', 'B'), Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.BYTE))), + Term.sequence(StringReaderTerms.characters('s', 'S'), Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.SHORT))), + Term.sequence(StringReaderTerms.characters('i', 'I'), Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.INT))), + Term.sequence(StringReaderTerms.characters('l', 'L'), Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.LONG))) + ) ), - Term.sequence( - StringReaderTerms.characters('i', 'I'), - Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.INT)) - ), - Term.sequence( - StringReaderTerms.characters('l', 'L'), - Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.LONG)) - ) - ) + Term.sequence(StringReaderTerms.characters('b', 'B'), Term.marker(integerSuffix, new IntegerSuffix(null, TypeSuffix.BYTE))), + Term.sequence(StringReaderTerms.characters('s', 'S'), Term.marker(integerSuffix, new IntegerSuffix(null, TypeSuffix.SHORT))), + Term.sequence(StringReaderTerms.characters('i', 'I'), Term.marker(integerSuffix, new IntegerSuffix(null, TypeSuffix.INT))), + Term.sequence(StringReaderTerms.characters('l', 'L'), Term.marker(integerSuffix, new IntegerSuffix(null, TypeSuffix.LONG))) ), - Term.sequence( - StringReaderTerms.characters('s', 'S'), - Term.alternative( - Term.sequence( - StringReaderTerms.characters('b', 'B'), - Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.BYTE)) - ), - Term.sequence( - StringReaderTerms.characters('s', 'S'), - Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.SHORT)) - ), - Term.sequence( - StringReaderTerms.characters('i', 'I'), - Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.INT)) - ), - Term.sequence( - StringReaderTerms.characters('l', 'L'), - Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.LONG)) - ) - ) - ), - Term.sequence(StringReaderTerms.characters('b', 'B'), Term.marker(integerSuffix, new IntegerSuffix(null, TypeSuffix.BYTE))), - Term.sequence(StringReaderTerms.characters('s', 'S'), Term.marker(integerSuffix, new IntegerSuffix(null, TypeSuffix.SHORT))), - Term.sequence(StringReaderTerms.characters('i', 'I'), Term.marker(integerSuffix, new IntegerSuffix(null, TypeSuffix.INT))), - Term.sequence(StringReaderTerms.characters('l', 'L'), Term.marker(integerSuffix, new IntegerSuffix(null, TypeSuffix.LONG))) - ), scope -> scope.getOrThrow(integerSuffix) ); + Atom binaryNumeral = Atom.of("binary_numeral"); rules.put(binaryNumeral, BINARY_NUMERAL); + Atom decimalNumeral = Atom.of("decimal_numeral"); rules.put(decimalNumeral, DECIMAL_NUMERAL); + Atom hexNumeral = Atom.of("hex_numeral"); rules.put(hexNumeral, HEX_NUMERAL); + Atom integerLiteral = Atom.of("integer_literal"); NamedRule integerLiteralRule = rules.put( integerLiteral, - Term.sequence( - Term.optional(rules.named(sign)), - Term.alternative( - Term.sequence( - StringReaderTerms.character('0'), - Term.cut(), + Term.sequence( + Term.optional(rules.named(sign)), Term.alternative( - Term.sequence(StringReaderTerms.characters('x', 'X'), Term.cut(), rules.named(hexNumeral)), - Term.sequence(StringReaderTerms.characters('b', 'B'), rules.named(binaryNumeral)), - Term.sequence(rules.named(decimalNumeral), Term.cut(), Term.fail(ERROR_LEADING_ZERO_NOT_ALLOWED)), - Term.marker(decimalNumeral, "0") - ) - ), - rules.named(decimalNumeral) + Term.sequence( + StringReaderTerms.character('0'), + Term.cut(), + Term.alternative( + Term.sequence(StringReaderTerms.characters('x', 'X'), Term.cut(), rules.named(hexNumeral)), + Term.sequence(StringReaderTerms.characters('b', 'B'), rules.named(binaryNumeral)), + Term.sequence(rules.named(decimalNumeral), Term.cut(), Term.fail(ERROR_LEADING_ZERO_NOT_ALLOWED)), + Term.marker(decimalNumeral, "0") + ) + ), + rules.named(decimalNumeral) + ), + Term.optional(rules.named(integerSuffix)) ), - Term.optional(rules.named(integerSuffix)) - ), - scope -> { - IntegerSuffix suffix = scope.getOrDefault(integerSuffix, IntegerSuffix.EMPTY); - Sign signValue = scope.getOrDefault(sign, Sign.PLUS); - String decimalContents = scope.get(decimalNumeral); - if (decimalContents != null) { - return new IntegerLiteral(signValue, Base.DECIMAL, decimalContents, suffix); + scope -> { + IntegerSuffix suffix = scope.getOrDefault(integerSuffix, IntegerSuffix.EMPTY); + Sign signValue = scope.getOrDefault(sign, Sign.PLUS); + String decimalContents = scope.get(decimalNumeral); + if (decimalContents != null) { + return new IntegerLiteral(signValue, Base.DECIMAL, decimalContents, suffix); + } + String hexContents = scope.get(hexNumeral); + if (hexContents != null) { + return new IntegerLiteral(signValue, Base.HEX, hexContents, suffix); + } + String binaryContents = scope.getOrThrow(binaryNumeral); + return new IntegerLiteral(signValue, Base.BINARY, binaryContents, suffix); } - String string1 = scope.get(hexNumeral); - if (string1 != null) { - return new IntegerLiteral(signValue, Base.HEX, string1, suffix); - } - String string2 = scope.getOrThrow(binaryNumeral); - return new IntegerLiteral(signValue, Base.BINARY, string2, suffix); - } ); + Atom floatTypeSuffix = Atom.of("float_type_suffix"); rules.put( floatTypeSuffix, - Term.alternative( - Term.sequence(StringReaderTerms.characters('f', 'F'), Term.marker(floatTypeSuffix, TypeSuffix.FLOAT)), - Term.sequence(StringReaderTerms.characters('d', 'D'), Term.marker(floatTypeSuffix, TypeSuffix.DOUBLE)) - ), + Term.alternative( + Term.sequence(StringReaderTerms.characters('f', 'F'), Term.marker(floatTypeSuffix, TypeSuffix.FLOAT)), + Term.sequence(StringReaderTerms.characters('d', 'D'), Term.marker(floatTypeSuffix, TypeSuffix.DOUBLE)) + ), scope -> scope.getOrThrow(floatTypeSuffix) ); + Atom> floatExponentPart = Atom.of("float_exponent_part"); rules.put( floatExponentPart, - Term.sequence(StringReaderTerms.characters('e', 'E'), Term.optional(rules.named(sign)), rules.named(decimalNumeral)), + Term.sequence( + StringReaderTerms.characters('e', 'E'), + Term.optional(rules.named(sign)), + rules.named(decimalNumeral) + ), scope -> new Signed<>(scope.getOrDefault(sign, Sign.PLUS), scope.getOrThrow(decimalNumeral)) ); + Atom floatWholePart = Atom.of("float_whole_part"); Atom floatFractionPart = Atom.of("float_fraction_part"); Atom floatLiteral = Atom.of("float_literal"); rules.putComplex( floatLiteral, - Term.sequence( - Term.optional(rules.named(sign)), - Term.alternative( - Term.sequence( - rules.namedWithAlias(decimalNumeral, floatWholePart), - StringReaderTerms.character('.'), - Term.cut(), - Term.optional(rules.namedWithAlias(decimalNumeral, floatFractionPart)), - Term.optional(rules.named(floatExponentPart)), - Term.optional(rules.named(floatTypeSuffix)) - ), - Term.sequence( - StringReaderTerms.character('.'), - Term.cut(), - rules.namedWithAlias(decimalNumeral, floatFractionPart), - Term.optional(rules.named(floatExponentPart)), - Term.optional(rules.named(floatTypeSuffix)) - ), - Term.sequence(rules.namedWithAlias(decimalNumeral, floatWholePart), rules.named(floatExponentPart), Term.cut(), Term.optional(rules.named(floatTypeSuffix))), - Term.sequence(rules.namedWithAlias(decimalNumeral, floatWholePart), Term.optional(rules.named(floatExponentPart)), rules.named(floatTypeSuffix)) - ) - ), + Term.sequence( + Term.optional(rules.named(sign)), + Term.alternative( + Term.sequence( + rules.namedWithAlias(decimalNumeral, floatWholePart), + StringReaderTerms.character('.'), + Term.cut(), + Term.optional(rules.namedWithAlias(decimalNumeral, floatFractionPart)), + Term.optional(rules.named(floatExponentPart)), + Term.optional(rules.named(floatTypeSuffix)) + ), + Term.sequence( + StringReaderTerms.character('.'), + Term.cut(), + rules.namedWithAlias(decimalNumeral, floatFractionPart), + Term.optional(rules.named(floatExponentPart)), + Term.optional(rules.named(floatTypeSuffix)) + ), + Term.sequence( + rules.namedWithAlias(decimalNumeral, floatWholePart), + rules.named(floatExponentPart), + Term.cut(), + Term.optional(rules.named(floatTypeSuffix)) + ), + Term.sequence( + rules.namedWithAlias(decimalNumeral, floatWholePart), + Term.optional(rules.named(floatExponentPart)), + rules.named(floatTypeSuffix) + ) + ) + ), state -> { Scope scope = state.scope(); Sign wholeSign = scope.getOrDefault(sign, Sign.PLUS); @@ -402,116 +401,160 @@ public class SnbtGrammar { Signed exponent = scope.get(floatExponentPart); TypeSuffix typeSuffix = scope.get(floatTypeSuffix); return createFloat(ops, wholeSign, whole, fraction, exponent, typeSuffix, state); - } + } ); + Atom stringHex2 = Atom.of("string_hex_2"); rules.put(stringHex2, new SimpleHexLiteralParseRule(2)); + Atom stringHex4 = Atom.of("string_hex_4"); rules.put(stringHex4, new SimpleHexLiteralParseRule(4)); + Atom stringHex8 = Atom.of("string_hex_8"); rules.put(stringHex8, new SimpleHexLiteralParseRule(8)); + Atom stringUnicodeName = Atom.of("string_unicode_name"); rules.put(stringUnicodeName, new GreedyPatternParseRule(UNICODE_NAME, ERROR_INVALID_CHARACTER_NAME)); + Atom stringEscapeSequence = Atom.of("string_escape_sequence"); rules.putComplex( stringEscapeSequence, - Term.alternative( - Term.sequence(StringReaderTerms.character('b'), Term.marker(stringEscapeSequence, "\b")), - Term.sequence(StringReaderTerms.character('s'), Term.marker(stringEscapeSequence, " ")), - Term.sequence(StringReaderTerms.character('t'), Term.marker(stringEscapeSequence, "\t")), - Term.sequence(StringReaderTerms.character('n'), Term.marker(stringEscapeSequence, "\n")), - Term.sequence(StringReaderTerms.character('f'), Term.marker(stringEscapeSequence, "\f")), - Term.sequence(StringReaderTerms.character('r'), Term.marker(stringEscapeSequence, "\r")), - Term.sequence(StringReaderTerms.character('\\'), Term.marker(stringEscapeSequence, "\\")), - Term.sequence(StringReaderTerms.character('\''), Term.marker(stringEscapeSequence, "'")), - Term.sequence(StringReaderTerms.character('"'), Term.marker(stringEscapeSequence, "\"")), - Term.sequence(StringReaderTerms.character('x'), rules.named(stringHex2)), - Term.sequence(StringReaderTerms.character('u'), rules.named(stringHex4)), - Term.sequence(StringReaderTerms.character('U'), rules.named(stringHex8)), - Term.sequence(StringReaderTerms.character('N'), StringReaderTerms.character('{'), rules.named(stringUnicodeName), StringReaderTerms.character('}')) - ), + Term.alternative( + Term.sequence(StringReaderTerms.character('b'), Term.marker(stringEscapeSequence, "\b")), + Term.sequence(StringReaderTerms.character('s'), Term.marker(stringEscapeSequence, " ")), + Term.sequence(StringReaderTerms.character('t'), Term.marker(stringEscapeSequence, "\t")), + Term.sequence(StringReaderTerms.character('n'), Term.marker(stringEscapeSequence, "\n")), + Term.sequence(StringReaderTerms.character('f'), Term.marker(stringEscapeSequence, "\f")), + Term.sequence(StringReaderTerms.character('r'), Term.marker(stringEscapeSequence, "\r")), + Term.sequence(StringReaderTerms.character('\\'), Term.marker(stringEscapeSequence, "\\")), + Term.sequence(StringReaderTerms.character('\''), Term.marker(stringEscapeSequence, "'")), + Term.sequence(StringReaderTerms.character('"'), Term.marker(stringEscapeSequence, "\"")), + Term.sequence(StringReaderTerms.character('x'), rules.named(stringHex2)), + Term.sequence(StringReaderTerms.character('u'), rules.named(stringHex4)), + Term.sequence(StringReaderTerms.character('U'), rules.named(stringHex8)), + Term.sequence( + StringReaderTerms.character('N'), + StringReaderTerms.character('{'), + rules.named(stringUnicodeName), + StringReaderTerms.character('}') + ) + ), state -> { Scope scope = state.scope(); String plainEscape = scope.getAny(stringEscapeSequence); if (plainEscape != null) { return plainEscape; - } + } String hexEscape = scope.getAny(stringHex2, stringHex4, stringHex8); if (hexEscape != null) { int codePoint = HexFormat.fromHexDigits(hexEscape); if (!Character.isValidCodePoint(codePoint)) { - state.errorCollector() - .store(state.mark(), DelayedException.create(ERROR_INVALID_CODEPOINT, String.format(Locale.ROOT, "U+%08X", codePoint))); - return null; - } + state.errorCollector().store(state.mark(), DelayedException.create(ERROR_INVALID_CODEPOINT, String.format(Locale.ROOT, "U+%08X", codePoint))); + return null; + } return Character.toString(codePoint); - } + } String character = scope.getOrThrow(stringUnicodeName); int codePoint; - try { - codePoint = Character.codePointOf(character); - } catch (IllegalArgumentException var12x) { - state.errorCollector().store(state.mark(), ERROR_INVALID_CHARACTER_NAME); - return null; - } + try { + codePoint = Character.codePointOf(character); + } catch (IllegalArgumentException var12x) { + state.errorCollector().store(state.mark(), ERROR_INVALID_CHARACTER_NAME); + return null; + } return Character.toString(codePoint); - } + } ); + Atom stringPlainContents = Atom.of("string_plain_contents"); rules.put(stringPlainContents, PLAIN_STRING_CHUNK); + Atom> stringChunks = Atom.of("string_chunks"); Atom stringContents = Atom.of("string_contents"); Atom singleQuotedStringChunk = Atom.of("single_quoted_string_chunk"); - NamedRule namedRule1 = rules.put( + NamedRule singleQuotedStringChunkRule = rules.put( singleQuotedStringChunk, - Term.alternative( - rules.namedWithAlias(stringPlainContents, stringContents), - Term.sequence(StringReaderTerms.character('\\'), rules.namedWithAlias(stringEscapeSequence, stringContents)), - Term.sequence(StringReaderTerms.character('"'), Term.marker(stringContents, "\"")) - ), + Term.alternative( + rules.namedWithAlias(stringPlainContents, stringContents), + Term.sequence(StringReaderTerms.character('\\'), rules.namedWithAlias(stringEscapeSequence, stringContents)), + Term.sequence(StringReaderTerms.character('"'), Term.marker(stringContents, "\"")) + ), scope -> scope.getOrThrow(stringContents) ); Atom singleQuotedStringContents = Atom.of("single_quoted_string_contents"); - rules.put(singleQuotedStringContents, Term.repeated(namedRule1, stringChunks), scope -> joinList(scope.getOrThrow(stringChunks))); + rules.put( + singleQuotedStringContents, + Term.repeated(singleQuotedStringChunkRule, stringChunks), + scope -> joinList(scope.getOrThrow(stringChunks)) + ); + Atom doubleQuotedStringChunk = Atom.of("double_quoted_string_chunk"); - NamedRule namedRule2 = rules.put( + NamedRule doubleQuotedStringChunkRule = rules.put( doubleQuotedStringChunk, - Term.alternative( - rules.namedWithAlias(stringPlainContents, stringContents), - Term.sequence(StringReaderTerms.character('\\'), rules.namedWithAlias(stringEscapeSequence, stringContents)), - Term.sequence(StringReaderTerms.character('\''), Term.marker(stringContents, "'")) - ), + Term.alternative( + rules.namedWithAlias(stringPlainContents, stringContents), + Term.sequence(StringReaderTerms.character('\\'), rules.namedWithAlias(stringEscapeSequence, stringContents)), + Term.sequence(StringReaderTerms.character('\''), Term.marker(stringContents, "'")) + ), scope -> scope.getOrThrow(stringContents) ); Atom doubleQuotedStringContents = Atom.of("double_quoted_string_contents"); - rules.put(doubleQuotedStringContents, Term.repeated(namedRule2, stringChunks), scope -> joinList(scope.getOrThrow(stringChunks))); + rules.put( + doubleQuotedStringContents, + Term.repeated(doubleQuotedStringChunkRule, stringChunks), + scope -> joinList(scope.getOrThrow(stringChunks)) + ); + Atom quotedStringLiteral = Atom.of("quoted_string_literal"); rules.put( quotedStringLiteral, - Term.alternative( - Term.sequence( - StringReaderTerms.character('"'), Term.cut(), Term.optional(rules.namedWithAlias(doubleQuotedStringContents, stringContents)), StringReaderTerms.character('"') + Term.alternative( + Term.sequence( + StringReaderTerms.character('"'), + Term.cut(), + Term.optional(rules.namedWithAlias(doubleQuotedStringContents, stringContents)), + StringReaderTerms.character('"') + ), + Term.sequence( + StringReaderTerms.character('\''), + Term.optional(rules.namedWithAlias(singleQuotedStringContents, stringContents)), + StringReaderTerms.character('\'') + ) ), - Term.sequence(StringReaderTerms.character('\''), Term.optional(rules.namedWithAlias(singleQuotedStringContents, stringContents)), StringReaderTerms.character('\'')) - ), scope -> scope.getOrThrow(stringContents) ); + Atom unquotedString = Atom.of("unquoted_string"); - rules.put(unquotedString, new UnquotedStringParseRule(1, ERROR_EXPECTED_UNQUOTED_STRING)); + rules.put( + unquotedString, + new UnquotedStringParseRule(1, ERROR_EXPECTED_UNQUOTED_STRING) + ); + Atom literal = Atom.of("literal"); Atom> argumentList = Atom.of("arguments"); rules.put( - argumentList, Term.repeatedWithTrailingSeparator(rules.forward(literal), argumentList, StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR)), scope -> scope.getOrThrow(argumentList) + argumentList, + Term.repeatedWithTrailingSeparator( + rules.forward(literal), + argumentList, + StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR) + ), + scope -> scope.getOrThrow(argumentList) ); + Atom unquotedStringOrBuiltIn = Atom.of("unquoted_string_or_builtin"); rules.putComplex( unquotedStringOrBuiltIn, - Term.sequence( - rules.named(unquotedString), - Term.optional(Term.sequence(StringReaderTerms.character('('), rules.named(argumentList), StringReaderTerms.character(')'))) - ), + Term.sequence( + rules.named(unquotedString), + Term.optional(Term.sequence( + StringReaderTerms.character('('), + rules.named(argumentList), + StringReaderTerms.character(')') + )) + ), state -> { Scope scope = state.scope(); String contents = scope.getOrThrow(unquotedString); @@ -522,114 +565,192 @@ public class SnbtGrammar { SnbtOperations.BuiltinOperation operation = SnbtOperations.BUILTIN_OPERATIONS.get(key); if (operation != null) { return operation.run(ops, arguments, state); - } + } state.errorCollector().store(state.mark(), DelayedException.create(ERROR_NO_SUCH_OPERATION, key.toString())); - return null; + return null; } else if (contents.equalsIgnoreCase("true")) { return trueValue; } else if (contents.equalsIgnoreCase("false")) { return falseValue; } else if (contents.equalsIgnoreCase("null")) { - return Objects.requireNonNullElseGet(ops.empty(), () -> { - if (isJavaType) { - return (T) CachedParseState.JAVA_NULL_VALUE_MARKER; - } - return nullString; - }); - } + return Objects.requireNonNullElseGet(ops.empty(), () -> { + if (isJavaType) { + return (T) CachedParseState.JAVA_NULL_VALUE_MARKER; + } + return nullString; + }); + } return ops.createString(contents); - } + } state.errorCollector().store(state.mark(), SnbtOperations.BUILTIN_IDS, ERROR_INVALID_UNQUOTED_START); - return null; - } + return null; + } ); + Atom mapKey = Atom.of("map_key"); - rules.put(mapKey, Term.alternative(rules.named(quotedStringLiteral), rules.named(unquotedString)), scope -> scope.getAnyOrThrow(quotedStringLiteral, unquotedString)); + rules.put( + mapKey, + Term.alternative( + rules.named(quotedStringLiteral), + rules.named(unquotedString) + ), + scope -> scope.getAnyOrThrow(quotedStringLiteral, unquotedString) + ); + Atom> mapEntry = Atom.of("map_entry"); NamedRule> mapEntryRule = rules.putComplex( - mapEntry, Term.sequence(rules.named(mapKey), StringReaderTerms.character(TagParser.NAME_VALUE_SEPARATOR), rules.named(literal)), state -> { + mapEntry, + Term.sequence( + rules.named(mapKey), + StringReaderTerms.character(TagParser.NAME_VALUE_SEPARATOR), + rules.named(literal) + ), + state -> { Scope scope = state.scope(); String key = scope.getOrThrow(mapKey); if (key.isEmpty()) { state.errorCollector().store(state.mark(), ERROR_EMPTY_KEY); - return null; - } + return null; + } T value = scope.getOrThrow(literal); return Map.entry(key, value); - } + } ); + Atom>> mapEntries = Atom.of("map_entries"); - rules.put(mapEntries, Term.repeatedWithTrailingSeparator(mapEntryRule, mapEntries, StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR)), scope -> scope.getOrThrow(mapEntries)); + rules.put( + mapEntries, + Term.repeatedWithTrailingSeparator( + mapEntryRule, + mapEntries, + StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR) + ), + scope -> scope.getOrThrow(mapEntries) + ); + Atom mapLiteral = Atom.of("map_literal"); - rules.put(mapLiteral, Term.sequence(StringReaderTerms.character('{'), Scope.increaseDepth(), rules.named(mapEntries), Scope.decreaseDepth(), StringReaderTerms.character('}')), scope -> { - List> entries = scope.getOrThrow(mapEntries); - if (entries.isEmpty()) { - return emptyMapValue; - } - Builder builder = ImmutableMap.builderWithExpectedSize(entries.size()); + rules.put( + mapLiteral, + Term.sequence( + StringReaderTerms.character('{'), + Scope.increaseDepth(), + rules.named(mapEntries), + Scope.decreaseDepth(), + StringReaderTerms.character('}') + ), + scope -> { + List> entries = scope.getOrThrow(mapEntries); + if (entries.isEmpty()) { + return emptyMapValue; + } + Builder builder = ImmutableMap.builderWithExpectedSize(entries.size()); + for (Entry e : entries) { + builder.put(ops.createString(e.getKey()), e.getValue()); + } + return ops.createMap(builder.buildKeepingLast()); + } + ); - for (Entry e : entries) { - builder.put(ops.createString(e.getKey()), e.getValue()); - } - - return ops.createMap(builder.buildKeepingLast()); - }); Atom> listEntries = Atom.of("list_entries"); rules.put( - listEntries, Term.repeatedWithTrailingSeparator(rules.forward(literal), listEntries, StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR)), scope -> scope.getOrThrow(listEntries) + listEntries, + Term.repeatedWithTrailingSeparator( + rules.forward(literal), + listEntries, + StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR) + ), + scope -> scope.getOrThrow(listEntries) ); + Atom arrayPrefix = Atom.of("array_prefix"); rules.put( arrayPrefix, - Term.alternative( - Term.sequence(StringReaderTerms.character('B'), Term.marker(arrayPrefix, ArrayPrefix.BYTE)), - Term.sequence(StringReaderTerms.character('L'), Term.marker(arrayPrefix, ArrayPrefix.LONG)), - Term.sequence(StringReaderTerms.character('I'), Term.marker(arrayPrefix, ArrayPrefix.INT)) - ), + Term.alternative( + Term.sequence(StringReaderTerms.character('B'), Term.marker(arrayPrefix, ArrayPrefix.BYTE)), + Term.sequence(StringReaderTerms.character('L'), Term.marker(arrayPrefix, ArrayPrefix.LONG)), + Term.sequence(StringReaderTerms.character('I'), Term.marker(arrayPrefix, ArrayPrefix.INT)) + ), scope -> scope.getOrThrow(arrayPrefix) ); + Atom> intArrayEntries = Atom.of("int_array_entries"); - rules.put(intArrayEntries, Term.repeatedWithTrailingSeparator(integerLiteralRule, intArrayEntries, StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR)), scope -> scope.getOrThrow(intArrayEntries)); + rules.put( + intArrayEntries, + Term.repeatedWithTrailingSeparator( + integerLiteralRule, + intArrayEntries, + StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR) + ), + scope -> scope.getOrThrow(intArrayEntries) + ); + Atom listLiteral = Atom.of("list_literal"); rules.putComplex( listLiteral, - Term.sequence( - StringReaderTerms.character('['), - Scope.increaseDepth(), - Term.alternative(Term.sequence(rules.named(arrayPrefix), StringReaderTerms.character(';'), rules.named(intArrayEntries)), rules.named(listEntries)), - Scope.decreaseDepth(), - StringReaderTerms.character(']') - ), + Term.sequence( + StringReaderTerms.character('['), + Scope.increaseDepth(), + Term.alternative( + Term.sequence( + rules.named(arrayPrefix), + StringReaderTerms.character(';'), + rules.named(intArrayEntries) + ), + rules.named(listEntries) + ), + Scope.decreaseDepth(), + StringReaderTerms.character(']') + ), state -> { Scope scope = state.scope(); ArrayPrefix arrayType = scope.get(arrayPrefix); if (arrayType != null) { List entries = scope.getOrThrow(intArrayEntries); return entries.isEmpty() ? arrayType.create(ops) : arrayType.create(ops, entries, state); - } + } List entries = scope.getOrThrow(listEntries); return entries.isEmpty() ? emptyList : ops.createList(entries.stream()); - } + } ); + NamedRule literalRule = rules.putComplex( literal, - Term.alternative( - Term.sequence(Term.positiveLookahead(NUMBER_LOOKEAHEAD), Term.alternative(rules.namedWithAlias(floatLiteral, literal), rules.named(integerLiteral))), - Term.sequence(Term.positiveLookahead(StringReaderTerms.characters('"', '\'')), Term.cut(), rules.named(quotedStringLiteral)), - Term.sequence(Term.positiveLookahead(StringReaderTerms.character('{')), Term.cut(), rules.namedWithAlias(mapLiteral, literal)), - Term.sequence(Term.positiveLookahead(StringReaderTerms.character('[')), Term.cut(), rules.namedWithAlias(listLiteral, literal)), - rules.namedWithAlias(unquotedStringOrBuiltIn, literal) - ), + Term.alternative( + Term.sequence( + Term.positiveLookahead(NUMBER_LOOKEAHEAD), + Term.alternative( + rules.namedWithAlias(floatLiteral, literal), + rules.named(integerLiteral) + ) + ), + Term.sequence( + Term.positiveLookahead(StringReaderTerms.characters('"', '\'')), + Term.cut(), + rules.named(quotedStringLiteral) + ), + Term.sequence( + Term.positiveLookahead(StringReaderTerms.character('{')), + Term.cut(), + rules.namedWithAlias(mapLiteral, literal) + ), + Term.sequence( + Term.positiveLookahead(StringReaderTerms.character('[')), + Term.cut(), + rules.namedWithAlias(listLiteral, literal) + ), + rules.namedWithAlias(unquotedStringOrBuiltIn, literal) + ), state -> { Scope scope = state.scope(); String quotedString = scope.get(quotedStringLiteral); if (quotedString != null) { return ops.createString(quotedString); - } + } IntegerLiteral integer = scope.get(integerLiteral); return integer != null ? integer.create(ops, state) : scope.getOrThrow(literal); - } + } ); + return new Grammar<>(rules, literalRule); } @@ -646,16 +767,13 @@ public class SnbtGrammar { @Override public T create(DynamicOps ops, List entries, ParseState state) { ByteList result = new ByteArrayList(); - for (IntegerLiteral entry : entries) { Number number = this.buildNumber(entry, state); if (number == null) { return null; } - result.add(number.byteValue()); } - return ops.createByteList(ByteBuffer.wrap(result.toByteArray())); } }, @@ -669,16 +787,13 @@ public class SnbtGrammar { @Override public T create(DynamicOps ops, List entries, ParseState state) { IntStream.Builder result = IntStream.builder(); - for (IntegerLiteral entry : entries) { Number parsedNumber = this.buildNumber(entry, state); if (parsedNumber == null) { return null; } - result.add(parsedNumber.intValue()); } - return ops.createIntList(result.build()); } }, @@ -692,16 +807,13 @@ public class SnbtGrammar { @Override public T create(DynamicOps ops, List entries, ParseState state) { LongStream.Builder result = LongStream.builder(); - for (IntegerLiteral entry : entries) { Number parsedNumber = this.buildNumber(entry, state); if (parsedNumber == null) { return null; } - result.add(parsedNumber.longValue()); } - return ops.createLongList(result.build()); } }; @@ -717,7 +829,6 @@ public class SnbtGrammar { public boolean isAllowed(TypeSuffix type) { return type == this.defaultType || this.additionalTypes.contains(type); } - public abstract T create(DynamicOps ops); @Nullable @@ -742,7 +853,6 @@ public class SnbtGrammar { return !this.isAllowed(type) ? null : type; } } - enum Base { BINARY, DECIMAL, diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedCommandSyntaxException.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedCommandSyntaxException.java index cb4912898..21204ec40 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedCommandSyntaxException.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedCommandSyntaxException.java @@ -10,7 +10,7 @@ import java.util.Optional; import java.util.function.Supplier; public class LocalizedCommandSyntaxException extends CommandSyntaxException { - public static final int CONTEXT_AMOUNT = 10; + 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;