This commit is contained in:
Timerix22 2022-01-13 18:44:29 +03:00
parent 8f4fa3de6e
commit be5dbfc938
7 changed files with 432 additions and 56 deletions

View File

@ -310,7 +310,7 @@ public class DtsodV21 : Dictionary<string, dynamic>, IDtsod
value = stringValue.Remove(stringValue.Length - 1).ToLong(); value = stringValue.Remove(stringValue.Length - 1).ToLong();
break; break;
default: default:
value = stringValue.ToShort(); value = stringValue.ToInt();
break; break;
} }
break; break;

View File

@ -345,7 +345,7 @@ public class DtsodV22 : Dictionary<string, DtsodV22.ValueStruct>, IDtsod
break; break;
default: default:
type = ValueTypes.Int; type = ValueTypes.Int;
value = stringValue.ToShort(); value = stringValue.ToInt();
break; break;
} }
break; break;

308
Dtsod/DtsodV23.cs Normal file
View File

@ -0,0 +1,308 @@
using System.Globalization;
namespace DTLib.Dtsod;
// v23
// в процессе создания v30 появились идеи по улучшению 20-ой серии
// новый парсер (опять)
// улучшена сериализация и десериализация листов
public class DtsodV23 : DtsodDict<string, dynamic>, IDtsod
{
public DtsodVersion Version { get; } = DtsodVersion.V30;
public IDictionary<string, dynamic> ToDictionary() => this;
public DtsodV23() : base() => UpdateLazy();
public DtsodV23(IDictionary<string, dynamic> dict) : base(dict) => UpdateLazy();
public DtsodV23(string serialized) : this() => Append(Deserialize(serialized));
static DtsodV23 Deserialize(string text)
{
char c;
int i = -1; // ++i в ReadName
StringBuilder b = new();
Dictionary<string, dynamic> output = new();
bool partOfDollarList = false;
for (; i < text.Length; i++)
{
string name = ReadName();
dynamic value = ReadValue(out bool _);
if (partOfDollarList)
{
if (!output.TryGetValue(name, out var dollarList))
{
dollarList = new List<dynamic>();
output.Add(name, dollarList);
}
dollarList.Add(value);
}
else output.Add(name, value);
}
return new DtsodV23(output);
string ReadName()
{
while (i < text.Length - 1)
{
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($"DtsodV23.Deserialize() error: unexpected {c}");
default:
b.Append(c);
break;
}
}
throw new Exception("DtsodV23.Deserialize.ReadName() error: end of text\ntext:\n" + text);
}
dynamic ReadValue(out bool endOfList)
{
endOfList = false;
void ReadString()
{
while ((c != '"' && c != '\'') || (text[i - 1] == '\\' && text[i - 2] != '\\'))
{
b.Append(c);
if (++i >= text.Length) throw new Exception("DtsodV23.Deserialize() error: end of text\ntext:\n" + text);
c = text[i];
}
b.Append('"');
}
DtsodV23 ReadDtsod()
{
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 '"':
case '\'':
ReadString();
break;
default:
b.Append(c);
break;
}
if (++i >= text.Length) throw new Exception("DtsodV23.Deserialize() error: end of text\ntext:\n" + text);
c = text[i];
}
b.Clear();
return Deserialize(b.ToString());
}
List<dynamic> ReadList()
{
List<dynamic> list = new();
while (true)
{
list.Add(ReadValue(out bool _eol));
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);
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:
return value_str.Contains('.')
? value_str.ToDouble()
: value_str.ToInt();
}
};
}
while (i < text.Length - 1)
{
c = text[++i];
switch (c)
{
case ' ':
case '\t':
case '\r':
case '\n':
break;
case '#':
SkipComment();
break;
case '"':
case '\'':
ReadString();
break;
case ';':
case ',':
string str = b.ToString();
b.Clear();
return ParseValue(str);
case '[':
return ReadList();
case ']':
endOfList = true;
break;
case '{':
return ReadDtsod();
case '=':
case ':':
case '}':
case '$':
throw new Exception($"DtsodV23.Deserialize() error: unexpected {c}");
default:
b.Append(c);
break;
}
}
throw new Exception("DtsodV23.Deserialize.ReadValue() error: end of text\ntext:\n" + text);
}
void SkipComment()
{
while (text[i] != '\n')
if (++i >= text.Length) throw new Exception("DtsodV23.Deserialize() error: end of text\ntext:\n" + text);
}
}
internal static readonly Dictionary<Type, Action<dynamic, StringBuilder>> TypeSerializeFuncs = new()
{
{ typeof(bool), (val, b) => b.Append(val.ToString()) },
{ typeof(char), (val, b) => b.Append('\'').Append(val).Append('\'') },
{ typeof(string), (val, b) => b.Append('"').Append(val).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") }
};
short tabscount = -1;
protected StringBuilder Serialize(IDictionary<string, dynamic> dtsod, 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);
b.Append(";\n");
void SerializeType(dynamic value)
{
if (value is IList _list)
{
b.Append('[');
foreach (object el in _list)
{
SerializeType(el);
b.Append(',');
}
b.Remove(b.Length - 1, 1).Append(']');
}
else if (value is IDictionary _dict)
{
b.Append('{');
Serialize(value, b);
b.Append('}');
}
else b.Append(TypeSerializeFuncs[value.GetType()].Invoke(value, b));
}
}
tabscount--;
return b;
}
protected Lazy<string> serialized;
protected void UpdateLazy() => serialized = new(() => Serialize(this).ToString());
public override string ToString() => serialized.Value;
}

