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 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(); 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))}"); } } }