public List <TemplateSection> Parse(string text)
        {
            this.text = text;
            var stack = new Stack <ParserStackEntry>();

            while (!IsEOF())
            {
                if (LookingAt("{{"))
                {
                    CaptureLiteralText();

                    if (LookingAt("{{#"))
                    {
                        position += 3;
                        string key = ExtractKey();

                        stack.Push(new ParserStackEntry {
                            key = key, sections = sections
                        });
                        sections = new List <TemplateSection>();
                    }
                    else if (LookingAt("{{/"))
                    {
                        position += 3;
                        string key = ExtractKey();

                        if (stack.Count == 0)
                        {
                            throw Error($"Found a close for '{key}', but no section is open.");
                        }
                        ParserStackEntry top = stack.Pop();
                        if (top.key != key)
                        {
                            throw Error($"Mismatched close; expected a close for '{top.key}' but got '{key}'.");
                        }
                        BlockTemplateSection section = new BlockTemplateSection(key, sections);
                        sections = top.sections;
                        sections.Add(section);
                    }
                    else
                    {
                        position += 2;
                        string key = ExtractKey();

                        sections.Add(new VariableReferenceTemplateSection(key));
                    }

                    literalStart = position;
                }
                else
                {
                    position++;
                }
            }

            CaptureLiteralText();
            if (stack.Count > 0)
            {
                throw Error($"Unexpected end of template; did not find a close for section '{stack.Peek().key}'.");
            }

            return(sections);
        }
        static List <TemplateSection> ParseTemplate(string text)
        {
            int position     = 0;
            int literalStart = 0;
            Stack <ParserStackEntry> stack = new Stack <ParserStackEntry>();

            var sections = new List <TemplateSection>();

            while (!IsEOF())
            {
                if (LookingAt("{{"))
                {
                    CaptureLiteralText();

                    if (LookingAt("{{#"))
                    {
                        position += 3;
                        string key = ExtractKey();

                        stack.Push(new ParserStackEntry {
                            key = key, sections = sections
                        });
                        sections = new List <TemplateSection>();
                    }
                    else if (LookingAt("{{/"))
                    {
                        position += 3;
                        string key = ExtractKey();

                        if (stack.Count == 0)
                        {
                            throw Error($"Found a close for '{key}', but no section is open.");
                        }
                        ParserStackEntry top = stack.Pop();
                        if (top.key != key)
                        {
                            throw Error($"Mismatched close; expected a close for '{top.key}' but got '{key}'.");
                        }
                        NestedSection section = new NestedSection(key, sections);
                        sections = top.sections;
                        sections.Add(section);
                    }
                    else
                    {
                        position += 2;
                        string key = ExtractKey();

                        sections.Add(new VariableSection(key));
                    }

                    literalStart = position;
                }
                else
                {
                    position++;
                }
            }

            CaptureLiteralText();
            if (stack.Count > 0)
            {
                throw Error($"Unexpected end of template; did not find a close for section '{stack.Peek().key}'.");
            }

            return(sections);

            void CaptureLiteralText()
            {
                if (position != literalStart)
                {
                    string literal = text.Substring(literalStart, position - literalStart);
                    sections.Add(new LiteralSection(literal));
                }
            }

            bool IsEOF()
            {
                return(position >= text.Length);
            }

            Exception Error(string v)
            {
                throw new InvalidDataException($"{v} (at position {position}");
            }

            string ExtractKey()
            {
                if (IsEOF())
                {
                    throw Error("Unexpected end of template; did not find '}}'");
                }
                int end = text.IndexOf("}}", position);

                if (end < 0)
                {
                    throw Error("Unexpected end of template; did not find '}}'");
                }
                string key = text.Substring(position, end - position);

                position = end + 2;

                return(key);
            }

            bool LookingAt(string token)
            {
                int i = position;
                int j = 0;

                while (i < text.Length)
                {
                    if (j == token.Length)
                    {
                        return(true);
                    }
                    if (text[i] != token[j])
                    {
                        break;
                    }
                    i++; j++;
                }
                return(false);
            }
        }