View File

@ -4,5 +4,6 @@ public enum DtsodVersion : byte
{ {
V21 = 21, V21 = 21,
V22 = 22, V22 = 22,
V23 = 23,
V30 = 30 V30 = 30
} }

View File

@ -7,6 +7,7 @@ public static class DtsodVersionConverter
{ {
DtsodVersion.V21 => new DtsodV21(src.ToDictionary()), DtsodVersion.V21 => new DtsodV21(src.ToDictionary()),
DtsodVersion.V22 => throw new NotImplementedException("Converting dtsods to V22 isn't implemented"), DtsodVersion.V22 => throw new NotImplementedException("Converting dtsods to V22 isn't implemented"),
DtsodVersion.V23 => new DtsodV23(src.ToDictionary()),
DtsodVersion.V30 => new DtsodV30(src.ToDictionary()), DtsodVersion.V30 => new DtsodV30(src.ToDictionary()),
_ => throw new Exception($"DtsodVersionConverter.Convert() error: unknown target version <{targetVersion}>"), _ => throw new Exception($"DtsodVersionConverter.Convert() error: unknown target version <{targetVersion}>"),
}; };

View File

@ -1,5 +1,4 @@
using System.Globalization; using System.Globalization;
using System.Linq;
namespace DTLib.Dtsod; namespace DTLib.Dtsod;
@ -13,10 +12,6 @@ public class DtsodV30 : DtsodDict<string, dynamic>, IDtsod
public DtsodV30(IDictionary<string, dynamic> dict) : base(dict) => UpdateLazy(); public DtsodV30(IDictionary<string, dynamic> dict) : base(dict) => UpdateLazy();
public DtsodV30(string serialized) : this() => Append(Deserialize(serialized)); public DtsodV30(string serialized) : this() => Append(Deserialize(serialized));
#if DEBUG
static void DebugLog(params string[] msg) => PublicLog.Log(msg);
#endif
static IDictionary<string, dynamic> Deserialize(string text) static IDictionary<string, dynamic> Deserialize(string text)
{ {
char c; char c;
@ -27,7 +22,7 @@ public class DtsodV30 : DtsodDict<string, dynamic>, IDtsod
Type ReadType() Type ReadType()
{ {
while (i < text.Length) while (i < text.Length - 1)
{ {
c = text[++i]; c = text[++i];
switch (c) switch (c)
@ -43,9 +38,10 @@ public class DtsodV30 : DtsodDict<string, dynamic>, IDtsod
case ':': case ':':
string _type = b.ToString(); string _type = b.ToString();
b.Clear(); b.Clear();
return TypeHelper.TypeFromString(_type); return TypeHelper.Instance.TypeFromString(_type);
case '=': case '=':
case '"': case '"':
case '\'':
case ';': case ';':
case '[': case '[':
case ']': case ']':
@ -63,7 +59,7 @@ public class DtsodV30 : DtsodDict<string, dynamic>, IDtsod
string ReadName() string ReadName()
{ {
while (i < text.Length) while (i < text.Length - 1)
{ {
c = text[++i]; c = text[++i];
switch (c) switch (c)
@ -82,6 +78,7 @@ public class DtsodV30 : DtsodDict<string, dynamic>, IDtsod
return _name; return _name;
case ':': case ':':
case '"': case '"':
case '\'':
case ';': case ';':
case '[': case '[':
case ']': case ']':
@ -97,12 +94,12 @@ public class DtsodV30 : DtsodDict<string, dynamic>, IDtsod
throw new Exception("DtsodV30.Deserialize.ReadName() error: end of text\ntext:\n" + text); throw new Exception("DtsodV30.Deserialize.ReadName() error: end of text\ntext:\n" + text);
} }
object[] ReadValue() object ReadValue(ref bool endoflist)
{ {
void ReadString() void ReadString()
{ {
c = text[++i]; //пропускает начальный символ '"' c = text[++i]; //пропускает начальный символ '"'
while (c != '"' || (text[i - 1] == '\\' && text[i - 2] != '\\')) while ((c != '"' && c != '\'') || (text[i - 1] == '\\' && text[i - 2] != '\\'))
{ {
b.Append(c); b.Append(c);
if (++i >= text.Length) throw new Exception("DtsodV30.Deserialize() error: end of text\ntext:\n" + text); if (++i >= text.Length) throw new Exception("DtsodV30.Deserialize() error: end of text\ntext:\n" + text);
@ -110,13 +107,13 @@ public class DtsodV30 : DtsodDict<string, dynamic>, IDtsod
} }
} }
bool endoflist = false; // выставляется в цикле в ReadValue()
List<object> ReadList() List<object> ReadList()
{ {
List<dynamic> list = new(); List<object> list = new();
while (!endoflist) bool _endoflist = false;
list.Add(CreateInstance(ReadType(), ReadValue())); while (!_endoflist)
endoflist = false; list.Add(CreateInstance(ReadType(), ReadValue(ref _endoflist)));
b.Clear();
return list; return list;
} }
@ -146,6 +143,7 @@ public class DtsodV30 : DtsodDict<string, dynamic>, IDtsod
b.Append(c); b.Append(c);
break; break;
case '"': case '"':
case '\'':
b.Append('"'); b.Append('"');
ReadString(); ReadString();
b.Append('"'); b.Append('"');
@ -159,10 +157,11 @@ public class DtsodV30 : DtsodDict<string, dynamic>, IDtsod
c = text[i]; c = text[i];
} }
b.Clear();
return Deserialize(b.ToString()); return Deserialize(b.ToString());
} }
while (i < text.Length) while (i < text.Length-1)
{ {
c = text[++i]; c = text[++i];
switch (c) switch (c)
@ -176,29 +175,21 @@ public class DtsodV30 : DtsodDict<string, dynamic>, IDtsod
SkipComment(); SkipComment();
break; break;
case '"': case '"':
case '\'':
ReadString(); ReadString();
break; break;
case ';': // один параметр case ';': // один параметр
case ',': // для листов case ',': // для листов
string str = b.ToString(); string str = b.ToString();
b.Clear(); b.Clear();
// hardcoded "null" value return str == "null" ? null : str;
return str == "null" ? (new object[] { null }) : (new object[] { str });
case '[': case '[':
{ return ReadList();
object[] _value = ReadList().ToArray();
b.Clear();
return _value;
}
case ']': case ']':
endoflist = true; endoflist = true;
goto case ','; goto case ',';
case '{': case '{':
{ return ReadDictionary();
object[] _value = new object[] { ReadDictionary() };
b.Clear();
return _value;
}
case '=': case '=':
case ':': case ':':
case '}': case '}':
@ -218,25 +209,42 @@ public class DtsodV30 : DtsodDict<string, dynamic>, IDtsod
if (++i >= text.Length) throw new Exception("DtsodV30.Deserialize() error: end of text\ntext:\n" + text); if (++i >= text.Length) throw new Exception("DtsodV30.Deserialize() error: end of text\ntext:\n" + text);
} }
object CreateInstance(Type type, object[] ctor_args) object CreateInstance(Type type, object ctor_arg)
{ {
if (TypeHelper.BaseTypeConstructors.TryGetValue(type, out Func<string, dynamic> ctor)) if (TypeHelper.Instance.BaseTypeConstructors.TryGetValue(type, out Func<string, dynamic> ctor))
return (object)ctor.Invoke((string)ctor_args[0]); return (object)ctor.Invoke((string)ctor_arg);
else if (type.CustomAttributes.Any(a => a.AttributeType == typeof(DtsodSerializableAttribute))) else if (type.CustomAttributes.Any(a => a.AttributeType == typeof(DtsodSerializableAttribute)))
return Activator.CreateInstance(type, ctor_args); return Activator.CreateInstance(type, ((IDictionary<string, object>)ctor_arg).Values.ToArray());
else throw new Exception($"type {type.AssemblyQualifiedName} doesn't have DtsodSerializableAttribute"); else if (typeof(ICollection).IsAssignableFrom(type))
{
var method_As = typeof(TypeHelper).GetMethod("As",
System.Reflection.BindingFlags.Static |
System.Reflection.BindingFlags.NonPublic)
.MakeGenericMethod(type.GetGenericArguments()[0]);
object collection = type.GetConstructor(Type.EmptyTypes).Invoke(null);
var method_Add = type.GetMethod("Add");
Log(method_Add.Name);
foreach (object el in (IEnumerable)ctor_arg)
{
var pel = method_As.Invoke(null, new object[] { el });
method_Add.Invoke(collection, new object[] { pel });
}
return collection;
}
else throw new Exception($"can't create instance of {type.FullName}");
} }
Dictionary<string, dynamic> output = new(); Dictionary<string, dynamic> output = new();
Type type; Type type;
string name; string name;
object[] value; object value;
for (; i < text.Length; i++) for (; i < text.Length; i++)
{ {
type = ReadType(); type = ReadType();
name = ReadName(); name = ReadName();
value = ReadValue(); bool _ = false;
value = ReadValue(ref _);
output.Add(name, CreateInstance(type, value)); output.Add(name, CreateInstance(type, value));
} }
@ -252,22 +260,32 @@ public class DtsodV30 : DtsodDict<string, dynamic>, IDtsod
StringBuilder b = new(); StringBuilder b = new();
foreach (KeyValuePair<string, dynamic> pair in dtsod) foreach (KeyValuePair<string, dynamic> 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<string, dynamic>).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)));
} }
void SerializeObject(string name, dynamic inst)
{
Type type = inst.GetType();
b.Append(TypeHelper.Instance.TypeToString(type)).Append(':')
.Append(name).Append('=');
if (TypeHelper.Instance.BaseTypeNames.ContainsKey(type))
{
if (type == typeof(decimal) || type == typeof(double) || type == typeof(float))
b.Append(inst.ToString(CultureInfo.InvariantCulture));
else b.Append(inst.ToString());
}
else if (typeof(IDictionary<string, dynamic>).IsAssignableFrom(type))
b.Append("\n{\n").Append(Serialize(inst, tabsCount++)).Append("};\n");
else if (type.CustomAttributes.Any(a => a.AttributeType == typeof(DtsodSerializableAttribute)))
{
var props = type.GetProperties().Where(p => p.CustomAttributes.Any(a => a.AttributeType == typeof(DtsodSerializableAttribute)));
foreach (var prop in props)
{
var propval = prop.GetValue(inst);
}
}
else throw new Exception($"can't serialize type {type.FullName}");
} }
return b.ToString(); return b.ToString();

