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.
33 using System.Collections;
34 using System.Collections.Specialized;
37 namespace Microsoft.Build.BuildEngine {
39 internal sealed class ConditionTokenizer {
40 string inputString = null;
42 int tokenPosition = 0;
46 bool ignoreWhiteSpace = true;
48 static TokenType[] charIndexToTokenType = new TokenType[128];
49 static Hashtable keywordToTokenType = CollectionsUtil.CreateCaseInsensitiveHashtable ();
51 static ConditionTokenizer ()
53 for (int i = 0; i < 128; i++)
54 charIndexToTokenType [i] = TokenType.Invalid;
56 foreach (CharToTokenType cht in charToTokenType)
57 charIndexToTokenType [(int) cht.ch] = cht.tokenType;
61 public ConditionTokenizer ()
63 this.ignoreWhiteSpace = true;
66 public void Tokenize (string s)
70 this.token = new Token (null, TokenType.BOF);
75 private void SkipWhiteSpace ()
79 while ((ch = PeekChar ()) != -1) {
80 if (!Char.IsWhiteSpace ((char)ch))
86 private int PeekChar ()
88 if (position < inputString.Length)
89 return (int) inputString [position];
94 private int ReadChar ()
96 if (position < inputString.Length)
97 return (int) inputString [position++];
102 public void Expect (TokenType type)
104 if (token.Type != type)
105 throw new ExpressionParseException ("Expected token type of type: " + type + ", got " + token.Type +
106 " (" + token.Value + ") .");
113 return token.Type == TokenType.EOF;
116 public bool IsNumber ()
118 return token.Type == TokenType.Number;
121 public bool IsToken (TokenType type)
123 return token.Type == type;
126 public bool IsPunctation ()
128 return (token.Type >= TokenType.FirstPunct && token.Type < TokenType.LastPunct);
131 // FIXME: add 'and' and 'or' tokens
132 public void GetNextToken ()
134 if (token.Type == TokenType.EOF)
135 throw new ExpressionParseException ("Cannot read past the end of stream.");
137 if (IgnoreWhiteSpace)
140 tokenPosition = position;
145 token = new Token (null, TokenType.EOF);
151 if (IgnoreWhiteSpace == false && Char.IsWhiteSpace (ch)) {
152 StringBuilder sb = new StringBuilder ();
155 while ((ch2 = PeekChar ()) != -1) {
156 if (!Char.IsWhiteSpace ((char) ch2))
159 sb.Append ((char)ch2);
163 token = new Token (sb.ToString (), TokenType.WhiteSpace);
167 if (Char.IsDigit (ch)) {
168 StringBuilder sb = new StringBuilder ();
173 while ((i = PeekChar ()) != -1) {
176 if (Char.IsDigit (ch) || ch == '.')
177 sb.Append ((char) ReadChar ());
182 token = new Token (sb.ToString (), TokenType.Number);
187 StringBuilder sb = new StringBuilder ();
193 while ((i = PeekChar ()) != -1) {
196 sb.Append ((char) ReadChar ());
202 temp = sb.ToString ();
204 // FIXME: test extreme cases
205 token = new Token (temp.Substring (1, temp.Length - 2), TokenType.String);
210 if (ch == '_' || Char.IsLetter (ch)) {
211 StringBuilder sb = new StringBuilder ();
213 sb.Append ((char) ch);
216 while ((i = PeekChar ()) != -1) {
217 if ((char) i == '_' || Char.IsLetterOrDigit ((char) i))
218 sb.Append ((char) ReadChar ());
223 string temp = sb.ToString ();
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);
230 token = new Token (sb.ToString (), TokenType.String);
236 if (ch == '!' && PeekChar () == (int) '=') {
237 token = new Token ("!=", TokenType.NotEqual);
242 if (ch == '<' && PeekChar () == (int) '=') {
243 token = new Token ("<=", TokenType.LessOrEqual);
248 if (ch == '>' && PeekChar () == (int) '=') {
249 token = new Token (">=", TokenType.GreaterThanOrEqual);
254 if (ch == '=' && PeekChar () == (int) '=') {
255 token = new Token ("==", TokenType.Equal);
260 if (ch == '-' && PeekChar () == (int) '>') {
261 token = new Token ("->", TokenType.Transform);
266 if (ch >= 32 && ch < 128) {
267 if (charIndexToTokenType [ch] != TokenType.Invalid) {
268 token = new Token (new String (ch, 1), charIndexToTokenType [ch]);
271 throw new ExpressionParseException (String.Format ("Invalid punctuation: {0}", ch));
274 throw new ExpressionParseException (String.Format ("Invalid token: {0}", ch));
277 public int TokenPosition {
278 get { return tokenPosition; }
282 get { return token; }
285 public bool IgnoreWhiteSpace {
286 get { return ignoreWhiteSpace; }
287 set { ignoreWhiteSpace = value; }
290 struct CharToTokenType {
292 public TokenType tokenType;
294 public CharToTokenType (char ch, TokenType tokenType)
297 this.tokenType = tokenType;
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),