DTLib/DTLib.Dtsod/DtsodV21.cs
2023-02-24 20:51:23 +06:00

329 lines
12 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

namespace DTLib.Dtsod;
// v21
// парсер теперь не может игнорировать комменты, потом починю
// теперь числовые значения конвертируются в правильный тип, а не в int64/uint64 (новый вариант switch из c#9.0 делал какую-то дичь)
// исправлены некоторые другие баги
public class DtsodV21 : Dictionary<string, dynamic>, IDtsod
{
public DtsodVersion Version { get; } = DtsodVersion.V21;
public IDictionary<string, dynamic> ToDictionary() => this;
readonly string Text;
//public Dictionary<string, dynamic> Values { get; set; }
public DtsodV21(string text)
{
Text = text;
foreach (KeyValuePair<string, dynamic> pair in Parse(text))
Add(pair.Key, pair.Value);
}
public DtsodV21(IDictionary<string, dynamic> rawDict)
{
Text = "";
foreach (KeyValuePair<string, dynamic> pair in rawDict)
Add(pair.Key, pair.Value);
}
// выдаёт Exception
public new dynamic this[string key]
{
get => TryGetValue(key, out dynamic value) ? value : throw new Exception($"Dtsod[{key}] key not found");
set
{
if (!TrySetValue(key, value)) throw new Exception($"Dtsod[{key}] key not found");
}
}
// не выдаёт KeyNotFoundException
public new bool TryGetValue(string key, out dynamic value)
{
try
{
value = base[key];
return true;
}
catch (KeyNotFoundException)
{
value = null;
return false;
}
}
public bool TrySetValue(string key, dynamic value)
{
try
{
base[key] = value;
return true;
}
catch (KeyNotFoundException)
{
return false;
}
}
public override string ToString() => Text;
enum ValueType
{
List,
Complex,
String,
Default
}
Dictionary<string, dynamic> Parse(string text)
{
Dictionary<string, dynamic> parsed = new();
int i = 0;
for (; i < text.Length; i++)
ReadName();
return parsed;
// СЛОМАНО
/*void ReadCommentLine()
{
for (; i < text.Length && text[i] != '\n'; i++) DebugNoTime("h", text[i].ToString());
}*/
void ReadName()
{
bool isListElem = false;
dynamic value;
StringBuilder defaultNameBuilder = new();
for (; i < text.Length; i++)
{
switch (text[i])
{
case ' ':
case '\t':
case '\r':
case '\n':
break;
case ':':
i++;
string name = defaultNameBuilder.ToString();
value = ReadValue();
// если value это null, эта строка выдавала ошибку
//DebugNoTime("c", $"parsed.Add({name}, {value} { value.GetType() })");
if (isListElem)
{
if (!parsed.ContainsKey(name))
parsed.Add(name, new List<dynamic>());
parsed[name].Add(value);
}
else
parsed.Add(name, value);
return;
// строка, начинающаяся с # будет считаться комментом
case '#':
//ReadCommentLine();
break;
case '}':
throw new Exception("Parse.ReadName() error: unexpected '}' at " + i + " char");
// если $ перед названием параметра поставить, значение value добавится в лист с названием name
case '$':
if (defaultNameBuilder.ToString().Length != 0)
throw new Exception("Parse.ReadName() error: unexpected '$' at " + i + " char");
isListElem = true;
break;
case ';':
throw new Exception("Parse.ReadName() error: unexpected ';' at " + i + " char");
default:
defaultNameBuilder.Append(text[i]);
break;
}
}
}
dynamic ReadValue()
{
ValueType type = ValueType.Default;
dynamic value = null;
string ReadString()
{
i++;
StringBuilder valueBuilder = new();
valueBuilder.Append('"');
for (; text[i] != '"' || text[i - 1] == '\\'; i++)
{
valueBuilder.Append(text[i]);
}
valueBuilder.Append('"');
type = ValueType.String;
return valueBuilder.ToString();
}
List<dynamic> ReadList()
{
i++;
List<dynamic> output = new();
StringBuilder valueBuilder = new();
for (; text[i] != ']'; i++)
{
switch (text[i])
{
case ' ':
case '\t':
case '\r':
case '\n':
break;
case ',':
ParseValueToRightType(valueBuilder.ToString());
output.Add(value);
valueBuilder.Clear();
break;
default:
valueBuilder.Append(text[i]);
break;
}
}
if (valueBuilder.Length > 0)
{
ParseValueToRightType(valueBuilder.ToString());
output.Add(value);
}
type = ValueType.List;
return output;
}
Dictionary<string, dynamic> ReadComplex()
{
StringBuilder valueBuilder = new();
int balance = 1;
i++;
for (; balance != 0; i++)
{
switch (text[i])
{
case '"':
valueBuilder.Append(ReadString());
break;
case '}':
balance--;
if (balance != 0)
valueBuilder.Append(text[i]);
break;
case '{':
balance++;
valueBuilder.Append(text[i]);
break;
default:
valueBuilder.Append(text[i]);
break;
}
}
i--; // i++ в for выполняется даже когда balance == 0, то есть text[i] получается == ;, что ломает всё
type = ValueType.Complex;
return Parse(valueBuilder.ToString());
}
void ParseValueToRightType(string stringValue)
{
switch (stringValue)
{
// bool
case "true":
case "false":
value = stringValue.ToBool();
break;
// null
case "null":
value = null;
break;
default:
if (stringValue.Contains('"'))
value = stringValue.Remove(stringValue.Length - 1).Remove(0, 1);
// double
else if (stringValue.Contains('.'))
value = stringValue.ToDouble();
// ushort; ulong; uint
else if (stringValue.Length > 2 && stringValue[stringValue.Length - 2] == 'u')
{
switch (stringValue[stringValue.Length - 1])
{
case 's':
value = stringValue.Remove(stringValue.Length - 2).ToUShort();
break;
case 'i':
value = stringValue.Remove(stringValue.Length - 2).ToUInt();
break;
case 'l':
value = stringValue.Remove(stringValue.Length - 2).ToULong();
break;
default:
throw new Exception($"Dtsod.Parse.ReadValue() error: value= wrong type <u{stringValue[stringValue.Length - 1]}>");
}
}
// short; long; int
else
switch (stringValue[stringValue.Length - 1])
{
case 's':
value = stringValue.Remove(stringValue.Length - 1).ToShort();
break;
case 'l':
value = stringValue.Remove(stringValue.Length - 1).ToLong();
break;
default:
value = stringValue.ToInt();
break;
}
break;
}
}
StringBuilder defaultValueBuilder = new();
for (; i < text.Length; i++)
{
switch (text[i])
{
case ' ':
case '\t':
case '\r':
case '\n':
break;
case '"':
value = ReadString();
break;
case ';':
switch (type)
{
case ValueType.String:
ParseValueToRightType(value);
break;
case ValueType.Default:
ParseValueToRightType(defaultValueBuilder.ToString());
break;
}
return value;
case '[':
value = ReadList();
break;
case '{':
value = ReadComplex();
break;
// строка, начинающаяся с # будет считаться комментом
case '#':
//ReadCommentLine();
break;
default:
defaultValueBuilder.Append(text[i]);
break;
}
}
throw new Exception("Dtsod.Parse.ReadValue error: wtf it's the end of function");
}
}
}