View File

@ -1,8 +1,11 @@
namespace DTLib.Dtsod; namespace DTLib.Dtsod;
public static class TypeHelper public class TypeHelper
{ {
public static readonly Dictionary<Type, Func<string, dynamic>> BaseTypeConstructors = new() static Lazy<TypeHelper> _inst = new();
public static TypeHelper Instance => _inst.Value;
internal readonly Dictionary<Type, Func<string, dynamic>> BaseTypeConstructors = new()
{ {
{ typeof(bool), (inp) => inp.ToBool() }, { typeof(bool), (inp) => inp.ToBool() },
{ typeof(char), (inp) => inp.ToChar() }, { typeof(char), (inp) => inp.ToChar() },
@ -20,7 +23,7 @@ public static class TypeHelper
{ typeof(decimal), (inp) => inp.ToDecimal() } { typeof(decimal), (inp) => inp.ToDecimal() }
}; };
public static Dictionary<Type, string> BaseTypeNames = new() internal Dictionary<Type, string> BaseTypeNames = new()
{ {
{ typeof(bool), "bool" }, { typeof(bool), "bool" },
{ typeof(char), "char" }, { typeof(char), "char" },
@ -38,12 +41,55 @@ public static class TypeHelper
{ typeof(decimal), "decimal" } { typeof(decimal), "decimal" }
}; };
public static string TypeToString(Type t) => private DtsodDict<string, Type> ST_extensions = new()
{
{ "List<bool>", typeof(List<bool>) },
{ "List<char>", typeof(List<char>) },
{ "List<string>", typeof(List<string>) },
{ "List<byte>", typeof(List<byte>) },
{ "List<sbyte>", typeof(List<sbyte>) },
{ "List<short>", typeof(List<short>) },
{ "List<ushort>", typeof(List<ushort>) },
{ "List<int>", typeof(List<int>) },
{ "List<uint>", typeof(List<uint>) },
{ "List<long>", typeof(List<long>) },
{ "List<ulong>", typeof(List<ulong>) },
{ "List<float>", typeof(List<float>) },
{ "List<double>", typeof(List<double>) },
{ "List<decimal>", typeof(List<decimal>) },
};
private DtsodDict<Type, string> TS_extensions = new()
{
{ typeof(List<bool>), "List<bool>" },
{ typeof(List<char>), "List<char>" },
{ typeof(List<string>), "List<string>" },
{ typeof(List<byte>), "List<byte>" },
{ typeof(List<sbyte>), "List<sbyte>" },
{ typeof(List<short>), "List<short>" },
{ typeof(List<ushort>), "List<ushort>" },
{ typeof(List<int>), "List<int>" },
{ typeof(List<uint>), "List<uint>" },
{ typeof(List<long>), "List<long>" },
{ typeof(List<ulong>), "List<ulong>" },
{ typeof(List<float>), "List<float>" },
{ typeof(List<double>), "List<double>" },
{ typeof(List<decimal>), "List<decimal>" },
};
public TypeHelper Extend(string name, Type type)
{
ST_extensions.Add(name, type);
TS_extensions.Add(type, name);
return this;
}
public string TypeToString(Type t) =>
BaseTypeNames.TryGetValue(t, out string name) BaseTypeNames.TryGetValue(t, out string name)
? name ? name
: t.AssemblyQualifiedName; : TS_extensions.TryGetValue(t, out name)
? name
public static Type TypeFromString(string str) => str switch : t.FullName;
public Type TypeFromString(string str) => str switch
{ {
"bool" => typeof(bool), "bool" => typeof(bool),
"char" => typeof(char), "char" => typeof(char),
@ -59,8 +105,10 @@ public static class TypeHelper
"float" => typeof(float), "float" => typeof(float),
"double" => typeof(double), "double" => typeof(double),
"decimal" => typeof(decimal), "decimal" => typeof(decimal),
_ => Type.GetType(str, false) ?? _ => ST_extensions.TryGetValue(str, out var t)
throw new Exception($"DtsodV30.Deserialize.ParseType() error: type {str} doesn't exists") ? t
: Type.GetType(str, false)
?? throw new Exception($"DtsodV30.Deserialize.ParseType() error: type {str} doesn't exists")
}; };
static internal T As<T>(object inst) where T : class => inst as T;
} }