using System.Globalization; namespace DTLib.Dtsod; // v23 // в процессе создания v30 появились идеи по улучшению 20-ой серии // новый парсер (опять) // улучшена сериализация и десериализация листов public class DtsodV23 : DtsodDict, IDtsod { public DtsodVersion Version { get; } = DtsodVersion.V23; public IDictionary ToDictionary() => this; public DtsodV23() : base() {} public DtsodV23(IDictionary dict) : base(dict) {} public DtsodV23(string serialized) => Append(Deserialize(serialized)); static DtsodV23 Deserialize(string _text) { char[] text = _text.ToArray(); char c; int i = -1; // ++i в ReadName StringBuilder b = new(); Dictionary output = new(); bool partOfDollarList = false; while (i < text.Length) { string name = ReadName(); if (name == "") goto end; dynamic value = ReadValue(out bool _); if (partOfDollarList) { if (!output.TryGetValue(name, out var dollarList)) { dollarList = new List(); output.Add(name, dollarList); } dollarList.Add(value); } else output.Add(name, value); } end:return new DtsodV23(output); 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 '$': partOfDollarList = true; break; case '=': case '"': case '\'': case ';': case '[': case ']': case '{': case '}': throw new Exception($"unexpected {c}"); default: b.Append(c); break; } } return b.Length == 0 ? "" : throw new Exception("DtsodV23.Deserialize.ReadName() error: end of text\ntext:\n" + _text); } dynamic ReadValue(out bool endOfList) { endOfList = false; void ReadString() { bool prevIsBackslash = false; b.Append('"'); c = text[++i]; while (c != '"' || prevIsBackslash) { prevIsBackslash = c == '\\' && !prevIsBackslash; b.Append(c); if (++i >= text.Length) throw new Exception("end of text\ntext:\n" + _text); c = text[i]; } b.Append('"'); } DtsodV23 ReadDtsod() { short bracketBalance = 1; while (bracketBalance != 0) { if (++i >= text.Length) //пропускает начальный символ '{' throw new Exception("end of text\ntext:\n" + _text); c = text[i]; 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 '"': ReadString(); break; default: b.Append(c); break; } } var __text = b.ToString(); b.Clear(); return Deserialize(__text); } List ReadList() { List list = new(); while (true) { var item = ReadValue(out bool _eol); if(item!=null) list.Add(item); if (_eol) break; } b.Clear(); return list; } dynamic ParseValue(string value_str) { switch (value_str) { case "true": case "false": return value_str.ToBool(); case "null": return null; default: if (value_str.Contains('"')) return value_str.Substring(1, value_str.Length - 2).Replace("\\\\","\\").Replace("\\\"","\""); else if (value_str.Contains('\'')) return value_str[1]; else switch (value_str[value_str.Length - 1]) { case 's': return value_str[value_str.Length - 2] == 'u' ? value_str.Remove(value_str.Length - 2).ToUShort() : value_str.Remove(value_str.Length - 1).ToShort(); case 'u': return value_str.Remove(value_str.Length - 1).ToUInt(); case 'i': return value_str[value_str.Length - 2] == 'u' ? value_str.Remove(value_str.Length - 2).ToUInt() : value_str.Remove(value_str.Length - 1).ToInt(); case 'l': return value_str[value_str.Length - 2] == 'u' ? value_str.Remove(value_str.Length - 2).ToULong() : value_str.Remove(value_str.Length - 1).ToLong(); case 'b': return value_str[value_str.Length - 2] == 's' ? value_str.Remove(value_str.Length - 2).ToSByte() : value_str.Remove(value_str.Length - 1).ToByte(); case 'f': return value_str.Remove(value_str.Length - 1).ToFloat(); case 'e': return value_str[value_str.Length - 2] == 'd' ? value_str.Remove(value_str.Length - 2).ToDecimal() : throw new Exception("can't parse value:" + value_str); default: if (value_str.Contains('.')) return (object)(value_str.ToDouble()); else { try { return (object)(value_str.ToInt()); } catch (FormatException) { Log("r", $"can't parse value: {value_str}"); return null; } } } }; } object value = null; while (++i < text.Length) { c = text[i]; switch (c) { case ' ': case '\t': case '\r': case '\n': break; case '#': SkipComment(); break; case '"': ReadString(); break; case '\'': b.Append(c).Append(text[++i]); c = text[++i]; if (c != '\'') throw new Exception("after <\'> should be char"); b.Append(c); break; case ';': case ',': if(b.Length == 0) return value; string str = b.ToString(); b.Clear(); return ParseValue(str); case '[': value=ReadList(); goto case ';'; break; case ']': endOfList = true; break; case '{': value=ReadDtsod(); break; case '=': case ':': case '}': case '$': throw new Exception($"unexpected {c}"); default: b.Append(c); break; } } throw new Exception("DtsodV23.Deserialize.ReadValue() error: end of text\ntext:\n" + _text); } void SkipComment() { while (i < text.Length && text[i] != '\n') i++; } } internal static readonly Dictionary> TypeSerializeFuncs = new() { { typeof(bool), (val, b) => b.Append((bool)val ? "true" : "false") }, { typeof(char), (val, b) => b.Append('\'').Append(val).Append('\'') }, { typeof(string), (val, b) => b.Append('"').Append(val.Replace("\\","\\\\").Replace("\"", "\\\"")).Append('"') }, { typeof(byte), (val, b) => b.Append(val.ToString()).Append('b') }, { typeof(sbyte), (val, b) => b.Append(val.ToString()).Append("sb") }, { typeof(short), (val, b) => b.Append(val.ToString()).Append('s') }, { typeof(ushort), (val, b) => b.Append(val.ToString()).Append("us") }, { typeof(int), (val, b) => b.Append(val.ToString()) }, { typeof(uint), (val, b) => b.Append(val.ToString()).Append("ui") }, { typeof(long), (val, b) => b.Append(val.ToString()).Append('l') }, { typeof(ulong), (val, b) => b.Append(val.ToString()).Append("ul") }, { typeof(float), (val, b) => b.Append(val.ToString(CultureInfo.InvariantCulture)).Append('f') }, { typeof(double), (val, b) => b.Append(val.ToString(CultureInfo.InvariantCulture)) }, { typeof(decimal), (val, b) => b.Append(val.ToString(CultureInfo.InvariantCulture)).Append("de") } }; protected StringBuilder Serialize(DtsodV23 dtsod, ref int tabscount, StringBuilder b = null) {; tabscount++; if (b is null) b = new StringBuilder(); foreach (var pair in dtsod) { b.Append('\t', tabscount).Append(pair.Key).Append(": "); SerializeType(pair.Value, ref tabscount); b.Append(";\n"); void SerializeType(dynamic value, ref int tabscount) { if (value is null) b.Append("null"); else if (value is DtsodV23 _dtsod) { b.Append("{\n"); Serialize(_dtsod, ref tabscount, b); b.Append('}'); } else if (value is IList _list) { b.Append('['); if(_list.Count>0){ foreach (object el in _list) { SerializeType(el, ref tabscount); b.Append(','); } b.Remove(b.Length - 1, 1); } b.Append(']'); } else TypeSerializeFuncs[value.GetType()].Invoke(value, b); } } tabscount--; return b; } public override string ToString() { int tabscount = -1; return Serialize(this, ref tabscount).ToString(); } ///serializes dtsod as part of list public string ToString(string partialListName) { int tabscount = 0; var builder = new StringBuilder().Append('$').Append(partialListName).Append(":\n{\n"); return Serialize(this, ref tabscount, builder).Append("};\n").ToString(); } }