#region License Information /* HeuristicLab * Copyright (C) 2002-2012 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.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 '+')* /// 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 '+')* 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 { private static Dictionary tokens = new Dictionary { {"-", "DASH"}, {"&", "AMPERSAND"}, {".", "DOT"}, {"+", "PLUS"}, {",", "COMMA"}, {"[", "OPEN_BRACKET"}, {"]", "CLOSE_BRACKET"}, {"*", "ASTERISK"}, {" ", "SPACE"}, {"=", "EQUALS"}, {"`", "BACKTICK"} }; private static Regex NumberRegex = new Regex("^\\d+$"); private static Regex IdentifierRegex = new Regex("^[_a-zA-Z][_a-zA-Z0-9]*$"); private static Regex TokenRegex = new Regex("[-&.+,\\[\\]* =`]|[a-f0-9]+|\\d+|[_a-zA-Z][_a-zA-Z0-9]*"); public string 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; 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 != null) 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". /// /// 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("AMPERSAND"); return t; } private TypeName TransformSimpleTypeSpec() { List nameSpace = new List(); nameSpace.Add(ConsumeIdentifier()); while (ConsumeToken("DOT")) nameSpace.Add(ConsumeIdentifier()); List className = new List(); if (nameSpace.Count > 0) { className.Add(nameSpace[nameSpace.Count - 1]); nameSpace.RemoveAt(nameSpace.Count - 1); } while (ConsumeToken("PLUS")) className.Add(ConsumeIdentifier()); TypeName typeName = new TypeName( string.Join(".", nameSpace.ToArray()), string.Join("+", className.ToArray())); if (ConsumeToken("BACKTICK")) { int nGenericArgs = ConsumeNumber(); if (ConsumeToken("OPEN_BRACKET") && CanConsumeToken("OPEN_BRACKET")) { typeName.GenericArgs.AddRange(TransformGenerics()); ConsumeToken("CLOSE_BRACKET", true); } } StringBuilder pointerOrArray = new StringBuilder(); while (true) { if (ConsumeToken("ASTERSIK")) { pointerOrArray.Append("*"); } else if (ConsumeToken("OPEN_BRACKET")) { pointerOrArray.Append('['); ParseDimension(pointerOrArray); while (ConsumeToken("COMMA")) { pointerOrArray.Append(","); ParseDimension(pointerOrArray); } ConsumeToken("CLOSE_BRACKET", true); pointerOrArray.Append(']'); } else { break; } } typeName.MemoryMagic = pointerOrArray.ToString(); if (ConsumeComma()) { StringBuilder sb = new StringBuilder(); sb.Append(ConsumeIdentifier()); while (CanConsumeToken("DOT") || CanConsumeToken("DASH") || CanConsumeNumber() || CanConsumeIdentifier()) { if (ConsumeToken("DOT")) sb.Append('.'); else if (ConsumeToken("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("ASTERSIK")) { sb.Append("*"); } else if (CanConsumeNumber()) { sb.Append(ConsumeNumber()); ConsumeToken("DOT", true); ConsumeToken("DOT", true); if (ConsumeToken("DOT")) { sb.Append("..."); } else { sb.Append("..").Append(ConsumeNumber()); } } } private IEnumerable TransformGenerics() { ConsumeToken("OPEN_BRACKET", true); yield return TransformSimpleTypeSpec(); ConsumeToken("CLOSE_BRACKET", true); while (ConsumeToken("COMMA")) { ConsumeToken("OPEN_BRACKET", true); yield return TransformSimpleTypeSpec(); ConsumeToken("CLOSE_BRACKET", true); } } private KeyValuePair TransformAssemblyProperty() { if (ConsumeIdentifier("Version")) { ConsumeToken("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("EQUALS", true); return new KeyValuePair(name, ConsumeToken()); } private string TransformVersion() { StringBuilder version = new StringBuilder(); version.Append(ConsumeNumber()); ConsumeToken("DOT"); version.Append('.').Append(ConsumeNumber()); ConsumeToken("DOT"); version.Append('.').Append(ConsumeNumber()); ConsumeToken("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("COMMA")) { while (ConsumeToken("SPACE")) ; return true; } else { return false; } } private bool ConsumeToken(string name) { return ConsumeToken(name, false); } private bool CanConsumeToken(string name) { if (tokens.Count == 0) return false; if (tokens.Peek().Name == name) return true; return false; } private bool ConsumeToken(string name, bool force) { if (tokens.Count == 0) if (force) throw new ParseError(String.Format( "end of input while expecting token \"{0}\"", name)); else return false; if (tokens.Peek().Name == name) { tokens.Dequeue(); return true; } else if (force) { throw new ParseError(String.Format( "expected \"{0}\" found \"{1}\"", name, tokens.Peek().ToString())); } else { return false; } } } }