using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.RegularExpressions;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: InternalsVisibleTo("Tommy.Extensions")]
[assembly: InternalsVisibleTo("Tommy.Tests")]
[assembly: AssemblyCompany("Denis Zhidkikh")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright © 2021 Denis Zhidkikh")]
[assembly: AssemblyDescription("\r\n A simple TOML parser and writer inspired by SimpleJSON.\r\n Provides minimal and simple API for parsing and writing TOML files.\r\n Compliant with TOML 1.0.0 format spec.\r\n ")]
[assembly: AssemblyFileVersion("3.1.2.0")]
[assembly: AssemblyInformationalVersion("3.1.2")]
[assembly: AssemblyProduct("Tommy")]
[assembly: AssemblyTitle("Tommy")]
[assembly: AssemblyVersion("3.1.2.0")]
namespace Tommy;
public abstract class TomlNode : IEnumerable
{
public virtual bool HasValue { get; }
public virtual bool IsArray { get; }
public virtual bool IsTable { get; }
public virtual bool IsString { get; }
public virtual bool IsInteger { get; }
public virtual bool IsFloat { get; }
public bool IsDateTime
{
get
{
if (!IsDateTimeLocal)
{
return IsDateTimeOffset;
}
return true;
}
}
public virtual bool IsDateTimeLocal { get; }
public virtual bool IsDateTimeOffset { get; }
public virtual bool IsBoolean { get; }
public virtual string Comment { get; set; }
public virtual int CollapseLevel { get; set; }
public virtual TomlTable AsTable => this as TomlTable;
public virtual TomlString AsString => this as TomlString;
public virtual TomlInteger AsInteger => this as TomlInteger;
public virtual TomlFloat AsFloat => this as TomlFloat;
public virtual TomlBoolean AsBoolean => this as TomlBoolean;
public virtual TomlDateTimeLocal AsDateTimeLocal => this as TomlDateTimeLocal;
public virtual TomlDateTimeOffset AsDateTimeOffset => this as TomlDateTimeOffset;
public virtual TomlDateTime AsDateTime => this as TomlDateTime;
public virtual TomlArray AsArray => this as TomlArray;
public virtual int ChildrenCount => 0;
public virtual TomlNode this[string key]
{
get
{
return null;
}
set
{
}
}
public virtual TomlNode this[int index]
{
get
{
return null;
}
set
{
}
}
public virtual IEnumerable<TomlNode> Children
{
get
{
yield break;
}
}
public virtual IEnumerable<string> Keys
{
get
{
yield break;
}
}
public IEnumerator GetEnumerator()
{
return Children.GetEnumerator();
}
public virtual bool TryGetNode(string key, out TomlNode node)
{
node = null;
return false;
}
public virtual bool HasKey(string key)
{
return false;
}
public virtual bool HasItemAt(int index)
{
return false;
}
public virtual void Add(string key, TomlNode node)
{
}
public virtual void Add(TomlNode node)
{
}
public virtual void Delete(TomlNode node)
{
}
public virtual void Delete(string key)
{
}
public virtual void Delete(int index)
{
}
public virtual void AddRange(IEnumerable<TomlNode> nodes)
{
foreach (TomlNode node in nodes)
{
Add(node);
}
}
public virtual void WriteTo(TextWriter tw, string name = null)
{
tw.WriteLine(ToInlineToml());
}
public virtual string ToInlineToml()
{
return ToString();
}
public static implicit operator TomlNode(string value)
{
return new TomlString
{
Value = value
};
}
public static implicit operator TomlNode(bool value)
{
return new TomlBoolean
{
Value = value
};
}
public static implicit operator TomlNode(long value)
{
return new TomlInteger
{
Value = value
};
}
public static implicit operator TomlNode(float value)
{
return new TomlFloat
{
Value = value
};
}
public static implicit operator TomlNode(double value)
{
return new TomlFloat
{
Value = value
};
}
public static implicit operator TomlNode(DateTime value)
{
return new TomlDateTimeLocal
{
Value = value
};
}
public static implicit operator TomlNode(DateTimeOffset value)
{
return new TomlDateTimeOffset
{
Value = value
};
}
public static implicit operator TomlNode(TomlNode[] nodes)
{
TomlArray tomlArray = new TomlArray();
tomlArray.AddRange(nodes);
return tomlArray;
}
public static implicit operator string(TomlNode value)
{
return value.ToString();
}
public static implicit operator int(TomlNode value)
{
return (int)value.AsInteger.Value;
}
public static implicit operator long(TomlNode value)
{
return value.AsInteger.Value;
}
public static implicit operator float(TomlNode value)
{
return (float)value.AsFloat.Value;
}
public static implicit operator double(TomlNode value)
{
return value.AsFloat.Value;
}
public static implicit operator bool(TomlNode value)
{
return value.AsBoolean.Value;
}
public static implicit operator DateTime(TomlNode value)
{
return value.AsDateTimeLocal.Value;
}
public static implicit operator DateTimeOffset(TomlNode value)
{
return value.AsDateTimeOffset.Value;
}
}
public class TomlString : TomlNode
{
public override bool HasValue { get; } = true;
public override bool IsString { get; } = true;
public bool IsMultiline { get; set; }
public bool MultilineTrimFirstLine { get; set; }
public bool PreferLiteral { get; set; }
public string Value { get; set; }
public override string ToString()
{
return Value;
}
public override string ToInlineToml()
{
if (Value.IndexOf(new string('\'', (!IsMultiline) ? 1 : 3), StringComparison.Ordinal) != -1 && PreferLiteral)
{
PreferLiteral = false;
}
string text = new string(PreferLiteral ? '\'' : '"', (!IsMultiline) ? 1 : 3);
string text2 = (PreferLiteral ? Value : Value.Escape(!IsMultiline));
if (IsMultiline)
{
text2 = text2.Replace("\r\n", "\n").Replace("\n", Environment.NewLine);
}
if (IsMultiline && (MultilineTrimFirstLine || (!MultilineTrimFirstLine && text2.StartsWith(Environment.NewLine))))
{
text2 = Environment.NewLine + text2;
}
return text + text2 + text;
}
}
public class TomlInteger : TomlNode
{
public enum Base
{
Binary = 2,
Octal = 8,
Decimal = 10,
Hexadecimal = 16
}
public override bool IsInteger { get; } = true;
public override bool HasValue { get; } = true;
public Base IntegerBase { get; set; } = Base.Decimal;
public long Value { get; set; }
public override string ToString()
{
return Value.ToString();
}
public override string ToInlineToml()
{
if (IntegerBase == Base.Decimal)
{
return Value.ToString(CultureInfo.InvariantCulture);
}
return "0" + TomlSyntax.BaseIdentifiers[(int)IntegerBase] + Convert.ToString(Value, (int)IntegerBase);
}
}
public class TomlFloat : TomlNode, IFormattable
{
public override bool IsFloat { get; } = true;
public override bool HasValue { get; } = true;
public double Value { get; set; }
public override string ToString()
{
return Value.ToString(CultureInfo.InvariantCulture);
}
public string ToString(string format, IFormatProvider formatProvider)
{
return Value.ToString(format, formatProvider);
}
public string ToString(IFormatProvider formatProvider)
{
return Value.ToString(formatProvider);
}
public override string ToInlineToml()
{
double value = Value;
if (double.IsNaN(value))
{
return "nan";
}
if (double.IsPositiveInfinity(value))
{
return "inf";
}
if (double.IsNegativeInfinity(value))
{
return "-inf";
}
double num = value;
return num.ToString("G", CultureInfo.InvariantCulture).ToLowerInvariant();
}
}
public class TomlBoolean : TomlNode
{
public override bool IsBoolean { get; } = true;
public override bool HasValue { get; } = true;
public bool Value { get; set; }
public override string ToString()
{
return Value.ToString();
}
public override string ToInlineToml()
{
if (!Value)
{
return "false";
}
return "true";
}
}
public class TomlDateTime : TomlNode, IFormattable
{
public int SecondsPrecision { get; set; }
public override bool HasValue { get; } = true;
public virtual string ToString(string format, IFormatProvider formatProvider)
{
return string.Empty;
}
public virtual string ToString(IFormatProvider formatProvider)
{
return string.Empty;
}
protected virtual string ToInlineTomlInternal()
{
return string.Empty;
}
public override string ToInlineToml()
{
return ToInlineTomlInternal().Replace(" ", "T").Replace("+00:00", "Z");
}
}
public class TomlDateTimeOffset : TomlDateTime
{
public override bool IsDateTimeOffset { get; } = true;
public DateTimeOffset Value { get; set; }
public override string ToString()
{
return Value.ToString(CultureInfo.CurrentCulture);
}
public override string ToString(IFormatProvider formatProvider)
{
return Value.ToString(formatProvider);
}
public override string ToString(string format, IFormatProvider formatProvider)
{
return Value.ToString(format, formatProvider);
}
protected override string ToInlineTomlInternal()
{
return Value.ToString(TomlSyntax.RFC3339Formats[base.SecondsPrecision]);
}
}
public class TomlDateTimeLocal : TomlDateTime
{
public enum DateTimeStyle
{
Date,
Time,
DateTime
}
public override bool IsDateTimeLocal { get; } = true;
public DateTimeStyle Style { get; set; } = DateTimeStyle.DateTime;
public DateTime Value { get; set; }
public override string ToString()
{
return Value.ToString(CultureInfo.CurrentCulture);
}
public override string ToString(IFormatProvider formatProvider)
{
return Value.ToString(formatProvider);
}
public override string ToString(string format, IFormatProvider formatProvider)
{
return Value.ToString(format, formatProvider);
}
public override string ToInlineToml()
{
return Style switch
{
DateTimeStyle.Date => Value.ToString(TomlSyntax.LocalDateFormat),
DateTimeStyle.Time => Value.ToString(TomlSyntax.RFC3339LocalTimeFormats[base.SecondsPrecision]),
_ => Value.ToString(TomlSyntax.RFC3339LocalDateTimeFormats[base.SecondsPrecision]),
};
}
}
public class TomlArray : TomlNode
{
private List<TomlNode> values;
public override bool HasValue { get; } = true;
public override bool IsArray { get; } = true;
public bool IsMultiline { get; set; }
public bool IsTableArray { get; set; }
public List<TomlNode> RawArray => values ?? (values = new List<TomlNode>());
public override TomlNode this[int index]
{
get
{
if (index < RawArray.Count)
{
return RawArray[index];
}
return this[index] = new TomlLazy(this);
}
set
{
if (index == RawArray.Count)
{
RawArray.Add(value);
}
else
{
RawArray[index] = value;
}
}
}
public override int ChildrenCount => RawArray.Count;
public override IEnumerable<TomlNode> Children => RawArray.AsEnumerable();
public override void Add(TomlNode node)
{
RawArray.Add(node);
}
public override void AddRange(IEnumerable<TomlNode> nodes)
{
RawArray.AddRange(nodes);
}
public override void Delete(TomlNode node)
{
RawArray.Remove(node);
}
public override void Delete(int index)
{
RawArray.RemoveAt(index);
}
public override string ToString()
{
return ToString(multiline: false);
}
public string ToString(bool multiline)
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append('[');
if (ChildrenCount != 0)
{
string value = (multiline ? (Environment.NewLine + " ") : " ");
string self = (multiline ? $"{','}{Environment.NewLine} " : $"{','} ");
string value2 = (multiline ? Environment.NewLine : " ");
stringBuilder.Append(value).Append(self.Join(RawArray.Select((TomlNode n) => n.ToInlineToml()))).Append(value2);
}
stringBuilder.Append(']');
return stringBuilder.ToString();
}
public override void WriteTo(TextWriter tw, string name = null)
{
if (!IsTableArray)
{
tw.WriteLine(ToString(IsMultiline));
return;
}
if (Comment != null)
{
tw.WriteLine();
Comment.AsComment(tw);
}
tw.Write('[');
tw.Write('[');
tw.Write(name);
tw.Write(']');
tw.Write(']');
tw.WriteLine();
bool flag = true;
foreach (TomlNode item in RawArray)
{
TomlTable obj = (item as TomlTable) ?? throw new TomlFormatException("The array is marked as array table but contains non-table nodes!");
obj.IsInline = false;
if (!flag)
{
tw.WriteLine();
Comment?.AsComment(tw);
tw.Write('[');
tw.Write('[');
tw.Write(name);
tw.Write(']');
tw.Write(']');
tw.WriteLine();
}
flag = false;
obj.WriteTo(tw, name, writeSectionName: false);
}
}
}
public class TomlTable : TomlNode
{
private Dictionary<string, TomlNode> children;
public override bool HasValue { get; }
public override bool IsTable { get; } = true;
public bool IsInline { get; set; }
public Dictionary<string, TomlNode> RawTable => children ?? (children = new Dictionary<string, TomlNode>());
public override TomlNode this[string key]
{
get
{
if (RawTable.TryGetValue(key, out var value))
{
return value;
}
TomlLazy tomlLazy = new TomlLazy(this);
RawTable[key] = tomlLazy;
return tomlLazy;
}
set
{
RawTable[key] = value;
}
}
public override int ChildrenCount => RawTable.Count;
public override IEnumerable<TomlNode> Children => RawTable.Select((KeyValuePair<string, TomlNode> kv) => kv.Value);
public override IEnumerable<string> Keys => RawTable.Select((KeyValuePair<string, TomlNode> kv) => kv.Key);
public override bool HasKey(string key)
{
return RawTable.ContainsKey(key);
}
public override void Add(string key, TomlNode node)
{
RawTable.Add(key, node);
}
public override bool TryGetNode(string key, out TomlNode node)
{
return RawTable.TryGetValue(key, out node);
}
public override void Delete(TomlNode node)
{
RawTable.Remove(RawTable.First((KeyValuePair<string, TomlNode> kv) => kv.Value == node).Key);
}
public override void Delete(string key)
{
RawTable.Remove(key);
}
public override string ToString()
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append('{');
if (ChildrenCount != 0)
{
LinkedList<KeyValuePair<string, TomlNode>> linkedList = CollectCollapsedItems("", 0, normalizeOrder: false);
if (linkedList.Count != 0)
{
stringBuilder.Append(' ').Append($"{','} ".Join(linkedList.Select((KeyValuePair<string, TomlNode> n) => $"{n.Key} {'='} {n.Value.ToInlineToml()}")));
}
stringBuilder.Append(' ');
}
stringBuilder.Append('}');
return stringBuilder.ToString();
}
private LinkedList<KeyValuePair<string, TomlNode>> CollectCollapsedItems(string prefix = "", int level = 0, bool normalizeOrder = true)
{
LinkedList<KeyValuePair<string, TomlNode>> linkedList = new LinkedList<KeyValuePair<string, TomlNode>>();
LinkedList<KeyValuePair<string, TomlNode>> linkedList2 = (normalizeOrder ? new LinkedList<KeyValuePair<string, TomlNode>>() : linkedList);
foreach (KeyValuePair<string, TomlNode> item in RawTable)
{
TomlNode value = item.Value;
string text = item.Key.AsKey();
if (value is TomlTable tomlTable)
{
LinkedList<KeyValuePair<string, TomlNode>> linkedList3 = tomlTable.CollectCollapsedItems(prefix + text + ".", level + 1, normalizeOrder);
if (linkedList3.Count == 0 && value.CollapseLevel == level)
{
linkedList2.AddLast(new KeyValuePair<string, TomlNode>(prefix + text, value));
}
foreach (KeyValuePair<string, TomlNode> item2 in linkedList3)
{
linkedList2.AddLast(item2);
}
}
else if (value.CollapseLevel == level)
{
linkedList.AddLast(new KeyValuePair<string, TomlNode>(prefix + text, value));
}
}
if (normalizeOrder)
{
foreach (KeyValuePair<string, TomlNode> item3 in linkedList2)
{
linkedList.AddLast(item3);
}
}
return linkedList;
}
public override void WriteTo(TextWriter tw, string name = null)
{
WriteTo(tw, name, writeSectionName: true);
}
internal void WriteTo(TextWriter tw, string name, bool writeSectionName)
{
if (IsInline && name != null)
{
tw.WriteLine(ToInlineToml());
return;
}
LinkedList<KeyValuePair<string, TomlNode>> linkedList = CollectCollapsedItems();
if (linkedList.Count == 0)
{
return;
}
bool flag = !linkedList.All(delegate(KeyValuePair<string, TomlNode> n)
{
TomlNode value2 = n.Value;
if (value2 is TomlTable tomlTable2)
{
if (!tomlTable2.IsInline)
{
goto IL_002e;
}
}
else if (value2 is TomlArray tomlArray2 && tomlArray2.IsTableArray)
{
goto IL_002e;
}
return false;
IL_002e:
return true;
});
Comment?.AsComment(tw);
if (name != null && (flag || Comment != null) && writeSectionName)
{
tw.Write('[');
tw.Write(name);
tw.Write(']');
tw.WriteLine();
}
else if (Comment != null)
{
tw.WriteLine();
}
string text = ((name == null) ? "" : (name + "."));
bool flag2 = true;
foreach (KeyValuePair<string, TomlNode> item in linkedList)
{
string key = item.Key;
TomlNode value = item.Value;
if (value is TomlArray tomlArray)
{
if (tomlArray.IsTableArray)
{
goto IL_011e;
}
}
else if (value is TomlTable tomlTable && !tomlTable.IsInline)
{
goto IL_011e;
}
bool flag3 = false;
goto IL_0126;
IL_011e:
flag3 = true;
goto IL_0126;
IL_0126:
if (flag3)
{
if (!flag2)
{
tw.WriteLine();
}
flag2 = false;
item.Value.WriteTo(tw, text + key);
}
else
{
flag2 = false;
item.Value.Comment?.AsComment(tw);
tw.Write(key);
tw.Write(' ');
tw.Write('=');
tw.Write(' ');
item.Value.WriteTo(tw, text + key);
}
}
}
}
internal class TomlLazy : TomlNode
{
private readonly TomlNode parent;
private TomlNode replacement;
public override TomlNode this[int index]
{
get
{
return Set<TomlArray>()[index];
}
set
{
Set<TomlArray>()[index] = value;
}
}
public override TomlNode this[string key]
{
get
{
return Set<TomlTable>()[key];
}
set
{
Set<TomlTable>()[key] = value;
}
}
public TomlLazy(TomlNode parent)
{
this.parent = parent;
}
public override void Add(TomlNode node)
{
Set<TomlArray>().Add(node);
}
public override void Add(string key, TomlNode node)
{
Set<TomlTable>().Add(key, node);
}
public override void AddRange(IEnumerable<TomlNode> nodes)
{
Set<TomlArray>().AddRange(nodes);
}
private TomlNode Set<T>() where T : TomlNode, new()
{
if (replacement != null)
{
return replacement;
}
T val = new T
{
Comment = Comment
};
if (parent.IsTable)
{
TomlNode node;
string text = parent.Keys.FirstOrDefault((string s) => parent.TryGetNode(s, out node) && node.Equals(this));
if (text == null)
{
return null;
}
parent[text] = val;
}
else
{
if (!parent.IsArray)
{
return null;
}
int num = parent.Children.TakeWhile((TomlNode child) => child != this).Count();
if (num == parent.ChildrenCount)
{
return null;
}
parent[num] = val;
}
replacement = val;
return val;
}
}
public class TOMLParser : IDisposable
{
public enum ParseState
{
None,
KeyValuePair,
SkipToNextLine,
Table
}
private readonly TextReader reader;
private ParseState currentState;
private int line;
private int col;
private List<TomlSyntaxException> syntaxErrors;
public bool ForceASCII { get; set; }
public TOMLParser(TextReader reader)
{
this.reader = reader;
line = (col = 0);
}
public void Dispose()
{
reader?.Dispose();
}
public TomlTable Parse()
{
syntaxErrors = new List<TomlSyntaxException>();
line = (col = 1);
TomlTable tomlTable = new TomlTable();
TomlTable tomlTable2 = tomlTable;
currentState = ParseState.None;
List<string> parts = new List<string>();
bool flag = false;
StringBuilder stringBuilder = null;
bool flag2 = true;
int num;
while ((num = reader.Peek()) >= 0)
{
char c = (char)num;
if (currentState != 0)
{
goto IL_011d;
}
if (!TomlSyntax.IsWhiteSpace(c))
{
if (TomlSyntax.IsNewLine(c))
{
if (stringBuilder != null && flag2)
{
tomlTable.Comment = stringBuilder.ToString().TrimEnd(new char[0]);
stringBuilder = null;
flag2 = false;
}
if (TomlSyntax.IsLineBreak(c))
{
AdvanceLine();
}
}
else
{
if (c == '#')
{
if (stringBuilder == null)
{
stringBuilder = new StringBuilder();
}
stringBuilder.AppendLine(ParseComment());
AdvanceLine(1);
continue;
}
flag2 = false;
if (c != '[')
{
if (TomlSyntax.IsBareKey(c) || TomlSyntax.IsQuoted(c))
{
currentState = ParseState.KeyValuePair;
goto IL_011d;
}
AddError($"Unexpected character \"{c}\"");
continue;
}
currentState = ParseState.Table;
}
}
goto IL_0368;
IL_011d:
if (currentState == ParseState.KeyValuePair)
{
TomlNode tomlNode = ReadKeyValuePair(parts);
if (tomlNode == null)
{
stringBuilder = null;
parts.Clear();
if (currentState != 0)
{
AddError("Failed to parse key-value pair!");
}
continue;
}
tomlNode.Comment = stringBuilder?.ToString()?.TrimEnd(new char[0]);
bool num2 = InsertNode(tomlNode, tomlTable2, parts);
stringBuilder = null;
parts.Clear();
if (num2)
{
currentState = ParseState.SkipToNextLine;
}
continue;
}
if (currentState == ParseState.Table)
{
if (parts.Count == 0)
{
if (c == '[')
{
ConsumeChar();
flag = true;
}
if (!ReadKeyName(ref parts, ']'))
{
parts.Clear();
}
else if (parts.Count == 0)
{
AddError("Table name is emtpy.");
flag = false;
stringBuilder = null;
parts.Clear();
}
continue;
}
if (c == ']')
{
if (flag)
{
ConsumeChar();
int num3 = reader.Peek();
if (num3 < 0 || (ushort)num3 != 93)
{
AddError("Array table " + ".".Join(parts) + " has only one closing bracket.");
parts.Clear();
flag = false;
stringBuilder = null;
continue;
}
}
tomlTable2 = CreateTable(tomlTable, parts, flag);
if (tomlTable2 != null)
{
tomlTable2.IsInline = false;
tomlTable2.Comment = stringBuilder?.ToString()?.TrimEnd(new char[0]);
}
parts.Clear();
flag = false;
stringBuilder = null;
if (tomlTable2 == null)
{
if (currentState != 0)
{
AddError("Error creating table array!");
}
tomlTable2 = tomlTable;
continue;
}
currentState = ParseState.SkipToNextLine;
goto IL_0368;
}
if (parts.Count != 0)
{
AddError($"Unexpected character \"{c}\"");
parts.Clear();
flag = false;
stringBuilder = null;
}
}
if (currentState == ParseState.SkipToNextLine && !TomlSyntax.IsWhiteSpace(c))
{
switch (c)
{
case '\n':
case '#':
currentState = ParseState.None;
AdvanceLine();
if (c == '#')
{
col++;
ParseComment();
continue;
}
break;
default:
AddError($"Unexpected character \"{c}\" at the end of the line.");
break;
case '\r':
break;
}
}
goto IL_0368;
IL_0368:
reader.Read();
col++;
}
if (currentState != 0 && currentState != ParseState.SkipToNextLine)
{
AddError("Unexpected end of file!");
}
if (syntaxErrors.Count > 0)
{
throw new TomlParseException(tomlTable, syntaxErrors);
}
return tomlTable;
}
private bool AddError(string message, bool skipLine = true)
{
syntaxErrors.Add(new TomlSyntaxException(message, currentState, line, col));
if (skipLine)
{
reader.ReadLine();
AdvanceLine(1);
}
currentState = ParseState.None;
return false;
}
private void AdvanceLine(int startCol = 0)
{
line++;
col = startCol;
}
private int ConsumeChar()
{
col++;
return reader.Read();
}
private TomlNode ReadKeyValuePair(List<string> keyParts)
{
int num;
while ((num = reader.Peek()) >= 0)
{
char c = (char)num;
if (TomlSyntax.IsQuoted(c) || TomlSyntax.IsBareKey(c))
{
if (keyParts.Count != 0)
{
AddError("Encountered extra characters in key definition!");
return null;
}
if (!ReadKeyName(ref keyParts, '='))
{
return null;
}
continue;
}
if (TomlSyntax.IsWhiteSpace(c))
{
ConsumeChar();
continue;
}
if (c == '=')
{
ConsumeChar();
return ReadValue();
}
AddError($"Unexpected character \"{c}\" in key name.");
return null;
}
return null;
}
private TomlNode ReadValue(bool skipNewlines = false)
{
int num;
while ((num = reader.Peek()) >= 0)
{
char c = (char)num;
if (TomlSyntax.IsWhiteSpace(c))
{
ConsumeChar();
continue;
}
if (c == '#')
{
AddError("No value found!");
return null;
}
if (TomlSyntax.IsNewLine(c))
{
if (skipNewlines)
{
reader.Read();
AdvanceLine(1);
continue;
}
AddError("Encountered a newline when expecting a value!");
return null;
}
if (TomlSyntax.IsQuoted(c))
{
char excess;
bool flag = IsTripleQuote(c, out excess);
if (currentState == ParseState.None)
{
return null;
}
string text = (flag ? ReadQuotedValueMultiLine(c) : ReadQuotedValueSingleLine(c, excess));
if (text == null)
{
return null;
}
return new TomlString
{
Value = text,
IsMultiline = flag,
PreferLiteral = (c == '\'')
};
}
return c switch
{
'{' => ReadInlineTable(),
'[' => ReadArray(),
_ => ReadTomlValue(),
};
}
return null;
}
private bool ReadKeyName(ref List<string> parts, char until)
{
StringBuilder stringBuilder = new StringBuilder();
bool flag = false;
bool flag2 = false;
int num;
while ((num = reader.Peek()) >= 0)
{
char c = (char)num;
if (c == until)
{
break;
}
if (TomlSyntax.IsWhiteSpace(c))
{
flag2 = true;
}
else
{
if (stringBuilder.Length == 0)
{
flag2 = false;
}
if (c == '.')
{
if (stringBuilder.Length == 0 && !flag)
{
return AddError("Found an extra subkey separator in " + ".".Join(parts) + "...");
}
parts.Add(stringBuilder.ToString());
stringBuilder.Length = 0;
flag = false;
flag2 = false;
}
else
{
if (flag2)
{
return AddError("Invalid spacing in key name");
}
if (TomlSyntax.IsQuoted(c))
{
if (flag)
{
return AddError("Expected a subkey separator but got extra data instead!");
}
if (stringBuilder.Length != 0)
{
return AddError("Encountered a quote in the middle of subkey name!");
}
col++;
stringBuilder.Append(ReadQuotedValueSingleLine((char)reader.Read()));
flag = true;
continue;
}
if (!TomlSyntax.IsBareKey(c))
{
break;
}
stringBuilder.Append(c);
}
}
reader.Read();
col++;
}
if (stringBuilder.Length == 0 && !flag)
{
return AddError("Found an extra subkey separator in " + ".".Join(parts) + "...");
}
parts.Add(stringBuilder.ToString());
return true;
}
private string ReadRawValue()
{
StringBuilder stringBuilder = new StringBuilder();
int num;
while ((num = reader.Peek()) >= 0)
{
char c = (char)num;
if (c == '#' || TomlSyntax.IsNewLine(c) || TomlSyntax.IsValueSeparator(c))
{
break;
}
stringBuilder.Append(c);
ConsumeChar();
}
return stringBuilder.ToString().Trim();
}
private TomlNode ReadTomlValue()
{
string text = ReadRawValue();
string text2 = text;
string text3 = text2;
int numberBase;
TomlNode tomlNode = (TomlSyntax.IsBoolean(text3) ? ((TomlNode)bool.Parse(text3)) : (TomlSyntax.IsNaN(text2) ? ((TomlNode)double.NaN) : (TomlSyntax.IsPosInf(text2) ? ((TomlNode)double.PositiveInfinity) : (TomlSyntax.IsNegInf(text2) ? ((TomlNode)double.NegativeInfinity) : (TomlSyntax.IsInteger(text2) ? ((TomlNode)long.Parse(text.RemoveAll('_'), CultureInfo.InvariantCulture)) : (TomlSyntax.IsFloat(text2) ? ((TomlNode)double.Parse(text.RemoveAll('_'), CultureInfo.InvariantCulture)) : ((!TomlSyntax.IsIntegerWithBase(text2, out numberBase)) ? null : new TomlInteger
{
Value = Convert.ToInt64(text.Substring(2).RemoveAll('_'), numberBase),
IntegerBase = (TomlInteger.Base)numberBase
})))))));
TomlNode tomlNode2 = tomlNode;
if (tomlNode2 != null)
{
return tomlNode2;
}
text = text.Replace(" ", "T");
if (StringUtils.TryParseDateTime(text, TomlSyntax.RFC3339LocalDateTimeFormats, DateTimeStyles.AssumeLocal, (StringUtils.TryDateParseDelegate<DateTime>)DateTime.TryParseExact, out DateTime dateTime, out int parsedFormat))
{
return new TomlDateTimeLocal
{
Value = dateTime,
SecondsPrecision = parsedFormat
};
}
if (DateTime.TryParseExact(text, TomlSyntax.LocalDateFormat, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out dateTime))
{
return new TomlDateTimeLocal
{
Value = dateTime,
Style = TomlDateTimeLocal.DateTimeStyle.Date
};
}
if (StringUtils.TryParseDateTime(text, TomlSyntax.RFC3339LocalTimeFormats, DateTimeStyles.AssumeLocal, (StringUtils.TryDateParseDelegate<DateTime>)DateTime.TryParseExact, out dateTime, out parsedFormat))
{
return new TomlDateTimeLocal
{
Value = dateTime,
Style = TomlDateTimeLocal.DateTimeStyle.Time,
SecondsPrecision = parsedFormat
};
}
if (StringUtils.TryParseDateTime(text, TomlSyntax.RFC3339Formats, DateTimeStyles.None, (StringUtils.TryDateParseDelegate<DateTimeOffset>)DateTimeOffset.TryParseExact, out DateTimeOffset dateTime2, out parsedFormat))
{
return new TomlDateTimeOffset
{
Value = dateTime2,
SecondsPrecision = parsedFormat
};
}
AddError("Value \"" + text + "\" is not a valid TOML value!");
return null;
}
private TomlArray ReadArray()
{
ConsumeChar();
TomlArray tomlArray = new TomlArray();
TomlNode tomlNode = null;
bool flag = true;
int num;
while ((num = reader.Peek()) >= 0)
{
char c = (char)num;
switch (c)
{
case ']':
break;
case '#':
reader.ReadLine();
AdvanceLine(1);
continue;
default:
if (TomlSyntax.IsWhiteSpace(c) || TomlSyntax.IsNewLine(c))
{
if (TomlSyntax.IsLineBreak(c))
{
AdvanceLine();
}
}
else
{
if (c != ',')
{
if (!flag)
{
AddError("Missing separator between values");
return null;
}
tomlNode = ReadValue(skipNewlines: true);
if (tomlNode == null)
{
if (currentState != 0)
{
AddError("Failed to determine and parse a value!");
}
return null;
}
flag = false;
continue;
}
if (tomlNode == null)
{
AddError("Encountered multiple value separators");
return null;
}
tomlArray.Add(tomlNode);
tomlNode = null;
flag = true;
}
ConsumeChar();
continue;
}
ConsumeChar();
break;
}
if (tomlNode != null)
{
tomlArray.Add(tomlNode);
}
return tomlArray;
}
private TomlNode ReadInlineTable()
{
ConsumeChar();
TomlTable tomlTable = new TomlTable
{
IsInline = true
};
TomlNode tomlNode = null;
bool flag = false;
List<string> list = new List<string>();
int num;
while ((num = reader.Peek()) >= 0)
{
char c = (char)num;
switch (c)
{
case '}':
break;
case '#':
AddError("Incomplete inline table definition!");
return null;
default:
if (TomlSyntax.IsNewLine(c))
{
AddError("Inline tables are only allowed to be on single line");
return null;
}
if (!TomlSyntax.IsWhiteSpace(c))
{
if (c != ',')
{
flag = false;
tomlNode = ReadKeyValuePair(list);
continue;
}
if (tomlNode == null)
{
AddError("Encountered multiple value separators in inline table!");
return null;
}
if (!InsertNode(tomlNode, tomlTable, list))
{
return null;
}
list.Clear();
tomlNode = null;
flag = true;
}
ConsumeChar();
continue;
}
ConsumeChar();
break;
}
if (flag)
{
AddError("Trailing commas are not allowed in inline tables.");
return null;
}
if (tomlNode != null && !InsertNode(tomlNode, tomlTable, list))
{
return null;
}
return tomlTable;
}
private bool IsTripleQuote(char quote, out char excess)
{
ConsumeChar();
int num;
if ((num = reader.Peek()) < 0)
{
excess = '\0';
return AddError("Unexpected end of file!");
}
if ((ushort)num != quote)
{
excess = '\0';
return false;
}
excess = (char)ConsumeChar();
if ((num = reader.Peek()) < 0 || (ushort)num != quote)
{
return false;
}
ConsumeChar();
excess = '\0';
return true;
}
private bool ProcessQuotedValueCharacter(char quote, bool isNonLiteral, char c, StringBuilder sb, ref bool escaped)
{
if (TomlSyntax.MustBeEscaped(c))
{
return AddError($"The character U+{(int)c:X8} must be escaped in a string!");
}
if (escaped)
{
sb.Append(c);
escaped = false;
return false;
}
if (c == quote)
{
return true;
}
if (isNonLiteral && c == '\\')
{
escaped = true;
}
if (c == '\n')
{
return AddError("Encountered newline in single line string!");
}
sb.Append(c);
return false;
}
private string ReadQuotedValueSingleLine(char quote, char initialData = '\0')
{
bool flag = quote == '"';
StringBuilder stringBuilder = new StringBuilder();
bool escaped = false;
if (initialData != 0)
{
bool flag2 = ProcessQuotedValueCharacter(quote, flag, initialData, stringBuilder, ref escaped);
if (currentState == ParseState.None)
{
return null;
}
if (flag2)
{
if (flag)
{
if (stringBuilder.ToString().TryUnescape(out var unescaped, out var exception))
{
return unescaped;
}
AddError(exception.Message);
return null;
}
return stringBuilder.ToString();
}
}
bool flag3 = false;
int num;
while ((num = reader.Read()) >= 0)
{
col++;
char c = (char)num;
flag3 = ProcessQuotedValueCharacter(quote, flag, c, stringBuilder, ref escaped);
if (flag3)
{
if (currentState != 0)
{
break;
}
return null;
}
}
if (!flag3)
{
AddError("Unclosed string.");
return null;
}
if (!flag)
{
return stringBuilder.ToString();
}
if (stringBuilder.ToString().TryUnescape(out var unescaped2, out var exception2))
{
return unescaped2;
}
AddError(exception2.Message);
return null;
}
private string ReadQuotedValueMultiLine(char quote)
{
bool flag = quote == '"';
StringBuilder stringBuilder = new StringBuilder();
bool flag2 = false;
bool flag3 = false;
bool flag4 = false;
int num = 0;
bool flag5 = true;
int num2;
while ((num2 = ConsumeChar()) >= 0)
{
char c = (char)num2;
if (TomlSyntax.MustBeEscaped(c, allowNewLines: true))
{
AddError($"The character U+{(int)c:X8} must be escaped!");
return null;
}
if (flag5 && TomlSyntax.IsNewLine(c))
{
if (TomlSyntax.IsLineBreak(c))
{
flag5 = false;
}
else
{
AdvanceLine();
}
continue;
}
flag5 = false;
if (flag2)
{
stringBuilder.Append(c);
flag2 = false;
continue;
}
if (flag3)
{
if (TomlSyntax.IsEmptySpace(c))
{
if (TomlSyntax.IsLineBreak(c))
{
flag4 = true;
AdvanceLine();
}
continue;
}
if (!flag4)
{
AddError("Non-whitespace character after trim marker.");
return null;
}
flag4 = false;
flag3 = false;
}
if (flag && c == '\\')
{
int num3 = reader.Peek();
char c2 = (char)num3;
if (num3 >= 0)
{
if (TomlSyntax.IsEmptySpace(c2))
{
flag3 = true;
continue;
}
if (c2 == quote || c2 == '\\')
{
flag2 = true;
}
}
}
num = ((c == quote) ? (num + 1) : 0);
if (num == 3)
{
break;
}
stringBuilder.Append(c);
}
num = 0;
while ((num2 = reader.Peek()) >= 0)
{
char c3 = (char)num2;
if (c3 != quote || ++num >= 3)
{
break;
}
stringBuilder.Append(c3);
ConsumeChar();
}
stringBuilder.Length -= 2;
if (!flag)
{
return stringBuilder.ToString();
}
if (stringBuilder.ToString().TryUnescape(out var unescaped, out var exception))
{
return unescaped;
}
AddError(exception.Message);
return null;
}
private bool InsertNode(TomlNode node, TomlNode root, IList<string> path)
{
TomlNode tomlNode = root;
if (path.Count > 1)
{
for (int i = 0; i < path.Count - 1; i++)
{
string key = path[i];
if (!tomlNode.TryGetNode(key, out var node2))
{
node2 = (tomlNode[key] = new TomlTable());
}
else if (node2.HasValue)
{
return AddError("The key " + ".".Join(path) + " already has a value assigned to it!");
}
tomlNode = node2;
if (tomlNode is TomlTable tomlTable && tomlTable.IsInline)
{
return AddError("Cannot assign " + ".".Join(path) + " because it will edit an immutable table.");
}
}
}
if (tomlNode.HasKey(path[path.Count - 1]))
{
return AddError("The key " + ".".Join(path) + " is already defined!");
}
tomlNode[path[path.Count - 1]] = node;
node.CollapseLevel = path.Count - 1;
return true;
}
private TomlTable CreateTable(TomlNode root, IList<string> path, bool arrayTable)
{
if (path.Count == 0)
{
return null;
}
TomlNode tomlNode = root;
for (int i = 0; i < path.Count; i++)
{
string key = path[i];
if (tomlNode.TryGetNode(key, out var node))
{
if (node.IsArray && arrayTable)
{
TomlArray tomlArray = (TomlArray)node;
if (!tomlArray.IsTableArray)
{
AddError("The array " + ".".Join(path) + " cannot be redefined as an array table!");
return null;
}
if (i == path.Count - 1)
{
tomlNode = new TomlTable();
tomlArray.Add(tomlNode);
break;
}
tomlNode = tomlArray[tomlArray.ChildrenCount - 1];
continue;
}
if (node.HasValue)
{
if (!(node is TomlArray tomlArray2) || !tomlArray2.IsTableArray)
{
AddError("The key " + ".".Join(path) + " has a value assigned to it!");
return null;
}
tomlNode = tomlArray2[tomlArray2.ChildrenCount - 1];
continue;
}
if (i == path.Count - 1)
{
if (arrayTable && !node.IsArray)
{
AddError("The table " + ".".Join(path) + " cannot be redefined as an array table!");
return null;
}
if (node is TomlTable tomlTable && !tomlTable.IsInline)
{
AddError("The table " + ".".Join(path) + " is defined multiple times!");
return null;
}
}
}
else
{
if (i == path.Count - 1 && arrayTable)
{
TomlTable tomlTable2 = new TomlTable();
TomlArray tomlArray3 = new TomlArray
{
IsTableArray = true
};
tomlArray3.Add(tomlTable2);
tomlNode[key] = tomlArray3;
tomlNode = tomlTable2;
break;
}
node = (tomlNode[key] = new TomlTable
{
IsInline = true
});
}
tomlNode = node;
}
return (TomlTable)tomlNode;
}
private string ParseComment()
{
ConsumeChar();
string obj = reader.ReadLine()?.Trim() ?? "";
if (obj.Any((char ch) => TomlSyntax.MustBeEscaped(ch)))
{
AddError("Comment must not contain control characters other than tab.", skipLine: false);
}
return obj;
}
}
public static class TOML
{
public static bool ForceASCII { get; set; }
public static TomlTable Parse(TextReader reader)
{
using TOMLParser tOMLParser = new TOMLParser(reader)
{
ForceASCII = ForceASCII
};
return tOMLParser.Parse();
}
}
public class TomlFormatException : Exception
{
public TomlFormatException(string message)
: base(message)
{
}
}
public class TomlParseException : Exception
{
public TomlTable ParsedTable { get; }
public IEnumerable<TomlSyntaxException> SyntaxErrors { get; }
public TomlParseException(TomlTable parsed, IEnumerable<TomlSyntaxException> exceptions)
: base("TOML file contains format errors")
{
ParsedTable = parsed;
SyntaxErrors = exceptions;
}
}
public class TomlSyntaxException : Exception
{
public TOMLParser.ParseState ParseState { get; }
public int Line { get; }
public int Column { get; }
public TomlSyntaxException(string message, TOMLParser.ParseState state, int line, int col)
: base(message)
{
ParseState = state;
Line = line;
Column = col;
}
}
internal static class TomlSyntax
{
public const string TRUE_VALUE = "true";
public const string FALSE_VALUE = "false";
public const string NAN_VALUE = "nan";
public const string POS_NAN_VALUE = "+nan";
public const string NEG_NAN_VALUE = "-nan";
public const string INF_VALUE = "inf";
public const string POS_INF_VALUE = "+inf";
public const string NEG_INF_VALUE = "-inf";
public static readonly Regex IntegerPattern = new Regex("^(\\+|-)?(?!_)(0|(?!0)(_?\\d)*)$", RegexOptions.Compiled);
public static readonly Regex BasedIntegerPattern = new Regex("^0(?<base>x|b|o)(?!_)(_?[0-9A-F])*$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
public static readonly Regex FloatPattern = new Regex("^(\\+|-)?(?!_)(0|(?!0)(_?\\d)+)(((e(\\+|-)?(?!_)(_?\\d)+)?)|(\\.(?!_)(_?\\d)+(e(\\+|-)?(?!_)(_?\\d)+)?))$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
public static readonly Dictionary<string, int> IntegerBases = new Dictionary<string, int>
{
["x"] = 16,
["o"] = 8,
["b"] = 2
};
public static readonly Dictionary<int, string> BaseIdentifiers = new Dictionary<int, string>
{
[2] = "b",
[8] = "o",
[16] = "x"
};
public const string RFC3339EmptySeparator = " ";
public const string ISO861Separator = "T";
public const string ISO861ZeroZone = "+00:00";
public const string RFC3339ZeroZone = "Z";
public static readonly string[] RFC3339Formats = new string[8] { "yyyy'-'MM-ddTHH':'mm':'ssK", "yyyy'-'MM-ddTHH':'mm':'ss'.'fK", "yyyy'-'MM-ddTHH':'mm':'ss'.'ffK", "yyyy'-'MM-ddTHH':'mm':'ss'.'fffK", "yyyy'-'MM-ddTHH':'mm':'ss'.'ffffK", "yyyy'-'MM-ddTHH':'mm':'ss'.'fffffK", "yyyy'-'MM-ddTHH':'mm':'ss'.'ffffffK", "yyyy'-'MM-ddTHH':'mm':'ss'.'fffffffK" };
public static readonly string[] RFC3339LocalDateTimeFormats = new string[8] { "yyyy'-'MM-ddTHH':'mm':'ss", "yyyy'-'MM-ddTHH':'mm':'ss'.'f", "yyyy'-'MM-ddTHH':'mm':'ss'.'ff", "yyyy'-'MM-ddTHH':'mm':'ss'.'fff", "yyyy'-'MM-ddTHH':'mm':'ss'.'ffff", "yyyy'-'MM-ddTHH':'mm':'ss'.'fffff", "yyyy'-'MM-ddTHH':'mm':'ss'.'ffffff", "yyyy'-'MM-ddTHH':'mm':'ss'.'fffffff" };
public static readonly string LocalDateFormat = "yyyy'-'MM'-'dd";
public static readonly string[] RFC3339LocalTimeFormats = new string[8] { "HH':'mm':'ss", "HH':'mm':'ss'.'f", "HH':'mm':'ss'.'ff", "HH':'mm':'ss'.'fff", "HH':'mm':'ss'.'ffff", "HH':'mm':'ss'.'fffff", "HH':'mm':'ss'.'ffffff", "HH':'mm':'ss'.'fffffff" };
public const char ARRAY_END_SYMBOL = ']';
public const char ITEM_SEPARATOR = ',';
public const char ARRAY_START_SYMBOL = '[';
public const char BASIC_STRING_SYMBOL = '"';
public const char COMMENT_SYMBOL = '#';
public const char ESCAPE_SYMBOL = '\\';
public const char KEY_VALUE_SEPARATOR = '=';
public const char NEWLINE_CARRIAGE_RETURN_CHARACTER = '\r';
public const char NEWLINE_CHARACTER = '\n';
public const char SUBKEY_SEPARATOR = '.';
public const char TABLE_END_SYMBOL = ']';
public const char TABLE_START_SYMBOL = '[';
public const char INLINE_TABLE_START_SYMBOL = '{';
public const char INLINE_TABLE_END_SYMBOL = '}';
public const char LITERAL_STRING_SYMBOL = '\'';
public const char INT_NUMBER_SEPARATOR = '_';
public static readonly char[] NewLineCharacters = new char[2] { '\n', '\r' };
public static bool IsBoolean(string s)
{
if (!(s == "true"))
{
return s == "false";
}
return true;
}
public static bool IsPosInf(string s)
{
if (!(s == "inf"))
{
return s == "+inf";
}
return true;
}
public static bool IsNegInf(string s)
{
return s == "-inf";
}
public static bool IsNaN(string s)
{
if (!(s == "nan") && !(s == "+nan"))
{
return s == "-nan";
}
return true;
}
public static bool IsInteger(string s)
{
return IntegerPattern.IsMatch(s);
}
public static bool IsFloat(string s)
{
return FloatPattern.IsMatch(s);
}
public static bool IsIntegerWithBase(string s, out int numberBase)
{
numberBase = 10;
Match match = BasedIntegerPattern.Match(s);
if (!match.Success)
{
return false;
}
IntegerBases.TryGetValue(match.Groups["base"].Value, out numberBase);
return true;
}
public static bool IsQuoted(char c)
{
if (c != '"')
{
return c == '\'';
}
return true;
}
public static bool IsWhiteSpace(char c)
{
if (c != ' ')
{
return c == '\t';
}
return true;
}
public static bool IsNewLine(char c)
{
if (c != '\n')
{
return c == '\r';
}
return true;
}
public static bool IsLineBreak(char c)
{
return c == '\n';
}
public static bool IsEmptySpace(char c)
{
if (!IsWhiteSpace(c))
{
return IsNewLine(c);
}
return true;
}
public static bool IsBareKey(char c)
{
switch (c)
{
case '-':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
case 'G':
case 'H':
case 'I':
case 'J':
case 'K':
case 'L':
case 'M':
case 'N':
case 'O':
case 'P':
case 'Q':
case 'R':
case 'S':
case 'T':
case 'U':
case 'V':
case 'W':
case 'X':
case 'Y':
case 'Z':
case '_':
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
case 'g':
case 'h':
case 'i':
case 'j':
case 'k':
case 'l':
case 'm':
case 'n':
case 'o':
case 'p':
case 'q':
case 'r':
case 's':
case 't':
case 'u':
case 'v':
case 'w':
case 'x':
case 'y':
case 'z':
return true;
default:
return false;
}
}
public static bool MustBeEscaped(char c, bool allowNewLines = false)
{
if (c >= '\u000e')
{
if (c <= '\u001f' || c == '\u007f')
{
goto IL_001f;
}
}
else if (c <= '\b' || c == '\v' || c == '\f')
{
goto IL_001f;
}
bool flag = false;
goto IL_0025;
IL_001f:
flag = true;
goto IL_0025;
IL_0025:
bool flag2 = flag;
if (!allowNewLines)
{
flag2 = flag2 || (c >= '\n' && c <= '\u000e');
}
return flag2;
}
public static bool IsValueSeparator(char c)
{
if (c != ',' && c != ']')
{
return c == '}';
}
return true;
}
}
internal static class StringUtils
{
public delegate bool TryDateParseDelegate<T>(string s, string format, IFormatProvider ci, DateTimeStyles dts, out T dt);
public static string AsKey(this string key)
{
if (key == string.Empty || key.Any((char c) => !TomlSyntax.IsBareKey(c)))
{
return $"{'"'}{key.Escape()}{'"'}";
}
return key;
}
public static string Join(this string self, IEnumerable<string> subItems)
{
StringBuilder stringBuilder = new StringBuilder();
bool flag = true;
foreach (string subItem in subItems)
{
if (!flag)
{
stringBuilder.Append(self);
}
flag = false;
stringBuilder.Append(subItem);
}
return stringBuilder.ToString();
}
public static bool TryParseDateTime<T>(string s, string[] formats, DateTimeStyles styles, TryDateParseDelegate<T> parser, out T dateTime, out int parsedFormat)
{
parsedFormat = 0;
dateTime = default(T);
for (int i = 0; i < formats.Length; i++)
{
string format = formats[i];
if (parser(s, format, CultureInfo.InvariantCulture, styles, out dateTime))
{
parsedFormat = i;
return true;
}
}
return false;
}
public static void AsComment(this string self, TextWriter tw)
{
string[] array = self.Split(new char[1] { '\n' });
foreach (string text in array)
{
tw.WriteLine($"{'#'} {text.Trim()}");
}
}
public static string RemoveAll(this string txt, char toRemove)
{
StringBuilder stringBuilder = new StringBuilder(txt.Length);
foreach (char item in txt.Where((char c) => c != toRemove))
{
stringBuilder.Append(item);
}
return stringBuilder.ToString();
}
public static string Escape(this string txt, bool escapeNewlines = true)
{
StringBuilder stringBuilder = new StringBuilder(txt.Length + 2);
StringBuilder stringBuilder2;
object value;
for (int j = 0; j < txt.Length; stringBuilder2.Append(value), j++)
{
char c2 = txt[j];
stringBuilder2 = stringBuilder;
switch (c2)
{
case '\b':
value = "\\b";
continue;
case '\t':
value = "\\t";
continue;
case '\n':
if (escapeNewlines)
{
value = "\\n";
continue;
}
break;
case '\f':
value = "\\f";
continue;
case '\r':
if (escapeNewlines)
{
value = "\\r";
continue;
}
break;
case '\\':
value = "\\\\";
continue;
case '"':
value = "\\\"";
continue;
}
value = ((!TomlSyntax.MustBeEscaped(c2, !escapeNewlines) && (!TOML.ForceASCII || c2 <= '\u007f')) ? ((object)c2) : CodePoint(txt, ref j, c2));
}
return stringBuilder.ToString();
static string CodePoint(string txt, ref int i, char c)
{
if (!char.IsSurrogatePair(txt, i))
{
return $"\\u{(ushort)c:X4}";
}
return $"\\U{char.ConvertToUtf32(txt, i++):X8}";
}
}
public static bool TryUnescape(this string txt, out string unescaped, out Exception exception)
{
try
{
exception = null;
unescaped = txt.Unescape();
return true;
}
catch (Exception ex)
{
exception = ex;
unescaped = null;
return false;
}
}
public static string Unescape(this string txt)
{
if (string.IsNullOrEmpty(txt))
{
return txt;
}
StringBuilder stringBuilder = new StringBuilder(txt.Length);
int num2 = 0;
while (num2 < txt.Length)
{
int num3 = txt.IndexOf('\\', num2);
int num4 = num3 + 1;
if (num3 < 0 || num3 == txt.Length - 1)
{
num3 = txt.Length;
}
stringBuilder.Append(txt, num2, num3 - num2);
if (num3 >= txt.Length)
{
break;
}
char c = txt[num4];
StringBuilder stringBuilder2 = stringBuilder;
stringBuilder2.Append(c switch
{
'b' => "\b",
't' => "\t",
'n' => "\n",
'f' => "\f",
'r' => "\r",
'\'' => "'",
'"' => "\"",
'\\' => "\\",
'u' => CodePoint(num4, txt, ref num3, 4),
'U' => CodePoint(num4, txt, ref num3, 8),
_ => throw new Exception("Undefined escape sequence!"),
});
num2 = num3 + 2;
}
return stringBuilder.ToString();
static string CodePoint(int next, string txt, ref int num, int size)
{
if (next + size >= txt.Length)
{
throw new Exception("Undefined escape sequence!");
}
num += size;
return char.ConvertFromUtf32(Convert.ToInt32(txt.Substring(next + 1, size), 16));
}
}
}