DTLib/Dtsod.cs

420 lines
17 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using static DTLib.PublicLog;
namespace DTLib
{
//
// это как json но не совсем
//
public class Dtsod : Dictionary<string, dynamic>
{
static readonly bool debug = false;
public string Text { get; }
//public Dictionary<string, dynamic> Values { get; set; }
public Dtsod(string text)
{
Text = text;
foreach (KeyValuePair<string, dynamic> pair in ParseNew(text))
Add(pair.Key, pair.Value);
}
// выдаёт Exception
new public 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
new public 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,
/*Double,
Long,
Ulong,
Short,
Ushort,
Int,
Uint,
Null,
Boolean,*/
Default
}
Dictionary<string, dynamic> ParseNew(string text)
{
Dictionary<string, dynamic> parsed = new();
int i = 0;
for (; i < text.Length; i++) ReadName();
return parsed;
void ReadName()
{
void ReadCommentLine()
{
for (; i < text.Length && text[i] != '\n'; i++) ;
}
bool isListElem = false;
dynamic value = null;
StringBuilder defaultNameBuilder = new();
if (debug) LogNoTime("m", "ReadName");
for (; i < text.Length; i++)
{
if (debug) LogNoTime("w", text[i].ToString());
switch (text[i])
{
case ' ':
case '\t':
case '\r':
case '\n':
break;
case ':':
i++;
value = ReadValue();
string name = defaultNameBuilder.ToString();
if (debug) LogNoTime("c", $"parsed.Add({name}, {value})\n");
if (isListElem)
{
if (!parsed.ContainsKey(name)) parsed.Add(name, new List<dynamic>());
parsed[name].Add(value);
}
else parsed.Add(name, value);
if (debug) LogNoTime("g", "ReadName return\n");
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;
string ReadString()
{
i++;
StringBuilder valueBuilder = new();
valueBuilder.Append('"');
for (; text[i] != '"' || text[i - 1] == '\\'; i++)
{
if (debug) LogNoTime("gray", text[i].ToString());
valueBuilder.Append(text[i]);
}
valueBuilder.Append('"');
if (debug) LogNoTime("gray", text[i].ToString());
type = ValueType.String;
return valueBuilder.ToString();
}
List<dynamic> ReadList()
{
i++;
List<dynamic> output = new();
StringBuilder valueBuilder = new();
for (; text[i] != ']'; i++)
{
if (debug) LogNoTime("c", text[i].ToString());
switch (text[i])
{
case ' ':
case '\t':
case '\r':
case '\n':
break;
case ',':
output.Add(ParseValueToRightType(valueBuilder.ToString()));
valueBuilder.Clear();
break;
default:
valueBuilder.Append(text[i]);
break;
}
}
if (valueBuilder.Length > 0)
output.Add(ParseValueToRightType(valueBuilder.ToString()));
if (debug) LogNoTime("c", text[i].ToString());
type = ValueType.List;
return output;
}
Dictionary<string, dynamic> ReadComplex()
{
i++;
StringBuilder valueBuilder = new();
for (; text[i] != '}'; i++)
{
if (debug) LogNoTime("y", text[i].ToString());
if (text[i] == '"')
{
valueBuilder.Append(ReadString());
}
else valueBuilder.Append(text[i]);
}
if (debug) LogNoTime("y", text[i].ToString());
type = ValueType.Complex;
if (debug) LogNoTime("g", valueBuilder.ToString());
return ParseNew(valueBuilder.ToString());
}
dynamic ParseValueToRightType(string stringValue)
{
if (debug) LogNoTime("g", $"\nParseValueToRightType({stringValue})");
return stringValue switch
{
_ when stringValue.Contains('"') => stringValue.Remove(stringValue.Length - 1).Remove(0, 1),
// bool
"true" or "false" => stringValue.ToBool(),
// null
"null" => null,
// double
_ when stringValue.Contains('.') => stringValue.ToDouble(),
// ushort, ulong, uint
_ when (stringValue.Length > 2 && stringValue[stringValue.Length - 2] == 'u') => stringValue[stringValue.Length - 1] switch
{
's' => stringValue.Remove(stringValue.Length - 2).ToUShort(),
'i' => stringValue.Remove(stringValue.Length - 2).ToUInt(),
'l' => stringValue.Remove(stringValue.Length - 2).ToULong(),
_ => throw new Exception($"Dtsod.Parse.ReadValue() error: wrong type <u{stringValue[stringValue.Length - 1]}>")
},
// short, long, int
_ => stringValue[stringValue.Length - 1] switch
{
's' => stringValue.Remove(stringValue.Length - 1).ToShort(),
'l' => stringValue.Remove(stringValue.Length - 1).ToLong(),
_ => stringValue.ToInt()
}
};
}
dynamic value = null;
StringBuilder defaultValueBuilder = new();
if (debug) LogNoTime("m", "\nReadValue\n");
for (; i < text.Length; i++)
{
if (debug) LogNoTime("b", text[i].ToString());
switch (text[i])
{
case ' ':
case '\t':
case '\r':
case '\n':
break;
case '"':
value = ReadString();
break;
case ';':
if (debug) LogNoTime("g", $"\nReadValue returns type {type} value <{value}>\n");
return type switch
{
ValueType.List or ValueType.Complex => value,
ValueType.String => ParseValueToRightType(value),
ValueType.Default => ParseValueToRightType(defaultValueBuilder.ToString()),
_ => throw new Exception($"Dtlib.Parse.ReadValue() error: can't convert value to type <{type}>")
};
case '[':
value = ReadList();
break;
case '{':
value = ReadComplex();
break;
default:
defaultValueBuilder.Append(text[i]);
break;
}
}
throw new Exception("Dtsod.Parse.ReadValue error: end of text");
}
}
Dictionary<string, dynamic> ParseOld(string text)
{
Dictionary<string, dynamic> output = new();
StringBuilder nameStrB = new();
StringBuilder valStrB = new();
dynamic value = null;
bool readValue = false;
bool readString = false;
bool readListElem = false;
bool isList = false;
dynamic StringToElse(string str)
{
if (readString) return str;
// bool
switch (str)
{
// предустановленные значения
case "true": return true;
case "false": return false;
case "null": return null;
default:
// double
if (str.Contains(".")) return SimpleConverter.ToDouble(str);
// ushort, uint, ulong
else if (str.Length > 2 && str[str.Length - 2] == 'u')
return str[str.Length - 1] switch
{
's' => SimpleConverter.ToUShort(str.Remove(str.Length - 2)),
'i' => SimpleConverter.ToUInt(str.Remove(str.Length - 2)),
'l' => SimpleConverter.ToULong(str.Remove(str.Length - 2)),
_ => throw new Exception($"ParseConfig() error: unknown data type <u{str[str.Length - 1]}>"),
};
// short, int, long
else return str[str.Length - 1] switch
{
's' => SimpleConverter.ToShort(str.Remove(str.Length - 1)),
'l' => SimpleConverter.ToLong(str.Remove(str.Length - 1)),
_ => SimpleConverter.ToInt(str),
};
}
}
for (int i = 0; i < text.Length; i++)
{
if (debug) LogNoTime(text[i].ToString());
void ReadString()
{
i++;
while (text[i] != '"' || text[i - 1] == '\\')
{
if (debug) LogNoTime(text[i].ToString());
valStrB.Append(text[i]);
i++;
}
}
switch (text[i])
{
case '{':
i++;
for (; text[i] != '}'; i++)
{
if (text[i] == '"') ReadString();
else valStrB.Append(text[i]);
}
value = ParseOld(valStrB.ToString());
valStrB.Clear();
break;
case '}':
throw new Exception("ParseConfig() error: unexpected '}' at " + i + "char");
case '"':
readString = true;
ReadString();
break;
case ':':
readValue = true;
break;
case ' ':
case '\t':
case '\r':
case '\n':
break;
case '[':
isList = true;
value = new List<dynamic>();
break;
case ',':
case ']':
if (isList) value.Add(StringToElse(valStrB.ToString()));
else throw new Exception($"unexpected <{text[i]}> at text[{i}]");
valStrB.Clear();
break;
case ';':
// конвертация value в нужный тип данных
if (!isList && valStrB.Length > 0) value = StringToElse(valStrB.ToString());
if (readListElem)
{
if (!output.ContainsKey(nameStrB.ToString())) output.Add(nameStrB.ToString(), new List<dynamic>());
output[nameStrB.ToString()].Add(value);
}
else output.Add(nameStrB.ToString(), value);
nameStrB.Clear();
valStrB.Clear();
value = null;
readValue = false;
readString = false;
readListElem = false;
isList = false;
break;
// коммент
case '#':
for (; i < text.Length && text[i] != '\n'; i++) ;
break;
// если $ перед названием параметра поставить, значение (value) добавится в лист с таким названием (nameStrB.ToString())
case '$':
if (nameStrB.ToString().Length != 0) throw new Exception("unexpected usage of '$' at char " + i.ToString());
readListElem = true;
break;
default:
if (readValue) valStrB.Append(text[i]);
else nameStrB.Append(text[i]);
break;
};
}
return output;
}
}
}