diff --git a/Dtsod/DtsodV22.cs b/Dtsod/DtsodV22.cs index befd24e..e84a9c2 100644 --- a/Dtsod/DtsodV22.cs +++ b/Dtsod/DtsodV22.cs @@ -3,6 +3,7 @@ using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; +using static System.Net.Mime.MediaTypeNames; namespace DTLib.Dtsod { @@ -20,7 +21,7 @@ namespace DTLib.Dtsod // v22 // метод ToString() теперь действительно деконструирует объект в текст, а не возвращает параметр text из конструктора - public class DtsodV22 : Dictionary + class DtsodV22 : Dictionary { static readonly bool debug = false; @@ -106,7 +107,7 @@ namespace DTLib.Dtsod Dictionary parsed = new(); int i = 0; for (; i < text.Length; i++) ReadName(); - Debug("g", $"Parse returns {parsed.Keys.Count} keys\n"); + DebugNoTime("g", $"Parse returns {parsed.Keys.Count} keys\n"); return new DtsodV22(parsed); // СЛОМАНО @@ -122,7 +123,7 @@ namespace DTLib.Dtsod dynamic value = null; StringBuilder defaultNameBuilder = new(); - Debug("m", "ReadName\n"); + DebugNoTime("m", "ReadName\n"); for (; i < text.Length; i++) { switch (text[i]) @@ -136,7 +137,7 @@ namespace DTLib.Dtsod i++; string name = defaultNameBuilder.ToString(); value = ReadValue(out var type, out var isList); - Debug("c", $"parsed.Add({name},{type} {value} )\n"); + DebugNoTime("c", $"parsed.Add({name},{type} {value} )\n"); if (isListElem) { if (!parsed.ContainsKey(name)) parsed.Add(name, new(type, new List(), isList)); @@ -152,14 +153,14 @@ namespace DTLib.Dtsod throw new Exception("Parse.ReadName() error: unexpected '}' at " + i + " char"); // если $ перед названием параметра поставить, значение value добавится в лист с названием name case '$': - Debug("w", text[i].ToString()); + 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: - Debug("w", text[i].ToString()); + DebugNoTime("w", text[i].ToString()); defaultNameBuilder.Append(text[i]); break; } @@ -179,11 +180,11 @@ namespace DTLib.Dtsod valueBuilder.Append('"'); for (; text[i] != '"' || text[i - 1] == '\\'; i++) { - Debug("gray", text[i].ToString()); + DebugNoTime("gray", text[i].ToString()); valueBuilder.Append(text[i]); } valueBuilder.Append('"'); - Debug("gray", text[i].ToString()); + DebugNoTime("gray", text[i].ToString()); type = ValueTypes.String; return valueBuilder.ToString(); } @@ -195,7 +196,7 @@ namespace DTLib.Dtsod StringBuilder valueBuilder = new(); for (; text[i] != ']'; i++) { - Debug("c", text[i].ToString()); + DebugNoTime("c", text[i].ToString()); switch (text[i]) { case ' ': @@ -218,7 +219,7 @@ namespace DTLib.Dtsod ParseValueToRightType(valueBuilder.ToString()); output.Add(value); } - Debug("c", text[i].ToString()); + DebugNoTime("c", text[i].ToString()); type = ValueTypes.List; return output; } @@ -230,7 +231,7 @@ namespace DTLib.Dtsod i++; for (; balance != 0; i++) { - Debug("y", text[i].ToString()); + DebugNoTime("y", text[i].ToString()); switch (text[i]) { case '"': @@ -238,12 +239,12 @@ namespace DTLib.Dtsod break; case '}': balance--; - Debug("b", $"\nbalance -- = {balance}\n"); + DebugNoTime("b", $"\nbalance -- = {balance}\n"); if (balance != 0) valueBuilder.Append(text[i]); break; case '{': balance++; - Debug("b", $"\nbalance ++ = {balance}\n"); + DebugNoTime("b", $"\nbalance ++ = {balance}\n"); valueBuilder.Append(text[i]); break; default: @@ -258,7 +259,7 @@ namespace DTLib.Dtsod void ParseValueToRightType(string stringValue) { - Debug("b", $"\nParseValueToRightType({stringValue})\n"); + DebugNoTime("b", $"\nParseValueToRightType({stringValue})\n"); switch (stringValue) { @@ -327,10 +328,10 @@ namespace DTLib.Dtsod } StringBuilder defaultValueBuilder = new(); - Debug("m", "\nReadValue\n"); + DebugNoTime("m", "\nReadValue\n"); for (; i < text.Length; i++) { - Debug("b", text[i].ToString()); + DebugNoTime("b", text[i].ToString()); switch (text[i]) { case ' ': @@ -448,9 +449,15 @@ namespace DTLib.Dtsod return outBuilder.ToString(); } - void Debug(params string[] msg) + void DebugNoTime(params string[] msg) { if (debug) PublicLog.LogNoTime(msg); } + + public void Expand(DtsodV22 newPart) + { + foreach (KeyValuePair pair in newPart) + Add(pair.Key, pair.Value); + } } } diff --git a/Dtsod/DtsodV23.cs b/Dtsod/DtsodV23.cs new file mode 100644 index 0000000..584c939 --- /dev/null +++ b/Dtsod/DtsodV23.cs @@ -0,0 +1,468 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using static System.Net.Mime.MediaTypeNames; + +namespace DTLib.Dtsod +{ + // + // это как json но не совсем + // + // v20 + // полностью переписан парсер + // + // v21 + // парсер теперь не может игнорировать комменты, потом починю + // теперь числовые значения конвертируются в правильный тип, а не в int64/uint64 (новый вариант switch из c#9.0 делал какую-то херню) + // исправлены некоторые другие баги + // + // v22 + // метод ToString() теперь действительно деконструирует объект в текст, а не возвращает параметр text из конструктора + // + // v23 + // добавлен конструктор без параметров и метод Expand + + public class DtsodV23 : Dictionary + { + static readonly bool debug = false; + + public struct ValueStruct + { + public dynamic Value; + public ValueTypes Type; + public bool IsList; + public ValueStruct(ValueTypes type, dynamic value, bool isList) + { + Value = value; + Type = type; + IsList = isList; + } + public ValueStruct(ValueTypes type, dynamic value) + { + Value = value; + Type = type; + IsList = false; + } + } + + public DtsodV23() { } + + public DtsodV23(string text) + { + foreach (KeyValuePair pair in Parse(text)) + Add(pair.Key, pair.Value); + } + + + public DtsodV23(Dictionary dict) + { + foreach (KeyValuePair pair in dict) + Add(pair.Key, pair.Value); + } + + // выдаёт Exception + public new dynamic this[string key] + { + get + { + if (TryGetValue(key, out dynamic value)) return value; + else throw new Exception($"Dtsod[{key}] key not found"); + } + set + { + if (TrySetValue(key, value)) return; + else throw new Exception($"Dtsod[{key}] key not found"); + } + } + + // не выдаёт 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; + if (value is IList) isList = true; + else isList = false; + base[key] = new(base[key].Type, value, isList); + return true; + } + catch (KeyNotFoundException) + { + return false; + } + } + + DtsodV23 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 new DtsodV23(parsed); + + // СЛОМАНО + /*void ReadCommentLine() + { + for (; i < text.Length && text[i] != '\n'; i++) Debug("gray", text[i].ToString()); + }*/ + + void ReadName() + { + + bool isListElem = false; + dynamic value = null; + 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 var type, out var 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(); + 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("gray", text[i].ToString()); + valueBuilder.Append(text[i]); + } + valueBuilder.Append('"'); + DebugNoTime("gray", 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: + 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(); + break; + default: + defaultValueBuilder.Append(text[i]); + break; + } + } + throw new Exception("Dtsod.Parse.ReadValue error: wtf it's the end of function"); + } + } + + public override string ToString() => Deconstruct(this); + + ushort tabCount = 0; + string Deconstruct(DtsodV23 dtsod) + { + StringBuilder outBuilder = new(); + foreach (var 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("\"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("uk"); + 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(); + } + + void DebugNoTime(params string[] msg) + { + if (debug) PublicLog.LogNoTime(msg); + } + + public void Expand(DtsodV23 newPart) + { + foreach (KeyValuePair pair in newPart) + Add(pair.Key, pair.Value); + } + } +}