Merge pull request #601 from knocte/sock_improvements
[mono.git] / mcs / class / Microsoft.Build.Engine / Microsoft.Build.BuildEngine / ConditionParser.cs
index c7ebc49074c9e62e14d60bd1c32ad8c98fea42aa..ec538abc29f3441d5927656608e29ecdb24ed11c 100644 (file)
 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
-#if NET_2_0
-
 using System;
 using System.Collections;
+using System.Collections.Generic;
 using System.Text;
 
 namespace Microsoft.Build.BuildEngine {
 
        internal class ConditionParser {
        
-               ConditionTokenizer tokenizer = new ConditionTokenizer ();
+               ConditionTokenizer tokenizer;
+               string conditionStr;
                
-               private ConditionParser (string condition)
+               ConditionParser (string condition)
                {
+                       tokenizer = new ConditionTokenizer ();
                        tokenizer.Tokenize (condition);
+                       conditionStr = condition;
                }
                
+               public static bool ParseAndEvaluate (string condition, Project context)
+               {
+                       if (String.IsNullOrEmpty (condition))
+                               return true;
+
+                       try {
+                               ConditionExpression ce = ParseCondition (condition);
+
+                               if (!ce.CanEvaluateToBool (context))
+                                       throw new InvalidProjectFileException (String.Format ("Can not evaluate \"{0}\" to bool.", condition));
+
+                               return ce.BoolEvaluate (context);
+                       } catch (ExpressionParseException epe) {
+                               throw new InvalidProjectFileException (
+                                               String.Format ("Unable to parse condition \"{0}\" : {1}", condition, epe.Message),
+                                               epe);
+                       } catch (ExpressionEvaluationException epe) {
+                               throw new InvalidProjectFileException (
+                                               String.Format ("Unable to evaluate condition \"{0}\" : {1}", condition, epe.Message),
+                                               epe);
+                       }
+               }
+
                public static ConditionExpression ParseCondition (string condition)
                {
                        ConditionParser parser = new ConditionParser (condition);
                        ConditionExpression e = parser.ParseExpression ();
                        
                        if (!parser.tokenizer.IsEOF ())
-                               throw new ExpressionParseException (String.Format ("Unexpected token: {0}", parser.tokenizer.Token.Value));
+                               throw new ExpressionParseException (String.Format ("Unexpected token found, {0}, in condition \"{1}\"", parser.tokenizer.Token, condition));
                        
                        return e;
                }
                
-               private ConditionExpression ParseExpression ()
+               ConditionExpression ParseExpression ()
                {
                        return ParseBooleanExpression ();
                }
                
-               private ConditionExpression ParseBooleanExpression ()
+               ConditionExpression ParseBooleanExpression ()
                {
                        return ParseBooleanAnd ();
                }
+
+               public static string And (string a, string b)
+               {
+                       return a + " and " + b;
+               }
                
-               private ConditionExpression ParseBooleanAnd ()
+               ConditionExpression ParseBooleanAnd ()
                {
                        ConditionExpression e = ParseBooleanOr ();
                        
                        while (tokenizer.IsToken (TokenType.And)) {
                                tokenizer.GetNextToken ();
-                               e = new ConditionAndExpression ((ConditionExpression) e, (ConditionExpression) ParseBooleanOr ());
+                               e = new ConditionAndExpression (e, ParseBooleanOr ());
                        }
                        
                        return e;
                }
                
-               private ConditionExpression ParseBooleanOr ()
+               ConditionExpression ParseBooleanOr ()
                {
                        ConditionExpression e = ParseRelationalExpression ();
                        
                        while (tokenizer.IsToken (TokenType.Or)) {
                                tokenizer.GetNextToken ();
-                               e = new ConditionOrExpression ((ConditionExpression) e, (ConditionExpression) ParseRelationalExpression ());
+                               e = new ConditionOrExpression (e, ParseRelationalExpression ());
                        }
                        
                        return e;
                }
                
-               private ConditionExpression ParseRelationalExpression ()
+               ConditionExpression ParseRelationalExpression ()
                {
                        ConditionExpression e = ParseFactorExpression ();
+                       
                        Token opToken;
                        RelationOperator op;
                        
@@ -127,25 +158,125 @@ namespace Microsoft.Build.BuildEngine {
                                default:
                                        throw new ExpressionParseException (String.Format ("Wrong relation operator {0}", opToken.Value));
                                }
-                               
-                               e =  new ConditionRelationalExpression ((ConditionExpression) e, ParseFactorExpression (), op);
+
+                               e =  new ConditionRelationalExpression (e, ParseFactorExpression (), op);
                        }
                        
                        return e;
                }
                
-               // FIXME: parse sub expression in parens, parse TokenType.Not, parse functions
-               private ConditionExpression ParseFactorExpression ()
+               ConditionExpression ParseFactorExpression ()
                {
+                       ConditionExpression e;
                        Token token = tokenizer.Token;
                        tokenizer.GetNextToken ();
+
+                       if (token.Type == TokenType.LeftParen) {
+                               e = ParseExpression ();
+                               tokenizer.Expect (TokenType.RightParen);
+                       } else if (token.Type == TokenType.String && tokenizer.Token.Type == TokenType.LeftParen) {
+                               e = ParseFunctionExpression (token.Value);
+                       } else if (token.Type == TokenType.String) {
+                               e = new ConditionFactorExpression (token);
+                       } else if (token.Type == TokenType.Number) {
+                               e = new ConditionFactorExpression (token);
+                       } else if (token.Type == TokenType.Item || token.Type == TokenType.Property
+                                       || token.Type == TokenType.Metadata) {
+                               e = ParseReferenceExpression (token.Value);
+                       } else if (token.Type == TokenType.Not) {
+                               e = ParseNotExpression ();
+                       } else
+                               throw new ExpressionParseException (String.Format ("Unexpected token {0}, while parsing condition \"{1}\"", token, conditionStr));
                        
-                       if (token.Type != TokenType.String && token.Type != TokenType.Number)
-                               throw new ExpressionParseException (String.Format ("Unexpected token type {0}.", token.Type));
+                       return e;
+               }
+
+               ConditionExpression ParseNotExpression ()
+               {
+                       return new ConditionNotExpression (ParseFactorExpression ());
+               }
+
+               ConditionExpression ParseFunctionExpression (string function_name)
+               {
+                       return new ConditionFunctionExpression (function_name, ParseFunctionArguments ());
+               }
+               
+               List <ConditionFactorExpression> ParseFunctionArguments ()
+               {
+                       List <ConditionFactorExpression> list = new List <ConditionFactorExpression> ();
+                       ConditionFactorExpression e;
+                       
+                       while (true) {
+                               tokenizer.GetNextToken ();
+                               if (tokenizer.Token.Type == TokenType.RightParen) {
+                                       tokenizer.GetNextToken ();
+                                       break;
+                               }
+                               if (tokenizer.Token.Type == TokenType.Comma)
+                                       continue;
+                                       
+                               tokenizer.Putback (tokenizer.Token);
+                               e = (ConditionFactorExpression) ParseFactorExpression ();
+                               list.Add (e);
+                       }
                        
-                       return new ConditionFactorExpression (token);
+                       return list;
+               }
+
+               //@prefix: @ or $
+               ConditionExpression ParseReferenceExpression (string prefix)
+               {
+                       StringBuilder sb = new StringBuilder ();
+
+                       string ref_type = prefix [0] == '$' ? "a property" : "an item list";
+                       int token_pos = tokenizer.Token.Position;
+                       IsAtToken (TokenType.LeftParen, String.Format (
+                                               "Expected {0} at position {1} in condition \"{2}\". Missing opening parantheses after the '{3}'.",
+                                               ref_type, token_pos, conditionStr, prefix));
+                       tokenizer.GetNextToken ();
+
+                       sb.AppendFormat ("{0}({1}", prefix, tokenizer.Token.Value);
+
+                       tokenizer.GetNextToken ();
+                       if (prefix == "@" && tokenizer.Token.Type == TokenType.Transform) {
+                               tokenizer.GetNextToken ();
+                               sb.AppendFormat ("->'{0}'", tokenizer.Token.Value);
+
+                               tokenizer.GetNextToken ();
+                               if (tokenizer.Token.Type == TokenType.Comma) {
+                                       tokenizer.GetNextToken ();
+                                       sb.AppendFormat (", '{0}'", tokenizer.Token.Value);
+                                       tokenizer.GetNextToken ();
+                               }
+                       }
+
+                       IsAtToken (TokenType.RightParen, String.Format (
+                                               "Expected {0} at position {1} in condition \"{2}\". Missing closing parantheses'.",
+                                               ref_type, token_pos, conditionStr, prefix));
+                       tokenizer.GetNextToken ();
+
+                       sb.Append (")");
+
+                       //FIXME: HACKY!
+                       return new ConditionFactorExpression (new Token (sb.ToString (), TokenType.String, token_pos));
+               }
+
+               // used to check current token type
+               void IsAtToken (TokenType type, string error_msg)
+               {
+                       if (tokenizer.Token.Type != type) {
+                               if (!String.IsNullOrEmpty (error_msg))
+                                       throw new ExpressionParseException (error_msg);
+
+                               if (tokenizer.Token.Type == TokenType.EOF)
+                                       throw new ExpressionParseException (String.Format (
+                                                               "Expected a \"{0}\" but the condition ended abruptly, while parsing condition \"{1}\"",
+                                                               Token.TypeAsString (type), conditionStr));
+
+                               throw new ExpressionParseException (String.Format (
+                                                               "Expected \"{0}\" token,  but got {1}, while parsing \"{2}\"",
+                                                               Token.TypeAsString (type), tokenizer.Token, conditionStr));
+                       }
                }
        }
 }
-
-#endif