Skip to content
Snippets Groups Projects
Select Git revision
  • 9870e69301e853a14d9908a6a8f006968398c96c
  • devel default
  • master
  • fo
  • jirka/typing
  • fo-base
  • mj/submit-images
  • jk/issue-96
  • jk/issue-196
  • honza/add-contestant
  • honza/mr7
  • honza/mrf
  • honza/mrd
  • honza/mra
  • honza/mr6
  • honza/submit-images
  • honza/kolo-vs-soutez
  • jh-stress-test-wip
  • shorten-schools
19 results

rights.py

Blame
  • ConfigParser.cs 10.75 KiB
    #nullable enable
    using System;
    using System.Collections.Generic;
    using System.Text;
    
    using System.IO;
    
    using System.Linq;
    
    using Color = System.Drawing.Color;
    
    namespace Config;
    
    abstract class ConfigException: Exception
    {
    	public override string ToString()
    	{
    		return $"Mistake in config: {ErrorString()}";
    	}
    	abstract public string ErrorString();
    }
    abstract class ConfigDuplicitException: ConfigException
    {
    	public string Name;
    	public int LineA, LineB;
    	public ConfigDuplicitException(string name, int lineA, int lineB)
    	{
    		Name = name;
    		LineA = lineA;
    		LineB = lineB;
    	}
    }
    class ConfigDuplicitSectionException: ConfigDuplicitException
    {
    	public ConfigDuplicitSectionException(string name, int lineA, int lineB)
    		:base(name, lineA, lineB){}
    	public override string ErrorString()
    	{
    		return $"Duplicit section name {Name} on lines {LineA}, {LineB}";
    	}
    }
    class ConfigDuplicitKeyException: ConfigDuplicitException
    {
    	public ConfigDuplicitKeyException(string name, int lineA, int lineB)
    		:base(name, lineA, lineB){}
    	public override string ErrorString()
    	{
    		return $"Duplicit key name {Name} on lines {LineA}, {LineB}";
    	}
    }
    class ConfigNoAmenException: ConfigException
    {
    	public ConfigValue Value;
    	public ConfigNoAmenException(ConfigValue value)
    	{
    		Value = value;
    	}
    	public override string ErrorString()
    	{
    		return $"End string \"{Value.amen}\" for block {Value.Key} starting at line {Value.Line} not found";
    	}
    }
    class ConfigMixedTabException: ConfigException
    {
    	public ConfigValue Value;
    	public int Line;
    	public ConfigMixedTabException(ConfigValue value, int line)
    	{
    		Value = value;
    		Line = line;
    	}
    	public override string ErrorString()
    	{
    		return $"{Line}: {Value.FullName}: Combination of tabs and spaces is not supported.";
    	}
    }
    class ConfigWrongIndentException: ConfigException
    {
    	public ConfigValue? Value;
    	public int Line;
    	public ConfigWrongIndentException(ConfigValue? value, int line)
    	{
    		Value = value;
    		Line = line;
    	}
    	public override string ErrorString()
    	{
    		if(Value == null)
    			return $"{Line}: Wrong indent.";
    		return $"{Line}: {Value.FullName}: Wrong indent.";
    	}
    }
    class ConfigSectionNotDefinedException: ConfigException
    {
    	public string Name;
    	public ConfigSectionNotDefinedException(string name)
    	{
    		Name = name;
    	}
    	public override string ErrorString()
    	{
    		return $"Section {Name} is not defined.";
    	}
    }
    class ConfigNotDefinedException: ConfigException
    {
    	public ConfigSection Section;
    	public string Name;
    	public ConfigNotDefinedException(ConfigSection section, string name)
    	{
    		Section = section;
    		Name = name;
    	}
    	public override string ErrorString()
    	{
    		return $"{Section.SectionName ?? "[global]"}: Attribute {Name} is not defined.";
    	}
    }
    class ConfigNoValueException: ConfigException
    {
    	public ConfigValue Value;
    	public string? Reason;
    	public ConfigNoValueException(ConfigValue value, string? reason=null)
    	{
    		Value = value;
    		Reason = reason;
    	}
    	public override string ErrorString()
    	{
    		return $"{Value.Line}: Attribute {Value.FullName} need value." ;
    	}
    }
    class ConfigParseAttributeException: ConfigException
    {
    	public ConfigValue Value;
    	public string? Reason;
    	public ConfigParseAttributeException(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" +
    			(Reason==null?".":$": {Reason}.");
    	}
    }
    class ConfigMistake: ConfigException
    {
    	public ConfigValue Value;
    	public string Text;
    	public ConfigMistake(ConfigValue value, string text)
    	{
    		Value = value;
    		Text = text;
    	}
    	public override string ErrorString()
    	{
    		string name = Value.Section.SectionName != null ? $"{Value.Section.SectionName}: {Value.Key}" : Value.Key;
    		return $"{Value.Line}: {Value.FullName}: {Text}";
    	}
    }
    
    class ConfigValue
    {
    	public ConfigSection Section {get; init;}
    	internal string? amen;
    	internal string? tabs;
    	public string Key {get; init;}
    	internal string? Value;
    	public bool HasText() => Value != null;
    	public int Line;
    	public string FullName
    	{
    		get => Section.SectionName != null ? $"{Section.SectionName}: {Key}" : Key;
    	}
    	public string AsString()
    	{
    		if(Value == null) throw new ConfigNoValueException(this);
    		return Value;
    	}
    	public string AsPath()
    	{
    		string s = AsString();
    		StringBuilder output = new();
    		for(int i=0;i<s.Length;i++)
    		{
    			if(i == 0 && s[i]=='~')
    			{
    				output.Append(Environment.GetEnvironmentVariable("HOME") ?? "");
    			}
    			else  if(s[i]=='$')
    			{
    				int start, end;
    				if(s[i+1]=='{')
    				{
    					start = i+2;
    					while(i<s.Length && s[i]!='}') i++;
    					end = i;
    				}
    				else
    				{
    					start = i+1;
    					while(i+1<s.Length && "qwertyuiopasdfghjklzxcvbnm1234567890QWERTYUIOPASDFGHJKLZXCVBNM_".Contains(s[i+1])) i++;
    					end = i+1;
    				}
    				output.Append(Environment.GetEnvironmentVariable(s[start..end]) ?? "");
    			}
    			else
    				output.Append(s[i]);
    		}
    		return output.ToString();
    	}
    	public Color AsColor()
    	{
    		try
    		{
    			return System.Drawing.ColorTranslator.FromHtml(AsString());
    		} catch (ArgumentException)
    		{
    			throw new ConfigParseAttributeException(this, "String is not color");
    		}
    	}
    	public double AsDouble()
    	{
    		try
    		{
    			return double.Parse(AsString());
    		} catch (ArgumentException)
    		{
    			throw new ConfigParseAttributeException(this, "String is not color");
    		}
    	}
    	public ConfigParser AsConfig()
    	{
    		return new ConfigParser(AsString(), Line);
    	}
    	public static implicit operator string(ConfigValue v) => v.AsString();
    	internal ConfigValue(ConfigSection _Section, string _Key, int _line)
    	{
    		Section = _Section;
    		Key = _Key;
    		Line = _line;
    	}
    }
    class ConfigSection: IReadOnlyList<ConfigValue>
    {
    	public ConfigParser Root {get; init;}
    	public string? SectionName {get; init;}
    	public int Line;
    	List<ConfigValue> values = new();
    	Dictionary<string, ConfigValue> valueByName = new();
    	internal ConfigSection(ConfigParser _Root, string? _SectionName, int _line)
    	{
    		Root = _Root;
    		SectionName = _SectionName;
    		Line = _line;
    	}
    	internal void Add(ConfigValue 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()
    	{
    		foreach(var s in values)
    			yield return s;
    	}
    	System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    		=> GetEnumerator();
    	public int Count{ get{return values.Count;}}
    	public ConfigValue this[int i]
    	{
    		get { return values[i]; }
    	}
    	public ConfigValue? this[string key]
    	{
    		get { return Optional(key); }
    	}
    	public ConfigValue? Optional(string key)
    	{
    		if(!valueByName.TryGetValue(key, out var r))
    			return null;
    		return r;
    	}
    	public ConfigValue Mandatory(string key)
    	{
    		if(!valueByName.TryGetValue(key, out var r))
    			throw new ConfigNotDefinedException(this, key);
    		return r;
    	}
    	public void Print(TextWriter w)
    	{
    		w.WriteLine($"### {SectionName}");
    		foreach(var x in values)
    		{
    			w.WriteLine($"'{x.Key}' = '{(x.HasText() ? x.AsString() : "null")}'");
    		}
    	}
    }
    class ConfigParser: IReadOnlyList<ConfigSection>
    {
    	public ConfigSection MainSection;
    	List<ConfigSection> sections = new();
    	Dictionary<string, ConfigSection> sectionByName = new();
    	public ConfigParser(string s, int firstLineIndex = 1)
    	{
    		string[] lines = s.Split("\n");
    		var currentSection = MainSection = new(this, null, 0);
    		ConfigValue? lastVal = null;
    		for(int i=0;i<lines.Count();i++)
    		{
    			string l = lines[i];
    			string tl = l.TrimEnd();
    			//Console.WriteLine($"l: '{l}' sl:'{tl}'");
    			if(l.Trim() == "") continue;
    			if(l.Trim()[0] == '#') continue;
    			if(tl[0] == '[' && tl[^1] == ']' && tl.Length >= 3)
    			{
    			#pragma warning disable 8604
    				lastVal = null;
    				currentSection = new ConfigSection(this, tl[1..^1], firstLineIndex + i);
    				if(sectionByName.ContainsKey(currentSection.SectionName))
    				{
    					throw new ConfigDuplicitSectionException(currentSection.SectionName, sectionByName[currentSection.SectionName].Line, firstLineIndex + i);
    				}
    				sections.Add(currentSection);
    				sectionByName[currentSection.SectionName] = currentSection;
    				continue;
    			#pragma warning restore 8604
    			}
    			if(tl[0]==' ' || tl[0]=='\t')
    			{
    				if(lastVal == null)
    					throw new ConfigWrongIndentException(null, firstLineIndex + i);
    				if(lastVal.tabs == null)
    				{
    					int spacesLen=0;
    					for(;tl[spacesLen]==tl[0];spacesLen++);
    					if(tl[spacesLen]==' ' || tl[spacesLen]=='\t')
    						throw new ConfigMixedTabException(lastVal, firstLineIndex + i);
    					lastVal.tabs = tl[0..spacesLen];
    					if(lastVal.Value == null || lastVal.Value == "")
    					{
    						lastVal.Line = firstLineIndex + i;
    						lastVal.Value = tl[lastVal.tabs.Length..];
    					}
    					else
    						lastVal.Value += "\n" + tl[lastVal.tabs.Length..];
    				}
    				else
    				{
    					if(!tl.StartsWith(lastVal.tabs))
    						throw new ConfigWrongIndentException(lastVal, firstLineIndex + i);
    					lastVal.Value += "\n" + tl[lastVal.tabs.Length..];
    				}
    				continue;
    			}
    			int indexIs = l.IndexOf("=");
    			int indexArrow = l.IndexOf("<<");
    			if(indexIs == -1 && indexArrow == -1)
    			{
    				currentSection.Add(lastVal = new ConfigValue(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));
    			if(indexIs != -1 && (indexArrow == -1 || indexIs < indexArrow))
    			{
    				string val = l[(indexIs+1)..].Trim();
    				lastVal.Value = val;
    			}
    			else
    			{
    				lastVal.amen = l[(indexArrow+2)..].Trim();
    				StringBuilder val = new();
    				i++;
    				bool first = true;
    				while(true)
    				{
    					if(i>=lines.Count())
    						throw new ConfigNoAmenException(lastVal);
    					if(lines[i].Trim() == lastVal.amen) break;
    					if(!first) val.Append("\n");
    					val.Append(lines[i]);
    					i++;
    					first = false;
    				}
    				lastVal.Value = val.ToString();
    			}
    		}
    	}
    	public void Print(TextWriter w)
    	{
    		MainSection.Print(w);
    		foreach(var x in sections) x.Print(w);
    	}
    	public IEnumerator<ConfigSection> GetEnumerator()
    	{
    		foreach(var s in sections)
    			yield return s;
    	}
    	System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    		=> GetEnumerator();
    	public int Count{ get{return sections.Count;}}
    	public ConfigSection this[int i]
    	{
    		get { return sections[i]; }
    	}
    	public ConfigSection? this[string? key]
    	{
    		get { return Optional(key); }
    	}
    	public ConfigSection? Optional(string? key)
    	{
    		if(key == null) return MainSection;
    		if(!sectionByName.TryGetValue(key, out var r))
    			return null;
    		return r;
    	}
    	public ConfigSection Mandatory(string? key)
    	{
    		if(key == null) return MainSection;
    		if(!sectionByName.TryGetValue(key, out var r))
    			throw new ConfigSectionNotDefinedException(key);
    		return r;
    	}
    }