New test.
[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.Specialized;
35 using System.Text;
36
37 namespace Microsoft.Build.BuildEngine {
38
39         internal sealed class ConditionTokenizer {
40                 string  inputString = null;
41                 int     position = 0;
42                 int     tokenPosition = 0;
43                 
44                 Token   token;
45                 
46                 bool    ignoreWhiteSpace = true;
47                 
48                 static TokenType[] charIndexToTokenType = new TokenType[128];
49                 static Hashtable keywordToTokenType = CollectionsUtil.CreateCaseInsensitiveHashtable ();
50
51                 static ConditionTokenizer ()
52                 {
53                         for (int i = 0; i < 128; i++)
54                                 charIndexToTokenType [i] = TokenType.Invalid;
55                         
56                         foreach (CharToTokenType cht in charToTokenType)
57                                 charIndexToTokenType [(int) cht.ch] = cht.tokenType;
58                         
59                 }
60                 
61                 public ConditionTokenizer ()
62                 {
63                         this.ignoreWhiteSpace = true;
64                 }
65                 
66                 public void Tokenize (string s)
67                 {
68                         if (s == null)
69                                 throw new ArgumentNullException ("s");
70                 
71                         this.inputString = s;
72                         this.position = 0;
73                         this.token = new Token (null, TokenType.BOF);
74
75                         GetNextToken ();
76                 }
77                 
78                 private void SkipWhiteSpace ()
79                 {
80                         int ch;
81                         
82                         while ((ch = PeekChar ()) != -1) {
83                                 if (!Char.IsWhiteSpace ((char)ch))
84                                         break;
85                                 ReadChar ();
86                         }
87                 }
88                 
89                 private int PeekChar ()
90                 {
91                         if (position < inputString.Length)
92                                 return (int) inputString [position];
93                         else
94                                 return -1;
95                 }
96                 
97                 private int ReadChar ()
98                 {
99                         if (position < inputString.Length)
100                                 return (int) inputString [position++];
101                         else
102                                 return -1;
103                 }
104                 
105                 public void Expect (TokenType type)
106                 {
107                         if (token.Type != type)
108                                 throw new ExpressionParseException ("Expected token type of type: " + type + ", got " + token.Type +
109                                         " (" + token.Value + ") .");
110                         
111                         GetNextToken ();
112                 }
113                 
114                 public bool IsEOF ()
115                 {
116                         return token.Type == TokenType.EOF;
117                 }
118                 
119                 public bool IsNumber ()
120                 {
121                         return token.Type == TokenType.Number;
122                 }
123                 
124                 public bool IsToken (TokenType type)
125                 {
126                         return token.Type == type;
127                 }
128                 
129                 public bool IsPunctation ()
130                 {
131                         return (token.Type >= TokenType.FirstPunct && token.Type < TokenType.LastPunct);
132                 }
133                 
134                 // FIXME: add 'and' and 'or' tokens
135                 public void GetNextToken ()
136                 {
137                         if (token.Type == TokenType.EOF)
138                                 throw new ExpressionParseException ("Cannot read past the end of stream.");
139                         
140                         if (IgnoreWhiteSpace)
141                                 SkipWhiteSpace ();
142                         
143                         tokenPosition = position;
144                         
145                         int i = PeekChar ();
146                         
147                         if (i == -1) {
148                                 token = new Token (null, TokenType.EOF);
149                                 return;
150                         }
151                         
152                         char ch = (char) i;
153                         
154                         if (IgnoreWhiteSpace == false && Char.IsWhiteSpace (ch)) {
155                                 StringBuilder sb = new StringBuilder ();
156                                 int ch2;
157
158                                 while ((ch2 = PeekChar ()) != -1)  {
159                                         if (!Char.IsWhiteSpace ((char) ch2))
160                                                 break;
161
162                                         sb.Append ((char)ch2);
163                                         ReadChar();
164                                 }
165                                 
166                                 token = new Token (sb.ToString (), TokenType.WhiteSpace);
167                                 return;
168                         }
169                         
170                         if (Char.IsDigit (ch)) {
171                                 StringBuilder sb = new StringBuilder ();
172                                 
173                                 sb.Append (ch);
174                                 ReadChar ();
175                                 
176                                 while ((i = PeekChar ()) != -1) {
177                                         ch = (char) i;
178                                         
179                                         if (Char.IsDigit (ch) || ch == '.')
180                                                 sb.Append ((char) ReadChar ());
181                                         else
182                                                 break;
183                                 }
184                                 
185                                 token = new Token (sb.ToString (), TokenType.Number);
186                                 return;
187                         }
188                         
189                         if (ch == '\'') {
190                                 StringBuilder sb = new StringBuilder ();
191                                 string temp;
192                                 
193                                 sb.Append (ch);
194                                 ReadChar ();
195                                 
196                                 while ((i = PeekChar ()) != -1) {
197                                         ch = (char) i;
198                                         
199                                         sb.Append ((char) ReadChar ());
200                                         
201                                         if (ch == '\'')
202                                                 break;
203                                 }
204                                 
205                                 temp = sb.ToString ();
206                                 
207                                 // FIXME: test extreme cases
208                                 token = new Token (temp.Substring (1, temp.Length - 2), TokenType.String);
209                                 
210                                 return;
211                         }
212                         
213                         if (ch == '_' || Char.IsLetter (ch)) {
214                                 StringBuilder sb = new StringBuilder ();
215                                 
216                                 sb.Append ((char) ch);
217                                 ReadChar ();
218                                 
219                                 while ((i = PeekChar ()) != -1) {
220                                         if ((char) i == '_' || Char.IsLetterOrDigit ((char) i))
221                                                 sb.Append ((char) ReadChar ());
222                                         else
223                                                 break;
224                                 }
225                                 
226                                 string temp = sb.ToString ();
227                                 
228                                 if (temp.ToLower () == "and")
229                                         token = new Token (temp, TokenType.And);
230                                 else if (temp.ToLower () == "or")
231                                         token = new Token (temp, TokenType.Or);
232                                 else
233                                         token = new Token (sb.ToString (), TokenType.String);
234                                 return;
235                         }
236                         
237                         ReadChar ();
238                         
239                         if (ch == '!' && PeekChar () == (int) '=') {
240                                 token = new Token ("!=", TokenType.NotEqual);
241                                 ReadChar ();
242                                 return;
243                         }
244                         
245                         if (ch == '<' && PeekChar () == (int) '=') {
246                                 token = new Token ("<=", TokenType.LessOrEqual);
247                                 ReadChar ();
248                                 return;
249                         }
250                         
251                         if (ch == '>' && PeekChar () == (int) '=') {
252                                 token = new Token (">=", TokenType.GreaterOrEqual);
253                                 ReadChar ();
254                                 return;
255                         }
256                         
257                         if (ch == '=' && PeekChar () == (int) '=') {
258                                 token = new Token ("==", TokenType.Equal);
259                                 ReadChar ();
260                                 return;
261                         }
262                         
263                         if (ch == '-' && PeekChar () == (int) '>') {
264                                 token = new Token ("->", TokenType.Transform);
265                                 ReadChar ();
266                                 return;
267                         }
268                         
269                         if (ch >= 32 && ch < 128) {
270                                 if (charIndexToTokenType [ch] != TokenType.Invalid) {
271                                         token = new Token (new String (ch, 1), charIndexToTokenType [ch]);
272                                         return;
273                                 } else
274                                         throw new ExpressionParseException (String.Format ("Invalid punctuation: {0}", ch));
275                         }
276                         
277                         throw new ExpressionParseException (String.Format ("Invalid token: {0}", ch));
278                 }
279                 
280                 public int TokenPosition {
281                         get { return tokenPosition; }
282                 }
283                 
284                 public Token Token {
285                         get { return token; }
286                 }
287                 
288                 public bool IgnoreWhiteSpace {
289                         get { return ignoreWhiteSpace; }
290                         set { ignoreWhiteSpace = value; }
291                 }
292                 
293                 struct CharToTokenType {
294                         public char ch;
295                         public TokenType tokenType;
296                         
297                         public CharToTokenType (char ch, TokenType tokenType)
298                         {
299                                 this.ch = ch;
300                                 this.tokenType = tokenType;
301                         }
302                 }
303                 
304                 static CharToTokenType[] charToTokenType = {
305                         new CharToTokenType ('<', TokenType.Less),
306                         new CharToTokenType ('>', TokenType.Greater),
307                         new CharToTokenType ('=', TokenType.Equal),
308                         new CharToTokenType ('(', TokenType.LeftParen),
309                         new CharToTokenType (')', TokenType.RightParen),
310                         new CharToTokenType ('.', TokenType.Dot),
311                         new CharToTokenType (',', TokenType.Comma),
312                         new CharToTokenType ('!', TokenType.Not),
313                         new CharToTokenType ('@', TokenType.Item),
314                         new CharToTokenType ('$', TokenType.Property),
315                         new CharToTokenType ('%', TokenType.Metadata),
316                         new CharToTokenType ('\'', TokenType.Apostrophe),
317                 };
318         }
319 }
320
321 #endif