Forgot this in changelog
[mono.git] / mcs / class / Microsoft.Build.Engine / Microsoft.Build.BuildEngine / ConditionTokenizer.cs
1 //
2 // ConditionTokenizer.cs
3 //
4 // Author:
5 //   Marek Sieradzki (marek.sieradzki@gmail.com)
6 //   Jaroslaw Kowalski <jaak@jkowalski.net>
7 // 
8 // (C) 2006 Marek Sieradzki
9 // (C) 2004-2006 Jaroslaw Kowalski
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 //
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 //
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29
30 #if NET_2_0
31
32 using System;
33 using System.Collections;
34 using System.Collections.Generic;
35 using System.Collections.Specialized;
36 using System.Text;
37
38 namespace Microsoft.Build.BuildEngine {
39
40         internal sealed class ConditionTokenizer {
41         
42                 string  inputString = null;
43                 int     position = 0;
44                 int     tokenPosition = 0;
45                 
46                 Token   token;
47                 
48                 bool    ignoreWhiteSpace = true;
49                 
50                 static TokenType[] charIndexToTokenType = new TokenType[128];
51                 static Dictionary <string, TokenType> keywords = new Dictionary <string, TokenType> (StringComparer.InvariantCultureIgnoreCase);
52
53                 static ConditionTokenizer ()
54                 {
55                         for (int i = 0; i < 128; i++)
56                                 charIndexToTokenType [i] = TokenType.Invalid;
57                         
58                         foreach (CharToTokenType cht in charToTokenType)
59                                 charIndexToTokenType [(int) cht.ch] = cht.tokenType;
60                         
61                         keywords.Add ("and", TokenType.And);
62                         keywords.Add ("or", TokenType.Or);
63                 }
64                 
65                 public ConditionTokenizer ()
66                 {
67                         this.ignoreWhiteSpace = true;
68                 }
69                 
70                 public void Tokenize (string s)
71                 {
72                         if (s == null)
73                                 throw new ArgumentNullException ("s");
74                 
75                         this.inputString = s;
76                         this.position = 0;
77                         this.token = new Token (null, TokenType.BOF);
78
79                         GetNextToken ();
80                 }
81                 
82                 private void SkipWhiteSpace ()
83                 {
84                         int ch;
85                         
86                         while ((ch = PeekChar ()) != -1) {
87                                 if (!Char.IsWhiteSpace ((char)ch))
88                                         break;
89                                 ReadChar ();
90                         }
91                 }
92                 
93                 private int PeekChar ()
94                 {
95                         if (position < inputString.Length)
96                                 return (int) inputString [position];
97                         else
98                                 return -1;
99                 }
100                 
101                 private int ReadChar ()
102                 {
103                         if (position < inputString.Length)
104                                 return (int) inputString [position++];
105                         else
106                                 return -1;
107                 }
108                 
109                 public void Expect (TokenType type)
110                 {
111                         if (token.Type != type)
112                                 throw new ExpressionParseException ("Expected token type of type: " + type + ", got " + token.Type +
113                                         " (" + token.Value + ") .");
114                         
115                         GetNextToken ();
116                 }
117                 
118                 public bool IsEOF ()
119                 {
120                         return token.Type == TokenType.EOF;
121                 }
122                 
123                 public bool IsNumber ()
124                 {
125                         return token.Type == TokenType.Number;
126                 }
127                 
128                 public bool IsToken (TokenType type)
129                 {
130                         return token.Type == type;
131                 }
132                 
133                 public bool IsPunctation ()
134                 {
135                         return (token.Type >= TokenType.FirstPunct && token.Type < TokenType.LastPunct);
136                 }
137                 
138                 // FIXME: add 'and' and 'or' tokens
139                 public void GetNextToken ()
140                 {
141                         if (token.Type == TokenType.EOF)
142                                 throw new ExpressionParseException ("Cannot read past the end of stream.");
143                         
144                         if (IgnoreWhiteSpace)
145                                 SkipWhiteSpace ();
146                         
147                         tokenPosition = position;
148                         
149                         int i = PeekChar ();
150                         
151                         if (i == -1) {
152                                 token = new Token (null, TokenType.EOF);
153                                 return;
154                         }
155                         
156                         char ch = (char) i;
157                         
158                         if (IgnoreWhiteSpace == false && Char.IsWhiteSpace (ch)) {
159                                 StringBuilder sb = new StringBuilder ();
160                                 int ch2;
161
162                                 while ((ch2 = PeekChar ()) != -1)  {
163                                         if (!Char.IsWhiteSpace ((char) ch2))
164                                                 break;
165
166                                         sb.Append ((char)ch2);
167                                         ReadChar();
168                                 }
169                                 
170                                 token = new Token (sb.ToString (), TokenType.WhiteSpace);
171                                 return;
172                         }
173                         
174                         // FIXME: looks like a hack: if '-' is here '->' won't be tokenized
175                         // maybe we should treat item reference as a token
176                         if (Char.IsDigit (ch) || ch == '-') {
177                                 StringBuilder sb = new StringBuilder ();
178                                 
179                                 sb.Append (ch);
180                                 ReadChar ();
181                                 
182                                 while ((i = PeekChar ()) != -1) {
183                                         ch = (char) i;
184                                         
185                                         if (Char.IsDigit (ch) || ch == '.')
186                                                 sb.Append ((char) ReadChar ());
187                                         else
188                                                 break;
189                                 }
190                                 
191                                 token = new Token (sb.ToString (), TokenType.Number);
192                                 return;
193                         }
194                         
195                         if (ch == '\'') {
196                                 StringBuilder sb = new StringBuilder ();
197                                 string temp;
198                                 
199                                 sb.Append (ch);
200                                 ReadChar ();
201                                 
202                                 while ((i = PeekChar ()) != -1) {
203                                         ch = (char) i;
204                                         
205                                         sb.Append ((char) ReadChar ());
206                                         
207                                         if (ch == '\'')
208                                                 break;
209                                 }
210                                 
211                                 temp = sb.ToString ();
212                                 
213                                 // FIXME: test extreme cases
214                                 // it fails on '$(something) == ''
215                                 token = new Token (temp.Substring (1, temp.Length - 2), TokenType.String);
216                                 
217                                 return;
218                         }
219                         
220                         if (ch == '_' || Char.IsLetter (ch)) {
221                                 StringBuilder sb = new StringBuilder ();
222                                 
223                                 sb.Append ((char) ch);
224                                 ReadChar ();
225                                 
226                                 while ((i = PeekChar ()) != -1) {
227                                         if ((char) i == '_' || Char.IsLetterOrDigit ((char) i))
228                                                 sb.Append ((char) ReadChar ());
229                                         else
230                                                 break;
231                                 }
232                                 
233                                 string temp = sb.ToString ();
234                                 
235                                 if (keywords.ContainsKey (temp))
236                                         token = new Token (temp, keywords [temp]);
237                                 else
238                                         token = new Token (temp, TokenType.String);
239                                         
240                                 return;
241                         }
242                         
243                         ReadChar ();
244                         
245                         if (ch == '!' && PeekChar () == (int) '=') {
246                                 token = new Token ("!=", TokenType.NotEqual);
247                                 ReadChar ();
248                                 return;
249                         }
250                         
251                         if (ch == '<' && PeekChar () == (int) '=') {
252                                 token = new Token ("<=", TokenType.LessOrEqual);
253                                 ReadChar ();
254                                 return;
255                         }
256                         
257                         if (ch == '>' && PeekChar () == (int) '=') {
258                                 token = new Token (">=", TokenType.GreaterOrEqual);
259                                 ReadChar ();
260                                 return;
261                         }
262                         
263                         if (ch == '=' && PeekChar () == (int) '=') {
264                                 token = new Token ("==", TokenType.Equal);
265                                 ReadChar ();
266                                 return;
267                         }
268                         
269                         if (ch == '-' && PeekChar () == (int) '>') {
270                                 token = new Token ("->", TokenType.Transform);
271                                 ReadChar ();
272                                 return;
273                         }
274                         
275                         if (ch >= 32 && ch < 128) {
276                                 if (charIndexToTokenType [ch] != TokenType.Invalid) {
277                                         token = new Token (new String (ch, 1), charIndexToTokenType [ch]);
278                                         return;
279                                 } else
280                                         throw new ExpressionParseException (String.Format ("Invalid punctuation: {0}", ch));
281                         }
282                         
283                         throw new ExpressionParseException (String.Format ("Invalid token: {0}", ch));
284                 }
285                 
286                 public int TokenPosition {
287                         get { return tokenPosition; }
288                 }
289                 
290                 public Token Token {
291                         get { return token; }
292                 }
293                 
294                 public bool IgnoreWhiteSpace {
295                         get { return ignoreWhiteSpace; }
296                         set { ignoreWhiteSpace = value; }
297                 }
298                 
299                 struct CharToTokenType {
300                         public char ch;
301                         public TokenType tokenType;
302                         
303                         public CharToTokenType (char ch, TokenType tokenType)
304                         {
305                                 this.ch = ch;
306                                 this.tokenType = tokenType;
307                         }
308                 }
309                 
310                 static CharToTokenType[] charToTokenType = {
311                         new CharToTokenType ('<', TokenType.Less),
312                         new CharToTokenType ('>', TokenType.Greater),
313                         new CharToTokenType ('=', TokenType.Equal),
314                         new CharToTokenType ('(', TokenType.LeftParen),
315                         new CharToTokenType (')', TokenType.RightParen),
316                         new CharToTokenType ('.', TokenType.Dot),
317                         new CharToTokenType (',', TokenType.Comma),
318                         new CharToTokenType ('!', TokenType.Not),
319                         new CharToTokenType ('@', TokenType.Item),
320                         new CharToTokenType ('$', TokenType.Property),
321                         new CharToTokenType ('%', TokenType.Metadata),
322                         new CharToTokenType ('\'', TokenType.Apostrophe),
323                 };
324         }
325 }
326
327 #endif