using System.Collections.Generic;
public static void Main()
GraphObject.Read(str).Dump();
public enum GraphElementType {
public abstract class GraphElement
public static readonly string ROOT_NAME = "<ROOT>";
public GraphElementType ElementType { get; }
public string Name {get;}
protected GraphElement(GraphElementType type, string name) {
public class GraphValue : GraphElement
public GraphValue(string name) : base(GraphElementType.Value, name) { }
public class GraphObject : GraphElement {
public List<GraphElement> Properties {get;} = new List<GraphElement>();
public GraphObject(string name) : base(GraphElementType.Object, name) { }
public static GraphObject Read(string graphString)
return Read(new StringReader(graphString));
public static GraphObject Read(TextReader reader) {
return Read(GraphElement.ROOT_NAME, reader);
protected static GraphObject Read(string name, TextReader reader)
if (reader.Read() != '{') throw new ApplicationException("Invalid object start");
var o = new GraphObject(name);
var next = ConsumeWhitespaceAndPeek(reader);
if (next == -1) throw new ApplicationException("Unexpected end of input, unclosed object");
var str = ConsumeUntilWhitespaceOrBrace(reader);
next = ConsumeWhitespaceAndPeek(reader);
if (str.Length > 0 && next == '{')
o.Properties.Add(Read(str, reader));
o.Properties.Add(new GraphValue(str));
private static string ConsumeUntilWhitespaceOrBrace(TextReader reader) {
var b = new StringBuilder();
var next = reader.Peek();
while (next != -1 && !Char.IsWhiteSpace((char)next) && next != '}' && next != '{') {
private static int ConsumeWhitespaceAndPeek(TextReader reader) {
var next = reader.Peek();
while(next != -1 && Char.IsWhiteSpace((char)next)) {