fe777c5c82
Former-commit-id: 6a76a29bd07d86e57c6c8da45c65ed5447d38a61
326 lines
8.4 KiB
C#
326 lines
8.4 KiB
C#
//
|
|
// ConditionTokenizer.cs
|
|
//
|
|
// Author:
|
|
// Marek Sieradzki (marek.sieradzki@gmail.com)
|
|
// Jaroslaw Kowalski <jaak@jkowalski.net>
|
|
//
|
|
// (C) 2006 Marek Sieradzki
|
|
// (C) 2004-2006 Jaroslaw Kowalski
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining
|
|
// a copy of this software and associated documentation files (the
|
|
// "Software"), to deal in the Software without restriction, including
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
|
// permit persons to whom the Software is furnished to do so, subject to
|
|
// the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be
|
|
// included in all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Specialized;
|
|
using System.Text;
|
|
|
|
namespace Microsoft.Build.BuildEngine {
|
|
|
|
internal sealed class ConditionTokenizer {
|
|
|
|
string inputString = null;
|
|
int position = 0;
|
|
int tokenPosition = 0;
|
|
|
|
Token token;
|
|
Token putback = null;
|
|
|
|
// bool ignoreWhiteSpace = true;
|
|
|
|
static TokenType[] charIndexToTokenType = new TokenType[128];
|
|
static Dictionary <string, TokenType> keywords = new Dictionary <string, TokenType> (StringComparer.OrdinalIgnoreCase);
|
|
|
|
static ConditionTokenizer ()
|
|
{
|
|
for (int i = 0; i < 128; i++)
|
|
charIndexToTokenType [i] = TokenType.Invalid;
|
|
|
|
foreach (CharToTokenType cht in charToTokenType)
|
|
charIndexToTokenType [(int) cht.ch] = cht.tokenType;
|
|
|
|
keywords.Add ("and", TokenType.And);
|
|
keywords.Add ("or", TokenType.Or);
|
|
}
|
|
|
|
public ConditionTokenizer ()
|
|
{
|
|
// this.ignoreWhiteSpace = true;
|
|
}
|
|
|
|
public void Tokenize (string s)
|
|
{
|
|
if (s == null)
|
|
throw new ArgumentNullException ("s");
|
|
|
|
this.inputString = s;
|
|
this.position = 0;
|
|
this.token = new Token (null, TokenType.BOF, 0);
|
|
|
|
GetNextToken ();
|
|
}
|
|
|
|
void SkipWhiteSpace ()
|
|
{
|
|
int ch;
|
|
|
|
while ((ch = PeekChar ()) != -1) {
|
|
if (!Char.IsWhiteSpace ((char)ch))
|
|
break;
|
|
ReadChar ();
|
|
}
|
|
}
|
|
|
|
int PeekChar ()
|
|
{
|
|
if (position < inputString.Length)
|
|
return (int) inputString [position];
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
int ReadChar ()
|
|
{
|
|
if (position < inputString.Length)
|
|
return (int) inputString [position++];
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
public void Expect (TokenType type)
|
|
{
|
|
if (token.Type != type)
|
|
throw new ExpressionParseException ("Expected token type of type: " + type + ", got " + token.Type +
|
|
" (" + token.Value + ") .");
|
|
|
|
GetNextToken ();
|
|
}
|
|
|
|
public bool IsEOF ()
|
|
{
|
|
return token.Type == TokenType.EOF;
|
|
}
|
|
|
|
public bool IsNumber ()
|
|
{
|
|
return token.Type == TokenType.Number;
|
|
}
|
|
|
|
public bool IsToken (TokenType type)
|
|
{
|
|
return token.Type == type;
|
|
}
|
|
|
|
public bool IsPunctation ()
|
|
{
|
|
return (token.Type >= TokenType.FirstPunct && token.Type < TokenType.LastPunct);
|
|
}
|
|
|
|
// FIXME test this
|
|
public void Putback (Token token)
|
|
{
|
|
putback = token;
|
|
}
|
|
|
|
public void GetNextToken ()
|
|
{
|
|
if (putback != null) {
|
|
token = putback;
|
|
putback = null;
|
|
return;
|
|
}
|
|
|
|
if (token.Type == TokenType.EOF)
|
|
throw new ExpressionParseException (String.Format (
|
|
"Error while parsing condition \"{0}\", ended abruptly.",
|
|
inputString));
|
|
|
|
SkipWhiteSpace ();
|
|
|
|
tokenPosition = position;
|
|
|
|
// int i = PeekChar ();
|
|
int i = ReadChar ();
|
|
|
|
if (i == -1) {
|
|
token = new Token (null, TokenType.EOF, tokenPosition);
|
|
return;
|
|
}
|
|
|
|
char ch = (char) i;
|
|
|
|
|
|
// FIXME: looks like a hack: if '-' is here '->' won't be tokenized
|
|
// maybe we should treat item reference as a token
|
|
if (ch == '-' && PeekChar () == '>') {
|
|
ReadChar ();
|
|
token = new Token ("->", TokenType.Transform, tokenPosition);
|
|
} else if (Char.IsDigit (ch) || ch == '-') {
|
|
StringBuilder sb = new StringBuilder ();
|
|
|
|
sb.Append (ch);
|
|
|
|
while ((i = PeekChar ()) != -1) {
|
|
ch = (char) i;
|
|
|
|
if (Char.IsDigit (ch) || ch == '.')
|
|
sb.Append ((char) ReadChar ());
|
|
else
|
|
break;
|
|
}
|
|
|
|
token = new Token (sb.ToString (), TokenType.Number, tokenPosition);
|
|
} else if (ch == '\'' && position < inputString.Length) {
|
|
StringBuilder sb = new StringBuilder ();
|
|
string temp;
|
|
|
|
sb.Append (ch);
|
|
bool is_itemref = (PeekChar () == '@');
|
|
int num_open_braces = 0;
|
|
bool in_literal = false;
|
|
|
|
while ((i = PeekChar ()) != -1) {
|
|
ch = (char) i;
|
|
if (ch == '(' && !in_literal && is_itemref)
|
|
num_open_braces ++;
|
|
if (ch == ')' && !in_literal && is_itemref)
|
|
num_open_braces --;
|
|
|
|
sb.Append ((char) ReadChar ());
|
|
|
|
if (ch == '\'') {
|
|
if (num_open_braces == 0)
|
|
break;
|
|
in_literal = !in_literal;
|
|
}
|
|
}
|
|
|
|
temp = sb.ToString ();
|
|
|
|
token = new Token (temp.Substring (1, temp.Length - 2), TokenType.String, tokenPosition);
|
|
|
|
} else if (ch == '_' || Char.IsLetter (ch)) {
|
|
StringBuilder sb = new StringBuilder ();
|
|
|
|
sb.Append ((char) ch);
|
|
|
|
while ((i = PeekChar ()) != -1) {
|
|
if ((char) i == '_' || Char.IsLetterOrDigit ((char) i))
|
|
sb.Append ((char) ReadChar ());
|
|
else
|
|
break;
|
|
}
|
|
|
|
string temp = sb.ToString ();
|
|
|
|
if (keywords.ContainsKey (temp))
|
|
token = new Token (temp, keywords [temp], tokenPosition);
|
|
else
|
|
token = new Token (temp, TokenType.String, tokenPosition);
|
|
|
|
} else if (ch == '!' && PeekChar () == (int) '=') {
|
|
token = new Token ("!=", TokenType.NotEqual, tokenPosition);
|
|
ReadChar ();
|
|
} else if (ch == '<' && PeekChar () == (int) '=') {
|
|
token = new Token ("<=", TokenType.LessOrEqual, tokenPosition);
|
|
ReadChar ();
|
|
} else if (ch == '>' && PeekChar () == (int) '=') {
|
|
token = new Token (">=", TokenType.GreaterOrEqual, tokenPosition);
|
|
ReadChar ();
|
|
} else if (ch == '=' && PeekChar () == (int) '=') {
|
|
token = new Token ("==", TokenType.Equal, tokenPosition);
|
|
ReadChar ();
|
|
} else if (ch >= 32 && ch < 128) {
|
|
if (charIndexToTokenType [ch] != TokenType.Invalid) {
|
|
token = new Token (new String (ch, 1), charIndexToTokenType [ch], tokenPosition);
|
|
return;
|
|
} else
|
|
throw new ExpressionParseException (String.Format ("Invalid punctuation: {0}", ch));
|
|
} else
|
|
throw new ExpressionParseException (String.Format ("Invalid token: {0}", ch));
|
|
}
|
|
|
|
public void ScanForClosingParens (int parensCounter = 1)
|
|
{
|
|
tokenPosition = position;
|
|
int start = position;
|
|
int ch;
|
|
while ((ch = ReadChar ()) >= 0) {
|
|
switch (ch) {
|
|
case ')':
|
|
if (--parensCounter == 0) {
|
|
--position;
|
|
token = new Token (inputString.Substring (start, position - start), TokenType.String, tokenPosition);
|
|
return;
|
|
}
|
|
break;
|
|
case '(':
|
|
++parensCounter;
|
|
break;
|
|
}
|
|
}
|
|
|
|
token = new Token (null, TokenType.EOF, tokenPosition);
|
|
}
|
|
|
|
public int TokenPosition {
|
|
get { return tokenPosition; }
|
|
}
|
|
|
|
public Token Token {
|
|
get { return token; }
|
|
}
|
|
|
|
/*
|
|
public bool IgnoreWhiteSpace {
|
|
get { return ignoreWhiteSpace; }
|
|
set { ignoreWhiteSpace = value; }
|
|
}
|
|
*/
|
|
|
|
struct CharToTokenType {
|
|
public char ch;
|
|
public TokenType tokenType;
|
|
|
|
public CharToTokenType (char ch, TokenType tokenType)
|
|
{
|
|
this.ch = ch;
|
|
this.tokenType = tokenType;
|
|
}
|
|
}
|
|
|
|
static CharToTokenType[] charToTokenType = {
|
|
new CharToTokenType ('<', TokenType.Less),
|
|
new CharToTokenType ('>', TokenType.Greater),
|
|
new CharToTokenType ('=', TokenType.Equal),
|
|
new CharToTokenType ('(', TokenType.LeftParen),
|
|
new CharToTokenType (')', TokenType.RightParen),
|
|
new CharToTokenType ('.', TokenType.Dot),
|
|
new CharToTokenType (',', TokenType.Comma),
|
|
new CharToTokenType ('!', TokenType.Not),
|
|
new CharToTokenType ('@', TokenType.Item),
|
|
new CharToTokenType ('$', TokenType.Property),
|
|
new CharToTokenType ('%', TokenType.Metadata),
|
|
new CharToTokenType ('\'', TokenType.Apostrophe),
|
|
};
|
|
}
|
|
}
|