2 // ConditionTokenizer.cs
5 // Marek Sieradzki (marek.sieradzki@gmail.com)
6 // Jaroslaw Kowalski <jaak@jkowalski.net>
8 // (C) 2006 Marek Sieradzki
9 // (C) 2004-2006 Jaroslaw Kowalski
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:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
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.
31 using System.Collections;
32 using System.Collections.Generic;
33 using System.Collections.Specialized;
36 namespace Microsoft.Build.BuildEngine {
38 internal sealed class ConditionTokenizer {
40 string inputString = null;
42 int tokenPosition = 0;
47 // bool ignoreWhiteSpace = true;
49 static TokenType[] charIndexToTokenType = new TokenType[128];
50 static Dictionary <string, TokenType> keywords = new Dictionary <string, TokenType> (StringComparer.OrdinalIgnoreCase);
52 static ConditionTokenizer ()
54 for (int i = 0; i < 128; i++)
55 charIndexToTokenType [i] = TokenType.Invalid;
57 foreach (CharToTokenType cht in charToTokenType)
58 charIndexToTokenType [(int) cht.ch] = cht.tokenType;
60 keywords.Add ("and", TokenType.And);
61 keywords.Add ("or", TokenType.Or);
64 public ConditionTokenizer ()
66 // this.ignoreWhiteSpace = true;
69 public void Tokenize (string s)
72 throw new ArgumentNullException ("s");
76 this.token = new Token (null, TokenType.BOF, 0);
81 void SkipWhiteSpace ()
85 while ((ch = PeekChar ()) != -1) {
86 if (!Char.IsWhiteSpace ((char)ch))
94 if (position < inputString.Length)
95 return (int) inputString [position];
102 if (position < inputString.Length)
103 return (int) inputString [position++];
108 public void Expect (TokenType type)
110 if (token.Type != type)
111 throw new ExpressionParseException ("Expected token type of type: " + type + ", got " + token.Type +
112 " (" + token.Value + ") .");
119 return token.Type == TokenType.EOF;
122 public bool IsNumber ()
124 return token.Type == TokenType.Number;
127 public bool IsToken (TokenType type)
129 return token.Type == type;
132 public bool IsPunctation ()
134 return (token.Type >= TokenType.FirstPunct && token.Type < TokenType.LastPunct);
138 public void Putback (Token token)
143 public void GetNextToken ()
145 if (putback != null) {
151 if (token.Type == TokenType.EOF)
152 throw new ExpressionParseException (String.Format (
153 "Error while parsing condition \"{0}\", ended abruptly.",
158 tokenPosition = position;
160 // int i = PeekChar ();
164 token = new Token (null, TokenType.EOF, tokenPosition);
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 () == '>') {
175 token = new Token ("->", TokenType.Transform, tokenPosition);
176 } else if (Char.IsDigit (ch) || ch == '-') {
177 StringBuilder sb = new StringBuilder ();
181 while ((i = PeekChar ()) != -1) {
184 if (Char.IsDigit (ch) || ch == '.')
185 sb.Append ((char) ReadChar ());
190 token = new Token (sb.ToString (), TokenType.Number, tokenPosition);
191 } else if (ch == '\'' && position < inputString.Length) {
192 StringBuilder sb = new StringBuilder ();
196 bool is_itemref = (PeekChar () == '@');
197 int num_open_braces = 0;
198 bool in_literal = false;
200 while ((i = PeekChar ()) != -1) {
202 if (ch == '(' && !in_literal && is_itemref)
204 if (ch == ')' && !in_literal && is_itemref)
207 sb.Append ((char) ReadChar ());
210 if (num_open_braces == 0)
212 in_literal = !in_literal;
216 temp = sb.ToString ();
218 token = new Token (temp.Substring (1, temp.Length - 2), TokenType.String, tokenPosition);
220 } else if (ch == '_' || Char.IsLetter (ch)) {
221 StringBuilder sb = new StringBuilder ();
223 sb.Append ((char) ch);
225 while ((i = PeekChar ()) != -1) {
226 if ((char) i == '_' || Char.IsLetterOrDigit ((char) i))
227 sb.Append ((char) ReadChar ());
232 string temp = sb.ToString ();
234 if (keywords.ContainsKey (temp))
235 token = new Token (temp, keywords [temp], tokenPosition);
237 token = new Token (temp, TokenType.String, tokenPosition);
239 } else if (ch == '!' && PeekChar () == (int) '=') {
240 token = new Token ("!=", TokenType.NotEqual, tokenPosition);
242 } else if (ch == '<' && PeekChar () == (int) '=') {
243 token = new Token ("<=", TokenType.LessOrEqual, tokenPosition);
245 } else if (ch == '>' && PeekChar () == (int) '=') {
246 token = new Token (">=", TokenType.GreaterOrEqual, tokenPosition);
248 } else if (ch == '=' && PeekChar () == (int) '=') {
249 token = new Token ("==", TokenType.Equal, tokenPosition);
251 } else if (ch >= 32 && ch < 128) {
252 if (charIndexToTokenType [ch] != TokenType.Invalid) {
253 token = new Token (new String (ch, 1), charIndexToTokenType [ch], tokenPosition);
256 throw new ExpressionParseException (String.Format ("Invalid punctuation: {0}", ch));
258 throw new ExpressionParseException (String.Format ("Invalid token: {0}", ch));
261 public int TokenPosition {
262 get { return tokenPosition; }
266 get { return token; }
270 public bool IgnoreWhiteSpace {
271 get { return ignoreWhiteSpace; }
272 set { ignoreWhiteSpace = value; }
276 struct CharToTokenType {
278 public TokenType tokenType;
280 public CharToTokenType (char ch, TokenType tokenType)
283 this.tokenType = tokenType;
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),