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