diff --git a/ConfigParser.cs b/ConfigParser.cs index 5227b2f78ceeb5ec6e5778ba4bf0019c624ddca1..a928747729578ad1ef7a39d21e48639fe23bd0fd 100644 --- a/ConfigParser.cs +++ b/ConfigParser.cs @@ -66,35 +66,35 @@ class ConfigDuplicitKeyException: ConfigDuplicitException } class ConfigNoAmenException: ConfigException { - public ConfigValue Value; - public ConfigNoAmenException(ConfigValue value) + public ConfigKeyValue Value; + public ConfigNoAmenException(ConfigKeyValue value) { Value = value; } public override string ErrorString() { - return $"End string \"{Value.amen}\" for block {Value.Key} starting at line {Value.Line} not found"; + return $"End string \"{Value.amen}\" for block \"{Value.Key}\" starting at line {Value.Line} not found"; } } class ConfigMixedTabException: ConfigException { - public ConfigValue Value; + public ConfigKeyValue Value; public int Line; - public ConfigMixedTabException(ConfigValue value, int line) + public ConfigMixedTabException(ConfigKeyValue value, int line) { Value = value; Line = line; } public override string ErrorString() { - return $"{Line}: {Value.FullName}: Combination of tabs and spaces is not supported."; + return $"{Value.Place}: Combination of tabs and spaces is not supported."; } } class ConfigWrongIndentException: ConfigException { - public ConfigValue? Value; + public ConfigKeyValue? Value; public int Line; - public ConfigWrongIndentException(ConfigValue? value, int line) + public ConfigWrongIndentException(ConfigKeyValue? value, int line) { Value = value; Line = line; @@ -103,7 +103,7 @@ class ConfigWrongIndentException: ConfigException { if(Value == null) return $"{Line}: Wrong indent."; - return $"{Line}: {Value.FullName}: Wrong indent."; + return $"{Value.Place}: Wrong indent."; } } class ConfigSectionNotDefinedException: ConfigException @@ -115,7 +115,7 @@ class ConfigSectionNotDefinedException: ConfigException } public override string ErrorString() { - return $"Section {Name} is not defined."; + return $"Section \"{Name}\" is not defined."; } } class ConfigNotDefinedException: ConfigException @@ -129,7 +129,7 @@ class ConfigNotDefinedException: ConfigException } public override string ErrorString() { - return $"{Section.SectionName ?? "[global]"}: Attribute {Name} is not defined."; + return $"{Section.SectionName ?? "[global]"}: Option \"{Name}\" is not defined."; } } class ConfigNoValueException: ConfigException @@ -143,22 +143,21 @@ class ConfigNoValueException: ConfigException } public override string ErrorString() { - return $"{Value.Line}: Attribute {Value.FullName} need value." ; + return $"{Value.Place}: Value needed." ; } } -class ConfigParseAttributeException: ConfigException +class ConfigParseValueException: ConfigException { public ConfigValue Value; public string? Reason; - public ConfigParseAttributeException(ConfigValue value, string? reason=null) + public ConfigParseValueException(ConfigValue value, string? reason=null) { Value = value; Reason = reason; } public override string ErrorString() { - string name = Value.Section.SectionName != null ? $"{Value.Section.SectionName}: {Value.Key}" : Value.Key; - return $"{Value.Line}: Parsing attribute {Value.FullName} fail" + + return $"{Value.Place}: Parsing fail" + (Reason==null?".":$": {Reason}."); } } @@ -173,24 +172,19 @@ class ConfigMistake: ConfigException } public override string ErrorString() { - string name = Value.Section.SectionName != null ? $"{Value.Section.SectionName}: {Value.Key}" : Value.Key; - return $"{Value.Line}: {Value.FullName}: {Text}"; + return $"{Value.Place}: {Text}"; } } -class ConfigValue +abstract class ConfigValue { - public ConfigSection Section {get; init;} - internal string? amen; - internal string? tabs; - public string Key {get; init;} + public int Line; internal string? Value; public bool HasText() => Value != null; - public int Line; - public string FullName - { - get => Section.SectionName != null ? $"{Section.SectionName}: {Key}" : Key; - } + + public virtual string Place{get => $"{Line}: {FullName}";} + public abstract string FullName{get;} // For showing in error + public string AsString() { if(Value == null) throw new ConfigNoValueException(this); @@ -235,7 +229,7 @@ class ConfigValue return System.Drawing.ColorTranslator.FromHtml(AsString()); } catch (ArgumentException) { - throw new ConfigParseAttributeException(this, "String is not color"); + throw new ConfigParseValueException(this, "String is not color"); } } public bool AsBool() @@ -246,7 +240,7 @@ class ConfigValue return true; if(v == "false" || v == "no" || v == "unuse" || v == "0") return false; - throw new ConfigParseAttributeException(this, "String is not bool"); + throw new ConfigParseValueException(this, "String is not bool"); } public double AsDouble() @@ -256,7 +250,7 @@ class ConfigValue return double.Parse(AsString()); } catch (FormatException) { - throw new ConfigParseAttributeException(this, "String is not number"); + throw new ConfigParseValueException(this, "String is not number"); } } public int AsInt() @@ -266,7 +260,7 @@ class ConfigValue return int.Parse(AsString()); } catch (FormatException) { - throw new ConfigParseAttributeException(this, "String is not int"); + throw new ConfigParseValueException(this, "String is not int"); } } public int AsMs() @@ -278,34 +272,86 @@ class ConfigValue return new ConfigParser(AsString(), Line); } public static implicit operator string(ConfigValue v) => v.AsString(); - internal ConfigValue(ConfigSection _Section, string _Key, int _line) +} +class ConfigKeyValue: ConfigValue +{ + public ConfigSection Section {get; init;} + internal string? amen; + internal string? tabs; + public string Key {get; init;} + public override string FullName + { + get => Section.SectionName != null ? $"{Section.SectionName}: {Key}" : Key; + } + internal ConfigKeyValue(ConfigSection _Section, string _Key, int _line) { Section = _Section; Key = _Key; Line = _line; } + public ConfigKeyAsValue KeyAsValue() => new(this); } -class ConfigSection: IReadOnlyList<ConfigValue> +class ConfigKeyAsValue: ConfigValue +{ + public ConfigSection Section {get; init;} + public override string FullName + { + get => (Section.SectionName != null ? $"{Section.SectionName}: " : "") + $"Key {Value}"; + } + internal ConfigKeyAsValue(ConfigKeyValue keyValue) + { + Line = keyValue.Line; + Value = keyValue.Key; + Section = keyValue.Section; + } +} +class ConfigSectionNameAsValue: ConfigValue +{ + public ConfigSection Section {get; init;} + public override string FullName + { + get => "Section name " + (Section.SectionName != null ? $"\"{Section.SectionName}\"" : "of main section"); + } + internal ConfigSectionNameAsValue(ConfigSection _Section) + { + Section = _Section; + Value = Section.SectionName; + Line = Section.Line; + } +} +class ConfigSectionNameAsDefaultValue: ConfigSectionNameAsValue +{ + protected string defaultFor; + internal ConfigSectionNameAsDefaultValue(ConfigSection _Section, string _defaultFor): base(_Section) + { + defaultFor = _defaultFor; + } + public override string FullName + { + get => base.FullName + $" as default for \"{defaultFor}\" option"; + } +} +class ConfigSection: IReadOnlyList<ConfigKeyValue> { public ConfigParser Root {get; init;} public string? SectionName {get; init;} public int Line; - List<ConfigValue> values = new(); - Dictionary<string, ConfigValue> valueByName = new(); + List<ConfigKeyValue> values = new(); + Dictionary<string, ConfigKeyValue> valueByName = new(); internal ConfigSection(ConfigParser _Root, string? _SectionName, int _line) { Root = _Root; SectionName = _SectionName; Line = _line; } - internal void Add(ConfigValue v) + internal void Add(ConfigKeyValue v) { if(valueByName.ContainsKey(v.Key)) throw new ConfigDuplicitKeyException(v.Key, v.Line, valueByName[v.Key].Line); valueByName[v.Key] = v; values.Add(v); } - public IEnumerator<ConfigValue> GetEnumerator() + public IEnumerator<ConfigKeyValue> GetEnumerator() { foreach(var s in values) yield return s; @@ -313,21 +359,25 @@ class ConfigSection: IReadOnlyList<ConfigValue> System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); public int Count{ get{return values.Count;}} - public ConfigValue this[int i] + public ConfigKeyValue this[int i] { get { return values[i]; } } - public ConfigValue? this[string key] + public ConfigKeyValue? this[string key] { get { return Optional(key); } } - public ConfigValue? Optional(string key) + public ConfigKeyValue? Optional(string key) { if(!valueByName.TryGetValue(key, out var r)) return null; return r; } - public ConfigValue Mandatory(string key) + public ConfigValue OptionalDefaultIsSectionName(string key) + { + return (ConfigValue?)Optional(key) ?? new ConfigSectionNameAsDefaultValue(this, key); + } + public ConfigKeyValue Mandatory(string key) { if(!valueByName.TryGetValue(key, out var r)) throw new ConfigNotDefinedException(this, key); @@ -345,6 +395,7 @@ class ConfigSection: IReadOnlyList<ConfigValue> w.WriteLine($"'{x.Key}' = '{(x.HasText() ? x.AsString() : "null")}'"); } } + public ConfigSectionNameAsValue SectionNameAsValue() => new(this); } class ConfigParser: IReadOnlyList<ConfigSection> { @@ -355,7 +406,7 @@ class ConfigParser: IReadOnlyList<ConfigSection> { string[] lines = s.Split("\n"); var currentSection = MainSection = new(this, null, 0); - ConfigValue? lastVal = null; + ConfigKeyValue? lastVal = null; for(int i=0;i<lines.Count();i++) { string l = lines[i]; @@ -408,12 +459,12 @@ class ConfigParser: IReadOnlyList<ConfigSection> int indexArrow = l.IndexOf("<<"); if(indexIs == -1 && indexArrow == -1) { - currentSection.Add(lastVal = new ConfigValue(currentSection, tl, firstLineIndex + i)); + currentSection.Add(lastVal = new ConfigKeyValue(currentSection, tl, firstLineIndex + i)); continue; } int index = indexIs == -1 ? indexArrow : indexArrow == -1 ? indexIs : Math.Min(indexIs, indexArrow); string key = l[0..index].Trim(); - currentSection.Add(lastVal = new ConfigValue(currentSection, key, firstLineIndex + i)); + currentSection.Add(lastVal = new ConfigKeyValue(currentSection, key, firstLineIndex + i)); if(indexIs != -1 && (indexArrow == -1 || indexIs < indexArrow)) { string val = l[(indexIs+1)..].Trim();