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