#region License Information /* HeuristicLab * Copyright (C) 2002-2013 Heuristic and Evolutionary Algorithms Laboratory (HEAL) * * This file is part of HeuristicLab. * * HeuristicLab is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * HeuristicLab is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with HeuristicLab. If not, see . */ #endregion using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.RegularExpressions; namespace HeuristicLab.Persistence.Auxiliary { /// /// Error during type name parsing, thrown by . /// public class ParseError : Exception { /// /// Initializes a new instance of the class. /// /// The message. public ParseError(string message) : base(message) { } } /// /// Parse a .NET type name using the following grammar: /// /// /// TypeSpec := SimpleTypeSpec '&'? /// /// SimpleTypeSpec := (IDENTIFIER '.')* /// (IDENTIFIER '`\d+'? '+')* /// IDENTIFIER /// ( '`\d+[' Generics ']' )? /// (\*|\[(\d+\.\.\d+|\d+\.\.\.|(|\*)(,(|\*))*)\])* /// (',\s*' IDENTIFIER (',\s*' AssemblyProperty)* )? /// /// Generics := '[' SimpleTypeSpec ']' (',[' SimpleTypeSpec ']') /// /// AssemblyProperty := 'Version=' Version /// | 'PublicKey(Token)?=[a-fA-F0-9]+' /// | 'Culture=[a-zA-F0-9]+' /// /// Version := \d+\.\d+\.\d+\.\d+ /// /// IDENTIFIER = [_a-zA-Z][_a-ZA-Z0-9]* /// /// public class TypeNameParser { /* TypeSpec := SimpleTypeSpec '&'? SimpleTypeSpec := (IDENTIFIER '.')* (IDENTIFIER '`\d+'? ( '+' IDENTIFIER '`\d+'? )* ( '[' Generics ']' )? (\*|\[(\d+\.\.\d+|\d+\.\.\.|(|\*)(,(|\*))*)\])* (',\s*' IDENTIFIER (',\s*' AssemblyProperty)* )? Generics := '[' SimpleTypeSpec ']' (',[' SimpleTypeSpec ']')* AssemblyProperty := 'Version=' Version | 'PublicKey(Token)?=[a-fA-F0-9]+' | 'Culture=[a-zA-F0-9]+' Version := \d+\.\d+\.\d+\.\d+ IDENTIFIER = [_a-zA-Z][_a-ZA-Z0-9]* */ private class Token { public enum Symbol { None, Dash, Ampersand, Dot, Plus, Comma, OpenBracket, CloseBracket, Asterisk, Space, Equals, Backtick } private static readonly Dictionary TOKENS = new Dictionary { {"-", Symbol.Dash}, {"&", Symbol.Ampersand}, {".", Symbol.Dot}, {"+", Symbol.Plus}, {",", Symbol.Comma}, {"[", Symbol.OpenBracket}, {"]", Symbol.CloseBracket}, {"*", Symbol.Asterisk}, {" ", Symbol.Space}, {"=", Symbol.Equals}, {"`", Symbol.Backtick}}; private static readonly Regex NumberRegex = new Regex("^\\d+$"); private static readonly Regex IdentifierRegex = new Regex("^[_a-zA-Z][_a-zA-Z0-9]*$"); private static readonly Regex TokenRegex = new Regex("[-&.+,\\[\\]* =`]|[a-f0-9]+|\\d+|[_a-zA-Z][_a-zA-Z0-9]*"); public Symbol Name { get; private set; } public string Value { get; private set; } public bool IsIdentifier { get; private set; } public int? Number { get; private set; } public int Position { get; private set; } private Token(string value, int pos) { Position = pos; Name = Symbol.None; if (TOKENS.ContainsKey(value)) { Name = TOKENS[value]; } else if (NumberRegex.IsMatch(value)) { Number = int.Parse(value); } else { Value = value; IsIdentifier = IdentifierRegex.IsMatch(value); } } public static IEnumerable Tokenize(string s) { int pos = 0; foreach (Match m in TokenRegex.Matches(s)) { yield return new Token(m.Value, pos); pos += m.Length; } } public override string ToString() { if (Name != Symbol.None) return "Token(" + Name + ")"; if (Value != null) return "\"" + Value + "\""; if (Number != null) return Number.ToString(); return ""; } } private Queue tokens; private int Position { get { if (tokens.Count == 0) return -1; else return tokens.Peek().Position; } } private TypeNameParser(string s) { tokens = new Queue(Token.Tokenize(s)); } /// /// Parses the specified typename string as obtained by /// System.Object.GetType().FullName or /// System.Object.GetType().AssemblyQualifiedName. /// /// The typename string. /// A representing the type name. public static TypeName Parse(string s) { TypeNameParser p = new TypeNameParser(s); try { return p.TransformTypeSpec(); } catch (ParseError x) { if (p.Position > 0) throw new ParseError(String.Format( "Could not parse typename: {0}\n\"{1}====>{2}<===={3}", x.Message, s.Substring(0, p.Position), s[p.Position], s.Substring(p.Position, s.Length - p.Position))); else throw new ParseError(String.Format( "Could not parse typenname \"{0}\" at end of input: {1}", s, x.Message)); } } private TypeName TransformTypeSpec() { TypeName t = TransformSimpleTypeSpec(); t.IsReference = ConsumeToken(Token.Symbol.Ampersand); return t; } private TypeName TransformSimpleTypeSpec() { var nameSpace = new List { ConsumeIdentifier() }; while (ConsumeToken(Token.Symbol.Dot)) nameSpace.Add(ConsumeIdentifier()); var className = new List(); if (nameSpace.Count > 0) { className.Add(nameSpace[nameSpace.Count - 1]); nameSpace.RemoveAt(nameSpace.Count - 1); } var genericArgCounts = new List { ConsumeToken(Token.Symbol.Backtick) ? ConsumeNumber() : 0 }; while (ConsumeToken(Token.Symbol.Plus)) { className.Add(ConsumeIdentifier()); genericArgCounts.Add(ConsumeToken(Token.Symbol.Backtick) ? ConsumeNumber() : 0); } var nGenericArgs = genericArgCounts.Sum(); var typeName = new TypeName( string.Join(".", nameSpace.ToArray()), string.Join("+", className.ToArray()), nGenericArgs > genericArgCounts.Last() ? genericArgCounts : null); if (nGenericArgs > 0 && ConsumeToken(Token.Symbol.OpenBracket, true)) { typeName.GenericArgs.AddRange(TransformGenerics()); ConsumeToken(Token.Symbol.CloseBracket, true); } var pointerOrArray = new StringBuilder(); while (true) { if (ConsumeToken(Token.Symbol.Asterisk)) { pointerOrArray.Append("*"); } else if (ConsumeToken(Token.Symbol.OpenBracket)) { pointerOrArray.Append('['); ParseDimension(pointerOrArray); while (ConsumeToken(Token.Symbol.Comma)) { pointerOrArray.Append(","); ParseDimension(pointerOrArray); } ConsumeToken(Token.Symbol.CloseBracket, true); pointerOrArray.Append(']'); } else { break; } } typeName.MemoryMagic = pointerOrArray.ToString(); if (ConsumeComma()) { var sb = new StringBuilder(); sb.Append(ConsumeIdentifier()); while (CanConsumeToken(Token.Symbol.Dot) || CanConsumeToken(Token.Symbol.Dash) || CanConsumeNumber() || CanConsumeIdentifier()) { if (ConsumeToken(Token.Symbol.Dot)) sb.Append('.'); else if (ConsumeToken(Token.Symbol.Dash)) sb.Append('-'); else if (CanConsumeNumber()) sb.Append(ConsumeNumber()); else sb.Append(ConsumeIdentifier()); } typeName.AssemblyName = sb.ToString(); while (ConsumeComma()) { KeyValuePair property = TransformAssemblyProperty(); typeName.AssemblyAttribues.Add(property.Key, property.Value); } } return typeName; } private void ParseDimension(StringBuilder sb) { if (ConsumeToken(Token.Symbol.Asterisk)) { sb.Append("*"); } else if (CanConsumeNumber()) { sb.Append(ConsumeNumber()); ConsumeToken(Token.Symbol.Dot, true); ConsumeToken(Token.Symbol.Dot, true); if (ConsumeToken(Token.Symbol.Dot)) { sb.Append("..."); } else { sb.Append("..").Append(ConsumeNumber()); } } } private IEnumerable TransformGenerics() { ConsumeToken(Token.Symbol.OpenBracket, true); yield return TransformSimpleTypeSpec(); ConsumeToken(Token.Symbol.CloseBracket, true); while (ConsumeToken(Token.Symbol.Comma)) { ConsumeToken(Token.Symbol.OpenBracket, true); yield return TransformSimpleTypeSpec(); ConsumeToken(Token.Symbol.CloseBracket, true); } } private KeyValuePair TransformAssemblyProperty() { if (ConsumeIdentifier("Version")) { ConsumeToken(Token.Symbol.Equals, true); return new KeyValuePair( "Version", TransformVersion()); } else if (ConsumeIdentifier("PublicKey")) { return ConsumeAssignment("PublicKey"); } else if (ConsumeIdentifier("PublicKeyToken")) { return ConsumeAssignment("PublicKeyToken"); } else if (ConsumeIdentifier("Culture")) { return ConsumeAssignment("Culture"); } else if (ConsumeIdentifier("Custom")) { return ConsumeAssignment("Custom"); } else { throw new ParseError(String.Format( "Invalid assembly property \"{0}\"", tokens.Peek().ToString())); } } private KeyValuePair ConsumeAssignment(string name) { ConsumeToken(Token.Symbol.Equals, true); return new KeyValuePair(name, ConsumeToken()); } private string TransformVersion() { StringBuilder version = new StringBuilder(); version.Append(ConsumeNumber()); ConsumeToken(Token.Symbol.Dot); version.Append('.').Append(ConsumeNumber()); ConsumeToken(Token.Symbol.Dot); version.Append('.').Append(ConsumeNumber()); ConsumeToken(Token.Symbol.Dot); version.Append('.').Append(ConsumeNumber()); return version.ToString(); } private bool CanConsumeNumber() { if (tokens.Count == 0) return false; return tokens.Peek().Number != null; } private int ConsumeNumber() { if (tokens.Count == 0) throw new ParseError("End of input while expecting number"); if (tokens.Peek().Number != null) return (int)tokens.Dequeue().Number; throw new ParseError(string.Format( "Number expected, found \"{0}\" instead", tokens.Peek().ToString())); } private bool ConsumeIdentifier(string value) { if (tokens.Count == 0) return false; if (tokens.Peek().Value == value && tokens.Peek().IsIdentifier) { tokens.Dequeue(); return true; } else { return false; } } private bool CanConsumeIdentifier() { return tokens.Count > 0 && tokens.Peek().Value != null; } private string ConsumeIdentifier() { if (tokens.Count == 0) throw new ParseError("End of input while expecting identifier"); if (tokens.Peek().Value != null && tokens.Peek().IsIdentifier) return tokens.Dequeue().Value; throw new ParseError(String.Format( "Identifier expected, found \"{0}\" instead", tokens.Peek().ToString())); } private string ConsumeToken() { if (tokens.Count == 0) throw new ParseError("End of input while expecting token"); if (tokens.Peek().Value != null) return tokens.Dequeue().Value; throw new ParseError(String.Format( "Token expected, found \"{0}\" instead", tokens.Peek().ToString())); } private bool ConsumeComma() { if (ConsumeToken(Token.Symbol.Comma)) { while (ConsumeToken(Token.Symbol.Space)) ; return true; } else { return false; } } private bool ConsumeToken(Token.Symbol symbol) { return ConsumeToken(symbol, false); } private bool CanConsumeToken(Token.Symbol symbol) { if (tokens.Count == 0) return false; if (tokens.Peek().Name == symbol) return true; return false; } private bool ConsumeToken(Token.Symbol symbol, bool force) { if (tokens.Count == 0) if (force) throw new ParseError(String.Format( "end of input while expecting token \"{0}\"", symbol)); else return false; if (tokens.Peek().Name == symbol) { tokens.Dequeue(); return true; } else if (force) { throw new ParseError(String.Format( "expected \"{0}\" found \"{1}\"", symbol, tokens.Peek().ToString())); } else { return false; } } } }