namespace DTLib.Dtsod; // v21 // парсер теперь не может игнорировать комменты, потом починю // теперь числовые значения конвертируются в правильный тип, а не в int64/uint64 (новый вариант switch из c#9.0 делал какую-то дичь) // исправлены некоторые другие баги public class DtsodV21 : Dictionary, IDtsod { public DtsodVersion Version { get; } = DtsodVersion.V21; public IDictionary ToDictionary() => this; readonly string Text; //public Dictionary Values { get; set; } public DtsodV21(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); } // выдаёт 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(); 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()); 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 ReadList() { i++; List 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 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 "); }; } // 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"); } } }