/// <summary> /// Generate the code that we will use to access this array. Loop symantics in this framework are basically "foreach" rather than "for" - so /// we return an object that can be used to reference each array element. /// </summary> /// <param name="env"></param> /// <param name="context"></param> /// <param name="indexName"></param> /// <param name="popVariableContext"></param> /// <returns></returns> public Tuple <Expression, IDeclaredParameter> AddLoop(IGeneratedQueryCode env, ICodeContext context, CompositionContainer container) { /// /// First, we will need to know the length of this array /// var lenExpression = Expression.ArrayLength(_arrayExpression); var lenTranslation = ExpressionToCPP.GetExpression(lenExpression, env, context, container); /// /// Next, generate the expression that forms the basis of the index lookup. We don't /// translate this - that only gets to happen when one is actually looking at a final result. /// var loopVariable = DeclarableParameter.CreateDeclarableParameterExpression(typeof(int)); var indexExpression = Expression.MakeBinary(ExpressionType.ArrayIndex, _arrayExpression, loopVariable); /// /// Now the for loop statement! /// env.Add(new StatementForLoop(loopVariable, lenTranslation)); /// /// Return the index expression - the thing that can be used to replace all expressions and /// reference the item we are looping over. /// return(Tuple.Create <Expression, IDeclaredParameter>(indexExpression, loopVariable)); }
/// <summary> /// Deal with an inline conditional expression (test ? ans1 : ans2). We can translate this directly to C++, fortunately! /// </summary> /// <param name="expression"></param> /// <returns></returns> /// <remarks> /// We turn this into a real if statement, rather than a fake if statement. This is to try to keep any code /// associated with the side that won't be executed, not being executed. /// Note that Resolver has some good code already to special case handle a bool result. /// </remarks> protected override Expression VisitConditional(ConditionalExpression expression) { var testExpression = expression.Test; var trueExpression = expression.IfTrue; var falseExpression = expression.IfFalse; // Run the test. var testBoolInCode = AssignExpreaaionToEvaluationIfNeededBool(_codeEnv, _codeContext, MEFContainer, testExpression); // Next, do the result cache. var resultInCode = DeclarableParameter.CreateDeclarableParameterExpression(expression.Type); _codeEnv.Add(resultInCode); _result = resultInCode; // Get the result if the test is true. var topScope = _codeEnv.CurrentScope; _codeEnv.Add(new Statements.StatementFilter(testBoolInCode)); _codeEnv.Add(new Statements.StatementAssign(resultInCode, GetExpression(trueExpression, _codeEnv, _codeContext, MEFContainer))); _codeEnv.CurrentScope = topScope; _codeEnv.Add(new Statements.StatementFilter(GetExpression(Expression.Not(testBoolInCode), _codeEnv, _codeContext, MEFContainer))); _codeEnv.Add(new Statements.StatementAssign(resultInCode, GetExpression(falseExpression, _codeEnv, _codeContext, MEFContainer))); _codeEnv.CurrentScope = topScope; // Result is set. Continue to process other items in the tree. return(expression); }
/// <summary> /// Actually add the loop to the code and return everything! /// </summary> /// <param name="env"></param> /// <param name="context"></param> /// <param name="container"></param> /// <returns></returns> public Tuple <Expression, IDeclaredParameter> AddLoop(IGeneratedQueryCode env, ICodeContext context, CompositionContainer container) { // Create the index variable! var loopVariable = DeclarableParameter.CreateDeclarableParameterExpression(typeof(int)); var floop = new Statements.StatementForLoop(loopVariable, _maxValue, _minValue); env.Add(floop); return(Tuple.Create(loopVariable as Expression, loopVariable as IDeclaredParameter)); }
/// <summary> /// We only support a sub-class of expressions for now - so we'd better make sure we are protected! /// </summary> /// <param name="expression"></param> /// <returns></returns> protected override Expression VisitConditional(ConditionalExpression expression) { // We can support complex sub-expressions so long as they don't leak out of the // comparison. if (expression.Type.IsClass && ( CheckForSubQueries.CheckExpression(expression.IfFalse) || CheckForSubQueries.CheckExpression(expression.IfTrue)) ) { throw new NotSupportedException(string.Format("Complex true/false clauses in a conditional expression are not supported: '{0}'", expression.ToString())); } // If this is a class as a result, then we can't do much extra processing here. So skip. if (expression.Type.IsClass) { return(base.VisitConditional(expression)); } // Run the code for the test, and then create the if/then/else that will support it. var testExpression = base.Visit(expression.Test); var testExpressionEvaluation = ExpressionToCPP.GetExpression(testExpression, GeneratedCode, CodeContext, MEFContainer); var testBoolInCode = testExpressionEvaluation is DeclarableParameter p ? p : DeclarableParameter.CreateDeclarableParameterExpression(typeof(bool)); if (testBoolInCode != testExpressionEvaluation) { GeneratedCode.Add(testBoolInCode); GeneratedCode.Add(new Statements.StatementAssign(testBoolInCode, testExpressionEvaluation)); } // The result var conditionalResult = DeclarableParameter.CreateDeclarableParameterExpression(expression.Type); GeneratedCode.Add(conditionalResult); // Do the if true statement var topScope = GeneratedCode.CurrentScope; GeneratedCode.Add(new Statements.StatementFilter(testBoolInCode)); var iftrueExpression = Visit(expression.IfTrue); GeneratedCode.Add(new Statements.StatementAssign(conditionalResult, ExpressionToCPP.GetExpression(iftrueExpression, GeneratedCode, CodeContext, MEFContainer))); GeneratedCode.CurrentScope = topScope; // Do the if false statement GeneratedCode.Add(new Statements.StatementFilter(ExpressionToCPP.GetExpression(Expression.Not(testBoolInCode), GeneratedCode, CodeContext, MEFContainer))); var ifFalseExpression = Visit(expression.IfFalse); GeneratedCode.Add(new Statements.StatementAssign(conditionalResult, ExpressionToCPP.GetExpression(ifFalseExpression, GeneratedCode, CodeContext, MEFContainer))); GeneratedCode.CurrentScope = topScope; // Consider this expression now transformed, so return the result, not // the conditional expression itself. return(conditionalResult); }
public void TestDeclarableParameterReplacement() { var e1 = DeclarableParameter.CreateDeclarableParameterExpression(typeof(int)); var e2 = DeclarableParameter.CreateDeclarableParameterExpression(typeof(int)); var cc = new CodeContext(); cc.Add(e1.ParameterName, e2); var expr = ParameterReplacementExpressionVisitor.ReplaceParameters(e1, cc); Assert.AreEqual(e2, expr, "value of translation"); }
/// <summary> /// Some method is being called. Offer plug-ins a chance to transform this method call. /// </summary> /// <param name="expression"></param> /// <returns></returns> protected override Expression VisitMethodCall(MethodCallExpression expression) { _result = TypeHandlers.CodeMethodCall(expression, _codeEnv, MEFContainer); // Cache this so that we don't have to re-call it later (if need be) if this is a simple type. if (_result.Type.IsNumberType() && !_result.IsSimpleTerm()) { var cachedValue = DeclarableParameter.CreateDeclarableParameterExpression(_result.Type); _codeEnv.Add(cachedValue); var assign = new Statements.StatementAssign(cachedValue, _result); _codeEnv.Add(assign); _result = cachedValue; } // Always return the expression return(expression); }
/// <summary> /// Evaluate an expression. If the result is just DeclareableParameter, return that, otherwise make an assignment. /// </summary> /// <param name="ce"></param> /// <param name="cc"></param> /// <param name="container"></param> /// <param name="exprToHandIn"></param> /// <returns></returns> private static DeclarableParameter AssignExpreaaionToEvaluationIfNeededBool(IGeneratedQueryCode ce, ICodeContext cc, CompositionContainer container, Expression exprToHandIn, IScopeInfo whereToDeclare = null) { IValue exprEvaluation = GetExpression(exprToHandIn, ce, cc, container); if (exprEvaluation is DeclarableParameter p) { // We can only return this if the variable is declared at the place we want it to be! var currentScope = ce.CurrentScope; try { if (whereToDeclare != null) { ce.CurrentScope = whereToDeclare; } if (ce.CodeBody.AllDeclaredVariables.Where(dp => dp == p).Any()) { return(p); } } finally { ce.CurrentScope = currentScope; } } // Create and assign an expression. var result = DeclarableParameter.CreateDeclarableParameterExpression(typeof(bool)); result.InitialValue = new ValSimple("false", typeof(bool)); if (whereToDeclare == null) { ce.Add(result); } else { var currentScope = ce.CurrentScope; ce.CurrentScope = whereToDeclare; ce.Add(result); ce.CurrentScope = currentScope; } ce.Add(new Statements.StatementAssign(result, exprEvaluation)); return(result); }
/// <summary> /// We are looking at a&&b or a||b. We wnat to make sure we evaluate b iff we need it, depending on the result of a. /// </summary> /// <param name="expr"></param> /// <param name="ce"></param> /// <param name="cc"></param> /// <param name="container"></param> /// <returns></returns> /// <remarks> /// To prevent us from updating variables (which makes optmization harder), we will implement the code as follows for a&&b: /// bool_1 = false; bool_2 = false; bool_3 = false; /// bool_1 = a /// if (bool_1) bool_2 = b /// bool_3 = bool_1 && bool_2 ///</remarks> private static IValue GetExpressionForBoolAndOr(Expression expr, IGeneratedQueryCode ce, ICodeContext cc, CompositionContainer container) { // Svae to make sure we can get back. var outterScope = ce.CurrentScope; // Create a variable to hold the result of this test var resultBool3 = DeclarableParameter.CreateDeclarableParameterExpression(typeof(bool)); resultBool3.InitialValue = new ValSimple("false", typeof(bool)); ce.Add(resultBool3); // Create and evaluate bool_1 var binaryExpression = expr as BinaryExpression; DeclarableParameter resultBool1 = AssignExpreaaionToEvaluationIfNeededBool(ce, cc, container, binaryExpression.Left); // Now, see if we need to evalute the right hand operand. if (expr.NodeType == ExpressionType.AndAlso) { ce.Add(new Statements.StatementFilter(resultBool1)); } else { var notYet = new ValSimple($"!{resultBool1.RawValue}", typeof(bool), new IDeclaredParameter[] { resultBool1 }); ce.Add(new Statements.StatementFilter(notYet)); } // Create and evaluate bool 1. var resultBool2 = AssignExpreaaionToEvaluationIfNeededBool(ce, cc, container, binaryExpression.Right, outterScope); ce.CurrentScope = outterScope; // Finally, evaluate bool3. var termEvaluation = expr.NodeType == ExpressionType.AndAlso ? $"{resultBool1.RawValue}&&{resultBool2.RawValue}" : $"{resultBool1.RawValue}||{resultBool2.RawValue}"; ce.Add(new Statements.StatementAssign(resultBool3, new ValSimple(termEvaluation, typeof(bool), new[] { resultBool1, resultBool2 }))); // Return the value we've now filled. return(resultBool3); }