From d04855bff2f8ed362c969a0233f2458d8ae457af Mon Sep 17 00:00:00 2001 From: Timerix22 Date: Fri, 31 Dec 2021 00:17:32 +0300 Subject: [PATCH] dtsod --- Dtsod/DtsodV21.cs | 717 +++++++++---------- Dtsod/DtsodV22.cs | 868 ++++++++++++------------ Dtsod/DtsodVersion.cs | 8 + Dtsod/DtsodVersionConverter.cs | 13 + Dtsod/IDtsod.cs | 8 + Dtsod/V30/DtsodDict.cs | 67 ++ Dtsod/V30/DtsodSerializableAttribute.cs | 11 +- Dtsod/V30/DtsodV30.cs | 284 +++++++- Dtsod/V30/DtsodVersion.cs | 9 - Dtsod/V30/TypeHelper.cs | 66 ++ 10 files changed, 1250 insertions(+), 801 deletions(-) create mode 100644 Dtsod/DtsodVersion.cs create mode 100644 Dtsod/DtsodVersionConverter.cs create mode 100644 Dtsod/IDtsod.cs create mode 100644 Dtsod/V30/DtsodDict.cs delete mode 100644 Dtsod/V30/DtsodVersion.cs create mode 100644 Dtsod/V30/TypeHelper.cs diff --git a/Dtsod/DtsodV21.cs b/Dtsod/DtsodV21.cs index 20c73e1..f855c76 100644 --- a/Dtsod/DtsodV21.cs +++ b/Dtsod/DtsodV21.cs @@ -1,363 +1,374 @@ -using DTLib.Extensions; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using static DTLib.PublicLog; +namespace DTLib.Dtsod; -namespace DTLib.Dtsod +// v21 +// парсер теперь не может игнорировать комменты, потом починю +// теперь числовые значения конвертируются в правильный тип, а не в int64/uint64 (новый вариант switch из c#9.0 делал какую-то дичь) +// исправлены некоторые другие баги + +public class DtsodV21 : Dictionary, IDtsod { - // - // это как json но не совсем - // - // v2.0 - // полностью переписан парсер - // - // v2.1 - // парсер теперь не может игнорировать комменты, потом починю - // теперь числовые значения конвертируются в правильный тип, а не в int64/uint64 (новый вариант switch из c#9.0 делал какую-то херню) - // исправлены некоторые другие баги + public DtsodVersion Version { get; } = DtsodVersion.V21; - public class DtsodV21 : Dictionary + public IDictionary ToDictionary() => this; + + readonly string Text; + + //public Dictionary Values { get; set; } + public DtsodV21(string text) { - static readonly bool debug = false; - string Text; + Text = text; + foreach (KeyValuePair pair in Parse(text)) + Add(pair.Key, pair.Value); + } + public DtsodV21(IDictionary rawDict) + { + Text = ""; + foreach (KeyValuePair pair in rawDict) + Add(pair.Key, pair.Value); + } - //public Dictionary Values { get; set; } - public DtsodV21(string text) + + // выдаёт Exception + public new dynamic this[string key] + { + get => TryGetValue(key, out dynamic value) ? value : throw new Exception($"Dtsod[{key}] key not found"); + set { - Text = text; - foreach (KeyValuePair pair in Parse(text)) - Add(pair.Key, pair.Value); - } - public DtsodV21(Dictionary rawDict) - { - Text = ""; - foreach (KeyValuePair 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 Parse(string text) - { - Dictionary parsed = new(); - int i = 0; - for (; i < text.Length; i++) - ReadName(); - DebugNoTime("g", $"Parse returns {parsed.Keys.Count} keys\n"); - 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(); - - DebugNoTime("m", "ReadName\n"); - 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() })\n"); - if (isListElem) - { - if (!parsed.ContainsKey(name)) - parsed.Add(name, new List()); - 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 '$': - DebugNoTime("w", text[i].ToString()); - 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: - DebugNoTime("w", text[i].ToString()); - 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++) - { - DebugNoTime("h", text[i].ToString()); - valueBuilder.Append(text[i]); - } - valueBuilder.Append('"'); - DebugNoTime("h", text[i].ToString()); - type = ValueType.String; - return valueBuilder.ToString(); - } - - List ReadList() - { - i++; - List output = new(); - StringBuilder valueBuilder = new(); - for (; text[i] != ']'; i++) - { - DebugNoTime("c", text[i].ToString()); - 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); - } - DebugNoTime("c", text[i].ToString()); - type = ValueType.List; - return output; - } - - Dictionary ReadComplex() - { - StringBuilder valueBuilder = new(); - int balance = 1; - i++; - for (; balance != 0; i++) - { - DebugNoTime("y", text[i].ToString()); - switch (text[i]) - { - case '"': - valueBuilder.Append(ReadString()); - break; - case '}': - balance--; - DebugNoTime("b", $"\nbalance -- = {balance}\n"); - if (balance != 0) - valueBuilder.Append(text[i]); - break; - case '{': - balance++; - DebugNoTime("b", $"\nbalance ++ = {balance}\n"); - 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) - { - - DebugNoTime("b", $"\nParseValueToRightType({stringValue})\n"); - 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 "); - }; - } - // 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.ToShort(); - break; - } - break; - }; - } - - StringBuilder defaultValueBuilder = new(); - DebugNoTime("m", "\nReadValue\n"); - for (; i < text.Length; i++) - { - DebugNoTime("b", text[i].ToString()); - 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"); - } - } - - void Debug(params string[] msg) - { - if (debug) - Log(msg); - } - void DebugNoTime(params string[] msg) - { - if (debug) - LogNoTime(msg); + 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 Parse(string text) + { + Dictionary parsed = new(); + int i = 0; + for (; i < text.Length; i++) + ReadName(); +#if DEBUG + DebugNoTime("g", $"Parse returns {parsed.Keys.Count} keys\n"); +#endif + 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(); + +#if DEBUG + DebugNoTime("m", "ReadName\n"); +#endif + 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() })\n"); + if (isListElem) + { + if (!parsed.ContainsKey(name)) + parsed.Add(name, new List()); + 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 DEBUG + DebugNoTime("w", text[i].ToString()); +#endif + 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: +#if DEBUG + DebugNoTime("w", text[i].ToString()); +#endif + 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++) + { +#if DEBUG + DebugNoTime("h", text[i].ToString()); +#endif + valueBuilder.Append(text[i]); + } + valueBuilder.Append('"'); +#if DEBUG + DebugNoTime("h", text[i].ToString()); +#endif + type = ValueType.String; + return valueBuilder.ToString(); + } + + List ReadList() + { + i++; + List output = new(); + StringBuilder valueBuilder = new(); + for (; text[i] != ']'; i++) + { +#if DEBUG + DebugNoTime("c", text[i].ToString()); +#endif + 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); + } +#if DEBUG + DebugNoTime("c", text[i].ToString()); +#endif + type = ValueType.List; + return output; + } + + Dictionary ReadComplex() + { + StringBuilder valueBuilder = new(); + int balance = 1; + i++; + for (; balance != 0; i++) + { +#if DEBUG + DebugNoTime("y", text[i].ToString()); +#endif + switch (text[i]) + { + case '"': + valueBuilder.Append(ReadString()); + break; + case '}': + balance--; +#if DEBUG + DebugNoTime("b", $"\nbalance -- = {balance}\n"); +#endif + if (balance != 0) + valueBuilder.Append(text[i]); + break; + case '{': + balance++; +#if DEBUG + DebugNoTime("b", $"\nbalance ++ = {balance}\n"); +#endif + 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) + { + +#if DEBUG + DebugNoTime("b", $"\nParseValueToRightType({stringValue})\n"); +#endif + 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 "); + }; + } + // 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.ToShort(); + break; + } + break; + }; + } + + StringBuilder defaultValueBuilder = new(); +#if DEBUG + DebugNoTime("m", "\nReadValue\n"); +#endif + for (; i < text.Length; i++) + { +#if DEBUG + DebugNoTime("b", text[i].ToString()); +#endif + 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"); + } + } + +#if DEBUG + void Debug(params string[] msg) => PublicLog.Log(msg); + void DebugNoTime(params string[] msg) => PublicLog.LogNoTime(msg); +#endif } diff --git a/Dtsod/DtsodV22.cs b/Dtsod/DtsodV22.cs index 1e55848..f9f71ca 100644 --- a/Dtsod/DtsodV22.cs +++ b/Dtsod/DtsodV22.cs @@ -1,135 +1,215 @@ -using DTLib.Extensions; -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Text; +namespace DTLib.Dtsod; -namespace DTLib.Dtsod +// v22 +// метод ToString() теперь деконструирует объект в текст, а не возвращает параметр text из конструктора +// деконструкция листов не работает из-за костыльного определения типов данных + +public class DtsodV22 : Dictionary, IDtsod { - // - // это как json но не совсем - // - // v20 - // полностью переписан парсер - // - // v21 - // парсер теперь не может игнорировать комменты, потом починю - // теперь числовые значения конвертируются в правильный тип, а не в int64/uint64 (новый вариант switch из c#9.0 делал какую-то херню) - // исправлены некоторые другие баги - // - // v22 - // метод ToString() теперь действительно деконструирует объект в текст, а не возвращает параметр text из конструктора + public DtsodVersion Version { get; } = DtsodVersion.V22; - public class DtsodV22 : Dictionary + public IDictionary ToDictionary() { - static readonly bool debug = false; + Dictionary newdict = new(); + foreach (KeyValuePair pair in this) + newdict.Add(pair.Key, pair.Value.Value); + return newdict; + } - public struct ValueStruct + public struct ValueStruct + { + public dynamic Value; + public ValueTypes Type; + public bool IsList; + public ValueStruct(ValueTypes type, dynamic value, bool isList) { - public dynamic Value; - public ValueTypes Type; - public bool IsList; - public ValueStruct(ValueTypes type, dynamic value, bool isList) - { - Value = value; - Type = type; - IsList = isList; - } + Value = value; + Type = type; + IsList = isList; } + } - public enum ValueTypes + public enum ValueTypes + { + List, + Complex, + String, + Short, + Int, + Long, + UShort, + UInt, + ULong, + Double, + Null, + Bool, + Unknown + } + + public DtsodV22() { } + + public DtsodV22(string text) + { + foreach (KeyValuePair pair in Parse(text)) + Add(pair.Key, pair.Value); + } + + public DtsodV22(Dictionary dict) + { + foreach (KeyValuePair pair in dict) + 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 { - List, - Complex, - String, - Short, - Int, - Long, - UShort, - UInt, - ULong, - Double, - Null, - Bool, - Unknown + if (!TrySetValue(key, value)) throw new Exception($"Dtsod[{key}] key not found"); } + } - public DtsodV22() { } - - public DtsodV22(string text) + // не выдаёт KeyNotFoundException + public bool TryGetValue(string key, out dynamic value) + { + try { - foreach (KeyValuePair pair in Parse(text)) - Add(pair.Key, pair.Value); + value = base[key].Value; + return true; } - - public DtsodV22(Dictionary dict) + catch (KeyNotFoundException) { - foreach (KeyValuePair pair in dict) - Add(pair.Key, pair.Value); + value = null; + return false; } - - // выдаёт Exception - public new dynamic this[string key] + } + public bool TrySetValue(string key, dynamic value) + { + try { - 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"); - } + bool isList = value is IList; + base[key] = new(base[key].Type, value, isList); + return true; } + catch (KeyNotFoundException) + { return false; } + } - // не выдаёт KeyNotFoundException - public bool TryGetValue(string key, out dynamic value) - { - try - { - value = base[key].Value; - return true; - } - catch (KeyNotFoundException) - { - value = null; - return false; - } - } - public bool TrySetValue(string key, dynamic value) - { - try - { - bool isList = value is IList; - base[key] = new(base[key].Type, value, isList); - return true; - } - catch (KeyNotFoundException) - { return false; } - } + DtsodV22 Parse(string text) + { + Dictionary parsed = new(); + int i = 0; + for (; i < text.Length; i++) + ReadName(); +#if DEBUG + DebugNoTime("g", $"Parse returns {parsed.Keys.Count} keys\n"); +#endif + return new DtsodV22(parsed); - DtsodV22 Parse(string text) + // СЛОМАНО + /*void ReadCommentLine() { - Dictionary parsed = new(); - int i = 0; + for (; i < text.Length && text[i] != '\n'; i++) Debug("h", text[i].ToString()); + }*/ + + void ReadName() + { + + bool isListElem = false; + dynamic value; + StringBuilder defaultNameBuilder = new(); + +#if DEBUG + DebugNoTime("m", "ReadName\n"); +#endif for (; i < text.Length; i++) - ReadName(); - DebugNoTime("g", $"Parse returns {parsed.Keys.Count} keys\n"); - return new DtsodV22(parsed); - - // СЛОМАНО - /*void ReadCommentLine() { - for (; i < text.Length && text[i] != '\n'; i++) Debug("h", text[i].ToString()); - }*/ - - void ReadName() - { - - bool isListElem = false; - dynamic value; - StringBuilder defaultNameBuilder = new(); - - DebugNoTime("m", "ReadName\n"); - for (; i < text.Length; i++) + switch (text[i]) { + case ' ': + case '\t': + case '\r': + case '\n': + break; + case ':': + i++; + string name = defaultNameBuilder.ToString(); + value = ReadValue(out ValueTypes type, out bool isList); +#if DEBUG + DebugNoTime("c", $"parsed.Add({name},{type} {value} )\n"); +#endif + if (isListElem) + { + if (!parsed.ContainsKey(name)) + parsed.Add(name, new(type, new List(), isList)); + parsed[name].Value.Add(value); + } + else parsed.Add(name, new(type, value, isList)); + return; + // строка, начинающаяся с # будет считаться комментом + case '#': + //ReadCommentLine(); + break; + case '}': + throw new Exception("Parse.ReadName() error: unexpected '}' at " + i + " char"); + // если $ перед названием параметра поставить, значение value добавится в лист с названием name + case '$': +#if DEBUG + DebugNoTime("w", text[i].ToString()); +#endif + 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: +#if DEBUG + DebugNoTime("w", text[i].ToString()); +#endif + defaultNameBuilder.Append(text[i]); + break; + } + } + } + + dynamic ReadValue(out ValueTypes outType, out bool isList) + { + ValueTypes type = ValueTypes.Unknown; + isList = false; + dynamic value = null; + + string ReadString() + { + i++; + StringBuilder valueBuilder = new(); + valueBuilder.Append('"'); + for (; text[i] != '"' || text[i - 1] == '\\'; i++) + { +#if DEBUG + DebugNoTime("h", text[i].ToString()); +#endif + valueBuilder.Append(text[i]); + } + valueBuilder.Append('"'); +#if DEBUG + DebugNoTime("h", text[i].ToString()); +#endif + type = ValueTypes.String; + return valueBuilder.ToString(); + } + + List ReadList() + { + i++; + List output = new(); + StringBuilder valueBuilder = new(); + for (; text[i] != ']'; i++) + { +#if DEBUG + DebugNoTime("c", text[i].ToString()); +#endif switch (text[i]) { case ' ': @@ -137,341 +217,279 @@ namespace DTLib.Dtsod case '\r': case '\n': break; - case ':': - i++; - string name = defaultNameBuilder.ToString(); - value = ReadValue(out ValueTypes type, out bool isList); - DebugNoTime("c", $"parsed.Add({name},{type} {value} )\n"); - if (isListElem) - { - if (!parsed.ContainsKey(name)) - parsed.Add(name, new(type, new List(), isList)); - parsed[name].Value.Add(value); - } - else - parsed.Add(name, new(type, value, isList)); - return; - // строка, начинающаяся с # будет считаться комментом - case '#': - //ReadCommentLine(); + 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); + } +#if DEBUG + DebugNoTime("c", text[i].ToString()); +#endif + type = ValueTypes.List; + return output; + } + + Dictionary ReadComplex() + { + StringBuilder valueBuilder = new(); + int balance = 1; + i++; + for (; balance != 0; i++) + { +#if DEBUG + DebugNoTime("y", text[i].ToString()); +#endif + switch (text[i]) + { + case '"': + valueBuilder.Append(ReadString()); break; case '}': - throw new Exception("Parse.ReadName() error: unexpected '}' at " + i + " char"); - // если $ перед названием параметра поставить, значение value добавится в лист с названием name - case '$': - DebugNoTime("w", text[i].ToString()); - 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: - DebugNoTime("w", text[i].ToString()); - defaultNameBuilder.Append(text[i]); - break; - } - } - } - - dynamic ReadValue(out ValueTypes outType, out bool isList) - { - ValueTypes type = ValueTypes.Unknown; - isList = false; - dynamic value = null; - - string ReadString() - { - i++; - StringBuilder valueBuilder = new(); - valueBuilder.Append('"'); - for (; text[i] != '"' || text[i - 1] == '\\'; i++) - { - DebugNoTime("h", text[i].ToString()); - valueBuilder.Append(text[i]); - } - valueBuilder.Append('"'); - DebugNoTime("h", text[i].ToString()); - type = ValueTypes.String; - return valueBuilder.ToString(); - } - - List ReadList() - { - i++; - List output = new(); - StringBuilder valueBuilder = new(); - for (; text[i] != ']'; i++) - { - DebugNoTime("c", text[i].ToString()); - switch (text[i]) - { - case ' ': - case '\t': - case '\r': - case '\n': - break; - case ',': - ParseValueToRightType(valueBuilder.ToString()); - output.Add(value); - valueBuilder.Clear(); - break; - default: + balance--; +#if DEBUG + DebugNoTime("b", $"\nbalance -- = {balance}\n"); +#endif + if (balance != 0) valueBuilder.Append(text[i]); - break; - } - } - if (valueBuilder.Length > 0) - { - ParseValueToRightType(valueBuilder.ToString()); - output.Add(value); - } - DebugNoTime("c", text[i].ToString()); - type = ValueTypes.List; - return output; - } - - Dictionary ReadComplex() - { - StringBuilder valueBuilder = new(); - int balance = 1; - i++; - for (; balance != 0; i++) - { - DebugNoTime("y", text[i].ToString()); - switch (text[i]) - { - case '"': - valueBuilder.Append(ReadString()); - break; - case '}': - balance--; - DebugNoTime("b", $"\nbalance -- = {balance}\n"); - if (balance != 0) - valueBuilder.Append(text[i]); - break; - case '{': - balance++; - DebugNoTime("b", $"\nbalance ++ = {balance}\n"); - valueBuilder.Append(text[i]); - break; - default: - valueBuilder.Append(text[i]); - break; - } - } - i--; // i++ в for выполняется даже когда balance == 0, то есть text[i] получается == ;, что ломает всё - type = ValueTypes.Complex; - return Parse(valueBuilder.ToString()); - } - - void ParseValueToRightType(string stringValue) - { - DebugNoTime("b", $"\nParseValueToRightType({stringValue})\n"); - switch (stringValue) - { - - // bool - case "true": - case "false": - type = ValueTypes.Bool; - value = stringValue.ToBool(); - break; - // null - case "null": - type = ValueTypes.Null; - value = null; - break; - default: - if (stringValue.Contains('"')) - { - type = ValueTypes.String; - value = stringValue.Remove(stringValue.Length - 1).Remove(0, 1); - } - // double - else if (stringValue.Contains('.')) - { - type = ValueTypes.Double; - value = stringValue.ToDouble(); - } - // ushort; ulong; uint - else if (stringValue.Length > 2 && stringValue[stringValue.Length - 2] == 'u') - { - switch (stringValue[stringValue.Length - 1]) - { - case 's': - type = ValueTypes.UShort; - value = stringValue.Remove(stringValue.Length - 2).ToUShort(); - break; - case 'i': - type = ValueTypes.UInt; - value = stringValue.Remove(stringValue.Length - 2).ToUInt(); - break; - case 'l': - type = ValueTypes.ULong; - value = stringValue.Remove(stringValue.Length - 2).ToULong(); - break; - default: - throw new Exception($"Dtsod.Parse.ReadValue() error: value <{stringValue}> has wrong type"); - }; - } - // short; long; int - else - switch (stringValue[stringValue.Length - 1]) - { - case 's': - type = ValueTypes.Short; - value = stringValue.Remove(stringValue.Length - 1).ToShort(); - break; - case 'l': - type = ValueTypes.Long; - value = stringValue.Remove(stringValue.Length - 1).ToLong(); - break; - default: - type = ValueTypes.Int; - value = stringValue.ToShort(); - break; - } - break; - }; - } - - StringBuilder defaultValueBuilder = new(); - DebugNoTime("m", "\nReadValue\n"); - for (; i < text.Length; i++) - { - DebugNoTime("b", text[i].ToString()); - switch (text[i]) - { - case ' ': - case '\t': - case '\r': - case '\n': - break; - case '"': - value = ReadString(); - break; - case '[': - value = ReadList(); break; case '{': - value = ReadComplex(); - break; - case ';': - switch (type) - { - case ValueTypes.String: - ParseValueToRightType(value); - break; - case ValueTypes.Unknown: - ParseValueToRightType(defaultValueBuilder.ToString()); - break; - case ValueTypes.List: - isList = true; - break; - }; - outType = type; - return value; - // строка, начинающаяся с # будет считаться комментом - case '#': - //ReadCommentLine(); + balance++; +#if DEBUG + DebugNoTime("b", $"\nbalance ++ = {balance}\n"); +#endif + valueBuilder.Append(text[i]); break; default: - defaultValueBuilder.Append(text[i]); + valueBuilder.Append(text[i]); break; } } - throw new Exception("Dtsod.Parse.ReadValue error: wtf it's the end of function"); + i--; // i++ в for выполняется даже когда balance == 0, то есть text[i] получается == ;, что ломает всё + type = ValueTypes.Complex; + return Parse(valueBuilder.ToString()); } - } - public override string ToString() => Deconstruct(this); - - ushort tabCount = 0; - string Deconstruct(DtsodV22 dtsod) - { - StringBuilder outBuilder = new(); - foreach (string key in dtsod.Keys) + void ParseValueToRightType(string stringValue) { - outBuilder.Append('\t', tabCount); - outBuilder.Append(key); - outBuilder.Append(": "); - dtsod.TryGetValue(key, out ValueStruct value); - switch (value.Type) +#if DEBUG + DebugNoTime("b", $"\nParseValueToRightType({stringValue})\n"); +#endif + switch (stringValue) { - case ValueTypes.List: - outBuilder.Append('[').Append(StringConverter.MergeToString((IEnumerable)value.Value, ",")).Append(']'); - //outBuilder.Append("\"list deconstruction is'nt implemented yet\""); + + // bool + case "true": + case "false": + type = ValueTypes.Bool; + value = stringValue.ToBool(); break; - case ValueTypes.Complex: - outBuilder.Append('\n'); - outBuilder.Append('\t', tabCount); - outBuilder.Append("{\n"); - tabCount++; - outBuilder.Append(Deconstruct(value.Value)); - tabCount--; - outBuilder.Append('\t', tabCount); - outBuilder.Append('}'); - break; - case ValueTypes.String: - outBuilder.Append('\"'); - outBuilder.Append(value.Value.ToString()); - outBuilder.Append('\"'); - break; - case ValueTypes.Short: - outBuilder.Append(value.Value.ToString()); - outBuilder.Append('s'); - break; - case ValueTypes.Int: - outBuilder.Append(value.Value.ToString()); - break; - case ValueTypes.Long: - outBuilder.Append(value.Value.ToString()); - outBuilder.Append('l'); - break; - case ValueTypes.UShort: - outBuilder.Append(value.Value.ToString()); - outBuilder.Append("us"); - break; - case ValueTypes.UInt: - outBuilder.Append(value.Value.ToString()); - outBuilder.Append("ui"); - break; - case ValueTypes.ULong: - outBuilder.Append(value.Value.ToString()); - outBuilder.Append("ul"); - break; - case ValueTypes.Double: - outBuilder.Append(value.Value.ToString(System.Globalization.CultureInfo.InvariantCulture)); - break; - case ValueTypes.Null: - outBuilder.Append("null"); - break; - case ValueTypes.Bool: - outBuilder.Append(value.Value.ToString().ToLower()); + // null + case "null": + type = ValueTypes.Null; + value = null; break; default: - throw new Exception($"Dtsod.Deconstruct() error: unknown type: {value.Type}"); - } - - outBuilder.Append(";\n"); + if (stringValue.Contains('"')) + { + type = ValueTypes.String; + value = stringValue.Remove(stringValue.Length - 1).Remove(0, 1); + } + // double + else if (stringValue.Contains('.')) + { + type = ValueTypes.Double; + value = stringValue.ToDouble(); + } + // ushort; ulong; uint + else if (stringValue.Length > 2 && stringValue[stringValue.Length - 2] == 'u') + { + switch (stringValue[stringValue.Length - 1]) + { + case 's': + type = ValueTypes.UShort; + value = stringValue.Remove(stringValue.Length - 2).ToUShort(); + break; + case 'i': + type = ValueTypes.UInt; + value = stringValue.Remove(stringValue.Length - 2).ToUInt(); + break; + case 'l': + type = ValueTypes.ULong; + value = stringValue.Remove(stringValue.Length - 2).ToULong(); + break; + default: + throw new Exception($"Dtsod.Parse.ReadValue() error: value <{stringValue}> has wrong type"); + }; + } + // short; long; int + else + switch (stringValue[stringValue.Length - 1]) + { + case 's': + type = ValueTypes.Short; + value = stringValue.Remove(stringValue.Length - 1).ToShort(); + break; + case 'l': + type = ValueTypes.Long; + value = stringValue.Remove(stringValue.Length - 1).ToLong(); + break; + default: + type = ValueTypes.Int; + value = stringValue.ToShort(); + break; + } + break; + }; } - return outBuilder.ToString(); - } - void DebugNoTime(params string[] msg) - { - if (debug) - PublicLog.LogNoTime(msg); + StringBuilder defaultValueBuilder = new(); +#if DEBUG + DebugNoTime("m", "\nReadValue\n"); +#endif + for (; i < text.Length; i++) + { +#if DEBUG + DebugNoTime("b", text[i].ToString()); +#endif + switch (text[i]) + { + case ' ': + case '\t': + case '\r': + case '\n': + break; + case '"': + value = ReadString(); + break; + case '[': + value = ReadList(); + break; + case '{': + value = ReadComplex(); + break; + case ';': + switch (type) + { + case ValueTypes.String: + ParseValueToRightType(value); + break; + case ValueTypes.Unknown: + ParseValueToRightType(defaultValueBuilder.ToString()); + break; + case ValueTypes.List: + isList = true; + break; + }; + outType = type; + return value; + // строка, начинающаяся с # будет считаться комментом + case '#': + //ReadCommentLine(); + break; + default: + defaultValueBuilder.Append(text[i]); + break; + } + } + throw new Exception("Dtsod.Parse.ReadValue error: wtf it's the end of function"); } - - public DtsodV22 Extend(DtsodV22 newPart) - { - foreach (KeyValuePair pair in newPart) - Add(pair.Key, pair.Value); - return this; - } - - public void Add(KeyValuePair a) => Add(a.Key, a.Value); } + + public override string ToString() => Deconstruct(this); + + ushort tabCount = 0; + string Deconstruct(DtsodV22 dtsod) + { + StringBuilder outBuilder = new(); + foreach (string key in dtsod.Keys) + { + outBuilder.Append('\t', tabCount); + outBuilder.Append(key); + outBuilder.Append(": "); + dtsod.TryGetValue(key, out ValueStruct value); + switch (value.Type) + { + case ValueTypes.List: + outBuilder.Append('[').Append(StringConverter.MergeToString((IEnumerable)value.Value, ",")).Append(']'); + //outBuilder.Append("\"list deconstruction is'nt implemented yet\""); + break; + case ValueTypes.Complex: + outBuilder.Append('\n'); + outBuilder.Append('\t', tabCount); + outBuilder.Append("{\n"); + tabCount++; + outBuilder.Append(Deconstruct(value.Value)); + tabCount--; + outBuilder.Append('\t', tabCount); + outBuilder.Append('}'); + break; + case ValueTypes.String: + outBuilder.Append('\"'); + outBuilder.Append(value.Value.ToString()); + outBuilder.Append('\"'); + break; + case ValueTypes.Short: + outBuilder.Append(value.Value.ToString()); + outBuilder.Append('s'); + break; + case ValueTypes.Int: + outBuilder.Append(value.Value.ToString()); + break; + case ValueTypes.Long: + outBuilder.Append(value.Value.ToString()); + outBuilder.Append('l'); + break; + case ValueTypes.UShort: + outBuilder.Append(value.Value.ToString()); + outBuilder.Append("us"); + break; + case ValueTypes.UInt: + outBuilder.Append(value.Value.ToString()); + outBuilder.Append("ui"); + break; + case ValueTypes.ULong: + outBuilder.Append(value.Value.ToString()); + outBuilder.Append("ul"); + break; + case ValueTypes.Double: + outBuilder.Append(value.Value.ToString(System.Globalization.CultureInfo.InvariantCulture)); + break; + case ValueTypes.Null: + outBuilder.Append("null"); + break; + case ValueTypes.Bool: + outBuilder.Append(value.Value.ToString().ToLower()); + break; + default: + throw new Exception($"Dtsod.Deconstruct() error: unknown type: {value.Type}"); + } + + outBuilder.Append(";\n"); + } + return outBuilder.ToString(); + } + + public void Add(KeyValuePair a) => Add(a.Key, a.Value); + + public DtsodV22 Extend(DtsodV22 newPart) + { + foreach (KeyValuePair pair in newPart) + Add(pair.Key, pair.Value); + return this; + } + +#if DEBUG + void Debug(params string[] msg) => PublicLog.Log(msg); + void DebugNoTime(params string[] msg) => PublicLog.LogNoTime(msg); +#endif } diff --git a/Dtsod/DtsodVersion.cs b/Dtsod/DtsodVersion.cs new file mode 100644 index 0000000..9ba4090 --- /dev/null +++ b/Dtsod/DtsodVersion.cs @@ -0,0 +1,8 @@ +namespace DTLib.Dtsod; + +public enum DtsodVersion : byte +{ + V21 = 21, + V22 = 22, + V30 = 30 +} diff --git a/Dtsod/DtsodVersionConverter.cs b/Dtsod/DtsodVersionConverter.cs new file mode 100644 index 0000000..a14264f --- /dev/null +++ b/Dtsod/DtsodVersionConverter.cs @@ -0,0 +1,13 @@ +namespace DTLib.Dtsod; + +public static class DtsodVersionConverter +{ + public static IDtsod Convert(IDtsod src, DtsodVersion targetVersion) + => targetVersion switch + { + DtsodVersion.V21 => new DtsodV21(src.ToDictionary()), + DtsodVersion.V22 => throw new NotImplementedException("Converting dtsods to V22 isn't implemented"), + DtsodVersion.V30 => new DtsodV30(src.ToDictionary()), + _ => throw new Exception($"DtsodVersionConverter.Convert() error: unknown target version <{targetVersion}>"), + }; +} diff --git a/Dtsod/IDtsod.cs b/Dtsod/IDtsod.cs new file mode 100644 index 0000000..a9e8b56 --- /dev/null +++ b/Dtsod/IDtsod.cs @@ -0,0 +1,8 @@ +namespace DTLib.Dtsod; + +public interface IDtsod +{ + public DtsodVersion Version { get; } + + public IDictionary ToDictionary(); +} diff --git a/Dtsod/V30/DtsodDict.cs b/Dtsod/V30/DtsodDict.cs new file mode 100644 index 0000000..edf727f --- /dev/null +++ b/Dtsod/V30/DtsodDict.cs @@ -0,0 +1,67 @@ +using System.Collections; + +namespace DTLib.Dtsod; + +public class DtsodDict : IDictionary +{ + // да, вместо собственной реализации интерфейса это ссылки на Dictionary + readonly Dictionary baseDict; + + public DtsodDict() => baseDict = new(); + public DtsodDict(IDictionary srcDict) => baseDict = new(srcDict); + + + public virtual TVal this[TKey key] + { + get => TryGetValue(key, out TVal value) ? value : throw new Exception($"Dtsod[{key}] key not found"); + set + { + if (!TrySetValue(key, value)) throw new KeyNotFoundException($"DtsodDict[{key}] key not found"); + } + } + + public virtual bool TryGetValue(TKey key, out TVal value) => baseDict.TryGetValue(key, out value); + + public virtual bool TrySetValue(TKey key, TVal value) + { + if (ContainsKey(key)) + { + baseDict[key] = value; + return true; + } + else return false; + } + public virtual void Append(ICollection> anotherDtsod) + { + foreach (var pair in anotherDtsod) + Add(pair.Key, pair.Value); + } + + public virtual void Add(TKey key, TVal value) => baseDict.Add(key, value); + + public virtual void Add(KeyValuePair pair) + => ((ICollection>)baseDict).Add(pair); + + + public int Count => baseDict.Count; + public ICollection Keys { get => baseDict.Keys; } + public ICollection Values { get => baseDict.Values; } + public bool IsReadOnly { get; } = false; + + public virtual void Clear() => baseDict.Clear(); + + public virtual bool ContainsKey(TKey key) => baseDict.ContainsKey(key); + + bool ICollection>.Contains(KeyValuePair pair) + => ((ICollection>)baseDict).Contains(pair); + + void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) + => ((ICollection>)baseDict).CopyTo(array, arrayIndex); + + public virtual bool Remove(TKey key) => baseDict.Remove(key); + bool ICollection>.Remove(KeyValuePair pair) + => ((ICollection>)baseDict).Remove(pair); + + IEnumerator IEnumerable.GetEnumerator() => baseDict.GetEnumerator(); + public IEnumerator> GetEnumerator() => baseDict.GetEnumerator(); +} diff --git a/Dtsod/V30/DtsodSerializableAttribute.cs b/Dtsod/V30/DtsodSerializableAttribute.cs index bf974ca..d52c0d7 100644 --- a/Dtsod/V30/DtsodSerializableAttribute.cs +++ b/Dtsod/V30/DtsodSerializableAttribute.cs @@ -1,10 +1,7 @@ -using System; +namespace DTLib.Dtsod; -namespace DTLib.Dtsod +public class DtsodSerializableAttribute : Attribute { - public class DtsodSerializableAttribute : Attribute - { - public DtsodVersion Version; - public DtsodSerializableAttribute(DtsodVersion ver) => Version = ver; - } + public DtsodVersion Version; + public DtsodSerializableAttribute(DtsodVersion ver) => Version = ver; } diff --git a/Dtsod/V30/DtsodV30.cs b/Dtsod/V30/DtsodV30.cs index 7195a34..ca3194e 100644 --- a/Dtsod/V30/DtsodV30.cs +++ b/Dtsod/V30/DtsodV30.cs @@ -1,17 +1,287 @@ -namespace DTLib.Dtsod +using System.Globalization; +using System.Linq; + +namespace DTLib.Dtsod; + +public class DtsodV30 : DtsodDict, IDtsod { - public static class DtsodV30 + public DtsodVersion Version { get; } = DtsodVersion.V30; + + public IDictionary ToDictionary() => this; + + public DtsodV30() : base() => UpdateLazy(); + public DtsodV30(IDictionary dict) : base(dict) => UpdateLazy(); + public DtsodV30(string serialized) : this() { Append(Deserialize(serialized)); } + +#if DEBUG + static void DebugLog(params string[] msg) => PublicLog.Log(msg); +#endif + + static IDictionary Deserialize(string text) { - /* - public static DtsodV30 FromObject(object target) + char c; + int i = -1; // ++i в ReadType + StringBuilder b = new(); + + + Type ReadType() { + while (i < text.Length) + { + c = text[++i]; + switch (c) + { + case ' ': + case '\t': + case '\r': + case '\n': + break; + case '#': + SkipComment(); + break; + case ':': + string _type = b.ToString(); + b.Clear(); + return TypeHelper.TypeFromString(_type); + case '=': + case '"': + case ';': + case '[': + case ']': + case '{': + case '}': + throw new Exception($"DtsodV30.Deserialize() error: unexpected {c}"); + default: + b.Append(c); + break; + } + } + + throw new Exception("DtsodV30.Deserialize.ReadType() error: end of text\ntext:\n" + text); } - public T ToObject() + string ReadName() { - + while (i < text.Length) + { + c = text[++i]; + switch (c) + { + case ' ': + case '\t': + case '\r': + case '\n': + break; + case '#': + SkipComment(); + break; + case '=': + string _name = b.ToString(); + b.Clear(); + return _name; + case ':': + case '"': + case ';': + case '[': + case ']': + case '{': + case '}': + throw new Exception($"DtsodV30.Deserialize() error: unexpected {c}"); + default: + b.Append(c); + break; + } + } + + throw new Exception("DtsodV30.Deserialize.ReadName() error: end of text\ntext:\n" + text); } - */ + + object[] ReadValue() + { + void ReadString() + { + c = text[++i]; //пропускает начальный символ '"' + while (c != '"' || (text[i - 1] == '\\' && text[i - 2] != '\\')) + { + b.Append(c); + if (++i >= text.Length) throw new Exception("DtsodV30.Deserialize() error: end of text\ntext:\n" + text); + c = text[i]; + } + } + + bool endoflist = false; // выставляется в цикле в ReadValue() + List ReadList() + { + List list = new(); + while (!endoflist) + list.Add(CreateInstance(ReadType(), ReadValue())); + endoflist = false; + return list; + } + + IDictionary ReadDictionary() + { + short bracketBalance = 1; + c = text[++i]; //пропускает начальный символ '{' + while (bracketBalance != 0) + { + switch (c) + { + case ' ': + case '\t': + case '\r': + case '\n': + break; + case '#': + SkipComment(); + break; + case '{': + bracketBalance++; + b.Append(c); + break; + case '}': + bracketBalance--; + if (bracketBalance != 0) + b.Append(c); + break; + case '"': + b.Append('"'); + ReadString(); + b.Append('"'); + break; + default: + b.Append(c); + break; + } + + if (++i >= text.Length) throw new Exception("DtsodV30.Deserialize() error: end of text\ntext:\n" + text); + c = text[i]; + } + + return Deserialize(b.ToString()); + } + + while (i < text.Length) + { + c = text[++i]; + switch (c) + { + case ' ': + case '\t': + case '\r': + case '\n': + break; + case '#': + SkipComment(); + break; + case '"': + ReadString(); + break; + case ';': // один параметр + case ',': // для листов + var str = b.ToString(); + b.Clear(); + // hardcoded "null" value + return str == "null" ? (new object[] { null }) : (new object[] { str }); + case '[': + { + object[] _value = ReadList().ToArray(); + b.Clear(); + return _value; + } + case ']': + endoflist = true; + goto case ','; + case '{': + { + object[] _value = new object[] { ReadDictionary() }; + b.Clear(); + return _value; + } + case '=': + case ':': + case '}': + throw new Exception($"DtsodV30.Deserialize() error: unexpected {c}"); + default: + b.Append(c); + break; + } + } + + throw new Exception("DtsodV30.Deserialize.ReadValue() error: end of text\ntext:\n" + text); + } + + void SkipComment() + { + while (text[i] != '\n') + if (++i >= text.Length) throw new Exception("DtsodV30.Deserialize() error: end of text\ntext:\n" + text); + } + + object CreateInstance(Type type, object[] ctor_args) + { + if (TypeHelper.BaseTypeConstructors.TryGetValue(type, out var ctor)) + return (object)ctor.Invoke((string)ctor_args[0]); + else if (type.CustomAttributes.Any(a => a.AttributeType == typeof(DtsodSerializableAttribute))) + return Activator.CreateInstance(type, ctor_args); + else throw new Exception($"type {type.AssemblyQualifiedName} doesn't have DtsodSerializableAttribute"); + } + + Dictionary output = new(); + Type type; + string name; + object[] value; + + for (; i < text.Length; i++) + { + type = ReadType(); + name = ReadName(); + value = ReadValue(); + output.Add(name, CreateInstance(type, value)); + } + + return output; } + + public override void Append(ICollection> anotherDtsod) + { + base.Append(anotherDtsod); + //UpdateLazy(); + } + + public override void Add(string key, dynamic value) + { + base.Add(key, (object)value); + //UpdateLazy(); + } + + protected static string Serialize(IDictionary dtsod, ushort tabsCount = 0) + { + StringBuilder b = new(); + foreach (var pair in dtsod) + { + Type type = pair.Value.GetType(); + b.Append(TypeHelper.TypeToString(type)).Append(':') + .Append(pair.Key).Append('='); + if (TypeHelper.BaseTypeNames.ContainsKey(type)) + { + if (type == typeof(decimal) || type == typeof(double) || type == typeof(float)) + b.Append(pair.Value.ToString(CultureInfo.InvariantCulture)); + else b.Append(pair.Value.ToString()); + } + else if (typeof(IDictionary).IsAssignableFrom(type)) + b.Append("\n{\n").Append(Serialize(pair.Value, tabsCount++)).Append("};\n"); + else + { + type.GetProperties().Where(p => p.CustomAttributes.Any(a => a.AttributeType == typeof(DtsodSerializableAttribute))); + + } + } + + return b.ToString(); + } + + protected Lazy serialized; + protected void UpdateLazy() => serialized = new(() => Serialize(this)); + public override string ToString() => serialized.Value; } diff --git a/Dtsod/V30/DtsodVersion.cs b/Dtsod/V30/DtsodVersion.cs deleted file mode 100644 index a64ee93..0000000 --- a/Dtsod/V30/DtsodVersion.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace DTLib.Dtsod -{ - public enum DtsodVersion : byte - { - V21 = 21, - V22 = 22, - V30 = 30 - } -} diff --git a/Dtsod/V30/TypeHelper.cs b/Dtsod/V30/TypeHelper.cs new file mode 100644 index 0000000..0e671a5 --- /dev/null +++ b/Dtsod/V30/TypeHelper.cs @@ -0,0 +1,66 @@ +namespace DTLib.Dtsod; + +public static class TypeHelper +{ + static public readonly Dictionary> BaseTypeConstructors = new() + { + { typeof(bool), (inp) => inp.ToBool() }, + { typeof(char), (inp) => inp.ToChar() }, + { typeof(string), (inp) => inp.ToString() }, + { typeof(byte), (inp) => inp.ToByte() }, + { typeof(sbyte), (inp) => inp.ToSByte() }, + { typeof(short), (inp) => inp.ToShort() }, + { typeof(ushort), (inp) => inp.ToUShort() }, + { typeof(int), (inp) => inp.ToInt() }, + { typeof(uint), (inp) => inp.ToUInt() }, + { typeof(long), (inp) => inp.ToLong() }, + { typeof(ulong), (inp) => inp.ToULong() }, + { typeof(float), (inp) => inp.ToFloat() }, + { typeof(double), (inp) => inp.ToDouble() }, + { typeof(decimal), (inp) => inp.ToDecimal() } + }; + + static public Dictionary BaseTypeNames = new() + { + { typeof(bool), "bool" }, + { typeof(char), "char" }, + { typeof(string), "string" }, + { typeof(byte), "byte" }, + { typeof(sbyte), "sbyte" }, + { typeof(short), "short" }, + { typeof(ushort), "ushort" }, + { typeof(int), "int" }, + { typeof(uint), "uint" }, + { typeof(long), "long" }, + { typeof(ulong), "ulong" }, + { typeof(float), "float" }, + { typeof(double), "double" }, + { typeof(decimal), "decimal" } + }; + + static public string TypeToString(Type t) => + BaseTypeNames.TryGetValue(t, out var name) + ? name + : t.AssemblyQualifiedName; + + static public Type TypeFromString(string str) => str switch + { + "bool" => typeof(bool), + "char" => typeof(char), + "string" => typeof(string), + "byte" => typeof(byte), + "sbyte" => typeof(sbyte), + "short" => typeof(short), + "ushort" => typeof(ushort), + "int" => typeof(int), + "uint" => typeof(uint), + "long" => typeof(long), + "ulong" => typeof(ulong), + "float" => typeof(float), + "double" => typeof(double), + "decimal" => typeof(decimal), + _ => Type.GetType(str, false) ?? + throw new Exception($"DtsodV30.Deserialize.ParseType() error: type {str} doesn't exists") + }; + +}