private static bool StartsWithKeyword(string expression, PreprocessorOperation operation) { expression = expression.ToUpper(CultureInfo.InvariantCulture); switch (operation) { case PreprocessorOperation.Not: if (expression.StartsWith("NOT ", StringComparison.Ordinal) || expression.StartsWith("NOT(", StringComparison.Ordinal)) { return(true); } break; case PreprocessorOperation.And: if (expression.StartsWith("AND ", StringComparison.Ordinal) || expression.StartsWith("AND(", StringComparison.Ordinal)) { return(true); } break; case PreprocessorOperation.Or: if (expression.StartsWith("OR ", StringComparison.Ordinal) || expression.StartsWith("OR(", StringComparison.Ordinal)) { return(true); } break; default: break; } return(false); }
private void UpdateExpressionValue(ref bool currentValue, PreprocessorOperation operation, bool prevResult) { switch (operation) { case PreprocessorOperation.And: currentValue = currentValue && prevResult; break; case PreprocessorOperation.Or: currentValue = currentValue || prevResult; break; case PreprocessorOperation.Not: currentValue = !currentValue; break; default: throw new Exception("Unexpected Preprocessor Operator: " + operation.ToString()); } }
/// <summary> /// Tests expression to see if it starts with a keyword. /// </summary> /// <param name="expression">Expression to test.</param> /// <param name="operation">Operation to test for.</param> /// <returns>true if expression starts with a keyword.</returns> private static bool StartsWithKeyword(string expression, PreprocessorOperation operation) { expression = expression.ToUpper(CultureInfo.InvariantCulture); switch (operation) { case PreprocessorOperation.Not: if (expression.StartsWith("NOT ", StringComparison.Ordinal) || expression.StartsWith("NOT(", StringComparison.Ordinal)) { return true; } break; case PreprocessorOperation.And: if (expression.StartsWith("AND ", StringComparison.Ordinal) || expression.StartsWith("AND(", StringComparison.Ordinal)) { return true; } break; case PreprocessorOperation.Or: if (expression.StartsWith("OR ", StringComparison.Ordinal) || expression.StartsWith("OR(", StringComparison.Ordinal)) { return true; } break; default: break; } return false; }
/// <summary> /// Recurse through the expression to evaluate if it is true or false. /// The expression is evaluated left to right. /// The expression is case-sensitive (converted to upper case) with the /// following exceptions: variable names and keywords (and, not, or). /// Comparisons with = and != are string comparisons. /// Comparisons with inequality operators must be done on valid integers. /// /// The operator precedence is: /// "" /// () /// <, >, <=, >=, =, != /// Not /// And, Or /// /// Valid expressions include: /// not $(var.B) or not $(var.C) /// (($(var.A))and $(var.B) ="2")or Not((($(var.C))) and $(var.A)) /// (($(var.A)) and $(var.B) = " 3 ") or $(var.C) /// $(var.A) and $(var.C) = "3" or $(var.C) and $(var.D) = $(env.windir) /// $(var.A) and $(var.B)>2 or $(var.B) <= 2 /// $(var.A) != "2" /// </summary> /// <param name="originalExpression">The original expression</param> /// <param name="expression">The expression currently being evaluated</param> /// <param name="prevResultOperation">The operation to apply to this result</param> /// <param name="prevResult">The previous result to apply to this result</param> /// <returns>Boolean to indicate if the expression is true or false</returns> private bool EvaluateExpressionRecurse(string originalExpression, ref string expression, PreprocessorOperation prevResultOperation, bool prevResult) { bool expressionValue = false; expression = expression.Trim(); if (expression.Length == 0) { throw new WixException(WixErrors.UnexpectedEmptySubexpression(this.GetCurrentSourceLineNumbers(), originalExpression)); } // If the expression starts with parenthesis, evaluate it if (expression.IndexOf('(') == 0) { int endSubExpressionIndex; string subExpression = this.GetParenthesisExpression(originalExpression, expression, out endSubExpressionIndex); expressionValue = this.EvaluateExpressionRecurse(originalExpression, ref subExpression, PreprocessorOperation.And, true); // Now get the rest of the expression that hasn't been evaluated expression = expression.Substring(endSubExpressionIndex).Trim(); } else { // Check for NOT if (StartsWithKeyword(expression, PreprocessorOperation.Not)) { expression = expression.Substring(3).Trim(); if (expression.Length == 0) { throw new WixException(WixErrors.ExpectedExpressionAfterNot(this.GetCurrentSourceLineNumbers(), originalExpression)); } expressionValue = this.EvaluateExpressionRecurse(originalExpression, ref expression, PreprocessorOperation.Not, true); } else // Expect a literal { expressionValue = this.EvaluateAtomicExpression(originalExpression, ref expression); // Expect the literal that was just evaluated to already be cut off } } this.UpdateExpressionValue(ref expressionValue, prevResultOperation, prevResult); // If there's still an expression left, it must start with AND or OR. if (expression.Trim().Length > 0) { if (StartsWithKeyword(expression, PreprocessorOperation.And)) { expression = expression.Substring(3); return this.EvaluateExpressionRecurse(originalExpression, ref expression, PreprocessorOperation.And, expressionValue); } else if (StartsWithKeyword(expression, PreprocessorOperation.Or)) { expression = expression.Substring(2); return this.EvaluateExpressionRecurse(originalExpression, ref expression, PreprocessorOperation.Or, expressionValue); } else { throw new WixException(WixErrors.InvalidSubExpression(this.GetCurrentSourceLineNumbers(), expression, originalExpression)); } } return expressionValue; }
/// <summary> /// Updates expression based on operation. /// </summary> /// <param name="currentValue">State to update.</param> /// <param name="operation">Operation to apply to current value.</param> /// <param name="prevResult">Previous result.</param> private void UpdateExpressionValue(ref bool currentValue, PreprocessorOperation operation, bool prevResult) { switch (operation) { case PreprocessorOperation.And: currentValue = currentValue && prevResult; break; case PreprocessorOperation.Or: currentValue = currentValue || prevResult; break; case PreprocessorOperation.Not: currentValue = !currentValue; break; default: throw new WixException(WixErrors.UnexpectedPreprocessorOperator(this.GetCurrentSourceLineNumbers(), operation.ToString())); } }
private bool EvaluateExpressionRecurse(string originalExpression, ref string expression, PreprocessorOperation prevResultOperation, bool prevResult) { bool expressionValue = false; expression = expression.Trim(); if (expression.Length == 0) { throw new Exception("Unexpected Empty Subexpression: " + originalExpression); } // If the expression starts with parenthesis, evaluate it if (expression.IndexOf('(') == 0) { int endSubExpressionIndex; string subExpression = this.GetParenthesisExpression(originalExpression, expression, out endSubExpressionIndex); expressionValue = this.EvaluateExpressionRecurse(originalExpression, ref subExpression, PreprocessorOperation.And, true); // Now get the rest of the expression that hasn't been evaluated expression = expression.Substring(endSubExpressionIndex).Trim(); } else { // Check for NOT if (StartsWithKeyword(expression, PreprocessorOperation.Not)) { expression = expression.Substring(3).Trim(); if (expression.Length == 0) { throw new Exception("Expected Expression After Not: " + originalExpression); } expressionValue = this.EvaluateExpressionRecurse(originalExpression, ref expression, PreprocessorOperation.Not, true); } else // Expect a literal { expressionValue = this.EvaluateAtomicExpression(originalExpression, ref expression); // Expect the literal that was just evaluated to already be cut off } } this.UpdateExpressionValue(ref expressionValue, prevResultOperation, prevResult); // If there's still an expression left, it must start with AND or OR. if (expression.Trim().Length > 0) { if (StartsWithKeyword(expression, PreprocessorOperation.And)) { expression = expression.Substring(3); return(this.EvaluateExpressionRecurse(originalExpression, ref expression, PreprocessorOperation.And, expressionValue)); } else if (StartsWithKeyword(expression, PreprocessorOperation.Or)) { expression = expression.Substring(2); return(this.EvaluateExpressionRecurse(originalExpression, ref expression, PreprocessorOperation.Or, expressionValue)); } else { throw new Exception("Invalid Sub Expression: " + expression); } } return(expressionValue); }
/// <summary> /// Tests expression to see if it starts with a keyword. /// </summary> /// <param name="expression">Expression to test.</param> /// <param name="operation">Operation to test for.</param> /// <returns>true if expression starts with a keyword.</returns> private bool StartsWithKeyword(string expression, PreprocessorOperation operation) { expression = expression.ToUpper(); switch (operation) { case PreprocessorOperation.Not: if (expression.StartsWith("NOT ") || expression.StartsWith("NOT(")) { return true; } break; case PreprocessorOperation.And: if (expression.StartsWith("AND ") || expression.StartsWith("AND(")) { return true; } break; case PreprocessorOperation.Or: if (expression.StartsWith("OR ") || expression.StartsWith("OR(")) { return true; } break; default: break; } return false; }