view Implab.ServiceHost/Unity/TypeReferenceParser.cs @ 272:9d1cca834b05 v3

preview version of Unity xml configuration
author cin
date Thu, 26 Apr 2018 03:14:54 +0300
parents ff581cff7003
children 963b17c275be
line wrap: on
line source

using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;

namespace Implab.ServiceHost.Unity {
    internal class TypeReferenceParser {
        enum TokenType {
            None,

            Word,

            Dot,

            Comma,

            OpenList,

            CloseList,

            Eof
        }

        readonly Regex _tokens = new Regex(@"([\w\+]+)|\s*([\.{},])\s*");

        TokenType m_token;

        string m_tokenValue;

        int m_pos;

        int m_tokenPos;

        readonly string m_text;

        TokenType Token { get { return m_token; } }

        string TokenValue { get { return m_tokenValue; } }

        int TokenPos { get { return m_tokenPos; } }

        public TypeReferenceParser(string text) {
            Safe.ArgumentNotEmpty(text, nameof(text));
            m_text = text;
        }

        bool ReadToken() {
            if (m_pos >= m_text.Length) {
                m_token = TokenType.Eof;
                m_tokenValue = null;
                return false;
            }

            var m = _tokens.Match(m_text, m_pos);

            if (m.Success) {
                m_tokenPos = m_pos;
                m_pos += m.Length;
                if (m.Groups[1].Success) {
                    m_token = TokenType.Word;
                    m_tokenValue = m.Groups[1].Value;
                } else if (m.Groups[2].Success) {
                    m_tokenValue = null;
                    switch (m.Groups[2].Value) {
                        case "{":
                            m_token = TokenType.OpenList;
                            break;
                        case "}":
                            m_token = TokenType.CloseList;
                            break;
                        case ".":
                            m_token = TokenType.Dot;
                            break;
                        case ",":
                            m_token = TokenType.Comma;
                            break;
                    }
                }
                return true;
            }
            throw new FormatException($"Failed to parse '{m_text}' at pos {m_pos}");
        }

        public TypeReference Parse() {
            var result = ReadTypeReference();
            if (ReadToken())
                ThrowUnexpectedToken();
            return result;
        }

        string[] ReadTypeName() {
            var parts = new List<string>();

            string current = null;
            bool stop = false;
            while ((!stop) && ReadToken()) {
                switch (Token) {
                    case TokenType.Word:
                        if (current != null)
                            ThrowUnexpectedToken();
                        current = TokenValue;
                        break;
                    case TokenType.Dot:
                        if (current == null)
                            ThrowUnexpectedToken();
                        parts.Add(current);
                        current = null;
                        break;
                    default:
                        stop = true;
                        break;
                }
            }
            if (current != null)
                parts.Add(current);

            if (parts.Count == 0)
                return null;

            return parts.ToArray();
        }

        TypeReference ReadTypeReference() {

            var parts = ReadTypeName();
            if (parts == null)
                return null;

            var typeReference = new TypeReference {
                Namespace = string.Join(".", parts, 0, parts.Length - 1),
                TypeName = parts[parts.Length - 1]
            };

            switch (Token) {
                case TokenType.OpenList:
                    typeReference.GenericParameters = ReadTypeReferenceList();
                    if (Token != TokenType.CloseList)
                        ThrowUnexpectedToken();
                    ReadToken();
                    break;
            }

            return typeReference;
        }

        TypeReference[] ReadTypeReferenceList() {
            var list = new List<TypeReference>();

            do {
                var typeReference = ReadTypeReference();
                list.Add(typeReference);
            } while (Token == TokenType.Comma);

            return list.ToArray();
        }

        void ThrowUnexpectedToken() {
            throw new FormatException($"Unexpected '{Token}' at pos {TokenPos}: -->{m_text.Substring(TokenPos, Math.Min(m_text.Length - TokenPos, 10))}");
        }

    }
}