// // Top node of grammar // See grammar for how the following methods relate to each // other. // private GenericExpressionNode Expr(string expression) { GenericExpressionNode node = BooleanTerm(expression); if (!lexer.IsNext(Token.TokenType.EndOfInput)) { node = ExprPrime(expression, node); } #region REMOVE_COMPAT_WARNING // Check for potential change in behavior if (LoggingServices != null && !warnedForExpression && node.PotentialAndOrConflict()) { // We only want to warn once even if there multiple () sub expressions warnedForExpression = true; // Try to figure out where this expression was located string projectFile = String.Empty; int lineNumber = 0; int columnNumber = 0; if (this.conditionAttribute != null) { projectFile = XmlUtilities.GetXmlNodeFile(this.conditionAttribute, String.Empty /* no project file if XML is purely in-memory */); XmlSearcher.GetLineColumnByNode(this.conditionAttribute, out lineNumber, out columnNumber); } // Log a warning regarding the fact the expression may have been evaluated // incorrectly in earlier version of MSBuild LoggingServices.LogWarning(logBuildEventContext, new BuildEventFileInfo(projectFile, lineNumber, columnNumber), "ConditionMaybeEvaluatedIncorrectly", expression); } #endregion return(node); }
// // Main entry point for parser. // You pass in the expression you want to parse, and you get an // ExpressionTree out the back end. // internal GenericExpressionNode Parse(string expression, XmlAttribute conditionAttributeRef, ParserOptions optionSettings) { // We currently have no support (and no scenarios) for disallowing property references // in Conditions. ErrorUtilities.VerifyThrow(0 != (optionSettings & ParserOptions.AllowProperties), "Properties should always be allowed."); this.conditionAttribute = conditionAttributeRef; this.options = optionSettings; lexer = new Scanner(expression, options); if (!lexer.Advance()) { errorPosition = lexer.GetErrorPosition(); ProjectErrorUtilities.VerifyThrowInvalidProject(false, this.conditionAttribute, lexer.GetErrorResource(), expression, errorPosition, lexer.UnexpectedlyFound); } GenericExpressionNode node = Expr(expression); if (!lexer.IsNext(Token.TokenType.EndOfInput)) { errorPosition = lexer.GetErrorPosition(); ProjectErrorUtilities.VerifyThrowInvalidProject(false, this.conditionAttribute, "UnexpectedTokenInCondition", expression, lexer.IsNextString(), errorPosition); } return(node); }
private GenericExpressionNode BooleanTermPrime(string expression, GenericExpressionNode lhs) { if (lexer.IsNext(Token.TokenType.EndOfInput)) { return(lhs); } else if (Same(expression, Token.TokenType.And)) { GenericExpressionNode rhs = RelationalExpr(expression); if (rhs == null) { errorPosition = lexer.GetErrorPosition(); ProjectErrorUtilities.VerifyThrowInvalidProject(false, this.conditionAttribute, "UnexpectedTokenInCondition", expression, lexer.IsNextString(), errorPosition); } OperatorExpressionNode andNode = new AndExpressionNode(); andNode.LeftChild = lhs; andNode.RightChild = rhs; return(BooleanTermPrime(expression, andNode)); } else { // Should this be error case? return(lhs); } }
/// <summary> /// Expands properties and items in the argument, and verifies that the result is consistent /// with a scalar parameter type. /// </summary> /// <param name="function">Function name for errors</param> /// <param name="argumentNode">Argument to be expanded</param> /// <returns>Scalar result</returns> /// <owner>danmose</owner> private string ExpandArgumentForScalarParameter(string function, GenericExpressionNode argumentNode, ConditionEvaluationState state) { string argument = argumentNode.GetUnexpandedValue(state); List <TaskItem> items = state.expanderToUse.ExpandAllIntoTaskItems(argument, state.conditionAttribute); string expandedValue = String.Empty; if (items.Count == 0) { // Empty argument, that's fine. } else if (items.Count == 1) { expandedValue = items[0].ItemSpec; } else // too many items for the function { // We only allow a single item to be passed into a scalar parameter. ProjectErrorUtilities.VerifyThrowInvalidProject(false, state.conditionAttribute, "CannotPassMultipleItemsIntoScalarFunction", function, argument, state.expanderToUse.ExpandAllIntoString(argument, state.conditionAttribute)); } return(expandedValue); }
/// <summary> /// Evaluates a string representing a condition from a "condition" attribute. /// If the condition is a malformed string, it throws an InvalidProjectFileException. /// This method uses cached expression trees to avoid generating them from scratch every time it's called. /// This method is thread safe and is called from engine and task execution module threads /// </summary> /// <param name="condition">Can be null</param> /// <param name="conditionAttribute">XML attribute on which the condition is evaluated</param> /// <param name="expander">All the data available for expanding embedded properties, metadata, and items</param> /// <param name="conditionedPropertiesTable">Can be null</param> /// <param name="itemListOptions"></param> /// <param name="loggingServices">Can be null</param> /// <param name="buildEventContext"> contains contextual information for logging events</param> /// <returns>true, if the expression evaluates to true, otherwise false</returns> internal static bool EvaluateCondition ( string condition, XmlAttribute conditionAttribute, Expander expander, Hashtable conditionedPropertiesTable, ParserOptions itemListOptions, EngineLoggingServices loggingServices, BuildEventContext buildEventContext ) { ErrorUtilities.VerifyThrow((conditionAttribute != null) || (condition.Length == 0), "If condition is non-empty, you must provide the XML node representing the condition."); // An empty condition is equivalent to a "true" condition. if ((null == condition) || (condition.Length == 0)) { return(true); } Hashtable cachedExpressionTreesForCurrentOptions = cachedExpressionTrees[(int)itemListOptions]; // Try and see if we have an expression tree for this condition already GenericExpressionNode parsedExpression = (GenericExpressionNode)cachedExpressionTreesForCurrentOptions[condition]; if (parsedExpression == null) { Parser conditionParser = new Parser(); #region REMOVE_COMPAT_WARNING conditionParser.LoggingServices = loggingServices; conditionParser.LogBuildEventContext = buildEventContext; #endregion parsedExpression = conditionParser.Parse(condition, conditionAttribute, itemListOptions); // It's possible two threads will add a different tree to the same entry in the hashtable, // but it should be rare and it's not a problem - the previous entry will be thrown away. // We could ensure no dupes with double check locking but it's not really necessary here. // Also, we don't want to lock on every read. lock (cachedExpressionTreesForCurrentOptions) { cachedExpressionTreesForCurrentOptions[condition] = parsedExpression; } } ConditionEvaluationState state = new ConditionEvaluationState(conditionAttribute, expander, conditionedPropertiesTable, condition); bool result; // We are evaluating this expression now and it can cache some state for the duration, // so we don't want multiple threads working on the same expression lock (parsedExpression) { result = parsedExpression.Evaluate(state); parsedExpression.ResetState(); } return(result); }
private void Args(string expression, ArrayList arglist) { GenericExpressionNode arg = Arg(expression); arglist.Add(arg); if (Same(expression, Token.TokenType.Comma)) { Args(expression, arglist); } }
private GenericExpressionNode BooleanTerm(string expression) { GenericExpressionNode node = RelationalExpr(expression); if (node == null) { errorPosition = lexer.GetErrorPosition(); ProjectErrorUtilities.VerifyThrowInvalidProject(false, this.conditionAttribute, "UnexpectedTokenInCondition", expression, lexer.IsNextString(), errorPosition); } if (!lexer.IsNext(Token.TokenType.EndOfInput)) { node = BooleanTermPrime(expression, node); } return(node); }
private GenericExpressionNode RelationalExpr(string expression) { { GenericExpressionNode lhs = Factor(expression); if (lhs == null) { errorPosition = lexer.GetErrorPosition(); ProjectErrorUtilities.VerifyThrowInvalidProject(false, this.conditionAttribute, "UnexpectedTokenInCondition", expression, lexer.IsNextString(), errorPosition); } OperatorExpressionNode node = RelationalOperation(expression); if (node == null) { return(lhs); } GenericExpressionNode rhs = Factor(expression); node.LeftChild = lhs; node.RightChild = rhs; return(node); } }
private GenericExpressionNode ExprPrime(string expression, GenericExpressionNode lhs) { if (Same(expression, Token.TokenType.EndOfInput)) { return(lhs); } else if (Same(expression, Token.TokenType.Or)) { OperatorExpressionNode orNode = new OrExpressionNode(); GenericExpressionNode rhs = BooleanTerm(expression); orNode.LeftChild = lhs; orNode.RightChild = rhs; return(ExprPrime(expression, orNode)); } else { // I think this is ok. ExprPrime always shows up at // the rightmost side of the grammar rhs, the EndOfInput case // takes care of things return(lhs); } }
private GenericExpressionNode BooleanTermPrime(string expression, GenericExpressionNode lhs) { if (lexer.IsNext(Token.TokenType.EndOfInput)) { return lhs; } else if (Same(expression, Token.TokenType.And)) { GenericExpressionNode rhs = RelationalExpr(expression); if (null == rhs) { errorPosition = lexer.GetErrorPosition(); ProjectErrorUtilities.VerifyThrowInvalidProject(false, this.conditionAttribute, "UnexpectedTokenInCondition", expression, lexer.IsNextString(), errorPosition); } OperatorExpressionNode andNode = new AndExpressionNode(); andNode.LeftChild = lhs; andNode.RightChild = rhs; return BooleanTermPrime(expression, andNode); } else { // Should this be error case? return lhs; } }
private GenericExpressionNode ExprPrime(string expression, GenericExpressionNode lhs) { if (Same(expression, Token.TokenType.EndOfInput)) { return lhs; } else if (Same(expression, Token.TokenType.Or)) { OperatorExpressionNode orNode = new OrExpressionNode(); GenericExpressionNode rhs = BooleanTerm(expression); orNode.LeftChild = lhs; orNode.RightChild = rhs; return ExprPrime( expression, orNode ); } else { // I think this is ok. ExprPrime always shows up at // the rightmost side of the grammar rhs, the EndOfInput case // takes care of things return lhs; } }
private GenericExpressionNode Factor(string expression) { // Checks for TokenTypes String, Numeric, Property, ItemMetadata, and ItemList. GenericExpressionNode arg = this.Arg(expression); // If it's one of those, return it. if (arg != null) { return(arg); } // If it's not one of those, check for other TokenTypes. Token current = lexer.CurrentToken; if (Same(expression, Token.TokenType.Function)) { if (!Same(expression, Token.TokenType.LeftParenthesis)) { errorPosition = lexer.GetErrorPosition(); ProjectErrorUtilities.VerifyThrowInvalidProject(false, this.conditionAttribute, "UnexpectedTokenInCondition", lexer.IsNextString(), errorPosition); return(null); } ArrayList arglist = new ArrayList(); Arglist(expression, arglist); if (!Same(expression, Token.TokenType.RightParenthesis)) { errorPosition = lexer.GetErrorPosition(); ProjectErrorUtilities.VerifyThrowInvalidProject(false, this.conditionAttribute, "UnexpectedTokenInCondition", expression, lexer.IsNextString(), errorPosition); return(null); } return(new FunctionCallExpressionNode(current.String, arglist)); } else if (Same(expression, Token.TokenType.LeftParenthesis)) { GenericExpressionNode child = Expr(expression); if (Same(expression, Token.TokenType.RightParenthesis)) { return(child); } else { errorPosition = lexer.GetErrorPosition(); ProjectErrorUtilities.VerifyThrowInvalidProject(false, this.conditionAttribute, "UnexpectedTokenInCondition", expression, lexer.IsNextString(), errorPosition); } } else if (Same(expression, Token.TokenType.Not)) { OperatorExpressionNode notNode = new NotExpressionNode(); GenericExpressionNode expr = Factor(expression); if (expr == null) { errorPosition = lexer.GetErrorPosition(); ProjectErrorUtilities.VerifyThrowInvalidProject(false, this.conditionAttribute, "UnexpectedTokenInCondition", expression, lexer.IsNextString(), errorPosition); } notNode.LeftChild = expr; return(notNode); } else { errorPosition = lexer.GetErrorPosition(); ProjectErrorUtilities.VerifyThrowInvalidProject(false, this.conditionAttribute, "UnexpectedTokenInCondition", expression, lexer.IsNextString(), errorPosition); } return(null); }
/// <summary> /// Expands properties and items in the argument, and verifies that the result is consistent /// with a scalar parameter type. /// </summary> /// <param name="function">Function name for errors</param> /// <param name="argumentNode">Argument to be expanded</param> /// <returns>Scalar result</returns> /// <owner>danmose</owner> private string ExpandArgumentForScalarParameter(string function, GenericExpressionNode argumentNode, ConditionEvaluationState state) { string argument = argumentNode.GetUnexpandedValue(state); List<TaskItem> items = state.expanderToUse.ExpandAllIntoTaskItems(argument, state.conditionAttribute); string expandedValue = String.Empty; if (items.Count == 0) { // Empty argument, that's fine. } else if (items.Count == 1) { expandedValue = items[0].ItemSpec; } else // too many items for the function { // We only allow a single item to be passed into a scalar parameter. ProjectErrorUtilities.VerifyThrowInvalidProject(false, state.conditionAttribute, "CannotPassMultipleItemsIntoScalarFunction", function, argument, state.expanderToUse.ExpandAllIntoString(argument, state.conditionAttribute)); } return expandedValue; }