Standardized positioning of scanner after reading each data element
This commit is contained in:
parent
f46d240767
commit
05126cee2a
|
@ -24,13 +24,54 @@ public static class StringNbt
|
||||||
public static CompoundTag Parse(string source)
|
public static CompoundTag Parse(string source)
|
||||||
{
|
{
|
||||||
var bytes = Encoding.UTF8.GetBytes(source);
|
var bytes = Encoding.UTF8.GetBytes(source);
|
||||||
var scanner = new Scanner(bytes, Encoding.UTF8);
|
return Parse(bytes, Encoding.UTF8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parse the given <paramref name="source"/> text into a <see cref="ListTag"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="source">A string containing the SNBT code to parse.</param>
|
||||||
|
/// <returns>The <see cref="CompoundTag"/> instance described in the source text.</returns>
|
||||||
|
/// <exception cref="ArgumentNullException">When <paramref name="source"/> is <see langword="null"/>.</exception>
|
||||||
|
/// <exception cref="SyntaxErrorException">When <paramref name="source"/> is invalid SNBT code.</exception>
|
||||||
|
public static ListTag ParseList(string source)
|
||||||
|
{
|
||||||
|
var bytes = Encoding.UTF8.GetBytes(source);
|
||||||
|
return ParseList(bytes, Encoding.UTF8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parse the given <paramref name="source"/> text into a <see cref="CompoundTag"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="source">A string containing the SNBT code to parse.</param>
|
||||||
|
/// <param name="encoding">Encoding of the <paramref name="source"/>.</param>
|
||||||
|
/// <returns>The <see cref="CompoundTag"/> instance described in the source text.</returns>
|
||||||
|
/// <exception cref="ArgumentNullException">When <paramref name="source"/> is <see langword="null"/>.</exception>
|
||||||
|
/// <exception cref="SyntaxErrorException">When <paramref name="source"/> is invalid SNBT code.</exception>
|
||||||
|
public static CompoundTag Parse(ReadOnlySpan<byte> source, Encoding? encoding = null)
|
||||||
|
{
|
||||||
|
var scanner = new Scanner(source, encoding ?? Encoding.UTF8);
|
||||||
scanner.MoveNext(true, true);
|
scanner.MoveNext(true, true);
|
||||||
scanner.AssertChar('{');
|
scanner.AssertChar('{');
|
||||||
return ParseCompound(null, ref scanner);
|
return ParseCompound(null, ref scanner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parse the given <paramref name="source"/> text into a <see cref="ListTag"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="source">A string containing the SNBT code to parse.</param>
|
||||||
|
/// <param name="encoding">Encoding of the <paramref name="source"/>.</param>
|
||||||
|
/// <returns>The <see cref="CompoundTag"/> instance described in the source text.</returns>
|
||||||
|
/// <exception cref="ArgumentNullException">When <paramref name="source"/> is <see langword="null"/>.</exception>
|
||||||
|
/// <exception cref="SyntaxErrorException">When <paramref name="source"/> is invalid SNBT code.</exception>
|
||||||
|
public static ListTag ParseList(ReadOnlySpan<byte> source, Encoding? encoding = null)
|
||||||
|
{
|
||||||
|
var scanner = new Scanner(source, encoding ?? Encoding.UTF8);
|
||||||
|
scanner.MoveNext(true, true);
|
||||||
|
scanner.AssertChar('[');
|
||||||
|
return ParseList(null, ref scanner);
|
||||||
|
}
|
||||||
|
|
||||||
private static CompoundTag ParseCompound(string? name, ref Scanner scanner)
|
private static CompoundTag ParseCompound(string? name, ref Scanner scanner)
|
||||||
{
|
{
|
||||||
scanner.MoveNext(true, true);
|
scanner.MoveNext(true, true);
|
||||||
|
@ -40,23 +81,20 @@ public static class StringNbt
|
||||||
if (scanner.Current == '}')
|
if (scanner.Current == '}')
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
while (true)
|
while (!scanner.IsEndOfInput)
|
||||||
{
|
{
|
||||||
// Read the name of the tag
|
// Read the name of the tag
|
||||||
var childName = ParseString(ref scanner, out _);
|
var childName = ParseString(ref scanner, out _);
|
||||||
|
|
||||||
// Move to and asser the next significant character is a deliminator.
|
// Move to and asser the next significant character is a deliminator.
|
||||||
// scanner.MoveNext(true, true);
|
scanner.MoveNext(true, true);
|
||||||
scanner.AssertChar(':');
|
scanner.AssertChar(':');
|
||||||
|
|
||||||
// Move to and parse the tag value
|
// Move to and parse the tag value
|
||||||
scanner.MoveNext(true, true);
|
scanner.MoveNext(true, true);
|
||||||
var tag = ParseTag(childName, ref scanner);
|
var tag = ParseTag(childName, ref scanner);
|
||||||
result.Add(tag);
|
result.Add(tag);
|
||||||
// scanner.MoveNext(true, true);
|
scanner.MoveNext(true, true);
|
||||||
|
|
||||||
if (char.IsWhiteSpace(scanner.Current))
|
|
||||||
scanner.MoveNext(true, true);
|
|
||||||
|
|
||||||
// Comma encountered, read another tag.
|
// Comma encountered, read another tag.
|
||||||
if (scanner.Current == ',')
|
if (scanner.Current == ',')
|
||||||
|
@ -68,7 +106,7 @@ public static class StringNbt
|
||||||
// Closing brace encountered, break loop.
|
// Closing brace encountered, break loop.
|
||||||
if (scanner.Current == '}')
|
if (scanner.Current == '}')
|
||||||
{
|
{
|
||||||
scanner.MoveNext(true, false);
|
// scanner.MoveNext(true, false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,6 +117,15 @@ public static class StringNbt
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parses the next logical chunk of data as a string.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="scanner">A reference to the <see cref="Scanner"/> context.</param>
|
||||||
|
/// <param name="quoted">
|
||||||
|
/// Flag indicating if the value was enclosed in a pair of matching single/double quotes, otherwise
|
||||||
|
/// <see langword="false"/> if it was read as a literal span of characters from the input.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>The scanned string.</returns>
|
||||||
private static string ParseString(ref Scanner scanner, out bool quoted)
|
private static string ParseString(ref Scanner scanner, out bool quoted)
|
||||||
{
|
{
|
||||||
var quote = scanner.Current;
|
var quote = scanner.Current;
|
||||||
|
@ -105,7 +152,6 @@ public static class StringNbt
|
||||||
if (scanner.Current == quote)
|
if (scanner.Current == quote)
|
||||||
{
|
{
|
||||||
closed = true;
|
closed = true;
|
||||||
scanner.Position++;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,8 +175,12 @@ public static class StringNbt
|
||||||
var start = scanner.Position;
|
var start = scanner.Position;
|
||||||
for (var length = 0; scanner.MoveNext(false, true); length++)
|
for (var length = 0; scanner.MoveNext(false, true); length++)
|
||||||
{
|
{
|
||||||
if (!AllowedInUnquoted(scanner.Current))
|
if (AllowedInUnquoted(scanner.Current))
|
||||||
return new string(scanner.Source.Slice(start, length + 1));
|
continue;
|
||||||
|
|
||||||
|
// Step back one character so not to consume the one that failed the permission check.
|
||||||
|
scanner.Position--;
|
||||||
|
return new string(scanner.Source.Slice(start, length + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
|
@ -179,11 +229,10 @@ public static class StringNbt
|
||||||
return new IntTag(name, hex);
|
return new IntTag(name, hex);
|
||||||
}
|
}
|
||||||
|
|
||||||
// When all else fails, assume it is an unquoted string
|
// When all else fails, is could only have been an unquoted string
|
||||||
return new StringTag(name, value);
|
return new StringTag(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static bool TryParseNumber(string? name, string value, char suffix, out Tag tag)
|
private static bool TryParseNumber(string? name, string value, char suffix, out Tag tag)
|
||||||
{
|
{
|
||||||
// A much less complicated char.ToLower()
|
// A much less complicated char.ToLower()
|
||||||
|
@ -239,25 +288,23 @@ public static class StringNbt
|
||||||
if (scanner.Current == ']')
|
if (scanner.Current == ']')
|
||||||
return new ListTag(name, TagType.End);
|
return new ListTag(name, TagType.End);
|
||||||
|
|
||||||
if (scanner.Peek() == ';')
|
// No type-prefix, must be a ListTag
|
||||||
{
|
if (scanner.Peek() != ';')
|
||||||
// This is an array of integer values
|
return ParseList(name, ref scanner);
|
||||||
var prefix = scanner.Current;
|
|
||||||
scanner.Position += 2;
|
|
||||||
return prefix switch
|
|
||||||
{
|
|
||||||
'B' => new ByteArrayTag(name, ParseArrayValues<byte>(ref scanner)),
|
|
||||||
'I' => new IntArrayTag(name, ParseArrayValues<int>(ref scanner)),
|
|
||||||
'L' => new LongArrayTag(name, ParseArrayValues<long>(ref scanner)),
|
|
||||||
_ => throw scanner.SyntaxError($"Invalid type specifier. Expected 'B', 'I', or 'L', got '{prefix}'.")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// No prefix, so this must be a list of tags if valid
|
// This is an array of integral values
|
||||||
return ParseList(name, ref scanner);
|
var prefix = scanner.Current;
|
||||||
|
scanner.Position += 2;
|
||||||
|
return prefix switch
|
||||||
|
{
|
||||||
|
'B' => new ByteArrayTag(name, ParseArrayValues<byte>(ref scanner)),
|
||||||
|
'I' => new IntArrayTag(name, ParseArrayValues<int>(ref scanner)),
|
||||||
|
'L' => new LongArrayTag(name, ParseArrayValues<long>(ref scanner)),
|
||||||
|
_ => throw scanner.SyntaxError($"Invalid type specifier. Expected 'B', 'I', or 'L', got '{prefix}'.")
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Tag ParseList(string? name, ref Scanner scanner)
|
private static ListTag ParseList(string? name, ref Scanner scanner)
|
||||||
{
|
{
|
||||||
var list = new List<Tag>();
|
var list = new List<Tag>();
|
||||||
while (true)
|
while (true)
|
||||||
|
@ -265,8 +312,7 @@ public static class StringNbt
|
||||||
var child = ParseTag(null, ref scanner);
|
var child = ParseTag(null, ref scanner);
|
||||||
list.Add(child);
|
list.Add(child);
|
||||||
|
|
||||||
if (char.IsWhiteSpace(scanner.Current))
|
scanner.MoveNext(true, true);
|
||||||
scanner.MoveNext(true, true);
|
|
||||||
|
|
||||||
// Comma encountered, read another tag.
|
// Comma encountered, read another tag.
|
||||||
if (scanner.Current == ',')
|
if (scanner.Current == ',')
|
||||||
|
@ -278,7 +324,6 @@ public static class StringNbt
|
||||||
// Closing brace encountered, break loop.
|
// Closing brace encountered, break loop.
|
||||||
if (scanner.Current == ']')
|
if (scanner.Current == ']')
|
||||||
{
|
{
|
||||||
scanner.MoveNext(true, false);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -295,7 +340,6 @@ public static class StringNbt
|
||||||
// Early-out for []
|
// Early-out for []
|
||||||
if (scanner.Current == ']')
|
if (scanner.Current == ']')
|
||||||
{
|
{
|
||||||
scanner.Position++;
|
|
||||||
return Array.Empty<T>();
|
return Array.Empty<T>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -318,7 +362,6 @@ public static class StringNbt
|
||||||
for (var i = 0; i < values.Length; i++)
|
for (var i = 0; i < values.Length; i++)
|
||||||
values[i] = T.Parse(strings[i], NumberStyles.Integer, NumberFormatInfo.InvariantInfo);
|
values[i] = T.Parse(strings[i], NumberStyles.Integer, NumberFormatInfo.InvariantInfo);
|
||||||
|
|
||||||
scanner.Position++; // Consume the closing ']'
|
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue