Files
UltimateFishing2020/Assets/Scripts/Assembly-CSharp/Gaia/FullSerializer/fsJsonParser.cs
2026-03-04 10:03:45 +08:00

524 lines
11 KiB
C#

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
namespace Gaia.FullSerializer
{
public class fsJsonParser
{
private int _start;
private string _input;
private readonly StringBuilder _cachedStringBuilder = new StringBuilder(256);
private fsResult MakeFailure(string message)
{
int num = Math.Max(0, _start - 20);
int length = Math.Min(50, _input.Length - num);
return fsResult.Fail("Error while parsing: " + message + "; context = <" + _input.Substring(num, length) + ">");
}
private bool TryMoveNext()
{
if (_start < _input.Length)
{
_start++;
return true;
}
return false;
}
private bool HasValue()
{
return HasValue(0);
}
private bool HasValue(int offset)
{
if (_start + offset >= 0)
{
return _start + offset < _input.Length;
}
return false;
}
private char Character()
{
return Character(0);
}
private char Character(int offset)
{
return _input[_start + offset];
}
private void SkipSpace()
{
while (HasValue())
{
if (char.IsWhiteSpace(Character()))
{
TryMoveNext();
continue;
}
if (!HasValue(1) || Character(0) != '/')
{
break;
}
if (Character(1) == '/')
{
while (HasValue() && !Environment.NewLine.Contains(Character().ToString() ?? ""))
{
TryMoveNext();
}
}
else
{
if (Character(1) != '*')
{
continue;
}
TryMoveNext();
TryMoveNext();
while (HasValue(1))
{
if (Character(0) == '*' && Character(1) == '/')
{
TryMoveNext();
TryMoveNext();
TryMoveNext();
break;
}
TryMoveNext();
}
}
}
}
private bool IsHex(char c)
{
if ((c < '0' || c > '9') && (c < 'a' || c > 'f'))
{
if (c >= 'A')
{
return c <= 'F';
}
return false;
}
return true;
}
private uint ParseSingleChar(char c1, uint multipliyer)
{
uint result = 0u;
if (c1 >= '0' && c1 <= '9')
{
result = (uint)(c1 - 48) * multipliyer;
}
else if (c1 >= 'A' && c1 <= 'F')
{
result = (uint)(c1 - 65 + 10) * multipliyer;
}
else if (c1 >= 'a' && c1 <= 'f')
{
result = (uint)(c1 - 97 + 10) * multipliyer;
}
return result;
}
private uint ParseUnicode(char c1, char c2, char c3, char c4)
{
uint num = ParseSingleChar(c1, 4096u);
uint num2 = ParseSingleChar(c2, 256u);
uint num3 = ParseSingleChar(c3, 16u);
uint num4 = ParseSingleChar(c4, 1u);
return num + num2 + num3 + num4;
}
private fsResult TryUnescapeChar(out char escaped)
{
TryMoveNext();
if (!HasValue())
{
escaped = ' ';
return MakeFailure("Unexpected end of input after \\");
}
switch (Character())
{
case '\\':
TryMoveNext();
escaped = '\\';
return fsResult.Success;
case '/':
TryMoveNext();
escaped = '/';
return fsResult.Success;
case '"':
TryMoveNext();
escaped = '"';
return fsResult.Success;
case 'a':
TryMoveNext();
escaped = '\a';
return fsResult.Success;
case 'b':
TryMoveNext();
escaped = '\b';
return fsResult.Success;
case 'f':
TryMoveNext();
escaped = '\f';
return fsResult.Success;
case 'n':
TryMoveNext();
escaped = '\n';
return fsResult.Success;
case 'r':
TryMoveNext();
escaped = '\r';
return fsResult.Success;
case 't':
TryMoveNext();
escaped = '\t';
return fsResult.Success;
case '0':
TryMoveNext();
escaped = '\0';
return fsResult.Success;
case 'u':
TryMoveNext();
if (IsHex(Character(0)) && IsHex(Character(1)) && IsHex(Character(2)) && IsHex(Character(3)))
{
uint num = ParseUnicode(Character(0), Character(1), Character(2), Character(3));
TryMoveNext();
TryMoveNext();
TryMoveNext();
TryMoveNext();
escaped = (char)num;
return fsResult.Success;
}
escaped = '\0';
return MakeFailure($"invalid escape sequence '\\u{Character(0)}{Character(1)}{Character(2)}{Character(3)}'\n");
default:
escaped = '\0';
return MakeFailure($"Invalid escape sequence \\{Character()}");
}
}
private fsResult TryParseExact(string content)
{
for (int i = 0; i < content.Length; i++)
{
if (Character() != content[i])
{
return MakeFailure("Expected " + content[i]);
}
if (!TryMoveNext())
{
return MakeFailure("Unexpected end of content when parsing " + content);
}
}
return fsResult.Success;
}
private fsResult TryParseTrue(out fsData data)
{
fsResult result = TryParseExact("true");
if (result.Succeeded)
{
data = new fsData(boolean: true);
return fsResult.Success;
}
data = null;
return result;
}
private fsResult TryParseFalse(out fsData data)
{
fsResult result = TryParseExact("false");
if (result.Succeeded)
{
data = new fsData(boolean: false);
return fsResult.Success;
}
data = null;
return result;
}
private fsResult TryParseNull(out fsData data)
{
fsResult result = TryParseExact("null");
if (result.Succeeded)
{
data = new fsData();
return fsResult.Success;
}
data = null;
return result;
}
private bool IsSeparator(char c)
{
if (!char.IsWhiteSpace(c) && c != ',' && c != '}')
{
return c == ']';
}
return true;
}
private fsResult TryParseNumber(out fsData data)
{
int start = _start;
while (TryMoveNext() && HasValue() && !IsSeparator(Character()))
{
}
string text = _input.Substring(start, _start - start);
if (!text.Contains("."))
{
switch (text)
{
case "Infinity":
case "-Infinity":
case "NaN":
break;
default:
{
if (!ulong.TryParse(text, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
{
data = null;
return MakeFailure("Bad UInt64 format with " + text);
}
data = new fsData(result);
return fsResult.Success;
}
}
}
if (!double.TryParse(text, NumberStyles.Any, CultureInfo.InvariantCulture, out var result2))
{
data = null;
return MakeFailure("Bad double format with " + text);
}
data = new fsData(result2);
return fsResult.Success;
}
private fsResult TryParseString(out string str)
{
_cachedStringBuilder.Length = 0;
if (Character() != '"' || !TryMoveNext())
{
str = string.Empty;
return MakeFailure("Expected initial \" when parsing a string");
}
while (HasValue() && Character() != '"')
{
char c = Character();
if (c == '\\')
{
char escaped;
fsResult result = TryUnescapeChar(out escaped);
if (result.Failed)
{
str = string.Empty;
return result;
}
_cachedStringBuilder.Append(escaped);
}
else
{
_cachedStringBuilder.Append(c);
if (!TryMoveNext())
{
str = string.Empty;
return MakeFailure("Unexpected end of input when reading a string");
}
}
}
if (!HasValue() || Character() != '"' || !TryMoveNext())
{
str = string.Empty;
return MakeFailure("No closing \" when parsing a string");
}
str = _cachedStringBuilder.ToString();
return fsResult.Success;
}
private fsResult TryParseArray(out fsData arr)
{
if (Character() != '[')
{
arr = null;
return MakeFailure("Expected initial [ when parsing an array");
}
if (!TryMoveNext())
{
arr = null;
return MakeFailure("Unexpected end of input when parsing an array");
}
SkipSpace();
List<fsData> list = new List<fsData>();
while (HasValue() && Character() != ']')
{
fsData data;
fsResult result = RunParse(out data);
if (result.Failed)
{
arr = null;
return result;
}
list.Add(data);
SkipSpace();
if (HasValue() && Character() == ',')
{
if (!TryMoveNext())
{
break;
}
SkipSpace();
}
}
if (!HasValue() || Character() != ']' || !TryMoveNext())
{
arr = null;
return MakeFailure("No closing ] for array");
}
arr = new fsData(list);
return fsResult.Success;
}
private fsResult TryParseObject(out fsData obj)
{
if (Character() != '{')
{
obj = null;
return MakeFailure("Expected initial { when parsing an object");
}
if (!TryMoveNext())
{
obj = null;
return MakeFailure("Unexpected end of input when parsing an object");
}
SkipSpace();
Dictionary<string, fsData> dictionary = new Dictionary<string, fsData>(fsConfig.IsCaseSensitive ? StringComparer.CurrentCulture : StringComparer.CurrentCultureIgnoreCase);
while (HasValue() && Character() != '}')
{
SkipSpace();
fsResult result = TryParseString(out var str);
if (result.Failed)
{
obj = null;
return result;
}
SkipSpace();
if (!HasValue() || Character() != ':' || !TryMoveNext())
{
obj = null;
return MakeFailure("Expected : after key \"" + str + "\"");
}
SkipSpace();
result = RunParse(out var data);
if (result.Failed)
{
obj = null;
return result;
}
dictionary.Add(str, data);
SkipSpace();
if (HasValue() && Character() == ',')
{
if (!TryMoveNext())
{
break;
}
SkipSpace();
}
}
if (!HasValue() || Character() != '}' || !TryMoveNext())
{
obj = null;
return MakeFailure("No closing } for object");
}
obj = new fsData(dictionary);
return fsResult.Success;
}
private fsResult RunParse(out fsData data)
{
SkipSpace();
if (!HasValue())
{
data = null;
return MakeFailure("Unexpected end of input");
}
switch (Character())
{
case '+':
case '-':
case '.':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case 'I':
case 'N':
return TryParseNumber(out data);
case '"':
{
string str;
fsResult result = TryParseString(out str);
if (result.Failed)
{
data = null;
return result;
}
data = new fsData(str);
return fsResult.Success;
}
case '[':
return TryParseArray(out data);
case '{':
return TryParseObject(out data);
case 't':
return TryParseTrue(out data);
case 'f':
return TryParseFalse(out data);
case 'n':
return TryParseNull(out data);
default:
data = null;
return MakeFailure("unable to parse; invalid token \"" + Character() + "\"");
}
}
public static fsResult Parse(string input, out fsData data)
{
if (string.IsNullOrEmpty(input))
{
data = null;
return fsResult.Fail("No input");
}
return new fsJsonParser(input).RunParse(out data);
}
public static fsData Parse(string input)
{
Parse(input, out var data).AssertSuccess();
return data;
}
private fsJsonParser(string input)
{
_input = input;
_start = 0;
}
}
}