Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 2993 was 2859, checked in by epitzer, 14 years ago

fix handling of PublicKeyToken in TypeNameParser (#548)

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