TypeReferenceParser.cs
244 lines
| 7.1 KiB
| text/x-csharp
|
CSharpLexer
cin
|
r267 | using System; | |
using System.Collections.Generic; | |||
cin
|
r278 | using System.Linq; | |
cin
|
r267 | using System.Text.RegularExpressions; | |
namespace Implab.ServiceHost.Unity { | |||
cin
|
r268 | internal class TypeReferenceParser { | |
cin
|
r267 | enum TokenType { | |
None, | |||
Word, | |||
Dot, | |||
Comma, | |||
OpenList, | |||
CloseList, | |||
cin
|
r277 | OpenArray, | |
CloseArray, | |||
cin
|
r278 | Plus, | |
cin
|
r267 | Eof | |
} | |||
cin
|
r278 | readonly Regex _tokens = new Regex(@"\G(?:([\w]+)|\s*([\+\.{},\[\]])\s*)", RegexOptions.Compiled); | |
cin
|
r267 | ||
TokenType m_token; | |||
string m_tokenValue; | |||
int m_pos; | |||
cin
|
r268 | int m_tokenPos; | |
cin
|
r267 | readonly string m_text; | |
TokenType Token { get { return m_token; } } | |||
string TokenValue { get { return m_tokenValue; } } | |||
cin
|
r268 | int TokenPos { get { return m_tokenPos; } } | |
cin
|
r267 | 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) { | |||
cin
|
r268 | m_tokenPos = m_pos; | |
cin
|
r267 | 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; | |||
cin
|
r277 | case "[": | |
m_token = TokenType.OpenArray; | |||
break; | |||
case "]": | |||
m_token = TokenType.CloseArray; | |||
break; | |||
cin
|
r278 | case "+": | |
m_token = TokenType.Plus; | |||
break; | |||
cin
|
r267 | } | |
} | |||
return true; | |||
} | |||
throw new FormatException($"Failed to parse '{m_text}' at pos {m_pos}"); | |||
} | |||
cin
|
r268 | public TypeReference Parse() { | |
var result = ReadTypeReference(); | |||
cin
|
r277 | if (Token != TokenType.Eof) | |
cin
|
r268 | ThrowUnexpectedToken(); | |
return result; | |||
cin
|
r267 | } | |
cin
|
r278 | string[] ReadQTypeName() { | |
cin
|
r267 | 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(); | |||
} | |||
cin
|
r278 | string ReadNQTypeName() { | |
ReadToken(); | |||
if (Token != TokenType.Word) | |||
ThrowUnexpectedToken(); | |||
return TokenValue; | |||
} | |||
cin
|
r267 | TypeReference ReadTypeReference() { | |
cin
|
r278 | var parts = ReadQTypeName(); | |
cin
|
r267 | if (parts == null) | |
return null; | |||
cin
|
r278 | var genericParameters = ReadGenericParams(); | |
var typeReference = TypeReference.Create( | |||
string.Join(".", parts, 0, parts.Length - 1), | |||
parts[parts.Length - 1], | |||
genericParameters.Length | |||
); | |||
if (genericParameters.Length > 0 && genericParameters.All(x => x != null)) | |||
typeReference = typeReference.MakeGenericType(genericParameters); | |||
typeReference = ReadArraySpec(typeReference); | |||
if(Token == TokenType.Plus) | |||
return ReadNestedType(typeReference); | |||
return typeReference; | |||
} | |||
TypeReference ReadNestedType(TypeReference declaringType) { | |||
var name = ReadNQTypeName(); | |||
if(string.IsNullOrEmpty(name)) | |||
throw new FormatException("Nested type name can't be empty"); | |||
ReadToken(); | |||
var genericParameters = ReadGenericParams(); | |||
var typeReference = declaringType.Create( | |||
name, | |||
genericParameters.Length | |||
); | |||
cin
|
r267 | ||
cin
|
r278 | if (genericParameters.Length > 0 && genericParameters.All(x => x != null)) | |
typeReference = typeReference.MakeGenericType(genericParameters); | |||
typeReference = ReadArraySpec(typeReference); | |||
if(Token == TokenType.Plus) | |||
return ReadNestedType(typeReference); | |||
return typeReference; | |||
} | |||
TypeReference[] ReadGenericParams() { | |||
if (Token == TokenType.OpenList) { | |||
var genericParameters = ReadTypeReferenceList(); | |||
if (Token != TokenType.CloseList) | |||
ThrowUnexpectedToken(); | |||
ReadToken(); | |||
return genericParameters; | |||
} | |||
return Array.Empty<TypeReference>(); | |||
} | |||
TypeReference ReadArraySpec(TypeReference typeReference) { | |||
while (Token == TokenType.OpenArray) { | |||
var rank = CountRank(); | |||
if (Token != TokenType.CloseArray) | |||
ThrowUnexpectedToken(); | |||
typeReference = typeReference.MakeArrayType(rank); | |||
ReadToken(); | |||
cin
|
r267 | } | |
return typeReference; | |||
} | |||
cin
|
r278 | int CountRank() { | |
int rank = 0; | |||
do { | |||
rank++; | |||
} while(ReadToken() && Token == TokenType.Comma); | |||
return rank; | |||
cin
|
r277 | } | |
cin
|
r267 | TypeReference[] ReadTypeReferenceList() { | |
cin
|
r268 | var list = new List<TypeReference>(); | |
cin
|
r267 | ||
cin
|
r268 | do { | |
var typeReference = ReadTypeReference(); | |||
list.Add(typeReference); | |||
} while (Token == TokenType.Comma); | |||
return list.ToArray(); | |||
cin
|
r267 | } | |
void ThrowUnexpectedToken() { | |||
cin
|
r268 | throw new FormatException($"Unexpected '{Token}' at pos {TokenPos}: -->{m_text.Substring(TokenPos, Math.Min(m_text.Length - TokenPos, 10))}"); | |
cin
|
r267 | } | |
} | |||
} |