Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.Persistence/3.3/Auxiliary/TypeNameParser.cs @ 1795

Last change on this file since 1795 was 1795, checked in by epitzer, 15 years ago

Also make sure major and minor version match (not only newer) + better tests. (#613)

File size: 10.6 KB
Line 
1using System;
2using System.Text;
3using System.Text.RegularExpressions;
4using System.Reflection.Emit;
5using System.Collections.Generic;
6
7namespace HeuristicLab.Persistence.Auxiliary {
8
9  public class ParseError : Exception {
10    public ParseError(string message) : base(message) { }
11  }
12
13  public class TypeNameParser {
14
15    /*
16      TypeSpec := SimpleTypeSpec '&'?
17
18      SimpleTypeSpec := (IDENTIFIER '.')*
19                        (IDENTIFIER '+')*
20                         IDENTIFIER
21                        ( '`\d+[' Generics ']' )?
22                        (\*|\[(\d+\.\.\d+|\d+\.\.\.|(|\*)(,(|\*))*)\])*
23                        (',\s*' IDENTIFIER (',\s*' AssemblyProperty)* )?
24
25      Generics := '[' SimpleTypeSpec ']' (',[' SimpleTypeSpec ']')
26
27      AssemblyProperty := 'Version=' Version
28                 |  'PublicKey(Token)?=[a-fA-F0-9]+'
29                 |  'Culture=[a-zA-F0-9]+'
30
31      Version := '\d+\.\d+\.\d+\.\d+)'
32
33      IDENTIFIER = [a-zA-Z][a-ZA-Z0-9]*
34    */
35
36
37    class Token {
38      private static Dictionary<string, string> tokens =
39        new Dictionary<string, string> {
40          {"&", "AMPERSAND"},
41          {".", "DOT"},
42          {"-", "DASH"},
43          {"+", "PLUS"},
44          {",", "COMMA"},
45          {"[", "OPEN_BRACKET"},
46          {"]", "CLOSE_BRACKET"},
47          {"*", "ASTERISK"},
48          {" ", "SPACE"},
49          {"=", "EQUALS"},
50          {"`", "BACKTICK"} };
51      private static Regex NumberRegex = new Regex("^\\d+$");
52      private static Regex TokenRegex = new Regex("[-&.+,\\[\\]* =`]|\\d+|[a-zA-Z][a-zA-Z0-9]*");
53      public string Name { get; private set; }
54      public string Value { get; private set; }
55      public int? Number { get; private set; }
56      public int Position { get; private set; }
57      private Token(string value, int pos) {
58        Position = pos;
59        if (tokens.ContainsKey(value)) {
60          Name = tokens[value];
61        } else if (NumberRegex.IsMatch(value)) {
62          Number = int.Parse(value);
63        } else {
64          Value = value;
65        }
66      }
67      public static IEnumerable<Token> Tokenize(string s) {
68        int pos = 0;
69        foreach (Match m in TokenRegex.Matches(s)) {
70          yield return new Token(m.Value, pos);
71          pos += m.Length;
72        }
73      }
74      public override string ToString() {
75        if (Name != null)
76          return "Token(" + Name + ")";
77        if (Value != null)
78          return "\"" + Value + "\"";
79        if (Number != null)
80          return Number.ToString();
81        return "<empty>";
82      }
83    }
84
85    private Queue<Token> tokens;
86    private int Position {
87      get {
88        if (tokens.Count == 0)
89          return -1;
90        else
91          return tokens.Peek().Position;
92      }
93    }
94
95    private TypeNameParser(string s) {
96      tokens = new Queue<Token>(Token.Tokenize(s));
97    }
98
99    public static TypeName Parse(string s) {
100      TypeNameParser p = new TypeNameParser(s);
101      try {
102        return p.TransformTypeSpec();
103      } catch (ParseError x) {
104        if (p.Position > 0)
105          throw new ParseError(String.Format(
106            "Could not parse typename: {0}\n\"{1}====>{2}<===={3}",
107            x.Message,
108            s.Substring(0, p.Position),
109            s[p.Position],
110            s.Substring(p.Position, s.Length - p.Position)));
111        else
112          throw new ParseError(String.Format(
113            "Could not parse typenname \"{0}\" at end of input: {1}",
114            s,
115            x.Message));
116      }
117    }
118
119    private TypeName TransformTypeSpec() {
120      TypeName t = TransformSimpleTypeSpec();
121      t.IsReference = ConsumeToken("AMPERSAND");
122      return t;
123    }
124
125    private TypeName TransformSimpleTypeSpec() {
126      List<string> nameSpace = new List<string>();
127      nameSpace.Add(ConsumeIdentifier());
128      while (ConsumeToken("DOT"))
129        nameSpace.Add(ConsumeIdentifier());
130      List<string> className = new List<string>();
131      if (nameSpace.Count > 0) {
132        className.Add(nameSpace[nameSpace.Count - 1]);
133        nameSpace.RemoveAt(nameSpace.Count - 1);
134      }
135      while (ConsumeToken("PLUS"))
136        className.Add(ConsumeIdentifier());
137      TypeName typeName = new TypeName(
138        string.Join(".", nameSpace.ToArray()),
139        string.Join("+", className.ToArray()));
140      if (ConsumeToken("BACKTICK")) {
141        int nGenericArgs = ConsumeNumber();
142        if (ConsumeToken("OPEN_BRACKET") &&
143          CanConsumeToken("OPEN_BRACKET")) {
144          typeName.GenericArgs.AddRange(TransformGenerics());
145          ConsumeToken("CLOSE_BRACKET", true);
146        }
147      }
148      StringBuilder pointerOrArray = new StringBuilder();
149      while (true) {
150        if (ConsumeToken("ASTERSIK")) {
151          pointerOrArray.Append("*");
152        } else if (ConsumeToken("OPEN_BRACKET")) {
153          pointerOrArray.Append('[');
154          ParseDimension(pointerOrArray);
155          while (ConsumeToken("COMMA")) {
156            pointerOrArray.Append(",");
157            ParseDimension(pointerOrArray);
158          }
159          ConsumeToken("CLOSE_BRACKET", true);
160          pointerOrArray.Append(']');
161        } else {
162          break;
163        }
164      }
165      typeName.MemoryMagic = pointerOrArray.ToString();
166      if (ConsumeComma()) {
167        StringBuilder sb = new StringBuilder();
168        sb.Append(ConsumeIdentifier());
169        while (CanConsumeToken("DOT") ||
170          CanConsumeToken("DASH") ||
171          CanConsumeNumber() ||
172          CanConsumeIdentifier()) {
173          if (ConsumeToken("DOT"))
174            sb.Append('.');
175          else if (ConsumeToken("DASH"))
176            sb.Append('-');
177          else if (CanConsumeNumber())
178            sb.Append(ConsumeNumber());
179          else
180            sb.Append(ConsumeIdentifier());
181        }
182        typeName.AssemblyName = sb.ToString();
183        while (ConsumeComma()) {
184          KeyValuePair<string, string> property =
185            TransformAssemblyProperty();
186          typeName.AssemblyAttribues.Add(property.Key, property.Value);
187        }
188      }
189      return typeName;
190    }
191
192    private void ParseDimension(StringBuilder sb) {
193      if (ConsumeToken("ASTERSIK")) {
194        sb.Append("*");
195      } else if (CanConsumeNumber()) {
196        sb.Append(ConsumeNumber());
197        ConsumeToken("DOT", true);
198        ConsumeToken("DOT", true);
199        if (ConsumeToken("DOT")) {
200          sb.Append("...");
201        } else {
202          sb.Append("..").Append(ConsumeNumber());
203        }
204      }
205    }
206
207    private IEnumerable<TypeName> TransformGenerics() {
208      ConsumeToken("OPEN_BRACKET", true);
209      yield return TransformSimpleTypeSpec();
210      ConsumeToken("CLOSE_BRACKET", true);
211      while (ConsumeToken("COMMA")) {
212        ConsumeToken("OPEN_BRACKET", true);
213        yield return TransformSimpleTypeSpec();
214        ConsumeToken("CLOSE_BRACKET", true);
215      }
216    }
217
218    private KeyValuePair<string, string> TransformAssemblyProperty() {
219      if (ConsumeIdentifier("Version")) {
220        ConsumeToken("EQUALS", true);
221        return new KeyValuePair<string, string>(
222          "Version",
223          TransformVersion());
224      } else if (ConsumeIdentifier("PublicKey")) {
225        return ConsumeAssignment("PublicKey");
226      } else if (ConsumeIdentifier("PublicKeyToken")) {
227        return ConsumeAssignment("PublicKeyToken");
228      } else if (ConsumeIdentifier("Culture")) {
229        return ConsumeAssignment("Culture");
230      } else if (ConsumeIdentifier("Custom")) {
231        return ConsumeAssignment("Custom");
232      } else {
233        throw new ParseError(String.Format(
234          "Invalid assembly property \"{0}\"",
235          tokens.Peek().ToString()));
236      }
237    }
238
239    private KeyValuePair<string, string> ConsumeAssignment(string name) {
240      ConsumeToken("EQUALS", true);
241      return new KeyValuePair<string, string>(name, ConsumeIdentifier());
242    }
243
244    private string TransformVersion() {
245      StringBuilder version = new StringBuilder();
246      version.Append(ConsumeNumber());
247      ConsumeToken("DOT");
248      version.Append('.').Append(ConsumeNumber());
249      ConsumeToken("DOT");
250      version.Append('.').Append(ConsumeNumber());
251      ConsumeToken("DOT");
252      version.Append('.').Append(ConsumeNumber());
253      return version.ToString();
254    }
255
256    private bool CanConsumeNumber() {
257      if (tokens.Count == 0)
258        return false;
259      return tokens.Peek().Number != null;
260    }
261
262    private int ConsumeNumber() {
263      if (tokens.Count == 0)
264        throw new ParseError("End of input while expecting number");
265      if (tokens.Peek().Number != null)
266        return (int)tokens.Dequeue().Number;
267      throw new ParseError(string.Format(
268        "Number expected, found \"{0}\" instead",
269        tokens.Peek().ToString()));
270    }
271
272    private bool ConsumeIdentifier(string value) {
273      if (tokens.Count == 0)
274        return false;
275      if (tokens.Peek().Value == value) {
276        tokens.Dequeue();
277        return true;
278      } else {
279        return false;
280      }
281    }
282
283    private bool CanConsumeIdentifier() {
284      return tokens.Count > 0 && tokens.Peek().Value != null;
285    }
286
287    private string ConsumeIdentifier() {
288      if (tokens.Count == 0)
289        throw new ParseError("End of input while expecting identifier");
290      if (tokens.Peek().Value != null)
291        return tokens.Dequeue().Value;
292      throw new ParseError(String.Format(
293        "Identifier expected, found \"{0}\" instead",
294        tokens.Peek().ToString()));
295    }
296
297    private bool ConsumeComma() {
298      if (ConsumeToken("COMMA")) {
299        while (ConsumeToken("SPACE")) ;
300        return true;
301      } else {
302        return false;
303      }
304    }
305
306    private bool ConsumeToken(string name) {
307      return ConsumeToken(name, false);
308    }
309
310    private bool CanConsumeToken(string name) {
311      if (tokens.Count == 0)
312        return false;
313      if (tokens.Peek().Name == name)
314        return true;
315      return false;
316    }
317
318    private bool ConsumeToken(string name, bool force) {
319      if (tokens.Count == 0)
320        if (force)
321          throw new ParseError(String.Format(
322            "end of input while expecting token \"{0}\"",
323            name));
324        else
325          return false;
326      if (tokens.Peek().Name == name) {
327        tokens.Dequeue();
328        return true;
329      } else if (force) {
330        throw new ParseError(String.Format(
331          "expected \"{0}\" found \"{1}\"",
332          name,
333          tokens.Peek().ToString()));
334      } else {
335        return false;
336      }
337    }
338  }
339}
Note: See TracBrowser for help on using the repository browser.